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/.
28 #include "gdkconfig.h"
30 #include "gdk/gdkkeysyms.h"
34 #include "gtkinvisible.h"
37 #include "gtkwindow.h"
39 static GSList *source_widgets = NULL;
41 typedef struct _GtkDragSourceSite GtkDragSourceSite;
42 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
43 typedef struct _GtkDragDestSite GtkDragDestSite;
44 typedef struct _GtkDragDestInfo GtkDragDestInfo;
45 typedef struct _GtkDragAnim GtkDragAnim;
46 typedef struct _GtkDragFindData GtkDragFindData;
56 struct _GtkDragSourceSite
58 GdkModifierType start_button_mask;
59 GtkTargetList *target_list; /* Targets for drag data */
60 GdkDragAction actions; /* Possible actions */
63 GtkImageType icon_type;
66 GtkImagePixmapData pixmap;
67 GtkImagePixbufData pixbuf;
68 GtkImageStockData stock;
72 GdkColormap *colormap; /* Colormap for drag icon */
74 /* Stored button press information to detect drag beginning */
79 struct _GtkDragSourceInfo
82 GtkTargetList *target_list; /* Targets for drag data */
83 GdkDragAction possible_actions; /* Actions allowed by source */
84 GdkDragContext *context; /* drag context */
85 GtkWidget *icon_window; /* Window for drag */
86 GtkWidget *fallback_icon; /* Window for drag used on other screens */
87 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
88 GdkCursor *cursor; /* Cursor for drag */
89 gint hot_x, hot_y; /* Hot spot for drag */
90 gint button; /* mouse button starting drag */
92 GtkDragStatus status; /* drag status */
93 GdkEvent *last_event; /* pending event */
95 gint start_x, start_y; /* Initial position */
96 gint cur_x, cur_y; /* Current Position */
97 GdkScreen *cur_screen; /* Current screen for pointer */
99 guint32 grab_time; /* timestamp for initial grab */
100 GList *selections; /* selections we've claimed */
102 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
104 guint update_idle; /* Idle function to update the drag */
105 guint drop_timeout; /* Timeout for aborting drop */
106 guint destroy_icon : 1; /* If true, destroy icon_window
108 guint have_grab : 1; /* Do we still have the pointer grab
112 struct _GtkDragDestSite
114 GtkDestDefaults flags;
115 GtkTargetList *target_list;
116 GdkDragAction actions;
117 GdkWindow *proxy_window;
118 GdkDragProtocol proxy_protocol;
120 guint proxy_coords : 1;
124 struct _GtkDragDestInfo
126 GtkWidget *widget; /* Widget in which drag is in */
127 GdkDragContext *context; /* Drag context */
128 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
129 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
130 guint dropped : 1; /* Set after we receive a drop */
131 guint32 proxy_drop_time; /* Timestamp for proxied drop */
132 guint proxy_drop_wait : 1; /* Set if we are waiting for a
133 * status reply before sending
136 gint drop_x, drop_y; /* Position of drop */
139 #define DROP_ABORT_TIME 300000
141 #define ANIM_STEP_TIME 50
142 #define ANIM_STEP_LENGTH 50
143 #define ANIM_MIN_STEPS 5
144 #define ANIM_MAX_STEPS 10
148 GtkDragSourceInfo *info;
153 struct _GtkDragFindData
157 GdkDragContext *context;
158 GtkDragDestInfo *info;
161 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
162 gint x, gint y, guint32 time);
166 /* Enumeration for some targets we handle internally */
169 TARGET_MOTIF_SUCCESS = 0x40000000,
170 TARGET_MOTIF_FAILURE,
176 static GdkPixmap *default_icon_pixmap = NULL;
177 static GdkPixmap *default_icon_mask = NULL;
178 static GdkColormap *default_icon_colormap = NULL;
179 static gint default_icon_hot_x;
180 static gint default_icon_hot_y;
182 /* Forward declarations */
183 static void gtk_drag_get_event_actions (GdkEvent *event,
185 GdkDragAction actions,
186 GdkDragAction *suggested_action,
187 GdkDragAction *possible_actions);
188 static GdkCursor * gtk_drag_get_cursor (GdkDisplay *display,
189 GdkDragAction action);
190 static GtkWidget *gtk_drag_get_ipc_widget (GdkScreen *screen);
191 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
193 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
194 GdkEventExpose *event,
197 static void gtk_drag_selection_received (GtkWidget *widget,
198 GtkSelectionData *selection_data,
201 static void gtk_drag_find_widget (GtkWidget *widget,
202 GtkDragFindData *data);
203 static void gtk_drag_proxy_begin (GtkWidget *widget,
204 GtkDragDestInfo *dest_info,
206 static void gtk_drag_dest_realized (GtkWidget *widget);
207 static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
208 GtkWidget *previous_toplevel);
209 static void gtk_drag_dest_site_destroy (gpointer data);
210 static void gtk_drag_dest_leave (GtkWidget *widget,
211 GdkDragContext *context,
213 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
214 GdkDragContext *context,
218 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
219 GdkDragContext *context,
224 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
226 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
228 static void gtk_drag_clear_source_info (GdkDragContext *context);
230 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
233 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
235 static void gtk_drag_drop (GtkDragSourceInfo *info,
237 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
240 static void gtk_drag_cancel (GtkDragSourceInfo *info,
243 static gint gtk_drag_source_event_cb (GtkWidget *widget,
246 static void gtk_drag_source_site_destroy (gpointer data);
247 static void gtk_drag_selection_get (GtkWidget *widget,
248 GtkSelectionData *selection_data,
252 static gint gtk_drag_anim_timeout (gpointer data);
253 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
254 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
255 static void gtk_drag_add_update_idle (GtkDragSourceInfo *info);
257 static void gtk_drag_update (GtkDragSourceInfo *info,
262 static gint gtk_drag_motion_cb (GtkWidget *widget,
263 GdkEventMotion *event,
265 static gint gtk_drag_key_cb (GtkWidget *widget,
268 static gint gtk_drag_button_release_cb (GtkWidget *widget,
269 GdkEventButton *event,
271 static gint gtk_drag_abort_timeout (gpointer data);
273 /************************
274 * Cursor and Icon data *
275 ************************/
277 #define action_ask_width 16
278 #define action_ask_height 16
279 static const guchar action_ask_bits[] = {
280 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
281 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
282 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
284 #define action_ask_mask_width 16
285 #define action_ask_mask_height 16
286 static const guchar action_ask_mask_bits[] = {
287 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
288 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
289 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
291 #define action_copy_width 16
292 #define action_copy_height 16
293 static const guchar action_copy_bits[] = {
294 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
295 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
296 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
298 #define action_copy_mask_width 16
299 #define action_copy_mask_height 16
300 static const guchar action_copy_mask_bits[] = {
301 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
302 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
303 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
305 #define action_move_width 16
306 #define action_move_height 16
307 static const guchar action_move_bits[] = {
308 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
309 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
310 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
312 #define action_move_mask_width 16
313 #define action_move_mask_height 16
314 static const guchar action_move_mask_bits[] = {
315 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
316 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
317 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
319 #define action_link_width 16
320 #define action_link_height 16
321 static const guchar action_link_bits[] = {
322 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
323 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
324 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
326 #define action_link_mask_width 16
327 #define action_link_mask_height 16
328 static const guchar action_link_mask_bits[] = {
329 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
330 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
331 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
333 #define action_none_width 16
334 #define action_none_height 16
335 static const guchar action_none_bits[] = {
336 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
337 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
338 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
340 #define action_none_mask_width 16
341 #define action_none_mask_height 16
342 static const guchar action_none_mask_bits[] = {
343 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
344 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
345 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
347 #define CURSOR_WIDTH 16
348 #define CURSOR_HEIGHT 16
351 GdkDragAction action;
356 { GDK_ACTION_DEFAULT, 0 },
357 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
358 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
359 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
360 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
361 { 0 , action_none_bits, action_none_mask_bits, NULL },
364 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
366 /*********************
367 * Utility functions *
368 *********************/
371 set_can_change_screen (GtkWidget *widget,
372 gboolean can_change_screen)
374 can_change_screen = can_change_screen != FALSE;
376 g_object_set_data (G_OBJECT (widget), "gtk-dnd-can-change-screen",
377 GUINT_TO_POINTER (can_change_screen));
381 get_can_change_screen (GtkWidget *widget)
383 return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
387 /*************************************************************
388 * gtk_drag_get_ipc_widget:
389 * Return a invisible, off-screen, override-redirect
394 *************************************************************/
397 gtk_drag_get_ipc_widget (GdkScreen *screen)
400 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
401 "gtk-dnd-ipc-widgets");
405 GSList *tmp = drag_widgets;
406 result = drag_widgets->data;
407 drag_widgets = drag_widgets->next;
408 g_object_set_data (G_OBJECT (screen),
409 "gtk-dnd-ipc-widgets",
411 g_slist_free_1 (tmp);
415 result = gtk_invisible_new_for_screen (screen);
416 gtk_widget_show (result);
422 /***************************************************************
423 * gtk_drag_release_ipc_widget:
424 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
426 * widget: the widget to release.
428 ***************************************************************/
431 gtk_drag_release_ipc_widget (GtkWidget *widget)
433 GdkScreen *screen = gtk_widget_get_screen (widget);
434 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
435 "gtk-dnd-ipc-widgets");
436 drag_widgets = g_slist_prepend (drag_widgets, widget);
437 g_object_set_data (G_OBJECT (screen),
438 "gtk-dnd-ipc-widgets",
443 gtk_drag_get_event_time (GdkEvent *event)
445 guint32 tm = GDK_CURRENT_TIME;
450 case GDK_MOTION_NOTIFY:
451 tm = event->motion.time; break;
452 case GDK_BUTTON_PRESS:
453 case GDK_2BUTTON_PRESS:
454 case GDK_3BUTTON_PRESS:
455 case GDK_BUTTON_RELEASE:
456 tm = event->button.time; break;
458 case GDK_KEY_RELEASE:
459 tm = event->key.time; break;
460 case GDK_ENTER_NOTIFY:
461 case GDK_LEAVE_NOTIFY:
462 tm = event->crossing.time; break;
463 case GDK_PROPERTY_NOTIFY:
464 tm = event->property.time; break;
465 case GDK_SELECTION_CLEAR:
466 case GDK_SELECTION_REQUEST:
467 case GDK_SELECTION_NOTIFY:
468 tm = event->selection.time; break;
469 case GDK_PROXIMITY_IN:
470 case GDK_PROXIMITY_OUT:
471 tm = event->proximity.time; break;
472 default: /* use current time */
480 gtk_drag_get_event_actions (GdkEvent *event,
482 GdkDragAction actions,
483 GdkDragAction *suggested_action,
484 GdkDragAction *possible_actions)
486 *suggested_action = 0;
487 *possible_actions = 0;
491 GdkModifierType state = 0;
495 case GDK_MOTION_NOTIFY:
496 state = event->motion.state;
498 case GDK_BUTTON_PRESS:
499 case GDK_2BUTTON_PRESS:
500 case GDK_3BUTTON_PRESS:
501 case GDK_BUTTON_RELEASE:
502 state = event->button.state;
505 case GDK_KEY_RELEASE:
506 state = event->key.state;
508 case GDK_ENTER_NOTIFY:
509 case GDK_LEAVE_NOTIFY:
510 state = event->crossing.state;
516 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
518 *suggested_action = GDK_ACTION_ASK;
519 *possible_actions = actions;
521 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
523 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
525 if (actions & GDK_ACTION_LINK)
527 *suggested_action = GDK_ACTION_LINK;
528 *possible_actions = GDK_ACTION_LINK;
531 else if (state & GDK_CONTROL_MASK)
533 if (actions & GDK_ACTION_COPY)
535 *suggested_action = GDK_ACTION_COPY;
536 *possible_actions = GDK_ACTION_COPY;
542 if (actions & GDK_ACTION_MOVE)
544 *suggested_action = GDK_ACTION_MOVE;
545 *possible_actions = GDK_ACTION_MOVE;
552 *possible_actions = actions;
554 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
555 *suggested_action = GDK_ACTION_ASK;
556 else if (actions & GDK_ACTION_COPY)
557 *suggested_action = GDK_ACTION_COPY;
558 else if (actions & GDK_ACTION_MOVE)
559 *suggested_action = GDK_ACTION_MOVE;
560 else if (actions & GDK_ACTION_LINK)
561 *suggested_action = GDK_ACTION_LINK;
566 *possible_actions = actions;
568 if (actions & GDK_ACTION_COPY)
569 *suggested_action = GDK_ACTION_COPY;
570 else if (actions & GDK_ACTION_MOVE)
571 *suggested_action = GDK_ACTION_MOVE;
572 else if (actions & GDK_ACTION_LINK)
573 *suggested_action = GDK_ACTION_LINK;
580 gtk_drag_get_cursor (GdkDisplay *display,
581 GdkDragAction action)
585 for (i = 0 ; i < n_drag_cursors - 1; i++)
586 if (drag_cursors[i].action == action)
588 if (drag_cursors[i].cursor != NULL)
590 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
592 gdk_cursor_unref (drag_cursors[i].cursor);
593 drag_cursors[i].cursor = NULL;
597 if (drag_cursors[i].cursor == NULL)
599 GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
600 GdkColor fg = { 0, 0x0000, 0x0000, 0x0000 };
601 GdkScreen *screen = gdk_display_get_default_screen (display);
602 GdkWindow *window = gdk_screen_get_root_window (screen);
605 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].bits, CURSOR_WIDTH, CURSOR_HEIGHT);
608 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].mask, CURSOR_WIDTH, CURSOR_HEIGHT);
610 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
612 g_object_unref (pixmap);
613 g_object_unref (mask);
616 return drag_cursors[i].cursor;
619 /********************
621 ********************/
623 /*************************************************************
625 * Get the data for a drag or drop
627 * context - drag context
628 * target - format to retrieve the data in.
629 * time - timestamp of triggering event.
632 *************************************************************/
635 gtk_drag_get_data (GtkWidget *widget,
636 GdkDragContext *context,
640 GtkWidget *selection_widget;
642 g_return_if_fail (GTK_IS_WIDGET (widget));
643 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
644 g_return_if_fail (!context->is_source);
646 selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
648 g_object_ref (context);
649 g_object_ref (widget);
651 g_signal_connect (selection_widget, "selection_received",
652 G_CALLBACK (gtk_drag_selection_received), widget);
654 g_object_set_data (G_OBJECT (selection_widget), "drag-context", context);
656 gtk_selection_convert (selection_widget,
657 gdk_drag_get_selection (context),
663 /*************************************************************
664 * gtk_drag_get_source_widget:
665 * Get the widget the was the source of this drag, if
666 * the drag originated from this application.
668 * context: The drag context for this drag
670 * The source widget, or NULL if the drag originated from
671 * a different application.
672 *************************************************************/
675 gtk_drag_get_source_widget (GdkDragContext *context)
679 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
680 g_return_val_if_fail (!context->is_source, NULL);
682 tmp_list = source_widgets;
685 GtkWidget *ipc_widget = tmp_list->data;
687 if (ipc_widget->window == context->source_window)
689 GtkDragSourceInfo *info;
690 info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
692 return info ? info->widget : NULL;
695 tmp_list = tmp_list->next;
701 /*************************************************************
703 * Notify the drag source that the transfer of data
706 * context: The drag context for this drag
707 * success: Was the data successfully transferred?
708 * time: The timestamp to use when notifying the destination.
710 *************************************************************/
713 gtk_drag_finish (GdkDragContext *context,
718 GdkAtom target = GDK_NONE;
720 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
721 g_return_if_fail (!context->is_source);
725 target = gdk_atom_intern ("DELETE", FALSE);
727 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
729 target = gdk_atom_intern (success ?
730 "XmTRANSFER_SUCCESS" :
731 "XmTRANSFER_FAILURE",
735 if (target != GDK_NONE)
737 GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
739 g_object_ref (context);
741 g_object_set_data (G_OBJECT (selection_widget), "drag-context", context);
742 g_signal_connect (selection_widget, "selection_received",
743 G_CALLBACK (gtk_drag_selection_received),
746 gtk_selection_convert (selection_widget,
747 gdk_drag_get_selection (context),
752 if (!(success && del))
753 gdk_drop_finish (context, success, time);
756 /*************************************************************
757 * gtk_drag_highlight_expose:
758 * Callback for expose_event for highlighted widgets.
764 *************************************************************/
767 gtk_drag_highlight_expose (GtkWidget *widget,
768 GdkEventExpose *event,
771 gint x, y, width, height;
773 if (GTK_WIDGET_DRAWABLE (widget))
775 if (GTK_WIDGET_NO_WINDOW (widget))
777 x = widget->allocation.x;
778 y = widget->allocation.y;
779 width = widget->allocation.width;
780 height = widget->allocation.height;
786 gdk_drawable_get_size (widget->window, &width, &height);
789 gtk_paint_shadow (widget->style, widget->window,
790 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
792 x, y, width, height);
794 gdk_draw_rectangle (widget->window,
795 widget->style->black_gc,
797 x, y, width - 1, height - 1);
803 /*************************************************************
804 * gtk_drag_highlight:
805 * Highlight the given widget in the default manner.
809 *************************************************************/
812 gtk_drag_highlight (GtkWidget *widget)
814 g_return_if_fail (GTK_IS_WIDGET (widget));
816 g_signal_connect_after (widget, "expose_event",
817 G_CALLBACK (gtk_drag_highlight_expose),
820 gtk_widget_queue_draw (widget);
823 /*************************************************************
824 * gtk_drag_unhighlight:
825 * Refresh the given widget to remove the highlight.
829 *************************************************************/
832 gtk_drag_unhighlight (GtkWidget *widget)
834 g_return_if_fail (GTK_IS_WIDGET (widget));
836 g_signal_handlers_disconnect_by_func (widget,
837 gtk_drag_highlight_expose,
840 gtk_widget_queue_draw (widget);
844 gtk_drag_dest_set_internal (GtkWidget *widget,
845 GtkDragDestSite *site)
847 GtkDragDestSite *old_site;
849 g_return_if_fail (widget != NULL);
851 /* HACK, do this in the destroy */
852 old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
855 g_signal_handlers_disconnect_by_func (widget,
856 gtk_drag_dest_realized,
858 g_signal_handlers_disconnect_by_func (widget,
859 gtk_drag_dest_hierarchy_changed,
863 if (GTK_WIDGET_REALIZED (widget))
864 gtk_drag_dest_realized (widget);
866 g_signal_connect (widget, "realize",
867 G_CALLBACK (gtk_drag_dest_realized), site);
868 g_signal_connect (widget, "hierarchy_changed",
869 G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
871 g_object_set_data_full (G_OBJECT (widget), "gtk-drag-dest",
872 site, gtk_drag_dest_site_destroy);
876 /*************************************************************
878 * Register a drop site, and possibly add default behaviors.
881 * flags: Which types of default drag behavior to use
882 * targets: Table of targets that can be accepted
883 * n_targets: Number of of entries in targets
886 *************************************************************/
889 gtk_drag_dest_set (GtkWidget *widget,
890 GtkDestDefaults flags,
891 const GtkTargetEntry *targets,
893 GdkDragAction actions)
895 GtkDragDestSite *site;
897 g_return_if_fail (GTK_IS_WIDGET (widget));
899 site = g_new (GtkDragDestSite, 1);
902 site->have_drag = FALSE;
904 site->target_list = gtk_target_list_new (targets, n_targets);
906 site->target_list = NULL;
907 site->actions = actions;
908 site->do_proxy = FALSE;
909 site->proxy_window = NULL;
911 gtk_drag_dest_set_internal (widget, site);
914 /*************************************************************
915 * gtk_drag_dest_set_proxy:
916 * Set up this widget to proxy drags elsewhere.
919 * proxy_window: window to which forward drag events
920 * protocol: Drag protocol which the dest widget accepts
921 * use_coordinates: If true, send the same coordinates to the
922 * destination, because it is a embedded
925 *************************************************************/
928 gtk_drag_dest_set_proxy (GtkWidget *widget,
929 GdkWindow *proxy_window,
930 GdkDragProtocol protocol,
931 gboolean use_coordinates)
933 GtkDragDestSite *site;
935 g_return_if_fail (GTK_IS_WIDGET (widget));
936 g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
938 site = g_new (GtkDragDestSite, 1);
941 site->have_drag = FALSE;
942 site->target_list = NULL;
944 site->proxy_window = proxy_window;
946 g_object_ref (proxy_window);
947 site->do_proxy = TRUE;
948 site->proxy_protocol = protocol;
949 site->proxy_coords = use_coordinates;
951 gtk_drag_dest_set_internal (widget, site);
954 /*************************************************************
955 * gtk_drag_dest_unset
956 * Unregister this widget as a drag target.
960 *************************************************************/
963 gtk_drag_dest_unset (GtkWidget *widget)
965 g_return_if_fail (GTK_IS_WIDGET (widget));
967 g_object_set_data (G_OBJECT (widget), "gtk-drag-dest", NULL);
971 * gtk_drag_dest_get_target_list:
972 * @widget: a #GtkWidget
974 * Returns the list of targets this widget can accept from
977 * Return value: the #GtkTargetList, or %NULL if none
980 gtk_drag_dest_get_target_list (GtkWidget *widget)
982 GtkDragDestSite *site;
984 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
986 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
988 return site ? site->target_list : NULL;
992 * gtk_drag_dest_set_target_list:
993 * @widget: a #GtkWidget that's a drag destination
994 * @target_list: list of droppable targets, or %NULL for none
996 * Sets the target types that this widget can accept from drag-and-drop.
997 * The widget must first be made into a drag destination with
998 * gtk_drag_dest_set().
1001 gtk_drag_dest_set_target_list (GtkWidget *widget,
1002 GtkTargetList *target_list)
1004 GtkDragDestSite *site;
1006 g_return_if_fail (GTK_IS_WIDGET (widget));
1008 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1012 g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
1013 "to make the widget into a drag destination");
1018 gtk_target_list_ref (target_list);
1020 if (site->target_list)
1021 gtk_target_list_unref (site->target_list);
1023 site->target_list = target_list;
1027 /*************************************************************
1028 * _gtk_drag_dest_handle_event:
1029 * Called from widget event handling code on Drag events
1033 * toplevel: Toplevel widget that received the event
1036 *************************************************************/
1039 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1042 GtkDragDestInfo *info;
1043 GdkDragContext *context;
1045 g_return_if_fail (toplevel != NULL);
1046 g_return_if_fail (event != NULL);
1048 context = event->dnd.context;
1050 info = gtk_drag_get_dest_info (context, TRUE);
1052 /* Find the widget for the event */
1053 switch (event->type)
1055 case GDK_DRAG_ENTER:
1058 case GDK_DRAG_LEAVE:
1061 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1062 info->widget = NULL;
1066 case GDK_DRAG_MOTION:
1067 case GDK_DROP_START:
1069 GtkDragFindData data;
1072 if (event->type == GDK_DROP_START)
1074 info->dropped = TRUE;
1075 /* We send a leave here so that the widget unhighlights
1080 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1081 info->widget = NULL;
1085 gdk_window_get_position (toplevel->window, &tx, &ty);
1087 data.x = event->dnd.x_root - tx;
1088 data.y = event->dnd.y_root - ty;
1089 data.context = context;
1092 data.toplevel = TRUE;
1093 data.callback = (event->type == GDK_DRAG_MOTION) ?
1094 gtk_drag_dest_motion : gtk_drag_dest_drop;
1095 data.time = event->dnd.time;
1097 gtk_drag_find_widget (toplevel, &data);
1099 if (info->widget && !data.found)
1101 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1102 info->widget = NULL;
1107 if (event->type == GDK_DRAG_MOTION)
1110 gdk_drag_status (context, 0, event->dnd.time);
1112 else if (event->type == GDK_DROP_START && !info->proxy_source)
1114 gdk_drop_reply (context, data.found, event->dnd.time);
1115 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1116 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1122 g_assert_not_reached ();
1127 * gtk_drag_dest_find_target:
1128 * @widget: drag destination widget
1129 * @context: drag context
1130 * @target_list: list of droppable targets, or %NULL to use
1131 * gtk_drag_dest_get_target_list (@widget).
1133 * Looks for a match between @context->targets and the
1134 * @dest_target_list, returning the first matching target, otherwise
1135 * returning %GDK_NONE. @dest_target_list should usually be the return
1136 * value from gtk_drag_dest_get_target_list(), but some widgets may
1137 * have different valid targets for different parts of the widget; in
1138 * that case, they will have to implement a drag_motion handler that
1139 * passes the correct target list to this function.
1141 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1144 gtk_drag_dest_find_target (GtkWidget *widget,
1145 GdkDragContext *context,
1146 GtkTargetList *target_list)
1149 GList *tmp_source = NULL;
1150 GtkWidget *source_widget;
1152 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1153 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1154 g_return_val_if_fail (!context->is_source, GDK_NONE);
1157 source_widget = gtk_drag_get_source_widget (context);
1159 if (target_list == NULL)
1160 target_list = gtk_drag_dest_get_target_list (widget);
1162 if (target_list == NULL)
1165 tmp_target = target_list->list;
1168 GtkTargetPair *pair = tmp_target->data;
1169 tmp_source = context->targets;
1172 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1174 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1175 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1176 return pair->target;
1180 tmp_source = tmp_source->next;
1182 tmp_target = tmp_target->next;
1189 gtk_drag_selection_received (GtkWidget *widget,
1190 GtkSelectionData *selection_data,
1194 GdkDragContext *context;
1195 GtkDragDestInfo *info;
1196 GtkWidget *drop_widget;
1200 context = g_object_get_data (G_OBJECT (widget), "drag-context");
1201 info = gtk_drag_get_dest_info (context, FALSE);
1203 if (info->proxy_data &&
1204 info->proxy_data->target == selection_data->target)
1206 gtk_selection_data_set (info->proxy_data,
1207 selection_data->type,
1208 selection_data->format,
1209 selection_data->data,
1210 selection_data->length);
1215 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1217 gtk_drag_finish (context, TRUE, FALSE, time);
1219 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1220 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1226 GtkDragDestSite *site;
1228 site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1230 if (site && site->target_list)
1234 if (gtk_target_list_find (site->target_list,
1235 selection_data->target,
1238 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1239 selection_data->length >= 0)
1240 g_signal_emit_by_name (drop_widget,
1241 "drag_data_received",
1242 context, info->drop_x, info->drop_y,
1249 g_signal_emit_by_name (drop_widget,
1250 "drag_data_received",
1251 context, info->drop_x, info->drop_y,
1256 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1259 gtk_drag_finish (context,
1260 (selection_data->length >= 0),
1261 (context->action == GDK_ACTION_MOVE),
1265 g_object_unref (drop_widget);
1268 g_signal_handlers_disconnect_by_func (widget,
1269 gtk_drag_selection_received,
1272 g_object_set_data (G_OBJECT (widget), "drag-context", NULL);
1273 g_object_unref (context);
1275 gtk_drag_release_ipc_widget (widget);
1279 prepend_and_ref_widget (GtkWidget *widget,
1282 GSList **slist_p = data;
1284 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1287 /*************************************************************
1288 * gtk_drag_find_widget:
1289 * Recursive callback used to locate widgets for
1290 * DRAG_MOTION and DROP_START events.
1294 *************************************************************/
1297 gtk_drag_find_widget (GtkWidget *widget,
1298 GtkDragFindData *data)
1300 GtkAllocation new_allocation;
1301 gint allocation_to_window_x = 0;
1302 gint allocation_to_window_y = 0;
1306 if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget))
1309 /* Note that in the following code, we only count the
1310 * position as being inside a WINDOW widget if it is inside
1311 * widget->window; points that are outside of widget->window
1312 * but within the allocation are not counted. This is consistent
1313 * with the way we highlight drag targets.
1315 * data->x,y are relative to widget->parent->window (if
1316 * widget is not a toplevel, widget->window otherwise).
1317 * We compute the allocation of widget in the same coordinates,
1318 * clipping to widget->window, and all intermediate
1319 * windows. If data->x,y is inside that, then we translate
1320 * our coordinates to be relative to widget->window and
1323 new_allocation = widget->allocation;
1328 GdkWindow *window = widget->window;
1330 /* Compute the offset from allocation-relative to
1331 * window-relative coordinates.
1333 allocation_to_window_x = widget->allocation.x;
1334 allocation_to_window_y = widget->allocation.y;
1336 if (!GTK_WIDGET_NO_WINDOW (widget))
1338 /* The allocation is relative to the parent window for
1339 * window widgets, not to widget->window.
1341 gdk_window_get_position (window, &tx, &ty);
1343 allocation_to_window_x -= tx;
1344 allocation_to_window_y -= ty;
1347 new_allocation.x = 0 + allocation_to_window_x;
1348 new_allocation.y = 0 + allocation_to_window_y;
1350 while (window && window != widget->parent->window)
1352 GdkRectangle window_rect = { 0, 0, 0, 0 };
1354 gdk_drawable_get_size (window, &window_rect.width, &window_rect.height);
1356 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1358 gdk_window_get_position (window, &tx, &ty);
1359 new_allocation.x += tx;
1361 new_allocation.y += ty;
1364 window = gdk_window_get_parent (window);
1367 if (!window) /* Window and widget heirarchies didn't match. */
1371 if (data->toplevel ||
1372 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1373 (data->x < new_allocation.x + new_allocation.width) &&
1374 (data->y < new_allocation.y + new_allocation.height)))
1376 /* First, check if the drag is in a valid drop site in
1377 * one of our children
1379 if (GTK_IS_CONTAINER (widget))
1381 GtkDragFindData new_data = *data;
1382 GSList *children = NULL;
1385 new_data.x -= x_offset;
1386 new_data.y -= y_offset;
1387 new_data.found = FALSE;
1388 new_data.toplevel = FALSE;
1390 /* need to reference children temporarily in case the
1391 * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1393 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1394 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1396 if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1397 gtk_drag_find_widget (tmp_list->data, &new_data);
1398 g_object_unref (tmp_list->data);
1400 g_slist_free (children);
1402 data->found = new_data.found;
1405 /* If not, and this widget is registered as a drop site, check to
1406 * emit "drag_motion" to check if we are actually in
1410 g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
1412 data->found = data->callback (widget,
1414 data->x - x_offset - allocation_to_window_x,
1415 data->y - y_offset - allocation_to_window_y,
1417 /* If so, send a "drag_leave" to the last widget */
1420 if (data->info->widget && data->info->widget != widget)
1422 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1424 data->info->widget = widget;
1431 gtk_drag_proxy_begin (GtkWidget *widget,
1432 GtkDragDestInfo *dest_info,
1435 GtkDragSourceInfo *source_info;
1437 GdkDragContext *context;
1438 GtkWidget *ipc_widget;
1440 if (dest_info->proxy_source)
1442 gdk_drag_abort (dest_info->proxy_source->context, time);
1443 gtk_drag_source_info_destroy (dest_info->proxy_source);
1444 dest_info->proxy_source = NULL;
1447 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1448 context = gdk_drag_begin (ipc_widget->window,
1449 dest_info->context->targets);
1451 source_info = gtk_drag_get_source_info (context, TRUE);
1453 source_info->ipc_widget = ipc_widget;
1454 source_info->widget = gtk_widget_ref (widget);
1456 source_info->target_list = gtk_target_list_new (NULL, 0);
1457 tmp_list = dest_info->context->targets;
1460 gtk_target_list_add (source_info->target_list,
1461 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1462 tmp_list = tmp_list->next;
1465 source_info->proxy_dest = dest_info;
1467 g_signal_connect (ipc_widget,
1469 G_CALLBACK (gtk_drag_selection_get),
1472 dest_info->proxy_source = source_info;
1476 gtk_drag_dest_info_destroy (gpointer data)
1478 GtkDragDestInfo *info = data;
1483 static GtkDragDestInfo *
1484 gtk_drag_get_dest_info (GdkDragContext *context,
1487 GtkDragDestInfo *info;
1488 static GQuark info_quark = 0;
1490 info_quark = g_quark_from_static_string ("gtk-dest-info");
1492 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1493 if (!info && create)
1495 info = g_new (GtkDragDestInfo, 1);
1496 info->widget = NULL;
1497 info->context = context;
1498 info->proxy_source = NULL;
1499 info->proxy_data = NULL;
1500 info->dropped = FALSE;
1501 info->proxy_drop_wait = FALSE;
1502 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1503 info, gtk_drag_dest_info_destroy);
1509 static GQuark dest_info_quark = 0;
1511 static GtkDragSourceInfo *
1512 gtk_drag_get_source_info (GdkDragContext *context,
1515 GtkDragSourceInfo *info;
1516 if (!dest_info_quark)
1517 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1519 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1520 if (!info && create)
1522 info = g_new0 (GtkDragSourceInfo, 1);
1523 info->context = context;
1524 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1531 gtk_drag_clear_source_info (GdkDragContext *context)
1533 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1537 gtk_drag_dest_realized (GtkWidget *widget)
1539 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1541 if (GTK_WIDGET_TOPLEVEL (toplevel))
1542 gdk_window_register_dnd (toplevel->window);
1546 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1547 GtkWidget *previous_toplevel)
1549 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1551 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1552 gdk_window_register_dnd (toplevel->window);
1556 gtk_drag_dest_site_destroy (gpointer data)
1558 GtkDragDestSite *site = data;
1560 if (site->proxy_window)
1561 g_object_unref (site->proxy_window);
1563 if (site->target_list)
1564 gtk_target_list_unref (site->target_list);
1570 * Default drag handlers
1573 gtk_drag_dest_leave (GtkWidget *widget,
1574 GdkDragContext *context,
1577 GtkDragDestSite *site;
1579 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1580 g_return_if_fail (site != NULL);
1584 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1586 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1588 gdk_drag_abort (info->proxy_source->context, time);
1589 gtk_drag_source_info_destroy (info->proxy_source);
1590 info->proxy_source = NULL;
1597 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1598 gtk_drag_unhighlight (widget);
1600 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1601 g_signal_emit_by_name (widget, "drag_leave",
1604 site->have_drag = FALSE;
1609 gtk_drag_dest_motion (GtkWidget *widget,
1610 GdkDragContext *context,
1615 GtkDragDestSite *site;
1616 GdkDragAction action = 0;
1619 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1620 g_return_val_if_fail (site != NULL, FALSE);
1625 GdkEvent *current_event;
1626 GdkWindow *dest_window;
1627 GdkDragProtocol proto;
1629 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1631 if (!info->proxy_source || info->proxy_source->widget != widget)
1632 gtk_drag_proxy_begin (widget, info, time);
1634 current_event = gtk_get_current_event ();
1636 if (site->proxy_window)
1638 dest_window = site->proxy_window;
1639 proto = site->proxy_protocol;
1643 gdk_drag_find_window_for_screen (info->proxy_source->context,
1645 gdk_drawable_get_screen (current_event->dnd.window),
1646 current_event->dnd.x_root,
1647 current_event->dnd.y_root,
1648 &dest_window, &proto);
1651 gdk_drag_motion (info->proxy_source->context,
1653 current_event->dnd.x_root,
1654 current_event->dnd.y_root,
1655 context->suggested_action,
1656 context->actions, time);
1658 if (!site->proxy_window && dest_window)
1659 g_object_unref (dest_window);
1661 selection = gdk_drag_get_selection (info->proxy_source->context);
1663 selection != gdk_drag_get_selection (info->context))
1664 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1666 gdk_event_free (current_event);
1671 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1673 if (context->suggested_action & site->actions)
1674 action = context->suggested_action;
1681 if ((site->actions & (1 << i)) &&
1682 (context->actions & (1 << i)))
1690 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1692 if (!site->have_drag)
1694 site->have_drag = TRUE;
1695 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1696 gtk_drag_highlight (widget);
1699 gdk_drag_status (context, action, time);
1703 gdk_drag_status (context, 0, time);
1708 g_signal_emit_by_name (widget, "drag_motion",
1709 context, x, y, time, &retval);
1711 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1715 gtk_drag_dest_drop (GtkWidget *widget,
1716 GdkDragContext *context,
1721 GtkDragDestSite *site;
1722 GtkDragDestInfo *info;
1724 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
1725 g_return_val_if_fail (site != NULL, FALSE);
1727 info = gtk_drag_get_dest_info (context, FALSE);
1728 g_return_val_if_fail (info != NULL, FALSE);
1735 if (info->proxy_source ||
1736 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1738 gtk_drag_drop (info->proxy_source, time);
1742 /* We need to synthesize a motion event, wait for a status,
1743 * and, if we get a good one, do a drop.
1746 GdkEvent *current_event;
1748 GdkWindow *dest_window;
1749 GdkDragProtocol proto;
1751 gtk_drag_proxy_begin (widget, info, time);
1752 info->proxy_drop_wait = TRUE;
1753 info->proxy_drop_time = time;
1755 current_event = gtk_get_current_event ();
1757 if (site->proxy_window)
1759 dest_window = site->proxy_window;
1760 proto = site->proxy_protocol;
1764 gdk_drag_find_window_for_screen (info->proxy_source->context,
1766 gdk_drawable_get_screen (current_event->dnd.window),
1767 current_event->dnd.x_root,
1768 current_event->dnd.y_root,
1769 &dest_window, &proto);
1772 gdk_drag_motion (info->proxy_source->context,
1774 current_event->dnd.x_root,
1775 current_event->dnd.y_root,
1776 context->suggested_action,
1777 context->actions, time);
1779 if (!site->proxy_window && dest_window)
1780 g_object_unref (dest_window);
1782 selection = gdk_drag_get_selection (info->proxy_source->context);
1784 selection != gdk_drag_get_selection (info->context))
1785 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1787 gdk_event_free (current_event);
1796 if (site->flags & GTK_DEST_DEFAULT_DROP)
1798 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1800 if (target == GDK_NONE)
1802 gtk_drag_finish (context, FALSE, FALSE, time);
1806 gtk_drag_get_data (widget, context, target, time);
1809 g_signal_emit_by_name (widget, "drag_drop",
1810 context, x, y, time, &retval);
1812 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1820 /* Like GtkDragBegin, but also takes a GtkDragSourceSite,
1821 * so that we can set the icon from the source site information
1823 static GdkDragContext *
1824 gtk_drag_begin_internal (GtkWidget *widget,
1825 GtkDragSourceSite *site,
1826 GtkTargetList *target_list,
1827 GdkDragAction actions,
1831 GtkDragSourceInfo *info;
1832 GList *targets = NULL;
1834 guint32 time = GDK_CURRENT_TIME;
1835 GdkDragAction possible_actions, suggested_action;
1836 GdkDragContext *context;
1837 GtkWidget *ipc_widget;
1840 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1842 gtk_drag_get_event_actions (event, button, actions,
1843 &suggested_action, &possible_actions);
1845 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), suggested_action);
1848 time = gdk_event_get_time (event);
1850 if (gdk_pointer_grab (ipc_widget->window, FALSE,
1851 GDK_POINTER_MOTION_MASK |
1852 GDK_BUTTON_RELEASE_MASK, NULL,
1855 gtk_drag_release_ipc_widget (ipc_widget);
1859 if (gdk_keyboard_grab (ipc_widget->window, FALSE, time) != 0)
1861 gtk_drag_release_ipc_widget (ipc_widget);
1865 /* We use a GTK grab here to override any grabs that the widget
1866 * we are dragging from might have held
1868 gtk_grab_add (ipc_widget);
1870 tmp_list = g_list_last (target_list->list);
1873 GtkTargetPair *pair = tmp_list->data;
1874 targets = g_list_prepend (targets,
1875 GINT_TO_POINTER (pair->target));
1876 tmp_list = tmp_list->prev;
1879 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1881 context = gdk_drag_begin (ipc_widget->window, targets);
1882 g_list_free (targets);
1884 info = gtk_drag_get_source_info (context, TRUE);
1886 info->ipc_widget = ipc_widget;
1887 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", info);
1889 info->widget = gtk_widget_ref (widget);
1891 info->button = button;
1892 info->cursor = cursor;
1893 info->target_list = target_list;
1894 gtk_target_list_ref (target_list);
1896 info->possible_actions = actions;
1898 info->status = GTK_DRAG_STATUS_DRAG;
1899 info->last_event = NULL;
1900 info->selections = NULL;
1901 info->icon_window = NULL;
1902 info->destroy_icon = FALSE;
1904 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1905 * the drag icon, it will be in the right place
1907 if (event && event->type == GDK_MOTION_NOTIFY)
1909 info->cur_screen = gtk_widget_get_screen (widget);
1910 info->cur_x = event->motion.x_root;
1911 info->cur_y = event->motion.y_root;
1915 gdk_display_get_pointer (gtk_widget_get_display (widget),
1916 &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
1919 g_signal_emit_by_name (widget, "drag_begin",
1922 /* Ensure that we have an icon before we start the drag; the
1923 * application may have set one in ::drag_begin, or it may
1926 if (!info->icon_window)
1928 if (!site || site->icon_type == GTK_IMAGE_EMPTY)
1929 gtk_drag_set_icon_default (context);
1931 switch (site->icon_type)
1933 case GTK_IMAGE_PIXMAP:
1934 gtk_drag_set_icon_pixmap (context,
1936 site->icon_data.pixmap.pixmap,
1940 case GTK_IMAGE_PIXBUF:
1941 gtk_drag_set_icon_pixbuf (context,
1942 site->icon_data.pixbuf.pixbuf,
1945 case GTK_IMAGE_STOCK:
1946 gtk_drag_set_icon_stock (context,
1947 site->icon_data.stock.stock_id,
1950 case GTK_IMAGE_EMPTY:
1952 g_assert_not_reached();
1957 if (event && event->type == GDK_MOTION_NOTIFY)
1958 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1960 info->start_x = info->cur_x;
1961 info->start_y = info->cur_y;
1963 g_signal_connect (info->ipc_widget, "button_release_event",
1964 G_CALLBACK (gtk_drag_button_release_cb), info);
1965 g_signal_connect (info->ipc_widget, "motion_notify_event",
1966 G_CALLBACK (gtk_drag_motion_cb), info);
1967 g_signal_connect (info->ipc_widget, "key_press_event",
1968 G_CALLBACK (gtk_drag_key_cb), info);
1969 g_signal_connect (info->ipc_widget, "key_release_event",
1970 G_CALLBACK (gtk_drag_key_cb), info);
1971 g_signal_connect (info->ipc_widget, "selection_get",
1972 G_CALLBACK (gtk_drag_selection_get), info);
1974 info->have_grab = TRUE;
1975 info->grab_time = time;
1977 return info->context;
1982 * @widget: the source widget.
1983 * @targets: The targets (data formats) in which the
1984 * source can provide the data.
1985 * @actions: A bitmask of the allowed drag actions for this drag.
1986 * @button: The button the user clicked to start the drag.
1987 * @event: The event that triggered the start of the drag.
1989 * Initiates a drag on the source side. The function
1990 * only needs to be used when the application is
1991 * starting drags itself, and is not needed when
1992 * gtk_drag_source_set() is used.
1994 * Return value: the context for this drag.
1997 gtk_drag_begin (GtkWidget *widget,
1998 GtkTargetList *targets,
1999 GdkDragAction actions,
2003 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2004 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
2005 g_return_val_if_fail (targets != NULL, NULL);
2007 return gtk_drag_begin_internal (widget, NULL, targets,
2008 actions, button, event);
2011 /*************************************************************
2012 * gtk_drag_source_set:
2013 * Register a drop site, and possibly add default behaviors.
2016 * start_button_mask: Mask of allowed buttons to start drag
2017 * targets: Table of targets for this source
2019 * actions: Actions allowed for this source
2021 *************************************************************/
2024 gtk_drag_source_set (GtkWidget *widget,
2025 GdkModifierType start_button_mask,
2026 const GtkTargetEntry *targets,
2028 GdkDragAction actions)
2030 GtkDragSourceSite *site;
2032 g_return_if_fail (GTK_IS_WIDGET (widget));
2034 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2036 gtk_widget_add_events (widget,
2037 gtk_widget_get_events (widget) |
2038 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2039 GDK_BUTTON_MOTION_MASK);
2043 if (site->target_list)
2044 gtk_target_list_unref (site->target_list);
2048 site = g_new0 (GtkDragSourceSite, 1);
2050 site->icon_type = GTK_IMAGE_EMPTY;
2052 g_signal_connect (widget, "button_press_event",
2053 G_CALLBACK (gtk_drag_source_event_cb),
2055 g_signal_connect (widget, "motion_notify_event",
2056 G_CALLBACK (gtk_drag_source_event_cb),
2059 g_object_set_data_full (G_OBJECT (widget),
2061 site, gtk_drag_source_site_destroy);
2064 site->start_button_mask = start_button_mask;
2067 site->target_list = gtk_target_list_new (targets, n_targets);
2069 site->target_list = NULL;
2071 site->actions = actions;
2075 /*************************************************************
2076 * gtk_drag_source_unset
2077 * Unregister this widget as a drag source.
2081 *************************************************************/
2084 gtk_drag_source_unset (GtkWidget *widget)
2086 GtkDragSourceSite *site;
2088 g_return_if_fail (GTK_IS_WIDGET (widget));
2090 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2094 g_signal_handlers_disconnect_by_func (widget,
2095 gtk_drag_source_event_cb,
2097 g_signal_handlers_disconnect_by_func (widget,
2098 gtk_drag_source_event_cb,
2100 g_object_set_data (G_OBJECT (widget), "gtk-site-data", NULL);
2105 * gtk_drag_source_get_target_list:
2106 * @widget: a #GtkWidget
2108 * Gets the list of targets this widget can provide for
2111 * Return value: the #GtkTargetList, or %NULL if none
2114 gtk_drag_source_get_target_list (GtkWidget *widget)
2116 GtkDragSourceSite *site;
2118 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
2120 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2122 return site ? site->target_list : NULL;
2126 * gtk_drag_source_set_target_list:
2127 * @widget: a #GtkWidget that's a drag source
2128 * @target_list: list of draggable targets, or %NULL for none
2130 * Changes the target types that this widget offers for drag-and-drop.
2131 * The widget must first be made into a drag source with
2132 * gtk_drag_source_set().
2135 gtk_drag_source_set_target_list (GtkWidget *widget,
2136 GtkTargetList *target_list)
2138 GtkDragSourceSite *site;
2140 g_return_if_fail (GTK_IS_WIDGET (widget));
2142 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2145 g_warning ("gtk_drag_source_set_target_list() requires the widget "
2146 "to already be a drag source.");
2151 gtk_target_list_ref (target_list);
2153 if (site->target_list)
2154 gtk_target_list_unref (site->target_list);
2156 site->target_list = target_list;
2160 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2162 switch (site->icon_type)
2164 case GTK_IMAGE_EMPTY:
2166 case GTK_IMAGE_PIXMAP:
2167 if (site->icon_data.pixmap.pixmap)
2168 g_object_unref (site->icon_data.pixmap.pixmap);
2169 if (site->icon_mask)
2170 g_object_unref (site->icon_mask);
2172 case GTK_IMAGE_PIXBUF:
2173 g_object_unref (site->icon_data.pixbuf.pixbuf);
2175 case GTK_IMAGE_STOCK:
2176 g_free (site->icon_data.stock.stock_id);
2179 g_assert_not_reached();
2182 site->icon_type = GTK_IMAGE_EMPTY;
2185 g_object_unref (site->colormap);
2186 site->colormap = NULL;
2190 * gtk_drag_source_set_icon:
2191 * @widget: a #GtkWidget
2192 * @colormap: the colormap of the icon
2193 * @pixmap: the image data for the icon
2194 * @mask: the transparency mask for an image.
2196 * Sets the icon that will be used for drags from a particular widget
2197 * from a pixmap/mask. GTK+ retains references for the arguments, and
2198 * will release them when they are no longer needed.
2199 * Use gtk_drag_source_set_icon_pixbuf() instead.
2202 gtk_drag_source_set_icon (GtkWidget *widget,
2203 GdkColormap *colormap,
2207 GtkDragSourceSite *site;
2209 g_return_if_fail (GTK_IS_WIDGET (widget));
2210 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2211 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2212 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2214 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2215 g_return_if_fail (site != NULL);
2217 g_object_ref (colormap);
2218 g_object_ref (pixmap);
2220 g_object_ref (mask);
2222 gtk_drag_source_unset_icon (site);
2224 site->icon_type = GTK_IMAGE_PIXMAP;
2226 site->icon_data.pixmap.pixmap = pixmap;
2227 site->icon_mask = mask;
2228 site->colormap = colormap;
2232 * gtk_drag_source_set_icon_pixbuf:
2233 * @widget: a #GtkWidget
2234 * @pixbuf: the #GdkPixbuf for the drag icon
2236 * Sets the icon that will be used for drags from a particular widget
2237 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2238 * release it when it is no longer needed.
2241 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2244 GtkDragSourceSite *site;
2246 g_return_if_fail (GTK_IS_WIDGET (widget));
2247 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2249 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2250 g_return_if_fail (site != NULL);
2251 g_object_ref (pixbuf);
2253 gtk_drag_source_unset_icon (site);
2255 site->icon_type = GTK_IMAGE_PIXBUF;
2256 site->icon_data.pixbuf.pixbuf = pixbuf;
2260 * gtk_drag_source_set_icon_stock:
2261 * @widget: a #GtkWidget
2262 * @stock_id: the ID of the stock icon to use
2264 * Sets the icon that will be used for drags from a particular source
2268 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2269 const gchar *stock_id)
2271 GtkDragSourceSite *site;
2273 g_return_if_fail (GTK_IS_WIDGET (widget));
2274 g_return_if_fail (stock_id != NULL);
2276 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2277 g_return_if_fail (site != NULL);
2279 gtk_drag_source_unset_icon (site);
2281 site->icon_type = GTK_IMAGE_STOCK;
2282 site->icon_data.stock.stock_id = g_strdup (stock_id);
2286 gtk_drag_get_icon (GtkDragSourceInfo *info,
2287 GtkWidget **icon_window,
2291 if (get_can_change_screen (info->icon_window))
2292 gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2295 if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2297 if (!info->fallback_icon)
2299 gint save_hot_x, save_hot_y;
2300 gboolean save_destroy_icon;
2301 GtkWidget *save_icon_window;
2303 /* HACK to get the appropriate icon
2305 save_icon_window = info->icon_window;
2306 save_hot_x = info->hot_x;
2307 save_hot_y = info->hot_x;
2308 save_destroy_icon = info->destroy_icon;
2310 info->icon_window = NULL;
2311 gtk_drag_set_icon_default (info->context);
2312 info->fallback_icon = info->icon_window;
2314 info->icon_window = save_icon_window;
2315 info->hot_x = save_hot_x;
2316 info->hot_y = save_hot_y;
2317 info->destroy_icon = save_destroy_icon;
2320 gtk_widget_hide (info->icon_window);
2322 *icon_window = info->fallback_icon;
2323 gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2325 if (!default_icon_pixmap)
2332 *hot_x = default_icon_hot_x;
2333 *hot_y = default_icon_hot_y;
2338 if (info->fallback_icon)
2339 gtk_widget_hide (info->fallback_icon);
2341 *icon_window = info->icon_window;
2342 *hot_x = info->hot_x;
2343 *hot_y = info->hot_y;
2348 gtk_drag_update_icon (GtkDragSourceInfo *info)
2350 if (info->icon_window)
2352 GtkWidget *icon_window;
2355 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2357 gtk_window_move (GTK_WINDOW (icon_window),
2358 info->cur_x - hot_x,
2359 info->cur_y - hot_y);
2361 if (GTK_WIDGET_VISIBLE (icon_window))
2362 gdk_window_raise (icon_window->window);
2364 gtk_widget_show (icon_window);
2369 gtk_drag_set_icon_window (GdkDragContext *context,
2373 gboolean destroy_on_release)
2375 GtkDragSourceInfo *info;
2377 info = gtk_drag_get_source_info (context, FALSE);
2378 gtk_drag_remove_icon (info);
2381 gtk_widget_ref (widget);
2383 info->icon_window = widget;
2384 info->hot_x = hot_x;
2385 info->hot_y = hot_y;
2386 info->destroy_icon = destroy_on_release;
2388 gtk_drag_update_icon (info);
2392 * gtk_drag_set_icon_widget:
2393 * @context: the context for a drag. (This must be called
2394 with a context for the source side of a drag)
2395 * @widget: a toplevel window to use as an icon.
2396 * @hot_x: the X offset within @widget of the hotspot.
2397 * @hot_y: the Y offset within @widget of the hotspot.
2399 * Changes the icon for a widget to a given widget. GTK+
2400 * will not destroy the icon, so if you don't want
2401 * it to persist, you should connect to the "drag_end"
2402 * signal and destroy it yourself.
2405 gtk_drag_set_icon_widget (GdkDragContext *context,
2410 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2411 g_return_if_fail (context->is_source);
2412 g_return_if_fail (GTK_IS_WIDGET (widget));
2414 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2418 icon_window_realize (GtkWidget *window,
2424 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
2425 gtk_widget_get_colormap (window),
2426 &pixmap, &mask, 128);
2428 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2431 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2433 g_object_unref (pixmap);
2436 g_object_unref (mask);
2440 set_icon_stock_pixbuf (GdkDragContext *context,
2441 const gchar *stock_id,
2450 g_return_if_fail (context != NULL);
2451 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2452 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2454 screen = gdk_drawable_get_screen (context->source_window);
2456 /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
2457 gtk_widget_push_colormap (NULL);
2458 window = gtk_window_new (GTK_WINDOW_POPUP);
2459 gtk_window_set_screen (GTK_WINDOW (window), screen);
2460 set_can_change_screen (window, TRUE);
2461 gtk_widget_pop_colormap ();
2463 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2464 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2468 pixbuf = gtk_widget_render_icon (window, stock_id,
2469 GTK_ICON_SIZE_DND, NULL);
2473 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2474 gtk_widget_destroy (window);
2480 g_object_ref (pixbuf);
2482 width = gdk_pixbuf_get_width (pixbuf);
2483 height = gdk_pixbuf_get_width (pixbuf);
2485 gtk_widget_set_size_request (window,
2486 gdk_pixbuf_get_width (pixbuf),
2487 gdk_pixbuf_get_height (pixbuf));
2489 g_signal_connect_closure (window, "realize",
2490 g_cclosure_new (G_CALLBACK (icon_window_realize),
2492 (GClosureNotify)g_object_unref),
2495 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2499 * gtk_drag_set_icon_pixbuf:
2500 * @context: the context for a drag. (This must be called
2501 * with a context for the source side of a drag)
2502 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2503 * @hot_x: the X offset within @widget of the hotspot.
2504 * @hot_y: the Y offset within @widget of the hotspot.
2506 * Sets @pixbuf as the icon for a given drag.
2509 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
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_PIXBUF (pixbuf));
2518 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2522 * gtk_drag_set_icon_stock:
2523 * @context: the context for a drag. (This must be called
2524 * with a context for the source side of a drag)
2525 * @stock_id: the ID of the stock icon to use for the drag.
2526 * @hot_x: the X offset within the icon of the hotspot.
2527 * @hot_y: the Y offset within the icon of the hotspot.
2529 * Sets the the icon for a given drag from a stock ID.
2532 gtk_drag_set_icon_stock (GdkDragContext *context,
2533 const gchar *stock_id,
2537 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2538 g_return_if_fail (context->is_source);
2539 g_return_if_fail (stock_id != NULL);
2541 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2545 * gtk_drag_set_icon_pixmap:
2546 * @context: the context for a drag. (This must be called
2547 * with a context for the source side of a drag)
2548 * @colormap: the colormap of the icon
2549 * @pixmap: the image data for the icon
2550 * @mask: the transparency mask for the icon
2551 * @hot_x: the X offset within @pixmap of the hotspot.
2552 * @hot_y: the Y offset within @pixmap of the hotspot.
2554 * Sets @pixmap as the icon for a given drag. GTK+ retains
2555 * references for the arguments, and will release them when
2556 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2557 * will be more convenient to use.
2560 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2561 GdkColormap *colormap,
2571 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2572 g_return_if_fail (context->is_source);
2573 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2574 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2575 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2577 screen = gdk_colormap_get_screen (colormap);
2579 g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
2580 g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
2582 gdk_drawable_get_size (pixmap, &width, &height);
2584 gtk_widget_push_colormap (colormap);
2586 window = gtk_window_new (GTK_WINDOW_POPUP);
2587 gtk_window_set_screen (GTK_WINDOW (window), screen);
2588 set_can_change_screen (window, FALSE);
2589 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2590 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2592 gtk_widget_pop_colormap ();
2594 gtk_widget_set_size_request (window, width, height);
2595 gtk_widget_realize (window);
2597 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2600 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2602 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2606 * gtk_drag_set_icon_default:
2607 * @context: the context for a drag. (This must be called
2608 with a context for the source side of a drag)
2610 * Sets the icon for a particular drag to the default
2614 gtk_drag_set_icon_default (GdkDragContext *context)
2616 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2617 g_return_if_fail (context->is_source);
2619 if (!default_icon_pixmap)
2620 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2622 gtk_drag_set_icon_pixmap (context,
2623 default_icon_colormap,
2624 default_icon_pixmap,
2627 default_icon_hot_y);
2631 * gtk_drag_set_default_icon:
2632 * @colormap: the colormap of the icon
2633 * @pixmap: the image data for the icon
2634 * @mask: the transparency mask for an image.
2635 * @hot_x: The X offset within @widget of the hotspot.
2636 * @hot_y: The Y offset within @widget of the hotspot.
2638 * Changes the default drag icon. GTK+ retains references for the
2639 * arguments, and will release them when they are no longer needed.
2640 * This function is obsolete. The default icon should now be changed
2641 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2644 gtk_drag_set_default_icon (GdkColormap *colormap,
2650 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2651 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2652 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2654 if (default_icon_colormap)
2655 g_object_unref (default_icon_colormap);
2656 if (default_icon_pixmap)
2657 g_object_unref (default_icon_pixmap);
2658 if (default_icon_mask)
2659 g_object_unref (default_icon_mask);
2661 default_icon_colormap = colormap;
2662 g_object_ref (colormap);
2664 default_icon_pixmap = pixmap;
2665 g_object_ref (pixmap);
2667 default_icon_mask = mask;
2669 g_object_ref (mask);
2671 default_icon_hot_x = hot_x;
2672 default_icon_hot_y = hot_y;
2676 /*************************************************************
2677 * _gtk_drag_source_handle_event:
2678 * Called from widget event handling code on Drag events
2682 * toplevel: Toplevel widget that received the event
2685 *************************************************************/
2688 _gtk_drag_source_handle_event (GtkWidget *widget,
2691 GtkDragSourceInfo *info;
2692 GdkDragContext *context;
2694 g_return_if_fail (widget != NULL);
2695 g_return_if_fail (event != NULL);
2697 context = event->dnd.context;
2698 info = gtk_drag_get_source_info (context, FALSE);
2702 switch (event->type)
2704 case GDK_DRAG_STATUS:
2708 if (info->proxy_dest)
2710 if (!event->dnd.send_event)
2712 if (info->proxy_dest->proxy_drop_wait)
2714 gboolean result = context->action != 0;
2716 /* Aha - we can finally pass the MOTIF DROP on... */
2717 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2719 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2721 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2725 gdk_drag_status (info->proxy_dest->context,
2726 event->dnd.context->action,
2731 else if (info->have_grab)
2733 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2734 event->dnd.context->action);
2735 if (info->cursor != cursor)
2737 gdk_pointer_grab (widget->window, FALSE,
2738 GDK_POINTER_MOTION_MASK |
2739 GDK_BUTTON_RELEASE_MASK,
2741 cursor, info->grab_time);
2742 info->cursor = cursor;
2745 if (info->last_event)
2746 gtk_drag_add_update_idle (info);
2751 case GDK_DROP_FINISHED:
2752 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2755 g_assert_not_reached ();
2759 /*************************************************************
2760 * gtk_drag_source_check_selection:
2761 * Check if we've set up handlers/claimed the selection
2762 * for a given drag. If not, add them.
2766 *************************************************************/
2769 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2775 tmp_list = info->selections;
2778 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2780 tmp_list = tmp_list->next;
2783 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2787 info->selections = g_list_prepend (info->selections,
2788 GUINT_TO_POINTER (selection));
2790 tmp_list = info->target_list->list;
2793 GtkTargetPair *pair = tmp_list->data;
2795 gtk_selection_add_target (info->ipc_widget,
2799 tmp_list = tmp_list->next;
2802 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2804 gtk_selection_add_target (info->ipc_widget,
2806 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2807 TARGET_MOTIF_SUCCESS);
2808 gtk_selection_add_target (info->ipc_widget,
2810 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2811 TARGET_MOTIF_FAILURE);
2814 gtk_selection_add_target (info->ipc_widget,
2816 gdk_atom_intern ("DELETE", FALSE),
2820 /*************************************************************
2821 * gtk_drag_drop_finished:
2822 * Clean up from the drag, and display snapback, if necessary.
2828 *************************************************************/
2831 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2835 gtk_drag_source_release_selections (info, time);
2837 if (info->proxy_dest)
2839 /* The time from the event isn't reliable for Xdnd drags */
2840 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2841 info->proxy_dest->proxy_drop_time);
2842 gtk_drag_source_info_destroy (info);
2848 gtk_drag_source_info_destroy (info);
2852 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2856 anim->n_steps = MAX (info->cur_x - info->start_x,
2857 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2858 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2860 info->cur_screen = gtk_widget_get_screen (info->widget);
2861 gtk_drag_update_icon (info);
2863 /* Mark the context as dead, so if the destination decides
2864 * to respond really late, we still are OK.
2866 gtk_drag_clear_source_info (info->context);
2867 g_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2873 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2876 GdkDisplay *display = gtk_widget_get_display (info->widget);
2877 GList *tmp_list = info->selections;
2881 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2882 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2883 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2885 tmp_list = tmp_list->next;
2888 g_list_free (info->selections);
2889 info->selections = NULL;
2892 /*************************************************************
2894 * Send a drop event.
2898 *************************************************************/
2901 gtk_drag_drop (GtkDragSourceInfo *info,
2904 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2906 GtkSelectionData selection_data;
2908 /* GTK+ traditionally has used application/x-rootwin-drop, but the
2909 * XDND spec specifies x-rootwindow-drop.
2911 GdkAtom target1 = gdk_atom_intern ("application/x-rootwindow-drop", FALSE);
2912 GdkAtom target2 = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2914 tmp_list = info->target_list->list;
2917 GtkTargetPair *pair = tmp_list->data;
2919 if (pair->target == target1 || pair->target == target2)
2921 selection_data.selection = GDK_NONE;
2922 selection_data.target = pair->target;
2923 selection_data.data = NULL;
2924 selection_data.length = -1;
2926 g_signal_emit_by_name (info->widget, "drag_data_get",
2927 info->context, &selection_data,
2931 /* FIXME: Should we check for length >= 0 here? */
2932 gtk_drag_drop_finished (info, TRUE, time);
2935 tmp_list = tmp_list->next;
2937 gtk_drag_drop_finished (info, FALSE, time);
2941 if (info->icon_window)
2942 gtk_widget_hide (info->icon_window);
2944 gdk_drag_drop (info->context, time);
2945 info->drop_timeout = g_timeout_add (DROP_ABORT_TIME,
2946 gtk_drag_abort_timeout,
2952 * Source side callbacks.
2956 gtk_drag_source_event_cb (GtkWidget *widget,
2960 GtkDragSourceSite *site;
2961 gboolean retval = FALSE;
2962 site = (GtkDragSourceSite *)data;
2964 switch (event->type)
2966 case GDK_BUTTON_PRESS:
2967 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2969 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2970 site->x = event->button.x;
2971 site->y = event->button.y;
2975 case GDK_BUTTON_RELEASE:
2976 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2977 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2980 case GDK_MOTION_NOTIFY:
2981 if (site->state & event->motion.state & site->start_button_mask)
2983 /* FIXME: This is really broken and can leave us
2989 if (site->state & event->motion.state &
2990 GDK_BUTTON1_MASK << (i - 1))
2994 if (gtk_drag_check_threshold (widget, site->x, site->y,
2995 event->motion.x, event->motion.y))
2997 GdkDragContext *context;
3000 context = gtk_drag_begin_internal (widget, site, site->target_list,
3009 default: /* hit for 2/3BUTTON_PRESS */
3017 gtk_drag_source_site_destroy (gpointer data)
3019 GtkDragSourceSite *site = data;
3021 if (site->target_list)
3022 gtk_target_list_unref (site->target_list);
3024 gtk_drag_source_unset_icon (site);
3029 gtk_drag_selection_get (GtkWidget *widget,
3030 GtkSelectionData *selection_data,
3035 GtkDragSourceInfo *info = data;
3036 static GdkAtom null_atom = GDK_NONE;
3040 null_atom = gdk_atom_intern ("NULL", FALSE);
3045 g_signal_emit_by_name (info->widget,
3048 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3050 case TARGET_MOTIF_SUCCESS:
3051 gtk_drag_drop_finished (info, TRUE, time);
3052 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3054 case TARGET_MOTIF_FAILURE:
3055 gtk_drag_drop_finished (info, FALSE, time);
3056 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
3059 if (info->proxy_dest)
3061 /* This is sort of dangerous and needs to be thought
3064 info->proxy_dest->proxy_data = selection_data;
3065 gtk_drag_get_data (info->widget,
3066 info->proxy_dest->context,
3067 selection_data->target,
3070 info->proxy_dest->proxy_data = NULL;
3074 if (gtk_target_list_find (info->target_list,
3075 selection_data->target,
3078 g_signal_emit_by_name (info->widget, "drag_data_get",
3090 gtk_drag_anim_timeout (gpointer data)
3092 GtkDragAnim *anim = data;
3096 GDK_THREADS_ENTER ();
3098 if (anim->step == anim->n_steps)
3100 gtk_drag_source_info_destroy (anim->info);
3107 x = (anim->info->start_x * (anim->step + 1) +
3108 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3109 y = (anim->info->start_y * (anim->step + 1) +
3110 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3111 if (anim->info->icon_window)
3113 GtkWidget *icon_window;
3116 gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);
3118 gtk_window_move (GTK_WINDOW (icon_window),
3128 GDK_THREADS_LEAVE ();
3134 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3136 if (info->icon_window)
3138 gtk_widget_hide (info->icon_window);
3139 if (info->destroy_icon)
3140 gtk_widget_destroy (info->icon_window);
3142 if (info->fallback_icon)
3144 gtk_widget_destroy (info->fallback_icon);
3145 info->fallback_icon = NULL;
3148 g_object_unref (info->icon_window);
3149 info->icon_window = NULL;
3154 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3156 gtk_drag_remove_icon (info);
3158 if (!info->proxy_dest)
3159 g_signal_emit_by_name (info->widget, "drag_end",
3163 g_object_unref (info->widget);
3166 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3167 gtk_drag_button_release_cb,
3169 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3172 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3175 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3176 gtk_drag_selection_get,
3179 gtk_selection_remove_all (info->ipc_widget);
3180 g_object_set_data (G_OBJECT (info->ipc_widget), "gtk-info", NULL);
3181 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3182 gtk_drag_release_ipc_widget (info->ipc_widget);
3184 gtk_target_list_unref (info->target_list);
3186 gtk_drag_clear_source_info (info->context);
3187 g_object_unref (info->context);
3189 if (info->drop_timeout)
3190 g_source_remove (info->drop_timeout);
3196 gtk_drag_update_idle (gpointer data)
3198 GtkDragSourceInfo *info = data;
3199 GdkWindow *dest_window;
3200 GdkDragProtocol protocol;
3203 GdkDragAction action;
3204 GdkDragAction possible_actions;
3207 GDK_THREADS_ENTER ();
3209 info->update_idle = 0;
3211 time = gtk_drag_get_event_time (info->last_event);
3212 gtk_drag_get_event_actions (info->last_event,
3214 info->possible_actions,
3215 &action, &possible_actions);
3216 gtk_drag_update_icon (info);
3217 gdk_drag_find_window_for_screen (info->context,
3218 info->icon_window ? info->icon_window->window : NULL,
3219 info->cur_screen, info->cur_x, info->cur_y,
3220 &dest_window, &protocol);
3222 if (!gdk_drag_motion (info->context, dest_window, protocol,
3223 info->cur_x, info->cur_y, action,
3227 gdk_event_free ((GdkEvent *)info->last_event);
3228 info->last_event = NULL;
3232 g_object_unref (dest_window);
3234 selection = gdk_drag_get_selection (info->context);
3236 gtk_drag_source_check_selection (info, selection, time);
3238 GDK_THREADS_LEAVE ();
3244 gtk_drag_add_update_idle (GtkDragSourceInfo *info)
3246 /* We use an idle lowerthan GDK_PRIORITY_REDRAW so that exposes
3247 * from the last move can catch up before we move again.
3249 if (!info->update_idle)
3250 info->update_idle = g_idle_add_full (GDK_PRIORITY_REDRAW + 5,
3251 gtk_drag_update_idle,
3258 * @info: DragSourceInfo for the drag
3259 * @screen: new screen
3260 * @x_root: new X position
3261 * @y_root: new y position
3262 * @event: event received requiring update
3264 * Updates the status of the drag; called when the
3265 * cursor moves or the modifier changes
3268 gtk_drag_update (GtkDragSourceInfo *info,
3274 info->cur_screen = screen;
3275 info->cur_x = x_root;
3276 info->cur_y = y_root;
3277 if (info->last_event)
3278 gdk_event_free ((GdkEvent *)info->last_event);
3279 info->last_event = gdk_event_copy ((GdkEvent *)event);
3281 gtk_drag_add_update_idle (info);
3284 /*************************************************************
3286 * Called when the user finishes to drag, either by
3287 * releasing the mouse, or by pressing Esc.
3289 * info: Source info for the drag
3290 * time: Timestamp for ending the drag
3292 *************************************************************/
3295 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3297 GdkEvent *send_event;
3298 GtkWidget *source_widget = info->widget;
3299 GdkDisplay *display = gtk_widget_get_display (source_widget);
3301 if (info->update_idle)
3303 g_source_remove (info->update_idle);
3304 info->update_idle = 0;
3307 if (info->last_event)
3309 gdk_event_free (info->last_event);
3310 info->last_event = NULL;
3313 info->have_grab = FALSE;
3315 gdk_display_pointer_ungrab (display, time);
3316 gdk_display_keyboard_ungrab (display, time);
3317 gtk_grab_remove (info->ipc_widget);
3319 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3320 gtk_drag_button_release_cb,
3322 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3325 g_signal_handlers_disconnect_by_func (info->ipc_widget,
3329 /* Send on a release pair to the the original
3330 * widget to convince it to release its grab. We need to
3331 * call gtk_propagate_event() here, instead of
3332 * gtk_widget_event() because widget like GtkList may
3333 * expect propagation.
3336 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3337 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3338 send_event->button.send_event = TRUE;
3339 send_event->button.time = time;
3340 send_event->button.x = 0;
3341 send_event->button.y = 0;
3342 send_event->button.axes = NULL;
3343 send_event->button.state = 0;
3344 send_event->button.button = info->button;
3345 send_event->button.device = gdk_display_get_core_pointer (display);
3346 send_event->button.x_root = 0;
3347 send_event->button.y_root = 0;
3349 gtk_propagate_event (source_widget, send_event);
3350 gdk_event_free (send_event);
3353 /*************************************************************
3355 * Called on cancellation of a drag, either by the user
3356 * or programmatically.
3358 * info: Source info for the drag
3359 * time: Timestamp for ending the drag
3361 *************************************************************/
3364 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3366 gtk_drag_end (info, time);
3367 gdk_drag_abort (info->context, time);
3368 gtk_drag_drop_finished (info, FALSE, time);
3371 /*************************************************************
3372 * gtk_drag_motion_cb:
3373 * "motion_notify_event" callback during drag.
3377 *************************************************************/
3380 gtk_drag_motion_cb (GtkWidget *widget,
3381 GdkEventMotion *event,
3384 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3386 gint x_root, y_root;
3390 GdkDisplay *display = gtk_widget_get_display (widget);
3392 gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
3393 event->x_root = x_root;
3394 event->y_root = y_root;
3397 screen = gdk_event_get_screen ((GdkEvent *)event);
3399 gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
3404 /*************************************************************
3406 * "key_press/release_event" callback during drag.
3410 *************************************************************/
3413 gtk_drag_key_cb (GtkWidget *widget,
3417 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3418 GdkModifierType state;
3419 GdkWindow *root_window;
3421 if (event->type == GDK_KEY_PRESS)
3423 if (event->keyval == GDK_Escape)
3425 gtk_drag_cancel (info, event->time);
3431 /* Now send a "motion" so that the modifier state is updated */
3433 /* The state is not yet updated in the event, so we need
3434 * to query it here. We could use XGetModifierMapping, but
3435 * that would be overkill.
3437 root_window = gtk_widget_get_root_window (widget);
3438 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3440 event->state = state;
3441 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
3446 /*************************************************************
3447 * gtk_drag_button_release_cb:
3448 * "button_release_event" callback during drag.
3452 *************************************************************/
3455 gtk_drag_button_release_cb (GtkWidget *widget,
3456 GdkEventButton *event,
3459 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3461 if (event->button != info->button)
3464 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3466 gtk_drag_end (info, event->time);
3467 gtk_drag_drop (info, event->time);
3471 gtk_drag_cancel (info, event->time);
3478 gtk_drag_abort_timeout (gpointer data)
3480 GtkDragSourceInfo *info = data;
3481 guint32 time = GDK_CURRENT_TIME;
3483 GDK_THREADS_ENTER ();
3485 if (info->proxy_dest)
3486 time = info->proxy_dest->proxy_drop_time;
3488 info->drop_timeout = 0;
3489 gtk_drag_drop_finished (info, FALSE, time);
3491 GDK_THREADS_LEAVE ();
3497 * gtk_drag_check_threshold:
3498 * @widget: a #GtkWidget
3499 * @start_x: X coordinate of start of drag
3500 * @start_y: Y coordinate of start of drag
3501 * @current_x: current X coordinate
3502 * @current_y: current Y coordinate
3504 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3505 * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
3506 * should trigger the beginning of a drag-and-drop operation.
3508 * Return Value: %TRUE if the drag threshold has been passed.
3511 gtk_drag_check_threshold (GtkWidget *widget,
3517 gint drag_threshold;
3519 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3521 g_object_get (gtk_widget_get_settings (widget),
3522 "gtk-dnd-drag-threshold", &drag_threshold,
3525 return (ABS (current_x - start_x) > drag_threshold ||
3526 ABS (current_y - start_y) > drag_threshold);