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 cb8c076321)
This commit is contained in:
Michael Natterer 2010-05-29 05:04:54 +02:00
parent 3e38ddd3e6
commit 9b9945b8e7

View File

@ -47,6 +47,7 @@
#include "gtkmain.h" #include "gtkmain.h"
#include "gtkplug.h" #include "gtkplug.h"
#include "gtkstock.h" #include "gtkstock.h"
#include "gtktooltip.h"
#include "gtkwindow.h" #include "gtkwindow.h"
#include "gtkintl.h" #include "gtkintl.h"
#include "gtkdndcursors.h" #include "gtkdndcursors.h"
@ -59,7 +60,6 @@ typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
typedef struct _GtkDragDestSite GtkDragDestSite; typedef struct _GtkDragDestSite GtkDragDestSite;
typedef struct _GtkDragDestInfo GtkDragDestInfo; typedef struct _GtkDragDestInfo GtkDragDestInfo;
typedef struct _GtkDragAnim GtkDragAnim; typedef struct _GtkDragAnim GtkDragAnim;
typedef struct _GtkDragFindData GtkDragFindData;
typedef enum typedef enum
@ -170,18 +170,11 @@ struct _GtkDragAnim
gint n_steps; gint n_steps;
}; };
struct _GtkDragFindData typedef gboolean (* GtkDragDestCallback) (GtkWidget *widget,
{ GdkDragContext *context,
gint x; gint x,
gint y; gint y,
GdkDragContext *context; guint32 time);
GtkDragDestInfo *info;
gboolean found;
gboolean toplevel;
gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
gint x, gint y, guint32 time);
guint32 time;
};
/* Enumeration for some targets we handle internally */ /* Enumeration for some targets we handle internally */
@ -221,8 +214,13 @@ static void gtk_drag_selection_received (GtkWidget *widget,
GtkSelectionData *selection_data, GtkSelectionData *selection_data,
guint time, guint time,
gpointer data); gpointer data);
static void gtk_drag_find_widget (GtkWidget *widget, static gboolean gtk_drag_find_widget (GtkWidget *widget,
GtkDragFindData *data); GdkDragContext *context,
GtkDragDestInfo *info,
gint x,
gint y,
guint32 time,
GtkDragDestCallback callback);
static void gtk_drag_proxy_begin (GtkWidget *widget, static void gtk_drag_proxy_begin (GtkWidget *widget,
GtkDragDestInfo *dest_info, GtkDragDestInfo *dest_info,
guint32 time); guint32 time);
@ -1619,8 +1617,8 @@ _gtk_drag_dest_handle_event (GtkWidget *toplevel,
case GDK_DRAG_MOTION: case GDK_DRAG_MOTION:
case GDK_DROP_START: case GDK_DROP_START:
{ {
GtkDragFindData data;
gint tx, ty; gint tx, ty;
gboolean found;
if (event->type == GDK_DROP_START) if (event->type == GDK_DROP_START)
{ {
@ -1648,19 +1646,17 @@ _gtk_drag_dest_handle_event (GtkWidget *toplevel,
#endif /* GDK_WINDOWING_X11 */ #endif /* GDK_WINDOWING_X11 */
gdk_window_get_position (toplevel->window, &tx, &ty); gdk_window_get_position (toplevel->window, &tx, &ty);
data.x = event->dnd.x_root - tx; found = gtk_drag_find_widget (toplevel,
data.y = event->dnd.y_root - ty; context,
data.context = context; info,
data.info = info; event->dnd.x_root - tx,
data.found = FALSE; event->dnd.y_root - ty,
data.toplevel = TRUE; event->dnd.time,
data.callback = (event->type == GDK_DRAG_MOTION) ? (event->type == GDK_DRAG_MOTION) ?
gtk_drag_dest_motion : gtk_drag_dest_drop; gtk_drag_dest_motion :
data.time = event->dnd.time; gtk_drag_dest_drop);
gtk_drag_find_widget (toplevel, &data); if (info->widget && !found)
if (info->widget && !data.found)
{ {
gtk_drag_dest_leave (info->widget, context, event->dnd.time); gtk_drag_dest_leave (info->widget, context, event->dnd.time);
info->widget = NULL; info->widget = NULL;
@ -1670,13 +1666,13 @@ _gtk_drag_dest_handle_event (GtkWidget *toplevel,
*/ */
if (event->type == GDK_DRAG_MOTION) if (event->type == GDK_DRAG_MOTION)
{ {
if (!data.found) if (!found)
gdk_drag_status (context, 0, event->dnd.time); gdk_drag_status (context, 0, event->dnd.time);
} }
else if (event->type == GDK_DROP_START && !info->proxy_source) else if (event->type == GDK_DROP_START && !info->proxy_source)
{ {
gdk_drop_reply (context, data.found, event->dnd.time); gdk_drop_reply (context, found, event->dnd.time);
if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found) if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !found)
gtk_drag_finish (context, FALSE, FALSE, event->dnd.time); 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); 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: * gtk_drag_find_widget:
* Recursive callback used to locate widgets for * Function used to locate widgets for
* DRAG_MOTION and DROP_START events. * DRAG_MOTION and DROP_START events.
* arguments:
*
* results:
*************************************************************/ *************************************************************/
static void static gboolean
gtk_drag_find_widget (GtkWidget *widget, gtk_drag_find_widget (GtkWidget *widget,
GtkDragFindData *data) GdkDragContext *context,
GtkDragDestInfo *info,
gint x,
gint y,
guint32 time,
GtkDragDestCallback callback)
{ {
GtkAllocation new_allocation; if (!gtk_widget_get_mapped (widget) ||
gint allocation_to_window_x = 0; !gtk_widget_get_sensitive (widget))
gint allocation_to_window_y = 0; return FALSE;
gint x_offset = 0;
gint y_offset = 0;
if (data->found || !gtk_widget_get_mapped (widget) || !gtk_widget_get_sensitive (widget)) /* Get the widget at the pointer coordinates and travel up
return; * 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 while (widget)
* 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)
{ {
gint tx, ty; GtkWidget *parent;
GdkWindow *window = widget->window; GList *hierarchy = NULL;
gboolean found = FALSE;
/* Compute the offset from allocation-relative to if (!gtk_widget_get_mapped (widget) ||
* window-relative coordinates. !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; for (parent = widget;
allocation_to_window_y = widget->allocation.y; parent;
parent = gtk_widget_get_parent (parent))
{
hierarchy = g_list_prepend (hierarchy, g_object_ref (parent));
}
if (gtk_widget_get_has_window (widget)) /* If the current widget is registered as a drop site, check to
{ * emit "drag-motion" to check if we are actually in a drop
/* The allocation is relative to the parent window for * site.
* 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 (GTK_IS_CONTAINER (widget)) if (g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
{ {
GtkDragFindData new_data = *data; found = callback (widget, context, x, y, time);
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;
}
/* 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 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 static void