[gtk+/multitouch: 2/129] Add GtkWidget::captured-event signal



commit d83640dc34c1a07b6837d2244040cdabc6ca016c
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Tue Feb 8 14:49:31 2011 +0100

    Add GtkWidget::captured-event signal
    
    https://bugzilla.gnome.org/show_bug.cgi?id=641836

 gtk/gtkmain.c          |  206 +++++++++++++++++++++++++++++++-----------------
 gtk/gtkwidget.c        |   62 ++++++++++++++
 gtk/gtkwidget.h        |    3 +-
 gtk/gtkwidgetprivate.h |    3 +
 4 files changed, 201 insertions(+), 73 deletions(-)
---
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index e8a15b4..f6ee681 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -132,6 +132,9 @@
 
 #include "a11y/gailutil.h"
 
+static gboolean gtk_propagate_captured_event (GtkWidget *widget,
+                                              GdkEvent  *event);
+
 /* Private type definitions
  */
 typedef struct _GtkKeySnooperData        GtkKeySnooperData;
@@ -1628,14 +1631,16 @@ 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);
+      if (!gtk_propagate_captured_event (grab_widget, event))
+        gtk_propagate_event (grab_widget, event);
       break;
 
     case GDK_KEY_PRESS:
@@ -1684,7 +1689,8 @@ gtk_main_do_event (GdkEvent *event)
     case GDK_BUTTON_RELEASE:
     case GDK_PROXIMITY_IN:
     case GDK_PROXIMITY_OUT:
-      gtk_propagate_event (grab_widget, event);
+      if (!gtk_propagate_captured_event (grab_widget, event))
+        gtk_propagate_event (grab_widget, event);
       break;
 
     case GDK_ENTER_NOTIFY:
@@ -1692,7 +1698,8 @@ gtk_main_do_event (GdkEvent *event)
                                      gdk_event_get_device (event),
                                      event->any.window);
       if (gtk_widget_is_sensitive (grab_widget))
-        gtk_widget_event (grab_widget, event);
+        if (!_gtk_widget_captured_event (grab_widget, event))
+          gtk_widget_event (grab_widget, event);
       break;
 
     case GDK_LEAVE_NOTIFY:
@@ -1700,7 +1707,8 @@ gtk_main_do_event (GdkEvent *event)
                                      gdk_event_get_device (event),
                                      NULL);
       if (gtk_widget_is_sensitive (grab_widget))
-        gtk_widget_event (grab_widget, event);
+        if (!_gtk_widget_captured_event (grab_widget, event))
+          gtk_widget_event (grab_widget, event);
       break;
 
     case GDK_DRAG_STATUS:
@@ -2329,45 +2337,87 @@ 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)
 {
-  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);
+
+      widget = tmp;
 
-  handled_event = FALSE;
+      if (handled_event || !widget)
+        break;
+    }
+
+  return handled_event;
+}
+
+static gboolean
+propagate_event_down (GtkWidget *widget,
+                      GdkEvent  *event)
+{
+  gint handled_event = FALSE;
+  GList *widgets = NULL;
+  GList *l;
 
-  g_object_ref (widget);
+  widgets = g_list_prepend (widgets, g_object_ref (widget));
+  while (TRUE)
+    {
+      widget = gtk_widget_get_parent (widget);
+      if (!widget)
+        break;
+
+      widgets = g_list_prepend (widgets, g_object_ref (widget));
+    }
 
-  if ((event->type == GDK_KEY_PRESS) ||
-      (event->type == GDK_KEY_RELEASE))
+  for (l = widgets; l && !handled_event; l = g_list_next (l))
+    {
+      widget = (GtkWidget *)l->data;
+
+      if (!gtk_widget_is_sensitive (widget))
+        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)
+{
+  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
@@ -2379,11 +2429,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)
             {
@@ -2391,45 +2442,56 @@ 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);
+  /* Other events get propagated up/down the widget tree */
+  return captured ? propagate_event_down (widget, event) : propagate_event_up (widget, event);
+}
 
-          tmp = gtk_widget_get_parent (widget);
-          g_object_unref (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)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (event != NULL);
 
-          widget = tmp;
+  propagate_event (widget, event, FALSE);
+}
 
-          if (!handled_event && widget)
-            g_object_ref (widget);
-          else
-            break;
-        }
-    }
-  else
-    g_object_unref (widget);
+static gboolean
+gtk_propagate_captured_event (GtkWidget *widget,
+                              GdkEvent  *event)
+{
+  return propagate_event (widget, event, TRUE);
 }
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 0e4d198..7171791 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -484,6 +484,7 @@ enum {
   QUERY_TOOLTIP,
   DRAG_FAILED,
   STYLE_UPDATED,
+  CAPTURED_EVENT,
   LAST_SIGNAL
 };
 
@@ -705,6 +706,7 @@ static void gtk_widget_set_device_enabled_internal (GtkWidget *widget,
                                                     GdkDevice *device,
                                                     gboolean   recurse,
                                                     gboolean   enabled);
+static gboolean event_window_is_still_viewable (GdkEvent *event);
 
 /* --- variables --- */
 static gpointer         gtk_widget_parent_class = NULL;
@@ -1798,6 +1800,9 @@ gtk_widget_class_init (GtkWidgetClass *klass)
    * #GtkWidget::key-press-event) and finally a generic
    * #GtkWidget::event-after signal.
    *
+   * An event can be captured before ::event signal is emitted by connecting to
+   * ::captured-event event signal.
+   *
    * Returns: %TRUE to stop other handlers from being invoked for the event
    * and to cancel the emission of the second specific ::event signal.
    *   %FALSE to propagate the event further and to allow the emission of
@@ -1835,6 +1840,33 @@ gtk_widget_class_init (GtkWidgetClass *klass)
 		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 
   /**
+   * GtkWidget::captured-event:
+   * @widget: the object which received the signal.
+   * @event: the #GdkEvent which triggered this signal
+   *
+   * The ::captured-event signal is emitted before the ::event signal to
+   * allow capturing an event before the specialized events are emitted.
+   * The event is propagated starting from the top-level container to
+   * the widget that received the event going down the hierarchy.
+   *
+   * Returns: %TRUE to stop other handlers from being invoked for the event
+   * and to cancel the emission of the ::event signal.
+   *   %FALSE to propagate the event further and to allow the emission of
+   *   the ::event signal.
+   *
+   * Since: 3.2
+   */
+  widget_signals[CAPTURED_EVENT] =
+    g_signal_new (I_("captured-event"),
+		  G_TYPE_FROM_CLASS (klass),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (GtkWidgetClass, captured_event),
+		  _gtk_boolean_handled_accumulator, NULL,
+		  _gtk_marshal_BOOLEAN__BOXED,
+		  G_TYPE_BOOLEAN, 1,
+		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+  /**
    * GtkWidget::button-press-event:
    * @widget: the object which received the signal.
    * @event: (type Gdk.EventButton): the #GdkEventButton which triggered
@@ -5870,6 +5902,36 @@ gtk_widget_event (GtkWidget *widget,
   return gtk_widget_event_internal (widget, event);
 }
 
+gboolean
+_gtk_widget_captured_event (GtkWidget *widget,
+                            GdkEvent  *event)
+{
+  gboolean return_val = FALSE;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
+  g_return_val_if_fail (WIDGET_REALIZED_FOR_EVENT (widget, event), TRUE);
+
+  if (event->type == GDK_EXPOSE)
+    {
+      g_warning ("Events of type GDK_EXPOSE cannot be synthesized. To get "
+		 "the same effect, call gdk_window_invalidate_rect/region(), "
+		 "followed by gdk_window_process_updates().");
+      return TRUE;
+    }
+
+  if (!event_window_is_still_viewable (event))
+    return TRUE;
+
+  g_object_ref (widget);
+
+  g_signal_emit (widget, widget_signals[CAPTURED_EVENT], 0, event, &return_val);
+  return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event);
+
+  g_object_unref (widget);
+
+  return return_val;
+}
+
 /* Returns TRUE if a translation should be done */
 gboolean
 _gtk_widget_get_translation_to_window (GtkWidget      *widget,
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 76d192c..1f7b15a 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -425,6 +425,8 @@ struct _GtkWidgetClass
 
   void         (* style_updated)          (GtkWidget *widget);
 
+  void         (* captured_event)         (GtkWidget *widget,
+                                           GdkEvent  *event);
   /*< private >*/
 
   GtkWidgetClassPrivate *priv;
@@ -436,7 +438,6 @@ struct _GtkWidgetClass
   void (*_gtk_reserved5) (void);
   void (*_gtk_reserved6) (void);
   void (*_gtk_reserved7) (void);
-  void (*_gtk_reserved8) (void);
 };
 
 struct _GtkWidgetAuxInfo
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 241a570..cbe659e 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -168,6 +168,9 @@ void              _gtk_widget_set_style                    (GtkWidget *widget,
                                                             GtkStyle  *style);
 
 
+gboolean          _gtk_widget_captured_event               (GtkWidget *widget,
+                                                            GdkEvent  *event);
+
 G_END_DECLS
 
 #endif /* __GTK_WIDGET_PRIVATE_H__ */



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