]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkmain.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkmain.c
index 799db279358bcc696833ac95ad6b4cb041ef7845..64e06d5059b45b2585b3aef3c2c65ca5af3bc353 100644 (file)
@@ -12,9 +12,7 @@
  * 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
@@ -61,8 +59,8 @@
  * int
  * main (int argc, char **argv)
  * {
- *   /&ast; Initialize i18n support &ast;/
- *   gtk_set_locale ();
+ *   /&ast; Initialize i18n support with bindtextdomain(), etc. &ast;/
+ *   ...
  *
  *   /&ast; Initialize the widget set &ast;/
  *   gtk_init (&argc, &argv);
@@ -91,9 +89,6 @@
 
 #include "config.h"
 
-#include "gtkmainprivate.h"
-
-#include <glib.h>
 #include "gdk/gdk.h"
 
 #include <locale.h>
 
 #include "gtkintl.h"
 
-#include "gtkaccelmap.h"
+#include "gtkaccelmapprivate.h"
 #include "gtkbox.h"
 #include "gtkclipboard.h"
+#include "gtkdebug.h"
 #include "gtkdnd.h"
-#include "gtkversion.h"
+#include "gtkmain.h"
+#include "gtkmenu.h"
 #include "gtkmodules.h"
-#include "gtkrc.h"
+#include "gtkmodulesprivate.h"
+#include "gtkprivate.h"
 #include "gtkrecentmanager.h"
+#include "gtkresources.h"
 #include "gtkselectionprivate.h"
 #include "gtksettingsprivate.h"
+#include "gtktooltip.h"
+#include "gtkversion.h"
 #include "gtkwidgetprivate.h"
 #include "gtkwindowprivate.h"
-#include "gtktooltip.h"
-#include "gtkdebug.h"
-#include "gtkmenu.h"
-
-#ifdef G_OS_WIN32
-
-static HMODULE gtk_dll;
-
-BOOL WINAPI
-DllMain (HINSTANCE hinstDLL,
-         DWORD     fdwReason,
-         LPVOID    lpvReserved)
-{
-  switch (fdwReason)
-    {
-    case DLL_PROCESS_ATTACH:
-      gtk_dll = (HMODULE) hinstDLL;
-      break;
-    }
-
-  return TRUE;
-}
-
-/* These here before inclusion of gtkprivate.h so that the original
- * GTK_LIBDIR and GTK_LOCALEDIR definitions are seen. Yeah, this is a
- * bit sucky.
- */
-const gchar *
-_gtk_get_libdir (void)
-{
-  static char *gtk_libdir = NULL;
-  if (gtk_libdir == NULL)
-    {
-      gchar *root = g_win32_get_package_installation_directory_of_module (gtk_dll);
-      gchar *slash = strrchr (root, '\\');
-      if (g_ascii_strcasecmp (slash + 1, ".libs") == 0)
-        gtk_libdir = GTK_LIBDIR;
-      else
-        gtk_libdir = g_build_filename (root, "lib", NULL);
-      g_free (root);
-    }
-
-  return gtk_libdir;
-}
 
-const gchar *
-_gtk_get_localedir (void)
-{
-  static char *gtk_localedir = NULL;
-  if (gtk_localedir == NULL)
-    {
-      const gchar *p;
-      gchar *root, *temp;
-      
-      /* GTK_LOCALEDIR ends in either /lib/locale or
-       * /share/locale. Scan for that slash.
-       */
-      p = GTK_LOCALEDIR + strlen (GTK_LOCALEDIR);
-      while (*--p != '/')
-        ;
-      while (*--p != '/')
-        ;
-
-      root = g_win32_get_package_installation_directory_of_module (gtk_dll);
-      temp = g_build_filename (root, p, NULL);
-      g_free (root);
-
-      /* gtk_localedir is passed to bindtextdomain() which isn't
-       * UTF-8-aware.
-       */
-      gtk_localedir = g_win32_locale_filename_from_utf8 (temp);
-      g_free (temp);
-    }
-  return gtk_localedir;
-}
-
-#endif
-
-#include "gtkprivate.h"
+#include "a11y/gtkaccessibility.h"
 
 /* Private type definitions
  */
@@ -248,6 +172,7 @@ static const GDebugKey gtk_debug_keys[] = {
   {"printing", GTK_DEBUG_PRINTING},
   {"builder", GTK_DEBUG_BUILDER},
   {"size-request", GTK_DEBUG_SIZE_REQUEST},
+  {"no-css-cache", GTK_DEBUG_NO_CSS_CACHE}
 };
 #endif /* G_ENABLE_DEBUG */
 
@@ -448,48 +373,6 @@ check_setugid (void)
   return TRUE;
 }
 
-#ifdef G_OS_WIN32
-
-const gchar *
-_gtk_get_datadir (void)
-{
-  static char *gtk_datadir = NULL;
-  if (gtk_datadir == NULL)
-    {
-      gchar *root = g_win32_get_package_installation_directory_of_module (gtk_dll);
-      gtk_datadir = g_build_filename (root, "share", NULL);
-      g_free (root);
-    }
-
-  return gtk_datadir;
-}
-
-const gchar *
-_gtk_get_sysconfdir (void)
-{
-  static char *gtk_sysconfdir = NULL;
-  if (gtk_sysconfdir == NULL)
-    {
-      gchar *root = g_win32_get_package_installation_directory_of_module (gtk_dll);
-      gtk_sysconfdir = g_build_filename (root, "etc", NULL);
-      g_free (root);
-    }
-
-  return gtk_sysconfdir;
-}
-
-const gchar *
-_gtk_get_data_prefix (void)
-{
-  static char *gtk_data_prefix = NULL;
-  if (gtk_data_prefix == NULL)
-    gtk_data_prefix = g_win32_get_package_installation_directory_of_module (gtk_dll);
-
-  return gtk_data_prefix;
-}
-
-#endif /* G_OS_WIN32 */
-
 static gboolean do_setlocale = TRUE;
 
 /**
@@ -738,22 +621,6 @@ setlocale_initialization (void)
     }
 }
 
-static void
-check_mixed_deps (void)
-{
-  GModule *module;
-  gpointer func;
-
-  module = g_module_open (NULL, 0);
-
-  if (g_module_symbol (module, "gtk_progress_get_type", &func))
-    {
-      g_error ("GTK+ 2.x symbols detected. Using GTK+ 2.x and GTK+ 3 in the same process is not supported");
-    }
-
-  g_module_close (module);
-}
-
 static void
 do_pre_parse_initialization (int    *argc,
                              char ***argv)
@@ -765,11 +632,12 @@ do_pre_parse_initialization (int    *argc,
 
   pre_initialized = TRUE;
 
-  check_mixed_deps ();
+  if (_gtk_module_has_mixed_deps (NULL))
+    g_error ("GTK+ 2.x symbols detected. Using GTK+ 2.x and GTK+ 3 in the same process is not supported");
 
   gdk_pre_parse_libgtk_only ();
   gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL);
-  
+
 #ifdef G_ENABLE_DEBUG
   env_string = g_getenv ("GTK_DEBUG");
   if (env_string != NULL)
@@ -792,8 +660,8 @@ gettext_initialization (void)
   setlocale_initialization ();
 
 #ifdef ENABLE_NLS
-  bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
-  bindtextdomain (GETTEXT_PACKAGE "-properties", GTK_LOCALEDIR);
+  bindtextdomain (GETTEXT_PACKAGE, _gtk_get_localedir ());
+  bindtextdomain (GETTEXT_PACKAGE "-properties", _gtk_get_localedir ());
 #    ifdef HAVE_BIND_TEXTDOMAIN_CODESET
   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
   bind_textdomain_codeset (GETTEXT_PACKAGE "-properties", "UTF-8");
@@ -839,8 +707,7 @@ do_post_parse_initialization (int    *argc,
       g_warning ("Whoever translated default:LTR did so wrongly.\n");
   }
 
-  /* do what the call to gtk_type_init() used to do */
-  g_type_init ();
+  _gtk_register_resource ();
 
   _gtk_accel_map_init ();
 
@@ -858,6 +725,8 @@ do_post_parse_initialization (int    *argc,
     {
       _gtk_modules_init (argc, argv, NULL);
     }
+
+  _gtk_accessibility_init ();
 }
 
 
@@ -935,7 +804,7 @@ gtk_set_debug_flags (guint flags)
 }
 
 /**
- * gtk_get_option_group:
+ * gtk_get_option_group: (skip)
  * @open_default_display: whether to open the default display
  *     when parsing the commandline arguments
  *
@@ -974,13 +843,17 @@ gtk_get_option_group (gboolean open_default_display)
 
 /**
  * gtk_init_with_args:
- * @argc: a pointer to the number of command line arguments
- * @argv: a pointer to the array of command line arguments
+ * @argc: (inout): Address of the <parameter>argc</parameter> parameter of
+ *     your main() function (or 0 if @argv is %NULL). This will be changed if 
+ *     any arguments were handled.
+ * @argv: (array length=argc) (inout) (allow-none): Address of the
+ *     <parameter>argv</parameter> parameter of main(), or %NULL. Any options
+ *     understood by GTK+ are stripped before return.
  * @parameter_string: a string which is displayed in
  *    the first line of <option>--help</option> output, after
  *    <literal><replaceable>programname</replaceable> [OPTION...]</literal>
- * @entries: a %NULL-terminated array of #GOptionEntrys
- *    describing the options of your program
+ * @entries: (array zero-terminated=1): a %NULL-terminated array
+ *    of #GOptionEntrys describing the options of your program
  * @translation_domain: a translation domain to use for translating
  *    the <option>--help</option> output for the options in @entries
  *    and the @parameter_string with gettext(), or %NULL
@@ -1036,7 +909,8 @@ gtk_init_with_args (gint                 *argc,
 /**
  * gtk_parse_args:
  * @argc: (inout): a pointer to the number of command line arguments
- * @argv: (array) (inout): a pointer to the array of command line arguments
+ * @argv: (array length=argc) (inout): a pointer to the array of
+ *     command line arguments
  *
  * Parses command line arguments, and initializes global
  * attributes of GTK+, but does not actually open a connection
@@ -1089,10 +963,11 @@ gtk_parse_args (int    *argc,
 /**
  * gtk_init_check:
  * @argc: (inout): Address of the <parameter>argc</parameter> parameter of
- *     your main() function. Changed if any arguments were handled
+ *     your main() function (or 0 if @argv is %NULL). This will be changed if 
+ *     any arguments were handled.
  * @argv: (array length=argc) (inout) (allow-none): Address of the
- *     <parameter>argv</parameter> parameter of main()
- *   Any parameters understood by gtk_init() are stripped before return
+ *     <parameter>argv</parameter> parameter of main(), or %NULL. Any options
+ *     understood by GTK+ are stripped before return.
  *
  * This function does the same work as gtk_init() with only a single
  * change: It does not terminate the program if the windowing system
@@ -1122,15 +997,20 @@ gtk_init_check (int    *argc,
 /**
  * gtk_init:
  * @argc: (inout): Address of the <parameter>argc</parameter> parameter of
- *     your main() function. Changed if any arguments were handled
+ *     your main() function (or 0 if @argv is %NULL). This will be changed if 
+ *     any arguments were handled.
  * @argv: (array length=argc) (inout) (allow-none): Address of the
- *     <parameter>argv</parameter> parameter of main(). Any options
+ *     <parameter>argv</parameter> parameter of main(), or %NULL. Any options
  *     understood by GTK+ are stripped before return.
  *
  * Call this function before using any other GTK+ functions in your GUI
  * applications.  It will initialize everything needed to operate the
  * toolkit and parses some standard command line options.
  *
+ * Although you are expected to pass the @argc, @argv parameters from main() to 
+ * this function, it is possible to pass %NULL if @argv is not available or 
+ * commandline handling is not required.
+ *
  * @argc and @argv are adjusted accordingly so your own code will
  * never see those standard arguments.
  *
@@ -1231,66 +1111,6 @@ gtk_init_check_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof
 
 #endif
 
-/*
- * _gtk_get_lc_ctype:
- *
- * Return the Unix-style locale string for the language currently in
- * effect. On Unix systems, this is the return value from
- * <literal>setlocale(LC_CTYPE, NULL)</literal>, and the user can
- * affect this through the environment variables LC_ALL, LC_CTYPE or
- * LANG (checked in that order). The locale strings typically is in
- * the form lang_COUNTRY, where lang is an ISO-639 language code, and
- * COUNTRY is an ISO-3166 country code. For instance, sv_FI for
- * Swedish as written in Finland or pt_BR for Portuguese as written in
- * Brazil.
- *
- * On Windows, the C library doesn't use any such environment
- * variables, and setting them won't affect the behaviour of functions
- * like ctime(). The user sets the locale through the Regional Options
- * in the Control Panel. The C library (in the setlocale() function)
- * does not use country and language codes, but country and language
- * names spelled out in English.
- * However, this function does check the above environment
- * variables, and does return a Unix-style locale string based on
- * either said environment variables or the thread's current locale.
- *
- * Return value: a dynamically allocated string, free with g_free().
- */
-
-gchar *
-_gtk_get_lc_ctype (void)
-{
-#ifdef G_OS_WIN32
-  /* Somebody might try to set the locale for this process using the
-   * LANG or LC_ environment variables. The Microsoft C library
-   * doesn't know anything about them. You set the locale in the
-   * Control Panel. Setting these env vars won't have any affect on
-   * locale-dependent C library functions like ctime(). But just for
-   * kicks, do obey LC_ALL, LC_CTYPE and LANG in GTK. (This also makes
-   * it easier to test GTK and Pango in various default languages, you
-   * don't have to clickety-click in the Control Panel, you can simply
-   * start the program with LC_ALL=something on the command line.)
-   */
-  gchar *p;
-
-  p = getenv ("LC_ALL");
-  if (p != NULL)
-    return g_strdup (p);
-
-  p = getenv ("LC_CTYPE");
-  if (p != NULL)
-    return g_strdup (p);
-
-  p = getenv ("LANG");
-  if (p != NULL)
-    return g_strdup (p);
-
-  return g_win32_getlocale ();
-#else
-  return g_strdup (setlocale (LC_CTYPE, NULL));
-#endif
-}
-
 /**
  * gtk_get_default_language:
  *
@@ -1332,9 +1152,9 @@ gtk_main (void)
 
   if (g_main_loop_is_running (main_loops->data))
     {
-      GDK_THREADS_LEAVE ();
+      gdk_threads_leave ();
       g_main_loop_run (loop);
-      GDK_THREADS_ENTER ();
+      gdk_threads_enter ();
       gdk_flush ();
     }
 
@@ -1346,11 +1166,15 @@ gtk_main (void)
 
   if (gtk_main_loop_level == 0)
     {
+      /* Keep this section in sync with gtk_application_shutdown() */
+
       /* Try storing all clipboard data we have */
       _gtk_clipboard_store_all ();
 
       /* Synchronize the recent manager singleton */
       _gtk_recent_manager_sync ();
+
+      _gtk_accessibility_shutdown ();
     }
 }
 
@@ -1409,9 +1233,9 @@ gtk_events_pending (void)
 {
   gboolean result;
 
-  GDK_THREADS_LEAVE ();
+  gdk_threads_leave ();
   result = g_main_context_pending (NULL);
-  GDK_THREADS_ENTER ();
+  gdk_threads_enter ();
 
   return result;
 }
@@ -1432,9 +1256,9 @@ gtk_events_pending (void)
 gboolean
 gtk_main_iteration (void)
 {
-  GDK_THREADS_LEAVE ();
+  gdk_threads_leave ();
   g_main_context_iteration (NULL, TRUE);
-  GDK_THREADS_ENTER ();
+  gdk_threads_enter ();
 
   if (main_loops)
     return !g_main_loop_is_running (main_loops->data);
@@ -1456,9 +1280,9 @@ gtk_main_iteration (void)
 gboolean
 gtk_main_iteration_do (gboolean blocking)
 {
-  GDK_THREADS_LEAVE ();
+  gdk_threads_leave ();
   g_main_context_iteration (NULL, blocking);
-  GDK_THREADS_ENTER ();
+  gdk_threads_enter ();
 
   if (main_loops)
     return !g_main_loop_is_running (main_loops->data);
@@ -1514,6 +1338,14 @@ rewrite_event_for_window (GdkEvent  *event,
                                 new_window,
                                 &event->motion.x, &event->motion.y);
       break;
+    case GDK_TOUCH_BEGIN:
+    case GDK_TOUCH_UPDATE:
+    case GDK_TOUCH_END:
+    case GDK_TOUCH_CANCEL:
+      rewrite_events_translate (event->any.window,
+                                new_window,
+                                &event->touch.x, &event->touch.y);
+      break;
     case GDK_KEY_PRESS:
     case GDK_KEY_RELEASE:
     case GDK_PROXIMITY_IN:
@@ -1559,6 +1391,10 @@ rewrite_event_for_grabs (GdkEvent *event)
     case GDK_PROXIMITY_OUT:
     case GDK_KEY_PRESS:
     case GDK_KEY_RELEASE:
+    case GDK_TOUCH_BEGIN:
+    case GDK_TOUCH_UPDATE:
+    case GDK_TOUCH_END:
+    case GDK_TOUCH_CANCEL:
       display = gdk_window_get_display (event->any.window);
       device = gdk_event_get_device (event);
 
@@ -1603,8 +1439,7 @@ rewrite_event_for_grabs (GdkEvent *event)
  * </para></listitem>
  * <listitem><para>
  *   Find the widget which got the event. If the widget can't be determined
- *   the event is thrown away unless it belongs to a INCR transaction. In that
- *   case it is passed to gtk_selection_incr_event().
+ *   the event is thrown away unless it belongs to a INCR transaction.
  * </para></listitem>
  * <listitem><para>
  *   Then the event is pushed onto a stack so you can query the currently
@@ -1646,6 +1481,7 @@ gtk_main_do_event (GdkEvent *event)
 {
   GtkWidget *event_widget;
   GtkWidget *grab_widget = NULL;
+  GtkWidget *topmost_widget = NULL;
   GtkWindowGroup *window_group;
   GdkEvent *rewritten_event = NULL;
   GdkDevice *device;
@@ -1705,12 +1541,20 @@ gtk_main_do_event (GdkEvent *event)
   if (!grab_widget)
     grab_widget = gtk_window_group_get_current_grab (window_group);
 
+  /* Find out the topmost widget where captured event propagation
+   * should start, which is the widget holding the GTK+ grab
+   * if any, otherwise it's left NULL and events are emitted
+   * from the toplevel (or topmost parentless parent).
+   */
+  if (grab_widget)
+    topmost_widget = grab_widget;
+
   /* If the grab widget is an ancestor of the event widget
    * then we send the event to the original event widget.
    * This is the key to implementing modality.
    */
   if (!grab_widget ||
-      (gtk_widget_is_sensitive (event_widget) &&
+      ((gtk_widget_is_sensitive (event_widget) || event->type == GDK_SCROLL) &&
        gtk_widget_is_ancestor (event_widget, grab_widget)))
     grab_widget = event_widget;
 
@@ -1801,27 +1645,41 @@ gtk_main_do_event (GdkEvent *event)
     case GDK_WINDOW_STATE:
     case GDK_GRAB_BROKEN:
     case GDK_DAMAGE:
-      gtk_widget_event (event_widget, event);
+      if (!_gtk_widget_captured_event (event_widget, event))
+        gtk_widget_event (event_widget, event);
       break;
 
     case GDK_SCROLL:
     case GDK_BUTTON_PRESS:
     case GDK_2BUTTON_PRESS:
     case GDK_3BUTTON_PRESS:
-      gtk_propagate_event (grab_widget, event);
+    case GDK_TOUCH_BEGIN:
+      if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
+        gtk_propagate_event (grab_widget, event);
       break;
 
     case GDK_KEY_PRESS:
     case GDK_KEY_RELEASE:
-      if (key_snoopers)
-        {
-          if (gtk_invoke_key_snoopers (grab_widget, event))
-            break;
-        }
+      if (gtk_invoke_key_snoopers (grab_widget, event))
+        break;
+
+      /* make focus visible in a window that receives a key event */
+      {
+        GtkWidget *window;
+        GtkPolicyType visible_focus;
+
+        window = gtk_widget_get_toplevel (grab_widget);
+        g_object_get (gtk_widget_get_settings (grab_widget), "gtk-visible-focus", &visible_focus, NULL);
+        if (GTK_IS_WINDOW (window) && visible_focus != GTK_POLICY_NEVER)
+          gtk_window_set_focus_visible (GTK_WINDOW (window), TRUE);
+      }
+
       /* Catch alt press to enable auto-mnemonics;
        * menus are handled elsewhere
+       * FIXME: this does not work with mnemonic modifiers other than Alt
        */
       if ((event->key.keyval == GDK_KEY_Alt_L || event->key.keyval == GDK_KEY_Alt_R) &&
+          ((event->key.state & (gtk_accelerator_get_default_mod_mask ()) & ~(GDK_RELEASE_MASK|GDK_MOD1_MASK)) == 0) &&
           !GTK_IS_MENU_SHELL (grab_widget))
         {
           gboolean auto_mnemonics;
@@ -1837,9 +1695,13 @@ gtk_main_do_event (GdkEvent *event)
               mnemonics_visible = (event->type == GDK_KEY_PRESS);
 
               window = gtk_widget_get_toplevel (grab_widget);
-
               if (GTK_IS_WINDOW (window))
-                gtk_window_set_mnemonics_visible (GTK_WINDOW (window), mnemonics_visible);
+                {
+                  if (mnemonics_visible)
+                    _gtk_window_set_auto_mnemonics_visible (GTK_WINDOW (window));
+                  else
+                    gtk_window_set_mnemonics_visible (GTK_WINDOW (window), FALSE);
+                }
             }
         }
       /* else fall through */
@@ -1847,22 +1709,32 @@ gtk_main_do_event (GdkEvent *event)
     case GDK_BUTTON_RELEASE:
     case GDK_PROXIMITY_IN:
     case GDK_PROXIMITY_OUT:
-      gtk_propagate_event (grab_widget, event);
+    case GDK_TOUCH_UPDATE:
+    case GDK_TOUCH_END:
+    case GDK_TOUCH_CANCEL:
+      if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
+        gtk_propagate_event (grab_widget, event);
       break;
 
     case GDK_ENTER_NOTIFY:
-      _gtk_widget_set_device_window (event_widget,
-                                     gdk_event_get_device (event),
-                                     event->any.window);
-      if (gtk_widget_is_sensitive (grab_widget))
+      if (event->crossing.detail != GDK_NOTIFY_VIRTUAL &&
+          event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL)
+        _gtk_widget_set_device_window (event_widget,
+                                       gdk_event_get_device (event),
+                                       event->any.window);
+      if (gtk_widget_is_sensitive (grab_widget) &&
+          !_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
         gtk_widget_event (grab_widget, event);
       break;
 
     case GDK_LEAVE_NOTIFY:
-      _gtk_widget_set_device_window (event_widget,
-                                     gdk_event_get_device (event),
-                                     NULL);
-      if (gtk_widget_is_sensitive (grab_widget))
+      if (event->crossing.detail != GDK_NOTIFY_VIRTUAL &&
+          event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL)
+        _gtk_widget_set_device_window (event_widget,
+                                       gdk_event_get_device (event),
+                                       NULL);
+      if (gtk_widget_is_sensitive (grab_widget) &&
+          !_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
         gtk_widget_event (grab_widget, event);
       break;
 
@@ -1890,6 +1762,7 @@ gtk_main_do_event (GdkEvent *event)
       || event->type == GDK_DRAG_ENTER
       || event->type == GDK_GRAB_BROKEN
       || event->type == GDK_MOTION_NOTIFY
+      || event->type == GDK_TOUCH_UPDATE
       || event->type == GDK_SCROLL)
     {
       _gtk_tooltip_handle_event (event);
@@ -2149,7 +2022,7 @@ gtk_grab_notify (GtkWindowGroup *group,
 }
 
 /**
- * gtk_grab_add:
+ * gtk_grab_add: (method)
  * @widget: The widget that grabs keyboard and pointer events
  *
  * Makes @widget the current grabbed widget.
@@ -2203,7 +2076,7 @@ gtk_grab_get_current (void)
 }
 
 /**
- * gtk_grab_remove:
+ * gtk_grab_remove: (method)
  * @widget: The widget which gives up the grab
  *
  * Removes the grab from the given widget.
@@ -2237,7 +2110,7 @@ gtk_grab_remove (GtkWidget *widget)
 /**
  * gtk_device_grab_add:
  * @widget: a #GtkWidget
- * @device: a #GtkDevice to grab on.
+ * @device: a #GdkDevice to grab on.
  * @block_others: %TRUE to prevent other devices to interact with @widget.
  *
  * Adds a GTK+ grab on @device, so all the events on @device and its
@@ -2297,7 +2170,7 @@ gtk_device_grab_remove (GtkWidget *widget,
 }
 
 /**
- * gtk_key_snooper_install:
+ * gtk_key_snooper_install: (skip)
  * @snooper: a #GtkKeySnoopFunc
  * @func_data: data to pass to @snooper
  *
@@ -2306,6 +2179,9 @@ gtk_device_grab_remove (GtkWidget *widget,
  *
  * Returns: a unique id for this key snooper for use with
  *    gtk_key_snooper_remove().
+ *
+ * Deprecated: 3.4: Key snooping should not be done. Events should
+ *     be handled by widgets.
  */
 guint
 gtk_key_snooper_install (GtkKeySnoopFunc snooper,
@@ -2330,6 +2206,9 @@ gtk_key_snooper_install (GtkKeySnoopFunc snooper,
  * @snooper_handler_id: Identifies the key snooper to remove
  *
  * Removes the key snooper function with the given id.
+ *
+ * Deprecated: 3.4: Key snooping should not be done. Events should
+ *     be handled by widgets.
  */
 void
 gtk_key_snooper_remove (guint snooper_id)
@@ -2361,6 +2240,8 @@ gtk_invoke_key_snoopers (GtkWidget *grab_widget,
   GSList *slist;
   gint return_val = FALSE;
 
+  return_val = _gtk_accessibility_key_snooper (grab_widget, (GdkEventKey *) event);
+
   slist = key_snoopers;
   while (slist && !return_val)
     {
@@ -2383,9 +2264,9 @@ gtk_invoke_key_snoopers (GtkWidget *grab_widget,
  * the current event will be the #GdkEventButton that triggered
  * the ::clicked signal.
  *
- * Return value: a copy of the current event, or %NULL if there is
- *     no current event. The returned event must be freed with
- *     gdk_event_free().
+ * Return value: (transfer full): a copy of the current event, or
+ *     %NULL if there is no current event. The returned event must be
+ *     freed with gdk_event_free().
  */
 GdkEvent*
 gtk_get_current_event (void)
@@ -2416,7 +2297,7 @@ gtk_get_current_event_time (void)
 
 /**
  * gtk_get_current_event_state:
- * @state: a location to store the state of the current event
+ * @state: (out): a location to store the state of the current event
  *
  * If there is a current event and it has a state field, place
  * that state field in @state and return %TRUE, otherwise return
@@ -2484,45 +2365,104 @@ gtk_get_event_widget (GdkEvent *event)
   return widget;
 }
 
-/**
- * gtk_propagate_event:
- * @widget: a #GtkWidget
- * @event: an event
- *
- * Sends an event to a widget, propagating the event to parent widgets
- * if the event remains unhandled.
- *
- * Events received by GTK+ from GDK normally begin in gtk_main_do_event().
- * Depending on the type of event, existence of modal dialogs, grabs, etc.,
- * the event may be propagated; if so, this function is used.
- *
- * gtk_propagate_event() calls gtk_widget_event() on each widget it
- * decides to send the event to. So gtk_widget_event() is the lowest-level
- * function; it simply emits the #GtkWidget::event and possibly an
- * event-specific signal on a widget. gtk_propagate_event() is a bit
- * higher-level, and gtk_main_do_event() is the highest level.
- *
- * All that said, you most likely don't want to use any of these
- * functions; synthesizing events is rarely needed. There are almost
- * certainly better ways to achieve your goals. For example, use
- * gdk_window_invalidate_rect() or gtk_widget_queue_draw() instead
- * of making up expose events.
- */
-void
-gtk_propagate_event (GtkWidget *widget,
-                     GdkEvent  *event)
+static gboolean
+propagate_event_up (GtkWidget *widget,
+                    GdkEvent  *event,
+                    GtkWidget *topmost)
 {
-  gint handled_event;
+  gboolean handled_event = FALSE;
 
-  g_return_if_fail (GTK_IS_WIDGET (widget));
-  g_return_if_fail (event != NULL);
+  /* Propagate event up the widget tree so that
+   * parents can see the button and motion
+   * events of the children.
+   */
+  while (TRUE)
+    {
+      GtkWidget *tmp;
+
+      g_object_ref (widget);
+
+      /* Scroll events are special cased here because it
+       * feels wrong when scrolling a GtkViewport, say,
+       * to have children of the viewport eat the scroll
+       * event
+       */
+      if (!gtk_widget_is_sensitive (widget))
+        handled_event = event->type != GDK_SCROLL;
+      else
+        handled_event = gtk_widget_event (widget, event);
+
+      tmp = gtk_widget_get_parent (widget);
+      g_object_unref (widget);
+
+      if (widget == topmost)
+        break;
 
-  handled_event = FALSE;
+      widget = tmp;
 
-  g_object_ref (widget);
+      if (handled_event || !widget)
+        break;
+    }
+
+  return handled_event;
+}
+
+static gboolean
+propagate_event_down (GtkWidget *widget,
+                      GdkEvent  *event,
+                      GtkWidget *topmost)
+{
+  gint handled_event = FALSE;
+  GList *widgets = NULL;
+  GList *l;
 
-  if ((event->type == GDK_KEY_PRESS) ||
-      (event->type == GDK_KEY_RELEASE))
+  widgets = g_list_prepend (widgets, g_object_ref (widget));
+  while (widget && widget != topmost)
+    {
+      widget = gtk_widget_get_parent (widget);
+      if (!widget)
+        break;
+
+      widgets = g_list_prepend (widgets, g_object_ref (widget));
+
+      if (widget == topmost)
+        break;
+    }
+
+  for (l = widgets; l && !handled_event; l = g_list_next (l))
+    {
+      widget = (GtkWidget *)l->data;
+
+      if (!gtk_widget_is_sensitive (widget))
+        {
+          /* stop propagating on SCROLL, but don't handle the event, so it
+           * can propagate up again and reach its handling widget
+           */
+          if (event->type == GDK_SCROLL)
+            break;
+          else
+            handled_event = TRUE;
+        }
+      else
+        handled_event = _gtk_widget_captured_event (widget, event);
+    }
+  g_list_free_full (widgets, (GDestroyNotify)g_object_unref);
+
+  return handled_event;
+}
+
+static gboolean
+propagate_event (GtkWidget *widget,
+                 GdkEvent  *event,
+                 gboolean   captured,
+                 GtkWidget *topmost)
+{
+  gboolean handled_event = FALSE;
+  gboolean (* propagate_func) (GtkWidget *widget, GdkEvent  *event);
+
+  propagate_func = captured ? _gtk_widget_captured_event : gtk_widget_event;
+
+  if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
     {
       /* Only send key events within Window widgets to the Window
        * The Window widget will in turn pass the
@@ -2534,11 +2474,12 @@ gtk_propagate_event (GtkWidget *widget,
       window = gtk_widget_get_toplevel (widget);
       if (GTK_IS_WINDOW (window))
         {
+          g_object_ref (widget);
           /* If there is a grab within the window, give the grab widget
            * a first crack at the key event
            */
           if (widget != window && gtk_widget_has_grab (widget))
-            handled_event = gtk_widget_event (widget, event);
+            handled_event = propagate_func (widget, event);
 
           if (!handled_event)
             {
@@ -2546,61 +2487,59 @@ gtk_propagate_event (GtkWidget *widget,
               if (GTK_IS_WINDOW (window))
                 {
                   if (gtk_widget_is_sensitive (window))
-                    gtk_widget_event (window, event);
+                    handled_event = propagate_func (window, event);
                 }
             }
 
-          handled_event = TRUE; /* don't send to widget */
+          g_object_unref (widget);
+          return handled_event;
         }
     }
 
-  /* Other events get propagated up the widget tree
-   * so that parents can see the button and motion
-   * events of the children.
-   */
-  if (!handled_event)
-    {
-      while (TRUE)
-        {
-          GtkWidget *tmp;
-
-          /* Scroll events are special cased here because it
-           * feels wrong when scrolling a GtkViewport, say,
-           * to have children of the viewport eat the scroll
-           * event
-           */
-          if (!gtk_widget_is_sensitive (widget))
-            handled_event = event->type != GDK_SCROLL;
-          else
-            handled_event = gtk_widget_event (widget, event);
-
-          tmp = gtk_widget_get_parent (widget);
-          g_object_unref (widget);
+  /* Other events get propagated up/down the widget tree */
+  return captured ?
+    propagate_event_down (widget, event, topmost) :
+    propagate_event_up (widget, event, topmost);
+}
 
-          widget = tmp;
+/**
+ * gtk_propagate_event:
+ * @widget: a #GtkWidget
+ * @event: an event
+ *
+ * Sends an event to a widget, propagating the event to parent widgets
+ * if the event remains unhandled.
+ *
+ * Events received by GTK+ from GDK normally begin in gtk_main_do_event().
+ * Depending on the type of event, existence of modal dialogs, grabs, etc.,
+ * the event may be propagated; if so, this function is used.
+ *
+ * gtk_propagate_event() calls gtk_widget_event() on each widget it
+ * decides to send the event to. So gtk_widget_event() is the lowest-level
+ * function; it simply emits the #GtkWidget::event and possibly an
+ * event-specific signal on a widget. gtk_propagate_event() is a bit
+ * higher-level, and gtk_main_do_event() is the highest level.
+ *
+ * All that said, you most likely don't want to use any of these
+ * functions; synthesizing events is rarely needed. There are almost
+ * certainly better ways to achieve your goals. For example, use
+ * gdk_window_invalidate_rect() or gtk_widget_queue_draw() instead
+ * of making up expose events.
+ */
+void
+gtk_propagate_event (GtkWidget *widget,
+                     GdkEvent  *event)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (event != NULL);
 
-          if (!handled_event && widget)
-            g_object_ref (widget);
-          else
-            break;
-        }
-    }
-  else
-    g_object_unref (widget);
+  propagate_event (widget, event, FALSE, NULL);
 }
 
 gboolean
-_gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
-                                  GValue                *return_accu,
-                                  const GValue          *handler_return,
-                                  gpointer               dummy)
+_gtk_propagate_captured_event (GtkWidget *widget,
+                               GdkEvent  *event,
+                               GtkWidget *topmost)
 {
-  gboolean continue_emission;
-  gboolean signal_handled;
-
-  signal_handled = g_value_get_boolean (handler_return);
-  g_value_set_boolean (return_accu, signal_handled);
-  continue_emission = !signal_handled;
-
-  return continue_emission;
+  return propagate_event (widget, event, TRUE, topmost);
 }