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 (widget != NULL);
620 g_return_if_fail (context != NULL);
622 selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
624 gdk_drag_context_ref (context);
625 gtk_widget_ref (widget);
627 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
628 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
630 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
632 gtk_selection_convert (selection_widget,
633 gdk_drag_get_selection (context),
639 /*************************************************************
640 * gtk_drag_get_source_widget:
641 * Get the widget the was the source of this drag, if
642 * the drag originated from this application.
644 * context: The drag context for this drag
646 * The source widget, or NULL if the drag originated from
647 * a different application.
648 *************************************************************/
651 gtk_drag_get_source_widget (GdkDragContext *context)
655 tmp_list = source_widgets;
658 GtkWidget *ipc_widget = tmp_list->data;
660 if (ipc_widget->window == context->source_window)
662 GtkDragSourceInfo *info;
663 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
665 return info ? info->widget : NULL;
668 tmp_list = tmp_list->next;
674 /*************************************************************
676 * Notify the drag source that the transfer of data
679 * context: The drag context for this drag
680 * success: Was the data successfully transferred?
681 * time: The timestamp to use when notifying the destination.
683 *************************************************************/
686 gtk_drag_finish (GdkDragContext *context,
691 GdkAtom target = GDK_NONE;
693 g_return_if_fail (context != NULL);
697 target = gdk_atom_intern ("DELETE", FALSE);
699 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
701 target = gdk_atom_intern (success ?
702 "XmTRANSFER_SUCCESS" :
703 "XmTRANSFER_FAILURE",
707 if (target != GDK_NONE)
709 GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
711 gdk_drag_context_ref (context);
713 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
714 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
715 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
718 gtk_selection_convert (selection_widget,
719 gdk_drag_get_selection (context),
724 if (!(success && del))
725 gdk_drop_finish (context, success, time);
728 /*************************************************************
729 * gtk_drag_highlight_expose:
730 * Callback for expose_event for highlighted widgets.
736 *************************************************************/
739 gtk_drag_highlight_expose (GtkWidget *widget,
740 GdkEventExpose *event,
743 gint x, y, width, height;
745 if (GTK_WIDGET_DRAWABLE (widget))
747 if (GTK_WIDGET_NO_WINDOW (widget))
749 x = widget->allocation.x;
750 y = widget->allocation.y;
751 width = widget->allocation.width;
752 height = widget->allocation.height;
758 gdk_window_get_size (widget->window, &width, &height);
761 gtk_draw_shadow (widget->style, widget->window,
762 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
763 x, y, width, height);
765 gdk_draw_rectangle (widget->window,
766 widget->style->black_gc,
768 x, y, width - 1, height - 1);
774 /*************************************************************
775 * gtk_drag_highlight:
776 * Highlight the given widget in the default manner.
780 *************************************************************/
783 gtk_drag_highlight (GtkWidget *widget)
785 gtk_signal_connect_after (GTK_OBJECT (widget), "expose_event",
786 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
789 gtk_widget_queue_draw (widget);
792 /*************************************************************
793 * gtk_drag_unhighlight:
794 * Refresh the given widget to remove the highlight.
798 *************************************************************/
801 gtk_drag_unhighlight (GtkWidget *widget)
803 g_return_if_fail (widget != NULL);
805 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
806 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
809 gtk_widget_queue_clear (widget);
813 gtk_drag_dest_set_internal (GtkWidget *widget,
814 GtkDragDestSite *site)
816 GtkDragDestSite *old_site;
818 g_return_if_fail (widget != NULL);
820 /* HACK, do this in the destroy */
821 old_site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
823 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), old_site);
825 if (GTK_WIDGET_REALIZED (widget))
826 gtk_drag_dest_realized (widget);
828 gtk_signal_connect (GTK_OBJECT (widget), "realize",
829 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), site);
830 gtk_signal_connect (GTK_OBJECT (widget), "hierarchy_changed",
831 GTK_SIGNAL_FUNC (gtk_drag_dest_hierarchy_changed), site);
833 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
834 site, gtk_drag_dest_site_destroy);
838 /*************************************************************
840 * Register a drop site, and possibly add default behaviors.
843 * flags: Which types of default drag behavior to use
844 * targets: Table of targets that can be accepted
845 * n_targets: Number of of entries in targets
848 *************************************************************/
851 gtk_drag_dest_set (GtkWidget *widget,
852 GtkDestDefaults flags,
853 const GtkTargetEntry *targets,
855 GdkDragAction actions)
857 GtkDragDestSite *site;
859 g_return_if_fail (widget != NULL);
861 site = g_new (GtkDragDestSite, 1);
864 site->have_drag = FALSE;
866 site->target_list = gtk_target_list_new (targets, n_targets);
868 site->target_list = NULL;
869 site->actions = actions;
870 site->do_proxy = FALSE;
871 site->proxy_window = NULL;
873 gtk_drag_dest_set_internal (widget, site);
876 /*************************************************************
877 * gtk_drag_dest_set_proxy:
878 * Set up this widget to proxy drags elsewhere.
881 * proxy_window: window to which forward drag events
882 * protocol: Drag protocol which the dest widget accepts
883 * use_coordinates: If true, send the same coordinates to the
884 * destination, because it is a embedded
887 *************************************************************/
890 gtk_drag_dest_set_proxy (GtkWidget *widget,
891 GdkWindow *proxy_window,
892 GdkDragProtocol protocol,
893 gboolean use_coordinates)
895 GtkDragDestSite *site;
897 g_return_if_fail (widget != NULL);
899 site = g_new (GtkDragDestSite, 1);
902 site->have_drag = FALSE;
903 site->target_list = NULL;
905 site->proxy_window = proxy_window;
907 gdk_window_ref (proxy_window);
908 site->do_proxy = TRUE;
909 site->proxy_protocol = protocol;
910 site->proxy_coords = use_coordinates;
912 gtk_drag_dest_set_internal (widget, site);
915 /*************************************************************
916 * gtk_drag_dest_unset
917 * Unregister this widget as a drag target.
921 *************************************************************/
924 gtk_drag_dest_unset (GtkWidget *widget)
926 g_return_if_fail (widget != NULL);
928 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
932 * gtk_drag_dest_get_target_list:
933 * @widget: a #GtkWidget
935 * Returns the list of targets this widget can accept from
938 * Return value: the #GtkTargetList, or %NULL if none
941 gtk_drag_dest_get_target_list (GtkWidget *widget)
943 GtkDragDestSite *site;
945 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
947 return site ? site->target_list : NULL;
951 * gtk_drag_dest_set_target_list:
952 * @widget: a #GtkWidget that's a drag destination
953 * @target_list: list of droppable targets, or %NULL for none
955 * Sets the target types that this widget can accept from drag-and-drop.
956 * The widget must first be made into a drag destination with
957 * gtk_drag_dest_set().
960 gtk_drag_dest_set_target_list (GtkWidget *widget,
961 GtkTargetList *target_list)
963 GtkDragDestSite *site;
965 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
969 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");
974 gtk_target_list_ref (target_list);
976 if (site->target_list)
977 gtk_target_list_unref (site->target_list);
979 site->target_list = target_list;
983 /*************************************************************
984 * _gtk_drag_dest_handle_event:
985 * Called from widget event handling code on Drag events
989 * toplevel: Toplevel widget that received the event
992 *************************************************************/
995 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
998 GtkDragDestInfo *info;
999 GdkDragContext *context;
1001 g_return_if_fail (toplevel != NULL);
1002 g_return_if_fail (event != NULL);
1004 context = event->dnd.context;
1006 info = gtk_drag_get_dest_info (context, TRUE);
1008 /* Find the widget for the event */
1009 switch (event->type)
1011 case GDK_DRAG_ENTER:
1014 case GDK_DRAG_LEAVE:
1017 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1018 info->widget = NULL;
1022 case GDK_DRAG_MOTION:
1023 case GDK_DROP_START:
1025 GtkDragFindData data;
1028 if (event->type == GDK_DROP_START)
1030 info->dropped = TRUE;
1031 /* We send a leave here so that the widget unhighlights
1036 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1037 info->widget = NULL;
1041 gdk_window_get_origin (toplevel->window, &tx, &ty);
1043 data.x = event->dnd.x_root - tx;
1044 data.y = event->dnd.y_root - ty;
1045 data.context = context;
1048 data.toplevel = TRUE;
1049 data.callback = (event->type == GDK_DRAG_MOTION) ?
1050 gtk_drag_dest_motion : gtk_drag_dest_drop;
1051 data.time = event->dnd.time;
1053 gtk_drag_find_widget (toplevel, &data);
1055 if (info->widget && !data.found)
1057 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1058 info->widget = NULL;
1063 if (event->type == GDK_DRAG_MOTION)
1066 gdk_drag_status (context, 0, event->dnd.time);
1068 else if (event->type == GDK_DROP_START && !info->proxy_source)
1070 gdk_drop_reply (context, data.found, event->dnd.time);
1071 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1072 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1078 g_assert_not_reached ();
1083 * gtk_drag_dest_find_target:
1084 * @widget: drag destination widget
1085 * @context: drag context
1086 * @target_list: list of droppable targets, or %NULL to use
1087 * gtk_drag_dest_get_target_list (@widget).
1089 * Looks for a match between @context->targets and the
1090 * @dest_target_list, returning the first matching target, otherwise
1091 * returning %GDK_NONE. @dest_target_list should usually be the return
1092 * value from gtk_drag_dest_get_target_list(), but some widgets may
1093 * have different valid targets for different parts of the widget; in
1094 * that case, they will have to implement a drag_motion handler that
1095 * passes the correct target list to this function.
1097 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1100 gtk_drag_dest_find_target (GtkWidget *widget,
1101 GdkDragContext *context,
1102 GtkTargetList *target_list)
1105 GList *tmp_source = NULL;
1106 GtkWidget *source_widget;
1108 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1109 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1111 source_widget = gtk_drag_get_source_widget (context);
1113 if (target_list == NULL)
1114 target_list = gtk_drag_dest_get_target_list (widget);
1116 if (target_list == NULL)
1119 tmp_target = target_list->list;
1122 GtkTargetPair *pair = tmp_target->data;
1123 tmp_source = context->targets;
1126 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1128 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1129 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1130 return pair->target;
1134 tmp_source = tmp_source->next;
1136 tmp_target = tmp_target->next;
1143 gtk_drag_selection_received (GtkWidget *widget,
1144 GtkSelectionData *selection_data,
1148 GdkDragContext *context;
1149 GtkDragDestInfo *info;
1150 GtkWidget *drop_widget;
1154 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1155 info = gtk_drag_get_dest_info (context, FALSE);
1157 if (info->proxy_data &&
1158 info->proxy_data->target == selection_data->target)
1160 gtk_selection_data_set (info->proxy_data,
1161 selection_data->type,
1162 selection_data->format,
1163 selection_data->data,
1164 selection_data->length);
1169 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1171 gtk_drag_finish (context, TRUE, FALSE, time);
1173 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1174 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1180 GtkDragDestSite *site;
1182 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1184 if (site && site->target_list)
1188 if (gtk_target_list_find (site->target_list,
1189 selection_data->target,
1192 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1193 selection_data->length >= 0)
1194 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1195 "drag_data_received",
1196 context, info->drop_x, info->drop_y,
1203 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1204 "drag_data_received",
1205 context, info->drop_x, info->drop_y,
1206 selection_data, 0, time);
1209 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1212 gtk_drag_finish (context,
1213 (selection_data->length >= 0),
1214 (context->action == GDK_ACTION_MOVE),
1218 gtk_widget_unref (drop_widget);
1221 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1222 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1225 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1226 gdk_drag_context_unref (context);
1228 gtk_drag_release_ipc_widget (widget);
1232 prepend_and_ref_widget (GtkWidget *widget,
1235 GSList **slist_p = data;
1237 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1240 /*************************************************************
1241 * gtk_drag_find_widget:
1242 * Recursive callback used to locate widgets for
1243 * DRAG_MOTION and DROP_START events.
1247 *************************************************************/
1250 gtk_drag_find_widget (GtkWidget *widget,
1251 GtkDragFindData *data)
1253 GtkAllocation new_allocation;
1254 gint allocation_to_window_x = 0;
1255 gint allocation_to_window_y = 0;
1259 if (data->found || !GTK_WIDGET_MAPPED (widget))
1262 /* Note that in the following code, we only count the
1263 * position as being inside a WINDOW widget if it is inside
1264 * widget->window; points that are outside of widget->window
1265 * but within the allocation are not counted. This is consistent
1266 * with the way we highlight drag targets.
1268 * data->x,y are relative to widget->parent->window (if
1269 * widget is not a toplevel, widget->window otherwise).
1270 * We compute the allocation of widget in the same coordinates,
1271 * clipping to widget->window, and all intermediate
1272 * windows. If data->x,y is inside that, then we translate
1273 * our coordinates to be relative to widget->window and
1276 new_allocation = widget->allocation;
1281 GdkWindow *window = widget->window;
1283 /* Compute the offset from allocation-relative to
1284 * window-relative coordinates.
1286 allocation_to_window_x = widget->allocation.x;
1287 allocation_to_window_y = widget->allocation.y;
1289 if (!GTK_WIDGET_NO_WINDOW (widget))
1291 /* The allocation is relative to the parent window for
1292 * window widgets, not to widget->window.
1294 gdk_window_get_position (window, &tx, &ty);
1296 allocation_to_window_x -= tx;
1297 allocation_to_window_y -= ty;
1300 new_allocation.x = 0 + allocation_to_window_x;
1301 new_allocation.y = 0 + allocation_to_window_y;
1303 while (window && window != widget->parent->window)
1305 GdkRectangle window_rect = { 0, 0, 0, 0 };
1307 gdk_window_get_size (window, &window_rect.width, &window_rect.height);
1309 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1311 gdk_window_get_position (window, &tx, &ty);
1312 new_allocation.x += tx;
1314 new_allocation.y += ty;
1317 window = gdk_window_get_parent (window);
1320 if (!window) /* Window and widget heirarchies didn't match. */
1324 if (data->toplevel ||
1325 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1326 (data->x < new_allocation.x + new_allocation.width) &&
1327 (data->y < new_allocation.y + new_allocation.height)))
1329 /* First, check if the drag is in a valid drop site in
1330 * one of our children
1332 if (GTK_IS_CONTAINER (widget))
1334 GtkDragFindData new_data = *data;
1335 GSList *children = NULL;
1338 new_data.x -= x_offset;
1339 new_data.y -= y_offset;
1340 new_data.found = FALSE;
1341 new_data.toplevel = FALSE;
1343 /* need to reference children temporarily in case the
1344 * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1346 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1347 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1349 if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1350 gtk_drag_find_widget (tmp_list->data, &new_data);
1351 gtk_widget_unref (tmp_list->data);
1353 g_slist_free (children);
1355 data->found = new_data.found;
1358 /* If not, and this widget is registered as a drop site, check to
1359 * emit "drag_motion" to check if we are actually in
1363 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1365 data->found = data->callback (widget,
1367 data->x - x_offset - allocation_to_window_x,
1368 data->y - y_offset - allocation_to_window_y,
1370 /* If so, send a "drag_leave" to the last widget */
1373 if (data->info->widget && data->info->widget != widget)
1375 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1377 data->info->widget = widget;
1384 gtk_drag_proxy_begin (GtkWidget *widget,
1385 GtkDragDestInfo *dest_info,
1388 GtkDragSourceInfo *source_info;
1390 GdkDragContext *context;
1391 GtkWidget *ipc_widget;
1393 if (dest_info->proxy_source)
1395 gdk_drag_abort (dest_info->proxy_source->context, time);
1396 gtk_drag_source_info_destroy (dest_info->proxy_source);
1397 dest_info->proxy_source = NULL;
1400 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1401 context = gdk_drag_begin (ipc_widget->window,
1402 dest_info->context->targets);
1404 source_info = gtk_drag_get_source_info (context, TRUE);
1406 source_info->ipc_widget = ipc_widget;
1407 source_info->widget = gtk_widget_ref (widget);
1409 source_info->target_list = gtk_target_list_new (NULL, 0);
1410 tmp_list = dest_info->context->targets;
1413 gtk_target_list_add (source_info->target_list,
1414 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1415 tmp_list = tmp_list->next;
1418 source_info->proxy_dest = dest_info;
1420 gtk_signal_connect (GTK_OBJECT (ipc_widget),
1422 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1425 dest_info->proxy_source = source_info;
1429 gtk_drag_dest_info_destroy (gpointer data)
1431 GtkDragDestInfo *info = data;
1436 static GtkDragDestInfo *
1437 gtk_drag_get_dest_info (GdkDragContext *context,
1440 GtkDragDestInfo *info;
1441 static GQuark info_quark = 0;
1443 info_quark = g_quark_from_static_string ("gtk-dest-info");
1445 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1446 if (!info && create)
1448 info = g_new (GtkDragDestInfo, 1);
1449 info->widget = NULL;
1450 info->context = context;
1451 info->proxy_source = NULL;
1452 info->proxy_data = NULL;
1453 info->dropped = FALSE;
1454 info->proxy_drop_wait = FALSE;
1455 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1456 info, gtk_drag_dest_info_destroy);
1462 static GQuark dest_info_quark = 0;
1464 static GtkDragSourceInfo *
1465 gtk_drag_get_source_info (GdkDragContext *context,
1468 GtkDragSourceInfo *info;
1469 if (!dest_info_quark)
1470 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1472 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1473 if (!info && create)
1475 info = g_new0 (GtkDragSourceInfo, 1);
1476 info->context = context;
1477 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1484 gtk_drag_clear_source_info (GdkDragContext *context)
1486 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1490 gtk_drag_dest_realized (GtkWidget *widget)
1492 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1494 if (GTK_WIDGET_TOPLEVEL (toplevel))
1495 gdk_window_register_dnd (toplevel->window);
1499 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1500 GtkWidget *previous_toplevel)
1502 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1504 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1505 gdk_window_register_dnd (toplevel->window);
1509 gtk_drag_dest_site_destroy (gpointer data)
1511 GtkDragDestSite *site = data;
1513 if (site->proxy_window)
1514 g_object_unref (site->proxy_window);
1516 if (site->target_list)
1517 gtk_target_list_unref (site->target_list);
1523 * Default drag handlers
1526 gtk_drag_dest_leave (GtkWidget *widget,
1527 GdkDragContext *context,
1530 GtkDragDestSite *site;
1532 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1533 g_return_if_fail (site != NULL);
1537 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1539 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1541 gdk_drag_abort (info->proxy_source->context, time);
1542 gtk_drag_source_info_destroy (info->proxy_source);
1543 info->proxy_source = NULL;
1550 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1551 gtk_drag_unhighlight (widget);
1553 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1554 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1557 site->have_drag = FALSE;
1562 gtk_drag_dest_motion (GtkWidget *widget,
1563 GdkDragContext *context,
1568 GtkDragDestSite *site;
1569 GdkDragAction action = 0;
1572 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1573 g_return_val_if_fail (site != NULL, FALSE);
1578 GdkEvent *current_event;
1579 GdkWindow *dest_window;
1580 GdkDragProtocol proto;
1582 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1584 if (!info->proxy_source || info->proxy_source->widget != widget)
1585 gtk_drag_proxy_begin (widget, info, time);
1587 current_event = gtk_get_current_event ();
1589 if (site->proxy_window)
1591 dest_window = site->proxy_window;
1592 proto = site->proxy_protocol;
1596 gdk_drag_find_window (info->proxy_source->context,
1598 current_event->dnd.x_root,
1599 current_event->dnd.y_root,
1600 &dest_window, &proto);
1603 gdk_drag_motion (info->proxy_source->context,
1605 current_event->dnd.x_root,
1606 current_event->dnd.y_root,
1607 context->suggested_action,
1608 context->actions, time);
1610 if (!site->proxy_window && dest_window)
1611 gdk_window_unref (dest_window);
1613 selection = gdk_drag_get_selection (info->proxy_source->context);
1615 selection != gdk_drag_get_selection (info->context))
1616 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1618 gdk_event_free (current_event);
1623 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1625 if (context->suggested_action & site->actions)
1626 action = context->suggested_action;
1633 if ((site->actions & (1 << i)) &&
1634 (context->actions & (1 << i)))
1642 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1644 if (!site->have_drag)
1646 site->have_drag = TRUE;
1647 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1648 gtk_drag_highlight (widget);
1651 gdk_drag_status (context, action, time);
1655 gdk_drag_status (context, 0, time);
1660 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1661 context, x, y, time, &retval);
1663 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1667 gtk_drag_dest_drop (GtkWidget *widget,
1668 GdkDragContext *context,
1673 GtkDragDestSite *site;
1674 GtkDragDestInfo *info;
1676 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1677 g_return_val_if_fail (site != NULL, FALSE);
1679 info = gtk_drag_get_dest_info (context, FALSE);
1680 g_return_val_if_fail (info != NULL, FALSE);
1687 if (info->proxy_source ||
1688 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1690 gtk_drag_drop (info->proxy_source, time);
1694 /* We need to synthesize a motion event, wait for a status,
1695 * and, if we get a good one, do a drop.
1698 GdkEvent *current_event;
1700 GdkWindow *dest_window;
1701 GdkDragProtocol proto;
1703 gtk_drag_proxy_begin (widget, info, time);
1704 info->proxy_drop_wait = TRUE;
1705 info->proxy_drop_time = time;
1707 current_event = gtk_get_current_event ();
1709 if (site->proxy_window)
1711 dest_window = site->proxy_window;
1712 proto = site->proxy_protocol;
1716 gdk_drag_find_window (info->proxy_source->context,
1718 current_event->dnd.x_root,
1719 current_event->dnd.y_root,
1720 &dest_window, &proto);
1723 gdk_drag_motion (info->proxy_source->context,
1725 current_event->dnd.x_root,
1726 current_event->dnd.y_root,
1727 context->suggested_action,
1728 context->actions, time);
1730 if (!site->proxy_window && dest_window)
1731 gdk_window_unref (dest_window);
1733 selection = gdk_drag_get_selection (info->proxy_source->context);
1735 selection != gdk_drag_get_selection (info->context))
1736 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1738 gdk_event_free (current_event);
1747 if (site->flags & GTK_DEST_DEFAULT_DROP)
1749 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1751 if (target == GDK_NONE)
1753 gtk_drag_finish (context, FALSE, FALSE, time);
1757 gtk_drag_get_data (widget, context, target, time);
1760 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1761 context, x, y, time, &retval);
1763 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1771 /*************************************************************
1772 * gtk_drag_begin: Start a drag operation
1775 * widget: Widget from which drag starts
1776 * handlers: List of handlers to supply the data for the drag
1777 * button: Button user used to start drag
1778 * time: Time of event starting drag
1781 *************************************************************/
1784 gtk_drag_begin (GtkWidget *widget,
1785 GtkTargetList *target_list,
1786 GdkDragAction actions,
1790 GtkDragSourceInfo *info;
1791 GList *targets = NULL;
1793 guint32 time = GDK_CURRENT_TIME;
1794 GdkDragAction possible_actions, suggested_action;
1795 GdkDragContext *context;
1796 GtkWidget *ipc_widget;
1798 g_return_val_if_fail (widget != NULL, NULL);
1799 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1800 g_return_val_if_fail (target_list != NULL, NULL);
1803 time = gdk_event_get_time (event);
1805 tmp_list = g_list_last (target_list->list);
1808 GtkTargetPair *pair = tmp_list->data;
1809 targets = g_list_prepend (targets,
1810 GINT_TO_POINTER (pair->target));
1811 tmp_list = tmp_list->prev;
1814 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1815 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1817 context = gdk_drag_begin (ipc_widget->window, targets);
1818 g_list_free (targets);
1820 info = gtk_drag_get_source_info (context, TRUE);
1822 info->ipc_widget = ipc_widget;
1823 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1825 info->widget = gtk_widget_ref (widget);
1828 info->button = button;
1829 info->target_list = target_list;
1830 gtk_target_list_ref (target_list);
1832 info->possible_actions = actions;
1834 info->cursor = NULL;
1835 info->status = GTK_DRAG_STATUS_DRAG;
1836 info->last_event = NULL;
1837 info->selections = NULL;
1838 info->icon_window = NULL;
1839 info->destroy_icon = FALSE;
1841 gtk_drag_get_event_actions (event, info->button, actions,
1842 &suggested_action, &possible_actions);
1844 info->cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), suggested_action);
1846 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1847 * the drag icon, it will be in the right place
1849 if (event && event->type == GDK_MOTION_NOTIFY)
1851 info->cur_x = event->motion.x_root;
1852 info->cur_y = event->motion.y_root;
1856 gdk_window_get_pointer (gtk_widget_get_root_window (widget),
1857 &info->cur_x, &info->cur_y, NULL);
1860 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1863 if (event && event->type == GDK_MOTION_NOTIFY)
1864 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1866 info->start_x = info->cur_x;
1867 info->start_y = info->cur_y;
1869 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1870 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1871 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1872 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1873 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
1874 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1875 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
1876 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1877 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1878 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1880 /* We use a GTK grab here to override any grabs that the widget
1881 * we are dragging from might have held
1883 gtk_grab_add (info->ipc_widget);
1884 if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1885 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1886 GDK_BUTTON_RELEASE_MASK, NULL,
1887 info->cursor, time) == 0)
1889 if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1891 gtk_drag_cancel (info, time);
1896 info->have_grab = TRUE;
1897 info->grab_time = time;
1899 return info->context;
1902 /*************************************************************
1903 * gtk_drag_source_set:
1904 * Register a drop site, and possibly add default behaviors.
1907 * start_button_mask: Mask of allowed buttons to start drag
1908 * targets: Table of targets for this source
1910 * actions: Actions allowed for this source
1912 *************************************************************/
1915 gtk_drag_source_set (GtkWidget *widget,
1916 GdkModifierType start_button_mask,
1917 const GtkTargetEntry *targets,
1919 GdkDragAction actions)
1921 GtkDragSourceSite *site;
1923 g_return_if_fail (widget != NULL);
1925 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1927 gtk_widget_add_events (widget,
1928 gtk_widget_get_events (widget) |
1929 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1930 GDK_BUTTON_MOTION_MASK);
1934 if (site->target_list)
1935 gtk_target_list_unref (site->target_list);
1939 site = g_new0 (GtkDragSourceSite, 1);
1941 site->icon_type = GTK_IMAGE_EMPTY;
1943 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1944 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1946 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1947 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1950 gtk_object_set_data_full (GTK_OBJECT (widget),
1952 site, gtk_drag_source_site_destroy);
1955 site->start_button_mask = start_button_mask;
1958 site->target_list = gtk_target_list_new (targets, n_targets);
1960 site->target_list = NULL;
1962 site->actions = actions;
1966 /*************************************************************
1967 * gtk_drag_source_unset
1968 * Unregister this widget as a drag source.
1972 *************************************************************/
1975 gtk_drag_source_unset (GtkWidget *widget)
1977 GtkDragSourceSite *site;
1979 g_return_if_fail (widget != NULL);
1981 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1985 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1986 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1991 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
1993 switch (site->icon_type)
1995 case GTK_IMAGE_EMPTY:
1997 case GTK_IMAGE_PIXMAP:
1998 if (site->icon_data.pixmap.pixmap)
1999 gdk_pixmap_unref (site->icon_data.pixmap.pixmap);
2000 if (site->icon_mask)
2001 gdk_pixmap_unref (site->icon_mask);
2003 case GTK_IMAGE_PIXBUF:
2004 g_object_unref (G_OBJECT (site->icon_data.pixbuf.pixbuf));
2006 case GTK_IMAGE_STOCK:
2007 g_free (G_OBJECT (site->icon_data.stock.stock_id));
2010 g_assert_not_reached();
2013 site->icon_type = GTK_IMAGE_EMPTY;
2016 gdk_colormap_unref (site->colormap);
2017 site->colormap = NULL;
2021 * gtk_drag_source_set_icon:
2022 * @widget: a #GtkWidget
2023 * @colormap: the colormap of the icon
2024 * @pixmap: the image data for the icon
2025 * @mask: the transparency mask for an image.
2027 * Sets the icon that will be used for drags from a particular widget
2028 * from a pixmap/mask. GTK+ retains references for the arguments, and
2029 * will release them when they are no longer needed.
2030 * Use gtk_drag_source_set_icon_pixbuf() instead.
2033 gtk_drag_source_set_icon (GtkWidget *widget,
2034 GdkColormap *colormap,
2038 GtkDragSourceSite *site;
2040 g_return_if_fail (widget != NULL);
2041 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2042 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2043 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2045 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2046 g_return_if_fail (site != NULL);
2048 gdk_colormap_ref (colormap);
2049 gdk_pixmap_ref (pixmap);
2051 gdk_pixmap_ref (mask);
2053 gtk_drag_source_unset_icon (site);
2055 site->icon_type = GTK_IMAGE_PIXMAP;
2057 site->icon_data.pixmap.pixmap = pixmap;
2058 site->icon_mask = mask;
2059 site->colormap = colormap;
2063 * gtk_drag_source_set_icon_pixbuf:
2064 * @widget: a #GtkWidget
2065 * @pixbuf: the #GdkPixbuf for the drag icon
2067 * Sets the icon that will be used for drags from a particular widget
2068 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2069 * release it when it is no longer needed.
2072 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2075 GtkDragSourceSite *site;
2077 g_return_if_fail (widget != NULL);
2078 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2080 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2081 g_return_if_fail (site != NULL);
2082 gdk_pixbuf_ref (pixbuf);
2084 gtk_drag_source_unset_icon (site);
2086 site->icon_type = GTK_IMAGE_PIXBUF;
2087 site->icon_data.pixbuf.pixbuf = pixbuf;
2091 * gtk_drag_source_set_icon_stock:
2092 * @widget: a #GtkWidget
2093 * @stock_id: the ID of the stock icon to use
2095 * Sets the icon that will be used for drags from a particular source
2099 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2100 const gchar *stock_id)
2102 GtkDragSourceSite *site;
2104 g_return_if_fail (widget != NULL);
2105 g_return_if_fail (stock_id != NULL);
2107 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2108 g_return_if_fail (site != NULL);
2110 gtk_drag_source_unset_icon (site);
2112 site->icon_data.stock.stock_id = g_strdup (stock_id);
2116 gtk_drag_set_icon_window (GdkDragContext *context,
2120 gboolean destroy_on_release)
2122 GtkDragSourceInfo *info;
2124 g_return_if_fail (context != NULL);
2125 g_return_if_fail (widget != NULL);
2127 info = gtk_drag_get_source_info (context, FALSE);
2128 gtk_drag_remove_icon (info);
2130 info->icon_window = widget;
2131 info->hot_x = hot_x;
2132 info->hot_y = hot_y;
2136 gtk_widget_set_uposition (widget,
2137 info->cur_x - info->hot_x,
2138 info->cur_y - info->hot_y);
2139 gtk_widget_ref (widget);
2140 gdk_window_raise (widget->window);
2141 gtk_widget_show (widget);
2144 info->destroy_icon = destroy_on_release;
2148 * gtk_drag_set_icon_widget:
2149 * @context: the context for a drag. (This must be called
2150 with a context for the source side of a drag)
2151 * @widget: a toplevel window to use as an icon.
2152 * @hot_x: the X offset within @widget of the hotspot.
2153 * @hot_y: the Y offset within @widget of the hotspot.
2155 * Changes the icon for a widget to a given widget. GTK+
2156 * will not destroy the icon, so if you don't want
2157 * it to persist, you should connect to the "drag_end"
2158 * signal and destroy it yourself.
2161 gtk_drag_set_icon_widget (GdkDragContext *context,
2166 g_return_if_fail (context != NULL);
2167 g_return_if_fail (widget != NULL);
2169 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2173 set_icon_stock_pixbuf (GdkDragContext *context,
2174 const gchar *stock_id,
2184 GdkColormap *colormap;
2186 g_return_if_fail (context != NULL);
2187 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2188 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2190 screen = gdk_drawable_get_screen (context->source_window);
2191 colormap = gdk_screen_get_rgb_colormap (screen);
2193 gtk_widget_push_colormap (colormap);
2194 window = gtk_window_new (GTK_WINDOW_POPUP);
2195 gtk_window_set_screen (GTK_WINDOW (window), screen);
2196 gtk_widget_pop_colormap ();
2198 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2199 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2203 pixbuf = gtk_widget_render_icon (window, stock_id,
2204 GTK_ICON_SIZE_DND, NULL);
2208 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2209 gtk_widget_destroy (window);
2215 width = gdk_pixbuf_get_width (pixbuf);
2216 height = gdk_pixbuf_get_width (pixbuf);
2218 gtk_widget_set_usize (window,
2219 gdk_pixbuf_get_width (pixbuf),
2220 gdk_pixbuf_get_height (pixbuf));
2221 gtk_widget_realize (window);
2223 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf, colormap, &pixmap, &mask, 128);
2225 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2228 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2230 g_object_unref (G_OBJECT (pixmap));
2233 g_object_unref (G_OBJECT (mask));
2235 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2239 * gtk_drag_set_icon_pixbuf:
2240 * @context: the context for a drag. (This must be called
2241 * with a context for the source side of a drag)
2242 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2243 * @hot_x: the X offset within @widget of the hotspot.
2244 * @hot_y: the Y offset within @widget of the hotspot.
2246 * Sets @pixbuf as the icon for a given drag.
2249 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2254 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2255 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2257 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2261 * gtk_drag_set_icon_stock:
2262 * @context: the context for a drag. (This must be called
2263 * with a context for the source side of a drag)
2264 * @stock_id: the ID of the stock icon to use for the drag.
2265 * @hot_x: the X offset within the icon of the hotspot.
2266 * @hot_y: the Y offset within the icon of the hotspot.
2268 * Sets the the icon for a given drag from a stock ID.
2271 gtk_drag_set_icon_stock (GdkDragContext *context,
2272 const gchar *stock_id,
2276 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2277 g_return_if_fail (stock_id != NULL);
2279 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2283 * gtk_drag_set_icon_pixmap:
2284 * @context: the context for a drag. (This must be called
2285 * with a context for the source side of a drag)
2286 * @colormap: the colormap of the icon
2287 * @pixmap: the image data for the icon
2288 * @mask: the transparency mask for the icon
2289 * @hot_x: the X offset within @pixmap of the hotspot.
2290 * @hot_y: the Y offset within @pixmap of the hotspot.
2292 * Sets @pixmap as the icon for a given drag. GTK+ retains
2293 * references for the arguments, and will release them when
2294 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2295 * will be more convenient to use.
2298 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2299 GdkColormap *colormap,
2308 g_return_if_fail (context != NULL);
2309 g_return_if_fail (colormap != NULL);
2310 g_return_if_fail (pixmap != NULL);
2312 gdk_window_get_size (pixmap, &width, &height);
2314 gtk_widget_push_colormap (colormap);
2316 window = gtk_window_new (GTK_WINDOW_POPUP);
2317 gtk_window_set_screen (GTK_WINDOW (window), gdk_drawable_get_screen (context->source_window));
2318 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2319 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2321 gtk_widget_pop_colormap ();
2323 gtk_widget_set_usize (window, width, height);
2324 gtk_widget_realize (window);
2326 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2329 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2331 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2335 * gtk_drag_set_icon_default:
2336 * @context: the context for a drag. (This must be called
2337 with a context for the source side of a drag)
2339 * Sets the icon for a particular drag to the default
2343 gtk_drag_set_icon_default (GdkDragContext *context)
2345 g_return_if_fail (context != NULL);
2347 if (!default_icon_pixmap)
2348 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2350 gtk_drag_set_icon_pixmap (context,
2351 default_icon_colormap,
2352 default_icon_pixmap,
2355 default_icon_hot_y);
2359 * gtk_drag_set_default_icon:
2360 * @colormap: the colormap of the icon
2361 * @pixmap: the image data for the icon
2362 * @mask: the transparency mask for an image.
2363 * @hot_x: The X offset within @widget of the hotspot.
2364 * @hot_y: The Y offset within @widget of the hotspot.
2366 * Changes the default drag icon. GTK+ retains references for the
2367 * arguments, and will release them when they are no longer needed.
2368 * This function is obsolete. The default icon should now be changed
2369 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2372 gtk_drag_set_default_icon (GdkColormap *colormap,
2378 g_return_if_fail (colormap != NULL);
2379 g_return_if_fail (pixmap != NULL);
2381 if (default_icon_colormap)
2382 gdk_colormap_unref (default_icon_colormap);
2383 if (default_icon_pixmap)
2384 gdk_pixmap_unref (default_icon_pixmap);
2385 if (default_icon_mask)
2386 gdk_pixmap_unref (default_icon_mask);
2388 default_icon_colormap = colormap;
2389 gdk_colormap_ref (colormap);
2391 default_icon_pixmap = pixmap;
2392 gdk_pixmap_ref (pixmap);
2394 default_icon_mask = mask;
2396 gdk_pixmap_ref (mask);
2398 default_icon_hot_x = hot_x;
2399 default_icon_hot_y = hot_y;
2403 /*************************************************************
2404 * _gtk_drag_source_handle_event:
2405 * Called from widget event handling code on Drag events
2409 * toplevel: Toplevel widget that received the event
2412 *************************************************************/
2415 _gtk_drag_source_handle_event (GtkWidget *widget,
2418 GtkDragSourceInfo *info;
2419 GdkDragContext *context;
2421 g_return_if_fail (widget != NULL);
2422 g_return_if_fail (event != NULL);
2424 context = event->dnd.context;
2425 info = gtk_drag_get_source_info (context, FALSE);
2429 switch (event->type)
2431 case GDK_DRAG_STATUS:
2435 if (info->proxy_dest)
2437 if (!event->dnd.send_event)
2439 if (info->proxy_dest->proxy_drop_wait)
2441 gboolean result = context->action != 0;
2443 /* Aha - we can finally pass the MOTIF DROP on... */
2444 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2446 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2448 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2452 gdk_drag_status (info->proxy_dest->context,
2453 event->dnd.context->action,
2458 else if (info->have_grab)
2460 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2461 event->dnd.context->action);
2462 if (info->cursor != cursor)
2464 gdk_pointer_grab (widget->window, FALSE,
2465 GDK_POINTER_MOTION_MASK |
2466 GDK_POINTER_MOTION_HINT_MASK |
2467 GDK_BUTTON_RELEASE_MASK,
2469 cursor, info->grab_time);
2470 info->cursor = cursor;
2473 if (info->last_event)
2475 gtk_drag_update (info,
2476 info->cur_x, info->cur_y,
2478 info->last_event = NULL;
2484 case GDK_DROP_FINISHED:
2485 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2488 g_assert_not_reached ();
2492 /*************************************************************
2493 * gtk_drag_source_check_selection:
2494 * Check if we've set up handlers/claimed the selection
2495 * for a given drag. If not, add them.
2499 *************************************************************/
2502 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2508 tmp_list = info->selections;
2511 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2513 tmp_list = tmp_list->next;
2516 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2520 info->selections = g_list_prepend (info->selections,
2521 GUINT_TO_POINTER (selection));
2523 tmp_list = info->target_list->list;
2526 GtkTargetPair *pair = tmp_list->data;
2528 gtk_selection_add_target (info->ipc_widget,
2532 tmp_list = tmp_list->next;
2535 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2537 gtk_selection_add_target (info->ipc_widget,
2539 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2540 TARGET_MOTIF_SUCCESS);
2541 gtk_selection_add_target (info->ipc_widget,
2543 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2544 TARGET_MOTIF_FAILURE);
2547 gtk_selection_add_target (info->ipc_widget,
2549 gdk_atom_intern ("DELETE", FALSE),
2553 /*************************************************************
2554 * gtk_drag_drop_finished:
2555 * Clean up from the drag, and display snapback, if necessary.
2561 *************************************************************/
2564 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2568 gtk_drag_source_release_selections (info, time);
2570 if (info->proxy_dest)
2572 /* The time from the event isn't reliable for Xdnd drags */
2573 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2574 info->proxy_dest->proxy_drop_time);
2575 gtk_drag_source_info_destroy (info);
2581 gtk_drag_source_info_destroy (info);
2585 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2589 anim->n_steps = MAX (info->cur_x - info->start_x,
2590 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2591 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2592 if (info->icon_window)
2594 gtk_widget_show (info->icon_window);
2595 gdk_window_raise (info->icon_window->window);
2598 /* Mark the context as dead, so if the destination decides
2599 * to respond really late, we still are OK.
2601 gtk_drag_clear_source_info (info->context);
2602 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2608 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2611 GdkDisplay *display = gtk_widget_get_display (info->widget);
2612 GList *tmp_list = info->selections;
2616 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2617 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2618 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2620 tmp_list = tmp_list->next;
2623 g_list_free (info->selections);
2624 info->selections = NULL;
2627 /*************************************************************
2629 * Send a drop event.
2633 *************************************************************/
2636 gtk_drag_drop (GtkDragSourceInfo *info,
2639 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2641 GtkSelectionData selection_data;
2643 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2645 tmp_list = info->target_list->list;
2648 GtkTargetPair *pair = tmp_list->data;
2650 if (pair->target == target)
2652 selection_data.selection = GDK_NONE;
2653 selection_data.target = target;
2654 selection_data.data = NULL;
2655 selection_data.length = -1;
2657 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2658 info->context, &selection_data,
2662 /* FIXME: Should we check for length >= 0 here? */
2663 gtk_drag_drop_finished (info, TRUE, time);
2666 tmp_list = tmp_list->next;
2668 gtk_drag_drop_finished (info, FALSE, time);
2672 if (info->icon_window)
2673 gtk_widget_hide (info->icon_window);
2675 gdk_drag_drop (info->context, time);
2676 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2677 gtk_drag_abort_timeout,
2683 * Source side callbacks.
2687 gtk_drag_source_event_cb (GtkWidget *widget,
2691 GtkDragSourceSite *site;
2692 gboolean retval = FALSE;
2693 site = (GtkDragSourceSite *)data;
2695 switch (event->type)
2697 case GDK_BUTTON_PRESS:
2698 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2700 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2701 site->x = event->button.x;
2702 site->y = event->button.y;
2706 case GDK_BUTTON_RELEASE:
2707 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2708 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2711 case GDK_MOTION_NOTIFY:
2712 if (site->state & event->motion.state & site->start_button_mask)
2714 /* FIXME: This is really broken and can leave us
2720 if (site->state & event->motion.state &
2721 GDK_BUTTON1_MASK << (i - 1))
2725 if (gtk_drag_check_threshold (widget, site->x, site->y,
2726 event->motion.x, event->motion.y))
2728 GtkDragSourceInfo *info;
2729 GdkDragContext *context;
2732 context = gtk_drag_begin (widget, site->target_list,
2736 info = gtk_drag_get_source_info (context, FALSE);
2738 if (!info->icon_window)
2740 switch (site->icon_type)
2742 case GTK_IMAGE_EMPTY:
2743 gtk_drag_set_icon_default (context);
2745 case GTK_IMAGE_PIXMAP:
2746 gtk_drag_set_icon_pixmap (context,
2748 site->icon_data.pixmap.pixmap,
2752 case GTK_IMAGE_PIXBUF:
2753 gtk_drag_set_icon_pixbuf (context,
2754 site->icon_data.pixbuf.pixbuf,
2757 case GTK_IMAGE_STOCK:
2758 gtk_drag_set_icon_stock (context,
2759 site->icon_data.stock.stock_id,
2763 g_assert_not_reached();
2773 default: /* hit for 2/3BUTTON_PRESS */
2781 gtk_drag_source_site_destroy (gpointer data)
2783 GtkDragSourceSite *site = data;
2785 if (site->target_list)
2786 gtk_target_list_unref (site->target_list);
2788 gtk_drag_source_unset_icon (site);
2793 gtk_drag_selection_get (GtkWidget *widget,
2794 GtkSelectionData *selection_data,
2799 GtkDragSourceInfo *info = data;
2800 static GdkAtom null_atom = GDK_NONE;
2804 null_atom = gdk_atom_intern ("NULL", FALSE);
2809 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2812 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2814 case TARGET_MOTIF_SUCCESS:
2815 gtk_drag_drop_finished (info, TRUE, time);
2816 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2818 case TARGET_MOTIF_FAILURE:
2819 gtk_drag_drop_finished (info, FALSE, time);
2820 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2823 if (info->proxy_dest)
2825 /* This is sort of dangerous and needs to be thought
2828 info->proxy_dest->proxy_data = selection_data;
2829 gtk_drag_get_data (info->widget,
2830 info->proxy_dest->context,
2831 selection_data->target,
2834 info->proxy_dest->proxy_data = NULL;
2838 if (gtk_target_list_find (info->target_list,
2839 selection_data->target,
2842 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2854 gtk_drag_anim_timeout (gpointer data)
2856 GtkDragAnim *anim = data;
2860 GDK_THREADS_ENTER ();
2862 if (anim->step == anim->n_steps)
2864 gtk_drag_source_info_destroy (anim->info);
2871 x = (anim->info->start_x * (anim->step + 1) +
2872 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2873 y = (anim->info->start_y * (anim->step + 1) +
2874 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2875 if (anim->info->icon_window)
2876 gtk_widget_set_uposition (anim->info->icon_window,
2877 x - anim->info->hot_x,
2878 y - anim->info->hot_y);
2885 GDK_THREADS_LEAVE ();
2891 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2893 if (info->icon_window)
2895 gtk_widget_hide (info->icon_window);
2896 if (info->destroy_icon)
2897 gtk_widget_destroy (info->icon_window);
2899 gtk_widget_unref (info->icon_window);
2900 info->icon_window = NULL;
2905 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
2907 gtk_drag_remove_icon (info);
2909 if (!info->proxy_dest)
2910 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2914 gtk_widget_unref (info->widget);
2916 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2917 gtk_selection_remove_all (info->ipc_widget);
2918 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2919 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2920 gtk_drag_release_ipc_widget (info->ipc_widget);
2922 gtk_target_list_unref (info->target_list);
2924 gtk_drag_clear_source_info (info->context);
2925 gdk_drag_context_unref (info->context);
2927 if (info->drop_timeout)
2928 gtk_timeout_remove (info->drop_timeout);
2933 /*************************************************************
2935 * Function to update the status of the drag when the
2936 * cursor moves or the modifier changes
2938 * info: DragSourceInfo for the drag
2939 * x_root, y_root: position of darg
2940 * event: The event that triggered this call
2942 *************************************************************/
2945 gtk_drag_update (GtkDragSourceInfo *info,
2950 GdkDragAction action;
2951 GdkDragAction possible_actions;
2952 GdkWindow *window = NULL;
2953 GdkWindow *dest_window;
2954 GdkDragProtocol protocol;
2956 guint32 time = gtk_drag_get_event_time (event);
2958 gtk_drag_get_event_actions (event,
2960 info->possible_actions,
2961 &action, &possible_actions);
2962 info->cur_x = x_root;
2963 info->cur_y = y_root;
2965 if (info->icon_window)
2967 gdk_window_raise (info->icon_window->window);
2968 gtk_widget_set_uposition (info->icon_window,
2969 info->cur_x - info->hot_x,
2970 info->cur_y - info->hot_y);
2971 window = info->icon_window->window;
2974 gdk_drag_find_window (info->context,
2975 window, x_root, y_root,
2976 &dest_window, &protocol);
2978 if (gdk_drag_motion (info->context, dest_window, protocol,
2979 x_root, y_root, action,
2983 if (info->last_event != event) /* Paranoia, should not happen */
2985 if (info->last_event)
2986 gdk_event_free ((GdkEvent *)info->last_event);
2987 info->last_event = gdk_event_copy ((GdkEvent *)event);
2992 if (info->last_event)
2994 gdk_event_free ((GdkEvent *)info->last_event);
2995 info->last_event = NULL;
3000 gdk_window_unref (dest_window);
3002 selection = gdk_drag_get_selection (info->context);
3004 gtk_drag_source_check_selection (info, selection, time);
3007 /*************************************************************
3009 * Called when the user finishes to drag, either by
3010 * releasing the mouse, or by pressing Esc.
3012 * info: Source info for the drag
3013 * time: Timestamp for ending the drag
3015 *************************************************************/
3018 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3020 GdkEvent *send_event;
3021 GtkWidget *source_widget = info->widget;
3022 GdkDisplay *display = gtk_widget_get_display (source_widget);
3024 info->have_grab = FALSE;
3026 gdk_display_pointer_ungrab (display, time);
3027 gdk_display_keyboard_ungrab (display, time);
3028 gtk_grab_remove (info->ipc_widget);
3030 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3031 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
3033 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3034 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
3036 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3037 GTK_SIGNAL_FUNC (gtk_drag_key_cb),
3040 /* Send on a release pair to the the original
3041 * widget to convince it to release its grab. We need to
3042 * call gtk_propagate_event() here, instead of
3043 * gtk_widget_event() because widget like GtkList may
3044 * expect propagation.
3047 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3048 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3049 send_event->button.send_event = TRUE;
3050 send_event->button.time = time;
3051 send_event->button.x = 0;
3052 send_event->button.y = 0;
3053 send_event->button.axes = NULL;
3054 send_event->button.state = 0;
3055 send_event->button.button = info->button;
3056 send_event->button.device = gdk_display_get_core_pointer (display);
3057 send_event->button.x_root = 0;
3058 send_event->button.y_root = 0;
3060 gtk_propagate_event (source_widget, send_event);
3061 gdk_event_free (send_event);
3064 /*************************************************************
3066 * Called on cancellation of a drag, either by the user
3067 * or programmatically.
3069 * info: Source info for the drag
3070 * time: Timestamp for ending the drag
3072 *************************************************************/
3075 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3077 gtk_drag_end (info, time);
3078 gdk_drag_abort (info->context, time);
3079 gtk_drag_drop_finished (info, FALSE, time);
3082 /*************************************************************
3083 * gtk_drag_motion_cb:
3084 * "motion_notify_event" callback during drag.
3088 *************************************************************/
3091 gtk_drag_motion_cb (GtkWidget *widget,
3092 GdkEventMotion *event,
3095 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3096 gint x_root, y_root;
3100 GdkWindow *root_window = gtk_widget_get_root_window (widget);
3102 gdk_window_get_pointer (root_window, &x_root, &y_root, NULL);
3103 event->x_root = x_root;
3104 event->y_root = y_root;
3107 gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
3112 /*************************************************************
3114 * "key_press/release_event" callback during drag.
3118 *************************************************************/
3121 gtk_drag_key_cb (GtkWidget *widget,
3125 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3126 GdkModifierType state;
3127 GdkWindow *root_window;
3129 if (event->type == GDK_KEY_PRESS)
3131 if (event->keyval == GDK_Escape)
3133 gtk_drag_cancel (info, event->time);
3139 /* Now send a "motion" so that the modifier state is updated */
3141 /* The state is not yet updated in the event, so we need
3142 * to query it here. We could use XGetModifierMapping, but
3143 * that would be overkill.
3145 root_window = gtk_widget_get_root_window (widget);
3146 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3148 event->state = state;
3149 gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
3154 /*************************************************************
3155 * gtk_drag_button_release_cb:
3156 * "button_release_event" callback during drag.
3160 *************************************************************/
3163 gtk_drag_button_release_cb (GtkWidget *widget,
3164 GdkEventButton *event,
3167 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3169 if (event->button != info->button)
3172 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3174 gtk_drag_end (info, event->time);
3175 gtk_drag_drop (info, event->time);
3179 gtk_drag_cancel (info, event->time);
3186 gtk_drag_abort_timeout (gpointer data)
3188 GtkDragSourceInfo *info = data;
3189 guint32 time = GDK_CURRENT_TIME;
3191 GDK_THREADS_ENTER ();
3193 if (info->proxy_dest)
3194 time = info->proxy_dest->proxy_drop_time;
3196 info->drop_timeout = 0;
3197 gtk_drag_drop_finished (info, FALSE, time);
3199 GDK_THREADS_LEAVE ();
3205 * gtk_drag_check_threshold:
3206 * @widget: a #GtkWidget
3207 * @start_x: X coordinate of start of drag
3208 * @start_y: Y coordinate of start of drag
3209 * @current_x: current X coordinate
3210 * @current_y: current Y coordinate
3212 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3213 * at (@current_x, @current_y) has passed the GTK+ drag threshhold, and thus
3214 * should trigger the beginning of a drag-and-drop operation.
3216 * Return Value: %TRUE if the drag threshold has been passed.
3219 gtk_drag_check_threshold (GtkWidget *widget,
3225 gint drag_threshold;
3227 g_object_get (gtk_widget_get_settings (widget),
3228 "gtk-dnd-drag-threshold", &drag_threshold,
3231 return (ABS (current_x - start_x) > drag_threshold ||
3232 ABS (current_y - start_y) > drag_threshold);