1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-1999. 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/.
30 #include "gtkinvisible.h"
32 #include "gtksignal.h"
33 #include "gtkwindow.h"
35 static GSList *drag_widgets = NULL;
36 static GSList *source_widgets = NULL;
38 typedef struct _GtkDragSourceSite GtkDragSourceSite;
39 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
40 typedef struct _GtkDragDestSite GtkDragDestSite;
41 typedef struct _GtkDragDestInfo GtkDragDestInfo;
42 typedef struct _GtkDragAnim GtkDragAnim;
43 typedef struct _GtkDragFindData GtkDragFindData;
53 struct _GtkDragSourceSite
55 GdkModifierType start_button_mask;
56 GtkTargetList *target_list; /* Targets for drag data */
57 GdkDragAction actions; /* Possible actions */
58 GdkColormap *colormap; /* Colormap for drag icon */
59 GdkPixmap *pixmap; /* Icon for drag data */
62 /* Stored button press information to detect drag beginning */
67 struct _GtkDragSourceInfo
70 GtkTargetList *target_list; /* Targets for drag data */
71 GdkDragAction possible_actions; /* Actions allowed by source */
72 GdkDragContext *context; /* drag context */
73 GtkWidget *icon_window; /* Window for drag */
74 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
75 GdkCursor *cursor; /* Cursor for drag */
76 gint hot_x, hot_y; /* Hot spot for drag */
77 gint button; /* mouse button starting drag */
79 GtkDragStatus status; /* drag status */
80 GdkEvent *last_event; /* motion event waiting for response */
82 gint start_x, start_y; /* Initial position */
83 gint cur_x, cur_y; /* Current Position */
85 GList *selections; /* selections we've claimed */
87 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
89 guint drop_timeout; /* Timeout for aborting drop */
90 guint destroy_icon : 1; /* If true, destroy icon_window
94 struct _GtkDragDestSite
96 GtkDestDefaults flags;
97 GtkTargetList *target_list;
98 GdkDragAction actions;
99 GdkWindow *proxy_window;
100 GdkDragProtocol proxy_protocol;
101 gboolean do_proxy : 1;
102 gboolean proxy_coords : 1;
103 gboolean have_drag : 1;
106 struct _GtkDragDestInfo
108 GtkWidget *widget; /* Widget in which drag is in */
109 GdkDragContext *context; /* Drag context */
110 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
111 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
112 gboolean dropped : 1; /* Set after we receive a drop */
113 guint32 proxy_drop_time; /* Timestamp for proxied drop */
114 gboolean proxy_drop_wait : 1; /* Set if we are waiting for a
115 * status reply before sending
118 gint drop_x, drop_y; /* Position of drop */
121 #define DROP_ABORT_TIME 300000
123 #define ANIM_STEP_TIME 50
124 #define ANIM_STEP_LENGTH 50
125 #define ANIM_MIN_STEPS 5
126 #define ANIM_MAX_STEPS 10
130 GtkDragSourceInfo *info;
135 struct _GtkDragFindData
139 GdkDragContext *context;
140 GtkDragDestInfo *info;
143 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
144 gint x, gint y, guint32 time);
148 /* Enumeration for some targets we handle internally */
151 TARGET_MOTIF_SUCCESS = 0x40000000,
152 TARGET_MOTIF_FAILURE,
158 static GdkPixmap *default_icon_pixmap = NULL;
159 static GdkPixmap *default_icon_mask = NULL;
160 static GdkColormap *default_icon_colormap = NULL;
161 static gint default_icon_hot_x;
162 static gint default_icon_hot_y;
164 /* Forward declarations */
165 static void gtk_drag_get_event_actions (GdkEvent *event,
167 GdkDragAction actions,
168 GdkDragAction *suggested_action,
169 GdkDragAction *possible_actions);
170 static GdkCursor * gtk_drag_get_cursor (GdkDragAction action);
171 static GtkWidget *gtk_drag_get_ipc_widget (void);
172 static void gtk_drag_release_ipc_widget (GtkWidget *widget);
174 static void gtk_drag_highlight_paint (GtkWidget *widget);
175 static gboolean gtk_drag_highlight_expose (GtkWidget *widget,
176 GdkEventExpose *event,
180 static GdkAtom gtk_drag_dest_find_target (GtkWidget *widget,
181 GtkDragDestSite *site,
182 GdkDragContext *context);
183 static void gtk_drag_selection_received (GtkWidget *widget,
184 GtkSelectionData *selection_data,
187 static void gtk_drag_find_widget (GtkWidget *widget,
188 GtkDragFindData *data);
189 static void gtk_drag_proxy_begin (GtkWidget *widget,
190 GtkDragDestInfo *dest_info);
191 static void gtk_drag_dest_info_destroy (gpointer data);
192 static void gtk_drag_dest_realized (GtkWidget *widget);
193 static void gtk_drag_dest_site_destroy (gpointer data);
194 static void gtk_drag_dest_leave (GtkWidget *widget,
195 GdkDragContext *context,
197 static gboolean gtk_drag_dest_motion (GtkWidget *widget,
198 GdkDragContext *context,
202 static gboolean gtk_drag_dest_drop (GtkWidget *widget,
203 GdkDragContext *context,
208 static void gtk_drag_source_check_selection (GtkDragSourceInfo *info,
211 static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
213 static void gtk_drag_drop (GtkDragSourceInfo *info,
215 static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
219 static gint gtk_drag_source_event_cb (GtkWidget *widget,
222 static void gtk_drag_source_site_destroy (gpointer data);
223 static void gtk_drag_selection_get (GtkWidget *widget,
224 GtkSelectionData *selection_data,
228 static gint gtk_drag_anim_timeout (gpointer data);
229 static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
230 static void gtk_drag_source_info_destroy (gpointer data);
231 static gint gtk_drag_motion_cb (GtkWidget *widget,
232 GdkEventMotion *event,
234 static gint gtk_drag_button_release_cb (GtkWidget *widget,
235 GdkEventButton *event,
237 static gint gtk_drag_abort_timeout (gpointer data);
239 /************************
240 * Cursor and Icon data *
241 ************************/
243 #define action_ask_width 16
244 #define action_ask_height 16
245 static const char action_ask_bits[] = {
246 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
247 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
248 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
250 #define action_ask_mask_width 16
251 #define action_ask_mask_height 16
252 static const char action_ask_mask_bits[] = {
253 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
254 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
255 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
257 #define action_copy_width 16
258 #define action_copy_height 16
259 static const char action_copy_bits[] = {
260 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
261 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
262 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
264 #define action_copy_mask_width 16
265 #define action_copy_mask_height 16
266 static const char action_copy_mask_bits[] = {
267 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
268 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
269 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
271 #define action_move_width 16
272 #define action_move_height 16
273 static const char action_move_bits[] = {
274 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
275 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
276 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
278 #define action_move_mask_width 16
279 #define action_move_mask_height 16
280 static const char action_move_mask_bits[] = {
281 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
282 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
283 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
285 #define action_link_width 16
286 #define action_link_height 16
287 static const char action_link_bits[] = {
288 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
289 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
290 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
292 #define action_link_mask_width 16
293 #define action_link_mask_height 16
294 static const char action_link_mask_bits[] = {
295 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
296 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
297 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
299 #define action_none_width 16
300 #define action_none_height 16
301 static const char action_none_bits[] = {
302 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
303 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
304 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
306 #define action_none_mask_width 16
307 #define action_none_mask_height 16
308 static const char action_none_mask_bits[] = {
309 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
310 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
311 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
313 #define CURSOR_WIDTH 16
314 #define CURSOR_HEIGHT 16
317 GdkDragAction action;
322 { GDK_ACTION_DEFAULT, 0 },
323 { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL },
324 { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL },
325 { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL },
326 { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL },
327 { 0 , action_none_bits, action_none_mask_bits, NULL },
330 static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
333 static const char * drag_default_xpm[] = {
346 " ...+++++++++++.. ",
347 " ..+.++++++++++++.. ",
348 " .++.++++++++++++.. ",
349 " .+++.++++++++++++.. ",
350 " .++++.++++++++++++. ",
351 " .+++.+++++++++++++.. ",
352 " .++.+++++++++++++++.. ",
353 " .+.+++++++++++++++++.. ",
354 " ..+++++++++++++++++++.. ",
355 " ..++++++++++++++++++++. ",
356 " .++++++++++++++++++++.. ",
357 " ..+++++++++++++++++.. ",
358 " .++++++++++++++++.. ",
359 " ..+++++++++++++... ",
371 /*********************
372 * Utility functions *
373 *********************/
375 /*************************************************************
376 * gtk_drag_get_ipc_widget:
377 * Return a invisible, off-screen, override-redirect
382 *************************************************************/
385 gtk_drag_get_ipc_widget (void)
391 GSList *tmp = drag_widgets;
392 result = drag_widgets->data;
393 drag_widgets = drag_widgets->next;
394 g_slist_free_1 (tmp);
398 result = gtk_invisible_new ();
399 gtk_widget_show (result);
405 /***************************************************************
406 * gtk_drag_release_ipc_widget:
407 * Releases widget retrieved with gtk_drag_get_ipc_widget ()
409 * widget: the widget to release.
411 ***************************************************************/
414 gtk_drag_release_ipc_widget (GtkWidget *widget)
416 drag_widgets = g_slist_prepend (drag_widgets, widget);
420 gtk_drag_get_event_actions (GdkEvent *event,
422 GdkDragAction actions,
423 GdkDragAction *suggested_action,
424 GdkDragAction *possible_actions)
426 *suggested_action = 0;
427 *possible_actions = 0;
431 GdkModifierType state = 0;
435 case GDK_MOTION_NOTIFY:
436 state = event->motion.state;
438 case GDK_BUTTON_PRESS:
439 case GDK_2BUTTON_PRESS:
440 case GDK_3BUTTON_PRESS:
441 case GDK_BUTTON_RELEASE:
442 state = event->button.state;
445 case GDK_KEY_RELEASE:
446 state = event->key.state;
448 case GDK_ENTER_NOTIFY:
449 case GDK_LEAVE_NOTIFY:
450 state = event->crossing.state;
456 if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
458 *suggested_action = GDK_ACTION_ASK;
459 *possible_actions = actions;
461 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
463 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
465 if (actions & GDK_ACTION_LINK)
467 *suggested_action = GDK_ACTION_LINK;
468 *possible_actions = GDK_ACTION_LINK;
471 else if (state & GDK_CONTROL_MASK)
473 if (actions & GDK_ACTION_COPY)
475 *suggested_action = GDK_ACTION_COPY;
476 *possible_actions = GDK_ACTION_COPY;
482 if (actions & GDK_ACTION_MOVE)
484 *suggested_action = GDK_ACTION_MOVE;
485 *possible_actions = GDK_ACTION_MOVE;
492 *possible_actions = actions;
494 if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
495 *suggested_action = GDK_ACTION_ASK;
496 else if (actions & GDK_ACTION_COPY)
497 *suggested_action = GDK_ACTION_COPY;
498 else if (actions & GDK_ACTION_MOVE)
499 *suggested_action = GDK_ACTION_MOVE;
500 else if (actions & GDK_ACTION_LINK)
501 *suggested_action = GDK_ACTION_LINK;
509 gtk_drag_get_cursor (GdkDragAction action)
513 for (i = 0 ; i < n_drag_cursors - 1; i++)
514 if (drag_cursors[i].action == action)
517 if (drag_cursors[i].cursor == NULL)
522 gdk_bitmap_create_from_data (NULL,
523 drag_cursors[i].bits,
524 CURSOR_WIDTH, CURSOR_HEIGHT);
526 gdk_bitmap_create_from_data (NULL,
527 drag_cursors[i].mask,
528 CURSOR_WIDTH, CURSOR_HEIGHT);
530 gdk_color_white (gdk_colormap_get_system (), &bg);
531 gdk_color_black (gdk_colormap_get_system (), &fg);
533 drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
535 gdk_pixmap_unref (pixmap);
536 gdk_pixmap_unref (mask);
539 return drag_cursors[i].cursor;
542 /********************
544 ********************/
546 /*************************************************************
548 * Get the data for a drag or drop
550 * context - drag context
551 * target - format to retrieve the data in.
552 * time - timestamp of triggering event.
555 *************************************************************/
558 gtk_drag_get_data (GtkWidget *widget,
559 GdkDragContext *context,
563 GtkWidget *selection_widget;
565 g_return_if_fail (widget != NULL);
566 g_return_if_fail (context != NULL);
568 selection_widget = gtk_drag_get_ipc_widget ();
570 gdk_drag_context_ref (context);
571 gtk_widget_ref (widget);
573 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
574 GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
576 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
578 gtk_selection_convert (selection_widget,
579 gdk_drag_get_selection (context),
585 /*************************************************************
586 * gtk_drag_get_source_widget:
587 * Get the widget the was the source of this drag, if
588 * the drag originated from this application.
590 * context: The drag context for this drag
592 * The source widget, or NULL if the drag originated from
593 * a different application.
594 *************************************************************/
597 gtk_drag_get_source_widget (GdkDragContext *context)
601 tmp_list = source_widgets;
604 GtkWidget *ipc_widget = tmp_list->data;
606 if (ipc_widget->window == context->source_window)
608 GtkDragSourceInfo *info;
609 info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
611 return info ? info->widget : NULL;
614 tmp_list = tmp_list->next;
620 /*************************************************************
622 * Notify the drag source that the transfer of data
625 * context: The drag context for this drag
626 * success: Was the data successfully transferred?
627 * time: The timestamp to use when notifying the destination.
629 *************************************************************/
632 gtk_drag_finish (GdkDragContext *context,
637 GdkAtom target = GDK_NONE;
639 g_return_if_fail (context != NULL);
643 target = gdk_atom_intern ("DELETE", FALSE);
645 else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
647 target = gdk_atom_intern (success ?
648 "XmTRANSFER_SUCCESS" :
649 "XmTRANSFER_FAILURE",
653 if (target != GDK_NONE)
655 GtkWidget *selection_widget = gtk_drag_get_ipc_widget ();
657 gdk_drag_context_ref (context);
659 gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
660 gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
661 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
664 gtk_selection_convert (selection_widget,
665 gdk_drag_get_selection (context),
671 gdk_drop_finish (context, success, time);
674 /*************************************************************
675 * gtk_drag_highlight_paint:
676 * Paint a highlight indicating drag status onto the widget.
680 *************************************************************/
683 gtk_drag_highlight_paint (GtkWidget *widget)
685 gint x, y, width, height;
687 g_return_if_fail (widget != NULL);
689 if (GTK_WIDGET_DRAWABLE (widget))
691 if (GTK_WIDGET_NO_WINDOW (widget))
693 x = widget->allocation.x;
694 y = widget->allocation.y;
695 width = widget->allocation.width;
696 height = widget->allocation.height;
702 gdk_window_get_size (widget->window, &width, &height);
705 gtk_draw_shadow (widget->style, widget->window,
706 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
707 x, y, width, height);
709 gdk_draw_rectangle (widget->window,
710 widget->style->black_gc,
712 x, y, width - 1, height - 1);
716 /*************************************************************
717 * gtk_drag_highlight_expose:
718 * Callback for expose_event for highlighted widgets.
724 *************************************************************/
727 gtk_drag_highlight_expose (GtkWidget *widget,
728 GdkEventExpose *event,
731 gtk_drag_highlight_paint (widget);
735 /*************************************************************
736 * gtk_drag_highlight:
737 * Highlight the given widget in the default manner.
741 *************************************************************/
744 gtk_drag_highlight (GtkWidget *widget)
746 gtk_signal_connect_after (GTK_OBJECT (widget), "draw",
747 GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
749 gtk_signal_connect (GTK_OBJECT (widget), "expose_event",
750 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
753 gtk_widget_queue_draw (widget);
756 /*************************************************************
757 * gtk_drag_unhighlight:
758 * Refresh the given widget to remove the highlight.
762 *************************************************************/
765 gtk_drag_unhighlight (GtkWidget *widget)
767 g_return_if_fail (widget != NULL);
769 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
770 GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
772 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
773 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
776 gtk_widget_queue_clear (widget);
779 /*************************************************************
781 * Register a drop site, and possibly add default behaviors.
784 * flags: Which types of default drag behavior to use
785 * targets: Table of targets that can be accepted
786 * n_targets: Number of of entries in targets
789 *************************************************************/
792 gtk_drag_dest_set (GtkWidget *widget,
793 GtkDestDefaults flags,
794 const GtkTargetEntry *targets,
796 GdkDragAction actions)
798 GtkDragDestSite *site;
800 g_return_if_fail (widget != NULL);
802 /* HACK, do this in the destroy */
803 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
805 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
807 if (GTK_WIDGET_REALIZED (widget))
808 gtk_drag_dest_realized (widget);
810 gtk_signal_connect (GTK_OBJECT (widget), "realize",
811 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
813 site = g_new (GtkDragDestSite, 1);
816 site->have_drag = FALSE;
818 site->target_list = gtk_target_list_new (targets, n_targets);
820 site->target_list = NULL;
822 site->actions = actions;
823 site->do_proxy = FALSE;
825 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
826 site, gtk_drag_dest_site_destroy);
829 /*************************************************************
830 * gtk_drag_dest_set_proxy:
831 * Set up this widget to proxy drags elsewhere.
834 * proxy_window: window to which forward drag events
835 * protocol: Drag protocol which the dest widget accepts
836 * use_coordinates: If true, send the same coordinates to the
837 * destination, because it is a embedded
840 *************************************************************/
843 gtk_drag_dest_set_proxy (GtkWidget *widget,
844 GdkWindow *proxy_window,
845 GdkDragProtocol protocol,
846 gboolean use_coordinates)
848 GtkDragDestSite *site;
850 g_return_if_fail (widget != NULL);
852 /* HACK, do this in the destroy */
853 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
855 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
857 if (GTK_WIDGET_REALIZED (widget))
858 gtk_drag_dest_realized (widget);
860 gtk_signal_connect (GTK_OBJECT (widget), "realize",
861 GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
863 site = g_new (GtkDragDestSite, 1);
866 site->have_drag = FALSE;
867 site->target_list = NULL;
869 site->proxy_window = proxy_window;
871 gdk_window_ref (proxy_window);
872 site->do_proxy = TRUE;
873 site->proxy_protocol = protocol;
874 site->proxy_coords = use_coordinates;
876 gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
877 site, gtk_drag_dest_site_destroy);
880 /*************************************************************
881 * gtk_drag_dest_unset
882 * Unregister this widget as a drag target.
886 *************************************************************/
889 gtk_drag_dest_unset (GtkWidget *widget)
891 g_return_if_fail (widget != NULL);
893 gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
896 /*************************************************************
897 * gtk_drag_dest_handle_event:
898 * Called from widget event handling code on Drag events
902 * toplevel: Toplevel widget that received the event
905 *************************************************************/
908 gtk_drag_dest_handle_event (GtkWidget *toplevel,
911 GtkDragDestInfo *info;
912 GdkDragContext *context;
914 g_return_if_fail (toplevel != NULL);
915 g_return_if_fail (event != NULL);
917 context = event->dnd.context;
919 info = g_dataset_get_data (context, "gtk-info");
922 info = g_new (GtkDragDestInfo, 1);
924 info->context = event->dnd.context;
925 info->proxy_source = NULL;
926 info->proxy_data = NULL;
927 info->dropped = FALSE;
928 info->proxy_drop_wait = FALSE;
929 g_dataset_set_data_full (context,
932 gtk_drag_dest_info_destroy);
935 /* Find the widget for the event */
944 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
949 case GDK_DRAG_MOTION:
952 GtkDragFindData data;
955 if (event->type == GDK_DROP_START)
956 info->dropped = TRUE;
958 gdk_window_get_origin (toplevel->window, &tx, &ty);
960 data.x = event->dnd.x_root - tx;
961 data.y = event->dnd.y_root - ty;
962 data.context = context;
965 data.toplevel = TRUE;
966 data.callback = (event->type == GDK_DRAG_MOTION) ?
967 gtk_drag_dest_motion : gtk_drag_dest_drop;
968 data.time = event->dnd.time;
970 gtk_drag_find_widget (toplevel, &data);
972 /* We send a leave here so that the widget unhighlights
976 ((event->type == GDK_DROP_START) || (!data.found)))
978 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
984 if (event->type == GDK_DRAG_MOTION)
987 gdk_drag_status (context, 0, event->dnd.time);
989 else if (event->type == GDK_DROP_START)
991 gdk_drop_reply (context, data.found, event->dnd.time);
992 if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
993 gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
999 g_assert_not_reached ();
1003 /*************************************************************
1004 * gtk_drag_dest_find_target:
1005 * Decide on a target for the drag.
1010 *************************************************************/
1013 gtk_drag_dest_find_target (GtkWidget *widget,
1014 GtkDragDestSite *site,
1015 GdkDragContext *context)
1018 GList *tmp_source = NULL;
1019 GtkWidget *source_widget = gtk_drag_get_source_widget (context);
1021 tmp_target = site->target_list->list;
1024 GtkTargetPair *pair = tmp_target->data;
1025 tmp_source = context->targets;
1028 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1030 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1031 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1032 return pair->target;
1036 tmp_source = tmp_source->next;
1038 tmp_target = tmp_target->next;
1045 gtk_drag_selection_received (GtkWidget *widget,
1046 GtkSelectionData *selection_data,
1050 GdkDragContext *context;
1051 GtkDragDestInfo *info;
1052 GtkWidget *drop_widget;
1056 context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1057 info = g_dataset_get_data (context, "gtk-info");
1059 if (info->proxy_data &&
1060 info->proxy_data->target == selection_data->target)
1062 gtk_selection_data_set (info->proxy_data,
1063 selection_data->type,
1064 selection_data->format,
1065 selection_data->data,
1066 selection_data->length);
1071 if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1073 gtk_drag_finish (context, TRUE, FALSE, time);
1075 else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1076 (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1082 GtkDragDestSite *site;
1084 site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1086 if (site->target_list)
1090 if (gtk_target_list_find (site->target_list,
1091 selection_data->target,
1094 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1095 selection_data->length >= 0)
1096 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1097 "drag_data_received",
1098 context, info->drop_x, info->drop_y,
1105 gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1106 "drag_data_received",
1107 context, info->drop_x, info->drop_y,
1108 selection_data, 0, time);
1111 if (site->flags & GTK_DEST_DEFAULT_DROP)
1114 gtk_drag_finish (context,
1115 (selection_data->length >= 0),
1116 (context->action == GDK_ACTION_MOVE),
1120 gtk_widget_unref (drop_widget);
1123 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1124 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1127 gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1128 gdk_drag_context_unref (context);
1130 gtk_drag_release_ipc_widget (widget);
1133 /*************************************************************
1134 * gtk_drag_find_widget:
1135 * Recursive callback used to locate widgets for
1136 * DRAG_MOTION and DROP_START events.
1140 *************************************************************/
1143 gtk_drag_find_widget (GtkWidget *widget,
1144 GtkDragFindData *data)
1146 GtkAllocation new_allocation;
1150 new_allocation = widget->allocation;
1152 if (data->found || !GTK_WIDGET_MAPPED (widget))
1155 /* Note that in the following code, we only count the
1156 * position as being inside a WINDOW widget if it is inside
1157 * widget->window; points that are outside of widget->window
1158 * but within the allocation are not counted. This is consistent
1159 * with the way we highlight drag targets.
1161 if (!GTK_WIDGET_NO_WINDOW (widget))
1163 new_allocation.x = 0;
1164 new_allocation.y = 0;
1169 GdkWindow *window = widget->window;
1170 while (window != widget->parent->window)
1172 gint tx, ty, twidth, theight;
1173 gdk_window_get_size (window, &twidth, &theight);
1175 if (new_allocation.x < 0)
1177 new_allocation.width += new_allocation.x;
1178 new_allocation.x = 0;
1180 if (new_allocation.y < 0)
1182 new_allocation.height += new_allocation.y;
1183 new_allocation.y = 0;
1185 if (new_allocation.x + new_allocation.width > twidth)
1186 new_allocation.width = twidth - new_allocation.x;
1187 if (new_allocation.y + new_allocation.height > theight)
1188 new_allocation.height = theight - new_allocation.y;
1190 gdk_window_get_position (window, &tx, &ty);
1191 new_allocation.x += tx;
1193 new_allocation.y += ty;
1196 window = gdk_window_get_parent (window);
1200 if (data->toplevel ||
1201 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1202 (data->x < new_allocation.x + new_allocation.width) &&
1203 (data->y < new_allocation.y + new_allocation.height)))
1205 /* First, check if the drag is in a valid drop site in
1206 * one of our children
1208 if (GTK_IS_CONTAINER (widget))
1210 GtkDragFindData new_data = *data;
1212 new_data.x -= x_offset;
1213 new_data.y -= y_offset;
1214 new_data.found = FALSE;
1215 new_data.toplevel = FALSE;
1217 gtk_container_forall (GTK_CONTAINER (widget),
1218 (GtkCallback)gtk_drag_find_widget,
1221 data->found = new_data.found;
1224 /* If not, and this widget is registered as a drop site, check to
1225 * emit "drag_motion" to check if we are actually in
1229 gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1231 data->found = data->callback (widget,
1233 data->x - new_allocation.x,
1234 data->y - new_allocation.y,
1236 /* If so, send a "drag_leave" to the last widget */
1239 if (data->info->widget && data->info->widget != widget)
1241 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1243 data->info->widget = widget;
1250 gtk_drag_proxy_begin (GtkWidget *widget,
1251 GtkDragDestInfo *dest_info)
1253 GtkDragSourceInfo *source_info;
1256 source_info = g_new0 (GtkDragSourceInfo, 1);
1257 source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1259 source_info->widget = widget;
1260 gtk_widget_ref (source_info->widget);
1261 source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1262 dest_info->context->targets);
1264 source_info->target_list = gtk_target_list_new (NULL, 0);
1265 tmp_list = dest_info->context->targets;
1268 gtk_target_list_add (source_info->target_list,
1269 GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1270 tmp_list = tmp_list->next;
1273 source_info->proxy_dest = dest_info;
1275 g_dataset_set_data (source_info->context, "gtk-info", source_info);
1277 gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget),
1279 GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1282 dest_info->proxy_source = source_info;
1286 gtk_drag_dest_info_destroy (gpointer data)
1288 GtkDragDestInfo *info = data;
1294 gtk_drag_dest_realized (GtkWidget *widget)
1296 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1297 gdk_window_register_dnd (toplevel->window);
1301 gtk_drag_dest_site_destroy (gpointer data)
1303 GtkDragDestSite *site = data;
1305 if (site->target_list)
1306 gtk_target_list_unref (site->target_list);
1312 * Default drag handlers
1315 gtk_drag_dest_leave (GtkWidget *widget,
1316 GdkDragContext *context,
1319 GtkDragDestSite *site;
1321 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1322 g_return_if_fail (site != NULL);
1326 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1328 if (info->proxy_source && !info->dropped)
1329 gdk_drag_abort (info->proxy_source->context, time);
1335 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1336 gtk_drag_unhighlight (widget);
1338 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1339 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1342 site->have_drag = FALSE;
1347 gtk_drag_dest_motion (GtkWidget *widget,
1348 GdkDragContext *context,
1353 GtkDragDestSite *site;
1354 GdkDragAction action = 0;
1357 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1358 g_return_val_if_fail (site != NULL, FALSE);
1363 GdkEvent *current_event;
1364 GdkWindow *dest_window;
1365 GdkDragProtocol proto;
1367 GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1369 if (!info->proxy_source)
1370 gtk_drag_proxy_begin (widget, info);
1372 current_event = gtk_get_current_event ();
1374 if (site->proxy_window)
1376 dest_window = site->proxy_window;
1377 proto = site->proxy_protocol;
1381 gdk_drag_find_window (info->proxy_source->context,
1383 current_event->dnd.x_root,
1384 current_event->dnd.y_root,
1385 &dest_window, &proto);
1388 gdk_drag_motion (info->proxy_source->context,
1390 current_event->dnd.x_root,
1391 current_event->dnd.y_root,
1392 context->suggested_action,
1393 context->actions, time);
1395 if (!site->proxy_window && dest_window)
1396 gdk_window_unref (dest_window);
1398 selection = gdk_drag_get_selection (info->proxy_source->context);
1400 selection != gdk_drag_get_selection (info->context))
1401 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1403 gdk_event_free (current_event);
1408 if (site->flags & GTK_DEST_DEFAULT_MOTION)
1410 if (context->suggested_action & site->actions)
1411 action = context->suggested_action;
1418 if ((site->actions & (1 << i)) &&
1419 (context->actions & (1 << i)))
1427 if (action && gtk_drag_dest_find_target (widget, site, context))
1429 if (!site->have_drag)
1431 site->have_drag = TRUE;
1432 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1433 gtk_drag_highlight (widget);
1436 gdk_drag_status (context, action, time);
1440 gdk_drag_status (context, 0, time);
1445 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1446 context, x, y, time, &retval);
1448 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1452 gtk_drag_dest_drop (GtkWidget *widget,
1453 GdkDragContext *context,
1458 GtkDragDestSite *site;
1459 GtkDragDestInfo *info;
1461 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1462 g_return_val_if_fail (site != NULL, FALSE);
1464 info = g_dataset_get_data (context, "gtk-info");
1465 g_return_val_if_fail (info != NULL, FALSE);
1472 if (info->proxy_source ||
1473 (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1475 gtk_drag_drop (info->proxy_source, time);
1479 /* We need to synthesize a motion event, wait for a status,
1480 * and, if we get a good one, do a drop.
1483 GdkEvent *current_event;
1485 GdkWindow *dest_window;
1486 GdkDragProtocol proto;
1488 gtk_drag_proxy_begin (widget, info);
1489 info->proxy_drop_wait = TRUE;
1490 info->proxy_drop_time = time;
1492 current_event = gtk_get_current_event ();
1494 if (site->proxy_window)
1496 dest_window = site->proxy_window;
1497 proto = site->proxy_protocol;
1501 gdk_drag_find_window (info->proxy_source->context,
1503 current_event->dnd.x_root,
1504 current_event->dnd.y_root,
1505 &dest_window, &proto);
1508 gdk_drag_motion (info->proxy_source->context,
1510 current_event->dnd.x_root,
1511 current_event->dnd.y_root,
1512 context->suggested_action,
1513 context->actions, time);
1515 if (!site->proxy_window && dest_window)
1516 gdk_window_unref (dest_window);
1518 selection = gdk_drag_get_selection (info->proxy_source->context);
1520 selection != gdk_drag_get_selection (info->context))
1521 gtk_drag_source_check_selection (info->proxy_source, selection, time);
1523 gdk_event_free (current_event);
1533 if (site->flags & GTK_DEST_DEFAULT_DROP)
1535 GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
1537 if (target == GDK_NONE)
1540 gtk_drag_get_data (widget, context, target, time);
1543 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1544 context, x, y, time, &retval);
1546 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1554 /*************************************************************
1555 * gtk_drag_begin: Start a drag operation
1558 * widget: Widget from which drag starts
1559 * handlers: List of handlers to supply the data for the drag
1560 * button: Button user used to start drag
1561 * time: Time of event starting drag
1564 *************************************************************/
1567 gtk_drag_begin (GtkWidget *widget,
1568 GtkTargetList *target_list,
1569 GdkDragAction actions,
1573 GtkDragSourceInfo *info;
1574 GList *targets = NULL;
1576 guint32 time = GDK_CURRENT_TIME;
1577 GdkDragAction possible_actions, suggested_action;
1579 g_return_val_if_fail (widget != NULL, NULL);
1580 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1581 g_return_val_if_fail (target_list != NULL, NULL);
1584 time = gdk_event_get_time (event);
1586 info = g_new0 (GtkDragSourceInfo, 1);
1587 info->ipc_widget = gtk_drag_get_ipc_widget ();
1588 source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
1590 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1592 tmp_list = g_list_last (target_list->list);
1595 GtkTargetPair *pair = tmp_list->data;
1596 targets = g_list_prepend (targets,
1597 GINT_TO_POINTER (pair->target));
1598 tmp_list = tmp_list->prev;
1601 info->widget = widget;
1602 gtk_widget_ref (info->widget);
1604 info->context = gdk_drag_begin (info->ipc_widget->window, targets);
1605 g_list_free (targets);
1607 g_dataset_set_data (info->context, "gtk-info", info);
1609 info->button = button;
1610 info->target_list = target_list;
1611 gtk_target_list_ref (target_list);
1613 info->possible_actions = actions;
1615 info->cursor = NULL;
1616 info->status = GTK_DRAG_STATUS_DRAG;
1617 info->last_event = NULL;
1618 info->selections = NULL;
1619 info->icon_window = NULL;
1620 info->destroy_icon = FALSE;
1622 gtk_drag_get_event_actions (event, info->button, actions,
1623 &suggested_action, &possible_actions);
1626 info->cursor = gtk_drag_get_cursor (suggested_action);
1628 /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1629 * the drag icon, it will be in the right place
1631 if (event->type == GDK_MOTION_NOTIFY)
1633 info->cur_x = event->motion.x_root;
1634 info->cur_y = event->motion.y_root;
1639 gdk_window_get_pointer (GDK_ROOT_PARENT (), &x, &y, NULL);
1645 gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1648 /* We use a GTK grab here to override any grabs that the widget
1649 * we are dragging from might have held
1652 gtk_grab_add (info->ipc_widget);
1653 gdk_pointer_grab (info->ipc_widget->window, FALSE,
1654 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1655 GDK_BUTTON_RELEASE_MASK, NULL,
1656 info->cursor, time);
1658 if (event->type == GDK_MOTION_NOTIFY)
1659 gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1661 info->start_x = info->cur_x;
1662 info->start_y = info->cur_y;
1664 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1665 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1666 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1667 GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1668 gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1669 GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1671 return info->context;
1674 /*************************************************************
1675 * gtk_drag_source_set:
1676 * Register a drop site, and possibly add default behaviors.
1679 * start_button_mask: Mask of allowed buttons to start drag
1680 * targets: Table of targets for this source
1682 * actions: Actions allowed for this source
1684 *************************************************************/
1687 gtk_drag_source_set (GtkWidget *widget,
1688 GdkModifierType start_button_mask,
1689 const GtkTargetEntry *targets,
1691 GdkDragAction actions)
1693 GtkDragSourceSite *site;
1695 g_return_if_fail (widget != NULL);
1697 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1699 gtk_widget_add_events (widget,
1700 gtk_widget_get_events (widget) |
1701 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1702 GDK_BUTTON_MOTION_MASK);
1706 if (site->target_list)
1707 gtk_target_list_unref (site->target_list);
1711 site = g_new0 (GtkDragSourceSite, 1);
1713 gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1714 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1716 gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1717 GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1720 gtk_object_set_data_full (GTK_OBJECT (widget),
1722 site, gtk_drag_source_site_destroy);
1725 site->start_button_mask = start_button_mask;
1728 site->target_list = gtk_target_list_new (targets, n_targets);
1730 site->target_list = NULL;
1732 site->actions = actions;
1736 /*************************************************************
1737 * gtk_drag_source_unset
1738 * Unregister this widget as a drag source.
1742 *************************************************************/
1745 gtk_drag_source_unset (GtkWidget *widget)
1747 GtkDragSourceSite *site;
1749 g_return_if_fail (widget != NULL);
1751 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1755 gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1756 gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1760 /*************************************************************
1761 * gtk_drag_source_set_icon:
1762 * Set an icon for drags from this source.
1764 * colormap: Colormap for this icon
1768 *************************************************************/
1771 gtk_drag_source_set_icon (GtkWidget *widget,
1772 GdkColormap *colormap,
1776 GtkDragSourceSite *site;
1778 g_return_if_fail (widget != NULL);
1780 site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1781 g_return_if_fail (site != NULL);
1784 gdk_colormap_unref (site->colormap);
1786 gdk_pixmap_unref (site->pixmap);
1788 gdk_pixmap_unref (site->mask);
1790 site->colormap = colormap;
1792 gdk_colormap_ref (colormap);
1794 site->pixmap = pixmap;
1796 gdk_pixmap_ref (pixmap);
1800 gdk_pixmap_ref (mask);
1803 /*************************************************************
1804 * gtk_drag_set_icon_window:
1805 * Set a widget as the icon for a drag.
1812 *************************************************************/
1815 gtk_drag_set_icon_window (GdkDragContext *context,
1819 gboolean destroy_on_release)
1821 GtkDragSourceInfo *info;
1823 g_return_if_fail (context != NULL);
1824 g_return_if_fail (widget != NULL);
1826 info = g_dataset_get_data (context, "gtk-info");
1827 gtk_drag_remove_icon (info);
1829 info->icon_window = widget;
1830 info->hot_x = hot_x;
1831 info->hot_y = hot_y;
1835 gtk_widget_set_uposition (widget,
1836 info->cur_x - info->hot_x,
1837 info->cur_y - info->hot_y);
1838 gtk_widget_ref (widget);
1839 gdk_window_raise (widget->window);
1840 gtk_widget_show (widget);
1843 info->destroy_icon = destroy_on_release;
1846 /*************************************************************
1847 * gtk_drag_set_icon_widget:
1848 * Set a widget as the icon for a drag.
1855 *************************************************************/
1858 gtk_drag_set_icon_widget (GdkDragContext *context,
1863 g_return_if_fail (context != NULL);
1864 g_return_if_fail (widget != NULL);
1866 gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
1869 /*************************************************************
1870 * gtk_drag_set_icon_pixmap:
1871 * Set a widget as the icon for a drag.
1874 * colormap: Colormap for the icon window.
1880 *************************************************************/
1883 gtk_drag_set_icon_pixmap (GdkDragContext *context,
1884 GdkColormap *colormap,
1893 g_return_if_fail (context != NULL);
1894 g_return_if_fail (colormap != NULL);
1895 g_return_if_fail (pixmap != NULL);
1897 gdk_window_get_size (pixmap, &width, &height);
1899 gtk_widget_push_visual (gdk_colormap_get_visual (colormap));
1900 gtk_widget_push_colormap (colormap);
1902 window = gtk_window_new (GTK_WINDOW_POPUP);
1903 gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1904 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
1906 gtk_widget_pop_visual ();
1907 gtk_widget_pop_colormap ();
1909 gtk_widget_set_usize (window, width, height);
1910 gtk_widget_realize (window);
1912 gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
1915 gtk_widget_shape_combine_mask (window, mask, 0, 0);
1917 gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
1920 /*************************************************************
1921 * gtk_drag_set_icon_default:
1922 * Set the icon for a drag to the default icon.
1926 *************************************************************/
1929 gtk_drag_set_icon_default (GdkDragContext *context)
1931 g_return_if_fail (context != NULL);
1933 if (!default_icon_pixmap)
1935 default_icon_colormap = gdk_colormap_get_system ();
1936 default_icon_pixmap =
1937 gdk_pixmap_colormap_create_from_xpm_d (NULL,
1938 default_icon_colormap,
1940 NULL, drag_default_xpm);
1941 default_icon_hot_x = -2;
1942 default_icon_hot_y = -2;
1945 gtk_drag_set_icon_pixmap (context,
1946 default_icon_colormap,
1947 default_icon_pixmap,
1950 default_icon_hot_y);
1953 /*************************************************************
1954 * gtk_drag_set_default_icon:
1955 * Set a default icon for all drags as a pixmap.
1957 * colormap: Colormap for the icon window.
1963 *************************************************************/
1966 gtk_drag_set_default_icon (GdkColormap *colormap,
1972 g_return_if_fail (colormap != NULL);
1973 g_return_if_fail (pixmap != NULL);
1975 if (default_icon_colormap)
1976 gdk_colormap_unref (default_icon_colormap);
1977 if (default_icon_pixmap)
1978 gdk_pixmap_unref (default_icon_pixmap);
1979 if (default_icon_mask)
1980 gdk_pixmap_unref (default_icon_pixmap);
1982 default_icon_colormap = colormap;
1983 gdk_colormap_ref (colormap);
1985 default_icon_pixmap = pixmap;
1986 gdk_pixmap_ref (pixmap);
1988 default_icon_mask = mask;
1990 gdk_pixmap_ref (mask);
1992 default_icon_hot_x = hot_x;
1993 default_icon_hot_y = hot_y;
1997 /*************************************************************
1998 * gtk_drag_source_handle_event:
1999 * Called from widget event handling code on Drag events
2003 * toplevel: Toplevel widget that received the event
2006 *************************************************************/
2009 gtk_drag_source_handle_event (GtkWidget *widget,
2012 GtkDragSourceInfo *info;
2013 GdkDragContext *context;
2015 g_return_if_fail (widget != NULL);
2016 g_return_if_fail (event != NULL);
2018 context = event->dnd.context;
2019 info = g_dataset_get_data (context, "gtk-info");
2023 switch (event->type)
2025 case GDK_DRAG_STATUS:
2029 if (info->proxy_dest)
2031 if (!event->dnd.send_event)
2033 if (info->proxy_dest->proxy_drop_wait)
2035 /* Aha - we can finally pass the MOTIF DROP on... */
2036 gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2040 gdk_drag_status (info->proxy_dest->context,
2041 event->dnd.context->action,
2048 cursor = gtk_drag_get_cursor (event->dnd.context->action);
2049 if (info->cursor != cursor)
2051 #if GDK_WINDOWING == GDK_WINDOWING_X11
2052 XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window),
2053 PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
2054 ((GdkCursorPrivate *)cursor)->xcursor,
2056 #elif GDK_WINDOWING == GDK_WINDOWING_WIN32
2057 gdk_pointer_grab (widget->window, FALSE,
2058 GDK_POINTER_MOTION_MASK |
2059 GDK_POINTER_MOTION_HINT_MASK |
2060 GDK_BUTTON_RELEASE_MASK,
2062 info->cursor, event->dnd.time);
2064 info->cursor = cursor;
2067 if (info->last_event)
2069 gtk_drag_motion_cb (info->widget,
2070 (GdkEventMotion *)info->last_event,
2072 info->last_event = NULL;
2078 case GDK_DROP_FINISHED:
2079 gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2082 g_assert_not_reached ();
2086 /*************************************************************
2087 * gtk_drag_source_check_selection:
2088 * Check if we've set up handlers/claimed the selection
2089 * for a given drag. If not, add them.
2093 *************************************************************/
2096 gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2102 tmp_list = info->selections;
2105 if (GPOINTER_TO_UINT (tmp_list->data) == selection)
2107 tmp_list = tmp_list->next;
2110 gtk_selection_owner_set (info->ipc_widget, selection, time);
2111 info->selections = g_list_prepend (info->selections,
2112 GUINT_TO_POINTER (selection));
2114 tmp_list = info->target_list->list;
2117 GtkTargetPair *pair = tmp_list->data;
2119 gtk_selection_add_target (info->ipc_widget,
2123 tmp_list = tmp_list->next;
2126 if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2128 gtk_selection_add_target (info->ipc_widget,
2130 gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2131 TARGET_MOTIF_SUCCESS);
2132 gtk_selection_add_target (info->ipc_widget,
2134 gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2135 TARGET_MOTIF_FAILURE);
2138 gtk_selection_add_target (info->ipc_widget,
2140 gdk_atom_intern ("DELETE", FALSE),
2144 /*************************************************************
2145 * gtk_drag_drop_finished:
2146 * Clean up from the drag, and display snapback, if necessary.
2152 *************************************************************/
2155 gtk_drag_drop_finished (GtkDragSourceInfo *info,
2159 gtk_drag_source_release_selections (info, time);
2161 if (info->proxy_dest)
2163 /* The time from the event isn't reliable for Xdnd drags */
2164 gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2165 info->proxy_dest->proxy_drop_time);
2166 gtk_drag_source_info_destroy (info);
2172 gtk_drag_source_info_destroy (info);
2176 GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2180 anim->n_steps = MAX (info->cur_x - info->start_x,
2181 info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2182 anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2183 if (info->icon_window)
2185 gtk_widget_show (info->icon_window);
2186 gdk_window_raise (info->icon_window->window);
2189 /* Mark the context as dead, so if the destination decides
2190 * to respond really late, we still are OK.
2192 g_dataset_set_data (info->context, "gtk-info", NULL);
2193 gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2199 gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2202 GList *tmp_list = info->selections;
2205 GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2206 if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2207 gtk_selection_owner_set (NULL, selection, time);
2208 tmp_list = tmp_list->next;
2211 g_list_free (info->selections);
2212 info->selections = NULL;
2215 /*************************************************************
2217 * Send a drop event.
2221 *************************************************************/
2224 gtk_drag_drop (GtkDragSourceInfo *info,
2227 if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2229 GtkSelectionData selection_data;
2231 GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2233 tmp_list = info->target_list->list;
2236 GtkTargetPair *pair = tmp_list->data;
2238 if (pair->target == target)
2240 selection_data.selection = GDK_NONE;
2241 selection_data.target = target;
2242 selection_data.data = NULL;
2243 selection_data.length = -1;
2245 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2246 info->context, &selection_data,
2250 /* FIXME: Should we check for length >= 0 here? */
2251 gtk_drag_drop_finished (info, TRUE, time);
2254 tmp_list = tmp_list->next;
2256 gtk_drag_drop_finished (info, FALSE, time);
2260 if (info->icon_window)
2261 gtk_widget_hide (info->icon_window);
2263 gdk_drag_drop (info->context, time);
2264 info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2265 gtk_drag_abort_timeout,
2271 * Source side callbacks.
2275 gtk_drag_source_event_cb (GtkWidget *widget,
2279 GtkDragSourceSite *site;
2280 site = (GtkDragSourceSite *)data;
2282 switch (event->type)
2284 case GDK_BUTTON_PRESS:
2285 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2287 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2288 site->x = event->button.x;
2289 site->y = event->button.y;
2293 case GDK_BUTTON_RELEASE:
2294 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2296 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2300 case GDK_MOTION_NOTIFY:
2301 if (site->state & event->motion.state & site->start_button_mask)
2303 /* FIXME: This is really broken and can leave us
2309 if (site->state & event->motion.state &
2310 GDK_BUTTON1_MASK << (i - 1))
2314 if (MAX (ABS (site->x - event->motion.x),
2315 ABS (site->y - event->motion.y)) > 3)
2317 GtkDragSourceInfo *info;
2318 GdkDragContext *context;
2321 context = gtk_drag_begin (widget, site->target_list,
2326 info = g_dataset_get_data (context, "gtk-info");
2328 if (!info->icon_window)
2331 gtk_drag_set_icon_pixmap (context,
2334 site->mask, -2, -2);
2336 gtk_drag_set_icon_default (context);
2344 default: /* hit for 2/3BUTTON_PRESS */
2351 gtk_drag_source_site_destroy (gpointer data)
2353 GtkDragSourceSite *site = data;
2355 if (site->target_list)
2356 gtk_target_list_unref (site->target_list);
2359 gdk_pixmap_unref (site->pixmap);
2362 gdk_pixmap_unref (site->mask);
2368 gtk_drag_selection_get (GtkWidget *widget,
2369 GtkSelectionData *selection_data,
2374 GtkDragSourceInfo *info = data;
2375 static GdkAtom null_atom = GDK_NONE;
2379 null_atom = gdk_atom_intern ("NULL", FALSE);
2384 gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2387 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2389 case TARGET_MOTIF_SUCCESS:
2390 gtk_drag_drop_finished (info, TRUE, time);
2391 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2393 case TARGET_MOTIF_FAILURE:
2394 gtk_drag_drop_finished (info, FALSE, time);
2395 gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2398 if (info->proxy_dest)
2400 /* This is sort of dangerous and needs to be thought
2403 info->proxy_dest->proxy_data = selection_data;
2404 gtk_drag_get_data (info->widget,
2405 info->proxy_dest->context,
2406 selection_data->target,
2409 info->proxy_dest->proxy_data = NULL;
2413 if (gtk_target_list_find (info->target_list,
2414 selection_data->target,
2417 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2429 gtk_drag_anim_timeout (gpointer data)
2431 GtkDragAnim *anim = data;
2435 GDK_THREADS_ENTER ();
2437 if (anim->step == anim->n_steps)
2439 gtk_drag_source_info_destroy (anim->info);
2446 x = (anim->info->start_x * (anim->step + 1) +
2447 anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2448 y = (anim->info->start_y * (anim->step + 1) +
2449 anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2450 if (anim->info->icon_window)
2451 gtk_widget_set_uposition (anim->info->icon_window,
2452 x - anim->info->hot_x,
2453 y - anim->info->hot_y);
2460 GDK_THREADS_LEAVE ();
2466 gtk_drag_remove_icon (GtkDragSourceInfo *info)
2468 if (info->icon_window)
2470 gtk_widget_hide (info->icon_window);
2471 if (info->destroy_icon)
2472 gtk_widget_destroy (info->icon_window);
2474 gtk_widget_unref (info->icon_window);
2475 info->icon_window = NULL;
2480 gtk_drag_source_info_destroy (gpointer data)
2482 GtkDragSourceInfo *info = data;
2484 gtk_drag_remove_icon (data);
2486 if (!info->proxy_dest)
2487 gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2491 gtk_widget_unref (info->widget);
2493 gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2494 gtk_selection_remove_all (info->ipc_widget);
2495 gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2496 source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2497 gtk_drag_release_ipc_widget (info->ipc_widget);
2499 gtk_target_list_unref (info->target_list);
2501 g_dataset_set_data (info->context, "gtk-info", NULL);
2502 gdk_drag_context_unref (info->context);
2504 if (info->drop_timeout)
2505 gtk_timeout_remove (info->drop_timeout);
2510 /*************************************************************
2511 * gtk_drag_motion_cb:
2512 * "motion_notify_event" callback during drag.
2516 *************************************************************/
2519 gtk_drag_motion_cb (GtkWidget *widget,
2520 GdkEventMotion *event,
2523 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2525 GdkDragAction action;
2526 GdkDragAction possible_actions;
2527 GdkWindow *window = NULL;
2528 GdkWindow *dest_window;
2529 GdkDragProtocol protocol;
2530 gint x_root, y_root;
2534 gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2535 event->x_root = x_root;
2536 event->y_root = y_root;
2539 gtk_drag_get_event_actions ((GdkEvent *)event,
2541 info->possible_actions,
2542 &action, &possible_actions);
2544 info->cur_x = event->x_root;
2545 info->cur_y = event->y_root;
2547 if (info->icon_window)
2549 gdk_window_raise (info->icon_window->window);
2550 gtk_widget_set_uposition (info->icon_window,
2551 info->cur_x - info->hot_x,
2552 info->cur_y - info->hot_y);
2553 window = info->icon_window->window;
2556 gdk_drag_find_window (info->context,
2557 window, event->x_root, event->y_root,
2558 &dest_window, &protocol);
2560 if (gdk_drag_motion (info->context, dest_window, protocol,
2561 event->x_root, event->y_root, action,
2565 if (info->last_event)
2566 gdk_event_free ((GdkEvent *)info->last_event);
2568 info->last_event = gdk_event_copy ((GdkEvent *)event);
2572 gdk_window_unref (dest_window);
2574 selection = gdk_drag_get_selection (info->context);
2576 gtk_drag_source_check_selection (info, selection, event->time);
2579 /* We ignore the response, so we can respond precisely to the drop
2582 gdk_window_get_pointer (widget->window, NULL, NULL, NULL);
2588 /*************************************************************
2589 * gtk_drag_motion_cb:
2590 * "button_release_event" callback during drag.
2594 *************************************************************/
2597 gtk_drag_button_release_cb (GtkWidget *widget,
2598 GdkEventButton *event,
2601 GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2602 GtkWidget *source_widget = info->widget;
2603 GdkEvent send_event;
2605 gtk_widget_ref (source_widget);
2607 if (event->button != info->button)
2610 gdk_pointer_ungrab (event->time);
2612 gtk_grab_remove (widget);
2613 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
2614 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2616 gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
2617 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2620 if ((info->context->action != 0) && (info->context->dest_window != NULL))
2622 gtk_drag_drop (info, event->time);
2626 gdk_drag_abort (info->context, event->time);
2627 gtk_drag_drop_finished (info, FALSE, event->time);
2630 /* Send on a release pair to the the original
2631 * widget to convince it to release its grab. We need to
2632 * call gtk_propagate_event() here, instead of
2633 * gtk_widget_event() because widget like GtkList may
2634 * expect propagation.
2637 send_event.button.type = GDK_BUTTON_RELEASE;
2638 send_event.button.window = GDK_ROOT_PARENT ();
2639 send_event.button.send_event = TRUE;
2640 send_event.button.time = event->time;
2641 send_event.button.x = 0;
2642 send_event.button.y = 0;
2643 send_event.button.pressure = 0.;
2644 send_event.button.xtilt = 0.;
2645 send_event.button.ytilt = 0.;
2646 send_event.button.state = event->state;
2647 send_event.button.button = event->button;
2648 send_event.button.source = GDK_SOURCE_PEN;
2649 send_event.button.deviceid = GDK_CORE_POINTER;
2650 send_event.button.x_root = 0;
2651 send_event.button.y_root = 0;
2653 gtk_propagate_event (source_widget, &send_event);
2655 gtk_widget_unref (source_widget);
2661 gtk_drag_abort_timeout (gpointer data)
2663 GtkDragSourceInfo *info = data;
2664 guint32 time = GDK_CURRENT_TIME;
2666 if (info->proxy_dest)
2667 time = info->proxy_dest->proxy_drop_time;
2669 info->drop_timeout = 0;
2670 gtk_drag_drop_finished (info, FALSE, time);