GtkApplicationWindow: Always install accelerators

We want accelerators to work, even if the menus are not shown
locally, so we can't rely on the GtkMenu code to set them up
for us. Currently, this code only installs accelerators when the
window is realized.
This commit is contained in:
Matthias Clasen 2011-12-05 17:50:17 -05:00 committed by Ryan Lortie
parent 073a924481
commit 0baa663182

View File

@ -26,6 +26,7 @@
#include "gtkmodelmenu.h"
#include "gactionmuxer.h"
#include "gtkaccelgroup.h"
#include "gtkaccelmap.h"
#include "gtkintl.h"
/**
@ -83,6 +84,7 @@ struct _GtkApplicationWindowPrivate
GSimpleActionGroup *actions;
GtkWidget *menubar;
GtkAccelGroup *accels;
GSList *accel_closures;
GMenu *app_menu_section;
GMenu *menubar_section;
@ -190,6 +192,143 @@ 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;
}
typedef struct {
GtkApplicationWindow *window;
GActionGroup *actions;
} AccelData;
/* 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)
{
AccelData *d = data;
GtkApplicationWindow *window = d->window;
GActionGroup *actions = d->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, "<Actions>/"))
return;
path = accel_path + strlen ("<Actions>/");
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;
}
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);
}
g_free (action_name);
}
static void
gtk_application_window_update_accels (GtkApplicationWindow *window)
{
GtkApplication *application;
AccelData data;
GActionMuxer *muxer;
/* FIXME: we should keep the muxer around, and listen for changes.
* Also, we should listen for accel map changes
*/
application = gtk_window_get_application (GTK_WINDOW (window));
free_accel_closures (window);
muxer = g_action_muxer_new ();
g_action_muxer_insert (muxer, "app", G_ACTION_GROUP (application));
g_action_muxer_insert (muxer, "win", G_ACTION_GROUP (window));
data.window = window;
data.actions = G_ACTION_GROUP (muxer);
gtk_accel_map_foreach (&data, add_accel_closure);
g_object_unref (muxer);
}
static void
gtk_application_window_shell_shows_app_menu_changed (GObject *object,
GParamSpec *pspec,
@ -448,6 +587,7 @@ gtk_application_window_real_realize (GtkWidget *widget)
gtk_application_window_update_shell_shows_app_menu (window, settings);
gtk_application_window_update_shell_shows_menubar (window, settings);
gtk_application_window_update_menubar (window);
gtk_application_window_update_accels (window);
GTK_WIDGET_CLASS (gtk_application_window_parent_class)
->realize (widget);
@ -545,6 +685,8 @@ 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);