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 GList *selections; /* selections we've claimed */
99 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
101 guint drop_timeout; /* Timeout for aborting drop */
102 guint destroy_icon : 1; /* If true, destroy icon_window
104 guint have_grab : 1; /* Do we still have the pointer grab
108 struct _GtkDragDestSite
110 GtkDestDefaults flags;
111 GtkTargetList *target_list;
112 GdkDragAction actions;
113 GdkWindow *proxy_window;
114 GdkDragProtocol proxy_protocol;
115 gboolean do_proxy : 1;
116 gboolean proxy_coords : 1;
117 gboolean have_drag : 1;
120 struct _GtkDragDestInfo
122 GtkWidget *widget; /* Widget in which drag is in */
123 GdkDragContext *context; /* Drag context */
124 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
125 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
126 gboolean dropped : 1; /* Set after we receive a drop */
127 guint32 proxy_drop_time; /* Timestamp for proxied drop */
128 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
129 * status reply before sending
132 gint drop_x, drop_y; /* Position of drop */
135 #define DROP_ABORT_TIME 300000
137 #define ANIM_STEP_TIME 50
138 #define ANIM_STEP_LENGTH 50
139 #define ANIM_MIN_STEPS 5
140 #define ANIM_MAX_STEPS 10
144 GtkDragSourceInfo *info;
149 struct _GtkDragFindData
153 GdkDragContext *context;
154 GtkDragDestInfo *info;
157 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
158 gint x, gint y, guint32 time);
162 /* Enumeration for some targets we handle internally */
165 TARGET_MOTIF_SUCCESS = 0x40000000,
166 TARGET_MOTIF_FAILURE,
172 static GdkPixmap *default_icon_pixmap = NULL;
173 static GdkPixmap *default_icon_mask = NULL;
174 static GdkColormap *default_icon_colormap = NULL;
175 static gint default_icon_hot_x;
176 static gint default_icon_hot_y;
178 /* Forward declarations */
179 static void gtk_drag_get_event_actions (GdkEvent *event,
181 GdkDragAction actions,
182 GdkDragAction *suggested_action,
183 GdkDragAction *possible_actions);
184 static GdkCursor * gtk_drag_get_cursor (GdkScreen *screen,
185 GdkDragAction action);
186 static GtkWidget *gtk_drag_get_ipc_widget (GdkScreen *screen);
187 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
189 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
190 GdkEventExpose *event,
193 static void gtk_drag_selection_received (GtkWidget *widget,
194 GtkSelectionData *selection_data,
197 static void gtk_drag_find_widget (GtkWidget *widget,
198 GtkDragFindData *data);
199 static void gtk_drag_proxy_begin (GtkWidget *widget,
200 GtkDragDestInfo *dest_info,
202 static void gtk_drag_dest_realized (GtkWidget *widget);
203 static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
204 GtkWidget *previous_toplevel);
205 static void gtk_drag_dest_site_destroy (gpointer data);
206 static void gtk_drag_dest_leave (GtkWidget *widget,
207 GdkDragContext *context,
209 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
210 GdkDragContext *context,
214 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
215 GdkDragContext *context,
220 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
222 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
224 static void gtk_drag_clear_source_info (GdkDragContext *context);
226 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
229 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
231 static void gtk_drag_drop (GtkDragSourceInfo *info,
233 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
237 static gint gtk_drag_source_event_cb (GtkWidget *widget,
240 static void gtk_drag_source_site_destroy (gpointer data);
241 static void gtk_drag_selection_get (GtkWidget *widget,
242 GtkSelectionData *selection_data,
246 static gint gtk_drag_anim_timeout (gpointer data);
247 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
248 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
249 static void gtk_drag_update (GtkDragSourceInfo *info,
253 static gint gtk_drag_motion_cb (GtkWidget *widget,
254 GdkEventMotion *event,
256 static gint gtk_drag_key_cb (GtkWidget *widget,
259 static gint gtk_drag_button_release_cb (GtkWidget *widget,
260 GdkEventButton *event,
262 static gint gtk_drag_abort_timeout (gpointer data);
264 /************************
265 * Cursor and Icon data *
266 ************************/
268 #define action_ask_width 16
269 #define action_ask_height 16
270 static const guchar action_ask_bits[] = {
271 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
272 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
273 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
275 #define action_ask_mask_width 16
276 #define action_ask_mask_height 16
277 static const guchar action_ask_mask_bits[] = {
278 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
279 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
280 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
282 #define action_copy_width 16
283 #define action_copy_height 16
284 static const guchar action_copy_bits[] = {
285 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
286 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
287 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
289 #define action_copy_mask_width 16
290 #define action_copy_mask_height 16
291 static const guchar action_copy_mask_bits[] = {
292 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
293 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
294 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
296 #define action_move_width 16
297 #define action_move_height 16
298 static const guchar action_move_bits[] = {
299 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
300 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
301 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
303 #define action_move_mask_width 16
304 #define action_move_mask_height 16
305 static const guchar action_move_mask_bits[] = {
306 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
307 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
308 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
310 #define action_link_width 16
311 #define action_link_height 16
312 static const guchar action_link_bits[] = {
313 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
314 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
315 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
317 #define action_link_mask_width 16
318 #define action_link_mask_height 16
319 static const guchar action_link_mask_bits[] = {
320 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
321 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
322 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
324 #define action_none_width 16
325 #define action_none_height 16
326 static const guchar action_none_bits[] = {
327 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
328 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
329 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
331 #define action_none_mask_width 16
332 #define action_none_mask_height 16
333 static const guchar action_none_mask_bits[] = {
334 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
335 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
336 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
338 #define CURSOR_WIDTH 16
339 #define CURSOR_HEIGHT 16
342 GdkDragAction action;
347 { GDK_ACTION_DEFAULT, 0 },
348 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
349 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
350 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
351 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
352 { 0 , action_none_bits, action_none_mask_bits, NULL },
355 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
357 /*********************
358 * Utility functions *
359 *********************/
361 /*************************************************************
362 * gtk_drag_get_ipc_widget:
363 * Return a invisible, off-screen, override-redirect
368 *************************************************************/
371 gtk_drag_get_ipc_widget (GdkScreen *screen)
374 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
375 "gtk-dnd-ipc-widgets");
379 GSList *tmp = drag_widgets;
380 result = drag_widgets->data;
381 drag_widgets = drag_widgets->next;
382 g_object_set_data (G_OBJECT (screen),
383 "gtk-dnd-ipc-widgets",
385 g_slist_free_1 (tmp);
389 result = gtk_invisible_new_for_screen (screen);
390 gtk_widget_show (result);
396 /***************************************************************
397 * gtk_drag_release_ipc_widget:
398 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
400 * widget: the widget to release.
402 ***************************************************************/
405 gtk_drag_release_ipc_widget (GtkWidget *widget)
407 GdkScreen *screen = gtk_widget_get_screen (widget);
408 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
409 "gtk-dnd-ipc-widgets");
410 drag_widgets = g_slist_prepend (drag_widgets, widget);
411 g_object_set_data (G_OBJECT (screen),
412 "gtk-dnd-ipc-widgets",
417 gtk_drag_get_event_time (GdkEvent *event)
419 guint32 tm = GDK_CURRENT_TIME;
424 case GDK_MOTION_NOTIFY:
425 tm = event->motion.time; break;
426 case GDK_BUTTON_PRESS:
427 case GDK_2BUTTON_PRESS:
428 case GDK_3BUTTON_PRESS:
429 case GDK_BUTTON_RELEASE:
430 tm = event->button.time; break;
432 case GDK_KEY_RELEASE:
433 tm = event->key.time; break;
434 case GDK_ENTER_NOTIFY:
435 case GDK_LEAVE_NOTIFY:
436 tm = event->crossing.time; break;
437 case GDK_PROPERTY_NOTIFY:
438 tm = event->property.time; break;
439 case GDK_SELECTION_CLEAR:
440 case GDK_SELECTION_REQUEST:
441 case GDK_SELECTION_NOTIFY:
442 tm = event->selection.time; break;
443 case GDK_PROXIMITY_IN:
444 case GDK_PROXIMITY_OUT:
445 tm = event->proximity.time; break;
446 default: /* use current time */
454 gtk_drag_get_event_actions (GdkEvent *event,
456 GdkDragAction actions,
457 GdkDragAction *suggested_action,
458 GdkDragAction *possible_actions)
460 *suggested_action = 0;
461 *possible_actions = 0;
465 GdkModifierType state = 0;
469 case GDK_MOTION_NOTIFY:
470 state = event->motion.state;
472 case GDK_BUTTON_PRESS:
473 case GDK_2BUTTON_PRESS:
474 case GDK_3BUTTON_PRESS:
475 case GDK_BUTTON_RELEASE:
476 state = event->button.state;
479 case GDK_KEY_RELEASE:
480 state = event->key.state;
482 case GDK_ENTER_NOTIFY:
483 case GDK_LEAVE_NOTIFY:
484 state = event->crossing.state;
490 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
492 *suggested_action = GDK_ACTION_ASK;
493 *possible_actions = actions;
495 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
497 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
499 if (actions & GDK_ACTION_LINK)
501 *suggested_action = GDK_ACTION_LINK;
502 *possible_actions = GDK_ACTION_LINK;
505 else if (state & GDK_CONTROL_MASK)
507 if (actions & GDK_ACTION_COPY)
509 *suggested_action = GDK_ACTION_COPY;
510 *possible_actions = GDK_ACTION_COPY;
516 if (actions & GDK_ACTION_MOVE)
518 *suggested_action = GDK_ACTION_MOVE;
519 *possible_actions = GDK_ACTION_MOVE;
526 *possible_actions = actions;
528 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
529 *suggested_action = GDK_ACTION_ASK;
530 else if (actions & GDK_ACTION_COPY)
531 *suggested_action = GDK_ACTION_COPY;
532 else if (actions & GDK_ACTION_MOVE)
533 *suggested_action = GDK_ACTION_MOVE;
534 else if (actions & GDK_ACTION_LINK)
535 *suggested_action = GDK_ACTION_LINK;
540 *possible_actions = actions;
542 if (actions & GDK_ACTION_COPY)
543 *suggested_action = GDK_ACTION_COPY;
544 else if (actions & GDK_ACTION_MOVE)
545 *suggested_action = GDK_ACTION_MOVE;
546 else if (actions & GDK_ACTION_LINK)
547 *suggested_action = GDK_ACTION_LINK;
554 gtk_drag_get_cursor (GdkScreen *screen,
555 GdkDragAction action)
559 for (i = 0 ; i < n_drag_cursors - 1; i++)
560 if (drag_cursors[i].action == action)
562 if (drag_cursors[i].cursor != NULL)
564 if (screen != gdk_cursor_get_screen (drag_cursors[i].cursor))
566 gdk_cursor_unref (drag_cursors[i].cursor);
567 drag_cursors[i].cursor = NULL;
571 if (drag_cursors[i].cursor == NULL)
573 GdkColormap *colormap;
577 gdk_bitmap_create_from_data (gdk_screen_get_root_window (screen),
578 drag_cursors[i].bits, CURSOR_WIDTH, CURSOR_HEIGHT);
581 gdk_bitmap_create_from_data (gdk_screen_get_root_window (screen),
582 drag_cursors[i].mask, CURSOR_WIDTH, CURSOR_HEIGHT);
584 colormap = gdk_screen_get_system_colormap (screen);
585 gdk_color_white (colormap, &bg);
586 gdk_color_black (colormap, &fg);
588 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
590 gdk_pixmap_unref (pixmap);
591 gdk_pixmap_unref (mask);
594 return drag_cursors[i].cursor;
597 /********************
599 ********************/
601 /*************************************************************
603 * Get the data for a drag or drop
605 * context - drag context
606 * target - format to retrieve the data in.
607 * time - timestamp of triggering event.
610 *************************************************************/
613 gtk_drag_get_data (GtkWidget *widget,
614 GdkDragContext *context,
618 GtkWidget *selection_widget;
620 g_return_if_fail (widget != NULL);
621 g_return_if_fail (context != NULL);
623 selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
625 gdk_drag_context_ref (context);
626 gtk_widget_ref (widget);
628 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
629 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
631 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
633 gtk_selection_convert (selection_widget,
634 gdk_drag_get_selection (context),
640 /*************************************************************
641 * gtk_drag_get_source_widget:
642 * Get the widget the was the source of this drag, if
643 * the drag originated from this application.
645 * context: The drag context for this drag
647 * The source widget, or NULL if the drag originated from
648 * a different application.
649 *************************************************************/
652 gtk_drag_get_source_widget (GdkDragContext *context)
656 tmp_list = source_widgets;
659 GtkWidget *ipc_widget = tmp_list->data;
661 if (ipc_widget->window == context->source_window)
663 GtkDragSourceInfo *info;
664 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
666 return info ? info->widget : NULL;
669 tmp_list = tmp_list->next;
675 /*************************************************************
677 * Notify the drag source that the transfer of data
680 * context: The drag context for this drag
681 * success: Was the data successfully transferred?
682 * time: The timestamp to use when notifying the destination.
684 *************************************************************/
687 gtk_drag_finish (GdkDragContext *context,
692 GdkAtom target = GDK_NONE;
694 g_return_if_fail (context != NULL);
698 target = gdk_atom_intern ("DELETE", FALSE);
700 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
702 target = gdk_atom_intern (success ?
703 "XmTRANSFER_SUCCESS" :
704 "XmTRANSFER_FAILURE",
708 if (target != GDK_NONE)
710 GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
712 gdk_drag_context_ref (context);
714 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
715 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
716 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
719 gtk_selection_convert (selection_widget,
720 gdk_drag_get_selection (context),
725 if (!(success && del))
726 gdk_drop_finish (context, success, time);
729 /*************************************************************
730 * gtk_drag_highlight_expose:
731 * Callback for expose_event for highlighted widgets.
737 *************************************************************/
740 gtk_drag_highlight_expose (GtkWidget *widget,
741 GdkEventExpose *event,
744 gint x, y, width, height;
746 if (GTK_WIDGET_DRAWABLE (widget))
748 if (GTK_WIDGET_NO_WINDOW (widget))
750 x = widget->allocation.x;
751 y = widget->allocation.y;
752 width = widget->allocation.width;
753 height = widget->allocation.height;
759 gdk_window_get_size (widget->window, &width, &height);
762 gtk_draw_shadow (widget->style, widget->window,
763 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
764 x, y, width, height);
766 gdk_draw_rectangle (widget->window,
767 widget->style->black_gc,
769 x, y, width - 1, height - 1);
775 /*************************************************************
776 * gtk_drag_highlight:
777 * Highlight the given widget in the default manner.
781 *************************************************************/
784 gtk_drag_highlight (GtkWidget *widget)
786 gtk_signal_connect_after (GTK_OBJECT (widget), "expose_event",
787 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
790 gtk_widget_queue_draw (widget);
793 /*************************************************************
794 * gtk_drag_unhighlight:
795 * Refresh the given widget to remove the highlight.
799 *************************************************************/
802 gtk_drag_unhighlight (GtkWidget *widget)
804 g_return_if_fail (widget != NULL);
806 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
807 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
810 gtk_widget_queue_clear (widget);
814 gtk_drag_dest_set_internal (GtkWidget *widget,
815 GtkDragDestSite *site)
817 GtkDragDestSite *old_site;
819 g_return_if_fail (widget != NULL);
821 /* HACK, do this in the destroy */
822 old_site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
824 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), old_site);
826 if (GTK_WIDGET_REALIZED (widget))
827 gtk_drag_dest_realized (widget);
829 gtk_signal_connect (GTK_OBJECT (widget), "realize",
830 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), site);
831 gtk_signal_connect (GTK_OBJECT (widget), "hierarchy_changed",
832 GTK_SIGNAL_FUNC (gtk_drag_dest_hierarchy_changed), site);
834 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
835 site, gtk_drag_dest_site_destroy);
839 /*************************************************************
841 * Register a drop site, and possibly add default behaviors.
844 * flags: Which types of default drag behavior to use
845 * targets: Table of targets that can be accepted
846 * n_targets: Number of of entries in targets
849 *************************************************************/
852 gtk_drag_dest_set (GtkWidget *widget,
853 GtkDestDefaults flags,
854 const GtkTargetEntry *targets,
856 GdkDragAction actions)
858 GtkDragDestSite *site;
860 g_return_if_fail (widget != NULL);
862 site = g_new (GtkDragDestSite, 1);
865 site->have_drag = FALSE;
867 site->target_list = gtk_target_list_new (targets, n_targets);
869 site->target_list = NULL;
871 site->actions = actions;
872 site->do_proxy = FALSE;
874 gtk_drag_dest_set_internal (widget, site);
877 /*************************************************************
878 * gtk_drag_dest_set_proxy:
879 * Set up this widget to proxy drags elsewhere.
882 * proxy_window: window to which forward drag events
883 * protocol: Drag protocol which the dest widget accepts
884 * use_coordinates: If true, send the same coordinates to the
885 * destination, because it is a embedded
888 *************************************************************/
891 gtk_drag_dest_set_proxy (GtkWidget *widget,
892 GdkWindow *proxy_window,
893 GdkDragProtocol protocol,
894 gboolean use_coordinates)
896 GtkDragDestSite *site;
898 g_return_if_fail (widget != NULL);
900 site = g_new (GtkDragDestSite, 1);
903 site->have_drag = FALSE;
904 site->target_list = NULL;
906 site->proxy_window = proxy_window;
908 gdk_window_ref (proxy_window);
909 site->do_proxy = TRUE;
910 site->proxy_protocol = protocol;
911 site->proxy_coords = use_coordinates;
913 gtk_drag_dest_set_internal (widget, site);
916 /*************************************************************
917 * gtk_drag_dest_unset
918 * Unregister this widget as a drag target.
922 *************************************************************/
925 gtk_drag_dest_unset (GtkWidget *widget)
927 g_return_if_fail (widget != NULL);
929 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
933 * gtk_drag_dest_get_target_list:
934 * @widget: a #GtkWidget
936 * Returns the list of targets this widget can accept from
939 * Return value: the #GtkTargetList, or %NULL if none
942 gtk_drag_dest_get_target_list (GtkWidget *widget)
944 GtkDragDestSite *site;
946 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
948 return site ? site->target_list : NULL;
952 * gtk_drag_dest_set_target_list:
953 * @widget: a #GtkWidget that's a drag destination
954 * @target_list: list of droppable targets, or %NULL for none
956 * Sets the target types that this widget can accept from drag-and-drop.
957 * The widget must first be made into a drag destination with
958 * gtk_drag_dest_set().
961 gtk_drag_dest_set_target_list (GtkWidget *widget,
962 GtkTargetList *target_list)
964 GtkDragDestSite *site;
966 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
970 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");
975 gtk_target_list_ref (target_list);
977 if (site->target_list)
978 gtk_target_list_unref (site->target_list);
980 site->target_list = target_list;
984 /*************************************************************
985 * _gtk_drag_dest_handle_event:
986 * Called from widget event handling code on Drag events
990 * toplevel: Toplevel widget that received the event
993 *************************************************************/
996 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
999 GtkDragDestInfo *info;
1000 GdkDragContext *context;
1002 g_return_if_fail (toplevel != NULL);
1003 g_return_if_fail (event != NULL);
1005 context = event->dnd.context;
1007 info = gtk_drag_get_dest_info (context, TRUE);
1009 /* Find the widget for the event */
1010 switch (event->type)
1012 case GDK_DRAG_ENTER:
1015 case GDK_DRAG_LEAVE:
1018 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1019 info->widget = NULL;
1023 case GDK_DRAG_MOTION:
1024 case GDK_DROP_START:
1026 GtkDragFindData data;
1029 if (event->type == GDK_DROP_START)
1031 info->dropped = TRUE;
1032 /* We send a leave here so that the widget unhighlights
1037 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1038 info->widget = NULL;
1042 gdk_window_get_origin (toplevel->window, &tx, &ty);
1044 data.x = event->dnd.x_root - tx;
1045 data.y = event->dnd.y_root - ty;
1046 data.context = context;
1049 data.toplevel = TRUE;
1050 data.callback = (event->type == GDK_DRAG_MOTION) ?
1051 gtk_drag_dest_motion : gtk_drag_dest_drop;
1052 data.time = event->dnd.time;
1054 gtk_drag_find_widget (toplevel, &data);
1056 if (info->widget && !data.found)
1058 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1059 info->widget = NULL;
1064 if (event->type == GDK_DRAG_MOTION)
1067 gdk_drag_status (context, 0, event->dnd.time);
1069 else if (event->type == GDK_DROP_START && !info->proxy_source)
1071 gdk_drop_reply (context, data.found, event->dnd.time);
1072 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1073 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1079 g_assert_not_reached ();
1084 * gtk_drag_dest_find_target:
1085 * @widget: drag destination widget
1086 * @context: drag context
1087 * @target_list: list of droppable targets, or %NULL to use
1088 * gtk_drag_dest_get_target_list (@widget).
1090 * Looks for a match between @context->targets and the
1091 * @dest_target_list, returning the first matching target, otherwise
1092 * returning %GDK_NONE. @dest_target_list should usually be the return
1093 * value from gtk_drag_dest_get_target_list(), but some widgets may
1094 * have different valid targets for different parts of the widget; in
1095 * that case, they will have to implement a drag_motion handler that
1096 * passes the correct target list to this function.
1098 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1101 gtk_drag_dest_find_target (GtkWidget *widget,
1102 GdkDragContext *context,
1103 GtkTargetList *target_list)
1106 GList *tmp_source = NULL;
1107 GtkWidget *source_widget;
1109 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1110 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1112 source_widget = gtk_drag_get_source_widget (context);
1114 if (target_list == NULL)
1115 target_list = gtk_drag_dest_get_target_list (widget);
1117 if (target_list == NULL)
1120 tmp_target = target_list->list;
1123 GtkTargetPair *pair = tmp_target->data;
1124 tmp_source = context->targets;
1127 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1129 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1130 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1131 return pair->target;
1135 tmp_source = tmp_source->next;
1137 tmp_target = tmp_target->next;
1144 gtk_drag_selection_received (GtkWidget *widget,
1145 GtkSelectionData *selection_data,
1149 GdkDragContext *context;
1150 GtkDragDestInfo *info;
1151 GtkWidget *drop_widget;
1155 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1156 info = gtk_drag_get_dest_info (context, FALSE);
1158 if (info->proxy_data &&
1159 info->proxy_data->target == selection_data->target)
1161 gtk_selection_data_set (info->proxy_data,
1162 selection_data->type,
1163 selection_data->format,
1164 selection_data->data,
1165 selection_data->length);
1170 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1172 gtk_drag_finish (context, TRUE, FALSE, time);
1174 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1175 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1181 GtkDragDestSite *site;
1183 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1185 if (site && site->target_list)
1189 if (gtk_target_list_find (site->target_list,
1190 selection_data->target,
1193 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1194 selection_data->length >= 0)
1195 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1196 "drag_data_received",
1197 context, info->drop_x, info->drop_y,
1204 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1205 "drag_data_received",
1206 context, info->drop_x, info->drop_y,
1207 selection_data, 0, time);
1210 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1213 gtk_drag_finish (context,
1214 (selection_data->length >= 0),
1215 (context->action == GDK_ACTION_MOVE),
1219 gtk_widget_unref (drop_widget);
1222 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1223 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1226 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1227 gdk_drag_context_unref (context);
1229 gtk_drag_release_ipc_widget (widget);
1233 prepend_and_ref_widget (GtkWidget *widget,
1236 GSList **slist_p = data;
1238 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1241 /*************************************************************
1242 * gtk_drag_find_widget:
1243 * Recursive callback used to locate widgets for
1244 * DRAG_MOTION and DROP_START events.
1248 *************************************************************/
1251 gtk_drag_find_widget (GtkWidget *widget,
1252 GtkDragFindData *data)
1254 GtkAllocation new_allocation;
1255 gint allocation_to_window_x = 0;
1256 gint allocation_to_window_y = 0;
1260 if (data->found || !GTK_WIDGET_MAPPED (widget))
1263 /* Note that in the following code, we only count the
1264 * position as being inside a WINDOW widget if it is inside
1265 * widget->window; points that are outside of widget->window
1266 * but within the allocation are not counted. This is consistent
1267 * with the way we highlight drag targets.
1269 * data->x,y are relative to widget->parent->window (if
1270 * widget is not a toplevel, widget->window otherwise).
1271 * We compute the allocation of widget in the same coordinates,
1272 * clipping to widget->window, and all intermediate
1273 * windows. If data->x,y is inside that, then we translate
1274 * our coordinates to be relative to widget->window and
1277 new_allocation = widget->allocation;
1282 GdkWindow *window = widget->window;
1284 /* Compute the offset from allocation-relative to
1285 * window-relative coordinates.
1287 allocation_to_window_x = widget->allocation.x;
1288 allocation_to_window_y = widget->allocation.y;
1290 if (!GTK_WIDGET_NO_WINDOW (widget))
1292 /* The allocation is relative to the parent window for
1293 * window widgets, not to widget->window.
1295 gdk_window_get_position (window, &tx, &ty);
1297 allocation_to_window_x -= tx;
1298 allocation_to_window_y -= ty;
1301 new_allocation.x = 0 + allocation_to_window_x;
1302 new_allocation.y = 0 + allocation_to_window_y;
1304 while (window && window != widget->parent->window)
1306 GdkRectangle window_rect = { 0, 0, 0, 0 };
1308 gdk_window_get_size (window, &window_rect.width, &window_rect.height);
1310 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1312 gdk_window_get_position (window, &tx, &ty);
1313 new_allocation.x += tx;
1315 new_allocation.y += ty;
1318 window = gdk_window_get_parent (window);
1321 if (!window) /* Window and widget heirarchies didn't match. */
1325 if (data->toplevel ||
1326 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1327 (data->x < new_allocation.x + new_allocation.width) &&
1328 (data->y < new_allocation.y + new_allocation.height)))
1330 /* First, check if the drag is in a valid drop site in
1331 * one of our children
1333 if (GTK_IS_CONTAINER (widget))
1335 GtkDragFindData new_data = *data;
1336 GSList *children = NULL;
1339 new_data.x -= x_offset;
1340 new_data.y -= y_offset;
1341 new_data.found = FALSE;
1342 new_data.toplevel = FALSE;
1344 /* need to reference children temporarily in case the
1345 * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1347 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1348 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1350 if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1351 gtk_drag_find_widget (tmp_list->data, &new_data);
1352 gtk_widget_unref (tmp_list->data);
1354 g_slist_free (children);
1356 data->found = new_data.found;
1359 /* If not, and this widget is registered as a drop site, check to
1360 * emit "drag_motion" to check if we are actually in
1364 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1366 data->found = data->callback (widget,
1368 data->x - x_offset - allocation_to_window_x,
1369 data->y - y_offset - allocation_to_window_y,
1371 /* If so, send a "drag_leave" to the last widget */
1374 if (data->info->widget && data->info->widget != widget)
1376 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1378 data->info->widget = widget;
1385 gtk_drag_proxy_begin (GtkWidget *widget,
1386 GtkDragDestInfo *dest_info,
1389 GtkDragSourceInfo *source_info;
1391 GdkDragContext *context;
1392 GtkWidget *ipc_widget;
1394 if (dest_info->proxy_source)
1396 gdk_drag_abort (dest_info->proxy_source->context, time);
1397 gtk_drag_source_info_destroy (dest_info->proxy_source);
1398 dest_info->proxy_source = NULL;
1401 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1402 context = gdk_drag_begin (ipc_widget->window,
1403 dest_info->context->targets);
1405 source_info = gtk_drag_get_source_info (context, TRUE);
1407 source_info->ipc_widget = ipc_widget;
1408 source_info->widget = gtk_widget_ref (widget);
1410 source_info->target_list = gtk_target_list_new (NULL, 0);
1411 tmp_list = dest_info->context->targets;
1414 gtk_target_list_add (source_info->target_list,
1415 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1416 tmp_list = tmp_list->next;
1419 source_info->proxy_dest = dest_info;
1421 gtk_signal_connect (GTK_OBJECT (ipc_widget),
1423 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1426 dest_info->proxy_source = source_info;
1430 gtk_drag_dest_info_destroy (gpointer data)
1432 GtkDragDestInfo *info = data;
1437 static GtkDragDestInfo *
1438 gtk_drag_get_dest_info (GdkDragContext *context,
1441 GtkDragDestInfo *info;
1442 static GQuark info_quark = 0;
1444 info_quark = g_quark_from_static_string ("gtk-dest-info");
1446 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1447 if (!info && create)
1449 info = g_new (GtkDragDestInfo, 1);
1450 info->widget = NULL;
1451 info->context = context;
1452 info->proxy_source = NULL;
1453 info->proxy_data = NULL;
1454 info->dropped = FALSE;
1455 info->proxy_drop_wait = FALSE;
1456 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1457 info, gtk_drag_dest_info_destroy);
1463 static GQuark dest_info_quark = 0;
1465 static GtkDragSourceInfo *
1466 gtk_drag_get_source_info (GdkDragContext *context,
1469 GtkDragSourceInfo *info;
1470 if (!dest_info_quark)
1471 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1473 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1474 if (!info && create)
1476 info = g_new0 (GtkDragSourceInfo, 1);
1477 info->context = context;
1478 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1485 gtk_drag_clear_source_info (GdkDragContext *context)
1487 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1491 gtk_drag_dest_realized (GtkWidget *widget)
1493 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1495 if (GTK_WIDGET_TOPLEVEL (toplevel))
1496 gdk_window_register_dnd (toplevel->window);
1500 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1501 GtkWidget *previous_toplevel)
1503 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1505 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1506 gdk_window_register_dnd (toplevel->window);
1510 gtk_drag_dest_site_destroy (gpointer data)
1512 GtkDragDestSite *site = data;
1514 if (site->proxy_window)
1515 g_object_unref (site->proxy_window);
1517 if (site->target_list)
1518 gtk_target_list_unref (site->target_list);
1524 * Default drag handlers
1527 gtk_drag_dest_leave (GtkWidget *widget,
1528 GdkDragContext *context,
1531 GtkDragDestSite *site;
1533 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1534 g_return_if_fail (site != NULL);
1538 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1540 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1542 gdk_drag_abort (info->proxy_source->context, time);
1543 gtk_drag_source_info_destroy (info->proxy_source);
1544 info->proxy_source = NULL;
1551 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1552 gtk_drag_unhighlight (widget);
1554 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1555 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1558 site->have_drag = FALSE;
1563 gtk_drag_dest_motion (GtkWidget *widget,
1564 GdkDragContext *context,
1569 GtkDragDestSite *site;
1570 GdkDragAction action = 0;
1573 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1574 g_return_val_if_fail (site != NULL, FALSE);
1579 GdkEvent *current_event;
1580 GdkWindow *dest_window;
1581 GdkDragProtocol proto;
1583 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1585 if (!info->proxy_source || info->proxy_source->widget != widget)
1586 gtk_drag_proxy_begin (widget, info, time);
1588 current_event = gtk_get_current_event ();
1590 if (site->proxy_window)
1592 dest_window = site->proxy_window;
1593 proto = site->proxy_protocol;
1597 gdk_drag_find_window (info->proxy_source->context,
1599 current_event->dnd.x_root,
1600 current_event->dnd.y_root,
1601 &dest_window, &proto);
1604 gdk_drag_motion (info->proxy_source->context,
1606 current_event->dnd.x_root,
1607 current_event->dnd.y_root,
1608 context->suggested_action,
1609 context->actions, time);
1611 if (!site->proxy_window && dest_window)
1612 gdk_window_unref (dest_window);
1614 selection = gdk_drag_get_selection (info->proxy_source->context);
1616 selection != gdk_drag_get_selection (info->context))
1617 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1619 gdk_event_free (current_event);
1624 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1626 if (context->suggested_action & site->actions)
1627 action = context->suggested_action;
1634 if ((site->actions & (1 << i)) &&
1635 (context->actions & (1 << i)))
1643 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1645 if (!site->have_drag)
1647 site->have_drag = TRUE;
1648 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1649 gtk_drag_highlight (widget);
1652 gdk_drag_status (context, action, time);
1656 gdk_drag_status (context, 0, time);
1661 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1662 context, x, y, time, &retval);
1664 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1668 gtk_drag_dest_drop (GtkWidget *widget,
1669 GdkDragContext *context,
1674 GtkDragDestSite *site;
1675 GtkDragDestInfo *info;
1677 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1678 g_return_val_if_fail (site != NULL, FALSE);
1680 info = gtk_drag_get_dest_info (context, FALSE);
1681 g_return_val_if_fail (info != NULL, FALSE);
1688 if (info->proxy_source ||
1689 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1691 gtk_drag_drop (info->proxy_source, time);
1695 /* We need to synthesize a motion event, wait for a status,
1696 * and, if we get a good one, do a drop.
1699 GdkEvent *current_event;
1701 GdkWindow *dest_window;
1702 GdkDragProtocol proto;
1704 gtk_drag_proxy_begin (widget, info, time);
1705 info->proxy_drop_wait = TRUE;
1706 info->proxy_drop_time = time;
1708 current_event = gtk_get_current_event ();
1710 if (site->proxy_window)
1712 dest_window = site->proxy_window;
1713 proto = site->proxy_protocol;
1717 gdk_drag_find_window (info->proxy_source->context,
1719 current_event->dnd.x_root,
1720 current_event->dnd.y_root,
1721 &dest_window, &proto);
1724 gdk_drag_motion (info->proxy_source->context,
1726 current_event->dnd.x_root,
1727 current_event->dnd.y_root,
1728 context->suggested_action,
1729 context->actions, time);
1731 if (!site->proxy_window && dest_window)
1732 gdk_window_unref (dest_window);
1734 selection = gdk_drag_get_selection (info->proxy_source->context);
1736 selection != gdk_drag_get_selection (info->context))
1737 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1739 gdk_event_free (current_event);
1748 if (site->flags & GTK_DEST_DEFAULT_DROP)
1750 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1752 if (target == GDK_NONE)
1754 gtk_drag_finish (context, FALSE, FALSE, time);
1758 gtk_drag_get_data (widget, context, target, time);
1761 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1762 context, x, y, time, &retval);
1764 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1772 /*************************************************************
1773 * gtk_drag_begin: Start a drag operation
1776 * widget: Widget from which drag starts
1777 * handlers: List of handlers to supply the data for the drag
1778 * button: Button user used to start drag
1779 * time: Time of event starting drag
1782 *************************************************************/
1785 gtk_drag_begin (GtkWidget *widget,
1786 GtkTargetList *target_list,
1787 GdkDragAction actions,
1791 GtkDragSourceInfo *info;
1792 GList *targets = NULL;
1794 guint32 time = GDK_CURRENT_TIME;
1795 GdkDragAction possible_actions, suggested_action;
1796 GdkDragContext *context;
1797 GtkWidget *ipc_widget;
1799 g_return_val_if_fail (widget != NULL, NULL);
1800 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1801 g_return_val_if_fail (target_list != NULL, NULL);
1804 time = gdk_event_get_time (event);
1806 tmp_list = g_list_last (target_list->list);
1809 GtkTargetPair *pair = tmp_list->data;
1810 targets = g_list_prepend (targets,
1811 GINT_TO_POINTER (pair->target));
1812 tmp_list = tmp_list->prev;
1815 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1816 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1818 context = gdk_drag_begin (ipc_widget->window, targets);
1819 g_list_free (targets);
1821 info = gtk_drag_get_source_info (context, TRUE);
1823 info->ipc_widget = ipc_widget;
1824 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1826 info->widget = gtk_widget_ref (widget);
1829 info->button = button;
1830 info->target_list = target_list;
1831 gtk_target_list_ref (target_list);
1833 info->possible_actions = actions;
1835 info->cursor = NULL;
1836 info->status = GTK_DRAG_STATUS_DRAG;
1837 info->last_event = NULL;
1838 info->selections = NULL;
1839 info->icon_window = NULL;
1840 info->destroy_icon = FALSE;
1842 gtk_drag_get_event_actions (event, info->button, actions,
1843 &suggested_action, &possible_actions);
1845 info->cursor = gtk_drag_get_cursor (gtk_widget_get_screen (widget), suggested_action);
1847 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1848 * the drag icon, it will be in the right place
1850 if (event && event->type == GDK_MOTION_NOTIFY)
1852 info->cur_x = event->motion.x_root;
1853 info->cur_y = event->motion.y_root;
1857 gdk_window_get_pointer (gtk_widget_get_root_window (widget),
1858 &info->cur_x, &info->cur_y, NULL);
1861 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1864 if (event && event->type == GDK_MOTION_NOTIFY)
1865 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1867 info->start_x = info->cur_x;
1868 info->start_y = info->cur_y;
1870 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1871 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1872 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1873 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1874 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
1875 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1876 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
1877 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1878 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1879 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1881 /* We use a GTK grab here to override any grabs that the widget
1882 * we are dragging from might have held
1884 gtk_grab_add (info->ipc_widget);
1885 if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1886 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1887 GDK_BUTTON_RELEASE_MASK, NULL,
1888 info->cursor, time) == 0)
1890 if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1892 /* FIXME: This should be cleaned up... */
1896 ev.type = GDK_BUTTON_RELEASE;
1897 ev.button = info->button;
1899 gtk_drag_button_release_cb (widget, &ev, info);
1905 info->have_grab = TRUE;
1907 return info->context;
1910 /*************************************************************
1911 * gtk_drag_source_set:
1912 * Register a drop site, and possibly add default behaviors.
1915 * start_button_mask: Mask of allowed buttons to start drag
1916 * targets: Table of targets for this source
1918 * actions: Actions allowed for this source
1920 *************************************************************/
1923 gtk_drag_source_set (GtkWidget *widget,
1924 GdkModifierType start_button_mask,
1925 const GtkTargetEntry *targets,
1927 GdkDragAction actions)
1929 GtkDragSourceSite *site;
1931 g_return_if_fail (widget != NULL);
1933 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1935 gtk_widget_add_events (widget,
1936 gtk_widget_get_events (widget) |
1937 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1938 GDK_BUTTON_MOTION_MASK);
1942 if (site->target_list)
1943 gtk_target_list_unref (site->target_list);
1947 site = g_new0 (GtkDragSourceSite, 1);
1949 site->icon_type = GTK_IMAGE_EMPTY;
1951 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1952 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1954 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1955 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1958 gtk_object_set_data_full (GTK_OBJECT (widget),
1960 site, gtk_drag_source_site_destroy);
1963 site->start_button_mask = start_button_mask;
1966 site->target_list = gtk_target_list_new (targets, n_targets);
1968 site->target_list = NULL;
1970 site->actions = actions;
1974 /*************************************************************
1975 * gtk_drag_source_unset
1976 * Unregister this widget as a drag source.
1980 *************************************************************/
1983 gtk_drag_source_unset (GtkWidget *widget)
1985 GtkDragSourceSite *site;
1987 g_return_if_fail (widget != NULL);
1989 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1993 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1994 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1999 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2001 switch (site->icon_type)
2003 case GTK_IMAGE_EMPTY:
2005 case GTK_IMAGE_PIXMAP:
2006 if (site->icon_data.pixmap.pixmap)
2007 gdk_pixmap_unref (site->icon_data.pixmap.pixmap);
2008 if (site->icon_mask)
2009 gdk_pixmap_unref (site->icon_mask);
2011 case GTK_IMAGE_PIXBUF:
2012 g_object_unref (G_OBJECT (site->icon_data.pixbuf.pixbuf));
2014 case GTK_IMAGE_STOCK:
2015 g_free (G_OBJECT (site->icon_data.stock.stock_id));
2018 g_assert_not_reached();
2021 site->icon_type = GTK_IMAGE_EMPTY;
2024 gdk_colormap_unref (site->colormap);
2025 site->colormap = NULL;
2029 * gtk_drag_source_set_icon:
2030 * @widget: a #GtkWidget
2031 * @colormap: the colormap of the icon
2032 * @pixmap: the image data for the icon
2033 * @mask: the transparency mask for an image.
2035 * Sets the icon that will be used for drags from a particular widget
2036 * from a pixmap/mask. GTK+ retains references for the arguments, and
2037 * will release them when they are no longer needed.
2038 * Use gtk_drag_source_set_icon_pixbuf() instead.
2041 gtk_drag_source_set_icon (GtkWidget *widget,
2042 GdkColormap *colormap,
2046 GtkDragSourceSite *site;
2048 g_return_if_fail (widget != NULL);
2049 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2050 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2051 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2053 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2054 g_return_if_fail (site != NULL);
2056 gdk_colormap_ref (colormap);
2057 gdk_pixmap_ref (pixmap);
2059 gdk_pixmap_ref (mask);
2061 gtk_drag_source_unset_icon (site);
2063 site->icon_type = GTK_IMAGE_PIXMAP;
2065 site->icon_data.pixmap.pixmap = pixmap;
2066 site->icon_mask = mask;
2067 site->colormap = colormap;
2071 * gtk_drag_source_set_icon_pixbuf:
2072 * @widget: a #GtkWidget
2073 * @pixbuf: the #GdkPixbuf for the drag icon
2075 * Sets the icon that will be used for drags from a particular widget
2076 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2077 * release it when it is no longer needed.
2080 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2083 GtkDragSourceSite *site;
2085 g_return_if_fail (widget != NULL);
2086 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2088 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2089 g_return_if_fail (site != NULL);
2090 gdk_pixbuf_ref (pixbuf);
2092 gtk_drag_source_unset_icon (site);
2094 site->icon_type = GTK_IMAGE_PIXBUF;
2095 site->icon_data.pixbuf.pixbuf = pixbuf;
2099 * gtk_drag_source_set_icon_stock:
2100 * @widget: a #GtkWidget
2101 * @stock_id: the ID of the stock icon to use
2103 * Sets the icon that will be used for drags from a particular source
2107 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2108 const gchar *stock_id)
2110 GtkDragSourceSite *site;
2112 g_return_if_fail (widget != NULL);
2113 g_return_if_fail (stock_id != NULL);
2115 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2116 g_return_if_fail (site != NULL);
2118 gtk_drag_source_unset_icon (site);
2120 site->icon_data.stock.stock_id = g_strdup (stock_id);
2124 gtk_drag_set_icon_window (GdkDragContext *context,
2128 gboolean destroy_on_release)
2130 GtkDragSourceInfo *info;
2132 g_return_if_fail (context != NULL);
2133 g_return_if_fail (widget != NULL);
2135 info = gtk_drag_get_source_info (context, FALSE);
2136 gtk_drag_remove_icon (info);
2138 info->icon_window = widget;
2139 info->hot_x = hot_x;
2140 info->hot_y = hot_y;
2144 gtk_widget_set_uposition (widget,
2145 info->cur_x - info->hot_x,
2146 info->cur_y - info->hot_y);
2147 gtk_widget_ref (widget);
2148 gdk_window_raise (widget->window);
2149 gtk_widget_show (widget);
2152 info->destroy_icon = destroy_on_release;
2156 * gtk_drag_set_icon_widget:
2157 * @context: the context for a drag. (This must be called
2158 with a context for the source side of a drag)
2159 * @widget: a toplevel window to use as an icon.
2160 * @hot_x: the X offset within @widget of the hotspot.
2161 * @hot_y: the Y offset within @widget of the hotspot.
2163 * Changes the icon for a widget to a given widget. GTK+
2164 * will not destroy the icon, so if you don't want
2165 * it to persist, you should connect to the "drag_end"
2166 * signal and destroy it yourself.
2169 gtk_drag_set_icon_widget (GdkDragContext *context,
2174 g_return_if_fail (context != NULL);
2175 g_return_if_fail (widget != NULL);
2177 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2181 set_icon_stock_pixbuf (GdkDragContext *context,
2182 const gchar *stock_id,
2192 GdkColormap *colormap;
2194 g_return_if_fail (context != NULL);
2195 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2196 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2198 screen = gdk_drawable_get_screen (context->source_window);
2199 colormap = gdk_screen_get_rgb_colormap (screen);
2201 gtk_widget_push_colormap (colormap);
2202 window = gtk_window_new (GTK_WINDOW_POPUP);
2203 gtk_window_set_screen (GTK_WINDOW (window), screen);
2204 gtk_widget_pop_colormap ();
2206 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2207 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2211 pixbuf = gtk_widget_render_icon (window, stock_id,
2212 GTK_ICON_SIZE_DND, NULL);
2216 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2217 gtk_widget_destroy (window);
2223 width = gdk_pixbuf_get_width (pixbuf);
2224 height = gdk_pixbuf_get_width (pixbuf);
2226 gtk_widget_set_usize (window,
2227 gdk_pixbuf_get_width (pixbuf),
2228 gdk_pixbuf_get_height (pixbuf));
2229 gtk_widget_realize (window);
2231 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf, colormap, &pixmap, &mask, 128);
2233 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2236 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2238 g_object_unref (G_OBJECT (pixmap));
2241 g_object_unref (G_OBJECT (mask));
2243 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2247 * gtk_drag_set_icon_pixbuf:
2248 * @context: the context for a drag. (This must be called
2249 * with a context for the source side of a drag)
2250 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2251 * @hot_x: the X offset within @widget of the hotspot.
2252 * @hot_y: the Y offset within @widget of the hotspot.
2254 * Sets @pixbuf as the icon for a given drag.
2257 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2262 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2263 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2265 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2269 * gtk_drag_set_icon_stock:
2270 * @context: the context for a drag. (This must be called
2271 * with a context for the source side of a drag)
2272 * @stock_id: the ID of the stock icon to use for the drag.
2273 * @hot_x: the X offset within the icon of the hotspot.
2274 * @hot_y: the Y offset within the icon of the hotspot.
2276 * Sets the the icon for a given drag from a stock ID.
2279 gtk_drag_set_icon_stock (GdkDragContext *context,
2280 const gchar *stock_id,
2284 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2285 g_return_if_fail (stock_id != NULL);
2287 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2291 * gtk_drag_set_icon_pixmap:
2292 * @context: the context for a drag. (This must be called
2293 * with a context for the source side of a drag)
2294 * @colormap: the colormap of the icon
2295 * @pixmap: the image data for the icon
2296 * @mask: the transparency mask for the icon
2297 * @hot_x: the X offset within @pixmap of the hotspot.
2298 * @hot_y: the Y offset within @pixmap of the hotspot.
2300 * Sets @pixmap as the icon for a given drag. GTK+ retains
2301 * references for the arguments, and will release them when
2302 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2303 * will be more convenient to use.
2306 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2307 GdkColormap *colormap,
2316 g_return_if_fail (context != NULL);
2317 g_return_if_fail (colormap != NULL);
2318 g_return_if_fail (pixmap != NULL);
2320 gdk_window_get_size (pixmap, &width, &height);
2322 gtk_widget_push_colormap (colormap);
2324 window = gtk_window_new (GTK_WINDOW_POPUP);
2325 gtk_window_set_screen (GTK_WINDOW (window), gdk_drawable_get_screen (context->source_window));
2326 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2327 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2329 gtk_widget_pop_colormap ();
2331 gtk_widget_set_usize (window, width, height);
2332 gtk_widget_realize (window);
2334 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2337 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2339 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2343 * gtk_drag_set_icon_default:
2344 * @context: the context for a drag. (This must be called
2345 with a context for the source side of a drag)
2347 * Sets the icon for a particular drag to the default
2351 gtk_drag_set_icon_default (GdkDragContext *context)
2353 g_return_if_fail (context != NULL);
2355 if (!default_icon_pixmap)
2356 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2358 gtk_drag_set_icon_pixmap (context,
2359 default_icon_colormap,
2360 default_icon_pixmap,
2363 default_icon_hot_y);
2367 * gtk_drag_set_default_icon:
2368 * @colormap: the colormap of the icon
2369 * @pixmap: the image data for the icon
2370 * @mask: the transparency mask for an image.
2371 * @hot_x: The X offset within @widget of the hotspot.
2372 * @hot_y: The Y offset within @widget of the hotspot.
2374 * Changes the default drag icon. GTK+ retains references for the
2375 * arguments, and will release them when they are no longer needed.
2376 * This function is obsolete. The default icon should now be changed
2377 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2380 gtk_drag_set_default_icon (GdkColormap *colormap,
2386 g_return_if_fail (colormap != NULL);
2387 g_return_if_fail (pixmap != NULL);
2389 if (default_icon_colormap)
2390 gdk_colormap_unref (default_icon_colormap);
2391 if (default_icon_pixmap)
2392 gdk_pixmap_unref (default_icon_pixmap);
2393 if (default_icon_mask)
2394 gdk_pixmap_unref (default_icon_mask);
2396 default_icon_colormap = colormap;
2397 gdk_colormap_ref (colormap);
2399 default_icon_pixmap = pixmap;
2400 gdk_pixmap_ref (pixmap);
2402 default_icon_mask = mask;
2404 gdk_pixmap_ref (mask);
2406 default_icon_hot_x = hot_x;
2407 default_icon_hot_y = hot_y;
2411 /*************************************************************
2412 * _gtk_drag_source_handle_event:
2413 * Called from widget event handling code on Drag events
2417 * toplevel: Toplevel widget that received the event
2420 *************************************************************/
2423 _gtk_drag_source_handle_event (GtkWidget *widget,
2426 GtkDragSourceInfo *info;
2427 GdkDragContext *context;
2429 g_return_if_fail (widget != NULL);
2430 g_return_if_fail (event != NULL);
2432 context = event->dnd.context;
2433 info = gtk_drag_get_source_info (context, FALSE);
2437 switch (event->type)
2439 case GDK_DRAG_STATUS:
2443 if (info->proxy_dest)
2445 if (!event->dnd.send_event)
2447 if (info->proxy_dest->proxy_drop_wait)
2449 gboolean result = context->action != 0;
2451 /* Aha - we can finally pass the MOTIF DROP on... */
2452 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2454 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2456 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2460 gdk_drag_status (info->proxy_dest->context,
2461 event->dnd.context->action,
2466 else if (info->have_grab)
2468 cursor = gtk_drag_get_cursor (gtk_widget_get_screen (widget),
2469 event->dnd.context->action);
2470 if (info->cursor != cursor)
2472 gdk_pointer_grab (widget->window, FALSE,
2473 GDK_POINTER_MOTION_MASK |
2474 GDK_POINTER_MOTION_HINT_MASK |
2475 GDK_BUTTON_RELEASE_MASK,
2477 cursor, event->dnd.time);
2478 info->cursor = cursor;
2481 if (info->last_event)
2483 gtk_drag_update (info,
2484 info->cur_x, info->cur_y,
2486 info->last_event = NULL;
2492 case GDK_DROP_FINISHED:
2493 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2496 g_assert_not_reached ();
2500 /*************************************************************
2501 * gtk_drag_source_check_selection:
2502 * Check if we've set up handlers/claimed the selection
2503 * for a given drag. If not, add them.
2507 *************************************************************/
2510 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2516 tmp_list = info->selections;
2519 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2521 tmp_list = tmp_list->next;
2524 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2528 info->selections = g_list_prepend (info->selections,
2529 GUINT_TO_POINTER (selection));
2531 tmp_list = info->target_list->list;
2534 GtkTargetPair *pair = tmp_list->data;
2536 gtk_selection_add_target (info->ipc_widget,
2540 tmp_list = tmp_list->next;
2543 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2545 gtk_selection_add_target (info->ipc_widget,
2547 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2548 TARGET_MOTIF_SUCCESS);
2549 gtk_selection_add_target (info->ipc_widget,
2551 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2552 TARGET_MOTIF_FAILURE);
2555 gtk_selection_add_target (info->ipc_widget,
2557 gdk_atom_intern ("DELETE", FALSE),
2561 /*************************************************************
2562 * gtk_drag_drop_finished:
2563 * Clean up from the drag, and display snapback, if necessary.
2569 *************************************************************/
2572 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2576 gtk_drag_source_release_selections (info, time);
2578 if (info->proxy_dest)
2580 /* The time from the event isn't reliable for Xdnd drags */
2581 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2582 info->proxy_dest->proxy_drop_time);
2583 gtk_drag_source_info_destroy (info);
2589 gtk_drag_source_info_destroy (info);
2593 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2597 anim->n_steps = MAX (info->cur_x - info->start_x,
2598 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2599 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2600 if (info->icon_window)
2602 gtk_widget_show (info->icon_window);
2603 gdk_window_raise (info->icon_window->window);
2606 /* Mark the context as dead, so if the destination decides
2607 * to respond really late, we still are OK.
2609 gtk_drag_clear_source_info (info->context);
2610 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2616 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2619 GdkDisplay *display = gtk_widget_get_display (info->widget);
2620 GList *tmp_list = info->selections;
2624 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2625 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2626 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2628 tmp_list = tmp_list->next;
2631 g_list_free (info->selections);
2632 info->selections = NULL;
2635 /*************************************************************
2637 * Send a drop event.
2641 *************************************************************/
2644 gtk_drag_drop (GtkDragSourceInfo *info,
2647 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2649 GtkSelectionData selection_data;
2651 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2653 tmp_list = info->target_list->list;
2656 GtkTargetPair *pair = tmp_list->data;
2658 if (pair->target == target)
2660 selection_data.selection = GDK_NONE;
2661 selection_data.target = target;
2662 selection_data.data = NULL;
2663 selection_data.length = -1;
2665 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2666 info->context, &selection_data,
2670 /* FIXME: Should we check for length >= 0 here? */
2671 gtk_drag_drop_finished (info, TRUE, time);
2674 tmp_list = tmp_list->next;
2676 gtk_drag_drop_finished (info, FALSE, time);
2680 if (info->icon_window)
2681 gtk_widget_hide (info->icon_window);
2683 gdk_drag_drop (info->context, time);
2684 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2685 gtk_drag_abort_timeout,
2691 * Source side callbacks.
2695 gtk_drag_source_event_cb (GtkWidget *widget,
2699 GtkDragSourceSite *site;
2700 gboolean retval = FALSE;
2701 site = (GtkDragSourceSite *)data;
2703 switch (event->type)
2705 case GDK_BUTTON_PRESS:
2706 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2708 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2709 site->x = event->button.x;
2710 site->y = event->button.y;
2714 case GDK_BUTTON_RELEASE:
2715 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2716 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2719 case GDK_MOTION_NOTIFY:
2720 if (site->state & event->motion.state & site->start_button_mask)
2722 /* FIXME: This is really broken and can leave us
2728 if (site->state & event->motion.state &
2729 GDK_BUTTON1_MASK << (i - 1))
2733 if (gtk_drag_check_threshold (widget, site->x, site->y,
2734 event->motion.x, event->motion.y))
2736 GtkDragSourceInfo *info;
2737 GdkDragContext *context;
2740 context = gtk_drag_begin (widget, site->target_list,
2744 info = gtk_drag_get_source_info (context, FALSE);
2746 if (!info->icon_window)
2748 switch (site->icon_type)
2750 case GTK_IMAGE_EMPTY:
2751 gtk_drag_set_icon_default (context);
2753 case GTK_IMAGE_PIXMAP:
2754 gtk_drag_set_icon_pixmap (context,
2756 site->icon_data.pixmap.pixmap,
2760 case GTK_IMAGE_PIXBUF:
2761 gtk_drag_set_icon_pixbuf (context,
2762 site->icon_data.pixbuf.pixbuf,
2765 case GTK_IMAGE_STOCK:
2766 gtk_drag_set_icon_stock (context,
2767 site->icon_data.stock.stock_id,
2771 g_assert_not_reached();
2781 default: /* hit for 2/3BUTTON_PRESS */
2789 gtk_drag_source_site_destroy (gpointer data)
2791 GtkDragSourceSite *site = data;
2793 if (site->target_list)
2794 gtk_target_list_unref (site->target_list);
2796 gtk_drag_source_unset_icon (site);
2801 gtk_drag_selection_get (GtkWidget *widget,
2802 GtkSelectionData *selection_data,
2807 GtkDragSourceInfo *info = data;
2808 static GdkAtom null_atom = GDK_NONE;
2812 null_atom = gdk_atom_intern ("NULL", FALSE);
2817 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2820 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2822 case TARGET_MOTIF_SUCCESS:
2823 gtk_drag_drop_finished (info, TRUE, time);
2824 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2826 case TARGET_MOTIF_FAILURE:
2827 gtk_drag_drop_finished (info, FALSE, time);
2828 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2831 if (info->proxy_dest)
2833 /* This is sort of dangerous and needs to be thought
2836 info->proxy_dest->proxy_data = selection_data;
2837 gtk_drag_get_data (info->widget,
2838 info->proxy_dest->context,
2839 selection_data->target,
2842 info->proxy_dest->proxy_data = NULL;
2846 if (gtk_target_list_find (info->target_list,
2847 selection_data->target,
2850 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2862 gtk_drag_anim_timeout (gpointer data)
2864 GtkDragAnim *anim = data;
2868 GDK_THREADS_ENTER ();
2870 if (anim->step == anim->n_steps)
2872 gtk_drag_source_info_destroy (anim->info);
2879 x = (anim->info->start_x * (anim->step + 1) +
2880 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2881 y = (anim->info->start_y * (anim->step + 1) +
2882 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2883 if (anim->info->icon_window)
2884 gtk_widget_set_uposition (anim->info->icon_window,
2885 x - anim->info->hot_x,
2886 y - anim->info->hot_y);
2893 GDK_THREADS_LEAVE ();
2899 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2901 if (info->icon_window)
2903 gtk_widget_hide (info->icon_window);
2904 if (info->destroy_icon)
2905 gtk_widget_destroy (info->icon_window);
2907 gtk_widget_unref (info->icon_window);
2908 info->icon_window = NULL;
2913 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
2915 gtk_drag_remove_icon (info);
2917 if (!info->proxy_dest)
2918 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2922 gtk_widget_unref (info->widget);
2924 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2925 gtk_selection_remove_all (info->ipc_widget);
2926 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2927 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2928 gtk_drag_release_ipc_widget (info->ipc_widget);
2930 gtk_target_list_unref (info->target_list);
2932 gtk_drag_clear_source_info (info->context);
2933 gdk_drag_context_unref (info->context);
2935 if (info->drop_timeout)
2936 gtk_timeout_remove (info->drop_timeout);
2941 /*************************************************************
2943 * Function to update the status of the drag when the
2944 * cursor moves or the modifier changes
2946 * info: DragSourceInfo for the drag
2947 * x_root, y_root: position of darg
2948 * event: The event that triggered this call
2950 *************************************************************/
2953 gtk_drag_update (GtkDragSourceInfo *info,
2958 GdkDragAction action;
2959 GdkDragAction possible_actions;
2960 GdkWindow *window = NULL;
2961 GdkWindow *dest_window;
2962 GdkDragProtocol protocol;
2964 guint32 time = gtk_drag_get_event_time (event);
2966 gtk_drag_get_event_actions (event,
2968 info->possible_actions,
2969 &action, &possible_actions);
2970 info->cur_x = x_root;
2971 info->cur_y = y_root;
2973 if (info->icon_window)
2975 gdk_window_raise (info->icon_window->window);
2976 gtk_widget_set_uposition (info->icon_window,
2977 info->cur_x - info->hot_x,
2978 info->cur_y - info->hot_y);
2979 window = info->icon_window->window;
2982 gdk_drag_find_window (info->context,
2983 window, x_root, y_root,
2984 &dest_window, &protocol);
2986 if (gdk_drag_motion (info->context, dest_window, protocol,
2987 x_root, y_root, action,
2991 if (info->last_event != event) /* Paranoia, should not happen */
2993 if (info->last_event)
2994 gdk_event_free ((GdkEvent *)info->last_event);
2995 info->last_event = gdk_event_copy ((GdkEvent *)event);
3000 if (info->last_event)
3002 gdk_event_free ((GdkEvent *)info->last_event);
3003 info->last_event = NULL;
3008 gdk_window_unref (dest_window);
3010 selection = gdk_drag_get_selection (info->context);
3012 gtk_drag_source_check_selection (info, selection, time);
3015 /*************************************************************
3017 * Called when the user finishes to drag, either by
3018 * releasing the mouse, or by pressing Esc.
3020 * widget: GtkInvisible widget for this drag
3023 *************************************************************/
3026 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3028 GdkEvent send_event;
3029 GtkWidget *source_widget = info->widget;
3030 GdkDisplay *display = gtk_widget_get_display (source_widget);
3032 info->have_grab = FALSE;
3034 gdk_display_pointer_ungrab (display, time);
3035 gdk_display_keyboard_ungrab (display, time);
3036 gtk_grab_remove (info->ipc_widget);
3038 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3039 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
3041 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3042 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
3045 /* Send on a release pair to the the original
3046 * widget to convince it to release its grab. We need to
3047 * call gtk_propagate_event() here, instead of
3048 * gtk_widget_event() because widget like GtkList may
3049 * expect propagation.
3052 send_event.button.type = GDK_BUTTON_RELEASE;
3053 send_event.button.window = gtk_widget_get_root_window (source_widget);
3054 send_event.button.send_event = TRUE;
3055 send_event.button.time = time;
3056 send_event.button.x = 0;
3057 send_event.button.y = 0;
3058 send_event.button.axes = NULL;
3059 send_event.button.state = 0;
3060 send_event.button.button = info->button;
3061 send_event.button.device = gdk_device_get_core_pointer ();
3062 send_event.button.x_root = 0;
3063 send_event.button.y_root = 0;
3065 gtk_propagate_event (source_widget, &send_event);
3068 /*************************************************************
3069 * gtk_drag_motion_cb:
3070 * "motion_notify_event" callback during drag.
3074 *************************************************************/
3077 gtk_drag_motion_cb (GtkWidget *widget,
3078 GdkEventMotion *event,
3081 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3082 gint x_root, y_root;
3086 GdkWindow *root_window = gtk_widget_get_root_window (widget);
3088 gdk_window_get_pointer (root_window, &x_root, &y_root, NULL);
3089 event->x_root = x_root;
3090 event->y_root = y_root;
3093 gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
3098 /*************************************************************
3100 * "key_press/release_event" callback during drag.
3104 *************************************************************/
3107 gtk_drag_key_cb (GtkWidget *widget,
3111 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3112 GdkModifierType state;
3113 GdkWindow *root_window;
3115 if (event->type == GDK_KEY_PRESS)
3117 if (event->keyval == GDK_Escape)
3119 gtk_drag_end (info, event->time);
3120 gdk_drag_abort (info->context, event->time);
3121 gtk_drag_drop_finished (info, FALSE, event->time);
3127 /* Now send a "motion" so that the modifier state is updated */
3129 /* The state is not yet updated in the event, so we need
3130 * to query it here. We could use XGetModifierMapping, but
3131 * that would be overkill.
3133 root_window = gtk_widget_get_root_window (widget);
3134 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3136 event->state = state;
3137 gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
3142 /*************************************************************
3143 * gtk_drag_button_release_cb:
3144 * "button_release_event" callback during drag.
3148 *************************************************************/
3151 gtk_drag_button_release_cb (GtkWidget *widget,
3152 GdkEventButton *event,
3155 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3157 if (event->button != info->button)
3160 gtk_drag_end (info, event->time);
3162 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3164 gtk_drag_drop (info, event->time);
3168 gdk_drag_abort (info->context, event->time);
3169 gtk_drag_drop_finished (info, FALSE, event->time);
3176 gtk_drag_abort_timeout (gpointer data)
3178 GtkDragSourceInfo *info = data;
3179 guint32 time = GDK_CURRENT_TIME;
3181 GDK_THREADS_ENTER ();
3183 if (info->proxy_dest)
3184 time = info->proxy_dest->proxy_drop_time;
3186 info->drop_timeout = 0;
3187 gtk_drag_drop_finished (info, FALSE, time);
3189 GDK_THREADS_LEAVE ();
3195 * gtk_drag_check_threshold:
3196 * @widget: a #GtkWidget
3197 * @start_x: X coordinate of start of drag
3198 * @start_y: Y coordinate of start of drag
3199 * @current_x: current X coordinate
3200 * @current_y: current Y coordinate
3202 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3203 * at (@current_x, @current_y) has passed the GTK+ drag threshhold, and thus
3204 * should trigger the beginning of a drag-and-drop operation.
3206 * Return Value: %TRUE if the drag threshold has been passed.
3209 gtk_drag_check_threshold (GtkWidget *widget,
3215 gint drag_threshold;
3217 g_object_get (gtk_widget_get_settings (widget),
3218 "gtk-dnd-drag-threshold", &drag_threshold,
3221 return (ABS (current_x - start_x) > drag_threshold ||
3222 ABS (current_y - start_y) > drag_threshold);