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, see <http://www.gnu.org/licenses/>.
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
33 #include "gtkiconfactory.h"
34 #include "gtkicontheme.h"
35 #include "gtkimageprivate.h"
36 #include "gtkinvisible.h"
40 #include "gtkwindow.h"
42 #include "gtkquartz.h"
43 #include "gdk/quartz/gdkquartz.h"
44 #include "gtkselectionprivate.h"
46 typedef struct _GtkDragSourceSite GtkDragSourceSite;
47 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
48 typedef struct _GtkDragDestSite GtkDragDestSite;
49 typedef struct _GtkDragDestInfo GtkDragDestInfo;
50 typedef struct _GtkDragFindData GtkDragFindData;
52 static void gtk_drag_find_widget (GtkWidget *widget,
53 GtkDragFindData *data);
54 static void gtk_drag_dest_site_destroy (gpointer data);
55 static void gtk_drag_dest_leave (GtkWidget *widget,
56 GdkDragContext *context,
58 static GtkDragDestInfo *gtk_drag_get_dest_info (GdkDragContext *context,
60 static void gtk_drag_source_site_destroy (gpointer data);
62 static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context,
65 extern GdkDragContext *gdk_quartz_drag_source_context (); /* gdk/quartz/gdkdnd-quartz.c */
67 struct _GtkDragSourceSite
69 GdkModifierType start_button_mask;
70 GtkTargetList *target_list; /* Targets for drag data */
71 GdkDragAction actions; /* Possible actions */
74 GtkImageType icon_type;
77 GtkImagePixbufData pixbuf;
78 GtkImageStockData stock;
79 GtkImageIconNameData name;
82 /* Stored button press information to detect drag beginning */
87 struct _GtkDragSourceInfo
89 GtkWidget *source_widget;
91 GtkTargetList *target_list; /* Targets for drag data */
92 GdkDragAction possible_actions; /* Actions allowed by source */
93 GdkDragContext *context; /* drag context */
94 NSEvent *nsevent; /* what started it */
95 gint hot_x, hot_y; /* Hot spot for drag */
96 GdkPixbuf *icon_pixbuf;
101 struct _GtkDragDestSite
103 GtkDestDefaults flags;
104 GtkTargetList *target_list;
105 GdkDragAction actions;
107 guint track_motion : 1;
110 struct _GtkDragDestInfo
112 GtkWidget *widget; /* Widget in which drag is in */
113 GdkDragContext *context; /* Drag context */
114 guint dropped : 1; /* Set after we receive a drop */
115 gint drop_x, drop_y; /* Position of drop */
118 struct _GtkDragFindData
122 GdkDragContext *context;
123 GtkDragDestInfo *info;
126 gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
127 gint x, gint y, guint32 time);
132 @interface GtkDragSourceOwner : NSObject {
133 GtkDragSourceInfo *info;
138 @implementation GtkDragSourceOwner
139 -(void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
142 GtkSelectionData selection_data;
144 selection_data.selection = GDK_NONE;
145 selection_data.data = NULL;
146 selection_data.length = -1;
147 selection_data.target = _gtk_quartz_pasteboard_type_to_atom (type);
148 selection_data.display = gdk_display_get_default ();
150 if (gtk_target_list_find (info->target_list,
151 selection_data.target,
154 g_signal_emit_by_name (info->widget, "drag-data-get",
160 if (selection_data.length >= 0)
161 _gtk_quartz_set_selection_data_for_pasteboard (sender, &selection_data);
163 g_free (selection_data.data);
167 - (id)initWithInfo:(GtkDragSourceInfo *)anInfo
182 gtk_drag_get_data (GtkWidget *widget,
183 GdkDragContext *context,
187 id <NSDraggingInfo> dragging_info;
188 NSPasteboard *pasteboard;
189 GtkSelectionData *selection_data;
190 GtkDragDestInfo *info;
191 GtkDragDestSite *site;
193 dragging_info = gdk_quartz_drag_context_get_dragging_info_libgtk_only (context);
194 pasteboard = [dragging_info draggingPasteboard];
196 info = gtk_drag_get_dest_info (context, FALSE);
197 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
199 selection_data = _gtk_quartz_get_selection_data_from_pasteboard (pasteboard,
202 if (site && site->target_list)
206 if (gtk_target_list_find (site->target_list,
207 selection_data->target,
210 if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
211 selection_data->length >= 0)
212 g_signal_emit_by_name (widget,
213 "drag-data-received",
214 context, info->drop_x, info->drop_y,
221 g_signal_emit_by_name (widget,
222 "drag-data-received",
223 context, info->drop_x, info->drop_y,
228 if (site && site->flags & GTK_DEST_DEFAULT_DROP)
230 gtk_drag_finish (context,
231 (selection_data->length >= 0),
232 (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
238 gtk_drag_finish (GdkDragContext *context,
243 GtkDragSourceInfo *info;
244 GdkDragContext* source_context = gdk_quartz_drag_source_context ();
248 info = gtk_drag_get_source_info (source_context, FALSE);
251 info->success = success;
258 gtk_drag_dest_info_destroy (gpointer data)
260 GtkDragDestInfo *info = data;
265 static GtkDragDestInfo *
266 gtk_drag_get_dest_info (GdkDragContext *context,
269 GtkDragDestInfo *info;
270 static GQuark info_quark = 0;
272 info_quark = g_quark_from_static_string ("gtk-dest-info");
274 info = g_object_get_qdata (G_OBJECT (context), info_quark);
277 info = g_new (GtkDragDestInfo, 1);
279 info->context = context;
280 info->dropped = FALSE;
281 g_object_set_qdata_full (G_OBJECT (context), info_quark,
282 info, gtk_drag_dest_info_destroy);
288 static GQuark dest_info_quark = 0;
290 static GtkDragSourceInfo *
291 gtk_drag_get_source_info (GdkDragContext *context,
294 GtkDragSourceInfo *info;
296 if (!dest_info_quark)
297 dest_info_quark = g_quark_from_static_string ("gtk-source-info");
299 info = g_object_get_qdata (G_OBJECT (context), dest_info_quark);
302 info = g_new0 (GtkDragSourceInfo, 1);
303 info->context = context;
304 g_object_set_qdata (G_OBJECT (context), dest_info_quark, info);
311 gtk_drag_clear_source_info (GdkDragContext *context)
313 g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL);
317 gtk_drag_get_source_widget (GdkDragContext *context)
319 GtkDragSourceInfo *info;
320 GdkDragContext* real_source_context = gdk_quartz_drag_source_context();
322 if (!real_source_context)
325 info = gtk_drag_get_source_info (real_source_context, FALSE);
329 return info->source_widget;
332 /*************************************************************
333 * gtk_drag_highlight_draw:
334 * Callback for expose_event for highlighted widgets.
340 *************************************************************/
343 gtk_drag_highlight_draw (GtkWidget *widget,
347 int width = gtk_widget_get_allocated_width (widget);
348 int height = gtk_widget_get_allocated_height (widget);
349 GtkStyleContext *context = gtk_widget_get_style_context (widget);
351 gtk_style_context_save (context);
352 gtk_style_context_add_class (context, GTK_STYLE_CLASS_DND);
354 gtk_render_frame (context, cr, 0, 0, width, height);
356 gtk_style_context_restore (context);
358 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
359 cairo_set_line_width (cr, 1.0);
362 width - 1, height - 1);
368 /*************************************************************
369 * gtk_drag_highlight:
370 * Highlight the given widget in the default manner.
374 *************************************************************/
377 gtk_drag_highlight (GtkWidget *widget)
379 g_return_if_fail (GTK_IS_WIDGET (widget));
381 g_signal_connect_after (widget, "draw",
382 G_CALLBACK (gtk_drag_highlight_draw),
385 gtk_widget_queue_draw (widget);
388 /*************************************************************
389 * gtk_drag_unhighlight:
390 * Refresh the given widget to remove the highlight.
394 *************************************************************/
397 gtk_drag_unhighlight (GtkWidget *widget)
399 g_return_if_fail (GTK_IS_WIDGET (widget));
401 g_signal_handlers_disconnect_by_func (widget,
402 gtk_drag_highlight_draw,
405 gtk_widget_queue_draw (widget);
409 get_toplevel_nswindow (GtkWidget *widget)
411 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
412 GdkWindow *window = gtk_widget_get_window (toplevel);
414 if (gtk_widget_is_toplevel (toplevel) && window)
415 return [gdk_quartz_window_get_nsview (window) window];
421 register_types (GtkWidget *widget, GtkDragDestSite *site)
423 if (site->target_list)
425 NSWindow *nswindow = get_toplevel_nswindow (widget);
427 NSAutoreleasePool *pool;
432 pool = [[NSAutoreleasePool alloc] init];
433 types = _gtk_quartz_target_list_to_pasteboard_types (site->target_list);
435 [nswindow registerForDraggedTypes:[types allObjects]];
443 gtk_drag_dest_realized (GtkWidget *widget,
446 GtkDragDestSite *site = user_data;
448 register_types (widget, site);
452 gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
453 GtkWidget *previous_toplevel,
456 GtkDragDestSite *site = user_data;
458 register_types (widget, site);
462 gtk_drag_dest_site_destroy (gpointer data)
464 GtkDragDestSite *site = data;
466 if (site->target_list)
467 gtk_target_list_unref (site->target_list);
473 gtk_drag_dest_set (GtkWidget *widget,
474 GtkDestDefaults flags,
475 const GtkTargetEntry *targets,
477 GdkDragAction actions)
479 GtkDragDestSite *old_site, *site;
481 g_return_if_fail (GTK_IS_WIDGET (widget));
483 old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
485 site = g_new (GtkDragDestSite, 1);
487 site->have_drag = FALSE;
489 site->target_list = gtk_target_list_new (targets, n_targets);
491 site->target_list = NULL;
492 site->actions = actions;
495 site->track_motion = old_site->track_motion;
497 site->track_motion = FALSE;
499 gtk_drag_dest_unset (widget);
501 if (gtk_widget_get_realized (widget))
502 gtk_drag_dest_realized (widget, site);
504 g_signal_connect (widget, "realize",
505 G_CALLBACK (gtk_drag_dest_realized), site);
506 g_signal_connect (widget, "hierarchy-changed",
507 G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
509 g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
510 site, gtk_drag_dest_site_destroy);
514 gtk_drag_dest_set_proxy (GtkWidget *widget,
515 GdkWindow *proxy_window,
516 GdkDragProtocol protocol,
517 gboolean use_coordinates)
519 g_warning ("gtk_drag_dest_set_proxy is not supported on Mac OS X.");
523 gtk_drag_dest_unset (GtkWidget *widget)
525 GtkDragDestSite *old_site;
527 g_return_if_fail (GTK_IS_WIDGET (widget));
529 old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
532 g_signal_handlers_disconnect_by_func (widget,
533 gtk_drag_dest_realized,
535 g_signal_handlers_disconnect_by_func (widget,
536 gtk_drag_dest_hierarchy_changed,
540 g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
544 gtk_drag_dest_get_target_list (GtkWidget *widget)
546 GtkDragDestSite *site;
548 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
550 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
552 return site ? site->target_list : NULL;
556 gtk_drag_dest_set_target_list (GtkWidget *widget,
557 GtkTargetList *target_list)
559 GtkDragDestSite *site;
561 g_return_if_fail (GTK_IS_WIDGET (widget));
563 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
567 g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
568 "to make the widget into a drag destination");
573 gtk_target_list_ref (target_list);
575 if (site->target_list)
576 gtk_target_list_unref (site->target_list);
578 site->target_list = target_list;
580 register_types (widget, site);
584 gtk_drag_dest_add_text_targets (GtkWidget *widget)
586 GtkTargetList *target_list;
588 target_list = gtk_drag_dest_get_target_list (widget);
590 gtk_target_list_ref (target_list);
592 target_list = gtk_target_list_new (NULL, 0);
593 gtk_target_list_add_text_targets (target_list, 0);
594 gtk_drag_dest_set_target_list (widget, target_list);
595 gtk_target_list_unref (target_list);
599 gtk_drag_dest_add_image_targets (GtkWidget *widget)
601 GtkTargetList *target_list;
603 target_list = gtk_drag_dest_get_target_list (widget);
605 gtk_target_list_ref (target_list);
607 target_list = gtk_target_list_new (NULL, 0);
608 gtk_target_list_add_image_targets (target_list, 0, FALSE);
609 gtk_drag_dest_set_target_list (widget, target_list);
610 gtk_target_list_unref (target_list);
614 gtk_drag_dest_add_uri_targets (GtkWidget *widget)
616 GtkTargetList *target_list;
618 target_list = gtk_drag_dest_get_target_list (widget);
620 gtk_target_list_ref (target_list);
622 target_list = gtk_target_list_new (NULL, 0);
623 gtk_target_list_add_uri_targets (target_list, 0);
624 gtk_drag_dest_set_target_list (widget, target_list);
625 gtk_target_list_unref (target_list);
629 prepend_and_ref_widget (GtkWidget *widget,
632 GSList **slist_p = data;
634 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
638 gtk_drag_find_widget (GtkWidget *widget,
639 GtkDragFindData *data)
641 GtkAllocation new_allocation;
642 gint allocation_to_window_x = 0;
643 gint allocation_to_window_y = 0;
647 if (data->found || !gtk_widget_get_mapped (widget) || !gtk_widget_get_sensitive (widget))
650 /* Note that in the following code, we only count the
651 * position as being inside a WINDOW widget if it is inside
652 * widget->window; points that are outside of widget->window
653 * but within the allocation are not counted. This is consistent
654 * with the way we highlight drag targets.
656 * data->x,y are relative to widget->parent->window (if
657 * widget is not a toplevel, widget->window otherwise).
658 * We compute the allocation of widget in the same coordinates,
659 * clipping to widget->window, and all intermediate
660 * windows. If data->x,y is inside that, then we translate
661 * our coordinates to be relative to widget->window and
664 gtk_widget_get_allocation (widget, &new_allocation);
666 if (gtk_widget_get_parent (widget))
669 GdkWindow *window = gtk_widget_get_window (widget);
670 GdkWindow *parent_window;
671 GtkAllocation allocation;
673 parent_window = gtk_widget_get_window (gtk_widget_get_parent (widget));
675 /* Compute the offset from allocation-relative to
676 * window-relative coordinates.
678 gtk_widget_get_allocation (widget, &allocation);
679 allocation_to_window_x = allocation.x;
680 allocation_to_window_y = allocation.y;
682 if (gtk_widget_get_has_window (widget))
684 /* The allocation is relative to the parent window for
685 * window widgets, not to widget->window.
687 gdk_window_get_position (window, &tx, &ty);
689 allocation_to_window_x -= tx;
690 allocation_to_window_y -= ty;
693 new_allocation.x = 0 + allocation_to_window_x;
694 new_allocation.y = 0 + allocation_to_window_y;
696 while (window && window != parent_window)
698 GdkRectangle window_rect = { 0, 0, 0, 0 };
700 window_rect.width = gdk_window_get_width (window);
701 window_rect.height = gdk_window_get_height (window);
703 gdk_rectangle_intersect (&new_allocation, &window_rect, &new_allocation);
705 gdk_window_get_position (window, &tx, &ty);
706 new_allocation.x += tx;
708 new_allocation.y += ty;
711 window = gdk_window_get_parent (window);
714 if (!window) /* Window and widget heirarchies didn't match. */
718 if (data->toplevel ||
719 ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
720 (data->x < new_allocation.x + new_allocation.width) &&
721 (data->y < new_allocation.y + new_allocation.height)))
723 /* First, check if the drag is in a valid drop site in
724 * one of our children
726 if (GTK_IS_CONTAINER (widget))
728 GtkDragFindData new_data = *data;
729 GSList *children = NULL;
732 new_data.x -= x_offset;
733 new_data.y -= y_offset;
734 new_data.found = FALSE;
735 new_data.toplevel = FALSE;
737 /* need to reference children temporarily in case the
738 * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
740 gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
741 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
743 if (!new_data.found && gtk_widget_is_drawable (tmp_list->data))
744 gtk_drag_find_widget (tmp_list->data, &new_data);
745 g_object_unref (tmp_list->data);
747 g_slist_free (children);
749 data->found = new_data.found;
752 /* If not, and this widget is registered as a drop site, check to
753 * emit "drag-motion" to check if we are actually in
757 g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
759 data->found = data->callback (widget,
761 data->x - x_offset - allocation_to_window_x,
762 data->y - y_offset - allocation_to_window_y,
764 /* If so, send a "drag-leave" to the last widget */
767 if (data->info->widget && data->info->widget != widget)
769 gtk_drag_dest_leave (data->info->widget, data->context, data->time);
771 data->info->widget = widget;
778 gtk_drag_dest_leave (GtkWidget *widget,
779 GdkDragContext *context,
782 GtkDragDestSite *site;
784 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
785 g_return_if_fail (site != NULL);
787 if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
788 gtk_drag_unhighlight (widget);
790 if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
792 g_signal_emit_by_name (widget, "drag-leave", context, time);
794 site->have_drag = FALSE;
798 gtk_drag_dest_motion (GtkWidget *widget,
799 GdkDragContext *context,
804 GtkDragDestSite *site;
805 GdkDragAction action = 0;
808 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
809 g_return_val_if_fail (site != NULL, FALSE);
811 if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
813 if (gdk_drag_context_get_suggested_action (context) & site->actions)
814 action = gdk_drag_context_get_suggested_action (context);
816 if (action && gtk_drag_dest_find_target (widget, context, NULL))
818 if (!site->have_drag)
820 site->have_drag = TRUE;
821 if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
822 gtk_drag_highlight (widget);
825 gdk_drag_status (context, action, time);
829 gdk_drag_status (context, 0, time);
830 if (!site->track_motion)
835 g_signal_emit_by_name (widget, "drag-motion",
836 context, x, y, time, &retval);
838 return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
842 gtk_drag_dest_drop (GtkWidget *widget,
843 GdkDragContext *context,
848 GtkDragDestSite *site;
849 GtkDragDestInfo *info;
852 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
853 g_return_val_if_fail (site != NULL, FALSE);
855 info = gtk_drag_get_dest_info (context, FALSE);
856 g_return_val_if_fail (info != NULL, FALSE);
861 if (site->flags & GTK_DEST_DEFAULT_DROP)
863 GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
865 if (target == GDK_NONE)
867 gtk_drag_finish (context, FALSE, FALSE, time);
871 gtk_drag_get_data (widget, context, target, time);
874 g_signal_emit_by_name (widget, "drag-drop",
875 context, x, y, time, &retval);
877 return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
881 gtk_drag_dest_set_track_motion (GtkWidget *widget,
882 gboolean track_motion)
884 GtkDragDestSite *site;
886 g_return_if_fail (GTK_IS_WIDGET (widget));
888 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
890 g_return_if_fail (site != NULL);
892 site->track_motion = track_motion != FALSE;
896 gtk_drag_dest_get_track_motion (GtkWidget *widget)
898 GtkDragDestSite *site;
900 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
902 site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
905 return site->track_motion;
911 _gtk_drag_dest_handle_event (GtkWidget *toplevel,
914 GtkDragDestInfo *info;
915 GdkDragContext *context;
917 g_return_if_fail (toplevel != NULL);
918 g_return_if_fail (event != NULL);
920 context = event->dnd.context;
922 info = gtk_drag_get_dest_info (context, TRUE);
924 /* Find the widget for the event */
933 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
938 case GDK_DRAG_MOTION:
941 GtkDragFindData data;
944 if (event->type == GDK_DROP_START)
946 info->dropped = TRUE;
947 /* We send a leave here so that the widget unhighlights
952 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
957 gdk_window_get_position (gtk_widget_get_window (toplevel), &tx, &ty);
959 data.x = event->dnd.x_root - tx;
960 data.y = event->dnd.y_root - ty;
961 data.context = context;
964 data.toplevel = TRUE;
965 data.callback = (event->type == GDK_DRAG_MOTION) ?
966 gtk_drag_dest_motion : gtk_drag_dest_drop;
967 data.time = event->dnd.time;
969 gtk_drag_find_widget (toplevel, &data);
971 if (info->widget && !data.found)
973 gtk_drag_dest_leave (info->widget, context, event->dnd.time);
979 if (event->type == GDK_DRAG_MOTION)
982 gdk_drag_status (context, 0, event->dnd.time);
987 g_assert_not_reached ();
994 gtk_drag_dest_find_target (GtkWidget *widget,
995 GdkDragContext *context,
996 GtkTargetList *target_list)
998 id <NSDraggingInfo> dragging_info;
999 NSPasteboard *pasteboard;
1000 GtkWidget *source_widget;
1002 GList *tmp_source = NULL;
1003 GList *source_targets;
1005 g_return_val_if_fail (GTK_IS_WIDGET (widget), GDK_NONE);
1006 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), GDK_NONE);
1008 dragging_info = gdk_quartz_drag_context_get_dragging_info_libgtk_only (context);
1009 pasteboard = [dragging_info draggingPasteboard];
1011 source_widget = gtk_drag_get_source_widget (context);
1013 if (target_list == NULL)
1014 target_list = gtk_drag_dest_get_target_list (widget);
1016 if (target_list == NULL)
1019 source_targets = _gtk_quartz_pasteboard_types_to_atom_list ([pasteboard types]);
1020 tmp_target = target_list->list;
1023 GtkTargetPair *pair = tmp_target->data;
1024 tmp_source = source_targets;
1027 if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1029 if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1030 (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1032 g_list_free (source_targets);
1033 return pair->target;
1038 tmp_source = tmp_source->next;
1040 tmp_target = tmp_target->next;
1043 g_list_free (source_targets);
1048 gtk_drag_begin_idle (gpointer arg)
1050 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1051 GdkDragContext* context = (GdkDragContext*) arg;
1052 GtkDragSourceInfo* info = gtk_drag_get_source_info (context, FALSE);
1054 NSPasteboard *pasteboard;
1055 GtkDragSourceOwner *owner;
1058 NSImage *drag_image;
1060 g_assert (info != NULL);
1062 pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1063 owner = [[GtkDragSourceOwner alloc] initWithInfo:info];
1065 types = _gtk_quartz_target_list_to_pasteboard_types (info->target_list);
1067 [pasteboard declareTypes:[types allObjects] owner:owner];
1072 if ((nswindow = get_toplevel_nswindow (info->source_widget)) == NULL)
1073 return G_SOURCE_REMOVE;
1075 /* Ref the context. It's unreffed when the drag has been aborted */
1076 g_object_ref (info->context);
1078 /* FIXME: If the event isn't a mouse event, use the global cursor position instead */
1079 point = [info->nsevent locationInWindow];
1081 drag_image = _gtk_quartz_create_image_from_pixbuf (info->icon_pixbuf);
1082 if (drag_image == NULL)
1084 g_object_unref (info->context);
1085 return G_SOURCE_REMOVE;
1088 point.x -= info->hot_x;
1089 point.y -= info->hot_y;
1091 [nswindow dragImage:drag_image
1093 offset:NSMakeSize(0, 0)
1095 pasteboard:pasteboard
1099 [info->nsevent release];
1100 [drag_image release];
1104 return G_SOURCE_REMOVE;
1106 /* Fake protocol to let us call GdkNSView gdkWindow without including
1107 * gdk/GdkNSView.h (which we can't because it pulls in the internal-only
1111 - (GdkWindow *)gdkWindow;
1114 static GdkDragContext *
1115 gtk_drag_begin_internal (GtkWidget *widget,
1116 GtkDragSourceSite *site,
1117 GtkTargetList *target_list,
1118 GdkDragAction actions,
1122 GtkDragSourceInfo *info;
1125 GdkDragContext *context;
1126 NSWindow *nswindow = get_toplevel_nswindow (widget);
1127 NSPoint point = {0, 0};
1129 double time = (double)g_get_real_time ();
1131 NSTimeInterval nstime;
1135 if (gdk_event_get_coords (event, &x, &y))
1140 time = (double)gdk_event_get_time (event);
1143 nstime = [[NSDate dateWithTimeIntervalSince1970: time / 1000] timeIntervalSinceReferenceDate];
1144 nsevent = [NSEvent mouseEventWithType: NSLeftMouseDown
1148 windowNumber: [nswindow windowNumber]
1149 context: [nswindow graphicsContext]
1154 window = [(id<GdkNSView>)[nswindow contentView] gdkWindow];
1155 g_return_val_if_fail (nsevent != NULL, NULL);
1157 context = gdk_drag_begin (window, NULL);
1158 g_return_val_if_fail (context != NULL, NULL);
1160 info = gtk_drag_get_source_info (context, TRUE);
1161 info->nsevent = nsevent;
1162 [info->nsevent retain];
1164 info->source_widget = g_object_ref (widget);
1165 info->widget = g_object_ref (widget);
1166 info->target_list = target_list;
1167 gtk_target_list_ref (target_list);
1169 info->possible_actions = actions;
1171 g_signal_emit_by_name (widget, "drag-begin", info->context);
1173 /* Ensure that we have an icon before we start the drag; the
1174 * application may have set one in ::drag_begin, or it may
1177 if (!info->icon_pixbuf)
1179 if (!site || site->icon_type == GTK_IMAGE_EMPTY)
1180 gtk_drag_set_icon_default (context);
1183 switch (site->icon_type)
1185 case GTK_IMAGE_PIXBUF:
1186 gtk_drag_set_icon_pixbuf (context,
1187 site->icon_data.pixbuf.pixbuf,
1190 case GTK_IMAGE_STOCK:
1191 gtk_drag_set_icon_stock (context,
1192 site->icon_data.stock.stock_id,
1195 case GTK_IMAGE_ICON_NAME:
1196 gtk_drag_set_icon_name (context,
1197 site->icon_data.name.icon_name,
1200 case GTK_IMAGE_EMPTY:
1202 g_assert_not_reached();
1208 /* drag will begin in an idle handler to avoid nested run loops */
1210 g_idle_add_full (G_PRIORITY_HIGH_IDLE, gtk_drag_begin_idle, context, NULL);
1212 pointer = gdk_drag_context_get_device (info->context);
1213 gdk_device_ungrab (pointer, 0);
1219 gtk_drag_begin (GtkWidget *widget,
1220 GtkTargetList *targets,
1221 GdkDragAction actions,
1225 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1226 g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
1227 g_return_val_if_fail (targets != NULL, NULL);
1229 return gtk_drag_begin_internal (widget, NULL, targets,
1230 actions, button, event);
1235 gtk_drag_source_event_cb (GtkWidget *widget,
1239 GtkDragSourceSite *site;
1240 gboolean retval = FALSE;
1241 site = (GtkDragSourceSite *)data;
1243 switch (event->type)
1245 case GDK_BUTTON_PRESS:
1246 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
1248 site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
1249 site->x = event->button.x;
1250 site->y = event->button.y;
1254 case GDK_BUTTON_RELEASE:
1255 if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
1256 site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
1259 case GDK_MOTION_NOTIFY:
1260 if (site->state & event->motion.state & site->start_button_mask)
1262 /* FIXME: This is really broken and can leave us
1268 if (site->state & event->motion.state &
1269 GDK_BUTTON1_MASK << (i - 1))
1273 if (gtk_drag_check_threshold (widget, site->x, site->y,
1274 event->motion.x, event->motion.y))
1277 gtk_drag_begin_internal (widget, site, site->target_list,
1286 default: /* hit for 2/3BUTTON_PRESS */
1294 gtk_drag_source_set (GtkWidget *widget,
1295 GdkModifierType start_button_mask,
1296 const GtkTargetEntry *targets,
1298 GdkDragAction actions)
1300 GtkDragSourceSite *site;
1302 g_return_if_fail (GTK_IS_WIDGET (widget));
1304 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1306 gtk_widget_add_events (widget,
1307 gtk_widget_get_events (widget) |
1308 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1309 GDK_BUTTON_MOTION_MASK);
1313 if (site->target_list)
1314 gtk_target_list_unref (site->target_list);
1318 site = g_new0 (GtkDragSourceSite, 1);
1320 site->icon_type = GTK_IMAGE_EMPTY;
1322 g_signal_connect (widget, "button-press-event",
1323 G_CALLBACK (gtk_drag_source_event_cb),
1325 g_signal_connect (widget, "button-release-event",
1326 G_CALLBACK (gtk_drag_source_event_cb),
1328 g_signal_connect (widget, "motion-notify-event",
1329 G_CALLBACK (gtk_drag_source_event_cb),
1332 g_object_set_data_full (G_OBJECT (widget),
1333 I_("gtk-site-data"),
1334 site, gtk_drag_source_site_destroy);
1337 site->start_button_mask = start_button_mask;
1339 site->target_list = gtk_target_list_new (targets, n_targets);
1341 site->actions = actions;
1344 /*************************************************************
1345 * gtk_drag_source_unset
1346 * Unregister this widget as a drag source.
1350 *************************************************************/
1353 gtk_drag_source_unset (GtkWidget *widget)
1355 GtkDragSourceSite *site;
1357 g_return_if_fail (GTK_IS_WIDGET (widget));
1359 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1363 g_signal_handlers_disconnect_by_func (widget,
1364 gtk_drag_source_event_cb,
1366 g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
1371 gtk_drag_source_get_target_list (GtkWidget *widget)
1373 GtkDragSourceSite *site;
1375 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1377 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1379 return site ? site->target_list : NULL;
1384 gtk_drag_source_set_target_list (GtkWidget *widget,
1385 GtkTargetList *target_list)
1387 GtkDragSourceSite *site;
1389 g_return_if_fail (GTK_IS_WIDGET (widget));
1391 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1394 g_warning ("gtk_drag_source_set_target_list() requires the widget "
1395 "to already be a drag source.");
1400 gtk_target_list_ref (target_list);
1402 if (site->target_list)
1403 gtk_target_list_unref (site->target_list);
1405 site->target_list = target_list;
1409 * gtk_drag_source_add_text_targets:
1410 * @widget: a #GtkWidget that's is a drag source
1412 * Add the text targets supported by #GtkSelection to
1413 * the target list of the drag source. The targets
1414 * are added with @info = 0. If you need another value,
1415 * use gtk_target_list_add_text_targets() and
1416 * gtk_drag_source_set_target_list().
1421 gtk_drag_source_add_text_targets (GtkWidget *widget)
1423 GtkTargetList *target_list;
1425 target_list = gtk_drag_source_get_target_list (widget);
1427 gtk_target_list_ref (target_list);
1429 target_list = gtk_target_list_new (NULL, 0);
1430 gtk_target_list_add_text_targets (target_list, 0);
1431 gtk_drag_source_set_target_list (widget, target_list);
1432 gtk_target_list_unref (target_list);
1436 gtk_drag_source_add_image_targets (GtkWidget *widget)
1438 GtkTargetList *target_list;
1440 target_list = gtk_drag_source_get_target_list (widget);
1442 gtk_target_list_ref (target_list);
1444 target_list = gtk_target_list_new (NULL, 0);
1445 gtk_target_list_add_image_targets (target_list, 0, TRUE);
1446 gtk_drag_source_set_target_list (widget, target_list);
1447 gtk_target_list_unref (target_list);
1451 gtk_drag_source_add_uri_targets (GtkWidget *widget)
1453 GtkTargetList *target_list;
1455 target_list = gtk_drag_source_get_target_list (widget);
1457 gtk_target_list_ref (target_list);
1459 target_list = gtk_target_list_new (NULL, 0);
1460 gtk_target_list_add_uri_targets (target_list, 0);
1461 gtk_drag_source_set_target_list (widget, target_list);
1462 gtk_target_list_unref (target_list);
1466 gtk_drag_source_unset_icon (GtkDragSourceSite *site)
1468 switch (site->icon_type)
1470 case GTK_IMAGE_EMPTY:
1472 case GTK_IMAGE_PIXBUF:
1473 g_object_unref (site->icon_data.pixbuf.pixbuf);
1475 case GTK_IMAGE_STOCK:
1476 g_free (site->icon_data.stock.stock_id);
1478 case GTK_IMAGE_ICON_NAME:
1479 g_free (site->icon_data.name.icon_name);
1482 g_assert_not_reached();
1485 site->icon_type = GTK_IMAGE_EMPTY;
1489 gtk_drag_source_site_destroy (gpointer data)
1491 GtkDragSourceSite *site = data;
1493 if (site->target_list)
1494 gtk_target_list_unref (site->target_list);
1496 gtk_drag_source_unset_icon (site);
1501 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
1504 GtkDragSourceSite *site;
1506 g_return_if_fail (GTK_IS_WIDGET (widget));
1507 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1509 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1510 g_return_if_fail (site != NULL);
1511 g_object_ref (pixbuf);
1513 gtk_drag_source_unset_icon (site);
1515 site->icon_type = GTK_IMAGE_PIXBUF;
1516 site->icon_data.pixbuf.pixbuf = pixbuf;
1520 * gtk_drag_source_set_icon_stock:
1521 * @widget: a #GtkWidget
1522 * @stock_id: the ID of the stock icon to use
1524 * Sets the icon that will be used for drags from a particular source
1528 gtk_drag_source_set_icon_stock (GtkWidget *widget,
1529 const gchar *stock_id)
1531 GtkDragSourceSite *site;
1533 g_return_if_fail (GTK_IS_WIDGET (widget));
1534 g_return_if_fail (stock_id != NULL);
1536 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1537 g_return_if_fail (site != NULL);
1539 gtk_drag_source_unset_icon (site);
1541 site->icon_type = GTK_IMAGE_STOCK;
1542 site->icon_data.stock.stock_id = g_strdup (stock_id);
1546 * gtk_drag_source_set_icon_name:
1547 * @widget: a #GtkWidget
1548 * @icon_name: name of icon to use
1550 * Sets the icon that will be used for drags from a particular source
1551 * to a themed icon. See the docs for #GtkIconTheme for more details.
1556 gtk_drag_source_set_icon_name (GtkWidget *widget,
1557 const gchar *icon_name)
1559 GtkDragSourceSite *site;
1561 g_return_if_fail (GTK_IS_WIDGET (widget));
1562 g_return_if_fail (icon_name != NULL);
1564 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
1565 g_return_if_fail (site != NULL);
1567 gtk_drag_source_unset_icon (site);
1569 site->icon_type = GTK_IMAGE_ICON_NAME;
1570 site->icon_data.name.icon_name = g_strdup (icon_name);
1575 * gtk_drag_set_icon_widget:
1576 * @context: the context for a drag. (This must be called
1577 with a context for the source side of a drag)
1578 * @widget: a toplevel window to use as an icon.
1579 * @hot_x: the X offset within @widget of the hotspot.
1580 * @hot_y: the Y offset within @widget of the hotspot.
1582 * Changes the icon for a widget to a given widget. GTK+
1583 * will not destroy the icon, so if you don't want
1584 * it to persist, you should connect to the "drag-end"
1585 * signal and destroy it yourself.
1588 gtk_drag_set_icon_widget (GdkDragContext *context,
1593 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1594 g_return_if_fail (GTK_IS_WIDGET (widget));
1596 g_warning ("gtk_drag_set_icon_widget is not supported on Mac OS X");
1600 set_icon_stock_pixbuf (GdkDragContext *context,
1601 const gchar *stock_id,
1606 GtkDragSourceInfo *info;
1608 info = gtk_drag_get_source_info (context, FALSE);
1612 pixbuf = gtk_widget_render_icon_pixbuf (info->widget, stock_id,
1617 g_warning ("Cannot load drag icon from stock_id %s", stock_id);
1622 g_object_ref (pixbuf);
1624 if (info->icon_pixbuf)
1625 g_object_unref (info->icon_pixbuf);
1626 info->icon_pixbuf = pixbuf;
1627 info->hot_x = hot_x;
1628 info->hot_y = hot_y;
1632 * gtk_drag_set_icon_pixbuf:
1633 * @context: the context for a drag. (This must be called
1634 * with a context for the source side of a drag)
1635 * @pixbuf: the #GdkPixbuf to use as the drag icon.
1636 * @hot_x: the X offset within @widget of the hotspot.
1637 * @hot_y: the Y offset within @widget of the hotspot.
1639 * Sets @pixbuf as the icon for a given drag.
1642 gtk_drag_set_icon_pixbuf (GdkDragContext *context,
1647 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1648 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1650 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
1654 * gtk_drag_set_icon_stock:
1655 * @context: the context for a drag. (This must be called
1656 * with a context for the source side of a drag)
1657 * @stock_id: the ID of the stock icon to use for the drag.
1658 * @hot_x: the X offset within the icon of the hotspot.
1659 * @hot_y: the Y offset within the icon of the hotspot.
1661 * Sets the icon for a given drag from a stock ID.
1664 gtk_drag_set_icon_stock (GdkDragContext *context,
1665 const gchar *stock_id,
1670 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1671 g_return_if_fail (stock_id != NULL);
1673 set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
1677 /* XXX: This function is in gdk, too. Should it be in Cairo? */
1679 _gtk_cairo_surface_extents (cairo_surface_t *surface,
1680 GdkRectangle *extents)
1682 double x1, x2, y1, y2;
1685 g_return_val_if_fail (surface != NULL, FALSE);
1686 g_return_val_if_fail (extents != NULL, FALSE);
1688 cr = cairo_create (surface);
1689 cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
1698 if (x1 < G_MININT || x1 > G_MAXINT ||
1699 y1 < G_MININT || y1 > G_MAXINT ||
1700 x2 > G_MAXINT || y2 > G_MAXINT)
1702 extents->x = extents->y = extents->width = extents->height = 0;
1708 extents->width = x2;
1709 extents->height = y2;
1715 * gtk_drag_set_icon_surface:
1716 * @context: the context for a drag. (This must be called
1717 * with a context for the source side of a drag)
1718 * @surface: the surface to use as icon
1720 * Sets @surface as the icon for a given drag. GTK+ retains
1721 * references for the arguments, and will release them when
1722 * they are no longer needed.
1724 * To position the surface relative to the mouse, use
1725 * cairo_surface_set_device_offset() on @surface. The mouse
1726 * cursor will be positioned at the (0,0) coordinate of the
1730 gtk_drag_set_icon_surface (GdkDragContext *context,
1731 cairo_surface_t *surface)
1734 GdkRectangle extents;
1735 double x_offset, y_offset;
1737 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1738 g_return_if_fail (surface != NULL);
1740 _gtk_cairo_surface_extents (surface, &extents);
1741 cairo_surface_get_device_offset (surface, &x_offset, &y_offset);
1743 pixbuf = gdk_pixbuf_get_from_surface (surface,
1744 extents.x, extents.y,
1745 extents.width, extents.height);
1746 gtk_drag_set_icon_pixbuf (context, pixbuf, -x_offset, -y_offset);
1747 g_object_unref (pixbuf);
1751 * gtk_drag_set_icon_name:
1752 * @context: the context for a drag. (This must be called
1753 * with a context for the source side of a drag)
1754 * @icon_name: name of icon to use
1755 * @hot_x: the X offset of the hotspot within the icon
1756 * @hot_y: the Y offset of the hotspot within the icon
1758 * Sets the icon for a given drag from a named themed icon. See
1759 * the docs for #GtkIconTheme for more details. Note that the
1760 * size of the icon depends on the icon theme (the icon is
1761 * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus
1762 * @hot_x and @hot_y have to be used with care.
1767 gtk_drag_set_icon_name (GdkDragContext *context,
1768 const gchar *icon_name,
1773 GtkSettings *settings;
1774 GtkIconTheme *icon_theme;
1776 gint width, height, icon_size;
1778 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1779 g_return_if_fail (icon_name != NULL);
1781 screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
1782 g_return_if_fail (screen != NULL);
1784 settings = gtk_settings_get_for_screen (screen);
1785 if (gtk_icon_size_lookup_for_settings (settings,
1788 icon_size = MAX (width, height);
1790 icon_size = 32; /* default value for GTK_ICON_SIZE_DND */
1792 icon_theme = gtk_icon_theme_get_for_screen (screen);
1794 pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name,
1795 icon_size, 0, NULL);
1797 set_icon_stock_pixbuf (context, NULL, pixbuf, hot_x, hot_y);
1799 g_warning ("Cannot load drag icon from icon name %s", icon_name);
1803 * gtk_drag_set_icon_default:
1804 * @context: the context for a drag. (This must be called
1805 with a context for the source side of a drag)
1807 * Sets the icon for a particular drag to the default
1811 gtk_drag_set_icon_default (GdkDragContext *context)
1813 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1815 gtk_drag_set_icon_stock (context, GTK_STOCK_DND, -2, -2);
1819 gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
1821 NSPasteboard *pasteboard;
1822 NSAutoreleasePool *pool;
1824 if (info->icon_pixbuf)
1825 g_object_unref (info->icon_pixbuf);
1827 g_signal_emit_by_name (info->widget, "drag-end",
1830 if (info->source_widget)
1831 g_object_unref (info->source_widget);
1834 g_object_unref (info->widget);
1836 gtk_target_list_unref (info->target_list);
1838 pool = [[NSAutoreleasePool alloc] init];
1840 /* Empty the pasteboard, so that it will not accidentally access
1841 * info->context after it has been destroyed.
1843 pasteboard = [NSPasteboard pasteboardWithName: NSDragPboard];
1844 [pasteboard declareTypes: nil owner: nil];
1848 gtk_drag_clear_source_info (info->context);
1849 g_object_unref (info->context);
1856 drag_drop_finished_idle_cb (gpointer data)
1858 gtk_drag_source_info_destroy (data);
1859 return G_SOURCE_REMOVE;
1863 gtk_drag_drop_finished (GtkDragSourceInfo *info)
1865 if (info->success && info->delete)
1866 g_signal_emit_by_name (info->source_widget, "drag-data-delete",
1869 /* Workaround for the fact that the NS API blocks until the drag is
1870 * over. This way the context is still valid when returning from
1871 * drag_begin, even if it will still be quite useless. See bug #501588.
1873 g_idle_add (drag_drop_finished_idle_cb, info);
1876 /*************************************************************
1877 * _gtk_drag_source_handle_event:
1878 * Called from widget event handling code on Drag events
1882 * toplevel: Toplevel widget that received the event
1885 *************************************************************/
1888 _gtk_drag_source_handle_event (GtkWidget *widget,
1891 GtkDragSourceInfo *info;
1892 GdkDragContext *context;
1894 g_return_if_fail (widget != NULL);
1895 g_return_if_fail (event != NULL);
1897 context = event->dnd.context;
1898 info = gtk_drag_get_source_info (context, FALSE);
1902 switch (event->type)
1904 case GDK_DROP_FINISHED:
1905 gtk_drag_drop_finished (info);
1908 g_assert_not_reached ();
1914 gtk_drag_check_threshold (GtkWidget *widget,
1920 gint drag_threshold;
1922 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
1924 g_object_get (gtk_widget_get_settings (widget),
1925 "gtk-dnd-drag-threshold", &drag_threshold,
1928 return (ABS (current_x - start_x) > drag_threshold ||
1929 ABS (current_y - start_y) > drag_threshold);