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/.
32 #include "gdkconfig.h"
34 #include "gdk/gdkkeysyms.h"
36 #ifdef GDK_WINDOWING_X11
38 #include <X11/keysym.h>
39 #include "gdk/x11/gdkx.h"
43 #include "gtkiconfactory.h"
44 #include "gtkicontheme.h"
46 #include "gtkinvisible.h"
50 #include "gtktooltip.h"
51 #include "gtkwindow.h"
53 #include "gtkdndcursors.h"
55 static GSList *source_widgets = NULL;
57 typedef struct _GtkDragSourceSite GtkDragSourceSite;
58 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
59 typedef struct _GtkDragDestSite GtkDragDestSite;
60 typedef struct _GtkDragDestInfo GtkDragDestInfo;
61 typedef struct _GtkDragAnim GtkDragAnim;
71 struct _GtkDragSourceSite
73 GdkModifierType start_button_mask;
74 GtkTargetList *target_list; /* Targets for drag data */
75 GdkDragAction actions; /* Possible actions */
78 GtkImageType icon_type;
81 GtkImagePixmapData pixmap;
82 GtkImagePixbufData pixbuf;
83 GtkImageStockData stock;
84 GtkImageIconNameData name;
88 GdkColormap *colormap; /* Colormap for drag icon */
90 /* Stored button press information to detect drag beginning */
95 struct _GtkDragSourceInfo
98 GtkTargetList *target_list; /* Targets for drag data */
99 GdkDragAction possible_actions; /* Actions allowed by source */
100 GdkDragContext *context; /* drag context */
101 GtkWidget *icon_window; /* Window for drag */
102 GtkWidget *fallback_icon; /* Window for drag used on other screens */
103 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
104 GdkCursor *cursor; /* Cursor for drag */
105 gint hot_x, hot_y; /* Hot spot for drag */
106 gint button; /* mouse button starting drag */
108 GtkDragStatus status; /* drag status */
109 GdkEvent *last_event; /* pending event */
111 gint start_x, start_y; /* Initial position */
112 gint cur_x, cur_y; /* Current Position */
113 GdkScreen *cur_screen; /* Current screen for pointer */
115 guint32 grab_time; /* timestamp for initial grab */
116 GList *selections; /* selections we've claimed */
118 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
120 guint update_idle; /* Idle function to update the drag */
121 guint drop_timeout; /* Timeout for aborting drop */
122 guint destroy_icon : 1; /* If true, destroy icon_window
124 guint have_grab : 1; /* Do we still have the pointer grab
126 GdkPixbuf *icon_pixbuf;
127 GdkCursor *drag_cursors[6];
130 struct _GtkDragDestSite
132 GtkDestDefaults flags;
133 GtkTargetList *target_list;
134 GdkDragAction actions;
135 GdkWindow *proxy_window;
136 GdkDragProtocol proxy_protocol;
138 guint proxy_coords : 1;
140 guint track_motion : 1;
143 struct _GtkDragDestInfo
145 GtkWidget *widget; /* Widget in which drag is in */
146 GdkDragContext *context; /* Drag context */
147 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
148 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
149 guint dropped : 1; /* Set after we receive a drop */
150 guint32 proxy_drop_time; /* Timestamp for proxied drop */
151 guint proxy_drop_wait : 1; /* Set if we are waiting for a
152 * status reply before sending
155 gint drop_x, drop_y; /* Position of drop */
158 #define DROP_ABORT_TIME 300000
160 #define ANIM_STEP_TIME 50
161 #define ANIM_STEP_LENGTH 50
162 #define ANIM_MIN_STEPS 5
163 #define ANIM_MAX_STEPS 10
167 GtkDragSourceInfo *info;
172 typedef gboolean (* GtkDragDestCallback) (GtkWidget *widget,
173 GdkDragContext *context,
178 /* Enumeration for some targets we handle internally */
181 TARGET_MOTIF_SUCCESS = 0x40000000,
182 TARGET_MOTIF_FAILURE,
188 static GdkPixmap *default_icon_pixmap = NULL;
189 static GdkPixmap *default_icon_mask = NULL;
190 static GdkColormap *default_icon_colormap = NULL;
191 static gint default_icon_hot_x;
192 static gint default_icon_hot_y;
194 /* Forward declarations */
195 static void gtk_drag_get_event_actions (GdkEvent *event,
197 GdkDragAction actions,
198 GdkDragAction *suggested_action,
199 GdkDragAction *possible_actions);
200 static GdkCursor * gtk_drag_get_cursor (GdkDisplay *display,
201 GdkDragAction action,
202 GtkDragSourceInfo *info);
203 static void gtk_drag_update_cursor (GtkDragSourceInfo *info);
204 static GtkWidget *gtk_drag_get_ipc_widget (GtkWidget *widget);
205 static GtkWidget *gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen);
206 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
208 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
209 GdkEventExpose *event,
212 static void gtk_drag_selection_received (GtkWidget *widget,
213 GtkSelectionData *selection_data,
216 static gboolean gtk_drag_find_widget (GtkWidget *widget,
217 GdkDragContext *context,
218 GtkDragDestInfo *info,
222 GtkDragDestCallback callback);
223 static void gtk_drag_proxy_begin (GtkWidget *widget,
224 GtkDragDestInfo *dest_info,
226 static void gtk_drag_dest_realized (GtkWidget *widget);
227 static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
228 GtkWidget *previous_toplevel);
229 static void gtk_drag_dest_site_destroy (gpointer data);
230 static void gtk_drag_dest_leave (GtkWidget *widget,
231 GdkDragContext *context,
233 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
234 GdkDragContext *context,
238 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
239 GdkDragContext *context,
244 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
246 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
248 static void gtk_drag_clear_source_info (GdkDragContext *context);
250 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
253 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
255 static void gtk_drag_drop (GtkDragSourceInfo *info,
257 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
258 GtkDragResult result,
260 static void gtk_drag_cancel (GtkDragSourceInfo *info,
261 GtkDragResult result,
264 static gboolean gtk_drag_source_event_cb (GtkWidget *widget,
267 static void gtk_drag_source_site_destroy (gpointer data);
268 static void gtk_drag_selection_get (GtkWidget *widget,
269 GtkSelectionData *selection_data,
273 static gboolean gtk_drag_anim_timeout (gpointer data);
274 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
275 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
276 static void gtk_drag_add_update_idle (GtkDragSourceInfo *info);
278 static void gtk_drag_update (GtkDragSourceInfo *info,
283 static gboolean gtk_drag_motion_cb (GtkWidget *widget,
284 GdkEventMotion *event,
286 static gboolean gtk_drag_key_cb (GtkWidget *widget,
289 static gboolean gtk_drag_grab_broken_event_cb (GtkWidget *widget,
290 GdkEventGrabBroken *event,
292 static void gtk_drag_grab_notify_cb (GtkWidget *widget,
293 gboolean was_grabbed,
295 static gboolean gtk_drag_button_release_cb (GtkWidget *widget,
296 GdkEventButton *event,
298 static gboolean gtk_drag_abort_timeout (gpointer data);
300 static void set_icon_stock_pixbuf (GdkDragContext *context,
301 const gchar *stock_id,
305 gboolean force_window);
307 /************************
308 * Cursor and Icon data *
309 ************************/
312 GdkDragAction action;
318 { GDK_ACTION_DEFAULT, NULL },
319 { GDK_ACTION_ASK, "dnd-ask", dnd_cursor_ask, NULL, NULL },
320 { GDK_ACTION_COPY, "dnd-copy", dnd_cursor_copy, NULL, NULL },
321 { GDK_ACTION_MOVE, "dnd-move", dnd_cursor_move, NULL, NULL },
322 { GDK_ACTION_LINK, "dnd-link", dnd_cursor_link, NULL, NULL },
323 { 0 , "dnd-none", dnd_cursor_none, NULL, NULL },
326 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
328 /*********************
329 * Utility functions *
330 *********************/
333 set_can_change_screen (GtkWidget *widget,
334 gboolean can_change_screen)
336 can_change_screen = can_change_screen != FALSE;
338 g_object_set_data (G_OBJECT (widget), I_("gtk-dnd-can-change-screen"),
339 GUINT_TO_POINTER (can_change_screen));
343 get_can_change_screen (GtkWidget *widget)
345 return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
350 gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen)
353 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
354 "gtk-dnd-ipc-widgets");
358 GSList *tmp = drag_widgets;
359 result = drag_widgets->data;
360 drag_widgets = drag_widgets->next;
361 g_object_set_data (G_OBJECT (screen),
362 I_("gtk-dnd-ipc-widgets"),
364 g_slist_free_1 (tmp);
368 result = gtk_window_new (GTK_WINDOW_POPUP);
369 gtk_window_set_screen (GTK_WINDOW (result), screen);
370 gtk_window_resize (GTK_WINDOW (result), 1, 1);
371 gtk_window_move (GTK_WINDOW (result), -100, -100);
372 gtk_widget_show (result);
379 gtk_drag_get_ipc_widget (GtkWidget *widget)
384 result = gtk_drag_get_ipc_widget_for_screen (gtk_widget_get_screen (widget));
386 toplevel = gtk_widget_get_toplevel (widget);
388 if (GTK_IS_WINDOW (toplevel))
390 if (GTK_WINDOW (toplevel)->group)
391 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
392 GTK_WINDOW (result));
398 /* FIXME: modifying the XEvent window as in root_key_filter() isn't
399 * going to work with XGE/XI2, since the actual event to handle would
400 * be allocated/freed before GDK gets to translate the event.
401 * Active grabs on the keyboard are used instead at the moment...
403 #if defined (GDK_WINDOWING_X11) && !defined (XINPUT_2)
406 * We want to handle a handful of keys during DND, e.g. Escape to abort.
407 * Grabbing the keyboard has the unfortunate side-effect of preventing
408 * useful things such as using Alt-Tab to cycle between windows or
409 * switching workspaces. Therefore, we just grab the few keys we are
410 * interested in. Note that we need to put the grabs on the root window
411 * in order for them to still work when the focus is moved to another
414 * GDK needs a little help to successfully deliver root key events...
417 static GdkFilterReturn
418 root_key_filter (GdkXEvent *xevent,
422 XEvent *ev = (XEvent *) xevent;
424 if ((ev->type == KeyPress || ev->type == KeyRelease) &&
425 ev->xkey.root == ev->xkey.window)
426 ev->xkey.window = (Window)data;
428 return GDK_FILTER_CONTINUE;
436 static GrabKey grab_keys[] = {
445 { XK_Down, Mod1Mask },
447 { XK_Left, Mod1Mask },
449 { XK_Right, Mod1Mask },
451 { XK_KP_Up, Mod1Mask },
453 { XK_KP_Down, Mod1Mask },
455 { XK_KP_Left, Mod1Mask },
457 { XK_KP_Right, Mod1Mask }
461 grab_dnd_keys (GtkWidget *widget,
466 GdkWindow *window, *root;
469 window = widget->window;
470 root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
472 gdk_error_trap_push ();
474 for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
476 keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
477 XGrabKey (GDK_WINDOW_XDISPLAY (window),
478 keycode, grab_keys[i].modifiers,
479 GDK_WINDOW_XID (root),
486 gdk_error_trap_pop ();
488 gdk_window_add_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
492 ungrab_dnd_keys (GtkWidget *widget,
497 GdkWindow *window, *root;
500 window = widget->window;
501 root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
503 gdk_window_remove_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
505 gdk_error_trap_push ();
507 for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
509 keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
510 XUngrabKey (GDK_WINDOW_XDISPLAY (window),
511 keycode, grab_keys[i].modifiers,
512 GDK_WINDOW_XID (root));
516 gdk_error_trap_pop ();
519 #else /* GDK_WINDOWING_X11 && !XINPUT_2 */
522 grab_dnd_keys (GtkWidget *widget,
526 gdk_device_grab (device, widget->window,
527 GDK_OWNERSHIP_APPLICATION, FALSE,
528 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
533 ungrab_dnd_keys (GtkWidget *widget,
537 gdk_device_ungrab (device, time);
540 #endif /* GDK_WINDOWING_X11 */
543 /***************************************************************
544 * gtk_drag_release_ipc_widget:
545 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
547 * widget: the widget to release.
549 ***************************************************************/
552 gtk_drag_release_ipc_widget (GtkWidget *widget)
554 GtkWindow *window = GTK_WINDOW (widget);
555 GdkScreen *screen = gtk_widget_get_screen (widget);
556 GdkDragContext *context = g_object_get_data (G_OBJECT (widget), "drag-context");
557 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
558 "gtk-dnd-ipc-widgets");
559 GdkDevice *pointer, *keyboard;
563 pointer = gdk_drag_context_get_device (context);
564 keyboard = gdk_device_get_associated_device (pointer);
567 ungrab_dnd_keys (widget, keyboard, GDK_CURRENT_TIME);
571 gtk_window_group_remove_window (window->group, window);
572 drag_widgets = g_slist_prepend (drag_widgets, widget);
573 g_object_set_data (G_OBJECT (screen),
574 I_("gtk-dnd-ipc-widgets"),
579 gtk_drag_get_event_time (GdkEvent *event)
581 guint32 tm = GDK_CURRENT_TIME;
586 case GDK_MOTION_NOTIFY:
587 tm = event->motion.time; break;
588 case GDK_BUTTON_PRESS:
589 case GDK_2BUTTON_PRESS:
590 case GDK_3BUTTON_PRESS:
591 case GDK_BUTTON_RELEASE:
592 tm = event->button.time; break;
594 case GDK_KEY_RELEASE:
595 tm = event->key.time; break;
596 case GDK_ENTER_NOTIFY:
597 case GDK_LEAVE_NOTIFY:
598 tm = event->crossing.time; break;
599 case GDK_PROPERTY_NOTIFY:
600 tm = event->property.time; break;
601 case GDK_SELECTION_CLEAR:
602 case GDK_SELECTION_REQUEST:
603 case GDK_SELECTION_NOTIFY:
604 tm = event->selection.time; break;
605 case GDK_PROXIMITY_IN:
606 case GDK_PROXIMITY_OUT:
607 tm = event->proximity.time; break;
608 default: /* use current time */
616 gtk_drag_get_event_actions (GdkEvent *event,
618 GdkDragAction actions,
619 GdkDragAction *suggested_action,
620 GdkDragAction *possible_actions)
622 *suggested_action = 0;
623 *possible_actions = 0;
627 GdkModifierType state = 0;
631 case GDK_MOTION_NOTIFY:
632 state = event->motion.state;
634 case GDK_BUTTON_PRESS:
635 case GDK_2BUTTON_PRESS:
636 case GDK_3BUTTON_PRESS:
637 case GDK_BUTTON_RELEASE:
638 state = event->button.state;
641 case GDK_KEY_RELEASE:
642 state = event->key.state;
644 case GDK_ENTER_NOTIFY:
645 case GDK_LEAVE_NOTIFY:
646 state = event->crossing.state;
652 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
654 *suggested_action = GDK_ACTION_ASK;
655 *possible_actions = actions;
657 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
659 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
661 if (actions & GDK_ACTION_LINK)
663 *suggested_action = GDK_ACTION_LINK;
664 *possible_actions = GDK_ACTION_LINK;
667 else if (state & GDK_CONTROL_MASK)
669 if (actions & GDK_ACTION_COPY)
671 *suggested_action = GDK_ACTION_COPY;
672 *possible_actions = GDK_ACTION_COPY;
678 if (actions & GDK_ACTION_MOVE)
680 *suggested_action = GDK_ACTION_MOVE;
681 *possible_actions = GDK_ACTION_MOVE;
688 *possible_actions = actions;
690 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
691 *suggested_action = GDK_ACTION_ASK;
692 else if (actions & GDK_ACTION_COPY)
693 *suggested_action = GDK_ACTION_COPY;
694 else if (actions & GDK_ACTION_MOVE)
695 *suggested_action = GDK_ACTION_MOVE;
696 else if (actions & GDK_ACTION_LINK)
697 *suggested_action = GDK_ACTION_LINK;
702 *possible_actions = actions;
704 if (actions & GDK_ACTION_COPY)
705 *suggested_action = GDK_ACTION_COPY;
706 else if (actions & GDK_ACTION_MOVE)
707 *suggested_action = GDK_ACTION_MOVE;
708 else if (actions & GDK_ACTION_LINK)
709 *suggested_action = GDK_ACTION_LINK;
714 gtk_drag_can_use_rgba_cursor (GdkDisplay *display,
718 guint max_width, max_height;
720 if (!gdk_display_supports_cursor_color (display))
723 if (!gdk_display_supports_cursor_alpha (display))
726 gdk_display_get_maximal_cursor_size (display,
729 if (width > max_width || height > max_height)
731 /* can't use rgba cursor (too large) */
739 gtk_drag_get_cursor (GdkDisplay *display,
740 GdkDragAction action,
741 GtkDragSourceInfo *info)
745 /* reconstruct the cursors for each new drag (thus !info),
746 * to catch cursor theme changes
750 for (i = 0 ; i < n_drag_cursors - 1; i++)
751 if (drag_cursors[i].cursor != NULL)
753 gdk_cursor_unref (drag_cursors[i].cursor);
754 drag_cursors[i].cursor = NULL;
758 for (i = 0 ; i < n_drag_cursors - 1; i++)
759 if (drag_cursors[i].action == action)
762 if (drag_cursors[i].pixbuf == NULL)
763 drag_cursors[i].pixbuf =
764 gdk_pixbuf_new_from_inline (-1, drag_cursors[i].data, FALSE, NULL);
766 if (drag_cursors[i].cursor != NULL)
768 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
770 gdk_cursor_unref (drag_cursors[i].cursor);
771 drag_cursors[i].cursor = NULL;
775 if (drag_cursors[i].cursor == NULL)
776 drag_cursors[i].cursor = gdk_cursor_new_from_name (display, drag_cursors[i].name);
778 if (drag_cursors[i].cursor == NULL)
779 drag_cursors[i].cursor = gdk_cursor_new_from_pixbuf (display, drag_cursors[i].pixbuf, 0, 0);
781 if (info && info->icon_pixbuf)
783 gint cursor_width, cursor_height;
784 gint icon_width, icon_height;
786 GdkPixbuf *cursor_pixbuf, *pixbuf;
788 gint icon_x, icon_y, ref_x, ref_y;
790 if (info->drag_cursors[i] != NULL)
792 if (display == gdk_cursor_get_display (info->drag_cursors[i]))
793 return info->drag_cursors[i];
795 gdk_cursor_unref (info->drag_cursors[i]);
796 info->drag_cursors[i] = NULL;
799 icon_x = info->hot_x;
800 icon_y = info->hot_y;
801 icon_width = gdk_pixbuf_get_width (info->icon_pixbuf);
802 icon_height = gdk_pixbuf_get_height (info->icon_pixbuf);
805 cursor_pixbuf = gdk_cursor_get_image (drag_cursors[i].cursor);
807 cursor_pixbuf = g_object_ref (drag_cursors[i].pixbuf);
810 if (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"))
811 hot_x = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"));
813 if (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"))
814 hot_y = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"));
817 /* The code below is an attempt to let cursor themes
818 * determine the attachment of the icon to enable things
819 * like the following:
827 * It does not work since Xcursor doesn't allow to attach
828 * any additional information to cursors in a retrievable
829 * way (there are comments, but no way to get at them
830 * short of searching for the actual cursor file).
831 * If this code ever gets used, the icon_window placement
832 * must be changed to recognize these placement options
833 * as well. Note that this code ignores info->hot_x/y.
835 for (j = 0; j < 10; j++)
840 GtkAnchorType icon_anchor;
842 g_snprintf (key, 32, "comment%d", j);
843 opt = gdk_pixbuf_get_option (cursor_pixbuf, key);
844 if (opt && g_str_has_prefix ("icon-attach:", opt))
846 toks = g_strsplit (opt + strlen ("icon-attach:"), "'", -1);
847 if (g_strv_length (toks) != 3)
852 icon_anchor = atoi (toks[0]);
853 icon_x = atoi (toks[1]);
854 icon_y = atoi (toks[2]);
858 case GTK_ANCHOR_NORTH:
859 case GTK_ANCHOR_CENTER:
860 case GTK_ANCHOR_SOUTH:
861 icon_x += icon_width / 2;
863 case GTK_ANCHOR_NORTH_EAST:
864 case GTK_ANCHOR_EAST:
865 case GTK_ANCHOR_SOUTH_EAST:
866 icon_x += icon_width;
873 case GTK_ANCHOR_WEST:
874 case GTK_ANCHOR_CENTER:
875 case GTK_ANCHOR_EAST:
876 icon_y += icon_height / 2;
878 case GTK_ANCHOR_SOUTH_WEST:
879 case GTK_ANCHOR_SOUTH:
880 case GTK_ANCHOR_SOUTH_EAST:
881 icon_x += icon_height;
893 cursor_width = gdk_pixbuf_get_width (cursor_pixbuf);
894 cursor_height = gdk_pixbuf_get_height (cursor_pixbuf);
896 ref_x = MAX (hot_x, icon_x);
897 ref_y = MAX (hot_y, icon_y);
898 width = ref_x + MAX (cursor_width - hot_x, icon_width - icon_x);
899 height = ref_y + MAX (cursor_height - hot_y, icon_height - icon_y);
901 if (gtk_drag_can_use_rgba_cursor (display, width, height))
903 /* Composite cursor and icon so that both hotspots
904 * end up at (ref_x, ref_y)
906 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
909 gdk_pixbuf_fill (pixbuf, 0xff000000);
911 gdk_pixbuf_composite (info->icon_pixbuf, pixbuf,
912 ref_x - icon_x, ref_y - icon_y,
913 icon_width, icon_height,
914 ref_x - icon_x, ref_y - icon_y,
916 GDK_INTERP_BILINEAR, 255);
918 gdk_pixbuf_composite (cursor_pixbuf, pixbuf,
919 ref_x - hot_x, ref_y - hot_y,
920 cursor_width, cursor_height,
921 ref_x - hot_x, ref_y - hot_y,
923 GDK_INTERP_BILINEAR, 255);
925 info->drag_cursors[i] =
926 gdk_cursor_new_from_pixbuf (display, pixbuf, ref_x, ref_y);
928 g_object_unref (pixbuf);
931 g_object_unref (cursor_pixbuf);
933 if (info->drag_cursors[i] != NULL)
934 return info->drag_cursors[i];
937 return drag_cursors[i].cursor;
941 gtk_drag_update_cursor (GtkDragSourceInfo *info)
946 if (!info->have_grab)
949 for (i = 0 ; i < n_drag_cursors - 1; i++)
950 if (info->cursor == drag_cursors[i].cursor ||
951 info->cursor == info->drag_cursors[i])
954 if (i == n_drag_cursors)
957 cursor = gtk_drag_get_cursor (gdk_cursor_get_display (info->cursor),
958 drag_cursors[i].action, info);
960 if (cursor != info->cursor)
964 pointer = gdk_drag_context_get_device (info->context);
965 gdk_device_grab (pointer, info->ipc_widget->window,
966 GDK_OWNERSHIP_APPLICATION, FALSE,
967 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
968 cursor, info->grab_time);
969 info->cursor = cursor;
973 /********************
975 ********************/
977 /*************************************************************
979 * Get the data for a drag or drop
981 * context - drag context
982 * target - format to retrieve the data in.
983 * time - timestamp of triggering event.
986 *************************************************************/
989 gtk_drag_get_data (GtkWidget *widget,
990 GdkDragContext *context,
994 GtkWidget *selection_widget;
996 g_return_if_fail (GTK_IS_WIDGET (widget));
997 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
998 g_return_if_fail (!context->is_source);
1000 selection_widget = gtk_drag_get_ipc_widget (widget);
1002 g_object_ref (context);
1003 g_object_ref (widget);
1005 g_signal_connect (selection_widget, "selection-received",
1006 G_CALLBACK (gtk_drag_selection_received), widget);
1008 g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1010 gtk_selection_convert (selection_widget,
1011 gdk_drag_get_selection (context),
1017 /*************************************************************
1018 * gtk_drag_get_source_widget:
1019 * Get the widget the was the source of this drag, if
1020 * the drag originated from this application.
1022 * context: The drag context for this drag
1024 * The source widget, or NULL if the drag originated from
1025 * a different application.
1026 *************************************************************/
1029 gtk_drag_get_source_widget (GdkDragContext *context)
1033 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
1034 g_return_val_if_fail (!context->is_source, NULL);
1036 tmp_list = source_widgets;
1039 GtkWidget *ipc_widget = tmp_list->data;
1041 if (ipc_widget->window == context->source_window)
1043 GtkDragSourceInfo *info;
1044 info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
1046 return info ? info->widget : NULL;
1049 tmp_list = tmp_list->next;
1055 /*************************************************************
1057 * Notify the drag source that the transfer of data
1060 * context: The drag context for this drag
1061 * success: Was the data successfully transferred?
1062 * time: The timestamp to use when notifying the destination.
1064 *************************************************************/
1067 gtk_drag_finish (GdkDragContext *context,
1072 GdkAtom target = GDK_NONE;
1074 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1075 g_return_if_fail (!context->is_source);
1079 target = gdk_atom_intern_static_string ("DELETE");
1081 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
1083 target = gdk_atom_intern_static_string (success ?
1084 "XmTRANSFER_SUCCESS" :
1085 "XmTRANSFER_FAILURE");
1088 if (target != GDK_NONE)
1090 GtkWidget *selection_widget = gtk_drag_get_ipc_widget_for_screen (gdk_drawable_get_screen (context->source_window));
1092 g_object_ref (context);
1094 g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1095 g_signal_connect (selection_widget, "selection-received",
1096 G_CALLBACK (gtk_drag_selection_received),
1099 gtk_selection_convert (selection_widget,
1100 gdk_drag_get_selection (context),
1105 if (!(success && del))
1106 gdk_drop_finish (context, success, time);
1109 /*************************************************************
1110 * gtk_drag_highlight_expose:
1111 * Callback for expose_event for highlighted widgets.
1117 *************************************************************/
1120 gtk_drag_highlight_expose (GtkWidget *widget,
1121 GdkEventExpose *event,
1124 gint x, y, width, height;
1126 if (gtk_widget_is_drawable (widget))
1130 if (!gtk_widget_get_has_window (widget))
1132 x = widget->allocation.x;
1133 y = widget->allocation.y;
1134 width = widget->allocation.width;
1135 height = widget->allocation.height;
1141 gdk_drawable_get_size (widget->window, &width, &height);
1144 gtk_paint_shadow (widget->style, widget->window,
1145 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
1146 &event->area, widget, "dnd",
1147 x, y, width, height);
1149 cr = gdk_cairo_create (widget->window);
1150 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
1151 cairo_set_line_width (cr, 1.0);
1152 cairo_rectangle (cr,
1154 width - 1, height - 1);
1162 /*************************************************************
1163 * gtk_drag_highlight:
1164 * Highlight the given widget in the default manner.
1168 *************************************************************/
1171 gtk_drag_highlight (GtkWidget *widget)
1173 g_return_if_fail (GTK_IS_WIDGET (widget));
1175 g_signal_connect_after (widget, "expose-event",
1176 G_CALLBACK (gtk_drag_highlight_expose),
1179 gtk_widget_queue_draw (widget);
1182 /*************************************************************
1183 * gtk_drag_unhighlight:
1184 * Refresh the given widget to remove the highlight.
1188 *************************************************************/
1191 gtk_drag_unhighlight (GtkWidget *widget)
1193 g_return_if_fail (GTK_IS_WIDGET (widget));
1195 g_signal_handlers_disconnect_by_func (widget,
1196 gtk_drag_highlight_expose,
1199 gtk_widget_queue_draw (widget);
1203 gtk_drag_dest_set_internal (GtkWidget *widget,
1204 GtkDragDestSite *site)
1206 GtkDragDestSite *old_site;
1208 g_return_if_fail (widget != NULL);
1210 /* HACK, do this in the destroy */
1211 old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1214 g_signal_handlers_disconnect_by_func (widget,
1215 gtk_drag_dest_realized,
1217 g_signal_handlers_disconnect_by_func (widget,
1218 gtk_drag_dest_hierarchy_changed,
1221 site->track_motion = old_site->track_motion;
1224 if (gtk_widget_get_realized (widget))
1225 gtk_drag_dest_realized (widget);
1227 g_signal_connect (widget, "realize",
1228 G_CALLBACK (gtk_drag_dest_realized), site);
1229 g_signal_connect (widget, "hierarchy-changed",
1230 G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
1232 g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
1233 site, gtk_drag_dest_site_destroy);
1237 * gtk_drag_dest_set:
1238 * @widget: a #GtkWidget
1239 * @flags: which types of default drag behavior to use
1240 * @targets: (allow-none) (array length=n_targets): a pointer to an array of #GtkTargetEntry<!-- -->s
1241 * indicating the drop types that this @widget will accept, or %NULL.
1242 * Later you can access the list with gtk_drag_dest_get_target_list()
1243 * and gtk_drag_dest_find_target().
1244 * @n_targets: the number of entries in @targets
1245 * @actions: a bitmask of possible actions for a drop onto this @widget.
1247 * Sets a widget as a potential drop destination, and adds default behaviors.
1249 * The default behaviors listed in @flags have an effect similar
1250 * to installing default handlers for the widget's drag-and-drop signals
1251 * (#GtkWidget:drag-motion, #GtkWidget:drag-drop, ...). They all exist
1252 * for convenience. When passing #GTK_DEST_DEFAULT_ALL for instance it is
1253 * sufficient to connect to the widget's #GtkWidget::drag-data-received
1254 * signal to get primitive, but consistent drag-and-drop support.
1256 * Things become more complicated when you try to preview the dragged data,
1257 * as described in the documentation for #GtkWidget:drag-motion. The default
1258 * behaviors described by @flags make some assumptions, that can conflict
1259 * with your own signal handlers. For instance #GTK_DEST_DEFAULT_DROP causes
1260 * invokations of gdk_drag_status() in the context of #GtkWidget:drag-motion,
1261 * and invokations of gtk_drag_finish() in #GtkWidget:drag-data-received.
1262 * Especially the later is dramatic, when your own #GtkWidget:drag-motion
1263 * handler calls gtk_drag_get_data() to inspect the dragged data.
1265 * There's no way to set a default action here, you can use the
1266 * #GtkWidget:drag-motion callback for that. Here's an example which selects
1267 * the action to use depending on whether the control key is pressed or not:
1270 * drag_motion (GtkWidget *widget,
1271 * GdkDragContext *context,
1276 * GdkModifierType mask;
1278 * gdk_window_get_pointer (gtk_widget_get_window (widget),
1279 * NULL, NULL, &mask);
1280 * if (mask & GDK_CONTROL_MASK)
1281 * gdk_drag_status (context, GDK_ACTION_COPY, time);
1283 * gdk_drag_status (context, GDK_ACTION_MOVE, time);
1288 gtk_drag_dest_set (GtkWidget *widget,
1289 GtkDestDefaults flags,
1290 const GtkTargetEntry *targets,
1292 GdkDragAction actions)
1294 GtkDragDestSite *site;
1296 g_return_if_fail (GTK_IS_WIDGET (widget));
1298 site = g_new (GtkDragDestSite, 1);
1300 site->flags = flags;
1301 site->have_drag = FALSE;
1303 site->target_list = gtk_target_list_new (targets, n_targets);
1305 site->target_list = NULL;
1306 site->actions = actions;
1307 site->do_proxy = FALSE;
1308 site->proxy_window = NULL;
1309 site->track_motion = FALSE;
1311 gtk_drag_dest_set_internal (widget, site);
1314 /*************************************************************
1315 * gtk_drag_dest_set_proxy:
1316 * Set up this widget to proxy drags elsewhere.
1319 * proxy_window: window to which forward drag events
1320 * protocol: Drag protocol which the dest widget accepts
1321 * use_coordinates: If true, send the same coordinates to the
1322 * destination, because it is a embedded
1325 *************************************************************/
1328 gtk_drag_dest_set_proxy (GtkWidget *widget,
1329 GdkWindow *proxy_window,
1330 GdkDragProtocol protocol,
1331 gboolean use_coordinates)
1333 GtkDragDestSite *site;
1335 g_return_if_fail (GTK_IS_WIDGET (widget));
1336 g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
1338 site = g_new (GtkDragDestSite, 1);
1341 site->have_drag = FALSE;
1342 site->target_list = NULL;
1344 site->proxy_window = proxy_window;
1346 g_object_ref (proxy_window);
1347 site->do_proxy = TRUE;
1348 site->proxy_protocol = protocol;
1349 site->proxy_coords = use_coordinates;
1350 site->track_motion = FALSE;
1352 gtk_drag_dest_set_internal (widget, site);
1355 /*************************************************************
1356 * gtk_drag_dest_unset
1357 * Unregister this widget as a drag target.
1361 *************************************************************/
1364 gtk_drag_dest_unset (GtkWidget *widget)
1366 GtkDragDestSite *old_site;
1368 g_return_if_fail (GTK_IS_WIDGET (widget));
1370 old_site = g_object_get_data (G_OBJECT (widget),
1374 g_signal_handlers_disconnect_by_func (widget,
1375 gtk_drag_dest_realized,
1377 g_signal_handlers_disconnect_by_func (widget,
1378 gtk_drag_dest_hierarchy_changed,
1382 g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
1386 * gtk_drag_dest_get_target_list:
1387 * @widget: a #GtkWidget
1389 * Returns the list of targets this widget can accept from
1392 * Return value: the #GtkTargetList, or %NULL if none
1395 gtk_drag_dest_get_target_list (GtkWidget *widget)
1397 GtkDragDestSite *site;
1399 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1401 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1403 return site ? site->target_list : NULL;
1407 * gtk_drag_dest_set_target_list:
1408 * @widget: a #GtkWidget that's a drag destination
1409 * @target_list: (allow-none): list of droppable targets, or %NULL for none
1411 * Sets the target types that this widget can accept from drag-and-drop.
1412 * The widget must first be made into a drag destination with
1413 * gtk_drag_dest_set().
1416 gtk_drag_dest_set_target_list (GtkWidget *widget,
1417 GtkTargetList *target_list)
1419 GtkDragDestSite *site;
1421 g_return_if_fail (GTK_IS_WIDGET (widget));
1423 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1427 g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1428 "to make the widget into a drag destination");
1433 gtk_target_list_ref (target_list);
1435 if (site->target_list)
1436 gtk_target_list_unref (site->target_list);
1438 site->target_list = target_list;
1442 * gtk_drag_dest_add_text_targets:
1443 * @widget: a #GtkWidget that's a drag destination
1445 * Add the text targets supported by #GtkSelection to
1446 * the target list of the drag destination. The targets
1447 * are added with @info = 0. If you need another value,
1448 * use gtk_target_list_add_text_targets() and
1449 * gtk_drag_dest_set_target_list().
1454 gtk_drag_dest_add_text_targets (GtkWidget *widget)
1456 GtkTargetList *target_list;
1458 target_list = gtk_drag_dest_get_target_list (widget);
1460 gtk_target_list_ref (target_list);
1462 target_list = gtk_target_list_new (NULL, 0);
1463 gtk_target_list_add_text_targets (target_list, 0);
1464 gtk_drag_dest_set_target_list (widget, target_list);
1465 gtk_target_list_unref (target_list);
1469 * gtk_drag_dest_add_image_targets:
1470 * @widget: a #GtkWidget that's a drag destination
1472 * Add the image targets supported by #GtkSelection to
1473 * the target list of the drag destination. The targets
1474 * are added with @info = 0. If you need another value,
1475 * use gtk_target_list_add_image_targets() and
1476 * gtk_drag_dest_set_target_list().
1481 gtk_drag_dest_add_image_targets (GtkWidget *widget)
1483 GtkTargetList *target_list;
1485 target_list = gtk_drag_dest_get_target_list (widget);
1487 gtk_target_list_ref (target_list);
1489 target_list = gtk_target_list_new (NULL, 0);
1490 gtk_target_list_add_image_targets (target_list, 0, FALSE);
1491 gtk_drag_dest_set_target_list (widget, target_list);
1492 gtk_target_list_unref (target_list);
1496 * gtk_drag_dest_add_uri_targets:
1497 * @widget: a #GtkWidget that's a drag destination
1499 * Add the URI targets supported by #GtkSelection to
1500 * the target list of the drag destination. The targets
1501 * are added with @info = 0. If you need another value,
1502 * use gtk_target_list_add_uri_targets() and
1503 * gtk_drag_dest_set_target_list().
1508 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
1510 GtkTargetList *target_list;
1512 target_list = gtk_drag_dest_get_target_list (widget);
1514 gtk_target_list_ref (target_list);
1516 target_list = gtk_target_list_new (NULL, 0);
1517 gtk_target_list_add_uri_targets (target_list, 0);
1518 gtk_drag_dest_set_target_list (widget, target_list);
1519 gtk_target_list_unref (target_list);
1523 * gtk_drag_dest_set_track_motion:
1524 * @widget: a #GtkWidget that's a drag destination
1525 * @track_motion: whether to accept all targets
1527 * Tells the widget to emit ::drag-motion and ::drag-leave
1528 * events regardless of the targets and the %GTK_DEST_DEFAULT_MOTION
1531 * This may be used when a widget wants to do generic
1532 * actions regardless of the targets that the source offers.
1537 gtk_drag_dest_set_track_motion (GtkWidget *widget,
1538 gboolean track_motion)
1540 GtkDragDestSite *site;
1542 g_return_if_fail (GTK_IS_WIDGET (widget));
1544 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1546 g_return_if_fail (site != NULL);
1548 site->track_motion = track_motion != FALSE;
1552 * gtk_drag_dest_get_track_motion:
1553 * @widget: a #GtkWidget that's a drag destination
1555 * Returns whether the widget has been configured to always
1556 * emit ::drag-motion signals.
1558 * Return Value: %TRUE if the widget always emits ::drag-motion events
1563 gtk_drag_dest_get_track_motion (GtkWidget *widget)
1565 GtkDragDestSite *site;
1567 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1569 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1572 return site->track_motion;
1577 /*************************************************************
1578 * _gtk_drag_dest_handle_event:
1579 * Called from widget event handling code on Drag events
1583 * toplevel: Toplevel widget that received the event
1586 *************************************************************/
1589 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1592 GtkDragDestInfo *info;
1593 GdkDragContext *context;
1595 g_return_if_fail (toplevel != NULL);
1596 g_return_if_fail (event != NULL);
1598 context = event->dnd.context;
1600 info = gtk_drag_get_dest_info (context, TRUE);
1602 /* Find the widget for the event */
1603 switch (event->type)
1605 case GDK_DRAG_ENTER:
1608 case GDK_DRAG_LEAVE:
1611 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1612 info->widget = NULL;
1616 case GDK_DRAG_MOTION:
1617 case GDK_DROP_START:
1622 if (event->type == GDK_DROP_START)
1624 info->dropped = TRUE;
1625 /* We send a leave here so that the widget unhighlights
1630 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1631 info->widget = NULL;
1635 #ifdef GDK_WINDOWING_X11
1636 /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
1638 * Currently gdk_window_get_position doesn't provide reliable
1639 * information for embedded windows, so we call the much more
1640 * expensive gdk_window_get_origin().
1642 if (GTK_IS_PLUG (toplevel))
1643 gdk_window_get_origin (toplevel->window, &tx, &ty);
1645 #endif /* GDK_WINDOWING_X11 */
1646 gdk_window_get_position (toplevel->window, &tx, &ty);
1648 found = gtk_drag_find_widget (toplevel,
1651 event->dnd.x_root - tx,
1652 event->dnd.y_root - ty,
1654 (event->type == GDK_DRAG_MOTION) ?
1655 gtk_drag_dest_motion :
1656 gtk_drag_dest_drop);
1658 if (info->widget && !found)
1660 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1661 info->widget = NULL;
1666 if (event->type == GDK_DRAG_MOTION)
1669 gdk_drag_status (context, 0, event->dnd.time);
1671 else if (event->type == GDK_DROP_START && !info->proxy_source)
1673 gdk_drop_reply (context, found, event->dnd.time);
1674 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !found)
1675 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1681 g_assert_not_reached ();
1686 * gtk_drag_dest_find_target:
1687 * @widget: drag destination widget
1688 * @context: drag context
1689 * @target_list: (allow-none): list of droppable targets, or %NULL to use
1690 * gtk_drag_dest_get_target_list (@widget).
1692 * Looks for a match between @context->targets and the
1693 * @dest_target_list, returning the first matching target, otherwise
1694 * returning %GDK_NONE. @dest_target_list should usually be the return
1695 * value from gtk_drag_dest_get_target_list(), but some widgets may
1696 * have different valid targets for different parts of the widget; in
1697 * that case, they will have to implement a drag_motion handler that
1698 * passes the correct target list to this function.
1700 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1703 gtk_drag_dest_find_target (GtkWidget *widget,
1704 GdkDragContext *context,
1705 GtkTargetList *target_list)
1708 GList *tmp_source = NULL;
1709 GtkWidget *source_widget;
1711 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1712 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1713 g_return_val_if_fail (!context->is_source, GDK_NONE);
1716 source_widget = gtk_drag_get_source_widget (context);
1718 if (target_list == NULL)
1719 target_list = gtk_drag_dest_get_target_list (widget);
1721 if (target_list == NULL)
1724 tmp_target = target_list->list;
1727 GtkTargetPair *pair = tmp_target->data;
1728 tmp_source = context->targets;
1731 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1733 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1734 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)) &&
1735 (!(pair->flags & GTK_TARGET_OTHER_APP) || !source_widget) &&
1736 (!(pair->flags & GTK_TARGET_OTHER_WIDGET) || (source_widget != widget)))
1737 return pair->target;
1741 tmp_source = tmp_source->next;
1743 tmp_target = tmp_target->next;
1750 gtk_drag_selection_received (GtkWidget *widget,
1751 GtkSelectionData *selection_data,
1755 GdkDragContext *context;
1756 GtkDragDestInfo *info;
1757 GtkWidget *drop_widget;
1761 context = g_object_get_data (G_OBJECT (widget), "drag-context");
1762 info = gtk_drag_get_dest_info (context, FALSE);
1764 if (info->proxy_data &&
1765 info->proxy_data->target == selection_data->target)
1767 gtk_selection_data_set (info->proxy_data,
1768 selection_data->type,
1769 selection_data->format,
1770 selection_data->data,
1771 selection_data->length);
1776 if (selection_data->target == gdk_atom_intern_static_string ("DELETE"))
1778 gtk_drag_finish (context, TRUE, FALSE, time);
1780 else if ((selection_data->target == gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS")) ||
1781 (selection_data->target == gdk_atom_intern_static_string ("XmTRANSFER_FAILURE")))
1787 GtkDragDestSite *site;
1789 site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1791 if (site && site->target_list)
1795 if (gtk_target_list_find (site->target_list,
1796 selection_data->target,
1799 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1800 selection_data->length >= 0)
1801 g_signal_emit_by_name (drop_widget,
1802 "drag-data-received",
1803 context, info->drop_x, info->drop_y,
1810 g_signal_emit_by_name (drop_widget,
1811 "drag-data-received",
1812 context, info->drop_x, info->drop_y,
1817 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1820 gtk_drag_finish (context,
1821 (selection_data->length >= 0),
1822 (context->action == GDK_ACTION_MOVE),
1826 g_object_unref (drop_widget);
1829 g_signal_handlers_disconnect_by_func (widget,
1830 gtk_drag_selection_received,
1833 g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
1834 g_object_unref (context);
1836 gtk_drag_release_ipc_widget (widget);
1839 /*************************************************************
1840 * gtk_drag_find_widget:
1841 * Function used to locate widgets for
1842 * DRAG_MOTION and DROP_START events.
1843 *************************************************************/
1846 gtk_drag_find_widget (GtkWidget *widget,
1847 GdkDragContext *context,
1848 GtkDragDestInfo *info,
1852 GtkDragDestCallback callback)
1854 if (!gtk_widget_get_mapped (widget) ||
1855 !gtk_widget_get_sensitive (widget))
1858 /* Get the widget at the pointer coordinates and travel up
1859 * the widget hierarchy from there.
1861 widget = _gtk_widget_find_at_coords (gtk_widget_get_window (widget),
1869 GList *hierarchy = NULL;
1870 gboolean found = FALSE;
1872 if (!gtk_widget_get_mapped (widget) ||
1873 !gtk_widget_get_sensitive (widget))
1876 /* need to reference the entire hierarchy temporarily in case the
1877 * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
1879 for (parent = widget;
1881 parent = gtk_widget_get_parent (parent))
1883 hierarchy = g_list_prepend (hierarchy, g_object_ref (parent));
1886 /* If the current widget is registered as a drop site, check to
1887 * emit "drag-motion" to check if we are actually in a drop
1890 if (g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1892 found = callback (widget, context, x, y, time);
1894 /* If so, send a "drag-leave" to the last widget */
1897 if (info->widget && info->widget != widget)
1899 gtk_drag_dest_leave (info->widget, context, time);
1902 info->widget = widget;
1908 /* Get the parent before unreffing the hierarchy because
1909 * invoking the callback might have destroyed the widget
1911 parent = gtk_widget_get_parent (widget);
1913 /* The parent might be going away when unreffing the
1914 * hierarchy, so also protect againt that
1917 g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
1920 g_list_foreach (hierarchy, (GFunc) g_object_unref, NULL);
1921 g_list_free (hierarchy);
1927 g_object_remove_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
1931 if (!gtk_widget_translate_coordinates (widget, parent, x, y, &x, &y))
1941 gtk_drag_proxy_begin (GtkWidget *widget,
1942 GtkDragDestInfo *dest_info,
1945 GtkDragSourceInfo *source_info;
1947 GdkDragContext *context;
1948 GtkWidget *ipc_widget;
1950 if (dest_info->proxy_source)
1952 gdk_drag_abort (dest_info->proxy_source->context, time);
1953 gtk_drag_source_info_destroy (dest_info->proxy_source);
1954 dest_info->proxy_source = NULL;
1957 ipc_widget = gtk_drag_get_ipc_widget (widget);
1958 context = gdk_drag_begin (ipc_widget->window,
1959 dest_info->context->targets);
1961 source_info = gtk_drag_get_source_info (context, TRUE);
1963 source_info->ipc_widget = ipc_widget;
1964 source_info->widget = g_object_ref (widget);
1966 source_info->target_list = gtk_target_list_new (NULL, 0);
1967 tmp_list = dest_info->context->targets;
1970 gtk_target_list_add (source_info->target_list,
1971 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1972 tmp_list = tmp_list->next;
1975 source_info->proxy_dest = dest_info;
1977 g_signal_connect (ipc_widget,
1979 G_CALLBACK (gtk_drag_selection_get),
1982 dest_info->proxy_source = source_info;
1986 gtk_drag_dest_info_destroy (gpointer data)
1988 GtkDragDestInfo *info = data;
1993 static GtkDragDestInfo *
1994 gtk_drag_get_dest_info (GdkDragContext *context,
1997 GtkDragDestInfo *info;
1998 static GQuark info_quark = 0;
2000 info_quark = g_quark_from_static_string ("gtk-dest-info");
2002 info = g_object_get_qdata (G_OBJECT (context), info_quark);
2003 if (!info && create)
2005 info = g_new (GtkDragDestInfo, 1);
2006 info->widget = NULL;
2007 info->context = context;
2008 info->proxy_source = NULL;
2009 info->proxy_data = NULL;
2010 info->dropped = FALSE;
2011 info->proxy_drop_wait = FALSE;
2012 g_object_set_qdata_full (G_OBJECT (context), info_quark,
2013 info, gtk_drag_dest_info_destroy);
2019 static GQuark dest_info_quark = 0;
2021 static GtkDragSourceInfo *
2022 gtk_drag_get_source_info (GdkDragContext *context,
2025 GtkDragSourceInfo *info;
2026 if (!dest_info_quark)
2027 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
2029 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
2030 if (!info && create)
2032 info = g_new0 (GtkDragSourceInfo, 1);
2033 info->context = context;
2034 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
2041 gtk_drag_clear_source_info (GdkDragContext *context)
2043 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
2047 gtk_drag_dest_realized (GtkWidget *widget)
2049 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2051 if (gtk_widget_is_toplevel (toplevel))
2052 gdk_window_register_dnd (toplevel->window);
2056 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
2057 GtkWidget *previous_toplevel)
2059 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
2061 if (gtk_widget_is_toplevel (toplevel) && gtk_widget_get_realized (toplevel))
2062 gdk_window_register_dnd (toplevel->window);
2066 gtk_drag_dest_site_destroy (gpointer data)
2068 GtkDragDestSite *site = data;
2070 if (site->proxy_window)
2071 g_object_unref (site->proxy_window);
2073 if (site->target_list)
2074 gtk_target_list_unref (site->target_list);
2080 * Default drag handlers
2083 gtk_drag_dest_leave (GtkWidget *widget,
2084 GdkDragContext *context,
2087 GtkDragDestSite *site;
2089 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2090 g_return_if_fail (site != NULL);
2094 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2096 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
2098 gdk_drag_abort (info->proxy_source->context, time);
2099 gtk_drag_source_info_destroy (info->proxy_source);
2100 info->proxy_source = NULL;
2107 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
2108 gtk_drag_unhighlight (widget);
2110 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
2112 g_signal_emit_by_name (widget, "drag-leave", context, time);
2114 site->have_drag = FALSE;
2119 gtk_drag_dest_motion (GtkWidget *widget,
2120 GdkDragContext *context,
2125 GtkDragDestSite *site;
2126 GdkDragAction action = 0;
2129 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2130 g_return_val_if_fail (site != NULL, FALSE);
2135 GdkEvent *current_event;
2136 GdkWindow *dest_window;
2137 GdkDragProtocol proto;
2139 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
2141 if (!info->proxy_source || info->proxy_source->widget != widget)
2142 gtk_drag_proxy_begin (widget, info, time);
2144 current_event = gtk_get_current_event ();
2146 if (site->proxy_window)
2148 dest_window = site->proxy_window;
2149 proto = site->proxy_protocol;
2153 gdk_drag_find_window_for_screen (info->proxy_source->context,
2155 gdk_drawable_get_screen (current_event->dnd.window),
2156 current_event->dnd.x_root,
2157 current_event->dnd.y_root,
2158 &dest_window, &proto);
2161 gdk_drag_motion (info->proxy_source->context,
2163 current_event->dnd.x_root,
2164 current_event->dnd.y_root,
2165 context->suggested_action,
2166 context->actions, time);
2168 if (!site->proxy_window && dest_window)
2169 g_object_unref (dest_window);
2171 selection = gdk_drag_get_selection (info->proxy_source->context);
2173 selection != gdk_drag_get_selection (info->context))
2174 gtk_drag_source_check_selection (info->proxy_source, selection, time);
2176 gdk_event_free (current_event);
2181 if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
2183 if (context->suggested_action & site->actions)
2184 action = context->suggested_action;
2189 for (i = 0; i < 8; i++)
2191 if ((site->actions & (1 << i)) &&
2192 (context->actions & (1 << i)))
2200 if (action && gtk_drag_dest_find_target (widget, context, NULL))
2202 if (!site->have_drag)
2204 site->have_drag = TRUE;
2205 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
2206 gtk_drag_highlight (widget);
2209 gdk_drag_status (context, action, time);
2213 gdk_drag_status (context, 0, time);
2214 if (!site->track_motion)
2219 g_signal_emit_by_name (widget, "drag-motion",
2220 context, x, y, time, &retval);
2222 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
2226 gtk_drag_dest_drop (GtkWidget *widget,
2227 GdkDragContext *context,
2232 GtkDragDestSite *site;
2233 GtkDragDestInfo *info;
2235 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
2236 g_return_val_if_fail (site != NULL, FALSE);
2238 info = gtk_drag_get_dest_info (context, FALSE);
2239 g_return_val_if_fail (info != NULL, FALSE);
2246 if (info->proxy_source ||
2247 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
2249 gtk_drag_drop (info->proxy_source, time);
2253 /* We need to synthesize a motion event, wait for a status,
2254 * and, if we get a good one, do a drop.
2257 GdkEvent *current_event;
2259 GdkWindow *dest_window;
2260 GdkDragProtocol proto;
2262 gtk_drag_proxy_begin (widget, info, time);
2263 info->proxy_drop_wait = TRUE;
2264 info->proxy_drop_time = time;
2266 current_event = gtk_get_current_event ();
2268 if (site->proxy_window)
2270 dest_window = site->proxy_window;
2271 proto = site->proxy_protocol;
2275 gdk_drag_find_window_for_screen (info->proxy_source->context,
2277 gdk_drawable_get_screen (current_event->dnd.window),
2278 current_event->dnd.x_root,
2279 current_event->dnd.y_root,
2280 &dest_window, &proto);
2283 gdk_drag_motion (info->proxy_source->context,
2285 current_event->dnd.x_root,
2286 current_event->dnd.y_root,
2287 context->suggested_action,
2288 context->actions, time);
2290 if (!site->proxy_window && dest_window)
2291 g_object_unref (dest_window);
2293 selection = gdk_drag_get_selection (info->proxy_source->context);
2295 selection != gdk_drag_get_selection (info->context))
2296 gtk_drag_source_check_selection (info->proxy_source, selection, time);
2298 gdk_event_free (current_event);
2307 if (site->flags & GTK_DEST_DEFAULT_DROP)
2309 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
2311 if (target == GDK_NONE)
2313 gtk_drag_finish (context, FALSE, FALSE, time);
2317 gtk_drag_get_data (widget, context, target, time);
2320 g_signal_emit_by_name (widget, "drag-drop",
2321 context, x, y, time, &retval);
2323 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
2331 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
2332 * so that we can set the icon from the source site information
2334 static GdkDragContext *
2335 gtk_drag_begin_internal (GtkWidget *widget,
2336 GtkDragSourceSite *site,
2337 GtkTargetList *target_list,
2338 GdkDragAction actions,
2342 GtkDragSourceInfo *info;
2343 GList *targets = NULL;
2345 guint32 time = GDK_CURRENT_TIME;
2346 GdkDragAction possible_actions, suggested_action;
2347 GdkDragContext *context;
2348 GtkWidget *ipc_widget;
2350 GdkDevice *pointer, *keyboard;
2352 pointer = keyboard = NULL;
2353 ipc_widget = gtk_drag_get_ipc_widget (widget);
2355 gtk_drag_get_event_actions (event, button, actions,
2356 &suggested_action, &possible_actions);
2358 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2364 time = gdk_event_get_time (event);
2365 pointer = gdk_event_get_device (event);
2367 if (pointer->source == GDK_SOURCE_KEYBOARD)
2370 pointer = gdk_device_get_associated_device (keyboard);
2373 keyboard = gdk_device_get_associated_device (pointer);
2377 GdkDeviceManager *device_manager;
2379 device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
2380 pointer = gdk_device_manager_get_client_pointer (device_manager);
2381 keyboard = gdk_device_get_associated_device (pointer);
2387 if (gdk_device_grab (pointer, ipc_widget->window,
2388 GDK_OWNERSHIP_APPLICATION, FALSE,
2389 GDK_POINTER_MOTION_MASK |
2390 GDK_BUTTON_RELEASE_MASK,
2391 cursor, time) != GDK_GRAB_SUCCESS)
2393 gtk_drag_release_ipc_widget (ipc_widget);
2398 grab_dnd_keys (ipc_widget, keyboard, time);
2400 /* We use a GTK grab here to override any grabs that the widget
2401 * we are dragging from might have held
2403 gtk_device_grab_add (ipc_widget, pointer, FALSE);
2405 tmp_list = g_list_last (target_list->list);
2408 GtkTargetPair *pair = tmp_list->data;
2409 targets = g_list_prepend (targets,
2410 GINT_TO_POINTER (pair->target));
2411 tmp_list = tmp_list->prev;
2414 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
2416 context = gdk_drag_begin (ipc_widget->window, targets);
2417 gdk_drag_context_set_device (context, pointer);
2418 g_list_free (targets);
2420 info = gtk_drag_get_source_info (context, TRUE);
2422 info->ipc_widget = ipc_widget;
2423 g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
2425 info->widget = g_object_ref (widget);
2427 info->button = button;
2428 info->cursor = cursor;
2429 info->target_list = target_list;
2430 gtk_target_list_ref (target_list);
2432 info->possible_actions = actions;
2434 info->status = GTK_DRAG_STATUS_DRAG;
2435 info->last_event = NULL;
2436 info->selections = NULL;
2437 info->icon_window = NULL;
2438 info->destroy_icon = FALSE;
2440 /* Set cur_x, cur_y here so if the "drag-begin" signal shows
2441 * the drag icon, it will be in the right place
2443 if (event && event->type == GDK_MOTION_NOTIFY)
2445 info->cur_screen = gtk_widget_get_screen (widget);
2446 info->cur_x = event->motion.x_root;
2447 info->cur_y = event->motion.y_root;
2451 gdk_display_get_device_state (gtk_widget_get_display (widget), pointer,
2452 &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
2455 g_signal_emit_by_name (widget, "drag-begin", info->context);
2457 /* Ensure that we have an icon before we start the drag; the
2458 * application may have set one in ::drag_begin, or it may
2461 if (!info->icon_window && !info->icon_pixbuf)
2463 if (!site || site->icon_type == GTK_IMAGE_EMPTY)
2464 gtk_drag_set_icon_default (context);
2466 switch (site->icon_type)
2468 case GTK_IMAGE_PIXMAP:
2469 gtk_drag_set_icon_pixmap (context,
2471 site->icon_data.pixmap.pixmap,
2475 case GTK_IMAGE_PIXBUF:
2476 gtk_drag_set_icon_pixbuf (context,
2477 site->icon_data.pixbuf.pixbuf,
2480 case GTK_IMAGE_STOCK:
2481 gtk_drag_set_icon_stock (context,
2482 site->icon_data.stock.stock_id,
2485 case GTK_IMAGE_ICON_NAME:
2486 gtk_drag_set_icon_name (context,
2487 site->icon_data.name.icon_name,
2490 case GTK_IMAGE_EMPTY:
2492 g_assert_not_reached();
2497 /* We need to composite the icon into the cursor, if we are
2498 * not using an icon window.
2500 if (info->icon_pixbuf)
2502 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2506 if (cursor != info->cursor)
2508 gdk_device_grab (pointer, widget->window,
2509 GDK_OWNERSHIP_APPLICATION, FALSE,
2510 GDK_POINTER_MOTION_MASK |
2511 GDK_BUTTON_RELEASE_MASK,
2513 info->cursor = cursor;
2517 if (event && event->type == GDK_MOTION_NOTIFY)
2518 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
2520 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
2522 info->start_x = info->cur_x;
2523 info->start_y = info->cur_y;
2525 g_signal_connect (info->ipc_widget, "grab-broken-event",
2526 G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
2527 g_signal_connect (info->ipc_widget, "grab-notify",
2528 G_CALLBACK (gtk_drag_grab_notify_cb), info);
2529 g_signal_connect (info->ipc_widget, "button-release-event",
2530 G_CALLBACK (gtk_drag_button_release_cb), info);
2531 g_signal_connect (info->ipc_widget, "motion-notify-event",
2532 G_CALLBACK (gtk_drag_motion_cb), info);
2533 g_signal_connect (info->ipc_widget, "key-press-event",
2534 G_CALLBACK (gtk_drag_key_cb), info);
2535 g_signal_connect (info->ipc_widget, "key-release-event",
2536 G_CALLBACK (gtk_drag_key_cb), info);
2537 g_signal_connect (info->ipc_widget, "selection-get",
2538 G_CALLBACK (gtk_drag_selection_get), info);
2540 info->have_grab = TRUE;
2541 info->grab_time = time;
2543 return info->context;
2548 * @widget: the source widget.
2549 * @targets: The targets (data formats) in which the
2550 * source can provide the data.
2551 * @actions: A bitmask of the allowed drag actions for this drag.
2552 * @button: The button the user clicked to start the drag.
2553 * @event: The event that triggered the start of the drag.
2555 * Initiates a drag on the source side. The function
2556 * only needs to be used when the application is
2557 * starting drags itself, and is not needed when
2558 * gtk_drag_source_set() is used.
2560 * Return value: the context for this drag.
2563 gtk_drag_begin (GtkWidget *widget,
2564 GtkTargetList *targets,
2565 GdkDragAction actions,
2569 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2570 g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
2571 g_return_val_if_fail (targets != NULL, NULL);
2573 return gtk_drag_begin_internal (widget, NULL, targets,
2574 actions, button, event);
2578 * gtk_drag_source_set:
2579 * @widget: a #GtkWidget
2580 * @start_button_mask: the bitmask of buttons that can start the drag
2581 * @targets: (allow-none) (array length=n_targets): the table of targets that the drag will support,
2583 * @n_targets: the number of items in @targets
2584 * @actions: the bitmask of possible actions for a drag from this widget
2586 * Sets up a widget so that GTK+ will start a drag operation when the user
2587 * clicks and drags on the widget. The widget must have a window.
2590 gtk_drag_source_set (GtkWidget *widget,
2591 GdkModifierType start_button_mask,
2592 const GtkTargetEntry *targets,
2594 GdkDragAction actions)
2596 GtkDragSourceSite *site;
2598 g_return_if_fail (GTK_IS_WIDGET (widget));
2600 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2602 gtk_widget_add_events (widget,
2603 gtk_widget_get_events (widget) |
2604 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2605 GDK_BUTTON_MOTION_MASK);
2609 if (site->target_list)
2610 gtk_target_list_unref (site->target_list);
2614 site = g_new0 (GtkDragSourceSite, 1);
2616 site->icon_type = GTK_IMAGE_EMPTY;
2618 g_signal_connect (widget, "button-press-event",
2619 G_CALLBACK (gtk_drag_source_event_cb),
2621 g_signal_connect (widget, "button-release-event",
2622 G_CALLBACK (gtk_drag_source_event_cb),
2624 g_signal_connect (widget, "motion-notify-event",
2625 G_CALLBACK (gtk_drag_source_event_cb),
2628 g_object_set_data_full (G_OBJECT (widget),
2629 I_("gtk-site-data"),
2630 site, gtk_drag_source_site_destroy);
2633 site->start_button_mask = start_button_mask;
2635 site->target_list = gtk_target_list_new (targets, n_targets);
2637 site->actions = actions;
2640 /*************************************************************
2641 * gtk_drag_source_unset
2642 * Unregister this widget as a drag source.
2646 *************************************************************/
2649 gtk_drag_source_unset (GtkWidget *widget)
2651 GtkDragSourceSite *site;
2653 g_return_if_fail (GTK_IS_WIDGET (widget));
2655 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2659 g_signal_handlers_disconnect_by_func (widget,
2660 gtk_drag_source_event_cb,
2662 g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
2667 * gtk_drag_source_get_target_list:
2668 * @widget: a #GtkWidget
2670 * Gets the list of targets this widget can provide for
2673 * Return value: the #GtkTargetList, or %NULL if none
2678 gtk_drag_source_get_target_list (GtkWidget *widget)
2680 GtkDragSourceSite *site;
2682 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2684 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2686 return site ? site->target_list : NULL;
2690 * gtk_drag_source_set_target_list:
2691 * @widget: a #GtkWidget that's a drag source
2692 * @target_list: (allow-none): list of draggable targets, or %NULL for none
2694 * Changes the target types that this widget offers for drag-and-drop.
2695 * The widget must first be made into a drag source with
2696 * gtk_drag_source_set().
2701 gtk_drag_source_set_target_list (GtkWidget *widget,
2702 GtkTargetList *target_list)
2704 GtkDragSourceSite *site;
2706 g_return_if_fail (GTK_IS_WIDGET (widget));
2708 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2711 g_warning ("gtk_drag_source_set_target_list() requires the widget "
2712 "to already be a drag source.");
2717 gtk_target_list_ref (target_list);
2719 if (site->target_list)
2720 gtk_target_list_unref (site->target_list);
2722 site->target_list = target_list;
2726 * gtk_drag_source_add_text_targets:
2727 * @widget: a #GtkWidget that's is a drag source
2729 * Add the text targets supported by #GtkSelection to
2730 * the target list of the drag source. The targets
2731 * are added with @info = 0. If you need another value,
2732 * use gtk_target_list_add_text_targets() and
2733 * gtk_drag_source_set_target_list().
2738 gtk_drag_source_add_text_targets (GtkWidget *widget)
2740 GtkTargetList *target_list;
2742 target_list = gtk_drag_source_get_target_list (widget);
2744 gtk_target_list_ref (target_list);
2746 target_list = gtk_target_list_new (NULL, 0);
2747 gtk_target_list_add_text_targets (target_list, 0);
2748 gtk_drag_source_set_target_list (widget, target_list);
2749 gtk_target_list_unref (target_list);
2753 * gtk_drag_source_add_image_targets:
2754 * @widget: a #GtkWidget that's is a drag source
2756 * Add the writable image targets supported by #GtkSelection to
2757 * the target list of the drag source. The targets
2758 * are added with @info = 0. If you need another value,
2759 * use gtk_target_list_add_image_targets() and
2760 * gtk_drag_source_set_target_list().
2765 gtk_drag_source_add_image_targets (GtkWidget *widget)
2767 GtkTargetList *target_list;
2769 target_list = gtk_drag_source_get_target_list (widget);
2771 gtk_target_list_ref (target_list);
2773 target_list = gtk_target_list_new (NULL, 0);
2774 gtk_target_list_add_image_targets (target_list, 0, TRUE);
2775 gtk_drag_source_set_target_list (widget, target_list);
2776 gtk_target_list_unref (target_list);
2780 * gtk_drag_source_add_uri_targets:
2781 * @widget: a #GtkWidget that's is a drag source
2783 * Add the URI targets supported by #GtkSelection to
2784 * the target list of the drag source. The targets
2785 * are added with @info = 0. If you need another value,
2786 * use gtk_target_list_add_uri_targets() and
2787 * gtk_drag_source_set_target_list().
2792 gtk_drag_source_add_uri_targets (GtkWidget *widget)
2794 GtkTargetList *target_list;
2796 target_list = gtk_drag_source_get_target_list (widget);
2798 gtk_target_list_ref (target_list);
2800 target_list = gtk_target_list_new (NULL, 0);
2801 gtk_target_list_add_uri_targets (target_list, 0);
2802 gtk_drag_source_set_target_list (widget, target_list);
2803 gtk_target_list_unref (target_list);
2807 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2809 switch (site->icon_type)
2811 case GTK_IMAGE_EMPTY:
2813 case GTK_IMAGE_PIXMAP:
2814 if (site->icon_data.pixmap.pixmap)
2815 g_object_unref (site->icon_data.pixmap.pixmap);
2816 if (site->icon_mask)
2817 g_object_unref (site->icon_mask);
2819 case GTK_IMAGE_PIXBUF:
2820 g_object_unref (site->icon_data.pixbuf.pixbuf);
2822 case GTK_IMAGE_STOCK:
2823 g_free (site->icon_data.stock.stock_id);
2825 case GTK_IMAGE_ICON_NAME:
2826 g_free (site->icon_data.name.icon_name);
2829 g_assert_not_reached();
2832 site->icon_type = GTK_IMAGE_EMPTY;
2835 g_object_unref (site->colormap);
2836 site->colormap = NULL;
2840 * gtk_drag_source_set_icon:
2841 * @widget: a #GtkWidget
2842 * @colormap: the colormap of the icon
2843 * @pixmap: the image data for the icon
2844 * @mask: (allow-none): the transparency mask for an image.
2846 * Sets the icon that will be used for drags from a particular widget
2847 * from a pixmap/mask. GTK+ retains references for the arguments, and
2848 * will release them when they are no longer needed.
2849 * Use gtk_drag_source_set_icon_pixbuf() instead.
2852 gtk_drag_source_set_icon (GtkWidget *widget,
2853 GdkColormap *colormap,
2857 GtkDragSourceSite *site;
2859 g_return_if_fail (GTK_IS_WIDGET (widget));
2860 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2861 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2862 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2864 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2865 g_return_if_fail (site != NULL);
2867 g_object_ref (colormap);
2868 g_object_ref (pixmap);
2870 g_object_ref (mask);
2872 gtk_drag_source_unset_icon (site);
2874 site->icon_type = GTK_IMAGE_PIXMAP;
2876 site->icon_data.pixmap.pixmap = pixmap;
2877 site->icon_mask = mask;
2878 site->colormap = colormap;
2882 * gtk_drag_source_set_icon_pixbuf:
2883 * @widget: a #GtkWidget
2884 * @pixbuf: the #GdkPixbuf for the drag icon
2886 * Sets the icon that will be used for drags from a particular widget
2887 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2888 * release it when it is no longer needed.
2891 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2894 GtkDragSourceSite *site;
2896 g_return_if_fail (GTK_IS_WIDGET (widget));
2897 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2899 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2900 g_return_if_fail (site != NULL);
2901 g_object_ref (pixbuf);
2903 gtk_drag_source_unset_icon (site);
2905 site->icon_type = GTK_IMAGE_PIXBUF;
2906 site->icon_data.pixbuf.pixbuf = pixbuf;
2910 * gtk_drag_source_set_icon_stock:
2911 * @widget: a #GtkWidget
2912 * @stock_id: the ID of the stock icon to use
2914 * Sets the icon that will be used for drags from a particular source
2918 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2919 const gchar *stock_id)
2921 GtkDragSourceSite *site;
2923 g_return_if_fail (GTK_IS_WIDGET (widget));
2924 g_return_if_fail (stock_id != NULL);
2926 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2927 g_return_if_fail (site != NULL);
2929 gtk_drag_source_unset_icon (site);
2931 site->icon_type = GTK_IMAGE_STOCK;
2932 site->icon_data.stock.stock_id = g_strdup (stock_id);
2936 * gtk_drag_source_set_icon_name:
2937 * @widget: a #GtkWidget
2938 * @icon_name: name of icon to use
2940 * Sets the icon that will be used for drags from a particular source
2941 * to a themed icon. See the docs for #GtkIconTheme for more details.
2946 gtk_drag_source_set_icon_name (GtkWidget *widget,
2947 const gchar *icon_name)
2949 GtkDragSourceSite *site;
2951 g_return_if_fail (GTK_IS_WIDGET (widget));
2952 g_return_if_fail (icon_name != NULL);
2954 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2955 g_return_if_fail (site != NULL);
2957 gtk_drag_source_unset_icon (site);
2959 site->icon_type = GTK_IMAGE_ICON_NAME;
2960 site->icon_data.name.icon_name = g_strdup (icon_name);
2964 gtk_drag_get_icon (GtkDragSourceInfo *info,
2965 GtkWidget **icon_window,
2969 if (get_can_change_screen (info->icon_window))
2970 gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2973 if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2975 if (!info->fallback_icon)
2977 gint save_hot_x, save_hot_y;
2978 gboolean save_destroy_icon;
2979 GtkWidget *save_icon_window;
2981 /* HACK to get the appropriate icon
2983 save_icon_window = info->icon_window;
2984 save_hot_x = info->hot_x;
2985 save_hot_y = info->hot_x;
2986 save_destroy_icon = info->destroy_icon;
2988 info->icon_window = NULL;
2989 if (!default_icon_pixmap)
2990 set_icon_stock_pixbuf (info->context,
2991 GTK_STOCK_DND, NULL, -2, -2, TRUE);
2993 gtk_drag_set_icon_pixmap (info->context,
2994 default_icon_colormap,
2995 default_icon_pixmap,
2998 default_icon_hot_y);
2999 info->fallback_icon = info->icon_window;
3001 info->icon_window = save_icon_window;
3002 info->hot_x = save_hot_x;
3003 info->hot_y = save_hot_y;
3004 info->destroy_icon = save_destroy_icon;
3007 gtk_widget_hide (info->icon_window);
3009 *icon_window = info->fallback_icon;
3010 gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
3012 if (!default_icon_pixmap)
3019 *hot_x = default_icon_hot_x;
3020 *hot_y = default_icon_hot_y;
3025 if (info->fallback_icon)
3026 gtk_widget_hide (info->fallback_icon);
3028 *icon_window = info->icon_window;
3029 *hot_x = info->hot_x;
3030 *hot_y = info->hot_y;
3035 gtk_drag_update_icon (GtkDragSourceInfo *info)
3037 if (info->icon_window)
3039 GtkWidget *icon_window;
3042 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
3044 gtk_window_move (GTK_WINDOW (icon_window),
3045 info->cur_x - hot_x,
3046 info->cur_y - hot_y);
3048 if (gtk_widget_get_visible (icon_window))
3049 gdk_window_raise (icon_window->window);
3051 gtk_widget_show (icon_window);
3056 gtk_drag_set_icon_window (GdkDragContext *context,
3060 gboolean destroy_on_release)
3062 GtkDragSourceInfo *info;
3064 info = gtk_drag_get_source_info (context, FALSE);
3067 if (destroy_on_release)
3068 gtk_widget_destroy (widget);
3072 gtk_drag_remove_icon (info);
3075 g_object_ref (widget);
3077 info->icon_window = widget;
3078 info->hot_x = hot_x;
3079 info->hot_y = hot_y;
3080 info->destroy_icon = destroy_on_release;
3082 if (widget && info->icon_pixbuf)
3084 g_object_unref (info->icon_pixbuf);
3085 info->icon_pixbuf = NULL;
3088 gtk_drag_update_cursor (info);
3089 gtk_drag_update_icon (info);
3093 * gtk_drag_set_icon_widget:
3094 * @context: the context for a drag. (This must be called
3095 with a context for the source side of a drag)
3096 * @widget: a toplevel window to use as an icon.
3097 * @hot_x: the X offset within @widget of the hotspot.
3098 * @hot_y: the Y offset within @widget of the hotspot.
3100 * Changes the icon for a widget to a given widget. GTK+
3101 * will not destroy the icon, so if you don't want
3102 * it to persist, you should connect to the "drag-end"
3103 * signal and destroy it yourself.
3106 gtk_drag_set_icon_widget (GdkDragContext *context,
3111 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3112 g_return_if_fail (context->is_source);
3113 g_return_if_fail (GTK_IS_WIDGET (widget));
3115 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
3119 icon_window_realize (GtkWidget *window,
3125 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
3126 gtk_widget_get_colormap (window),
3127 &pixmap, &mask, 128);
3129 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
3130 g_object_unref (pixmap);
3134 gtk_widget_shape_combine_mask (window, mask, 0, 0);
3135 g_object_unref (mask);
3140 set_icon_stock_pixbuf (GdkDragContext *context,
3141 const gchar *stock_id,
3145 gboolean force_window)
3150 GdkDisplay *display;
3152 g_return_if_fail (context != NULL);
3153 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
3154 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
3156 screen = gdk_drawable_get_screen (context->source_window);
3158 /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
3159 gtk_widget_push_colormap (NULL);
3160 window = gtk_window_new (GTK_WINDOW_POPUP);
3161 gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3162 gtk_window_set_screen (GTK_WINDOW (window), screen);
3163 set_can_change_screen (window, TRUE);
3164 gtk_widget_pop_colormap ();
3166 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3167 gtk_widget_set_app_paintable (window, TRUE);
3171 pixbuf = gtk_widget_render_icon (window, stock_id,
3172 GTK_ICON_SIZE_DND, NULL);
3176 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
3177 gtk_widget_destroy (window);
3183 g_object_ref (pixbuf);
3185 display = gdk_drawable_get_display (context->source_window);
3186 width = gdk_pixbuf_get_width (pixbuf);
3187 height = gdk_pixbuf_get_height (pixbuf);
3189 if (!force_window &&
3190 gtk_drag_can_use_rgba_cursor (display, width + 2, height + 2))
3192 GtkDragSourceInfo *info;
3194 gtk_widget_destroy (window);
3196 info = gtk_drag_get_source_info (context, FALSE);
3198 if (info->icon_pixbuf)
3199 g_object_unref (info->icon_pixbuf);
3200 info->icon_pixbuf = pixbuf;
3202 gtk_drag_set_icon_window (context, NULL, hot_x, hot_y, TRUE);
3206 gtk_widget_set_size_request (window, width, height);
3208 g_signal_connect_closure (window, "realize",
3209 g_cclosure_new (G_CALLBACK (icon_window_realize),
3211 (GClosureNotify)g_object_unref),
3214 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3219 * gtk_drag_set_icon_pixbuf:
3220 * @context: the context for a drag. (This must be called
3221 * with a context for the source side of a drag)
3222 * @pixbuf: the #GdkPixbuf to use as the drag icon.
3223 * @hot_x: the X offset within @widget of the hotspot.
3224 * @hot_y: the Y offset within @widget of the hotspot.
3226 * Sets @pixbuf as the icon for a given drag.
3229 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
3234 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3235 g_return_if_fail (context->is_source);
3236 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
3238 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3242 * gtk_drag_set_icon_stock:
3243 * @context: the context for a drag. (This must be called
3244 * with a context for the source side of a drag)
3245 * @stock_id: the ID of the stock icon to use for the drag.
3246 * @hot_x: the X offset within the icon of the hotspot.
3247 * @hot_y: the Y offset within the icon of the hotspot.
3249 * Sets the icon for a given drag from a stock ID.
3252 gtk_drag_set_icon_stock (GdkDragContext *context,
3253 const gchar *stock_id,
3257 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3258 g_return_if_fail (context->is_source);
3259 g_return_if_fail (stock_id != NULL);
3261 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y, FALSE);
3265 * gtk_drag_set_icon_pixmap:
3266 * @context: the context for a drag. (This must be called
3267 * with a context for the source side of a drag)
3268 * @colormap: the colormap of the icon
3269 * @pixmap: the image data for the icon
3270 * @mask: (allow-none): the transparency mask for the icon or %NULL for none.
3271 * @hot_x: the X offset within @pixmap of the hotspot.
3272 * @hot_y: the Y offset within @pixmap of the hotspot.
3274 * Sets @pixmap as the icon for a given drag. GTK+ retains
3275 * references for the arguments, and will release them when
3276 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
3277 * will be more convenient to use.
3280 gtk_drag_set_icon_pixmap (GdkDragContext *context,
3281 GdkColormap *colormap,
3291 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3292 g_return_if_fail (context->is_source);
3293 g_return_if_fail (GDK_IS_COLORMAP (colormap));
3294 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
3295 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
3297 screen = gdk_colormap_get_screen (colormap);
3299 g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
3300 g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
3302 gdk_drawable_get_size (pixmap, &width, &height);
3304 gtk_widget_push_colormap (colormap);
3306 window = gtk_window_new (GTK_WINDOW_POPUP);
3307 gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
3308 gtk_window_set_screen (GTK_WINDOW (window), screen);
3309 set_can_change_screen (window, FALSE);
3310 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
3311 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
3313 gtk_widget_pop_colormap ();
3315 gtk_widget_set_size_request (window, width, height);
3316 gtk_widget_realize (window);
3318 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
3321 gtk_widget_shape_combine_mask (window, mask, 0, 0);
3323 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
3327 * gtk_drag_set_icon_name:
3328 * @context: the context for a drag. (This must be called
3329 * with a context for the source side of a drag)
3330 * @icon_name: name of icon to use
3331 * @hot_x: the X offset of the hotspot within the icon
3332 * @hot_y: the Y offset of the hotspot within the icon
3334 * Sets the icon for a given drag from a named themed icon. See
3335 * the docs for #GtkIconTheme for more details. Note that the
3336 * size of the icon depends on the icon theme (the icon is
3337 * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus
3338 * @hot_x and @hot_y have to be used with care.
3343 gtk_drag_set_icon_name (GdkDragContext *context,
3344 const gchar *icon_name,
3349 GtkSettings *settings;
3350 GtkIconTheme *icon_theme;
3352 gint width, height, icon_size;
3354 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3355 g_return_if_fail (context->is_source);
3356 g_return_if_fail (icon_name != NULL);
3358 screen = gdk_drawable_get_screen (context->source_window);
3359 g_return_if_fail (screen != NULL);
3361 settings = gtk_settings_get_for_screen (screen);
3362 if (gtk_icon_size_lookup_for_settings (settings,
3365 icon_size = MAX (width, height);
3367 icon_size = 32; /* default value for GTK_ICON_SIZE_DND */
3369 icon_theme = gtk_icon_theme_get_for_screen (screen);
3371 pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
3372 icon_size, 0, NULL);
3374 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y, FALSE);
3376 g_warning ("Cannot load drag icon from icon name %s", icon_name);
3380 * gtk_drag_set_icon_default:
3381 * @context: the context for a drag. (This must be called
3382 with a context for the source side of a drag)
3384 * Sets the icon for a particular drag to the default
3388 gtk_drag_set_icon_default (GdkDragContext *context)
3390 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
3391 g_return_if_fail (context->is_source);
3393 if (!default_icon_pixmap)
3394 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
3396 gtk_drag_set_icon_pixmap (context,
3397 default_icon_colormap,
3398 default_icon_pixmap,
3401 default_icon_hot_y);
3404 /*************************************************************
3405 * _gtk_drag_source_handle_event:
3406 * Called from widget event handling code on Drag events
3410 * toplevel: Toplevel widget that received the event
3413 *************************************************************/
3416 _gtk_drag_source_handle_event (GtkWidget *widget,
3419 GtkDragSourceInfo *info;
3420 GdkDragContext *context;
3422 g_return_if_fail (widget != NULL);
3423 g_return_if_fail (event != NULL);
3425 context = event->dnd.context;
3426 info = gtk_drag_get_source_info (context, FALSE);
3430 switch (event->type)
3432 case GDK_DRAG_STATUS:
3436 if (info->proxy_dest)
3438 if (!event->dnd.send_event)
3440 if (info->proxy_dest->proxy_drop_wait)
3442 gboolean result = context->action != 0;
3444 /* Aha - we can finally pass the MOTIF DROP on... */
3445 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
3447 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
3449 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
3453 gdk_drag_status (info->proxy_dest->context,
3454 event->dnd.context->action,
3459 else if (info->have_grab)
3461 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
3462 event->dnd.context->action,
3464 if (info->cursor != cursor)
3468 pointer = gdk_drag_context_get_device (context);
3469 gdk_device_grab (pointer, widget->window,
3470 GDK_OWNERSHIP_APPLICATION, FALSE,
3471 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3472 cursor, info->grab_time);
3473 info->cursor = cursor;
3476 gtk_drag_add_update_idle (info);
3481 case GDK_DROP_FINISHED:
3482 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, event->dnd.time);
3485 g_assert_not_reached ();
3489 /*************************************************************
3490 * gtk_drag_source_check_selection:
3491 * Check if we've set up handlers/claimed the selection
3492 * for a given drag. If not, add them.
3496 *************************************************************/
3499 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
3505 tmp_list = info->selections;
3508 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
3510 tmp_list = tmp_list->next;
3513 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
3517 info->selections = g_list_prepend (info->selections,
3518 GUINT_TO_POINTER (selection));
3520 tmp_list = info->target_list->list;
3523 GtkTargetPair *pair = tmp_list->data;
3525 gtk_selection_add_target (info->ipc_widget,
3529 tmp_list = tmp_list->next;
3532 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
3534 gtk_selection_add_target (info->ipc_widget,
3536 gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS"),
3537 TARGET_MOTIF_SUCCESS);
3538 gtk_selection_add_target (info->ipc_widget,
3540 gdk_atom_intern_static_string ("XmTRANSFER_FAILURE"),
3541 TARGET_MOTIF_FAILURE);
3544 gtk_selection_add_target (info->ipc_widget,
3546 gdk_atom_intern_static_string ("DELETE"),
3550 /*************************************************************
3551 * gtk_drag_drop_finished:
3552 * Clean up from the drag, and display snapback, if necessary.
3558 *************************************************************/
3561 gtk_drag_drop_finished (GtkDragSourceInfo *info,
3562 GtkDragResult result,
3567 success = (result == GTK_DRAG_RESULT_SUCCESS);
3568 gtk_drag_source_release_selections (info, time);
3570 if (info->proxy_dest)
3572 /* The time from the event isn't reliable for Xdnd drags */
3573 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
3574 info->proxy_dest->proxy_drop_time);
3575 gtk_drag_source_info_destroy (info);
3580 g_signal_emit_by_name (info->widget, "drag-failed",
3581 info->context, result, &success);
3585 gtk_drag_source_info_destroy (info);
3589 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
3593 anim->n_steps = MAX (info->cur_x - info->start_x,
3594 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
3595 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
3597 info->cur_screen = gtk_widget_get_screen (info->widget);
3599 if (!info->icon_window)
3600 set_icon_stock_pixbuf (info->context, NULL, info->icon_pixbuf,
3603 gtk_drag_update_icon (info);
3605 /* Mark the context as dead, so if the destination decides
3606 * to respond really late, we still are OK.
3608 gtk_drag_clear_source_info (info->context);
3609 gdk_threads_add_timeout (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
3615 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
3618 GdkDisplay *display = gtk_widget_get_display (info->widget);
3619 GList *tmp_list = info->selections;
3623 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
3624 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
3625 gtk_selection_owner_set_for_display (display, NULL, selection, time);
3627 tmp_list = tmp_list->next;
3630 g_list_free (info->selections);
3631 info->selections = NULL;
3634 /*************************************************************
3636 * Send a drop event.
3640 *************************************************************/
3643 gtk_drag_drop (GtkDragSourceInfo *info,
3646 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
3648 GtkSelectionData selection_data;
3650 /* GTK+ traditionally has used application/x-rootwin-drop, but the
3651 * XDND spec specifies x-rootwindow-drop.
3653 GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3654 GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3656 tmp_list = info->target_list->list;
3659 GtkTargetPair *pair = tmp_list->data;
3661 if (pair->target == target1 || pair->target == target2)
3663 selection_data.selection = GDK_NONE;
3664 selection_data.target = pair->target;
3665 selection_data.data = NULL;
3666 selection_data.length = -1;
3668 g_signal_emit_by_name (info->widget, "drag-data-get",
3669 info->context, &selection_data,
3673 /* FIXME: Should we check for length >= 0 here? */
3674 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3677 tmp_list = tmp_list->next;
3679 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3683 if (info->icon_window)
3684 gtk_widget_hide (info->icon_window);
3686 gdk_drag_drop (info->context, time);
3687 info->drop_timeout = gdk_threads_add_timeout (DROP_ABORT_TIME,
3688 gtk_drag_abort_timeout,
3694 * Source side callbacks.
3698 gtk_drag_source_event_cb (GtkWidget *widget,
3702 GtkDragSourceSite *site;
3703 gboolean retval = FALSE;
3704 site = (GtkDragSourceSite *)data;
3706 switch (event->type)
3708 case GDK_BUTTON_PRESS:
3709 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3711 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
3712 site->x = event->button.x;
3713 site->y = event->button.y;
3717 case GDK_BUTTON_RELEASE:
3718 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
3719 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
3722 case GDK_MOTION_NOTIFY:
3723 if (site->state & event->motion.state & site->start_button_mask)
3725 /* FIXME: This is really broken and can leave us
3731 if (site->state & event->motion.state &
3732 GDK_BUTTON1_MASK << (i - 1))
3736 if (gtk_drag_check_threshold (widget, site->x, site->y,
3737 event->motion.x, event->motion.y))
3740 gtk_drag_begin_internal (widget, site, site->target_list,
3749 default: /* hit for 2/3BUTTON_PRESS */
3757 gtk_drag_source_site_destroy (gpointer data)
3759 GtkDragSourceSite *site = data;
3761 if (site->target_list)
3762 gtk_target_list_unref (site->target_list);
3764 gtk_drag_source_unset_icon (site);
3769 gtk_drag_selection_get (GtkWidget *widget,
3770 GtkSelectionData *selection_data,
3775 GtkDragSourceInfo *info = data;
3776 static GdkAtom null_atom = GDK_NONE;
3780 null_atom = gdk_atom_intern_static_string ("NULL");
3785 g_signal_emit_by_name (info->widget,
3788 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3790 case TARGET_MOTIF_SUCCESS:
3791 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_SUCCESS, time);
3792 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3794 case TARGET_MOTIF_FAILURE:
3795 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_NO_TARGET, time);
3796 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3799 if (info->proxy_dest)
3801 /* This is sort of dangerous and needs to be thought
3804 info->proxy_dest->proxy_data = selection_data;
3805 gtk_drag_get_data (info->widget,
3806 info->proxy_dest->context,
3807 selection_data->target,
3810 info->proxy_dest->proxy_data = NULL;
3814 if (gtk_target_list_find (info->target_list,
3815 selection_data->target,
3818 g_signal_emit_by_name (info->widget, "drag-data-get",
3830 gtk_drag_anim_timeout (gpointer data)
3832 GtkDragAnim *anim = data;
3836 if (anim->step == anim->n_steps)
3838 gtk_drag_source_info_destroy (anim->info);
3845 x = (anim->info->start_x * (anim->step + 1) +
3846 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3847 y = (anim->info->start_y * (anim->step + 1) +
3848 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3849 if (anim->info->icon_window)
3851 GtkWidget *icon_window;
3854 gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);
3855 gtk_window_move (GTK_WINDOW (icon_window),
3869 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3871 if (info->icon_window)
3873 gtk_widget_hide (info->icon_window);
3874 if (info->destroy_icon)
3875 gtk_widget_destroy (info->icon_window);
3877 if (info->fallback_icon)
3879 gtk_widget_destroy (info->fallback_icon);
3880 info->fallback_icon = NULL;
3883 g_object_unref (info->icon_window);
3884 info->icon_window = NULL;
3889 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3893 for (i = 0; i < n_drag_cursors; i++)
3895 if (info->drag_cursors[i] != NULL)
3897 gdk_cursor_unref (info->drag_cursors[i]);
3898 info->drag_cursors[i] = NULL;
3902 gtk_drag_remove_icon (info);
3904 if (info->icon_pixbuf)
3906 g_object_unref (info->icon_pixbuf);
3907 info->icon_pixbuf = NULL;
3910 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3911 gtk_drag_grab_broken_event_cb,
3913 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3914 gtk_drag_grab_notify_cb,
3916 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3917 gtk_drag_button_release_cb,
3919 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3922 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3925 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3926 gtk_drag_selection_get,
3929 if (!info->proxy_dest)
3930 g_signal_emit_by_name (info->widget, "drag-end", info->context);
3933 g_object_unref (info->widget);
3935 gtk_selection_remove_all (info->ipc_widget);
3936 g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), NULL);
3937 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3938 gtk_drag_release_ipc_widget (info->ipc_widget);
3940 gtk_target_list_unref (info->target_list);
3942 gtk_drag_clear_source_info (info->context);
3943 g_object_unref (info->context);
3945 if (info->drop_timeout)
3946 g_source_remove (info->drop_timeout);
3948 if (info->update_idle)
3949 g_source_remove (info->update_idle);
3955 gtk_drag_update_idle (gpointer data)
3957 GtkDragSourceInfo *info = data;
3958 GdkWindow *dest_window;
3959 GdkDragProtocol protocol;
3962 GdkDragAction action;
3963 GdkDragAction possible_actions;
3966 info->update_idle = 0;
3968 if (info->last_event)
3970 time = gtk_drag_get_event_time (info->last_event);
3971 gtk_drag_get_event_actions (info->last_event,
3973 info->possible_actions,
3974 &action, &possible_actions);
3975 gtk_drag_update_icon (info);
3976 gdk_drag_find_window_for_screen (info->context,
3977 info->icon_window ? info->icon_window->window : NULL,
3978 info->cur_screen, info->cur_x, info->cur_y,
3979 &dest_window, &protocol);
3981 if (!gdk_drag_motion (info->context, dest_window, protocol,
3982 info->cur_x, info->cur_y, action,
3986 gdk_event_free ((GdkEvent *)info->last_event);
3987 info->last_event = NULL;
3991 g_object_unref (dest_window);
3993 selection = gdk_drag_get_selection (info->context);
3995 gtk_drag_source_check_selection (info, selection, time);
4003 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
4005 /* We use an idle lower than GDK_PRIORITY_REDRAW so that exposes
4006 * from the last move can catch up before we move again.
4008 if (!info->update_idle)
4009 info->update_idle = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW + 5,
4010 gtk_drag_update_idle,
4017 * @info: DragSourceInfo for the drag
4018 * @screen: new screen
4019 * @x_root: new X position
4020 * @y_root: new y position
4021 * @event: event received requiring update
4023 * Updates the status of the drag; called when the
4024 * cursor moves or the modifier changes
4027 gtk_drag_update (GtkDragSourceInfo *info,
4033 info->cur_screen = screen;
4034 info->cur_x = x_root;
4035 info->cur_y = y_root;
4036 if (info->last_event)
4038 gdk_event_free ((GdkEvent *)info->last_event);
4039 info->last_event = NULL;
4042 info->last_event = gdk_event_copy ((GdkEvent *)event);
4044 gtk_drag_add_update_idle (info);
4047 /*************************************************************
4049 * Called when the user finishes to drag, either by
4050 * releasing the mouse, or by pressing Esc.
4052 * info: Source info for the drag
4053 * time: Timestamp for ending the drag
4055 *************************************************************/
4058 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
4060 GdkEvent *send_event;
4061 GtkWidget *source_widget = info->widget;
4062 GdkDevice *pointer, *keyboard;
4064 pointer = gdk_drag_context_get_device (info->context);
4065 keyboard = gdk_device_get_associated_device (pointer);
4067 if (info->update_idle)
4069 g_source_remove (info->update_idle);
4070 info->update_idle = 0;
4073 if (info->last_event)
4075 gdk_event_free (info->last_event);
4076 info->last_event = NULL;
4079 info->have_grab = FALSE;
4081 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4082 gtk_drag_grab_broken_event_cb,
4084 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4085 gtk_drag_grab_notify_cb,
4087 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4088 gtk_drag_button_release_cb,
4090 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4093 g_signal_handlers_disconnect_by_func (info->ipc_widget,
4097 gdk_device_ungrab (pointer, time);
4098 ungrab_dnd_keys (info->ipc_widget, keyboard, time);
4099 gtk_device_grab_remove (info->ipc_widget, pointer);
4101 /* Send on a release pair to the original
4102 * widget to convince it to release its grab. We need to
4103 * call gtk_propagate_event() here, instead of
4104 * gtk_widget_event() because widget like GtkList may
4105 * expect propagation.
4108 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
4109 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
4110 send_event->button.send_event = TRUE;
4111 send_event->button.time = time;
4112 send_event->button.x = 0;
4113 send_event->button.y = 0;
4114 send_event->button.axes = NULL;
4115 send_event->button.state = 0;
4116 send_event->button.button = info->button;
4117 send_event->button.device = pointer;
4118 send_event->button.x_root = 0;
4119 send_event->button.y_root = 0;
4121 gtk_propagate_event (source_widget, send_event);
4122 gdk_event_free (send_event);
4125 /*************************************************************
4127 * Called on cancellation of a drag, either by the user
4128 * or programmatically.
4130 * info: Source info for the drag
4131 * time: Timestamp for ending the drag
4133 *************************************************************/
4136 gtk_drag_cancel (GtkDragSourceInfo *info, GtkDragResult result, guint32 time)
4138 gtk_drag_end (info, time);
4139 gdk_drag_abort (info->context, time);
4140 gtk_drag_drop_finished (info, result, time);
4143 /*************************************************************
4144 * gtk_drag_motion_cb:
4145 * "motion-notify-event" callback during drag.
4149 *************************************************************/
4152 gtk_drag_motion_cb (GtkWidget *widget,
4153 GdkEventMotion *event,
4156 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4158 gint x_root, y_root;
4162 GdkDisplay *display = gtk_widget_get_display (widget);
4164 gdk_display_get_device_state (display, event->device,
4165 &screen, &x_root, &y_root, NULL);
4166 event->x_root = x_root;
4167 event->y_root = y_root;
4170 screen = gdk_event_get_screen ((GdkEvent *)event);
4172 gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *) event);
4177 /*************************************************************
4179 * "key-press/release-event" callback during drag.
4183 *************************************************************/
4186 #define SMALL_STEP 1
4189 gtk_drag_key_cb (GtkWidget *widget,
4193 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4194 GdkModifierType state;
4195 GdkWindow *root_window;
4200 state = event->state & gtk_accelerator_get_default_mod_mask ();
4201 pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
4203 if (event->type == GDK_KEY_PRESS)
4205 switch (event->keyval)
4208 gtk_drag_cancel (info, GTK_DRAG_RESULT_USER_CANCELLED, event->time);
4216 gtk_drag_end (info, event->time);
4217 gtk_drag_drop (info, event->time);
4222 dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4227 dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4232 dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
4237 dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
4243 /* Now send a "motion" so that the modifier state is updated */
4245 /* The state is not yet updated in the event, so we need
4246 * to query it here. We could use XGetModifierMapping, but
4247 * that would be overkill.
4249 root_window = gtk_widget_get_root_window (widget);
4250 gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
4251 event->state = state;
4253 if (dx != 0 || dy != 0)
4257 gdk_display_warp_device (gtk_widget_get_display (widget), pointer,
4258 gtk_widget_get_screen (widget),
4259 info->cur_x, info->cur_y);
4262 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
4268 gtk_drag_grab_broken_event_cb (GtkWidget *widget,
4269 GdkEventGrabBroken *event,
4272 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4274 /* Don't cancel if we break the implicit grab from the initial button_press.
4275 * Also, don't cancel if we re-grab on the widget or on our IPC window, for
4276 * example, when changing the drag cursor.
4279 || event->grab_window == info->widget->window
4280 || event->grab_window == info->ipc_widget->window)
4283 gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4288 gtk_drag_grab_notify_cb (GtkWidget *widget,
4289 gboolean was_grabbed,
4292 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4295 pointer = gdk_drag_context_get_device (info->context);
4297 if (gtk_widget_device_is_shadowed (widget, pointer))
4299 /* We have to block callbacks to avoid recursion here, because
4300 gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
4301 g_signal_handlers_block_by_func (widget, gtk_drag_grab_notify_cb, data);
4302 gtk_drag_cancel (info, GTK_DRAG_RESULT_GRAB_BROKEN, gtk_get_current_event_time ());
4303 g_signal_handlers_unblock_by_func (widget, gtk_drag_grab_notify_cb, data);
4308 /*************************************************************
4309 * gtk_drag_button_release_cb:
4310 * "button-release-event" callback during drag.
4314 *************************************************************/
4317 gtk_drag_button_release_cb (GtkWidget *widget,
4318 GdkEventButton *event,
4321 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
4323 if (event->button != info->button)
4326 if ((info->context->action != 0) && (info->context->dest_window != 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 gtk_drag_abort_timeout (gpointer data)
4342 GtkDragSourceInfo *info = data;
4343 guint32 time = GDK_CURRENT_TIME;
4345 if (info->proxy_dest)
4346 time = info->proxy_dest->proxy_drop_time;
4348 info->drop_timeout = 0;
4349 gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED, time);
4355 * gtk_drag_check_threshold:
4356 * @widget: a #GtkWidget
4357 * @start_x: X coordinate of start of drag
4358 * @start_y: Y coordinate of start of drag
4359 * @current_x: current X coordinate
4360 * @current_y: current Y coordinate
4362 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
4363 * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
4364 * should trigger the beginning of a drag-and-drop operation.
4366 * Return Value: %TRUE if the drag threshold has been passed.
4369 gtk_drag_check_threshold (GtkWidget *widget,
4375 gint drag_threshold;
4377 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
4379 g_object_get (gtk_widget_get_settings (widget),
4380 "gtk-dnd-drag-threshold", &drag_threshold,
4383 return (ABS (current_x - start_x) > drag_threshold ||
4384 ABS (current_y - start_y) > drag_threshold);