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),
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
1066 * Looks for a match between @context->targets and the
1067 * @dest_target_list, returning the first matching target, otherwise
1068 * returning %GDK_NONE. @dest_target_list should usually be the return
1069 * value from gtk_drag_dest_get_target_list(), but some widgets may
1070 * have different valid targets for different parts of the widget; in
1071 * that case, they will have to implement a drag_motion handler that
1072 * passes the correct target list to this function.
1074 * Return value: first target that the source offers and the dest can accept, or %GDK_NONE
1077 gtk_drag_dest_find_target (GtkWidget *widget,
1078 GdkDragContext *context,
1079 GtkTargetList *target_list)
1082 GList *tmp_source = NULL;
1083 GtkWidget *source_widget = gtk_drag_get_source_widget (context);
1085 if (target_list == NULL)
1088 tmp_target = target_list->list;
1091 GtkTargetPair *pair = tmp_target->data;
1092 tmp_source = context->targets;
1095 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1097 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1098 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1099 return pair->target;
1103 tmp_source = tmp_source->next;
1105 tmp_target = tmp_target->next;
1112 gtk_drag_selection_received (GtkWidget *widget,
1113 GtkSelectionData *selection_data,
1117 GdkDragContext *context;
1118 GtkDragDestInfo *info;
1119 GtkWidget *drop_widget;
1123 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1124 info = gtk_drag_get_dest_info (context, FALSE);
1126 if (info->proxy_data &&
1127 info->proxy_data->target == selection_data->target)
1129 gtk_selection_data_set (info->proxy_data,
1130 selection_data->type,
1131 selection_data->format,
1132 selection_data->data,
1133 selection_data->length);
1138 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1140 gtk_drag_finish (context, TRUE, FALSE, time);
1142 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1143 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1149 GtkDragDestSite *site;
1151 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1153 if (site && site->target_list)
1157 if (gtk_target_list_find (site->target_list,
1158 selection_data->target,
1161 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1162 selection_data->length >= 0)
1163 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1164 "drag_data_received",
1165 context, info->drop_x, info->drop_y,
1172 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1173 "drag_data_received",
1174 context, info->drop_x, info->drop_y,
1175 selection_data, 0, time);
1178 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1181 gtk_drag_finish (context,
1182 (selection_data->length >= 0),
1183 (context->action == GDK_ACTION_MOVE),
1187 gtk_widget_unref (drop_widget);
1190 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1191 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1194 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1195 gdk_drag_context_unref (context);
1197 gtk_drag_release_ipc_widget (widget);
1200 /*************************************************************
1201 * gtk_drag_find_widget:
1202 * Recursive callback used to locate widgets for
1203 * DRAG_MOTION and DROP_START events.
1207 *************************************************************/
1210 gtk_drag_find_widget (GtkWidget *widget,
1211 GtkDragFindData *data)
1213 GtkAllocation new_allocation;
1217 new_allocation = widget->allocation;
1219 if (data->found || !GTK_WIDGET_MAPPED (widget))
1222 /* Note that in the following code, we only count the
1223 * position as being inside a WINDOW widget if it is inside
1224 * widget->window; points that are outside of widget->window
1225 * but within the allocation are not counted. This is consistent
1226 * with the way we highlight drag targets.
1228 if (!GTK_WIDGET_NO_WINDOW (widget))
1230 new_allocation.x = 0;
1231 new_allocation.y = 0;
1236 GdkWindow *window = widget->window;
1237 while (window != widget->parent->window)
1239 gint tx, ty, twidth, theight;
1240 gdk_window_get_size (window, &twidth, &theight);
1242 if (new_allocation.x < 0)
1244 new_allocation.width += new_allocation.x;
1245 new_allocation.x = 0;
1247 if (new_allocation.y < 0)
1249 new_allocation.height += new_allocation.y;
1250 new_allocation.y = 0;
1252 if (new_allocation.x + new_allocation.width > twidth)
1253 new_allocation.width = twidth - new_allocation.x;
1254 if (new_allocation.y + new_allocation.height > theight)
1255 new_allocation.height = theight - new_allocation.y;
1257 gdk_window_get_position (window, &tx, &ty);
1258 new_allocation.x += tx;
1260 new_allocation.y += ty;
1263 window = gdk_window_get_parent (window);
1267 if (data->toplevel ||
1268 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1269 (data->x < new_allocation.x + new_allocation.width) &&
1270 (data->y < new_allocation.y + new_allocation.height)))
1272 /* First, check if the drag is in a valid drop site in
1273 * one of our children
1275 if (GTK_IS_CONTAINER (widget))
1277 GtkDragFindData new_data = *data;
1279 new_data.x -= x_offset;
1280 new_data.y -= y_offset;
1281 new_data.found = FALSE;
1282 new_data.toplevel = FALSE;
1284 gtk_container_forall (GTK_CONTAINER (widget),
1285 (GtkCallback)gtk_drag_find_widget,
1288 data->found = new_data.found;
1291 /* If not, and this widget is registered as a drop site, check to
1292 * emit "drag_motion" to check if we are actually in
1296 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1298 data->found = data->callback (widget,
1300 data->x - new_allocation.x,
1301 data->y - new_allocation.y,
1303 /* If so, send a "drag_leave" to the last widget */
1306 if (data->info->widget && data->info->widget != widget)
1308 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1310 data->info->widget = widget;
1317 gtk_drag_proxy_begin (GtkWidget *widget,
1318 GtkDragDestInfo *dest_info,
1321 GtkDragSourceInfo *source_info;
1323 GdkDragContext *context;
1324 GtkWidget *ipc_widget;
1326 if (dest_info->proxy_source)
1328 gdk_drag_abort (dest_info->proxy_source->context, time);
1329 gtk_drag_source_info_destroy (dest_info->proxy_source);
1330 dest_info->proxy_source = NULL;
1333 ipc_widget = gtk_drag_get_ipc_widget ();
1334 context = gdk_drag_begin (ipc_widget->window,
1335 dest_info->context->targets);
1337 source_info = gtk_drag_get_source_info (context, TRUE);
1339 source_info->ipc_widget = ipc_widget;
1340 source_info->widget = gtk_widget_ref (widget);
1342 source_info->target_list = gtk_target_list_new (NULL, 0);
1343 tmp_list = dest_info->context->targets;
1346 gtk_target_list_add (source_info->target_list,
1347 GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1348 tmp_list = tmp_list->next;
1351 source_info->proxy_dest = dest_info;
1353 gtk_signal_connect (GTK_OBJECT (ipc_widget),
1355 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1358 dest_info->proxy_source = source_info;
1362 gtk_drag_dest_info_destroy (gpointer data)
1364 GtkDragDestInfo *info = data;
1369 static GtkDragDestInfo *
1370 gtk_drag_get_dest_info (GdkDragContext *context,
1373 GtkDragDestInfo *info;
1374 static GQuark info_quark = 0;
1376 info_quark = g_quark_from_static_string ("gtk-dest-info");
1378 info = g_object_get_qdata (G_OBJECT (context), info_quark);
1379 if (!info && create)
1381 info = g_new (GtkDragDestInfo, 1);
1382 info->widget = NULL;
1383 info->context = context;
1384 info->proxy_source = NULL;
1385 info->proxy_data = NULL;
1386 info->dropped = FALSE;
1387 info->proxy_drop_wait = FALSE;
1388 g_object_set_qdata_full (G_OBJECT (context), info_quark,
1389 info, gtk_drag_dest_info_destroy);
1395 static GQuark dest_info_quark = 0;
1397 static GtkDragSourceInfo *
1398 gtk_drag_get_source_info (GdkDragContext *context,
1401 GtkDragSourceInfo *info;
1402 if (!dest_info_quark)
1403 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
1405 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
1406 if (!info && create)
1408 info = g_new0 (GtkDragSourceInfo, 1);
1409 info->context = context;
1410 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
1417 gtk_drag_clear_source_info (GdkDragContext *context)
1419 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
1423 gtk_drag_dest_realized (GtkWidget *widget)
1425 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1427 if (GTK_WIDGET_TOPLEVEL (toplevel))
1428 gdk_window_register_dnd (toplevel->window);
1432 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
1433 GtkWidget *previous_toplevel)
1435 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1437 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
1438 gdk_window_register_dnd (toplevel->window);
1442 gtk_drag_dest_site_destroy (gpointer data)
1444 GtkDragDestSite *site = data;
1446 if (site->target_list)
1447 gtk_target_list_unref (site->target_list);
1453 * Default drag handlers
1456 gtk_drag_dest_leave (GtkWidget *widget,
1457 GdkDragContext *context,
1460 GtkDragDestSite *site;
1462 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1463 g_return_if_fail (site != NULL);
1467 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1469 if (info->proxy_source && info->proxy_source->widget == widget && !info->dropped)
1471 gdk_drag_abort (info->proxy_source->context, time);
1472 gtk_drag_source_info_destroy (info->proxy_source);
1473 info->proxy_source = NULL;
1480 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1481 gtk_drag_unhighlight (widget);
1483 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1484 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1487 site->have_drag = FALSE;
1492 gtk_drag_dest_motion (GtkWidget *widget,
1493 GdkDragContext *context,
1498 GtkDragDestSite *site;
1499 GdkDragAction action = 0;
1502 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1503 g_return_val_if_fail (site != NULL, FALSE);
1508 GdkEvent *current_event;
1509 GdkWindow *dest_window;
1510 GdkDragProtocol proto;
1512 GtkDragDestInfo *info = gtk_drag_get_dest_info (context, FALSE);
1514 if (!info->proxy_source || info->proxy_source->widget != widget)
1515 gtk_drag_proxy_begin (widget, info, time);
1517 current_event = gtk_get_current_event ();
1519 if (site->proxy_window)
1521 dest_window = site->proxy_window;
1522 proto = site->proxy_protocol;
1526 gdk_drag_find_window (info->proxy_source->context,
1528 current_event->dnd.x_root,
1529 current_event->dnd.y_root,
1530 &dest_window, &proto);
1533 gdk_drag_motion (info->proxy_source->context,
1535 current_event->dnd.x_root,
1536 current_event->dnd.y_root,
1537 context->suggested_action,
1538 context->actions, time);
1540 if (!site->proxy_window && dest_window)
1541 gdk_window_unref (dest_window);
1543 selection = gdk_drag_get_selection (info->proxy_source->context);
1545 selection != gdk_drag_get_selection (info->context))
1546 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1548 gdk_event_free (current_event);
1553 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1555 if (context->suggested_action & site->actions)
1556 action = context->suggested_action;
1563 if ((site->actions & (1 << i)) &&
1564 (context->actions & (1 << i)))
1572 if (action && gtk_drag_dest_find_target (widget, context, site->target_list))
1574 if (!site->have_drag)
1576 site->have_drag = TRUE;
1577 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1578 gtk_drag_highlight (widget);
1581 gdk_drag_status (context, action, time);
1585 gdk_drag_status (context, 0, time);
1590 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1591 context, x, y, time, &retval);
1593 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1597 gtk_drag_dest_drop (GtkWidget *widget,
1598 GdkDragContext *context,
1603 GtkDragDestSite *site;
1604 GtkDragDestInfo *info;
1606 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1607 g_return_val_if_fail (site != NULL, FALSE);
1609 info = gtk_drag_get_dest_info (context, FALSE);
1610 g_return_val_if_fail (info != NULL, FALSE);
1617 if (info->proxy_source ||
1618 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1620 gtk_drag_drop (info->proxy_source, time);
1624 /* We need to synthesize a motion event, wait for a status,
1625 * and, if we get a good one, do a drop.
1628 GdkEvent *current_event;
1630 GdkWindow *dest_window;
1631 GdkDragProtocol proto;
1633 gtk_drag_proxy_begin (widget, info, time);
1634 info->proxy_drop_wait = TRUE;
1635 info->proxy_drop_time = time;
1637 current_event = gtk_get_current_event ();
1639 if (site->proxy_window)
1641 dest_window = site->proxy_window;
1642 proto = site->proxy_protocol;
1646 gdk_drag_find_window (info->proxy_source->context,
1648 current_event->dnd.x_root,
1649 current_event->dnd.y_root,
1650 &dest_window, &proto);
1653 gdk_drag_motion (info->proxy_source->context,
1655 current_event->dnd.x_root,
1656 current_event->dnd.y_root,
1657 context->suggested_action,
1658 context->actions, time);
1660 if (!site->proxy_window && dest_window)
1661 gdk_window_unref (dest_window);
1663 selection = gdk_drag_get_selection (info->proxy_source->context);
1665 selection != gdk_drag_get_selection (info->context))
1666 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1668 gdk_event_free (current_event);
1677 if (site->flags & GTK_DEST_DEFAULT_DROP)
1679 GdkAtom target = gtk_drag_dest_find_target (widget, context, site->target_list);
1681 if (target == GDK_NONE)
1684 gtk_drag_get_data (widget, context, target, time);
1687 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1688 context, x, y, time, &retval);
1690 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1698 /*************************************************************
1699 * gtk_drag_begin: Start a drag operation
1702 * widget: Widget from which drag starts
1703 * handlers: List of handlers to supply the data for the drag
1704 * button: Button user used to start drag
1705 * time: Time of event starting drag
1708 *************************************************************/
1711 gtk_drag_begin (GtkWidget *widget,
1712 GtkTargetList *target_list,
1713 GdkDragAction actions,
1717 GtkDragSourceInfo *info;
1718 GList *targets = NULL;
1720 guint32 time = GDK_CURRENT_TIME;
1721 GdkDragAction possible_actions, suggested_action;
1722 GdkDragContext *context;
1723 GtkWidget *ipc_widget;
1725 g_return_val_if_fail (widget != NULL, NULL);
1726 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1727 g_return_val_if_fail (target_list != NULL, NULL);
1730 time = gdk_event_get_time (event);
1732 tmp_list = g_list_last (target_list->list);
1735 GtkTargetPair *pair = tmp_list->data;
1736 targets = g_list_prepend (targets,
1737 GINT_TO_POINTER (pair->target));
1738 tmp_list = tmp_list->prev;
1741 ipc_widget = gtk_drag_get_ipc_widget ();
1742 source_widgets = g_slist_prepend (source_widgets, ipc_widget);
1744 context = gdk_drag_begin (ipc_widget->window, targets);
1745 g_list_free (targets);
1747 info = gtk_drag_get_source_info (context, TRUE);
1749 info->ipc_widget = ipc_widget;
1750 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1752 info->widget = gtk_widget_ref (widget);
1755 info->button = button;
1756 info->target_list = target_list;
1757 gtk_target_list_ref (target_list);
1759 info->possible_actions = actions;
1761 info->cursor = NULL;
1762 info->status = GTK_DRAG_STATUS_DRAG;
1763 info->last_event = NULL;
1764 info->selections = NULL;
1765 info->icon_window = NULL;
1766 info->destroy_icon = FALSE;
1768 gtk_drag_get_event_actions (event, info->button, actions,
1769 &suggested_action, &possible_actions);
1771 info->cursor = gtk_drag_get_cursor (suggested_action);
1773 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1774 * the drag icon, it will be in the right place
1776 if (event && event->type == GDK_MOTION_NOTIFY)
1778 info->cur_x = event->motion.x_root;
1779 info->cur_y = event->motion.y_root;
1784 gdk_window_get_pointer (GDK_ROOT_PARENT (), &x, &y, NULL);
1790 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1793 if (event && event->type == GDK_MOTION_NOTIFY)
1794 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1796 info->start_x = info->cur_x;
1797 info->start_y = info->cur_y;
1799 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1800 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1801 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1802 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1803 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
1804 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1805 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
1806 GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1807 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1808 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1810 /* We use a GTK grab here to override any grabs that the widget
1811 * we are dragging from might have held
1813 gtk_grab_add (info->ipc_widget);
1814 if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1815 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1816 GDK_BUTTON_RELEASE_MASK, NULL,
1817 info->cursor, time) == 0)
1819 if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1821 /* FIXME: This should be cleaned up... */
1825 ev.type = GDK_BUTTON_RELEASE;
1826 ev.button = info->button;
1828 gtk_drag_button_release_cb (widget, &ev, info);
1834 return info->context;
1837 /*************************************************************
1838 * gtk_drag_source_set:
1839 * Register a drop site, and possibly add default behaviors.
1842 * start_button_mask: Mask of allowed buttons to start drag
1843 * targets: Table of targets for this source
1845 * actions: Actions allowed for this source
1847 *************************************************************/
1850 gtk_drag_source_set (GtkWidget *widget,
1851 GdkModifierType start_button_mask,
1852 const GtkTargetEntry *targets,
1854 GdkDragAction actions)
1856 GtkDragSourceSite *site;
1858 g_return_if_fail (widget != NULL);
1860 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1862 gtk_widget_add_events (widget,
1863 gtk_widget_get_events (widget) |
1864 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1865 GDK_BUTTON_MOTION_MASK);
1869 if (site->target_list)
1870 gtk_target_list_unref (site->target_list);
1874 site = g_new0 (GtkDragSourceSite, 1);
1876 site->icon_type = GTK_IMAGE_EMPTY;
1878 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1879 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1881 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1882 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1885 gtk_object_set_data_full (GTK_OBJECT (widget),
1887 site, gtk_drag_source_site_destroy);
1890 site->start_button_mask = start_button_mask;
1893 site->target_list = gtk_target_list_new (targets, n_targets);
1895 site->target_list = NULL;
1897 site->actions = actions;
1901 /*************************************************************
1902 * gtk_drag_source_unset
1903 * Unregister this widget as a drag source.
1907 *************************************************************/
1910 gtk_drag_source_unset (GtkWidget *widget)
1912 GtkDragSourceSite *site;
1914 g_return_if_fail (widget != NULL);
1916 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1920 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1921 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1926 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
1928 switch (site->icon_type)
1930 case GTK_IMAGE_EMPTY:
1932 case GTK_IMAGE_PIXMAP:
1933 if (site->icon_data.pixmap.pixmap)
1934 gdk_pixmap_unref (site->icon_data.pixmap.pixmap);
1935 if (site->icon_mask)
1936 gdk_pixmap_unref (site->icon_mask);
1938 case GTK_IMAGE_PIXBUF:
1939 g_object_unref (G_OBJECT (site->icon_data.pixbuf.pixbuf));
1941 case GTK_IMAGE_STOCK:
1942 g_free (G_OBJECT (site->icon_data.stock.stock_id));
1945 g_assert_not_reached();
1948 site->icon_type = GTK_IMAGE_EMPTY;
1951 gdk_colormap_unref (site->colormap);
1952 site->colormap = NULL;
1956 * gtk_drag_source_set_icon:
1957 * @widget: a #GtkWidget
1958 * @colormap: the colormap of the icon
1959 * @pixmap: the image data for the icon
1960 * @mask: the transparency mask for an image.
1962 * Sets the icon that will be used for drags from a particular widget
1963 * from a pixmap/mask. GTK+ retains a reference count for the
1964 * arguments, and will release them when they are no longer needed.
1965 * Use gtk_drag_source_set_icon_pixbuf() instead.
1968 gtk_drag_source_set_icon (GtkWidget *widget,
1969 GdkColormap *colormap,
1973 GtkDragSourceSite *site;
1975 g_return_if_fail (widget != NULL);
1976 g_return_if_fail (GDK_IS_COLORMAP (colormap));
1977 g_return_if_fail (GDK_IS_PIXMAP (pixmap));
1978 g_return_if_fail (!mask || GDK_IS_PIXMAP (mask));
1980 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1981 g_return_if_fail (site != NULL);
1983 gdk_colormap_ref (colormap);
1984 gdk_pixmap_ref (pixmap);
1986 gdk_pixmap_ref (mask);
1988 gtk_drag_source_unset_icon (site);
1990 site->icon_type = GTK_IMAGE_PIXMAP;
1992 site->icon_data.pixmap.pixmap = pixmap;
1993 site->icon_mask = mask;
1994 site->colormap = colormap;
1998 * gtk_drag_source_set_icon_pixbuf:
1999 * @widget: a #GtkWidget
2000 * @pixbuf: the #GdkPixbuf for the drag icon
2002 * Sets the icon that will be used for drags from a particular widget
2003 * from a #GdkPixbuf. GTK+ retains a reference count @pixbuf.
2004 * and will release it when it is no longer needed.
2007 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
2010 GtkDragSourceSite *site;
2012 g_return_if_fail (widget != NULL);
2013 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2015 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2016 g_return_if_fail (site != NULL);
2018 gdk_pixbuf_ref (pixbuf);
2020 gtk_drag_source_unset_icon (site);
2022 site->icon_type = GTK_IMAGE_PIXBUF;
2023 site->icon_data.pixbuf.pixbuf = pixbuf;
2027 * gtk_drag_source_set_icon_stock:
2028 * @widget: a #GtkWidget
2029 * @stock: the ID of the stock icon to use..
2030 * @size: size at which to render the stock icon
2032 * Sets the icon that will be used for drags from a particular to
2036 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2037 const gchar *stock_id)
2039 GtkDragSourceSite *site;
2041 g_return_if_fail (widget != NULL);
2042 g_return_if_fail (stock_id != NULL);
2044 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2045 g_return_if_fail (site != NULL);
2047 gtk_drag_source_unset_icon (site);
2049 site->icon_data.stock.stock_id = g_strdup (stock_id);
2053 gtk_drag_set_icon_window (GdkDragContext *context,
2057 gboolean destroy_on_release)
2059 GtkDragSourceInfo *info;
2061 g_return_if_fail (context != NULL);
2062 g_return_if_fail (widget != NULL);
2064 info = gtk_drag_get_source_info (context, FALSE);
2065 gtk_drag_remove_icon (info);
2067 info->icon_window = widget;
2068 info->hot_x = hot_x;
2069 info->hot_y = hot_y;
2073 gtk_widget_set_uposition (widget,
2074 info->cur_x - info->hot_x,
2075 info->cur_y - info->hot_y);
2076 gtk_widget_ref (widget);
2077 gdk_window_raise (widget->window);
2078 gtk_widget_show (widget);
2081 info->destroy_icon = destroy_on_release;
2085 * gtk_drag_set_icon_widget:
2086 * @context: the context for a drag. (This must be called
2087 with a context for the source side of a drag)
2088 * @widget: a toplevel window to use as an icon.
2089 * @hot_x: the X offset within @widget of the hotspot.
2090 * @hot_y: the Y offset within @widget of the hotspot.
2092 * Changes the icon for a widget to a given widget. GTK+
2093 * will not destroy the icon, so if you don't want
2094 * it to persist, you should connect to the "drag_end"
2095 * signal and destroy it yourself.
2098 gtk_drag_set_icon_widget (GdkDragContext *context,
2103 g_return_if_fail (context != NULL);
2104 g_return_if_fail (widget != NULL);
2106 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2110 set_icon_stock_pixbuf (GdkDragContext *context,
2111 const gchar *stock_id,
2121 g_return_if_fail (context != NULL);
2122 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2123 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2125 gtk_widget_push_colormap (gdk_rgb_get_colormap ());
2126 window = gtk_window_new (GTK_WINDOW_POPUP);
2127 gtk_widget_pop_colormap ();
2129 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2130 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2134 pixbuf = gtk_widget_render_icon (window, stock_id,
2135 GTK_ICON_SIZE_DND, NULL);
2139 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2140 gtk_widget_destroy (window);
2146 width = gdk_pixbuf_get_width (pixbuf);
2147 height = gdk_pixbuf_get_width (pixbuf);
2149 gtk_widget_set_usize (window,
2150 gdk_pixbuf_get_width (pixbuf),
2151 gdk_pixbuf_get_height (pixbuf));
2152 gtk_widget_realize (window);
2154 gdk_pixbuf_render_pixmap_and_mask (pixbuf, &pixmap, &mask, 128);
2156 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2159 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2161 g_object_unref (G_OBJECT (pixmap));
2162 g_object_unref (G_OBJECT (mask));
2164 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2168 * gtk_drag_set_icon_pixbuf:
2169 * @context: the context for a drag. (This must be called
2170 * with a context for the source side of a drag)
2171 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2172 * @hot_x: the X offset within @widget of the hotspot.
2173 * @hot_y: the Y offset within @widget of the hotspot.
2175 * Sets @pixbuf as the icon for a given drag.
2178 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2183 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2184 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2186 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2190 * gtk_drag_set_icon_pixbuf:
2191 * @context: the context for a drag. (This must be called
2192 * with a context for the source side of a drag)
2193 * @stock: the ID of the stock icon to use for the drag.
2194 * @hot_x: the X offset within the icon of the hotspot.
2195 * @hot_y: the Y offset within the icon of the hotspot.
2197 * Sets the the icon for a given drag from a stock ID.
2200 gtk_drag_set_icon_stock (GdkDragContext *context,
2201 const gchar *stock_id,
2205 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2206 g_return_if_fail (stock_id != NULL);
2208 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2212 * gtk_drag_set_icon_pixmap:
2213 * @context: the context for a drag. (This must be called
2214 * with a context for the source side of a drag)
2215 * @colormap: the colormap of the icon
2216 * @pixmap: the image data for the icon
2217 * @mask: the transparency mask for the icon
2218 * @hot_x: the X offset within @pixmap of the hotspot.
2219 * @hot_y: the Y offset within @pixmap of the hotspot.
2221 * Sets @pixmap as the icon for a given drag. GTK+ retains a
2222 * reference count for the arguments, and will release them when
2223 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2224 * will be more convenient to use.
2227 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2228 GdkColormap *colormap,
2237 g_return_if_fail (context != NULL);
2238 g_return_if_fail (colormap != NULL);
2239 g_return_if_fail (pixmap != NULL);
2241 gdk_window_get_size (pixmap, &width, &height);
2243 gtk_widget_push_colormap (colormap);
2245 window = gtk_window_new (GTK_WINDOW_POPUP);
2246 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2247 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2249 gtk_widget_pop_colormap ();
2251 gtk_widget_set_usize (window, width, height);
2252 gtk_widget_realize (window);
2254 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2257 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2259 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2263 * gtk_drag_set_icon_default:
2264 * @context: the context for a drag. (This must be called
2265 with a context for the source side of a drag)
2267 * Sets the icon for a particular drag to the default
2271 gtk_drag_set_icon_default (GdkDragContext *context)
2273 g_return_if_fail (context != NULL);
2275 if (!default_icon_pixmap)
2276 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2278 gtk_drag_set_icon_pixmap (context,
2279 default_icon_colormap,
2280 default_icon_pixmap,
2283 default_icon_hot_y);
2287 * gtk_drag_set_default_icon:
2288 * @colormap: the colormap of the icon
2289 * @pixmap: the image data for the icon
2290 * @mask: the transparency mask for an image.
2291 * @hot_x: The X offset within @widget of the hotspot.
2292 * @hot_y: The Y offset within @widget of the hotspot.
2294 * Changes the default drag icon. GTK+ retains a reference count for the
2295 * arguments, and will release them when they are no longer needed.
2296 * This function is obsolete. The default icon should now be changed
2297 * via the stock system by changing the stock pixbuf for GTK_STOCK_DND.
2300 gtk_drag_set_default_icon (GdkColormap *colormap,
2306 g_return_if_fail (colormap != NULL);
2307 g_return_if_fail (pixmap != NULL);
2309 if (default_icon_colormap)
2310 gdk_colormap_unref (default_icon_colormap);
2311 if (default_icon_pixmap)
2312 gdk_pixmap_unref (default_icon_pixmap);
2313 if (default_icon_mask)
2314 gdk_pixmap_unref (default_icon_mask);
2316 default_icon_colormap = colormap;
2317 gdk_colormap_ref (colormap);
2319 default_icon_pixmap = pixmap;
2320 gdk_pixmap_ref (pixmap);
2322 default_icon_mask = mask;
2324 gdk_pixmap_ref (mask);
2326 default_icon_hot_x = hot_x;
2327 default_icon_hot_y = hot_y;
2331 /*************************************************************
2332 * _gtk_drag_source_handle_event:
2333 * Called from widget event handling code on Drag events
2337 * toplevel: Toplevel widget that received the event
2340 *************************************************************/
2343 _gtk_drag_source_handle_event (GtkWidget *widget,
2346 GtkDragSourceInfo *info;
2347 GdkDragContext *context;
2349 g_return_if_fail (widget != NULL);
2350 g_return_if_fail (event != NULL);
2352 context = event->dnd.context;
2353 info = gtk_drag_get_source_info (context, FALSE);
2357 switch (event->type)
2359 case GDK_DRAG_STATUS:
2363 if (info->proxy_dest)
2365 if (!event->dnd.send_event)
2367 if (info->proxy_dest->proxy_drop_wait)
2369 gboolean result = context->action != 0;
2371 /* Aha - we can finally pass the MOTIF DROP on... */
2372 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2374 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2376 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2380 gdk_drag_status (info->proxy_dest->context,
2381 event->dnd.context->action,
2388 cursor = gtk_drag_get_cursor (event->dnd.context->action);
2389 if (info->cursor != cursor)
2391 gdk_pointer_grab (widget->window, FALSE,
2392 GDK_POINTER_MOTION_MASK |
2393 GDK_POINTER_MOTION_HINT_MASK |
2394 GDK_BUTTON_RELEASE_MASK,
2396 cursor, event->dnd.time);
2397 info->cursor = cursor;
2400 if (info->last_event)
2402 gtk_drag_update (info,
2403 info->cur_x, info->cur_y,
2405 info->last_event = NULL;
2411 case GDK_DROP_FINISHED:
2412 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2415 g_assert_not_reached ();
2419 /*************************************************************
2420 * gtk_drag_source_check_selection:
2421 * Check if we've set up handlers/claimed the selection
2422 * for a given drag. If not, add them.
2426 *************************************************************/
2429 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2435 tmp_list = info->selections;
2438 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
2440 tmp_list = tmp_list->next;
2443 gtk_selection_owner_set (info->ipc_widget, selection, time);
2444 info->selections = g_list_prepend (info->selections,
2445 GUINT_TO_POINTER (selection));
2447 tmp_list = info->target_list->list;
2450 GtkTargetPair *pair = tmp_list->data;
2452 gtk_selection_add_target (info->ipc_widget,
2456 tmp_list = tmp_list->next;
2459 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2461 gtk_selection_add_target (info->ipc_widget,
2463 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2464 TARGET_MOTIF_SUCCESS);
2465 gtk_selection_add_target (info->ipc_widget,
2467 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2468 TARGET_MOTIF_FAILURE);
2471 gtk_selection_add_target (info->ipc_widget,
2473 gdk_atom_intern ("DELETE", FALSE),
2477 /*************************************************************
2478 * gtk_drag_drop_finished:
2479 * Clean up from the drag, and display snapback, if necessary.
2485 *************************************************************/
2488 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2492 gtk_drag_source_release_selections (info, time);
2494 if (info->proxy_dest)
2496 /* The time from the event isn't reliable for Xdnd drags */
2497 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2498 info->proxy_dest->proxy_drop_time);
2499 gtk_drag_source_info_destroy (info);
2505 gtk_drag_source_info_destroy (info);
2509 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2513 anim->n_steps = MAX (info->cur_x - info->start_x,
2514 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2515 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2516 if (info->icon_window)
2518 gtk_widget_show (info->icon_window);
2519 gdk_window_raise (info->icon_window->window);
2522 /* Mark the context as dead, so if the destination decides
2523 * to respond really late, we still are OK.
2525 gtk_drag_clear_source_info (info->context);
2526 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2532 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2535 GList *tmp_list = info->selections;
2538 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2539 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2540 gtk_selection_owner_set (NULL, selection, time);
2541 tmp_list = tmp_list->next;
2544 g_list_free (info->selections);
2545 info->selections = NULL;
2548 /*************************************************************
2550 * Send a drop event.
2554 *************************************************************/
2557 gtk_drag_drop (GtkDragSourceInfo *info,
2560 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2562 GtkSelectionData selection_data;
2564 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2566 tmp_list = info->target_list->list;
2569 GtkTargetPair *pair = tmp_list->data;
2571 if (pair->target == target)
2573 selection_data.selection = GDK_NONE;
2574 selection_data.target = target;
2575 selection_data.data = NULL;
2576 selection_data.length = -1;
2578 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2579 info->context, &selection_data,
2583 /* FIXME: Should we check for length >= 0 here? */
2584 gtk_drag_drop_finished (info, TRUE, time);
2587 tmp_list = tmp_list->next;
2589 gtk_drag_drop_finished (info, FALSE, time);
2593 if (info->icon_window)
2594 gtk_widget_hide (info->icon_window);
2596 gdk_drag_drop (info->context, time);
2597 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2598 gtk_drag_abort_timeout,
2604 * Source side callbacks.
2608 gtk_drag_source_event_cb (GtkWidget *widget,
2612 GtkDragSourceSite *site;
2613 gboolean retval = FALSE;
2614 site = (GtkDragSourceSite *)data;
2616 switch (event->type)
2618 case GDK_BUTTON_PRESS:
2619 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2621 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2622 site->x = event->button.x;
2623 site->y = event->button.y;
2627 case GDK_BUTTON_RELEASE:
2628 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2629 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2632 case GDK_MOTION_NOTIFY:
2633 if (site->state & event->motion.state & site->start_button_mask)
2635 /* FIXME: This is really broken and can leave us
2641 if (site->state & event->motion.state &
2642 GDK_BUTTON1_MASK << (i - 1))
2646 if (gtk_drag_check_threshold (widget, site->x, site->y,
2647 event->motion.x, event->motion.y))
2649 GtkDragSourceInfo *info;
2650 GdkDragContext *context;
2653 context = gtk_drag_begin (widget, site->target_list,
2657 info = gtk_drag_get_source_info (context, FALSE);
2659 if (!info->icon_window)
2661 switch (site->icon_type)
2663 case GTK_IMAGE_EMPTY:
2664 gtk_drag_set_icon_default (context);
2666 case GTK_IMAGE_PIXMAP:
2667 gtk_drag_set_icon_pixmap (context,
2669 site->icon_data.pixmap.pixmap,
2673 case GTK_IMAGE_PIXBUF:
2674 gtk_drag_set_icon_pixbuf (context,
2675 site->icon_data.pixbuf.pixbuf,
2678 case GTK_IMAGE_STOCK:
2679 gtk_drag_set_icon_stock (context,
2680 site->icon_data.stock.stock_id,
2684 g_assert_not_reached();
2694 default: /* hit for 2/3BUTTON_PRESS */
2702 gtk_drag_source_site_destroy (gpointer data)
2704 GtkDragSourceSite *site = data;
2706 if (site->target_list)
2707 gtk_target_list_unref (site->target_list);
2709 gtk_drag_source_unset_icon (site);
2714 gtk_drag_selection_get (GtkWidget *widget,
2715 GtkSelectionData *selection_data,
2720 GtkDragSourceInfo *info = data;
2721 static GdkAtom null_atom = GDK_NONE;
2725 null_atom = gdk_atom_intern ("NULL", FALSE);
2730 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2733 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2735 case TARGET_MOTIF_SUCCESS:
2736 gtk_drag_drop_finished (info, TRUE, time);
2737 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2739 case TARGET_MOTIF_FAILURE:
2740 gtk_drag_drop_finished (info, FALSE, time);
2741 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2744 if (info->proxy_dest)
2746 /* This is sort of dangerous and needs to be thought
2749 info->proxy_dest->proxy_data = selection_data;
2750 gtk_drag_get_data (info->widget,
2751 info->proxy_dest->context,
2752 selection_data->target,
2755 info->proxy_dest->proxy_data = NULL;
2759 if (gtk_target_list_find (info->target_list,
2760 selection_data->target,
2763 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2775 gtk_drag_anim_timeout (gpointer data)
2777 GtkDragAnim *anim = data;
2781 GDK_THREADS_ENTER ();
2783 if (anim->step == anim->n_steps)
2785 gtk_drag_source_info_destroy (anim->info);
2792 x = (anim->info->start_x * (anim->step + 1) +
2793 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2794 y = (anim->info->start_y * (anim->step + 1) +
2795 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2796 if (anim->info->icon_window)
2797 gtk_widget_set_uposition (anim->info->icon_window,
2798 x - anim->info->hot_x,
2799 y - anim->info->hot_y);
2806 GDK_THREADS_LEAVE ();
2812 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2814 if (info->icon_window)
2816 gtk_widget_hide (info->icon_window);
2817 if (info->destroy_icon)
2818 gtk_widget_destroy (info->icon_window);
2820 gtk_widget_unref (info->icon_window);
2821 info->icon_window = NULL;
2826 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
2828 gtk_drag_remove_icon (info);
2830 if (!info->proxy_dest)
2831 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2835 gtk_widget_unref (info->widget);
2837 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2838 gtk_selection_remove_all (info->ipc_widget);
2839 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2840 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2841 gtk_drag_release_ipc_widget (info->ipc_widget);
2843 gtk_target_list_unref (info->target_list);
2845 gtk_drag_clear_source_info (info->context);
2846 gdk_drag_context_unref (info->context);
2848 if (info->drop_timeout)
2849 gtk_timeout_remove (info->drop_timeout);
2854 /*************************************************************
2856 * Function to update the status of the drag when the
2857 * cursor moves or the modifier changes
2859 * info: DragSourceInfo for the drag
2860 * x_root, y_root: position of darg
2861 * event: The event that triggered this call
2863 *************************************************************/
2866 gtk_drag_update (GtkDragSourceInfo *info,
2871 GdkDragAction action;
2872 GdkDragAction possible_actions;
2873 GdkWindow *window = NULL;
2874 GdkWindow *dest_window;
2875 GdkDragProtocol protocol;
2877 guint32 time = gtk_drag_get_event_time (event);
2879 gtk_drag_get_event_actions (event,
2881 info->possible_actions,
2882 &action, &possible_actions);
2883 info->cur_x = x_root;
2884 info->cur_y = y_root;
2886 if (info->icon_window)
2888 gdk_window_raise (info->icon_window->window);
2889 gtk_widget_set_uposition (info->icon_window,
2890 info->cur_x - info->hot_x,
2891 info->cur_y - info->hot_y);
2892 window = info->icon_window->window;
2895 gdk_drag_find_window (info->context,
2896 window, x_root, y_root,
2897 &dest_window, &protocol);
2899 if (gdk_drag_motion (info->context, dest_window, protocol,
2900 x_root, y_root, action,
2904 if (info->last_event != event) /* Paranoia, should not happen */
2906 if (info->last_event)
2907 gdk_event_free ((GdkEvent *)info->last_event);
2908 info->last_event = gdk_event_copy ((GdkEvent *)event);
2913 if (info->last_event)
2915 gdk_event_free ((GdkEvent *)info->last_event);
2916 info->last_event = NULL;
2921 gdk_window_unref (dest_window);
2923 selection = gdk_drag_get_selection (info->context);
2925 gtk_drag_source_check_selection (info, selection, time);
2928 /*************************************************************
2930 * Called when the user finishes to drag, either by
2931 * releasing the mouse, or by pressing Esc.
2933 * widget: GtkInvisible widget for this drag
2936 *************************************************************/
2939 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
2941 GdkEvent send_event;
2942 GtkWidget *source_widget = info->widget;
2944 gdk_pointer_ungrab (time);
2945 gdk_keyboard_ungrab (time);
2947 gtk_grab_remove (info->ipc_widget);
2949 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
2950 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2952 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
2953 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2956 /* Send on a release pair to the the original
2957 * widget to convince it to release its grab. We need to
2958 * call gtk_propagate_event() here, instead of
2959 * gtk_widget_event() because widget like GtkList may
2960 * expect propagation.
2963 send_event.button.type = GDK_BUTTON_RELEASE;
2964 send_event.button.window = GDK_ROOT_PARENT ();
2965 send_event.button.send_event = TRUE;
2966 send_event.button.time = time;
2967 send_event.button.x = 0;
2968 send_event.button.y = 0;
2969 send_event.button.axes = NULL;
2970 send_event.button.state = 0;
2971 send_event.button.button = info->button;
2972 send_event.button.device = gdk_device_get_core_pointer ();
2973 send_event.button.x_root = 0;
2974 send_event.button.y_root = 0;
2976 gtk_propagate_event (source_widget, &send_event);
2979 /*************************************************************
2980 * gtk_drag_motion_cb:
2981 * "motion_notify_event" callback during drag.
2985 *************************************************************/
2988 gtk_drag_motion_cb (GtkWidget *widget,
2989 GdkEventMotion *event,
2992 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2993 gint x_root, y_root;
2997 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2998 event->x_root = x_root;
2999 event->y_root = y_root;
3002 gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
3007 /*************************************************************
3009 * "key_press/release_event" callback during drag.
3013 *************************************************************/
3016 gtk_drag_key_cb (GtkWidget *widget,
3020 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3021 GdkModifierType state;
3023 if (event->type == GDK_KEY_PRESS)
3025 if (event->keyval == GDK_Escape)
3027 gtk_drag_end (info, event->time);
3028 gdk_drag_abort (info->context, event->time);
3029 gtk_drag_drop_finished (info, FALSE, event->time);
3035 /* Now send a "motion" so that the modifier state is updated */
3037 /* The state is not yet updated in the event, so we need
3038 * to query it here. We could use XGetModifierMapping, but
3039 * that would be overkill.
3041 gdk_window_get_pointer (GDK_ROOT_PARENT(), NULL, NULL, &state);
3043 event->state = state;
3044 gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
3049 /*************************************************************
3050 * gtk_drag_button_release_cb:
3051 * "button_release_event" callback during drag.
3055 *************************************************************/
3058 gtk_drag_button_release_cb (GtkWidget *widget,
3059 GdkEventButton *event,
3062 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3064 if (event->button != info->button)
3067 gtk_drag_end (info, event->time);
3069 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3071 gtk_drag_drop (info, event->time);
3075 gdk_drag_abort (info->context, event->time);
3076 gtk_drag_drop_finished (info, FALSE, event->time);
3083 gtk_drag_abort_timeout (gpointer data)
3085 GtkDragSourceInfo *info = data;
3086 guint32 time = GDK_CURRENT_TIME;
3088 GDK_THREADS_ENTER ();
3090 if (info->proxy_dest)
3091 time = info->proxy_dest->proxy_drop_time;
3093 info->drop_timeout = 0;
3094 gtk_drag_drop_finished (info, FALSE, time);
3096 GDK_THREADS_LEAVE ();
3102 * gtk_drag_check_threshold:
3103 * @widget: a #GtkWidget
3104 * @start_x: X coordinate of start of drag
3105 * @start_y: Y coordinate of start of drag
3106 * @current_x: current X coordinate
3107 * @current_y: current Y coordinate
3109 * Checks to see if a mouse drag starting at (start_x, start_y) and ending
3110 * at (current_x, current_y) has passed the GTK drag threshhold, and thus
3111 * should trigger the beginning of a drag-and-drop operation.
3113 * Return Value: If the drag threshold has been passed.
3116 gtk_drag_check_threshold (GtkWidget *widget,
3122 #define DRAG_THRESHOLD 8
3124 return (ABS (current_x - start_x) > DRAG_THRESHOLD ||
3125 ABS (current_y - start_y) > DRAG_THRESHOLD);