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"
35 #include "gtksignal.h"
37 #include "gtkwindow.h"
39 static GSList *source_widgets = NULL;
41 typedef struct _GtkDragSourceSite GtkDragSourceSite;
42 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
43 typedef struct _GtkDragDestSite GtkDragDestSite;
44 typedef struct _GtkDragDestInfo GtkDragDestInfo;
45 typedef struct _GtkDragAnim GtkDragAnim;
46 typedef struct _GtkDragFindData GtkDragFindData;
56 struct _GtkDragSourceSite
58 GdkModifierType start_button_mask;
59 GtkTargetList *target_list; /* Targets for drag data */
60 GdkDragAction actions; /* Possible actions */
63 GtkImageType icon_type;
66 GtkImagePixmapData pixmap;
67 GtkImagePixbufData pixbuf;
68 GtkImageStockData stock;
72 GdkColormap *colormap; /* Colormap for drag icon */
74 /* Stored button press information to detect drag beginning */
79 struct _GtkDragSourceInfo
82 GtkTargetList *target_list; /* Targets for drag data */
83 GdkDragAction possible_actions; /* Actions allowed by source */
84 GdkDragContext *context; /* drag context */
85 GtkWidget *icon_window; /* Window for drag */
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 */
97 guint32 grab_time; /* timestamp for initial grab */
98 GList *selections; /* selections we've claimed */
100 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
102 guint drop_timeout; /* Timeout for aborting drop */
103 guint destroy_icon : 1; /* If true, destroy icon_window
105 guint have_grab : 1; /* Do we still have the pointer grab
109 struct _GtkDragDestSite
111 GtkDestDefaults flags;
112 GtkTargetList *target_list;
113 GdkDragAction actions;
114 GdkWindow *proxy_window;
115 GdkDragProtocol proxy_protocol;
116 gboolean do_proxy : 1;
117 gboolean proxy_coords : 1;
118 gboolean have_drag : 1;
121 struct _GtkDragDestInfo
123 GtkWidget *widget; /* Widget in which drag is in */
124 GdkDragContext *context; /* Drag context */
125 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
126 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
127 gboolean dropped : 1; /* Set after we receive a drop */
128 guint32 proxy_drop_time; /* Timestamp for proxied drop */
129 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
130 * status reply before sending
133 gint drop_x, drop_y; /* Position of drop */
136 #define DROP_ABORT_TIME 300000
138 #define ANIM_STEP_TIME 50
139 #define ANIM_STEP_LENGTH 50
140 #define ANIM_MIN_STEPS 5
141 #define ANIM_MAX_STEPS 10
145 GtkDragSourceInfo *info;
150 struct _GtkDragFindData
154 GdkDragContext *context;
155 GtkDragDestInfo *info;
158 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
159 gint x, gint y, guint32 time);
163 /* Enumeration for some targets we handle internally */
166 TARGET_MOTIF_SUCCESS = 0x40000000,
167 TARGET_MOTIF_FAILURE,
173 static GdkPixmap *default_icon_pixmap = NULL;
174 static GdkPixmap *default_icon_mask = NULL;
175 static GdkColormap *default_icon_colormap = NULL;
176 static gint default_icon_hot_x;
177 static gint default_icon_hot_y;
179 /* Forward declarations */
180 static void gtk_drag_get_event_actions (GdkEvent *event,
182 GdkDragAction actions,
183 GdkDragAction *suggested_action,
184 GdkDragAction *possible_actions);
185 static GdkCursor * gtk_drag_get_cursor (GdkDisplay *display,
186 GdkDragAction action);
187 static GtkWidget *gtk_drag_get_ipc_widget (GdkScreen *screen);
188 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
190 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
191 GdkEventExpose *event,
194 static void gtk_drag_selection_received (GtkWidget *widget,
195 GtkSelectionData *selection_data,
198 static void gtk_drag_find_widget (GtkWidget *widget,
199 GtkDragFindData *data);
200 static void gtk_drag_proxy_begin (GtkWidget *widget,
201 GtkDragDestInfo *dest_info,
203 static void gtk_drag_dest_realized (GtkWidget *widget);
204 static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
205 GtkWidget *previous_toplevel);
206 static void gtk_drag_dest_site_destroy (gpointer data);
207 static void gtk_drag_dest_leave (GtkWidget *widget,
208 GdkDragContext *context,
210 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
211 GdkDragContext *context,
215 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
216 GdkDragContext *context,
221 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
223 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
225 static void gtk_drag_clear_source_info (GdkDragContext *context);
227 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
230 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
232 static void gtk_drag_drop (GtkDragSourceInfo *info,
234 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
237 static void gtk_drag_cancel (GtkDragSourceInfo *info,
240 static gint gtk_drag_source_event_cb (GtkWidget *widget,
243 static void gtk_drag_source_site_destroy (gpointer data);
244 static void gtk_drag_selection_get (GtkWidget *widget,
245 GtkSelectionData *selection_data,
249 static gint gtk_drag_anim_timeout (gpointer data);
250 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
251 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
252 static void gtk_drag_update (GtkDragSourceInfo *info,
256 static gint gtk_drag_motion_cb (GtkWidget *widget,
257 GdkEventMotion *event,
259 static gint gtk_drag_key_cb (GtkWidget *widget,
262 static gint gtk_drag_button_release_cb (GtkWidget *widget,
263 GdkEventButton *event,
265 static gint gtk_drag_abort_timeout (gpointer data);
267 /************************
268 * Cursor and Icon data *
269 ************************/
271 #define action_ask_width 16
272 #define action_ask_height 16
273 static const guchar action_ask_bits[] = {
274 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
275 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
276 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
278 #define action_ask_mask_width 16
279 #define action_ask_mask_height 16
280 static const guchar action_ask_mask_bits[] = {
281 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
282 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
283 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
285 #define action_copy_width 16
286 #define action_copy_height 16
287 static const guchar action_copy_bits[] = {
288 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
289 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
290 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
292 #define action_copy_mask_width 16
293 #define action_copy_mask_height 16
294 static const guchar action_copy_mask_bits[] = {
295 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
296 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
297 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
299 #define action_move_width 16
300 #define action_move_height 16
301 static const guchar action_move_bits[] = {
302 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
303 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
304 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
306 #define action_move_mask_width 16
307 #define action_move_mask_height 16
308 static const guchar action_move_mask_bits[] = {
309 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
310 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
311 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
313 #define action_link_width 16
314 #define action_link_height 16
315 static const guchar action_link_bits[] = {
316 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
317 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
318 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
320 #define action_link_mask_width 16
321 #define action_link_mask_height 16
322 static const guchar action_link_mask_bits[] = {
323 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
324 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
325 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
327 #define action_none_width 16
328 #define action_none_height 16
329 static const guchar action_none_bits[] = {
330 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
331 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
332 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
334 #define action_none_mask_width 16
335 #define action_none_mask_height 16
336 static const guchar action_none_mask_bits[] = {
337 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
338 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
339 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
341 #define CURSOR_WIDTH 16
342 #define CURSOR_HEIGHT 16
345 GdkDragAction action;
350 { GDK_ACTION_DEFAULT, 0 },
351 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
352 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
353 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
354 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
355 { 0 , action_none_bits, action_none_mask_bits, NULL },
358 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
360 /*********************
361 * Utility functions *
362 *********************/
364 /*************************************************************
365 * gtk_drag_get_ipc_widget:
366 * Return a invisible, off-screen, override-redirect
371 *************************************************************/
374 gtk_drag_get_ipc_widget (GdkScreen *screen)
377 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
378 "gtk-dnd-ipc-widgets");
382 GSList *tmp = drag_widgets;
383 result = drag_widgets->data;
384 drag_widgets = drag_widgets->next;
385 g_object_set_data (G_OBJECT (screen),
386 "gtk-dnd-ipc-widgets",
388 g_slist_free_1 (tmp);
392 result = gtk_invisible_new_for_screen (screen);
393 gtk_widget_show (result);
399 /***************************************************************
400 * gtk_drag_release_ipc_widget:
401 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
403 * widget: the widget to release.
405 ***************************************************************/
408 gtk_drag_release_ipc_widget (GtkWidget *widget)
410 GdkScreen *screen = gtk_widget_get_screen (widget);
411 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
412 "gtk-dnd-ipc-widgets");
413 drag_widgets = g_slist_prepend (drag_widgets, widget);
414 g_object_set_data (G_OBJECT (screen),
415 "gtk-dnd-ipc-widgets",
420 gtk_drag_get_event_time (GdkEvent *event)
422 guint32 tm = GDK_CURRENT_TIME;
427 case GDK_MOTION_NOTIFY:
428 tm = event->motion.time; break;
429 case GDK_BUTTON_PRESS:
430 case GDK_2BUTTON_PRESS:
431 case GDK_3BUTTON_PRESS:
432 case GDK_BUTTON_RELEASE:
433 tm = event->button.time; break;
435 case GDK_KEY_RELEASE:
436 tm = event->key.time; break;
437 case GDK_ENTER_NOTIFY:
438 case GDK_LEAVE_NOTIFY:
439 tm = event->crossing.time; break;
440 case GDK_PROPERTY_NOTIFY:
441 tm = event->property.time; break;
442 case GDK_SELECTION_CLEAR:
443 case GDK_SELECTION_REQUEST:
444 case GDK_SELECTION_NOTIFY:
445 tm = event->selection.time; break;
446 case GDK_PROXIMITY_IN:
447 case GDK_PROXIMITY_OUT:
448 tm = event->proximity.time; break;
449 default: /* use current time */
457 gtk_drag_get_event_actions (GdkEvent *event,
459 GdkDragAction actions,
460 GdkDragAction *suggested_action,
461 GdkDragAction *possible_actions)
463 *suggested_action = 0;
464 *possible_actions = 0;
468 GdkModifierType state = 0;
472 case GDK_MOTION_NOTIFY:
473 state = event->motion.state;
475 case GDK_BUTTON_PRESS:
476 case GDK_2BUTTON_PRESS:
477 case GDK_3BUTTON_PRESS:
478 case GDK_BUTTON_RELEASE:
479 state = event->button.state;
482 case GDK_KEY_RELEASE:
483 state = event->key.state;
485 case GDK_ENTER_NOTIFY:
486 case GDK_LEAVE_NOTIFY:
487 state = event->crossing.state;
493 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
495 *suggested_action = GDK_ACTION_ASK;
496 *possible_actions = actions;
498 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
500 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
502 if (actions & GDK_ACTION_LINK)
504 *suggested_action = GDK_ACTION_LINK;
505 *possible_actions = GDK_ACTION_LINK;
508 else if (state & GDK_CONTROL_MASK)
510 if (actions & GDK_ACTION_COPY)
512 *suggested_action = GDK_ACTION_COPY;
513 *possible_actions = GDK_ACTION_COPY;
519 if (actions & GDK_ACTION_MOVE)
521 *suggested_action = GDK_ACTION_MOVE;
522 *possible_actions = GDK_ACTION_MOVE;
529 *possible_actions = actions;
531 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
532 *suggested_action = GDK_ACTION_ASK;
533 else if (actions & GDK_ACTION_COPY)
534 *suggested_action = GDK_ACTION_COPY;
535 else if (actions & GDK_ACTION_MOVE)
536 *suggested_action = GDK_ACTION_MOVE;
537 else if (actions & GDK_ACTION_LINK)
538 *suggested_action = GDK_ACTION_LINK;
543 *possible_actions = actions;
545 if (actions & GDK_ACTION_COPY)
546 *suggested_action = GDK_ACTION_COPY;
547 else if (actions & GDK_ACTION_MOVE)
548 *suggested_action = GDK_ACTION_MOVE;
549 else if (actions & GDK_ACTION_LINK)
550 *suggested_action = GDK_ACTION_LINK;
557 gtk_drag_get_cursor (GdkDisplay *display,
558 GdkDragAction action)
562 for (i = 0 ; i < n_drag_cursors - 1; i++)
563 if (drag_cursors[i].action == action)
565 if (drag_cursors[i].cursor != NULL)
567 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
569 gdk_cursor_unref (drag_cursors[i].cursor);
570 drag_cursors[i].cursor = NULL;
574 if (drag_cursors[i].cursor == NULL)
576 GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
577 GdkColor fg = { 0, 0x0000, 0x0000, 0x0000 };
578 GdkScreen *screen = gdk_display_get_default_screen (display);
579 GdkWindow *window = gdk_screen_get_root_window (screen);
582 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].bits, CURSOR_WIDTH, CURSOR_HEIGHT);
585 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].mask, CURSOR_WIDTH, CURSOR_HEIGHT);
587 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
589 gdk_pixmap_unref (pixmap);
590 gdk_pixmap_unref (mask);
593 return drag_cursors[i].cursor;
596 /********************
598 ********************/
600 /*************************************************************
602 * Get the data for a drag or drop
604 * context - drag context
605 * target - format to retrieve the data in.
606 * time - timestamp of triggering event.
609 *************************************************************/
612 gtk_drag_get_data (GtkWidget *widget,
613 GdkDragContext *context,
617 GtkWidget *selection_widget;
619 g_return_if_fail (GTK_IS_WIDGET (widget));
620 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
621 g_return_if_fail (!context->is_source);
623 selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
625 gdk_drag_context_ref (context);
626 gtk_widget_ref (widget);
628 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
629 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
631 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
633 gtk_selection_convert (selection_widget,
634 gdk_drag_get_selection (context),
640 /*************************************************************
641 * gtk_drag_get_source_widget:
642 * Get the widget the was the source of this drag, if
643 * the drag originated from this application.
645 * context: The drag context for this drag
647 * The source widget, or NULL if the drag originated from
648 * a different application.
649 *************************************************************/
652 gtk_drag_get_source_widget (GdkDragContext *context)
656 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
657 g_return_val_if_fail (!context->is_source, NULL);
659 tmp_list = source_widgets;
662 GtkWidget *ipc_widget = tmp_list->data;
664 if (ipc_widget->window == context->source_window)
666 GtkDragSourceInfo *info;
667 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
669 return info ? info->widget : NULL;
672 tmp_list = tmp_list->next;
678 /*************************************************************
680 * Notify the drag source that the transfer of data
683 * context: The drag context for this drag
684 * success: Was the data successfully transferred?
685 * time: The timestamp to use when notifying the destination.
687 *************************************************************/
690 gtk_drag_finish (GdkDragContext *context,
695 GdkAtom target = GDK_NONE;
697 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
698 g_return_if_fail (!context->is_source);
702 target = gdk_atom_intern ("DELETE", FALSE);
704 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
706 target = gdk_atom_intern (success ?
707 "XmTRANSFER_SUCCESS" :
708 "XmTRANSFER_FAILURE",
712 if (target != GDK_NONE)
714 GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
716 gdk_drag_context_ref (context);
718 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
719 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
720 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
723 gtk_selection_convert (selection_widget,
724 gdk_drag_get_selection (context),
729 if (!(success && del))
730 gdk_drop_finish (context, success, time);
733 /*************************************************************
734 * gtk_drag_highlight_expose:
735 * Callback for expose_event for highlighted widgets.
741 *************************************************************/
744 gtk_drag_highlight_expose (GtkWidget *widget,
745 GdkEventExpose *event,
748 gint x, y, width, height;
750 if (GTK_WIDGET_DRAWABLE (widget))
752 if (GTK_WIDGET_NO_WINDOW (widget))
754 x = widget->allocation.x;
755 y = widget->allocation.y;
756 width = widget->allocation.width;
757 height = widget->allocation.height;
763 gdk_window_get_size (widget->window, &width, &height);
766 gtk_draw_shadow (widget->style, widget->window,
767 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
768 x, y, width, height);
770 gdk_draw_rectangle (widget->window,
771 widget->style->black_gc,
773 x, y, width - 1, height - 1);
779 /*************************************************************
780 * gtk_drag_highlight:
781 * Highlight the given widget in the default manner.
785 *************************************************************/
788 gtk_drag_highlight (GtkWidget *widget)
790 g_return_if_fail (GTK_IS_WIDGET (widget));
792 gtk_signal_connect_after (GTK_OBJECT (widget), "expose_event",
793 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
796 gtk_widget_queue_draw (widget);
799 /*************************************************************
800 * gtk_drag_unhighlight:
801 * Refresh the given widget to remove the highlight.
805 *************************************************************/
808 gtk_drag_unhighlight (GtkWidget *widget)
810 g_return_if_fail (GTK_IS_WIDGET (widget));
812 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
813 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
816 gtk_widget_queue_clear (widget);
820 gtk_drag_dest_set_internal (GtkWidget *widget,
821 GtkDragDestSite *site)
823 GtkDragDestSite *old_site;
825 g_return_if_fail (widget != NULL);
827 /* HACK, do this in the destroy */
828 old_site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
830 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), old_site);
832 if (GTK_WIDGET_REALIZED (widget))
833 gtk_drag_dest_realized (widget);
835 gtk_signal_connect (GTK_OBJECT (widget), "realize",
836 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), site);
837 gtk_signal_connect (GTK_OBJECT (widget), "hierarchy_changed",
838 GTK_SIGNAL_FUNC (gtk_drag_dest_hierarchy_changed), site);
840 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
841 site, gtk_drag_dest_site_destroy);
845 /*************************************************************
847 * Register a drop site, and possibly add default behaviors.
850 * flags: Which types of default drag behavior to use
851 * targets: Table of targets that can be accepted
852 * n_targets: Number of of entries in targets
855 *************************************************************/
858 gtk_drag_dest_set (GtkWidget *widget,
859 GtkDestDefaults flags,
860 const GtkTargetEntry *targets,
862 GdkDragAction actions)
864 GtkDragDestSite *site;
866 g_return_if_fail (GTK_IS_WIDGET (widget));
868 site = g_new (GtkDragDestSite, 1);
871 site->have_drag = FALSE;
873 site->target_list = gtk_target_list_new (targets, n_targets);
875 site->target_list = NULL;
876 site->actions = actions;
877 site->do_proxy = FALSE;
878 site->proxy_window = NULL;
880 gtk_drag_dest_set_internal (widget, site);
883 /*************************************************************
884 * gtk_drag_dest_set_proxy:
885 * Set up this widget to proxy drags elsewhere.
888 * proxy_window: window to which forward drag events
889 * protocol: Drag protocol which the dest widget accepts
890 * use_coordinates: If true, send the same coordinates to the
891 * destination, because it is a embedded
894 *************************************************************/
897 gtk_drag_dest_set_proxy (GtkWidget *widget,
898 GdkWindow *proxy_window,
899 GdkDragProtocol protocol,
900 gboolean use_coordinates)
902 GtkDragDestSite *site;
904 g_return_if_fail (GTK_IS_WIDGET (widget));
905 g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
907 site = g_new (GtkDragDestSite, 1);
910 site->have_drag = FALSE;
911 site->target_list = NULL;
913 site->proxy_window = proxy_window;
915 gdk_window_ref (proxy_window);
916 site->do_proxy = TRUE;
917 site->proxy_protocol = protocol;
918 site->proxy_coords = use_coordinates;
920 gtk_drag_dest_set_internal (widget, site);
923 /*************************************************************
924 * gtk_drag_dest_unset
925 * Unregister this widget as a drag target.
929 *************************************************************/
932 gtk_drag_dest_unset (GtkWidget *widget)
934 g_return_if_fail (GTK_IS_WIDGET (widget));
936 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
940 * gtk_drag_dest_get_target_list:
941 * @widget: a #GtkWidget
943 * Returns the list of targets this widget can accept from
946 * Return value: the #GtkTargetList, or %NULL if none
949 gtk_drag_dest_get_target_list (GtkWidget *widget)
951 GtkDragDestSite *site;
953 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
955 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
957 return site ? site->target_list : NULL;
961 * gtk_drag_dest_set_target_list:
962 * @widget: a #GtkWidget that's a drag destination
963 * @target_list: list of droppable targets, or %NULL for none
965 * Sets the target types that this widget can accept from drag-and-drop.
966 * The widget must first be made into a drag destination with
967 * gtk_drag_dest_set().
970 gtk_drag_dest_set_target_list (GtkWidget *widget,
971 GtkTargetList *target_list)
973 GtkDragDestSite *site;
975 g_return_if_fail (GTK_IS_WIDGET (widget));
977 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
981 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");
986 gtk_target_list_ref (target_list);
988 if (site->target_list)
989 gtk_target_list_unref (site->target_list);
991 site->target_list = target_list;
995 /*************************************************************
996 * _gtk_drag_dest_handle_event:
997 * Called from widget event handling code on Drag events
1001 * toplevel: Toplevel widget that received the event
1004 *************************************************************/
1007 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1010 GtkDragDestInfo *info;
1011 GdkDragContext *context;
1013 g_return_if_fail (toplevel != NULL);
1014 g_return_if_fail (event != NULL);
1016 context = event->dnd.context;
1018 info = gtk_drag_get_dest_info (context, TRUE);
1020 /* Find the widget for the event */
1021 switch (event->type)
1023 case GDK_DRAG_ENTER:
1026 case GDK_DRAG_LEAVE:
1029 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1030 info->widget = NULL;
1034 case GDK_DRAG_MOTION:
1035 case GDK_DROP_START:
1037 GtkDragFindData data;
1040 if (event->type == GDK_DROP_START)
1042 info->dropped = TRUE;
1043 /* We send a leave here so that the widget unhighlights
1048 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1049 info->widget = NULL;
1053 gdk_window_get_origin (toplevel->window, &tx, &ty);
1055 data.x = event->dnd.x_root - tx;
1056 data.y = event->dnd.y_root - ty;
1057 data.context = context;
1060 data.toplevel = TRUE;
1061 data.callback = (event->type == GDK_DRAG_MOTION) ?
1062 gtk_drag_dest_motion : gtk_drag_dest_drop;
1063 data.time = event->dnd.time;
1065 gtk_drag_find_widget (toplevel, &data);
1067 if (info->widget && !data.found)
1069 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1070 info->widget = NULL;
1075 if (event->type == GDK_DRAG_MOTION)
1078 gdk_drag_status (context, 0, event->dnd.time);
1080 else if (event->type == GDK_DROP_START && !info->proxy_source)
1082 gdk_drop_reply (context, data.found, event->dnd.time);
1083 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1084 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1090 g_assert_not_reached ();
1095 * gtk_drag_dest_find_target:
1096 * @widget: drag destination widget
1097 * @context: drag context
1098 * @target_list: list of droppable targets, or %NULL to use
1099 * gtk_drag_dest_get_target_list (@widget).
1101 * Looks for a match between @context->targets and the
1102 * @dest_target_list, returning the first matching target, otherwise
1103 * returning %GDK_NONE. @dest_target_list should usually be the return
1104 * value from gtk_drag_dest_get_target_list(), but some widgets may
1105 * have different valid targets for different parts of the widget; in
1106 * that case, they will have to implement a drag_motion handler that
1107 * passes the correct target list to this function.
1109 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1112 gtk_drag_dest_find_target (GtkWidget *widget,
1113 GdkDragContext *context,
1114 GtkTargetList *target_list)
1117 GList *tmp_source = NULL;
1118 GtkWidget *source_widget;
1120 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1121 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1122 g_return_val_if_fail (!context->is_source, GDK_NONE);
1125 source_widget = gtk_drag_get_source_widget (context);
1127 if (target_list == NULL)
1128 target_list = gtk_drag_dest_get_target_list (widget);
1130 if (target_list == NULL)
1133 tmp_target = target_list->list;
1136 GtkTargetPair *pair = tmp_target->data;
1137 tmp_source = context->targets;
1140 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1142 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1143 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1144 return pair->target;
1148 tmp_source = tmp_source->next;
1150 tmp_target = tmp_target->next;
1157 gtk_drag_selection_received (GtkWidget *widget,
1158 GtkSelectionData *selection_data,
1162 GdkDragContext *context;
1163 GtkDragDestInfo *info;
1164 GtkWidget *drop_widget;
1168 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1169 info = gtk_drag_get_dest_info (context, FALSE);
1171 if (info->proxy_data &&
1172 info->proxy_data->target == selection_data->target)
1174 gtk_selection_data_set (info->proxy_data,
1175 selection_data->type,
1176 selection_data->format,
1177 selection_data->data,
1178 selection_data->length);
1183 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1185 gtk_drag_finish (context, TRUE, FALSE, time);
1187 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1188 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1194 GtkDragDestSite *site;
1196 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1198 if (site && site->target_list)
1202 if (gtk_target_list_find (site->target_list,
1203 selection_data->target,
1206 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1207 selection_data->length >= 0)
1208 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1209 "drag_data_received",
1210 context, info->drop_x, info->drop_y,
1217 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1218 "drag_data_received",
1219 context, info->drop_x, info->drop_y,
1220 selection_data, 0, time);
1223 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1226 gtk_drag_finish (context,
1227 (selection_data->length >= 0),
1228 (context->action == GDK_ACTION_MOVE),
1232 gtk_widget_unref (drop_widget);
1235 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1236 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1239 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1240 gdk_drag_context_unref (context);
1242 gtk_drag_release_ipc_widget (widget);
1246 prepend_and_ref_widget (GtkWidget *widget,
1249 GSList **slist_p = data;
1251 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1254 /*************************************************************
1255 * gtk_drag_find_widget:
1256 * Recursive callback used to locate widgets for
1257 * DRAG_MOTION and DROP_START events.
1261 *************************************************************/
1264 gtk_drag_find_widget (GtkWidget *widget,
1265 GtkDragFindData *data)
1267 GtkAllocation new_allocation;
1268 gint allocation_to_window_x = 0;
1269 gint allocation_to_window_y = 0;
1273 if (data->found || !GTK_WIDGET_MAPPED (widget))
1276 /* Note that in the following code, we only count the
1277 * position as being inside a WINDOW widget if it is inside
1278 * widget->window; points that are outside of widget->window
1279 * but within the allocation are not counted. This is consistent
1280 * with the way we highlight drag targets.
1282 * data->x,y are relative to widget->parent->window (if
1283 * widget is not a toplevel, widget->window otherwise).
1284 * We compute the allocation of widget in the same coordinates,
1285 * clipping to widget->window, and all intermediate
1286 * windows. If data->x,y is inside that, then we translate
1287 * our coordinates to be relative to widget->window and
1290 new_allocation = widget->allocation;
1295 GdkWindow *window = widget->window;
1297 /* Compute the offset from allocation-relative to
1298 * window-relative coordinates.
1300 allocation_to_window_x = widget->allocation.x;
1301 allocation_to_window_y = widget->allocation.y;
1303 if (!GTK_WIDGET_NO_WINDOW (widget))
1305 /* The allocation is relative to the parent window for
1306 * window widgets, not to widget->window.
1308 gdk_window_get_position (window, &tx, &ty);
1310 allocation_to_window_x -= tx;
1311 allocation_to_window_y -= ty;
1314 new_allocation.x = 0 + allocation_to_window_x;
1315 new_allocation.y = 0 + allocation_to_window_y;
1317 while (window && window != widget->parent->window)
1319 GdkRectangle window_rect = { 0, 0, 0, 0 };
1321 gdk_window_get_size (window, &window_rect.width, &window_rect.height);
1323 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1325 gdk_window_get_position (window, &tx, &ty);
1326 new_allocation.x += tx;
1328 new_allocation.y += ty;
1331 window = gdk_window_get_parent (window);
1334 if (!window) /* Window and widget heirarchies didn't match. */
1338 if (data->toplevel ||
1339 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1340 (data->x < new_allocation.x + new_allocation.width) &&
1341 (data->y < new_allocation.y + new_allocation.height)))
1343 /* First, check if the drag is in a valid drop site in
1344 * one of our children
1346 if (GTK_IS_CONTAINER (widget))
1348 GtkDragFindData new_data = *data;
1349 GSList *children = NULL;
1352 new_data.x -= x_offset;
1353 new_data.y -= y_offset;
1354 new_data.found = FALSE;
1355 new_data.toplevel = FALSE;
1357 /* need to reference children temporarily in case the
1358 * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1360 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1361 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1363 if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1364 gtk_drag_find_widget (tmp_list->data, &new_data);
1365 gtk_widget_unref (tmp_list->data);
1367 g_slist_free (children);
1369 data->found = new_data.found;
1372 /* If not, and this widget is registered as a drop site, check to
1373 * emit "drag_motion" to check if we are actually in
1377 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1379 data->found = data->callback (widget,
1381 data->x - x_offset - allocation_to_window_x,
1382 data->y - y_offset - allocation_to_window_y,
1384 /* If so, send a "drag_leave" to the last widget */
1387 if (data->info->widget && data->info->widget != widget)
1389 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1391 data->info->widget = widget;
1398 gtk_drag_proxy_begin (GtkWidget *widget,
1399 GtkDragDestInfo *dest_info,
1402 GtkDragSourceInfo *source_info;
1404 GdkDragContext *context;
1405 GtkWidget *ipc_widget;
1407 if (dest_info->proxy_source)
1409 gdk_drag_abort (dest_info->proxy_source->context, time);
1410 gtk_drag_source_info_destroy (dest_info->proxy_source);
1411 dest_info->proxy_source = NULL;
1414 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1415 context = gdk_drag_begin (ipc_widget->window,
1416 dest_info->context->targets);
1418 source_info = gtk_drag_get_source_info (context, TRUE);
1420 source_info->ipc_widget = ipc_widget;
1421 source_info->widget = gtk_widget_ref (widget);
1423 source_info->target_list = gtk_target_list_new (NULL, 0);
1424 tmp_list = dest_info->context->targets;
1427 gtk_target_list_add (source_info->target_list,
1428 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1429 tmp_list = tmp_list->next;
1432 source_info->proxy_dest = dest_info;
1434 gtk_signal_connect (GTK_OBJECT (ipc_widget),
1436 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1439 dest_info->proxy_source = source_info;
1443 gtk_drag_dest_info_destroy (gpointer data)
1445 GtkDragDestInfo *info = data;
1450 static GtkDragDestInfo *
1451 gtk_drag_get_dest_info (GdkDragContext *context,
1454 GtkDragDestInfo *info;
1455 static GQuark info_quark = 0;
1457 info_quark = g_quark_from_static_string ("gtk-dest-info");
1459 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1460 if (!info && create)
1462 info = g_new (GtkDragDestInfo, 1);
1463 info->widget = NULL;
1464 info->context = context;
1465 info->proxy_source = NULL;
1466 info->proxy_data = NULL;
1467 info->dropped = FALSE;
1468 info->proxy_drop_wait = FALSE;
1469 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1470 info, gtk_drag_dest_info_destroy);
1476 static GQuark dest_info_quark = 0;
1478 static GtkDragSourceInfo *
1479 gtk_drag_get_source_info (GdkDragContext *context,
1482 GtkDragSourceInfo *info;
1483 if (!dest_info_quark)
1484 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1486 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1487 if (!info && create)
1489 info = g_new0 (GtkDragSourceInfo, 1);
1490 info->context = context;
1491 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1498 gtk_drag_clear_source_info (GdkDragContext *context)
1500 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1504 gtk_drag_dest_realized (GtkWidget *widget)
1506 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1508 if (GTK_WIDGET_TOPLEVEL (toplevel))
1509 gdk_window_register_dnd (toplevel->window);
1513 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1514 GtkWidget *previous_toplevel)
1516 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1518 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1519 gdk_window_register_dnd (toplevel->window);
1523 gtk_drag_dest_site_destroy (gpointer data)
1525 GtkDragDestSite *site = data;
1527 if (site->proxy_window)
1528 g_object_unref (site->proxy_window);
1530 if (site->target_list)
1531 gtk_target_list_unref (site->target_list);
1537 * Default drag handlers
1540 gtk_drag_dest_leave (GtkWidget *widget,
1541 GdkDragContext *context,
1544 GtkDragDestSite *site;
1546 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1547 g_return_if_fail (site != NULL);
1551 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1553 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1555 gdk_drag_abort (info->proxy_source->context, time);
1556 gtk_drag_source_info_destroy (info->proxy_source);
1557 info->proxy_source = NULL;
1564 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1565 gtk_drag_unhighlight (widget);
1567 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1568 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1571 site->have_drag = FALSE;
1576 gtk_drag_dest_motion (GtkWidget *widget,
1577 GdkDragContext *context,
1582 GtkDragDestSite *site;
1583 GdkDragAction action = 0;
1586 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1587 g_return_val_if_fail (site != NULL, FALSE);
1592 GdkEvent *current_event;
1593 GdkWindow *dest_window;
1594 GdkDragProtocol proto;
1596 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1598 if (!info->proxy_source || info->proxy_source->widget != widget)
1599 gtk_drag_proxy_begin (widget, info, time);
1601 current_event = gtk_get_current_event ();
1603 if (site->proxy_window)
1605 dest_window = site->proxy_window;
1606 proto = site->proxy_protocol;
1610 gdk_drag_find_window (info->proxy_source->context,
1612 current_event->dnd.x_root,
1613 current_event->dnd.y_root,
1614 &dest_window, &proto);
1617 gdk_drag_motion (info->proxy_source->context,
1619 current_event->dnd.x_root,
1620 current_event->dnd.y_root,
1621 context->suggested_action,
1622 context->actions, time);
1624 if (!site->proxy_window && dest_window)
1625 gdk_window_unref (dest_window);
1627 selection = gdk_drag_get_selection (info->proxy_source->context);
1629 selection != gdk_drag_get_selection (info->context))
1630 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1632 gdk_event_free (current_event);
1637 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1639 if (context->suggested_action & site->actions)
1640 action = context->suggested_action;
1647 if ((site->actions & (1 << i)) &&
1648 (context->actions & (1 << i)))
1656 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1658 if (!site->have_drag)
1660 site->have_drag = TRUE;
1661 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1662 gtk_drag_highlight (widget);
1665 gdk_drag_status (context, action, time);
1669 gdk_drag_status (context, 0, time);
1674 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1675 context, x, y, time, &retval);
1677 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1681 gtk_drag_dest_drop (GtkWidget *widget,
1682 GdkDragContext *context,
1687 GtkDragDestSite *site;
1688 GtkDragDestInfo *info;
1690 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1691 g_return_val_if_fail (site != NULL, FALSE);
1693 info = gtk_drag_get_dest_info (context, FALSE);
1694 g_return_val_if_fail (info != NULL, FALSE);
1701 if (info->proxy_source ||
1702 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1704 gtk_drag_drop (info->proxy_source, time);
1708 /* We need to synthesize a motion event, wait for a status,
1709 * and, if we get a good one, do a drop.
1712 GdkEvent *current_event;
1714 GdkWindow *dest_window;
1715 GdkDragProtocol proto;
1717 gtk_drag_proxy_begin (widget, info, time);
1718 info->proxy_drop_wait = TRUE;
1719 info->proxy_drop_time = time;
1721 current_event = gtk_get_current_event ();
1723 if (site->proxy_window)
1725 dest_window = site->proxy_window;
1726 proto = site->proxy_protocol;
1730 gdk_drag_find_window (info->proxy_source->context,
1732 current_event->dnd.x_root,
1733 current_event->dnd.y_root,
1734 &dest_window, &proto);
1737 gdk_drag_motion (info->proxy_source->context,
1739 current_event->dnd.x_root,
1740 current_event->dnd.y_root,
1741 context->suggested_action,
1742 context->actions, time);
1744 if (!site->proxy_window && dest_window)
1745 gdk_window_unref (dest_window);
1747 selection = gdk_drag_get_selection (info->proxy_source->context);
1749 selection != gdk_drag_get_selection (info->context))
1750 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1752 gdk_event_free (current_event);
1761 if (site->flags & GTK_DEST_DEFAULT_DROP)
1763 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1765 if (target == GDK_NONE)
1767 gtk_drag_finish (context, FALSE, FALSE, time);
1771 gtk_drag_get_data (widget, context, target, time);
1774 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1775 context, x, y, time, &retval);
1777 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1785 /*************************************************************
1786 * gtk_drag_begin: Start a drag operation
1789 * widget: Widget from which drag starts
1790 * handlers: List of handlers to supply the data for the drag
1791 * button: Button user used to start drag
1792 * time: Time of event starting drag
1795 *************************************************************/
1798 gtk_drag_begin (GtkWidget *widget,
1799 GtkTargetList *target_list,
1800 GdkDragAction actions,
1804 GtkDragSourceInfo *info;
1805 GList *targets = NULL;
1807 guint32 time = GDK_CURRENT_TIME;
1808 GdkDragAction possible_actions, suggested_action;
1809 GdkDragContext *context;
1810 GtkWidget *ipc_widget;
1812 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1813 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1814 g_return_val_if_fail (target_list != NULL, NULL);
1817 time = gdk_event_get_time (event);
1819 tmp_list = g_list_last (target_list->list);
1822 GtkTargetPair *pair = tmp_list->data;
1823 targets = g_list_prepend (targets,
1824 GINT_TO_POINTER (pair->target));
1825 tmp_list = tmp_list->prev;
1828 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1829 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1831 context = gdk_drag_begin (ipc_widget->window, targets);
1832 g_list_free (targets);
1834 info = gtk_drag_get_source_info (context, TRUE);
1836 info->ipc_widget = ipc_widget;
1837 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1839 info->widget = gtk_widget_ref (widget);
1842 info->button = button;
1843 info->target_list = target_list;
1844 gtk_target_list_ref (target_list);
1846 info->possible_actions = actions;
1848 info->cursor = NULL;
1849 info->status = GTK_DRAG_STATUS_DRAG;
1850 info->last_event = NULL;
1851 info->selections = NULL;
1852 info->icon_window = NULL;
1853 info->destroy_icon = FALSE;
1855 gtk_drag_get_event_actions (event, info->button, actions,
1856 &suggested_action, &possible_actions);
1858 info->cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), suggested_action);
1860 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1861 * the drag icon, it will be in the right place
1863 if (event && event->type == GDK_MOTION_NOTIFY)
1865 info->cur_x = event->motion.x_root;
1866 info->cur_y = event->motion.y_root;
1870 gdk_window_get_pointer (gtk_widget_get_root_window (widget),
1871 &info->cur_x, &info->cur_y, NULL);
1874 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1877 if (event && event->type == GDK_MOTION_NOTIFY)
1878 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1880 info->start_x = info->cur_x;
1881 info->start_y = info->cur_y;
1883 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1884 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1885 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1886 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1887 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
1888 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1889 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
1890 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1891 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1892 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1894 /* We use a GTK grab here to override any grabs that the widget
1895 * we are dragging from might have held
1897 gtk_grab_add (info->ipc_widget);
1898 if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1899 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1900 GDK_BUTTON_RELEASE_MASK, NULL,
1901 info->cursor, time) == 0)
1903 if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1905 gtk_drag_cancel (info, time);
1910 info->have_grab = TRUE;
1911 info->grab_time = time;
1913 return info->context;
1916 /*************************************************************
1917 * gtk_drag_source_set:
1918 * Register a drop site, and possibly add default behaviors.
1921 * start_button_mask: Mask of allowed buttons to start drag
1922 * targets: Table of targets for this source
1924 * actions: Actions allowed for this source
1926 *************************************************************/
1929 gtk_drag_source_set (GtkWidget *widget,
1930 GdkModifierType start_button_mask,
1931 const GtkTargetEntry *targets,
1933 GdkDragAction actions)
1935 GtkDragSourceSite *site;
1937 g_return_if_fail (GTK_IS_WIDGET (widget));
1939 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1941 gtk_widget_add_events (widget,
1942 gtk_widget_get_events (widget) |
1943 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1944 GDK_BUTTON_MOTION_MASK);
1948 if (site->target_list)
1949 gtk_target_list_unref (site->target_list);
1953 site = g_new0 (GtkDragSourceSite, 1);
1955 site->icon_type = GTK_IMAGE_EMPTY;
1957 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1958 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1960 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1961 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1964 gtk_object_set_data_full (GTK_OBJECT (widget),
1966 site, gtk_drag_source_site_destroy);
1969 site->start_button_mask = start_button_mask;
1972 site->target_list = gtk_target_list_new (targets, n_targets);
1974 site->target_list = NULL;
1976 site->actions = actions;
1980 /*************************************************************
1981 * gtk_drag_source_unset
1982 * Unregister this widget as a drag source.
1986 *************************************************************/
1989 gtk_drag_source_unset (GtkWidget *widget)
1991 GtkDragSourceSite *site;
1993 g_return_if_fail (GTK_IS_WIDGET (widget));
1995 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1999 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
2000 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
2005 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2007 switch (site->icon_type)
2009 case GTK_IMAGE_EMPTY:
2011 case GTK_IMAGE_PIXMAP:
2012 if (site->icon_data.pixmap.pixmap)
2013 gdk_pixmap_unref (site->icon_data.pixmap.pixmap);
2014 if (site->icon_mask)
2015 gdk_pixmap_unref (site->icon_mask);
2017 case GTK_IMAGE_PIXBUF:
2018 g_object_unref (G_OBJECT (site->icon_data.pixbuf.pixbuf));
2020 case GTK_IMAGE_STOCK:
2021 g_free (G_OBJECT (site->icon_data.stock.stock_id));
2024 g_assert_not_reached();
2027 site->icon_type = GTK_IMAGE_EMPTY;
2030 gdk_colormap_unref (site->colormap);
2031 site->colormap = NULL;
2035 * gtk_drag_source_set_icon:
2036 * @widget: a #GtkWidget
2037 * @colormap: the colormap of the icon
2038 * @pixmap: the image data for the icon
2039 * @mask: the transparency mask for an image.
2041 * Sets the icon that will be used for drags from a particular widget
2042 * from a pixmap/mask. GTK+ retains references for the arguments, and
2043 * will release them when they are no longer needed.
2044 * Use gtk_drag_source_set_icon_pixbuf() instead.
2047 gtk_drag_source_set_icon (GtkWidget *widget,
2048 GdkColormap *colormap,
2052 GtkDragSourceSite *site;
2054 g_return_if_fail (GTK_IS_WIDGET (widget));
2055 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2056 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2057 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2059 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2060 g_return_if_fail (site != NULL);
2062 gdk_colormap_ref (colormap);
2063 gdk_pixmap_ref (pixmap);
2065 gdk_pixmap_ref (mask);
2067 gtk_drag_source_unset_icon (site);
2069 site->icon_type = GTK_IMAGE_PIXMAP;
2071 site->icon_data.pixmap.pixmap = pixmap;
2072 site->icon_mask = mask;
2073 site->colormap = colormap;
2077 * gtk_drag_source_set_icon_pixbuf:
2078 * @widget: a #GtkWidget
2079 * @pixbuf: the #GdkPixbuf for the drag icon
2081 * Sets the icon that will be used for drags from a particular widget
2082 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2083 * release it when it is no longer needed.
2086 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2089 GtkDragSourceSite *site;
2091 g_return_if_fail (GTK_IS_WIDGET (widget));
2092 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2094 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2095 g_return_if_fail (site != NULL);
2096 gdk_pixbuf_ref (pixbuf);
2098 gtk_drag_source_unset_icon (site);
2100 site->icon_type = GTK_IMAGE_PIXBUF;
2101 site->icon_data.pixbuf.pixbuf = pixbuf;
2105 * gtk_drag_source_set_icon_stock:
2106 * @widget: a #GtkWidget
2107 * @stock_id: the ID of the stock icon to use
2109 * Sets the icon that will be used for drags from a particular source
2113 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2114 const gchar *stock_id)
2116 GtkDragSourceSite *site;
2118 g_return_if_fail (GTK_IS_WIDGET (widget));
2119 g_return_if_fail (stock_id != NULL);
2121 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2122 g_return_if_fail (site != NULL);
2124 gtk_drag_source_unset_icon (site);
2126 site->icon_data.stock.stock_id = g_strdup (stock_id);
2130 gtk_drag_set_icon_window (GdkDragContext *context,
2134 gboolean destroy_on_release)
2136 GtkDragSourceInfo *info;
2138 info = gtk_drag_get_source_info (context, FALSE);
2139 gtk_drag_remove_icon (info);
2141 info->icon_window = widget;
2142 info->hot_x = hot_x;
2143 info->hot_y = hot_y;
2147 gtk_widget_set_uposition (widget,
2148 info->cur_x - info->hot_x,
2149 info->cur_y - info->hot_y);
2150 gtk_widget_ref (widget);
2151 gdk_window_raise (widget->window);
2152 gtk_widget_show (widget);
2155 info->destroy_icon = destroy_on_release;
2159 * gtk_drag_set_icon_widget:
2160 * @context: the context for a drag. (This must be called
2161 with a context for the source side of a drag)
2162 * @widget: a toplevel window to use as an icon.
2163 * @hot_x: the X offset within @widget of the hotspot.
2164 * @hot_y: the Y offset within @widget of the hotspot.
2166 * Changes the icon for a widget to a given widget. GTK+
2167 * will not destroy the icon, so if you don't want
2168 * it to persist, you should connect to the "drag_end"
2169 * signal and destroy it yourself.
2172 gtk_drag_set_icon_widget (GdkDragContext *context,
2177 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2178 g_return_if_fail (context->is_source);
2179 g_return_if_fail (GTK_IS_WIDGET (widget));
2181 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2185 set_icon_stock_pixbuf (GdkDragContext *context,
2186 const gchar *stock_id,
2196 GdkColormap *colormap;
2198 g_return_if_fail (context != NULL);
2199 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2200 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2202 screen = gdk_drawable_get_screen (context->source_window);
2203 colormap = gdk_screen_get_rgb_colormap (screen);
2205 gtk_widget_push_colormap (colormap);
2206 window = gtk_window_new (GTK_WINDOW_POPUP);
2207 gtk_window_set_screen (GTK_WINDOW (window), screen);
2208 gtk_widget_pop_colormap ();
2210 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2211 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2215 pixbuf = gtk_widget_render_icon (window, stock_id,
2216 GTK_ICON_SIZE_DND, NULL);
2220 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2221 gtk_widget_destroy (window);
2227 width = gdk_pixbuf_get_width (pixbuf);
2228 height = gdk_pixbuf_get_width (pixbuf);
2230 gtk_widget_set_usize (window,
2231 gdk_pixbuf_get_width (pixbuf),
2232 gdk_pixbuf_get_height (pixbuf));
2233 gtk_widget_realize (window);
2235 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf, colormap, &pixmap, &mask, 128);
2237 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2240 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2242 g_object_unref (G_OBJECT (pixmap));
2245 g_object_unref (G_OBJECT (mask));
2247 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2251 * gtk_drag_set_icon_pixbuf:
2252 * @context: the context for a drag. (This must be called
2253 * with a context for the source side of a drag)
2254 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2255 * @hot_x: the X offset within @widget of the hotspot.
2256 * @hot_y: the Y offset within @widget of the hotspot.
2258 * Sets @pixbuf as the icon for a given drag.
2261 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2266 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2267 g_return_if_fail (context->is_source);
2268 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2270 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2274 * gtk_drag_set_icon_stock:
2275 * @context: the context for a drag. (This must be called
2276 * with a context for the source side of a drag)
2277 * @stock_id: the ID of the stock icon to use for the drag.
2278 * @hot_x: the X offset within the icon of the hotspot.
2279 * @hot_y: the Y offset within the icon of the hotspot.
2281 * Sets the the icon for a given drag from a stock ID.
2284 gtk_drag_set_icon_stock (GdkDragContext *context,
2285 const gchar *stock_id,
2289 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2290 g_return_if_fail (context->is_source);
2291 g_return_if_fail (stock_id != NULL);
2293 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2297 * gtk_drag_set_icon_pixmap:
2298 * @context: the context for a drag. (This must be called
2299 * with a context for the source side of a drag)
2300 * @colormap: the colormap of the icon
2301 * @pixmap: the image data for the icon
2302 * @mask: the transparency mask for the icon
2303 * @hot_x: the X offset within @pixmap of the hotspot.
2304 * @hot_y: the Y offset within @pixmap of the hotspot.
2306 * Sets @pixmap as the icon for a given drag. GTK+ retains
2307 * references for the arguments, and will release them when
2308 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2309 * will be more convenient to use.
2312 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2313 GdkColormap *colormap,
2322 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2323 g_return_if_fail (context->is_source);
2324 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2325 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2326 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2328 gdk_window_get_size (pixmap, &width, &height);
2330 gtk_widget_push_colormap (colormap);
2332 window = gtk_window_new (GTK_WINDOW_POPUP);
2333 gtk_window_set_screen (GTK_WINDOW (window), gdk_drawable_get_screen (context->source_window));
2334 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2335 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2337 gtk_widget_pop_colormap ();
2339 gtk_widget_set_usize (window, width, height);
2340 gtk_widget_realize (window);
2342 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2345 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2347 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2351 * gtk_drag_set_icon_default:
2352 * @context: the context for a drag. (This must be called
2353 with a context for the source side of a drag)
2355 * Sets the icon for a particular drag to the default
2359 gtk_drag_set_icon_default (GdkDragContext *context)
2361 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2362 g_return_if_fail (context->is_source);
2364 if (!default_icon_pixmap)
2365 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2367 gtk_drag_set_icon_pixmap (context,
2368 default_icon_colormap,
2369 default_icon_pixmap,
2372 default_icon_hot_y);
2376 * gtk_drag_set_default_icon:
2377 * @colormap: the colormap of the icon
2378 * @pixmap: the image data for the icon
2379 * @mask: the transparency mask for an image.
2380 * @hot_x: The X offset within @widget of the hotspot.
2381 * @hot_y: The Y offset within @widget of the hotspot.
2383 * Changes the default drag icon. GTK+ retains references for the
2384 * arguments, and will release them when they are no longer needed.
2385 * This function is obsolete. The default icon should now be changed
2386 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2389 gtk_drag_set_default_icon (GdkColormap *colormap,
2395 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2396 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2397 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2399 if (default_icon_colormap)
2400 gdk_colormap_unref (default_icon_colormap);
2401 if (default_icon_pixmap)
2402 gdk_pixmap_unref (default_icon_pixmap);
2403 if (default_icon_mask)
2404 gdk_pixmap_unref (default_icon_mask);
2406 default_icon_colormap = colormap;
2407 gdk_colormap_ref (colormap);
2409 default_icon_pixmap = pixmap;
2410 gdk_pixmap_ref (pixmap);
2412 default_icon_mask = mask;
2414 gdk_pixmap_ref (mask);
2416 default_icon_hot_x = hot_x;
2417 default_icon_hot_y = hot_y;
2421 /*************************************************************
2422 * _gtk_drag_source_handle_event:
2423 * Called from widget event handling code on Drag events
2427 * toplevel: Toplevel widget that received the event
2430 *************************************************************/
2433 _gtk_drag_source_handle_event (GtkWidget *widget,
2436 GtkDragSourceInfo *info;
2437 GdkDragContext *context;
2439 g_return_if_fail (widget != NULL);
2440 g_return_if_fail (event != NULL);
2442 context = event->dnd.context;
2443 info = gtk_drag_get_source_info (context, FALSE);
2447 switch (event->type)
2449 case GDK_DRAG_STATUS:
2453 if (info->proxy_dest)
2455 if (!event->dnd.send_event)
2457 if (info->proxy_dest->proxy_drop_wait)
2459 gboolean result = context->action != 0;
2461 /* Aha - we can finally pass the MOTIF DROP on... */
2462 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2464 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2466 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2470 gdk_drag_status (info->proxy_dest->context,
2471 event->dnd.context->action,
2476 else if (info->have_grab)
2478 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2479 event->dnd.context->action);
2480 if (info->cursor != cursor)
2482 gdk_pointer_grab (widget->window, FALSE,
2483 GDK_POINTER_MOTION_MASK |
2484 GDK_POINTER_MOTION_HINT_MASK |
2485 GDK_BUTTON_RELEASE_MASK,
2487 cursor, info->grab_time);
2488 info->cursor = cursor;
2491 if (info->last_event)
2493 gtk_drag_update (info,
2494 info->cur_x, info->cur_y,
2496 info->last_event = NULL;
2502 case GDK_DROP_FINISHED:
2503 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2506 g_assert_not_reached ();
2510 /*************************************************************
2511 * gtk_drag_source_check_selection:
2512 * Check if we've set up handlers/claimed the selection
2513 * for a given drag. If not, add them.
2517 *************************************************************/
2520 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2526 tmp_list = info->selections;
2529 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2531 tmp_list = tmp_list->next;
2534 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2538 info->selections = g_list_prepend (info->selections,
2539 GUINT_TO_POINTER (selection));
2541 tmp_list = info->target_list->list;
2544 GtkTargetPair *pair = tmp_list->data;
2546 gtk_selection_add_target (info->ipc_widget,
2550 tmp_list = tmp_list->next;
2553 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2555 gtk_selection_add_target (info->ipc_widget,
2557 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2558 TARGET_MOTIF_SUCCESS);
2559 gtk_selection_add_target (info->ipc_widget,
2561 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2562 TARGET_MOTIF_FAILURE);
2565 gtk_selection_add_target (info->ipc_widget,
2567 gdk_atom_intern ("DELETE", FALSE),
2571 /*************************************************************
2572 * gtk_drag_drop_finished:
2573 * Clean up from the drag, and display snapback, if necessary.
2579 *************************************************************/
2582 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2586 gtk_drag_source_release_selections (info, time);
2588 if (info->proxy_dest)
2590 /* The time from the event isn't reliable for Xdnd drags */
2591 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2592 info->proxy_dest->proxy_drop_time);
2593 gtk_drag_source_info_destroy (info);
2599 gtk_drag_source_info_destroy (info);
2603 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2607 anim->n_steps = MAX (info->cur_x - info->start_x,
2608 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2609 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2610 if (info->icon_window)
2612 gtk_widget_show (info->icon_window);
2613 gdk_window_raise (info->icon_window->window);
2616 /* Mark the context as dead, so if the destination decides
2617 * to respond really late, we still are OK.
2619 gtk_drag_clear_source_info (info->context);
2620 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2626 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2629 GdkDisplay *display = gtk_widget_get_display (info->widget);
2630 GList *tmp_list = info->selections;
2634 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2635 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2636 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2638 tmp_list = tmp_list->next;
2641 g_list_free (info->selections);
2642 info->selections = NULL;
2645 /*************************************************************
2647 * Send a drop event.
2651 *************************************************************/
2654 gtk_drag_drop (GtkDragSourceInfo *info,
2657 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2659 GtkSelectionData selection_data;
2661 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2663 tmp_list = info->target_list->list;
2666 GtkTargetPair *pair = tmp_list->data;
2668 if (pair->target == target)
2670 selection_data.selection = GDK_NONE;
2671 selection_data.target = target;
2672 selection_data.data = NULL;
2673 selection_data.length = -1;
2675 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2676 info->context, &selection_data,
2680 /* FIXME: Should we check for length >= 0 here? */
2681 gtk_drag_drop_finished (info, TRUE, time);
2684 tmp_list = tmp_list->next;
2686 gtk_drag_drop_finished (info, FALSE, time);
2690 if (info->icon_window)
2691 gtk_widget_hide (info->icon_window);
2693 gdk_drag_drop (info->context, time);
2694 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2695 gtk_drag_abort_timeout,
2701 * Source side callbacks.
2705 gtk_drag_source_event_cb (GtkWidget *widget,
2709 GtkDragSourceSite *site;
2710 gboolean retval = FALSE;
2711 site = (GtkDragSourceSite *)data;
2713 switch (event->type)
2715 case GDK_BUTTON_PRESS:
2716 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2718 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2719 site->x = event->button.x;
2720 site->y = event->button.y;
2724 case GDK_BUTTON_RELEASE:
2725 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2726 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2729 case GDK_MOTION_NOTIFY:
2730 if (site->state & event->motion.state & site->start_button_mask)
2732 /* FIXME: This is really broken and can leave us
2738 if (site->state & event->motion.state &
2739 GDK_BUTTON1_MASK << (i - 1))
2743 if (gtk_drag_check_threshold (widget, site->x, site->y,
2744 event->motion.x, event->motion.y))
2746 GtkDragSourceInfo *info;
2747 GdkDragContext *context;
2750 context = gtk_drag_begin (widget, site->target_list,
2754 info = gtk_drag_get_source_info (context, FALSE);
2756 if (!info->icon_window)
2758 switch (site->icon_type)
2760 case GTK_IMAGE_EMPTY:
2761 gtk_drag_set_icon_default (context);
2763 case GTK_IMAGE_PIXMAP:
2764 gtk_drag_set_icon_pixmap (context,
2766 site->icon_data.pixmap.pixmap,
2770 case GTK_IMAGE_PIXBUF:
2771 gtk_drag_set_icon_pixbuf (context,
2772 site->icon_data.pixbuf.pixbuf,
2775 case GTK_IMAGE_STOCK:
2776 gtk_drag_set_icon_stock (context,
2777 site->icon_data.stock.stock_id,
2781 g_assert_not_reached();
2791 default: /* hit for 2/3BUTTON_PRESS */
2799 gtk_drag_source_site_destroy (gpointer data)
2801 GtkDragSourceSite *site = data;
2803 if (site->target_list)
2804 gtk_target_list_unref (site->target_list);
2806 gtk_drag_source_unset_icon (site);
2811 gtk_drag_selection_get (GtkWidget *widget,
2812 GtkSelectionData *selection_data,
2817 GtkDragSourceInfo *info = data;
2818 static GdkAtom null_atom = GDK_NONE;
2822 null_atom = gdk_atom_intern ("NULL", FALSE);
2827 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2830 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2832 case TARGET_MOTIF_SUCCESS:
2833 gtk_drag_drop_finished (info, TRUE, time);
2834 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2836 case TARGET_MOTIF_FAILURE:
2837 gtk_drag_drop_finished (info, FALSE, time);
2838 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2841 if (info->proxy_dest)
2843 /* This is sort of dangerous and needs to be thought
2846 info->proxy_dest->proxy_data = selection_data;
2847 gtk_drag_get_data (info->widget,
2848 info->proxy_dest->context,
2849 selection_data->target,
2852 info->proxy_dest->proxy_data = NULL;
2856 if (gtk_target_list_find (info->target_list,
2857 selection_data->target,
2860 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2872 gtk_drag_anim_timeout (gpointer data)
2874 GtkDragAnim *anim = data;
2878 GDK_THREADS_ENTER ();
2880 if (anim->step == anim->n_steps)
2882 gtk_drag_source_info_destroy (anim->info);
2889 x = (anim->info->start_x * (anim->step + 1) +
2890 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2891 y = (anim->info->start_y * (anim->step + 1) +
2892 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2893 if (anim->info->icon_window)
2894 gtk_widget_set_uposition (anim->info->icon_window,
2895 x - anim->info->hot_x,
2896 y - anim->info->hot_y);
2903 GDK_THREADS_LEAVE ();
2909 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2911 if (info->icon_window)
2913 gtk_widget_hide (info->icon_window);
2914 if (info->destroy_icon)
2915 gtk_widget_destroy (info->icon_window);
2917 gtk_widget_unref (info->icon_window);
2918 info->icon_window = NULL;
2923 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
2925 gtk_drag_remove_icon (info);
2927 if (!info->proxy_dest)
2928 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2932 gtk_widget_unref (info->widget);
2934 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2935 gtk_selection_remove_all (info->ipc_widget);
2936 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2937 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2938 gtk_drag_release_ipc_widget (info->ipc_widget);
2940 gtk_target_list_unref (info->target_list);
2942 gtk_drag_clear_source_info (info->context);
2943 gdk_drag_context_unref (info->context);
2945 if (info->drop_timeout)
2946 gtk_timeout_remove (info->drop_timeout);
2951 /*************************************************************
2953 * Function to update the status of the drag when the
2954 * cursor moves or the modifier changes
2956 * info: DragSourceInfo for the drag
2957 * x_root, y_root: position of darg
2958 * event: The event that triggered this call
2960 *************************************************************/
2963 gtk_drag_update (GtkDragSourceInfo *info,
2968 GdkDragAction action;
2969 GdkDragAction possible_actions;
2970 GdkWindow *window = NULL;
2971 GdkWindow *dest_window;
2972 GdkDragProtocol protocol;
2974 guint32 time = gtk_drag_get_event_time (event);
2976 gtk_drag_get_event_actions (event,
2978 info->possible_actions,
2979 &action, &possible_actions);
2980 info->cur_x = x_root;
2981 info->cur_y = y_root;
2983 if (info->icon_window)
2985 gdk_window_raise (info->icon_window->window);
2986 gtk_widget_set_uposition (info->icon_window,
2987 info->cur_x - info->hot_x,
2988 info->cur_y - info->hot_y);
2989 window = info->icon_window->window;
2992 gdk_drag_find_window (info->context,
2993 window, x_root, y_root,
2994 &dest_window, &protocol);
2996 if (gdk_drag_motion (info->context, dest_window, protocol,
2997 x_root, y_root, action,
3001 if (info->last_event != event) /* Paranoia, should not happen */
3003 if (info->last_event)
3004 gdk_event_free ((GdkEvent *)info->last_event);
3005 info->last_event = gdk_event_copy ((GdkEvent *)event);
3010 if (info->last_event)
3012 gdk_event_free ((GdkEvent *)info->last_event);
3013 info->last_event = NULL;
3018 gdk_window_unref (dest_window);
3020 selection = gdk_drag_get_selection (info->context);
3022 gtk_drag_source_check_selection (info, selection, time);
3025 /*************************************************************
3027 * Called when the user finishes to drag, either by
3028 * releasing the mouse, or by pressing Esc.
3030 * info: Source info for the drag
3031 * time: Timestamp for ending the drag
3033 *************************************************************/
3036 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3038 GdkEvent *send_event;
3039 GtkWidget *source_widget = info->widget;
3040 GdkDisplay *display = gtk_widget_get_display (source_widget);
3042 info->have_grab = FALSE;
3044 gdk_display_pointer_ungrab (display, time);
3045 gdk_display_keyboard_ungrab (display, time);
3046 gtk_grab_remove (info->ipc_widget);
3048 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3049 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
3051 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3052 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
3054 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3055 GTK_SIGNAL_FUNC (gtk_drag_key_cb),
3058 /* Send on a release pair to the the original
3059 * widget to convince it to release its grab. We need to
3060 * call gtk_propagate_event() here, instead of
3061 * gtk_widget_event() because widget like GtkList may
3062 * expect propagation.
3065 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3066 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3067 send_event->button.send_event = TRUE;
3068 send_event->button.time = time;
3069 send_event->button.x = 0;
3070 send_event->button.y = 0;
3071 send_event->button.axes = NULL;
3072 send_event->button.state = 0;
3073 send_event->button.button = info->button;
3074 send_event->button.device = gdk_display_get_core_pointer (display);
3075 send_event->button.x_root = 0;
3076 send_event->button.y_root = 0;
3078 gtk_propagate_event (source_widget, send_event);
3079 gdk_event_free (send_event);
3082 /*************************************************************
3084 * Called on cancellation of a drag, either by the user
3085 * or programmatically.
3087 * info: Source info for the drag
3088 * time: Timestamp for ending the drag
3090 *************************************************************/
3093 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3095 gtk_drag_end (info, time);
3096 gdk_drag_abort (info->context, time);
3097 gtk_drag_drop_finished (info, FALSE, time);
3100 /*************************************************************
3101 * gtk_drag_motion_cb:
3102 * "motion_notify_event" callback during drag.
3106 *************************************************************/
3109 gtk_drag_motion_cb (GtkWidget *widget,
3110 GdkEventMotion *event,
3113 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3114 gint x_root, y_root;
3118 GdkWindow *root_window = gtk_widget_get_root_window (widget);
3120 gdk_window_get_pointer (root_window, &x_root, &y_root, NULL);
3121 event->x_root = x_root;
3122 event->y_root = y_root;
3125 gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
3130 /*************************************************************
3132 * "key_press/release_event" callback during drag.
3136 *************************************************************/
3139 gtk_drag_key_cb (GtkWidget *widget,
3143 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3144 GdkModifierType state;
3145 GdkWindow *root_window;
3147 if (event->type == GDK_KEY_PRESS)
3149 if (event->keyval == GDK_Escape)
3151 gtk_drag_cancel (info, event->time);
3157 /* Now send a "motion" so that the modifier state is updated */
3159 /* The state is not yet updated in the event, so we need
3160 * to query it here. We could use XGetModifierMapping, but
3161 * that would be overkill.
3163 root_window = gtk_widget_get_root_window (widget);
3164 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3166 event->state = state;
3167 gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
3172 /*************************************************************
3173 * gtk_drag_button_release_cb:
3174 * "button_release_event" callback during drag.
3178 *************************************************************/
3181 gtk_drag_button_release_cb (GtkWidget *widget,
3182 GdkEventButton *event,
3185 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3187 if (event->button != info->button)
3190 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3192 gtk_drag_end (info, event->time);
3193 gtk_drag_drop (info, event->time);
3197 gtk_drag_cancel (info, event->time);
3204 gtk_drag_abort_timeout (gpointer data)
3206 GtkDragSourceInfo *info = data;
3207 guint32 time = GDK_CURRENT_TIME;
3209 GDK_THREADS_ENTER ();
3211 if (info->proxy_dest)
3212 time = info->proxy_dest->proxy_drop_time;
3214 info->drop_timeout = 0;
3215 gtk_drag_drop_finished (info, FALSE, time);
3217 GDK_THREADS_LEAVE ();
3223 * gtk_drag_check_threshold:
3224 * @widget: a #GtkWidget
3225 * @start_x: X coordinate of start of drag
3226 * @start_y: Y coordinate of start of drag
3227 * @current_x: current X coordinate
3228 * @current_y: current Y coordinate
3230 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3231 * at (@current_x, @current_y) has passed the GTK+ drag threshhold, and thus
3232 * should trigger the beginning of a drag-and-drop operation.
3234 * Return Value: %TRUE if the drag threshold has been passed.
3237 gtk_drag_check_threshold (GtkWidget *widget,
3243 gint drag_threshold;
3245 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3247 g_object_get (gtk_widget_get_settings (widget),
3248 "gtk-dnd-drag-threshold", &drag_threshold,
3251 return (ABS (current_x - start_x) > drag_threshold ||
3252 ABS (current_y - start_y) > drag_threshold);