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() "
1012 "to make the widget into a drag destination");
1017 gtk_target_list_ref (target_list);
1019 if (site->target_list)
1020 gtk_target_list_unref (site->target_list);
1022 site->target_list = target_list;
1026 /*************************************************************
1027 * _gtk_drag_dest_handle_event:
1028 * Called from widget event handling code on Drag events
1032 * toplevel: Toplevel widget that received the event
1035 *************************************************************/
1038 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1041 GtkDragDestInfo *info;
1042 GdkDragContext *context;
1044 g_return_if_fail (toplevel != NULL);
1045 g_return_if_fail (event != NULL);
1047 context = event->dnd.context;
1049 info = gtk_drag_get_dest_info (context, TRUE);
1051 /* Find the widget for the event */
1052 switch (event->type)
1054 case GDK_DRAG_ENTER:
1057 case GDK_DRAG_LEAVE:
1060 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1061 info->widget = NULL;
1065 case GDK_DRAG_MOTION:
1066 case GDK_DROP_START:
1068 GtkDragFindData data;
1071 if (event->type == GDK_DROP_START)
1073 info->dropped = TRUE;
1074 /* We send a leave here so that the widget unhighlights
1079 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1080 info->widget = NULL;
1084 gdk_window_get_position (toplevel->window, &tx, &ty);
1086 data.x = event->dnd.x_root - tx;
1087 data.y = event->dnd.y_root - ty;
1088 data.context = context;
1091 data.toplevel = TRUE;
1092 data.callback = (event->type == GDK_DRAG_MOTION) ?
1093 gtk_drag_dest_motion : gtk_drag_dest_drop;
1094 data.time = event->dnd.time;
1096 gtk_drag_find_widget (toplevel, &data);
1098 if (info->widget && !data.found)
1100 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1101 info->widget = NULL;
1106 if (event->type == GDK_DRAG_MOTION)
1109 gdk_drag_status (context, 0, event->dnd.time);
1111 else if (event->type == GDK_DROP_START && !info->proxy_source)
1113 gdk_drop_reply (context, data.found, event->dnd.time);
1114 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1115 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1121 g_assert_not_reached ();
1126 * gtk_drag_dest_find_target:
1127 * @widget: drag destination widget
1128 * @context: drag context
1129 * @target_list: list of droppable targets, or %NULL to use
1130 * gtk_drag_dest_get_target_list (@widget).
1132 * Looks for a match between @context->targets and the
1133 * @dest_target_list, returning the first matching target, otherwise
1134 * returning %GDK_NONE. @dest_target_list should usually be the return
1135 * value from gtk_drag_dest_get_target_list(), but some widgets may
1136 * have different valid targets for different parts of the widget; in
1137 * that case, they will have to implement a drag_motion handler that
1138 * passes the correct target list to this function.
1140 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1143 gtk_drag_dest_find_target (GtkWidget *widget,
1144 GdkDragContext *context,
1145 GtkTargetList *target_list)
1148 GList *tmp_source = NULL;
1149 GtkWidget *source_widget;
1151 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1152 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1153 g_return_val_if_fail (!context->is_source, GDK_NONE);
1156 source_widget = gtk_drag_get_source_widget (context);
1158 if (target_list == NULL)
1159 target_list = gtk_drag_dest_get_target_list (widget);
1161 if (target_list == NULL)
1164 tmp_target = target_list->list;
1167 GtkTargetPair *pair = tmp_target->data;
1168 tmp_source = context->targets;
1171 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1173 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1174 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1175 return pair->target;
1179 tmp_source = tmp_source->next;
1181 tmp_target = tmp_target->next;
1188 gtk_drag_selection_received (GtkWidget *widget,
1189 GtkSelectionData *selection_data,
1193 GdkDragContext *context;
1194 GtkDragDestInfo *info;
1195 GtkWidget *drop_widget;
1199 context = g_object_get_data (G_OBJECT (widget), "drag-context");
1200 info = gtk_drag_get_dest_info (context, FALSE);
1202 if (info->proxy_data &&
1203 info->proxy_data->target == selection_data->target)
1205 gtk_selection_data_set (info->proxy_data,
1206 selection_data->type,
1207 selection_data->format,
1208 selection_data->data,
1209 selection_data->length);
1214 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1216 gtk_drag_finish (context, TRUE, FALSE, time);
1218 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1219 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1225 GtkDragDestSite *site;
1227 site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1229 if (site && site->target_list)
1233 if (gtk_target_list_find (site->target_list,
1234 selection_data->target,
1237 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1238 selection_data->length >= 0)
1239 g_signal_emit_by_name (drop_widget,
1240 "drag_data_received",
1241 context, info->drop_x, info->drop_y,
1248 g_signal_emit_by_name (drop_widget,
1249 "drag_data_received",
1250 context, info->drop_x, info->drop_y,
1255 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1258 gtk_drag_finish (context,
1259 (selection_data->length >= 0),
1260 (context->action == GDK_ACTION_MOVE),
1264 g_object_unref (drop_widget);
1267 g_signal_handlers_disconnect_by_func (widget,
1268 gtk_drag_selection_received,
1271 g_object_set_data (G_OBJECT (widget), "drag-context", NULL);
1272 g_object_unref (context);
1274 gtk_drag_release_ipc_widget (widget);
1278 prepend_and_ref_widget (GtkWidget *widget,
1281 GSList **slist_p = data;
1283 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1286 /*************************************************************
1287 * gtk_drag_find_widget:
1288 * Recursive callback used to locate widgets for
1289 * DRAG_MOTION and DROP_START events.
1293 *************************************************************/
1296 gtk_drag_find_widget (GtkWidget *widget,
1297 GtkDragFindData *data)
1299 GtkAllocation new_allocation;
1300 gint allocation_to_window_x = 0;
1301 gint allocation_to_window_y = 0;
1305 if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget))
1308 /* Note that in the following code, we only count the
1309 * position as being inside a WINDOW widget if it is inside
1310 * widget->window; points that are outside of widget->window
1311 * but within the allocation are not counted. This is consistent
1312 * with the way we highlight drag targets.
1314 * data->x,y are relative to widget->parent->window (if
1315 * widget is not a toplevel, widget->window otherwise).
1316 * We compute the allocation of widget in the same coordinates,
1317 * clipping to widget->window, and all intermediate
1318 * windows. If data->x,y is inside that, then we translate
1319 * our coordinates to be relative to widget->window and
1322 new_allocation = widget->allocation;
1327 GdkWindow *window = widget->window;
1329 /* Compute the offset from allocation-relative to
1330 * window-relative coordinates.
1332 allocation_to_window_x = widget->allocation.x;
1333 allocation_to_window_y = widget->allocation.y;
1335 if (!GTK_WIDGET_NO_WINDOW (widget))
1337 /* The allocation is relative to the parent window for
1338 * window widgets, not to widget->window.
1340 gdk_window_get_position (window, &tx, &ty);
1342 allocation_to_window_x -= tx;
1343 allocation_to_window_y -= ty;
1346 new_allocation.x = 0 + allocation_to_window_x;
1347 new_allocation.y = 0 + allocation_to_window_y;
1349 while (window && window != widget->parent->window)
1351 GdkRectangle window_rect = { 0, 0, 0, 0 };
1353 gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
1355 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1357 gdk_window_get_position (window, &tx, &ty);
1358 new_allocation.x += tx;
1360 new_allocation.y += ty;
1363 window = gdk_window_get_parent (window);
1366 if (!window) /* Window and widget heirarchies didn't match. */
1370 if (data->toplevel ||
1371 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1372 (data->x < new_allocation.x + new_allocation.width) &&
1373 (data->y < new_allocation.y + new_allocation.height)))
1375 /* First, check if the drag is in a valid drop site in
1376 * one of our children
1378 if (GTK_IS_CONTAINER (widget))
1380 GtkDragFindData new_data = *data;
1381 GSList *children = NULL;
1384 new_data.x -= x_offset;
1385 new_data.y -= y_offset;
1386 new_data.found = FALSE;
1387 new_data.toplevel = FALSE;
1389 /* need to reference children temporarily in case the
1390 * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1392 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1393 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1395 if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1396 gtk_drag_find_widget (tmp_list->data, &new_data);
1397 g_object_unref (tmp_list->data);
1399 g_slist_free (children);
1401 data->found = new_data.found;
1404 /* If not, and this widget is registered as a drop site, check to
1405 * emit "drag_motion" to check if we are actually in
1409 g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1411 data->found = data->callback (widget,
1413 data->x - x_offset - allocation_to_window_x,
1414 data->y - y_offset - allocation_to_window_y,
1416 /* If so, send a "drag_leave" to the last widget */
1419 if (data->info->widget && data->info->widget != widget)
1421 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1423 data->info->widget = widget;
1430 gtk_drag_proxy_begin (GtkWidget *widget,
1431 GtkDragDestInfo *dest_info,
1434 GtkDragSourceInfo *source_info;
1436 GdkDragContext *context;
1437 GtkWidget *ipc_widget;
1439 if (dest_info->proxy_source)
1441 gdk_drag_abort (dest_info->proxy_source->context, time);
1442 gtk_drag_source_info_destroy (dest_info->proxy_source);
1443 dest_info->proxy_source = NULL;
1446 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1447 context = gdk_drag_begin (ipc_widget->window,
1448 dest_info->context->targets);
1450 source_info = gtk_drag_get_source_info (context, TRUE);
1452 source_info->ipc_widget = ipc_widget;
1453 source_info->widget = gtk_widget_ref (widget);
1455 source_info->target_list = gtk_target_list_new (NULL, 0);
1456 tmp_list = dest_info->context->targets;
1459 gtk_target_list_add (source_info->target_list,
1460 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1461 tmp_list = tmp_list->next;
1464 source_info->proxy_dest = dest_info;
1466 g_signal_connect (ipc_widget,
1468 G_CALLBACK (gtk_drag_selection_get),
1471 dest_info->proxy_source = source_info;
1475 gtk_drag_dest_info_destroy (gpointer data)
1477 GtkDragDestInfo *info = data;
1482 static GtkDragDestInfo *
1483 gtk_drag_get_dest_info (GdkDragContext *context,
1486 GtkDragDestInfo *info;
1487 static GQuark info_quark = 0;
1489 info_quark = g_quark_from_static_string ("gtk-dest-info");
1491 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1492 if (!info && create)
1494 info = g_new (GtkDragDestInfo, 1);
1495 info->widget = NULL;
1496 info->context = context;
1497 info->proxy_source = NULL;
1498 info->proxy_data = NULL;
1499 info->dropped = FALSE;
1500 info->proxy_drop_wait = FALSE;
1501 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1502 info, gtk_drag_dest_info_destroy);
1508 static GQuark dest_info_quark = 0;
1510 static GtkDragSourceInfo *
1511 gtk_drag_get_source_info (GdkDragContext *context,
1514 GtkDragSourceInfo *info;
1515 if (!dest_info_quark)
1516 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1518 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1519 if (!info && create)
1521 info = g_new0 (GtkDragSourceInfo, 1);
1522 info->context = context;
1523 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1530 gtk_drag_clear_source_info (GdkDragContext *context)
1532 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1536 gtk_drag_dest_realized (GtkWidget *widget)
1538 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1540 if (GTK_WIDGET_TOPLEVEL (toplevel))
1541 gdk_window_register_dnd (toplevel->window);
1545 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1546 GtkWidget *previous_toplevel)
1548 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1550 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1551 gdk_window_register_dnd (toplevel->window);
1555 gtk_drag_dest_site_destroy (gpointer data)
1557 GtkDragDestSite *site = data;
1559 if (site->proxy_window)
1560 g_object_unref (site->proxy_window);
1562 if (site->target_list)
1563 gtk_target_list_unref (site->target_list);
1569 * Default drag handlers
1572 gtk_drag_dest_leave (GtkWidget *widget,
1573 GdkDragContext *context,
1576 GtkDragDestSite *site;
1578 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1579 g_return_if_fail (site != NULL);
1583 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1585 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1587 gdk_drag_abort (info->proxy_source->context, time);
1588 gtk_drag_source_info_destroy (info->proxy_source);
1589 info->proxy_source = NULL;
1596 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1597 gtk_drag_unhighlight (widget);
1599 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1600 g_signal_emit_by_name (widget, "drag_leave",
1603 site->have_drag = FALSE;
1608 gtk_drag_dest_motion (GtkWidget *widget,
1609 GdkDragContext *context,
1614 GtkDragDestSite *site;
1615 GdkDragAction action = 0;
1618 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1619 g_return_val_if_fail (site != NULL, FALSE);
1624 GdkEvent *current_event;
1625 GdkWindow *dest_window;
1626 GdkDragProtocol proto;
1628 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1630 if (!info->proxy_source || info->proxy_source->widget != widget)
1631 gtk_drag_proxy_begin (widget, info, time);
1633 current_event = gtk_get_current_event ();
1635 if (site->proxy_window)
1637 dest_window = site->proxy_window;
1638 proto = site->proxy_protocol;
1642 gdk_drag_find_window_for_screen (info->proxy_source->context,
1644 gdk_drawable_get_screen (current_event->dnd.window),
1645 current_event->dnd.x_root,
1646 current_event->dnd.y_root,
1647 &dest_window, &proto);
1650 gdk_drag_motion (info->proxy_source->context,
1652 current_event->dnd.x_root,
1653 current_event->dnd.y_root,
1654 context->suggested_action,
1655 context->actions, time);
1657 if (!site->proxy_window && dest_window)
1658 g_object_unref (dest_window);
1660 selection = gdk_drag_get_selection (info->proxy_source->context);
1662 selection != gdk_drag_get_selection (info->context))
1663 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1665 gdk_event_free (current_event);
1670 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1672 if (context->suggested_action & site->actions)
1673 action = context->suggested_action;
1680 if ((site->actions & (1 << i)) &&
1681 (context->actions & (1 << i)))
1689 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1691 if (!site->have_drag)
1693 site->have_drag = TRUE;
1694 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1695 gtk_drag_highlight (widget);
1698 gdk_drag_status (context, action, time);
1702 gdk_drag_status (context, 0, time);
1707 g_signal_emit_by_name (widget, "drag_motion",
1708 context, x, y, time, &retval);
1710 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1714 gtk_drag_dest_drop (GtkWidget *widget,
1715 GdkDragContext *context,
1720 GtkDragDestSite *site;
1721 GtkDragDestInfo *info;
1723 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1724 g_return_val_if_fail (site != NULL, FALSE);
1726 info = gtk_drag_get_dest_info (context, FALSE);
1727 g_return_val_if_fail (info != NULL, FALSE);
1734 if (info->proxy_source ||
1735 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1737 gtk_drag_drop (info->proxy_source, time);
1741 /* We need to synthesize a motion event, wait for a status,
1742 * and, if we get a good one, do a drop.
1745 GdkEvent *current_event;
1747 GdkWindow *dest_window;
1748 GdkDragProtocol proto;
1750 gtk_drag_proxy_begin (widget, info, time);
1751 info->proxy_drop_wait = TRUE;
1752 info->proxy_drop_time = time;
1754 current_event = gtk_get_current_event ();
1756 if (site->proxy_window)
1758 dest_window = site->proxy_window;
1759 proto = site->proxy_protocol;
1763 gdk_drag_find_window_for_screen (info->proxy_source->context,
1765 gdk_drawable_get_screen (current_event->dnd.window),
1766 current_event->dnd.x_root,
1767 current_event->dnd.y_root,
1768 &dest_window, &proto);
1771 gdk_drag_motion (info->proxy_source->context,
1773 current_event->dnd.x_root,
1774 current_event->dnd.y_root,
1775 context->suggested_action,
1776 context->actions, time);
1778 if (!site->proxy_window && dest_window)
1779 g_object_unref (dest_window);
1781 selection = gdk_drag_get_selection (info->proxy_source->context);
1783 selection != gdk_drag_get_selection (info->context))
1784 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1786 gdk_event_free (current_event);
1795 if (site->flags & GTK_DEST_DEFAULT_DROP)
1797 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1799 if (target == GDK_NONE)
1801 gtk_drag_finish (context, FALSE, FALSE, time);
1805 gtk_drag_get_data (widget, context, target, time);
1808 g_signal_emit_by_name (widget, "drag_drop",
1809 context, x, y, time, &retval);
1811 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1819 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
1820 * so that we can set the icon from the source site information
1822 static GdkDragContext *
1823 gtk_drag_begin_internal (GtkWidget *widget,
1824 GtkDragSourceSite *site,
1825 GtkTargetList *target_list,
1826 GdkDragAction actions,
1830 GtkDragSourceInfo *info;
1831 GList *targets = NULL;
1833 guint32 time = GDK_CURRENT_TIME;
1834 GdkDragAction possible_actions, suggested_action;
1835 GdkDragContext *context;
1836 GtkWidget *ipc_widget;
1839 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1841 gtk_drag_get_event_actions (event, button, actions,
1842 &suggested_action, &possible_actions);
1844 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), suggested_action);
1847 time = gdk_event_get_time (event);
1849 if (gdk_pointer_grab (ipc_widget->window, FALSE,
1850 GDK_POINTER_MOTION_MASK |
1851 GDK_BUTTON_RELEASE_MASK, NULL,
1854 gtk_drag_release_ipc_widget (ipc_widget);
1858 if (gdk_keyboard_grab (ipc_widget->window, FALSE, time) != 0)
1860 gtk_drag_release_ipc_widget (ipc_widget);
1864 /* We use a GTK grab here to override any grabs that the widget
1865 * we are dragging from might have held
1867 gtk_grab_add (ipc_widget);
1869 tmp_list = g_list_last (target_list->list);
1872 GtkTargetPair *pair = tmp_list->data;
1873 targets = g_list_prepend (targets,
1874 GINT_TO_POINTER (pair->target));
1875 tmp_list = tmp_list->prev;
1878 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1880 context = gdk_drag_begin (ipc_widget->window, targets);
1881 g_list_free (targets);
1883 info = gtk_drag_get_source_info (context, TRUE);
1885 info->ipc_widget = ipc_widget;
1886 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", info);
1888 info->widget = gtk_widget_ref (widget);
1890 info->button = button;
1891 info->cursor = cursor;
1892 info->target_list = target_list;
1893 gtk_target_list_ref (target_list);
1895 info->possible_actions = actions;
1897 info->status = GTK_DRAG_STATUS_DRAG;
1898 info->last_event = NULL;
1899 info->selections = NULL;
1900 info->icon_window = NULL;
1901 info->destroy_icon = FALSE;
1903 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1904 * the drag icon, it will be in the right place
1906 if (event && event->type == GDK_MOTION_NOTIFY)
1908 info->cur_screen = gtk_widget_get_screen (widget);
1909 info->cur_x = event->motion.x_root;
1910 info->cur_y = event->motion.y_root;
1914 gdk_display_get_pointer (gtk_widget_get_display (widget),
1915 &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
1918 g_signal_emit_by_name (widget, "drag_begin",
1921 /* Ensure that we have an icon before we start the drag; the
1922 * application may have set one in ::drag_begin, or it may
1925 if (!info->icon_window)
1927 if (!site || site->icon_type == GTK_IMAGE_EMPTY)
1928 gtk_drag_set_icon_default (context);
1930 switch (site->icon_type)
1932 case GTK_IMAGE_PIXMAP:
1933 gtk_drag_set_icon_pixmap (context,
1935 site->icon_data.pixmap.pixmap,
1939 case GTK_IMAGE_PIXBUF:
1940 gtk_drag_set_icon_pixbuf (context,
1941 site->icon_data.pixbuf.pixbuf,
1944 case GTK_IMAGE_STOCK:
1945 gtk_drag_set_icon_stock (context,
1946 site->icon_data.stock.stock_id,
1949 case GTK_IMAGE_EMPTY:
1951 g_assert_not_reached();
1956 if (event && event->type == GDK_MOTION_NOTIFY)
1957 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1959 info->start_x = info->cur_x;
1960 info->start_y = info->cur_y;
1962 g_signal_connect (info->ipc_widget, "button_release_event",
1963 G_CALLBACK (gtk_drag_button_release_cb), info);
1964 g_signal_connect (info->ipc_widget, "motion_notify_event",
1965 G_CALLBACK (gtk_drag_motion_cb), info);
1966 g_signal_connect (info->ipc_widget, "key_press_event",
1967 G_CALLBACK (gtk_drag_key_cb), info);
1968 g_signal_connect (info->ipc_widget, "key_release_event",
1969 G_CALLBACK (gtk_drag_key_cb), info);
1970 g_signal_connect (info->ipc_widget, "selection_get",
1971 G_CALLBACK (gtk_drag_selection_get), info);
1973 info->have_grab = TRUE;
1974 info->grab_time = time;
1976 return info->context;
1981 * @widget: the source widget.
1982 * @targets: The targets (data formats) in which the
1983 * source can provide the data.
1984 * @actions: A bitmask of the allowed drag actions for this drag.
1985 * @button: The button the user clicked to start the drag.
1986 * @event: The event that triggered the start of the drag.
1988 * Initiates a drag on the source side. The function
1989 * only needs to be used when the application is
1990 * starting drags itself, and is not needed when
1991 * gtk_drag_source_set() is used.
1993 * Return value: the context for this drag.
1996 gtk_drag_begin (GtkWidget *widget,
1997 GtkTargetList *targets,
1998 GdkDragAction actions,
2002 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2003 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
2004 g_return_val_if_fail (targets != NULL, NULL);
2006 return gtk_drag_begin_internal (widget, NULL, targets,
2007 actions, button, event);
2010 /*************************************************************
2011 * gtk_drag_source_set:
2012 * Register a drop site, and possibly add default behaviors.
2015 * start_button_mask: Mask of allowed buttons to start drag
2016 * targets: Table of targets for this source
2018 * actions: Actions allowed for this source
2020 *************************************************************/
2023 gtk_drag_source_set (GtkWidget *widget,
2024 GdkModifierType start_button_mask,
2025 const GtkTargetEntry *targets,
2027 GdkDragAction actions)
2029 GtkDragSourceSite *site;
2031 g_return_if_fail (GTK_IS_WIDGET (widget));
2033 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2035 gtk_widget_add_events (widget,
2036 gtk_widget_get_events (widget) |
2037 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2038 GDK_BUTTON_MOTION_MASK);
2042 if (site->target_list)
2043 gtk_target_list_unref (site->target_list);
2047 site = g_new0 (GtkDragSourceSite, 1);
2049 site->icon_type = GTK_IMAGE_EMPTY;
2051 g_signal_connect (widget, "button_press_event",
2052 G_CALLBACK (gtk_drag_source_event_cb),
2054 g_signal_connect (widget, "motion_notify_event",
2055 G_CALLBACK (gtk_drag_source_event_cb),
2058 g_object_set_data_full (G_OBJECT (widget),
2060 site, gtk_drag_source_site_destroy);
2063 site->start_button_mask = start_button_mask;
2066 site->target_list = gtk_target_list_new (targets, n_targets);
2068 site->target_list = NULL;
2070 site->actions = actions;
2074 /*************************************************************
2075 * gtk_drag_source_unset
2076 * Unregister this widget as a drag source.
2080 *************************************************************/
2083 gtk_drag_source_unset (GtkWidget *widget)
2085 GtkDragSourceSite *site;
2087 g_return_if_fail (GTK_IS_WIDGET (widget));
2089 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2093 g_signal_handlers_disconnect_by_func (widget,
2094 gtk_drag_source_event_cb,
2096 g_signal_handlers_disconnect_by_func (widget,
2097 gtk_drag_source_event_cb,
2099 g_object_set_data (G_OBJECT (widget), "gtk-site-data", NULL);
2104 * gtk_drag_source_get_target_list:
2105 * @widget: a #GtkWidget
2107 * Gets the list of targets this widget can provide for
2110 * Return value: the #GtkTargetList, or %NULL if none
2113 gtk_drag_source_get_target_list (GtkWidget *widget)
2115 GtkDragSourceSite *site;
2117 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2119 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2121 return site ? site->target_list : NULL;
2125 * gtk_drag_source_set_target_list:
2126 * @widget: a #GtkWidget that's a drag source
2127 * @target_list: list of draggable targets, or %NULL for none
2129 * Changes the target types that this widget offers for drag-and-drop.
2130 * The widget must first be made into a drag source with
2131 * gtk_drag_source_set().
2134 gtk_drag_source_set_target_list (GtkWidget *widget,
2135 GtkTargetList *target_list)
2137 GtkDragSourceSite *site;
2139 g_return_if_fail (GTK_IS_WIDGET (widget));
2141 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2144 g_warning ("gtk_drag_source_set_target_list() requires the widget "
2145 "to already be a drag source.");
2150 gtk_target_list_ref (target_list);
2152 if (site->target_list)
2153 gtk_target_list_unref (site->target_list);
2155 site->target_list = target_list;
2159 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2161 switch (site->icon_type)
2163 case GTK_IMAGE_EMPTY:
2165 case GTK_IMAGE_PIXMAP:
2166 if (site->icon_data.pixmap.pixmap)
2167 g_object_unref (site->icon_data.pixmap.pixmap);
2168 if (site->icon_mask)
2169 g_object_unref (site->icon_mask);
2171 case GTK_IMAGE_PIXBUF:
2172 g_object_unref (site->icon_data.pixbuf.pixbuf);
2174 case GTK_IMAGE_STOCK:
2175 g_free (site->icon_data.stock.stock_id);
2178 g_assert_not_reached();
2181 site->icon_type = GTK_IMAGE_EMPTY;
2184 g_object_unref (site->colormap);
2185 site->colormap = NULL;
2189 * gtk_drag_source_set_icon:
2190 * @widget: a #GtkWidget
2191 * @colormap: the colormap of the icon
2192 * @pixmap: the image data for the icon
2193 * @mask: the transparency mask for an image.
2195 * Sets the icon that will be used for drags from a particular widget
2196 * from a pixmap/mask. GTK+ retains references for the arguments, and
2197 * will release them when they are no longer needed.
2198 * Use gtk_drag_source_set_icon_pixbuf() instead.
2201 gtk_drag_source_set_icon (GtkWidget *widget,
2202 GdkColormap *colormap,
2206 GtkDragSourceSite *site;
2208 g_return_if_fail (GTK_IS_WIDGET (widget));
2209 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2210 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2211 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2213 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2214 g_return_if_fail (site != NULL);
2216 g_object_ref (colormap);
2217 g_object_ref (pixmap);
2219 g_object_ref (mask);
2221 gtk_drag_source_unset_icon (site);
2223 site->icon_type = GTK_IMAGE_PIXMAP;
2225 site->icon_data.pixmap.pixmap = pixmap;
2226 site->icon_mask = mask;
2227 site->colormap = colormap;
2231 * gtk_drag_source_set_icon_pixbuf:
2232 * @widget: a #GtkWidget
2233 * @pixbuf: the #GdkPixbuf for the drag icon
2235 * Sets the icon that will be used for drags from a particular widget
2236 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2237 * release it when it is no longer needed.
2240 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2243 GtkDragSourceSite *site;
2245 g_return_if_fail (GTK_IS_WIDGET (widget));
2246 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2248 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2249 g_return_if_fail (site != NULL);
2250 g_object_ref (pixbuf);
2252 gtk_drag_source_unset_icon (site);
2254 site->icon_type = GTK_IMAGE_PIXBUF;
2255 site->icon_data.pixbuf.pixbuf = pixbuf;
2259 * gtk_drag_source_set_icon_stock:
2260 * @widget: a #GtkWidget
2261 * @stock_id: the ID of the stock icon to use
2263 * Sets the icon that will be used for drags from a particular source
2267 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2268 const gchar *stock_id)
2270 GtkDragSourceSite *site;
2272 g_return_if_fail (GTK_IS_WIDGET (widget));
2273 g_return_if_fail (stock_id != NULL);
2275 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2276 g_return_if_fail (site != NULL);
2278 gtk_drag_source_unset_icon (site);
2280 site->icon_type = GTK_IMAGE_STOCK;
2281 site->icon_data.stock.stock_id = g_strdup (stock_id);
2285 gtk_drag_get_icon (GtkDragSourceInfo *info,
2286 GtkWidget **icon_window,
2290 if (get_can_change_screen (info->icon_window))
2291 gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2294 if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2296 if (!info->fallback_icon)
2298 gint save_hot_x, save_hot_y;
2299 gboolean save_destroy_icon;
2300 GtkWidget *save_icon_window;
2302 /* HACK to get the appropriate icon
2304 save_icon_window = info->icon_window;
2305 save_hot_x = info->hot_x;
2306 save_hot_y = info->hot_x;
2307 save_destroy_icon = info->destroy_icon;
2309 info->icon_window = NULL;
2310 gtk_drag_set_icon_default (info->context);
2311 info->fallback_icon = info->icon_window;
2313 info->icon_window = save_icon_window;
2314 info->hot_x = save_hot_x;
2315 info->hot_y = save_hot_y;
2316 info->destroy_icon = save_destroy_icon;
2319 gtk_widget_hide (info->icon_window);
2321 *icon_window = info->fallback_icon;
2322 gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2324 if (!default_icon_pixmap)
2331 *hot_x = default_icon_hot_x;
2332 *hot_y = default_icon_hot_y;
2337 if (info->fallback_icon)
2338 gtk_widget_hide (info->fallback_icon);
2340 *icon_window = info->icon_window;
2341 *hot_x = info->hot_x;
2342 *hot_y = info->hot_y;
2347 gtk_drag_update_icon (GtkDragSourceInfo *info)
2349 if (info->icon_window)
2351 GtkWidget *icon_window;
2354 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2356 gtk_window_move (GTK_WINDOW (icon_window),
2357 info->cur_x - hot_x,
2358 info->cur_y - hot_y);
2360 if (GTK_WIDGET_VISIBLE (icon_window))
2361 gdk_window_raise (icon_window->window);
2363 gtk_widget_show (icon_window);
2368 gtk_drag_set_icon_window (GdkDragContext *context,
2372 gboolean destroy_on_release)
2374 GtkDragSourceInfo *info;
2376 info = gtk_drag_get_source_info (context, FALSE);
2377 gtk_drag_remove_icon (info);
2380 gtk_widget_ref (widget);
2382 info->icon_window = widget;
2383 info->hot_x = hot_x;
2384 info->hot_y = hot_y;
2385 info->destroy_icon = destroy_on_release;
2387 gtk_drag_update_icon (info);
2391 * gtk_drag_set_icon_widget:
2392 * @context: the context for a drag. (This must be called
2393 with a context for the source side of a drag)
2394 * @widget: a toplevel window to use as an icon.
2395 * @hot_x: the X offset within @widget of the hotspot.
2396 * @hot_y: the Y offset within @widget of the hotspot.
2398 * Changes the icon for a widget to a given widget. GTK+
2399 * will not destroy the icon, so if you don't want
2400 * it to persist, you should connect to the "drag_end"
2401 * signal and destroy it yourself.
2404 gtk_drag_set_icon_widget (GdkDragContext *context,
2409 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2410 g_return_if_fail (context->is_source);
2411 g_return_if_fail (GTK_IS_WIDGET (widget));
2413 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2417 icon_window_realize (GtkWidget *window,
2423 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
2424 gtk_widget_get_colormap (window),
2425 &pixmap, &mask, 128);
2427 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2430 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2432 g_object_unref (pixmap);
2435 g_object_unref (mask);
2439 set_icon_stock_pixbuf (GdkDragContext *context,
2440 const gchar *stock_id,
2449 g_return_if_fail (context != NULL);
2450 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2451 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2453 screen = gdk_drawable_get_screen (context->source_window);
2455 /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
2456 gtk_widget_push_colormap (NULL);
2457 window = gtk_window_new (GTK_WINDOW_POPUP);
2458 gtk_window_set_screen (GTK_WINDOW (window), screen);
2459 set_can_change_screen (window, TRUE);
2460 gtk_widget_pop_colormap ();
2462 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2463 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2467 pixbuf = gtk_widget_render_icon (window, stock_id,
2468 GTK_ICON_SIZE_DND, NULL);
2472 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2473 gtk_widget_destroy (window);
2479 g_object_ref (pixbuf);
2481 width = gdk_pixbuf_get_width (pixbuf);
2482 height = gdk_pixbuf_get_width (pixbuf);
2484 gtk_widget_set_size_request (window,
2485 gdk_pixbuf_get_width (pixbuf),
2486 gdk_pixbuf_get_height (pixbuf));
2488 g_signal_connect_closure (window, "realize",
2489 g_cclosure_new (G_CALLBACK (icon_window_realize),
2491 (GClosureNotify)g_object_unref),
2494 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2498 * gtk_drag_set_icon_pixbuf:
2499 * @context: the context for a drag. (This must be called
2500 * with a context for the source side of a drag)
2501 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2502 * @hot_x: the X offset within @widget of the hotspot.
2503 * @hot_y: the Y offset within @widget of the hotspot.
2505 * Sets @pixbuf as the icon for a given drag.
2508 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2513 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2514 g_return_if_fail (context->is_source);
2515 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2517 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2521 * gtk_drag_set_icon_stock:
2522 * @context: the context for a drag. (This must be called
2523 * with a context for the source side of a drag)
2524 * @stock_id: the ID of the stock icon to use for the drag.
2525 * @hot_x: the X offset within the icon of the hotspot.
2526 * @hot_y: the Y offset within the icon of the hotspot.
2528 * Sets the the icon for a given drag from a stock ID.
2531 gtk_drag_set_icon_stock (GdkDragContext *context,
2532 const gchar *stock_id,
2536 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2537 g_return_if_fail (context->is_source);
2538 g_return_if_fail (stock_id != NULL);
2540 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2544 * gtk_drag_set_icon_pixmap:
2545 * @context: the context for a drag. (This must be called
2546 * with a context for the source side of a drag)
2547 * @colormap: the colormap of the icon
2548 * @pixmap: the image data for the icon
2549 * @mask: the transparency mask for the icon
2550 * @hot_x: the X offset within @pixmap of the hotspot.
2551 * @hot_y: the Y offset within @pixmap of the hotspot.
2553 * Sets @pixmap as the icon for a given drag. GTK+ retains
2554 * references for the arguments, and will release them when
2555 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2556 * will be more convenient to use.
2559 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2560 GdkColormap *colormap,
2570 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2571 g_return_if_fail (context->is_source);
2572 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2573 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2574 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2576 screen = gdk_colormap_get_screen (colormap);
2578 g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
2579 g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
2581 gdk_drawable_get_size (pixmap, &width, &height);
2583 gtk_widget_push_colormap (colormap);
2585 window = gtk_window_new (GTK_WINDOW_POPUP);
2586 gtk_window_set_screen (GTK_WINDOW (window), screen);
2587 set_can_change_screen (window, FALSE);
2588 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2589 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2591 gtk_widget_pop_colormap ();
2593 gtk_widget_set_size_request (window, width, height);
2594 gtk_widget_realize (window);
2596 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2599 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2601 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2605 * gtk_drag_set_icon_default:
2606 * @context: the context for a drag. (This must be called
2607 with a context for the source side of a drag)
2609 * Sets the icon for a particular drag to the default
2613 gtk_drag_set_icon_default (GdkDragContext *context)
2615 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2616 g_return_if_fail (context->is_source);
2618 if (!default_icon_pixmap)
2619 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2621 gtk_drag_set_icon_pixmap (context,
2622 default_icon_colormap,
2623 default_icon_pixmap,
2626 default_icon_hot_y);
2630 * gtk_drag_set_default_icon:
2631 * @colormap: the colormap of the icon
2632 * @pixmap: the image data for the icon
2633 * @mask: the transparency mask for an image.
2634 * @hot_x: The X offset within @widget of the hotspot.
2635 * @hot_y: The Y offset within @widget of the hotspot.
2637 * Changes the default drag icon. GTK+ retains references for the
2638 * arguments, and will release them when they are no longer needed.
2639 * This function is obsolete. The default icon should now be changed
2640 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2643 gtk_drag_set_default_icon (GdkColormap *colormap,
2649 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2650 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2651 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2653 if (default_icon_colormap)
2654 g_object_unref (default_icon_colormap);
2655 if (default_icon_pixmap)
2656 g_object_unref (default_icon_pixmap);
2657 if (default_icon_mask)
2658 g_object_unref (default_icon_mask);
2660 default_icon_colormap = colormap;
2661 g_object_ref (colormap);
2663 default_icon_pixmap = pixmap;
2664 g_object_ref (pixmap);
2666 default_icon_mask = mask;
2668 g_object_ref (mask);
2670 default_icon_hot_x = hot_x;
2671 default_icon_hot_y = hot_y;
2675 /*************************************************************
2676 * _gtk_drag_source_handle_event:
2677 * Called from widget event handling code on Drag events
2681 * toplevel: Toplevel widget that received the event
2684 *************************************************************/
2687 _gtk_drag_source_handle_event (GtkWidget *widget,
2690 GtkDragSourceInfo *info;
2691 GdkDragContext *context;
2693 g_return_if_fail (widget != NULL);
2694 g_return_if_fail (event != NULL);
2696 context = event->dnd.context;
2697 info = gtk_drag_get_source_info (context, FALSE);
2701 switch (event->type)
2703 case GDK_DRAG_STATUS:
2707 if (info->proxy_dest)
2709 if (!event->dnd.send_event)
2711 if (info->proxy_dest->proxy_drop_wait)
2713 gboolean result = context->action != 0;
2715 /* Aha - we can finally pass the MOTIF DROP on... */
2716 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2718 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2720 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2724 gdk_drag_status (info->proxy_dest->context,
2725 event->dnd.context->action,
2730 else if (info->have_grab)
2732 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2733 event->dnd.context->action);
2734 if (info->cursor != cursor)
2736 gdk_pointer_grab (widget->window, FALSE,
2737 GDK_POINTER_MOTION_MASK |
2738 GDK_BUTTON_RELEASE_MASK,
2740 cursor, info->grab_time);
2741 info->cursor = cursor;
2744 if (info->last_event)
2745 gtk_drag_add_update_idle (info);
2750 case GDK_DROP_FINISHED:
2751 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2754 g_assert_not_reached ();
2758 /*************************************************************
2759 * gtk_drag_source_check_selection:
2760 * Check if we've set up handlers/claimed the selection
2761 * for a given drag. If not, add them.
2765 *************************************************************/
2768 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2774 tmp_list = info->selections;
2777 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2779 tmp_list = tmp_list->next;
2782 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2786 info->selections = g_list_prepend (info->selections,
2787 GUINT_TO_POINTER (selection));
2789 tmp_list = info->target_list->list;
2792 GtkTargetPair *pair = tmp_list->data;
2794 gtk_selection_add_target (info->ipc_widget,
2798 tmp_list = tmp_list->next;
2801 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2803 gtk_selection_add_target (info->ipc_widget,
2805 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2806 TARGET_MOTIF_SUCCESS);
2807 gtk_selection_add_target (info->ipc_widget,
2809 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2810 TARGET_MOTIF_FAILURE);
2813 gtk_selection_add_target (info->ipc_widget,
2815 gdk_atom_intern ("DELETE", FALSE),
2819 /*************************************************************
2820 * gtk_drag_drop_finished:
2821 * Clean up from the drag, and display snapback, if necessary.
2827 *************************************************************/
2830 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2834 gtk_drag_source_release_selections (info, time);
2836 if (info->proxy_dest)
2838 /* The time from the event isn't reliable for Xdnd drags */
2839 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2840 info->proxy_dest->proxy_drop_time);
2841 gtk_drag_source_info_destroy (info);
2847 gtk_drag_source_info_destroy (info);
2851 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2855 anim->n_steps = MAX (info->cur_x - info->start_x,
2856 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2857 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2859 info->cur_screen = gtk_widget_get_screen (info->widget);
2860 gtk_drag_update_icon (info);
2862 /* Mark the context as dead, so if the destination decides
2863 * to respond really late, we still are OK.
2865 gtk_drag_clear_source_info (info->context);
2866 g_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2872 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2875 GdkDisplay *display = gtk_widget_get_display (info->widget);
2876 GList *tmp_list = info->selections;
2880 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2881 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2882 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2884 tmp_list = tmp_list->next;
2887 g_list_free (info->selections);
2888 info->selections = NULL;
2891 /*************************************************************
2893 * Send a drop event.
2897 *************************************************************/
2900 gtk_drag_drop (GtkDragSourceInfo *info,
2903 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2905 GtkSelectionData selection_data;
2907 /* GTK+ traditionally has used application/x-rootwin-drop, but the
2908 * XDND spec specifies x-rootwindow-drop.
2910 GdkAtom target1 = gdk_atom_intern ("application/x-rootwindow-drop", FALSE);
2911 GdkAtom target2 = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2913 tmp_list = info->target_list->list;
2916 GtkTargetPair *pair = tmp_list->data;
2918 if (pair->target == target1 || pair->target == target2)
2920 selection_data.selection = GDK_NONE;
2921 selection_data.target = pair->target;
2922 selection_data.data = NULL;
2923 selection_data.length = -1;
2925 g_signal_emit_by_name (info->widget, "drag_data_get",
2926 info->context, &selection_data,
2930 /* FIXME: Should we check for length >= 0 here? */
2931 gtk_drag_drop_finished (info, TRUE, time);
2934 tmp_list = tmp_list->next;
2936 gtk_drag_drop_finished (info, FALSE, time);
2940 if (info->icon_window)
2941 gtk_widget_hide (info->icon_window);
2943 gdk_drag_drop (info->context, time);
2944 info->drop_timeout = g_timeout_add (DROP_ABORT_TIME,
2945 gtk_drag_abort_timeout,
2951 * Source side callbacks.
2955 gtk_drag_source_event_cb (GtkWidget *widget,
2959 GtkDragSourceSite *site;
2960 gboolean retval = FALSE;
2961 site = (GtkDragSourceSite *)data;
2963 switch (event->type)
2965 case GDK_BUTTON_PRESS:
2966 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2968 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2969 site->x = event->button.x;
2970 site->y = event->button.y;
2974 case GDK_BUTTON_RELEASE:
2975 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2976 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2979 case GDK_MOTION_NOTIFY:
2980 if (site->state & event->motion.state & site->start_button_mask)
2982 /* FIXME: This is really broken and can leave us
2988 if (site->state & event->motion.state &
2989 GDK_BUTTON1_MASK << (i - 1))
2993 if (gtk_drag_check_threshold (widget, site->x, site->y,
2994 event->motion.x, event->motion.y))
2996 GdkDragContext *context;
2999 context = gtk_drag_begin_internal (widget, site, site->target_list,
3008 default: /* hit for 2/3BUTTON_PRESS */
3016 gtk_drag_source_site_destroy (gpointer data)
3018 GtkDragSourceSite *site = data;
3020 if (site->target_list)
3021 gtk_target_list_unref (site->target_list);
3023 gtk_drag_source_unset_icon (site);
3028 gtk_drag_selection_get (GtkWidget *widget,
3029 GtkSelectionData *selection_data,
3034 GtkDragSourceInfo *info = data;
3035 static GdkAtom null_atom = GDK_NONE;
3039 null_atom = gdk_atom_intern ("NULL", FALSE);
3044 g_signal_emit_by_name (info->widget,
3047 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3049 case TARGET_MOTIF_SUCCESS:
3050 gtk_drag_drop_finished (info, TRUE, time);
3051 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3053 case TARGET_MOTIF_FAILURE:
3054 gtk_drag_drop_finished (info, FALSE, time);
3055 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3058 if (info->proxy_dest)
3060 /* This is sort of dangerous and needs to be thought
3063 info->proxy_dest->proxy_data = selection_data;
3064 gtk_drag_get_data (info->widget,
3065 info->proxy_dest->context,
3066 selection_data->target,
3069 info->proxy_dest->proxy_data = NULL;
3073 if (gtk_target_list_find (info->target_list,
3074 selection_data->target,
3077 g_signal_emit_by_name (info->widget, "drag_data_get",
3089 gtk_drag_anim_timeout (gpointer data)
3091 GtkDragAnim *anim = data;
3095 GDK_THREADS_ENTER ();
3097 if (anim->step == anim->n_steps)
3099 gtk_drag_source_info_destroy (anim->info);
3106 x = (anim->info->start_x * (anim->step + 1) +
3107 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3108 y = (anim->info->start_y * (anim->step + 1) +
3109 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3110 if (anim->info->icon_window)
3112 GtkWidget *icon_window;
3115 gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);
3117 gtk_window_move (GTK_WINDOW (icon_window),
3127 GDK_THREADS_LEAVE ();
3133 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3135 if (info->icon_window)
3137 gtk_widget_hide (info->icon_window);
3138 if (info->destroy_icon)
3139 gtk_widget_destroy (info->icon_window);
3141 if (info->fallback_icon)
3143 gtk_widget_destroy (info->fallback_icon);
3144 info->fallback_icon = NULL;
3147 g_object_unref (info->icon_window);
3148 info->icon_window = NULL;
3153 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3155 gtk_drag_remove_icon (info);
3157 if (!info->proxy_dest)
3158 g_signal_emit_by_name (info->widget, "drag_end",
3162 g_object_unref (info->widget);
3165 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3166 gtk_drag_button_release_cb,
3168 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3171 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3174 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3175 gtk_drag_selection_get,
3178 gtk_selection_remove_all (info->ipc_widget);
3179 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", NULL);
3180 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3181 gtk_drag_release_ipc_widget (info->ipc_widget);
3183 gtk_target_list_unref (info->target_list);
3185 gtk_drag_clear_source_info (info->context);
3186 g_object_unref (info->context);
3188 if (info->drop_timeout)
3189 g_source_remove (info->drop_timeout);
3195 gtk_drag_update_idle (gpointer data)
3197 GtkDragSourceInfo *info = data;
3198 GdkWindow *dest_window;
3199 GdkDragProtocol protocol;
3202 GdkDragAction action;
3203 GdkDragAction possible_actions;
3206 GDK_THREADS_ENTER ();
3208 info->update_idle = 0;
3210 time = gtk_drag_get_event_time (info->last_event);
3211 gtk_drag_get_event_actions (info->last_event,
3213 info->possible_actions,
3214 &action, &possible_actions);
3215 gtk_drag_update_icon (info);
3216 gdk_drag_find_window_for_screen (info->context,
3217 info->icon_window ? info->icon_window->window : NULL,
3218 info->cur_screen, info->cur_x, info->cur_y,
3219 &dest_window, &protocol);
3221 if (!gdk_drag_motion (info->context, dest_window, protocol,
3222 info->cur_x, info->cur_y, action,
3226 gdk_event_free ((GdkEvent *)info->last_event);
3227 info->last_event = NULL;
3231 g_object_unref (dest_window);
3233 selection = gdk_drag_get_selection (info->context);
3235 gtk_drag_source_check_selection (info, selection, time);
3237 GDK_THREADS_LEAVE ();
3243 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
3245 /* We use an idle lowerthan GDK_PRIORITY_REDRAW so that exposes
3246 * from the last move can catch up before we move again.
3248 if (!info->update_idle)
3249 info->update_idle = g_idle_add_full (GDK_PRIORITY_REDRAW + 5,
3250 gtk_drag_update_idle,
3257 * @info: DragSourceInfo for the drag
3258 * @screen: new screen
3259 * @x_root: new X position
3260 * @y_root: new y position
3261 * @event: event received requiring update
3263 * Updates the status of the drag; called when the
3264 * cursor moves or the modifier changes
3267 gtk_drag_update (GtkDragSourceInfo *info,
3273 info->cur_screen = screen;
3274 info->cur_x = x_root;
3275 info->cur_y = y_root;
3276 if (info->last_event)
3277 gdk_event_free ((GdkEvent *)info->last_event);
3278 info->last_event = gdk_event_copy ((GdkEvent *)event);
3280 gtk_drag_add_update_idle (info);
3283 /*************************************************************
3285 * Called when the user finishes to drag, either by
3286 * releasing the mouse, or by pressing Esc.
3288 * info: Source info for the drag
3289 * time: Timestamp for ending the drag
3291 *************************************************************/
3294 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3296 GdkEvent *send_event;
3297 GtkWidget *source_widget = info->widget;
3298 GdkDisplay *display = gtk_widget_get_display (source_widget);
3300 if (info->update_idle)
3302 g_source_remove (info->update_idle);
3303 info->update_idle = 0;
3306 if (info->last_event)
3308 gdk_event_free (info->last_event);
3309 info->last_event = NULL;
3312 info->have_grab = FALSE;
3314 gdk_display_pointer_ungrab (display, time);
3315 gdk_display_keyboard_ungrab (display, time);
3316 gtk_grab_remove (info->ipc_widget);
3318 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3319 gtk_drag_button_release_cb,
3321 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3324 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3328 /* Send on a release pair to the the original
3329 * widget to convince it to release its grab. We need to
3330 * call gtk_propagate_event() here, instead of
3331 * gtk_widget_event() because widget like GtkList may
3332 * expect propagation.
3335 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3336 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3337 send_event->button.send_event = TRUE;
3338 send_event->button.time = time;
3339 send_event->button.x = 0;
3340 send_event->button.y = 0;
3341 send_event->button.axes = NULL;
3342 send_event->button.state = 0;
3343 send_event->button.button = info->button;
3344 send_event->button.device = gdk_display_get_core_pointer (display);
3345 send_event->button.x_root = 0;
3346 send_event->button.y_root = 0;
3348 gtk_propagate_event (source_widget, send_event);
3349 gdk_event_free (send_event);
3352 /*************************************************************
3354 * Called on cancellation of a drag, either by the user
3355 * or programmatically.
3357 * info: Source info for the drag
3358 * time: Timestamp for ending the drag
3360 *************************************************************/
3363 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3365 gtk_drag_end (info, time);
3366 gdk_drag_abort (info->context, time);
3367 gtk_drag_drop_finished (info, FALSE, time);
3370 /*************************************************************
3371 * gtk_drag_motion_cb:
3372 * "motion_notify_event" callback during drag.
3376 *************************************************************/
3379 gtk_drag_motion_cb (GtkWidget *widget,
3380 GdkEventMotion *event,
3383 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3385 gint x_root, y_root;
3389 GdkDisplay *display = gtk_widget_get_display (widget);
3391 gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
3392 event->x_root = x_root;
3393 event->y_root = y_root;
3396 screen = gdk_event_get_screen ((GdkEvent *)event);
3398 gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
3403 /*************************************************************
3405 * "key_press/release_event" callback during drag.
3409 *************************************************************/
3412 gtk_drag_key_cb (GtkWidget *widget,
3416 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3417 GdkModifierType state;
3418 GdkWindow *root_window;
3420 if (event->type == GDK_KEY_PRESS)
3422 if (event->keyval == GDK_Escape)
3424 gtk_drag_cancel (info, event->time);
3430 /* Now send a "motion" so that the modifier state is updated */
3432 /* The state is not yet updated in the event, so we need
3433 * to query it here. We could use XGetModifierMapping, but
3434 * that would be overkill.
3436 root_window = gtk_widget_get_root_window (widget);
3437 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3439 event->state = state;
3440 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
3445 /*************************************************************
3446 * gtk_drag_button_release_cb:
3447 * "button_release_event" callback during drag.
3451 *************************************************************/
3454 gtk_drag_button_release_cb (GtkWidget *widget,
3455 GdkEventButton *event,
3458 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3460 if (event->button != info->button)
3463 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3465 gtk_drag_end (info, event->time);
3466 gtk_drag_drop (info, event->time);
3470 gtk_drag_cancel (info, event->time);
3477 gtk_drag_abort_timeout (gpointer data)
3479 GtkDragSourceInfo *info = data;
3480 guint32 time = GDK_CURRENT_TIME;
3482 GDK_THREADS_ENTER ();
3484 if (info->proxy_dest)
3485 time = info->proxy_dest->proxy_drop_time;
3487 info->drop_timeout = 0;
3488 gtk_drag_drop_finished (info, FALSE, time);
3490 GDK_THREADS_LEAVE ();
3496 * gtk_drag_check_threshold:
3497 * @widget: a #GtkWidget
3498 * @start_x: X coordinate of start of drag
3499 * @start_y: Y coordinate of start of drag
3500 * @current_x: current X coordinate
3501 * @current_y: current Y coordinate
3503 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3504 * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
3505 * should trigger the beginning of a drag-and-drop operation.
3507 * Return Value: %TRUE if the drag threshold has been passed.
3510 gtk_drag_check_threshold (GtkWidget *widget,
3516 gint drag_threshold;
3518 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3520 g_object_get (gtk_widget_get_settings (widget),
3521 "gtk-dnd-drag-threshold", &drag_threshold,
3524 return (ABS (current_x - start_x) > drag_threshold ||
3525 ABS (current_y - start_y) > drag_threshold);