[gtk+/wip/action-descriptions: 1/2] GtkApplication: a new approach to accels



commit 01bc118c00b43513bba8c575187e80355db7e529
Author: Ryan Lortie <desrt desrt ca>
Date:   Mon Jun 17 10:53:42 2013 -0400

    GtkApplication: a new approach to accels
    
    Rework how accels are handled on GtkApplicationWindow.
    
    Instead of having GtkApplication fill the GtkAccelMap which is then used
    by GtkApplicationWindow to create a GtkAccelGroup filled with closures
    that is then associated with the window, do it directly.
    
    GtkApplication now keeps a list of accels and their actions.
    Accelerators on a GtkApplicationWindow ask GtkApplication to execute the
    appropriate action.
    
    This saves a fair bit of complexity and memory use (due to not having to
    create all those closures and accelmap entries).  The new approach also
    supports multiple accels per action (although there is not yet a public
    API for it).

 gtk/gtkapplication.c        |  429 +++++++++++++++++++++++++++++++++++++++----
 gtk/gtkapplication.h        |   23 +++-
 gtk/gtkapplicationprivate.h |   13 ++
 gtk/gtkapplicationwindow.c  |  143 --------------
 gtk/gtkwindow.c             |   40 +++--
 gtk/gtkwindowprivate.h      |    2 +
 6 files changed, 459 insertions(+), 191 deletions(-)
---
diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c
index d605846..efa4c67 100644
--- a/gtk/gtkapplication.c
+++ b/gtk/gtkapplication.c
@@ -139,6 +139,371 @@ enum {
 
 G_DEFINE_TYPE (GtkApplication, gtk_application, G_TYPE_APPLICATION)
 
+/* Accel handling */
+typedef struct
+{
+  gchar *name;
+  gchar *target;
+} DetailedAction;
+
+typedef struct
+{
+  guint           key;
+  GdkModifierType modifier;
+} AccelKey;
+
+typedef struct
+{
+  GHashTable *action_to_accels;
+  GHashTable *accel_to_actions;
+} Accels;
+
+static AccelKey *
+accel_key_copy (const AccelKey *source)
+{
+  AccelKey *dest;
+
+  dest = g_slice_new (AccelKey);
+  dest->key = source->key;
+  dest->modifier = source->modifier;
+
+  return dest;
+}
+
+static void
+accel_key_free (gpointer data)
+{
+  AccelKey *key = data;
+
+  g_slice_free (AccelKey, key);
+}
+
+static guint
+accel_key_hash (gconstpointer data)
+{
+  const AccelKey *key = data;
+
+  return key->key + (key->modifier << 16);
+}
+
+static gboolean
+accel_key_equal (gconstpointer a,
+                 gconstpointer b)
+{
+  const AccelKey *ak = a;
+  const AccelKey *bk = b;
+
+  return ak->key == bk->key && ak->modifier == bk->modifier;
+}
+
+static gboolean
+detailed_action_equal (gconstpointer a,
+                       gconstpointer b)
+{
+  const DetailedAction *da = a;
+  const DetailedAction *db = b;
+
+  return g_str_equal (da->name, db->name) && (g_strcmp0 (da->target, db->target) == 0);
+}
+
+static guint
+detailed_action_hash (gconstpointer a)
+{
+  const DetailedAction *da = a;
+  guint hash;
+
+  hash = g_str_hash (da->name);
+
+  if (da->target)
+    hash = hash * 33 + g_str_hash (da->target);
+
+  return hash;
+}
+
+static void
+detailed_action_free (gpointer data)
+{
+  DetailedAction *da = data;
+
+  g_free (da->name);
+  g_free (da->target);
+  g_slice_free (DetailedAction, da);
+}
+
+static DetailedAction *
+detailed_action_new (const gchar *name,
+                     GVariant    *target)
+{
+  DetailedAction *da;
+
+  da = g_slice_new (DetailedAction);
+  da->name = g_strdup (name);
+  da->target = target ? g_variant_print (target, TRUE) : NULL;
+
+  return da;
+}
+
+static void
+accels_foreach_key (Accels                   *accels,
+                    GtkWindow                *window,
+                    GtkWindowKeysForeachFunc  callback,
+                    gpointer                  user_data)
+{
+  GHashTableIter iter;
+  gpointer key;
+
+  g_hash_table_iter_init (&iter, accels->accel_to_actions);
+  while (g_hash_table_iter_next (&iter, &key, NULL))
+    {
+      AccelKey *accel_key = key;
+
+      (* callback) (window, accel_key->key, accel_key->modifier, FALSE, user_data);
+    }
+}
+
+static gboolean
+accels_activate (Accels          *accels,
+                 GActionGroup    *action_group,
+                 guint            key,
+                 GdkModifierType  modifier)
+{
+  AccelKey accel_key = { key, modifier };
+  DetailedAction **actions;
+  gint i;
+
+  actions = g_hash_table_lookup (accels->accel_to_actions, &accel_key);
+
+  if (actions == NULL)
+    return FALSE;
+
+  /* We may have more than one action on a given accel.  This could be
+   * the case if we have different types of windows with different
+   * actions in each.
+   *
+   * Find the first one that will successfully activate and use it.
+   */
+  for (i = 0; actions[i]; i++)
+    {
+      const GVariantType *parameter_type;
+      gboolean enabled;
+      GVariant *target;
+
+      if (!g_action_group_query_action (action_group, actions[i]->name, &enabled, &parameter_type, NULL, 
NULL, NULL))
+        continue;
+
+      if (!enabled)
+        continue;
+
+      /* We found an action with the correct name and it's enabled.
+       * This is the action that we are going to try to invoke.
+       *
+       * There is still the possibility that the target value doesn't
+       * match the expected parameter type.  In that case, we will print
+       * a warning.
+       *
+       * Note: we want to hold a ref on the target while we're invoking
+       * the action to prevent trouble if someone uninstalls the accel
+       * from the handler.  That's not a problem since we're parsing it.
+       */
+      if (actions[i]->target)
+        {
+          GError *error = NULL;
+
+          if (parameter_type == NULL)
+            {
+              gchar *accel_str = gtk_accelerator_name (key, modifier);
+              g_warning ("Accelerator '%s' tries to invoke action '%s' with target '%s' but action has no 
parameter",
+                         accel_str, actions[i]->name, actions[i]->target);
+              g_free (accel_str);
+              return TRUE;
+            }
+
+          target = g_variant_parse (NULL, actions[i]->target, NULL, NULL, &error);
+          g_assert_no_error (error);
+          g_assert (target);
+
+          if (!g_variant_is_of_type (target, parameter_type))
+            {
+              gchar *accel_str = gtk_accelerator_name (key, modifier);
+              gchar *typestr = g_variant_type_dup_string (parameter_type);
+              g_warning ("Accelerator '%s' tries to invoke action '%s' with target '%s',"
+                         " but action expects parameter with type '%s'",
+                         accel_str, actions[i]->name, actions[i]->target, typestr);
+              g_variant_unref (target);
+              g_free (accel_str);
+              g_free (typestr);
+              return TRUE;
+            }
+        }
+      else
+        {
+          if (parameter_type != NULL)
+            {
+              gchar *accel_str = gtk_accelerator_name (key, modifier);
+              gchar *typestr = g_variant_type_dup_string (parameter_type);
+              g_warning ("Accelerator '%s' tries to invoke action '%s' without target,"
+                         " but action expects parameter with type '%s'", accel_str, actions[i]->name, 
typestr);
+              g_free (accel_str);
+              g_free (typestr);
+              return TRUE;
+            }
+
+          target = NULL;
+        }
+
+      g_action_group_activate_action (action_group, actions[i]->name, target);
+
+      if (target)
+        g_variant_unref (target);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+accels_add_entry (Accels         *accels,
+                  AccelKey       *key,
+                  DetailedAction *detailed_action)
+{
+  DetailedAction **old;
+  DetailedAction **new;
+  gint n;
+
+  old = g_hash_table_lookup (accels->accel_to_actions, key);
+  if (old != NULL)
+    for (n = 0; old[n]; n++)  /* find the length */
+      ;
+  else
+    n = 0;
+
+  new = g_new (DetailedAction *, n + 1 + 1);
+  memcpy (new, old, n * sizeof (DetailedAction *));
+  new[n] = detailed_action;
+  new[n + 1] = NULL;
+
+  g_hash_table_insert (accels->accel_to_actions, accel_key_copy (key), new);
+}
+
+static void
+accels_remove_entry (Accels         *accels,
+                     AccelKey       *key,
+                     DetailedAction *detailed_action)
+{
+  DetailedAction **old;
+  DetailedAction **new;
+  gint n, i;
+
+  /* if we can't find the entry then something has gone very wrong... */
+  old = g_hash_table_lookup (accels->accel_to_actions, key);
+  g_assert (old != NULL);
+
+  for (n = 0; old[n]; n++)  /* find the length */
+    ;
+  g_assert_cmpint (n, >, 0);
+
+  if (n == 1)
+    {
+      /* The simple case of removing the last action for an accel. */
+      g_assert (detailed_action_equal (old[0], detailed_action));
+      g_hash_table_remove (accels->accel_to_actions, key);
+      return;
+    }
+
+  for (i = 0; i < n; i++)
+    if (detailed_action_equal (old[i], detailed_action))
+      break;
+
+  /* We must have found it... */
+  g_assert_cmpint (i, <, n);
+
+  new = g_new (DetailedAction *, n - 1 + 1);
+  memcpy (new, old, i * sizeof (DetailedAction *));
+  memcpy (new + i, old + i + 1, (n - (i + 1)) * sizeof (DetailedAction *));
+  new[n - 1] = NULL;
+
+  g_hash_table_insert (accels->accel_to_actions, accel_key_copy (key), new);
+}
+
+static void
+accels_set_accels_for_action (Accels              *accels,
+                              const gchar         *action_name,
+                              GVariant            *target,
+                              const gchar * const *accelerators)
+{
+  DetailedAction *detailed_action;
+  AccelKey *keys, *old_keys;
+  gint i, n;
+
+  n = accelerators ? g_strv_length ((gchar **) accelerators) : 0;
+
+  if (n > 0)
+    {
+      keys = g_new0 (AccelKey, n + 1);
+
+      for (i = 0; i < n; i++)
+        {
+          gtk_accelerator_parse (accelerators[i], &keys[i].key, &keys[i].modifier);
+
+          if (keys[i].key == 0)
+            {
+              gchar *detailed_str;
+
+              detailed_str = g_action_print_detailed_name (action_name, target);
+              g_warning ("Unable to parse accelerator '%s' for action '%s': ignored request to install %d 
accelerators",
+                         accelerators[i], detailed_str, n);
+              g_free (detailed_str);
+              g_free (keys);
+              return;
+            }
+        }
+    }
+  else
+    keys = NULL;
+
+  detailed_action = detailed_action_new (action_name, target);
+
+  old_keys = g_hash_table_lookup (accels->action_to_accels, detailed_action);
+  if (old_keys)
+    {
+      /* We need to remove accel entries from existing keys */
+      for (i = 0; old_keys[i].key; i++)
+        accels_remove_entry (accels, &old_keys[i], detailed_action);
+    }
+
+  if (keys)
+    {
+      gint i;
+
+      g_hash_table_replace (accels->action_to_accels, detailed_action, keys);
+
+      for (i = 0; i < n; i++)
+        accels_add_entry (accels, &keys[i], detailed_action);
+    }
+  else
+    {
+      g_hash_table_remove (accels->action_to_accels, detailed_action);
+      detailed_action_free (detailed_action);
+    }
+}
+
+static void
+accels_init (Accels *accels)
+{
+  accels->accel_to_actions = g_hash_table_new_full (accel_key_hash, accel_key_equal,
+                                                    accel_key_free, g_free);
+  accels->action_to_accels = g_hash_table_new_full (detailed_action_hash, detailed_action_equal,
+                                                    detailed_action_free, g_free);
+}
+
+static void
+accels_finalize (Accels *accels)
+{
+  g_hash_table_unref (accels->accel_to_actions);
+  g_hash_table_unref (accels->action_to_accels);
+}
+
 struct _GtkApplicationPrivate
 {
   GList *windows;
@@ -147,6 +512,7 @@ struct _GtkApplicationPrivate
 
   GMenuModel      *app_menu;
   GMenuModel      *menubar;
+  Accels           accels;
 
 #ifdef GDK_WINDOWING_X11
   GDBusConnection *session_bus;
@@ -496,6 +862,8 @@ gtk_application_init (GtkApplication *application)
                                                    GTK_TYPE_APPLICATION,
                                                    GtkApplicationPrivate);
 
+  accels_init (&application->priv->accels);
+
 #ifdef GDK_WINDOWING_X11
   application->priv->next_id = 1;
 #endif
@@ -666,6 +1034,8 @@ gtk_application_finalize (GObject *object)
   g_clear_object (&application->priv->app_menu);
   g_clear_object (&application->priv->menubar);
 
+  accels_finalize (&application->priv->accels);
+
   G_OBJECT_CLASS (gtk_application_parent_class)
     ->finalize (object);
 }
@@ -977,31 +1347,13 @@ gtk_application_add_accelerator (GtkApplication *application,
                                  const gchar    *action_name,
                                  GVariant       *parameter)
 {
-  gchar *accel_path;
-  guint accel_key;
-  GdkModifierType accel_mods;
+  const gchar *accelerators[2] = { accelerator, NULL };
 
   g_return_if_fail (GTK_IS_APPLICATION (application));
+  g_return_if_fail (action_name != NULL);
+  g_return_if_fail (accelerator != NULL);
 
-  /* Call this here, since gtk_init() is only getting called in startup() */
-  _gtk_accel_map_init ();
-
-  gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
-
-  if (accel_key == 0)
-    {
-      g_warning ("Failed to parse accelerator: '%s'\n", accelerator);
-      return;
-    }
-
-  accel_path = _gtk_accel_path_for_action (action_name, parameter);
-
-  if (gtk_accel_map_lookup_entry (accel_path, NULL))
-    gtk_accel_map_change_entry (accel_path, accel_key, accel_mods, TRUE);
-  else
-    gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
-
-  g_free (accel_path);
+  accels_set_accels_for_action (&application->priv->accels, action_name, parameter, accelerators);
 }
 
 /**
@@ -1021,21 +1373,10 @@ gtk_application_remove_accelerator (GtkApplication *application,
                                     const gchar    *action_name,
                                     GVariant       *parameter)
 {
-  gchar *accel_path;
-
   g_return_if_fail (GTK_IS_APPLICATION (application));
+  g_return_if_fail (action_name != NULL);
 
-  accel_path = _gtk_accel_path_for_action (action_name, parameter);
-
-  if (!gtk_accel_map_lookup_entry (accel_path, NULL))
-    {
-      g_warning ("No accelerator found for '%s'\n", accel_path);
-      g_free (accel_path);
-      return;
-    }
-
-  gtk_accel_map_change_entry (accel_path, 0, 0, FALSE);
-  g_free (accel_path);
+  accels_set_accels_for_action (&application->priv->accels, action_name, parameter, NULL);
 }
 
 /**
@@ -1688,3 +2029,21 @@ gtk_application_is_inhibited (GtkApplication             *application,
 }
 
 #endif
+
+gboolean
+gtk_application_activate_accel (GtkApplication  *application,
+                                GActionGroup    *action_group,
+                                guint            key,
+                                GdkModifierType  modifier)
+{
+  return accels_activate (&application->priv->accels, action_group, key, modifier);
+}
+
+void
+gtk_application_foreach_accel_keys (GtkApplication           *application,
+                                    GtkWindow                *window,
+                                    GtkWindowKeysForeachFunc  callback,
+                                    gpointer                  user_data)
+{
+  accels_foreach_key (&application->priv->accels, window, callback, user_data);
+}
diff --git a/gtk/gtkapplication.h b/gtk/gtkapplication.h
index 28eed3e..6f6344d 100644
--- a/gtk/gtkapplication.h
+++ b/gtk/gtkapplication.h
@@ -127,7 +127,28 @@ GtkWindow *      gtk_application_get_window_by_id   (GtkApplication
 GDK_AVAILABLE_IN_3_6
 GtkWindow *      gtk_application_get_active_window  (GtkApplication             *application);
 
+GDK_AVAILABLE_IN_3_10
+gchar **         gtk_application_list_action_descriptions        (GtkApplication       *application);
+
+typedef struct _GtkActionDescription GtkActionDescription;
+
+GDK_AVAILABLE_IN_3_10
+void             gtk_application_add_action_description          (GtkApplication       *application,
+                                                                  GtkActionDescription *description);
+
+GDK_AVAILABLE_IN_3_10
+void             gtk_application_remove_action_description       (GtkApplication       *application,
+                                                                  const gchar          
*detailed_action_name);
+
+GDK_AVAILABLE_IN_3_10
+gchar **         gtk_application_get_accels_for_action           (GtkApplication       *application,
+                                                                  const gchar          
*detailed_action_name);
+
+GDK_AVAILABLE_IN_3_10
+void             gtk_application_set_accels_for_action           (GtkApplication       *application,
+                                                                  const gchar          *detailed_action_name,
+                                                                  const gchar * const  *accels);
+
 G_END_DECLS
 
 #endif /* __GTK_APPLICATION_H__ */
-
diff --git a/gtk/gtkapplicationprivate.h b/gtk/gtkapplicationprivate.h
index bd84b1a..04b7885 100644
--- a/gtk/gtkapplicationprivate.h
+++ b/gtk/gtkapplicationprivate.h
@@ -22,6 +22,7 @@
 #define __GTK_APPLICATION_PRIVATE_H__
 
 #include "gtkapplicationwindow.h"
+#include "gtkwindowprivate.h"
 
 G_GNUC_INTERNAL
 gboolean                gtk_application_window_publish                  (GtkApplicationWindow *window,
@@ -40,4 +41,16 @@ const gchar *           gtk_application_get_app_menu_object_path        (GtkAppl
 G_GNUC_INTERNAL
 const gchar *           gtk_application_get_menubar_object_path         (GtkApplication       *application);
 
+G_GNUC_INTERNAL
+gboolean                gtk_application_activate_accel                  (GtkApplication           
*application,
+                                                                         GActionGroup             
*action_group,
+                                                                         guint                     key,
+                                                                         GdkModifierType           modifier);
+
+G_GNUC_INTERNAL
+void                    gtk_application_foreach_accel_keys              (GtkApplication           
*application,
+                                                                         GtkWindow                *window,
+                                                                         GtkWindowKeysForeachFunc  callback,
+                                                                         gpointer                  
user_data);
+
 #endif /* __GTK_APPLICATION_PRIVATE_H__ */
diff --git a/gtk/gtkapplicationwindow.c b/gtk/gtkapplicationwindow.c
index 10fc588..4619456 100644
--- a/gtk/gtkapplicationwindow.c
+++ b/gtk/gtkapplicationwindow.c
@@ -24,8 +24,6 @@
 #include "gtkapplicationprivate.h"
 #include "gtkwidgetprivate.h"
 #include "gtkwindowprivate.h"
-#include "gtkaccelgroup.h"
-#include "gtkaccelmap.h"
 #include "gtkmenubar.h"
 #include "gtkintl.h"
 #include "gtksettings.h"
@@ -214,9 +212,6 @@ struct _GtkApplicationWindowPrivate
 {
   GSimpleActionGroup *actions;
   GtkWidget *menubar;
-  GtkAccelGroup *accels;
-  GSList *accel_closures;
-  guint accel_map_changed_id;
 
   GMenu *app_menu_section;
   GMenu *menubar_section;
@@ -370,124 +365,6 @@ gtk_application_window_update_shell_shows_menubar (GtkApplicationWindow *window,
     }
 }
 
-typedef struct {
-  GClosure closure;
-  gchar *action_name;
-  GVariant *parameter;
-} AccelClosure;
-
-static void
-accel_activate (GClosure     *closure,
-                GValue       *return_value,
-                guint         n_param_values,
-                const GValue *param_values,
-                gpointer      invocation_hint,
-                gpointer      marshal_data)
-{
-  AccelClosure *aclosure = (AccelClosure*)closure;
-  GActionGroup *actions;
-
-  actions = G_ACTION_GROUP (closure->data);
-  if (g_action_group_get_action_enabled (actions, aclosure->action_name))
-    {
-       g_action_group_activate_action (actions, aclosure->action_name, aclosure->parameter);
-
-      /* we handled the accelerator */
-      g_value_set_boolean (return_value, TRUE);
-    }
-}
-
-static void
-free_accel_closures (GtkApplicationWindow *window)
-{
-  GSList *l;
-
-  for (l = window->priv->accel_closures; l; l = l->next)
-    {
-       AccelClosure *closure = l->data;
-
-       gtk_accel_group_disconnect (window->priv->accels, &closure->closure);
-
-       g_object_unref (closure->closure.data);
-       if (closure->parameter)
-         g_variant_unref (closure->parameter);
-       g_free (closure->action_name);
-       g_closure_invalidate (&closure->closure);
-       g_closure_unref (&closure->closure);
-    }
-  g_slist_free (window->priv->accel_closures);
-  window->priv->accel_closures = NULL;
-}
-
-/* Hack. We iterate over the accel map instead of the actions,
- * in order to pull the parameters out of accel map entries
- */
-static void
-add_accel_closure (gpointer         data,
-                   const gchar     *accel_path,
-                   guint            accel_key,
-                   GdkModifierType  accel_mods,
-                   gboolean         changed)
-{
-  GtkApplicationWindow *window = data;
-  GActionGroup *actions;
-  const gchar *path;
-  const gchar *p;
-  gchar *action_name;
-  GVariant *parameter;
-  AccelClosure *closure;
-
-  if (accel_key == 0)
-    return;
-
-  if (!g_str_has_prefix (accel_path, "<GAction>/"))
-    return;
-
-  path = accel_path + strlen ("<GAction>/");
-  p = strchr (path, '/');
-  if (p)
-    {
-      action_name = g_strndup (path, p - path);
-      parameter = g_variant_parse (NULL, p + 1, NULL, NULL, NULL);
-      if (!parameter)
-        g_warning ("Failed to parse parameter from '%s'\n", accel_path);
-    }
-  else
-    {
-      action_name = g_strdup (path);
-      parameter = NULL;
-    }
-
-  actions = G_ACTION_GROUP (_gtk_widget_get_action_muxer (GTK_WIDGET (window)));
-  if (g_action_group_has_action (actions, action_name))
-    {
-      closure = (AccelClosure*) g_closure_new_object (sizeof (AccelClosure), g_object_ref (actions));
-      g_closure_set_marshal (&closure->closure, accel_activate);
-
-      closure->action_name = g_strdup (action_name);
-      closure->parameter = parameter ? g_variant_ref_sink (parameter) : NULL;
-
-      window->priv->accel_closures = g_slist_prepend (window->priv->accel_closures, g_closure_ref 
(&closure->closure));
-      g_closure_sink (&closure->closure);
-
-      gtk_accel_group_connect_by_path (window->priv->accels, accel_path, &closure->closure);
-    }
-  else if (parameter)
-    {
-      g_variant_unref (parameter);
-    }
-
-  g_free (action_name);
-}
-
-static void
-gtk_application_window_update_accels (GtkApplicationWindow *window)
-{
-  free_accel_closures (window);
-
-  gtk_accel_map_foreach (window, add_accel_closure);
-}
-
 static void
 gtk_application_window_shell_shows_app_menu_changed (GObject    *object,
                                                      GParamSpec *pspec,
@@ -757,12 +634,6 @@ gtk_application_window_real_realize (GtkWidget *widget)
   gtk_application_window_update_shell_shows_menubar (window, settings);
   gtk_application_window_update_menubar (window);
 
-  /* Update the accelerators, and ensure we do again
-   * if the accel map changes */
-  gtk_application_window_update_accels (window);
-  window->priv->accel_map_changed_id = g_signal_connect_swapped (gtk_accel_map_get (), "changed",
-                                                                G_CALLBACK 
(gtk_application_window_update_accels), window);
-
   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
     ->realize (widget);
 
@@ -799,7 +670,6 @@ gtk_application_window_real_realize (GtkWidget *widget)
 static void
 gtk_application_window_real_unrealize (GtkWidget *widget)
 {
-  GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
   GtkSettings *settings;
 
   settings = gtk_widget_get_settings (widget);
@@ -807,8 +677,6 @@ gtk_application_window_real_unrealize (GtkWidget *widget)
   g_signal_handlers_disconnect_by_func (settings, gtk_application_window_shell_shows_app_menu_changed, 
widget);
   g_signal_handlers_disconnect_by_func (settings, gtk_application_window_shell_shows_menubar_changed, 
widget);
 
-  g_signal_handler_disconnect (gtk_accel_map_get (), window->priv->accel_map_changed_id);
-
   GTK_WIDGET_CLASS (gtk_application_window_parent_class)
     ->unrealize (widget);
 }
@@ -932,12 +800,9 @@ gtk_application_window_dispose (GObject *object)
       window->priv->menubar = NULL;
     }
 
-  free_accel_closures (window);
-
   g_clear_object (&window->priv->app_menu_section);
   g_clear_object (&window->priv->menubar_section);
   g_clear_object (&window->priv->actions);
-  g_clear_object (&window->priv->accels);
 
   G_OBJECT_CLASS (gtk_application_window_parent_class)
     ->dispose (object);
@@ -951,8 +816,6 @@ gtk_application_window_init (GtkApplicationWindow *window)
   window->priv->actions = gtk_application_window_actions_new (window);
   window->priv->app_menu_section = g_menu_new ();
   window->priv->menubar_section = g_menu_new ();
-  window->priv->accels = gtk_accel_group_new ();
-  gtk_window_add_accel_group (GTK_WINDOW (window), window->priv->accels);
 
   gtk_widget_insert_action_group (GTK_WIDGET (window), "win", G_ACTION_GROUP (window->priv->actions));
 
@@ -1075,12 +938,6 @@ gtk_application_window_set_show_menubar (GtkApplicationWindow *window,
     }
 }
 
-GtkAccelGroup *
-gtk_application_window_get_accel_group (GtkApplicationWindow *window)
-{
-  return window->priv->accels;
-}
-
 /**
  * gtk_application_window_get_id:
  * @window: a #GtkApplicationWindow
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index f4b3008..2c15494 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -54,6 +54,7 @@
 #include "gtkbutton.h"
 #include "gtkheaderbar.h"
 #include "a11y/gtkwindowaccessible.h"
+#include "gtkapplicationprivate.h"
 
 #include "deprecated/gtkstyle.h"
 
@@ -463,7 +464,6 @@ static void     resize_grip_destroy_window            (GtkWindow    *window);
 static void     update_grip_visibility                (GtkWindow    *window);
 static void     update_window_buttons                 (GtkWindow    *window);
 
-static void        gtk_window_notify_keys_changed (GtkWindow   *window);
 static GtkKeyHash *gtk_window_get_key_hash        (GtkWindow   *window);
 static void        gtk_window_free_key_hash       (GtkWindow   *window);
 static void       gtk_window_on_composited_changed (GdkScreen *screen,
@@ -2164,8 +2164,8 @@ handle_keys_changed (gpointer data)
   return FALSE;
 }
 
-static void
-gtk_window_notify_keys_changed (GtkWindow *window)
+void
+_gtk_window_notify_keys_changed (GtkWindow *window)
 {
   GtkWindowPrivate *priv = window->priv;
 
@@ -2191,9 +2191,9 @@ gtk_window_add_accel_group (GtkWindow     *window,
 
   _gtk_accel_group_attach (accel_group, G_OBJECT (window));
   g_signal_connect_object (accel_group, "accel-changed",
-                          G_CALLBACK (gtk_window_notify_keys_changed),
+                          G_CALLBACK (_gtk_window_notify_keys_changed),
                           window, G_CONNECT_SWAPPED);
-  gtk_window_notify_keys_changed (window);
+  _gtk_window_notify_keys_changed (window);
 }
 
 /**
@@ -2211,10 +2211,10 @@ gtk_window_remove_accel_group (GtkWindow     *window,
   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
 
   g_signal_handlers_disconnect_by_func (accel_group,
-                                       gtk_window_notify_keys_changed,
+                                       _gtk_window_notify_keys_changed,
                                        window);
   _gtk_accel_group_detach (accel_group, G_OBJECT (window));
-  gtk_window_notify_keys_changed (window);
+  _gtk_window_notify_keys_changed (window);
 }
 
 static GtkMnemonicHash *
@@ -2247,7 +2247,7 @@ gtk_window_add_mnemonic (GtkWindow *window,
 
   _gtk_mnemonic_hash_add (gtk_window_get_mnemonic_hash (window, TRUE),
                          keyval, target);
-  gtk_window_notify_keys_changed (window);
+  _gtk_window_notify_keys_changed (window);
 }
 
 /**
@@ -2268,7 +2268,7 @@ gtk_window_remove_mnemonic (GtkWindow *window,
   
   _gtk_mnemonic_hash_remove (gtk_window_get_mnemonic_hash (window, TRUE),
                             keyval, target);
-  gtk_window_notify_keys_changed (window);
+  _gtk_window_notify_keys_changed (window);
 }
 
 /**
@@ -2322,7 +2322,7 @@ gtk_window_set_mnemonic_modifier (GtkWindow      *window,
   priv = window->priv;
 
   priv->mnemonic_modifier = modifier;
-  gtk_window_notify_keys_changed (window);
+  _gtk_window_notify_keys_changed (window);
 }
 
 /**
@@ -2954,6 +2954,8 @@ gtk_window_set_application (GtkWindow      *window,
       /* don't use a normal cast: application may be NULL */
       gtk_widget_insert_action_group (GTK_WIDGET (window), "app", (GActionGroup *) application);
 
+      _gtk_window_notify_keys_changed (window);
+
       g_object_notify (G_OBJECT (window), "application");
     }
 }
@@ -10673,6 +10675,9 @@ _gtk_window_keys_foreach (GtkWindow                *window,
       
       groups = groups->next;
     }
+
+  if (window->priv->application)
+    gtk_application_foreach_accel_keys (window->priv->application, window, func, func_data);
 }
 
 static void
@@ -10824,8 +10829,19 @@ gtk_window_activate_key (GtkWindow   *window,
       else
         {
           if (enable_accels)
-            return gtk_accel_groups_activate (G_OBJECT (window), found_entry->keyval,
-                                              found_entry->modifiers);
+            {
+              if (gtk_accel_groups_activate (G_OBJECT (window), found_entry->keyval, found_entry->modifiers))
+                return TRUE;
+
+              if (window->priv->application)
+                {
+                  GtkActionMuxer *muxer = _gtk_widget_get_action_muxer (GTK_WIDGET (window));
+
+                  return gtk_application_activate_accel (window->priv->application,
+                                                         G_ACTION_GROUP (muxer),
+                                                         found_entry->keyval, found_entry->modifiers);
+                }
+            }
         }
     }
 
diff --git a/gtk/gtkwindowprivate.h b/gtk/gtkwindowprivate.h
index 507d796..3c27e2e 100644
--- a/gtk/gtkwindowprivate.h
+++ b/gtk/gtkwindowprivate.h
@@ -82,6 +82,8 @@ gboolean        _gtk_window_query_nonaccels     (GtkWindow      *window,
 
 void            _gtk_window_schedule_mnemonics_visible (GtkWindow *window);
 
+void            _gtk_window_notify_keys_changed (GtkWindow *window);
+
 G_END_DECLS
 
 #endif /* __GTK_WINDOW_PRIVATE_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]