From 9b9945b8e701a32701a2ca152a7059ade85f75c2 Mon Sep 17 00:00:00 2001 From: Michael Natterer Date: Sat, 29 May 2010 05:04:54 +0200 Subject: [PATCH] Bug 607628 - DnD operation doesn't work when using offscreen Changed the way to find the drop widget from a top->bottom recursion using GdkWindow positions to a liner bottom->top walk up the widget hierarchy using _gtk_widget_find_at_coords() and gtk_widget_translate_coordinates(), which both do the right things for offscreen widgets. (cherry picked from commit cb8c0763215bd18219c2d32b56e4bfe8a576f6b3) --- gtk/gtkdnd.c | 263 ++++++++++++++++++++------------------------------- 1 file changed, 104 insertions(+), 159 deletions(-) diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c index 03f04363d5..a6409858dc 100644 --- a/gtk/gtkdnd.c +++ b/gtk/gtkdnd.c @@ -47,6 +47,7 @@ #include "gtkmain.h" #include "gtkplug.h" #include "gtkstock.h" +#include "gtktooltip.h" #include "gtkwindow.h" #include "gtkintl.h" #include "gtkdndcursors.h" @@ -59,7 +60,6 @@ typedef struct _GtkDragSourceInfo GtkDragSourceInfo; typedef struct _GtkDragDestSite GtkDragDestSite; typedef struct _GtkDragDestInfo GtkDragDestInfo; typedef struct _GtkDragAnim GtkDragAnim; -typedef struct _GtkDragFindData GtkDragFindData; typedef enum @@ -170,18 +170,11 @@ struct _GtkDragAnim gint n_steps; }; -struct _GtkDragFindData -{ - gint x; - gint y; - GdkDragContext *context; - GtkDragDestInfo *info; - gboolean found; - gboolean toplevel; - gboolean (*callback) (GtkWidget *widget, GdkDragContext *context, - gint x, gint y, guint32 time); - guint32 time; -}; +typedef gboolean (* GtkDragDestCallback) (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint32 time); /* Enumeration for some targets we handle internally */ @@ -221,8 +214,13 @@ static void gtk_drag_selection_received (GtkWidget *widget, GtkSelectionData *selection_data, guint time, gpointer data); -static void gtk_drag_find_widget (GtkWidget *widget, - GtkDragFindData *data); +static gboolean gtk_drag_find_widget (GtkWidget *widget, + GdkDragContext *context, + GtkDragDestInfo *info, + gint x, + gint y, + guint32 time, + GtkDragDestCallback callback); static void gtk_drag_proxy_begin (GtkWidget *widget, GtkDragDestInfo *dest_info, guint32 time); @@ -1619,8 +1617,8 @@ _gtk_drag_dest_handle_event (GtkWidget *toplevel, case GDK_DRAG_MOTION: case GDK_DROP_START: { - GtkDragFindData data; gint tx, ty; + gboolean found; if (event->type == GDK_DROP_START) { @@ -1648,19 +1646,17 @@ _gtk_drag_dest_handle_event (GtkWidget *toplevel, #endif /* GDK_WINDOWING_X11 */ gdk_window_get_position (toplevel->window, &tx, &ty); - data.x = event->dnd.x_root - tx; - data.y = event->dnd.y_root - ty; - data.context = context; - data.info = info; - data.found = FALSE; - data.toplevel = TRUE; - data.callback = (event->type == GDK_DRAG_MOTION) ? - gtk_drag_dest_motion : gtk_drag_dest_drop; - data.time = event->dnd.time; + found = gtk_drag_find_widget (toplevel, + context, + info, + event->dnd.x_root - tx, + event->dnd.y_root - ty, + event->dnd.time, + (event->type == GDK_DRAG_MOTION) ? + gtk_drag_dest_motion : + gtk_drag_dest_drop); - gtk_drag_find_widget (toplevel, &data); - - if (info->widget && !data.found) + if (info->widget && !found) { gtk_drag_dest_leave (info->widget, context, event->dnd.time); info->widget = NULL; @@ -1670,13 +1666,13 @@ _gtk_drag_dest_handle_event (GtkWidget *toplevel, */ if (event->type == GDK_DRAG_MOTION) { - if (!data.found) + if (!found) gdk_drag_status (context, 0, event->dnd.time); } else if (event->type == GDK_DROP_START && !info->proxy_source) { - gdk_drop_reply (context, data.found, event->dnd.time); - if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found) + gdk_drop_reply (context, found, event->dnd.time); + if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !found) gtk_drag_finish (context, FALSE, FALSE, event->dnd.time); } } @@ -1841,156 +1837,105 @@ gtk_drag_selection_received (GtkWidget *widget, gtk_drag_release_ipc_widget (widget); } -static void -prepend_and_ref_widget (GtkWidget *widget, - gpointer data) -{ - GSList **slist_p = data; - - *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget)); -} - /************************************************************* * gtk_drag_find_widget: - * Recursive callback used to locate widgets for + * Function used to locate widgets for * DRAG_MOTION and DROP_START events. - * arguments: - * - * results: *************************************************************/ -static void -gtk_drag_find_widget (GtkWidget *widget, - GtkDragFindData *data) +static gboolean +gtk_drag_find_widget (GtkWidget *widget, + GdkDragContext *context, + GtkDragDestInfo *info, + gint x, + gint y, + guint32 time, + GtkDragDestCallback callback) { - GtkAllocation new_allocation; - gint allocation_to_window_x = 0; - gint allocation_to_window_y = 0; - gint x_offset = 0; - gint y_offset = 0; + if (!gtk_widget_get_mapped (widget) || + !gtk_widget_get_sensitive (widget)) + return FALSE; - if (data->found || !gtk_widget_get_mapped (widget) || !gtk_widget_get_sensitive (widget)) - return; + /* Get the widget at the pointer coordinates and travel up + * the widget hierarchy from there. + */ + widget = _gtk_widget_find_at_coords (gtk_widget_get_window (widget), + x, y, &x, &y); + if (!widget) + return FALSE; - /* Note that in the following code, we only count the - * position as being inside a WINDOW widget if it is inside - * widget->window; points that are outside of widget->window - * but within the allocation are not counted. This is consistent - * with the way we highlight drag targets. - * - * data->x,y are relative to widget->parent->window (if - * widget is not a toplevel, widget->window otherwise). - * We compute the allocation of widget in the same coordinates, - * clipping to widget->window, and all intermediate - * windows. If data->x,y is inside that, then we translate - * our coordinates to be relative to widget->window and - * recurse. - */ - new_allocation = widget->allocation; - - if (widget->parent) + while (widget) { - gint tx, ty; - GdkWindow *window = widget->window; + GtkWidget *parent; + GList *hierarchy = NULL; + gboolean found = FALSE; - /* Compute the offset from allocation-relative to - * window-relative coordinates. + if (!gtk_widget_get_mapped (widget) || + !gtk_widget_get_sensitive (widget)) + return FALSE; + + /* need to reference the entire hierarchy temporarily in case the + * ::drag-motion/::drag-drop callbacks change the widget hierarchy. */ - allocation_to_window_x = widget->allocation.x; - allocation_to_window_y = widget->allocation.y; + for (parent = widget; + parent; + parent = gtk_widget_get_parent (parent)) + { + hierarchy = g_list_prepend (hierarchy, g_object_ref (parent)); + } - if (gtk_widget_get_has_window (widget)) - { - /* The allocation is relative to the parent window for - * window widgets, not to widget->window. - */ - gdk_window_get_position (window, &tx, &ty); - - allocation_to_window_x -= tx; - allocation_to_window_y -= ty; - } - - new_allocation.x = 0 + allocation_to_window_x; - new_allocation.y = 0 + allocation_to_window_y; - - while (window && window != widget->parent->window) - { - GdkRectangle window_rect = { 0, 0, 0, 0 }; - - gdk_drawable_get_size (window, &window_rect.width, &window_rect.height); - - gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation); - - gdk_window_get_position (window, &tx, &ty); - new_allocation.x += tx; - x_offset += tx; - new_allocation.y += ty; - y_offset += ty; - - window = gdk_window_get_parent (window); - } - - if (!window) /* Window and widget heirarchies didn't match. */ - return; - } - - if (data->toplevel || - ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) && - (data->x < new_allocation.x + new_allocation.width) && - (data->y < new_allocation.y + new_allocation.height))) - { - /* First, check if the drag is in a valid drop site in - * one of our children + /* If the current widget is registered as a drop site, check to + * emit "drag-motion" to check if we are actually in a drop + * site. */ - if (GTK_IS_CONTAINER (widget)) + if (g_object_get_data (G_OBJECT (widget), "gtk-drag-dest")) { - GtkDragFindData new_data = *data; - GSList *children = NULL; - GSList *tmp_list; - - new_data.x -= x_offset; - new_data.y -= y_offset; - new_data.found = FALSE; - new_data.toplevel = FALSE; - - /* need to reference children temporarily in case the - * ::drag-motion/::drag-drop callbacks change the widget hierarchy. - */ - gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children); - for (tmp_list = children; tmp_list; tmp_list = tmp_list->next) - { - if (!new_data.found && gtk_widget_is_drawable (tmp_list->data)) - gtk_drag_find_widget (tmp_list->data, &new_data); - g_object_unref (tmp_list->data); - } - g_slist_free (children); - - data->found = new_data.found; - } + found = callback (widget, context, x, y, time); - /* If not, and this widget is registered as a drop site, check to - * emit "drag-motion" to check if we are actually in - * a drop site. - */ - if (!data->found && - g_object_get_data (G_OBJECT (widget), "gtk-drag-dest")) - { - data->found = data->callback (widget, - data->context, - data->x - x_offset - allocation_to_window_x, - data->y - y_offset - allocation_to_window_y, - data->time); /* If so, send a "drag-leave" to the last widget */ - if (data->found) + if (found) { - if (data->info->widget && data->info->widget != widget) + if (info->widget && info->widget != widget) { - gtk_drag_dest_leave (data->info->widget, data->context, data->time); + gtk_drag_dest_leave (info->widget, context, time); } - data->info->widget = widget; + + info->widget = widget; } } + + if (!found) + { + /* Get the parent before unreffing the hierarchy because + * invoking the callback might have destroyed the widget + */ + parent = gtk_widget_get_parent (widget); + + /* The parent might be going away when unreffing the + * hierarchy, so also protect againt that + */ + if (parent) + g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &parent); + } + + g_list_foreach (hierarchy, (GFunc) g_object_unref, NULL); + g_list_free (hierarchy); + + if (found) + return TRUE; + + if (parent) + g_object_remove_weak_pointer (G_OBJECT (parent), (gpointer *) &parent); + else + return FALSE; + + if (!gtk_widget_translate_coordinates (widget, parent, x, y, &x, &y)) + return FALSE; + + widget = parent; } + + return FALSE; } static void