1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27 #include "gdkconfig.h"
29 #include "gdk/gdkkeysyms.h"
33 #include "gtkinvisible.h"
35 #include "gtksignal.h"
37 #include "gtkwindow.h"
39 static GSList *source_widgets = NULL;
41 typedef struct _GtkDragSourceSite GtkDragSourceSite;
42 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
43 typedef struct _GtkDragDestSite GtkDragDestSite;
44 typedef struct _GtkDragDestInfo GtkDragDestInfo;
45 typedef struct _GtkDragAnim GtkDragAnim;
46 typedef struct _GtkDragFindData GtkDragFindData;
56 struct _GtkDragSourceSite
58 GdkModifierType start_button_mask;
59 GtkTargetList *target_list; /* Targets for drag data */
60 GdkDragAction actions; /* Possible actions */
63 GtkImageType icon_type;
66 GtkImagePixmapData pixmap;
67 GtkImagePixbufData pixbuf;
68 GtkImageStockData stock;
72 GdkColormap *colormap; /* Colormap for drag icon */
74 /* Stored button press information to detect drag beginning */
79 struct _GtkDragSourceInfo
82 GtkTargetList *target_list; /* Targets for drag data */
83 GdkDragAction possible_actions; /* Actions allowed by source */
84 GdkDragContext *context; /* drag context */
85 GtkWidget *icon_window; /* Window for drag */
86 GtkWidget *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; /* motion event waiting for response */
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 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;
118 gboolean do_proxy : 1;
119 gboolean proxy_coords : 1;
120 gboolean have_drag : 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 gboolean dropped : 1; /* Set after we receive a drop */
130 guint32 proxy_drop_time; /* Timestamp for proxied drop */
131 gboolean 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_update (GtkDragSourceInfo *info,
259 static gint gtk_drag_motion_cb (GtkWidget *widget,
260 GdkEventMotion *event,
262 static gint gtk_drag_key_cb (GtkWidget *widget,
265 static gint gtk_drag_button_release_cb (GtkWidget *widget,
266 GdkEventButton *event,
268 static gint gtk_drag_abort_timeout (gpointer data);
270 /************************
271 * Cursor and Icon data *
272 ************************/
274 #define action_ask_width 16
275 #define action_ask_height 16
276 static const guchar action_ask_bits[] = {
277 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
278 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
279 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
281 #define action_ask_mask_width 16
282 #define action_ask_mask_height 16
283 static const guchar action_ask_mask_bits[] = {
284 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
285 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
286 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
288 #define action_copy_width 16
289 #define action_copy_height 16
290 static const guchar action_copy_bits[] = {
291 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
292 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
293 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
295 #define action_copy_mask_width 16
296 #define action_copy_mask_height 16
297 static const guchar action_copy_mask_bits[] = {
298 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
299 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
300 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
302 #define action_move_width 16
303 #define action_move_height 16
304 static const guchar action_move_bits[] = {
305 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
306 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
307 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
309 #define action_move_mask_width 16
310 #define action_move_mask_height 16
311 static const guchar action_move_mask_bits[] = {
312 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
313 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
314 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
316 #define action_link_width 16
317 #define action_link_height 16
318 static const guchar action_link_bits[] = {
319 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
320 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
321 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
323 #define action_link_mask_width 16
324 #define action_link_mask_height 16
325 static const guchar action_link_mask_bits[] = {
326 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
327 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
328 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
330 #define action_none_width 16
331 #define action_none_height 16
332 static const guchar action_none_bits[] = {
333 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
334 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
335 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
337 #define action_none_mask_width 16
338 #define action_none_mask_height 16
339 static const guchar action_none_mask_bits[] = {
340 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
341 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
342 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
344 #define CURSOR_WIDTH 16
345 #define CURSOR_HEIGHT 16
348 GdkDragAction action;
353 { GDK_ACTION_DEFAULT, 0 },
354 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
355 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
356 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
357 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
358 { 0 , action_none_bits, action_none_mask_bits, NULL },
361 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
363 /*********************
364 * Utility functions *
365 *********************/
368 set_can_change_screen (GtkWidget *widget,
369 gboolean can_change_screen)
371 can_change_screen = can_change_screen != FALSE;
373 g_object_set_data (G_OBJECT (widget), "gtk-dnd-can-change-screen",
374 GUINT_TO_POINTER (can_change_screen));
378 get_can_change_screen (GtkWidget *widget)
380 return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;
384 /*************************************************************
385 * gtk_drag_get_ipc_widget:
386 * Return a invisible, off-screen, override-redirect
391 *************************************************************/
394 gtk_drag_get_ipc_widget (GdkScreen *screen)
397 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
398 "gtk-dnd-ipc-widgets");
402 GSList *tmp = drag_widgets;
403 result = drag_widgets->data;
404 drag_widgets = drag_widgets->next;
405 g_object_set_data (G_OBJECT (screen),
406 "gtk-dnd-ipc-widgets",
408 g_slist_free_1 (tmp);
412 result = gtk_invisible_new_for_screen (screen);
413 gtk_widget_show (result);
419 /***************************************************************
420 * gtk_drag_release_ipc_widget:
421 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
423 * widget: the widget to release.
425 ***************************************************************/
428 gtk_drag_release_ipc_widget (GtkWidget *widget)
430 GdkScreen *screen = gtk_widget_get_screen (widget);
431 GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
432 "gtk-dnd-ipc-widgets");
433 drag_widgets = g_slist_prepend (drag_widgets, widget);
434 g_object_set_data (G_OBJECT (screen),
435 "gtk-dnd-ipc-widgets",
440 gtk_drag_get_event_time (GdkEvent *event)
442 guint32 tm = GDK_CURRENT_TIME;
447 case GDK_MOTION_NOTIFY:
448 tm = event->motion.time; break;
449 case GDK_BUTTON_PRESS:
450 case GDK_2BUTTON_PRESS:
451 case GDK_3BUTTON_PRESS:
452 case GDK_BUTTON_RELEASE:
453 tm = event->button.time; break;
455 case GDK_KEY_RELEASE:
456 tm = event->key.time; break;
457 case GDK_ENTER_NOTIFY:
458 case GDK_LEAVE_NOTIFY:
459 tm = event->crossing.time; break;
460 case GDK_PROPERTY_NOTIFY:
461 tm = event->property.time; break;
462 case GDK_SELECTION_CLEAR:
463 case GDK_SELECTION_REQUEST:
464 case GDK_SELECTION_NOTIFY:
465 tm = event->selection.time; break;
466 case GDK_PROXIMITY_IN:
467 case GDK_PROXIMITY_OUT:
468 tm = event->proximity.time; break;
469 default: /* use current time */
477 gtk_drag_get_event_actions (GdkEvent *event,
479 GdkDragAction actions,
480 GdkDragAction *suggested_action,
481 GdkDragAction *possible_actions)
483 *suggested_action = 0;
484 *possible_actions = 0;
488 GdkModifierType state = 0;
492 case GDK_MOTION_NOTIFY:
493 state = event->motion.state;
495 case GDK_BUTTON_PRESS:
496 case GDK_2BUTTON_PRESS:
497 case GDK_3BUTTON_PRESS:
498 case GDK_BUTTON_RELEASE:
499 state = event->button.state;
502 case GDK_KEY_RELEASE:
503 state = event->key.state;
505 case GDK_ENTER_NOTIFY:
506 case GDK_LEAVE_NOTIFY:
507 state = event->crossing.state;
513 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
515 *suggested_action = GDK_ACTION_ASK;
516 *possible_actions = actions;
518 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
520 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
522 if (actions & GDK_ACTION_LINK)
524 *suggested_action = GDK_ACTION_LINK;
525 *possible_actions = GDK_ACTION_LINK;
528 else if (state & GDK_CONTROL_MASK)
530 if (actions & GDK_ACTION_COPY)
532 *suggested_action = GDK_ACTION_COPY;
533 *possible_actions = GDK_ACTION_COPY;
539 if (actions & GDK_ACTION_MOVE)
541 *suggested_action = GDK_ACTION_MOVE;
542 *possible_actions = GDK_ACTION_MOVE;
549 *possible_actions = actions;
551 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
552 *suggested_action = GDK_ACTION_ASK;
553 else if (actions & GDK_ACTION_COPY)
554 *suggested_action = GDK_ACTION_COPY;
555 else if (actions & GDK_ACTION_MOVE)
556 *suggested_action = GDK_ACTION_MOVE;
557 else if (actions & GDK_ACTION_LINK)
558 *suggested_action = GDK_ACTION_LINK;
563 *possible_actions = actions;
565 if (actions & GDK_ACTION_COPY)
566 *suggested_action = GDK_ACTION_COPY;
567 else if (actions & GDK_ACTION_MOVE)
568 *suggested_action = GDK_ACTION_MOVE;
569 else if (actions & GDK_ACTION_LINK)
570 *suggested_action = GDK_ACTION_LINK;
577 gtk_drag_get_cursor (GdkDisplay *display,
578 GdkDragAction action)
582 for (i = 0 ; i < n_drag_cursors - 1; i++)
583 if (drag_cursors[i].action == action)
585 if (drag_cursors[i].cursor != NULL)
587 if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
589 gdk_cursor_unref (drag_cursors[i].cursor);
590 drag_cursors[i].cursor = NULL;
594 if (drag_cursors[i].cursor == NULL)
596 GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
597 GdkColor fg = { 0, 0x0000, 0x0000, 0x0000 };
598 GdkScreen *screen = gdk_display_get_default_screen (display);
599 GdkWindow *window = gdk_screen_get_root_window (screen);
602 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].bits, CURSOR_WIDTH, CURSOR_HEIGHT);
605 gdk_bitmap_create_from_data (window, (gchar *)drag_cursors[i].mask, CURSOR_WIDTH, CURSOR_HEIGHT);
607 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
609 gdk_pixmap_unref (pixmap);
610 gdk_pixmap_unref (mask);
613 return drag_cursors[i].cursor;
616 /********************
618 ********************/
620 /*************************************************************
622 * Get the data for a drag or drop
624 * context - drag context
625 * target - format to retrieve the data in.
626 * time - timestamp of triggering event.
629 *************************************************************/
632 gtk_drag_get_data (GtkWidget *widget,
633 GdkDragContext *context,
637 GtkWidget *selection_widget;
639 g_return_if_fail (GTK_IS_WIDGET (widget));
640 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
641 g_return_if_fail (!context->is_source);
643 selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
645 gdk_drag_context_ref (context);
646 gtk_widget_ref (widget);
648 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
649 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
651 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
653 gtk_selection_convert (selection_widget,
654 gdk_drag_get_selection (context),
660 /*************************************************************
661 * gtk_drag_get_source_widget:
662 * Get the widget the was the source of this drag, if
663 * the drag originated from this application.
665 * context: The drag context for this drag
667 * The source widget, or NULL if the drag originated from
668 * a different application.
669 *************************************************************/
672 gtk_drag_get_source_widget (GdkDragContext *context)
676 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
677 g_return_val_if_fail (!context->is_source, NULL);
679 tmp_list = source_widgets;
682 GtkWidget *ipc_widget = tmp_list->data;
684 if (ipc_widget->window == context->source_window)
686 GtkDragSourceInfo *info;
687 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
689 return info ? info->widget : NULL;
692 tmp_list = tmp_list->next;
698 /*************************************************************
700 * Notify the drag source that the transfer of data
703 * context: The drag context for this drag
704 * success: Was the data successfully transferred?
705 * time: The timestamp to use when notifying the destination.
707 *************************************************************/
710 gtk_drag_finish (GdkDragContext *context,
715 GdkAtom target = GDK_NONE;
717 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
718 g_return_if_fail (!context->is_source);
722 target = gdk_atom_intern ("DELETE", FALSE);
724 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
726 target = gdk_atom_intern (success ?
727 "XmTRANSFER_SUCCESS" :
728 "XmTRANSFER_FAILURE",
732 if (target != GDK_NONE)
734 GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
736 gdk_drag_context_ref (context);
738 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
739 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
740 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
743 gtk_selection_convert (selection_widget,
744 gdk_drag_get_selection (context),
749 if (!(success && del))
750 gdk_drop_finish (context, success, time);
753 /*************************************************************
754 * gtk_drag_highlight_expose:
755 * Callback for expose_event for highlighted widgets.
761 *************************************************************/
764 gtk_drag_highlight_expose (GtkWidget *widget,
765 GdkEventExpose *event,
768 gint x, y, width, height;
770 if (GTK_WIDGET_DRAWABLE (widget))
772 if (GTK_WIDGET_NO_WINDOW (widget))
774 x = widget->allocation.x;
775 y = widget->allocation.y;
776 width = widget->allocation.width;
777 height = widget->allocation.height;
783 gdk_window_get_size (widget->window, &width, &height);
786 gtk_draw_shadow (widget->style, widget->window,
787 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
788 x, y, width, height);
790 gdk_draw_rectangle (widget->window,
791 widget->style->black_gc,
793 x, y, width - 1, height - 1);
799 /*************************************************************
800 * gtk_drag_highlight:
801 * Highlight the given widget in the default manner.
805 *************************************************************/
808 gtk_drag_highlight (GtkWidget *widget)
810 g_return_if_fail (GTK_IS_WIDGET (widget));
812 gtk_signal_connect_after (GTK_OBJECT (widget), "expose_event",
813 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
816 gtk_widget_queue_draw (widget);
819 /*************************************************************
820 * gtk_drag_unhighlight:
821 * Refresh the given widget to remove the highlight.
825 *************************************************************/
828 gtk_drag_unhighlight (GtkWidget *widget)
830 g_return_if_fail (GTK_IS_WIDGET (widget));
832 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
833 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
836 gtk_widget_queue_clear (widget);
840 gtk_drag_dest_set_internal (GtkWidget *widget,
841 GtkDragDestSite *site)
843 GtkDragDestSite *old_site;
845 g_return_if_fail (widget != NULL);
847 /* HACK, do this in the destroy */
848 old_site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
850 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), old_site);
852 if (GTK_WIDGET_REALIZED (widget))
853 gtk_drag_dest_realized (widget);
855 gtk_signal_connect (GTK_OBJECT (widget), "realize",
856 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), site);
857 gtk_signal_connect (GTK_OBJECT (widget), "hierarchy_changed",
858 GTK_SIGNAL_FUNC (gtk_drag_dest_hierarchy_changed), site);
860 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
861 site, gtk_drag_dest_site_destroy);
865 /*************************************************************
867 * Register a drop site, and possibly add default behaviors.
870 * flags: Which types of default drag behavior to use
871 * targets: Table of targets that can be accepted
872 * n_targets: Number of of entries in targets
875 *************************************************************/
878 gtk_drag_dest_set (GtkWidget *widget,
879 GtkDestDefaults flags,
880 const GtkTargetEntry *targets,
882 GdkDragAction actions)
884 GtkDragDestSite *site;
886 g_return_if_fail (GTK_IS_WIDGET (widget));
888 site = g_new (GtkDragDestSite, 1);
891 site->have_drag = FALSE;
893 site->target_list = gtk_target_list_new (targets, n_targets);
895 site->target_list = NULL;
896 site->actions = actions;
897 site->do_proxy = FALSE;
898 site->proxy_window = NULL;
900 gtk_drag_dest_set_internal (widget, site);
903 /*************************************************************
904 * gtk_drag_dest_set_proxy:
905 * Set up this widget to proxy drags elsewhere.
908 * proxy_window: window to which forward drag events
909 * protocol: Drag protocol which the dest widget accepts
910 * use_coordinates: If true, send the same coordinates to the
911 * destination, because it is a embedded
914 *************************************************************/
917 gtk_drag_dest_set_proxy (GtkWidget *widget,
918 GdkWindow *proxy_window,
919 GdkDragProtocol protocol,
920 gboolean use_coordinates)
922 GtkDragDestSite *site;
924 g_return_if_fail (GTK_IS_WIDGET (widget));
925 g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
927 site = g_new (GtkDragDestSite, 1);
930 site->have_drag = FALSE;
931 site->target_list = NULL;
933 site->proxy_window = proxy_window;
935 gdk_window_ref (proxy_window);
936 site->do_proxy = TRUE;
937 site->proxy_protocol = protocol;
938 site->proxy_coords = use_coordinates;
940 gtk_drag_dest_set_internal (widget, site);
943 /*************************************************************
944 * gtk_drag_dest_unset
945 * Unregister this widget as a drag target.
949 *************************************************************/
952 gtk_drag_dest_unset (GtkWidget *widget)
954 g_return_if_fail (GTK_IS_WIDGET (widget));
956 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
960 * gtk_drag_dest_get_target_list:
961 * @widget: a #GtkWidget
963 * Returns the list of targets this widget can accept from
966 * Return value: the #GtkTargetList, or %NULL if none
969 gtk_drag_dest_get_target_list (GtkWidget *widget)
971 GtkDragDestSite *site;
973 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
975 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
977 return site ? site->target_list : NULL;
981 * gtk_drag_dest_set_target_list:
982 * @widget: a #GtkWidget that's a drag destination
983 * @target_list: list of droppable targets, or %NULL for none
985 * Sets the target types that this widget can accept from drag-and-drop.
986 * The widget must first be made into a drag destination with
987 * gtk_drag_dest_set().
990 gtk_drag_dest_set_target_list (GtkWidget *widget,
991 GtkTargetList *target_list)
993 GtkDragDestSite *site;
995 g_return_if_fail (GTK_IS_WIDGET (widget));
997 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1001 g_warning ("can't set a target list on a widget until you've called gtk_drag_dest_set() to make the widget into a drag destination");
1006 gtk_target_list_ref (target_list);
1008 if (site->target_list)
1009 gtk_target_list_unref (site->target_list);
1011 site->target_list = target_list;
1015 /*************************************************************
1016 * _gtk_drag_dest_handle_event:
1017 * Called from widget event handling code on Drag events
1021 * toplevel: Toplevel widget that received the event
1024 *************************************************************/
1027 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
1030 GtkDragDestInfo *info;
1031 GdkDragContext *context;
1033 g_return_if_fail (toplevel != NULL);
1034 g_return_if_fail (event != NULL);
1036 context = event->dnd.context;
1038 info = gtk_drag_get_dest_info (context, TRUE);
1040 /* Find the widget for the event */
1041 switch (event->type)
1043 case GDK_DRAG_ENTER:
1046 case GDK_DRAG_LEAVE:
1049 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1050 info->widget = NULL;
1054 case GDK_DRAG_MOTION:
1055 case GDK_DROP_START:
1057 GtkDragFindData data;
1060 if (event->type == GDK_DROP_START)
1062 info->dropped = TRUE;
1063 /* We send a leave here so that the widget unhighlights
1068 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1069 info->widget = NULL;
1073 gdk_window_get_origin (toplevel->window, &tx, &ty);
1075 data.x = event->dnd.x_root - tx;
1076 data.y = event->dnd.y_root - ty;
1077 data.context = context;
1080 data.toplevel = TRUE;
1081 data.callback = (event->type == GDK_DRAG_MOTION) ?
1082 gtk_drag_dest_motion : gtk_drag_dest_drop;
1083 data.time = event->dnd.time;
1085 gtk_drag_find_widget (toplevel, &data);
1087 if (info->widget && !data.found)
1089 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1090 info->widget = NULL;
1095 if (event->type == GDK_DRAG_MOTION)
1098 gdk_drag_status (context, 0, event->dnd.time);
1100 else if (event->type == GDK_DROP_START && !info->proxy_source)
1102 gdk_drop_reply (context, data.found, event->dnd.time);
1103 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1104 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1110 g_assert_not_reached ();
1115 * gtk_drag_dest_find_target:
1116 * @widget: drag destination widget
1117 * @context: drag context
1118 * @target_list: list of droppable targets, or %NULL to use
1119 * gtk_drag_dest_get_target_list (@widget).
1121 * Looks for a match between @context->targets and the
1122 * @dest_target_list, returning the first matching target, otherwise
1123 * returning %GDK_NONE. @dest_target_list should usually be the return
1124 * value from gtk_drag_dest_get_target_list(), but some widgets may
1125 * have different valid targets for different parts of the widget; in
1126 * that case, they will have to implement a drag_motion handler that
1127 * passes the correct target list to this function.
1129 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1132 gtk_drag_dest_find_target (GtkWidget *widget,
1133 GdkDragContext *context,
1134 GtkTargetList *target_list)
1137 GList *tmp_source = NULL;
1138 GtkWidget *source_widget;
1140 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1141 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1142 g_return_val_if_fail (!context->is_source, GDK_NONE);
1145 source_widget = gtk_drag_get_source_widget (context);
1147 if (target_list == NULL)
1148 target_list = gtk_drag_dest_get_target_list (widget);
1150 if (target_list == NULL)
1153 tmp_target = target_list->list;
1156 GtkTargetPair *pair = tmp_target->data;
1157 tmp_source = context->targets;
1160 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1162 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1163 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1164 return pair->target;
1168 tmp_source = tmp_source->next;
1170 tmp_target = tmp_target->next;
1177 gtk_drag_selection_received (GtkWidget *widget,
1178 GtkSelectionData *selection_data,
1182 GdkDragContext *context;
1183 GtkDragDestInfo *info;
1184 GtkWidget *drop_widget;
1188 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1189 info = gtk_drag_get_dest_info (context, FALSE);
1191 if (info->proxy_data &&
1192 info->proxy_data->target == selection_data->target)
1194 gtk_selection_data_set (info->proxy_data,
1195 selection_data->type,
1196 selection_data->format,
1197 selection_data->data,
1198 selection_data->length);
1203 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1205 gtk_drag_finish (context, TRUE, FALSE, time);
1207 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1208 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1214 GtkDragDestSite *site;
1216 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1218 if (site && site->target_list)
1222 if (gtk_target_list_find (site->target_list,
1223 selection_data->target,
1226 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1227 selection_data->length >= 0)
1228 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1229 "drag_data_received",
1230 context, info->drop_x, info->drop_y,
1237 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1238 "drag_data_received",
1239 context, info->drop_x, info->drop_y,
1240 selection_data, 0, time);
1243 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1246 gtk_drag_finish (context,
1247 (selection_data->length >= 0),
1248 (context->action == GDK_ACTION_MOVE),
1252 gtk_widget_unref (drop_widget);
1255 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1256 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1259 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1260 gdk_drag_context_unref (context);
1262 gtk_drag_release_ipc_widget (widget);
1266 prepend_and_ref_widget (GtkWidget *widget,
1269 GSList **slist_p = data;
1271 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
1274 /*************************************************************
1275 * gtk_drag_find_widget:
1276 * Recursive callback used to locate widgets for
1277 * DRAG_MOTION and DROP_START events.
1281 *************************************************************/
1284 gtk_drag_find_widget (GtkWidget *widget,
1285 GtkDragFindData *data)
1287 GtkAllocation new_allocation;
1288 gint allocation_to_window_x = 0;
1289 gint allocation_to_window_y = 0;
1293 if (data->found || !GTK_WIDGET_MAPPED (widget))
1296 /* Note that in the following code, we only count the
1297 * position as being inside a WINDOW widget if it is inside
1298 * widget->window; points that are outside of widget->window
1299 * but within the allocation are not counted. This is consistent
1300 * with the way we highlight drag targets.
1302 * data->x,y are relative to widget->parent->window (if
1303 * widget is not a toplevel, widget->window otherwise).
1304 * We compute the allocation of widget in the same coordinates,
1305 * clipping to widget->window, and all intermediate
1306 * windows. If data->x,y is inside that, then we translate
1307 * our coordinates to be relative to widget->window and
1310 new_allocation = widget->allocation;
1315 GdkWindow *window = widget->window;
1317 /* Compute the offset from allocation-relative to
1318 * window-relative coordinates.
1320 allocation_to_window_x = widget->allocation.x;
1321 allocation_to_window_y = widget->allocation.y;
1323 if (!GTK_WIDGET_NO_WINDOW (widget))
1325 /* The allocation is relative to the parent window for
1326 * window widgets, not to widget->window.
1328 gdk_window_get_position (window, &tx, &ty);
1330 allocation_to_window_x -= tx;
1331 allocation_to_window_y -= ty;
1334 new_allocation.x = 0 + allocation_to_window_x;
1335 new_allocation.y = 0 + allocation_to_window_y;
1337 while (window && window != widget->parent->window)
1339 GdkRectangle window_rect = { 0, 0, 0, 0 };
1341 gdk_window_get_size (window, &window_rect.width, &window_rect.height);
1343 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
1345 gdk_window_get_position (window, &tx, &ty);
1346 new_allocation.x += tx;
1348 new_allocation.y += ty;
1351 window = gdk_window_get_parent (window);
1354 if (!window) /* Window and widget heirarchies didn't match. */
1358 if (data->toplevel ||
1359 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1360 (data->x < new_allocation.x + new_allocation.width) &&
1361 (data->y < new_allocation.y + new_allocation.height)))
1363 /* First, check if the drag is in a valid drop site in
1364 * one of our children
1366 if (GTK_IS_CONTAINER (widget))
1368 GtkDragFindData new_data = *data;
1369 GSList *children = NULL;
1372 new_data.x -= x_offset;
1373 new_data.y -= y_offset;
1374 new_data.found = FALSE;
1375 new_data.toplevel = FALSE;
1377 /* need to reference children temporarily in case the
1378 * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
1380 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
1381 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
1383 if (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
1384 gtk_drag_find_widget (tmp_list->data, &new_data);
1385 gtk_widget_unref (tmp_list->data);
1387 g_slist_free (children);
1389 data->found = new_data.found;
1392 /* If not, and this widget is registered as a drop site, check to
1393 * emit "drag_motion" to check if we are actually in
1397 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1399 data->found = data->callback (widget,
1401 data->x - x_offset - allocation_to_window_x,
1402 data->y - y_offset - allocation_to_window_y,
1404 /* If so, send a "drag_leave" to the last widget */
1407 if (data->info->widget && data->info->widget != widget)
1409 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1411 data->info->widget = widget;
1418 gtk_drag_proxy_begin (GtkWidget *widget,
1419 GtkDragDestInfo *dest_info,
1422 GtkDragSourceInfo *source_info;
1424 GdkDragContext *context;
1425 GtkWidget *ipc_widget;
1427 if (dest_info->proxy_source)
1429 gdk_drag_abort (dest_info->proxy_source->context, time);
1430 gtk_drag_source_info_destroy (dest_info->proxy_source);
1431 dest_info->proxy_source = NULL;
1434 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1435 context = gdk_drag_begin (ipc_widget->window,
1436 dest_info->context->targets);
1438 source_info = gtk_drag_get_source_info (context, TRUE);
1440 source_info->ipc_widget = ipc_widget;
1441 source_info->widget = gtk_widget_ref (widget);
1443 source_info->target_list = gtk_target_list_new (NULL, 0);
1444 tmp_list = dest_info->context->targets;
1447 gtk_target_list_add (source_info->target_list,
1448 GDK_POINTER_TO_ATOM (tmp_list->data), 0, 0);
1449 tmp_list = tmp_list->next;
1452 source_info->proxy_dest = dest_info;
1454 gtk_signal_connect (GTK_OBJECT (ipc_widget),
1456 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1459 dest_info->proxy_source = source_info;
1463 gtk_drag_dest_info_destroy (gpointer data)
1465 GtkDragDestInfo *info = data;
1470 static GtkDragDestInfo *
1471 gtk_drag_get_dest_info (GdkDragContext *context,
1474 GtkDragDestInfo *info;
1475 static GQuark info_quark = 0;
1477 info_quark = g_quark_from_static_string ("gtk-dest-info");
1479 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1480 if (!info && create)
1482 info = g_new (GtkDragDestInfo, 1);
1483 info->widget = NULL;
1484 info->context = context;
1485 info->proxy_source = NULL;
1486 info->proxy_data = NULL;
1487 info->dropped = FALSE;
1488 info->proxy_drop_wait = FALSE;
1489 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1490 info, gtk_drag_dest_info_destroy);
1496 static GQuark dest_info_quark = 0;
1498 static GtkDragSourceInfo *
1499 gtk_drag_get_source_info (GdkDragContext *context,
1502 GtkDragSourceInfo *info;
1503 if (!dest_info_quark)
1504 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1506 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1507 if (!info && create)
1509 info = g_new0 (GtkDragSourceInfo, 1);
1510 info->context = context;
1511 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1518 gtk_drag_clear_source_info (GdkDragContext *context)
1520 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1524 gtk_drag_dest_realized (GtkWidget *widget)
1526 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1528 if (GTK_WIDGET_TOPLEVEL (toplevel))
1529 gdk_window_register_dnd (toplevel->window);
1533 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1534 GtkWidget *previous_toplevel)
1536 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1538 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1539 gdk_window_register_dnd (toplevel->window);
1543 gtk_drag_dest_site_destroy (gpointer data)
1545 GtkDragDestSite *site = data;
1547 if (site->proxy_window)
1548 g_object_unref (site->proxy_window);
1550 if (site->target_list)
1551 gtk_target_list_unref (site->target_list);
1557 * Default drag handlers
1560 gtk_drag_dest_leave (GtkWidget *widget,
1561 GdkDragContext *context,
1564 GtkDragDestSite *site;
1566 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1567 g_return_if_fail (site != NULL);
1571 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1573 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1575 gdk_drag_abort (info->proxy_source->context, time);
1576 gtk_drag_source_info_destroy (info->proxy_source);
1577 info->proxy_source = NULL;
1584 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1585 gtk_drag_unhighlight (widget);
1587 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1588 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1591 site->have_drag = FALSE;
1596 gtk_drag_dest_motion (GtkWidget *widget,
1597 GdkDragContext *context,
1602 GtkDragDestSite *site;
1603 GdkDragAction action = 0;
1606 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1607 g_return_val_if_fail (site != NULL, FALSE);
1612 GdkEvent *current_event;
1613 GdkWindow *dest_window;
1614 GdkDragProtocol proto;
1616 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1618 if (!info->proxy_source || info->proxy_source->widget != widget)
1619 gtk_drag_proxy_begin (widget, info, time);
1621 current_event = gtk_get_current_event ();
1623 if (site->proxy_window)
1625 dest_window = site->proxy_window;
1626 proto = site->proxy_protocol;
1630 gdk_drag_find_window_for_screen (info->proxy_source->context,
1632 gdk_drawable_get_screen (current_event->dnd.window),
1633 current_event->dnd.x_root,
1634 current_event->dnd.y_root,
1635 &dest_window, &proto);
1638 gdk_drag_motion (info->proxy_source->context,
1640 current_event->dnd.x_root,
1641 current_event->dnd.y_root,
1642 context->suggested_action,
1643 context->actions, time);
1645 if (!site->proxy_window && dest_window)
1646 gdk_window_unref (dest_window);
1648 selection = gdk_drag_get_selection (info->proxy_source->context);
1650 selection != gdk_drag_get_selection (info->context))
1651 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1653 gdk_event_free (current_event);
1658 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1660 if (context->suggested_action & site->actions)
1661 action = context->suggested_action;
1668 if ((site->actions & (1 << i)) &&
1669 (context->actions & (1 << i)))
1677 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1679 if (!site->have_drag)
1681 site->have_drag = TRUE;
1682 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1683 gtk_drag_highlight (widget);
1686 gdk_drag_status (context, action, time);
1690 gdk_drag_status (context, 0, time);
1695 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1696 context, x, y, time, &retval);
1698 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1702 gtk_drag_dest_drop (GtkWidget *widget,
1703 GdkDragContext *context,
1708 GtkDragDestSite *site;
1709 GtkDragDestInfo *info;
1711 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1712 g_return_val_if_fail (site != NULL, FALSE);
1714 info = gtk_drag_get_dest_info (context, FALSE);
1715 g_return_val_if_fail (info != NULL, FALSE);
1722 if (info->proxy_source ||
1723 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1725 gtk_drag_drop (info->proxy_source, time);
1729 /* We need to synthesize a motion event, wait for a status,
1730 * and, if we get a good one, do a drop.
1733 GdkEvent *current_event;
1735 GdkWindow *dest_window;
1736 GdkDragProtocol proto;
1738 gtk_drag_proxy_begin (widget, info, time);
1739 info->proxy_drop_wait = TRUE;
1740 info->proxy_drop_time = time;
1742 current_event = gtk_get_current_event ();
1744 if (site->proxy_window)
1746 dest_window = site->proxy_window;
1747 proto = site->proxy_protocol;
1751 gdk_drag_find_window_for_screen (info->proxy_source->context,
1753 gdk_drawable_get_screen (current_event->dnd.window),
1754 current_event->dnd.x_root,
1755 current_event->dnd.y_root,
1756 &dest_window, &proto);
1759 gdk_drag_motion (info->proxy_source->context,
1761 current_event->dnd.x_root,
1762 current_event->dnd.y_root,
1763 context->suggested_action,
1764 context->actions, time);
1766 if (!site->proxy_window && dest_window)
1767 gdk_window_unref (dest_window);
1769 selection = gdk_drag_get_selection (info->proxy_source->context);
1771 selection != gdk_drag_get_selection (info->context))
1772 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1774 gdk_event_free (current_event);
1783 if (site->flags & GTK_DEST_DEFAULT_DROP)
1785 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1787 if (target == GDK_NONE)
1789 gtk_drag_finish (context, FALSE, FALSE, time);
1793 gtk_drag_get_data (widget, context, target, time);
1796 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1797 context, x, y, time, &retval);
1799 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1807 /*************************************************************
1808 * gtk_drag_begin: Start a drag operation
1811 * widget: Widget from which drag starts
1812 * handlers: List of handlers to supply the data for the drag
1813 * button: Button user used to start drag
1814 * time: Time of event starting drag
1817 *************************************************************/
1820 gtk_drag_begin (GtkWidget *widget,
1821 GtkTargetList *target_list,
1822 GdkDragAction actions,
1826 GtkDragSourceInfo *info;
1827 GList *targets = NULL;
1829 guint32 time = GDK_CURRENT_TIME;
1830 GdkDragAction possible_actions, suggested_action;
1831 GdkDragContext *context;
1832 GtkWidget *ipc_widget;
1834 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1835 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1836 g_return_val_if_fail (target_list != NULL, NULL);
1839 time = gdk_event_get_time (event);
1841 tmp_list = g_list_last (target_list->list);
1844 GtkTargetPair *pair = tmp_list->data;
1845 targets = g_list_prepend (targets,
1846 GINT_TO_POINTER (pair->target));
1847 tmp_list = tmp_list->prev;
1850 ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
1851 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1853 context = gdk_drag_begin (ipc_widget->window, targets);
1854 g_list_free (targets);
1856 info = gtk_drag_get_source_info (context, TRUE);
1858 info->ipc_widget = ipc_widget;
1859 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1861 info->widget = gtk_widget_ref (widget);
1864 info->button = button;
1865 info->target_list = target_list;
1866 gtk_target_list_ref (target_list);
1868 info->possible_actions = actions;
1870 info->cursor = NULL;
1871 info->status = GTK_DRAG_STATUS_DRAG;
1872 info->last_event = NULL;
1873 info->selections = NULL;
1874 info->icon_window = NULL;
1875 info->destroy_icon = FALSE;
1877 gtk_drag_get_event_actions (event, info->button, actions,
1878 &suggested_action, &possible_actions);
1880 info->cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget), suggested_action);
1882 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1883 * the drag icon, it will be in the right place
1885 if (event && event->type == GDK_MOTION_NOTIFY)
1887 info->cur_screen = gtk_widget_get_screen (widget);
1888 info->cur_x = event->motion.x_root;
1889 info->cur_y = event->motion.y_root;
1893 gdk_display_get_pointer (gtk_widget_get_display (widget),
1894 &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
1897 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1900 if (event && event->type == GDK_MOTION_NOTIFY)
1901 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1903 info->start_x = info->cur_x;
1904 info->start_y = info->cur_y;
1906 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1907 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1908 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1909 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1910 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
1911 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1912 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
1913 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1914 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1915 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1917 /* We use a GTK grab here to override any grabs that the widget
1918 * we are dragging from might have held
1920 gtk_grab_add (info->ipc_widget);
1921 if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1922 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1923 GDK_BUTTON_RELEASE_MASK, NULL,
1924 info->cursor, time) == 0)
1926 if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1928 gtk_drag_cancel (info, time);
1933 info->have_grab = TRUE;
1934 info->grab_time = time;
1936 return info->context;
1939 /*************************************************************
1940 * gtk_drag_source_set:
1941 * Register a drop site, and possibly add default behaviors.
1944 * start_button_mask: Mask of allowed buttons to start drag
1945 * targets: Table of targets for this source
1947 * actions: Actions allowed for this source
1949 *************************************************************/
1952 gtk_drag_source_set (GtkWidget *widget,
1953 GdkModifierType start_button_mask,
1954 const GtkTargetEntry *targets,
1956 GdkDragAction actions)
1958 GtkDragSourceSite *site;
1960 g_return_if_fail (GTK_IS_WIDGET (widget));
1962 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1964 gtk_widget_add_events (widget,
1965 gtk_widget_get_events (widget) |
1966 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1967 GDK_BUTTON_MOTION_MASK);
1971 if (site->target_list)
1972 gtk_target_list_unref (site->target_list);
1976 site = g_new0 (GtkDragSourceSite, 1);
1978 site->icon_type = GTK_IMAGE_EMPTY;
1980 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1981 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1983 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1984 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1987 gtk_object_set_data_full (GTK_OBJECT (widget),
1989 site, gtk_drag_source_site_destroy);
1992 site->start_button_mask = start_button_mask;
1995 site->target_list = gtk_target_list_new (targets, n_targets);
1997 site->target_list = NULL;
1999 site->actions = actions;
2003 /*************************************************************
2004 * gtk_drag_source_unset
2005 * Unregister this widget as a drag source.
2009 *************************************************************/
2012 gtk_drag_source_unset (GtkWidget *widget)
2014 GtkDragSourceSite *site;
2016 g_return_if_fail (GTK_IS_WIDGET (widget));
2018 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2022 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
2023 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
2028 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
2030 switch (site->icon_type)
2032 case GTK_IMAGE_EMPTY:
2034 case GTK_IMAGE_PIXMAP:
2035 if (site->icon_data.pixmap.pixmap)
2036 gdk_pixmap_unref (site->icon_data.pixmap.pixmap);
2037 if (site->icon_mask)
2038 gdk_pixmap_unref (site->icon_mask);
2040 case GTK_IMAGE_PIXBUF:
2041 g_object_unref (G_OBJECT (site->icon_data.pixbuf.pixbuf));
2043 case GTK_IMAGE_STOCK:
2044 g_free (G_OBJECT (site->icon_data.stock.stock_id));
2047 g_assert_not_reached();
2050 site->icon_type = GTK_IMAGE_EMPTY;
2053 gdk_colormap_unref (site->colormap);
2054 site->colormap = NULL;
2058 * gtk_drag_source_set_icon:
2059 * @widget: a #GtkWidget
2060 * @colormap: the colormap of the icon
2061 * @pixmap: the image data for the icon
2062 * @mask: the transparency mask for an image.
2064 * Sets the icon that will be used for drags from a particular widget
2065 * from a pixmap/mask. GTK+ retains references for the arguments, and
2066 * will release them when they are no longer needed.
2067 * Use gtk_drag_source_set_icon_pixbuf() instead.
2070 gtk_drag_source_set_icon (GtkWidget *widget,
2071 GdkColormap *colormap,
2075 GtkDragSourceSite *site;
2077 g_return_if_fail (GTK_IS_WIDGET (widget));
2078 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2079 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2080 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2082 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2083 g_return_if_fail (site != NULL);
2085 gdk_colormap_ref (colormap);
2086 gdk_pixmap_ref (pixmap);
2088 gdk_pixmap_ref (mask);
2090 gtk_drag_source_unset_icon (site);
2092 site->icon_type = GTK_IMAGE_PIXMAP;
2094 site->icon_data.pixmap.pixmap = pixmap;
2095 site->icon_mask = mask;
2096 site->colormap = colormap;
2100 * gtk_drag_source_set_icon_pixbuf:
2101 * @widget: a #GtkWidget
2102 * @pixbuf: the #GdkPixbuf for the drag icon
2104 * Sets the icon that will be used for drags from a particular widget
2105 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
2106 * release it when it is no longer needed.
2109 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2112 GtkDragSourceSite *site;
2114 g_return_if_fail (GTK_IS_WIDGET (widget));
2115 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2117 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2118 g_return_if_fail (site != NULL);
2119 gdk_pixbuf_ref (pixbuf);
2121 gtk_drag_source_unset_icon (site);
2123 site->icon_type = GTK_IMAGE_PIXBUF;
2124 site->icon_data.pixbuf.pixbuf = pixbuf;
2128 * gtk_drag_source_set_icon_stock:
2129 * @widget: a #GtkWidget
2130 * @stock_id: the ID of the stock icon to use
2132 * Sets the icon that will be used for drags from a particular source
2136 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2137 const gchar *stock_id)
2139 GtkDragSourceSite *site;
2141 g_return_if_fail (GTK_IS_WIDGET (widget));
2142 g_return_if_fail (stock_id != NULL);
2144 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2145 g_return_if_fail (site != NULL);
2147 gtk_drag_source_unset_icon (site);
2149 site->icon_data.stock.stock_id = g_strdup (stock_id);
2153 gtk_drag_get_icon (GtkDragSourceInfo *info,
2154 GtkWidget **icon_window,
2158 if (get_can_change_screen (info->icon_window))
2159 gtk_window_set_screen (GTK_WINDOW (info->icon_window),
2162 if (gtk_widget_get_screen (info->icon_window) != info->cur_screen)
2164 if (!info->fallback_icon)
2166 gint save_hot_x, save_hot_y;
2167 gboolean save_destroy_icon;
2168 GtkWidget *save_icon_window;
2170 /* HACK to get the appropriate icon
2172 save_icon_window = info->icon_window;
2173 save_hot_x = info->hot_x;
2174 save_hot_y = info->hot_x;
2175 save_destroy_icon = info->destroy_icon;
2177 info->icon_window = NULL;
2178 gtk_drag_set_icon_default (info->context);
2179 info->fallback_icon = info->icon_window;
2181 info->icon_window = save_icon_window;
2182 info->hot_x = save_hot_x;
2183 info->hot_y = save_hot_y;
2184 info->destroy_icon = save_destroy_icon;
2187 gtk_widget_hide (info->icon_window);
2189 *icon_window = info->fallback_icon;
2190 gtk_window_set_screen (GTK_WINDOW (*icon_window), info->cur_screen);
2192 if (!default_icon_pixmap)
2199 *hot_x = default_icon_hot_x;
2200 *hot_y = default_icon_hot_y;
2205 if (info->fallback_icon)
2206 gtk_widget_hide (info->fallback_icon);
2208 *icon_window = info->icon_window;
2209 *hot_x = info->hot_x;
2210 *hot_y = info->hot_y;
2215 gtk_drag_update_icon (GtkDragSourceInfo *info)
2217 if (info->icon_window)
2219 GtkWidget *icon_window;
2222 gtk_drag_get_icon (info, &icon_window, &hot_x, &hot_y);
2224 gtk_widget_set_uposition (icon_window,
2225 info->cur_x - hot_x,
2226 info->cur_y - hot_y);
2228 if (GTK_WIDGET_VISIBLE (icon_window))
2229 gdk_window_raise (icon_window->window);
2231 gtk_widget_show (icon_window);
2236 gtk_drag_set_icon_window (GdkDragContext *context,
2240 gboolean destroy_on_release)
2242 GtkDragSourceInfo *info;
2244 info = gtk_drag_get_source_info (context, FALSE);
2245 gtk_drag_remove_icon (info);
2248 gtk_widget_ref (widget);
2250 info->icon_window = widget;
2251 info->hot_x = hot_x;
2252 info->hot_y = hot_y;
2253 info->destroy_icon = destroy_on_release;
2255 gtk_drag_update_icon (info);
2259 * gtk_drag_set_icon_widget:
2260 * @context: the context for a drag. (This must be called
2261 with a context for the source side of a drag)
2262 * @widget: a toplevel window to use as an icon.
2263 * @hot_x: the X offset within @widget of the hotspot.
2264 * @hot_y: the Y offset within @widget of the hotspot.
2266 * Changes the icon for a widget to a given widget. GTK+
2267 * will not destroy the icon, so if you don't want
2268 * it to persist, you should connect to the "drag_end"
2269 * signal and destroy it yourself.
2272 gtk_drag_set_icon_widget (GdkDragContext *context,
2277 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2278 g_return_if_fail (context->is_source);
2279 g_return_if_fail (GTK_IS_WIDGET (widget));
2281 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2285 icon_window_realize (GtkWidget *window,
2291 gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf,
2292 gtk_widget_get_colormap (window),
2293 &pixmap, &mask, 128);
2295 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2298 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2300 g_object_unref (G_OBJECT (pixmap));
2303 g_object_unref (G_OBJECT (mask));
2307 set_icon_stock_pixbuf (GdkDragContext *context,
2308 const gchar *stock_id,
2317 g_return_if_fail (context != NULL);
2318 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2319 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2321 screen = gdk_drawable_get_screen (context->source_window);
2323 /* Push a NULL colormap to guard against gtk_widget_push_colormap() */
2324 gtk_widget_push_colormap (NULL);
2325 window = gtk_window_new (GTK_WINDOW_POPUP);
2326 gtk_window_set_screen (GTK_WINDOW (window), screen);
2327 set_can_change_screen (window, TRUE);
2328 gtk_widget_pop_colormap ();
2330 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2331 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2335 pixbuf = gtk_widget_render_icon (window, stock_id,
2336 GTK_ICON_SIZE_DND, NULL);
2340 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2341 gtk_widget_destroy (window);
2347 g_object_ref (pixbuf);
2349 width = gdk_pixbuf_get_width (pixbuf);
2350 height = gdk_pixbuf_get_width (pixbuf);
2352 gtk_widget_set_usize (window,
2353 gdk_pixbuf_get_width (pixbuf),
2354 gdk_pixbuf_get_height (pixbuf));
2356 g_signal_connect_closure (window, "realize",
2357 g_cclosure_new (G_CALLBACK (icon_window_realize),
2359 (GClosureNotify)g_object_unref),
2362 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2366 * gtk_drag_set_icon_pixbuf:
2367 * @context: the context for a drag. (This must be called
2368 * with a context for the source side of a drag)
2369 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2370 * @hot_x: the X offset within @widget of the hotspot.
2371 * @hot_y: the Y offset within @widget of the hotspot.
2373 * Sets @pixbuf as the icon for a given drag.
2376 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2381 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2382 g_return_if_fail (context->is_source);
2383 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2385 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2389 * gtk_drag_set_icon_stock:
2390 * @context: the context for a drag. (This must be called
2391 * with a context for the source side of a drag)
2392 * @stock_id: the ID of the stock icon to use for the drag.
2393 * @hot_x: the X offset within the icon of the hotspot.
2394 * @hot_y: the Y offset within the icon of the hotspot.
2396 * Sets the the icon for a given drag from a stock ID.
2399 gtk_drag_set_icon_stock (GdkDragContext *context,
2400 const gchar *stock_id,
2404 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2405 g_return_if_fail (context->is_source);
2406 g_return_if_fail (stock_id != NULL);
2408 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2412 * gtk_drag_set_icon_pixmap:
2413 * @context: the context for a drag. (This must be called
2414 * with a context for the source side of a drag)
2415 * @colormap: the colormap of the icon
2416 * @pixmap: the image data for the icon
2417 * @mask: the transparency mask for the icon
2418 * @hot_x: the X offset within @pixmap of the hotspot.
2419 * @hot_y: the Y offset within @pixmap of the hotspot.
2421 * Sets @pixmap as the icon for a given drag. GTK+ retains
2422 * references for the arguments, and will release them when
2423 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2424 * will be more convenient to use.
2427 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2428 GdkColormap *colormap,
2438 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2439 g_return_if_fail (context->is_source);
2440 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2441 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2442 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2444 screen = gdk_colormap_get_screen (colormap);
2446 g_return_if_fail (gdk_drawable_get_screen (pixmap) == screen);
2447 g_return_if_fail (!mask || gdk_drawable_get_screen (mask) == screen);
2449 gdk_window_get_size (pixmap, &width, &height);
2451 gtk_widget_push_colormap (colormap);
2453 window = gtk_window_new (GTK_WINDOW_POPUP);
2454 gtk_window_set_screen (GTK_WINDOW (window), screen);
2455 set_can_change_screen (window, FALSE);
2456 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2457 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2459 gtk_widget_pop_colormap ();
2461 gtk_widget_set_usize (window, width, height);
2462 gtk_widget_realize (window);
2464 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2467 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2469 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2473 * gtk_drag_set_icon_default:
2474 * @context: the context for a drag. (This must be called
2475 with a context for the source side of a drag)
2477 * Sets the icon for a particular drag to the default
2481 gtk_drag_set_icon_default (GdkDragContext *context)
2483 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2484 g_return_if_fail (context->is_source);
2486 if (!default_icon_pixmap)
2487 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2489 gtk_drag_set_icon_pixmap (context,
2490 default_icon_colormap,
2491 default_icon_pixmap,
2494 default_icon_hot_y);
2498 * gtk_drag_set_default_icon:
2499 * @colormap: the colormap of the icon
2500 * @pixmap: the image data for the icon
2501 * @mask: the transparency mask for an image.
2502 * @hot_x: The X offset within @widget of the hotspot.
2503 * @hot_y: The Y offset within @widget of the hotspot.
2505 * Changes the default drag icon. GTK+ retains references for the
2506 * arguments, and will release them when they are no longer needed.
2507 * This function is obsolete. The default icon should now be changed
2508 * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
2511 gtk_drag_set_default_icon (GdkColormap *colormap,
2517 g_return_if_fail (GDK_IS_COLORMAP (colormap));
2518 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
2519 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
2521 if (default_icon_colormap)
2522 gdk_colormap_unref (default_icon_colormap);
2523 if (default_icon_pixmap)
2524 gdk_pixmap_unref (default_icon_pixmap);
2525 if (default_icon_mask)
2526 gdk_pixmap_unref (default_icon_mask);
2528 default_icon_colormap = colormap;
2529 gdk_colormap_ref (colormap);
2531 default_icon_pixmap = pixmap;
2532 gdk_pixmap_ref (pixmap);
2534 default_icon_mask = mask;
2536 gdk_pixmap_ref (mask);
2538 default_icon_hot_x = hot_x;
2539 default_icon_hot_y = hot_y;
2543 /*************************************************************
2544 * _gtk_drag_source_handle_event:
2545 * Called from widget event handling code on Drag events
2549 * toplevel: Toplevel widget that received the event
2552 *************************************************************/
2555 _gtk_drag_source_handle_event (GtkWidget *widget,
2558 GtkDragSourceInfo *info;
2559 GdkDragContext *context;
2561 g_return_if_fail (widget != NULL);
2562 g_return_if_fail (event != NULL);
2564 context = event->dnd.context;
2565 info = gtk_drag_get_source_info (context, FALSE);
2569 switch (event->type)
2571 case GDK_DRAG_STATUS:
2575 if (info->proxy_dest)
2577 if (!event->dnd.send_event)
2579 if (info->proxy_dest->proxy_drop_wait)
2581 gboolean result = context->action != 0;
2583 /* Aha - we can finally pass the MOTIF DROP on... */
2584 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2586 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2588 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2592 gdk_drag_status (info->proxy_dest->context,
2593 event->dnd.context->action,
2598 else if (info->have_grab)
2600 cursor = gtk_drag_get_cursor (gtk_widget_get_display (widget),
2601 event->dnd.context->action);
2602 if (info->cursor != cursor)
2604 gdk_pointer_grab (widget->window, FALSE,
2605 GDK_POINTER_MOTION_MASK |
2606 GDK_POINTER_MOTION_HINT_MASK |
2607 GDK_BUTTON_RELEASE_MASK,
2609 cursor, info->grab_time);
2610 info->cursor = cursor;
2613 if (info->last_event)
2615 gtk_drag_update (info,
2616 info->cur_screen, info->cur_x, info->cur_y,
2618 info->last_event = NULL;
2624 case GDK_DROP_FINISHED:
2625 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2628 g_assert_not_reached ();
2632 /*************************************************************
2633 * gtk_drag_source_check_selection:
2634 * Check if we've set up handlers/claimed the selection
2635 * for a given drag. If not, add them.
2639 *************************************************************/
2642 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2648 tmp_list = info->selections;
2651 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2653 tmp_list = tmp_list->next;
2656 gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
2660 info->selections = g_list_prepend (info->selections,
2661 GUINT_TO_POINTER (selection));
2663 tmp_list = info->target_list->list;
2666 GtkTargetPair *pair = tmp_list->data;
2668 gtk_selection_add_target (info->ipc_widget,
2672 tmp_list = tmp_list->next;
2675 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2677 gtk_selection_add_target (info->ipc_widget,
2679 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2680 TARGET_MOTIF_SUCCESS);
2681 gtk_selection_add_target (info->ipc_widget,
2683 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2684 TARGET_MOTIF_FAILURE);
2687 gtk_selection_add_target (info->ipc_widget,
2689 gdk_atom_intern ("DELETE", FALSE),
2693 /*************************************************************
2694 * gtk_drag_drop_finished:
2695 * Clean up from the drag, and display snapback, if necessary.
2701 *************************************************************/
2704 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2708 gtk_drag_source_release_selections (info, time);
2710 if (info->proxy_dest)
2712 /* The time from the event isn't reliable for Xdnd drags */
2713 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2714 info->proxy_dest->proxy_drop_time);
2715 gtk_drag_source_info_destroy (info);
2721 gtk_drag_source_info_destroy (info);
2725 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2729 anim->n_steps = MAX (info->cur_x - info->start_x,
2730 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2731 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2733 info->cur_screen = gtk_widget_get_screen (info->widget);
2734 gtk_drag_update_icon (info);
2736 /* Mark the context as dead, so if the destination decides
2737 * to respond really late, we still are OK.
2739 gtk_drag_clear_source_info (info->context);
2740 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2746 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2749 GdkDisplay *display = gtk_widget_get_display (info->widget);
2750 GList *tmp_list = info->selections;
2754 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2755 if (gdk_selection_owner_get_for_display (display, selection) == info->ipc_widget->window)
2756 gtk_selection_owner_set_for_display (display, NULL, selection, time);
2758 tmp_list = tmp_list->next;
2761 g_list_free (info->selections);
2762 info->selections = NULL;
2765 /*************************************************************
2767 * Send a drop event.
2771 *************************************************************/
2774 gtk_drag_drop (GtkDragSourceInfo *info,
2777 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2779 GtkSelectionData selection_data;
2781 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2783 tmp_list = info->target_list->list;
2786 GtkTargetPair *pair = tmp_list->data;
2788 if (pair->target == target)
2790 selection_data.selection = GDK_NONE;
2791 selection_data.target = target;
2792 selection_data.data = NULL;
2793 selection_data.length = -1;
2795 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2796 info->context, &selection_data,
2800 /* FIXME: Should we check for length >= 0 here? */
2801 gtk_drag_drop_finished (info, TRUE, time);
2804 tmp_list = tmp_list->next;
2806 gtk_drag_drop_finished (info, FALSE, time);
2810 if (info->icon_window)
2811 gtk_widget_hide (info->icon_window);
2813 gdk_drag_drop (info->context, time);
2814 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2815 gtk_drag_abort_timeout,
2821 * Source side callbacks.
2825 gtk_drag_source_event_cb (GtkWidget *widget,
2829 GtkDragSourceSite *site;
2830 gboolean retval = FALSE;
2831 site = (GtkDragSourceSite *)data;
2833 switch (event->type)
2835 case GDK_BUTTON_PRESS:
2836 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2838 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2839 site->x = event->button.x;
2840 site->y = event->button.y;
2844 case GDK_BUTTON_RELEASE:
2845 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2846 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2849 case GDK_MOTION_NOTIFY:
2850 if (site->state & event->motion.state & site->start_button_mask)
2852 /* FIXME: This is really broken and can leave us
2858 if (site->state & event->motion.state &
2859 GDK_BUTTON1_MASK << (i - 1))
2863 if (gtk_drag_check_threshold (widget, site->x, site->y,
2864 event->motion.x, event->motion.y))
2866 GtkDragSourceInfo *info;
2867 GdkDragContext *context;
2870 context = gtk_drag_begin (widget, site->target_list,
2874 info = gtk_drag_get_source_info (context, FALSE);
2876 if (!info->icon_window)
2878 switch (site->icon_type)
2880 case GTK_IMAGE_EMPTY:
2881 gtk_drag_set_icon_default (context);
2883 case GTK_IMAGE_PIXMAP:
2884 gtk_drag_set_icon_pixmap (context,
2886 site->icon_data.pixmap.pixmap,
2890 case GTK_IMAGE_PIXBUF:
2891 gtk_drag_set_icon_pixbuf (context,
2892 site->icon_data.pixbuf.pixbuf,
2895 case GTK_IMAGE_STOCK:
2896 gtk_drag_set_icon_stock (context,
2897 site->icon_data.stock.stock_id,
2901 g_assert_not_reached();
2911 default: /* hit for 2/3BUTTON_PRESS */
2919 gtk_drag_source_site_destroy (gpointer data)
2921 GtkDragSourceSite *site = data;
2923 if (site->target_list)
2924 gtk_target_list_unref (site->target_list);
2926 gtk_drag_source_unset_icon (site);
2931 gtk_drag_selection_get (GtkWidget *widget,
2932 GtkSelectionData *selection_data,
2937 GtkDragSourceInfo *info = data;
2938 static GdkAtom null_atom = GDK_NONE;
2942 null_atom = gdk_atom_intern ("NULL", FALSE);
2947 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2950 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2952 case TARGET_MOTIF_SUCCESS:
2953 gtk_drag_drop_finished (info, TRUE, time);
2954 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2956 case TARGET_MOTIF_FAILURE:
2957 gtk_drag_drop_finished (info, FALSE, time);
2958 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2961 if (info->proxy_dest)
2963 /* This is sort of dangerous and needs to be thought
2966 info->proxy_dest->proxy_data = selection_data;
2967 gtk_drag_get_data (info->widget,
2968 info->proxy_dest->context,
2969 selection_data->target,
2972 info->proxy_dest->proxy_data = NULL;
2976 if (gtk_target_list_find (info->target_list,
2977 selection_data->target,
2980 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2992 gtk_drag_anim_timeout (gpointer data)
2994 GtkDragAnim *anim = data;
2998 GDK_THREADS_ENTER ();
3000 if (anim->step == anim->n_steps)
3002 gtk_drag_source_info_destroy (anim->info);
3009 x = (anim->info->start_x * (anim->step + 1) +
3010 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3011 y = (anim->info->start_y * (anim->step + 1) +
3012 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
3013 if (anim->info->icon_window)
3015 GtkWidget *icon_window;
3018 gtk_drag_get_icon (anim->info, &icon_window, &hot_x, &hot_y);
3020 gtk_widget_set_uposition (icon_window,
3030 GDK_THREADS_LEAVE ();
3036 gtk_drag_remove_icon (GtkDragSourceInfo *info)
3038 if (info->icon_window)
3040 gtk_widget_hide (info->icon_window);
3041 if (info->destroy_icon)
3042 gtk_widget_destroy (info->icon_window);
3044 if (info->fallback_icon)
3046 gtk_widget_destroy (info->fallback_icon);
3047 info->fallback_icon = NULL;
3050 gtk_widget_unref (info->icon_window);
3051 info->icon_window = NULL;
3056 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
3058 gtk_drag_remove_icon (info);
3060 if (!info->proxy_dest)
3061 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
3065 gtk_widget_unref (info->widget);
3067 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
3068 gtk_selection_remove_all (info->ipc_widget);
3069 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
3070 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
3071 gtk_drag_release_ipc_widget (info->ipc_widget);
3073 gtk_target_list_unref (info->target_list);
3075 gtk_drag_clear_source_info (info->context);
3076 gdk_drag_context_unref (info->context);
3078 if (info->drop_timeout)
3079 gtk_timeout_remove (info->drop_timeout);
3084 /*************************************************************
3086 * Function to update the status of the drag when the
3087 * cursor moves or the modifier changes
3089 * info: DragSourceInfo for the drag
3090 * x_root, y_root: position of darg
3091 * event: The event that triggered this call
3093 *************************************************************/
3096 gtk_drag_update (GtkDragSourceInfo *info,
3102 GdkDragAction action;
3103 GdkDragAction possible_actions;
3104 GdkWindow *dest_window;
3105 GdkDragProtocol protocol;
3107 guint32 time = gtk_drag_get_event_time (event);
3109 gtk_drag_get_event_actions (event,
3111 info->possible_actions,
3112 &action, &possible_actions);
3113 info->cur_screen = screen;
3114 info->cur_x = x_root;
3115 info->cur_y = y_root;
3117 gtk_drag_update_icon (info);
3118 gdk_drag_find_window_for_screen (info->context,
3119 info->icon_window ? info->icon_window->window : NULL,
3120 screen, x_root, y_root,
3121 &dest_window, &protocol);
3123 if (gdk_drag_motion (info->context, dest_window, protocol,
3124 x_root, y_root, action,
3128 if (info->last_event != event) /* Paranoia, should not happen */
3130 if (info->last_event)
3131 gdk_event_free ((GdkEvent *)info->last_event);
3132 info->last_event = gdk_event_copy ((GdkEvent *)event);
3137 if (info->last_event)
3139 gdk_event_free ((GdkEvent *)info->last_event);
3140 info->last_event = NULL;
3145 gdk_window_unref (dest_window);
3147 selection = gdk_drag_get_selection (info->context);
3149 gtk_drag_source_check_selection (info, selection, time);
3152 /*************************************************************
3154 * Called when the user finishes to drag, either by
3155 * releasing the mouse, or by pressing Esc.
3157 * info: Source info for the drag
3158 * time: Timestamp for ending the drag
3160 *************************************************************/
3163 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
3165 GdkEvent *send_event;
3166 GtkWidget *source_widget = info->widget;
3167 GdkDisplay *display = gtk_widget_get_display (source_widget);
3169 info->have_grab = FALSE;
3171 gdk_display_pointer_ungrab (display, time);
3172 gdk_display_keyboard_ungrab (display, time);
3173 gtk_grab_remove (info->ipc_widget);
3175 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3176 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
3178 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3179 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
3181 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
3182 GTK_SIGNAL_FUNC (gtk_drag_key_cb),
3185 /* Send on a release pair to the the original
3186 * widget to convince it to release its grab. We need to
3187 * call gtk_propagate_event() here, instead of
3188 * gtk_widget_event() because widget like GtkList may
3189 * expect propagation.
3192 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
3193 send_event->button.window = g_object_ref (gtk_widget_get_root_window (source_widget));
3194 send_event->button.send_event = TRUE;
3195 send_event->button.time = time;
3196 send_event->button.x = 0;
3197 send_event->button.y = 0;
3198 send_event->button.axes = NULL;
3199 send_event->button.state = 0;
3200 send_event->button.button = info->button;
3201 send_event->button.device = gdk_display_get_core_pointer (display);
3202 send_event->button.x_root = 0;
3203 send_event->button.y_root = 0;
3205 gtk_propagate_event (source_widget, send_event);
3206 gdk_event_free (send_event);
3209 /*************************************************************
3211 * Called on cancellation of a drag, either by the user
3212 * or programmatically.
3214 * info: Source info for the drag
3215 * time: Timestamp for ending the drag
3217 *************************************************************/
3220 gtk_drag_cancel (GtkDragSourceInfo *info, guint32 time)
3222 gtk_drag_end (info, time);
3223 gdk_drag_abort (info->context, time);
3224 gtk_drag_drop_finished (info, FALSE, time);
3227 /*************************************************************
3228 * gtk_drag_motion_cb:
3229 * "motion_notify_event" callback during drag.
3233 *************************************************************/
3236 gtk_drag_motion_cb (GtkWidget *widget,
3237 GdkEventMotion *event,
3240 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3242 gint x_root, y_root;
3246 GdkDisplay *display = gtk_widget_get_display (widget);
3248 gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
3249 event->x_root = x_root;
3250 event->y_root = y_root;
3253 screen = gdk_event_get_screen ((GdkEvent *)event);
3255 gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
3260 /*************************************************************
3262 * "key_press/release_event" callback during drag.
3266 *************************************************************/
3269 gtk_drag_key_cb (GtkWidget *widget,
3273 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3274 GdkModifierType state;
3275 GdkWindow *root_window;
3277 if (event->type == GDK_KEY_PRESS)
3279 if (event->keyval == GDK_Escape)
3281 gtk_drag_cancel (info, event->time);
3287 /* Now send a "motion" so that the modifier state is updated */
3289 /* The state is not yet updated in the event, so we need
3290 * to query it here. We could use XGetModifierMapping, but
3291 * that would be overkill.
3293 root_window = gtk_widget_get_root_window (widget);
3294 gdk_window_get_pointer (root_window, NULL, NULL, &state);
3296 event->state = state;
3297 gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
3302 /*************************************************************
3303 * gtk_drag_button_release_cb:
3304 * "button_release_event" callback during drag.
3308 *************************************************************/
3311 gtk_drag_button_release_cb (GtkWidget *widget,
3312 GdkEventButton *event,
3315 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3317 if (event->button != info->button)
3320 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3322 gtk_drag_end (info, event->time);
3323 gtk_drag_drop (info, event->time);
3327 gtk_drag_cancel (info, event->time);
3334 gtk_drag_abort_timeout (gpointer data)
3336 GtkDragSourceInfo *info = data;
3337 guint32 time = GDK_CURRENT_TIME;
3339 GDK_THREADS_ENTER ();
3341 if (info->proxy_dest)
3342 time = info->proxy_dest->proxy_drop_time;
3344 info->drop_timeout = 0;
3345 gtk_drag_drop_finished (info, FALSE, time);
3347 GDK_THREADS_LEAVE ();
3353 * gtk_drag_check_threshold:
3354 * @widget: a #GtkWidget
3355 * @start_x: X coordinate of start of drag
3356 * @start_y: Y coordinate of start of drag
3357 * @current_x: current X coordinate
3358 * @current_y: current Y coordinate
3360 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
3361 * at (@current_x, @current_y) has passed the GTK+ drag threshhold, and thus
3362 * should trigger the beginning of a drag-and-drop operation.
3364 * Return Value: %TRUE if the drag threshold has been passed.
3367 gtk_drag_check_threshold (GtkWidget *widget,
3373 gint drag_threshold;
3375 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
3377 g_object_get (gtk_widget_get_settings (widget),
3378 "gtk-dnd-drag-threshold", &drag_threshold,
3381 return (ABS (current_x - start_x) > drag_threshold ||
3382 ABS (current_y - start_y) > drag_threshold);