Re: Accelerator-only actions, take two
- From: Matthias Clasen <maclas gmx de>
- To: gtk-devel-list gnome org
- Subject: Re: Accelerator-only actions, take two
- Date: 16 Sep 2003 01:29:59 +0200
Am Die, 2003-09-09 um 00.47 schrieb Matthias Clasen:
> Here is another attempt to provide accelerator-only actions. This patch
> also switches to installing accelerators on the actions themself, not on
> the proxies. But unlike my first attempt, the lifecycle of accelerators
> is exactly as it in the current code: they are installed when a proxy
> menuitem is connected and uninstalled when it is disconnected again. In
> order to properly handle multiple proxies for the same action, we have
> to count how often the accelerator for an action has been installed, and
> only uninstall it when the count goes down to zero again.
> Accelerator-only actions are supported via
> <accelerator name="foo" action="bar"/> elements below the root <ui>
> element in the ui descriptions.
>
> Does this look better ?
>
> Matthias
Since nobody seems to have further comments on this, I'm going to commit
an updated version of this in a few days. The only difference to the
last version is that I forgot to reinstall accel closures when reusing a
menuitem with a different action.
Matthias
Index: gtk/gtkaction.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkaction.c,v
retrieving revision 1.8
diff -u -p -r1.8 gtkaction.c
--- gtk/gtkaction.c 6 Sep 2003 23:52:00 -0000 1.8
+++ gtk/gtkaction.c 15 Sep 2003 23:27:34 -0000
@@ -59,7 +59,10 @@ struct _GtkActionPrivate
guint is_important : 1;
/* accelerator */
- GQuark accel_quark;
+ guint accel_count;
+ GtkAccelGroup *accel_group;
+ GClosure *accel_closure;
+ GQuark accel_quark;
/* list of proxy widgets */
GSList *proxies;
@@ -130,10 +133,16 @@ static void gtk_action_get_property (GOb
static GtkWidget *create_menu_item (GtkAction *action);
static GtkWidget *create_tool_item (GtkAction *action);
-static void connect_proxy (GtkAction *action,
- GtkWidget *proxy);
+static void connect_proxy (GtkAction *action,
+ GtkWidget *proxy);
static void disconnect_proxy (GtkAction *action,
GtkWidget *proxy);
+static void closure_accel_activate (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
static GObjectClass *parent_class = NULL;
static guint action_signals[LAST_SIGNAL] = { 0 };
@@ -260,7 +269,17 @@ gtk_action_init (GtkAction *action)
action->private_data->label_set = FALSE;
action->private_data->short_label_set = FALSE;
+ action->private_data->accel_count = 0;
+ action->private_data->accel_closure =
+ g_closure_new_object (sizeof (GClosure), G_OBJECT (action));
+ g_closure_set_marshal (action->private_data->accel_closure,
+ closure_accel_activate);
+ g_closure_ref (action->private_data->accel_closure);
+ g_closure_sink (action->private_data->accel_closure);
+
action->private_data->accel_quark = 0;
+ action->private_data->accel_count = 0;
+ action->private_data->accel_group = NULL;
action->private_data->proxies = NULL;
}
@@ -277,6 +296,10 @@ gtk_action_finalize (GObject *object)
g_free (action->private_data->short_label);
g_free (action->private_data->tooltip);
g_free (action->private_data->stock_id);
+
+ g_object_unref (action->private_data->accel_closure);
+ if (action->private_data->accel_group)
+ g_object_unref (action->private_data->accel_group);
}
static void
@@ -441,7 +464,7 @@ remove_proxy (GtkWidget *proxy,
GtkAction *action)
{
if (GTK_IS_MENU_ITEM (proxy))
- gtk_menu_item_set_accel_path (GTK_MENU_ITEM (proxy), NULL);
+ gtk_action_disconnect_accelerator (action);
action->private_data->proxies = g_slist_remove (action->private_data->proxies, proxy);
}
@@ -524,8 +547,8 @@ gtk_action_create_menu_proxy (GtkToolIte
}
static void
-connect_proxy (GtkAction *action,
- GtkWidget *proxy)
+connect_proxy (GtkAction *action,
+ GtkWidget *proxy)
{
g_object_ref (action);
g_object_set_data_full (G_OBJECT (proxy), "gtk-action", action,
@@ -552,6 +575,13 @@ connect_proxy (GtkAction *action,
GtkWidget *label;
/* menu item specific synchronisers ... */
+ if (action->private_data->accel_quark)
+ {
+ gtk_action_connect_accelerator (action);
+ gtk_menu_item_set_accel_path (GTK_MENU_ITEM (proxy),
+ g_quark_to_string (action->private_data->accel_quark));
+ }
+
label = GTK_BIN (proxy)->child;
/* make sure label is a label */
@@ -560,16 +590,20 @@ connect_proxy (GtkAction *action,
gtk_container_remove (GTK_CONTAINER (proxy), label);
label = NULL;
}
+
if (!label)
- {
- label = g_object_new (GTK_TYPE_ACCEL_LABEL,
- "use_underline", TRUE,
- "xalign", 0.0,
- "visible", TRUE,
- "parent", proxy,
- "accel_widget", proxy,
- NULL);
- }
+ label = g_object_new (GTK_TYPE_ACCEL_LABEL,
+ "use_underline", TRUE,
+ "xalign", 0.0,
+ "visible", TRUE,
+ "parent", proxy,
+ NULL);
+
+ if (GTK_IS_ACCEL_LABEL (label))
+ g_object_set (G_OBJECT (label),
+ "accel_closure", action->private_data->accel_closure,
+ NULL);
+
gtk_label_set_label (GTK_LABEL (label), action->private_data->label);
g_signal_connect_object (action, "notify::label",
G_CALLBACK (gtk_action_sync_label), proxy, 0);
@@ -599,15 +633,10 @@ connect_proxy (GtkAction *action,
proxy, 0);
}
- if (action->private_data->accel_quark)
- {
- gtk_menu_item_set_accel_path (GTK_MENU_ITEM (proxy),
- g_quark_to_string (action->private_data->accel_quark));
- }
-
g_signal_connect_object (proxy, "activate",
G_CALLBACK (gtk_action_activate), action,
G_CONNECT_SWAPPED);
+
}
else if (GTK_IS_TOOL_BUTTON (proxy))
{
@@ -923,6 +952,21 @@ gtk_action_unblock_activate_from (GtkAct
action);
}
+static void
+closure_accel_activate (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ if (GTK_ACTION (closure->data)->private_data->sensitive)
+ g_signal_emit (closure->data, action_signals[ACTIVATE], 0);
+
+ /* we handled the accelerator */
+ g_value_set_boolean (return_value, TRUE);
+}
+
/**
* gtk_action_set_accel_path:
* @action: the action object
@@ -938,5 +982,60 @@ void
gtk_action_set_accel_path (GtkAction *action,
const gchar *accel_path)
{
+ g_return_if_fail (GTK_IS_ACTION (action));
+
action->private_data->accel_quark = g_quark_from_string (accel_path);
+}
+
+void
+gtk_action_set_accel_group (GtkAction *action,
+ GtkAccelGroup *accel_group)
+{
+ g_return_if_fail (GTK_IS_ACTION (action));
+ g_return_if_fail (accel_group == NULL || GTK_IS_ACCEL_GROUP (accel_group));
+
+ if (accel_group)
+ g_object_ref (accel_group);
+ if (action->private_data->accel_group)
+ g_object_unref (action->private_data->accel_group);
+
+ action->private_data->accel_group = accel_group;
+}
+
+void
+gtk_action_connect_accelerator (GtkAction *action)
+{
+ g_return_if_fail (GTK_IS_ACTION (action));
+
+ if (!action->private_data->accel_quark ||
+ !action->private_data->accel_group)
+ return;
+
+ if (action->private_data->accel_count == 0)
+ {
+ const gchar *accel_path =
+ g_quark_to_string (action->private_data->accel_quark);
+
+ gtk_accel_group_connect_by_path (action->private_data->accel_group,
+ accel_path,
+ action->private_data->accel_closure);
+ }
+
+ action->private_data->accel_count++;
+}
+
+void
+gtk_action_disconnect_accelerator (GtkAction *action)
+{
+ g_return_if_fail (GTK_IS_ACTION (action));
+
+ if (!action->private_data->accel_quark ||
+ !action->private_data->accel_group)
+ return;
+
+ action->private_data->accel_count--;
+
+ if (action->private_data->accel_count == 0)
+ gtk_accel_group_disconnect (action->private_data->accel_group,
+ action->private_data->accel_closure);
}
Index: gtk/gtkaction.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkaction.h,v
retrieving revision 1.2
diff -u -p -r1.2 gtkaction.h
--- gtk/gtkaction.h 15 Sep 2003 22:21:24 -0000 1.2
+++ gtk/gtkaction.h 15 Sep 2003 23:27:34 -0000
@@ -78,30 +78,36 @@ struct _GtkActionClass
void (*_gtk_reserved4) (void);
};
-GType gtk_action_get_type (void);
-const gchar* gtk_action_get_name (GtkAction *action);
-void gtk_action_activate (GtkAction *action);
-GtkWidget * gtk_action_create_icon (GtkAction *action,
- GtkIconSize icon_size);
-GtkWidget * gtk_action_create_menu_item (GtkAction *action);
-GtkWidget * gtk_action_create_tool_item (GtkAction *action);
-void gtk_action_connect_proxy (GtkAction *action,
- GtkWidget *proxy);
-void gtk_action_disconnect_proxy (GtkAction *action,
- GtkWidget *proxy);
-GSList * gtk_action_get_proxies (GtkAction *action);
+GType gtk_action_get_type (void);
+const gchar* gtk_action_get_name (GtkAction *action);
+void gtk_action_activate (GtkAction *action);
+GtkWidget * gtk_action_create_icon (GtkAction *action,
+ GtkIconSize icon_size);
+GtkWidget * gtk_action_create_menu_item (GtkAction *action);
+GtkWidget * gtk_action_create_tool_item (GtkAction *action);
+void gtk_action_connect_proxy (GtkAction *action,
+ GtkWidget *proxy);
+void gtk_action_disconnect_proxy (GtkAction *action,
+ GtkWidget *proxy);
+GSList * gtk_action_get_proxies (GtkAction *action);
+
/* protected ... for use by child actions */
-void gtk_action_block_activate_from (GtkAction *action,
- GtkWidget *proxy);
-void gtk_action_unblock_activate_from (GtkAction *action,
- GtkWidget *proxy);
+void gtk_action_block_activate_from (GtkAction *action,
+ GtkWidget *proxy);
+void gtk_action_unblock_activate_from (GtkAction *action,
+ GtkWidget *proxy);
/* protected ... for use by action groups */
-void gtk_action_set_accel_path (GtkAction *action,
- const gchar *accel_path);
+void gtk_action_set_accel_path (GtkAction *action,
+ const gchar *accel_path);
+void gtk_action_set_accel_group (GtkAction *action,
+ GtkAccelGroup *accel_group);
+void gtk_action_connect_accelerator (GtkAction *action);
+void gtk_action_disconnect_accelerator (GtkAction *action);
+
Index: gtk/gtkuimanager.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkuimanager.c,v
retrieving revision 1.20
diff -u -p -r1.20 gtkuimanager.c
--- gtk/gtkuimanager.c 15 Sep 2003 22:21:24 -0000 1.20
+++ gtk/gtkuimanager.c 15 Sep 2003 23:27:35 -0000
@@ -56,6 +56,7 @@ typedef enum
NODE_TYPE_MENUITEM,
NODE_TYPE_TOOLITEM,
NODE_TYPE_SEPARATOR,
+ NODE_TYPE_ACCELERATOR
} NodeType;
@@ -861,6 +862,24 @@ start_element_handler (GMarkupParseConte
switch (element_name[0])
{
+ case 'a':
+ if (ctx->state == STATE_ROOT && !strcmp (element_name, "accelerator"))
+ {
+ ctx->state = STATE_ROOT;
+ ctx->current = get_child_node (self, ctx->current,
+ node_name, strlen (node_name),
+ NODE_TYPE_ACCELERATOR,
+ TRUE, FALSE);
+ if (NODE_INFO (ctx->current)->action_name == 0)
+ NODE_INFO (ctx->current)->action_name = action_quark;
+
+ node_prepend_ui_reference (NODE_INFO (ctx->current),
+ ctx->merge_id, action_quark);
+ NODE_INFO (ctx->current)->dirty = TRUE;
+
+ raise_error = FALSE;
+ }
+ break;
case 'u':
if (ctx->state == STATE_START && !strcmp (element_name, "ui"))
{
@@ -1335,6 +1354,9 @@ gtk_ui_manager_add_ui (GtkUIManager
case GTK_UI_MANAGER_POPUP:
node_type = NODE_TYPE_POPUP;
break;
+ case GTK_UI_MANAGER_ACCELERATOR:
+ node_type = NODE_TYPE_ACCELERATOR;
+ break;
default: ;
/* do nothing */
}
@@ -1702,6 +1724,9 @@ update_node (GtkUIManager *self,
goto recurse_children;
}
+ if (action)
+ gtk_action_set_accel_group (action, self->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.
*/
@@ -1733,11 +1758,7 @@ update_node (GtkUIManager *self,
break;
case NODE_TYPE_POPUP:
if (info->proxy == NULL)
- {
- info->proxy = gtk_menu_new ();
- gtk_menu_set_accel_group (GTK_MENU (info->proxy),
- self->private_data->accel_group);
- }
+ info->proxy = gtk_menu_new ();
break;
case NODE_TYPE_MENU:
{
@@ -1774,7 +1795,6 @@ update_node (GtkUIManager *self,
tearoff = gtk_tearoff_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), menu);
- gtk_menu_set_accel_group (GTK_MENU (menu), self->private_data->accel_group);
gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos);
}
}
@@ -2013,6 +2033,9 @@ update_node (GtkUIManager *self,
}
}
break;
+ case NODE_TYPE_ACCELERATOR:
+ gtk_action_connect_accelerator (action);
+ break;
}
if (action)
@@ -2047,6 +2070,8 @@ update_node (GtkUIManager *self,
gtk_widget_destroy (info->proxy);
if (info->extra)
gtk_widget_destroy (info->extra);
+ if (info->type == NODE_TYPE_ACCELERATOR)
+ gtk_action_disconnect_accelerator (info->action);
free_node (node);
g_node_destroy (node);
}
@@ -2147,7 +2172,8 @@ static const gchar *open_tag_format[] =
"%*s<popup name='%s' action=\"%s\">\n",
"%*s<menuitem name=\"%s\" action=\"%s\"/>\n",
"%*s<toolitem name=\"%s\" action=\"%s\"/>\n",
- "%*s<separator/>\n",
+ "%*s<separator name=\"%s\"/>\n",
+ "%*s<accelerator name=\"%s\" action=\"%s\"/>\n",
};
static const gchar *close_tag_format[] = {
@@ -2159,6 +2185,7 @@ static const gchar *close_tag_format[] =
"%*s</placeholder>\n",
"%*s</placeholder>\n",
"%*s</popup>\n",
+ "",
"",
"",
"",
Index: gtk/gtkuimanager.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkuimanager.h,v
retrieving revision 1.10
diff -u -p -r1.10 gtkuimanager.h
--- gtk/gtkuimanager.h 4 Sep 2003 20:39:14 -0000 1.10
+++ gtk/gtkuimanager.h 15 Sep 2003 23:27:35 -0000
@@ -81,7 +81,8 @@ typedef enum {
GTK_UI_MANAGER_POPUP,
GTK_UI_MANAGER_MENUITEM,
GTK_UI_MANAGER_TOOLITEM,
- GTK_UI_MANAGER_SEPARATOR
+ GTK_UI_MANAGER_SEPARATOR,
+ GTK_UI_MANAGER_ACCELERATOR
} GtkUIManagerItemType;
GType gtk_ui_manager_get_type (void);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]