[gtk] Revert "Merge branch 'disable-window-test' into 'master'"



commit 31db61588543a1ba0935ac8ecb2ecac574c0a836
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Mar 19 18:03:16 2020 -0400

    Revert "Merge branch 'disable-window-test' into 'master'"
    
    This reverts commit 3ac4c76b18cc89a841ce09f0943539f16988fd21, reversing
    changes made to 6ec96d2e989d029a303b8b20ec72b86f974c0e87.

 demos/gtk-demo/demo.gresource.xml                  |    1 -
 demos/gtk-demo/fontplane.c                         |   25 +
 demos/gtk-demo/main.c                              |    1 +
 demos/gtk-demo/meson.build                         |    1 -
 demos/gtk-demo/shortcut_triggers.c                 |   91 -
 demos/gtk-demo/sliding_puzzle.c                    |   79 +-
 docs/reference/gtk/gtk4-docs.xml                   |    9 -
 docs/reference/gtk/gtk4-sections.txt               |  244 ++-
 docs/reference/gtk/gtk4.types.in                   |    5 +-
 docs/tools/widgets.c                               |    8 +-
 gdk/broadway/broadway.js                           |    3 +-
 gdk/gdktypes.h                                     |   10 +-
 gtk/gtk.h                                          |    7 +-
 gtk/gtkaccelgroup.c                                | 1257 ++++++++++----
 gtk/gtkaccelgroup.h                                |  176 +-
 gtk/gtkaccelgroupprivate.h                         |   16 +-
 gtk/gtkaccellabel.c                                |  590 ++++++-
 gtk/gtkaccellabel.h                                |   10 +
 gtk/gtkaccelmap.c                                  | 1086 ++++++++++++
 gtk/gtkaccelmap.h                                  |  109 ++
 gtk/gtkaccelmapprivate.h                           |   39 +
 gtk/gtkapplication.c                               |    1 +
 gtk/gtkapplicationaccels.c                         |  521 ++++--
 gtk/gtkapplicationaccelsprivate.h                  |   10 +-
 gtk/gtkassistant.c                                 |    8 +-
 gtk/gtkbindings.c                                  | 1793 ++++++++++++++++++++
 gtk/gtkbindings.h                                  |  124 ++
 .../shortcuts.h => gtkbindingsprivate.h}           |   21 +-
 gtk/gtkbuilder.c                                   |   29 +-
 gtk/gtkcoloreditor.c                               |   25 +-
 gtk/gtkcolorplane.c                                |   37 +-
 gtk/gtkcolorscale.c                                |   39 +-
 gtk/gtkcolorswatch.c                               |   21 +-
 gtk/gtkcombobox.c                                  |  155 +-
 gtk/gtkconcatmodel.c                               |  254 ---
 gtk/gtkconcatmodelprivate.h                        |   52 -
 gtk/gtkdialog.c                                    |    5 +-
 gtk/gtkemojichooser.c                              |   40 +-
 gtk/gtkentry.c                                     |    1 +
 gtk/gtkenums.h                                     |   19 -
 gtk/gtkeventcontrollerfocus.c                      |    1 +
 gtk/gtkeventcontrollerkey.c                        |    4 +
 gtk/gtkfilechooserwidget.c                         |  299 ++--
 gtk/gtkflowbox.c                                   |  101 +-
 gtk/gtkiconview.c                                  |  144 +-
 gtk/gtkinfobar.c                                   |   19 +-
 gtk/gtklabel.c                                     |  226 ++-
 gtk/gtklinkbutton.c                                |   24 +-
 gtk/gtklistbox.c                                   |   97 +-
 gtk/gtkmain.c                                      |   12 +-
 gtk/gtkmnemonichash.c                              |  201 +++
 gtk/gtkmnemonichash.h                              |   52 +
 gtk/gtkmodelbutton.c                               |    6 +-
 gtk/gtkmountoperation.c                            |   21 +-
 gtk/gtknotebook.c                                  |  203 ++-
 gtk/gtkpaned.c                                     |  194 +--
 gtk/gtkpasswordentry.c                             |    1 +
 gtk/gtkplacesview.c                                |   62 +-
 gtk/gtkplacesviewrow.c                             |   30 +-
 gtk/gtkpopover.c                                   |   83 +-
 gtk/gtkpopovermenu.c                               |   84 +-
 gtk/gtkrange.c                                     |   10 +
 gtk/gtkroot.c                                      |    2 -
 gtk/gtkscale.c                                     |   13 +-
 gtk/gtkscalebutton.c                               |   40 +-
 gtk/gtkscrollbar.c                                 |   11 +
 gtk/gtkscrolledwindow.c                            |   66 +-
 gtk/gtksearchentry.c                               |   22 +-
 gtk/gtkshortcut.c                                  |  392 -----
 gtk/gtkshortcut.h                                  |   62 -
 gtk/gtkshortcutaction.c                            | 1134 -------------
 gtk/gtkshortcutaction.h                            |  135 --
 gtk/gtkshortcutactionprivate.h                     |   29 -
 gtk/gtkshortcutcontroller.c                        |  673 --------
 gtk/gtkshortcutcontroller.h                        |   70 -
 gtk/gtkshortcutcontrollerprivate.h                 |   28 -
 gtk/gtkshortcutlabel.c                             |    3 +-
 gtk/gtkshortcutmanager.c                           |  106 --
 gtk/gtkshortcutmanager.h                           |   71 -
 gtk/gtkshortcutmanagerprivate.h                    |   12 -
 gtk/gtkshortcutssection.c                          |   36 +-
 gtk/gtkshortcutswindow.c                           |   12 +-
 gtk/gtkshortcuttrigger.c                           |  976 -----------
 gtk/gtkshortcuttrigger.h                           |  121 --
 gtk/gtkspinbutton.c                                |   32 +-
 gtk/gtktext.c                                      |  309 ++--
 gtk/gtktextview.c                                  |  363 ++--
 gtk/gtktreeview.c                                  |  428 ++---
 gtk/gtktypes.h                                     |    3 -
 gtk/gtkwidget.c                                    |  724 +++++---
 gtk/gtkwidget.h                                    |   39 +-
 gtk/gtkwidgetprivate.h                             |    9 +-
 gtk/gtkwindow.c                                    |  859 ++++++++--
 gtk/gtkwindow.h                                    |   31 +
 gtk/gtkwindowprivate.h                             |    1 +
 gtk/inspector/init.c                               |    2 -
 gtk/inspector/meson.build                          |    1 -
 gtk/inspector/shortcuts.c                          |  158 --
 gtk/inspector/shortcuts.ui                         |   59 -
 gtk/inspector/window.c                             |    3 -
 gtk/inspector/window.h                             |    1 -
 gtk/inspector/window.ui                            |   10 -
 gtk/meson.build                                    |   16 +-
 gtk/ui/gtkcoloreditor.ui                           |    6 +-
 gtk/ui/gtkfilechooserwidget.ui                     |   11 +
 gtk/ui/gtkplacesviewrow.ui                         |    6 +
 po-properties/POTFILES.in                          |    1 +
 po/POTFILES.in                                     |    1 +
 tests/testgtk.c                                    |   53 +-
 tests/testmenubutton.c                             |    4 +
 testsuite/css/parser/at-invalid-22.css             |    1 +
 testsuite/css/parser/at-invalid-22.errors          |    1 +
 testsuite/css/parser/at-invalid-22.ref.css         |    0
 testsuite/css/parser/meson.build                   |    3 +
 testsuite/gtk/accel.c                              |   15 +-
 testsuite/gtk/action.c                             |    7 +-
 testsuite/gtk/builder.c                            |   53 +
 testsuite/gtk/concatmodel.c                        |  372 ----
 testsuite/gtk/defaultvalue.c                       |    5 -
 testsuite/gtk/meson.build                          |    6 +-
 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
@@ -140,6 +140,17 @@ update_value (GtkFontPlane *plane,
   gtk_widget_queue_draw (widget);
 }
 
+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,
@@ -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/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))
     {
@@ -242,24 +276,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)
 {
@@ -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
@@ -4537,6 +4617,26 @@ gtk_css_provider_error_quark
 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>
@@ -5961,144 +6061,6 @@ GTK_EVENT_CONTROLLER_MOTION_GET_CLASS
 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>
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;
 
 /**
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 8fe078e6a9..603fe9f9fd 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -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
@@ -204,6 +1087,20 @@ is_control (const gchar *string)
           (string[8] == '>'));
 }
 
+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)
 {
@@ -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);
@@ -242,6 +317,17 @@ gtk_accel_label_new (const gchar *string)
   return GTK_WIDGET (accel_label);
 }
 
+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)
 {
@@ -254,6 +340,26 @@ gtk_accel_label_finalize (GObject *object)
   G_OBJECT_CLASS (gtk_accel_label_parent_class)->finalize (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, &parameter_type, NULL, NULL, 
NULL))
+        continue;
+
+      if (!enabled)
+        continue;
+
+      /* We found an action with the correct name and it's enabled.
+       * This is the action that we are going to try to invoke.
+       *
+       * There is still the possibility that the target value doesn't
+       * match the expected parameter type.  In that case, we will print
+       * a warning.
+       *
+       * Note: we want to hold a ref on the target while we're invoking
+       * the action to prevent trouble if someone uninstalls the accel
+       * from the handler.  That's not a problem since we're parsing it.
+       */
+      if (actions[i] != sep) /* if it has a target... */
+        {
+          GError *error = NULL;
+
+          if (parameter_type == NULL)
+            {
+              gchar *accel_str = gtk_accelerator_name (key, modifier);
+              g_warning ("Accelerator '%s' tries to invoke action '%s' with target, but action has no 
parameter",
+                         accel_str, action_name);
+              g_free (accel_str);
+              return TRUE;
+            }
+
+          target = g_variant_parse (NULL, actions[i], sep, NULL, &error);
+          g_assert_no_error (error);
+          g_assert (target);
+
+          if (!g_variant_is_of_type (target, parameter_type))
+            {
+              gchar *accel_str = gtk_accelerator_name (key, modifier);
+              gchar *typestr = g_variant_type_dup_string (parameter_type);
+              gchar *targetstr = g_variant_print (target, TRUE);
+              g_warning ("Accelerator '%s' tries to invoke action '%s' with target '%s',"
+                         " but action expects parameter with type '%s'", accel_str, action_name, targetstr, 
typestr);
+              g_variant_unref (target);
+              g_free (targetstr);
+              g_free (accel_str);
+              g_free (typestr);
+              return TRUE;
+            }
+        }
+      else
+        {
+          if (parameter_type != NULL)
+            {
+              gchar *accel_str = gtk_accelerator_name (key, modifier);
+              gchar *typestr = g_variant_type_dup_string (parameter_type);
+              g_warning ("Accelerator '%s' tries to invoke action '%s' without target,"
+                         " but action expects parameter with type '%s'", accel_str, action_name, typestr);
+              g_free (accel_str);
+              g_free (typestr);
+              return TRUE;
+            }
+
+          target = NULL;
+        }
+
+      g_action_group_activate_action (action_group, action_name, target);
+
+      if (target)
+        g_variant_unref (target);
+
+      return TRUE;
+    }
+
+  return FALSE;
 }
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, &params))
+    {
+      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 (&params[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 (&parameters, 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
similarity index 59%
rename from gtk/inspector/shortcuts.h
rename to 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)
 
@@ -164,29 +160,6 @@ gtk_color_scale_init (GtkColorScale *scale)
   gtk_widget_add_css_class (GTK_WIDGET (scale), "color");
 }
 
-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,
@@ -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/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
@@ -972,25 +972,6 @@ typedef enum
   GTK_PAN_DIRECTION_DOWN
 } 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
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
@@ -194,6 +193,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)
 {
@@ -309,19 +320,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)
 {
@@ -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/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/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/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");
@@ -1674,6 +1637,32 @@ gtk_widget_class_init (GtkWidgetClass *klass)
                               G_TYPE_FROM_CLASS (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);
 
@@ -8875,6 +8996,90 @@ static const GtkBuildableParser accessibility_parser =
     accessibility_text,
   };
 
+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;
@@ -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 *
@@ -2381,6 +2542,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
@@ -184,6 +190,31 @@ GDK_AVAILABLE_IN_ALL
 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
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/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
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
@@ -1389,6 +1389,58 @@ test_message_dialog (void)
   g_object_unref (builder);
 }
 
+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)
 {
@@ -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/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')



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]