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 Library 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 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library 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.
23 #include "gtkinvisible.h"
25 #include "gtksignal.h"
26 #include "gtkwindow.h"
28 static GSList *drag_widgets = NULL;
29 static GSList *source_widgets = NULL;
31 typedef struct _GtkDragSourceSite GtkDragSourceSite;
32 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
33 typedef struct _GtkDragDestSite GtkDragDestSite;
34 typedef struct _GtkDragDestInfo GtkDragDestInfo;
35 typedef struct _GtkDragAnim GtkDragAnim;
36 typedef struct _GtkDragFindData GtkDragFindData;
45 struct _GtkDragSourceSite {
46 GdkModifierType start_button_mask;
47 GtkTargetList *target_list; /* Targets for drag data */
48 GdkDragAction actions; /* Possible actions */
49 GdkColormap *colormap; /* Colormap for drag icon */
50 GdkPixmap *pixmap; /* Icon for drag data */
53 /* Stored button press information to detect drag beginning */
58 struct _GtkDragSourceInfo {
60 GtkTargetList *target_list; /* Targets for drag data */
61 GdkDragContext *context; /* drag context */
62 GtkWidget *icon_window; /* Window for drag */
63 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
64 GdkCursor *cursor; /* Cursor for drag */
65 gint hot_x, hot_y; /* Hot spot for drag */
66 gint button; /* mouse button starting drag */
68 GtkDragStatus status; /* drag status */
69 GdkEvent *last_event; /* motion event waiting for response */
71 gint start_x, start_y; /* Initial position */
72 gint cur_x, cur_y; /* Current Position */
74 GList *selections; /* selections we've claimed */
76 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
78 guint drop_timeout; /* Timeout for aborting drop */
81 struct _GtkDragDestSite {
82 GtkDestDefaults flags;
83 GtkTargetList *target_list;
84 GdkDragAction actions;
85 GdkWindow *proxy_window;
86 GdkDragProtocol proxy_protocol;
87 gboolean do_proxy : 1;
88 gboolean proxy_coords : 1;
89 gboolean have_drag : 1;
92 struct _GtkDragDestInfo {
93 GtkWidget *widget; /* Widget in which drag is in */
94 GdkDragContext *context; /* Drag context */
95 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
96 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
97 gboolean dropped : 1; /* Set after we receive a drop */
98 guint32 proxy_drop_time; /* Timestamp for proxied drop */
99 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
100 * status reply before sending
103 gint drop_x, drop_y; /* Position of drop */
106 #define DROP_ABORT_TIME 300000
108 #define ANIM_STEP_TIME 50
109 #define ANIM_STEP_LENGTH 50
110 #define ANIM_MIN_STEPS 5
111 #define ANIM_MAX_STEPS 10
113 struct _GtkDragAnim {
114 GtkDragSourceInfo *info;
119 struct _GtkDragFindData {
122 GdkDragContext *context;
123 GtkDragDestInfo *info;
126 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
127 gint x, gint y, guint32 time);
131 /* Enumeration for some targets we handle internally */
134 TARGET_MOTIF_SUCCESS = 0x40000000,
135 TARGET_MOTIF_FAILURE,
141 static GdkPixmap *default_icon_pixmap = NULL;
142 static GdkPixmap *default_icon_mask = NULL;
143 static GdkColormap *default_icon_colormap = NULL;
144 static gint default_icon_hot_x;
145 static gint default_icon_hot_y;
147 /* Forward declarations */
148 static GdkDragAction gtk_drag_get_event_action (GdkEvent *event,
150 GdkDragAction actions);
151 static GdkCursor * gtk_drag_get_cursor (GdkDragAction action);
152 static GtkWidget *gtk_drag_get_ipc_widget (void);
153 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
155 static GdkAtom gtk_drag_dest_find_target (GtkWidget *widget,
156 GtkDragDestSite *site,
157 GdkDragContext *context);
158 static void gtk_drag_selection_received (GtkWidget *widget,
159 GtkSelectionData *selection_data,
162 static void gtk_drag_find_widget (GtkWidget *widget,
163 GtkDragFindData *data);
164 static void gtk_drag_proxy_begin (GtkWidget *widget,
165 GtkDragDestInfo *dest_info);
166 static void gtk_drag_dest_info_destroy (gpointer data);
167 static void gtk_drag_dest_realized (GtkWidget *widget);
168 static void gtk_drag_dest_site_destroy (gpointer data);
169 static void gtk_drag_dest_leave (GtkWidget *widget,
170 GdkDragContext *context,
172 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
173 GdkDragContext *context,
177 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
178 GdkDragContext *context,
183 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
186 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
188 static void gtk_drag_drop (GtkDragSourceInfo *info,
190 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
194 static gint gtk_drag_source_event_cb (GtkWidget *widget,
197 static void gtk_drag_source_site_destroy (gpointer data);
198 static void gtk_drag_selection_get (GtkWidget *widget,
199 GtkSelectionData *selection_data,
203 static gint gtk_drag_anim_timeout (gpointer data);
204 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
205 static void gtk_drag_source_info_destroy (gpointer data);
206 static gint gtk_drag_motion_cb (GtkWidget *widget,
207 GdkEventMotion *event,
209 static gint gtk_drag_button_release_cb (GtkWidget *widget,
210 GdkEventButton *event,
212 static gint gtk_drag_abort_timeout (gpointer data);
214 /************************
215 * Cursor and Icon data *
216 ************************/
218 #define action_ask_width 16
219 #define action_ask_height 16
220 static const char action_ask_bits[] = {
221 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
222 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
223 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
225 #define action_ask_mask_width 16
226 #define action_ask_mask_height 16
227 static const char action_ask_mask_bits[] = {
228 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
229 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
230 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
232 #define action_copy_width 16
233 #define action_copy_height 16
234 static const char action_copy_bits[] = {
235 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
236 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
237 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
239 #define action_copy_mask_width 16
240 #define action_copy_mask_height 16
241 static const char action_copy_mask_bits[] = {
242 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
243 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
244 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
246 #define action_move_width 16
247 #define action_move_height 16
248 static const char action_move_bits[] = {
249 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
250 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
251 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
253 #define action_move_mask_width 16
254 #define action_move_mask_height 16
255 static const char action_move_mask_bits[] = {
256 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
257 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
258 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
260 #define action_link_width 16
261 #define action_link_height 16
262 static const char action_link_bits[] = {
263 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
264 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
265 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
267 #define action_link_mask_width 16
268 #define action_link_mask_height 16
269 static const char action_link_mask_bits[] = {
270 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
271 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
272 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
274 #define action_none_width 16
275 #define action_none_height 16
276 static const char action_none_bits[] = {
277 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
278 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
279 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
281 #define action_none_mask_width 16
282 #define action_none_mask_height 16
283 static const char action_none_mask_bits[] = {
284 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
285 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
286 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
288 #define CURSOR_WIDTH 16
289 #define CURSOR_HEIGHT 16
292 GdkDragAction action;
297 { GDK_ACTION_DEFAULT, 0 },
298 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
299 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
300 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
301 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
302 { 0 , action_none_bits, action_none_mask_bits, NULL },
305 static const gint n_drag_cursors = sizeof(drag_cursors) / sizeof(drag_cursors[0]);
308 static const char * drag_default_xpm[] = {
321 " ...+++++++++++.. ",
322 " ..+.++++++++++++.. ",
323 " .++.++++++++++++.. ",
324 " .+++.++++++++++++.. ",
325 " .++++.++++++++++++. ",
326 " .+++.+++++++++++++.. ",
327 " .++.+++++++++++++++.. ",
328 " .+.+++++++++++++++++.. ",
329 " ..+++++++++++++++++++.. ",
330 " ..++++++++++++++++++++. ",
331 " .++++++++++++++++++++.. ",
332 " ..+++++++++++++++++.. ",
333 " .++++++++++++++++.. ",
334 " ..+++++++++++++... ",
346 /*********************
347 * Utility functions *
348 *********************/
350 /*************************************************************
351 * gtk_drag_get_ipc_widget:
352 * Return a invisible, off-screen, override-redirect
357 *************************************************************/
360 gtk_drag_get_ipc_widget(void)
366 GSList *tmp = drag_widgets;
367 result = drag_widgets->data;
368 drag_widgets = drag_widgets->next;
369 g_slist_free_1 (tmp);
373 result = gtk_invisible_new();
374 gtk_widget_show (result);
380 /*************************************************************
381 * gtk_drag_release_ipc_widget:
382 * Releases widget retrieved with gtk_drag_get_ipc_widget()
384 * widget: the widget to release.
386 *************************************************************/
389 gtk_drag_release_ipc_widget (GtkWidget *widget)
391 drag_widgets = g_slist_prepend (drag_widgets, widget);
395 gtk_drag_get_event_action (GdkEvent *event, gint button, GdkDragAction actions)
399 GdkModifierType state = 0;
403 case GDK_MOTION_NOTIFY:
404 state = event->motion.state;
406 case GDK_BUTTON_PRESS:
407 case GDK_2BUTTON_PRESS:
408 case GDK_3BUTTON_PRESS:
409 case GDK_BUTTON_RELEASE:
410 state = event->button.state;
413 case GDK_KEY_RELEASE:
414 state = event->key.state;
416 case GDK_ENTER_NOTIFY:
417 case GDK_LEAVE_NOTIFY:
418 state = event->crossing.state;
425 return GDK_ACTION_ASK;
427 if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
429 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
430 return (actions & GDK_ACTION_LINK) ? GDK_ACTION_LINK : 0;
431 else if (state & GDK_CONTROL_MASK)
432 return (actions & GDK_ACTION_COPY) ? GDK_ACTION_COPY : 0;
434 return (actions & GDK_ACTION_MOVE) ? GDK_ACTION_MOVE : 0;
438 if (actions & GDK_ACTION_COPY)
439 return GDK_ACTION_COPY;
440 else if (actions & GDK_ACTION_MOVE)
441 return GDK_ACTION_MOVE;
442 else if (actions & GDK_ACTION_LINK)
443 return GDK_ACTION_LINK;
451 gtk_drag_get_cursor (GdkDragAction action)
455 for (i = 0 ; i < n_drag_cursors - 1; i++)
456 if (drag_cursors[i].action == action)
459 if (drag_cursors[i].cursor == NULL)
464 gdk_bitmap_create_from_data (NULL,
465 drag_cursors[i].bits,
466 CURSOR_WIDTH, CURSOR_HEIGHT);
468 gdk_bitmap_create_from_data (NULL,
469 drag_cursors[i].mask,
470 CURSOR_WIDTH, CURSOR_HEIGHT);
472 gdk_color_white (gdk_colormap_get_system(), &bg);
473 gdk_color_black (gdk_colormap_get_system(), &fg);
475 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
477 gdk_pixmap_unref (pixmap);
478 gdk_pixmap_unref (mask);
481 return drag_cursors[i].cursor;
484 /********************
486 ********************/
488 /*************************************************************
490 * Get the data for a drag or drop
492 * context - drag context
493 * target - format to retrieve the data in.
494 * time - timestamp of triggering event.
497 *************************************************************/
500 gtk_drag_get_data (GtkWidget *widget,
501 GdkDragContext *context,
505 GtkWidget *selection_widget;
507 g_return_if_fail (widget != NULL);
508 g_return_if_fail (context != NULL);
510 selection_widget = gtk_drag_get_ipc_widget();
512 gdk_drag_context_ref (context);
513 gtk_widget_ref (widget);
515 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
516 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
518 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
520 gtk_selection_convert (selection_widget,
521 gdk_drag_get_selection(context),
527 /*************************************************************
528 * gtk_drag_get_source_widget:
529 * Get the widget the was the source of this drag, if
530 * the drag originated from this application.
532 * context: The drag context for this drag
534 * The source widget, or NULL if the drag originated from
535 * a different application.
536 *************************************************************/
539 gtk_drag_get_source_widget (GdkDragContext *context)
543 tmp_list = source_widgets;
546 GtkWidget *ipc_widget = tmp_list->data;
548 if (ipc_widget->window == context->source_window)
550 GtkDragSourceInfo *info;
551 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
553 return info ? info->widget : NULL;
556 tmp_list = tmp_list->next;
562 /*************************************************************
564 * Notify the drag source that the transfer of data
567 * context: The drag context for this drag
568 * success: Was the data successfully transferred?
569 * time: The timestamp to use when notifying the destination.
571 *************************************************************/
574 gtk_drag_finish (GdkDragContext *context,
579 GdkAtom target = GDK_NONE;
581 g_return_if_fail (context != NULL);
585 target = gdk_atom_intern ("DELETE", FALSE);
587 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
589 target = gdk_atom_intern (success ?
590 "XmTRANSFER_SUCCESS" :
591 "XmTRANSFER_FAILURE",
595 if (target != GDK_NONE)
597 GtkWidget *selection_widget = gtk_drag_get_ipc_widget();
599 gdk_drag_context_ref (context);
601 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
602 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
603 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
606 gtk_selection_convert (selection_widget,
607 gdk_drag_get_selection(context),
613 gdk_drop_finish (context, success, time);
616 /*************************************************************
617 * gtk_drag_highlight:
618 * Highlight the given widget in the default manner.
622 *************************************************************/
625 gtk_drag_highlight (GtkWidget *widget)
629 g_return_if_fail (widget != NULL);
631 if (GTK_WIDGET_NO_WINDOW (widget))
633 x = widget->allocation.x;
634 y = widget->allocation.y;
642 gtk_draw_shadow (widget->style, widget->window,
643 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
645 widget->allocation.width,
646 widget->allocation.height);
648 gdk_draw_rectangle (widget->window,
649 widget->style->black_gc,
652 widget->allocation.width - 1,
653 widget->allocation.height - 1);
656 /*************************************************************
657 * gtk_drag_unhighlight:
658 * Refresh the given widget to remove the highlight.
662 *************************************************************/
665 gtk_drag_unhighlight (GtkWidget *widget)
669 g_return_if_fail (widget != NULL);
671 if (GTK_WIDGET_NO_WINDOW (widget))
673 x = widget->allocation.x;
674 y = widget->allocation.y;
682 gdk_window_clear_area_e (widget->window,
684 widget->allocation.width,
685 widget->allocation.height);
688 /*************************************************************
690 * Register a drop site, and possibly add default behaviors.
693 * flags: Which types of default drag behavior to use
694 * targets: Table of targets that can be accepted
695 * n_targets: Number of of entries in targets
698 *************************************************************/
701 gtk_drag_dest_set (GtkWidget *widget,
702 GtkDestDefaults flags,
703 const GtkTargetEntry *targets,
705 GdkDragAction actions)
707 GtkDragDestSite *site;
709 g_return_if_fail (widget != NULL);
711 /* HACK, do this in the destroy */
712 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
714 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
716 if (GTK_WIDGET_REALIZED (widget))
717 gtk_drag_dest_realized (widget);
719 gtk_signal_connect (GTK_OBJECT (widget), "realize",
720 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
722 site = g_new (GtkDragDestSite, 1);
725 site->have_drag = FALSE;
727 site->target_list = gtk_target_list_new (targets, n_targets);
729 site->target_list = NULL;
731 site->actions = actions;
732 site->do_proxy = FALSE;
734 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
735 site, gtk_drag_dest_site_destroy);
738 /*************************************************************
739 * gtk_drag_dest_set_proxy:
740 * Set up this widget to proxy drags elsewhere.
743 * proxy_window: window to which forward drag events
744 * protocol: Drag protocol which the dest widget accepts
745 * use_coordinates: If true, send the same coordinates to the
746 * destination, because it is a embedded
749 *************************************************************/
752 gtk_drag_dest_set_proxy (GtkWidget *widget,
753 GdkWindow *proxy_window,
754 GdkDragProtocol protocol,
755 gboolean use_coordinates)
757 GtkDragDestSite *site;
759 g_return_if_fail (widget != NULL);
761 /* HACK, do this in the destroy */
762 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
764 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
766 if (GTK_WIDGET_REALIZED (widget))
767 gtk_drag_dest_realized (widget);
769 gtk_signal_connect (GTK_OBJECT (widget), "realize",
770 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
772 site = g_new (GtkDragDestSite, 1);
775 site->have_drag = FALSE;
776 site->target_list = NULL;
778 site->proxy_window = proxy_window;
780 gdk_window_ref (proxy_window);
781 site->do_proxy = TRUE;
782 site->proxy_protocol = protocol;
783 site->proxy_coords = use_coordinates;
785 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
786 site, gtk_drag_dest_site_destroy);
789 /*************************************************************
790 * gtk_drag_dest_unset
791 * Unregister this widget as a drag target.
795 *************************************************************/
798 gtk_drag_dest_unset (GtkWidget *widget)
800 g_return_if_fail (widget != NULL);
802 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
805 /*************************************************************
806 * gtk_drag_dest_handle_event:
807 * Called from widget event handling code on Drag events
811 * toplevel: Toplevel widget that received the event
814 *************************************************************/
817 gtk_drag_dest_handle_event (GtkWidget *toplevel,
820 GtkDragDestInfo *info;
821 GdkDragContext *context;
823 g_return_if_fail (toplevel != NULL);
824 g_return_if_fail (event != NULL);
826 context = event->dnd.context;
828 info = g_dataset_get_data (context, "gtk-info");
831 info = g_new (GtkDragDestInfo, 1);
833 info->context = event->dnd.context;
834 info->proxy_source = NULL;
835 info->proxy_data = NULL;
836 info->dropped = FALSE;
837 info->proxy_drop_wait = FALSE;
838 g_dataset_set_data_full (context,
841 gtk_drag_dest_info_destroy);
844 /* Find the widget for the event */
853 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
858 case GDK_DRAG_MOTION:
861 GtkDragFindData data;
864 if (event->type == GDK_DROP_START)
865 info->dropped = TRUE;
867 gdk_window_get_origin (toplevel->window, &tx, &ty);
869 data.x = event->dnd.x_root - tx;
870 data.y = event->dnd.y_root - ty;
871 data.context = context;
874 data.toplevel = TRUE;
875 data.callback = (event->type == GDK_DRAG_MOTION) ?
876 gtk_drag_dest_motion : gtk_drag_dest_drop;
877 data.time = event->dnd.time;
879 gtk_drag_find_widget (toplevel, &data);
881 /* We send a leave here so that the widget unhighlights
885 ((event->type == GDK_DROP_START) || (!data.found)))
887 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
893 if (event->type == GDK_DRAG_MOTION)
896 gdk_drag_status (context, 0, event->dnd.time);
898 else if (event->type == GDK_DROP_START)
900 gdk_drop_reply (context, data.found, event->dnd.time);
901 if (context->protocol == GDK_DRAG_PROTO_MOTIF)
902 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
908 g_assert_not_reached();
912 /*************************************************************
913 * gtk_drag_dest_find_target:
914 * Decide on a target for the drag.
919 *************************************************************/
922 gtk_drag_dest_find_target (GtkWidget *widget,
923 GtkDragDestSite *site,
924 GdkDragContext *context)
927 GList *tmp_source = NULL;
928 GtkWidget *source_widget = gtk_drag_get_source_widget (context);
930 tmp_target = site->target_list->list;
933 GtkTargetPair *pair = tmp_target->data;
934 tmp_source = context->targets;
937 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
939 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
940 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
945 tmp_source = tmp_source->next;
947 tmp_target = tmp_target->next;
954 gtk_drag_selection_received (GtkWidget *widget,
955 GtkSelectionData *selection_data,
959 GdkDragContext *context;
960 GtkDragDestInfo *info;
961 GtkWidget *drop_widget;
965 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
966 info = g_dataset_get_data (context, "gtk-info");
968 if (info->proxy_data &&
969 info->proxy_data->target == selection_data->target)
971 gtk_selection_data_set (info->proxy_data,
972 selection_data->type,
973 selection_data->format,
974 selection_data->data,
975 selection_data->length);
980 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
982 gtk_drag_finish (context, TRUE, FALSE, time);
984 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
985 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
991 GtkDragDestSite *site;
993 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
995 if (site->target_list)
999 if (gtk_target_list_find (site->target_list,
1000 selection_data->target,
1003 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1004 selection_data->length >= 0)
1005 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1006 "drag_data_received",
1007 context, info->drop_x, info->drop_y,
1014 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1015 "drag_data_received",
1016 context, info->drop_x, info->drop_y,
1017 selection_data, 0, time);
1020 if (site->flags & GTK_DEST_DEFAULT_DROP)
1023 gtk_drag_finish (context,
1024 (selection_data->length >= 0),
1025 (context->action == GDK_ACTION_MOVE),
1029 gtk_widget_unref (drop_widget);
1032 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1033 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1036 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1037 gdk_drag_context_unref (context);
1039 gtk_drag_release_ipc_widget (widget);
1042 /*************************************************************
1043 * gtk_drag_find_widget:
1044 * Recursive callback used to locate widgets for
1045 * DRAG_MOTION and DROP_START events.
1049 *************************************************************/
1052 gtk_drag_find_widget (GtkWidget *widget,
1053 GtkDragFindData *data)
1055 GtkAllocation new_allocation;
1059 new_allocation = widget->allocation;
1061 if (data->found || !GTK_WIDGET_MAPPED (widget))
1064 if (!GTK_WIDGET_NO_WINDOW (widget))
1066 new_allocation.x = 0;
1067 new_allocation.y = 0;
1072 GdkWindow *window = widget->window;
1073 while (window != widget->parent->window)
1075 gint tx, ty, twidth, theight;
1076 gdk_window_get_size (window, &twidth, &theight);
1078 if (new_allocation.x < 0)
1080 new_allocation.width += new_allocation.x;
1081 new_allocation.x = 0;
1083 if (new_allocation.y < 0)
1085 new_allocation.height += new_allocation.y;
1086 new_allocation.y = 0;
1088 if (new_allocation.x + new_allocation.width > twidth)
1089 new_allocation.width = twidth - new_allocation.x;
1090 if (new_allocation.y + new_allocation.height > theight)
1091 new_allocation.height = theight - new_allocation.y;
1093 gdk_window_get_position (window, &tx, &ty);
1094 new_allocation.x += tx;
1096 new_allocation.y += ty;
1099 window = gdk_window_get_parent (window);
1103 if (data->toplevel ||
1104 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1105 (data->x < new_allocation.x + new_allocation.width) &&
1106 (data->y < new_allocation.y + new_allocation.height)))
1108 /* First, check if the drag is in a valid drop site in
1109 * one of our children
1111 if (GTK_IS_CONTAINER (widget))
1113 GtkDragFindData new_data = *data;
1115 new_data.x -= x_offset;
1116 new_data.y -= y_offset;
1117 new_data.found = FALSE;
1118 new_data.toplevel = FALSE;
1120 gtk_container_forall (GTK_CONTAINER (widget),
1121 (GtkCallback)gtk_drag_find_widget,
1124 data->found = new_data.found;
1127 /* If not, and this widget is registered as a drop site, check to
1128 * emit "drag_motion" to check if we are actually in
1132 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1134 data->found = data->callback (widget,
1136 data->x - new_allocation.x,
1137 data->y - new_allocation.y,
1139 /* If so, send a "drag_leave" to the last widget */
1142 if (data->info->widget && data->info->widget != widget)
1144 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1146 data->info->widget = widget;
1153 gtk_drag_proxy_begin (GtkWidget *widget, GtkDragDestInfo *dest_info)
1155 GtkDragSourceInfo *source_info;
1158 source_info = g_new0 (GtkDragSourceInfo, 1);
1159 source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1161 source_info->widget = widget;
1162 gtk_widget_ref (source_info->widget);
1163 source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1164 dest_info->context->targets,
1165 dest_info->context->actions);
1167 source_info->target_list = gtk_target_list_new (NULL, 0);
1168 tmp_list = dest_info->context->targets;
1171 gtk_target_list_add (source_info->target_list,
1172 GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1173 tmp_list = tmp_list->next;
1176 source_info->proxy_dest = dest_info;
1178 g_dataset_set_data (source_info->context, "gtk-info", source_info);
1180 gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget),
1182 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1185 dest_info->proxy_source = source_info;
1189 gtk_drag_dest_info_destroy (gpointer data)
1191 GtkDragDestInfo *info = data;
1197 gtk_drag_dest_realized (GtkWidget *widget)
1199 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1200 gdk_window_register_dnd (toplevel->window);
1204 gtk_drag_dest_site_destroy (gpointer data)
1206 GtkDragDestSite *site = data;
1208 if (site->target_list)
1209 gtk_target_list_unref (site->target_list);
1215 * Default drag handlers
1218 gtk_drag_dest_leave (GtkWidget *widget,
1219 GdkDragContext *context,
1222 GtkDragDestSite *site;
1224 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1225 g_return_if_fail (site != NULL);
1229 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1231 if (info->proxy_source && !info->dropped)
1232 gdk_drag_abort (info->proxy_source->context, time);
1238 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1239 gtk_drag_unhighlight (widget);
1241 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1242 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1245 site->have_drag = FALSE;
1250 gtk_drag_dest_motion (GtkWidget *widget,
1251 GdkDragContext *context,
1256 GtkDragDestSite *site;
1257 GdkDragAction action = 0;
1260 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1261 g_return_val_if_fail (site != NULL, FALSE);
1266 GdkEvent *current_event;
1267 GdkWindow *dest_window;
1268 GdkDragProtocol proto;
1270 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1272 if (!info->proxy_source)
1273 gtk_drag_proxy_begin (widget, info);
1275 current_event = gtk_get_current_event ();
1277 if (site->proxy_window)
1279 dest_window = site->proxy_window;
1280 proto = site->proxy_protocol;
1284 gdk_drag_find_window (info->proxy_source->context,
1286 current_event->dnd.x_root,
1287 current_event->dnd.y_root,
1288 &dest_window, &proto);
1291 gdk_drag_motion (info->proxy_source->context,
1293 current_event->dnd.x_root,
1294 current_event->dnd.y_root,
1295 context->suggested_action, time);
1297 selection = gdk_drag_get_selection (info->proxy_source->context);
1299 selection != gdk_drag_get_selection (info->context))
1300 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1302 gdk_event_free (current_event);
1307 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1309 if (context->suggested_action & site->actions)
1310 action = context->suggested_action;
1317 if ((site->actions & (1 << i)) &&
1318 (context->actions & (1 << i)))
1326 if (action && gtk_drag_dest_find_target (widget, site, context))
1328 if (!site->have_drag)
1330 site->have_drag = TRUE;
1331 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1332 gtk_drag_highlight (widget);
1335 gdk_drag_status (context, action, time);
1339 gdk_drag_status (context, 0, time);
1344 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1345 context, x, y, time, &retval);
1347 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1351 gtk_drag_dest_drop (GtkWidget *widget,
1352 GdkDragContext *context,
1357 GtkDragDestSite *site;
1358 GtkDragDestInfo *info;
1360 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1361 g_return_val_if_fail (site != NULL, FALSE);
1363 info = g_dataset_get_data (context, "gtk-info");
1364 g_return_val_if_fail (info != NULL, FALSE);
1371 if (info->proxy_source ||
1372 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1374 gtk_drag_drop (info->proxy_source, time);
1378 /* We need to synthesize a motion event, wait for a status,
1379 * and, if we get one a good one, do a drop.
1382 GdkEvent *current_event;
1384 GdkWindow *dest_window;
1385 GdkDragProtocol proto;
1387 gtk_drag_proxy_begin (widget, info);
1388 info->proxy_drop_wait = TRUE;
1389 info->proxy_drop_time = time;
1391 current_event = gtk_get_current_event ();
1393 if (site->proxy_window)
1395 dest_window = site->proxy_window;
1396 proto = site->proxy_protocol;
1400 gdk_drag_find_window (info->proxy_source->context,
1402 current_event->dnd.x_root,
1403 current_event->dnd.y_root,
1404 &dest_window, &proto);
1407 gdk_drag_motion (info->proxy_source->context,
1409 current_event->dnd.x_root,
1410 current_event->dnd.y_root,
1411 context->suggested_action, time);
1413 selection = gdk_drag_get_selection (info->proxy_source->context);
1415 selection != gdk_drag_get_selection (info->context))
1416 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1418 gdk_event_free (current_event);
1428 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1430 GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
1432 if (target == GDK_NONE)
1435 gtk_drag_get_data (widget, context, target, time);
1438 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1439 context, x, y, time, &retval);
1441 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1449 /*************************************************************
1450 * gtk_drag_begin: Start a drag operation
1453 * widget: Widget from which drag starts
1454 * handlers: List of handlers to supply the data for the drag
1455 * button: Button user used to start drag
1456 * time: Time of event starting drag
1459 *************************************************************/
1462 gtk_drag_begin (GtkWidget *widget,
1463 GtkTargetList *target_list,
1464 GdkDragAction actions,
1468 GtkDragSourceInfo *info;
1469 GList *targets = NULL;
1471 guint32 time = GDK_CURRENT_TIME;
1473 g_return_val_if_fail (widget != NULL, NULL);
1474 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1475 g_return_val_if_fail (target_list != NULL, NULL);
1478 time = gdk_event_get_time (event);
1480 info = g_new0 (GtkDragSourceInfo, 1);
1481 info->ipc_widget = gtk_drag_get_ipc_widget ();
1482 source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
1484 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1486 tmp_list = g_list_last (target_list->list);
1489 GtkTargetPair *pair = tmp_list->data;
1490 targets = g_list_prepend (targets,
1491 GINT_TO_POINTER (pair->target));
1492 tmp_list = tmp_list->prev;
1495 info->widget = widget;
1496 gtk_widget_ref (info->widget);
1498 info->context = gdk_drag_begin (info->ipc_widget->window,
1500 g_list_free (targets);
1502 g_dataset_set_data (info->context, "gtk-info", info);
1504 info->button = button;
1505 info->target_list = target_list;
1506 gtk_target_list_ref (target_list);
1508 info->cursor = NULL;
1509 info->status = GTK_DRAG_STATUS_DRAG;
1510 info->last_event = NULL;
1511 info->selections = NULL;
1512 info->icon_window = NULL;
1515 info->cursor = gtk_drag_get_cursor (
1516 gtk_drag_get_event_action (event, info->button, actions));
1518 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1521 /* We use a GTK grab here to override any grabs that the widget
1522 * we are dragging from might have held
1525 gtk_grab_add (info->ipc_widget);
1526 gdk_pointer_grab (info->ipc_widget->window, FALSE,
1527 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1528 GDK_BUTTON_RELEASE_MASK, NULL,
1529 info->cursor, time);
1531 if (event->type == GDK_MOTION_NOTIFY)
1532 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1536 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x, &y, NULL);
1541 if (info->icon_window)
1543 gdk_window_raise (info->icon_window->window);
1544 gtk_widget_set_uposition (info->icon_window, x - info->hot_x, y - info->hot_y);
1548 info->start_x = info->cur_x;
1549 info->start_y = info->cur_y;
1551 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1552 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1553 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1554 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1555 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1556 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1558 return info->context;
1561 /*************************************************************
1562 * gtk_drag_source_set:
1563 * Register a drop site, and possibly add default behaviors.
1566 * start_button_mask: Mask of allowed buttons to start drag
1567 * targets: Table of targets for this source
1569 * actions: Actions allowed for this source
1571 *************************************************************/
1574 gtk_drag_source_set (GtkWidget *widget,
1575 GdkModifierType start_button_mask,
1576 const GtkTargetEntry *targets,
1578 GdkDragAction actions)
1580 GtkDragSourceSite *site;
1582 g_return_if_fail (widget != NULL);
1584 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1586 gtk_widget_add_events (widget,
1587 gtk_widget_get_events (widget) |
1588 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1589 GDK_BUTTON_MOTION_MASK);
1593 if (site->target_list)
1594 gtk_target_list_unref (site->target_list);
1598 site = g_new0 (GtkDragSourceSite, 1);
1600 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1601 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1603 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1604 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1607 gtk_object_set_data_full (GTK_OBJECT (widget),
1609 site, gtk_drag_source_site_destroy);
1612 site->start_button_mask = start_button_mask;
1615 site->target_list = gtk_target_list_new (targets, n_targets);
1617 site->target_list = NULL;
1619 site->actions = actions;
1623 /*************************************************************
1624 * gtk_drag_source_unset
1625 * Unregister this widget as a drag source.
1629 *************************************************************/
1632 gtk_drag_source_unset (GtkWidget *widget)
1634 GtkDragSourceSite *site;
1636 g_return_if_fail (widget != NULL);
1638 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1642 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1643 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1647 /*************************************************************
1648 * gtk_drag_source_set_icon:
1649 * Set an icon for drags from this source.
1651 * colormap: Colormap for this icon
1655 *************************************************************/
1658 gtk_drag_source_set_icon (GtkWidget *widget,
1659 GdkColormap *colormap,
1663 GtkDragSourceSite *site;
1665 g_return_if_fail (widget != NULL);
1667 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1668 g_return_if_fail (site != NULL);
1671 gdk_colormap_unref (site->colormap);
1673 gdk_pixmap_unref (site->pixmap);
1675 gdk_pixmap_unref (site->mask);
1677 site->colormap = colormap;
1679 gdk_colormap_ref (colormap);
1681 site->pixmap = pixmap;
1683 gdk_pixmap_ref (pixmap);
1687 gdk_pixmap_ref (mask);
1690 /*************************************************************
1691 * gtk_drag_set_icon_widget:
1692 * Set a widget as the icon for a drag.
1699 *************************************************************/
1702 gtk_drag_set_icon_widget (GdkDragContext *context,
1707 GtkDragSourceInfo *info;
1709 g_return_if_fail (context != NULL);
1710 g_return_if_fail (widget != NULL);
1712 info = g_dataset_get_data (context, "gtk-info");
1713 gtk_drag_remove_icon (info);
1715 info->icon_window = widget;
1718 gtk_widget_set_uposition (widget, info->cur_x, info->cur_y);
1719 gtk_widget_ref (widget);
1720 gdk_window_raise (widget->window);
1721 gtk_widget_show (widget);
1724 info->hot_x = hot_x;
1725 info->hot_y = hot_y;
1728 /*************************************************************
1729 * gtk_drag_set_icon_pixmap:
1730 * Set a widget as the icon for a drag.
1733 * colormap: Colormap for the icon window.
1739 *************************************************************/
1742 gtk_drag_set_icon_pixmap (GdkDragContext *context,
1743 GdkColormap *colormap,
1752 g_return_if_fail (context != NULL);
1753 g_return_if_fail (colormap != NULL);
1754 g_return_if_fail (pixmap != NULL);
1756 gdk_window_get_size (pixmap, &width, &height);
1758 gtk_widget_push_visual (gdk_colormap_get_visual(colormap));
1759 gtk_widget_push_colormap (colormap);
1761 window = gtk_window_new (GTK_WINDOW_POPUP);
1762 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1763 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
1765 gtk_widget_pop_visual ();
1766 gtk_widget_pop_colormap ();
1768 gtk_widget_set_usize (window, width, height);
1769 gtk_widget_realize (window);
1771 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1774 gtk_widget_shape_combine_mask (window, mask, 0, 0);
1776 gtk_drag_set_icon_widget (context, window, hot_x, hot_y);
1779 /*************************************************************
1780 * gtk_drag_set_icon_default:
1781 * Set the icon for a drag to the default icon.
1785 *************************************************************/
1788 gtk_drag_set_icon_default (GdkDragContext *context)
1790 g_return_if_fail (context != NULL);
1792 if (!default_icon_pixmap)
1794 default_icon_colormap = gdk_colormap_get_system ();
1795 default_icon_pixmap =
1796 gdk_pixmap_colormap_create_from_xpm_d (NULL,
1797 default_icon_colormap,
1799 NULL, drag_default_xpm);
1800 default_icon_hot_x = -2;
1801 default_icon_hot_y = -2;
1804 gtk_drag_set_icon_pixmap (context,
1805 default_icon_colormap,
1806 default_icon_pixmap,
1809 default_icon_hot_y);
1812 /*************************************************************
1813 * gtk_drag_set_default_icon:
1814 * Set a default icon for all drags as a pixmap.
1816 * colormap: Colormap for the icon window.
1822 *************************************************************/
1825 gtk_drag_set_default_icon (GdkColormap *colormap,
1831 g_return_if_fail (colormap != NULL);
1832 g_return_if_fail (pixmap != NULL);
1834 if (default_icon_colormap)
1835 gdk_colormap_unref (default_icon_colormap);
1836 if (default_icon_pixmap)
1837 gdk_pixmap_unref (default_icon_pixmap);
1838 if (default_icon_mask)
1839 gdk_pixmap_unref (default_icon_pixmap);
1841 default_icon_colormap = colormap;
1842 gdk_colormap_ref (colormap);
1844 default_icon_pixmap = pixmap;
1845 gdk_pixmap_ref (pixmap);
1847 default_icon_mask = mask;
1849 gdk_pixmap_ref (mask);
1851 default_icon_hot_x = hot_x;
1852 default_icon_hot_y = hot_y;
1856 /*************************************************************
1857 * gtk_drag_source_handle_event:
1858 * Called from widget event handling code on Drag events
1862 * toplevel: Toplevel widget that received the event
1865 *************************************************************/
1868 gtk_drag_source_handle_event (GtkWidget *widget,
1871 GtkDragSourceInfo *info;
1872 GdkDragContext *context;
1874 g_return_if_fail (widget != NULL);
1875 g_return_if_fail (event != NULL);
1877 context = event->dnd.context;
1878 info = g_dataset_get_data (context, "gtk-info");
1882 switch (event->type)
1884 case GDK_DRAG_STATUS:
1888 if (info->proxy_dest)
1890 if (!event->dnd.send_event)
1892 if (info->proxy_dest->proxy_drop_wait)
1894 /* Aha - we can finally pass the MOTIF DROP on... */
1895 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
1899 gdk_drag_status (info->proxy_dest->context,
1900 event->dnd.context->action,
1907 cursor = gtk_drag_get_cursor (event->dnd.context->action);
1908 if (info->cursor != cursor)
1910 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window),
1911 PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
1912 ((GdkCursorPrivate *)cursor)->xcursor,
1914 info->cursor = cursor;
1917 if (info->last_event)
1919 gtk_drag_motion_cb (info->widget,
1920 (GdkEventMotion *)info->last_event,
1922 info->last_event = NULL;
1928 case GDK_DROP_FINISHED:
1929 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
1932 g_assert_not_reached();
1936 /*************************************************************
1937 * gtk_drag_source_check_selection:
1938 * Check if we've set up handlers/claimed the selection
1939 * for a given drag. If not, add them.
1943 *************************************************************/
1946 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
1952 tmp_list = info->selections;
1955 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
1957 tmp_list = tmp_list->next;
1960 gtk_selection_owner_set (info->ipc_widget, selection, time);
1961 info->selections = g_list_prepend (info->selections,
1962 GUINT_TO_POINTER (selection));
1964 tmp_list = info->target_list->list;
1967 GtkTargetPair *pair = tmp_list->data;
1969 gtk_selection_add_target (info->ipc_widget,
1973 tmp_list = tmp_list->next;
1976 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
1978 gtk_selection_add_target (info->ipc_widget,
1980 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
1981 TARGET_MOTIF_SUCCESS);
1982 gtk_selection_add_target (info->ipc_widget,
1984 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
1985 TARGET_MOTIF_FAILURE);
1988 gtk_selection_add_target (info->ipc_widget,
1990 gdk_atom_intern ("DELETE", FALSE),
1994 /*************************************************************
1995 * gtk_drag_drop_finished:
1996 * Clean up from the drag, and display snapback, if necessary.
2002 *************************************************************/
2005 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2009 gtk_drag_source_release_selections (info, time);
2011 if (info->proxy_dest)
2013 /* The time from the event isn't reliable for Xdnd drags */
2014 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2015 info->proxy_dest->proxy_drop_time);
2016 gtk_drag_source_info_destroy (info);
2022 gtk_drag_source_info_destroy (info);
2026 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2030 anim->n_steps = MAX (info->cur_x - info->start_x,
2031 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2032 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2033 if (info->icon_window)
2035 gtk_widget_show(info->icon_window);
2036 gdk_window_raise (info->icon_window->window);
2039 /* Mark the context as dead, so if the destination decides
2040 * to respond really late, we still are OK.
2042 g_dataset_set_data (info->context, "gtk-info", NULL);
2043 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2049 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2052 GList *tmp_list = info->selections;
2055 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2056 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2057 gtk_selection_owner_set (NULL, selection, time);
2058 tmp_list = tmp_list->next;
2061 g_list_free (info->selections);
2062 info->selections = NULL;
2065 /*************************************************************
2067 * Send a drop event.
2071 *************************************************************/
2074 gtk_drag_drop (GtkDragSourceInfo *info, guint32 time)
2076 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2078 GtkSelectionData selection_data;
2080 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2082 tmp_list = info->target_list->list;
2085 GtkTargetPair *pair = tmp_list->data;
2087 if (pair->target == target)
2089 selection_data.selection = GDK_NONE;
2090 selection_data.target = target;
2091 selection_data.data = NULL;
2092 selection_data.length = -1;
2094 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2095 info->context, &selection_data,
2099 /* FIXME: Should we check for length >= 0 here? */
2100 gtk_drag_drop_finished (info, TRUE, time);
2103 tmp_list = tmp_list->next;
2105 gtk_drag_drop_finished (info, FALSE, time);
2109 if (info->icon_window)
2110 gtk_widget_hide (info->icon_window);
2112 gdk_drag_drop (info->context, time);
2113 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2114 gtk_drag_abort_timeout,
2120 * Source side callbacks.
2124 gtk_drag_source_event_cb (GtkWidget *widget,
2128 GtkDragSourceSite *site;
2129 site = (GtkDragSourceSite *)data;
2131 switch (event->type)
2133 case GDK_BUTTON_PRESS:
2134 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2136 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2137 site->x = event->button.x;
2138 site->y = event->button.y;
2142 case GDK_BUTTON_RELEASE:
2143 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2145 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2149 case GDK_MOTION_NOTIFY:
2150 if (site->state & event->motion.state & site->start_button_mask)
2152 /* FIXME: This is really broken and can leave us
2158 if (site->state & event->motion.state &
2159 GDK_BUTTON1_MASK << (i - 1))
2163 if (MAX (abs(site->x - event->motion.x),
2164 abs(site->y - event->motion.y)) > 3)
2166 GtkDragSourceInfo *info;
2167 GdkDragContext *context;
2170 context = gtk_drag_begin (widget, site->target_list,
2175 info = g_dataset_get_data (context, "gtk-info");
2177 if (!info->icon_window)
2180 gtk_drag_set_icon_pixmap (context,
2183 site->mask, -2, -2);
2185 gtk_drag_set_icon_default (context);
2193 default: /* hit for 2/3BUTTON_PRESS */
2200 gtk_drag_source_site_destroy (gpointer data)
2202 GtkDragSourceSite *site = data;
2204 if (site->target_list)
2205 gtk_target_list_unref (site->target_list);
2208 gdk_pixmap_unref (site->pixmap);
2211 gdk_pixmap_unref (site->mask);
2217 gtk_drag_selection_get (GtkWidget *widget,
2218 GtkSelectionData *selection_data,
2223 GtkDragSourceInfo *info = data;
2224 static GdkAtom null_atom = GDK_NONE;
2228 null_atom = gdk_atom_intern ("NULL", FALSE);
2233 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2236 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2238 case TARGET_MOTIF_SUCCESS:
2239 gtk_drag_drop_finished (info, TRUE, time);
2240 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2242 case TARGET_MOTIF_FAILURE:
2243 gtk_drag_drop_finished (info, FALSE, time);
2244 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2247 if (info->proxy_dest)
2249 /* This is sort of dangerous and needs to be thought
2252 info->proxy_dest->proxy_data = selection_data;
2253 gtk_drag_get_data (info->widget,
2254 info->proxy_dest->context,
2255 selection_data->target,
2258 info->proxy_dest->proxy_data = NULL;
2262 if (gtk_target_list_find (info->target_list,
2263 selection_data->target,
2266 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2278 gtk_drag_anim_timeout (gpointer data)
2280 GtkDragAnim *anim = data;
2284 GDK_THREADS_ENTER ();
2286 if (anim->step == anim->n_steps)
2288 gtk_drag_source_info_destroy (anim->info);
2295 x = (anim->info->start_x * (anim->step + 1) +
2296 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2297 y = (anim->info->start_y * (anim->step + 1) +
2298 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2299 if (anim->info->icon_window)
2300 gtk_widget_set_uposition (anim->info->icon_window, x, y);
2307 GDK_THREADS_LEAVE ();
2313 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2315 if (info->icon_window)
2317 gtk_widget_hide (info->icon_window);
2318 gtk_widget_unref (info->icon_window);
2320 info->icon_window = NULL;
2325 gtk_drag_source_info_destroy (gpointer data)
2327 GtkDragSourceInfo *info = data;
2329 gtk_drag_remove_icon (data);
2331 if (!info->proxy_dest)
2332 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2336 gtk_widget_unref (info->widget);
2338 gtk_selection_remove_all (info->ipc_widget);
2339 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2340 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2341 gtk_drag_release_ipc_widget (info->ipc_widget);
2343 gtk_target_list_unref (info->target_list);
2345 g_dataset_set_data (info->context, "gtk-info", NULL);
2346 gdk_drag_context_unref (info->context);
2348 if (info->drop_timeout)
2349 gtk_timeout_remove (info->drop_timeout);
2354 /*************************************************************
2355 * gtk_drag_motion_cb:
2356 * "motion_notify_event" callback during drag.
2360 *************************************************************/
2363 gtk_drag_motion_cb (GtkWidget *widget,
2364 GdkEventMotion *event,
2367 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2369 GdkDragAction action;
2370 GdkWindow *window = NULL;
2371 GdkWindow *dest_window;
2372 GdkDragProtocol protocol;
2373 gint x_root, y_root;
2377 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2378 event->x_root = x_root;
2379 event->y_root = y_root;
2382 action = gtk_drag_get_event_action ((GdkEvent *)event,
2384 info->context->actions);
2386 info->cur_x = event->x_root - info->hot_x;
2387 info->cur_y = event->y_root - info->hot_y;
2389 if (info->icon_window)
2391 gdk_window_raise (info->icon_window->window);
2392 gtk_widget_set_uposition (info->icon_window, info->cur_x, info->cur_y);
2393 window = info->icon_window->window;
2396 gdk_drag_find_window (info->context,
2397 window, event->x_root, event->y_root,
2398 &dest_window, &protocol);
2400 if (gdk_drag_motion (info->context, dest_window, protocol,
2401 event->x_root, event->y_root, action,
2404 if (info->last_event)
2405 gdk_event_free ((GdkEvent *)info->last_event);
2407 info->last_event = gdk_event_copy ((GdkEvent *)event);
2411 gdk_window_unref (dest_window);
2413 selection = gdk_drag_get_selection (info->context);
2415 gtk_drag_source_check_selection (info, selection, event->time);
2418 /* We ignore the response, so we can respond precisely to the drop
2421 gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2427 /*************************************************************
2428 * gtk_drag_motion_cb:
2429 * "button_release_event" callback during drag.
2433 *************************************************************/
2436 gtk_drag_button_release_cb (GtkWidget *widget,
2437 GdkEventButton *event,
2440 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2441 GtkWidget *source_widget = info->widget;
2442 GdkEvent send_event;
2444 gtk_widget_ref (source_widget);
2446 if (event->button != info->button)
2449 gdk_pointer_ungrab (event->time);
2451 if ((info->context->action != 0) && (info->context->dest_window != NULL))
2453 gtk_drag_drop (info, event->time);
2457 gdk_drag_abort (info->context, event->time);
2458 gtk_drag_drop_finished (info, FALSE, event->time);
2461 gtk_grab_remove (widget);
2462 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), info);
2464 /* Send on a release pair to the the original
2465 * widget to convince it to release its grab. We need to
2466 * call gtk_propagate_event() here, instead of
2467 * gtk_widget_event() because widget like GtkList may
2468 * expect propagation.
2471 send_event.button.type = GDK_BUTTON_RELEASE;
2472 send_event.button.window = source_widget->window;
2473 send_event.button.send_event = TRUE;
2474 send_event.button.time = event->time;
2475 send_event.button.x = 0;
2476 send_event.button.y = 0;
2477 send_event.button.pressure = 0.;
2478 send_event.button.xtilt = 0.;
2479 send_event.button.ytilt = 0.;
2480 send_event.button.state = event->state;
2481 send_event.button.button = event->button;
2482 send_event.button.source = GDK_SOURCE_PEN;
2483 send_event.button.deviceid = GDK_CORE_POINTER;
2484 send_event.button.x_root = 0;
2485 send_event.button.y_root = 0;
2487 gtk_propagate_event (source_widget, &send_event);
2489 gtk_widget_unref (source_widget);
2495 gtk_drag_abort_timeout (gpointer data)
2497 GtkDragSourceInfo *info = data;
2498 guint32 time = GDK_CURRENT_TIME;
2500 if (info->proxy_dest)
2501 time = info->proxy_dest->proxy_drop_time;
2503 info->drop_timeout = 0;
2504 gtk_drag_drop_finished (info, FALSE, time);