1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27 #include "gdkconfig.h"
29 #include "gdk/gdkkeysyms.h"
33 #include "gtkinvisible.h"
36 #include "gtkwindow.h"
38 static GSList *source_widgets = NULL;
40 typedef struct _GtkDragSourceSite GtkDragSourceSite;
41 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
42 typedef struct _GtkDragDestSite GtkDragDestSite;
43 typedef struct _GtkDragDestInfo GtkDragDestInfo;
44 typedef struct _GtkDragAnim GtkDragAnim;
45 typedef struct _GtkDragFindData GtkDragFindData;
55 struct _GtkDragSourceSite
57 GdkModifierType start_button_mask;
58 GtkTargetList *target_list; /* Targets for drag data */
59 GdkDragAction actions; /* Possible actions */
62 GtkImageType icon_type;
65 GtkImagePixmapData pixmap;
66 GtkImagePixbufData pixbuf;
67 GtkImageStockData stock;
71 GdkColormap *colormap; /* Colormap for drag icon */
73 /* Stored button press information to detect drag beginning */
78 struct _GtkDragSourceInfo
81 GtkTargetList *target_list; /* Targets for drag data */
82 GdkDragAction possible_actions; /* Actions allowed by source */
83 GdkDragContext *context; /* drag context */
84 GtkWidget *icon_window; /* Window for drag */
85 GtkWidget *fallback_icon; /* Window for drag used on other screens */
86 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
87 GdkCursor *cursor; /* Cursor for drag */
88 gint hot_x, hot_y; /* Hot spot for drag */
89 gint button; /* mouse button starting drag */
91 GtkDragStatus status; /* drag status */
92 GdkEvent *last_event; /* pending event */
94 gint start_x, start_y; /* Initial position */
95 gint cur_x, cur_y; /* Current Position */
96 GdkScreen *cur_screen; /* Current screen for pointer */
98 guint32 grab_time; /* timestamp for initial grab */
99 GList *selections; /* selections we've claimed */
101 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
103 guint update_idle; /* Idle function to update the drag */
104 guint drop_timeout; /* Timeout for aborting drop */
105 guint destroy_icon : 1; /* If true, destroy icon_window
107 guint have_grab : 1; /* Do we still have the pointer grab
111 struct _GtkDragDestSite
113 GtkDestDefaults flags;
114 GtkTargetList *target_list;
115 GdkDragAction actions;
116 GdkWindow *proxy_window;
117 GdkDragProtocol proxy_protocol;
119 guint proxy_coords : 1;
123 struct _GtkDragDestInfo
125 GtkWidget *widget; /* Widget in which drag is in */
126 GdkDragContext *context; /* Drag context */
127 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
128 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
129 guint dropped : 1; /* Set after we receive a drop */
130 guint32 proxy_drop_time; /* Timestamp for proxied drop */
131 guint proxy_drop_wait : 1; /* Set if we are waiting for a
132 * status reply before sending
135 gint drop_x, drop_y; /* Position of drop */
138 #define DROP_ABORT_TIME 300000
140 #define ANIM_STEP_TIME 50
141 #define ANIM_STEP_LENGTH 50
142 #define ANIM_MIN_STEPS 5
143 #define ANIM_MAX_STEPS 10
147 GtkDragSourceInfo *info;
152 struct _GtkDragFindData
156 GdkDragContext *context;
157 GtkDragDestInfo *info;
160 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
161 gint x, gint y, guint32 time);
165 /* Enumeration for some targets we handle internally */
168 TARGET_MOTIF_SUCCESS = 0x40000000,
169 TARGET_MOTIF_FAILURE,
175 static GdkPixmap *default_icon_pixmap = NULL;
176 static GdkPixmap *default_icon_mask = NULL;
177 static GdkColormap *default_icon_colormap = NULL;
178 static gint default_icon_hot_x;
179 static gint default_icon_hot_y;
181 /* Forward declarations */
182 static void gtk_drag_get_event_actions (GdkEvent *event,
184 GdkDragAction actions,
185 GdkDragAction *suggested_action,
186 GdkDragAction *possible_actions);
187 static GdkCursor * gtk_drag_get_cursor (GdkDisplay *display,
188 GdkDragAction action);
189 static GtkWidget *gtk_drag_get_ipc_widget (GdkScreen *screen);
190 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
192 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
193 GdkEventExpose *event,
196 static void gtk_drag_selection_received (GtkWidget *widget,
197 GtkSelectionData *selection_data,
200 static void gtk_drag_find_widget (GtkWidget *widget,
201 GtkDragFindData *data);
202 static void gtk_drag_proxy_begin (GtkWidget *widget,
203 GtkDragDestInfo *dest_info,
205 static void gtk_drag_dest_realized (GtkWidget *widget);
206 static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
207 GtkWidget *previous_toplevel);
208 static void gtk_drag_dest_site_destroy (gpointer data);
209 static void gtk_drag_dest_leave (GtkWidget *widget,
210 GdkDragContext *context,
212 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
213 GdkDragContext *context,
217 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
218 GdkDragContext *context,
223 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
225 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
227 static void gtk_drag_clear_source_info (GdkDragContext *context);
229 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
232 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
234 static void gtk_drag_drop (GtkDragSourceInfo *info,
236 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
239 static void gtk_drag_cancel (GtkDragSourceInfo *info,
242 static gint gtk_drag_source_event_cb (GtkWidget *widget,
245 static void gtk_drag_source_site_destroy (gpointer data);
246 static void gtk_drag_selection_get (GtkWidget *widget,
247 GtkSelectionData *selection_data,
251 static gint gtk_drag_anim_timeout (gpointer data);
252 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
253 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
254 static void gtk_drag_add_update_idle (GtkDragSourceInfo *info);
256 static void gtk_drag_update (GtkDragSourceInfo *info,
261 static gint gtk_drag_motion_cb (GtkWidget *widget,
262 GdkEventMotion *event,
264 static gint gtk_drag_key_cb (GtkWidget *widget,
267 static gint gtk_drag_button_release_cb (GtkWidget *widget,
268 GdkEventButton *event,
270 static gint gtk_drag_abort_timeout (gpointer data);
272 /************************
273 * Cursor and Icon data *
274 ************************/
276 #define action_ask_width 16
277 #define action_ask_height 16
278 static const guchar action_ask_bits[] = {
279 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
280 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
281 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
283 #define action_ask_mask_width 16
284 #define action_ask_mask_height 16
285 static const guchar action_ask_mask_bits[] = {
286 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
287 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
288 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
290 #define action_copy_width 16
291 #define action_copy_height 16
292 static const guchar action_copy_bits[] = {
293 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
294 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
295 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
297 #define action_copy_mask_width 16
298 #define action_copy_mask_height 16
299 static const guchar action_copy_mask_bits[] = {
300 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
301 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
302 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
304 #define action_move_width 16
305 #define action_move_height 16
306 static const guchar action_move_bits[] = {
307 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
308 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
309 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
311 #define action_move_mask_width 16
312 #define action_move_mask_height 16
313 static const guchar action_move_mask_bits[] = {
314 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
315 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
316 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
318 #define action_link_width 16
319 #define action_link_height 16
320 static const guchar action_link_bits[] = {
321 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
322 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
323 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
325 #define action_link_mask_width 16
326 #define action_link_mask_height 16
327 static const guchar action_link_mask_bits[] = {
328 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
329 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
330 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
332 #define action_none_width 16
333 #define action_none_height 16
334 static const guchar action_none_bits[] = {
335 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
336 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
337 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
339 #define action_none_mask_width 16
340 #define action_none_mask_height 16
341 static const guchar action_none_mask_bits[] = {
342 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
343 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
344 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
346 #define CURSOR_WIDTH 16
347 #define CURSOR_HEIGHT 16
350 GdkDragAction action;
355 { GDK_ACTION_DEFAULT, 0 },
356 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
357 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
358 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
359 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
360 { 0 , action_none_bits, action_none_mask_bits, NULL },
363 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
365 /*********************
366 * Utility functions *
367 *********************/
370 set_can_change_screen (GtkWidget *widget,
371 gboolean can_change_screen)
373 can_change_screen = can_change_screen != FALSE;
375 g_object_set_data (G_OBJECT (widget), "gtk-dnd-can-change-screen",
376 GUINT_TO_POINTER (can_change_screen));
380 get_can_change_screen (GtkWidget *widget)
382 return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
386 /*************************************************************
387 * gtk_drag_get_ipc_widget:
388 * Return a invisible, off-screen, override-redirect
393 *************************************************************/
396 gtk_drag_get_ipc_widget (GdkScreen *screen)
399 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
400 "gtk-dnd-ipc-widgets");
404 GSList *tmp = drag_widgets;
405 result = drag_widgets->data;
406 drag_widgets = drag_widgets->next;
407 g_object_set_data (G_OBJECT (screen),
408 "gtk-dnd-ipc-widgets",
410 g_slist_free_1 (tmp);
414 result = gtk_invisible_new_for_screen (screen);
415 gtk_widget_show (result);
421 /***************************************************************
422 * gtk_drag_release_ipc_widget:
423 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
425 * widget: the widget to release.
427 ***************************************************************/
430 gtk_drag_release_ipc_widget (GtkWidget *widget)
432 GdkScreen *screen = gtk_widget_get_screen (widget);
433 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
434 "gtk-dnd-ipc-widgets");
435 drag_widgets = g_slist_prepend (drag_widgets, widget);
436 g_object_set_data (G_OBJECT (screen),
437 "gtk-dnd-ipc-widgets",
442 gtk_drag_get_event_time (GdkEvent *event)
444 guint32 tm = GDK_CURRENT_TIME;
449 case GDK_MOTION_NOTIFY:
450 tm = event->motion.time; break;
451 case GDK_BUTTON_PRESS:
452 case GDK_2BUTTON_PRESS:
453 case GDK_3BUTTON_PRESS:
454 case GDK_BUTTON_RELEASE:
455 tm = event->button.time; break;
457 case GDK_KEY_RELEASE:
458 tm = event->key.time; break;
459 case GDK_ENTER_NOTIFY:
460 case GDK_LEAVE_NOTIFY:
461 tm = event->crossing.time; break;
462 case GDK_PROPERTY_NOTIFY:
463 tm = event->property.time; break;
464 case GDK_SELECTION_CLEAR:
465 case GDK_SELECTION_REQUEST:
466 case GDK_SELECTION_NOTIFY:
467 tm = event->selection.time; break;
468 case GDK_PROXIMITY_IN:
469 case GDK_PROXIMITY_OUT:
470 tm = event->proximity.time; break;
471 default: /* use current time */
479 gtk_drag_get_event_actions (GdkEvent *event,
481 GdkDragAction actions,
482 GdkDragAction *suggested_action,
483 GdkDragAction *possible_actions)
485 *suggested_action = 0;
486 *possible_actions = 0;
490 GdkModifierType state = 0;
494 case GDK_MOTION_NOTIFY:
495 state = event->motion.state;
497 case GDK_BUTTON_PRESS:
498 case GDK_2BUTTON_PRESS:
499 case GDK_3BUTTON_PRESS:
500 case GDK_BUTTON_RELEASE:
501 state = event->button.state;
504 case GDK_KEY_RELEASE:
505 state = event->key.state;
507 case GDK_ENTER_NOTIFY:
508 case GDK_LEAVE_NOTIFY:
509 state = event->crossing.state;
515 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
517 *suggested_action = GDK_ACTION_ASK;
518 *possible_actions = actions;
520 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
522 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
524 if (actions & GDK_ACTION_LINK)
526 *suggested_action = GDK_ACTION_LINK;
527 *possible_actions = GDK_ACTION_LINK;
530 else if (state & GDK_CONTROL_MASK)
532 if (actions & GDK_ACTION_COPY)
534 *suggested_action = GDK_ACTION_COPY;
535 *possible_actions = GDK_ACTION_COPY;
541 if (actions & GDK_ACTION_MOVE)
543 *suggested_action = GDK_ACTION_MOVE;
544 *possible_actions = GDK_ACTION_MOVE;
551 *possible_actions = actions;
553 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
554 *suggested_action = GDK_ACTION_ASK;
555 else if (actions & GDK_ACTION_COPY)
556 *suggested_action = GDK_ACTION_COPY;
557 else if (actions & GDK_ACTION_MOVE)
558 *suggested_action = GDK_ACTION_MOVE;
559 else if (actions & GDK_ACTION_LINK)
560 *suggested_action = GDK_ACTION_LINK;
565 *possible_actions = actions;
567 if (actions & GDK_ACTION_COPY)
568 *suggested_action = GDK_ACTION_COPY;
569 else if (actions & GDK_ACTION_MOVE)
570 *suggested_action = GDK_ACTION_MOVE;
571 else if (actions & GDK_ACTION_LINK)
572 *suggested_action = GDK_ACTION_LINK;
579 gtk_drag_get_cursor (GdkDisplay *display,
580 GdkDragAction action)
584 for (i = 0 ; i < n_drag_cursors - 1; i++)
585 if (drag_cursors[i].action == action)
587 if (drag_cursors[i].cursor != NULL)
589 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
591 gdk_cursor_unref (drag_cursors[i].cursor);
592 drag_cursors[i].cursor = NULL;
596 if (drag_cursors[i].cursor == NULL)
598 GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
599 GdkColor fg = { 0, 0x0000, 0x0000, 0x0000 };
600 GdkScreen *screen = gdk_display_get_default_screen (display);
601 GdkWindow *window = gdk_screen_get_root_window (screen);
604 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].bits, CURSOR_WIDTH, CURSOR_HEIGHT);
607 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].mask, CURSOR_WIDTH, CURSOR_HEIGHT);
609 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
611 g_object_unref (pixmap);
612 g_object_unref (mask);
615 return drag_cursors[i].cursor;
618 /********************
620 ********************/
622 /*************************************************************
624 * Get the data for a drag or drop
626 * context - drag context
627 * target - format to retrieve the data in.
628 * time - timestamp of triggering event.
631 *************************************************************/
634 gtk_drag_get_data (GtkWidget *widget,
635 GdkDragContext *context,
639 GtkWidget *selection_widget;
641 g_return_if_fail (GTK_IS_WIDGET (widget));
642 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
643 g_return_if_fail (!context->is_source);
645 selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
647 g_object_ref (context);
648 g_object_ref (widget);
650 g_signal_connect (selection_widget, "selection_received",
651 G_CALLBACK (gtk_drag_selection_received), widget);
653 g_object_set_data (G_OBJECT (selection_widget), "drag-context", context);
655 gtk_selection_convert (selection_widget,
656 gdk_drag_get_selection (context),
662 /*************************************************************
663 * gtk_drag_get_source_widget:
664 * Get the widget the was the source of this drag, if
665 * the drag originated from this application.
667 * context: The drag context for this drag
669 * The source widget, or NULL if the drag originated from
670 * a different application.
671 *************************************************************/
674 gtk_drag_get_source_widget (GdkDragContext *context)
678 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
679 g_return_val_if_fail (!context->is_source, NULL);
681 tmp_list = source_widgets;
684 GtkWidget *ipc_widget = tmp_list->data;
686 if (ipc_widget->window == context->source_window)
688 GtkDragSourceInfo *info;
689 info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
691 return info ? info->widget : NULL;
694 tmp_list = tmp_list->next;
700 /*************************************************************
702 * Notify the drag source that the transfer of data
705 * context: The drag context for this drag
706 * success: Was the data successfully transferred?
707 * time: The timestamp to use when notifying the destination.
709 *************************************************************/
712 gtk_drag_finish (GdkDragContext *context,
717 GdkAtom target = GDK_NONE;
719 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
720 g_return_if_fail (!context->is_source);
724 target = gdk_atom_intern ("DELETE", FALSE);
726 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
728 target = gdk_atom_intern (success ?
729 "XmTRANSFER_SUCCESS" :
730 "XmTRANSFER_FAILURE",
734 if (target != GDK_NONE)
736 GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
738 g_object_ref (context);
740 g_object_set_data (G_OBJECT (selection_widget), "drag-context", context);
741 g_signal_connect (selection_widget, "selection_received",
742 G_CALLBACK (gtk_drag_selection_received),
745 gtk_selection_convert (selection_widget,
746 gdk_drag_get_selection (context),
751 if (!(success && del))
752 gdk_drop_finish (context, success, time);
755 /*************************************************************
756 * gtk_drag_highlight_expose:
757 * Callback for expose_event for highlighted widgets.
763 *************************************************************/
766 gtk_drag_highlight_expose (GtkWidget *widget,
767 GdkEventExpose *event,
770 gint x, y, width, height;
772 if (GTK_WIDGET_DRAWABLE (widget))
774 if (GTK_WIDGET_NO_WINDOW (widget))
776 x = widget->allocation.x;
777 y = widget->allocation.y;
778 width = widget->allocation.width;
779 height = widget->allocation.height;
785 gdk_drawable_get_size (widget->window, &width, &height);
788 gtk_paint_shadow (widget->style, widget->window,
789 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
791 x, y, width, height);
793 gdk_draw_rectangle (widget->window,
794 widget->style->black_gc,
796 x, y, width - 1, height - 1);
802 /*************************************************************
803 * gtk_drag_highlight:
804 * Highlight the given widget in the default manner.
808 *************************************************************/
811 gtk_drag_highlight (GtkWidget *widget)
813 g_return_if_fail (GTK_IS_WIDGET (widget));
815 g_signal_connect_after (widget, "expose_event",
816 G_CALLBACK (gtk_drag_highlight_expose),
819 gtk_widget_queue_draw (widget);
822 /*************************************************************
823 * gtk_drag_unhighlight:
824 * Refresh the given widget to remove the highlight.
828 *************************************************************/
831 gtk_drag_unhighlight (GtkWidget *widget)
833 g_return_if_fail (GTK_IS_WIDGET (widget));
835 g_signal_handlers_disconnect_by_func (widget,
836 gtk_drag_highlight_expose,
839 gtk_widget_queue_draw (widget);
843 gtk_drag_dest_set_internal (GtkWidget *widget,
844 GtkDragDestSite *site)
846 GtkDragDestSite *old_site;
848 g_return_if_fail (widget != NULL);
850 /* HACK, do this in the destroy */
851 old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
854 g_signal_handlers_disconnect_by_func (widget,
855 gtk_drag_dest_realized,
857 g_signal_handlers_disconnect_by_func (widget,
858 gtk_drag_dest_hierarchy_changed,
862 if (GTK_WIDGET_REALIZED (widget))
863 gtk_drag_dest_realized (widget);
865 g_signal_connect (widget, "realize",
866 G_CALLBACK (gtk_drag_dest_realized), site);
867 g_signal_connect (widget, "hierarchy_changed",
868 G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
870 g_object_set_data_full (G_OBJECT (widget), "gtk-drag-dest",
871 site, gtk_drag_dest_site_destroy);
875 /*************************************************************
877 * Register a drop site, and possibly add default behaviors.
880 * flags: Which types of default drag behavior to use
881 * targets: Table of targets that can be accepted
882 * n_targets: Number of of entries in targets
885 *************************************************************/
888 gtk_drag_dest_set (GtkWidget *widget,
889 GtkDestDefaults flags,
890 const GtkTargetEntry *targets,
892 GdkDragAction actions)
894 GtkDragDestSite *site;
896 g_return_if_fail (GTK_IS_WIDGET (widget));
898 site = g_new (GtkDragDestSite, 1);
901 site->have_drag = FALSE;
903 site->target_list = gtk_target_list_new (targets, n_targets);
905 site->target_list = NULL;
906 site->actions = actions;
907 site->do_proxy = FALSE;
908 site->proxy_window = NULL;
910 gtk_drag_dest_set_internal (widget, site);
913 /*************************************************************
914 * gtk_drag_dest_set_proxy:
915 * Set up this widget to proxy drags elsewhere.
918 * proxy_window: window to which forward drag events
919 * protocol: Drag protocol which the dest widget accepts
920 * use_coordinates: If true, send the same coordinates to the
921 * destination, because it is a embedded
924 *************************************************************/
927 gtk_drag_dest_set_proxy (GtkWidget *widget,
928 GdkWindow *proxy_window,
929 GdkDragProtocol protocol,
930 gboolean use_coordinates)
932 GtkDragDestSite *site;
934 g_return_if_fail (GTK_IS_WIDGET (widget));
935 g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
937 site = g_new (GtkDragDestSite, 1);
940 site->have_drag = FALSE;
941 site->target_list = NULL;
943 site->proxy_window = proxy_window;
945 g_object_ref (proxy_window);
946 site->do_proxy = TRUE;
947 site->proxy_protocol = protocol;
948 site->proxy_coords = use_coordinates;
950 gtk_drag_dest_set_internal (widget, site);
953 /*************************************************************
954 * gtk_drag_dest_unset
955 * Unregister this widget as a drag target.
959 *************************************************************/
962 gtk_drag_dest_unset (GtkWidget *widget)
964 g_return_if_fail (GTK_IS_WIDGET (widget));
966 g_object_set_data (G_OBJECT (widget), "gtk-drag-dest", NULL);
970 * gtk_drag_dest_get_target_list:
971 * @widget: a #GtkWidget
973 * Returns the list of targets this widget can accept from
976 * Return value: the #GtkTargetList, or %NULL if none
979 gtk_drag_dest_get_target_list (GtkWidget *widget)
981 GtkDragDestSite *site;
983 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
985 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
987 return site ? site->target_list : NULL;
991 * gtk_drag_dest_set_target_list:
992 * @widget: a #GtkWidget that's a drag destination
993 * @target_list: list of droppable targets, or %NULL for none
995 * Sets the target types that this widget can accept from drag-and-drop.
996 * The widget must first be made into a drag destination with
997 * gtk_drag_dest_set().
1000 gtk_drag_dest_set_target_list (GtkWidget *widget,
1001 GtkTargetList *target_list)
1003 GtkDragDestSite *site;
1005 g_return_if_fail (GTK_IS_WIDGET (widget));
1007 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1011 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");
1016 gtk_target_list_ref (target_list);
1018 if (site->target_list)
1019 gtk_target_list_unref (site->target_list);
1021 site->target_list = target_list;
1025 /*************************************************************
1026 * _gtk_drag_dest_handle_event:
1027 * Called from widget event handling code on Drag events
1031 * toplevel: Toplevel widget that received the event
1034 *************************************************************/
1037 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1040 GtkDragDestInfo *info;
1041 GdkDragContext *context;
1043 g_return_if_fail (toplevel != NULL);
1044 g_return_if_fail (event != NULL);
1046 context = event->dnd.context;
1048 info = gtk_drag_get_dest_info (context, TRUE);
1050 /* Find the widget for the event */
1051 switch (event->type)
1053 case GDK_DRAG_ENTER:
1056 case GDK_DRAG_LEAVE:
1059 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1060 info->widget = NULL;
1064 case GDK_DRAG_MOTION:
1065 case GDK_DROP_START:
1067 GtkDragFindData data;
1070 if (event->type == GDK_DROP_START)
1072 info->dropped = TRUE;
1073 /* We send a leave here so that the widget unhighlights
1078 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1079 info->widget = NULL;
1083 gdk_window_get_position (toplevel->window, &tx, &ty);
1085 data.x = event->dnd.x_root - tx;
1086 data.y = event->dnd.y_root - ty;
1087 data.context = context;
1090 data.toplevel = TRUE;
1091 data.callback = (event->type == GDK_DRAG_MOTION) ?
1092 gtk_drag_dest_motion : gtk_drag_dest_drop;
1093 data.time = event->dnd.time;
1095 gtk_drag_find_widget (toplevel, &data);
1097 if (info->widget && !data.found)
1099 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1100 info->widget = NULL;
1105 if (event->type == GDK_DRAG_MOTION)
1108 gdk_drag_status (context, 0, event->dnd.time);
1110 else if (event->type == GDK_DROP_START && !info->proxy_source)
1112 gdk_drop_reply (context, data.found, event->dnd.time);
1113 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1114 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1120 g_assert_not_reached ();
1125 * gtk_drag_dest_find_target:
1126 * @widget: drag destination widget
1127 * @context: drag context
1128 * @target_list: list of droppable targets, or %NULL to use
1129 * gtk_drag_dest_get_target_list (@widget).
1131 * Looks for a match between @context->targets and the
1132 * @dest_target_list, returning the first matching target, otherwise
1133 * returning %GDK_NONE. @dest_target_list should usually be the return
1134 * value from gtk_drag_dest_get_target_list(), but some widgets may
1135 * have different valid targets for different parts of the widget; in
1136 * that case, they will have to implement a drag_motion handler that
1137 * passes the correct target list to this function.
1139 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1142 gtk_drag_dest_find_target (GtkWidget *widget,
1143 GdkDragContext *context,
1144 GtkTargetList *target_list)
1147 GList *tmp_source = NULL;
1148 GtkWidget *source_widget;
1150 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1151 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1152 g_return_val_if_fail (!context->is_source, GDK_NONE);
1155 source_widget = gtk_drag_get_source_widget (context);
1157 if (target_list == NULL)
1158 target_list = gtk_drag_dest_get_target_list (widget);
1160 if (target_list == NULL)
1163 tmp_target = target_list->list;
1166 GtkTargetPair *pair = tmp_target->data;
1167 tmp_source = context->targets;
1170 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1172 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1173 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1174 return pair->target;
1178 tmp_source = tmp_source->next;
1180 tmp_target = tmp_target->next;
1187 gtk_drag_selection_received (GtkWidget *widget,
1188 GtkSelectionData *selection_data,
1192 GdkDragContext *context;
1193 GtkDragDestInfo *info;
1194 GtkWidget *drop_widget;
1198 context = g_object_get_data (G_OBJECT (widget), "drag-context");
1199 info = gtk_drag_get_dest_info (context, FALSE);
1201 if (info->proxy_data &&
1202 info->proxy_data->target == selection_data->target)
1204 gtk_selection_data_set (info->proxy_data,
1205 selection_data->type,
1206 selection_data->format,
1207 selection_data->data,
1208 selection_data->length);
1213 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1215 gtk_drag_finish (context, TRUE, FALSE, time);
1217 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1218 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1224 GtkDragDestSite *site;
1226 site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1228 if (site && site->target_list)
1232 if (gtk_target_list_find (site->target_list,
1233 selection_data->target,
1236 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1237 selection_data->length >= 0)
1238 g_signal_emit_by_name (drop_widget,
1239 "drag_data_received",
1240 context, info->drop_x, info->drop_y,
1247 g_signal_emit_by_name (drop_widget,
1248 "drag_data_received",
1249 context, info->drop_x, info->drop_y,
1254 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1257 gtk_drag_finish (context,
1258 (selection_data->length >= 0),
1259 (context->action == GDK_ACTION_MOVE),
1263 g_object_unref (drop_widget);
1266 g_signal_handlers_disconnect_by_func (widget,
1267 gtk_drag_selection_received,
1270 g_object_set_data (G_OBJECT (widget), "drag-context", NULL);
1271 g_object_unref (context);
1273 gtk_drag_release_ipc_widget (widget);
1277 prepend_and_ref_widget (GtkWidget *widget,
1280 GSList **slist_p = data;
1282 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1285 /*************************************************************
1286 * gtk_drag_find_widget:
1287 * Recursive callback used to locate widgets for
1288 * DRAG_MOTION and DROP_START events.
1292 *************************************************************/
1295 gtk_drag_find_widget (GtkWidget *widget,
1296 GtkDragFindData *data)
1298 GtkAllocation new_allocation;
1299 gint allocation_to_window_x = 0;
1300 gint allocation_to_window_y = 0;
1304 if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget))
1307 /* Note that in the following code, we only count the
1308 * position as being inside a WINDOW widget if it is inside
1309 * widget->window; points that are outside of widget->window
1310 * but within the allocation are not counted. This is consistent
1311 * with the way we highlight drag targets.
1313 * data->x,y are relative to widget->parent->window (if
1314 * widget is not a toplevel, widget->window otherwise).
1315 * We compute the allocation of widget in the same coordinates,
1316 * clipping to widget->window, and all intermediate
1317 * windows. If data->x,y is inside that, then we translate
1318 * our coordinates to be relative to widget->window and
1321 new_allocation = widget->allocation;
1326 GdkWindow *window = widget->window;
1328 /* Compute the offset from allocation-relative to
1329 * window-relative coordinates.
1331 allocation_to_window_x = widget->allocation.x;
1332 allocation_to_window_y = widget->allocation.y;
1334 if (!GTK_WIDGET_NO_WINDOW (widget))
1336 /* The allocation is relative to the parent window for
1337 * window widgets, not to widget->window.
1339 gdk_window_get_position (window, &tx, &ty);
1341 allocation_to_window_x -= tx;
1342 allocation_to_window_y -= ty;
1345 new_allocation.x = 0 + allocation_to_window_x;
1346 new_allocation.y = 0 + allocation_to_window_y;
1348 while (window && window != widget->parent->window)
1350 GdkRectangle window_rect = { 0, 0, 0, 0 };
1352 gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
1354 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1356 gdk_window_get_position (window, &tx, &ty);
1357 new_allocation.x += tx;
1359 new_allocation.y += ty;
1362 window = gdk_window_get_parent (window);
1365 if (!window) /* Window and widget heirarchies didn't match. */
1369 if (data->toplevel ||
1370 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1371 (data->x < new_allocation.x + new_allocation.width) &&
1372 (data->y < new_allocation.y + new_allocation.height)))
1374 /* First, check if the drag is in a valid drop site in
1375 * one of our children
1377 if (GTK_IS_CONTAINER (widget))
1379 GtkDragFindData new_data = *data;
1380 GSList *children = NULL;
1383 new_data.x -= x_offset;
1384 new_data.y -= y_offset;
1385 new_data.found = FALSE;
1386 new_data.toplevel = FALSE;
1388 /* need to reference children temporarily in case the
1389 * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1391 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1392 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1394 if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1395 gtk_drag_find_widget (tmp_list->data, &new_data);
1396 g_object_unref (tmp_list->data);
1398 g_slist_free (children);
1400 data->found = new_data.found;
1403 /* If not, and this widget is registered as a drop site, check to
1404 * emit "drag_motion" to check if we are actually in
1408 g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1410 data->found = data->callback (widget,
1412 data->x - x_offset - allocation_to_window_x,
1413 data->y - y_offset - allocation_to_window_y,
1415 /* If so, send a "drag_leave" to the last widget */
1418 if (data->info->widget && data->info->widget != widget)
1420 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1422 data->info->widget = widget;
1429 gtk_drag_proxy_begin (GtkWidget *widget,
1430 GtkDragDestInfo *dest_info,
1433 GtkDragSourceInfo *source_info;
1435 GdkDragContext *context;
1436 GtkWidget *ipc_widget;
1438 if (dest_info->proxy_source)
1440 gdk_drag_abort (dest_info->proxy_source->context, time);
1441 gtk_drag_source_info_destroy (dest_info->proxy_source);
1442 dest_info->proxy_source = NULL;
1445 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1446 context = gdk_drag_begin (ipc_widget->window,
1447 dest_info->context->targets);
1449 source_info = gtk_drag_get_source_info (context, TRUE);
1451 source_info->ipc_widget = ipc_widget;
1452 source_info->widget = gtk_widget_ref (widget);
1454 source_info->target_list = gtk_target_list_new (NULL, 0);
1455 tmp_list = dest_info->context->targets;
1458 gtk_target_list_add (source_info->target_list,
1459 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1460 tmp_list = tmp_list->next;
1463 source_info->proxy_dest = dest_info;
1465 g_signal_connect (ipc_widget,
1467 G_CALLBACK (gtk_drag_selection_get),
1470 dest_info->proxy_source = source_info;
1474 gtk_drag_dest_info_destroy (gpointer data)
1476 GtkDragDestInfo *info = data;
1481 static GtkDragDestInfo *
1482 gtk_drag_get_dest_info (GdkDragContext *context,
1485 GtkDragDestInfo *info;
1486 static GQuark info_quark = 0;
1488 info_quark = g_quark_from_static_string ("gtk-dest-info");
1490 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1491 if (!info && create)
1493 info = g_new (GtkDragDestInfo, 1);
1494 info->widget = NULL;
1495 info->context = context;
1496 info->proxy_source = NULL;
1497 info->proxy_data = NULL;
1498 info->dropped = FALSE;
1499 info->proxy_drop_wait = FALSE;
1500 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1501 info, gtk_drag_dest_info_destroy);
1507 static GQuark dest_info_quark = 0;
1509 static GtkDragSourceInfo *
1510 gtk_drag_get_source_info (GdkDragContext *context,
1513 GtkDragSourceInfo *info;
1514 if (!dest_info_quark)
1515 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1517 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1518 if (!info && create)
1520 info = g_new0 (GtkDragSourceInfo, 1);
1521 info->context = context;
1522 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1529 gtk_drag_clear_source_info (GdkDragContext *context)
1531 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1535 gtk_drag_dest_realized (GtkWidget *widget)
1537 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1539 if (GTK_WIDGET_TOPLEVEL (toplevel))
1540 gdk_window_register_dnd (toplevel->window);
1544 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1545 GtkWidget *previous_toplevel)
1547 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1549 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1550 gdk_window_register_dnd (toplevel->window);
1554 gtk_drag_dest_site_destroy (gpointer data)
1556 GtkDragDestSite *site = data;
1558 if (site->proxy_window)
1559 g_object_unref (site->proxy_window);
1561 if (site->target_list)
1562 gtk_target_list_unref (site->target_list);
1568 * Default drag handlers
1571 gtk_drag_dest_leave (GtkWidget *widget,
1572 GdkDragContext *context,
1575 GtkDragDestSite *site;
1577 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1578 g_return_if_fail (site != NULL);
1582 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1584 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1586 gdk_drag_abort (info->proxy_source->context, time);
1587 gtk_drag_source_info_destroy (info->proxy_source);
1588 info->proxy_source = NULL;
1595 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1596 gtk_drag_unhighlight (widget);
1598 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1599 g_signal_emit_by_name (widget, "drag_leave",
1602 site->have_drag = FALSE;
1607 gtk_drag_dest_motion (GtkWidget *widget,
1608 GdkDragContext *context,
1613 GtkDragDestSite *site;
1614 GdkDragAction action = 0;
1617 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1618 g_return_val_if_fail (site != NULL, FALSE);
1623 GdkEvent *current_event;
1624 GdkWindow *dest_window;
1625 GdkDragProtocol proto;
1627 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1629 if (!info->proxy_source || info->proxy_source->widget != widget)
1630 gtk_drag_proxy_begin (widget, info, time);
1632 current_event = gtk_get_current_event ();
1634 if (site->proxy_window)
1636 dest_window = site->proxy_window;
1637 proto = site->proxy_protocol;
1641 gdk_drag_find_window_for_screen (info->proxy_source->context,
1643 gdk_drawable_get_screen (current_event->dnd.window),
1644 current_event->dnd.x_root,
1645 current_event->dnd.y_root,
1646 &dest_window, &proto);
1649 gdk_drag_motion (info->proxy_source->context,
1651 current_event->dnd.x_root,
1652 current_event->dnd.y_root,
1653 context->suggested_action,
1654 context->actions, time);
1656 if (!site->proxy_window && dest_window)
1657 g_object_unref (dest_window);
1659 selection = gdk_drag_get_selection (info->proxy_source->context);
1661 selection != gdk_drag_get_selection (info->context))
1662 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1664 gdk_event_free (current_event);
1669 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1671 if (context->suggested_action & site->actions)
1672 action = context->suggested_action;
1679 if ((site->actions & (1 << i)) &&
1680 (context->actions & (1 << i)))
1688 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1690 if (!site->have_drag)
1692 site->have_drag = TRUE;
1693 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1694 gtk_drag_highlight (widget);
1697 gdk_drag_status (context, action, time);
1701 gdk_drag_status (context, 0, time);
1706 g_signal_emit_by_name (widget, "drag_motion",
1707 context, x, y, time, &retval);
1709 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1713 gtk_drag_dest_drop (GtkWidget *widget,
1714 GdkDragContext *context,
1719 GtkDragDestSite *site;
1720 GtkDragDestInfo *info;
1722 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1723 g_return_val_if_fail (site != NULL, FALSE);
1725 info = gtk_drag_get_dest_info (context, FALSE);
1726 g_return_val_if_fail (info != NULL, FALSE);
1733 if (info->proxy_source ||
1734 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1736 gtk_drag_drop (info->proxy_source, time);
1740 /* We need to synthesize a motion event, wait for a status,
1741 * and, if we get a good one, do a drop.
1744 GdkEvent *current_event;
1746 GdkWindow *dest_window;
1747 GdkDragProtocol proto;
1749 gtk_drag_proxy_begin (widget, info, time);
1750 info->proxy_drop_wait = TRUE;
1751 info->proxy_drop_time = time;
1753 current_event = gtk_get_current_event ();
1755 if (site->proxy_window)
1757 dest_window = site->proxy_window;
1758 proto = site->proxy_protocol;
1762 gdk_drag_find_window_for_screen (info->proxy_source->context,
1764 gdk_drawable_get_screen (current_event->dnd.window),
1765 current_event->dnd.x_root,
1766 current_event->dnd.y_root,
1767 &dest_window, &proto);
1770 gdk_drag_motion (info->proxy_source->context,
1772 current_event->dnd.x_root,
1773 current_event->dnd.y_root,
1774 context->suggested_action,
1775 context->actions, time);
1777 if (!site->proxy_window && dest_window)
1778 g_object_unref (dest_window);
1780 selection = gdk_drag_get_selection (info->proxy_source->context);
1782 selection != gdk_drag_get_selection (info->context))
1783 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1785 gdk_event_free (current_event);
1794 if (site->flags & GTK_DEST_DEFAULT_DROP)
1796 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1798 if (target == GDK_NONE)
1800 gtk_drag_finish (context, FALSE, FALSE, time);
1804 gtk_drag_get_data (widget, context, target, time);
1807 g_signal_emit_by_name (widget, "drag_drop",
1808 context, x, y, time, &retval);
1810 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1818 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
1819 * so that we can set the icon from the source site information
1821 static GdkDragContext *
1822 gtk_drag_begin_internal (GtkWidget *widget,
1823 GtkDragSourceSite *site,
1824 GtkTargetList *target_list,
1825 GdkDragAction actions,
1829 GtkDragSourceInfo *info;
1830 GList *targets = NULL;
1832 guint32 time = GDK_CURRENT_TIME;
1833 GdkDragAction possible_actions, suggested_action;
1834 GdkDragContext *context;
1835 GtkWidget *ipc_widget;
1838 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1840 gtk_drag_get_event_actions (event, button, actions,
1841 &suggested_action, &possible_actions);
1843 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), suggested_action);
1846 time = gdk_event_get_time (event);
1848 if (gdk_pointer_grab (ipc_widget->window, FALSE,
1849 GDK_POINTER_MOTION_MASK |
1850 GDK_BUTTON_RELEASE_MASK, NULL,
1853 gtk_drag_release_ipc_widget (ipc_widget);
1857 if (gdk_keyboard_grab (ipc_widget->window, FALSE, time) != 0)
1859 gtk_drag_release_ipc_widget (ipc_widget);
1863 /* We use a GTK grab here to override any grabs that the widget
1864 * we are dragging from might have held
1866 gtk_grab_add (ipc_widget);
1868 tmp_list = g_list_last (target_list->list);
1871 GtkTargetPair *pair = tmp_list->data;
1872 targets = g_list_prepend (targets,
1873 GINT_TO_POINTER (pair->target));
1874 tmp_list = tmp_list->prev;
1877 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1879 context = gdk_drag_begin (ipc_widget->window, targets);
1880 g_list_free (targets);
1882 info = gtk_drag_get_source_info (context, TRUE);
1884 info->ipc_widget = ipc_widget;
1885 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", info);
1887 info->widget = gtk_widget_ref (widget);
1889 info->button = button;
1890 info->cursor = cursor;
1891 info->target_list = target_list;
1892 gtk_target_list_ref (target_list);
1894 info->possible_actions = actions;
1896 info->status = GTK_DRAG_STATUS_DRAG;
1897 info->last_event = NULL;
1898 info->selections = NULL;
1899 info->icon_window = NULL;
1900 info->destroy_icon = FALSE;
1902 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1903 * the drag icon, it will be in the right place
1905 if (event && event->type == GDK_MOTION_NOTIFY)
1907 info->cur_screen = gtk_widget_get_screen (widget);
1908 info->cur_x = event->motion.x_root;
1909 info->cur_y = event->motion.y_root;
1913 gdk_display_get_pointer (gtk_widget_get_display (widget),
1914 &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
1917 g_signal_emit_by_name (widget, "drag_begin",
1920 /* Ensure that we have an icon before we start the drag; the
1921 * application may have set one in ::drag_begin, or it may
1924 if (!info->icon_window)
1926 if (!site || site->icon_type == GTK_IMAGE_EMPTY)
1927 gtk_drag_set_icon_default (context);
1929 switch (site->icon_type)
1931 case GTK_IMAGE_PIXMAP:
1932 gtk_drag_set_icon_pixmap (context,
1934 site->icon_data.pixmap.pixmap,
1938 case GTK_IMAGE_PIXBUF:
1939 gtk_drag_set_icon_pixbuf (context,
1940 site->icon_data.pixbuf.pixbuf,
1943 case GTK_IMAGE_STOCK:
1944 gtk_drag_set_icon_stock (context,
1945 site->icon_data.stock.stock_id,
1948 case GTK_IMAGE_EMPTY:
1950 g_assert_not_reached();
1955 if (event && event->type == GDK_MOTION_NOTIFY)
1956 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1958 info->start_x = info->cur_x;
1959 info->start_y = info->cur_y;
1961 g_signal_connect (info->ipc_widget, "button_release_event",
1962 G_CALLBACK (gtk_drag_button_release_cb), info);
1963 g_signal_connect (info->ipc_widget, "motion_notify_event",
1964 G_CALLBACK (gtk_drag_motion_cb), info);
1965 g_signal_connect (info->ipc_widget, "key_press_event",
1966 G_CALLBACK (gtk_drag_key_cb), info);
1967 g_signal_connect (info->ipc_widget, "key_release_event",
1968 G_CALLBACK (gtk_drag_key_cb), info);
1969 g_signal_connect (info->ipc_widget, "selection_get",
1970 G_CALLBACK (gtk_drag_selection_get), info);
1972 info->have_grab = TRUE;
1973 info->grab_time = time;
1975 return info->context;
1980 * @widget: the source widget.
1981 * @target_list: The targets (data formats) in which the
1982 * source can provide the data.
1983 * @actions: A bitmask of the allowed drag actions for this drag.
1984 * @button: The button the user clicked to start the drag.
1985 * @event: The event that triggered the start of the drag.
1987 * Initiates a drag on the source side. The function
1988 * only needs to be used when the application is
1989 * starting drags itself, and is not needed when
1990 * gtk_drag_source_set() is used.
1992 * Return value: the context for this drag.
1995 gtk_drag_begin (GtkWidget *widget,
1996 GtkTargetList *target_list,
1997 GdkDragAction actions,
2001 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2002 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
2003 g_return_val_if_fail (target_list != NULL, NULL);
2005 return gtk_drag_begin_internal (widget, NULL, target_list,
2006 actions, button, event);
2009 /*************************************************************
2010 * gtk_drag_source_set:
2011 * Register a drop site, and possibly add default behaviors.
2014 * start_button_mask: Mask of allowed buttons to start drag
2015 * targets: Table of targets for this source
2017 * actions: Actions allowed for this source
2019 *************************************************************/
2022 gtk_drag_source_set (GtkWidget *widget,
2023 GdkModifierType start_button_mask,
2024 const GtkTargetEntry *targets,
2026 GdkDragAction actions)
2028 GtkDragSourceSite *site;
2030 g_return_if_fail (GTK_IS_WIDGET (widget));
2032 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2034 gtk_widget_add_events (widget,
2035 gtk_widget_get_events (widget) |
2036 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2037 GDK_BUTTON_MOTION_MASK);
2041 if (site->target_list)
2042 gtk_target_list_unref (site->target_list);
2046 site = g_new0 (GtkDragSourceSite, 1);
2048 site->icon_type = GTK_IMAGE_EMPTY;
2050 g_signal_connect (widget, "button_press_event",
2051 G_CALLBACK (gtk_drag_source_event_cb),
2053 g_signal_connect (widget, "motion_notify_event",
2054 G_CALLBACK (gtk_drag_source_event_cb),
2057 g_object_set_data_full (G_OBJECT (widget),
2059 site, gtk_drag_source_site_destroy);
2062 site->start_button_mask = start_button_mask;
2065 site->target_list = gtk_target_list_new (targets, n_targets);
2067 site->target_list = NULL;
2069 site->actions = actions;
2073 /*************************************************************
2074 * gtk_drag_source_unset
2075 * Unregister this widget as a drag source.
2079 *************************************************************/
2082 gtk_drag_source_unset (GtkWidget *widget)
2084 GtkDragSourceSite *site;
2086 g_return_if_fail (GTK_IS_WIDGET (widget));
2088 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2092 g_signal_handlers_disconnect_by_func (widget,
2093 gtk_drag_source_event_cb,
2095 g_signal_handlers_disconnect_by_func (widget,
2096 gtk_drag_source_event_cb,
2098 g_object_set_data (G_OBJECT (widget), "gtk-site-data", NULL);
2103 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2105 switch (site->icon_type)
2107 case GTK_IMAGE_EMPTY:
2109 case GTK_IMAGE_PIXMAP:
2110 if (site->icon_data.pixmap.pixmap)
2111 g_object_unref (site->icon_data.pixmap.pixmap);
2112 if (site->icon_mask)
2113 g_object_unref (site->icon_mask);
2115 case GTK_IMAGE_PIXBUF:
2116 g_object_unref (site->icon_data.pixbuf.pixbuf);
2118 case GTK_IMAGE_STOCK:
2119 g_free (site->icon_data.stock.stock_id);
2122 g_assert_not_reached();
2125 site->icon_type = GTK_IMAGE_EMPTY;
2128 g_object_unref (site->colormap);
2129 site->colormap = NULL;
2133 * gtk_drag_source_set_icon:
2134 * @widget: a #GtkWidget
2135 * @colormap: the colormap of the icon
2136 * @pixmap: the image data for the icon
2137 * @mask: the transparency mask for an image.
2139 * Sets the icon that will be used for drags from a particular widget
2140 * from a pixmap/mask. GTK+ retains references for the arguments, and
2141 * will release them when they are no longer needed.
2142 * Use gtk_drag_source_set_icon_pixbuf() instead.
2145 gtk_drag_source_set_icon (GtkWidget *widget,
2146 GdkColormap *colormap,
2150 GtkDragSourceSite *site;
2152 g_return_if_fail (GTK_IS_WIDGET (widget));
2153 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2154 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2155 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2157 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2158 g_return_if_fail (site != NULL);
2160 g_object_ref (colormap);
2161 g_object_ref (pixmap);
2163 g_object_ref (mask);
2165 gtk_drag_source_unset_icon (site);
2167 site->icon_type = GTK_IMAGE_PIXMAP;
2169 site->icon_data.pixmap.pixmap = pixmap;
2170 site->icon_mask = mask;
2171 site->colormap = colormap;
2175 * gtk_drag_source_set_icon_pixbuf:
2176 * @widget: a #GtkWidget
2177 * @pixbuf: the #GdkPixbuf for the drag icon
2179 * Sets the icon that will be used for drags from a particular widget
2180 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2181 * release it when it is no longer needed.
2184 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2187 GtkDragSourceSite *site;
2189 g_return_if_fail (GTK_IS_WIDGET (widget));
2190 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2192 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2193 g_return_if_fail (site != NULL);
2194 g_object_ref (pixbuf);
2196 gtk_drag_source_unset_icon (site);
2198 site->icon_type = GTK_IMAGE_PIXBUF;
2199 site->icon_data.pixbuf.pixbuf = pixbuf;
2203 * gtk_drag_source_set_icon_stock:
2204 * @widget: a #GtkWidget
2205 * @stock_id: the ID of the stock icon to use
2207 * Sets the icon that will be used for drags from a particular source
2211 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2212 const gchar *stock_id)
2214 GtkDragSourceSite *site;
2216 g_return_if_fail (GTK_IS_WIDGET (widget));
2217 g_return_if_fail (stock_id != NULL);
2219 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2220 g_return_if_fail (site != NULL);
2222 gtk_drag_source_unset_icon (site);
2224 site->icon_type = GTK_IMAGE_STOCK;
2225 site->icon_data.stock.stock_id = g_strdup (stock_id);
2229 gtk_drag_get_icon (GtkDragSourceInfo *info,
2230 GtkWidget **icon_window,
2234 if (get_can_change_screen (info->icon_window))
2235 gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2238 if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2240 if (!info->fallback_icon)
2242 gint save_hot_x, save_hot_y;
2243 gboolean save_destroy_icon;
2244 GtkWidget *save_icon_window;
2246 /* HACK to get the appropriate icon
2248 save_icon_window = info->icon_window;
2249 save_hot_x = info->hot_x;
2250 save_hot_y = info->hot_x;
2251 save_destroy_icon = info->destroy_icon;
2253 info->icon_window = NULL;
2254 gtk_drag_set_icon_default (info->context);
2255 info->fallback_icon = info->icon_window;
2257 info->icon_window = save_icon_window;
2258 info->hot_x = save_hot_x;
2259 info->hot_y = save_hot_y;
2260 info->destroy_icon = save_destroy_icon;
2263 gtk_widget_hide (info->icon_window);
2265 *icon_window = info->fallback_icon;
2266 gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2268 if (!default_icon_pixmap)
2275 *hot_x = default_icon_hot_x;
2276 *hot_y = default_icon_hot_y;
2281 if (info->fallback_icon)
2282 gtk_widget_hide (info->fallback_icon);
2284 *icon_window = info->icon_window;
2285 *hot_x = info->hot_x;
2286 *hot_y = info->hot_y;
2291 gtk_drag_update_icon (GtkDragSourceInfo *info)
2293 if (info->icon_window)
2295 GtkWidget *icon_window;
2298 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2300 gtk_window_move (GTK_WINDOW (icon_window),
2301 info->cur_x - hot_x,
2302 info->cur_y - hot_y);
2304 if (GTK_WIDGET_VISIBLE (icon_window))
2305 gdk_window_raise (icon_window->window);
2307 gtk_widget_show (icon_window);
2312 gtk_drag_set_icon_window (GdkDragContext *context,
2316 gboolean destroy_on_release)
2318 GtkDragSourceInfo *info;
2320 info = gtk_drag_get_source_info (context, FALSE);
2321 gtk_drag_remove_icon (info);
2324 gtk_widget_ref (widget);
2326 info->icon_window = widget;
2327 info->hot_x = hot_x;
2328 info->hot_y = hot_y;
2329 info->destroy_icon = destroy_on_release;
2331 gtk_drag_update_icon (info);
2335 * gtk_drag_set_icon_widget:
2336 * @context: the context for a drag. (This must be called
2337 with a context for the source side of a drag)
2338 * @widget: a toplevel window to use as an icon.
2339 * @hot_x: the X offset within @widget of the hotspot.
2340 * @hot_y: the Y offset within @widget of the hotspot.
2342 * Changes the icon for a widget to a given widget. GTK+
2343 * will not destroy the icon, so if you don't want
2344 * it to persist, you should connect to the "drag_end"
2345 * signal and destroy it yourself.
2348 gtk_drag_set_icon_widget (GdkDragContext *context,
2353 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2354 g_return_if_fail (context->is_source);
2355 g_return_if_fail (GTK_IS_WIDGET (widget));
2357 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2361 icon_window_realize (GtkWidget *window,
2367 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
2368 gtk_widget_get_colormap (window),
2369 &pixmap, &mask, 128);
2371 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2374 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2376 g_object_unref (pixmap);
2379 g_object_unref (mask);
2383 set_icon_stock_pixbuf (GdkDragContext *context,
2384 const gchar *stock_id,
2393 g_return_if_fail (context != NULL);
2394 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2395 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2397 screen = gdk_drawable_get_screen (context->source_window);
2399 /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
2400 gtk_widget_push_colormap (NULL);
2401 window = gtk_window_new (GTK_WINDOW_POPUP);
2402 gtk_window_set_screen (GTK_WINDOW (window), screen);
2403 set_can_change_screen (window, TRUE);
2404 gtk_widget_pop_colormap ();
2406 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2407 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2411 pixbuf = gtk_widget_render_icon (window, stock_id,
2412 GTK_ICON_SIZE_DND, NULL);
2416 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2417 gtk_widget_destroy (window);
2423 g_object_ref (pixbuf);
2425 width = gdk_pixbuf_get_width (pixbuf);
2426 height = gdk_pixbuf_get_width (pixbuf);
2428 gtk_widget_set_size_request (window,
2429 gdk_pixbuf_get_width (pixbuf),
2430 gdk_pixbuf_get_height (pixbuf));
2432 g_signal_connect_closure (window, "realize",
2433 g_cclosure_new (G_CALLBACK (icon_window_realize),
2435 (GClosureNotify)g_object_unref),
2438 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2442 * gtk_drag_set_icon_pixbuf:
2443 * @context: the context for a drag. (This must be called
2444 * with a context for the source side of a drag)
2445 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2446 * @hot_x: the X offset within @widget of the hotspot.
2447 * @hot_y: the Y offset within @widget of the hotspot.
2449 * Sets @pixbuf as the icon for a given drag.
2452 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2457 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2458 g_return_if_fail (context->is_source);
2459 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2461 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2465 * gtk_drag_set_icon_stock:
2466 * @context: the context for a drag. (This must be called
2467 * with a context for the source side of a drag)
2468 * @stock_id: the ID of the stock icon to use for the drag.
2469 * @hot_x: the X offset within the icon of the hotspot.
2470 * @hot_y: the Y offset within the icon of the hotspot.
2472 * Sets the the icon for a given drag from a stock ID.
2475 gtk_drag_set_icon_stock (GdkDragContext *context,
2476 const gchar *stock_id,
2480 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2481 g_return_if_fail (context->is_source);
2482 g_return_if_fail (stock_id != NULL);
2484 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2488 * gtk_drag_set_icon_pixmap:
2489 * @context: the context for a drag. (This must be called
2490 * with a context for the source side of a drag)
2491 * @colormap: the colormap of the icon
2492 * @pixmap: the image data for the icon
2493 * @mask: the transparency mask for the icon
2494 * @hot_x: the X offset within @pixmap of the hotspot.
2495 * @hot_y: the Y offset within @pixmap of the hotspot.
2497 * Sets @pixmap as the icon for a given drag. GTK+ retains
2498 * references for the arguments, and will release them when
2499 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2500 * will be more convenient to use.
2503 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2504 GdkColormap *colormap,
2514 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2515 g_return_if_fail (context->is_source);
2516 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2517 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2518 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2520 screen = gdk_colormap_get_screen (colormap);
2522 g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
2523 g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
2525 gdk_drawable_get_size (pixmap, &width, &height);
2527 gtk_widget_push_colormap (colormap);
2529 window = gtk_window_new (GTK_WINDOW_POPUP);
2530 gtk_window_set_screen (GTK_WINDOW (window), screen);
2531 set_can_change_screen (window, FALSE);
2532 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2533 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2535 gtk_widget_pop_colormap ();
2537 gtk_widget_set_size_request (window, width, height);
2538 gtk_widget_realize (window);
2540 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2543 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2545 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2549 * gtk_drag_set_icon_default:
2550 * @context: the context for a drag. (This must be called
2551 with a context for the source side of a drag)
2553 * Sets the icon for a particular drag to the default
2557 gtk_drag_set_icon_default (GdkDragContext *context)
2559 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2560 g_return_if_fail (context->is_source);
2562 if (!default_icon_pixmap)
2563 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2565 gtk_drag_set_icon_pixmap (context,
2566 default_icon_colormap,
2567 default_icon_pixmap,
2570 default_icon_hot_y);
2574 * gtk_drag_set_default_icon:
2575 * @colormap: the colormap of the icon
2576 * @pixmap: the image data for the icon
2577 * @mask: the transparency mask for an image.
2578 * @hot_x: The X offset within @widget of the hotspot.
2579 * @hot_y: The Y offset within @widget of the hotspot.
2581 * Changes the default drag icon. GTK+ retains references for the
2582 * arguments, and will release them when they are no longer needed.
2583 * This function is obsolete. The default icon should now be changed
2584 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2587 gtk_drag_set_default_icon (GdkColormap *colormap,
2593 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2594 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2595 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2597 if (default_icon_colormap)
2598 g_object_unref (default_icon_colormap);
2599 if (default_icon_pixmap)
2600 g_object_unref (default_icon_pixmap);
2601 if (default_icon_mask)
2602 g_object_unref (default_icon_mask);
2604 default_icon_colormap = colormap;
2605 g_object_ref (colormap);
2607 default_icon_pixmap = pixmap;
2608 g_object_ref (pixmap);
2610 default_icon_mask = mask;
2612 g_object_ref (mask);
2614 default_icon_hot_x = hot_x;
2615 default_icon_hot_y = hot_y;
2619 /*************************************************************
2620 * _gtk_drag_source_handle_event:
2621 * Called from widget event handling code on Drag events
2625 * toplevel: Toplevel widget that received the event
2628 *************************************************************/
2631 _gtk_drag_source_handle_event (GtkWidget *widget,
2634 GtkDragSourceInfo *info;
2635 GdkDragContext *context;
2637 g_return_if_fail (widget != NULL);
2638 g_return_if_fail (event != NULL);
2640 context = event->dnd.context;
2641 info = gtk_drag_get_source_info (context, FALSE);
2645 switch (event->type)
2647 case GDK_DRAG_STATUS:
2651 if (info->proxy_dest)
2653 if (!event->dnd.send_event)
2655 if (info->proxy_dest->proxy_drop_wait)
2657 gboolean result = context->action != 0;
2659 /* Aha - we can finally pass the MOTIF DROP on... */
2660 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2662 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2664 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2668 gdk_drag_status (info->proxy_dest->context,
2669 event->dnd.context->action,
2674 else if (info->have_grab)
2676 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2677 event->dnd.context->action);
2678 if (info->cursor != cursor)
2680 gdk_pointer_grab (widget->window, FALSE,
2681 GDK_POINTER_MOTION_MASK |
2682 GDK_BUTTON_RELEASE_MASK,
2684 cursor, info->grab_time);
2685 info->cursor = cursor;
2688 if (info->last_event)
2689 gtk_drag_add_update_idle (info);
2694 case GDK_DROP_FINISHED:
2695 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2698 g_assert_not_reached ();
2702 /*************************************************************
2703 * gtk_drag_source_check_selection:
2704 * Check if we've set up handlers/claimed the selection
2705 * for a given drag. If not, add them.
2709 *************************************************************/
2712 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2718 tmp_list = info->selections;
2721 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2723 tmp_list = tmp_list->next;
2726 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2730 info->selections = g_list_prepend (info->selections,
2731 GUINT_TO_POINTER (selection));
2733 tmp_list = info->target_list->list;
2736 GtkTargetPair *pair = tmp_list->data;
2738 gtk_selection_add_target (info->ipc_widget,
2742 tmp_list = tmp_list->next;
2745 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2747 gtk_selection_add_target (info->ipc_widget,
2749 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2750 TARGET_MOTIF_SUCCESS);
2751 gtk_selection_add_target (info->ipc_widget,
2753 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2754 TARGET_MOTIF_FAILURE);
2757 gtk_selection_add_target (info->ipc_widget,
2759 gdk_atom_intern ("DELETE", FALSE),
2763 /*************************************************************
2764 * gtk_drag_drop_finished:
2765 * Clean up from the drag, and display snapback, if necessary.
2771 *************************************************************/
2774 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2778 gtk_drag_source_release_selections (info, time);
2780 if (info->proxy_dest)
2782 /* The time from the event isn't reliable for Xdnd drags */
2783 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2784 info->proxy_dest->proxy_drop_time);
2785 gtk_drag_source_info_destroy (info);
2791 gtk_drag_source_info_destroy (info);
2795 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2799 anim->n_steps = MAX (info->cur_x - info->start_x,
2800 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2801 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2803 info->cur_screen = gtk_widget_get_screen (info->widget);
2804 gtk_drag_update_icon (info);
2806 /* Mark the context as dead, so if the destination decides
2807 * to respond really late, we still are OK.
2809 gtk_drag_clear_source_info (info->context);
2810 g_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2816 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2819 GdkDisplay *display = gtk_widget_get_display (info->widget);
2820 GList *tmp_list = info->selections;
2824 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2825 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2826 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2828 tmp_list = tmp_list->next;
2831 g_list_free (info->selections);
2832 info->selections = NULL;
2835 /*************************************************************
2837 * Send a drop event.
2841 *************************************************************/
2844 gtk_drag_drop (GtkDragSourceInfo *info,
2847 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2849 GtkSelectionData selection_data;
2851 /* GTK+ traditionally has used application/x-rootwin-drop, but the
2852 * XDND spec specifies x-rootwindow-drop.
2854 GdkAtom target1 = gdk_atom_intern ("application/x-rootwindow-drop", FALSE);
2855 GdkAtom target2 = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2857 tmp_list = info->target_list->list;
2860 GtkTargetPair *pair = tmp_list->data;
2862 if (pair->target == target1 || pair->target == target2)
2864 selection_data.selection = GDK_NONE;
2865 selection_data.target = pair->target;
2866 selection_data.data = NULL;
2867 selection_data.length = -1;
2869 g_signal_emit_by_name (info->widget, "drag_data_get",
2870 info->context, &selection_data,
2874 /* FIXME: Should we check for length >= 0 here? */
2875 gtk_drag_drop_finished (info, TRUE, time);
2878 tmp_list = tmp_list->next;
2880 gtk_drag_drop_finished (info, FALSE, time);
2884 if (info->icon_window)
2885 gtk_widget_hide (info->icon_window);
2887 gdk_drag_drop (info->context, time);
2888 info->drop_timeout = g_timeout_add (DROP_ABORT_TIME,
2889 gtk_drag_abort_timeout,
2895 * Source side callbacks.
2899 gtk_drag_source_event_cb (GtkWidget *widget,
2903 GtkDragSourceSite *site;
2904 gboolean retval = FALSE;
2905 site = (GtkDragSourceSite *)data;
2907 switch (event->type)
2909 case GDK_BUTTON_PRESS:
2910 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2912 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2913 site->x = event->button.x;
2914 site->y = event->button.y;
2918 case GDK_BUTTON_RELEASE:
2919 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2920 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2923 case GDK_MOTION_NOTIFY:
2924 if (site->state & event->motion.state & site->start_button_mask)
2926 /* FIXME: This is really broken and can leave us
2932 if (site->state & event->motion.state &
2933 GDK_BUTTON1_MASK << (i - 1))
2937 if (gtk_drag_check_threshold (widget, site->x, site->y,
2938 event->motion.x, event->motion.y))
2940 GdkDragContext *context;
2943 context = gtk_drag_begin_internal (widget, site, site->target_list,
2952 default: /* hit for 2/3BUTTON_PRESS */
2960 gtk_drag_source_site_destroy (gpointer data)
2962 GtkDragSourceSite *site = data;
2964 if (site->target_list)
2965 gtk_target_list_unref (site->target_list);
2967 gtk_drag_source_unset_icon (site);
2972 gtk_drag_selection_get (GtkWidget *widget,
2973 GtkSelectionData *selection_data,
2978 GtkDragSourceInfo *info = data;
2979 static GdkAtom null_atom = GDK_NONE;
2983 null_atom = gdk_atom_intern ("NULL", FALSE);
2988 g_signal_emit_by_name (info->widget,
2991 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2993 case TARGET_MOTIF_SUCCESS:
2994 gtk_drag_drop_finished (info, TRUE, time);
2995 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2997 case TARGET_MOTIF_FAILURE:
2998 gtk_drag_drop_finished (info, FALSE, time);
2999 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3002 if (info->proxy_dest)
3004 /* This is sort of dangerous and needs to be thought
3007 info->proxy_dest->proxy_data = selection_data;
3008 gtk_drag_get_data (info->widget,
3009 info->proxy_dest->context,
3010 selection_data->target,
3013 info->proxy_dest->proxy_data = NULL;
3017 if (gtk_target_list_find (info->target_list,
3018 selection_data->target,
3021 g_signal_emit_by_name (info->widget, "drag_data_get",
3033 gtk_drag_anim_timeout (gpointer data)
3035 GtkDragAnim *anim = data;
3039 GDK_THREADS_ENTER ();
3041 if (anim->step == anim->n_steps)
3043 gtk_drag_source_info_destroy (anim->info);
3050 x = (anim->info->start_x * (anim->step + 1) +
3051 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3052 y = (anim->info->start_y * (anim->step + 1) +
3053 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3054 if (anim->info->icon_window)
3056 GtkWidget *icon_window;
3059 gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);
3061 gtk_window_move (GTK_WINDOW (icon_window),
3071 GDK_THREADS_LEAVE ();
3077 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3079 if (info->icon_window)
3081 gtk_widget_hide (info->icon_window);
3082 if (info->destroy_icon)
3083 gtk_widget_destroy (info->icon_window);
3085 if (info->fallback_icon)
3087 gtk_widget_destroy (info->fallback_icon);
3088 info->fallback_icon = NULL;
3091 g_object_unref (info->icon_window);
3092 info->icon_window = NULL;
3097 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3099 gtk_drag_remove_icon (info);
3101 if (!info->proxy_dest)
3102 g_signal_emit_by_name (info->widget, "drag_end",
3106 g_object_unref (info->widget);
3109 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3110 gtk_drag_button_release_cb,
3112 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3115 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3118 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3119 gtk_drag_selection_get,
3122 gtk_selection_remove_all (info->ipc_widget);
3123 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", NULL);
3124 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3125 gtk_drag_release_ipc_widget (info->ipc_widget);
3127 gtk_target_list_unref (info->target_list);
3129 gtk_drag_clear_source_info (info->context);
3130 g_object_unref (info->context);
3132 if (info->drop_timeout)
3133 g_source_remove (info->drop_timeout);
3139 gtk_drag_update_idle (gpointer data)
3141 GtkDragSourceInfo *info = data;
3142 GdkWindow *dest_window;
3143 GdkDragProtocol protocol;
3146 GdkDragAction action;
3147 GdkDragAction possible_actions;
3150 info->update_idle = 0;
3152 time = gtk_drag_get_event_time (info->last_event);
3153 gtk_drag_get_event_actions (info->last_event,
3155 info->possible_actions,
3156 &action, &possible_actions);
3157 gtk_drag_update_icon (info);
3158 gdk_drag_find_window_for_screen (info->context,
3159 info->icon_window ? info->icon_window->window : NULL,
3160 info->cur_screen, info->cur_x, info->cur_y,
3161 &dest_window, &protocol);
3163 if (!gdk_drag_motion (info->context, dest_window, protocol,
3164 info->cur_x, info->cur_y, action,
3168 gdk_event_free ((GdkEvent *)info->last_event);
3169 info->last_event = NULL;
3173 g_object_unref (dest_window);
3175 selection = gdk_drag_get_selection (info->context);
3177 gtk_drag_source_check_selection (info, selection, time);
3183 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
3185 /* We use an idle lowerthan GDK_PRIORITY_REDRAW so that exposes
3186 * from the last move can catch up before we move again.
3188 if (!info->update_idle)
3189 info->update_idle = g_idle_add_full (GDK_PRIORITY_REDRAW + 5,
3190 gtk_drag_update_idle,
3197 * @info: DragSourceInfo for the drag
3198 * @screen: new screen
3199 * @x_root: new X position
3200 * @y_root: new y position
3201 * @event: event received requiring update
3203 * Updates the status of the drag; called when the
3204 * cursor moves or the modifier changes
3207 gtk_drag_update (GtkDragSourceInfo *info,
3213 info->cur_screen = screen;
3214 info->cur_x = x_root;
3215 info->cur_y = y_root;
3216 if (info->last_event)
3217 gdk_event_free ((GdkEvent *)info->last_event);
3218 info->last_event = gdk_event_copy ((GdkEvent *)event);
3220 gtk_drag_add_update_idle (info);
3223 /*************************************************************
3225 * Called when the user finishes to drag, either by
3226 * releasing the mouse, or by pressing Esc.
3228 * info: Source info for the drag
3229 * time: Timestamp for ending the drag
3231 *************************************************************/
3234 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3236 GdkEvent *send_event;
3237 GtkWidget *source_widget = info->widget;
3238 GdkDisplay *display = gtk_widget_get_display (source_widget);
3240 if (info->update_idle)
3242 g_source_remove (info->update_idle);
3243 info->update_idle = 0;
3246 if (info->last_event)
3248 gdk_event_free (info->last_event);
3249 info->last_event = NULL;
3252 info->have_grab = FALSE;
3254 gdk_display_pointer_ungrab (display, time);
3255 gdk_display_keyboard_ungrab (display, time);
3256 gtk_grab_remove (info->ipc_widget);
3258 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3259 gtk_drag_button_release_cb,
3261 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3264 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3268 /* Send on a release pair to the the original
3269 * widget to convince it to release its grab. We need to
3270 * call gtk_propagate_event() here, instead of
3271 * gtk_widget_event() because widget like GtkList may
3272 * expect propagation.
3275 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3276 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3277 send_event->button.send_event = TRUE;
3278 send_event->button.time = time;
3279 send_event->button.x = 0;
3280 send_event->button.y = 0;
3281 send_event->button.axes = NULL;
3282 send_event->button.state = 0;
3283 send_event->button.button = info->button;
3284 send_event->button.device = gdk_display_get_core_pointer (display);
3285 send_event->button.x_root = 0;
3286 send_event->button.y_root = 0;
3288 gtk_propagate_event (source_widget, send_event);
3289 gdk_event_free (send_event);
3292 /*************************************************************
3294 * Called on cancellation of a drag, either by the user
3295 * or programmatically.
3297 * info: Source info for the drag
3298 * time: Timestamp for ending the drag
3300 *************************************************************/
3303 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3305 gtk_drag_end (info, time);
3306 gdk_drag_abort (info->context, time);
3307 gtk_drag_drop_finished (info, FALSE, time);
3310 /*************************************************************
3311 * gtk_drag_motion_cb:
3312 * "motion_notify_event" callback during drag.
3316 *************************************************************/
3319 gtk_drag_motion_cb (GtkWidget *widget,
3320 GdkEventMotion *event,
3323 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3325 gint x_root, y_root;
3329 GdkDisplay *display = gtk_widget_get_display (widget);
3331 gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
3332 event->x_root = x_root;
3333 event->y_root = y_root;
3336 screen = gdk_event_get_screen ((GdkEvent *)event);
3338 gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
3343 /*************************************************************
3345 * "key_press/release_event" callback during drag.
3349 *************************************************************/
3352 gtk_drag_key_cb (GtkWidget *widget,
3356 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3357 GdkModifierType state;
3358 GdkWindow *root_window;
3360 if (event->type == GDK_KEY_PRESS)
3362 if (event->keyval == GDK_Escape)
3364 gtk_drag_cancel (info, event->time);
3370 /* Now send a "motion" so that the modifier state is updated */
3372 /* The state is not yet updated in the event, so we need
3373 * to query it here. We could use XGetModifierMapping, but
3374 * that would be overkill.
3376 root_window = gtk_widget_get_root_window (widget);
3377 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3379 event->state = state;
3380 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
3385 /*************************************************************
3386 * gtk_drag_button_release_cb:
3387 * "button_release_event" callback during drag.
3391 *************************************************************/
3394 gtk_drag_button_release_cb (GtkWidget *widget,
3395 GdkEventButton *event,
3398 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3400 if (event->button != info->button)
3403 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3405 gtk_drag_end (info, event->time);
3406 gtk_drag_drop (info, event->time);
3410 gtk_drag_cancel (info, event->time);
3417 gtk_drag_abort_timeout (gpointer data)
3419 GtkDragSourceInfo *info = data;
3420 guint32 time = GDK_CURRENT_TIME;
3422 GDK_THREADS_ENTER ();
3424 if (info->proxy_dest)
3425 time = info->proxy_dest->proxy_drop_time;
3427 info->drop_timeout = 0;
3428 gtk_drag_drop_finished (info, FALSE, time);
3430 GDK_THREADS_LEAVE ();
3436 * gtk_drag_check_threshold:
3437 * @widget: a #GtkWidget
3438 * @start_x: X coordinate of start of drag
3439 * @start_y: Y coordinate of start of drag
3440 * @current_x: current X coordinate
3441 * @current_y: current Y coordinate
3443 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3444 * at (@current_x, @current_y) has passed the GTK+ drag threshhold, and thus
3445 * should trigger the beginning of a drag-and-drop operation.
3447 * Return Value: %TRUE if the drag threshold has been passed.
3450 gtk_drag_check_threshold (GtkWidget *widget,
3456 gint drag_threshold;
3458 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3460 g_object_get (gtk_widget_get_settings (widget),
3461 "gtk-dnd-drag-threshold", &drag_threshold,
3464 return (ABS (current_x - start_x) > drag_threshold ||
3465 ABS (current_y - start_y) > drag_threshold);