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 GdkDragContext *context; /* drag context */
62 GtkWidget *icon_window; /* Window for drag */
63 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
64 GdkCursor *cursor; /* Cursor for drag */
65 gint hot_x, hot_y; /* Hot spot for drag */
66 gint button; /* mouse button starting drag */
68 GtkDragStatus status; /* drag status */
69 GdkEvent *last_event; /* motion event waiting for response */
71 gint start_x, start_y; /* Initial position */
72 gint cur_x, cur_y; /* Current Position */
74 GList *selections; /* selections we've claimed */
76 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
78 guint drop_timeout; /* Timeout for aborting drop */
81 struct _GtkDragDestSite {
82 GtkDestDefaults flags;
83 GtkTargetList *target_list;
84 GdkDragAction actions;
85 GdkWindow *proxy_window;
86 GdkDragProtocol proxy_protocol;
87 gboolean do_proxy : 1;
88 gboolean proxy_coords : 1;
89 gboolean have_drag : 1;
92 struct _GtkDragDestInfo {
93 GtkWidget *widget; /* Widget in which drag is in */
94 GdkDragContext *context; /* Drag context */
95 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
96 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
97 gboolean dropped : 1; /* Set after we receive a drop */
98 guint32 proxy_drop_time; /* Timestamp for proxied drop */
99 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
100 * status reply before sending
103 gint drop_x, drop_y; /* Position of drop */
106 #define DROP_ABORT_TIME 300000
108 #define ANIM_STEP_TIME 50
109 #define ANIM_STEP_LENGTH 50
110 #define ANIM_MIN_STEPS 5
111 #define ANIM_MAX_STEPS 10
113 struct _GtkDragAnim {
114 GtkDragSourceInfo *info;
119 struct _GtkDragFindData {
122 GdkDragContext *context;
123 GtkDragDestInfo *info;
126 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
127 gint x, gint y, guint32 time);
131 /* Enumeration for some targets we handle internally */
134 TARGET_MOTIF_SUCCESS = 0x40000000,
135 TARGET_MOTIF_FAILURE,
141 static GdkPixmap *default_icon_pixmap = NULL;
142 static GdkPixmap *default_icon_mask = NULL;
143 static GdkColormap *default_icon_colormap = NULL;
144 static gint default_icon_hot_x;
145 static gint default_icon_hot_y;
147 /* Forward declarations */
148 static GdkDragAction gtk_drag_get_event_action (GdkEvent *event,
150 GdkDragAction actions);
151 static GdkCursor * gtk_drag_get_cursor (GdkDragAction action);
152 static GtkWidget *gtk_drag_get_ipc_widget (void);
153 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
155 static GdkAtom gtk_drag_dest_find_target (GtkWidget *widget,
156 GtkDragDestSite *site,
157 GdkDragContext *context);
158 static void gtk_drag_selection_received (GtkWidget *widget,
159 GtkSelectionData *selection_data,
162 static void gtk_drag_find_widget (GtkWidget *widget,
163 GtkDragFindData *data);
164 static void gtk_drag_proxy_begin (GtkWidget *widget,
165 GtkDragDestInfo *dest_info);
166 static void gtk_drag_dest_info_destroy (gpointer data);
167 static void gtk_drag_dest_realized (GtkWidget *widget);
168 static void gtk_drag_dest_site_destroy (gpointer data);
169 static void gtk_drag_dest_leave (GtkWidget *widget,
170 GdkDragContext *context,
172 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
173 GdkDragContext *context,
177 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
178 GdkDragContext *context,
183 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
186 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
188 static void gtk_drag_drop (GtkDragSourceInfo *info,
190 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
194 static gint gtk_drag_source_event_cb (GtkWidget *widget,
197 static void gtk_drag_source_site_destroy (gpointer data);
198 static void gtk_drag_selection_get (GtkWidget *widget,
199 GtkSelectionData *selection_data,
203 static gint gtk_drag_anim_timeout (gpointer data);
204 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
205 static void gtk_drag_source_info_destroy (gpointer data);
206 static gint gtk_drag_motion_cb (GtkWidget *widget,
207 GdkEventMotion *event,
209 static gint gtk_drag_button_release_cb (GtkWidget *widget,
210 GdkEventButton *event,
212 static gint gtk_drag_abort_timeout (gpointer data);
214 /************************
215 * Cursor and Icon data *
216 ************************/
218 #define action_ask_width 16
219 #define action_ask_height 16
220 static const char action_ask_bits[] = {
221 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
222 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
223 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
225 #define action_ask_mask_width 16
226 #define action_ask_mask_height 16
227 static const char action_ask_mask_bits[] = {
228 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
229 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
230 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
232 #define action_copy_width 16
233 #define action_copy_height 16
234 static const char action_copy_bits[] = {
235 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
236 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
237 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
239 #define action_copy_mask_width 16
240 #define action_copy_mask_height 16
241 static const char action_copy_mask_bits[] = {
242 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
243 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
244 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
246 #define action_move_width 16
247 #define action_move_height 16
248 static const char action_move_bits[] = {
249 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
250 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
251 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
253 #define action_move_mask_width 16
254 #define action_move_mask_height 16
255 static const char action_move_mask_bits[] = {
256 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
257 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
258 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
260 #define action_link_width 16
261 #define action_link_height 16
262 static const char action_link_bits[] = {
263 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
264 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
265 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
267 #define action_link_mask_width 16
268 #define action_link_mask_height 16
269 static const char action_link_mask_bits[] = {
270 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
271 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
272 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
274 #define action_none_width 16
275 #define action_none_height 16
276 static const char action_none_bits[] = {
277 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
278 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
279 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
281 #define action_none_mask_width 16
282 #define action_none_mask_height 16
283 static const char action_none_mask_bits[] = {
284 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
285 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
286 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
288 #define CURSOR_WIDTH 16
289 #define CURSOR_HEIGHT 16
292 GdkDragAction action;
297 { GDK_ACTION_DEFAULT, 0 },
298 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
299 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
300 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
301 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
302 { 0 , action_none_bits, action_none_mask_bits, NULL },
305 static const gint n_drag_cursors = sizeof(drag_cursors) / sizeof(drag_cursors[0]);
308 static const char * drag_default_xpm[] = {
321 " ...+++++++++++.. ",
322 " ..+.++++++++++++.. ",
323 " .++.++++++++++++.. ",
324 " .+++.++++++++++++.. ",
325 " .++++.++++++++++++. ",
326 " .+++.+++++++++++++.. ",
327 " .++.+++++++++++++++.. ",
328 " .+.+++++++++++++++++.. ",
329 " ..+++++++++++++++++++.. ",
330 " ..++++++++++++++++++++. ",
331 " .++++++++++++++++++++.. ",
332 " ..+++++++++++++++++.. ",
333 " .++++++++++++++++.. ",
334 " ..+++++++++++++... ",
346 /*********************
347 * Utility functions *
348 *********************/
350 /*************************************************************
351 * gtk_drag_get_ipc_widget:
352 * Return a invisible, off-screen, override-redirect
357 *************************************************************/
360 gtk_drag_get_ipc_widget(void)
366 GSList *tmp = drag_widgets;
367 result = drag_widgets->data;
368 drag_widgets = drag_widgets->next;
369 g_slist_free_1 (tmp);
373 result = gtk_invisible_new();
374 gtk_widget_show (result);
380 /*************************************************************
381 * gtk_drag_release_ipc_widget:
382 * Releases widget retrieved with gtk_drag_get_ipc_widget()
384 * widget: the widget to release.
386 *************************************************************/
389 gtk_drag_release_ipc_widget (GtkWidget *widget)
391 drag_widgets = g_slist_prepend (drag_widgets, widget);
395 gtk_drag_get_event_action (GdkEvent *event, gint button, GdkDragAction actions)
399 GdkModifierType state = 0;
403 case GDK_MOTION_NOTIFY:
404 state = event->motion.state;
406 case GDK_BUTTON_PRESS:
407 case GDK_2BUTTON_PRESS:
408 case GDK_3BUTTON_PRESS:
409 case GDK_BUTTON_RELEASE:
410 state = event->button.state;
413 case GDK_KEY_RELEASE:
414 state = event->key.state;
416 case GDK_ENTER_NOTIFY:
417 case GDK_LEAVE_NOTIFY:
418 state = event->crossing.state;
424 if (((button == 2) || (button == 3)) && (actions & GDK_ACTION_ASK))
425 return GDK_ACTION_ASK;
427 if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
429 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
430 return (actions & GDK_ACTION_LINK) ? GDK_ACTION_LINK : 0;
431 else if (state & GDK_CONTROL_MASK)
432 return (actions & GDK_ACTION_COPY) ? GDK_ACTION_COPY : 0;
434 return (actions & GDK_ACTION_MOVE) ? GDK_ACTION_MOVE : 0;
438 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
439 return GDK_ACTION_ASK;
441 if (actions & GDK_ACTION_COPY)
442 return GDK_ACTION_COPY;
443 else if (actions & GDK_ACTION_MOVE)
444 return GDK_ACTION_MOVE;
445 else if (actions & GDK_ACTION_LINK)
446 return GDK_ACTION_LINK;
454 gtk_drag_get_cursor (GdkDragAction action)
458 for (i = 0 ; i < n_drag_cursors - 1; i++)
459 if (drag_cursors[i].action == action)
462 if (drag_cursors[i].cursor == NULL)
467 gdk_bitmap_create_from_data (NULL,
468 drag_cursors[i].bits,
469 CURSOR_WIDTH, CURSOR_HEIGHT);
471 gdk_bitmap_create_from_data (NULL,
472 drag_cursors[i].mask,
473 CURSOR_WIDTH, CURSOR_HEIGHT);
475 gdk_color_white (gdk_colormap_get_system(), &bg);
476 gdk_color_black (gdk_colormap_get_system(), &fg);
478 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
480 gdk_pixmap_unref (pixmap);
481 gdk_pixmap_unref (mask);
484 return drag_cursors[i].cursor;
487 /********************
489 ********************/
491 /*************************************************************
493 * Get the data for a drag or drop
495 * context - drag context
496 * target - format to retrieve the data in.
497 * time - timestamp of triggering event.
500 *************************************************************/
503 gtk_drag_get_data (GtkWidget *widget,
504 GdkDragContext *context,
508 GtkWidget *selection_widget;
510 g_return_if_fail (widget != NULL);
511 g_return_if_fail (context != NULL);
513 selection_widget = gtk_drag_get_ipc_widget();
515 gdk_drag_context_ref (context);
516 gtk_widget_ref (widget);
518 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
519 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
521 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
523 gtk_selection_convert (selection_widget,
524 gdk_drag_get_selection(context),
530 /*************************************************************
531 * gtk_drag_get_source_widget:
532 * Get the widget the was the source of this drag, if
533 * the drag originated from this application.
535 * context: The drag context for this drag
537 * The source widget, or NULL if the drag originated from
538 * a different application.
539 *************************************************************/
542 gtk_drag_get_source_widget (GdkDragContext *context)
546 tmp_list = source_widgets;
549 GtkWidget *ipc_widget = tmp_list->data;
551 if (ipc_widget->window == context->source_window)
553 GtkDragSourceInfo *info;
554 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
556 return info ? info->widget : NULL;
559 tmp_list = tmp_list->next;
565 /*************************************************************
567 * Notify the drag source that the transfer of data
570 * context: The drag context for this drag
571 * success: Was the data successfully transferred?
572 * time: The timestamp to use when notifying the destination.
574 *************************************************************/
577 gtk_drag_finish (GdkDragContext *context,
582 GdkAtom target = GDK_NONE;
584 g_return_if_fail (context != NULL);
588 target = gdk_atom_intern ("DELETE", FALSE);
590 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
592 target = gdk_atom_intern (success ?
593 "XmTRANSFER_SUCCESS" :
594 "XmTRANSFER_FAILURE",
598 if (target != GDK_NONE)
600 GtkWidget *selection_widget = gtk_drag_get_ipc_widget();
602 gdk_drag_context_ref (context);
604 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
605 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
606 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
609 gtk_selection_convert (selection_widget,
610 gdk_drag_get_selection(context),
616 gdk_drop_finish (context, success, time);
619 /*************************************************************
620 * gtk_drag_highlight:
621 * Highlight the given widget in the default manner.
625 *************************************************************/
628 gtk_drag_highlight (GtkWidget *widget)
632 g_return_if_fail (widget != NULL);
634 if (GTK_WIDGET_NO_WINDOW (widget))
636 x = widget->allocation.x;
637 y = widget->allocation.y;
645 gtk_draw_shadow (widget->style, widget->window,
646 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
648 widget->allocation.width,
649 widget->allocation.height);
651 gdk_draw_rectangle (widget->window,
652 widget->style->black_gc,
655 widget->allocation.width - 1,
656 widget->allocation.height - 1);
659 /*************************************************************
660 * gtk_drag_unhighlight:
661 * Refresh the given widget to remove the highlight.
665 *************************************************************/
668 gtk_drag_unhighlight (GtkWidget *widget)
672 g_return_if_fail (widget != NULL);
674 if (GTK_WIDGET_NO_WINDOW (widget))
676 x = widget->allocation.x;
677 y = widget->allocation.y;
685 gdk_window_clear_area_e (widget->window,
687 widget->allocation.width,
688 widget->allocation.height);
691 /*************************************************************
693 * Register a drop site, and possibly add default behaviors.
696 * flags: Which types of default drag behavior to use
697 * targets: Table of targets that can be accepted
698 * n_targets: Number of of entries in targets
701 *************************************************************/
704 gtk_drag_dest_set (GtkWidget *widget,
705 GtkDestDefaults flags,
706 const GtkTargetEntry *targets,
708 GdkDragAction actions)
710 GtkDragDestSite *site;
712 g_return_if_fail (widget != NULL);
714 /* HACK, do this in the destroy */
715 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
717 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
719 if (GTK_WIDGET_REALIZED (widget))
720 gtk_drag_dest_realized (widget);
722 gtk_signal_connect (GTK_OBJECT (widget), "realize",
723 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
725 site = g_new (GtkDragDestSite, 1);
728 site->have_drag = FALSE;
730 site->target_list = gtk_target_list_new (targets, n_targets);
732 site->target_list = NULL;
734 site->actions = actions;
735 site->do_proxy = FALSE;
737 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
738 site, gtk_drag_dest_site_destroy);
741 /*************************************************************
742 * gtk_drag_dest_set_proxy:
743 * Set up this widget to proxy drags elsewhere.
746 * proxy_window: window to which forward drag events
747 * protocol: Drag protocol which the dest widget accepts
748 * use_coordinates: If true, send the same coordinates to the
749 * destination, because it is a embedded
752 *************************************************************/
755 gtk_drag_dest_set_proxy (GtkWidget *widget,
756 GdkWindow *proxy_window,
757 GdkDragProtocol protocol,
758 gboolean use_coordinates)
760 GtkDragDestSite *site;
762 g_return_if_fail (widget != NULL);
764 /* HACK, do this in the destroy */
765 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
767 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
769 if (GTK_WIDGET_REALIZED (widget))
770 gtk_drag_dest_realized (widget);
772 gtk_signal_connect (GTK_OBJECT (widget), "realize",
773 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
775 site = g_new (GtkDragDestSite, 1);
778 site->have_drag = FALSE;
779 site->target_list = NULL;
781 site->proxy_window = proxy_window;
783 gdk_window_ref (proxy_window);
784 site->do_proxy = TRUE;
785 site->proxy_protocol = protocol;
786 site->proxy_coords = use_coordinates;
788 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
789 site, gtk_drag_dest_site_destroy);
792 /*************************************************************
793 * gtk_drag_dest_unset
794 * Unregister this widget as a drag target.
798 *************************************************************/
801 gtk_drag_dest_unset (GtkWidget *widget)
803 g_return_if_fail (widget != NULL);
805 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
808 /*************************************************************
809 * gtk_drag_dest_handle_event:
810 * Called from widget event handling code on Drag events
814 * toplevel: Toplevel widget that received the event
817 *************************************************************/
820 gtk_drag_dest_handle_event (GtkWidget *toplevel,
823 GtkDragDestInfo *info;
824 GdkDragContext *context;
826 g_return_if_fail (toplevel != NULL);
827 g_return_if_fail (event != NULL);
829 context = event->dnd.context;
831 info = g_dataset_get_data (context, "gtk-info");
834 info = g_new (GtkDragDestInfo, 1);
836 info->context = event->dnd.context;
837 info->proxy_source = NULL;
838 info->proxy_data = NULL;
839 info->dropped = FALSE;
840 info->proxy_drop_wait = FALSE;
841 g_dataset_set_data_full (context,
844 gtk_drag_dest_info_destroy);
847 /* Find the widget for the event */
856 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
861 case GDK_DRAG_MOTION:
864 GtkDragFindData data;
867 if (event->type == GDK_DROP_START)
868 info->dropped = TRUE;
870 gdk_window_get_origin (toplevel->window, &tx, &ty);
872 data.x = event->dnd.x_root - tx;
873 data.y = event->dnd.y_root - ty;
874 data.context = context;
877 data.toplevel = TRUE;
878 data.callback = (event->type == GDK_DRAG_MOTION) ?
879 gtk_drag_dest_motion : gtk_drag_dest_drop;
880 data.time = event->dnd.time;
882 gtk_drag_find_widget (toplevel, &data);
884 /* We send a leave here so that the widget unhighlights
888 ((event->type == GDK_DROP_START) || (!data.found)))
890 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
896 if (event->type == GDK_DRAG_MOTION)
899 gdk_drag_status (context, 0, event->dnd.time);
901 else if (event->type == GDK_DROP_START)
903 gdk_drop_reply (context, data.found, event->dnd.time);
904 if (context->protocol == GDK_DRAG_PROTO_MOTIF)
905 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
911 g_assert_not_reached();
915 /*************************************************************
916 * gtk_drag_dest_find_target:
917 * Decide on a target for the drag.
922 *************************************************************/
925 gtk_drag_dest_find_target (GtkWidget *widget,
926 GtkDragDestSite *site,
927 GdkDragContext *context)
930 GList *tmp_source = NULL;
931 GtkWidget *source_widget = gtk_drag_get_source_widget (context);
933 tmp_target = site->target_list->list;
936 GtkTargetPair *pair = tmp_target->data;
937 tmp_source = context->targets;
940 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
942 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
943 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
948 tmp_source = tmp_source->next;
950 tmp_target = tmp_target->next;
957 gtk_drag_selection_received (GtkWidget *widget,
958 GtkSelectionData *selection_data,
962 GdkDragContext *context;
963 GtkDragDestInfo *info;
964 GtkWidget *drop_widget;
968 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
969 info = g_dataset_get_data (context, "gtk-info");
971 if (info->proxy_data &&
972 info->proxy_data->target == selection_data->target)
974 gtk_selection_data_set (info->proxy_data,
975 selection_data->type,
976 selection_data->format,
977 selection_data->data,
978 selection_data->length);
983 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
985 gtk_drag_finish (context, TRUE, FALSE, time);
987 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
988 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
994 GtkDragDestSite *site;
996 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
998 if (site->target_list)
1002 if (gtk_target_list_find (site->target_list,
1003 selection_data->target,
1006 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1007 selection_data->length >= 0)
1008 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1009 "drag_data_received",
1010 context, info->drop_x, info->drop_y,
1017 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1018 "drag_data_received",
1019 context, info->drop_x, info->drop_y,
1020 selection_data, 0, time);
1023 if (site->flags & GTK_DEST_DEFAULT_DROP)
1026 gtk_drag_finish (context,
1027 (selection_data->length >= 0),
1028 (context->action == GDK_ACTION_MOVE),
1032 gtk_widget_unref (drop_widget);
1035 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1036 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1039 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1040 gdk_drag_context_unref (context);
1042 gtk_drag_release_ipc_widget (widget);
1045 /*************************************************************
1046 * gtk_drag_find_widget:
1047 * Recursive callback used to locate widgets for
1048 * DRAG_MOTION and DROP_START events.
1052 *************************************************************/
1055 gtk_drag_find_widget (GtkWidget *widget,
1056 GtkDragFindData *data)
1058 GtkAllocation new_allocation;
1062 new_allocation = widget->allocation;
1064 if (data->found || !GTK_WIDGET_MAPPED (widget))
1067 if (!GTK_WIDGET_NO_WINDOW (widget))
1069 new_allocation.x = 0;
1070 new_allocation.y = 0;
1075 GdkWindow *window = widget->window;
1076 while (window != widget->parent->window)
1078 gint tx, ty, twidth, theight;
1079 gdk_window_get_size (window, &twidth, &theight);
1081 if (new_allocation.x < 0)
1083 new_allocation.width += new_allocation.x;
1084 new_allocation.x = 0;
1086 if (new_allocation.y < 0)
1088 new_allocation.height += new_allocation.y;
1089 new_allocation.y = 0;
1091 if (new_allocation.x + new_allocation.width > twidth)
1092 new_allocation.width = twidth - new_allocation.x;
1093 if (new_allocation.y + new_allocation.height > theight)
1094 new_allocation.height = theight - new_allocation.y;
1096 gdk_window_get_position (window, &tx, &ty);
1097 new_allocation.x += tx;
1099 new_allocation.y += ty;
1102 window = gdk_window_get_parent (window);
1106 if (data->toplevel ||
1107 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1108 (data->x < new_allocation.x + new_allocation.width) &&
1109 (data->y < new_allocation.y + new_allocation.height)))
1111 /* First, check if the drag is in a valid drop site in
1112 * one of our children
1114 if (GTK_IS_CONTAINER (widget))
1116 GtkDragFindData new_data = *data;
1118 new_data.x -= x_offset;
1119 new_data.y -= y_offset;
1120 new_data.found = FALSE;
1121 new_data.toplevel = FALSE;
1123 gtk_container_forall (GTK_CONTAINER (widget),
1124 (GtkCallback)gtk_drag_find_widget,
1127 data->found = new_data.found;
1130 /* If not, and this widget is registered as a drop site, check to
1131 * emit "drag_motion" to check if we are actually in
1135 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1137 data->found = data->callback (widget,
1139 data->x - new_allocation.x,
1140 data->y - new_allocation.y,
1142 /* If so, send a "drag_leave" to the last widget */
1145 if (data->info->widget && data->info->widget != widget)
1147 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1149 data->info->widget = widget;
1156 gtk_drag_proxy_begin (GtkWidget *widget, GtkDragDestInfo *dest_info)
1158 GtkDragSourceInfo *source_info;
1161 source_info = g_new0 (GtkDragSourceInfo, 1);
1162 source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1164 source_info->widget = widget;
1165 gtk_widget_ref (source_info->widget);
1166 source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1167 dest_info->context->targets,
1168 dest_info->context->actions);
1170 source_info->target_list = gtk_target_list_new (NULL, 0);
1171 tmp_list = dest_info->context->targets;
1174 gtk_target_list_add (source_info->target_list,
1175 GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1176 tmp_list = tmp_list->next;
1179 source_info->proxy_dest = dest_info;
1181 g_dataset_set_data (source_info->context, "gtk-info", source_info);
1183 gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget),
1185 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1188 dest_info->proxy_source = source_info;
1192 gtk_drag_dest_info_destroy (gpointer data)
1194 GtkDragDestInfo *info = data;
1200 gtk_drag_dest_realized (GtkWidget *widget)
1202 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1203 gdk_window_register_dnd (toplevel->window);
1207 gtk_drag_dest_site_destroy (gpointer data)
1209 GtkDragDestSite *site = data;
1211 if (site->target_list)
1212 gtk_target_list_unref (site->target_list);
1218 * Default drag handlers
1221 gtk_drag_dest_leave (GtkWidget *widget,
1222 GdkDragContext *context,
1225 GtkDragDestSite *site;
1227 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1228 g_return_if_fail (site != NULL);
1232 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1234 if (info->proxy_source && !info->dropped)
1235 gdk_drag_abort (info->proxy_source->context, time);
1241 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1242 gtk_drag_unhighlight (widget);
1244 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1245 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1248 site->have_drag = FALSE;
1253 gtk_drag_dest_motion (GtkWidget *widget,
1254 GdkDragContext *context,
1259 GtkDragDestSite *site;
1260 GdkDragAction action = 0;
1263 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1264 g_return_val_if_fail (site != NULL, FALSE);
1269 GdkEvent *current_event;
1270 GdkWindow *dest_window;
1271 GdkDragProtocol proto;
1273 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1275 if (!info->proxy_source)
1276 gtk_drag_proxy_begin (widget, info);
1278 current_event = gtk_get_current_event ();
1280 if (site->proxy_window)
1282 dest_window = site->proxy_window;
1283 proto = site->proxy_protocol;
1287 gdk_drag_find_window (info->proxy_source->context,
1289 current_event->dnd.x_root,
1290 current_event->dnd.y_root,
1291 &dest_window, &proto);
1294 gdk_drag_motion (info->proxy_source->context,
1296 current_event->dnd.x_root,
1297 current_event->dnd.y_root,
1298 context->suggested_action, time);
1300 selection = gdk_drag_get_selection (info->proxy_source->context);
1302 selection != gdk_drag_get_selection (info->context))
1303 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1305 gdk_event_free (current_event);
1310 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1312 if (context->suggested_action & site->actions)
1313 action = context->suggested_action;
1320 if ((site->actions & (1 << i)) &&
1321 (context->actions & (1 << i)))
1329 if (action && gtk_drag_dest_find_target (widget, site, context))
1331 if (!site->have_drag)
1333 site->have_drag = TRUE;
1334 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1335 gtk_drag_highlight (widget);
1338 gdk_drag_status (context, action, time);
1342 gdk_drag_status (context, 0, time);
1347 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1348 context, x, y, time, &retval);
1350 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1354 gtk_drag_dest_drop (GtkWidget *widget,
1355 GdkDragContext *context,
1360 GtkDragDestSite *site;
1361 GtkDragDestInfo *info;
1363 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1364 g_return_val_if_fail (site != NULL, FALSE);
1366 info = g_dataset_get_data (context, "gtk-info");
1367 g_return_val_if_fail (info != NULL, FALSE);
1374 if (info->proxy_source ||
1375 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1377 gtk_drag_drop (info->proxy_source, time);
1381 /* We need to synthesize a motion event, wait for a status,
1382 * and, if we get one a good one, do a drop.
1385 GdkEvent *current_event;
1387 GdkWindow *dest_window;
1388 GdkDragProtocol proto;
1390 gtk_drag_proxy_begin (widget, info);
1391 info->proxy_drop_wait = TRUE;
1392 info->proxy_drop_time = time;
1394 current_event = gtk_get_current_event ();
1396 if (site->proxy_window)
1398 dest_window = site->proxy_window;
1399 proto = site->proxy_protocol;
1403 gdk_drag_find_window (info->proxy_source->context,
1405 current_event->dnd.x_root,
1406 current_event->dnd.y_root,
1407 &dest_window, &proto);
1410 gdk_drag_motion (info->proxy_source->context,
1412 current_event->dnd.x_root,
1413 current_event->dnd.y_root,
1414 context->suggested_action, time);
1416 selection = gdk_drag_get_selection (info->proxy_source->context);
1418 selection != gdk_drag_get_selection (info->context))
1419 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1421 gdk_event_free (current_event);
1431 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1433 GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
1435 if (target == GDK_NONE)
1438 gtk_drag_get_data (widget, context, target, time);
1441 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1442 context, x, y, time, &retval);
1444 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1452 /*************************************************************
1453 * gtk_drag_begin: Start a drag operation
1456 * widget: Widget from which drag starts
1457 * handlers: List of handlers to supply the data for the drag
1458 * button: Button user used to start drag
1459 * time: Time of event starting drag
1462 *************************************************************/
1465 gtk_drag_begin (GtkWidget *widget,
1466 GtkTargetList *target_list,
1467 GdkDragAction actions,
1471 GtkDragSourceInfo *info;
1472 GList *targets = NULL;
1474 guint32 time = GDK_CURRENT_TIME;
1476 g_return_val_if_fail (widget != NULL, NULL);
1477 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1478 g_return_val_if_fail (target_list != NULL, NULL);
1481 time = gdk_event_get_time (event);
1483 info = g_new0 (GtkDragSourceInfo, 1);
1484 info->ipc_widget = gtk_drag_get_ipc_widget ();
1485 source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
1487 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1489 tmp_list = g_list_last (target_list->list);
1492 GtkTargetPair *pair = tmp_list->data;
1493 targets = g_list_prepend (targets,
1494 GINT_TO_POINTER (pair->target));
1495 tmp_list = tmp_list->prev;
1498 info->widget = widget;
1499 gtk_widget_ref (info->widget);
1501 info->context = gdk_drag_begin (info->ipc_widget->window,
1503 g_list_free (targets);
1505 g_dataset_set_data (info->context, "gtk-info", info);
1507 info->button = button;
1508 info->target_list = target_list;
1509 gtk_target_list_ref (target_list);
1511 info->cursor = NULL;
1512 info->status = GTK_DRAG_STATUS_DRAG;
1513 info->last_event = NULL;
1514 info->selections = NULL;
1515 info->icon_window = NULL;
1518 info->cursor = gtk_drag_get_cursor (
1519 gtk_drag_get_event_action (event, info->button, actions));
1521 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1524 /* We use a GTK grab here to override any grabs that the widget
1525 * we are dragging from might have held
1528 gtk_grab_add (info->ipc_widget);
1529 gdk_pointer_grab (info->ipc_widget->window, FALSE,
1530 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1531 GDK_BUTTON_RELEASE_MASK, NULL,
1532 info->cursor, time);
1534 if (event->type == GDK_MOTION_NOTIFY)
1535 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1539 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x, &y, NULL);
1544 if (info->icon_window)
1546 gdk_window_raise (info->icon_window->window);
1547 gtk_widget_set_uposition (info->icon_window, x - info->hot_x, y - info->hot_y);
1551 info->start_x = info->cur_x;
1552 info->start_y = info->cur_y;
1554 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1555 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1556 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1557 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1558 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1559 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1561 return info->context;
1564 /*************************************************************
1565 * gtk_drag_source_set:
1566 * Register a drop site, and possibly add default behaviors.
1569 * start_button_mask: Mask of allowed buttons to start drag
1570 * targets: Table of targets for this source
1572 * actions: Actions allowed for this source
1574 *************************************************************/
1577 gtk_drag_source_set (GtkWidget *widget,
1578 GdkModifierType start_button_mask,
1579 const GtkTargetEntry *targets,
1581 GdkDragAction actions)
1583 GtkDragSourceSite *site;
1585 g_return_if_fail (widget != NULL);
1587 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1589 gtk_widget_add_events (widget,
1590 gtk_widget_get_events (widget) |
1591 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1592 GDK_BUTTON_MOTION_MASK);
1596 if (site->target_list)
1597 gtk_target_list_unref (site->target_list);
1601 site = g_new0 (GtkDragSourceSite, 1);
1603 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1604 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1606 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1607 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1610 gtk_object_set_data_full (GTK_OBJECT (widget),
1612 site, gtk_drag_source_site_destroy);
1615 site->start_button_mask = start_button_mask;
1618 site->target_list = gtk_target_list_new (targets, n_targets);
1620 site->target_list = NULL;
1622 site->actions = actions;
1626 /*************************************************************
1627 * gtk_drag_source_unset
1628 * Unregister this widget as a drag source.
1632 *************************************************************/
1635 gtk_drag_source_unset (GtkWidget *widget)
1637 GtkDragSourceSite *site;
1639 g_return_if_fail (widget != NULL);
1641 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1645 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1646 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1650 /*************************************************************
1651 * gtk_drag_source_set_icon:
1652 * Set an icon for drags from this source.
1654 * colormap: Colormap for this icon
1658 *************************************************************/
1661 gtk_drag_source_set_icon (GtkWidget *widget,
1662 GdkColormap *colormap,
1666 GtkDragSourceSite *site;
1668 g_return_if_fail (widget != NULL);
1670 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1671 g_return_if_fail (site != NULL);
1674 gdk_colormap_unref (site->colormap);
1676 gdk_pixmap_unref (site->pixmap);
1678 gdk_pixmap_unref (site->mask);
1680 site->colormap = colormap;
1682 gdk_colormap_ref (colormap);
1684 site->pixmap = pixmap;
1686 gdk_pixmap_ref (pixmap);
1690 gdk_pixmap_ref (mask);
1693 /*************************************************************
1694 * gtk_drag_set_icon_widget:
1695 * Set a widget as the icon for a drag.
1702 *************************************************************/
1705 gtk_drag_set_icon_widget (GdkDragContext *context,
1710 GtkDragSourceInfo *info;
1712 g_return_if_fail (context != NULL);
1713 g_return_if_fail (widget != NULL);
1715 info = g_dataset_get_data (context, "gtk-info");
1716 gtk_drag_remove_icon (info);
1718 info->icon_window = widget;
1721 gtk_widget_set_uposition (widget, info->cur_x, info->cur_y);
1722 gtk_widget_ref (widget);
1723 gdk_window_raise (widget->window);
1724 gtk_widget_show (widget);
1727 info->hot_x = hot_x;
1728 info->hot_y = hot_y;
1731 /*************************************************************
1732 * gtk_drag_set_icon_pixmap:
1733 * Set a widget as the icon for a drag.
1736 * colormap: Colormap for the icon window.
1742 *************************************************************/
1745 gtk_drag_set_icon_pixmap (GdkDragContext *context,
1746 GdkColormap *colormap,
1755 g_return_if_fail (context != NULL);
1756 g_return_if_fail (colormap != NULL);
1757 g_return_if_fail (pixmap != NULL);
1759 gdk_window_get_size (pixmap, &width, &height);
1761 gtk_widget_push_visual (gdk_colormap_get_visual(colormap));
1762 gtk_widget_push_colormap (colormap);
1764 window = gtk_window_new (GTK_WINDOW_POPUP);
1765 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1766 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
1768 gtk_widget_pop_visual ();
1769 gtk_widget_pop_colormap ();
1771 gtk_widget_set_usize (window, width, height);
1772 gtk_widget_realize (window);
1774 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1777 gtk_widget_shape_combine_mask (window, mask, 0, 0);
1779 gtk_drag_set_icon_widget (context, window, hot_x, hot_y);
1782 /*************************************************************
1783 * gtk_drag_set_icon_default:
1784 * Set the icon for a drag to the default icon.
1788 *************************************************************/
1791 gtk_drag_set_icon_default (GdkDragContext *context)
1793 g_return_if_fail (context != NULL);
1795 if (!default_icon_pixmap)
1797 default_icon_colormap = gdk_colormap_get_system ();
1798 default_icon_pixmap =
1799 gdk_pixmap_colormap_create_from_xpm_d (NULL,
1800 default_icon_colormap,
1802 NULL, drag_default_xpm);
1803 default_icon_hot_x = -2;
1804 default_icon_hot_y = -2;
1807 gtk_drag_set_icon_pixmap (context,
1808 default_icon_colormap,
1809 default_icon_pixmap,
1812 default_icon_hot_y);
1815 /*************************************************************
1816 * gtk_drag_set_default_icon:
1817 * Set a default icon for all drags as a pixmap.
1819 * colormap: Colormap for the icon window.
1825 *************************************************************/
1828 gtk_drag_set_default_icon (GdkColormap *colormap,
1834 g_return_if_fail (colormap != NULL);
1835 g_return_if_fail (pixmap != NULL);
1837 if (default_icon_colormap)
1838 gdk_colormap_unref (default_icon_colormap);
1839 if (default_icon_pixmap)
1840 gdk_pixmap_unref (default_icon_pixmap);
1841 if (default_icon_mask)
1842 gdk_pixmap_unref (default_icon_pixmap);
1844 default_icon_colormap = colormap;
1845 gdk_colormap_ref (colormap);
1847 default_icon_pixmap = pixmap;
1848 gdk_pixmap_ref (pixmap);
1850 default_icon_mask = mask;
1852 gdk_pixmap_ref (mask);
1854 default_icon_hot_x = hot_x;
1855 default_icon_hot_y = hot_y;
1859 /*************************************************************
1860 * gtk_drag_source_handle_event:
1861 * Called from widget event handling code on Drag events
1865 * toplevel: Toplevel widget that received the event
1868 *************************************************************/
1871 gtk_drag_source_handle_event (GtkWidget *widget,
1874 GtkDragSourceInfo *info;
1875 GdkDragContext *context;
1877 g_return_if_fail (widget != NULL);
1878 g_return_if_fail (event != NULL);
1880 context = event->dnd.context;
1881 info = g_dataset_get_data (context, "gtk-info");
1885 switch (event->type)
1887 case GDK_DRAG_STATUS:
1891 if (info->proxy_dest)
1893 if (!event->dnd.send_event)
1895 if (info->proxy_dest->proxy_drop_wait)
1897 /* Aha - we can finally pass the MOTIF DROP on... */
1898 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
1902 gdk_drag_status (info->proxy_dest->context,
1903 event->dnd.context->action,
1910 cursor = gtk_drag_get_cursor (event->dnd.context->action);
1911 if (info->cursor != cursor)
1913 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window),
1914 PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
1915 ((GdkCursorPrivate *)cursor)->xcursor,
1917 info->cursor = cursor;
1920 if (info->last_event)
1922 gtk_drag_motion_cb (info->widget,
1923 (GdkEventMotion *)info->last_event,
1925 info->last_event = NULL;
1931 case GDK_DROP_FINISHED:
1932 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
1935 g_assert_not_reached();
1939 /*************************************************************
1940 * gtk_drag_source_check_selection:
1941 * Check if we've set up handlers/claimed the selection
1942 * for a given drag. If not, add them.
1946 *************************************************************/
1949 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
1955 tmp_list = info->selections;
1958 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
1960 tmp_list = tmp_list->next;
1963 gtk_selection_owner_set (info->ipc_widget, selection, time);
1964 info->selections = g_list_prepend (info->selections,
1965 GUINT_TO_POINTER (selection));
1967 tmp_list = info->target_list->list;
1970 GtkTargetPair *pair = tmp_list->data;
1972 gtk_selection_add_target (info->ipc_widget,
1976 tmp_list = tmp_list->next;
1979 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
1981 gtk_selection_add_target (info->ipc_widget,
1983 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
1984 TARGET_MOTIF_SUCCESS);
1985 gtk_selection_add_target (info->ipc_widget,
1987 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
1988 TARGET_MOTIF_FAILURE);
1991 gtk_selection_add_target (info->ipc_widget,
1993 gdk_atom_intern ("DELETE", FALSE),
1997 /*************************************************************
1998 * gtk_drag_drop_finished:
1999 * Clean up from the drag, and display snapback, if necessary.
2005 *************************************************************/
2008 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2012 gtk_drag_source_release_selections (info, time);
2014 if (info->proxy_dest)
2016 /* The time from the event isn't reliable for Xdnd drags */
2017 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2018 info->proxy_dest->proxy_drop_time);
2019 gtk_drag_source_info_destroy (info);
2025 gtk_drag_source_info_destroy (info);
2029 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2033 anim->n_steps = MAX (info->cur_x - info->start_x,
2034 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2035 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2036 if (info->icon_window)
2038 gtk_widget_show(info->icon_window);
2039 gdk_window_raise (info->icon_window->window);
2042 /* Mark the context as dead, so if the destination decides
2043 * to respond really late, we still are OK.
2045 g_dataset_set_data (info->context, "gtk-info", NULL);
2046 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2052 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2055 GList *tmp_list = info->selections;
2058 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2059 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2060 gtk_selection_owner_set (NULL, selection, time);
2061 tmp_list = tmp_list->next;
2064 g_list_free (info->selections);
2065 info->selections = NULL;
2068 /*************************************************************
2070 * Send a drop event.
2074 *************************************************************/
2077 gtk_drag_drop (GtkDragSourceInfo *info, guint32 time)
2079 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2081 GtkSelectionData selection_data;
2083 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2085 tmp_list = info->target_list->list;
2088 GtkTargetPair *pair = tmp_list->data;
2090 if (pair->target == target)
2092 selection_data.selection = GDK_NONE;
2093 selection_data.target = target;
2094 selection_data.data = NULL;
2095 selection_data.length = -1;
2097 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2098 info->context, &selection_data,
2102 /* FIXME: Should we check for length >= 0 here? */
2103 gtk_drag_drop_finished (info, TRUE, time);
2106 tmp_list = tmp_list->next;
2108 gtk_drag_drop_finished (info, FALSE, time);
2112 if (info->icon_window)
2113 gtk_widget_hide (info->icon_window);
2115 gdk_drag_drop (info->context, time);
2116 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2117 gtk_drag_abort_timeout,
2123 * Source side callbacks.
2127 gtk_drag_source_event_cb (GtkWidget *widget,
2131 GtkDragSourceSite *site;
2132 site = (GtkDragSourceSite *)data;
2134 switch (event->type)
2136 case GDK_BUTTON_PRESS:
2137 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2139 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2140 site->x = event->button.x;
2141 site->y = event->button.y;
2145 case GDK_BUTTON_RELEASE:
2146 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2148 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2152 case GDK_MOTION_NOTIFY:
2153 if (site->state & event->motion.state & site->start_button_mask)
2155 /* FIXME: This is really broken and can leave us
2161 if (site->state & event->motion.state &
2162 GDK_BUTTON1_MASK << (i - 1))
2166 if (MAX (abs(site->x - event->motion.x),
2167 abs(site->y - event->motion.y)) > 3)
2169 GtkDragSourceInfo *info;
2170 GdkDragContext *context;
2173 context = gtk_drag_begin (widget, site->target_list,
2178 info = g_dataset_get_data (context, "gtk-info");
2180 if (!info->icon_window)
2183 gtk_drag_set_icon_pixmap (context,
2186 site->mask, -2, -2);
2188 gtk_drag_set_icon_default (context);
2196 default: /* hit for 2/3BUTTON_PRESS */
2203 gtk_drag_source_site_destroy (gpointer data)
2205 GtkDragSourceSite *site = data;
2207 if (site->target_list)
2208 gtk_target_list_unref (site->target_list);
2211 gdk_pixmap_unref (site->pixmap);
2214 gdk_pixmap_unref (site->mask);
2220 gtk_drag_selection_get (GtkWidget *widget,
2221 GtkSelectionData *selection_data,
2226 GtkDragSourceInfo *info = data;
2227 static GdkAtom null_atom = GDK_NONE;
2231 null_atom = gdk_atom_intern ("NULL", FALSE);
2236 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2239 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2241 case TARGET_MOTIF_SUCCESS:
2242 gtk_drag_drop_finished (info, TRUE, time);
2243 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2245 case TARGET_MOTIF_FAILURE:
2246 gtk_drag_drop_finished (info, FALSE, time);
2247 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2250 if (info->proxy_dest)
2252 /* This is sort of dangerous and needs to be thought
2255 info->proxy_dest->proxy_data = selection_data;
2256 gtk_drag_get_data (info->widget,
2257 info->proxy_dest->context,
2258 selection_data->target,
2261 info->proxy_dest->proxy_data = NULL;
2265 if (gtk_target_list_find (info->target_list,
2266 selection_data->target,
2269 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2281 gtk_drag_anim_timeout (gpointer data)
2283 GtkDragAnim *anim = data;
2287 GDK_THREADS_ENTER ();
2289 if (anim->step == anim->n_steps)
2291 gtk_drag_source_info_destroy (anim->info);
2298 x = (anim->info->start_x * (anim->step + 1) +
2299 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2300 y = (anim->info->start_y * (anim->step + 1) +
2301 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2302 if (anim->info->icon_window)
2303 gtk_widget_set_uposition (anim->info->icon_window, x, y);
2310 GDK_THREADS_LEAVE ();
2316 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2318 if (info->icon_window)
2320 gtk_widget_hide (info->icon_window);
2321 gtk_widget_unref (info->icon_window);
2323 info->icon_window = NULL;
2328 gtk_drag_source_info_destroy (gpointer data)
2330 GtkDragSourceInfo *info = data;
2332 gtk_drag_remove_icon (data);
2334 if (!info->proxy_dest)
2335 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2339 gtk_widget_unref (info->widget);
2341 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2342 gtk_selection_remove_all (info->ipc_widget);
2343 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2344 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2345 gtk_drag_release_ipc_widget (info->ipc_widget);
2347 gtk_target_list_unref (info->target_list);
2349 g_dataset_set_data (info->context, "gtk-info", NULL);
2350 gdk_drag_context_unref (info->context);
2352 if (info->drop_timeout)
2353 gtk_timeout_remove (info->drop_timeout);
2358 /*************************************************************
2359 * gtk_drag_motion_cb:
2360 * "motion_notify_event" callback during drag.
2364 *************************************************************/
2367 gtk_drag_motion_cb (GtkWidget *widget,
2368 GdkEventMotion *event,
2371 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2373 GdkDragAction action;
2374 GdkWindow *window = NULL;
2375 GdkWindow *dest_window;
2376 GdkDragProtocol protocol;
2377 gint x_root, y_root;
2381 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2382 event->x_root = x_root;
2383 event->y_root = y_root;
2386 action = gtk_drag_get_event_action ((GdkEvent *)event,
2388 info->context->actions);
2390 info->cur_x = event->x_root - info->hot_x;
2391 info->cur_y = event->y_root - info->hot_y;
2393 if (info->icon_window)
2395 gdk_window_raise (info->icon_window->window);
2396 gtk_widget_set_uposition (info->icon_window, info->cur_x, info->cur_y);
2397 window = info->icon_window->window;
2400 gdk_drag_find_window (info->context,
2401 window, event->x_root, event->y_root,
2402 &dest_window, &protocol);
2404 if (gdk_drag_motion (info->context, dest_window, protocol,
2405 event->x_root, event->y_root, action,
2408 if (info->last_event)
2409 gdk_event_free ((GdkEvent *)info->last_event);
2411 info->last_event = gdk_event_copy ((GdkEvent *)event);
2415 gdk_window_unref (dest_window);
2417 selection = gdk_drag_get_selection (info->context);
2419 gtk_drag_source_check_selection (info, selection, event->time);
2422 /* We ignore the response, so we can respond precisely to the drop
2425 gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2431 /*************************************************************
2432 * gtk_drag_motion_cb:
2433 * "button_release_event" callback during drag.
2437 *************************************************************/
2440 gtk_drag_button_release_cb (GtkWidget *widget,
2441 GdkEventButton *event,
2444 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2445 GtkWidget *source_widget = info->widget;
2446 GdkEvent send_event;
2448 gtk_widget_ref (source_widget);
2450 if (event->button != info->button)
2453 gdk_pointer_ungrab (event->time);
2455 if ((info->context->action != 0) && (info->context->dest_window != NULL))
2457 gtk_drag_drop (info, event->time);
2461 gdk_drag_abort (info->context, event->time);
2462 gtk_drag_drop_finished (info, FALSE, event->time);
2465 gtk_grab_remove (widget);
2466 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
2467 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2469 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
2470 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2473 /* Send on a release pair to the the original
2474 * widget to convince it to release its grab. We need to
2475 * call gtk_propagate_event() here, instead of
2476 * gtk_widget_event() because widget like GtkList may
2477 * expect propagation.
2480 send_event.button.type = GDK_BUTTON_RELEASE;
2481 send_event.button.window = GDK_ROOT_PARENT ();
2482 send_event.button.send_event = TRUE;
2483 send_event.button.time = event->time;
2484 send_event.button.x = 0;
2485 send_event.button.y = 0;
2486 send_event.button.pressure = 0.;
2487 send_event.button.xtilt = 0.;
2488 send_event.button.ytilt = 0.;
2489 send_event.button.state = event->state;
2490 send_event.button.button = event->button;
2491 send_event.button.source = GDK_SOURCE_PEN;
2492 send_event.button.deviceid = GDK_CORE_POINTER;
2493 send_event.button.x_root = 0;
2494 send_event.button.y_root = 0;
2496 gtk_propagate_event (source_widget, &send_event);
2498 gtk_widget_unref (source_widget);
2504 gtk_drag_abort_timeout (gpointer data)
2506 GtkDragSourceInfo *info = data;
2507 guint32 time = GDK_CURRENT_TIME;
2509 if (info->proxy_dest)
2510 time = info->proxy_dest->proxy_drop_time;
2512 info->drop_timeout = 0;
2513 gtk_drag_drop_finished (info, FALSE, time);