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 "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 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)
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 (data->found || !GTK_WIDGET_MAPPED (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_forall (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_unset
1621 * Unregister this widget as a drag source.
1625 *************************************************************/
1628 gtk_drag_source_unset (GtkWidget *widget)
1630 GtkDragSourceSite *site;
1632 g_return_if_fail (widget != NULL);
1634 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1638 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1639 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1643 /*************************************************************
1644 * gtk_drag_source_set_icon:
1645 * Set an icon for drags from this source.
1647 * colormap: Colormap for this icon
1651 *************************************************************/
1654 gtk_drag_source_set_icon (GtkWidget *widget,
1655 GdkColormap *colormap,
1659 GtkDragSourceSite *site;
1661 g_return_if_fail (widget != NULL);
1663 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1664 g_return_if_fail (site != NULL);
1667 gdk_colormap_unref (site->colormap);
1669 gdk_pixmap_unref (site->pixmap);
1671 gdk_pixmap_unref (site->mask);
1673 site->colormap = colormap;
1675 gdk_colormap_ref (colormap);
1677 site->pixmap = pixmap;
1679 gdk_pixmap_ref (pixmap);
1683 gdk_pixmap_ref (mask);
1686 /*************************************************************
1687 * gtk_drag_set_icon_widget:
1688 * Set a widget as the icon for a drag.
1695 *************************************************************/
1698 gtk_drag_set_icon_widget (GdkDragContext *context,
1703 GtkDragSourceInfo *info;
1705 g_return_if_fail (context != NULL);
1706 g_return_if_fail (widget != NULL);
1708 info = g_dataset_get_data (context, "gtk-info");
1709 gtk_drag_remove_icon (info);
1711 info->icon_window = widget;
1714 gtk_widget_set_uposition (widget, info->cur_x, info->cur_y);
1715 gtk_widget_ref (widget);
1716 gdk_window_raise (widget->window);
1717 gtk_widget_show (widget);
1720 info->hot_x = hot_x;
1721 info->hot_y = hot_y;
1724 /*************************************************************
1725 * gtk_drag_set_icon_pixmap:
1726 * Set a widget as the icon for a drag.
1729 * colormap: Colormap for the icon window.
1735 *************************************************************/
1738 gtk_drag_set_icon_pixmap (GdkDragContext *context,
1739 GdkColormap *colormap,
1748 g_return_if_fail (context != NULL);
1749 g_return_if_fail (colormap != NULL);
1750 g_return_if_fail (pixmap != NULL);
1752 gdk_window_get_size (pixmap, &width, &height);
1754 gtk_widget_push_visual (gdk_colormap_get_visual(colormap));
1755 gtk_widget_push_colormap (colormap);
1757 window = gtk_window_new (GTK_WINDOW_POPUP);
1758 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
1760 gtk_widget_pop_visual ();
1761 gtk_widget_pop_colormap ();
1763 gtk_widget_set_usize (window, width, height);
1764 gtk_widget_realize (window);
1766 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1769 gtk_widget_shape_combine_mask (window, mask, 0, 0);
1771 gtk_drag_set_icon_widget (context, window, hot_x, hot_y);
1774 /*************************************************************
1775 * gtk_drag_set_icon_default:
1776 * Set the icon for a drag to the default icon.
1780 *************************************************************/
1783 gtk_drag_set_icon_default (GdkDragContext *context)
1785 g_return_if_fail (context != NULL);
1787 if (!default_icon_pixmap)
1789 default_icon_colormap = gdk_colormap_get_system ();
1790 default_icon_pixmap =
1791 gdk_pixmap_colormap_create_from_xpm_d (NULL,
1792 default_icon_colormap,
1794 NULL, drag_default_xpm);
1795 default_icon_hot_x = -2;
1796 default_icon_hot_y = -2;
1799 gtk_drag_set_icon_pixmap (context,
1800 default_icon_colormap,
1801 default_icon_pixmap,
1804 default_icon_hot_y);
1807 /*************************************************************
1808 * gtk_drag_set_default_icon:
1809 * Set a default icon for all drags as a pixmap.
1811 * colormap: Colormap for the icon window.
1817 *************************************************************/
1820 gtk_drag_set_default_icon (GdkColormap *colormap,
1826 g_return_if_fail (colormap != NULL);
1827 g_return_if_fail (pixmap != NULL);
1829 if (default_icon_colormap)
1830 gdk_colormap_unref (default_icon_colormap);
1831 if (default_icon_pixmap)
1832 gdk_pixmap_unref (default_icon_pixmap);
1833 if (default_icon_mask)
1834 gdk_pixmap_unref (default_icon_pixmap);
1836 default_icon_colormap = colormap;
1837 gdk_colormap_ref (colormap);
1839 default_icon_pixmap = pixmap;
1840 gdk_pixmap_ref (pixmap);
1842 default_icon_mask = mask;
1844 gdk_pixmap_ref (mask);
1846 default_icon_hot_x = hot_x;
1847 default_icon_hot_y = hot_y;
1851 /*************************************************************
1852 * gtk_drag_source_handle_event:
1853 * Called from widget event handling code on Drag events
1857 * toplevel: Toplevel widget that received the event
1860 *************************************************************/
1863 gtk_drag_source_handle_event (GtkWidget *widget,
1866 GtkDragSourceInfo *info;
1867 GdkDragContext *context;
1869 g_return_if_fail (widget != NULL);
1870 g_return_if_fail (event != NULL);
1872 context = event->dnd.context;
1873 info = g_dataset_get_data (context, "gtk-info");
1877 switch (event->type)
1879 case GDK_DRAG_STATUS:
1883 if (info->proxy_dest)
1885 if (!event->dnd.send_event)
1887 if (info->proxy_dest->proxy_drop_wait)
1889 /* Aha - we can finally pass the MOTIF DROP on... */
1890 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
1894 gdk_drag_status (info->proxy_dest->context,
1895 event->dnd.context->action,
1902 cursor = gtk_drag_get_cursor (event->dnd.context->action);
1903 if (info->cursor != cursor)
1905 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window),
1906 PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
1907 ((GdkCursorPrivate *)cursor)->xcursor,
1909 info->cursor = cursor;
1912 if (info->last_event)
1914 gtk_drag_motion_cb (info->widget,
1915 (GdkEventMotion *)info->last_event,
1917 info->last_event = NULL;
1923 case GDK_DROP_FINISHED:
1924 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
1927 g_assert_not_reached();
1931 /*************************************************************
1932 * gtk_drag_source_check_selection:
1933 * Check if we've set up handlers/claimed the selection
1934 * for a given drag. If not, add them.
1938 *************************************************************/
1941 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
1947 tmp_list = info->selections;
1950 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
1952 tmp_list = tmp_list->next;
1955 gtk_selection_owner_set (info->ipc_widget, selection, time);
1956 info->selections = g_list_prepend (info->selections,
1957 GUINT_TO_POINTER (selection));
1959 tmp_list = info->target_list->list;
1962 GtkTargetPair *pair = tmp_list->data;
1964 gtk_selection_add_target (info->ipc_widget,
1968 tmp_list = tmp_list->next;
1971 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
1973 gtk_selection_add_target (info->ipc_widget,
1975 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
1976 TARGET_MOTIF_SUCCESS);
1977 gtk_selection_add_target (info->ipc_widget,
1979 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
1980 TARGET_MOTIF_FAILURE);
1983 gtk_selection_add_target (info->ipc_widget,
1985 gdk_atom_intern ("DELETE", FALSE),
1989 /*************************************************************
1990 * gtk_drag_drop_finished:
1991 * Clean up from the drag, and display snapback, if necessary.
1997 *************************************************************/
2000 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2004 if (info->proxy_dest)
2006 /* The time from the event isn't reliable for Xdnd drags */
2007 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2008 info->proxy_dest->proxy_drop_time);
2014 gtk_drag_source_info_destroy (info);
2018 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2022 anim->n_steps = MAX (info->cur_x - info->start_x,
2023 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2024 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2025 if (info->icon_window)
2027 gtk_widget_show(info->icon_window);
2028 gdk_window_raise (info->icon_window->window);
2031 /* Mark the context as dead, so if the destination decides
2032 * to respond really late, we still are OK.
2034 g_dataset_set_data (info->context, "gtk-info", NULL);
2035 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2039 gtk_drag_source_release_selections (info, GDK_CURRENT_TIME); /* fixme */
2043 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2046 GList *tmp_list = info->selections;
2049 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2050 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2051 gtk_selection_owner_set (NULL, selection, time);
2052 tmp_list = tmp_list->next;
2055 g_list_free (info->selections);
2056 info->selections = NULL;
2059 /*************************************************************
2061 * Send a drop event.
2065 *************************************************************/
2068 gtk_drag_drop (GtkDragSourceInfo *info, guint32 time)
2070 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2072 GtkSelectionData selection_data;
2074 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2076 tmp_list = info->target_list->list;
2079 GtkTargetPair *pair = tmp_list->data;
2081 if (pair->target == target)
2083 selection_data.selection = GDK_NONE;
2084 selection_data.target = target;
2085 selection_data.data = NULL;
2086 selection_data.length = -1;
2088 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2089 info->context, &selection_data,
2093 /* FIXME: Should we check for length >= 0 here? */
2094 gtk_drag_drop_finished (info, TRUE, time);
2097 tmp_list = tmp_list->next;
2099 gtk_drag_drop_finished (info, FALSE, time);
2103 if (info->icon_window)
2104 gtk_widget_hide (info->icon_window);
2106 gdk_drag_drop (info->context, time);
2107 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2108 gtk_drag_abort_timeout,
2114 * Source side callbacks.
2118 gtk_drag_source_event_cb (GtkWidget *widget,
2122 GtkDragSourceSite *site;
2123 site = (GtkDragSourceSite *)data;
2125 switch (event->type)
2127 case GDK_BUTTON_PRESS:
2128 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2130 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2131 site->x = event->button.x;
2132 site->y = event->button.y;
2136 case GDK_BUTTON_RELEASE:
2137 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2139 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2143 case GDK_MOTION_NOTIFY:
2144 if (site->state & event->motion.state & site->start_button_mask)
2146 /* FIXME: This is really broken and can leave us
2152 if (site->state & event->motion.state &
2153 GDK_BUTTON1_MASK << (i - 1))
2157 if (MAX (abs(site->x - event->motion.x),
2158 abs(site->y - event->motion.y)) > 3)
2160 GtkDragSourceInfo *info;
2161 GdkDragContext *context;
2164 context = gtk_drag_begin (widget, site->target_list,
2169 info = g_dataset_get_data (context, "gtk-info");
2171 if (!info->icon_window)
2174 gtk_drag_set_icon_pixmap (context,
2177 site->mask, -2, -2);
2179 gtk_drag_set_icon_default (context);
2187 default: /* hit for 2/3BUTTON_PRESS */
2194 gtk_drag_source_site_destroy (gpointer data)
2196 GtkDragSourceSite *site = data;
2198 if (site->target_list)
2199 gtk_target_list_unref (site->target_list);
2202 gdk_pixmap_unref (site->pixmap);
2205 gdk_pixmap_unref (site->mask);
2211 gtk_drag_selection_get (GtkWidget *widget,
2212 GtkSelectionData *selection_data,
2217 GtkDragSourceInfo *info = data;
2218 static GdkAtom null_atom = GDK_NONE;
2222 null_atom = gdk_atom_intern ("NULL", FALSE);
2227 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2230 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2232 case TARGET_MOTIF_SUCCESS:
2233 gtk_drag_drop_finished (info, TRUE, time);
2234 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2236 case TARGET_MOTIF_FAILURE:
2237 gtk_drag_drop_finished (info, FALSE, time);
2238 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2241 if (info->proxy_dest)
2243 /* This is sort of dangerous and needs to be thought
2246 info->proxy_dest->proxy_data = selection_data;
2247 gtk_drag_get_data (info->widget,
2248 info->proxy_dest->context,
2249 selection_data->target,
2252 info->proxy_dest->proxy_data = NULL;
2256 if (gtk_target_list_find (info->target_list,
2257 selection_data->target,
2260 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2272 gtk_drag_anim_timeout (gpointer data)
2274 GtkDragAnim *anim = data;
2278 GDK_THREADS_ENTER ();
2280 if (anim->step == anim->n_steps)
2282 gtk_drag_source_info_destroy (anim->info);
2289 x = (anim->info->start_x * (anim->step + 1) +
2290 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2291 y = (anim->info->start_y * (anim->step + 1) +
2292 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2293 if (anim->info->icon_window)
2294 gtk_widget_set_uposition (anim->info->icon_window, x, y);
2301 GDK_THREADS_LEAVE ();
2307 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2309 if (info->icon_window)
2311 gtk_widget_hide (info->icon_window);
2312 gtk_widget_unref (info->icon_window);
2314 info->icon_window = NULL;
2319 gtk_drag_source_info_destroy (gpointer data)
2321 GtkDragSourceInfo *info = data;
2323 gtk_drag_remove_icon (data);
2325 if (!info->proxy_dest)
2326 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2330 gtk_widget_unref (info->widget);
2332 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2333 gtk_selection_remove_all (info->ipc_widget);
2334 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2335 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2336 gtk_drag_release_ipc_widget (info->ipc_widget);
2338 gtk_target_list_unref (info->target_list);
2340 g_dataset_set_data (info->context, "gtk-info", NULL);
2341 gdk_drag_context_unref (info->context);
2343 if (info->drop_timeout)
2344 gtk_timeout_remove (info->drop_timeout);
2349 /*************************************************************
2350 * gtk_drag_motion_cb:
2351 * "motion_notify_event" callback during drag.
2355 *************************************************************/
2358 gtk_drag_motion_cb (GtkWidget *widget,
2359 GdkEventMotion *event,
2362 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2364 GdkDragAction action;
2365 GdkWindow *window = NULL;
2366 GdkWindow *dest_window;
2367 GdkDragProtocol protocol;
2368 gint x_root, y_root;
2372 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2373 event->x_root = x_root;
2374 event->y_root = y_root;
2377 action = gtk_drag_get_event_action ((GdkEvent *)event,
2379 info->context->actions);
2381 info->cur_x = event->x_root - info->hot_x;
2382 info->cur_y = event->y_root - info->hot_y;
2384 if (info->icon_window)
2386 gdk_window_raise (info->icon_window->window);
2387 gtk_widget_set_uposition (info->icon_window, info->cur_x, info->cur_y);
2388 window = info->icon_window->window;
2391 gdk_drag_find_window (info->context,
2392 window, event->x_root, event->y_root,
2393 &dest_window, &protocol);
2395 if (gdk_drag_motion (info->context, dest_window, protocol,
2396 event->x_root, event->y_root, action,
2399 if (info->last_event)
2400 gdk_event_free ((GdkEvent *)info->last_event);
2402 info->last_event = gdk_event_copy ((GdkEvent *)event);
2406 gdk_window_unref (dest_window);
2408 selection = gdk_drag_get_selection (info->context);
2410 gtk_drag_source_check_selection (info, selection, event->time);
2413 /* We ignore the response, so we can respond precisely to the drop
2416 gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2422 /*************************************************************
2423 * gtk_drag_motion_cb:
2424 * "button_release_event" callback during drag.
2428 *************************************************************/
2431 gtk_drag_button_release_cb (GtkWidget *widget,
2432 GdkEventButton *event,
2435 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2436 GtkWidget *source_widget = info->widget;
2437 GdkEvent send_event;
2439 gtk_widget_ref (source_widget);
2441 if (event->button != info->button)
2444 gdk_pointer_ungrab (event->time);
2446 if ((info->context->action != 0) && (info->context->dest_window != NULL))
2448 gtk_drag_drop (info, event->time);
2452 gdk_drag_abort (info->context, event->time);
2453 gtk_drag_drop_finished (info, FALSE, event->time);
2456 gtk_grab_remove (widget);
2458 send_event.button.type = GDK_BUTTON_RELEASE;
2459 send_event.button.window = source_widget->window;
2460 send_event.button.x = 0;
2461 send_event.button.y = 0;
2462 send_event.button.state = event->state;
2463 send_event.button.button = event->button;
2465 send_event.button.time = event->time;
2467 /* Send on the button release to the original widget to
2468 * convince it to release its grab
2470 gtk_widget_event (source_widget, &send_event);
2471 gtk_widget_unref (source_widget);
2477 gtk_drag_abort_timeout (gpointer data)
2479 GtkDragSourceInfo *info = data;
2480 guint32 time = GDK_CURRENT_TIME;
2482 if (info->proxy_dest)
2483 time = info->proxy_dest->proxy_drop_time;
2485 info->drop_timeout = 0;
2486 gtk_drag_drop_finished (info, FALSE, time);