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,
63 typedef struct _Node Node;
73 GtkWidget *extra; /*GtkMenu for submenus, second separator for placeholders*/
80 #define GTK_UI_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_UI_MANAGER, GtkUIManagerPrivate))
82 struct _GtkUIManagerPrivate
84 GtkAccelGroup *accel_group;
93 gboolean add_tearoffs;
96 #define NODE_INFO(node) ((Node *)node->data)
98 typedef struct _NodeUIReference NodeUIReference;
100 struct _NodeUIReference
106 static void gtk_ui_manager_class_init (GtkUIManagerClass *class);
107 static void gtk_ui_manager_init (GtkUIManager *self);
108 static void gtk_ui_manager_finalize (GObject *object);
109 static void gtk_ui_manager_set_property (GObject *object,
113 static void gtk_ui_manager_get_property (GObject *object,
117 static void queue_update (GtkUIManager *self);
118 static void dirty_all_nodes (GtkUIManager *self);
119 static GNode * get_child_node (GtkUIManager *self,
121 const gchar *childname,
122 gint childname_length,
126 static GNode * get_node (GtkUIManager *self,
130 static gboolean free_node (GNode *node);
131 static void node_prepend_ui_reference (Node *node,
133 GQuark action_quark);
134 static void node_remove_ui_reference (Node *node,
152 static guint merge_signals[LAST_SIGNAL] = { 0 };
154 static GMemChunk *merge_node_chunk = NULL;
157 gtk_ui_manager_get_type (void)
159 static GtkType type = 0;
163 static const GTypeInfo type_info =
165 sizeof (GtkUIManagerClass),
166 (GBaseInitFunc) NULL,
167 (GBaseFinalizeFunc) NULL,
168 (GClassInitFunc) gtk_ui_manager_class_init,
169 (GClassFinalizeFunc) NULL,
172 sizeof (GtkUIManager),
174 (GInstanceInitFunc) gtk_ui_manager_init,
177 type = g_type_register_static (G_TYPE_OBJECT,
185 gtk_ui_manager_class_init (GtkUIManagerClass *klass)
187 GObjectClass *gobject_class;
189 gobject_class = G_OBJECT_CLASS (klass);
191 if (!merge_node_chunk)
192 merge_node_chunk = g_mem_chunk_create (Node, 64,
195 gobject_class->finalize = gtk_ui_manager_finalize;
196 gobject_class->set_property = gtk_ui_manager_set_property;
197 gobject_class->get_property = gtk_ui_manager_get_property;
200 * GtkUIManager:add-tearoffs:
202 * The "add-tearoffs" property controls whether generated menus
203 * have tearoff menu items.
205 * Note that this only affects regular menus. Generated popup
206 * menus never have tearoff menu items.
210 g_object_class_install_property (gobject_class,
212 g_param_spec_boolean ("add_tearoffs",
213 _("Add tearoffs to menus"),
214 _("Whether tearoff menu items should be added to menus"),
216 G_PARAM_READABLE | G_PARAM_WRITABLE));
219 * GtkUIManager::add-widget:
220 * @merge: a #GtkUIManager
221 * @widget: the added widget
223 * The add_widget signal is emitted for each generated menubar and toolbar.
224 * It is not emitted for generated popup menus, which can be obtained by
225 * gtk_ui_manager_get_widget().
229 merge_signals[ADD_WIDGET] =
230 g_signal_new ("add_widget",
231 G_OBJECT_CLASS_TYPE (klass),
232 G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
233 G_STRUCT_OFFSET (GtkUIManagerClass, add_widget), NULL, NULL,
234 g_cclosure_marshal_VOID__OBJECT,
239 * GtkUIManager::changed:
240 * @merge: a #GtkUIManager
242 * The "changed" signal is emitted whenever the merged UI changes.
246 merge_signals[CHANGED] =
247 g_signal_new ("changed",
248 G_OBJECT_CLASS_TYPE (klass),
249 G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
250 G_STRUCT_OFFSET (GtkUIManagerClass, changed), NULL, NULL,
251 g_cclosure_marshal_VOID__VOID,
254 g_type_class_add_private (gobject_class, sizeof (GtkUIManagerPrivate));
259 gtk_ui_manager_init (GtkUIManager *self)
264 self->private_data = GTK_UI_MANAGER_GET_PRIVATE (self);
266 self->private_data->accel_group = gtk_accel_group_new ();
268 self->private_data->root_node = NULL;
269 self->private_data->action_groups = NULL;
271 self->private_data->last_merge_id = 0;
272 self->private_data->add_tearoffs = FALSE;
274 merge_id = gtk_ui_manager_new_merge_id (self);
275 node = get_child_node (self, NULL, "ui", 2,
276 NODE_TYPE_ROOT, TRUE, FALSE);
277 node_prepend_ui_reference (NODE_INFO (node), merge_id, 0);
281 gtk_ui_manager_finalize (GObject *object)
283 GtkUIManager *self = GTK_UI_MANAGER (object);
285 if (self->private_data->update_tag != 0)
287 g_source_remove (self->private_data->update_tag);
288 self->private_data->update_tag = 0;
291 g_node_traverse (self->private_data->root_node,
292 G_POST_ORDER, G_TRAVERSE_ALL, -1,
293 (GNodeTraverseFunc)free_node, 0);
294 g_node_destroy (self->private_data->root_node);
295 self->private_data->root_node = NULL;
297 g_list_foreach (self->private_data->action_groups,
298 (GFunc) g_object_unref, NULL);
299 g_list_free (self->private_data->action_groups);
300 self->private_data->action_groups = NULL;
302 g_object_unref (self->private_data->accel_group);
303 self->private_data->accel_group = NULL;
307 gtk_ui_manager_set_property (GObject *object,
312 GtkUIManager *self = GTK_UI_MANAGER (object);
316 case PROP_ADD_TEAROFFS:
317 gtk_ui_manager_set_add_tearoffs (self, g_value_get_boolean (value));
320 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
326 gtk_ui_manager_get_property (GObject *object,
331 GtkUIManager *self = GTK_UI_MANAGER (object);
335 case PROP_ADD_TEAROFFS:
336 g_value_set_boolean (value, self->private_data->add_tearoffs);
339 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346 * gtk_ui_manager_new:
348 * Creates a new ui manager object.
350 * Return value: a new ui manager object.
355 gtk_ui_manager_new (void)
357 return g_object_new (GTK_TYPE_UI_MANAGER, NULL);
362 * gtk_ui_manager_get_add_tearoffs:
363 * @self: a #GtkUIManager
365 * Returns whether menus generated by this #GtkUIManager
366 * will have tearoff menu items.
368 * Return value: whether tearoff menu items are added
373 gtk_ui_manager_get_add_tearoffs (GtkUIManager *self)
375 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), FALSE);
377 return self->private_data->add_tearoffs;
382 * gtk_ui_manager_set_add_tearoffs:
383 * @self: a #GtkUIManager
384 * @add_tearoffs: whether tearoff menu items are added
386 * Sets the "add_tearoffs" property, which controls whether menus
387 * generated by this #GtkUIManager will have tearoff menu items.
389 * Note that this only affects regular menus. Generated popup
390 * menus never have tearoff menu items.
395 gtk_ui_manager_set_add_tearoffs (GtkUIManager *self,
396 gboolean add_tearoffs)
398 g_return_if_fail (GTK_IS_UI_MANAGER (self));
400 add_tearoffs = add_tearoffs != FALSE;
402 if (add_tearoffs != self->private_data->add_tearoffs)
404 self->private_data->add_tearoffs = add_tearoffs;
406 dirty_all_nodes (self);
408 g_object_notify (G_OBJECT (self), "add_tearoffs");
413 * gtk_ui_manager_insert_action_group:
414 * @self: a #GtkUIManager object
415 * @action_group: the action group to be inserted
416 * @pos: the position at which the group will be inserted.
418 * Inserts an action group into the list of action groups associated
419 * with @self. Actions in earlier groups hide actions with the same
420 * name in later groups.
425 gtk_ui_manager_insert_action_group (GtkUIManager *self,
426 GtkActionGroup *action_group,
429 g_return_if_fail (GTK_IS_UI_MANAGER (self));
430 g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
431 g_return_if_fail (g_list_find (self->private_data->action_groups,
432 action_group) == NULL);
434 g_object_ref (action_group);
435 self->private_data->action_groups =
436 g_list_insert (self->private_data->action_groups, action_group, pos);
438 /* dirty all nodes, as action bindings may change */
439 dirty_all_nodes (self);
443 * gtk_ui_manager_remove_action_group:
444 * @self: a #GtkUIManager object
445 * @action_group: the action group to be removed
447 * Removes an action group from the list of action groups associated
453 gtk_ui_manager_remove_action_group (GtkUIManager *self,
454 GtkActionGroup *action_group)
456 g_return_if_fail (GTK_IS_UI_MANAGER (self));
457 g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
458 g_return_if_fail (g_list_find (self->private_data->action_groups,
459 action_group) != NULL);
461 self->private_data->action_groups =
462 g_list_remove (self->private_data->action_groups, action_group);
463 g_object_unref (action_group);
465 /* dirty all nodes, as action bindings may change */
466 dirty_all_nodes (self);
470 * gtk_ui_manager_get_action_groups:
471 * @self: a #GtkUIManager object
473 * Returns the list of action groups associated with @self.
475 * Return value: a #GList of action groups. The list is owned by GTK+
476 * and should not be modified.
481 gtk_ui_manager_get_action_groups (GtkUIManager *self)
483 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
485 return self->private_data->action_groups;
489 * gtk_ui_manager_get_accel_group:
490 * @self: a #GtkUIManager object
492 * Returns the #GtkAccelGroup associated with @self.
494 * Return value: the #GtkAccelGroup.
499 gtk_ui_manager_get_accel_group (GtkUIManager *self)
501 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
503 return self->private_data->accel_group;
507 * gtk_ui_manager_get_widget:
508 * @self: a #GtkUIManager
511 * Looks up a widget by following a path.
512 * The path consists of the names specified in the XML description of the UI.
513 * separated by '/'. Elements which don't have a name or action attribute in
514 * the XML (e.g. <popup>) can be addressed by their XML element name
515 * (e.g. "popup"). The root element ("/ui") can be omitted in the path.
517 * Return value: the widget found by following the path, or %NULL if no widget
523 gtk_ui_manager_get_widget (GtkUIManager *self,
528 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
529 g_return_val_if_fail (path != NULL, NULL);
531 /* ensure that there are no pending updates before we get the
533 gtk_ui_manager_ensure_update (self);
535 node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
540 return NODE_INFO (node)->proxy;
544 * gtk_ui_manager_get_action:
545 * @self: a #GtkUIManager
548 * Looks up an action by following a path. See gtk_ui_manager_get_widget()
549 * for more information about paths.
551 * Return value: the action whose proxy widget is found by following the path,
552 * or %NULL if no widget was found.
557 gtk_ui_manager_get_action (GtkUIManager *self,
562 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
563 g_return_val_if_fail (path != NULL, NULL);
565 /* ensure that there are no pending updates before we get
567 gtk_ui_manager_ensure_update (self);
569 node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
574 return NODE_INFO (node)->action;
578 get_child_node (GtkUIManager *self,
580 const gchar *childname,
581 gint childname_length,
588 g_return_val_if_fail (parent == NULL ||
589 (NODE_INFO (parent)->type != NODE_TYPE_MENUITEM &&
590 NODE_INFO (parent)->type != NODE_TYPE_TOOLITEM),
597 for (child = parent->children; child != NULL; child = child->next)
599 if (strlen (NODE_INFO (child)->name) == childname_length &&
600 !strncmp (NODE_INFO (child)->name, childname, childname_length))
602 /* if undecided about node type, set it */
603 if (NODE_INFO (child)->type == NODE_TYPE_UNDECIDED)
604 NODE_INFO (child)->type = node_type;
606 /* warn about type mismatch */
607 if (NODE_INFO (child)->type != NODE_TYPE_UNDECIDED &&
608 node_type != NODE_TYPE_UNDECIDED &&
609 NODE_INFO (child)->type != node_type)
610 g_warning ("node type doesn't match %d (%s is type %d)",
612 NODE_INFO (child)->name,
613 NODE_INFO (child)->type);
619 if (!child && create)
623 mnode = g_chunk_new0 (Node, merge_node_chunk);
624 mnode->type = node_type;
625 mnode->name = g_strndup (childname, childname_length);
629 child = g_node_prepend_data (parent, mnode);
631 child = g_node_append_data (parent, mnode);
636 /* handle root node */
637 if (self->private_data->root_node)
639 child = self->private_data->root_node;
640 if (strncmp (NODE_INFO (child)->name, childname, childname_length) != 0)
641 g_warning ("root node name '%s' doesn't match '%s'",
642 childname, NODE_INFO (child)->name);
643 if (NODE_INFO (child)->type != NODE_TYPE_ROOT)
644 g_warning ("base element must be of type ROOT");
650 mnode = g_chunk_new0 (Node, merge_node_chunk);
651 mnode->type = node_type;
652 mnode->name = g_strndup (childname, childname_length);
655 child = self->private_data->root_node = g_node_new (mnode);
663 get_node (GtkUIManager *self,
668 const gchar *pos, *end;
669 GNode *parent, *node;
671 end = path + strlen (path);
673 parent = node = NULL;
679 slash = strchr (pos, '/');
681 length = slash - pos;
683 length = strlen (pos);
685 node = get_child_node (self, parent, pos, length, NODE_TYPE_UNDECIDED,
690 pos += length + 1; /* move past the node name and the slash too */
694 if (node != NULL && NODE_INFO (node)->type == NODE_TYPE_UNDECIDED)
695 NODE_INFO (node)->type = node_type;
701 free_node (GNode *node)
703 Node *info = NODE_INFO (node);
705 g_list_foreach (info->uifiles, (GFunc) g_free, NULL);
706 g_list_free (info->uifiles);
709 g_object_unref (info->action);
711 g_chunk_free (info, merge_node_chunk);
717 * gtk_ui_manager_new_merge_id:
718 * @self: a #GtkUIManager
720 * Returns an unused merge id, suitable for use with
721 * gtk_ui_manager_add_ui().
723 * Return value: an unused merge id.
728 gtk_ui_manager_new_merge_id (GtkUIManager *self)
730 self->private_data->last_merge_id++;
732 return self->private_data->last_merge_id;
736 node_prepend_ui_reference (Node *node,
740 NodeUIReference *reference;
742 reference = g_new (NodeUIReference, 1);
743 reference->action_quark = action_quark;
744 reference->merge_id = merge_id;
746 /* Prepend the reference */
747 node->uifiles = g_list_prepend (node->uifiles, reference);
753 node_remove_ui_reference (Node *node,
758 for (p = node->uifiles; p != NULL; p = p->next)
760 NodeUIReference *reference = p->data;
762 if (reference->merge_id == merge_id)
764 node->uifiles = g_list_remove_link (node->uifiles, p);
773 /* -------------------- The UI file parser -------------------- */
786 typedef struct _ParseContext ParseContext;
790 ParseState prev_state;
800 start_element_handler (GMarkupParseContext *context,
801 const gchar *element_name,
802 const gchar **attribute_names,
803 const gchar **attribute_values,
807 ParseContext *ctx = user_data;
808 GtkUIManager *self = ctx->self;
811 const gchar *node_name;
816 gboolean raise_error = TRUE;
822 for (i = 0; attribute_names[i] != NULL; i++)
824 if (!strcmp (attribute_names[i], "name"))
826 node_name = attribute_values[i];
828 else if (!strcmp (attribute_names[i], "action"))
830 action = attribute_values[i];
831 action_quark = g_quark_from_string (attribute_values[i]);
833 else if (!strcmp (attribute_names[i], "position"))
835 top = !strcmp (attribute_values[i], "top");
839 gint line_number, char_number;
841 g_markup_parse_context_get_position (context,
842 &line_number, &char_number);
845 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
846 _("Unknown attribute '%s' on line %d char %d"),
848 line_number, char_number);
853 /* Work out a name for this node. Either the name attribute, or
854 * the action, or the element name */
855 if (node_name == NULL)
860 node_name = element_name;
863 switch (element_name[0])
866 if (ctx->state == STATE_ROOT && !strcmp (element_name, "accelerator"))
868 ctx->state = STATE_ROOT;
869 ctx->current = get_child_node (self, ctx->current,
870 node_name, strlen (node_name),
871 NODE_TYPE_ACCELERATOR,
873 if (NODE_INFO (ctx->current)->action_name == 0)
874 NODE_INFO (ctx->current)->action_name = action_quark;
876 node_prepend_ui_reference (NODE_INFO (ctx->current),
877 ctx->merge_id, action_quark);
878 NODE_INFO (ctx->current)->dirty = TRUE;
884 if (ctx->state == STATE_START && !strcmp (element_name, "ui"))
886 ctx->state = STATE_ROOT;
887 ctx->current = self->private_data->root_node;
890 node_prepend_ui_reference (NODE_INFO (ctx->current),
891 ctx->merge_id, action_quark);
895 if (ctx->state == STATE_ROOT && !strcmp (element_name, "menubar"))
897 ctx->state = STATE_MENU;
898 ctx->current = get_child_node (self, ctx->current,
899 node_name, strlen (node_name),
902 if (NODE_INFO (ctx->current)->action_name == 0)
903 NODE_INFO (ctx->current)->action_name = action_quark;
905 node_prepend_ui_reference (NODE_INFO (ctx->current),
906 ctx->merge_id, action_quark);
907 NODE_INFO (ctx->current)->dirty = TRUE;
911 else if (ctx->state == STATE_MENU && !strcmp (element_name, "menu"))
913 ctx->current = get_child_node (self, ctx->current,
914 node_name, strlen (node_name),
917 if (NODE_INFO (ctx->current)->action_name == 0)
918 NODE_INFO (ctx->current)->action_name = action_quark;
920 node_prepend_ui_reference (NODE_INFO (ctx->current),
921 ctx->merge_id, action_quark);
922 NODE_INFO (ctx->current)->dirty = TRUE;
926 else if (ctx->state == STATE_MENU && !strcmp (element_name, "menuitem"))
930 ctx->state = STATE_MENUITEM;
931 node = get_child_node (self, ctx->current,
932 node_name, strlen (node_name),
935 if (NODE_INFO (node)->action_name == 0)
936 NODE_INFO (node)->action_name = action_quark;
938 node_prepend_ui_reference (NODE_INFO (node),
939 ctx->merge_id, action_quark);
940 NODE_INFO (node)->dirty = TRUE;
946 if (ctx->state == STATE_ROOT && !strcmp (element_name, "popup"))
948 ctx->state = STATE_MENU;
949 ctx->current = get_child_node (self, ctx->current,
950 node_name, strlen (node_name),
953 if (NODE_INFO (ctx->current)->action_name == 0)
954 NODE_INFO (ctx->current)->action_name = action_quark;
956 node_prepend_ui_reference (NODE_INFO (ctx->current),
957 ctx->merge_id, action_quark);
958 NODE_INFO (ctx->current)->dirty = TRUE;
962 else if ((ctx->state == STATE_MENU || ctx->state == STATE_TOOLBAR) &&
963 !strcmp (element_name, "placeholder"))
965 if (ctx->state == STATE_TOOLBAR)
966 ctx->current = get_child_node (self, ctx->current,
967 node_name, strlen (node_name),
968 NODE_TYPE_TOOLBAR_PLACEHOLDER,
971 ctx->current = get_child_node (self, ctx->current,
972 node_name, strlen (node_name),
973 NODE_TYPE_MENU_PLACEHOLDER,
976 node_prepend_ui_reference (NODE_INFO (ctx->current),
977 ctx->merge_id, action_quark);
978 NODE_INFO (ctx->current)->dirty = TRUE;
984 if ((ctx->state == STATE_MENU || ctx->state == STATE_TOOLBAR) &&
985 !strcmp (element_name, "separator"))
989 if (ctx->state == STATE_TOOLBAR)
990 ctx->state = STATE_TOOLITEM;
992 ctx->state = STATE_MENUITEM;
993 node = get_child_node (self, ctx->current,
994 node_name, strlen (node_name),
997 if (NODE_INFO (node)->action_name == 0)
998 NODE_INFO (node)->action_name = action_quark;
1000 node_prepend_ui_reference (NODE_INFO (node),
1001 ctx->merge_id, action_quark);
1002 NODE_INFO (node)->dirty = TRUE;
1004 raise_error = FALSE;
1008 if (ctx->state == STATE_ROOT && !strcmp (element_name, "toolbar"))
1010 ctx->state = STATE_TOOLBAR;
1011 ctx->current = get_child_node (self, ctx->current,
1012 node_name, strlen (node_name),
1015 if (NODE_INFO (ctx->current)->action_name == 0)
1016 NODE_INFO (ctx->current)->action_name = action_quark;
1018 node_prepend_ui_reference (NODE_INFO (ctx->current),
1019 ctx->merge_id, action_quark);
1020 NODE_INFO (ctx->current)->dirty = TRUE;
1022 raise_error = FALSE;
1024 else if (ctx->state == STATE_TOOLBAR && !strcmp (element_name, "toolitem"))
1028 ctx->state = STATE_TOOLITEM;
1029 node = get_child_node (self, ctx->current,
1030 node_name, strlen (node_name),
1033 if (NODE_INFO (node)->action_name == 0)
1034 NODE_INFO (node)->action_name = action_quark;
1036 node_prepend_ui_reference (NODE_INFO (node),
1037 ctx->merge_id, action_quark);
1038 NODE_INFO (node)->dirty = TRUE;
1040 raise_error = FALSE;
1048 gint line_number, char_number;
1050 g_markup_parse_context_get_position (context,
1051 &line_number, &char_number);
1054 G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1055 _("Unexpected start tag '%s' on line %d char %d"),
1057 line_number, char_number);
1062 end_element_handler (GMarkupParseContext *context,
1063 const gchar *element_name,
1067 ParseContext *ctx = user_data;
1073 /* no need to GError here, GMarkup already catches this */
1076 ctx->current = NULL;
1077 ctx->state = STATE_END;
1081 ctx->current = ctx->current->parent;
1082 if (NODE_INFO (ctx->current)->type == NODE_TYPE_ROOT)
1083 ctx->state = STATE_ROOT;
1084 /* else, stay in same state */
1086 case STATE_MENUITEM:
1087 ctx->state = STATE_MENU;
1089 case STATE_TOOLITEM:
1090 ctx->state = STATE_TOOLBAR;
1096 cleanup (GMarkupParseContext *context,
1100 ParseContext *ctx = user_data;
1102 ctx->current = NULL;
1103 /* should also walk through the tree and get rid of nodes related to
1104 * this UI file's tag */
1106 gtk_ui_manager_remove_ui (ctx->self, ctx->merge_id);
1110 xml_isspace (char c)
1112 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
1116 text_handler (GMarkupParseContext *context,
1126 end = text + text_len;
1127 while (p != end && xml_isspace (*p))
1132 gint line_number, char_number;
1134 g_markup_parse_context_get_position (context,
1135 &line_number, &char_number);
1138 G_MARKUP_ERROR_INVALID_CONTENT,
1139 _("Unexpected character data on line %d char %d"),
1140 line_number, char_number);
1145 static GMarkupParser ui_parser = {
1146 start_element_handler,
1147 end_element_handler,
1154 add_ui_from_string (GtkUIManager *self,
1155 const gchar *buffer,
1157 gboolean needs_root,
1160 ParseContext ctx = { 0 };
1161 GMarkupParseContext *context;
1163 ctx.state = STATE_START;
1166 ctx.merge_id = gtk_ui_manager_new_merge_id (self);
1168 context = g_markup_parse_context_new (&ui_parser, 0, &ctx, NULL);
1171 if (!g_markup_parse_context_parse (context, "<ui>", -1, error))
1174 if (!g_markup_parse_context_parse (context, buffer, length, error))
1178 if (!g_markup_parse_context_parse (context, "</ui>", -1, error))
1181 if (!g_markup_parse_context_end_parse (context, error))
1184 g_markup_parse_context_free (context);
1186 queue_update (self);
1188 g_signal_emit (self, merge_signals[CHANGED], 0);
1190 return ctx.merge_id;
1194 g_markup_parse_context_free (context);
1200 * gtk_ui_manager_add_ui_from_string:
1201 * @self: a #GtkUIManager object
1202 * @buffer: the string to parse
1203 * @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
1204 * @error: return location for an error
1206 * Parses a string containing a <link linkend="XML-UI">UI definition</link> and
1207 * merges it with the current contents of @self. An enclosing <ui>
1208 * element is added if it is missing.
1210 * Return value: The merge id for the merged UI. The merge id can be used
1211 * to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred,
1212 * the return value is 0.
1217 gtk_ui_manager_add_ui_from_string (GtkUIManager *self,
1218 const gchar *buffer,
1222 gboolean needs_root = TRUE;
1226 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), 0);
1227 g_return_val_if_fail (buffer != NULL, 0);
1230 length = strlen (buffer);
1233 end = buffer + length;
1234 while (p != end && xml_isspace (*p))
1237 if (end - p >= 4 && strncmp (p, "<ui>", 4) == 0)
1240 return add_ui_from_string (self, buffer, length, needs_root, error);
1244 * gtk_ui_manager_add_ui_from_file:
1245 * @self: a #GtkUIManager object
1246 * @filename: the name of the file to parse
1247 * @error: return location for an error
1249 * Parses a file containing a <link linkend="XML-UI">UI definition</link> and
1250 * merges it with the current contents of @self.
1252 * Return value: The merge id for the merged UI. The merge id can be used
1253 * to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred,
1254 * the return value is 0.
1259 gtk_ui_manager_add_ui_from_file (GtkUIManager *self,
1260 const gchar *filename,
1267 g_return_val_if_fail (GTK_IS_UI_MANAGER (self), 0);
1269 if (!g_file_get_contents (filename, &buffer, &length, error))
1272 res = add_ui_from_string (self, buffer, length, FALSE, error);
1279 * gtk_ui_manager_add_ui:
1280 * @self: a #GtkUIManager
1281 * @merge_id: the merge id for the merged UI, see gtk_ui_manager_new_merge_id()
1283 * @name: the name for the added UI element
1284 * @action: the name of the action to be proxied, or %NULL to add a separator
1285 * @type: the type of UI element to add.
1286 * @top: if %TRUE, the UI element is added before its siblings, otherwise it
1287 * is added after its siblings.
1289 * Adds a UI element to the current contents of @self.
1291 * If @type is %GTK_UI_MANAGER_AUTO, GTK+ inserts a menuitem, toolitem or
1292 * separator if such an element can be inserted at the place determined by
1293 * @path. Otherwise @type must indicate an element that can be inserted at
1294 * the place determined by @path.
1299 gtk_ui_manager_add_ui (GtkUIManager *self,
1303 const gchar *action,
1304 GtkUIManagerItemType type,
1310 GQuark action_quark = 0;
1312 g_return_if_fail (GTK_IS_UI_MANAGER (self));
1313 g_return_if_fail (merge_id > 0);
1314 g_return_if_fail (name != NULL);
1316 node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
1321 node_type = NODE_TYPE_UNDECIDED;
1323 switch (NODE_INFO (node)->type)
1325 case NODE_TYPE_MENUBAR:
1326 case NODE_TYPE_MENU:
1327 case NODE_TYPE_POPUP:
1328 case NODE_TYPE_MENU_PLACEHOLDER:
1331 case GTK_UI_MANAGER_AUTO:
1333 node_type = NODE_TYPE_MENUITEM;
1335 node_type = NODE_TYPE_SEPARATOR;
1337 case GTK_UI_MANAGER_MENU:
1338 node_type = NODE_TYPE_MENU;
1340 case GTK_UI_MANAGER_MENUITEM:
1341 node_type = NODE_TYPE_MENUITEM;
1343 case GTK_UI_MANAGER_SEPARATOR:
1344 node_type = NODE_TYPE_SEPARATOR;
1346 case GTK_UI_MANAGER_PLACEHOLDER:
1347 node_type = NODE_TYPE_MENU_PLACEHOLDER;
1353 case NODE_TYPE_TOOLBAR:
1354 case NODE_TYPE_TOOLBAR_PLACEHOLDER:
1357 case GTK_UI_MANAGER_AUTO:
1359 node_type = NODE_TYPE_TOOLITEM;
1361 node_type = NODE_TYPE_SEPARATOR;
1363 case GTK_UI_MANAGER_TOOLITEM:
1364 node_type = NODE_TYPE_TOOLITEM;
1366 case GTK_UI_MANAGER_SEPARATOR:
1367 node_type = NODE_TYPE_SEPARATOR;
1369 case GTK_UI_MANAGER_PLACEHOLDER:
1370 node_type = NODE_TYPE_MENU_PLACEHOLDER;
1376 case NODE_TYPE_ROOT:
1379 case GTK_UI_MANAGER_MENUBAR:
1380 node_type = NODE_TYPE_MENUBAR;
1382 case GTK_UI_MANAGER_TOOLBAR:
1383 node_type = NODE_TYPE_TOOLBAR;
1385 case GTK_UI_MANAGER_POPUP:
1386 node_type = NODE_TYPE_POPUP;
1388 case GTK_UI_MANAGER_ACCELERATOR:
1389 node_type = NODE_TYPE_ACCELERATOR;
1399 if (node_type == NODE_TYPE_UNDECIDED)
1402 child = get_child_node (self, node,
1403 name, strlen (name),
1404 node_type, TRUE, top);
1407 action_quark = g_quark_from_string (action);
1409 node_prepend_ui_reference (NODE_INFO (child),
1410 merge_id, action_quark);
1412 if (NODE_INFO (node)->action_name == 0)
1413 NODE_INFO (child)->action_name = action_quark;
1415 NODE_INFO (child)->dirty = TRUE;
1417 queue_update (self);
1419 g_signal_emit (self, merge_signals[CHANGED], 0);
1423 remove_ui (GNode *node,
1426 guint merge_id = GPOINTER_TO_UINT (user_data);
1428 node_remove_ui_reference (NODE_INFO (node), merge_id);
1430 return FALSE; /* continue */
1434 * gtk_ui_manager_remove_ui:
1435 * @self: a #GtkUIManager object
1436 * @merge_id: a merge id as returned by gtk_ui_manager_add_ui_from_string()
1438 * Unmerges the part of @self<!-- -->s content identified by @merge_id.
1443 gtk_ui_manager_remove_ui (GtkUIManager *self,
1446 g_node_traverse (self->private_data->root_node,
1447 G_POST_ORDER, G_TRAVERSE_ALL, -1,
1448 remove_ui, GUINT_TO_POINTER (merge_id));
1450 queue_update (self);
1452 g_signal_emit (self, merge_signals[CHANGED], 0);
1455 /* -------------------- Updates -------------------- */
1459 get_action_by_name (GtkUIManager *merge,
1460 const char *action_name)
1468 for (tmp = merge->private_data->action_groups; tmp != NULL; tmp = tmp->next)
1470 GtkActionGroup *action_group = tmp->data;
1473 action = gtk_action_group_get_action (action_group, action_name);
1483 find_menu_position (GNode *node,
1484 GtkWidget **menushell_p,
1487 GtkWidget *menushell;
1490 g_return_val_if_fail (node != NULL, FALSE);
1491 g_return_val_if_fail (NODE_INFO (node)->type == NODE_TYPE_MENU ||
1492 NODE_INFO (node)->type == NODE_TYPE_POPUP ||
1493 NODE_INFO (node)->type == NODE_TYPE_MENU_PLACEHOLDER ||
1494 NODE_INFO (node)->type == NODE_TYPE_MENUITEM ||
1495 NODE_INFO (node)->type == NODE_TYPE_SEPARATOR,
1498 /* first sibling -- look at parent */
1499 if (node->prev == NULL)
1504 parent = node->parent;
1505 switch (NODE_INFO (parent)->type)
1507 case NODE_TYPE_MENUBAR:
1508 case NODE_TYPE_POPUP:
1509 menushell = NODE_INFO (parent)->proxy;
1512 case NODE_TYPE_MENU:
1513 menushell = NODE_INFO (parent)->proxy;
1514 if (GTK_IS_MENU_ITEM (menushell))
1515 menushell = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menushell));
1516 siblings = gtk_container_get_children (GTK_CONTAINER (menushell));
1517 if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
1522 case NODE_TYPE_MENU_PLACEHOLDER:
1523 menushell = gtk_widget_get_parent (NODE_INFO (parent)->proxy);
1524 g_return_val_if_fail (GTK_IS_MENU_SHELL (menushell), FALSE);
1525 pos = g_list_index (GTK_MENU_SHELL (menushell)->children,
1526 NODE_INFO (parent)->proxy) + 1;
1529 g_warning("%s: bad parent node type %d", G_STRLOC,
1530 NODE_INFO (parent)->type);
1536 GtkWidget *prev_child;
1539 sibling = node->prev;
1540 if (NODE_INFO (sibling)->type == NODE_TYPE_MENU_PLACEHOLDER)
1541 prev_child = NODE_INFO (sibling)->extra; /* second Separator */
1543 prev_child = NODE_INFO (sibling)->proxy;
1545 g_return_val_if_fail (GTK_IS_WIDGET (prev_child), FALSE);
1546 menushell = gtk_widget_get_parent (prev_child);
1547 g_return_val_if_fail (GTK_IS_MENU_SHELL (menushell), FALSE);
1549 pos = g_list_index (GTK_MENU_SHELL (menushell)->children, prev_child) + 1;
1553 *menushell_p = menushell;
1561 find_toolbar_position (GNode *node,
1562 GtkWidget **toolbar_p,
1568 g_return_val_if_fail (node != NULL, FALSE);
1569 g_return_val_if_fail (NODE_INFO (node)->type == NODE_TYPE_TOOLBAR ||
1570 NODE_INFO (node)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER ||
1571 NODE_INFO (node)->type == NODE_TYPE_TOOLITEM ||
1572 NODE_INFO (node)->type == NODE_TYPE_SEPARATOR,
1575 /* first sibling -- look at parent */
1576 if (node->prev == NULL)
1580 parent = node->parent;
1581 switch (NODE_INFO (parent)->type)
1583 case NODE_TYPE_TOOLBAR:
1584 toolbar = NODE_INFO (parent)->proxy;
1587 case NODE_TYPE_TOOLBAR_PLACEHOLDER:
1588 toolbar = gtk_widget_get_parent (NODE_INFO (parent)->proxy);
1589 g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), FALSE);
1590 pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar),
1591 GTK_TOOL_ITEM (NODE_INFO (parent)->proxy)) + 1;
1594 g_warning ("%s: bad parent node type %d", G_STRLOC,
1595 NODE_INFO (parent)->type);
1601 GtkWidget *prev_child;
1604 sibling = node->prev;
1605 if (NODE_INFO (sibling)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER)
1606 prev_child = NODE_INFO (sibling)->extra; /* second Separator */
1608 prev_child = NODE_INFO (sibling)->proxy;
1610 g_return_val_if_fail (GTK_IS_WIDGET (prev_child), FALSE);
1611 toolbar = gtk_widget_get_parent (prev_child);
1612 g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), FALSE);
1614 pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar),
1615 GTK_TOOL_ITEM (prev_child)) + 1;
1619 *toolbar_p = toolbar;
1627 SEPARATOR_MODE_SMART,
1628 SEPARATOR_MODE_VISIBLE,
1629 SEPARATOR_MODE_HIDDEN
1633 update_smart_separators (GtkWidget *proxy)
1635 GtkWidget *parent = NULL;
1637 if (GTK_IS_MENU (proxy) || GTK_IS_TOOLBAR (proxy))
1639 else if (GTK_IS_MENU_ITEM (proxy) || GTK_IS_TOOL_ITEM (proxy))
1640 parent = gtk_widget_get_parent (proxy);
1645 GList *children, *cur, *last;
1647 children = gtk_container_get_children (GTK_CONTAINER (parent));
1654 if (GTK_IS_SEPARATOR_MENU_ITEM (cur->data) ||
1655 GTK_IS_SEPARATOR_TOOL_ITEM (cur->data))
1658 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cur->data),
1659 "gtk-separator-mode"));
1662 case SEPARATOR_MODE_VISIBLE:
1663 gtk_widget_show (GTK_WIDGET (cur->data));
1667 case SEPARATOR_MODE_HIDDEN:
1668 gtk_widget_hide (GTK_WIDGET (cur->data));
1670 case SEPARATOR_MODE_SMART:
1673 gtk_widget_show (GTK_WIDGET (cur->data));
1678 gtk_widget_hide (GTK_WIDGET (cur->data));
1682 else if (GTK_WIDGET_VISIBLE (cur->data))
1685 if (GTK_IS_TEAROFF_MENU_ITEM (cur->data))
1695 gtk_widget_hide (GTK_WIDGET (last->data));
1700 update_node (GtkUIManager *self,
1702 gboolean add_tearoffs)
1707 #ifdef DEBUG_UI_MANAGER
1711 g_return_if_fail (node != NULL);
1712 g_return_if_fail (NODE_INFO (node) != NULL);
1714 info = NODE_INFO (node);
1716 #ifdef DEBUG_UI_MANAGER
1717 g_print ("update_node name=%s dirty=%d (", info->name, info->dirty);
1718 for (tmp = info->uifiles; tmp != NULL; tmp = tmp->next)
1720 NodeUIReference *ref = tmp->data;
1721 g_print("%s:%u", g_quark_to_string (ref->action_quark), ref->merge_id);
1730 const gchar *action_name;
1731 NodeUIReference *ref;
1733 if (info->uifiles == NULL) {
1734 /* We may need to remove this node.
1735 * This must be done in post order
1737 goto recurse_children;
1740 ref = info->uifiles->data;
1741 action_name = g_quark_to_string (ref->action_quark);
1742 action = get_action_by_name (self, action_name);
1744 info->dirty = FALSE;
1746 /* Check if the node doesn't have an action and must have an action */
1747 if (action == NULL &&
1748 info->type != NODE_TYPE_ROOT &&
1749 info->type != NODE_TYPE_MENUBAR &&
1750 info->type != NODE_TYPE_TOOLBAR &&
1751 info->type != NODE_TYPE_SEPARATOR &&
1752 info->type != NODE_TYPE_MENU_PLACEHOLDER &&
1753 info->type != NODE_TYPE_TOOLBAR_PLACEHOLDER)
1755 g_warning ("%s: missing action", info->name);
1757 goto recurse_children;
1761 gtk_action_set_accel_group (action, self->private_data->accel_group);
1763 /* If the widget already has a proxy and the action hasn't changed, then
1764 * we only have to update the tearoff menu items.
1766 if (info->proxy != NULL && action == info->action)
1768 if (info->type == NODE_TYPE_MENU)
1773 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
1774 siblings = gtk_container_get_children (GTK_CONTAINER (menu));
1775 if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
1776 g_object_set (G_OBJECT (siblings->data), "visible", add_tearoffs, 0);
1779 goto recurse_children;
1784 case NODE_TYPE_MENUBAR:
1785 if (info->proxy == NULL)
1787 info->proxy = gtk_menu_bar_new ();
1788 gtk_widget_show (info->proxy);
1789 g_signal_emit (self, merge_signals[ADD_WIDGET], 0, info->proxy);
1792 case NODE_TYPE_POPUP:
1793 if (info->proxy == NULL)
1794 info->proxy = gtk_menu_new ();
1796 case NODE_TYPE_MENU:
1798 GtkWidget *prev_submenu = NULL;
1801 /* remove the proxy if it is of the wrong type ... */
1802 if (info->proxy && G_OBJECT_TYPE (info->proxy) !=
1803 GTK_ACTION_GET_CLASS (action)->menu_item_type)
1805 prev_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
1808 g_object_ref (prev_submenu);
1809 gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
1811 gtk_action_disconnect_proxy (info->action, info->proxy);
1812 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1816 /* create proxy if needed ... */
1817 if (info->proxy == NULL)
1819 GtkWidget *menushell;
1822 if (find_menu_position (node, &menushell, &pos))
1826 info->proxy = gtk_action_create_menu_item (action);
1827 menu = gtk_menu_new ();
1828 tearoff = gtk_tearoff_menu_item_new ();
1829 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
1830 gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), menu);
1831 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos);
1835 gtk_action_connect_proxy (action, info->proxy);
1838 gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy),
1840 g_object_unref (prev_submenu);
1842 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
1843 siblings = gtk_container_get_children (GTK_CONTAINER (menu));
1844 if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
1845 g_object_set (G_OBJECT (siblings->data), "visible", add_tearoffs, 0);
1848 case NODE_TYPE_UNDECIDED:
1849 g_warning ("found 'undecided node!");
1851 case NODE_TYPE_ROOT:
1853 case NODE_TYPE_TOOLBAR:
1854 if (info->proxy == NULL)
1856 info->proxy = gtk_toolbar_new ();
1857 gtk_widget_show (info->proxy);
1858 g_signal_emit (self, merge_signals[ADD_WIDGET], 0, info->proxy);
1861 case NODE_TYPE_MENU_PLACEHOLDER:
1862 /* create menu items for placeholders if necessary ... */
1863 if (!GTK_IS_SEPARATOR_MENU_ITEM (info->proxy) ||
1864 !GTK_IS_SEPARATOR_MENU_ITEM (info->extra))
1868 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1874 gtk_container_remove (GTK_CONTAINER (info->extra->parent),
1879 if (info->proxy == NULL)
1881 GtkWidget *menushell;
1884 if (find_menu_position (node, &menushell, &pos))
1886 info->proxy = gtk_separator_menu_item_new ();
1887 g_object_set_data (G_OBJECT (info->proxy),
1888 "gtk-separator-mode",
1889 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
1890 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
1891 NODE_INFO (node)->proxy, pos);
1893 info->extra = gtk_separator_menu_item_new ();
1894 g_object_set_data (G_OBJECT (info->extra),
1895 "gtk-separator-mode",
1896 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
1897 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
1898 NODE_INFO (node)->extra, pos+1);
1902 case NODE_TYPE_TOOLBAR_PLACEHOLDER:
1903 /* create toolbar items for placeholders if necessary ... */
1904 if (!GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy) ||
1905 !GTK_IS_SEPARATOR_TOOL_ITEM (info->extra))
1909 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1915 gtk_container_remove (GTK_CONTAINER (info->extra->parent),
1920 if (info->proxy == NULL)
1925 if (find_toolbar_position (node, &toolbar, &pos))
1929 item = gtk_separator_tool_item_new ();
1930 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
1931 info->proxy = GTK_WIDGET (item);
1932 g_object_set_data (G_OBJECT (info->proxy),
1933 "gtk-separator-mode",
1934 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
1936 item = gtk_separator_tool_item_new ();
1937 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos+1);
1938 info->extra = GTK_WIDGET (item);
1939 g_object_set_data (G_OBJECT (info->extra),
1940 "gtk-separator-mode",
1941 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
1945 case NODE_TYPE_MENUITEM:
1946 /* remove the proxy if it is of the wrong type ... */
1947 if (info->proxy && G_OBJECT_TYPE (info->proxy) !=
1948 GTK_ACTION_GET_CLASS (action)->menu_item_type)
1950 g_signal_handlers_disconnect_by_func (info->proxy,
1951 G_CALLBACK (update_smart_separators),
1953 gtk_action_disconnect_proxy (info->action, info->proxy);
1954 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1958 /* create proxy if needed ... */
1959 if (info->proxy == NULL)
1961 GtkWidget *menushell;
1964 if (find_menu_position (node, &menushell, &pos))
1966 info->proxy = gtk_action_create_menu_item (action);
1968 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
1974 g_signal_handlers_disconnect_by_func (info->proxy,
1975 G_CALLBACK (update_smart_separators),
1977 gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
1978 gtk_action_connect_proxy (action, info->proxy);
1980 g_signal_connect (info->proxy, "notify::visible",
1981 G_CALLBACK (update_smart_separators), 0);
1983 case NODE_TYPE_TOOLITEM:
1984 /* remove the proxy if it is of the wrong type ... */
1985 if (info->proxy && G_OBJECT_TYPE (info->proxy) !=
1986 GTK_ACTION_GET_CLASS (action)->toolbar_item_type)
1988 g_signal_handlers_disconnect_by_func (info->proxy,
1989 G_CALLBACK (update_smart_separators),
1991 gtk_action_disconnect_proxy (info->action, info->proxy);
1992 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
1996 /* create proxy if needed ... */
1997 if (info->proxy == NULL)
2002 if (find_toolbar_position (node, &toolbar, &pos))
2004 info->proxy = gtk_action_create_tool_item (action);
2006 gtk_toolbar_insert (GTK_TOOLBAR (toolbar),
2007 GTK_TOOL_ITEM (info->proxy), pos);
2012 g_signal_handlers_disconnect_by_func (info->proxy,
2013 G_CALLBACK (update_smart_separators),
2015 gtk_action_connect_proxy (action, info->proxy);
2017 g_signal_connect (info->proxy, "notify::visible",
2018 G_CALLBACK (update_smart_separators), 0);
2020 case NODE_TYPE_SEPARATOR:
2021 if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR ||
2022 NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER)
2027 if (GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy))
2029 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2034 if (find_toolbar_position (node, &toolbar, &pos))
2036 GtkToolItem *item = gtk_separator_tool_item_new ();
2037 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
2038 info->proxy = GTK_WIDGET (item);
2039 g_object_set_data (G_OBJECT (info->proxy),
2040 "gtk-separator-mode",
2041 GINT_TO_POINTER (SEPARATOR_MODE_SMART));
2042 gtk_widget_show (info->proxy);
2047 GtkWidget *menushell;
2050 if (GTK_IS_SEPARATOR_MENU_ITEM (info->proxy))
2052 gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
2057 if (find_menu_position (node, &menushell, &pos))
2059 info->proxy = gtk_separator_menu_item_new ();
2060 g_object_set_data (G_OBJECT (info->proxy),
2061 "gtk-separator-mode",
2062 GINT_TO_POINTER (SEPARATOR_MODE_SMART));
2063 gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
2065 gtk_widget_show (info->proxy);
2069 case NODE_TYPE_ACCELERATOR:
2070 gtk_action_connect_accelerator (action);
2075 g_object_ref (action);
2077 g_object_unref (info->action);
2078 info->action = action;
2082 /* process children */
2083 child = node->children;
2089 child = current->next;
2090 update_node (self, current, add_tearoffs && (info->type != NODE_TYPE_POPUP));
2095 if (info->type == NODE_TYPE_MENU)
2096 update_smart_separators (gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy)));
2097 else if (info->type == NODE_TYPE_TOOLBAR)
2098 update_smart_separators (info->proxy);
2101 /* handle cleanup of dead nodes */
2102 if (node->children == NULL && info->uifiles == NULL)
2105 gtk_widget_destroy (info->proxy);
2107 gtk_widget_destroy (info->extra);
2108 if (info->type == NODE_TYPE_ACCELERATOR)
2109 gtk_action_disconnect_accelerator (info->action);
2111 g_node_destroy (node);
2116 do_updates (GtkUIManager *self)
2118 /* this function needs to check through the tree for dirty nodes.
2119 * For such nodes, it needs to do the following:
2121 * 1) check if they are referenced by any loaded UI files anymore.
2122 * In which case, the proxy widget should be destroyed, unless
2123 * there are any subnodes.
2125 * 2) lookup the action for this node again. If it is different to
2126 * the current one (or if no previous action has been looked up),
2127 * the proxy is reconnected to the new action (or a new proxy widget
2128 * is created and added to the parent container).
2130 update_node (self, self->private_data->root_node,
2131 self->private_data->add_tearoffs);
2133 self->private_data->update_tag = 0;
2139 queue_update (GtkUIManager *self)
2141 if (self->private_data->update_tag != 0)
2144 self->private_data->update_tag = g_idle_add ((GSourceFunc)do_updates, self);
2149 * gtk_ui_manager_ensure_update:
2150 * @self: a #GtkUIManager
2152 * Makes sure that all pending updates to the UI have been completed.
2154 * This may occasionally be necessary, since #GtkUIManager updates the
2155 * UI in an idle function. A typical example where this function is
2156 * useful is to enforce that the menubar and toolbar have been added to
2157 * the main window before showing it:
2160 * gtk_container_add (GTK_CONTAINER (window), vbox);
2161 * g_signal_connect (merge, "add_widget",
2162 * G_CALLBACK (add_widget), vbox);
2163 * gtk_ui_manager_add_ui_from_file (merge, "my-menus");
2164 * gtk_ui_manager_add_ui_from_file (merge, "my-toolbars");
2165 * gtk_ui_manager_ensure_update (merge);
2166 * gtk_widget_show (window);
2168 * </informalexample>
2173 gtk_ui_manager_ensure_update (GtkUIManager *self)
2175 if (self->private_data->update_tag != 0)
2177 g_source_remove (self->private_data->update_tag);
2183 dirty_traverse_func (GNode *node,
2186 NODE_INFO (node)->dirty = TRUE;
2191 dirty_all_nodes (GtkUIManager *self)
2193 g_node_traverse (self->private_data->root_node,
2194 G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2195 dirty_traverse_func, NULL);
2196 queue_update (self);
2199 static const gchar *open_tag_format[] = {
2202 "%*s<menubar name=\"%s\">\n",
2203 "%*s<menu name='%s' action=\"%s\">\n",
2204 "%*s<toolbar name=\"%s\">\n",
2205 "%*s<placeholder name=\"%s\">\n",
2206 "%*s<placeholder name=\"%s\">\n",
2207 "%*s<popup name='%s' action=\"%s\">\n",
2208 "%*s<menuitem name=\"%s\" action=\"%s\"/>\n",
2209 "%*s<toolitem name=\"%s\" action=\"%s\"/>\n",
2210 "%*s<separator name=\"%s\"/>\n",
2211 "%*s<accelerator name=\"%s\" action=\"%s\"/>\n",
2214 static const gchar *close_tag_format[] = {
2215 "%*s</UNDECIDED>\n",
2220 "%*s</placeholder>\n",
2221 "%*s</placeholder>\n",
2230 print_node (GtkUIManager *self,
2240 g_string_append_printf (buffer, open_tag_format[mnode->type],
2243 g_quark_to_string (mnode->action_name));
2245 for (child = node->children; child != NULL; child = child->next)
2246 print_node (self, child, indent_level + 2, buffer);
2248 g_string_append_printf (buffer, close_tag_format[mnode->type],
2254 * gtk_ui_manager_get_ui:
2255 * @self: a #GtkUIManager
2257 * Creates a <link linkend="XML-UI">UI definition</link> of the merged UI.
2259 * Return value: A newly allocated string containing an XML representation of
2265 gtk_ui_manager_get_ui (GtkUIManager *self)
2269 buffer = g_string_new (NULL);
2271 gtk_ui_manager_ensure_update (self);
2273 print_node (self, self->private_data->root_node, 0, buffer);
2275 return g_string_free (buffer, FALSE);