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) || !GTK_WIDGET_SENSITIVE (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_type = GTK_IMAGE_STOCK;
2162 site->icon_data.stock.stock_id = g_strdup (stock_id);
2166 gtk_drag_get_icon (GtkDragSourceInfo *info,
2167 GtkWidget **icon_window,
2171 if (get_can_change_screen (info->icon_window))
2172 gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2175 if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2177 if (!info->fallback_icon)
2179 gint save_hot_x, save_hot_y;
2180 gboolean save_destroy_icon;
2181 GtkWidget *save_icon_window;
2183 /* HACK to get the appropriate icon
2185 save_icon_window = info->icon_window;
2186 save_hot_x = info->hot_x;
2187 save_hot_y = info->hot_x;
2188 save_destroy_icon = info->destroy_icon;
2190 info->icon_window = NULL;
2191 gtk_drag_set_icon_default (info->context);
2192 info->fallback_icon = info->icon_window;
2194 info->icon_window = save_icon_window;
2195 info->hot_x = save_hot_x;
2196 info->hot_y = save_hot_y;
2197 info->destroy_icon = save_destroy_icon;
2200 gtk_widget_hide (info->icon_window);
2202 *icon_window = info->fallback_icon;
2203 gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2205 if (!default_icon_pixmap)
2212 *hot_x = default_icon_hot_x;
2213 *hot_y = default_icon_hot_y;
2218 if (info->fallback_icon)
2219 gtk_widget_hide (info->fallback_icon);
2221 *icon_window = info->icon_window;
2222 *hot_x = info->hot_x;
2223 *hot_y = info->hot_y;
2228 gtk_drag_update_icon (GtkDragSourceInfo *info)
2230 if (info->icon_window)
2232 GtkWidget *icon_window;
2235 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2237 gtk_window_move (GTK_WINDOW (icon_window),
2238 info->cur_x - hot_x,
2239 info->cur_y - hot_y);
2241 if (GTK_WIDGET_VISIBLE (icon_window))
2242 gdk_window_raise (icon_window->window);
2244 gtk_widget_show (icon_window);
2249 gtk_drag_set_icon_window (GdkDragContext *context,
2253 gboolean destroy_on_release)
2255 GtkDragSourceInfo *info;
2257 info = gtk_drag_get_source_info (context, FALSE);
2258 gtk_drag_remove_icon (info);
2261 gtk_widget_ref (widget);
2263 info->icon_window = widget;
2264 info->hot_x = hot_x;
2265 info->hot_y = hot_y;
2266 info->destroy_icon = destroy_on_release;
2268 gtk_drag_update_icon (info);
2272 * gtk_drag_set_icon_widget:
2273 * @context: the context for a drag. (This must be called
2274 with a context for the source side of a drag)
2275 * @widget: a toplevel window to use as an icon.
2276 * @hot_x: the X offset within @widget of the hotspot.
2277 * @hot_y: the Y offset within @widget of the hotspot.
2279 * Changes the icon for a widget to a given widget. GTK+
2280 * will not destroy the icon, so if you don't want
2281 * it to persist, you should connect to the "drag_end"
2282 * signal and destroy it yourself.
2285 gtk_drag_set_icon_widget (GdkDragContext *context,
2290 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2291 g_return_if_fail (context->is_source);
2292 g_return_if_fail (GTK_IS_WIDGET (widget));
2294 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2298 icon_window_realize (GtkWidget *window,
2304 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
2305 gtk_widget_get_colormap (window),
2306 &pixmap, &mask, 128);
2308 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2311 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2313 g_object_unref (pixmap);
2316 g_object_unref (mask);
2320 set_icon_stock_pixbuf (GdkDragContext *context,
2321 const gchar *stock_id,
2330 g_return_if_fail (context != NULL);
2331 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2332 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2334 screen = gdk_drawable_get_screen (context->source_window);
2336 /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
2337 gtk_widget_push_colormap (NULL);
2338 window = gtk_window_new (GTK_WINDOW_POPUP);
2339 gtk_window_set_screen (GTK_WINDOW (window), screen);
2340 set_can_change_screen (window, TRUE);
2341 gtk_widget_pop_colormap ();
2343 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2344 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2348 pixbuf = gtk_widget_render_icon (window, stock_id,
2349 GTK_ICON_SIZE_DND, NULL);
2353 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2354 gtk_widget_destroy (window);
2360 g_object_ref (pixbuf);
2362 width = gdk_pixbuf_get_width (pixbuf);
2363 height = gdk_pixbuf_get_width (pixbuf);
2365 gtk_widget_set_size_request (window,
2366 gdk_pixbuf_get_width (pixbuf),
2367 gdk_pixbuf_get_height (pixbuf));
2369 g_signal_connect_closure (window, "realize",
2370 g_cclosure_new (G_CALLBACK (icon_window_realize),
2372 (GClosureNotify)g_object_unref),
2375 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2379 * gtk_drag_set_icon_pixbuf:
2380 * @context: the context for a drag. (This must be called
2381 * with a context for the source side of a drag)
2382 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2383 * @hot_x: the X offset within @widget of the hotspot.
2384 * @hot_y: the Y offset within @widget of the hotspot.
2386 * Sets @pixbuf as the icon for a given drag.
2389 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2394 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2395 g_return_if_fail (context->is_source);
2396 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2398 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2402 * gtk_drag_set_icon_stock:
2403 * @context: the context for a drag. (This must be called
2404 * with a context for the source side of a drag)
2405 * @stock_id: the ID of the stock icon to use for the drag.
2406 * @hot_x: the X offset within the icon of the hotspot.
2407 * @hot_y: the Y offset within the icon of the hotspot.
2409 * Sets the the icon for a given drag from a stock ID.
2412 gtk_drag_set_icon_stock (GdkDragContext *context,
2413 const gchar *stock_id,
2417 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2418 g_return_if_fail (context->is_source);
2419 g_return_if_fail (stock_id != NULL);
2421 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2425 * gtk_drag_set_icon_pixmap:
2426 * @context: the context for a drag. (This must be called
2427 * with a context for the source side of a drag)
2428 * @colormap: the colormap of the icon
2429 * @pixmap: the image data for the icon
2430 * @mask: the transparency mask for the icon
2431 * @hot_x: the X offset within @pixmap of the hotspot.
2432 * @hot_y: the Y offset within @pixmap of the hotspot.
2434 * Sets @pixmap as the icon for a given drag. GTK+ retains
2435 * references for the arguments, and will release them when
2436 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2437 * will be more convenient to use.
2440 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2441 GdkColormap *colormap,
2451 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2452 g_return_if_fail (context->is_source);
2453 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2454 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2455 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2457 screen = gdk_colormap_get_screen (colormap);
2459 g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
2460 g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
2462 gdk_drawable_get_size (pixmap, &width, &height);
2464 gtk_widget_push_colormap (colormap);
2466 window = gtk_window_new (GTK_WINDOW_POPUP);
2467 gtk_window_set_screen (GTK_WINDOW (window), screen);
2468 set_can_change_screen (window, FALSE);
2469 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2470 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2472 gtk_widget_pop_colormap ();
2474 gtk_widget_set_size_request (window, width, height);
2475 gtk_widget_realize (window);
2477 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2480 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2482 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2486 * gtk_drag_set_icon_default:
2487 * @context: the context for a drag. (This must be called
2488 with a context for the source side of a drag)
2490 * Sets the icon for a particular drag to the default
2494 gtk_drag_set_icon_default (GdkDragContext *context)
2496 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2497 g_return_if_fail (context->is_source);
2499 if (!default_icon_pixmap)
2500 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2502 gtk_drag_set_icon_pixmap (context,
2503 default_icon_colormap,
2504 default_icon_pixmap,
2507 default_icon_hot_y);
2511 * gtk_drag_set_default_icon:
2512 * @colormap: the colormap of the icon
2513 * @pixmap: the image data for the icon
2514 * @mask: the transparency mask for an image.
2515 * @hot_x: The X offset within @widget of the hotspot.
2516 * @hot_y: The Y offset within @widget of the hotspot.
2518 * Changes the default drag icon. GTK+ retains references for the
2519 * arguments, and will release them when they are no longer needed.
2520 * This function is obsolete. The default icon should now be changed
2521 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2524 gtk_drag_set_default_icon (GdkColormap *colormap,
2530 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2531 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2532 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2534 if (default_icon_colormap)
2535 g_object_unref (default_icon_colormap);
2536 if (default_icon_pixmap)
2537 g_object_unref (default_icon_pixmap);
2538 if (default_icon_mask)
2539 g_object_unref (default_icon_mask);
2541 default_icon_colormap = colormap;
2542 g_object_ref (colormap);
2544 default_icon_pixmap = pixmap;
2545 g_object_ref (pixmap);
2547 default_icon_mask = mask;
2549 g_object_ref (mask);
2551 default_icon_hot_x = hot_x;
2552 default_icon_hot_y = hot_y;
2556 /*************************************************************
2557 * _gtk_drag_source_handle_event:
2558 * Called from widget event handling code on Drag events
2562 * toplevel: Toplevel widget that received the event
2565 *************************************************************/
2568 _gtk_drag_source_handle_event (GtkWidget *widget,
2571 GtkDragSourceInfo *info;
2572 GdkDragContext *context;
2574 g_return_if_fail (widget != NULL);
2575 g_return_if_fail (event != NULL);
2577 context = event->dnd.context;
2578 info = gtk_drag_get_source_info (context, FALSE);
2582 switch (event->type)
2584 case GDK_DRAG_STATUS:
2588 if (info->proxy_dest)
2590 if (!event->dnd.send_event)
2592 if (info->proxy_dest->proxy_drop_wait)
2594 gboolean result = context->action != 0;
2596 /* Aha - we can finally pass the MOTIF DROP on... */
2597 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2599 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2601 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2605 gdk_drag_status (info->proxy_dest->context,
2606 event->dnd.context->action,
2611 else if (info->have_grab)
2613 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2614 event->dnd.context->action);
2615 if (info->cursor != cursor)
2617 gdk_pointer_grab (widget->window, FALSE,
2618 GDK_POINTER_MOTION_MASK |
2619 GDK_POINTER_MOTION_HINT_MASK |
2620 GDK_BUTTON_RELEASE_MASK,
2622 cursor, info->grab_time);
2623 info->cursor = cursor;
2626 if (info->last_event)
2628 gtk_drag_update (info,
2629 info->cur_screen, info->cur_x, info->cur_y,
2631 info->last_event = NULL;
2637 case GDK_DROP_FINISHED:
2638 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2641 g_assert_not_reached ();
2645 /*************************************************************
2646 * gtk_drag_source_check_selection:
2647 * Check if we've set up handlers/claimed the selection
2648 * for a given drag. If not, add them.
2652 *************************************************************/
2655 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2661 tmp_list = info->selections;
2664 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2666 tmp_list = tmp_list->next;
2669 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2673 info->selections = g_list_prepend (info->selections,
2674 GUINT_TO_POINTER (selection));
2676 tmp_list = info->target_list->list;
2679 GtkTargetPair *pair = tmp_list->data;
2681 gtk_selection_add_target (info->ipc_widget,
2685 tmp_list = tmp_list->next;
2688 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2690 gtk_selection_add_target (info->ipc_widget,
2692 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2693 TARGET_MOTIF_SUCCESS);
2694 gtk_selection_add_target (info->ipc_widget,
2696 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2697 TARGET_MOTIF_FAILURE);
2700 gtk_selection_add_target (info->ipc_widget,
2702 gdk_atom_intern ("DELETE", FALSE),
2706 /*************************************************************
2707 * gtk_drag_drop_finished:
2708 * Clean up from the drag, and display snapback, if necessary.
2714 *************************************************************/
2717 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2721 gtk_drag_source_release_selections (info, time);
2723 if (info->proxy_dest)
2725 /* The time from the event isn't reliable for Xdnd drags */
2726 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2727 info->proxy_dest->proxy_drop_time);
2728 gtk_drag_source_info_destroy (info);
2734 gtk_drag_source_info_destroy (info);
2738 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2742 anim->n_steps = MAX (info->cur_x - info->start_x,
2743 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2744 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2746 info->cur_screen = gtk_widget_get_screen (info->widget);
2747 gtk_drag_update_icon (info);
2749 /* Mark the context as dead, so if the destination decides
2750 * to respond really late, we still are OK.
2752 gtk_drag_clear_source_info (info->context);
2753 g_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2759 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2762 GdkDisplay *display = gtk_widget_get_display (info->widget);
2763 GList *tmp_list = info->selections;
2767 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2768 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2769 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2771 tmp_list = tmp_list->next;
2774 g_list_free (info->selections);
2775 info->selections = NULL;
2778 /*************************************************************
2780 * Send a drop event.
2784 *************************************************************/
2787 gtk_drag_drop (GtkDragSourceInfo *info,
2790 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2792 GtkSelectionData selection_data;
2794 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2796 tmp_list = info->target_list->list;
2799 GtkTargetPair *pair = tmp_list->data;
2801 if (pair->target == target)
2803 selection_data.selection = GDK_NONE;
2804 selection_data.target = target;
2805 selection_data.data = NULL;
2806 selection_data.length = -1;
2808 g_signal_emit_by_name (info->widget, "drag_data_get",
2809 info->context, &selection_data,
2813 /* FIXME: Should we check for length >= 0 here? */
2814 gtk_drag_drop_finished (info, TRUE, time);
2817 tmp_list = tmp_list->next;
2819 gtk_drag_drop_finished (info, FALSE, time);
2823 if (info->icon_window)
2824 gtk_widget_hide (info->icon_window);
2826 gdk_drag_drop (info->context, time);
2827 info->drop_timeout = g_timeout_add (DROP_ABORT_TIME,
2828 gtk_drag_abort_timeout,
2834 * Source side callbacks.
2838 gtk_drag_source_event_cb (GtkWidget *widget,
2842 GtkDragSourceSite *site;
2843 gboolean retval = FALSE;
2844 site = (GtkDragSourceSite *)data;
2846 switch (event->type)
2848 case GDK_BUTTON_PRESS:
2849 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2851 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2852 site->x = event->button.x;
2853 site->y = event->button.y;
2857 case GDK_BUTTON_RELEASE:
2858 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2859 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2862 case GDK_MOTION_NOTIFY:
2863 if (site->state & event->motion.state & site->start_button_mask)
2865 /* FIXME: This is really broken and can leave us
2871 if (site->state & event->motion.state &
2872 GDK_BUTTON1_MASK << (i - 1))
2876 if (gtk_drag_check_threshold (widget, site->x, site->y,
2877 event->motion.x, event->motion.y))
2879 GtkDragSourceInfo *info;
2880 GdkDragContext *context;
2883 context = gtk_drag_begin (widget, site->target_list,
2887 info = gtk_drag_get_source_info (context, FALSE);
2889 if (!info->icon_window)
2891 switch (site->icon_type)
2893 case GTK_IMAGE_EMPTY:
2894 gtk_drag_set_icon_default (context);
2896 case GTK_IMAGE_PIXMAP:
2897 gtk_drag_set_icon_pixmap (context,
2899 site->icon_data.pixmap.pixmap,
2903 case GTK_IMAGE_PIXBUF:
2904 gtk_drag_set_icon_pixbuf (context,
2905 site->icon_data.pixbuf.pixbuf,
2908 case GTK_IMAGE_STOCK:
2909 gtk_drag_set_icon_stock (context,
2910 site->icon_data.stock.stock_id,
2914 g_assert_not_reached();
2924 default: /* hit for 2/3BUTTON_PRESS */
2932 gtk_drag_source_site_destroy (gpointer data)
2934 GtkDragSourceSite *site = data;
2936 if (site->target_list)
2937 gtk_target_list_unref (site->target_list);
2939 gtk_drag_source_unset_icon (site);
2944 gtk_drag_selection_get (GtkWidget *widget,
2945 GtkSelectionData *selection_data,
2950 GtkDragSourceInfo *info = data;
2951 static GdkAtom null_atom = GDK_NONE;
2955 null_atom = gdk_atom_intern ("NULL", FALSE);
2960 g_signal_emit_by_name (info->widget,
2963 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2965 case TARGET_MOTIF_SUCCESS:
2966 gtk_drag_drop_finished (info, TRUE, time);
2967 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2969 case TARGET_MOTIF_FAILURE:
2970 gtk_drag_drop_finished (info, FALSE, time);
2971 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2974 if (info->proxy_dest)
2976 /* This is sort of dangerous and needs to be thought
2979 info->proxy_dest->proxy_data = selection_data;
2980 gtk_drag_get_data (info->widget,
2981 info->proxy_dest->context,
2982 selection_data->target,
2985 info->proxy_dest->proxy_data = NULL;
2989 if (gtk_target_list_find (info->target_list,
2990 selection_data->target,
2993 g_signal_emit_by_name (info->widget, "drag_data_get",
3005 gtk_drag_anim_timeout (gpointer data)
3007 GtkDragAnim *anim = data;
3011 GDK_THREADS_ENTER ();
3013 if (anim->step == anim->n_steps)
3015 gtk_drag_source_info_destroy (anim->info);
3022 x = (anim->info->start_x * (anim->step + 1) +
3023 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3024 y = (anim->info->start_y * (anim->step + 1) +
3025 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3026 if (anim->info->icon_window)
3028 GtkWidget *icon_window;
3031 gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);
3033 gtk_window_move (GTK_WINDOW (icon_window),
3043 GDK_THREADS_LEAVE ();
3049 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3051 if (info->icon_window)
3053 gtk_widget_hide (info->icon_window);
3054 if (info->destroy_icon)
3055 gtk_widget_destroy (info->icon_window);
3057 if (info->fallback_icon)
3059 gtk_widget_destroy (info->fallback_icon);
3060 info->fallback_icon = NULL;
3063 g_object_unref (info->icon_window);
3064 info->icon_window = NULL;
3069 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3071 gtk_drag_remove_icon (info);
3073 if (!info->proxy_dest)
3074 g_signal_emit_by_name (info->widget, "drag_end",
3078 g_object_unref (info->widget);
3081 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3082 gtk_drag_button_release_cb,
3084 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3087 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3090 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3091 gtk_drag_selection_get,
3094 gtk_selection_remove_all (info->ipc_widget);
3095 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", NULL);
3096 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3097 gtk_drag_release_ipc_widget (info->ipc_widget);
3099 gtk_target_list_unref (info->target_list);
3101 gtk_drag_clear_source_info (info->context);
3102 g_object_unref (info->context);
3104 if (info->drop_timeout)
3105 g_source_remove (info->drop_timeout);
3110 /*************************************************************
3112 * Function to update the status of the drag when the
3113 * cursor moves or the modifier changes
3115 * info: DragSourceInfo for the drag
3116 * x_root, y_root: position of darg
3117 * event: The event that triggered this call
3119 *************************************************************/
3122 gtk_drag_update (GtkDragSourceInfo *info,
3128 GdkDragAction action;
3129 GdkDragAction possible_actions;
3130 GdkWindow *dest_window;
3131 GdkDragProtocol protocol;
3133 guint32 time = gtk_drag_get_event_time (event);
3135 gtk_drag_get_event_actions (event,
3137 info->possible_actions,
3138 &action, &possible_actions);
3139 info->cur_screen = screen;
3140 info->cur_x = x_root;
3141 info->cur_y = y_root;
3143 gtk_drag_update_icon (info);
3144 gdk_drag_find_window_for_screen (info->context,
3145 info->icon_window ? info->icon_window->window : NULL,
3146 screen, x_root, y_root,
3147 &dest_window, &protocol);
3149 if (gdk_drag_motion (info->context, dest_window, protocol,
3150 x_root, y_root, action,
3154 if (info->last_event != event) /* Paranoia, should not happen */
3156 if (info->last_event)
3157 gdk_event_free ((GdkEvent *)info->last_event);
3158 info->last_event = gdk_event_copy ((GdkEvent *)event);
3163 if (info->last_event)
3165 gdk_event_free ((GdkEvent *)info->last_event);
3166 info->last_event = NULL;
3171 g_object_unref (dest_window);
3173 selection = gdk_drag_get_selection (info->context);
3175 gtk_drag_source_check_selection (info, selection, time);
3178 /*************************************************************
3180 * Called when the user finishes to drag, either by
3181 * releasing the mouse, or by pressing Esc.
3183 * info: Source info for the drag
3184 * time: Timestamp for ending the drag
3186 *************************************************************/
3189 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3191 GdkEvent *send_event;
3192 GtkWidget *source_widget = info->widget;
3193 GdkDisplay *display = gtk_widget_get_display (source_widget);
3195 info->have_grab = FALSE;
3197 gdk_display_pointer_ungrab (display, time);
3198 gdk_display_keyboard_ungrab (display, time);
3199 gtk_grab_remove (info->ipc_widget);
3201 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3202 gtk_drag_button_release_cb,
3204 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3207 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3211 /* Send on a release pair to the the original
3212 * widget to convince it to release its grab. We need to
3213 * call gtk_propagate_event() here, instead of
3214 * gtk_widget_event() because widget like GtkList may
3215 * expect propagation.
3218 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3219 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3220 send_event->button.send_event = TRUE;
3221 send_event->button.time = time;
3222 send_event->button.x = 0;
3223 send_event->button.y = 0;
3224 send_event->button.axes = NULL;
3225 send_event->button.state = 0;
3226 send_event->button.button = info->button;
3227 send_event->button.device = gdk_display_get_core_pointer (display);
3228 send_event->button.x_root = 0;
3229 send_event->button.y_root = 0;
3231 gtk_propagate_event (source_widget, send_event);
3232 gdk_event_free (send_event);
3235 /*************************************************************
3237 * Called on cancellation of a drag, either by the user
3238 * or programmatically.
3240 * info: Source info for the drag
3241 * time: Timestamp for ending the drag
3243 *************************************************************/
3246 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3248 gtk_drag_end (info, time);
3249 gdk_drag_abort (info->context, time);
3250 gtk_drag_drop_finished (info, FALSE, time);
3253 /*************************************************************
3254 * gtk_drag_motion_cb:
3255 * "motion_notify_event" callback during drag.
3259 *************************************************************/
3262 gtk_drag_motion_cb (GtkWidget *widget,
3263 GdkEventMotion *event,
3266 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3268 gint x_root, y_root;
3272 GdkDisplay *display = gtk_widget_get_display (widget);
3274 gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
3275 event->x_root = x_root;
3276 event->y_root = y_root;
3279 screen = gdk_event_get_screen ((GdkEvent *)event);
3281 gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
3286 /*************************************************************
3288 * "key_press/release_event" callback during drag.
3292 *************************************************************/
3295 gtk_drag_key_cb (GtkWidget *widget,
3299 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3300 GdkModifierType state;
3301 GdkWindow *root_window;
3303 if (event->type == GDK_KEY_PRESS)
3305 if (event->keyval == GDK_Escape)
3307 gtk_drag_cancel (info, event->time);
3313 /* Now send a "motion" so that the modifier state is updated */
3315 /* The state is not yet updated in the event, so we need
3316 * to query it here. We could use XGetModifierMapping, but
3317 * that would be overkill.
3319 root_window = gtk_widget_get_root_window (widget);
3320 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3322 event->state = state;
3323 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
3328 /*************************************************************
3329 * gtk_drag_button_release_cb:
3330 * "button_release_event" callback during drag.
3334 *************************************************************/
3337 gtk_drag_button_release_cb (GtkWidget *widget,
3338 GdkEventButton *event,
3341 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3343 if (event->button != info->button)
3346 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3348 gtk_drag_end (info, event->time);
3349 gtk_drag_drop (info, event->time);
3353 gtk_drag_cancel (info, event->time);
3360 gtk_drag_abort_timeout (gpointer data)
3362 GtkDragSourceInfo *info = data;
3363 guint32 time = GDK_CURRENT_TIME;
3365 GDK_THREADS_ENTER ();
3367 if (info->proxy_dest)
3368 time = info->proxy_dest->proxy_drop_time;
3370 info->drop_timeout = 0;
3371 gtk_drag_drop_finished (info, FALSE, time);
3373 GDK_THREADS_LEAVE ();
3379 * gtk_drag_check_threshold:
3380 * @widget: a #GtkWidget
3381 * @start_x: X coordinate of start of drag
3382 * @start_y: Y coordinate of start of drag
3383 * @current_x: current X coordinate
3384 * @current_y: current Y coordinate
3386 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3387 * at (@current_x, @current_y) has passed the GTK+ drag threshhold, and thus
3388 * should trigger the beginning of a drag-and-drop operation.
3390 * Return Value: %TRUE if the drag threshold has been passed.
3393 gtk_drag_check_threshold (GtkWidget *widget,
3399 gint drag_threshold;
3401 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3403 g_object_get (gtk_widget_get_settings (widget),
3404 "gtk-dnd-drag-threshold", &drag_threshold,
3407 return (ABS (current_x - start_x) > drag_threshold ||
3408 ABS (current_y - start_y) > drag_threshold);