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 | |
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.
120 files changed, 8634 insertions, 7769 deletions
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index 7e4046cb6a..4dde63322a 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -210,7 +210,6 @@ <file>search_entry.c</file> <file>search_entry2.c</file> <file>shortcuts.c</file> - <file>shortcut_triggers.c</file> <file>sizegroup.c</file> <file>sidebar.c</file> <file>sliding_puzzle.c</file> diff --git a/demos/gtk-demo/fontplane.c b/demos/gtk-demo/fontplane.c index 1f7e074adb..b7b07cb7ad 100644 --- a/demos/gtk-demo/fontplane.c +++ b/demos/gtk-demo/fontplane.c @@ -141,6 +141,17 @@ update_value (GtkFontPlane *plane, } static void +hold_action (GtkGestureLongPress *gesture, + gdouble x, + gdouble y, + GtkFontPlane *plane) +{ + gboolean handled; + + g_signal_emit_by_name (plane, "popup-menu", &handled); +} + +static void plane_drag_gesture_begin (GtkGestureDrag *gesture, gdouble start_x, gdouble start_y, @@ -150,6 +161,13 @@ plane_drag_gesture_begin (GtkGestureDrag *gesture, button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + if (button == GDK_BUTTON_SECONDARY) + { + gboolean handled; + + g_signal_emit_by_name (plane, "popup-menu", &handled); + } + if (button != GDK_BUTTON_PRIMARY) { gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); @@ -200,6 +218,13 @@ gtk_font_plane_init (GtkFontPlane *plane) G_CALLBACK (plane_drag_gesture_end), plane); gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0); gtk_widget_add_controller (GTK_WIDGET (plane), GTK_EVENT_CONTROLLER (gesture)); + + gesture = gtk_gesture_long_press_new (); + g_signal_connect (gesture, "pressed", + G_CALLBACK (hold_action), plane); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), + TRUE); + gtk_widget_add_controller (GTK_WIDGET (plane), GTK_EVENT_CONTROLLER (gesture)); } static void diff --git a/demos/gtk-demo/main.c b/demos/gtk-demo/main.c index aef7b1922a..c552f9874e 100644 --- a/demos/gtk-demo/main.c +++ b/demos/gtk-demo/main.c @@ -310,6 +310,7 @@ static const char *types[] = "GtkIconView ", "GtkCellRendererText ", "GtkContainer ", + "GtkAccelGroup ", "GtkPaned ", "GtkPrintOperation ", "GtkPrintContext ", diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build index 08694a7355..8dbd14cc2d 100644 --- a/demos/gtk-demo/meson.build +++ b/demos/gtk-demo/meson.build @@ -62,7 +62,6 @@ demos = files([ 'search_entry.c', 'search_entry2.c', 'shortcuts.c', - 'shortcut_triggers.c', 'sidebar.c', 'sizegroup.c', 'sliding_puzzle.c', diff --git a/demos/gtk-demo/shortcut_triggers.c b/demos/gtk-demo/shortcut_triggers.c deleted file mode 100644 index d5b59c3de8..0000000000 --- a/demos/gtk-demo/shortcut_triggers.c +++ /dev/null @@ -1,91 +0,0 @@ -/* Shortcuts - * - * GtkShortcut is the abstraction used by GTK to handle shortcuts from - * keyboard or other input devices. - * - * Shortcut triggers can be used to weave complex sequences of key - * presses into sophisticated mechanisms to activate shortcuts. - * - * This demo code shows creative ways to do that. - */ - -#include <gtk/gtk.h> - -static GtkWidget *window = NULL; - -static gboolean -shortcut_activated (GtkWidget *widget, - GVariant *unused, - gpointer row) -{ - g_print ("activated %s\n", gtk_label_get_label (row)); - return TRUE; -} - -static GtkShortcutTrigger * -create_ctrl_g (void) -{ - return gtk_keyval_trigger_new (GDK_KEY_g, GDK_CONTROL_MASK); -} - -static GtkShortcutTrigger * -create_x (void) -{ - return gtk_keyval_trigger_new (GDK_KEY_x, 0); -} - -struct { - const char *description; - GtkShortcutTrigger * (* create_trigger_func) (void); -} shortcuts[] = { - { "Press Ctrl-G", create_ctrl_g }, - { "Press X", create_x }, -}; - -GtkWidget * -do_shortcut_triggers (GtkWidget *do_widget) -{ - guint i; - - if (!window) - { - GtkWidget *list; - GtkEventController *controller; - - window = gtk_window_new (); - gtk_window_set_display (GTK_WINDOW (window), - gtk_widget_get_display (do_widget)); - gtk_window_set_title (GTK_WINDOW (window), "Shortcuts"); - - g_signal_connect (window, "destroy", - G_CALLBACK (gtk_widget_destroyed), &window); - - list = gtk_list_box_new (); - g_object_set (list, "margin", 6, NULL); - gtk_container_add (GTK_CONTAINER (window), list); - - for (i = 0; i < G_N_ELEMENTS (shortcuts); i++) - { - GtkShortcut *shortcut; - GtkWidget *row; - - row = gtk_label_new (shortcuts[i].description); - gtk_container_add (GTK_CONTAINER (list), row); - - controller = gtk_shortcut_controller_new (); - gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (controller), GTK_SHORTCUT_SCOPE_GLOBAL); - gtk_widget_add_controller (row, controller); - - shortcut = gtk_shortcut_new (shortcuts[i].create_trigger_func(), - gtk_callback_action_new (shortcut_activated, row, NULL)); - gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); - } - } - - if (!gtk_widget_get_visible (window)) - gtk_widget_show (window); - else - gtk_widget_destroy (window); - - return window; -} diff --git a/demos/gtk-demo/sliding_puzzle.c b/demos/gtk-demo/sliding_puzzle.c index 5dcc0351bc..0abb5c9f6e 100644 --- a/demos/gtk-demo/sliding_puzzle.c +++ b/demos/gtk-demo/sliding_puzzle.c @@ -160,13 +160,47 @@ check_solved (GtkWidget *grid) } static gboolean -puzzle_key_pressed (GtkWidget *grid, - GVariant *args, - gpointer unused) +puzzle_key_pressed (GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + GtkWidget *grid) { int dx, dy; - g_variant_get (args, "(ii)", &dx, &dy); + dx = 0; + dy = 0; + + switch (keyval) + { + case GDK_KEY_KP_Left: + case GDK_KEY_Left: + /* left */ + dx = -1; + break; + + case GDK_KEY_KP_Up: + case GDK_KEY_Up: + /* up */ + dy = -1; + break; + + case GDK_KEY_KP_Right: + case GDK_KEY_Right: + /* right */ + dx = 1; + break; + + case GDK_KEY_KP_Down: + case GDK_KEY_Down: + /* down */ + dy = 1; + break; + + default: + /* We return FALSE here because we didn't handle the key that was pressed */ + return FALSE; + } if (!move_puzzle (grid, dx, dy)) { @@ -243,24 +277,6 @@ puzzle_button_pressed (GtkGestureClick *gesture, } static void -add_move_binding (GtkShortcutController *controller, - guint keyval, - guint kp_keyval, - int dx, - int dy) -{ - GtkShortcut *shortcut; - - shortcut = gtk_shortcut_new_with_arguments ( - gtk_alternative_trigger_new (gtk_keyval_trigger_new (keyval, 0), - gtk_keyval_trigger_new (kp_keyval, 0)), - gtk_callback_action_new (puzzle_key_pressed, NULL, NULL), - "(ii)", dx, dy); - gtk_shortcut_controller_add_shortcut (controller, shortcut); - g_object_unref (shortcut); -} - -static void start_puzzle (GdkPaintable *paintable) { GtkWidget *picture, *grid; @@ -282,21 +298,12 @@ start_puzzle (GdkPaintable *paintable) aspect_ratio = 1.0; gtk_aspect_frame_set (GTK_ASPECT_FRAME (frame), 0.5, 0.5, aspect_ratio, FALSE); - /* Add shortcuts so people can use the arrow + /* Add a key event controller so people can use the arrow * keys to move the puzzle */ - controller = gtk_shortcut_controller_new (); - add_move_binding (GTK_SHORTCUT_CONTROLLER (controller), - GDK_KEY_Left, GDK_KEY_KP_Left, - -1, 0); - add_move_binding (GTK_SHORTCUT_CONTROLLER (controller), - GDK_KEY_Right, GDK_KEY_KP_Right, - 1, 0); - add_move_binding (GTK_SHORTCUT_CONTROLLER (controller), - GDK_KEY_Up, GDK_KEY_KP_Up, - 0, -1); - add_move_binding (GTK_SHORTCUT_CONTROLLER (controller), - GDK_KEY_Down, GDK_KEY_KP_Down, - 0, 1); + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, "key-pressed", + G_CALLBACK (puzzle_key_pressed), + grid); gtk_widget_add_controller (GTK_WIDGET (grid), controller); controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 1bbcabc48b..aabc016df4 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -336,15 +336,6 @@ <xi:include href="xml/gtkgesturezoom.xml" /> <xi:include href="xml/gtkgesturestylus.xml" /> <xi:include href="xml/gtkpadcontroller.xml" /> - <xi:include href="xml/gtkshortcutcontroller.xml" /> - </chapter> - - <chapter> - <title>Keyboard shortcuts</title> - <xi:include href="xml/gtkshortcut.xml" /> - <xi:include href="xml/gtkshortcuttrigger.xml" /> - <xi:include href="xml/gtkshortcutaction.xml" /> - <xi:include href="xml/gtkshortcutmanager.xml" /> </chapter> <chapter> diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 647fcafb5c..13b3f831f6 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -53,6 +53,26 @@ gtk_about_dialog_get_type <SECTION> <FILE>gtkaccelgroup</FILE> <TITLE>Keyboard Accelerators</TITLE> +GtkAccelGroup +GtkAccelGroupClass +gtk_accel_group_new +GtkAccelFlags +gtk_accel_group_connect +gtk_accel_group_connect_by_path +GtkAccelGroupActivate +GtkAccelGroupFindFunc +gtk_accel_group_disconnect +gtk_accel_group_disconnect_key +gtk_accel_group_activate +gtk_accel_group_lock +gtk_accel_group_unlock +gtk_accel_group_get_is_locked +gtk_accel_group_from_accel_closure +gtk_accel_group_get_modifier_mask +gtk_accel_groups_activate +gtk_accel_groups_from_object +gtk_accel_group_find +GtkAccelKey gtk_accelerator_valid gtk_accelerator_parse gtk_accelerator_name @@ -62,6 +82,52 @@ gtk_accelerator_name_with_keycode gtk_accelerator_get_label_with_keycode gtk_accelerator_set_default_mod_mask gtk_accelerator_get_default_mod_mask + +<SUBSECTION Standard> +GTK_TYPE_ACCEL_GROUP +GTK_ACCEL_GROUP +GTK_IS_ACCEL_GROUP +GTK_ACCEL_GROUP_CLASS +GTK_IS_ACCEL_GROUP_CLASS +GTK_ACCEL_GROUP_GET_CLASS + +<SUBSECTION Private> +GTK_ACCEL_GROUP_GET_PRIVATE +GtkAccelGroupPrivate +GtkAccelGroupEntry +gtk_accel_group_query +gtk_accel_group_get_type +</SECTION> + +<SECTION> +<FILE>gtkaccelmap</FILE> +<TITLE>Accelerator Maps</TITLE> +GtkAccelMap +GtkAccelMapForeach +gtk_accel_map_add_entry +gtk_accel_map_lookup_entry +gtk_accel_map_change_entry +gtk_accel_map_load +gtk_accel_map_save +gtk_accel_map_foreach +gtk_accel_map_load_fd +gtk_accel_map_save_fd +gtk_accel_map_load_scanner +gtk_accel_map_add_filter +gtk_accel_map_foreach_unfiltered +gtk_accel_map_get +gtk_accel_map_lock_path +gtk_accel_map_unlock_path +<SUBSECTION Standard> +GTK_ACCEL_MAP +GTK_TYPE_ACCEL_MAP +GTK_IS_ACCEL_MAP +GTK_ACCEL_MAP_CLASS +GTK_IS_ACCEL_MAP_CLASS +GTK_ACCEL_MAP_GET_CLASS +GtkAccelMapClass +<SUBSECTION Private> +gtk_accel_map_get_type </SECTION> <SECTION> @@ -69,6 +135,10 @@ gtk_accelerator_get_default_mod_mask <TITLE>GtkAccelLabel</TITLE> GtkAccelLabel gtk_accel_label_new +gtk_accel_label_set_accel_closure +gtk_accel_label_get_accel_closure +gtk_accel_label_get_accel_widget +gtk_accel_label_set_accel_widget gtk_accel_label_get_accel_width gtk_accel_label_set_accel gtk_accel_label_get_accel @@ -3964,9 +4034,10 @@ gtk_widget_add_tick_callback gtk_widget_remove_tick_callback gtk_widget_size_allocate gtk_widget_allocate -gtk_widget_class_add_shortcut -gtk_widget_class_add_binding -gtk_widget_class_add_binding_signal +gtk_widget_add_accelerator +gtk_widget_remove_accelerator +gtk_widget_set_accel_path +gtk_widget_list_accel_closures gtk_widget_can_activate_accel gtk_widget_activate gtk_widget_is_focus @@ -4182,6 +4253,8 @@ gtk_window_new gtk_window_set_title gtk_window_set_resizable gtk_window_get_resizable +gtk_window_add_accel_group +gtk_window_remove_accel_group gtk_window_set_modal gtk_window_set_default_size gtk_window_set_hide_on_close @@ -4193,6 +4266,11 @@ gtk_window_is_active gtk_window_is_maximized gtk_window_get_toplevels gtk_window_list_toplevels +gtk_window_add_mnemonic +gtk_window_remove_mnemonic +gtk_window_mnemonic_activate +gtk_window_activate_key +gtk_window_propagate_key_event gtk_window_get_focus gtk_window_set_focus gtk_window_get_default_widget @@ -4213,6 +4291,7 @@ gtk_window_set_keep_above gtk_window_set_keep_below gtk_window_set_decorated gtk_window_set_deletable +gtk_window_set_mnemonic_modifier gtk_window_set_accept_focus gtk_window_set_focus_on_map gtk_window_set_startup_id @@ -4222,6 +4301,7 @@ gtk_window_get_default_icon_name gtk_window_get_default_size gtk_window_get_destroy_with_parent gtk_window_get_icon_name +gtk_window_get_mnemonic_modifier gtk_window_get_modal gtk_window_get_size gtk_window_get_title @@ -4538,6 +4618,26 @@ gtk_css_section_get_type </SECTION> <SECTION> +<FILE>gtkbindings</FILE> +<TITLE>Bindings</TITLE> +GtkBindingSet +gtk_binding_set_new +gtk_binding_set_by_class +gtk_binding_set_find +gtk_bindings_activate +gtk_bindings_activate_event +gtk_binding_set_activate +gtk_binding_entry_add_action +gtk_binding_entry_add_action_variant +GtkBindingCallback +gtk_binding_entry_add_callback +gtk_binding_entry_add_signal +gtk_binding_entry_add_signal_from_string +gtk_binding_entry_skip +gtk_binding_entry_remove +</SECTION> + +<SECTION> <FILE>gtkenums</FILE> <TITLE>Standard Enumerations</TITLE> GtkBaselinePosition @@ -5962,144 +6062,6 @@ gtk_event_controller_motion_get_type </SECTION> <SECTION> -<FILE>gtkshortcuttrigger</FILE> -<TITLE>GtkShortcutTrigger</TITLE> -GtkShortcutTrigger -gtk_shortcut_trigger_ref -gtk_shortcut_trigger_unref -GtkShortcutTriggerType -gtk_shortcut_trigger_get_trigger_type -gtk_shortcut_trigger_parse_string -gtk_shortcut_trigger_trigger -gtk_shortcut_trigger_hash -gtk_shortcut_trigger_equal -gtk_shortcut_trigger_compare -gtk_shortcut_trigger_to_string -gtk_shortcut_trigger_print -gtk_shortcut_trigger_to_label -gtk_shortcut_trigger_print_label - -<SUBSECTION> -gtk_keyval_trigger_new -gtk_keyval_trigger_get_modifiers -gtk_keyval_trigger_get_keyval - -<SUBSECTION> -gtk_mnemonic_trigger_new -gtk_mnemonic_trigger_get_keyval - -<SUBSECTION Private> -gtk_shortcut_trigger_get_type -</SECTION> - -<SECTION> -<FILE>gtkshortcutaction</FILE> -<TITLE>GtkShortcutAction</TITLE> -GtkShortcutAction -gtk_shortcut_action_ref -gtk_shortcut_action_unref -GtkShortcutActionType -gtk_shortcut_action_get_action_type -gtk_shortcut_action_to_string -gtk_shortcut_action_print -gtk_shortcut_action_activate - -<SUBSECTION> -gtk_nothing_action_new - -<SUBSECTION> -gtk_callback_action_new - -<SUBSECTION> -gtk_mnemonic_action_new - -<SUBSECTION> -gtk_activate_action_new - -<SUBSECTION> -gtk_signal_action_new -gtk_signal_action_get_signal_name - -<SUBSECTION> -gtk_action_action_new -gtk_action_action_get_name - -<SUBSECTION> -gtk_gaction_action_new -gtk_gaction_action_get_gaction - -<SUBSECTION Private> -gtk_shortcut_action_get_type -</SECTION> - -<SECTION> -<FILE>gtkshortcut</FILE> -<TITLE>GtkShortcut</TITLE> -GtkShortcut -gtk_shortcut_new -gtk_shortcut_new_with_arguments -gtk_shortcut_get_trigger -gtk_shortcut_set_trigger -gtk_shortcut_get_action -gtk_shortcut_set_action -gtk_shortcut_get_arguments -gtk_shortcut_set_arguments - -<SUBSECTION Standard> -GTK_TYPE_SHORTCUT -GTK_SHORTCUT -GTK_SHORTCUT_CLASS -GTK_IS_SHORTCUT -GTK_IS_SHORTCUT_CLASS -GTK_SHORTCUT_GET_CLASS - -<SUBSECTION Private> -gtk_shortcut_get_type -</SECTION> - -<SECTION> -<FILE>gtkshortcutmanager</FILE> -<TITLE>GtkShortcutManager</TITLE> -GtkShortcutManager -GtkShortcutManagerInterface -</SECTION> - -<SECTION> -<FILE>gtkshortcutcontroller</FILE> -<TITLE>GtkShortcutController</TITLE> -GtkShortcutController -gtk_shortcut_controller_new -gtk_shortcut_controller_new_with_model -GtkShortcutScope -GtkShortcutManager -GtkShortcutManagerInterface -gtk_shortcut_controller_set_mnemonics_modifiers -gtk_shortcut_controller_get_mnemonics_modifiers -gtk_shortcut_controller_set_scope -gtk_shortcut_controller_get_scope -gtk_shortcut_controller_add_shortcut -gtk_shortcut_controller_remove_shortcut - -<SUBSECTION Standard> -GTK_TYPE_SHORTCUT_CONTROLLER -GTK_SHORTCUT_CONTROLLER -GTK_SHORTCUT_CONTROLLER_CLASS -GTK_IS_SHORTCUT_CONTROLLER -GTK_IS_SHORTCUT_CONTROLLER_CLASS -GTK_SHORTCUT_CONTROLLER_GET_CLASS -GTK_TYPE_SHORTCUT_MANAGER -GTK_SHORTCUT_MANAGER -GTK_SHORTCUT_MANAGER_CLASS -GTK_IS_SHORTCUT_MANAGER -GTK_IS_SHORTCUT_MANAGER_CLASS -GTK_SHORTCUT_MANAGER_GET_CLASS - -<SUBSECTION Private> -gtk_shortcut_controller_get_type -gtk_shortcut_manager_get_type -</SECTION> - -<SECTION> <FILE>gtkeventcontrollerkey</FILE> <TITLE>GtkEventControllerKey</TITLE> GtkEventControllerKey diff --git a/docs/reference/gtk/gtk4.types.in b/docs/reference/gtk/gtk4.types.in index d7d9b0106b..1c7b9eabb2 100644 --- a/docs/reference/gtk/gtk4.types.in +++ b/docs/reference/gtk/gtk4.types.in @@ -2,7 +2,9 @@ #include <gtk/gtkunixprint.h> gtk_about_dialog_get_type +gtk_accel_group_get_type gtk_accel_label_get_type +gtk_accel_map_get_type gtk_accessible_get_type gtk_actionable_get_type gtk_action_bar_get_type @@ -163,10 +165,7 @@ gtk_search_entry_get_type gtk_selection_model_get_type gtk_separator_get_type gtk_settings_get_type -gtk_shortcut_get_type -gtk_shortcut_controller_get_type gtk_shortcut_label_get_type -gtk_shortcut_manager_get_type gtk_shortcuts_window_get_type gtk_shortcuts_section_get_type gtk_shortcuts_group_get_type diff --git a/docs/tools/widgets.c b/docs/tools/widgets.c index 7b63677368..fba3e02a0a 100644 --- a/docs/tools/widgets.c +++ b/docs/tools/widgets.c @@ -281,20 +281,26 @@ create_accel_label (void) { WidgetInfo *info; GtkWidget *widget, *button, *box; + GtkAccelGroup *accel_group; widget = gtk_accel_label_new ("Accel Label"); button = gtk_button_new_with_label ("Quit"); + gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (widget), button); gtk_widget_hide (button); box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (box), widget); gtk_container_add (GTK_CONTAINER (box), button); - gtk_accel_label_set_accel (GTK_ACCEL_LABEL (widget), GDK_KEY_Q, GDK_CONTROL_MASK); + gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (widget), button); + accel_group = gtk_accel_group_new(); info = new_widget_info ("accel-label", box, SMALL); + gtk_widget_add_accelerator (button, "activate", accel_group, GDK_KEY_Q, GDK_CONTROL_MASK, + GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); + return info; } diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js index 6cb576d40c..267ef205ee 100644 --- a/gdk/broadway/broadway.js +++ b/gdk/broadway/broadway.js @@ -75,7 +75,7 @@ const GDK_CROSSING_GRAB = 1; const GDK_CROSSING_UNGRAB = 2; // GdkModifierType -const GDK_SHIFT_MASK = 1 << 0; +const GDK_SHIFT_MASK = 1 << 0; const GDK_LOCK_MASK = 1 << 1; const GDK_CONTROL_MASK = 1 << 2; const GDK_MOD1_MASK = 1 << 3; @@ -91,6 +91,7 @@ const GDK_BUTTON5_MASK = 1 << 12; const GDK_SUPER_MASK = 1 << 26; const GDK_HYPER_MASK = 1 << 27; const GDK_META_MASK = 1 << 28; +const GDK_RELEASE_MASK = 1 << 30; var useDataUrls = window.location.search.includes("datauri"); diff --git a/gdk/gdktypes.h b/gdk/gdktypes.h index 7eb74b080f..5b0f6be7cc 100644 --- a/gdk/gdktypes.h +++ b/gdk/gdktypes.h @@ -232,7 +232,8 @@ typedef enum * @GDK_HYPER_MASK: the Hyper modifier * @GDK_META_MASK: the Meta modifier * @GDK_MODIFIER_RESERVED_29_MASK: A reserved bit flag; do not use in your own code - * @GDK_MODIFIER_RESERVED_30_MASK: A reserved bit flag; do not use in your own code + * @GDK_RELEASE_MASK: not used in GDK itself. GTK uses it to differentiate + * between (keyval, modifiers) pairs from key press and release events. * @GDK_MODIFIER_MASK: a mask covering all modifier types. * * A set of bit-flags to indicate the state of modifier keys and mouse buttons @@ -293,11 +294,12 @@ typedef enum GDK_META_MASK = 1 << 28, GDK_MODIFIER_RESERVED_29_MASK = 1 << 29, - GDK_MODIFIER_RESERVED_30_MASK = 1 << 30, + + GDK_RELEASE_MASK = 1 << 30, /* Combination of GDK_SHIFT_MASK..GDK_BUTTON5_MASK + GDK_SUPER_MASK - + GDK_HYPER_MASK + GDK_META_MASK */ - GDK_MODIFIER_MASK = 0x1c001fff + + GDK_HYPER_MASK + GDK_META_MASK + GDK_RELEASE_MASK */ + GDK_MODIFIER_MASK = 0x5c001fff } GdkModifierType; /** @@ -34,6 +34,7 @@ #include <gtk/gtkaboutdialog.h> #include <gtk/gtkaccelgroup.h> #include <gtk/gtkaccellabel.h> +#include <gtk/gtkaccelmap.h> #include <gtk/gtkaccessible.h> #include <gtk/gtkactionable.h> #include <gtk/gtkactionbar.h> @@ -48,6 +49,7 @@ #include <gtk/gtkassistant.h> #include <gtk/gtkbin.h> #include <gtk/gtkbinlayout.h> +#include <gtk/gtkbindings.h> #include <gtk/gtkborder.h> #include <gtk/gtkboxlayout.h> #include <gtk/gtkbox.h> @@ -196,16 +198,11 @@ #include <gtk/gtkselectionmodel.h> #include <gtk/gtkseparator.h> #include <gtk/gtksettings.h> -#include <gtk/gtkshortcut.h> -#include <gtk/gtkshortcutaction.h> -#include <gtk/gtkshortcutcontroller.h> #include <gtk/gtkshortcutlabel.h> -#include <gtk/gtkshortcutmanager.h> #include <gtk/gtkshortcutsgroup.h> #include <gtk/gtkshortcutssection.h> #include <gtk/gtkshortcutsshortcut.h> #include <gtk/gtkshortcutswindow.h> -#include <gtk/gtkshortcuttrigger.h> #include <gtk/gtkshow.h> #include <gtk/gtksingleselection.h> #include <gtk/gtkslicelistmodel.h> diff --git a/gtk/gtkaccelgroup.c b/gtk/gtkaccelgroup.c index 553ec84ce3..dbf4e261af 100644 --- a/gtk/gtkaccelgroup.c +++ b/gtk/gtkaccelgroup.c @@ -29,6 +29,7 @@ #include "gtkaccelgroup.h" #include "gtkaccelgroupprivate.h" #include "gtkaccellabelprivate.h" +#include "gtkaccelmapprivate.h" #include "gtkintl.h" #include "gtkmarshalers.h" #include "gtkprivate.h" @@ -55,11 +56,893 @@ * and mnemonics, of course. */ +/* --- prototypes --- */ +static void gtk_accel_group_finalize (GObject *object); +static void gtk_accel_group_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void accel_closure_invalidate (gpointer data, + GClosure *closure); + + /* --- variables --- */ +static guint signal_accel_activate = 0; +static guint signal_accel_changed = 0; +static guint quark_acceleratable_groups = 0; static guint default_accel_mod_mask = 0; +enum { + PROP_0, + PROP_IS_LOCKED, + PROP_MODIFIER_MASK, + N_PROPERTIES +}; + +static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; + +G_DEFINE_TYPE_WITH_PRIVATE (GtkAccelGroup, gtk_accel_group, G_TYPE_OBJECT) + /* --- functions --- */ +static void +gtk_accel_group_class_init (GtkAccelGroupClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + quark_acceleratable_groups = g_quark_from_static_string ("gtk-acceleratable-accel-groups"); + + object_class->finalize = gtk_accel_group_finalize; + object_class->get_property = gtk_accel_group_get_property; + + class->accel_changed = NULL; + + obj_properties [PROP_IS_LOCKED] = + g_param_spec_boolean ("is-locked", + "Is locked", + "Is the accel group locked", + FALSE, + G_PARAM_READABLE); + + obj_properties [PROP_MODIFIER_MASK] = + g_param_spec_flags ("modifier-mask", + "Modifier Mask", + "Modifier Mask", + GDK_TYPE_MODIFIER_TYPE, + default_accel_mod_mask, + G_PARAM_READABLE); + + g_object_class_install_properties (object_class, + N_PROPERTIES, + obj_properties); + + /** + * GtkAccelGroup::accel-activate: + * @accel_group: the #GtkAccelGroup which received the signal + * @acceleratable: the object on which the accelerator was activated + * @keyval: the accelerator keyval + * @modifier: the modifier combination of the accelerator + * + * The accel-activate signal is an implementation detail of + * #GtkAccelGroup and not meant to be used by applications. + * + * Returns: %TRUE if the accelerator was activated + */ + signal_accel_activate = + g_signal_new (I_("accel-activate"), + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_DETAILED, + 0, + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__OBJECT_UINT_FLAGS, + G_TYPE_BOOLEAN, 3, + G_TYPE_OBJECT, + G_TYPE_UINT, + GDK_TYPE_MODIFIER_TYPE); + /** + * GtkAccelGroup::accel-changed: + * @accel_group: the #GtkAccelGroup which received the signal + * @keyval: the accelerator keyval + * @modifier: the modifier combination of the accelerator + * @accel_closure: the #GClosure of the accelerator + * + * The accel-changed signal is emitted when an entry + * is added to or removed from the accel group. + * + * Widgets like #GtkAccelLabel which display an associated + * accelerator should connect to this signal, and rebuild + * their visual representation if the @accel_closure is theirs. + */ + signal_accel_changed = + g_signal_new (I_("accel-changed"), + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (GtkAccelGroupClass, accel_changed), + NULL, NULL, + _gtk_marshal_VOID__UINT_FLAGS_BOXED, + G_TYPE_NONE, 3, + G_TYPE_UINT, + GDK_TYPE_MODIFIER_TYPE, + G_TYPE_CLOSURE); +} + +static void +gtk_accel_group_finalize (GObject *object) +{ + GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object); + guint i; + + for (i = 0; i < accel_group->priv->n_accels; i++) + { + GtkAccelGroupEntry *entry = &accel_group->priv->priv_accels[i]; + + if (entry->accel_path_quark) + { + const gchar *accel_path = g_quark_to_string (entry->accel_path_quark); + + _gtk_accel_map_remove_group (accel_path, accel_group); + } + g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate); + + /* remove quick_accel_add() refcount */ + g_closure_unref (entry->closure); + } + + g_free (accel_group->priv->priv_accels); + + G_OBJECT_CLASS (gtk_accel_group_parent_class)->finalize (object); +} + +static void +gtk_accel_group_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object); + + switch (param_id) + { + case PROP_IS_LOCKED: + g_value_set_boolean (value, accel_group->priv->lock_count > 0); + break; + case PROP_MODIFIER_MASK: + g_value_set_flags (value, accel_group->priv->modifier_mask); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +gtk_accel_group_init (GtkAccelGroup *accel_group) +{ + GtkAccelGroupPrivate *priv; + + accel_group->priv = gtk_accel_group_get_instance_private (accel_group); + priv = accel_group->priv; + + priv->lock_count = 0; + priv->modifier_mask = gtk_accelerator_get_default_mod_mask (); + priv->acceleratables = NULL; + priv->n_accels = 0; + priv->priv_accels = NULL; +} + +/** + * gtk_accel_group_new: + * + * Creates a new #GtkAccelGroup. + * + * Returns: a new #GtkAccelGroup object + */ +GtkAccelGroup* +gtk_accel_group_new (void) +{ + return g_object_new (GTK_TYPE_ACCEL_GROUP, NULL); +} + +/** + * gtk_accel_group_get_is_locked: + * @accel_group: a #GtkAccelGroup + * + * Locks are added and removed using gtk_accel_group_lock() and + * gtk_accel_group_unlock(). + * + * Returns: %TRUE if there are 1 or more locks on the @accel_group, + * %FALSE otherwise. + */ +gboolean +gtk_accel_group_get_is_locked (GtkAccelGroup *accel_group) +{ + g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE); + + return accel_group->priv->lock_count > 0; +} + +/** + * gtk_accel_group_get_modifier_mask: + * @accel_group: a #GtkAccelGroup + * + * Gets a #GdkModifierType representing the mask for this + * @accel_group. For example, #GDK_CONTROL_MASK, #GDK_SHIFT_MASK, etc. + * + * Returns: the modifier mask for this accel group. + */ +GdkModifierType +gtk_accel_group_get_modifier_mask (GtkAccelGroup *accel_group) +{ + g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), 0); + + return accel_group->priv->modifier_mask; +} + +static void +accel_group_weak_ref_detach (GSList *free_list, + GObject *stale_object) +{ + GSList *slist; + + for (slist = free_list; slist; slist = slist->next) + { + GtkAccelGroup *accel_group; + + accel_group = slist->data; + accel_group->priv->acceleratables = g_slist_remove (accel_group->priv->acceleratables, stale_object); + g_object_unref (accel_group); + } + g_slist_free (free_list); + g_object_set_qdata (stale_object, quark_acceleratable_groups, NULL); +} + +void +_gtk_accel_group_attach (GtkAccelGroup *accel_group, + GObject *object) +{ + GSList *slist; + + g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); + g_return_if_fail (G_IS_OBJECT (object)); + g_return_if_fail (g_slist_find (accel_group->priv->acceleratables, object) == NULL); + + g_object_ref (accel_group); + accel_group->priv->acceleratables = g_slist_prepend (accel_group->priv->acceleratables, object); + slist = g_object_get_qdata (object, quark_acceleratable_groups); + if (slist) + g_object_weak_unref (object, + (GWeakNotify) accel_group_weak_ref_detach, + slist); + slist = g_slist_prepend (slist, accel_group); + g_object_set_qdata (object, quark_acceleratable_groups, slist); + g_object_weak_ref (object, + (GWeakNotify) accel_group_weak_ref_detach, + slist); +} + +void +_gtk_accel_group_detach (GtkAccelGroup *accel_group, + GObject *object) +{ + GSList *slist; + + g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); + g_return_if_fail (G_IS_OBJECT (object)); + g_return_if_fail (g_slist_find (accel_group->priv->acceleratables, object) != NULL); + + accel_group->priv->acceleratables = g_slist_remove (accel_group->priv->acceleratables, object); + slist = g_object_get_qdata (object, quark_acceleratable_groups); + g_object_weak_unref (object, + (GWeakNotify) accel_group_weak_ref_detach, + slist); + slist = g_slist_remove (slist, accel_group); + g_object_set_qdata (object, quark_acceleratable_groups, slist); + if (slist) + g_object_weak_ref (object, + (GWeakNotify) accel_group_weak_ref_detach, + slist); + g_object_unref (accel_group); +} + +/** + * gtk_accel_groups_from_object: + * @object: a #GObject, usually a #GtkWindow + * + * Gets a list of all accel groups which are attached to @object. + * + * Returns: (element-type GtkAccelGroup) (transfer none): a list of + * all accel groups which are attached to @object + */ +GSList* +gtk_accel_groups_from_object (GObject *object) +{ + g_return_val_if_fail (G_IS_OBJECT (object), NULL); + + return g_object_get_qdata (object, quark_acceleratable_groups); +} + +/** + * gtk_accel_group_find: + * @accel_group: a #GtkAccelGroup + * @find_func: (scope call): a function to filter the entries + * of @accel_group with + * @data: data to pass to @find_func + * + * Finds the first entry in an accelerator group for which + * @find_func returns %TRUE and returns its #GtkAccelKey. + * + * Returns: (transfer none): the key of the first entry passing + * @find_func. The key is owned by GTK+ and must not be freed. + */ +GtkAccelKey* +gtk_accel_group_find (GtkAccelGroup *accel_group, + GtkAccelGroupFindFunc find_func, + gpointer data) +{ + GtkAccelKey *key = NULL; + guint i; + + g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL); + g_return_val_if_fail (find_func != NULL, NULL); + + g_object_ref (accel_group); + for (i = 0; i < accel_group->priv->n_accels; i++) + if (find_func (&accel_group->priv->priv_accels[i].key, + accel_group->priv->priv_accels[i].closure, + data)) + { + key = &accel_group->priv->priv_accels[i].key; + break; + } + g_object_unref (accel_group); + + return key; +} + +/** + * gtk_accel_group_lock: + * @accel_group: a #GtkAccelGroup + * + * Locks the given accelerator group. + * + * Locking an acelerator group prevents the accelerators contained + * within it to be changed during runtime. Refer to + * gtk_accel_map_change_entry() about runtime accelerator changes. + * + * If called more than once, @accel_group remains locked until + * gtk_accel_group_unlock() has been called an equivalent number + * of times. + */ +void +gtk_accel_group_lock (GtkAccelGroup *accel_group) +{ + g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); + + accel_group->priv->lock_count += 1; + + if (accel_group->priv->lock_count == 1) { + /* State change from unlocked to locked */ + g_object_notify_by_pspec (G_OBJECT (accel_group), obj_properties[PROP_IS_LOCKED]); + } +} + +/** + * gtk_accel_group_unlock: + * @accel_group: a #GtkAccelGroup + * + * Undoes the last call to gtk_accel_group_lock() on this @accel_group. + */ +void +gtk_accel_group_unlock (GtkAccelGroup *accel_group) +{ + g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); + g_return_if_fail (accel_group->priv->lock_count > 0); + + accel_group->priv->lock_count -= 1; + + if (accel_group->priv->lock_count < 1) { + /* State change from locked to unlocked */ + g_object_notify_by_pspec (G_OBJECT (accel_group), obj_properties[PROP_IS_LOCKED]); + } +} + +static void +accel_closure_invalidate (gpointer data, + GClosure *closure) +{ + GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (data); + + gtk_accel_group_disconnect (accel_group, closure); +} + +static int +bsearch_compare_accels (const void *d1, + const void *d2) +{ + const GtkAccelGroupEntry *entry1 = d1; + const GtkAccelGroupEntry *entry2 = d2; + + if (entry1->key.accel_key == entry2->key.accel_key) + return entry1->key.accel_mods < entry2->key.accel_mods ? -1 : entry1->key.accel_mods > entry2->key.accel_mods; + else + return entry1->key.accel_key < entry2->key.accel_key ? -1 : 1; +} + +static void +quick_accel_add (GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods, + GtkAccelFlags accel_flags, + GClosure *closure, + GQuark path_quark) +{ + guint pos, i = accel_group->priv->n_accels++; + GtkAccelGroupEntry key; + + /* find position */ + key.key.accel_key = accel_key; + key.key.accel_mods = accel_mods; + for (pos = 0; pos < i; pos++) + if (bsearch_compare_accels (&key, accel_group->priv->priv_accels + pos) < 0) + break; + + /* insert at position, ref closure */ + accel_group->priv->priv_accels = g_renew (GtkAccelGroupEntry, accel_group->priv->priv_accels, accel_group->priv->n_accels); + memmove (accel_group->priv->priv_accels + pos + 1, accel_group->priv->priv_accels + pos, + (i - pos) * sizeof (accel_group->priv->priv_accels[0])); + accel_group->priv->priv_accels[pos].key.accel_key = accel_key; + accel_group->priv->priv_accels[pos].key.accel_mods = accel_mods; + accel_group->priv->priv_accels[pos].key.accel_flags = accel_flags; + accel_group->priv->priv_accels[pos].closure = g_closure_ref (closure); + accel_group->priv->priv_accels[pos].accel_path_quark = path_quark; + g_closure_sink (closure); + + /* handle closure invalidation and reverse lookups */ + g_closure_add_invalidate_notifier (closure, accel_group, accel_closure_invalidate); + + /* get accel path notification */ + if (path_quark) + _gtk_accel_map_add_group (g_quark_to_string (path_quark), accel_group); + + /* connect and notify changed */ + if (accel_key) + { + gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods); + GQuark accel_quark = g_quark_from_string (accel_name); + + g_free (accel_name); + + /* setup handler */ + g_signal_connect_closure_by_id (accel_group, signal_accel_activate, accel_quark, closure, FALSE); + + /* and notify */ + g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure); + } +} + +static void +quick_accel_remove (GtkAccelGroup *accel_group, + guint pos) +{ + GQuark accel_quark = 0; + GtkAccelGroupEntry *entry = accel_group->priv->priv_accels + pos; + guint accel_key = entry->key.accel_key; + GdkModifierType accel_mods = entry->key.accel_mods; + GClosure *closure = entry->closure; + + /* quark for notification */ + if (accel_key) + { + gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods); + + accel_quark = g_quark_from_string (accel_name); + g_free (accel_name); + } + + /* clean up closure invalidate notification and disconnect */ + g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate); + if (accel_quark) + g_signal_handlers_disconnect_matched (accel_group, + G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_CLOSURE, + signal_accel_activate, accel_quark, + closure, NULL, NULL); + /* clean up accel path notification */ + if (entry->accel_path_quark) + _gtk_accel_map_remove_group (g_quark_to_string (entry->accel_path_quark), accel_group); + + /* physically remove */ + accel_group->priv->n_accels -= 1; + memmove (entry, entry + 1, + (accel_group->priv->n_accels - pos) * sizeof (accel_group->priv->priv_accels[0])); + + /* and notify */ + if (accel_quark) + g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure); + + /* remove quick_accel_add() refcount */ + g_closure_unref (closure); +} + +static GtkAccelGroupEntry* +quick_accel_find (GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods, + guint *count_p) +{ + GtkAccelGroupEntry *entry; + GtkAccelGroupEntry key; + + *count_p = 0; + + if (!accel_group->priv->n_accels) + return NULL; + + key.key.accel_key = accel_key; + key.key.accel_mods = accel_mods; + entry = bsearch (&key, accel_group->priv->priv_accels, accel_group->priv->n_accels, + sizeof (accel_group->priv->priv_accels[0]), bsearch_compare_accels); + + if (!entry) + return NULL; + + /* step back to the first member */ + for (; entry > accel_group->priv->priv_accels; entry--) + if (entry[-1].key.accel_key != accel_key || + entry[-1].key.accel_mods != accel_mods) + break; + /* count equal members */ + for (; entry + *count_p < accel_group->priv->priv_accels + accel_group->priv->n_accels; (*count_p)++) + if (entry[*count_p].key.accel_key != accel_key || + entry[*count_p].key.accel_mods != accel_mods) + break; + return entry; +} + +/** + * gtk_accel_group_connect: + * @accel_group: the accelerator group to install an accelerator in + * @accel_key: key value of the accelerator + * @accel_mods: modifier combination of the accelerator + * @accel_flags: a flag mask to configure this accelerator + * @closure: closure to be executed upon accelerator activation + * + * Installs an accelerator in this group. When @accel_group is being + * activated in response to a call to gtk_accel_groups_activate(), + * @closure will be invoked if the @accel_key and @accel_mods from + * gtk_accel_groups_activate() match those of this connection. + * + * The signature used for the @closure is that of #GtkAccelGroupActivate. + * + * Note that, due to implementation details, a single closure can + * only be connected to one accelerator group. + */ +void +gtk_accel_group_connect (GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods, + GtkAccelFlags accel_flags, + GClosure *closure) +{ + g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); + g_return_if_fail (closure != NULL); + g_return_if_fail (accel_key > 0); + g_return_if_fail (gtk_accel_group_from_accel_closure (closure) == NULL); + + g_object_ref (accel_group); + if (!closure->is_invalid) + quick_accel_add (accel_group, + gdk_keyval_to_lower (accel_key), + accel_mods, accel_flags, closure, 0); + g_object_unref (accel_group); +} + +/** + * gtk_accel_group_connect_by_path: + * @accel_group: the accelerator group to install an accelerator in + * @accel_path: path used for determining key and modifiers + * @closure: closure to be executed upon accelerator activation + * + * Installs an accelerator in this group, using an accelerator path + * to look up the appropriate key and modifiers (see + * gtk_accel_map_add_entry()). When @accel_group is being activated + * in response to a call to gtk_accel_groups_activate(), @closure will + * be invoked if the @accel_key and @accel_mods from + * gtk_accel_groups_activate() match the key and modifiers for the path. + * + * The signature used for the @closure is that of #GtkAccelGroupActivate. + * + * Note that @accel_path string will be stored in a #GQuark. Therefore, + * if you pass a static string, you can save some memory by interning it + * first with g_intern_static_string(). + */ +void +gtk_accel_group_connect_by_path (GtkAccelGroup *accel_group, + const gchar *accel_path, + GClosure *closure) +{ + guint accel_key = 0; + GdkModifierType accel_mods = 0; + GtkAccelKey key; + + g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); + g_return_if_fail (closure != NULL); + g_return_if_fail (_gtk_accel_path_is_valid (accel_path)); + + if (closure->is_invalid) + return; + + g_object_ref (accel_group); + + if (gtk_accel_map_lookup_entry (accel_path, &key)) + { + accel_key = gdk_keyval_to_lower (key.accel_key); + accel_mods = key.accel_mods; + } + + quick_accel_add (accel_group, accel_key, accel_mods, GTK_ACCEL_VISIBLE, closure, + g_quark_from_string (accel_path)); + + g_object_unref (accel_group); +} + +/** + * gtk_accel_group_disconnect: + * @accel_group: the accelerator group to remove an accelerator from + * @closure: (allow-none): the closure to remove from this accelerator + * group, or %NULL to remove all closures + * + * Removes an accelerator previously installed through + * gtk_accel_group_connect(). + * + * Returns: %TRUE if the closure was found and got disconnected + */ +gboolean +gtk_accel_group_disconnect (GtkAccelGroup *accel_group, + GClosure *closure) +{ + guint i; + + g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE); + + for (i = 0; i < accel_group->priv->n_accels; i++) + if (accel_group->priv->priv_accels[i].closure == closure) + { + g_object_ref (accel_group); + quick_accel_remove (accel_group, i); + g_object_unref (accel_group); + return TRUE; + } + return FALSE; +} + +/** + * gtk_accel_group_disconnect_key: + * @accel_group: the accelerator group to install an accelerator in + * @accel_key: key value of the accelerator + * @accel_mods: modifier combination of the accelerator + * + * Removes an accelerator previously installed through + * gtk_accel_group_connect(). + * + * Returns: %TRUE if there was an accelerator which could be + * removed, %FALSE otherwise + */ +gboolean +gtk_accel_group_disconnect_key (GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods) +{ + GtkAccelGroupEntry *entries; + GSList *slist, *clist = NULL; + gboolean removed_one = FALSE; + guint n; + + g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE); + + g_object_ref (accel_group); + + accel_key = gdk_keyval_to_lower (accel_key); + entries = quick_accel_find (accel_group, accel_key, accel_mods, &n); + while (n--) + { + GClosure *closure = g_closure_ref (entries[n].closure); + + clist = g_slist_prepend (clist, closure); + } + + for (slist = clist; slist; slist = slist->next) + { + GClosure *closure = slist->data; + + removed_one |= gtk_accel_group_disconnect (accel_group, closure); + g_closure_unref (closure); + } + g_slist_free (clist); + + g_object_unref (accel_group); + + return removed_one; +} + +void +_gtk_accel_group_reconnect (GtkAccelGroup *accel_group, + GQuark accel_path_quark) +{ + GSList *slist, *clist = NULL; + guint i; + + g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); + + g_object_ref (accel_group); + + for (i = 0; i < accel_group->priv->n_accels; i++) + if (accel_group->priv->priv_accels[i].accel_path_quark == accel_path_quark) + { + GClosure *closure = g_closure_ref (accel_group->priv->priv_accels[i].closure); + + clist = g_slist_prepend (clist, closure); + } + + for (slist = clist; slist; slist = slist->next) + { + GClosure *closure = slist->data; + + gtk_accel_group_disconnect (accel_group, closure); + gtk_accel_group_connect_by_path (accel_group, g_quark_to_string (accel_path_quark), closure); + g_closure_unref (closure); + } + g_slist_free (clist); + + g_object_unref (accel_group); +} + +GSList* +_gtk_accel_group_get_accelerables (GtkAccelGroup *accel_group) +{ + g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL); + + return accel_group->priv->acceleratables; +} + +/** + * gtk_accel_group_query: + * @accel_group: the accelerator group to query + * @accel_key: key value of the accelerator + * @accel_mods: modifier combination of the accelerator + * @n_entries: (out) (optional): location to return the number + * of entries found, or %NULL + * + * Queries an accelerator group for all entries matching @accel_key + * and @accel_mods. + * + * Returns: (nullable) (transfer none) (array length=n_entries): an array of + * @n_entries #GtkAccelGroupEntry elements, or %NULL. The array + * is owned by GTK+ and must not be freed. + */ +GtkAccelGroupEntry* +gtk_accel_group_query (GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods, + guint *n_entries) +{ + GtkAccelGroupEntry *entries; + guint n; + + g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL); + + entries = quick_accel_find (accel_group, gdk_keyval_to_lower (accel_key), accel_mods, &n); + + if (n_entries) + *n_entries = entries ? n : 0; + + return entries; +} + +/** + * gtk_accel_group_from_accel_closure: + * @closure: a #GClosure + * + * Finds the #GtkAccelGroup to which @closure is connected; + * see gtk_accel_group_connect(). + * + * Returns: (nullable) (transfer none): the #GtkAccelGroup to which @closure + * is connected, or %NULL + */ +GtkAccelGroup* +gtk_accel_group_from_accel_closure (GClosure *closure) +{ + guint i; + + g_return_val_if_fail (closure != NULL, NULL); + + /* A few remarks on what we do here. in general, we need a way to + * reverse lookup accel_groups from closures that are being used in + * accel groups. this could be done e.g via a hashtable. it is however + * cheaper (memory wise) to just use the invalidation notifier on the + * closure itself (which we need to install anyway), that contains the + * accel group as data which, besides needing to peek a bit at closure + * internals, works just as good. + */ + for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++) + if (closure->notifiers[i].notify == accel_closure_invalidate) + return closure->notifiers[i].data; + + return NULL; +} + +/** + * gtk_accel_group_activate: + * @accel_group: a #GtkAccelGroup + * @accel_quark: the quark for the accelerator name + * @acceleratable: the #GObject, usually a #GtkWindow, on which + * to activate the accelerator + * @accel_key: accelerator keyval from a key event + * @accel_mods: keyboard state mask from a key event + * + * Finds the first accelerator in @accel_group that matches + * @accel_key and @accel_mods, and activates it. + * + * Returns: %TRUE if an accelerator was activated and handled + * this keypress + */ +gboolean +gtk_accel_group_activate (GtkAccelGroup *accel_group, + GQuark accel_quark, + GObject *acceleratable, + guint accel_key, + GdkModifierType accel_mods) +{ + gboolean was_handled; + + g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE); + g_return_val_if_fail (G_IS_OBJECT (acceleratable), FALSE); + + was_handled = FALSE; + g_signal_emit (accel_group, signal_accel_activate, accel_quark, + acceleratable, accel_key, accel_mods, &was_handled); + + return was_handled; +} + +/** + * gtk_accel_groups_activate: + * @object: the #GObject, usually a #GtkWindow, on which + * to activate the accelerator + * @accel_key: accelerator keyval from a key event + * @accel_mods: keyboard state mask from a key event + * + * Finds the first accelerator in any #GtkAccelGroup attached + * to @object that matches @accel_key and @accel_mods, and + * activates that accelerator. + * + * Returns: %TRUE if an accelerator was activated and handled + * this keypress + */ +gboolean +gtk_accel_groups_activate (GObject *object, + guint accel_key, + GdkModifierType accel_mods) +{ + g_return_val_if_fail (G_IS_OBJECT (object), FALSE); + + if (gtk_accelerator_valid (accel_key, accel_mods)) + { + gchar *accel_name; + GQuark accel_quark; + GSList *slist; + + accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ())); + accel_quark = g_quark_from_string (accel_name); + g_free (accel_name); + + for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next) + if (gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods)) + return TRUE; + } + + return FALSE; +} + /** * gtk_accelerator_valid: * @keyval: a GDK keyval @@ -205,6 +1088,20 @@ is_control (const gchar *string) } static inline gboolean +is_release (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'r' || string[1] == 'R') && + (string[2] == 'e' || string[2] == 'E') && + (string[3] == 'l' || string[3] == 'L') && + (string[4] == 'e' || string[4] == 'E') && + (string[5] == 'a' || string[5] == 'A') && + (string[6] == 's' || string[6] == 'S') && + (string[7] == 'e' || string[7] == 'E') && + (string[8] == '>')); +} + +static inline gboolean is_meta (const gchar *string) { return ((string[0] == '<') && @@ -265,7 +1162,6 @@ is_keycode (const gchar *string) /** * gtk_accelerator_parse_with_keycode: * @accelerator: string representing an accelerator - * @display: (allow-none): the #GdkDisplay to look up @accelerator_codes in * @accelerator_key: (out) (allow-none): return location for accelerator * keyval, or %NULL * @accelerator_codes: (out) (array zero-terminated=1) (transfer full) (allow-none): @@ -286,12 +1182,9 @@ is_keycode (const gchar *string) * * If the parse fails, @accelerator_key, @accelerator_mods and * @accelerator_codes will be set to 0 (zero). - * - * Returns: %TRUE if parsing succeeded */ -gboolean +void gtk_accelerator_parse_with_keycode (const gchar *accelerator, - GdkDisplay *display, guint *accelerator_key, guint **accelerator_codes, GdkModifierType *accelerator_mods) @@ -307,8 +1200,7 @@ gtk_accelerator_parse_with_keycode (const gchar *accelerator, *accelerator_mods = 0; if (accelerator_codes) *accelerator_codes = NULL; - - g_return_val_if_fail (accelerator != NULL, FALSE); + g_return_if_fail (accelerator != NULL); error = FALSE; keyval = 0; @@ -318,7 +1210,13 @@ gtk_accelerator_parse_with_keycode (const gchar *accelerator, { if (*accelerator == '<') { - if (len >= 9 && is_primary (accelerator)) + if (len >= 9 && is_release (accelerator)) + { + accelerator += 9; + len -= 9; + mods |= GDK_RELEASE_MASK; + } + else if (len >= 9 && is_primary (accelerator)) { accelerator += 9; len -= 9; @@ -455,7 +1353,7 @@ gtk_accelerator_parse_with_keycode (const gchar *accelerator, if (keyval && accelerator_codes != NULL) { - GdkKeymap *keymap = gdk_display_get_keymap (display ? display : gdk_display_get_default ()); + GdkKeymap *keymap = gdk_display_get_keymap (gdk_display_get_default ()); GdkKeymapKey *keys; gint n_keys, i, j; @@ -518,8 +1416,6 @@ out: *accelerator_key = gdk_keyval_to_lower (keyval); if (accelerator_mods) *accelerator_mods = mods; - - return !error; } /** @@ -543,12 +1439,12 @@ out: * If the parse fails, @accelerator_key and @accelerator_mods will * be set to 0 (zero). */ -gboolean +void gtk_accelerator_parse (const gchar *accelerator, guint *accelerator_key, GdkModifierType *accelerator_mods) { - return gtk_accelerator_parse_with_keycode (accelerator, NULL, accelerator_key, NULL, accelerator_mods); + gtk_accelerator_parse_with_keycode (accelerator, accelerator_key, NULL, accelerator_mods); } /** @@ -609,6 +1505,7 @@ gchar* gtk_accelerator_name (guint accelerator_key, GdkModifierType accelerator_mods) { + static const gchar text_release[] = "<Release>"; static const gchar text_primary[] = "<Primary>"; static const gchar text_shift[] = "<Shift>"; static const gchar text_control[] = "<Control>"; @@ -633,6 +1530,8 @@ gtk_accelerator_name (guint accelerator_key, saved_mods = accelerator_mods; l = 0; + if (accelerator_mods & GDK_RELEASE_MASK) + l += sizeof (text_release) - 1; if (accelerator_mods & _gtk_get_primary_accel_mod ()) { l += sizeof (text_primary) - 1; @@ -665,6 +1564,11 @@ gtk_accelerator_name (guint accelerator_key, accelerator_mods = saved_mods; l = 0; accelerator[l] = 0; + if (accelerator_mods & GDK_RELEASE_MASK) + { + strcpy (accelerator + l, text_release); + l += sizeof (text_release) - 1; + } if (accelerator_mods & _gtk_get_primary_accel_mod ()) { strcpy (accelerator + l, text_primary); @@ -768,131 +1672,6 @@ gtk_accelerator_get_label_with_keycode (GdkDisplay *display, return gtk_label; } -/* Underscores in key names are better displayed as spaces - * E.g., Page_Up should be “Page Up”. - * - * Some keynames also have prefixes that are not suitable - * for display, e.g XF86AudioMute, so strip those out, too. - * - * This function is only called on untranslated keynames, - * so no need to be UTF-8 safe. - */ -static void -append_without_underscores (GString *s, - const char *str) -{ - const char *p; - - if (g_str_has_prefix (str, "XF86")) - p = str + 4; - else if (g_str_has_prefix (str, "ISO_")) - p = str + 4; - else - p = str; - - for ( ; *p; p++) - { - if (*p == '_') - g_string_append_c (s, ' '); - else - g_string_append_c (s, *p); - } -} - -/* On Mac, if the key has symbolic representation (e.g. arrow keys), - * append it to gstring and return TRUE; otherwise return FALSE. - * See http://docs.info.apple.com/article.html?path=Mac/10.5/en/cdb_symbs.html - * for the list of special keys. */ -static gboolean -append_keyval_symbol (guint accelerator_key, - GString *gstring) -{ -#ifdef GDK_WINDOWING_QUARTZ - switch (accelerator_key) - { - case GDK_KEY_Return: - /* U+21A9 LEFTWARDS ARROW WITH HOOK */ - g_string_append (gstring, "\xe2\x86\xa9"); - return TRUE; - - case GDK_KEY_ISO_Enter: - /* U+2324 UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS */ - g_string_append (gstring, "\xe2\x8c\xa4"); - return TRUE; - - case GDK_KEY_Left: - /* U+2190 LEFTWARDS ARROW */ - g_string_append (gstring, "\xe2\x86\x90"); - return TRUE; - - case GDK_KEY_Up: - /* U+2191 UPWARDS ARROW */ - g_string_append (gstring, "\xe2\x86\x91"); - return TRUE; - - case GDK_KEY_Right: - /* U+2192 RIGHTWARDS ARROW */ - g_string_append (gstring, "\xe2\x86\x92"); - return TRUE; - - case GDK_KEY_Down: - /* U+2193 DOWNWARDS ARROW */ - g_string_append (gstring, "\xe2\x86\x93"); - return TRUE; - - case GDK_KEY_Page_Up: - /* U+21DE UPWARDS ARROW WITH DOUBLE STROKE */ - g_string_append (gstring, "\xe2\x87\x9e"); - return TRUE; - - case GDK_KEY_Page_Down: - /* U+21DF DOWNWARDS ARROW WITH DOUBLE STROKE */ - g_string_append (gstring, "\xe2\x87\x9f"); - return TRUE; - - case GDK_KEY_Home: - /* U+2196 NORTH WEST ARROW */ - g_string_append (gstring, "\xe2\x86\x96"); - return TRUE; - - case GDK_KEY_End: - /* U+2198 SOUTH EAST ARROW */ - g_string_append (gstring, "\xe2\x86\x98"); - return TRUE; - - case GDK_KEY_Escape: - /* U+238B BROKEN CIRCLE WITH NORTHWEST ARROW */ - g_string_append (gstring, "\xe2\x8e\x8b"); - return TRUE; - - case GDK_KEY_BackSpace: - /* U+232B ERASE TO THE LEFT */ - g_string_append (gstring, "\xe2\x8c\xab"); - return TRUE; - - case GDK_KEY_Delete: - /* U+2326 ERASE TO THE RIGHT */ - g_string_append (gstring, "\xe2\x8c\xa6"); - return TRUE; - - default: - return FALSE; - } -#else /* !GDK_WINDOWING_QUARTZ */ - return FALSE; -#endif -} - -static void -append_separator (GString *string) -{ -#ifndef GDK_WINDOWING_QUARTZ - g_string_append (string, "+"); -#else - /* no separator on quartz */ -#endif -} - /** * gtk_accelerator_get_label: * @accelerator_key: accelerator keyval @@ -907,202 +1686,16 @@ gchar* gtk_accelerator_get_label (guint accelerator_key, GdkModifierType accelerator_mods) { - GString *gstring; - - gstring = g_string_new (NULL); - - gtk_accelerator_print_label (gstring, accelerator_key, accelerator_mods); - - return g_string_free (gstring, FALSE); -} - -void -gtk_accelerator_print_label (GString *gstring, - guint accelerator_key, - GdkModifierType accelerator_mods) -{ - gboolean seen_mod = FALSE; - gunichar ch; - - if (accelerator_mods & GDK_SHIFT_MASK) - { -#ifndef GDK_WINDOWING_QUARTZ - /* This is the text that should appear next to menu accelerators - * that use the shift key. If the text on this key isn't typically - * translated on keyboards used for your language, don't translate - * this. - */ - g_string_append (gstring, C_("keyboard label", "Shift")); -#else - /* U+21E7 UPWARDS WHITE ARROW */ - g_string_append (gstring, "\xe2\x87\xa7"); -#endif - seen_mod = TRUE; - } - - if (accelerator_mods & GDK_CONTROL_MASK) - { - if (seen_mod) - append_separator (gstring); - -#ifndef GDK_WINDOWING_QUARTZ - /* This is the text that should appear next to menu accelerators - * that use the control key. If the text on this key isn't typically - * translated on keyboards used for your language, don't translate - * this. - */ - g_string_append (gstring, C_("keyboard label", "Ctrl")); -#else - /* U+2303 UP ARROWHEAD */ - g_string_append (gstring, "\xe2\x8c\x83"); -#endif - seen_mod = TRUE; - } - - if (accelerator_mods & GDK_MOD1_MASK) - { - if (seen_mod) - append_separator (gstring); - -#ifndef GDK_WINDOWING_QUARTZ - /* This is the text that should appear next to menu accelerators - * that use the alt key. If the text on this key isn't typically - * translated on keyboards used for your language, don't translate - * this. - */ - g_string_append (gstring, C_("keyboard label", "Alt")); -#else - /* U+2325 OPTION KEY */ - g_string_append (gstring, "\xe2\x8c\xa5"); -#endif - seen_mod = TRUE; - } - - if (accelerator_mods & GDK_MOD2_MASK) - { - if (seen_mod) - append_separator (gstring); - - g_string_append (gstring, "Mod2"); - seen_mod = TRUE; - } + GtkAccelLabelClass *klass; + gchar *label; - if (accelerator_mods & GDK_MOD3_MASK) - { - if (seen_mod) - append_separator (gstring); + klass = g_type_class_ref (GTK_TYPE_ACCEL_LABEL); + label = _gtk_accel_label_class_get_accelerator_label (klass, + accelerator_key, + accelerator_mods); + g_type_class_unref (klass); /* klass is kept alive since gtk uses static types */ - g_string_append (gstring, "Mod3"); - seen_mod = TRUE; - } - - if (accelerator_mods & GDK_MOD4_MASK) - { - if (seen_mod) - append_separator (gstring); - - g_string_append (gstring, "Mod4"); - seen_mod = TRUE; - } - - if (accelerator_mods & GDK_MOD5_MASK) - { - if (seen_mod) - append_separator (gstring); - - g_string_append (gstring, "Mod5"); - seen_mod = TRUE; - } - - if (accelerator_mods & GDK_SUPER_MASK) - { - if (seen_mod) - append_separator (gstring); - - /* This is the text that should appear next to menu accelerators - * that use the super key. If the text on this key isn't typically - * translated on keyboards used for your language, don't translate - * this. - */ - g_string_append (gstring, C_("keyboard label", "Super")); - seen_mod = TRUE; - } - - if (accelerator_mods & GDK_HYPER_MASK) - { - if (seen_mod) - append_separator (gstring); - - /* This is the text that should appear next to menu accelerators - * that use the hyper key. If the text on this key isn't typically - * translated on keyboards used for your language, don't translate - * this. - */ - g_string_append (gstring, C_("keyboard label", "Hyper")); - seen_mod = TRUE; - } - - if (accelerator_mods & GDK_META_MASK) - { - if (seen_mod) - append_separator (gstring); - -#ifndef GDK_WINDOWING_QUARTZ - /* This is the text that should appear next to menu accelerators - * that use the meta key. If the text on this key isn't typically - * translated on keyboards used for your language, don't translate - * this. - */ - g_string_append (gstring, C_("keyboard label", "Meta")); -#else - /* Command key symbol U+2318 PLACE OF INTEREST SIGN */ - g_string_append (gstring, "\xe2\x8c\x98"); -#endif - seen_mod = TRUE; - } - - ch = gdk_keyval_to_unicode (accelerator_key); - if (ch && (ch == ' ' || g_unichar_isgraph (ch))) - { - if (seen_mod) - append_separator (gstring); - - switch (ch) - { - case ' ': - g_string_append (gstring, C_("keyboard label", "Space")); - break; - case '\\': - g_string_append (gstring, C_("keyboard label", "Backslash")); - break; - default: - g_string_append_unichar (gstring, g_unichar_toupper (ch)); - break; - } - } - else if (!append_keyval_symbol (accelerator_key, gstring)) - { - const char *tmp; - - tmp = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key)); - if (tmp != NULL) - { - if (seen_mod) - append_separator (gstring); - - if (tmp[0] != 0 && tmp[1] == 0) - g_string_append_c (gstring, g_ascii_toupper (tmp[0])); - else - { - const char *str; - str = g_dpgettext2 (GETTEXT_PACKAGE, "keyboard label", tmp); - if (str == tmp) - append_without_underscores (gstring, tmp); - else - g_string_append (gstring, str); - } - } - } + return label; } /** diff --git a/gtk/gtkaccelgroup.h b/gtk/gtkaccelgroup.h index 69d0a3f227..bdb4d29888 100644 --- a/gtk/gtkaccelgroup.h +++ b/gtk/gtkaccelgroup.h @@ -36,17 +36,172 @@ G_BEGIN_DECLS +/* --- type macros --- */ +#define GTK_TYPE_ACCEL_GROUP (gtk_accel_group_get_type ()) +#define GTK_ACCEL_GROUP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_ACCEL_GROUP, GtkAccelGroup)) +#define GTK_ACCEL_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_ACCEL_GROUP, GtkAccelGroupClass)) +#define GTK_IS_ACCEL_GROUP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_ACCEL_GROUP)) +#define GTK_IS_ACCEL_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ACCEL_GROUP)) +#define GTK_ACCEL_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_ACCEL_GROUP, GtkAccelGroupClass)) + + +/* --- accel flags --- */ +/** + * GtkAccelFlags: + * @GTK_ACCEL_VISIBLE: Accelerator is visible + * @GTK_ACCEL_LOCKED: Accelerator not removable + * @GTK_ACCEL_MASK: Mask + * + * Accelerator flags used with gtk_accel_group_connect(). + */ +typedef enum +{ + GTK_ACCEL_VISIBLE = 1 << 0, + GTK_ACCEL_LOCKED = 1 << 1, + GTK_ACCEL_MASK = 0x07 +} GtkAccelFlags; + + +/* --- typedefs & structures --- */ +typedef struct _GtkAccelGroup GtkAccelGroup; +typedef struct _GtkAccelGroupClass GtkAccelGroupClass; +typedef struct _GtkAccelGroupPrivate GtkAccelGroupPrivate; +typedef struct _GtkAccelKey GtkAccelKey; +typedef struct _GtkAccelGroupEntry GtkAccelGroupEntry; +typedef gboolean (*GtkAccelGroupActivate) (GtkAccelGroup *accel_group, + GObject *acceleratable, + guint keyval, + GdkModifierType modifier); + +/** + * GtkAccelGroupFindFunc: + * @key: + * @closure: + * @data: (closure): + */ +typedef gboolean (*GtkAccelGroupFindFunc) (GtkAccelKey *key, + GClosure *closure, + gpointer data); + +/** + * GtkAccelGroup: + * + * An object representing and maintaining a group of accelerators. + */ +struct _GtkAccelGroup +{ + GObject parent; + GtkAccelGroupPrivate *priv; +}; + +/** + * GtkAccelGroupClass: + * @parent_class: The parent class. + * @accel_changed: Signal emitted when an entry is added to or removed + * from the accel group. + */ +struct _GtkAccelGroupClass +{ + GObjectClass parent_class; + + /*< public >*/ + + void (*accel_changed) (GtkAccelGroup *accel_group, + guint keyval, + GdkModifierType modifier, + GClosure *accel_closure); + + /*< private >*/ + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); +}; + +/** + * GtkAccelKey: + * @accel_key: The accelerator keyval + * @accel_mods:The accelerator modifiers + * @accel_flags: The accelerator flags + */ +struct _GtkAccelKey +{ + guint accel_key; + GdkModifierType accel_mods; + guint accel_flags : 16; +}; + + +/* -- Accelerator Groups --- */ +GDK_AVAILABLE_IN_ALL +GType gtk_accel_group_get_type (void) G_GNUC_CONST; +GDK_AVAILABLE_IN_ALL +GtkAccelGroup* gtk_accel_group_new (void); +GDK_AVAILABLE_IN_ALL +gboolean gtk_accel_group_get_is_locked (GtkAccelGroup *accel_group); +GDK_AVAILABLE_IN_ALL +GdkModifierType + gtk_accel_group_get_modifier_mask (GtkAccelGroup *accel_group); +GDK_AVAILABLE_IN_ALL +void gtk_accel_group_lock (GtkAccelGroup *accel_group); +GDK_AVAILABLE_IN_ALL +void gtk_accel_group_unlock (GtkAccelGroup *accel_group); +GDK_AVAILABLE_IN_ALL +void gtk_accel_group_connect (GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods, + GtkAccelFlags accel_flags, + GClosure *closure); +GDK_AVAILABLE_IN_ALL +void gtk_accel_group_connect_by_path (GtkAccelGroup *accel_group, + const gchar *accel_path, + GClosure *closure); +GDK_AVAILABLE_IN_ALL +gboolean gtk_accel_group_disconnect (GtkAccelGroup *accel_group, + GClosure *closure); +GDK_AVAILABLE_IN_ALL +gboolean gtk_accel_group_disconnect_key (GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods); +GDK_AVAILABLE_IN_ALL +gboolean gtk_accel_group_activate (GtkAccelGroup *accel_group, + GQuark accel_quark, + GObject *acceleratable, + guint accel_key, + GdkModifierType accel_mods); + + +/* --- GtkActivatable glue --- */ +void _gtk_accel_group_attach (GtkAccelGroup *accel_group, + GObject *object); +void _gtk_accel_group_detach (GtkAccelGroup *accel_group, + GObject *object); +GDK_AVAILABLE_IN_ALL +gboolean gtk_accel_groups_activate (GObject *object, + guint accel_key, + GdkModifierType accel_mods); +GDK_AVAILABLE_IN_ALL +GSList* gtk_accel_groups_from_object (GObject *object); +GDK_AVAILABLE_IN_ALL +GtkAccelKey* gtk_accel_group_find (GtkAccelGroup *accel_group, + GtkAccelGroupFindFunc find_func, + gpointer data); +GDK_AVAILABLE_IN_ALL +GtkAccelGroup* gtk_accel_group_from_accel_closure (GClosure *closure); + + /* --- Accelerators--- */ GDK_AVAILABLE_IN_ALL gboolean gtk_accelerator_valid (guint keyval, GdkModifierType modifiers) G_GNUC_CONST; GDK_AVAILABLE_IN_ALL -gboolean gtk_accelerator_parse (const gchar *accelerator, +void gtk_accelerator_parse (const gchar *accelerator, guint *accelerator_key, GdkModifierType *accelerator_mods); GDK_AVAILABLE_IN_ALL -gboolean gtk_accelerator_parse_with_keycode (const gchar *accelerator, - GdkDisplay *display, +void gtk_accelerator_parse_with_keycode (const gchar *accelerator, guint *accelerator_key, guint **accelerator_codes, GdkModifierType *accelerator_mods); @@ -72,6 +227,21 @@ GDK_AVAILABLE_IN_ALL GdkModifierType gtk_accelerator_get_default_mod_mask (void); +GDK_AVAILABLE_IN_ALL +GtkAccelGroupEntry* gtk_accel_group_query (GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods, + guint *n_entries); + +struct _GtkAccelGroupEntry +{ + GtkAccelKey key; + GClosure *closure; + GQuark accel_path_quark; +}; + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkAccelGroup, g_object_unref) + G_END_DECLS #endif /* __GTK_ACCEL_GROUP_H__ */ diff --git a/gtk/gtkaccelgroupprivate.h b/gtk/gtkaccelgroupprivate.h index c5b4daef55..15da43ae2b 100644 --- a/gtk/gtkaccelgroupprivate.h +++ b/gtk/gtkaccelgroupprivate.h @@ -25,9 +25,19 @@ G_BEGIN_DECLS -void gtk_accelerator_print_label (GString *gstring, - guint accelerator_key, - GdkModifierType accelerator_mods); +struct _GtkAccelGroupPrivate +{ + guint lock_count; + GdkModifierType modifier_mask; + GSList *acceleratables; + guint n_accels; + GtkAccelGroupEntry *priv_accels; +}; + +void _gtk_accel_group_reconnect (GtkAccelGroup *accel_group, + GQuark accel_path_quark); +GSList* _gtk_accel_group_get_accelerables (GtkAccelGroup *accel_group); + G_END_DECLS #endif /* __GTK_ACCEL_GROUP_PRIVATE_H__ */ diff --git a/gtk/gtkaccellabel.c b/gtk/gtkaccellabel.c index 4fabce26a5..75ee16bc8c 100644 --- a/gtk/gtkaccellabel.c +++ b/gtk/gtkaccellabel.c @@ -29,7 +29,9 @@ #include <string.h> #include "gtklabel.h" +#include "gtkaccellabel.h" #include "gtkaccellabelprivate.h" +#include "gtkaccelmap.h" #include "gtkintl.h" #include "gtkmain.h" #include "gtkprivate.h" @@ -68,6 +70,7 @@ * set (see #GtkAccelFlags). * A #GtkAccelLabel can display multiple accelerators and even signal names, * though it is almost always used to display just one accelerator key. + * ]| * * # CSS nodes * @@ -83,6 +86,8 @@ enum { PROP_0, + PROP_ACCEL_CLOSURE, + PROP_ACCEL_WIDGET, PROP_LABEL, PROP_USE_UNDERLINE, LAST_PROP @@ -109,6 +114,10 @@ struct _GtkAccelLabelPrivate GtkWidget *text_label; GtkWidget *accel_label; + GtkWidget *accel_widget; /* done */ + GClosure *accel_closure; /* has set function */ + GtkAccelGroup *accel_group; /* set by set_accel_closure() */ + guint accel_key; /* manual accel key specification if != 0 */ GdkModifierType accel_mods; }; @@ -123,6 +132,7 @@ static void gtk_accel_label_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void gtk_accel_label_destroy (GtkWidget *widget); static void gtk_accel_label_finalize (GObject *object); G_DEFINE_TYPE_WITH_PRIVATE (GtkAccelLabel, gtk_accel_label, GTK_TYPE_WIDGET) @@ -137,8 +147,56 @@ gtk_accel_label_class_init (GtkAccelLabelClass *class) gobject_class->set_property = gtk_accel_label_set_property; gobject_class->get_property = gtk_accel_label_get_property; + widget_class->destroy = gtk_accel_label_destroy; + gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_ACCEL_LABEL); +#ifndef GDK_WINDOWING_QUARTZ + /* This is the text that should appear next to menu accelerators + * that use the shift key. If the text on this key isn't typically + * translated on keyboards used for your language, don't translate + * this. + */ + class->mod_name_shift = g_strdup (C_("keyboard label", "Shift")); + /* This is the text that should appear next to menu accelerators + * that use the control key. If the text on this key isn't typically + * translated on keyboards used for your language, don't translate + * this. + */ + class->mod_name_control = g_strdup (C_("keyboard label", "Ctrl")); + /* This is the text that should appear next to menu accelerators + * that use the alt key. If the text on this key isn't typically + * translated on keyboards used for your language, don't translate + * this. + */ + class->mod_name_alt = g_strdup (C_("keyboard label", "Alt")); + class->mod_separator = g_strdup ("+"); +#else /* GDK_WINDOWING_QUARTZ */ + + /* U+21E7 UPWARDS WHITE ARROW */ + class->mod_name_shift = g_strdup ("\xe2\x87\xa7"); + /* U+2303 UP ARROWHEAD */ + class->mod_name_control = g_strdup ("\xe2\x8c\x83"); + /* U+2325 OPTION KEY */ + class->mod_name_alt = g_strdup ("\xe2\x8c\xa5"); + class->mod_separator = g_strdup (""); + +#endif /* GDK_WINDOWING_QUARTZ */ + + props[PROP_ACCEL_CLOSURE] = + g_param_spec_boxed ("accel-closure", + P_("Accelerator Closure"), + P_("The closure to be monitored for accelerator changes"), + G_TYPE_CLOSURE, + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + props[PROP_ACCEL_WIDGET] = + g_param_spec_object ("accel-widget", + P_("Accelerator Widget"), + P_("The widget to be monitored for accelerator changes"), + GTK_TYPE_WIDGET, + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + props[PROP_LABEL] = g_param_spec_string ("label", P_("Label"), @@ -171,6 +229,12 @@ gtk_accel_label_set_property (GObject *object, switch (prop_id) { + case PROP_ACCEL_CLOSURE: + gtk_accel_label_set_accel_closure (accel_label, g_value_get_boxed (value)); + break; + case PROP_ACCEL_WIDGET: + gtk_accel_label_set_accel_widget (accel_label, g_value_get_object (value)); + break; case PROP_LABEL: gtk_accel_label_set_label (accel_label, g_value_get_string (value)); break; @@ -190,9 +254,16 @@ gtk_accel_label_get_property (GObject *object, GParamSpec *pspec) { GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (object); + GtkAccelLabelPrivate *priv = gtk_accel_label_get_instance_private (accel_label); switch (prop_id) { + case PROP_ACCEL_CLOSURE: + g_value_set_boxed (value, priv->accel_closure); + break; + case PROP_ACCEL_WIDGET: + g_value_set_object (value, priv->accel_widget); + break; case PROP_LABEL: g_value_set_string (value, gtk_accel_label_get_label (accel_label)); break; @@ -210,6 +281,10 @@ gtk_accel_label_init (GtkAccelLabel *accel_label) { GtkAccelLabelPrivate *priv = gtk_accel_label_get_instance_private (accel_label); + priv->accel_widget = NULL; + priv->accel_closure = NULL; + priv->accel_group = NULL; + priv->text_label = gtk_label_new (""); gtk_widget_set_hexpand (priv->text_label, TRUE); gtk_label_set_xalign (GTK_LABEL (priv->text_label), 0.0f); @@ -243,6 +318,17 @@ gtk_accel_label_new (const gchar *string) } static void +gtk_accel_label_destroy (GtkWidget *widget) +{ + GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (widget); + + gtk_accel_label_set_accel_widget (accel_label, NULL); + gtk_accel_label_set_accel_closure (accel_label, NULL); + + GTK_WIDGET_CLASS (gtk_accel_label_parent_class)->destroy (widget); +} + +static void gtk_accel_label_finalize (GObject *object) { GtkAccelLabel *accel_label = GTK_ACCEL_LABEL (object); @@ -255,6 +341,26 @@ gtk_accel_label_finalize (GObject *object) } /** + * gtk_accel_label_get_accel_widget: + * @accel_label: a #GtkAccelLabel + * + * Fetches the widget monitored by this accelerator label. See + * gtk_accel_label_set_accel_widget(). + * + * Returns: (nullable) (transfer none): the widget monitored by @accel_label, + * or %NULL if it is not monitoring a widget. + **/ +GtkWidget * +gtk_accel_label_get_accel_widget (GtkAccelLabel *accel_label) +{ + GtkAccelLabelPrivate *priv = gtk_accel_label_get_instance_private (accel_label); + + g_return_val_if_fail (GTK_IS_ACCEL_LABEL (accel_label), NULL); + + return priv->accel_widget; +} + +/** * gtk_accel_label_get_accel_width: * @accel_label: a #GtkAccelLabel. * @@ -278,6 +384,463 @@ gtk_accel_label_get_accel_width (GtkAccelLabel *accel_label) return min; } +static void +refetch_widget_accel_closure (GtkAccelLabel *accel_label) +{ + GtkAccelLabelPrivate *priv = gtk_accel_label_get_instance_private (accel_label); + GClosure *closure = NULL; + GList *clist, *list; + + g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label)); + g_return_if_fail (GTK_IS_WIDGET (priv->accel_widget)); + + clist = gtk_widget_list_accel_closures (priv->accel_widget); + for (list = clist; list; list = list->next) + { + /* we just take the first closure used */ + closure = list->data; + break; + } + + g_list_free (clist); + gtk_accel_label_set_accel_closure (accel_label, closure); +} + +static void +accel_widget_weak_ref_cb (GtkAccelLabel *accel_label, + GtkWidget *old_accel_widget) +{ + GtkAccelLabelPrivate *priv = gtk_accel_label_get_instance_private (accel_label); + + g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label)); + g_return_if_fail (GTK_IS_WIDGET (priv->accel_widget)); + + g_signal_handlers_disconnect_by_func (priv->accel_widget, + refetch_widget_accel_closure, + accel_label); + priv->accel_widget = NULL; + g_object_notify_by_pspec (G_OBJECT (accel_label), props[PROP_ACCEL_WIDGET]); +} + +/** + * gtk_accel_label_set_accel_widget: + * @accel_label: a #GtkAccelLabel + * @accel_widget: (nullable): the widget to be monitored, or %NULL + * + * Sets the widget to be monitored by this accelerator label. Passing %NULL for + * @accel_widget will dissociate @accel_label from its current widget, if any. + */ +void +gtk_accel_label_set_accel_widget (GtkAccelLabel *accel_label, + GtkWidget *accel_widget) +{ + GtkAccelLabelPrivate *priv = gtk_accel_label_get_instance_private (accel_label); + + g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label)); + + if (accel_widget) + g_return_if_fail (GTK_IS_WIDGET (accel_widget)); + + if (accel_widget != priv->accel_widget) + { + if (priv->accel_widget) + { + gtk_accel_label_set_accel_closure (accel_label, NULL); + g_signal_handlers_disconnect_by_func (priv->accel_widget, + refetch_widget_accel_closure, + accel_label); + g_object_weak_unref (G_OBJECT (priv->accel_widget), + (GWeakNotify) accel_widget_weak_ref_cb, accel_label); + } + + priv->accel_widget = accel_widget; + + if (priv->accel_widget) + { + g_object_weak_ref (G_OBJECT (priv->accel_widget), + (GWeakNotify) accel_widget_weak_ref_cb, accel_label); + g_signal_connect_object (priv->accel_widget, "accel-closures-changed", + G_CALLBACK (refetch_widget_accel_closure), + accel_label, G_CONNECT_SWAPPED); + refetch_widget_accel_closure (accel_label); + } + + g_object_notify_by_pspec (G_OBJECT (accel_label), props[PROP_ACCEL_WIDGET]); + } +} + +static void +gtk_accel_label_reset (GtkAccelLabel *accel_label) +{ + gtk_accel_label_refetch (accel_label); +} + +static void +check_accel_changed (GtkAccelGroup *accel_group, + guint keyval, + GdkModifierType modifier, + GClosure *accel_closure, + GtkAccelLabel *accel_label) +{ + GtkAccelLabelPrivate *priv = gtk_accel_label_get_instance_private (accel_label); + + if (accel_closure == priv->accel_closure) + gtk_accel_label_reset (accel_label); +} + +/** + * gtk_accel_label_set_accel_closure: + * @accel_label: a #GtkAccelLabel + * @accel_closure: (nullable): the closure to monitor for accelerator changes, + * or %NULL + * + * Sets the closure to be monitored by this accelerator label. The closure + * must be connected to an accelerator group; see gtk_accel_group_connect(). + * Passing %NULL for @accel_closure will dissociate @accel_label from its + * current closure, if any. + **/ +void +gtk_accel_label_set_accel_closure (GtkAccelLabel *accel_label, + GClosure *accel_closure) +{ + GtkAccelLabelPrivate *priv = gtk_accel_label_get_instance_private (accel_label); + + g_return_if_fail (GTK_IS_ACCEL_LABEL (accel_label)); + + if (accel_closure) + g_return_if_fail (gtk_accel_group_from_accel_closure (accel_closure) != NULL); + + if (accel_closure != priv->accel_closure) + { + if (priv->accel_closure) + { + g_signal_handlers_disconnect_by_func (priv->accel_group, + check_accel_changed, + accel_label); + priv->accel_group = NULL; + g_closure_unref (priv->accel_closure); + } + + priv->accel_closure = accel_closure; + + if (priv->accel_closure) + { + g_closure_ref (priv->accel_closure); + priv->accel_group = gtk_accel_group_from_accel_closure (accel_closure); + g_signal_connect_object (priv->accel_group, "accel-changed", G_CALLBACK (check_accel_changed), + accel_label, 0); + } + + gtk_accel_label_reset (accel_label); + g_object_notify_by_pspec (G_OBJECT (accel_label), props[PROP_ACCEL_CLOSURE]); + } +} + +/** + * gtk_accel_label_get_accel_closure: + * @accel_label: a #GtkAccelLabel + * + * Fetches the closure monitored by this accelerator label. See + * gtk_accel_label_set_accel_closure(). + * + * Returns: (nullable) (transfer none): the closure monitored by @accel_label, + * or %NULL if it is not monitoring a closure. + */ +GClosure * +gtk_accel_label_get_accel_closure (GtkAccelLabel *accel_label) +{ + GtkAccelLabelPrivate *priv = gtk_accel_label_get_instance_private (accel_label); + + g_return_val_if_fail (GTK_IS_ACCEL_LABEL (accel_label), NULL); + + return priv->accel_closure; +} + +static gboolean +find_accel (GtkAccelKey *key, + GClosure *closure, + gpointer data) +{ + return data == (gpointer) closure; +} + +/* Underscores in key names are better displayed as spaces + * E.g., Page_Up should be “Page Up”. + * + * Some keynames also have prefixes that are not suitable + * for display, e.g XF86AudioMute, so strip those out, too. + * + * This function is only called on untranslated keynames, + * so no need to be UTF-8 safe. + */ +static void +append_without_underscores (GString *s, + const char *str) +{ + const char *p; + + if (g_str_has_prefix (str, "XF86")) + p = str + 4; + else if (g_str_has_prefix (str, "ISO_")) + p = str + 4; + else + p = str; + + for ( ; *p; p++) + { + if (*p == '_') + g_string_append_c (s, ' '); + else + g_string_append_c (s, *p); + } +} + +/* On Mac, if the key has symbolic representation (e.g. arrow keys), + * append it to gstring and return TRUE; otherwise return FALSE. + * See http://docs.info.apple.com/article.html?path=Mac/10.5/en/cdb_symbs.html + * for the list of special keys. */ +static gboolean +append_keyval_symbol (guint accelerator_key, + GString *gstring) +{ +#ifdef GDK_WINDOWING_QUARTZ + switch (accelerator_key) + { + case GDK_KEY_Return: + /* U+21A9 LEFTWARDS ARROW WITH HOOK */ + g_string_append (gstring, "\xe2\x86\xa9"); + return TRUE; + + case GDK_KEY_ISO_Enter: + /* U+2324 UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS */ + g_string_append (gstring, "\xe2\x8c\xa4"); + return TRUE; + + case GDK_KEY_Left: + /* U+2190 LEFTWARDS ARROW */ + g_string_append (gstring, "\xe2\x86\x90"); + return TRUE; + + case GDK_KEY_Up: + /* U+2191 UPWARDS ARROW */ + g_string_append (gstring, "\xe2\x86\x91"); + return TRUE; + + case GDK_KEY_Right: + /* U+2192 RIGHTWARDS ARROW */ + g_string_append (gstring, "\xe2\x86\x92"); + return TRUE; + + case GDK_KEY_Down: + /* U+2193 DOWNWARDS ARROW */ + g_string_append (gstring, "\xe2\x86\x93"); + return TRUE; + + case GDK_KEY_Page_Up: + /* U+21DE UPWARDS ARROW WITH DOUBLE STROKE */ + g_string_append (gstring, "\xe2\x87\x9e"); + return TRUE; + + case GDK_KEY_Page_Down: + /* U+21DF DOWNWARDS ARROW WITH DOUBLE STROKE */ + g_string_append (gstring, "\xe2\x87\x9f"); + return TRUE; + + case GDK_KEY_Home: + /* U+2196 NORTH WEST ARROW */ + g_string_append (gstring, "\xe2\x86\x96"); + return TRUE; + + case GDK_KEY_End: + /* U+2198 SOUTH EAST ARROW */ + g_string_append (gstring, "\xe2\x86\x98"); + return TRUE; + + case GDK_KEY_Escape: + /* U+238B BROKEN CIRCLE WITH NORTHWEST ARROW */ + g_string_append (gstring, "\xe2\x8e\x8b"); + return TRUE; + + case GDK_KEY_BackSpace: + /* U+232B ERASE TO THE LEFT */ + g_string_append (gstring, "\xe2\x8c\xab"); + return TRUE; + + case GDK_KEY_Delete: + /* U+2326 ERASE TO THE RIGHT */ + g_string_append (gstring, "\xe2\x8c\xa6"); + return TRUE; + + default: + return FALSE; + } +#else /* !GDK_WINDOWING_QUARTZ */ + return FALSE; +#endif +} + +gchar * +_gtk_accel_label_class_get_accelerator_label (GtkAccelLabelClass *klass, + guint accelerator_key, + GdkModifierType accelerator_mods) +{ + GString *gstring; + gboolean seen_mod = FALSE; + gunichar ch; + + gstring = g_string_sized_new (10); /* ~len('backspace') */ + + if (accelerator_mods & GDK_SHIFT_MASK) + { + g_string_append (gstring, klass->mod_name_shift); + seen_mod = TRUE; + } + + if (accelerator_mods & GDK_CONTROL_MASK) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + g_string_append (gstring, klass->mod_name_control); + seen_mod = TRUE; + } + + if (accelerator_mods & GDK_MOD1_MASK) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + g_string_append (gstring, klass->mod_name_alt); + seen_mod = TRUE; + } + + if (accelerator_mods & GDK_MOD2_MASK) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + + g_string_append (gstring, "Mod2"); + seen_mod = TRUE; + } + + if (accelerator_mods & GDK_MOD3_MASK) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + + g_string_append (gstring, "Mod3"); + seen_mod = TRUE; + } + + if (accelerator_mods & GDK_MOD4_MASK) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + + g_string_append (gstring, "Mod4"); + seen_mod = TRUE; + } + + if (accelerator_mods & GDK_MOD5_MASK) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + + g_string_append (gstring, "Mod5"); + seen_mod = TRUE; + } + + if (accelerator_mods & GDK_SUPER_MASK) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + + /* This is the text that should appear next to menu accelerators + * that use the super key. If the text on this key isn't typically + * translated on keyboards used for your language, don't translate + * this. + */ + g_string_append (gstring, C_("keyboard label", "Super")); + seen_mod = TRUE; + } + + if (accelerator_mods & GDK_HYPER_MASK) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + + /* This is the text that should appear next to menu accelerators + * that use the hyper key. If the text on this key isn't typically + * translated on keyboards used for your language, don't translate + * this. + */ + g_string_append (gstring, C_("keyboard label", "Hyper")); + seen_mod = TRUE; + } + + if (accelerator_mods & GDK_META_MASK) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + +#ifndef GDK_WINDOWING_QUARTZ + /* This is the text that should appear next to menu accelerators + * that use the meta key. If the text on this key isn't typically + * translated on keyboards used for your language, don't translate + * this. + */ + g_string_append (gstring, C_("keyboard label", "Meta")); +#else + /* Command key symbol U+2318 PLACE OF INTEREST SIGN */ + g_string_append (gstring, "\xe2\x8c\x98"); +#endif + seen_mod = TRUE; + } + + ch = gdk_keyval_to_unicode (accelerator_key); + if (ch && (ch == ' ' || g_unichar_isgraph (ch))) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + + switch (ch) + { + case ' ': + g_string_append (gstring, C_("keyboard label", "Space")); + break; + case '\\': + g_string_append (gstring, C_("keyboard label", "Backslash")); + break; + default: + g_string_append_unichar (gstring, g_unichar_toupper (ch)); + break; + } + } + else if (!append_keyval_symbol (accelerator_key, gstring)) + { + const char *tmp; + + tmp = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key)); + if (tmp != NULL) + { + if (seen_mod) + g_string_append (gstring, klass->mod_separator); + + if (tmp[0] != 0 && tmp[1] == 0) + g_string_append_c (gstring, g_ascii_toupper (tmp[0])); + else + { + const gchar *str; + str = g_dpgettext2 (GETTEXT_PACKAGE, "keyboard label", tmp); + if (str == tmp) + append_without_underscores (gstring, tmp); + else + g_string_append (gstring, str); + } + } + } + + return g_string_free (gstring, FALSE); +} + /** * gtk_accel_label_refetch: * @accel_label: a #GtkAccelLabel. @@ -301,7 +864,7 @@ gtk_accel_label_refetch (GtkAccelLabel *accel_label) "gtk-enable-accels", &enable_accels, NULL); - if (enable_accels && priv->accel_key) + if (enable_accels && (priv->accel_closure || priv->accel_key)) { gboolean have_accel = FALSE; guint accel_key; @@ -315,9 +878,30 @@ gtk_accel_label_refetch (GtkAccelLabel *accel_label) have_accel = TRUE; } + /* If we don't have a hardcoded value, check the accel group */ + if (!have_accel) + { + GtkAccelKey *key; + + key = gtk_accel_group_find (priv->accel_group, find_accel, priv->accel_closure); + + if (key && key->accel_flags & GTK_ACCEL_VISIBLE) + { + accel_key = key->accel_key; + accel_mods = key->accel_mods; + have_accel = TRUE; + } + } + /* If we found a key using either method, set it */ if (have_accel) - accel_string = gtk_accelerator_get_label (accel_key, accel_mods); + { + GtkAccelLabelClass *klass; + + klass = GTK_ACCEL_LABEL_GET_CLASS (accel_label); + accel_string = _gtk_accel_label_class_get_accelerator_label (klass, accel_key, accel_mods); + } + else /* Otherwise we have a closure with no key. Show "-/-". */ accel_string = g_strdup ("-/-"); @@ -359,7 +943,7 @@ gtk_accel_label_set_accel (GtkAccelLabel *accel_label, priv->accel_key = accelerator_key; priv->accel_mods = accelerator_mods; - gtk_accel_label_refetch (accel_label); + gtk_accel_label_reset (accel_label); } /** diff --git a/gtk/gtkaccellabel.h b/gtk/gtkaccellabel.h index 7177b79ceb..a5e63559f1 100644 --- a/gtk/gtkaccellabel.h +++ b/gtk/gtkaccellabel.h @@ -46,8 +46,18 @@ GType gtk_accel_label_get_type (void) G_GNUC_CONST; GDK_AVAILABLE_IN_ALL GtkWidget *gtk_accel_label_new (const gchar *string); GDK_AVAILABLE_IN_ALL +GtkWidget *gtk_accel_label_get_accel_widget (GtkAccelLabel *accel_label); +GDK_AVAILABLE_IN_ALL guint gtk_accel_label_get_accel_width (GtkAccelLabel *accel_label); GDK_AVAILABLE_IN_ALL +void gtk_accel_label_set_accel_widget (GtkAccelLabel *accel_label, + GtkWidget *accel_widget); +GDK_AVAILABLE_IN_ALL +void gtk_accel_label_set_accel_closure (GtkAccelLabel *accel_label, + GClosure *accel_closure); +GDK_AVAILABLE_IN_ALL +GClosure * gtk_accel_label_get_accel_closure (GtkAccelLabel *accel_label); +GDK_AVAILABLE_IN_ALL gboolean gtk_accel_label_refetch (GtkAccelLabel *accel_label); GDK_AVAILABLE_IN_ALL void gtk_accel_label_set_accel (GtkAccelLabel *accel_label, diff --git a/gtk/gtkaccelmap.c b/gtk/gtkaccelmap.c new file mode 100644 index 0000000000..2c24d78fa8 --- /dev/null +++ b/gtk/gtkaccelmap.c @@ -0,0 +1,1086 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1998, 2001 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "gtkaccelmapprivate.h" +#include "gtkaccelgroupprivate.h" + +#include "gtkmarshalers.h" +#include "gtkwindowprivate.h" +#include "gtkintl.h" + +#include <glib/gstdio.h> + +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef G_OS_WIN32 +#include <io.h> +#endif + + +/** + * SECTION:gtkaccelmap + * @Short_description: Loadable keyboard accelerator specifications + * @Title: Accelerator Maps + * @See_also: #GtkAccelGroup, #GtkAccelKey, gtk_widget_set_accel_path(), gtk_menu_item_set_accel_path() + * + * Accelerator maps are used to define runtime configurable accelerators. + * Functions for manipulating them are are usually used by higher level + * convenience mechanisms and are thus considered + * “low-level”. You’ll want to use them if you’re manually creating menus that + * should have user-configurable accelerators. + * + * An accelerator is uniquely defined by: + * - accelerator path + * - accelerator key + * - accelerator modifiers + * + * The accelerator path must consist of + * “<WINDOWTYPE>/Category1/Category2/.../Action”, where WINDOWTYPE + * should be a unique application-specific identifier that corresponds + * to the kind of window the accelerator is being used in, e.g. + * “Gimp-Image”, “Abiword-Document” or “Gnumeric-Settings”. + * The “Category1/.../Action” portion is most appropriately chosen by + * the action the accelerator triggers, i.e. for accelerators on menu + * items, choose the item’s menu path, e.g. “File/Save As”, + * “Image/View/Zoom” or “Edit/Select All”. So a full valid accelerator + * path may look like: “<Gimp-Toolbox>/File/Dialogs/Tool Options...”. + * + * All accelerators are stored inside one global #GtkAccelMap that can + * be obtained using gtk_accel_map_get(). See + * [Monitoring changes][monitoring-changes] for additional + * details. + * + * # Manipulating accelerators + * + * New accelerators can be added using gtk_accel_map_add_entry(). + * To search for specific accelerator, use gtk_accel_map_lookup_entry(). + * Modifications of existing accelerators should be done using + * gtk_accel_map_change_entry(). + * + * In order to avoid having some accelerators changed, they can be + * locked using gtk_accel_map_lock_path(). Unlocking is done using + * gtk_accel_map_unlock_path(). + * + * # Saving and loading accelerator maps + * + * Accelerator maps can be saved to and loaded from some external + * resource. For simple saving and loading from file, + * gtk_accel_map_save() and gtk_accel_map_load() are provided. + * Saving and loading can also be done by providing file descriptor + * to gtk_accel_map_save_fd() and gtk_accel_map_load_fd(). + * + * # Monitoring changes + * + * #GtkAccelMap object is only useful for monitoring changes of + * accelerators. By connecting to #GtkAccelMap::changed signal, one + * can monitor changes of all accelerators. It is also possible to + * monitor only single accelerator path by using it as a detail of + * the #GtkAccelMap::changed signal. + */ + + +/* --- structures --- */ +struct _GtkAccelMap +{ + GObject parent_instance; +}; + +struct _GtkAccelMapClass +{ + GObjectClass parent_class; +}; + +typedef struct { + const gchar *accel_path; + guint accel_key; + guint accel_mods; + guint std_accel_key; + guint std_accel_mods; + guint changed : 1; + guint lock_count : 15; + GSList *groups; +} AccelEntry; + +/* --- signals --- */ +enum { + CHANGED, + LAST_SIGNAL +}; + +/* --- variables --- */ + +static GHashTable *accel_entry_ht = NULL; /* accel_path -> AccelEntry */ +static GSList *accel_filters = NULL; +static gulong accel_map_signals[LAST_SIGNAL] = { 0, }; + +/* --- prototypes --- */ +static void do_accel_map_changed (AccelEntry *entry); + +/* --- functions --- */ +static guint +accel_entry_hash (gconstpointer key) +{ + const AccelEntry *entry = key; + + return g_str_hash (entry->accel_path); +} + +static gboolean +accel_entry_equal (gconstpointer key1, + gconstpointer key2) +{ + const AccelEntry *entry1 = key1; + const AccelEntry *entry2 = key2; + + return g_str_equal (entry1->accel_path, entry2->accel_path); +} + +static int +accel_entry_compare (gconstpointer a, + gconstpointer b) +{ + const AccelEntry *entry1 = a; + const AccelEntry *entry2 = b; + + return strcmp (entry1->accel_path, entry2->accel_path); +} + +static inline AccelEntry* +accel_path_lookup (const gchar *accel_path) +{ + AccelEntry ekey; + + ekey.accel_path = accel_path; + + /* safety NULL check for return_if_fail()s */ + return accel_path ? g_hash_table_lookup (accel_entry_ht, &ekey) : NULL; +} + +void +_gtk_accel_map_init (void) +{ + if (accel_entry_ht == NULL) + accel_entry_ht = g_hash_table_new (accel_entry_hash, accel_entry_equal); +} + +gboolean +_gtk_accel_path_is_valid (const gchar *accel_path) +{ + gchar *p; + + if (!accel_path || accel_path[0] != '<' || + accel_path[1] == '<' || accel_path[1] == '>' || !accel_path[1]) + return FALSE; + p = strchr (accel_path, '>'); + if (!p || (p[1] != 0 && p[1] != '/')) + return FALSE; + return TRUE; +} + +/** + * gtk_accel_map_add_entry: + * @accel_path: valid accelerator path + * @accel_key: the accelerator key + * @accel_mods: the accelerator modifiers + * + * Registers a new accelerator with the global accelerator map. + * This function should only be called once per @accel_path + * with the canonical @accel_key and @accel_mods for this path. + * To change the accelerator during runtime programatically, use + * gtk_accel_map_change_entry(). + * + * Set @accel_key and @accel_mods to 0 to request a removal of + * the accelerator. + * + * Note that @accel_path string will be stored in a #GQuark. Therefore, if you + * pass a static string, you can save some memory by interning it first with + * g_intern_static_string(). + */ +void +gtk_accel_map_add_entry (const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods) +{ + AccelEntry *entry; + + g_return_if_fail (_gtk_accel_path_is_valid (accel_path)); + + if (!accel_key) + accel_mods = 0; + else + accel_mods &= gtk_accelerator_get_default_mod_mask (); + + entry = accel_path_lookup (accel_path); + if (entry) + { + if (!entry->std_accel_key && !entry->std_accel_mods && + (accel_key || accel_mods)) + { + entry->std_accel_key = accel_key; + entry->std_accel_mods = accel_mods; + if (!entry->changed) + gtk_accel_map_change_entry (entry->accel_path, accel_key, accel_mods, TRUE); + } + } + else + { + entry = g_slice_new0 (AccelEntry); + entry->accel_path = g_intern_string (accel_path); + entry->std_accel_key = accel_key; + entry->std_accel_mods = accel_mods; + entry->accel_key = accel_key; + entry->accel_mods = accel_mods; + entry->changed = FALSE; + entry->lock_count = 0; + g_hash_table_insert (accel_entry_ht, entry, entry); + + do_accel_map_changed (entry); + } +} + +/** + * gtk_accel_map_lookup_entry: + * @accel_path: a valid accelerator path + * @key: (allow-none) (out): the accelerator key to be filled in (optional) + * + * Looks up the accelerator entry for @accel_path and fills in @key. + * + * Returns: %TRUE if @accel_path is known, %FALSE otherwise + */ +gboolean +gtk_accel_map_lookup_entry (const gchar *accel_path, + GtkAccelKey *key) +{ + AccelEntry *entry; + + g_return_val_if_fail (_gtk_accel_path_is_valid (accel_path), FALSE); + + entry = accel_path_lookup (accel_path); + if (entry && key) + { + key->accel_key = entry->accel_key; + key->accel_mods = entry->accel_mods; + key->accel_flags = 0; + } + + return entry ? TRUE : FALSE; +} + +static void +hash2slist_foreach (gpointer key, + gpointer value, + gpointer user_data) +{ + GSList **slist_p = user_data; + + *slist_p = g_slist_prepend (*slist_p, value); +} + +static GSList* +g_hash_table_slist_values (GHashTable *hash_table) +{ + GSList *slist = NULL; + + g_return_val_if_fail (hash_table != NULL, NULL); + + g_hash_table_foreach (hash_table, hash2slist_foreach, &slist); + + return slist; +} + +/* if simulate==TRUE, return whether accel_path can be changed to + * accel_key && accel_mods. otherwise, return whether accel_path + * was actually changed. + */ +static gboolean +internal_change_entry (const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean replace, + gboolean simulate) +{ + GSList *node, *slist, *win_list, *group_list, *replace_list = NULL; + GHashTable *group_hm, *window_hm; + gboolean change_accel, removable, can_change = TRUE, seen_accel = FALSE; + GQuark entry_quark; + AccelEntry *entry = accel_path_lookup (accel_path); + + /* not much todo if there's no entry yet */ + if (!entry) + { + if (!simulate) + { + gtk_accel_map_add_entry (accel_path, 0, 0); + entry = accel_path_lookup (accel_path); + g_assert (entry); + entry->accel_key = accel_key; + entry->accel_mods = accel_mods; + entry->changed = TRUE; + + do_accel_map_changed (entry); + } + return TRUE; + } + + /* if there's nothing to change, not much todo either */ + if (entry->accel_key == accel_key && entry->accel_mods == accel_mods) + { + if (!simulate) + entry->changed = TRUE; + return simulate ? TRUE : FALSE; + } + + /* The no-change case has already been handled, so + * simulate doesn't make a difference here. + */ + if (entry->lock_count > 0) + return FALSE; + + /* nobody's interested, easy going */ + if (!entry->groups) + { + if (!simulate) + { + entry->accel_key = accel_key; + entry->accel_mods = accel_mods; + entry->changed = TRUE; + + do_accel_map_changed (entry); + } + return TRUE; + } + + /* 1) fetch all accel groups affected by this entry */ + entry_quark = g_quark_try_string (entry->accel_path); + group_hm = g_hash_table_new (NULL, NULL); + window_hm = g_hash_table_new (NULL, NULL); + for (slist = entry->groups; slist; slist = slist->next) + g_hash_table_insert (group_hm, slist->data, slist->data); + + /* 2) collect acceleratables affected */ + group_list = g_hash_table_slist_values (group_hm); + for (slist = group_list; slist; slist = slist->next) + { + GtkAccelGroup *group = slist->data; + + for (node = _gtk_accel_group_get_accelerables (group); node; node = node->next) + g_hash_table_insert (window_hm, node->data, node->data); + } + g_slist_free (group_list); + + /* 3) include all accel groups used by acceleratables */ + win_list = g_hash_table_slist_values (window_hm); + g_hash_table_destroy (window_hm); + for (slist = win_list; slist; slist = slist->next) + for (node = gtk_accel_groups_from_object (slist->data); node; node = node->next) + g_hash_table_insert (group_hm, node->data, node->data); + group_list = g_hash_table_slist_values (group_hm); + g_hash_table_destroy (group_hm); + + /* 4) walk the acceleratables and figure whether they occupy accel_key&accel_mods */ + if (accel_key) + for (slist = win_list; slist; slist = slist->next) + if (GTK_IS_WINDOW (slist->data)) /* bad kludge in lack of a GtkAcceleratable */ + if (_gtk_window_query_nonaccels (slist->data, accel_key, accel_mods)) + { + seen_accel = TRUE; + break; + } + removable = !seen_accel; + + /* 5) walk all accel groups and search for locks */ + if (removable) + for (slist = group_list; slist; slist = slist->next) + { + GtkAccelGroup *group = slist->data; + GtkAccelGroupEntry *ag_entry; + guint i, n; + + n = 0; + ag_entry = entry->accel_key ? gtk_accel_group_query (group, entry->accel_key, entry->accel_mods, &n) : NULL; + for (i = 0; i < n; i++) + if (ag_entry[i].accel_path_quark == entry_quark) + { + can_change = !(ag_entry[i].key.accel_flags & GTK_ACCEL_LOCKED); + if (!can_change) + goto break_loop_step5; + } + + n = 0; + ag_entry = accel_key ? gtk_accel_group_query (group, accel_key, accel_mods, &n) : NULL; + for (i = 0; i < n; i++) + { + seen_accel = TRUE; + removable = !gtk_accel_group_get_is_locked (group) && !(ag_entry[i].key.accel_flags & GTK_ACCEL_LOCKED); + if (!removable) + goto break_loop_step5; + if (ag_entry[i].accel_path_quark) + replace_list = g_slist_prepend (replace_list, GUINT_TO_POINTER (ag_entry[i].accel_path_quark)); + } + } + break_loop_step5: + + /* 6) check whether we can remove existing accelerators */ + if (removable && can_change) + for (slist = replace_list; slist; slist = slist->next) + if (!internal_change_entry (g_quark_to_string (GPOINTER_TO_UINT (slist->data)), 0, 0, FALSE, TRUE)) + { + removable = FALSE; + break; + } + + /* 7) check conditions and proceed if possible */ + change_accel = can_change && (!seen_accel || (removable && replace)); + + if (change_accel && !simulate) + { + /* ref accel groups */ + for (slist = group_list; slist; slist = slist->next) + g_object_ref (slist->data); + + /* 8) remove existing accelerators */ + for (slist = replace_list; slist; slist = slist->next) + internal_change_entry (g_quark_to_string (GPOINTER_TO_UINT (slist->data)), 0, 0, FALSE, FALSE); + + /* 9) install new accelerator */ + entry->accel_key = accel_key; + entry->accel_mods = accel_mods; + entry->changed = TRUE; + + for (slist = group_list; slist; slist = slist->next) + _gtk_accel_group_reconnect (slist->data, g_quark_from_string (entry->accel_path)); + + /* unref accel groups */ + for (slist = group_list; slist; slist = slist->next) + g_object_unref (slist->data); + + do_accel_map_changed (entry); + } + g_slist_free (replace_list); + g_slist_free (group_list); + g_slist_free (win_list); + + return change_accel; +} + +/** + * gtk_accel_map_change_entry: + * @accel_path: a valid accelerator path + * @accel_key: the new accelerator key + * @accel_mods: the new accelerator modifiers + * @replace: %TRUE if other accelerators may be deleted upon conflicts + * + * Changes the @accel_key and @accel_mods currently associated with @accel_path. + * Due to conflicts with other accelerators, a change may not always be possible, + * @replace indicates whether other accelerators may be deleted to resolve such + * conflicts. A change will only occur if all conflicts could be resolved (which + * might not be the case if conflicting accelerators are locked). Successful + * changes are indicated by a %TRUE return value. + * + * Note that @accel_path string will be stored in a #GQuark. Therefore, if you + * pass a static string, you can save some memory by interning it first with + * g_intern_static_string(). + * + * Returns: %TRUE if the accelerator could be changed, %FALSE otherwise + */ +gboolean +gtk_accel_map_change_entry (const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean replace) +{ + g_return_val_if_fail (_gtk_accel_path_is_valid (accel_path), FALSE); + + return internal_change_entry (accel_path, accel_key, accel_key ? accel_mods : 0, replace, FALSE); +} + +static guint +accel_map_parse_accel_path (GScanner *scanner) +{ + guint accel_key = 0; + GdkModifierType accel_mods = 0; + gchar *path, *accel; + + /* parse accel path */ + g_scanner_get_next_token (scanner); + if (scanner->token != G_TOKEN_STRING) + return G_TOKEN_STRING; + + /* test if the next token is an accelerator */ + g_scanner_peek_next_token (scanner); + if (scanner->next_token != G_TOKEN_STRING) + { + /* if not so, eat that token and error out */ + g_scanner_get_next_token (scanner); + return G_TOKEN_STRING; + } + + /* get the full accelerator specification */ + path = g_strdup (scanner->value.v_string); + g_scanner_get_next_token (scanner); + accel = g_strdup (scanner->value.v_string); + + /* ensure the entry is present */ + gtk_accel_map_add_entry (path, 0, 0); + + /* and propagate it */ + gtk_accelerator_parse (accel, &accel_key, &accel_mods); + gtk_accel_map_change_entry (path, accel_key, accel_mods, TRUE); + + g_free (accel); + g_free (path); + + /* check correct statement end */ + g_scanner_get_next_token (scanner); + if (scanner->token != ')') + return ')'; + else + return G_TOKEN_NONE; +} + +static void +accel_map_parse_statement (GScanner *scanner) +{ + guint expected_token; + + g_scanner_get_next_token (scanner); + + if (scanner->token == G_TOKEN_SYMBOL) + { + guint (*parser_func) (GScanner*); + + parser_func = (guint (*) (GScanner *))scanner->value.v_symbol; + + expected_token = parser_func (scanner); + } + else + expected_token = G_TOKEN_SYMBOL; + + /* skip rest of statement on errrors + */ + if (expected_token != G_TOKEN_NONE) + { + register guint level; + + level = 1; + if (scanner->token == ')') + level--; + if (scanner->token == '(') + level++; + + while (!g_scanner_eof (scanner) && level > 0) + { + g_scanner_get_next_token (scanner); + + if (scanner->token == '(') + level++; + else if (scanner->token == ')') + level--; + } + } +} + +/** + * gtk_accel_map_load_scanner: + * @scanner: a #GScanner which has already been provided with an input file + * + * #GScanner variant of gtk_accel_map_load(). + */ +void +gtk_accel_map_load_scanner (GScanner *scanner) +{ + gboolean skip_comment_single; + gboolean symbol_2_token; + gchar *cpair_comment_single; + gpointer saved_symbol; + + g_return_if_fail (scanner != NULL); + + /* configure scanner */ + skip_comment_single = scanner->config->skip_comment_single; + scanner->config->skip_comment_single = TRUE; + cpair_comment_single = scanner->config->cpair_comment_single; + scanner->config->cpair_comment_single = (char *) ";\n"; + symbol_2_token = scanner->config->symbol_2_token; + scanner->config->symbol_2_token = FALSE; + saved_symbol = g_scanner_lookup_symbol (scanner, "gtk_accel_path"); + g_scanner_scope_add_symbol (scanner, 0, "gtk_accel_path", + accel_map_parse_accel_path); + + /* outer parsing loop + */ + g_scanner_peek_next_token (scanner); + while (scanner->next_token == '(') + { + g_scanner_get_next_token (scanner); + + accel_map_parse_statement (scanner); + + g_scanner_peek_next_token (scanner); + } + + /* restore config */ + scanner->config->skip_comment_single = skip_comment_single; + scanner->config->cpair_comment_single = cpair_comment_single; + scanner->config->symbol_2_token = symbol_2_token; + g_scanner_scope_remove_symbol (scanner, 0, "gtk_accel_path"); + if (saved_symbol) + g_scanner_scope_add_symbol (scanner, 0, "gtk_accel_path", saved_symbol); +} + +/** + * gtk_accel_map_load_fd: + * @fd: a valid readable file descriptor + * + * Filedescriptor variant of gtk_accel_map_load(). + * + * Note that the file descriptor will not be closed by this function. + */ +void +gtk_accel_map_load_fd (gint fd) +{ + GScanner *scanner; + + g_return_if_fail (fd >= 0); + + /* create and setup scanner */ + scanner = g_scanner_new (NULL); + g_scanner_input_file (scanner, fd); + + gtk_accel_map_load_scanner (scanner); + + g_scanner_destroy (scanner); +} + +/** + * gtk_accel_map_load: + * @file_name: (type filename): a file containing accelerator specifications, + * in the GLib file name encoding + * + * Parses a file previously saved with gtk_accel_map_save() for + * accelerator specifications, and propagates them accordingly. + */ +void +gtk_accel_map_load (const gchar *file_name) +{ + gint fd; + + g_return_if_fail (file_name != NULL); + + fd = g_open (file_name, O_RDONLY, 0); + if (fd < 0) + return; + + gtk_accel_map_load_fd (fd); + + close (fd); +} + +static gboolean +write_all (gint fd, + gchar *buf, + gsize to_write) +{ + while (to_write > 0) + { + gssize count = write (fd, buf, to_write); + if (count < 0) + { + if (errno != EINTR) + return FALSE; + } + else + { + to_write -= count; + buf += count; + } + } + + return TRUE; +} + +static void +accel_map_print (gpointer data, + const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean changed) +{ + GString *gstring = g_string_new (changed ? NULL : "; "); + gint fd = GPOINTER_TO_INT (data); + gchar *tmp, *name; + + g_string_append (gstring, "(gtk_accel_path \""); + + tmp = g_strescape (accel_path, NULL); + g_string_append (gstring, tmp); + g_free (tmp); + + g_string_append (gstring, "\" \""); + + name = gtk_accelerator_name (accel_key, accel_mods); + tmp = g_strescape (name, NULL); + g_free (name); + g_string_append (gstring, tmp); + g_free (tmp); + + g_string_append (gstring, "\")\n"); + + write_all (fd, gstring->str, gstring->len); + + g_string_free (gstring, TRUE); +} + +/** + * gtk_accel_map_save_fd: + * @fd: a valid writable file descriptor + * + * Filedescriptor variant of gtk_accel_map_save(). + * + * Note that the file descriptor will not be closed by this function. + */ +void +gtk_accel_map_save_fd (gint fd) +{ + GString *gstring; + + g_return_if_fail (fd >= 0); + + gstring = g_string_new ("; "); + if (g_get_prgname ()) + g_string_append (gstring, g_get_prgname ()); + g_string_append (gstring, " GtkAccelMap rc-file -*- scheme -*-\n"); + g_string_append (gstring, "; this file is an automated accelerator map dump\n"); + g_string_append (gstring, ";\n"); + + write_all (fd, gstring->str, gstring->len); + + g_string_free (gstring, TRUE); + + gtk_accel_map_foreach (GINT_TO_POINTER (fd), accel_map_print); +} + +/** + * gtk_accel_map_save: + * @file_name: (type filename): the name of the file to contain + * accelerator specifications, in the GLib file name encoding + * + * Saves current accelerator specifications (accelerator path, key + * and modifiers) to @file_name. + * The file is written in a format suitable to be read back in by + * gtk_accel_map_load(). + */ +void +gtk_accel_map_save (const gchar *file_name) +{ + gint fd; + + g_return_if_fail (file_name != NULL); + + fd = g_open (file_name, O_CREAT | O_TRUNC | O_WRONLY, 0644); + if (fd < 0) + return; + + gtk_accel_map_save_fd (fd); + + close (fd); +} + +/** + * gtk_accel_map_foreach: + * @data: (allow-none): data to be passed into @foreach_func + * @foreach_func: (scope call): function to be executed for each accel + * map entry which is not filtered out + * + * Loops over the entries in the accelerator map whose accel path + * doesn’t match any of the filters added with gtk_accel_map_add_filter(), + * and execute @foreach_func on each. The signature of @foreach_func is + * that of #GtkAccelMapForeach, the @changed parameter indicates whether + * this accelerator was changed during runtime (thus, would need + * saving during an accelerator map dump). + */ +void +gtk_accel_map_foreach (gpointer data, + GtkAccelMapForeach foreach_func) +{ + GSList *entries, *slist, *node; + + g_return_if_fail (foreach_func != NULL); + + entries = g_hash_table_slist_values (accel_entry_ht); + entries = g_slist_sort (entries, accel_entry_compare); + + for (slist = entries; slist; slist = slist->next) + { + AccelEntry *entry = slist->data; + gboolean changed = entry->accel_key != entry->std_accel_key || entry->accel_mods != entry->std_accel_mods; + gboolean skip = FALSE; + + for (node = accel_filters; node; node = node->next) + if (g_pattern_match_string (node->data, entry->accel_path)) + { + skip = TRUE; + break; + } + + if (!skip) + foreach_func (data, entry->accel_path, entry->accel_key, entry->accel_mods, changed); + } + g_slist_free (entries); +} + +/** + * gtk_accel_map_foreach_unfiltered: + * @data: data to be passed into @foreach_func + * @foreach_func: (scope call): function to be executed for each accel + * map entry + * + * Loops over all entries in the accelerator map, and execute + * @foreach_func on each. The signature of @foreach_func is that of + * #GtkAccelMapForeach, the @changed parameter indicates whether + * this accelerator was changed during runtime (thus, would need + * saving during an accelerator map dump). + */ +void +gtk_accel_map_foreach_unfiltered (gpointer data, + GtkAccelMapForeach foreach_func) +{ + GSList *entries, *slist; + + g_return_if_fail (foreach_func != NULL); + + entries = g_hash_table_slist_values (accel_entry_ht); + for (slist = entries; slist; slist = slist->next) + { + AccelEntry *entry = slist->data; + gboolean changed = entry->accel_key != entry->std_accel_key || entry->accel_mods != entry->std_accel_mods; + + foreach_func (data, entry->accel_path, entry->accel_key, entry->accel_mods, changed); + } + g_slist_free (entries); +} + +/** + * gtk_accel_map_add_filter: + * @filter_pattern: a pattern (see #GPatternSpec) + * + * Adds a filter to the global list of accel path filters. + * + * Accel map entries whose accel path matches one of the filters + * are skipped by gtk_accel_map_foreach(). + * + * This function is intended for GTK+ modules that create their own + * menus, but don’t want them to be saved into the applications accelerator + * map dump. + */ +void +gtk_accel_map_add_filter (const gchar *filter_pattern) +{ + GPatternSpec *pspec; + GSList *slist; + + g_return_if_fail (filter_pattern != NULL); + + pspec = g_pattern_spec_new (filter_pattern); + for (slist = accel_filters; slist; slist = slist->next) + if (g_pattern_spec_equal (pspec, slist->data)) + { + g_pattern_spec_free (pspec); + return; + } + accel_filters = g_slist_prepend (accel_filters, pspec); +} + +void +_gtk_accel_map_add_group (const gchar *accel_path, + GtkAccelGroup *accel_group) +{ + AccelEntry *entry; + + g_return_if_fail (_gtk_accel_path_is_valid (accel_path)); + g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); + + entry = accel_path_lookup (accel_path); + if (!entry) + { + gtk_accel_map_add_entry (accel_path, 0, 0); + entry = accel_path_lookup (accel_path); + } + entry->groups = g_slist_prepend (entry->groups, accel_group); +} + +void +_gtk_accel_map_remove_group (const gchar *accel_path, + GtkAccelGroup *accel_group) +{ + AccelEntry *entry; + + entry = accel_path_lookup (accel_path); + g_return_if_fail (entry != NULL); + g_return_if_fail (g_slist_find (entry->groups, accel_group)); + + entry->groups = g_slist_remove (entry->groups, accel_group); +} + + +/** + * gtk_accel_map_lock_path: + * @accel_path: a valid accelerator path + * + * Locks the given accelerator path. If the accelerator map doesn’t yet contain + * an entry for @accel_path, a new one is created. + * + * Locking an accelerator path prevents its accelerator from being changed + * during runtime. A locked accelerator path can be unlocked by + * gtk_accel_map_unlock_path(). Refer to gtk_accel_map_change_entry() + * for information about runtime accelerator changes. + * + * If called more than once, @accel_path remains locked until + * gtk_accel_map_unlock_path() has been called an equivalent number + * of times. + * + * Note that locking of individual accelerator paths is independent from + * locking the #GtkAccelGroup containing them. For runtime accelerator + * changes to be possible, both the accelerator path and its #GtkAccelGroup + * have to be unlocked. + **/ +void +gtk_accel_map_lock_path (const gchar *accel_path) +{ + AccelEntry *entry; + + g_return_if_fail (_gtk_accel_path_is_valid (accel_path)); + + entry = accel_path_lookup (accel_path); + + if (!entry) + { + gtk_accel_map_add_entry (accel_path, 0, 0); + entry = accel_path_lookup (accel_path); + } + + entry->lock_count += 1; +} + +/** + * gtk_accel_map_unlock_path: + * @accel_path: a valid accelerator path + * + * Undoes the last call to gtk_accel_map_lock_path() on this @accel_path. + * Refer to gtk_accel_map_lock_path() for information about accelerator path locking. + **/ +void +gtk_accel_map_unlock_path (const gchar *accel_path) +{ + AccelEntry *entry; + + g_return_if_fail (_gtk_accel_path_is_valid (accel_path)); + + entry = accel_path_lookup (accel_path); + + g_return_if_fail (entry != NULL && entry->lock_count > 0); + + entry->lock_count -= 1; +} + +G_DEFINE_TYPE (GtkAccelMap, gtk_accel_map, G_TYPE_OBJECT) + +static void +gtk_accel_map_class_init (GtkAccelMapClass *accel_map_class) +{ + /** + * GtkAccelMap::changed: + * @object: the global accel map object + * @accel_path: the path of the accelerator that changed + * @accel_key: the key value for the new accelerator + * @accel_mods: the modifier mask for the new accelerator + * + * Notifies of a change in the global accelerator map. + * The path is also used as the detail for the signal, + * so it is possible to connect to + * changed::`accel_path`. + */ + accel_map_signals[CHANGED] = g_signal_new (I_("changed"), + G_TYPE_FROM_CLASS (accel_map_class), + G_SIGNAL_DETAILED|G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _gtk_marshal_VOID__STRING_UINT_FLAGS, + G_TYPE_NONE, 3, + G_TYPE_STRING, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE); +} + +static void +gtk_accel_map_init (GtkAccelMap *map) +{ +} + +static GtkAccelMap *accel_map; + +/** + * gtk_accel_map_get: + * + * Gets the singleton global #GtkAccelMap object. This object + * is useful only for notification of changes to the accelerator + * map via the ::changed signal; it isn’t a parameter to the + * other accelerator map functions. + * + * Returns: (transfer none): the global #GtkAccelMap object + **/ +GtkAccelMap * +gtk_accel_map_get (void) +{ + if (!accel_map) + accel_map = g_object_new (GTK_TYPE_ACCEL_MAP, NULL); + + return accel_map; +} + +static void +do_accel_map_changed (AccelEntry *entry) +{ + if (accel_map) + g_signal_emit (accel_map, + accel_map_signals[CHANGED], + g_quark_from_string (entry->accel_path), + entry->accel_path, + entry->accel_key, + entry->accel_mods); +} + +gchar * +_gtk_accel_path_for_action (const gchar *action_name, + GVariant *parameter) +{ + GString *s; + + s = g_string_new ("<GAction>/"); + g_string_append (s, action_name); + if (parameter) + { + g_string_append_c (s, '/'); + g_variant_print_string (parameter, s, FALSE); + } + return g_string_free (s, FALSE); +} + diff --git a/gtk/gtkaccelmap.h b/gtk/gtkaccelmap.h new file mode 100644 index 0000000000..e04f251299 --- /dev/null +++ b/gtk/gtkaccelmap.h @@ -0,0 +1,109 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1998, 2001 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GTK_ACCEL_MAP_H__ +#define __GTK_ACCEL_MAP_H__ + + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#include <gtk/gtkaccelgroup.h> + +G_BEGIN_DECLS + +/* --- global GtkAccelMap object --- */ +#define GTK_TYPE_ACCEL_MAP (gtk_accel_map_get_type ()) +#define GTK_ACCEL_MAP(accel_map) (G_TYPE_CHECK_INSTANCE_CAST ((accel_map), GTK_TYPE_ACCEL_MAP, GtkAccelMap)) +#define GTK_ACCEL_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_ACCEL_MAP, GtkAccelMapClass)) +#define GTK_IS_ACCEL_MAP(accel_map) (G_TYPE_CHECK_INSTANCE_TYPE ((accel_map), GTK_TYPE_ACCEL_MAP)) +#define GTK_IS_ACCEL_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ACCEL_MAP)) +#define GTK_ACCEL_MAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_ACCEL_MAP, GtkAccelMapClass)) + +typedef struct _GtkAccelMap GtkAccelMap; +typedef struct _GtkAccelMapClass GtkAccelMapClass; + +/* --- notifier --- */ +/** + * GtkAccelMapForeach: + * @data: User data passed to gtk_accel_map_foreach() or + * gtk_accel_map_foreach_unfiltered() + * @accel_path: Accel path of the current accelerator + * @accel_key: Key of the current accelerator + * @accel_mods: Modifiers of the current accelerator + * @changed: Changed flag of the accelerator (if %TRUE, accelerator has changed + * during runtime and would need to be saved during an accelerator dump) + */ +typedef void (*GtkAccelMapForeach) (gpointer data, + const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean changed); + + +/* --- public API --- */ + +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_add_entry (const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods); +GDK_AVAILABLE_IN_ALL +gboolean gtk_accel_map_lookup_entry (const gchar *accel_path, + GtkAccelKey *key); +GDK_AVAILABLE_IN_ALL +gboolean gtk_accel_map_change_entry (const gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gboolean replace); +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_load (const gchar *file_name); +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_save (const gchar *file_name); +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_foreach (gpointer data, + GtkAccelMapForeach foreach_func); +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_load_fd (gint fd); +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_load_scanner (GScanner *scanner); +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_save_fd (gint fd); + +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_lock_path (const gchar *accel_path); +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_unlock_path (const gchar *accel_path); + +/* --- filter functions --- */ +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_add_filter (const gchar *filter_pattern); +GDK_AVAILABLE_IN_ALL +void gtk_accel_map_foreach_unfiltered (gpointer data, + GtkAccelMapForeach foreach_func); + +/* --- notification --- */ +GDK_AVAILABLE_IN_ALL +GType gtk_accel_map_get_type (void) G_GNUC_CONST; +GDK_AVAILABLE_IN_ALL +GtkAccelMap *gtk_accel_map_get (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkAccelMap, g_object_unref) + +G_END_DECLS + +#endif /* __GTK_ACCEL_MAP_H__ */ diff --git a/gtk/gtkaccelmapprivate.h b/gtk/gtkaccelmapprivate.h new file mode 100644 index 0000000000..8be1e5e74a --- /dev/null +++ b/gtk/gtkaccelmapprivate.h @@ -0,0 +1,39 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1998, 2001 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GTK_ACCEL_MAP_PRIVATE_H__ +#define __GTK_ACCEL_MAP_PRIVATE_H__ + + +#include <gtk/gtkaccelmap.h> + +G_BEGIN_DECLS + +void _gtk_accel_map_init (void); + +void _gtk_accel_map_add_group (const gchar *accel_path, + GtkAccelGroup *accel_group); +void _gtk_accel_map_remove_group (const gchar *accel_path, + GtkAccelGroup *accel_group); +gboolean _gtk_accel_path_is_valid (const gchar *accel_path); + +gchar * _gtk_accel_path_for_action (const gchar *action_name, + GVariant *parameter); + +G_END_DECLS + +#endif /* __GTK_ACCEL_MAP_PRIVATE_H__ */ diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c index efd2e3a55f..768915c807 100644 --- a/gtk/gtkapplication.c +++ b/gtk/gtkapplication.c @@ -37,6 +37,7 @@ #include "gtkmarshalers.h" #include "gtkmain.h" #include "gtkrecentmanager.h" +#include "gtkaccelmapprivate.h" #include "gtkicontheme.h" #include "gtkbuilder.h" #include "gtkshortcutswindow.h" diff --git a/gtk/gtkapplicationaccels.c b/gtk/gtkapplicationaccels.c index e4e60bfc4b..a27501facf 100644 --- a/gtk/gtkapplicationaccels.c +++ b/gtk/gtkapplicationaccels.c @@ -22,28 +22,134 @@ #include "config.h" #include "gtkapplicationaccelsprivate.h" - #include "gtkactionmuxerprivate.h" -#include "gtkshortcut.h" -#include "gtkshortcutaction.h" -#include "gtkshortcuttrigger.h" + +#include <string.h> + +typedef struct +{ + guint key; + GdkModifierType modifier; +} AccelKey; struct _GtkApplicationAccels { GObject parent; - GListModel *shortcuts; + GHashTable *action_to_accels; + GHashTable *accel_to_actions; }; G_DEFINE_TYPE (GtkApplicationAccels, gtk_application_accels, G_TYPE_OBJECT) +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 +add_entry (GtkApplicationAccels *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_renew (const gchar *, old, n + 1 + 1); + new[n] = action_and_target; + new[n + 1] = NULL; + + g_hash_table_insert (accels->accel_to_actions, accel_key_copy (key), new); +} + +static void +remove_entry (GtkApplicationAccels *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 gtk_application_accels_finalize (GObject *object) { GtkApplicationAccels *accels = GTK_APPLICATION_ACCELS (object); - g_list_store_remove_all (G_LIST_STORE (accels->shortcuts)); - g_object_unref (accels->shortcuts); + g_hash_table_unref (accels->accel_to_actions); + g_hash_table_unref (accels->action_to_accels); G_OBJECT_CLASS (gtk_application_accels_parent_class)->finalize (object); } @@ -59,7 +165,9 @@ gtk_application_accels_class_init (GtkApplicationAccelsClass *klass) static void gtk_application_accels_init (GtkApplicationAccels *accels) { - accels->shortcuts = G_LIST_MODEL (g_list_store_new (GTK_TYPE_SHORTCUT)); + 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); } GtkApplicationAccels * @@ -73,97 +181,54 @@ gtk_application_accels_set_accels_for_action (GtkApplicationAccels *accels, const gchar *detailed_action_name, const gchar * const *accelerators) { - gchar *action_name; - GVariant *target; - GtkShortcut *shortcut; - GtkShortcutTrigger *trigger = NULL; - GError *error = NULL; - guint i; - - if (!g_action_parse_detailed_name (detailed_action_name, &action_name, &target, &error)) - { - g_critical ("Error parsing action name: %s", error->message); - g_error_free (error); - return; - } - - /* remove the accelerator if it already exists */ - for (i = 0; i < g_list_model_get_n_items (accels->shortcuts); i++) - { - GtkShortcut *shortcut_i = g_list_model_get_item (accels->shortcuts, i); - GtkShortcutAction *action = gtk_shortcut_get_action (shortcut_i); - GVariant *args = gtk_shortcut_get_arguments (shortcut_i); - - if (gtk_shortcut_action_get_action_type (action) != GTK_SHORTCUT_ACTION_ACTION || - !g_str_equal (gtk_action_action_get_name (action), action_name)) - continue; + gchar *action_and_target; + AccelKey *keys, *old_keys; + gint i, n; - if ((target == NULL && args != NULL) || - (target != NULL && (args == NULL || !g_variant_equal (target, args)))) - continue; - - g_list_store_remove (G_LIST_STORE (accels->shortcuts), i); - break; - } + action_and_target = gtk_normalise_detailed_action_name (detailed_action_name); - if (accelerators == NULL) - goto out; + n = accelerators ? g_strv_length ((gchar **) accelerators) : 0; - for (i = 0; accelerators[i]; i++) + if (n > 0) { - GtkShortcutTrigger *new_trigger; - guint key, modifier; + keys = g_new0 (AccelKey, n + 1); - if (!gtk_accelerator_parse (accelerators[i], &key, &modifier)) + for (i = 0; i < n; i++) { - g_critical ("Unable to parse accelerator '%s': ignored request to install accelerators", - accelerators[i]); - if (trigger) - gtk_shortcut_trigger_unref (trigger); - goto out;; + 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 (action_and_target); + g_free (keys); + return; + } } - new_trigger = gtk_keyval_trigger_new (key, modifier); - if (trigger) - trigger = gtk_alternative_trigger_new (trigger, new_trigger); - else - trigger = new_trigger; } - if (trigger == NULL) - goto out; - - shortcut = gtk_shortcut_new (trigger, gtk_action_action_new (action_name)); - gtk_shortcut_set_arguments (shortcut, target); - g_list_store_append (G_LIST_STORE (accels->shortcuts), shortcut); - g_object_unref (shortcut); - -out: - g_free (action_name); - if (target) - g_variant_unref (target); -} + else + keys = NULL; -static void -append_accelerators (GPtrArray *accels, - GtkShortcutTrigger *trigger) -{ - switch (gtk_shortcut_trigger_get_trigger_type (trigger)) + old_keys = g_hash_table_lookup (accels->action_to_accels, action_and_target); + if (old_keys) { - case GTK_SHORTCUT_TRIGGER_KEYVAL: - g_ptr_array_add (accels, - gtk_accelerator_name (gtk_keyval_trigger_get_keyval (trigger), - gtk_keyval_trigger_get_modifiers (trigger))); - return; + /* We need to remove accel entries from existing keys */ + for (i = 0; old_keys[i].key; i++) + remove_entry (accels, &old_keys[i], action_and_target); + } - case GTK_SHORTCUT_TRIGGER_ALTERNATIVE: - append_accelerators (accels, gtk_alternative_trigger_get_first (trigger)); - append_accelerators (accels, gtk_alternative_trigger_get_second (trigger)); - return; + if (keys) + { + g_hash_table_replace (accels->action_to_accels, action_and_target, keys); - case GTK_SHORTCUT_TRIGGER_MNEMONIC: - case GTK_SHORTCUT_TRIGGER_NEVER: - default: - /* not an accelerator */ - return; + for (i = 0; i < n; i++) + add_entry (accels, &keys[i], action_and_target); + } + else + { + g_hash_table_remove (accels->action_to_accels, action_and_target); + g_free (action_and_target); } } @@ -171,137 +236,223 @@ gchar ** gtk_application_accels_get_accels_for_action (GtkApplicationAccels *accels, const gchar *detailed_action_name) { - GPtrArray *result; - char *action_name; - GVariant *target; - GError *error = NULL; - guint i; + gchar *action_and_target; + AccelKey *keys; + gchar **result; + gint n, i = 0; - result = g_ptr_array_new (); + action_and_target = gtk_normalise_detailed_action_name (detailed_action_name); - if (!g_action_parse_detailed_name (detailed_action_name, &action_name, &target, &error)) + keys = g_hash_table_lookup (accels->action_to_accels, action_and_target); + if (!keys) { - g_critical ("Error parsing action name: %s", error->message); - g_error_free (error); - g_ptr_array_add (result, NULL); - return (gchar **) g_ptr_array_free (result, FALSE); + g_free (action_and_target); + return g_new0 (gchar *, 0 + 1); } - for (i = 0; i < g_list_model_get_n_items (accels->shortcuts); i++) - { - GtkShortcut *shortcut = g_list_model_get_item (accels->shortcuts, i); - GtkShortcutAction *action = gtk_shortcut_get_action (shortcut); - GVariant *args = gtk_shortcut_get_arguments (shortcut); + for (n = 0; keys[n].key; n++) + ; - if (gtk_shortcut_action_get_action_type (action) != GTK_SHORTCUT_ACTION_ACTION || - !g_str_equal (gtk_action_action_get_name (action), action_name)) - continue; + result = g_new0 (gchar *, n + 1); - if ((target == NULL && args != NULL) || - (target != NULL && (args == NULL || !g_variant_equal (target, args)))) - continue; - - append_accelerators (result, gtk_shortcut_get_trigger (shortcut)); - break; - } + for (i = 0; i < n; i++) + result[i] = gtk_accelerator_name (keys[i].key, keys[i].modifier); - g_free (action_name); - if (target) - g_variant_unref (target); - g_ptr_array_add (result, NULL); - return (gchar **) g_ptr_array_free (result, FALSE); + g_free (action_and_target); + return result; } -static gboolean -trigger_matches_accel (GtkShortcutTrigger *trigger, - guint keyval, - GdkModifierType modifiers) +gchar ** +gtk_application_accels_get_actions_for_accel (GtkApplicationAccels *accels, + const gchar *accel) { - switch (gtk_shortcut_trigger_get_trigger_type (trigger)) + const gchar * const *actions_and_targets; + gchar **detailed_actions; + AccelKey accel_key; + guint i, n; + + gtk_accelerator_parse (accel, &accel_key.key, &accel_key.modifier); + + if (accel_key.key == 0) { - case GTK_SHORTCUT_TRIGGER_KEYVAL: - return gtk_keyval_trigger_get_keyval (trigger) == keyval - && gtk_keyval_trigger_get_modifiers (trigger) == modifiers; - - case GTK_SHORTCUT_TRIGGER_ALTERNATIVE: - return trigger_matches_accel (gtk_alternative_trigger_get_first (trigger), keyval, modifiers) - || trigger_matches_accel (gtk_alternative_trigger_get_second (trigger), keyval, modifiers); - - case GTK_SHORTCUT_TRIGGER_MNEMONIC: - case GTK_SHORTCUT_TRIGGER_NEVER: - default: - return FALSE; + g_critical ("invalid accelerator string '%s'", accel); + g_return_val_if_fail (accel_key.key != 0, NULL); } -} -static char * -get_detailed_name_for_shortcut (GtkShortcut *shortcut) -{ - GtkShortcutAction *action = gtk_shortcut_get_action (shortcut); + actions_and_targets = g_hash_table_lookup (accels->accel_to_actions, &accel_key); + n = actions_and_targets ? g_strv_length ((gchar **) actions_and_targets) : 0; - if (gtk_shortcut_action_get_action_type (action) != GTK_SHORTCUT_ACTION_ACTION) - return NULL; + detailed_actions = g_new0 (gchar *, n + 1); - return g_action_print_detailed_name (gtk_action_action_get_name (action), gtk_shortcut_get_arguments (shortcut)); + for (i = 0; i < n; i++) + { + const gchar *action_and_target = actions_and_targets[i]; + const gchar *sep; + GVariant *target; + + sep = strrchr (action_and_target, '|'); + target = g_variant_parse (NULL, action_and_target, sep, NULL, NULL); + detailed_actions[i] = g_action_print_detailed_name (sep + 1, target); + if (target) + g_variant_unref (target); + } + + detailed_actions[n] = NULL; + + return detailed_actions; } gchar ** -gtk_application_accels_get_actions_for_accel (GtkApplicationAccels *accels, - const gchar *accel) +gtk_application_accels_list_action_descriptions (GtkApplicationAccels *accels) { - GPtrArray *result; - guint key, modifiers; - guint i; + GHashTableIter iter; + gchar **result; + gint n, i = 0; + gpointer key; - if (!gtk_accelerator_parse (accel, &key, &modifiers)) - { - g_critical ("invalid accelerator string '%s'", accel); - return NULL; - } + n = g_hash_table_size (accels->action_to_accels); + result = g_new (gchar *, n + 1); - result = g_ptr_array_new (); - - for (i = 0; i < g_list_model_get_n_items (accels->shortcuts); i++) + g_hash_table_iter_init (&iter, accels->action_to_accels); + while (g_hash_table_iter_next (&iter, &key, NULL)) { - GtkShortcut *shortcut = g_list_model_get_item (accels->shortcuts, i); - char *detailed_name; - - if (!trigger_matches_accel (gtk_shortcut_get_trigger (shortcut), key, modifiers)) - continue; - - detailed_name = get_detailed_name_for_shortcut (shortcut); - if (detailed_name) - g_ptr_array_add (result, detailed_name); + 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; - g_ptr_array_add (result, NULL); - return (gchar **) g_ptr_array_free (result, FALSE); + return result; } -gchar ** -gtk_application_accels_list_action_descriptions (GtkApplicationAccels *accels) +void +gtk_application_accels_foreach_key (GtkApplicationAccels *accels, + GtkWindow *window, + GtkWindowKeysForeachFunc callback, + gpointer user_data) { - GPtrArray *result; - guint i; + GHashTableIter iter; + gpointer key; - result = g_ptr_array_new (); - - for (i = 0; i < g_list_model_get_n_items (accels->shortcuts); i++) + g_hash_table_iter_init (&iter, accels->accel_to_actions); + while (g_hash_table_iter_next (&iter, &key, NULL)) { - GtkShortcut *shortcut = g_list_model_get_item (accels->shortcuts, i); - char *detailed_name; + AccelKey *accel_key = key; - detailed_name = get_detailed_name_for_shortcut (shortcut); - if (detailed_name) - g_ptr_array_add (result, detailed_name); + (* callback) (window, accel_key->key, accel_key->modifier, FALSE, user_data); } - - g_ptr_array_add (result, NULL); - return (gchar **) g_ptr_array_free (result, FALSE); } -GListModel * -gtk_application_accels_get_shortcuts (GtkApplicationAccels *accels) +gboolean +gtk_application_accels_activate (GtkApplicationAccels *accels, + GActionGroup *action_group, + guint key, + GdkModifierType modifier) { - return accels->shortcuts; + 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, ¶meter_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; } diff --git a/gtk/gtkapplicationaccelsprivate.h b/gtk/gtkapplicationaccelsprivate.h index fbde7da368..b126401a78 100644 --- a/gtk/gtkapplicationaccelsprivate.h +++ b/gtk/gtkapplicationaccelsprivate.h @@ -47,7 +47,15 @@ gchar ** gtk_application_accels_get_actions_for_accel (GtkApplicat gchar ** gtk_application_accels_list_action_descriptions (GtkApplicationAccels *accels); -GListModel * gtk_application_accels_get_shortcuts (GtkApplicationAccels *accels); +void gtk_application_accels_foreach_key (GtkApplicationAccels *accels, + GtkWindow *window, + GtkWindowKeysForeachFunc callback, + gpointer user_data); + +gboolean gtk_application_accels_activate (GtkApplicationAccels *accels, + GActionGroup *action_group, + guint key, + GdkModifierType modifier); G_END_DECLS diff --git a/gtk/gtkassistant.c b/gtk/gtkassistant.c index b0b46c1dd2..4161e2d1b4 100644 --- a/gtk/gtkassistant.c +++ b/gtk/gtkassistant.c @@ -65,6 +65,7 @@ #include "gtkassistant.h" +#include "gtkbindings.h" #include "gtkbox.h" #include "gtkbuildable.h" #include "gtkbutton.h" @@ -509,6 +510,7 @@ gtk_assistant_class_init (GtkAssistantClass *class) GtkWidgetClass *widget_class; GtkContainerClass *container_class; GtkWindowClass *window_class; + GtkBindingSet *binding_set; gobject_class = (GObjectClass *) class; widget_class = (GtkWidgetClass *) class; @@ -617,10 +619,8 @@ gtk_assistant_class_init (GtkAssistantClass *class) NULL, G_TYPE_NONE, 0); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Escape, 0, - "escape", - NULL); + binding_set = gtk_binding_set_by_class (class); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "escape", 0); /** * GtkAssistant:use-header-bar: diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c new file mode 100644 index 0000000000..9f3d879d04 --- /dev/null +++ b/gtk/gtkbindings.c @@ -0,0 +1,1793 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * GtkBindingSet: Keybinding manager for GObjects. + * Copyright (C) 1998 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" +#include <string.h> +#include <stdarg.h> + +#include "gtkbindingsprivate.h" +#include "gtkkeyhash.h" +#include "gtkstylecontext.h" +#include "gtkwidget.h" +#include "gtkintl.h" + +/** + * SECTION:gtkbindings + * @Title: Bindings + * @Short_description: Key bindings for individual widgets + * @See_also: Keyboard Accelerators, Mnemonics, #GtkCssProvider + * + * #GtkBindingSet provides a mechanism for configuring GTK+ key bindings + * through CSS files. This eases key binding adjustments for application + * developers as well as users and provides GTK+ users or administrators + * with high key binding configurability which requires no application + * or toolkit side changes. + * + * In order for bindings to work in a custom widget implementation, the + * widget’s #GtkWidget:can-focus and #GtkWidget:has-focus properties + * must both be true. For example, by calling gtk_widget_set_can_focus() + * in the widget’s initialisation function; and by calling + * gtk_widget_grab_focus() when the widget is clicked. + */ + +/* --- defines --- */ +#define BINDING_MOD_MASK() (gtk_accelerator_get_default_mod_mask () | GDK_RELEASE_MASK) + + +/* --- structures --- */ +typedef enum { + GTK_BINDING_TOKEN_BIND, + GTK_BINDING_TOKEN_UNBIND +} GtkBindingTokens; + +typedef struct _GtkBindingEntry GtkBindingEntry; +typedef struct _GtkBindingSignal GtkBindingSignal; +typedef struct _GtkBindingArg GtkBindingArg; +typedef struct _GtkBindingSignalSignal GtkBindingSignalSignal; +typedef struct _GtkBindingSignalAction GtkBindingSignalAction; +typedef struct _GtkBindingSignalCallback GtkBindingSignalCallback; + +/** + * GtkBindingSet: + * @set_name: unique name of this binding set + * @priority: unused + * @entries: the key binding entries in this binding set + * @current: implementation detail + * + * A binding set maintains a list of activatable key bindings. + * A single binding set can match multiple types of widgets. + * Similar to style contexts, can be matched by any information contained + * in a widgets #GtkWidgetPath. When a binding within a set is matched upon + * activation, an action signal is emitted on the target widget to carry out + * the actual activation. + */ +struct _GtkBindingSet +{ + gchar *set_name; + gint priority; + GtkBindingEntry *entries; + GtkBindingEntry *current; +}; + +/** + * GtkBindingEntry: + * @keyval: key value to match + * @modifiers: key modifiers to match + * @binding_set: binding set this entry belongs to + * @destroyed: implementation detail + * @in_emission: implementation detail + * @marks_unbound: implementation detail + * @set_next: linked list of entries maintained by binding set + * @hash_next: implementation detail + * @signals: action signals of this entry + * + * Each key binding element of a binding sets binding list is + * represented by a GtkBindingEntry. + */ +struct _GtkBindingEntry +{ + /* key portion */ + guint keyval; + GdkModifierType modifiers; + + GtkBindingSet *binding_set; + guint destroyed : 1; + guint in_emission : 1; + guint marks_unbound : 1; + GtkBindingEntry *set_next; + GtkBindingEntry *hash_next; + GtkBindingSignal *signals; +}; + +/** + * GtkBindingArg: + * @arg_type: implementation detail + * + * A #GtkBindingArg holds the data associated with + * an argument for a key binding signal emission as + * stored in #GtkBindingSignal. + */ +struct _GtkBindingArg +{ + GType arg_type; + union { + glong long_data; + gdouble double_data; + gchar *string_data; + } d; +}; + +typedef enum +{ + GTK_BINDING_SIGNAL, + GTK_BINDING_ACTION, + GTK_BINDING_CALLBACK +} GtkBindingActionType; + +/** + * GtkBindingSignal: + * @next: implementation detail + * @action_type: Actual type of the action + * + * A GtkBindingSignal stores the necessary information to + * activate a widget in response to a key press via a signal + * emission. + */ +struct _GtkBindingSignal +{ + GtkBindingSignal *next; + GtkBindingActionType action_type; +}; + +struct _GtkBindingSignalSignal +{ + GtkBindingSignal parent; + const gchar *signal_name; + guint n_args; + GtkBindingArg *args; +}; + +struct _GtkBindingSignalAction +{ + GtkBindingSignal parent; + const gchar *action_name; + GVariant *variant; +}; + +struct _GtkBindingSignalCallback +{ + GtkBindingSignal parent; + GtkBindingCallback callback; + GVariant *args; + gpointer user_data; + GDestroyNotify user_destroy; +}; + +/* --- variables --- */ +static GHashTable *binding_entry_hash_table = NULL; +static GSList *binding_key_hashes = NULL; +static GSList *binding_set_list = NULL; +static const gchar key_class_binding_set[] = "gtk-class-binding-set"; +static GQuark key_id_class_binding_set = 0; + + +/* --- functions --- */ + +static GtkBindingSignal* +binding_signal_new_signal (const gchar *signal_name, + guint n_args) +{ + GtkBindingSignalSignal *signal; + + signal = (GtkBindingSignalSignal *) g_slice_alloc0 (sizeof (GtkBindingSignalSignal) + n_args * sizeof (GtkBindingArg)); + signal->parent.next = NULL; + signal->parent.action_type = GTK_BINDING_SIGNAL; + signal->signal_name = g_intern_string (signal_name); + signal->n_args = n_args; + signal->args = (GtkBindingArg *)(signal + 1); + + return &signal->parent; +} + +static GtkBindingSignal* +binding_signal_new_action (const gchar *action_name, + GVariant *variant) +{ + GtkBindingSignalAction *signal; + + signal = g_slice_new0 (GtkBindingSignalAction); + signal->parent.next = NULL; + signal->parent.action_type = GTK_BINDING_ACTION; + signal->action_name = g_intern_string (action_name); + signal->variant = variant; + if (variant) + g_variant_ref_sink (variant); + + return &signal->parent; +} + +static GtkBindingSignal * +binding_signal_new_callback (GtkBindingCallback callback, + GVariant *args, + gpointer user_data, + GDestroyNotify user_destroy) +{ + GtkBindingSignalCallback *signal; + + signal = g_slice_new0 (GtkBindingSignalCallback); + signal->parent.next = NULL; + signal->parent.action_type = GTK_BINDING_CALLBACK; + signal->callback = callback; + signal->args = args; + if (args) + g_variant_ref_sink (args); + signal->user_data = user_data; + signal->user_destroy = user_destroy; + + return &signal->parent; +} + +static void +binding_signal_free (GtkBindingSignal *signal) +{ + guint i; + + switch (signal->action_type) + { + case GTK_BINDING_SIGNAL: + { + GtkBindingSignalSignal *sig = (GtkBindingSignalSignal *) signal; + for (i = 0; i < sig->n_args; i++) + { + if (G_TYPE_FUNDAMENTAL (sig->args[i].arg_type) == G_TYPE_STRING) + g_free (sig->args[i].d.string_data); + } + g_slice_free1 (sizeof (GtkBindingSignalSignal) + sig->n_args * sizeof (GtkBindingArg), sig); + } + break; + + case GTK_BINDING_ACTION: + { + GtkBindingSignalAction *sig = (GtkBindingSignalAction *) signal; + g_clear_pointer (&sig->variant, g_variant_unref); + g_slice_free (GtkBindingSignalAction, sig); + } + break; + + case GTK_BINDING_CALLBACK: + { + GtkBindingSignalCallback *sig = (GtkBindingSignalCallback *) signal; + if (sig->user_destroy) + sig->user_destroy (sig->user_data); + g_slice_free (GtkBindingSignalCallback, sig); + } + break; + + default: + g_assert_not_reached (); + break; + } +} + +static guint +binding_entry_hash (gconstpointer key) +{ + register const GtkBindingEntry *e = key; + register guint h; + + h = e->keyval; + h ^= e->modifiers; + + return h; +} + +static gint +binding_entries_compare (gconstpointer a, + gconstpointer b) +{ + register const GtkBindingEntry *ea = a; + register const GtkBindingEntry *eb = b; + + return (ea->keyval == eb->keyval && ea->modifiers == eb->modifiers); +} + +static void +binding_key_hash_insert_entry (GtkKeyHash *key_hash, + GtkBindingEntry *entry) +{ + guint keyval = entry->keyval; + + /* We store lowercased accelerators. To deal with this, if <Shift> + * was specified, uppercase. + */ + if (entry->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 & ~GDK_RELEASE_MASK, entry); +} + +static void +binding_key_hash_destroy (gpointer data) +{ + GtkKeyHash *key_hash = data; + + binding_key_hashes = g_slist_remove (binding_key_hashes, key_hash); + _gtk_key_hash_free (key_hash); +} + +static void +insert_entries_into_key_hash (gpointer key, + gpointer value, + gpointer data) +{ + GtkKeyHash *key_hash = data; + GtkBindingEntry *entry = value; + + for (; entry; entry = entry->hash_next) + binding_key_hash_insert_entry (key_hash, entry); +} + +static GtkKeyHash * +binding_key_hash_for_keymap (GdkKeymap *keymap) +{ + static GQuark key_hash_quark = 0; + GtkKeyHash *key_hash; + + if (!key_hash_quark) + key_hash_quark = g_quark_from_static_string ("gtk-binding-key-hash"); + + key_hash = g_object_get_qdata (G_OBJECT (keymap), key_hash_quark); + + if (!key_hash) + { + key_hash = _gtk_key_hash_new (keymap, NULL); + g_object_set_qdata_full (G_OBJECT (keymap), key_hash_quark, key_hash, binding_key_hash_destroy); + + if (binding_entry_hash_table) + g_hash_table_foreach (binding_entry_hash_table, + insert_entries_into_key_hash, + key_hash); + + binding_key_hashes = g_slist_prepend (binding_key_hashes, key_hash); + } + + return key_hash; +} + + +static GtkBindingEntry* +binding_entry_new (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) +{ + GSList *tmp_list; + GtkBindingEntry *entry; + + if (!binding_entry_hash_table) + binding_entry_hash_table = g_hash_table_new (binding_entry_hash, binding_entries_compare); + + entry = g_new (GtkBindingEntry, 1); + entry->keyval = keyval; + entry->modifiers = modifiers; + entry->binding_set = binding_set, + entry->destroyed = FALSE; + entry->in_emission = FALSE; + entry->marks_unbound = FALSE; + entry->signals = NULL; + + entry->set_next = binding_set->entries; + binding_set->entries = entry; + + entry->hash_next = g_hash_table_lookup (binding_entry_hash_table, entry); + if (entry->hash_next) + g_hash_table_remove (binding_entry_hash_table, entry->hash_next); + g_hash_table_insert (binding_entry_hash_table, entry, entry); + + for (tmp_list = binding_key_hashes; tmp_list; tmp_list = tmp_list->next) + { + GtkKeyHash *key_hash = tmp_list->data; + binding_key_hash_insert_entry (key_hash, entry); + } + + return entry; +} + +static void +binding_entry_free (GtkBindingEntry *entry) +{ + GtkBindingSignal *sig; + + g_assert (entry->set_next == NULL && + entry->hash_next == NULL && + entry->in_emission == FALSE && + entry->destroyed == TRUE); + + entry->destroyed = FALSE; + + sig = entry->signals; + while (sig) + { + GtkBindingSignal *prev; + + prev = sig; + sig = prev->next; + binding_signal_free (prev); + } + g_free (entry); +} + +static void +binding_entry_destroy (GtkBindingEntry *entry) +{ + GtkBindingEntry *o_entry; + register GtkBindingEntry *tmp; + GtkBindingEntry *begin; + register GtkBindingEntry *last; + GSList *tmp_list; + + /* unlink from binding set + */ + last = NULL; + tmp = entry->binding_set->entries; + while (tmp) + { + if (tmp == entry) + { + if (last) + last->set_next = entry->set_next; + else + entry->binding_set->entries = entry->set_next; + break; + } + last = tmp; + tmp = last->set_next; + } + entry->set_next = NULL; + + o_entry = g_hash_table_lookup (binding_entry_hash_table, entry); + begin = o_entry; + last = NULL; + tmp = begin; + while (tmp) + { + if (tmp == entry) + { + if (last) + last->hash_next = entry->hash_next; + else + begin = entry->hash_next; + break; + } + last = tmp; + tmp = last->hash_next; + } + entry->hash_next = NULL; + + if (!begin) + g_hash_table_remove (binding_entry_hash_table, entry); + else if (begin != o_entry) + { + g_hash_table_remove (binding_entry_hash_table, entry); + g_hash_table_insert (binding_entry_hash_table, begin, begin); + } + + for (tmp_list = binding_key_hashes; tmp_list; tmp_list = tmp_list->next) + { + GtkKeyHash *key_hash = tmp_list->data; + _gtk_key_hash_remove_entry (key_hash, entry); + } + + entry->destroyed = TRUE; + + if (!entry->in_emission) + binding_entry_free (entry); +} + +static GtkBindingEntry* +binding_ht_lookup_entry (GtkBindingSet *set, + guint keyval, + GdkModifierType modifiers) +{ + GtkBindingEntry lookup_entry = { 0 }; + GtkBindingEntry *entry; + + if (!binding_entry_hash_table) + return NULL; + + lookup_entry.keyval = keyval; + lookup_entry.modifiers = modifiers; + + entry = g_hash_table_lookup (binding_entry_hash_table, &lookup_entry); + for (; entry; entry = entry->hash_next) + if (entry->binding_set == set) + return entry; + + return NULL; +} + +static gboolean +binding_compose_params (GObject *object, + GtkBindingArg *args, + GSignalQuery *query, + GValue **params_p) +{ + GValue *params; + const GType *types; + guint i; + gboolean valid; + + params = g_new0 (GValue, query->n_params + 1); + *params_p = params; + + /* The instance we emit on is the first object in the array + */ + g_value_init (params, G_TYPE_OBJECT); + g_value_set_object (params, G_OBJECT (object)); + params++; + + types = query->param_types; + valid = TRUE; + for (i = 1; i < query->n_params + 1 && valid; i++) + { + GValue tmp_value = G_VALUE_INIT; + + g_value_init (params, *types); + + switch (G_TYPE_FUNDAMENTAL (args->arg_type)) + { + case G_TYPE_DOUBLE: + g_value_init (&tmp_value, G_TYPE_DOUBLE); + g_value_set_double (&tmp_value, args->d.double_data); + break; + case G_TYPE_LONG: + g_value_init (&tmp_value, G_TYPE_LONG); + g_value_set_long (&tmp_value, args->d.long_data); + break; + case G_TYPE_STRING: + /* gtk_rc_parse_flags/enum() has fancier parsing for this; we can't call + * that since we don't have a GParamSpec, so just do something simple + */ + if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_ENUM) + { + GEnumClass *class = G_ENUM_CLASS (g_type_class_ref (*types)); + GEnumValue *enum_value; + + valid = FALSE; + + enum_value = g_enum_get_value_by_name (class, args->d.string_data); + if (!enum_value) + enum_value = g_enum_get_value_by_nick (class, args->d.string_data); + + if (enum_value) + { + g_value_init (&tmp_value, *types); + g_value_set_enum (&tmp_value, enum_value->value); + valid = TRUE; + } + + g_type_class_unref (class); + } + /* This is just a hack for compatibility with GTK+-1.2 where a string + * could be used for a single flag value / without the support for multiple + * values in gtk_rc_parse_flags(), this isn't very useful. + */ + else if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_FLAGS) + { + GFlagsClass *class = G_FLAGS_CLASS (g_type_class_ref (*types)); + GFlagsValue *flags_value; + + valid = FALSE; + + flags_value = g_flags_get_value_by_name (class, args->d.string_data); + if (!flags_value) + flags_value = g_flags_get_value_by_nick (class, args->d.string_data); + if (flags_value) + { + g_value_init (&tmp_value, *types); + g_value_set_flags (&tmp_value, flags_value->value); + valid = TRUE; + } + + g_type_class_unref (class); + } + else + { + g_value_init (&tmp_value, G_TYPE_STRING); + g_value_set_static_string (&tmp_value, args->d.string_data); + } + break; + default: + valid = FALSE; + break; + } + + if (valid) + { + if (!g_value_transform (&tmp_value, params)) + valid = FALSE; + + g_value_unset (&tmp_value); + } + + types++; + params++; + args++; + } + + if (!valid) + { + guint j; + + for (j = 0; j < i; j++) + g_value_unset (&(*params_p)[j]); + + g_free (*params_p); + *params_p = NULL; + } + + return valid; +} + +static gboolean +binding_signal_activate_signal (GtkBindingSignalSignal *sig, + GObject *object) +{ + GSignalQuery query; + guint signal_id; + GValue *params = NULL; + GValue return_val = G_VALUE_INIT; + gboolean handled = FALSE; + + signal_id = g_signal_lookup (sig->signal_name, G_OBJECT_TYPE (object)); + if (!signal_id) + { + g_warning ("gtk_binding_entry_activate(): " + "could not find signal \"%s\" in the '%s' class ancestry", + sig->signal_name, + g_type_name (G_OBJECT_TYPE (object))); + return FALSE; + } + + g_signal_query (signal_id, &query); + if (query.n_params != sig->n_args || + (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) || + !binding_compose_params (object, sig->args, &query, ¶ms)) + { + g_warning ("gtk_binding_entry_activate(): " + "signature mismatch for signal \"%s\" in the '%s' class ancestry", + sig->signal_name, + g_type_name (G_OBJECT_TYPE (object))); + return FALSE; + } + else if (!(query.signal_flags & G_SIGNAL_ACTION)) + { + g_warning ("gtk_binding_entry_activate(): " + "signal \"%s\" in the '%s' class ancestry cannot be used for action emissions", + sig->signal_name, + g_type_name (G_OBJECT_TYPE (object))); + return FALSE; + } + + if (query.return_type == G_TYPE_BOOLEAN) + g_value_init (&return_val, G_TYPE_BOOLEAN); + + g_signal_emitv (params, signal_id, 0, &return_val); + + if (query.return_type == G_TYPE_BOOLEAN) + { + if (g_value_get_boolean (&return_val)) + handled = TRUE; + g_value_unset (&return_val); + } + else + handled = TRUE; + + if (params != NULL) + { + guint i; + + for (i = 0; i < query.n_params + 1; i++) + g_value_unset (¶ms[i]); + + g_free (params); + } + + return handled; +} + +static gboolean +binding_signal_activate_action (GtkBindingSignalAction *sig, + GObject *object) +{ + if (!GTK_IS_WIDGET (object)) + { + g_warning ("gtk_binding_entry_activate(): " + "actions must be emitted on GtkWidget subtypes, %s is not supported", + G_OBJECT_TYPE_NAME (object)); + return FALSE; + } + + if (!gtk_widget_activate_action_variant (GTK_WIDGET (object), sig->action_name, sig->variant)) + { + g_warning ("gtk_binding_entry_activate(): " + "action \"%s\" does not exist on class \"%s\"", + sig->action_name, + G_OBJECT_TYPE_NAME (object)); + return FALSE; + } + + return TRUE; +} + +static gboolean +binding_signal_activate_callback (GtkBindingSignalCallback *sig, + GObject *object) +{ + if (!GTK_IS_WIDGET (object)) + { + g_warning ("gtk_binding_entry_activate(): " + "callbacks must be run on GtkWidget subtypes, %s is not supported", + G_OBJECT_TYPE_NAME (object)); + return FALSE; + } + + sig->callback (GTK_WIDGET (object), sig->args, sig->user_data); + + return TRUE; +} + +static gboolean +gtk_binding_entry_activate (GtkBindingEntry *entry, + GObject *object) +{ + GtkBindingSignal *sig; + gboolean old_emission; + gboolean handled = FALSE; + + old_emission = entry->in_emission; + entry->in_emission = TRUE; + + g_object_ref (object); + + for (sig = entry->signals; sig; sig = sig->next) + { + switch (sig->action_type) + { + case GTK_BINDING_SIGNAL: + handled = binding_signal_activate_signal ((GtkBindingSignalSignal *) sig, object); + break; + + case GTK_BINDING_ACTION: + handled = binding_signal_activate_action ((GtkBindingSignalAction *) sig, object); + break; + + case GTK_BINDING_CALLBACK: + handled = binding_signal_activate_callback ((GtkBindingSignalCallback *) sig, object); + break; + + default: + g_assert_not_reached (); + break; + } + + if (entry->destroyed) + break; + } + + g_object_unref (object); + + entry->in_emission = old_emission; + if (entry->destroyed && !entry->in_emission) + binding_entry_free (entry); + + return handled; +} + +/** + * gtk_binding_set_new: (skip) + * @set_name: unique name of this binding set + * + * GTK+ maintains a global list of binding sets. Each binding set has + * a unique name which needs to be specified upon creation. + * + * Returns: (transfer none): new binding set + */ +GtkBindingSet* +gtk_binding_set_new (const gchar *set_name) +{ + GtkBindingSet *binding_set; + + g_return_val_if_fail (set_name != NULL, NULL); + + binding_set = g_new (GtkBindingSet, 1); + binding_set->set_name = (gchar *) g_intern_string (set_name); + binding_set->entries = NULL; + binding_set->current = NULL; + + binding_set_list = g_slist_prepend (binding_set_list, binding_set); + + return binding_set; +} + +/** + * gtk_binding_set_by_class: (skip) + * @object_class: a valid #GObject class + * + * This function returns the binding set named after the type name of + * the passed in class structure. New binding sets are created on + * demand by this function. + * + * Returns: (transfer none): the binding set corresponding to + * @object_class + */ +GtkBindingSet* +gtk_binding_set_by_class (gpointer object_class) +{ + GObjectClass *class = object_class; + GtkBindingSet* binding_set; + + g_return_val_if_fail (G_IS_OBJECT_CLASS (class), NULL); + + if (!key_id_class_binding_set) + key_id_class_binding_set = g_quark_from_static_string (key_class_binding_set); + + binding_set = g_dataset_id_get_data (class, key_id_class_binding_set); + + if (binding_set) + return binding_set; + + binding_set = gtk_binding_set_new (g_type_name (G_OBJECT_CLASS_TYPE (class))); + g_dataset_id_set_data (class, key_id_class_binding_set, binding_set); + + return binding_set; +} + +static GtkBindingSet* +gtk_binding_set_find_interned (const gchar *set_name) +{ + GSList *slist; + + for (slist = binding_set_list; slist; slist = slist->next) + { + GtkBindingSet *binding_set; + + binding_set = slist->data; + if (binding_set->set_name == set_name) + return binding_set; + } + + return NULL; +} + +/** + * gtk_binding_set_find: + * @set_name: unique binding set name + * + * Find a binding set by its globally unique name. + * + * The @set_name can either be a name used for gtk_binding_set_new() + * or the type name of a class used in gtk_binding_set_by_class(). + * + * Returns: (nullable) (transfer none): %NULL or the specified binding set + */ +GtkBindingSet* +gtk_binding_set_find (const gchar *set_name) +{ + g_return_val_if_fail (set_name != NULL, NULL); + + return gtk_binding_set_find_interned (g_intern_string (set_name)); +} + +/** + * gtk_binding_set_activate: + * @binding_set: a #GtkBindingSet set to activate + * @keyval: key value of the binding + * @modifiers: key modifier of the binding + * @object: object to activate when binding found + * + * Find a key binding matching @keyval and @modifiers within + * @binding_set and activate the binding on @object. + * + * Returns: %TRUE if a binding was found and activated + */ +gboolean +gtk_binding_set_activate (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + GObject *object) +{ + GtkBindingEntry *entry; + + g_return_val_if_fail (binding_set != NULL, FALSE); + g_return_val_if_fail (G_IS_OBJECT (object), FALSE); + + keyval = gdk_keyval_to_lower (keyval); + modifiers = modifiers & BINDING_MOD_MASK (); + + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + if (entry) + return gtk_binding_entry_activate (entry, object); + + return FALSE; +} + +static void +gtk_binding_entry_clear_internal (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) +{ + GtkBindingEntry *entry; + + keyval = gdk_keyval_to_lower (keyval); + modifiers = modifiers & BINDING_MOD_MASK (); + + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + if (entry) + binding_entry_destroy (entry); + + entry = binding_entry_new (binding_set, keyval, modifiers); +} + +/** + * gtk_binding_entry_skip: + * @binding_set: a #GtkBindingSet to skip an entry of + * @keyval: key value of binding to skip + * @modifiers: key modifier of binding to skip + * + * Install a binding on @binding_set which causes key lookups + * to be aborted, to prevent bindings from lower priority sets + * to be activated. + */ +void +gtk_binding_entry_skip (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) +{ + GtkBindingEntry *entry; + + g_return_if_fail (binding_set != NULL); + + keyval = gdk_keyval_to_lower (keyval); + modifiers = modifiers & BINDING_MOD_MASK (); + + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + if (entry) + binding_entry_destroy (entry); + + entry = binding_entry_new (binding_set, keyval, modifiers); + entry->marks_unbound = TRUE; +} + +/** + * gtk_binding_entry_remove: + * @binding_set: a #GtkBindingSet to remove an entry of + * @keyval: key value of binding to remove + * @modifiers: key modifier of binding to remove + * + * Remove a binding previously installed via + * gtk_binding_entry_add_signal() on @binding_set. + */ +void +gtk_binding_entry_remove (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) +{ + GtkBindingEntry *entry; + + g_return_if_fail (binding_set != NULL); + + keyval = gdk_keyval_to_lower (keyval); + modifiers = modifiers & BINDING_MOD_MASK (); + + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + if (entry) + binding_entry_destroy (entry); +} + +static void +gtk_binding_entry_add_binding_signal (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + GtkBindingSignal *signal) +{ + GtkBindingEntry *entry; + GtkBindingSignal **signal_p; + + keyval = gdk_keyval_to_lower (keyval); + modifiers = modifiers & BINDING_MOD_MASK (); + + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + if (!entry) + { + gtk_binding_entry_clear_internal (binding_set, keyval, modifiers); + entry = binding_ht_lookup_entry (binding_set, keyval, modifiers); + } + signal_p = &entry->signals; + while (*signal_p) + signal_p = &(*signal_p)->next; + *signal_p = signal; +} + +/* + * gtk_binding_entry_add_signall: + * @binding_set: a #GtkBindingSet to add a signal to + * @keyval: key value + * @modifiers: key modifier + * @signal_name: signal name to be bound + * @binding_args: (transfer none) (element-type GtkBindingArg): + * list of #GtkBindingArg signal arguments + * + * Override or install a new key binding for @keyval with @modifiers on + * @binding_set. + */ +static void +gtk_binding_entry_add_signall (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + const gchar *signal_name, + GSList *binding_args) +{ + GtkBindingSignal *signal; + GSList *slist; + guint n = 0; + GtkBindingArg *arg; + + g_return_if_fail (binding_set != NULL); + g_return_if_fail (signal_name != NULL); + + signal = binding_signal_new_signal (signal_name, g_slist_length (binding_args)); + + arg = ((GtkBindingSignalSignal *) signal)->args; + for (slist = binding_args; slist; slist = slist->next) + { + GtkBindingArg *tmp_arg; + + tmp_arg = slist->data; + if (!tmp_arg) + { + g_warning ("gtk_binding_entry_add_signall(): arg[%u] is 'NULL'", n); + binding_signal_free (signal); + return; + } + switch (G_TYPE_FUNDAMENTAL (tmp_arg->arg_type)) + { + case G_TYPE_LONG: + arg->arg_type = G_TYPE_LONG; + arg->d.long_data = tmp_arg->d.long_data; + break; + case G_TYPE_DOUBLE: + arg->arg_type = G_TYPE_DOUBLE; + arg->d.double_data = tmp_arg->d.double_data; + break; + case G_TYPE_STRING: + arg->arg_type = G_TYPE_STRING; + arg->d.string_data = g_strdup (tmp_arg->d.string_data); + if (!arg->d.string_data) + { + g_warning ("gtk_binding_entry_add_signall(): value of 'string' arg[%u] is 'NULL'", n); + binding_signal_free (signal); + return; + } + break; + default: + g_warning ("gtk_binding_entry_add_signall(): unsupported type '%s' for arg[%u]", + g_type_name (arg->arg_type), n); + binding_signal_free (signal); + return; + } + arg++; + n++; + } + + gtk_binding_entry_add_binding_signal (binding_set, keyval, modifiers, signal); +} + +/** + * gtk_binding_entry_add_signal: + * @binding_set: a #GtkBindingSet to install an entry for + * @keyval: key value of binding to install + * @modifiers: key modifier of binding to install + * @signal_name: signal to execute upon activation + * @n_args: number of arguments to @signal_name + * @...: arguments to @signal_name + * + * Override or install a new key binding for @keyval with @modifiers on + * @binding_set. When the binding is activated, @signal_name will be + * emitted on the target widget, with @n_args @Varargs used as + * arguments. + * + * Each argument to the signal must be passed as a pair of varargs: the + * #GType of the argument, followed by the argument value (which must + * be of the given type). There must be @n_args pairs in total. + * + * ## Adding a Key Binding + * + * |[<!-- language="C" --> + * GtkBindingSet *binding_set; + * GdkModifierType modmask = GDK_CONTROL_MASK; + * int count = 1; + * gtk_binding_entry_add_signal (binding_set, + * GDK_KEY_space, + * modmask, + * "move-cursor", 2, + * GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_PAGES, + * G_TYPE_INT, count, + * G_TYPE_BOOLEAN, FALSE); + * ]| + */ +void +gtk_binding_entry_add_signal (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + const gchar *signal_name, + guint n_args, + ...) +{ + GSList *slist, *free_slist; + va_list args; + guint i; + + g_return_if_fail (binding_set != NULL); + g_return_if_fail (signal_name != NULL); + + va_start (args, n_args); + slist = NULL; + for (i = 0; i < n_args; i++) + { + GtkBindingArg *arg; + + arg = g_slice_new0 (GtkBindingArg); + slist = g_slist_prepend (slist, arg); + + arg->arg_type = va_arg (args, GType); + switch (G_TYPE_FUNDAMENTAL (arg->arg_type)) + { + case G_TYPE_CHAR: + case G_TYPE_UCHAR: + case G_TYPE_INT: + case G_TYPE_UINT: + case G_TYPE_BOOLEAN: + case G_TYPE_ENUM: + case G_TYPE_FLAGS: + arg->arg_type = G_TYPE_LONG; + arg->d.long_data = va_arg (args, gint); + break; + case G_TYPE_LONG: + case G_TYPE_ULONG: + arg->arg_type = G_TYPE_LONG; + arg->d.long_data = va_arg (args, glong); + break; + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + arg->arg_type = G_TYPE_DOUBLE; + arg->d.double_data = va_arg (args, gdouble); + break; + case G_TYPE_STRING: + arg->arg_type = G_TYPE_STRING; + arg->d.string_data = va_arg (args, gchar*); + if (!arg->d.string_data) + { + g_warning ("gtk_binding_entry_add_signal(): type '%s' arg[%u] is 'NULL'", + g_type_name (arg->arg_type), + i); + i += n_args + 1; + } + break; + default: + g_warning ("gtk_binding_entry_add_signal(): unsupported type '%s' for arg[%u]", + g_type_name (arg->arg_type), i); + i += n_args + 1; + break; + } + } + va_end (args); + + if (i == n_args || i == 0) + { + slist = g_slist_reverse (slist); + gtk_binding_entry_add_signall (binding_set, keyval, modifiers, signal_name, slist); + } + + free_slist = slist; + while (slist) + { + g_slice_free (GtkBindingArg, slist->data); + slist = slist->next; + } + g_slist_free (free_slist); +} + +/** + * gtk_binding_entry_add_action_variant: + * @binding_set: a #GtkBindingSet to install an entry for + * @keyval: key value of binding to install + * @modifiers: key modifier of binding to install + * @action_name: signal to execute upon activation + * @args: #GVariant of the arguments or %NULL if none + * + * Override or install a new key binding for @keyval with @modifiers on + * @binding_set. When the binding is activated, @action_name will be + * activated on the target widget, with @args used as arguments. + */ +void +gtk_binding_entry_add_action_variant (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + const gchar *action_name, + GVariant *args) +{ + g_return_if_fail (binding_set != NULL); + g_return_if_fail (action_name != NULL); + + gtk_binding_entry_add_binding_signal (binding_set, + keyval, + modifiers, + binding_signal_new_action (action_name, args)); +} + +/** + * gtk_binding_entry_add_action: + * @binding_set: a #GtkBindingSet to install an entry for + * @keyval: key value of binding to install + * @modifiers: key modifier of binding to install + * @action_name: signal to execute upon activation + * @format_string: GVariant format string for arguments or %NULL + * for no arguments + * @...: arguments, as given by format string + * + * Override or install a new key binding for @keyval with @modifiers on + * @binding_set. When the binding is activated, @action_name will be + * activated on the target widget, with arguments read according to + * @format_string. + */ +void +gtk_binding_entry_add_action (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + const char *action_name, + const char *format_string, + ...) +{ + GVariant *parameters = NULL; + + g_return_if_fail (binding_set != NULL); + g_return_if_fail (action_name != NULL); + + if (format_string != NULL) + { + va_list args; + + va_start (args, format_string); + parameters = g_variant_new_va (format_string, NULL, &args); + va_end (args); + + g_variant_ref_sink (parameters); + } + + gtk_binding_entry_add_action_variant (binding_set, keyval, modifiers, action_name, parameters); + + g_clear_pointer (¶meters, g_variant_unref); +} + +void +gtk_binding_entry_add_callback (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + GtkBindingCallback callback, + GVariant *args, + gpointer user_data, + GDestroyNotify user_destroy) +{ + g_return_if_fail (binding_set != NULL); + g_return_if_fail (callback != NULL); + + gtk_binding_entry_add_binding_signal (binding_set, + keyval, + modifiers, + binding_signal_new_callback (callback, args, user_data, user_destroy)); + +} + +static guint +gtk_binding_parse_signal (GScanner *scanner, + GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) +{ + gchar *signal; + guint expected_token = 0; + GSList *args; + GSList *slist; + gboolean done; + gboolean negate; + gboolean need_arg; + gboolean seen_comma; + + g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR); + + g_scanner_get_next_token (scanner); + + if (scanner->token != G_TOKEN_STRING) + return G_TOKEN_STRING; + + g_scanner_peek_next_token (scanner); + + if (scanner->next_token != '(') + { + g_scanner_get_next_token (scanner); + return '('; + } + + signal = g_strdup (scanner->value.v_string); + g_scanner_get_next_token (scanner); + + negate = FALSE; + args = NULL; + done = FALSE; + need_arg = TRUE; + seen_comma = FALSE; + scanner->config->scan_symbols = FALSE; + + do + { + GtkBindingArg *arg; + + if (need_arg) + expected_token = G_TOKEN_INT; + else + expected_token = ')'; + + g_scanner_get_next_token (scanner); + + switch ((guint) scanner->token) + { + case G_TOKEN_FLOAT: + if (need_arg) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_DOUBLE; + arg->d.double_data = scanner->value.v_float; + + if (negate) + { + arg->d.double_data = - arg->d.double_data; + negate = FALSE; + } + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + + break; + case G_TOKEN_INT: + if (need_arg) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_LONG; + arg->d.long_data = scanner->value.v_int; + + if (negate) + { + arg->d.long_data = - arg->d.long_data; + negate = FALSE; + } + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + break; + case G_TOKEN_STRING: + if (need_arg && !negate) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_STRING; + arg->d.string_data = g_strdup (scanner->value.v_string); + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + + break; + case G_TOKEN_IDENTIFIER: + if (need_arg && !negate) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_STRING; + arg->d.string_data = g_strdup (scanner->value.v_identifier); + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + + break; + case '-': + if (!need_arg) + done = TRUE; + else if (negate) + { + expected_token = G_TOKEN_INT; + done = TRUE; + } + else + negate = TRUE; + + break; + case ',': + seen_comma = TRUE; + if (need_arg) + done = TRUE; + else + need_arg = TRUE; + + break; + case ')': + if (!(need_arg && seen_comma) && !negate) + { + args = g_slist_reverse (args); + gtk_binding_entry_add_signall (binding_set, + keyval, + modifiers, + signal, + args); + expected_token = G_TOKEN_NONE; + } + + done = TRUE; + break; + default: + done = TRUE; + break; + } + } + while (!done); + + scanner->config->scan_symbols = TRUE; + + for (slist = args; slist; slist = slist->next) + { + GtkBindingArg *arg; + + arg = slist->data; + + if (G_TYPE_FUNDAMENTAL (arg->arg_type) == G_TYPE_STRING) + g_free (arg->d.string_data); + g_free (arg); + } + + g_slist_free (args); + g_free (signal); + + return expected_token; +} + +static inline guint +gtk_binding_parse_bind (GScanner *scanner, + GtkBindingSet *binding_set) +{ + guint keyval = 0; + GdkModifierType modifiers = 0; + gboolean unbind = FALSE; + + g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR); + + g_scanner_get_next_token (scanner); + + if (scanner->token != G_TOKEN_SYMBOL) + return G_TOKEN_SYMBOL; + + if (scanner->value.v_symbol != GUINT_TO_POINTER (GTK_BINDING_TOKEN_BIND) && + scanner->value.v_symbol != GUINT_TO_POINTER (GTK_BINDING_TOKEN_UNBIND)) + return G_TOKEN_SYMBOL; + + unbind = (scanner->value.v_symbol == GUINT_TO_POINTER (GTK_BINDING_TOKEN_UNBIND)); + g_scanner_get_next_token (scanner); + + if (scanner->token != (guint) G_TOKEN_STRING) + return G_TOKEN_STRING; + + gtk_accelerator_parse (scanner->value.v_string, &keyval, &modifiers); + modifiers &= BINDING_MOD_MASK (); + + if (keyval == 0) + return G_TOKEN_STRING; + + if (unbind) + { + gtk_binding_entry_skip (binding_set, keyval, modifiers); + return G_TOKEN_NONE; + } + + g_scanner_get_next_token (scanner); + + if (scanner->token != '{') + return '{'; + + gtk_binding_entry_clear_internal (binding_set, keyval, modifiers); + g_scanner_peek_next_token (scanner); + + while (scanner->next_token != '}') + { + guint expected_token; + + if (scanner->next_token == G_TOKEN_STRING) + { + expected_token = gtk_binding_parse_signal (scanner, + binding_set, + keyval, + modifiers); + if (expected_token != G_TOKEN_NONE) + return expected_token; + } + else + { + g_scanner_get_next_token (scanner); + return '}'; + } + + g_scanner_peek_next_token (scanner); + } + + g_scanner_get_next_token (scanner); + + return G_TOKEN_NONE; +} + +static GScanner * +create_signal_scanner (void) +{ + GScanner *scanner; + + scanner = g_scanner_new (NULL); + scanner->config->cset_identifier_nth = (char *) G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_"; + + g_scanner_scope_add_symbol (scanner, 0, "bind", GUINT_TO_POINTER (GTK_BINDING_TOKEN_BIND)); + g_scanner_scope_add_symbol (scanner, 0, "unbind", GUINT_TO_POINTER (GTK_BINDING_TOKEN_UNBIND)); + + g_scanner_set_scope (scanner, 0); + + return scanner; +} + +/** + * gtk_binding_entry_add_signal_from_string: + * @binding_set: a #GtkBindingSet + * @signal_desc: a signal description + * + * Parses a signal description from @signal_desc and incorporates + * it into @binding_set. + * + * Signal descriptions may either bind a key combination to + * one or more signals: + * |[ + * bind "key" { + * "signalname" (param, ...) + * ... + * } + * ]| + * + * Or they may also unbind a key combination: + * |[ + * unbind "key" + * ]| + * + * Key combinations must be in a format that can be parsed by + * gtk_accelerator_parse(). + * + * Returns: %G_TOKEN_NONE if the signal was successfully parsed and added, + * the expected token otherwise + */ +GTokenType +gtk_binding_entry_add_signal_from_string (GtkBindingSet *binding_set, + const gchar *signal_desc) +{ + static GScanner *scanner = NULL; + GTokenType ret; + + g_return_val_if_fail (binding_set != NULL, G_TOKEN_NONE); + g_return_val_if_fail (signal_desc != NULL, G_TOKEN_NONE); + + if (G_UNLIKELY (!scanner)) + scanner = create_signal_scanner (); + + g_scanner_input_text (scanner, signal_desc, + (guint) strlen (signal_desc)); + + ret = gtk_binding_parse_bind (scanner, binding_set); + + /* Reset for next use */ + g_scanner_set_scope (scanner, 0); + + return ret; +} + +static gint +find_entry_with_binding (GtkBindingEntry *entry, + GtkBindingSet *binding_set) +{ + return (entry->binding_set == binding_set) ? 0 : 1; +} + +static gboolean +binding_activate (GtkBindingSet *binding_set, + GSList *entries, + GObject *object, + gboolean is_release, + gboolean *unbound) +{ + GtkBindingEntry *entry; + GSList *elem; + + elem = g_slist_find_custom (entries, binding_set, + (GCompareFunc) find_entry_with_binding); + + if (!elem) + return FALSE; + + entry = elem->data; + + if (is_release != ((entry->modifiers & GDK_RELEASE_MASK) != 0)) + return FALSE; + + if (entry->marks_unbound) + { + *unbound = TRUE; + return FALSE; + } + + if (gtk_binding_entry_activate (entry, object)) + return TRUE; + + return FALSE; +} + +static gboolean +gtk_bindings_activate_list (GObject *object, + GSList *entries, + gboolean is_release) +{ + GtkBindingSet *binding_set; + gboolean handled = FALSE; + gboolean unbound = FALSE; + + if (!entries) + return FALSE; + + if (!handled) + { + GType class_type; + + class_type = G_TYPE_FROM_INSTANCE (object); + + while (class_type && !handled) + { + binding_set = gtk_binding_set_find_interned (g_type_name (class_type)); + class_type = g_type_parent (class_type); + + if (!binding_set) + continue; + + handled = binding_activate (binding_set, entries, + object, is_release, + &unbound); + if (unbound) + break; + } + + if (unbound) + return FALSE; + } + + return handled; +} + +/** + * gtk_bindings_activate: + * @object: object to activate when binding found + * @keyval: key value of the binding + * @modifiers: key modifier of the binding + * + * Find a key binding matching @keyval and @modifiers and activate the + * binding on @object. + * + * Returns: %TRUE if a binding was found and activated + */ +gboolean +gtk_bindings_activate (GObject *object, + guint keyval, + GdkModifierType modifiers) +{ + GSList *entries = NULL; + GdkDisplay *display; + GtkKeyHash *key_hash; + gboolean handled = FALSE; + gboolean is_release; + + if (!GTK_IS_WIDGET (object)) + return FALSE; + + is_release = (modifiers & GDK_RELEASE_MASK) != 0; + modifiers = modifiers & BINDING_MOD_MASK () & ~GDK_RELEASE_MASK; + + display = gtk_widget_get_display (GTK_WIDGET (object)); + key_hash = binding_key_hash_for_keymap (gdk_display_get_keymap (display)); + + entries = _gtk_key_hash_lookup_keyval (key_hash, keyval, modifiers); + + handled = gtk_bindings_activate_list (object, entries, is_release); + + g_slist_free (entries); + + return handled; +} + +/** + * gtk_bindings_activate_event: + * @object: a #GObject (generally must be a widget) + * @event: a key event + * + * Looks up key bindings for @object to find one matching + * @event, and if one was found, activate it. + * + * Returns: %TRUE if a matching key binding was found + */ +gboolean +gtk_bindings_activate_event (GObject *object, + GdkEvent *event) +{ + GSList *entries = NULL; + GdkDisplay *display; + GtkKeyHash *key_hash; + gboolean handled = FALSE; + + if (!GTK_IS_WIDGET (object)) + return FALSE; + + display = gtk_widget_get_display (GTK_WIDGET (object)); + key_hash = binding_key_hash_for_keymap (gdk_display_get_keymap (display)); + + entries = _gtk_key_hash_lookup (key_hash, + gdk_key_event_get_keycode (event), + gdk_event_get_modifier_state (event), + BINDING_MOD_MASK () & ~GDK_RELEASE_MASK, + gdk_key_event_get_group(event)); + + handled = gtk_bindings_activate_list (object, entries, + gdk_event_get_event_type (event) == GDK_KEY_RELEASE); + + g_slist_free (entries); + + return handled; +} diff --git a/gtk/gtkbindings.h b/gtk/gtkbindings.h new file mode 100644 index 0000000000..ef8756f6bd --- /dev/null +++ b/gtk/gtkbindings.h @@ -0,0 +1,124 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * GtkBindingSet: Keybinding manager for GObjects. + * Copyright (C) 1998 Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GTK_BINDINGS_H__ +#define __GTK_BINDINGS_H__ + + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#include <gdk/gdk.h> +#include <gtk/gtkenums.h> +#include <gtk/gtktypes.h> + +G_BEGIN_DECLS + +typedef struct _GtkBindingSet GtkBindingSet; + +/** + * GtkBindingCallback: + * @widget: The object to invoke the callback on + * @args: (allow-none): The arguments or %NULL if none + * @user_data: The user data passed when registering the callback + * + * Prototype of the callback function registered with + * gtk_binding_entry_add_callback. + */ +typedef void (* GtkBindingCallback) (GtkWidget *widget, + GVariant *args, + gpointer user_data); + +GDK_AVAILABLE_IN_ALL +GtkBindingSet *gtk_binding_set_new (const gchar *set_name); +GDK_AVAILABLE_IN_ALL +GtkBindingSet *gtk_binding_set_by_class (gpointer object_class); +GDK_AVAILABLE_IN_ALL +GtkBindingSet *gtk_binding_set_find (const gchar *set_name); + +GDK_AVAILABLE_IN_ALL +gboolean gtk_bindings_activate (GObject *object, + guint keyval, + GdkModifierType modifiers); +GDK_AVAILABLE_IN_ALL +gboolean gtk_bindings_activate_event (GObject *object, + GdkEvent *event); +GDK_AVAILABLE_IN_ALL +gboolean gtk_binding_set_activate (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + GObject *object); + +GDK_AVAILABLE_IN_ALL +void gtk_binding_entry_skip (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers); +GDK_AVAILABLE_IN_ALL +void gtk_binding_entry_add_signal (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + const gchar *signal_name, + guint n_args, + ...); + +GDK_AVAILABLE_IN_ALL +GTokenType gtk_binding_entry_add_signal_from_string + (GtkBindingSet *binding_set, + const gchar *signal_desc); +GDK_AVAILABLE_IN_ALL +void gtk_binding_entry_add_action_variant + (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + const char *action_name, + GVariant *args); +GDK_AVAILABLE_IN_ALL +void gtk_binding_entry_add_action (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + const char *action_name, + const char *format_string, + ...); + +GDK_AVAILABLE_IN_ALL +void gtk_binding_entry_add_callback(GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + GtkBindingCallback callback, + GVariant *args, + gpointer user_data, + GDestroyNotify user_destroy); + +GDK_AVAILABLE_IN_ALL +void gtk_binding_entry_remove (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers); + +G_END_DECLS + +#endif /* __GTK_BINDINGS_H__ */ diff --git a/gtk/inspector/shortcuts.h b/gtk/gtkbindingsprivate.h index 145cb86408..b99126119b 100644 --- a/gtk/inspector/shortcuts.h +++ b/gtk/gtkbindingsprivate.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2020 Red Hat, Inc. +/* GTK - The GIMP Toolkit + * Copyright (C) 2011 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,17 +15,16 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef _GTK_INSPECTOR_SHORTCUTS_H_ -#define _GTK_INSPECTOR_SHORTCUTS_H_ +#ifndef __GTK_BINDINGS_PRIVATE_H__ +#define __GTK_BINDINGS_PRIVATE_H__ -#include <gtk/gtkbox.h> +#include "gtkbindings.h" -#define GTK_TYPE_INSPECTOR_SHORTCUTS (gtk_inspector_shortcuts_get_type ()) +G_BEGIN_DECLS -G_DECLARE_FINAL_TYPE (GtkInspectorShortcuts, gtk_inspector_shortcuts, GTK, INSPECTOR_SHORTCUTS, GtkWidget) +guint _gtk_binding_parse_binding (GScanner *scanner); +void _gtk_binding_reset_parsed (void); +G_END_DECLS -void gtk_inspector_shortcuts_set_object (GtkInspectorShortcuts *sl, - GObject *object); - -#endif +#endif /* __GTK_BINDINGS_PRIVATE_H__ */ diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c index a101faab5c..ba3f367ddc 100644 --- a/gtk/gtkbuilder.c +++ b/gtk/gtkbuilder.c @@ -215,20 +215,16 @@ #include "gtkbuilderprivate.h" -#include "gdkpixbufutilsprivate.h" #include "gtkbuildable.h" #include "gtkbuilderscopeprivate.h" #include "gtkdebug.h" #include "gtkmain.h" -#include "gtkicontheme.h" #include "gtkintl.h" #include "gtkprivate.h" -#include "gtkshortcutactionprivate.h" -#include "gtkshortcuttrigger.h" -#include "gtktestutils.h" #include "gtktypebuiltins.h" #include "gtkicontheme.h" #include "gtkiconthemeprivate.h" +#include "gdkpixbufutilsprivate.h" static void gtk_builder_finalize (GObject *object); static void gtk_builder_set_property (GObject *object, @@ -2096,29 +2092,6 @@ gtk_builder_value_from_string_type (GtkBuilder *builder, ret = FALSE; } } - else if (G_VALUE_HOLDS (value, GTK_TYPE_SHORTCUT_TRIGGER)) - { - GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (string); - - if (trigger) - g_value_take_boxed (value, trigger); - else - { - g_set_error (error, - GTK_BUILDER_ERROR, - GTK_BUILDER_ERROR_INVALID_VALUE, - "Could not parse shortcut trigger '%s'", - string); - ret = FALSE; - } - } - else if (G_VALUE_HOLDS (value, GTK_TYPE_SHORTCUT_ACTION)) - { - GtkShortcutAction *action = gtk_shortcut_action_parse_builder (builder, string, error); - - /* Works for success and failure (NULL) case */ - g_value_take_boxed (value, action); - } else if (G_VALUE_HOLDS (value, G_TYPE_STRV)) { gchar **vector = g_strsplit (string, "\n", 0); diff --git a/gtk/gtkcoloreditor.c b/gtk/gtkcoloreditor.c index 9fb73fa027..6f3ac11b6d 100644 --- a/gtk/gtkcoloreditor.c +++ b/gtk/gtkcoloreditor.c @@ -189,44 +189,35 @@ dismiss_current_popup (GtkColorEditor *editor) } static void -popup_edit (GtkWidget *widget, - const char *action_name, - GVariant *parameters) +popup_edit (GtkWidget *widget, + GtkColorEditor *editor) { - GtkColorEditor *editor = GTK_COLOR_EDITOR (widget); GtkWidget *popup = NULL; GtkRoot *root; GtkWidget *focus; gint position; gint s, e; - const char *param; - - param = g_variant_get_string (parameters, NULL); - if (strcmp (param, "sv") == 0) + if (widget == editor->priv->sv_plane) { popup = editor->priv->sv_popup; focus = editor->priv->s_entry; position = 0; } - else if (strcmp (param, "h") == 0) + else if (widget == editor->priv->h_slider) { popup = editor->priv->h_popup; focus = editor->priv->h_entry; gtk_range_get_slider_range (GTK_RANGE (editor->priv->h_slider), &s, &e); position = (s + e) / 2; } - else if (strcmp (param, "a") == 0) + else if (widget == editor->priv->a_slider) { popup = editor->priv->a_popup; focus = editor->priv->a_entry; gtk_range_get_slider_range (GTK_RANGE (editor->priv->a_slider), &s, &e); position = (s + e) / 2; } - else - { - g_warning ("unsupported popup_edit parameter %s", param); - } if (popup == editor->priv->current_popup) dismiss_current_popup (editor); @@ -553,12 +544,8 @@ gtk_color_editor_class_init (GtkColorEditorClass *class) gtk_widget_class_bind_template_callback (widget_class, entry_text_changed); gtk_widget_class_bind_template_callback (widget_class, entry_apply); gtk_widget_class_bind_template_callback (widget_class, entry_focus_changed); + gtk_widget_class_bind_template_callback (widget_class, popup_edit); gtk_widget_class_bind_template_callback (widget_class, pick_color); - - gtk_widget_class_install_action (widget_class, "color.edit", "s", popup_edit); - gtk_widget_class_install_action (widget_class, "color.edit", "s", popup_edit); - gtk_widget_class_install_action (widget_class, "color.edit", "s", popup_edit); - } static void diff --git a/gtk/gtkcolorplane.c b/gtk/gtkcolorplane.c index 36a9ca6b8e..c73b9eec22 100644 --- a/gtk/gtkcolorplane.c +++ b/gtk/gtkcolorplane.c @@ -28,10 +28,6 @@ #include "gtksnapshot.h" #include "gtkprivate.h" #include "gtkeventcontrollerkey.h" -#include "gtkshortcutcontroller.h" -#include "gtkshortcuttrigger.h" -#include "gtkshortcutaction.h" -#include "gtkshortcut.h" struct _GtkColorPlanePrivate { @@ -248,11 +244,11 @@ static void hold_action (GtkGestureLongPress *gesture, gdouble x, gdouble y, - GtkWidget *plane) + GtkColorPlane *plane) { - gtk_widget_activate_action (plane, - "color.edit", - "s", gtk_widget_get_name (plane)); + gboolean handled; + + g_signal_emit_by_name (plane, "popup-menu", &handled); } static void @@ -348,7 +344,7 @@ static void plane_drag_gesture_begin (GtkGestureDrag *gesture, gdouble start_x, gdouble start_y, - GtkWidget *plane) + GtkColorPlane *plane) { guint button; @@ -356,9 +352,9 @@ plane_drag_gesture_begin (GtkGestureDrag *gesture, if (button == GDK_BUTTON_SECONDARY) { - gtk_widget_activate_action (plane, - "color.edit", - "s", gtk_widget_get_name (plane)); + gboolean handled; + + g_signal_emit_by_name (plane, "popup-menu", &handled); } if (button != GDK_BUTTON_PRIMARY) @@ -367,9 +363,9 @@ plane_drag_gesture_begin (GtkGestureDrag *gesture, return; } - set_cross_cursor (plane, TRUE); - update_color (GTK_COLOR_PLANE (plane), start_x, start_y); - gtk_widget_grab_focus (plane); + set_cross_cursor (GTK_WIDGET (plane), TRUE); + update_color (plane, start_x, start_y); + gtk_widget_grab_focus (GTK_WIDGET (plane)); gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); } @@ -401,9 +397,6 @@ gtk_color_plane_init (GtkColorPlane *plane) GtkEventController *controller; GtkGesture *gesture; AtkObject *atk_obj; - GtkShortcutTrigger *trigger; - GtkShortcutAction *action; - GtkShortcut *shortcut; plane->priv = gtk_color_plane_get_instance_private (plane); @@ -437,14 +430,6 @@ gtk_color_plane_init (GtkColorPlane *plane) g_signal_connect (controller, "key-pressed", G_CALLBACK (key_controller_key_pressed), plane); gtk_widget_add_controller (GTK_WIDGET (plane), controller); - - controller = gtk_shortcut_controller_new (); - trigger = gtk_alternative_trigger_new (gtk_keyval_trigger_new (GDK_KEY_F10, GDK_SHIFT_MASK), - gtk_keyval_trigger_new (GDK_KEY_Menu, 0)); - action = gtk_action_action_new ("color.edit"); - shortcut = gtk_shortcut_new_with_arguments (trigger, action, "s", "sv"); - gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); - gtk_widget_add_controller (GTK_WIDGET (plane), controller); } static void diff --git a/gtk/gtkcolorscale.c b/gtk/gtkcolorscale.c index 56d120b111..d4bc48452a 100644 --- a/gtk/gtkcolorscale.c +++ b/gtk/gtkcolorscale.c @@ -29,10 +29,6 @@ #include "gtkprivate.h" #include "gtkintl.h" #include "gtksnapshot.h" -#include "gtkshortcutcontroller.h" -#include "gtkshortcuttrigger.h" -#include "gtkshortcutaction.h" -#include "gtkshortcut.h" #include <math.h> @@ -52,7 +48,7 @@ enum static void hold_action (GtkGestureLongPress *gesture, gdouble x, gdouble y, - GtkWidget *scale); + GtkColorScale *scale); G_DEFINE_TYPE_WITH_PRIVATE (GtkColorScale, gtk_color_scale, GTK_TYPE_SCALE) @@ -165,29 +161,6 @@ gtk_color_scale_init (GtkColorScale *scale) } static void -scale_constructed (GObject *object) -{ - GtkColorScale *scale = GTK_COLOR_SCALE (object); - GtkColorScalePrivate *priv = gtk_color_scale_get_instance_private (scale); - GtkEventController *controller; - GtkShortcutTrigger *trigger; - GtkShortcutAction *action; - GtkShortcut *shortcut; - - controller = gtk_shortcut_controller_new (); - trigger = gtk_alternative_trigger_new (gtk_keyval_trigger_new (GDK_KEY_F10, GDK_SHIFT_MASK), - gtk_keyval_trigger_new (GDK_KEY_Menu, 0)); - action = gtk_action_action_new ("color.edit"); - shortcut = gtk_shortcut_new_with_arguments (trigger, - action, - "s", - priv->type == GTK_COLOR_SCALE_ALPHA - ? "a" : "h"); - gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); - gtk_widget_add_controller (GTK_WIDGET (scale), controller); -} - -static void scale_get_property (GObject *object, guint prop_id, GValue *value, @@ -250,11 +223,11 @@ static void hold_action (GtkGestureLongPress *gesture, gdouble x, gdouble y, - GtkWidget *scale) + GtkColorScale *scale) { - gtk_widget_activate_action (scale, - "color.edit", - "s", gtk_widget_get_name (scale)); + gboolean handled; + + g_signal_emit_by_name (scale, "popup-menu", &handled); } static void @@ -272,7 +245,6 @@ gtk_color_scale_class_init (GtkColorScaleClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); - object_class->constructed = scale_constructed; object_class->finalize = scale_finalize; object_class->get_property = scale_get_property; object_class->set_property = scale_set_property; @@ -281,7 +253,6 @@ gtk_color_scale_class_init (GtkColorScaleClass *class) g_param_spec_int ("scale-type", P_("Scale type"), P_("Scale type"), 0, 1, 0, GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - } void diff --git a/gtk/gtkcolorswatch.c b/gtk/gtkcolorswatch.c index f68003cc69..ca6ab4d54f 100644 --- a/gtk/gtkcolorswatch.c +++ b/gtk/gtkcolorswatch.c @@ -348,12 +348,13 @@ gtk_color_swatch_measure (GtkWidget *widget, *natural = MAX (*natural, min); } -static void -swatch_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters) + + +static gboolean +swatch_popup_menu (GtkWidget *widget) { do_popup (GTK_COLOR_SWATCH (widget)); + return TRUE; } static void @@ -479,6 +480,7 @@ gtk_color_swatch_class_init (GtkColorSwatchClass *class) widget_class->measure = gtk_color_swatch_measure; widget_class->snapshot = swatch_snapshot; + widget_class->popup_menu = swatch_popup_menu; widget_class->size_allocate = swatch_size_allocate; widget_class->state_flags_changed = swatch_state_flags_changed; @@ -495,17 +497,6 @@ gtk_color_swatch_class_init (GtkColorSwatchClass *class) g_param_spec_boolean ("can-drop", P_("Can Drop"), P_("Whether the swatch should accept drops"), FALSE, GTK_PARAM_READWRITE)); - gtk_widget_class_install_action (widget_class, "menu.popup", NULL, swatch_popup_menu); - - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_F10, GDK_SHIFT_MASK, - "menu.popup", - NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_Menu, 0, - "menu.popup", - NULL); - gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_COLOR_SWATCH_ACCESSIBLE); gtk_widget_class_set_css_name (widget_class, I_("colorswatch")); } diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c index cdf69804e9..5e5bdd1c39 100644 --- a/gtk/gtkcombobox.c +++ b/gtk/gtkcombobox.c @@ -19,12 +19,12 @@ #include "gtkcomboboxprivate.h" +#include "gtkbindings.h" #include "gtkbox.h" #include "gtkcellareabox.h" #include "gtkcelllayout.h" #include "gtkcellrenderertext.h" #include "gtkcellview.h" -#include "gtkeventcontrollerkey.h" #include "gtkeventcontrollerscroll.h" #include "gtkframe.h" #include "gtkbuiltiniconprivate.h" @@ -33,11 +33,10 @@ #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtkprivate.h" -#include "gtkshortcutcontroller.h" #include "gtktogglebutton.h" #include "gtktreepopoverprivate.h" #include "gtktypebuiltins.h" -#include "gtkwidgetprivate.h" +#include "gtkeventcontrollerkey.h" #include "a11y/gtkcomboboxaccessible.h" @@ -410,6 +409,7 @@ gtk_combo_box_class_init (GtkComboBoxClass *klass) GObjectClass *object_class; GtkContainerClass *container_class; GtkWidgetClass *widget_class; + GtkBindingSet *binding_set; container_class = (GtkContainerClass *)klass; container_class->forall = gtk_combo_box_forall; @@ -560,77 +560,57 @@ gtk_combo_box_class_init (GtkComboBoxClass *klass) G_TYPE_STRING, 1, G_TYPE_STRING); /* key bindings */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Down, GDK_MOD1_MASK, - "popup", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Down, GDK_MOD1_MASK, - "popup", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Up, GDK_MOD1_MASK, - "popdown", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Up, GDK_MOD1_MASK, - "popdown", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Escape, 0, - "popdown", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Up, 0, - "move-active", - "(i)", GTK_SCROLL_STEP_UP); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Up, 0, - "move-active", - "(i)", GTK_SCROLL_STEP_UP); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Up, 0, - "move-active", - "(i)", GTK_SCROLL_PAGE_UP); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Page_Up, 0, - "move-active", - "(i)", GTK_SCROLL_PAGE_UP); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Home, 0, - "move-active", - "(i)", GTK_SCROLL_START); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Home, 0, - "move-active", - "(i)", GTK_SCROLL_START); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Down, 0, - "move-active", - "(i)", GTK_SCROLL_STEP_DOWN); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Down, 0, - "move-active", - "(i)", GTK_SCROLL_STEP_DOWN); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Down, 0, - "move-active", - "(i)", GTK_SCROLL_PAGE_DOWN); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Page_Down, 0, - "move-active", - "(i)", GTK_SCROLL_PAGE_DOWN); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_End, 0, - "move-active", - "(i)", GTK_SCROLL_END); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_End, 0, - "move-active", - "(i)", GTK_SCROLL_END); + binding_set = gtk_binding_set_by_class (widget_class); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_MOD1_MASK, + "popup", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_MOD1_MASK, + "popup", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_MOD1_MASK, + "popdown", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_MOD1_MASK, + "popdown", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, + "popdown", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0, + "move-active", 1, + GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END); /* properties */ g_object_class_override_property (object_class, @@ -846,7 +826,6 @@ gtk_combo_box_init (GtkComboBox *combo_box) { GtkComboBoxPrivate *priv = gtk_combo_box_get_instance_private (combo_box); GtkEventController *controller; - GList *controllers, *list; priv->active = -1; priv->active_row = NULL; @@ -881,19 +860,6 @@ gtk_combo_box_init (GtkComboBox *combo_box) G_CALLBACK (gtk_combo_box_scroll_controller_scroll), combo_box); gtk_widget_add_controller (GTK_WIDGET (combo_box), controller); - - controllers = gtk_widget_list_controllers (priv->popup_widget, GTK_PHASE_BUBBLE); - for (list = controllers; list; list = list->next) - { - if (GTK_IS_SHORTCUT_CONTROLLER (list->data)) - { - g_object_ref (list->data); - gtk_widget_remove_controller (priv->popup_widget, list->data); - gtk_widget_add_controller (priv->popup_widget, list->data); - break; - } - } - g_list_free (controllers); } static void @@ -1823,7 +1789,18 @@ gtk_combo_box_menu_key (GtkEventControllerKey *key, GdkModifierType modifiers, GtkComboBox *combo_box) { - gtk_event_controller_key_forward (key, GTK_WIDGET (combo_box)); + GtkWidget *widget; + GdkEvent *event; + + widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (key)); + event = gtk_get_current_event (); + + if (!gtk_bindings_activate_event (G_OBJECT (widget), event)) + { + gtk_event_controller_key_forward (key, GTK_WIDGET (combo_box)); + } + + gdk_event_unref (event); return TRUE; } diff --git a/gtk/gtkconcatmodel.c b/gtk/gtkconcatmodel.c deleted file mode 100644 index d96290e6f3..0000000000 --- a/gtk/gtkconcatmodel.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - - -/* - * SECTION:gtkconcatmodel - * @Short_description: concatenation list model - * @Title: GtkConcatModel - * @See_also: #GListModel - * - * #GtkConcatModel is a #GListModel implementation that takes a list of list models - * and presents them as one concatenated list. - * - * Node that all the types of the passed in list models must match the concat model's - * type. If they are not, you must use a common ancestor type for the #GtkConcatModel, - * %G_TYPE_OBJECT being the ultimate option. - **/ - -#include "config.h" - -#include "gtkconcatmodelprivate.h" - -struct _GtkConcatModel -{ - GObject parent_instance; - - GType item_type; - guint n_items; - - GList *models; -}; - -struct _GtkConcatModelClass -{ - GObjectClass parent_class; -}; - -static GType -gtk_concat_model_list_model_get_item_type (GListModel *list) -{ - GtkConcatModel *self = GTK_CONCAT_MODEL (list); - - return self->item_type; -} - -static guint -gtk_concat_model_list_model_get_n_items (GListModel *list) -{ - GtkConcatModel *self = GTK_CONCAT_MODEL (list); - - return self->n_items; -} - -static gpointer -gtk_concat_model_list_model_get_item (GListModel *list, - guint position) -{ - GtkConcatModel *self = GTK_CONCAT_MODEL (list); - GList *l; - - /* FIXME: Use an RBTree to make this O(log N) */ - for (l = self->models; l; l = l->next) - { - guint n = g_list_model_get_n_items (l->data); - - if (position < n) - return g_list_model_get_item (l->data, position); - - position -= n; - } - - return NULL; -} - -static void -gtk_concat_model_list_model_init (GListModelInterface *iface) -{ - iface->get_item_type = gtk_concat_model_list_model_get_item_type; - iface->get_n_items = gtk_concat_model_list_model_get_n_items; - iface->get_item = gtk_concat_model_list_model_get_item; -} - -G_DEFINE_TYPE_WITH_CODE (GtkConcatModel, gtk_concat_model, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_concat_model_list_model_init)) - -static void -gtk_concat_model_items_changed (GListModel *model, - guint position, - guint removed, - guint added, - GtkConcatModel *self) -{ - GList *l; - - for (l = self->models; l; l = l->next) - { - if (l->data == model) - break; - - position += g_list_model_get_n_items (l->data); - } - - self->n_items -= removed; - self->n_items += added; - - g_list_model_items_changed (G_LIST_MODEL (self), - position, - removed, - added); -} - -static void -gtk_concat_model_remove_internal (GtkConcatModel *self, - GListModel *model, - gboolean emit_signals) -{ - guint n_items, position; - GList *l; - - position = 0; - for (l = self->models; l; l = l->next) - { - if (l->data == model) - break; - - position += g_list_model_get_n_items (l->data); - } - - g_return_if_fail (l != NULL); - - self->models = g_list_delete_link (self->models, l); - n_items = g_list_model_get_n_items (model); - self->n_items -= n_items; - g_signal_handlers_disconnect_by_func (model, gtk_concat_model_items_changed, self); - g_object_unref (model); - - if (n_items && emit_signals) - g_list_model_items_changed (G_LIST_MODEL (self), - position, - n_items, - 0); -} - -static void -gtk_concat_model_dispose (GObject *object) -{ - GtkConcatModel *self = GTK_CONCAT_MODEL (object); - - /* FIXME: Make this work without signal emissions */ - while (self->models) - gtk_concat_model_remove_internal (self, self->models->data, FALSE); - - G_OBJECT_CLASS (gtk_concat_model_parent_class)->dispose (object); -} - -static void -gtk_concat_model_class_init (GtkConcatModelClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = gtk_concat_model_dispose; -} - -static void -gtk_concat_model_init (GtkConcatModel *self) -{ -} - -GtkConcatModel * -gtk_concat_model_new (GType item_type) -{ - GtkConcatModel *self; - - g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL); - - self = g_object_new (GTK_TYPE_CONCAT_MODEL, NULL); - - self->item_type = item_type; - - return self; -} - -void -gtk_concat_model_append (GtkConcatModel *self, - GListModel *model) -{ - guint n_items; - - g_return_if_fail (GTK_IS_CONCAT_MODEL (self)); - g_return_if_fail (G_IS_LIST_MODEL (model)); - g_return_if_fail (g_type_is_a (g_list_model_get_item_type (model), self->item_type)); - - g_object_ref (model); - g_signal_connect (model, "items-changed", G_CALLBACK (gtk_concat_model_items_changed), self); - self->models = g_list_append (self->models, model); - n_items = g_list_model_get_n_items (model); - self->n_items += n_items; - - if (n_items) - g_list_model_items_changed (G_LIST_MODEL (self), - self->n_items - n_items, - 0, - n_items); -} - -void -gtk_concat_model_remove (GtkConcatModel *self, - GListModel *model) -{ - g_return_if_fail (GTK_IS_CONCAT_MODEL (self)); - g_return_if_fail (G_IS_LIST_MODEL (model)); - - gtk_concat_model_remove_internal (self, model, TRUE); -} - -GListModel * -gtk_concat_model_get_model_for_item (GtkConcatModel *self, - guint position) -{ - GList *l; - - g_return_val_if_fail (GTK_IS_CONCAT_MODEL (self), NULL); - - /* FIXME: Use an RBTree to make this O(log N) */ - for (l = self->models; l; l = l->next) - { - guint n = g_list_model_get_n_items (l->data); - - if (position < n) - return l->data; - - position -= n; - } - - return NULL; -} - diff --git a/gtk/gtkconcatmodelprivate.h b/gtk/gtkconcatmodelprivate.h deleted file mode 100644 index 005a9f3da8..0000000000 --- a/gtk/gtkconcatmodelprivate.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - - -#ifndef __GTK_CONCAT_MODEL_H__ -#define __GTK_CONCAT_MODEL_H__ - -#include <gio/gio.h> - -G_BEGIN_DECLS - -#define GTK_TYPE_CONCAT_MODEL (gtk_concat_model_get_type ()) -#define GTK_CONCAT_MODEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_CONCAT_MODEL, GtkConcatModel)) -#define GTK_CONCAT_MODEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_CONCAT_MODEL, GtkConcatModelClass)) -#define GTK_IS_CONCAT_MODEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_CONCAT_MODEL)) -#define GTK_IS_CONCAT_MODEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_CONCAT_MODEL)) -#define GTK_CONCAT_MODEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_CONCAT_MODEL, GtkConcatModelClass)) - -typedef struct _GtkConcatModel GtkConcatModel; -typedef struct _GtkConcatModelClass GtkConcatModelClass; - -GType gtk_concat_model_get_type (void) G_GNUC_CONST; - -GtkConcatModel * gtk_concat_model_new (GType item_type); - -void gtk_concat_model_append (GtkConcatModel *self, - GListModel *model); -void gtk_concat_model_remove (GtkConcatModel *self, - GListModel *model); - -GListModel * gtk_concat_model_get_model_for_item (GtkConcatModel *self, - guint position); - -G_END_DECLS - -#endif /* __GTK_CONCAT_MODEL_H__ */ diff --git a/gtk/gtkdialog.c b/gtk/gtkdialog.c index a2cc9e15ef..bf9e412cfd 100644 --- a/gtk/gtkdialog.c +++ b/gtk/gtkdialog.c @@ -38,6 +38,7 @@ #include "gtkcontainerprivate.h" #include "gtkmain.h" #include "gtkintl.h" +#include "gtkbindings.h" #include "gtkprivate.h" #include "gtkbuildable.h" #include "gtkbuilderprivate.h" @@ -489,6 +490,7 @@ gtk_dialog_class_init (GtkDialogClass *class) GObjectClass *gobject_class; GtkWidgetClass *widget_class; GtkWindowClass *window_class; + GtkBindingSet *binding_set; gobject_class = G_OBJECT_CLASS (class); widget_class = GTK_WIDGET_CLASS (class); @@ -563,7 +565,8 @@ gtk_dialog_class_init (GtkDialogClass *class) -1, 1, -1, GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Escape, 0, "close", NULL); + binding_set = gtk_binding_set_by_class (class); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0); /* Bind class to template */ diff --git a/gtk/gtkemojichooser.c b/gtk/gtkemojichooser.c index 7d31123bf8..66594db645 100644 --- a/gtk/gtkemojichooser.c +++ b/gtk/gtkemojichooser.c @@ -119,39 +119,12 @@ gtk_emoji_chooser_child_focus (GtkWidget *widget, return GTK_WIDGET_CLASS (gtk_emoji_chooser_child_parent_class)->focus (widget, direction); } -static void show_variations (GtkEmojiChooser *chooser, - GtkWidget *child); - -static void -gtk_emoji_chooser_child_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *paramters) -{ - GtkWidget *chooser; - - chooser = gtk_widget_get_ancestor (widget, GTK_TYPE_EMOJI_CHOOSER); - - show_variations (GTK_EMOJI_CHOOSER (chooser), widget); -} - static void gtk_emoji_chooser_child_class_init (GtkEmojiChooserChildClass *class) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); widget_class->size_allocate = gtk_emoji_chooser_child_size_allocate; widget_class->focus = gtk_emoji_chooser_child_focus; - - gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_emoji_chooser_child_popup_menu); - - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_F10, GDK_SHIFT_MASK, - "menu.popup", - NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_Menu, 0, - "menu.popup", - NULL); - gtk_widget_class_set_css_name (widget_class, "emoji"); } @@ -448,6 +421,16 @@ pressed_cb (GtkGesture *gesture, show_variations (chooser, child); } +static gboolean +popup_menu (GtkWidget *widget, + gpointer data) +{ + GtkEmojiChooser *chooser = data; + + show_variations (chooser, widget); + return TRUE; +} + static void add_emoji (GtkWidget *box, gboolean prepend, @@ -505,6 +488,9 @@ add_emoji (GtkWidget *box, if (modifier != 0) g_object_set_data (G_OBJECT (child), "modifier", GUINT_TO_POINTER (modifier)); + if (chooser) + g_signal_connect (child, "popup-menu", G_CALLBACK (popup_menu), chooser); + gtk_container_add (GTK_CONTAINER (child), label); gtk_flow_box_insert (GTK_FLOW_BOX (box), child, prepend ? 0 : -1); } diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index 13fd885acf..21f7cfe16e 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -31,6 +31,7 @@ #include "gtkentryprivate.h" #include "gtkadjustment.h" +#include "gtkbindings.h" #include "gtkbox.h" #include "gtkbutton.h" #include "gtkcelleditable.h" diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h index 658be5f338..8cb42cff20 100644 --- a/gtk/gtkenums.h +++ b/gtk/gtkenums.h @@ -973,25 +973,6 @@ typedef enum } GtkPanDirection; /** - * GtkShortcutScope: - * @GTK_SHORTCUT_SCOPE_LOCAL: Shortcuts are handled inside - * the widget the controller belongs to. - * @GTK_SHORTCUT_SCOPE_MANAGED: Shortcuts are handled by - * the first ancestor that is a #GtkShortcutManager - * @GTK_SHORTCUT_SCOPE_GLOBAL: Shortcuts are handled by - * the root widget. - * - * Describes where #GtkShortcuts added to a - * #GtkShortcutController get handled. - */ -typedef enum -{ - GTK_SHORTCUT_SCOPE_LOCAL, - GTK_SHORTCUT_SCOPE_MANAGED, - GTK_SHORTCUT_SCOPE_GLOBAL -} GtkShortcutScope; - -/** * GtkPopoverConstraint: * @GTK_POPOVER_CONSTRAINT_NONE: Don't constrain the popover position * beyond what is imposed by the implementation diff --git a/gtk/gtkeventcontrollerfocus.c b/gtk/gtkeventcontrollerfocus.c index 500a09b99a..a8a6aaa296 100644 --- a/gtk/gtkeventcontrollerfocus.c +++ b/gtk/gtkeventcontrollerfocus.c @@ -35,6 +35,7 @@ #include "gtkwidgetprivate.h" #include "gtkeventcontrollerprivate.h" #include "gtkeventcontrollerfocus.h" +#include "gtkbindings.h" #include "gtkenums.h" #include "gtkmain.h" #include "gtktypebuiltins.h" diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index 62614dbf79..efb1befb58 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -35,6 +35,7 @@ #include "gtkwidgetprivate.h" #include "gtkeventcontrollerprivate.h" #include "gtkeventcontrollerkey.h" +#include "gtkbindings.h" #include "gtkenums.h" #include "gtkmain.h" #include "gtktypebuiltins.h" @@ -367,6 +368,9 @@ gtk_event_controller_key_forward (GtkEventControllerKey *controller, GTK_PHASE_BUBBLE)) return TRUE; + if (gtk_bindings_activate_event (G_OBJECT (widget), controller->current_event)) + return TRUE; + return FALSE; } diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index ec809192e5..c2c852a134 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -23,6 +23,7 @@ #include "gtkfilechooserwidgetprivate.h" #include "gtkbookmarksmanagerprivate.h" +#include "gtkbindings.h" #include "gtkbutton.h" #include "gtkcelllayout.h" #include "gtkcellrendererpixbuf.h" @@ -84,10 +85,6 @@ #include "gtkbinlayout.h" #include "gtkwidgetprivate.h" #include "gtkpopovermenuprivate.h" -#include "gtkshortcutcontroller.h" -#include "gtkshortcuttrigger.h" -#include "gtkshortcutaction.h" -#include "gtkshortcut.h" #include <cairo-gobject.h> @@ -1193,22 +1190,125 @@ places_sidebar_show_error_message_cb (GtkPlacesSidebar *sidebar, } static gboolean -trigger_location_entry (GtkWidget *widget, - GVariant *arguments, - gpointer unused) +should_trigger_location_entry (GtkFileChooserWidget *impl, + guint keyval, + GdkModifierType state, + const char **string) { - GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (widget); GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl); + GdkModifierType no_text_input_mask; if (priv->operation_mode == OPERATION_MODE_SEARCH) return FALSE; - if (priv->action != GTK_FILE_CHOOSER_ACTION_OPEN && - priv->action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + no_text_input_mask = + gtk_widget_get_modifier_mask (GTK_WIDGET (impl), GDK_MODIFIER_INTENT_NO_TEXT_INPUT); + + if (state & no_text_input_mask) return FALSE; - location_popup_handler (impl, g_variant_get_string (arguments, NULL)); - return TRUE; + switch (keyval) + { + case GDK_KEY_slash: + case GDK_KEY_KP_Divide: + *string = "/"; + return TRUE; + + case GDK_KEY_period: + *string = "."; + return TRUE; + + case GDK_KEY_asciitilde: + *string = "~"; + return TRUE; + + default: + return FALSE; + } +} + +/* Handles key press events on the file list, so that we can trap Enter to + * activate the default button on our own. Also, checks to see if “/” has been + * pressed. + */ +static gboolean +treeview_key_press_cb (GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + gpointer data) +{ + GtkFileChooserWidget *impl = (GtkFileChooserWidget *) data; + GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl); + const char *string; + + if (should_trigger_location_entry (impl, keyval, state, &string) && + (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)) + { + location_popup_handler (impl, string); + return GDK_EVENT_STOP; + } + + if ((keyval == GDK_KEY_Return || + keyval == GDK_KEY_ISO_Enter || + keyval == GDK_KEY_KP_Enter || + keyval == GDK_KEY_space || + keyval == GDK_KEY_KP_Space) && + !(state & gtk_accelerator_get_default_mod_mask ()) && + priv->action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + { + gtk_widget_activate_default (GTK_WIDGET (impl)); + return GDK_EVENT_STOP; + } + + if (keyval == GDK_KEY_Escape && + priv->operation_mode == OPERATION_MODE_SEARCH) + { + return gtk_event_controller_key_forward (controller, + GTK_WIDGET (gtk_search_entry_get_text_widget (GTK_SEARCH_ENTRY (priv->search_entry)))); + } + + return GDK_EVENT_PROPAGATE; +} + +static gboolean +widget_key_press_cb (GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType state, + gpointer data) +{ + GtkFileChooserWidget *impl = (GtkFileChooserWidget *) data; + GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl); + gboolean handled = FALSE; + const char *string; + + if (should_trigger_location_entry (impl, keyval, state, &string)) + { + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + { + location_popup_handler (impl, string); + handled = TRUE; + } + } + else + { + priv->starting_search = TRUE; + if (gtk_event_controller_key_forward (controller, priv->search_entry)) + { + gtk_widget_grab_focus (priv->search_entry); + + if (priv->operation_mode != OPERATION_MODE_SEARCH && + priv->starting_search) + operation_mode_set (impl, OPERATION_MODE_SEARCH); + + handled = TRUE; + } + } + + return handled; } /* Callback used from gtk_tree_selection_selected_foreach(); adds a bookmark for @@ -2032,12 +2132,11 @@ file_list_show_popover (GtkFileChooserWidget *impl, gtk_popover_popup (GTK_POPOVER (priv->browse_files_popover)); } +/* Callback used for the GtkWidget::popup-menu signal of the file list */ static gboolean -list_popup_menu_cb (GtkWidget *widget, - GVariant *args, - gpointer user_data) +list_popup_menu_cb (GtkWidget *widget, + GtkFileChooserWidget *impl) { - GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (user_data); GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl); graphene_rect_t bounds; @@ -2052,16 +2151,6 @@ list_popup_menu_cb (GtkWidget *widget, return FALSE; } -static void -files_list_clicked (GtkGesture *gesture, - int n_press, - double x, - double y, - GtkFileChooserWidget *impl) -{ - list_popup_menu_cb (NULL, NULL, impl); -} - /* Callback used when a button is pressed on the file list. We trap button 3 to * bring up a popup menu. */ @@ -7372,20 +7461,18 @@ show_hidden_handler (GtkFileChooserWidget *impl) } static void -add_normal_and_shifted_binding (GtkWidgetClass *widget_class, +add_normal_and_shifted_binding (GtkBindingSet *binding_set, guint keyval, GdkModifierType modifiers, const gchar *signal_name) { - gtk_widget_class_add_binding_signal (widget_class, - keyval, modifiers, - signal_name, - NULL); + gtk_binding_entry_add_signal (binding_set, + keyval, modifiers, + signal_name, 0); - gtk_widget_class_add_binding_signal (widget_class, - keyval, modifiers | GDK_SHIFT_MASK, - signal_name, - NULL); + gtk_binding_entry_add_signal (binding_set, + keyval, modifiers | GDK_SHIFT_MASK, + signal_name, 0); } static void @@ -7396,6 +7483,7 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) }; GObjectClass *gobject_class = G_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + GtkBindingSet *binding_set; gint i; gobject_class->finalize = gtk_file_chooser_widget_finalize; @@ -7676,84 +7764,71 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) NULL, G_TYPE_NONE, 0); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_l, GDK_CONTROL_MASK, - "location-toggle-popup", - NULL); + binding_set = gtk_binding_set_by_class (class); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_v, GDK_CONTROL_MASK, - "location-popup-on-paste", - NULL); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_l, GDK_CONTROL_MASK, + "location-toggle-popup", + 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_v, GDK_CONTROL_MASK, + "location-popup-on-paste", + 0); - add_normal_and_shifted_binding (widget_class, + add_normal_and_shifted_binding (binding_set, GDK_KEY_Up, GDK_MOD1_MASK, "up-folder"); - add_normal_and_shifted_binding (widget_class, + + add_normal_and_shifted_binding (binding_set, GDK_KEY_KP_Up, GDK_MOD1_MASK, "up-folder"); - add_normal_and_shifted_binding (widget_class, + add_normal_and_shifted_binding (binding_set, GDK_KEY_Down, GDK_MOD1_MASK, "down-folder"); - add_normal_and_shifted_binding (widget_class, + add_normal_and_shifted_binding (binding_set, GDK_KEY_KP_Down, GDK_MOD1_MASK, "down-folder"); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Home, GDK_MOD1_MASK, - "home-folder", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Home, GDK_MOD1_MASK, - "home-folder", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_d, GDK_MOD1_MASK, - "desktop-folder", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_h, GDK_CONTROL_MASK, - "show-hidden", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_s, GDK_MOD1_MASK, - "search-shortcut", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_f, GDK_CONTROL_MASK, - "search-shortcut", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_r, GDK_MOD1_MASK, - "recent-shortcut", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_p, GDK_MOD1_MASK, - "places-shortcut", - NULL); - gtk_widget_class_add_binding (widget_class, - GDK_KEY_slash, 0, - trigger_location_entry, - "s", "/"); - gtk_widget_class_add_binding (widget_class, - GDK_KEY_KP_Divide, 0, - trigger_location_entry, - "s", "/"); - gtk_widget_class_add_binding (widget_class, - GDK_KEY_period, 0, - trigger_location_entry, - "s", "."); - gtk_widget_class_add_binding (widget_class, - GDK_KEY_asciitilde, 0, - trigger_location_entry, - "s", "~"); - - for (i = 0; i < G_N_ELEMENTS (quick_bookmark_keyvals); i++) - gtk_widget_class_add_binding_signal (widget_class, - quick_bookmark_keyvals[i], GDK_MOD1_MASK, - "quick-bookmark", - "(i)", i); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Home, GDK_MOD1_MASK, + "home-folder", + 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_KP_Home, GDK_MOD1_MASK, + "home-folder", + 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_d, GDK_MOD1_MASK, + "desktop-folder", + 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_h, GDK_CONTROL_MASK, + "show-hidden", + 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_s, GDK_MOD1_MASK, + "search-shortcut", + 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_f, GDK_CONTROL_MASK, + "search-shortcut", + 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_r, GDK_MOD1_MASK, + "recent-shortcut", + 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_p, GDK_MOD1_MASK, + "places-shortcut", + 0); + + for (i = 0; i < 10; i++) + gtk_binding_entry_add_signal (binding_set, + quick_bookmark_keyvals[i], GDK_MOD1_MASK, + "quick-bookmark", + 1, G_TYPE_INT, i); g_object_class_install_property (gobject_class, PROP_SEARCH_MODE, g_param_spec_boolean ("search-mode", @@ -7818,6 +7893,7 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, box); /* And a *lot* of callbacks to bind ... */ + gtk_widget_class_bind_template_callback (widget_class, list_popup_menu_cb); gtk_widget_class_bind_template_callback (widget_class, file_list_query_tooltip_cb); gtk_widget_class_bind_template_callback (widget_class, list_row_activated); gtk_widget_class_bind_template_callback (widget_class, list_selection_changed); @@ -7837,6 +7913,8 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) gtk_widget_class_bind_template_callback (widget_class, rename_file_end); gtk_widget_class_bind_template_callback (widget_class, click_cb); gtk_widget_class_bind_template_callback (widget_class, long_press_cb); + gtk_widget_class_bind_template_callback (widget_class, treeview_key_press_cb); + gtk_widget_class_bind_template_callback (widget_class, widget_key_press_cb); gtk_widget_class_set_css_name (widget_class, I_("filechooser")); @@ -7852,11 +7930,6 @@ post_process_ui (GtkFileChooserWidget *impl) GList *cells; GFile *file; GtkDropTarget *target; - GtkGesture *gesture; - GtkEventController *controller; - GtkShortcutTrigger *trigger; - GtkShortcutAction *action; - GtkShortcut *shortcut; /* Setup file list treeview */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); @@ -7915,20 +7988,6 @@ post_process_ui (GtkFileChooserWidget *impl) priv->item_actions); gtk_search_entry_set_key_capture_widget (GTK_SEARCH_ENTRY (priv->search_entry), priv->search_entry); - - gesture = gtk_gesture_click_new (); - gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY); - g_signal_connect (gesture, "pressed", G_CALLBACK (files_list_clicked), impl); - gtk_widget_add_controller (GTK_WIDGET (priv->browse_files_tree_view), GTK_EVENT_CONTROLLER (gesture)); - - controller = gtk_shortcut_controller_new (); - trigger = gtk_alternative_trigger_new (gtk_keyval_trigger_new (GDK_KEY_F10, GDK_SHIFT_MASK), - gtk_keyval_trigger_new (GDK_KEY_Menu, 0)); - action = gtk_callback_action_new (list_popup_menu_cb, impl, NULL); - shortcut = gtk_shortcut_new (trigger, action); - gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); - gtk_widget_add_controller (GTK_WIDGET (priv->browse_files_tree_view), controller); - } void diff --git a/gtk/gtkflowbox.c b/gtk/gtkflowbox.c index afaa8c38f2..1a269e8528 100644 --- a/gtk/gtkflowbox.c +++ b/gtk/gtkflowbox.c @@ -78,6 +78,7 @@ #include "gtkflowbox.h" #include "gtkadjustment.h" +#include "gtkbindings.h" #include "gtkcontainerprivate.h" #include "gtkcsscolorvalueprivate.h" #include "gtkcssnodeprivate.h" @@ -2986,7 +2987,7 @@ gtk_flow_box_focus (GtkWidget *widget, } static void -gtk_flow_box_add_move_binding (GtkWidgetClass *widget_class, +gtk_flow_box_add_move_binding (GtkBindingSet *binding_set, guint keyval, GdkModifierType modmask, GtkMovementStep step, @@ -2997,7 +2998,7 @@ gtk_flow_box_add_move_binding (GtkWidgetClass *widget_class, GdkModifierType modify_mod_mask = GDK_CONTROL_MASK; display = gdk_display_get_default (); - if (display != NULL) + if (display) { extend_mod_mask = gdk_keymap_get_modifier_mask (gdk_display_get_keymap (display), GDK_MODIFIER_INTENT_EXTEND_SELECTION); @@ -3005,22 +3006,26 @@ gtk_flow_box_add_move_binding (GtkWidgetClass *widget_class, GDK_MODIFIER_INTENT_MODIFY_SELECTION); } - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask, - "move-cursor", - "(ii)", step, count); - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask | extend_mod_mask, - "move-cursor", - "(ii)", step, count); - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask | modify_mod_mask, - "move-cursor", - "(ii)", step, count); - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask | extend_mod_mask | modify_mod_mask, - "move-cursor", - "(ii)", step, count); + gtk_binding_entry_add_signal (binding_set, keyval, modmask, + "move-cursor", 2, + GTK_TYPE_MOVEMENT_STEP, step, + G_TYPE_INT, count, + NULL); + gtk_binding_entry_add_signal (binding_set, keyval, modmask | extend_mod_mask, + "move-cursor", 2, + GTK_TYPE_MOVEMENT_STEP, step, + G_TYPE_INT, count, + NULL); + gtk_binding_entry_add_signal (binding_set, keyval, modmask | modify_mod_mask, + "move-cursor", 2, + GTK_TYPE_MOVEMENT_STEP, step, + G_TYPE_INT, count, + NULL); + gtk_binding_entry_add_signal (binding_set, keyval, modmask | extend_mod_mask | modify_mod_mask, + "move-cursor", 2, + GTK_TYPE_MOVEMENT_STEP, step, + G_TYPE_INT, count, + NULL); } static void @@ -3382,6 +3387,7 @@ gtk_flow_box_class_init (GtkFlowBoxClass *class) GObjectClass *object_class = G_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class); + GtkBindingSet *binding_set; object_class->finalize = gtk_flow_box_finalize; object_class->get_property = gtk_flow_box_get_property; @@ -3658,57 +3664,50 @@ gtk_flow_box_class_init (GtkFlowBoxClass *class) widget_class->activate_signal = signals[ACTIVATE_CURSOR_CHILD]; - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_Home, 0, + binding_set = gtk_binding_set_by_class (class); + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_KP_Home, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_End, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_KP_End, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_KP_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_Up, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_KP_Up, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_Down, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_KP_Down, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_Page_Up, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_MOVEMENT_PAGES, -1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0, GTK_MOVEMENT_PAGES, -1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_Page_Down, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_MOVEMENT_PAGES, 1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0, GTK_MOVEMENT_PAGES, 1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_Right, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_KP_Right, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_Left, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); - gtk_flow_box_add_move_binding (widget_class, GDK_KEY_KP_Left, 0, + gtk_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_space, GDK_CONTROL_MASK, - "toggle-cursor-child", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Space, GDK_CONTROL_MASK, - "toggle-cursor-child", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK, - "select-all", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "unselect-all", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK, + "toggle-cursor-child", 0, NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK, + "toggle-cursor-child", 0, NULL); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, + "select-all", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "unselect-all", 0); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_FLOW_BOX_ACCESSIBLE); gtk_widget_class_set_css_name (widget_class, I_("flowbox")); diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c index f3bb6ead98..ab9a9edced 100644 --- a/gtk/gtkiconview.c +++ b/gtk/gtkiconview.c @@ -21,6 +21,7 @@ #include "gtkaccessible.h" #include "gtkadjustmentprivate.h" +#include "gtkbindings.h" #include "gtkcellareabox.h" #include "gtkcellareacontext.h" #include "gtkcelllayout.h" @@ -234,7 +235,7 @@ static gboolean gtk_icon_view_unselect_all_internal (GtkIco static void gtk_icon_view_update_rubberband (GtkIconView *icon_view); static void gtk_icon_view_item_invalidate_size (GtkIconViewItem *item); static void gtk_icon_view_invalidate_sizes (GtkIconView *icon_view); -static void gtk_icon_view_add_move_binding (GtkWidgetClass *widget_class, +static void gtk_icon_view_add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask, GtkMovementStep step, @@ -336,9 +337,16 @@ G_DEFINE_TYPE_WITH_CODE (GtkIconView, gtk_icon_view, GTK_TYPE_CONTAINER, static void gtk_icon_view_class_init (GtkIconViewClass *klass) { - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + GObjectClass *gobject_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + GtkBindingSet *binding_set; + + binding_set = gtk_binding_set_by_class (klass); + + gobject_class = (GObjectClass *) klass; + widget_class = (GtkWidgetClass *) klass; + container_class = (GtkContainerClass *) klass; gobject_class->constructed = gtk_icon_view_constructed; gobject_class->dispose = gtk_icon_view_dispose; @@ -805,88 +813,70 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) _gtk_marshal_BOOLEAN__ENUM_INTv); /* Key bindings */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK, - "select-all", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "unselect-all", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_space, GDK_CONTROL_MASK, - "toggle-cursor-item", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Space, GDK_CONTROL_MASK, - "toggle-cursor-item", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_space, 0, - "activate-cursor-item", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Space, 0, - "activate-cursor-item", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Return, 0, - "activate-cursor-item", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_ISO_Enter, 0, - "activate-cursor-item", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Enter, 0, - "activate-cursor-item", - NULL); - - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Up, 0, + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, + "select-all", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "unselect-all", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK, + "toggle-cursor-item", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK, + "toggle-cursor-item", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0, + "activate-cursor-item", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0, + "activate-cursor-item", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, + "activate-cursor-item", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0, + "activate-cursor-item", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, + "activate-cursor-item", 0); + + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Up, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_KP_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Down, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Down, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_KP_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_p, GDK_CONTROL_MASK, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_p, GDK_CONTROL_MASK, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_n, GDK_CONTROL_MASK, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_n, GDK_CONTROL_MASK, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Home, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Home, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_KP_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_End, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_End, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_KP_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Page_Up, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_MOVEMENT_PAGES, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0, GTK_MOVEMENT_PAGES, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Page_Down, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_MOVEMENT_PAGES, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0, GTK_MOVEMENT_PAGES, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Right, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Left, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Right, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_KP_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); - gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Left, 0, + gtk_icon_view_add_move_binding (binding_set, GDK_KEY_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ICON_VIEW_ACCESSIBLE); @@ -3415,35 +3405,35 @@ gtk_icon_view_build_items (GtkIconView *icon_view) } static void -gtk_icon_view_add_move_binding (GtkWidgetClass *widget_class, +gtk_icon_view_add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask, GtkMovementStep step, gint count) { - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask, - I_("move-cursor"), - "(ii)", step, count); + gtk_binding_entry_add_signal (binding_set, keyval, modmask, + I_("move-cursor"), 2, + G_TYPE_ENUM, step, + G_TYPE_INT, count); - gtk_widget_class_add_binding_signal (widget_class, - keyval, GDK_SHIFT_MASK, - "move-cursor", - "(ii)", step, count); + gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK, + "move-cursor", 2, + G_TYPE_ENUM, step, + G_TYPE_INT, count); if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) return; - gtk_widget_class_add_binding_signal (widget_class, - keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "move-cursor", - "(ii)", step, count); + gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "move-cursor", 2, + G_TYPE_ENUM, step, + G_TYPE_INT, count); - gtk_widget_class_add_binding_signal (widget_class, - keyval, GDK_CONTROL_MASK, - "move-cursor", - "(ii)", step, count); + gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK, + "move-cursor", 2, + G_TYPE_ENUM, step, + G_TYPE_INT, count); } static gboolean diff --git a/gtk/gtkinfobar.c b/gtk/gtkinfobar.c index 49c98a391d..b265373831 100644 --- a/gtk/gtkinfobar.c +++ b/gtk/gtkinfobar.c @@ -39,6 +39,7 @@ #include "gtklabel.h" #include "gtkbutton.h" #include "gtkenums.h" +#include "gtkbindings.h" #include "gtkdialog.h" #include "gtkrevealer.h" #include "gtkintl.h" @@ -377,9 +378,14 @@ gtk_info_bar_dispose (GObject *object) static void gtk_info_bar_class_init (GtkInfoBarClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + GObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + GtkBindingSet *binding_set; + + object_class = G_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + container_class = GTK_CONTAINER_CLASS (klass); object_class->get_property = gtk_info_bar_get_property; object_class->set_property = gtk_info_bar_set_property; @@ -463,10 +469,9 @@ gtk_info_bar_class_init (GtkInfoBarClass *klass) NULL, G_TYPE_NONE, 0); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Escape, 0, - "close", - NULL); + binding_set = gtk_binding_set_by_class (klass); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0); gtk_widget_class_set_css_name (widget_class, I_("infobar")); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index b1f12d12e7..7cd4f4856d 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -26,6 +26,7 @@ #include "gtklabelprivate.h" +#include "gtkbindings.h" #include "gtkbuildable.h" #include "gtkbuilderprivate.h" #include "gtkcssnodeprivate.h" @@ -41,9 +42,6 @@ #include "gtknotebook.h" #include "gtkpango.h" #include "gtkprivate.h" -#include "gtkshortcut.h" -#include "gtkshortcutcontroller.h" -#include "gtkshortcuttrigger.h" #include "gtkshow.h" #include "gtksnapshot.h" #include "gtkstylecontextprivate.h" @@ -278,7 +276,7 @@ struct _GtkLabelPrivate { GtkLabelSelectionInfo *select_info; GtkWidget *mnemonic_widget; - GtkEventController *mnemonic_controller; + GtkWindow *mnemonic_window; PangoAttrList *attrs; PangoAttrList *markup_attrs; @@ -416,6 +414,7 @@ static GParamSpec *label_props[NUM_PROPERTIES] = { NULL, }; static guint signals[LAST_SIGNAL] = { 0 }; static GQuark quark_shortcuts_connected; +static GQuark quark_mnemonic_menu; static GQuark quark_mnemonics_visible_connected; static GQuark quark_gtk_signal; static GQuark quark_link; @@ -480,9 +479,7 @@ static void gtk_label_set_markup_internal (GtkLabel *label, static void gtk_label_recalculate (GtkLabel *label); static void gtk_label_root (GtkWidget *widget); static void gtk_label_unroot (GtkWidget *widget); -static void gtk_label_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters); +static gboolean gtk_label_popup_menu (GtkWidget *widget); static void gtk_label_do_popup (GtkLabel *label, double x, double y); @@ -502,7 +499,9 @@ static void gtk_label_update_active_link (GtkWidget *widget, static gboolean gtk_label_mnemonic_activate (GtkWidget *widget, gboolean group_cycling); -static void gtk_label_setup_mnemonic (GtkLabel *label); +static void gtk_label_setup_mnemonic (GtkLabel *label, + GtkWidget *toplevel, + guint last_key); static void gtk_label_buildable_interface_init (GtkBuildableIface *iface); static gboolean gtk_label_buildable_custom_tag_start (GtkBuildable *buildable, @@ -607,7 +606,7 @@ G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_WIDGET, gtk_label_buildable_interface_init)) static void -add_move_binding (GtkWidgetClass *widget_class, +add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask, GtkMovementStep step, @@ -615,16 +614,18 @@ add_move_binding (GtkWidgetClass *widget_class, { g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0); - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask, - "move-cursor", - "(iib)", step, count, FALSE); + gtk_binding_entry_add_signal (binding_set, keyval, modmask, + "move-cursor", 3, + G_TYPE_ENUM, step, + G_TYPE_INT, count, + G_TYPE_BOOLEAN, FALSE); /* Selection-extending version */ - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask | GDK_SHIFT_MASK, - "move-cursor", - "(iib)", step, count, TRUE); + gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK, + "move-cursor", 3, + G_TYPE_ENUM, step, + G_TYPE_INT, count, + G_TYPE_BOOLEAN, TRUE); } static void @@ -632,6 +633,7 @@ gtk_label_class_init (GtkLabelClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + GtkBindingSet *binding_set; gobject_class->set_property = gtk_label_set_property; gobject_class->get_property = gtk_label_get_property; @@ -647,6 +649,7 @@ gtk_label_class_init (GtkLabelClass *class) widget_class->root = gtk_label_root; widget_class->unroot = gtk_label_unroot; widget_class->mnemonic_activate = gtk_label_mnemonic_activate; + widget_class->popup_menu = gtk_label_popup_menu; widget_class->grab_focus = gtk_label_grab_focus; widget_class->focus = gtk_label_focus; widget_class->get_request_mode = gtk_label_get_request_mode; @@ -1023,127 +1026,127 @@ gtk_label_class_init (GtkLabelClass *class) g_object_class_install_properties (gobject_class, NUM_PROPERTIES, label_props); - gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_label_popup_menu); - /* * Key bindings */ - - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_F10, GDK_SHIFT_MASK, - "menu.popup", - NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_Menu, 0, - "menu.popup", - NULL); + binding_set = gtk_binding_set_by_class (class); /* Moving the insertion point */ - add_move_binding (widget_class, GDK_KEY_Right, 0, + add_move_binding (binding_set, GDK_KEY_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); - add_move_binding (widget_class, GDK_KEY_Left, 0, + add_move_binding (binding_set, GDK_KEY_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); - add_move_binding (widget_class, GDK_KEY_KP_Right, 0, + add_move_binding (binding_set, GDK_KEY_KP_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Left, 0, + add_move_binding (binding_set, GDK_KEY_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); - add_move_binding (widget_class, GDK_KEY_f, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_f, GDK_CONTROL_MASK, GTK_MOVEMENT_LOGICAL_POSITIONS, 1); - add_move_binding (widget_class, GDK_KEY_b, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_b, GDK_CONTROL_MASK, GTK_MOVEMENT_LOGICAL_POSITIONS, -1); - add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1); - add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, -1); - add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, -1); /* select all */ - gtk_widget_class_add_binding (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK, - (GtkShortcutFunc) gtk_label_select_all, - NULL); - gtk_widget_class_add_binding (widget_class, - GDK_KEY_slash, GDK_CONTROL_MASK, - (GtkShortcutFunc) gtk_label_select_all, - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, + "move-cursor", 3, + G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS, + G_TYPE_INT, -1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, + "move-cursor", 3, + G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS, + G_TYPE_INT, 1, + G_TYPE_BOOLEAN, TRUE); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, + "move-cursor", 3, + G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS, + G_TYPE_INT, -1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, + "move-cursor", 3, + G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS, + G_TYPE_INT, 1, + G_TYPE_BOOLEAN, TRUE); /* unselect all */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - "move-cursor", - "(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_backslash, GDK_CONTROL_MASK, - "move-cursor", - "(iib)", GTK_MOVEMENT_PARAGRAPH_ENDS, 0, FALSE); - - add_move_binding (widget_class, GDK_KEY_f, GDK_MOD1_MASK, + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "move-cursor", 3, + G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS, + G_TYPE_INT, 0, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK, + "move-cursor", 3, + G_TYPE_ENUM, GTK_MOVEMENT_PARAGRAPH_ENDS, + G_TYPE_INT, 0, + G_TYPE_BOOLEAN, FALSE); + + add_move_binding (binding_set, GDK_KEY_f, GDK_MOD1_MASK, GTK_MOVEMENT_WORDS, 1); - add_move_binding (widget_class, GDK_KEY_b, GDK_MOD1_MASK, + add_move_binding (binding_set, GDK_KEY_b, GDK_MOD1_MASK, GTK_MOVEMENT_WORDS, -1); - add_move_binding (widget_class, GDK_KEY_Home, 0, + add_move_binding (binding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_End, 0, + add_move_binding (binding_set, GDK_KEY_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Home, 0, + add_move_binding (binding_set, GDK_KEY_KP_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_KP_End, 0, + add_move_binding (binding_set, GDK_KEY_KP_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); - add_move_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Home, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_KP_End, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, 1); /* copy */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_c, GDK_CONTROL_MASK, - "copy-clipboard", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Return, 0, - "activate-current-link", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_ISO_Enter, 0, - "activate-current-link", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Enter, 0, - "activate-current-link", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK, + "copy-clipboard", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, + "activate-current-link", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0, + "activate-current-link", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, + "activate-current-link", 0); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LABEL_ACCESSIBLE); gtk_widget_class_set_css_name (widget_class, I_("label")); quark_shortcuts_connected = g_quark_from_static_string ("gtk-label-shortcuts-connected"); + quark_mnemonic_menu = g_quark_from_static_string ("gtk-mnemonic-menu"); quark_mnemonics_visible_connected = g_quark_from_static_string ("gtk-label-mnemonics-visible-connected"); quark_gtk_signal = g_quark_from_static_string ("gtk-signal"); quark_link = g_quark_from_static_string ("link"); @@ -1346,6 +1349,7 @@ gtk_label_init (GtkLabel *label) priv->attrs = NULL; priv->mnemonic_widget = NULL; + priv->mnemonic_window = NULL; priv->mnemonics_visible = FALSE; } @@ -1813,41 +1817,30 @@ gtk_label_mnemonic_activate (GtkWidget *widget, } static void -gtk_label_setup_mnemonic (GtkLabel *label) +gtk_label_setup_mnemonic (GtkLabel *label, + GtkWidget *toplevel, + guint last_key) { GtkLabelPrivate *priv = gtk_label_get_instance_private (label); GtkWidget *widget = GTK_WIDGET (label); - GtkShortcut *shortcut; - if (priv->mnemonic_keyval == GDK_KEY_VoidSymbol) + if (last_key != GDK_KEY_VoidSymbol) { - if (priv->mnemonic_controller) - { - gtk_widget_remove_controller (widget, priv->mnemonic_controller); - priv->mnemonic_controller = NULL; - } - return; + if (priv->mnemonic_window) + { + gtk_window_remove_mnemonic (priv->mnemonic_window, + last_key, + widget); + priv->mnemonic_window = NULL; + } } - if (priv->mnemonic_controller == NULL) - { - priv->mnemonic_controller = gtk_shortcut_controller_new (); - gtk_event_controller_set_propagation_phase (priv->mnemonic_controller, GTK_PHASE_CAPTURE); - gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (priv->mnemonic_controller), GTK_SHORTCUT_SCOPE_MANAGED); - shortcut = gtk_shortcut_new (gtk_mnemonic_trigger_new (priv->mnemonic_keyval), - gtk_mnemonic_action_new ()); - gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (priv->mnemonic_controller), shortcut); - gtk_widget_add_controller (GTK_WIDGET (label), priv->mnemonic_controller); - g_object_unref (shortcut); - } - else - { - shortcut = g_list_model_get_item (G_LIST_MODEL (priv->mnemonic_controller), 0); - gtk_shortcut_set_trigger (shortcut, gtk_mnemonic_trigger_new (priv->mnemonic_keyval)); - g_object_unref (shortcut); - } + if (priv->mnemonic_keyval == GDK_KEY_VoidSymbol) + goto done; connect_mnemonics_visible_notify (GTK_LABEL (widget)); + + done:; } static void @@ -1890,12 +1883,13 @@ static void gtk_label_root (GtkWidget *widget) { GtkLabel *label = GTK_LABEL (widget); + GtkLabelPrivate *priv = gtk_label_get_instance_private (label); GtkSettings *settings; gboolean shortcuts_connected; GTK_WIDGET_CLASS (gtk_label_parent_class)->root (widget); - gtk_label_setup_mnemonic (label); + gtk_label_setup_mnemonic (label, GTK_WIDGET (gtk_widget_get_root (widget)), priv->mnemonic_keyval); /* The PangoContext is replaced when the display changes, so clear the layouts */ gtk_label_clear_layout (GTK_LABEL (widget)); @@ -1922,8 +1916,9 @@ static void gtk_label_unroot (GtkWidget *widget) { GtkLabel *label = GTK_LABEL (widget); + GtkLabelPrivate *priv = gtk_label_get_instance_private (label); - gtk_label_setup_mnemonic (label); + gtk_label_setup_mnemonic (label, NULL, priv->mnemonic_keyval); GTK_WIDGET_CLASS (gtk_label_parent_class)->unroot (widget); } @@ -2171,7 +2166,7 @@ gtk_label_recalculate (GtkLabel *label) if (keyval != priv->mnemonic_keyval) { - gtk_label_setup_mnemonic (label); + gtk_label_setup_mnemonic (label, GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (label))), keyval); g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_MNEMONIC_KEYVAL]); } @@ -6071,14 +6066,13 @@ gtk_label_do_popup (GtkLabel *label, gtk_popover_popup (GTK_POPOVER (priv->popup_menu)); } -static void -gtk_label_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters) +static gboolean +gtk_label_popup_menu (GtkWidget *widget) { GtkLabel *label = GTK_LABEL (widget); gtk_label_do_popup (label, -1, -1); + return TRUE; } static void diff --git a/gtk/gtklinkbutton.c b/gtk/gtklinkbutton.c index 96831ec083..d64c3f3a83 100644 --- a/gtk/gtklinkbutton.c +++ b/gtk/gtklinkbutton.c @@ -123,9 +123,7 @@ static void gtk_link_button_set_property (GObject *object, const GValue *value, GParamSpec *pspec); static void gtk_link_button_clicked (GtkButton *button); -static void gtk_link_button_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters); +static gboolean gtk_link_button_popup_menu (GtkWidget *widget); static gboolean gtk_link_button_query_tooltip_cb (GtkWidget *widget, gint x, gint y, @@ -171,6 +169,8 @@ gtk_link_button_class_init (GtkLinkButtonClass *klass) gobject_class->get_property = gtk_link_button_get_property; gobject_class->finalize = gtk_link_button_finalize; + widget_class->popup_menu = gtk_link_button_popup_menu; + button_class->clicked = gtk_link_button_clicked; klass->activate_link = gtk_link_button_activate_link; @@ -230,17 +230,6 @@ gtk_link_button_class_init (GtkLinkButtonClass *klass) gtk_widget_class_install_action (widget_class, "clipboard.copy", NULL, gtk_link_button_activate_clipboard_copy); - - gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_link_button_popup_menu); - - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_F10, GDK_SHIFT_MASK, - "menu.popup", - NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_Menu, 0, - "menu.popup", - NULL); } static GMenuModel * @@ -514,12 +503,11 @@ gtk_link_button_clicked (GtkButton *button) g_signal_emit (button, link_signals[ACTIVATE_LINK], 0, &retval); } -static void -gtk_link_button_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters) +static gboolean +gtk_link_button_popup_menu (GtkWidget *widget) { gtk_link_button_do_popup (GTK_LINK_BUTTON (widget), -1, -1); + return TRUE; } /** diff --git a/gtk/gtklistbox.c b/gtk/gtklistbox.c index c5de939513..ffa4a0112c 100644 --- a/gtk/gtklistbox.c +++ b/gtk/gtklistbox.c @@ -21,6 +21,7 @@ #include "gtkactionhelperprivate.h" #include "gtkadjustmentprivate.h" +#include "gtkbindings.h" #include "gtkbuildable.h" #include "gtkcontainerprivate.h" #include "gtkcssnodeprivate.h" @@ -231,7 +232,7 @@ static GSequenceIter * gtk_list_box_get_next_visible (GtkListBo GSequenceIter *iter); static void gtk_list_box_apply_filter (GtkListBox *box, GtkListBoxRow *row); -static void gtk_list_box_add_move_binding (GtkWidgetClass *widget_class, +static void gtk_list_box_add_move_binding (GtkBindingSet *binding_set, guint keyval, GdkModifierType modmask, GtkMovementStep step, @@ -435,6 +436,9 @@ gtk_list_box_class_init (GtkListBoxClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + GtkBindingSet *binding_set; + + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LIST_BOX_ACCESSIBLE); object_class->get_property = gtk_list_box_get_property; object_class->set_property = gtk_list_box_set_property; @@ -607,52 +611,43 @@ gtk_list_box_class_init (GtkListBoxClass *klass) widget_class->activate_signal = signals[ACTIVATE_CURSOR_ROW]; - gtk_list_box_add_move_binding (widget_class, GDK_KEY_Home, 0, + binding_set = gtk_binding_set_by_class (klass); + gtk_list_box_add_move_binding (binding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_Home, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_End, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_End, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_Up, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_Up, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_Down, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_Down, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_Page_Up, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_MOVEMENT_PAGES, -1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0, GTK_MOVEMENT_PAGES, -1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_Page_Down, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_MOVEMENT_PAGES, 1); - gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, + gtk_list_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0, GTK_MOVEMENT_PAGES, 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_space, GDK_CONTROL_MASK, - "toggle-cursor-row", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Space, GDK_CONTROL_MASK, - "toggle-cursor-row", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK, - "select-all", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "unselect-all", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK, + "toggle-cursor-row", 0, NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK, + "toggle-cursor-row", 0, NULL); - gtk_widget_class_set_css_name (widget_class, I_("list")); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, + "select-all", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "unselect-all", 0); - gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LIST_BOX_ACCESSIBLE); + gtk_widget_class_set_css_name (widget_class, I_("list")); } static void @@ -1440,7 +1435,7 @@ gtk_list_box_set_accept_unpaired_release (GtkListBox *box, } static void -gtk_list_box_add_move_binding (GtkWidgetClass *widget_class, +gtk_list_box_add_move_binding (GtkBindingSet *binding_set, guint keyval, GdkModifierType modmask, GtkMovementStep step, @@ -1459,22 +1454,26 @@ gtk_list_box_add_move_binding (GtkWidgetClass *widget_class, GDK_MODIFIER_INTENT_MODIFY_SELECTION); } - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask, - "move-cursor", - "(ii)", step, count); - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask | extend_mod_mask, - "move-cursor", - "(ii)", step, count); - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask | modify_mod_mask, - "move-cursor", - "(ii)", step, count); - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask | extend_mod_mask | modify_mod_mask, - "move-cursor", - "(ii)", step, count); + gtk_binding_entry_add_signal (binding_set, keyval, modmask, + "move-cursor", 2, + GTK_TYPE_MOVEMENT_STEP, step, + G_TYPE_INT, count, + NULL); + gtk_binding_entry_add_signal (binding_set, keyval, modmask | extend_mod_mask, + "move-cursor", 2, + GTK_TYPE_MOVEMENT_STEP, step, + G_TYPE_INT, count, + NULL); + gtk_binding_entry_add_signal (binding_set, keyval, modmask | modify_mod_mask, + "move-cursor", 2, + GTK_TYPE_MOVEMENT_STEP, step, + G_TYPE_INT, count, + NULL); + gtk_binding_entry_add_signal (binding_set, keyval, modmask | extend_mod_mask | modify_mod_mask, + "move-cursor", 2, + GTK_TYPE_MOVEMENT_STEP, step, + G_TYPE_INT, count, + NULL); } static void diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 9fd83c07dc..bc164008bb 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -114,6 +114,7 @@ #include "gtkintl.h" +#include "gtkaccelmapprivate.h" #include "gtkbox.h" #include "gtkdebug.h" #include "gtkdropprivate.h" @@ -662,6 +663,8 @@ do_post_parse_initialization (void) gsk_ensure_resources (); _gtk_ensure_resources (); + _gtk_accel_map_init (); + gtk_initialized = TRUE; #ifdef G_OS_UNIX @@ -1694,11 +1697,14 @@ gtk_main_do_event (GdkEvent *event) current_events = g_list_prepend (current_events, event); if (is_pointing_event (event)) - { - target_widget = handle_pointing_event (event); - } + target_widget = handle_pointing_event (event); else if (is_key_event (event)) { + if (gdk_event_get_event_type (event) == GDK_KEY_PRESS && + GTK_IS_WINDOW (target_widget) && + gtk_window_activate_key (GTK_WINDOW (target_widget), event)) + goto cleanup; + target_widget = handle_key_event (event); } else if (is_focus_event (event)) diff --git a/gtk/gtkmnemonichash.c b/gtk/gtkmnemonichash.c new file mode 100644 index 0000000000..e6504842e1 --- /dev/null +++ b/gtk/gtkmnemonichash.c @@ -0,0 +1,201 @@ +/* gtkmnemonichash.c: Sets of mnemonics with cycling + * + * GTK - The GIMP Toolkit + * Copyright (C) 2002, Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "gtkmnemonichash.h" +#include "gtknative.h" + +struct _GtkMnemnonicHash +{ + GHashTable *hash; +}; + + +GtkMnemonicHash * +_gtk_mnemonic_hash_new (void) +{ + GtkMnemonicHash *mnemonic_hash = g_new (GtkMnemonicHash, 1); + + mnemonic_hash->hash = g_hash_table_new (g_direct_hash, NULL); + + return mnemonic_hash; +} + +static void +mnemonic_hash_free_foreach (gpointer key, + gpointer value, + gpointer user) +{ + guint keyval = GPOINTER_TO_UINT (key); + GSList *targets = value; + + gchar *name = gtk_accelerator_name (keyval, 0); + + g_warning ("mnemonic \"%s\" wasn't removed for widget (%p)", + name, targets->data); + g_free (name); + + g_slist_free (targets); +} + +void +_gtk_mnemonic_hash_free (GtkMnemonicHash *mnemonic_hash) +{ + g_hash_table_foreach (mnemonic_hash->hash, + mnemonic_hash_free_foreach, + NULL); + + g_hash_table_destroy (mnemonic_hash->hash); + g_free (mnemonic_hash); +} + +void +_gtk_mnemonic_hash_add (GtkMnemonicHash *mnemonic_hash, + guint keyval, + GtkWidget *target) +{ + gpointer key = GUINT_TO_POINTER (keyval); + GSList *targets, *new_targets; + + g_return_if_fail (GTK_IS_WIDGET (target)); + + targets = g_hash_table_lookup (mnemonic_hash->hash, key); + g_return_if_fail (g_slist_find (targets, target) == NULL); + + new_targets = g_slist_append (targets, target); + if (new_targets != targets) + g_hash_table_insert (mnemonic_hash->hash, key, new_targets); +} + +void +_gtk_mnemonic_hash_remove (GtkMnemonicHash *mnemonic_hash, + guint keyval, + GtkWidget *target) +{ + gpointer key = GUINT_TO_POINTER (keyval); + GSList *targets, *new_targets; + + g_return_if_fail (GTK_IS_WIDGET (target)); + + targets = g_hash_table_lookup (mnemonic_hash->hash, key); + + g_return_if_fail (targets && g_slist_find (targets, target) != NULL); + + new_targets = g_slist_remove (targets, target); + if (new_targets != targets) + { + if (new_targets == NULL) + g_hash_table_remove (mnemonic_hash->hash, key); + else + g_hash_table_insert (mnemonic_hash->hash, key, new_targets); + } +} + +gboolean +_gtk_mnemonic_hash_activate (GtkMnemonicHash *mnemonic_hash, + guint keyval) +{ + GSList *list, *targets; + GtkWidget *widget, *chosen_widget; + GdkSurface *surface; + gboolean overloaded; + + targets = g_hash_table_lookup (mnemonic_hash->hash, + GUINT_TO_POINTER (keyval)); + if (!targets) + return FALSE; + + overloaded = FALSE; + chosen_widget = NULL; + for (list = targets; list; list = list->next) + { + widget = GTK_WIDGET (list->data); + surface = gtk_native_get_surface (gtk_widget_get_native (widget)); + + if (gtk_widget_is_sensitive (widget) && + gtk_widget_get_mapped (widget) && + surface && gdk_surface_is_viewable (surface)) + { + if (chosen_widget) + { + overloaded = TRUE; + break; + } + else + chosen_widget = widget; + } + } + + if (chosen_widget) + { + /* For round robin we put the activated entry on + * the end of the list after activation + */ + targets = g_slist_remove (targets, chosen_widget); + targets = g_slist_append (targets, chosen_widget); + g_hash_table_insert (mnemonic_hash->hash, + GUINT_TO_POINTER (keyval), + targets); + + return gtk_widget_mnemonic_activate (chosen_widget, overloaded); + } + return FALSE; +} + +GSList * +_gtk_mnemonic_hash_lookup (GtkMnemonicHash *mnemonic_hash, + guint keyval) +{ + return g_hash_table_lookup (mnemonic_hash->hash, GUINT_TO_POINTER (keyval)); +} + +static void +mnemonic_hash_foreach_func (gpointer key, + gpointer value, + gpointer data) +{ + struct { + GtkMnemonicHashForeach func; + gpointer func_data; + } *info = data; + + guint keyval = GPOINTER_TO_UINT (key); + GSList *targets = value; + + (*info->func) (keyval, targets, info->func_data); +} + +void +_gtk_mnemonic_hash_foreach (GtkMnemonicHash *mnemonic_hash, + GtkMnemonicHashForeach func, + gpointer func_data) +{ + struct { + GtkMnemonicHashForeach func; + gpointer func_data; + } info; + + info.func = func; + info.func_data = func_data; + + g_hash_table_foreach (mnemonic_hash->hash, + mnemonic_hash_foreach_func, + &info); +} diff --git a/gtk/gtkmnemonichash.h b/gtk/gtkmnemonichash.h new file mode 100644 index 0000000000..832b2eee39 --- /dev/null +++ b/gtk/gtkmnemonichash.h @@ -0,0 +1,52 @@ +/* gtkmnemonichash.h: Sets of mnemonics with cycling + * + * GTK - The GIMP Toolkit + * Copyright (C) 2002, Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GTK_MNEMONIC_HASH_H__ +#define __GTK_MNEMONIC_HASH_H__ + +#include <gdk/gdk.h> +#include <gtk/gtkwidget.h> + +G_BEGIN_DECLS + +typedef struct _GtkMnemnonicHash GtkMnemonicHash; + +typedef void (*GtkMnemonicHashForeach) (guint keyval, + GSList *targets, + gpointer data); + +GtkMnemonicHash *_gtk_mnemonic_hash_new (void); +void _gtk_mnemonic_hash_free (GtkMnemonicHash *mnemonic_hash); +void _gtk_mnemonic_hash_add (GtkMnemonicHash *mnemonic_hash, + guint keyval, + GtkWidget *target); +void _gtk_mnemonic_hash_remove (GtkMnemonicHash *mnemonic_hash, + guint keyval, + GtkWidget *target); +gboolean _gtk_mnemonic_hash_activate (GtkMnemonicHash *mnemonic_hash, + guint keyval); +GSList * _gtk_mnemonic_hash_lookup (GtkMnemonicHash *mnemonic_hash, + guint keyval); +void _gtk_mnemonic_hash_foreach (GtkMnemonicHash *mnemonic_hash, + GtkMnemonicHashForeach func, + gpointer func_data); + +G_END_DECLS + +#endif /* __GTK_MNEMONIC_HASH_H__ */ diff --git a/gtk/gtkmodelbutton.c b/gtk/gtkmodelbutton.c index 49b69b28e8..25c75087ef 100644 --- a/gtk/gtkmodelbutton.c +++ b/gtk/gtkmodelbutton.c @@ -727,6 +727,7 @@ update_accel (GtkModelButton *self, { guint key; GdkModifierType mods; + GtkAccelLabelClass *accel_class; char *str; if (!self->accel_label) @@ -738,9 +739,12 @@ update_accel (GtkModelButton *self, } gtk_accelerator_parse (accel, &key, &mods); - str = gtk_accelerator_get_label (key, mods); + + accel_class = g_type_class_ref (GTK_TYPE_ACCEL_LABEL); + str = _gtk_accel_label_class_get_accelerator_label (accel_class, key, mods); gtk_label_set_label (GTK_LABEL (self->accel_label), str); g_free (str); + g_type_class_unref (accel_class); } else { diff --git a/gtk/gtkmountoperation.c b/gtk/gtkmountoperation.c index a3debb0861..51d926c401 100644 --- a/gtk/gtkmountoperation.c +++ b/gtk/gtkmountoperation.c @@ -57,10 +57,6 @@ #include "gtkpopover.h" #include "gtksnapshot.h" #include "gdktextureprivate.h" -#include "gtkshortcutcontroller.h" -#include "gtkshortcuttrigger.h" -#include "gtkshortcutaction.h" -#include "gtkshortcut.h" #include <glib/gprintf.h> /** @@ -1462,7 +1458,6 @@ do_popup_menu_for_process_tree_view (GtkWidget *widget, static gboolean on_popup_menu_for_process_tree_view (GtkWidget *widget, - GVariant *args, gpointer user_data) { GtkMountOperation *op = GTK_MOUNT_OPERATION (user_data); @@ -1508,10 +1503,6 @@ create_show_processes_dialog (GtkMountOperation *op, gchar *s; gboolean use_header; GtkGesture *gesture; - GtkEventController *controller; - GtkShortcutTrigger *trigger; - GtkShortcutAction *action; - GtkShortcut *shortcut; priv = op->priv; @@ -1598,15 +1589,9 @@ create_show_processes_dialog (GtkMountOperation *op, gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view); gtk_container_add (GTK_CONTAINER (vbox), scrolled_window); - controller = gtk_shortcut_controller_new (); - trigger = gtk_alternative_trigger_new (gtk_keyval_trigger_new (GDK_KEY_F10, GDK_SHIFT_MASK), - gtk_keyval_trigger_new (GDK_KEY_Menu, 0)); - action = gtk_callback_action_new (on_popup_menu_for_process_tree_view, - op, - NULL); - shortcut = gtk_shortcut_new_with_arguments (trigger, action, "s", "sv"); - gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); - gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); + g_signal_connect (tree_view, "popup-menu", + G_CALLBACK (on_popup_menu_for_process_tree_view), + op); gesture = gtk_gesture_click_new (); gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY); diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index a625013109..d20342b1f2 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -27,6 +27,7 @@ #include "gtknotebook.h" +#include "gtkbindings.h" #include "gtkbox.h" #include "gtkboxlayout.h" #include "gtkbuildable.h" @@ -772,9 +773,7 @@ static void gtk_notebook_dispose (GObject *object); /*** GtkWidget Methods ***/ static void gtk_notebook_destroy (GtkWidget *widget); static void gtk_notebook_unmap (GtkWidget *widget); -static void gtk_notebook_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters); +static gboolean gtk_notebook_popup_menu (GtkWidget *widget); static void gtk_notebook_motion (GtkEventController *controller, double x, double y, @@ -936,53 +935,49 @@ G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER, gtk_notebook_buildable_init)) static void -add_tab_bindings (GtkWidgetClass *widget_class, +add_tab_bindings (GtkBindingSet *binding_set, GdkModifierType modifiers, GtkDirectionType direction) { - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Tab, modifiers, - "move_focus_out", - "(i)", direction); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Tab, modifiers, - "move_focus_out", - "(i)", direction); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers, + "move_focus_out", 1, + GTK_TYPE_DIRECTION_TYPE, direction); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers, + "move_focus_out", 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, GDK_CONTROL_MASK, - "move_focus_out", - "(i)", direction); - gtk_widget_class_add_binding_signal (widget_class, - keypad_keysym, GDK_CONTROL_MASK, - "move_focus_out", - "(i)", direction); + gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK, + "move_focus_out", 1, + GTK_TYPE_DIRECTION_TYPE, direction); + gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_CONTROL_MASK, + "move_focus_out", 1, + GTK_TYPE_DIRECTION_TYPE, direction); } static void -add_reorder_bindings (GtkWidgetClass *widget_class, +add_reorder_bindings (GtkBindingSet *binding_set, guint keysym, GtkDirectionType direction, gboolean move_to_last) { guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left; - gtk_widget_class_add_binding_signal (widget_class, - keysym, GDK_MOD1_MASK, - "reorder_tab", - "(ib)", direction, move_to_last); - gtk_widget_class_add_binding_signal (widget_class, - keypad_keysym, GDK_MOD1_MASK, - "reorder_tab", - "(ib)", direction, move_to_last); + gtk_binding_entry_add_signal (binding_set, keysym, GDK_MOD1_MASK, + "reorder_tab", 2, + GTK_TYPE_DIRECTION_TYPE, direction, + G_TYPE_BOOLEAN, move_to_last); + gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_MOD1_MASK, + "reorder_tab", 2, + GTK_TYPE_DIRECTION_TYPE, direction, + G_TYPE_BOOLEAN, move_to_last); } static gboolean @@ -1040,6 +1035,7 @@ gtk_notebook_class_init (GtkNotebookClass *class) GObjectClass *gobject_class = G_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class); + GtkBindingSet *binding_set; gobject_class->set_property = gtk_notebook_set_property; gobject_class->get_property = gtk_notebook_get_property; @@ -1048,6 +1044,7 @@ gtk_notebook_class_init (GtkNotebookClass *class) widget_class->destroy = gtk_notebook_destroy; widget_class->unmap = gtk_notebook_unmap; + widget_class->popup_menu = gtk_notebook_popup_menu; widget_class->grab_notify = gtk_notebook_grab_notify; widget_class->state_flags_changed = gtk_notebook_state_flags_changed; widget_class->direction_changed = gtk_notebook_direction_changed; @@ -1311,77 +1308,67 @@ gtk_notebook_class_init (GtkNotebookClass *class) G_TYPE_FROM_CLASS (gobject_class), _gtk_marshal_OBJECT__OBJECTv); - gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_notebook_popup_menu); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_space, 0, - "select-page", - "(b)", FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Space, 0, - "select-page", - "(b)", FALSE); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Home, 0, - "focus-tab", - "(i)", GTK_NOTEBOOK_TAB_FIRST); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Home, 0, - "focus-tab", - "(i)", GTK_NOTEBOOK_TAB_FIRST); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_End, 0, - "focus-tab", - "(i)", GTK_NOTEBOOK_TAB_LAST); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_End, 0, - "focus-tab", - "(i)", GTK_NOTEBOOK_TAB_LAST); - - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_F10, GDK_SHIFT_MASK, - "menu.popup", - NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_Menu, 0, - "menu.popup", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Up, GDK_CONTROL_MASK, - "change-current-page", - "(i)", -1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Down, GDK_CONTROL_MASK, - "change-current-page", - "(i)", 1); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Up, GDK_CONTROL_MASK | GDK_MOD1_MASK, - "change-current-page", - "(i)", -1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Down, GDK_CONTROL_MASK | GDK_MOD1_MASK, - "change-current-page", - "(i)", 1); - - 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_reorder_bindings (widget_class, GDK_KEY_Up, GTK_DIR_UP, FALSE); - add_reorder_bindings (widget_class, GDK_KEY_Down, GTK_DIR_DOWN, FALSE); - add_reorder_bindings (widget_class, GDK_KEY_Left, GTK_DIR_LEFT, FALSE); - add_reorder_bindings (widget_class, GDK_KEY_Right, GTK_DIR_RIGHT, FALSE); - add_reorder_bindings (widget_class, GDK_KEY_Home, GTK_DIR_LEFT, TRUE); - add_reorder_bindings (widget_class, GDK_KEY_Home, GTK_DIR_UP, TRUE); - add_reorder_bindings (widget_class, GDK_KEY_End, GTK_DIR_RIGHT, TRUE); - add_reorder_bindings (widget_class, GDK_KEY_End, GTK_DIR_DOWN, TRUE); - - add_tab_bindings (widget_class, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD); - add_tab_bindings (widget_class, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD); + binding_set = gtk_binding_set_by_class (class); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_space, 0, + "select-page", 1, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_KP_Space, 0, + "select-page", 1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Home, 0, + "focus-tab", 1, + GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_KP_Home, 0, + "focus-tab", 1, + GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_End, 0, + "focus-tab", 1, + GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_KP_End, 0, + "focus-tab", 1, + GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST); + + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Page_Up, GDK_CONTROL_MASK, + "change-current-page", 1, + G_TYPE_INT, -1); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Page_Down, GDK_CONTROL_MASK, + "change-current-page", 1, + G_TYPE_INT, 1); + + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Page_Up, GDK_CONTROL_MASK | GDK_MOD1_MASK, + "change-current-page", 1, + G_TYPE_INT, -1); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Page_Down, GDK_CONTROL_MASK | GDK_MOD1_MASK, + "change-current-page", 1, + G_TYPE_INT, 1); + + 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_reorder_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP, FALSE); + add_reorder_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN, FALSE); + add_reorder_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT, FALSE); + add_reorder_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT, FALSE); + add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_LEFT, TRUE); + add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_UP, TRUE); + add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_RIGHT, TRUE); + add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_DOWN, TRUE); + + add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD); + 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_NOTEBOOK_ACCESSIBLE); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); @@ -2611,16 +2598,20 @@ gtk_notebook_gesture_pressed (GtkGestureClick *gesture, } } -static void -gtk_notebook_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters) + +static gboolean +gtk_notebook_popup_menu (GtkWidget *widget) { GtkNotebook *notebook = GTK_NOTEBOOK (widget); GtkNotebookPrivate *priv = notebook->priv; if (priv->menu) - gtk_popover_popup (GTK_POPOVER (priv->menu)); + { + gtk_popover_popup (GTK_POPOVER (priv->menu)); + return TRUE; + } + + return FALSE; } static void diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c index 96c937ca13..b396957906 100644 --- a/gtk/gtkpaned.c +++ b/gtk/gtkpaned.c @@ -26,6 +26,7 @@ #include "gtkpaned.h" +#include "gtkbindings.h" #include "gtkcontainerprivate.h" #include "gtkcssnodeprivate.h" #include "gtkcssstylepropertyprivate.h" @@ -277,29 +278,24 @@ static guint signals[LAST_SIGNAL] = { 0 }; static GParamSpec *paned_props[LAST_PROP] = { NULL, }; static void -add_tab_bindings (GtkWidgetClass *widget_class, - GdkModifierType modifiers) +add_tab_bindings (GtkBindingSet *binding_set, + GdkModifierType modifiers) { - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Tab, modifiers, - "toggle-handle-focus", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Tab, modifiers, - "toggle-handle-focus", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers, + "toggle-handle-focus", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers, + "toggle-handle-focus", 0); } static void -add_move_binding (GtkWidgetClass *widget_class, +add_move_binding (GtkBindingSet *binding_set, guint keyval, GdkModifierType mask, GtkScrollType scroll) { - gtk_widget_class_add_binding_signal (widget_class, - keyval, mask, - "move-handle", - "(i)", scroll); + gtk_binding_entry_add_signal (binding_set, keyval, mask, + "move-handle", 1, + GTK_TYPE_SCROLL_TYPE, scroll); } static void @@ -341,9 +337,16 @@ gtk_paned_handle_contains (GtkGizmo *handle, static void gtk_paned_class_init (GtkPanedClass *class) { - GObjectClass *object_class = G_OBJECT_CLASS (class); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); - GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class); + GObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + GtkPanedClass *paned_class; + GtkBindingSet *binding_set; + + object_class = (GObjectClass *) class; + widget_class = (GtkWidgetClass *) class; + container_class = (GtkContainerClass *) class; + paned_class = (GtkPanedClass *) class; object_class->set_property = gtk_paned_set_property; object_class->get_property = gtk_paned_get_property; @@ -361,12 +364,12 @@ gtk_paned_class_init (GtkPanedClass *class) container_class->child_type = gtk_paned_child_type; container_class->set_focus_child = gtk_paned_set_focus_child; - class->cycle_child_focus = gtk_paned_cycle_child_focus; - class->toggle_handle_focus = gtk_paned_toggle_handle_focus; - class->move_handle = gtk_paned_move_handle; - class->cycle_handle_focus = gtk_paned_cycle_handle_focus; - class->accept_position = gtk_paned_accept_position; - class->cancel_position = gtk_paned_cancel_position; + paned_class->cycle_child_focus = gtk_paned_cycle_child_focus; + paned_class->toggle_handle_focus = gtk_paned_toggle_handle_focus; + paned_class->move_handle = gtk_paned_move_handle; + paned_class->cycle_handle_focus = gtk_paned_cycle_handle_focus; + paned_class->accept_position = gtk_paned_accept_position; + paned_class->cancel_position = gtk_paned_cancel_position; paned_props[PROP_POSITION] = @@ -604,87 +607,84 @@ gtk_paned_class_init (GtkPanedClass *class) _gtk_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN, 0); + binding_set = gtk_binding_set_by_class (class); + /* F6 and friends */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_F6, 0, - "cycle-child-focus", - "(b)", FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_F6, GDK_SHIFT_MASK, - "cycle-child-focus", - "(b)", TRUE); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_F6, 0, + "cycle-child-focus", 1, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_F6, GDK_SHIFT_MASK, + "cycle-child-focus", 1, + G_TYPE_BOOLEAN, TRUE); /* F8 and friends */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_F8, 0, - "cycle-handle-focus", - "(b)", FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_F8, GDK_SHIFT_MASK, - "cycle-handle-focus", - "(b)", TRUE); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_F8, 0, + "cycle-handle-focus", 1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_F8, GDK_SHIFT_MASK, + "cycle-handle-focus", 1, + G_TYPE_BOOLEAN, TRUE); - add_tab_bindings (widget_class, 0); - add_tab_bindings (widget_class, GDK_CONTROL_MASK); - add_tab_bindings (widget_class, GDK_SHIFT_MASK); - add_tab_bindings (widget_class, GDK_CONTROL_MASK | GDK_SHIFT_MASK); + add_tab_bindings (binding_set, 0); + add_tab_bindings (binding_set, GDK_CONTROL_MASK); + add_tab_bindings (binding_set, GDK_SHIFT_MASK); + add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK); /* accept and cancel positions */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Escape, 0, - "cancel-position", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Return, 0, - "accept-position", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_ISO_Enter, 0, - "accept-position", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Enter, 0, - "accept-position", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_space, 0, - "accept-position", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Space, 0, - "accept-position", - NULL); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Escape, 0, + "cancel-position", 0); + + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Return, 0, + "accept-position", 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_ISO_Enter, 0, + "accept-position", 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_KP_Enter, 0, + "accept-position", 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_space, 0, + "accept-position", 0); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_KP_Space, 0, + "accept-position", 0); /* move handle */ - add_move_binding (widget_class, GDK_KEY_Left, 0, GTK_SCROLL_STEP_LEFT); - add_move_binding (widget_class, GDK_KEY_KP_Left, 0, GTK_SCROLL_STEP_LEFT); - add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT); - add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT); - - add_move_binding (widget_class, GDK_KEY_Right, 0, GTK_SCROLL_STEP_RIGHT); - add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT); - add_move_binding (widget_class, GDK_KEY_KP_Right, 0, GTK_SCROLL_STEP_RIGHT); - add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT); - - add_move_binding (widget_class, GDK_KEY_Up, 0, GTK_SCROLL_STEP_UP); - add_move_binding (widget_class, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP); - add_move_binding (widget_class, GDK_KEY_KP_Up, 0, GTK_SCROLL_STEP_UP); - add_move_binding (widget_class, GDK_KEY_KP_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP); - add_move_binding (widget_class, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_UP); - add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP); - - add_move_binding (widget_class, GDK_KEY_Down, 0, GTK_SCROLL_STEP_DOWN); - add_move_binding (widget_class, GDK_KEY_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN); - add_move_binding (widget_class, GDK_KEY_KP_Down, 0, GTK_SCROLL_STEP_DOWN); - add_move_binding (widget_class, GDK_KEY_KP_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN); - add_move_binding (widget_class, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT); - add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT); - - add_move_binding (widget_class, GDK_KEY_Home, 0, GTK_SCROLL_START); - add_move_binding (widget_class, GDK_KEY_KP_Home, 0, GTK_SCROLL_START); - add_move_binding (widget_class, GDK_KEY_End, 0, GTK_SCROLL_END); - add_move_binding (widget_class, GDK_KEY_KP_End, 0, GTK_SCROLL_END); + add_move_binding (binding_set, GDK_KEY_Left, 0, GTK_SCROLL_STEP_LEFT); + add_move_binding (binding_set, GDK_KEY_KP_Left, 0, GTK_SCROLL_STEP_LEFT); + add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT); + add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT); + + add_move_binding (binding_set, GDK_KEY_Right, 0, GTK_SCROLL_STEP_RIGHT); + add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT); + add_move_binding (binding_set, GDK_KEY_KP_Right, 0, GTK_SCROLL_STEP_RIGHT); + add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT); + + add_move_binding (binding_set, GDK_KEY_Up, 0, GTK_SCROLL_STEP_UP); + add_move_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP); + add_move_binding (binding_set, GDK_KEY_KP_Up, 0, GTK_SCROLL_STEP_UP); + add_move_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP); + add_move_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_UP); + add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP); + + add_move_binding (binding_set, GDK_KEY_Down, 0, GTK_SCROLL_STEP_DOWN); + add_move_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN); + add_move_binding (binding_set, GDK_KEY_KP_Down, 0, GTK_SCROLL_STEP_DOWN); + add_move_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN); + add_move_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT); + add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT); + + add_move_binding (binding_set, GDK_KEY_Home, 0, GTK_SCROLL_START); + add_move_binding (binding_set, GDK_KEY_KP_Home, 0, GTK_SCROLL_START); + add_move_binding (binding_set, GDK_KEY_End, 0, GTK_SCROLL_END); + add_move_binding (binding_set, GDK_KEY_KP_End, 0, GTK_SCROLL_END); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_PANED_ACCESSIBLE); gtk_widget_class_set_css_name (widget_class, I_("paned")); diff --git a/gtk/gtkpasswordentry.c b/gtk/gtkpasswordentry.c index 0ed1fe043f..f39531639b 100644 --- a/gtk/gtkpasswordentry.c +++ b/gtk/gtkpasswordentry.c @@ -23,6 +23,7 @@ #include "gtkpasswordentry.h" #include "gtkaccessible.h" +#include "gtkbindings.h" #include "gtktextprivate.h" #include "gtkeditable.h" #include "gtkgestureclick.h" diff --git a/gtk/gtkplacesview.c b/gtk/gtkplacesview.c index 61938f345c..b7b431a944 100644 --- a/gtk/gtkplacesview.c +++ b/gtk/gtkplacesview.c @@ -106,15 +106,7 @@ static void mount_volume (GtkPlacesView static void on_eject_button_clicked (GtkWidget *widget, GtkPlacesViewRow *row); -static gboolean on_row_popup_menu (GtkWidget *widget, - GVariant *args, - gpointer user_data); - -static void click_cb (GtkGesture *gesture, - int n_press, - double x, - double y, - gpointer user_data); +static gboolean on_row_popup_menu (GtkPlacesViewRow *row); static void populate_servers (GtkPlacesView *view); @@ -681,28 +673,12 @@ insert_row (GtkPlacesView *view, gboolean is_network) { GtkPlacesViewPrivate *priv; - GtkEventController *controller; - GtkShortcutTrigger *trigger; - GtkShortcutAction *action; - GtkShortcut *shortcut; - GtkGesture *gesture; priv = gtk_places_view_get_instance_private (view); g_object_set_data (G_OBJECT (row), "is-network", GINT_TO_POINTER (is_network)); - controller = gtk_shortcut_controller_new (); - trigger = gtk_alternative_trigger_new (gtk_keyval_trigger_new (GDK_KEY_F10, GDK_SHIFT_MASK), - gtk_keyval_trigger_new (GDK_KEY_Menu, 0)); - action = gtk_callback_action_new (on_row_popup_menu, row, NULL); - shortcut = gtk_shortcut_new (trigger, action); - gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); - gtk_widget_add_controller (GTK_WIDGET (row), controller); - - gesture = gtk_gesture_click_new (); - gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY); - g_signal_connect (gesture, "pressed", G_CALLBACK (click_cb), row); - gtk_widget_add_controller (row, GTK_EVENT_CONTROLLER (gesture)); + g_signal_connect (row, "popup-menu", G_CALLBACK (on_row_popup_menu), row); g_signal_connect (gtk_places_view_row_get_eject_button (GTK_PLACES_VIEW_ROW (row)), "clicked", @@ -1723,12 +1699,10 @@ get_menu_model (void) return G_MENU_MODEL (menu); } -static gboolean -on_row_popup_menu (GtkWidget *widget, - GVariant *args, - gpointer user_data) +static void +popup_menu (GtkPlacesViewRow *row, + GdkEventButton *event) { - GtkPlacesViewRow *row = GTK_PLACES_VIEW_ROW (widget); GtkPlacesViewPrivate *priv; GtkWidget *view; GMount *mount; @@ -1756,39 +1730,27 @@ on_row_popup_menu (GtkWidget *widget, GMenuModel *model = get_menu_model (); priv->popup_menu = gtk_popover_menu_new_from_model (model); + gtk_widget_set_parent (priv->popup_menu, GTK_WIDGET (view)); gtk_popover_set_position (GTK_POPOVER (priv->popup_menu), GTK_POS_BOTTOM); gtk_popover_set_has_arrow (GTK_POPOVER (priv->popup_menu), FALSE); - gtk_widget_set_halign (priv->popup_menu, GTK_ALIGN_CENTER); + gtk_widget_set_halign (priv->popup_menu, GTK_ALIGN_START); g_object_unref (model); } - if (priv->row_for_action) - g_object_set_data (G_OBJECT (priv->row_for_action), "menu", NULL); - - g_object_ref (priv->popup_menu); - gtk_widget_unparent (priv->popup_menu); + gtk_widget_set_halign (priv->popup_menu, GTK_ALIGN_CENTER); gtk_widget_set_parent (priv->popup_menu, GTK_WIDGET (row)); - g_object_unref (priv->popup_menu); priv->row_for_action = row; - if (priv->row_for_action) - g_object_set_data (G_OBJECT (priv->row_for_action), "menu", priv->popup_menu); - gtk_popover_popup (GTK_POPOVER (priv->popup_menu)); - - return TRUE; } -static void -click_cb (GtkGesture *gesture, - int n_press, - double x, - double y, - gpointer user_data) +static gboolean +on_row_popup_menu (GtkPlacesViewRow *row) { - on_row_popup_menu (GTK_WIDGET (user_data), NULL, NULL); + popup_menu (row, NULL); + return TRUE; } static gboolean diff --git a/gtk/gtkplacesviewrow.c b/gtk/gtkplacesviewrow.c index e3deff9a84..3df575aa42 100644 --- a/gtk/gtkplacesviewrow.c +++ b/gtk/gtkplacesviewrow.c @@ -35,7 +35,6 @@ #include "gtkspinner.h" #include "gtkstack.h" #include "gtktypebuiltins.h" -#include "gtknative.h" #else #include <gtk/gtk.h> #endif @@ -195,6 +194,18 @@ measure_available_space (GtkPlacesViewRow *row) } static void +pressed_cb (GtkGesture *gesture, + int n_pressed, + double x, + double y, + GtkPlacesViewRow *row) +{ + gboolean menu_activated; + + g_signal_emit_by_name (row, "popup-menu", &menu_activated); +} + +static void gtk_places_view_row_finalize (GObject *object) { GtkPlacesViewRow *self = GTK_PLACES_VIEW_ROW (object); @@ -310,19 +321,6 @@ gtk_places_view_row_set_property (GObject *object, } static void -gtk_places_view_row_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - GtkWidget *menu = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "menu")); - - GTK_WIDGET_CLASS (gtk_places_view_row_parent_class)->size_allocate (widget, width, height, baseline); - if (menu) - gtk_native_check_resize (GTK_NATIVE (menu)); -} - -static void gtk_places_view_row_class_init (GtkPlacesViewRowClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); @@ -332,8 +330,6 @@ gtk_places_view_row_class_init (GtkPlacesViewRowClass *klass) object_class->get_property = gtk_places_view_row_get_property; object_class->set_property = gtk_places_view_row_set_property; - widget_class->size_allocate = gtk_places_view_row_size_allocate; - properties[PROP_ICON] = g_param_spec_object ("icon", P_("Icon of the row"), @@ -395,6 +391,8 @@ gtk_places_view_row_class_init (GtkPlacesViewRowClass *klass) gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, icon_image); gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, name_label); gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, path_label); + + gtk_widget_class_bind_template_callback (widget_class, pressed_cb); } static void diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c index 5644413929..ef7253d123 100644 --- a/gtk/gtkpopover.c +++ b/gtk/gtkpopover.c @@ -103,9 +103,11 @@ #include "gtkwidgetprivate.h" #include "gtkeventcontrollerkey.h" #include "gtkcssnodeprivate.h" +#include "gtkbindings.h" #include "gtkbinlayout.h" #include "gtkenums.h" #include "gtktypebuiltins.h" +#include "gtkmnemonichash.h" #include "gtkgizmoprivate.h" #include "gtkintl.h" #include "gtkprivate.h" @@ -119,8 +121,6 @@ #include "gtkcsscolorvalueprivate.h" #include "gtkcssnumbervalueprivate.h" #include "gtksnapshot.h" -#include "gtkshortcut.h" -#include "gtkshortcuttrigger.h" #include "gtkrender.h" #include "gtkstylecontextprivate.h" @@ -1445,41 +1445,37 @@ gtk_popover_remove (GtkContainer *container, } static void -add_tab_bindings (GtkWidgetClass *widget_class, +add_tab_bindings (GtkBindingSet *binding_set, GdkModifierType modifiers, GtkDirectionType direction) { - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Tab, modifiers, - "move-focus", - "(i)", direction); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Tab, modifiers, - "move-focus", - "(i)", direction); + 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 void @@ -1488,6 +1484,7 @@ gtk_popover_class_init (GtkPopoverClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + GtkBindingSet *binding_set; object_class->dispose = gtk_popover_dispose; object_class->finalize = gtk_popover_finalize; @@ -1566,22 +1563,24 @@ gtk_popover_class_init (GtkPopoverClass *klass) G_TYPE_NONE, 0); - 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_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); + binding_set = gtk_binding_set_by_class (klass); + + 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_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_widget_class_set_css_name (widget_class, "popover"); } diff --git a/gtk/gtkpopovermenu.c b/gtk/gtkpopovermenu.c index edfbdb078c..d9779bd2f7 100644 --- a/gtk/gtkpopovermenu.c +++ b/gtk/gtkpopovermenu.c @@ -32,6 +32,7 @@ #include "gtkeventcontrollermotion.h" #include "gtkmain.h" #include "gtktypebuiltins.h" +#include "gtkbindings.h" #include "gtkmodelbuttonprivate.h" #include "gtkpopovermenubar.h" @@ -361,37 +362,37 @@ gtk_popover_menu_focus (GtkWidget *widget, static void -add_tab_bindings (GtkWidgetClass *widget_class, +add_tab_bindings (GtkBindingSet *binding_set, GdkModifierType modifiers, GtkDirectionType direction) { - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Tab, modifiers, - "move-focus", - "(i)", direction); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Tab, modifiers, - "move-focus", - "(i)", direction); + 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 void @@ -407,6 +408,7 @@ gtk_popover_menu_class_init (GtkPopoverMenuClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkBindingSet *binding_set; object_class->dispose = gtk_popover_menu_dispose; object_class->set_property = gtk_popover_menu_set_property; @@ -433,26 +435,28 @@ gtk_popover_menu_class_init (GtkPopoverMenuClass *klass) G_TYPE_MENU_MODEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - 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_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_space, 0, - "activate-default", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, 0, - "activate-default", NULL); + binding_set = gtk_binding_set_by_class (klass); + + 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_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_space, 0, + "activate-default", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0, + "activate-default", 0); } /** diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c index b204596650..7ab475d2a3 100644 --- a/gtk/gtkrange.c +++ b/gtk/gtkrange.c @@ -1883,6 +1883,16 @@ gtk_range_click_gesture_pressed (GtkGestureClick *gesture, mouse_location == priv->highlight_widget) mouse_location = priv->trough_widget; + if (mouse_location == priv->slider_widget && + gdk_event_triggers_context_menu (event)) + { + gboolean handled; + + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + g_signal_emit_by_name (widget, "popup-menu", &handled); + return; + } + if (mouse_location == priv->slider_widget) { /* Shift-click in the slider = fine adjustment */ diff --git a/gtk/gtkroot.c b/gtk/gtkroot.c index 3e6a7b78b6..dce284a57f 100644 --- a/gtk/gtkroot.c +++ b/gtk/gtkroot.c @@ -27,8 +27,6 @@ #include "gtkprivate.h" #include "gtkintl.h" -#include "gtkshortcutmanager.h" - /** * SECTION:gtkroot * @Title: GtkRoot diff --git a/gtk/gtkscale.c b/gtk/gtkscale.c index 9583c3ea9c..244c095251 100644 --- a/gtk/gtkscale.c +++ b/gtk/gtkscale.c @@ -28,6 +28,7 @@ #include "gtkscale.h" #include "gtkadjustment.h" +#include "gtkbindings.h" #include "gtkbuildable.h" #include "gtkbuilderprivate.h" #include "gtkgizmoprivate.h" @@ -622,11 +623,10 @@ gtk_scale_size_allocate (GtkWidget *widget, } } -#define add_slider_binding(binding_set, keyval, mask, scroll) \ - gtk_widget_class_add_binding_signal (widget_class, \ - keyval, mask, \ - I_("move-slider"), \ - "(i)", scroll) +#define add_slider_binding(binding_set, keyval, mask, scroll) \ + gtk_binding_entry_add_signal (binding_set, keyval, mask, \ + I_("move-slider"), 1, \ + GTK_TYPE_SCROLL_TYPE, scroll) static void gtk_scale_value_changed (GtkRange *range) @@ -650,6 +650,7 @@ gtk_scale_class_init (GtkScaleClass *class) GObjectClass *gobject_class; GtkWidgetClass *widget_class; GtkRangeClass *range_class; + GtkBindingSet *binding_set; gobject_class = G_OBJECT_CLASS (class); range_class = (GtkRangeClass*) class; @@ -705,6 +706,8 @@ gtk_scale_class_init (GtkScaleClass *class) * blind users etc. don't care about scale orientation. */ + binding_set = gtk_binding_set_by_class (class); + add_slider_binding (binding_set, GDK_KEY_Left, 0, GTK_SCROLL_STEP_LEFT); diff --git a/gtk/gtkscalebutton.c b/gtk/gtkscalebutton.c index 76c2173d49..3b41e6a64c 100644 --- a/gtk/gtkscalebutton.c +++ b/gtk/gtkscalebutton.c @@ -37,6 +37,7 @@ #include "gtkscalebutton.h" #include "gtkadjustment.h" +#include "gtkbindings.h" #include "gtkbox.h" #include "gtkbuttonprivate.h" #include "gtkimage.h" @@ -170,6 +171,7 @@ gtk_scale_button_class_init (GtkScaleButtonClass *klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass); + GtkBindingSet *binding_set; gobject_class->constructed = gtk_scale_button_constructed; gobject_class->finalize = gtk_scale_button_finalize; @@ -296,30 +298,20 @@ gtk_scale_button_class_init (GtkScaleButtonClass *klass) G_TYPE_NONE, 0); /* Key bindings */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_space, 0, - "popup", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Space, 0, - "popup", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Return, 0, - "popup", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_ISO_Enter, 0, - "popup", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Enter, 0, - "popup", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Escape, 0, - "popdown", - NULL); + binding_set = gtk_binding_set_by_class (widget_class); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0, + "popup", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0, + "popup", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, + "popup", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0, + "popup", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, + "popup", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, + "popdown", 0); /* Bind class to template */ diff --git a/gtk/gtkscrollbar.c b/gtk/gtkscrollbar.c index f97d3d7647..432000ae5f 100644 --- a/gtk/gtkscrollbar.c +++ b/gtk/gtkscrollbar.c @@ -209,6 +209,16 @@ gtk_scrollbar_class_init (GtkScrollbarClass *class) gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); } +static gboolean +emit_popup_menu (GtkWidget *self) +{ + gboolean handled; + + g_signal_emit_by_name (self, "popup-menu", &handled); + + return handled; +} + static void gtk_scrollbar_init (GtkScrollbar *self) { @@ -217,6 +227,7 @@ gtk_scrollbar_init (GtkScrollbar *self) priv->orientation = GTK_ORIENTATION_HORIZONTAL; priv->range = g_object_new (GTK_TYPE_RANGE, NULL); + g_signal_connect_swapped (priv->range, "popup-menu", G_CALLBACK (emit_popup_menu), self); gtk_widget_set_hexpand (priv->range, TRUE); gtk_widget_set_vexpand (priv->range, TRUE); gtk_widget_set_parent (priv->range, GTK_WIDGET (self)); diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index 3bb8d023b8..6debc150ae 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -28,6 +28,7 @@ #include "gtkadjustment.h" #include "gtkadjustmentprivate.h" +#include "gtkbindings.h" #include "gtkeventcontrollermotion.h" #include "gtkeventcontrollerscroll.h" #include "gtkgesturedrag.h" @@ -412,7 +413,7 @@ static GParamSpec *properties[NUM_PROPERTIES]; G_DEFINE_TYPE_WITH_PRIVATE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN) static void -add_scroll_binding (GtkWidgetClass *widget_class, +add_scroll_binding (GtkBindingSet *binding_set, guint keyval, GdkModifierType mask, GtkScrollType scroll, @@ -420,29 +421,27 @@ add_scroll_binding (GtkWidgetClass *widget_class, { guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left; - gtk_widget_class_add_binding_signal (widget_class, - keyval, mask, - "scroll-child", - "(ib)", scroll, horizontal); - gtk_widget_class_add_binding_signal (widget_class, - keypad_keyval, mask, - "scroll-child", - "(ib)", scroll, horizontal); + gtk_binding_entry_add_signal (binding_set, keyval, mask, + "scroll-child", 2, + GTK_TYPE_SCROLL_TYPE, scroll, + G_TYPE_BOOLEAN, horizontal); + gtk_binding_entry_add_signal (binding_set, keypad_keyval, mask, + "scroll-child", 2, + GTK_TYPE_SCROLL_TYPE, scroll, + G_TYPE_BOOLEAN, horizontal); } static void -add_tab_bindings (GtkWidgetClass *widget_class, +add_tab_bindings (GtkBindingSet *binding_set, GdkModifierType modifiers, GtkDirectionType direction) { - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Tab, modifiers, - "move-focus-out", - "(i)", direction); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Tab, modifiers, - "move-focus-out", - "(i)", direction); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers, + "move-focus-out", 1, + GTK_TYPE_DIRECTION_TYPE, direction); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers, + "move-focus-out", 1, + GTK_TYPE_DIRECTION_TYPE, direction); } static void @@ -518,6 +517,7 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class) GObjectClass *gobject_class = G_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class); + GtkBindingSet *binding_set; gobject_class->set_property = gtk_scrolled_window_set_property; gobject_class->get_property = gtk_scrolled_window_get_property; @@ -789,23 +789,25 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class) NULL, NULL, NULL, G_TYPE_NONE, 1, GTK_TYPE_POSITION_TYPE); - add_scroll_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, TRUE); - add_scroll_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD, TRUE); - add_scroll_binding (widget_class, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, FALSE); - add_scroll_binding (widget_class, GDK_KEY_Down, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD, FALSE); + binding_set = gtk_binding_set_by_class (class); - add_scroll_binding (widget_class, GDK_KEY_Page_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_BACKWARD, TRUE); - add_scroll_binding (widget_class, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_FORWARD, TRUE); - add_scroll_binding (widget_class, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_BACKWARD, FALSE); - add_scroll_binding (widget_class, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_FORWARD, FALSE); + add_scroll_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, TRUE); + add_scroll_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD, TRUE); + add_scroll_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, FALSE); + add_scroll_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD, FALSE); - add_scroll_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START, TRUE); - add_scroll_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK, GTK_SCROLL_END, TRUE); - add_scroll_binding (widget_class, GDK_KEY_Home, 0, GTK_SCROLL_START, FALSE); - add_scroll_binding (widget_class, GDK_KEY_End, 0, GTK_SCROLL_END, FALSE); + add_scroll_binding (binding_set, GDK_KEY_Page_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_BACKWARD, TRUE); + add_scroll_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_FORWARD, TRUE); + add_scroll_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_BACKWARD, FALSE); + add_scroll_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_FORWARD, FALSE); - add_tab_bindings (widget_class, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD); - add_tab_bindings (widget_class, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD); + add_scroll_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START, TRUE); + add_scroll_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK, GTK_SCROLL_END, TRUE); + add_scroll_binding (binding_set, GDK_KEY_Home, 0, GTK_SCROLL_START, FALSE); + add_scroll_binding (binding_set, GDK_KEY_End, 0, GTK_SCROLL_END, FALSE); + + add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD); + 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_SCROLLED_WINDOW_ACCESSIBLE); gtk_widget_class_set_css_name (widget_class, I_("scrolledwindow")); diff --git a/gtk/gtksearchentry.c b/gtk/gtksearchentry.c index 5d92aeebba..535aaf946a 100644 --- a/gtk/gtksearchentry.c +++ b/gtk/gtksearchentry.c @@ -30,6 +30,7 @@ #include "gtksearchentryprivate.h" #include "gtkaccessible.h" +#include "gtkbindings.h" #include "gtkeditable.h" #include "gtkboxlayout.h" #include "gtkgestureclick.h" @@ -266,6 +267,7 @@ gtk_search_entry_class_init (GtkSearchEntryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkBindingSet *binding_set; object_class->finalize = gtk_search_entry_finalize; object_class->get_property = gtk_search_entry_get_property; @@ -399,18 +401,14 @@ gtk_search_entry_class_init (GtkSearchEntryClass *klass) NULL, G_TYPE_NONE, 0); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_g, GDK_CONTROL_MASK, - "next-match", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_g, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - "previous-match", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Escape, 0, - "stop-search", - NULL); + binding_set = gtk_binding_set_by_class (klass); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK, + "next-match", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "previous-match", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, + "stop-search", 0); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ENTRY_ACCESSIBLE); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); diff --git a/gtk/gtkshortcut.c b/gtk/gtkshortcut.c deleted file mode 100644 index 6a7ff1a148..0000000000 --- a/gtk/gtkshortcut.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - -#include "config.h" - -#include "gtkshortcut.h" - -#include "gtkintl.h" -#include "gtkshortcutaction.h" -#include "gtkshortcuttrigger.h" -#include "gtkwidget.h" - -/** - * SECTION:gtkshortcut - * @title: GtkShortcut - * @short_description: An object describing a keyboard shortcut - * @see_also: #GtkShortcutController, #GtkShortcutAction, - * #GtkShortcutTrigger - * - * GtkShortcut is the low level object used for managing keyboard - * shortcuts. - * - * It contains a description of how to trigger the shortcut via a - * #GtkShortcutTrigger and a way to activate the shortcut on a widget - * via #GtkShortcutAction. - * - * The actual work is usually done via #GtkShortcutController, which - * decides if and when to activate a shortcut. Using that controller - * directly however is rarely necessary as various higher level - * convenience APIs exist on #GtkWidgets that make it easier to use - * shortcuts in GTK. - * - * #GtkShortcut does provide functionality to make it easy for users - * to work with shortcuts, either by providing informational strings - * for display purposes or by allowing shortcuts to be configured. - */ - -struct _GtkShortcut -{ - GObject parent_instance; - - GtkShortcutAction *action; - GtkShortcutTrigger *trigger; - GVariant *args; -}; - -enum -{ - PROP_0, - PROP_ACTION, - PROP_ARGUMENTS, - PROP_TRIGGER, - - N_PROPS -}; - -G_DEFINE_TYPE (GtkShortcut, gtk_shortcut, G_TYPE_OBJECT) - -static GParamSpec *properties[N_PROPS] = { NULL, }; - -static void -gtk_shortcut_dispose (GObject *object) -{ - GtkShortcut *self = GTK_SHORTCUT (object); - - g_clear_pointer (&self->action, gtk_shortcut_action_unref); - g_clear_pointer (&self->trigger, gtk_shortcut_trigger_unref); - g_clear_pointer (&self->args, g_variant_unref); - - G_OBJECT_CLASS (gtk_shortcut_parent_class)->dispose (object); -} - -static void -gtk_shortcut_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GtkShortcut *self = GTK_SHORTCUT (object); - - switch (property_id) - { - case PROP_ACTION: - g_value_set_boxed (value, self->action); - break; - - case PROP_ARGUMENTS: - g_value_set_variant (value, self->args); - break; - - case PROP_TRIGGER: - g_value_set_boxed (value, self->trigger); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gtk_shortcut_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkShortcut *self = GTK_SHORTCUT (object); - - switch (property_id) - { - case PROP_ACTION: - gtk_shortcut_set_action (self, g_value_dup_boxed (value)); - break; - - case PROP_ARGUMENTS: - gtk_shortcut_set_arguments (self, g_value_get_variant (value)); - break; - - case PROP_TRIGGER: - gtk_shortcut_set_trigger (self, g_value_dup_boxed (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gtk_shortcut_class_init (GtkShortcutClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->dispose = gtk_shortcut_dispose; - gobject_class->get_property = gtk_shortcut_get_property; - gobject_class->set_property = gtk_shortcut_set_property; - - /** - * GtkShortcut:action: - * - * The action that gets activated by this shortcut. - */ - properties[PROP_ACTION] = - g_param_spec_boxed ("action", - P_("Action"), - P_("The action activated by this shortcut"), - GTK_TYPE_SHORTCUT_ACTION, - G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - - /** - * GtkShortcut:arguments: - * - * Arguments passed to activation. - */ - properties[PROP_ARGUMENTS] = - g_param_spec_variant ("arguments", - P_("Arguments"), - P_("Arguments passed to activation"), - G_VARIANT_TYPE_ANY, - NULL, - G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - - /** - * GtkShortcut:trigger: - * - * The trigger that triggers this shortcut. - */ - properties[PROP_TRIGGER] = - g_param_spec_boxed ("trigger", - P_("Trigger"), - P_("The trigger for this shortcut"), - GTK_TYPE_SHORTCUT_TRIGGER, - G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (gobject_class, N_PROPS, properties); -} - -static void -gtk_shortcut_init (GtkShortcut *self) -{ - self->action = gtk_nothing_action_new (); - self->trigger = gtk_shortcut_trigger_ref (gtk_never_trigger_get ()); -} - -/** - * gtk_shortcut_new: - * @trigger: (transfer full) (allow-none): The trigger that will trigger the shortcut - * @action: (transfer full) (allow-none): The action that will be activated upon - * triggering - * - * Creates a new #GtkShortcut that is triggered by @trigger and then activates - * @action. - * - * Returns: a new #GtkShortcut - **/ -GtkShortcut * -gtk_shortcut_new (GtkShortcutTrigger *trigger, - GtkShortcutAction *action) -{ - GtkShortcut *shortcut; - - shortcut = g_object_new (GTK_TYPE_SHORTCUT, - "action", action, - "trigger", trigger, - NULL); - - if (trigger) - gtk_shortcut_trigger_unref (trigger); - if (action) - gtk_shortcut_action_unref (action); - - return shortcut; -} - -/** - * gtk_shortcut_new_with_arguments: (skip) - * @trigger: (transfer full) (allow-none): The trigger that will trigger the shortcut - * @action: (transfer full) (allow-none): The action that will be activated upon - * triggering - * @format_string: (allow-none): GVariant format string for arguments or %NULL for - * no arguments - * @...: arguments, as given by format string. - * - * Creates a new #GtkShortcut that is triggered by @trigger and then activates - * @action with arguments given by @format_string. - * - * Returns: a new #GtkShortcut - **/ -GtkShortcut * -gtk_shortcut_new_with_arguments (GtkShortcutTrigger *trigger, - GtkShortcutAction *action, - const gchar *format_string, - ...) -{ - GtkShortcut *shortcut; - GVariant *args; - - if (format_string) - { - va_list valist; - va_start (valist, format_string); - args = g_variant_new_va (format_string, NULL, &valist); - va_end (valist); - } - else - { - args = NULL; - } - - shortcut = g_object_new (GTK_TYPE_SHORTCUT, - "action", action, - "arguments", args, - "trigger", trigger, - NULL); - - if (trigger) - gtk_shortcut_trigger_unref (trigger); - if (action) - gtk_shortcut_action_unref (action); - - return shortcut; -} - -/** - * gtk_shortcut_get_action: - * @self: a #GtkShortcut - * - * Gets the action that is activated by this shortcut. - * - * Returns: (transfer none): the action - **/ -GtkShortcutAction * -gtk_shortcut_get_action (GtkShortcut *self) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT (self), NULL); - - return self->action; -} - -/** - * gtk_shortcut_set_action: - * @self: a #GtkShortcut - * @action: (transfer full) (nullable): The new action. - * If the @action is %NULL, the nothing action will be used. - * - * Sets the new action for @self to be @action. - **/ -void -gtk_shortcut_set_action (GtkShortcut *self, - GtkShortcutAction *action) -{ - g_return_if_fail (GTK_IS_SHORTCUT (self)); - - if (action == NULL) - action = gtk_nothing_action_new (); - - if (self->action == action) - { - gtk_shortcut_action_unref (action); - return; - } - - gtk_shortcut_action_unref (self->action); - self->action = action; - - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTION]); -} - -/** - * gtk_shortcut_get_trigger: - * @self: a #GtkShortcut - * - * Gets the trigger used to trigger @self. - * - * Returns: (transfer none): the trigger used - **/ -GtkShortcutTrigger * -gtk_shortcut_get_trigger (GtkShortcut *self) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT (self), NULL); - - return self->trigger; -} - -/** - * gtk_shortcut_set_trigger: - * @self: a #GtkShortcut - * @trigger: (transfer full) (nullable): The new trigger. - * If the @trigger is %NULL, the never trigger will be used. - * - * Sets the new trigger for @self to be @trigger. - **/ -void -gtk_shortcut_set_trigger (GtkShortcut *self, - GtkShortcutTrigger *trigger) -{ - g_return_if_fail (GTK_IS_SHORTCUT (self)); - - if (trigger == NULL) - trigger = gtk_shortcut_trigger_ref (gtk_never_trigger_get ()); - - if (self->trigger == trigger) - { - gtk_shortcut_trigger_unref (trigger); - return; - } - - gtk_shortcut_trigger_unref (self->trigger); - self->trigger = trigger; - - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TRIGGER]); -} - -GVariant * -gtk_shortcut_get_arguments (GtkShortcut *self) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT (self), NULL); - - return self->args; -} - -void -gtk_shortcut_set_arguments (GtkShortcut *self, - GVariant *args) -{ - g_return_if_fail (GTK_IS_SHORTCUT (self)); - - if (self->args == args) - return; - - g_clear_pointer (&self->args, g_variant_unref); - if (args) - self->args = g_variant_ref_sink (args); - - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ARGUMENTS]); -} diff --git a/gtk/gtkshortcut.h b/gtk/gtkshortcut.h deleted file mode 100644 index 77c89754a2..0000000000 --- a/gtk/gtkshortcut.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - -#ifndef __GTK_SHORTCUT_H__ -#define __GTK_SHORTCUT_H__ - -#include <gtk/gtktypes.h> - -G_BEGIN_DECLS - -#define GTK_TYPE_SHORTCUT (gtk_shortcut_get_type ()) - -GDK_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (GtkShortcut, gtk_shortcut, GTK, SHORTCUT, GObject) - -GDK_AVAILABLE_IN_ALL -GtkShortcut * gtk_shortcut_new (GtkShortcutTrigger *trigger, - GtkShortcutAction *action); -GDK_AVAILABLE_IN_ALL -GtkShortcut * gtk_shortcut_new_with_arguments (GtkShortcutTrigger *trigger, - GtkShortcutAction *action, - const gchar *format_string, - ...); - -GDK_AVAILABLE_IN_ALL -GtkShortcutTrigger * - gtk_shortcut_get_trigger (GtkShortcut *self); -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_set_trigger (GtkShortcut *self, - GtkShortcutTrigger *trigger); -GDK_AVAILABLE_IN_ALL -GtkShortcutAction * - gtk_shortcut_get_action (GtkShortcut *self); -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_set_action (GtkShortcut *self, - GtkShortcutAction *action); - -GDK_AVAILABLE_IN_ALL -GVariant * gtk_shortcut_get_arguments (GtkShortcut *self); -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_set_arguments (GtkShortcut *self, - GVariant *args); - -G_END_DECLS - -#endif /* __GTK_SHORTCUT_H__ */ diff --git a/gtk/gtkshortcutaction.c b/gtk/gtkshortcutaction.c deleted file mode 100644 index 86f59e7809..0000000000 --- a/gtk/gtkshortcutaction.c +++ /dev/null @@ -1,1134 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - -/** - * SECTION:gtkshortcutaction - * @Title: GtkShortcutAction - * @Short_description: Tracking if shortcuts should be activated - * @See_also: #GtkShortcut - * - * #GtkShortcutAction is the object used to track if a #GtkShortcut should be - * activated. For this purpose, gtk_shortcut_action_action() can be called - * on a #GdkEvent. - * - * #GtkShortcutActions contain functions that allow easy presentation to end - * users as well as being printed for debugging. - * - * All #GtkShortcutActions are immutable, you can only specify their properties - * during construction. If you want to change a action, you have to replace it - * with a new one. - */ - -#include "config.h" - -#include "gtkshortcutactionprivate.h" - -#include "gtkbuilder.h" -#include "gtkwidgetprivate.h" - -typedef struct _GtkShortcutActionClass GtkShortcutActionClass; - -#define GTK_IS_SHORTCUT_ACTION_TYPE(action,type) (GTK_IS_SHORTCUT_ACTION (action) && (action)->action_class->action_type == (type)) - -struct _GtkShortcutAction -{ - const GtkShortcutActionClass *action_class; - - gatomicrefcount ref_count; -}; - -struct _GtkShortcutActionClass -{ - GtkShortcutActionType action_type; - gsize struct_size; - const char *type_name; - - void (* finalize) (GtkShortcutAction *action); - gboolean (* activate) (GtkShortcutAction *action, - GtkShortcutActionFlags flags, - GtkWidget *widget, - GVariant *args); - void (* print) (GtkShortcutAction *action, - GString *string); -}; - -G_DEFINE_BOXED_TYPE (GtkShortcutAction, gtk_shortcut_action, - gtk_shortcut_action_ref, - gtk_shortcut_action_unref) - -static void -gtk_shortcut_action_finalize (GtkShortcutAction *self) -{ - self->action_class->finalize (self); - - g_free (self); -} - -/*< private > - * gtk_shortcut_action_new: - * @action_class: class structure for this action - * - * Returns: (transfer full): the newly created #GtkShortcutAction - */ -static GtkShortcutAction * -gtk_shortcut_action_new (const GtkShortcutActionClass *action_class) -{ - GtkShortcutAction *self; - - g_return_val_if_fail (action_class != NULL, NULL); - - self = g_malloc0 (action_class->struct_size); - g_atomic_ref_count_init (&self->ref_count); - - self->action_class = action_class; - - return self; -} - -/** - * gtk_shortcut_action_ref: - * @self: a #GtkShortcutAction - * - * Acquires a reference on the given #GtkShortcutAction. - * - * Returns: (transfer full): the #GtkShortcutAction with an additional reference - */ -GtkShortcutAction * -gtk_shortcut_action_ref (GtkShortcutAction *self) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION (self), NULL); - - g_atomic_ref_count_inc (&self->ref_count); - - return self; -} - -/** - * gtk_shortcut_action_unref: - * @self: (transfer full): a #GtkShortcutAction - * - * Releases a reference on the given #GtkShortcutAction. - * - * If the reference was the last, the resources associated to the @action are - * freed. - */ -void -gtk_shortcut_action_unref (GtkShortcutAction *self) -{ - g_return_if_fail (GTK_IS_SHORTCUT_ACTION (self)); - - if (g_atomic_ref_count_dec (&self->ref_count)) - gtk_shortcut_action_finalize (self); -} - -/** - * gtk_shortcut_action_get_action_type: - * @self: a #GtkShortcutAction - * - * Returns the type of the @action. - * - * Returns: the type of the #GtkShortcutAction - */ -GtkShortcutActionType -gtk_shortcut_action_get_action_type (GtkShortcutAction *self) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION (self), GTK_SHORTCUT_ACTION_NOTHING); - - return self->action_class->action_type; -} - -/** - * gtk_shortcut_action_to_string: - * @self: a #GtkShortcutAction - * - * Prints the given action into a human-readable string. - * This is a small wrapper around gtk_shortcut_action_print() to help - * when debugging. - * - * Returns: (transfer full): a new string - **/ -char * -gtk_shortcut_action_to_string (GtkShortcutAction *self) -{ - GString *string; - - g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION (self), NULL); - - string = g_string_new (NULL); - gtk_shortcut_action_print (self, string); - - return g_string_free (string, FALSE); -} - -/** - * gtk_shortcut_action_print: - * @self: a #GtkShortcutAction - * @string: a #GString to print into - * - * Prints the given action into a string for the developer. - * This is meant for debugging and logging. - * - * The form of the representation may change at any time and is - * not guaranteed to stay identical. - **/ -void -gtk_shortcut_action_print (GtkShortcutAction *self, - GString *string) -{ - g_return_if_fail (GTK_IS_SHORTCUT_ACTION (self)); - g_return_if_fail (string != NULL); - - return self->action_class->print (self, string); -} - -/** - * gtk_shortcut_action_activate: - * @self: a #GtkShortcutAction - * @flags: flags to activate with - * @widget: Target of the activation - * @args: (allow-none): arguments to pass - * - * Activates the action on the @widget with the given @args. - * - * Note that some actions do ignore the passed in @flags, @widget or - * @args. - * - * Activation of an action can fail for various reasons. If the action - * is not supported by the @widget, if the @args don't match the action - * or if the activation otherwise had no effect, %FALSE will be returned. - * - * Returns: %TRUE if this action was activated successfully - **/ -gboolean -gtk_shortcut_action_activate (GtkShortcutAction *self, - GtkShortcutActionFlags flags, - GtkWidget *widget, - GVariant *args) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION (self), FALSE); - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - - return self->action_class->activate (self, flags, widget, args); -} - -static char * -string_is_function (const char *string, - const char *function_name) -{ - gsize len; - - if (!g_str_has_prefix (string, function_name)) - return NULL; - string += strlen (function_name); - - if (string[0] != '(') - return NULL; - string ++; - - len = strlen (string); - if (len == 0 || string[len - 1] != ')') - return NULL; - - return g_strndup (string, len - 1); -} - -GtkShortcutAction * -gtk_shortcut_action_parse_builder (GtkBuilder *builder, - const char *string, - GError **error) -{ - GtkShortcutAction *result; - char *arg; - - if (g_str_equal (string, "nothing")) - return gtk_nothing_action_new (); - if (g_str_equal (string, "activate")) - return gtk_activate_action_new (); - if (g_str_equal (string, "mnemonic-activate")) - return gtk_mnemonic_action_new (); - - if ((arg = string_is_function (string, "action"))) - { - result = gtk_action_action_new (arg); - g_free (arg); - } - else if ((arg = string_is_function (string, "signal"))) - { - result = gtk_signal_action_new (arg); - g_free (arg); - } - else if ((arg = string_is_function (string, "gaction"))) - { - GObject *object = gtk_builder_get_object (builder, arg); - - if (object == NULL) - { - g_set_error (error, - GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_ID, - "Action with ID \"%s\" not found", arg); - g_free (arg); - return NULL; - } - else if (!G_IS_ACTION (object)) - { - g_set_error (error, - GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_ID, - "Object with ID \"%s\" is not a GAction", arg); - g_free (arg); - return NULL; - } - - result = gtk_gaction_action_new (G_ACTION (object)); - g_free (arg); - } - else - { - g_set_error (error, - GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, - "String \"%s\" does not specify a GtkShortcutAction", string); - return NULL; - } - - return result; -} - -/*** GTK_SHORTCUT_ACTION_NOTHING ***/ - -typedef struct _GtkNothingAction GtkNothingAction; - -struct _GtkNothingAction -{ - GtkShortcutAction action; -}; - -static void -gtk_nothing_action_finalize (GtkShortcutAction *action) -{ - g_assert_not_reached (); -} - -static gboolean -gtk_nothing_action_activate (GtkShortcutAction *action, - GtkShortcutActionFlags flags, - GtkWidget *widget, - GVariant *args) -{ - return FALSE; -} - -static void -gtk_nothing_action_print (GtkShortcutAction *action, - GString *string) -{ - g_string_append (string, "nothing"); -} - -static const GtkShortcutActionClass GTK_NOTHING_ACTION_CLASS = { - GTK_SHORTCUT_ACTION_NOTHING, - sizeof (GtkNothingAction), - "GtkNothingAction", - gtk_nothing_action_finalize, - gtk_nothing_action_activate, - gtk_nothing_action_print -}; - -static GtkNothingAction nothing = { { >K_NOTHING_ACTION_CLASS, 1 } }; - -/** - * gtk_nothing_action_new: - * - * Gets the nothing action. This is an action that does nothing and where - * activating it always fails. - * - * Returns: The nothing action - */ -GtkShortcutAction * -gtk_nothing_action_new (void) -{ - return gtk_shortcut_action_ref (¬hing.action); -} - -/*** GTK_SHORTCUT_ACTION_CALLBACK ***/ - -typedef struct _GtkCallbackAction GtkCallbackAction; - -struct _GtkCallbackAction -{ - GtkShortcutAction action; - - GtkShortcutFunc callback; - gpointer user_data; - GDestroyNotify destroy_notify; -}; - -static void -gtk_callback_action_finalize (GtkShortcutAction *action) -{ - GtkCallbackAction *self = (GtkCallbackAction *) action; - - if (self->destroy_notify) - self->destroy_notify (self->user_data); -} - -static gboolean -gtk_callback_action_activate (GtkShortcutAction *action, - GtkShortcutActionFlags flags, - GtkWidget *widget, - GVariant *args) -{ - GtkCallbackAction *self = (GtkCallbackAction *) action; - - return self->callback (widget, args, self->user_data); -} - -static void -gtk_callback_action_print (GtkShortcutAction *action, - GString *string) -{ - GtkCallbackAction *self = (GtkCallbackAction *) action; - - g_string_append_printf (string, "callback(%p)", self->callback); -} - -static const GtkShortcutActionClass GTK_CALLBACK_ACTION_CLASS = { - GTK_SHORTCUT_ACTION_CALLBACK, - sizeof (GtkCallbackAction), - "GtkCallbackAction", - gtk_callback_action_finalize, - gtk_callback_action_activate, - gtk_callback_action_print -}; - -/** - * gtk_callback_action_new: - * @callback: the callback to call - * @data: - * @destroy: - * - * Create a custom action that calls the given @callback when - * activated. - * - * Returns: A new shortcut action - **/ -GtkShortcutAction * -gtk_callback_action_new (GtkShortcutFunc callback, - gpointer data, - GDestroyNotify destroy) -{ - GtkCallbackAction *self; - - g_return_val_if_fail (callback != NULL, NULL); - - self = (GtkCallbackAction *) gtk_shortcut_action_new (>K_CALLBACK_ACTION_CLASS); - - self->callback = callback; - self->user_data = data; - self->destroy_notify = destroy; - - return &self->action; -} - -/*** GTK_SHORTCUT_ACTION_ACTIVATE ***/ - -typedef struct _GtkActivateAction GtkActivateAction; - -struct _GtkActivateAction -{ - GtkShortcutAction action; -}; - -static void -gtk_activate_action_finalize (GtkShortcutAction *action) -{ - g_assert_not_reached (); -} - -static gboolean -gtk_activate_action_activate (GtkShortcutAction *action, - GtkShortcutActionFlags flags, - GtkWidget *widget, - GVariant *args) -{ - return gtk_widget_activate (widget); -} - -static void -gtk_activate_action_print (GtkShortcutAction *action, - GString *string) -{ - g_string_append (string, "activate"); -} - -static const GtkShortcutActionClass GTK_ACTIVATE_ACTION_CLASS = { - GTK_SHORTCUT_ACTION_ACTIVATE, - sizeof (GtkActivateAction), - "GtkActivateAction", - gtk_activate_action_finalize, - gtk_activate_action_activate, - gtk_activate_action_print -}; - -static GtkActivateAction activate = { { >K_ACTIVATE_ACTION_CLASS, 1 } }; - -/** - * gtk_activate_action_new: - * - * Gets the activate action. This is an action that calls gtk_widget_activate() - * on the given widget upon activation. - * - * Returns: The activate action - */ -GtkShortcutAction * -gtk_activate_action_new (void) -{ - return gtk_shortcut_action_ref (&activate.action); -} - -/*** GTK_SHORTCUT_ACTION_MNEMONIC ***/ - -typedef struct _GtkMnemonicAction GtkMnemonicAction; - -struct _GtkMnemonicAction -{ - GtkShortcutAction action; -}; - -static void -gtk_mnemonic_action_finalize (GtkShortcutAction *action) -{ - g_assert_not_reached (); -} - -static gboolean -gtk_mnemonic_action_activate (GtkShortcutAction *action, - GtkShortcutActionFlags flags, - GtkWidget *widget, - GVariant *args) -{ - return gtk_widget_mnemonic_activate (widget, flags & GTK_SHORTCUT_ACTION_EXCLUSIVE ? FALSE : TRUE); -} - -static void -gtk_mnemonic_action_print (GtkShortcutAction *action, - GString *string) -{ - g_string_append (string, "mnemonic-activate"); -} - -static const GtkShortcutActionClass GTK_MNEMONIC_ACTION_CLASS = { - GTK_SHORTCUT_ACTION_MNEMONIC, - sizeof (GtkMnemonicAction), - "GtkMnemonicAction", - gtk_mnemonic_action_finalize, - gtk_mnemonic_action_activate, - gtk_mnemonic_action_print -}; - -static GtkMnemonicAction mnemonic = { { >K_MNEMONIC_ACTION_CLASS, 1 } }; - -/** - * gtk_mnemonic_action_new: - * - * Gets the mnemonic action. This is an action that calls - * gtk_widget_mnemonic_activate() on the given widget upon activation. - * - * Returns: The mnemonic action - */ -GtkShortcutAction * -gtk_mnemonic_action_new (void) -{ - return gtk_shortcut_action_ref (&mnemonic.action); -} - -/*** GTK_SHORTCUT_ACTION_SIGNAL ***/ - -typedef struct _GtkSignalAction GtkSignalAction; - -struct _GtkSignalAction -{ - GtkShortcutAction action; - - char *name; -}; - -static void -gtk_signal_action_finalize (GtkShortcutAction *action) -{ - GtkSignalAction *self = (GtkSignalAction *) action; - - g_free (self->name); -} - -static gboolean -binding_compose_params (GtkWidget *widget, - GVariantIter *args, - GSignalQuery *query, - GValue **params_p) -{ - GValue *params; - const GType *types; - guint i; - gboolean valid; - - params = g_new0 (GValue, query->n_params + 1); - *params_p = params; - - /* The instance we emit on is the first object in the array - */ - g_value_init (params, G_TYPE_OBJECT); - g_value_set_object (params, G_OBJECT (widget)); - params++; - - types = query->param_types; - valid = TRUE; - for (i = 1; i < query->n_params + 1 && valid; i++) - { - GValue tmp_value = G_VALUE_INIT; - GVariant *tmp_variant; - - g_value_init (params, *types); - tmp_variant = g_variant_iter_next_value (args); - - switch ((guint) g_variant_classify (tmp_variant)) - { - case G_VARIANT_CLASS_BOOLEAN: - g_value_init (&tmp_value, G_TYPE_BOOLEAN); - g_value_set_boolean (&tmp_value, g_variant_get_boolean (tmp_variant)); - break; - case G_VARIANT_CLASS_DOUBLE: - g_value_init (&tmp_value, G_TYPE_DOUBLE); - g_value_set_double (&tmp_value, g_variant_get_double (tmp_variant)); - break; - case G_VARIANT_CLASS_INT32: - g_value_init (&tmp_value, G_TYPE_LONG); - g_value_set_long (&tmp_value, g_variant_get_int32 (tmp_variant)); - break; - case G_VARIANT_CLASS_UINT32: - g_value_init (&tmp_value, G_TYPE_LONG); - g_value_set_long (&tmp_value, g_variant_get_uint32 (tmp_variant)); - break; - case G_VARIANT_CLASS_INT64: - g_value_init (&tmp_value, G_TYPE_LONG); - g_value_set_long (&tmp_value, g_variant_get_int64 (tmp_variant)); - break; - case G_VARIANT_CLASS_STRING: - /* gtk_rc_parse_flags/enum() has fancier parsing for this; we can't call - * that since we don't have a GParamSpec, so just do something simple - */ - if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_ENUM) - { - GEnumClass *class = G_ENUM_CLASS (g_type_class_ref (*types)); - GEnumValue *enum_value; - const char *s = g_variant_get_string (tmp_variant, NULL); - - valid = FALSE; - - enum_value = g_enum_get_value_by_name (class, s); - if (!enum_value) - enum_value = g_enum_get_value_by_nick (class, s); - - if (enum_value) - { - g_value_init (&tmp_value, *types); - g_value_set_enum (&tmp_value, enum_value->value); - valid = TRUE; - } - - g_type_class_unref (class); - } - /* This is just a hack for compatibility with GTK+-1.2 where a string - * could be used for a single flag value / without the support for multiple - * values in gtk_rc_parse_flags(), this isn't very useful. - */ - else if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_FLAGS) - { - GFlagsClass *class = G_FLAGS_CLASS (g_type_class_ref (*types)); - GFlagsValue *flags_value; - const char *s = g_variant_get_string (tmp_variant, NULL); - - valid = FALSE; - - flags_value = g_flags_get_value_by_name (class, s); - if (!flags_value) - flags_value = g_flags_get_value_by_nick (class, s); - if (flags_value) - { - g_value_init (&tmp_value, *types); - g_value_set_flags (&tmp_value, flags_value->value); - valid = TRUE; - } - - g_type_class_unref (class); - } - else - { - g_value_init (&tmp_value, G_TYPE_STRING); - g_value_set_static_string (&tmp_value, g_variant_get_string (tmp_variant, NULL)); - } - break; - default: - valid = FALSE; - break; - } - - if (valid) - { - if (!g_value_transform (&tmp_value, params)) - valid = FALSE; - - g_value_unset (&tmp_value); - } - - g_variant_unref (tmp_variant); - types++; - params++; - } - - if (!valid) - { - guint j; - - for (j = 0; j < i; j++) - g_value_unset (&(*params_p)[j]); - - g_free (*params_p); - *params_p = NULL; - } - - return valid; -} - -static gboolean -gtk_signal_action_emit_signal (GtkWidget *widget, - const char *signal, - GVariant *args, - gboolean *handled, - GError **error) -{ - GSignalQuery query; - guint signal_id; - GValue *params = NULL; - GValue return_val = G_VALUE_INIT; - GVariantIter args_iter; - gsize n_args; - guint i; - - *handled = FALSE; - - signal_id = g_signal_lookup (signal, G_OBJECT_TYPE (widget)); - if (!signal_id) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Could not find signal \"%s\" in the '%s' class ancestry", - signal, - g_type_name (G_OBJECT_TYPE (widget))); - return FALSE; - } - - g_signal_query (signal_id, &query); - if (args == NULL) - n_args = 0; - else if (g_variant_is_of_type (args, G_VARIANT_TYPE_TUPLE)) - n_args = g_variant_iter_init (&args_iter, args); - else - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "argument GVariant is not a tuple"); - return FALSE; - } - if (query.n_params != n_args || - (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) || - !binding_compose_params (widget, &args_iter, &query, ¶ms)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "signature mismatch for signal \"%s\" in the '%s' class ancestry", - signal, - g_type_name (G_OBJECT_TYPE (widget))); - return FALSE; - } - else if (!(query.signal_flags & G_SIGNAL_ACTION)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "signal \"%s\" in the '%s' class ancestry cannot be used for action emissions", - signal, - g_type_name (G_OBJECT_TYPE (widget))); - return FALSE; - } - - if (query.return_type == G_TYPE_BOOLEAN) - g_value_init (&return_val, G_TYPE_BOOLEAN); - - g_signal_emitv (params, signal_id, 0, &return_val); - - if (query.return_type == G_TYPE_BOOLEAN) - { - if (g_value_get_boolean (&return_val)) - *handled = TRUE; - g_value_unset (&return_val); - } - else - *handled = TRUE; - - if (params != NULL) - { - for (i = 0; i < query.n_params + 1; i++) - g_value_unset (¶ms[i]); - - g_free (params); - } - - return TRUE; -} - -static gboolean -gtk_signal_action_activate (GtkShortcutAction *action, - GtkShortcutActionFlags flags, - GtkWidget *widget, - GVariant *args) -{ - GtkSignalAction *self = (GtkSignalAction *) action; - GError *error = NULL; - gboolean handled; - - if (!gtk_signal_action_emit_signal (widget, - self->name, - args, - &handled, - &error)) - { - g_warning ("gtk_signal_action_activate(): %s", - error->message); - g_clear_error (&error); - return FALSE; - } - - return handled; -} - -static void -gtk_signal_action_print (GtkShortcutAction *action, - GString *string) -{ - GtkSignalAction *self = (GtkSignalAction *) action; - - g_string_append_printf (string, "signal(%s)", self->name); -} - -static const GtkShortcutActionClass GTK_SIGNAL_ACTION_CLASS = { - GTK_SHORTCUT_ACTION_SIGNAL, - sizeof (GtkSignalAction), - "GtkSignalAction", - gtk_signal_action_finalize, - gtk_signal_action_activate, - gtk_signal_action_print -}; - -/** - * gtk_signal_action_new: - * @signal_name: name of the signal to emit - * - * Creates an action that when activated, emits the given action signal - * on the provided widget unpacking the given args into arguments passed - * to the signal. - * - * Returns: a new #GtkShortcutAction - **/ -GtkShortcutAction * -gtk_signal_action_new (const char *signal_name) -{ - GtkSignalAction *self; - - g_return_val_if_fail (signal_name != NULL, NULL); - - self = (GtkSignalAction *) gtk_shortcut_action_new (>K_SIGNAL_ACTION_CLASS); - - self->name = g_strdup (signal_name); - - return &self->action; -} - -/** - * gtk_signal_action_get_signal_name: - * @action: a signal action - * - * Returns the name of the signal that will be emitted. - * - * Returns: the name of the signal to emit - **/ -const char * -gtk_signal_action_get_signal_name (GtkShortcutAction *action) -{ - GtkSignalAction *self = (GtkSignalAction *) action; - - g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION_TYPE (action, GTK_SHORTCUT_ACTION_SIGNAL), NULL); - - return self->name; -} - -/*** GTK_SHORTCUT_ACTION_ACTION ***/ - -typedef struct _GtkActionAction GtkActionAction; - -struct _GtkActionAction -{ - GtkShortcutAction action; - - char *name; -}; - -static void -gtk_action_action_finalize (GtkShortcutAction *action) -{ - GtkSignalAction *self = (GtkSignalAction *) action; - - g_free (self->name); -} - -static gboolean -gtk_shortcut_trigger_check_parameter_type (GVariant *args, - const GVariantType *parameter_type) -{ - if (args) - { - if (parameter_type == NULL) - { - g_warning ("Trying to invoke action with arguments, but action has no parameter"); - return FALSE; - } - - if (!g_variant_is_of_type (args, parameter_type)) - { - gchar *typestr = g_variant_type_dup_string (parameter_type); - gchar *targetstr = g_variant_print (args, TRUE); - g_warning ("Trying to invoke action with target '%s'," - " but action expects parameter with type '%s'", targetstr, typestr); - g_free (targetstr); - g_free (typestr); - return FALSE; - } - } - else - { - if (parameter_type != NULL) - { - gchar *typestr = g_variant_type_dup_string (parameter_type); - g_warning ("Trying to invoke action without arguments," - " but action expects parameter with type '%s'", typestr); - g_free (typestr); - return FALSE; - } - } - - return TRUE; -} - -static gboolean -gtk_action_action_activate (GtkShortcutAction *action, - GtkShortcutActionFlags flags, - GtkWidget *widget, - GVariant *args) -{ - GtkSignalAction *self = (GtkSignalAction *) action; - GActionGroup *action_group; - const GVariantType *parameter_type; - gboolean enabled; - - action_group = G_ACTION_GROUP (_gtk_widget_get_action_muxer (widget, FALSE)); - if (action_group == NULL) - return FALSE; - - if (!g_action_group_query_action (action_group, self->name, &enabled, ¶meter_type, NULL, NULL, NULL)) - return FALSE; - - if (!enabled) - return FALSE; - - /* 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 args don't - * match the expected parameter type. In that case, we will print - * a warning. - */ - if (!gtk_shortcut_trigger_check_parameter_type (args, parameter_type)) - return FALSE; - - g_action_group_activate_action (action_group, self->name, args); - - return TRUE; -} - -static void -gtk_action_action_print (GtkShortcutAction *action, - GString *string) -{ - GtkActionAction *self = (GtkActionAction *) action; - - g_string_append_printf (string, "action(%s)", self->name); -} - -static const GtkShortcutActionClass GTK_ACTION_ACTION_CLASS = { - GTK_SHORTCUT_ACTION_ACTION, - sizeof (GtkActionAction), - "GtkActionAction", - gtk_action_action_finalize, - gtk_action_action_activate, - gtk_action_action_print -}; - -/** - * gtk_action_action_new: - * @name: the detailed name of the action - * - * Creates an action that when activated, activates the action given by - * the detailed @name on the widget passing the given arguments to it. - * - * See gtk_widget_insert_action_group() for how to add actions to widgets. - * - * Returns: a new #GtkShortcutAction - **/ -GtkShortcutAction * -gtk_action_action_new (const char *name) -{ - GtkActionAction *self; - - g_return_val_if_fail (name != NULL, NULL); - - self = (GtkActionAction *) gtk_shortcut_action_new (>K_ACTION_ACTION_CLASS); - - self->name = g_strdup (name); - - return &self->action; -} - -/** - * gtk_action_action_get_name: - * @action: an action action - * - * Returns the name of the action that will be activated. - * - * Returns: the name of the action to activate - **/ -const char * -gtk_action_action_get_name (GtkShortcutAction *action) -{ - GtkActionAction *self = (GtkActionAction *) action; - - g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION_TYPE (action, GTK_SHORTCUT_ACTION_ACTION), NULL); - - return self->name; -} - -/*** GTK_SHORTCUT_ACTION_GACTION ***/ - -typedef struct _GtkGActionAction GtkGActionAction; - -struct _GtkGActionAction -{ - GtkShortcutAction action; - - GAction *gaction; -}; - -static void -gtk_gaction_action_finalize (GtkShortcutAction *action) -{ - GtkGActionAction *self = (GtkGActionAction *) action; - - g_object_unref (self->gaction); -} - -static gboolean -gtk_gaction_action_activate (GtkShortcutAction *action, - GtkShortcutActionFlags flags, - GtkWidget *widget, - GVariant *args) -{ - GtkGActionAction *self = (GtkGActionAction *) action; - - if (!gtk_shortcut_trigger_check_parameter_type (args, g_action_get_parameter_type (self->gaction))) - return FALSE; - - if (!g_action_get_enabled (self->gaction)) - return FALSE; - - g_action_activate (self->gaction, args); - - return TRUE; -} - -static void -gtk_gaction_action_print (GtkShortcutAction *action, - GString *string) -{ - GtkGActionAction *self = (GtkGActionAction *) action; - - g_string_append_printf (string, "gaction(%s %p)", g_action_get_name (self->gaction), self->gaction); -} - -static const GtkShortcutActionClass GTK_GACTION_ACTION_CLASS = { - GTK_SHORTCUT_ACTION_GACTION, - sizeof (GtkGActionAction), - "GtkGActionAction", - gtk_gaction_action_finalize, - gtk_gaction_action_activate, - gtk_gaction_action_print -}; - -/** - * gtk_gaction_action_new: - * @action: a #GAction - * - * Creates a new action that will activate the given @gaction when activated - * with the passed in arguments. - * - * Returns: a new #GtkShortcutAction - **/ -GtkShortcutAction * -gtk_gaction_action_new (GAction *action) -{ - GtkGActionAction *self; - - g_return_val_if_fail (G_IS_ACTION (action), NULL); - - self = (GtkGActionAction *) gtk_shortcut_action_new (>K_GACTION_ACTION_CLASS); - - self->gaction = g_object_ref (action); - - return &self->action; -} - -/** - * gtk_gaction_action_get_gaction: - * @action: a gaction action - * - * Queries the #GAction that will be activated when this action is activated. - * - * Returns: (transfer none): The #GAction that will be activated - **/ -GAction * -gtk_gaction_action_get_gaction (GtkShortcutAction *action) -{ - GtkGActionAction *self = (GtkGActionAction *) action; - - g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION_TYPE (action, GTK_SHORTCUT_ACTION_GACTION), NULL); - - return self->gaction; -} - diff --git a/gtk/gtkshortcutaction.h b/gtk/gtkshortcutaction.h deleted file mode 100644 index 95904fbfb8..0000000000 --- a/gtk/gtkshortcutaction.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - -#ifndef __GTK_SHORTCUT_ACTION_H__ -#define __GTK_SHORTCUT_ACTION_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only <gtk/gtk.h> can be included directly." -#endif - -#include <gtk/gtktypes.h> - -G_BEGIN_DECLS - -#define GTK_TYPE_SHORTCUT_ACTION (gtk_shortcut_action_get_type ()) - -#define GTK_IS_SHORTCUT_ACTION(obj) ((obj) != NULL) - -/** - * GtkShortcutFunc: - * @widget: The widget passed to the activation - * @args: The arguments passed to the activation - * @user_data: The user data provided when activating the action - * - * Prototype for shortcuts based on user callbacks. - */ -typedef gboolean (* GtkShortcutFunc) (GtkWidget *widget, - GVariant *args, - gpointer user_data); - -/** - * GtkShortcutActionFlags: - * @GTK_SHORTCUT_ACTION_EXCLUSIVE: The action is the only - * action that can be activated. If this flag is not set, - * a future activation may select a different action. - * - * List of flags that can be passed to action activation. - * More flags may be added in the future. - **/ -typedef enum { - GTK_SHORTCUT_ACTION_EXCLUSIVE = 1 << 0 -} GtkShortcutActionFlags; - -/** - * GtkShortcutActionType: - * @GTK_SHORTCUT_ACTION_NOTHING: Don't ever activate - * @GTK_SHORTCUT_ACTION_CALLBACK: Call a custom user-provided callback - * @GTK_SHORTCUT_ACTION_ACTIVATE: Call gtk_widget_activate() on the widget - * @GTK_SHORTCUT_ACTION_MNEMONIC: Call gtk_widget_mnemonic_activate() - * on the widget - * @GTK_SHORTCUT_ACTION_SIGNAL: Emit the given action signal on the widget - * @GTK_SHORTCUT_ACTION_ACTION: Call the provided action on the widget - * @GTK_SHORTCUT_ACTION_GACTION: Activate a GAction - * - * The type of a action determines what the action does when activated. - **/ -typedef enum { - GTK_SHORTCUT_ACTION_NOTHING, - GTK_SHORTCUT_ACTION_CALLBACK, - GTK_SHORTCUT_ACTION_ACTIVATE, - GTK_SHORTCUT_ACTION_MNEMONIC, - GTK_SHORTCUT_ACTION_SIGNAL, - GTK_SHORTCUT_ACTION_ACTION, - GTK_SHORTCUT_ACTION_GACTION -} GtkShortcutActionType; - -GDK_AVAILABLE_IN_ALL -GType gtk_shortcut_action_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -GtkShortcutAction * gtk_shortcut_action_ref (GtkShortcutAction *self); -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_action_unref (GtkShortcutAction *self); - -GDK_AVAILABLE_IN_ALL -GtkShortcutActionType gtk_shortcut_action_get_action_type (GtkShortcutAction *self); - -GDK_AVAILABLE_IN_ALL -char * gtk_shortcut_action_to_string (GtkShortcutAction *self); -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_action_print (GtkShortcutAction *self, - GString *string); -GDK_AVAILABLE_IN_ALL -gboolean gtk_shortcut_action_activate (GtkShortcutAction *self, - GtkShortcutActionFlags flags, - GtkWidget *widget, - GVariant *args); - -GDK_AVAILABLE_IN_ALL -GtkShortcutAction * gtk_nothing_action_new (void); - -GDK_AVAILABLE_IN_ALL -GtkShortcutAction * gtk_callback_action_new (GtkShortcutFunc callback, - gpointer data, - GDestroyNotify destroy); - -GDK_AVAILABLE_IN_ALL -GtkShortcutAction * gtk_mnemonic_action_new (void); -GDK_AVAILABLE_IN_ALL -GtkShortcutAction * gtk_activate_action_new (void); - -GDK_AVAILABLE_IN_ALL -GtkShortcutAction * gtk_signal_action_new (const char *signal_name); -GDK_AVAILABLE_IN_ALL -const char * gtk_signal_action_get_signal_name (GtkShortcutAction *action); - -GDK_AVAILABLE_IN_ALL -GtkShortcutAction * gtk_action_action_new (const char *name); -GDK_AVAILABLE_IN_ALL -const char * gtk_action_action_get_name (GtkShortcutAction *action); - -GDK_AVAILABLE_IN_ALL -GtkShortcutAction * gtk_gaction_action_new (GAction *action); -GDK_AVAILABLE_IN_ALL -GAction * gtk_gaction_action_get_gaction (GtkShortcutAction *action); - -G_END_DECLS - -#endif /* __GTK_SHORTCUT_ACTION_H__ */ diff --git a/gtk/gtkshortcutactionprivate.h b/gtk/gtkshortcutactionprivate.h deleted file mode 100644 index 4c5a0db3ef..0000000000 --- a/gtk/gtkshortcutactionprivate.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - -#ifndef __GTK_SHORTCUT_ACTION_PRIVATE_H__ -#define __GTK_SHORTCUT_ACTION_PRIVATE_H__ - -#include "gtkshortcutaction.h" - -GtkShortcutAction * gtk_shortcut_action_parse_builder (GtkBuilder *builder, - const char *string, - GError **error); - -#endif /* __GTK_SHORTCUT_ACTION_PRIVATE_H__ */ diff --git a/gtk/gtkshortcutcontroller.c b/gtk/gtkshortcutcontroller.c deleted file mode 100644 index 0c2d1ba040..0000000000 --- a/gtk/gtkshortcutcontroller.c +++ /dev/null @@ -1,673 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - - -/** - * SECTION:gtkshortcutcontroller - * @Short_description: Event controller for shortcuts - * @Title: GtkShortcutController - * @See_also: #GtkEventController, #GtkShortcut - * - * #GtkShortcutController is an event controller that manages shortcuts. - * - * #GtkShortcutController implements #GListModel for querying the shortcuts that - * have been added to it. - **/ - -#include "config.h" - -#include "gtkshortcutcontrollerprivate.h" - -#include "gtkbuildable.h" -#include "gtkconcatmodelprivate.h" -#include "gtkeventcontrollerprivate.h" -#include "gtkintl.h" -#include "gtkshortcut.h" -#include "gtkshortcutmanager.h" -#include "gtkshortcuttrigger.h" -#include "gtktypebuiltins.h" -#include "gtkwidgetprivate.h" - -#include <gdk/gdk.h> - -struct _GtkShortcutController -{ - GtkEventController parent_instance; - - GListModel *shortcuts; - GtkShortcutScope scope; - GdkModifierType mnemonics_modifiers; - - guint custom_shortcuts : 1; -}; - -struct _GtkShortcutControllerClass -{ - GtkEventControllerClass parent_class; -}; - -enum { - PROP_0, - PROP_MNEMONICS_MODIFIERS, - PROP_MODEL, - PROP_SCOPE, - - N_PROPS -}; - -static GParamSpec *properties[N_PROPS] = { NULL, }; - -static GType -gtk_shortcut_controller_list_model_get_item_type (GListModel *list) -{ - return GTK_TYPE_SHORTCUT; -} - -static guint -gtk_shortcut_controller_list_model_get_n_items (GListModel *list) -{ - GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (list); - - return g_list_model_get_n_items (self->shortcuts); -} - -static gpointer -gtk_shortcut_controller_list_model_get_item (GListModel *list, - guint position) -{ - GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (list); - - return g_list_model_get_item (self->shortcuts, position); -} - -static void -gtk_shortcut_controller_list_model_init (GListModelInterface *iface) -{ - iface->get_item_type = gtk_shortcut_controller_list_model_get_item_type; - iface->get_n_items = gtk_shortcut_controller_list_model_get_n_items; - iface->get_item = gtk_shortcut_controller_list_model_get_item; -} - -static void -gtk_shortcut_controller_buildable_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *type) -{ - if (type != NULL) - { - GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type); - } - if (GTK_IS_SHORTCUT (child)) - { - gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (buildable), GTK_SHORTCUT (child)); - } - else - { - g_warning ("Cannot add an object of type %s to a controller of type %s", - g_type_name (G_OBJECT_TYPE (child)), g_type_name (G_OBJECT_TYPE (buildable))); - } -} - -static void -gtk_shortcut_controller_buildable_init (GtkBuildableIface *iface) -{ - iface->add_child = gtk_shortcut_controller_buildable_add_child; -} - -G_DEFINE_TYPE_WITH_CODE (GtkShortcutController, gtk_shortcut_controller, - GTK_TYPE_EVENT_CONTROLLER, - G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_shortcut_controller_list_model_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_shortcut_controller_buildable_init)) - -static gboolean -gtk_shortcut_controller_is_rooted (GtkShortcutController *self) -{ - GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)); - - if (widget == NULL) - return FALSE; - - return gtk_widget_get_root (widget) != NULL; -} - -static void -gtk_shortcut_controller_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (object); - - switch (prop_id) - { - case PROP_MNEMONICS_MODIFIERS: - gtk_shortcut_controller_set_mnemonics_modifiers (self, g_value_get_flags (value)); - break; - - case PROP_MODEL: - { - GListModel *model = g_value_get_object (value); - if (model && g_list_model_get_item_type (model) != GTK_TYPE_SHORTCUT) - { - g_warning ("Setting a model with type '%s' on a shortcut controller that requires 'GtkShortcut'", - g_type_name (g_list_model_get_item_type (model))); - model = NULL; - } - if (model == NULL) - { - self->shortcuts = G_LIST_MODEL (g_list_store_new (GTK_TYPE_SHORTCUT)); - self->custom_shortcuts = TRUE; - } - else - { - self->shortcuts = g_object_ref (model); - self->custom_shortcuts = FALSE; - } - g_signal_connect_swapped (self->shortcuts, "items-changed", G_CALLBACK (g_list_model_items_changed), self); - } - break; - - case PROP_SCOPE: - gtk_shortcut_controller_set_scope (self, g_value_get_enum (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gtk_shortcut_controller_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (object); - - switch (prop_id) - { - case PROP_MNEMONICS_MODIFIERS: - g_value_set_flags (value, self->mnemonics_modifiers); - break; - - case PROP_SCOPE: - g_value_set_enum (value, self->scope); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gtk_shortcut_controller_dispose (GObject *object) -{ - GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (object); - - if (self->custom_shortcuts) - g_list_store_remove_all (G_LIST_STORE (self->shortcuts)); - - G_OBJECT_CLASS (gtk_shortcut_controller_parent_class)->dispose (object); -} - -static void -gtk_shortcut_controller_finalize (GObject *object) -{ - GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (object); - - g_signal_handlers_disconnect_by_func (self->shortcuts, g_list_model_items_changed, self); - g_clear_object (&self->shortcuts); - - G_OBJECT_CLASS (gtk_shortcut_controller_parent_class)->finalize (object); -} - -static gboolean -gtk_shortcut_controller_trigger_shortcut (GtkShortcutController *self, - GtkShortcut *shortcut, - guint position, - GdkEvent *event, - gboolean enable_mnemonics) -{ - GtkWidget *widget; - - if (!gtk_shortcut_trigger_trigger (gtk_shortcut_get_trigger (shortcut), event, enable_mnemonics)) - return FALSE; - - widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)); - if (!self->custom_shortcuts && - GTK_IS_CONCAT_MODEL (self->shortcuts)) - { - GListModel *model = gtk_concat_model_get_model_for_item (GTK_CONCAT_MODEL (self->shortcuts), position); - if (GTK_IS_SHORTCUT_CONTROLLER (model)) - widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (model)); - } - - return gtk_shortcut_action_activate (gtk_shortcut_get_action (shortcut), - GTK_SHORTCUT_ACTION_EXCLUSIVE, /* FIXME */ - widget, - gtk_shortcut_get_arguments (shortcut)); -} - -static gboolean -gtk_shortcut_controller_run_controllers (GtkEventController *controller, - GdkEvent *event, - double x, - double y, - gboolean enable_mnemonics) -{ - GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller); - guint i; - - for (i = 0; i < g_list_model_get_n_items (self->shortcuts); i++) - { - if (gtk_shortcut_controller_trigger_shortcut (self, - g_list_model_get_item (self->shortcuts, i), - i, - event, - enable_mnemonics)) - return TRUE; - } - - return FALSE; -} - -static gboolean -gtk_shortcut_controller_handle_event (GtkEventController *controller, - GdkEvent *event, - double x, - double y) -{ - GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller); - gboolean enable_mnemonics; - - if (self->scope != GTK_SHORTCUT_SCOPE_LOCAL) - return FALSE; - - if (gdk_event_get_event_type (event) == GDK_KEY_PRESS) - { - GdkModifierType modifiers; - modifiers = gdk_event_get_modifier_state (event); - enable_mnemonics = (modifiers & gtk_accelerator_get_default_mod_mask ()) == self->mnemonics_modifiers; - } - else - { - enable_mnemonics = FALSE; - } - - return gtk_shortcut_controller_run_controllers (controller, event, x, y, enable_mnemonics); -} - -static void -gtk_shortcut_controller_set_widget (GtkEventController *controller, - GtkWidget *widget) -{ - GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller); - - GTK_EVENT_CONTROLLER_CLASS (gtk_shortcut_controller_parent_class)->set_widget (controller, widget); - - if (_gtk_widget_get_root (widget)) - gtk_shortcut_controller_root (self); -} - -static void -gtk_shortcut_controller_unset_widget (GtkEventController *controller) -{ - GtkShortcutController *self = GTK_SHORTCUT_CONTROLLER (controller); - GtkWidget *widget = gtk_event_controller_get_widget (controller); - - if (_gtk_widget_get_root (widget)) - gtk_shortcut_controller_unroot (self); - - GTK_EVENT_CONTROLLER_CLASS (gtk_shortcut_controller_parent_class)->unset_widget (controller); -} - -static void -gtk_shortcut_controller_class_init (GtkShortcutControllerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); - - object_class->dispose = gtk_shortcut_controller_dispose; - object_class->finalize = gtk_shortcut_controller_finalize; - object_class->set_property = gtk_shortcut_controller_set_property; - object_class->get_property = gtk_shortcut_controller_get_property; - - controller_class->handle_event = gtk_shortcut_controller_handle_event; - controller_class->set_widget = gtk_shortcut_controller_set_widget; - controller_class->unset_widget = gtk_shortcut_controller_unset_widget; - - /** - * GtkShortcutController:mnemonic-modifiers: - * - * The modifiers that need to be pressed to allow mnemonics activation. - */ - properties[PROP_MNEMONICS_MODIFIERS] = - g_param_spec_flags ("mnemonic-modifiers", - P_("Mnemonic modifers"), - P_("The modifiers to be pressed to allow mnemonics activation"), - GDK_TYPE_MODIFIER_TYPE, - GDK_MOD1_MASK, - G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - - /** - * GtkShortcutController:model: - * - * A list model to take shortcuts from - */ - properties[PROP_MODEL] = - g_param_spec_object ("model", - P_("Model"), - P_("A list model to take shortcuts from"), - G_TYPE_LIST_MODEL, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - - /** - * GtkShortcutController:scope: - * - * What scope the shortcuts will be handled in. - */ - properties[PROP_SCOPE] = - g_param_spec_enum ("scope", - P_("Scope"), - P_("What scope the shortcuts will be handled in"), - GTK_TYPE_SHORTCUT_SCOPE, - GTK_SHORTCUT_SCOPE_LOCAL, - G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -gtk_shortcut_controller_init (GtkShortcutController *self) -{ - self->mnemonics_modifiers = GDK_MOD1_MASK; -} - -void -gtk_shortcut_controller_root (GtkShortcutController *self) -{ - GtkShortcutManager *manager; - - switch (self->scope) - { - case GTK_SHORTCUT_SCOPE_LOCAL: - return; - - case GTK_SHORTCUT_SCOPE_MANAGED: - { - GtkWidget *widget; - - for (widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)); - !GTK_IS_SHORTCUT_MANAGER (widget); - widget = _gtk_widget_get_parent (widget)) - ; - - if (!GTK_IS_SHORTCUT_MANAGER (widget)) - return; - - manager = GTK_SHORTCUT_MANAGER (widget); - } - break; - - case GTK_SHORTCUT_SCOPE_GLOBAL: - { - GtkRoot *root = gtk_widget_get_root (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self))); - - if (!GTK_IS_SHORTCUT_MANAGER (root)) - return; - - manager = GTK_SHORTCUT_MANAGER (root); - } - break; - - default: - g_assert_not_reached (); - return; - } - - GTK_SHORTCUT_MANAGER_GET_IFACE (manager)->add_controller (manager, self); -} - -void -gtk_shortcut_controller_unroot (GtkShortcutController *self) -{ - GtkShortcutManager *manager; - - switch (self->scope) - { - case GTK_SHORTCUT_SCOPE_LOCAL: - return; - - case GTK_SHORTCUT_SCOPE_MANAGED: - { - GtkWidget *widget; - - for (widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)); - !GTK_IS_SHORTCUT_MANAGER (widget); - widget = _gtk_widget_get_parent (widget)) - ; - - if (!GTK_IS_SHORTCUT_MANAGER (widget)) - return; - - manager = GTK_SHORTCUT_MANAGER (widget); - } - break; - - case GTK_SHORTCUT_SCOPE_GLOBAL: - { - GtkRoot *root = gtk_widget_get_root (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self))); - - if (!GTK_IS_SHORTCUT_MANAGER (root)) - return; - - manager = GTK_SHORTCUT_MANAGER (root); - } - break; - - default: - g_assert_not_reached (); - return; - } - - GTK_SHORTCUT_MANAGER_GET_IFACE (manager)->remove_controller (manager, self); -} - -GtkEventController * -gtk_shortcut_controller_new (void) -{ - return g_object_new (GTK_TYPE_SHORTCUT_CONTROLLER, - NULL); -} - -GtkEventController * -gtk_shortcut_controller_new_for_model (GListModel *model) -{ - g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL); - g_return_val_if_fail (g_list_model_get_item_type (model) == GTK_TYPE_SHORTCUT, NULL); - - return g_object_new (GTK_TYPE_SHORTCUT_CONTROLLER, - "model", model, - NULL); -} - -/** - * gtk_shortcut_controller_add_shortcut: - * @self: the controller - * @shortcut: a #GtkShortcut - * - * Adds @shortcut to the list of shortcuts handled by @self. - * - * If this controller uses an external shortcut list, this function does - * nothing. - * - * The shortcut is added to the list so that it is triggered before - * all existing shortcuts. - * - * FIXME: What's supposed to happen if a shortcut gets added twice? - **/ -void -gtk_shortcut_controller_add_shortcut (GtkShortcutController *self, - GtkShortcut *shortcut) -{ - g_return_if_fail (GTK_IS_SHORTCUT_CONTROLLER (self)); - g_return_if_fail (GTK_IS_SHORTCUT (shortcut)); - - if (!self->custom_shortcuts) - return; - - g_list_store_append (G_LIST_STORE (self->shortcuts), shortcut); -} - -/** - * gtk_shortcut_controller_remove_shortcut: - * @self: the controller - * @shortcut: a #GtkShortcut - * - * Removes @shortcut from the list of shortcuts handled by @self. - * - * If @shortcut had not been added to @controller or this controller - * uses an external shortcut list, this function does nothing. - **/ -void -gtk_shortcut_controller_remove_shortcut (GtkShortcutController *self, - GtkShortcut *shortcut) -{ - guint i; - - g_return_if_fail (GTK_IS_SHORTCUT_CONTROLLER (self)); - g_return_if_fail (GTK_IS_SHORTCUT (shortcut)); - - if (!self->custom_shortcuts) - return; - - for (i = 0; i < g_list_model_get_n_items (self->shortcuts); i++) - { - GtkShortcut *item = g_list_model_get_item (self->shortcuts, i); - - if (item == shortcut) - { - g_object_unref (item); - g_list_store_remove (G_LIST_STORE (self->shortcuts), i); - return; - } - - g_object_unref (item); - } -} - -/** - * gtk_shortcut_controller_set_scope: - * @self: a #GtkShortcutController - * @scope: the new scope to use - * - * Sets the controller to have the given @scope. - * - * The scope allows shortcuts to be activated outside of the normal - * event propagation. In particular, it allows installing global - * keyboard shortcuts that can be activated even when a widget does - * not have focus. - **/ -void -gtk_shortcut_controller_set_scope (GtkShortcutController *self, - GtkShortcutScope scope) -{ - gboolean rooted; - - g_return_if_fail (GTK_IS_SHORTCUT_CONTROLLER (self)); - - if (self->scope == scope) - return; - - rooted = gtk_shortcut_controller_is_rooted (self); - - if (rooted) - gtk_shortcut_controller_unroot (self); - - self->scope = scope; - - if (rooted) - gtk_shortcut_controller_root (self); - - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SCOPE]); -} - -/** - * gtk_shortcut_controller_get_scope: - * @self: a #GtkShortcutController - * - * Gets the scope for when this controller activates its shortcuts. See - * gtk_shortcut_controller_set_scope() for details. - * - * Returns: the controller's scope - **/ -GtkShortcutScope -gtk_shortcut_controller_get_scope (GtkShortcutController *self) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT_CONTROLLER (self), GTK_SHORTCUT_SCOPE_LOCAL); - - return self->scope; -} - -/** - * gtk_shortcut_controller_set_mnemonics_modifiers: - * @self: a #GtkShortcutController - * @modifiers: the new mnemonics_modifiers to use - * - * Sets the controller to have the given @mnemonics_modifiers. - * - * The mnemonics modifiers determines which modifiers need to be pressed to allow - * activation of shortcuts with mnemonics triggers. - * - * This value is only relevant for local shortcut controllers. Global and managed - * shortcut controllers will have their shortcuts activated from other places which - * have their own modifiers for activating mnemonics. - **/ -void -gtk_shortcut_controller_set_mnemonics_modifiers (GtkShortcutController *self, - GdkModifierType modifiers) -{ - g_return_if_fail (GTK_IS_SHORTCUT_CONTROLLER (self)); - - if (self->mnemonics_modifiers == modifiers) - return; - - self->mnemonics_modifiers = modifiers; - - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MNEMONICS_MODIFIERS]); -} - -/** - * gtk_shortcut_controller_get_mnemonics_modifiers: - * @self: a #GtkShortcutController - * - * Gets the mnemonics modifiers for when this controller activates its shortcuts. See - * gtk_shortcut_controller_set_mnemonics_modifiers() for details. - * - * Returns: the controller's mnemonics modifiers - **/ -GdkModifierType -gtk_shortcut_controller_get_mnemonics_modifiers (GtkShortcutController *self) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT_CONTROLLER (self), GTK_SHORTCUT_SCOPE_LOCAL); - - return self->mnemonics_modifiers; -} - diff --git a/gtk/gtkshortcutcontroller.h b/gtk/gtkshortcutcontroller.h deleted file mode 100644 index aeb50e7f91..0000000000 --- a/gtk/gtkshortcutcontroller.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - - -#ifndef __GTK_SHORTCUT_CONTROLLER_H__ -#define __GTK_SHORTCUT_CONTROLLER_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only <gtk/gtk.h> can be included directly." -#endif - -#include <gtk/gtkeventcontroller.h> - -G_BEGIN_DECLS - -#define GTK_TYPE_SHORTCUT_CONTROLLER (gtk_shortcut_controller_get_type ()) -#define GTK_SHORTCUT_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_SHORTCUT_CONTROLLER, GtkShortcutController)) -#define GTK_SHORTCUT_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_SHORTCUT_CONTROLLER, GtkShortcutControllerClass)) -#define GTK_IS_SHORTCUT_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_SHORTCUT_CONTROLLER)) -#define GTK_IS_SHORTCUT_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_SHORTCUT_CONTROLLER)) -#define GTK_SHORTCUT_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_SHORTCUT_CONTROLLER, GtkShortcutControllerClass)) - -typedef struct _GtkShortcutController GtkShortcutController; -typedef struct _GtkShortcutControllerClass GtkShortcutControllerClass; - -GDK_AVAILABLE_IN_ALL -GType gtk_shortcut_controller_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -GtkEventController * gtk_shortcut_controller_new (void); -GDK_AVAILABLE_IN_ALL -GtkEventController * gtk_shortcut_controller_new_for_model (GListModel *list); - -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_controller_set_mnemonics_modifiers (GtkShortcutController *self, - GdkModifierType modifiers); -GDK_AVAILABLE_IN_ALL -GdkModifierType gtk_shortcut_controller_get_mnemonics_modifiers (GtkShortcutController *self); -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_controller_set_scope (GtkShortcutController *self, - GtkShortcutScope scope); -GDK_AVAILABLE_IN_ALL -GtkShortcutScope gtk_shortcut_controller_get_scope (GtkShortcutController *self); - -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_controller_add_shortcut (GtkShortcutController *self, - GtkShortcut *shortcut); -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_controller_remove_shortcut (GtkShortcutController *self, - GtkShortcut *shortcut); - -G_END_DECLS - -#endif /* __GTK_SHORTCUT_CONTROLLER_H__ */ diff --git a/gtk/gtkshortcutcontrollerprivate.h b/gtk/gtkshortcutcontrollerprivate.h deleted file mode 100644 index db1b52cecc..0000000000 --- a/gtk/gtkshortcutcontrollerprivate.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - -#ifndef __GTK_SHORTCUT_CONTROLLER_PRIVATE_H__ -#define __GTK_SHORTCUT_CONTROLLER_PRIVATE_H__ - -#include "gtkshortcutcontroller.h" - -void gtk_shortcut_controller_root (GtkShortcutController *controller); -void gtk_shortcut_controller_unroot (GtkShortcutController *controller); - -#endif /* __GTK_SHORTCUT_CONTROLLER_H__ */ diff --git a/gtk/gtkshortcutlabel.c b/gtk/gtkshortcutlabel.c index 90471af3ee..704324fa8c 100644 --- a/gtk/gtkshortcutlabel.c +++ b/gtk/gtkshortcutlabel.c @@ -303,7 +303,8 @@ parse_combination (GtkShortcutLabel *self, accels = g_strsplit (str, "&", 0); for (k = 0; accels[k]; k++) { - if (!gtk_accelerator_parse (accels[k], &key, &modifier)) + gtk_accelerator_parse (accels[k], &key, &modifier); + if (key == 0 && modifier == 0) { retval = FALSE; break; diff --git a/gtk/gtkshortcutmanager.c b/gtk/gtkshortcutmanager.c deleted file mode 100644 index 64fdd7ba8e..0000000000 --- a/gtk/gtkshortcutmanager.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - -#include "config.h" - -#include "gtkshortcutmanager.h" -#include "gtkshortcutmanagerprivate.h" -#include "gtkconcatmodelprivate.h" - -/** - * SECTION:gtkshortcutmanager - * @title: GtkShortcutManager - * @short_description: Interface for managing shortcuts - * - * The GtkShortcutManager interface is used to implement - * shortcut scopes. - */ - -G_DEFINE_INTERFACE (GtkShortcutManager, gtk_shortcut_manager, G_TYPE_OBJECT) - -void -gtk_shortcut_manager_create_controllers (GtkWidget *widget) -{ - GtkConcatModel *model; - GtkEventController *controller; - - model = gtk_concat_model_new (GTK_TYPE_SHORTCUT); - g_object_set_data_full (G_OBJECT (widget), "gtk-shortcut-manager-bubble", model, g_object_unref); - controller = gtk_shortcut_controller_new_for_model (G_LIST_MODEL (model)); - gtk_event_controller_set_name (controller, "gtk-shortcut-manager-bubble"); - gtk_widget_add_controller (widget, controller); - - model = gtk_concat_model_new (GTK_TYPE_SHORTCUT); - g_object_set_data_full (G_OBJECT (widget), "gtk-shortcut-manager-capture", model, g_object_unref); - controller = gtk_shortcut_controller_new_for_model (G_LIST_MODEL (model)); - gtk_event_controller_set_name (controller, "gtk-shortcut-manager-capture"); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); - gtk_widget_add_controller (widget, controller); -} - -static GtkConcatModel * -gtk_shortcut_manager_get_model (GtkShortcutManager *self, - GtkPropagationPhase phase) -{ - switch (phase) - { - case GTK_PHASE_CAPTURE: - return g_object_get_data (G_OBJECT (self), "gtk-shortcut-manager-capture"); - case GTK_PHASE_BUBBLE: - return g_object_get_data (G_OBJECT (self), "gtk-shortcut-manager-bubble"); - case GTK_PHASE_NONE: - case GTK_PHASE_TARGET: - return NULL; - default: - g_assert_not_reached (); - return NULL; - } -} - -static void -gtk_shortcut_manager_default_add_controller (GtkShortcutManager *self, - GtkShortcutController *controller) -{ - GtkConcatModel *model; - - model = gtk_shortcut_manager_get_model (self, - gtk_event_controller_get_propagation_phase (GTK_EVENT_CONTROLLER (controller))); - if (model) - gtk_concat_model_append (model, G_LIST_MODEL (controller)); -} - -static void -gtk_shortcut_manager_default_remove_controller (GtkShortcutManager *self, - GtkShortcutController *controller) -{ - GtkConcatModel *model; - - model = gtk_shortcut_manager_get_model (self, - gtk_event_controller_get_propagation_phase (GTK_EVENT_CONTROLLER (controller))); - if (model) - gtk_concat_model_remove (model, G_LIST_MODEL (controller)); -} - -static void -gtk_shortcut_manager_default_init (GtkShortcutManagerInterface *iface) -{ - iface->add_controller = gtk_shortcut_manager_default_add_controller; - iface->remove_controller = gtk_shortcut_manager_default_remove_controller; -} - diff --git a/gtk/gtkshortcutmanager.h b/gtk/gtkshortcutmanager.h deleted file mode 100644 index 0d4ece9745..0000000000 --- a/gtk/gtkshortcutmanager.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - -#ifndef __GTK_SHORTCUT_MANAGER_H__ -#define __GTK_SHORTCUT_MANAGER_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only <gtk/gtk.h> can be included directly." -#endif - -#include <gtk/gtkshortcutcontroller.h> -#include <gtk/gtkwidget.h> - -G_BEGIN_DECLS - -#define GTK_TYPE_SHORTCUT_MANAGER (gtk_shortcut_manager_get_type ()) - -GDK_AVAILABLE_IN_ALL -G_DECLARE_INTERFACE (GtkShortcutManager, gtk_shortcut_manager, GTK, SHORTCUT_MANAGER, GtkWidget) - -/** - * GtkShortcutManager: - * - * This object is used to implement support for #GtkShortcutScopes. Every - * widget that implements #GtkShortcutManager will be used as a - * %GTK_SHORTCUT_SCOPE_MANAGED. - */ - -/** - * GtkShortcutManagerInterface: - * @add_controller: Add a #GtkShortcutController to be managed. - * @remove_controller: Remove a #GtkShortcutController that had previously - * been added. - * - * The list of functions that can be implemented for the #GtkShortcutManager interface. - * - * Note that no function is mandatory to implement, the default implementation will work - * fine. - */ -struct _GtkShortcutManagerInterface -{ - /*< private >*/ - GTypeInterface g_iface; - - /*< public >*/ - void (* add_controller) (GtkShortcutManager *self, - GtkShortcutController *controller); - void (* remove_controller) (GtkShortcutManager *self, - GtkShortcutController *controller); -}; - - -G_END_DECLS - -#endif /* __GTK_SHORTCUT_MANAGER_H__ */ diff --git a/gtk/gtkshortcutmanagerprivate.h b/gtk/gtkshortcutmanagerprivate.h deleted file mode 100644 index 72424dba23..0000000000 --- a/gtk/gtkshortcutmanagerprivate.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __GTK_SHORTCUT_MANAGER_PRIVATE_H__ -#define __GTK_SHORTCUT_MANAGER__PRIVATE_H__ - -#include "gtkshortcutmanager.h" - -G_BEGIN_DECLS - -void gtk_shortcut_manager_create_controllers (GtkWidget *widget); - -G_END_DECLS - -#endif /* __GTK_SHORTCUT_MANAGER_PRIVATE_H__ */ diff --git a/gtk/gtkshortcutssection.c b/gtk/gtkshortcutssection.c index 337ece2539..6a95e348a1 100644 --- a/gtk/gtkshortcutssection.c +++ b/gtk/gtkshortcutssection.c @@ -30,6 +30,7 @@ #include "gtkorientable.h" #include "gtksizegroup.h" #include "gtkwidget.h" +#include "gtkbindings.h" #include "gtkprivate.h" #include "gtkmarshalers.h" #include "gtkgesturepan.h" @@ -306,6 +307,7 @@ gtk_shortcuts_section_class_init (GtkShortcutsSectionClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + GtkBindingSet *binding_set; object_class->finalize = gtk_shortcuts_section_finalize; object_class->get_property = gtk_shortcuts_section_get_property; @@ -386,23 +388,23 @@ gtk_shortcuts_section_class_init (GtkShortcutsSectionClass *klass) G_TYPE_BOOLEAN, 1, G_TYPE_INT); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Up, 0, - "change-current-page", - "(i)", -1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Down, 0, - "change-current-page", - "(i)", 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Up, GDK_CONTROL_MASK, - "change-current-page", - "(i)", -1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Page_Down, GDK_CONTROL_MASK, - "change-current-page", - "(i)", 1); - + binding_set = gtk_binding_set_by_class (klass); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Page_Up, 0, + "change-current-page", 1, + G_TYPE_INT, -1); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Page_Down, 0, + "change-current-page", 1, + G_TYPE_INT, 1); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Page_Up, GDK_CONTROL_MASK, + "change-current-page", 1, + G_TYPE_INT, -1); + gtk_binding_entry_add_signal (binding_set, + GDK_KEY_Page_Down, GDK_CONTROL_MASK, + "change-current-page", 1, + G_TYPE_INT, 1); gtk_widget_class_set_css_name (widget_class, I_("shortcuts-section")); } diff --git a/gtk/gtkshortcutswindow.c b/gtk/gtkshortcutswindow.c index a2d580f0eb..292b1171be 100644 --- a/gtk/gtkshortcutswindow.c +++ b/gtk/gtkshortcutswindow.c @@ -20,6 +20,7 @@ #include "gtkshortcutswindowprivate.h" +#include "gtkbindings.h" #include "gtkbox.h" #include "gtkgrid.h" #include "gtkheaderbar.h" @@ -773,6 +774,7 @@ gtk_shortcuts_window_class_init (GtkShortcutsWindowClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + GtkBindingSet *binding_set = gtk_binding_set_by_class (klass); object_class->constructed = gtk_shortcuts_window_constructed; object_class->finalize = gtk_shortcuts_window_finalize; @@ -854,14 +856,8 @@ gtk_shortcuts_window_class_init (GtkShortcutsWindowClass *klass) G_TYPE_NONE, 0); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Escape, 0, - "close", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_f, GDK_CONTROL_MASK, - "search", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_f, GDK_CONTROL_MASK, "search", 0); g_type_ensure (GTK_TYPE_SHORTCUTS_GROUP); g_type_ensure (GTK_TYPE_SHORTCUTS_SHORTCUT); diff --git a/gtk/gtkshortcuttrigger.c b/gtk/gtkshortcuttrigger.c deleted file mode 100644 index dce75a1191..0000000000 --- a/gtk/gtkshortcuttrigger.c +++ /dev/null @@ -1,976 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - -/** - * SECTION:gtkshortcuttrigger - * @Title: GtkShortcutTrigger - * @Short_description: Triggers to track if shortcuts should be activated - * @See_also: #GtkShortcut - * - * #GtkShortcutTrigger is the object used to track if a #GtkShortcut should be - * activated. For this purpose, gtk_shortcut_trigger_trigger() can be called - * on a #GdkEvent. - * - * #GtkShortcutTriggers contain functions that allow easy presentation to end - * users as well as being printed for debugging. - * - * All #GtkShortcutTriggers are immutable, you can only specify their properties - * during construction. If you want to change a trigger, you have to replace it - * with a new one. - */ - -#include "config.h" - -#include "gtkshortcuttrigger.h" - -#include "gtkaccelgroupprivate.h" - -typedef struct _GtkShortcutTriggerClass GtkShortcutTriggerClass; - -#define GTK_IS_SHORTCUT_TRIGGER_TYPE(trigger,type) (GTK_IS_SHORTCUT_TRIGGER (trigger) && (trigger)->trigger_class->trigger_type == (type)) - -struct _GtkShortcutTrigger -{ - const GtkShortcutTriggerClass *trigger_class; - - gatomicrefcount ref_count; -}; - -struct _GtkShortcutTriggerClass -{ - GtkShortcutTriggerType trigger_type; - gsize struct_size; - const char *type_name; - - void (* finalize) (GtkShortcutTrigger *trigger); - gboolean (* trigger) (GtkShortcutTrigger *trigger, - GdkEvent *event, - gboolean enable_mnemonics); - guint (* hash) (GtkShortcutTrigger *trigger); - int (* compare) (GtkShortcutTrigger *trigger1, - GtkShortcutTrigger *trigger2); - void (* print) (GtkShortcutTrigger *trigger, - GString *string); - gboolean (* print_label) (GtkShortcutTrigger *trigger, - GdkDisplay *display, - GString *string); -}; - -G_DEFINE_BOXED_TYPE (GtkShortcutTrigger, gtk_shortcut_trigger, - gtk_shortcut_trigger_ref, - gtk_shortcut_trigger_unref) - -static void -gtk_shortcut_trigger_finalize (GtkShortcutTrigger *self) -{ - self->trigger_class->finalize (self); - - g_free (self); -} - -/*< private > - * gtk_shortcut_trigger_new: - * @trigger_class: class structure for this trigger - * - * Returns: (transfer full): the newly created #GtkShortcutTrigger - */ -static GtkShortcutTrigger * -gtk_shortcut_trigger_new (const GtkShortcutTriggerClass *trigger_class) -{ - GtkShortcutTrigger *self; - - g_return_val_if_fail (trigger_class != NULL, NULL); - - self = g_malloc0 (trigger_class->struct_size); - g_atomic_ref_count_init (&self->ref_count); - - self->trigger_class = trigger_class; - - return self; -} - -/** - * gtk_shortcut_trigger_ref: - * @self: a #GtkShortcutTrigger - * - * Acquires a reference on the given #GtkShortcutTrigger. - * - * Returns: (transfer full): the #GtkShortcutTrigger with - * an additional reference - */ -GtkShortcutTrigger * -gtk_shortcut_trigger_ref (GtkShortcutTrigger *self) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (self), NULL); - - g_atomic_ref_count_inc (&self->ref_count); - - return self; -} - -/** - * gtk_shortcut_trigger_unref: - * @self: (transfer full): a #GtkShortcutTrigger - * - * Releases a reference on the given #GtkShortcutTrigger. - * - * If the reference was the last, the resources associated - * to the trigger are freed. - */ -void -gtk_shortcut_trigger_unref (GtkShortcutTrigger *self) -{ - g_return_if_fail (GTK_IS_SHORTCUT_TRIGGER (self)); - - if (g_atomic_ref_count_dec (&self->ref_count)) - gtk_shortcut_trigger_finalize (self); -} - -/** - * gtk_shortcut_trigger_get_trigger_type: - * @self: a #GtkShortcutTrigger - * - * Returns the type of the @trigger. - * - * Returns: the type of the #GtkShortcutTrigger - */ -GtkShortcutTriggerType -gtk_shortcut_trigger_get_trigger_type (GtkShortcutTrigger *self) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (self), GTK_SHORTCUT_TRIGGER_NEVER); - - return self->trigger_class->trigger_type; -} - -/** - * gtk_shortcut_trigger_trigger: - * @self: a #GtkShortcutTrigger - * @event: the event to check - * @enable_mnemonics: %TRUE if mnemonics should trigger. Usually the - * value of this property is determined by checking that the passed - * in @event is a Key event and has the right modifiers set. - * - * Checks if the given @event triggers @self. If so, - * returns %TRUE. - * - * Returns: %TRUE if this event triggered the trigger - **/ -gboolean -gtk_shortcut_trigger_trigger (GtkShortcutTrigger *self, - GdkEvent *event, - gboolean enable_mnemonics) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (self), FALSE); - - return self->trigger_class->trigger (self, event, enable_mnemonics); -} - -/** - * gtk_shortcut_trigger_parse_string: - * @string: the string to parse - * - * Tries to parse the given string into a trigger. On success, - * the parsed trigger is returned. When parsing failed, %NULL is - * returned. - * - * FIXME: Document the supported format here once we've figured - * it out. - * For now, this function only supports gtk_accelerator_parse() and - * can only return a trigger of type %GTK_SHORTCUT_TRIGGER_KEYVAL. - * - * Returns: a new #GtkShortcutTrigger or %NULL on error - **/ -GtkShortcutTrigger * -gtk_shortcut_trigger_parse_string (const char *string) -{ - GdkModifierType modifiers; - guint keyval; - - g_return_val_if_fail (string != NULL, NULL); - - if (gtk_accelerator_parse (string, &keyval, &modifiers)) - return gtk_keyval_trigger_new (keyval, modifiers); - - return NULL; -} - -/** - * gtk_shortcut_trigger_to_string: - * @self: a #GtkShortcutTrigger - * - * Prints the given trigger into a human-readable string. - * This is a small wrapper around gdk_content_formats_print() - * to help when debugging. - * - * Returns: (transfer full): a new string - **/ -char * -gtk_shortcut_trigger_to_string (GtkShortcutTrigger *self) -{ - GString *string; - - g_return_val_if_fail (self != NULL, NULL); - - string = g_string_new (NULL); - gtk_shortcut_trigger_print (self, string); - - return g_string_free (string, FALSE); -} - -/** - * gtk_shortcut_trigger_print: - * @self: a #GtkShortcutTrigger - * @string: a #GString to print into - * - * Prints the given trigger into a string for the developer. - * This is meant for debugging and logging. - * - * The form of the representation may change at any time - * and is not guaranteed to stay identical. - **/ -void -gtk_shortcut_trigger_print (GtkShortcutTrigger *self, - GString *string) -{ - g_return_if_fail (GTK_IS_SHORTCUT_TRIGGER (self)); - g_return_if_fail (string != NULL); - - return self->trigger_class->print (self, string); -} - -/** - * gtk_shortcut_trigger_to_label: - * @self: a #GtkShortcutTrigger - * @display: #GdkDisplay to print for - * - * Gets textual representation for the given trigger. This - * function is returning a translated string for presentation - * to end users for example in menu items or in help texts. - * - * The @display in use may influence the resulting string in - * various forms, such as resolving hardware keycodes or by - * causing display-specific modifier names. - * - * The form of the representation may change at any time and is - * not guaranteed to stay identical. - * - * Returns: (transfer full): a new string - **/ -char * -gtk_shortcut_trigger_to_label (GtkShortcutTrigger *self, - GdkDisplay *display) -{ - GString *string; - - g_return_val_if_fail (self != NULL, NULL); - - string = g_string_new (NULL); - gtk_shortcut_trigger_print_label (self, display, string); - - return g_string_free (string, FALSE); -} - -/** - * gtk_shortcut_trigger_print_label: - * @self: a #GtkShortcutTrigger - * @display: #GdkDisplay to print for - * @string: a #GString to print into - * - * Prints the given trigger into a string. This function is - * returning a translated string for presentation to end users - * for example in menu items or in help texts. - * - * The @display in use may influence the resulting string in - * various forms, such as resolving hardware keycodes or by - * causing display-specific modifier names. - * - * The form of the representation may change at any time and is - * not guaranteed to stay identical. - * - * Returns: %TRUE if something was printed or %FALSE if the - * trigger did not have a textual representation suitable - * for end users. - **/ -gboolean -gtk_shortcut_trigger_print_label (GtkShortcutTrigger *self, - GdkDisplay *display, - GString *string) -{ - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (self), FALSE); - g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE); - g_return_val_if_fail (string != NULL, FALSE); - - return self->trigger_class->print_label (self, display, string); -} - -/** - * gtk_shortcut_trigger_hash: - * @trigger: (type GtkShortcutTrigger): a #GtkShortcutTrigger - * - * Generates a hash value for a #GtkShortcutTrigger. - * - * The output of this function is guaranteed to be the same for a given - * value only per-process. It may change between different processor - * architectures or even different versions of GTK. Do not use this - * function as a basis for building protocols or file formats. - * - * The types of @trigger is #gconstpointer only to allow use of this - * function with #GHashTable. They must each be a #GtkShortcutTrigger. - * - * Returns: a hash value corresponding to @trigger - **/ -guint -gtk_shortcut_trigger_hash (gconstpointer trigger) -{ - GtkShortcutTrigger *t = (GtkShortcutTrigger *) trigger; - - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (trigger), 0); - - return t->trigger_class->hash (t); -} - -/** - * gtk_shortcut_trigger_equal: - * @trigger1: (type GtkShortcutTrigger): a #GtkShortcutTrigger - * @trigger2: (type GtkShortcutTrigger): a #GtkShortcutTrigger - * - * Checks if @trigger1 and @trigger2 trigger under the same conditions. - * - * The types of @one and @two are #gconstpointer only to allow use of this - * function with #GHashTable. They must each be a #GtkShortcutTrigger. - * - * Returns: %TRUE if @trigger1 and @trigger2 are equal - **/ -gboolean -gtk_shortcut_trigger_equal (gconstpointer trigger1, - gconstpointer trigger2) -{ - return gtk_shortcut_trigger_compare (trigger1, trigger2) == 0; -} - -/** - * gtk_shortcut_trigger_compare: - * @trigger1: (type GtkShortcutTrigger): a #GtkShortcutTrigger - * @trigger2: (type GtkShortcutTrigger): a #GtkShortcutTrigger - * - * - * The types of @one and @two are #gconstpointer only to allow use of this - * function as a #GCompareFunc. They must each be a #GtkShortcutTrigger. - * - * Returns: An integer less than, equal to, or greater than zero if - * @trigger1 is found, respectively, to be less than, to match, - * or be greater than @trigger2. - **/ -gint -gtk_shortcut_trigger_compare (gconstpointer trigger1, - gconstpointer trigger2) -{ - GtkShortcutTrigger *t1 = (GtkShortcutTrigger *) trigger1; - GtkShortcutTrigger *t2 = (GtkShortcutTrigger *) trigger2; - - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (trigger1), -1); - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (trigger2), 1); - - if (t1->trigger_class != t2->trigger_class) - return t1->trigger_class->trigger_type - t2->trigger_class->trigger_type; - - return t1->trigger_class->compare (t1, t2); -} - -/*** GTK_SHORTCUT_TRIGGER_NEVER ***/ - -typedef struct _GtkNeverTrigger GtkNeverTrigger; - -struct _GtkNeverTrigger -{ - GtkShortcutTrigger trigger; - - guint never; - GdkModifierType modifiers; -}; - -static void -gtk_never_trigger_finalize (GtkShortcutTrigger *trigger) -{ - g_assert_not_reached (); -} - -static gboolean -gtk_never_trigger_trigger (GtkShortcutTrigger *trigger, - GdkEvent *event, - gboolean enable_mnemonics) -{ - return FALSE; -} - -static guint -gtk_never_trigger_hash (GtkShortcutTrigger *trigger) -{ - return GTK_SHORTCUT_TRIGGER_NEVER; -} - -static int -gtk_never_trigger_compare (GtkShortcutTrigger *trigger1, - GtkShortcutTrigger *trigger2) -{ - return 0; -} - -static void -gtk_never_trigger_print (GtkShortcutTrigger *trigger, - GString *string) - -{ - g_string_append (string, "<never>"); -} - -static gboolean -gtk_never_trigger_print_label (GtkShortcutTrigger *trigger, - GdkDisplay *display, - GString *string) -{ - return FALSE; -} - -static const GtkShortcutTriggerClass GTK_NEVER_TRIGGER_CLASS = { - GTK_SHORTCUT_TRIGGER_NEVER, - sizeof (GtkNeverTrigger), - "GtkNeverTrigger", - gtk_never_trigger_finalize, - gtk_never_trigger_trigger, - gtk_never_trigger_hash, - gtk_never_trigger_compare, - gtk_never_trigger_print, - gtk_never_trigger_print_label -}; - -static GtkNeverTrigger never = { { >K_NEVER_TRIGGER_CLASS, 1 } }; - -/** - * gtk_never_trigger_get: - * - * Gets the never trigger. This is a singleton for a trigger - * that never triggers. Use this trigger instead of %NULL - * because it implements all virtual functions. - * - * Returns: (transfer none): The never trigger - */ -GtkShortcutTrigger * -gtk_never_trigger_get (void) -{ - return &never.trigger; -} - -/*** GTK_KEYVAL_TRIGGER ***/ - -typedef struct _GtkKeyvalTrigger GtkKeyvalTrigger; - -struct _GtkKeyvalTrigger -{ - GtkShortcutTrigger trigger; - - guint keyval; - GdkModifierType modifiers; -}; - -static void -gtk_keyval_trigger_finalize (GtkShortcutTrigger *trigger) -{ -} - -static gboolean -gtk_keyval_trigger_trigger (GtkShortcutTrigger *trigger, - GdkEvent *event, - gboolean enable_mnemonics) -{ - GtkKeyvalTrigger *self = (GtkKeyvalTrigger *) trigger; - GdkModifierType modifiers; - guint keyval; - - if (gdk_event_get_event_type (event) != GDK_KEY_PRESS) - return FALSE; - - /* XXX: This needs to deal with groups */ - modifiers = gdk_event_get_modifier_state (event); - keyval = gdk_key_event_get_keyval (event); - - if (keyval == GDK_KEY_ISO_Left_Tab) - keyval = GDK_KEY_Tab; - else - keyval = gdk_keyval_to_lower (keyval); - - return keyval == self->keyval && modifiers == self->modifiers; -} - -static guint -gtk_keyval_trigger_hash (GtkShortcutTrigger *trigger) -{ - GtkKeyvalTrigger *self = (GtkKeyvalTrigger *) trigger; - - return (self->modifiers << 24) - | (self->modifiers >> 8) - | (self->keyval << 16) - | GTK_SHORTCUT_TRIGGER_KEYVAL; -} - -static int -gtk_keyval_trigger_compare (GtkShortcutTrigger *trigger1, - GtkShortcutTrigger *trigger2) -{ - GtkKeyvalTrigger *self1 = (GtkKeyvalTrigger *) trigger1; - GtkKeyvalTrigger *self2 = (GtkKeyvalTrigger *) trigger2; - - if (self1->modifiers != self2->modifiers) - return self2->modifiers - self1->modifiers; - - return self1->keyval - self2->keyval; -} - -static void -gtk_keyval_trigger_print (GtkShortcutTrigger *trigger, - GString *string) - -{ - GtkKeyvalTrigger *self = (GtkKeyvalTrigger *) trigger; - char *accelerator_name; - - accelerator_name = gtk_accelerator_name (self->keyval, self->modifiers); - g_string_append (string, accelerator_name); - g_free (accelerator_name); -} - -static gboolean -gtk_keyval_trigger_print_label (GtkShortcutTrigger *trigger, - GdkDisplay *display, - GString *string) -{ - GtkKeyvalTrigger *self = (GtkKeyvalTrigger *) trigger; - - gtk_accelerator_print_label (string, self->keyval, self->modifiers); - - return TRUE; -} - -static const GtkShortcutTriggerClass GTK_KEYVAL_TRIGGER_CLASS = { - GTK_SHORTCUT_TRIGGER_KEYVAL, - sizeof (GtkKeyvalTrigger), - "GtkKeyvalTrigger", - gtk_keyval_trigger_finalize, - gtk_keyval_trigger_trigger, - gtk_keyval_trigger_hash, - gtk_keyval_trigger_compare, - gtk_keyval_trigger_print, - gtk_keyval_trigger_print_label -}; - -/** - * gtk_keyval_trigger_new: - * @keyval: The keyval to trigger for - * @modifiers: the modifiers that need to be present - * - * Creates a #GtkShortcutTrigger that will trigger whenever - * the key with the given @keyval and @modifiers is pressed. - * - * Returns: A new #GtkShortcutTrigger - */ -GtkShortcutTrigger * -gtk_keyval_trigger_new (guint keyval, - GdkModifierType modifiers) -{ - GtkKeyvalTrigger *self; - - self = (GtkKeyvalTrigger *) gtk_shortcut_trigger_new (>K_KEYVAL_TRIGGER_CLASS); - - /* We store keyvals as lower key */ - if (keyval == GDK_KEY_ISO_Left_Tab) - self->keyval = GDK_KEY_Tab; - else - self->keyval = gdk_keyval_to_lower (keyval); - self->modifiers = modifiers; - - return &self->trigger; -} - -/** - * gtk_keyval_trigger_get_modifiers: - * @self: a keyval #GtkShortcutTrigger - * - * Gets the modifiers that must be present to succeed - * triggering @self. - * - * Returns: the modifiers - **/ -GdkModifierType -gtk_keyval_trigger_get_modifiers (GtkShortcutTrigger *self) -{ - GtkKeyvalTrigger *trigger = (GtkKeyvalTrigger *) self; - - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER_TYPE (self, GTK_SHORTCUT_TRIGGER_KEYVAL), 0); - - return trigger->modifiers; -} - -/** - * gtk_keyval_trigger_get_keyval: - * @self: a keyval #GtkShortcutTrigger - * - * Gets the keyval that must be pressed to succeed - * triggering @self. - * - * Returns: the keyval - **/ -guint -gtk_keyval_trigger_get_keyval (GtkShortcutTrigger *self) -{ - GtkKeyvalTrigger *trigger = (GtkKeyvalTrigger *) self; - - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER_TYPE (self, GTK_SHORTCUT_TRIGGER_KEYVAL), 0); - - return trigger->keyval; -} - -/*** GTK_MNEMONIC_TRIGGER ***/ - -typedef struct _GtkMnemonicTrigger GtkMnemonicTrigger; - -struct _GtkMnemonicTrigger -{ - GtkShortcutTrigger trigger; - - guint keyval; -}; - -static void -gtk_mnemonic_trigger_finalize (GtkShortcutTrigger *trigger) -{ -} - -static gboolean -gtk_mnemonic_trigger_trigger (GtkShortcutTrigger *trigger, - GdkEvent *event, - gboolean enable_mnemonics) -{ - GtkMnemonicTrigger *self = (GtkMnemonicTrigger *) trigger; - guint keyval; - - if (!enable_mnemonics) - return FALSE; - - if (gdk_event_get_event_type (event) != GDK_KEY_PRESS) - return FALSE; - - /* XXX: This needs to deal with groups */ - keyval = gdk_key_event_get_keyval (event); - - if (keyval == GDK_KEY_ISO_Left_Tab) - keyval = GDK_KEY_Tab; - else - keyval = gdk_keyval_to_lower (keyval); - - return keyval == self->keyval; -} - -static guint -gtk_mnemonic_trigger_hash (GtkShortcutTrigger *trigger) -{ - GtkMnemonicTrigger *self = (GtkMnemonicTrigger *) trigger; - - return (self->keyval << 8) - | GTK_SHORTCUT_TRIGGER_MNEMONIC; -} - -static int -gtk_mnemonic_trigger_compare (GtkShortcutTrigger *trigger1, - GtkShortcutTrigger *trigger2) -{ - GtkMnemonicTrigger *self1 = (GtkMnemonicTrigger *) trigger1; - GtkMnemonicTrigger *self2 = (GtkMnemonicTrigger *) trigger2; - - return self1->keyval - self2->keyval; -} - -static void -gtk_mnemonic_trigger_print (GtkShortcutTrigger *trigger, - GString *string) -{ - GtkMnemonicTrigger *self = (GtkMnemonicTrigger *) trigger; - const char *keyval_str; - - keyval_str = gdk_keyval_name (self->keyval); - if (keyval_str == NULL) - keyval_str = "???"; - - g_string_append (string, "<Mnemonic>"); - g_string_append (string, keyval_str); -} - -static gboolean -gtk_mnemonic_trigger_print_label (GtkShortcutTrigger *trigger, - GdkDisplay *display, - GString *string) -{ - GtkMnemonicTrigger *self = (GtkMnemonicTrigger *) trigger; - const char *keyval_str; - - keyval_str = gdk_keyval_name (self->keyval); - if (keyval_str == NULL) - return FALSE; - - g_string_append (string, keyval_str); - - return TRUE; -} - -static const GtkShortcutTriggerClass GTK_MNEMONIC_TRIGGER_CLASS = { - GTK_SHORTCUT_TRIGGER_MNEMONIC, - sizeof (GtkMnemonicTrigger), - "GtkMnemonicTrigger", - gtk_mnemonic_trigger_finalize, - gtk_mnemonic_trigger_trigger, - gtk_mnemonic_trigger_hash, - gtk_mnemonic_trigger_compare, - gtk_mnemonic_trigger_print, - gtk_mnemonic_trigger_print_label -}; - -/** - * gtk_mnemonic_trigger_new: - * @keyval: The keyval to trigger for - * - * Creates a #GtkShortcutTrigger that will trigger whenever the key with - * the given @keyval is pressed and mnemonics have been activated. - * - * Mnemonics are activated by calling code when a key event with the right - * modifiers is detected. - * - * Returns: A new #GtkShortcutTrigger - */ -GtkShortcutTrigger * -gtk_mnemonic_trigger_new (guint keyval) -{ - GtkMnemonicTrigger *self; - - self = (GtkMnemonicTrigger *) gtk_shortcut_trigger_new (>K_MNEMONIC_TRIGGER_CLASS); - - /* We store keyvals as lower key */ - if (keyval == GDK_KEY_ISO_Left_Tab) - self->keyval = GDK_KEY_Tab; - else - self->keyval = gdk_keyval_to_lower (keyval); - - return &self->trigger; -} - -/** - * gtk_mnemonic_trigger_get_keyval: - * @trigger: a mnemonic #GtkShortcutTrigger - * - * Gets the keyval that must be pressed to succeed triggering @self. - * - * Returns: the keyval - **/ -guint -gtk_mnemonic_trigger_get_keyval (GtkShortcutTrigger *trigger) -{ - GtkMnemonicTrigger *self = (GtkMnemonicTrigger *) trigger; - - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER_TYPE (trigger, GTK_SHORTCUT_TRIGGER_MNEMONIC), 0); - - return self->keyval; -} - -/*** GTK_ALTERNATIVE_TRIGGER ***/ - -typedef struct _GtkAlternativeTrigger GtkAlternativeTrigger; - -struct _GtkAlternativeTrigger -{ - GtkShortcutTrigger trigger; - - GtkShortcutTrigger *first; - GtkShortcutTrigger *second; -}; - -static void -gtk_alternative_trigger_finalize (GtkShortcutTrigger *trigger) -{ - GtkAlternativeTrigger *self = (GtkAlternativeTrigger *) trigger; - - gtk_shortcut_trigger_unref (self->first); - gtk_shortcut_trigger_unref (self->second); -} - -static gboolean -gtk_alternative_trigger_trigger (GtkShortcutTrigger *trigger, - GdkEvent *event, - gboolean enable_mnemonics) -{ - GtkAlternativeTrigger *self = (GtkAlternativeTrigger *) trigger; - - if (gtk_shortcut_trigger_trigger (self->first, event, enable_mnemonics)) - return TRUE; - - if (gtk_shortcut_trigger_trigger (self->second, event, enable_mnemonics)) - return TRUE; - - return FALSE; -} - -static guint -gtk_alternative_trigger_hash (GtkShortcutTrigger *trigger) -{ - GtkAlternativeTrigger *self = (GtkAlternativeTrigger *) trigger; - guint result; - - result = gtk_shortcut_trigger_hash (self->first); - result <<= 5; - - result |= gtk_shortcut_trigger_hash (self->second); - result <<= 5; - - return result | GTK_SHORTCUT_TRIGGER_ALTERNATIVE; -} - -static int -gtk_alternative_trigger_compare (GtkShortcutTrigger *trigger1, - GtkShortcutTrigger *trigger2) -{ - GtkAlternativeTrigger *self1 = (GtkAlternativeTrigger *) trigger1; - GtkAlternativeTrigger *self2 = (GtkAlternativeTrigger *) trigger2; - int cmp; - - cmp = gtk_shortcut_trigger_compare (self1->first, self2->first); - if (cmp != 0) - return cmp; - - return gtk_shortcut_trigger_compare (self1->second, self2->second); -} - -static void -gtk_alternative_trigger_print (GtkShortcutTrigger *trigger, - GString *string) -{ - GtkAlternativeTrigger *self = (GtkAlternativeTrigger *) trigger; - - gtk_shortcut_trigger_print (self->first, string); - g_string_append (string, ", "); - gtk_shortcut_trigger_print (self->second, string); -} - -static gboolean -gtk_alternative_trigger_print_label (GtkShortcutTrigger *trigger, - GdkDisplay *display, - GString *string) -{ - GtkAlternativeTrigger *self = (GtkAlternativeTrigger *) trigger; - - if (gtk_shortcut_trigger_print_label (self->first, display, string)) - { - g_string_append (string, ", "); - if (!gtk_shortcut_trigger_print_label (self->second, display, string)) - g_string_truncate (string, string->len - 2); - return TRUE; - } - else - { - return gtk_shortcut_trigger_print_label (self->second, display, string); - } -} - -static const GtkShortcutTriggerClass GTK_ALTERNATIVE_TRIGGER_CLASS = { - GTK_SHORTCUT_TRIGGER_ALTERNATIVE, - sizeof (GtkAlternativeTrigger), - "GtkAlternativeTrigger", - gtk_alternative_trigger_finalize, - gtk_alternative_trigger_trigger, - gtk_alternative_trigger_hash, - gtk_alternative_trigger_compare, - gtk_alternative_trigger_print, - gtk_alternative_trigger_print_label -}; - -/** - * gtk_alternative_trigger_new: - * @first: (transfer full): The first trigger that may trigger - * @second: (transfer full): The second trigger that may trigger - * - * Creates a #GtkShortcutTrigger that will trigger whenever - * either of the two given triggers gets triggered. - * - * Note that nesting is allowed, so if you want more than two - * alternative, create a new alternative trigger for each option. - * - * Returns: a new #GtkShortcutTrigger - */ -GtkShortcutTrigger * -gtk_alternative_trigger_new (GtkShortcutTrigger *first, - GtkShortcutTrigger *second) -{ - GtkAlternativeTrigger *self; - - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (first), NULL); - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER (second), NULL); - - self = (GtkAlternativeTrigger *) gtk_shortcut_trigger_new (>K_ALTERNATIVE_TRIGGER_CLASS); - - self->first = first; - self->second = second; - - return &self->trigger; -} - -/** - * gtk_alternative_trigger_get_first: - * @self: an alternative #GtkShortcutTrigger - * - * Gets the first of the two alternative triggers that may - * trigger @self. gtk_alternative_trigger_get_second() will - * return the other one. - * - * Returns: (transfer none): the first alternative trigger - **/ -GtkShortcutTrigger * -gtk_alternative_trigger_get_first (GtkShortcutTrigger *self) -{ - GtkAlternativeTrigger *trigger = (GtkAlternativeTrigger *) self; - - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER_TYPE (self, GTK_SHORTCUT_TRIGGER_ALTERNATIVE), 0); - - return trigger->first; -} - -/** - * gtk_alternative_trigger_get_second: - * @self: an alternative #GtkShortcutTrigger - * - * Gets the second of the two alternative triggers that may - * trigger @self. gtk_alternative_trigger_get_first() will - * return the other one. - * - * Returns: (transfer none): the second alternative trigger - **/ -GtkShortcutTrigger * -gtk_alternative_trigger_get_second (GtkShortcutTrigger *self) -{ - GtkAlternativeTrigger *trigger = (GtkAlternativeTrigger *) self; - - g_return_val_if_fail (GTK_IS_SHORTCUT_TRIGGER_TYPE (self, GTK_SHORTCUT_TRIGGER_ALTERNATIVE), 0); - - return trigger->second; -} diff --git a/gtk/gtkshortcuttrigger.h b/gtk/gtkshortcuttrigger.h deleted file mode 100644 index b0ab8af862..0000000000 --- a/gtk/gtkshortcuttrigger.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright © 2018 Benjamin Otte - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Authors: Benjamin Otte <otte@gnome.org> - */ - -#ifndef __GTK_SHORTCUT_TRIGGER_H__ -#define __GTK_SHORTCUT_TRIGGER_H__ - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only <gtk/gtk.h> can be included directly." -#endif - -#include <gtk/gtktypes.h> - -G_BEGIN_DECLS - -#define GTK_TYPE_SHORTCUT_TRIGGER (gtk_shortcut_trigger_get_type ()) - -#define GTK_IS_SHORTCUT_TRIGGER(obj) ((obj) != NULL) - -/** - * GtkShortcutTriggerType: - * @GTK_SHORTCUT_TRIGGER_NEVER: Never ever trigger - * @GTK_SHORTCUT_TRIGGER_KEYVAL: Trigger if a key event with matching - * modifiers and keyval is received. - * @GTK_SHORTCUT_TRIGGER_MNEMONIC: Trigger if a key event with matching - * keyval is received and mnemonics are enabled for this event. - * @GTK_SHORTCUT_TRIGGER_ALTERNAITVE: Trigger if either if two - * alternatives triggers - * - * The type of a trigger determines what the trigger triggers on. - **/ -typedef enum { - GTK_SHORTCUT_TRIGGER_NEVER, - GTK_SHORTCUT_TRIGGER_KEYVAL, - GTK_SHORTCUT_TRIGGER_MNEMONIC, - GTK_SHORTCUT_TRIGGER_ALTERNATIVE -} GtkShortcutTriggerType; - -GDK_AVAILABLE_IN_ALL -GType gtk_shortcut_trigger_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -GtkShortcutTrigger * gtk_shortcut_trigger_ref (GtkShortcutTrigger *self); -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_trigger_unref (GtkShortcutTrigger *self); - -GDK_AVAILABLE_IN_ALL -GtkShortcutTriggerType gtk_shortcut_trigger_get_trigger_type (GtkShortcutTrigger *self); - -GDK_AVAILABLE_IN_ALL -GtkShortcutTrigger * gtk_shortcut_trigger_parse_string (const char *string); - -GDK_AVAILABLE_IN_ALL -char * gtk_shortcut_trigger_to_string (GtkShortcutTrigger *self); -GDK_AVAILABLE_IN_ALL -void gtk_shortcut_trigger_print (GtkShortcutTrigger *self, - GString *string); -GDK_AVAILABLE_IN_ALL -char * gtk_shortcut_trigger_to_label (GtkShortcutTrigger *self, - GdkDisplay *display); -GDK_AVAILABLE_IN_ALL -gboolean gtk_shortcut_trigger_print_label (GtkShortcutTrigger *self, - GdkDisplay *display, - GString *string); - -GDK_AVAILABLE_IN_ALL -guint gtk_shortcut_trigger_hash (gconstpointer trigger); -GDK_AVAILABLE_IN_ALL -gboolean gtk_shortcut_trigger_equal (gconstpointer trigger1, - gconstpointer trigger2); -GDK_AVAILABLE_IN_ALL -gint gtk_shortcut_trigger_compare (gconstpointer trigger1, - gconstpointer trigger2); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_shortcut_trigger_trigger (GtkShortcutTrigger *self, - GdkEvent *event, - gboolean enable_mnemonics); - -GDK_AVAILABLE_IN_ALL -GtkShortcutTrigger * gtk_never_trigger_get (void); - -GDK_AVAILABLE_IN_ALL -GtkShortcutTrigger * gtk_keyval_trigger_new (guint keyval, - GdkModifierType modifiers); -GDK_AVAILABLE_IN_ALL -GdkModifierType gtk_keyval_trigger_get_modifiers (GtkShortcutTrigger *self); -GDK_AVAILABLE_IN_ALL -guint gtk_keyval_trigger_get_keyval (GtkShortcutTrigger *self); - -GDK_AVAILABLE_IN_ALL -GtkShortcutTrigger * gtk_mnemonic_trigger_new (guint keyval); -GDK_AVAILABLE_IN_ALL -guint gtk_mnemonic_trigger_get_keyval (GtkShortcutTrigger *self); - -GDK_AVAILABLE_IN_ALL -GtkShortcutTrigger * gtk_alternative_trigger_new (GtkShortcutTrigger *one, - GtkShortcutTrigger *two); -GDK_AVAILABLE_IN_ALL -GtkShortcutTrigger * gtk_alternative_trigger_get_first (GtkShortcutTrigger *self); -GDK_AVAILABLE_IN_ALL -GtkShortcutTrigger * gtk_alternative_trigger_get_second (GtkShortcutTrigger *self); - -G_END_DECLS - -#endif /* __GTK_SHORTCUT_TRIGGER_H__ */ diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c index 0176797f1e..548bac5f16 100644 --- a/gtk/gtkspinbutton.c +++ b/gtk/gtkspinbutton.c @@ -30,6 +30,7 @@ #include "gtkspinbutton.h" #include "gtkadjustment.h" +#include "gtkbindings.h" #include "gtkbox.h" #include "gtkbutton.h" #include "gtkcssstylepropertyprivate.h" @@ -316,10 +317,10 @@ G_DEFINE_TYPE_WITH_CODE (GtkSpinButton, gtk_spin_button, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE, gtk_spin_button_cell_editable_init)) -#define add_spin_binding(widget_class, keyval, mask, scroll) \ - gtk_widget_class_add_binding_signal (widget_class, keyval, mask, \ - "change-value", \ - "(i)", scroll) +#define add_spin_binding(binding_set, keyval, mask, scroll) \ + gtk_binding_entry_add_signal (binding_set, keyval, mask, \ + "change-value", 1, \ + GTK_TYPE_SCROLL_TYPE, scroll) static gboolean @@ -346,6 +347,7 @@ gtk_spin_button_class_init (GtkSpinButtonClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + GtkBindingSet *binding_set; gobject_class->finalize = gtk_spin_button_finalize; gobject_class->set_property = gtk_spin_button_set_property; @@ -543,16 +545,18 @@ gtk_spin_button_class_init (GtkSpinButtonClass *class) G_TYPE_NONE, 1, GTK_TYPE_SCROLL_TYPE); - add_spin_binding (widget_class, GDK_KEY_Up, 0, GTK_SCROLL_STEP_UP); - add_spin_binding (widget_class, GDK_KEY_KP_Up, 0, GTK_SCROLL_STEP_UP); - add_spin_binding (widget_class, GDK_KEY_Down, 0, GTK_SCROLL_STEP_DOWN); - add_spin_binding (widget_class, GDK_KEY_KP_Down, 0, GTK_SCROLL_STEP_DOWN); - add_spin_binding (widget_class, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_UP); - add_spin_binding (widget_class, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_DOWN); - add_spin_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK, GTK_SCROLL_END); - add_spin_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START); - add_spin_binding (widget_class, GDK_KEY_Page_Up, GDK_CONTROL_MASK, GTK_SCROLL_END); - add_spin_binding (widget_class, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_START); + binding_set = gtk_binding_set_by_class (class); + + add_spin_binding (binding_set, GDK_KEY_Up, 0, GTK_SCROLL_STEP_UP); + add_spin_binding (binding_set, GDK_KEY_KP_Up, 0, GTK_SCROLL_STEP_UP); + add_spin_binding (binding_set, GDK_KEY_Down, 0, GTK_SCROLL_STEP_DOWN); + add_spin_binding (binding_set, GDK_KEY_KP_Down, 0, GTK_SCROLL_STEP_DOWN); + add_spin_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_UP); + add_spin_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_DOWN); + add_spin_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK, GTK_SCROLL_END); + add_spin_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START); + add_spin_binding (binding_set, GDK_KEY_Page_Up, GDK_CONTROL_MASK, GTK_SCROLL_END); + add_spin_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_START); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SPIN_BUTTON_ACCESSIBLE); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); diff --git a/gtk/gtktext.c b/gtk/gtktext.c index 70a5d5276c..b052cebb62 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -25,6 +25,7 @@ #include "gtkactionable.h" #include "gtkadjustment.h" +#include "gtkbindings.h" #include "gtkbox.h" #include "gtkbutton.h" #include "gtkcssnodeprivate.h" @@ -381,9 +382,7 @@ static void gtk_text_set_alignment (GtkText *self, /* Default signal handlers */ static GMenuModel *gtk_text_get_menu_model (GtkText *self); -static void gtk_text_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters); +static gboolean gtk_text_popup_menu (GtkWidget *widget); static void gtk_text_move_cursor (GtkText *self, GtkMovementStep step, int count, @@ -680,7 +679,7 @@ G_DEFINE_TYPE_WITH_CODE (GtkText, gtk_text, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, gtk_text_editable_init)) static void -add_move_binding (GtkWidgetClass *widget_class, +add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask, GtkMovementStep step, @@ -688,22 +687,28 @@ add_move_binding (GtkWidgetClass *widget_class, { g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0); - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask, - "move-cursor", - "(iib)", step, count, FALSE); + gtk_binding_entry_add_signal (binding_set, keyval, modmask, + "move-cursor", 3, + G_TYPE_ENUM, step, + G_TYPE_INT, count, + G_TYPE_BOOLEAN, FALSE); + /* Selection-extending version */ - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask | GDK_SHIFT_MASK, - "move-cursor", - "(iib)", step, count, TRUE); + gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK, + "move-cursor", 3, + G_TYPE_ENUM, step, + G_TYPE_INT, count, + G_TYPE_BOOLEAN, TRUE); } static void gtk_text_class_init (GtkTextClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + GtkWidgetClass *widget_class; + GtkBindingSet *binding_set; + + widget_class = (GtkWidgetClass*) class; gobject_class->dispose = gtk_text_dispose; gobject_class->finalize = gtk_text_finalize; @@ -723,6 +728,7 @@ gtk_text_class_init (GtkTextClass *class) widget_class->state_flags_changed = gtk_text_state_flags_changed; widget_class->root = gtk_text_root; widget_class->mnemonic_activate = gtk_text_mnemonic_activate; + widget_class->popup_menu = gtk_text_popup_menu; class->move_cursor = gtk_text_move_cursor; class->insert_at_cursor = gtk_text_insert_at_cursor; @@ -1190,217 +1196,187 @@ gtk_text_class_init (GtkTextClass *class) gtk_widget_class_install_action (widget_class, "text.undo", NULL, gtk_text_real_undo); gtk_widget_class_install_action (widget_class, "text.redo", NULL, gtk_text_real_redo); - gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_text_popup_menu); /* * Key bindings */ - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_F10, GDK_SHIFT_MASK, - "menu.popup", - NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_Menu, 0, - "menu.popup", - NULL); + binding_set = gtk_binding_set_by_class (class); /* Moving the insertion point */ - add_move_binding (widget_class, GDK_KEY_Right, 0, + add_move_binding (binding_set, GDK_KEY_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); - add_move_binding (widget_class, GDK_KEY_Left, 0, + add_move_binding (binding_set, GDK_KEY_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); - add_move_binding (widget_class, GDK_KEY_KP_Right, 0, + add_move_binding (binding_set, GDK_KEY_KP_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Left, 0, + add_move_binding (binding_set, GDK_KEY_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); - add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1); - add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, -1); - add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, -1); - add_move_binding (widget_class, GDK_KEY_Home, 0, + add_move_binding (binding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_End, 0, + add_move_binding (binding_set, GDK_KEY_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Home, 0, + add_move_binding (binding_set, GDK_KEY_KP_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_KP_End, 0, + add_move_binding (binding_set, GDK_KEY_KP_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); - add_move_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Home, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_KP_End, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, 1); /* Select all */ - gtk_widget_class_add_binding (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK, - (GtkShortcutFunc) gtk_text_select_all, - NULL); - - gtk_widget_class_add_binding (widget_class, - GDK_KEY_slash, GDK_CONTROL_MASK, - (GtkShortcutFunc) gtk_text_select_all, - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, + "move-cursor", 3, + GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, + G_TYPE_INT, -1, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, + "move-cursor", 3, + GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, + G_TYPE_INT, 1, + G_TYPE_BOOLEAN, TRUE); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, + "move-cursor", 3, + GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, + G_TYPE_INT, -1, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, + "move-cursor", 3, + GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, + G_TYPE_INT, 1, + G_TYPE_BOOLEAN, TRUE); /* Unselect all */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_backslash, GDK_CONTROL_MASK, - "move-cursor", - "(iib)", GTK_MOVEMENT_VISUAL_POSITIONS, 0, FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - "move-cursor", - "(iib)", GTK_MOVEMENT_VISUAL_POSITIONS, 0, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK, + "move-cursor", 3, + GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 0, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "move-cursor", 3, + GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 0, + G_TYPE_BOOLEAN, FALSE); /* Activate */ - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0, - "activate", - NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0, - "activate", - NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, 0, - "activate", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, + "activate", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0, + "activate", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, + "activate", 0); /* Deleting text */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Delete, 0, - "delete-from-cursor", - "(ii)", GTK_DELETE_CHARS, 1); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Delete, 0, - "delete-from-cursor", - "(ii)", GTK_DELETE_CHARS, 1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, 0, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_CHARS, + G_TYPE_INT, 1); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, 0, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_CHARS, + G_TYPE_INT, 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_BackSpace, 0, - "backspace", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0, + "backspace", 0); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_u, GDK_CONTROL_MASK, - "delete-from-cursor", - "(ii)", GTK_DELETE_PARAGRAPH_ENDS, -1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_u, GDK_CONTROL_MASK, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS, + G_TYPE_INT, -1); /* Make this do the same as Backspace, to help with mis-typing */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_BackSpace, GDK_SHIFT_MASK, - "backspace", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Delete, GDK_CONTROL_MASK, - "delete-from-cursor", - "(ii)", GTK_DELETE_WORD_ENDS, 1); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Delete, GDK_CONTROL_MASK, - "delete-from-cursor", - "(ii)", GTK_DELETE_WORD_ENDS, 1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_SHIFT_MASK, + "backspace", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_CONTROL_MASK, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, + G_TYPE_INT, 1); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_CONTROL_MASK, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, + G_TYPE_INT, 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_BackSpace, GDK_CONTROL_MASK, - "delete-from-cursor", - "(ii)", GTK_DELETE_WORD_ENDS, -1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, + G_TYPE_INT, -1); /* Cut/copy/paste */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_x, GDK_CONTROL_MASK, - "cut-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_c, GDK_CONTROL_MASK, - "copy-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_v, GDK_CONTROL_MASK, - "paste-clipboard", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Delete, GDK_SHIFT_MASK, - "cut-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Insert, GDK_CONTROL_MASK, - "copy-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Insert, GDK_SHIFT_MASK, - "paste-clipboard", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Delete, GDK_SHIFT_MASK, - "cut-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Insert, GDK_CONTROL_MASK, - "copy-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Insert, GDK_SHIFT_MASK, - "paste-clipboard", - NULL); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_x, GDK_CONTROL_MASK, + "cut-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK, + "copy-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_v, GDK_CONTROL_MASK, + "paste-clipboard", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_SHIFT_MASK, + "cut-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_CONTROL_MASK, + "copy-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_SHIFT_MASK, + "paste-clipboard", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_SHIFT_MASK, + "cut-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, GDK_CONTROL_MASK, + "copy-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, GDK_SHIFT_MASK, + "paste-clipboard", 0); /* Overwrite */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Insert, 0, - "toggle-overwrite", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Insert, 0, - "toggle-overwrite", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, 0, + "toggle-overwrite", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, 0, + "toggle-overwrite", 0); /* Emoji */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_period, GDK_CONTROL_MASK, - "insert-emoji", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_semicolon, GDK_CONTROL_MASK, - "insert-emoji", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_period, GDK_CONTROL_MASK, + "insert-emoji", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_semicolon, GDK_CONTROL_MASK, + "insert-emoji", 0); /* Undo/Redo */ - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_z, GDK_CONTROL_MASK, - "text.undo", NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_y, GDK_CONTROL_MASK, - "text.redo", NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "text.redo", NULL); - + gtk_binding_entry_add_action (binding_set, GDK_KEY_z, GDK_CONTROL_MASK, + "text.undo", NULL); + gtk_binding_entry_add_action (binding_set, GDK_KEY_y, GDK_CONTROL_MASK, + "text.redo", NULL); + gtk_binding_entry_add_action (binding_set, GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "text.redo", NULL); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_TEXT_ACCESSIBLE); gtk_widget_class_set_css_name (widget_class, I_("text")); @@ -5936,12 +5912,11 @@ gtk_text_mnemonic_activate (GtkWidget *widget, return GDK_EVENT_STOP; } -static void -gtk_text_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters) +static gboolean +gtk_text_popup_menu (GtkWidget *widget) { gtk_text_do_popup (GTK_TEXT (widget), -1, -1); + return TRUE; } static void diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index 60842f76f5..62687d0f29 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -30,6 +30,7 @@ #include <string.h> #include "gtkadjustmentprivate.h" +#include "gtkbindings.h" #include "gtkcsscolorvalueprivate.h" #include "gtkdebug.h" #include "gtkdropcontrollermotion.h" @@ -434,9 +435,7 @@ static gboolean gtk_text_view_drag_drop (GtkDropTarget *dest, double y, GtkTextView *text_view); -static void gtk_text_view_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters); +static gboolean gtk_text_view_popup_menu (GtkWidget *widget); static void gtk_text_view_move_cursor (GtkTextView *text_view, GtkMovementStep step, gint count, @@ -775,7 +774,7 @@ gtk_text_view_drop_scroll_leave (GtkDropControllerMotion *motion, } static void -add_move_binding (GtkWidgetClass *widget_class, +add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask, GtkMovementStep step, @@ -783,16 +782,18 @@ add_move_binding (GtkWidgetClass *widget_class, { g_assert ((modmask & GDK_SHIFT_MASK) == 0); - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask, - "move-cursor", - "(iib)", step, count, FALSE); + gtk_binding_entry_add_signal (binding_set, keyval, modmask, + "move-cursor", 3, + G_TYPE_ENUM, step, + G_TYPE_INT, count, + G_TYPE_BOOLEAN, FALSE); /* Selection-extending version */ - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask | GDK_SHIFT_MASK, - "move-cursor", - "(iib)", step, count, TRUE); + gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK, + "move-cursor", 3, + G_TYPE_ENUM, step, + G_TYPE_INT, count, + G_TYPE_BOOLEAN, TRUE); } static void @@ -801,6 +802,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + GtkBindingSet *binding_set; /* Default handlers and virtual methods */ @@ -820,6 +822,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass) widget_class->snapshot = gtk_text_view_snapshot; widget_class->focus = gtk_text_view_focus; + widget_class->popup_menu = gtk_text_view_popup_menu; + container_class->add = gtk_text_view_add; container_class->remove = gtk_text_view_remove; container_class->forall = gtk_text_view_forall; @@ -1462,288 +1466,237 @@ gtk_text_view_class_init (GtkTextViewClass *klass) gtk_widget_class_install_action (widget_class, "text.undo", NULL, gtk_text_view_real_undo); gtk_widget_class_install_action (widget_class, "text.redo", NULL, gtk_text_view_real_redo); - gtk_widget_class_install_action (widget_class, "menu.popup", NULL, gtk_text_view_popup_menu); /* * Key bindings */ - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_F10, GDK_SHIFT_MASK, - "menu.popup", - NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_Menu, 0, - "menu.popup", - NULL); - + binding_set = gtk_binding_set_by_class (klass); + /* Moving the insertion point */ - add_move_binding (widget_class, GDK_KEY_Right, 0, + add_move_binding (binding_set, GDK_KEY_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Right, 0, + add_move_binding (binding_set, GDK_KEY_KP_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); - add_move_binding (widget_class, GDK_KEY_Left, 0, + add_move_binding (binding_set, GDK_KEY_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); - add_move_binding (widget_class, GDK_KEY_KP_Left, 0, + add_move_binding (binding_set, GDK_KEY_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); - add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1); - add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, -1); - add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, -1); - add_move_binding (widget_class, GDK_KEY_Up, 0, + add_move_binding (binding_set, GDK_KEY_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1); - add_move_binding (widget_class, GDK_KEY_KP_Up, 0, + add_move_binding (binding_set, GDK_KEY_KP_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1); - add_move_binding (widget_class, GDK_KEY_Down, 0, + add_move_binding (binding_set, GDK_KEY_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1); - add_move_binding (widget_class, GDK_KEY_KP_Down, 0, + add_move_binding (binding_set, GDK_KEY_KP_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1); - add_move_binding (widget_class, GDK_KEY_Up, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_MOVEMENT_PARAGRAPHS, -1); - add_move_binding (widget_class, GDK_KEY_KP_Up, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK, GTK_MOVEMENT_PARAGRAPHS, -1); - add_move_binding (widget_class, GDK_KEY_Down, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK, GTK_MOVEMENT_PARAGRAPHS, 1); - add_move_binding (widget_class, GDK_KEY_KP_Down, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK, GTK_MOVEMENT_PARAGRAPHS, 1); - add_move_binding (widget_class, GDK_KEY_Home, 0, + add_move_binding (binding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_KP_Home, 0, + add_move_binding (binding_set, GDK_KEY_KP_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_End, 0, + add_move_binding (binding_set, GDK_KEY_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); - add_move_binding (widget_class, GDK_KEY_KP_End, 0, + add_move_binding (binding_set, GDK_KEY_KP_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); - add_move_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_KP_Home, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, -1); - add_move_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, 1); - add_move_binding (widget_class, GDK_KEY_KP_End, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK, GTK_MOVEMENT_BUFFER_ENDS, 1); - add_move_binding (widget_class, GDK_KEY_Page_Up, 0, + add_move_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_MOVEMENT_PAGES, -1); - add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, + add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0, GTK_MOVEMENT_PAGES, -1); - add_move_binding (widget_class, GDK_KEY_Page_Down, 0, + add_move_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_MOVEMENT_PAGES, 1); - add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, + add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0, GTK_MOVEMENT_PAGES, 1); - add_move_binding (widget_class, GDK_KEY_Page_Up, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Page_Up, GDK_CONTROL_MASK, GTK_MOVEMENT_HORIZONTAL_PAGES, -1); - add_move_binding (widget_class, GDK_KEY_KP_Page_Up, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Page_Up, GDK_CONTROL_MASK, GTK_MOVEMENT_HORIZONTAL_PAGES, -1); - add_move_binding (widget_class, GDK_KEY_Page_Down, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_MOVEMENT_HORIZONTAL_PAGES, 1); - add_move_binding (widget_class, GDK_KEY_KP_Page_Down, GDK_CONTROL_MASK, + add_move_binding (binding_set, GDK_KEY_KP_Page_Down, GDK_CONTROL_MASK, GTK_MOVEMENT_HORIZONTAL_PAGES, 1); /* Select all */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_CONTROL_MASK, - "select-all", - "(b)", TRUE); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_slash, GDK_CONTROL_MASK, - "select-all", - "(b)", TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, + "select-all", 1, + G_TYPE_BOOLEAN, TRUE); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, + "select-all", 1, + G_TYPE_BOOLEAN, TRUE); /* Unselect all */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_backslash, GDK_CONTROL_MASK, - "select-all", - "(b)", FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK, + "select-all", 1, + G_TYPE_BOOLEAN, FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - "select-all", - "(b)", FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "select-all", 1, + G_TYPE_BOOLEAN, FALSE); /* Deleting text */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Delete, 0, - "delete-from-cursor", - "(ii)", GTK_DELETE_CHARS, 1); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Delete, 0, - "delete-from-cursor", - "(ii)", GTK_DELETE_CHARS, 1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, 0, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_CHARS, + G_TYPE_INT, 1); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, 0, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_CHARS, + G_TYPE_INT, 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_BackSpace, 0, - "backspace", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0, + "backspace", 0); /* Make this do the same as Backspace, to help with mis-typing */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_BackSpace, GDK_SHIFT_MASK, - "backspace", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Delete, GDK_CONTROL_MASK, - "delete-from-cursor", - "(ii)", GTK_DELETE_WORD_ENDS, 1); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Delete, GDK_CONTROL_MASK, - "delete-from-cursor", - "(ii)", GTK_DELETE_WORD_ENDS, 1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_SHIFT_MASK, + "backspace", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_CONTROL_MASK, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, + G_TYPE_INT, 1); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_CONTROL_MASK, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, + G_TYPE_INT, 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_BackSpace, GDK_CONTROL_MASK, - "delete-from-cursor", - "(ii)", GTK_DELETE_WORD_ENDS, -1); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Delete, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - "delete-from-cursor", - "(ii)", GTK_DELETE_PARAGRAPH_ENDS, 1); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Delete, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - "delete-from-cursor", - "(ii)", GTK_DELETE_PARAGRAPH_ENDS, 1); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_BackSpace, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - "delete-from-cursor", - "(ii)", GTK_DELETE_PARAGRAPH_ENDS, -1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, + G_TYPE_INT, -1); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS, + G_TYPE_INT, 1); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS, + G_TYPE_INT, 1); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "delete-from-cursor", 2, + G_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS, + G_TYPE_INT, -1); /* Cut/copy/paste */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_x, GDK_CONTROL_MASK, - "cut-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_c, GDK_CONTROL_MASK, - "copy-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_v, GDK_CONTROL_MASK, - "paste-clipboard", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Delete, GDK_SHIFT_MASK, - "cut-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Insert, GDK_CONTROL_MASK, - "copy-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Insert, GDK_SHIFT_MASK, - "paste-clipboard", - NULL); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Delete, GDK_SHIFT_MASK, - "cut-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Insert, GDK_CONTROL_MASK, - "copy-clipboard", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Insert, GDK_SHIFT_MASK, - "paste-clipboard", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_x, GDK_CONTROL_MASK, + "cut-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK, + "copy-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_v, GDK_CONTROL_MASK, + "paste-clipboard", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_SHIFT_MASK, + "cut-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, GDK_CONTROL_MASK, + "copy-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, GDK_SHIFT_MASK, + "paste-clipboard", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_SHIFT_MASK, + "cut-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_CONTROL_MASK, + "copy-clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_SHIFT_MASK, + "paste-clipboard", 0); /* Undo/Redo */ - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_z, GDK_CONTROL_MASK, - "text.undo", NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_y, GDK_CONTROL_MASK, - "text.redo", NULL); - gtk_widget_class_add_binding_action (widget_class, - GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "text.redo", NULL); + gtk_binding_entry_add_action (binding_set, GDK_KEY_z, GDK_CONTROL_MASK, + "text.undo", NULL); + gtk_binding_entry_add_action (binding_set, GDK_KEY_y, GDK_CONTROL_MASK, + "text.redo", NULL); + gtk_binding_entry_add_action (binding_set, GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "text.redo", NULL); /* Overwrite */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Insert, 0, - "toggle-overwrite", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Insert, 0, - "toggle-overwrite", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, 0, + "toggle-overwrite", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, 0, + "toggle-overwrite", 0); /* Emoji */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_period, GDK_CONTROL_MASK, - "insert-emoji", - NULL); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_semicolon, GDK_CONTROL_MASK, - "insert-emoji", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_period, GDK_CONTROL_MASK, + "insert-emoji", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_semicolon, GDK_CONTROL_MASK, + "insert-emoji", 0); /* Caret mode */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_F7, 0, - "toggle-cursor-visible", - NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_F7, 0, + "toggle-cursor-visible", 0); /* Control-tab focus motion */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Tab, GDK_CONTROL_MASK, - "move-focus", - "(i)", GTK_DIR_TAB_FORWARD); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Tab, GDK_CONTROL_MASK, - "move-focus", - "(i)", GTK_DIR_TAB_FORWARD); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, GDK_CONTROL_MASK, + "move-focus", 1, + GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, GDK_CONTROL_MASK, + "move-focus", 1, + GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - "move-focus", - "(i)", GTK_DIR_TAB_BACKWARD); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK, - "move-focus", - "(i)", GTK_DIR_TAB_BACKWARD); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "move-focus", 1, + GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "move-focus", 1, + GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_TEXT_VIEW_ACCESSIBLE); gtk_widget_class_set_css_name (widget_class, I_("textview")); @@ -4475,9 +4428,6 @@ gtk_text_view_size_allocate (GtkWidget *widget, if (priv->popup_menu) gtk_native_check_resize (GTK_NATIVE (priv->popup_menu)); - - if (priv->selection_bubble) - gtk_native_check_resize (GTK_NATIVE (priv->selection_bubble)); } static void @@ -8705,12 +8655,11 @@ gtk_text_view_do_popup (GtkTextView *text_view, gdk_event_unref (trigger_event); } -static void -gtk_text_view_popup_menu (GtkWidget *widget, - const char *action_name, - GVariant *parameters) +static gboolean +gtk_text_view_popup_menu (GtkWidget *widget) { gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL); + return TRUE; } static void diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index 51fa6d5229..a30d0c0973 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -22,6 +22,7 @@ #include "gtkadjustmentprivate.h" #include "gtkbox.h" +#include "gtkbindings.h" #include "gtkbuildable.h" #include "gtkbutton.h" #include "gtkcelllayout.h" @@ -53,7 +54,6 @@ #include "gtkrendericonprivate.h" #include "gtkscrollable.h" #include "gtksettingsprivate.h" -#include "gtkshortcutcontroller.h" #include "gtksnapshot.h" #include "gtkstylecontextprivate.h" #include "gtktooltip.h" @@ -657,11 +657,6 @@ static void gtk_tree_view_size_allocate (GtkWidget *widget, static void gtk_tree_view_snapshot (GtkWidget *widget, GtkSnapshot *snapshot); -static gboolean gtk_tree_view_forward_controller_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkTreeView *tree_view); static gboolean gtk_tree_view_key_controller_key_pressed (GtkEventControllerKey *key, guint keyval, guint keycode, @@ -770,7 +765,7 @@ static void invalidate_empty_focus (GtkTreeView *tree_view); static gboolean gtk_tree_view_is_expander_column (GtkTreeView *tree_view, GtkTreeViewColumn *column); static inline gboolean gtk_tree_view_draw_expanders (GtkTreeView *tree_view); -static void gtk_tree_view_add_move_binding (GtkWidgetClass *widget_class, +static void gtk_tree_view_add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask, gboolean add_shifted_binding, @@ -1004,9 +999,16 @@ G_DEFINE_TYPE_WITH_CODE (GtkTreeView, gtk_tree_view, GTK_TYPE_CONTAINER, static void gtk_tree_view_class_init (GtkTreeViewClass *class) { - GObjectClass *o_class = G_OBJECT_CLASS (class); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); - GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class); + GObjectClass *o_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + GtkBindingSet *binding_set; + + binding_set = gtk_binding_set_by_class (class); + + o_class = (GObjectClass *) class; + widget_class = (GtkWidgetClass *) class; + container_class = (GtkContainerClass *) class; /* GObject signals */ o_class->set_property = gtk_tree_view_set_property; @@ -1501,198 +1503,217 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) _gtk_marshal_BOOLEAN__VOIDv); /* Key bindings */ - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Up, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Up, 0, TRUE, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Up, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Up, 0, TRUE, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Down, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Down, 0, TRUE, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Down, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Down, 0, TRUE, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_p, GDK_CONTROL_MASK, FALSE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_p, GDK_CONTROL_MASK, FALSE, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_n, GDK_CONTROL_MASK, FALSE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_n, GDK_CONTROL_MASK, FALSE, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Home, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Home, 0, TRUE, GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Home, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Home, 0, TRUE, GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_End, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_End, 0, TRUE, GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_End, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_End, 0, TRUE, GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Page_Up, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Page_Up, 0, TRUE, GTK_MOVEMENT_PAGES, -1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0, TRUE, GTK_MOVEMENT_PAGES, -1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Page_Down, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Page_Down, 0, TRUE, GTK_MOVEMENT_PAGES, 1); - gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, TRUE, + gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0, TRUE, GTK_MOVEMENT_PAGES, 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Right, 0, - "move-cursor", - "(ii)", GTK_MOVEMENT_VISUAL_POSITIONS, 1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, 0, "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Left, 0, - "move-cursor", - "(ii)", GTK_MOVEMENT_VISUAL_POSITIONS, -1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, 0, "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Right, 0, - "move-cursor", - "(ii)", GTK_MOVEMENT_VISUAL_POSITIONS, 1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, 0, "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Left, 0, - "move-cursor", - "(ii)", GTK_MOVEMENT_VISUAL_POSITIONS, -1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, 0, "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Right, GDK_CONTROL_MASK, - "move-cursor", - "(ii)", GTK_MOVEMENT_VISUAL_POSITIONS, 1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, + "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Left, GDK_CONTROL_MASK, - "move-cursor", - "(ii)", GTK_MOVEMENT_VISUAL_POSITIONS, -1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, + "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Right, GDK_CONTROL_MASK, - "move-cursor", - "(ii)", GTK_MOVEMENT_VISUAL_POSITIONS, 1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK, + "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Left, GDK_CONTROL_MASK, - "move-cursor", - "(ii)", GTK_MOVEMENT_VISUAL_POSITIONS, -1); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK, + "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, GDK_CONTROL_MASK, "toggle-cursor-row", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_a, GDK_CONTROL_MASK, "select-all", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "select-all", NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, "select-all", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, "select-all", 0); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "unselect-all", NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK, "unselect-all", 0); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, GDK_SHIFT_MASK, "select-cursor-row", "(b)", TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", "(b)", TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_SHIFT_MASK, "select-cursor-row", 1, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1, + G_TYPE_BOOLEAN, TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, 0, "select-cursor-row", "(b)", TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, 0, "select-cursor-row", "(b)", TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0, "select-cursor-row", "(b)", TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0, "select-cursor-row", "(b)", TRUE); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, 0, "select-cursor-row", "(b)", TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0, "select-cursor-row", 1, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0, "select-cursor-row", 1, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, "select-cursor-row", 1, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0, "select-cursor-row", 1, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, "select-cursor-row", 1, + G_TYPE_BOOLEAN, TRUE); /* expand and collapse rows */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_plus, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, FALSE); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_asterisk, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Multiply, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, TRUE); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_slash, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Divide, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_plus, 0, "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_asterisk, 0, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Multiply, 0, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, 0, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Divide, 0, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE); /* Not doable on US keyboards */ - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_plus, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Add, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Add, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Add, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", TRUE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Right, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Right, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, TRUE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, TRUE, TRUE); - - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_minus, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_minus, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Subtract, 0, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, FALSE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Subtract, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", TRUE, FALSE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Left, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, FALSE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Left, GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, FALSE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, FALSE, TRUE); - gtk_widget_class_add_binding_signal (widget_class, - GDK_KEY_KP_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "expand-collapse-cursor-row", - "(bbb)", FALSE, FALSE, TRUE); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_BackSpace, 0, "select-cursor-parent", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", NULL); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_f, GDK_CONTROL_MASK, "start-interactive-search", NULL); - - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_F, GDK_CONTROL_MASK, "start-interactive-search", NULL); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_plus, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Add, 0, "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Add, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Add, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_minus, 0, "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_minus, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Subtract, 0, "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Subtract, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, GDK_SHIFT_MASK, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand-collapse-cursor-row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0, "select-cursor-parent", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_f, GDK_CONTROL_MASK, "start-interactive-search", 0); + + gtk_binding_entry_add_signal (binding_set, GDK_KEY_F, GDK_CONTROL_MASK, "start-interactive-search", 0); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_TREE_VIEW_ACCESSIBLE); gtk_widget_class_set_css_name (widget_class, I_("treeview")); @@ -1704,7 +1725,6 @@ gtk_tree_view_init (GtkTreeView *tree_view) GtkCssNode *widget_node; GtkGesture *gesture; GtkEventController *controller; - GList *list, *controllers; gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE); gtk_widget_set_overflow (GTK_WIDGET (tree_view), GTK_OVERFLOW_HIDDEN); @@ -1766,24 +1786,6 @@ gtk_tree_view_init (GtkTreeView *tree_view) gtk_css_node_set_state (tree_view->header_node, gtk_css_node_get_state (widget_node)); g_object_unref (tree_view->header_node); - controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "key-pressed", - G_CALLBACK (gtk_tree_view_forward_controller_key_pressed), tree_view); - gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); - - controllers = gtk_widget_list_controllers (GTK_WIDGET (tree_view), GTK_PHASE_BUBBLE); - for (list = controllers; list; list = list->next) - { - if (GTK_IS_SHORTCUT_CONTROLLER (list->data)) - { - g_object_ref (list->data); - gtk_widget_remove_controller (GTK_WIDGET (tree_view), list->data); - gtk_widget_add_controller (GTK_WIDGET (tree_view), list->data); - break; - } - } - g_list_free (controllers); - tree_view->click_gesture = gtk_gesture_click_new (); gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (tree_view->click_gesture), 0); g_signal_connect (tree_view->click_gesture, "pressed", @@ -5310,6 +5312,7 @@ gtk_tree_view_key_controller_key_pressed (GtkEventControllerKey *key, { GtkWidget *widget = GTK_WIDGET (tree_view); GtkWidget *button; + GdkEvent *event; if (tree_view->rubber_band_status) { @@ -5429,16 +5432,16 @@ gtk_tree_view_key_controller_key_pressed (GtkEventControllerKey *key, } } - return FALSE; -} + /* Handle the keybindings. */ + event = gtk_get_current_event (); + if (gtk_bindings_activate_event (G_OBJECT (widget), event)) + { + gdk_event_unref (event); + return TRUE; + } + + gdk_event_unref (event); -static gboolean -gtk_tree_view_forward_controller_key_pressed (GtkEventControllerKey *key, - guint keyval, - guint keycode, - GdkModifierType state, - GtkTreeView *tree_view) -{ if (tree_view->search_entry_avoid_unhandled_binding) { tree_view->search_entry_avoid_unhandled_binding = FALSE; @@ -5484,6 +5487,15 @@ gtk_tree_view_key_controller_key_released (GtkEventControllerKey *key, GdkModifierType state, GtkTreeView *tree_view) { + GdkEvent *event; + + if (tree_view->rubber_band_status) + return; + + /* Handle the keybindings. */ + event = gtk_get_current_event (); + gtk_bindings_activate_event (G_OBJECT (tree_view), event); + gdk_event_unref (event); } static void @@ -8902,7 +8914,7 @@ gtk_tree_view_draw_expanders (GtkTreeView *tree_view) } static void -gtk_tree_view_add_move_binding (GtkWidgetClass *widget_class, +gtk_tree_view_add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask, gboolean add_shifted_binding, @@ -8910,29 +8922,29 @@ gtk_tree_view_add_move_binding (GtkWidgetClass *widget_class, gint count) { - gtk_widget_class_add_binding_signal (widget_class, - keyval, modmask, - "move-cursor", - "(ii)", step, count); + gtk_binding_entry_add_signal (binding_set, keyval, modmask, + "move-cursor", 2, + G_TYPE_ENUM, step, + G_TYPE_INT, count); if (add_shifted_binding) - gtk_widget_class_add_binding_signal (widget_class, - keyval, GDK_SHIFT_MASK, - "move-cursor", - "(ii)", step, count); + gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK, + "move-cursor", 2, + G_TYPE_ENUM, step, + G_TYPE_INT, count); if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) return; - gtk_widget_class_add_binding_signal (widget_class, keyval, - GDK_CONTROL_MASK | GDK_SHIFT_MASK, - "move-cursor", - "(ii)", step, count); + gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "move-cursor", 2, + G_TYPE_ENUM, step, + G_TYPE_INT, count); - gtk_widget_class_add_binding_signal (widget_class, - keyval, GDK_CONTROL_MASK, - "move-cursor", - "(ii)", step, count); + gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK, + "move-cursor", 2, + G_TYPE_ENUM, step, + G_TYPE_INT, count); } static gint diff --git a/gtk/gtktypes.h b/gtk/gtktypes.h index 8edd13fcd1..a5ad190720 100644 --- a/gtk/gtktypes.h +++ b/gtk/gtktypes.h @@ -45,9 +45,6 @@ typedef struct _GtkNative GtkNative; typedef struct _GtkRequisition GtkRequisition; typedef struct _GtkRoot GtkRoot; typedef struct _GtkSettings GtkSettings; -typedef struct _GtkShortcut GtkShortcut; -typedef struct _GtkShortcutAction GtkShortcutAction; -typedef struct _GtkShortcutTrigger GtkShortcutTrigger; typedef GdkSnapshot GtkSnapshot; typedef struct _GtkStyleContext GtkStyleContext; typedef struct _GtkTooltip GtkTooltip; diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 8a02f96012..634a50dbea 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -26,12 +26,13 @@ #include "gtkwidgetprivate.h" +#include "gtkaccelmapprivate.h" #include "gtkaccelgroupprivate.h" #include "gtkaccessible.h" #include "gtkapplicationprivate.h" +#include "gtkbindings.h" #include "gtkbuildable.h" #include "gtkbuilderprivate.h" -#include "gtkconcatmodelprivate.h" #include "gtkcontainerprivate.h" #include "gtkcssboxesprivate.h" #include "gtkcssfiltervalueprivate.h" @@ -58,11 +59,6 @@ #include "gtknativeprivate.h" #include "gtkscrollable.h" #include "gtksettingsprivate.h" -#include "gtkshortcut.h" -#include "gtkshortcutcontrollerprivate.h" -#include "gtkshortcutmanager.h" -#include "gtkshortcutmanagerprivate.h" -#include "gtkshortcuttrigger.h" #include "gtksizegroup-private.h" #include "gtksnapshotprivate.h" #include "gtkstylecontextprivate.h" @@ -497,7 +493,6 @@ typedef struct { struct _GtkWidgetClassPrivate { GtkWidgetTemplate *template; - GListStore *shortcuts; GType accessible_type; AtkRole accessible_role; GQuark css_name; @@ -520,6 +515,7 @@ enum { MNEMONIC_ACTIVATE, MOVE_FOCUS, KEYNAV_FAILED, + POPUP_MENU, ACCEL_CLOSURES_CHANGED, CAN_ACTIVATE_ACCEL, QUERY_TOOLTIP, @@ -705,6 +701,8 @@ static gpointer gtk_widget_parent_class = NULL; static guint widget_signals[LAST_SIGNAL] = { 0 }; GtkTextDirection gtk_default_direction = GTK_TEXT_DIR_LTR; +static GQuark quark_accel_path = 0; +static GQuark quark_accel_closures = 0; static GQuark quark_pango_context = 0; static GQuark quark_mnemonic_labels = 0; static GQuark quark_tooltip_markup = 0; @@ -786,29 +784,9 @@ static void gtk_widget_base_class_init (gpointer g_class) { GtkWidgetClass *klass = g_class; - GtkWidgetClassPrivate *priv; - - priv = klass->priv = G_TYPE_CLASS_GET_PRIVATE (g_class, GTK_TYPE_WIDGET, GtkWidgetClassPrivate); - - priv->template = NULL; - if (priv->shortcuts == NULL) - { - priv->shortcuts = g_list_store_new (GTK_TYPE_SHORTCUT); - } - else - { - GListModel *parent_shortcuts = G_LIST_MODEL (priv->shortcuts); - guint i; - - priv->shortcuts = g_list_store_new (GTK_TYPE_SHORTCUT); - for (i = 0; i < g_list_model_get_n_items (parent_shortcuts); i++) - { - GtkShortcut *shortcut = g_list_model_get_item (parent_shortcuts, i); - g_list_store_append (priv->shortcuts, shortcut); - g_object_unref (shortcut); - } - } + klass->priv = G_TYPE_CLASS_GET_PRIVATE (g_class, GTK_TYPE_WIDGET, GtkWidgetClassPrivate); + klass->priv->template = NULL; } static void @@ -861,30 +839,12 @@ gtk_widget_real_grab_notify (GtkWidget *widget, static void gtk_widget_real_root (GtkWidget *widget) { - GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); - GList *l; - gtk_widget_forall (widget, (GtkCallback) gtk_widget_root, NULL); - - for (l = priv->event_controllers; l; l = l->next) - { - if (GTK_IS_SHORTCUT_CONTROLLER (l->data)) - gtk_shortcut_controller_root (GTK_SHORTCUT_CONTROLLER (l->data)); - } } static void gtk_widget_real_unroot (GtkWidget *widget) { - GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); - GList *l; - - for (l = priv->event_controllers; l; l = l->next) - { - if (GTK_IS_SHORTCUT_CONTROLLER (l->data)) - gtk_shortcut_controller_unroot (GTK_SHORTCUT_CONTROLLER (l->data)); - } - gtk_widget_forall (widget, (GtkCallback) gtk_widget_unroot, NULL); } @@ -892,10 +852,13 @@ static void gtk_widget_class_init (GtkWidgetClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkBindingSet *binding_set; g_type_class_adjust_private_offset (klass, &GtkWidget_private_offset); gtk_widget_parent_class = g_type_class_peek_parent (klass); + quark_accel_path = g_quark_from_static_string ("gtk-accel-path"); + quark_accel_closures = g_quark_from_static_string ("gtk-accel-closures"); quark_pango_context = g_quark_from_static_string ("gtk-pango-context"); quark_mnemonic_labels = g_quark_from_static_string ("gtk-mnemonic-labels"); quark_tooltip_markup = g_quark_from_static_string ("gtk-tooltip-markup"); @@ -1675,6 +1638,32 @@ gtk_widget_class_init (GtkWidgetClass *klass) _gtk_marshal_BOOLEAN__INT_INT_BOOLEAN_OBJECTv); /** + * GtkWidget::popup-menu: + * @widget: the object which received the signal + * + * This signal gets emitted whenever a widget should pop up a context + * menu. This usually happens through the standard key binding mechanism; + * by pressing a certain key while a widget is focused, the user can cause + * the widget to pop up a menu. For example, the #GtkEntry widget creates + * a menu with clipboard commands. See the + * [Popup Menu Migration Checklist][checklist-popup-menu] + * for an example of how to use this signal. + * + * Returns: %TRUE if a menu was activated + */ + widget_signals[POPUP_MENU] = + g_signal_new (I_("popup-menu"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkWidgetClass, popup_menu), + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + g_signal_set_va_marshaller (widget_signals[POPUP_MENU], + G_TYPE_FROM_CLASS (klass), + _gtk_marshal_BOOLEAN__VOIDv); + + /** * GtkWidget::accel-closures-changed: * @widget: the object which received the signal. * @@ -1715,6 +1704,12 @@ gtk_widget_class_init (GtkWidgetClass *klass) G_TYPE_FROM_CLASS (klass), _gtk_marshal_BOOLEAN__UINTv); + binding_set = gtk_binding_set_by_class (klass); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_F10, GDK_SHIFT_MASK, + "popup-menu", 0); + gtk_binding_entry_add_signal (binding_set, GDK_KEY_Menu, 0, + "popup-menu", 0); + gtk_widget_class_set_accessible_type (klass, GTK_TYPE_WIDGET_ACCESSIBLE); gtk_widget_class_set_css_name (klass, I_("widget")); } @@ -1722,9 +1717,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) static void gtk_widget_base_class_finalize (GtkWidgetClass *klass) { - template_data_free (klass->priv->template); - g_object_unref (klass->priv->shortcuts); } static void @@ -2355,7 +2348,6 @@ gtk_widget_init (GTypeInstance *instance, gpointer g_class) GtkWidget *widget = GTK_WIDGET (instance); GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); GType layout_manager_type; - GtkEventController *controller; widget->priv = priv; @@ -2424,19 +2416,9 @@ gtk_widget_init (GTypeInstance *instance, gpointer g_class) if (g_type_is_a (G_TYPE_FROM_CLASS (g_class), GTK_TYPE_ROOT)) priv->root = (GtkRoot *) widget; - if (g_type_is_a (G_TYPE_FROM_CLASS (g_class), GTK_TYPE_SHORTCUT_MANAGER)) - gtk_shortcut_manager_create_controllers (widget); - layout_manager_type = gtk_widget_class_get_layout_manager_type (g_class); if (layout_manager_type != G_TYPE_INVALID) gtk_widget_set_layout_manager (widget, g_object_new (layout_manager_type, NULL)); - - if (g_list_model_get_n_items (G_LIST_MODEL (GTK_WIDGET_CLASS (g_class)->priv->shortcuts)) > 0) - { - controller = gtk_shortcut_controller_new_for_model (G_LIST_MODEL (GTK_WIDGET_CLASS (g_class)->priv->shortcuts)); - gtk_event_controller_set_name (controller, "gtk-widget-class-shortcuts"); - gtk_widget_add_controller (widget, controller); - } } /** @@ -4329,220 +4311,350 @@ gtk_widget_real_size_allocate (GtkWidget *widget, { } +static gboolean +gtk_widget_real_can_activate_accel (GtkWidget *widget, + guint signal_id) +{ + GdkSurface *surface; + + /* widgets must be onscreen for accels to take effect */ + if (!gtk_widget_is_sensitive (widget) || + !_gtk_widget_get_mapped (widget)) + return FALSE; + + surface = gtk_widget_get_surface (widget); + + return gdk_surface_is_viewable (surface); +} + /** - * gtk_widget_class_add_binding: (skip) - * @widget_class: the class to add the binding to - * @keyval: key value of binding to install - * @mods: key modifier of binding to install - * @callback: the callback to call upon activation - * @format_string: GVariant format string for arguments or %NULL for - * no arguments - * @...: arguments, as given by format string. + * gtk_widget_can_activate_accel: + * @widget: a #GtkWidget + * @signal_id: the ID of a signal installed on @widget * - * Creates a new shortcut for @widget_class that calls the given @callback - * with arguments read according to @format_string. - * The arguments and format string must be provided in the same way as - * with g_variant_new(). + * Determines whether an accelerator that activates the signal + * identified by @signal_id can currently be activated. + * This is done by emitting the #GtkWidget::can-activate-accel + * signal on @widget; if the signal isn’t overridden by a + * handler or in a derived widget, then the default check is + * that the widget must be sensitive, and the widget and all + * its ancestors mapped. * - * This function is a convenience wrapper around - * gtk_widget_class_add_shortcut() and must be called during class - * initialization. It does not provide for user_data, if you need that, - * you will have to use gtk_widget_class_add_shortcut() with a custom - * shortcut. + * Returns: %TRUE if the accelerator can be activated. **/ -void -gtk_widget_class_add_binding (GtkWidgetClass *widget_class, - guint keyval, - GdkModifierType mods, - GtkShortcutFunc func, - const gchar *format_string, - ...) +gboolean +gtk_widget_can_activate_accel (GtkWidget *widget, + guint signal_id) { - GtkShortcut *shortcut; + gboolean can_activate = FALSE; + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + g_signal_emit (widget, widget_signals[CAN_ACTIVATE_ACCEL], 0, signal_id, &can_activate); + return can_activate; +} - g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class)); +typedef struct { + GClosure closure; + guint signal_id; +} AccelClosure; + +static void +closure_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; + gboolean can_activate = gtk_widget_can_activate_accel (closure->data, aclosure->signal_id); + + if (can_activate) + g_signal_emit (closure->data, aclosure->signal_id, 0); - shortcut = gtk_shortcut_new (gtk_keyval_trigger_new (keyval, mods), - gtk_callback_action_new (func, NULL, NULL)); - if (format_string) + /* whether accelerator was handled */ + g_value_set_boolean (return_value, can_activate); +} + +static void +closures_destroy (gpointer data) +{ + GSList *slist, *closures = data; + + for (slist = closures; slist; slist = slist->next) { - va_list args; - va_start (args, format_string); - gtk_shortcut_set_arguments (shortcut, - g_variant_new_va (format_string, NULL, &args)); - va_end (args); + g_closure_invalidate (slist->data); + g_closure_unref (slist->data); } + g_slist_free (closures); +} - gtk_widget_class_add_shortcut (widget_class, shortcut); +static GClosure* +widget_new_accel_closure (GtkWidget *widget, + guint signal_id) +{ + AccelClosure *aclosure; + GClosure *closure = NULL; + GSList *slist, *closures; - g_object_unref (shortcut); + closures = g_object_steal_qdata (G_OBJECT (widget), quark_accel_closures); + for (slist = closures; slist; slist = slist->next) + if (!gtk_accel_group_from_accel_closure (slist->data)) + { + /* reuse this closure */ + closure = slist->data; + break; + } + if (!closure) + { + closure = g_closure_new_object (sizeof (AccelClosure), G_OBJECT (widget)); + closures = g_slist_prepend (closures, g_closure_ref (closure)); + g_closure_sink (closure); + g_closure_set_marshal (closure, closure_accel_activate); + } + g_object_set_qdata_full (G_OBJECT (widget), quark_accel_closures, closures, closures_destroy); + + aclosure = (AccelClosure*) closure; + g_assert (closure->data == widget); + g_assert (closure->marshal == closure_accel_activate); + aclosure->signal_id = signal_id; + + return closure; } /** - * gtk_widget_class_add_binding_signal: (skip) - * @widget_class: the class to add the binding to - * @keyval: key value of binding to install - * @mods: key modifier of binding to install - * @signal: the signal to execute - * @format_string: GVariant format string for arguments or %NULL for - * no arguments - * @...: arguments, as given by format string. - * - * Creates a new shortcut for @widget_class that emits the given action - * @signal with arguments read according to @format_string. - * The arguments and format string must be provided in the same way as - * with g_variant_new(). + * gtk_widget_add_accelerator: + * @widget: widget to install an accelerator on + * @accel_signal: widget signal to emit on accelerator activation + * @accel_group: accel group for this widget, added to its toplevel + * @accel_key: GDK keyval of the accelerator + * @accel_mods: modifier key combination of the accelerator + * @accel_flags: flag accelerators, e.g. %GTK_ACCEL_VISIBLE * - * This function is a convenience wrapper around - * gtk_widget_class_add_shortcut() and must be called during class - * initialization. + * Installs an accelerator for this @widget in @accel_group that causes + * @accel_signal to be emitted if the accelerator is activated. + * The @accel_group needs to be added to the widget’s toplevel via + * gtk_window_add_accel_group(), and the signal must be of type %G_SIGNAL_ACTION. + * Accelerators added through this function are not user changeable during + * runtime. If you want to support accelerators that can be changed by the + * user, use gtk_accel_map_add_entry() and gtk_widget_set_accel_path() or + * gtk_menu_item_set_accel_path() instead. */ void -gtk_widget_class_add_binding_signal (GtkWidgetClass *widget_class, - guint keyval, - GdkModifierType mods, - const gchar *signal, - const gchar *format_string, - ...) +gtk_widget_add_accelerator (GtkWidget *widget, + const gchar *accel_signal, + GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods, + GtkAccelFlags accel_flags) { - GtkShortcut *shortcut; + GClosure *closure; + GSignalQuery query; - g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class)); - g_return_if_fail (g_signal_lookup (signal, G_TYPE_FROM_CLASS (widget_class))); - /* XXX: validate variant format for signal */ - - shortcut = gtk_shortcut_new (gtk_keyval_trigger_new (keyval, mods), - gtk_signal_action_new (signal)); - if (format_string) - { - va_list args; - va_start (args, format_string); - gtk_shortcut_set_arguments (shortcut, - g_variant_new_va (format_string, NULL, &args)); - va_end (args); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (accel_signal != NULL); + g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); + + g_signal_query (g_signal_lookup (accel_signal, G_OBJECT_TYPE (widget)), &query); + if (!query.signal_id || + !(query.signal_flags & G_SIGNAL_ACTION) || + query.return_type != G_TYPE_NONE || + query.n_params) + { + /* hmm, should be elaborate enough */ + g_warning (G_STRLOC ": widget '%s' has no activatable signal \"%s\" without arguments", + G_OBJECT_TYPE_NAME (widget), accel_signal); + return; } - gtk_widget_class_add_shortcut (widget_class, shortcut); + closure = widget_new_accel_closure (widget, query.signal_id); + + g_object_ref (widget); + + /* install the accelerator. since we don't map this onto an accel_path, + * the accelerator will automatically be locked. + */ + gtk_accel_group_connect (accel_group, + accel_key, + accel_mods, + accel_flags | GTK_ACCEL_LOCKED, + closure); + + g_signal_emit (widget, widget_signals[ACCEL_CLOSURES_CHANGED], 0); - g_object_unref (shortcut); + g_object_unref (widget); } /** - * gtk_widget_class_add_binding_action: (skip) - * @widget_class: the class to add the binding to - * @keyval: key value of binding to install - * @mods: key modifier of binding to install - * @action_name: the action to activate - * @format_string: GVariant format string for arguments or %NULL for - * no arguments - * @...: arguments, as given by format string. + * gtk_widget_remove_accelerator: + * @widget: widget to install an accelerator on + * @accel_group: accel group for this widget + * @accel_key: GDK keyval of the accelerator + * @accel_mods: modifier key combination of the accelerator * - * Creates a new shortcut for @widget_class that activates the given - * @action_name with arguments read according to @format_string. - * The arguments and format string must be provided in the same way as - * with g_variant_new(). + * Removes an accelerator from @widget, previously installed with + * gtk_widget_add_accelerator(). * - * This function is a convenience wrapper around - * gtk_widget_class_add_shortcut() and must be called during class - * initialization. + * Returns: whether an accelerator was installed and could be removed */ -void -gtk_widget_class_add_binding_action (GtkWidgetClass *widget_class, - guint keyval, - GdkModifierType mods, - const gchar *action_name, - const gchar *format_string, - ...) +gboolean +gtk_widget_remove_accelerator (GtkWidget *widget, + GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods) { - GtkShortcut *shortcut; + GtkAccelGroupEntry *ag_entry; + GList *slist, *clist; + guint n; - g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class)); - /* XXX: validate variant format for action */ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE); - shortcut = gtk_shortcut_new (gtk_keyval_trigger_new (keyval, mods), - gtk_action_action_new (action_name)); - if (format_string) + ag_entry = gtk_accel_group_query (accel_group, accel_key, accel_mods, &n); + clist = gtk_widget_list_accel_closures (widget); + for (slist = clist; slist; slist = slist->next) { - va_list args; - va_start (args, format_string); - gtk_shortcut_set_arguments (shortcut, - g_variant_new_va (format_string, NULL, &args)); - va_end (args); + guint i; + + for (i = 0; i < n; i++) + if (slist->data == (gpointer) ag_entry[i].closure) + { + gboolean is_removed = gtk_accel_group_disconnect (accel_group, slist->data); + + g_signal_emit (widget, widget_signals[ACCEL_CLOSURES_CHANGED], 0); + + g_list_free (clist); + + return is_removed; + } } + g_list_free (clist); - gtk_widget_class_add_shortcut (widget_class, shortcut); + g_warning (G_STRLOC ": no accelerator (%u,%u) installed in accel group (%p) for %s (%p)", + accel_key, accel_mods, accel_group, + G_OBJECT_TYPE_NAME (widget), widget); - g_object_unref (shortcut); + return FALSE; } /** - * gtk_widget_class_add_shortcut: - * @widget_class: the class to add the shortcut to - * @shortcut: (transfer none): the #GtkShortcut to add - * - * Installs a shortcut in @widget_class. Every instance created for - * @widget_class or its subclasses will inherit this shortcut and - * trigger it. + * gtk_widget_list_accel_closures: + * @widget: widget to list accelerator closures for * - * Shortcuts added this way will be triggered in the @GTK_PHASE_BUBBLE - * phase, which means they may also trigger if child widgets have focus. + * Lists the closures used by @widget for accelerator group connections + * with gtk_accel_group_connect_by_path() or gtk_accel_group_connect(). + * The closures can be used to monitor accelerator changes on @widget, + * by connecting to the @GtkAccelGroup::accel-changed signal of the + * #GtkAccelGroup of a closure which can be found out with + * gtk_accel_group_from_accel_closure(). * - * This function must only be used in class initialization functions - * otherwise it is not guaranteed that the shortcut will be installed. - **/ -void -gtk_widget_class_add_shortcut (GtkWidgetClass *widget_class, - GtkShortcut *shortcut) + * Returns: (transfer container) (element-type GClosure): + * a newly allocated #GList of closures + */ +GList* +gtk_widget_list_accel_closures (GtkWidget *widget) { - GtkWidgetClassPrivate *priv; - - g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class)); - g_return_if_fail (GTK_IS_SHORTCUT (shortcut)); + GSList *slist; + GList *clist = NULL; - priv = widget_class->priv; + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); - g_list_store_append (priv->shortcuts, shortcut); + for (slist = g_object_get_qdata (G_OBJECT (widget), quark_accel_closures); slist; slist = slist->next) + if (gtk_accel_group_from_accel_closure (slist->data)) + clist = g_list_prepend (clist, slist->data); + return clist; } -static gboolean -gtk_widget_real_can_activate_accel (GtkWidget *widget, - guint signal_id) +typedef struct { + GQuark path_quark; + GtkAccelGroup *accel_group; + GClosure *closure; +} AccelPath; + +static void +destroy_accel_path (gpointer data) { - GdkSurface *surface; + AccelPath *apath = data; - /* widgets must be onscreen for accels to take effect */ - if (!gtk_widget_is_sensitive (widget) || - !_gtk_widget_get_mapped (widget)) - return FALSE; + gtk_accel_group_disconnect (apath->accel_group, apath->closure); - surface = gtk_widget_get_surface (widget); + /* closures_destroy takes care of unrefing the closure */ + g_object_unref (apath->accel_group); - return gdk_surface_is_viewable (surface); + g_slice_free (AccelPath, apath); } + /** - * gtk_widget_can_activate_accel: + * gtk_widget_set_accel_path: * @widget: a #GtkWidget - * @signal_id: the ID of a signal installed on @widget + * @accel_path: (allow-none): path used to look up the accelerator + * @accel_group: (allow-none): a #GtkAccelGroup. * - * Determines whether an accelerator that activates the signal - * identified by @signal_id can currently be activated. - * This is done by emitting the #GtkWidget::can-activate-accel - * signal on @widget; if the signal isn’t overridden by a - * handler or in a derived widget, then the default check is - * that the widget must be sensitive, and the widget and all - * its ancestors mapped. + * Given an accelerator group, @accel_group, and an accelerator path, + * @accel_path, sets up an accelerator in @accel_group so whenever the + * key binding that is defined for @accel_path is pressed, @widget + * will be activated. This removes any accelerators (for any + * accelerator group) installed by previous calls to + * gtk_widget_set_accel_path(). Associating accelerators with + * paths allows them to be modified by the user and the modifications + * to be saved for future use. (See gtk_accel_map_save().) * - * Returns: %TRUE if the accelerator can be activated. + * This function is a low level function that would most likely + * be used by a menu creation system. + * + * If you only want to + * set up accelerators on menu items gtk_menu_item_set_accel_path() + * provides a somewhat more convenient interface. + * + * Note that @accel_path string will be stored in a #GQuark. Therefore, if you + * pass a static string, you can save some memory by interning it first with + * g_intern_static_string(). **/ -gboolean -gtk_widget_can_activate_accel (GtkWidget *widget, - guint signal_id) +void +gtk_widget_set_accel_path (GtkWidget *widget, + const gchar *accel_path, + GtkAccelGroup *accel_group) { - gboolean can_activate = FALSE; - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - g_signal_emit (widget, widget_signals[CAN_ACTIVATE_ACCEL], 0, signal_id, &can_activate); - return can_activate; + AccelPath *apath; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (GTK_WIDGET_GET_CLASS (widget)->activate_signal != 0); + + if (accel_path) + { + g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); + g_return_if_fail (_gtk_accel_path_is_valid (accel_path)); + + gtk_accel_map_add_entry (accel_path, 0, 0); + apath = g_slice_new (AccelPath); + apath->accel_group = g_object_ref (accel_group); + apath->path_quark = g_quark_from_string (accel_path); + apath->closure = widget_new_accel_closure (widget, GTK_WIDGET_GET_CLASS (widget)->activate_signal); + } + else + apath = NULL; + + /* also removes possible old settings */ + g_object_set_qdata_full (G_OBJECT (widget), quark_accel_path, apath, destroy_accel_path); + + if (apath) + gtk_accel_group_connect_by_path (apath->accel_group, g_quark_to_string (apath->path_quark), apath->closure); + + g_signal_emit (widget, widget_signals[ACCEL_CLOSURES_CHANGED], 0); +} + +const gchar* +_gtk_widget_get_accel_path (GtkWidget *widget, + gboolean *locked) +{ + AccelPath *apath; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + apath = g_object_get_qdata (G_OBJECT (widget), quark_accel_path); + if (locked) + *locked = apath ? gtk_accel_group_get_is_locked (apath->accel_group) : TRUE; + return apath ? g_quark_to_string (apath->path_quark) : NULL; } /** @@ -4798,6 +4910,11 @@ gtk_widget_event (GtkWidget *widget, if (return_val == FALSE) return_val |= gtk_widget_run_controllers (widget, event, target, x, y, GTK_PHASE_BUBBLE); + if (return_val == FALSE && + (gdk_event_get_event_type (event) == GDK_KEY_PRESS || + gdk_event_get_event_type (event) == GDK_KEY_RELEASE)) + return_val |= gtk_bindings_activate_event (G_OBJECT (widget), event); + return return_val; } @@ -7440,6 +7557,10 @@ gtk_widget_real_destroy (GtkWidget *object) priv->accessible = NULL; } + /* wipe accelerator closures (keep order) */ + g_object_set_qdata (G_OBJECT (widget), quark_accel_path, NULL); + g_object_set_qdata (G_OBJECT (widget), quark_accel_closures, NULL); + /* Callers of add_mnemonic_label() should disconnect on ::destroy */ g_object_set_qdata (G_OBJECT (widget), quark_mnemonic_labels, NULL); @@ -8877,6 +8998,90 @@ static const GtkBuildableParser accessibility_parser = typedef struct { + GObject *object; + GtkBuilder *builder; + guint key; + guint modifiers; + gchar *signal; +} AccelGroupParserData; + +static void +accel_group_start_element (GtkBuildableParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + AccelGroupParserData *data = (AccelGroupParserData*)user_data; + + if (strcmp (element_name, "accelerator") == 0) + { + const gchar *key_str = NULL; + const gchar *signal = NULL; + const gchar *modifiers_str = NULL; + guint key = 0; + guint modifiers = 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_STRING, "key", &key_str, + G_MARKUP_COLLECT_STRING, "signal", &signal, + G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "modifiers", &modifiers_str, + G_MARKUP_COLLECT_INVALID)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + key = gdk_keyval_from_name (key_str); + if (key == 0) + { + g_set_error (error, + GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, + "Could not parse key '%s'", key_str); + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + + if (modifiers_str != NULL) + { + GFlagsValue aliases[2] = { + { 0, "primary", "primary" }, + { 0, NULL, NULL } + }; + + aliases[0].value = _gtk_get_primary_accel_mod (); + + if (!_gtk_builder_flags_from_string (GDK_TYPE_MODIFIER_TYPE, aliases, + modifiers_str, &modifiers, error)) + { + _gtk_builder_prefix_error (data->builder, context, error); + return; + } + } + + data->key = key; + data->modifiers = modifiers; + data->signal = g_strdup (signal); + } + else + { + _gtk_builder_error_unhandled_tag (data->builder, context, + "GtkWidget", element_name, + error); + } +} + +static const GtkBuildableParser accel_group_parser = + { + accel_group_start_element, + }; + +typedef struct +{ GtkBuilder *builder; GSList *classes; } StyleParserData; @@ -9078,6 +9283,20 @@ gtk_widget_buildable_custom_tag_start (GtkBuildable *buildable, GtkBuildableParser *parser, gpointer *parser_data) { + if (strcmp (tagname, "accelerator") == 0) + { + AccelGroupParserData *data; + + data = g_slice_new0 (AccelGroupParserData); + data->object = (GObject *)g_object_ref (buildable); + data->builder = builder; + + *parser = accel_group_parser; + *parser_data = data; + + return TRUE; + } + if (strcmp (tagname, "accessibility") == 0) { AccessibilitySubParserData *data; @@ -9130,6 +9349,45 @@ gtk_widget_buildable_custom_tag_end (GtkBuildable *buildable, { } +void +_gtk_widget_buildable_finish_accelerator (GtkWidget *widget, + GtkWidget *toplevel, + gpointer user_data) +{ + AccelGroupParserData *accel_data; + GSList *accel_groups; + GtkAccelGroup *accel_group; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (GTK_IS_WIDGET (toplevel)); + g_return_if_fail (user_data != NULL); + + accel_data = (AccelGroupParserData*)user_data; + accel_groups = gtk_accel_groups_from_object (G_OBJECT (toplevel)); + if (g_slist_length (accel_groups) == 0) + { + accel_group = gtk_accel_group_new (); + if (GTK_IS_WINDOW (toplevel)) + gtk_window_add_accel_group (GTK_WINDOW (toplevel), accel_group); + } + else + { + g_assert (g_slist_length (accel_groups) == 1); + accel_group = g_slist_nth_data (accel_groups, 0); + } + + gtk_widget_add_accelerator (GTK_WIDGET (accel_data->object), + accel_data->signal, + accel_group, + accel_data->key, + accel_data->modifiers, + GTK_ACCEL_VISIBLE); + + g_object_unref (accel_data->object); + g_free (accel_data->signal); + g_slice_free (AccelGroupParserData, accel_data); +} + static void gtk_widget_buildable_finish_layout_properties (GtkWidget *widget, GtkWidget *parent, @@ -9203,7 +9461,19 @@ gtk_widget_buildable_custom_finished (GtkBuildable *buildable, const gchar *tagname, gpointer user_data) { - if (strcmp (tagname, "accessibility") == 0) + if (strcmp (tagname, "accelerator") == 0) + { + AccelGroupParserData *accel_data; + GtkRoot *root; + + accel_data = (AccelGroupParserData*)user_data; + g_assert (accel_data->object); + + root = _gtk_widget_get_root (GTK_WIDGET (accel_data->object)); + + _gtk_widget_buildable_finish_accelerator (GTK_WIDGET (buildable), GTK_WIDGET (root), user_data); + } + else if (strcmp (tagname, "accessibility") == 0) { AccessibilitySubParserData *a11y_data; @@ -11583,24 +11853,6 @@ gtk_widget_reset_controllers (GtkWidget *widget) } } -GList * -gtk_widget_list_controllers (GtkWidget *widget, - GtkPropagationPhase phase) -{ - GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); - GList *res = NULL, *l; - - for (l = priv->event_controllers; l; l = l->next) - { - GtkEventController *controller = l->data; - - if (gtk_event_controller_get_propagation_phase (controller) == phase) - res = g_list_prepend (res, controller); - } - - return g_list_reverse (res); -} - static inline void gtk_widget_maybe_add_debug_render_nodes (GtkWidget *widget, GtkSnapshot *snapshot) diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index d9becfab17..9121d5bb7e 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -33,8 +33,6 @@ #include <gsk/gsk.h> #include <gtk/gtkaccelgroup.h> #include <gtk/gtkborder.h> -#include <gtk/gtkshortcut.h> -#include <gtk/gtkshortcutaction.h> #include <gtk/gtktypes.h> #include <atk/atk.h> @@ -376,32 +374,23 @@ GDK_AVAILABLE_IN_ALL GType gtk_widget_class_get_layout_manager_type (GtkWidgetClass *widget_class); GDK_AVAILABLE_IN_ALL -void gtk_widget_class_add_binding (GtkWidgetClass *widget_class, - guint keyval, - GdkModifierType mods, - GtkShortcutFunc callback, - const gchar *format_string, - ...); +void gtk_widget_add_accelerator (GtkWidget *widget, + const gchar *accel_signal, + GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods, + GtkAccelFlags accel_flags); GDK_AVAILABLE_IN_ALL -void gtk_widget_class_add_binding_signal - (GtkWidgetClass *widget_class, - GdkModifierType mods, - guint keyval, - const gchar *signal, - const gchar *format_string, - ...); +gboolean gtk_widget_remove_accelerator (GtkWidget *widget, + GtkAccelGroup *accel_group, + guint accel_key, + GdkModifierType accel_mods); GDK_AVAILABLE_IN_ALL -void gtk_widget_class_add_binding_action - (GtkWidgetClass *widget_class, - GdkModifierType mods, - guint keyval, - const gchar *action_name, - const gchar *format_string, - ...); +void gtk_widget_set_accel_path (GtkWidget *widget, + const gchar *accel_path, + GtkAccelGroup *accel_group); GDK_AVAILABLE_IN_ALL -void gtk_widget_class_add_shortcut (GtkWidgetClass *widget_class, - GtkShortcut *shortcut); - +GList* gtk_widget_list_accel_closures (GtkWidget *widget); GDK_AVAILABLE_IN_ALL gboolean gtk_widget_can_activate_accel (GtkWidget *widget, guint signal_id); diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 82943b5eb5..1018c9c915 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -230,6 +230,9 @@ void _gtk_widget_add_attached_window (GtkWidget *widget, void _gtk_widget_remove_attached_window (GtkWidget *widget, GtkWindow *window); +const gchar* _gtk_widget_get_accel_path (GtkWidget *widget, + gboolean *locked); + AtkObject * _gtk_widget_peek_accessible (GtkWidget *widget); void _gtk_widget_set_has_default (GtkWidget *widget, @@ -255,6 +258,9 @@ void _gtk_widget_synthesize_crossing (GtkWidget *fro GdkDevice *device, GdkCrossingMode mode); +void _gtk_widget_buildable_finish_accelerator (GtkWidget *widget, + GtkWidget *toplevel, + gpointer user_data); GtkStyleContext * _gtk_widget_peek_style_context (GtkWidget *widget); gboolean _gtk_widget_captured_event (GtkWidget *widget, @@ -277,9 +283,6 @@ gboolean gtk_widget_has_size_request (GtkWidget *widget); void gtk_widget_reset_controllers (GtkWidget *widget); -GList * gtk_widget_list_controllers (GtkWidget *widget, - GtkPropagationPhase phase); - gboolean gtk_widget_query_tooltip (GtkWidget *widget, gint x, gint y, 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 diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h index 2563ac5cac..8f4c964fee 100644 --- a/gtk/gtkwindow.h +++ b/gtk/gtkwindow.h @@ -97,6 +97,12 @@ GDK_AVAILABLE_IN_ALL void gtk_window_set_startup_id (GtkWindow *window, const gchar *startup_id); GDK_AVAILABLE_IN_ALL +void gtk_window_add_accel_group (GtkWindow *window, + GtkAccelGroup *accel_group); +GDK_AVAILABLE_IN_ALL +void gtk_window_remove_accel_group (GtkWindow *window, + GtkAccelGroup *accel_group); +GDK_AVAILABLE_IN_ALL void gtk_window_set_focus (GtkWindow *window, GtkWidget *focus); GDK_AVAILABLE_IN_ALL @@ -185,6 +191,31 @@ void gtk_window_set_has_user_ref_count (GtkWindow *window, gboolean setting); GDK_AVAILABLE_IN_ALL +void gtk_window_add_mnemonic (GtkWindow *window, + guint keyval, + GtkWidget *target); +GDK_AVAILABLE_IN_ALL +void gtk_window_remove_mnemonic (GtkWindow *window, + guint keyval, + GtkWidget *target); +GDK_AVAILABLE_IN_ALL +gboolean gtk_window_mnemonic_activate (GtkWindow *window, + guint keyval, + GdkModifierType modifier); +GDK_AVAILABLE_IN_ALL +void gtk_window_set_mnemonic_modifier (GtkWindow *window, + GdkModifierType modifier); +GDK_AVAILABLE_IN_ALL +GdkModifierType gtk_window_get_mnemonic_modifier (GtkWindow *window); + +GDK_AVAILABLE_IN_ALL +gboolean gtk_window_activate_key (GtkWindow *window, + GdkEvent *event); +GDK_AVAILABLE_IN_ALL +gboolean gtk_window_propagate_key_event (GtkWindow *window, + GdkEvent *event); + +GDK_AVAILABLE_IN_ALL void gtk_window_present (GtkWindow *window); GDK_AVAILABLE_IN_ALL void gtk_window_present_with_time (GtkWindow *window, diff --git a/gtk/gtkwindowprivate.h b/gtk/gtkwindowprivate.h index 8192644b72..5fc156f8f9 100644 --- a/gtk/gtkwindowprivate.h +++ b/gtk/gtkwindowprivate.h @@ -56,6 +56,7 @@ void gtk_window_check_resize (GtkWindow *self); typedef void (*GtkWindowKeysForeachFunc) (GtkWindow *window, guint keyval, GdkModifierType modifiers, + gboolean is_mnemonic, gpointer data); gboolean gtk_window_emit_close_request (GtkWindow *window); diff --git a/gtk/inspector/init.c b/gtk/inspector/init.c index 126ce44b31..2681e62e81 100644 --- a/gtk/inspector/init.c +++ b/gtk/inspector/init.c @@ -40,7 +40,6 @@ #include "prop-list.h" #include "recorder.h" #include "resource-list.h" -#include "shortcuts.h" #include "size-groups.h" #include "statistics.h" #include "visual.h" @@ -75,7 +74,6 @@ gtk_inspector_init (void) g_type_ensure (GTK_TYPE_INSPECTOR_PROP_LIST); g_type_ensure (GTK_TYPE_INSPECTOR_RECORDER); g_type_ensure (GTK_TYPE_INSPECTOR_RESOURCE_LIST); - g_type_ensure (GTK_TYPE_INSPECTOR_SHORTCUTS); g_type_ensure (GTK_TYPE_INSPECTOR_SIZE_GROUPS); g_type_ensure (GTK_TYPE_INSPECTOR_STATISTICS); g_type_ensure (GTK_TYPE_INSPECTOR_VISUAL); diff --git a/gtk/inspector/meson.build b/gtk/inspector/meson.build index 4f32b75639..c945386d2d 100644 --- a/gtk/inspector/meson.build +++ b/gtk/inspector/meson.build @@ -28,7 +28,6 @@ inspector_sources = files( 'recording.c', 'renderrecording.c', 'resource-list.c', - 'shortcuts.c', 'size-groups.c', 'startrecording.c', 'statistics.c', diff --git a/gtk/inspector/shortcuts.c b/gtk/inspector/shortcuts.c deleted file mode 100644 index 3f708371cd..0000000000 --- a/gtk/inspector/shortcuts.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2020 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" -#include <glib/gi18n-lib.h> - -#include "shortcuts.h" -#include "gtklabel.h" -#include "gtklistbox.h" -#include "gtksizegroup.h" -#include "gtkstack.h" -#include "gtkshortcut.h" -#include "gtkshortcuttrigger.h" -#include "gtkshortcutcontroller.h" - -struct _GtkInspectorShortcuts -{ - GtkWidget parent; - - GtkWidget *box; - GtkWidget *list; - - GtkSizeGroup *trigger; - GtkSizeGroup *action; -}; - -G_DEFINE_TYPE (GtkInspectorShortcuts, gtk_inspector_shortcuts, GTK_TYPE_WIDGET) - -static void -gtk_inspector_shortcuts_init (GtkInspectorShortcuts *sl) -{ - gtk_widget_init_template (GTK_WIDGET (sl)); -} - -static GtkWidget * -create_row (gpointer item, - gpointer user_data) -{ - GtkShortcut *shortcut = GTK_SHORTCUT (item); - GtkInspectorShortcuts *sl = GTK_INSPECTOR_SHORTCUTS (user_data); - GtkShortcutTrigger *trigger; - GtkShortcutAction *action; - char *s; - GtkWidget *row; - GtkWidget *label; - - trigger = gtk_shortcut_get_trigger (shortcut); - action = gtk_shortcut_get_action (shortcut); - - row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); - - s = gtk_shortcut_trigger_to_string (trigger); - label = gtk_label_new (s); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - g_free (s); - gtk_container_add (GTK_CONTAINER (row), label); - gtk_size_group_add_widget (sl->trigger, label); - - s = gtk_shortcut_action_to_string (action); - label = gtk_label_new (s); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - g_free (s); - gtk_container_add (GTK_CONTAINER (row), label); - gtk_size_group_add_widget (sl->action, label); - - return row; -} - -void -gtk_inspector_shortcuts_set_object (GtkInspectorShortcuts *sl, - GObject *object) -{ - GtkWidget *stack; - GtkStackPage *page; - - stack = gtk_widget_get_parent (GTK_WIDGET (sl)); - page = gtk_stack_get_page (GTK_STACK (stack), GTK_WIDGET (sl)); - - if (GTK_IS_SHORTCUT_CONTROLLER (object)) - { - g_object_set (page, "visible", TRUE, NULL); - gtk_list_box_bind_model (GTK_LIST_BOX (sl->list), - G_LIST_MODEL (object), - create_row, - sl, - NULL); - } - else - { - g_object_set (page, "visible", FALSE, NULL); - gtk_list_box_bind_model (GTK_LIST_BOX (sl->list), - NULL, - NULL, - NULL, - NULL); - } -} - -static void -gtk_inspector_shortcuts_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline) -{ - GtkInspectorShortcuts *shortcuts = GTK_INSPECTOR_SHORTCUTS (widget); - - gtk_widget_measure (shortcuts->box, - orientation, - for_size, - minimum, natural, - minimum_baseline, natural_baseline); -} - -static void -gtk_inspector_shortcuts_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - GtkInspectorShortcuts *shortcuts = GTK_INSPECTOR_SHORTCUTS (widget); - - gtk_widget_size_allocate (shortcuts->box, - &(GtkAllocation) { 0, 0, width, height }, - baseline); -} - - -static void -gtk_inspector_shortcuts_class_init (GtkInspectorShortcutsClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - widget_class->measure = gtk_inspector_shortcuts_measure; - widget_class->size_allocate = gtk_inspector_shortcuts_size_allocate; - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/shortcuts.ui"); - gtk_widget_class_bind_template_child (widget_class, GtkInspectorShortcuts, box); - gtk_widget_class_bind_template_child (widget_class, GtkInspectorShortcuts, list); - gtk_widget_class_bind_template_child (widget_class, GtkInspectorShortcuts, trigger); - gtk_widget_class_bind_template_child (widget_class, GtkInspectorShortcuts, action); -} diff --git a/gtk/inspector/shortcuts.ui b/gtk/inspector/shortcuts.ui deleted file mode 100644 index 7ac55eb17a..0000000000 --- a/gtk/inspector/shortcuts.ui +++ /dev/null @@ -1,59 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface domain="gtk40"> - <template class="GtkInspectorShortcuts" parent="GtkWidget"> - <style> - <class name="view"/> - </style> - <child> - <object class="GtkBox" id="box"> - <property name="orientation">vertical</property> - <child> - <object class="GtkBox"> - <style> - <class name="header"/> - </style> - <child> - <object class="GtkLabel" id="trigger_heading"> - <property name="label" translatable="yes">Trigger</property> - <property name="xalign">0</property> - </object> - </child> - <child> - <object class="GtkLabel" id="action_heading"> - <property name="label" translatable="yes">Action</property> - <property name="xalign">0</property> - </object> - </child> - </object> - </child> - <child> - <object class="GtkScrolledWindow"> - <property name="hexpand">1</property> - <property name="vexpand">1</property> - <property name="hscrollbar-policy">never</property> - <child> - <object class="GtkListBox" id="list"> - <style> - <class name="list"/> - </style> - <property name="selection-mode">none</property> - </object> - </child> - </object> - </child> - </object> - </child> - </template> - <object class="GtkSizeGroup" id="trigger"> - <property name="mode">horizontal</property> - <widgets> - <widget name="trigger_heading"/> - </widgets> - </object> - <object class="GtkSizeGroup" id="action"> - <property name="mode">horizontal</property> - <widgets> - <widget name="action_heading"/> - </widgets> - </object> -</interface> diff --git a/gtk/inspector/window.c b/gtk/inspector/window.c index 2b86655b43..6b4b2524d5 100644 --- a/gtk/inspector/window.c +++ b/gtk/inspector/window.c @@ -37,7 +37,6 @@ #include "size-groups.h" #include "data-list.h" #include "actions.h" -#include "shortcuts.h" #include "menu.h" #include "misc-info.h" #include "magnifier.h" @@ -92,7 +91,6 @@ set_selected_object (GtkInspectorWindow *iw, gtk_inspector_size_groups_set_object (GTK_INSPECTOR_SIZE_GROUPS (iw->size_groups), selected); gtk_inspector_data_list_set_object (GTK_INSPECTOR_DATA_LIST (iw->data_list), selected); gtk_inspector_actions_set_object (GTK_INSPECTOR_ACTIONS (iw->actions), selected); - gtk_inspector_shortcuts_set_object (GTK_INSPECTOR_SHORTCUTS (iw->shortcuts), selected); gtk_inspector_menu_set_object (GTK_INSPECTOR_MENU (iw->menu), selected); gtk_inspector_controllers_set_object (GTK_INSPECTOR_CONTROLLERS (iw->controllers), selected); gtk_inspector_magnifier_set_object (GTK_INSPECTOR_MAGNIFIER (iw->magnifier), selected); @@ -423,7 +421,6 @@ gtk_inspector_window_class_init (GtkInspectorWindowClass *klass) gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, size_groups); gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, data_list); gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, actions); - gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, shortcuts); gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, menu); gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, misc_info); gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, controllers); diff --git a/gtk/inspector/window.h b/gtk/inspector/window.h index 6ac9a7eafc..3f29706141 100644 --- a/gtk/inspector/window.h +++ b/gtk/inspector/window.h @@ -66,7 +66,6 @@ typedef struct GtkWidget *size_groups; GtkWidget *data_list; GtkWidget *actions; - GtkWidget *shortcuts; GtkWidget *menu; GtkWidget *misc_info; GtkWidget *controllers; diff --git a/gtk/inspector/window.ui b/gtk/inspector/window.ui index 4623a72229..963464be7a 100644 --- a/gtk/inspector/window.ui +++ b/gtk/inspector/window.ui @@ -449,16 +449,6 @@ </property> </object> </child> - <child> - <object class="GtkStackPage"> - <property name="name">shortcuts</property> - <property name="title" translatable="yes">Shortcuts</property> - <property name="child"> - <object class="GtkInspectorShortcuts" id="shortcuts"> - </object> - </property> - </object> - </child> </object> </child> </object> diff --git a/gtk/meson.build b/gtk/meson.build index 095b71158d..bf7e08a06c 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -119,6 +119,7 @@ gtk_private_sources = files([ 'gtkmenusectionbox.c', 'gtkmenutracker.c', 'gtkmenutrackeritem.c', + 'gtkmnemonichash.c', 'gtkpango.c', 'gskpango.c', 'gtkpathbar.c', @@ -150,6 +151,7 @@ gtk_public_sources = files([ 'gtkaboutdialog.c', 'gtkaccelgroup.c', 'gtkaccellabel.c', + 'gtkaccelmap.c', 'gtkaccessible.c', 'gtkactionable.c', 'gtkactionbar.c', @@ -164,6 +166,7 @@ gtk_public_sources = files([ 'gtkassistant.c', 'gtkbin.c', 'gtkbinlayout.c', + 'gtkbindings.c', 'gtkborder.c', 'gtkboxlayout.c', 'gtkbox.c', @@ -199,7 +202,6 @@ gtk_public_sources = files([ 'gtkcombobox.c', 'gtkcomboboxtext.c', 'gtkcomposetable.c', - 'gtkconcatmodel.c', 'gtkconstraintguide.c', 'gtkconstraintlayout.c', 'gtkconstraint.c', @@ -329,16 +331,11 @@ gtk_public_sources = files([ 'gtkselectionmodel.c', 'gtkseparator.c', 'gtksettings.c', - 'gtkshortcut.c', - 'gtkshortcutaction.c', - 'gtkshortcutcontroller.c', 'gtkshortcutlabel.c', - 'gtkshortcutmanager.c', 'gtkshortcutsgroup.c', 'gtkshortcutssection.c', 'gtkshortcutsshortcut.c', 'gtkshortcutswindow.c', - 'gtkshortcuttrigger.c', 'gtkshow.c', 'gtksidebarrow.c', 'gtksingleselection.c', @@ -405,6 +402,7 @@ gtk_public_headers = files([ 'gtkaboutdialog.h', 'gtkaccelgroup.h', 'gtkaccellabel.h', + 'gtkaccelmap.h', 'gtkaccessible.h', 'gtkactionable.h', 'gtkactionbar.h', @@ -419,6 +417,7 @@ gtk_public_headers = files([ 'gtkassistant.h', 'gtkbin.h', 'gtkbinlayout.h', + 'gtkbindings.h', 'gtkborder.h', 'gtkbox.h', 'gtkboxlayout.h', @@ -569,16 +568,11 @@ gtk_public_headers = files([ 'gtkselectionmodel.h', 'gtkseparator.h', 'gtksettings.h', - 'gtkshortcut.h', - 'gtkshortcutaction.h', - 'gtkshortcutcontroller.h', 'gtkshortcutlabel.h', - 'gtkshortcutmanager.h', 'gtkshortcutsgroup.h', 'gtkshortcutssection.h', 'gtkshortcutsshortcut.h', 'gtkshortcutswindow.h', - 'gtkshortcuttrigger.h', 'gtkshow.h', 'gtksingleselection.h', 'gtksizegroup.h', diff --git a/gtk/ui/gtkcoloreditor.ui b/gtk/ui/gtkcoloreditor.ui index d03b287278..4075102a5e 100644 --- a/gtk/ui/gtkcoloreditor.ui +++ b/gtk/ui/gtkcoloreditor.ui @@ -83,12 +83,12 @@ </child> <child> <object class="GtkColorScale" id="h_slider"> - <property name="name">h</property> <property name="can-focus">True</property> <property name="orientation">vertical</property> <property name="adjustment">h_adj</property> <property name="draw-value">False</property> <property name="has-origin">False</property> + <signal name="popup-menu" handler="popup_edit" swapped="no"/> <layout> <property name="left-attach">0</property> <property name="top-attach">1</property> @@ -97,7 +97,6 @@ </child> <child> <object class="GtkColorScale" id="a_slider"> - <property name="name">a</property> <property name="can-focus">True</property> <property name="adjustment">a_adj</property> <property name="draw-value">False</property> @@ -106,6 +105,7 @@ <style> <class name="marks-before"/> </style> + <signal name="popup-menu" handler="popup_edit" swapped="no"/> <layout> <property name="left-attach">1</property> <property name="top-attach">2</property> @@ -115,13 +115,13 @@ </child> <child> <object class="GtkColorPlane" id="sv_plane"> - <property name="name">sv</property> <property name="width-request">300</property> <property name="height-request">300</property> <property name="can-focus">True</property> <property name="h-adjustment">h_adj</property> <property name="s-adjustment">s_adj</property> <property name="v-adjustment">v_adj</property> + <signal name="popup-menu" handler="popup_edit" swapped="no"/> <layout> <property name="left-attach">1</property> <property name="top-attach">1</property> diff --git a/gtk/ui/gtkfilechooserwidget.ui b/gtk/ui/gtkfilechooserwidget.ui index dca1716e23..ff7ad061fd 100644 --- a/gtk/ui/gtkfilechooserwidget.ui +++ b/gtk/ui/gtkfilechooserwidget.ui @@ -160,6 +160,12 @@ <signal name="pressed" handler="click_cb" swapped="no"/> </object> </child> + <child> + <object class="GtkEventControllerKey"> + <signal name="key-pressed" handler="treeview_key_press_cb" swapped="no"/> + </object> + </child> + <signal name="popup-menu" handler="list_popup_menu_cb" swapped="no"/> <signal name="query-tooltip" handler="file_list_query_tooltip_cb" swapped="no"/> <signal name="row-activated" handler="list_row_activated" swapped="no"/> <signal name="keynav-failed" handler="browse_files_tree_view_keynav_failed_cb"/> @@ -362,6 +368,11 @@ </child> </object> </child> + <child> + <object class="GtkEventControllerKey"> + <signal name="key-pressed" handler="widget_key_press_cb" swapped="no"/> + </object> + </child> </template> <object class="GtkSizeGroup" id="browse_path_bar_size_group"> <property name="mode">vertical</property> diff --git a/gtk/ui/gtkplacesviewrow.ui b/gtk/ui/gtkplacesviewrow.ui index 1177326b40..31b1473c8d 100644 --- a/gtk/ui/gtkplacesviewrow.ui +++ b/gtk/ui/gtkplacesviewrow.ui @@ -81,5 +81,11 @@ </child> </object> </child> + <child> + <object class="GtkGestureClick"> + <property name="button">3</property> + <signal name="pressed" handler="pressed_cb" swapped="no"/> + </object> + </child> </template> </interface> diff --git a/po-properties/POTFILES.in b/po-properties/POTFILES.in index 2c3bd92850..8929b238fa 100644 --- a/po-properties/POTFILES.in +++ b/po-properties/POTFILES.in @@ -77,6 +77,7 @@ gtk/gtkapplicationwindow.c gtk/gtkaspectframe.c gtk/gtkassistant.c gtk/gtkbin.c +gtk/gtkbindings.c gtk/gtkbookmarksmanager.c gtk/gtkbox.c gtk/gtkboxlayout.c diff --git a/po/POTFILES.in b/po/POTFILES.in index 0be7778fed..644f28115e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -77,6 +77,7 @@ gtk/gtkapplicationwindow.c gtk/gtkaspectframe.c gtk/gtkassistant.c gtk/gtkbin.c +gtk/gtkbindings.c gtk/gtkbookmarksmanager.c gtk/gtkbox.c gtk/gtkboxlayout.c diff --git a/tests/testgtk.c b/tests/testgtk.c index 1f6b8c8439..4c591dfa49 100644 --- a/tests/testgtk.c +++ b/tests/testgtk.c @@ -1603,33 +1603,25 @@ create_listbox (GtkWidget *widget) static GtkWidget * -accel_button_new (const gchar *text, - const gchar *accel) +accel_button_new (GtkAccelGroup *accel_group, + const gchar *text, + const gchar *accel) { guint keyval; GdkModifierType modifiers; GtkWidget *button; GtkWidget *label; - GtkEventController *controller; - GtkShortcut *shortcut; - if (!gtk_accelerator_parse (accel, &keyval, &modifiers)) - { - g_assert_not_reached (); - } + gtk_accelerator_parse (accel, &keyval, &modifiers); + g_assert (keyval); button = gtk_button_new (); - controller = gtk_shortcut_controller_new (); - gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (controller), GTK_SHORTCUT_SCOPE_GLOBAL); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); - shortcut = gtk_shortcut_new (gtk_keyval_trigger_new (keyval, modifiers), - gtk_activate_action_new ()); - gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); - g_object_unref (shortcut); - gtk_widget_add_controller (button, controller); + gtk_widget_add_accelerator (button, "activate", accel_group, + keyval, modifiers, GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED); label = gtk_accel_label_new (text); - gtk_accel_label_set_accel (GTK_ACCEL_LABEL (label), keyval, modifiers); + gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), button); + gtk_widget_show (label); gtk_container_add (GTK_CONTAINER (button), label); @@ -1644,6 +1636,7 @@ create_key_lookup (GtkWidget *widget) if (!window) { + GtkAccelGroup *accel_group = gtk_accel_group_new (); GtkWidget *button; GtkWidget *content_area; @@ -1658,6 +1651,8 @@ create_key_lookup (GtkWidget *widget) */ gtk_window_set_default_size (GTK_WINDOW (window), 300, -1); + gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); + content_area = gtk_dialog_get_content_area (GTK_DIALOG (window)); button = gtk_button_new_with_mnemonic ("Button 1 (_a)"); @@ -1670,23 +1665,23 @@ create_key_lookup (GtkWidget *widget) gtk_container_add (GTK_CONTAINER (content_area), button); button = gtk_button_new_with_mnemonic ("Button 6 (_b)"); gtk_container_add (GTK_CONTAINER (content_area), button); - button = accel_button_new ("Button 7", "<Alt><Shift>b"); + button = accel_button_new (accel_group, "Button 7", "<Alt><Shift>b"); gtk_container_add (GTK_CONTAINER (content_area), button); - button = accel_button_new ("Button 8", "<Alt>d"); + button = accel_button_new (accel_group, "Button 8", "<Alt>d"); gtk_container_add (GTK_CONTAINER (content_area), button); - button = accel_button_new ("Button 9", "<Alt>Cyrillic_ve"); + button = accel_button_new (accel_group, "Button 9", "<Alt>Cyrillic_ve"); gtk_container_add (GTK_CONTAINER (content_area), button); button = gtk_button_new_with_mnemonic ("Button 10 (_1)"); gtk_container_add (GTK_CONTAINER (content_area), button); button = gtk_button_new_with_mnemonic ("Button 11 (_!)"); gtk_container_add (GTK_CONTAINER (content_area), button); - button = accel_button_new ("Button 12", "<Super>a"); + button = accel_button_new (accel_group, "Button 12", "<Super>a"); gtk_container_add (GTK_CONTAINER (content_area), button); - button = accel_button_new ("Button 13", "<Hyper>a"); + button = accel_button_new (accel_group, "Button 13", "<Hyper>a"); gtk_container_add (GTK_CONTAINER (content_area), button); - button = accel_button_new ("Button 14", "<Meta>a"); + button = accel_button_new (accel_group, "Button 14", "<Meta>a"); gtk_container_add (GTK_CONTAINER (content_area), button); - button = accel_button_new ("Button 15", "<Shift><Mod4>b"); + button = accel_button_new (accel_group, "Button 15", "<Shift><Mod4>b"); gtk_container_add (GTK_CONTAINER (content_area), button); window_ptr = &window; @@ -6237,6 +6232,7 @@ main (int argc, char *argv[]) { GtkCssProvider *provider, *memory_provider; GdkDisplay *display; + GtkBindingSet *binding_set; int i; gboolean done_benchmarks = FALSE; @@ -6320,6 +6316,15 @@ main (int argc, char *argv[]) if (done_benchmarks) return 0; + /* bindings test + */ + binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_WIDGET)); + gtk_binding_entry_add_signal (binding_set, + '9', GDK_CONTROL_MASK | GDK_RELEASE_MASK, + "debug_msg", + 1, + G_TYPE_STRING, "GtkWidgetClass <ctrl><release>9 test"); + memory_provider = gtk_css_provider_new (); gtk_css_provider_load_from_data (memory_provider, "#testgtk-version-label {\n" diff --git a/tests/testmenubutton.c b/tests/testmenubutton.c index cb2b647b0f..f165d88aa7 100644 --- a/tests/testmenubutton.c +++ b/tests/testmenubutton.c @@ -40,6 +40,7 @@ int main (int argc, char **argv) GtkWidget *label; GtkWidget *check; GtkWidget *combo; + GtkAccelGroup *accel_group; guint i; guint row = 0; GMenu *menu; @@ -54,6 +55,9 @@ int main (int argc, char **argv) gtk_grid_set_column_spacing (GTK_GRID (grid), 12); gtk_container_add (GTK_CONTAINER (window), grid); + accel_group = gtk_accel_group_new (); + gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); + /* horizontal alignment */ label = gtk_label_new ("Horizontal Alignment:"); gtk_widget_show (label); diff --git a/testsuite/css/parser/at-invalid-22.css b/testsuite/css/parser/at-invalid-22.css new file mode 100644 index 0000000000..db9626914a --- /dev/null +++ b/testsuite/css/parser/at-invalid-22.css @@ -0,0 +1 @@ +@define-color color darker ( @blue ) ; diff --git a/testsuite/css/parser/at-invalid-22.errors b/testsuite/css/parser/at-invalid-22.errors new file mode 100644 index 0000000000..168e43daff --- /dev/null +++ b/testsuite/css/parser/at-invalid-22.errors @@ -0,0 +1 @@ +at-invalid-22.css:1:21-27: error: GTK_CSS_PARSER_ERROR_SYNTAX diff --git a/testsuite/css/parser/at-invalid-22.ref.css b/testsuite/css/parser/at-invalid-22.ref.css new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/testsuite/css/parser/at-invalid-22.ref.css diff --git a/testsuite/css/parser/meson.build b/testsuite/css/parser/meson.build index e128e1971b..ea4b815a59 100644 --- a/testsuite/css/parser/meson.build +++ b/testsuite/css/parser/meson.build @@ -92,6 +92,9 @@ test_data = [ 'at-invalid-21.css', 'at-invalid-21.errors', 'at-invalid-21.ref.css', + 'at-invalid-22.css', + 'at-invalid-22.errors', + 'at-invalid-22.ref.css', 'at-valid-01.css', 'at-valid-01.ref.css', 'at-valid-02.css', diff --git a/testsuite/gtk/accel.c b/testsuite/gtk/accel.c index c49a10e72f..da031da7be 100644 --- a/testsuite/gtk/accel.c +++ b/testsuite/gtk/accel.c @@ -28,20 +28,19 @@ test_one_accel (const char *accel, char *label, *name; accel_key = 0; - g_assert (gtk_accelerator_parse_with_keycode (accel, - gdk_display_get_default (), - &accel_key, - &keycodes, - &mods)); + gtk_accelerator_parse_with_keycode (accel, + &accel_key, + &keycodes, + &mods); if (has_keysym) { guint accel_key_2; GdkModifierType mods_2; - g_assert (gtk_accelerator_parse (accel, - &accel_key_2, - &mods_2)); + gtk_accelerator_parse (accel, + &accel_key_2, + &mods_2); g_assert (accel_key == accel_key_2); g_assert (mods == mods_2); } diff --git a/testsuite/gtk/action.c b/testsuite/gtk/action.c index f15dce7ac1..23a78fa23e 100644 --- a/testsuite/gtk/action.c +++ b/testsuite/gtk/action.c @@ -360,7 +360,6 @@ test_introspection (void) } expected[] = { { GTK_TYPE_TEXT, "text.undo", NULL, NULL }, { GTK_TYPE_TEXT, "text.redo", NULL, NULL }, - { GTK_TYPE_TEXT, "menu.popup", NULL, NULL }, { GTK_TYPE_TEXT, "clipboard.cut", NULL, NULL }, { GTK_TYPE_TEXT, "clipboard.copy", NULL, NULL }, { GTK_TYPE_TEXT, "clipboard.paste", NULL, NULL }, @@ -379,9 +378,9 @@ test_introspection (void) &property)) { g_assert (expected[i].owner == owner); - g_assert_cmpstr (expected[i].name, ==, name); - g_assert_cmpstr (expected[i].params, ==, params ? g_variant_type_peek_string (params) : NULL); - g_assert_cmpstr (expected[i].property, ==, property); + g_assert (strcmp (expected[i].name, name) == 0); + g_assert (g_strcmp0 (expected[i].params, params ? g_variant_type_peek_string (params) : NULL) == 0); + g_assert (g_strcmp0 (expected[i].property, property) == 0); i++; } g_assert (i == G_N_ELEMENTS (expected)); diff --git a/testsuite/gtk/builder.c b/testsuite/gtk/builder.c index c7178d476d..15afb24c4d 100644 --- a/testsuite/gtk/builder.c +++ b/testsuite/gtk/builder.c @@ -1390,6 +1390,58 @@ test_message_dialog (void) } static void +test_accelerators (void) +{ + GtkBuilder *builder; + const gchar *buffer = + "<interface>" + " <object class=\"GtkWindow\" id=\"window1\">" + " <child>" + " <object class=\"GtkButton\" id=\"button1\">" + " <accelerator key=\"q\" modifiers=\"GDK_CONTROL_MASK\" signal=\"clicked\"/>" + " </object>" + " </child>" + " </object>" + "</interface>"; + const gchar *buffer2 = + "<interface>" + " <object class=\"GtkWindow\" id=\"window1\">" + " <child>" + " <object class=\"GtkTreeView\" id=\"treeview1\">" + " </object>" + " </child>" + " </object>" + "</interface>"; + GObject *window1; + GSList *accel_groups; + GObject *accel_group; + + builder = builder_new_from_string (buffer, -1, NULL); + window1 = gtk_builder_get_object (builder, "window1"); + g_assert (window1); + g_assert (GTK_IS_WINDOW (window1)); + + accel_groups = gtk_accel_groups_from_object (window1); + g_assert (g_slist_length (accel_groups) == 1); + accel_group = g_slist_nth_data (accel_groups, 0); + g_assert (accel_group); + + gtk_widget_destroy (GTK_WIDGET (window1)); + g_object_unref (builder); + + builder = builder_new_from_string (buffer2, -1, NULL); + window1 = gtk_builder_get_object (builder, "window1"); + g_assert (window1); + g_assert (GTK_IS_WINDOW (window1)); + + accel_groups = gtk_accel_groups_from_object (window1); + g_assert_cmpint (g_slist_length (accel_groups), ==, 0); + + gtk_widget_destroy (GTK_WIDGET (window1)); + g_object_unref (builder); +} + +static void test_widget (void) { const gchar *buffer = @@ -2445,6 +2497,7 @@ main (int argc, char **argv) #endif g_test_add_func ("/Builder/CellView", test_cell_view); g_test_add_func ("/Builder/Dialog", test_dialog); + g_test_add_func ("/Builder/Accelerators", test_accelerators); g_test_add_func ("/Builder/Widget", test_widget); g_test_add_func ("/Builder/Value From String", test_value_from_string); g_test_add_func ("/Builder/Reference Counting", test_reference_counting); diff --git a/testsuite/gtk/concatmodel.c b/testsuite/gtk/concatmodel.c deleted file mode 100644 index 3e6292b536..0000000000 --- a/testsuite/gtk/concatmodel.c +++ /dev/null @@ -1,372 +0,0 @@ -/* GtkRBTree tests. - * - * Copyright (C) 2011, Red Hat, Inc. - * Authors: Benjamin Otte <otte@gnome.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <locale.h> - -#include "../../gtk/gtkconcatmodelprivate.h" - -/* _gtk_rbtree_test */ - -static GQuark number_quark; -static GQuark changes_quark; - -static guint -get (GListModel *model, - guint position) -{ - GObject *object = g_list_model_get_item (model, position); - g_assert (object != NULL); - return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark)); -} - -static char * -model_to_string (GListModel *model) -{ - GString *string = g_string_new (NULL); - guint i; - - for (i = 0; i < g_list_model_get_n_items (model); i++) - { - if (i > 0) - g_string_append (string, " "); - g_string_append_printf (string, "%u", get (model, i)); - } - - return g_string_free (string, FALSE); -} - -static void -add (GListStore *store, - guint number) -{ - GObject *object; - - /* o cannot be differentiated from NULL, so don't use it */ - g_assert (number != 0); - - object = g_object_new (G_TYPE_OBJECT, NULL); - g_object_set_qdata (object, number_quark, GUINT_TO_POINTER (number)); - g_list_store_append (store, object); - g_object_unref (object); -} - -static void -remove (GListStore *store, - guint position) -{ - g_list_store_remove (store, position); -} - -#define assert_model(model, expected) G_STMT_START{ \ - char *s = model_to_string (G_LIST_MODEL (model)); \ - if (!g_str_equal (s, expected)) \ - g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - #model " == " #expected, s, "==", expected); \ - g_free (s); \ -}G_STMT_END - -#define assert_changes(model, expected) G_STMT_START{ \ - GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \ - if (!g_str_equal (changes->str, expected)) \ - g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - #model " == " #expected, changes->str, "==", expected); \ - g_string_set_size (changes, 0); \ -}G_STMT_END - -static GListStore * -new_empty_store (void) -{ - return g_list_store_new (G_TYPE_OBJECT); -} - -static GListStore * -new_store (guint start, - guint end) -{ - GListStore *store = new_empty_store (); - guint i; - - for (i = start; i <= end; i++) - add (store, i); - - return store; -} - -static void -items_changed (GListModel *model, - guint position, - guint removed, - guint added, - GString *changes) -{ - g_assert (removed != 0 || added != 0); - - if (changes->len) - g_string_append (changes, ", "); - - if (removed == 1 && added == 0) - { - g_string_append_printf (changes, "-%u", position); - } - else if (removed == 0 && added == 1) - { - g_string_append_printf (changes, "+%u", position); - } - else - { - g_string_append_printf (changes, "%u", position); - if (removed > 0) - g_string_append_printf (changes, "-%u", removed); - if (added > 0) - g_string_append_printf (changes, "+%u", added); - } -} - -static void -free_changes (gpointer data) -{ - GString *changes = data; - - /* all changes must have been checked via assert_changes() before */ - g_assert_cmpstr (changes->str, ==, ""); - - g_string_free (changes, TRUE); -} - -static GtkConcatModel * -new_model (void) -{ - GtkConcatModel *model = gtk_concat_model_new (G_TYPE_OBJECT); - GString *changes; - - changes = g_string_new (""); - g_object_set_qdata_full (G_OBJECT(model), changes_quark, changes, free_changes); - g_signal_connect (model, "items-changed", G_CALLBACK (items_changed), changes); - - return model; -} - -static void -test_append (void) -{ - GListStore *store = new_store (1, 3); - GtkConcatModel *concat = new_model (); - - gtk_concat_model_append (concat, G_LIST_MODEL (store)); - - assert_model (concat, "1 2 3"); - assert_changes (concat, "0+3"); - - g_object_unref (store); - g_object_unref (concat); -} - -static void -test_append_and_add (void) -{ - GListStore *store = new_empty_store (); - GtkConcatModel *concat = new_model (); - - gtk_concat_model_append (concat, G_LIST_MODEL (store)); - - add (store, 1); - add (store, 2); - add (store, 3); - assert_model (concat, "1 2 3"); - assert_changes (concat, "+0, +1, +2"); - - g_object_unref (store); - g_object_unref (concat); -} - -static void -test_append_and_remove (void) -{ - GListStore *store = new_store (1, 3); - GtkConcatModel *concat = new_model (); - - gtk_concat_model_append (concat, G_LIST_MODEL (store)); - gtk_concat_model_remove (concat, G_LIST_MODEL (store)); - - assert_model (concat, ""); - assert_changes (concat, "0+3, 0-3"); - - /* Check that all signal handlers are gone */ - g_list_store_remove_all (store); - - g_object_unref (store); - g_object_unref (concat); -} - -static void -test_append_and_remove_items (void) -{ - GListStore *store = new_empty_store (); - GtkConcatModel *concat = new_model (); - - gtk_concat_model_append (concat, G_LIST_MODEL (store)); - - add (store, 1); - add (store, 2); - add (store, 3); - remove (store, 0); - remove (store, 1); - remove (store, 0); - - assert_model (concat, ""); - assert_changes (concat, "+0, +1, +2, -0, -1, -0"); - - g_object_unref (store); - g_object_unref (concat); -} - -static void -test_append_many (void) -{ - GListStore *store[5] = { new_store (1, 3), new_store (4, 4), new_store (5, 10), new_empty_store (), new_store (11, 20) }; - GtkConcatModel *concat = new_model (); - guint i; - - for (i = 0; i < G_N_ELEMENTS (store); i++) - gtk_concat_model_append (concat, G_LIST_MODEL (store[i])); - - assert_model (concat, "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20"); - assert_changes (concat, "0+3, +3, 4+6, 10+10"); - - for (i = 0; i < G_N_ELEMENTS (store); i++) - g_object_unref (store[i]); - g_object_unref (concat); -} - -static void -test_append_many_and_add (void) -{ - GListStore *store[3]; - GtkConcatModel *concat = new_model (); - guint i, j; - - for (i = 0; i < G_N_ELEMENTS (store); i++) - { - store[i] = new_empty_store (); - gtk_concat_model_append (concat, G_LIST_MODEL (store[i])); - } - - for (i = 0; i < G_N_ELEMENTS (store); i++) - { - for (j = 0; j < G_N_ELEMENTS (store); j++) - { - add (store[(i + j) % G_N_ELEMENTS (store)], i * 3 + j + 1); - } - } - - assert_model (concat, "1 6 8 2 4 9 3 5 7"); - assert_changes (concat, "+0, +1, +2, +2, +4, +1, +6, +2, +5"); - - for (i = 0; i < G_N_ELEMENTS (store); i++) - g_object_unref (store[i]); - g_object_unref (concat); -} - -static void -test_append_many_and_remove (void) -{ - GListStore *store[5]; - GtkConcatModel *concat = new_model (); - guint i; - - store[0] = new_empty_store (); - gtk_concat_model_append (concat, G_LIST_MODEL (store[0])); - for (i = 1; i < G_N_ELEMENTS (store); i++) - { - store[i] = new_store (i * (i - 1) / 2 + 1, i * (i + 1) / 2); - gtk_concat_model_append (concat, G_LIST_MODEL (store[i])); - } - - assert_model (concat, "1 2 3 4 5 6 7 8 9 10"); - assert_changes (concat, "+0, 1+2, 3+3, 6+4"); - - for (i = 0; i < G_N_ELEMENTS (store); i++) - { - gtk_concat_model_remove (concat, G_LIST_MODEL (store[(3 * i) % G_N_ELEMENTS (store)])); - } - - assert_model (concat, ""); - assert_changes (concat, "3-3, -0, 2-4, 0-2"); - - for (i = 0; i < G_N_ELEMENTS (store); i++) - { - g_list_store_remove_all (store[i]); - g_object_unref (store[i]); - } - - g_object_unref (concat); -} - -static void -test_append_many_and_remove_items (void) -{ - GListStore *store[5]; - GtkConcatModel *concat = new_model (); - guint i; - - store[0] = new_empty_store (); - gtk_concat_model_append (concat, G_LIST_MODEL (store[0])); - for (i = 1; i < G_N_ELEMENTS (store); i++) - { - store[i] = new_store (i * (i - 1) / 2 + 1, i * (i + 1) / 2); - gtk_concat_model_append (concat, G_LIST_MODEL (store[i])); - } - - assert_model (concat, "1 2 3 4 5 6 7 8 9 10"); - assert_changes (concat, "+0, 1+2, 3+3, 6+4"); - - for (i = 1; i < G_N_ELEMENTS (store); i++) - { - remove (store[i], 3 % i); - } - - assert_model (concat, "2 5 6 7 8 9"); - assert_changes (concat, "-0, -1, -1, -6"); - - for (i = 0; i < G_N_ELEMENTS (store); i++) - g_object_unref (store[i]); - g_object_unref (concat); -} - -int -main (int argc, char *argv[]) -{ - g_test_init (&argc, &argv, NULL); - setlocale (LC_ALL, "C"); - g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s"); - - number_quark = g_quark_from_static_string ("Hell and fire was spawned to be released."); - changes_quark = g_quark_from_static_string ("What did I see? Can I believe what I saw?"); - - g_test_add_func ("/compatmodel/append", test_append); - g_test_add_func ("/compatmodel/append_and_add", test_append_and_add); - g_test_add_func ("/compatmodel/append_and_remove", test_append_and_remove); - g_test_add_func ("/compatmodel/append_and_remove_items", test_append_and_remove_items); - g_test_add_func ("/compatmodel/append_many", test_append_many); - g_test_add_func ("/compatmodel/append_many_and_add", test_append_many_and_add); - g_test_add_func ("/compatmodel/append_many_and_remove", test_append_many_and_remove); - g_test_add_func ("/compatmodel/append_many_and_remove_items", test_append_many_and_remove_items); - - return g_test_run (); -} diff --git a/testsuite/gtk/defaultvalue.c b/testsuite/gtk/defaultvalue.c index 4222c32b70..ab735dc482 100644 --- a/testsuite/gtk/defaultvalue.c +++ b/testsuite/gtk/defaultvalue.c @@ -324,11 +324,6 @@ G_GNUC_END_IGNORE_DEPRECATIONS if (g_type_is_a (type, GTK_TYPE_SETTINGS)) continue; - if (g_type_is_a (type, GTK_TYPE_SHORTCUT) && - (strcmp (pspec->name, "action") == 0 || - strcmp (pspec->name, "trigger") == 0)) - continue; - if (g_type_is_a (type, GTK_TYPE_SPIN_BUTTON) && (strcmp (pspec->name, "adjustment") == 0)) continue; diff --git a/testsuite/gtk/meson.build b/testsuite/gtk/meson.build index 15c32cd2a3..983a33ffad 100644 --- a/testsuite/gtk/meson.build +++ b/testsuite/gtk/meson.build @@ -17,7 +17,6 @@ tests = [ ['builderparser'], ['cellarea'], ['check-icon-names'], - ['concatmodel', ['../../gtk/gtkconcatmodel.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']], ['constraint-solver', [ '../../gtk/gtkconstraintsolver.c', '../../gtk/gtkconstraintexpression.c', @@ -65,6 +64,7 @@ tests = [ ['treepath'], ['treeview'], ['typename'], + ['window'], ['displayclose'], ['revealer-size'], ['widgetorder'], @@ -72,10 +72,6 @@ tests = [ # Tests that are expected to fail xfail = [ - # one of the window resizing tests fails after - # the GdkToplevel refactoring, and needs a big - # gtkwindow.c configure request cleanup - 'window', ] is_debug = get_option('buildtype').startswith('debug') |