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,
238 static gint gtk_drag_source_event_cb (GtkWidget *widget,
241 static void gtk_drag_source_site_destroy (gpointer data);
242 static void gtk_drag_selection_get (GtkWidget *widget,
243 GtkSelectionData *selection_data,
247 static gint gtk_drag_anim_timeout (gpointer data);
248 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
249 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
250 static void gtk_drag_update (GtkDragSourceInfo *info,
254 static gint gtk_drag_motion_cb (GtkWidget *widget,
255 GdkEventMotion *event,
257 static gint gtk_drag_key_cb (GtkWidget *widget,
260 static gint gtk_drag_button_release_cb (GtkWidget *widget,
261 GdkEventButton *event,
263 static gint gtk_drag_abort_timeout (gpointer data);
265 /************************
266 * Cursor and Icon data *
267 ************************/
269 #define action_ask_width 16
270 #define action_ask_height 16
271 static const guchar action_ask_bits[] = {
272 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
273 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
274 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
276 #define action_ask_mask_width 16
277 #define action_ask_mask_height 16
278 static const guchar action_ask_mask_bits[] = {
279 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
280 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
281 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
283 #define action_copy_width 16
284 #define action_copy_height 16
285 static const guchar action_copy_bits[] = {
286 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
287 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
288 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
290 #define action_copy_mask_width 16
291 #define action_copy_mask_height 16
292 static const guchar action_copy_mask_bits[] = {
293 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
294 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
295 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
297 #define action_move_width 16
298 #define action_move_height 16
299 static const guchar action_move_bits[] = {
300 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
301 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
302 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
304 #define action_move_mask_width 16
305 #define action_move_mask_height 16
306 static const guchar action_move_mask_bits[] = {
307 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
308 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
309 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
311 #define action_link_width 16
312 #define action_link_height 16
313 static const guchar action_link_bits[] = {
314 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
315 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
316 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
318 #define action_link_mask_width 16
319 #define action_link_mask_height 16
320 static const guchar action_link_mask_bits[] = {
321 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
322 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
323 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
325 #define action_none_width 16
326 #define action_none_height 16
327 static const guchar action_none_bits[] = {
328 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
329 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
330 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
332 #define action_none_mask_width 16
333 #define action_none_mask_height 16
334 static const guchar action_none_mask_bits[] = {
335 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
336 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
337 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
339 #define CURSOR_WIDTH 16
340 #define CURSOR_HEIGHT 16
343 GdkDragAction action;
348 { GDK_ACTION_DEFAULT, 0 },
349 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
350 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
351 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
352 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
353 { 0 , action_none_bits, action_none_mask_bits, NULL },
356 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
358 /*********************
359 * Utility functions *
360 *********************/
362 /*************************************************************
363 * gtk_drag_get_ipc_widget:
364 * Return a invisible, off-screen, override-redirect
369 *************************************************************/
372 gtk_drag_get_ipc_widget (GdkScreen *screen)
375 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
376 "gtk-dnd-ipc-widgets");
380 GSList *tmp = drag_widgets;
381 result = drag_widgets->data;
382 drag_widgets = drag_widgets->next;
383 g_object_set_data (G_OBJECT (screen),
384 "gtk-dnd-ipc-widgets",
386 g_slist_free_1 (tmp);
390 result = gtk_invisible_new_for_screen (screen);
391 gtk_widget_show (result);
397 /***************************************************************
398 * gtk_drag_release_ipc_widget:
399 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
401 * widget: the widget to release.
403 ***************************************************************/
406 gtk_drag_release_ipc_widget (GtkWidget *widget)
408 GdkScreen *screen = gtk_widget_get_screen (widget);
409 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
410 "gtk-dnd-ipc-widgets");
411 drag_widgets = g_slist_prepend (drag_widgets, widget);
412 g_object_set_data (G_OBJECT (screen),
413 "gtk-dnd-ipc-widgets",
418 gtk_drag_get_event_time (GdkEvent *event)
420 guint32 tm = GDK_CURRENT_TIME;
425 case GDK_MOTION_NOTIFY:
426 tm = event->motion.time; break;
427 case GDK_BUTTON_PRESS:
428 case GDK_2BUTTON_PRESS:
429 case GDK_3BUTTON_PRESS:
430 case GDK_BUTTON_RELEASE:
431 tm = event->button.time; break;
433 case GDK_KEY_RELEASE:
434 tm = event->key.time; break;
435 case GDK_ENTER_NOTIFY:
436 case GDK_LEAVE_NOTIFY:
437 tm = event->crossing.time; break;
438 case GDK_PROPERTY_NOTIFY:
439 tm = event->property.time; break;
440 case GDK_SELECTION_CLEAR:
441 case GDK_SELECTION_REQUEST:
442 case GDK_SELECTION_NOTIFY:
443 tm = event->selection.time; break;
444 case GDK_PROXIMITY_IN:
445 case GDK_PROXIMITY_OUT:
446 tm = event->proximity.time; break;
447 default: /* use current time */
455 gtk_drag_get_event_actions (GdkEvent *event,
457 GdkDragAction actions,
458 GdkDragAction *suggested_action,
459 GdkDragAction *possible_actions)
461 *suggested_action = 0;
462 *possible_actions = 0;
466 GdkModifierType state = 0;
470 case GDK_MOTION_NOTIFY:
471 state = event->motion.state;
473 case GDK_BUTTON_PRESS:
474 case GDK_2BUTTON_PRESS:
475 case GDK_3BUTTON_PRESS:
476 case GDK_BUTTON_RELEASE:
477 state = event->button.state;
480 case GDK_KEY_RELEASE:
481 state = event->key.state;
483 case GDK_ENTER_NOTIFY:
484 case GDK_LEAVE_NOTIFY:
485 state = event->crossing.state;
491 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
493 *suggested_action = GDK_ACTION_ASK;
494 *possible_actions = actions;
496 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
498 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
500 if (actions & GDK_ACTION_LINK)
502 *suggested_action = GDK_ACTION_LINK;
503 *possible_actions = GDK_ACTION_LINK;
506 else if (state & GDK_CONTROL_MASK)
508 if (actions & GDK_ACTION_COPY)
510 *suggested_action = GDK_ACTION_COPY;
511 *possible_actions = GDK_ACTION_COPY;
517 if (actions & GDK_ACTION_MOVE)
519 *suggested_action = GDK_ACTION_MOVE;
520 *possible_actions = GDK_ACTION_MOVE;
527 *possible_actions = actions;
529 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
530 *suggested_action = GDK_ACTION_ASK;
531 else if (actions & GDK_ACTION_COPY)
532 *suggested_action = GDK_ACTION_COPY;
533 else if (actions & GDK_ACTION_MOVE)
534 *suggested_action = GDK_ACTION_MOVE;
535 else if (actions & GDK_ACTION_LINK)
536 *suggested_action = GDK_ACTION_LINK;
541 *possible_actions = actions;
543 if (actions & GDK_ACTION_COPY)
544 *suggested_action = GDK_ACTION_COPY;
545 else if (actions & GDK_ACTION_MOVE)
546 *suggested_action = GDK_ACTION_MOVE;
547 else if (actions & GDK_ACTION_LINK)
548 *suggested_action = GDK_ACTION_LINK;
555 gtk_drag_get_cursor (GdkDisplay *display,
556 GdkDragAction action)
560 for (i = 0 ; i < n_drag_cursors - 1; i++)
561 if (drag_cursors[i].action == action)
563 if (drag_cursors[i].cursor != NULL)
565 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
567 gdk_cursor_unref (drag_cursors[i].cursor);
568 drag_cursors[i].cursor = NULL;
572 if (drag_cursors[i].cursor == NULL)
574 GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
575 GdkColor fg = { 0, 0x0000, 0x0000, 0x0000 };
576 GdkScreen *screen = gdk_display_get_default_screen (display);
577 GdkWindow *window = gdk_screen_get_root_window (screen);
580 gdk_bitmap_create_from_data (window, drag_cursors[i].bits, CURSOR_WIDTH, CURSOR_HEIGHT);
583 gdk_bitmap_create_from_data (window, drag_cursors[i].mask, CURSOR_WIDTH, CURSOR_HEIGHT);
585 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
587 gdk_pixmap_unref (pixmap);
588 gdk_pixmap_unref (mask);
591 return drag_cursors[i].cursor;
594 /********************
596 ********************/
598 /*************************************************************
600 * Get the data for a drag or drop
602 * context - drag context
603 * target - format to retrieve the data in.
604 * time - timestamp of triggering event.
607 *************************************************************/
610 gtk_drag_get_data (GtkWidget *widget,
611 GdkDragContext *context,
615 GtkWidget *selection_widget;
617 g_return_if_fail (widget != NULL);
618 g_return_if_fail (context != NULL);
620 selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
622 gdk_drag_context_ref (context);
623 gtk_widget_ref (widget);
625 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
626 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
628 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
630 gtk_selection_convert (selection_widget,
631 gdk_drag_get_selection (context),
637 /*************************************************************
638 * gtk_drag_get_source_widget:
639 * Get the widget the was the source of this drag, if
640 * the drag originated from this application.
642 * context: The drag context for this drag
644 * The source widget, or NULL if the drag originated from
645 * a different application.
646 *************************************************************/
649 gtk_drag_get_source_widget (GdkDragContext *context)
653 tmp_list = source_widgets;
656 GtkWidget *ipc_widget = tmp_list->data;
658 if (ipc_widget->window == context->source_window)
660 GtkDragSourceInfo *info;
661 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
663 return info ? info->widget : NULL;
666 tmp_list = tmp_list->next;
672 /*************************************************************
674 * Notify the drag source that the transfer of data
677 * context: The drag context for this drag
678 * success: Was the data successfully transferred?
679 * time: The timestamp to use when notifying the destination.
681 *************************************************************/
684 gtk_drag_finish (GdkDragContext *context,
689 GdkAtom target = GDK_NONE;
691 g_return_if_fail (context != NULL);
695 target = gdk_atom_intern ("DELETE", FALSE);
697 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
699 target = gdk_atom_intern (success ?
700 "XmTRANSFER_SUCCESS" :
701 "XmTRANSFER_FAILURE",
705 if (target != GDK_NONE)
707 GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
709 gdk_drag_context_ref (context);
711 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
712 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
713 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
716 gtk_selection_convert (selection_widget,
717 gdk_drag_get_selection (context),
722 if (!(success && del))
723 gdk_drop_finish (context, success, time);
726 /*************************************************************
727 * gtk_drag_highlight_expose:
728 * Callback for expose_event for highlighted widgets.
734 *************************************************************/
737 gtk_drag_highlight_expose (GtkWidget *widget,
738 GdkEventExpose *event,
741 gint x, y, width, height;
743 if (GTK_WIDGET_DRAWABLE (widget))
745 if (GTK_WIDGET_NO_WINDOW (widget))
747 x = widget->allocation.x;
748 y = widget->allocation.y;
749 width = widget->allocation.width;
750 height = widget->allocation.height;
756 gdk_window_get_size (widget->window, &width, &height);
759 gtk_draw_shadow (widget->style, widget->window,
760 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
761 x, y, width, height);
763 gdk_draw_rectangle (widget->window,
764 widget->style->black_gc,
766 x, y, width - 1, height - 1);
772 /*************************************************************
773 * gtk_drag_highlight:
774 * Highlight the given widget in the default manner.
778 *************************************************************/
781 gtk_drag_highlight (GtkWidget *widget)
783 gtk_signal_connect_after (GTK_OBJECT (widget), "expose_event",
784 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
787 gtk_widget_queue_draw (widget);
790 /*************************************************************
791 * gtk_drag_unhighlight:
792 * Refresh the given widget to remove the highlight.
796 *************************************************************/
799 gtk_drag_unhighlight (GtkWidget *widget)
801 g_return_if_fail (widget != NULL);
803 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
804 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
807 gtk_widget_queue_clear (widget);
811 gtk_drag_dest_set_internal (GtkWidget *widget,
812 GtkDragDestSite *site)
814 GtkDragDestSite *old_site;
816 g_return_if_fail (widget != NULL);
818 /* HACK, do this in the destroy */
819 old_site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
821 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), old_site);
823 if (GTK_WIDGET_REALIZED (widget))
824 gtk_drag_dest_realized (widget);
826 gtk_signal_connect (GTK_OBJECT (widget), "realize",
827 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), site);
828 gtk_signal_connect (GTK_OBJECT (widget), "hierarchy_changed",
829 GTK_SIGNAL_FUNC (gtk_drag_dest_hierarchy_changed), site);
831 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
832 site, gtk_drag_dest_site_destroy);
836 /*************************************************************
838 * Register a drop site, and possibly add default behaviors.
841 * flags: Which types of default drag behavior to use
842 * targets: Table of targets that can be accepted
843 * n_targets: Number of of entries in targets
846 *************************************************************/
849 gtk_drag_dest_set (GtkWidget *widget,
850 GtkDestDefaults flags,
851 const GtkTargetEntry *targets,
853 GdkDragAction actions)
855 GtkDragDestSite *site;
857 g_return_if_fail (widget != NULL);
859 site = g_new (GtkDragDestSite, 1);
862 site->have_drag = FALSE;
864 site->target_list = gtk_target_list_new (targets, n_targets);
866 site->target_list = NULL;
867 site->actions = actions;
868 site->do_proxy = FALSE;
869 site->proxy_window = NULL;
871 gtk_drag_dest_set_internal (widget, site);
874 /*************************************************************
875 * gtk_drag_dest_set_proxy:
876 * Set up this widget to proxy drags elsewhere.
879 * proxy_window: window to which forward drag events
880 * protocol: Drag protocol which the dest widget accepts
881 * use_coordinates: If true, send the same coordinates to the
882 * destination, because it is a embedded
885 *************************************************************/
888 gtk_drag_dest_set_proxy (GtkWidget *widget,
889 GdkWindow *proxy_window,
890 GdkDragProtocol protocol,
891 gboolean use_coordinates)
893 GtkDragDestSite *site;
895 g_return_if_fail (widget != NULL);
897 site = g_new (GtkDragDestSite, 1);
900 site->have_drag = FALSE;
901 site->target_list = NULL;
903 site->proxy_window = proxy_window;
905 gdk_window_ref (proxy_window);
906 site->do_proxy = TRUE;
907 site->proxy_protocol = protocol;
908 site->proxy_coords = use_coordinates;
910 gtk_drag_dest_set_internal (widget, site);
913 /*************************************************************
914 * gtk_drag_dest_unset
915 * Unregister this widget as a drag target.
919 *************************************************************/
922 gtk_drag_dest_unset (GtkWidget *widget)
924 g_return_if_fail (widget != NULL);
926 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
930 * gtk_drag_dest_get_target_list:
931 * @widget: a #GtkWidget
933 * Returns the list of targets this widget can accept from
936 * Return value: the #GtkTargetList, or %NULL if none
939 gtk_drag_dest_get_target_list (GtkWidget *widget)
941 GtkDragDestSite *site;
943 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
945 return site ? site->target_list : NULL;
949 * gtk_drag_dest_set_target_list:
950 * @widget: a #GtkWidget that's a drag destination
951 * @target_list: list of droppable targets, or %NULL for none
953 * Sets the target types that this widget can accept from drag-and-drop.
954 * The widget must first be made into a drag destination with
955 * gtk_drag_dest_set().
958 gtk_drag_dest_set_target_list (GtkWidget *widget,
959 GtkTargetList *target_list)
961 GtkDragDestSite *site;
963 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
967 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");
972 gtk_target_list_ref (target_list);
974 if (site->target_list)
975 gtk_target_list_unref (site->target_list);
977 site->target_list = target_list;
981 /*************************************************************
982 * _gtk_drag_dest_handle_event:
983 * Called from widget event handling code on Drag events
987 * toplevel: Toplevel widget that received the event
990 *************************************************************/
993 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
996 GtkDragDestInfo *info;
997 GdkDragContext *context;
999 g_return_if_fail (toplevel != NULL);
1000 g_return_if_fail (event != NULL);
1002 context = event->dnd.context;
1004 info = gtk_drag_get_dest_info (context, TRUE);
1006 /* Find the widget for the event */
1007 switch (event->type)
1009 case GDK_DRAG_ENTER:
1012 case GDK_DRAG_LEAVE:
1015 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1016 info->widget = NULL;
1020 case GDK_DRAG_MOTION:
1021 case GDK_DROP_START:
1023 GtkDragFindData data;
1026 if (event->type == GDK_DROP_START)
1028 info->dropped = TRUE;
1029 /* We send a leave here so that the widget unhighlights
1034 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1035 info->widget = NULL;
1039 gdk_window_get_origin (toplevel->window, &tx, &ty);
1041 data.x = event->dnd.x_root - tx;
1042 data.y = event->dnd.y_root - ty;
1043 data.context = context;
1046 data.toplevel = TRUE;
1047 data.callback = (event->type == GDK_DRAG_MOTION) ?
1048 gtk_drag_dest_motion : gtk_drag_dest_drop;
1049 data.time = event->dnd.time;
1051 gtk_drag_find_widget (toplevel, &data);
1053 if (info->widget && !data.found)
1055 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1056 info->widget = NULL;
1061 if (event->type == GDK_DRAG_MOTION)
1064 gdk_drag_status (context, 0, event->dnd.time);
1066 else if (event->type == GDK_DROP_START && !info->proxy_source)
1068 gdk_drop_reply (context, data.found, event->dnd.time);
1069 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1070 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1076 g_assert_not_reached ();
1081 * gtk_drag_dest_find_target:
1082 * @widget: drag destination widget
1083 * @context: drag context
1084 * @target_list: list of droppable targets, or %NULL to use
1085 * gtk_drag_dest_get_target_list (@widget).
1087 * Looks for a match between @context->targets and the
1088 * @dest_target_list, returning the first matching target, otherwise
1089 * returning %GDK_NONE. @dest_target_list should usually be the return
1090 * value from gtk_drag_dest_get_target_list(), but some widgets may
1091 * have different valid targets for different parts of the widget; in
1092 * that case, they will have to implement a drag_motion handler that
1093 * passes the correct target list to this function.
1095 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1098 gtk_drag_dest_find_target (GtkWidget *widget,
1099 GdkDragContext *context,
1100 GtkTargetList *target_list)
1103 GList *tmp_source = NULL;
1104 GtkWidget *source_widget;
1106 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1107 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1109 source_widget = gtk_drag_get_source_widget (context);
1111 if (target_list == NULL)
1112 target_list = gtk_drag_dest_get_target_list (widget);
1114 if (target_list == NULL)
1117 tmp_target = target_list->list;
1120 GtkTargetPair *pair = tmp_target->data;
1121 tmp_source = context->targets;
1124 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1126 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1127 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1128 return pair->target;
1132 tmp_source = tmp_source->next;
1134 tmp_target = tmp_target->next;
1141 gtk_drag_selection_received (GtkWidget *widget,
1142 GtkSelectionData *selection_data,
1146 GdkDragContext *context;
1147 GtkDragDestInfo *info;
1148 GtkWidget *drop_widget;
1152 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1153 info = gtk_drag_get_dest_info (context, FALSE);
1155 if (info->proxy_data &&
1156 info->proxy_data->target == selection_data->target)
1158 gtk_selection_data_set (info->proxy_data,
1159 selection_data->type,
1160 selection_data->format,
1161 selection_data->data,
1162 selection_data->length);
1167 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1169 gtk_drag_finish (context, TRUE, FALSE, time);
1171 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1172 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1178 GtkDragDestSite *site;
1180 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1182 if (site && site->target_list)
1186 if (gtk_target_list_find (site->target_list,
1187 selection_data->target,
1190 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1191 selection_data->length >= 0)
1192 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1193 "drag_data_received",
1194 context, info->drop_x, info->drop_y,
1201 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1202 "drag_data_received",
1203 context, info->drop_x, info->drop_y,
1204 selection_data, 0, time);
1207 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1210 gtk_drag_finish (context,
1211 (selection_data->length >= 0),
1212 (context->action == GDK_ACTION_MOVE),
1216 gtk_widget_unref (drop_widget);
1219 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1220 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1223 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1224 gdk_drag_context_unref (context);
1226 gtk_drag_release_ipc_widget (widget);
1230 prepend_and_ref_widget (GtkWidget *widget,
1233 GSList **slist_p = data;
1235 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1238 /*************************************************************
1239 * gtk_drag_find_widget:
1240 * Recursive callback used to locate widgets for
1241 * DRAG_MOTION and DROP_START events.
1245 *************************************************************/
1248 gtk_drag_find_widget (GtkWidget *widget,
1249 GtkDragFindData *data)
1251 GtkAllocation new_allocation;
1252 gint allocation_to_window_x = 0;
1253 gint allocation_to_window_y = 0;
1257 if (data->found || !GTK_WIDGET_MAPPED (widget))
1260 /* Note that in the following code, we only count the
1261 * position as being inside a WINDOW widget if it is inside
1262 * widget->window; points that are outside of widget->window
1263 * but within the allocation are not counted. This is consistent
1264 * with the way we highlight drag targets.
1266 * data->x,y are relative to widget->parent->window (if
1267 * widget is not a toplevel, widget->window otherwise).
1268 * We compute the allocation of widget in the same coordinates,
1269 * clipping to widget->window, and all intermediate
1270 * windows. If data->x,y is inside that, then we translate
1271 * our coordinates to be relative to widget->window and
1274 new_allocation = widget->allocation;
1279 GdkWindow *window = widget->window;
1281 /* Compute the offset from allocation-relative to
1282 * window-relative coordinates.
1284 allocation_to_window_x = widget->allocation.x;
1285 allocation_to_window_y = widget->allocation.y;
1287 if (!GTK_WIDGET_NO_WINDOW (widget))
1289 /* The allocation is relative to the parent window for
1290 * window widgets, not to widget->window.
1292 gdk_window_get_position (window, &tx, &ty);
1294 allocation_to_window_x -= tx;
1295 allocation_to_window_y -= ty;
1298 new_allocation.x = 0 + allocation_to_window_x;
1299 new_allocation.y = 0 + allocation_to_window_y;
1301 while (window && window != widget->parent->window)
1303 GdkRectangle window_rect = { 0, 0, 0, 0 };
1305 gdk_window_get_size (window, &window_rect.width, &window_rect.height);
1307 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1309 gdk_window_get_position (window, &tx, &ty);
1310 new_allocation.x += tx;
1312 new_allocation.y += ty;
1315 window = gdk_window_get_parent (window);
1318 if (!window) /* Window and widget heirarchies didn't match. */
1322 if (data->toplevel ||
1323 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1324 (data->x < new_allocation.x + new_allocation.width) &&
1325 (data->y < new_allocation.y + new_allocation.height)))
1327 /* First, check if the drag is in a valid drop site in
1328 * one of our children
1330 if (GTK_IS_CONTAINER (widget))
1332 GtkDragFindData new_data = *data;
1333 GSList *children = NULL;
1336 new_data.x -= x_offset;
1337 new_data.y -= y_offset;
1338 new_data.found = FALSE;
1339 new_data.toplevel = FALSE;
1341 /* need to reference children temporarily in case the
1342 * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1344 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1345 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1347 if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1348 gtk_drag_find_widget (tmp_list->data, &new_data);
1349 gtk_widget_unref (tmp_list->data);
1351 g_slist_free (children);
1353 data->found = new_data.found;
1356 /* If not, and this widget is registered as a drop site, check to
1357 * emit "drag_motion" to check if we are actually in
1361 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1363 data->found = data->callback (widget,
1365 data->x - x_offset - allocation_to_window_x,
1366 data->y - y_offset - allocation_to_window_y,
1368 /* If so, send a "drag_leave" to the last widget */
1371 if (data->info->widget && data->info->widget != widget)
1373 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1375 data->info->widget = widget;
1382 gtk_drag_proxy_begin (GtkWidget *widget,
1383 GtkDragDestInfo *dest_info,
1386 GtkDragSourceInfo *source_info;
1388 GdkDragContext *context;
1389 GtkWidget *ipc_widget;
1391 if (dest_info->proxy_source)
1393 gdk_drag_abort (dest_info->proxy_source->context, time);
1394 gtk_drag_source_info_destroy (dest_info->proxy_source);
1395 dest_info->proxy_source = NULL;
1398 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1399 context = gdk_drag_begin (ipc_widget->window,
1400 dest_info->context->targets);
1402 source_info = gtk_drag_get_source_info (context, TRUE);
1404 source_info->ipc_widget = ipc_widget;
1405 source_info->widget = gtk_widget_ref (widget);
1407 source_info->target_list = gtk_target_list_new (NULL, 0);
1408 tmp_list = dest_info->context->targets;
1411 gtk_target_list_add (source_info->target_list,
1412 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1413 tmp_list = tmp_list->next;
1416 source_info->proxy_dest = dest_info;
1418 gtk_signal_connect (GTK_OBJECT (ipc_widget),
1420 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1423 dest_info->proxy_source = source_info;
1427 gtk_drag_dest_info_destroy (gpointer data)
1429 GtkDragDestInfo *info = data;
1434 static GtkDragDestInfo *
1435 gtk_drag_get_dest_info (GdkDragContext *context,
1438 GtkDragDestInfo *info;
1439 static GQuark info_quark = 0;
1441 info_quark = g_quark_from_static_string ("gtk-dest-info");
1443 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1444 if (!info && create)
1446 info = g_new (GtkDragDestInfo, 1);
1447 info->widget = NULL;
1448 info->context = context;
1449 info->proxy_source = NULL;
1450 info->proxy_data = NULL;
1451 info->dropped = FALSE;
1452 info->proxy_drop_wait = FALSE;
1453 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1454 info, gtk_drag_dest_info_destroy);
1460 static GQuark dest_info_quark = 0;
1462 static GtkDragSourceInfo *
1463 gtk_drag_get_source_info (GdkDragContext *context,
1466 GtkDragSourceInfo *info;
1467 if (!dest_info_quark)
1468 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1470 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1471 if (!info && create)
1473 info = g_new0 (GtkDragSourceInfo, 1);
1474 info->context = context;
1475 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1482 gtk_drag_clear_source_info (GdkDragContext *context)
1484 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1488 gtk_drag_dest_realized (GtkWidget *widget)
1490 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1492 if (GTK_WIDGET_TOPLEVEL (toplevel))
1493 gdk_window_register_dnd (toplevel->window);
1497 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1498 GtkWidget *previous_toplevel)
1500 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1502 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1503 gdk_window_register_dnd (toplevel->window);
1507 gtk_drag_dest_site_destroy (gpointer data)
1509 GtkDragDestSite *site = data;
1511 if (site->proxy_window)
1512 g_object_unref (site->proxy_window);
1514 if (site->target_list)
1515 gtk_target_list_unref (site->target_list);
1521 * Default drag handlers
1524 gtk_drag_dest_leave (GtkWidget *widget,
1525 GdkDragContext *context,
1528 GtkDragDestSite *site;
1530 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1531 g_return_if_fail (site != NULL);
1535 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1537 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1539 gdk_drag_abort (info->proxy_source->context, time);
1540 gtk_drag_source_info_destroy (info->proxy_source);
1541 info->proxy_source = NULL;
1548 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1549 gtk_drag_unhighlight (widget);
1551 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1552 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1555 site->have_drag = FALSE;
1560 gtk_drag_dest_motion (GtkWidget *widget,
1561 GdkDragContext *context,
1566 GtkDragDestSite *site;
1567 GdkDragAction action = 0;
1570 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1571 g_return_val_if_fail (site != NULL, FALSE);
1576 GdkEvent *current_event;
1577 GdkWindow *dest_window;
1578 GdkDragProtocol proto;
1580 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1582 if (!info->proxy_source || info->proxy_source->widget != widget)
1583 gtk_drag_proxy_begin (widget, info, time);
1585 current_event = gtk_get_current_event ();
1587 if (site->proxy_window)
1589 dest_window = site->proxy_window;
1590 proto = site->proxy_protocol;
1594 gdk_drag_find_window (info->proxy_source->context,
1596 current_event->dnd.x_root,
1597 current_event->dnd.y_root,
1598 &dest_window, &proto);
1601 gdk_drag_motion (info->proxy_source->context,
1603 current_event->dnd.x_root,
1604 current_event->dnd.y_root,
1605 context->suggested_action,
1606 context->actions, time);
1608 if (!site->proxy_window && dest_window)
1609 gdk_window_unref (dest_window);
1611 selection = gdk_drag_get_selection (info->proxy_source->context);
1613 selection != gdk_drag_get_selection (info->context))
1614 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1616 gdk_event_free (current_event);
1621 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1623 if (context->suggested_action & site->actions)
1624 action = context->suggested_action;
1631 if ((site->actions & (1 << i)) &&
1632 (context->actions & (1 << i)))
1640 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1642 if (!site->have_drag)
1644 site->have_drag = TRUE;
1645 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1646 gtk_drag_highlight (widget);
1649 gdk_drag_status (context, action, time);
1653 gdk_drag_status (context, 0, time);
1658 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1659 context, x, y, time, &retval);
1661 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1665 gtk_drag_dest_drop (GtkWidget *widget,
1666 GdkDragContext *context,
1671 GtkDragDestSite *site;
1672 GtkDragDestInfo *info;
1674 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1675 g_return_val_if_fail (site != NULL, FALSE);
1677 info = gtk_drag_get_dest_info (context, FALSE);
1678 g_return_val_if_fail (info != NULL, FALSE);
1685 if (info->proxy_source ||
1686 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1688 gtk_drag_drop (info->proxy_source, time);
1692 /* We need to synthesize a motion event, wait for a status,
1693 * and, if we get a good one, do a drop.
1696 GdkEvent *current_event;
1698 GdkWindow *dest_window;
1699 GdkDragProtocol proto;
1701 gtk_drag_proxy_begin (widget, info, time);
1702 info->proxy_drop_wait = TRUE;
1703 info->proxy_drop_time = time;
1705 current_event = gtk_get_current_event ();
1707 if (site->proxy_window)
1709 dest_window = site->proxy_window;
1710 proto = site->proxy_protocol;
1714 gdk_drag_find_window (info->proxy_source->context,
1716 current_event->dnd.x_root,
1717 current_event->dnd.y_root,
1718 &dest_window, &proto);
1721 gdk_drag_motion (info->proxy_source->context,
1723 current_event->dnd.x_root,
1724 current_event->dnd.y_root,
1725 context->suggested_action,
1726 context->actions, time);
1728 if (!site->proxy_window && dest_window)
1729 gdk_window_unref (dest_window);
1731 selection = gdk_drag_get_selection (info->proxy_source->context);
1733 selection != gdk_drag_get_selection (info->context))
1734 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1736 gdk_event_free (current_event);
1745 if (site->flags & GTK_DEST_DEFAULT_DROP)
1747 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1749 if (target == GDK_NONE)
1751 gtk_drag_finish (context, FALSE, FALSE, time);
1755 gtk_drag_get_data (widget, context, target, time);
1758 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1759 context, x, y, time, &retval);
1761 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1769 /*************************************************************
1770 * gtk_drag_begin: Start a drag operation
1773 * widget: Widget from which drag starts
1774 * handlers: List of handlers to supply the data for the drag
1775 * button: Button user used to start drag
1776 * time: Time of event starting drag
1779 *************************************************************/
1782 gtk_drag_begin (GtkWidget *widget,
1783 GtkTargetList *target_list,
1784 GdkDragAction actions,
1788 GtkDragSourceInfo *info;
1789 GList *targets = NULL;
1791 guint32 time = GDK_CURRENT_TIME;
1792 GdkDragAction possible_actions, suggested_action;
1793 GdkDragContext *context;
1794 GtkWidget *ipc_widget;
1796 g_return_val_if_fail (widget != NULL, NULL);
1797 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1798 g_return_val_if_fail (target_list != NULL, NULL);
1801 time = gdk_event_get_time (event);
1803 tmp_list = g_list_last (target_list->list);
1806 GtkTargetPair *pair = tmp_list->data;
1807 targets = g_list_prepend (targets,
1808 GINT_TO_POINTER (pair->target));
1809 tmp_list = tmp_list->prev;
1812 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1813 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1815 context = gdk_drag_begin (ipc_widget->window, targets);
1816 g_list_free (targets);
1818 info = gtk_drag_get_source_info (context, TRUE);
1820 info->ipc_widget = ipc_widget;
1821 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1823 info->widget = gtk_widget_ref (widget);
1826 info->button = button;
1827 info->target_list = target_list;
1828 gtk_target_list_ref (target_list);
1830 info->possible_actions = actions;
1832 info->cursor = NULL;
1833 info->status = GTK_DRAG_STATUS_DRAG;
1834 info->last_event = NULL;
1835 info->selections = NULL;
1836 info->icon_window = NULL;
1837 info->destroy_icon = FALSE;
1839 gtk_drag_get_event_actions (event, info->button, actions,
1840 &suggested_action, &possible_actions);
1842 info->cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), suggested_action);
1844 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1845 * the drag icon, it will be in the right place
1847 if (event && event->type == GDK_MOTION_NOTIFY)
1849 info->cur_x = event->motion.x_root;
1850 info->cur_y = event->motion.y_root;
1854 gdk_window_get_pointer (gtk_widget_get_root_window (widget),
1855 &info->cur_x, &info->cur_y, NULL);
1858 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1861 if (event && event->type == GDK_MOTION_NOTIFY)
1862 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1864 info->start_x = info->cur_x;
1865 info->start_y = info->cur_y;
1867 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1868 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1869 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1870 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1871 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
1872 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1873 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
1874 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1875 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1876 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1878 /* We use a GTK grab here to override any grabs that the widget
1879 * we are dragging from might have held
1881 gtk_grab_add (info->ipc_widget);
1882 if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1883 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1884 GDK_BUTTON_RELEASE_MASK, NULL,
1885 info->cursor, time) == 0)
1887 if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1889 /* FIXME: This should be cleaned up... */
1893 ev.type = GDK_BUTTON_RELEASE;
1894 ev.button = info->button;
1896 gtk_drag_button_release_cb (widget, &ev, info);
1902 info->have_grab = TRUE;
1903 info->grab_time = time;
1905 return info->context;
1908 /*************************************************************
1909 * gtk_drag_source_set:
1910 * Register a drop site, and possibly add default behaviors.
1913 * start_button_mask: Mask of allowed buttons to start drag
1914 * targets: Table of targets for this source
1916 * actions: Actions allowed for this source
1918 *************************************************************/
1921 gtk_drag_source_set (GtkWidget *widget,
1922 GdkModifierType start_button_mask,
1923 const GtkTargetEntry *targets,
1925 GdkDragAction actions)
1927 GtkDragSourceSite *site;
1929 g_return_if_fail (widget != NULL);
1931 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1933 gtk_widget_add_events (widget,
1934 gtk_widget_get_events (widget) |
1935 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1936 GDK_BUTTON_MOTION_MASK);
1940 if (site->target_list)
1941 gtk_target_list_unref (site->target_list);
1945 site = g_new0 (GtkDragSourceSite, 1);
1947 site->icon_type = GTK_IMAGE_EMPTY;
1949 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1950 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1952 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1953 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1956 gtk_object_set_data_full (GTK_OBJECT (widget),
1958 site, gtk_drag_source_site_destroy);
1961 site->start_button_mask = start_button_mask;
1964 site->target_list = gtk_target_list_new (targets, n_targets);
1966 site->target_list = NULL;
1968 site->actions = actions;
1972 /*************************************************************
1973 * gtk_drag_source_unset
1974 * Unregister this widget as a drag source.
1978 *************************************************************/
1981 gtk_drag_source_unset (GtkWidget *widget)
1983 GtkDragSourceSite *site;
1985 g_return_if_fail (widget != NULL);
1987 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1991 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1992 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1997 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
1999 switch (site->icon_type)
2001 case GTK_IMAGE_EMPTY:
2003 case GTK_IMAGE_PIXMAP:
2004 if (site->icon_data.pixmap.pixmap)
2005 gdk_pixmap_unref (site->icon_data.pixmap.pixmap);
2006 if (site->icon_mask)
2007 gdk_pixmap_unref (site->icon_mask);
2009 case GTK_IMAGE_PIXBUF:
2010 g_object_unref (G_OBJECT (site->icon_data.pixbuf.pixbuf));
2012 case GTK_IMAGE_STOCK:
2013 g_free (G_OBJECT (site->icon_data.stock.stock_id));
2016 g_assert_not_reached();
2019 site->icon_type = GTK_IMAGE_EMPTY;
2022 gdk_colormap_unref (site->colormap);
2023 site->colormap = NULL;
2027 * gtk_drag_source_set_icon:
2028 * @widget: a #GtkWidget
2029 * @colormap: the colormap of the icon
2030 * @pixmap: the image data for the icon
2031 * @mask: the transparency mask for an image.
2033 * Sets the icon that will be used for drags from a particular widget
2034 * from a pixmap/mask. GTK+ retains references for the arguments, and
2035 * will release them when they are no longer needed.
2036 * Use gtk_drag_source_set_icon_pixbuf() instead.
2039 gtk_drag_source_set_icon (GtkWidget *widget,
2040 GdkColormap *colormap,
2044 GtkDragSourceSite *site;
2046 g_return_if_fail (widget != NULL);
2047 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2048 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2049 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2051 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2052 g_return_if_fail (site != NULL);
2054 gdk_colormap_ref (colormap);
2055 gdk_pixmap_ref (pixmap);
2057 gdk_pixmap_ref (mask);
2059 gtk_drag_source_unset_icon (site);
2061 site->icon_type = GTK_IMAGE_PIXMAP;
2063 site->icon_data.pixmap.pixmap = pixmap;
2064 site->icon_mask = mask;
2065 site->colormap = colormap;
2069 * gtk_drag_source_set_icon_pixbuf:
2070 * @widget: a #GtkWidget
2071 * @pixbuf: the #GdkPixbuf for the drag icon
2073 * Sets the icon that will be used for drags from a particular widget
2074 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2075 * release it when it is no longer needed.
2078 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2081 GtkDragSourceSite *site;
2083 g_return_if_fail (widget != NULL);
2084 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2086 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2087 g_return_if_fail (site != NULL);
2088 gdk_pixbuf_ref (pixbuf);
2090 gtk_drag_source_unset_icon (site);
2092 site->icon_type = GTK_IMAGE_PIXBUF;
2093 site->icon_data.pixbuf.pixbuf = pixbuf;
2097 * gtk_drag_source_set_icon_stock:
2098 * @widget: a #GtkWidget
2099 * @stock_id: the ID of the stock icon to use
2101 * Sets the icon that will be used for drags from a particular source
2105 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2106 const gchar *stock_id)
2108 GtkDragSourceSite *site;
2110 g_return_if_fail (widget != NULL);
2111 g_return_if_fail (stock_id != NULL);
2113 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2114 g_return_if_fail (site != NULL);
2116 gtk_drag_source_unset_icon (site);
2118 site->icon_data.stock.stock_id = g_strdup (stock_id);
2122 gtk_drag_set_icon_window (GdkDragContext *context,
2126 gboolean destroy_on_release)
2128 GtkDragSourceInfo *info;
2130 g_return_if_fail (context != NULL);
2131 g_return_if_fail (widget != NULL);
2133 info = gtk_drag_get_source_info (context, FALSE);
2134 gtk_drag_remove_icon (info);
2136 info->icon_window = widget;
2137 info->hot_x = hot_x;
2138 info->hot_y = hot_y;
2142 gtk_widget_set_uposition (widget,
2143 info->cur_x - info->hot_x,
2144 info->cur_y - info->hot_y);
2145 gtk_widget_ref (widget);
2146 gdk_window_raise (widget->window);
2147 gtk_widget_show (widget);
2150 info->destroy_icon = destroy_on_release;
2154 * gtk_drag_set_icon_widget:
2155 * @context: the context for a drag. (This must be called
2156 with a context for the source side of a drag)
2157 * @widget: a toplevel window to use as an icon.
2158 * @hot_x: the X offset within @widget of the hotspot.
2159 * @hot_y: the Y offset within @widget of the hotspot.
2161 * Changes the icon for a widget to a given widget. GTK+
2162 * will not destroy the icon, so if you don't want
2163 * it to persist, you should connect to the "drag_end"
2164 * signal and destroy it yourself.
2167 gtk_drag_set_icon_widget (GdkDragContext *context,
2172 g_return_if_fail (context != NULL);
2173 g_return_if_fail (widget != NULL);
2175 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2179 set_icon_stock_pixbuf (GdkDragContext *context,
2180 const gchar *stock_id,
2190 GdkColormap *colormap;
2192 g_return_if_fail (context != NULL);
2193 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2194 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2196 screen = gdk_drawable_get_screen (context->source_window);
2197 colormap = gdk_screen_get_rgb_colormap (screen);
2199 gtk_widget_push_colormap (colormap);
2200 window = gtk_window_new (GTK_WINDOW_POPUP);
2201 gtk_window_set_screen (GTK_WINDOW (window), screen);
2202 gtk_widget_pop_colormap ();
2204 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2205 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2209 pixbuf = gtk_widget_render_icon (window, stock_id,
2210 GTK_ICON_SIZE_DND, NULL);
2214 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2215 gtk_widget_destroy (window);
2221 width = gdk_pixbuf_get_width (pixbuf);
2222 height = gdk_pixbuf_get_width (pixbuf);
2224 gtk_widget_set_usize (window,
2225 gdk_pixbuf_get_width (pixbuf),
2226 gdk_pixbuf_get_height (pixbuf));
2227 gtk_widget_realize (window);
2229 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf, colormap, &pixmap, &mask, 128);
2231 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2234 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2236 g_object_unref (G_OBJECT (pixmap));
2239 g_object_unref (G_OBJECT (mask));
2241 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2245 * gtk_drag_set_icon_pixbuf:
2246 * @context: the context for a drag. (This must be called
2247 * with a context for the source side of a drag)
2248 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2249 * @hot_x: the X offset within @widget of the hotspot.
2250 * @hot_y: the Y offset within @widget of the hotspot.
2252 * Sets @pixbuf as the icon for a given drag.
2255 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2260 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2261 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2263 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2267 * gtk_drag_set_icon_stock:
2268 * @context: the context for a drag. (This must be called
2269 * with a context for the source side of a drag)
2270 * @stock_id: the ID of the stock icon to use for the drag.
2271 * @hot_x: the X offset within the icon of the hotspot.
2272 * @hot_y: the Y offset within the icon of the hotspot.
2274 * Sets the the icon for a given drag from a stock ID.
2277 gtk_drag_set_icon_stock (GdkDragContext *context,
2278 const gchar *stock_id,
2282 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2283 g_return_if_fail (stock_id != NULL);
2285 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2289 * gtk_drag_set_icon_pixmap:
2290 * @context: the context for a drag. (This must be called
2291 * with a context for the source side of a drag)
2292 * @colormap: the colormap of the icon
2293 * @pixmap: the image data for the icon
2294 * @mask: the transparency mask for the icon
2295 * @hot_x: the X offset within @pixmap of the hotspot.
2296 * @hot_y: the Y offset within @pixmap of the hotspot.
2298 * Sets @pixmap as the icon for a given drag. GTK+ retains
2299 * references for the arguments, and will release them when
2300 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2301 * will be more convenient to use.
2304 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2305 GdkColormap *colormap,
2314 g_return_if_fail (context != NULL);
2315 g_return_if_fail (colormap != NULL);
2316 g_return_if_fail (pixmap != NULL);
2318 gdk_window_get_size (pixmap, &width, &height);
2320 gtk_widget_push_colormap (colormap);
2322 window = gtk_window_new (GTK_WINDOW_POPUP);
2323 gtk_window_set_screen (GTK_WINDOW (window), gdk_drawable_get_screen (context->source_window));
2324 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2325 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2327 gtk_widget_pop_colormap ();
2329 gtk_widget_set_usize (window, width, height);
2330 gtk_widget_realize (window);
2332 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2335 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2337 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2341 * gtk_drag_set_icon_default:
2342 * @context: the context for a drag. (This must be called
2343 with a context for the source side of a drag)
2345 * Sets the icon for a particular drag to the default
2349 gtk_drag_set_icon_default (GdkDragContext *context)
2351 g_return_if_fail (context != NULL);
2353 if (!default_icon_pixmap)
2354 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2356 gtk_drag_set_icon_pixmap (context,
2357 default_icon_colormap,
2358 default_icon_pixmap,
2361 default_icon_hot_y);
2365 * gtk_drag_set_default_icon:
2366 * @colormap: the colormap of the icon
2367 * @pixmap: the image data for the icon
2368 * @mask: the transparency mask for an image.
2369 * @hot_x: The X offset within @widget of the hotspot.
2370 * @hot_y: The Y offset within @widget of the hotspot.
2372 * Changes the default drag icon. GTK+ retains references for the
2373 * arguments, and will release them when they are no longer needed.
2374 * This function is obsolete. The default icon should now be changed
2375 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2378 gtk_drag_set_default_icon (GdkColormap *colormap,
2384 g_return_if_fail (colormap != NULL);
2385 g_return_if_fail (pixmap != NULL);
2387 if (default_icon_colormap)
2388 gdk_colormap_unref (default_icon_colormap);
2389 if (default_icon_pixmap)
2390 gdk_pixmap_unref (default_icon_pixmap);
2391 if (default_icon_mask)
2392 gdk_pixmap_unref (default_icon_mask);
2394 default_icon_colormap = colormap;
2395 gdk_colormap_ref (colormap);
2397 default_icon_pixmap = pixmap;
2398 gdk_pixmap_ref (pixmap);
2400 default_icon_mask = mask;
2402 gdk_pixmap_ref (mask);
2404 default_icon_hot_x = hot_x;
2405 default_icon_hot_y = hot_y;
2409 /*************************************************************
2410 * _gtk_drag_source_handle_event:
2411 * Called from widget event handling code on Drag events
2415 * toplevel: Toplevel widget that received the event
2418 *************************************************************/
2421 _gtk_drag_source_handle_event (GtkWidget *widget,
2424 GtkDragSourceInfo *info;
2425 GdkDragContext *context;
2427 g_return_if_fail (widget != NULL);
2428 g_return_if_fail (event != NULL);
2430 context = event->dnd.context;
2431 info = gtk_drag_get_source_info (context, FALSE);
2435 switch (event->type)
2437 case GDK_DRAG_STATUS:
2441 if (info->proxy_dest)
2443 if (!event->dnd.send_event)
2445 if (info->proxy_dest->proxy_drop_wait)
2447 gboolean result = context->action != 0;
2449 /* Aha - we can finally pass the MOTIF DROP on... */
2450 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2452 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2454 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2458 gdk_drag_status (info->proxy_dest->context,
2459 event->dnd.context->action,
2464 else if (info->have_grab)
2466 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2467 event->dnd.context->action);
2468 if (info->cursor != cursor)
2470 gdk_pointer_grab (widget->window, FALSE,
2471 GDK_POINTER_MOTION_MASK |
2472 GDK_POINTER_MOTION_HINT_MASK |
2473 GDK_BUTTON_RELEASE_MASK,
2475 cursor, info->grab_time);
2476 info->cursor = cursor;
2479 if (info->last_event)
2481 gtk_drag_update (info,
2482 info->cur_x, info->cur_y,
2484 info->last_event = NULL;
2490 case GDK_DROP_FINISHED:
2491 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2494 g_assert_not_reached ();
2498 /*************************************************************
2499 * gtk_drag_source_check_selection:
2500 * Check if we've set up handlers/claimed the selection
2501 * for a given drag. If not, add them.
2505 *************************************************************/
2508 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2514 tmp_list = info->selections;
2517 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2519 tmp_list = tmp_list->next;
2522 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2526 info->selections = g_list_prepend (info->selections,
2527 GUINT_TO_POINTER (selection));
2529 tmp_list = info->target_list->list;
2532 GtkTargetPair *pair = tmp_list->data;
2534 gtk_selection_add_target (info->ipc_widget,
2538 tmp_list = tmp_list->next;
2541 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2543 gtk_selection_add_target (info->ipc_widget,
2545 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2546 TARGET_MOTIF_SUCCESS);
2547 gtk_selection_add_target (info->ipc_widget,
2549 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2550 TARGET_MOTIF_FAILURE);
2553 gtk_selection_add_target (info->ipc_widget,
2555 gdk_atom_intern ("DELETE", FALSE),
2559 /*************************************************************
2560 * gtk_drag_drop_finished:
2561 * Clean up from the drag, and display snapback, if necessary.
2567 *************************************************************/
2570 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2574 gtk_drag_source_release_selections (info, time);
2576 if (info->proxy_dest)
2578 /* The time from the event isn't reliable for Xdnd drags */
2579 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2580 info->proxy_dest->proxy_drop_time);
2581 gtk_drag_source_info_destroy (info);
2587 gtk_drag_source_info_destroy (info);
2591 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2595 anim->n_steps = MAX (info->cur_x - info->start_x,
2596 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2597 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2598 if (info->icon_window)
2600 gtk_widget_show (info->icon_window);
2601 gdk_window_raise (info->icon_window->window);
2604 /* Mark the context as dead, so if the destination decides
2605 * to respond really late, we still are OK.
2607 gtk_drag_clear_source_info (info->context);
2608 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2614 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2617 GdkDisplay *display = gtk_widget_get_display (info->widget);
2618 GList *tmp_list = info->selections;
2622 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2623 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2624 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2626 tmp_list = tmp_list->next;
2629 g_list_free (info->selections);
2630 info->selections = NULL;
2633 /*************************************************************
2635 * Send a drop event.
2639 *************************************************************/
2642 gtk_drag_drop (GtkDragSourceInfo *info,
2645 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2647 GtkSelectionData selection_data;
2649 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2651 tmp_list = info->target_list->list;
2654 GtkTargetPair *pair = tmp_list->data;
2656 if (pair->target == target)
2658 selection_data.selection = GDK_NONE;
2659 selection_data.target = target;
2660 selection_data.data = NULL;
2661 selection_data.length = -1;
2663 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2664 info->context, &selection_data,
2668 /* FIXME: Should we check for length >= 0 here? */
2669 gtk_drag_drop_finished (info, TRUE, time);
2672 tmp_list = tmp_list->next;
2674 gtk_drag_drop_finished (info, FALSE, time);
2678 if (info->icon_window)
2679 gtk_widget_hide (info->icon_window);
2681 gdk_drag_drop (info->context, time);
2682 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2683 gtk_drag_abort_timeout,
2689 * Source side callbacks.
2693 gtk_drag_source_event_cb (GtkWidget *widget,
2697 GtkDragSourceSite *site;
2698 gboolean retval = FALSE;
2699 site = (GtkDragSourceSite *)data;
2701 switch (event->type)
2703 case GDK_BUTTON_PRESS:
2704 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2706 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2707 site->x = event->button.x;
2708 site->y = event->button.y;
2712 case GDK_BUTTON_RELEASE:
2713 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2714 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2717 case GDK_MOTION_NOTIFY:
2718 if (site->state & event->motion.state & site->start_button_mask)
2720 /* FIXME: This is really broken and can leave us
2726 if (site->state & event->motion.state &
2727 GDK_BUTTON1_MASK << (i - 1))
2731 if (gtk_drag_check_threshold (widget, site->x, site->y,
2732 event->motion.x, event->motion.y))
2734 GtkDragSourceInfo *info;
2735 GdkDragContext *context;
2738 context = gtk_drag_begin (widget, site->target_list,
2742 info = gtk_drag_get_source_info (context, FALSE);
2744 if (!info->icon_window)
2746 switch (site->icon_type)
2748 case GTK_IMAGE_EMPTY:
2749 gtk_drag_set_icon_default (context);
2751 case GTK_IMAGE_PIXMAP:
2752 gtk_drag_set_icon_pixmap (context,
2754 site->icon_data.pixmap.pixmap,
2758 case GTK_IMAGE_PIXBUF:
2759 gtk_drag_set_icon_pixbuf (context,
2760 site->icon_data.pixbuf.pixbuf,
2763 case GTK_IMAGE_STOCK:
2764 gtk_drag_set_icon_stock (context,
2765 site->icon_data.stock.stock_id,
2769 g_assert_not_reached();
2779 default: /* hit for 2/3BUTTON_PRESS */
2787 gtk_drag_source_site_destroy (gpointer data)
2789 GtkDragSourceSite *site = data;
2791 if (site->target_list)
2792 gtk_target_list_unref (site->target_list);
2794 gtk_drag_source_unset_icon (site);
2799 gtk_drag_selection_get (GtkWidget *widget,
2800 GtkSelectionData *selection_data,
2805 GtkDragSourceInfo *info = data;
2806 static GdkAtom null_atom = GDK_NONE;
2810 null_atom = gdk_atom_intern ("NULL", FALSE);
2815 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2818 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2820 case TARGET_MOTIF_SUCCESS:
2821 gtk_drag_drop_finished (info, TRUE, time);
2822 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2824 case TARGET_MOTIF_FAILURE:
2825 gtk_drag_drop_finished (info, FALSE, time);
2826 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2829 if (info->proxy_dest)
2831 /* This is sort of dangerous and needs to be thought
2834 info->proxy_dest->proxy_data = selection_data;
2835 gtk_drag_get_data (info->widget,
2836 info->proxy_dest->context,
2837 selection_data->target,
2840 info->proxy_dest->proxy_data = NULL;
2844 if (gtk_target_list_find (info->target_list,
2845 selection_data->target,
2848 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2860 gtk_drag_anim_timeout (gpointer data)
2862 GtkDragAnim *anim = data;
2866 GDK_THREADS_ENTER ();
2868 if (anim->step == anim->n_steps)
2870 gtk_drag_source_info_destroy (anim->info);
2877 x = (anim->info->start_x * (anim->step + 1) +
2878 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2879 y = (anim->info->start_y * (anim->step + 1) +
2880 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2881 if (anim->info->icon_window)
2882 gtk_widget_set_uposition (anim->info->icon_window,
2883 x - anim->info->hot_x,
2884 y - anim->info->hot_y);
2891 GDK_THREADS_LEAVE ();
2897 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2899 if (info->icon_window)
2901 gtk_widget_hide (info->icon_window);
2902 if (info->destroy_icon)
2903 gtk_widget_destroy (info->icon_window);
2905 gtk_widget_unref (info->icon_window);
2906 info->icon_window = NULL;
2911 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
2913 gtk_drag_remove_icon (info);
2915 if (!info->proxy_dest)
2916 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2920 gtk_widget_unref (info->widget);
2922 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2923 gtk_selection_remove_all (info->ipc_widget);
2924 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2925 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2926 gtk_drag_release_ipc_widget (info->ipc_widget);
2928 gtk_target_list_unref (info->target_list);
2930 gtk_drag_clear_source_info (info->context);
2931 gdk_drag_context_unref (info->context);
2933 if (info->drop_timeout)
2934 gtk_timeout_remove (info->drop_timeout);
2939 /*************************************************************
2941 * Function to update the status of the drag when the
2942 * cursor moves or the modifier changes
2944 * info: DragSourceInfo for the drag
2945 * x_root, y_root: position of darg
2946 * event: The event that triggered this call
2948 *************************************************************/
2951 gtk_drag_update (GtkDragSourceInfo *info,
2956 GdkDragAction action;
2957 GdkDragAction possible_actions;
2958 GdkWindow *window = NULL;
2959 GdkWindow *dest_window;
2960 GdkDragProtocol protocol;
2962 guint32 time = gtk_drag_get_event_time (event);
2964 gtk_drag_get_event_actions (event,
2966 info->possible_actions,
2967 &action, &possible_actions);
2968 info->cur_x = x_root;
2969 info->cur_y = y_root;
2971 if (info->icon_window)
2973 gdk_window_raise (info->icon_window->window);
2974 gtk_widget_set_uposition (info->icon_window,
2975 info->cur_x - info->hot_x,
2976 info->cur_y - info->hot_y);
2977 window = info->icon_window->window;
2980 gdk_drag_find_window (info->context,
2981 window, x_root, y_root,
2982 &dest_window, &protocol);
2984 if (gdk_drag_motion (info->context, dest_window, protocol,
2985 x_root, y_root, action,
2989 if (info->last_event != event) /* Paranoia, should not happen */
2991 if (info->last_event)
2992 gdk_event_free ((GdkEvent *)info->last_event);
2993 info->last_event = gdk_event_copy ((GdkEvent *)event);
2998 if (info->last_event)
3000 gdk_event_free ((GdkEvent *)info->last_event);
3001 info->last_event = NULL;
3006 gdk_window_unref (dest_window);
3008 selection = gdk_drag_get_selection (info->context);
3010 gtk_drag_source_check_selection (info, selection, time);
3013 /*************************************************************
3015 * Called when the user finishes to drag, either by
3016 * releasing the mouse, or by pressing Esc.
3018 * widget: GtkInvisible widget for this drag
3021 *************************************************************/
3024 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3026 GdkEvent send_event;
3027 GtkWidget *source_widget = info->widget;
3028 GdkDisplay *display = gtk_widget_get_display (source_widget);
3030 info->have_grab = FALSE;
3032 gdk_display_pointer_ungrab (display, time);
3033 gdk_display_keyboard_ungrab (display, time);
3034 gtk_grab_remove (info->ipc_widget);
3036 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3037 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
3039 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3040 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
3042 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3043 GTK_SIGNAL_FUNC (gtk_drag_key_cb),
3046 /* Send on a release pair to the the original
3047 * widget to convince it to release its grab. We need to
3048 * call gtk_propagate_event() here, instead of
3049 * gtk_widget_event() because widget like GtkList may
3050 * expect propagation.
3053 send_event.button.type = GDK_BUTTON_RELEASE;
3054 send_event.button.window = gtk_widget_get_root_window (source_widget);
3055 send_event.button.send_event = TRUE;
3056 send_event.button.time = time;
3057 send_event.button.x = 0;
3058 send_event.button.y = 0;
3059 send_event.button.axes = NULL;
3060 send_event.button.state = 0;
3061 send_event.button.button = info->button;
3062 send_event.button.device = gdk_device_get_core_pointer ();
3063 send_event.button.x_root = 0;
3064 send_event.button.y_root = 0;
3066 gtk_propagate_event (source_widget, &send_event);
3069 /*************************************************************
3070 * gtk_drag_motion_cb:
3071 * "motion_notify_event" callback during drag.
3075 *************************************************************/
3078 gtk_drag_motion_cb (GtkWidget *widget,
3079 GdkEventMotion *event,
3082 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3083 gint x_root, y_root;
3087 GdkWindow *root_window = gtk_widget_get_root_window (widget);
3089 gdk_window_get_pointer (root_window, &x_root, &y_root, NULL);
3090 event->x_root = x_root;
3091 event->y_root = y_root;
3094 gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
3099 /*************************************************************
3101 * "key_press/release_event" callback during drag.
3105 *************************************************************/
3108 gtk_drag_key_cb (GtkWidget *widget,
3112 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3113 GdkModifierType state;
3114 GdkWindow *root_window;
3116 if (event->type == GDK_KEY_PRESS)
3118 if (event->keyval == GDK_Escape)
3120 gtk_drag_end (info, event->time);
3121 gdk_drag_abort (info->context, event->time);
3122 gtk_drag_drop_finished (info, FALSE, event->time);
3128 /* Now send a "motion" so that the modifier state is updated */
3130 /* The state is not yet updated in the event, so we need
3131 * to query it here. We could use XGetModifierMapping, but
3132 * that would be overkill.
3134 root_window = gtk_widget_get_root_window (widget);
3135 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3137 event->state = state;
3138 gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
3143 /*************************************************************
3144 * gtk_drag_button_release_cb:
3145 * "button_release_event" callback during drag.
3149 *************************************************************/
3152 gtk_drag_button_release_cb (GtkWidget *widget,
3153 GdkEventButton *event,
3156 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3158 if (event->button != info->button)
3161 gtk_drag_end (info, event->time);
3163 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3165 gtk_drag_drop (info, event->time);
3169 gdk_drag_abort (info->context, event->time);
3170 gtk_drag_drop_finished (info, FALSE, event->time);
3177 gtk_drag_abort_timeout (gpointer data)
3179 GtkDragSourceInfo *info = data;
3180 guint32 time = GDK_CURRENT_TIME;
3182 GDK_THREADS_ENTER ();
3184 if (info->proxy_dest)
3185 time = info->proxy_dest->proxy_drop_time;
3187 info->drop_timeout = 0;
3188 gtk_drag_drop_finished (info, FALSE, time);
3190 GDK_THREADS_LEAVE ();
3196 * gtk_drag_check_threshold:
3197 * @widget: a #GtkWidget
3198 * @start_x: X coordinate of start of drag
3199 * @start_y: Y coordinate of start of drag
3200 * @current_x: current X coordinate
3201 * @current_y: current Y coordinate
3203 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3204 * at (@current_x, @current_y) has passed the GTK+ drag threshhold, and thus
3205 * should trigger the beginning of a drag-and-drop operation.
3207 * Return Value: %TRUE if the drag threshold has been passed.
3210 gtk_drag_check_threshold (GtkWidget *widget,
3216 gint drag_threshold;
3218 g_object_get (gtk_widget_get_settings (widget),
3219 "gtk-dnd-drag-threshold", &drag_threshold,
3222 return (ABS (current_x - start_x) > drag_threshold ||
3223 ABS (current_y - start_y) > drag_threshold);