1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
35 #ifdef GDK_WINDOWING_X11
37 #include <X11/keysym.h>
38 #include "gdk/x11/gdkx.h"
42 #include "gtkiconfactory.h"
43 #include "gtkicontheme.h"
44 #include "gtkimageprivate.h"
45 #include "gtkinvisible.h"
49 #include "gtktooltip.h"
50 #include "gtkwindow.h"
52 #include "gtkdndcursors.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 */
98 GtkImageType icon_type;
101 GtkImagePixbufData pixbuf;
102 GtkImageStockData stock;
103 GtkImageIconNameData name;
104 GtkImageGIconData gicon;
107 /* Stored button press information to detect drag beginning */
112 struct _GtkDragSourceInfo
115 GtkTargetList *target_list; /* Targets for drag data */
116 GdkDragAction possible_actions; /* Actions allowed by source */
117 GdkDragContext *context; /* drag context */
118 GtkWidget *icon_window; /* Window for drag */
119 GtkWidget *fallback_icon; /* Window for drag used on other screens */
120 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
121 GdkCursor *cursor; /* Cursor for drag */
122 gint hot_x, hot_y; /* Hot spot for drag */
123 gint button; /* mouse button starting drag */
125 GtkDragStatus status; /* drag status */
126 GdkEvent *last_event; /* pending event */
128 gint start_x, start_y; /* Initial position */
129 gint cur_x, cur_y; /* Current Position */
130 GdkScreen *cur_screen; /* Current screen for pointer */
132 guint32 grab_time; /* timestamp for initial grab */
133 GList *selections; /* selections we've claimed */
135 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
137 guint update_idle; /* Idle function to update the drag */
138 guint drop_timeout; /* Timeout for aborting drop */
139 guint destroy_icon : 1; /* If true, destroy icon_window */
140 guint have_grab : 1; /* Do we still have the pointer grab */
141 GdkPixbuf *icon_pixbuf;
142 GdkCursor *drag_cursors[6];
145 struct _GtkDragDestSite
147 GtkDestDefaults flags;
148 GtkTargetList *target_list;
149 GdkDragAction actions;
150 GdkWindow *proxy_window;
151 GdkDragProtocol proxy_protocol;
153 guint proxy_coords : 1;
155 guint track_motion : 1;
158 struct _GtkDragDestInfo
160 GtkWidget *widget; /* Widget in which drag is in */
161 GdkDragContext *context; /* Drag context */
162 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
163 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
164 guint32 proxy_drop_time; /* Timestamp for proxied drop */
165 guint proxy_drop_wait : 1; /* Set if we are waiting for a
166 * status reply before sending
169 guint dropped : 1; /* Set after we receive a drop */
170 gint drop_x, drop_y; /* Position of drop */
173 #define DROP_ABORT_TIME 300000
175 #define ANIM_STEP_TIME 50
176 #define ANIM_STEP_LENGTH 50
177 #define ANIM_MIN_STEPS 5
178 #define ANIM_MAX_STEPS 10
182 GtkDragSourceInfo *info;
187 typedef gboolean (* GtkDragDestCallback) (GtkWidget *widget,
188 GdkDragContext *context,
193 /* Enumeration for some targets we handle internally */
196 TARGET_MOTIF_SUCCESS = 0x40000000,
197 TARGET_MOTIF_FAILURE,
201 /* Forward declarations */
202 static void gtk_drag_get_event_actions (GdkEvent *event,
204 GdkDragAction actions,
205 GdkDragAction *suggested_action,
206 GdkDragAction *possible_actions);
207 static GdkCursor * gtk_drag_get_cursor (GdkDisplay *display,
208 GdkDragAction action,
209 GtkDragSourceInfo *info);
210 static void gtk_drag_update_cursor (GtkDragSourceInfo *info);
211 static GtkWidget *gtk_drag_get_ipc_widget (GtkWidget *widget);
212 static GtkWidget *gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen);
213 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
215 static void gtk_drag_selection_received (GtkWidget *widget,
216 GtkSelectionData *selection_data,
219 static gboolean gtk_drag_find_widget (GtkWidget *widget,
220 GdkDragContext *context,
221 GtkDragDestInfo *info,
225 GtkDragDestCallback callback);
226 static void gtk_drag_proxy_begin (GtkWidget *widget,
227 GtkDragDestInfo *dest_info,
229 static void gtk_drag_dest_realized (GtkWidget *widget);
230 static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
231 GtkWidget *previous_toplevel);
232 static void gtk_drag_dest_site_destroy (gpointer data);
233 static void gtk_drag_dest_leave (GtkWidget *widget,
234 GdkDragContext *context,
236 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
237 GdkDragContext *context,
241 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
242 GdkDragContext *context,
247 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
249 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
251 static void gtk_drag_clear_source_info (GdkDragContext *context);
253 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
256 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
258 static void gtk_drag_drop (GtkDragSourceInfo *info,
260 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
261 GtkDragResult result,
263 static void gtk_drag_cancel (GtkDragSourceInfo *info,
264 GtkDragResult result,
267 static gboolean gtk_drag_source_event_cb (GtkWidget *widget,
270 static void gtk_drag_source_site_destroy (gpointer data);
271 static void gtk_drag_selection_get (GtkWidget *widget,
272 GtkSelectionData *selection_data,
276 static gboolean gtk_drag_anim_timeout (gpointer data);
277 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
278 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
279 static void gtk_drag_add_update_idle (GtkDragSourceInfo *info);
281 static void gtk_drag_update (GtkDragSourceInfo *info,
286 static gboolean gtk_drag_motion_cb (GtkWidget *widget,
287 GdkEventMotion *event,
289 static gboolean gtk_drag_key_cb (GtkWidget *widget,
292 static gboolean gtk_drag_grab_broken_event_cb (GtkWidget *widget,
293 GdkEventGrabBroken *event,
295 static void gtk_drag_grab_notify_cb (GtkWidget *widget,
296 gboolean was_grabbed,
298 static gboolean gtk_drag_button_release_cb (GtkWidget *widget,
299 GdkEventButton *event,
301 static gboolean gtk_drag_abort_timeout (gpointer data);
303 static void set_icon_stock_pixbuf (GdkDragContext *context,
304 const gchar *stock_id,
308 gboolean force_window);
310 /************************
311 * Cursor and Icon data *
312 ************************/
315 GdkDragAction action;
321 { GDK_ACTION_DEFAULT, NULL },
322 { GDK_ACTION_ASK, "dnd-ask", dnd_cursor_ask, NULL, NULL },
323 { GDK_ACTION_COPY, "dnd-copy", dnd_cursor_copy, NULL, NULL },
324 { GDK_ACTION_MOVE, "dnd-move", dnd_cursor_move, NULL, NULL },
325 { GDK_ACTION_LINK, "dnd-link", dnd_cursor_link, NULL, NULL },
326 { 0 , "dnd-none", dnd_cursor_none, NULL, NULL },
329 /*********************
330 * Utility functions *
331 *********************/
334 set_can_change_screen (GtkWidget *widget,
335 gboolean can_change_screen)
337 can_change_screen = can_change_screen != FALSE;
339 g_object_set_data (G_OBJECT (widget), I_("gtk-dnd-can-change-screen"),
340 GUINT_TO_POINTER (can_change_screen));
344 get_can_change_screen (GtkWidget *widget)
346 return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
351 gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen)
354 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
355 "gtk-dnd-ipc-widgets");
359 GSList *tmp = drag_widgets;
360 result = drag_widgets->data;
361 drag_widgets = drag_widgets->next;
362 g_object_set_data (G_OBJECT (screen),
363 I_("gtk-dnd-ipc-widgets"),
365 g_slist_free_1 (tmp);
369 result = gtk_window_new (GTK_WINDOW_POPUP);
370 gtk_window_set_screen (GTK_WINDOW (result), screen);
371 gtk_window_resize (GTK_WINDOW (result), 1, 1);
372 gtk_window_move (GTK_WINDOW (result), -100, -100);
373 gtk_widget_show (result);
380 gtk_drag_get_ipc_widget (GtkWidget *widget)
385 result = gtk_drag_get_ipc_widget_for_screen (gtk_widget_get_screen (widget));
387 toplevel = gtk_widget_get_toplevel (widget);
389 if (GTK_IS_WINDOW (toplevel))
391 if (gtk_window_has_group (GTK_WINDOW (toplevel)))
392 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
393 GTK_WINDOW (result));
399 /* FIXME: modifying the XEvent window as in root_key_filter() isn't
400 * going to work with XGE/XI2, since the actual event to handle would
401 * be allocated/freed before GDK gets to translate the event.
402 * Active grabs on the keyboard are used instead at the moment...
404 #if defined (GDK_WINDOWING_X11) && !defined (XINPUT_2)
407 * We want to handle a handful of keys during DND, e.g. Escape to abort.
408 * Grabbing the keyboard has the unfortunate side-effect of preventing
409 * useful things such as using Alt-Tab to cycle between windows or
410 * switching workspaces. Therefore, we just grab the few keys we are
411 * interested in. Note that we need to put the grabs on the root window
412 * in order for them to still work when the focus is moved to another
415 * GDK needs a little help to successfully deliver root key events...
418 static GdkFilterReturn
419 root_key_filter (GdkXEvent *xevent,
423 XEvent *ev = (XEvent *) xevent;
425 if ((ev->type == KeyPress || ev->type == KeyRelease) &&
426 ev->xkey.root == ev->xkey.window)
427 ev->xkey.window = (Window)data;
429 return GDK_FILTER_CONTINUE;
437 static GrabKey grab_keys[] = {
446 { XK_Down, Mod1Mask },
448 { XK_Left, Mod1Mask },
450 { XK_Right, Mod1Mask },
452 { XK_KP_Up, Mod1Mask },
454 { XK_KP_Down, Mod1Mask },
456 { XK_KP_Left, Mod1Mask },
458 { XK_KP_Right, Mod1Mask }
462 grab_dnd_keys (GtkWidget *widget,
467 GdkWindow *window, *root;
470 window = gtk_widget_get_window (widget);
471 root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
473 gdk_error_trap_push ();
475 for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
477 keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
478 XGrabKey (GDK_WINDOW_XDISPLAY (window),
479 keycode, grab_keys[i].modifiers,
480 GDK_WINDOW_XID (root),
487 gdk_error_trap_pop_ignored ();
489 gdk_window_add_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
493 ungrab_dnd_keys (GtkWidget *widget,
498 GdkWindow *window, *root;
501 window = gtk_widget_get_window (widget);
502 root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
504 gdk_window_remove_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
506 gdk_error_trap_push ();
508 for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
510 keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
511 XUngrabKey (GDK_WINDOW_XDISPLAY (window),
512 keycode, grab_keys[i].modifiers,
513 GDK_WINDOW_XID (root));
517 gdk_error_trap_pop_ignored ();
520 #else /* GDK_WINDOWING_X11 && !XINPUT_2 */
523 grab_dnd_keys (GtkWidget *widget,
527 gdk_device_grab (device,
528 gtk_widget_get_window (widget),
529 GDK_OWNERSHIP_APPLICATION, FALSE,
530 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
535 ungrab_dnd_keys (GtkWidget *widget,
539 gdk_device_ungrab (device, time);
542 #endif /* GDK_WINDOWING_X11 */
545 /***************************************************************
546 * gtk_drag_release_ipc_widget:
547 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
549 * widget: the widget to release.
551 ***************************************************************/
554 gtk_drag_release_ipc_widget (GtkWidget *widget)
556 GtkWindow *window = GTK_WINDOW (widget);
557 GdkScreen *screen = gtk_widget_get_screen (widget);
558 GdkDragContext *context = g_object_get_data (G_OBJECT (widget), "drag-context");
559 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
560 "gtk-dnd-ipc-widgets");
561 GdkDevice *pointer, *keyboard;
565 pointer = gdk_drag_context_get_device (context);
566 keyboard = gdk_device_get_associated_device (pointer);
569 ungrab_dnd_keys (widget, keyboard, GDK_CURRENT_TIME);
572 if (gtk_window_has_group (window))
573 gtk_window_group_remove_window (gtk_window_get_group (window),
575 drag_widgets = g_slist_prepend (drag_widgets, widget);
576 g_object_set_data (G_OBJECT (screen),
577 I_("gtk-dnd-ipc-widgets"),
582 gtk_drag_get_event_time (GdkEvent *event)
584 guint32 tm = GDK_CURRENT_TIME;
589 case GDK_MOTION_NOTIFY:
590 tm = event->motion.time; break;
591 case GDK_BUTTON_PRESS:
592 case GDK_2BUTTON_PRESS:
593 case GDK_3BUTTON_PRESS:
594 case GDK_BUTTON_RELEASE:
595 tm = event->button.time; break;
597 case GDK_KEY_RELEASE:
598 tm = event->key.time; break;
599 case GDK_ENTER_NOTIFY:
600 case GDK_LEAVE_NOTIFY:
601 tm = event->crossing.time; break;
602 case GDK_PROPERTY_NOTIFY:
603 tm = event->property.time; break;
604 case GDK_SELECTION_CLEAR:
605 case GDK_SELECTION_REQUEST:
606 case GDK_SELECTION_NOTIFY:
607 tm = event->selection.time; break;
608 case GDK_PROXIMITY_IN:
609 case GDK_PROXIMITY_OUT:
610 tm = event->proximity.time; break;
611 default: /* use current time */
619 gtk_drag_get_event_actions (GdkEvent *event,
621 GdkDragAction actions,
622 GdkDragAction *suggested_action,
623 GdkDragAction *possible_actions)
625 *suggested_action = 0;
626 *possible_actions = 0;
630 GdkModifierType state = 0;
634 case GDK_MOTION_NOTIFY:
635 state = event->motion.state;
637 case GDK_BUTTON_PRESS:
638 case GDK_2BUTTON_PRESS:
639 case GDK_3BUTTON_PRESS:
640 case GDK_BUTTON_RELEASE:
641 state = event->button.state;
644 case GDK_KEY_RELEASE:
645 state = event->key.state;
647 case GDK_ENTER_NOTIFY:
648 case GDK_LEAVE_NOTIFY:
649 state = event->crossing.state;
655 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
657 *suggested_action = GDK_ACTION_ASK;
658 *possible_actions = actions;
660 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
662 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
664 if (actions & GDK_ACTION_LINK)
666 *suggested_action = GDK_ACTION_LINK;
667 *possible_actions = GDK_ACTION_LINK;
670 else if (state & GDK_CONTROL_MASK)
672 if (actions & GDK_ACTION_COPY)
674 *suggested_action = GDK_ACTION_COPY;
675 *possible_actions = GDK_ACTION_COPY;
681 if (actions & GDK_ACTION_MOVE)
683 *suggested_action = GDK_ACTION_MOVE;
684 *possible_actions = GDK_ACTION_MOVE;
691 *possible_actions = actions;
693 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
694 *suggested_action = GDK_ACTION_ASK;
695 else if (actions & GDK_ACTION_COPY)
696 *suggested_action = GDK_ACTION_COPY;
697 else if (actions & GDK_ACTION_MOVE)
698 *suggested_action = GDK_ACTION_MOVE;
699 else if (actions & GDK_ACTION_LINK)
700 *suggested_action = GDK_ACTION_LINK;
705 *possible_actions = actions;
707 if (actions & GDK_ACTION_COPY)
708 *suggested_action = GDK_ACTION_COPY;
709 else if (actions & GDK_ACTION_MOVE)
710 *suggested_action = GDK_ACTION_MOVE;
711 else if (actions & GDK_ACTION_LINK)
712 *suggested_action = GDK_ACTION_LINK;
717 gtk_drag_can_use_rgba_cursor (GdkDisplay *display,
721 guint max_width, max_height;
723 if (!gdk_display_supports_cursor_color (display))
726 if (!gdk_display_supports_cursor_alpha (display))
729 gdk_display_get_maximal_cursor_size (display,
732 if (width > max_width || height > max_height)
734 /* can't use rgba cursor (too large) */
742 gtk_drag_get_cursor (GdkDisplay *display,
743 GdkDragAction action,
744 GtkDragSourceInfo *info)
748 /* reconstruct the cursors for each new drag (thus !info),
749 * to catch cursor theme changes
753 for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
754 if (drag_cursors[i].cursor != NULL)
756 g_object_unref (drag_cursors[i].cursor);
757 drag_cursors[i].cursor = NULL;
761 for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
762 if (drag_cursors[i].action == action)
765 if (drag_cursors[i].pixbuf == NULL)
766 drag_cursors[i].pixbuf =
767 gdk_pixbuf_new_from_inline (-1, drag_cursors[i].data, FALSE, NULL);
769 if (drag_cursors[i].cursor != NULL)
771 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
773 g_object_unref (drag_cursors[i].cursor);
774 drag_cursors[i].cursor = NULL;
778 if (drag_cursors[i].cursor == NULL)
779 drag_cursors[i].cursor = gdk_cursor_new_from_name (display, drag_cursors[i].name);
781 if (drag_cursors[i].cursor == NULL)
782 drag_cursors[i].cursor = gdk_cursor_new_from_pixbuf (display, drag_cursors[i].pixbuf, 0, 0);
784 if (info && info->icon_pixbuf)
786 gint cursor_width, cursor_height;
787 gint icon_width, icon_height;
789 GdkPixbuf *cursor_pixbuf, *pixbuf;
791 gint icon_x, icon_y, ref_x, ref_y;
793 if (info->drag_cursors[i] != NULL)
795 if (display == gdk_cursor_get_display (info->drag_cursors[i]))
796 return info->drag_cursors[i];
798 g_object_unref (info->drag_cursors[i]);
799 info->drag_cursors[i] = NULL;
802 icon_x = info->hot_x;
803 icon_y = info->hot_y;
804 icon_width = gdk_pixbuf_get_width (info->icon_pixbuf);
805 icon_height = gdk_pixbuf_get_height (info->icon_pixbuf);
808 cursor_pixbuf = gdk_cursor_get_image (drag_cursors[i].cursor);
810 cursor_pixbuf = g_object_ref (drag_cursors[i].pixbuf);
813 if (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"))
814 hot_x = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"));
816 if (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"))
817 hot_y = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"));
820 /* The code below is an attempt to let cursor themes
821 * determine the attachment of the icon to enable things
822 * like the following:
830 * It does not work since Xcursor doesn't allow to attach
831 * any additional information to cursors in a retrievable
832 * way (there are comments, but no way to get at them
833 * short of searching for the actual cursor file).
834 * If this code ever gets used, the icon_window placement
835 * must be changed to recognize these placement options
836 * as well. Note that this code ignores info->hot_x/y.
838 for (j = 0; j < 10; j++)
843 GtkAnchorType icon_anchor;
845 g_snprintf (key, 32, "comment%d", j);
846 opt = gdk_pixbuf_get_option (cursor_pixbuf, key);
847 if (opt && g_str_has_prefix ("icon-attach:", opt))
849 toks = g_strsplit (opt + strlen ("icon-attach:"), "'", -1);
850 if (g_strv_length (toks) != 3)
855 icon_anchor = atoi (toks[0]);
856 icon_x = atoi (toks[1]);
857 icon_y = atoi (toks[2]);
861 case GTK_ANCHOR_NORTH:
862 case GTK_ANCHOR_CENTER:
863 case GTK_ANCHOR_SOUTH:
864 icon_x += icon_width / 2;
866 case GTK_ANCHOR_NORTH_EAST:
867 case GTK_ANCHOR_EAST:
868 case GTK_ANCHOR_SOUTH_EAST:
869 icon_x += icon_width;
876 case GTK_ANCHOR_WEST:
877 case GTK_ANCHOR_CENTER:
878 case GTK_ANCHOR_EAST:
879 icon_y += icon_height / 2;
881 case GTK_ANCHOR_SOUTH_WEST:
882 case GTK_ANCHOR_SOUTH:
883 case GTK_ANCHOR_SOUTH_EAST:
884 icon_x += icon_height;
896 cursor_width = gdk_pixbuf_get_width (cursor_pixbuf);
897 cursor_height = gdk_pixbuf_get_height (cursor_pixbuf);
899 ref_x = MAX (hot_x, icon_x);
900 ref_y = MAX (hot_y, icon_y);
901 width = ref_x + MAX (cursor_width - hot_x, icon_width - icon_x);
902 height = ref_y + MAX (cursor_height - hot_y, icon_height - icon_y);
904 if (gtk_drag_can_use_rgba_cursor (display, width, height))
906 /* Composite cursor and icon so that both hotspots
907 * end up at (ref_x, ref_y)
909 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
912 gdk_pixbuf_fill (pixbuf, 0xff000000);
914 gdk_pixbuf_composite (info->icon_pixbuf, pixbuf,
915 ref_x - icon_x, ref_y - icon_y,
916 icon_width, icon_height,
917 ref_x - icon_x, ref_y - icon_y,
919 GDK_INTERP_BILINEAR, 255);
921 gdk_pixbuf_composite (cursor_pixbuf, pixbuf,
922 ref_x - hot_x, ref_y - hot_y,
923 cursor_width, cursor_height,
924 ref_x - hot_x, ref_y - hot_y,
926 GDK_INTERP_BILINEAR, 255);
928 info->drag_cursors[i] =
929 gdk_cursor_new_from_pixbuf (display, pixbuf, ref_x, ref_y);
931 g_object_unref (pixbuf);
934 g_object_unref (cursor_pixbuf);
936 if (info->drag_cursors[i] != NULL)
937 return info->drag_cursors[i];
940 return drag_cursors[i].cursor;
944 gtk_drag_update_cursor (GtkDragSourceInfo *info)
949 if (!info->have_grab)
952 for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
953 if (info->cursor == drag_cursors[i].cursor ||
954 info->cursor == info->drag_cursors[i])
957 if (i == G_N_ELEMENTS (drag_cursors))
960 cursor = gtk_drag_get_cursor (gdk_cursor_get_display (info->cursor),
961 drag_cursors[i].action, info);
963 if (cursor != info->cursor)
967 pointer = gdk_drag_context_get_device (info->context);
968 gdk_device_grab (pointer,
969 gtk_widget_get_window (info->ipc_widget),
970 GDK_OWNERSHIP_APPLICATION, FALSE,
971 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
972 cursor, info->grab_time);
973 info->cursor = cursor;
977 /********************
979 ********************/
983 * @widget: the widget that will receive the
984 * #GtkWidget::drag-data-received signal.
985 * @context: the drag context
986 * @target: the target (form of the data) to retrieve.
987 * @time_: a timestamp for retrieving the data. This will
988 * generally be the time received in a #GtkWidget::drag-motion"
989 * or #GtkWidget::drag-drop" signal.
991 * Gets the data associated with a drag. When the data
992 * is received or the retrieval fails, GTK+ will emit a
993 * #GtkWidget::drag-data-received signal. Failure of the retrieval
994 * is indicated by the length field of the @selection_data
995 * signal parameter being negative. However, when gtk_drag_get_data()
996 * is called implicitely because the %GTK_DEST_DEFAULT_DROP was set,
997 * then the widget will not receive notification of failed
1001 gtk_drag_get_data (GtkWidget *widget,
1002 GdkDragContext *context,
1006 GtkWidget *selection_widget;
1008 g_return_if_fail (GTK_IS_WIDGET (widget));
1009 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1011 selection_widget = gtk_drag_get_ipc_widget (widget);
1013 g_object_ref (context);
1014 g_object_ref (widget);
1016 g_signal_connect (selection_widget, "selection-received",
1017 G_CALLBACK (gtk_drag_selection_received), widget);
1019 g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1021 gtk_selection_convert (selection_widget,
1022 gdk_drag_get_selection (context),
1029 * gtk_drag_get_source_widget:
1030 * @context: a (destination side) drag context
1032 * Determines the source widget for a drag.
1034 * Return value: (transfer none): if the drag is occurring
1035 * within a single application, a pointer to the source widget.
1039 gtk_drag_get_source_widget (GdkDragContext *context)
1043 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
1045 tmp_list = source_widgets;
1048 GtkWidget *ipc_widget = tmp_list->data;
1050 if (gtk_widget_get_window (ipc_widget) == gdk_drag_context_get_source_window (context))
1052 GtkDragSourceInfo *info;
1053 info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
1055 return info ? info->widget : NULL;
1058 tmp_list = tmp_list->next;
1066 * @context: the drag context.
1067 * @success: a flag indicating whether the drop was successful
1068 * @del: a flag indicating whether the source should delete the
1069 * original data. (This should be %TRUE for a move)
1070 * @time_: the timestamp from the #GtkWidget::drag-drop signal.
1072 * Informs the drag source that the drop is finished, and
1073 * that the data of the drag will no longer be required.
1076 gtk_drag_finish (GdkDragContext *context,
1081 GdkAtom target = GDK_NONE;
1083 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1087 target = gdk_atom_intern_static_string ("DELETE");
1089 else if (gdk_drag_context_get_protocol (context) == GDK_DRAG_PROTO_MOTIF)
1091 target = gdk_atom_intern_static_string (success ?
1092 "XmTRANSFER_SUCCESS" :
1093 "XmTRANSFER_FAILURE");
1096 if (target != GDK_NONE)
1098 GtkWidget *selection_widget = gtk_drag_get_ipc_widget_for_screen (gdk_window_get_screen (gdk_drag_context_get_source_window (context)));
1100 g_object_ref (context);
1102 g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1103 g_signal_connect (selection_widget, "selection-received",
1104 G_CALLBACK (gtk_drag_selection_received),
1107 gtk_selection_convert (selection_widget,
1108 gdk_drag_get_selection (context),
1113 if (!(success && del))
1114 gdk_drop_finish (context, success, time);
1117 /*************************************************************
1118 * gtk_drag_highlight_draw:
1119 * Callback for expose_event for highlighted widgets.
1125 *************************************************************/
1128 gtk_drag_highlight_draw (GtkWidget *widget,
1132 int width = gtk_widget_get_allocated_width (widget);
1133 int height = gtk_widget_get_allocated_height (widget);
1134 GtkStyleContext *context;
1136 context = gtk_widget_get_style_context (widget);
1138 gtk_style_context_save (context);
1139 gtk_style_context_add_class (context, GTK_STYLE_CLASS_DND);
1141 gtk_render_frame (context, cr, 0, 0, width, height);
1143 gtk_style_context_restore (context);
1145 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
1146 cairo_set_line_width (cr, 1.0);
1147 cairo_rectangle (cr,
1149 width - 1, height - 1);
1156 * gtk_drag_highlight:
1157 * @widget: a widget to highlight
1159 * Draws a highlight around a widget. This will attach
1160 * handlers to #GtkWidget::draw, so the highlight
1161 * will continue to be displayed until gtk_drag_unhighlight()
1165 gtk_drag_highlight (GtkWidget *widget)
1167 g_return_if_fail (GTK_IS_WIDGET (widget));
1169 g_signal_connect_after (widget, "draw",
1170 G_CALLBACK (gtk_drag_highlight_draw),
1173 gtk_widget_queue_draw (widget);
1177 * gtk_drag_unhighlight:
1178 * @widget: a widget to remove the highlight from.
1180 * Removes a highlight set by gtk_drag_highlight() from
1184 gtk_drag_unhighlight (GtkWidget *widget)
1186 g_return_if_fail (GTK_IS_WIDGET (widget));
1188 g_signal_handlers_disconnect_by_func (widget,
1189 gtk_drag_highlight_draw,
1192 gtk_widget_queue_draw (widget);
1196 gtk_drag_dest_set_internal (GtkWidget *widget,
1197 GtkDragDestSite *site)
1199 GtkDragDestSite *old_site;
1201 g_return_if_fail (widget != NULL);
1203 /* HACK, do this in the destroy */
1204 old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1207 g_signal_handlers_disconnect_by_func (widget,
1208 gtk_drag_dest_realized,
1210 g_signal_handlers_disconnect_by_func (widget,
1211 gtk_drag_dest_hierarchy_changed,
1214 site->track_motion = old_site->track_motion;
1217 if (gtk_widget_get_realized (widget))
1218 gtk_drag_dest_realized (widget);
1220 g_signal_connect (widget, "realize",
1221 G_CALLBACK (gtk_drag_dest_realized), site);
1222 g_signal_connect (widget, "hierarchy-changed",
1223 G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
1225 g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
1226 site, gtk_drag_dest_site_destroy);
1230 * gtk_drag_dest_set: (method)
1231 * @widget: a #GtkWidget
1232 * @flags: which types of default drag behavior to use
1233 * @targets: (allow-none) (array length=n_targets): a pointer to an array of #GtkTargetEntry<!-- -->s
1234 * indicating the drop types that this @widget will accept, or %NULL.
1235 * Later you can access the list with gtk_drag_dest_get_target_list()
1236 * and gtk_drag_dest_find_target().
1237 * @n_targets: the number of entries in @targets
1238 * @actions: a bitmask of possible actions for a drop onto this @widget.
1240 * Sets a widget as a potential drop destination, and adds default behaviors.
1242 * The default behaviors listed in @flags have an effect similar
1243 * to installing default handlers for the widget's drag-and-drop signals
1244 * (#GtkWidget::drag-motion, #GtkWidget::drag-drop, ...). They all exist
1245 * for convenience. When passing #GTK_DEST_DEFAULT_ALL for instance it is
1246 * sufficient to connect to the widget's #GtkWidget::drag-data-received
1247 * signal to get primitive, but consistent drag-and-drop support.
1249 * Things become more complicated when you try to preview the dragged data,
1250 * as described in the documentation for #GtkWidget::drag-motion. The default
1251 * behaviors described by @flags make some assumptions, that can conflict
1252 * with your own signal handlers. For instance #GTK_DEST_DEFAULT_DROP causes
1253 * invokations of gdk_drag_status() in the context of #GtkWidget::drag-motion,
1254 * and invokations of gtk_drag_finish() in #GtkWidget::drag-data-received.
1255 * Especially the later is dramatic, when your own #GtkWidget::drag-motion
1256 * handler calls gtk_drag_get_data() to inspect the dragged data.
1258 * There's no way to set a default action here, you can use the
1259 * #GtkWidget::drag-motion callback for that. Here's an example which selects
1260 * the action to use depending on whether the control key is pressed or not:
1263 * drag_motion (GtkWidget *widget,
1264 * GdkDragContext *context,
1269 * GdkModifierType mask;
1271 * gdk_window_get_pointer (gtk_widget_get_window (widget),
1272 * NULL, NULL, &mask);
1273 * if (mask & GDK_CONTROL_MASK)
1274 * gdk_drag_status (context, GDK_ACTION_COPY, time);
1276 * gdk_drag_status (context, GDK_ACTION_MOVE, time);
1281 gtk_drag_dest_set (GtkWidget *widget,
1282 GtkDestDefaults flags,
1283 const GtkTargetEntry *targets,
1285 GdkDragAction actions)
1287 GtkDragDestSite *site;
1289 g_return_if_fail (GTK_IS_WIDGET (widget));
1291 site = g_slice_new0 (GtkDragDestSite);
1293 site->flags = flags;
1294 site->have_drag = FALSE;
1296 site->target_list = gtk_target_list_new (targets, n_targets);
1298 site->target_list = NULL;
1299 site->actions = actions;
1300 site->do_proxy = FALSE;
1301 site->proxy_window = NULL;
1302 site->track_motion = FALSE;
1304 gtk_drag_dest_set_internal (widget, site);
1308 * gtk_drag_dest_set_proxy:
1309 * @widget: a #GtkWidget
1310 * @proxy_window: the window to which to forward drag events
1311 * @protocol: the drag protocol which the @proxy_window accepts
1312 * (You can use gdk_drag_get_protocol() to determine this)
1313 * @use_coordinates: If %TRUE, send the same coordinates to the
1314 * destination, because it is an embedded
1317 * Sets this widget as a proxy for drops to another window.
1320 gtk_drag_dest_set_proxy (GtkWidget *widget,
1321 GdkWindow *proxy_window,
1322 GdkDragProtocol protocol,
1323 gboolean use_coordinates)
1325 GtkDragDestSite *site;
1327 g_return_if_fail (GTK_IS_WIDGET (widget));
1328 g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
1330 site = g_slice_new (GtkDragDestSite);
1333 site->have_drag = FALSE;
1334 site->target_list = NULL;
1336 site->proxy_window = proxy_window;
1338 g_object_ref (proxy_window);
1339 site->do_proxy = TRUE;
1340 site->proxy_protocol = protocol;
1341 site->proxy_coords = use_coordinates;
1342 site->track_motion = FALSE;
1344 gtk_drag_dest_set_internal (widget, site);
1348 * gtk_drag_dest_unset:
1349 * @widget: a #GtkWidget
1351 * Clears information about a drop destination set with
1352 * gtk_drag_dest_set(). The widget will no longer receive
1353 * notification of drags.
1356 gtk_drag_dest_unset (GtkWidget *widget)
1358 GtkDragDestSite *old_site;
1360 g_return_if_fail (GTK_IS_WIDGET (widget));
1362 old_site = g_object_get_data (G_OBJECT (widget),
1366 g_signal_handlers_disconnect_by_func (widget,
1367 gtk_drag_dest_realized,
1369 g_signal_handlers_disconnect_by_func (widget,
1370 gtk_drag_dest_hierarchy_changed,
1374 g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
1378 * gtk_drag_dest_get_target_list: (method)
1379 * @widget: a #GtkWidget
1381 * Returns the list of targets this widget can accept from
1384 * Return value: (transfer none): the #GtkTargetList, or %NULL if none
1387 gtk_drag_dest_get_target_list (GtkWidget *widget)
1389 GtkDragDestSite *site;
1391 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1393 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1395 return site ? site->target_list : NULL;
1399 * gtk_drag_dest_set_target_list: (method)
1400 * @widget: a #GtkWidget that's a drag destination
1401 * @target_list: (allow-none): list of droppable targets, or %NULL for none
1403 * Sets the target types that this widget can accept from drag-and-drop.
1404 * The widget must first be made into a drag destination with
1405 * gtk_drag_dest_set().
1408 gtk_drag_dest_set_target_list (GtkWidget *widget,
1409 GtkTargetList *target_list)
1411 GtkDragDestSite *site;
1413 g_return_if_fail (GTK_IS_WIDGET (widget));
1415 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1419 g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1420 "to make the widget into a drag destination");
1425 gtk_target_list_ref (target_list);
1427 if (site->target_list)
1428 gtk_target_list_unref (site->target_list);
1430 site->target_list = target_list;
1434 * gtk_drag_dest_add_text_targets: (method)
1435 * @widget: a #GtkWidget that's a drag destination
1437 * Add the text targets supported by #GtkSelection to
1438 * the target list of the drag destination. The targets
1439 * are added with @info = 0. If you need another value,
1440 * use gtk_target_list_add_text_targets() and
1441 * gtk_drag_dest_set_target_list().
1446 gtk_drag_dest_add_text_targets (GtkWidget *widget)
1448 GtkTargetList *target_list;
1450 target_list = gtk_drag_dest_get_target_list (widget);
1452 gtk_target_list_ref (target_list);
1454 target_list = gtk_target_list_new (NULL, 0);
1455 gtk_target_list_add_text_targets (target_list, 0);
1456 gtk_drag_dest_set_target_list (widget, target_list);
1457 gtk_target_list_unref (target_list);
1461 * gtk_drag_dest_add_image_targets: (method)
1462 * @widget: a #GtkWidget that's a drag destination
1464 * Add the image targets supported by #GtkSelection to
1465 * the target list of the drag destination. The targets
1466 * are added with @info = 0. If you need another value,
1467 * use gtk_target_list_add_image_targets() and
1468 * gtk_drag_dest_set_target_list().
1473 gtk_drag_dest_add_image_targets (GtkWidget *widget)
1475 GtkTargetList *target_list;
1477 target_list = gtk_drag_dest_get_target_list (widget);
1479 gtk_target_list_ref (target_list);
1481 target_list = gtk_target_list_new (NULL, 0);
1482 gtk_target_list_add_image_targets (target_list, 0, FALSE);
1483 gtk_drag_dest_set_target_list (widget, target_list);
1484 gtk_target_list_unref (target_list);
1488 * gtk_drag_dest_add_uri_targets: (method)
1489 * @widget: a #GtkWidget that's a drag destination
1491 * Add the URI targets supported by #GtkSelection to
1492 * the target list of the drag destination. The targets
1493 * are added with @info = 0. If you need another value,
1494 * use gtk_target_list_add_uri_targets() and
1495 * gtk_drag_dest_set_target_list().
1500 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
1502 GtkTargetList *target_list;
1504 target_list = gtk_drag_dest_get_target_list (widget);
1506 gtk_target_list_ref (target_list);
1508 target_list = gtk_target_list_new (NULL, 0);
1509 gtk_target_list_add_uri_targets (target_list, 0);
1510 gtk_drag_dest_set_target_list (widget, target_list);
1511 gtk_target_list_unref (target_list);
1515 * gtk_drag_dest_set_track_motion: (method)
1516 * @widget: a #GtkWidget that's a drag destination
1517 * @track_motion: whether to accept all targets
1519 * Tells the widget to emit #GtkWidget::drag-motion and
1520 * #GtkWidget::drag-leave events regardless of the targets and the
1521 * %GTK_DEST_DEFAULT_MOTION flag.
1523 * This may be used when a widget wants to do generic
1524 * actions regardless of the targets that the source offers.
1529 gtk_drag_dest_set_track_motion (GtkWidget *widget,
1530 gboolean track_motion)
1532 GtkDragDestSite *site;
1534 g_return_if_fail (GTK_IS_WIDGET (widget));
1536 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1538 g_return_if_fail (site != NULL);
1540 site->track_motion = track_motion != FALSE;
1544 * gtk_drag_dest_get_track_motion: (method)
1545 * @widget: a #GtkWidget that's a drag destination
1547 * Returns whether the widget has been configured to always
1548 * emit #GtkWidget::drag-motion signals.
1550 * Return Value: %TRUE if the widget always emits
1551 * #GtkWidget::drag-motion events
1556 gtk_drag_dest_get_track_motion (GtkWidget *widget)
1558 GtkDragDestSite *site;
1560 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1562 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1565 return site->track_motion;
1570 /*************************************************************
1571 * _gtk_drag_dest_handle_event:
1572 * Called from widget event handling code on Drag events
1576 * toplevel: Toplevel widget that received the event
1579 *************************************************************/
1582 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1585 GtkDragDestInfo *info;
1586 GdkDragContext *context;
1588 g_return_if_fail (toplevel != NULL);
1589 g_return_if_fail (event != NULL);
1591 context = event->dnd.context;
1593 info = gtk_drag_get_dest_info (context, TRUE);
1595 /* Find the widget for the event */
1596 switch (event->type)
1598 case GDK_DRAG_ENTER:
1601 case GDK_DRAG_LEAVE:
1604 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1605 info->widget = NULL;
1609 case GDK_DRAG_MOTION:
1610 case GDK_DROP_START:
1616 if (event->type == GDK_DROP_START)
1618 info->dropped = TRUE;
1619 /* We send a leave here so that the widget unhighlights
1624 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1625 info->widget = NULL;
1629 window = gtk_widget_get_window (toplevel);
1631 #ifdef GDK_WINDOWING_X11
1632 /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
1634 * Currently gdk_window_get_position doesn't provide reliable
1635 * information for embedded windows, so we call the much more
1636 * expensive gdk_window_get_origin().
1638 if (GTK_IS_PLUG (toplevel))
1639 gdk_window_get_origin (window, &tx, &ty);
1641 #endif /* GDK_WINDOWING_X11 */
1642 gdk_window_get_position (window, &tx, &ty);
1644 found = gtk_drag_find_widget (toplevel,
1647 event->dnd.x_root - tx,
1648 event->dnd.y_root - ty,
1650 (event->type == GDK_DRAG_MOTION) ?
1651 gtk_drag_dest_motion :
1652 gtk_drag_dest_drop);
1654 if (info->widget && !found)
1656 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1657 info->widget = NULL;
1662 if (event->type == GDK_DRAG_MOTION)
1665 gdk_drag_status (context, 0, event->dnd.time);
1667 else if (event->type == GDK_DROP_START && !info->proxy_source)
1669 gdk_drop_reply (context, found, event->dnd.time);
1670 if ((gdk_drag_context_get_protocol (context) == GDK_DRAG_PROTO_MOTIF) && !found)
1671 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1677 g_assert_not_reached ();
1682 * gtk_drag_dest_find_target: (method)
1683 * @widget: drag destination widget
1684 * @context: drag context
1685 * @target_list: (allow-none): list of droppable targets, or %NULL to use
1686 * gtk_drag_dest_get_target_list (@widget).
1688 * Looks for a match between the supported targets of @context and the
1689 * @dest_target_list, returning the first matching target, otherwise
1690 * returning %GDK_NONE. @dest_target_list should usually be the return
1691 * value from gtk_drag_dest_get_target_list(), but some widgets may
1692 * have different valid targets for different parts of the widget; in
1693 * that case, they will have to implement a drag_motion handler that
1694 * passes the correct target list to this function.
1696 * Return value: (transfer none): first target that the source offers
1697 * and the dest can accept, or %GDK_NONE
1700 gtk_drag_dest_find_target (GtkWidget *widget,
1701 GdkDragContext *context,
1702 GtkTargetList *target_list)
1705 GList *tmp_source = NULL;
1706 GtkWidget *source_widget;
1708 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1709 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1712 source_widget = gtk_drag_get_source_widget (context);
1714 if (target_list == NULL)
1715 target_list = gtk_drag_dest_get_target_list (widget);
1717 if (target_list == NULL)
1720 tmp_target = target_list->list;
1723 GtkTargetPair *pair = tmp_target->data;
1724 tmp_source = gdk_drag_context_list_targets (context);
1727 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1729 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1730 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)) &&
1731 (!(pair->flags & GTK_TARGET_OTHER_APP) || !source_widget) &&
1732 (!(pair->flags & GTK_TARGET_OTHER_WIDGET) || (source_widget != widget)))
1733 return pair->target;
1737 tmp_source = tmp_source->next;
1739 tmp_target = tmp_target->next;
1746 gtk_drag_selection_received (GtkWidget *widget,
1747 GtkSelectionData *selection_data,
1751 GdkDragContext *context;
1752 GtkDragDestInfo *info;
1753 GtkWidget *drop_widget;
1758 context = g_object_get_data (G_OBJECT (widget), "drag-context");
1759 info = gtk_drag_get_dest_info (context, FALSE);
1761 if (info->proxy_data &&
1762 gtk_selection_data_get_target (info->proxy_data) == gtk_selection_data_get_target (selection_data))
1764 gtk_selection_data_set (info->proxy_data,
1765 gtk_selection_data_get_data_type (selection_data),
1766 gtk_selection_data_get_format (selection_data),
1767 gtk_selection_data_get_data (selection_data),
1768 gtk_selection_data_get_length (selection_data));
1773 target = gtk_selection_data_get_target (selection_data);
1774 if (target == gdk_atom_intern_static_string ("DELETE"))
1776 gtk_drag_finish (context, TRUE, FALSE, time);
1778 else if ((target == gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS")) ||
1779 (target == gdk_atom_intern_static_string ("XmTRANSFER_FAILURE")))
1785 GtkDragDestSite *site;
1787 site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1789 if (site && site->target_list)
1793 if (gtk_target_list_find (site->target_list,
1797 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1798 gtk_selection_data_get_length (selection_data) >= 0)
1799 g_signal_emit_by_name (drop_widget,
1800 "drag-data-received",
1801 context, info->drop_x, info->drop_y,
1808 g_signal_emit_by_name (drop_widget,
1809 "drag-data-received",
1810 context, info->drop_x, info->drop_y,
1815 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1818 gtk_drag_finish (context,
1819 (gtk_selection_data_get_length (selection_data) >= 0),
1820 (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
1824 g_object_unref (drop_widget);
1827 g_signal_handlers_disconnect_by_func (widget,
1828 gtk_drag_selection_received,
1831 g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
1832 g_object_unref (context);
1834 gtk_drag_release_ipc_widget (widget);
1837 /*************************************************************
1838 * gtk_drag_find_widget:
1839 * Function used to locate widgets for
1840 * DRAG_MOTION and DROP_START events.
1841 *************************************************************/
1844 gtk_drag_find_widget (GtkWidget *widget,
1845 GdkDragContext *context,
1846 GtkDragDestInfo *info,
1850 GtkDragDestCallback callback)
1852 if (!gtk_widget_get_mapped (widget) ||
1853 !gtk_widget_get_sensitive (widget))
1856 /* Get the widget at the pointer coordinates and travel up
1857 * the widget hierarchy from there.
1859 widget = _gtk_widget_find_at_coords (gtk_widget_get_window (widget),
1867 GList *hierarchy = NULL;
1868 gboolean found = FALSE;
1870 if (!gtk_widget_get_mapped (widget) ||
1871 !gtk_widget_get_sensitive (widget))
1874 /* need to reference the entire hierarchy temporarily in case the
1875 * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
1877 for (parent = widget;
1879 parent = gtk_widget_get_parent (parent))
1881 hierarchy = g_list_prepend (hierarchy, g_object_ref (parent));
1884 /* If the current widget is registered as a drop site, check to
1885 * emit "drag-motion" to check if we are actually in a drop
1888 if (g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1890 found = callback (widget, context, x, y, time);
1892 /* If so, send a "drag-leave" to the last widget */
1895 if (info->widget && info->widget != widget)
1897 gtk_drag_dest_leave (info->widget, context, time);
1900 info->widget = widget;
1906 /* Get the parent before unreffing the hierarchy because
1907 * invoking the callback might have destroyed the widget
1909 parent = gtk_widget_get_parent (widget);
1911 /* The parent might be going away when unreffing the
1912 * hierarchy, so also protect againt that
1915 g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
1918 g_list_foreach (hierarchy, (GFunc) g_object_unref, NULL);
1919 g_list_free (hierarchy);
1925 g_object_remove_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
1929 if (!gtk_widget_translate_coordinates (widget, parent, x, y, &x, &y))
1939 gtk_drag_proxy_begin (GtkWidget *widget,
1940 GtkDragDestInfo *dest_info,
1943 GtkDragSourceInfo *source_info;
1945 GdkDragContext *context;
1946 GtkWidget *ipc_widget;
1948 if (dest_info->proxy_source)
1950 gdk_drag_abort (dest_info->proxy_source->context, time);
1951 gtk_drag_source_info_destroy (dest_info->proxy_source);
1952 dest_info->proxy_source = NULL;
1955 ipc_widget = gtk_drag_get_ipc_widget (widget);
1956 context = gdk_drag_begin (gtk_widget_get_window (ipc_widget),
1957 gdk_drag_context_list_targets (dest_info->context));
1959 source_info = gtk_drag_get_source_info (context, TRUE);
1961 source_info->ipc_widget = ipc_widget;
1962 source_info->widget = g_object_ref (widget);
1964 source_info->target_list = gtk_target_list_new (NULL, 0);
1965 tmp_list = gdk_drag_context_list_targets (dest_info->context);
1968 gtk_target_list_add (source_info->target_list,
1969 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1970 tmp_list = tmp_list->next;
1973 source_info->proxy_dest = dest_info;
1975 g_signal_connect (ipc_widget,
1977 G_CALLBACK (gtk_drag_selection_get),
1980 dest_info->proxy_source = source_info;
1984 gtk_drag_dest_info_destroy (gpointer data)
1986 g_slice_free (GtkDragDestInfo, data);
1989 static GtkDragDestInfo *
1990 gtk_drag_get_dest_info (GdkDragContext *context,
1993 GtkDragDestInfo *info;
1994 static GQuark info_quark = 0;
1996 info_quark = g_quark_from_static_string ("gtk-dest-info");
1998 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1999 if (!info && create)
2001 info = g_slice_new0 (GtkDragDestInfo);
2002 info->context = context;
2003 g_object_set_qdata_full (G_OBJECT (context), info_quark,
2004 info, gtk_drag_dest_info_destroy);
2010 static GQuark dest_info_quark = 0;
2012 static GtkDragSourceInfo *
2013 gtk_drag_get_source_info (GdkDragContext *context,
2016 GtkDragSourceInfo *info;
2017 if (!dest_info_quark)
2018 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
2020 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
2021 if (!info && create)
2023 info = g_new0 (GtkDragSourceInfo, 1);
2024 info->context = context;
2025 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
2032 gtk_drag_clear_source_info (GdkDragContext *context)
2034 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
2038 gtk_drag_dest_realized (GtkWidget *widget)
2040 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2042 if (gtk_widget_is_toplevel (toplevel))
2043 gdk_window_register_dnd (gtk_widget_get_window (toplevel));
2047 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
2048 GtkWidget *previous_toplevel)
2050 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2052 if (gtk_widget_is_toplevel (toplevel) && gtk_widget_get_realized (toplevel))
2053 gdk_window_register_dnd (gtk_widget_get_window (toplevel));
2057 gtk_drag_dest_site_destroy (gpointer data)
2059 GtkDragDestSite *site = data;
2061 if (site->proxy_window)
2062 g_object_unref (site->proxy_window);
2064 if (site->target_list)
2065 gtk_target_list_unref (site->target_list);
2067 g_slice_free (GtkDragDestSite, site);
2071 * Default drag handlers
2074 gtk_drag_dest_leave (GtkWidget *widget,
2075 GdkDragContext *context,
2078 GtkDragDestSite *site;
2080 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2081 g_return_if_fail (site != NULL);
2085 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2087 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
2089 gdk_drag_abort (info->proxy_source->context, time);
2090 gtk_drag_source_info_destroy (info->proxy_source);
2091 info->proxy_source = NULL;
2098 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
2099 gtk_drag_unhighlight (widget);
2101 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
2103 g_signal_emit_by_name (widget, "drag-leave", context, time);
2105 site->have_drag = FALSE;
2110 gtk_drag_dest_motion (GtkWidget *widget,
2111 GdkDragContext *context,
2116 GtkDragDestSite *site;
2117 GdkDragAction action = 0;
2120 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2121 g_return_val_if_fail (site != NULL, FALSE);
2126 GdkEvent *current_event;
2127 GdkWindow *dest_window;
2128 GdkDragProtocol proto;
2130 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2132 if (!info->proxy_source || info->proxy_source->widget != widget)
2133 gtk_drag_proxy_begin (widget, info, time);
2135 current_event = gtk_get_current_event ();
2137 if (site->proxy_window)
2139 dest_window = site->proxy_window;
2140 proto = site->proxy_protocol;
2144 gdk_drag_find_window_for_screen (info->proxy_source->context,
2146 gdk_window_get_screen (current_event->dnd.window),
2147 current_event->dnd.x_root,
2148 current_event->dnd.y_root,
2149 &dest_window, &proto);
2152 gdk_drag_motion (info->proxy_source->context,
2154 current_event->dnd.x_root,
2155 current_event->dnd.y_root,
2156 gdk_drag_context_get_suggested_action (context),
2157 gdk_drag_context_get_actions (context),
2160 if (!site->proxy_window && dest_window)
2161 g_object_unref (dest_window);
2163 selection = gdk_drag_get_selection (info->proxy_source->context);
2165 selection != gdk_drag_get_selection (info->context))
2166 gtk_drag_source_check_selection (info->proxy_source, selection, time);
2168 gdk_event_free (current_event);
2173 if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
2175 if (gdk_drag_context_get_suggested_action (context) & site->actions)
2176 action = gdk_drag_context_get_suggested_action (context);
2181 for (i = 0; i < 8; i++)
2183 if ((site->actions & (1 << i)) &&
2184 (gdk_drag_context_get_actions (context) & (1 << i)))
2192 if (action && gtk_drag_dest_find_target (widget, context, NULL))
2194 if (!site->have_drag)
2196 site->have_drag = TRUE;
2197 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
2198 gtk_drag_highlight (widget);
2201 gdk_drag_status (context, action, time);
2205 gdk_drag_status (context, 0, time);
2206 if (!site->track_motion)
2211 g_signal_emit_by_name (widget, "drag-motion",
2212 context, x, y, time, &retval);
2214 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
2218 gtk_drag_dest_drop (GtkWidget *widget,
2219 GdkDragContext *context,
2224 GtkDragDestSite *site;
2225 GtkDragDestInfo *info;
2227 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2228 g_return_val_if_fail (site != NULL, FALSE);
2230 info = gtk_drag_get_dest_info (context, FALSE);
2231 g_return_val_if_fail (info != NULL, FALSE);
2238 if (info->proxy_source ||
2239 (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_ROOTWIN))
2241 gtk_drag_drop (info->proxy_source, time);
2245 /* We need to synthesize a motion event, wait for a status,
2246 * and, if we get a good one, do a drop.
2249 GdkEvent *current_event;
2251 GdkWindow *dest_window;
2252 GdkDragProtocol proto;
2254 gtk_drag_proxy_begin (widget, info, time);
2255 info->proxy_drop_wait = TRUE;
2256 info->proxy_drop_time = time;
2258 current_event = gtk_get_current_event ();
2260 if (site->proxy_window)
2262 dest_window = site->proxy_window;
2263 proto = site->proxy_protocol;
2267 gdk_drag_find_window_for_screen (info->proxy_source->context,
2269 gdk_window_get_screen (current_event->dnd.window),
2270 current_event->dnd.x_root,
2271 current_event->dnd.y_root,
2272 &dest_window, &proto);
2275 gdk_drag_motion (info->proxy_source->context,
2277 current_event->dnd.x_root,
2278 current_event->dnd.y_root,
2279 gdk_drag_context_get_suggested_action (context),
2280 gdk_drag_context_get_actions (context),
2283 if (!site->proxy_window && dest_window)
2284 g_object_unref (dest_window);
2286 selection = gdk_drag_get_selection (info->proxy_source->context);
2288 selection != gdk_drag_get_selection (info->context))
2289 gtk_drag_source_check_selection (info->proxy_source, selection, time);
2291 gdk_event_free (current_event);
2300 if (site->flags & GTK_DEST_DEFAULT_DROP)
2302 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
2304 if (target == GDK_NONE)
2306 gtk_drag_finish (context, FALSE, FALSE, time);
2310 gtk_drag_get_data (widget, context, target, time);
2313 g_signal_emit_by_name (widget, "drag-drop",
2314 context, x, y, time, &retval);
2316 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
2324 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
2325 * so that we can set the icon from the source site information
2327 static GdkDragContext *
2328 gtk_drag_begin_internal (GtkWidget *widget,
2329 GtkDragSourceSite *site,
2330 GtkTargetList *target_list,
2331 GdkDragAction actions,
2335 GtkDragSourceInfo *info;
2336 GList *targets = NULL;
2338 guint32 time = GDK_CURRENT_TIME;
2339 GdkDragAction possible_actions, suggested_action;
2340 GdkDragContext *context;
2341 GtkWidget *ipc_widget;
2343 GdkDevice *pointer, *keyboard;
2344 GdkWindow *ipc_window;
2346 pointer = keyboard = NULL;
2347 ipc_widget = gtk_drag_get_ipc_widget (widget);
2349 gtk_drag_get_event_actions (event, button, actions,
2350 &suggested_action, &possible_actions);
2352 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2358 time = gdk_event_get_time (event);
2359 if (time == GDK_CURRENT_TIME)
2360 time = gtk_get_current_event_time ();
2362 pointer = gdk_event_get_device (event);
2364 if (gdk_device_get_source (pointer) == GDK_SOURCE_KEYBOARD)
2367 pointer = gdk_device_get_associated_device (keyboard);
2370 keyboard = gdk_device_get_associated_device (pointer);
2374 GdkDeviceManager *device_manager;
2376 device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
2377 pointer = gdk_device_manager_get_client_pointer (device_manager);
2378 keyboard = gdk_device_get_associated_device (pointer);
2384 ipc_window = gtk_widget_get_window (ipc_widget);
2386 if (gdk_device_grab (pointer, ipc_window,
2387 GDK_OWNERSHIP_APPLICATION, FALSE,
2388 GDK_POINTER_MOTION_MASK |
2389 GDK_BUTTON_RELEASE_MASK,
2390 cursor, time) != GDK_GRAB_SUCCESS)
2392 gtk_drag_release_ipc_widget (ipc_widget);
2397 grab_dnd_keys (ipc_widget, keyboard, time);
2399 /* We use a GTK grab here to override any grabs that the widget
2400 * we are dragging from might have held
2402 gtk_device_grab_add (ipc_widget, pointer, FALSE);
2404 tmp_list = g_list_last (target_list->list);
2407 GtkTargetPair *pair = tmp_list->data;
2408 targets = g_list_prepend (targets,
2409 GINT_TO_POINTER (pair->target));
2410 tmp_list = tmp_list->prev;
2413 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
2415 context = gdk_drag_begin (ipc_window, targets);
2416 gdk_drag_context_set_device (context, pointer);
2417 g_list_free (targets);
2419 info = gtk_drag_get_source_info (context, TRUE);
2421 info->ipc_widget = ipc_widget;
2422 g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
2424 info->widget = g_object_ref (widget);
2426 info->button = button;
2427 info->cursor = cursor;
2428 info->target_list = target_list;
2429 gtk_target_list_ref (target_list);
2431 info->possible_actions = actions;
2433 info->status = GTK_DRAG_STATUS_DRAG;
2434 info->last_event = NULL;
2435 info->selections = NULL;
2436 info->icon_window = NULL;
2437 info->destroy_icon = FALSE;
2439 /* Set cur_x, cur_y here so if the "drag-begin" signal shows
2440 * the drag icon, it will be in the right place
2442 if (event && event->type == GDK_MOTION_NOTIFY)
2444 info->cur_screen = gtk_widget_get_screen (widget);
2445 info->cur_x = event->motion.x_root;
2446 info->cur_y = event->motion.y_root;
2450 gdk_device_get_position (pointer, &info->cur_screen, &info->cur_x, &info->cur_y);
2453 g_signal_emit_by_name (widget, "drag-begin", info->context);
2455 /* Ensure that we have an icon before we start the drag; the
2456 * application may have set one in ::drag_begin, or it may
2459 if (!info->icon_window && !info->icon_pixbuf)
2461 if (!site || site->icon_type == GTK_IMAGE_EMPTY)
2462 gtk_drag_set_icon_default (context);
2464 switch (site->icon_type)
2466 case GTK_IMAGE_PIXBUF:
2467 gtk_drag_set_icon_pixbuf (context,
2468 site->icon_data.pixbuf.pixbuf,
2471 case GTK_IMAGE_STOCK:
2472 gtk_drag_set_icon_stock (context,
2473 site->icon_data.stock.stock_id,
2476 case GTK_IMAGE_ICON_NAME:
2477 gtk_drag_set_icon_name (context,
2478 site->icon_data.name.icon_name,
2481 case GTK_IMAGE_GICON:
2482 gtk_drag_set_icon_gicon (context,
2483 site->icon_data.gicon.icon,
2486 case GTK_IMAGE_EMPTY:
2488 g_assert_not_reached();
2493 /* We need to composite the icon into the cursor, if we are
2494 * not using an icon window.
2496 if (info->icon_pixbuf)
2498 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2502 if (cursor != info->cursor)
2504 gdk_device_grab (pointer, gtk_widget_get_window (widget),
2505 GDK_OWNERSHIP_APPLICATION, FALSE,
2506 GDK_POINTER_MOTION_MASK |
2507 GDK_BUTTON_RELEASE_MASK,
2509 info->cursor = cursor;
2513 if (event && event->type == GDK_MOTION_NOTIFY)
2514 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
2516 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
2518 info->start_x = info->cur_x;
2519 info->start_y = info->cur_y;
2521 g_signal_connect (info->ipc_widget, "grab-broken-event",
2522 G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
2523 g_signal_connect (info->ipc_widget, "grab-notify",
2524 G_CALLBACK (gtk_drag_grab_notify_cb), info);
2525 g_signal_connect (info->ipc_widget, "button-release-event",
2526 G_CALLBACK (gtk_drag_button_release_cb), info);
2527 g_signal_connect (info->ipc_widget, "motion-notify-event",
2528 G_CALLBACK (gtk_drag_motion_cb), info);
2529 g_signal_connect (info->ipc_widget, "key-press-event",
2530 G_CALLBACK (gtk_drag_key_cb), info);
2531 g_signal_connect (info->ipc_widget, "key-release-event",
2532 G_CALLBACK (gtk_drag_key_cb), info);
2533 g_signal_connect (info->ipc_widget, "selection-get",
2534 G_CALLBACK (gtk_drag_selection_get), info);
2536 info->have_grab = TRUE;
2537 info->grab_time = time;
2539 return info->context;
2543 * gtk_drag_begin: (method)
2544 * @widget: the source widget.
2545 * @targets: The targets (data formats) in which the
2546 * source can provide the data.
2547 * @actions: A bitmask of the allowed drag actions for this drag.
2548 * @button: The button the user clicked to start the drag.
2549 * @event: The event that triggered the start of the drag.
2551 * Initiates a drag on the source side. The function
2552 * only needs to be used when the application is
2553 * starting drags itself, and is not needed when
2554 * gtk_drag_source_set() is used.
2556 * The @event is used to retrieve the timestamp that will be used internally to
2557 * grab the pointer. If @event is #NULL, then GDK_CURRENT_TIME will be used.
2558 * However, you should try to pass a real event in all cases, since that can be
2559 * used by GTK+ to get information about the start position of the drag, for
2560 * example if the @event is a %GDK_MOTION_NOTIFY.
2562 * Generally there are three cases when you want to start a drag by hand by
2563 * calling this function:
2565 * 1. During a #GtkWidget::button-press-event handler, if you want to start a drag
2566 * immediately when the user presses the mouse button. Pass the @event
2567 * that you have in your #GtkWidget::button-press-event handler.
2569 * 2. During a #GtkWidget::motion-notify-event handler, if you want to start a drag
2570 * when the mouse moves past a certain threshold distance after a button-press.
2571 * Pass the @event that you have in your #GtkWidget::motion-notify-event handler.
2573 * 3. During a timeout handler, if you want to start a drag after the mouse
2574 * button is held down for some time. Try to save the last event that you got
2575 * from the mouse, using gdk_event_copy(), and pass it to this function
2576 * (remember to free the event with gdk_event_free() when you are done).
2577 * If you can really not pass a real event, pass #NULL instead.
2579 * Return value: (transfer none): the context for this drag.
2582 gtk_drag_begin (GtkWidget *widget,
2583 GtkTargetList *targets,
2584 GdkDragAction actions,
2588 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2589 g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
2590 g_return_val_if_fail (targets != NULL, NULL);
2592 return gtk_drag_begin_internal (widget, NULL, targets,
2593 actions, button, event);
2597 * gtk_drag_source_set: (method)
2598 * @widget: a #GtkWidget
2599 * @start_button_mask: the bitmask of buttons that can start the drag
2600 * @targets: (allow-none) (array length=n_targets): the table of targets that the drag will support,
2602 * @n_targets: the number of items in @targets
2603 * @actions: the bitmask of possible actions for a drag from this widget
2605 * Sets up a widget so that GTK+ will start a drag operation when the user
2606 * clicks and drags on the widget. The widget must have a window.
2609 gtk_drag_source_set (GtkWidget *widget,
2610 GdkModifierType start_button_mask,
2611 const GtkTargetEntry *targets,
2613 GdkDragAction actions)
2615 GtkDragSourceSite *site;
2617 g_return_if_fail (GTK_IS_WIDGET (widget));
2619 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2621 gtk_widget_add_events (widget,
2622 gtk_widget_get_events (widget) |
2623 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2624 GDK_BUTTON_MOTION_MASK);
2628 if (site->target_list)
2629 gtk_target_list_unref (site->target_list);
2633 site = g_slice_new0 (GtkDragSourceSite);
2635 site->icon_type = GTK_IMAGE_EMPTY;
2637 g_signal_connect (widget, "button-press-event",
2638 G_CALLBACK (gtk_drag_source_event_cb),
2640 g_signal_connect (widget, "button-release-event",
2641 G_CALLBACK (gtk_drag_source_event_cb),
2643 g_signal_connect (widget, "motion-notify-event",
2644 G_CALLBACK (gtk_drag_source_event_cb),
2647 g_object_set_data_full (G_OBJECT (widget),
2648 I_("gtk-site-data"),
2649 site, gtk_drag_source_site_destroy);
2652 site->start_button_mask = start_button_mask;
2654 site->target_list = gtk_target_list_new (targets, n_targets);
2656 site->actions = actions;
2660 * gtk_drag_source_unset:
2661 * @widget: a #GtkWidget
2663 * Undoes the effects of gtk_drag_source_set().
2666 gtk_drag_source_unset (GtkWidget *widget)
2668 GtkDragSourceSite *site;
2670 g_return_if_fail (GTK_IS_WIDGET (widget));
2672 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2676 g_signal_handlers_disconnect_by_func (widget,
2677 gtk_drag_source_event_cb,
2679 g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
2684 * gtk_drag_source_get_target_list: (method)
2685 * @widget: a #GtkWidget
2687 * Gets the list of targets this widget can provide for
2690 * Return value: (transfer none): the #GtkTargetList, or %NULL if none
2695 gtk_drag_source_get_target_list (GtkWidget *widget)
2697 GtkDragSourceSite *site;
2699 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2701 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2703 return site ? site->target_list : NULL;
2707 * gtk_drag_source_set_target_list: (method)
2708 * @widget: a #GtkWidget that's a drag source
2709 * @target_list: (allow-none): list of draggable targets, or %NULL for none
2711 * Changes the target types that this widget offers for drag-and-drop.
2712 * The widget must first be made into a drag source with
2713 * gtk_drag_source_set().
2718 gtk_drag_source_set_target_list (GtkWidget *widget,
2719 GtkTargetList *target_list)
2721 GtkDragSourceSite *site;
2723 g_return_if_fail (GTK_IS_WIDGET (widget));
2725 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2728 g_warning ("gtk_drag_source_set_target_list() requires the widget "
2729 "to already be a drag source.");
2734 gtk_target_list_ref (target_list);
2736 if (site->target_list)
2737 gtk_target_list_unref (site->target_list);
2739 site->target_list = target_list;
2743 * gtk_drag_source_add_text_targets: (method)
2744 * @widget: a #GtkWidget that's is a drag source
2746 * Add the text targets supported by #GtkSelection to
2747 * the target list of the drag source. The targets
2748 * are added with @info = 0. If you need another value,
2749 * use gtk_target_list_add_text_targets() and
2750 * gtk_drag_source_set_target_list().
2755 gtk_drag_source_add_text_targets (GtkWidget *widget)
2757 GtkTargetList *target_list;
2759 target_list = gtk_drag_source_get_target_list (widget);
2761 gtk_target_list_ref (target_list);
2763 target_list = gtk_target_list_new (NULL, 0);
2764 gtk_target_list_add_text_targets (target_list, 0);
2765 gtk_drag_source_set_target_list (widget, target_list);
2766 gtk_target_list_unref (target_list);
2770 * gtk_drag_source_add_image_targets: (method)
2771 * @widget: a #GtkWidget that's is a drag source
2773 * Add the writable image targets supported by #GtkSelection to
2774 * the target list of the drag source. The targets
2775 * are added with @info = 0. If you need another value,
2776 * use gtk_target_list_add_image_targets() and
2777 * gtk_drag_source_set_target_list().
2782 gtk_drag_source_add_image_targets (GtkWidget *widget)
2784 GtkTargetList *target_list;
2786 target_list = gtk_drag_source_get_target_list (widget);
2788 gtk_target_list_ref (target_list);
2790 target_list = gtk_target_list_new (NULL, 0);
2791 gtk_target_list_add_image_targets (target_list, 0, TRUE);
2792 gtk_drag_source_set_target_list (widget, target_list);
2793 gtk_target_list_unref (target_list);
2797 * gtk_drag_source_add_uri_targets: (method)
2798 * @widget: a #GtkWidget that's is a drag source
2800 * Add the URI targets supported by #GtkSelection to
2801 * the target list of the drag source. The targets
2802 * are added with @info = 0. If you need another value,
2803 * use gtk_target_list_add_uri_targets() and
2804 * gtk_drag_source_set_target_list().
2809 gtk_drag_source_add_uri_targets (GtkWidget *widget)
2811 GtkTargetList *target_list;
2813 target_list = gtk_drag_source_get_target_list (widget);
2815 gtk_target_list_ref (target_list);
2817 target_list = gtk_target_list_new (NULL, 0);
2818 gtk_target_list_add_uri_targets (target_list, 0);
2819 gtk_drag_source_set_target_list (widget, target_list);
2820 gtk_target_list_unref (target_list);
2824 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2826 switch (site->icon_type)
2828 case GTK_IMAGE_EMPTY:
2830 case GTK_IMAGE_PIXBUF:
2831 g_object_unref (site->icon_data.pixbuf.pixbuf);
2833 case GTK_IMAGE_STOCK:
2834 g_free (site->icon_data.stock.stock_id);
2836 case GTK_IMAGE_ICON_NAME:
2837 g_free (site->icon_data.name.icon_name);
2839 case GTK_IMAGE_GICON:
2840 _gtk_image_gicon_data_clear (&(site->icon_data.gicon));
2843 g_assert_not_reached();
2846 site->icon_type = GTK_IMAGE_EMPTY;
2850 * gtk_drag_source_set_icon_pixbuf: (method)
2851 * @widget: a #GtkWidget
2852 * @pixbuf: the #GdkPixbuf for the drag icon
2854 * Sets the icon that will be used for drags from a particular widget
2855 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2856 * release it when it is no longer needed.
2859 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2862 GtkDragSourceSite *site;
2864 g_return_if_fail (GTK_IS_WIDGET (widget));
2865 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2867 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2868 g_return_if_fail (site != NULL);
2869 g_object_ref (pixbuf);
2871 gtk_drag_source_unset_icon (site);
2873 site->icon_type = GTK_IMAGE_PIXBUF;
2874 site->icon_data.pixbuf.pixbuf = pixbuf;
2878 * gtk_drag_source_set_icon_stock: (method)
2879 * @widget: a #GtkWidget
2880 * @stock_id: the ID of the stock icon to use
2882 * Sets the icon that will be used for drags from a particular source
2886 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2887 const gchar *stock_id)
2889 GtkDragSourceSite *site;
2891 g_return_if_fail (GTK_IS_WIDGET (widget));
2892 g_return_if_fail (stock_id != NULL);
2894 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2895 g_return_if_fail (site != NULL);
2897 gtk_drag_source_unset_icon (site);
2899 site->icon_type = GTK_IMAGE_STOCK;
2900 site->icon_data.stock.stock_id = g_strdup (stock_id);
2904 * gtk_drag_source_set_icon_name: (method)
2905 * @widget: a #GtkWidget
2906 * @icon_name: name of icon to use
2908 * Sets the icon that will be used for drags from a particular source
2909 * to a themed icon. See the docs for #GtkIconTheme for more details.
2914 gtk_drag_source_set_icon_name (GtkWidget *widget,
2915 const gchar *icon_name)
2917 GtkDragSourceSite *site;
2919 g_return_if_fail (GTK_IS_WIDGET (widget));
2920 g_return_if_fail (icon_name != NULL);
2922 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2923 g_return_if_fail (site != NULL);
2925 gtk_drag_source_unset_icon (site);
2927 site->icon_type = GTK_IMAGE_ICON_NAME;
2928 site->icon_data.name.icon_name = g_strdup (icon_name);
2932 * gtk_drag_source_set_icon_gicon: (method)
2933 * @widget: a #GtkWidget
2936 * Sets the icon that will be used for drags from a particular source
2937 * to @icon. See the docs for #GtkIconTheme for more details.
2942 gtk_drag_source_set_icon_gicon (GtkWidget *widget,
2945 GtkDragSourceSite *site;
2947 g_return_if_fail (GTK_IS_WIDGET (widget));
2948 g_return_if_fail (icon != NULL);
2950 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2951 g_return_if_fail (site != NULL);
2953 gtk_drag_source_unset_icon (site);
2955 site->icon_type = GTK_IMAGE_GICON;
2956 site->icon_data.gicon.icon = g_object_ref (icon);
2960 gtk_drag_get_icon (GtkDragSourceInfo *info,
2961 GtkWidget **icon_window,
2965 if (get_can_change_screen (info->icon_window))
2966 gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2969 if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2971 if (!info->fallback_icon)
2973 gint save_hot_x, save_hot_y;
2974 gboolean save_destroy_icon;
2975 GtkWidget *save_icon_window;
2977 /* HACK to get the appropriate icon
2979 save_icon_window = info->icon_window;
2980 save_hot_x = info->hot_x;
2981 save_hot_y = info->hot_x;
2982 save_destroy_icon = info->destroy_icon;
2984 info->icon_window = NULL;
2985 set_icon_stock_pixbuf (info->context,
2986 GTK_STOCK_DND, NULL, -2, -2, TRUE);
2987 info->fallback_icon = info->icon_window;
2989 info->icon_window = save_icon_window;
2990 info->hot_x = save_hot_x;
2991 info->hot_y = save_hot_y;
2992 info->destroy_icon = save_destroy_icon;
2995 gtk_widget_hide (info->icon_window);
2997 *icon_window = info->fallback_icon;
2998 gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
3005 if (info->fallback_icon)
3006 gtk_widget_hide (info->fallback_icon);
3008 *icon_window = info->icon_window;
3009 *hot_x = info->hot_x;
3010 *hot_y = info->hot_y;
3015 gtk_drag_update_icon (GtkDragSourceInfo *info)
3017 if (info->icon_window)
3019 GtkWidget *icon_window;
3022 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
3024 gtk_window_move (GTK_WINDOW (icon_window),
3025 info->cur_x - hot_x,
3026 info->cur_y - hot_y);
3028 if (gtk_widget_get_visible (icon_window))
3029 gdk_window_raise (gtk_widget_get_window (icon_window));
3031 gtk_widget_show (icon_window);
3036 gtk_drag_set_icon_window (GdkDragContext *context,
3040 gboolean destroy_on_release)
3042 GtkDragSourceInfo *info;
3044 info = gtk_drag_get_source_info (context, FALSE);
3047 if (destroy_on_release)
3048 gtk_widget_destroy (widget);
3052 gtk_drag_remove_icon (info);
3055 g_object_ref (widget);
3057 info->icon_window = widget;
3058 info->hot_x = hot_x;
3059 info->hot_y = hot_y;
3060 info->destroy_icon = destroy_on_release;
3062 if (widget && info->icon_pixbuf)
3064 g_object_unref (info->icon_pixbuf);
3065 info->icon_pixbuf = NULL;
3068 gtk_drag_update_cursor (info);
3069 gtk_drag_update_icon (info);
3073 * gtk_drag_set_icon_widget:
3074 * @context: the context for a drag. (This must be called
3075 with a context for the source side of a drag)
3076 * @widget: a toplevel window to use as an icon.
3077 * @hot_x: the X offset within @widget of the hotspot.
3078 * @hot_y: the Y offset within @widget of the hotspot.
3080 * Changes the icon for a widget to a given widget. GTK+
3081 * will not destroy the icon, so if you don't want
3082 * it to persist, you should connect to the "drag-end"
3083 * signal and destroy it yourself.
3086 gtk_drag_set_icon_widget (GdkDragContext *context,
3091 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3092 g_return_if_fail (GTK_IS_WIDGET (widget));
3094 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
3098 icon_window_realize (GtkWidget *window,
3101 cairo_surface_t *surface;
3102 cairo_pattern_t *pattern;
3105 surface = gdk_window_create_similar_surface (gtk_widget_get_window (window),
3106 CAIRO_CONTENT_COLOR,
3107 gdk_pixbuf_get_width (pixbuf),
3108 gdk_pixbuf_get_height (pixbuf));
3110 cr = cairo_create (surface);
3111 cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
3112 gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
3114 cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
3116 cairo_pop_group_to_source (cr);
3120 pattern = cairo_pattern_create_for_surface (surface);
3121 gdk_window_set_background_pattern (gtk_widget_get_window (window), pattern);
3122 cairo_pattern_destroy (pattern);
3124 cairo_surface_destroy (surface);
3126 if (gdk_pixbuf_get_has_alpha (pixbuf))
3128 cairo_region_t *region;
3130 surface = cairo_image_surface_create (CAIRO_FORMAT_A1,
3131 gdk_pixbuf_get_width (pixbuf),
3132 gdk_pixbuf_get_height (pixbuf));
3134 cr = cairo_create (surface);
3135 gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
3139 region = gdk_cairo_region_create_from_surface (surface);
3140 gtk_widget_shape_combine_region (window, region);
3141 cairo_region_destroy (region);
3143 cairo_surface_destroy (surface);
3148 set_icon_stock_pixbuf (GdkDragContext *context,
3149 const gchar *stock_id,
3153 gboolean force_window)
3158 GdkDisplay *display;
3160 g_return_if_fail (context != NULL);
3161 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
3162 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
3164 screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
3166 window = gtk_window_new (GTK_WINDOW_POPUP);
3167 gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3168 gtk_window_set_screen (GTK_WINDOW (window), screen);
3169 set_can_change_screen (window, TRUE);
3171 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3172 gtk_widget_set_app_paintable (window, TRUE);
3176 pixbuf = gtk_widget_render_icon_pixbuf (window, stock_id,
3181 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
3182 gtk_widget_destroy (window);
3188 g_object_ref (pixbuf);
3190 display = gdk_window_get_display (gdk_drag_context_get_source_window (context));
3191 width = gdk_pixbuf_get_width (pixbuf);
3192 height = gdk_pixbuf_get_height (pixbuf);
3194 if (!force_window &&
3195 gtk_drag_can_use_rgba_cursor (display, width + 2, height + 2))
3197 GtkDragSourceInfo *info;
3199 gtk_widget_destroy (window);
3201 info = gtk_drag_get_source_info (context, FALSE);
3203 if (info->icon_pixbuf)
3204 g_object_unref (info->icon_pixbuf);
3205 info->icon_pixbuf = pixbuf;
3207 gtk_drag_set_icon_window (context, NULL, hot_x, hot_y, TRUE);
3211 gtk_widget_set_size_request (window, width, height);
3213 g_signal_connect_closure (window, "realize",
3214 g_cclosure_new (G_CALLBACK (icon_window_realize),
3216 (GClosureNotify)g_object_unref),
3219 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3224 * gtk_drag_set_icon_pixbuf:
3225 * @context: the context for a drag. (This must be called
3226 * with a context for the source side of a drag)
3227 * @pixbuf: the #GdkPixbuf to use as the drag icon.
3228 * @hot_x: the X offset within @widget of the hotspot.
3229 * @hot_y: the Y offset within @widget of the hotspot.
3231 * Sets @pixbuf as the icon for a given drag.
3234 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
3239 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3240 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
3242 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3246 * gtk_drag_set_icon_stock:
3247 * @context: the context for a drag. (This must be called
3248 * with a context for the source side of a drag)
3249 * @stock_id: the ID of the stock icon to use for the drag.
3250 * @hot_x: the X offset within the icon of the hotspot.
3251 * @hot_y: the Y offset within the icon of the hotspot.
3253 * Sets the icon for a given drag from a stock ID.
3256 gtk_drag_set_icon_stock (GdkDragContext *context,
3257 const gchar *stock_id,
3261 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3262 g_return_if_fail (stock_id != NULL);
3264 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y, FALSE);
3267 /* XXX: This function is in gdk, too. Should it be in Cairo? */
3269 _gtk_cairo_surface_extents (cairo_surface_t *surface,
3270 GdkRectangle *extents)
3272 double x1, x2, y1, y2;
3275 g_return_val_if_fail (surface != NULL, FALSE);
3276 g_return_val_if_fail (extents != NULL, FALSE);
3278 cr = cairo_create (surface);
3279 cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
3288 if (x1 < G_MININT || x1 > G_MAXINT ||
3289 y1 < G_MININT || y1 > G_MAXINT ||
3290 x2 > G_MAXINT || y2 > G_MAXINT)
3292 extents->x = extents->y = extents->width = extents->height = 0;
3298 extents->width = x2;
3299 extents->height = y2;
3305 * gtk_drag_set_icon_surface:
3306 * @context: the context for a drag. (This must be called
3307 * with a context for the source side of a drag)
3308 * @surface: the surface to use as icon
3310 * Sets @surface as the icon for a given drag. GTK+ retains
3311 * references for the arguments, and will release them when
3312 * they are no longer needed.
3314 * To position the surface relative to the mouse, use
3315 * cairo_surface_set_device_offset() on @surface. The mouse
3316 * cursor will be positioned at the (0,0) coordinate of the
3320 gtk_drag_set_icon_surface (GdkDragContext *context,
3321 cairo_surface_t *surface)
3325 GdkRectangle extents;
3326 cairo_pattern_t *pattern;
3328 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3329 g_return_if_fail (surface != NULL);
3331 _gtk_cairo_surface_extents (surface, &extents);
3334 screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
3336 window = gtk_window_new (GTK_WINDOW_POPUP);
3337 gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3338 gtk_window_set_screen (GTK_WINDOW (window), screen);
3339 set_can_change_screen (window, TRUE);
3341 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3342 gtk_widget_set_app_paintable (window, TRUE);
3344 gtk_widget_set_size_request (window, extents.width, extents.height);
3345 gtk_widget_realize (window);
3347 if (cairo_surface_get_content (surface) != CAIRO_CONTENT_COLOR)
3349 cairo_surface_t *saturated;
3350 cairo_region_t *region;
3353 region = gdk_cairo_region_create_from_surface (surface);
3354 cairo_region_translate (region, -extents.x, -extents.y);
3356 gtk_widget_shape_combine_region (window, region);
3357 cairo_region_destroy (region);
3359 /* Need to saturate the colors, so it doesn't look like semi-transparent
3360 * pixels were painted on black. */
3361 saturated = gdk_window_create_similar_surface (gtk_widget_get_window (window),
3362 CAIRO_CONTENT_COLOR,
3366 cr = cairo_create (saturated);
3367 cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
3368 cairo_set_source_surface (cr, surface, -extents.x, -extents.y);
3370 cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
3372 cairo_pop_group_to_source (cr);
3376 pattern = cairo_pattern_create_for_surface (saturated);
3378 cairo_surface_destroy (saturated);
3382 cairo_matrix_t matrix;
3384 pattern = cairo_pattern_create_for_surface (surface);
3385 cairo_matrix_init_translate (&matrix, extents.x, extents.y);
3386 cairo_pattern_set_matrix (pattern, &matrix);
3389 gdk_window_set_background_pattern (gtk_widget_get_window (window), pattern);
3391 gtk_drag_set_icon_window (context, window, extents.x, extents.y, TRUE);
3395 * gtk_drag_set_icon_name:
3396 * @context: the context for a drag. (This must be called
3397 * with a context for the source side of a drag)
3398 * @icon_name: name of icon to use
3399 * @hot_x: the X offset of the hotspot within the icon
3400 * @hot_y: the Y offset of the hotspot within the icon
3402 * Sets the icon for a given drag from a named themed icon. See
3403 * the docs for #GtkIconTheme for more details. Note that the
3404 * size of the icon depends on the icon theme (the icon is
3405 * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus
3406 * @hot_x and @hot_y have to be used with care.
3411 gtk_drag_set_icon_name (GdkDragContext *context,
3412 const gchar *icon_name,
3418 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3419 g_return_if_fail (icon_name != NULL);
3421 icon = g_themed_icon_new (icon_name);
3422 gtk_drag_set_icon_gicon (context, icon, hot_x, hot_y);
3423 g_object_unref (icon);
3427 * gtk_drag_set_icon_gicon:
3428 * @context: the context for a drag. (This must be called
3429 * with a context for the source side of a drag)
3431 * @hot_x: the X offset of the hotspot within the icon
3432 * @hot_y: the Y offset of the hotspot within the icon
3434 * Sets the icon for a given drag from the given @icon. See the
3435 * documentation for gtk_drag_set_icon_name() for more details about
3436 * using icons in drag and drop.
3441 gtk_drag_set_icon_gicon (GdkDragContext *context,
3447 GtkSettings *settings;
3448 GtkIconTheme *icon_theme;
3449 GtkIconInfo *icon_info;
3451 gint width, height, icon_size;
3453 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3454 g_return_if_fail (icon != NULL);
3456 screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
3457 g_return_if_fail (screen != NULL);
3459 settings = gtk_settings_get_for_screen (screen);
3460 if (gtk_icon_size_lookup_for_settings (settings,
3463 icon_size = MAX (width, height);
3465 icon_size = 32; /* default value for GTK_ICON_SIZE_DND */
3467 icon_theme = gtk_icon_theme_get_for_screen (screen);
3469 icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme, icon, icon_size, 0);
3470 if (icon_info != NULL)
3472 pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
3478 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3481 char *str = g_icon_to_string (icon);
3482 g_warning ("Cannot load drag icon from GIcon '%s'", str);
3488 * gtk_drag_set_icon_default:
3489 * @context: the context for a drag. (This must be called
3490 with a context for the source side of a drag)
3492 * Sets the icon for a particular drag to the default
3496 gtk_drag_set_icon_default (GdkDragContext *context)
3498 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3500 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
3503 /*************************************************************
3504 * _gtk_drag_source_handle_event:
3505 * Called from widget event handling code on Drag events
3509 * toplevel: Toplevel widget that received the event
3512 *************************************************************/
3515 _gtk_drag_source_handle_event (GtkWidget *widget,
3518 GtkDragSourceInfo *info;
3519 GdkDragContext *context;
3521 g_return_if_fail (widget != NULL);
3522 g_return_if_fail (event != NULL);
3524 context = event->dnd.context;
3525 info = gtk_drag_get_source_info (context, FALSE);
3529 switch (event->type)
3531 case GDK_DRAG_STATUS:
3535 if (info->proxy_dest)
3537 if (!event->dnd.send_event)
3539 if (info->proxy_dest->proxy_drop_wait)
3541 gboolean result = gdk_drag_context_get_selected_action (context) != 0;
3543 /* Aha - we can finally pass the MOTIF DROP on... */
3544 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
3546 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
3548 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
3552 gdk_drag_status (info->proxy_dest->context,
3553 gdk_drag_context_get_selected_action (event->dnd.context),
3558 else if (info->have_grab)
3560 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
3561 gdk_drag_context_get_selected_action (event->dnd.context),
3563 if (info->cursor != cursor)
3567 pointer = gdk_drag_context_get_device (context);
3568 gdk_device_grab (pointer, gtk_widget_get_window (widget),
3569 GDK_OWNERSHIP_APPLICATION, FALSE,
3570 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3571 cursor, info->grab_time);
3572 info->cursor = cursor;
3575 gtk_drag_add_update_idle (info);
3580 case GDK_DROP_FINISHED:
3581 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, event->dnd.time);
3584 g_assert_not_reached ();
3588 /*************************************************************
3589 * gtk_drag_source_check_selection:
3590 * Check if we've set up handlers/claimed the selection
3591 * for a given drag. If not, add them.
3595 *************************************************************/
3598 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
3604 tmp_list = info->selections;
3607 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
3609 tmp_list = tmp_list->next;
3612 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
3616 info->selections = g_list_prepend (info->selections,
3617 GUINT_TO_POINTER (selection));
3619 tmp_list = info->target_list->list;
3622 GtkTargetPair *pair = tmp_list->data;
3624 gtk_selection_add_target (info->ipc_widget,
3628 tmp_list = tmp_list->next;
3631 if (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_MOTIF)
3633 gtk_selection_add_target (info->ipc_widget,
3635 gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS"),
3636 TARGET_MOTIF_SUCCESS);
3637 gtk_selection_add_target (info->ipc_widget,
3639 gdk_atom_intern_static_string ("XmTRANSFER_FAILURE"),
3640 TARGET_MOTIF_FAILURE);
3643 gtk_selection_add_target (info->ipc_widget,
3645 gdk_atom_intern_static_string ("DELETE"),
3649 /*************************************************************
3650 * gtk_drag_drop_finished:
3651 * Clean up from the drag, and display snapback, if necessary.
3657 *************************************************************/
3660 gtk_drag_drop_finished (GtkDragSourceInfo *info,
3661 GtkDragResult result,
3666 success = (result == GTK_DRAG_RESULT_SUCCESS);
3667 gtk_drag_source_release_selections (info, time);
3669 if (info->proxy_dest)
3671 /* The time from the event isn't reliable for Xdnd drags */
3672 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
3673 info->proxy_dest->proxy_drop_time);
3674 gtk_drag_source_info_destroy (info);
3679 g_signal_emit_by_name (info->widget, "drag-failed",
3680 info->context, result, &success);
3684 gtk_drag_source_info_destroy (info);
3688 GtkDragAnim *anim = g_slice_new0 (GtkDragAnim);
3692 anim->n_steps = MAX (info->cur_x - info->start_x,
3693 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
3694 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
3696 info->cur_screen = gtk_widget_get_screen (info->widget);
3698 if (!info->icon_window)
3699 set_icon_stock_pixbuf (info->context, NULL, info->icon_pixbuf,
3702 gtk_drag_update_icon (info);
3704 /* Mark the context as dead, so if the destination decides
3705 * to respond really late, we still are OK.
3707 gtk_drag_clear_source_info (info->context);
3708 gdk_threads_add_timeout (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
3714 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
3717 GdkDisplay *display = gtk_widget_get_display (info->widget);
3718 GList *tmp_list = info->selections;
3722 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
3723 if (gdk_selection_owner_get_for_display (display, selection) == gtk_widget_get_window (info->ipc_widget))
3724 gtk_selection_owner_set_for_display (display, NULL, selection, time);
3726 tmp_list = tmp_list->next;
3729 g_list_free (info->selections);
3730 info->selections = NULL;
3733 /*************************************************************
3735 * Send a drop event.
3739 *************************************************************/
3742 gtk_drag_drop (GtkDragSourceInfo *info,
3745 if (gdk_drag_context_get_protocol (info->context) == GDK_DRAG_PROTO_ROOTWIN)
3747 GtkSelectionData selection_data;
3749 /* GTK+ traditionally has used application/x-rootwin-drop, but the
3750 * XDND spec specifies x-rootwindow-drop.
3752 GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3753 GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3755 tmp_list = info->target_list->list;
3758 GtkTargetPair *pair = tmp_list->data;
3760 if (pair->target == target1 || pair->target == target2)
3762 selection_data.selection = GDK_NONE;
3763 selection_data.target = pair->target;
3764 selection_data.data = NULL;
3765 selection_data.length = -1;
3767 g_signal_emit_by_name (info->widget, "drag-data-get",
3768 info->context, &selection_data,
3772 /* FIXME: Should we check for length >= 0 here? */
3773 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3776 tmp_list = tmp_list->next;
3778 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3782 if (info->icon_window)
3783 gtk_widget_hide (info->icon_window);
3785 gdk_drag_drop (info->context, time);
3786 info->drop_timeout = gdk_threads_add_timeout (DROP_ABORT_TIME,
3787 gtk_drag_abort_timeout,
3793 * Source side callbacks.
3797 gtk_drag_source_event_cb (GtkWidget *widget,
3801 GtkDragSourceSite *site;
3802 gboolean retval = FALSE;
3803 site = (GtkDragSourceSite *)data;
3805 switch (event->type)
3807 case GDK_BUTTON_PRESS:
3808 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3810 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
3811 site->x = event->button.x;
3812 site->y = event->button.y;
3816 case GDK_BUTTON_RELEASE:
3817 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3818 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
3821 case GDK_MOTION_NOTIFY:
3822 if (site->state & event->motion.state & site->start_button_mask)
3824 /* FIXME: This is really broken and can leave us
3830 if (site->state & event->motion.state &
3831 GDK_BUTTON1_MASK << (i - 1))
3835 if (gtk_drag_check_threshold (widget, site->x, site->y,
3836 event->motion.x, event->motion.y))
3839 gtk_drag_begin_internal (widget, site, site->target_list,
3848 default: /* hit for 2/3BUTTON_PRESS */
3856 gtk_drag_source_site_destroy (gpointer data)
3858 GtkDragSourceSite *site = data;
3860 if (site->target_list)
3861 gtk_target_list_unref (site->target_list);
3863 gtk_drag_source_unset_icon (site);
3864 g_slice_free (GtkDragSourceSite, site);
3868 gtk_drag_selection_get (GtkWidget *widget,
3869 GtkSelectionData *selection_data,
3874 GtkDragSourceInfo *info = data;
3875 static GdkAtom null_atom = GDK_NONE;
3879 null_atom = gdk_atom_intern_static_string ("NULL");
3884 g_signal_emit_by_name (info->widget,
3887 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3889 case TARGET_MOTIF_SUCCESS:
3890 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3891 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3893 case TARGET_MOTIF_FAILURE:
3894 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3895 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3898 if (info->proxy_dest)
3900 /* This is sort of dangerous and needs to be thought
3903 info->proxy_dest->proxy_data = selection_data;
3904 gtk_drag_get_data (info->widget,
3905 info->proxy_dest->context,
3906 gtk_selection_data_get_target (selection_data),
3909 info->proxy_dest->proxy_data = NULL;
3913 if (gtk_target_list_find (info->target_list,
3914 gtk_selection_data_get_target (selection_data),
3917 g_signal_emit_by_name (info->widget, "drag-data-get",
3929 gtk_drag_anim_timeout (gpointer data)
3932 GtkDragSourceInfo *info;
3939 if (anim->step == anim->n_steps)
3941 gtk_drag_source_info_destroy (anim->info);
3942 g_slice_free (GtkDragAnim, anim);
3948 x = (info->start_x * (anim->step + 1) +
3949 info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3950 y = (info->start_y * (anim->step + 1) +
3951 info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3952 if (info->icon_window)
3954 GtkWidget *icon_window;
3957 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
3958 gtk_window_move (GTK_WINDOW (icon_window),
3972 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3974 if (info->icon_window)
3976 gtk_widget_hide (info->icon_window);
3977 if (info->destroy_icon)
3978 gtk_widget_destroy (info->icon_window);
3980 if (info->fallback_icon)
3982 gtk_widget_destroy (info->fallback_icon);
3983 info->fallback_icon = NULL;
3986 g_object_unref (info->icon_window);
3987 info->icon_window = NULL;
3992 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3996 for (i = 0; i < G_N_ELEMENTS (drag_cursors); i++)
3998 if (info->drag_cursors[i] != NULL)
4000 g_object_unref (info->drag_cursors[i]);
4001 info->drag_cursors[i] = NULL;
4005 gtk_drag_remove_icon (info);
4007 if (info->icon_pixbuf)
4009 g_object_unref (info->icon_pixbuf);
4010 info->icon_pixbuf = NULL;
4013 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4014 gtk_drag_grab_broken_event_cb,
4016 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4017 gtk_drag_grab_notify_cb,
4019 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4020 gtk_drag_button_release_cb,
4022 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4025 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4028 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4029 gtk_drag_selection_get,
4032 if (!info->proxy_dest)
4033 g_signal_emit_by_name (info->widget, "drag-end", info->context);
4036 g_object_unref (info->widget);
4038 gtk_selection_remove_all (info->ipc_widget);
4039 g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), NULL);
4040 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
4041 gtk_drag_release_ipc_widget (info->ipc_widget);
4043 gtk_target_list_unref (info->target_list);
4045 gtk_drag_clear_source_info (info->context);
4046 g_object_unref (info->context);
4048 if (info->drop_timeout)
4049 g_source_remove (info->drop_timeout);
4051 if (info->update_idle)
4052 g_source_remove (info->update_idle);
4058 gtk_drag_update_idle (gpointer data)
4060 GtkDragSourceInfo *info = data;
4061 GdkWindow *dest_window;
4062 GdkDragProtocol protocol;
4065 GdkDragAction action;
4066 GdkDragAction possible_actions;
4069 info->update_idle = 0;
4071 if (info->last_event)
4073 time = gtk_drag_get_event_time (info->last_event);
4074 gtk_drag_get_event_actions (info->last_event,
4076 info->possible_actions,
4077 &action, &possible_actions);
4078 gtk_drag_update_icon (info);
4079 gdk_drag_find_window_for_screen (info->context,
4080 info->icon_window ? gtk_widget_get_window (info->icon_window) : NULL,
4081 info->cur_screen, info->cur_x, info->cur_y,
4082 &dest_window, &protocol);
4084 if (!gdk_drag_motion (info->context, dest_window, protocol,
4085 info->cur_x, info->cur_y, action,
4089 gdk_event_free ((GdkEvent *)info->last_event);
4090 info->last_event = NULL;
4094 g_object_unref (dest_window);
4096 selection = gdk_drag_get_selection (info->context);
4098 gtk_drag_source_check_selection (info, selection, time);
4106 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
4108 /* We use an idle lower than GDK_PRIORITY_REDRAW so that exposes
4109 * from the last move can catch up before we move again.
4111 if (!info->update_idle)
4112 info->update_idle = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW + 5,
4113 gtk_drag_update_idle,
4120 * @info: DragSourceInfo for the drag
4121 * @screen: new screen
4122 * @x_root: new X position
4123 * @y_root: new y position
4124 * @event: event received requiring update
4126 * Updates the status of the drag; called when the
4127 * cursor moves or the modifier changes
4130 gtk_drag_update (GtkDragSourceInfo *info,
4136 info->cur_screen = screen;
4137 info->cur_x = x_root;
4138 info->cur_y = y_root;
4139 if (info->last_event)
4141 gdk_event_free ((GdkEvent *)info->last_event);
4142 info->last_event = NULL;
4145 info->last_event = gdk_event_copy ((GdkEvent *)event);
4147 gtk_drag_add_update_idle (info);
4150 /*************************************************************
4152 * Called when the user finishes to drag, either by
4153 * releasing the mouse, or by pressing Esc.
4155 * info: Source info for the drag
4156 * time: Timestamp for ending the drag
4158 *************************************************************/
4161 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
4163 GtkWidget *source_widget = info->widget;
4164 GdkDevice *pointer, *keyboard;
4166 pointer = gdk_drag_context_get_device (info->context);
4167 keyboard = gdk_device_get_associated_device (pointer);
4169 /* Prevent ungrab before grab (see bug 623865) */
4170 if (info->grab_time == GDK_CURRENT_TIME)
4171 time = GDK_CURRENT_TIME;
4173 if (info->update_idle)
4175 g_source_remove (info->update_idle);
4176 info->update_idle = 0;
4179 if (info->last_event)
4181 gdk_event_free (info->last_event);
4182 info->last_event = NULL;
4185 info->have_grab = FALSE;
4187 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4188 gtk_drag_grab_broken_event_cb,
4190 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4191 gtk_drag_grab_notify_cb,
4193 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4194 gtk_drag_button_release_cb,
4196 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4199 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4203 gdk_device_ungrab (pointer, time);
4204 ungrab_dnd_keys (info->ipc_widget, keyboard, time);
4205 gtk_device_grab_remove (info->ipc_widget, pointer);
4207 if (gtk_widget_get_realized (source_widget))
4209 GdkEvent *send_event;
4211 /* Send on a release pair to the original widget to convince it
4212 * to release its grab. We need to call gtk_propagate_event()
4213 * here, instead of gtk_widget_event() because widget like
4214 * GtkList may expect propagation.
4217 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
4218 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
4219 send_event->button.send_event = TRUE;
4220 send_event->button.time = time;
4221 send_event->button.x = 0;
4222 send_event->button.y = 0;
4223 send_event->button.axes = NULL;
4224 send_event->button.state = 0;
4225 send_event->button.button = info->button;
4226 send_event->button.device = pointer;
4227 send_event->button.x_root = 0;
4228 send_event->button.y_root = 0;
4230 gtk_propagate_event (source_widget, send_event);
4231 gdk_event_free (send_event);
4235 /*************************************************************
4237 * Called on cancellation of a drag, either by the user
4238 * or programmatically.
4240 * info: Source info for the drag
4241 * time: Timestamp for ending the drag
4243 *************************************************************/
4246 gtk_drag_cancel (GtkDragSourceInfo *info, GtkDragResult result, guint32 time)
4248 gtk_drag_end (info, time);
4249 gdk_drag_abort (info->context, time);
4250 gtk_drag_drop_finished (info, result, time);
4253 /*************************************************************
4254 * gtk_drag_motion_cb:
4255 * "motion-notify-event" callback during drag.
4259 *************************************************************/
4262 gtk_drag_motion_cb (GtkWidget *widget,
4263 GdkEventMotion *event,
4266 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4268 gint x_root, y_root;
4272 gdk_device_get_position (event->device, &screen, &x_root, &y_root);
4273 event->x_root = x_root;
4274 event->y_root = y_root;
4277 screen = gdk_event_get_screen ((GdkEvent *)event);
4279 x_root = (gint)(event->x_root + 0.5);
4280 y_root = (gint)(event->y_root + 0.5);
4281 gtk_drag_update (info, screen, x_root, y_root, (GdkEvent *) event);
4286 /*************************************************************
4288 * "key-press/release-event" callback during drag.
4292 *************************************************************/
4295 #define SMALL_STEP 1
4298 gtk_drag_key_cb (GtkWidget *widget,
4302 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4303 GdkModifierType state;
4304 GdkWindow *root_window;
4309 state = event->state & gtk_accelerator_get_default_mod_mask ();
4310 pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
4312 if (event->type == GDK_KEY_PRESS)
4314 switch (event->keyval)
4316 case GDK_KEY_Escape:
4317 gtk_drag_cancel (info, GTK_DRAG_RESULT_USER_CANCELLED, event->time);
4321 case GDK_KEY_Return:
4322 case GDK_KEY_ISO_Enter:
4323 case GDK_KEY_KP_Enter:
4324 case GDK_KEY_KP_Space:
4325 if ((gdk_drag_context_get_selected_action (info->context) != 0) &&
4326 (gdk_drag_context_get_dest_window (info->context) != NULL))
4328 gtk_drag_end (info, event->time);
4329 gtk_drag_drop (info, event->time);
4333 gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4340 dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4344 case GDK_KEY_KP_Down:
4345 dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4349 case GDK_KEY_KP_Left:
4350 dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4354 case GDK_KEY_KP_Right:
4355 dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4360 /* Now send a "motion" so that the modifier state is updated */
4362 /* The state is not yet updated in the event, so we need
4363 * to query it here. We could use XGetModifierMapping, but
4364 * that would be overkill.
4366 root_window = gtk_widget_get_root_window (widget);
4367 gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
4368 event->state = state;
4370 if (dx != 0 || dy != 0)
4374 gdk_device_warp (pointer,
4375 gtk_widget_get_screen (widget),
4376 info->cur_x, info->cur_y);
4379 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
4385 gtk_drag_grab_broken_event_cb (GtkWidget *widget,
4386 GdkEventGrabBroken *event,
4389 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4391 /* Don't cancel if we break the implicit grab from the initial button_press.
4392 * Also, don't cancel if we re-grab on the widget or on our IPC window, for
4393 * example, when changing the drag cursor.
4396 || event->grab_window == gtk_widget_get_window (info->widget)
4397 || event->grab_window == gtk_widget_get_window (info->ipc_widget))
4400 gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4405 gtk_drag_grab_notify_cb (GtkWidget *widget,
4406 gboolean was_grabbed,
4409 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4412 pointer = gdk_drag_context_get_device (info->context);
4414 if (gtk_widget_device_is_shadowed (widget, pointer))
4416 /* We have to block callbacks to avoid recursion here, because
4417 gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
4418 g_signal_handlers_block_by_func (widget, gtk_drag_grab_notify_cb, data);
4419 gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4420 g_signal_handlers_unblock_by_func (widget, gtk_drag_grab_notify_cb, data);
4425 /*************************************************************
4426 * gtk_drag_button_release_cb:
4427 * "button-release-event" callback during drag.
4431 *************************************************************/
4434 gtk_drag_button_release_cb (GtkWidget *widget,
4435 GdkEventButton *event,
4438 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4440 if (event->button != info->button)
4443 if ((gdk_drag_context_get_selected_action (info->context) != 0) &&
4444 (gdk_drag_context_get_dest_window (info->context) != NULL))
4446 gtk_drag_end (info, event->time);
4447 gtk_drag_drop (info, event->time);
4451 gtk_drag_cancel (info, GTK_DRAG_RESULT_NO_TARGET, event->time);
4458 gtk_drag_abort_timeout (gpointer data)
4460 GtkDragSourceInfo *info = data;
4461 guint32 time = GDK_CURRENT_TIME;
4463 if (info->proxy_dest)
4464 time = info->proxy_dest->proxy_drop_time;
4466 info->drop_timeout = 0;
4467 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED, time);
4473 * gtk_drag_check_threshold: (method)
4474 * @widget: a #GtkWidget
4475 * @start_x: X coordinate of start of drag
4476 * @start_y: Y coordinate of start of drag
4477 * @current_x: current X coordinate
4478 * @current_y: current Y coordinate
4480 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
4481 * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
4482 * should trigger the beginning of a drag-and-drop operation.
4484 * Return Value: %TRUE if the drag threshold has been passed.
4487 gtk_drag_check_threshold (GtkWidget *widget,
4493 gint drag_threshold;
4495 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
4497 g_object_get (gtk_widget_get_settings (widget),
4498 "gtk-dnd-drag-threshold", &drag_threshold,
4501 return (ABS (current_x - start_x) > drag_threshold ||
4502 ABS (current_y - start_y) > drag_threshold);