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/.
27 #include "gdkconfig.h"
29 #include "gdk/gdkkeysyms.h"
33 #include "gtkinvisible.h"
36 #include "gtkwindow.h"
38 static GSList *source_widgets = NULL;
40 typedef struct _GtkDragSourceSite GtkDragSourceSite;
41 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
42 typedef struct _GtkDragDestSite GtkDragDestSite;
43 typedef struct _GtkDragDestInfo GtkDragDestInfo;
44 typedef struct _GtkDragAnim GtkDragAnim;
45 typedef struct _GtkDragFindData GtkDragFindData;
55 struct _GtkDragSourceSite
57 GdkModifierType start_button_mask;
58 GtkTargetList *target_list; /* Targets for drag data */
59 GdkDragAction actions; /* Possible actions */
62 GtkImageType icon_type;
65 GtkImagePixmapData pixmap;
66 GtkImagePixbufData pixbuf;
67 GtkImageStockData stock;
71 GdkColormap *colormap; /* Colormap for drag icon */
73 /* Stored button press information to detect drag beginning */
78 struct _GtkDragSourceInfo
81 GtkTargetList *target_list; /* Targets for drag data */
82 GdkDragAction possible_actions; /* Actions allowed by source */
83 GdkDragContext *context; /* drag context */
84 GtkWidget *icon_window; /* Window for drag */
85 GtkWidget *fallback_icon; /* Window for drag used on other screens */
86 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
87 GdkCursor *cursor; /* Cursor for drag */
88 gint hot_x, hot_y; /* Hot spot for drag */
89 gint button; /* mouse button starting drag */
91 GtkDragStatus status; /* drag status */
92 GdkEvent *last_event; /* motion event waiting for response */
94 gint start_x, start_y; /* Initial position */
95 gint cur_x, cur_y; /* Current Position */
96 GdkScreen *cur_screen; /* Current screen for pointer */
98 guint32 grab_time; /* timestamp for initial grab */
99 GList *selections; /* selections we've claimed */
101 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
103 guint drop_timeout; /* Timeout for aborting drop */
104 guint destroy_icon : 1; /* If true, destroy icon_window
106 guint have_grab : 1; /* Do we still have the pointer grab
110 struct _GtkDragDestSite
112 GtkDestDefaults flags;
113 GtkTargetList *target_list;
114 GdkDragAction actions;
115 GdkWindow *proxy_window;
116 GdkDragProtocol proxy_protocol;
117 gboolean do_proxy : 1;
118 gboolean proxy_coords : 1;
119 gboolean have_drag : 1;
122 struct _GtkDragDestInfo
124 GtkWidget *widget; /* Widget in which drag is in */
125 GdkDragContext *context; /* Drag context */
126 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
127 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
128 gboolean dropped : 1; /* Set after we receive a drop */
129 guint32 proxy_drop_time; /* Timestamp for proxied drop */
130 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
131 * status reply before sending
134 gint drop_x, drop_y; /* Position of drop */
137 #define DROP_ABORT_TIME 300000
139 #define ANIM_STEP_TIME 50
140 #define ANIM_STEP_LENGTH 50
141 #define ANIM_MIN_STEPS 5
142 #define ANIM_MAX_STEPS 10
146 GtkDragSourceInfo *info;
151 struct _GtkDragFindData
155 GdkDragContext *context;
156 GtkDragDestInfo *info;
159 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
160 gint x, gint y, guint32 time);
164 /* Enumeration for some targets we handle internally */
167 TARGET_MOTIF_SUCCESS = 0x40000000,
168 TARGET_MOTIF_FAILURE,
174 static GdkPixmap *default_icon_pixmap = NULL;
175 static GdkPixmap *default_icon_mask = NULL;
176 static GdkColormap *default_icon_colormap = NULL;
177 static gint default_icon_hot_x;
178 static gint default_icon_hot_y;
180 /* Forward declarations */
181 static void gtk_drag_get_event_actions (GdkEvent *event,
183 GdkDragAction actions,
184 GdkDragAction *suggested_action,
185 GdkDragAction *possible_actions);
186 static GdkCursor * gtk_drag_get_cursor (GdkDisplay *display,
187 GdkDragAction action);
188 static GtkWidget *gtk_drag_get_ipc_widget (GdkScreen *screen);
189 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
191 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
192 GdkEventExpose *event,
195 static void gtk_drag_selection_received (GtkWidget *widget,
196 GtkSelectionData *selection_data,
199 static void gtk_drag_find_widget (GtkWidget *widget,
200 GtkDragFindData *data);
201 static void gtk_drag_proxy_begin (GtkWidget *widget,
202 GtkDragDestInfo *dest_info,
204 static void gtk_drag_dest_realized (GtkWidget *widget);
205 static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
206 GtkWidget *previous_toplevel);
207 static void gtk_drag_dest_site_destroy (gpointer data);
208 static void gtk_drag_dest_leave (GtkWidget *widget,
209 GdkDragContext *context,
211 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
212 GdkDragContext *context,
216 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
217 GdkDragContext *context,
222 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
224 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
226 static void gtk_drag_clear_source_info (GdkDragContext *context);
228 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
231 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
233 static void gtk_drag_drop (GtkDragSourceInfo *info,
235 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
238 static void gtk_drag_cancel (GtkDragSourceInfo *info,
241 static gint gtk_drag_source_event_cb (GtkWidget *widget,
244 static void gtk_drag_source_site_destroy (gpointer data);
245 static void gtk_drag_selection_get (GtkWidget *widget,
246 GtkSelectionData *selection_data,
250 static gint gtk_drag_anim_timeout (gpointer data);
251 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
252 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
253 static void gtk_drag_update (GtkDragSourceInfo *info,
258 static gint gtk_drag_motion_cb (GtkWidget *widget,
259 GdkEventMotion *event,
261 static gint gtk_drag_key_cb (GtkWidget *widget,
264 static gint gtk_drag_button_release_cb (GtkWidget *widget,
265 GdkEventButton *event,
267 static gint gtk_drag_abort_timeout (gpointer data);
269 /************************
270 * Cursor and Icon data *
271 ************************/
273 #define action_ask_width 16
274 #define action_ask_height 16
275 static const guchar action_ask_bits[] = {
276 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
277 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
278 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
280 #define action_ask_mask_width 16
281 #define action_ask_mask_height 16
282 static const guchar action_ask_mask_bits[] = {
283 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
284 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
285 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
287 #define action_copy_width 16
288 #define action_copy_height 16
289 static const guchar action_copy_bits[] = {
290 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
291 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
292 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
294 #define action_copy_mask_width 16
295 #define action_copy_mask_height 16
296 static const guchar action_copy_mask_bits[] = {
297 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
298 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
299 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
301 #define action_move_width 16
302 #define action_move_height 16
303 static const guchar action_move_bits[] = {
304 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
305 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
306 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
308 #define action_move_mask_width 16
309 #define action_move_mask_height 16
310 static const guchar action_move_mask_bits[] = {
311 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
312 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
313 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
315 #define action_link_width 16
316 #define action_link_height 16
317 static const guchar action_link_bits[] = {
318 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
319 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
320 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
322 #define action_link_mask_width 16
323 #define action_link_mask_height 16
324 static const guchar action_link_mask_bits[] = {
325 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
326 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
327 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
329 #define action_none_width 16
330 #define action_none_height 16
331 static const guchar action_none_bits[] = {
332 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
333 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
334 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
336 #define action_none_mask_width 16
337 #define action_none_mask_height 16
338 static const guchar action_none_mask_bits[] = {
339 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
340 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
341 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
343 #define CURSOR_WIDTH 16
344 #define CURSOR_HEIGHT 16
347 GdkDragAction action;
352 { GDK_ACTION_DEFAULT, 0 },
353 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
354 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
355 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
356 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
357 { 0 , action_none_bits, action_none_mask_bits, NULL },
360 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
362 /*********************
363 * Utility functions *
364 *********************/
367 set_can_change_screen (GtkWidget *widget,
368 gboolean can_change_screen)
370 can_change_screen = can_change_screen != FALSE;
372 g_object_set_data (G_OBJECT (widget), "gtk-dnd-can-change-screen",
373 GUINT_TO_POINTER (can_change_screen));
377 get_can_change_screen (GtkWidget *widget)
379 return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
383 /*************************************************************
384 * gtk_drag_get_ipc_widget:
385 * Return a invisible, off-screen, override-redirect
390 *************************************************************/
393 gtk_drag_get_ipc_widget (GdkScreen *screen)
396 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
397 "gtk-dnd-ipc-widgets");
401 GSList *tmp = drag_widgets;
402 result = drag_widgets->data;
403 drag_widgets = drag_widgets->next;
404 g_object_set_data (G_OBJECT (screen),
405 "gtk-dnd-ipc-widgets",
407 g_slist_free_1 (tmp);
411 result = gtk_invisible_new_for_screen (screen);
412 gtk_widget_show (result);
418 /***************************************************************
419 * gtk_drag_release_ipc_widget:
420 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
422 * widget: the widget to release.
424 ***************************************************************/
427 gtk_drag_release_ipc_widget (GtkWidget *widget)
429 GdkScreen *screen = gtk_widget_get_screen (widget);
430 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
431 "gtk-dnd-ipc-widgets");
432 drag_widgets = g_slist_prepend (drag_widgets, widget);
433 g_object_set_data (G_OBJECT (screen),
434 "gtk-dnd-ipc-widgets",
439 gtk_drag_get_event_time (GdkEvent *event)
441 guint32 tm = GDK_CURRENT_TIME;
446 case GDK_MOTION_NOTIFY:
447 tm = event->motion.time; break;
448 case GDK_BUTTON_PRESS:
449 case GDK_2BUTTON_PRESS:
450 case GDK_3BUTTON_PRESS:
451 case GDK_BUTTON_RELEASE:
452 tm = event->button.time; break;
454 case GDK_KEY_RELEASE:
455 tm = event->key.time; break;
456 case GDK_ENTER_NOTIFY:
457 case GDK_LEAVE_NOTIFY:
458 tm = event->crossing.time; break;
459 case GDK_PROPERTY_NOTIFY:
460 tm = event->property.time; break;
461 case GDK_SELECTION_CLEAR:
462 case GDK_SELECTION_REQUEST:
463 case GDK_SELECTION_NOTIFY:
464 tm = event->selection.time; break;
465 case GDK_PROXIMITY_IN:
466 case GDK_PROXIMITY_OUT:
467 tm = event->proximity.time; break;
468 default: /* use current time */
476 gtk_drag_get_event_actions (GdkEvent *event,
478 GdkDragAction actions,
479 GdkDragAction *suggested_action,
480 GdkDragAction *possible_actions)
482 *suggested_action = 0;
483 *possible_actions = 0;
487 GdkModifierType state = 0;
491 case GDK_MOTION_NOTIFY:
492 state = event->motion.state;
494 case GDK_BUTTON_PRESS:
495 case GDK_2BUTTON_PRESS:
496 case GDK_3BUTTON_PRESS:
497 case GDK_BUTTON_RELEASE:
498 state = event->button.state;
501 case GDK_KEY_RELEASE:
502 state = event->key.state;
504 case GDK_ENTER_NOTIFY:
505 case GDK_LEAVE_NOTIFY:
506 state = event->crossing.state;
512 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
514 *suggested_action = GDK_ACTION_ASK;
515 *possible_actions = actions;
517 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
519 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
521 if (actions & GDK_ACTION_LINK)
523 *suggested_action = GDK_ACTION_LINK;
524 *possible_actions = GDK_ACTION_LINK;
527 else if (state & GDK_CONTROL_MASK)
529 if (actions & GDK_ACTION_COPY)
531 *suggested_action = GDK_ACTION_COPY;
532 *possible_actions = GDK_ACTION_COPY;
538 if (actions & GDK_ACTION_MOVE)
540 *suggested_action = GDK_ACTION_MOVE;
541 *possible_actions = GDK_ACTION_MOVE;
548 *possible_actions = actions;
550 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
551 *suggested_action = GDK_ACTION_ASK;
552 else if (actions & GDK_ACTION_COPY)
553 *suggested_action = GDK_ACTION_COPY;
554 else if (actions & GDK_ACTION_MOVE)
555 *suggested_action = GDK_ACTION_MOVE;
556 else if (actions & GDK_ACTION_LINK)
557 *suggested_action = GDK_ACTION_LINK;
562 *possible_actions = actions;
564 if (actions & GDK_ACTION_COPY)
565 *suggested_action = GDK_ACTION_COPY;
566 else if (actions & GDK_ACTION_MOVE)
567 *suggested_action = GDK_ACTION_MOVE;
568 else if (actions & GDK_ACTION_LINK)
569 *suggested_action = GDK_ACTION_LINK;
576 gtk_drag_get_cursor (GdkDisplay *display,
577 GdkDragAction action)
581 for (i = 0 ; i < n_drag_cursors - 1; i++)
582 if (drag_cursors[i].action == action)
584 if (drag_cursors[i].cursor != NULL)
586 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
588 gdk_cursor_unref (drag_cursors[i].cursor);
589 drag_cursors[i].cursor = NULL;
593 if (drag_cursors[i].cursor == NULL)
595 GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
596 GdkColor fg = { 0, 0x0000, 0x0000, 0x0000 };
597 GdkScreen *screen = gdk_display_get_default_screen (display);
598 GdkWindow *window = gdk_screen_get_root_window (screen);
601 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].bits, CURSOR_WIDTH, CURSOR_HEIGHT);
604 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].mask, CURSOR_WIDTH, CURSOR_HEIGHT);
606 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
608 g_object_unref (pixmap);
609 g_object_unref (mask);
612 return drag_cursors[i].cursor;
615 /********************
617 ********************/
619 /*************************************************************
621 * Get the data for a drag or drop
623 * context - drag context
624 * target - format to retrieve the data in.
625 * time - timestamp of triggering event.
628 *************************************************************/
631 gtk_drag_get_data (GtkWidget *widget,
632 GdkDragContext *context,
636 GtkWidget *selection_widget;
638 g_return_if_fail (GTK_IS_WIDGET (widget));
639 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
640 g_return_if_fail (!context->is_source);
642 selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
644 g_object_ref (context);
645 g_object_ref (widget);
647 g_signal_connect (selection_widget, "selection_received",
648 G_CALLBACK (gtk_drag_selection_received), widget);
650 g_object_set_data (G_OBJECT (selection_widget), "drag-context", context);
652 gtk_selection_convert (selection_widget,
653 gdk_drag_get_selection (context),
659 /*************************************************************
660 * gtk_drag_get_source_widget:
661 * Get the widget the was the source of this drag, if
662 * the drag originated from this application.
664 * context: The drag context for this drag
666 * The source widget, or NULL if the drag originated from
667 * a different application.
668 *************************************************************/
671 gtk_drag_get_source_widget (GdkDragContext *context)
675 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
676 g_return_val_if_fail (!context->is_source, NULL);
678 tmp_list = source_widgets;
681 GtkWidget *ipc_widget = tmp_list->data;
683 if (ipc_widget->window == context->source_window)
685 GtkDragSourceInfo *info;
686 info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
688 return info ? info->widget : NULL;
691 tmp_list = tmp_list->next;
697 /*************************************************************
699 * Notify the drag source that the transfer of data
702 * context: The drag context for this drag
703 * success: Was the data successfully transferred?
704 * time: The timestamp to use when notifying the destination.
706 *************************************************************/
709 gtk_drag_finish (GdkDragContext *context,
714 GdkAtom target = GDK_NONE;
716 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
717 g_return_if_fail (!context->is_source);
721 target = gdk_atom_intern ("DELETE", FALSE);
723 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
725 target = gdk_atom_intern (success ?
726 "XmTRANSFER_SUCCESS" :
727 "XmTRANSFER_FAILURE",
731 if (target != GDK_NONE)
733 GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
735 g_object_ref (context);
737 g_object_set_data (G_OBJECT (selection_widget), "drag-context", context);
738 g_signal_connect (selection_widget, "selection_received",
739 G_CALLBACK (gtk_drag_selection_received),
742 gtk_selection_convert (selection_widget,
743 gdk_drag_get_selection (context),
748 if (!(success && del))
749 gdk_drop_finish (context, success, time);
752 /*************************************************************
753 * gtk_drag_highlight_expose:
754 * Callback for expose_event for highlighted widgets.
760 *************************************************************/
763 gtk_drag_highlight_expose (GtkWidget *widget,
764 GdkEventExpose *event,
767 gint x, y, width, height;
769 if (GTK_WIDGET_DRAWABLE (widget))
771 if (GTK_WIDGET_NO_WINDOW (widget))
773 x = widget->allocation.x;
774 y = widget->allocation.y;
775 width = widget->allocation.width;
776 height = widget->allocation.height;
782 gdk_drawable_get_size (widget->window, &width, &height);
785 gtk_paint_shadow (widget->style, widget->window,
786 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
788 x, y, width, height);
790 gdk_draw_rectangle (widget->window,
791 widget->style->black_gc,
793 x, y, width - 1, height - 1);
799 /*************************************************************
800 * gtk_drag_highlight:
801 * Highlight the given widget in the default manner.
805 *************************************************************/
808 gtk_drag_highlight (GtkWidget *widget)
810 g_return_if_fail (GTK_IS_WIDGET (widget));
812 g_signal_connect_after (widget, "expose_event",
813 G_CALLBACK (gtk_drag_highlight_expose),
816 gtk_widget_queue_draw (widget);
819 /*************************************************************
820 * gtk_drag_unhighlight:
821 * Refresh the given widget to remove the highlight.
825 *************************************************************/
828 gtk_drag_unhighlight (GtkWidget *widget)
830 g_return_if_fail (GTK_IS_WIDGET (widget));
832 g_signal_handlers_disconnect_by_func (widget,
833 gtk_drag_highlight_expose,
836 gtk_widget_queue_draw (widget);
840 gtk_drag_dest_set_internal (GtkWidget *widget,
841 GtkDragDestSite *site)
843 GtkDragDestSite *old_site;
845 g_return_if_fail (widget != NULL);
847 /* HACK, do this in the destroy */
848 old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
851 g_signal_handlers_disconnect_by_func (widget,
852 gtk_drag_dest_realized,
854 g_signal_handlers_disconnect_by_func (widget,
855 gtk_drag_dest_hierarchy_changed,
859 if (GTK_WIDGET_REALIZED (widget))
860 gtk_drag_dest_realized (widget);
862 g_signal_connect (widget, "realize",
863 G_CALLBACK (gtk_drag_dest_realized), site);
864 g_signal_connect (widget, "hierarchy_changed",
865 G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
867 g_object_set_data_full (G_OBJECT (widget), "gtk-drag-dest",
868 site, gtk_drag_dest_site_destroy);
872 /*************************************************************
874 * Register a drop site, and possibly add default behaviors.
877 * flags: Which types of default drag behavior to use
878 * targets: Table of targets that can be accepted
879 * n_targets: Number of of entries in targets
882 *************************************************************/
885 gtk_drag_dest_set (GtkWidget *widget,
886 GtkDestDefaults flags,
887 const GtkTargetEntry *targets,
889 GdkDragAction actions)
891 GtkDragDestSite *site;
893 g_return_if_fail (GTK_IS_WIDGET (widget));
895 site = g_new (GtkDragDestSite, 1);
898 site->have_drag = FALSE;
900 site->target_list = gtk_target_list_new (targets, n_targets);
902 site->target_list = NULL;
903 site->actions = actions;
904 site->do_proxy = FALSE;
905 site->proxy_window = NULL;
907 gtk_drag_dest_set_internal (widget, site);
910 /*************************************************************
911 * gtk_drag_dest_set_proxy:
912 * Set up this widget to proxy drags elsewhere.
915 * proxy_window: window to which forward drag events
916 * protocol: Drag protocol which the dest widget accepts
917 * use_coordinates: If true, send the same coordinates to the
918 * destination, because it is a embedded
921 *************************************************************/
924 gtk_drag_dest_set_proxy (GtkWidget *widget,
925 GdkWindow *proxy_window,
926 GdkDragProtocol protocol,
927 gboolean use_coordinates)
929 GtkDragDestSite *site;
931 g_return_if_fail (GTK_IS_WIDGET (widget));
932 g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
934 site = g_new (GtkDragDestSite, 1);
937 site->have_drag = FALSE;
938 site->target_list = NULL;
940 site->proxy_window = proxy_window;
942 g_object_ref (proxy_window);
943 site->do_proxy = TRUE;
944 site->proxy_protocol = protocol;
945 site->proxy_coords = use_coordinates;
947 gtk_drag_dest_set_internal (widget, site);
950 /*************************************************************
951 * gtk_drag_dest_unset
952 * Unregister this widget as a drag target.
956 *************************************************************/
959 gtk_drag_dest_unset (GtkWidget *widget)
961 g_return_if_fail (GTK_IS_WIDGET (widget));
963 g_object_set_data (G_OBJECT (widget), "gtk-drag-dest", NULL);
967 * gtk_drag_dest_get_target_list:
968 * @widget: a #GtkWidget
970 * Returns the list of targets this widget can accept from
973 * Return value: the #GtkTargetList, or %NULL if none
976 gtk_drag_dest_get_target_list (GtkWidget *widget)
978 GtkDragDestSite *site;
980 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
982 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
984 return site ? site->target_list : NULL;
988 * gtk_drag_dest_set_target_list:
989 * @widget: a #GtkWidget that's a drag destination
990 * @target_list: list of droppable targets, or %NULL for none
992 * Sets the target types that this widget can accept from drag-and-drop.
993 * The widget must first be made into a drag destination with
994 * gtk_drag_dest_set().
997 gtk_drag_dest_set_target_list (GtkWidget *widget,
998 GtkTargetList *target_list)
1000 GtkDragDestSite *site;
1002 g_return_if_fail (GTK_IS_WIDGET (widget));
1004 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1008 g_warning ("can't set a target list on a widget until you've called gtk_drag_dest_set() to make the widget into a drag destination");
1013 gtk_target_list_ref (target_list);
1015 if (site->target_list)
1016 gtk_target_list_unref (site->target_list);
1018 site->target_list = target_list;
1022 /*************************************************************
1023 * _gtk_drag_dest_handle_event:
1024 * Called from widget event handling code on Drag events
1028 * toplevel: Toplevel widget that received the event
1031 *************************************************************/
1034 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1037 GtkDragDestInfo *info;
1038 GdkDragContext *context;
1040 g_return_if_fail (toplevel != NULL);
1041 g_return_if_fail (event != NULL);
1043 context = event->dnd.context;
1045 info = gtk_drag_get_dest_info (context, TRUE);
1047 /* Find the widget for the event */
1048 switch (event->type)
1050 case GDK_DRAG_ENTER:
1053 case GDK_DRAG_LEAVE:
1056 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1057 info->widget = NULL;
1061 case GDK_DRAG_MOTION:
1062 case GDK_DROP_START:
1064 GtkDragFindData data;
1067 if (event->type == GDK_DROP_START)
1069 info->dropped = TRUE;
1070 /* We send a leave here so that the widget unhighlights
1075 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1076 info->widget = NULL;
1080 gdk_window_get_origin (toplevel->window, &tx, &ty);
1082 data.x = event->dnd.x_root - tx;
1083 data.y = event->dnd.y_root - ty;
1084 data.context = context;
1087 data.toplevel = TRUE;
1088 data.callback = (event->type == GDK_DRAG_MOTION) ?
1089 gtk_drag_dest_motion : gtk_drag_dest_drop;
1090 data.time = event->dnd.time;
1092 gtk_drag_find_widget (toplevel, &data);
1094 if (info->widget && !data.found)
1096 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1097 info->widget = NULL;
1102 if (event->type == GDK_DRAG_MOTION)
1105 gdk_drag_status (context, 0, event->dnd.time);
1107 else if (event->type == GDK_DROP_START && !info->proxy_source)
1109 gdk_drop_reply (context, data.found, event->dnd.time);
1110 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1111 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1117 g_assert_not_reached ();
1122 * gtk_drag_dest_find_target:
1123 * @widget: drag destination widget
1124 * @context: drag context
1125 * @target_list: list of droppable targets, or %NULL to use
1126 * gtk_drag_dest_get_target_list (@widget).
1128 * Looks for a match between @context->targets and the
1129 * @dest_target_list, returning the first matching target, otherwise
1130 * returning %GDK_NONE. @dest_target_list should usually be the return
1131 * value from gtk_drag_dest_get_target_list(), but some widgets may
1132 * have different valid targets for different parts of the widget; in
1133 * that case, they will have to implement a drag_motion handler that
1134 * passes the correct target list to this function.
1136 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1139 gtk_drag_dest_find_target (GtkWidget *widget,
1140 GdkDragContext *context,
1141 GtkTargetList *target_list)
1144 GList *tmp_source = NULL;
1145 GtkWidget *source_widget;
1147 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1148 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1149 g_return_val_if_fail (!context->is_source, GDK_NONE);
1152 source_widget = gtk_drag_get_source_widget (context);
1154 if (target_list == NULL)
1155 target_list = gtk_drag_dest_get_target_list (widget);
1157 if (target_list == NULL)
1160 tmp_target = target_list->list;
1163 GtkTargetPair *pair = tmp_target->data;
1164 tmp_source = context->targets;
1167 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1169 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1170 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1171 return pair->target;
1175 tmp_source = tmp_source->next;
1177 tmp_target = tmp_target->next;
1184 gtk_drag_selection_received (GtkWidget *widget,
1185 GtkSelectionData *selection_data,
1189 GdkDragContext *context;
1190 GtkDragDestInfo *info;
1191 GtkWidget *drop_widget;
1195 context = g_object_get_data (G_OBJECT (widget), "drag-context");
1196 info = gtk_drag_get_dest_info (context, FALSE);
1198 if (info->proxy_data &&
1199 info->proxy_data->target == selection_data->target)
1201 gtk_selection_data_set (info->proxy_data,
1202 selection_data->type,
1203 selection_data->format,
1204 selection_data->data,
1205 selection_data->length);
1210 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1212 gtk_drag_finish (context, TRUE, FALSE, time);
1214 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1215 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1221 GtkDragDestSite *site;
1223 site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1225 if (site && site->target_list)
1229 if (gtk_target_list_find (site->target_list,
1230 selection_data->target,
1233 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1234 selection_data->length >= 0)
1235 g_signal_emit_by_name (drop_widget,
1236 "drag_data_received",
1237 context, info->drop_x, info->drop_y,
1244 g_signal_emit_by_name (drop_widget,
1245 "drag_data_received",
1246 context, info->drop_x, info->drop_y,
1251 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1254 gtk_drag_finish (context,
1255 (selection_data->length >= 0),
1256 (context->action == GDK_ACTION_MOVE),
1260 g_object_unref (drop_widget);
1263 g_signal_handlers_disconnect_by_func (widget,
1264 gtk_drag_selection_received,
1267 g_object_set_data (G_OBJECT (widget), "drag-context", NULL);
1268 g_object_unref (context);
1270 gtk_drag_release_ipc_widget (widget);
1274 prepend_and_ref_widget (GtkWidget *widget,
1277 GSList **slist_p = data;
1279 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1282 /*************************************************************
1283 * gtk_drag_find_widget:
1284 * Recursive callback used to locate widgets for
1285 * DRAG_MOTION and DROP_START events.
1289 *************************************************************/
1292 gtk_drag_find_widget (GtkWidget *widget,
1293 GtkDragFindData *data)
1295 GtkAllocation new_allocation;
1296 gint allocation_to_window_x = 0;
1297 gint allocation_to_window_y = 0;
1301 if (data->found || !GTK_WIDGET_MAPPED (widget))
1304 /* Note that in the following code, we only count the
1305 * position as being inside a WINDOW widget if it is inside
1306 * widget->window; points that are outside of widget->window
1307 * but within the allocation are not counted. This is consistent
1308 * with the way we highlight drag targets.
1310 * data->x,y are relative to widget->parent->window (if
1311 * widget is not a toplevel, widget->window otherwise).
1312 * We compute the allocation of widget in the same coordinates,
1313 * clipping to widget->window, and all intermediate
1314 * windows. If data->x,y is inside that, then we translate
1315 * our coordinates to be relative to widget->window and
1318 new_allocation = widget->allocation;
1323 GdkWindow *window = widget->window;
1325 /* Compute the offset from allocation-relative to
1326 * window-relative coordinates.
1328 allocation_to_window_x = widget->allocation.x;
1329 allocation_to_window_y = widget->allocation.y;
1331 if (!GTK_WIDGET_NO_WINDOW (widget))
1333 /* The allocation is relative to the parent window for
1334 * window widgets, not to widget->window.
1336 gdk_window_get_position (window, &tx, &ty);
1338 allocation_to_window_x -= tx;
1339 allocation_to_window_y -= ty;
1342 new_allocation.x = 0 + allocation_to_window_x;
1343 new_allocation.y = 0 + allocation_to_window_y;
1345 while (window && window != widget->parent->window)
1347 GdkRectangle window_rect = { 0, 0, 0, 0 };
1349 gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
1351 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1353 gdk_window_get_position (window, &tx, &ty);
1354 new_allocation.x += tx;
1356 new_allocation.y += ty;
1359 window = gdk_window_get_parent (window);
1362 if (!window) /* Window and widget heirarchies didn't match. */
1366 if (data->toplevel ||
1367 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1368 (data->x < new_allocation.x + new_allocation.width) &&
1369 (data->y < new_allocation.y + new_allocation.height)))
1371 /* First, check if the drag is in a valid drop site in
1372 * one of our children
1374 if (GTK_IS_CONTAINER (widget))
1376 GtkDragFindData new_data = *data;
1377 GSList *children = NULL;
1380 new_data.x -= x_offset;
1381 new_data.y -= y_offset;
1382 new_data.found = FALSE;
1383 new_data.toplevel = FALSE;
1385 /* need to reference children temporarily in case the
1386 * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1388 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1389 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1391 if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1392 gtk_drag_find_widget (tmp_list->data, &new_data);
1393 g_object_unref (tmp_list->data);
1395 g_slist_free (children);
1397 data->found = new_data.found;
1400 /* If not, and this widget is registered as a drop site, check to
1401 * emit "drag_motion" to check if we are actually in
1405 g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1407 data->found = data->callback (widget,
1409 data->x - x_offset - allocation_to_window_x,
1410 data->y - y_offset - allocation_to_window_y,
1412 /* If so, send a "drag_leave" to the last widget */
1415 if (data->info->widget && data->info->widget != widget)
1417 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1419 data->info->widget = widget;
1426 gtk_drag_proxy_begin (GtkWidget *widget,
1427 GtkDragDestInfo *dest_info,
1430 GtkDragSourceInfo *source_info;
1432 GdkDragContext *context;
1433 GtkWidget *ipc_widget;
1435 if (dest_info->proxy_source)
1437 gdk_drag_abort (dest_info->proxy_source->context, time);
1438 gtk_drag_source_info_destroy (dest_info->proxy_source);
1439 dest_info->proxy_source = NULL;
1442 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1443 context = gdk_drag_begin (ipc_widget->window,
1444 dest_info->context->targets);
1446 source_info = gtk_drag_get_source_info (context, TRUE);
1448 source_info->ipc_widget = ipc_widget;
1449 source_info->widget = gtk_widget_ref (widget);
1451 source_info->target_list = gtk_target_list_new (NULL, 0);
1452 tmp_list = dest_info->context->targets;
1455 gtk_target_list_add (source_info->target_list,
1456 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1457 tmp_list = tmp_list->next;
1460 source_info->proxy_dest = dest_info;
1462 g_signal_connect (ipc_widget,
1464 G_CALLBACK (gtk_drag_selection_get),
1467 dest_info->proxy_source = source_info;
1471 gtk_drag_dest_info_destroy (gpointer data)
1473 GtkDragDestInfo *info = data;
1478 static GtkDragDestInfo *
1479 gtk_drag_get_dest_info (GdkDragContext *context,
1482 GtkDragDestInfo *info;
1483 static GQuark info_quark = 0;
1485 info_quark = g_quark_from_static_string ("gtk-dest-info");
1487 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1488 if (!info && create)
1490 info = g_new (GtkDragDestInfo, 1);
1491 info->widget = NULL;
1492 info->context = context;
1493 info->proxy_source = NULL;
1494 info->proxy_data = NULL;
1495 info->dropped = FALSE;
1496 info->proxy_drop_wait = FALSE;
1497 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1498 info, gtk_drag_dest_info_destroy);
1504 static GQuark dest_info_quark = 0;
1506 static GtkDragSourceInfo *
1507 gtk_drag_get_source_info (GdkDragContext *context,
1510 GtkDragSourceInfo *info;
1511 if (!dest_info_quark)
1512 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1514 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1515 if (!info && create)
1517 info = g_new0 (GtkDragSourceInfo, 1);
1518 info->context = context;
1519 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1526 gtk_drag_clear_source_info (GdkDragContext *context)
1528 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1532 gtk_drag_dest_realized (GtkWidget *widget)
1534 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1536 if (GTK_WIDGET_TOPLEVEL (toplevel))
1537 gdk_window_register_dnd (toplevel->window);
1541 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1542 GtkWidget *previous_toplevel)
1544 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1546 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1547 gdk_window_register_dnd (toplevel->window);
1551 gtk_drag_dest_site_destroy (gpointer data)
1553 GtkDragDestSite *site = data;
1555 if (site->proxy_window)
1556 g_object_unref (site->proxy_window);
1558 if (site->target_list)
1559 gtk_target_list_unref (site->target_list);
1565 * Default drag handlers
1568 gtk_drag_dest_leave (GtkWidget *widget,
1569 GdkDragContext *context,
1572 GtkDragDestSite *site;
1574 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1575 g_return_if_fail (site != NULL);
1579 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1581 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1583 gdk_drag_abort (info->proxy_source->context, time);
1584 gtk_drag_source_info_destroy (info->proxy_source);
1585 info->proxy_source = NULL;
1592 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1593 gtk_drag_unhighlight (widget);
1595 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1596 g_signal_emit_by_name (widget, "drag_leave",
1599 site->have_drag = FALSE;
1604 gtk_drag_dest_motion (GtkWidget *widget,
1605 GdkDragContext *context,
1610 GtkDragDestSite *site;
1611 GdkDragAction action = 0;
1614 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1615 g_return_val_if_fail (site != NULL, FALSE);
1620 GdkEvent *current_event;
1621 GdkWindow *dest_window;
1622 GdkDragProtocol proto;
1624 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1626 if (!info->proxy_source || info->proxy_source->widget != widget)
1627 gtk_drag_proxy_begin (widget, info, time);
1629 current_event = gtk_get_current_event ();
1631 if (site->proxy_window)
1633 dest_window = site->proxy_window;
1634 proto = site->proxy_protocol;
1638 gdk_drag_find_window_for_screen (info->proxy_source->context,
1640 gdk_drawable_get_screen (current_event->dnd.window),
1641 current_event->dnd.x_root,
1642 current_event->dnd.y_root,
1643 &dest_window, &proto);
1646 gdk_drag_motion (info->proxy_source->context,
1648 current_event->dnd.x_root,
1649 current_event->dnd.y_root,
1650 context->suggested_action,
1651 context->actions, time);
1653 if (!site->proxy_window && dest_window)
1654 g_object_unref (dest_window);
1656 selection = gdk_drag_get_selection (info->proxy_source->context);
1658 selection != gdk_drag_get_selection (info->context))
1659 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1661 gdk_event_free (current_event);
1666 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1668 if (context->suggested_action & site->actions)
1669 action = context->suggested_action;
1676 if ((site->actions & (1 << i)) &&
1677 (context->actions & (1 << i)))
1685 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1687 if (!site->have_drag)
1689 site->have_drag = TRUE;
1690 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1691 gtk_drag_highlight (widget);
1694 gdk_drag_status (context, action, time);
1698 gdk_drag_status (context, 0, time);
1703 g_signal_emit_by_name (widget, "drag_motion",
1704 context, x, y, time, &retval);
1706 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1710 gtk_drag_dest_drop (GtkWidget *widget,
1711 GdkDragContext *context,
1716 GtkDragDestSite *site;
1717 GtkDragDestInfo *info;
1719 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1720 g_return_val_if_fail (site != NULL, FALSE);
1722 info = gtk_drag_get_dest_info (context, FALSE);
1723 g_return_val_if_fail (info != NULL, FALSE);
1730 if (info->proxy_source ||
1731 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1733 gtk_drag_drop (info->proxy_source, time);
1737 /* We need to synthesize a motion event, wait for a status,
1738 * and, if we get a good one, do a drop.
1741 GdkEvent *current_event;
1743 GdkWindow *dest_window;
1744 GdkDragProtocol proto;
1746 gtk_drag_proxy_begin (widget, info, time);
1747 info->proxy_drop_wait = TRUE;
1748 info->proxy_drop_time = time;
1750 current_event = gtk_get_current_event ();
1752 if (site->proxy_window)
1754 dest_window = site->proxy_window;
1755 proto = site->proxy_protocol;
1759 gdk_drag_find_window_for_screen (info->proxy_source->context,
1761 gdk_drawable_get_screen (current_event->dnd.window),
1762 current_event->dnd.x_root,
1763 current_event->dnd.y_root,
1764 &dest_window, &proto);
1767 gdk_drag_motion (info->proxy_source->context,
1769 current_event->dnd.x_root,
1770 current_event->dnd.y_root,
1771 context->suggested_action,
1772 context->actions, time);
1774 if (!site->proxy_window && dest_window)
1775 g_object_unref (dest_window);
1777 selection = gdk_drag_get_selection (info->proxy_source->context);
1779 selection != gdk_drag_get_selection (info->context))
1780 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1782 gdk_event_free (current_event);
1791 if (site->flags & GTK_DEST_DEFAULT_DROP)
1793 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1795 if (target == GDK_NONE)
1797 gtk_drag_finish (context, FALSE, FALSE, time);
1801 gtk_drag_get_data (widget, context, target, time);
1804 g_signal_emit_by_name (widget, "drag_drop",
1805 context, x, y, time, &retval);
1807 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1815 /*************************************************************
1816 * gtk_drag_begin: Start a drag operation
1819 * widget: Widget from which drag starts
1820 * handlers: List of handlers to supply the data for the drag
1821 * button: Button user used to start drag
1822 * time: Time of event starting drag
1825 *************************************************************/
1828 gtk_drag_begin (GtkWidget *widget,
1829 GtkTargetList *target_list,
1830 GdkDragAction actions,
1834 GtkDragSourceInfo *info;
1835 GList *targets = NULL;
1837 guint32 time = GDK_CURRENT_TIME;
1838 GdkDragAction possible_actions, suggested_action;
1839 GdkDragContext *context;
1840 GtkWidget *ipc_widget;
1842 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1843 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1844 g_return_val_if_fail (target_list != NULL, NULL);
1847 time = gdk_event_get_time (event);
1849 tmp_list = g_list_last (target_list->list);
1852 GtkTargetPair *pair = tmp_list->data;
1853 targets = g_list_prepend (targets,
1854 GINT_TO_POINTER (pair->target));
1855 tmp_list = tmp_list->prev;
1858 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1859 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1861 context = gdk_drag_begin (ipc_widget->window, targets);
1862 g_list_free (targets);
1864 info = gtk_drag_get_source_info (context, TRUE);
1866 info->ipc_widget = ipc_widget;
1867 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", info);
1869 info->widget = gtk_widget_ref (widget);
1871 info->button = button;
1872 info->target_list = target_list;
1873 gtk_target_list_ref (target_list);
1875 info->possible_actions = actions;
1877 info->cursor = NULL;
1878 info->status = GTK_DRAG_STATUS_DRAG;
1879 info->last_event = NULL;
1880 info->selections = NULL;
1881 info->icon_window = NULL;
1882 info->destroy_icon = FALSE;
1884 gtk_drag_get_event_actions (event, info->button, actions,
1885 &suggested_action, &possible_actions);
1887 info->cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), suggested_action);
1889 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1890 * the drag icon, it will be in the right place
1892 if (event && event->type == GDK_MOTION_NOTIFY)
1894 info->cur_screen = gtk_widget_get_screen (widget);
1895 info->cur_x = event->motion.x_root;
1896 info->cur_y = event->motion.y_root;
1900 gdk_display_get_pointer (gtk_widget_get_display (widget),
1901 &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
1904 g_signal_emit_by_name (widget, "drag_begin",
1907 if (event && event->type == GDK_MOTION_NOTIFY)
1908 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1910 info->start_x = info->cur_x;
1911 info->start_y = info->cur_y;
1913 g_signal_connect (info->ipc_widget, "button_release_event",
1914 G_CALLBACK (gtk_drag_button_release_cb), info);
1915 g_signal_connect (info->ipc_widget, "motion_notify_event",
1916 G_CALLBACK (gtk_drag_motion_cb), info);
1917 g_signal_connect (info->ipc_widget, "key_press_event",
1918 G_CALLBACK (gtk_drag_key_cb), info);
1919 g_signal_connect (info->ipc_widget, "key_release_event",
1920 G_CALLBACK (gtk_drag_key_cb), info);
1921 g_signal_connect (info->ipc_widget, "selection_get",
1922 G_CALLBACK (gtk_drag_selection_get), info);
1924 /* We use a GTK grab here to override any grabs that the widget
1925 * we are dragging from might have held
1927 gtk_grab_add (info->ipc_widget);
1928 if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1929 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1930 GDK_BUTTON_RELEASE_MASK, NULL,
1931 info->cursor, time) == 0)
1933 if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1935 gtk_drag_cancel (info, time);
1940 info->have_grab = TRUE;
1941 info->grab_time = time;
1943 return info->context;
1946 /*************************************************************
1947 * gtk_drag_source_set:
1948 * Register a drop site, and possibly add default behaviors.
1951 * start_button_mask: Mask of allowed buttons to start drag
1952 * targets: Table of targets for this source
1954 * actions: Actions allowed for this source
1956 *************************************************************/
1959 gtk_drag_source_set (GtkWidget *widget,
1960 GdkModifierType start_button_mask,
1961 const GtkTargetEntry *targets,
1963 GdkDragAction actions)
1965 GtkDragSourceSite *site;
1967 g_return_if_fail (GTK_IS_WIDGET (widget));
1969 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1971 gtk_widget_add_events (widget,
1972 gtk_widget_get_events (widget) |
1973 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1974 GDK_BUTTON_MOTION_MASK);
1978 if (site->target_list)
1979 gtk_target_list_unref (site->target_list);
1983 site = g_new0 (GtkDragSourceSite, 1);
1985 site->icon_type = GTK_IMAGE_EMPTY;
1987 g_signal_connect (widget, "button_press_event",
1988 G_CALLBACK (gtk_drag_source_event_cb),
1990 g_signal_connect (widget, "motion_notify_event",
1991 G_CALLBACK (gtk_drag_source_event_cb),
1994 g_object_set_data_full (G_OBJECT (widget),
1996 site, gtk_drag_source_site_destroy);
1999 site->start_button_mask = start_button_mask;
2002 site->target_list = gtk_target_list_new (targets, n_targets);
2004 site->target_list = NULL;
2006 site->actions = actions;
2010 /*************************************************************
2011 * gtk_drag_source_unset
2012 * Unregister this widget as a drag source.
2016 *************************************************************/
2019 gtk_drag_source_unset (GtkWidget *widget)
2021 GtkDragSourceSite *site;
2023 g_return_if_fail (GTK_IS_WIDGET (widget));
2025 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2029 g_signal_handlers_disconnect_by_func (widget,
2030 gtk_drag_source_event_cb,
2032 g_signal_handlers_disconnect_by_func (widget,
2033 gtk_drag_source_event_cb,
2035 g_object_set_data (G_OBJECT (widget), "gtk-site-data", NULL);
2040 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2042 switch (site->icon_type)
2044 case GTK_IMAGE_EMPTY:
2046 case GTK_IMAGE_PIXMAP:
2047 if (site->icon_data.pixmap.pixmap)
2048 g_object_unref (site->icon_data.pixmap.pixmap);
2049 if (site->icon_mask)
2050 g_object_unref (site->icon_mask);
2052 case GTK_IMAGE_PIXBUF:
2053 g_object_unref (site->icon_data.pixbuf.pixbuf);
2055 case GTK_IMAGE_STOCK:
2056 g_free (G_OBJECT (site->icon_data.stock.stock_id));
2059 g_assert_not_reached();
2062 site->icon_type = GTK_IMAGE_EMPTY;
2065 g_object_unref (site->colormap);
2066 site->colormap = NULL;
2070 * gtk_drag_source_set_icon:
2071 * @widget: a #GtkWidget
2072 * @colormap: the colormap of the icon
2073 * @pixmap: the image data for the icon
2074 * @mask: the transparency mask for an image.
2076 * Sets the icon that will be used for drags from a particular widget
2077 * from a pixmap/mask. GTK+ retains references for the arguments, and
2078 * will release them when they are no longer needed.
2079 * Use gtk_drag_source_set_icon_pixbuf() instead.
2082 gtk_drag_source_set_icon (GtkWidget *widget,
2083 GdkColormap *colormap,
2087 GtkDragSourceSite *site;
2089 g_return_if_fail (GTK_IS_WIDGET (widget));
2090 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2091 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2092 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2094 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2095 g_return_if_fail (site != NULL);
2097 g_object_ref (colormap);
2098 g_object_ref (pixmap);
2100 g_object_ref (mask);
2102 gtk_drag_source_unset_icon (site);
2104 site->icon_type = GTK_IMAGE_PIXMAP;
2106 site->icon_data.pixmap.pixmap = pixmap;
2107 site->icon_mask = mask;
2108 site->colormap = colormap;
2112 * gtk_drag_source_set_icon_pixbuf:
2113 * @widget: a #GtkWidget
2114 * @pixbuf: the #GdkPixbuf for the drag icon
2116 * Sets the icon that will be used for drags from a particular widget
2117 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2118 * release it when it is no longer needed.
2121 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2124 GtkDragSourceSite *site;
2126 g_return_if_fail (GTK_IS_WIDGET (widget));
2127 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2129 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2130 g_return_if_fail (site != NULL);
2131 g_object_ref (pixbuf);
2133 gtk_drag_source_unset_icon (site);
2135 site->icon_type = GTK_IMAGE_PIXBUF;
2136 site->icon_data.pixbuf.pixbuf = pixbuf;
2140 * gtk_drag_source_set_icon_stock:
2141 * @widget: a #GtkWidget
2142 * @stock_id: the ID of the stock icon to use
2144 * Sets the icon that will be used for drags from a particular source
2148 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2149 const gchar *stock_id)
2151 GtkDragSourceSite *site;
2153 g_return_if_fail (GTK_IS_WIDGET (widget));
2154 g_return_if_fail (stock_id != NULL);
2156 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2157 g_return_if_fail (site != NULL);
2159 gtk_drag_source_unset_icon (site);
2161 site->icon_data.stock.stock_id = g_strdup (stock_id);
2165 gtk_drag_get_icon (GtkDragSourceInfo *info,
2166 GtkWidget **icon_window,
2170 if (get_can_change_screen (info->icon_window))
2171 gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2174 if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2176 if (!info->fallback_icon)
2178 gint save_hot_x, save_hot_y;
2179 gboolean save_destroy_icon;
2180 GtkWidget *save_icon_window;
2182 /* HACK to get the appropriate icon
2184 save_icon_window = info->icon_window;
2185 save_hot_x = info->hot_x;
2186 save_hot_y = info->hot_x;
2187 save_destroy_icon = info->destroy_icon;
2189 info->icon_window = NULL;
2190 gtk_drag_set_icon_default (info->context);
2191 info->fallback_icon = info->icon_window;
2193 info->icon_window = save_icon_window;
2194 info->hot_x = save_hot_x;
2195 info->hot_y = save_hot_y;
2196 info->destroy_icon = save_destroy_icon;
2199 gtk_widget_hide (info->icon_window);
2201 *icon_window = info->fallback_icon;
2202 gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2204 if (!default_icon_pixmap)
2211 *hot_x = default_icon_hot_x;
2212 *hot_y = default_icon_hot_y;
2217 if (info->fallback_icon)
2218 gtk_widget_hide (info->fallback_icon);
2220 *icon_window = info->icon_window;
2221 *hot_x = info->hot_x;
2222 *hot_y = info->hot_y;
2227 gtk_drag_update_icon (GtkDragSourceInfo *info)
2229 if (info->icon_window)
2231 GtkWidget *icon_window;
2234 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2236 gtk_window_move (GTK_WINDOW (icon_window),
2237 info->cur_x - hot_x,
2238 info->cur_y - hot_y);
2240 if (GTK_WIDGET_VISIBLE (icon_window))
2241 gdk_window_raise (icon_window->window);
2243 gtk_widget_show (icon_window);
2248 gtk_drag_set_icon_window (GdkDragContext *context,
2252 gboolean destroy_on_release)
2254 GtkDragSourceInfo *info;
2256 info = gtk_drag_get_source_info (context, FALSE);
2257 gtk_drag_remove_icon (info);
2260 gtk_widget_ref (widget);
2262 info->icon_window = widget;
2263 info->hot_x = hot_x;
2264 info->hot_y = hot_y;
2265 info->destroy_icon = destroy_on_release;
2267 gtk_drag_update_icon (info);
2271 * gtk_drag_set_icon_widget:
2272 * @context: the context for a drag. (This must be called
2273 with a context for the source side of a drag)
2274 * @widget: a toplevel window to use as an icon.
2275 * @hot_x: the X offset within @widget of the hotspot.
2276 * @hot_y: the Y offset within @widget of the hotspot.
2278 * Changes the icon for a widget to a given widget. GTK+
2279 * will not destroy the icon, so if you don't want
2280 * it to persist, you should connect to the "drag_end"
2281 * signal and destroy it yourself.
2284 gtk_drag_set_icon_widget (GdkDragContext *context,
2289 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2290 g_return_if_fail (context->is_source);
2291 g_return_if_fail (GTK_IS_WIDGET (widget));
2293 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2297 icon_window_realize (GtkWidget *window,
2303 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
2304 gtk_widget_get_colormap (window),
2305 &pixmap, &mask, 128);
2307 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2310 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2312 g_object_unref (G_OBJECT (pixmap));
2315 g_object_unref (G_OBJECT (mask));
2319 set_icon_stock_pixbuf (GdkDragContext *context,
2320 const gchar *stock_id,
2329 g_return_if_fail (context != NULL);
2330 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2331 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2333 screen = gdk_drawable_get_screen (context->source_window);
2335 /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
2336 gtk_widget_push_colormap (NULL);
2337 window = gtk_window_new (GTK_WINDOW_POPUP);
2338 gtk_window_set_screen (GTK_WINDOW (window), screen);
2339 set_can_change_screen (window, TRUE);
2340 gtk_widget_pop_colormap ();
2342 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2343 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2347 pixbuf = gtk_widget_render_icon (window, stock_id,
2348 GTK_ICON_SIZE_DND, NULL);
2352 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2353 gtk_widget_destroy (window);
2359 g_object_ref (pixbuf);
2361 width = gdk_pixbuf_get_width (pixbuf);
2362 height = gdk_pixbuf_get_width (pixbuf);
2364 gtk_widget_set_size_request (window,
2365 gdk_pixbuf_get_width (pixbuf),
2366 gdk_pixbuf_get_height (pixbuf));
2368 g_signal_connect_closure (window, "realize",
2369 g_cclosure_new (G_CALLBACK (icon_window_realize),
2371 (GClosureNotify)g_object_unref),
2374 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2378 * gtk_drag_set_icon_pixbuf:
2379 * @context: the context for a drag. (This must be called
2380 * with a context for the source side of a drag)
2381 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2382 * @hot_x: the X offset within @widget of the hotspot.
2383 * @hot_y: the Y offset within @widget of the hotspot.
2385 * Sets @pixbuf as the icon for a given drag.
2388 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2393 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2394 g_return_if_fail (context->is_source);
2395 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2397 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2401 * gtk_drag_set_icon_stock:
2402 * @context: the context for a drag. (This must be called
2403 * with a context for the source side of a drag)
2404 * @stock_id: the ID of the stock icon to use for the drag.
2405 * @hot_x: the X offset within the icon of the hotspot.
2406 * @hot_y: the Y offset within the icon of the hotspot.
2408 * Sets the the icon for a given drag from a stock ID.
2411 gtk_drag_set_icon_stock (GdkDragContext *context,
2412 const gchar *stock_id,
2416 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2417 g_return_if_fail (context->is_source);
2418 g_return_if_fail (stock_id != NULL);
2420 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2424 * gtk_drag_set_icon_pixmap:
2425 * @context: the context for a drag. (This must be called
2426 * with a context for the source side of a drag)
2427 * @colormap: the colormap of the icon
2428 * @pixmap: the image data for the icon
2429 * @mask: the transparency mask for the icon
2430 * @hot_x: the X offset within @pixmap of the hotspot.
2431 * @hot_y: the Y offset within @pixmap of the hotspot.
2433 * Sets @pixmap as the icon for a given drag. GTK+ retains
2434 * references for the arguments, and will release them when
2435 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2436 * will be more convenient to use.
2439 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2440 GdkColormap *colormap,
2450 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2451 g_return_if_fail (context->is_source);
2452 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2453 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2454 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2456 screen = gdk_colormap_get_screen (colormap);
2458 g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
2459 g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
2461 gdk_drawable_get_size (pixmap, &width, &height);
2463 gtk_widget_push_colormap (colormap);
2465 window = gtk_window_new (GTK_WINDOW_POPUP);
2466 gtk_window_set_screen (GTK_WINDOW (window), screen);
2467 set_can_change_screen (window, FALSE);
2468 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2469 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2471 gtk_widget_pop_colormap ();
2473 gtk_widget_set_size_request (window, width, height);
2474 gtk_widget_realize (window);
2476 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2479 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2481 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2485 * gtk_drag_set_icon_default:
2486 * @context: the context for a drag. (This must be called
2487 with a context for the source side of a drag)
2489 * Sets the icon for a particular drag to the default
2493 gtk_drag_set_icon_default (GdkDragContext *context)
2495 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2496 g_return_if_fail (context->is_source);
2498 if (!default_icon_pixmap)
2499 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2501 gtk_drag_set_icon_pixmap (context,
2502 default_icon_colormap,
2503 default_icon_pixmap,
2506 default_icon_hot_y);
2510 * gtk_drag_set_default_icon:
2511 * @colormap: the colormap of the icon
2512 * @pixmap: the image data for the icon
2513 * @mask: the transparency mask for an image.
2514 * @hot_x: The X offset within @widget of the hotspot.
2515 * @hot_y: The Y offset within @widget of the hotspot.
2517 * Changes the default drag icon. GTK+ retains references for the
2518 * arguments, and will release them when they are no longer needed.
2519 * This function is obsolete. The default icon should now be changed
2520 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2523 gtk_drag_set_default_icon (GdkColormap *colormap,
2529 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2530 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2531 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2533 if (default_icon_colormap)
2534 g_object_unref (default_icon_colormap);
2535 if (default_icon_pixmap)
2536 g_object_unref (default_icon_pixmap);
2537 if (default_icon_mask)
2538 g_object_unref (default_icon_mask);
2540 default_icon_colormap = colormap;
2541 g_object_ref (colormap);
2543 default_icon_pixmap = pixmap;
2544 g_object_ref (pixmap);
2546 default_icon_mask = mask;
2548 g_object_ref (mask);
2550 default_icon_hot_x = hot_x;
2551 default_icon_hot_y = hot_y;
2555 /*************************************************************
2556 * _gtk_drag_source_handle_event:
2557 * Called from widget event handling code on Drag events
2561 * toplevel: Toplevel widget that received the event
2564 *************************************************************/
2567 _gtk_drag_source_handle_event (GtkWidget *widget,
2570 GtkDragSourceInfo *info;
2571 GdkDragContext *context;
2573 g_return_if_fail (widget != NULL);
2574 g_return_if_fail (event != NULL);
2576 context = event->dnd.context;
2577 info = gtk_drag_get_source_info (context, FALSE);
2581 switch (event->type)
2583 case GDK_DRAG_STATUS:
2587 if (info->proxy_dest)
2589 if (!event->dnd.send_event)
2591 if (info->proxy_dest->proxy_drop_wait)
2593 gboolean result = context->action != 0;
2595 /* Aha - we can finally pass the MOTIF DROP on... */
2596 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2598 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2600 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2604 gdk_drag_status (info->proxy_dest->context,
2605 event->dnd.context->action,
2610 else if (info->have_grab)
2612 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2613 event->dnd.context->action);
2614 if (info->cursor != cursor)
2616 gdk_pointer_grab (widget->window, FALSE,
2617 GDK_POINTER_MOTION_MASK |
2618 GDK_POINTER_MOTION_HINT_MASK |
2619 GDK_BUTTON_RELEASE_MASK,
2621 cursor, info->grab_time);
2622 info->cursor = cursor;
2625 if (info->last_event)
2627 gtk_drag_update (info,
2628 info->cur_screen, info->cur_x, info->cur_y,
2630 info->last_event = NULL;
2636 case GDK_DROP_FINISHED:
2637 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2640 g_assert_not_reached ();
2644 /*************************************************************
2645 * gtk_drag_source_check_selection:
2646 * Check if we've set up handlers/claimed the selection
2647 * for a given drag. If not, add them.
2651 *************************************************************/
2654 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2660 tmp_list = info->selections;
2663 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2665 tmp_list = tmp_list->next;
2668 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2672 info->selections = g_list_prepend (info->selections,
2673 GUINT_TO_POINTER (selection));
2675 tmp_list = info->target_list->list;
2678 GtkTargetPair *pair = tmp_list->data;
2680 gtk_selection_add_target (info->ipc_widget,
2684 tmp_list = tmp_list->next;
2687 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2689 gtk_selection_add_target (info->ipc_widget,
2691 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2692 TARGET_MOTIF_SUCCESS);
2693 gtk_selection_add_target (info->ipc_widget,
2695 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2696 TARGET_MOTIF_FAILURE);
2699 gtk_selection_add_target (info->ipc_widget,
2701 gdk_atom_intern ("DELETE", FALSE),
2705 /*************************************************************
2706 * gtk_drag_drop_finished:
2707 * Clean up from the drag, and display snapback, if necessary.
2713 *************************************************************/
2716 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2720 gtk_drag_source_release_selections (info, time);
2722 if (info->proxy_dest)
2724 /* The time from the event isn't reliable for Xdnd drags */
2725 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2726 info->proxy_dest->proxy_drop_time);
2727 gtk_drag_source_info_destroy (info);
2733 gtk_drag_source_info_destroy (info);
2737 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2741 anim->n_steps = MAX (info->cur_x - info->start_x,
2742 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2743 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2745 info->cur_screen = gtk_widget_get_screen (info->widget);
2746 gtk_drag_update_icon (info);
2748 /* Mark the context as dead, so if the destination decides
2749 * to respond really late, we still are OK.
2751 gtk_drag_clear_source_info (info->context);
2752 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2758 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2761 GdkDisplay *display = gtk_widget_get_display (info->widget);
2762 GList *tmp_list = info->selections;
2766 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2767 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2768 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2770 tmp_list = tmp_list->next;
2773 g_list_free (info->selections);
2774 info->selections = NULL;
2777 /*************************************************************
2779 * Send a drop event.
2783 *************************************************************/
2786 gtk_drag_drop (GtkDragSourceInfo *info,
2789 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2791 GtkSelectionData selection_data;
2793 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2795 tmp_list = info->target_list->list;
2798 GtkTargetPair *pair = tmp_list->data;
2800 if (pair->target == target)
2802 selection_data.selection = GDK_NONE;
2803 selection_data.target = target;
2804 selection_data.data = NULL;
2805 selection_data.length = -1;
2807 g_signal_emit_by_name (info->widget, "drag_data_get",
2808 info->context, &selection_data,
2812 /* FIXME: Should we check for length >= 0 here? */
2813 gtk_drag_drop_finished (info, TRUE, time);
2816 tmp_list = tmp_list->next;
2818 gtk_drag_drop_finished (info, FALSE, time);
2822 if (info->icon_window)
2823 gtk_widget_hide (info->icon_window);
2825 gdk_drag_drop (info->context, time);
2826 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2827 gtk_drag_abort_timeout,
2833 * Source side callbacks.
2837 gtk_drag_source_event_cb (GtkWidget *widget,
2841 GtkDragSourceSite *site;
2842 gboolean retval = FALSE;
2843 site = (GtkDragSourceSite *)data;
2845 switch (event->type)
2847 case GDK_BUTTON_PRESS:
2848 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2850 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2851 site->x = event->button.x;
2852 site->y = event->button.y;
2856 case GDK_BUTTON_RELEASE:
2857 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2858 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2861 case GDK_MOTION_NOTIFY:
2862 if (site->state & event->motion.state & site->start_button_mask)
2864 /* FIXME: This is really broken and can leave us
2870 if (site->state & event->motion.state &
2871 GDK_BUTTON1_MASK << (i - 1))
2875 if (gtk_drag_check_threshold (widget, site->x, site->y,
2876 event->motion.x, event->motion.y))
2878 GtkDragSourceInfo *info;
2879 GdkDragContext *context;
2882 context = gtk_drag_begin (widget, site->target_list,
2886 info = gtk_drag_get_source_info (context, FALSE);
2888 if (!info->icon_window)
2890 switch (site->icon_type)
2892 case GTK_IMAGE_EMPTY:
2893 gtk_drag_set_icon_default (context);
2895 case GTK_IMAGE_PIXMAP:
2896 gtk_drag_set_icon_pixmap (context,
2898 site->icon_data.pixmap.pixmap,
2902 case GTK_IMAGE_PIXBUF:
2903 gtk_drag_set_icon_pixbuf (context,
2904 site->icon_data.pixbuf.pixbuf,
2907 case GTK_IMAGE_STOCK:
2908 gtk_drag_set_icon_stock (context,
2909 site->icon_data.stock.stock_id,
2913 g_assert_not_reached();
2923 default: /* hit for 2/3BUTTON_PRESS */
2931 gtk_drag_source_site_destroy (gpointer data)
2933 GtkDragSourceSite *site = data;
2935 if (site->target_list)
2936 gtk_target_list_unref (site->target_list);
2938 gtk_drag_source_unset_icon (site);
2943 gtk_drag_selection_get (GtkWidget *widget,
2944 GtkSelectionData *selection_data,
2949 GtkDragSourceInfo *info = data;
2950 static GdkAtom null_atom = GDK_NONE;
2954 null_atom = gdk_atom_intern ("NULL", FALSE);
2959 g_signal_emit_by_name (info->widget,
2962 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2964 case TARGET_MOTIF_SUCCESS:
2965 gtk_drag_drop_finished (info, TRUE, time);
2966 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2968 case TARGET_MOTIF_FAILURE:
2969 gtk_drag_drop_finished (info, FALSE, time);
2970 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2973 if (info->proxy_dest)
2975 /* This is sort of dangerous and needs to be thought
2978 info->proxy_dest->proxy_data = selection_data;
2979 gtk_drag_get_data (info->widget,
2980 info->proxy_dest->context,
2981 selection_data->target,
2984 info->proxy_dest->proxy_data = NULL;
2988 if (gtk_target_list_find (info->target_list,
2989 selection_data->target,
2992 g_signal_emit_by_name (info->widget, "drag_data_get",
3004 gtk_drag_anim_timeout (gpointer data)
3006 GtkDragAnim *anim = data;
3010 GDK_THREADS_ENTER ();
3012 if (anim->step == anim->n_steps)
3014 gtk_drag_source_info_destroy (anim->info);
3021 x = (anim->info->start_x * (anim->step + 1) +
3022 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3023 y = (anim->info->start_y * (anim->step + 1) +
3024 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3025 if (anim->info->icon_window)
3027 GtkWidget *icon_window;
3030 gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);
3032 gtk_window_move (GTK_WINDOW (icon_window),
3042 GDK_THREADS_LEAVE ();
3048 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3050 if (info->icon_window)
3052 gtk_widget_hide (info->icon_window);
3053 if (info->destroy_icon)
3054 gtk_widget_destroy (info->icon_window);
3056 if (info->fallback_icon)
3058 gtk_widget_destroy (info->fallback_icon);
3059 info->fallback_icon = NULL;
3062 g_object_unref (info->icon_window);
3063 info->icon_window = NULL;
3068 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3070 gtk_drag_remove_icon (info);
3072 if (!info->proxy_dest)
3073 g_signal_emit_by_name (info->widget, "drag_end",
3077 g_object_unref (info->widget);
3080 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3081 gtk_drag_button_release_cb,
3083 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3086 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3089 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3090 gtk_drag_selection_get,
3093 gtk_selection_remove_all (info->ipc_widget);
3094 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", NULL);
3095 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3096 gtk_drag_release_ipc_widget (info->ipc_widget);
3098 gtk_target_list_unref (info->target_list);
3100 gtk_drag_clear_source_info (info->context);
3101 g_object_unref (info->context);
3103 if (info->drop_timeout)
3104 gtk_timeout_remove (info->drop_timeout);
3109 /*************************************************************
3111 * Function to update the status of the drag when the
3112 * cursor moves or the modifier changes
3114 * info: DragSourceInfo for the drag
3115 * x_root, y_root: position of darg
3116 * event: The event that triggered this call
3118 *************************************************************/
3121 gtk_drag_update (GtkDragSourceInfo *info,
3127 GdkDragAction action;
3128 GdkDragAction possible_actions;
3129 GdkWindow *dest_window;
3130 GdkDragProtocol protocol;
3132 guint32 time = gtk_drag_get_event_time (event);
3134 gtk_drag_get_event_actions (event,
3136 info->possible_actions,
3137 &action, &possible_actions);
3138 info->cur_screen = screen;
3139 info->cur_x = x_root;
3140 info->cur_y = y_root;
3142 gtk_drag_update_icon (info);
3143 gdk_drag_find_window_for_screen (info->context,
3144 info->icon_window ? info->icon_window->window : NULL,
3145 screen, x_root, y_root,
3146 &dest_window, &protocol);
3148 if (gdk_drag_motion (info->context, dest_window, protocol,
3149 x_root, y_root, action,
3153 if (info->last_event != event) /* Paranoia, should not happen */
3155 if (info->last_event)
3156 gdk_event_free ((GdkEvent *)info->last_event);
3157 info->last_event = gdk_event_copy ((GdkEvent *)event);
3162 if (info->last_event)
3164 gdk_event_free ((GdkEvent *)info->last_event);
3165 info->last_event = NULL;
3170 g_object_unref (dest_window);
3172 selection = gdk_drag_get_selection (info->context);
3174 gtk_drag_source_check_selection (info, selection, time);
3177 /*************************************************************
3179 * Called when the user finishes to drag, either by
3180 * releasing the mouse, or by pressing Esc.
3182 * info: Source info for the drag
3183 * time: Timestamp for ending the drag
3185 *************************************************************/
3188 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3190 GdkEvent *send_event;
3191 GtkWidget *source_widget = info->widget;
3192 GdkDisplay *display = gtk_widget_get_display (source_widget);
3194 info->have_grab = FALSE;
3196 gdk_display_pointer_ungrab (display, time);
3197 gdk_display_keyboard_ungrab (display, time);
3198 gtk_grab_remove (info->ipc_widget);
3200 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3201 gtk_drag_button_release_cb,
3203 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3206 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3210 /* Send on a release pair to the the original
3211 * widget to convince it to release its grab. We need to
3212 * call gtk_propagate_event() here, instead of
3213 * gtk_widget_event() because widget like GtkList may
3214 * expect propagation.
3217 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3218 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3219 send_event->button.send_event = TRUE;
3220 send_event->button.time = time;
3221 send_event->button.x = 0;
3222 send_event->button.y = 0;
3223 send_event->button.axes = NULL;
3224 send_event->button.state = 0;
3225 send_event->button.button = info->button;
3226 send_event->button.device = gdk_display_get_core_pointer (display);
3227 send_event->button.x_root = 0;
3228 send_event->button.y_root = 0;
3230 gtk_propagate_event (source_widget, send_event);
3231 gdk_event_free (send_event);
3234 /*************************************************************
3236 * Called on cancellation of a drag, either by the user
3237 * or programmatically.
3239 * info: Source info for the drag
3240 * time: Timestamp for ending the drag
3242 *************************************************************/
3245 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3247 gtk_drag_end (info, time);
3248 gdk_drag_abort (info->context, time);
3249 gtk_drag_drop_finished (info, FALSE, time);
3252 /*************************************************************
3253 * gtk_drag_motion_cb:
3254 * "motion_notify_event" callback during drag.
3258 *************************************************************/
3261 gtk_drag_motion_cb (GtkWidget *widget,
3262 GdkEventMotion *event,
3265 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3267 gint x_root, y_root;
3271 GdkDisplay *display = gtk_widget_get_display (widget);
3273 gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
3274 event->x_root = x_root;
3275 event->y_root = y_root;
3278 screen = gdk_event_get_screen ((GdkEvent *)event);
3280 gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
3285 /*************************************************************
3287 * "key_press/release_event" callback during drag.
3291 *************************************************************/
3294 gtk_drag_key_cb (GtkWidget *widget,
3298 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3299 GdkModifierType state;
3300 GdkWindow *root_window;
3302 if (event->type == GDK_KEY_PRESS)
3304 if (event->keyval == GDK_Escape)
3306 gtk_drag_cancel (info, event->time);
3312 /* Now send a "motion" so that the modifier state is updated */
3314 /* The state is not yet updated in the event, so we need
3315 * to query it here. We could use XGetModifierMapping, but
3316 * that would be overkill.
3318 root_window = gtk_widget_get_root_window (widget);
3319 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3321 event->state = state;
3322 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
3327 /*************************************************************
3328 * gtk_drag_button_release_cb:
3329 * "button_release_event" callback during drag.
3333 *************************************************************/
3336 gtk_drag_button_release_cb (GtkWidget *widget,
3337 GdkEventButton *event,
3340 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3342 if (event->button != info->button)
3345 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3347 gtk_drag_end (info, event->time);
3348 gtk_drag_drop (info, event->time);
3352 gtk_drag_cancel (info, event->time);
3359 gtk_drag_abort_timeout (gpointer data)
3361 GtkDragSourceInfo *info = data;
3362 guint32 time = GDK_CURRENT_TIME;
3364 GDK_THREADS_ENTER ();
3366 if (info->proxy_dest)
3367 time = info->proxy_dest->proxy_drop_time;
3369 info->drop_timeout = 0;
3370 gtk_drag_drop_finished (info, FALSE, time);
3372 GDK_THREADS_LEAVE ();
3378 * gtk_drag_check_threshold:
3379 * @widget: a #GtkWidget
3380 * @start_x: X coordinate of start of drag
3381 * @start_y: Y coordinate of start of drag
3382 * @current_x: current X coordinate
3383 * @current_y: current Y coordinate
3385 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3386 * at (@current_x, @current_y) has passed the GTK+ drag threshhold, and thus
3387 * should trigger the beginning of a drag-and-drop operation.
3389 * Return Value: %TRUE if the drag threshold has been passed.
3392 gtk_drag_check_threshold (GtkWidget *widget,
3398 gint drag_threshold;
3400 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3402 g_object_get (gtk_widget_get_settings (widget),
3403 "gtk-dnd-drag-threshold", &drag_threshold,
3406 return (ABS (current_x - start_x) > drag_threshold ||
3407 ABS (current_y - start_y) > drag_threshold);