/* GTK - The GIMP Toolkit
* Copyright ( C ) 1995 - 1999 Peter Mattis , Spencer Kimball and Josh MacDonald
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see < http : //www.gnu.org/licenses/>.
*/
/*
* Modified by the GTK + Team and others 1997 - 2000 . See the AUTHORS
* file for a list of people on the GTK + Team . See the ChangeLog
* files for a list of changes . These files are distributed with
* GTK + at ftp : //ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
#include "gtkdragsourceprivate.h"
#include "gtkgesturedrag.h"
#include "gtkgesturesingleprivate.h"
#include "gtkimagedefinitionprivate.h"
#include "gtknative.h"
#include "gtkwidgetprivate.h"
#include "gtkimageprivate.h"
#include "gtkdragicon.h"
#include "gtkprivate.h"
#include "gtkmarshalers.h"
#include "gtkpicture.h"
#include "gtksettingsprivate.h"
#include "gtkgesturesingle.h"
#define MIN_TIME_TO_DND 100
/**
* GtkDragSource :
*
* An event controller to initiate Drag - And - Drop operations .
*
* ` GtkDragSource ` can be set up with the necessary
* ingredients for a DND operation ahead of time . This includes
* the source for the data that is being transferred , in the form
* of a [ class @ Gdk . ContentProvider ] , the desired action , and the icon to
* use during the drag operation . After setting it up , the drag
* source must be added to a widget as an event controller , using
* [ method @ Gtk . Widget . add_controller ] .
*
* ` ` ` c
* static void
* my_widget_init ( MyWidget * self )
* {
* GtkDragSource * drag_source = gtk_drag_source_new ( ) ;
*
* g_signal_connect ( drag_source , " prepare " , G_CALLBACK ( on_drag_prepare ) , self ) ;
* g_signal_connect ( drag_source , " drag - begin " , G_CALLBACK ( on_drag_begin ) , self ) ;
*
* gtk_widget_add_controller ( GTK_WIDGET ( self ) , GTK_EVENT_CONTROLLER ( drag_source ) ) ;
* }
* ` ` `
*
* Setting up the content provider and icon ahead of time only makes
* sense when the data does not change . More commonly , you will want
* to set them up just in time . To do so , ` GtkDragSource ` has
* [ signal @ Gtk . DragSource : : prepare ] and [ signal @ Gtk . DragSource : : drag - begin ]
* signals .
*
* The : : prepare signal is emitted before a drag is started , and
* can be used to set the content provider and actions that the
* drag should be started with .
*
* ` ` ` c
* static GdkContentProvider *
* on_drag_prepare ( GtkDragSource * source ,
* double x ,
* double y ,
* MyWidget * self )
* {
* // This widget supports two types of content: GFile objects
* // and GdkPixbuf objects; GTK will handle the serialization
* // of these types automatically
* GFile * file = my_widget_get_file ( self ) ;
* GdkPixbuf * pixbuf = my_widget_get_pixbuf ( self ) ;
*
* return gdk_content_provider_new_union ( ( GdkContentProvider * [ 2 ] ) {
* gdk_content_provider_new_typed ( G_TYPE_FILE , file ) ,
* gdk_content_provider_new_typed ( GDK_TYPE_PIXBUF , pixbuf ) ,
* } , 2 ) ;
* }
* ` ` `
*
* The : : drag - begin signal is emitted after the ` GdkDrag ` object has
* been created , and can be used to set up the drag icon .
*
* ` ` ` c
* static void
* on_drag_begin ( GtkDragSource * source ,
* GdkDrag * drag ,
* MyWidget * self )
* {
* // Set the widget as the drag icon
* GdkPaintable * paintable = gtk_widget_paintable_new ( GTK_WIDGET ( self ) ) ;
* gtk_drag_source_set_icon ( source , paintable , 0 , 0 ) ;
* g_object_unref ( paintable ) ;
* }
* ` ` `
*
* During the DND operation , ` GtkDragSource ` emits signals that
* can be used to obtain updates about the status of the operation ,
* but it is not normally necessary to connect to any signals ,
* except for one case : when the supported actions include
* % GDK_ACTION_MOVE , you need to listen for the
* [ signal @ Gtk . DragSource : : drag - end ] signal and delete the
* data after it has been transferred .
*/
struct _GtkDragSource
{
GtkGestureSingle parent_instance;
GdkContentProvider *content;
GdkDragAction actions;
GdkPaintable *paintable;
int hot_x;
int hot_y;
double start_x;
double start_y;
guint timeout_id;
GdkDrag *drag;
};
struct _GtkDragSourceClass
{
GtkGestureSingleClass parent_class;
GdkContentProvider *(* prepare) (GtkDragSource *source,
double x,
double y);
};
enum {
PROP_CONTENT = 1 ,
PROP_ACTIONS,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
enum {
PREPARE,
DRAG_BEGIN,
DRAG_END,
DRAG_CANCEL,
NUM_SIGNALS
};
static guint signals[NUM_SIGNALS];
static void gtk_drag_source_dnd_finished_cb (GdkDrag *drag,
GtkDragSource *source);
static void gtk_drag_source_cancel_cb (GdkDrag *drag,
GdkDragCancelReason reason,
GtkDragSource *source);
static GdkContentProvider *gtk_drag_source_prepare (GtkDragSource *source,
double x,
double y);
static void gtk_drag_source_drag_begin (GtkDragSource *source);
G_DEFINE_TYPE (GtkDragSource, gtk_drag_source, GTK_TYPE_GESTURE_SINGLE);
static void
gtk_drag_source_init (GtkDragSource *source)
{
source->actions = GDK_ACTION_COPY;
}
static void
gtk_drag_source_finalize (GObject *object)
{
GtkDragSource *source = GTK_DRAG_SOURCE (object);
g_clear_object (&source->content);
g_clear_object (&source->paintable);
g_clear_handle_id (&source->timeout_id, g_source_remove);
G_OBJECT_CLASS (gtk_drag_source_parent_class)->finalize (object);
}
static void
gtk_drag_source_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkDragSource *source = GTK_DRAG_SOURCE (object);
switch (prop_id)
{
case PROP_CONTENT:
gtk_drag_source_set_content (source, g_value_get_object (value));
break ;
case PROP_ACTIONS:
gtk_drag_source_set_actions (source, g_value_get_flags (value));
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_drag_source_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkDragSource *source = GTK_DRAG_SOURCE (object);
switch (prop_id)
{
case PROP_CONTENT:
g_value_set_object (value, gtk_drag_source_get_content (source));
break ;
case PROP_ACTIONS:
g_value_set_flags (value, gtk_drag_source_get_actions (source));
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static gboolean
gtk_drag_source_filter_event (GtkEventController *controller,
GdkEvent *event)
{
/* Let touchpad swipe events go through, only if they match n-points */
if (gdk_event_get_event_type (event) == GDK_TOUCHPAD_SWIPE)
{
guint n_points;
guint n_fingers;
g_object_get (G_OBJECT (controller), "n-points" , &n_points, NULL);
n_fingers = gdk_touchpad_event_get_n_fingers (event);
if (n_fingers == n_points)
return FALSE ;
else
return TRUE ;
}
return GTK_EVENT_CONTROLLER_CLASS (gtk_drag_source_parent_class)->filter_event (controller, event);
}
static gboolean
drag_timeout (gpointer user_data)
{
GtkDragSource *source = user_data;
source->timeout_id = 0 ;
return G_SOURCE_REMOVE;
}
static void
gtk_drag_source_begin (GtkGesture *gesture,
GdkEventSequence *sequence)
{
GtkDragSource *source = GTK_DRAG_SOURCE (gesture);
GdkEventSequence *current;
current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
g_clear_handle_id (&source->timeout_id, g_source_remove);
source->timeout_id = g_timeout_add (MIN_TIME_TO_DND, drag_timeout, source);
gtk_gesture_get_point (gesture, current, &source->start_x, &source->start_y);
}
static void
gtk_drag_source_update (GtkGesture *gesture,
GdkEventSequence *sequence)
{
GtkDragSource *source = GTK_DRAG_SOURCE (gesture);
GtkWidget *widget;
double x, y;
if (!gtk_gesture_is_recognized (gesture))
return ;
gtk_gesture_get_point (gesture, sequence, &x, &y);
widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
if (gtk_drag_check_threshold_double (widget, source->start_x, source->start_y, x, y) &&
!source->timeout_id)
{
gtk_drag_source_drag_begin (source);
}
}
static void
gtk_drag_source_class_init (GtkDragSourceClass *class )
{
GObjectClass *object_class = G_OBJECT_CLASS (class );
GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class );
GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (class );
object_class->finalize = gtk_drag_source_finalize;
object_class->set_property = gtk_drag_source_set_property;
object_class->get_property = gtk_drag_source_get_property;
controller_class->filter_event = gtk_drag_source_filter_event;
gesture_class->begin = gtk_drag_source_begin;
gesture_class->update = gtk_drag_source_update;
gesture_class->end = NULL;
class ->prepare = gtk_drag_source_prepare;
/**
* GtkDragSource : content :
*
* The data that is offered by drag operations from this source .
*/
properties[PROP_CONTENT] =
g_param_spec_object ("content" , NULL, NULL,
GDK_TYPE_CONTENT_PROVIDER,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDragSource : actions :
*
* The actions that are supported by drag operations from the source .
*
* Note that you must handle the [ signal @ Gtk . DragSource : : drag - end ] signal
* if the actions include % GDK_ACTION_MOVE .
*/
properties[PROP_ACTIONS] =
g_param_spec_flags ("actions" , NULL, NULL,
GDK_TYPE_DRAG_ACTION, GDK_ACTION_COPY,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
/**
* GtkDragSource : : prepare :
* @ source : the ` GtkDragSource `
* @ x : the X coordinate of the drag starting point
* @ y : the Y coordinate of the drag starting point
*
* Emitted when a drag is about to be initiated .
*
* It returns the ` GdkContentProvider ` to use for the drag that is about
* to start . The default handler for this signal returns the value of
* the [ property @ Gtk . DragSource : content ] property , so if you set up that
* property ahead of time , you don ' t need to connect to this signal .
*
* Returns : ( transfer full ) ( nullable ) : a ` GdkContentProvider `
*/
signals[PREPARE] =
g_signal_new (I_("prepare" ),
G_TYPE_FROM_CLASS (class ),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDragSourceClass, prepare),
g_signal_accumulator_first_wins, NULL,
_gtk_marshal_OBJECT__DOUBLE_DOUBLE,
GDK_TYPE_CONTENT_PROVIDER, 2 ,
G_TYPE_DOUBLE, G_TYPE_DOUBLE);
g_signal_set_va_marshaller (signals[PREPARE],
GTK_TYPE_DRAG_SOURCE,
_gtk_marshal_OBJECT__DOUBLE_DOUBLEv);
/**
* GtkDragSource : : drag - begin :
* @ source : the ` GtkDragSource `
* @ drag : the ` GdkDrag ` object
*
* Emitted on the drag source when a drag is started .
*
* It can be used to e . g . set a custom drag icon with
* [ method @ Gtk . DragSource . set_icon ] .
*/
signals[DRAG_BEGIN] =
g_signal_new (I_("drag-begin" ),
G_TYPE_FROM_CLASS (class ),
G_SIGNAL_RUN_LAST,
0 ,
NULL, NULL,
NULL,
G_TYPE_NONE, 1 ,
GDK_TYPE_DRAG);
/**
* GtkDragSource : : drag - end :
* @ source : the ` GtkDragSource `
* @ drag : the ` GdkDrag ` object
* @ delete_data : % TRUE if the drag was performing % GDK_ACTION_MOVE ,
* and the data should be deleted
*
* Emitted on the drag source when a drag is finished .
*
* A typical reason to connect to this signal is to undo
* things done in [ signal @ Gtk . DragSource : : prepare ] or
* [ signal @ Gtk . DragSource : : drag - begin ] handlers .
*/
signals[DRAG_END] =
g_signal_new (I_("drag-end" ),
G_TYPE_FROM_CLASS (class ),
G_SIGNAL_RUN_LAST,
0 ,
NULL, NULL,
_gtk_marshal_VOID__OBJECT_BOOLEAN,
G_TYPE_NONE, 2 ,
GDK_TYPE_DRAG,
G_TYPE_BOOLEAN);
g_signal_set_va_marshaller (signals[DRAG_END],
GTK_TYPE_DRAG_SOURCE,
_gtk_marshal_VOID__OBJECT_BOOLEANv);
/**
* GtkDragSource : : drag - cancel :
* @ source : the ` GtkDragSource `
* @ drag : the ` GdkDrag ` object
* @ reason : information on why the drag failed
*
* Emitted on the drag source when a drag has failed .
*
* The signal handler may handle a failed drag operation based on
* the type of error . It should return % TRUE if the failure has been handled
* and the default " drag operation failed " animation should not be shown .
*
* Returns : % TRUE if the failed drag operation has been already handled
*/
signals[DRAG_CANCEL] =
g_signal_new (I_("drag-cancel" ),
G_TYPE_FROM_CLASS (class ),
G_SIGNAL_RUN_LAST,
0 ,
_gtk_boolean_handled_accumulator, NULL,
_gtk_marshal_BOOLEAN__OBJECT_ENUM,
G_TYPE_BOOLEAN, 2 ,
GDK_TYPE_DRAG,
GDK_TYPE_DRAG_CANCEL_REASON);
g_signal_set_va_marshaller (signals[DRAG_CANCEL],
G_TYPE_FROM_CLASS (class ),
_gtk_marshal_BOOLEAN__OBJECT_ENUMv);
}
static GdkContentProvider *
gtk_drag_source_prepare (GtkDragSource *source,
double x,
double y)
{
if (source->actions == 0 )
return NULL;
if (source->content == NULL)
return NULL;
return g_object_ref (source->content);
}
static void
drag_end (GtkDragSource *source,
gboolean success)
{
gboolean delete_data;
g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_dnd_finished_cb, source);
g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_cancel_cb, source);
delete_data = success && gdk_drag_get_selected_action (source->drag) == GDK_ACTION_MOVE;
g_signal_emit (source, signals[DRAG_END], 0 , source->drag, delete_data);
gdk_drag_drop_done (source->drag, success);
g_clear_object (&source->drag);
g_clear_handle_id (&source->timeout_id, g_source_remove);
g_object_unref (source);
}
static void
gtk_drag_source_dnd_finished_cb (GdkDrag *drag,
GtkDragSource *source)
{
drag_end (source, TRUE );
}
static void
gtk_drag_source_cancel_cb (GdkDrag *drag,
GdkDragCancelReason reason,
GtkDragSource *source)
{
gboolean success = FALSE ;
g_signal_emit (source, signals[DRAG_CANCEL], 0 , source->drag, reason, &success);
drag_end (source, success);
}
static void
gtk_drag_source_ensure_icon (GtkDragSource *self,
GdkDrag *drag)
{
GdkContentProvider *provider;
GtkWidget *icon, *child;
GdkContentFormats *formats;
const GType *types;
gsize i, n_types;
icon = gtk_drag_icon_get_for_drag (drag);
/* If an icon has been set already, we don't need to set one. */
if (gtk_drag_icon_get_child (GTK_DRAG_ICON (icon)))
return ;
if (self->paintable)
{
gtk_drag_icon_set_from_paintable (drag,
self->paintable,
self->hot_x,
self->hot_y);
return ;
}
gdk_drag_set_hotspot (drag, -2 , -2 );
provider = gdk_drag_get_content (drag);
formats = gdk_content_provider_ref_formats (provider);
types = gdk_content_formats_get_gtypes (formats, &n_types);
for (i = 0 ; i < n_types; i++)
{
GValue value = G_VALUE_INIT;
g_value_init (&value, types[i]);
if (gdk_content_provider_get_value (provider, &value, NULL))
{
child = gtk_drag_icon_create_widget_for_value (&value);
if (child)
{
gtk_drag_icon_set_child (GTK_DRAG_ICON (icon), child);
g_value_unset (&value);
gdk_content_formats_unref (formats);
return ;
}
}
g_value_unset (&value);
}
gdk_content_formats_unref (formats);
child = gtk_image_new_from_icon_name ("text-x-generic" );
gtk_image_set_icon_size (GTK_IMAGE (child), GTK_ICON_SIZE_LARGE);
gtk_drag_icon_set_child (GTK_DRAG_ICON (icon), child);
}
static void
gtk_drag_source_drag_begin (GtkDragSource *source)
{
GtkWidget *widget;
GdkDevice *device, *pointer;
GdkSeat *seat;
graphene_point_t p;
GtkNative *native;
GdkSurface *surface;
double px, py;
double dx, dy;
GdkContentProvider *content = NULL;
widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source));
device = gtk_gesture_get_device (GTK_GESTURE (source));
seat = gdk_device_get_seat (device);
if (device == gdk_seat_get_keyboard (seat))
pointer = gdk_seat_get_pointer (seat);
else
pointer = device;
native = gtk_widget_get_native (widget);
surface = gtk_native_get_surface (native);
if (!gtk_widget_compute_point (widget, GTK_WIDGET (native),
&GRAPHENE_POINT_INIT (source->start_x, source->start_y),
&p))
return ;
gdk_surface_get_device_position (surface, pointer, &px, &py, NULL);
dx = round (px - p.x);
dy = round (py - p.y);
g_signal_emit (source, signals[PREPARE], 0 , source->start_x, source->start_y, &content);
if (!content)
return ;
source->drag = gdk_drag_begin (surface, pointer, content, source->actions, dx, dy);
g_object_unref (content);
if (source->drag == NULL)
return ;
gtk_widget_reset_controllers (widget);
g_signal_emit (source, signals[DRAG_BEGIN], 0 , source->drag);
gtk_drag_source_ensure_icon (source, source->drag);
/* Keep the source alive until the drag is done */
g_object_ref (source);
g_signal_connect (source->drag, "dnd-finished" ,
G_CALLBACK (gtk_drag_source_dnd_finished_cb), source);
g_signal_connect (source->drag, "cancel" ,
G_CALLBACK (gtk_drag_source_cancel_cb), source);
}
/**
* gtk_drag_source_new :
*
* Creates a new ` GtkDragSource ` object .
*
* Returns : the new ` GtkDragSource `
*/
GtkDragSource *
gtk_drag_source_new (void )
{
return g_object_new (GTK_TYPE_DRAG_SOURCE, NULL);
}
/**
* gtk_drag_source_get_content :
* @ source : a ` GtkDragSource `
*
* Gets the current content provider of a ` GtkDragSource ` .
*
* Returns : ( nullable ) ( transfer none ) : the ` GdkContentProvider ` of @ source
*/
GdkContentProvider *
gtk_drag_source_get_content (GtkDragSource *source)
{
g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);
return source->content;
}
/**
* gtk_drag_source_set_content :
* @ source : a ` GtkDragSource `
* @ content : ( nullable ) : a ` GdkContentProvider `
*
* Sets a content provider on a ` GtkDragSource ` .
*
* When the data is requested in the cause of a DND operation ,
* it will be obtained from the content provider .
*
* This function can be called before a drag is started ,
* or in a handler for the [ signal @ Gtk . DragSource : : prepare ] signal .
*
* You may consider setting the content provider back to
* % NULL in a [ signal @ Gtk . DragSource : : drag - end ] signal handler .
*/
void
gtk_drag_source_set_content (GtkDragSource *source,
GdkContentProvider *content)
{
g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
if (!g_set_object (&source->content, content))
return ;
g_object_notify_by_pspec (G_OBJECT (source), properties[PROP_CONTENT]);
}
/**
* gtk_drag_source_get_actions :
* @ source : a ` GtkDragSource `
*
* Gets the actions that are currently set on the ` GtkDragSource ` .
*
* Returns : the actions set on @ source
*/
GdkDragAction
gtk_drag_source_get_actions (GtkDragSource *source)
{
g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), GDK_ACTION_NONE);
return source->actions;
}
/**
* gtk_drag_source_set_actions :
* @ source : a ` GtkDragSource `
* @ actions : the actions to offer
*
* Sets the actions on the ` GtkDragSource ` .
*
* During a DND operation , the actions are offered to potential
* drop targets . If @ actions include % GDK_ACTION_MOVE , you need
* to listen to the [ signal @ Gtk . DragSource : : drag - end ] signal and
* handle @ delete_data being % TRUE .
*
* This function can be called before a drag is started ,
* or in a handler for the [ signal @ Gtk . DragSource : : prepare ] signal .
*/
void
gtk_drag_source_set_actions (GtkDragSource *source,
GdkDragAction actions)
{
g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
if (source->actions == actions)
return ;
source->actions = actions;
g_object_notify_by_pspec (G_OBJECT (source), properties[PROP_ACTIONS]);
}
/**
* gtk_drag_source_set_icon :
* @ source : a ` GtkDragSource `
* @ paintable : ( nullable ) : the ` GdkPaintable ` to use as icon
* @ hot_x : the hotspot X coordinate on the icon
* @ hot_y : the hotspot Y coordinate on the icon
*
* Sets a paintable to use as icon during DND operations .
*
* The hotspot coordinates determine the point on the icon
* that gets aligned with the hotspot of the cursor .
*
* If @ paintable is % NULL , a default icon is used .
*
* This function can be called before a drag is started , or in
* a [ signal @ Gtk . DragSource : : prepare ] or
* [ signal @ Gtk . DragSource : : drag - begin ] signal handler .
*/
void
gtk_drag_source_set_icon (GtkDragSource *source,
GdkPaintable *paintable,
int hot_x,
int hot_y)
{
g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
g_set_object (&source->paintable, paintable);
source->hot_x = hot_x;
source->hot_y = hot_y;
}
/**
* gtk_drag_source_get_drag :
* @ source : a ` GtkDragSource `
*
* Returns the underlying ` GdkDrag ` object for an ongoing drag .
*
* Returns : ( nullable ) ( transfer none ) : the ` GdkDrag ` of the current
* drag operation
*/
GdkDrag *
gtk_drag_source_get_drag (GtkDragSource *source)
{
g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);
return source->drag;
}
/**
* gtk_drag_source_drag_cancel :
* @ source : a ` GtkDragSource `
*
* Cancels a currently ongoing drag operation .
*/
void
gtk_drag_source_drag_cancel (GtkDragSource *source)
{
g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
if (source->drag)
{
gboolean success = FALSE ;
g_signal_emit (source, signals[DRAG_CANCEL], 0 , source->drag, GDK_DRAG_CANCEL_ERROR, &success);
drag_end (source, FALSE );
}
}
/**
* gtk_drag_check_threshold : ( method )
* @ widget : a widget
* @ start_x : X coordinate of start of drag
* @ start_y : Y coordinate of start of drag
* @ current_x : current X coordinate
* @ current_y : current Y coordinate
*
* Checks to see if a drag movement has passed the GTK drag threshold .
*
* Returns : true if the drag threshold has been passed
*/
gboolean
gtk_drag_check_threshold (GtkWidget *widget,
int start_x,
int start_y,
int current_x,
int current_y)
{
int drag_threshold;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE );
drag_threshold = gtk_settings_get_dnd_drag_threshold (gtk_widget_get_settings (widget));
return (ABS (current_x - start_x) > drag_threshold ||
ABS (current_y - start_y) > drag_threshold);
}
gboolean
gtk_drag_check_threshold_double (GtkWidget *widget,
double start_x,
double start_y,
double current_x,
double current_y)
{
int drag_threshold;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE );
drag_threshold = gtk_settings_get_dnd_drag_threshold (gtk_widget_get_settings (widget));
return (ABS (current_x - start_x) > drag_threshold ||
ABS (current_y - start_y) > drag_threshold);
}
Messung V0.5 in Prozent C=98 H=94 G=95
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet am 2026-07-03)
¤
*© Formatika GbR, Deutschland