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 Lesser 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 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser 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.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27 #include "gdkconfig.h"
29 #include "gdk/gdkkeysyms.h"
33 #include "gtkinvisible.h"
35 #include "gtksignal.h"
37 #include "gtkwindow.h"
39 static GSList *drag_widgets = NULL;
40 static GSList *source_widgets = NULL;
42 typedef struct _GtkDragSourceSite GtkDragSourceSite;
43 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
44 typedef struct _GtkDragDestSite GtkDragDestSite;
45 typedef struct _GtkDragDestInfo GtkDragDestInfo;
46 typedef struct _GtkDragAnim GtkDragAnim;
47 typedef struct _GtkDragFindData GtkDragFindData;
57 struct _GtkDragSourceSite
59 GdkModifierType start_button_mask;
60 GtkTargetList *target_list; /* Targets for drag data */
61 GdkDragAction actions; /* Possible actions */
64 GtkImageType icon_type;
67 GtkImagePixmapData pixmap;
68 GtkImagePixbufData pixbuf;
69 GtkImageStockData stock;
73 GdkColormap *colormap; /* Colormap for drag icon */
75 /* Stored button press information to detect drag beginning */
80 struct _GtkDragSourceInfo
83 GtkTargetList *target_list; /* Targets for drag data */
84 GdkDragAction possible_actions; /* Actions allowed by source */
85 GdkDragContext *context; /* drag context */
86 GtkWidget *icon_window; /* Window for drag */
87 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
88 GdkCursor *cursor; /* Cursor for drag */
89 gint hot_x, hot_y; /* Hot spot for drag */
90 gint button; /* mouse button starting drag */
92 GtkDragStatus status; /* drag status */
93 GdkEvent *last_event; /* motion event waiting for response */
95 gint start_x, start_y; /* Initial position */
96 gint cur_x, cur_y; /* Current Position */
98 GList *selections; /* selections we've claimed */
100 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
102 guint drop_timeout; /* Timeout for aborting drop */
103 guint destroy_icon : 1; /* If true, destroy icon_window
107 struct _GtkDragDestSite
109 GtkDestDefaults flags;
110 GtkTargetList *target_list;
111 GdkDragAction actions;
112 GdkWindow *proxy_window;
113 GdkDragProtocol proxy_protocol;
114 gboolean do_proxy : 1;
115 gboolean proxy_coords : 1;
116 gboolean have_drag : 1;
119 struct _GtkDragDestInfo
121 GtkWidget *widget; /* Widget in which drag is in */
122 GdkDragContext *context; /* Drag context */
123 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
124 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
125 gboolean dropped : 1; /* Set after we receive a drop */
126 guint32 proxy_drop_time; /* Timestamp for proxied drop */
127 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
128 * status reply before sending
131 gint drop_x, drop_y; /* Position of drop */
134 #define DROP_ABORT_TIME 300000
136 #define ANIM_STEP_TIME 50
137 #define ANIM_STEP_LENGTH 50
138 #define ANIM_MIN_STEPS 5
139 #define ANIM_MAX_STEPS 10
143 GtkDragSourceInfo *info;
148 struct _GtkDragFindData
152 GdkDragContext *context;
153 GtkDragDestInfo *info;
156 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
157 gint x, gint y, guint32 time);
161 /* Enumeration for some targets we handle internally */
164 TARGET_MOTIF_SUCCESS = 0x40000000,
165 TARGET_MOTIF_FAILURE,
171 static GdkPixmap *default_icon_pixmap = NULL;
172 static GdkPixmap *default_icon_mask = NULL;
173 static GdkColormap *default_icon_colormap = NULL;
174 static gint default_icon_hot_x;
175 static gint default_icon_hot_y;
177 /* Forward declarations */
178 static void gtk_drag_get_event_actions (GdkEvent *event,
180 GdkDragAction actions,
181 GdkDragAction *suggested_action,
182 GdkDragAction *possible_actions);
183 static GdkCursor * gtk_drag_get_cursor (GdkDragAction action);
184 static GtkWidget *gtk_drag_get_ipc_widget (void);
185 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
187 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
188 GdkEventExpose *event,
191 static void gtk_drag_selection_received (GtkWidget *widget,
192 GtkSelectionData *selection_data,
195 static void gtk_drag_find_widget (GtkWidget *widget,
196 GtkDragFindData *data);
197 static void gtk_drag_proxy_begin (GtkWidget *widget,
198 GtkDragDestInfo *dest_info,
200 static void gtk_drag_dest_realized (GtkWidget *widget);
201 static void gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
202 GtkWidget *previous_toplevel);
203 static void gtk_drag_dest_site_destroy (gpointer data);
204 static void gtk_drag_dest_leave (GtkWidget *widget,
205 GdkDragContext *context,
207 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
208 GdkDragContext *context,
212 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
213 GdkDragContext *context,
218 static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDragContext *context,
220 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
222 static void gtk_drag_clear_source_info (GdkDragContext *context);
224 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
227 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
229 static void gtk_drag_drop (GtkDragSourceInfo *info,
231 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
235 static gint gtk_drag_source_event_cb (GtkWidget *widget,
238 static void gtk_drag_source_site_destroy (gpointer data);
239 static void gtk_drag_selection_get (GtkWidget *widget,
240 GtkSelectionData *selection_data,
244 static gint gtk_drag_anim_timeout (gpointer data);
245 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
246 static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
247 static void gtk_drag_update (GtkDragSourceInfo *info,
251 static gint gtk_drag_motion_cb (GtkWidget *widget,
252 GdkEventMotion *event,
254 static gint gtk_drag_key_cb (GtkWidget *widget,
257 static gint gtk_drag_button_release_cb (GtkWidget *widget,
258 GdkEventButton *event,
260 static gint gtk_drag_abort_timeout (gpointer data);
262 /************************
263 * Cursor and Icon data *
264 ************************/
266 #define action_ask_width 16
267 #define action_ask_height 16
268 static const guchar action_ask_bits[] = {
269 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
270 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
271 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
273 #define action_ask_mask_width 16
274 #define action_ask_mask_height 16
275 static const guchar action_ask_mask_bits[] = {
276 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
277 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
278 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
280 #define action_copy_width 16
281 #define action_copy_height 16
282 static const guchar action_copy_bits[] = {
283 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
284 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
285 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
287 #define action_copy_mask_width 16
288 #define action_copy_mask_height 16
289 static const guchar action_copy_mask_bits[] = {
290 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
291 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
292 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
294 #define action_move_width 16
295 #define action_move_height 16
296 static const guchar action_move_bits[] = {
297 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
298 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
299 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
301 #define action_move_mask_width 16
302 #define action_move_mask_height 16
303 static const guchar action_move_mask_bits[] = {
304 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
305 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
306 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
308 #define action_link_width 16
309 #define action_link_height 16
310 static const guchar action_link_bits[] = {
311 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
312 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
313 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
315 #define action_link_mask_width 16
316 #define action_link_mask_height 16
317 static const guchar action_link_mask_bits[] = {
318 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
319 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
320 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
322 #define action_none_width 16
323 #define action_none_height 16
324 static const guchar action_none_bits[] = {
325 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
326 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
327 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
329 #define action_none_mask_width 16
330 #define action_none_mask_height 16
331 static const guchar action_none_mask_bits[] = {
332 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
333 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
334 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
336 #define CURSOR_WIDTH 16
337 #define CURSOR_HEIGHT 16
340 GdkDragAction action;
345 { GDK_ACTION_DEFAULT, 0 },
346 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
347 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
348 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
349 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
350 { 0 , action_none_bits, action_none_mask_bits, NULL },
353 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
355 /*********************
356 * Utility functions *
357 *********************/
359 /*************************************************************
360 * gtk_drag_get_ipc_widget:
361 * Return a invisible, off-screen, override-redirect
366 *************************************************************/
369 gtk_drag_get_ipc_widget (void)
375 GSList *tmp = drag_widgets;
376 result = drag_widgets->data;
377 drag_widgets = drag_widgets->next;
378 g_slist_free_1 (tmp);
382 result = gtk_invisible_new ();
383 gtk_widget_show (result);
389 /***************************************************************
390 * gtk_drag_release_ipc_widget:
391 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
393 * widget: the widget to release.
395 ***************************************************************/
398 gtk_drag_release_ipc_widget (GtkWidget *widget)
400 drag_widgets = g_slist_prepend (drag_widgets, widget);
404 gtk_drag_get_event_time (GdkEvent *event)
406 guint32 tm = GDK_CURRENT_TIME;
411 case GDK_MOTION_NOTIFY:
412 tm = event->motion.time; break;
413 case GDK_BUTTON_PRESS:
414 case GDK_2BUTTON_PRESS:
415 case GDK_3BUTTON_PRESS:
416 case GDK_BUTTON_RELEASE:
417 tm = event->button.time; break;
419 case GDK_KEY_RELEASE:
420 tm = event->key.time; break;
421 case GDK_ENTER_NOTIFY:
422 case GDK_LEAVE_NOTIFY:
423 tm = event->crossing.time; break;
424 case GDK_PROPERTY_NOTIFY:
425 tm = event->property.time; break;
426 case GDK_SELECTION_CLEAR:
427 case GDK_SELECTION_REQUEST:
428 case GDK_SELECTION_NOTIFY:
429 tm = event->selection.time; break;
430 case GDK_PROXIMITY_IN:
431 case GDK_PROXIMITY_OUT:
432 tm = event->proximity.time; break;
433 default: /* use current time */
441 gtk_drag_get_event_actions (GdkEvent *event,
443 GdkDragAction actions,
444 GdkDragAction *suggested_action,
445 GdkDragAction *possible_actions)
447 *suggested_action = 0;
448 *possible_actions = 0;
452 GdkModifierType state = 0;
456 case GDK_MOTION_NOTIFY:
457 state = event->motion.state;
459 case GDK_BUTTON_PRESS:
460 case GDK_2BUTTON_PRESS:
461 case GDK_3BUTTON_PRESS:
462 case GDK_BUTTON_RELEASE:
463 state = event->button.state;
466 case GDK_KEY_RELEASE:
467 state = event->key.state;
469 case GDK_ENTER_NOTIFY:
470 case GDK_LEAVE_NOTIFY:
471 state = event->crossing.state;
477 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
479 *suggested_action = GDK_ACTION_ASK;
480 *possible_actions = actions;
482 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
484 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
486 if (actions & GDK_ACTION_LINK)
488 *suggested_action = GDK_ACTION_LINK;
489 *possible_actions = GDK_ACTION_LINK;
492 else if (state & GDK_CONTROL_MASK)
494 if (actions & GDK_ACTION_COPY)
496 *suggested_action = GDK_ACTION_COPY;
497 *possible_actions = GDK_ACTION_COPY;
503 if (actions & GDK_ACTION_MOVE)
505 *suggested_action = GDK_ACTION_MOVE;
506 *possible_actions = GDK_ACTION_MOVE;
513 *possible_actions = actions;
515 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
516 *suggested_action = GDK_ACTION_ASK;
517 else if (actions & GDK_ACTION_COPY)
518 *suggested_action = GDK_ACTION_COPY;
519 else if (actions & GDK_ACTION_MOVE)
520 *suggested_action = GDK_ACTION_MOVE;
521 else if (actions & GDK_ACTION_LINK)
522 *suggested_action = GDK_ACTION_LINK;
527 *possible_actions = actions;
529 if (actions & GDK_ACTION_COPY)
530 *suggested_action = GDK_ACTION_COPY;
531 else if (actions & GDK_ACTION_MOVE)
532 *suggested_action = GDK_ACTION_MOVE;
533 else if (actions & GDK_ACTION_LINK)
534 *suggested_action = GDK_ACTION_LINK;
541 gtk_drag_get_cursor (GdkDragAction action)
545 for (i = 0 ; i < n_drag_cursors - 1; i++)
546 if (drag_cursors[i].action == action)
549 if (drag_cursors[i].cursor == NULL)
554 gdk_bitmap_create_from_data (NULL,
555 drag_cursors[i].bits,
556 CURSOR_WIDTH, CURSOR_HEIGHT);
558 gdk_bitmap_create_from_data (NULL,
559 drag_cursors[i].mask,
560 CURSOR_WIDTH, CURSOR_HEIGHT);
562 gdk_color_white (gdk_colormap_get_system (), &bg);
563 gdk_color_black (gdk_colormap_get_system (), &fg);
565 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
567 gdk_pixmap_unref (pixmap);
568 gdk_pixmap_unref (mask);
571 return drag_cursors[i].cursor;
574 /********************
576 ********************/
578 /*************************************************************
580 * Get the data for a drag or drop
582 * context - drag context
583 * target - format to retrieve the data in.
584 * time - timestamp of triggering event.
587 *************************************************************/
590 gtk_drag_get_data (GtkWidget *widget,
591 GdkDragContext *context,
595 GtkWidget *selection_widget;
597 g_return_if_fail (widget != NULL);
598 g_return_if_fail (context != NULL);
600 selection_widget = gtk_drag_get_ipc_widget ();
602 gdk_drag_context_ref (context);
603 gtk_widget_ref (widget);
605 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
606 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
608 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
610 gtk_selection_convert (selection_widget,
611 gdk_drag_get_selection (context),
617 /*************************************************************
618 * gtk_drag_get_source_widget:
619 * Get the widget the was the source of this drag, if
620 * the drag originated from this application.
622 * context: The drag context for this drag
624 * The source widget, or NULL if the drag originated from
625 * a different application.
626 *************************************************************/
629 gtk_drag_get_source_widget (GdkDragContext *context)
633 tmp_list = source_widgets;
636 GtkWidget *ipc_widget = tmp_list->data;
638 if (ipc_widget->window == context->source_window)
640 GtkDragSourceInfo *info;
641 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
643 return info ? info->widget : NULL;
646 tmp_list = tmp_list->next;
652 /*************************************************************
654 * Notify the drag source that the transfer of data
657 * context: The drag context for this drag
658 * success: Was the data successfully transferred?
659 * time: The timestamp to use when notifying the destination.
661 *************************************************************/
664 gtk_drag_finish (GdkDragContext *context,
669 GdkAtom target = GDK_NONE;
671 g_return_if_fail (context != NULL);
675 target = gdk_atom_intern ("DELETE", FALSE);
677 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
679 target = gdk_atom_intern (success ?
680 "XmTRANSFER_SUCCESS" :
681 "XmTRANSFER_FAILURE",
685 if (target != GDK_NONE)
687 GtkWidget *selection_widget = gtk_drag_get_ipc_widget ();
689 gdk_drag_context_ref (context);
691 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
692 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
693 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
696 gtk_selection_convert (selection_widget,
697 gdk_drag_get_selection (context),
702 if (!(success && del))
703 gdk_drop_finish (context, success, time);
706 /*************************************************************
707 * gtk_drag_highlight_expose:
708 * Callback for expose_event for highlighted widgets.
714 *************************************************************/
717 gtk_drag_highlight_expose (GtkWidget *widget,
718 GdkEventExpose *event,
721 gint x, y, width, height;
723 if (GTK_WIDGET_DRAWABLE (widget))
725 if (GTK_WIDGET_NO_WINDOW (widget))
727 x = widget->allocation.x;
728 y = widget->allocation.y;
729 width = widget->allocation.width;
730 height = widget->allocation.height;
736 gdk_window_get_size (widget->window, &width, &height);
739 gtk_draw_shadow (widget->style, widget->window,
740 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
741 x, y, width, height);
743 gdk_draw_rectangle (widget->window,
744 widget->style->black_gc,
746 x, y, width - 1, height - 1);
752 /*************************************************************
753 * gtk_drag_highlight:
754 * Highlight the given widget in the default manner.
758 *************************************************************/
761 gtk_drag_highlight (GtkWidget *widget)
763 gtk_signal_connect (GTK_OBJECT (widget), "expose_event",
764 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
767 gtk_widget_queue_draw (widget);
770 /*************************************************************
771 * gtk_drag_unhighlight:
772 * Refresh the given widget to remove the highlight.
776 *************************************************************/
779 gtk_drag_unhighlight (GtkWidget *widget)
781 g_return_if_fail (widget != NULL);
783 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
784 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
787 gtk_widget_queue_clear (widget);
791 gtk_drag_dest_set_internal (GtkWidget *widget,
792 GtkDragDestSite *site)
794 GtkDragDestSite *old_site;
796 g_return_if_fail (widget != NULL);
798 /* HACK, do this in the destroy */
799 old_site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
801 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), old_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), site);
808 gtk_signal_connect (GTK_OBJECT (widget), "hierarchy_changed",
809 GTK_SIGNAL_FUNC (gtk_drag_dest_hierarchy_changed), site);
811 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
812 site, gtk_drag_dest_site_destroy);
816 /*************************************************************
818 * Register a drop site, and possibly add default behaviors.
821 * flags: Which types of default drag behavior to use
822 * targets: Table of targets that can be accepted
823 * n_targets: Number of of entries in targets
826 *************************************************************/
829 gtk_drag_dest_set (GtkWidget *widget,
830 GtkDestDefaults flags,
831 const GtkTargetEntry *targets,
833 GdkDragAction actions)
835 GtkDragDestSite *site;
837 g_return_if_fail (widget != NULL);
839 site = g_new (GtkDragDestSite, 1);
842 site->have_drag = FALSE;
844 site->target_list = gtk_target_list_new (targets, n_targets);
846 site->target_list = NULL;
848 site->actions = actions;
849 site->do_proxy = FALSE;
851 gtk_drag_dest_set_internal (widget, site);
854 /*************************************************************
855 * gtk_drag_dest_set_proxy:
856 * Set up this widget to proxy drags elsewhere.
859 * proxy_window: window to which forward drag events
860 * protocol: Drag protocol which the dest widget accepts
861 * use_coordinates: If true, send the same coordinates to the
862 * destination, because it is a embedded
865 *************************************************************/
868 gtk_drag_dest_set_proxy (GtkWidget *widget,
869 GdkWindow *proxy_window,
870 GdkDragProtocol protocol,
871 gboolean use_coordinates)
873 GtkDragDestSite *site;
875 g_return_if_fail (widget != NULL);
877 site = g_new (GtkDragDestSite, 1);
880 site->have_drag = FALSE;
881 site->target_list = NULL;
883 site->proxy_window = proxy_window;
885 gdk_window_ref (proxy_window);
886 site->do_proxy = TRUE;
887 site->proxy_protocol = protocol;
888 site->proxy_coords = use_coordinates;
890 gtk_drag_dest_set_internal (widget, site);
893 /*************************************************************
894 * gtk_drag_dest_unset
895 * Unregister this widget as a drag target.
899 *************************************************************/
902 gtk_drag_dest_unset (GtkWidget *widget)
904 g_return_if_fail (widget != NULL);
906 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
910 * gtk_drag_dest_get_target_list:
911 * @widget: a #GtkWidget
913 * Returns the list of targets this widget can accept from
916 * Return value: the #GtkTargetList, or %NULL if none
919 gtk_drag_dest_get_target_list (GtkWidget *widget)
921 GtkDragDestSite *site;
923 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
925 return site ? site->target_list : NULL;
929 * gtk_drag_dest_set_target_list:
930 * @widget: a #GtkWidget that's a drag destination
931 * @target_list: list of droppable targets, or %NULL for none
933 * Sets the target types that this widget can accept from drag-and-drop.
934 * The widget must first be made into a drag destination with
935 * gtk_drag_dest_set().
938 gtk_drag_dest_set_target_list (GtkWidget *widget,
939 GtkTargetList *target_list)
941 GtkDragDestSite *site;
943 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
947 g_warning ("can't set a target list on a widget until you've called gtk_drag_dest_set() to make the widget into a drag destination");
952 gtk_target_list_ref (target_list);
954 if (site->target_list)
955 gtk_target_list_unref (site->target_list);
957 site->target_list = target_list;
961 /*************************************************************
962 * _gtk_drag_dest_handle_event:
963 * Called from widget event handling code on Drag events
967 * toplevel: Toplevel widget that received the event
970 *************************************************************/
973 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
976 GtkDragDestInfo *info;
977 GdkDragContext *context;
979 g_return_if_fail (toplevel != NULL);
980 g_return_if_fail (event != NULL);
982 context = event->dnd.context;
984 info = gtk_drag_get_dest_info (context, TRUE);
986 /* Find the widget for the event */
995 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1000 case GDK_DRAG_MOTION:
1001 case GDK_DROP_START:
1003 GtkDragFindData data;
1006 if (event->type == GDK_DROP_START)
1008 info->dropped = TRUE;
1009 /* We send a leave here so that the widget unhighlights
1014 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1015 info->widget = NULL;
1019 gdk_window_get_origin (toplevel->window, &tx, &ty);
1021 data.x = event->dnd.x_root - tx;
1022 data.y = event->dnd.y_root - ty;
1023 data.context = context;
1026 data.toplevel = TRUE;
1027 data.callback = (event->type == GDK_DRAG_MOTION) ?
1028 gtk_drag_dest_motion : gtk_drag_dest_drop;
1029 data.time = event->dnd.time;
1031 gtk_drag_find_widget (toplevel, &data);
1033 if (info->widget && !data.found)
1035 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1036 info->widget = NULL;
1041 if (event->type == GDK_DRAG_MOTION)
1044 gdk_drag_status (context, 0, event->dnd.time);
1046 else if (event->type == GDK_DROP_START && !info->proxy_source)
1048 gdk_drop_reply (context, data.found, event->dnd.time);
1049 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1050 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1056 g_assert_not_reached ();
1061 * gtk_drag_dest_find_target:
1062 * @widget: drag destination widget
1063 * @context: drag context
1064 * @target_list: list of droppable targets, or %NULL to use
1065 * gtk_drag_dest_get_target_list (@widget).
1067 * Looks for a match between @context->targets and the
1068 * @dest_target_list, returning the first matching target, otherwise
1069 * returning %GDK_NONE. @dest_target_list should usually be the return
1070 * value from gtk_drag_dest_get_target_list(), but some widgets may
1071 * have different valid targets for different parts of the widget; in
1072 * that case, they will have to implement a drag_motion handler that
1073 * passes the correct target list to this function.
1075 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1078 gtk_drag_dest_find_target (GtkWidget *widget,
1079 GdkDragContext *context,
1080 GtkTargetList *target_list)
1083 GList *tmp_source = NULL;
1084 GtkWidget *source_widget;
1086 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1087 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1089 source_widget = gtk_drag_get_source_widget (context);
1091 if (target_list == NULL)
1092 target_list = gtk_drag_dest_get_target_list (widget);
1094 if (target_list == NULL)
1097 tmp_target = target_list->list;
1100 GtkTargetPair *pair = tmp_target->data;
1101 tmp_source = context->targets;
1104 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1106 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1107 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1108 return pair->target;
1112 tmp_source = tmp_source->next;
1114 tmp_target = tmp_target->next;
1121 gtk_drag_selection_received (GtkWidget *widget,
1122 GtkSelectionData *selection_data,
1126 GdkDragContext *context;
1127 GtkDragDestInfo *info;
1128 GtkWidget *drop_widget;
1132 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1133 info = gtk_drag_get_dest_info (context, FALSE);
1135 if (info->proxy_data &&
1136 info->proxy_data->target == selection_data->target)
1138 gtk_selection_data_set (info->proxy_data,
1139 selection_data->type,
1140 selection_data->format,
1141 selection_data->data,
1142 selection_data->length);
1147 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1149 gtk_drag_finish (context, TRUE, FALSE, time);
1151 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1152 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1158 GtkDragDestSite *site;
1160 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1162 if (site && site->target_list)
1166 if (gtk_target_list_find (site->target_list,
1167 selection_data->target,
1170 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1171 selection_data->length >= 0)
1172 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1173 "drag_data_received",
1174 context, info->drop_x, info->drop_y,
1181 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1182 "drag_data_received",
1183 context, info->drop_x, info->drop_y,
1184 selection_data, 0, time);
1187 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1190 gtk_drag_finish (context,
1191 (selection_data->length >= 0),
1192 (context->action == GDK_ACTION_MOVE),
1196 gtk_widget_unref (drop_widget);
1199 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1200 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1203 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1204 gdk_drag_context_unref (context);
1206 gtk_drag_release_ipc_widget (widget);
1209 /*************************************************************
1210 * gtk_drag_find_widget:
1211 * Recursive callback used to locate widgets for
1212 * DRAG_MOTION and DROP_START events.
1216 *************************************************************/
1219 gtk_drag_find_widget (GtkWidget *widget,
1220 GtkDragFindData *data)
1222 GtkAllocation new_allocation;
1226 new_allocation = widget->allocation;
1228 if (data->found || !GTK_WIDGET_MAPPED (widget))
1231 /* Note that in the following code, we only count the
1232 * position as being inside a WINDOW widget if it is inside
1233 * widget->window; points that are outside of widget->window
1234 * but within the allocation are not counted. This is consistent
1235 * with the way we highlight drag targets.
1237 if (!GTK_WIDGET_NO_WINDOW (widget))
1239 new_allocation.x = 0;
1240 new_allocation.y = 0;
1245 GdkWindow *window = widget->window;
1246 while (window != widget->parent->window)
1248 gint tx, ty, twidth, theight;
1249 gdk_window_get_size (window, &twidth, &theight);
1251 if (new_allocation.x < 0)
1253 new_allocation.width += new_allocation.x;
1254 new_allocation.x = 0;
1256 if (new_allocation.y < 0)
1258 new_allocation.height += new_allocation.y;
1259 new_allocation.y = 0;
1261 if (new_allocation.x + new_allocation.width > twidth)
1262 new_allocation.width = twidth - new_allocation.x;
1263 if (new_allocation.y + new_allocation.height > theight)
1264 new_allocation.height = theight - new_allocation.y;
1266 gdk_window_get_position (window, &tx, &ty);
1267 new_allocation.x += tx;
1269 new_allocation.y += ty;
1272 window = gdk_window_get_parent (window);
1276 if (data->toplevel ||
1277 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1278 (data->x < new_allocation.x + new_allocation.width) &&
1279 (data->y < new_allocation.y + new_allocation.height)))
1281 /* First, check if the drag is in a valid drop site in
1282 * one of our children
1284 if (GTK_IS_CONTAINER (widget))
1286 GtkDragFindData new_data = *data;
1288 new_data.x -= x_offset;
1289 new_data.y -= y_offset;
1290 new_data.found = FALSE;
1291 new_data.toplevel = FALSE;
1293 gtk_container_forall (GTK_CONTAINER (widget),
1294 (GtkCallback)gtk_drag_find_widget,
1297 data->found = new_data.found;
1300 /* If not, and this widget is registered as a drop site, check to
1301 * emit "drag_motion" to check if we are actually in
1305 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1307 data->found = data->callback (widget,
1309 data->x - new_allocation.x,
1310 data->y - new_allocation.y,
1312 /* If so, send a "drag_leave" to the last widget */
1315 if (data->info->widget && data->info->widget != widget)
1317 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1319 data->info->widget = widget;
1326 gtk_drag_proxy_begin (GtkWidget *widget,
1327 GtkDragDestInfo *dest_info,
1330 GtkDragSourceInfo *source_info;
1332 GdkDragContext *context;
1333 GtkWidget *ipc_widget;
1335 if (dest_info->proxy_source)
1337 gdk_drag_abort (dest_info->proxy_source->context, time);
1338 gtk_drag_source_info_destroy (dest_info->proxy_source);
1339 dest_info->proxy_source = NULL;
1342 ipc_widget = gtk_drag_get_ipc_widget ();
1343 context = gdk_drag_begin (ipc_widget->window,
1344 dest_info->context->targets);
1346 source_info = gtk_drag_get_source_info (context, TRUE);
1348 source_info->ipc_widget = ipc_widget;
1349 source_info->widget = gtk_widget_ref (widget);
1351 source_info->target_list = gtk_target_list_new (NULL, 0);
1352 tmp_list = dest_info->context->targets;
1355 gtk_target_list_add (source_info->target_list,
1356 GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1357 tmp_list = tmp_list->next;
1360 source_info->proxy_dest = dest_info;
1362 gtk_signal_connect (GTK_OBJECT (ipc_widget),
1364 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1367 dest_info->proxy_source = source_info;
1371 gtk_drag_dest_info_destroy (gpointer data)
1373 GtkDragDestInfo *info = data;
1378 static GtkDragDestInfo *
1379 gtk_drag_get_dest_info (GdkDragContext *context,
1382 GtkDragDestInfo *info;
1383 static GQuark info_quark = 0;
1385 info_quark = g_quark_from_static_string ("gtk-dest-info");
1387 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1388 if (!info && create)
1390 info = g_new (GtkDragDestInfo, 1);
1391 info->widget = NULL;
1392 info->context = context;
1393 info->proxy_source = NULL;
1394 info->proxy_data = NULL;
1395 info->dropped = FALSE;
1396 info->proxy_drop_wait = FALSE;
1397 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1398 info, gtk_drag_dest_info_destroy);
1404 static GQuark dest_info_quark = 0;
1406 static GtkDragSourceInfo *
1407 gtk_drag_get_source_info (GdkDragContext *context,
1410 GtkDragSourceInfo *info;
1411 if (!dest_info_quark)
1412 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1414 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1415 if (!info && create)
1417 info = g_new0 (GtkDragSourceInfo, 1);
1418 info->context = context;
1419 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1426 gtk_drag_clear_source_info (GdkDragContext *context)
1428 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1432 gtk_drag_dest_realized (GtkWidget *widget)
1434 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1436 if (GTK_WIDGET_TOPLEVEL (toplevel))
1437 gdk_window_register_dnd (toplevel->window);
1441 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1442 GtkWidget *previous_toplevel)
1444 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1446 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1447 gdk_window_register_dnd (toplevel->window);
1451 gtk_drag_dest_site_destroy (gpointer data)
1453 GtkDragDestSite *site = data;
1455 if (site->target_list)
1456 gtk_target_list_unref (site->target_list);
1462 * Default drag handlers
1465 gtk_drag_dest_leave (GtkWidget *widget,
1466 GdkDragContext *context,
1469 GtkDragDestSite *site;
1471 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1472 g_return_if_fail (site != NULL);
1476 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1478 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1480 gdk_drag_abort (info->proxy_source->context, time);
1481 gtk_drag_source_info_destroy (info->proxy_source);
1482 info->proxy_source = NULL;
1489 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1490 gtk_drag_unhighlight (widget);
1492 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1493 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1496 site->have_drag = FALSE;
1501 gtk_drag_dest_motion (GtkWidget *widget,
1502 GdkDragContext *context,
1507 GtkDragDestSite *site;
1508 GdkDragAction action = 0;
1511 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1512 g_return_val_if_fail (site != NULL, FALSE);
1517 GdkEvent *current_event;
1518 GdkWindow *dest_window;
1519 GdkDragProtocol proto;
1521 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1523 if (!info->proxy_source || info->proxy_source->widget != widget)
1524 gtk_drag_proxy_begin (widget, info, time);
1526 current_event = gtk_get_current_event ();
1528 if (site->proxy_window)
1530 dest_window = site->proxy_window;
1531 proto = site->proxy_protocol;
1535 gdk_drag_find_window (info->proxy_source->context,
1537 current_event->dnd.x_root,
1538 current_event->dnd.y_root,
1539 &dest_window, &proto);
1542 gdk_drag_motion (info->proxy_source->context,
1544 current_event->dnd.x_root,
1545 current_event->dnd.y_root,
1546 context->suggested_action,
1547 context->actions, time);
1549 if (!site->proxy_window && dest_window)
1550 gdk_window_unref (dest_window);
1552 selection = gdk_drag_get_selection (info->proxy_source->context);
1554 selection != gdk_drag_get_selection (info->context))
1555 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1557 gdk_event_free (current_event);
1562 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1564 if (context->suggested_action & site->actions)
1565 action = context->suggested_action;
1572 if ((site->actions & (1 << i)) &&
1573 (context->actions & (1 << i)))
1581 if (action && gtk_drag_dest_find_target (widget, context, NULL))
1583 if (!site->have_drag)
1585 site->have_drag = TRUE;
1586 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1587 gtk_drag_highlight (widget);
1590 gdk_drag_status (context, action, time);
1594 gdk_drag_status (context, 0, time);
1599 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1600 context, x, y, time, &retval);
1602 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1606 gtk_drag_dest_drop (GtkWidget *widget,
1607 GdkDragContext *context,
1612 GtkDragDestSite *site;
1613 GtkDragDestInfo *info;
1615 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1616 g_return_val_if_fail (site != NULL, FALSE);
1618 info = gtk_drag_get_dest_info (context, FALSE);
1619 g_return_val_if_fail (info != NULL, FALSE);
1626 if (info->proxy_source ||
1627 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1629 gtk_drag_drop (info->proxy_source, time);
1633 /* We need to synthesize a motion event, wait for a status,
1634 * and, if we get a good one, do a drop.
1637 GdkEvent *current_event;
1639 GdkWindow *dest_window;
1640 GdkDragProtocol proto;
1642 gtk_drag_proxy_begin (widget, info, time);
1643 info->proxy_drop_wait = TRUE;
1644 info->proxy_drop_time = time;
1646 current_event = gtk_get_current_event ();
1648 if (site->proxy_window)
1650 dest_window = site->proxy_window;
1651 proto = site->proxy_protocol;
1655 gdk_drag_find_window (info->proxy_source->context,
1657 current_event->dnd.x_root,
1658 current_event->dnd.y_root,
1659 &dest_window, &proto);
1662 gdk_drag_motion (info->proxy_source->context,
1664 current_event->dnd.x_root,
1665 current_event->dnd.y_root,
1666 context->suggested_action,
1667 context->actions, time);
1669 if (!site->proxy_window && dest_window)
1670 gdk_window_unref (dest_window);
1672 selection = gdk_drag_get_selection (info->proxy_source->context);
1674 selection != gdk_drag_get_selection (info->context))
1675 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1677 gdk_event_free (current_event);
1686 if (site->flags & GTK_DEST_DEFAULT_DROP)
1688 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
1690 if (target == GDK_NONE)
1692 gtk_drag_finish (context, FALSE, FALSE, time);
1696 gtk_drag_get_data (widget, context, target, time);
1699 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1700 context, x, y, time, &retval);
1702 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1710 /*************************************************************
1711 * gtk_drag_begin: Start a drag operation
1714 * widget: Widget from which drag starts
1715 * handlers: List of handlers to supply the data for the drag
1716 * button: Button user used to start drag
1717 * time: Time of event starting drag
1720 *************************************************************/
1723 gtk_drag_begin (GtkWidget *widget,
1724 GtkTargetList *target_list,
1725 GdkDragAction actions,
1729 GtkDragSourceInfo *info;
1730 GList *targets = NULL;
1732 guint32 time = GDK_CURRENT_TIME;
1733 GdkDragAction possible_actions, suggested_action;
1734 GdkDragContext *context;
1735 GtkWidget *ipc_widget;
1737 g_return_val_if_fail (widget != NULL, NULL);
1738 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1739 g_return_val_if_fail (target_list != NULL, NULL);
1742 time = gdk_event_get_time (event);
1744 tmp_list = g_list_last (target_list->list);
1747 GtkTargetPair *pair = tmp_list->data;
1748 targets = g_list_prepend (targets,
1749 GINT_TO_POINTER (pair->target));
1750 tmp_list = tmp_list->prev;
1753 ipc_widget = gtk_drag_get_ipc_widget ();
1754 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1756 context = gdk_drag_begin (ipc_widget->window, targets);
1757 g_list_free (targets);
1759 info = gtk_drag_get_source_info (context, TRUE);
1761 info->ipc_widget = ipc_widget;
1762 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1764 info->widget = gtk_widget_ref (widget);
1767 info->button = button;
1768 info->target_list = target_list;
1769 gtk_target_list_ref (target_list);
1771 info->possible_actions = actions;
1773 info->cursor = NULL;
1774 info->status = GTK_DRAG_STATUS_DRAG;
1775 info->last_event = NULL;
1776 info->selections = NULL;
1777 info->icon_window = NULL;
1778 info->destroy_icon = FALSE;
1780 gtk_drag_get_event_actions (event, info->button, actions,
1781 &suggested_action, &possible_actions);
1783 info->cursor = gtk_drag_get_cursor (suggested_action);
1785 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1786 * the drag icon, it will be in the right place
1788 if (event && event->type == GDK_MOTION_NOTIFY)
1790 info->cur_x = event->motion.x_root;
1791 info->cur_y = event->motion.y_root;
1796 gdk_window_get_pointer (GDK_ROOT_PARENT (), &x, &y, NULL);
1802 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1805 if (event && event->type == GDK_MOTION_NOTIFY)
1806 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1808 info->start_x = info->cur_x;
1809 info->start_y = info->cur_y;
1811 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1812 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1813 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1814 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1815 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
1816 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1817 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
1818 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1819 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1820 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1822 /* We use a GTK grab here to override any grabs that the widget
1823 * we are dragging from might have held
1825 gtk_grab_add (info->ipc_widget);
1826 if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1827 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1828 GDK_BUTTON_RELEASE_MASK, NULL,
1829 info->cursor, time) == 0)
1831 if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1833 /* FIXME: This should be cleaned up... */
1837 ev.type = GDK_BUTTON_RELEASE;
1838 ev.button = info->button;
1840 gtk_drag_button_release_cb (widget, &ev, info);
1846 return info->context;
1849 /*************************************************************
1850 * gtk_drag_source_set:
1851 * Register a drop site, and possibly add default behaviors.
1854 * start_button_mask: Mask of allowed buttons to start drag
1855 * targets: Table of targets for this source
1857 * actions: Actions allowed for this source
1859 *************************************************************/
1862 gtk_drag_source_set (GtkWidget *widget,
1863 GdkModifierType start_button_mask,
1864 const GtkTargetEntry *targets,
1866 GdkDragAction actions)
1868 GtkDragSourceSite *site;
1870 g_return_if_fail (widget != NULL);
1872 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1874 gtk_widget_add_events (widget,
1875 gtk_widget_get_events (widget) |
1876 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1877 GDK_BUTTON_MOTION_MASK);
1881 if (site->target_list)
1882 gtk_target_list_unref (site->target_list);
1886 site = g_new0 (GtkDragSourceSite, 1);
1888 site->icon_type = GTK_IMAGE_EMPTY;
1890 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1891 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1893 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1894 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1897 gtk_object_set_data_full (GTK_OBJECT (widget),
1899 site, gtk_drag_source_site_destroy);
1902 site->start_button_mask = start_button_mask;
1905 site->target_list = gtk_target_list_new (targets, n_targets);
1907 site->target_list = NULL;
1909 site->actions = actions;
1913 /*************************************************************
1914 * gtk_drag_source_unset
1915 * Unregister this widget as a drag source.
1919 *************************************************************/
1922 gtk_drag_source_unset (GtkWidget *widget)
1924 GtkDragSourceSite *site;
1926 g_return_if_fail (widget != NULL);
1928 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1932 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1933 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1938 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
1940 switch (site->icon_type)
1942 case GTK_IMAGE_EMPTY:
1944 case GTK_IMAGE_PIXMAP:
1945 if (site->icon_data.pixmap.pixmap)
1946 gdk_pixmap_unref (site->icon_data.pixmap.pixmap);
1947 if (site->icon_mask)
1948 gdk_pixmap_unref (site->icon_mask);
1950 case GTK_IMAGE_PIXBUF:
1951 g_object_unref (G_OBJECT (site->icon_data.pixbuf.pixbuf));
1953 case GTK_IMAGE_STOCK:
1954 g_free (G_OBJECT (site->icon_data.stock.stock_id));
1957 g_assert_not_reached();
1960 site->icon_type = GTK_IMAGE_EMPTY;
1963 gdk_colormap_unref (site->colormap);
1964 site->colormap = NULL;
1968 * gtk_drag_source_set_icon:
1969 * @widget: a #GtkWidget
1970 * @colormap: the colormap of the icon
1971 * @pixmap: the image data for the icon
1972 * @mask: the transparency mask for an image.
1974 * Sets the icon that will be used for drags from a particular widget
1975 * from a pixmap/mask. GTK+ retains a reference count for the
1976 * arguments, and will release them when they are no longer needed.
1977 * Use gtk_drag_source_set_icon_pixbuf() instead.
1980 gtk_drag_source_set_icon (GtkWidget *widget,
1981 GdkColormap *colormap,
1985 GtkDragSourceSite *site;
1987 g_return_if_fail (widget != NULL);
1988 g_return_if_fail (GDK_IS_COLORMAP (colormap));
1989 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
1990 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
1992 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1993 g_return_if_fail (site != NULL);
1995 gdk_colormap_ref (colormap);
1996 gdk_pixmap_ref (pixmap);
1998 gdk_pixmap_ref (mask);
2000 gtk_drag_source_unset_icon (site);
2002 site->icon_type = GTK_IMAGE_PIXMAP;
2004 site->icon_data.pixmap.pixmap = pixmap;
2005 site->icon_mask = mask;
2006 site->colormap = colormap;
2010 * gtk_drag_source_set_icon_pixbuf:
2011 * @widget: a #GtkWidget
2012 * @pixbuf: the #GdkPixbuf for the drag icon
2014 * Sets the icon that will be used for drags from a particular widget
2015 * from a #GdkPixbuf. GTK+ retains a reference count @pixbuf.
2016 * and will release it when it is no longer needed.
2019 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2022 GtkDragSourceSite *site;
2024 g_return_if_fail (widget != NULL);
2025 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2027 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2028 g_return_if_fail (site != NULL);
2030 gdk_pixbuf_ref (pixbuf);
2032 gtk_drag_source_unset_icon (site);
2034 site->icon_type = GTK_IMAGE_PIXBUF;
2035 site->icon_data.pixbuf.pixbuf = pixbuf;
2039 * gtk_drag_source_set_icon_stock:
2040 * @widget: a #GtkWidget
2041 * @stock: the ID of the stock icon to use..
2042 * @size: size at which to render the stock icon
2044 * Sets the icon that will be used for drags from a particular to
2048 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2049 const gchar *stock_id)
2051 GtkDragSourceSite *site;
2053 g_return_if_fail (widget != NULL);
2054 g_return_if_fail (stock_id != NULL);
2056 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2057 g_return_if_fail (site != NULL);
2059 gtk_drag_source_unset_icon (site);
2061 site->icon_data.stock.stock_id = g_strdup (stock_id);
2065 gtk_drag_set_icon_window (GdkDragContext *context,
2069 gboolean destroy_on_release)
2071 GtkDragSourceInfo *info;
2073 g_return_if_fail (context != NULL);
2074 g_return_if_fail (widget != NULL);
2076 info = gtk_drag_get_source_info (context, FALSE);
2077 gtk_drag_remove_icon (info);
2079 info->icon_window = widget;
2080 info->hot_x = hot_x;
2081 info->hot_y = hot_y;
2085 gtk_widget_set_uposition (widget,
2086 info->cur_x - info->hot_x,
2087 info->cur_y - info->hot_y);
2088 gtk_widget_ref (widget);
2089 gdk_window_raise (widget->window);
2090 gtk_widget_show (widget);
2093 info->destroy_icon = destroy_on_release;
2097 * gtk_drag_set_icon_widget:
2098 * @context: the context for a drag. (This must be called
2099 with a context for the source side of a drag)
2100 * @widget: a toplevel window to use as an icon.
2101 * @hot_x: the X offset within @widget of the hotspot.
2102 * @hot_y: the Y offset within @widget of the hotspot.
2104 * Changes the icon for a widget to a given widget. GTK+
2105 * will not destroy the icon, so if you don't want
2106 * it to persist, you should connect to the "drag_end"
2107 * signal and destroy it yourself.
2110 gtk_drag_set_icon_widget (GdkDragContext *context,
2115 g_return_if_fail (context != NULL);
2116 g_return_if_fail (widget != NULL);
2118 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2122 set_icon_stock_pixbuf (GdkDragContext *context,
2123 const gchar *stock_id,
2133 g_return_if_fail (context != NULL);
2134 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2135 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2137 gtk_widget_push_colormap (gdk_rgb_get_colormap ());
2138 window = gtk_window_new (GTK_WINDOW_POPUP);
2139 gtk_widget_pop_colormap ();
2141 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2142 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2146 pixbuf = gtk_widget_render_icon (window, stock_id,
2147 GTK_ICON_SIZE_DND, NULL);
2151 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2152 gtk_widget_destroy (window);
2158 width = gdk_pixbuf_get_width (pixbuf);
2159 height = gdk_pixbuf_get_width (pixbuf);
2161 gtk_widget_set_usize (window,
2162 gdk_pixbuf_get_width (pixbuf),
2163 gdk_pixbuf_get_height (pixbuf));
2164 gtk_widget_realize (window);
2166 gdk_pixbuf_render_pixmap_and_mask (pixbuf, &pixmap, &mask, 128);
2168 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2171 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2173 g_object_unref (G_OBJECT (pixmap));
2174 g_object_unref (G_OBJECT (mask));
2176 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2180 * gtk_drag_set_icon_pixbuf:
2181 * @context: the context for a drag. (This must be called
2182 * with a context for the source side of a drag)
2183 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2184 * @hot_x: the X offset within @widget of the hotspot.
2185 * @hot_y: the Y offset within @widget of the hotspot.
2187 * Sets @pixbuf as the icon for a given drag.
2190 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2195 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2196 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2198 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2202 * gtk_drag_set_icon_pixbuf:
2203 * @context: the context for a drag. (This must be called
2204 * with a context for the source side of a drag)
2205 * @stock: the ID of the stock icon to use for the drag.
2206 * @hot_x: the X offset within the icon of the hotspot.
2207 * @hot_y: the Y offset within the icon of the hotspot.
2209 * Sets the the icon for a given drag from a stock ID.
2212 gtk_drag_set_icon_stock (GdkDragContext *context,
2213 const gchar *stock_id,
2217 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2218 g_return_if_fail (stock_id != NULL);
2220 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2224 * gtk_drag_set_icon_pixmap:
2225 * @context: the context for a drag. (This must be called
2226 * with a context for the source side of a drag)
2227 * @colormap: the colormap of the icon
2228 * @pixmap: the image data for the icon
2229 * @mask: the transparency mask for the icon
2230 * @hot_x: the X offset within @pixmap of the hotspot.
2231 * @hot_y: the Y offset within @pixmap of the hotspot.
2233 * Sets @pixmap as the icon for a given drag. GTK+ retains a
2234 * reference count for the arguments, and will release them when
2235 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2236 * will be more convenient to use.
2239 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2240 GdkColormap *colormap,
2249 g_return_if_fail (context != NULL);
2250 g_return_if_fail (colormap != NULL);
2251 g_return_if_fail (pixmap != NULL);
2253 gdk_window_get_size (pixmap, &width, &height);
2255 gtk_widget_push_colormap (colormap);
2257 window = gtk_window_new (GTK_WINDOW_POPUP);
2258 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2259 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2261 gtk_widget_pop_colormap ();
2263 gtk_widget_set_usize (window, width, height);
2264 gtk_widget_realize (window);
2266 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2269 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2271 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2275 * gtk_drag_set_icon_default:
2276 * @context: the context for a drag. (This must be called
2277 with a context for the source side of a drag)
2279 * Sets the icon for a particular drag to the default
2283 gtk_drag_set_icon_default (GdkDragContext *context)
2285 g_return_if_fail (context != NULL);
2287 if (!default_icon_pixmap)
2288 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2290 gtk_drag_set_icon_pixmap (context,
2291 default_icon_colormap,
2292 default_icon_pixmap,
2295 default_icon_hot_y);
2299 * gtk_drag_set_default_icon:
2300 * @colormap: the colormap of the icon
2301 * @pixmap: the image data for the icon
2302 * @mask: the transparency mask for an image.
2303 * @hot_x: The X offset within @widget of the hotspot.
2304 * @hot_y: The Y offset within @widget of the hotspot.
2306 * Changes the default drag icon. GTK+ retains a reference count for the
2307 * arguments, and will release them when they are no longer needed.
2308 * This function is obsolete. The default icon should now be changed
2309 * via the stock system by changing the stock pixbuf for GTK_STOCK_DND.
2312 gtk_drag_set_default_icon (GdkColormap *colormap,
2318 g_return_if_fail (colormap != NULL);
2319 g_return_if_fail (pixmap != NULL);
2321 if (default_icon_colormap)
2322 gdk_colormap_unref (default_icon_colormap);
2323 if (default_icon_pixmap)
2324 gdk_pixmap_unref (default_icon_pixmap);
2325 if (default_icon_mask)
2326 gdk_pixmap_unref (default_icon_mask);
2328 default_icon_colormap = colormap;
2329 gdk_colormap_ref (colormap);
2331 default_icon_pixmap = pixmap;
2332 gdk_pixmap_ref (pixmap);
2334 default_icon_mask = mask;
2336 gdk_pixmap_ref (mask);
2338 default_icon_hot_x = hot_x;
2339 default_icon_hot_y = hot_y;
2343 /*************************************************************
2344 * _gtk_drag_source_handle_event:
2345 * Called from widget event handling code on Drag events
2349 * toplevel: Toplevel widget that received the event
2352 *************************************************************/
2355 _gtk_drag_source_handle_event (GtkWidget *widget,
2358 GtkDragSourceInfo *info;
2359 GdkDragContext *context;
2361 g_return_if_fail (widget != NULL);
2362 g_return_if_fail (event != NULL);
2364 context = event->dnd.context;
2365 info = gtk_drag_get_source_info (context, FALSE);
2369 switch (event->type)
2371 case GDK_DRAG_STATUS:
2375 if (info->proxy_dest)
2377 if (!event->dnd.send_event)
2379 if (info->proxy_dest->proxy_drop_wait)
2381 gboolean result = context->action != 0;
2383 /* Aha - we can finally pass the MOTIF DROP on... */
2384 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2386 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2388 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2392 gdk_drag_status (info->proxy_dest->context,
2393 event->dnd.context->action,
2400 cursor = gtk_drag_get_cursor (event->dnd.context->action);
2401 if (info->cursor != cursor)
2403 gdk_pointer_grab (widget->window, FALSE,
2404 GDK_POINTER_MOTION_MASK |
2405 GDK_POINTER_MOTION_HINT_MASK |
2406 GDK_BUTTON_RELEASE_MASK,
2408 cursor, event->dnd.time);
2409 info->cursor = cursor;
2412 if (info->last_event)
2414 gtk_drag_update (info,
2415 info->cur_x, info->cur_y,
2417 info->last_event = NULL;
2423 case GDK_DROP_FINISHED:
2424 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2427 g_assert_not_reached ();
2431 /*************************************************************
2432 * gtk_drag_source_check_selection:
2433 * Check if we've set up handlers/claimed the selection
2434 * for a given drag. If not, add them.
2438 *************************************************************/
2441 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2447 tmp_list = info->selections;
2450 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
2452 tmp_list = tmp_list->next;
2455 gtk_selection_owner_set (info->ipc_widget, selection, time);
2456 info->selections = g_list_prepend (info->selections,
2457 GUINT_TO_POINTER (selection));
2459 tmp_list = info->target_list->list;
2462 GtkTargetPair *pair = tmp_list->data;
2464 gtk_selection_add_target (info->ipc_widget,
2468 tmp_list = tmp_list->next;
2471 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2473 gtk_selection_add_target (info->ipc_widget,
2475 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2476 TARGET_MOTIF_SUCCESS);
2477 gtk_selection_add_target (info->ipc_widget,
2479 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2480 TARGET_MOTIF_FAILURE);
2483 gtk_selection_add_target (info->ipc_widget,
2485 gdk_atom_intern ("DELETE", FALSE),
2489 /*************************************************************
2490 * gtk_drag_drop_finished:
2491 * Clean up from the drag, and display snapback, if necessary.
2497 *************************************************************/
2500 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2504 gtk_drag_source_release_selections (info, time);
2506 if (info->proxy_dest)
2508 /* The time from the event isn't reliable for Xdnd drags */
2509 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2510 info->proxy_dest->proxy_drop_time);
2511 gtk_drag_source_info_destroy (info);
2517 gtk_drag_source_info_destroy (info);
2521 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2525 anim->n_steps = MAX (info->cur_x - info->start_x,
2526 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2527 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2528 if (info->icon_window)
2530 gtk_widget_show (info->icon_window);
2531 gdk_window_raise (info->icon_window->window);
2534 /* Mark the context as dead, so if the destination decides
2535 * to respond really late, we still are OK.
2537 gtk_drag_clear_source_info (info->context);
2538 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2544 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2547 GList *tmp_list = info->selections;
2550 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2551 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2552 gtk_selection_owner_set (NULL, selection, time);
2553 tmp_list = tmp_list->next;
2556 g_list_free (info->selections);
2557 info->selections = NULL;
2560 /*************************************************************
2562 * Send a drop event.
2566 *************************************************************/
2569 gtk_drag_drop (GtkDragSourceInfo *info,
2572 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2574 GtkSelectionData selection_data;
2576 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2578 tmp_list = info->target_list->list;
2581 GtkTargetPair *pair = tmp_list->data;
2583 if (pair->target == target)
2585 selection_data.selection = GDK_NONE;
2586 selection_data.target = target;
2587 selection_data.data = NULL;
2588 selection_data.length = -1;
2590 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2591 info->context, &selection_data,
2595 /* FIXME: Should we check for length >= 0 here? */
2596 gtk_drag_drop_finished (info, TRUE, time);
2599 tmp_list = tmp_list->next;
2601 gtk_drag_drop_finished (info, FALSE, time);
2605 if (info->icon_window)
2606 gtk_widget_hide (info->icon_window);
2608 gdk_drag_drop (info->context, time);
2609 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2610 gtk_drag_abort_timeout,
2616 * Source side callbacks.
2620 gtk_drag_source_event_cb (GtkWidget *widget,
2624 GtkDragSourceSite *site;
2625 gboolean retval = FALSE;
2626 site = (GtkDragSourceSite *)data;
2628 switch (event->type)
2630 case GDK_BUTTON_PRESS:
2631 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2633 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2634 site->x = event->button.x;
2635 site->y = event->button.y;
2639 case GDK_BUTTON_RELEASE:
2640 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2641 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2644 case GDK_MOTION_NOTIFY:
2645 if (site->state & event->motion.state & site->start_button_mask)
2647 /* FIXME: This is really broken and can leave us
2653 if (site->state & event->motion.state &
2654 GDK_BUTTON1_MASK << (i - 1))
2658 if (gtk_drag_check_threshold (widget, site->x, site->y,
2659 event->motion.x, event->motion.y))
2661 GtkDragSourceInfo *info;
2662 GdkDragContext *context;
2665 context = gtk_drag_begin (widget, site->target_list,
2669 info = gtk_drag_get_source_info (context, FALSE);
2671 if (!info->icon_window)
2673 switch (site->icon_type)
2675 case GTK_IMAGE_EMPTY:
2676 gtk_drag_set_icon_default (context);
2678 case GTK_IMAGE_PIXMAP:
2679 gtk_drag_set_icon_pixmap (context,
2681 site->icon_data.pixmap.pixmap,
2685 case GTK_IMAGE_PIXBUF:
2686 gtk_drag_set_icon_pixbuf (context,
2687 site->icon_data.pixbuf.pixbuf,
2690 case GTK_IMAGE_STOCK:
2691 gtk_drag_set_icon_stock (context,
2692 site->icon_data.stock.stock_id,
2696 g_assert_not_reached();
2706 default: /* hit for 2/3BUTTON_PRESS */
2714 gtk_drag_source_site_destroy (gpointer data)
2716 GtkDragSourceSite *site = data;
2718 if (site->target_list)
2719 gtk_target_list_unref (site->target_list);
2721 gtk_drag_source_unset_icon (site);
2726 gtk_drag_selection_get (GtkWidget *widget,
2727 GtkSelectionData *selection_data,
2732 GtkDragSourceInfo *info = data;
2733 static GdkAtom null_atom = GDK_NONE;
2737 null_atom = gdk_atom_intern ("NULL", FALSE);
2742 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2745 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2747 case TARGET_MOTIF_SUCCESS:
2748 gtk_drag_drop_finished (info, TRUE, time);
2749 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2751 case TARGET_MOTIF_FAILURE:
2752 gtk_drag_drop_finished (info, FALSE, time);
2753 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2756 if (info->proxy_dest)
2758 /* This is sort of dangerous and needs to be thought
2761 info->proxy_dest->proxy_data = selection_data;
2762 gtk_drag_get_data (info->widget,
2763 info->proxy_dest->context,
2764 selection_data->target,
2767 info->proxy_dest->proxy_data = NULL;
2771 if (gtk_target_list_find (info->target_list,
2772 selection_data->target,
2775 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2787 gtk_drag_anim_timeout (gpointer data)
2789 GtkDragAnim *anim = data;
2793 GDK_THREADS_ENTER ();
2795 if (anim->step == anim->n_steps)
2797 gtk_drag_source_info_destroy (anim->info);
2804 x = (anim->info->start_x * (anim->step + 1) +
2805 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2806 y = (anim->info->start_y * (anim->step + 1) +
2807 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2808 if (anim->info->icon_window)
2809 gtk_widget_set_uposition (anim->info->icon_window,
2810 x - anim->info->hot_x,
2811 y - anim->info->hot_y);
2818 GDK_THREADS_LEAVE ();
2824 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2826 if (info->icon_window)
2828 gtk_widget_hide (info->icon_window);
2829 if (info->destroy_icon)
2830 gtk_widget_destroy (info->icon_window);
2832 gtk_widget_unref (info->icon_window);
2833 info->icon_window = NULL;
2838 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
2840 gtk_drag_remove_icon (info);
2842 if (!info->proxy_dest)
2843 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2847 gtk_widget_unref (info->widget);
2849 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2850 gtk_selection_remove_all (info->ipc_widget);
2851 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2852 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2853 gtk_drag_release_ipc_widget (info->ipc_widget);
2855 gtk_target_list_unref (info->target_list);
2857 gtk_drag_clear_source_info (info->context);
2858 gdk_drag_context_unref (info->context);
2860 if (info->drop_timeout)
2861 gtk_timeout_remove (info->drop_timeout);
2866 /*************************************************************
2868 * Function to update the status of the drag when the
2869 * cursor moves or the modifier changes
2871 * info: DragSourceInfo for the drag
2872 * x_root, y_root: position of darg
2873 * event: The event that triggered this call
2875 *************************************************************/
2878 gtk_drag_update (GtkDragSourceInfo *info,
2883 GdkDragAction action;
2884 GdkDragAction possible_actions;
2885 GdkWindow *window = NULL;
2886 GdkWindow *dest_window;
2887 GdkDragProtocol protocol;
2889 guint32 time = gtk_drag_get_event_time (event);
2891 gtk_drag_get_event_actions (event,
2893 info->possible_actions,
2894 &action, &possible_actions);
2895 info->cur_x = x_root;
2896 info->cur_y = y_root;
2898 if (info->icon_window)
2900 gdk_window_raise (info->icon_window->window);
2901 gtk_widget_set_uposition (info->icon_window,
2902 info->cur_x - info->hot_x,
2903 info->cur_y - info->hot_y);
2904 window = info->icon_window->window;
2907 gdk_drag_find_window (info->context,
2908 window, x_root, y_root,
2909 &dest_window, &protocol);
2911 if (gdk_drag_motion (info->context, dest_window, protocol,
2912 x_root, y_root, action,
2916 if (info->last_event != event) /* Paranoia, should not happen */
2918 if (info->last_event)
2919 gdk_event_free ((GdkEvent *)info->last_event);
2920 info->last_event = gdk_event_copy ((GdkEvent *)event);
2925 if (info->last_event)
2927 gdk_event_free ((GdkEvent *)info->last_event);
2928 info->last_event = NULL;
2933 gdk_window_unref (dest_window);
2935 selection = gdk_drag_get_selection (info->context);
2937 gtk_drag_source_check_selection (info, selection, time);
2940 /*************************************************************
2942 * Called when the user finishes to drag, either by
2943 * releasing the mouse, or by pressing Esc.
2945 * widget: GtkInvisible widget for this drag
2948 *************************************************************/
2951 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
2953 GdkEvent send_event;
2954 GtkWidget *source_widget = info->widget;
2956 gdk_pointer_ungrab (time);
2957 gdk_keyboard_ungrab (time);
2959 gtk_grab_remove (info->ipc_widget);
2961 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
2962 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2964 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
2965 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2968 /* Send on a release pair to the the original
2969 * widget to convince it to release its grab. We need to
2970 * call gtk_propagate_event() here, instead of
2971 * gtk_widget_event() because widget like GtkList may
2972 * expect propagation.
2975 send_event.button.type = GDK_BUTTON_RELEASE;
2976 send_event.button.window = GDK_ROOT_PARENT ();
2977 send_event.button.send_event = TRUE;
2978 send_event.button.time = time;
2979 send_event.button.x = 0;
2980 send_event.button.y = 0;
2981 send_event.button.axes = NULL;
2982 send_event.button.state = 0;
2983 send_event.button.button = info->button;
2984 send_event.button.device = gdk_device_get_core_pointer ();
2985 send_event.button.x_root = 0;
2986 send_event.button.y_root = 0;
2988 gtk_propagate_event (source_widget, &send_event);
2991 /*************************************************************
2992 * gtk_drag_motion_cb:
2993 * "motion_notify_event" callback during drag.
2997 *************************************************************/
3000 gtk_drag_motion_cb (GtkWidget *widget,
3001 GdkEventMotion *event,
3004 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3005 gint x_root, y_root;
3009 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
3010 event->x_root = x_root;
3011 event->y_root = y_root;
3014 gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
3019 /*************************************************************
3021 * "key_press/release_event" callback during drag.
3025 *************************************************************/
3028 gtk_drag_key_cb (GtkWidget *widget,
3032 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3033 GdkModifierType state;
3035 if (event->type == GDK_KEY_PRESS)
3037 if (event->keyval == GDK_Escape)
3039 gtk_drag_end (info, event->time);
3040 gdk_drag_abort (info->context, event->time);
3041 gtk_drag_drop_finished (info, FALSE, event->time);
3047 /* Now send a "motion" so that the modifier state is updated */
3049 /* The state is not yet updated in the event, so we need
3050 * to query it here. We could use XGetModifierMapping, but
3051 * that would be overkill.
3053 gdk_window_get_pointer (GDK_ROOT_PARENT(), NULL, NULL, &state);
3055 event->state = state;
3056 gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
3061 /*************************************************************
3062 * gtk_drag_button_release_cb:
3063 * "button_release_event" callback during drag.
3067 *************************************************************/
3070 gtk_drag_button_release_cb (GtkWidget *widget,
3071 GdkEventButton *event,
3074 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3076 if (event->button != info->button)
3079 gtk_drag_end (info, event->time);
3081 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3083 gtk_drag_drop (info, event->time);
3087 gdk_drag_abort (info->context, event->time);
3088 gtk_drag_drop_finished (info, FALSE, event->time);
3095 gtk_drag_abort_timeout (gpointer data)
3097 GtkDragSourceInfo *info = data;
3098 guint32 time = GDK_CURRENT_TIME;
3100 GDK_THREADS_ENTER ();
3102 if (info->proxy_dest)
3103 time = info->proxy_dest->proxy_drop_time;
3105 info->drop_timeout = 0;
3106 gtk_drag_drop_finished (info, FALSE, time);
3108 GDK_THREADS_LEAVE ();
3114 * gtk_drag_check_threshold:
3115 * @widget: a #GtkWidget
3116 * @start_x: X coordinate of start of drag
3117 * @start_y: Y coordinate of start of drag
3118 * @current_x: current X coordinate
3119 * @current_y: current Y coordinate
3121 * Checks to see if a mouse drag starting at (start_x, start_y) and ending
3122 * at (current_x, current_y) has passed the GTK drag threshhold, and thus
3123 * should trigger the beginning of a drag-and-drop operation.
3125 * Return Value: If the drag threshold has been passed.
3128 gtk_drag_check_threshold (GtkWidget *widget,
3134 #define DRAG_THRESHOLD 8
3136 return (ABS (current_x - start_x) > DRAG_THRESHOLD ||
3137 ABS (current_y - start_y) > DRAG_THRESHOLD);