Re: Accelerator-only actions, take two



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]