/* 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 "gtkdroptargetasync.h"
#include "gtkdropprivate.h"
#include "gdk/gdkdropprivate.h"
#include "gtkeventcontrollerprivate.h"
#include "gtkmarshalers.h"
#include "gdk/gdkmarshalers.h"
#include "gtknative.h"
#include "gtktypebuiltins.h"
#include "gtkprivate.h"
/**
* GtkDropTargetAsync :
*
* An event controller to receive Drag - and - Drop operations , asynchronously .
*
* It is the more complete but also more complex method of handling drop
* operations compared to [ class @ Gtk . DropTarget ] , and you should only use
* it if ` GtkDropTarget ` doesn ' t provide all the features you need .
*
* To use a ` GtkDropTargetAsync ` to receive drops on a widget , you create
* a ` GtkDropTargetAsync ` object , configure which data formats and actions
* you support , connect to its signals , and then attach it to the widget
* with [ method @ Gtk . Widget . add_controller ] .
*
* During a drag operation , the first signal that a ` GtkDropTargetAsync `
* emits is [ signal @ Gtk . DropTargetAsync : : accept ] , which is meant to determine
* whether the target is a possible drop site for the ongoing drop . The
* default handler for the : : accept signal accepts the drop if it finds
* a compatible data format and an action that is supported on both sides .
*
* If it is , and the widget becomes a target , you will receive a
* [ signal @ Gtk . DropTargetAsync : : drag - enter ] signal , followed by
* [ signal @ Gtk . DropTargetAsync : : drag - motion ] signals as the pointer moves ,
* optionally a [ signal @ Gtk . DropTargetAsync : : drop ] signal when a drop happens ,
* and finally a [ signal @ Gtk . DropTargetAsync : : drag - leave ] signal when the
* pointer moves off the widget .
*
* The : : drag - enter and : : drag - motion handler return a ` GdkDragAction `
* to update the status of the ongoing operation . The : : drop handler
* should decide if it ultimately accepts the drop and if it does , it
* should initiate the data transfer and finish the operation by calling
* [ method @ Gdk . Drop . finish ] .
*
* Between the : : drag - enter and : : drag - leave signals the widget is a
* current drop target , and will receive the % GTK_STATE_FLAG_DROP_ACTIVE
* state , which can be used by themes to style the widget as a drop target .
*/
struct _GtkDropTargetAsync
{
GtkEventController parent_object;
GdkContentFormats *formats;
GdkDragAction actions;
GdkDrop *drop;
gboolean rejected;
};
struct _GtkDropTargetAsyncClass
{
GtkEventControllerClass parent_class;
gboolean (* accept) (GtkDropTargetAsync *self,
GdkDrop *drop);
GdkDragAction (* drag_enter) (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y);
GdkDragAction (* drag_motion) (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y);
void (* drag_leave) (GtkDropTargetAsync *self,
GdkDrop *drop);
gboolean (* drop) (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y);
};
enum {
PROP_0,
PROP_ACTIONS,
PROP_FORMATS,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
enum {
ACCEPT,
DRAG_ENTER,
DRAG_MOTION,
DRAG_LEAVE,
DROP,
NUM_SIGNALS
};
static guint signals[NUM_SIGNALS];
G_DEFINE_TYPE (GtkDropTargetAsync, gtk_drop_target_async, GTK_TYPE_EVENT_CONTROLLER);
static gboolean
gtk_drop_target_async_accept (GtkDropTargetAsync *self,
GdkDrop *drop)
{
if ((gdk_drop_get_actions (drop) & self->actions) == GDK_ACTION_NONE)
return FALSE ;
if (self->formats == NULL)
return TRUE ;
return gdk_content_formats_match (self->formats, gdk_drop_get_formats (drop));
}
static GdkDragAction
make_action_unique (GdkDragAction actions)
{
if (actions & GDK_ACTION_COPY)
return GDK_ACTION_COPY;
if (actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
if (actions & GDK_ACTION_LINK)
return GDK_ACTION_LINK;
return GDK_ACTION_NONE;
}
static GdkDragAction
gtk_drop_target_async_drag_enter (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y)
{
return make_action_unique (self->actions & gdk_drop_get_actions (drop));
}
static GdkDragAction
gtk_drop_target_async_drag_motion (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y)
{
return make_action_unique (self->actions & gdk_drop_get_actions (drop));
}
static gboolean
gtk_drop_target_async_drop (GtkDropTargetAsync *self,
GdkDrop *drop,
double x,
double y)
{
return FALSE ;
}
static gboolean
gtk_drop_target_async_filter_event (GtkEventController *controller,
GdkEvent *event)
{
switch ((int )gdk_event_get_event_type (event))
{
case GDK_DRAG_ENTER:
case GDK_DRAG_LEAVE:
case GDK_DRAG_MOTION:
case GDK_DROP_START:
return GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_async_parent_class)->filter_event (controller, event);
default :;
}
return TRUE ;
}
static gboolean
gtk_drop_target_async_handle_event (GtkEventController *controller,
GdkEvent *event,
double x,
double y)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (controller);
GtkWidget *widget = gtk_event_controller_get_widget (controller);
GdkDrop *drop;
switch ((int ) gdk_event_get_event_type (event))
{
case GDK_DRAG_MOTION:
{
GdkDragAction preferred_action;
drop = gdk_dnd_event_get_drop (event);
/* sanity check */
g_return_val_if_fail (self->drop == drop, FALSE );
if (self->rejected)
return FALSE ;
g_signal_emit (self, signals[DRAG_MOTION], 0 , drop, x, y, &preferred_action);
if (preferred_action && self->drop && !self->rejected &&
gtk_drop_status (self->drop, self->actions, preferred_action))
{
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE );
}
else
{
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
}
}
return FALSE ;
case GDK_DROP_START:
{
gboolean handled;
drop = gdk_dnd_event_get_drop (event);
/* sanity check */
g_return_val_if_fail (self->drop == drop, FALSE );
if (self->rejected)
return FALSE ;
g_signal_emit (self, signals[DROP], 0 , self->drop, x, y, &handled);
g_clear_object (&self->drop);
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
return handled;
}
default :
return FALSE ;
}
}
static void
gtk_drop_target_async_handle_crossing (GtkEventController *controller,
const GtkCrossingData *crossing,
double x,
double y)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (controller);
GtkWidget *widget = gtk_event_controller_get_widget (controller);
if (crossing->type != GTK_CROSSING_DROP)
return ;
if (self->drop && gdk_drop_is_finished (self->drop))
g_clear_object (&self->drop);
/* sanity check */
g_warn_if_fail (self->drop == NULL || self->drop == crossing->drop);
if (crossing->direction == GTK_CROSSING_IN)
{
gboolean accept = FALSE ;
GdkDragAction preferred_action;
if (self->drop != NULL)
return ;
self->drop = g_object_ref (crossing->drop);
g_signal_emit (self, signals[ACCEPT], 0 , self->drop, &accept);
self->rejected = !accept;
if (self->rejected || !self->drop)
return ;
g_signal_emit (self, signals[DRAG_ENTER], 0 , self->drop, x, y, &preferred_action);
if (preferred_action && self->drop && !self->rejected &&
gtk_drop_status (self->drop, self->actions, preferred_action))
{
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE );
}
}
else
{
if (crossing->new_descendent != NULL ||
crossing->new_target == widget ||
self->drop == NULL)
return ;
g_signal_emit (self, signals[DRAG_LEAVE], 0 , self->drop);
g_clear_object (&self->drop);
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
}
}
static void
gtk_drop_target_async_reset (GtkEventController *controller)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (controller);
GtkWidget *widget = gtk_event_controller_get_widget (controller);
if (self->drop)
{
g_signal_emit (self, signals[DRAG_LEAVE], 0 , self->drop);
g_clear_object (&self->drop);
}
if (widget)
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
}
static void
gtk_drop_target_async_finalize (GObject *object)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object);
g_clear_object (&self->drop);
g_clear_pointer (&self->formats, gdk_content_formats_unref);
G_OBJECT_CLASS (gtk_drop_target_async_parent_class)->finalize (object);
}
static void
gtk_drop_target_async_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object);
switch (prop_id)
{
case PROP_ACTIONS:
gtk_drop_target_async_set_actions (self, g_value_get_flags (value));
break ;
case PROP_FORMATS:
gtk_drop_target_async_set_formats (self, g_value_get_boxed (value));
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_drop_target_async_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object);
switch (prop_id)
{
case PROP_ACTIONS:
g_value_set_flags (value, gtk_drop_target_async_get_actions (self));
break ;
case PROP_FORMATS:
g_value_set_boxed (value, gtk_drop_target_async_get_formats (self));
break ;
default :
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_drop_target_async_class_init (GtkDropTargetAsyncClass *class )
{
GObjectClass *object_class = G_OBJECT_CLASS (class );
GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class );
object_class->finalize = gtk_drop_target_async_finalize;
object_class->set_property = gtk_drop_target_async_set_property;
object_class->get_property = gtk_drop_target_async_get_property;
controller_class->handle_event = gtk_drop_target_async_handle_event;
controller_class->filter_event = gtk_drop_target_async_filter_event;
controller_class->handle_crossing = gtk_drop_target_async_handle_crossing;
controller_class->reset = gtk_drop_target_async_reset;
class ->accept = gtk_drop_target_async_accept;
class ->drag_enter = gtk_drop_target_async_drag_enter;
class ->drag_motion = gtk_drop_target_async_drag_motion;
class ->drop = gtk_drop_target_async_drop;
/**
* GtkDropTargetAsync : actions :
*
* The ` GdkDragActions ` that this drop target supports .
*/
properties[PROP_ACTIONS] =
g_param_spec_flags ("actions" , NULL, NULL,
GDK_TYPE_DRAG_ACTION, GDK_ACTION_NONE,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkDropTargetAsync : formats :
*
* The ` GdkContentFormats ` that determines the supported data formats .
*/
properties[PROP_FORMATS] =
g_param_spec_boxed ("formats" , NULL, NULL,
GDK_TYPE_CONTENT_FORMATS,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
/**
* GtkDropTargetAsync : : accept :
* @ self : the ` GtkDropTargetAsync `
* @ drop : the ` GdkDrop `
*
* Emitted on the drop site when a drop operation is about to begin .
*
* If the drop is not accepted , % FALSE will be returned and the drop target
* will ignore the drop . If % TRUE is returned , the drop is accepted for now
* but may be rejected later via a call to [ method @ Gtk . DropTargetAsync . reject_drop ]
* or ultimately by returning % FALSE from a [ signal @ Gtk . DropTargetAsync : : drop ]
* handler .
*
* The default handler for this signal decides whether to accept the drop
* based on the formats provided by the @ drop .
*
* If the decision whether the drop will be accepted or rejected needs
* further processing , such as inspecting the data , this function should
* return % TRUE and proceed as is @ drop was accepted and if it decides to
* reject the drop later , it should call [ method @ Gtk . DropTargetAsync . reject_drop ] .
*
* Returns : % TRUE if @ drop is accepted
*/
signals[ACCEPT] =
g_signal_new (I_("accept" ),
G_TYPE_FROM_CLASS (class ),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetAsyncClass, accept),
g_signal_accumulator_first_wins, NULL,
_gdk_marshal_BOOLEAN__OBJECT,
G_TYPE_BOOLEAN, 1 ,
GDK_TYPE_DROP);
g_signal_set_va_marshaller (signals[ACCEPT],
GTK_TYPE_DROP_TARGET_ASYNC,
_gdk_marshal_BOOLEAN__OBJECTv);
/**
* GtkDropTargetAsync : : drag - enter :
* @ self : the ` GtkDropTargetAsync `
* @ drop : the ` GdkDrop `
* @ x : the x coordinate of the current pointer position
* @ y : the y coordinate of the current pointer position
*
* Emitted on the drop site when the pointer enters the widget .
*
* It can be used to set up custom highlighting .
*
* Returns : Preferred action for this drag operation .
*/
signals[DRAG_ENTER] =
g_signal_new (I_("drag-enter" ),
G_TYPE_FROM_CLASS (class ),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_enter),
g_signal_accumulator_first_wins, NULL,
_gtk_marshal_FLAGS__OBJECT_DOUBLE_DOUBLE,
GDK_TYPE_DRAG_ACTION, 3 ,
GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
g_signal_set_va_marshaller (signals[DRAG_ENTER],
GTK_TYPE_DROP_TARGET_ASYNC,
_gtk_marshal_FLAGS__OBJECT_DOUBLE_DOUBLEv);
/**
* GtkDropTargetAsync : : drag - motion :
* @ self : the ` GtkDropTargetAsync `
* @ drop : the ` GdkDrop `
* @ x : the x coordinate of the current pointer position
* @ y : the y coordinate of the current pointer position
*
* Emitted while the pointer is moving over the drop target .
*
* Returns : Preferred action for this drag operation .
*/
signals[DRAG_MOTION] =
g_signal_new (I_("drag-motion" ),
G_TYPE_FROM_CLASS (class ),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_motion),
g_signal_accumulator_first_wins, NULL,
_gtk_marshal_FLAGS__OBJECT_DOUBLE_DOUBLE,
GDK_TYPE_DRAG_ACTION, 3 ,
GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
g_signal_set_va_marshaller (signals[DRAG_MOTION],
GTK_TYPE_DROP_TARGET_ASYNC,
_gtk_marshal_FLAGS__OBJECT_DOUBLE_DOUBLEv);
/**
* GtkDropTargetAsync : : drag - leave :
* @ self : the ` GtkDropTargetAsync `
* @ drop : the ` GdkDrop `
*
* Emitted on the drop site when the pointer leaves the widget .
*
* Its main purpose it to undo things done in
* ` GtkDropTargetAsync ` : : drag - enter .
*/
signals[DRAG_LEAVE] =
g_signal_new (I_("drag-leave" ),
G_TYPE_FROM_CLASS (class ),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drag_leave),
NULL, NULL,
NULL,
G_TYPE_NONE, 1 ,
GDK_TYPE_DROP);
/**
* GtkDropTargetAsync : : drop :
* @ self : the ` GtkDropTargetAsync `
* @ drop : the ` GdkDrop `
* @ x : the x coordinate of the current pointer position
* @ y : the y coordinate of the current pointer position
*
* Emitted on the drop site when the user drops the data onto the widget .
*
* The signal handler must determine whether the pointer position is in a
* drop zone or not . If it is not in a drop zone , it returns % FALSE and no
* further processing is necessary .
*
* Otherwise , the handler returns % TRUE . In this case , this handler will
* accept the drop . The handler must ensure that [ method @ Gdk . Drop . finish ]
* is called to let the source know that the drop is done . The call to
* [ method @ Gdk . Drop . finish ] must only be done when all data has been received .
*
* To receive the data , use one of the read functions provided by
* [ class @ Gdk . Drop ] such as [ method @ Gdk . Drop . read_async ] or
* [ method @ Gdk . Drop . read_value_async ] .
*
* Returns : whether the drop is accepted at the given pointer position
*/
signals[DROP] =
g_signal_new (I_("drop" ),
G_TYPE_FROM_CLASS (class ),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDropTargetAsyncClass, drop),
g_signal_accumulator_first_wins, NULL,
_gtk_marshal_BOOLEAN__OBJECT_DOUBLE_DOUBLE,
G_TYPE_BOOLEAN, 3 ,
GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
g_signal_set_va_marshaller (signals[DROP],
GTK_TYPE_DROP_TARGET_ASYNC,
_gtk_marshal_BOOLEAN__OBJECT_DOUBLE_DOUBLEv);
}
static void
gtk_drop_target_async_init (GtkDropTargetAsync *self)
{
}
/**
* gtk_drop_target_async_new :
* @ formats : ( nullable ) ( transfer full ) : the supported data formats
* @ actions : the supported actions
*
* Creates a new ` GtkDropTargetAsync ` object .
*
* Returns : the new ` GtkDropTargetAsync `
*/
GtkDropTargetAsync *
gtk_drop_target_async_new (GdkContentFormats *formats,
GdkDragAction actions)
{
GtkDropTargetAsync *result;
result = g_object_new (GTK_TYPE_DROP_TARGET_ASYNC,
"formats" , formats,
"actions" , actions,
NULL);
g_clear_pointer (&formats, gdk_content_formats_unref);
return result;
}
/**
* gtk_drop_target_async_set_formats :
* @ self : a ` GtkDropTargetAsync `
* @ formats : ( nullable ) : the supported data formats or % NULL for any format
*
* Sets the data formats that this drop target will accept .
*/
void
gtk_drop_target_async_set_formats (GtkDropTargetAsync *self,
GdkContentFormats *formats)
{
g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self));
if (self->formats == formats)
return ;
if (self->formats)
gdk_content_formats_unref (self->formats);
self->formats = formats;
if (self->formats)
gdk_content_formats_ref (self->formats);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FORMATS]);
}
/**
* gtk_drop_target_async_get_formats :
* @ self : a ` GtkDropTargetAsync `
*
* Gets the data formats that this drop target accepts .
*
* If the result is % NULL , all formats are expected to be supported .
*
* Returns : ( nullable ) ( transfer none ) : the supported data formats
*/
GdkContentFormats *
gtk_drop_target_async_get_formats (GtkDropTargetAsync *self)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET_ASYNC (self), NULL);
return self->formats;
}
/**
* gtk_drop_target_async_set_actions :
* @ self : a ` GtkDropTargetAsync `
* @ actions : the supported actions
*
* Sets the actions that this drop target supports .
*/
void
gtk_drop_target_async_set_actions (GtkDropTargetAsync *self,
GdkDragAction actions)
{
g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self));
if (self->actions == actions)
return ;
self->actions = actions;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]);
}
/**
* gtk_drop_target_async_get_actions :
* @ self : a ` GtkDropTargetAsync `
*
* Gets the actions that this drop target supports .
*
* Returns : the actions that this drop target supports
*/
GdkDragAction
gtk_drop_target_async_get_actions (GtkDropTargetAsync *self)
{
g_return_val_if_fail (GTK_IS_DROP_TARGET_ASYNC (self), GDK_ACTION_NONE);
return self->actions;
}
/**
* gtk_drop_target_async_reject_drop :
* @ self : a ` GtkDropTargetAsync `
* @ drop : the ` GdkDrop ` of an ongoing drag operation
*
* Sets the @ drop as not accepted on this drag site .
*
* This function should be used when delaying the decision
* on whether to accept a drag or not until after reading
* the data .
*/
void
gtk_drop_target_async_reject_drop (GtkDropTargetAsync *self,
GdkDrop *drop)
{
g_return_if_fail (GTK_IS_DROP_TARGET_ASYNC (self));
g_return_if_fail (GDK_IS_DROP (drop));
if (self->rejected)
return ;
if (self->drop != drop)
return ;
self->rejected = TRUE ;
gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)),
GTK_STATE_FLAG_DROP_ACTIVE);
}
Messung V0.5 in Prozent C=98 H=96 G=96
¤ Dauer der Verarbeitung: 0.5 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland