diff options
author | Matthias Clasen <mclasen@redhat.com> | 2020-03-19 18:03:16 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2020-03-19 18:03:16 -0400 |
commit | 31db61588543a1ba0935ac8ecb2ecac574c0a836 (patch) | |
tree | a2e2092e086a9c7d618e2c9ae1fc7b5a659e8a7a /gtk/gtkwindow.c | |
parent | 3ac4c76b18cc89a841ce09f0943539f16988fd21 (diff) | |
download | gtk+-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.c | 859 |
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 |