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, see <http://www.gnu.org/licenses/>.
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
33 #ifdef GDK_WINDOWING_X11
35 #include <X11/keysym.h>
36 #include "gdk/x11/gdkx.h"
38 #include <X11/extensions/XInput2.h>
43 #include "gtkiconfactory.h"
44 #include "gtkiconhelperprivate.h"
45 #include "gtkicontheme.h"
46 #include "gtkinvisible.h"
50 #include "gtktooltip.h"
51 #include "gtkwindow.h"
53 #include "gtkselectionprivate.h"
58 * @Short_description: Functions for controlling drag and drop handling
59 * @Title: Drag and Drop
61 * GTK+ has a rich set of functions for doing inter-process
62 * communication via the drag-and-drop metaphor. GTK+
63 * can do drag-and-drop (DND) via multiple protocols.
64 * The currently supported protocols are the Xdnd and
67 * As well as the functions listed here, applications
68 * may need to use some facilities provided for
69 * <link linkend="gtk-Selections">Selections</link>.
70 * Also, the Drag and Drop API makes use of signals
71 * in the #GtkWidget class.
75 static GSList *source_widgets = NULL;
77 typedef struct _GtkDragSourceSite GtkDragSourceSite;
78 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
79 typedef struct _GtkDragDestSite GtkDragDestSite;
80 typedef struct _GtkDragDestInfo GtkDragDestInfo;
81 typedef struct _GtkDragAnim GtkDragAnim;
91 struct _GtkDragSourceSite
93 GdkModifierType start_button_mask;
94 GtkTargetList *target_list; /* Targets for drag data */
95 GdkDragAction actions; /* Possible actions */
97 GtkIconHelper *icon_helper;
99 /* Stored button press information to detect drag beginning */
104 struct _GtkDragSourceInfo
107 GtkTargetList *target_list; /* Targets for drag data */
108 GdkDragAction possible_actions; /* Actions allowed by source */
109 GdkDragContext *context; /* drag context */
110 GtkWidget *icon_window; /* Window for drag */
111 GtkWidget *fallback_icon; /* Window for drag used on other screens */
112 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
113 GdkCursor *cursor; /* Cursor for drag */
114 gint hot_x, hot_y; /* Hot spot for drag */
115 gint button; /* mouse button starting drag */
117 GtkDragStatus status; /* drag status */
118 GdkEvent *last_event; /* pending event */
120 gint start_x, start_y; /* Initial position */
121 gint cur_x, cur_y; /* Current Position */
122 GdkScreen *cur_screen; /* Current screen for pointer */
124 guint32 grab_time; /* timestamp for initial grab */
125 GList *selections; /* selections we've claimed */
127 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
129 guint update_idle; /* Idle function to update the drag */
130 guint drop_timeout; /* Timeout for aborting drop */
131 guint destroy_icon : 1; /* If true, destroy icon_window */
132 guint have_grab : 1; /* Do we still have the pointer grab */
133 GtkIconHelper *icon_helper;
134 GdkCursor *drag_cursors[6];
137 struct _GtkDragDestSite
139 GtkDestDefaults flags;
140 GtkTargetList *target_list;
141 GdkDragAction actions;
142 GdkWindow *proxy_window;
143 GdkDragProtocol proxy_protocol;
145 guint proxy_coords : 1;
147 guint track_motion : 1;
150 struct _GtkDragDestInfo
152 GtkWidget *widget; /* Widget in which drag is in */
153 GdkDragContext *context; /* Drag context */
154 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
155 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
156 guint32 proxy_drop_time; /* Timestamp for proxied drop */
157 guint proxy_drop_wait : 1; /* Set if we are waiting for a
158 * status reply before sending
161 guint dropped : 1; /* Set after we receive a drop */
162 gint drop_x, drop_y; /* Position of drop */
165 #define DROP_ABORT_TIME 300000
167 #define ANIM_STEP_TIME 50
168 #define ANIM_STEP_LENGTH 50
169 #define ANIM_MIN_STEPS 5
170 #define ANIM_MAX_STEPS 10
174 GtkDragSourceInfo *info;
179 typedef gboolean (* GtkDragDestCallback) (GtkWidget *widget,
180 GdkDragContext *context,
185 /* Enumeration for some targets we handle internally */
191 /* Forward declarations */
192 static void gtk_drag_get_event_actions (GdkEvent *event,
194 GdkDragAction actions,
195 GdkDragAction *suggested_action,
196 GdkDragAction *possible_actions);
197 static GdkCursor * gtk_drag_get_cursor (GtkWidget *widget,
199 GdkDragAction action,
200 GtkDragSourceInfo *info);
201 static void gtk_drag_update_cursor (GtkDragSourceInfo *info);
202 static GtkWidget *gtk_drag_get_ipc_widget (GtkWidget *widget);
203 static GtkWidget *gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen);
204 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
206 static void gtk_drag_selection_received (GtkWidget *widget,
207 GtkSelectionData *selection_data,
210 static gboolean gtk_drag_find_widget (GtkWidget *widget,
211 GdkDragContext *context,
212 GtkDragDestInfo *info,
216 GtkDragDestCallback callback);
217 static void gtk_drag_proxy_begin (GtkWidget *widget,
218 GtkDragDestInfo *dest_info,
220 static void gtk_drag_dest_realized (GtkWidget *widget);
221 static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
222 GtkWidget *previous_toplevel);
223 static void gtk_drag_dest_site_destroy (gpointer data);
224 static void gtk_drag_dest_leave (GtkWidget *widget,
225 GdkDragContext *context,
227 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
228 GdkDragContext *context,
232 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
233 GdkDragContext *context,
238 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
240 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
242 static void gtk_drag_clear_source_info (GdkDragContext *context);
244 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
247 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
249 static void gtk_drag_drop (GtkDragSourceInfo *info,
251 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
252 GtkDragResult result,
254 static void gtk_drag_cancel (GtkDragSourceInfo *info,
255 GtkDragResult result,
258 static gboolean gtk_drag_source_event_cb (GtkWidget *widget,
261 static void gtk_drag_source_site_destroy (gpointer data);
262 static void gtk_drag_selection_get (GtkWidget *widget,
263 GtkSelectionData *selection_data,
267 static gboolean gtk_drag_anim_timeout (gpointer data);
268 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
269 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
270 static void gtk_drag_add_update_idle (GtkDragSourceInfo *info);
272 static void gtk_drag_update (GtkDragSourceInfo *info,
277 static gboolean gtk_drag_motion_cb (GtkWidget *widget,
278 GdkEventMotion *event,
280 static gboolean gtk_drag_key_cb (GtkWidget *widget,
283 static gboolean gtk_drag_grab_broken_event_cb (GtkWidget *widget,
284 GdkEventGrabBroken *event,
286 static void gtk_drag_grab_notify_cb (GtkWidget *widget,
287 gboolean was_grabbed,
289 static gboolean gtk_drag_button_release_cb (GtkWidget *widget,
290 GdkEventButton *event,
292 static gboolean gtk_drag_abort_timeout (gpointer data);
294 static void set_icon_helper (GdkDragContext *context,
295 GtkIconHelper *helper,
298 gboolean force_window);
300 /************************
301 * Cursor and Icon data *
302 ************************/
305 GdkDragAction action;
310 { GDK_ACTION_DEFAULT, NULL },
311 { GDK_ACTION_ASK, "dnd-ask", NULL, NULL },
312 { GDK_ACTION_COPY, "dnd-copy", NULL, NULL },
313 { GDK_ACTION_MOVE, "dnd-move", NULL, NULL },
314 { GDK_ACTION_LINK, "dnd-link", NULL, NULL },
315 { 0 , "dnd-none", NULL, NULL },
318 /*********************
319 * Utility functions *
320 *********************/
323 set_can_change_screen (GtkWidget *widget,
324 gboolean can_change_screen)
326 can_change_screen = can_change_screen != FALSE;
328 g_object_set_data (G_OBJECT (widget), I_("gtk-dnd-can-change-screen"),
329 GUINT_TO_POINTER (can_change_screen));
333 get_can_change_screen (GtkWidget *widget)
335 return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
340 gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen)
343 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
344 "gtk-dnd-ipc-widgets");
348 GSList *tmp = drag_widgets;
349 result = drag_widgets->data;
350 drag_widgets = drag_widgets->next;
351 g_object_set_data (G_OBJECT (screen),
352 I_("gtk-dnd-ipc-widgets"),
354 g_slist_free_1 (tmp);
358 result = gtk_window_new (GTK_WINDOW_POPUP);
359 gtk_window_set_screen (GTK_WINDOW (result), screen);
360 gtk_window_resize (GTK_WINDOW (result), 1, 1);
361 gtk_window_move (GTK_WINDOW (result), -99, -99);
362 gtk_widget_show (result);
369 gtk_drag_get_ipc_widget (GtkWidget *widget)
374 result = gtk_drag_get_ipc_widget_for_screen (gtk_widget_get_screen (widget));
376 toplevel = gtk_widget_get_toplevel (widget);
378 if (GTK_IS_WINDOW (toplevel))
380 if (gtk_window_has_group (GTK_WINDOW (toplevel)))
381 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
382 GTK_WINDOW (result));
388 #if defined (GDK_WINDOWING_X11)
391 * We want to handle a handful of keys during DND, e.g. Escape to abort.
392 * Grabbing the keyboard has the unfortunate side-effect of preventing
393 * useful things such as using Alt-Tab to cycle between windows or
394 * switching workspaces. Therefore, we just grab the few keys we are
395 * interested in. Note that we need to put the grabs on the root window
396 * in order for them to still work when the focus is moved to another
399 * GDK needs a little help to successfully deliver root key events...
402 static GdkFilterReturn
403 root_key_filter (GdkXEvent *xevent,
407 XEvent *ev = (XEvent *) xevent;
409 if ((ev->type == KeyPress || ev->type == KeyRelease) &&
410 ev->xkey.root == ev->xkey.window)
411 ev->xkey.window = (Window)data;
412 else if (ev->type == GenericEvent)
414 XGenericEventCookie *cookie;
417 cookie = &ev->xcookie;
418 dev = (XIDeviceEvent *) cookie->data;
420 if (dev->evtype == XI_KeyPress ||
421 dev->evtype == XI_KeyRelease)
422 dev->event = (Window)data;
425 return GDK_FILTER_CONTINUE;
433 static GrabKey grab_keys[] = {
442 { XK_Down, Mod1Mask },
444 { XK_Left, Mod1Mask },
446 { XK_Right, Mod1Mask },
448 { XK_KP_Up, Mod1Mask },
450 { XK_KP_Down, Mod1Mask },
452 { XK_KP_Left, Mod1Mask },
454 { XK_KP_Right, Mod1Mask }
458 grab_dnd_keys (GtkWidget *widget,
463 GdkWindow *window, *root;
467 XIGrabModifiers mods;
470 unsigned char mask[(XI_LASTEVENT + 7)/8];
473 deviceid = gdk_x11_device_get_id (device);
475 if (GDK_IS_X11_DEVICE_MANAGER_XI2 (gdk_display_get_device_manager (gtk_widget_get_display (widget))))
481 window = gtk_widget_get_window (widget);
482 if (!GDK_IS_X11_WINDOW (window))
484 gdk_device_grab (device,
485 gtk_widget_get_window (widget),
486 GDK_OWNERSHIP_APPLICATION, FALSE,
487 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
493 root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
495 gdk_error_trap_push ();
497 for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
499 keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
500 if (keycode == NoSymbol)
506 memset (mask, 0, sizeof (mask));
507 XISetMask (mask, XI_KeyPress);
508 XISetMask (mask, XI_KeyRelease);
510 evmask.deviceid = deviceid;
511 evmask.mask_len = sizeof (mask);
515 mods.modifiers = grab_keys[i].modifiers;
517 XIGrabKeycode (GDK_WINDOW_XDISPLAY (window),
520 GDK_WINDOW_XID (root),
530 XGrabKey (GDK_WINDOW_XDISPLAY (window),
531 keycode, grab_keys[i].modifiers,
532 GDK_WINDOW_XID (root),
539 gdk_error_trap_pop_ignored ();
541 gdk_window_add_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
545 ungrab_dnd_keys (GtkWidget *widget,
550 GdkWindow *window, *root;
553 XIGrabModifiers mods;
558 deviceid = gdk_x11_device_get_id (device);
559 if (GDK_IS_X11_DEVICE_MANAGER_XI2 (gdk_display_get_device_manager (gtk_widget_get_display (widget))))
565 window = gtk_widget_get_window (widget);
566 if (!GDK_IS_X11_WINDOW (window))
568 gdk_device_ungrab (device, time);
572 root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
574 gdk_window_remove_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
576 gdk_error_trap_push ();
578 for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
580 keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
581 if (keycode == NoSymbol)
588 mods.modifiers = grab_keys[i].modifiers;
590 XIUngrabKeycode (GDK_WINDOW_XDISPLAY (window),
593 GDK_WINDOW_XID (root),
599 XUngrabKey (GDK_WINDOW_XDISPLAY (window),
600 keycode, grab_keys[i].modifiers,
601 GDK_WINDOW_XID (root));
605 gdk_error_trap_pop_ignored ();
608 #else /* !GDK_WINDOWING_X11 */
611 grab_dnd_keys (GtkWidget *widget,
615 gdk_device_grab (device,
616 gtk_widget_get_window (widget),
617 GDK_OWNERSHIP_APPLICATION, FALSE,
618 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
623 ungrab_dnd_keys (GtkWidget *widget,
627 gdk_device_ungrab (device, time);
630 #endif /* GDK_WINDOWING_X11 */
633 /***************************************************************
634 * gtk_drag_release_ipc_widget:
635 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
637 * widget: the widget to release.
639 ***************************************************************/
642 gtk_drag_release_ipc_widget (GtkWidget *widget)
644 GtkWindow *window = GTK_WINDOW (widget);
645 GdkScreen *screen = gtk_widget_get_screen (widget);
646 GdkDragContext *context = g_object_get_data (G_OBJECT (widget), "drag-context");
647 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
648 "gtk-dnd-ipc-widgets");
649 GdkDevice *pointer, *keyboard;
653 pointer = gdk_drag_context_get_device (context);
654 keyboard = gdk_device_get_associated_device (pointer);
657 ungrab_dnd_keys (widget, keyboard, GDK_CURRENT_TIME);
660 if (gtk_window_has_group (window))
661 gtk_window_group_remove_window (gtk_window_get_group (window),
663 drag_widgets = g_slist_prepend (drag_widgets, widget);
664 g_object_set_data (G_OBJECT (screen),
665 I_("gtk-dnd-ipc-widgets"),
670 gtk_drag_get_event_time (GdkEvent *event)
672 guint32 tm = GDK_CURRENT_TIME;
677 case GDK_MOTION_NOTIFY:
678 tm = event->motion.time; break;
679 case GDK_BUTTON_PRESS:
680 case GDK_2BUTTON_PRESS:
681 case GDK_3BUTTON_PRESS:
682 case GDK_BUTTON_RELEASE:
683 tm = event->button.time; break;
685 case GDK_KEY_RELEASE:
686 tm = event->key.time; break;
687 case GDK_ENTER_NOTIFY:
688 case GDK_LEAVE_NOTIFY:
689 tm = event->crossing.time; break;
690 case GDK_PROPERTY_NOTIFY:
691 tm = event->property.time; break;
692 case GDK_SELECTION_CLEAR:
693 case GDK_SELECTION_REQUEST:
694 case GDK_SELECTION_NOTIFY:
695 tm = event->selection.time; break;
696 case GDK_PROXIMITY_IN:
697 case GDK_PROXIMITY_OUT:
698 tm = event->proximity.time; break;
699 default: /* use current time */
707 gtk_drag_get_event_actions (GdkEvent *event,
709 GdkDragAction actions,
710 GdkDragAction *suggested_action,
711 GdkDragAction *possible_actions)
713 *suggested_action = 0;
714 *possible_actions = 0;
718 GdkModifierType state = 0;
722 case GDK_MOTION_NOTIFY:
723 state = event->motion.state;
725 case GDK_BUTTON_PRESS:
726 case GDK_2BUTTON_PRESS:
727 case GDK_3BUTTON_PRESS:
728 case GDK_BUTTON_RELEASE:
729 state = event->button.state;
732 case GDK_KEY_RELEASE:
733 state = event->key.state;
735 case GDK_ENTER_NOTIFY:
736 case GDK_LEAVE_NOTIFY:
737 state = event->crossing.state;
743 if ((button == GDK_BUTTON_MIDDLE || button == GDK_BUTTON_SECONDARY) && (actions & GDK_ACTION_ASK))
745 *suggested_action = GDK_ACTION_ASK;
746 *possible_actions = actions;
748 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
750 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
752 if (actions & GDK_ACTION_LINK)
754 *suggested_action = GDK_ACTION_LINK;
755 *possible_actions = GDK_ACTION_LINK;
758 else if (state & GDK_CONTROL_MASK)
760 if (actions & GDK_ACTION_COPY)
762 *suggested_action = GDK_ACTION_COPY;
763 *possible_actions = GDK_ACTION_COPY;
769 if (actions & GDK_ACTION_MOVE)
771 *suggested_action = GDK_ACTION_MOVE;
772 *possible_actions = GDK_ACTION_MOVE;
779 *possible_actions = actions;
781 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
782 *suggested_action = GDK_ACTION_ASK;
783 else if (actions & GDK_ACTION_COPY)
784 *suggested_action = GDK_ACTION_COPY;
785 else if (actions & GDK_ACTION_MOVE)
786 *suggested_action = GDK_ACTION_MOVE;
787 else if (actions & GDK_ACTION_LINK)
788 *suggested_action = GDK_ACTION_LINK;
793 *possible_actions = actions;
795 if (actions & GDK_ACTION_COPY)
796 *suggested_action = GDK_ACTION_COPY;
797 else if (actions & GDK_ACTION_MOVE)
798 *suggested_action = GDK_ACTION_MOVE;
799 else if (actions & GDK_ACTION_LINK)
800 *suggested_action = GDK_ACTION_LINK;
805 gtk_drag_can_use_rgba_cursor (GdkDisplay *display,
809 guint max_width, max_height;
811 if (!gdk_display_supports_cursor_color (display))
814 if (!gdk_display_supports_cursor_alpha (display))
817 gdk_display_get_maximal_cursor_size (display,
820 if (width > max_width || height > max_height)
822 /* can't use rgba cursor (too large) */
830 ensure_drag_cursor_pixbuf (int i)
832 if (drag_cursors[i].pixbuf == NULL)
834 char *path = g_strconcat ("/org/gtk/libgtk/cursor/", drag_cursors[i].name, ".png", NULL);
835 GInputStream *stream = g_resources_open_stream (path, 0, NULL);
838 drag_cursors[i].pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
839 g_object_unref (stream);
846 gtk_drag_get_cursor (GtkWidget *widget,
848 GdkDragAction action,
849 GtkDragSourceInfo *info)
853 /* reconstruct the cursors for each new drag (thus !info),
854 * to catch cursor theme changes
858 for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
859 if (drag_cursors[i].cursor != NULL)
861 g_object_unref (drag_cursors[i].cursor);
862 drag_cursors[i].cursor = NULL;
866 for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
867 if (drag_cursors[i].action == action)
870 if (drag_cursors[i].cursor != NULL)
872 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
874 g_object_unref (drag_cursors[i].cursor);
875 drag_cursors[i].cursor = NULL;
879 if (drag_cursors[i].cursor == NULL)
880 drag_cursors[i].cursor = gdk_cursor_new_from_name (display, drag_cursors[i].name);
882 if (drag_cursors[i].cursor == NULL)
884 ensure_drag_cursor_pixbuf (i);
885 drag_cursors[i].cursor = gdk_cursor_new_from_pixbuf (display, drag_cursors[i].pixbuf, 0, 0);
888 if (info && info->icon_helper)
890 gint cursor_width, cursor_height;
891 gint icon_width, icon_height;
893 GdkPixbuf *cursor_pixbuf, *pixbuf, *icon_pixbuf;
895 gint icon_x, icon_y, ref_x, ref_y;
897 if (info->drag_cursors[i] != NULL)
899 if (display == gdk_cursor_get_display (info->drag_cursors[i]))
900 return info->drag_cursors[i];
902 g_object_unref (info->drag_cursors[i]);
903 info->drag_cursors[i] = NULL;
906 icon_pixbuf = _gtk_icon_helper_ensure_pixbuf (info->icon_helper,
907 gtk_widget_get_style_context (widget));
909 icon_x = info->hot_x;
910 icon_y = info->hot_y;
911 icon_width = gdk_pixbuf_get_width (icon_pixbuf);
912 icon_height = gdk_pixbuf_get_height (icon_pixbuf);
915 cursor_pixbuf = gdk_cursor_get_image (drag_cursors[i].cursor);
918 ensure_drag_cursor_pixbuf (i);
919 cursor_pixbuf = g_object_ref (drag_cursors[i].pixbuf);
923 if (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"))
924 hot_x = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"));
926 if (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"))
927 hot_y = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"));
930 /* The code below is an attempt to let cursor themes
931 * determine the attachment of the icon to enable things
932 * like the following:
940 * It does not work since Xcursor doesn't allow to attach
941 * any additional information to cursors in a retrievable
942 * way (there are comments, but no way to get at them
943 * short of searching for the actual cursor file).
944 * If this code ever gets used, the icon_window placement
945 * must be changed to recognize these placement options
946 * as well. Note that this code ignores info->hot_x/y.
948 for (j = 0; j < 10; j++)
953 GtkAnchorType icon_anchor;
955 g_snprintf (key, 32, "comment%d", j);
956 opt = gdk_pixbuf_get_option (cursor_pixbuf, key);
957 if (opt && g_str_has_prefix ("icon-attach:", opt))
959 toks = g_strsplit (opt + strlen ("icon-attach:"), "'", -1);
960 if (g_strv_length (toks) != 3)
965 icon_anchor = atoi (toks[0]);
966 icon_x = atoi (toks[1]);
967 icon_y = atoi (toks[2]);
971 case GTK_ANCHOR_NORTH:
972 case GTK_ANCHOR_CENTER:
973 case GTK_ANCHOR_SOUTH:
974 icon_x += icon_width / 2;
976 case GTK_ANCHOR_NORTH_EAST:
977 case GTK_ANCHOR_EAST:
978 case GTK_ANCHOR_SOUTH_EAST:
979 icon_x += icon_width;
986 case GTK_ANCHOR_WEST:
987 case GTK_ANCHOR_CENTER:
988 case GTK_ANCHOR_EAST:
989 icon_y += icon_height / 2;
991 case GTK_ANCHOR_SOUTH_WEST:
992 case GTK_ANCHOR_SOUTH:
993 case GTK_ANCHOR_SOUTH_EAST:
994 icon_x += icon_height;
1006 cursor_width = gdk_pixbuf_get_width (cursor_pixbuf);
1007 cursor_height = gdk_pixbuf_get_height (cursor_pixbuf);
1009 ref_x = MAX (hot_x, icon_x);
1010 ref_y = MAX (hot_y, icon_y);
1011 width = ref_x + MAX (cursor_width - hot_x, icon_width - icon_x);
1012 height = ref_y + MAX (cursor_height - hot_y, icon_height - icon_y);
1014 if (gtk_drag_can_use_rgba_cursor (display, width, height))
1016 /* Composite cursor and icon so that both hotspots
1017 * end up at (ref_x, ref_y)
1019 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
1022 gdk_pixbuf_fill (pixbuf, 0xff000000);
1024 gdk_pixbuf_composite (icon_pixbuf, pixbuf,
1025 ref_x - icon_x, ref_y - icon_y,
1026 icon_width, icon_height,
1027 ref_x - icon_x, ref_y - icon_y,
1029 GDK_INTERP_BILINEAR, 255);
1031 gdk_pixbuf_composite (cursor_pixbuf, pixbuf,
1032 ref_x - hot_x, ref_y - hot_y,
1033 cursor_width, cursor_height,
1034 ref_x - hot_x, ref_y - hot_y,
1036 GDK_INTERP_BILINEAR, 255);
1038 info->drag_cursors[i] =
1039 gdk_cursor_new_from_pixbuf (display, pixbuf, ref_x, ref_y);
1041 g_object_unref (pixbuf);
1044 g_object_unref (cursor_pixbuf);
1045 g_object_unref (icon_pixbuf);
1047 if (info->drag_cursors[i] != NULL)
1048 return info->drag_cursors[i];
1051 return drag_cursors[i].cursor;
1055 gtk_drag_update_cursor (GtkDragSourceInfo *info)
1060 if (!info->have_grab)
1063 for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
1064 if (info->cursor == drag_cursors[i].cursor ||
1065 info->cursor == info->drag_cursors[i])
1068 if (i == G_N_ELEMENTS (drag_cursors))
1071 cursor = gtk_drag_get_cursor (info->widget,
1072 gdk_cursor_get_display (info->cursor),
1073 drag_cursors[i].action, info);
1075 if (cursor != info->cursor)
1079 pointer = gdk_drag_context_get_device (info->context);
1080 gdk_device_grab (pointer,
1081 gtk_widget_get_window (info->ipc_widget),
1082 GDK_OWNERSHIP_APPLICATION, FALSE,
1083 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
1084 cursor, info->grab_time);
1085 info->cursor = cursor;
1089 /********************
1090 * Destination side *
1091 ********************/
1094 * gtk_drag_get_data: (method)
1095 * @widget: the widget that will receive the
1096 * #GtkWidget::drag-data-received signal.
1097 * @context: the drag context
1098 * @target: the target (form of the data) to retrieve.
1099 * @time_: a timestamp for retrieving the data. This will
1100 * generally be the time received in a #GtkWidget::drag-motion"
1101 * or #GtkWidget::drag-drop" signal.
1103 * Gets the data associated with a drag. When the data
1104 * is received or the retrieval fails, GTK+ will emit a
1105 * #GtkWidget::drag-data-received signal. Failure of the retrieval
1106 * is indicated by the length field of the @selection_data
1107 * signal parameter being negative. However, when gtk_drag_get_data()
1108 * is called implicitely because the %GTK_DEST_DEFAULT_DROP was set,
1109 * then the widget will not receive notification of failed
1113 gtk_drag_get_data (GtkWidget *widget,
1114 GdkDragContext *context,
1118 GtkWidget *selection_widget;
1120 g_return_if_fail (GTK_IS_WIDGET (widget));
1121 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1123 selection_widget = gtk_drag_get_ipc_widget (widget);
1125 g_object_ref (context);
1126 g_object_ref (widget);
1128 g_signal_connect (selection_widget, "selection-received",
1129 G_CALLBACK (gtk_drag_selection_received), widget);
1131 g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1133 gtk_selection_convert (selection_widget,
1134 gdk_drag_get_selection (context),
1141 * gtk_drag_get_source_widget: (method)
1142 * @context: a (destination side) drag context
1144 * Determines the source widget for a drag.
1146 * Return value: (transfer none): if the drag is occurring
1147 * within a single application, a pointer to the source widget.
1151 gtk_drag_get_source_widget (GdkDragContext *context)
1155 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
1157 tmp_list = source_widgets;
1160 GtkWidget *ipc_widget = tmp_list->data;
1162 if (gtk_widget_get_window (ipc_widget) == gdk_drag_context_get_source_window (context))
1164 GtkDragSourceInfo *info;
1165 info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
1167 return info ? info->widget : NULL;
1170 tmp_list = tmp_list->next;
1177 * gtk_drag_finish: (method)
1178 * @context: the drag context.
1179 * @success: a flag indicating whether the drop was successful
1180 * @del: a flag indicating whether the source should delete the
1181 * original data. (This should be %TRUE for a move)
1182 * @time_: the timestamp from the #GtkWidget::drag-drop signal.
1184 * Informs the drag source that the drop is finished, and
1185 * that the data of the drag will no longer be required.
1188 gtk_drag_finish (GdkDragContext *context,
1193 GdkAtom target = GDK_NONE;
1195 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1199 target = gdk_atom_intern_static_string ("DELETE");
1202 if (target != GDK_NONE)
1204 GtkWidget *selection_widget = gtk_drag_get_ipc_widget_for_screen (gdk_window_get_screen (gdk_drag_context_get_source_window (context)));
1206 g_object_ref (context);
1208 g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1209 g_signal_connect (selection_widget, "selection-received",
1210 G_CALLBACK (gtk_drag_selection_received),
1213 gtk_selection_convert (selection_widget,
1214 gdk_drag_get_selection (context),
1219 if (!(success && del))
1220 gdk_drop_finish (context, success, time);
1223 /*************************************************************
1224 * gtk_drag_highlight_draw:
1225 * Callback for expose_event for highlighted widgets.
1231 *************************************************************/
1234 gtk_drag_highlight_draw (GtkWidget *widget,
1238 int width = gtk_widget_get_allocated_width (widget);
1239 int height = gtk_widget_get_allocated_height (widget);
1240 GtkStyleContext *context;
1242 context = gtk_widget_get_style_context (widget);
1244 gtk_style_context_save (context);
1245 gtk_style_context_add_class (context, GTK_STYLE_CLASS_DND);
1247 gtk_render_frame (context, cr, 0, 0, width, height);
1249 gtk_style_context_restore (context);
1251 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
1252 cairo_set_line_width (cr, 1.0);
1253 cairo_rectangle (cr,
1255 width - 1, height - 1);
1262 * gtk_drag_highlight: (method)
1263 * @widget: a widget to highlight
1265 * Draws a highlight around a widget. This will attach
1266 * handlers to #GtkWidget::draw, so the highlight
1267 * will continue to be displayed until gtk_drag_unhighlight()
1271 gtk_drag_highlight (GtkWidget *widget)
1273 g_return_if_fail (GTK_IS_WIDGET (widget));
1275 g_signal_connect_after (widget, "draw",
1276 G_CALLBACK (gtk_drag_highlight_draw),
1279 gtk_widget_queue_draw (widget);
1283 * gtk_drag_unhighlight: (method)
1284 * @widget: a widget to remove the highlight from.
1286 * Removes a highlight set by gtk_drag_highlight() from
1290 gtk_drag_unhighlight (GtkWidget *widget)
1292 g_return_if_fail (GTK_IS_WIDGET (widget));
1294 g_signal_handlers_disconnect_by_func (widget,
1295 gtk_drag_highlight_draw,
1298 gtk_widget_queue_draw (widget);
1302 gtk_drag_dest_set_internal (GtkWidget *widget,
1303 GtkDragDestSite *site)
1305 GtkDragDestSite *old_site;
1307 g_return_if_fail (widget != NULL);
1309 /* HACK, do this in the destroy */
1310 old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1313 g_signal_handlers_disconnect_by_func (widget,
1314 gtk_drag_dest_realized,
1316 g_signal_handlers_disconnect_by_func (widget,
1317 gtk_drag_dest_hierarchy_changed,
1320 site->track_motion = old_site->track_motion;
1323 if (gtk_widget_get_realized (widget))
1324 gtk_drag_dest_realized (widget);
1326 g_signal_connect (widget, "realize",
1327 G_CALLBACK (gtk_drag_dest_realized), site);
1328 g_signal_connect (widget, "hierarchy-changed",
1329 G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
1331 g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
1332 site, gtk_drag_dest_site_destroy);
1336 * gtk_drag_dest_set: (method)
1337 * @widget: a #GtkWidget
1338 * @flags: which types of default drag behavior to use
1339 * @targets: (allow-none) (array length=n_targets): a pointer to an array of #GtkTargetEntry<!-- -->s
1340 * indicating the drop types that this @widget will accept, or %NULL.
1341 * Later you can access the list with gtk_drag_dest_get_target_list()
1342 * and gtk_drag_dest_find_target().
1343 * @n_targets: the number of entries in @targets
1344 * @actions: a bitmask of possible actions for a drop onto this @widget.
1346 * Sets a widget as a potential drop destination, and adds default behaviors.
1348 * The default behaviors listed in @flags have an effect similar
1349 * to installing default handlers for the widget's drag-and-drop signals
1350 * (#GtkWidget::drag-motion, #GtkWidget::drag-drop, ...). They all exist
1351 * for convenience. When passing #GTK_DEST_DEFAULT_ALL for instance it is
1352 * sufficient to connect to the widget's #GtkWidget::drag-data-received
1353 * signal to get primitive, but consistent drag-and-drop support.
1355 * Things become more complicated when you try to preview the dragged data,
1356 * as described in the documentation for #GtkWidget::drag-motion. The default
1357 * behaviors described by @flags make some assumptions, that can conflict
1358 * with your own signal handlers. For instance #GTK_DEST_DEFAULT_DROP causes
1359 * invokations of gdk_drag_status() in the context of #GtkWidget::drag-motion,
1360 * and invokations of gtk_drag_finish() in #GtkWidget::drag-data-received.
1361 * Especially the later is dramatic, when your own #GtkWidget::drag-motion
1362 * handler calls gtk_drag_get_data() to inspect the dragged data.
1364 * There's no way to set a default action here, you can use the
1365 * #GtkWidget::drag-motion callback for that. Here's an example which selects
1366 * the action to use depending on whether the control key is pressed or not:
1369 * drag_motion (GtkWidget *widget,
1370 * GdkDragContext *context,
1375 * GdkModifierType mask;
1377 * gdk_window_get_pointer (gtk_widget_get_window (widget),
1378 * NULL, NULL, &mask);
1379 * if (mask & GDK_CONTROL_MASK)
1380 * gdk_drag_status (context, GDK_ACTION_COPY, time);
1382 * gdk_drag_status (context, GDK_ACTION_MOVE, time);
1387 gtk_drag_dest_set (GtkWidget *widget,
1388 GtkDestDefaults flags,
1389 const GtkTargetEntry *targets,
1391 GdkDragAction actions)
1393 GtkDragDestSite *site;
1395 g_return_if_fail (GTK_IS_WIDGET (widget));
1397 site = g_slice_new0 (GtkDragDestSite);
1399 site->flags = flags;
1400 site->have_drag = FALSE;
1402 site->target_list = gtk_target_list_new (targets, n_targets);
1404 site->target_list = NULL;
1405 site->actions = actions;
1406 site->do_proxy = FALSE;
1407 site->proxy_window = NULL;
1408 site->track_motion = FALSE;
1410 gtk_drag_dest_set_internal (widget, site);
1414 * gtk_drag_dest_set_proxy: (method)
1415 * @widget: a #GtkWidget
1416 * @proxy_window: the window to which to forward drag events
1417 * @protocol: the drag protocol which the @proxy_window accepts
1418 * (You can use gdk_drag_get_protocol() to determine this)
1419 * @use_coordinates: If %TRUE, send the same coordinates to the
1420 * destination, because it is an embedded
1423 * Sets this widget as a proxy for drops to another window.
1426 gtk_drag_dest_set_proxy (GtkWidget *widget,
1427 GdkWindow *proxy_window,
1428 GdkDragProtocol protocol,
1429 gboolean use_coordinates)
1431 GtkDragDestSite *site;
1433 g_return_if_fail (GTK_IS_WIDGET (widget));
1434 g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
1436 site = g_slice_new (GtkDragDestSite);
1439 site->have_drag = FALSE;
1440 site->target_list = NULL;
1442 site->proxy_window = proxy_window;
1444 g_object_ref (proxy_window);
1445 site->do_proxy = TRUE;
1446 site->proxy_protocol = protocol;
1447 site->proxy_coords = use_coordinates;
1448 site->track_motion = FALSE;
1450 gtk_drag_dest_set_internal (widget, site);
1454 * gtk_drag_dest_unset: (method)
1455 * @widget: a #GtkWidget
1457 * Clears information about a drop destination set with
1458 * gtk_drag_dest_set(). The widget will no longer receive
1459 * notification of drags.
1462 gtk_drag_dest_unset (GtkWidget *widget)
1464 GtkDragDestSite *old_site;
1466 g_return_if_fail (GTK_IS_WIDGET (widget));
1468 old_site = g_object_get_data (G_OBJECT (widget),
1472 g_signal_handlers_disconnect_by_func (widget,
1473 gtk_drag_dest_realized,
1475 g_signal_handlers_disconnect_by_func (widget,
1476 gtk_drag_dest_hierarchy_changed,
1480 g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
1484 * gtk_drag_dest_get_target_list: (method)
1485 * @widget: a #GtkWidget
1487 * Returns the list of targets this widget can accept from
1490 * Return value: (transfer none): the #GtkTargetList, or %NULL if none
1493 gtk_drag_dest_get_target_list (GtkWidget *widget)
1495 GtkDragDestSite *site;
1497 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1499 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1501 return site ? site->target_list : NULL;
1505 * gtk_drag_dest_set_target_list: (method)
1506 * @widget: a #GtkWidget that's a drag destination
1507 * @target_list: (allow-none): list of droppable targets, or %NULL for none
1509 * Sets the target types that this widget can accept from drag-and-drop.
1510 * The widget must first be made into a drag destination with
1511 * gtk_drag_dest_set().
1514 gtk_drag_dest_set_target_list (GtkWidget *widget,
1515 GtkTargetList *target_list)
1517 GtkDragDestSite *site;
1519 g_return_if_fail (GTK_IS_WIDGET (widget));
1521 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1525 g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1526 "to make the widget into a drag destination");
1531 gtk_target_list_ref (target_list);
1533 if (site->target_list)
1534 gtk_target_list_unref (site->target_list);
1536 site->target_list = target_list;
1540 * gtk_drag_dest_add_text_targets: (method)
1541 * @widget: a #GtkWidget that's a drag destination
1543 * Add the text targets supported by #GtkSelection to
1544 * the target list of the drag destination. The targets
1545 * are added with @info = 0. If you need another value,
1546 * use gtk_target_list_add_text_targets() and
1547 * gtk_drag_dest_set_target_list().
1552 gtk_drag_dest_add_text_targets (GtkWidget *widget)
1554 GtkTargetList *target_list;
1556 target_list = gtk_drag_dest_get_target_list (widget);
1558 gtk_target_list_ref (target_list);
1560 target_list = gtk_target_list_new (NULL, 0);
1561 gtk_target_list_add_text_targets (target_list, 0);
1562 gtk_drag_dest_set_target_list (widget, target_list);
1563 gtk_target_list_unref (target_list);
1567 * gtk_drag_dest_add_image_targets: (method)
1568 * @widget: a #GtkWidget that's a drag destination
1570 * Add the image targets supported by #GtkSelection to
1571 * the target list of the drag destination. The targets
1572 * are added with @info = 0. If you need another value,
1573 * use gtk_target_list_add_image_targets() and
1574 * gtk_drag_dest_set_target_list().
1579 gtk_drag_dest_add_image_targets (GtkWidget *widget)
1581 GtkTargetList *target_list;
1583 target_list = gtk_drag_dest_get_target_list (widget);
1585 gtk_target_list_ref (target_list);
1587 target_list = gtk_target_list_new (NULL, 0);
1588 gtk_target_list_add_image_targets (target_list, 0, FALSE);
1589 gtk_drag_dest_set_target_list (widget, target_list);
1590 gtk_target_list_unref (target_list);
1594 * gtk_drag_dest_add_uri_targets: (method)
1595 * @widget: a #GtkWidget that's a drag destination
1597 * Add the URI targets supported by #GtkSelection to
1598 * the target list of the drag destination. The targets
1599 * are added with @info = 0. If you need another value,
1600 * use gtk_target_list_add_uri_targets() and
1601 * gtk_drag_dest_set_target_list().
1606 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
1608 GtkTargetList *target_list;
1610 target_list = gtk_drag_dest_get_target_list (widget);
1612 gtk_target_list_ref (target_list);
1614 target_list = gtk_target_list_new (NULL, 0);
1615 gtk_target_list_add_uri_targets (target_list, 0);
1616 gtk_drag_dest_set_target_list (widget, target_list);
1617 gtk_target_list_unref (target_list);
1621 * gtk_drag_dest_set_track_motion: (method)
1622 * @widget: a #GtkWidget that's a drag destination
1623 * @track_motion: whether to accept all targets
1625 * Tells the widget to emit #GtkWidget::drag-motion and
1626 * #GtkWidget::drag-leave events regardless of the targets and the
1627 * %GTK_DEST_DEFAULT_MOTION flag.
1629 * This may be used when a widget wants to do generic
1630 * actions regardless of the targets that the source offers.
1635 gtk_drag_dest_set_track_motion (GtkWidget *widget,
1636 gboolean track_motion)
1638 GtkDragDestSite *site;
1640 g_return_if_fail (GTK_IS_WIDGET (widget));
1642 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1644 g_return_if_fail (site != NULL);
1646 site->track_motion = track_motion != FALSE;
1650 * gtk_drag_dest_get_track_motion: (method)
1651 * @widget: a #GtkWidget that's a drag destination
1653 * Returns whether the widget has been configured to always
1654 * emit #GtkWidget::drag-motion signals.
1656 * Return Value: %TRUE if the widget always emits
1657 * #GtkWidget::drag-motion events
1662 gtk_drag_dest_get_track_motion (GtkWidget *widget)
1664 GtkDragDestSite *site;
1666 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1668 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1671 return site->track_motion;
1676 /*************************************************************
1677 * _gtk_drag_dest_handle_event:
1678 * Called from widget event handling code on Drag events
1682 * toplevel: Toplevel widget that received the event
1685 *************************************************************/
1688 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1691 GtkDragDestInfo *info;
1692 GdkDragContext *context;
1694 g_return_if_fail (toplevel != NULL);
1695 g_return_if_fail (event != NULL);
1697 context = event->dnd.context;
1699 info = gtk_drag_get_dest_info (context, TRUE);
1701 /* Find the widget for the event */
1702 switch (event->type)
1704 case GDK_DRAG_ENTER:
1707 case GDK_DRAG_LEAVE:
1710 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1711 info->widget = NULL;
1715 case GDK_DRAG_MOTION:
1716 case GDK_DROP_START:
1722 if (event->type == GDK_DROP_START)
1724 info->dropped = TRUE;
1725 /* We send a leave here so that the widget unhighlights
1730 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1731 info->widget = NULL;
1735 window = gtk_widget_get_window (toplevel);
1737 #ifdef GDK_WINDOWING_X11
1738 /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
1740 * Currently gdk_window_get_position doesn't provide reliable
1741 * information for embedded windows, so we call the much more
1742 * expensive gdk_window_get_origin().
1744 if (GTK_IS_PLUG (toplevel))
1745 gdk_window_get_origin (window, &tx, &ty);
1747 #endif /* GDK_WINDOWING_X11 */
1748 gdk_window_get_position (window, &tx, &ty);
1750 found = gtk_drag_find_widget (toplevel,
1753 event->dnd.x_root - tx,
1754 event->dnd.y_root - ty,
1756 (event->type == GDK_DRAG_MOTION) ?
1757 gtk_drag_dest_motion :
1758 gtk_drag_dest_drop);
1760 if (info->widget && !found)
1762 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1763 info->widget = NULL;
1768 if (event->type == GDK_DRAG_MOTION)
1771 gdk_drag_status (context, 0, event->dnd.time);
1773 else if (event->type == GDK_DROP_START && !info->proxy_source)
1775 gdk_drop_reply (context, found, event->dnd.time);
1781 g_assert_not_reached ();
1786 * gtk_drag_dest_find_target: (method)
1787 * @widget: drag destination widget
1788 * @context: drag context
1789 * @target_list: (allow-none): list of droppable targets, or %NULL to use
1790 * gtk_drag_dest_get_target_list (@widget).
1792 * Looks for a match between the supported targets of @context and the
1793 * @dest_target_list, returning the first matching target, otherwise
1794 * returning %GDK_NONE. @dest_target_list should usually be the return
1795 * value from gtk_drag_dest_get_target_list(), but some widgets may
1796 * have different valid targets for different parts of the widget; in
1797 * that case, they will have to implement a drag_motion handler that
1798 * passes the correct target list to this function.
1800 * Return value: (transfer none): first target that the source offers
1801 * and the dest can accept, or %GDK_NONE
1804 gtk_drag_dest_find_target (GtkWidget *widget,
1805 GdkDragContext *context,
1806 GtkTargetList *target_list)
1809 GList *tmp_source = NULL;
1810 GtkWidget *source_widget;
1812 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1813 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1816 source_widget = gtk_drag_get_source_widget (context);
1818 if (target_list == NULL)
1819 target_list = gtk_drag_dest_get_target_list (widget);
1821 if (target_list == NULL)
1824 tmp_target = target_list->list;
1827 GtkTargetPair *pair = tmp_target->data;
1828 tmp_source = gdk_drag_context_list_targets (context);
1831 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1833 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1834 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)) &&
1835 (!(pair->flags & GTK_TARGET_OTHER_APP) || !source_widget) &&
1836 (!(pair->flags & GTK_TARGET_OTHER_WIDGET) || (source_widget != widget)))
1837 return pair->target;
1841 tmp_source = tmp_source->next;
1843 tmp_target = tmp_target->next;
1850 gtk_drag_selection_received (GtkWidget *widget,
1851 GtkSelectionData *selection_data,
1855 GdkDragContext *context;
1856 GtkDragDestInfo *info;
1857 GtkWidget *drop_widget;
1862 context = g_object_get_data (G_OBJECT (widget), "drag-context");
1863 info = gtk_drag_get_dest_info (context, FALSE);
1865 if (info->proxy_data &&
1866 gtk_selection_data_get_target (info->proxy_data) == gtk_selection_data_get_target (selection_data))
1868 gtk_selection_data_set (info->proxy_data,
1869 gtk_selection_data_get_data_type (selection_data),
1870 gtk_selection_data_get_format (selection_data),
1871 gtk_selection_data_get_data (selection_data),
1872 gtk_selection_data_get_length (selection_data));
1877 target = gtk_selection_data_get_target (selection_data);
1878 if (target == gdk_atom_intern_static_string ("DELETE"))
1880 gtk_drag_finish (context, TRUE, FALSE, time);
1882 else if ((target == gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS")) ||
1883 (target == gdk_atom_intern_static_string ("XmTRANSFER_FAILURE")))
1889 GtkDragDestSite *site;
1891 site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1893 if (site && site->target_list)
1897 if (gtk_target_list_find (site->target_list,
1901 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1902 gtk_selection_data_get_length (selection_data) >= 0)
1903 g_signal_emit_by_name (drop_widget,
1904 "drag-data-received",
1905 context, info->drop_x, info->drop_y,
1912 g_signal_emit_by_name (drop_widget,
1913 "drag-data-received",
1914 context, info->drop_x, info->drop_y,
1919 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1922 gtk_drag_finish (context,
1923 (gtk_selection_data_get_length (selection_data) >= 0),
1924 (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
1928 g_object_unref (drop_widget);
1931 g_signal_handlers_disconnect_by_func (widget,
1932 gtk_drag_selection_received,
1935 g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
1936 g_object_unref (context);
1938 gtk_drag_release_ipc_widget (widget);
1941 /*************************************************************
1942 * gtk_drag_find_widget:
1943 * Function used to locate widgets for
1944 * DRAG_MOTION and DROP_START events.
1945 *************************************************************/
1948 gtk_drag_find_widget (GtkWidget *widget,
1949 GdkDragContext *context,
1950 GtkDragDestInfo *info,
1954 GtkDragDestCallback callback)
1956 if (!gtk_widget_get_mapped (widget) ||
1957 !gtk_widget_get_sensitive (widget))
1960 /* Get the widget at the pointer coordinates and travel up
1961 * the widget hierarchy from there.
1963 widget = _gtk_widget_find_at_coords (gtk_widget_get_window (widget),
1971 GList *hierarchy = NULL;
1972 gboolean found = FALSE;
1974 if (!gtk_widget_get_mapped (widget) ||
1975 !gtk_widget_get_sensitive (widget))
1978 /* need to reference the entire hierarchy temporarily in case the
1979 * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
1981 for (parent = widget;
1983 parent = gtk_widget_get_parent (parent))
1985 hierarchy = g_list_prepend (hierarchy, g_object_ref (parent));
1988 /* If the current widget is registered as a drop site, check to
1989 * emit "drag-motion" to check if we are actually in a drop
1992 if (g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1994 found = callback (widget, context, x, y, time);
1996 /* If so, send a "drag-leave" to the last widget */
1999 if (info->widget && info->widget != widget)
2001 gtk_drag_dest_leave (info->widget, context, time);
2004 info->widget = widget;
2010 /* Get the parent before unreffing the hierarchy because
2011 * invoking the callback might have destroyed the widget
2013 parent = gtk_widget_get_parent (widget);
2015 /* The parent might be going away when unreffing the
2016 * hierarchy, so also protect againt that
2019 g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
2022 g_list_free_full (hierarchy, g_object_unref);
2028 g_object_remove_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
2032 if (!gtk_widget_translate_coordinates (widget, parent, x, y, &x, &y))
2042 gtk_drag_proxy_begin (GtkWidget *widget,
2043 GtkDragDestInfo *dest_info,
2046 GtkDragSourceInfo *source_info;
2048 GdkDragContext *context;
2049 GtkWidget *ipc_widget;
2051 if (dest_info->proxy_source)
2053 gdk_drag_abort (dest_info->proxy_source->context, time);
2054 gtk_drag_source_info_destroy (dest_info->proxy_source);
2055 dest_info->proxy_source = NULL;
2058 ipc_widget = gtk_drag_get_ipc_widget (widget);
2059 context = gdk_drag_begin (gtk_widget_get_window (ipc_widget),
2060 gdk_drag_context_list_targets (dest_info->context));
2062 source_info = gtk_drag_get_source_info (context, TRUE);
2064 source_info->ipc_widget = ipc_widget;
2065 source_info->widget = g_object_ref (widget);
2067 source_info->target_list = gtk_target_list_new (NULL, 0);
2068 tmp_list = gdk_drag_context_list_targets (dest_info->context);
2071 gtk_target_list_add (source_info->target_list,
2072 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
2073 tmp_list = tmp_list->next;
2076 source_info->proxy_dest = dest_info;
2078 g_signal_connect (ipc_widget,
2080 G_CALLBACK (gtk_drag_selection_get),
2083 dest_info->proxy_source = source_info;
2087 gtk_drag_dest_info_destroy (gpointer data)
2089 g_slice_free (GtkDragDestInfo, data);
2092 static GtkDragDestInfo *
2093 gtk_drag_get_dest_info (GdkDragContext *context,
2096 GtkDragDestInfo *info;
2097 static GQuark info_quark = 0;
2099 info_quark = g_quark_from_static_string ("gtk-dest-info");
2101 info = g_object_get_qdata (G_OBJECT (context), info_quark);
2102 if (!info && create)
2104 info = g_slice_new0 (GtkDragDestInfo);
2105 info->context = context;
2106 g_object_set_qdata_full (G_OBJECT (context), info_quark,
2107 info, gtk_drag_dest_info_destroy);
2113 static GQuark dest_info_quark = 0;
2115 static GtkDragSourceInfo *
2116 gtk_drag_get_source_info (GdkDragContext *context,
2119 GtkDragSourceInfo *info;
2120 if (!dest_info_quark)
2121 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
2123 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
2124 if (!info && create)
2126 info = g_new0 (GtkDragSourceInfo, 1);
2127 info->context = context;
2128 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
2135 gtk_drag_clear_source_info (GdkDragContext *context)
2137 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
2141 gtk_drag_dest_realized (GtkWidget *widget)
2143 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2145 if (gtk_widget_is_toplevel (toplevel))
2146 gdk_window_register_dnd (gtk_widget_get_window (toplevel));
2150 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
2151 GtkWidget *previous_toplevel)
2153 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2155 if (gtk_widget_is_toplevel (toplevel) && gtk_widget_get_realized (toplevel))
2156 gdk_window_register_dnd (gtk_widget_get_window (toplevel));
2160 gtk_drag_dest_site_destroy (gpointer data)
2162 GtkDragDestSite *site = data;
2164 if (site->proxy_window)
2165 g_object_unref (site->proxy_window);
2167 if (site->target_list)
2168 gtk_target_list_unref (site->target_list);
2170 g_slice_free (GtkDragDestSite, site);
2174 * Default drag handlers
2177 gtk_drag_dest_leave (GtkWidget *widget,
2178 GdkDragContext *context,
2181 GtkDragDestSite *site;
2183 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2184 g_return_if_fail (site != NULL);
2188 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2190 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
2192 gdk_drag_abort (info->proxy_source->context, time);
2193 gtk_drag_source_info_destroy (info->proxy_source);
2194 info->proxy_source = NULL;
2201 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
2202 gtk_drag_unhighlight (widget);
2204 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
2206 g_signal_emit_by_name (widget, "drag-leave", context, time);
2208 site->have_drag = FALSE;
2213 gtk_drag_dest_motion (GtkWidget *widget,
2214 GdkDragContext *context,
2219 GtkDragDestSite *site;
2220 GdkDragAction action = 0;
2223 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2224 g_return_val_if_fail (site != NULL, FALSE);
2229 GdkEvent *current_event;
2230 GdkWindow *dest_window;
2231 GdkDragProtocol proto;
2233 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2235 if (!info->proxy_source || info->proxy_source->widget != widget)
2236 gtk_drag_proxy_begin (widget, info, time);
2238 current_event = gtk_get_current_event ();
2240 if (site->proxy_window)
2242 dest_window = site->proxy_window;
2243 proto = site->proxy_protocol;
2247 gdk_drag_find_window_for_screen (info->proxy_source->context,
2249 gdk_window_get_screen (current_event->dnd.window),
2250 current_event->dnd.x_root,
2251 current_event->dnd.y_root,
2252 &dest_window, &proto);
2255 gdk_drag_motion (info->proxy_source->context,
2257 current_event->dnd.x_root,
2258 current_event->dnd.y_root,
2259 gdk_drag_context_get_suggested_action (context),
2260 gdk_drag_context_get_actions (context),
2263 if (!site->proxy_window && dest_window)
2264 g_object_unref (dest_window);
2266 selection = gdk_drag_get_selection (info->proxy_source->context);
2268 selection != gdk_drag_get_selection (info->context))
2269 gtk_drag_source_check_selection (info->proxy_source, selection, time);
2271 gdk_event_free (current_event);
2276 if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
2278 if (gdk_drag_context_get_suggested_action (context) & site->actions)
2279 action = gdk_drag_context_get_suggested_action (context);
2284 for (i = 0; i < 8; i++)
2286 if ((site->actions & (1 << i)) &&
2287 (gdk_drag_context_get_actions (context) & (1 << i)))
2295 if (action && gtk_drag_dest_find_target (widget, context, NULL))
2297 if (!site->have_drag)
2299 site->have_drag = TRUE;
2300 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
2301 gtk_drag_highlight (widget);
2304 gdk_drag_status (context, action, time);
2308 gdk_drag_status (context, 0, time);
2309 if (!site->track_motion)
2314 g_signal_emit_by_name (widget, "drag-motion",
2315 context, x, y, time, &retval);
2317 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
2321 gtk_drag_dest_drop (GtkWidget *widget,
2322 GdkDragContext *context,
2327 GtkDragDestSite *site;
2328 GtkDragDestInfo *info;
2330 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2331 g_return_val_if_fail (site != NULL, FALSE);
2333 info = gtk_drag_get_dest_info (context, FALSE);
2334 g_return_val_if_fail (info != NULL, FALSE);
2341 if (info->proxy_source ||
2342 (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_ROOTWIN))
2344 gtk_drag_drop (info->proxy_source, time);
2348 /* We need to synthesize a motion event, wait for a status,
2349 * and, if we get a good one, do a drop.
2352 GdkEvent *current_event;
2354 GdkWindow *dest_window;
2355 GdkDragProtocol proto;
2357 gtk_drag_proxy_begin (widget, info, time);
2358 info->proxy_drop_wait = TRUE;
2359 info->proxy_drop_time = time;
2361 current_event = gtk_get_current_event ();
2363 if (site->proxy_window)
2365 dest_window = site->proxy_window;
2366 proto = site->proxy_protocol;
2370 gdk_drag_find_window_for_screen (info->proxy_source->context,
2372 gdk_window_get_screen (current_event->dnd.window),
2373 current_event->dnd.x_root,
2374 current_event->dnd.y_root,
2375 &dest_window, &proto);
2378 gdk_drag_motion (info->proxy_source->context,
2380 current_event->dnd.x_root,
2381 current_event->dnd.y_root,
2382 gdk_drag_context_get_suggested_action (context),
2383 gdk_drag_context_get_actions (context),
2386 if (!site->proxy_window && dest_window)
2387 g_object_unref (dest_window);
2389 selection = gdk_drag_get_selection (info->proxy_source->context);
2391 selection != gdk_drag_get_selection (info->context))
2392 gtk_drag_source_check_selection (info->proxy_source, selection, time);
2394 gdk_event_free (current_event);
2403 if (site->flags & GTK_DEST_DEFAULT_DROP)
2405 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
2407 if (target == GDK_NONE)
2409 gtk_drag_finish (context, FALSE, FALSE, time);
2413 gtk_drag_get_data (widget, context, target, time);
2416 g_signal_emit_by_name (widget, "drag-drop",
2417 context, x, y, time, &retval);
2419 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
2427 static GtkIconHelper *
2428 gtk_drag_source_site_get_icon_helper (GtkDragSourceSite *site)
2430 GtkIconHelper *helper;
2433 helper = g_object_ref (site->icon_helper);
2435 helper = _gtk_icon_helper_new ();
2437 if (_gtk_icon_helper_get_is_empty (helper))
2438 _gtk_icon_helper_set_stock_id (helper, GTK_STOCK_DND, GTK_ICON_SIZE_DND);
2443 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
2444 * so that we can set the icon from the source site information
2446 static GdkDragContext *
2447 gtk_drag_begin_internal (GtkWidget *widget,
2448 GtkDragSourceSite *site,
2449 GtkTargetList *target_list,
2450 GdkDragAction actions,
2454 GtkDragSourceInfo *info;
2455 GList *targets = NULL;
2457 guint32 time = GDK_CURRENT_TIME;
2458 GdkDragAction possible_actions, suggested_action;
2459 GdkDragContext *context;
2460 GtkWidget *ipc_widget;
2462 GdkDevice *pointer, *keyboard;
2463 GdkWindow *ipc_window;
2465 pointer = keyboard = NULL;
2466 ipc_widget = gtk_drag_get_ipc_widget (widget);
2468 gtk_drag_get_event_actions (event, button, actions,
2469 &suggested_action, &possible_actions);
2471 cursor = gtk_drag_get_cursor (widget,
2472 gtk_widget_get_display (widget),
2478 time = gdk_event_get_time (event);
2479 if (time == GDK_CURRENT_TIME)
2480 time = gtk_get_current_event_time ();
2482 pointer = gdk_event_get_device (event);
2484 if (gdk_device_get_source (pointer) == GDK_SOURCE_KEYBOARD)
2487 pointer = gdk_device_get_associated_device (keyboard);
2490 keyboard = gdk_device_get_associated_device (pointer);
2494 GdkDeviceManager *device_manager;
2496 device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
2497 pointer = gdk_device_manager_get_client_pointer (device_manager);
2498 keyboard = gdk_device_get_associated_device (pointer);
2504 ipc_window = gtk_widget_get_window (ipc_widget);
2506 if (gdk_device_grab (pointer, ipc_window,
2507 GDK_OWNERSHIP_APPLICATION, FALSE,
2508 GDK_POINTER_MOTION_MASK |
2509 GDK_BUTTON_RELEASE_MASK,
2510 cursor, time) != GDK_GRAB_SUCCESS)
2512 gtk_drag_release_ipc_widget (ipc_widget);
2517 grab_dnd_keys (ipc_widget, keyboard, time);
2519 /* We use a GTK grab here to override any grabs that the widget
2520 * we are dragging from might have held
2522 gtk_device_grab_add (ipc_widget, pointer, FALSE);
2524 tmp_list = g_list_last (target_list->list);
2527 GtkTargetPair *pair = tmp_list->data;
2528 targets = g_list_prepend (targets,
2529 GINT_TO_POINTER (pair->target));
2530 tmp_list = tmp_list->prev;
2533 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
2535 context = gdk_drag_begin (ipc_window, targets);
2536 gdk_drag_context_set_device (context, pointer);
2537 g_list_free (targets);
2539 info = gtk_drag_get_source_info (context, TRUE);
2541 info->ipc_widget = ipc_widget;
2542 g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
2544 info->widget = g_object_ref (widget);
2546 info->button = button;
2547 info->cursor = cursor;
2548 info->target_list = target_list;
2549 gtk_target_list_ref (target_list);
2551 info->possible_actions = actions;
2553 info->status = GTK_DRAG_STATUS_DRAG;
2554 info->last_event = NULL;
2555 info->selections = NULL;
2556 info->icon_window = NULL;
2557 info->destroy_icon = FALSE;
2559 /* Set cur_x, cur_y here so if the "drag-begin" signal shows
2560 * the drag icon, it will be in the right place
2562 if (event && event->type == GDK_MOTION_NOTIFY)
2564 info->cur_screen = gtk_widget_get_screen (widget);
2565 info->cur_x = event->motion.x_root;
2566 info->cur_y = event->motion.y_root;
2570 gdk_device_get_position (pointer, &info->cur_screen, &info->cur_x, &info->cur_y);
2573 g_signal_emit_by_name (widget, "drag-begin", info->context);
2575 /* Ensure that we have an icon before we start the drag; the
2576 * application may have set one in ::drag_begin, or it may
2579 if (!info->icon_window && !info->icon_helper)
2580 info->icon_helper = gtk_drag_source_site_get_icon_helper (site);
2582 /* We need to composite the icon into the cursor, if we are
2583 * not using an icon window.
2585 if (info->icon_helper)
2587 cursor = gtk_drag_get_cursor (widget,
2588 gtk_widget_get_display (widget),
2592 if (cursor != info->cursor)
2594 gdk_device_grab (pointer, gtk_widget_get_window (widget),
2595 GDK_OWNERSHIP_APPLICATION, FALSE,
2596 GDK_POINTER_MOTION_MASK |
2597 GDK_BUTTON_RELEASE_MASK,
2599 info->cursor = cursor;
2603 if (event && event->type == GDK_MOTION_NOTIFY)
2604 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
2606 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
2608 info->start_x = info->cur_x;
2609 info->start_y = info->cur_y;
2611 g_signal_connect (info->ipc_widget, "grab-broken-event",
2612 G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
2613 g_signal_connect (info->ipc_widget, "grab-notify",
2614 G_CALLBACK (gtk_drag_grab_notify_cb), info);
2615 g_signal_connect (info->ipc_widget, "button-release-event",
2616 G_CALLBACK (gtk_drag_button_release_cb), info);
2617 g_signal_connect (info->ipc_widget, "motion-notify-event",
2618 G_CALLBACK (gtk_drag_motion_cb), info);
2619 g_signal_connect (info->ipc_widget, "key-press-event",
2620 G_CALLBACK (gtk_drag_key_cb), info);
2621 g_signal_connect (info->ipc_widget, "key-release-event",
2622 G_CALLBACK (gtk_drag_key_cb), info);
2623 g_signal_connect (info->ipc_widget, "selection-get",
2624 G_CALLBACK (gtk_drag_selection_get), info);
2626 info->have_grab = TRUE;
2627 info->grab_time = time;
2629 return info->context;
2633 * gtk_drag_begin: (method)
2634 * @widget: the source widget.
2635 * @targets: The targets (data formats) in which the
2636 * source can provide the data.
2637 * @actions: A bitmask of the allowed drag actions for this drag.
2638 * @button: The button the user clicked to start the drag.
2639 * @event: The event that triggered the start of the drag.
2641 * Initiates a drag on the source side. The function
2642 * only needs to be used when the application is
2643 * starting drags itself, and is not needed when
2644 * gtk_drag_source_set() is used.
2646 * The @event is used to retrieve the timestamp that will be used internally to
2647 * grab the pointer. If @event is #NULL, then GDK_CURRENT_TIME will be used.
2648 * However, you should try to pass a real event in all cases, since that can be
2649 * used by GTK+ to get information about the start position of the drag, for
2650 * example if the @event is a %GDK_MOTION_NOTIFY.
2652 * Generally there are three cases when you want to start a drag by hand by
2653 * calling this function:
2655 * 1. During a #GtkWidget::button-press-event handler, if you want to start a drag
2656 * immediately when the user presses the mouse button. Pass the @event
2657 * that you have in your #GtkWidget::button-press-event handler.
2659 * 2. During a #GtkWidget::motion-notify-event handler, if you want to start a drag
2660 * when the mouse moves past a certain threshold distance after a button-press.
2661 * Pass the @event that you have in your #GtkWidget::motion-notify-event handler.
2663 * 3. During a timeout handler, if you want to start a drag after the mouse
2664 * button is held down for some time. Try to save the last event that you got
2665 * from the mouse, using gdk_event_copy(), and pass it to this function
2666 * (remember to free the event with gdk_event_free() when you are done).
2667 * If you can really not pass a real event, pass #NULL instead.
2669 * Return value: (transfer none): the context for this drag.
2672 gtk_drag_begin (GtkWidget *widget,
2673 GtkTargetList *targets,
2674 GdkDragAction actions,
2678 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2679 g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
2680 g_return_val_if_fail (targets != NULL, NULL);
2682 return gtk_drag_begin_internal (widget, NULL, targets,
2683 actions, button, event);
2687 * gtk_drag_source_set: (method)
2688 * @widget: a #GtkWidget
2689 * @start_button_mask: the bitmask of buttons that can start the drag
2690 * @targets: (allow-none) (array length=n_targets): the table of targets that the drag will support,
2692 * @n_targets: the number of items in @targets
2693 * @actions: the bitmask of possible actions for a drag from this widget
2695 * Sets up a widget so that GTK+ will start a drag operation when the user
2696 * clicks and drags on the widget. The widget must have a window.
2699 gtk_drag_source_set (GtkWidget *widget,
2700 GdkModifierType start_button_mask,
2701 const GtkTargetEntry *targets,
2703 GdkDragAction actions)
2705 GtkDragSourceSite *site;
2707 g_return_if_fail (GTK_IS_WIDGET (widget));
2709 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2711 gtk_widget_add_events (widget,
2712 gtk_widget_get_events (widget) |
2713 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2714 GDK_BUTTON_MOTION_MASK);
2718 if (site->target_list)
2719 gtk_target_list_unref (site->target_list);
2723 site = g_slice_new0 (GtkDragSourceSite);
2724 site->icon_helper = _gtk_icon_helper_new ();
2726 g_signal_connect (widget, "button-press-event",
2727 G_CALLBACK (gtk_drag_source_event_cb),
2729 g_signal_connect (widget, "button-release-event",
2730 G_CALLBACK (gtk_drag_source_event_cb),
2732 g_signal_connect (widget, "motion-notify-event",
2733 G_CALLBACK (gtk_drag_source_event_cb),
2736 g_object_set_data_full (G_OBJECT (widget),
2737 I_("gtk-site-data"),
2738 site, gtk_drag_source_site_destroy);
2741 site->start_button_mask = start_button_mask;
2743 site->target_list = gtk_target_list_new (targets, n_targets);
2745 site->actions = actions;
2749 * gtk_drag_source_unset: (method)
2750 * @widget: a #GtkWidget
2752 * Undoes the effects of gtk_drag_source_set().
2755 gtk_drag_source_unset (GtkWidget *widget)
2757 GtkDragSourceSite *site;
2759 g_return_if_fail (GTK_IS_WIDGET (widget));
2761 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2765 g_signal_handlers_disconnect_by_func (widget,
2766 gtk_drag_source_event_cb,
2768 g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
2773 * gtk_drag_source_get_target_list: (method)
2774 * @widget: a #GtkWidget
2776 * Gets the list of targets this widget can provide for
2779 * Return value: (transfer none): the #GtkTargetList, or %NULL if none
2784 gtk_drag_source_get_target_list (GtkWidget *widget)
2786 GtkDragSourceSite *site;
2788 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2790 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2792 return site ? site->target_list : NULL;
2796 * gtk_drag_source_set_target_list: (method)
2797 * @widget: a #GtkWidget that's a drag source
2798 * @target_list: (allow-none): list of draggable targets, or %NULL for none
2800 * Changes the target types that this widget offers for drag-and-drop.
2801 * The widget must first be made into a drag source with
2802 * gtk_drag_source_set().
2807 gtk_drag_source_set_target_list (GtkWidget *widget,
2808 GtkTargetList *target_list)
2810 GtkDragSourceSite *site;
2812 g_return_if_fail (GTK_IS_WIDGET (widget));
2814 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2817 g_warning ("gtk_drag_source_set_target_list() requires the widget "
2818 "to already be a drag source.");
2823 gtk_target_list_ref (target_list);
2825 if (site->target_list)
2826 gtk_target_list_unref (site->target_list);
2828 site->target_list = target_list;
2832 * gtk_drag_source_add_text_targets: (method)
2833 * @widget: a #GtkWidget that's is a drag source
2835 * Add the text targets supported by #GtkSelection to
2836 * the target list of the drag source. The targets
2837 * are added with @info = 0. If you need another value,
2838 * use gtk_target_list_add_text_targets() and
2839 * gtk_drag_source_set_target_list().
2844 gtk_drag_source_add_text_targets (GtkWidget *widget)
2846 GtkTargetList *target_list;
2848 target_list = gtk_drag_source_get_target_list (widget);
2850 gtk_target_list_ref (target_list);
2852 target_list = gtk_target_list_new (NULL, 0);
2853 gtk_target_list_add_text_targets (target_list, 0);
2854 gtk_drag_source_set_target_list (widget, target_list);
2855 gtk_target_list_unref (target_list);
2859 * gtk_drag_source_add_image_targets: (method)
2860 * @widget: a #GtkWidget that's is a drag source
2862 * Add the writable image targets supported by #GtkSelection to
2863 * the target list of the drag source. The targets
2864 * are added with @info = 0. If you need another value,
2865 * use gtk_target_list_add_image_targets() and
2866 * gtk_drag_source_set_target_list().
2871 gtk_drag_source_add_image_targets (GtkWidget *widget)
2873 GtkTargetList *target_list;
2875 target_list = gtk_drag_source_get_target_list (widget);
2877 gtk_target_list_ref (target_list);
2879 target_list = gtk_target_list_new (NULL, 0);
2880 gtk_target_list_add_image_targets (target_list, 0, TRUE);
2881 gtk_drag_source_set_target_list (widget, target_list);
2882 gtk_target_list_unref (target_list);
2886 * gtk_drag_source_add_uri_targets: (method)
2887 * @widget: a #GtkWidget that's is a drag source
2889 * Add the URI targets supported by #GtkSelection to
2890 * the target list of the drag source. The targets
2891 * are added with @info = 0. If you need another value,
2892 * use gtk_target_list_add_uri_targets() and
2893 * gtk_drag_source_set_target_list().
2898 gtk_drag_source_add_uri_targets (GtkWidget *widget)
2900 GtkTargetList *target_list;
2902 target_list = gtk_drag_source_get_target_list (widget);
2904 gtk_target_list_ref (target_list);
2906 target_list = gtk_target_list_new (NULL, 0);
2907 gtk_target_list_add_uri_targets (target_list, 0);
2908 gtk_drag_source_set_target_list (widget, target_list);
2909 gtk_target_list_unref (target_list);
2913 * gtk_drag_source_set_icon_pixbuf: (method)
2914 * @widget: a #GtkWidget
2915 * @pixbuf: the #GdkPixbuf for the drag icon
2917 * Sets the icon that will be used for drags from a particular widget
2918 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2919 * release it when it is no longer needed.
2922 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2925 GtkDragSourceSite *site;
2927 g_return_if_fail (GTK_IS_WIDGET (widget));
2928 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2930 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2931 g_return_if_fail (site != NULL);
2932 g_object_ref (pixbuf);
2934 _gtk_icon_helper_set_pixbuf (site->icon_helper, pixbuf);
2938 * gtk_drag_source_set_icon_stock: (method)
2939 * @widget: a #GtkWidget
2940 * @stock_id: the ID of the stock icon to use
2942 * Sets the icon that will be used for drags from a particular source
2946 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2947 const gchar *stock_id)
2949 GtkDragSourceSite *site;
2951 g_return_if_fail (GTK_IS_WIDGET (widget));
2952 g_return_if_fail (stock_id != NULL);
2954 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2955 g_return_if_fail (site != NULL);
2957 _gtk_icon_helper_set_stock_id (site->icon_helper, stock_id, GTK_ICON_SIZE_DND);
2961 * gtk_drag_source_set_icon_name: (method)
2962 * @widget: a #GtkWidget
2963 * @icon_name: name of icon to use
2965 * Sets the icon that will be used for drags from a particular source
2966 * to a themed icon. See the docs for #GtkIconTheme for more details.
2971 gtk_drag_source_set_icon_name (GtkWidget *widget,
2972 const gchar *icon_name)
2974 GtkDragSourceSite *site;
2976 g_return_if_fail (GTK_IS_WIDGET (widget));
2977 g_return_if_fail (icon_name != NULL);
2979 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2980 g_return_if_fail (site != NULL);
2982 _gtk_icon_helper_set_icon_name (site->icon_helper, icon_name, GTK_ICON_SIZE_DND);
2986 * gtk_drag_source_set_icon_gicon: (method)
2987 * @widget: a #GtkWidget
2990 * Sets the icon that will be used for drags from a particular source
2991 * to @icon. See the docs for #GtkIconTheme for more details.
2996 gtk_drag_source_set_icon_gicon (GtkWidget *widget,
2999 GtkDragSourceSite *site;
3001 g_return_if_fail (GTK_IS_WIDGET (widget));
3002 g_return_if_fail (icon != NULL);
3004 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
3005 g_return_if_fail (site != NULL);
3007 _gtk_icon_helper_set_gicon (site->icon_helper, icon, GTK_ICON_SIZE_DND);
3011 gtk_drag_get_icon (GtkDragSourceInfo *info,
3012 GtkWidget **icon_window,
3016 if (get_can_change_screen (info->icon_window))
3017 gtk_window_set_screen (GTK_WINDOW (info->icon_window),
3020 if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
3022 if (!info->fallback_icon)
3024 gint save_hot_x, save_hot_y;
3025 gboolean save_destroy_icon;
3026 GtkWidget *save_icon_window;
3027 GtkIconHelper *helper;
3029 /* HACK to get the appropriate icon
3031 save_icon_window = info->icon_window;
3032 save_hot_x = info->hot_x;
3033 save_hot_y = info->hot_x;
3034 save_destroy_icon = info->destroy_icon;
3036 info->icon_window = NULL;
3038 helper = _gtk_icon_helper_new ();
3039 _gtk_icon_helper_set_stock_id (helper, GTK_STOCK_DND, GTK_ICON_SIZE_DND);
3040 set_icon_helper (info->context, helper, -2, -2, TRUE);
3041 info->fallback_icon = info->icon_window;
3043 info->icon_window = save_icon_window;
3044 info->hot_x = save_hot_x;
3045 info->hot_y = save_hot_y;
3046 info->destroy_icon = save_destroy_icon;
3048 g_object_unref (helper);
3051 gtk_widget_hide (info->icon_window);
3053 *icon_window = info->fallback_icon;
3054 gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
3061 if (info->fallback_icon)
3062 gtk_widget_hide (info->fallback_icon);
3064 *icon_window = info->icon_window;
3065 *hot_x = info->hot_x;
3066 *hot_y = info->hot_y;
3071 gtk_drag_update_icon (GtkDragSourceInfo *info)
3073 if (info->icon_window)
3075 GtkWidget *icon_window;
3078 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
3080 gtk_window_move (GTK_WINDOW (icon_window),
3081 info->cur_x - hot_x,
3082 info->cur_y - hot_y);
3084 if (gtk_widget_get_visible (icon_window))
3085 gdk_window_raise (gtk_widget_get_window (icon_window));
3087 gtk_widget_show (icon_window);
3092 gtk_drag_set_icon_window (GdkDragContext *context,
3096 gboolean destroy_on_release)
3098 GtkDragSourceInfo *info;
3100 info = gtk_drag_get_source_info (context, FALSE);
3103 if (destroy_on_release)
3104 gtk_widget_destroy (widget);
3108 gtk_drag_remove_icon (info);
3111 g_object_ref (widget);
3113 info->icon_window = widget;
3114 info->hot_x = hot_x;
3115 info->hot_y = hot_y;
3116 info->destroy_icon = destroy_on_release;
3118 if (widget && info->icon_helper)
3119 g_clear_object (&info->icon_helper);
3121 gtk_drag_update_cursor (info);
3122 gtk_drag_update_icon (info);
3126 * gtk_drag_set_icon_widget: (method)
3127 * @context: the context for a drag. (This must be called
3128 with a context for the source side of a drag)
3129 * @widget: a toplevel window to use as an icon.
3130 * @hot_x: the X offset within @widget of the hotspot.
3131 * @hot_y: the Y offset within @widget of the hotspot.
3133 * Changes the icon for a widget to a given widget. GTK+
3134 * will not destroy the icon, so if you don't want
3135 * it to persist, you should connect to the "drag-end"
3136 * signal and destroy it yourself.
3139 gtk_drag_set_icon_widget (GdkDragContext *context,
3144 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3145 g_return_if_fail (GTK_IS_WIDGET (widget));
3147 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
3151 icon_window_realize (GtkWidget *window,
3152 GtkIconHelper *helper)
3154 cairo_surface_t *surface;
3155 cairo_pattern_t *pattern;
3159 pixbuf = _gtk_icon_helper_ensure_pixbuf (helper, gtk_widget_get_style_context (window));
3160 surface = gdk_window_create_similar_surface (gtk_widget_get_window (window),
3161 CAIRO_CONTENT_COLOR,
3162 gdk_pixbuf_get_width (pixbuf),
3163 gdk_pixbuf_get_height (pixbuf));
3165 cr = cairo_create (surface);
3166 cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
3167 gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
3169 cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
3171 cairo_pop_group_to_source (cr);
3175 pattern = cairo_pattern_create_for_surface (surface);
3176 gdk_window_set_background_pattern (gtk_widget_get_window (window), pattern);
3177 cairo_pattern_destroy (pattern);
3179 cairo_surface_destroy (surface);
3181 if (gdk_pixbuf_get_has_alpha (pixbuf))
3183 cairo_region_t *region;
3185 surface = cairo_image_surface_create (CAIRO_FORMAT_A1,
3186 gdk_pixbuf_get_width (pixbuf),
3187 gdk_pixbuf_get_height (pixbuf));
3189 cr = cairo_create (surface);
3190 gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
3194 region = gdk_cairo_region_create_from_surface (surface);
3195 gtk_widget_shape_combine_region (window, region);
3196 cairo_region_destroy (region);
3198 cairo_surface_destroy (surface);
3201 g_object_unref (pixbuf);
3205 set_icon_helper (GdkDragContext *context,
3206 GtkIconHelper *helper,
3209 gboolean force_window)
3214 GdkDisplay *display;
3216 g_return_if_fail (context != NULL);
3217 g_return_if_fail (helper != NULL);
3219 screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
3221 window = gtk_window_new (GTK_WINDOW_POPUP);
3222 gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3223 gtk_window_set_screen (GTK_WINDOW (window), screen);
3224 set_can_change_screen (window, TRUE);
3226 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3227 gtk_widget_set_app_paintable (window, TRUE);
3229 display = gdk_window_get_display (gdk_drag_context_get_source_window (context));
3230 _gtk_icon_helper_get_size (helper,
3231 gtk_widget_get_style_context (window),
3234 if (!force_window &&
3235 gtk_drag_can_use_rgba_cursor (display, width + 2, height + 2))
3237 GtkDragSourceInfo *info;
3239 gtk_widget_destroy (window);
3241 info = gtk_drag_get_source_info (context, FALSE);
3243 if (info->icon_helper)
3244 g_object_unref (info->icon_helper);
3245 info->icon_helper = g_object_ref (helper);
3247 gtk_drag_set_icon_window (context, NULL, hot_x, hot_y, TRUE);
3251 gtk_widget_set_size_request (window, width, height);
3253 g_signal_connect_closure (window, "realize",
3254 g_cclosure_new (G_CALLBACK (icon_window_realize),
3255 g_object_ref (helper),
3256 (GClosureNotify)g_object_unref),
3259 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3264 * gtk_drag_set_icon_pixbuf: (method)
3265 * @context: the context for a drag. (This must be called
3266 * with a context for the source side of a drag)
3267 * @pixbuf: the #GdkPixbuf to use as the drag icon.
3268 * @hot_x: the X offset within @widget of the hotspot.
3269 * @hot_y: the Y offset within @widget of the hotspot.
3271 * Sets @pixbuf as the icon for a given drag.
3274 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
3279 GtkIconHelper *icon;
3281 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3282 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
3284 icon = _gtk_icon_helper_new ();
3285 _gtk_icon_helper_set_pixbuf (icon, pixbuf);
3286 set_icon_helper (context, icon, hot_x, hot_y, FALSE);
3288 g_object_unref (icon);
3292 * gtk_drag_set_icon_stock: (method)
3293 * @context: the context for a drag. (This must be called
3294 * with a context for the source side of a drag)
3295 * @stock_id: the ID of the stock icon to use for the drag.
3296 * @hot_x: the X offset within the icon of the hotspot.
3297 * @hot_y: the Y offset within the icon of the hotspot.
3299 * Sets the icon for a given drag from a stock ID.
3302 gtk_drag_set_icon_stock (GdkDragContext *context,
3303 const gchar *stock_id,
3307 GtkIconHelper *icon;
3309 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3310 g_return_if_fail (stock_id != NULL);
3312 icon = _gtk_icon_helper_new ();
3313 _gtk_icon_helper_set_stock_id (icon, stock_id, GTK_ICON_SIZE_DND);
3314 set_icon_helper (context, icon, hot_x, hot_y, FALSE);
3316 g_object_unref (icon);
3319 /* XXX: This function is in gdk, too. Should it be in Cairo? */
3321 _gtk_cairo_surface_extents (cairo_surface_t *surface,
3322 GdkRectangle *extents)
3324 double x1, x2, y1, y2;
3327 g_return_val_if_fail (surface != NULL, FALSE);
3328 g_return_val_if_fail (extents != NULL, FALSE);
3330 cr = cairo_create (surface);
3331 cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
3340 if (x1 < G_MININT || x1 > G_MAXINT ||
3341 y1 < G_MININT || y1 > G_MAXINT ||
3342 x2 > G_MAXINT || y2 > G_MAXINT)
3344 extents->x = extents->y = extents->width = extents->height = 0;
3350 extents->width = x2;
3351 extents->height = y2;
3357 * gtk_drag_set_icon_surface: (method)
3358 * @context: the context for a drag. (This must be called
3359 * with a context for the source side of a drag)
3360 * @surface: the surface to use as icon
3362 * Sets @surface as the icon for a given drag. GTK+ retains
3363 * references for the arguments, and will release them when
3364 * they are no longer needed.
3366 * To position the surface relative to the mouse, use
3367 * cairo_surface_set_device_offset() on @surface. The mouse
3368 * cursor will be positioned at the (0,0) coordinate of the
3372 gtk_drag_set_icon_surface (GdkDragContext *context,
3373 cairo_surface_t *surface)
3377 GdkRectangle extents;
3378 cairo_pattern_t *pattern;
3380 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3381 g_return_if_fail (surface != NULL);
3383 _gtk_cairo_surface_extents (surface, &extents);
3386 screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
3388 window = gtk_window_new (GTK_WINDOW_POPUP);
3389 gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3390 gtk_window_set_screen (GTK_WINDOW (window), screen);
3391 set_can_change_screen (window, TRUE);
3393 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3394 gtk_widget_set_app_paintable (window, TRUE);
3396 gtk_widget_set_size_request (window, extents.width, extents.height);
3397 gtk_widget_realize (window);
3399 if (cairo_surface_get_content (surface) != CAIRO_CONTENT_COLOR)
3401 cairo_surface_t *saturated;
3402 cairo_region_t *region;
3405 region = gdk_cairo_region_create_from_surface (surface);
3406 cairo_region_translate (region, -extents.x, -extents.y);
3408 gtk_widget_shape_combine_region (window, region);
3409 cairo_region_destroy (region);
3411 /* Need to saturate the colors, so it doesn't look like semi-transparent
3412 * pixels were painted on black. */
3413 saturated = gdk_window_create_similar_surface (gtk_widget_get_window (window),
3414 CAIRO_CONTENT_COLOR,
3418 cr = cairo_create (saturated);
3419 cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
3420 cairo_set_source_surface (cr, surface, -extents.x, -extents.y);
3422 cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
3424 cairo_pop_group_to_source (cr);
3428 pattern = cairo_pattern_create_for_surface (saturated);
3430 cairo_surface_destroy (saturated);
3434 cairo_matrix_t matrix;
3436 pattern = cairo_pattern_create_for_surface (surface);
3437 cairo_matrix_init_translate (&matrix, extents.x, extents.y);
3438 cairo_pattern_set_matrix (pattern, &matrix);
3441 gdk_window_set_background_pattern (gtk_widget_get_window (window), pattern);
3443 gtk_drag_set_icon_window (context, window, extents.x, extents.y, TRUE);
3447 * gtk_drag_set_icon_name: (method)
3448 * @context: the context for a drag. (This must be called
3449 * with a context for the source side of a drag)
3450 * @icon_name: name of icon to use
3451 * @hot_x: the X offset of the hotspot within the icon
3452 * @hot_y: the Y offset of the hotspot within the icon
3454 * Sets the icon for a given drag from a named themed icon. See
3455 * the docs for #GtkIconTheme for more details. Note that the
3456 * size of the icon depends on the icon theme (the icon is
3457 * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus
3458 * @hot_x and @hot_y have to be used with care.
3463 gtk_drag_set_icon_name (GdkDragContext *context,
3464 const gchar *icon_name,
3468 GtkIconHelper *icon;
3470 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3471 g_return_if_fail (icon_name != NULL);
3473 icon = _gtk_icon_helper_new ();
3474 _gtk_icon_helper_set_icon_name (icon, icon_name, GTK_ICON_SIZE_DND);
3475 set_icon_helper (context, icon, hot_x, hot_y, FALSE);
3477 g_object_unref (icon);
3481 * gtk_drag_set_icon_gicon: (method)
3482 * @context: the context for a drag. (This must be called
3483 * with a context for the source side of a drag)
3485 * @hot_x: the X offset of the hotspot within the icon
3486 * @hot_y: the Y offset of the hotspot within the icon
3488 * Sets the icon for a given drag from the given @icon. See the
3489 * documentation for gtk_drag_set_icon_name() for more details about
3490 * using icons in drag and drop.
3495 gtk_drag_set_icon_gicon (GdkDragContext *context,
3500 GtkIconHelper *helper;
3502 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3503 g_return_if_fail (icon != NULL);
3505 helper = _gtk_icon_helper_new ();
3506 _gtk_icon_helper_set_gicon (helper, icon, GTK_ICON_SIZE_DND);
3507 set_icon_helper (context, helper, hot_x, hot_y, FALSE);
3509 g_object_unref (helper);
3513 * gtk_drag_set_icon_default: (method)
3514 * @context: the context for a drag. (This must be called
3515 with a context for the source side of a drag)
3517 * Sets the icon for a particular drag to the default
3521 gtk_drag_set_icon_default (GdkDragContext *context)
3523 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3525 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
3528 /*************************************************************
3529 * _gtk_drag_source_handle_event:
3530 * Called from widget event handling code on Drag events
3534 * toplevel: Toplevel widget that received the event
3537 *************************************************************/
3540 _gtk_drag_source_handle_event (GtkWidget *widget,
3543 GtkDragSourceInfo *info;
3544 GdkDragContext *context;
3546 g_return_if_fail (widget != NULL);
3547 g_return_if_fail (event != NULL);
3549 context = event->dnd.context;
3550 info = gtk_drag_get_source_info (context, FALSE);
3554 switch (event->type)
3556 case GDK_DRAG_STATUS:
3560 if (info->proxy_dest)
3562 if (!event->dnd.send_event)
3564 if (info->proxy_dest->proxy_drop_wait)
3566 gboolean result = gdk_drag_context_get_selected_action (context) != 0;
3568 /* Aha - we can finally pass the DROP on... */
3569 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
3571 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
3573 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
3577 gdk_drag_status (info->proxy_dest->context,
3578 gdk_drag_context_get_selected_action (event->dnd.context),
3583 else if (info->have_grab)
3585 cursor = gtk_drag_get_cursor (widget,
3586 gtk_widget_get_display (widget),
3587 gdk_drag_context_get_selected_action (event->dnd.context),
3589 if (info->cursor != cursor)
3593 pointer = gdk_drag_context_get_device (context);
3594 gdk_device_grab (pointer, gtk_widget_get_window (widget),
3595 GDK_OWNERSHIP_APPLICATION, FALSE,
3596 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3597 cursor, info->grab_time);
3598 info->cursor = cursor;
3601 gtk_drag_add_update_idle (info);
3606 case GDK_DROP_FINISHED:
3607 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, event->dnd.time);
3610 g_assert_not_reached ();
3614 /*************************************************************
3615 * gtk_drag_source_check_selection:
3616 * Check if we've set up handlers/claimed the selection
3617 * for a given drag. If not, add them.
3621 *************************************************************/
3624 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
3630 tmp_list = info->selections;
3633 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
3635 tmp_list = tmp_list->next;
3638 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
3642 info->selections = g_list_prepend (info->selections,
3643 GUINT_TO_POINTER (selection));
3645 tmp_list = info->target_list->list;
3648 GtkTargetPair *pair = tmp_list->data;
3650 gtk_selection_add_target (info->ipc_widget,
3654 tmp_list = tmp_list->next;
3657 gtk_selection_add_target (info->ipc_widget,
3659 gdk_atom_intern_static_string ("DELETE"),
3663 /*************************************************************
3664 * gtk_drag_drop_finished:
3665 * Clean up from the drag, and display snapback, if necessary.
3671 *************************************************************/
3674 gtk_drag_drop_finished (GtkDragSourceInfo *info,
3675 GtkDragResult result,
3680 success = (result == GTK_DRAG_RESULT_SUCCESS);
3681 gtk_drag_source_release_selections (info, time);
3683 if (info->proxy_dest)
3685 /* The time from the event isn't reliable for Xdnd drags */
3686 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
3687 info->proxy_dest->proxy_drop_time);
3688 gtk_drag_source_info_destroy (info);
3693 g_signal_emit_by_name (info->widget, "drag-failed",
3694 info->context, result, &success);
3698 gtk_drag_source_info_destroy (info);
3702 GtkDragAnim *anim = g_slice_new0 (GtkDragAnim);
3706 anim->n_steps = MAX (info->cur_x - info->start_x,
3707 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
3708 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
3710 info->cur_screen = gtk_widget_get_screen (info->widget);
3712 if (!info->icon_window)
3713 set_icon_helper (info->context, info->icon_helper,
3716 gtk_drag_update_icon (info);
3718 /* Mark the context as dead, so if the destination decides
3719 * to respond really late, we still are OK.
3721 gtk_drag_clear_source_info (info->context);
3722 gdk_threads_add_timeout (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
3728 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
3731 GdkDisplay *display = gtk_widget_get_display (info->widget);
3732 GList *tmp_list = info->selections;
3736 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
3737 if (gdk_selection_owner_get_for_display (display, selection) == gtk_widget_get_window (info->ipc_widget))
3738 gtk_selection_owner_set_for_display (display, NULL, selection, time);
3740 tmp_list = tmp_list->next;
3743 g_list_free (info->selections);
3744 info->selections = NULL;
3747 /*************************************************************
3749 * Send a drop event.
3753 *************************************************************/
3756 gtk_drag_drop (GtkDragSourceInfo *info,
3759 if (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_ROOTWIN)
3761 GtkSelectionData selection_data;
3763 /* GTK+ traditionally has used application/x-rootwin-drop, but the
3764 * XDND spec specifies x-rootwindow-drop.
3766 GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3767 GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3769 tmp_list = info->target_list->list;
3772 GtkTargetPair *pair = tmp_list->data;
3774 if (pair->target == target1 || pair->target == target2)
3776 selection_data.selection = GDK_NONE;
3777 selection_data.target = pair->target;
3778 selection_data.data = NULL;
3779 selection_data.length = -1;
3781 g_signal_emit_by_name (info->widget, "drag-data-get",
3782 info->context, &selection_data,
3786 /* FIXME: Should we check for length >= 0 here? */
3787 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3790 tmp_list = tmp_list->next;
3792 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3796 if (info->icon_window)
3797 gtk_widget_hide (info->icon_window);
3799 gdk_drag_drop (info->context, time);
3800 info->drop_timeout = gdk_threads_add_timeout (DROP_ABORT_TIME,
3801 gtk_drag_abort_timeout,
3807 * Source side callbacks.
3811 gtk_drag_source_event_cb (GtkWidget *widget,
3815 GtkDragSourceSite *site;
3816 gboolean retval = FALSE;
3817 site = (GtkDragSourceSite *)data;
3819 switch (event->type)
3821 case GDK_BUTTON_PRESS:
3822 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3824 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
3825 site->x = event->button.x;
3826 site->y = event->button.y;
3830 case GDK_BUTTON_RELEASE:
3831 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3832 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
3835 case GDK_MOTION_NOTIFY:
3836 if (site->state & event->motion.state & site->start_button_mask)
3838 /* FIXME: This is really broken and can leave us
3844 if (site->state & event->motion.state &
3845 GDK_BUTTON1_MASK << (i - 1))
3849 if (gtk_drag_check_threshold (widget, site->x, site->y,
3850 event->motion.x, event->motion.y))
3853 gtk_drag_begin_internal (widget, site, site->target_list,
3862 default: /* hit for 2/3BUTTON_PRESS */
3870 gtk_drag_source_site_destroy (gpointer data)
3872 GtkDragSourceSite *site = data;
3874 if (site->target_list)
3875 gtk_target_list_unref (site->target_list);
3877 g_clear_object (&site->icon_helper);
3878 g_slice_free (GtkDragSourceSite, site);
3882 gtk_drag_selection_get (GtkWidget *widget,
3883 GtkSelectionData *selection_data,
3888 GtkDragSourceInfo *info = data;
3889 static GdkAtom null_atom = GDK_NONE;
3893 null_atom = gdk_atom_intern_static_string ("NULL");
3898 g_signal_emit_by_name (info->widget,
3901 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3904 if (info->proxy_dest)
3906 /* This is sort of dangerous and needs to be thought
3909 info->proxy_dest->proxy_data = selection_data;
3910 gtk_drag_get_data (info->widget,
3911 info->proxy_dest->context,
3912 gtk_selection_data_get_target (selection_data),
3915 info->proxy_dest->proxy_data = NULL;
3919 if (gtk_target_list_find (info->target_list,
3920 gtk_selection_data_get_target (selection_data),
3923 g_signal_emit_by_name (info->widget, "drag-data-get",
3935 gtk_drag_anim_timeout (gpointer data)
3938 GtkDragSourceInfo *info;
3945 if (anim->step == anim->n_steps)
3947 gtk_drag_source_info_destroy (anim->info);
3948 g_slice_free (GtkDragAnim, anim);
3954 x = (info->start_x * (anim->step + 1) +
3955 info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3956 y = (info->start_y * (anim->step + 1) +
3957 info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3958 if (info->icon_window)
3960 GtkWidget *icon_window;
3963 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
3964 gtk_window_move (GTK_WINDOW (icon_window),
3978 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3980 if (info->icon_window)
3982 gtk_widget_hide (info->icon_window);
3983 if (info->destroy_icon)
3984 gtk_widget_destroy (info->icon_window);
3986 if (info->fallback_icon)
3988 gtk_widget_destroy (info->fallback_icon);
3989 info->fallback_icon = NULL;
3992 g_object_unref (info->icon_window);
3993 info->icon_window = NULL;
3998 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
4002 for (i = 0; i < G_N_ELEMENTS (drag_cursors); i++)
4004 if (info->drag_cursors[i] != NULL)
4006 g_object_unref (info->drag_cursors[i]);
4007 info->drag_cursors[i] = NULL;
4011 gtk_drag_remove_icon (info);
4012 g_clear_object (&info->icon_helper);
4014 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4015 gtk_drag_grab_broken_event_cb,
4017 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4018 gtk_drag_grab_notify_cb,
4020 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4021 gtk_drag_button_release_cb,
4023 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4026 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4029 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4030 gtk_drag_selection_get,
4033 if (!info->proxy_dest)
4034 g_signal_emit_by_name (info->widget, "drag-end", info->context);
4037 g_object_unref (info->widget);
4039 gtk_selection_remove_all (info->ipc_widget);
4040 g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), NULL);
4041 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
4042 gtk_drag_release_ipc_widget (info->ipc_widget);
4044 gtk_target_list_unref (info->target_list);
4046 gtk_drag_clear_source_info (info->context);
4047 g_object_unref (info->context);
4049 if (info->drop_timeout)
4050 g_source_remove (info->drop_timeout);
4052 if (info->update_idle)
4053 g_source_remove (info->update_idle);
4059 gtk_drag_update_idle (gpointer data)
4061 GtkDragSourceInfo *info = data;
4062 GdkWindow *dest_window;
4063 GdkDragProtocol protocol;
4066 GdkDragAction action;
4067 GdkDragAction possible_actions;
4070 info->update_idle = 0;
4072 if (info->last_event)
4074 time = gtk_drag_get_event_time (info->last_event);
4075 gtk_drag_get_event_actions (info->last_event,
4077 info->possible_actions,
4078 &action, &possible_actions);
4079 gtk_drag_update_icon (info);
4080 gdk_drag_find_window_for_screen (info->context,
4081 info->icon_window ? gtk_widget_get_window (info->icon_window) : NULL,
4082 info->cur_screen, info->cur_x, info->cur_y,
4083 &dest_window, &protocol);
4085 if (!gdk_drag_motion (info->context, dest_window, protocol,
4086 info->cur_x, info->cur_y, action,
4090 gdk_event_free ((GdkEvent *)info->last_event);
4091 info->last_event = NULL;
4095 g_object_unref (dest_window);
4097 selection = gdk_drag_get_selection (info->context);
4099 gtk_drag_source_check_selection (info, selection, time);
4107 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
4109 /* We use an idle lower than GDK_PRIORITY_REDRAW so that exposes
4110 * from the last move can catch up before we move again.
4112 if (!info->update_idle)
4113 info->update_idle = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW + 5,
4114 gtk_drag_update_idle,
4121 * @info: DragSourceInfo for the drag
4122 * @screen: new screen
4123 * @x_root: new X position
4124 * @y_root: new y position
4125 * @event: event received requiring update
4127 * Updates the status of the drag; called when the
4128 * cursor moves or the modifier changes
4131 gtk_drag_update (GtkDragSourceInfo *info,
4137 info->cur_screen = screen;
4138 info->cur_x = x_root;
4139 info->cur_y = y_root;
4140 if (info->last_event)
4142 gdk_event_free ((GdkEvent *)info->last_event);
4143 info->last_event = NULL;
4146 info->last_event = gdk_event_copy ((GdkEvent *)event);
4148 gtk_drag_add_update_idle (info);
4151 /*************************************************************
4153 * Called when the user finishes to drag, either by
4154 * releasing the mouse, or by pressing Esc.
4156 * info: Source info for the drag
4157 * time: Timestamp for ending the drag
4159 *************************************************************/
4162 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
4164 GtkWidget *source_widget = info->widget;
4165 GdkDevice *pointer, *keyboard;
4167 pointer = gdk_drag_context_get_device (info->context);
4168 keyboard = gdk_device_get_associated_device (pointer);
4170 /* Prevent ungrab before grab (see bug 623865) */
4171 if (info->grab_time == GDK_CURRENT_TIME)
4172 time = GDK_CURRENT_TIME;
4174 if (info->update_idle)
4176 g_source_remove (info->update_idle);
4177 info->update_idle = 0;
4180 if (info->last_event)
4182 gdk_event_free (info->last_event);
4183 info->last_event = NULL;
4186 info->have_grab = FALSE;
4188 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4189 gtk_drag_grab_broken_event_cb,
4191 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4192 gtk_drag_grab_notify_cb,
4194 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4195 gtk_drag_button_release_cb,
4197 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4200 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4204 gdk_device_ungrab (pointer, time);
4205 ungrab_dnd_keys (info->ipc_widget, keyboard, time);
4206 gtk_device_grab_remove (info->ipc_widget, pointer);
4208 if (gtk_widget_get_realized (source_widget))
4210 GdkEvent *send_event;
4212 /* Send on a release pair to the original widget to convince it
4213 * to release its grab. We need to call gtk_propagate_event()
4214 * here, instead of gtk_widget_event() because widget like
4215 * GtkList may expect propagation.
4218 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
4219 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
4220 send_event->button.send_event = TRUE;
4221 send_event->button.time = time;
4222 send_event->button.x = 0;
4223 send_event->button.y = 0;
4224 send_event->button.axes = NULL;
4225 send_event->button.state = 0;
4226 send_event->button.button = info->button;
4227 send_event->button.device = pointer;
4228 send_event->button.x_root = 0;
4229 send_event->button.y_root = 0;
4231 gtk_propagate_event (source_widget, send_event);
4232 gdk_event_free (send_event);
4236 /*************************************************************
4238 * Called on cancellation of a drag, either by the user
4239 * or programmatically.
4241 * info: Source info for the drag
4242 * time: Timestamp for ending the drag
4244 *************************************************************/
4247 gtk_drag_cancel (GtkDragSourceInfo *info, GtkDragResult result, guint32 time)
4249 gtk_drag_end (info, time);
4250 gdk_drag_abort (info->context, time);
4251 gtk_drag_drop_finished (info, result, time);
4254 /*************************************************************
4255 * gtk_drag_motion_cb:
4256 * "motion-notify-event" callback during drag.
4260 *************************************************************/
4263 gtk_drag_motion_cb (GtkWidget *widget,
4264 GdkEventMotion *event,
4267 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4269 gint x_root, y_root;
4273 gdk_device_get_position (event->device, &screen, &x_root, &y_root);
4274 event->x_root = x_root;
4275 event->y_root = y_root;
4278 screen = gdk_event_get_screen ((GdkEvent *)event);
4280 x_root = (gint)(event->x_root + 0.5);
4281 y_root = (gint)(event->y_root + 0.5);
4282 gtk_drag_update (info, screen, x_root, y_root, (GdkEvent *) event);
4287 /*************************************************************
4289 * "key-press/release-event" callback during drag.
4293 *************************************************************/
4296 #define SMALL_STEP 1
4299 gtk_drag_key_cb (GtkWidget *widget,
4303 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4304 GdkModifierType state;
4305 GdkWindow *root_window;
4310 state = event->state & gtk_accelerator_get_default_mod_mask ();
4311 pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
4313 if (event->type == GDK_KEY_PRESS)
4315 switch (event->keyval)
4317 case GDK_KEY_Escape:
4318 gtk_drag_cancel (info, GTK_DRAG_RESULT_USER_CANCELLED, event->time);
4322 case GDK_KEY_Return:
4323 case GDK_KEY_ISO_Enter:
4324 case GDK_KEY_KP_Enter:
4325 case GDK_KEY_KP_Space:
4326 if ((gdk_drag_context_get_selected_action (info->context) != 0) &&
4327 (gdk_drag_context_get_dest_window (info->context) != NULL))
4329 gtk_drag_end (info, event->time);
4330 gtk_drag_drop (info, event->time);
4334 gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4341 dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4345 case GDK_KEY_KP_Down:
4346 dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4350 case GDK_KEY_KP_Left:
4351 dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4355 case GDK_KEY_KP_Right:
4356 dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4361 /* Now send a "motion" so that the modifier state is updated */
4363 /* The state is not yet updated in the event, so we need
4364 * to query it here. We could use XGetModifierMapping, but
4365 * that would be overkill.
4367 root_window = gtk_widget_get_root_window (widget);
4368 gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
4369 event->state = state;
4371 if (dx != 0 || dy != 0)
4375 gdk_device_warp (pointer,
4376 gtk_widget_get_screen (widget),
4377 info->cur_x, info->cur_y);
4380 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
4386 gtk_drag_grab_broken_event_cb (GtkWidget *widget,
4387 GdkEventGrabBroken *event,
4390 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4392 /* Don't cancel if we break the implicit grab from the initial button_press.
4393 * Also, don't cancel if we re-grab on the widget or on our IPC window, for
4394 * example, when changing the drag cursor.
4397 || event->grab_window == gtk_widget_get_window (info->widget)
4398 || event->grab_window == gtk_widget_get_window (info->ipc_widget))
4401 gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4406 gtk_drag_grab_notify_cb (GtkWidget *widget,
4407 gboolean was_grabbed,
4410 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4413 pointer = gdk_drag_context_get_device (info->context);
4415 if (gtk_widget_device_is_shadowed (widget, pointer))
4417 /* We have to block callbacks to avoid recursion here, because
4418 gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
4419 g_signal_handlers_block_by_func (widget, gtk_drag_grab_notify_cb, data);
4420 gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4421 g_signal_handlers_unblock_by_func (widget, gtk_drag_grab_notify_cb, data);
4426 /*************************************************************
4427 * gtk_drag_button_release_cb:
4428 * "button-release-event" callback during drag.
4432 *************************************************************/
4435 gtk_drag_button_release_cb (GtkWidget *widget,
4436 GdkEventButton *event,
4439 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4441 if (event->button != info->button)
4444 if ((gdk_drag_context_get_selected_action (info->context) != 0) &&
4445 (gdk_drag_context_get_dest_window (info->context) != NULL))
4447 gtk_drag_end (info, event->time);
4448 gtk_drag_drop (info, event->time);
4452 gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4459 gtk_drag_abort_timeout (gpointer data)
4461 GtkDragSourceInfo *info = data;
4462 guint32 time = GDK_CURRENT_TIME;
4464 if (info->proxy_dest)
4465 time = info->proxy_dest->proxy_drop_time;
4467 info->drop_timeout = 0;
4468 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED, time);
4474 * gtk_drag_check_threshold: (method)
4475 * @widget: a #GtkWidget
4476 * @start_x: X coordinate of start of drag
4477 * @start_y: Y coordinate of start of drag
4478 * @current_x: current X coordinate
4479 * @current_y: current Y coordinate
4481 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
4482 * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
4483 * should trigger the beginning of a drag-and-drop operation.
4485 * Return Value: %TRUE if the drag threshold has been passed.
4488 gtk_drag_check_threshold (GtkWidget *widget,
4494 gint drag_threshold;
4496 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
4498 g_object_get (gtk_widget_get_settings (widget),
4499 "gtk-dnd-drag-threshold", &drag_threshold,
4502 return (ABS (current_x - start_x) > drag_threshold ||
4503 ABS (current_y - start_y) > drag_threshold);