summaryrefslogtreecommitdiff
path: root/gtk/gtkwindow.c
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2020-03-19 18:03:16 -0400
committerMatthias Clasen <mclasen@redhat.com>2020-03-19 18:03:16 -0400
commit31db61588543a1ba0935ac8ecb2ecac574c0a836 (patch)
treea2e2092e086a9c7d618e2c9ae1fc7b5a659e8a7a /gtk/gtkwindow.c
parent3ac4c76b18cc89a841ce09f0943539f16988fd21 (diff)
downloadgtk+-31db61588543a1ba0935ac8ecb2ecac574c0a836.tar.gz
Revert "Merge branch 'disable-window-test' into 'master'"
This reverts commit 3ac4c76b18cc89a841ce09f0943539f16988fd21, reversing changes made to 6ec96d2e989d029a303b8b20ec72b86f974c0e87.
Diffstat (limited to 'gtk/gtkwindow.c')
-rw-r--r--gtk/gtkwindow.c859
1 files changed, 739 insertions, 120 deletions
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index be93fa6e90..3549009b2e 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -28,6 +28,7 @@
#include "gtkaccelgroupprivate.h"
#include "gtkapplicationprivate.h"
+#include "gtkbindings.h"
#include "gtkbox.h"
#include "gtkbuildable.h"
#include "gtkbuilderprivate.h"
@@ -53,6 +54,7 @@
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkmessagedialog.h"
+#include "gtkmnemonichash.h"
#include "gtkpointerfocusprivate.h"
#include "gtkpopovermenuprivate.h"
#include "gtkmodelbuttonprivate.h"
@@ -61,10 +63,6 @@
#include "gtkroot.h"
#include "gtknative.h"
#include "gtksettings.h"
-#include "gtkshortcut.h"
-#include "gtkshortcutcontroller.h"
-#include "gtkshortcutmanager.h"
-#include "gtkshortcuttrigger.h"
#include "gtksnapshot.h"
#include "gtkstylecontextprivate.h"
#include "gtktypebuiltins.h"
@@ -162,7 +160,7 @@
* widget that is added as a titlebar child.
*/
-#define MENU_BAR_ACCEL GDK_KEY_F10
+#define MENU_BAR_ACCEL "F10"
#define RESIZE_HANDLE_SIZE 20
#define MNEMONICS_DELAY 300 /* ms */
#define NO_CONTENT_CHILD_NAT 200
@@ -185,6 +183,8 @@ struct _GtkWindowPopover
typedef struct
{
+ GtkMnemonicHash *mnemonic_hash;
+
GtkWidget *attach_widget;
GtkWidget *default_widget;
GtkWidget *initial_focus;
@@ -197,6 +197,8 @@ typedef struct
GQueue popovers;
+ GdkModifierType mnemonic_modifier;
+
gchar *startup_id;
gchar *title;
@@ -260,7 +262,6 @@ typedef struct
GtkGesture *drag_gesture;
GtkGesture *bubble_drag_gesture;
GtkEventController *key_controller;
- GtkEventController *application_shortcut_controller;
GtkCssNode *decoration_node;
@@ -470,9 +471,6 @@ static void update_window_buttons (GtkWindow *window);
static void get_shadow_width (GtkWindow *window,
GtkBorder *shadow_width);
-static gboolean gtk_window_activate_menubar (GtkWidget *widget,
- GVariant *args,
- gpointer unused);
static GtkKeyHash *gtk_window_get_key_hash (GtkWindow *window);
static void gtk_window_free_key_hash (GtkWindow *window);
#ifdef GDK_WINDOWING_X11
@@ -507,6 +505,7 @@ static gboolean disable_startup_notification = FALSE;
static GQuark quark_gtk_window_key_hash = 0;
static GQuark quark_gtk_window_icon_info = 0;
+static GQuark quark_gtk_buildable_accels = 0;
static GtkBuildableIface *parent_buildable_iface;
@@ -529,8 +528,20 @@ static void gtk_window_buildable_set_buildable_property (GtkBuildable
GtkBuilder *builder,
const gchar *name,
const GValue *value);
+static void gtk_window_buildable_parser_finished (GtkBuildable *buildable,
+ GtkBuilder *builder);
+static gboolean gtk_window_buildable_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ GtkBuildableParser *parser,
+ gpointer *data);
+static void gtk_window_buildable_custom_finished (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer user_data);
-static void gtk_window_shortcut_manager_interface_init (GtkShortcutManagerInterface *iface);
/* GtkRoot */
static void gtk_window_root_interface_init (GtkRootInterface *iface);
static void gtk_window_native_interface_init (GtkNativeInterface *iface);
@@ -551,52 +562,41 @@ G_DEFINE_TYPE_WITH_CODE (GtkWindow, gtk_window, GTK_TYPE_BIN,
gtk_window_buildable_interface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_NATIVE,
gtk_window_native_interface_init)
- G_IMPLEMENT_INTERFACE (GTK_TYPE_SHORTCUT_MANAGER,
- gtk_window_shortcut_manager_interface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_ROOT,
gtk_window_root_interface_init))
static void
-add_tab_bindings (GtkWidgetClass *widget_class,
+add_tab_bindings (GtkBindingSet *binding_set,
GdkModifierType modifiers,
GtkDirectionType direction)
{
- GtkShortcut *shortcut;
-
- shortcut = gtk_shortcut_new_with_arguments (
- gtk_alternative_trigger_new (gtk_keyval_trigger_new (GDK_KEY_Tab, modifiers),
- gtk_keyval_trigger_new (GDK_KEY_KP_Tab, modifiers)),
- gtk_signal_action_new ("move-focus"),
- "(i)", direction);
-
- gtk_widget_class_add_shortcut (widget_class, shortcut);
-
- g_object_unref (shortcut);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
+ "move-focus", 1,
+ GTK_TYPE_DIRECTION_TYPE, direction);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
+ "move-focus", 1,
+ GTK_TYPE_DIRECTION_TYPE, direction);
}
static void
-add_arrow_bindings (GtkWidgetClass *widget_class,
+add_arrow_bindings (GtkBindingSet *binding_set,
guint keysym,
GtkDirectionType direction)
{
guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
- gtk_widget_class_add_binding_signal (widget_class, keysym, 0,
- "move-focus",
- "(i)",
- direction);
- gtk_widget_class_add_binding_signal (widget_class, keysym, GDK_CONTROL_MASK,
- "move-focus",
- "(i)",
- direction);
- gtk_widget_class_add_binding_signal (widget_class, keypad_keysym, 0,
- "move-focus",
- "(i)",
- direction);
- gtk_widget_class_add_binding_signal (widget_class, keypad_keysym, GDK_CONTROL_MASK,
- "move-focus",
- "(i)",
- direction);
+ gtk_binding_entry_add_signal (binding_set, keysym, 0,
+ "move-focus", 1,
+ GTK_TYPE_DIRECTION_TYPE, direction);
+ gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK,
+ "move-focus", 1,
+ GTK_TYPE_DIRECTION_TYPE, direction);
+ gtk_binding_entry_add_signal (binding_set, keypad_keysym, 0,
+ "move-focus", 1,
+ GTK_TYPE_DIRECTION_TYPE, direction);
+ gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_CONTROL_MASK,
+ "move-focus", 1,
+ GTK_TYPE_DIRECTION_TYPE, direction);
}
static guint32
@@ -757,11 +757,16 @@ static void
gtk_window_class_init (GtkWindowClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+ GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
+ GtkBindingSet *binding_set;
+ widget_class = (GtkWidgetClass*) klass;
+ container_class = (GtkContainerClass*) klass;
+
quark_gtk_window_key_hash = g_quark_from_static_string ("gtk-window-key-hash");
quark_gtk_window_icon_info = g_quark_from_static_string ("gtk-window-icon-info");
+ quark_gtk_buildable_accels = g_quark_from_static_string ("gtk-window-buildable-accels");
if (toplevel_list == NULL)
toplevel_list = g_list_store_new (GTK_TYPE_WIDGET);
@@ -1100,32 +1105,36 @@ gtk_window_class_init (GtkWindowClass *klass)
gtk_widget_class_install_action (widget_class, "default.activate", NULL,
gtk_window_activate_default_activate);
- gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, 0,
- "activate-focus", NULL);
- gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, 0,
- "activate-focus", NULL);
+ binding_set = gtk_binding_set_by_class (klass);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
+ "activate-focus", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0,
+ "activate-focus", 0);
- gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0,
- "activate-default", NULL);
- gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0,
- "activate-default", NULL);
- gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, 0,
- "activate-default", NULL);
-
- gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_I, GDK_CONTROL_MASK|GDK_SHIFT_MASK,
- "enable-debugging", "(b)", FALSE);
- gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_D, GDK_CONTROL_MASK|GDK_SHIFT_MASK,
- "enable-debugging", "(b)", TRUE);
-
- add_arrow_bindings (widget_class, GDK_KEY_Up, GTK_DIR_UP);
- add_arrow_bindings (widget_class, GDK_KEY_Down, GTK_DIR_DOWN);
- add_arrow_bindings (widget_class, GDK_KEY_Left, GTK_DIR_LEFT);
- add_arrow_bindings (widget_class, GDK_KEY_Right, GTK_DIR_RIGHT);
-
- add_tab_bindings (widget_class, 0, GTK_DIR_TAB_FORWARD);
- add_tab_bindings (widget_class, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
- add_tab_bindings (widget_class, GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
- add_tab_bindings (widget_class, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
+ "activate-default", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
+ "activate-default", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
+ "activate-default", 0);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_I, GDK_CONTROL_MASK|GDK_SHIFT_MASK,
+ "enable-debugging", 1,
+ G_TYPE_BOOLEAN, FALSE);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_D, GDK_CONTROL_MASK|GDK_SHIFT_MASK,
+ "enable-debugging", 1,
+ G_TYPE_BOOLEAN, TRUE);
+
+ add_arrow_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP);
+ add_arrow_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN);
+ add_arrow_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT);
+ add_arrow_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT);
+
+ add_tab_bindings (binding_set, 0, GTK_DIR_TAB_FORWARD);
+ add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
+ add_tab_bindings (binding_set, GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
+ add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_WINDOW_ACCESSIBLE);
gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_FRAME);
@@ -1716,9 +1725,9 @@ gtk_window_init (GtkWindow *window)
GtkWidget *widget;
GtkCssNode *widget_node;
GdkSeat *seat;
+ GtkEventController *motion_controller;
GtkEventController *controller;
GtkDropTargetAsync *target;
- GtkShortcut *shortcut;
widget = GTK_WIDGET (window);
@@ -1733,6 +1742,7 @@ gtk_window_init (GtkWindow *window)
priv->modal = FALSE;
priv->gravity = GDK_GRAVITY_NORTH_WEST;
priv->decorated = TRUE;
+ priv->mnemonic_modifier = GDK_MOD1_MASK;
priv->display = gdk_display_get_default ();
priv->state = GDK_SURFACE_STATE_WITHDRAWN;
@@ -1774,12 +1784,12 @@ gtk_window_init (GtkWindow *window)
g_signal_connect (seat, "device-removed",
G_CALLBACK (device_removed_cb), window);
- controller = gtk_event_controller_motion_new ();
- gtk_event_controller_set_propagation_phase (controller,
+ motion_controller = gtk_event_controller_motion_new ();
+ gtk_event_controller_set_propagation_phase (motion_controller,
GTK_PHASE_CAPTURE);
- g_signal_connect_swapped (controller, "motion",
+ g_signal_connect_swapped (motion_controller, "motion",
G_CALLBACK (gtk_window_capture_motion), window);
- gtk_widget_add_controller (widget, controller);
+ gtk_widget_add_controller (widget, motion_controller);
priv->key_controller = gtk_event_controller_key_new ();
gtk_event_controller_set_propagation_phase (priv->key_controller, GTK_PHASE_CAPTURE);
@@ -1797,15 +1807,6 @@ gtk_window_init (GtkWindow *window)
/* Shared constraint solver */
priv->constraint_solver = gtk_constraint_solver_new ();
-
- controller = gtk_shortcut_controller_new ();
- gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
-
- shortcut = gtk_shortcut_new (gtk_keyval_trigger_new (MENU_BAR_ACCEL, 0),
- gtk_callback_action_new (gtk_window_activate_menubar, NULL, NULL));
- gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
- gtk_event_controller_set_name (controller, "gtk-window-menubar-accel");
- gtk_widget_add_controller (widget, controller);
}
static GtkGesture *
@@ -2013,6 +2014,9 @@ gtk_window_buildable_interface_init (GtkBuildableIface *iface)
{
parent_buildable_iface = g_type_interface_peek_parent (iface);
iface->set_buildable_property = gtk_window_buildable_set_buildable_property;
+ iface->parser_finished = gtk_window_buildable_parser_finished;
+ iface->custom_tag_start = gtk_window_buildable_custom_tag_start;
+ iface->custom_finished = gtk_window_buildable_custom_finished;
iface->add_child = gtk_window_buildable_add_child;
}
@@ -2045,9 +2049,166 @@ gtk_window_buildable_set_buildable_property (GtkBuildable *buildable,
g_object_set_property (G_OBJECT (buildable), name, value);
}
+typedef struct {
+ gchar *name;
+ gint line;
+ gint col;
+} ItemData;
+
+static void
+item_data_free (gpointer data)
+{
+ ItemData *item_data = data;
+
+ g_free (item_data->name);
+ g_free (item_data);
+}
+
+static void
+item_list_free (gpointer data)
+{
+ GSList *list = data;
+
+ g_slist_free_full (list, item_data_free);
+}
+
+static void
+gtk_window_buildable_parser_finished (GtkBuildable *buildable,
+ GtkBuilder *builder)
+{
+ GtkWindow *window = GTK_WINDOW (buildable);
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+ GObject *object;
+ GSList *accels, *l;
+
+ if (priv->builder_visible)
+ gtk_widget_show (GTK_WIDGET (buildable));
+
+ accels = g_object_get_qdata (G_OBJECT (buildable), quark_gtk_buildable_accels);
+ for (l = accels; l; l = l->next)
+ {
+ ItemData *data = l->data;
+
+ object = _gtk_builder_lookup_object (builder, data->name, data->line, data->col);
+ if (!object)
+ continue;
+ gtk_window_add_accel_group (GTK_WINDOW (buildable), GTK_ACCEL_GROUP (object));
+ }
+
+ g_object_set_qdata (G_OBJECT (buildable), quark_gtk_buildable_accels, NULL);
+
+ parent_buildable_iface->parser_finished (buildable, builder);
+}
+
+typedef struct {
+ GObject *object;
+ GtkBuilder *builder;
+ GSList *items;
+} GSListSubParserData;
+
+static void
+window_start_element (GtkBuildableParseContext *context,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ gpointer user_data,
+ GError **error)
+{
+ GSListSubParserData *data = (GSListSubParserData*)user_data;
+
+ if (strcmp (element_name, "group") == 0)
+ {
+ const gchar *name;
+ ItemData *item_data;
+
+ if (!_gtk_builder_check_parent (data->builder, context, "accel-groups", error))
+ return;
+
+ if (!g_markup_collect_attributes (element_name, names, values, error,
+ G_MARKUP_COLLECT_STRING, "name", &name,
+ G_MARKUP_COLLECT_INVALID))
+ {
+ _gtk_builder_prefix_error (data->builder, context, error);
+ return;
+ }
+
+ item_data = g_new (ItemData, 1);
+ item_data->name = g_strdup (name);
+ gtk_buildable_parse_context_get_position (context, &item_data->line, &item_data->col);
+ data->items = g_slist_prepend (data->items, item_data);
+ }
+ else if (strcmp (element_name, "accel-groups") == 0)
+ {
+ if (!_gtk_builder_check_parent (data->builder, context, "object", error))
+ return;
+
+ if (!g_markup_collect_attributes (element_name, names, values, error,
+ G_MARKUP_COLLECT_INVALID, NULL, NULL,
+ G_MARKUP_COLLECT_INVALID))
+ _gtk_builder_prefix_error (data->builder, context, error);
+ }
+ else
+ {
+ _gtk_builder_error_unhandled_tag (data->builder, context,
+ "GtkWindow", element_name,
+ error);
+ }
+}
+
+static const GtkBuildableParser window_parser =
+ {
+ window_start_element
+ };
+
+static gboolean
+gtk_window_buildable_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ GtkBuildableParser *parser,
+ gpointer *parser_data)
+{
+ if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
+ tagname, parser, parser_data))
+ return TRUE;
+
+ if (strcmp (tagname, "accel-groups") == 0)
+ {
+ GSListSubParserData *data;
+
+ data = g_slice_new0 (GSListSubParserData);
+ data->items = NULL;
+ data->object = G_OBJECT (buildable);
+ data->builder = builder;
+
+ *parser = window_parser;
+ *parser_data = data;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
-gtk_window_shortcut_manager_interface_init (GtkShortcutManagerInterface *iface)
+gtk_window_buildable_custom_finished (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer user_data)
{
+ parent_buildable_iface->custom_finished (buildable, builder, child,
+ tagname, user_data);
+
+ if (strcmp (tagname, "accel-groups") == 0)
+ {
+ GSListSubParserData *data = (GSListSubParserData*)user_data;
+
+ g_object_set_qdata_full (G_OBJECT (buildable), quark_gtk_buildable_accels,
+ data->items, (GDestroyNotify) item_list_free);
+
+ g_slice_free (GSListSubParserData, data);
+ }
}
static GdkDisplay *
@@ -2382,6 +2543,174 @@ _gtk_window_notify_keys_changed (GtkWindow *window)
}
/**
+ * gtk_window_add_accel_group:
+ * @window: window to attach accelerator group to
+ * @accel_group: a #GtkAccelGroup
+ *
+ * Associate @accel_group with @window, such that calling
+ * gtk_accel_groups_activate() on @window will activate accelerators
+ * in @accel_group.
+ **/
+void
+gtk_window_add_accel_group (GtkWindow *window,
+ GtkAccelGroup *accel_group)
+{
+ g_return_if_fail (GTK_IS_WINDOW (window));
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+
+ _gtk_accel_group_attach (accel_group, G_OBJECT (window));
+ g_signal_connect_object (accel_group, "accel-changed",
+ G_CALLBACK (_gtk_window_notify_keys_changed),
+ window, G_CONNECT_SWAPPED);
+ _gtk_window_notify_keys_changed (window);
+}
+
+/**
+ * gtk_window_remove_accel_group:
+ * @window: a #GtkWindow
+ * @accel_group: a #GtkAccelGroup
+ *
+ * Reverses the effects of gtk_window_add_accel_group().
+ **/
+void
+gtk_window_remove_accel_group (GtkWindow *window,
+ GtkAccelGroup *accel_group)
+{
+ g_return_if_fail (GTK_IS_WINDOW (window));
+ g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
+
+ g_signal_handlers_disconnect_by_func (accel_group,
+ _gtk_window_notify_keys_changed,
+ window);
+ _gtk_accel_group_detach (accel_group, G_OBJECT (window));
+ _gtk_window_notify_keys_changed (window);
+}
+
+static GtkMnemonicHash *
+gtk_window_get_mnemonic_hash (GtkWindow *window,
+ gboolean create)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ if (!priv->mnemonic_hash && create)
+ priv->mnemonic_hash = _gtk_mnemonic_hash_new ();
+
+ return priv->mnemonic_hash;
+}
+
+/**
+ * gtk_window_add_mnemonic:
+ * @window: a #GtkWindow
+ * @keyval: the mnemonic
+ * @target: the widget that gets activated by the mnemonic
+ *
+ * Adds a mnemonic to this window.
+ */
+void
+gtk_window_add_mnemonic (GtkWindow *window,
+ guint keyval,
+ GtkWidget *target)
+{
+ g_return_if_fail (GTK_IS_WINDOW (window));
+ g_return_if_fail (GTK_IS_WIDGET (target));
+
+ _gtk_mnemonic_hash_add (gtk_window_get_mnemonic_hash (window, TRUE),
+ keyval, target);
+ _gtk_window_notify_keys_changed (window);
+}
+
+/**
+ * gtk_window_remove_mnemonic:
+ * @window: a #GtkWindow
+ * @keyval: the mnemonic
+ * @target: the widget that gets activated by the mnemonic
+ *
+ * Removes a mnemonic from this window.
+ */
+void
+gtk_window_remove_mnemonic (GtkWindow *window,
+ guint keyval,
+ GtkWidget *target)
+{
+ g_return_if_fail (GTK_IS_WINDOW (window));
+ g_return_if_fail (GTK_IS_WIDGET (target));
+
+ _gtk_mnemonic_hash_remove (gtk_window_get_mnemonic_hash (window, TRUE),
+ keyval, target);
+ _gtk_window_notify_keys_changed (window);
+}
+
+/**
+ * gtk_window_mnemonic_activate:
+ * @window: a #GtkWindow
+ * @keyval: the mnemonic
+ * @modifier: the modifiers
+ *
+ * Activates the targets associated with the mnemonic.
+ *
+ * Returns: %TRUE if the activation is done.
+ */
+gboolean
+gtk_window_mnemonic_activate (GtkWindow *window,
+ guint keyval,
+ GdkModifierType modifier)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
+
+ if (priv->mnemonic_modifier == (modifier & gtk_accelerator_get_default_mod_mask ()))
+ {
+ GtkMnemonicHash *mnemonic_hash = gtk_window_get_mnemonic_hash (window, FALSE);
+ if (mnemonic_hash)
+ return _gtk_mnemonic_hash_activate (mnemonic_hash, keyval);
+ }
+
+ return FALSE;
+}
+
+/**
+ * gtk_window_set_mnemonic_modifier:
+ * @window: a #GtkWindow
+ * @modifier: the modifier mask used to activate
+ * mnemonics on this window.
+ *
+ * Sets the mnemonic modifier for this window.
+ **/
+void
+gtk_window_set_mnemonic_modifier (GtkWindow *window,
+ GdkModifierType modifier)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ g_return_if_fail (GTK_IS_WINDOW (window));
+ g_return_if_fail ((modifier & ~GDK_MODIFIER_MASK) == 0);
+
+ priv->mnemonic_modifier = modifier;
+ _gtk_window_notify_keys_changed (window);
+}
+
+/**
+ * gtk_window_get_mnemonic_modifier:
+ * @window: a #GtkWindow
+ *
+ * Returns the mnemonic modifier for this window. See
+ * gtk_window_set_mnemonic_modifier().
+ *
+ * Returns: the modifier mask used to activate
+ * mnemonics on this window.
+ **/
+GdkModifierType
+gtk_window_get_mnemonic_modifier (GtkWindow *window)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ g_return_val_if_fail (GTK_IS_WINDOW (window), 0);
+
+ return priv->mnemonic_modifier;
+}
+
+/**
* gtk_window_get_focus:
* @window: a #GtkWindow
*
@@ -2796,9 +3125,6 @@ gtk_window_release_application (GtkWindow *window)
/* steal reference into temp variable */
application = priv->application;
priv->application = NULL;
- gtk_widget_remove_controller (GTK_WIDGET (window),
- priv->application_shortcut_controller);
- priv->application_shortcut_controller = NULL;
gtk_application_remove_window (application, window);
g_object_unref (application);
@@ -2839,18 +3165,9 @@ gtk_window_set_application (GtkWindow *window,
if (priv->application != NULL)
{
- GtkApplicationAccels *app_accels;
-
g_object_ref (priv->application);
gtk_application_add_window (priv->application, window);
-
- app_accels = gtk_application_get_application_accels (priv->application);
- priv->application_shortcut_controller = gtk_shortcut_controller_new_for_model (gtk_application_accels_get_shortcuts (app_accels));
- gtk_event_controller_set_name (priv->application_shortcut_controller, "gtk-application-shortcuts");
- gtk_event_controller_set_propagation_phase (priv->application_shortcut_controller, GTK_PHASE_CAPTURE);
- gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (priv->application_shortcut_controller), GTK_SHORTCUT_SCOPE_GLOBAL);
- gtk_widget_add_controller (GTK_WIDGET (window), priv->application_shortcut_controller);
}
_gtk_widget_update_parent_muxer (GTK_WIDGET (window));
@@ -4037,11 +4354,16 @@ gtk_window_finalize (GObject *object)
{
GtkWindow *window = GTK_WINDOW (object);
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+ GtkMnemonicHash *mnemonic_hash;
g_clear_pointer (&priv->extra_input_region, cairo_region_destroy);
g_free (priv->title);
gtk_window_release_application (window);
+ mnemonic_hash = gtk_window_get_mnemonic_hash (window, FALSE);
+ if (mnemonic_hash)
+ _gtk_mnemonic_hash_free (mnemonic_hash);
+
if (priv->geometry_info)
{
g_free (priv->geometry_info);
@@ -5360,6 +5682,8 @@ _gtk_window_query_nonaccels (GtkWindow *window,
guint accel_key,
GdkModifierType accel_mods)
{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
/* movement keys are considered locked accels */
@@ -5376,9 +5700,77 @@ _gtk_window_query_nonaccels (GtkWindow *window,
return TRUE;
}
+ /* mnemonics are considered locked accels */
+ if (accel_mods == priv->mnemonic_modifier)
+ {
+ GtkMnemonicHash *mnemonic_hash = gtk_window_get_mnemonic_hash (window, FALSE);
+ if (mnemonic_hash && _gtk_mnemonic_hash_lookup (mnemonic_hash, accel_key))
+ return TRUE;
+ }
+
return FALSE;
}
+/**
+ * gtk_window_propagate_key_event:
+ * @window: a #GtkWindow
+ * @event: a #GdkEvent
+ *
+ * Propagate a key press or release event to the focus widget and
+ * up the focus container chain until a widget handles @event.
+ * This is normally called by the default ::key_press_event and
+ * ::key_release_event handlers for toplevel windows,
+ * however in some cases it may be useful to call this directly when
+ * overriding the standard key handling for a toplevel window.
+ *
+ * Returns: %TRUE if a widget in the focus chain handled the event.
+ */
+gboolean
+gtk_window_propagate_key_event (GtkWindow *window,
+ GdkEvent *event)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+ gboolean handled = FALSE;
+ GtkWidget *widget, *focus, *target;
+
+ g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
+
+ widget = GTK_WIDGET (window);
+
+ focus = priv->focus_widget;
+ if (focus)
+ g_object_ref (focus);
+
+ target = focus;
+
+ while (!handled &&
+ focus && focus != widget &&
+ gtk_widget_get_root (focus) == GTK_ROOT (widget))
+ {
+ GtkWidget *parent;
+
+ if (gtk_widget_is_sensitive (focus))
+ {
+ handled = gtk_widget_event (focus, (GdkEvent *)event, target);
+ if (handled)
+ break;
+ }
+
+ parent = _gtk_widget_get_parent (focus);
+ if (parent)
+ g_object_ref (parent);
+
+ g_object_unref (focus);
+
+ focus = parent;
+ }
+
+ if (focus)
+ g_object_unref (focus);
+
+ return handled;
+}
+
static GtkWindowRegion
get_active_region_type (GtkWindow *window, gint x, gint y)
{
@@ -5424,6 +5816,9 @@ gtk_window_has_mnemonic_modifier_pressed (GtkWindow *window)
GList *seats, *s;
gboolean retval = FALSE;
+ if (!priv->mnemonic_modifier)
+ return FALSE;
+
seats = gdk_display_list_seats (gtk_widget_get_display (GTK_WIDGET (window)));
for (s = seats; s; s = s->next)
@@ -5432,7 +5827,7 @@ gtk_window_has_mnemonic_modifier_pressed (GtkWindow *window)
GdkModifierType mask;
gdk_device_get_state (dev, priv->surface, NULL, &mask);
- if ((mask & gtk_accelerator_get_default_mod_mask ()) == GDK_MOD1_MASK)
+ if (priv->mnemonic_modifier == (mask & gtk_accelerator_get_default_mod_mask ()))
{
retval = TRUE;
break;
@@ -7378,42 +7773,130 @@ _gtk_window_set_window_group (GtkWindow *window,
}
static gboolean
-gtk_window_activate_menubar (GtkWidget *widget,
- GVariant *args,
- gpointer unused)
+gtk_window_activate_menubar (GtkWindow *window,
+ GdkEvent *event)
{
- GtkWindow *window = GTK_WINDOW (widget);
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
- GList *tmp_menubars, *l;
- GPtrArray *menubars;
- GtkWidget *focus;
- GtkWidget *first;
+ guint keyval = 0;
+ GdkModifierType mods = 0;
- focus = gtk_window_get_focus (window);
+ gtk_accelerator_parse (MENU_BAR_ACCEL, &keyval, &mods);
- if (priv->title_box != NULL &&
- (focus == NULL || !gtk_widget_is_ancestor (focus, priv->title_box)) &&
- gtk_widget_child_focus (priv->title_box, GTK_DIR_TAB_FORWARD))
- return TRUE;
+ if (keyval == 0)
+ {
+ g_warning ("Failed to parse menu bar accelerator '%s'", MENU_BAR_ACCEL);
+ return FALSE;
+ }
- tmp_menubars = gtk_popover_menu_bar_get_viewable_menu_bars (window);
- if (tmp_menubars == NULL)
+ if (!(gdk_event_get_event_type (event) == GDK_KEY_PRESS ||
+ gdk_event_get_event_type (event) == GDK_KEY_RELEASE))
return FALSE;
- menubars = g_ptr_array_sized_new (g_list_length (tmp_menubars));;
- for (l = tmp_menubars; l; l = l->next)
- g_ptr_array_add (menubars, l->data);
+ /* FIXME this is wrong, needs to be in the global accel resolution
+ * thing, to properly consider i18n etc., but that probably requires
+ * AccelGroup changes etc.
+ */
+ if (gdk_key_event_get_keyval (event) == keyval &&
+ ((gdk_event_get_modifier_state (event) & gtk_accelerator_get_default_mod_mask ()) ==
+ (mods & gtk_accelerator_get_default_mod_mask ())))
+ {
+ GList *tmp_menubars, *l;
+ GPtrArray *menubars;
+ GtkWidget *focus;
+ GtkWidget *first;
+
+ focus = gtk_window_get_focus (window);
+
+ if (priv->title_box != NULL &&
+ (focus == NULL || !gtk_widget_is_ancestor (focus, priv->title_box)) &&
+ gtk_widget_child_focus (priv->title_box, GTK_DIR_TAB_FORWARD))
+ return TRUE;
+
+ tmp_menubars = gtk_popover_menu_bar_get_viewable_menu_bars (window);
+ if (tmp_menubars == NULL)
+ return FALSE;
- g_list_free (tmp_menubars);
+ menubars = g_ptr_array_sized_new (g_list_length (tmp_menubars));;
+ for (l = tmp_menubars; l; l = l->next)
+ g_ptr_array_add (menubars, l->data);
- gtk_widget_focus_sort (GTK_WIDGET (window), GTK_DIR_TAB_FORWARD, menubars);
+ g_list_free (tmp_menubars);
- first = g_ptr_array_index (menubars, 0);
- gtk_popover_menu_bar_select_first (GTK_POPOVER_MENU_BAR (first));
+ gtk_widget_focus_sort (GTK_WIDGET (window), GTK_DIR_TAB_FORWARD, menubars);
- g_ptr_array_free (menubars, TRUE);
+ first = g_ptr_array_index (menubars, 0);
+ gtk_popover_menu_bar_select_first (GTK_POPOVER_MENU_BAR (first));
- return TRUE;
+ g_ptr_array_free (menubars, TRUE);
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+gtk_window_mnemonic_hash_foreach (guint keyval,
+ GSList *targets,
+ gpointer data)
+{
+ struct {
+ GtkWindow *window;
+ GtkWindowKeysForeachFunc func;
+ gpointer func_data;
+ } *info = data;
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (info->window);
+
+ (*info->func) (info->window, keyval, priv->mnemonic_modifier, TRUE, info->func_data);
+}
+
+static void
+_gtk_window_keys_foreach (GtkWindow *window,
+ GtkWindowKeysForeachFunc func,
+ gpointer func_data)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+ GSList *groups;
+ GtkMnemonicHash *mnemonic_hash;
+
+ struct {
+ GtkWindow *window;
+ GtkWindowKeysForeachFunc func;
+ gpointer func_data;
+ } info;
+
+ info.window = window;
+ info.func = func;
+ info.func_data = func_data;
+
+ mnemonic_hash = gtk_window_get_mnemonic_hash (window, FALSE);
+ if (mnemonic_hash)
+ _gtk_mnemonic_hash_foreach (mnemonic_hash,
+ gtk_window_mnemonic_hash_foreach, &info);
+
+ groups = gtk_accel_groups_from_object (G_OBJECT (window));
+ while (groups)
+ {
+ GtkAccelGroup *group = groups->data;
+ gint i;
+
+ for (i = 0; i < group->priv->n_accels; i++)
+ {
+ GtkAccelKey *key = &group->priv->priv_accels[i].key;
+
+ if (key->accel_key)
+ (*func) (window, key->accel_key, key->accel_mods, FALSE, func_data);
+ }
+
+ groups = groups->next;
+ }
+
+ if (priv->application)
+ {
+ GtkApplicationAccels *app_accels;
+
+ app_accels = gtk_application_get_application_accels (priv->application);
+ gtk_application_accels_foreach_key (app_accels, window, func, func_data);
+ }
}
static void
@@ -7429,6 +7912,7 @@ struct _GtkWindowKeyEntry
{
guint keyval;
guint modifiers;
+ guint is_mnemonic : 1;
};
static void
@@ -7437,6 +7921,35 @@ window_key_entry_destroy (gpointer data)
g_slice_free (GtkWindowKeyEntry, data);
}
+static void
+add_to_key_hash (GtkWindow *window,
+ guint keyval,
+ GdkModifierType modifiers,
+ gboolean is_mnemonic,
+ gpointer data)
+{
+ GtkKeyHash *key_hash = data;
+
+ GtkWindowKeyEntry *entry = g_slice_new (GtkWindowKeyEntry);
+
+ entry->keyval = keyval;
+ entry->modifiers = modifiers;
+ entry->is_mnemonic = is_mnemonic;
+
+ /* GtkAccelGroup stores lowercased accelerators. To deal
+ * with this, if <Shift> was specified, uppercase.
+ */
+ if (modifiers & GDK_SHIFT_MASK)
+ {
+ if (keyval == GDK_KEY_Tab)
+ keyval = GDK_KEY_ISO_Left_Tab;
+ else
+ keyval = gdk_keyval_to_upper (keyval);
+ }
+
+ _gtk_key_hash_add_entry (key_hash, keyval, entry->modifiers, entry);
+}
+
static GtkKeyHash *
gtk_window_get_key_hash (GtkWindow *window)
{
@@ -7448,6 +7961,7 @@ gtk_window_get_key_hash (GtkWindow *window)
key_hash = _gtk_key_hash_new (gdk_display_get_keymap (priv->display),
(GDestroyNotify)window_key_entry_destroy);
+ _gtk_window_keys_foreach (window, add_to_key_hash, key_hash);
g_object_set_qdata (G_OBJECT (window), quark_gtk_window_key_hash, key_hash);
return key_hash;
@@ -7464,6 +7978,111 @@ gtk_window_free_key_hash (GtkWindow *window)
}
}
+/**
+ * gtk_window_activate_key:
+ * @window: a #GtkWindow
+ * @event: a #GdkEvent
+ *
+ * Activates mnemonics and accelerators for this #GtkWindow. This is normally
+ * called by the default ::key_press_event handler for toplevel windows,
+ * however in some cases it may be useful to call this directly when
+ * overriding the standard key handling for a toplevel window.
+ *
+ * Returns: %TRUE if a mnemonic or accelerator was found and activated.
+ */
+gboolean
+gtk_window_activate_key (GtkWindow *window,
+ GdkEvent *event)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+ GtkKeyHash *key_hash;
+ GtkWindowKeyEntry *found_entry = NULL;
+ gboolean enable_accels;
+
+ g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (!(gdk_event_get_event_type (event) == GDK_KEY_PRESS ||
+ gdk_event_get_event_type (event) == GDK_KEY_RELEASE))
+ return FALSE;
+
+ key_hash = gtk_window_get_key_hash (window);
+
+ if (key_hash)
+ {
+ GSList *tmp_list;
+ GSList *entries = _gtk_key_hash_lookup (key_hash,
+ gdk_key_event_get_keycode (event),
+ gdk_event_get_modifier_state (event),
+ gtk_accelerator_get_default_mod_mask (),
+ gdk_key_event_get_group (event));
+
+ g_object_get (gtk_widget_get_settings (GTK_WIDGET (window)),
+ "gtk-enable-accels", &enable_accels,
+ NULL);
+
+ for (tmp_list = entries; tmp_list; tmp_list = tmp_list->next)
+ {
+ GtkWindowKeyEntry *entry = tmp_list->data;
+ if (entry->is_mnemonic)
+ {
+ found_entry = entry;
+ break;
+ }
+ else
+ {
+ if (enable_accels && !found_entry)
+ {
+ found_entry = entry;
+ }
+ }
+ }
+
+ g_slist_free (entries);
+ }
+
+ if (found_entry)
+ {
+ if (found_entry->is_mnemonic)
+ {
+ return gtk_window_mnemonic_activate (window, found_entry->keyval,
+ found_entry->modifiers);
+ }
+ else
+ {
+ if (enable_accels)
+ {
+ if (gtk_accel_groups_activate (G_OBJECT (window), found_entry->keyval, found_entry->modifiers))
+ return TRUE;
+
+ if (priv->application)
+ {
+ GtkWidget *focused_widget;
+ GtkActionMuxer *muxer;
+ GtkApplicationAccels *app_accels;
+
+ focused_widget = gtk_window_get_focus (window);
+
+ if (focused_widget)
+ muxer = _gtk_widget_get_action_muxer (focused_widget, FALSE);
+ else
+ muxer = _gtk_widget_get_action_muxer (GTK_WIDGET (window), FALSE);
+
+ if (muxer == NULL)
+ return FALSE;
+
+ app_accels = gtk_application_get_application_accels (priv->application);
+ return gtk_application_accels_activate (app_accels,
+ G_ACTION_GROUP (muxer),
+ found_entry->keyval, found_entry->modifiers);
+ }
+ }
+ }
+ }
+
+ return gtk_window_activate_menubar (window, event);
+}
+
/*
* _gtk_window_set_is_active:
* @window: a #GtkWindow