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 GDK_POINTER_TO_ATOM (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 references for the arguments, and
1976 * 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 for @pixbuf and will
2016 * 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);
2029 gdk_pixbuf_ref (pixbuf);
2031 gtk_drag_source_unset_icon (site);
2033 site->icon_type = GTK_IMAGE_PIXBUF;
2034 site->icon_data.pixbuf.pixbuf = pixbuf;
2038 * gtk_drag_source_set_icon_stock:
2039 * @widget: a #GtkWidget
2040 * @stock_id: the ID of the stock icon to use
2042 * Sets the icon that will be used for drags from a particular source
2046 gtk_drag_source_set_icon_stock (GtkWidget *widget,
2047 const gchar *stock_id)
2049 GtkDragSourceSite *site;
2051 g_return_if_fail (widget != NULL);
2052 g_return_if_fail (stock_id != NULL);
2054 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
2055 g_return_if_fail (site != NULL);
2057 gtk_drag_source_unset_icon (site);
2059 site->icon_data.stock.stock_id = g_strdup (stock_id);
2063 gtk_drag_set_icon_window (GdkDragContext *context,
2067 gboolean destroy_on_release)
2069 GtkDragSourceInfo *info;
2071 g_return_if_fail (context != NULL);
2072 g_return_if_fail (widget != NULL);
2074 info = gtk_drag_get_source_info (context, FALSE);
2075 gtk_drag_remove_icon (info);
2077 info->icon_window = widget;
2078 info->hot_x = hot_x;
2079 info->hot_y = hot_y;
2083 gtk_widget_set_uposition (widget,
2084 info->cur_x - info->hot_x,
2085 info->cur_y - info->hot_y);
2086 gtk_widget_ref (widget);
2087 gdk_window_raise (widget->window);
2088 gtk_widget_show (widget);
2091 info->destroy_icon = destroy_on_release;
2095 * gtk_drag_set_icon_widget:
2096 * @context: the context for a drag. (This must be called
2097 with a context for the source side of a drag)
2098 * @widget: a toplevel window to use as an icon.
2099 * @hot_x: the X offset within @widget of the hotspot.
2100 * @hot_y: the Y offset within @widget of the hotspot.
2102 * Changes the icon for a widget to a given widget. GTK+
2103 * will not destroy the icon, so if you don't want
2104 * it to persist, you should connect to the "drag_end"
2105 * signal and destroy it yourself.
2108 gtk_drag_set_icon_widget (GdkDragContext *context,
2113 g_return_if_fail (context != NULL);
2114 g_return_if_fail (widget != NULL);
2116 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
2120 set_icon_stock_pixbuf (GdkDragContext *context,
2121 const gchar *stock_id,
2131 g_return_if_fail (context != NULL);
2132 g_return_if_fail (pixbuf != NULL || stock_id != NULL);
2133 g_return_if_fail (pixbuf == NULL || stock_id == NULL);
2135 gtk_widget_push_colormap (gdk_rgb_get_colormap ());
2136 window = gtk_window_new (GTK_WINDOW_POPUP);
2137 gtk_widget_pop_colormap ();
2139 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2140 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2144 pixbuf = gtk_widget_render_icon (window, stock_id,
2145 GTK_ICON_SIZE_DND, NULL);
2149 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
2150 gtk_widget_destroy (window);
2156 width = gdk_pixbuf_get_width (pixbuf);
2157 height = gdk_pixbuf_get_width (pixbuf);
2159 gtk_widget_set_usize (window,
2160 gdk_pixbuf_get_width (pixbuf),
2161 gdk_pixbuf_get_height (pixbuf));
2162 gtk_widget_realize (window);
2164 gdk_pixbuf_render_pixmap_and_mask (pixbuf, &pixmap, &mask, 128);
2166 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2169 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2171 g_object_unref (G_OBJECT (pixmap));
2172 g_object_unref (G_OBJECT (mask));
2174 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2178 * gtk_drag_set_icon_pixbuf:
2179 * @context: the context for a drag. (This must be called
2180 * with a context for the source side of a drag)
2181 * @pixbuf: the #GdkPixbuf to use as the drag icon.
2182 * @hot_x: the X offset within @widget of the hotspot.
2183 * @hot_y: the Y offset within @widget of the hotspot.
2185 * Sets @pixbuf as the icon for a given drag.
2188 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
2193 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2194 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
2196 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
2200 * gtk_drag_set_icon_stock:
2201 * @context: the context for a drag. (This must be called
2202 * with a context for the source side of a drag)
2203 * @stock: the ID of the stock icon to use for the drag.
2204 * @hot_x: the X offset within the icon of the hotspot.
2205 * @hot_y: the Y offset within the icon of the hotspot.
2207 * Sets the the icon for a given drag from a stock ID.
2210 gtk_drag_set_icon_stock (GdkDragContext *context,
2211 const gchar *stock_id,
2215 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
2216 g_return_if_fail (stock_id != NULL);
2218 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
2222 * gtk_drag_set_icon_pixmap:
2223 * @context: the context for a drag. (This must be called
2224 * with a context for the source side of a drag)
2225 * @colormap: the colormap of the icon
2226 * @pixmap: the image data for the icon
2227 * @mask: the transparency mask for the icon
2228 * @hot_x: the X offset within @pixmap of the hotspot.
2229 * @hot_y: the Y offset within @pixmap of the hotspot.
2231 * Sets @pixmap as the icon for a given drag. GTK+ retains
2232 * references for the arguments, and will release them when
2233 * they are no longer needed. In general, gtk_drag_set_icon_pixbuf()
2234 * will be more convenient to use.
2237 gtk_drag_set_icon_pixmap (GdkDragContext *context,
2238 GdkColormap *colormap,
2247 g_return_if_fail (context != NULL);
2248 g_return_if_fail (colormap != NULL);
2249 g_return_if_fail (pixmap != NULL);
2251 gdk_window_get_size (pixmap, &width, &height);
2253 gtk_widget_push_colormap (colormap);
2255 window = gtk_window_new (GTK_WINDOW_POPUP);
2256 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2257 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2259 gtk_widget_pop_colormap ();
2261 gtk_widget_set_usize (window, width, height);
2262 gtk_widget_realize (window);
2264 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2267 gtk_widget_shape_combine_mask (window, mask, 0, 0);
2269 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2273 * gtk_drag_set_icon_default:
2274 * @context: the context for a drag. (This must be called
2275 with a context for the source side of a drag)
2277 * Sets the icon for a particular drag to the default
2281 gtk_drag_set_icon_default (GdkDragContext *context)
2283 g_return_if_fail (context != NULL);
2285 if (!default_icon_pixmap)
2286 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
2288 gtk_drag_set_icon_pixmap (context,
2289 default_icon_colormap,
2290 default_icon_pixmap,
2293 default_icon_hot_y);
2297 * gtk_drag_set_default_icon:
2298 * @colormap: the colormap of the icon
2299 * @pixmap: the image data for the icon
2300 * @mask: the transparency mask for an image.
2301 * @hot_x: The X offset within @widget of the hotspot.
2302 * @hot_y: The Y offset within @widget of the hotspot.
2304 * Changes the default drag icon. GTK+ retains references for the
2305 * arguments, and will release them when they are no longer needed.
2306 * This function is obsolete. The default icon should now be changed
2307 * via the stock system by changing the stock pixbuf for %GTK_STOCK_DND.
2310 gtk_drag_set_default_icon (GdkColormap *colormap,
2316 g_return_if_fail (colormap != NULL);
2317 g_return_if_fail (pixmap != NULL);
2319 if (default_icon_colormap)
2320 gdk_colormap_unref (default_icon_colormap);
2321 if (default_icon_pixmap)
2322 gdk_pixmap_unref (default_icon_pixmap);
2323 if (default_icon_mask)
2324 gdk_pixmap_unref (default_icon_mask);
2326 default_icon_colormap = colormap;
2327 gdk_colormap_ref (colormap);
2329 default_icon_pixmap = pixmap;
2330 gdk_pixmap_ref (pixmap);
2332 default_icon_mask = mask;
2334 gdk_pixmap_ref (mask);
2336 default_icon_hot_x = hot_x;
2337 default_icon_hot_y = hot_y;
2341 /*************************************************************
2342 * _gtk_drag_source_handle_event:
2343 * Called from widget event handling code on Drag events
2347 * toplevel: Toplevel widget that received the event
2350 *************************************************************/
2353 _gtk_drag_source_handle_event (GtkWidget *widget,
2356 GtkDragSourceInfo *info;
2357 GdkDragContext *context;
2359 g_return_if_fail (widget != NULL);
2360 g_return_if_fail (event != NULL);
2362 context = event->dnd.context;
2363 info = gtk_drag_get_source_info (context, FALSE);
2367 switch (event->type)
2369 case GDK_DRAG_STATUS:
2373 if (info->proxy_dest)
2375 if (!event->dnd.send_event)
2377 if (info->proxy_dest->proxy_drop_wait)
2379 gboolean result = context->action != 0;
2381 /* Aha - we can finally pass the MOTIF DROP on... */
2382 gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2384 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2386 gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2390 gdk_drag_status (info->proxy_dest->context,
2391 event->dnd.context->action,
2398 cursor = gtk_drag_get_cursor (event->dnd.context->action);
2399 if (info->cursor != cursor)
2401 gdk_pointer_grab (widget->window, FALSE,
2402 GDK_POINTER_MOTION_MASK |
2403 GDK_POINTER_MOTION_HINT_MASK |
2404 GDK_BUTTON_RELEASE_MASK,
2406 cursor, event->dnd.time);
2407 info->cursor = cursor;
2410 if (info->last_event)
2412 gtk_drag_update (info,
2413 info->cur_x, info->cur_y,
2415 info->last_event = NULL;
2421 case GDK_DROP_FINISHED:
2422 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2425 g_assert_not_reached ();
2429 /*************************************************************
2430 * gtk_drag_source_check_selection:
2431 * Check if we've set up handlers/claimed the selection
2432 * for a given drag. If not, add them.
2436 *************************************************************/
2439 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2445 tmp_list = info->selections;
2448 if (GDK_POINTER_TO_ATOM (tmp_list->data) == selection)
2450 tmp_list = tmp_list->next;
2453 gtk_selection_owner_set (info->ipc_widget, selection, time);
2454 info->selections = g_list_prepend (info->selections,
2455 GUINT_TO_POINTER (selection));
2457 tmp_list = info->target_list->list;
2460 GtkTargetPair *pair = tmp_list->data;
2462 gtk_selection_add_target (info->ipc_widget,
2466 tmp_list = tmp_list->next;
2469 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2471 gtk_selection_add_target (info->ipc_widget,
2473 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2474 TARGET_MOTIF_SUCCESS);
2475 gtk_selection_add_target (info->ipc_widget,
2477 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2478 TARGET_MOTIF_FAILURE);
2481 gtk_selection_add_target (info->ipc_widget,
2483 gdk_atom_intern ("DELETE", FALSE),
2487 /*************************************************************
2488 * gtk_drag_drop_finished:
2489 * Clean up from the drag, and display snapback, if necessary.
2495 *************************************************************/
2498 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2502 gtk_drag_source_release_selections (info, time);
2504 if (info->proxy_dest)
2506 /* The time from the event isn't reliable for Xdnd drags */
2507 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2508 info->proxy_dest->proxy_drop_time);
2509 gtk_drag_source_info_destroy (info);
2515 gtk_drag_source_info_destroy (info);
2519 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2523 anim->n_steps = MAX (info->cur_x - info->start_x,
2524 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2525 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2526 if (info->icon_window)
2528 gtk_widget_show (info->icon_window);
2529 gdk_window_raise (info->icon_window->window);
2532 /* Mark the context as dead, so if the destination decides
2533 * to respond really late, we still are OK.
2535 gtk_drag_clear_source_info (info->context);
2536 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2542 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2545 GList *tmp_list = info->selections;
2548 GdkAtom selection = GDK_POINTER_TO_ATOM (tmp_list->data);
2549 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2550 gtk_selection_owner_set (NULL, selection, time);
2551 tmp_list = tmp_list->next;
2554 g_list_free (info->selections);
2555 info->selections = NULL;
2558 /*************************************************************
2560 * Send a drop event.
2564 *************************************************************/
2567 gtk_drag_drop (GtkDragSourceInfo *info,
2570 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2572 GtkSelectionData selection_data;
2574 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2576 tmp_list = info->target_list->list;
2579 GtkTargetPair *pair = tmp_list->data;
2581 if (pair->target == target)
2583 selection_data.selection = GDK_NONE;
2584 selection_data.target = target;
2585 selection_data.data = NULL;
2586 selection_data.length = -1;
2588 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2589 info->context, &selection_data,
2593 /* FIXME: Should we check for length >= 0 here? */
2594 gtk_drag_drop_finished (info, TRUE, time);
2597 tmp_list = tmp_list->next;
2599 gtk_drag_drop_finished (info, FALSE, time);
2603 if (info->icon_window)
2604 gtk_widget_hide (info->icon_window);
2606 gdk_drag_drop (info->context, time);
2607 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2608 gtk_drag_abort_timeout,
2614 * Source side callbacks.
2618 gtk_drag_source_event_cb (GtkWidget *widget,
2622 GtkDragSourceSite *site;
2623 gboolean retval = FALSE;
2624 site = (GtkDragSourceSite *)data;
2626 switch (event->type)
2628 case GDK_BUTTON_PRESS:
2629 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2631 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2632 site->x = event->button.x;
2633 site->y = event->button.y;
2637 case GDK_BUTTON_RELEASE:
2638 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2639 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2642 case GDK_MOTION_NOTIFY:
2643 if (site->state & event->motion.state & site->start_button_mask)
2645 /* FIXME: This is really broken and can leave us
2651 if (site->state & event->motion.state &
2652 GDK_BUTTON1_MASK << (i - 1))
2656 if (gtk_drag_check_threshold (widget, site->x, site->y,
2657 event->motion.x, event->motion.y))
2659 GtkDragSourceInfo *info;
2660 GdkDragContext *context;
2663 context = gtk_drag_begin (widget, site->target_list,
2667 info = gtk_drag_get_source_info (context, FALSE);
2669 if (!info->icon_window)
2671 switch (site->icon_type)
2673 case GTK_IMAGE_EMPTY:
2674 gtk_drag_set_icon_default (context);
2676 case GTK_IMAGE_PIXMAP:
2677 gtk_drag_set_icon_pixmap (context,
2679 site->icon_data.pixmap.pixmap,
2683 case GTK_IMAGE_PIXBUF:
2684 gtk_drag_set_icon_pixbuf (context,
2685 site->icon_data.pixbuf.pixbuf,
2688 case GTK_IMAGE_STOCK:
2689 gtk_drag_set_icon_stock (context,
2690 site->icon_data.stock.stock_id,
2694 g_assert_not_reached();
2704 default: /* hit for 2/3BUTTON_PRESS */
2712 gtk_drag_source_site_destroy (gpointer data)
2714 GtkDragSourceSite *site = data;
2716 if (site->target_list)
2717 gtk_target_list_unref (site->target_list);
2719 gtk_drag_source_unset_icon (site);
2724 gtk_drag_selection_get (GtkWidget *widget,
2725 GtkSelectionData *selection_data,
2730 GtkDragSourceInfo *info = data;
2731 static GdkAtom null_atom = GDK_NONE;
2735 null_atom = gdk_atom_intern ("NULL", FALSE);
2740 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2743 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2745 case TARGET_MOTIF_SUCCESS:
2746 gtk_drag_drop_finished (info, TRUE, time);
2747 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2749 case TARGET_MOTIF_FAILURE:
2750 gtk_drag_drop_finished (info, FALSE, time);
2751 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2754 if (info->proxy_dest)
2756 /* This is sort of dangerous and needs to be thought
2759 info->proxy_dest->proxy_data = selection_data;
2760 gtk_drag_get_data (info->widget,
2761 info->proxy_dest->context,
2762 selection_data->target,
2765 info->proxy_dest->proxy_data = NULL;
2769 if (gtk_target_list_find (info->target_list,
2770 selection_data->target,
2773 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2785 gtk_drag_anim_timeout (gpointer data)
2787 GtkDragAnim *anim = data;
2791 GDK_THREADS_ENTER ();
2793 if (anim->step == anim->n_steps)
2795 gtk_drag_source_info_destroy (anim->info);
2802 x = (anim->info->start_x * (anim->step + 1) +
2803 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2804 y = (anim->info->start_y * (anim->step + 1) +
2805 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2806 if (anim->info->icon_window)
2807 gtk_widget_set_uposition (anim->info->icon_window,
2808 x - anim->info->hot_x,
2809 y - anim->info->hot_y);
2816 GDK_THREADS_LEAVE ();
2822 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2824 if (info->icon_window)
2826 gtk_widget_hide (info->icon_window);
2827 if (info->destroy_icon)
2828 gtk_widget_destroy (info->icon_window);
2830 gtk_widget_unref (info->icon_window);
2831 info->icon_window = NULL;
2836 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
2838 gtk_drag_remove_icon (info);
2840 if (!info->proxy_dest)
2841 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2845 gtk_widget_unref (info->widget);
2847 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2848 gtk_selection_remove_all (info->ipc_widget);
2849 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2850 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2851 gtk_drag_release_ipc_widget (info->ipc_widget);
2853 gtk_target_list_unref (info->target_list);
2855 gtk_drag_clear_source_info (info->context);
2856 gdk_drag_context_unref (info->context);
2858 if (info->drop_timeout)
2859 gtk_timeout_remove (info->drop_timeout);
2864 /*************************************************************
2866 * Function to update the status of the drag when the
2867 * cursor moves or the modifier changes
2869 * info: DragSourceInfo for the drag
2870 * x_root, y_root: position of darg
2871 * event: The event that triggered this call
2873 *************************************************************/
2876 gtk_drag_update (GtkDragSourceInfo *info,
2881 GdkDragAction action;
2882 GdkDragAction possible_actions;
2883 GdkWindow *window = NULL;
2884 GdkWindow *dest_window;
2885 GdkDragProtocol protocol;
2887 guint32 time = gtk_drag_get_event_time (event);
2889 gtk_drag_get_event_actions (event,
2891 info->possible_actions,
2892 &action, &possible_actions);
2893 info->cur_x = x_root;
2894 info->cur_y = y_root;
2896 if (info->icon_window)
2898 gdk_window_raise (info->icon_window->window);
2899 gtk_widget_set_uposition (info->icon_window,
2900 info->cur_x - info->hot_x,
2901 info->cur_y - info->hot_y);
2902 window = info->icon_window->window;
2905 gdk_drag_find_window (info->context,
2906 window, x_root, y_root,
2907 &dest_window, &protocol);
2909 if (gdk_drag_motion (info->context, dest_window, protocol,
2910 x_root, y_root, action,
2914 if (info->last_event != event) /* Paranoia, should not happen */
2916 if (info->last_event)
2917 gdk_event_free ((GdkEvent *)info->last_event);
2918 info->last_event = gdk_event_copy ((GdkEvent *)event);
2923 if (info->last_event)
2925 gdk_event_free ((GdkEvent *)info->last_event);
2926 info->last_event = NULL;
2931 gdk_window_unref (dest_window);
2933 selection = gdk_drag_get_selection (info->context);
2935 gtk_drag_source_check_selection (info, selection, time);
2938 /*************************************************************
2940 * Called when the user finishes to drag, either by
2941 * releasing the mouse, or by pressing Esc.
2943 * widget: GtkInvisible widget for this drag
2946 *************************************************************/
2949 gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
2951 GdkEvent send_event;
2952 GtkWidget *source_widget = info->widget;
2954 gdk_pointer_ungrab (time);
2955 gdk_keyboard_ungrab (time);
2957 gtk_grab_remove (info->ipc_widget);
2959 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
2960 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2962 gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
2963 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2966 /* Send on a release pair to the the original
2967 * widget to convince it to release its grab. We need to
2968 * call gtk_propagate_event() here, instead of
2969 * gtk_widget_event() because widget like GtkList may
2970 * expect propagation.
2973 send_event.button.type = GDK_BUTTON_RELEASE;
2974 send_event.button.window = GDK_ROOT_PARENT ();
2975 send_event.button.send_event = TRUE;
2976 send_event.button.time = time;
2977 send_event.button.x = 0;
2978 send_event.button.y = 0;
2979 send_event.button.axes = NULL;
2980 send_event.button.state = 0;
2981 send_event.button.button = info->button;
2982 send_event.button.device = gdk_device_get_core_pointer ();
2983 send_event.button.x_root = 0;
2984 send_event.button.y_root = 0;
2986 gtk_propagate_event (source_widget, &send_event);
2989 /*************************************************************
2990 * gtk_drag_motion_cb:
2991 * "motion_notify_event" callback during drag.
2995 *************************************************************/
2998 gtk_drag_motion_cb (GtkWidget *widget,
2999 GdkEventMotion *event,
3002 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3003 gint x_root, y_root;
3007 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
3008 event->x_root = x_root;
3009 event->y_root = y_root;
3012 gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
3017 /*************************************************************
3019 * "key_press/release_event" callback during drag.
3023 *************************************************************/
3026 gtk_drag_key_cb (GtkWidget *widget,
3030 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3031 GdkModifierType state;
3033 if (event->type == GDK_KEY_PRESS)
3035 if (event->keyval == GDK_Escape)
3037 gtk_drag_end (info, event->time);
3038 gdk_drag_abort (info->context, event->time);
3039 gtk_drag_drop_finished (info, FALSE, event->time);
3045 /* Now send a "motion" so that the modifier state is updated */
3047 /* The state is not yet updated in the event, so we need
3048 * to query it here. We could use XGetModifierMapping, but
3049 * that would be overkill.
3051 gdk_window_get_pointer (GDK_ROOT_PARENT(), NULL, NULL, &state);
3053 event->state = state;
3054 gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
3059 /*************************************************************
3060 * gtk_drag_button_release_cb:
3061 * "button_release_event" callback during drag.
3065 *************************************************************/
3068 gtk_drag_button_release_cb (GtkWidget *widget,
3069 GdkEventButton *event,
3072 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
3074 if (event->button != info->button)
3077 gtk_drag_end (info, event->time);
3079 if ((info->context->action != 0) && (info->context->dest_window != NULL))
3081 gtk_drag_drop (info, event->time);
3085 gdk_drag_abort (info->context, event->time);
3086 gtk_drag_drop_finished (info, FALSE, event->time);
3093 gtk_drag_abort_timeout (gpointer data)
3095 GtkDragSourceInfo *info = data;
3096 guint32 time = GDK_CURRENT_TIME;
3098 GDK_THREADS_ENTER ();
3100 if (info->proxy_dest)
3101 time = info->proxy_dest->proxy_drop_time;
3103 info->drop_timeout = 0;
3104 gtk_drag_drop_finished (info, FALSE, time);
3106 GDK_THREADS_LEAVE ();
3112 * gtk_drag_check_threshold:
3113 * @widget: a #GtkWidget
3114 * @start_x: X coordinate of start of drag
3115 * @start_y: Y coordinate of start of drag
3116 * @current_x: current X coordinate
3117 * @current_y: current Y coordinate
3119 * Checks to see if a mouse drag starting at (start_x, start_y) and ending
3120 * at (current_x, current_y) has passed the GTK drag threshhold, and thus
3121 * should trigger the beginning of a drag-and-drop operation.
3123 * Return Value: If the drag threshold has been passed.
3126 gtk_drag_check_threshold (GtkWidget *widget,
3132 #define DRAG_THRESHOLD 8
3134 return (ABS (current_x - start_x) > DRAG_THRESHOLD ||
3135 ABS (current_y - start_y) > DRAG_THRESHOLD);