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).

This patch (and the ones before) Reviewed and ACK'd by Matthias Clasen.
This commit is contained in:
Ryan Lortie 2013-07-09 23:44:51 -04:00
parent afa8b0178f
commit 9a6ee36e9c
6 changed files with 522 additions and 191 deletions

View File

@ -135,12 +135,345 @@ enum {
PROP_ACTIVE_WINDOW
};
/* Accel handling */
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 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 };
const gchar **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;
const gchar *action_name;
const gchar *sep;
gboolean enabled;
GVariant *target;
sep = strrchr (actions[i], '|');
action_name = sep + 1;
if (!g_action_group_query_action (action_group, action_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] != sep) /* if it has a 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, but action has no parameter",
accel_str, action_name);
g_free (accel_str);
return TRUE;
}
target = g_variant_parse (NULL, actions[i], sep, 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);
gchar *targetstr = g_variant_print (target, TRUE);
g_warning ("Accelerator '%s' tries to invoke action '%s' with target '%s',"
" but action expects parameter with type '%s'", accel_str, action_name, targetstr, typestr);
g_variant_unref (target);
g_free (targetstr);
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, action_name, typestr);
g_free (accel_str);
g_free (typestr);
return TRUE;
}
target = NULL;
}
g_action_group_activate_action (action_group, action_name, target);
if (target)
g_variant_unref (target);
return TRUE;
}
return FALSE;
}
static void
accels_add_entry (Accels *accels,
AccelKey *key,
const gchar *action_and_target)
{
const gchar **old;
const gchar **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 (const gchar *, n + 1 + 1);
memcpy (new, old, n * sizeof (const gchar *));
new[n] = action_and_target;
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,
const gchar *action_and_target)
{
const gchar **old;
const gchar **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_cmpstr (old[0], ==, action_and_target);
g_hash_table_remove (accels->accel_to_actions, key);
return;
}
for (i = 0; i < n; i++)
if (g_str_equal (old[i], action_and_target))
break;
/* We must have found it... */
g_assert_cmpint (i, <, n);
new = g_new (const gchar *, n - 1 + 1);
memcpy (new, old, i * sizeof (const gchar *));
memcpy (new + i, old + i + 1, (n - (i + 1)) * sizeof (const gchar *));
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_and_target,
const gchar * const *accelerators)
{
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)
{
g_warning ("Unable to parse accelerator '%s': ignored request to install %d accelerators",
accelerators[i], n);
g_free (keys);
return;
}
}
}
else
keys = NULL;
old_keys = g_hash_table_lookup (accels->action_to_accels, action_and_target);
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], action_and_target);
}
if (keys)
{
gchar *my_key;
gint i;
my_key = g_strdup (action_and_target);
g_hash_table_replace (accels->action_to_accels, my_key, keys);
for (i = 0; i < n; i++)
accels_add_entry (accels, &keys[i], my_key);
}
else
g_hash_table_remove (accels->action_to_accels, action_and_target);
}
gchar **
accels_get_accels_for_action (Accels *accels,
const gchar *action_and_target)
{
AccelKey *keys;
gchar **result;
gint n, i = 0;
keys = g_hash_table_lookup (accels->action_to_accels, action_and_target);
if (!keys)
return g_new0 (gchar *, 0 + 1);
for (n = 0; keys[n].key; n++)
;
result = g_new0 (gchar *, n + 1);
for (i = 0; i < n; i++)
result[i] = gtk_accelerator_name (keys[i].key, keys[i].modifier);
return result;
}
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 (g_str_hash, g_str_equal, g_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;
GMenuModel *app_menu;
GMenuModel *menubar;
Accels accels;
gboolean register_session;
GtkActionMuxer *muxer;
@ -404,7 +737,6 @@ gtk_application_startup (GApplication *g_application)
G_APPLICATION_CLASS (gtk_application_parent_class)
->startup (g_application);
application->priv->muxer = gtk_action_muxer_new ();
gtk_action_muxer_insert (application->priv->muxer, "app", G_ACTION_GROUP (application));
gtk_init (0, 0);
@ -496,6 +828,10 @@ gtk_application_init (GtkApplication *application)
{
application->priv = gtk_application_get_instance_private (application);
application->priv->muxer = gtk_action_muxer_new ();
accels_init (&application->priv->accels);
#ifdef GDK_WINDOWING_X11
application->priv->next_id = 1;
#endif
@ -666,6 +1002,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);
}
@ -941,6 +1279,15 @@ gtk_application_get_active_window (GtkApplication *application)
return application->priv->windows ? application->priv->windows->data : NULL;
}
static void
gtk_application_update_accels (GtkApplication *application)
{
GList *l;
for (l = application->priv->windows; l != NULL; l = l->next)
_gtk_window_notify_keys_changed (l->data);
}
/**
* gtk_application_add_accelerator:
* @application: a #GtkApplication
@ -975,31 +1322,18 @@ 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 };
gchar *action_and_target;
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);
action_and_target = gtk_print_action_and_target (NULL, action_name, parameter);
accels_set_accels_for_action (&application->priv->accels, action_and_target, accelerators);
gtk_action_muxer_set_primary_accel (application->priv->muxer, action_and_target, accelerator);
gtk_application_update_accels (application);
g_free (action_and_target);
}
/**
@ -1019,21 +1353,16 @@ gtk_application_remove_accelerator (GtkApplication *application,
const gchar *action_name,
GVariant *parameter)
{
gchar *accel_path;
gchar *action_and_target;
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);
action_and_target = gtk_print_action_and_target (NULL, action_name, parameter);
accels_set_accels_for_action (&application->priv->accels, action_and_target, NULL);
gtk_action_muxer_set_primary_accel (application->priv->muxer, action_and_target, NULL);
gtk_application_update_accels (application);
g_free (action_and_target);
}
/**
@ -1707,3 +2036,106 @@ gtk_application_get_parent_muxer_for_window (GtkWindow *window)
return application->priv->muxer;
}
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);
}
gchar **
gtk_application_list_action_descriptions (GtkApplication *application)
{
GHashTableIter iter;
gchar **result;
gint n, i = 0;
gpointer key;
n = g_hash_table_size (application->priv->accels.action_to_accels);
result = g_new (gchar *, n + 1);
g_hash_table_iter_init (&iter, application->priv->accels.action_to_accels);
while (g_hash_table_iter_next (&iter, &key, NULL))
{
const gchar *action_and_target = key;
const gchar *sep;
GVariant *target;
sep = strrchr (action_and_target, '|');
target = g_variant_parse (NULL, action_and_target, sep, NULL, NULL);
result[i++] = g_action_print_detailed_name (sep + 1, target);
if (target)
g_variant_unref (target);
}
g_assert_cmpint (i, ==, n);
result[i] = NULL;
return result;
}
gchar *
normalise_detailed_name (const gchar *detailed_action_name)
{
GError *error = NULL;
gchar *action_and_target;
gchar *action_name;
GVariant *target;
g_action_parse_detailed_name (detailed_action_name, &action_name, &target, &error);
g_assert_no_error (error);
action_and_target = gtk_print_action_and_target (NULL, action_name, target);
if (target)
g_variant_unref (target);
g_free (action_name);
return action_and_target;
}
void
gtk_application_set_accels_for_action (GtkApplication *application,
const gchar *detailed_action_name,
const gchar * const *accels)
{
gchar *action_and_target;
g_return_if_fail (GTK_IS_APPLICATION (application));
g_return_if_fail (detailed_action_name != NULL);
action_and_target = normalise_detailed_name (detailed_action_name);
accels_set_accels_for_action (&application->priv->accels, action_and_target, accels);
gtk_action_muxer_set_primary_accel (application->priv->muxer, action_and_target, accels[0]);
gtk_application_update_accels (application);
g_free (action_and_target);
}
gchar **
gtk_application_get_accels_for_action (GtkApplication *application,
const gchar *detailed_action_name)
{
gchar *action_and_target;
gchar **accels;
g_return_if_fail (GTK_IS_APPLICATION (application));
g_return_if_fail (detailed_action_name != NULL);
action_and_target = normalise_detailed_name (detailed_action_name);
accels = accels_get_accels_for_action (&application->priv->accels, action_and_target);
g_free (action_and_target);
return accels;
}

View File

@ -127,7 +127,18 @@ 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);
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__ */

View File

@ -22,6 +22,7 @@
#define __GTK_APPLICATION_PRIVATE_H__
#include "gtkapplicationwindow.h"
#include "gtkwindowprivate.h"
#include "gtkactionmuxer.h"
@ -45,4 +46,16 @@ const gchar * gtk_application_get_menubar_object_path (GtkAppl
G_GNUC_INTERNAL
GtkActionMuxer * gtk_application_get_parent_muxer_for_window (GtkWindow *window);
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__ */

View File

@ -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"
@ -217,9 +215,6 @@ struct _GtkApplicationWindowPrivate
{
GSimpleActionGroup *actions;
GtkWidget *menubar;
GtkAccelGroup *accels;
GSList *accel_closures;
guint accel_map_changed_id;
gboolean show_menubar;
GMenu *app_menu_section;
@ -373,124 +368,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,
@ -761,12 +638,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);
@ -803,7 +674,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);
@ -811,8 +681,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);
}
@ -956,12 +824,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);
@ -975,8 +840,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));
@ -1098,12 +961,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

View File

@ -55,6 +55,7 @@
#include "gtkbutton.h"
#include "gtkheaderbar.h"
#include "a11y/gtkwindowaccessible.h"
#include "gtkapplicationprivate.h"
#ifdef GDK_WINDOWING_X11
#include "x11/gdkx.h"
@ -467,7 +468,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,
@ -2172,8 +2172,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;
@ -2199,9 +2199,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);
}
/**
@ -2219,10 +2219,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 *
@ -2255,7 +2255,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);
}
/**
@ -2276,7 +2276,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);
}
/**
@ -2330,7 +2330,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);
}
/**
@ -2960,6 +2960,8 @@ gtk_window_set_application (GtkWindow *window,
_gtk_widget_update_parent_muxer (GTK_WIDGET (window));
_gtk_window_notify_keys_changed (window);
g_object_notify (G_OBJECT (window), "application");
}
}
@ -11105,6 +11107,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
@ -11256,8 +11261,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);
}
}
}
}

View File

@ -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__ */