1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
28 #include "gdkconfig.h"
30 #include "gdk/gdkkeysyms.h"
34 #include "gtkinvisible.h"
38 #include "gtkwindow.h"
40 static GSList *source_widgets = NULL;
42 typedef struct _GtkDragSourceSite GtkDragSourceSite;
43 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
44 typedef struct _GtkDragDestSite GtkDragDestSite;
45 typedef struct _GtkDragDestInfo GtkDragDestInfo;
46 typedef struct _GtkDragAnim GtkDragAnim;
47 typedef struct _GtkDragFindData GtkDragFindData;
57 struct _GtkDragSourceSite
59 GdkModifierType start_button_mask;
60 GtkTargetList *target_list; /* Targets for drag data */
61 GdkDragAction actions; /* Possible actions */
64 GtkImageType icon_type;
67 GtkImagePixmapData pixmap;
68 GtkImagePixbufData pixbuf;
69 GtkImageStockData stock;
73 GdkColormap *colormap; /* Colormap for drag icon */
75 /* Stored button press information to detect drag beginning */
80 struct _GtkDragSourceInfo
83 GtkTargetList *target_list; /* Targets for drag data */
84 GdkDragAction possible_actions; /* Actions allowed by source */
85 GdkDragContext *context; /* drag context */
86 GtkWidget *icon_window; /* Window for drag */
87 GtkWidget *fallback_icon; /* Window for drag used on other screens */
88 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
89 GdkCursor *cursor; /* Cursor for drag */
90 gint hot_x, hot_y; /* Hot spot for drag */
91 gint button; /* mouse button starting drag */
93 GtkDragStatus status; /* drag status */
94 GdkEvent *last_event; /* pending event */
96 gint start_x, start_y; /* Initial position */
97 gint cur_x, cur_y; /* Current Position */
98 GdkScreen *cur_screen; /* Current screen for pointer */
100 guint32 grab_time; /* timestamp for initial grab */
101 GList *selections; /* selections we've claimed */
103 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
105 guint update_idle; /* Idle function to update the drag */
106 guint drop_timeout; /* Timeout for aborting drop */
107 guint destroy_icon : 1; /* If true, destroy icon_window
109 guint have_grab : 1; /* Do we still have the pointer grab
113 struct _GtkDragDestSite
115 GtkDestDefaults flags;
116 GtkTargetList *target_list;
117 GdkDragAction actions;
118 GdkWindow *proxy_window;
119 GdkDragProtocol proxy_protocol;
121 guint proxy_coords : 1;
125 struct _GtkDragDestInfo
127 GtkWidget *widget; /* Widget in which drag is in */
128 GdkDragContext *context; /* Drag context */
129 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
130 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
131 guint dropped : 1; /* Set after we receive a drop */
132 guint32 proxy_drop_time; /* Timestamp for proxied drop */
133 guint proxy_drop_wait : 1; /* Set if we are waiting for a
134 * status reply before sending
137 gint drop_x, drop_y; /* Position of drop */
140 #define DROP_ABORT_TIME 300000
142 #define ANIM_STEP_TIME 50
143 #define ANIM_STEP_LENGTH 50
144 #define ANIM_MIN_STEPS 5
145 #define ANIM_MAX_STEPS 10
149 GtkDragSourceInfo *info;
154 struct _GtkDragFindData
158 GdkDragContext *context;
159 GtkDragDestInfo *info;
162 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
163 gint x, gint y, guint32 time);
167 /* Enumeration for some targets we handle internally */
170 TARGET_MOTIF_SUCCESS = 0x40000000,
171 TARGET_MOTIF_FAILURE,
177 static GdkPixmap *default_icon_pixmap = NULL;
178 static GdkPixmap *default_icon_mask = NULL;
179 static GdkColormap *default_icon_colormap = NULL;
180 static gint default_icon_hot_x;
181 static gint default_icon_hot_y;
183 /* Forward declarations */
184 static void gtk_drag_get_event_actions (GdkEvent *event,
186 GdkDragAction actions,
187 GdkDragAction *suggested_action,
188 GdkDragAction *possible_actions);
189 static GdkCursor * gtk_drag_get_cursor (GdkDisplay *display,
190 GdkDragAction action);
191 static GtkWidget *gtk_drag_get_ipc_widget (GdkScreen *screen);
192 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
194 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
195 GdkEventExpose *event,
198 static void gtk_drag_selection_received (GtkWidget *widget,
199 GtkSelectionData *selection_data,
202 static void gtk_drag_find_widget (GtkWidget *widget,
203 GtkDragFindData *data);
204 static void gtk_drag_proxy_begin (GtkWidget *widget,
205 GtkDragDestInfo *dest_info,
207 static void gtk_drag_dest_realized (GtkWidget *widget);
208 static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
209 GtkWidget *previous_toplevel);
210 static void gtk_drag_dest_site_destroy (gpointer data);
211 static void gtk_drag_dest_leave (GtkWidget *widget,
212 GdkDragContext *context,
214 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
215 GdkDragContext *context,
219 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
220 GdkDragContext *context,
225 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
227 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
229 static void gtk_drag_clear_source_info (GdkDragContext *context);
231 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
234 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
236 static void gtk_drag_drop (GtkDragSourceInfo *info,
238 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
241 static void gtk_drag_cancel (GtkDragSourceInfo *info,
244 static gint gtk_drag_source_event_cb (GtkWidget *widget,
247 static void gtk_drag_source_site_destroy (gpointer data);
248 static void gtk_drag_selection_get (GtkWidget *widget,
249 GtkSelectionData *selection_data,
253 static gint gtk_drag_anim_timeout (gpointer data);
254 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
255 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
256 static void gtk_drag_add_update_idle (GtkDragSourceInfo *info);
258 static void gtk_drag_update (GtkDragSourceInfo *info,
263 static gint gtk_drag_motion_cb (GtkWidget *widget,
264 GdkEventMotion *event,
266 static gint gtk_drag_key_cb (GtkWidget *widget,
269 static gint gtk_drag_button_release_cb (GtkWidget *widget,
270 GdkEventButton *event,
272 static gint gtk_drag_abort_timeout (gpointer data);
274 /************************
275 * Cursor and Icon data *
276 ************************/
278 #define action_ask_width 16
279 #define action_ask_height 16
280 static const guchar action_ask_bits[] = {
281 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
282 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
283 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
285 #define action_ask_mask_width 16
286 #define action_ask_mask_height 16
287 static const guchar action_ask_mask_bits[] = {
288 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
289 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
290 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
292 #define action_copy_width 16
293 #define action_copy_height 16
294 static const guchar action_copy_bits[] = {
295 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
296 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
297 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
299 #define action_copy_mask_width 16
300 #define action_copy_mask_height 16
301 static const guchar action_copy_mask_bits[] = {
302 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
303 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
304 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
306 #define action_move_width 16
307 #define action_move_height 16
308 static const guchar action_move_bits[] = {
309 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
310 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
311 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
313 #define action_move_mask_width 16
314 #define action_move_mask_height 16
315 static const guchar action_move_mask_bits[] = {
316 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
317 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
318 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
320 #define action_link_width 16
321 #define action_link_height 16
322 static const guchar action_link_bits[] = {
323 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
324 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
325 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
327 #define action_link_mask_width 16
328 #define action_link_mask_height 16
329 static const guchar action_link_mask_bits[] = {
330 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
331 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
332 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
334 #define action_none_width 16
335 #define action_none_height 16
336 static const guchar action_none_bits[] = {
337 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
338 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
339 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
341 #define action_none_mask_width 16
342 #define action_none_mask_height 16
343 static const guchar action_none_mask_bits[] = {
344 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
345 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
346 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
348 #define CURSOR_WIDTH 16
349 #define CURSOR_HEIGHT 16
352 GdkDragAction action;
357 { GDK_ACTION_DEFAULT, 0 },
358 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
359 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
360 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
361 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
362 { 0 , action_none_bits, action_none_mask_bits, NULL },
365 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
367 /*********************
368 * Utility functions *
369 *********************/
372 set_can_change_screen (GtkWidget *widget,
373 gboolean can_change_screen)
375 can_change_screen = can_change_screen != FALSE;
377 g_object_set_data (G_OBJECT (widget), "gtk-dnd-can-change-screen",
378 GUINT_TO_POINTER (can_change_screen));
382 get_can_change_screen (GtkWidget *widget)
384 return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
388 /*************************************************************
389 * gtk_drag_get_ipc_widget:
390 * Return a invisible, off-screen, override-redirect
395 *************************************************************/
398 gtk_drag_get_ipc_widget (GdkScreen *screen)
401 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
402 "gtk-dnd-ipc-widgets");
406 GSList *tmp = drag_widgets;
407 result = drag_widgets->data;
408 drag_widgets = drag_widgets->next;
409 g_object_set_data (G_OBJECT (screen),
410 "gtk-dnd-ipc-widgets",
412 g_slist_free_1 (tmp);
416 result = gtk_invisible_new_for_screen (screen);
417 gtk_widget_show (result);
423 /***************************************************************
424 * gtk_drag_release_ipc_widget:
425 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
427 * widget: the widget to release.
429 ***************************************************************/
432 gtk_drag_release_ipc_widget (GtkWidget *widget)
434 GdkScreen *screen = gtk_widget_get_screen (widget);
435 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
436 "gtk-dnd-ipc-widgets");
437 drag_widgets = g_slist_prepend (drag_widgets, widget);
438 g_object_set_data (G_OBJECT (screen),
439 "gtk-dnd-ipc-widgets",
444 gtk_drag_get_event_time (GdkEvent *event)
446 guint32 tm = GDK_CURRENT_TIME;
451 case GDK_MOTION_NOTIFY:
452 tm = event->motion.time; break;
453 case GDK_BUTTON_PRESS:
454 case GDK_2BUTTON_PRESS:
455 case GDK_3BUTTON_PRESS:
456 case GDK_BUTTON_RELEASE:
457 tm = event->button.time; break;
459 case GDK_KEY_RELEASE:
460 tm = event->key.time; break;
461 case GDK_ENTER_NOTIFY:
462 case GDK_LEAVE_NOTIFY:
463 tm = event->crossing.time; break;
464 case GDK_PROPERTY_NOTIFY:
465 tm = event->property.time; break;
466 case GDK_SELECTION_CLEAR:
467 case GDK_SELECTION_REQUEST:
468 case GDK_SELECTION_NOTIFY:
469 tm = event->selection.time; break;
470 case GDK_PROXIMITY_IN:
471 case GDK_PROXIMITY_OUT:
472 tm = event->proximity.time; break;
473 default: /* use current time */
481 gtk_drag_get_event_actions (GdkEvent *event,
483 GdkDragAction actions,
484 GdkDragAction *suggested_action,
485 GdkDragAction *possible_actions)
487 *suggested_action = 0;
488 *possible_actions = 0;
492 GdkModifierType state = 0;
496 case GDK_MOTION_NOTIFY:
497 state = event->motion.state;
499 case GDK_BUTTON_PRESS:
500 case GDK_2BUTTON_PRESS:
501 case GDK_3BUTTON_PRESS:
502 case GDK_BUTTON_RELEASE:
503 state = event->button.state;
506 case GDK_KEY_RELEASE:
507 state = event->key.state;
509 case GDK_ENTER_NOTIFY:
510 case GDK_LEAVE_NOTIFY:
511 state = event->crossing.state;
517 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
519 *suggested_action = GDK_ACTION_ASK;
520 *possible_actions = actions;
522 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
524 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
526 if (actions & GDK_ACTION_LINK)
528 *suggested_action = GDK_ACTION_LINK;
529 *possible_actions = GDK_ACTION_LINK;
532 else if (state & GDK_CONTROL_MASK)
534 if (actions & GDK_ACTION_COPY)
536 *suggested_action = GDK_ACTION_COPY;
537 *possible_actions = GDK_ACTION_COPY;
543 if (actions & GDK_ACTION_MOVE)
545 *suggested_action = GDK_ACTION_MOVE;
546 *possible_actions = GDK_ACTION_MOVE;
553 *possible_actions = actions;
555 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
556 *suggested_action = GDK_ACTION_ASK;
557 else if (actions & GDK_ACTION_COPY)
558 *suggested_action = GDK_ACTION_COPY;
559 else if (actions & GDK_ACTION_MOVE)
560 *suggested_action = GDK_ACTION_MOVE;
561 else if (actions & GDK_ACTION_LINK)
562 *suggested_action = GDK_ACTION_LINK;
567 *possible_actions = actions;
569 if (actions & GDK_ACTION_COPY)
570 *suggested_action = GDK_ACTION_COPY;
571 else if (actions & GDK_ACTION_MOVE)
572 *suggested_action = GDK_ACTION_MOVE;
573 else if (actions & GDK_ACTION_LINK)
574 *suggested_action = GDK_ACTION_LINK;
581 gtk_drag_get_cursor (GdkDisplay *display,
582 GdkDragAction action)
586 for (i = 0 ; i < n_drag_cursors - 1; i++)
587 if (drag_cursors[i].action == action)
589 if (drag_cursors[i].cursor != NULL)
591 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
593 gdk_cursor_unref (drag_cursors[i].cursor);
594 drag_cursors[i].cursor = NULL;
598 if (drag_cursors[i].cursor == NULL)
600 GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
601 GdkColor fg = { 0, 0x0000, 0x0000, 0x0000 };
602 GdkScreen *screen = gdk_display_get_default_screen (display);
603 GdkWindow *window = gdk_screen_get_root_window (screen);
606 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].bits, CURSOR_WIDTH, CURSOR_HEIGHT);
609 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].mask, CURSOR_WIDTH, CURSOR_HEIGHT);
611 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
613 g_object_unref (pixmap);
614 g_object_unref (mask);
617 return drag_cursors[i].cursor;
620 /********************
622 ********************/
624 /*************************************************************
626 * Get the data for a drag or drop
628 * context - drag context
629 * target - format to retrieve the data in.
630 * time - timestamp of triggering event.
633 *************************************************************/
636 gtk_drag_get_data (GtkWidget *widget,
637 GdkDragContext *context,
641 GtkWidget *selection_widget;
643 g_return_if_fail (GTK_IS_WIDGET (widget));
644 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
645 g_return_if_fail (!context->is_source);
647 selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
649 g_object_ref (context);
650 g_object_ref (widget);
652 g_signal_connect (selection_widget, "selection_received",
653 G_CALLBACK (gtk_drag_selection_received), widget);
655 g_object_set_data (G_OBJECT (selection_widget), "drag-context", context);
657 gtk_selection_convert (selection_widget,
658 gdk_drag_get_selection (context),
664 /*************************************************************
665 * gtk_drag_get_source_widget:
666 * Get the widget the was the source of this drag, if
667 * the drag originated from this application.
669 * context: The drag context for this drag
671 * The source widget, or NULL if the drag originated from
672 * a different application.
673 *************************************************************/
676 gtk_drag_get_source_widget (GdkDragContext *context)
680 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
681 g_return_val_if_fail (!context->is_source, NULL);
683 tmp_list = source_widgets;
686 GtkWidget *ipc_widget = tmp_list->data;
688 if (ipc_widget->window == context->source_window)
690 GtkDragSourceInfo *info;
691 info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
693 return info ? info->widget : NULL;
696 tmp_list = tmp_list->next;
702 /*************************************************************
704 * Notify the drag source that the transfer of data
707 * context: The drag context for this drag
708 * success: Was the data successfully transferred?
709 * time: The timestamp to use when notifying the destination.
711 *************************************************************/
714 gtk_drag_finish (GdkDragContext *context,
719 GdkAtom target = GDK_NONE;
721 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
722 g_return_if_fail (!context->is_source);
726 target = gdk_atom_intern ("DELETE", FALSE);
728 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
730 target = gdk_atom_intern (success ?
731 "XmTRANSFER_SUCCESS" :
732 "XmTRANSFER_FAILURE",
736 if (target != GDK_NONE)
738 GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
740 g_object_ref (context);
742 g_object_set_data (G_OBJECT (selection_widget), "drag-context", context);
743 g_signal_connect (selection_widget, "selection_received",
744 G_CALLBACK (gtk_drag_selection_received),
747 gtk_selection_convert (selection_widget,
748 gdk_drag_get_selection (context),
753 if (!(success && del))
754 gdk_drop_finish (context, success, time);
757 /*************************************************************
758 * gtk_drag_highlight_expose:
759 * Callback for expose_event for highlighted widgets.
765 *************************************************************/
768 gtk_drag_highlight_expose (GtkWidget *widget,
769 GdkEventExpose *event,
772 gint x, y, width, height;
774 if (GTK_WIDGET_DRAWABLE (widget))
776 if (GTK_WIDGET_NO_WINDOW (widget))
778 x = widget->allocation.x;
779 y = widget->allocation.y;
780 width = widget->allocation.width;
781 height = widget->allocation.height;
787 gdk_drawable_get_size (widget->window, &width, &height);
790 gtk_paint_shadow (widget->style, widget->window,
791 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
793 x, y, width, height);
795 gdk_draw_rectangle (widget->window,
796 widget->style->black_gc,
798 x, y, width - 1, height - 1);
804 /*************************************************************
805 * gtk_drag_highlight:
806 * Highlight the given widget in the default manner.
810 *************************************************************/
813 gtk_drag_highlight (GtkWidget *widget)
815 g_return_if_fail (GTK_IS_WIDGET (widget));
817 g_signal_connect_after (widget, "expose_event",
818 G_CALLBACK (gtk_drag_highlight_expose),
821 gtk_widget_queue_draw (widget);
824 /*************************************************************
825 * gtk_drag_unhighlight:
826 * Refresh the given widget to remove the highlight.
830 *************************************************************/
833 gtk_drag_unhighlight (GtkWidget *widget)
835 g_return_if_fail (GTK_IS_WIDGET (widget));
837 g_signal_handlers_disconnect_by_func (widget,
838 gtk_drag_highlight_expose,
841 gtk_widget_queue_draw (widget);
845 gtk_drag_dest_set_internal (GtkWidget *widget,
846 GtkDragDestSite *site)
848 GtkDragDestSite *old_site;
850 g_return_if_fail (widget != NULL);
852 /* HACK, do this in the destroy */
853 old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
856 g_signal_handlers_disconnect_by_func (widget,
857 gtk_drag_dest_realized,
859 g_signal_handlers_disconnect_by_func (widget,
860 gtk_drag_dest_hierarchy_changed,
864 if (GTK_WIDGET_REALIZED (widget))
865 gtk_drag_dest_realized (widget);
867 g_signal_connect (widget, "realize",
868 G_CALLBACK (gtk_drag_dest_realized), site);
869 g_signal_connect (widget, "hierarchy_changed",
870 G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
872 g_object_set_data_full (G_OBJECT (widget), "gtk-drag-dest",
873 site, gtk_drag_dest_site_destroy);
877 /*************************************************************
879 * Register a drop site, and possibly add default behaviors.
882 * flags: Which types of default drag behavior to use
883 * targets: Table of targets that can be accepted
884 * n_targets: Number of of entries in targets
887 *************************************************************/
890 gtk_drag_dest_set (GtkWidget *widget,
891 GtkDestDefaults flags,
892 const GtkTargetEntry *targets,
894 GdkDragAction actions)
896 GtkDragDestSite *site;
898 g_return_if_fail (GTK_IS_WIDGET (widget));
900 site = g_new (GtkDragDestSite, 1);
903 site->have_drag = FALSE;
905 site->target_list = gtk_target_list_new (targets, n_targets);
907 site->target_list = NULL;
908 site->actions = actions;
909 site->do_proxy = FALSE;
910 site->proxy_window = NULL;
912 gtk_drag_dest_set_internal (widget, site);
915 /*************************************************************
916 * gtk_drag_dest_set_proxy:
917 * Set up this widget to proxy drags elsewhere.
920 * proxy_window: window to which forward drag events
921 * protocol: Drag protocol which the dest widget accepts
922 * use_coordinates: If true, send the same coordinates to the
923 * destination, because it is a embedded
926 *************************************************************/
929 gtk_drag_dest_set_proxy (GtkWidget *widget,
930 GdkWindow *proxy_window,
931 GdkDragProtocol protocol,
932 gboolean use_coordinates)
934 GtkDragDestSite *site;
936 g_return_if_fail (GTK_IS_WIDGET (widget));
937 g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
939 site = g_new (GtkDragDestSite, 1);
942 site->have_drag = FALSE;
943 site->target_list = NULL;
945 site->proxy_window = proxy_window;
947 g_object_ref (proxy_window);
948 site->do_proxy = TRUE;
949 site->proxy_protocol = protocol;
950 site->proxy_coords = use_coordinates;
952 gtk_drag_dest_set_internal (widget, site);
955 /*************************************************************
956 * gtk_drag_dest_unset
957 * Unregister this widget as a drag target.
961 *************************************************************/
964 gtk_drag_dest_unset (GtkWidget *widget)
966 g_return_if_fail (GTK_IS_WIDGET (widget));
968 g_object_set_data (G_OBJECT (widget), "gtk-drag-dest", NULL);
972 * gtk_drag_dest_get_target_list:
973 * @widget: a #GtkWidget
975 * Returns the list of targets this widget can accept from
978 * Return value: the #GtkTargetList, or %NULL if none
981 gtk_drag_dest_get_target_list (GtkWidget *widget)
983 GtkDragDestSite *site;
985 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
987 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
989 return site ? site->target_list : NULL;
993 * gtk_drag_dest_set_target_list:
994 * @widget: a #GtkWidget that's a drag destination
995 * @target_list: list of droppable targets, or %NULL for none
997 * Sets the target types that this widget can accept from drag-and-drop.
998 * The widget must first be made into a drag destination with
999 * gtk_drag_dest_set().
1002 gtk_drag_dest_set_target_list (GtkWidget *widget,
1003 GtkTargetList *target_list)
1005 GtkDragDestSite *site;
1007 g_return_if_fail (GTK_IS_WIDGET (widget));
1009 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1013 g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1014 "to make the widget into a drag destination");
1019 gtk_target_list_ref (target_list);
1021 if (site->target_list)
1022 gtk_target_list_unref (site->target_list);
1024 site->target_list = target_list;
1028 * gtk_drag_dest_add_text_targets:
1029 * @widget: a #GtkWidget that's a drag destination
1031 * Add the text targets supported by #GtkSelection to
1032 * the target list of the drag destination.
1037 gtk_drag_dest_add_text_targets (GtkWidget *widget)
1039 GtkTargetList *target_list;
1041 target_list = gtk_drag_dest_get_target_list (widget);
1043 target_list = gtk_target_list_new (NULL, 0);
1044 gtk_target_list_add_text_targets (target_list);
1045 gtk_drag_dest_set_target_list (widget, target_list);
1048 /*************************************************************
1049 * _gtk_drag_dest_handle_event:
1050 * Called from widget event handling code on Drag events
1054 * toplevel: Toplevel widget that received the event
1057 *************************************************************/
1060 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1063 GtkDragDestInfo *info;
1064 GdkDragContext *context;
1066 g_return_if_fail (toplevel != NULL);
1067 g_return_if_fail (event != NULL);
1069 context = event->dnd.context;
1071 info = gtk_drag_get_dest_info (context, TRUE);
1073 /* Find the widget for the event */
1074 switch (event->type)
1076 case GDK_DRAG_ENTER:
1079 case GDK_DRAG_LEAVE:
1082 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1083 info->widget = NULL;
1087 case GDK_DRAG_MOTION:
1088 case GDK_DROP_START:
1090 GtkDragFindData data;
1093 if (event->type == GDK_DROP_START)
1095 info->dropped = TRUE;
1096 /* We send a leave here so that the widget unhighlights
1101 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1102 info->widget = NULL;
1106 #ifdef GDK_WINDOWING_X11
1107 /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
1109 * Currently gdk_window_get_position doesn't provide reliable
1110 * information for embedded windows, so we call the much more
1111 * expensive gdk_window_get_origin().
1113 if (GTK_IS_PLUG (toplevel))
1114 gdk_window_get_origin (toplevel->window, &tx, &ty);
1116 #endif /* GDK_WINDOWING_X11 */
1117 gdk_window_get_position (toplevel->window, &tx, &ty);
1119 data.x = event->dnd.x_root - tx;
1120 data.y = event->dnd.y_root - ty;
1121 data.context = context;
1124 data.toplevel = TRUE;
1125 data.callback = (event->type == GDK_DRAG_MOTION) ?
1126 gtk_drag_dest_motion : gtk_drag_dest_drop;
1127 data.time = event->dnd.time;
1129 gtk_drag_find_widget (toplevel, &data);
1131 if (info->widget && !data.found)
1133 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1134 info->widget = NULL;
1139 if (event->type == GDK_DRAG_MOTION)
1142 gdk_drag_status (context, 0, event->dnd.time);
1144 else if (event->type == GDK_DROP_START && !info->proxy_source)
1146 gdk_drop_reply (context, data.found, event->dnd.time);
1147 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1148 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1154 g_assert_not_reached ();
1159 * gtk_drag_dest_find_target:
1160 * @widget: drag destination widget
1161 * @context: drag context
1162 * @target_list: list of droppable targets, or %NULL to use
1163 * gtk_drag_dest_get_target_list (@widget).
1165 * Looks for a match between @context->targets and the
1166 * @dest_target_list, returning the first matching target, otherwise
1167 * returning %GDK_NONE. @dest_target_list should usually be the return
1168 * value from gtk_drag_dest_get_target_list(), but some widgets may
1169 * have different valid targets for different parts of the widget; in
1170 * that case, they will have to implement a drag_motion handler that
1171 * passes the correct target list to this function.
1173 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1176 gtk_drag_dest_find_target (GtkWidget *widget,
1177 GdkDragContext *context,
1178 GtkTargetList *target_list)
1181 GList *tmp_source = NULL;
1182 GtkWidget *source_widget;
1184 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1185 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1186 g_return_val_if_fail (!context->is_source, GDK_NONE);
1189 source_widget = gtk_drag_get_source_widget (context);
1191 if (target_list == NULL)
1192 target_list = gtk_drag_dest_get_target_list (widget);
1194 if (target_list == NULL)
1197 tmp_target = target_list->list;
1200 GtkTargetPair *pair = tmp_target->data;
1201 tmp_source = context->targets;
1204 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1206 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1207 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1208 return pair->target;
1212 tmp_source = tmp_source->next;
1214 tmp_target = tmp_target->next;
1221 gtk_drag_selection_received (GtkWidget *widget,
1222 GtkSelectionData *selection_data,
1226 GdkDragContext *context;
1227 GtkDragDestInfo *info;
1228 GtkWidget *drop_widget;
1232 context = g_object_get_data (G_OBJECT (widget), "drag-context");
1233 info = gtk_drag_get_dest_info (context, FALSE);
1235 if (info->proxy_data &&
1236 info->proxy_data->target == selection_data->target)
1238 gtk_selection_data_set (info->proxy_data,
1239 selection_data->type,
1240 selection_data->format,
1241 selection_data->data,
1242 selection_data->length);
1247 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1249 gtk_drag_finish (context, TRUE, FALSE, time);
1251 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1252 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1258 GtkDragDestSite *site;
1260 site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1262 if (site && site->target_list)
1266 if (gtk_target_list_find (site->target_list,
1267 selection_data->target,
1270 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1271 selection_data->length >= 0)
1272 g_signal_emit_by_name (drop_widget,
1273 "drag_data_received",
1274 context, info->drop_x, info->drop_y,
1281 g_signal_emit_by_name (drop_widget,
1282 "drag_data_received",
1283 context, info->drop_x, info->drop_y,
1288 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1291 gtk_drag_finish (context,
1292 (selection_data->length >= 0),
1293 (context->action == GDK_ACTION_MOVE),
1297 g_object_unref (drop_widget);
1300 g_signal_handlers_disconnect_by_func (widget,
1301 gtk_drag_selection_received,
1304 g_object_set_data (G_OBJECT (widget), "drag-context", NULL);
1305 g_object_unref (context);
1307 gtk_drag_release_ipc_widget (widget);
1311 prepend_and_ref_widget (GtkWidget *widget,
1314 GSList **slist_p = data;
1316 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1319 /*************************************************************
1320 * gtk_drag_find_widget:
1321 * Recursive callback used to locate widgets for
1322 * DRAG_MOTION and DROP_START events.
1326 *************************************************************/
1329 gtk_drag_find_widget (GtkWidget *widget,
1330 GtkDragFindData *data)
1332 GtkAllocation new_allocation;
1333 gint allocation_to_window_x = 0;
1334 gint allocation_to_window_y = 0;
1338 if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget))
1341 /* Note that in the following code, we only count the
1342 * position as being inside a WINDOW widget if it is inside
1343 * widget->window; points that are outside of widget->window
1344 * but within the allocation are not counted. This is consistent
1345 * with the way we highlight drag targets.
1347 * data->x,y are relative to widget->parent->window (if
1348 * widget is not a toplevel, widget->window otherwise).
1349 * We compute the allocation of widget in the same coordinates,
1350 * clipping to widget->window, and all intermediate
1351 * windows. If data->x,y is inside that, then we translate
1352 * our coordinates to be relative to widget->window and
1355 new_allocation = widget->allocation;
1360 GdkWindow *window = widget->window;
1362 /* Compute the offset from allocation-relative to
1363 * window-relative coordinates.
1365 allocation_to_window_x = widget->allocation.x;
1366 allocation_to_window_y = widget->allocation.y;
1368 if (!GTK_WIDGET_NO_WINDOW (widget))
1370 /* The allocation is relative to the parent window for
1371 * window widgets, not to widget->window.
1373 gdk_window_get_position (window, &tx, &ty);
1375 allocation_to_window_x -= tx;
1376 allocation_to_window_y -= ty;
1379 new_allocation.x = 0 + allocation_to_window_x;
1380 new_allocation.y = 0 + allocation_to_window_y;
1382 while (window && window != widget->parent->window)
1384 GdkRectangle window_rect = { 0, 0, 0, 0 };
1386 gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
1388 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1390 gdk_window_get_position (window, &tx, &ty);
1391 new_allocation.x += tx;
1393 new_allocation.y += ty;
1396 window = gdk_window_get_parent (window);
1399 if (!window) /* Window and widget heirarchies didn't match. */
1403 if (data->toplevel ||
1404 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1405 (data->x < new_allocation.x + new_allocation.width) &&
1406 (data->y < new_allocation.y + new_allocation.height)))
1408 /* First, check if the drag is in a valid drop site in
1409 * one of our children
1411 if (GTK_IS_CONTAINER (widget))
1413 GtkDragFindData new_data = *data;
1414 GSList *children = NULL;
1417 new_data.x -= x_offset;
1418 new_data.y -= y_offset;
1419 new_data.found = FALSE;
1420 new_data.toplevel = FALSE;
1422 /* need to reference children temporarily in case the
1423 * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1425 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1426 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1428 if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1429 gtk_drag_find_widget (tmp_list->data, &new_data);
1430 g_object_unref (tmp_list->data);
1432 g_slist_free (children);
1434 data->found = new_data.found;
1437 /* If not, and this widget is registered as a drop site, check to
1438 * emit "drag_motion" to check if we are actually in
1442 g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1444 data->found = data->callback (widget,
1446 data->x - x_offset - allocation_to_window_x,
1447 data->y - y_offset - allocation_to_window_y,
1449 /* If so, send a "drag_leave" to the last widget */
1452 if (data->info->widget && data->info->widget != widget)
1454 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1456 data->info->widget = widget;
1463 gtk_drag_proxy_begin (GtkWidget *widget,
1464 GtkDragDestInfo *dest_info,
1467 GtkDragSourceInfo *source_info;
1469 GdkDragContext *context;
1470 GtkWidget *ipc_widget;
1472 if (dest_info->proxy_source)
1474 gdk_drag_abort (dest_info->proxy_source->context, time);
1475 gtk_drag_source_info_destroy (dest_info->proxy_source);
1476 dest_info->proxy_source = NULL;
1479 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1480 context = gdk_drag_begin (ipc_widget->window,
1481 dest_info->context->targets);
1483 source_info = gtk_drag_get_source_info (context, TRUE);
1485 source_info->ipc_widget = ipc_widget;
1486 source_info->widget = gtk_widget_ref (widget);
1488 source_info->target_list = gtk_target_list_new (NULL, 0);
1489 tmp_list = dest_info->context->targets;
1492 gtk_target_list_add (source_info->target_list,
1493 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1494 tmp_list = tmp_list->next;
1497 source_info->proxy_dest = dest_info;
1499 g_signal_connect (ipc_widget,
1501 G_CALLBACK (gtk_drag_selection_get),
1504 dest_info->proxy_source = source_info;
1508 gtk_drag_dest_info_destroy (gpointer data)
1510 GtkDragDestInfo *info = data;
1515 static GtkDragDestInfo *
1516 gtk_drag_get_dest_info (GdkDragContext *context,
1519 GtkDragDestInfo *info;
1520 static GQuark info_quark = 0;
1522 info_quark = g_quark_from_static_string ("gtk-dest-info");
1524 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1525 if (!info && create)
1527 info = g_new (GtkDragDestInfo, 1);
1528 info->widget = NULL;
1529 info->context = context;
1530 info->proxy_source = NULL;
1531 info->proxy_data = NULL;
1532 info->dropped = FALSE;
1533 info->proxy_drop_wait = FALSE;
1534 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1535 info, gtk_drag_dest_info_destroy);
1541 static GQuark dest_info_quark = 0;
1543 static GtkDragSourceInfo *
1544 gtk_drag_get_source_info (GdkDragContext *context,
1547 GtkDragSourceInfo *info;
1548 if (!dest_info_quark)
1549 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1551 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1552 if (!info && create)
1554 info = g_new0 (GtkDragSourceInfo, 1);
1555 info->context = context;
1556 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1563 gtk_drag_clear_source_info (GdkDragContext *context)
1565 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1569 gtk_drag_dest_realized (GtkWidget *widget)
1571 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1573 if (GTK_WIDGET_TOPLEVEL (toplevel))
1574 gdk_window_register_dnd (toplevel->window);
1578 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1579 GtkWidget *previous_toplevel)
1581 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1583 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1584 gdk_window_register_dnd (toplevel->window);
1588 gtk_drag_dest_site_destroy (gpointer data)
1590 GtkDragDestSite *site = data;
1592 if (site->proxy_window)
1593 g_object_unref (site->proxy_window);
1595 if (site->target_list)
1596 gtk_target_list_unref (site->target_list);
1602 * Default drag handlers
1605 gtk_drag_dest_leave (GtkWidget *widget,
1606 GdkDragContext *context,
1609 GtkDragDestSite *site;
1611 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1612 g_return_if_fail (site != NULL);
1616 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1618 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1620 gdk_drag_abort (info->proxy_source->context, time);
1621 gtk_drag_source_info_destroy (info->proxy_source);
1622 info->proxy_source = NULL;
1629 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1630 gtk_drag_unhighlight (widget);
1632 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1633 g_signal_emit_by_name (widget, "drag_leave",
1636 site->have_drag = FALSE;
1641 gtk_drag_dest_motion (GtkWidget *widget,
1642 GdkDragContext *context,
1647 GtkDragDestSite *site;
1648 GdkDragAction action = 0;
1651 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1652 g_return_val_if_fail (site != NULL, FALSE);
1657 GdkEvent *current_event;
1658 GdkWindow *dest_window;
1659 GdkDragProtocol proto;
1661 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1663 if (!info->proxy_source || info->proxy_source->widget != widget)
1664 gtk_drag_proxy_begin (widget, info, time);
1666 current_event = gtk_get_current_event ();
1668 if (site->proxy_window)
1670 dest_window = site->proxy_window;
1671 proto = site->proxy_protocol;
1675 gdk_drag_find_window_for_screen (info->proxy_source->context,
1677 gdk_drawable_get_screen (current_event->dnd.window),
1678 current_event->dnd.x_root,
1679 current_event->dnd.y_root,
1680 &dest_window, &proto);
1683 gdk_drag_motion (info->proxy_source->context,
1685 current_event->dnd.x_root,
1686 current_event->dnd.y_root,
1687 context->suggested_action,
1688 context->actions, time);
1690 if (!site->proxy_window && dest_window)
1691 g_object_unref (dest_window);
1693 selection = gdk_drag_get_selection (info->proxy_source->context);
1695 selection != gdk_drag_get_selection (info->context))
1696 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1698 gdk_event_free (current_event);
1703 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1705 if (context->suggested_action & site->actions)
1706 action = context->suggested_action;
1713 if ((site->actions & (1 << i)) &&
1714 (context->actions & (1 << i)))
1722 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1724 if (!site->have_drag)
1726 site->have_drag = TRUE;
1727 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1728 gtk_drag_highlight (widget);
1731 gdk_drag_status (context, action, time);
1735 gdk_drag_status (context, 0, time);
1740 g_signal_emit_by_name (widget, "drag_motion",
1741 context, x, y, time, &retval);
1743 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1747 gtk_drag_dest_drop (GtkWidget *widget,
1748 GdkDragContext *context,
1753 GtkDragDestSite *site;
1754 GtkDragDestInfo *info;
1756 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1757 g_return_val_if_fail (site != NULL, FALSE);
1759 info = gtk_drag_get_dest_info (context, FALSE);
1760 g_return_val_if_fail (info != NULL, FALSE);
1767 if (info->proxy_source ||
1768 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1770 gtk_drag_drop (info->proxy_source, time);
1774 /* We need to synthesize a motion event, wait for a status,
1775 * and, if we get a good one, do a drop.
1778 GdkEvent *current_event;
1780 GdkWindow *dest_window;
1781 GdkDragProtocol proto;
1783 gtk_drag_proxy_begin (widget, info, time);
1784 info->proxy_drop_wait = TRUE;
1785 info->proxy_drop_time = time;
1787 current_event = gtk_get_current_event ();
1789 if (site->proxy_window)
1791 dest_window = site->proxy_window;
1792 proto = site->proxy_protocol;
1796 gdk_drag_find_window_for_screen (info->proxy_source->context,
1798 gdk_drawable_get_screen (current_event->dnd.window),
1799 current_event->dnd.x_root,
1800 current_event->dnd.y_root,
1801 &dest_window, &proto);
1804 gdk_drag_motion (info->proxy_source->context,
1806 current_event->dnd.x_root,
1807 current_event->dnd.y_root,
1808 context->suggested_action,
1809 context->actions, time);
1811 if (!site->proxy_window && dest_window)
1812 g_object_unref (dest_window);
1814 selection = gdk_drag_get_selection (info->proxy_source->context);
1816 selection != gdk_drag_get_selection (info->context))
1817 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1819 gdk_event_free (current_event);
1828 if (site->flags & GTK_DEST_DEFAULT_DROP)
1830 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1832 if (target == GDK_NONE)
1834 gtk_drag_finish (context, FALSE, FALSE, time);
1838 gtk_drag_get_data (widget, context, target, time);
1841 g_signal_emit_by_name (widget, "drag_drop",
1842 context, x, y, time, &retval);
1844 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1852 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
1853 * so that we can set the icon from the source site information
1855 static GdkDragContext *
1856 gtk_drag_begin_internal (GtkWidget *widget,
1857 GtkDragSourceSite *site,
1858 GtkTargetList *target_list,
1859 GdkDragAction actions,
1863 GtkDragSourceInfo *info;
1864 GList *targets = NULL;
1866 guint32 time = GDK_CURRENT_TIME;
1867 GdkDragAction possible_actions, suggested_action;
1868 GdkDragContext *context;
1869 GtkWidget *ipc_widget;
1872 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1874 gtk_drag_get_event_actions (event, button, actions,
1875 &suggested_action, &possible_actions);
1877 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), suggested_action);
1880 time = gdk_event_get_time (event);
1882 if (gdk_pointer_grab (ipc_widget->window, FALSE,
1883 GDK_POINTER_MOTION_MASK |
1884 GDK_BUTTON_RELEASE_MASK, NULL,
1887 gtk_drag_release_ipc_widget (ipc_widget);
1891 if (gdk_keyboard_grab (ipc_widget->window, FALSE, time) != 0)
1893 gtk_drag_release_ipc_widget (ipc_widget);
1897 /* We use a GTK grab here to override any grabs that the widget
1898 * we are dragging from might have held
1900 gtk_grab_add (ipc_widget);
1902 tmp_list = g_list_last (target_list->list);
1905 GtkTargetPair *pair = tmp_list->data;
1906 targets = g_list_prepend (targets,
1907 GINT_TO_POINTER (pair->target));
1908 tmp_list = tmp_list->prev;
1911 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1913 context = gdk_drag_begin (ipc_widget->window, targets);
1914 g_list_free (targets);
1916 info = gtk_drag_get_source_info (context, TRUE);
1918 info->ipc_widget = ipc_widget;
1919 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", info);
1921 info->widget = gtk_widget_ref (widget);
1923 info->button = button;
1924 info->cursor = cursor;
1925 info->target_list = target_list;
1926 gtk_target_list_ref (target_list);
1928 info->possible_actions = actions;
1930 info->status = GTK_DRAG_STATUS_DRAG;
1931 info->last_event = NULL;
1932 info->selections = NULL;
1933 info->icon_window = NULL;
1934 info->destroy_icon = FALSE;
1936 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1937 * the drag icon, it will be in the right place
1939 if (event && event->type == GDK_MOTION_NOTIFY)
1941 info->cur_screen = gtk_widget_get_screen (widget);
1942 info->cur_x = event->motion.x_root;
1943 info->cur_y = event->motion.y_root;
1947 gdk_display_get_pointer (gtk_widget_get_display (widget),
1948 &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
1951 g_signal_emit_by_name (widget, "drag_begin",
1954 /* Ensure that we have an icon before we start the drag; the
1955 * application may have set one in ::drag_begin, or it may
1958 if (!info->icon_window)
1960 if (!site || site->icon_type == GTK_IMAGE_EMPTY)
1961 gtk_drag_set_icon_default (context);
1963 switch (site->icon_type)
1965 case GTK_IMAGE_PIXMAP:
1966 gtk_drag_set_icon_pixmap (context,
1968 site->icon_data.pixmap.pixmap,
1972 case GTK_IMAGE_PIXBUF:
1973 gtk_drag_set_icon_pixbuf (context,
1974 site->icon_data.pixbuf.pixbuf,
1977 case GTK_IMAGE_STOCK:
1978 gtk_drag_set_icon_stock (context,
1979 site->icon_data.stock.stock_id,
1982 case GTK_IMAGE_EMPTY:
1984 g_assert_not_reached();
1989 if (event && event->type == GDK_MOTION_NOTIFY)
1990 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1992 info->start_x = info->cur_x;
1993 info->start_y = info->cur_y;
1995 g_signal_connect (info->ipc_widget, "button_release_event",
1996 G_CALLBACK (gtk_drag_button_release_cb), info);
1997 g_signal_connect (info->ipc_widget, "motion_notify_event",
1998 G_CALLBACK (gtk_drag_motion_cb), info);
1999 g_signal_connect (info->ipc_widget, "key_press_event",
2000 G_CALLBACK (gtk_drag_key_cb), info);
2001 g_signal_connect (info->ipc_widget, "key_release_event",
2002 G_CALLBACK (gtk_drag_key_cb), info);
2003 g_signal_connect (info->ipc_widget, "selection_get",
2004 G_CALLBACK (gtk_drag_selection_get), info);
2006 info->have_grab = TRUE;
2007 info->grab_time = time;
2009 return info->context;
2014 * @widget: the source widget.
2015 * @targets: The targets (data formats) in which the
2016 * source can provide the data.
2017 * @actions: A bitmask of the allowed drag actions for this drag.
2018 * @button: The button the user clicked to start the drag.
2019 * @event: The event that triggered the start of the drag.
2021 * Initiates a drag on the source side. The function
2022 * only needs to be used when the application is
2023 * starting drags itself, and is not needed when
2024 * gtk_drag_source_set() is used.
2026 * Return value: the context for this drag.
2029 gtk_drag_begin (GtkWidget *widget,
2030 GtkTargetList *targets,
2031 GdkDragAction actions,
2035 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2036 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
2037 g_return_val_if_fail (targets != NULL, NULL);
2039 return gtk_drag_begin_internal (widget, NULL, targets,
2040 actions, button, event);
2043 /*************************************************************
2044 * gtk_drag_source_set:
2045 * Register a drop site, and possibly add default behaviors.
2048 * start_button_mask: Mask of allowed buttons to start drag
2049 * targets: Table of targets for this source
2051 * actions: Actions allowed for this source
2053 *************************************************************/
2056 gtk_drag_source_set (GtkWidget *widget,
2057 GdkModifierType start_button_mask,
2058 const GtkTargetEntry *targets,
2060 GdkDragAction actions)
2062 GtkDragSourceSite *site;
2064 g_return_if_fail (GTK_IS_WIDGET (widget));
2066 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2068 gtk_widget_add_events (widget,
2069 gtk_widget_get_events (widget) |
2070 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2071 GDK_BUTTON_MOTION_MASK);
2075 if (site->target_list)
2076 gtk_target_list_unref (site->target_list);
2080 site = g_new0 (GtkDragSourceSite, 1);
2082 site->icon_type = GTK_IMAGE_EMPTY;
2084 g_signal_connect (widget, "button_press_event",
2085 G_CALLBACK (gtk_drag_source_event_cb),
2087 g_signal_connect (widget, "motion_notify_event",
2088 G_CALLBACK (gtk_drag_source_event_cb),
2091 g_object_set_data_full (G_OBJECT (widget),
2093 site, gtk_drag_source_site_destroy);
2096 site->start_button_mask = start_button_mask;
2099 site->target_list = gtk_target_list_new (targets, n_targets);
2101 site->target_list = NULL;
2103 site->actions = actions;
2106 /*************************************************************
2107 * gtk_drag_source_unset
2108 * Unregister this widget as a drag source.
2112 *************************************************************/
2115 gtk_drag_source_unset (GtkWidget *widget)
2117 GtkDragSourceSite *site;
2119 g_return_if_fail (GTK_IS_WIDGET (widget));
2121 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2125 g_signal_handlers_disconnect_by_func (widget,
2126 gtk_drag_source_event_cb,
2128 g_signal_handlers_disconnect_by_func (widget,
2129 gtk_drag_source_event_cb,
2131 g_object_set_data (G_OBJECT (widget), "gtk-site-data", NULL);
2136 * gtk_drag_source_get_target_list:
2137 * @widget: a #GtkWidget
2139 * Gets the list of targets this widget can provide for
2142 * Return value: the #GtkTargetList, or %NULL if none
2147 gtk_drag_source_get_target_list (GtkWidget *widget)
2149 GtkDragSourceSite *site;
2151 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2153 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2155 return site ? site->target_list : NULL;
2159 * gtk_drag_source_set_target_list:
2160 * @widget: a #GtkWidget that's a drag source
2161 * @target_list: list of draggable targets, or %NULL for none
2163 * Changes the target types that this widget offers for drag-and-drop.
2164 * The widget must first be made into a drag source with
2165 * gtk_drag_source_set().
2170 gtk_drag_source_set_target_list (GtkWidget *widget,
2171 GtkTargetList *target_list)
2173 GtkDragSourceSite *site;
2175 g_return_if_fail (GTK_IS_WIDGET (widget));
2177 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2180 g_warning ("gtk_drag_source_set_target_list() requires the widget "
2181 "to already be a drag source.");
2186 gtk_target_list_ref (target_list);
2188 if (site->target_list)
2189 gtk_target_list_unref (site->target_list);
2191 site->target_list = target_list;
2195 * gtk_drag_source_add_text_targets:
2196 * @widget: a #GtkWidget that's is a drag source
2198 * Add the text targets supported by #GtkSelection to
2199 * the target list of the drag source.
2204 gtk_drag_source_add_text_targets (GtkWidget *widget)
2206 GtkTargetList *target_list;
2208 target_list = gtk_drag_source_get_target_list (widget);
2210 target_list = gtk_target_list_new (NULL, 0);
2211 gtk_target_list_add_text_targets (target_list);
2212 gtk_drag_source_set_target_list (widget, target_list);
2216 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2218 switch (site->icon_type)
2220 case GTK_IMAGE_EMPTY:
2222 case GTK_IMAGE_PIXMAP:
2223 if (site->icon_data.pixmap.pixmap)
2224 g_object_unref (site->icon_data.pixmap.pixmap);
2225 if (site->icon_mask)
2226 g_object_unref (site->icon_mask);
2228 case GTK_IMAGE_PIXBUF:
2229 g_object_unref (site->icon_data.pixbuf.pixbuf);
2231 case GTK_IMAGE_STOCK:
2232 g_free (site->icon_data.stock.stock_id);
2235 g_assert_not_reached();
2238 site->icon_type = GTK_IMAGE_EMPTY;
2241 g_object_unref (site->colormap);
2242 site->colormap = NULL;
2246 * gtk_drag_source_set_icon:
2247 * @widget: a #GtkWidget
2248 * @colormap: the colormap of the icon
2249 * @pixmap: the image data for the icon
2250 * @mask: the transparency mask for an image.
2252 * Sets the icon that will be used for drags from a particular widget
2253 * from a pixmap/mask. GTK+ retains references for the arguments, and
2254 * will release them when they are no longer needed.
2255 * Use gtk_drag_source_set_icon_pixbuf() instead.
2258 gtk_drag_source_set_icon (GtkWidget *widget,
2259 GdkColormap *colormap,
2263 GtkDragSourceSite *site;
2265 g_return_if_fail (GTK_IS_WIDGET (widget));
2266 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2267 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2268 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2270 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2271 g_return_if_fail (site != NULL);
2273 g_object_ref (colormap);
2274 g_object_ref (pixmap);
2276 g_object_ref (mask);
2278 gtk_drag_source_unset_icon (site);
2280 site->icon_type = GTK_IMAGE_PIXMAP;
2282 site->icon_data.pixmap.pixmap = pixmap;
2283 site->icon_mask = mask;
2284 site->colormap = colormap;
2288 * gtk_drag_source_set_icon_pixbuf:
2289 * @widget: a #GtkWidget
2290 * @pixbuf: the #GdkPixbuf for the drag icon
2292 * Sets the icon that will be used for drags from a particular widget
2293 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2294 * release it when it is no longer needed.
2297 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2300 GtkDragSourceSite *site;
2302 g_return_if_fail (GTK_IS_WIDGET (widget));
2303 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2305 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2306 g_return_if_fail (site != NULL);
2307 g_object_ref (pixbuf);
2309 gtk_drag_source_unset_icon (site);
2311 site->icon_type = GTK_IMAGE_PIXBUF;
2312 site->icon_data.pixbuf.pixbuf = pixbuf;
2316 * gtk_drag_source_set_icon_stock:
2317 * @widget: a #GtkWidget
2318 * @stock_id: the ID of the stock icon to use
2320 * Sets the icon that will be used for drags from a particular source
2324 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2325 const gchar *stock_id)
2327 GtkDragSourceSite *site;
2329 g_return_if_fail (GTK_IS_WIDGET (widget));
2330 g_return_if_fail (stock_id != NULL);
2332 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2333 g_return_if_fail (site != NULL);
2335 gtk_drag_source_unset_icon (site);
2337 site->icon_type = GTK_IMAGE_STOCK;
2338 site->icon_data.stock.stock_id = g_strdup (stock_id);
2342 gtk_drag_get_icon (GtkDragSourceInfo *info,
2343 GtkWidget **icon_window,
2347 if (get_can_change_screen (info->icon_window))
2348 gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2351 if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2353 if (!info->fallback_icon)
2355 gint save_hot_x, save_hot_y;
2356 gboolean save_destroy_icon;
2357 GtkWidget *save_icon_window;
2359 /* HACK to get the appropriate icon
2361 save_icon_window = info->icon_window;
2362 save_hot_x = info->hot_x;
2363 save_hot_y = info->hot_x;
2364 save_destroy_icon = info->destroy_icon;
2366 info->icon_window = NULL;
2367 gtk_drag_set_icon_default (info->context);
2368 info->fallback_icon = info->icon_window;
2370 info->icon_window = save_icon_window;
2371 info->hot_x = save_hot_x;
2372 info->hot_y = save_hot_y;
2373 info->destroy_icon = save_destroy_icon;
2376 gtk_widget_hide (info->icon_window);
2378 *icon_window = info->fallback_icon;
2379 gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2381 if (!default_icon_pixmap)
2388 *hot_x = default_icon_hot_x;
2389 *hot_y = default_icon_hot_y;
2394 if (info->fallback_icon)
2395 gtk_widget_hide (info->fallback_icon);
2397 *icon_window = info->icon_window;
2398 *hot_x = info->hot_x;
2399 *hot_y = info->hot_y;
2404 gtk_drag_update_icon (GtkDragSourceInfo *info)
2406 if (info->icon_window)
2408 GtkWidget *icon_window;
2411 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2413 gtk_window_move (GTK_WINDOW (icon_window),
2414 info->cur_x - hot_x,
2415 info->cur_y - hot_y);
2417 if (GTK_WIDGET_VISIBLE (icon_window))
2418 gdk_window_raise (icon_window->window);
2420 gtk_widget_show (icon_window);
2425 gtk_drag_set_icon_window (GdkDragContext *context,
2429 gboolean destroy_on_release)
2431 GtkDragSourceInfo *info;
2433 info = gtk_drag_get_source_info (context, FALSE);
2434 gtk_drag_remove_icon (info);
2437 gtk_widget_ref (widget);
2439 info->icon_window = widget;
2440 info->hot_x = hot_x;
2441 info->hot_y = hot_y;
2442 info->destroy_icon = destroy_on_release;
2444 gtk_drag_update_icon (info);
2448 * gtk_drag_set_icon_widget:
2449 * @context: the context for a drag. (This must be called
2450 with a context for the source side of a drag)
2451 * @widget: a toplevel window to use as an icon.
2452 * @hot_x: the X offset within @widget of the hotspot.
2453 * @hot_y: the Y offset within @widget of the hotspot.
2455 * Changes the icon for a widget to a given widget. GTK+
2456 * will not destroy the icon, so if you don't want
2457 * it to persist, you should connect to the "drag_end"
2458 * signal and destroy it yourself.
2461 gtk_drag_set_icon_widget (GdkDragContext *context,
2466 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2467 g_return_if_fail (context->is_source);
2468 g_return_if_fail (GTK_IS_WIDGET (widget));
2470 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2474 icon_window_realize (GtkWidget *window,
2480 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
2481 gtk_widget_get_colormap (window),
2482 &pixmap, &mask, 128);
2484 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2487 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2489 g_object_unref (pixmap);
2492 g_object_unref (mask);
2496 set_icon_stock_pixbuf (GdkDragContext *context,
2497 const gchar *stock_id,
2506 g_return_if_fail (context != NULL);
2507 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2508 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2510 screen = gdk_drawable_get_screen (context->source_window);
2512 /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
2513 gtk_widget_push_colormap (NULL);
2514 window = gtk_window_new (GTK_WINDOW_POPUP);
2515 gtk_window_set_screen (GTK_WINDOW (window), screen);
2516 set_can_change_screen (window, TRUE);
2517 gtk_widget_pop_colormap ();
2519 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2520 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2524 pixbuf = gtk_widget_render_icon (window, stock_id,
2525 GTK_ICON_SIZE_DND, NULL);
2529 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2530 gtk_widget_destroy (window);
2536 g_object_ref (pixbuf);
2538 width = gdk_pixbuf_get_width (pixbuf);
2539 height = gdk_pixbuf_get_width (pixbuf);
2541 gtk_widget_set_size_request (window,
2542 gdk_pixbuf_get_width (pixbuf),
2543 gdk_pixbuf_get_height (pixbuf));
2545 g_signal_connect_closure (window, "realize",
2546 g_cclosure_new (G_CALLBACK (icon_window_realize),
2548 (GClosureNotify)g_object_unref),
2551 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2555 * gtk_drag_set_icon_pixbuf:
2556 * @context: the context for a drag. (This must be called
2557 * with a context for the source side of a drag)
2558 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2559 * @hot_x: the X offset within @widget of the hotspot.
2560 * @hot_y: the Y offset within @widget of the hotspot.
2562 * Sets @pixbuf as the icon for a given drag.
2565 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2570 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2571 g_return_if_fail (context->is_source);
2572 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2574 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2578 * gtk_drag_set_icon_stock:
2579 * @context: the context for a drag. (This must be called
2580 * with a context for the source side of a drag)
2581 * @stock_id: the ID of the stock icon to use for the drag.
2582 * @hot_x: the X offset within the icon of the hotspot.
2583 * @hot_y: the Y offset within the icon of the hotspot.
2585 * Sets the the icon for a given drag from a stock ID.
2588 gtk_drag_set_icon_stock (GdkDragContext *context,
2589 const gchar *stock_id,
2593 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2594 g_return_if_fail (context->is_source);
2595 g_return_if_fail (stock_id != NULL);
2597 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2601 * gtk_drag_set_icon_pixmap:
2602 * @context: the context for a drag. (This must be called
2603 * with a context for the source side of a drag)
2604 * @colormap: the colormap of the icon
2605 * @pixmap: the image data for the icon
2606 * @mask: the transparency mask for the icon
2607 * @hot_x: the X offset within @pixmap of the hotspot.
2608 * @hot_y: the Y offset within @pixmap of the hotspot.
2610 * Sets @pixmap as the icon for a given drag. GTK+ retains
2611 * references for the arguments, and will release them when
2612 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2613 * will be more convenient to use.
2616 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2617 GdkColormap *colormap,
2627 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2628 g_return_if_fail (context->is_source);
2629 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2630 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2631 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2633 screen = gdk_colormap_get_screen (colormap);
2635 g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
2636 g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
2638 gdk_drawable_get_size (pixmap, &width, &height);
2640 gtk_widget_push_colormap (colormap);
2642 window = gtk_window_new (GTK_WINDOW_POPUP);
2643 gtk_window_set_screen (GTK_WINDOW (window), screen);
2644 set_can_change_screen (window, FALSE);
2645 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2646 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2648 gtk_widget_pop_colormap ();
2650 gtk_widget_set_size_request (window, width, height);
2651 gtk_widget_realize (window);
2653 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2656 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2658 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2662 * gtk_drag_set_icon_default:
2663 * @context: the context for a drag. (This must be called
2664 with a context for the source side of a drag)
2666 * Sets the icon for a particular drag to the default
2670 gtk_drag_set_icon_default (GdkDragContext *context)
2672 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2673 g_return_if_fail (context->is_source);
2675 if (!default_icon_pixmap)
2676 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2678 gtk_drag_set_icon_pixmap (context,
2679 default_icon_colormap,
2680 default_icon_pixmap,
2683 default_icon_hot_y);
2687 * gtk_drag_set_default_icon:
2688 * @colormap: the colormap of the icon
2689 * @pixmap: the image data for the icon
2690 * @mask: the transparency mask for an image.
2691 * @hot_x: The X offset within @widget of the hotspot.
2692 * @hot_y: The Y offset within @widget of the hotspot.
2694 * Changes the default drag icon. GTK+ retains references for the
2695 * arguments, and will release them when they are no longer needed.
2696 * This function is obsolete. The default icon should now be changed
2697 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2700 gtk_drag_set_default_icon (GdkColormap *colormap,
2706 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2707 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2708 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2710 if (default_icon_colormap)
2711 g_object_unref (default_icon_colormap);
2712 if (default_icon_pixmap)
2713 g_object_unref (default_icon_pixmap);
2714 if (default_icon_mask)
2715 g_object_unref (default_icon_mask);
2717 default_icon_colormap = colormap;
2718 g_object_ref (colormap);
2720 default_icon_pixmap = pixmap;
2721 g_object_ref (pixmap);
2723 default_icon_mask = mask;
2725 g_object_ref (mask);
2727 default_icon_hot_x = hot_x;
2728 default_icon_hot_y = hot_y;
2732 /*************************************************************
2733 * _gtk_drag_source_handle_event:
2734 * Called from widget event handling code on Drag events
2738 * toplevel: Toplevel widget that received the event
2741 *************************************************************/
2744 _gtk_drag_source_handle_event (GtkWidget *widget,
2747 GtkDragSourceInfo *info;
2748 GdkDragContext *context;
2750 g_return_if_fail (widget != NULL);
2751 g_return_if_fail (event != NULL);
2753 context = event->dnd.context;
2754 info = gtk_drag_get_source_info (context, FALSE);
2758 switch (event->type)
2760 case GDK_DRAG_STATUS:
2764 if (info->proxy_dest)
2766 if (!event->dnd.send_event)
2768 if (info->proxy_dest->proxy_drop_wait)
2770 gboolean result = context->action != 0;
2772 /* Aha - we can finally pass the MOTIF DROP on... */
2773 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2775 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2777 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2781 gdk_drag_status (info->proxy_dest->context,
2782 event->dnd.context->action,
2787 else if (info->have_grab)
2789 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2790 event->dnd.context->action);
2791 if (info->cursor != cursor)
2793 gdk_pointer_grab (widget->window, FALSE,
2794 GDK_POINTER_MOTION_MASK |
2795 GDK_BUTTON_RELEASE_MASK,
2797 cursor, info->grab_time);
2798 info->cursor = cursor;
2801 if (info->last_event)
2802 gtk_drag_add_update_idle (info);
2807 case GDK_DROP_FINISHED:
2808 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2811 g_assert_not_reached ();
2815 /*************************************************************
2816 * gtk_drag_source_check_selection:
2817 * Check if we've set up handlers/claimed the selection
2818 * for a given drag. If not, add them.
2822 *************************************************************/
2825 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2831 tmp_list = info->selections;
2834 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2836 tmp_list = tmp_list->next;
2839 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2843 info->selections = g_list_prepend (info->selections,
2844 GUINT_TO_POINTER (selection));
2846 tmp_list = info->target_list->list;
2849 GtkTargetPair *pair = tmp_list->data;
2851 gtk_selection_add_target (info->ipc_widget,
2855 tmp_list = tmp_list->next;
2858 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2860 gtk_selection_add_target (info->ipc_widget,
2862 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2863 TARGET_MOTIF_SUCCESS);
2864 gtk_selection_add_target (info->ipc_widget,
2866 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2867 TARGET_MOTIF_FAILURE);
2870 gtk_selection_add_target (info->ipc_widget,
2872 gdk_atom_intern ("DELETE", FALSE),
2876 /*************************************************************
2877 * gtk_drag_drop_finished:
2878 * Clean up from the drag, and display snapback, if necessary.
2884 *************************************************************/
2887 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2891 gtk_drag_source_release_selections (info, time);
2893 if (info->proxy_dest)
2895 /* The time from the event isn't reliable for Xdnd drags */
2896 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2897 info->proxy_dest->proxy_drop_time);
2898 gtk_drag_source_info_destroy (info);
2904 gtk_drag_source_info_destroy (info);
2908 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2912 anim->n_steps = MAX (info->cur_x - info->start_x,
2913 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2914 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2916 info->cur_screen = gtk_widget_get_screen (info->widget);
2917 gtk_drag_update_icon (info);
2919 /* Mark the context as dead, so if the destination decides
2920 * to respond really late, we still are OK.
2922 gtk_drag_clear_source_info (info->context);
2923 g_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2929 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2932 GdkDisplay *display = gtk_widget_get_display (info->widget);
2933 GList *tmp_list = info->selections;
2937 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2938 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2939 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2941 tmp_list = tmp_list->next;
2944 g_list_free (info->selections);
2945 info->selections = NULL;
2948 /*************************************************************
2950 * Send a drop event.
2954 *************************************************************/
2957 gtk_drag_drop (GtkDragSourceInfo *info,
2960 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2962 GtkSelectionData selection_data;
2964 /* GTK+ traditionally has used application/x-rootwin-drop, but the
2965 * XDND spec specifies x-rootwindow-drop.
2967 GdkAtom target1 = gdk_atom_intern ("application/x-rootwindow-drop", FALSE);
2968 GdkAtom target2 = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2970 tmp_list = info->target_list->list;
2973 GtkTargetPair *pair = tmp_list->data;
2975 if (pair->target == target1 || pair->target == target2)
2977 selection_data.selection = GDK_NONE;
2978 selection_data.target = pair->target;
2979 selection_data.data = NULL;
2980 selection_data.length = -1;
2982 g_signal_emit_by_name (info->widget, "drag_data_get",
2983 info->context, &selection_data,
2987 /* FIXME: Should we check for length >= 0 here? */
2988 gtk_drag_drop_finished (info, TRUE, time);
2991 tmp_list = tmp_list->next;
2993 gtk_drag_drop_finished (info, FALSE, time);
2997 if (info->icon_window)
2998 gtk_widget_hide (info->icon_window);
3000 gdk_drag_drop (info->context, time);
3001 info->drop_timeout = g_timeout_add (DROP_ABORT_TIME,
3002 gtk_drag_abort_timeout,
3008 * Source side callbacks.
3012 gtk_drag_source_event_cb (GtkWidget *widget,
3016 GtkDragSourceSite *site;
3017 gboolean retval = FALSE;
3018 site = (GtkDragSourceSite *)data;
3020 switch (event->type)
3022 case GDK_BUTTON_PRESS:
3023 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3025 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
3026 site->x = event->button.x;
3027 site->y = event->button.y;
3031 case GDK_BUTTON_RELEASE:
3032 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3033 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
3036 case GDK_MOTION_NOTIFY:
3037 if (site->state & event->motion.state & site->start_button_mask)
3039 /* FIXME: This is really broken and can leave us
3045 if (site->state & event->motion.state &
3046 GDK_BUTTON1_MASK << (i - 1))
3050 if (gtk_drag_check_threshold (widget, site->x, site->y,
3051 event->motion.x, event->motion.y))
3053 GdkDragContext *context;
3056 context = gtk_drag_begin_internal (widget, site, site->target_list,
3065 default: /* hit for 2/3BUTTON_PRESS */
3073 gtk_drag_source_site_destroy (gpointer data)
3075 GtkDragSourceSite *site = data;
3077 if (site->target_list)
3078 gtk_target_list_unref (site->target_list);
3080 gtk_drag_source_unset_icon (site);
3085 gtk_drag_selection_get (GtkWidget *widget,
3086 GtkSelectionData *selection_data,
3091 GtkDragSourceInfo *info = data;
3092 static GdkAtom null_atom = GDK_NONE;
3096 null_atom = gdk_atom_intern ("NULL", FALSE);
3101 g_signal_emit_by_name (info->widget,
3104 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3106 case TARGET_MOTIF_SUCCESS:
3107 gtk_drag_drop_finished (info, TRUE, time);
3108 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3110 case TARGET_MOTIF_FAILURE:
3111 gtk_drag_drop_finished (info, FALSE, time);
3112 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3115 if (info->proxy_dest)
3117 /* This is sort of dangerous and needs to be thought
3120 info->proxy_dest->proxy_data = selection_data;
3121 gtk_drag_get_data (info->widget,
3122 info->proxy_dest->context,
3123 selection_data->target,
3126 info->proxy_dest->proxy_data = NULL;
3130 if (gtk_target_list_find (info->target_list,
3131 selection_data->target,
3134 g_signal_emit_by_name (info->widget, "drag_data_get",
3146 gtk_drag_anim_timeout (gpointer data)
3148 GtkDragAnim *anim = data;
3152 GDK_THREADS_ENTER ();
3154 if (anim->step == anim->n_steps)
3156 gtk_drag_source_info_destroy (anim->info);
3163 x = (anim->info->start_x * (anim->step + 1) +
3164 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3165 y = (anim->info->start_y * (anim->step + 1) +
3166 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3167 if (anim->info->icon_window)
3169 GtkWidget *icon_window;
3172 gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);
3174 gtk_window_move (GTK_WINDOW (icon_window),
3184 GDK_THREADS_LEAVE ();
3190 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3192 if (info->icon_window)
3194 gtk_widget_hide (info->icon_window);
3195 if (info->destroy_icon)
3196 gtk_widget_destroy (info->icon_window);
3198 if (info->fallback_icon)
3200 gtk_widget_destroy (info->fallback_icon);
3201 info->fallback_icon = NULL;
3204 g_object_unref (info->icon_window);
3205 info->icon_window = NULL;
3210 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3212 gtk_drag_remove_icon (info);
3214 if (!info->proxy_dest)
3215 g_signal_emit_by_name (info->widget, "drag_end",
3219 g_object_unref (info->widget);
3222 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3223 gtk_drag_button_release_cb,
3225 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3228 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3231 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3232 gtk_drag_selection_get,
3235 gtk_selection_remove_all (info->ipc_widget);
3236 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", NULL);
3237 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3238 gtk_drag_release_ipc_widget (info->ipc_widget);
3240 gtk_target_list_unref (info->target_list);
3242 gtk_drag_clear_source_info (info->context);
3243 g_object_unref (info->context);
3245 if (info->drop_timeout)
3246 g_source_remove (info->drop_timeout);
3252 gtk_drag_update_idle (gpointer data)
3254 GtkDragSourceInfo *info = data;
3255 GdkWindow *dest_window;
3256 GdkDragProtocol protocol;
3259 GdkDragAction action;
3260 GdkDragAction possible_actions;
3263 GDK_THREADS_ENTER ();
3265 info->update_idle = 0;
3267 time = gtk_drag_get_event_time (info->last_event);
3268 gtk_drag_get_event_actions (info->last_event,
3270 info->possible_actions,
3271 &action, &possible_actions);
3272 gtk_drag_update_icon (info);
3273 gdk_drag_find_window_for_screen (info->context,
3274 info->icon_window ? info->icon_window->window : NULL,
3275 info->cur_screen, info->cur_x, info->cur_y,
3276 &dest_window, &protocol);
3278 if (!gdk_drag_motion (info->context, dest_window, protocol,
3279 info->cur_x, info->cur_y, action,
3283 gdk_event_free ((GdkEvent *)info->last_event);
3284 info->last_event = NULL;
3288 g_object_unref (dest_window);
3290 selection = gdk_drag_get_selection (info->context);
3292 gtk_drag_source_check_selection (info, selection, time);
3294 GDK_THREADS_LEAVE ();
3300 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
3302 /* We use an idle lowerthan GDK_PRIORITY_REDRAW so that exposes
3303 * from the last move can catch up before we move again.
3305 if (!info->update_idle)
3306 info->update_idle = g_idle_add_full (GDK_PRIORITY_REDRAW + 5,
3307 gtk_drag_update_idle,
3314 * @info: DragSourceInfo for the drag
3315 * @screen: new screen
3316 * @x_root: new X position
3317 * @y_root: new y position
3318 * @event: event received requiring update
3320 * Updates the status of the drag; called when the
3321 * cursor moves or the modifier changes
3324 gtk_drag_update (GtkDragSourceInfo *info,
3330 info->cur_screen = screen;
3331 info->cur_x = x_root;
3332 info->cur_y = y_root;
3333 if (info->last_event)
3334 gdk_event_free ((GdkEvent *)info->last_event);
3335 info->last_event = gdk_event_copy ((GdkEvent *)event);
3337 gtk_drag_add_update_idle (info);
3340 /*************************************************************
3342 * Called when the user finishes to drag, either by
3343 * releasing the mouse, or by pressing Esc.
3345 * info: Source info for the drag
3346 * time: Timestamp for ending the drag
3348 *************************************************************/
3351 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3353 GdkEvent *send_event;
3354 GtkWidget *source_widget = info->widget;
3355 GdkDisplay *display = gtk_widget_get_display (source_widget);
3357 if (info->update_idle)
3359 g_source_remove (info->update_idle);
3360 info->update_idle = 0;
3363 if (info->last_event)
3365 gdk_event_free (info->last_event);
3366 info->last_event = NULL;
3369 info->have_grab = FALSE;
3371 gdk_display_pointer_ungrab (display, time);
3372 gdk_display_keyboard_ungrab (display, time);
3373 gtk_grab_remove (info->ipc_widget);
3375 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3376 gtk_drag_button_release_cb,
3378 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3381 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3385 /* Send on a release pair to the the original
3386 * widget to convince it to release its grab. We need to
3387 * call gtk_propagate_event() here, instead of
3388 * gtk_widget_event() because widget like GtkList may
3389 * expect propagation.
3392 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3393 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3394 send_event->button.send_event = TRUE;
3395 send_event->button.time = time;
3396 send_event->button.x = 0;
3397 send_event->button.y = 0;
3398 send_event->button.axes = NULL;
3399 send_event->button.state = 0;
3400 send_event->button.button = info->button;
3401 send_event->button.device = gdk_display_get_core_pointer (display);
3402 send_event->button.x_root = 0;
3403 send_event->button.y_root = 0;
3405 gtk_propagate_event (source_widget, send_event);
3406 gdk_event_free (send_event);
3409 /*************************************************************
3411 * Called on cancellation of a drag, either by the user
3412 * or programmatically.
3414 * info: Source info for the drag
3415 * time: Timestamp for ending the drag
3417 *************************************************************/
3420 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3422 gtk_drag_end (info, time);
3423 gdk_drag_abort (info->context, time);
3424 gtk_drag_drop_finished (info, FALSE, time);
3427 /*************************************************************
3428 * gtk_drag_motion_cb:
3429 * "motion_notify_event" callback during drag.
3433 *************************************************************/
3436 gtk_drag_motion_cb (GtkWidget *widget,
3437 GdkEventMotion *event,
3440 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3442 gint x_root, y_root;
3446 GdkDisplay *display = gtk_widget_get_display (widget);
3448 gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
3449 event->x_root = x_root;
3450 event->y_root = y_root;
3453 screen = gdk_event_get_screen ((GdkEvent *)event);
3455 gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
3460 /*************************************************************
3462 * "key_press/release_event" callback during drag.
3466 *************************************************************/
3469 gtk_drag_key_cb (GtkWidget *widget,
3473 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3474 GdkModifierType state;
3475 GdkWindow *root_window;
3477 if (event->type == GDK_KEY_PRESS)
3479 if (event->keyval == GDK_Escape)
3481 gtk_drag_cancel (info, event->time);
3487 /* Now send a "motion" so that the modifier state is updated */
3489 /* The state is not yet updated in the event, so we need
3490 * to query it here. We could use XGetModifierMapping, but
3491 * that would be overkill.
3493 root_window = gtk_widget_get_root_window (widget);
3494 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3496 event->state = state;
3497 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
3502 /*************************************************************
3503 * gtk_drag_button_release_cb:
3504 * "button_release_event" callback during drag.
3508 *************************************************************/
3511 gtk_drag_button_release_cb (GtkWidget *widget,
3512 GdkEventButton *event,
3515 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3517 if (event->button != info->button)
3520 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3522 gtk_drag_end (info, event->time);
3523 gtk_drag_drop (info, event->time);
3527 gtk_drag_cancel (info, event->time);
3534 gtk_drag_abort_timeout (gpointer data)
3536 GtkDragSourceInfo *info = data;
3537 guint32 time = GDK_CURRENT_TIME;
3539 GDK_THREADS_ENTER ();
3541 if (info->proxy_dest)
3542 time = info->proxy_dest->proxy_drop_time;
3544 info->drop_timeout = 0;
3545 gtk_drag_drop_finished (info, FALSE, time);
3547 GDK_THREADS_LEAVE ();
3553 * gtk_drag_check_threshold:
3554 * @widget: a #GtkWidget
3555 * @start_x: X coordinate of start of drag
3556 * @start_y: Y coordinate of start of drag
3557 * @current_x: current X coordinate
3558 * @current_y: current Y coordinate
3560 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3561 * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
3562 * should trigger the beginning of a drag-and-drop operation.
3564 * Return Value: %TRUE if the drag threshold has been passed.
3567 gtk_drag_check_threshold (GtkWidget *widget,
3573 gint drag_threshold;
3575 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3577 g_object_get (gtk_widget_get_settings (widget),
3578 "gtk-dnd-drag-threshold", &drag_threshold,
3581 return (ABS (current_x - start_x) > drag_threshold ||
3582 ABS (current_y - start_y) > drag_threshold);