1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
23 #include "gtkinvisible.h"
25 #include "gtksignal.h"
26 #include "gtkwindow.h"
28 static GSList *drag_widgets = NULL;
29 static GSList *source_widgets = NULL;
31 typedef struct _GtkDragSourceSite GtkDragSourceSite;
32 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
33 typedef struct _GtkDragDestSite GtkDragDestSite;
34 typedef struct _GtkDragDestInfo GtkDragDestInfo;
35 typedef struct _GtkDragAnim GtkDragAnim;
36 typedef struct _GtkDragFindData GtkDragFindData;
46 struct _GtkDragSourceSite
48 GdkModifierType start_button_mask;
49 GtkTargetList *target_list; /* Targets for drag data */
50 GdkDragAction actions; /* Possible actions */
51 GdkColormap *colormap; /* Colormap for drag icon */
52 GdkPixmap *pixmap; /* Icon for drag data */
55 /* Stored button press information to detect drag beginning */
60 struct _GtkDragSourceInfo
63 GtkTargetList *target_list; /* Targets for drag data */
64 GdkDragAction possible_actions; /* Actions allowed by source */
65 GdkDragContext *context; /* drag context */
66 GtkWidget *icon_window; /* Window for drag */
67 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
68 GdkCursor *cursor; /* Cursor for drag */
69 gint hot_x, hot_y; /* Hot spot for drag */
70 gint button; /* mouse button starting drag */
72 GtkDragStatus status; /* drag status */
73 GdkEvent *last_event; /* motion event waiting for response */
75 gint start_x, start_y; /* Initial position */
76 gint cur_x, cur_y; /* Current Position */
78 GList *selections; /* selections we've claimed */
80 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
82 guint drop_timeout; /* Timeout for aborting drop */
83 guint destroy_icon : 1; /* If true, destroy icon_window
87 struct _GtkDragDestSite
89 GtkDestDefaults flags;
90 GtkTargetList *target_list;
91 GdkDragAction actions;
92 GdkWindow *proxy_window;
93 GdkDragProtocol proxy_protocol;
94 gboolean do_proxy : 1;
95 gboolean proxy_coords : 1;
96 gboolean have_drag : 1;
99 struct _GtkDragDestInfo
101 GtkWidget *widget; /* Widget in which drag is in */
102 GdkDragContext *context; /* Drag context */
103 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
104 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
105 gboolean dropped : 1; /* Set after we receive a drop */
106 guint32 proxy_drop_time; /* Timestamp for proxied drop */
107 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
108 * status reply before sending
111 gint drop_x, drop_y; /* Position of drop */
114 #define DROP_ABORT_TIME 300000
116 #define ANIM_STEP_TIME 50
117 #define ANIM_STEP_LENGTH 50
118 #define ANIM_MIN_STEPS 5
119 #define ANIM_MAX_STEPS 10
123 GtkDragSourceInfo *info;
128 struct _GtkDragFindData
132 GdkDragContext *context;
133 GtkDragDestInfo *info;
136 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
137 gint x, gint y, guint32 time);
141 /* Enumeration for some targets we handle internally */
144 TARGET_MOTIF_SUCCESS = 0x40000000,
145 TARGET_MOTIF_FAILURE,
151 static GdkPixmap *default_icon_pixmap = NULL;
152 static GdkPixmap *default_icon_mask = NULL;
153 static GdkColormap *default_icon_colormap = NULL;
154 static gint default_icon_hot_x;
155 static gint default_icon_hot_y;
157 /* Forward declarations */
158 static void gtk_drag_get_event_actions (GdkEvent *event,
160 GdkDragAction actions,
161 GdkDragAction *suggested_action,
162 GdkDragAction *possible_actions);
163 static GdkCursor * gtk_drag_get_cursor (GdkDragAction action);
164 static GtkWidget *gtk_drag_get_ipc_widget (void);
165 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
167 static void gtk_drag_highlight_paint (GtkWidget *widget);
168 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
169 GdkEventExpose *event,
173 static GdkAtom gtk_drag_dest_find_target (GtkWidget *widget,
174 GtkDragDestSite *site,
175 GdkDragContext *context);
176 static void gtk_drag_selection_received (GtkWidget *widget,
177 GtkSelectionData *selection_data,
180 static void gtk_drag_find_widget (GtkWidget *widget,
181 GtkDragFindData *data);
182 static void gtk_drag_proxy_begin (GtkWidget *widget,
183 GtkDragDestInfo *dest_info);
184 static void gtk_drag_dest_info_destroy (gpointer data);
185 static void gtk_drag_dest_realized (GtkWidget *widget);
186 static void gtk_drag_dest_site_destroy (gpointer data);
187 static void gtk_drag_dest_leave (GtkWidget *widget,
188 GdkDragContext *context,
190 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
191 GdkDragContext *context,
195 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
196 GdkDragContext *context,
201 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
204 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
206 static void gtk_drag_drop (GtkDragSourceInfo *info,
208 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
212 static gint gtk_drag_source_event_cb (GtkWidget *widget,
215 static void gtk_drag_source_site_destroy (gpointer data);
216 static void gtk_drag_selection_get (GtkWidget *widget,
217 GtkSelectionData *selection_data,
221 static gint gtk_drag_anim_timeout (gpointer data);
222 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
223 static void gtk_drag_source_info_destroy (gpointer data);
224 static gint gtk_drag_motion_cb (GtkWidget *widget,
225 GdkEventMotion *event,
227 static gint gtk_drag_button_release_cb (GtkWidget *widget,
228 GdkEventButton *event,
230 static gint gtk_drag_abort_timeout (gpointer data);
232 /************************
233 * Cursor and Icon data *
234 ************************/
236 #define action_ask_width 16
237 #define action_ask_height 16
238 static const char action_ask_bits[] = {
239 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
240 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
241 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
243 #define action_ask_mask_width 16
244 #define action_ask_mask_height 16
245 static const char action_ask_mask_bits[] = {
246 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
247 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
248 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
250 #define action_copy_width 16
251 #define action_copy_height 16
252 static const char action_copy_bits[] = {
253 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
254 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
255 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
257 #define action_copy_mask_width 16
258 #define action_copy_mask_height 16
259 static const char action_copy_mask_bits[] = {
260 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
261 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
262 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
264 #define action_move_width 16
265 #define action_move_height 16
266 static const char action_move_bits[] = {
267 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
268 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
269 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
271 #define action_move_mask_width 16
272 #define action_move_mask_height 16
273 static const char action_move_mask_bits[] = {
274 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
275 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
276 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
278 #define action_link_width 16
279 #define action_link_height 16
280 static const char action_link_bits[] = {
281 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
282 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
283 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
285 #define action_link_mask_width 16
286 #define action_link_mask_height 16
287 static const char action_link_mask_bits[] = {
288 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
289 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
290 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
292 #define action_none_width 16
293 #define action_none_height 16
294 static const char action_none_bits[] = {
295 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
296 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
297 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
299 #define action_none_mask_width 16
300 #define action_none_mask_height 16
301 static const char action_none_mask_bits[] = {
302 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
303 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
304 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
306 #define CURSOR_WIDTH 16
307 #define CURSOR_HEIGHT 16
310 GdkDragAction action;
315 { GDK_ACTION_DEFAULT, 0 },
316 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
317 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
318 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
319 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
320 { 0 , action_none_bits, action_none_mask_bits, NULL },
323 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
326 static const char * drag_default_xpm[] = {
339 " ...+++++++++++.. ",
340 " ..+.++++++++++++.. ",
341 " .++.++++++++++++.. ",
342 " .+++.++++++++++++.. ",
343 " .++++.++++++++++++. ",
344 " .+++.+++++++++++++.. ",
345 " .++.+++++++++++++++.. ",
346 " .+.+++++++++++++++++.. ",
347 " ..+++++++++++++++++++.. ",
348 " ..++++++++++++++++++++. ",
349 " .++++++++++++++++++++.. ",
350 " ..+++++++++++++++++.. ",
351 " .++++++++++++++++.. ",
352 " ..+++++++++++++... ",
364 /*********************
365 * Utility functions *
366 *********************/
368 /*************************************************************
369 * gtk_drag_get_ipc_widget:
370 * Return a invisible, off-screen, override-redirect
375 *************************************************************/
378 gtk_drag_get_ipc_widget (void)
384 GSList *tmp = drag_widgets;
385 result = drag_widgets->data;
386 drag_widgets = drag_widgets->next;
387 g_slist_free_1 (tmp);
391 result = gtk_invisible_new ();
392 gtk_widget_show (result);
398 /***************************************************************
399 * gtk_drag_release_ipc_widget:
400 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
402 * widget: the widget to release.
404 ***************************************************************/
407 gtk_drag_release_ipc_widget (GtkWidget *widget)
409 drag_widgets = g_slist_prepend (drag_widgets, widget);
413 gtk_drag_get_event_actions (GdkEvent *event,
415 GdkDragAction actions,
416 GdkDragAction *suggested_action,
417 GdkDragAction *possible_actions)
419 *suggested_action = 0;
420 *possible_actions = 0;
424 GdkModifierType state = 0;
428 case GDK_MOTION_NOTIFY:
429 state = event->motion.state;
431 case GDK_BUTTON_PRESS:
432 case GDK_2BUTTON_PRESS:
433 case GDK_3BUTTON_PRESS:
434 case GDK_BUTTON_RELEASE:
435 state = event->button.state;
438 case GDK_KEY_RELEASE:
439 state = event->key.state;
441 case GDK_ENTER_NOTIFY:
442 case GDK_LEAVE_NOTIFY:
443 state = event->crossing.state;
449 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
451 *suggested_action = GDK_ACTION_ASK;
452 *possible_actions = actions;
454 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
456 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
458 if (actions & GDK_ACTION_LINK)
460 *suggested_action = GDK_ACTION_LINK;
461 *possible_actions = GDK_ACTION_LINK;
464 else if (state & GDK_CONTROL_MASK)
466 if (actions & GDK_ACTION_COPY)
468 *suggested_action = GDK_ACTION_COPY;
469 *possible_actions = GDK_ACTION_COPY;
475 if (actions & GDK_ACTION_MOVE)
477 *suggested_action = GDK_ACTION_MOVE;
478 *possible_actions = GDK_ACTION_MOVE;
485 *possible_actions = actions;
487 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
488 *suggested_action = GDK_ACTION_ASK;
489 else if (actions & GDK_ACTION_COPY)
490 *suggested_action = GDK_ACTION_COPY;
491 else if (actions & GDK_ACTION_MOVE)
492 *suggested_action = GDK_ACTION_MOVE;
493 else if (actions & GDK_ACTION_LINK)
494 *suggested_action = GDK_ACTION_LINK;
502 gtk_drag_get_cursor (GdkDragAction action)
506 for (i = 0 ; i < n_drag_cursors - 1; i++)
507 if (drag_cursors[i].action == action)
510 if (drag_cursors[i].cursor == NULL)
515 gdk_bitmap_create_from_data (NULL,
516 drag_cursors[i].bits,
517 CURSOR_WIDTH, CURSOR_HEIGHT);
519 gdk_bitmap_create_from_data (NULL,
520 drag_cursors[i].mask,
521 CURSOR_WIDTH, CURSOR_HEIGHT);
523 gdk_color_white (gdk_colormap_get_system (), &bg);
524 gdk_color_black (gdk_colormap_get_system (), &fg);
526 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
528 gdk_pixmap_unref (pixmap);
529 gdk_pixmap_unref (mask);
532 return drag_cursors[i].cursor;
535 /********************
537 ********************/
539 /*************************************************************
541 * Get the data for a drag or drop
543 * context - drag context
544 * target - format to retrieve the data in.
545 * time - timestamp of triggering event.
548 *************************************************************/
551 gtk_drag_get_data (GtkWidget *widget,
552 GdkDragContext *context,
556 GtkWidget *selection_widget;
558 g_return_if_fail (widget != NULL);
559 g_return_if_fail (context != NULL);
561 selection_widget = gtk_drag_get_ipc_widget ();
563 gdk_drag_context_ref (context);
564 gtk_widget_ref (widget);
566 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
567 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
569 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
571 gtk_selection_convert (selection_widget,
572 gdk_drag_get_selection (context),
578 /*************************************************************
579 * gtk_drag_get_source_widget:
580 * Get the widget the was the source of this drag, if
581 * the drag originated from this application.
583 * context: The drag context for this drag
585 * The source widget, or NULL if the drag originated from
586 * a different application.
587 *************************************************************/
590 gtk_drag_get_source_widget (GdkDragContext *context)
594 tmp_list = source_widgets;
597 GtkWidget *ipc_widget = tmp_list->data;
599 if (ipc_widget->window == context->source_window)
601 GtkDragSourceInfo *info;
602 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
604 return info ? info->widget : NULL;
607 tmp_list = tmp_list->next;
613 /*************************************************************
615 * Notify the drag source that the transfer of data
618 * context: The drag context for this drag
619 * success: Was the data successfully transferred?
620 * time: The timestamp to use when notifying the destination.
622 *************************************************************/
625 gtk_drag_finish (GdkDragContext *context,
630 GdkAtom target = GDK_NONE;
632 g_return_if_fail (context != NULL);
636 target = gdk_atom_intern ("DELETE", FALSE);
638 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
640 target = gdk_atom_intern (success ?
641 "XmTRANSFER_SUCCESS" :
642 "XmTRANSFER_FAILURE",
646 if (target != GDK_NONE)
648 GtkWidget *selection_widget = gtk_drag_get_ipc_widget ();
650 gdk_drag_context_ref (context);
652 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
653 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
654 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
657 gtk_selection_convert (selection_widget,
658 gdk_drag_get_selection (context),
664 gdk_drop_finish (context, success, time);
667 /*************************************************************
668 * gtk_drag_highlight_paint:
669 * Paint a highlight indicating drag status onto the widget.
673 *************************************************************/
676 gtk_drag_highlight_paint (GtkWidget *widget)
678 gint x, y, width, height;
680 g_return_if_fail (widget != NULL);
682 if (GTK_WIDGET_DRAWABLE (widget))
684 if (GTK_WIDGET_NO_WINDOW (widget))
686 x = widget->allocation.x;
687 y = widget->allocation.y;
688 width = widget->allocation.width;
689 height = widget->allocation.height;
695 gdk_window_get_size (widget->window, &width, &height);
698 gtk_draw_shadow (widget->style, widget->window,
699 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
700 x, y, width, height);
702 gdk_draw_rectangle (widget->window,
703 widget->style->black_gc,
705 x, y, width - 1, height - 1);
709 /*************************************************************
710 * gtk_drag_highlight_expose:
711 * Callback for expose_event for highlighted widgets.
717 *************************************************************/
720 gtk_drag_highlight_expose (GtkWidget *widget,
721 GdkEventExpose *event,
724 gtk_drag_highlight_paint (widget);
728 /*************************************************************
729 * gtk_drag_highlight:
730 * Highlight the given widget in the default manner.
734 *************************************************************/
737 gtk_drag_highlight (GtkWidget *widget)
739 gtk_signal_connect_after (GTK_OBJECT (widget), "draw",
740 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
742 gtk_signal_connect (GTK_OBJECT (widget), "expose_event",
743 GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
746 gtk_widget_queue_draw (widget);
749 /*************************************************************
750 * gtk_drag_unhighlight:
751 * Refresh the given widget to remove the highlight.
755 *************************************************************/
758 gtk_drag_unhighlight (GtkWidget *widget)
760 g_return_if_fail (widget != NULL);
762 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
763 GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
765 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
766 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
769 gtk_widget_queue_clear (widget);
772 /*************************************************************
774 * Register a drop site, and possibly add default behaviors.
777 * flags: Which types of default drag behavior to use
778 * targets: Table of targets that can be accepted
779 * n_targets: Number of of entries in targets
782 *************************************************************/
785 gtk_drag_dest_set (GtkWidget *widget,
786 GtkDestDefaults flags,
787 const GtkTargetEntry *targets,
789 GdkDragAction actions)
791 GtkDragDestSite *site;
793 g_return_if_fail (widget != NULL);
795 /* HACK, do this in the destroy */
796 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
798 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
800 if (GTK_WIDGET_REALIZED (widget))
801 gtk_drag_dest_realized (widget);
803 gtk_signal_connect (GTK_OBJECT (widget), "realize",
804 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
806 site = g_new (GtkDragDestSite, 1);
809 site->have_drag = FALSE;
811 site->target_list = gtk_target_list_new (targets, n_targets);
813 site->target_list = NULL;
815 site->actions = actions;
816 site->do_proxy = FALSE;
818 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
819 site, gtk_drag_dest_site_destroy);
822 /*************************************************************
823 * gtk_drag_dest_set_proxy:
824 * Set up this widget to proxy drags elsewhere.
827 * proxy_window: window to which forward drag events
828 * protocol: Drag protocol which the dest widget accepts
829 * use_coordinates: If true, send the same coordinates to the
830 * destination, because it is a embedded
833 *************************************************************/
836 gtk_drag_dest_set_proxy (GtkWidget *widget,
837 GdkWindow *proxy_window,
838 GdkDragProtocol protocol,
839 gboolean use_coordinates)
841 GtkDragDestSite *site;
843 g_return_if_fail (widget != NULL);
845 /* HACK, do this in the destroy */
846 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
848 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
850 if (GTK_WIDGET_REALIZED (widget))
851 gtk_drag_dest_realized (widget);
853 gtk_signal_connect (GTK_OBJECT (widget), "realize",
854 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
856 site = g_new (GtkDragDestSite, 1);
859 site->have_drag = FALSE;
860 site->target_list = NULL;
862 site->proxy_window = proxy_window;
864 gdk_window_ref (proxy_window);
865 site->do_proxy = TRUE;
866 site->proxy_protocol = protocol;
867 site->proxy_coords = use_coordinates;
869 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
870 site, gtk_drag_dest_site_destroy);
873 /*************************************************************
874 * gtk_drag_dest_unset
875 * Unregister this widget as a drag target.
879 *************************************************************/
882 gtk_drag_dest_unset (GtkWidget *widget)
884 g_return_if_fail (widget != NULL);
886 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
889 /*************************************************************
890 * gtk_drag_dest_handle_event:
891 * Called from widget event handling code on Drag events
895 * toplevel: Toplevel widget that received the event
898 *************************************************************/
901 gtk_drag_dest_handle_event (GtkWidget *toplevel,
904 GtkDragDestInfo *info;
905 GdkDragContext *context;
907 g_return_if_fail (toplevel != NULL);
908 g_return_if_fail (event != NULL);
910 context = event->dnd.context;
912 info = g_dataset_get_data (context, "gtk-info");
915 info = g_new (GtkDragDestInfo, 1);
917 info->context = event->dnd.context;
918 info->proxy_source = NULL;
919 info->proxy_data = NULL;
920 info->dropped = FALSE;
921 info->proxy_drop_wait = FALSE;
922 g_dataset_set_data_full (context,
925 gtk_drag_dest_info_destroy);
928 /* Find the widget for the event */
937 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
942 case GDK_DRAG_MOTION:
945 GtkDragFindData data;
948 if (event->type == GDK_DROP_START)
949 info->dropped = TRUE;
951 gdk_window_get_origin (toplevel->window, &tx, &ty);
953 data.x = event->dnd.x_root - tx;
954 data.y = event->dnd.y_root - ty;
955 data.context = context;
958 data.toplevel = TRUE;
959 data.callback = (event->type == GDK_DRAG_MOTION) ?
960 gtk_drag_dest_motion : gtk_drag_dest_drop;
961 data.time = event->dnd.time;
963 gtk_drag_find_widget (toplevel, &data);
965 /* We send a leave here so that the widget unhighlights
969 ((event->type == GDK_DROP_START) || (!data.found)))
971 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
977 if (event->type == GDK_DRAG_MOTION)
980 gdk_drag_status (context, 0, event->dnd.time);
982 else if (event->type == GDK_DROP_START)
984 gdk_drop_reply (context, data.found, event->dnd.time);
985 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
986 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
992 g_assert_not_reached ();
996 /*************************************************************
997 * gtk_drag_dest_find_target:
998 * Decide on a target for the drag.
1003 *************************************************************/
1006 gtk_drag_dest_find_target (GtkWidget *widget,
1007 GtkDragDestSite *site,
1008 GdkDragContext *context)
1011 GList *tmp_source = NULL;
1012 GtkWidget *source_widget = gtk_drag_get_source_widget (context);
1014 tmp_target = site->target_list->list;
1017 GtkTargetPair *pair = tmp_target->data;
1018 tmp_source = context->targets;
1021 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1023 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1024 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1025 return pair->target;
1029 tmp_source = tmp_source->next;
1031 tmp_target = tmp_target->next;
1038 gtk_drag_selection_received (GtkWidget *widget,
1039 GtkSelectionData *selection_data,
1043 GdkDragContext *context;
1044 GtkDragDestInfo *info;
1045 GtkWidget *drop_widget;
1049 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1050 info = g_dataset_get_data (context, "gtk-info");
1052 if (info->proxy_data &&
1053 info->proxy_data->target == selection_data->target)
1055 gtk_selection_data_set (info->proxy_data,
1056 selection_data->type,
1057 selection_data->format,
1058 selection_data->data,
1059 selection_data->length);
1064 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1066 gtk_drag_finish (context, TRUE, FALSE, time);
1068 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1069 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1075 GtkDragDestSite *site;
1077 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1079 if (site->target_list)
1083 if (gtk_target_list_find (site->target_list,
1084 selection_data->target,
1087 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1088 selection_data->length >= 0)
1089 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1090 "drag_data_received",
1091 context, info->drop_x, info->drop_y,
1098 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1099 "drag_data_received",
1100 context, info->drop_x, info->drop_y,
1101 selection_data, 0, time);
1104 if (site->flags & GTK_DEST_DEFAULT_DROP)
1107 gtk_drag_finish (context,
1108 (selection_data->length >= 0),
1109 (context->action == GDK_ACTION_MOVE),
1113 gtk_widget_unref (drop_widget);
1116 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1117 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1120 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1121 gdk_drag_context_unref (context);
1123 gtk_drag_release_ipc_widget (widget);
1126 /*************************************************************
1127 * gtk_drag_find_widget:
1128 * Recursive callback used to locate widgets for
1129 * DRAG_MOTION and DROP_START events.
1133 *************************************************************/
1136 gtk_drag_find_widget (GtkWidget *widget,
1137 GtkDragFindData *data)
1139 GtkAllocation new_allocation;
1143 new_allocation = widget->allocation;
1145 if (data->found || !GTK_WIDGET_MAPPED (widget))
1148 /* Note that in the following code, we only count the
1149 * position as being inside a WINDOW widget if it is inside
1150 * widget->window; points that are outside of widget->window
1151 * but within the allocation are not counted. This is consistent
1152 * with the way we highlight drag targets.
1154 if (!GTK_WIDGET_NO_WINDOW (widget))
1156 new_allocation.x = 0;
1157 new_allocation.y = 0;
1162 GdkWindow *window = widget->window;
1163 while (window != widget->parent->window)
1165 gint tx, ty, twidth, theight;
1166 gdk_window_get_size (window, &twidth, &theight);
1168 if (new_allocation.x < 0)
1170 new_allocation.width += new_allocation.x;
1171 new_allocation.x = 0;
1173 if (new_allocation.y < 0)
1175 new_allocation.height += new_allocation.y;
1176 new_allocation.y = 0;
1178 if (new_allocation.x + new_allocation.width > twidth)
1179 new_allocation.width = twidth - new_allocation.x;
1180 if (new_allocation.y + new_allocation.height > theight)
1181 new_allocation.height = theight - new_allocation.y;
1183 gdk_window_get_position (window, &tx, &ty);
1184 new_allocation.x += tx;
1186 new_allocation.y += ty;
1189 window = gdk_window_get_parent (window);
1193 if (data->toplevel ||
1194 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1195 (data->x < new_allocation.x + new_allocation.width) &&
1196 (data->y < new_allocation.y + new_allocation.height)))
1198 /* First, check if the drag is in a valid drop site in
1199 * one of our children
1201 if (GTK_IS_CONTAINER (widget))
1203 GtkDragFindData new_data = *data;
1205 new_data.x -= x_offset;
1206 new_data.y -= y_offset;
1207 new_data.found = FALSE;
1208 new_data.toplevel = FALSE;
1210 gtk_container_forall (GTK_CONTAINER (widget),
1211 (GtkCallback)gtk_drag_find_widget,
1214 data->found = new_data.found;
1217 /* If not, and this widget is registered as a drop site, check to
1218 * emit "drag_motion" to check if we are actually in
1222 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1224 data->found = data->callback (widget,
1226 data->x - new_allocation.x,
1227 data->y - new_allocation.y,
1229 /* If so, send a "drag_leave" to the last widget */
1232 if (data->info->widget && data->info->widget != widget)
1234 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1236 data->info->widget = widget;
1243 gtk_drag_proxy_begin (GtkWidget *widget,
1244 GtkDragDestInfo *dest_info)
1246 GtkDragSourceInfo *source_info;
1249 source_info = g_new0 (GtkDragSourceInfo, 1);
1250 source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1252 source_info->widget = widget;
1253 gtk_widget_ref (source_info->widget);
1254 source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1255 dest_info->context->targets);
1257 source_info->target_list = gtk_target_list_new (NULL, 0);
1258 tmp_list = dest_info->context->targets;
1261 gtk_target_list_add (source_info->target_list,
1262 GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1263 tmp_list = tmp_list->next;
1266 source_info->proxy_dest = dest_info;
1268 g_dataset_set_data (source_info->context, "gtk-info", source_info);
1270 gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget),
1272 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1275 dest_info->proxy_source = source_info;
1279 gtk_drag_dest_info_destroy (gpointer data)
1281 GtkDragDestInfo *info = data;
1287 gtk_drag_dest_realized (GtkWidget *widget)
1289 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1290 gdk_window_register_dnd (toplevel->window);
1294 gtk_drag_dest_site_destroy (gpointer data)
1296 GtkDragDestSite *site = data;
1298 if (site->target_list)
1299 gtk_target_list_unref (site->target_list);
1305 * Default drag handlers
1308 gtk_drag_dest_leave (GtkWidget *widget,
1309 GdkDragContext *context,
1312 GtkDragDestSite *site;
1314 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1315 g_return_if_fail (site != NULL);
1319 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1321 if (info->proxy_source && !info->dropped)
1322 gdk_drag_abort (info->proxy_source->context, time);
1328 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1329 gtk_drag_unhighlight (widget);
1331 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1332 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1335 site->have_drag = FALSE;
1340 gtk_drag_dest_motion (GtkWidget *widget,
1341 GdkDragContext *context,
1346 GtkDragDestSite *site;
1347 GdkDragAction action = 0;
1350 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1351 g_return_val_if_fail (site != NULL, FALSE);
1356 GdkEvent *current_event;
1357 GdkWindow *dest_window;
1358 GdkDragProtocol proto;
1360 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1362 if (!info->proxy_source)
1363 gtk_drag_proxy_begin (widget, info);
1365 current_event = gtk_get_current_event ();
1367 if (site->proxy_window)
1369 dest_window = site->proxy_window;
1370 proto = site->proxy_protocol;
1374 gdk_drag_find_window (info->proxy_source->context,
1376 current_event->dnd.x_root,
1377 current_event->dnd.y_root,
1378 &dest_window, &proto);
1381 gdk_drag_motion (info->proxy_source->context,
1383 current_event->dnd.x_root,
1384 current_event->dnd.y_root,
1385 context->suggested_action,
1386 context->actions, time);
1388 if (!site->proxy_window && dest_window)
1389 gdk_window_unref (dest_window);
1391 selection = gdk_drag_get_selection (info->proxy_source->context);
1393 selection != gdk_drag_get_selection (info->context))
1394 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1396 gdk_event_free (current_event);
1401 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1403 if (context->suggested_action & site->actions)
1404 action = context->suggested_action;
1411 if ((site->actions & (1 << i)) &&
1412 (context->actions & (1 << i)))
1420 if (action && gtk_drag_dest_find_target (widget, site, context))
1422 if (!site->have_drag)
1424 site->have_drag = TRUE;
1425 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1426 gtk_drag_highlight (widget);
1429 gdk_drag_status (context, action, time);
1433 gdk_drag_status (context, 0, time);
1438 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1439 context, x, y, time, &retval);
1441 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1445 gtk_drag_dest_drop (GtkWidget *widget,
1446 GdkDragContext *context,
1451 GtkDragDestSite *site;
1452 GtkDragDestInfo *info;
1454 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1455 g_return_val_if_fail (site != NULL, FALSE);
1457 info = g_dataset_get_data (context, "gtk-info");
1458 g_return_val_if_fail (info != NULL, FALSE);
1465 if (info->proxy_source ||
1466 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1468 gtk_drag_drop (info->proxy_source, time);
1472 /* We need to synthesize a motion event, wait for a status,
1473 * and, if we get a good one, do a drop.
1476 GdkEvent *current_event;
1478 GdkWindow *dest_window;
1479 GdkDragProtocol proto;
1481 gtk_drag_proxy_begin (widget, info);
1482 info->proxy_drop_wait = TRUE;
1483 info->proxy_drop_time = time;
1485 current_event = gtk_get_current_event ();
1487 if (site->proxy_window)
1489 dest_window = site->proxy_window;
1490 proto = site->proxy_protocol;
1494 gdk_drag_find_window (info->proxy_source->context,
1496 current_event->dnd.x_root,
1497 current_event->dnd.y_root,
1498 &dest_window, &proto);
1501 gdk_drag_motion (info->proxy_source->context,
1503 current_event->dnd.x_root,
1504 current_event->dnd.y_root,
1505 context->suggested_action,
1506 context->actions, time);
1508 if (!site->proxy_window && dest_window)
1509 gdk_window_unref (dest_window);
1511 selection = gdk_drag_get_selection (info->proxy_source->context);
1513 selection != gdk_drag_get_selection (info->context))
1514 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1516 gdk_event_free (current_event);
1526 if (site->flags & GTK_DEST_DEFAULT_DROP)
1528 GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
1530 if (target == GDK_NONE)
1533 gtk_drag_get_data (widget, context, target, time);
1536 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1537 context, x, y, time, &retval);
1539 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1547 /*************************************************************
1548 * gtk_drag_begin: Start a drag operation
1551 * widget: Widget from which drag starts
1552 * handlers: List of handlers to supply the data for the drag
1553 * button: Button user used to start drag
1554 * time: Time of event starting drag
1557 *************************************************************/
1560 gtk_drag_begin (GtkWidget *widget,
1561 GtkTargetList *target_list,
1562 GdkDragAction actions,
1566 GtkDragSourceInfo *info;
1567 GList *targets = NULL;
1569 guint32 time = GDK_CURRENT_TIME;
1570 GdkDragAction possible_actions, suggested_action;
1572 g_return_val_if_fail (widget != NULL, NULL);
1573 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1574 g_return_val_if_fail (target_list != NULL, NULL);
1577 time = gdk_event_get_time (event);
1579 info = g_new0 (GtkDragSourceInfo, 1);
1580 info->ipc_widget = gtk_drag_get_ipc_widget ();
1581 source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
1583 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1585 tmp_list = g_list_last (target_list->list);
1588 GtkTargetPair *pair = tmp_list->data;
1589 targets = g_list_prepend (targets,
1590 GINT_TO_POINTER (pair->target));
1591 tmp_list = tmp_list->prev;
1594 info->widget = widget;
1595 gtk_widget_ref (info->widget);
1597 info->context = gdk_drag_begin (info->ipc_widget->window, targets);
1598 g_list_free (targets);
1600 g_dataset_set_data (info->context, "gtk-info", info);
1602 info->button = button;
1603 info->target_list = target_list;
1604 gtk_target_list_ref (target_list);
1606 info->possible_actions = actions;
1608 info->cursor = NULL;
1609 info->status = GTK_DRAG_STATUS_DRAG;
1610 info->last_event = NULL;
1611 info->selections = NULL;
1612 info->icon_window = NULL;
1613 info->destroy_icon = FALSE;
1615 gtk_drag_get_event_actions (event, info->button, actions,
1616 &suggested_action, &possible_actions);
1619 info->cursor = gtk_drag_get_cursor (suggested_action);
1621 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1622 * the drag icon, it will be in the right place
1624 if (event->type == GDK_MOTION_NOTIFY)
1626 info->cur_x = event->motion.x_root;
1627 info->cur_y = event->motion.y_root;
1632 gdk_window_get_pointer (GDK_ROOT_PARENT (), &x, &y, NULL);
1638 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1641 /* We use a GTK grab here to override any grabs that the widget
1642 * we are dragging from might have held
1645 gtk_grab_add (info->ipc_widget);
1646 gdk_pointer_grab (info->ipc_widget->window, FALSE,
1647 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1648 GDK_BUTTON_RELEASE_MASK, NULL,
1649 info->cursor, time);
1651 if (event->type == GDK_MOTION_NOTIFY)
1652 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1654 info->start_x = info->cur_x;
1655 info->start_y = info->cur_y;
1657 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1658 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1659 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1660 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1661 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1662 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1664 return info->context;
1667 /*************************************************************
1668 * gtk_drag_source_set:
1669 * Register a drop site, and possibly add default behaviors.
1672 * start_button_mask: Mask of allowed buttons to start drag
1673 * targets: Table of targets for this source
1675 * actions: Actions allowed for this source
1677 *************************************************************/
1680 gtk_drag_source_set (GtkWidget *widget,
1681 GdkModifierType start_button_mask,
1682 const GtkTargetEntry *targets,
1684 GdkDragAction actions)
1686 GtkDragSourceSite *site;
1688 g_return_if_fail (widget != NULL);
1690 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1692 gtk_widget_add_events (widget,
1693 gtk_widget_get_events (widget) |
1694 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1695 GDK_BUTTON_MOTION_MASK);
1699 if (site->target_list)
1700 gtk_target_list_unref (site->target_list);
1704 site = g_new0 (GtkDragSourceSite, 1);
1706 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1707 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1709 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1710 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1713 gtk_object_set_data_full (GTK_OBJECT (widget),
1715 site, gtk_drag_source_site_destroy);
1718 site->start_button_mask = start_button_mask;
1721 site->target_list = gtk_target_list_new (targets, n_targets);
1723 site->target_list = NULL;
1725 site->actions = actions;
1729 /*************************************************************
1730 * gtk_drag_source_unset
1731 * Unregister this widget as a drag source.
1735 *************************************************************/
1738 gtk_drag_source_unset (GtkWidget *widget)
1740 GtkDragSourceSite *site;
1742 g_return_if_fail (widget != NULL);
1744 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1748 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1749 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1753 /*************************************************************
1754 * gtk_drag_source_set_icon:
1755 * Set an icon for drags from this source.
1757 * colormap: Colormap for this icon
1761 *************************************************************/
1764 gtk_drag_source_set_icon (GtkWidget *widget,
1765 GdkColormap *colormap,
1769 GtkDragSourceSite *site;
1771 g_return_if_fail (widget != NULL);
1773 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1774 g_return_if_fail (site != NULL);
1777 gdk_colormap_unref (site->colormap);
1779 gdk_pixmap_unref (site->pixmap);
1781 gdk_pixmap_unref (site->mask);
1783 site->colormap = colormap;
1785 gdk_colormap_ref (colormap);
1787 site->pixmap = pixmap;
1789 gdk_pixmap_ref (pixmap);
1793 gdk_pixmap_ref (mask);
1796 /*************************************************************
1797 * gtk_drag_set_icon_window:
1798 * Set a widget as the icon for a drag.
1805 *************************************************************/
1808 gtk_drag_set_icon_window (GdkDragContext *context,
1812 gboolean destroy_on_release)
1814 GtkDragSourceInfo *info;
1816 g_return_if_fail (context != NULL);
1817 g_return_if_fail (widget != NULL);
1819 info = g_dataset_get_data (context, "gtk-info");
1820 gtk_drag_remove_icon (info);
1822 info->icon_window = widget;
1823 info->hot_x = hot_x;
1824 info->hot_y = hot_y;
1828 gtk_widget_set_uposition (widget,
1829 info->cur_x - info->hot_x,
1830 info->cur_y - info->hot_y);
1831 gtk_widget_ref (widget);
1832 gdk_window_raise (widget->window);
1833 gtk_widget_show (widget);
1836 info->destroy_icon = destroy_on_release;
1839 /*************************************************************
1840 * gtk_drag_set_icon_widget:
1841 * Set a widget as the icon for a drag.
1848 *************************************************************/
1851 gtk_drag_set_icon_widget (GdkDragContext *context,
1856 g_return_if_fail (context != NULL);
1857 g_return_if_fail (widget != NULL);
1859 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
1862 /*************************************************************
1863 * gtk_drag_set_icon_pixmap:
1864 * Set a widget as the icon for a drag.
1867 * colormap: Colormap for the icon window.
1873 *************************************************************/
1876 gtk_drag_set_icon_pixmap (GdkDragContext *context,
1877 GdkColormap *colormap,
1886 g_return_if_fail (context != NULL);
1887 g_return_if_fail (colormap != NULL);
1888 g_return_if_fail (pixmap != NULL);
1890 gdk_window_get_size (pixmap, &width, &height);
1892 gtk_widget_push_visual (gdk_colormap_get_visual (colormap));
1893 gtk_widget_push_colormap (colormap);
1895 window = gtk_window_new (GTK_WINDOW_POPUP);
1896 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1897 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
1899 gtk_widget_pop_visual ();
1900 gtk_widget_pop_colormap ();
1902 gtk_widget_set_usize (window, width, height);
1903 gtk_widget_realize (window);
1905 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1908 gtk_widget_shape_combine_mask (window, mask, 0, 0);
1910 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
1913 /*************************************************************
1914 * gtk_drag_set_icon_default:
1915 * Set the icon for a drag to the default icon.
1919 *************************************************************/
1922 gtk_drag_set_icon_default (GdkDragContext *context)
1924 g_return_if_fail (context != NULL);
1926 if (!default_icon_pixmap)
1928 default_icon_colormap = gdk_colormap_get_system ();
1929 default_icon_pixmap =
1930 gdk_pixmap_colormap_create_from_xpm_d (NULL,
1931 default_icon_colormap,
1933 NULL, drag_default_xpm);
1934 default_icon_hot_x = -2;
1935 default_icon_hot_y = -2;
1938 gtk_drag_set_icon_pixmap (context,
1939 default_icon_colormap,
1940 default_icon_pixmap,
1943 default_icon_hot_y);
1946 /*************************************************************
1947 * gtk_drag_set_default_icon:
1948 * Set a default icon for all drags as a pixmap.
1950 * colormap: Colormap for the icon window.
1956 *************************************************************/
1959 gtk_drag_set_default_icon (GdkColormap *colormap,
1965 g_return_if_fail (colormap != NULL);
1966 g_return_if_fail (pixmap != NULL);
1968 if (default_icon_colormap)
1969 gdk_colormap_unref (default_icon_colormap);
1970 if (default_icon_pixmap)
1971 gdk_pixmap_unref (default_icon_pixmap);
1972 if (default_icon_mask)
1973 gdk_pixmap_unref (default_icon_pixmap);
1975 default_icon_colormap = colormap;
1976 gdk_colormap_ref (colormap);
1978 default_icon_pixmap = pixmap;
1979 gdk_pixmap_ref (pixmap);
1981 default_icon_mask = mask;
1983 gdk_pixmap_ref (mask);
1985 default_icon_hot_x = hot_x;
1986 default_icon_hot_y = hot_y;
1990 /*************************************************************
1991 * gtk_drag_source_handle_event:
1992 * Called from widget event handling code on Drag events
1996 * toplevel: Toplevel widget that received the event
1999 *************************************************************/
2002 gtk_drag_source_handle_event (GtkWidget *widget,
2005 GtkDragSourceInfo *info;
2006 GdkDragContext *context;
2008 g_return_if_fail (widget != NULL);
2009 g_return_if_fail (event != NULL);
2011 context = event->dnd.context;
2012 info = g_dataset_get_data (context, "gtk-info");
2016 switch (event->type)
2018 case GDK_DRAG_STATUS:
2022 if (info->proxy_dest)
2024 if (!event->dnd.send_event)
2026 if (info->proxy_dest->proxy_drop_wait)
2028 /* Aha - we can finally pass the MOTIF DROP on... */
2029 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2033 gdk_drag_status (info->proxy_dest->context,
2034 event->dnd.context->action,
2041 cursor = gtk_drag_get_cursor (event->dnd.context->action);
2042 if (info->cursor != cursor)
2044 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window),
2045 PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
2046 ((GdkCursorPrivate *)cursor)->xcursor,
2048 info->cursor = cursor;
2051 if (info->last_event)
2053 gtk_drag_motion_cb (info->widget,
2054 (GdkEventMotion *)info->last_event,
2056 info->last_event = NULL;
2062 case GDK_DROP_FINISHED:
2063 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2066 g_assert_not_reached ();
2070 /*************************************************************
2071 * gtk_drag_source_check_selection:
2072 * Check if we've set up handlers/claimed the selection
2073 * for a given drag. If not, add them.
2077 *************************************************************/
2080 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2086 tmp_list = info->selections;
2089 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
2091 tmp_list = tmp_list->next;
2094 gtk_selection_owner_set (info->ipc_widget, selection, time);
2095 info->selections = g_list_prepend (info->selections,
2096 GUINT_TO_POINTER (selection));
2098 tmp_list = info->target_list->list;
2101 GtkTargetPair *pair = tmp_list->data;
2103 gtk_selection_add_target (info->ipc_widget,
2107 tmp_list = tmp_list->next;
2110 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2112 gtk_selection_add_target (info->ipc_widget,
2114 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2115 TARGET_MOTIF_SUCCESS);
2116 gtk_selection_add_target (info->ipc_widget,
2118 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2119 TARGET_MOTIF_FAILURE);
2122 gtk_selection_add_target (info->ipc_widget,
2124 gdk_atom_intern ("DELETE", FALSE),
2128 /*************************************************************
2129 * gtk_drag_drop_finished:
2130 * Clean up from the drag, and display snapback, if necessary.
2136 *************************************************************/
2139 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2143 gtk_drag_source_release_selections (info, time);
2145 if (info->proxy_dest)
2147 /* The time from the event isn't reliable for Xdnd drags */
2148 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2149 info->proxy_dest->proxy_drop_time);
2150 gtk_drag_source_info_destroy (info);
2156 gtk_drag_source_info_destroy (info);
2160 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2164 anim->n_steps = MAX (info->cur_x - info->start_x,
2165 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2166 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2167 if (info->icon_window)
2169 gtk_widget_show (info->icon_window);
2170 gdk_window_raise (info->icon_window->window);
2173 /* Mark the context as dead, so if the destination decides
2174 * to respond really late, we still are OK.
2176 g_dataset_set_data (info->context, "gtk-info", NULL);
2177 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2183 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2186 GList *tmp_list = info->selections;
2189 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2190 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2191 gtk_selection_owner_set (NULL, selection, time);
2192 tmp_list = tmp_list->next;
2195 g_list_free (info->selections);
2196 info->selections = NULL;
2199 /*************************************************************
2201 * Send a drop event.
2205 *************************************************************/
2208 gtk_drag_drop (GtkDragSourceInfo *info,
2211 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2213 GtkSelectionData selection_data;
2215 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2217 tmp_list = info->target_list->list;
2220 GtkTargetPair *pair = tmp_list->data;
2222 if (pair->target == target)
2224 selection_data.selection = GDK_NONE;
2225 selection_data.target = target;
2226 selection_data.data = NULL;
2227 selection_data.length = -1;
2229 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2230 info->context, &selection_data,
2234 /* FIXME: Should we check for length >= 0 here? */
2235 gtk_drag_drop_finished (info, TRUE, time);
2238 tmp_list = tmp_list->next;
2240 gtk_drag_drop_finished (info, FALSE, time);
2244 if (info->icon_window)
2245 gtk_widget_hide (info->icon_window);
2247 gdk_drag_drop (info->context, time);
2248 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2249 gtk_drag_abort_timeout,
2255 * Source side callbacks.
2259 gtk_drag_source_event_cb (GtkWidget *widget,
2263 GtkDragSourceSite *site;
2264 site = (GtkDragSourceSite *)data;
2266 switch (event->type)
2268 case GDK_BUTTON_PRESS:
2269 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2271 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2272 site->x = event->button.x;
2273 site->y = event->button.y;
2277 case GDK_BUTTON_RELEASE:
2278 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2280 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2284 case GDK_MOTION_NOTIFY:
2285 if (site->state & event->motion.state & site->start_button_mask)
2287 /* FIXME: This is really broken and can leave us
2293 if (site->state & event->motion.state &
2294 GDK_BUTTON1_MASK << (i - 1))
2298 if (MAX (abs (site->x - event->motion.x),
2299 abs (site->y - event->motion.y)) > 3)
2301 GtkDragSourceInfo *info;
2302 GdkDragContext *context;
2305 context = gtk_drag_begin (widget, site->target_list,
2310 info = g_dataset_get_data (context, "gtk-info");
2312 if (!info->icon_window)
2315 gtk_drag_set_icon_pixmap (context,
2318 site->mask, -2, -2);
2320 gtk_drag_set_icon_default (context);
2328 default: /* hit for 2/3BUTTON_PRESS */
2335 gtk_drag_source_site_destroy (gpointer data)
2337 GtkDragSourceSite *site = data;
2339 if (site->target_list)
2340 gtk_target_list_unref (site->target_list);
2343 gdk_pixmap_unref (site->pixmap);
2346 gdk_pixmap_unref (site->mask);
2352 gtk_drag_selection_get (GtkWidget *widget,
2353 GtkSelectionData *selection_data,
2358 GtkDragSourceInfo *info = data;
2359 static GdkAtom null_atom = GDK_NONE;
2363 null_atom = gdk_atom_intern ("NULL", FALSE);
2368 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2371 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2373 case TARGET_MOTIF_SUCCESS:
2374 gtk_drag_drop_finished (info, TRUE, time);
2375 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2377 case TARGET_MOTIF_FAILURE:
2378 gtk_drag_drop_finished (info, FALSE, time);
2379 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2382 if (info->proxy_dest)
2384 /* This is sort of dangerous and needs to be thought
2387 info->proxy_dest->proxy_data = selection_data;
2388 gtk_drag_get_data (info->widget,
2389 info->proxy_dest->context,
2390 selection_data->target,
2393 info->proxy_dest->proxy_data = NULL;
2397 if (gtk_target_list_find (info->target_list,
2398 selection_data->target,
2401 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2413 gtk_drag_anim_timeout (gpointer data)
2415 GtkDragAnim *anim = data;
2419 GDK_THREADS_ENTER ();
2421 if (anim->step == anim->n_steps)
2423 gtk_drag_source_info_destroy (anim->info);
2430 x = (anim->info->start_x * (anim->step + 1) +
2431 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2432 y = (anim->info->start_y * (anim->step + 1) +
2433 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2434 if (anim->info->icon_window)
2435 gtk_widget_set_uposition (anim->info->icon_window,
2436 x - anim->info->hot_x,
2437 y - anim->info->hot_y);
2444 GDK_THREADS_LEAVE ();
2450 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2452 if (info->icon_window)
2454 gtk_widget_hide (info->icon_window);
2455 if (info->destroy_icon)
2456 gtk_widget_destroy (info->icon_window);
2458 gtk_widget_unref (info->icon_window);
2459 info->icon_window = NULL;
2464 gtk_drag_source_info_destroy (gpointer data)
2466 GtkDragSourceInfo *info = data;
2468 gtk_drag_remove_icon (data);
2470 if (!info->proxy_dest)
2471 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2475 gtk_widget_unref (info->widget);
2477 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2478 gtk_selection_remove_all (info->ipc_widget);
2479 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2480 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2481 gtk_drag_release_ipc_widget (info->ipc_widget);
2483 gtk_target_list_unref (info->target_list);
2485 g_dataset_set_data (info->context, "gtk-info", NULL);
2486 gdk_drag_context_unref (info->context);
2488 if (info->drop_timeout)
2489 gtk_timeout_remove (info->drop_timeout);
2494 /*************************************************************
2495 * gtk_drag_motion_cb:
2496 * "motion_notify_event" callback during drag.
2500 *************************************************************/
2503 gtk_drag_motion_cb (GtkWidget *widget,
2504 GdkEventMotion *event,
2507 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2509 GdkDragAction action;
2510 GdkDragAction possible_actions;
2511 GdkWindow *window = NULL;
2512 GdkWindow *dest_window;
2513 GdkDragProtocol protocol;
2514 gint x_root, y_root;
2518 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2519 event->x_root = x_root;
2520 event->y_root = y_root;
2523 gtk_drag_get_event_actions ((GdkEvent *)event,
2525 info->possible_actions,
2526 &action, &possible_actions);
2528 info->cur_x = event->x_root;
2529 info->cur_y = event->y_root;
2531 if (info->icon_window)
2533 gdk_window_raise (info->icon_window->window);
2534 gtk_widget_set_uposition (info->icon_window,
2535 info->cur_x - info->hot_x,
2536 info->cur_y - info->hot_y);
2537 window = info->icon_window->window;
2540 gdk_drag_find_window (info->context,
2541 window, event->x_root, event->y_root,
2542 &dest_window, &protocol);
2544 if (gdk_drag_motion (info->context, dest_window, protocol,
2545 event->x_root, event->y_root, action,
2549 if (info->last_event)
2550 gdk_event_free ((GdkEvent *)info->last_event);
2552 info->last_event = gdk_event_copy ((GdkEvent *)event);
2556 gdk_window_unref (dest_window);
2558 selection = gdk_drag_get_selection (info->context);
2560 gtk_drag_source_check_selection (info, selection, event->time);
2563 /* We ignore the response, so we can respond precisely to the drop
2566 gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2572 /*************************************************************
2573 * gtk_drag_motion_cb:
2574 * "button_release_event" callback during drag.
2578 *************************************************************/
2581 gtk_drag_button_release_cb (GtkWidget *widget,
2582 GdkEventButton *event,
2585 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2586 GtkWidget *source_widget = info->widget;
2587 GdkEvent send_event;
2589 gtk_widget_ref (source_widget);
2591 if (event->button != info->button)
2594 gdk_pointer_ungrab (event->time);
2596 gtk_grab_remove (widget);
2597 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
2598 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2600 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
2601 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2604 if ((info->context->action != 0) && (info->context->dest_window != NULL))
2606 gtk_drag_drop (info, event->time);
2610 gdk_drag_abort (info->context, event->time);
2611 gtk_drag_drop_finished (info, FALSE, event->time);
2614 /* Send on a release pair to the the original
2615 * widget to convince it to release its grab. We need to
2616 * call gtk_propagate_event() here, instead of
2617 * gtk_widget_event() because widget like GtkList may
2618 * expect propagation.
2621 send_event.button.type = GDK_BUTTON_RELEASE;
2622 send_event.button.window = GDK_ROOT_PARENT ();
2623 send_event.button.send_event = TRUE;
2624 send_event.button.time = event->time;
2625 send_event.button.x = 0;
2626 send_event.button.y = 0;
2627 send_event.button.pressure = 0.;
2628 send_event.button.xtilt = 0.;
2629 send_event.button.ytilt = 0.;
2630 send_event.button.state = event->state;
2631 send_event.button.button = event->button;
2632 send_event.button.source = GDK_SOURCE_PEN;
2633 send_event.button.deviceid = GDK_CORE_POINTER;
2634 send_event.button.x_root = 0;
2635 send_event.button.y_root = 0;
2637 gtk_propagate_event (source_widget, &send_event);
2639 gtk_widget_unref (source_widget);
2645 gtk_drag_abort_timeout (gpointer data)
2647 GtkDragSourceInfo *info = data;
2648 guint32 time = GDK_CURRENT_TIME;
2650 if (info->proxy_dest)
2651 time = info->proxy_dest->proxy_drop_time;
2653 info->drop_timeout = 0;
2654 gtk_drag_drop_finished (info, FALSE, time);