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;
30 typedef struct _GtkDragSourceSite GtkDragSourceSite;
31 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
32 typedef struct _GtkDragDestSite GtkDragDestSite;
33 typedef struct _GtkDragDestInfo GtkDragDestInfo;
34 typedef struct _GtkDragAnim GtkDragAnim;
35 typedef struct _GtkDragFindData GtkDragFindData;
44 struct _GtkDragSourceSite {
45 GdkModifierType start_button_mask;
46 GtkTargetList *target_list; /* Targets for drag data */
47 GdkDragAction actions; /* Possible actions */
48 GdkColormap *colormap; /* Colormap for drag icon */
49 GdkPixmap *pixmap; /* Icon for drag data */
52 /* Stored button press information to detect drag beginning */
57 struct _GtkDragSourceInfo {
59 GtkTargetList *target_list; /* Targets for drag data */
60 GdkDragContext *context; /* drag context */
61 GtkWidget *icon_window; /* Window for drag */
62 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
63 GdkCursor *cursor; /* Cursor for drag */
64 gint hot_x, hot_y; /* Hot spot for drag */
65 gint button; /* mouse button starting drag */
67 GtkDragStatus status; /* drag status */
68 GdkEvent *last_event; /* motion event waiting for response */
70 gint start_x, start_y; /* Initial position */
71 gint cur_x, cur_y; /* Current Position */
73 GList *selections; /* selections we've claimed */
75 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
77 guint drop_timeout; /* Timeout for aborting drop */
80 struct _GtkDragDestSite {
81 GtkDestDefaults flags;
82 GtkTargetList *target_list;
83 GdkDragAction actions;
84 GdkWindow *proxy_window;
85 GdkDragProtocol proxy_protocol;
86 gboolean do_proxy : 1;
87 gboolean proxy_coords : 1;
88 gboolean have_drag : 1;
91 struct _GtkDragDestInfo {
92 GtkWidget *widget; /* Widget in which drag is in */
93 GdkDragContext *context; /* Drag context */
94 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
95 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
96 gboolean dropped : 1; /* Set after we receive a drop */
97 guint32 proxy_drop_time; /* Timestamp for proxied drop */
98 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
99 * status reply before sending
102 gint drop_x, drop_y; /* Position of drop */
105 #define DROP_ABORT_TIME 10000
107 #define ANIM_STEP_TIME 50
108 #define ANIM_STEP_LENGTH 50
109 #define ANIM_MIN_STEPS 5
110 #define ANIM_MAX_STEPS 10
112 struct _GtkDragAnim {
113 GtkDragSourceInfo *info;
118 struct _GtkDragFindData {
121 GdkDragContext *context;
122 GtkDragDestInfo *info;
124 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
125 gint x, gint y, guint32 time);
129 /* Enumeration for some targets we handle internally */
132 TARGET_MOTIF_SUCCESS = 0x40000000,
133 TARGET_MOTIF_FAILURE,
139 static GdkPixmap *default_icon_pixmap = NULL;
140 static GdkPixmap *default_icon_mask = NULL;
141 static GdkColormap *default_icon_colormap = NULL;
142 static gint default_icon_hot_x;
143 static gint default_icon_hot_y;
145 /* Forward declarations */
146 static GdkDragAction gtk_drag_get_event_action (GdkEvent *event,
148 GdkDragAction actions);
149 static GdkCursor * gtk_drag_get_cursor (GdkDragAction action);
150 static GtkWidget *gtk_drag_get_ipc_widget (void);
151 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
153 static GdkAtom gtk_drag_dest_find_target (GtkDragDestSite *site,
154 GdkDragContext *context);
155 static void gtk_drag_selection_received (GtkWidget *widget,
156 GtkSelectionData *selection_data,
159 static void gtk_drag_find_widget (GtkWidget *widget,
160 GtkDragFindData *data);
161 static void gtk_drag_proxy_begin (GtkWidget *widget,
162 GtkDragDestInfo *dest_info);
163 static void gtk_drag_dest_info_destroy (gpointer data);
164 static void gtk_drag_dest_realized (GtkWidget *widget);
165 static void gtk_drag_dest_site_destroy (gpointer data);
166 static void gtk_drag_dest_leave (GtkWidget *widget,
167 GdkDragContext *context,
169 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
170 GdkDragContext *context,
174 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
175 GdkDragContext *context,
180 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
183 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
185 static void gtk_drag_drop (GtkDragSourceInfo *info,
187 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
191 static gint gtk_drag_source_event_cb (GtkWidget *widget,
194 static void gtk_drag_source_site_destroy (gpointer data);
195 static void gtk_drag_selection_get (GtkWidget *widget,
196 GtkSelectionData *selection_data,
200 static gint gtk_drag_anim_timeout (gpointer data);
201 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
202 static void gtk_drag_source_info_destroy (gpointer data);
203 static gint gtk_drag_motion_cb (GtkWidget *widget,
204 GdkEventMotion *event,
206 static gint gtk_drag_button_release_cb (GtkWidget *widget,
207 GdkEventButton *event,
209 static gint gtk_drag_abort_timeout (gpointer data);
211 /************************
212 * Cursor and Icon data *
213 ************************/
215 #define action_ask_width 16
216 #define action_ask_height 16
217 static char action_ask_bits[] = {
218 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
219 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
220 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
222 #define action_ask_mask_width 16
223 #define action_ask_mask_height 16
224 static char action_ask_mask_bits[] = {
225 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
226 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
227 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
229 #define action_copy_width 16
230 #define action_copy_height 16
231 static char action_copy_bits[] = {
232 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
233 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
234 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
236 #define action_copy_mask_width 16
237 #define action_copy_mask_height 16
238 static char action_copy_mask_bits[] = {
239 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
240 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
241 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
243 #define action_move_width 16
244 #define action_move_height 16
245 static char action_move_bits[] = {
246 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
247 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
248 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
250 #define action_move_mask_width 16
251 #define action_move_mask_height 16
252 static char action_move_mask_bits[] = {
253 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
254 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
255 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
257 #define action_link_width 16
258 #define action_link_height 16
259 static char action_link_bits[] = {
260 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
261 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
262 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
264 #define action_link_mask_width 16
265 #define action_link_mask_height 16
266 static char action_link_mask_bits[] = {
267 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
268 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
269 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
271 #define action_none_width 16
272 #define action_none_height 16
273 static char action_none_bits[] = {
274 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
275 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
276 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
278 #define action_none_mask_width 16
279 #define action_none_mask_height 16
280 static char action_none_mask_bits[] = {
281 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
282 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
283 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
285 static char empty_bits[] = {
286 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
287 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
290 #define CURSOR_WIDTH 16
291 #define CURSOR_HEIGHT 16
294 GdkDragAction action;
299 { GDK_ACTION_DEFAULT, 0 },
300 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
301 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
302 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
303 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
304 { 0 , action_none_bits, action_none_mask_bits, NULL },
307 static gint n_drag_cursors = sizeof(drag_cursors) / sizeof(drag_cursors[0]);
310 static char * drag_default_xpm[] = {
323 " ...+++++++++++.. ",
324 " ..+.++++++++++++.. ",
325 " .++.++++++++++++.. ",
326 " .+++.++++++++++++.. ",
327 " .++++.++++++++++++. ",
328 " .+++.+++++++++++++.. ",
329 " .++.+++++++++++++++.. ",
330 " .+.+++++++++++++++++.. ",
331 " ..+++++++++++++++++++.. ",
332 " ..++++++++++++++++++++. ",
333 " .++++++++++++++++++++.. ",
334 " ..+++++++++++++++++.. ",
335 " .++++++++++++++++.. ",
336 " ..+++++++++++++... ",
348 /*********************
349 * Utility functions *
350 *********************/
352 /*************************************************************
353 * gtk_drag_get_ipc_widget:
354 * Return a invisible, off-screen, override-redirect
359 *************************************************************/
362 gtk_drag_get_ipc_widget(void)
368 GSList *tmp = drag_widgets;
369 result = drag_widgets->data;
370 drag_widgets = drag_widgets->next;
371 g_slist_free_1 (tmp);
375 result = gtk_invisible_new();
376 gtk_widget_show (result);
382 /*************************************************************
383 * gtk_drag_release_ipc_widget:
384 * Releases widget retrieved with gtk_drag_get_ipc_widget()
386 * widget: the widget to release.
388 *************************************************************/
391 gtk_drag_release_ipc_widget (GtkWidget *widget)
393 drag_widgets = g_slist_prepend (drag_widgets, widget);
397 gtk_drag_get_event_action (GdkEvent *event, gint button, GdkDragAction actions)
401 GdkModifierType state = 0;
405 case GDK_MOTION_NOTIFY:
406 state = event->motion.state;
408 case GDK_BUTTON_PRESS:
409 case GDK_2BUTTON_PRESS:
410 case GDK_3BUTTON_PRESS:
411 case GDK_BUTTON_RELEASE:
412 state = event->button.state;
415 case GDK_KEY_RELEASE:
416 state = event->key.state;
418 case GDK_ENTER_NOTIFY:
419 case GDK_LEAVE_NOTIFY:
420 state = event->crossing.state;
427 return GDK_ACTION_ASK;
429 if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
431 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
432 return (actions & GDK_ACTION_LINK) ? GDK_ACTION_LINK : 0;
433 else if (state & GDK_CONTROL_MASK)
434 return (actions & GDK_ACTION_COPY) ? GDK_ACTION_COPY : 0;
436 return (actions & GDK_ACTION_MOVE) ? GDK_ACTION_MOVE : 0;
440 if (actions & GDK_ACTION_COPY)
441 return GDK_ACTION_COPY;
442 else if (actions & GDK_ACTION_MOVE)
443 return GDK_ACTION_MOVE;
444 else if (actions & GDK_ACTION_LINK)
445 return GDK_ACTION_LINK;
453 gtk_drag_get_cursor (GdkDragAction action)
457 for (i = 0 ; i < n_drag_cursors - 1; i++)
458 if (drag_cursors[i].action == action)
461 if (drag_cursors[i].cursor == NULL)
466 gdk_bitmap_create_from_data (NULL,
467 drag_cursors[i].bits,
468 CURSOR_WIDTH, CURSOR_HEIGHT);
470 gdk_bitmap_create_from_data (NULL,
471 drag_cursors[i].mask,
472 CURSOR_WIDTH, CURSOR_HEIGHT);
474 gdk_color_white (gdk_colormap_get_system(), &bg);
475 gdk_color_black (gdk_colormap_get_system(), &fg);
477 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
479 gdk_pixmap_unref (pixmap);
480 gdk_pixmap_unref (mask);
483 return drag_cursors[i].cursor;
486 /********************
488 ********************/
490 /*************************************************************
492 * Get the data for a drag or drop
494 * context - drag context
495 * target - format to retrieve the data in.
496 * time - timestamp of triggering event.
499 *************************************************************/
502 gtk_drag_get_data (GtkWidget *widget,
503 GdkDragContext *context,
507 GtkWidget *selection_widget;
509 g_return_if_fail (widget != NULL);
510 g_return_if_fail (context != NULL);
512 selection_widget = gtk_drag_get_ipc_widget();
514 gdk_drag_context_ref (context);
515 gtk_widget_ref (widget);
517 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
518 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
520 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
522 gtk_selection_convert (selection_widget,
523 gdk_drag_get_selection(context),
528 /*************************************************************
530 * Notify the drag source that the transfer of data
533 * context: The drag context for this drag
534 * success: Was the data successfully transferred?
535 * time: The timestamp to use when notifying the destination.
537 *************************************************************/
540 gtk_drag_finish (GdkDragContext *context,
545 GdkAtom target = GDK_NONE;
547 g_return_if_fail (context != NULL);
551 target = gdk_atom_intern ("DELETE", FALSE);
553 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
555 target = gdk_atom_intern (success ?
556 "XmTRANSFER_SUCCESS" :
557 "XmTRANSFER_FAILURE",
561 if (target != GDK_NONE)
563 GtkWidget *selection_widget = gtk_drag_get_ipc_widget();
565 gdk_drag_context_ref (context);
567 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
568 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
569 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
572 gtk_selection_convert (selection_widget,
573 gdk_drag_get_selection(context),
579 gdk_drop_finish (context, success, time);
582 /*************************************************************
583 * gtk_drag_highlight:
584 * Highlight the given widget in the default manner.
588 *************************************************************/
591 gtk_drag_highlight (GtkWidget *widget)
595 g_return_if_fail (widget != NULL);
597 if (GTK_WIDGET_NO_WINDOW (widget))
599 x = widget->allocation.x;
600 y = widget->allocation.y;
608 gtk_draw_shadow (widget->style, widget->window,
609 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
611 widget->allocation.width,
612 widget->allocation.height);
614 gdk_draw_rectangle (widget->window,
615 widget->style->black_gc,
618 widget->allocation.width - 1,
619 widget->allocation.height - 1);
622 /*************************************************************
623 * gtk_drag_unhighlight:
624 * Refresh the given widget to remove the highlight.
628 *************************************************************/
631 gtk_drag_unhighlight (GtkWidget *widget)
635 g_return_if_fail (widget != NULL);
637 if (GTK_WIDGET_NO_WINDOW (widget))
639 x = widget->allocation.x;
640 y = widget->allocation.y;
648 gdk_window_clear_area_e (widget->window,
650 widget->allocation.width,
651 widget->allocation.height);
654 /*************************************************************
656 * Register a drop site, and possibly add default behaviors.
659 * flags: Which types of default drag behavior to use
660 * targets: Table of targets that can be accepted
661 * n_targets: Number of of entries in targets
664 *************************************************************/
667 gtk_drag_dest_set (GtkWidget *widget,
668 GtkDestDefaults flags,
669 GtkTargetEntry *targets,
671 GdkDragAction actions)
673 GtkDragDestSite *site;
675 g_return_if_fail (widget != NULL);
677 /* HACK, do this in the destroy */
678 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
680 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
682 if (GTK_WIDGET_REALIZED (widget))
683 gtk_drag_dest_realized (widget);
685 gtk_signal_connect (GTK_OBJECT (widget), "realize",
686 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
688 site = g_new (GtkDragDestSite, 1);
691 site->have_drag = FALSE;
693 site->target_list = gtk_target_list_new (targets, n_targets);
695 site->target_list = NULL;
697 site->actions = actions;
698 site->do_proxy = FALSE;
700 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
701 site, gtk_drag_dest_site_destroy);
704 /*************************************************************
705 * gtk_drag_dest_set_proxy:
706 * Set up this widget to proxy drags elsewhere.
709 * proxy_window: window to which forward drag events
710 * protocol: Drag protocol which the dest widget accepts
711 * use_coordinates: If true, send the same coordinates to the
712 * destination, because it is a embedded
715 *************************************************************/
718 gtk_drag_dest_set_proxy (GtkWidget *widget,
719 GdkWindow *proxy_window,
720 GdkDragProtocol protocol,
721 gboolean use_coordinates)
723 GtkDragDestSite *site;
725 g_return_if_fail (widget != NULL);
727 /* HACK, do this in the destroy */
728 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
730 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
732 if (GTK_WIDGET_REALIZED (widget))
733 gtk_drag_dest_realized (widget);
735 gtk_signal_connect (GTK_OBJECT (widget), "realize",
736 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
738 site = g_new (GtkDragDestSite, 1);
741 site->have_drag = FALSE;
742 site->target_list = NULL;
744 site->proxy_window = proxy_window;
746 gdk_window_ref (proxy_window);
747 site->do_proxy = TRUE;
748 site->proxy_protocol = protocol;
749 site->proxy_coords = use_coordinates;
751 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
752 site, gtk_drag_dest_site_destroy);
755 /*************************************************************
756 * gtk_drag_dest_unset
757 * Unregister this widget as a drag target.
761 *************************************************************/
764 gtk_drag_dest_unset (GtkWidget *widget)
766 g_return_if_fail (widget != NULL);
768 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
771 /*************************************************************
772 * gtk_drag_dest_handle_event:
773 * Called from widget event handling code on Drag events
777 * toplevel: Toplevel widget that received the event
780 *************************************************************/
783 gtk_drag_dest_handle_event (GtkWidget *toplevel,
786 GtkDragDestInfo *info;
787 GdkDragContext *context;
789 g_return_if_fail (toplevel != NULL);
790 g_return_if_fail (event != NULL);
792 context = event->dnd.context;
794 info = g_dataset_get_data (context, "gtk-info");
797 info = g_new (GtkDragDestInfo, 1);
799 info->context = event->dnd.context;
800 info->proxy_source = NULL;
801 info->proxy_data = NULL;
802 info->dropped = FALSE;
803 info->proxy_drop_wait = FALSE;
804 g_dataset_set_data_full (context,
807 gtk_drag_dest_info_destroy);
810 /* Find the widget for the event */
819 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
824 case GDK_DRAG_MOTION:
827 GtkDragFindData data;
830 if (event->type == GDK_DROP_START)
831 info->dropped = TRUE;
833 gdk_window_get_position (toplevel->window, &tx, &ty);
835 data.x = event->dnd.x_root - tx;
836 data.y = event->dnd.y_root - ty;
837 data.context = context;
840 data.callback = (event->type == GDK_DRAG_MOTION) ?
841 gtk_drag_dest_motion : gtk_drag_dest_drop;
842 data.time = event->dnd.time;
844 gtk_drag_find_widget (toplevel, &data);
846 /* We send a leave here so that the widget unhighlights
850 ((event->type == GDK_DROP_START) || (!data.found)))
852 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
858 if (event->type == GDK_DRAG_MOTION)
861 gdk_drag_status (context, 0, event->dnd.time);
863 else if (event->type == GDK_DROP_START)
864 gdk_drop_reply (context, data.found, event->dnd.time);
869 g_assert_not_reached();
873 /*************************************************************
874 * gtk_drag_dest_find_target:
875 * Decide on a target for the drag.
880 *************************************************************/
883 gtk_drag_dest_find_target (GtkDragDestSite *site,
884 GdkDragContext *context)
887 GList *tmp_source = NULL;
889 tmp_target = site->target_list->list;
892 GtkTargetPair *pair = tmp_target->data;
893 tmp_source = context->targets;
896 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
898 tmp_source = tmp_source->next;
900 tmp_target = tmp_target->next;
907 gtk_drag_selection_received (GtkWidget *widget,
908 GtkSelectionData *selection_data,
912 GdkDragContext *context;
913 GtkDragDestInfo *info;
914 GtkWidget *drop_widget;
918 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
919 info = g_dataset_get_data (context, "gtk-info");
921 if (info->proxy_data &&
922 info->proxy_data->target == selection_data->target)
924 gtk_selection_data_set (info->proxy_data,
925 selection_data->type,
926 selection_data->format,
927 selection_data->data,
928 selection_data->length);
933 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
935 gtk_drag_finish (context, TRUE, FALSE, time);
937 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
938 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
944 GtkDragDestSite *site;
946 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
948 if (site->target_list)
952 if (gtk_target_list_find (site->target_list,
953 selection_data->target,
956 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
957 selection_data->length >= 0)
958 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
959 "drag_data_received",
960 context, info->drop_x, info->drop_y,
967 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
968 "drag_data_received",
969 context, info->drop_x, info->drop_y,
970 selection_data, 0, time);
973 if (site->flags & GTK_DEST_DEFAULT_DROP)
976 gtk_drag_finish (context,
977 (selection_data->length >= 0),
978 (context->action == GDK_ACTION_MOVE),
982 gtk_widget_unref (drop_widget);
985 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
986 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
989 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
990 gdk_drag_context_unref (context);
992 gtk_drag_release_ipc_widget (widget);
995 /*************************************************************
996 * gtk_drag_find_widget:
997 * Recursive callback used to locate widgets for
998 * DRAG_MOTION and DROP_START events.
1002 *************************************************************/
1005 gtk_drag_find_widget (GtkWidget *widget,
1006 GtkDragFindData *data)
1008 GtkAllocation new_allocation;
1012 new_allocation = widget->allocation;
1014 if (!GTK_WIDGET_VISIBLE (widget))
1017 if (!GTK_WIDGET_NO_WINDOW (widget))
1019 new_allocation.x = 0;
1020 new_allocation.y = 0;
1025 GdkWindow *window = widget->window;
1026 while (window != widget->parent->window)
1028 gint tx, ty, twidth, theight;
1029 gdk_window_get_size (window, &twidth, &theight);
1031 if (new_allocation.x < 0)
1033 new_allocation.width += new_allocation.x;
1034 new_allocation.x = 0;
1036 if (new_allocation.y < 0)
1038 new_allocation.height += new_allocation.y;
1039 new_allocation.y = 0;
1041 if (new_allocation.x + new_allocation.width > twidth)
1042 new_allocation.width = twidth - new_allocation.x;
1043 if (new_allocation.y + new_allocation.height > theight)
1044 new_allocation.height = theight - new_allocation.y;
1046 gdk_window_get_position (window, &tx, &ty);
1047 new_allocation.x += tx;
1049 new_allocation.y += ty;
1052 window = gdk_window_get_parent (window);
1056 if ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1057 (data->x < new_allocation.x + new_allocation.width) &&
1058 (data->y < new_allocation.y + new_allocation.height))
1060 /* First, check if the drag is in a valid drop site in
1061 * one of our children
1063 if (GTK_IS_CONTAINER (widget))
1065 GtkDragFindData new_data = *data;
1067 new_data.x -= x_offset;
1068 new_data.y -= y_offset;
1069 new_data.found = FALSE;
1071 gtk_container_foreach (GTK_CONTAINER (widget),
1072 (GtkCallback)gtk_drag_find_widget,
1075 data->found = new_data.found;
1078 /* If not, and this widget is registered as a drop site, check to
1079 * emit "drag_motion" to check if we are actually in
1083 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1085 data->found = data->callback (widget,
1087 data->x - new_allocation.x,
1088 data->y - new_allocation.y,
1090 /* If so, send a "drag_leave" to the last widget */
1093 if (data->info->widget && data->info->widget != widget)
1095 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1097 data->info->widget = widget;
1104 gtk_drag_proxy_begin (GtkWidget *widget, GtkDragDestInfo *dest_info)
1106 GtkDragSourceInfo *source_info;
1109 source_info = g_new0 (GtkDragSourceInfo, 1);
1110 source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1112 source_info->widget = widget;
1113 gtk_widget_ref (source_info->widget);
1114 source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1115 dest_info->context->targets,
1116 dest_info->context->actions);
1118 source_info->target_list = gtk_target_list_new (NULL, 0);
1119 tmp_list = dest_info->context->targets;
1122 gtk_target_list_add (source_info->target_list,
1123 GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1124 tmp_list = tmp_list->next;
1127 source_info->proxy_dest = dest_info;
1129 g_dataset_set_data (source_info->context, "gtk-info", source_info);
1131 gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget),
1133 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1136 dest_info->proxy_source = source_info;
1140 gtk_drag_dest_info_destroy (gpointer data)
1142 GtkDragDestInfo *info = data;
1148 gtk_drag_dest_realized (GtkWidget *widget)
1150 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1151 gdk_window_register_dnd (toplevel->window);
1155 gtk_drag_dest_site_destroy (gpointer data)
1157 GtkDragDestSite *site = data;
1159 if (site->target_list)
1160 gtk_target_list_unref (site->target_list);
1166 * Default drag handlers
1169 gtk_drag_dest_leave (GtkWidget *widget,
1170 GdkDragContext *context,
1173 GtkDragDestSite *site;
1175 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1176 g_return_if_fail (site != NULL);
1180 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1182 if (info->proxy_source && !info->dropped)
1183 gdk_drag_abort (info->proxy_source->context, time);
1189 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1190 gtk_drag_unhighlight (widget);
1192 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1193 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1196 site->have_drag = FALSE;
1201 gtk_drag_dest_motion (GtkWidget *widget,
1202 GdkDragContext *context,
1207 GtkDragDestSite *site;
1208 GdkDragAction action = 0;
1211 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1212 g_return_val_if_fail (site != NULL, FALSE);
1217 GdkEvent *current_event;
1218 GdkWindow *dest_window;
1219 GdkDragProtocol proto;
1221 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1223 if (!info->proxy_source)
1224 gtk_drag_proxy_begin (widget, info);
1226 current_event = gtk_get_current_event ();
1228 if (site->proxy_window)
1230 dest_window = site->proxy_window;
1231 proto = site->proxy_protocol;
1235 gdk_drag_find_window (info->proxy_source->context,
1237 current_event->dnd.x_root,
1238 current_event->dnd.y_root,
1239 &dest_window, &proto);
1242 gdk_drag_motion (info->proxy_source->context,
1244 current_event->dnd.x_root,
1245 current_event->dnd.y_root,
1246 context->suggested_action, time);
1248 selection = gdk_drag_get_selection (info->proxy_source->context);
1250 selection != gdk_drag_get_selection (info->context))
1251 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1253 gdk_event_free (current_event);
1258 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1260 if (context->suggested_action & site->actions)
1261 action = context->suggested_action;
1268 if ((site->actions & (1 << i)) &&
1269 (context->actions & (1 << i)))
1277 if (action && gtk_drag_dest_find_target (site, context))
1279 if (!site->have_drag)
1281 site->have_drag = TRUE;
1282 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1283 gtk_drag_highlight (widget);
1286 gdk_drag_status (context, action, time);
1290 gdk_drag_status (context, 0, time);
1295 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1296 context, x, y, time, &retval);
1298 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1302 gtk_drag_dest_drop (GtkWidget *widget,
1303 GdkDragContext *context,
1308 GtkDragDestSite *site;
1309 GtkDragDestInfo *info;
1311 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1312 g_return_val_if_fail (site != NULL, FALSE);
1314 info = g_dataset_get_data (context, "gtk-info");
1315 g_return_val_if_fail (info != NULL, FALSE);
1322 if (info->proxy_source ||
1323 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1325 gtk_drag_drop (info->proxy_source, time);
1329 /* We need to synthesize a motion event, wait for a status,
1330 * and, if we get one a good one, do a drop.
1333 GdkEvent *current_event;
1335 GdkWindow *dest_window;
1336 GdkDragProtocol proto;
1338 gtk_drag_proxy_begin (widget, info);
1339 info->proxy_drop_wait = TRUE;
1340 info->proxy_drop_time = time;
1342 current_event = gtk_get_current_event ();
1344 if (site->proxy_window)
1346 dest_window = site->proxy_window;
1347 proto = site->proxy_protocol;
1351 gdk_drag_find_window (info->proxy_source->context,
1353 current_event->dnd.x_root,
1354 current_event->dnd.y_root,
1355 &dest_window, &proto);
1358 gdk_drag_motion (info->proxy_source->context,
1360 current_event->dnd.x_root,
1361 current_event->dnd.y_root,
1362 context->suggested_action, time);
1364 selection = gdk_drag_get_selection (info->proxy_source->context);
1366 selection != gdk_drag_get_selection (info->context))
1367 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1369 gdk_event_free (current_event);
1379 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1381 GdkAtom target = gtk_drag_dest_find_target (site, context);
1383 if (target == GDK_NONE)
1386 gtk_drag_get_data (widget, context, target, time);
1389 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1390 context, x, y, time, &retval);
1392 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1400 /*************************************************************
1401 * gtk_drag_begin: Start a drag operation
1404 * widget: Widget from which drag starts
1405 * handlers: List of handlers to supply the data for the drag
1406 * button: Button user used to start drag
1407 * time: Time of event starting drag
1410 *************************************************************/
1413 gtk_drag_begin (GtkWidget *widget,
1414 GtkTargetList *target_list,
1415 GdkDragAction actions,
1419 GtkDragSourceInfo *info;
1420 GList *targets = NULL;
1422 guint32 time = GDK_CURRENT_TIME;
1424 g_return_val_if_fail (widget != NULL, NULL);
1425 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1426 g_return_val_if_fail (target_list != NULL, NULL);
1429 time = gdk_event_get_time (event);
1431 info = g_new0 (GtkDragSourceInfo, 1);
1432 info->ipc_widget = gtk_drag_get_ipc_widget ();
1434 tmp_list = g_list_last (target_list->list);
1437 GtkTargetPair *pair = tmp_list->data;
1438 targets = g_list_prepend (targets,
1439 GINT_TO_POINTER (pair->target));
1440 tmp_list = tmp_list->prev;
1443 info->widget = widget;
1444 gtk_widget_ref (info->widget);
1446 info->context = gdk_drag_begin (info->ipc_widget->window,
1448 g_list_free (targets);
1450 g_dataset_set_data (info->context, "gtk-info", info);
1452 info->button = button;
1453 info->target_list = target_list;
1454 gtk_target_list_ref (target_list);
1456 info->cursor = NULL;
1457 info->status = GTK_DRAG_STATUS_DRAG;
1458 info->last_event = NULL;
1459 info->selections = NULL;
1460 info->icon_window = NULL;
1463 info->cursor = gtk_drag_get_cursor (
1464 gtk_drag_get_event_action (event, info->button, actions));
1466 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1469 /* We use a GTK grab here to override any grabs that the widget
1470 * we are dragging from might have held
1473 gtk_grab_add (info->ipc_widget);
1474 gdk_pointer_grab (info->ipc_widget->window, FALSE,
1475 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1476 GDK_BUTTON_RELEASE_MASK, NULL,
1477 info->cursor, time);
1479 if (event->type == GDK_MOTION_NOTIFY)
1480 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1484 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x, &y, NULL);
1489 if (info->icon_window)
1491 gdk_window_raise (info->icon_window->window);
1492 gtk_widget_set_uposition (info->icon_window, x - info->hot_x, y - info->hot_y);
1496 info->start_x = info->cur_x;
1497 info->start_y = info->cur_y;
1499 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1500 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1501 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1502 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1503 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1504 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1506 return info->context;
1509 /*************************************************************
1510 * gtk_drag_source_set:
1511 * Register a drop site, and possibly add default behaviors.
1514 * start_button_mask: Mask of allowed buttons to start drag
1515 * targets: Table of targets for this source
1517 * actions: Actions allowed for this source
1519 *************************************************************/
1522 gtk_drag_source_set (GtkWidget *widget,
1523 GdkModifierType start_button_mask,
1524 GtkTargetEntry *targets,
1526 GdkDragAction actions)
1528 GtkDragSourceSite *site;
1530 g_return_if_fail (widget != NULL);
1532 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1534 gtk_widget_add_events (widget,
1535 gtk_widget_get_events (widget) |
1536 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1537 GDK_BUTTON_MOTION_MASK);
1541 if (site->target_list)
1542 gtk_target_list_unref (site->target_list);
1546 site = g_new0 (GtkDragSourceSite, 1);
1548 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1549 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1551 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1552 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1555 gtk_object_set_data_full (GTK_OBJECT (widget),
1557 site, gtk_drag_source_site_destroy);
1560 site->start_button_mask = start_button_mask;
1563 site->target_list = gtk_target_list_new (targets, n_targets);
1565 site->target_list = NULL;
1567 site->actions = actions;
1571 /*************************************************************
1572 * gtk_drag_source_set_icon:
1573 * Set an icon for drags from this source.
1575 * colormap: Colormap for this icon
1579 *************************************************************/
1582 gtk_drag_source_set_icon (GtkWidget *widget,
1583 GdkColormap *colormap,
1587 GtkDragSourceSite *site;
1589 g_return_if_fail (widget != NULL);
1591 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1592 g_return_if_fail (site != NULL);
1595 gdk_colormap_unref (site->colormap);
1597 gdk_pixmap_unref (site->pixmap);
1599 gdk_pixmap_unref (site->mask);
1601 site->colormap = colormap;
1603 gdk_colormap_ref (colormap);
1605 site->pixmap = pixmap;
1607 gdk_pixmap_ref (pixmap);
1611 gdk_pixmap_ref (mask);
1614 /*************************************************************
1615 * gtk_drag_set_icon_widget:
1616 * Set a widget as the icon for a drag.
1623 *************************************************************/
1626 gtk_drag_set_icon_widget (GdkDragContext *context,
1631 GtkDragSourceInfo *info;
1633 g_return_if_fail (context != NULL);
1634 g_return_if_fail (widget != NULL);
1636 info = g_dataset_get_data (context, "gtk-info");
1637 gtk_drag_remove_icon (info);
1639 info->icon_window = widget;
1642 gtk_widget_set_uposition (widget, info->cur_x, info->cur_y);
1643 gtk_widget_ref (widget);
1644 gdk_window_raise (widget->window);
1645 gtk_widget_show (widget);
1648 info->hot_x = hot_x;
1649 info->hot_y = hot_y;
1652 /*************************************************************
1653 * gtk_drag_set_icon_pixmap:
1654 * Set a widget as the icon for a drag.
1657 * colormap: Colormap for the icon window.
1663 *************************************************************/
1666 gtk_drag_set_icon_pixmap (GdkDragContext *context,
1667 GdkColormap *colormap,
1676 g_return_if_fail (context != NULL);
1677 g_return_if_fail (colormap != NULL);
1678 g_return_if_fail (pixmap != NULL);
1680 gdk_window_get_size (pixmap, &width, &height);
1682 gtk_widget_push_visual (gdk_colormap_get_visual(colormap));
1683 gtk_widget_push_colormap (colormap);
1685 window = gtk_draw_window_new (GTK_WINDOW_POPUP);
1687 gtk_widget_pop_visual ();
1688 gtk_widget_pop_colormap ();
1690 gtk_widget_set_usize (window, width, height);
1691 gtk_widget_realize (window);
1693 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1696 gtk_widget_shape_combine_mask (window, mask, 0, 0);
1698 gtk_drag_set_icon_widget (context, window, hot_x, hot_y);
1701 /*************************************************************
1702 * gtk_drag_set_icon_default:
1703 * Set the icon for a drag to the default icon.
1707 *************************************************************/
1710 gtk_drag_set_icon_default (GdkDragContext *context)
1712 g_return_if_fail (context != NULL);
1714 if (!default_icon_pixmap)
1716 default_icon_colormap = gdk_colormap_get_system ();
1717 default_icon_pixmap =
1718 gdk_pixmap_colormap_create_from_xpm_d (NULL,
1719 default_icon_colormap,
1721 NULL, drag_default_xpm);
1722 default_icon_hot_x = -2;
1723 default_icon_hot_y = -2;
1726 gtk_drag_set_icon_pixmap (context,
1727 default_icon_colormap,
1728 default_icon_pixmap,
1731 default_icon_hot_y);
1734 /*************************************************************
1735 * gtk_drag_set_default_icon:
1736 * Set a default icon for all drags as a pixmap.
1738 * colormap: Colormap for the icon window.
1744 *************************************************************/
1747 gtk_drag_set_default_icon (GdkColormap *colormap,
1753 g_return_if_fail (colormap != NULL);
1754 g_return_if_fail (pixmap != NULL);
1756 if (default_icon_colormap)
1757 gdk_colormap_unref (default_icon_colormap);
1758 if (default_icon_pixmap)
1759 gdk_pixmap_unref (default_icon_pixmap);
1760 if (default_icon_mask)
1761 gdk_pixmap_unref (default_icon_pixmap);
1763 default_icon_colormap = colormap;
1764 gdk_colormap_ref (colormap);
1766 default_icon_pixmap = pixmap;
1767 gdk_pixmap_ref (pixmap);
1769 default_icon_mask = mask;
1771 gdk_pixmap_ref (mask);
1773 default_icon_hot_x = hot_x;
1774 default_icon_hot_y = hot_y;
1778 /*************************************************************
1779 * gtk_drag_source_handle_event:
1780 * Called from widget event handling code on Drag events
1784 * toplevel: Toplevel widget that received the event
1787 *************************************************************/
1790 gtk_drag_source_handle_event (GtkWidget *widget,
1793 GtkDragSourceInfo *info;
1794 GdkDragContext *context;
1796 g_return_if_fail (widget != NULL);
1797 g_return_if_fail (event != NULL);
1799 context = event->dnd.context;
1800 info = g_dataset_get_data (context, "gtk-info");
1804 switch (event->type)
1806 case GDK_DRAG_STATUS:
1810 if (info->proxy_dest)
1812 if (!event->dnd.send_event)
1814 if (info->proxy_dest->proxy_drop_wait)
1816 /* Aha - we can finally pass the MOTIF DROP on... */
1817 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
1821 gdk_drag_status (info->proxy_dest->context,
1822 event->dnd.context->action,
1829 cursor = gtk_drag_get_cursor (event->dnd.context->action);
1830 if (info->cursor != cursor)
1832 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window),
1833 PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
1834 ((GdkCursorPrivate *)cursor)->xcursor,
1836 info->cursor = cursor;
1839 if (info->last_event)
1841 gtk_drag_motion_cb (info->widget,
1842 (GdkEventMotion *)info->last_event,
1844 info->last_event = NULL;
1850 case GDK_DROP_FINISHED:
1851 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
1854 g_assert_not_reached();
1858 /*************************************************************
1859 * gtk_drag_source_check_selection:
1860 * Check if we've set up handlers/claimed the selection
1861 * for a given drag. If not, add them.
1865 *************************************************************/
1868 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
1874 tmp_list = info->selections;
1877 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
1879 tmp_list = tmp_list->next;
1882 gtk_selection_owner_set (info->ipc_widget, selection, time);
1883 info->selections = g_list_prepend (info->selections,
1884 GUINT_TO_POINTER (selection));
1886 tmp_list = info->target_list->list;
1889 GtkTargetPair *pair = tmp_list->data;
1891 gtk_selection_add_target (info->ipc_widget,
1895 tmp_list = tmp_list->next;
1898 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
1900 gtk_selection_add_target (info->ipc_widget,
1902 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
1903 TARGET_MOTIF_SUCCESS);
1904 gtk_selection_add_target (info->ipc_widget,
1906 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
1907 TARGET_MOTIF_FAILURE);
1910 gtk_selection_add_target (info->ipc_widget,
1912 gdk_atom_intern ("DELETE", FALSE),
1916 /*************************************************************
1917 * gtk_drag_drop_finished:
1918 * Clean up from the drag, and display snapback, if necessary.
1924 *************************************************************/
1927 gtk_drag_drop_finished (GtkDragSourceInfo *info,
1931 if (info->proxy_dest)
1933 /* The time from the event isn't reliable for Xdnd drags */
1934 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
1935 info->proxy_dest->proxy_drop_time);
1941 gtk_drag_source_info_destroy (info);
1945 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
1949 anim->n_steps = MAX (info->cur_x - info->start_x,
1950 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
1951 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
1953 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
1957 gtk_drag_source_release_selections (info, GDK_CURRENT_TIME); /* fixme */
1961 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
1964 GList *tmp_list = info->selections;
1967 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
1968 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
1969 gtk_selection_owner_set (NULL, selection, time);
1970 tmp_list = tmp_list->next;
1973 g_list_free (info->selections);
1974 info->selections = NULL;
1977 /*************************************************************
1979 * Send a drop event.
1983 *************************************************************/
1986 gtk_drag_drop (GtkDragSourceInfo *info, guint32 time)
1988 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
1990 GtkSelectionData selection_data;
1992 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
1994 tmp_list = info->target_list->list;
1997 GtkTargetPair *pair = tmp_list->data;
1999 if (pair->target == target)
2001 selection_data.selection = GDK_NONE;
2002 selection_data.target = target;
2003 selection_data.data = NULL;
2004 selection_data.length = -1;
2006 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2007 info->context, &selection_data,
2011 /* FIXME: Should we check for length >= 0 here? */
2012 gtk_drag_drop_finished (info, TRUE, time);
2015 tmp_list = tmp_list->next;
2017 gtk_drag_drop_finished (info, FALSE, time);
2021 gdk_drag_drop (info->context, time);
2022 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2023 gtk_drag_abort_timeout,
2029 * Source side callbacks.
2033 gtk_drag_source_event_cb (GtkWidget *widget,
2037 GtkDragSourceSite *site;
2038 site = (GtkDragSourceSite *)data;
2040 switch (event->type)
2042 case GDK_BUTTON_PRESS:
2043 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2045 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2046 site->x = event->button.x;
2047 site->y = event->button.y;
2051 case GDK_BUTTON_RELEASE:
2052 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2054 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2058 case GDK_MOTION_NOTIFY:
2059 if (site->state & event->motion.state & site->start_button_mask)
2061 /* FIXME: This is really broken and can leave us
2067 if (site->state & event->motion.state &
2068 GDK_BUTTON1_MASK << (i - 1))
2072 if (MAX (abs(site->x - event->motion.x),
2073 abs(site->y - event->motion.y)) > 3)
2075 GtkDragSourceInfo *info;
2076 GdkDragContext *context;
2079 context = gtk_drag_begin (widget, site->target_list,
2084 info = g_dataset_get_data (context, "gtk-info");
2086 if (!info->icon_window)
2089 gtk_drag_set_icon_pixmap (context,
2092 site->mask, -2, -2);
2094 gtk_drag_set_icon_default (context);
2102 default: /* hit for 2/3BUTTON_PRESS */
2109 gtk_drag_source_site_destroy (gpointer data)
2111 GtkDragSourceSite *site = data;
2113 if (site->target_list)
2114 gtk_target_list_unref (site->target_list);
2117 gdk_pixmap_unref (site->pixmap);
2120 gdk_pixmap_unref (site->mask);
2126 gtk_drag_selection_get (GtkWidget *widget,
2127 GtkSelectionData *selection_data,
2132 GtkDragSourceInfo *info = data;
2133 static GdkAtom null_atom = GDK_NONE;
2137 null_atom = gdk_atom_intern ("NULL", FALSE);
2142 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2145 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2147 case TARGET_MOTIF_SUCCESS:
2148 gtk_drag_drop_finished (info, TRUE, time);
2149 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2151 case TARGET_MOTIF_FAILURE:
2152 gtk_drag_drop_finished (info, FALSE, time);
2153 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2156 if (info->proxy_dest)
2158 /* This is sort of dangerous and needs to be thought
2161 info->proxy_dest->proxy_data = selection_data;
2162 gtk_drag_get_data (info->widget,
2163 info->proxy_dest->context,
2164 selection_data->target,
2167 info->proxy_dest->proxy_data = NULL;
2171 if (gtk_target_list_find (info->target_list,
2172 selection_data->target,
2175 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2187 gtk_drag_anim_timeout (gpointer data)
2189 GtkDragAnim *anim = data;
2192 if (anim->step == anim->n_steps)
2194 gtk_drag_source_info_destroy (anim->info);
2201 x = (anim->info->start_x * (anim->step + 1) +
2202 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2203 y = (anim->info->start_y * (anim->step + 1) +
2204 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2205 if (anim->info->icon_window)
2206 gtk_widget_set_uposition (anim->info->icon_window, x, y);
2215 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2217 if (info->icon_window)
2219 gtk_widget_hide (info->icon_window);
2220 gtk_widget_unref (info->icon_window);
2222 info->icon_window = NULL;
2227 gtk_drag_source_info_destroy (gpointer data)
2229 GtkDragSourceInfo *info = data;
2231 gtk_drag_remove_icon (data);
2233 if (!info->proxy_dest)
2234 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2238 gtk_widget_unref (info->widget);
2240 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2241 gtk_selection_remove_all (info->ipc_widget);
2242 gtk_drag_release_ipc_widget (info->ipc_widget);
2244 gtk_target_list_unref (info->target_list);
2246 g_dataset_set_data (info->context, "gtk-info", NULL);
2247 gdk_drag_context_unref (info->context);
2249 if (info->drop_timeout)
2250 gtk_timeout_remove (info->drop_timeout);
2255 /*************************************************************
2256 * gtk_drag_motion_cb:
2257 * "motion_notify_event" callback during drag.
2261 *************************************************************/
2264 gtk_drag_motion_cb (GtkWidget *widget,
2265 GdkEventMotion *event,
2268 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2270 GdkDragAction action;
2271 GdkWindow *window = NULL;
2272 GdkWindow *dest_window;
2273 GdkDragProtocol protocol;
2274 gint x_root, y_root;
2278 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2279 event->x_root = x_root;
2280 event->y_root = y_root;
2283 action = gtk_drag_get_event_action ((GdkEvent *)event,
2285 info->context->actions);
2287 info->cur_x = event->x_root - info->hot_x;
2288 info->cur_y = event->y_root - info->hot_y;
2290 if (info->icon_window)
2292 gdk_window_raise (info->icon_window->window);
2293 gtk_widget_set_uposition (info->icon_window, info->cur_x, info->cur_y);
2294 window = info->icon_window->window;
2297 gdk_drag_find_window (info->context,
2298 window, event->x_root, event->y_root,
2299 &dest_window, &protocol);
2301 if (gdk_drag_motion (info->context, dest_window, protocol,
2302 event->x_root, event->y_root, action,
2305 if (info->last_event)
2306 gdk_event_free ((GdkEvent *)info->last_event);
2308 info->last_event = gdk_event_copy ((GdkEvent *)event);
2312 gdk_window_unref (dest_window);
2314 selection = gdk_drag_get_selection (info->context);
2316 gtk_drag_source_check_selection (info, selection, event->time);
2319 /* We ignore the response, so we can respond precisely to the drop
2322 gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2328 /*************************************************************
2329 * gtk_drag_motion_cb:
2330 * "button_release_event" callback during drag.
2334 *************************************************************/
2337 gtk_drag_button_release_cb (GtkWidget *widget,
2338 GdkEventButton *event,
2341 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2342 GtkWidget *source_widget = info->widget;
2343 GdkEvent send_event;
2345 gtk_widget_ref (source_widget);
2347 if (event->button != info->button)
2350 gdk_pointer_ungrab (event->time);
2352 if ((info->context->action != 0) && (info->context->dest_window != NULL))
2354 gtk_drag_drop (info, event->time);
2358 gdk_drag_abort (info->context, event->time);
2359 gtk_drag_drop_finished (info, FALSE, event->time);
2362 gtk_grab_remove (widget);
2364 send_event.button.type = GDK_BUTTON_RELEASE;
2365 send_event.button.window = source_widget->window;
2366 send_event.button.x = 0;
2367 send_event.button.y = 0;
2368 send_event.button.state = event->state;
2369 send_event.button.button = event->button;
2371 send_event.button.time = event->time;
2373 /* Send on the button release to the original widget to
2374 * convince it to release its grab
2376 gtk_widget_event (source_widget, &send_event);
2377 gtk_widget_unref (source_widget);
2383 gtk_drag_abort_timeout (gpointer data)
2385 GtkDragSourceInfo *info = data;
2386 guint32 time = GDK_CURRENT_TIME;
2388 if (info->proxy_dest)
2389 time = info->proxy_dest->proxy_drop_time;
2391 info->drop_timeout = 0;
2392 gtk_drag_drop_finished (info, FALSE, time);