Transfer the Mac SDK bockbuild profiles & resources inside the Mono repository.
[mono.git] / bockbuild / mac-sdk / patches / gtk / 0009-gtk-Add-a-way-to-do-event-capture.patch
1 From a484a0af1fc0ab98694c13970a308edeb52f39a6 Mon Sep 17 00:00:00 2001
2 From: Carlos Garcia Campos <cgarcia@igalia.com>
3 Date: Tue, 8 Feb 2011 14:49:31 +0100
4 Subject: [PATCH 09/68] gtk: Add a way to do event capture
5
6 This patch adds a capture phase to GTK+'s event propagation
7 model. Events are first propagated from the toplevel (or the
8 grab widget, if a grab is in place) down to the target widget
9  and then back up. The second phase is using the existing
10 ::event signal, the new capture phase is using a private
11 API instead of a public signal for now.
12
13 This mechanism can be used in many places where we currently
14 have to prevent child widgets from getting events by putting
15 an input-only window over them. It will also be used to implement
16 kinetic scrolling in subsequent patches.
17
18 http://bugzilla.gnome.org/show_bug.cgi?id=641836
19
20 We automatically request more motion events in behalf of
21 the original widget if it listens to motion hints. So
22 the capturing widget doesn't need to handle such
23 implementation details.
24
25 We are not making event capture part of the public API for 3.4,
26 which is why there is no ::captured-event signal.
27
28 Conflicts:
29
30         gtk/gtkmain.c
31         gtk/gtkwidget.c
32         gtk/gtkwidgetprivate.h
33 ---
34  gtk/gtkmain.c    |  268 +++++++++++++++++++++++++++++++++++-------------------
35  gtk/gtkprivate.h |   14 +++
36  gtk/gtkwidget.c  |   53 +++++++++++
37  3 files changed, 241 insertions(+), 94 deletions(-)
38
39 diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
40 index 56c92db..21345ed 100644
41 --- a/gtk/gtkmain.c
42 +++ b/gtk/gtkmain.c
43 @@ -1490,7 +1490,8 @@ void
44  gtk_main_do_event (GdkEvent *event)
45  {
46    GtkWidget *event_widget;
47 -  GtkWidget *grab_widget;
48 +  GtkWidget *grab_widget = NULL;
49 +  GtkWidget *topmost_widget = NULL;
50    GtkWindowGroup *window_group;
51    GdkEvent *rewritten_event = NULL;
52    GList *tmp_list;
53 @@ -1552,7 +1553,14 @@ gtk_main_do_event (GdkEvent *event)
54    if (window_group->grabs)
55      {
56        grab_widget = window_group->grabs->data;
57 -
58 +
59 +      /* Find out the topmost widget where captured event propagation
60 +       * should start, which is the widget holding the GTK+ grab
61 +       * if any, otherwise it's left NULL and events are emitted
62 +       * from the toplevel (or topmost parentless parent).
63 +       */
64 +      topmost_widget = grab_widget;
65 +
66        /* If the grab widget is an ancestor of the event widget
67         *  then we send the event to the original event widget.
68         *  This is the key to implementing modality.
69 @@ -1636,14 +1644,16 @@ gtk_main_do_event (GdkEvent *event)
70      case GDK_WINDOW_STATE:
71      case GDK_GRAB_BROKEN:
72      case GDK_DAMAGE:
73 -      gtk_widget_event (event_widget, event);
74 +      if (!_gtk_widget_captured_event (event_widget, event))
75 +        gtk_widget_event (event_widget, event);
76        break;
77
78      case GDK_SCROLL:
79      case GDK_BUTTON_PRESS:
80      case GDK_2BUTTON_PRESS:
81      case GDK_3BUTTON_PRESS:
82 -      gtk_propagate_event (grab_widget, event);
83 +      if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
84 +        gtk_propagate_event (grab_widget, event);
85        break;
86
87      case GDK_KEY_PRESS:
88 @@ -1682,19 +1692,22 @@ gtk_main_do_event (GdkEvent *event)
89      case GDK_BUTTON_RELEASE:
90      case GDK_PROXIMITY_IN:
91      case GDK_PROXIMITY_OUT:
92 -      gtk_propagate_event (grab_widget, event);
93 +      if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
94 +        gtk_propagate_event (grab_widget, event);
95        break;
96
97      case GDK_ENTER_NOTIFY:
98        GTK_PRIVATE_SET_FLAG (event_widget, GTK_HAS_POINTER);
99        _gtk_widget_set_pointer_window (event_widget, event->any.window);
100 -      if (gtk_widget_is_sensitive (grab_widget))
101 +      if (gtk_widget_is_sensitive (grab_widget) &&
102 +          !_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
103         gtk_widget_event (grab_widget, event);
104        break;
105
106      case GDK_LEAVE_NOTIFY:
107        GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_HAS_POINTER);
108 -      if (gtk_widget_is_sensitive (grab_widget))
109 +      if (gtk_widget_is_sensitive (grab_widget) &&
110 +          !_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
111         gtk_widget_event (grab_widget, event);
112        break;
113
114 @@ -2400,44 +2413,96 @@ gtk_quit_invoke_function (GtkQuitFunction *quitf)
115      }
116  }
117
118 -/**
119 - * gtk_propagate_event:
120 - * @widget: a #GtkWidget
121 - * @event: an event
122 - *
123 - * Sends an event to a widget, propagating the event to parent widgets
124 - * if the event remains unhandled. Events received by GTK+ from GDK
125 - * normally begin in gtk_main_do_event(). Depending on the type of
126 - * event, existence of modal dialogs, grabs, etc., the event may be
127 - * propagated; if so, this function is used. gtk_propagate_event()
128 - * calls gtk_widget_event() on each widget it decides to send the
129 - * event to.  So gtk_widget_event() is the lowest-level function; it
130 - * simply emits the "event" and possibly an event-specific signal on a
131 - * widget.  gtk_propagate_event() is a bit higher-level, and
132 - * gtk_main_do_event() is the highest level.
133 - *
134 - * All that said, you most likely don't want to use any of these
135 - * functions; synthesizing events is rarely needed. Consider asking on
136 - * the mailing list for better ways to achieve your goals. For
137 - * example, use gdk_window_invalidate_rect() or
138 - * gtk_widget_queue_draw() instead of making up expose events.
139 - *
140 - **/
141 -void
142 -gtk_propagate_event (GtkWidget *widget,
143 -                    GdkEvent  *event)
144 +static gboolean
145 +propagate_event_up (GtkWidget *widget,
146 +                    GdkEvent  *event,
147 +                    GtkWidget *topmost)
148  {
149 -  gint handled_event;
150 -
151 -  g_return_if_fail (GTK_IS_WIDGET (widget));
152 -  g_return_if_fail (event != NULL);
153 -
154 -  handled_event = FALSE;
155 +  gboolean handled_event = FALSE;
156
157 -  g_object_ref (widget);
158 -
159 -  if ((event->type == GDK_KEY_PRESS) ||
160 -      (event->type == GDK_KEY_RELEASE))
161 +  /* Propagate event up the widget tree so that
162 +   * parents can see the button and motion
163 +   * events of the children.
164 +   */
165 +  while (TRUE)
166 +    {
167 +      GtkWidget *tmp;
168 +
169 +      g_object_ref (widget);
170 +
171 +      /* Scroll events are special cased here because it
172 +       * feels wrong when scrolling a GtkViewport, say,
173 +       * to have children of the viewport eat the scroll
174 +       * event
175 +       */
176 +      if (!gtk_widget_is_sensitive (widget))
177 +        handled_event = event->type != GDK_SCROLL;
178 +      else
179 +        handled_event = gtk_widget_event (widget, event);
180 +
181 +      tmp = gtk_widget_get_parent (widget);
182 +      g_object_unref (widget);
183 +
184 +      if (widget == topmost)
185 +        break;
186 +
187 +      widget = tmp;
188 +
189 +      if (handled_event || !widget)
190 +        break;
191 +    }
192 +
193 +  return handled_event;
194 +}
195 +
196 +static gboolean
197 +propagate_event_down (GtkWidget *widget,
198 +                      GdkEvent  *event,
199 +                      GtkWidget *topmost)
200 +{
201 +  gint handled_event = FALSE;
202 +  GList *widgets = NULL;
203 +  GList *l;
204 +
205 +  widgets = g_list_prepend (widgets, g_object_ref (widget));
206 +  while (widget && widget != topmost)
207 +    {
208 +      widget = gtk_widget_get_parent (widget);
209 +      if (!widget)
210 +        break;
211 +
212 +      widgets = g_list_prepend (widgets, g_object_ref (widget));
213 +
214 +      if (widget == topmost)
215 +        break;
216 +    }
217 +
218 +  for (l = widgets; l && !handled_event; l = g_list_next (l))
219 +    {
220 +      widget = (GtkWidget *)l->data;
221 +
222 +      if (!gtk_widget_is_sensitive (widget))
223 +        handled_event = TRUE;
224 +      else
225 +        handled_event = _gtk_widget_captured_event (widget, event);
226 +    }
227 +  g_list_free_full (widgets, (GDestroyNotify)g_object_unref);
228 +
229 +  return handled_event;
230 +}
231 +
232 +static gboolean
233 +propagate_event (GtkWidget *widget,
234 +                 GdkEvent  *event,
235 +                 gboolean   captured,
236 +                 GtkWidget *topmost)
237 +{
238 +  gboolean handled_event = FALSE;
239 +  gboolean (* propagate_func) (GtkWidget *widget, GdkEvent  *event);
240 +
241 +  propagate_func = captured ? _gtk_widget_captured_event : gtk_widget_event;
242 +
243 +  if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
244      {
245        /* Only send key events within Window widgets to the Window
246         *  The Window widget will in turn pass the
247 @@ -2448,60 +2513,75 @@ gtk_propagate_event (GtkWidget *widget,
248
249        window = gtk_widget_get_toplevel (widget);
250        if (GTK_IS_WINDOW (window))
251 -       {
252 -         /* If there is a grab within the window, give the grab widget
253 -          * a first crack at the key event
254 -          */
255 -         if (widget != window && gtk_widget_has_grab (widget))
256 -           handled_event = gtk_widget_event (widget, event);
257 -
258 -         if (!handled_event)
259 -           {
260 -             window = gtk_widget_get_toplevel (widget);
261 -             if (GTK_IS_WINDOW (window))
262 -               {
263 -                 if (gtk_widget_is_sensitive (window))
264 -                   gtk_widget_event (window, event);
265 -               }
266 -           }
267 -
268 -         handled_event = TRUE; /* don't send to widget */
269 -       }
270 +        {
271 +          g_object_ref (widget);
272 +          /* If there is a grab within the window, give the grab widget
273 +           * a first crack at the key event
274 +           */
275 +          if (widget != window && gtk_widget_has_grab (widget))
276 +            handled_event = propagate_func (widget, event);
277 +
278 +          if (!handled_event)
279 +            {
280 +              window = gtk_widget_get_toplevel (widget);
281 +              if (GTK_IS_WINDOW (window))
282 +                {
283 +                  if (gtk_widget_is_sensitive (window))
284 +                    handled_event = propagate_func (window, event);
285 +                }
286 +            }
287 +
288 +          g_object_unref (widget);
289 +          return handled_event;
290 +        }
291      }
292 -
293 -  /* Other events get propagated up the widget tree
294 -   *  so that parents can see the button and motion
295 -   *  events of the children.
296 -   */
297 -  if (!handled_event)
298 -    {
299 -      while (TRUE)
300 -       {
301 -         GtkWidget *tmp;
302
303 -         /* Scroll events are special cased here because it
304 -          * feels wrong when scrolling a GtkViewport, say,
305 -          * to have children of the viewport eat the scroll
306 -          * event
307 -          */
308 -         if (!gtk_widget_is_sensitive (widget))
309 -           handled_event = event->type != GDK_SCROLL;
310 -         else
311 -           handled_event = gtk_widget_event (widget, event);
312 -
313 -         tmp = widget->parent;
314 -         g_object_unref (widget);
315 +  /* Other events get propagated up/down the widget tree */
316 +  return captured ?
317 +    propagate_event_down (widget, event, topmost) :
318 +    propagate_event_up (widget, event, topmost);
319 +}
320
321 -         widget = tmp;
322 -
323 -         if (!handled_event && widget)
324 -           g_object_ref (widget);
325 -         else
326 -           break;
327 -       }
328 -    }
329 -  else
330 -    g_object_unref (widget);
331 +/**
332 + * gtk_propagate_event:
333 + * @widget: a #GtkWidget
334 + * @event: an event
335 + *
336 + * Sends an event to a widget, propagating the event to parent widgets
337 + * if the event remains unhandled.
338 + *
339 + * Events received by GTK+ from GDK normally begin in gtk_main_do_event().
340 + * Depending on the type of event, existence of modal dialogs, grabs, etc.,
341 + * the event may be propagated; if so, this function is used.
342 + *
343 + * gtk_propagate_event() calls gtk_widget_event() on each widget it
344 + * decides to send the event to. So gtk_widget_event() is the lowest-level
345 + * function; it simply emits the #GtkWidget::event and possibly an
346 + * event-specific signal on a widget. gtk_propagate_event() is a bit
347 + * higher-level, and gtk_main_do_event() is the highest level.
348 + *
349 + * All that said, you most likely don't want to use any of these
350 + * functions; synthesizing events is rarely needed. There are almost
351 + * certainly better ways to achieve your goals. For example, use
352 + * gdk_window_invalidate_rect() or gtk_widget_queue_draw() instead
353 + * of making up expose events.
354 + */
355 +void
356 +gtk_propagate_event (GtkWidget *widget,
357 +                     GdkEvent  *event)
358 +{
359 +  g_return_if_fail (GTK_IS_WIDGET (widget));
360 +  g_return_if_fail (event != NULL);
361 +
362 +  propagate_event (widget, event, FALSE, NULL);
363 +}
364 +
365 +gboolean
366 +_gtk_propagate_captured_event (GtkWidget *widget,
367 +                               GdkEvent  *event,
368 +                               GtkWidget *topmost)
369 +{
370 +  return propagate_event (widget, event, TRUE, topmost);
371  }
372
373  #if 0
374 diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
375 index 6386c32..3865a67 100644
376 --- a/gtk/gtkprivate.h
377 +++ b/gtk/gtkprivate.h
378 @@ -152,6 +152,20 @@ gboolean _gtk_translate_keyboard_accel_state     (GdkKeymap       *keymap,
379                                                    GdkModifierType *consumed_modifiers);
380
381
382 +gboolean        _gtk_propagate_captured_event  (GtkWidget       *widget,
383 +                                                GdkEvent        *event,
384 +                                                GtkWidget       *topmost);
385 +
386 +typedef gboolean (*GtkCapturedEventHandler) (GtkWidget *widget, GdkEvent *event);
387 +
388 +void              _gtk_widget_set_captured_event_handler (GtkWidget               *widget,
389 +                                                          GtkCapturedEventHandler  handler);
390 +
391 +gboolean          _gtk_widget_captured_event               (GtkWidget *widget,
392 +                                                            GdkEvent  *event);
393 +
394 +
395 +
396  G_END_DECLS
397
398  #endif /* __GTK_PRIVATE_H__ */
399 diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
400 index 1d1f6bb..8e38ee1 100644
401 --- a/gtk/gtkwidget.c
402 +++ b/gtk/gtkwidget.c
403 @@ -351,6 +351,7 @@ static void gtk_widget_set_usize_internal (GtkWidget *widget,
404                                            gint       height);
405  static void gtk_widget_get_draw_rectangle (GtkWidget    *widget,
406                                            GdkRectangle *rect);
407 +static gboolean event_window_is_still_viewable (GdkEvent *event);
408
409
410  /* --- variables --- */
411 @@ -4807,6 +4808,58 @@ gtk_widget_event (GtkWidget *widget,
412    return gtk_widget_event_internal (widget, event);
413  }
414
415 +void
416 +_gtk_widget_set_captured_event_handler (GtkWidget               *widget,
417 +                                        GtkCapturedEventHandler  callback)
418 +{
419 +  g_object_set_data (G_OBJECT (widget), "captured-event-handler", callback);
420 +}
421 +
422 +gboolean
423 +_gtk_widget_captured_event (GtkWidget *widget,
424 +                            GdkEvent  *event)
425 +{
426 +  gboolean return_val = FALSE;
427 +  GtkCapturedEventHandler handler;
428 +
429 +  g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
430 +  g_return_val_if_fail (WIDGET_REALIZED_FOR_EVENT (widget, event), TRUE);
431 +
432 +  if (event->type == GDK_EXPOSE)
433 +    {
434 +      g_warning ("Events of type GDK_EXPOSE cannot be synthesized. To get "
435 +                "the same effect, call gdk_window_invalidate_rect/region(), "
436 +                "followed by gdk_window_process_updates().");
437 +      return TRUE;
438 +    }
439 +
440 +  if (!event_window_is_still_viewable (event))
441 +    return TRUE;
442 +
443 +  handler = g_object_get_data (G_OBJECT (widget), "captured-event-handler");
444 +  if (!handler)
445 +    return FALSE;
446 +
447 +  g_object_ref (widget);
448 +
449 +  return_val = handler (widget, event);
450 +  return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event);
451 +
452 +  /* The widget that was originally to receive the event
453 +   * handles motion hints, but the capturing widget might
454 +   * not, so ensure we get further motion events.
455 +   */
456 +  if (return_val &&
457 +      event->type == GDK_MOTION_NOTIFY &&
458 +      event->motion.is_hint &&
459 +      (gdk_window_get_events (event->any.window) &
460 +       GDK_POINTER_MOTION_HINT_MASK) != 0)
461 +    gdk_event_request_motions (&event->motion);
462 +
463 +  g_object_unref (widget);
464 +
465 +  return return_val;
466 +}
467
468  /**
469   * gtk_widget_send_expose:
470 --
471 1.7.10.2 (Apple Git-33)