* 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 <config.h>
+#include "config.h"
#include <string.h>
+#include "gtkaccellabel.h"
+#include "gtkactivatable.h"
#include "gtkbuildable.h"
+#include "gtkimagemenuitem.h"
#include "gtkintl.h"
#include "gtkmarshalers.h"
#include "gtkmenu.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 DEBUG_UI_MANAGER
-typedef enum
+typedef enum
{
NODE_TYPE_UNDECIDED,
NODE_TYPE_ROOT,
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))
* 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),
* 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),
* 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),
* 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),
* 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),
* 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),
return;
group = gtk_ui_manager_get_accel_group (uimgr);
- groups = gtk_accel_groups_from_object (toplevel);
+ 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_connect (widget, "hierarchy-changed",
G_CALLBACK (child_hierarchy_changed_cb),
GTK_UI_MANAGER (buildable));
- return G_OBJECT (widget);
+ return g_object_ref (widget);
}
static void
GtkActionGroup *action_group,
gint pos)
{
+#ifdef G_ENABLE_DEBUG
+ GList *l;
+ const char *group_name;
+#endif
+
g_return_if_fail (GTK_IS_UI_MANAGER (self));
g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
g_return_if_fail (g_list_find (self->private_data->action_groups,
action_group) == NULL);
+#ifdef G_ENABLE_DEBUG
+ group_name = gtk_action_group_get_name (action_group);
+
+ for (l = self->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);
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), 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,
NULL);
/* dirty all nodes, as action bindings may change */
g_list_remove (self->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), 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,
NULL);
g_object_unref (action_group);
*
* Returns the list of action groups associated with @self.
*
- * 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
*
* Returns the #GtkAccelGroup associated with @self.
*
- * Return value: the #GtkAccelGroup.
+ * Return value: (transfer none): the #GtkAccelGroup.
*
* Since: 2.4
**/
* 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
* #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
**/
return GTK_UI_MANAGER_GET_CLASS (self)->get_action (self, 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,
GNode *parent,
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;
}
}
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);
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;
{
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
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;
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;
* @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.
*
* If @type is %GTK_UI_MANAGER_AUTO, GTK+ inserts a menuitem, toolitem or
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:
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);
gtk_ui_manager_remove_ui (GtkUIManager *self,
guint merge_id)
{
+ g_return_if_fail (GTK_IS_UI_MANAGER (self));
+
g_node_traverse (self->private_data->root_node,
G_POST_ORDER, G_TRAVERSE_ALL, -1,
remove_ui, GUINT_TO_POINTER (merge_id));
/**
* _gtk_menu_is_empty:
- * @menu: a #GtkMenu or %NULL
+ * @menu: (allow-none): 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
static void
update_node (GtkUIManager *self,
GNode *node,
- gboolean in_popup)
+ gboolean in_popup,
+ gboolean popup_accels)
{
Node *info;
GNode *child;
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 (",
}
}
- gtk_action_disconnect_proxy (info->action, info->proxy);
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), NULL);
gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
info->proxy);
g_object_unref (info->proxy);
}
}
else
- gtk_action_connect_proxy (action, info->proxy);
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), action);
if (prev_submenu)
{
g_signal_handlers_disconnect_by_func (info->proxy,
G_CALLBACK (update_smart_separators),
NULL);
- gtk_action_disconnect_proxy (info->action, info->proxy);
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), NULL);
gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
info->proxy);
g_object_unref (info->proxy);
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);
}
G_CALLBACK (update_smart_separators),
NULL);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
- gtk_action_connect_proxy (action, info->proxy);
+ 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)
+ if (in_popup && !popup_accels)
{
/* don't show accels in popups */
- GtkWidget *label = GTK_BIN (info->proxy)->child;
- g_object_set (label, "accel-closure", NULL, NULL);
+ GtkWidget *child = gtk_bin_get_child (GTK_BIN (info->proxy));
+ if (GTK_IS_ACCEL_LABEL (child))
+ g_object_set (child, "accel-closure", NULL, NULL);
}
}
g_signal_handlers_disconnect_by_func (info->proxy,
G_CALLBACK (update_smart_separators),
NULL);
- gtk_action_disconnect_proxy (info->action, info->proxy);
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (info->proxy), NULL);
gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
info->proxy);
g_object_unref (info->proxy);
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);
}
if (info->proxy)
{
- /* FIXME: we must re-set the tooltip, since tooltips on
- * toolitems can't be set before the toolitem is added
- * to the toolbar.
- */
- gchar *tooltip;
-
- g_object_get (G_OBJECT (action), "tooltip", &tooltip, NULL);
- g_object_set (G_OBJECT (action), "tooltip", tooltip, NULL);
- g_free (tooltip);
-
g_signal_connect (info->proxy, "notify::visible",
G_CALLBACK (update_smart_separators), NULL);
}
current = child;
child = current->next;
- update_node (self, current, in_popup);
+ update_node (self, current, in_popup, popup_accels);
}
if (info->proxy)
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);
* 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 (self, self->private_data->root_node, FALSE, FALSE);
self->private_data->update_tag = 0;
* 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:
- * <informalexample>
- * <programlisting>
+ * |[
* 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);
- * </programlisting>
- * </informalexample>
+ * ]|
*
* Since: 2.4
**/
return g_string_free (buffer, FALSE);
}
-#ifdef G_OS_WIN32
+#if defined (G_OS_WIN32) && !defined (_WIN64)
#undef gtk_ui_manager_add_ui_from_file