1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
23 #include "gtkinvisible.h"
25 #include "gtksignal.h"
26 #include "gtkwindow.h"
28 static GSList *drag_widgets = NULL;
29 static GSList *source_widgets = NULL;
31 typedef struct _GtkDragSourceSite GtkDragSourceSite;
32 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
33 typedef struct _GtkDragDestSite GtkDragDestSite;
34 typedef struct _GtkDragDestInfo GtkDragDestInfo;
35 typedef struct _GtkDragAnim GtkDragAnim;
36 typedef struct _GtkDragFindData GtkDragFindData;
45 struct _GtkDragSourceSite {
46 GdkModifierType start_button_mask;
47 GtkTargetList *target_list; /* Targets for drag data */
48 GdkDragAction actions; /* Possible actions */
49 GdkColormap *colormap; /* Colormap for drag icon */
50 GdkPixmap *pixmap; /* Icon for drag data */
53 /* Stored button press information to detect drag beginning */
58 struct _GtkDragSourceInfo {
60 GtkTargetList *target_list; /* Targets for drag data */
61 GdkDragAction possible_actions; /* Actions allowed by source */
62 GdkDragContext *context; /* drag context */
63 GtkWidget *icon_window; /* Window for drag */
64 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
65 GdkCursor *cursor; /* Cursor for drag */
66 gint hot_x, hot_y; /* Hot spot for drag */
67 gint button; /* mouse button starting drag */
69 GtkDragStatus status; /* drag status */
70 GdkEvent *last_event; /* motion event waiting for response */
72 gint start_x, start_y; /* Initial position */
73 gint cur_x, cur_y; /* Current Position */
75 GList *selections; /* selections we've claimed */
77 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
79 guint drop_timeout; /* Timeout for aborting drop */
82 struct _GtkDragDestSite {
83 GtkDestDefaults flags;
84 GtkTargetList *target_list;
85 GdkDragAction actions;
86 GdkWindow *proxy_window;
87 GdkDragProtocol proxy_protocol;
88 gboolean do_proxy : 1;
89 gboolean proxy_coords : 1;
90 gboolean have_drag : 1;
93 struct _GtkDragDestInfo {
94 GtkWidget *widget; /* Widget in which drag is in */
95 GdkDragContext *context; /* Drag context */
96 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
97 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
98 gboolean dropped : 1; /* Set after we receive a drop */
99 guint32 proxy_drop_time; /* Timestamp for proxied drop */
100 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
101 * status reply before sending
104 gint drop_x, drop_y; /* Position of drop */
107 #define DROP_ABORT_TIME 300000
109 #define ANIM_STEP_TIME 50
110 #define ANIM_STEP_LENGTH 50
111 #define ANIM_MIN_STEPS 5
112 #define ANIM_MAX_STEPS 10
114 struct _GtkDragAnim {
115 GtkDragSourceInfo *info;
120 struct _GtkDragFindData {
123 GdkDragContext *context;
124 GtkDragDestInfo *info;
127 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
128 gint x, gint y, guint32 time);
132 /* Enumeration for some targets we handle internally */
135 TARGET_MOTIF_SUCCESS = 0x40000000,
136 TARGET_MOTIF_FAILURE,
142 static GdkPixmap *default_icon_pixmap = NULL;
143 static GdkPixmap *default_icon_mask = NULL;
144 static GdkColormap *default_icon_colormap = NULL;
145 static gint default_icon_hot_x;
146 static gint default_icon_hot_y;
148 /* Forward declarations */
149 static void gtk_drag_get_event_actions (GdkEvent *event,
151 GdkDragAction actions,
152 GdkDragAction *suggested_action,
153 GdkDragAction *possible_actions);
154 static GdkCursor * gtk_drag_get_cursor (GdkDragAction action);
155 static GtkWidget *gtk_drag_get_ipc_widget (void);
156 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
158 static void gtk_drag_highlight_paint (GtkWidget *widget);
159 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
160 GdkEventExpose *event,
164 static GdkAtom gtk_drag_dest_find_target (GtkWidget *widget,
165 GtkDragDestSite *site,
166 GdkDragContext *context);
167 static void gtk_drag_selection_received (GtkWidget *widget,
168 GtkSelectionData *selection_data,
171 static void gtk_drag_find_widget (GtkWidget *widget,
172 GtkDragFindData *data);
173 static void gtk_drag_proxy_begin (GtkWidget *widget,
174 GtkDragDestInfo *dest_info);
175 static void gtk_drag_dest_info_destroy (gpointer data);
176 static void gtk_drag_dest_realized (GtkWidget *widget);
177 static void gtk_drag_dest_site_destroy (gpointer data);
178 static void gtk_drag_dest_leave (GtkWidget *widget,
179 GdkDragContext *context,
181 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
182 GdkDragContext *context,
186 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
187 GdkDragContext *context,
192 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
195 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
197 static void gtk_drag_drop (GtkDragSourceInfo *info,
199 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
203 static gint gtk_drag_source_event_cb (GtkWidget *widget,
206 static void gtk_drag_source_site_destroy (gpointer data);
207 static void gtk_drag_selection_get (GtkWidget *widget,
208 GtkSelectionData *selection_data,
212 static gint gtk_drag_anim_timeout (gpointer data);
213 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
214 static void gtk_drag_source_info_destroy (gpointer data);
215 static gint gtk_drag_motion_cb (GtkWidget *widget,
216 GdkEventMotion *event,
218 static gint gtk_drag_button_release_cb (GtkWidget *widget,
219 GdkEventButton *event,
221 static gint gtk_drag_abort_timeout (gpointer data);
223 /************************
224 * Cursor and Icon data *
225 ************************/
227 #define action_ask_width 16
228 #define action_ask_height 16
229 static const char action_ask_bits[] = {
230 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
231 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
232 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
234 #define action_ask_mask_width 16
235 #define action_ask_mask_height 16
236 static const char action_ask_mask_bits[] = {
237 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
238 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
239 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
241 #define action_copy_width 16
242 #define action_copy_height 16
243 static const char action_copy_bits[] = {
244 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
245 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
246 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
248 #define action_copy_mask_width 16
249 #define action_copy_mask_height 16
250 static const char action_copy_mask_bits[] = {
251 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
252 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
253 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
255 #define action_move_width 16
256 #define action_move_height 16
257 static const char action_move_bits[] = {
258 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
259 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
260 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
262 #define action_move_mask_width 16
263 #define action_move_mask_height 16
264 static const char action_move_mask_bits[] = {
265 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
266 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
267 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
269 #define action_link_width 16
270 #define action_link_height 16
271 static const char action_link_bits[] = {
272 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
273 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
274 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
276 #define action_link_mask_width 16
277 #define action_link_mask_height 16
278 static const char action_link_mask_bits[] = {
279 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
280 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
281 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
283 #define action_none_width 16
284 #define action_none_height 16
285 static const char action_none_bits[] = {
286 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
287 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
288 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
290 #define action_none_mask_width 16
291 #define action_none_mask_height 16
292 static const char action_none_mask_bits[] = {
293 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
294 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
295 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
297 #define CURSOR_WIDTH 16
298 #define CURSOR_HEIGHT 16
301 GdkDragAction action;
306 { GDK_ACTION_DEFAULT, 0 },
307 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
308 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
309 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
310 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
311 { 0 , action_none_bits, action_none_mask_bits, NULL },
314 static const gint n_drag_cursors = sizeof(drag_cursors) / sizeof(drag_cursors[0]);
317 static const char * drag_default_xpm[] = {
330 " ...+++++++++++.. ",
331 " ..+.++++++++++++.. ",
332 " .++.++++++++++++.. ",
333 " .+++.++++++++++++.. ",
334 " .++++.++++++++++++. ",
335 " .+++.+++++++++++++.. ",
336 " .++.+++++++++++++++.. ",
337 " .+.+++++++++++++++++.. ",
338 " ..+++++++++++++++++++.. ",
339 " ..++++++++++++++++++++. ",
340 " .++++++++++++++++++++.. ",
341 " ..+++++++++++++++++.. ",
342 " .++++++++++++++++.. ",
343 " ..+++++++++++++... ",
355 /*********************
356 * Utility functions *
357 *********************/
359 /*************************************************************
360 * gtk_drag_get_ipc_widget:
361 * Return a invisible, off-screen, override-redirect
366 *************************************************************/
369 gtk_drag_get_ipc_widget(void)
375 GSList *tmp = drag_widgets;
376 result = drag_widgets->data;
377 drag_widgets = drag_widgets->next;
378 g_slist_free_1 (tmp);
382 result = gtk_invisible_new();
383 gtk_widget_show (result);
389 /*************************************************************
390 * gtk_drag_release_ipc_widget:
391 * Releases widget retrieved with gtk_drag_get_ipc_widget()
393 * widget: the widget to release.
395 *************************************************************/
398 gtk_drag_release_ipc_widget (GtkWidget *widget)
400 drag_widgets = g_slist_prepend (drag_widgets, widget);
404 gtk_drag_get_event_actions (GdkEvent *event,
406 GdkDragAction actions,
407 GdkDragAction *suggested_action,
408 GdkDragAction *possible_actions)
410 *suggested_action = 0;
411 *possible_actions = 0;
415 GdkModifierType state = 0;
419 case GDK_MOTION_NOTIFY:
420 state = event->motion.state;
422 case GDK_BUTTON_PRESS:
423 case GDK_2BUTTON_PRESS:
424 case GDK_3BUTTON_PRESS:
425 case GDK_BUTTON_RELEASE:
426 state = event->button.state;
429 case GDK_KEY_RELEASE:
430 state = event->key.state;
432 case GDK_ENTER_NOTIFY:
433 case GDK_LEAVE_NOTIFY:
434 state = event->crossing.state;
440 if (((button == 2) || (button == 3)) && (actions & GDK_ACTION_ASK))
442 *suggested_action = GDK_ACTION_ASK;
443 *possible_actions = actions;
445 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
447 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
449 if (actions & GDK_ACTION_LINK)
451 *suggested_action = GDK_ACTION_LINK;
452 *possible_actions = GDK_ACTION_LINK;
455 else if (state & GDK_CONTROL_MASK)
457 if (actions & GDK_ACTION_COPY)
459 *suggested_action = GDK_ACTION_COPY;
460 *possible_actions = GDK_ACTION_COPY;
466 if (actions & GDK_ACTION_MOVE)
468 *suggested_action = GDK_ACTION_MOVE;
469 *possible_actions = GDK_ACTION_MOVE;
476 *possible_actions = actions;
478 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
479 *suggested_action = GDK_ACTION_ASK;
480 else if (actions & GDK_ACTION_COPY)
481 *suggested_action = GDK_ACTION_COPY;
482 else if (actions & GDK_ACTION_MOVE)
483 *suggested_action = GDK_ACTION_MOVE;
484 else if (actions & GDK_ACTION_LINK)
485 *suggested_action = GDK_ACTION_LINK;
493 gtk_drag_get_cursor (GdkDragAction action)
497 for (i = 0 ; i < n_drag_cursors - 1; i++)
498 if (drag_cursors[i].action == action)
501 if (drag_cursors[i].cursor == NULL)
506 gdk_bitmap_create_from_data (NULL,
507 drag_cursors[i].bits,
508 CURSOR_WIDTH, CURSOR_HEIGHT);
510 gdk_bitmap_create_from_data (NULL,
511 drag_cursors[i].mask,
512 CURSOR_WIDTH, CURSOR_HEIGHT);
514 gdk_color_white (gdk_colormap_get_system(), &bg);
515 gdk_color_black (gdk_colormap_get_system(), &fg);
517 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
519 gdk_pixmap_unref (pixmap);
520 gdk_pixmap_unref (mask);
523 return drag_cursors[i].cursor;
526 /********************
528 ********************/
530 /*************************************************************
532 * Get the data for a drag or drop
534 * context - drag context
535 * target - format to retrieve the data in.
536 * time - timestamp of triggering event.
539 *************************************************************/
542 gtk_drag_get_data (GtkWidget *widget,
543 GdkDragContext *context,
547 GtkWidget *selection_widget;
549 g_return_if_fail (widget != NULL);
550 g_return_if_fail (context != NULL);
552 selection_widget = gtk_drag_get_ipc_widget();
554 gdk_drag_context_ref (context);
555 gtk_widget_ref (widget);
557 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
558 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
560 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
562 gtk_selection_convert (selection_widget,
563 gdk_drag_get_selection(context),
569 /*************************************************************
570 * gtk_drag_get_source_widget:
571 * Get the widget the was the source of this drag, if
572 * the drag originated from this application.
574 * context: The drag context for this drag
576 * The source widget, or NULL if the drag originated from
577 * a different application.
578 *************************************************************/
581 gtk_drag_get_source_widget (GdkDragContext *context)
585 tmp_list = source_widgets;
588 GtkWidget *ipc_widget = tmp_list->data;
590 if (ipc_widget->window == context->source_window)
592 GtkDragSourceInfo *info;
593 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
595 return info ? info->widget : NULL;
598 tmp_list = tmp_list->next;
604 /*************************************************************
606 * Notify the drag source that the transfer of data
609 * context: The drag context for this drag
610 * success: Was the data successfully transferred?
611 * time: The timestamp to use when notifying the destination.
613 *************************************************************/
616 gtk_drag_finish (GdkDragContext *context,
621 GdkAtom target = GDK_NONE;
623 g_return_if_fail (context != NULL);
627 target = gdk_atom_intern ("DELETE", FALSE);
629 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
631 target = gdk_atom_intern (success ?
632 "XmTRANSFER_SUCCESS" :
633 "XmTRANSFER_FAILURE",
637 if (target != GDK_NONE)
639 GtkWidget *selection_widget = gtk_drag_get_ipc_widget();
641 gdk_drag_context_ref (context);
643 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
644 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
645 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
648 gtk_selection_convert (selection_widget,
649 gdk_drag_get_selection(context),
655 gdk_drop_finish (context, success, time);
658 /*************************************************************
659 * gtk_drag_highlight_paint:
660 * Paint a highlight indicating drag status onto the widget.
664 *************************************************************/
667 gtk_drag_highlight_paint (GtkWidget *widget)
669 gint x, y, width, height;
671 g_return_if_fail (widget != NULL);
673 if (GTK_WIDGET_DRAWABLE (widget))
675 if (GTK_WIDGET_NO_WINDOW (widget))
677 x = widget->allocation.x;
678 y = widget->allocation.y;
679 width = widget->allocation.width;
680 height = widget->allocation.height;
686 gdk_window_get_size (widget->window, &width, &height);
689 gtk_draw_shadow (widget->style, widget->window,
690 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
691 x, y, width, height);
693 gdk_draw_rectangle (widget->window,
694 widget->style->black_gc,
696 x, y, width - 1, height - 1);
700 /*************************************************************
701 * gtk_drag_highlight_expose:
702 * Callback for expose_event for highlighted widgets.
708 *************************************************************/
711 gtk_drag_highlight_expose (GtkWidget *widget,
712 GdkEventExpose *event,
715 gtk_drag_highlight_paint (widget);
719 /*************************************************************
720 * gtk_drag_highlight:
721 * Highlight the given widget in the default manner.
725 *************************************************************/
728 gtk_drag_highlight (GtkWidget *widget)
730 gtk_signal_connect_after (GTK_OBJECT (widget), "draw",
731 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
733 gtk_signal_connect (GTK_OBJECT (widget), "expose_event",
734 GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
737 gtk_widget_queue_draw (widget);
740 /*************************************************************
741 * gtk_drag_unhighlight:
742 * Refresh the given widget to remove the highlight.
746 *************************************************************/
749 gtk_drag_unhighlight (GtkWidget *widget)
751 g_return_if_fail (widget != NULL);
753 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
754 GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
756 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
757 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
760 gtk_widget_queue_clear (widget);
763 /*************************************************************
765 * Register a drop site, and possibly add default behaviors.
768 * flags: Which types of default drag behavior to use
769 * targets: Table of targets that can be accepted
770 * n_targets: Number of of entries in targets
773 *************************************************************/
776 gtk_drag_dest_set (GtkWidget *widget,
777 GtkDestDefaults flags,
778 const GtkTargetEntry *targets,
780 GdkDragAction actions)
782 GtkDragDestSite *site;
784 g_return_if_fail (widget != NULL);
786 /* HACK, do this in the destroy */
787 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
789 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
791 if (GTK_WIDGET_REALIZED (widget))
792 gtk_drag_dest_realized (widget);
794 gtk_signal_connect (GTK_OBJECT (widget), "realize",
795 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
797 site = g_new (GtkDragDestSite, 1);
800 site->have_drag = FALSE;
802 site->target_list = gtk_target_list_new (targets, n_targets);
804 site->target_list = NULL;
806 site->actions = actions;
807 site->do_proxy = FALSE;
809 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
810 site, gtk_drag_dest_site_destroy);
813 /*************************************************************
814 * gtk_drag_dest_set_proxy:
815 * Set up this widget to proxy drags elsewhere.
818 * proxy_window: window to which forward drag events
819 * protocol: Drag protocol which the dest widget accepts
820 * use_coordinates: If true, send the same coordinates to the
821 * destination, because it is a embedded
824 *************************************************************/
827 gtk_drag_dest_set_proxy (GtkWidget *widget,
828 GdkWindow *proxy_window,
829 GdkDragProtocol protocol,
830 gboolean use_coordinates)
832 GtkDragDestSite *site;
834 g_return_if_fail (widget != NULL);
836 /* HACK, do this in the destroy */
837 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
839 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
841 if (GTK_WIDGET_REALIZED (widget))
842 gtk_drag_dest_realized (widget);
844 gtk_signal_connect (GTK_OBJECT (widget), "realize",
845 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
847 site = g_new (GtkDragDestSite, 1);
850 site->have_drag = FALSE;
851 site->target_list = NULL;
853 site->proxy_window = proxy_window;
855 gdk_window_ref (proxy_window);
856 site->do_proxy = TRUE;
857 site->proxy_protocol = protocol;
858 site->proxy_coords = use_coordinates;
860 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
861 site, gtk_drag_dest_site_destroy);
864 /*************************************************************
865 * gtk_drag_dest_unset
866 * Unregister this widget as a drag target.
870 *************************************************************/
873 gtk_drag_dest_unset (GtkWidget *widget)
875 g_return_if_fail (widget != NULL);
877 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
880 /*************************************************************
881 * gtk_drag_dest_handle_event:
882 * Called from widget event handling code on Drag events
886 * toplevel: Toplevel widget that received the event
889 *************************************************************/
892 gtk_drag_dest_handle_event (GtkWidget *toplevel,
895 GtkDragDestInfo *info;
896 GdkDragContext *context;
898 g_return_if_fail (toplevel != NULL);
899 g_return_if_fail (event != NULL);
901 context = event->dnd.context;
903 info = g_dataset_get_data (context, "gtk-info");
906 info = g_new (GtkDragDestInfo, 1);
908 info->context = event->dnd.context;
909 info->proxy_source = NULL;
910 info->proxy_data = NULL;
911 info->dropped = FALSE;
912 info->proxy_drop_wait = FALSE;
913 g_dataset_set_data_full (context,
916 gtk_drag_dest_info_destroy);
919 /* Find the widget for the event */
928 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
933 case GDK_DRAG_MOTION:
936 GtkDragFindData data;
939 if (event->type == GDK_DROP_START)
940 info->dropped = TRUE;
942 gdk_window_get_origin (toplevel->window, &tx, &ty);
944 data.x = event->dnd.x_root - tx;
945 data.y = event->dnd.y_root - ty;
946 data.context = context;
949 data.toplevel = TRUE;
950 data.callback = (event->type == GDK_DRAG_MOTION) ?
951 gtk_drag_dest_motion : gtk_drag_dest_drop;
952 data.time = event->dnd.time;
954 gtk_drag_find_widget (toplevel, &data);
956 /* We send a leave here so that the widget unhighlights
960 ((event->type == GDK_DROP_START) || (!data.found)))
962 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
968 if (event->type == GDK_DRAG_MOTION)
971 gdk_drag_status (context, 0, event->dnd.time);
973 else if (event->type == GDK_DROP_START)
975 gdk_drop_reply (context, data.found, event->dnd.time);
976 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
977 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
983 g_assert_not_reached();
987 /*************************************************************
988 * gtk_drag_dest_find_target:
989 * Decide on a target for the drag.
994 *************************************************************/
997 gtk_drag_dest_find_target (GtkWidget *widget,
998 GtkDragDestSite *site,
999 GdkDragContext *context)
1002 GList *tmp_source = NULL;
1003 GtkWidget *source_widget = gtk_drag_get_source_widget (context);
1005 tmp_target = site->target_list->list;
1008 GtkTargetPair *pair = tmp_target->data;
1009 tmp_source = context->targets;
1012 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1014 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1015 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1016 return pair->target;
1020 tmp_source = tmp_source->next;
1022 tmp_target = tmp_target->next;
1029 gtk_drag_selection_received (GtkWidget *widget,
1030 GtkSelectionData *selection_data,
1034 GdkDragContext *context;
1035 GtkDragDestInfo *info;
1036 GtkWidget *drop_widget;
1040 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1041 info = g_dataset_get_data (context, "gtk-info");
1043 if (info->proxy_data &&
1044 info->proxy_data->target == selection_data->target)
1046 gtk_selection_data_set (info->proxy_data,
1047 selection_data->type,
1048 selection_data->format,
1049 selection_data->data,
1050 selection_data->length);
1055 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1057 gtk_drag_finish (context, TRUE, FALSE, time);
1059 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1060 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1066 GtkDragDestSite *site;
1068 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1070 if (site->target_list)
1074 if (gtk_target_list_find (site->target_list,
1075 selection_data->target,
1078 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1079 selection_data->length >= 0)
1080 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1081 "drag_data_received",
1082 context, info->drop_x, info->drop_y,
1089 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1090 "drag_data_received",
1091 context, info->drop_x, info->drop_y,
1092 selection_data, 0, time);
1095 if (site->flags & GTK_DEST_DEFAULT_DROP)
1098 gtk_drag_finish (context,
1099 (selection_data->length >= 0),
1100 (context->action == GDK_ACTION_MOVE),
1104 gtk_widget_unref (drop_widget);
1107 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1108 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1111 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1112 gdk_drag_context_unref (context);
1114 gtk_drag_release_ipc_widget (widget);
1117 /*************************************************************
1118 * gtk_drag_find_widget:
1119 * Recursive callback used to locate widgets for
1120 * DRAG_MOTION and DROP_START events.
1124 *************************************************************/
1127 gtk_drag_find_widget (GtkWidget *widget,
1128 GtkDragFindData *data)
1130 GtkAllocation new_allocation;
1134 new_allocation = widget->allocation;
1136 if (data->found || !GTK_WIDGET_MAPPED (widget))
1139 /* Note that in the following code, we only count the
1140 * position as being inside a WINDOW widget if it is inside
1141 * widget->window; points that are outside of widget->window
1142 * but within the allocation are not counted. This is consistent
1143 * with the way we highlight drag targets.
1145 if (!GTK_WIDGET_NO_WINDOW (widget))
1147 new_allocation.x = 0;
1148 new_allocation.y = 0;
1153 GdkWindow *window = widget->window;
1154 while (window != widget->parent->window)
1156 gint tx, ty, twidth, theight;
1157 gdk_window_get_size (window, &twidth, &theight);
1159 if (new_allocation.x < 0)
1161 new_allocation.width += new_allocation.x;
1162 new_allocation.x = 0;
1164 if (new_allocation.y < 0)
1166 new_allocation.height += new_allocation.y;
1167 new_allocation.y = 0;
1169 if (new_allocation.x + new_allocation.width > twidth)
1170 new_allocation.width = twidth - new_allocation.x;
1171 if (new_allocation.y + new_allocation.height > theight)
1172 new_allocation.height = theight - new_allocation.y;
1174 gdk_window_get_position (window, &tx, &ty);
1175 new_allocation.x += tx;
1177 new_allocation.y += ty;
1180 window = gdk_window_get_parent (window);
1184 if (data->toplevel ||
1185 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1186 (data->x < new_allocation.x + new_allocation.width) &&
1187 (data->y < new_allocation.y + new_allocation.height)))
1189 /* First, check if the drag is in a valid drop site in
1190 * one of our children
1192 if (GTK_IS_CONTAINER (widget))
1194 GtkDragFindData new_data = *data;
1196 new_data.x -= x_offset;
1197 new_data.y -= y_offset;
1198 new_data.found = FALSE;
1199 new_data.toplevel = FALSE;
1201 gtk_container_forall (GTK_CONTAINER (widget),
1202 (GtkCallback)gtk_drag_find_widget,
1205 data->found = new_data.found;
1208 /* If not, and this widget is registered as a drop site, check to
1209 * emit "drag_motion" to check if we are actually in
1213 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1215 data->found = data->callback (widget,
1217 data->x - new_allocation.x,
1218 data->y - new_allocation.y,
1220 /* If so, send a "drag_leave" to the last widget */
1223 if (data->info->widget && data->info->widget != widget)
1225 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1227 data->info->widget = widget;
1234 gtk_drag_proxy_begin (GtkWidget *widget, GtkDragDestInfo *dest_info)
1236 GtkDragSourceInfo *source_info;
1239 source_info = g_new0 (GtkDragSourceInfo, 1);
1240 source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1242 source_info->widget = widget;
1243 gtk_widget_ref (source_info->widget);
1244 source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1245 dest_info->context->targets);
1247 source_info->target_list = gtk_target_list_new (NULL, 0);
1248 tmp_list = dest_info->context->targets;
1251 gtk_target_list_add (source_info->target_list,
1252 GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1253 tmp_list = tmp_list->next;
1256 source_info->proxy_dest = dest_info;
1258 g_dataset_set_data (source_info->context, "gtk-info", source_info);
1260 gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget),
1262 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1265 dest_info->proxy_source = source_info;
1269 gtk_drag_dest_info_destroy (gpointer data)
1271 GtkDragDestInfo *info = data;
1277 gtk_drag_dest_realized (GtkWidget *widget)
1279 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1280 gdk_window_register_dnd (toplevel->window);
1284 gtk_drag_dest_site_destroy (gpointer data)
1286 GtkDragDestSite *site = data;
1288 if (site->target_list)
1289 gtk_target_list_unref (site->target_list);
1295 * Default drag handlers
1298 gtk_drag_dest_leave (GtkWidget *widget,
1299 GdkDragContext *context,
1302 GtkDragDestSite *site;
1304 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1305 g_return_if_fail (site != NULL);
1309 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1311 if (info->proxy_source && !info->dropped)
1312 gdk_drag_abort (info->proxy_source->context, time);
1318 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1319 gtk_drag_unhighlight (widget);
1321 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1322 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1325 site->have_drag = FALSE;
1330 gtk_drag_dest_motion (GtkWidget *widget,
1331 GdkDragContext *context,
1336 GtkDragDestSite *site;
1337 GdkDragAction action = 0;
1340 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1341 g_return_val_if_fail (site != NULL, FALSE);
1346 GdkEvent *current_event;
1347 GdkWindow *dest_window;
1348 GdkDragProtocol proto;
1350 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1352 if (!info->proxy_source)
1353 gtk_drag_proxy_begin (widget, info);
1355 current_event = gtk_get_current_event ();
1357 if (site->proxy_window)
1359 dest_window = site->proxy_window;
1360 proto = site->proxy_protocol;
1364 gdk_drag_find_window (info->proxy_source->context,
1366 current_event->dnd.x_root,
1367 current_event->dnd.y_root,
1368 &dest_window, &proto);
1371 gdk_drag_motion (info->proxy_source->context,
1373 current_event->dnd.x_root,
1374 current_event->dnd.y_root,
1375 context->suggested_action,
1376 context->actions, time);
1378 selection = gdk_drag_get_selection (info->proxy_source->context);
1380 selection != gdk_drag_get_selection (info->context))
1381 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1383 gdk_event_free (current_event);
1388 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1390 if (context->suggested_action & site->actions)
1391 action = context->suggested_action;
1398 if ((site->actions & (1 << i)) &&
1399 (context->actions & (1 << i)))
1407 if (action && gtk_drag_dest_find_target (widget, site, context))
1409 if (!site->have_drag)
1411 site->have_drag = TRUE;
1412 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1413 gtk_drag_highlight (widget);
1416 gdk_drag_status (context, action, time);
1420 gdk_drag_status (context, 0, time);
1425 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1426 context, x, y, time, &retval);
1428 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1432 gtk_drag_dest_drop (GtkWidget *widget,
1433 GdkDragContext *context,
1438 GtkDragDestSite *site;
1439 GtkDragDestInfo *info;
1441 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1442 g_return_val_if_fail (site != NULL, FALSE);
1444 info = g_dataset_get_data (context, "gtk-info");
1445 g_return_val_if_fail (info != NULL, FALSE);
1452 if (info->proxy_source ||
1453 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1455 gtk_drag_drop (info->proxy_source, time);
1459 /* We need to synthesize a motion event, wait for a status,
1460 * and, if we get a good one, do a drop.
1463 GdkEvent *current_event;
1465 GdkWindow *dest_window;
1466 GdkDragProtocol proto;
1468 gtk_drag_proxy_begin (widget, info);
1469 info->proxy_drop_wait = TRUE;
1470 info->proxy_drop_time = time;
1472 current_event = gtk_get_current_event ();
1474 if (site->proxy_window)
1476 dest_window = site->proxy_window;
1477 proto = site->proxy_protocol;
1481 gdk_drag_find_window (info->proxy_source->context,
1483 current_event->dnd.x_root,
1484 current_event->dnd.y_root,
1485 &dest_window, &proto);
1488 gdk_drag_motion (info->proxy_source->context,
1490 current_event->dnd.x_root,
1491 current_event->dnd.y_root,
1492 context->suggested_action,
1493 context->actions, time);
1495 selection = gdk_drag_get_selection (info->proxy_source->context);
1497 selection != gdk_drag_get_selection (info->context))
1498 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1500 gdk_event_free (current_event);
1510 if (site->flags & GTK_DEST_DEFAULT_DROP)
1512 GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
1514 if (target == GDK_NONE)
1517 gtk_drag_get_data (widget, context, target, time);
1520 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1521 context, x, y, time, &retval);
1523 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1531 /*************************************************************
1532 * gtk_drag_begin: Start a drag operation
1535 * widget: Widget from which drag starts
1536 * handlers: List of handlers to supply the data for the drag
1537 * button: Button user used to start drag
1538 * time: Time of event starting drag
1541 *************************************************************/
1544 gtk_drag_begin (GtkWidget *widget,
1545 GtkTargetList *target_list,
1546 GdkDragAction actions,
1550 GtkDragSourceInfo *info;
1551 GList *targets = NULL;
1553 guint32 time = GDK_CURRENT_TIME;
1554 GdkDragAction possible_actions, suggested_action;
1556 g_return_val_if_fail (widget != NULL, NULL);
1557 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1558 g_return_val_if_fail (target_list != NULL, NULL);
1561 time = gdk_event_get_time (event);
1563 info = g_new0 (GtkDragSourceInfo, 1);
1564 info->ipc_widget = gtk_drag_get_ipc_widget ();
1565 source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
1567 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1569 tmp_list = g_list_last (target_list->list);
1572 GtkTargetPair *pair = tmp_list->data;
1573 targets = g_list_prepend (targets,
1574 GINT_TO_POINTER (pair->target));
1575 tmp_list = tmp_list->prev;
1578 info->widget = widget;
1579 gtk_widget_ref (info->widget);
1581 info->context = gdk_drag_begin (info->ipc_widget->window, targets);
1582 g_list_free (targets);
1584 g_dataset_set_data (info->context, "gtk-info", info);
1586 info->button = button;
1587 info->target_list = target_list;
1588 gtk_target_list_ref (target_list);
1590 info->possible_actions = actions;
1592 info->cursor = NULL;
1593 info->status = GTK_DRAG_STATUS_DRAG;
1594 info->last_event = NULL;
1595 info->selections = NULL;
1596 info->icon_window = NULL;
1598 gtk_drag_get_event_actions (event, info->button, actions,
1599 &suggested_action, &possible_actions);
1602 info->cursor = gtk_drag_get_cursor (suggested_action);
1604 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1605 * the drag icon, it will be in the right place
1607 if (event->type == GDK_MOTION_NOTIFY)
1609 info->cur_x = event->motion.x_root;
1610 info->cur_y = event->motion.y_root;
1615 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x, &y, NULL);
1621 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1624 /* We use a GTK grab here to override any grabs that the widget
1625 * we are dragging from might have held
1628 gtk_grab_add (info->ipc_widget);
1629 gdk_pointer_grab (info->ipc_widget->window, FALSE,
1630 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1631 GDK_BUTTON_RELEASE_MASK, NULL,
1632 info->cursor, time);
1634 if (event->type == GDK_MOTION_NOTIFY)
1635 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1637 info->start_x = info->cur_x;
1638 info->start_y = info->cur_y;
1640 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1641 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1642 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1643 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1644 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1645 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1647 return info->context;
1650 /*************************************************************
1651 * gtk_drag_source_set:
1652 * Register a drop site, and possibly add default behaviors.
1655 * start_button_mask: Mask of allowed buttons to start drag
1656 * targets: Table of targets for this source
1658 * actions: Actions allowed for this source
1660 *************************************************************/
1663 gtk_drag_source_set (GtkWidget *widget,
1664 GdkModifierType start_button_mask,
1665 const GtkTargetEntry *targets,
1667 GdkDragAction actions)
1669 GtkDragSourceSite *site;
1671 g_return_if_fail (widget != NULL);
1673 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1675 gtk_widget_add_events (widget,
1676 gtk_widget_get_events (widget) |
1677 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1678 GDK_BUTTON_MOTION_MASK);
1682 if (site->target_list)
1683 gtk_target_list_unref (site->target_list);
1687 site = g_new0 (GtkDragSourceSite, 1);
1689 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1690 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1692 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1693 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1696 gtk_object_set_data_full (GTK_OBJECT (widget),
1698 site, gtk_drag_source_site_destroy);
1701 site->start_button_mask = start_button_mask;
1704 site->target_list = gtk_target_list_new (targets, n_targets);
1706 site->target_list = NULL;
1708 site->actions = actions;
1712 /*************************************************************
1713 * gtk_drag_source_unset
1714 * Unregister this widget as a drag source.
1718 *************************************************************/
1721 gtk_drag_source_unset (GtkWidget *widget)
1723 GtkDragSourceSite *site;
1725 g_return_if_fail (widget != NULL);
1727 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1731 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1732 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1736 /*************************************************************
1737 * gtk_drag_source_set_icon:
1738 * Set an icon for drags from this source.
1740 * colormap: Colormap for this icon
1744 *************************************************************/
1747 gtk_drag_source_set_icon (GtkWidget *widget,
1748 GdkColormap *colormap,
1752 GtkDragSourceSite *site;
1754 g_return_if_fail (widget != NULL);
1756 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1757 g_return_if_fail (site != NULL);
1760 gdk_colormap_unref (site->colormap);
1762 gdk_pixmap_unref (site->pixmap);
1764 gdk_pixmap_unref (site->mask);
1766 site->colormap = colormap;
1768 gdk_colormap_ref (colormap);
1770 site->pixmap = pixmap;
1772 gdk_pixmap_ref (pixmap);
1776 gdk_pixmap_ref (mask);
1779 /*************************************************************
1780 * gtk_drag_set_icon_widget:
1781 * Set a widget as the icon for a drag.
1788 *************************************************************/
1791 gtk_drag_set_icon_widget (GdkDragContext *context,
1796 GtkDragSourceInfo *info;
1798 g_return_if_fail (context != NULL);
1799 g_return_if_fail (widget != NULL);
1801 info = g_dataset_get_data (context, "gtk-info");
1802 gtk_drag_remove_icon (info);
1804 info->icon_window = widget;
1805 info->hot_x = hot_x;
1806 info->hot_y = hot_y;
1810 gtk_widget_set_uposition (widget,
1811 info->cur_x - info->hot_x,
1812 info->cur_y - info->hot_y);
1813 gtk_widget_ref (widget);
1814 gdk_window_raise (widget->window);
1815 gtk_widget_show (widget);
1819 /*************************************************************
1820 * gtk_drag_set_icon_pixmap:
1821 * Set a widget as the icon for a drag.
1824 * colormap: Colormap for the icon window.
1830 *************************************************************/
1833 gtk_drag_set_icon_pixmap (GdkDragContext *context,
1834 GdkColormap *colormap,
1843 g_return_if_fail (context != NULL);
1844 g_return_if_fail (colormap != NULL);
1845 g_return_if_fail (pixmap != NULL);
1847 gdk_window_get_size (pixmap, &width, &height);
1849 gtk_widget_push_visual (gdk_colormap_get_visual(colormap));
1850 gtk_widget_push_colormap (colormap);
1852 window = gtk_window_new (GTK_WINDOW_POPUP);
1853 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1854 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
1856 gtk_widget_pop_visual ();
1857 gtk_widget_pop_colormap ();
1859 gtk_widget_set_usize (window, width, height);
1860 gtk_widget_realize (window);
1862 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1865 gtk_widget_shape_combine_mask (window, mask, 0, 0);
1867 gtk_drag_set_icon_widget (context, window, hot_x, hot_y);
1870 /*************************************************************
1871 * gtk_drag_set_icon_default:
1872 * Set the icon for a drag to the default icon.
1876 *************************************************************/
1879 gtk_drag_set_icon_default (GdkDragContext *context)
1881 g_return_if_fail (context != NULL);
1883 if (!default_icon_pixmap)
1885 default_icon_colormap = gdk_colormap_get_system ();
1886 default_icon_pixmap =
1887 gdk_pixmap_colormap_create_from_xpm_d (NULL,
1888 default_icon_colormap,
1890 NULL, drag_default_xpm);
1891 default_icon_hot_x = -2;
1892 default_icon_hot_y = -2;
1895 gtk_drag_set_icon_pixmap (context,
1896 default_icon_colormap,
1897 default_icon_pixmap,
1900 default_icon_hot_y);
1903 /*************************************************************
1904 * gtk_drag_set_default_icon:
1905 * Set a default icon for all drags as a pixmap.
1907 * colormap: Colormap for the icon window.
1913 *************************************************************/
1916 gtk_drag_set_default_icon (GdkColormap *colormap,
1922 g_return_if_fail (colormap != NULL);
1923 g_return_if_fail (pixmap != NULL);
1925 if (default_icon_colormap)
1926 gdk_colormap_unref (default_icon_colormap);
1927 if (default_icon_pixmap)
1928 gdk_pixmap_unref (default_icon_pixmap);
1929 if (default_icon_mask)
1930 gdk_pixmap_unref (default_icon_pixmap);
1932 default_icon_colormap = colormap;
1933 gdk_colormap_ref (colormap);
1935 default_icon_pixmap = pixmap;
1936 gdk_pixmap_ref (pixmap);
1938 default_icon_mask = mask;
1940 gdk_pixmap_ref (mask);
1942 default_icon_hot_x = hot_x;
1943 default_icon_hot_y = hot_y;
1947 /*************************************************************
1948 * gtk_drag_source_handle_event:
1949 * Called from widget event handling code on Drag events
1953 * toplevel: Toplevel widget that received the event
1956 *************************************************************/
1959 gtk_drag_source_handle_event (GtkWidget *widget,
1962 GtkDragSourceInfo *info;
1963 GdkDragContext *context;
1965 g_return_if_fail (widget != NULL);
1966 g_return_if_fail (event != NULL);
1968 context = event->dnd.context;
1969 info = g_dataset_get_data (context, "gtk-info");
1973 switch (event->type)
1975 case GDK_DRAG_STATUS:
1979 if (info->proxy_dest)
1981 if (!event->dnd.send_event)
1983 if (info->proxy_dest->proxy_drop_wait)
1985 /* Aha - we can finally pass the MOTIF DROP on... */
1986 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
1990 gdk_drag_status (info->proxy_dest->context,
1991 event->dnd.context->action,
1998 cursor = gtk_drag_get_cursor (event->dnd.context->action);
1999 if (info->cursor != cursor)
2001 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window),
2002 PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
2003 ((GdkCursorPrivate *)cursor)->xcursor,
2005 info->cursor = cursor;
2008 if (info->last_event)
2010 gtk_drag_motion_cb (info->widget,
2011 (GdkEventMotion *)info->last_event,
2013 info->last_event = NULL;
2019 case GDK_DROP_FINISHED:
2020 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2023 g_assert_not_reached();
2027 /*************************************************************
2028 * gtk_drag_source_check_selection:
2029 * Check if we've set up handlers/claimed the selection
2030 * for a given drag. If not, add them.
2034 *************************************************************/
2037 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2043 tmp_list = info->selections;
2046 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
2048 tmp_list = tmp_list->next;
2051 gtk_selection_owner_set (info->ipc_widget, selection, time);
2052 info->selections = g_list_prepend (info->selections,
2053 GUINT_TO_POINTER (selection));
2055 tmp_list = info->target_list->list;
2058 GtkTargetPair *pair = tmp_list->data;
2060 gtk_selection_add_target (info->ipc_widget,
2064 tmp_list = tmp_list->next;
2067 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2069 gtk_selection_add_target (info->ipc_widget,
2071 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2072 TARGET_MOTIF_SUCCESS);
2073 gtk_selection_add_target (info->ipc_widget,
2075 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2076 TARGET_MOTIF_FAILURE);
2079 gtk_selection_add_target (info->ipc_widget,
2081 gdk_atom_intern ("DELETE", FALSE),
2085 /*************************************************************
2086 * gtk_drag_drop_finished:
2087 * Clean up from the drag, and display snapback, if necessary.
2093 *************************************************************/
2096 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2100 gtk_drag_source_release_selections (info, time);
2102 if (info->proxy_dest)
2104 /* The time from the event isn't reliable for Xdnd drags */
2105 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2106 info->proxy_dest->proxy_drop_time);
2107 gtk_drag_source_info_destroy (info);
2113 gtk_drag_source_info_destroy (info);
2117 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2121 anim->n_steps = MAX (info->cur_x - info->start_x,
2122 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2123 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2124 if (info->icon_window)
2126 gtk_widget_show(info->icon_window);
2127 gdk_window_raise (info->icon_window->window);
2130 /* Mark the context as dead, so if the destination decides
2131 * to respond really late, we still are OK.
2133 g_dataset_set_data (info->context, "gtk-info", NULL);
2134 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2140 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2143 GList *tmp_list = info->selections;
2146 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2147 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2148 gtk_selection_owner_set (NULL, selection, time);
2149 tmp_list = tmp_list->next;
2152 g_list_free (info->selections);
2153 info->selections = NULL;
2156 /*************************************************************
2158 * Send a drop event.
2162 *************************************************************/
2165 gtk_drag_drop (GtkDragSourceInfo *info, guint32 time)
2167 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2169 GtkSelectionData selection_data;
2171 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2173 tmp_list = info->target_list->list;
2176 GtkTargetPair *pair = tmp_list->data;
2178 if (pair->target == target)
2180 selection_data.selection = GDK_NONE;
2181 selection_data.target = target;
2182 selection_data.data = NULL;
2183 selection_data.length = -1;
2185 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2186 info->context, &selection_data,
2190 /* FIXME: Should we check for length >= 0 here? */
2191 gtk_drag_drop_finished (info, TRUE, time);
2194 tmp_list = tmp_list->next;
2196 gtk_drag_drop_finished (info, FALSE, time);
2200 if (info->icon_window)
2201 gtk_widget_hide (info->icon_window);
2203 gdk_drag_drop (info->context, time);
2204 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2205 gtk_drag_abort_timeout,
2211 * Source side callbacks.
2215 gtk_drag_source_event_cb (GtkWidget *widget,
2219 GtkDragSourceSite *site;
2220 site = (GtkDragSourceSite *)data;
2222 switch (event->type)
2224 case GDK_BUTTON_PRESS:
2225 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2227 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2228 site->x = event->button.x;
2229 site->y = event->button.y;
2233 case GDK_BUTTON_RELEASE:
2234 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2236 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2240 case GDK_MOTION_NOTIFY:
2241 if (site->state & event->motion.state & site->start_button_mask)
2243 /* FIXME: This is really broken and can leave us
2249 if (site->state & event->motion.state &
2250 GDK_BUTTON1_MASK << (i - 1))
2254 if (MAX (abs(site->x - event->motion.x),
2255 abs(site->y - event->motion.y)) > 3)
2257 GtkDragSourceInfo *info;
2258 GdkDragContext *context;
2261 context = gtk_drag_begin (widget, site->target_list,
2266 info = g_dataset_get_data (context, "gtk-info");
2268 if (!info->icon_window)
2271 gtk_drag_set_icon_pixmap (context,
2274 site->mask, -2, -2);
2276 gtk_drag_set_icon_default (context);
2284 default: /* hit for 2/3BUTTON_PRESS */
2291 gtk_drag_source_site_destroy (gpointer data)
2293 GtkDragSourceSite *site = data;
2295 if (site->target_list)
2296 gtk_target_list_unref (site->target_list);
2299 gdk_pixmap_unref (site->pixmap);
2302 gdk_pixmap_unref (site->mask);
2308 gtk_drag_selection_get (GtkWidget *widget,
2309 GtkSelectionData *selection_data,
2314 GtkDragSourceInfo *info = data;
2315 static GdkAtom null_atom = GDK_NONE;
2319 null_atom = gdk_atom_intern ("NULL", FALSE);
2324 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2327 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2329 case TARGET_MOTIF_SUCCESS:
2330 gtk_drag_drop_finished (info, TRUE, time);
2331 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2333 case TARGET_MOTIF_FAILURE:
2334 gtk_drag_drop_finished (info, FALSE, time);
2335 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2338 if (info->proxy_dest)
2340 /* This is sort of dangerous and needs to be thought
2343 info->proxy_dest->proxy_data = selection_data;
2344 gtk_drag_get_data (info->widget,
2345 info->proxy_dest->context,
2346 selection_data->target,
2349 info->proxy_dest->proxy_data = NULL;
2353 if (gtk_target_list_find (info->target_list,
2354 selection_data->target,
2357 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2369 gtk_drag_anim_timeout (gpointer data)
2371 GtkDragAnim *anim = data;
2375 GDK_THREADS_ENTER ();
2377 if (anim->step == anim->n_steps)
2379 gtk_drag_source_info_destroy (anim->info);
2386 x = (anim->info->start_x * (anim->step + 1) +
2387 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2388 y = (anim->info->start_y * (anim->step + 1) +
2389 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2390 if (anim->info->icon_window)
2391 gtk_widget_set_uposition (anim->info->icon_window,
2392 x - anim->info->hot_x,
2393 y - anim->info->hot_y);
2400 GDK_THREADS_LEAVE ();
2406 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2408 if (info->icon_window)
2410 gtk_widget_hide (info->icon_window);
2411 gtk_widget_unref (info->icon_window);
2413 info->icon_window = NULL;
2418 gtk_drag_source_info_destroy (gpointer data)
2420 GtkDragSourceInfo *info = data;
2422 gtk_drag_remove_icon (data);
2424 if (!info->proxy_dest)
2425 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2429 gtk_widget_unref (info->widget);
2431 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2432 gtk_selection_remove_all (info->ipc_widget);
2433 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2434 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2435 gtk_drag_release_ipc_widget (info->ipc_widget);
2437 gtk_target_list_unref (info->target_list);
2439 g_dataset_set_data (info->context, "gtk-info", NULL);
2440 gdk_drag_context_unref (info->context);
2442 if (info->drop_timeout)
2443 gtk_timeout_remove (info->drop_timeout);
2448 /*************************************************************
2449 * gtk_drag_motion_cb:
2450 * "motion_notify_event" callback during drag.
2454 *************************************************************/
2457 gtk_drag_motion_cb (GtkWidget *widget,
2458 GdkEventMotion *event,
2461 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2463 GdkDragAction action;
2464 GdkDragAction possible_actions;
2465 GdkWindow *window = NULL;
2466 GdkWindow *dest_window;
2467 GdkDragProtocol protocol;
2468 gint x_root, y_root;
2472 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2473 event->x_root = x_root;
2474 event->y_root = y_root;
2477 gtk_drag_get_event_actions ((GdkEvent *)event,
2479 info->possible_actions,
2480 &action, &possible_actions);
2482 info->cur_x = event->x_root;
2483 info->cur_y = event->y_root;
2485 if (info->icon_window)
2487 gdk_window_raise (info->icon_window->window);
2488 gtk_widget_set_uposition (info->icon_window,
2489 info->cur_x - info->hot_x,
2490 info->cur_y - info->hot_y);
2491 window = info->icon_window->window;
2494 gdk_drag_find_window (info->context,
2495 window, event->x_root, event->y_root,
2496 &dest_window, &protocol);
2498 if (gdk_drag_motion (info->context, dest_window, protocol,
2499 event->x_root, event->y_root, action,
2503 if (info->last_event)
2504 gdk_event_free ((GdkEvent *)info->last_event);
2506 info->last_event = gdk_event_copy ((GdkEvent *)event);
2510 gdk_window_unref (dest_window);
2512 selection = gdk_drag_get_selection (info->context);
2514 gtk_drag_source_check_selection (info, selection, event->time);
2517 /* We ignore the response, so we can respond precisely to the drop
2520 gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2526 /*************************************************************
2527 * gtk_drag_motion_cb:
2528 * "button_release_event" callback during drag.
2532 *************************************************************/
2535 gtk_drag_button_release_cb (GtkWidget *widget,
2536 GdkEventButton *event,
2539 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2540 GtkWidget *source_widget = info->widget;
2541 GdkEvent send_event;
2543 gtk_widget_ref (source_widget);
2545 if (event->button != info->button)
2548 gdk_pointer_ungrab (event->time);
2550 gtk_grab_remove (widget);
2551 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
2552 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2554 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
2555 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2558 if ((info->context->action != 0) && (info->context->dest_window != NULL))
2560 gtk_drag_drop (info, event->time);
2564 gdk_drag_abort (info->context, event->time);
2565 gtk_drag_drop_finished (info, FALSE, event->time);
2568 /* Send on a release pair to the the original
2569 * widget to convince it to release its grab. We need to
2570 * call gtk_propagate_event() here, instead of
2571 * gtk_widget_event() because widget like GtkList may
2572 * expect propagation.
2575 send_event.button.type = GDK_BUTTON_RELEASE;
2576 send_event.button.window = GDK_ROOT_PARENT ();
2577 send_event.button.send_event = TRUE;
2578 send_event.button.time = event->time;
2579 send_event.button.x = 0;
2580 send_event.button.y = 0;
2581 send_event.button.pressure = 0.;
2582 send_event.button.xtilt = 0.;
2583 send_event.button.ytilt = 0.;
2584 send_event.button.state = event->state;
2585 send_event.button.button = event->button;
2586 send_event.button.source = GDK_SOURCE_PEN;
2587 send_event.button.deviceid = GDK_CORE_POINTER;
2588 send_event.button.x_root = 0;
2589 send_event.button.y_root = 0;
2591 gtk_propagate_event (source_widget, &send_event);
2593 gtk_widget_unref (source_widget);
2599 gtk_drag_abort_timeout (gpointer data)
2601 GtkDragSourceInfo *info = data;
2602 guint32 time = GDK_CURRENT_TIME;
2604 if (info->proxy_dest)
2605 time = info->proxy_dest->proxy_drop_time;
2607 info->drop_timeout = 0;
2608 gtk_drag_drop_finished (info, FALSE, time);