1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 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 "gtkdrawwindow.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 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_position (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)
899 gdk_drop_reply (context, data.found, event->dnd.time);
904 g_assert_not_reached();
908 /*************************************************************
909 * gtk_drag_dest_find_target:
910 * Decide on a target for the drag.
915 *************************************************************/
918 gtk_drag_dest_find_target (GtkWidget *widget,
919 GtkDragDestSite *site,
920 GdkDragContext *context)
923 GList *tmp_source = NULL;
924 GtkWidget *source_widget = gtk_drag_get_source_widget (context);
926 tmp_target = site->target_list->list;
929 GtkTargetPair *pair = tmp_target->data;
930 tmp_source = context->targets;
933 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
935 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
936 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
941 tmp_source = tmp_source->next;
943 tmp_target = tmp_target->next;
950 gtk_drag_selection_received (GtkWidget *widget,
951 GtkSelectionData *selection_data,
955 GdkDragContext *context;
956 GtkDragDestInfo *info;
957 GtkWidget *drop_widget;
961 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
962 info = g_dataset_get_data (context, "gtk-info");
964 if (info->proxy_data &&
965 info->proxy_data->target == selection_data->target)
967 gtk_selection_data_set (info->proxy_data,
968 selection_data->type,
969 selection_data->format,
970 selection_data->data,
971 selection_data->length);
976 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
978 gtk_drag_finish (context, TRUE, FALSE, time);
980 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
981 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
987 GtkDragDestSite *site;
989 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
991 if (site->target_list)
995 if (gtk_target_list_find (site->target_list,
996 selection_data->target,
999 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1000 selection_data->length >= 0)
1001 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1002 "drag_data_received",
1003 context, info->drop_x, info->drop_y,
1010 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1011 "drag_data_received",
1012 context, info->drop_x, info->drop_y,
1013 selection_data, 0, time);
1016 if (site->flags & GTK_DEST_DEFAULT_DROP)
1019 gtk_drag_finish (context,
1020 (selection_data->length >= 0),
1021 (context->action == GDK_ACTION_MOVE),
1025 gtk_widget_unref (drop_widget);
1028 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1029 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1032 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1033 gdk_drag_context_unref (context);
1035 gtk_drag_release_ipc_widget (widget);
1038 /*************************************************************
1039 * gtk_drag_find_widget:
1040 * Recursive callback used to locate widgets for
1041 * DRAG_MOTION and DROP_START events.
1045 *************************************************************/
1048 gtk_drag_find_widget (GtkWidget *widget,
1049 GtkDragFindData *data)
1051 GtkAllocation new_allocation;
1055 new_allocation = widget->allocation;
1057 if (!GTK_WIDGET_VISIBLE (widget))
1060 if (!GTK_WIDGET_NO_WINDOW (widget))
1062 new_allocation.x = 0;
1063 new_allocation.y = 0;
1068 GdkWindow *window = widget->window;
1069 while (window != widget->parent->window)
1071 gint tx, ty, twidth, theight;
1072 gdk_window_get_size (window, &twidth, &theight);
1074 if (new_allocation.x < 0)
1076 new_allocation.width += new_allocation.x;
1077 new_allocation.x = 0;
1079 if (new_allocation.y < 0)
1081 new_allocation.height += new_allocation.y;
1082 new_allocation.y = 0;
1084 if (new_allocation.x + new_allocation.width > twidth)
1085 new_allocation.width = twidth - new_allocation.x;
1086 if (new_allocation.y + new_allocation.height > theight)
1087 new_allocation.height = theight - new_allocation.y;
1089 gdk_window_get_position (window, &tx, &ty);
1090 new_allocation.x += tx;
1092 new_allocation.y += ty;
1095 window = gdk_window_get_parent (window);
1099 if (data->toplevel ||
1100 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1101 (data->x < new_allocation.x + new_allocation.width) &&
1102 (data->y < new_allocation.y + new_allocation.height)))
1104 /* First, check if the drag is in a valid drop site in
1105 * one of our children
1107 if (GTK_IS_CONTAINER (widget))
1109 GtkDragFindData new_data = *data;
1111 new_data.x -= x_offset;
1112 new_data.y -= y_offset;
1113 new_data.found = FALSE;
1114 new_data.toplevel = FALSE;
1116 gtk_container_foreach (GTK_CONTAINER (widget),
1117 (GtkCallback)gtk_drag_find_widget,
1120 data->found = new_data.found;
1123 /* If not, and this widget is registered as a drop site, check to
1124 * emit "drag_motion" to check if we are actually in
1128 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1130 data->found = data->callback (widget,
1132 data->x - new_allocation.x,
1133 data->y - new_allocation.y,
1135 /* If so, send a "drag_leave" to the last widget */
1138 if (data->info->widget && data->info->widget != widget)
1140 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1142 data->info->widget = widget;
1149 gtk_drag_proxy_begin (GtkWidget *widget, GtkDragDestInfo *dest_info)
1151 GtkDragSourceInfo *source_info;
1154 source_info = g_new0 (GtkDragSourceInfo, 1);
1155 source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1157 source_info->widget = widget;
1158 gtk_widget_ref (source_info->widget);
1159 source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1160 dest_info->context->targets,
1161 dest_info->context->actions);
1163 source_info->target_list = gtk_target_list_new (NULL, 0);
1164 tmp_list = dest_info->context->targets;
1167 gtk_target_list_add (source_info->target_list,
1168 GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1169 tmp_list = tmp_list->next;
1172 source_info->proxy_dest = dest_info;
1174 g_dataset_set_data (source_info->context, "gtk-info", source_info);
1176 gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget),
1178 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1181 dest_info->proxy_source = source_info;
1185 gtk_drag_dest_info_destroy (gpointer data)
1187 GtkDragDestInfo *info = data;
1193 gtk_drag_dest_realized (GtkWidget *widget)
1195 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1196 gdk_window_register_dnd (toplevel->window);
1200 gtk_drag_dest_site_destroy (gpointer data)
1202 GtkDragDestSite *site = data;
1204 if (site->target_list)
1205 gtk_target_list_unref (site->target_list);
1211 * Default drag handlers
1214 gtk_drag_dest_leave (GtkWidget *widget,
1215 GdkDragContext *context,
1218 GtkDragDestSite *site;
1220 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1221 g_return_if_fail (site != NULL);
1225 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1227 if (info->proxy_source && !info->dropped)
1228 gdk_drag_abort (info->proxy_source->context, time);
1234 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1235 gtk_drag_unhighlight (widget);
1237 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1238 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1241 site->have_drag = FALSE;
1246 gtk_drag_dest_motion (GtkWidget *widget,
1247 GdkDragContext *context,
1252 GtkDragDestSite *site;
1253 GdkDragAction action = 0;
1256 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1257 g_return_val_if_fail (site != NULL, FALSE);
1262 GdkEvent *current_event;
1263 GdkWindow *dest_window;
1264 GdkDragProtocol proto;
1266 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1268 if (!info->proxy_source)
1269 gtk_drag_proxy_begin (widget, info);
1271 current_event = gtk_get_current_event ();
1273 if (site->proxy_window)
1275 dest_window = site->proxy_window;
1276 proto = site->proxy_protocol;
1280 gdk_drag_find_window (info->proxy_source->context,
1282 current_event->dnd.x_root,
1283 current_event->dnd.y_root,
1284 &dest_window, &proto);
1287 gdk_drag_motion (info->proxy_source->context,
1289 current_event->dnd.x_root,
1290 current_event->dnd.y_root,
1291 context->suggested_action, time);
1293 selection = gdk_drag_get_selection (info->proxy_source->context);
1295 selection != gdk_drag_get_selection (info->context))
1296 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1298 gdk_event_free (current_event);
1303 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1305 if (context->suggested_action & site->actions)
1306 action = context->suggested_action;
1313 if ((site->actions & (1 << i)) &&
1314 (context->actions & (1 << i)))
1322 if (action && gtk_drag_dest_find_target (widget, site, context))
1324 if (!site->have_drag)
1326 site->have_drag = TRUE;
1327 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1328 gtk_drag_highlight (widget);
1331 gdk_drag_status (context, action, time);
1335 gdk_drag_status (context, 0, time);
1340 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1341 context, x, y, time, &retval);
1343 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1347 gtk_drag_dest_drop (GtkWidget *widget,
1348 GdkDragContext *context,
1353 GtkDragDestSite *site;
1354 GtkDragDestInfo *info;
1356 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1357 g_return_val_if_fail (site != NULL, FALSE);
1359 info = g_dataset_get_data (context, "gtk-info");
1360 g_return_val_if_fail (info != NULL, FALSE);
1367 if (info->proxy_source ||
1368 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1370 gtk_drag_drop (info->proxy_source, time);
1374 /* We need to synthesize a motion event, wait for a status,
1375 * and, if we get one a good one, do a drop.
1378 GdkEvent *current_event;
1380 GdkWindow *dest_window;
1381 GdkDragProtocol proto;
1383 gtk_drag_proxy_begin (widget, info);
1384 info->proxy_drop_wait = TRUE;
1385 info->proxy_drop_time = time;
1387 current_event = gtk_get_current_event ();
1389 if (site->proxy_window)
1391 dest_window = site->proxy_window;
1392 proto = site->proxy_protocol;
1396 gdk_drag_find_window (info->proxy_source->context,
1398 current_event->dnd.x_root,
1399 current_event->dnd.y_root,
1400 &dest_window, &proto);
1403 gdk_drag_motion (info->proxy_source->context,
1405 current_event->dnd.x_root,
1406 current_event->dnd.y_root,
1407 context->suggested_action, time);
1409 selection = gdk_drag_get_selection (info->proxy_source->context);
1411 selection != gdk_drag_get_selection (info->context))
1412 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1414 gdk_event_free (current_event);
1424 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1426 GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
1428 if (target == GDK_NONE)
1431 gtk_drag_get_data (widget, context, target, time);
1434 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1435 context, x, y, time, &retval);
1437 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1445 /*************************************************************
1446 * gtk_drag_begin: Start a drag operation
1449 * widget: Widget from which drag starts
1450 * handlers: List of handlers to supply the data for the drag
1451 * button: Button user used to start drag
1452 * time: Time of event starting drag
1455 *************************************************************/
1458 gtk_drag_begin (GtkWidget *widget,
1459 GtkTargetList *target_list,
1460 GdkDragAction actions,
1464 GtkDragSourceInfo *info;
1465 GList *targets = NULL;
1467 guint32 time = GDK_CURRENT_TIME;
1469 g_return_val_if_fail (widget != NULL, NULL);
1470 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1471 g_return_val_if_fail (target_list != NULL, NULL);
1474 time = gdk_event_get_time (event);
1476 info = g_new0 (GtkDragSourceInfo, 1);
1477 info->ipc_widget = gtk_drag_get_ipc_widget ();
1478 source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
1480 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1482 tmp_list = g_list_last (target_list->list);
1485 GtkTargetPair *pair = tmp_list->data;
1486 targets = g_list_prepend (targets,
1487 GINT_TO_POINTER (pair->target));
1488 tmp_list = tmp_list->prev;
1491 info->widget = widget;
1492 gtk_widget_ref (info->widget);
1494 info->context = gdk_drag_begin (info->ipc_widget->window,
1496 g_list_free (targets);
1498 g_dataset_set_data (info->context, "gtk-info", info);
1500 info->button = button;
1501 info->target_list = target_list;
1502 gtk_target_list_ref (target_list);
1504 info->cursor = NULL;
1505 info->status = GTK_DRAG_STATUS_DRAG;
1506 info->last_event = NULL;
1507 info->selections = NULL;
1508 info->icon_window = NULL;
1511 info->cursor = gtk_drag_get_cursor (
1512 gtk_drag_get_event_action (event, info->button, actions));
1514 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1517 /* We use a GTK grab here to override any grabs that the widget
1518 * we are dragging from might have held
1521 gtk_grab_add (info->ipc_widget);
1522 gdk_pointer_grab (info->ipc_widget->window, FALSE,
1523 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1524 GDK_BUTTON_RELEASE_MASK, NULL,
1525 info->cursor, time);
1527 if (event->type == GDK_MOTION_NOTIFY)
1528 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1532 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x, &y, NULL);
1537 if (info->icon_window)
1539 gdk_window_raise (info->icon_window->window);
1540 gtk_widget_set_uposition (info->icon_window, x - info->hot_x, y - info->hot_y);
1544 info->start_x = info->cur_x;
1545 info->start_y = info->cur_y;
1547 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1548 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1549 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1550 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1551 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1552 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1554 return info->context;
1557 /*************************************************************
1558 * gtk_drag_source_set:
1559 * Register a drop site, and possibly add default behaviors.
1562 * start_button_mask: Mask of allowed buttons to start drag
1563 * targets: Table of targets for this source
1565 * actions: Actions allowed for this source
1567 *************************************************************/
1570 gtk_drag_source_set (GtkWidget *widget,
1571 GdkModifierType start_button_mask,
1572 GtkTargetEntry *targets,
1574 GdkDragAction actions)
1576 GtkDragSourceSite *site;
1578 g_return_if_fail (widget != NULL);
1580 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1582 gtk_widget_add_events (widget,
1583 gtk_widget_get_events (widget) |
1584 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1585 GDK_BUTTON_MOTION_MASK);
1589 if (site->target_list)
1590 gtk_target_list_unref (site->target_list);
1594 site = g_new0 (GtkDragSourceSite, 1);
1596 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1597 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1599 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1600 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1603 gtk_object_set_data_full (GTK_OBJECT (widget),
1605 site, gtk_drag_source_site_destroy);
1608 site->start_button_mask = start_button_mask;
1611 site->target_list = gtk_target_list_new (targets, n_targets);
1613 site->target_list = NULL;
1615 site->actions = actions;
1619 /*************************************************************
1620 * gtk_drag_source_set_icon:
1621 * Set an icon for drags from this source.
1623 * colormap: Colormap for this icon
1627 *************************************************************/
1630 gtk_drag_source_set_icon (GtkWidget *widget,
1631 GdkColormap *colormap,
1635 GtkDragSourceSite *site;
1637 g_return_if_fail (widget != NULL);
1639 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1640 g_return_if_fail (site != NULL);
1643 gdk_colormap_unref (site->colormap);
1645 gdk_pixmap_unref (site->pixmap);
1647 gdk_pixmap_unref (site->mask);
1649 site->colormap = colormap;
1651 gdk_colormap_ref (colormap);
1653 site->pixmap = pixmap;
1655 gdk_pixmap_ref (pixmap);
1659 gdk_pixmap_ref (mask);
1662 /*************************************************************
1663 * gtk_drag_set_icon_widget:
1664 * Set a widget as the icon for a drag.
1671 *************************************************************/
1674 gtk_drag_set_icon_widget (GdkDragContext *context,
1679 GtkDragSourceInfo *info;
1681 g_return_if_fail (context != NULL);
1682 g_return_if_fail (widget != NULL);
1684 info = g_dataset_get_data (context, "gtk-info");
1685 gtk_drag_remove_icon (info);
1687 info->icon_window = widget;
1690 gtk_widget_set_uposition (widget, info->cur_x, info->cur_y);
1691 gtk_widget_ref (widget);
1692 gdk_window_raise (widget->window);
1693 gtk_widget_show (widget);
1696 info->hot_x = hot_x;
1697 info->hot_y = hot_y;
1700 /*************************************************************
1701 * gtk_drag_set_icon_pixmap:
1702 * Set a widget as the icon for a drag.
1705 * colormap: Colormap for the icon window.
1711 *************************************************************/
1714 gtk_drag_set_icon_pixmap (GdkDragContext *context,
1715 GdkColormap *colormap,
1724 g_return_if_fail (context != NULL);
1725 g_return_if_fail (colormap != NULL);
1726 g_return_if_fail (pixmap != NULL);
1728 gdk_window_get_size (pixmap, &width, &height);
1730 gtk_widget_push_visual (gdk_colormap_get_visual(colormap));
1731 gtk_widget_push_colormap (colormap);
1733 window = gtk_draw_window_new (GTK_WINDOW_POPUP);
1735 gtk_widget_pop_visual ();
1736 gtk_widget_pop_colormap ();
1738 gtk_widget_set_usize (window, width, height);
1739 gtk_widget_realize (window);
1741 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1744 gtk_widget_shape_combine_mask (window, mask, 0, 0);
1746 gtk_drag_set_icon_widget (context, window, hot_x, hot_y);
1749 /*************************************************************
1750 * gtk_drag_set_icon_default:
1751 * Set the icon for a drag to the default icon.
1755 *************************************************************/
1758 gtk_drag_set_icon_default (GdkDragContext *context)
1760 g_return_if_fail (context != NULL);
1762 if (!default_icon_pixmap)
1764 default_icon_colormap = gdk_colormap_get_system ();
1765 default_icon_pixmap =
1766 gdk_pixmap_colormap_create_from_xpm_d (NULL,
1767 default_icon_colormap,
1769 NULL, drag_default_xpm);
1770 default_icon_hot_x = -2;
1771 default_icon_hot_y = -2;
1774 gtk_drag_set_icon_pixmap (context,
1775 default_icon_colormap,
1776 default_icon_pixmap,
1779 default_icon_hot_y);
1782 /*************************************************************
1783 * gtk_drag_set_default_icon:
1784 * Set a default icon for all drags as a pixmap.
1786 * colormap: Colormap for the icon window.
1792 *************************************************************/
1795 gtk_drag_set_default_icon (GdkColormap *colormap,
1801 g_return_if_fail (colormap != NULL);
1802 g_return_if_fail (pixmap != NULL);
1804 if (default_icon_colormap)
1805 gdk_colormap_unref (default_icon_colormap);
1806 if (default_icon_pixmap)
1807 gdk_pixmap_unref (default_icon_pixmap);
1808 if (default_icon_mask)
1809 gdk_pixmap_unref (default_icon_pixmap);
1811 default_icon_colormap = colormap;
1812 gdk_colormap_ref (colormap);
1814 default_icon_pixmap = pixmap;
1815 gdk_pixmap_ref (pixmap);
1817 default_icon_mask = mask;
1819 gdk_pixmap_ref (mask);
1821 default_icon_hot_x = hot_x;
1822 default_icon_hot_y = hot_y;
1826 /*************************************************************
1827 * gtk_drag_source_handle_event:
1828 * Called from widget event handling code on Drag events
1832 * toplevel: Toplevel widget that received the event
1835 *************************************************************/
1838 gtk_drag_source_handle_event (GtkWidget *widget,
1841 GtkDragSourceInfo *info;
1842 GdkDragContext *context;
1844 g_return_if_fail (widget != NULL);
1845 g_return_if_fail (event != NULL);
1847 context = event->dnd.context;
1848 info = g_dataset_get_data (context, "gtk-info");
1852 switch (event->type)
1854 case GDK_DRAG_STATUS:
1858 if (info->proxy_dest)
1860 if (!event->dnd.send_event)
1862 if (info->proxy_dest->proxy_drop_wait)
1864 /* Aha - we can finally pass the MOTIF DROP on... */
1865 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
1869 gdk_drag_status (info->proxy_dest->context,
1870 event->dnd.context->action,
1877 cursor = gtk_drag_get_cursor (event->dnd.context->action);
1878 if (info->cursor != cursor)
1880 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window),
1881 PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
1882 ((GdkCursorPrivate *)cursor)->xcursor,
1884 info->cursor = cursor;
1887 if (info->last_event)
1889 gtk_drag_motion_cb (info->widget,
1890 (GdkEventMotion *)info->last_event,
1892 info->last_event = NULL;
1898 case GDK_DROP_FINISHED:
1899 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
1902 g_assert_not_reached();
1906 /*************************************************************
1907 * gtk_drag_source_check_selection:
1908 * Check if we've set up handlers/claimed the selection
1909 * for a given drag. If not, add them.
1913 *************************************************************/
1916 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
1922 tmp_list = info->selections;
1925 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
1927 tmp_list = tmp_list->next;
1930 gtk_selection_owner_set (info->ipc_widget, selection, time);
1931 info->selections = g_list_prepend (info->selections,
1932 GUINT_TO_POINTER (selection));
1934 tmp_list = info->target_list->list;
1937 GtkTargetPair *pair = tmp_list->data;
1939 gtk_selection_add_target (info->ipc_widget,
1943 tmp_list = tmp_list->next;
1946 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
1948 gtk_selection_add_target (info->ipc_widget,
1950 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
1951 TARGET_MOTIF_SUCCESS);
1952 gtk_selection_add_target (info->ipc_widget,
1954 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
1955 TARGET_MOTIF_FAILURE);
1958 gtk_selection_add_target (info->ipc_widget,
1960 gdk_atom_intern ("DELETE", FALSE),
1964 /*************************************************************
1965 * gtk_drag_drop_finished:
1966 * Clean up from the drag, and display snapback, if necessary.
1972 *************************************************************/
1975 gtk_drag_drop_finished (GtkDragSourceInfo *info,
1979 if (info->proxy_dest)
1981 /* The time from the event isn't reliable for Xdnd drags */
1982 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
1983 info->proxy_dest->proxy_drop_time);
1989 gtk_drag_source_info_destroy (info);
1993 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
1997 anim->n_steps = MAX (info->cur_x - info->start_x,
1998 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
1999 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2000 if (info->icon_window)
2002 gtk_widget_show(info->icon_window);
2003 gdk_window_raise (info->icon_window->window);
2006 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2010 gtk_drag_source_release_selections (info, GDK_CURRENT_TIME); /* fixme */
2014 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2017 GList *tmp_list = info->selections;
2020 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2021 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2022 gtk_selection_owner_set (NULL, selection, time);
2023 tmp_list = tmp_list->next;
2026 g_list_free (info->selections);
2027 info->selections = NULL;
2030 /*************************************************************
2032 * Send a drop event.
2036 *************************************************************/
2039 gtk_drag_drop (GtkDragSourceInfo *info, guint32 time)
2041 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2043 GtkSelectionData selection_data;
2045 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2047 tmp_list = info->target_list->list;
2050 GtkTargetPair *pair = tmp_list->data;
2052 if (pair->target == target)
2054 selection_data.selection = GDK_NONE;
2055 selection_data.target = target;
2056 selection_data.data = NULL;
2057 selection_data.length = -1;
2059 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2060 info->context, &selection_data,
2064 /* FIXME: Should we check for length >= 0 here? */
2065 gtk_drag_drop_finished (info, TRUE, time);
2068 tmp_list = tmp_list->next;
2070 gtk_drag_drop_finished (info, FALSE, time);
2074 if (info->icon_window)
2075 gtk_widget_hide (info->icon_window);
2077 gdk_drag_drop (info->context, time);
2078 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2079 gtk_drag_abort_timeout,
2085 * Source side callbacks.
2089 gtk_drag_source_event_cb (GtkWidget *widget,
2093 GtkDragSourceSite *site;
2094 site = (GtkDragSourceSite *)data;
2096 switch (event->type)
2098 case GDK_BUTTON_PRESS:
2099 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2101 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2102 site->x = event->button.x;
2103 site->y = event->button.y;
2107 case GDK_BUTTON_RELEASE:
2108 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2110 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2114 case GDK_MOTION_NOTIFY:
2115 if (site->state & event->motion.state & site->start_button_mask)
2117 /* FIXME: This is really broken and can leave us
2123 if (site->state & event->motion.state &
2124 GDK_BUTTON1_MASK << (i - 1))
2128 if (MAX (abs(site->x - event->motion.x),
2129 abs(site->y - event->motion.y)) > 3)
2131 GtkDragSourceInfo *info;
2132 GdkDragContext *context;
2135 context = gtk_drag_begin (widget, site->target_list,
2140 info = g_dataset_get_data (context, "gtk-info");
2142 if (!info->icon_window)
2145 gtk_drag_set_icon_pixmap (context,
2148 site->mask, -2, -2);
2150 gtk_drag_set_icon_default (context);
2158 default: /* hit for 2/3BUTTON_PRESS */
2165 gtk_drag_source_site_destroy (gpointer data)
2167 GtkDragSourceSite *site = data;
2169 if (site->target_list)
2170 gtk_target_list_unref (site->target_list);
2173 gdk_pixmap_unref (site->pixmap);
2176 gdk_pixmap_unref (site->mask);
2182 gtk_drag_selection_get (GtkWidget *widget,
2183 GtkSelectionData *selection_data,
2188 GtkDragSourceInfo *info = data;
2189 static GdkAtom null_atom = GDK_NONE;
2193 null_atom = gdk_atom_intern ("NULL", FALSE);
2198 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2201 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2203 case TARGET_MOTIF_SUCCESS:
2204 gtk_drag_drop_finished (info, TRUE, time);
2205 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2207 case TARGET_MOTIF_FAILURE:
2208 gtk_drag_drop_finished (info, FALSE, time);
2209 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2212 if (info->proxy_dest)
2214 /* This is sort of dangerous and needs to be thought
2217 info->proxy_dest->proxy_data = selection_data;
2218 gtk_drag_get_data (info->widget,
2219 info->proxy_dest->context,
2220 selection_data->target,
2223 info->proxy_dest->proxy_data = NULL;
2227 if (gtk_target_list_find (info->target_list,
2228 selection_data->target,
2231 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2243 gtk_drag_anim_timeout (gpointer data)
2245 GtkDragAnim *anim = data;
2248 if (anim->step == anim->n_steps)
2250 gtk_drag_source_info_destroy (anim->info);
2257 x = (anim->info->start_x * (anim->step + 1) +
2258 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2259 y = (anim->info->start_y * (anim->step + 1) +
2260 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2261 if (anim->info->icon_window)
2262 gtk_widget_set_uposition (anim->info->icon_window, x, y);
2271 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2273 if (info->icon_window)
2275 gtk_widget_hide (info->icon_window);
2276 gtk_widget_unref (info->icon_window);
2278 info->icon_window = NULL;
2283 gtk_drag_source_info_destroy (gpointer data)
2285 GtkDragSourceInfo *info = data;
2287 gtk_drag_remove_icon (data);
2289 if (!info->proxy_dest)
2290 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2294 gtk_widget_unref (info->widget);
2296 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2297 gtk_selection_remove_all (info->ipc_widget);
2298 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2299 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2300 gtk_drag_release_ipc_widget (info->ipc_widget);
2302 gtk_target_list_unref (info->target_list);
2304 g_dataset_set_data (info->context, "gtk-info", NULL);
2305 gdk_drag_context_unref (info->context);
2307 if (info->drop_timeout)
2308 gtk_timeout_remove (info->drop_timeout);
2313 /*************************************************************
2314 * gtk_drag_motion_cb:
2315 * "motion_notify_event" callback during drag.
2319 *************************************************************/
2322 gtk_drag_motion_cb (GtkWidget *widget,
2323 GdkEventMotion *event,
2326 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2328 GdkDragAction action;
2329 GdkWindow *window = NULL;
2330 GdkWindow *dest_window;
2331 GdkDragProtocol protocol;
2332 gint x_root, y_root;
2336 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2337 event->x_root = x_root;
2338 event->y_root = y_root;
2341 action = gtk_drag_get_event_action ((GdkEvent *)event,
2343 info->context->actions);
2345 info->cur_x = event->x_root - info->hot_x;
2346 info->cur_y = event->y_root - info->hot_y;
2348 if (info->icon_window)
2350 gdk_window_raise (info->icon_window->window);
2351 gtk_widget_set_uposition (info->icon_window, info->cur_x, info->cur_y);
2352 window = info->icon_window->window;
2355 gdk_drag_find_window (info->context,
2356 window, event->x_root, event->y_root,
2357 &dest_window, &protocol);
2359 if (gdk_drag_motion (info->context, dest_window, protocol,
2360 event->x_root, event->y_root, action,
2363 if (info->last_event)
2364 gdk_event_free ((GdkEvent *)info->last_event);
2366 info->last_event = gdk_event_copy ((GdkEvent *)event);
2370 gdk_window_unref (dest_window);
2372 selection = gdk_drag_get_selection (info->context);
2374 gtk_drag_source_check_selection (info, selection, event->time);
2377 /* We ignore the response, so we can respond precisely to the drop
2380 gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2386 /*************************************************************
2387 * gtk_drag_motion_cb:
2388 * "button_release_event" callback during drag.
2392 *************************************************************/
2395 gtk_drag_button_release_cb (GtkWidget *widget,
2396 GdkEventButton *event,
2399 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2400 GtkWidget *source_widget = info->widget;
2401 GdkEvent send_event;
2403 gtk_widget_ref (source_widget);
2405 if (event->button != info->button)
2408 gdk_pointer_ungrab (event->time);
2410 if ((info->context->action != 0) && (info->context->dest_window != NULL))
2412 gtk_drag_drop (info, event->time);
2416 gdk_drag_abort (info->context, event->time);
2417 gtk_drag_drop_finished (info, FALSE, event->time);
2420 gtk_grab_remove (widget);
2422 send_event.button.type = GDK_BUTTON_RELEASE;
2423 send_event.button.window = source_widget->window;
2424 send_event.button.x = 0;
2425 send_event.button.y = 0;
2426 send_event.button.state = event->state;
2427 send_event.button.button = event->button;
2429 send_event.button.time = event->time;
2431 /* Send on the button release to the original widget to
2432 * convince it to release its grab
2434 gtk_widget_event (source_widget, &send_event);
2435 gtk_widget_unref (source_widget);
2441 gtk_drag_abort_timeout (gpointer data)
2443 GtkDragSourceInfo *info = data;
2444 guint32 time = GDK_CURRENT_TIME;
2446 if (info->proxy_dest)
2447 time = info->proxy_dest->proxy_drop_time;
2449 info->drop_timeout = 0;
2450 gtk_drag_drop_finished (info, FALSE, time);