Bug 56070 – Can't click button after setting it sensitive.
2008-07-31 Cody Russell <bratsche@gnome.org> Bug 56070 – Can't click button after setting it sensitive. * gtk/gtkwidget.[ch] * gtk/gtkwindow.c * gtk/gtkmain.c * gtk/gtkbutton.c * gtk/gtkprivate.h * gdk/gdkevents.h: Synthesize crossing events events where necessary. * gtk/tests/crossingevents.c: Add unit tests for crossing events. Big thanks to Ed Catmur, Matthias Clasen, and everyone else who has worked on and helped out with this. svn path=/trunk/; revision=20924
This commit is contained in:
		
				
					committed by
					
						
						Cody Russell
					
				
			
			
				
	
			
			
			
						parent
						
							e9d978dff9
						
					
				
				
					commit
					4e3c97b3f2
				
			
							
								
								
									
										16
									
								
								ChangeLog
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								ChangeLog
									
									
									
									
									
								
							@ -1,3 +1,19 @@
 | 
			
		||||
2008-07-31  Cody Russell  <bratsche@gnome.org>
 | 
			
		||||
 | 
			
		||||
	Bug 56070 – Can't click button after setting it sensitive.
 | 
			
		||||
 | 
			
		||||
	* gtk/gtkwidget.[ch]
 | 
			
		||||
	* gtk/gtkwindow.c
 | 
			
		||||
	* gtk/gtkmain.c
 | 
			
		||||
	* gtk/gtkbutton.c
 | 
			
		||||
	* gtk/gtkprivate.h
 | 
			
		||||
	* gdk/gdkevents.h: Synthesize crossing events events where necessary.
 | 
			
		||||
 | 
			
		||||
	* gtk/tests/crossingevents.c: Add unit tests for crossing events.
 | 
			
		||||
 | 
			
		||||
	Big thanks to Ed Catmur, Matthias Clasen, and everyone else who
 | 
			
		||||
	has worked on and helped out with this.
 | 
			
		||||
 | 
			
		||||
2008-07-31  Matthias Clasen  <mclasen@redhat.com>
 | 
			
		||||
 | 
			
		||||
	Bug 424207 – printing hangs on unreachable cups server
 | 
			
		||||
 | 
			
		||||
@ -259,8 +259,11 @@ Generated when the pointer enters or leaves a window.
 | 
			
		||||
@y: the y coordinate of the pointer relative to the window.
 | 
			
		||||
@x_root: the x coordinate of the pointer relative to the root of the screen.
 | 
			
		||||
@y_root: the y coordinate of the pointer relative to the root of the screen.
 | 
			
		||||
@mode: the crossing mode (%GDK_CROSSING_NORMAL, %GDK_CROSSING_GRAB or
 | 
			
		||||
  %GDK_CROSSING_UNGRAB).
 | 
			
		||||
@mode: the crossing mode (%GDK_CROSSING_NORMAL, %GDK_CROSSING_GRAB, 
 | 
			
		||||
  %GDK_CROSSING_UNGRAB, %GDK_CROSSING_GTK_GRAB, %GDK_CROSSING_GTK_UNGRAB or
 | 
			
		||||
  %GDK_CROSSING_STATE_CHANGED).  %GDK_CROSSING_GTK_GRAB, %GDK_CROSSING_GTK_UNGRAB,
 | 
			
		||||
  and %GDK_CROSSING_STATE_CHANGED were added in 2.14 and are always synthesized,
 | 
			
		||||
  never native.
 | 
			
		||||
@detail: the kind of crossing that happened (%GDK_NOTIFY_INFERIOR,
 | 
			
		||||
  %GDK_NOTIFY_ANCESTOR, %GDK_NOTIFY_VIRTUAL, %GDK_NOTIFY_NONLINEAR or
 | 
			
		||||
  %GDK_NOTIFY_NONLINEAR_VIRTUAL).
 | 
			
		||||
@ -474,6 +477,10 @@ Specifies the crossing mode for #GdkEventCrossing.
 | 
			
		||||
@GDK_CROSSING_NORMAL: crossing because of pointer motion.
 | 
			
		||||
@GDK_CROSSING_GRAB: crossing because a grab is activated.
 | 
			
		||||
@GDK_CROSSING_UNGRAB: crossing because a grab is deactivated.
 | 
			
		||||
@GDK_CROSSING_GTK_GRAB: crossing because a GTK+ grab is activated.
 | 
			
		||||
@GDK_CROSSING_GTK_UNGRAB: crossing because a GTK+ grab is deactivated.
 | 
			
		||||
@GDK_CROSSING_STATE_CHANGED: crossing because a GTK+ widget changed state (e.g.
 | 
			
		||||
   sensitivity).
 | 
			
		||||
 | 
			
		||||
<!-- ##### ENUM GdkNotifyType ##### -->
 | 
			
		||||
<para>
 | 
			
		||||
 | 
			
		||||
@ -225,7 +225,10 @@ typedef enum
 | 
			
		||||
{
 | 
			
		||||
  GDK_CROSSING_NORMAL,
 | 
			
		||||
  GDK_CROSSING_GRAB,
 | 
			
		||||
  GDK_CROSSING_UNGRAB
 | 
			
		||||
  GDK_CROSSING_UNGRAB,
 | 
			
		||||
  GDK_CROSSING_GTK_GRAB,
 | 
			
		||||
  GDK_CROSSING_GTK_UNGRAB,
 | 
			
		||||
  GDK_CROSSING_STATE_CHANGED
 | 
			
		||||
} GdkCrossingMode;
 | 
			
		||||
 | 
			
		||||
typedef enum
 | 
			
		||||
 | 
			
		||||
@ -1457,7 +1457,8 @@ gtk_button_leave_notify (GtkWidget        *widget,
 | 
			
		||||
  event_widget = gtk_get_event_widget ((GdkEvent*) event);
 | 
			
		||||
 | 
			
		||||
  if ((event_widget == widget) &&
 | 
			
		||||
      (event->detail != GDK_NOTIFY_INFERIOR))
 | 
			
		||||
      (event->detail != GDK_NOTIFY_INFERIOR) &&
 | 
			
		||||
      (GTK_WIDGET_SENSITIVE (event_widget)))
 | 
			
		||||
    {
 | 
			
		||||
      button->in_button = FALSE;
 | 
			
		||||
      gtk_button_leave (button);
 | 
			
		||||
 | 
			
		||||
@ -1569,25 +1569,15 @@ gtk_main_do_event (GdkEvent *event)
 | 
			
		||||
      break;
 | 
			
		||||
      
 | 
			
		||||
    case GDK_ENTER_NOTIFY:
 | 
			
		||||
      GTK_PRIVATE_SET_FLAG (event_widget, GTK_HAS_POINTER);
 | 
			
		||||
      _gtk_widget_set_pointer_window (event_widget, event->any.window);
 | 
			
		||||
      if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
 | 
			
		||||
	{
 | 
			
		||||
	  g_object_ref (event_widget);
 | 
			
		||||
	  
 | 
			
		||||
	gtk_widget_event (grab_widget, event);
 | 
			
		||||
	  if (event_widget == grab_widget)
 | 
			
		||||
	    GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
 | 
			
		||||
	  
 | 
			
		||||
	  g_object_unref (event_widget);
 | 
			
		||||
	}
 | 
			
		||||
      break;
 | 
			
		||||
      
 | 
			
		||||
    case GDK_LEAVE_NOTIFY:
 | 
			
		||||
      if (GTK_WIDGET_LEAVE_PENDING (event_widget))
 | 
			
		||||
	{
 | 
			
		||||
	  GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_LEAVE_PENDING);
 | 
			
		||||
	  gtk_widget_event (event_widget, event);
 | 
			
		||||
	}
 | 
			
		||||
      else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
 | 
			
		||||
      GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_HAS_POINTER);
 | 
			
		||||
      if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
 | 
			
		||||
	gtk_widget_event (grab_widget, event);
 | 
			
		||||
      break;
 | 
			
		||||
      
 | 
			
		||||
@ -1660,6 +1650,7 @@ typedef struct
 | 
			
		||||
  GtkWidget *new_grab_widget;
 | 
			
		||||
  gboolean   was_grabbed;
 | 
			
		||||
  gboolean   is_grabbed;
 | 
			
		||||
  gboolean   from_grab;
 | 
			
		||||
} GrabNotifyInfo;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@ -1682,12 +1673,30 @@ gtk_grab_notify_foreach (GtkWidget *child,
 | 
			
		||||
 | 
			
		||||
  g_object_ref (child);
 | 
			
		||||
 | 
			
		||||
  if (was_shadowed != is_shadowed)
 | 
			
		||||
    _gtk_widget_grab_notify (child, was_shadowed);
 | 
			
		||||
  
 | 
			
		||||
  if ((was_shadowed || is_shadowed) && GTK_IS_CONTAINER (child))
 | 
			
		||||
    gtk_container_forall (GTK_CONTAINER (child), gtk_grab_notify_foreach, info);
 | 
			
		||||
  
 | 
			
		||||
  if (is_shadowed)
 | 
			
		||||
    {
 | 
			
		||||
      GTK_PRIVATE_SET_FLAG (child, GTK_SHADOWED);
 | 
			
		||||
      if (!was_shadowed && GTK_WIDGET_HAS_POINTER (child)
 | 
			
		||||
	  && GTK_WIDGET_IS_SENSITIVE (child))
 | 
			
		||||
	_gtk_widget_synthesize_crossing (child, info->new_grab_widget,
 | 
			
		||||
					 GDK_CROSSING_GTK_GRAB);
 | 
			
		||||
    }
 | 
			
		||||
  else
 | 
			
		||||
    {
 | 
			
		||||
      GTK_PRIVATE_UNSET_FLAG (child, GTK_SHADOWED);
 | 
			
		||||
      if (was_shadowed && GTK_WIDGET_HAS_POINTER (child)
 | 
			
		||||
	  && GTK_WIDGET_IS_SENSITIVE (child))
 | 
			
		||||
	_gtk_widget_synthesize_crossing (info->old_grab_widget, child,
 | 
			
		||||
					 info->from_grab ? GDK_CROSSING_GTK_GRAB
 | 
			
		||||
					 : GDK_CROSSING_GTK_UNGRAB);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  if (was_shadowed != is_shadowed)
 | 
			
		||||
    _gtk_widget_grab_notify (child, was_shadowed);
 | 
			
		||||
  
 | 
			
		||||
  g_object_unref (child);
 | 
			
		||||
  
 | 
			
		||||
  info->was_grabbed = was_grabbed;
 | 
			
		||||
@ -1697,7 +1706,8 @@ gtk_grab_notify_foreach (GtkWidget *child,
 | 
			
		||||
static void
 | 
			
		||||
gtk_grab_notify (GtkWindowGroup *group,
 | 
			
		||||
		 GtkWidget      *old_grab_widget,
 | 
			
		||||
		 GtkWidget      *new_grab_widget)
 | 
			
		||||
		 GtkWidget      *new_grab_widget,
 | 
			
		||||
		 gboolean        from_grab)
 | 
			
		||||
{
 | 
			
		||||
  GList *toplevels;
 | 
			
		||||
  GrabNotifyInfo info;
 | 
			
		||||
@ -1707,6 +1717,7 @@ gtk_grab_notify (GtkWindowGroup *group,
 | 
			
		||||
 | 
			
		||||
  info.old_grab_widget = old_grab_widget;
 | 
			
		||||
  info.new_grab_widget = new_grab_widget;
 | 
			
		||||
  info.from_grab = from_grab;
 | 
			
		||||
 | 
			
		||||
  g_object_ref (group);
 | 
			
		||||
 | 
			
		||||
@ -1751,7 +1762,7 @@ gtk_grab_add (GtkWidget *widget)
 | 
			
		||||
      g_object_ref (widget);
 | 
			
		||||
      group->grabs = g_slist_prepend (group->grabs, widget);
 | 
			
		||||
 | 
			
		||||
      gtk_grab_notify (group, old_grab_widget, widget);
 | 
			
		||||
      gtk_grab_notify (group, old_grab_widget, widget, TRUE);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1787,7 +1798,7 @@ gtk_grab_remove (GtkWidget *widget)
 | 
			
		||||
      else
 | 
			
		||||
	new_grab_widget = NULL;
 | 
			
		||||
 | 
			
		||||
      gtk_grab_notify (group, widget, new_grab_widget);
 | 
			
		||||
      gtk_grab_notify (group, widget, new_grab_widget, FALSE);
 | 
			
		||||
      
 | 
			
		||||
      g_object_unref (widget);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,8 @@ typedef enum
 | 
			
		||||
{
 | 
			
		||||
  PRIVATE_GTK_USER_STYLE	= 1 <<  0,
 | 
			
		||||
  PRIVATE_GTK_RESIZE_PENDING	= 1 <<  2,
 | 
			
		||||
  PRIVATE_GTK_LEAVE_PENDING	= 1 <<  4,
 | 
			
		||||
  PRIVATE_GTK_HAS_POINTER	= 1 <<  3,   /* If the pointer is above a window belonging to the widget */
 | 
			
		||||
  PRIVATE_GTK_SHADOWED		= 1 <<  4,   /* If there is a grab in effect shadowing the widget */
 | 
			
		||||
  PRIVATE_GTK_HAS_SHAPE_MASK	= 1 <<  5,
 | 
			
		||||
  PRIVATE_GTK_IN_REPARENT       = 1 <<  6,
 | 
			
		||||
  PRIVATE_GTK_DIRECTION_SET     = 1 <<  7,   /* If the reading direction is not DIR_NONE */
 | 
			
		||||
@ -54,7 +55,8 @@ typedef enum
 | 
			
		||||
#define GTK_PRIVATE_FLAGS(wid)            (GTK_WIDGET (wid)->private_flags)
 | 
			
		||||
#define GTK_WIDGET_USER_STYLE(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_USER_STYLE) != 0)
 | 
			
		||||
#define GTK_CONTAINER_RESIZE_PENDING(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_RESIZE_PENDING) != 0)
 | 
			
		||||
#define GTK_WIDGET_LEAVE_PENDING(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_LEAVE_PENDING) != 0)
 | 
			
		||||
#define GTK_WIDGET_HAS_POINTER(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_HAS_POINTER) != 0)
 | 
			
		||||
#define GTK_WIDGET_SHADOWED(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_SHADOWED) != 0)
 | 
			
		||||
#define GTK_WIDGET_HAS_SHAPE_MASK(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_HAS_SHAPE_MASK) != 0)
 | 
			
		||||
#define GTK_WIDGET_IN_REPARENT(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_IN_REPARENT) != 0)
 | 
			
		||||
#define GTK_WIDGET_DIRECTION_SET(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_DIRECTION_SET) != 0)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										288
									
								
								gtk/gtkwidget.c
									
									
									
									
									
								
							
							
						
						
									
										288
									
								
								gtk/gtkwidget.c
									
									
									
									
									
								
							@ -298,6 +298,7 @@ static GQuark		quark_accel_closures = 0;
 | 
			
		||||
static GQuark		quark_event_mask = 0;
 | 
			
		||||
static GQuark		quark_extension_event_mode = 0;
 | 
			
		||||
static GQuark		quark_parent_window = 0;
 | 
			
		||||
static GQuark		quark_pointer_window = 0;
 | 
			
		||||
static GQuark		quark_shape_info = 0;
 | 
			
		||||
static GQuark		quark_input_shape_info = 0;
 | 
			
		||||
static GQuark		quark_colormap = 0;
 | 
			
		||||
@ -385,6 +386,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
 | 
			
		||||
  quark_event_mask = g_quark_from_static_string ("gtk-event-mask");
 | 
			
		||||
  quark_extension_event_mode = g_quark_from_static_string ("gtk-extension-event-mode");
 | 
			
		||||
  quark_parent_window = g_quark_from_static_string ("gtk-parent-window");
 | 
			
		||||
  quark_pointer_window = g_quark_from_static_string ("gtk-pointer-window");
 | 
			
		||||
  quark_shape_info = g_quark_from_static_string ("gtk-shape-info");
 | 
			
		||||
  quark_input_shape_info = g_quark_from_static_string ("gtk-input-shape-info");
 | 
			
		||||
  quark_colormap = g_quark_from_static_string ("gtk-colormap");
 | 
			
		||||
@ -8053,6 +8055,282 @@ _gtk_widget_peek_colormap (void)
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * _gtk_widget_set_pointer_window:
 | 
			
		||||
 * @widget: a #GtkWidget.
 | 
			
		||||
 * @pointer_window: the new pointer window.
 | 
			
		||||
 *  
 | 
			
		||||
 * Sets pointer window for @widget.  Does not ref @pointer_window.
 | 
			
		||||
 * Actually stores it on the #GdkScreen, but you don't need to know that.
 | 
			
		||||
 **/
 | 
			
		||||
void
 | 
			
		||||
_gtk_widget_set_pointer_window   (GtkWidget *widget,
 | 
			
		||||
				  GdkWindow *pointer_window)
 | 
			
		||||
{
 | 
			
		||||
  g_return_if_fail (GTK_IS_WIDGET (widget));
 | 
			
		||||
 | 
			
		||||
  GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (widget->window));
 | 
			
		||||
  g_object_set_qdata (G_OBJECT (screen), quark_pointer_window, pointer_window);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * _gtk_widget_get_pointer_window:
 | 
			
		||||
 * @widget: a #GtkWidget.
 | 
			
		||||
 *
 | 
			
		||||
 * Return value: the pointer window set on the #GdkScreen @widget is attached
 | 
			
		||||
 * to, or %NULL.
 | 
			
		||||
 **/
 | 
			
		||||
GdkWindow *
 | 
			
		||||
_gtk_widget_get_pointer_window   (GtkWidget *widget)
 | 
			
		||||
{
 | 
			
		||||
  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
 | 
			
		||||
 | 
			
		||||
  GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (widget->window));
 | 
			
		||||
  return g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
synth_crossing (GtkWidget      *widget,
 | 
			
		||||
		GdkEventType    type,
 | 
			
		||||
		GdkWindow      *window,
 | 
			
		||||
		GdkCrossingMode mode,
 | 
			
		||||
		GdkNotifyType   detail)
 | 
			
		||||
{
 | 
			
		||||
  GdkEvent *event;
 | 
			
		||||
  
 | 
			
		||||
  event = gdk_event_new (type);
 | 
			
		||||
 | 
			
		||||
  event->crossing.window = g_object_ref (window);
 | 
			
		||||
  event->crossing.send_event = TRUE;
 | 
			
		||||
  event->crossing.subwindow = g_object_ref (window);
 | 
			
		||||
  event->crossing.time = GDK_CURRENT_TIME;
 | 
			
		||||
  event->crossing.x = event->crossing.y = 0;
 | 
			
		||||
  event->crossing.x_root = event->crossing.y_root = 0;
 | 
			
		||||
  event->crossing.mode = mode;
 | 
			
		||||
  event->crossing.detail = detail;
 | 
			
		||||
  event->crossing.focus = FALSE;
 | 
			
		||||
  event->crossing.state = 0;
 | 
			
		||||
 | 
			
		||||
  if (!widget)
 | 
			
		||||
    widget = gtk_get_event_widget (event);
 | 
			
		||||
 | 
			
		||||
  if (widget)
 | 
			
		||||
    gtk_widget_event_internal (widget, event);
 | 
			
		||||
 | 
			
		||||
  gdk_event_free (event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * _gtk_widget_is_pointer_widget:
 | 
			
		||||
 * @widget: a #GtkWidget
 | 
			
		||||
 *
 | 
			
		||||
 * Returns %TRUE if the pointer window belongs to @widget.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
gboolean
 | 
			
		||||
_gtk_widget_is_pointer_widget (GtkWidget *widget)
 | 
			
		||||
{
 | 
			
		||||
  if (GTK_WIDGET_HAS_POINTER (widget))
 | 
			
		||||
    { 
 | 
			
		||||
      GdkWindow *win; 
 | 
			
		||||
      GtkWidget *wid;
 | 
			
		||||
 | 
			
		||||
      win = _gtk_widget_get_pointer_window (widget);
 | 
			
		||||
      if (win)
 | 
			
		||||
        { 
 | 
			
		||||
          gdk_window_get_user_data (win, &wid);
 | 
			
		||||
          if (wid == widget)
 | 
			
		||||
            return TRUE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * _gtk_widget_synthesize_crossing:
 | 
			
		||||
 * @from: the #GtkWidget the virtual pointer is leaving.
 | 
			
		||||
 * @to: the #GtkWidget the virtual pointer is moving to.
 | 
			
		||||
 * @mode: the #GdkCrossingMode to place on the synthesized events.
 | 
			
		||||
 *
 | 
			
		||||
 * Generate crossing event(s) on widget state (sensitivity) or GTK+ grab change.
 | 
			
		||||
 *
 | 
			
		||||
 * The real pointer window is the window that most recently received an enter notify
 | 
			
		||||
 * event.  Windows that don't select for crossing events can't become the real
 | 
			
		||||
 * poiner window.  The real pointer widget that owns the real pointer window.  The
 | 
			
		||||
 * effective pointer window is the same as the real pointer window unless the real
 | 
			
		||||
 * pointer widget is either insensitive or there is a grab on a widget that is not
 | 
			
		||||
 * an ancestor of the real pointer widget (in which case the effective pointer
 | 
			
		||||
 * window should be the root window).
 | 
			
		||||
 *
 | 
			
		||||
 * When the effective pointer window is the same as the real poiner window, we
 | 
			
		||||
 * receive crossing events from the windowing system.  When the effective pointer
 | 
			
		||||
 * window changes to become different from the real pointer window we synthesize
 | 
			
		||||
 * crossing events, attempting to follow X protocol rules:
 | 
			
		||||
 *
 | 
			
		||||
 * When the root window becomes the effective pointer window:
 | 
			
		||||
 *   - leave notify on real pointer window, detail Ancestor
 | 
			
		||||
 *   - leave notify on all of its ancestors, detail Virtual
 | 
			
		||||
 *   - enter notify on root window, detail Inferior
 | 
			
		||||
 *
 | 
			
		||||
 * When the root window ceases to be the effective pointer window:
 | 
			
		||||
 *   - leave notify on root window, detail Inferior
 | 
			
		||||
 *   - enter notify on all ancestors of real pointer window, detail Virtual
 | 
			
		||||
 *   - enter notify on real pointer window, detail Ancestor
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
_gtk_widget_synthesize_crossing (GtkWidget      *from,
 | 
			
		||||
				 GtkWidget      *to,
 | 
			
		||||
				 GdkCrossingMode mode)
 | 
			
		||||
{
 | 
			
		||||
  GdkWindow *from_window = NULL, *to_window = NULL;
 | 
			
		||||
 | 
			
		||||
  g_return_if_fail (from != NULL || to != NULL);
 | 
			
		||||
 | 
			
		||||
  if (from != NULL)
 | 
			
		||||
    from_window = GTK_WIDGET_HAS_POINTER (from)
 | 
			
		||||
      ? _gtk_widget_get_pointer_window (from) : from->window;
 | 
			
		||||
  if (to != NULL)
 | 
			
		||||
    to_window = GTK_WIDGET_HAS_POINTER (to)
 | 
			
		||||
      ? _gtk_widget_get_pointer_window (to) : to->window;
 | 
			
		||||
 | 
			
		||||
  if (from_window == NULL && to_window == NULL)
 | 
			
		||||
    ;
 | 
			
		||||
  else if (from_window != NULL && to_window == NULL)
 | 
			
		||||
    {
 | 
			
		||||
      GList *from_ancestors = NULL, *list;
 | 
			
		||||
      GdkWindow *from_ancestor = from_window;
 | 
			
		||||
 | 
			
		||||
      while (from_ancestor != NULL)
 | 
			
		||||
	{
 | 
			
		||||
	  if (from_ancestor != NULL)
 | 
			
		||||
	    {
 | 
			
		||||
	      from_ancestor = gdk_window_get_parent (from_ancestor);
 | 
			
		||||
	      if (from_ancestor == NULL)
 | 
			
		||||
		break;
 | 
			
		||||
	      from_ancestors = g_list_prepend (from_ancestors, from_ancestor);
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
      synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
 | 
			
		||||
		      mode, GDK_NOTIFY_ANCESTOR);
 | 
			
		||||
      for (list = g_list_last (from_ancestors); list; list = list->prev)
 | 
			
		||||
	{
 | 
			
		||||
	  synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
 | 
			
		||||
			  mode, GDK_NOTIFY_VIRTUAL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
      /* XXX: enter/inferior on root window? */
 | 
			
		||||
 | 
			
		||||
      g_list_free (from_ancestors);
 | 
			
		||||
    }
 | 
			
		||||
  else if (from_window == NULL && to_window != NULL)
 | 
			
		||||
    {
 | 
			
		||||
      GList *to_ancestors = NULL, *list;
 | 
			
		||||
      GdkWindow *to_ancestor = to_window;
 | 
			
		||||
 | 
			
		||||
      while (to_ancestor != NULL)
 | 
			
		||||
	{
 | 
			
		||||
	  if (to_ancestor != NULL)
 | 
			
		||||
	    {
 | 
			
		||||
	      to_ancestor = gdk_window_get_parent (to_ancestor);
 | 
			
		||||
	      if (to_ancestor == NULL)
 | 
			
		||||
		break;
 | 
			
		||||
	      to_ancestors = g_list_prepend (to_ancestors, to_ancestor);
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
      /* XXX: leave/inferior on root window? */
 | 
			
		||||
 | 
			
		||||
      for (list = to_ancestors; list; list = list->next)
 | 
			
		||||
	{
 | 
			
		||||
	  synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
 | 
			
		||||
			  mode, GDK_NOTIFY_VIRTUAL);
 | 
			
		||||
	}
 | 
			
		||||
      synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
 | 
			
		||||
		      mode, GDK_NOTIFY_ANCESTOR);
 | 
			
		||||
 | 
			
		||||
      g_list_free (to_ancestors);
 | 
			
		||||
    }
 | 
			
		||||
  else if (from_window == to_window)
 | 
			
		||||
    ;
 | 
			
		||||
  else
 | 
			
		||||
    {
 | 
			
		||||
      GList *from_ancestors = NULL, *to_ancestors = NULL, *list;
 | 
			
		||||
      GdkWindow *from_ancestor = from_window, *to_ancestor = to_window;
 | 
			
		||||
 | 
			
		||||
      while (from_ancestor != NULL || to_ancestor != NULL)
 | 
			
		||||
	{
 | 
			
		||||
	  if (from_ancestor != NULL)
 | 
			
		||||
	    {
 | 
			
		||||
	      from_ancestor = gdk_window_get_parent (from_ancestor);
 | 
			
		||||
	      if (from_ancestor == to_window)
 | 
			
		||||
		break;
 | 
			
		||||
	      from_ancestors = g_list_prepend (from_ancestors, from_ancestor);
 | 
			
		||||
	    }
 | 
			
		||||
	  if (to_ancestor != NULL)
 | 
			
		||||
	    {
 | 
			
		||||
	      to_ancestor = gdk_window_get_parent (to_ancestor);
 | 
			
		||||
	      if (to_ancestor == from_window)
 | 
			
		||||
		break;
 | 
			
		||||
	      to_ancestors = g_list_prepend (to_ancestors, to_ancestor);
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
      if (to_ancestor == from_window)
 | 
			
		||||
	{
 | 
			
		||||
	  if (mode != GDK_CROSSING_GTK_UNGRAB)
 | 
			
		||||
	    synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
 | 
			
		||||
			    mode, GDK_NOTIFY_INFERIOR);
 | 
			
		||||
	  for (list = to_ancestors; list; list = list->next)
 | 
			
		||||
	    synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data, 
 | 
			
		||||
			    mode, GDK_NOTIFY_VIRTUAL);
 | 
			
		||||
	  synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
 | 
			
		||||
			  mode, GDK_NOTIFY_ANCESTOR);
 | 
			
		||||
	}
 | 
			
		||||
      else if (from_ancestor == to_window)
 | 
			
		||||
	{
 | 
			
		||||
	  synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
 | 
			
		||||
			  mode, GDK_NOTIFY_ANCESTOR);
 | 
			
		||||
	  for (list = g_list_last (from_ancestors); list; list = list->prev)
 | 
			
		||||
	    {
 | 
			
		||||
	      synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
 | 
			
		||||
			      mode, GDK_NOTIFY_VIRTUAL);
 | 
			
		||||
	    }
 | 
			
		||||
	  if (mode != GDK_CROSSING_GTK_GRAB)
 | 
			
		||||
	    synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
 | 
			
		||||
			    mode, GDK_NOTIFY_INFERIOR);
 | 
			
		||||
	}
 | 
			
		||||
      else
 | 
			
		||||
	{
 | 
			
		||||
	  while (from_ancestors != NULL && to_ancestors != NULL 
 | 
			
		||||
		 && from_ancestors->data == to_ancestors->data)
 | 
			
		||||
	    {
 | 
			
		||||
	      from_ancestors = g_list_delete_link (from_ancestors, 
 | 
			
		||||
						   from_ancestors);
 | 
			
		||||
	      to_ancestors = g_list_delete_link (to_ancestors, to_ancestors);
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
	  synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
 | 
			
		||||
			  mode, GDK_NOTIFY_NONLINEAR);
 | 
			
		||||
 | 
			
		||||
	  for (list = g_list_last (from_ancestors); list; list = list->prev)
 | 
			
		||||
	    {
 | 
			
		||||
	      synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
 | 
			
		||||
			      mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
 | 
			
		||||
	    }
 | 
			
		||||
	  for (list = to_ancestors; list; list = list->next)
 | 
			
		||||
	    {
 | 
			
		||||
	      synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
 | 
			
		||||
			      mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
 | 
			
		||||
	    }
 | 
			
		||||
	  synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
 | 
			
		||||
			  mode, GDK_NOTIFY_NONLINEAR);
 | 
			
		||||
	}
 | 
			
		||||
      g_list_free (from_ancestors);
 | 
			
		||||
      g_list_free (to_ancestors);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
gtk_widget_propagate_state (GtkWidget           *widget,
 | 
			
		||||
			    GtkStateData        *data)
 | 
			
		||||
@ -8108,6 +8386,16 @@ gtk_widget_propagate_state (GtkWidget           *widget,
 | 
			
		||||
 | 
			
		||||
      g_signal_emit (widget, widget_signals[STATE_CHANGED], 0, old_state);
 | 
			
		||||
 | 
			
		||||
      if (GTK_WIDGET_HAS_POINTER (widget) && !GTK_WIDGET_SHADOWED (widget))
 | 
			
		||||
	{
 | 
			
		||||
	  if (!GTK_WIDGET_IS_SENSITIVE (widget))
 | 
			
		||||
	    _gtk_widget_synthesize_crossing (widget, NULL, 
 | 
			
		||||
					     GDK_CROSSING_STATE_CHANGED);
 | 
			
		||||
	  else if (old_state == GTK_STATE_INSENSITIVE)
 | 
			
		||||
	    _gtk_widget_synthesize_crossing (NULL, widget, 
 | 
			
		||||
					     GDK_CROSSING_STATE_CHANGED);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
      if (GTK_IS_CONTAINER (widget))
 | 
			
		||||
	{
 | 
			
		||||
	  data->parent_sensitive = (GTK_WIDGET_IS_SENSITIVE (widget) != FALSE);
 | 
			
		||||
 | 
			
		||||
@ -833,6 +833,14 @@ void              _gtk_widget_propagate_screen_changed    (GtkWidget    *widget,
 | 
			
		||||
							   GdkScreen    *previous_screen);
 | 
			
		||||
void		  _gtk_widget_propagate_composited_changed (GtkWidget    *widget);
 | 
			
		||||
 | 
			
		||||
void	   _gtk_widget_set_pointer_window  (GtkWidget      *widget,
 | 
			
		||||
					    GdkWindow      *pointer_window);
 | 
			
		||||
GdkWindow *_gtk_widget_get_pointer_window  (GtkWidget      *widget);
 | 
			
		||||
gboolean   _gtk_widget_is_pointer_widget   (GtkWidget      *widget);
 | 
			
		||||
void       _gtk_widget_synthesize_crossing (GtkWidget      *from,
 | 
			
		||||
					    GtkWidget      *to,
 | 
			
		||||
					    GdkCrossingMode mode);
 | 
			
		||||
 | 
			
		||||
GdkColormap* _gtk_widget_peek_colormap (void);
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
@ -2052,10 +2052,6 @@ gtk_window_unset_transient_for  (GtkWindow *window)
 | 
			
		||||
  
 | 
			
		||||
  if (window->transient_parent)
 | 
			
		||||
    {
 | 
			
		||||
      if (priv->transient_parent_group)
 | 
			
		||||
	gtk_window_group_remove_window (window->group,
 | 
			
		||||
					window);
 | 
			
		||||
 | 
			
		||||
      g_signal_handlers_disconnect_by_func (window->transient_parent,
 | 
			
		||||
					    gtk_window_transient_parent_realized,
 | 
			
		||||
					    window);
 | 
			
		||||
@ -2073,7 +2069,13 @@ gtk_window_unset_transient_for  (GtkWindow *window)
 | 
			
		||||
        disconnect_parent_destroyed (window);
 | 
			
		||||
      
 | 
			
		||||
      window->transient_parent = NULL;
 | 
			
		||||
 | 
			
		||||
      if (priv->transient_parent_group)
 | 
			
		||||
	{
 | 
			
		||||
	  priv->transient_parent_group = FALSE;
 | 
			
		||||
	  gtk_window_group_remove_window (window->group,
 | 
			
		||||
					  window);
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -51,6 +51,10 @@ TEST_PROGS			+= object
 | 
			
		||||
object_SOURCES			 = object.c pixbuf-init.c
 | 
			
		||||
object_LDADD			 = $(progs_ldadd)
 | 
			
		||||
 | 
			
		||||
TEST_PROGS			+= crossingevents
 | 
			
		||||
crossingevents_SOURCES		 = crossingevents.c
 | 
			
		||||
crossingevents_LDADD		 = $(progs_ldadd)
 | 
			
		||||
 | 
			
		||||
# this doesn't work in make distcheck, since it doesn't
 | 
			
		||||
# find file-chooser-test-dir 
 | 
			
		||||
# TEST_PROGS			+= filechooser
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1254
									
								
								gtk/tests/crossingevents.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1254
									
								
								gtk/tests/crossingevents.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user