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 */
85 struct _GtkDragDestSite
87 GtkDestDefaults flags;
88 GtkTargetList *target_list;
89 GdkDragAction actions;
90 GdkWindow *proxy_window;
91 GdkDragProtocol proxy_protocol;
92 gboolean do_proxy : 1;
93 gboolean proxy_coords : 1;
94 gboolean have_drag : 1;
97 struct _GtkDragDestInfo
99 GtkWidget *widget; /* Widget in which drag is in */
100 GdkDragContext *context; /* Drag context */
101 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
102 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
103 gboolean dropped : 1; /* Set after we receive a drop */
104 guint32 proxy_drop_time; /* Timestamp for proxied drop */
105 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
106 * status reply before sending
109 gint drop_x, drop_y; /* Position of drop */
112 #define DROP_ABORT_TIME 300000
114 #define ANIM_STEP_TIME 50
115 #define ANIM_STEP_LENGTH 50
116 #define ANIM_MIN_STEPS 5
117 #define ANIM_MAX_STEPS 10
121 GtkDragSourceInfo *info;
126 struct _GtkDragFindData
130 GdkDragContext *context;
131 GtkDragDestInfo *info;
134 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
135 gint x, gint y, guint32 time);
139 /* Enumeration for some targets we handle internally */
143 TARGET_MOTIF_SUCCESS = 0x40000000,
144 TARGET_MOTIF_FAILURE,
150 static GdkPixmap *default_icon_pixmap = NULL;
151 static GdkPixmap *default_icon_mask = NULL;
152 static GdkColormap *default_icon_colormap = NULL;
153 static gint default_icon_hot_x;
154 static gint default_icon_hot_y;
156 /* Forward declarations */
157 static void gtk_drag_get_event_actions (GdkEvent *event,
159 GdkDragAction actions,
160 GdkDragAction *suggested_action,
161 GdkDragAction *possible_actions);
162 static GdkCursor* gtk_drag_get_cursor (GdkDragAction action);
163 static GtkWidget* gtk_drag_get_ipc_widget (void);
164 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
166 static void gtk_drag_highlight_paint (GtkWidget *widget);
167 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
168 GdkEventExpose *event,
172 static GdkAtom gtk_drag_dest_find_target (GtkWidget *widget,
173 GtkDragDestSite *site,
174 GdkDragContext *context);
175 static void gtk_drag_selection_received (GtkWidget *widget,
176 GtkSelectionData *selection_data,
179 static void gtk_drag_find_widget (GtkWidget *widget,
180 GtkDragFindData *data);
181 static void gtk_drag_proxy_begin (GtkWidget *widget,
182 GtkDragDestInfo *dest_info);
183 static void gtk_drag_dest_info_destroy (gpointer data);
184 static void gtk_drag_dest_realized (GtkWidget *widget);
185 static void gtk_drag_dest_site_destroy (gpointer data);
186 static void gtk_drag_dest_leave (GtkWidget *widget,
187 GdkDragContext *context,
189 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
190 GdkDragContext *context,
194 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
195 GdkDragContext *context,
200 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
203 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
205 static void gtk_drag_drop (GtkDragSourceInfo *info,
207 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
211 static gint gtk_drag_source_event_cb (GtkWidget *widget,
214 static void gtk_drag_source_site_destroy (gpointer data);
215 static void gtk_drag_selection_get (GtkWidget *widget,
216 GtkSelectionData *selection_data,
220 static gint gtk_drag_anim_timeout (gpointer data);
221 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
222 static void gtk_drag_source_info_destroy (gpointer data);
223 static gint gtk_drag_motion_cb (GtkWidget *widget,
224 GdkEventMotion *event,
226 static gint gtk_drag_button_release_cb (GtkWidget *widget,
227 GdkEventButton *event,
229 static gint gtk_drag_abort_timeout (gpointer data);
231 /************************
232 * Cursor and Icon data *
233 ************************/
235 #define action_ask_width 16
236 #define action_ask_height 16
237 static const gchar action_ask_bits[] = {
238 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
239 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
240 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
242 #define action_ask_mask_width 16
243 #define action_ask_mask_height 16
244 static const gchar action_ask_mask_bits[] = {
245 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
246 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
247 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
249 #define action_copy_width 16
250 #define action_copy_height 16
251 static const gchar action_copy_bits[] = {
252 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
253 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
254 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
256 #define action_copy_mask_width 16
257 #define action_copy_mask_height 16
258 static const gchar action_copy_mask_bits[] = {
259 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
260 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
261 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
263 #define action_move_width 16
264 #define action_move_height 16
265 static const gchar action_move_bits[] = {
266 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
267 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
268 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
270 #define action_move_mask_width 16
271 #define action_move_mask_height 16
272 static const gchar action_move_mask_bits[] = {
273 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
274 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
275 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
277 #define action_link_width 16
278 #define action_link_height 16
279 static const gchar action_link_bits[] = {
280 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
281 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
282 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
284 #define action_link_mask_width 16
285 #define action_link_mask_height 16
286 static const gchar action_link_mask_bits[] = {
287 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
288 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
289 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
291 #define action_none_width 16
292 #define action_none_height 16
293 static const gchar action_none_bits[] = {
294 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
295 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
296 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
298 #define action_none_mask_width 16
299 #define action_none_mask_height 16
300 static const gchar action_none_mask_bits[] = {
301 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
302 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
303 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
305 #define CURSOR_WIDTH 16
306 #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 gchar *drag_default_xpm[] = {
339 " ...+++++++++++.. ",
340 " ..+.++++++++++++.. ",
341 " .++.++++++++++++.. ",
342 " .+++.++++++++++++.. ",
343 " .++++.++++++++++++. ",
344 " .+++.+++++++++++++.. ",
345 " .++.+++++++++++++++.. ",
346 " .+.+++++++++++++++++.. ",
347 " ..+++++++++++++++++++.. ",
348 " ..++++++++++++++++++++. ",
349 " .++++++++++++++++++++.. ",
350 " ..+++++++++++++++++.. ",
351 " .++++++++++++++++.. ",
352 " ..+++++++++++++... ",
365 /*********************
366 * Utility functions *
367 *********************/
369 /*************************************************************
370 * gtk_drag_get_ipc_widget:
371 * Return a invisible, off-screen, override-redirect
376 *************************************************************/
379 gtk_drag_get_ipc_widget (void)
385 GSList *tmp = drag_widgets;
387 result = drag_widgets->data;
388 drag_widgets = drag_widgets->next;
389 g_slist_free_1 (tmp);
393 result = gtk_invisible_new ();
394 gtk_widget_show (result);
400 /****************************************************************
401 * gtk_drag_release_ipc_widget:
402 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
404 * widget: the widget to release.
406 ****************************************************************/
409 gtk_drag_release_ipc_widget (GtkWidget *widget)
411 drag_widgets = g_slist_prepend (drag_widgets, widget);
415 gtk_drag_get_event_actions (GdkEvent *event,
417 GdkDragAction actions,
418 GdkDragAction *suggested_action,
419 GdkDragAction *possible_actions)
421 *suggested_action = 0;
422 *possible_actions = 0;
426 GdkModifierType state = 0;
430 case GDK_MOTION_NOTIFY:
431 state = event->motion.state;
433 case GDK_BUTTON_PRESS:
434 case GDK_2BUTTON_PRESS:
435 case GDK_3BUTTON_PRESS:
436 case GDK_BUTTON_RELEASE:
437 state = event->button.state;
440 case GDK_KEY_RELEASE:
441 state = event->key.state;
443 case GDK_ENTER_NOTIFY:
444 case GDK_LEAVE_NOTIFY:
445 state = event->crossing.state;
451 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
453 *suggested_action = GDK_ACTION_ASK;
454 *possible_actions = actions;
456 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
458 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
460 if (actions & GDK_ACTION_LINK)
462 *suggested_action = GDK_ACTION_LINK;
463 *possible_actions = GDK_ACTION_LINK;
466 else if (state & GDK_CONTROL_MASK)
468 if (actions & GDK_ACTION_COPY)
470 *suggested_action = GDK_ACTION_COPY;
471 *possible_actions = GDK_ACTION_COPY;
477 if (actions & GDK_ACTION_MOVE)
479 *suggested_action = GDK_ACTION_MOVE;
480 *possible_actions = GDK_ACTION_MOVE;
487 *possible_actions = actions;
489 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
490 *suggested_action = GDK_ACTION_ASK;
491 else if (actions & GDK_ACTION_COPY)
492 *suggested_action = GDK_ACTION_COPY;
493 else if (actions & GDK_ACTION_MOVE)
494 *suggested_action = GDK_ACTION_MOVE;
495 else if (actions & GDK_ACTION_LINK)
496 *suggested_action = GDK_ACTION_LINK;
504 gtk_drag_get_cursor (GdkDragAction action)
508 for (i = 0 ; i < n_drag_cursors - 1; i++)
509 if (drag_cursors[i].action == action)
512 if (drag_cursors[i].cursor == NULL)
517 gdk_bitmap_create_from_data (NULL,
518 drag_cursors[i].bits,
519 CURSOR_WIDTH, CURSOR_HEIGHT);
521 gdk_bitmap_create_from_data (NULL,
522 drag_cursors[i].mask,
523 CURSOR_WIDTH, CURSOR_HEIGHT);
525 gdk_color_white (gdk_colormap_get_system(), &bg);
526 gdk_color_black (gdk_colormap_get_system(), &fg);
528 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
530 gdk_pixmap_unref (pixmap);
531 gdk_pixmap_unref (mask);
534 return drag_cursors[i].cursor;
537 /********************
539 ********************/
541 /*************************************************************
543 * Get the data for a drag or drop
545 * context - drag context
546 * target - format to retrieve the data in.
547 * time - timestamp of triggering event.
550 *************************************************************/
553 gtk_drag_get_data (GtkWidget *widget,
554 GdkDragContext *context,
558 GtkWidget *selection_widget;
560 g_return_if_fail (widget != NULL);
561 g_return_if_fail (context != NULL);
563 selection_widget = gtk_drag_get_ipc_widget ();
565 gdk_drag_context_ref (context);
566 gtk_widget_ref (widget);
568 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
569 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
571 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
573 gtk_selection_convert (selection_widget,
574 gdk_drag_get_selection (context),
580 /*************************************************************
581 * gtk_drag_get_source_widget:
582 * Get the widget the was the source of this drag, if
583 * the drag originated from this application.
585 * context: The drag context for this drag
587 * The source widget, or NULL if the drag originated from
588 * a different application.
589 *************************************************************/
592 gtk_drag_get_source_widget (GdkDragContext *context)
596 tmp_list = source_widgets;
599 GtkWidget *ipc_widget = tmp_list->data;
601 if (ipc_widget->window == context->source_window)
603 GtkDragSourceInfo *info;
605 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
607 return info ? info->widget : NULL;
610 tmp_list = tmp_list->next;
616 /*************************************************************
618 * Notify the drag source that the transfer of data
621 * context: The drag context for this drag
622 * success: Was the data successfully transferred?
623 * time: The timestamp to use when notifying the destination.
625 *************************************************************/
628 gtk_drag_finish (GdkDragContext *context,
633 GdkAtom target = GDK_NONE;
635 g_return_if_fail (context != NULL);
639 target = gdk_atom_intern ("DELETE", FALSE);
641 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
643 target = gdk_atom_intern (success ?
644 "XmTRANSFER_SUCCESS" :
645 "XmTRANSFER_FAILURE",
649 if (target != GDK_NONE)
651 GtkWidget *selection_widget = gtk_drag_get_ipc_widget ();
653 gdk_drag_context_ref (context);
655 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
656 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
657 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
660 gtk_selection_convert (selection_widget,
661 gdk_drag_get_selection (context),
667 gdk_drop_finish (context, success, time);
670 /*************************************************************
671 * gtk_drag_highlight_paint:
672 * Paint a highlight indicating drag status onto the widget.
676 *************************************************************/
679 gtk_drag_highlight_paint (GtkWidget *widget)
681 gint x, y, width, height;
683 g_return_if_fail (widget != NULL);
685 if (GTK_WIDGET_DRAWABLE (widget))
687 if (GTK_WIDGET_NO_WINDOW (widget))
689 x = widget->allocation.x;
690 y = widget->allocation.y;
691 width = widget->allocation.width;
692 height = widget->allocation.height;
698 gdk_window_get_size (widget->window, &width, &height);
701 gtk_draw_shadow (widget->style, widget->window,
702 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
703 x, y, width, height);
705 gdk_draw_rectangle (widget->window,
706 widget->style->black_gc,
708 x, y, width - 1, height - 1);
712 /*************************************************************
713 * gtk_drag_highlight_expose:
714 * Callback for expose_event for highlighted widgets.
720 *************************************************************/
723 gtk_drag_highlight_expose (GtkWidget *widget,
724 GdkEventExpose *event,
727 gtk_drag_highlight_paint (widget);
731 /*************************************************************
732 * gtk_drag_highlight:
733 * Highlight the given widget in the default manner.
737 *************************************************************/
740 gtk_drag_highlight (GtkWidget *widget)
742 gtk_signal_connect_after (GTK_OBJECT (widget), "draw",
743 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
745 gtk_signal_connect (GTK_OBJECT (widget), "expose_event",
746 GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
749 gtk_widget_queue_draw (widget);
752 /*************************************************************
753 * gtk_drag_unhighlight:
754 * Refresh the given widget to remove the highlight.
758 *************************************************************/
761 gtk_drag_unhighlight (GtkWidget *widget)
763 g_return_if_fail (widget != NULL);
765 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
766 GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
768 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
769 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
772 gtk_widget_queue_clear (widget);
775 /*************************************************************
777 * Register a drop site, and possibly add default behaviors.
780 * flags: Which types of default drag behavior to use
781 * targets: Table of targets that can be accepted
782 * n_targets: Number of of entries in targets
785 *************************************************************/
788 gtk_drag_dest_set (GtkWidget *widget,
789 GtkDestDefaults flags,
790 const GtkTargetEntry *targets,
792 GdkDragAction actions)
794 GtkDragDestSite *site;
796 g_return_if_fail (widget != NULL);
798 /* HACK, do this in the destroy */
799 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
801 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
803 if (GTK_WIDGET_REALIZED (widget))
804 gtk_drag_dest_realized (widget);
806 gtk_signal_connect (GTK_OBJECT (widget), "realize",
807 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
809 site = g_new (GtkDragDestSite, 1);
812 site->have_drag = FALSE;
814 site->target_list = gtk_target_list_new (targets, n_targets);
816 site->target_list = NULL;
818 site->actions = actions;
819 site->do_proxy = FALSE;
821 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
822 site, gtk_drag_dest_site_destroy);
825 /*************************************************************
826 * gtk_drag_dest_set_proxy:
827 * Set up this widget to proxy drags elsewhere.
830 * proxy_window: window to which forward drag events
831 * protocol: Drag protocol which the dest widget accepts
832 * use_coordinates: If true, send the same coordinates to the
833 * destination, because it is a embedded
836 *************************************************************/
839 gtk_drag_dest_set_proxy (GtkWidget *widget,
840 GdkWindow *proxy_window,
841 GdkDragProtocol protocol,
842 gboolean use_coordinates)
844 GtkDragDestSite *site;
846 g_return_if_fail (widget != NULL);
848 /* HACK, do this in the destroy */
849 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
851 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
853 if (GTK_WIDGET_REALIZED (widget))
854 gtk_drag_dest_realized (widget);
856 gtk_signal_connect (GTK_OBJECT (widget), "realize",
857 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
859 site = g_new (GtkDragDestSite, 1);
862 site->have_drag = FALSE;
863 site->target_list = NULL;
865 site->proxy_window = proxy_window;
867 gdk_window_ref (proxy_window);
868 site->do_proxy = TRUE;
869 site->proxy_protocol = protocol;
870 site->proxy_coords = use_coordinates;
872 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
873 site, gtk_drag_dest_site_destroy);
876 /*************************************************************
877 * gtk_drag_dest_unset
878 * Unregister this widget as a drag target.
882 *************************************************************/
885 gtk_drag_dest_unset (GtkWidget *widget)
887 g_return_if_fail (widget != NULL);
889 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
892 /*************************************************************
893 * gtk_drag_dest_handle_event:
894 * Called from widget event handling code on Drag events
898 * toplevel: Toplevel widget that received the event
901 *************************************************************/
904 gtk_drag_dest_handle_event (GtkWidget *toplevel,
907 GtkDragDestInfo *info;
908 GdkDragContext *context;
910 g_return_if_fail (toplevel != NULL);
911 g_return_if_fail (event != NULL);
913 context = event->dnd.context;
915 info = g_dataset_get_data (context, "gtk-info");
918 info = g_new (GtkDragDestInfo, 1);
920 info->context = event->dnd.context;
921 info->proxy_source = NULL;
922 info->proxy_data = NULL;
923 info->dropped = FALSE;
924 info->proxy_drop_wait = FALSE;
925 g_dataset_set_data_full (context,
928 gtk_drag_dest_info_destroy);
931 /* Find the widget for the event */
940 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
945 case GDK_DRAG_MOTION:
948 GtkDragFindData data;
951 if (event->type == GDK_DROP_START)
952 info->dropped = TRUE;
954 gdk_window_get_origin (toplevel->window, &tx, &ty);
956 data.x = event->dnd.x_root - tx;
957 data.y = event->dnd.y_root - ty;
958 data.context = context;
961 data.toplevel = TRUE;
962 data.callback = (event->type == GDK_DRAG_MOTION) ?
963 gtk_drag_dest_motion : gtk_drag_dest_drop;
964 data.time = event->dnd.time;
966 gtk_drag_find_widget (toplevel, &data);
968 /* We send a leave here so that the widget unhighlights
972 ((event->type == GDK_DROP_START) || (!data.found)))
974 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
980 if (event->type == GDK_DRAG_MOTION)
983 gdk_drag_status (context, 0, event->dnd.time);
985 else if (event->type == GDK_DROP_START)
987 gdk_drop_reply (context, data.found, event->dnd.time);
988 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
989 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
995 g_assert_not_reached();
999 /*************************************************************
1000 * gtk_drag_dest_find_target:
1001 * Decide on a target for the drag.
1006 *************************************************************/
1009 gtk_drag_dest_find_target (GtkWidget *widget,
1010 GtkDragDestSite *site,
1011 GdkDragContext *context)
1014 GList *tmp_source = NULL;
1015 GtkWidget *source_widget = gtk_drag_get_source_widget (context);
1017 tmp_target = site->target_list->list;
1020 GtkTargetPair *pair = tmp_target->data;
1021 tmp_source = context->targets;
1024 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1026 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1027 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1028 return pair->target;
1032 tmp_source = tmp_source->next;
1034 tmp_target = tmp_target->next;
1041 gtk_drag_selection_received (GtkWidget *widget,
1042 GtkSelectionData *selection_data,
1046 GdkDragContext *context;
1047 GtkDragDestInfo *info;
1048 GtkWidget *drop_widget;
1052 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1053 info = g_dataset_get_data (context, "gtk-info");
1055 if (info->proxy_data &&
1056 info->proxy_data->target == selection_data->target)
1058 gtk_selection_data_set (info->proxy_data,
1059 selection_data->type,
1060 selection_data->format,
1061 selection_data->data,
1062 selection_data->length);
1067 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1069 gtk_drag_finish (context, TRUE, FALSE, time);
1071 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1072 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1078 GtkDragDestSite *site;
1080 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1082 if (site->target_list)
1086 if (gtk_target_list_find (site->target_list,
1087 selection_data->target,
1090 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1091 selection_data->length >= 0)
1092 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1093 "drag_data_received",
1094 context, info->drop_x, info->drop_y,
1101 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1102 "drag_data_received",
1103 context, info->drop_x, info->drop_y,
1104 selection_data, 0, time);
1107 if (site->flags & GTK_DEST_DEFAULT_DROP)
1110 gtk_drag_finish (context,
1111 (selection_data->length >= 0),
1112 (context->action == GDK_ACTION_MOVE),
1116 gtk_widget_unref (drop_widget);
1119 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1120 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1123 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1124 gdk_drag_context_unref (context);
1126 gtk_drag_release_ipc_widget (widget);
1129 /*************************************************************
1130 * gtk_drag_find_widget:
1131 * Recursive callback used to locate widgets for
1132 * DRAG_MOTION and DROP_START events.
1136 *************************************************************/
1139 gtk_drag_find_widget (GtkWidget *widget,
1140 GtkDragFindData *data)
1142 GtkAllocation new_allocation;
1146 new_allocation = widget->allocation;
1148 if (data->found || !GTK_WIDGET_MAPPED (widget))
1151 /* Note that in the following code, we only count the
1152 * position as being inside a WINDOW widget if it is inside
1153 * widget->window; points that are outside of widget->window
1154 * but within the allocation are not counted. This is consistent
1155 * with the way we highlight drag targets.
1157 if (!GTK_WIDGET_NO_WINDOW (widget))
1159 new_allocation.x = 0;
1160 new_allocation.y = 0;
1165 GdkWindow *window = widget->window;
1166 while (window != widget->parent->window)
1168 gint tx, ty, twidth, theight;
1169 gdk_window_get_size (window, &twidth, &theight);
1171 if (new_allocation.x < 0)
1173 new_allocation.width += new_allocation.x;
1174 new_allocation.x = 0;
1176 if (new_allocation.y < 0)
1178 new_allocation.height += new_allocation.y;
1179 new_allocation.y = 0;
1181 if (new_allocation.x + new_allocation.width > twidth)
1182 new_allocation.width = twidth - new_allocation.x;
1183 if (new_allocation.y + new_allocation.height > theight)
1184 new_allocation.height = theight - new_allocation.y;
1186 gdk_window_get_position (window, &tx, &ty);
1187 new_allocation.x += tx;
1189 new_allocation.y += ty;
1192 window = gdk_window_get_parent (window);
1196 if (data->toplevel ||
1197 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1198 (data->x < new_allocation.x + new_allocation.width) &&
1199 (data->y < new_allocation.y + new_allocation.height)))
1201 /* First, check if the drag is in a valid drop site in
1202 * one of our children
1204 if (GTK_IS_CONTAINER (widget))
1206 GtkDragFindData new_data = *data;
1208 new_data.x -= x_offset;
1209 new_data.y -= y_offset;
1210 new_data.found = FALSE;
1211 new_data.toplevel = FALSE;
1213 gtk_container_forall (GTK_CONTAINER (widget),
1214 (GtkCallback)gtk_drag_find_widget,
1217 data->found = new_data.found;
1220 /* If not, and this widget is registered as a drop site, check to
1221 * emit "drag_motion" to check if we are actually in
1225 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1227 data->found = data->callback (widget,
1229 data->x - new_allocation.x,
1230 data->y - new_allocation.y,
1232 /* If so, send a "drag_leave" to the last widget */
1235 if (data->info->widget && data->info->widget != widget)
1237 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1239 data->info->widget = widget;
1246 gtk_drag_proxy_begin (GtkWidget *widget,
1247 GtkDragDestInfo *dest_info)
1249 GtkDragSourceInfo *source_info;
1252 source_info = g_new0 (GtkDragSourceInfo, 1);
1253 source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1255 source_info->widget = widget;
1256 gtk_widget_ref (source_info->widget);
1257 source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1258 dest_info->context->targets);
1260 source_info->target_list = gtk_target_list_new (NULL, 0);
1261 tmp_list = dest_info->context->targets;
1264 gtk_target_list_add (source_info->target_list,
1265 GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1266 tmp_list = tmp_list->next;
1269 source_info->proxy_dest = dest_info;
1271 g_dataset_set_data (source_info->context, "gtk-info", source_info);
1273 gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget),
1275 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1278 dest_info->proxy_source = source_info;
1282 gtk_drag_dest_info_destroy (gpointer data)
1284 GtkDragDestInfo *info = data;
1290 gtk_drag_dest_realized (GtkWidget *widget)
1292 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1293 gdk_window_register_dnd (toplevel->window);
1297 gtk_drag_dest_site_destroy (gpointer data)
1299 GtkDragDestSite *site = data;
1301 if (site->target_list)
1302 gtk_target_list_unref (site->target_list);
1308 * Default drag handlers
1311 gtk_drag_dest_leave (GtkWidget *widget,
1312 GdkDragContext *context,
1315 GtkDragDestSite *site;
1317 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1318 g_return_if_fail (site != NULL);
1322 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1324 if (info->proxy_source && !info->dropped)
1325 gdk_drag_abort (info->proxy_source->context, time);
1331 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1332 gtk_drag_unhighlight (widget);
1334 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1335 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1338 site->have_drag = FALSE;
1343 gtk_drag_dest_motion (GtkWidget *widget,
1344 GdkDragContext *context,
1349 GtkDragDestSite *site;
1350 GdkDragAction action = 0;
1353 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1354 g_return_val_if_fail (site != NULL, FALSE);
1359 GdkEvent *current_event;
1360 GdkWindow *dest_window;
1361 GdkDragProtocol proto;
1363 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1365 if (!info->proxy_source)
1366 gtk_drag_proxy_begin (widget, info);
1368 current_event = gtk_get_current_event ();
1370 if (site->proxy_window)
1372 dest_window = site->proxy_window;
1373 proto = site->proxy_protocol;
1377 gdk_drag_find_window (info->proxy_source->context,
1379 current_event->dnd.x_root,
1380 current_event->dnd.y_root,
1381 &dest_window, &proto);
1384 gdk_drag_motion (info->proxy_source->context,
1386 current_event->dnd.x_root,
1387 current_event->dnd.y_root,
1388 context->suggested_action,
1389 context->actions, time);
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 selection = gdk_drag_get_selection (info->proxy_source->context);
1510 selection != gdk_drag_get_selection (info->context))
1511 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1513 gdk_event_free (current_event);
1523 if (site->flags & GTK_DEST_DEFAULT_DROP)
1525 GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
1527 if (target == GDK_NONE)
1530 gtk_drag_get_data (widget, context, target, time);
1533 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1534 context, x, y, time, &retval);
1536 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1544 /*************************************************************
1545 * gtk_drag_begin: Start a drag operation
1548 * widget: Widget from which drag starts
1549 * handlers: List of handlers to supply the data for the drag
1550 * button: Button user used to start drag
1551 * time: Time of event starting drag
1554 *************************************************************/
1557 gtk_drag_begin (GtkWidget *widget,
1558 GtkTargetList *target_list,
1559 GdkDragAction actions,
1563 GtkDragSourceInfo *info;
1564 GList *targets = NULL;
1566 guint32 time = GDK_CURRENT_TIME;
1567 GdkDragAction possible_actions, suggested_action;
1569 g_return_val_if_fail (widget != NULL, NULL);
1570 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1571 g_return_val_if_fail (target_list != NULL, NULL);
1574 time = gdk_event_get_time (event);
1576 info = g_new0 (GtkDragSourceInfo, 1);
1577 info->ipc_widget = gtk_drag_get_ipc_widget ();
1578 source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
1580 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1582 tmp_list = g_list_last (target_list->list);
1585 GtkTargetPair *pair = tmp_list->data;
1586 targets = g_list_prepend (targets,
1587 GINT_TO_POINTER (pair->target));
1588 tmp_list = tmp_list->prev;
1591 info->widget = widget;
1592 gtk_widget_ref (info->widget);
1594 info->context = gdk_drag_begin (info->ipc_widget->window, targets);
1595 g_list_free (targets);
1597 g_dataset_set_data (info->context, "gtk-info", info);
1599 info->button = button;
1600 info->target_list = target_list;
1601 gtk_target_list_ref (target_list);
1603 info->possible_actions = actions;
1605 info->cursor = NULL;
1606 info->status = GTK_DRAG_STATUS_DRAG;
1607 info->last_event = NULL;
1608 info->selections = NULL;
1609 info->icon_window = NULL;
1611 gtk_drag_get_event_actions (event, info->button, actions,
1612 &suggested_action, &possible_actions);
1615 info->cursor = gtk_drag_get_cursor (suggested_action);
1617 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1618 * the drag icon, it will be in the right place
1620 if (event->type == GDK_MOTION_NOTIFY)
1622 info->cur_x = event->motion.x_root;
1623 info->cur_y = event->motion.y_root;
1628 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x, &y, NULL);
1634 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1637 /* We use a GTK grab here to override any grabs that the widget
1638 * we are dragging from might have held
1641 gtk_grab_add (info->ipc_widget);
1642 gdk_pointer_grab (info->ipc_widget->window, FALSE,
1643 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1644 GDK_BUTTON_RELEASE_MASK, NULL,
1645 info->cursor, time);
1647 if (event->type == GDK_MOTION_NOTIFY)
1648 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1650 info->start_x = info->cur_x;
1651 info->start_y = info->cur_y;
1653 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1654 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1655 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1656 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1657 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1658 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1660 return info->context;
1663 /*************************************************************
1664 * gtk_drag_source_set:
1665 * Register a drop site, and possibly add default behaviors.
1668 * start_button_mask: Mask of allowed buttons to start drag
1669 * targets: Table of targets for this source
1671 * actions: Actions allowed for this source
1673 *************************************************************/
1676 gtk_drag_source_set (GtkWidget *widget,
1677 GdkModifierType start_button_mask,
1678 const GtkTargetEntry *targets,
1680 GdkDragAction actions)
1682 GtkDragSourceSite *site;
1684 g_return_if_fail (widget != NULL);
1686 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1688 gtk_widget_add_events (widget,
1689 gtk_widget_get_events (widget) |
1690 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1691 GDK_BUTTON_MOTION_MASK);
1695 if (site->target_list)
1696 gtk_target_list_unref (site->target_list);
1700 site = g_new0 (GtkDragSourceSite, 1);
1702 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1703 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1705 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1706 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1709 gtk_object_set_data_full (GTK_OBJECT (widget),
1711 site, gtk_drag_source_site_destroy);
1714 site->start_button_mask = start_button_mask;
1717 site->target_list = gtk_target_list_new (targets, n_targets);
1719 site->target_list = NULL;
1721 site->actions = actions;
1725 /*************************************************************
1726 * gtk_drag_source_unset
1727 * Unregister this widget as a drag source.
1731 *************************************************************/
1734 gtk_drag_source_unset (GtkWidget *widget)
1736 GtkDragSourceSite *site;
1738 g_return_if_fail (widget != NULL);
1740 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1744 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1745 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1749 /*************************************************************
1750 * gtk_drag_source_set_icon:
1751 * Set an icon for drags from this source.
1753 * colormap: Colormap for this icon
1757 *************************************************************/
1760 gtk_drag_source_set_icon (GtkWidget *widget,
1761 GdkColormap *colormap,
1765 GtkDragSourceSite *site;
1767 g_return_if_fail (widget != NULL);
1769 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1770 g_return_if_fail (site != NULL);
1773 gdk_colormap_unref (site->colormap);
1775 gdk_pixmap_unref (site->pixmap);
1777 gdk_pixmap_unref (site->mask);
1779 site->colormap = colormap;
1781 gdk_colormap_ref (colormap);
1783 site->pixmap = pixmap;
1785 gdk_pixmap_ref (pixmap);
1789 gdk_pixmap_ref (mask);
1792 /*************************************************************
1793 * gtk_drag_set_icon_widget:
1794 * Set a widget as the icon for a drag.
1801 *************************************************************/
1804 gtk_drag_set_icon_widget (GdkDragContext *context,
1809 GtkDragSourceInfo *info;
1811 g_return_if_fail (context != NULL);
1812 g_return_if_fail (widget != NULL);
1814 info = g_dataset_get_data (context, "gtk-info");
1815 gtk_drag_remove_icon (info);
1817 info->icon_window = widget;
1818 info->hot_x = hot_x;
1819 info->hot_y = hot_y;
1823 gtk_widget_set_uposition (widget,
1824 info->cur_x - info->hot_x,
1825 info->cur_y - info->hot_y);
1826 gtk_widget_ref (widget);
1827 gdk_window_raise (widget->window);
1828 gtk_widget_show (widget);
1832 /*************************************************************
1833 * gtk_drag_set_icon_pixmap:
1834 * Set a widget as the icon for a drag.
1837 * colormap: Colormap for the icon window.
1843 *************************************************************/
1846 gtk_drag_set_icon_pixmap (GdkDragContext *context,
1847 GdkColormap *colormap,
1856 g_return_if_fail (context != NULL);
1857 g_return_if_fail (colormap != NULL);
1858 g_return_if_fail (pixmap != NULL);
1860 gdk_window_get_size (pixmap, &width, &height);
1862 gtk_widget_push_visual (gdk_colormap_get_visual (colormap));
1863 gtk_widget_push_colormap (colormap);
1865 window = gtk_window_new (GTK_WINDOW_POPUP);
1866 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1867 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
1869 gtk_widget_pop_visual ();
1870 gtk_widget_pop_colormap ();
1872 gtk_widget_set_usize (window, width, height);
1873 gtk_widget_realize (window);
1875 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1878 gtk_widget_shape_combine_mask (window, mask, 0, 0);
1880 gtk_drag_set_icon_widget (context, window, hot_x, hot_y);
1883 /*************************************************************
1884 * gtk_drag_set_icon_default:
1885 * Set the icon for a drag to the default icon.
1889 *************************************************************/
1892 gtk_drag_set_icon_default (GdkDragContext *context)
1894 g_return_if_fail (context != NULL);
1896 if (!default_icon_pixmap)
1898 default_icon_colormap = gdk_colormap_get_system ();
1899 default_icon_pixmap =
1900 gdk_pixmap_colormap_create_from_xpm_d (NULL,
1901 default_icon_colormap,
1903 NULL, drag_default_xpm);
1904 default_icon_hot_x = -2;
1905 default_icon_hot_y = -2;
1908 gtk_drag_set_icon_pixmap (context,
1909 default_icon_colormap,
1910 default_icon_pixmap,
1913 default_icon_hot_y);
1916 /*************************************************************
1917 * gtk_drag_set_default_icon:
1918 * Set a default icon for all drags as a pixmap.
1920 * colormap: Colormap for the icon window.
1926 *************************************************************/
1929 gtk_drag_set_default_icon (GdkColormap *colormap,
1935 g_return_if_fail (colormap != NULL);
1936 g_return_if_fail (pixmap != NULL);
1938 if (default_icon_colormap)
1939 gdk_colormap_unref (default_icon_colormap);
1940 if (default_icon_pixmap)
1941 gdk_pixmap_unref (default_icon_pixmap);
1942 if (default_icon_mask)
1943 gdk_pixmap_unref (default_icon_pixmap);
1945 default_icon_colormap = colormap;
1946 gdk_colormap_ref (colormap);
1948 default_icon_pixmap = pixmap;
1949 gdk_pixmap_ref (pixmap);
1951 default_icon_mask = mask;
1953 gdk_pixmap_ref (mask);
1955 default_icon_hot_x = hot_x;
1956 default_icon_hot_y = hot_y;
1960 /*************************************************************
1961 * gtk_drag_source_handle_event:
1962 * Called from widget event handling code on Drag events
1966 * toplevel: Toplevel widget that received the event
1969 *************************************************************/
1972 gtk_drag_source_handle_event (GtkWidget *widget,
1975 GtkDragSourceInfo *info;
1976 GdkDragContext *context;
1978 g_return_if_fail (widget != NULL);
1979 g_return_if_fail (event != NULL);
1981 context = event->dnd.context;
1982 info = g_dataset_get_data (context, "gtk-info");
1986 switch (event->type)
1988 case GDK_DRAG_STATUS:
1992 if (info->proxy_dest)
1994 if (!event->dnd.send_event)
1996 if (info->proxy_dest->proxy_drop_wait)
1998 /* Aha - we can finally pass the MOTIF DROP on... */
1999 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2003 gdk_drag_status (info->proxy_dest->context,
2004 event->dnd.context->action,
2011 cursor = gtk_drag_get_cursor (event->dnd.context->action);
2012 if (info->cursor != cursor)
2014 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window),
2015 PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
2016 ((GdkCursorPrivate *)cursor)->xcursor,
2018 info->cursor = cursor;
2021 if (info->last_event)
2023 gtk_drag_motion_cb (info->widget,
2024 (GdkEventMotion *)info->last_event,
2026 info->last_event = NULL;
2032 case GDK_DROP_FINISHED:
2033 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2036 g_assert_not_reached();
2040 /*************************************************************
2041 * gtk_drag_source_check_selection:
2042 * Check if we've set up handlers/claimed the selection
2043 * for a given drag. If not, add them.
2047 *************************************************************/
2050 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2056 tmp_list = info->selections;
2059 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
2061 tmp_list = tmp_list->next;
2064 gtk_selection_owner_set (info->ipc_widget, selection, time);
2065 info->selections = g_list_prepend (info->selections,
2066 GUINT_TO_POINTER (selection));
2068 tmp_list = info->target_list->list;
2071 GtkTargetPair *pair = tmp_list->data;
2073 gtk_selection_add_target (info->ipc_widget,
2077 tmp_list = tmp_list->next;
2080 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2082 gtk_selection_add_target (info->ipc_widget,
2084 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2085 TARGET_MOTIF_SUCCESS);
2086 gtk_selection_add_target (info->ipc_widget,
2088 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2089 TARGET_MOTIF_FAILURE);
2092 gtk_selection_add_target (info->ipc_widget,
2094 gdk_atom_intern ("DELETE", FALSE),
2098 /*************************************************************
2099 * gtk_drag_drop_finished:
2100 * Clean up from the drag, and display snapback, if necessary.
2106 *************************************************************/
2109 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2113 gtk_drag_source_release_selections (info, time);
2115 if (info->proxy_dest)
2117 /* The time from the event isn't reliable for Xdnd drags */
2118 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2119 info->proxy_dest->proxy_drop_time);
2120 gtk_drag_source_info_destroy (info);
2126 gtk_drag_source_info_destroy (info);
2130 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2134 anim->n_steps = MAX (info->cur_x - info->start_x,
2135 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2136 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2137 if (info->icon_window)
2139 gtk_widget_show (info->icon_window);
2140 gdk_window_raise (info->icon_window->window);
2143 /* Mark the context as dead, so if the destination decides
2144 * to respond really late, we still are OK.
2146 g_dataset_set_data (info->context, "gtk-info", NULL);
2147 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2153 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2156 GList *tmp_list = info->selections;
2159 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2161 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2162 gtk_selection_owner_set (NULL, selection, time);
2163 tmp_list = tmp_list->next;
2166 g_list_free (info->selections);
2167 info->selections = NULL;
2170 /*************************************************************
2172 * Send a drop event.
2176 *************************************************************/
2179 gtk_drag_drop (GtkDragSourceInfo *info,
2182 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2184 GtkSelectionData selection_data;
2186 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2188 tmp_list = info->target_list->list;
2191 GtkTargetPair *pair = tmp_list->data;
2193 if (pair->target == target)
2195 selection_data.selection = GDK_NONE;
2196 selection_data.target = target;
2197 selection_data.data = NULL;
2198 selection_data.length = -1;
2200 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2201 info->context, &selection_data,
2205 /* FIXME: Should we check for length >= 0 here? */
2206 gtk_drag_drop_finished (info, TRUE, time);
2209 tmp_list = tmp_list->next;
2211 gtk_drag_drop_finished (info, FALSE, time);
2215 if (info->icon_window)
2216 gtk_widget_hide (info->icon_window);
2218 gdk_drag_drop (info->context, time);
2219 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2220 gtk_drag_abort_timeout,
2226 * Source side callbacks.
2230 gtk_drag_source_event_cb (GtkWidget *widget,
2234 GtkDragSourceSite *site;
2235 site = (GtkDragSourceSite *)data;
2237 switch (event->type)
2239 case GDK_BUTTON_PRESS:
2240 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2242 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2243 site->x = event->button.x;
2244 site->y = event->button.y;
2248 case GDK_BUTTON_RELEASE:
2249 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2251 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2255 case GDK_MOTION_NOTIFY:
2256 if (site->state & event->motion.state & site->start_button_mask)
2258 /* FIXME: This is really broken and can leave us
2264 if (site->state & event->motion.state &
2265 GDK_BUTTON1_MASK << (i - 1))
2269 if (MAX (abs(site->x - event->motion.x),
2270 abs(site->y - event->motion.y)) > 3)
2272 GtkDragSourceInfo *info;
2273 GdkDragContext *context;
2276 context = gtk_drag_begin (widget, site->target_list,
2281 info = g_dataset_get_data (context, "gtk-info");
2283 if (!info->icon_window)
2286 gtk_drag_set_icon_pixmap (context,
2289 site->mask, -2, -2);
2291 gtk_drag_set_icon_default (context);
2299 default: /* hit for 2/3BUTTON_PRESS */
2306 gtk_drag_source_site_destroy (gpointer data)
2308 GtkDragSourceSite *site = data;
2310 if (site->target_list)
2311 gtk_target_list_unref (site->target_list);
2314 gdk_pixmap_unref (site->pixmap);
2317 gdk_pixmap_unref (site->mask);
2323 gtk_drag_selection_get (GtkWidget *widget,
2324 GtkSelectionData *selection_data,
2329 GtkDragSourceInfo *info = data;
2330 static GdkAtom null_atom = GDK_NONE;
2334 null_atom = gdk_atom_intern ("NULL", FALSE);
2339 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2342 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2344 case TARGET_MOTIF_SUCCESS:
2345 gtk_drag_drop_finished (info, TRUE, time);
2346 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2348 case TARGET_MOTIF_FAILURE:
2349 gtk_drag_drop_finished (info, FALSE, time);
2350 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2353 if (info->proxy_dest)
2355 /* This is sort of dangerous and needs to be thought
2358 info->proxy_dest->proxy_data = selection_data;
2359 gtk_drag_get_data (info->widget,
2360 info->proxy_dest->context,
2361 selection_data->target,
2364 info->proxy_dest->proxy_data = NULL;
2368 if (gtk_target_list_find (info->target_list,
2369 selection_data->target,
2372 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2384 gtk_drag_anim_timeout (gpointer data)
2386 GtkDragAnim *anim = data;
2390 GDK_THREADS_ENTER ();
2392 if (anim->step == anim->n_steps)
2394 gtk_drag_source_info_destroy (anim->info);
2401 x = (anim->info->start_x * (anim->step + 1) +
2402 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2403 y = (anim->info->start_y * (anim->step + 1) +
2404 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2405 if (anim->info->icon_window)
2406 gtk_widget_set_uposition (anim->info->icon_window,
2407 x - anim->info->hot_x,
2408 y - anim->info->hot_y);
2415 GDK_THREADS_LEAVE ();
2421 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2423 if (info->icon_window)
2425 gtk_widget_hide (info->icon_window);
2426 gtk_widget_unref (info->icon_window);
2428 info->icon_window = NULL;
2433 gtk_drag_source_info_destroy (gpointer data)
2435 GtkDragSourceInfo *info = data;
2437 gtk_drag_remove_icon (data);
2439 if (!info->proxy_dest)
2440 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2444 gtk_widget_unref (info->widget);
2446 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2447 gtk_selection_remove_all (info->ipc_widget);
2448 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2449 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2450 gtk_drag_release_ipc_widget (info->ipc_widget);
2452 gtk_target_list_unref (info->target_list);
2454 g_dataset_set_data (info->context, "gtk-info", NULL);
2455 gdk_drag_context_unref (info->context);
2457 if (info->drop_timeout)
2458 gtk_timeout_remove (info->drop_timeout);
2463 /*************************************************************
2464 * gtk_drag_motion_cb:
2465 * "motion_notify_event" callback during drag.
2469 *************************************************************/
2472 gtk_drag_motion_cb (GtkWidget *widget,
2473 GdkEventMotion *event,
2476 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2478 GdkDragAction action;
2479 GdkDragAction possible_actions;
2480 GdkWindow *window = NULL;
2481 GdkWindow *dest_window;
2482 GdkDragProtocol protocol;
2483 gint x_root, y_root;
2487 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2488 event->x_root = x_root;
2489 event->y_root = y_root;
2492 gtk_drag_get_event_actions ((GdkEvent *)event,
2494 info->possible_actions,
2495 &action, &possible_actions);
2497 info->cur_x = event->x_root;
2498 info->cur_y = event->y_root;
2500 if (info->icon_window)
2502 gdk_window_raise (info->icon_window->window);
2503 gtk_widget_set_uposition (info->icon_window,
2504 info->cur_x - info->hot_x,
2505 info->cur_y - info->hot_y);
2506 window = info->icon_window->window;
2509 gdk_drag_find_window (info->context,
2510 window, event->x_root, event->y_root,
2511 &dest_window, &protocol);
2513 if (gdk_drag_motion (info->context, dest_window, protocol,
2514 event->x_root, event->y_root, action,
2518 if (info->last_event)
2519 gdk_event_free ((GdkEvent *)info->last_event);
2521 info->last_event = gdk_event_copy ((GdkEvent *)event);
2525 gdk_window_unref (dest_window);
2527 selection = gdk_drag_get_selection (info->context);
2529 gtk_drag_source_check_selection (info, selection, event->time);
2532 /* We ignore the response, so we can respond precisely to the drop
2535 gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2541 /*************************************************************
2542 * gtk_drag_motion_cb:
2543 * "button_release_event" callback during drag.
2547 *************************************************************/
2550 gtk_drag_button_release_cb (GtkWidget *widget,
2551 GdkEventButton *event,
2554 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2555 GtkWidget *source_widget = info->widget;
2556 GdkEvent send_event;
2558 gtk_widget_ref (source_widget);
2560 if (event->button != info->button)
2563 gdk_pointer_ungrab (event->time);
2565 gtk_grab_remove (widget);
2566 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
2567 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2569 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
2570 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2573 if ((info->context->action != 0) && (info->context->dest_window != NULL))
2575 gtk_drag_drop (info, event->time);
2579 gdk_drag_abort (info->context, event->time);
2580 gtk_drag_drop_finished (info, FALSE, event->time);
2583 /* Send on a release pair to the the original
2584 * widget to convince it to release its grab. We need to
2585 * call gtk_propagate_event() here, instead of
2586 * gtk_widget_event() because widget like GtkList may
2587 * expect propagation.
2590 send_event.button.type = GDK_BUTTON_RELEASE;
2591 send_event.button.window = GDK_ROOT_PARENT ();
2592 send_event.button.send_event = TRUE;
2593 send_event.button.time = event->time;
2594 send_event.button.x = 0;
2595 send_event.button.y = 0;
2596 send_event.button.pressure = 0.;
2597 send_event.button.xtilt = 0.;
2598 send_event.button.ytilt = 0.;
2599 send_event.button.state = event->state;
2600 send_event.button.button = event->button;
2601 send_event.button.source = GDK_SOURCE_PEN;
2602 send_event.button.deviceid = GDK_CORE_POINTER;
2603 send_event.button.x_root = 0;
2604 send_event.button.y_root = 0;
2606 gtk_propagate_event (source_widget, &send_event);
2608 gtk_widget_unref (source_widget);
2614 gtk_drag_abort_timeout (gpointer data)
2616 GtkDragSourceInfo *info = data;
2617 guint32 time = GDK_CURRENT_TIME;
2619 if (info->proxy_dest)
2620 time = info->proxy_dest->proxy_drop_time;
2622 info->drop_timeout = 0;
2623 gtk_drag_drop_finished (info, FALSE, time);