forked from AuroraMiddleware/gtk
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:
parent
3e38ddd3e6
commit
9b9945b8e7
263
gtk/gtkdnd.c
263
gtk/gtkdnd.c
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user