X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkuimanager.c;h=75189aac3ecc695ec664994f695a554f1f5c795c;hb=HEAD;hp=c624ba89857b4dc43280b3814f48b13819f69737;hpb=fbb2e3f4f51a4c0ccc8f2bf511f4766293fc4af0;p=~andy%2Fgtk diff --git a/gtk/gtkuimanager.c b/gtk/gtkuimanager.c index c624ba898..75189aac3 100644 --- a/gtk/gtkuimanager.c +++ b/gtk/gtkuimanager.c @@ -14,9 +14,7 @@ * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with the Gnome Library; see the file COPYING.LIB. If not, - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -25,29 +23,292 @@ * Modified by the GTK+ Team and others 2003. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ -#include +#include "config.h" #include +#include "gtkaccellabel.h" +#include "gtkactivatable.h" +#include "gtkbuildable.h" +#include "gtkimagemenuitem.h" #include "gtkintl.h" #include "gtkmarshalers.h" #include "gtkmenu.h" +#include "gtkmenushellprivate.h" #include "gtkmenubar.h" -#include "gtkmenushell.h" #include "gtkmenutoolbutton.h" #include "gtkseparatormenuitem.h" #include "gtkseparatortoolitem.h" -#include "gtktearoffmenuitem.h" #include "gtktoolbar.h" -#include "gtkuimanager.h" +#include "gtkwindow.h" #include "gtkprivate.h" -#include "gtkalias.h" + +#undef GDK_DEPRECATED +#undef GDK_DEPRECATED_FOR +#define GDK_DEPRECATED +#define GDK_DEPRECATED_FOR(f) + +#include "gtkuimanager.h" +#include "deprecated/gtktearoffmenuitem.h" + +/** + * SECTION:gtkuimanager + * @Short_description: Constructing menus and toolbars from an XML description + * @Title: GtkUIManager + * @See_also:#GtkBuilder + * + * A #GtkUIManager constructs a user interface (menus and toolbars) from + * one or more UI definitions, which reference actions from one or more + * action groups. + * + * + * UI Definitions + * + * The UI definitions are specified in an XML format which can be + * roughly described by the following DTD. + * + * + * Do not confuse the GtkUIManager UI Definitions described here with + * the similarly named GtkBuilder UI + * Definitions. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * ]]> + * + * There are some additional restrictions beyond those specified in the + * DTD, e.g. every toolitem must have a toolbar in its anchestry and + * every menuitem must have a menubar or popup in its anchestry. Since + * a #GMarkup parser is used to parse the UI description, it must not only + * be valid XML, but valid #GMarkup. + * + * If a name is not specified, it defaults to the action. If an action is + * not specified either, the element name is used. The name and action + * attributes must not contain '/' characters after parsing (since that + * would mess up path lookup) and must be usable as XML attributes when + * enclosed in doublequotes, thus they must not '"' characters or references + * to the " entity. + * + * + * A UI definition + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * ]]> + * + * + * The constructed widget hierarchy is very similar to the element tree + * of the XML, with the exception that placeholders are merged into their + * parents. The correspondence of XML elements to widgets should be + * almost obvious: + * + * + * menubar + * a #GtkMenuBar + * + * + * toolbar + * a #GtkToolbar + * + * + * popup + * a toplevel #GtkMenu + * + * + * menu + * a #GtkMenu attached to a menuitem + * + * + * menuitem + * a #GtkMenuItem subclass, the exact type depends on the + * action + * + * + * toolitem + * a #GtkToolItem subclass, the exact type depends on the + * action. Note that toolitem elements may contain a menu element, but only + * if their associated action specifies a #GtkMenuToolButton as proxy. + * + * + * separator + * a #GtkSeparatorMenuItem or + * #GtkSeparatorToolItem + * + * + * accelerator + * a keyboard accelerator + * + * + * + * The "position" attribute determines where a constructed widget is positioned + * wrt. to its siblings in the partially constructed tree. If it is + * "top", the widget is prepended, otherwise it is appended. + * + * + * + * UI Merging + * + * The most remarkable feature of #GtkUIManager is that it can overlay a set + * of menuitems and toolitems over another one, and demerge them later. + * + * Merging is done based on the names of the XML elements. Each element is + * identified by a path which consists of the names of its anchestors, separated + * by slashes. For example, the menuitem named "Left" in the example above + * has the path /ui/menubar/JustifyMenu/Left and the + * toolitem with the same name has path + * /ui/toolbar1/JustifyToolItems/Left. + * + * + * + * Accelerators + * + * Every action has an accelerator path. Accelerators are installed together with + * menuitem proxies, but they can also be explicitly added with <accelerator> + * elements in the UI definition. This makes it possible to have accelerators for + * actions even if they have no visible proxies. + * + * + * + * Smart Separators + * + * The separators created by #GtkUIManager are "smart", i.e. they do not show up + * in the UI unless they end up between two visible menu or tool items. Separators + * which are located at the very beginning or end of the menu or toolbar + * containing them, or multiple separators next to each other, are hidden. This + * is a useful feature, since the merging of UI elements from multiple sources + * can make it hard or impossible to determine in advance whether a separator + * will end up in such an unfortunate position. + * + * For separators in toolbars, you can set expand="true" to + * turn them from a small, visible separator to an expanding, invisible one. + * Toolitems following an expanding separator are effectively right-aligned. + * + * + * + * Empty Menus + * + * Submenus pose similar problems to separators inconnection with merging. It is + * impossible to know in advance whether they will end up empty after merging. + * #GtkUIManager offers two ways to treat empty submenus: + * + * + * make them disappear by hiding the menu item they're attached to + * + * + * add an insensitive "Empty" item + * + * + * The behaviour is chosen based on the "hide_if_empty" property of the action + * to which the submenu is associated. + * + * + * + * GtkUIManager as GtkBuildable + * + * The GtkUIManager implementation of the GtkBuildable interface accepts + * GtkActionGroup objects as <child> elements in UI definitions. + * + * A GtkUIManager UI definition as described above can be embedded in + * an GtkUIManager <object> element in a GtkBuilder UI definition. + * + * The widgets that are constructed by a GtkUIManager can be embedded in + * other parts of the constructed user interface with the help of the + * "constructor" attribute. See the example below. + * + * + * An embedded GtkUIManager UI definition + * + * + * + * + * + * _File + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * ]]> + * + * + * + */ + #undef DEBUG_UI_MANAGER -typedef enum +typedef enum { NODE_TYPE_UNDECIDED, NODE_TYPE_ROOT, @@ -79,9 +340,11 @@ struct _Node { guint dirty : 1; guint expand : 1; /* used for separators */ + guint popup_accels : 1; + guint always_show_image_set : 1; /* used for menu items */ + guint always_show_image : 1; /* used for menu items */ }; -#define GTK_UI_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_UI_MANAGER, GtkUIManagerPrivate)) struct _GtkUIManagerPrivate { @@ -107,8 +370,6 @@ struct _NodeUIReference GQuark action_quark; }; -static void gtk_ui_manager_class_init (GtkUIManagerClass *class); -static void gtk_ui_manager_init (GtkUIManager *self); static void gtk_ui_manager_finalize (GObject *object); static void gtk_ui_manager_set_property (GObject *object, guint prop_id, @@ -122,10 +383,10 @@ static GtkWidget * gtk_ui_manager_real_get_widget (GtkUIManager *manager, const gchar *path); static GtkAction * gtk_ui_manager_real_get_action (GtkUIManager *manager, const gchar *path); -static void queue_update (GtkUIManager *self); -static void dirty_all_nodes (GtkUIManager *self); +static void queue_update (GtkUIManager *manager); +static void dirty_all_nodes (GtkUIManager *manager); static void mark_node_dirty (GNode *node); -static GNode * get_child_node (GtkUIManager *self, +static GNode * get_child_node (GtkUIManager *manager, GNode *parent, GNode *sibling, const gchar *childname, @@ -133,7 +394,7 @@ static GNode * get_child_node (GtkUIManager *self, NodeType node_type, gboolean create, gboolean top); -static GNode * get_node (GtkUIManager *self, +static GNode * get_node (GtkUIManager *manager, const gchar *path, NodeType node_type, gboolean create); @@ -144,6 +405,30 @@ static void node_prepend_ui_reference (GNode *node, static void node_remove_ui_reference (GNode *node, guint merge_id); +/* GtkBuildable */ +static void gtk_ui_manager_buildable_init (GtkBuildableIface *iface); +static void gtk_ui_manager_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); +static GObject* gtk_ui_manager_buildable_construct_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name); +static gboolean gtk_ui_manager_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_ui_manager_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); +static void gtk_ui_manager_do_set_add_tearoffs (GtkUIManager *manager, + gboolean add_tearoffs); + + enum { @@ -163,52 +448,19 @@ enum PROP_UI }; -static GObjectClass *parent_class = NULL; static guint ui_manager_signals[LAST_SIGNAL] = { 0 }; -static GMemChunk *merge_node_chunk = NULL; - -GType -gtk_ui_manager_get_type (void) -{ - static GtkType type = 0; - - if (!type) - { - static const GTypeInfo type_info = - { - sizeof (GtkUIManagerClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gtk_ui_manager_class_init, - (GClassFinalizeFunc) NULL, - NULL, - - sizeof (GtkUIManager), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_ui_manager_init, - }; - - type = g_type_register_static (G_TYPE_OBJECT, - I_("GtkUIManager"), - &type_info, 0); - } - return type; -} +G_DEFINE_TYPE_WITH_CODE (GtkUIManager, gtk_ui_manager, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_ui_manager_buildable_init)) static void gtk_ui_manager_class_init (GtkUIManagerClass *klass) { GObjectClass *gobject_class; - parent_class = g_type_class_peek_parent (klass); - gobject_class = G_OBJECT_CLASS (klass); - if (!merge_node_chunk) - merge_node_chunk = g_mem_chunk_create (Node, 64, - G_ALLOC_AND_FREE); - gobject_class->finalize = gtk_ui_manager_finalize; gobject_class->set_property = gtk_ui_manager_set_property; gobject_class->get_property = gtk_ui_manager_get_property; @@ -225,6 +477,9 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) * menus never have tearoff menu items. * * Since: 2.4 + * + * Deprecated: 3.4: Tearoff menus are deprecated and should not + * be used in newly written code. */ g_object_class_install_property (gobject_class, PROP_ADD_TEAROFFS, @@ -232,7 +487,7 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) P_("Add tearoffs to menus"), P_("Whether tearoff menu items should be added to menus"), FALSE, - GTK_PARAM_READWRITE)); + GTK_PARAM_READWRITE | G_PARAM_DEPRECATED)); g_object_class_install_property (gobject_class, PROP_UI, @@ -245,17 +500,17 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) /** * GtkUIManager::add-widget: - * @merge: a #GtkUIManager + * @manager: a #GtkUIManager * @widget: the added widget * - * The add_widget signal is emitted for each generated menubar and toolbar. + * The ::add-widget signal is emitted for each generated menubar and toolbar. * It is not emitted for generated popup menus, which can be obtained by * gtk_ui_manager_get_widget(). * * Since: 2.4 */ ui_manager_signals[ADD_WIDGET] = - g_signal_new (I_("add_widget"), + g_signal_new (I_("add-widget"), G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (GtkUIManagerClass, add_widget), @@ -266,15 +521,15 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) /** * GtkUIManager::actions-changed: - * @merge: a #GtkUIManager + * @manager: a #GtkUIManager * - * The "actions-changed" signal is emitted whenever the set of actions + * The ::actions-changed signal is emitted whenever the set of actions * changes. * * Since: 2.4 */ ui_manager_signals[ACTIONS_CHANGED] = - g_signal_new (I_("actions_changed"), + g_signal_new (I_("actions-changed"), G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (GtkUIManagerClass, actions_changed), @@ -284,11 +539,11 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) /** * GtkUIManager::connect-proxy: - * @uimanager: the ui manager + * @manager: the ui manager * @action: the action * @proxy: the proxy * - * The connect_proxy signal is emitted after connecting a proxy to + * The ::connect-proxy signal is emitted after connecting a proxy to * an action in the group. * * This is intended for simple customizations for which a custom action @@ -298,7 +553,7 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) * Since: 2.4 */ ui_manager_signals[CONNECT_PROXY] = - g_signal_new (I_("connect_proxy"), + g_signal_new (I_("connect-proxy"), G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (GtkUIManagerClass, connect_proxy), @@ -310,17 +565,17 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) /** * GtkUIManager::disconnect-proxy: - * @uimanager: the ui manager + * @manager: the ui manager * @action: the action * @proxy: the proxy * - * The disconnect_proxy signal is emitted after disconnecting a proxy + * The ::disconnect-proxy signal is emitted after disconnecting a proxy * from an action in the group. * * Since: 2.4 */ ui_manager_signals[DISCONNECT_PROXY] = - g_signal_new (I_("disconnect_proxy"), + g_signal_new (I_("disconnect-proxy"), G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (GtkUIManagerClass, disconnect_proxy), @@ -332,10 +587,10 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) /** * GtkUIManager::pre-activate: - * @uimanager: the ui manager + * @manager: the ui manager * @action: the action * - * The pre_activate signal is emitted just before the @action + * The ::pre-activate signal is emitted just before the @action * is activated. * * This is intended for applications to get notification @@ -344,7 +599,7 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) * Since: 2.4 */ ui_manager_signals[PRE_ACTIVATE] = - g_signal_new (I_("pre_activate"), + g_signal_new (I_("pre-activate"), G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (GtkUIManagerClass, pre_activate), @@ -355,10 +610,10 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) /** * GtkUIManager::post-activate: - * @uimanager: the ui manager + * @manager: the ui manager * @action: the action * - * The post_activate signal is emitted just after the @action + * The ::post-activate signal is emitted just after the @action * is activated. * * This is intended for applications to get notification @@ -367,7 +622,7 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) * Since: 2.4 */ ui_manager_signals[POST_ACTIVATE] = - g_signal_new (I_("post_activate"), + g_signal_new (I_("post-activate"), G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (GtkUIManagerClass, post_activate), @@ -388,23 +643,25 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass) static void -gtk_ui_manager_init (GtkUIManager *self) +gtk_ui_manager_init (GtkUIManager *manager) { guint merge_id; GNode *node; - self->private_data = GTK_UI_MANAGER_GET_PRIVATE (self); + manager->private_data = G_TYPE_INSTANCE_GET_PRIVATE (manager, + GTK_TYPE_UI_MANAGER, + GtkUIManagerPrivate); - self->private_data->accel_group = gtk_accel_group_new (); + manager->private_data->accel_group = gtk_accel_group_new (); - self->private_data->root_node = NULL; - self->private_data->action_groups = NULL; + manager->private_data->root_node = NULL; + manager->private_data->action_groups = NULL; - self->private_data->last_merge_id = 0; - self->private_data->add_tearoffs = FALSE; + manager->private_data->last_merge_id = 0; + manager->private_data->add_tearoffs = FALSE; - merge_id = gtk_ui_manager_new_merge_id (self); - node = get_child_node (self, NULL, NULL, "ui", 2, + merge_id = gtk_ui_manager_new_merge_id (manager); + node = get_child_node (manager, NULL, NULL, "ui", 2, NODE_TYPE_ROOT, TRUE, FALSE); node_prepend_ui_reference (node, merge_id, 0); } @@ -412,29 +669,101 @@ gtk_ui_manager_init (GtkUIManager *self) static void gtk_ui_manager_finalize (GObject *object) { - GtkUIManager *self = GTK_UI_MANAGER (object); + GtkUIManager *manager = GTK_UI_MANAGER (object); - if (self->private_data->update_tag != 0) + if (manager->private_data->update_tag != 0) { - g_source_remove (self->private_data->update_tag); - self->private_data->update_tag = 0; + g_source_remove (manager->private_data->update_tag); + manager->private_data->update_tag = 0; } - g_node_traverse (self->private_data->root_node, + g_node_traverse (manager->private_data->root_node, G_POST_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)free_node, NULL); - g_node_destroy (self->private_data->root_node); - self->private_data->root_node = NULL; + g_node_destroy (manager->private_data->root_node); + manager->private_data->root_node = NULL; - g_list_foreach (self->private_data->action_groups, - (GFunc) g_object_unref, NULL); - g_list_free (self->private_data->action_groups); - self->private_data->action_groups = NULL; + g_list_free_full (manager->private_data->action_groups, g_object_unref); + manager->private_data->action_groups = NULL; - g_object_unref (self->private_data->accel_group); - self->private_data->accel_group = NULL; + g_object_unref (manager->private_data->accel_group); + manager->private_data->accel_group = NULL; - G_OBJECT_CLASS (parent_class)->finalize (object); + G_OBJECT_CLASS (gtk_ui_manager_parent_class)->finalize (object); +} + +static void +gtk_ui_manager_buildable_init (GtkBuildableIface *iface) +{ + iface->add_child = gtk_ui_manager_buildable_add_child; + iface->construct_child = gtk_ui_manager_buildable_construct_child; + iface->custom_tag_start = gtk_ui_manager_buildable_custom_tag_start; + iface->custom_tag_end = gtk_ui_manager_buildable_custom_tag_end; +} + +static void +gtk_ui_manager_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + GtkUIManager *manager = GTK_UI_MANAGER (buildable); + guint pos; + + g_return_if_fail (GTK_IS_ACTION_GROUP (child)); + + pos = g_list_length (manager->private_data->action_groups); + + gtk_ui_manager_insert_action_group (manager, + GTK_ACTION_GROUP (child), + pos); +} + +static void +child_hierarchy_changed_cb (GtkWidget *widget, + GtkWidget *unused, + GtkUIManager *uimgr) +{ + GtkWidget *toplevel; + GtkAccelGroup *group; + GSList *groups; + + toplevel = gtk_widget_get_toplevel (widget); + if (!toplevel || !GTK_IS_WINDOW (toplevel)) + return; + + group = gtk_ui_manager_get_accel_group (uimgr); + groups = gtk_accel_groups_from_object (G_OBJECT (toplevel)); + if (g_slist_find (groups, group) == NULL) + gtk_window_add_accel_group (GTK_WINDOW (toplevel), group); + + g_signal_handlers_disconnect_by_func (widget, + child_hierarchy_changed_cb, + uimgr); +} + +static GObject * +gtk_ui_manager_buildable_construct_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *id) +{ + GtkWidget *widget; + char *name; + + name = g_strdup_printf ("ui/%s", id); + widget = gtk_ui_manager_get_widget (GTK_UI_MANAGER (buildable), name); + if (!widget) + { + g_error ("Unknown ui manager child: %s\n", name); + g_free (name); + return NULL; + } + g_free (name); + + g_signal_connect (widget, "hierarchy-changed", + G_CALLBACK (child_hierarchy_changed_cb), + GTK_UI_MANAGER (buildable)); + return g_object_ref (widget); } static void @@ -443,12 +772,12 @@ gtk_ui_manager_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - GtkUIManager *self = GTK_UI_MANAGER (object); + GtkUIManager *manager = GTK_UI_MANAGER (object); switch (prop_id) { case PROP_ADD_TEAROFFS: - gtk_ui_manager_set_add_tearoffs (self, g_value_get_boolean (value)); + gtk_ui_manager_do_set_add_tearoffs (manager, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -462,15 +791,15 @@ gtk_ui_manager_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - GtkUIManager *self = GTK_UI_MANAGER (object); + GtkUIManager *manager = GTK_UI_MANAGER (object); switch (prop_id) { case PROP_ADD_TEAROFFS: - g_value_set_boolean (value, self->private_data->add_tearoffs); + g_value_set_boolean (value, manager->private_data->add_tearoffs); break; case PROP_UI: - g_value_set_string (value, gtk_ui_manager_get_ui (self)); + g_value_take_string (value, gtk_ui_manager_get_ui (manager)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -479,16 +808,16 @@ gtk_ui_manager_get_property (GObject *object, } static GtkWidget * -gtk_ui_manager_real_get_widget (GtkUIManager *self, +gtk_ui_manager_real_get_widget (GtkUIManager *manager, const gchar *path) { GNode *node; /* ensure that there are no pending updates before we get the * widget */ - gtk_ui_manager_ensure_update (self); + gtk_ui_manager_ensure_update (manager); - node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE); + node = get_node (manager, path, NODE_TYPE_UNDECIDED, FALSE); if (node == NULL) return NULL; @@ -497,16 +826,16 @@ gtk_ui_manager_real_get_widget (GtkUIManager *self, } static GtkAction * -gtk_ui_manager_real_get_action (GtkUIManager *self, +gtk_ui_manager_real_get_action (GtkUIManager *manager, const gchar *path) { GNode *node; /* ensure that there are no pending updates before we get * the action */ - gtk_ui_manager_ensure_update (self); + gtk_ui_manager_ensure_update (manager); - node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE); + node = get_node (manager, path, NODE_TYPE_UNDECIDED, FALSE); if (node == NULL) return NULL; @@ -533,7 +862,7 @@ gtk_ui_manager_new (void) /** * gtk_ui_manager_get_add_tearoffs: - * @self: a #GtkUIManager + * @manager: a #GtkUIManager * * Returns whether menus generated by this #GtkUIManager * will have tearoff menu items. @@ -541,19 +870,22 @@ gtk_ui_manager_new (void) * Return value: whether tearoff menu items are added * * Since: 2.4 + * + * Deprecated: 3.4: Tearoff menus are deprecated and should not + * be used in newly written code. **/ gboolean -gtk_ui_manager_get_add_tearoffs (GtkUIManager *self) +gtk_ui_manager_get_add_tearoffs (GtkUIManager *manager) { - g_return_val_if_fail (GTK_IS_UI_MANAGER (self), FALSE); + g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), FALSE); - return self->private_data->add_tearoffs; + return manager->private_data->add_tearoffs; } /** * gtk_ui_manager_set_add_tearoffs: - * @self: a #GtkUIManager + * @manager: a #GtkUIManager * @add_tearoffs: whether tearoff menu items are added * * Sets the "add_tearoffs" property, which controls whether menus @@ -563,22 +895,32 @@ gtk_ui_manager_get_add_tearoffs (GtkUIManager *self) * menus never have tearoff menu items. * * Since: 2.4 + * + * Deprecated: 3.4: Tearoff menus are deprecated and should not + * be used in newly written code. **/ -void -gtk_ui_manager_set_add_tearoffs (GtkUIManager *self, - gboolean add_tearoffs) +void +gtk_ui_manager_set_add_tearoffs (GtkUIManager *manager, + gboolean add_tearoffs) { - g_return_if_fail (GTK_IS_UI_MANAGER (self)); + g_return_if_fail (GTK_IS_UI_MANAGER (manager)); + + gtk_ui_manager_do_set_add_tearoffs (manager, add_tearoffs); +} +static void +gtk_ui_manager_do_set_add_tearoffs (GtkUIManager *manager, + gboolean add_tearoffs) +{ add_tearoffs = add_tearoffs != FALSE; - if (add_tearoffs != self->private_data->add_tearoffs) + if (add_tearoffs != manager->private_data->add_tearoffs) { - self->private_data->add_tearoffs = add_tearoffs; - - dirty_all_nodes (self); + manager->private_data->add_tearoffs = add_tearoffs; + + dirty_all_nodes (manager); - g_object_notify (G_OBJECT (self), "add-tearoffs"); + g_object_notify (G_OBJECT (manager), "add-tearoffs"); } } @@ -586,150 +928,176 @@ static void cb_proxy_connect_proxy (GtkActionGroup *group, GtkAction *action, GtkWidget *proxy, - GtkUIManager *self) + GtkUIManager *manager) { - g_signal_emit (self, ui_manager_signals[CONNECT_PROXY], 0, action, proxy); + g_signal_emit (manager, ui_manager_signals[CONNECT_PROXY], 0, action, proxy); } static void cb_proxy_disconnect_proxy (GtkActionGroup *group, GtkAction *action, GtkWidget *proxy, - GtkUIManager *self) + GtkUIManager *manager) { - g_signal_emit (self, ui_manager_signals[DISCONNECT_PROXY], 0, action, proxy); + g_signal_emit (manager, ui_manager_signals[DISCONNECT_PROXY], 0, action, proxy); } static void cb_proxy_pre_activate (GtkActionGroup *group, GtkAction *action, - GtkUIManager *self) + GtkUIManager *manager) { - g_signal_emit (self, ui_manager_signals[PRE_ACTIVATE], 0, action); + g_signal_emit (manager, ui_manager_signals[PRE_ACTIVATE], 0, action); } static void cb_proxy_post_activate (GtkActionGroup *group, GtkAction *action, - GtkUIManager *self) + GtkUIManager *manager) { - g_signal_emit (self, ui_manager_signals[POST_ACTIVATE], 0, action); + g_signal_emit (manager, ui_manager_signals[POST_ACTIVATE], 0, action); } /** * gtk_ui_manager_insert_action_group: - * @self: a #GtkUIManager object + * @manager: a #GtkUIManager object * @action_group: the action group to be inserted * @pos: the position at which the group will be inserted. * * Inserts an action group into the list of action groups associated - * with @self. Actions in earlier groups hide actions with the same + * with @manager. Actions in earlier groups hide actions with the same * name in later groups. * + * If @pos is larger than the number of action groups in @manager, or + * negative, @action_group will be inserted at the end of the internal + * list. + * * Since: 2.4 **/ void -gtk_ui_manager_insert_action_group (GtkUIManager *self, +gtk_ui_manager_insert_action_group (GtkUIManager *manager, GtkActionGroup *action_group, gint pos) { - g_return_if_fail (GTK_IS_UI_MANAGER (self)); +#ifdef G_ENABLE_DEBUG + GList *l; + const char *group_name; +#endif + + g_return_if_fail (GTK_IS_UI_MANAGER (manager)); g_return_if_fail (GTK_IS_ACTION_GROUP (action_group)); - g_return_if_fail (g_list_find (self->private_data->action_groups, + g_return_if_fail (g_list_find (manager->private_data->action_groups, action_group) == NULL); +#ifdef G_ENABLE_DEBUG + group_name = gtk_action_group_get_name (action_group); + + for (l = manager->private_data->action_groups; l; l = l->next) + { + GtkActionGroup *group = l->data; + + if (strcmp (gtk_action_group_get_name (group), group_name) == 0) + { + g_warning ("Inserting action group '%s' into UI manager which " + "already has a group with this name\n", group_name); + break; + } + } +#endif /* G_ENABLE_DEBUG */ + g_object_ref (action_group); - self->private_data->action_groups = - g_list_insert (self->private_data->action_groups, action_group, pos); + manager->private_data->action_groups = + g_list_insert (manager->private_data->action_groups, action_group, pos); g_object_connect (action_group, - "object_signal::connect_proxy", G_CALLBACK (cb_proxy_connect_proxy), self, - "object_signal::disconnect_proxy", G_CALLBACK (cb_proxy_disconnect_proxy), self, - "object_signal::pre_activate", G_CALLBACK (cb_proxy_pre_activate), self, - "object_signal::post_activate", G_CALLBACK (cb_proxy_post_activate), self, + "object-signal::connect-proxy", G_CALLBACK (cb_proxy_connect_proxy), manager, + "object-signal::disconnect-proxy", G_CALLBACK (cb_proxy_disconnect_proxy), manager, + "object-signal::pre-activate", G_CALLBACK (cb_proxy_pre_activate), manager, + "object-signal::post-activate", G_CALLBACK (cb_proxy_post_activate), manager, NULL); /* dirty all nodes, as action bindings may change */ - dirty_all_nodes (self); + dirty_all_nodes (manager); - g_signal_emit (self, ui_manager_signals[ACTIONS_CHANGED], 0); + g_signal_emit (manager, ui_manager_signals[ACTIONS_CHANGED], 0); } /** * gtk_ui_manager_remove_action_group: - * @self: a #GtkUIManager object + * @manager: a #GtkUIManager object * @action_group: the action group to be removed * * Removes an action group from the list of action groups associated - * with @self. + * with @manager. * * Since: 2.4 **/ void -gtk_ui_manager_remove_action_group (GtkUIManager *self, +gtk_ui_manager_remove_action_group (GtkUIManager *manager, GtkActionGroup *action_group) { - g_return_if_fail (GTK_IS_UI_MANAGER (self)); + g_return_if_fail (GTK_IS_UI_MANAGER (manager)); g_return_if_fail (GTK_IS_ACTION_GROUP (action_group)); - g_return_if_fail (g_list_find (self->private_data->action_groups, + g_return_if_fail (g_list_find (manager->private_data->action_groups, action_group) != NULL); - self->private_data->action_groups = - g_list_remove (self->private_data->action_groups, action_group); + manager->private_data->action_groups = + g_list_remove (manager->private_data->action_groups, action_group); g_object_disconnect (action_group, - "any_signal::connect_proxy", G_CALLBACK (cb_proxy_connect_proxy), self, - "any_signal::disconnect_proxy", G_CALLBACK (cb_proxy_disconnect_proxy), self, - "any_signal::pre_activate", G_CALLBACK (cb_proxy_pre_activate), self, - "any_signal::post_activate", G_CALLBACK (cb_proxy_post_activate), self, + "any-signal::connect-proxy", G_CALLBACK (cb_proxy_connect_proxy), manager, + "any-signal::disconnect-proxy", G_CALLBACK (cb_proxy_disconnect_proxy), manager, + "any-signal::pre-activate", G_CALLBACK (cb_proxy_pre_activate), manager, + "any-signal::post-activate", G_CALLBACK (cb_proxy_post_activate), manager, NULL); g_object_unref (action_group); /* dirty all nodes, as action bindings may change */ - dirty_all_nodes (self); + dirty_all_nodes (manager); - g_signal_emit (self, ui_manager_signals[ACTIONS_CHANGED], 0); + g_signal_emit (manager, ui_manager_signals[ACTIONS_CHANGED], 0); } /** * gtk_ui_manager_get_action_groups: - * @self: a #GtkUIManager object + * @manager: a #GtkUIManager object * - * Returns the list of action groups associated with @self. + * Returns the list of action groups associated with @manager. * - * Return value: a #GList of action groups. The list is owned by GTK+ + * Return value: (element-type GtkActionGroup) (transfer none): a #GList of + * action groups. The list is owned by GTK+ * and should not be modified. * * Since: 2.4 **/ GList * -gtk_ui_manager_get_action_groups (GtkUIManager *self) +gtk_ui_manager_get_action_groups (GtkUIManager *manager) { - g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL); + g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), NULL); - return self->private_data->action_groups; + return manager->private_data->action_groups; } /** * gtk_ui_manager_get_accel_group: - * @self: a #GtkUIManager object + * @manager: a #GtkUIManager object * - * Returns the #GtkAccelGroup associated with @self. + * Returns the #GtkAccelGroup associated with @manager. * - * Return value: the #GtkAccelGroup. + * Return value: (transfer none): the #GtkAccelGroup. * * Since: 2.4 **/ GtkAccelGroup * -gtk_ui_manager_get_accel_group (GtkUIManager *self) +gtk_ui_manager_get_accel_group (GtkUIManager *manager) { - g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL); + g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), NULL); - return self->private_data->accel_group; + return manager->private_data->accel_group; } /** * gtk_ui_manager_get_widget: - * @self: a #GtkUIManager + * @manager: a #GtkUIManager * @path: a path * * Looks up a widget by following a path. @@ -739,78 +1107,81 @@ gtk_ui_manager_get_accel_group (GtkUIManager *self) * (e.g. "popup"). The root element ("/ui") can be omitted in the path. * * Note that the widget found by following a path that ends in a <menu> - * element is the menuitem to which the menu is attached, not the menu itself. + * element is the menuitem to which the menu is attached, not the menu itmanager. * * Also note that the widgets constructed by a ui manager are not tied to * the lifecycle of the ui manager. If you add the widgets returned by this * function to some container or explicitly ref them, they will survive the * destruction of the ui manager. - * - * Return value: the widget found by following the path, or %NULL if no widget + * + * Return value: (transfer none): the widget found by following the path, or %NULL if no widget * was found. * * Since: 2.4 **/ GtkWidget * -gtk_ui_manager_get_widget (GtkUIManager *self, +gtk_ui_manager_get_widget (GtkUIManager *manager, const gchar *path) { - g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL); + g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), NULL); g_return_val_if_fail (path != NULL, NULL); - return GTK_UI_MANAGER_GET_CLASS (self)->get_widget (self, path); + return GTK_UI_MANAGER_GET_CLASS (manager)->get_widget (manager, path); } +typedef struct { + GtkUIManagerItemType types; + GSList *list; +} ToplevelData; + static void collect_toplevels (GNode *node, gpointer user_data) { - struct { - GtkUIManagerItemType types; - GSList *list; - } *data = user_data; - - switch (NODE_INFO (node)->type) { - case NODE_TYPE_MENUBAR: - if (data->types & GTK_UI_MANAGER_MENUBAR) - data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy); - break; - case NODE_TYPE_TOOLBAR: - if (data->types & GTK_UI_MANAGER_TOOLBAR) - data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy); - break; - case NODE_TYPE_POPUP: - if (data->types & GTK_UI_MANAGER_POPUP) - data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy); - break; - default: ; - } + ToplevelData *data = user_data; + + if (NODE_INFO (node)->proxy) + { + switch (NODE_INFO (node)->type) + { + case NODE_TYPE_MENUBAR: + if (data->types & GTK_UI_MANAGER_MENUBAR) + data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy); + break; + case NODE_TYPE_TOOLBAR: + if (data->types & GTK_UI_MANAGER_TOOLBAR) + data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy); + break; + case NODE_TYPE_POPUP: + if (data->types & GTK_UI_MANAGER_POPUP) + data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy); + break; + default: ; + } + } } /** * gtk_ui_manager_get_toplevels: - * @self: a #GtkUIManager + * @manager: a #GtkUIManager * @types: specifies the types of toplevel widgets to include. Allowed * types are #GTK_UI_MANAGER_MENUBAR, #GTK_UI_MANAGER_TOOLBAR and * #GTK_UI_MANAGER_POPUP. * * Obtains a list of all toplevel widgets of the requested types. - * - * Return value: a newly-allocated of all toplevel widgets of the requested - * types. + * + * Return value: (element-type GtkWidget) (transfer container): a newly-allocated #GSList of + * all toplevel widgets of the requested types. Free the returned list with g_slist_free(). * * Since: 2.4 **/ GSList * -gtk_ui_manager_get_toplevels (GtkUIManager *self, +gtk_ui_manager_get_toplevels (GtkUIManager *manager, GtkUIManagerItemType types) { - struct { - GtkUIManagerItemType types; - GSList *list; - } data; + ToplevelData data; - g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL); + g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), NULL); g_return_val_if_fail ((~(GTK_UI_MANAGER_MENUBAR | GTK_UI_MANAGER_TOOLBAR | GTK_UI_MANAGER_POPUP) & types) == 0, NULL); @@ -819,7 +1190,7 @@ gtk_ui_manager_get_toplevels (GtkUIManager *self, data.types = types; data.list = NULL; - g_node_children_foreach (self->private_data->root_node, + g_node_children_foreach (manager->private_data->root_node, G_TRAVERSE_ALL, collect_toplevels, &data); @@ -829,29 +1200,46 @@ gtk_ui_manager_get_toplevels (GtkUIManager *self, /** * gtk_ui_manager_get_action: - * @self: a #GtkUIManager + * @manager: a #GtkUIManager * @path: a path * * Looks up an action by following a path. See gtk_ui_manager_get_widget() * for more information about paths. * - * Return value: the action whose proxy widget is found by following the path, + * Return value: (transfer none): the action whose proxy widget is found by following the path, * or %NULL if no widget was found. * * Since: 2.4 **/ GtkAction * -gtk_ui_manager_get_action (GtkUIManager *self, +gtk_ui_manager_get_action (GtkUIManager *manager, const gchar *path) { - g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL); + g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), NULL); g_return_val_if_fail (path != NULL, NULL); - return GTK_UI_MANAGER_GET_CLASS (self)->get_action (self, path); + return GTK_UI_MANAGER_GET_CLASS (manager)->get_action (manager, path); +} + +static gboolean +node_is_dead (GNode *node) +{ + GNode *child; + + if (NODE_INFO (node)->uifiles != NULL) + return FALSE; + + for (child = node->children; child != NULL; child = child->next) + { + if (!node_is_dead (child)) + return FALSE; + } + + return TRUE; } static GNode * -get_child_node (GtkUIManager *self, +get_child_node (GtkUIManager *manager, GNode *parent, GNode *sibling, const gchar *childname, @@ -884,7 +1272,18 @@ get_child_node (GtkUIManager *self, node_type, NODE_INFO (child)->name, NODE_INFO (child)->type); - + + if (node_is_dead (child)) + { + /* This node was removed but is still dirty so + * it is still in the tree. We want to treat this + * as if it didn't exist, which means we move it + * to the position it would have been created at. + */ + g_node_unlink (child); + goto insert_child; + } + return child; } } @@ -893,25 +1292,25 @@ get_child_node (GtkUIManager *self, { Node *mnode; - mnode = g_chunk_new0 (Node, merge_node_chunk); + mnode = g_slice_new0 (Node); mnode->type = node_type; mnode->name = g_strndup (childname, childname_length); + child = g_node_new (mnode); + insert_child: if (sibling) { if (top) - child = g_node_insert_before (parent, sibling, - g_node_new (mnode)); + g_node_insert_before (parent, sibling, child); else - child = g_node_insert_after (parent, sibling, - g_node_new (mnode)); + g_node_insert_after (parent, sibling, child); } else { if (top) - child = g_node_prepend_data (parent, mnode); + g_node_prepend (parent, child); else - child = g_node_append_data (parent, mnode); + g_node_append (parent, child); } mark_node_dirty (child); @@ -920,9 +1319,9 @@ get_child_node (GtkUIManager *self, else { /* handle root node */ - if (self->private_data->root_node) + if (manager->private_data->root_node) { - child = self->private_data->root_node; + child = manager->private_data->root_node; if (strncmp (NODE_INFO (child)->name, childname, childname_length) != 0) g_warning ("root node name '%s' doesn't match '%s'", childname, NODE_INFO (child)->name); @@ -933,12 +1332,12 @@ get_child_node (GtkUIManager *self, { Node *mnode; - mnode = g_chunk_new0 (Node, merge_node_chunk); + mnode = g_slice_new0 (Node); mnode->type = node_type; mnode->name = g_strndup (childname, childname_length); mnode->dirty = TRUE; - child = self->private_data->root_node = g_node_new (mnode); + child = manager->private_data->root_node = g_node_new (mnode); } } @@ -946,7 +1345,7 @@ get_child_node (GtkUIManager *self, } static GNode * -get_node (GtkUIManager *self, +get_node (GtkUIManager *manager, const gchar *path, NodeType node_type, gboolean create) @@ -971,7 +1370,7 @@ get_node (GtkUIManager *self, else length = strlen (pos); - node = get_child_node (self, parent, NULL, pos, length, NODE_TYPE_UNDECIDED, + node = get_child_node (manager, parent, NULL, pos, length, NODE_TYPE_UNDECIDED, create, FALSE); if (!node) return NULL; @@ -986,29 +1385,33 @@ get_node (GtkUIManager *self, return node; } +static void +node_ui_reference_free (gpointer data) +{ + g_slice_free (NodeUIReference, data); +} + static gboolean free_node (GNode *node) { Node *info = NODE_INFO (node); - - g_list_foreach (info->uifiles, (GFunc) g_free, NULL); - g_list_free (info->uifiles); - if (info->action) - g_object_unref (info->action); - if (info->proxy) - g_object_unref (info->proxy); - if (info->extra) - g_object_unref (info->extra); - g_free (info->name); - g_chunk_free (info, merge_node_chunk); + g_list_free_full (info->uifiles, node_ui_reference_free); + info->uifiles = NULL; + + g_clear_object (&info->action); + g_clear_object (&info->proxy); + g_clear_object (&info->extra); + g_clear_pointer (&info->name, g_free); + g_slice_free (Node, info); + node->data = NULL; return FALSE; } /** * gtk_ui_manager_new_merge_id: - * @self: a #GtkUIManager + * @manager: a #GtkUIManager * * Returns an unused merge id, suitable for use with * gtk_ui_manager_add_ui(). @@ -1018,11 +1421,11 @@ free_node (GNode *node) * Since: 2.4 **/ guint -gtk_ui_manager_new_merge_id (GtkUIManager *self) +gtk_ui_manager_new_merge_id (GtkUIManager *manager) { - self->private_data->last_merge_id++; + manager->private_data->last_merge_id++; - return self->private_data->last_merge_id; + return manager->private_data->last_merge_id; } static void @@ -1038,7 +1441,7 @@ node_prepend_ui_reference (GNode *gnode, reference = node->uifiles->data; else { - reference = g_new (NodeUIReference, 1); + reference = g_slice_new (NodeUIReference); node->uifiles = g_list_prepend (node->uifiles, reference); } @@ -1064,7 +1467,7 @@ node_remove_ui_reference (GNode *gnode, if (p == node->uifiles) mark_node_dirty (gnode); node->uifiles = g_list_delete_link (node->uifiles, p); - g_free (reference); + g_slice_free (NodeUIReference, reference); break; } @@ -1091,7 +1494,7 @@ struct _ParseContext ParseState state; ParseState prev_state; - GtkUIManager *self; + GtkUIManager *manager; GNode *current; @@ -1107,7 +1510,7 @@ start_element_handler (GMarkupParseContext *context, GError **error) { ParseContext *ctx = user_data; - GtkUIManager *self = ctx->self; + GtkUIManager *manager = ctx->manager; gint i; const gchar *node_name; @@ -1115,7 +1518,9 @@ start_element_handler (GMarkupParseContext *context, GQuark action_quark; gboolean top; gboolean expand = FALSE; - + gboolean accelerators = FALSE; + gboolean always_show_image_set = FALSE, always_show_image = FALSE; + gboolean raise_error = TRUE; node_name = NULL; @@ -1142,20 +1547,18 @@ start_element_handler (GMarkupParseContext *context, { expand = !strcmp (attribute_values[i], "true"); } - else - { - gint line_number, char_number; - - g_markup_parse_context_get_position (context, - &line_number, &char_number); - g_set_error (error, - G_MARKUP_ERROR, - G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, - _("Unknown attribute '%s' on line %d char %d"), - attribute_names[i], - line_number, char_number); - return; - } + else if (!strcmp (attribute_names[i], "accelerators")) + { + accelerators = !strcmp (attribute_values[i], "true"); + } + else if (!strcmp (attribute_names[i], "always-show-image")) + { + always_show_image_set = TRUE; + always_show_image = !strcmp (attribute_values[i], "true"); + } + /* else silently skip unknown attributes to be compatible with + * future additional attributes. + */ } /* Work out a name for this node. Either the name attribute, or @@ -1174,7 +1577,7 @@ start_element_handler (GMarkupParseContext *context, if (ctx->state == STATE_ROOT && !strcmp (element_name, "accelerator")) { ctx->state = STATE_ACCELERATOR; - ctx->current = get_child_node (self, ctx->current, NULL, + ctx->current = get_child_node (manager, ctx->current, NULL, node_name, strlen (node_name), NODE_TYPE_ACCELERATOR, TRUE, FALSE); @@ -1190,7 +1593,7 @@ start_element_handler (GMarkupParseContext *context, if (ctx->state == STATE_START && !strcmp (element_name, "ui")) { ctx->state = STATE_ROOT; - ctx->current = self->private_data->root_node; + ctx->current = manager->private_data->root_node; raise_error = FALSE; node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark); @@ -1200,7 +1603,7 @@ start_element_handler (GMarkupParseContext *context, if (ctx->state == STATE_ROOT && !strcmp (element_name, "menubar")) { ctx->state = STATE_MENU; - ctx->current = get_child_node (self, ctx->current, NULL, + ctx->current = get_child_node (manager, ctx->current, NULL, node_name, strlen (node_name), NODE_TYPE_MENUBAR, TRUE, FALSE); @@ -1214,7 +1617,7 @@ start_element_handler (GMarkupParseContext *context, } else if (ctx->state == STATE_MENU && !strcmp (element_name, "menu")) { - ctx->current = get_child_node (self, ctx->current, NULL, + ctx->current = get_child_node (manager, ctx->current, NULL, node_name, strlen (node_name), NODE_TYPE_MENU, TRUE, top); @@ -1229,7 +1632,7 @@ start_element_handler (GMarkupParseContext *context, { ctx->state = STATE_MENU; - ctx->current = get_child_node (self, g_node_last_child (ctx->current), NULL, + ctx->current = get_child_node (manager, g_node_last_child (ctx->current), NULL, node_name, strlen (node_name), NODE_TYPE_MENU, TRUE, top); @@ -1245,13 +1648,16 @@ start_element_handler (GMarkupParseContext *context, GNode *node; ctx->state = STATE_MENUITEM; - node = get_child_node (self, ctx->current, NULL, + node = get_child_node (manager, ctx->current, NULL, node_name, strlen (node_name), NODE_TYPE_MENUITEM, TRUE, top); if (NODE_INFO (node)->action_name == 0) NODE_INFO (node)->action_name = action_quark; - + + NODE_INFO (node)->always_show_image_set = always_show_image_set; + NODE_INFO (node)->always_show_image = always_show_image; + node_prepend_ui_reference (node, ctx->merge_id, action_quark); raise_error = FALSE; @@ -1261,10 +1667,13 @@ start_element_handler (GMarkupParseContext *context, if (ctx->state == STATE_ROOT && !strcmp (element_name, "popup")) { ctx->state = STATE_MENU; - ctx->current = get_child_node (self, ctx->current, NULL, + ctx->current = get_child_node (manager, ctx->current, NULL, node_name, strlen (node_name), NODE_TYPE_POPUP, TRUE, FALSE); + + NODE_INFO (ctx->current)->popup_accels = accelerators; + if (NODE_INFO (ctx->current)->action_name == 0) NODE_INFO (ctx->current)->action_name = action_quark; @@ -1276,12 +1685,12 @@ start_element_handler (GMarkupParseContext *context, !strcmp (element_name, "placeholder")) { if (ctx->state == STATE_TOOLBAR) - ctx->current = get_child_node (self, ctx->current, NULL, + ctx->current = get_child_node (manager, ctx->current, NULL, node_name, strlen (node_name), NODE_TYPE_TOOLBAR_PLACEHOLDER, TRUE, top); else - ctx->current = get_child_node (self, ctx->current, NULL, + ctx->current = get_child_node (manager, ctx->current, NULL, node_name, strlen (node_name), NODE_TYPE_MENU_PLACEHOLDER, TRUE, top); @@ -1305,11 +1714,11 @@ start_element_handler (GMarkupParseContext *context, if (!strcmp (node_name, "separator")) { node_name = NULL; - length = -1; + length = 0; } else length = strlen (node_name); - node = get_child_node (self, ctx->current, NULL, + node = get_child_node (manager, ctx->current, NULL, node_name, length, NODE_TYPE_SEPARATOR, TRUE, top); @@ -1328,7 +1737,7 @@ start_element_handler (GMarkupParseContext *context, if (ctx->state == STATE_ROOT && !strcmp (element_name, "toolbar")) { ctx->state = STATE_TOOLBAR; - ctx->current = get_child_node (self, ctx->current, NULL, + ctx->current = get_child_node (manager, ctx->current, NULL, node_name, strlen (node_name), NODE_TYPE_TOOLBAR, TRUE, FALSE); @@ -1344,7 +1753,7 @@ start_element_handler (GMarkupParseContext *context, GNode *node; ctx->state = STATE_TOOLITEM; - node = get_child_node (self, ctx->current, NULL, + node = get_child_node (manager, ctx->current, NULL, node_name, strlen (node_name), NODE_TYPE_TOOLITEM, TRUE, top); @@ -1425,7 +1834,7 @@ cleanup (GMarkupParseContext *context, /* should also walk through the tree and get rid of nodes related to * this UI file's tag */ - gtk_ui_manager_remove_ui (ctx->self, ctx->merge_id); + gtk_ui_manager_remove_ui (ctx->manager, ctx->merge_id); } static gboolean @@ -1464,7 +1873,7 @@ text_handler (GMarkupParseContext *context, } -static GMarkupParser ui_parser = { +static const GMarkupParser ui_parser = { start_element_handler, end_element_handler, text_handler, @@ -1473,7 +1882,7 @@ static GMarkupParser ui_parser = { }; static guint -add_ui_from_string (GtkUIManager *self, +add_ui_from_string (GtkUIManager *manager, const gchar *buffer, gssize length, gboolean needs_root, @@ -1483,9 +1892,9 @@ add_ui_from_string (GtkUIManager *self, GMarkupParseContext *context; ctx.state = STATE_START; - ctx.self = self; + ctx.manager = manager; ctx.current = NULL; - ctx.merge_id = gtk_ui_manager_new_merge_id (self); + ctx.merge_id = gtk_ui_manager_new_merge_id (manager); context = g_markup_parse_context_new (&ui_parser, 0, &ctx, NULL); @@ -1505,9 +1914,9 @@ add_ui_from_string (GtkUIManager *self, g_markup_parse_context_free (context); - queue_update (self); + queue_update (manager); - g_object_notify (G_OBJECT (self), "ui"); + g_object_notify (G_OBJECT (manager), "ui"); return ctx.merge_id; @@ -1520,13 +1929,13 @@ add_ui_from_string (GtkUIManager *self, /** * gtk_ui_manager_add_ui_from_string: - * @self: a #GtkUIManager object + * @manager: a #GtkUIManager object * @buffer: the string to parse * @length: the length of @buffer (may be -1 if @buffer is nul-terminated) * @error: return location for an error * * Parses a string containing a UI definition and - * merges it with the current contents of @self. An enclosing <ui> + * merges it with the current contents of @manager. An enclosing <ui> * element is added if it is missing. * * Return value: The merge id for the merged UI. The merge id can be used @@ -1536,7 +1945,7 @@ add_ui_from_string (GtkUIManager *self, * Since: 2.4 **/ guint -gtk_ui_manager_add_ui_from_string (GtkUIManager *self, +gtk_ui_manager_add_ui_from_string (GtkUIManager *manager, const gchar *buffer, gssize length, GError **error) @@ -1545,7 +1954,7 @@ gtk_ui_manager_add_ui_from_string (GtkUIManager *self, const gchar *p; const gchar *end; - g_return_val_if_fail (GTK_IS_UI_MANAGER (self), 0); + g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), 0); g_return_val_if_fail (buffer != NULL, 0); if (length < 0) @@ -1559,17 +1968,17 @@ gtk_ui_manager_add_ui_from_string (GtkUIManager *self, if (end - p >= 4 && strncmp (p, "", 4) == 0) needs_root = FALSE; - return add_ui_from_string (self, buffer, length, needs_root, error); + return add_ui_from_string (manager, buffer, length, needs_root, error); } /** * gtk_ui_manager_add_ui_from_file: - * @self: a #GtkUIManager object - * @filename: the name of the file to parse + * @manager: a #GtkUIManager object + * @filename: (type filename): the name of the file to parse * @error: return location for an error * * Parses a file containing a UI definition and - * merges it with the current contents of @self. + * merges it with the current contents of @manager. * * Return value: The merge id for the merged UI. The merge id can be used * to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred, @@ -1578,7 +1987,7 @@ gtk_ui_manager_add_ui_from_string (GtkUIManager *self, * Since: 2.4 **/ guint -gtk_ui_manager_add_ui_from_file (GtkUIManager *self, +gtk_ui_manager_add_ui_from_file (GtkUIManager *manager, const gchar *filename, GError **error) { @@ -1586,29 +1995,64 @@ gtk_ui_manager_add_ui_from_file (GtkUIManager *self, gsize length; guint res; - g_return_val_if_fail (GTK_IS_UI_MANAGER (self), 0); + g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), 0); if (!g_file_get_contents (filename, &buffer, &length, error)) return 0; - res = add_ui_from_string (self, buffer, length, FALSE, error); + res = add_ui_from_string (manager, buffer, length, FALSE, error); g_free (buffer); return res; } +/** + * gtk_ui_manager_add_ui_from_resource: + * @manager: a #GtkUIManager object + * @resource_path: the resource path of the file to parse + * @error: return location for an error + * + * Parses a resource file containing a UI definition and + * merges it with the current contents of @manager. + * + * Return value: The merge id for the merged UI. The merge id can be used + * to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred, + * the return value is 0. + * + * Since: 3.4 + **/ +guint +gtk_ui_manager_add_ui_from_resource (GtkUIManager *manager, + const gchar *resource_path, + GError **error) +{ + GBytes *data; + guint res; + + g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), 0); + + data = g_resources_lookup_data (resource_path, 0, error); + if (data == NULL) + return 0; + + res = add_ui_from_string (manager, g_bytes_get_data (data, NULL), g_bytes_get_size (data), FALSE, error); + g_bytes_unref (data); + + return res; +} + /** * gtk_ui_manager_add_ui: - * @self: a #GtkUIManager + * @manager: a #GtkUIManager * @merge_id: the merge id for the merged UI, see gtk_ui_manager_new_merge_id() * @path: a path * @name: the name for the added UI element - * @action: the name of the action to be proxied, or %NULL to add a separator + * @action: (allow-none): the name of the action to be proxied, or %NULL to add a separator * @type: the type of UI element to add. - * @top: if %TRUE, the UI element is added before its siblings, otherwise it + * @top: if %TRUE, the UI element is added before its siblings, otherwise it * is added after its siblings. - * - * Adds a UI element to the current contents of @self. + * + * Adds a UI element to the current contents of @manager. * * If @type is %GTK_UI_MANAGER_AUTO, GTK+ inserts a menuitem, toolitem or * separator if such an element can be inserted at the place determined by @@ -1621,7 +2065,7 @@ gtk_ui_manager_add_ui_from_file (GtkUIManager *self, * Since: 2.4 **/ void -gtk_ui_manager_add_ui (GtkUIManager *self, +gtk_ui_manager_add_ui (GtkUIManager *manager, guint merge_id, const gchar *path, const gchar *name, @@ -1635,11 +2079,11 @@ gtk_ui_manager_add_ui (GtkUIManager *self, NodeType node_type; GQuark action_quark = 0; - g_return_if_fail (GTK_IS_UI_MANAGER (self)); + g_return_if_fail (GTK_IS_UI_MANAGER (manager)); g_return_if_fail (merge_id > 0); - g_return_if_fail (name != NULL); + g_return_if_fail (name != NULL || type == GTK_UI_MANAGER_SEPARATOR); - node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE); + node = get_node (manager, path, NODE_TYPE_UNDECIDED, FALSE); sibling = NULL; if (node == NULL) @@ -1717,6 +2161,7 @@ gtk_ui_manager_add_ui (GtkUIManager *self, node_type = NODE_TYPE_TOOLBAR; break; case GTK_UI_MANAGER_POPUP: + case GTK_UI_MANAGER_POPUP_WITH_ACCELS: node_type = NODE_TYPE_POPUP; break; case GTK_UI_MANAGER_ACCELERATOR: @@ -1737,10 +2182,13 @@ gtk_ui_manager_add_ui (GtkUIManager *self, return; } - child = get_child_node (self, node, sibling, - name, strlen (name), + child = get_child_node (manager, node, sibling, + name, name ? strlen (name) : 0, node_type, TRUE, top); + if (type == GTK_UI_MANAGER_POPUP_WITH_ACCELS) + NODE_INFO (child)->popup_accels = TRUE; + if (action != NULL) action_quark = g_quark_from_string (action); @@ -1749,9 +2197,9 @@ gtk_ui_manager_add_ui (GtkUIManager *self, if (NODE_INFO (child)->action_name == 0) NODE_INFO (child)->action_name = action_quark; - queue_update (self); + queue_update (manager); - g_object_notify (G_OBJECT (self), "ui"); + g_object_notify (G_OBJECT (manager), "ui"); } static gboolean @@ -1767,24 +2215,26 @@ remove_ui (GNode *node, /** * gtk_ui_manager_remove_ui: - * @self: a #GtkUIManager object + * @manager: a #GtkUIManager object * @merge_id: a merge id as returned by gtk_ui_manager_add_ui_from_string() * - * Unmerges the part of @selfs content identified by @merge_id. + * Unmerges the part of @managers content identified by @merge_id. * * Since: 2.4 **/ void -gtk_ui_manager_remove_ui (GtkUIManager *self, +gtk_ui_manager_remove_ui (GtkUIManager *manager, guint merge_id) { - g_node_traverse (self->private_data->root_node, + g_return_if_fail (GTK_IS_UI_MANAGER (manager)); + + g_node_traverse (manager->private_data->root_node, G_POST_ORDER, G_TRAVERSE_ALL, -1, remove_ui, GUINT_TO_POINTER (merge_id)); - queue_update (self); + queue_update (manager); - g_object_notify (G_OBJECT (self), "ui"); + g_object_notify (G_OBJECT (manager), "ui"); } /* -------------------- Updates -------------------- */ @@ -1815,20 +2265,20 @@ get_action_by_name (GtkUIManager *merge, } static gboolean -find_menu_position (GNode *node, - GtkWidget **menushell_p, - gint *pos_p) +find_menu_position (GNode *node, + GtkWidget **menushell_p, + gint *pos_p) { GtkWidget *menushell; - gint pos; + gint pos = 0; g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (NODE_INFO (node)->type == NODE_TYPE_MENU || - NODE_INFO (node)->type == NODE_TYPE_POPUP || - NODE_INFO (node)->type == NODE_TYPE_MENU_PLACEHOLDER || - NODE_INFO (node)->type == NODE_TYPE_MENUITEM || - NODE_INFO (node)->type == NODE_TYPE_SEPARATOR, - FALSE); + NODE_INFO (node)->type == NODE_TYPE_POPUP || + NODE_INFO (node)->type == NODE_TYPE_MENU_PLACEHOLDER || + NODE_INFO (node)->type == NODE_TYPE_MENUITEM || + NODE_INFO (node)->type == NODE_TYPE_SEPARATOR, + FALSE); /* first sibling -- look at parent */ if (node->prev == NULL) @@ -1858,12 +2308,12 @@ find_menu_position (GNode *node, case NODE_TYPE_MENU_PLACEHOLDER: menushell = gtk_widget_get_parent (NODE_INFO (parent)->proxy); g_return_val_if_fail (GTK_IS_MENU_SHELL (menushell), FALSE); - pos = g_list_index (GTK_MENU_SHELL (menushell)->children, + pos = g_list_index (GTK_MENU_SHELL (menushell)->priv->children, NODE_INFO (parent)->proxy) + 1; break; default: - g_warning("%s: bad parent node type %d", G_STRLOC, - NODE_INFO (parent)->type); + g_warning ("%s: bad parent node type %d", G_STRLOC, + NODE_INFO (parent)->type); return FALSE; } } @@ -1878,11 +2328,14 @@ find_menu_position (GNode *node, else prev_child = NODE_INFO (sibling)->proxy; - g_return_val_if_fail (GTK_IS_WIDGET (prev_child), FALSE); + if (!GTK_IS_WIDGET (prev_child)) + return FALSE; + menushell = gtk_widget_get_parent (prev_child); - g_return_val_if_fail (GTK_IS_MENU_SHELL (menushell), FALSE); + if (!GTK_IS_MENU_SHELL (menushell)) + return FALSE; - pos = g_list_index (GTK_MENU_SHELL (menushell)->children, prev_child) + 1; + pos = g_list_index (GTK_MENU_SHELL (menushell)->priv->children, prev_child) + 1; } if (menushell_p) @@ -1903,11 +2356,11 @@ find_toolbar_position (GNode *node, g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (NODE_INFO (node)->type == NODE_TYPE_TOOLBAR || - NODE_INFO (node)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER || - NODE_INFO (node)->type == NODE_TYPE_TOOLITEM || - NODE_INFO (node)->type == NODE_TYPE_SEPARATOR, - FALSE); - + NODE_INFO (node)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER || + NODE_INFO (node)->type == NODE_TYPE_TOOLITEM || + NODE_INFO (node)->type == NODE_TYPE_SEPARATOR, + FALSE); + /* first sibling -- look at parent */ if (node->prev == NULL) { @@ -1943,14 +2396,17 @@ find_toolbar_position (GNode *node, else prev_child = NODE_INFO (sibling)->proxy; - g_return_val_if_fail (GTK_IS_WIDGET (prev_child), FALSE); + if (!GTK_IS_WIDGET (prev_child)) + return FALSE; + toolbar = gtk_widget_get_parent (prev_child); - g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), FALSE); + if (!GTK_IS_TOOLBAR (toolbar)) + return FALSE; pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (prev_child)) + 1; } - + if (toolbar_p) *toolbar_p = toolbar; if (pos_p) @@ -1959,56 +2415,12 @@ find_toolbar_position (GNode *node, return TRUE; } -/** - * _gtk_menu_is_empty: - * @menu: a #GtkMenu or %NULL - * - * Determines whether @menu is empty. A menu is considered empty if it - * the only visible children are tearoff menu items or "filler" menu - * items which were inserted to mark the menu as empty. - * - * This function is used by #GtkAction. - * - * Return value: whether @menu is empty. - **/ -gboolean -_gtk_menu_is_empty (GtkWidget *menu) -{ - GList *children, *cur; - - g_return_val_if_fail (menu == NULL || GTK_IS_MENU (menu), TRUE); - - if (!menu) - return FALSE; - - children = gtk_container_get_children (GTK_CONTAINER (menu)); - - cur = children; - while (cur) - { - if (GTK_WIDGET_VISIBLE (cur->data)) - { - if (!GTK_IS_TEAROFF_MENU_ITEM (cur->data) && - !g_object_get_data (cur->data, "gtk-empty-menu-item")) - return FALSE; - } - cur = cur->next; - } - g_list_free (children); - - return TRUE; -} - enum { SEPARATOR_MODE_SMART, SEPARATOR_MODE_VISIBLE, SEPARATOR_MODE_HIDDEN }; -void _gtk_action_sync_menu_visible (GtkAction *action, - GtkWidget *proxy, - gboolean empty); - static void update_smart_separators (GtkWidget *proxy) { @@ -2068,7 +2480,7 @@ update_smart_separators (GtkWidget *proxy) break; } } - else if (GTK_WIDGET_VISIBLE (cur->data)) + else if (gtk_widget_get_visible (cur->data)) { last = NULL; if (GTK_IS_TEAROFF_MENU_ITEM (cur->data) || cur->data == filler) @@ -2107,9 +2519,10 @@ update_smart_separators (GtkWidget *proxy) } static void -update_node (GtkUIManager *self, +update_node (GtkUIManager *manager, GNode *node, - gboolean in_popup) + gboolean in_popup, + gboolean popup_accels) { Node *info; GNode *child; @@ -2129,7 +2542,11 @@ update_node (GtkUIManager *self, if (!info->dirty) return; - in_popup = in_popup || (info->type == NODE_TYPE_POPUP); + if (info->type == NODE_TYPE_POPUP) + { + in_popup = TRUE; + popup_accels = info->popup_accels; + } #ifdef DEBUG_UI_MANAGER g_print ("update_node name=%s dirty=%d popup %d (", @@ -2153,7 +2570,7 @@ update_node (GtkUIManager *self, ref = info->uifiles->data; action_name = g_quark_to_string (ref->action_quark); - action = get_action_by_name (self, action_name); + action = get_action_by_name (manager, action_name); info->dirty = FALSE; @@ -2167,13 +2584,13 @@ update_node (GtkUIManager *self, info->type != NODE_TYPE_MENU_PLACEHOLDER && info->type != NODE_TYPE_TOOLBAR_PLACEHOLDER) { - g_warning ("%s: missing action", info->name); + g_warning ("%s: missing action %s", info->name, action_name); - goto recurse_children; + return; } if (action) - gtk_action_set_accel_group (action, self->private_data->accel_group); + gtk_action_set_accel_group (action, manager->private_data->accel_group); /* If the widget already has a proxy and the action hasn't changed, then * we only have to update the tearoff menu items. @@ -2192,7 +2609,7 @@ update_node (GtkUIManager *self, siblings = gtk_container_get_children (GTK_CONTAINER (menu)); if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data)) { - if (self->private_data->add_tearoffs && !in_popup) + if (manager->private_data->add_tearoffs && !in_popup) gtk_widget_show (GTK_WIDGET (siblings->data)); else gtk_widget_hide (GTK_WIDGET (siblings->data)); @@ -2212,7 +2629,7 @@ update_node (GtkUIManager *self, g_object_ref_sink (info->proxy); gtk_widget_set_name (info->proxy, info->name); gtk_widget_show (info->proxy); - g_signal_emit (self, ui_manager_signals[ADD_WIDGET], 0, info->proxy); + g_signal_emit (manager, ui_manager_signals[ADD_WIDGET], 0, info->proxy); } break; case NODE_TYPE_POPUP: @@ -2226,8 +2643,9 @@ update_node (GtkUIManager *self, case NODE_TYPE_MENU: { GtkWidget *prev_submenu = NULL; - GtkWidget *menu; + GtkWidget *menu = NULL; GList *siblings; + /* remove the proxy if it is of the wrong type ... */ if (info->proxy && G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->menu_item_type) @@ -2242,59 +2660,72 @@ update_node (GtkUIManager *self, } } - gtk_action_disconnect_proxy (info->action, info->proxy); - gtk_container_remove (GTK_CONTAINER (info->proxy->parent), + gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), NULL); + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (info->proxy)), info->proxy); g_object_unref (info->proxy); info->proxy = NULL; } + /* create proxy if needed ... */ if (info->proxy == NULL) { - GtkWidget *menushell; - gint pos; + /* ... if the action already provides a menu, then use + * that menu instead of creating an empty one + */ + if ((NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM || + NODE_INFO (node->parent)->type == NODE_TYPE_MENUITEM) && + GTK_ACTION_GET_CLASS (action)->create_menu) + { + menu = gtk_action_create_menu (action); + } + + if (!menu) + { + GtkWidget *tearoff; + GtkWidget *filler; + + menu = gtk_menu_new (); + gtk_widget_set_name (menu, info->name); + tearoff = gtk_tearoff_menu_item_new (); + gtk_widget_set_no_show_all (tearoff, TRUE); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff); + filler = gtk_menu_item_new_with_label (_("Empty")); + g_object_set_data (G_OBJECT (filler), + I_("gtk-empty-menu-item"), + GINT_TO_POINTER (TRUE)); + gtk_widget_set_sensitive (filler, FALSE); + gtk_widget_set_no_show_all (filler, TRUE); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), filler); + } - if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM || - find_menu_position (node, &menushell, &pos)) + if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM) + { + info->proxy = menu; + g_object_ref_sink (info->proxy); + gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (NODE_INFO (node->parent)->proxy), + menu); + } + else { - GtkWidget *tearoff; - GtkWidget *filler; + GtkWidget *menushell; + gint pos; - menu = gtk_menu_new (); - gtk_widget_set_name (menu, info->name); - tearoff = gtk_tearoff_menu_item_new (); - gtk_widget_set_no_show_all (tearoff, TRUE); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff); - filler = gtk_menu_item_new_with_label (_("Empty")); - g_object_set_data (G_OBJECT (filler), - I_("gtk-empty-menu-item"), - GINT_TO_POINTER (TRUE)); - gtk_widget_set_sensitive (filler, FALSE); - gtk_widget_set_no_show_all (filler, TRUE); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), filler); - - if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM) - { - info->proxy = menu; - g_object_ref_sink (info->proxy); - gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (NODE_INFO (node->parent)->proxy), - menu); - } - else - { - info->proxy = gtk_action_create_menu_item (action); - g_object_ref_sink (info->proxy); - g_signal_connect (info->proxy, "notify::visible", - G_CALLBACK (update_smart_separators), NULL); - gtk_widget_set_name (info->proxy, info->name); - - gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), menu); - gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos); - } + if (find_menu_position (node, &menushell, &pos)) + { + info->proxy = gtk_action_create_menu_item (action); + g_object_ref_sink (info->proxy); + g_signal_connect (info->proxy, "notify::visible", + G_CALLBACK (update_smart_separators), NULL); + gtk_widget_set_name (info->proxy, info->name); + + gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), menu); + gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos); + } } } else - gtk_action_connect_proxy (action, info->proxy); + gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), action); if (prev_submenu) { @@ -2307,10 +2738,11 @@ update_node (GtkUIManager *self, menu = info->proxy; else menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy)); + siblings = gtk_container_get_children (GTK_CONTAINER (menu)); if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data)) { - if (self->private_data->add_tearoffs && !in_popup) + if (manager->private_data->add_tearoffs && !in_popup) gtk_widget_show (GTK_WIDGET (siblings->data)); else gtk_widget_hide (GTK_WIDGET (siblings->data)); @@ -2330,7 +2762,7 @@ update_node (GtkUIManager *self, g_object_ref_sink (info->proxy); gtk_widget_set_name (info->proxy, info->name); gtk_widget_show (info->proxy); - g_signal_emit (self, ui_manager_signals[ADD_WIDGET], 0, info->proxy); + g_signal_emit (manager, ui_manager_signals[ADD_WIDGET], 0, info->proxy); } break; case NODE_TYPE_MENU_PLACEHOLDER: @@ -2340,14 +2772,14 @@ update_node (GtkUIManager *self, { if (info->proxy) { - gtk_container_remove (GTK_CONTAINER (info->proxy->parent), + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (info->proxy)), info->proxy); g_object_unref (info->proxy); info->proxy = NULL; } if (info->extra) { - gtk_container_remove (GTK_CONTAINER (info->extra->parent), + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (info->extra)), info->extra); g_object_unref (info->extra); info->extra = NULL; @@ -2359,25 +2791,25 @@ update_node (GtkUIManager *self, gint pos; if (find_menu_position (node, &menushell, &pos)) - { + { info->proxy = gtk_separator_menu_item_new (); g_object_ref_sink (info->proxy); g_object_set_data (G_OBJECT (info->proxy), - I_("gtk-separator-mode"), - GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN)); + I_("gtk-separator-mode"), + GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN)); gtk_widget_set_no_show_all (info->proxy, TRUE); gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), - NODE_INFO (node)->proxy, pos); - + NODE_INFO (node)->proxy, pos); + info->extra = gtk_separator_menu_item_new (); g_object_ref_sink (info->extra); g_object_set_data (G_OBJECT (info->extra), - I_("gtk-separator-mode"), - GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN)); + I_("gtk-separator-mode"), + GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN)); gtk_widget_set_no_show_all (info->extra, TRUE); gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), - NODE_INFO (node)->extra, pos+1); - } + NODE_INFO (node)->extra, pos + 1); + } } break; case NODE_TYPE_TOOLBAR_PLACEHOLDER: @@ -2387,14 +2819,14 @@ update_node (GtkUIManager *self, { if (info->proxy) { - gtk_container_remove (GTK_CONTAINER (info->proxy->parent), + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (info->proxy)), info->proxy); g_object_unref (info->proxy); info->proxy = NULL; } if (info->extra) { - gtk_container_remove (GTK_CONTAINER (info->extra->parent), + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (info->extra)), info->extra); g_object_unref (info->extra); info->extra = NULL; @@ -2404,29 +2836,28 @@ update_node (GtkUIManager *self, { GtkWidget *toolbar; gint pos; + GtkToolItem *item; if (find_toolbar_position (node, &toolbar, &pos)) - { - GtkToolItem *item; - + { item = gtk_separator_tool_item_new (); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos); info->proxy = GTK_WIDGET (item); g_object_ref_sink (info->proxy); g_object_set_data (G_OBJECT (info->proxy), - I_("gtk-separator-mode"), - GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN)); + I_("gtk-separator-mode"), + GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN)); gtk_widget_set_no_show_all (info->proxy, TRUE); - + item = gtk_separator_tool_item_new (); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos+1); info->extra = GTK_WIDGET (item); g_object_ref_sink (info->extra); g_object_set_data (G_OBJECT (info->extra), - I_("gtk-separator-mode"), - GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN)); + I_("gtk-separator-mode"), + GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN)); gtk_widget_set_no_show_all (info->extra, TRUE); - } + } } break; case NODE_TYPE_MENUITEM: @@ -2437,8 +2868,8 @@ update_node (GtkUIManager *self, g_signal_handlers_disconnect_by_func (info->proxy, G_CALLBACK (update_smart_separators), NULL); - gtk_action_disconnect_proxy (info->action, info->proxy); - gtk_container_remove (GTK_CONTAINER (info->proxy->parent), + gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), NULL); + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (info->proxy)), info->proxy); g_object_unref (info->proxy); info->proxy = NULL; @@ -2450,14 +2881,19 @@ update_node (GtkUIManager *self, gint pos; if (find_menu_position (node, &menushell, &pos)) - { + { info->proxy = gtk_action_create_menu_item (action); g_object_ref_sink (info->proxy); gtk_widget_set_name (info->proxy, info->name); - + + if (info->always_show_image_set && + GTK_IS_IMAGE_MENU_ITEM (info->proxy)) + gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (info->proxy), + info->always_show_image); + gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos); - } + } } else { @@ -2465,18 +2901,21 @@ update_node (GtkUIManager *self, G_CALLBACK (update_smart_separators), NULL); gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL); - gtk_action_connect_proxy (action, info->proxy); - } - g_signal_connect (info->proxy, "notify::visible", - G_CALLBACK (update_smart_separators), NULL); - if (in_popup) - { - /* don't show accels in popups */ - GtkWidget *label = GTK_BIN (info->proxy)->child; - g_object_set (label, - "accel_closure", NULL, - NULL); + gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), action); } + + if (info->proxy) + { + g_signal_connect (info->proxy, "notify::visible", + G_CALLBACK (update_smart_separators), NULL); + if (in_popup && !popup_accels) + { + /* don't show accels in popups */ + GtkWidget *child = gtk_bin_get_child (GTK_BIN (info->proxy)); + if (GTK_IS_ACCEL_LABEL (child)) + g_object_set (child, "accel-closure", NULL, NULL); + } + } break; case NODE_TYPE_TOOLITEM: @@ -2487,8 +2926,8 @@ update_node (GtkUIManager *self, g_signal_handlers_disconnect_by_func (info->proxy, G_CALLBACK (update_smart_separators), NULL); - gtk_action_disconnect_proxy (info->action, info->proxy); - gtk_container_remove (GTK_CONTAINER (info->proxy->parent), + gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), NULL); + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (info->proxy)), info->proxy); g_object_unref (info->proxy); info->proxy = NULL; @@ -2500,31 +2939,28 @@ update_node (GtkUIManager *self, gint pos; if (find_toolbar_position (node, &toolbar, &pos)) - { + { info->proxy = gtk_action_create_tool_item (action); g_object_ref_sink (info->proxy); gtk_widget_set_name (info->proxy, info->name); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), - GTK_TOOL_ITEM (info->proxy), pos); - } + GTK_TOOL_ITEM (info->proxy), pos); + } } else { g_signal_handlers_disconnect_by_func (info->proxy, G_CALLBACK (update_smart_separators), NULL); - gtk_action_connect_proxy (action, info->proxy); + gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), action); } - /* FIXME: we must trigger the notify::tooltip handler, since - * tooltips on toolitems can't be set before the toolitem - * is added to the toolbar. - */ - g_object_notify (G_OBJECT (action), "tooltip"); - - g_signal_connect (info->proxy, "notify::visible", - G_CALLBACK (update_smart_separators), NULL); + if (info->proxy) + { + g_signal_connect (info->proxy, "notify::visible", + G_CALLBACK (update_smart_separators), NULL); + } break; case NODE_TYPE_SEPARATOR: if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR || @@ -2533,37 +2969,37 @@ update_node (GtkUIManager *self, GtkWidget *toolbar; gint pos; gint separator_mode; - + GtkToolItem *item; + if (GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy)) { - gtk_container_remove (GTK_CONTAINER (info->proxy->parent), + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (info->proxy)), info->proxy); g_object_unref (info->proxy); info->proxy = NULL; } if (find_toolbar_position (node, &toolbar, &pos)) - { - GtkToolItem *item = gtk_separator_tool_item_new (); + { + item = gtk_separator_tool_item_new (); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos); info->proxy = GTK_WIDGET (item); g_object_ref_sink (info->proxy); gtk_widget_set_no_show_all (info->proxy, TRUE); if (info->expand) - { - gtk_tool_item_set_expand (GTK_TOOL_ITEM (item), TRUE); - gtk_separator_tool_item_set_draw - (GTK_SEPARATOR_TOOL_ITEM (item), FALSE); - separator_mode = SEPARATOR_MODE_VISIBLE; - } - else - separator_mode = SEPARATOR_MODE_SMART; - + { + gtk_tool_item_set_expand (GTK_TOOL_ITEM (item), TRUE); + gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (item), FALSE); + separator_mode = SEPARATOR_MODE_VISIBLE; + } + else + separator_mode = SEPARATOR_MODE_SMART; + g_object_set_data (G_OBJECT (info->proxy), - I_("gtk-separator-mode"), - GINT_TO_POINTER (separator_mode)); + I_("gtk-separator-mode"), + GINT_TO_POINTER (separator_mode)); gtk_widget_show (info->proxy); - } + } } else { @@ -2572,7 +3008,7 @@ update_node (GtkUIManager *self, if (GTK_IS_SEPARATOR_MENU_ITEM (info->proxy)) { - gtk_container_remove (GTK_CONTAINER (info->proxy->parent), + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (info->proxy)), info->proxy); g_object_unref (info->proxy); info->proxy = NULL; @@ -2580,16 +3016,16 @@ update_node (GtkUIManager *self, if (find_menu_position (node, &menushell, &pos)) { - info->proxy = gtk_separator_menu_item_new (); + info->proxy = gtk_separator_menu_item_new (); g_object_ref_sink (info->proxy); gtk_widget_set_no_show_all (info->proxy, TRUE); g_object_set_data (G_OBJECT (info->proxy), - I_("gtk-separator-mode"), - GINT_TO_POINTER (SEPARATOR_MODE_SMART)); + I_("gtk-separator-mode"), + GINT_TO_POINTER (SEPARATOR_MODE_SMART)); gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos); gtk_widget_show (info->proxy); - } + } } break; case NODE_TYPE_ACCELERATOR: @@ -2612,7 +3048,7 @@ update_node (GtkUIManager *self, current = child; child = current->next; - update_node (self, current, in_popup); + update_node (manager, current, in_popup, popup_accels); } if (info->proxy) @@ -2632,7 +3068,7 @@ update_node (GtkUIManager *self, gtk_widget_destroy (info->proxy); if (info->extra) gtk_widget_destroy (info->extra); - if (info->type == NODE_TYPE_ACCELERATOR) + if (info->type == NODE_TYPE_ACCELERATOR && info->action != NULL) gtk_action_disconnect_accelerator (info->action); free_node (node); g_node_destroy (node); @@ -2640,7 +3076,7 @@ update_node (GtkUIManager *self, } static gboolean -do_updates (GtkUIManager *self) +do_updates (GtkUIManager *manager) { /* this function needs to check through the tree for dirty nodes. * For such nodes, it needs to do the following: @@ -2654,37 +3090,36 @@ do_updates (GtkUIManager *self) * the proxy is reconnected to the new action (or a new proxy widget * is created and added to the parent container). */ - update_node (self, self->private_data->root_node, FALSE); + update_node (manager, manager->private_data->root_node, FALSE, FALSE); - self->private_data->update_tag = 0; + manager->private_data->update_tag = 0; return FALSE; } static gboolean -do_updates_idle (GtkUIManager *self) +do_updates_idle (GtkUIManager *manager) { - GDK_THREADS_ENTER (); - do_updates (self); - GDK_THREADS_LEAVE (); + do_updates (manager); return FALSE; } static void -queue_update (GtkUIManager *self) +queue_update (GtkUIManager *manager) { - if (self->private_data->update_tag != 0) + if (manager->private_data->update_tag != 0) return; - self->private_data->update_tag = g_idle_add ((GSourceFunc)do_updates_idle, - self); + manager->private_data->update_tag = gdk_threads_add_idle ( + (GSourceFunc)do_updates_idle, + manager); } /** * gtk_ui_manager_ensure_update: - * @self: a #GtkUIManager + * @manager: a #GtkUIManager * * Makes sure that all pending updates to the UI have been completed. * @@ -2692,27 +3127,25 @@ queue_update (GtkUIManager *self) * UI in an idle function. A typical example where this function is * useful is to enforce that the menubar and toolbar have been added to * the main window before showing it: - * - * + * |[ * gtk_container_add (GTK_CONTAINER (window), vbox); - * g_signal_connect (merge, "add_widget", + * g_signal_connect (merge, "add-widget", * G_CALLBACK (add_widget), vbox); * gtk_ui_manager_add_ui_from_file (merge, "my-menus"); * gtk_ui_manager_add_ui_from_file (merge, "my-toolbars"); * gtk_ui_manager_ensure_update (merge); * gtk_widget_show (window); - * - * + * ]| * * Since: 2.4 **/ void -gtk_ui_manager_ensure_update (GtkUIManager *self) +gtk_ui_manager_ensure_update (GtkUIManager *manager) { - if (self->private_data->update_tag != 0) + if (manager->private_data->update_tag != 0) { - g_source_remove (self->private_data->update_tag); - do_updates (self); + g_source_remove (manager->private_data->update_tag); + do_updates (manager); } } @@ -2725,12 +3158,12 @@ dirty_traverse_func (GNode *node, } static void -dirty_all_nodes (GtkUIManager *self) +dirty_all_nodes (GtkUIManager *manager) { - g_node_traverse (self->private_data->root_node, + g_node_traverse (manager->private_data->root_node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, dirty_traverse_func, NULL); - queue_update (self); + queue_update (manager); } static void @@ -2743,49 +3176,61 @@ mark_node_dirty (GNode *node) NODE_INFO (p)->dirty = TRUE; } -static const gchar *const open_tag_format[] = { - "%*s\n", - "%*s\n", - "%*s\n", - "%*s\n", - "%*s\n", - "%*s\n", - "%*s\n", - "%*s\n", - NULL, - NULL, - NULL, - NULL -}; +static const gchar * +close_tag_format (NodeType type) +{ + switch (type) + { + case NODE_TYPE_UNDECIDED: return "%*s\n"; + case NODE_TYPE_ROOT: return "%*s\n"; + case NODE_TYPE_MENUBAR: return "%*s\n"; + case NODE_TYPE_MENU: return "%*s\n"; + case NODE_TYPE_TOOLBAR: return "%*s\n"; + case NODE_TYPE_MENU_PLACEHOLDER: + case NODE_TYPE_TOOLBAR_PLACEHOLDER: return "%*s\n"; + case NODE_TYPE_POPUP: return "%*s\n"; + default: return NULL; + } +} static void -print_node (GtkUIManager *self, +print_node (GtkUIManager *manager, GNode *node, gint indent_level, GString *buffer) { Node *mnode; GNode *child; + const gchar *open_fmt; + const gchar *close_fmt; mnode = node->data; - g_string_append_printf (buffer, open_tag_format[mnode->type], - indent_level, ""); + open_fmt = open_tag_format (mnode->type); + close_fmt = close_tag_format (mnode->type); + + g_string_append_printf (buffer, open_fmt, indent_level, ""); if (mnode->type != NODE_TYPE_ROOT) { @@ -2797,20 +3242,61 @@ print_node (GtkUIManager *self, g_quark_to_string (mnode->action_name)); } - g_string_append (buffer, - close_tag_format[mnode->type] ? ">\n" : "/>\n"); + g_string_append (buffer, close_fmt ? ">\n" : "/>\n"); for (child = node->children; child != NULL; child = child->next) - print_node (self, child, indent_level + 2, buffer); + print_node (manager, child, indent_level + 2, buffer); - if (close_tag_format[mnode->type]) - g_string_append_printf (buffer, close_tag_format[mnode->type], - indent_level, ""); + if (close_fmt) + g_string_append_printf (buffer, close_fmt, indent_level, ""); +} + +static gboolean +gtk_ui_manager_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + if (child) + return FALSE; + + if (strcmp (tagname, "ui") == 0) + { + ParseContext *ctx; + + ctx = g_new0 (ParseContext, 1); + ctx->state = STATE_START; + ctx->manager = GTK_UI_MANAGER (buildable); + ctx->current = NULL; + ctx->merge_id = gtk_ui_manager_new_merge_id (GTK_UI_MANAGER (buildable)); + + *data = ctx; + *parser = ui_parser; + + return TRUE; + } + + return FALSE; + +} + +static void +gtk_ui_manager_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + queue_update (GTK_UI_MANAGER (buildable)); + g_object_notify (G_OBJECT (buildable), "ui"); + g_free (data); } /** * gtk_ui_manager_get_ui: - * @self: a #GtkUIManager + * @manager: a #GtkUIManager * * Creates a UI definition of the merged UI. * @@ -2820,42 +3306,15 @@ print_node (GtkUIManager *self, * Since: 2.4 **/ gchar * -gtk_ui_manager_get_ui (GtkUIManager *self) +gtk_ui_manager_get_ui (GtkUIManager *manager) { GString *buffer; buffer = g_string_new (NULL); - gtk_ui_manager_ensure_update (self); + gtk_ui_manager_ensure_update (manager); - print_node (self, self->private_data->root_node, 0, buffer); + print_node (manager, manager->private_data->root_node, 0, buffer); return g_string_free (buffer, FALSE); } - -#ifdef G_OS_WIN32 - -#undef gtk_ui_manager_add_ui_from_file - -guint -gtk_ui_manager_add_ui_from_file (GtkUIManager *self, - const gchar *filename, - GError **error) -{ - gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error); - guint retval; - - if (utf8_filename == NULL) - return 0; - - retval = gtk_ui_manager_add_ui_from_file_utf8 (self, utf8_filename, error); - - g_free (utf8_filename); - - return retval; -} - -#endif - -#define __GTK_UI_MANAGER_C__ -#include "gtkaliasdef.c"