From a411959c910fbf86754b38ffcd7b9615d7788a74 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 29 Feb 2020 03:47:17 +0100 Subject: [PATCH] droptarget: Redo This is a huge reorganization of GtkDropTarget. I did not know how to split this up, so it's unfortunately all one commit. Highlights: - Split GtkDropTarget into GtkDropTarget and GtkDropTargetAsync GtkDropTarget is the simple one that only works with GTypes and offers a synchronous interface. GtkDropTargetAsync retains the full old functionality and allows handling mime types. - Drop events are handled differently Instead of picking a single drop target and sending all DND events to it, every event is sent to every drop target. The first one to handle the event gets to call gdk_drop_status(), further handlers do not interact with the GdkDrop. Of course, for the ultimate GDK_DROP_STARTING event, only the first one to accept the drop gets to handle it. This allows stacking DND event controllers that aren't necessarily interested in handling the event or that might decide later to drop it. - Port all widgets to either of those Both have a somewhat changed API due to the new event handling. For the ones who should use the sync version, lots of cleanup was involved to operate on a sync API. --- demos/gtk-demo/clipboard.c | 48 +- docs/reference/gtk/gtk4-sections.txt | 31 +- gtk/gtk.h | 3 +- gtk/gtkcalendar.c | 212 ++---- gtk/gtkcolorbutton.c | 43 +- gtk/gtkcolorswatch.c | 36 +- gtk/gtkdragdest.c | 871 ----------------------- gtk/gtkdragdest.h | 83 --- gtk/gtkdrop.c | 113 +++ gtk/gtkdropprivate.h | 38 + gtk/gtkdroptarget.c | 990 +++++++++++++++++++++++++++ gtk/gtkdroptarget.h | 86 +++ gtk/gtkdroptargetasync.c | 673 ++++++++++++++++++ gtk/gtkdroptargetasync.h | 77 +++ gtk/gtkfilechooserbutton.c | 292 +++----- gtk/gtkfilechooserwidget.c | 56 +- gtk/gtkiconview.c | 91 ++- gtk/gtkiconviewprivate.h | 6 +- gtk/gtklistbox.c | 1 - gtk/gtkmain.c | 35 +- gtk/gtknotebook.c | 164 ++--- gtk/gtkplacessidebar.c | 302 +++----- gtk/gtktext.c | 187 ++--- gtk/gtktextview.c | 129 +--- gtk/gtktreeview.c | 100 ++- gtk/gtkwidget.c | 2 +- gtk/gtkwindow.c | 32 +- gtk/meson.build | 7 +- tests/testdnd.c | 220 ++---- tests/testdnd2.c | 26 +- tests/testdnd3.c | 90 +-- tests/testlist3.c | 39 +- tests/testnotebookdnd.c | 50 +- tests/testtreednd.c | 24 +- testsuite/gtk/defaultvalue.c | 3 +- 35 files changed, 2753 insertions(+), 2407 deletions(-) delete mode 100644 gtk/gtkdragdest.c delete mode 100644 gtk/gtkdragdest.h create mode 100644 gtk/gtkdrop.c create mode 100644 gtk/gtkdropprivate.h create mode 100644 gtk/gtkdroptarget.c create mode 100644 gtk/gtkdroptarget.h create mode 100644 gtk/gtkdroptargetasync.c create mode 100644 gtk/gtkdroptargetasync.h diff --git a/demos/gtk-demo/clipboard.c b/demos/gtk-demo/clipboard.c index 9187c94641..1871e4a506 100644 --- a/demos/gtk-demo/clipboard.c +++ b/demos/gtk-demo/clipboard.c @@ -152,43 +152,17 @@ prepare_drag (GtkDragSource *source, return gdk_content_provider_new_typed (GDK_TYPE_TEXTURE, paintable); } -static void -got_texture (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GdkDrop *drop = GDK_DROP (source); - GtkWidget *image = data; - const GValue *value; - GError *error = NULL; - - value = gdk_drop_read_value_finish (drop, result, &error); - if (value) - { - GdkTexture *texture = g_value_get_object (value); - gtk_image_set_from_paintable (GTK_IMAGE (image), GDK_PAINTABLE (texture)); - } - else - { - g_print ("Failed to get data: %s\n", error->message); - g_error_free (error); - } -} - static gboolean drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkWidget *widget) + const GValue *value, + double x, + double y, + GtkImage *image) { - if (gdk_drop_has_value (drop, GDK_TYPE_TEXTURE)) - { - gdk_drop_read_value_async (drop, GDK_TYPE_TEXTURE, G_PRIORITY_DEFAULT, NULL, got_texture, widget); - return TRUE; - } + GdkTexture *texture = g_value_get_object (value); + gtk_image_set_from_paintable (GTK_IMAGE (image), GDK_PAINTABLE (texture)); - return FALSE; + return TRUE; } static void @@ -354,8 +328,8 @@ do_clipboard (GtkWidget *do_widget) gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source)); /* accept drops on image */ - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_TEXTURE), GDK_ACTION_COPY); - g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), image); + dest = gtk_drop_target_new (GDK_TYPE_TEXTURE, GDK_ACTION_COPY); + g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), image); gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest)); /* context menu on image */ @@ -383,8 +357,8 @@ do_clipboard (GtkWidget *do_widget) gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source)); /* accept drops on image */ - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_TEXTURE), GDK_ACTION_COPY); - g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), image); + dest = gtk_drop_target_new (GDK_TYPE_TEXTURE, GDK_ACTION_COPY); + g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), image); gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest)); /* context menu on image */ diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 008ab81f1e..08dc019e08 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -6813,14 +6813,16 @@ gtk_drag_source_get_type gtkdroptarget GtkDropTarget gtk_drop_target_new -gtk_drop_target_set_formats +gtk_drop_target_set_gtypes +gtk_drop_target_get_gtypes gtk_drop_target_get_formats gtk_drop_target_set_actions gtk_drop_target_get_actions +gtk_drop_target_set_preload +gtk_drop_target_get_preload gtk_drop_target_get_drop -gtk_drop_target_find_mimetype -gtk_drag_highlight -gtk_drag_unhighlight +gtk_drop_target_get_value +gtk_drop_target_reject GTK_TYPE_DROP_TARGET @@ -6833,6 +6835,27 @@ GTK_DROP_TARGET_GET_CLASS gtk_drop_target_get_type +
+gtkdroptargetasync +GtkDropTargetAsync +gtk_drop_target_async_new +gtk_drop_target_async_set_formats +gtk_drop_target_async_get_formats +gtk_drop_target_async_set_actions +gtk_drop_target_async_get_actions +gtk_drop_target_async_reject_drop + + +GTK_TYPE_DROP_TARGET_ASYNC +GTK_DROP_TARGET_ASYNC +GTK_DROP_TARGET_ASYNC_CLASS +GTK_IS_DROP_TARGET_ASYNC +GTK_IS_DROP_TARGET_ASYNC_CLASS +GTK_DROP_TARGET_ASYNC_GET_CLASS + +gtk_drop_target_async_get_type +
+
gtkdropcontrollermotion GtkDropControllerMotion diff --git a/gtk/gtk.h b/gtk/gtk.h index 8ee2ba1b0b..603fe9f9fd 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -90,11 +90,12 @@ #include #include #include -#include #include #include #include #include +#include +#include #include #include #include diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c index eb63b38db0..ec71a373cc 100644 --- a/gtk/gtkcalendar.c +++ b/gtk/gtkcalendar.c @@ -95,7 +95,7 @@ #endif #include "gtkcalendar.h" -#include "gtkdragdest.h" +#include "gtkdroptarget.h" #include "gtkintl.h" #include "gtkmain.h" #include "gtkmarshalers.h" @@ -285,17 +285,6 @@ static void gtk_calendar_focus_controller_focus (GtkEventController * GtkWidget *widget); static void gtk_calendar_state_flags_changed (GtkWidget *widget, GtkStateFlags previous_state); -static gboolean gtk_calendar_drag_accept (GtkDropTarget *dest, - GdkDrop *drop, - GtkCalendar *calendar); -static void gtk_calendar_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, - GtkCalendar *calendar); -static gboolean gtk_calendar_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkCalendar *calendar); static void calendar_invalidate_day (GtkCalendar *widget, @@ -320,6 +309,57 @@ static char *default_monthname[12]; G_DEFINE_TYPE_WITH_PRIVATE (GtkCalendar, gtk_calendar, GTK_TYPE_WIDGET) +static void +gtk_calendar_drag_notify_value (GtkDropTarget *target, + GParamSpec **pspec, + GtkCalendar *calendar) +{ + GDate *date; + const GValue *value; + + value = gtk_drop_target_get_value (target); + if (value == NULL) + return; + + date = g_date_new (); + g_date_set_parse (date, g_value_get_string (value)); + if (!g_date_valid (date)) + gtk_drop_target_reject (target); + g_date_free (date); +} + +static gboolean +gtk_calendar_drag_drop (GtkDropTarget *dest, + const GValue *value, + double x, + double y, + GtkCalendar *calendar) +{ + GDate *date; + GDateTime *datetime; + + date = g_date_new (); + g_date_set_parse (date, g_value_get_string (value)); + + if (!g_date_valid (date)) + { + g_warning ("Received invalid date data"); + g_date_free (date); + return FALSE; + } + + datetime = g_date_time_new_local (g_date_get_year (date), + g_date_get_month (date), + g_date_get_day (date), + 0, 0, 0); + g_date_free (date); + + gtk_calendar_select_day (calendar, datetime); + g_date_time_unref (datetime); + + return TRUE; +} + static void gtk_calendar_dispose (GObject *object) { @@ -518,6 +558,7 @@ gtk_calendar_init (GtkCalendar *calendar) GtkWidget *widget = GTK_WIDGET (calendar); GtkEventController *controller; GtkGesture *gesture; + GtkDropTarget *target; gint i; #ifdef G_OS_WIN32 wchar_t wbuffer[100]; @@ -535,7 +576,6 @@ gtk_calendar_init (GtkCalendar *calendar) #else gchar *week_start; #endif - GtkDropTarget *dest; int min_year_width; GDateTime *now; @@ -718,14 +758,11 @@ gtk_calendar_init (GtkCalendar *calendar) priv->in_drag = 0; - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (G_TYPE_STRING), - GDK_ACTION_COPY); - - g_signal_connect (dest, "accept", G_CALLBACK (gtk_calendar_drag_accept), calendar); - g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_calendar_drag_leave), calendar); - g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_calendar_drag_drop), calendar); - - gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (dest)); + target = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY); + gtk_drop_target_set_preload (target, TRUE); + g_signal_connect (target, "notify::value", G_CALLBACK (gtk_calendar_drag_notify_value), calendar); + g_signal_connect (target, "drop", G_CALLBACK (gtk_calendar_drag_drop), calendar); + gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (target)); priv->year_before = 0; @@ -1417,139 +1454,6 @@ gtk_calendar_state_flags_changed (GtkWidget *widget, } } -/* Get/set whether drag_motion requested the drag data and - * drag_data_received should thus not actually insert the data, - * since the data doesn’t result from a drop. - */ -static void -set_status_pending (GdkDrop *drop, - GdkDragAction suggested_action) -{ - g_object_set_data (G_OBJECT (drop), - I_("gtk-calendar-status-pending"), - GINT_TO_POINTER (suggested_action)); -} - -static GdkDragAction -get_status_pending (GdkDrop *drop) -{ - return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drop), - "gtk-calendar-status-pending")); -} - -static void -gtk_calendar_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, - GtkCalendar *calendar) -{ -} - -static void -got_text (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GtkDropTarget *dest = GTK_DROP_TARGET (data); - GtkCalendar *calendar = GTK_CALENDAR (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest))); - GdkDrop *drop = GDK_DROP (source); - gchar *str; - GDate *date; - GDateTime *datetime; - GdkDragAction suggested_action; - - suggested_action = get_status_pending (drop); - set_status_pending (drop, 0); - - str = gdk_drop_read_text_finish (drop, result, NULL); - - if (suggested_action) - { - if (str) - { - date = g_date_new (); - g_date_set_parse (date, str); - if (!g_date_valid (date)) - suggested_action = 0; - g_date_free (date); - g_free (str); - } - else - suggested_action = 0; - gdk_drop_status (drop, suggested_action); - if (suggested_action == 0) - gtk_drop_target_deny_drop (dest, drop); - return; - } - - date = g_date_new (); - if (str) - { - g_date_set_parse (date, str); - g_free (str); - } - - if (!g_date_valid (date)) - { - g_warning ("Received invalid date data"); - g_date_free (date); - gdk_drop_finish (drop, 0); - gtk_drop_target_deny_drop (dest, drop); - return; - } - - datetime = g_date_time_new_local (g_date_get_year (date), - g_date_get_month (date), - g_date_get_day (date), - 0, 0, 0); - g_date_free (date); - - gdk_drop_finish (drop, suggested_action); - - gtk_calendar_select_day (calendar, datetime); - g_date_time_unref (datetime); -} - -static gboolean -gtk_calendar_drag_accept (GtkDropTarget *dest, - GdkDrop *drop, - GtkCalendar *calendar) -{ - const char *target; - - target = gtk_drop_target_find_mimetype (dest); - if (!target || gdk_drop_get_actions (drop) == 0) - { - gdk_drop_status (drop, 0); - return FALSE; - } - else if (get_status_pending (drop) == 0) - { - set_status_pending (drop, gdk_drop_get_actions (drop)); - gdk_drop_read_text_async (drop, NULL, got_text, dest); - } - return TRUE; -} - -static gboolean -gtk_calendar_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkCalendar *calendar) -{ - const char *target; - - target = gtk_drop_target_find_mimetype (dest); - if (target != NULL) - { - set_status_pending (drop, 0); - gdk_drop_read_text_async (drop, NULL, got_text, dest); - return TRUE; - } - - return FALSE; -} - /**************************************** * Public API * diff --git a/gtk/gtkcolorbutton.c b/gtk/gtkcolorbutton.c index 9c951d21ae..17f52fba7d 100644 --- a/gtk/gtkcolorbutton.c +++ b/gtk/gtkcolorbutton.c @@ -36,17 +36,14 @@ #include "gtkcolorchooserprivate.h" #include "gtkcolorchooserdialog.h" #include "gtkcolorswatchprivate.h" -#include "gtkdragdest.h" #include "gtkdragsource.h" +#include "gtkdroptarget.h" #include "gtkintl.h" #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtkprivate.h" #include "gtksnapshot.h" #include "gtkstylecontext.h" -#include "gtkdragsource.h" -#include "gtkdragdest.h" -#include "gtkeventcontroller.h" /** @@ -230,33 +227,16 @@ gtk_color_button_class_init (GtkColorButtonClass *klass) gtk_widget_class_set_css_name (widget_class, "colorbutton"); } -static void -got_color (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GdkDrop *drop = GDK_DROP (source); - const GValue *value; - - value = gdk_drop_read_value_finish (drop, result, NULL); - if (value) - { - GdkRGBA *color = g_value_get_boxed (value); - gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (data), color); - gdk_drop_finish (drop, GDK_ACTION_COPY); - } - else - gdk_drop_finish (drop, 0); -} - static gboolean -gtk_color_button_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkColorButton *button) +gtk_color_button_drop (GtkDropTarget *dest, + const GValue *value, + double x, + double y, + GtkColorButton *button) { - gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, button); + GdkRGBA *color = g_value_get_boxed (value); + + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (button), color); return TRUE; } @@ -320,9 +300,8 @@ gtk_color_button_init (GtkColorButton *button) priv->rgba.alpha = 1; priv->use_alpha = FALSE; - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_RGBA), - GDK_ACTION_COPY); - g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_color_button_drag_drop), button); + dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY); + g_signal_connect (dest, "drop", G_CALLBACK (gtk_color_button_drop), button); gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (dest)); source = gtk_drag_source_new (); diff --git a/gtk/gtkcolorswatch.c b/gtk/gtkcolorswatch.c index f073a7ff68..ae424ad572 100644 --- a/gtk/gtkcolorswatch.c +++ b/gtk/gtkcolorswatch.c @@ -22,8 +22,8 @@ #include "gtkbox.h" #include "gtkcolorchooserprivate.h" #include "gtkcssnodeprivate.h" -#include "gtkdragdest.h" #include "gtkdragsource.h" +#include "gtkdroptarget.h" #include "gtkgesturelongpress.h" #include "gtkgestureclick.h" #include "gtkgesturesingle.h" @@ -133,33 +133,16 @@ gtk_color_swatch_drag_begin (GtkDragSource *source, g_object_unref (paintable); } -static void -got_color (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GdkDrop *drop = GDK_DROP (source); - const GValue *value; - - value = gdk_drop_read_value_finish (drop, result, NULL); - if (value) - { - GdkRGBA *color = g_value_get_boxed (value); - gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (data), color); - gdk_drop_finish (drop, GDK_ACTION_COPY); - } - else - gdk_drop_finish (drop, 0); -} - static gboolean swatch_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, + const GValue *value, + double x, + double y, GtkColorSwatch *swatch) + { - gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, swatch); + gtk_color_swatch_set_rgba (swatch, g_value_get_boxed (value)); + return TRUE; } @@ -670,9 +653,8 @@ gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch, if (can_drop && !priv->dest) { - priv->dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_RGBA), - GDK_ACTION_COPY); - g_signal_connect (priv->dest, "drag-drop", G_CALLBACK (swatch_drag_drop), swatch); + priv->dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY); + g_signal_connect (priv->dest, "drop", G_CALLBACK (swatch_drag_drop), swatch); gtk_widget_add_controller (GTK_WIDGET (swatch), GTK_EVENT_CONTROLLER (priv->dest)); } if (!can_drop && priv->dest) diff --git a/gtk/gtkdragdest.c b/gtk/gtkdragdest.c deleted file mode 100644 index 7f80e5a5f4..0000000000 --- a/gtk/gtkdragdest.c +++ /dev/null @@ -1,871 +0,0 @@ -/* 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 . - */ - -/* - * 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 "gtkdragdest.h" -#include "gtkdragdestprivate.h" - -#include "gtkintl.h" -#include "gtknative.h" -#include "gtktypebuiltins.h" -#include "gtkeventcontrollerprivate.h" -#include "gtkmarshalers.h" - - -/** - * SECTION:gtkdroptarget - * @Short_description: Event controller to receive DND drops - * @Title: GtkDropTarget - * - * GtkDropTarget is an auxiliary object that is used to receive - * Drag-and-Drop operations. - * - * To use a GtkDropTarget to receive drops on a widget, you create - * a GtkDropTarget object, configure which data formats and actions - * you support, connect to its signals, and then attach - * it to the widget with gtk_widget_add_controller(). - * - * During a drag operation, the first signal that a GtkDropTarget - * emits is #GtkDropTarget::accept, which is meant to determine - * whether the target is a possible drop site for the ongoing drag. - * The default handler for the ::accept signal accepts the drag - * if it finds a compatible data format an an action that is supported - * on both sides. - * - * If it is, and the widget becomes the current target, you will - * receive a #GtkDropTarget::drag-enter signal, followed by - * #GtkDropTarget::drag-motion signals as the pointer moves, and - * finally either a #GtkDropTarget::drag-leave signal when the pointer - * move off the widget, or a #GtkDropTarget::drag-drop signal when - * a drop happens. - * - * The ::drag-enter and ::drag-motion handler can call gdk_drop_status() - * to update the status of the ongoing operation. The ::drag-drop handler - * should initiate the data transfer and finish the operation by calling - * gdk_drop_finish(). - * - * Between the ::drag-enter and ::drag-leave signals the widget is the - * current drop target, and will receive the %GTK_STATE_FLAG_DROP_ACTIVE - * state, which can be used to style the widget as a drop targett. - */ - -struct _GtkDropTarget -{ - GtkEventController parent_object; - - GdkContentFormats *formats; - GdkDragAction actions; - - GtkWidget *widget; - GdkDrop *drop; - gboolean contains; - gboolean contains_pending; -}; - -struct _GtkDropTargetClass -{ - GtkEventControllerClass parent_class; - - gboolean (*accept ) (GtkDropTarget *dest, - GdkDrop *drop); -}; - -enum { - PROP_FORMATS = 1, - PROP_ACTIONS, - PROP_CONTAINS, - NUM_PROPERTIES -}; - -static GParamSpec *properties[NUM_PROPERTIES]; - -enum { - ACCEPT, - DRAG_ENTER, - DRAG_MOTION, - DRAG_LEAVE, - DRAG_DROP, - NUM_SIGNALS -}; - -static guint signals[NUM_SIGNALS]; - -static gboolean gtk_drop_target_accept (GtkDropTarget *dest, - GdkDrop *drop); - -static gboolean gtk_drop_target_handle_event (GtkEventController *controller, - GdkEvent *event, - double x, - double y); -static gboolean gtk_drop_target_filter_event (GtkEventController *controller, - GdkEvent *event); - -static gboolean gtk_drop_target_get_contains (GtkDropTarget *dest); -static void gtk_drop_target_set_contains (GtkDropTarget *dest, - gboolean contains); - -typedef enum { - GTK_DROP_STATUS_NONE, - GTK_DROP_STATUS_ACCEPTED, - GTK_DROP_STATUS_DENIED -} GtkDropStatus; - -static GtkDropStatus gtk_drop_target_get_drop_status (GtkDropTarget *dest, - GdkDrop *drop); -static void gtk_drop_target_set_drop_status (GtkDropTarget *dest, - GdkDrop *drop, - GtkDropStatus status); - -G_DEFINE_TYPE (GtkDropTarget, gtk_drop_target, GTK_TYPE_EVENT_CONTROLLER); - -static void -gtk_drop_target_init (GtkDropTarget *dest) -{ -} - -static void -gtk_drop_target_finalize (GObject *object) -{ - GtkDropTarget *dest = GTK_DROP_TARGET (object); - - g_clear_pointer (&dest->formats, gdk_content_formats_unref); - - G_OBJECT_CLASS (gtk_drop_target_parent_class)->finalize (object); -} - -static void -gtk_drop_target_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkDropTarget *dest = GTK_DROP_TARGET (object); - - switch (prop_id) - { - case PROP_FORMATS: - gtk_drop_target_set_formats (dest, g_value_get_boxed (value)); - break; - - case PROP_ACTIONS: - gtk_drop_target_set_actions (dest, g_value_get_flags (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gtk_drop_target_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkDropTarget *dest = GTK_DROP_TARGET (object); - - switch (prop_id) - { - case PROP_FORMATS: - g_value_set_boxed (value, gtk_drop_target_get_formats (dest)); - break; - - case PROP_ACTIONS: - g_value_set_flags (value, gtk_drop_target_get_actions (dest)); - break; - - case PROP_CONTAINS: - g_value_set_boolean (value, gtk_drop_target_get_contains (dest)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gtk_drop_target_class_init (GtkDropTargetClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class); - - object_class->finalize = gtk_drop_target_finalize; - object_class->set_property = gtk_drop_target_set_property; - object_class->get_property = gtk_drop_target_get_property; - - controller_class->handle_event = gtk_drop_target_handle_event; - controller_class->filter_event = gtk_drop_target_filter_event; - - class->accept = gtk_drop_target_accept; - - /** - * GtkDropTarget:formats: - * - * The #GdkContentFormats that determines the supported data formats - */ - properties[PROP_FORMATS] = - g_param_spec_boxed ("formats", P_("Formats"), P_("Formats"), - GDK_TYPE_CONTENT_FORMATS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkDropTarget:actions: - * - * The #GdkDragActions that this drop target supports - */ - properties[PROP_ACTIONS] = - g_param_spec_flags ("actions", P_("Actions"), P_("Actions"), - GDK_TYPE_DRAG_ACTION, 0, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); - - /** - * GtkDropTarget:contains: - * - * Whether the drop target is currently the targed of an ongoing drag operation, - * and highlighted. - */ - properties[PROP_CONTAINS] = - g_param_spec_boolean ("contains", P_("Contains an ongoing drag"), P_("Contains the current drag"), - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); - - g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); - - /** - * GtkDropTarget::drag-enter: - * @dest: the #GtkDropTarget - * @drop: the #GdkDrop - * - * The ::drag-enter signal is emitted on the drop site when the cursor - * enters the widget. It can be used to set up custom highlighting. - */ - signals[DRAG_ENTER] = - g_signal_new (I_("drag-enter"), - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - GDK_TYPE_DROP); - - /** - * GtkDropTarget::drag-leave: - * @dest: the #GtkDropTarget - * @drop: the #GdkDrop - * - * The ::drag-leave signal is emitted on the drop site when the cursor - * leaves the widget. Its main purpose it to undo things done in - * #GtkDropTarget::drag-enter. - */ - signals[DRAG_LEAVE] = - g_signal_new (I_("drag-leave"), - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - GDK_TYPE_DROP); - - /** - * GtkDropTarget::drag-motion: - * @dest: the #GtkDropTarget - * @drop: the #GdkDrop - * @x: the x coordinate of the current cursor position - * @y: the y coordinate of the current cursor position - * - * The ::drag motion signal is emitted while the pointer is moving - * over the drop target. - */ - signals[DRAG_MOTION] = - g_signal_new (I_("drag-motion"), - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 3, - GDK_TYPE_DROP, G_TYPE_INT, G_TYPE_INT); - - /** - * GtkWidget::accept: - * @dest: the #GtkDropTarget - * @drop: the #GdkDrop - * - * The ::accept signal is emitted on the drop site when the user - * moves the cursor over the widget during a drag. The signal handler - * must determine whether the cursor 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, the - * handler is responsible for providing the necessary information for - * displaying feedback to the user, by calling gdk_drag_status(). - * - * The default handler for this signal decides whether to accept the drop - * based on the type of the data. - * - * If the decision whether the drop will be accepted or rejected can't be - * made based solely the data format, handler may inspect the dragged data - * by calling one of the #GdkDrop read functions and return %TRUE to - * tentatively accept the drop. When the data arrives and is found to not be - * acceptable, a call to gtk_drop_target_deny_drop() should be made to reject - * the drop. - * - * Returns: whether the cursor position is in a drop zone - */ - signals[ACCEPT] = - g_signal_new (I_("accept"), - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkDropTargetClass, accept), - g_signal_accumulator_first_wins, NULL, - NULL, - G_TYPE_BOOLEAN, 1, - GDK_TYPE_DROP); - - /** - * GtkDropTarget::drag-drop: - * @dest: the #GtkDropTarget - * @drop: the #GdkDrop - * @x: the x coordinate of the current cursor position - * @y: the y coordinate of the current cursor position - * - * The ::drag-drop signal is emitted on the drop site when the user drops - * the data onto the widget. The signal handler must determine whether - * the cursor 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, the handler must - * ensure that gdk_drop_finish() is called to let the source know that - * the drop is done. The call to gtk_drag_finish() can be done either - * directly or after receiving the data. - * - * To receive the data, use one of the read functions provides by #GtkDrop - * and #GtkDragDest: gdk_drop_read_async(), gdk_drop_read_value_async(), - * gdk_drop_read_text_async(). - * - * You can use gtk_drop_target_get_drop() to obtain the #GtkDrop object - * for the ongoing operation in your signal handler. If you call one of the - * read functions in your handler, GTK will ensure that the #GtkDrop object - * stays alive until the read is completed. If you delay obtaining the data - * (e.g. to handle %GDK_ACTION_ASK by showing a #GtkPopover), you need to - * hold a reference on the #GtkDrop. - * - * Returns: whether the cursor position is in a drop zone - */ - signals[DRAG_DROP] = - g_signal_new (I_("drag-drop"), - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - NULL, - G_TYPE_BOOLEAN, 3, - GDK_TYPE_DROP, G_TYPE_INT, G_TYPE_INT); -} - -/** - * gtk_drop_target_new: - * @formats: (nullable) (transfer full): the supported data formats - * @actions: the supported actions - * - * Creates a new #GtkDropTarget object. - * - * Returns: the new #GtkDropTarget - */ -GtkDropTarget * -gtk_drop_target_new (GdkContentFormats *formats, - GdkDragAction actions) -{ - GtkDropTarget *result; - - result = g_object_new (GTK_TYPE_DROP_TARGET, - "formats", formats, - "actions", actions, - NULL); - - g_clear_pointer (&formats, gdk_content_formats_unref); - - return result; -} - -/** - * gtk_drop_target_set_formats: - * @dest: a #GtkDropTarget - * @formats: (nullable): the supported data formats - * - * Sets the data formats that this drop target will accept. - */ -void -gtk_drop_target_set_formats (GtkDropTarget *dest, - GdkContentFormats *formats) -{ - g_return_if_fail (GTK_IS_DROP_TARGET (dest)); - - if (dest->formats == formats) - return; - - if (dest->formats) - gdk_content_formats_unref (dest->formats); - - dest->formats = formats; - - if (dest->formats) - gdk_content_formats_ref (dest->formats); - - g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_FORMATS]); -} - -/** - * gtk_drop_target_get_formats: - * @dest: a #GtkDropTarget - * - * Gets the data formats that this drop target accepts. - * - * If the result is %NULL, all formats are expected to be supported. - * - * Returns: (nullable): the supported data formats - */ -GdkContentFormats * -gtk_drop_target_get_formats (GtkDropTarget *dest) -{ - g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), NULL); - - return dest->formats; -} - -/** - * gtk_drop_target_set_actions: - * @dest: a #GtkDropTarget - * @actions: the supported actions - * - * Sets the actions that this drop target supports. - */ -void -gtk_drop_target_set_actions (GtkDropTarget *dest, - GdkDragAction actions) -{ - g_return_if_fail (GTK_IS_DROP_TARGET (dest)); - - if (dest->actions == actions) - return; - - dest->actions = actions; - - g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_ACTIONS]); -} - -/** - * gtk_drop_target_get_actions: - * @dest: a #GtkDropTarget - * - * Gets the actions that this drop target supports. - * - * Returns: the actions that this drop target supports - */ -GdkDragAction -gtk_drop_target_get_actions (GtkDropTarget *dest) -{ - g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), 0); - - return dest->actions; -} - -/** - * gtk_drop_target_get_drop: - * @dest: a #GtkDropTarget - * - * Returns the underlying #GtkDrop object for an ongoing drag. - * - * Returns: (nullable) (transfer none): the #GtkDrop of the current drag operation, or %NULL - */ -GdkDrop * -gtk_drop_target_get_drop (GtkDropTarget *dest) -{ - g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), NULL); - - return dest->drop; -} - -static const char * -gtk_drop_target_match (GtkDropTarget *dest, - GdkDrop *drop) -{ - GdkContentFormats *formats; - const char *match; - - formats = gdk_content_formats_ref (dest->formats); - formats = gdk_content_formats_union_deserialize_mime_types (formats); - - match = gdk_content_formats_match_mime_type (formats, gdk_drop_get_formats (drop)); - - gdk_content_formats_unref (formats); - - return match; -} - -/** - * gtk_drop_target_find_mimetype: - * @dest: a #GtkDropTarget - * - * Returns a mimetype that is supported both by @dest and the ongoing - * drag. For more detailed control, you can use gdk_drop_get_formats() - * to obtain the content formats that are supported by the source. - * - * Returns: (nullable): a matching mimetype for the ongoing drag, or %NULL - */ -const char * -gtk_drop_target_find_mimetype (GtkDropTarget *dest) -{ - if (!dest->drop) - return NULL; - - if (dest->formats == NULL) - return NULL; - - return gtk_drop_target_match (dest, dest->drop); -} - -static gboolean -gtk_drop_target_accept (GtkDropTarget *dest, - GdkDrop *drop) -{ - if ((gdk_drop_get_actions (drop) & gtk_drop_target_get_actions (dest)) == 0) - return FALSE; - - if (dest->formats == NULL) - return TRUE; - - return gdk_content_formats_match (dest->formats, gdk_drop_get_formats (drop)); -} - -static void -set_drop (GtkDropTarget *dest, - GdkDrop *drop) -{ - if (dest->drop == drop) - return; - - if (dest->drop) - g_object_remove_weak_pointer (G_OBJECT (dest->drop), (gpointer *)&dest->drop); - - dest->drop = drop; - - if (dest->drop) - g_object_add_weak_pointer (G_OBJECT (dest->drop), (gpointer *)&dest->drop); -} - -static void -gtk_drop_target_emit_drag_enter (GtkDropTarget *dest, - GdkDrop *drop) -{ - set_drop (dest, drop); - g_signal_emit (dest, signals[DRAG_ENTER], 0, drop); -} - -static void -gtk_drop_target_emit_drag_leave (GtkDropTarget *dest, - GdkDrop *drop) -{ - set_drop (dest, drop); - g_signal_emit (dest, signals[DRAG_LEAVE], 0, drop); - set_drop (dest, NULL); -} - -static gboolean -gtk_drop_target_emit_accept (GtkDropTarget *dest, - GdkDrop *drop) -{ - gboolean result = FALSE; - - set_drop (dest, drop); - g_signal_emit (dest, signals[ACCEPT], 0, drop, &result); - - return result; -} - -static void -gtk_drop_target_emit_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y) -{ - set_drop (dest, drop); - g_signal_emit (dest, signals[DRAG_MOTION], 0, drop, x, y); -} - -static gboolean -gtk_drop_target_emit_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y) -{ - gboolean result = FALSE; - - set_drop (dest, drop); - g_signal_emit (dest, signals[DRAG_DROP], 0, drop, x, y, &result); - - return result; -} - -static void -gtk_drop_target_set_contains (GtkDropTarget *dest, - gboolean contains) -{ - if (dest->contains == contains) - return; - - dest->contains = contains; - - g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_CONTAINS]); -} - -static gboolean -gtk_drop_target_get_contains (GtkDropTarget *dest) -{ - return dest->contains; -} - -static gboolean -gtk_drop_target_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_parent_class)->filter_event (controller, event); - - default:; - } - - return TRUE; -} - -static void -clear_current_dest (gpointer data, GObject *former_object) -{ - g_object_set_data (G_OBJECT (data), "current-dest", NULL); -} - -static void -unset_current_dest (gpointer data) -{ - GtkDropTarget *dest = data; - GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); - - gtk_drop_target_set_contains (dest, FALSE); - if (widget) - gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE); -} - -static GtkDropTarget * -gtk_drop_get_current_dest (GdkDrop *drop) -{ - return GTK_DROP_TARGET (g_object_get_data (G_OBJECT (drop), "current-dest")); -} - -static void -gtk_drop_set_current_dest (GdkDrop *drop, - GtkDropTarget *dest) -{ - GtkDropTarget *old_dest; - GtkWidget *widget; - - old_dest = g_object_get_data (G_OBJECT (drop), "current-dest"); - - if (old_dest == dest) - return; - - if (old_dest) - { - gtk_drop_target_set_contains (old_dest, FALSE); - - widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (old_dest)); - if (widget) - gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE); - - gtk_drop_target_emit_drag_leave (old_dest, drop); - - g_object_weak_unref (G_OBJECT (old_dest), clear_current_dest, drop); - } - - g_object_set_data_full (G_OBJECT (drop), "current-dest", dest, unset_current_dest); - - if (dest) - { - g_object_weak_ref (G_OBJECT (dest), clear_current_dest, drop); - - gtk_drop_target_emit_drag_enter (dest, drop); - - widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); - if (widget) - gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE); - - gtk_drop_target_set_contains (dest, TRUE); - } -} - -static gboolean -gtk_drop_target_handle_event (GtkEventController *controller, - GdkEvent *event, - double x, - double y) -{ - GtkDropTarget *dest = GTK_DROP_TARGET (controller); - GdkDrop *drop; - GtkDropStatus status; - gboolean found = FALSE; - - drop = gdk_drag_event_get_drop (event); - - status = gtk_drop_target_get_drop_status (dest, drop); - if (status == GTK_DROP_STATUS_DENIED) - return FALSE; - - switch ((int)gdk_event_get_event_type (event)) - { - case GDK_DRAG_MOTION: - if (status != GTK_DROP_STATUS_ACCEPTED) - { - found = gtk_drop_target_emit_accept (dest, drop); - if (found) - gtk_drop_target_set_drop_status (dest, drop, GTK_DROP_STATUS_ACCEPTED); - } - else - found = TRUE; - - if (found) - { - gdk_drop_status (drop, gtk_drop_target_get_actions (dest)); - gtk_drop_set_current_dest (drop, dest); - gtk_drop_target_emit_drag_motion (dest, drop, x, y); - } - break; - - case GDK_DROP_START: - found = gtk_drop_target_emit_drag_drop (dest, drop, x, y); - break; - - default: - break; - } - - return found; -} - -/* - * This function is called if none of the event - * controllers has handled a drag event. - */ -void -gtk_drag_dest_handle_event (GtkWidget *toplevel, - GdkEvent *event) -{ - GdkDrop *drop; - GdkEventType event_type; - - g_return_if_fail (toplevel != NULL); - g_return_if_fail (event != NULL); - - event_type = gdk_event_get_event_type (event); - drop = gdk_drag_event_get_drop (event); - - switch ((guint) event_type) - { - case GDK_DRAG_LEAVE: - gtk_drop_set_current_dest (drop, NULL); - break; - - case GDK_DRAG_ENTER: - case GDK_DRAG_MOTION: - case GDK_DROP_START: - gtk_drop_set_current_dest (drop, NULL); - gdk_drop_status (drop, 0); - break; - - default: - g_assert_not_reached (); - } -} - -static GtkDropStatus -gtk_drop_target_get_drop_status (GtkDropTarget *dest, - GdkDrop *drop) -{ - GHashTable *denied; - - denied = (GHashTable *)g_object_get_data (G_OBJECT (drop), "denied-drags"); - if (denied) - return GPOINTER_TO_INT (g_hash_table_lookup (denied, dest)); - - return GTK_DROP_STATUS_NONE; -} - -static void -gtk_drop_target_set_drop_status (GtkDropTarget *dest, - GdkDrop *drop, - GtkDropStatus status) -{ - GHashTable *drags; - - drags = (GHashTable *)g_object_get_data (G_OBJECT (drop), "denied-drags"); - if (!drags) - { - drags = g_hash_table_new (NULL, NULL); - g_object_set_data_full (G_OBJECT (drop), "denied-drags", drags, (GDestroyNotify)g_hash_table_unref); - } - - g_hash_table_insert (drags, dest, GINT_TO_POINTER (status)); - - if (dest == gtk_drop_get_current_dest (drop)) - { - gdk_drop_status (drop, 0); - gtk_drop_set_current_dest (drop, NULL); - } -} - -/** - * gtk_drop_target_deny_drop: - * @dest: a #GtkDropTarget - * @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_deny_drop (GtkDropTarget *dest, - GdkDrop *drop) -{ - g_return_if_fail (GTK_IS_DROP_TARGET (dest)); - g_return_if_fail (GDK_IS_DROP (drop)); - - gtk_drop_target_set_drop_status (dest, drop, GTK_DROP_STATUS_DENIED); -} diff --git a/gtk/gtkdragdest.h b/gtk/gtkdragdest.h deleted file mode 100644 index 6ebec76107..0000000000 --- a/gtk/gtkdragdest.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */ -/* GTK - The GIMP Toolkit - * Copyright (C) 1995-1997 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 . - */ - -/* - * 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/. - */ - -#ifndef __GTK_DRAG_DEST_H__ -#define __GTK_DRAG_DEST_H__ - - -#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - - -G_BEGIN_DECLS - -typedef struct _GtkDropTarget GtkDropTarget; - - -#define GTK_TYPE_DROP_TARGET (gtk_drop_target_get_type ()) -#define GTK_DROP_TARGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DROP_TARGET, GtkDropTarget)) -#define GTK_DROP_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DROP_TARGET, GtkDropTargetClass)) -#define GTK_IS_DROP_TARGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DROP_TARGET)) -#define GTK_IS_DROP_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DROP_TARGET)) -#define GTK_DROP_TARGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DROP_TARGET, GtkDropTargetClass)) - -typedef struct _GtkDropTargetClass GtkDropTargetClass; - -GDK_AVAILABLE_IN_ALL -GType gtk_drop_target_get_type (void) G_GNUC_CONST; - -GDK_AVAILABLE_IN_ALL -GtkDropTarget *gtk_drop_target_new (GdkContentFormats *formats, - GdkDragAction actions); - -GDK_AVAILABLE_IN_ALL -void gtk_drop_target_set_formats (GtkDropTarget *dest, - GdkContentFormats *formats); -GDK_AVAILABLE_IN_ALL -GdkContentFormats *gtk_drop_target_get_formats (GtkDropTarget *dest); - -GDK_AVAILABLE_IN_ALL -void gtk_drop_target_set_actions (GtkDropTarget *dest, - GdkDragAction actions); -GDK_AVAILABLE_IN_ALL -GdkDragAction gtk_drop_target_get_actions (GtkDropTarget *dest); - -GDK_AVAILABLE_IN_ALL -GdkDrop *gtk_drop_target_get_drop (GtkDropTarget *dest); - -GDK_AVAILABLE_IN_ALL -const char *gtk_drop_target_find_mimetype (GtkDropTarget *dest); - -GDK_AVAILABLE_IN_ALL -void gtk_drop_target_deny_drop (GtkDropTarget *dest, - GdkDrop *drop); - - -G_END_DECLS - -#endif /* __GTK_DRAG_DEST_H__ */ diff --git a/gtk/gtkdrop.c b/gtk/gtkdrop.c new file mode 100644 index 0000000000..7c4c834dbc --- /dev/null +++ b/gtk/gtkdrop.c @@ -0,0 +1,113 @@ +/* + * Copyright © 2020 Benjamin Otte + * + * 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.1 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 . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gtkdropprivate.h" + +typedef struct _GtkDrop GtkDrop; + +struct _GtkDrop +{ + /* TRUE if we are waiting for a gdk_drop_status() call */ + gboolean waiting; + /* TRUE if begin_event() has been called but end_event() hasn't yet - purely for debugging */ + gboolean active; +}; + +static void +gtk_drop_free (gpointer data) +{ + GtkDrop *self = data; + + g_slice_free (GtkDrop, self); +} + +static GtkDrop * +gtk_drop_lookup (GdkDrop *drop) +{ + static GQuark drop_quark = 0; + GtkDrop *result; + + if (G_UNLIKELY (drop_quark == 0)) + drop_quark = g_quark_from_string ("-gtk-drop-data"); + + result = g_object_get_qdata (G_OBJECT (drop), drop_quark); + if (result == NULL) + { + result = g_slice_new0 (GtkDrop); + g_object_set_qdata_full (G_OBJECT (drop), drop_quark, result, gtk_drop_free); + } + + return result; +} + +void +gtk_drop_begin_event (GdkDrop *drop, + GdkEventType event_type) +{ + GtkDrop *self; + + self = gtk_drop_lookup (drop); + + g_assert (self->waiting == FALSE); + g_assert (self->active == FALSE); + + self->active = TRUE; + if (event_type == GDK_DRAG_ENTER || + event_type == GDK_DRAG_MOTION) + self->waiting = TRUE; +} + +void +gtk_drop_end_event (GdkDrop *drop) +{ + GtkDrop *self; + + self = gtk_drop_lookup (drop); + + g_assert (self->active == TRUE); + + if (self->waiting) + { + gdk_drop_status (drop, 0); + self->waiting = FALSE; + } + self->active = FALSE; +} + +gboolean +gtk_drop_status (GdkDrop *drop, + GdkDragAction actions, + GdkDragAction preferred_action) +{ + GtkDrop *self; + + self = gtk_drop_lookup (drop); + + g_assert (self->active == TRUE); + + if (!self->waiting) + return FALSE; + + gdk_drop_status (drop, actions); + self->waiting = FALSE; + return TRUE; +} + diff --git a/gtk/gtkdropprivate.h b/gtk/gtkdropprivate.h new file mode 100644 index 0000000000..769013bfcd --- /dev/null +++ b/gtk/gtkdropprivate.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2020 Benjamin Otte + * + * 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.1 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 . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_DROP_PRIVATE_H__ +#define __GTK_DROP_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + + +void gtk_drop_begin_event (GdkDrop *drop, + GdkEventType event_type); +void gtk_drop_end_event (GdkDrop *drop); + +gboolean gtk_drop_status (GdkDrop *drop, + GdkDragAction actions, + GdkDragAction preferred_action); + +G_END_DECLS + +#endif diff --git a/gtk/gtkdroptarget.c b/gtk/gtkdroptarget.c new file mode 100644 index 0000000000..3bd86fa93a --- /dev/null +++ b/gtk/gtkdroptarget.c @@ -0,0 +1,990 @@ +/* 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 . + */ + +/* + * 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 "gtkdroptarget.h" + +#include "gtkdropprivate.h" +#include "gtkeventcontrollerprivate.h" +#include "gtkintl.h" +#include "gtkmarshalers.h" +#include "gtknative.h" +#include "gtkprivate.h" +#include "gtktypebuiltins.h" + + +/** + * SECTION:gtkdroptarget + * @Short_description: Event controller to receive DND drops + * @Title: GtkDropTarget + * @See_also: #GdkDrop, #GtkDropTargetAsync + * + * GtkDropTarget is an event controller implementing a simple way to + * receive Drag-and-Drop operations. + * + * The most basic way to use a #GtkDropTarget to receive drops on a + * widget, is to create it via gtk_drop_target_new(), passing in the + * #GType of the data you want to receive and connect to the + * GtkDropTarget::drop signal to receive the data. + * + * #GtkDropTarget supports more options, such as: + * + * * rejecting potential drops via the GtkDropTarget::accept signal + * and the gtk_drop_target_reject() function to let other drop + * targets handle the drop + * * tracking an ongoing drag operation before the drop via the + * GtkDropTarget::enter, GtkDropTarget::motion and + * GtkDropTarget::leave signals + * * configuring how to receive data by setting the + * GtkDropTarget:preload property and listening for its availability + * via the GtkDropTarget:value property + * + * However, #GtkDropTarget is ultimately modeled in a synchronous way + * and only supports data transferred via #GType. + * If you want full control over an ongoing drop, the #GdkDropTargetAsync + * object gives you this ability. + * + * While a pointer is dragged over the drop target's widget and the drop + * has not been rejected, that widget will receive the + * %GTK_STATE_FLAG_DROP_ACTIVE state, which can be used to style the widget. + */ + +struct _GtkDropTarget +{ + GtkEventController parent_object; + + GdkContentFormats *formats; + GdkDragAction actions; + guint preload : 1; + + guint dropping : 1; + graphene_point_t coords; + GdkDrop *drop; + GCancellable *cancellable; /* NULL unless doing a read of value */ + GValue value; +}; + +struct _GtkDropTargetClass +{ + GtkEventControllerClass parent_class; + + gboolean (* accept) (GtkDropTarget *self, + GdkDrop *drop); + GdkDragAction (* enter) (GtkDropTarget *self, + double x, + double y); + GdkDragAction (* motion) (GtkDropTarget *self, + double x, + double y); + void (* leave) (GtkDropTarget *self, + GdkDrop *drop); + gboolean (* drop) (GtkDropTarget *self, + const GValue *value, + double x, + double y); +}; + +enum { + PROP_0, + PROP_ACTIONS, + PROP_DROP, + PROP_FORMATS, + PROP_PRELOAD, + PROP_VALUE, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES]; + +enum { + ACCEPT, + ENTER, + MOTION, + LEAVE, + DROP, + NUM_SIGNALS +}; + +static guint signals[NUM_SIGNALS]; + +G_DEFINE_TYPE (GtkDropTarget, gtk_drop_target, GTK_TYPE_EVENT_CONTROLLER); + +static void +gtk_drop_target_end_drop (GtkDropTarget *self) +{ + if (self->drop == NULL) + return; + + g_object_freeze_notify (G_OBJECT (self)); + + if (self->dropping) + { + gdk_drop_finish (self->drop, 0); + self->dropping = FALSE; + } + + g_clear_object (&self->drop); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DROP]); + + if (G_IS_VALUE (&self->value)) + { + g_value_unset (&self->value); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]); + } + + if (self->cancellable) + { + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + } + + gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)), + GTK_STATE_FLAG_DROP_ACTIVE); + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +gtk_drop_target_do_drop (GtkDropTarget *self) +{ + gboolean success; + + g_assert (self->dropping); + g_assert (G_IS_VALUE (&self->value)); + + g_signal_emit (self, signals[DROP], 0, &self->value, self->coords.x, self->coords.y, &success); + + if (success) + gdk_drop_finish (self->drop, gdk_drop_get_actions (self->drop)); + else + gdk_drop_finish (self->drop, 0); + + self->dropping = FALSE; + + gtk_drop_target_end_drop (self); +} + +static void +gtk_drop_target_load_done (GObject *source, + GAsyncResult *res, + gpointer data) +{ + GtkDropTarget *self = data; + const GValue *value; + GError *error = NULL; + + value = gdk_drop_read_value_finish (GDK_DROP (source), res, &error); + if (value == NULL) + { + /* If this happens, data/self is invalid */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + g_clear_error (&error); + return; + } + + g_clear_object (&self->cancellable); + /* XXX: Should this be a warning? */ + g_warning ("Failed to receive drop data: %s", error->message); + g_clear_error (&error); + gtk_drop_target_end_drop (self); + return; + } + + g_clear_object (&self->cancellable); + g_value_init (&self->value, G_VALUE_TYPE (value)); + g_value_copy (value, &self->value); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]); + + if (self->dropping) + gtk_drop_target_do_drop (self); +} + +static gboolean +gtk_drop_target_load (GtkDropTarget *self) +{ + g_assert (self->drop); + + if (G_IS_VALUE (&self->value)) + return TRUE; + + if (self->cancellable) + return FALSE; + + self->cancellable = g_cancellable_new (); + + gdk_drop_read_value_async (self->drop, + gdk_content_formats_match_gtype (self->formats, gdk_drop_get_formats (self->drop)), + G_PRIORITY_DEFAULT, + self->cancellable, + gtk_drop_target_load_done, + g_object_ref (self)); + return FALSE; +} + +static void +gtk_drop_target_start_drop (GtkDropTarget *self, + GdkDrop *drop) +{ + g_object_freeze_notify (G_OBJECT (self)); + + gtk_drop_target_end_drop (self); + + self->drop = g_object_ref (drop); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DROP]); + + if (self->preload) + gtk_drop_target_load (self); + + gtk_widget_set_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)), + GTK_STATE_FLAG_DROP_ACTIVE, + FALSE); + + g_object_thaw_notify (G_OBJECT (self)); +} + +static gboolean +gtk_drop_target_accept (GtkDropTarget *self, + GdkDrop *drop) +{ + if ((gdk_drop_get_actions (drop) & gtk_drop_target_get_actions (self)) == 0) + 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 0; +} + +static GdkDragAction +gtk_drop_target_enter (GtkDropTarget *self, + double x, + double y) +{ + return make_action_unique (self->actions & gdk_drop_get_actions (self->drop)); +} + +static GdkDragAction +gtk_drop_target_motion (GtkDropTarget *self, + double x, + double y) +{ + return make_action_unique (self->actions & gdk_drop_get_actions (self->drop)); +} + +static gboolean +gtk_drop_target_drop (GtkDropTarget *self, + const GValue *value, + double x, + double y) +{ + return FALSE; +} + +static gboolean +gtk_drop_target_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_parent_class)->filter_event (controller, event); + + default:; + } + + return TRUE; +} + +static gboolean +gtk_drop_target_handle_event (GtkEventController *controller, + GdkEvent *event, + double x, + double y) +{ + GtkDropTarget *self = GTK_DROP_TARGET (controller); + + /* All drops have been rejected. New drops only arrive via crossing + * events, so we can: */ + if (self->drop == NULL) + return FALSE; + + switch ((int) gdk_event_get_event_type (event)) + { + case GDK_DRAG_MOTION: + { + GtkWidget *widget = gtk_event_controller_get_widget (controller); + GdkDragAction preferred; + + /* sanity check */ + g_return_val_if_fail (self->drop == gdk_drag_event_get_drop (event), FALSE); + + graphene_point_init (&self->coords, x, y); + g_signal_emit (self, signals[MOTION], 0, x, y, &preferred); + if (preferred && + gtk_drop_status (self->drop, self->actions, preferred)) + { + 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: + { + /* sanity check */ + g_return_val_if_fail (self->drop == gdk_drag_event_get_drop (event), FALSE); + + graphene_point_init (&self->coords, x, y); + self->dropping = TRUE; + if (gtk_drop_target_load (self)) + gtk_drop_target_do_drop (self); + + return TRUE; + } + + default: + return FALSE; + } +} + +static void +gtk_drop_target_handle_crossing (GtkEventController *controller, + const GtkCrossingData *crossing, + double x, + double y) +{ + GtkDropTarget *self = GTK_DROP_TARGET (controller); + GtkWidget *widget = gtk_event_controller_get_widget (controller); + + if (crossing->type != GTK_CROSSING_DROP) + return; + + /* sanity check */ + g_warn_if_fail (self->drop == NULL || self->drop == crossing->drop); + + if (crossing->direction == GTK_CROSSING_IN) + { + gboolean accept = FALSE; + GdkDragAction preferred; + + if (self->drop != NULL) + return; + + /* if we were a target already but self->drop == NULL, the drop + * was rejected already */ + if (crossing->old_descendent != NULL || + crossing->old_target == widget) + return; + + g_signal_emit (self, signals[ACCEPT], 0, crossing->drop, &accept); + if (!accept) + return; + + graphene_point_init (&self->coords, x, y); + gtk_drop_target_start_drop (self, crossing->drop); + + g_signal_emit (self, signals[ENTER], 0, x, y, &preferred); + if (preferred && + gtk_drop_status (self->drop, self->actions, preferred)) + { + gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE); + } + else + { + gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE); + } + } + else + { + if (crossing->new_descendent != NULL || + crossing->new_target == widget) + return; + + g_signal_emit (self, signals[LEAVE], 0, self->drop); + if (!self->dropping) + gtk_drop_target_end_drop (self); + gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE); + } +} + +static void +gtk_drop_target_finalize (GObject *object) +{ + GtkDropTarget *self = GTK_DROP_TARGET (object); + + g_clear_pointer (&self->formats, gdk_content_formats_unref); + + G_OBJECT_CLASS (gtk_drop_target_parent_class)->finalize (object); +} + +static void +gtk_drop_target_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkDropTarget *self = GTK_DROP_TARGET (object); + + switch (prop_id) + { + case PROP_ACTIONS: + gtk_drop_target_set_actions (self, g_value_get_flags (value)); + break; + + case PROP_PRELOAD: + gtk_drop_target_set_preload (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtk_drop_target_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkDropTarget *self = GTK_DROP_TARGET (object); + + switch (prop_id) + { + case PROP_ACTIONS: + g_value_set_flags (value, self->actions); + break; + + case PROP_DROP: + g_value_set_object (value, self->drop); + break; + + case PROP_FORMATS: + g_value_set_boxed (value, self->formats); + break; + + case PROP_PRELOAD: + g_value_set_boolean (value, self->preload); + break; + + case PROP_VALUE: + if (G_IS_VALUE (&self->value)) + g_value_set_boxed (value, &self->value); + else + g_value_set_boxed (value, NULL); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtk_drop_target_class_init (GtkDropTargetClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class); + + object_class->finalize = gtk_drop_target_finalize; + object_class->set_property = gtk_drop_target_set_property; + object_class->get_property = gtk_drop_target_get_property; + + controller_class->handle_event = gtk_drop_target_handle_event; + controller_class->filter_event = gtk_drop_target_filter_event; + controller_class->handle_crossing = gtk_drop_target_handle_crossing; + + class->accept = gtk_drop_target_accept; + class->enter = gtk_drop_target_enter; + class->motion = gtk_drop_target_motion; + class->drop = gtk_drop_target_drop; + + /** + * GtkDropTarget:actions: + * + * The #GdkDragActions that this drop target supports + */ + properties[PROP_ACTIONS] = + g_param_spec_flags ("actions", + P_("Actions"), + P_("The actions supported by this drop target"), + GDK_TYPE_DRAG_ACTION, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkDropTarget:drop: + * + * The #GdkDrop that is currently being performed + */ + properties[PROP_DROP] = + g_param_spec_object ("drop", + P_("Drop"), + P_("Current drop"), + GDK_TYPE_DROP, + GTK_PARAM_READABLE); + + /** + * GtkDropTarget:formats: + * + * The #GdkContentFormats that determine the supported data formats + */ + properties[PROP_FORMATS] = + g_param_spec_boxed ("formats", + P_("Formats"), + P_("The supported formats"), + GDK_TYPE_CONTENT_FORMATS, + GTK_PARAM_READABLE); + + /** + * GtkDropTarget:preload: + * + * Whether the drop data should be preloaded when the pointer is only + * hovering over the widget but has not been released. + * + * Setting this property allows finer grained reaction to an ongoing + * drop at the cost of loading more data. + * + * The default value for this property is %FALSE to avoid downloading + * huge amounts of data by accident. + * For example, if somebody drags a full document of gigabytes of text + * from a text editor across a widget with a preloading drop target, + * this data will be downlaoded, even if the data is ultimately dropped + * elsewhere. + * + * For a lot of data formats, the amount of data is very small (like + * %GDK_TYPE_RGBA), so enabling this property does not hurt at all. + */ + properties[PROP_PRELOAD] = + g_param_spec_boolean ("preload", + P_("Preload"), + P_("Whether drop data should be preloaded while hovering"), + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkDropTarget:value: + * + * The value for this drop operation or %NULL if the data has not been + * loaded yet or no drop operation is going on. + * + * Data may be available before the GtkDropTarget::drop signal gets emitted - + * for example when the GtkDropTarget:preload property is set. + * You can use the GObject::notify signal to be notified of available data. + */ + properties[PROP_VALUE] = + g_param_spec_boxed ("value", + P_("Value"), + P_("The value for this drop operation"), + G_TYPE_VALUE, + GTK_PARAM_READABLE); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); + + /** + * GtkDropTarget::accept: + * @self: the #GtkDropTarget + * @drop: the #GdkDrop + * + * The ::accept signal is 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 gtk_drop_target_reject() or + * ultimately by returning %FALSE from GtkDropTarget::drop + * + * 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 + * inspecting the data, this function should return %TRUE, the + * GtkDropTarget:preload property should be set and the value + * should be inspected via the GObject::notify:value signal and then call + * gtk_drop_target_reject(). + * + * 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 (GtkDropTargetClass, accept), + g_signal_accumulator_first_wins, NULL, + NULL, + G_TYPE_BOOLEAN, 1, + GDK_TYPE_DROP); + + /** + * GtkDropTarget::enter: + * @self: the #GtkDropTarget + * @x: the x coordinate of the current pointer position + * @y: the y coordinate of the current pointer position + * + * The ::enter signal is 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 or 0 if dropping is not + * supported at the current @x,@y location. + */ + signals[ENTER] = + g_signal_new (I_("enter"), + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkDropTargetClass, enter), + g_signal_accumulator_first_wins, NULL, + NULL, + GDK_TYPE_DRAG_ACTION, 2, + G_TYPE_DOUBLE, G_TYPE_DOUBLE); + + /** + * GtkDropTarget::motion: + * @self: the #GtkDropTarget + * @x: the x coordinate of the current pointer position + * @y: the y coordinate of the current pointer position + * + * The ::motion signal is emitted while the pointer is moving + * over the drop target. + * + * Returns: Preferred action for this drag operation or 0 if dropping is not + * supported at the current @x,@y location. + */ + signals[MOTION] = + g_signal_new (I_("motion"), + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkDropTargetClass, motion), + g_signal_accumulator_first_wins, NULL, + NULL, + GDK_TYPE_DRAG_ACTION, 2, + G_TYPE_DOUBLE, G_TYPE_DOUBLE); + + /** + * GtkDropTarget::leave: + * @self: the #GtkDropTarget + * @drop: the #GdkDrop + * + * The ::leave signal is emitted on the drop site when the pointer + * leaves the widget. Its main purpose it to undo things done in + * #GtkDropTarget::enter. + */ + signals[LEAVE] = + g_signal_new (I_("leave"), + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkDropTargetClass, leave), + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkDropTarget::drop: + * @self: the #GtkDropTarget + * @value: the #GValue being dropped + * @x: the x coordinate of the current pointer position + * @y: the y coordinate of the current pointer position + * + * The ::drop signal is 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 is responsible for rading the given @value + * and performing the drop operation. + * + * Returns: whether the drop was accepted at the given pointer position + */ + signals[DROP] = + g_signal_new (I_("drop"), + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + g_signal_accumulator_first_wins, NULL, + NULL, + G_TYPE_BOOLEAN, 3, + G_TYPE_VALUE, G_TYPE_DOUBLE, G_TYPE_DOUBLE); +} + +static void +gtk_drop_target_init (GtkDropTarget *self) +{ + self->formats = gdk_content_formats_new (NULL, 0); +} + +/** + * gtk_drop_target_new: + * @type: The supported type or %G_TYPE_INVALID + * @actions: the supported actions + * + * Creates a new #GtkDropTarget object. + * + * If the drop target should support more than 1 type, pass + * %G_TYPE_INVALID for @type and then call + * gtk_drop_target_set_gtypes(). + * + * Returns: the new #GtkDropTarget + */ +GtkDropTarget * +gtk_drop_target_new (GType type, + GdkDragAction actions) +{ + GtkDropTarget *result; + + result = g_object_new (GTK_TYPE_DROP_TARGET, + "actions", actions, + NULL); + + if (type != G_TYPE_INVALID) + gtk_drop_target_set_gtypes (result, &type, 1); + + return result; +} + +/** + * gtk_drop_target_get_formats: + * @self: a #GtkDropTarget + * + * Gets the data formats that this drop target accepts. + * + * If the result is %NULL, all formats are expected to be supported. + * + * Returns: (nullable): the supported data formats + */ +GdkContentFormats * +gtk_drop_target_get_formats (GtkDropTarget *self) +{ + g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL); + + return self->formats; +} + +/** + * gtk_drop_target_set_gtypes: + * @self: a #GtkDropTarget + * @types: (nullable) (transfer none) (array length=n_types): + * all supported #GTypes that can be dropped + * @n_types: number of @types + * + * Sets the supported #GTypes for this drop target. + * + * The GtkDropTarget::drop signal will + **/ +void +gtk_drop_target_set_gtypes (GtkDropTarget *self, + GType *types, + gsize n_types) +{ + GdkContentFormatsBuilder *builder; + gsize i; + + g_return_if_fail (GTK_IS_DROP_TARGET (self)); + g_return_if_fail (n_types == 0 || types != NULL); + + gdk_content_formats_unref (self->formats); + + builder = gdk_content_formats_builder_new (); + for (i = 0; i < n_types; i++) + gdk_content_formats_builder_add_gtype (builder, types[i]); + + self->formats = gdk_content_formats_builder_free_to_formats (builder); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FORMATS]); +} + +/** + * gtk_drop_target_get_gtypes: + * @self: a #GtkDropTarget + * @n_gtypes: (out) (allow-none): optional pointer to take the + * number of #GTypes contained in the return value + * + * Gets the list of supported #GTypes for @self. If no type have been set, + * %NULL will be returned. + * + * Returns: (transfer none) (nullable): %G_TYPE_INVALID-terminated array of + * types included in @formats or %NULL if none. + **/ +const GType * +gtk_drop_target_get_gtypes (GtkDropTarget *self, + gsize *n_types) +{ + g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL); + + return gdk_content_formats_get_gtypes (self->formats, n_types); +} + +/** + * gtk_drop_target_set_actions: + * @self: a #GtkDropTarget + * @actions: the supported actions + * + * Sets the actions that this drop target supports. + */ +void +gtk_drop_target_set_actions (GtkDropTarget *self, + GdkDragAction actions) +{ + g_return_if_fail (GTK_IS_DROP_TARGET (self)); + + if (self->actions == actions) + return; + + self->actions = actions; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]); +} + +/** + * gtk_drop_target_get_actions: + * @self: a #GtkDropTarget + * + * Gets the actions that this drop target supports. + * + * Returns: the actions that this drop target supports + */ +GdkDragAction +gtk_drop_target_get_actions (GtkDropTarget *self) +{ + g_return_val_if_fail (GTK_IS_DROP_TARGET (self), 0); + + return self->actions; +} + +/** + * gtk_drop_target_set_preload: + * @self: a #GtkDropTarget + * @preload: %TRUE to preload drop data + * + * Sets the GtkDropTarget:preload property. + **/ +void +gtk_drop_target_set_preload (GtkDropTarget *self, + gboolean preload) +{ + g_return_if_fail (GTK_IS_DROP_TARGET (self)); + + if (self->preload == preload) + return; + + self->preload = preload; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRELOAD]); +} + +/** + * gtk_drop_target_get_preload: + * @self: a #GtkDropTarget + * + * Gets the value of the GtkDropTarget:preload property. + * + * Returns: %TRUE if drop data should be preloaded + */ +gboolean +gtk_drop_target_get_preload (GtkDropTarget *self) +{ + g_return_val_if_fail (GTK_IS_DROP_TARGET (self), 0); + + return self->preload; +} + +/** + * gtk_drop_target_get_drop: + * @self: a #GtkDropTarget + * + * Gets the currently handled drop operation. + * + * If no drop operation is going on, %NULL is returned. + * + * Returns: (nullable) (transfer none): The current drop + **/ +GdkDrop * +gtk_drop_target_get_drop (GtkDropTarget *self) +{ + g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL); + + return self->drop; +} + +/** + * gtk_drop_target_get_value: + * @self: a #GtkDropTarget + * + * Gets the value of the GtkDropTarget:value porperty. + * + * Returns: (nullable) (transfer none): The current drop data + **/ +const GValue * +gtk_drop_target_get_value (GtkDropTarget *self) +{ + g_return_val_if_fail (GTK_IS_DROP_TARGET (self), NULL); + + if (!G_IS_VALUE (&self->value)) + return NULL; + + return &self->value; +} + +/** + * gtk_drop_target_reject: + * @self: a #GtkDropTarget + * + * Rejects the ongoing drop operation. + * + * If no drop operation is ongoing - when GdkDropTarget:drop + * returns %NULL - this function does nothing. + * + * 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_reject (GtkDropTarget *self) +{ + g_return_if_fail (GTK_IS_DROP_TARGET (self)); + + if (self->drop == NULL) + return; + + gtk_drop_target_end_drop (self); +} + diff --git a/gtk/gtkdroptarget.h b/gtk/gtkdroptarget.h new file mode 100644 index 0000000000..4020f15648 --- /dev/null +++ b/gtk/gtkdroptarget.h @@ -0,0 +1,86 @@ +/* + * Copyright © 2020 Benjamin Otte + * + * 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.1 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 . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_DROP_TARGET_H__ +#define __GTK_DROP_TARGET_H__ + + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + + +G_BEGIN_DECLS + +typedef struct _GtkDropTarget GtkDropTarget; + + +#define GTK_TYPE_DROP_TARGET (gtk_drop_target_get_type ()) +#define GTK_DROP_TARGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DROP_TARGET, GtkDropTarget)) +#define GTK_DROP_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DROP_TARGET, GtkDropTargetClass)) +#define GTK_IS_DROP_TARGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DROP_TARGET)) +#define GTK_IS_DROP_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DROP_TARGET)) +#define GTK_DROP_TARGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DROP_TARGET, GtkDropTargetClass)) + +typedef struct _GtkDropTargetClass GtkDropTargetClass; + +GDK_AVAILABLE_IN_ALL +GType gtk_drop_target_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_ALL +GtkDropTarget * gtk_drop_target_new (GType type, + GdkDragAction actions); + +GDK_AVAILABLE_IN_ALL +void gtk_drop_target_set_gtypes (GtkDropTarget *self, + GType *types, + gsize n_types); +GDK_AVAILABLE_IN_ALL +const GType * gtk_drop_target_get_gtypes (GtkDropTarget *self, + gsize *n_types); +GDK_AVAILABLE_IN_ALL +GdkContentFormats * gtk_drop_target_get_formats (GtkDropTarget *self); + +GDK_AVAILABLE_IN_ALL +void gtk_drop_target_set_actions (GtkDropTarget *self, + GdkDragAction actions); +GDK_AVAILABLE_IN_ALL +GdkDragAction gtk_drop_target_get_actions (GtkDropTarget *self); + +GDK_AVAILABLE_IN_ALL +void gtk_drop_target_set_preload (GtkDropTarget *self, + gboolean preload); +GDK_AVAILABLE_IN_ALL +gboolean gtk_drop_target_get_preload (GtkDropTarget *self); + +GDK_AVAILABLE_IN_ALL +GdkDrop * gtk_drop_target_get_drop (GtkDropTarget *self); + +GDK_AVAILABLE_IN_ALL +const GValue * gtk_drop_target_get_value (GtkDropTarget *self); + +GDK_AVAILABLE_IN_ALL +void gtk_drop_target_reject (GtkDropTarget *self); + + +G_END_DECLS + +#endif /* __GTK_DROP_TARGET_H__ */ diff --git a/gtk/gtkdroptargetasync.c b/gtk/gtkdroptargetasync.c new file mode 100644 index 0000000000..0c2061927a --- /dev/null +++ b/gtk/gtkdroptargetasync.c @@ -0,0 +1,673 @@ +/* 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 . + */ + +/* + * 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 "gtkeventcontrollerprivate.h" +#include "gtkintl.h" +#include "gtkmarshalers.h" +#include "gtknative.h" +#include "gtktypebuiltins.h" + + +/** + * SECTION:gtkdroptargetasync + * @Short_description: Event controller to receive DND drops + * @Title: GtkDropTargetAsync + * @See_also: #GtkDropTarget + * + * GtkDropTargetAsync is an auxiliary object that can be used to receive + * Drag-and-Drop operations. + * It is the more complete but also more complex method of handling drop + * operations compared to #GtkDropTarget 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 gtk_widget_add_controller(). + * + * During a drag operation, the first signal that a GtkDropTargetAsync + * emits is #GtkDropTargetAsync::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 + * #GtkDropTargetAsync::drag-enter signal, followed by + * #GtkDropTargetAsync::drag-motion signals as the pointer moves, + * optionally a #GtkDropTargetAsync::drop signal when a drop happens, + * and finally a #GtkDropTargetAsync::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 + * 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) == 0) + 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 0; +} + +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); + GdkDrop *drop; + + switch ((int) gdk_event_get_event_type (event)) + { + case GDK_DRAG_MOTION: + { + GtkWidget *widget = gtk_event_controller_get_widget (controller); + GdkDragAction preferred_action; + + drop = gdk_drag_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 && + 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_drag_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); + 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; + + /* 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) + return; + + g_signal_emit (self, signals[DRAG_ENTER], 0, self->drop, x, y, &preferred_action); + if (preferred_action && + 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) + 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_finalize (GObject *object) +{ + GtkDropTargetAsync *self = GTK_DROP_TARGET_ASYNC (object); + + 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; + + 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", P_("Actions"), P_("Actions"), + GDK_TYPE_DRAG_ACTION, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkDropTargetAsync:formats: + * + * The #GdkContentFormats that determines the supported data formats + */ + properties[PROP_FORMATS] = + g_param_spec_boxed ("formats", P_("Formats"), P_("Formats"), + GDK_TYPE_CONTENT_FORMATS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); + + /** + * GtkWidget::accept: + * @self: the #GtkDropTargetAsync + * @drop: the #GdkDrop + * + * The ::accept signal is 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 gtk_drop_target_reject() or + * ultimately by returning %FALSE from GtkDropTarget::drop + * + * 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 procesing, 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 gtk_drop_target_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, + NULL, + G_TYPE_BOOLEAN, 1, + GDK_TYPE_DROP); + + /** + * 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 + * + * The ::drag-enter signal is 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, + NULL, + GDK_TYPE_DRAG_ACTION, 3, + GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + + /** + * 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 + * + * The ::drag-motion signal is 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, + NULL, + GDK_TYPE_DRAG_ACTION, 3, + GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + + /** + * GtkDropTargetAsync::drag-leave: + * @self: the #GtkDropTargetAsync + * @drop: the #GdkDrop + * + * The ::drag-leave signal is 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 + * + * The ::drop signal is 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 gdk_drop_finish() is + * called to let the source know that the drop is done. The call to + * gtk_drag_finish() must only be done when all data has been received. + * + * To receive the data, use one of the read functions provides by #GdkDrop + * such as gdk_drop_read_async() or 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, + 0, + g_signal_accumulator_first_wins, NULL, + NULL, + G_TYPE_BOOLEAN, 3, + GDK_TYPE_DROP, G_TYPE_DOUBLE, G_TYPE_DOUBLE); +} + +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): 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), 0); + + 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)); + g_return_if_fail (self->drop == drop); + + if (self->rejected) + return; + + self->rejected = TRUE; + gtk_widget_unset_state_flags (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (self)), + GTK_STATE_FLAG_DROP_ACTIVE); +} diff --git a/gtk/gtkdroptargetasync.h b/gtk/gtkdroptargetasync.h new file mode 100644 index 0000000000..6e9578ab53 --- /dev/null +++ b/gtk/gtkdroptargetasync.h @@ -0,0 +1,77 @@ +/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 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 . + */ + +/* + * 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/. + */ + +#ifndef __GTK_DROP_TARGET_ASYNC_H__ +#define __GTK_DROP_TARGET_ASYNC_H__ + + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + + +G_BEGIN_DECLS + +typedef struct _GtkDropTargetAsync GtkDropTargetAsync; +typedef struct _GtkDropTargetAsyncClass GtkDropTargetAsyncClass; + + +#define GTK_TYPE_DROP_TARGET_ASYNC (gtk_drop_target_async_get_type ()) +#define GTK_DROP_TARGET_ASYNC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DROP_TARGET_ASYNC, GtkDropTargetAsync)) +#define GTK_DROP_TARGET_ASYNC_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DROP_TARGET_ASYNC, GtkDropTargetAsyncClass)) +#define GTK_IS_DROP_TARGET_ASYNC(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DROP_TARGET_ASYNC)) +#define GTK_IS_DROP_TARGET_ASYNC_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DROP_TARGET_ASYNC)) +#define GTK_DROP_TARGET_ASYNC_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DROP_TARGET_ASYNC, GtkDropTargetAsyncClass)) + + +GDK_AVAILABLE_IN_ALL +GType gtk_drop_target_async_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_ALL +GtkDropTargetAsync * gtk_drop_target_async_new (GdkContentFormats *formats, + GdkDragAction actions); + +GDK_AVAILABLE_IN_ALL +void gtk_drop_target_async_set_formats (GtkDropTargetAsync *self, + GdkContentFormats *formats); +GDK_AVAILABLE_IN_ALL +GdkContentFormats * gtk_drop_target_async_get_formats (GtkDropTargetAsync *self); + +GDK_AVAILABLE_IN_ALL +void gtk_drop_target_async_set_actions (GtkDropTargetAsync *self, + GdkDragAction actions); +GDK_AVAILABLE_IN_ALL +GdkDragAction gtk_drop_target_async_get_actions (GtkDropTargetAsync *self); + +GDK_AVAILABLE_IN_ALL +void gtk_drop_target_async_reject_drop (GtkDropTargetAsync *self, + GdkDrop *drop); + + +G_END_DECLS + +#endif /* __GTK_DROP_TARGET_ASYNC_H__ */ diff --git a/gtk/gtkfilechooserbutton.c b/gtk/gtkfilechooserbutton.c index f73f52add8..ce15326723 100644 --- a/gtk/gtkfilechooserbutton.c +++ b/gtk/gtkfilechooserbutton.c @@ -37,7 +37,7 @@ #include "gtkcellrendererpixbuf.h" #include "gtkcombobox.h" #include "gtkcssiconthemevalueprivate.h" -#include "gtkdragdest.h" +#include "gtkdroptarget.h" #include "gtkicontheme.h" #include "gtkimage.h" #include "gtklabel.h" @@ -268,11 +268,6 @@ static void gtk_file_chooser_button_finalize (GObject *ob /* GtkWidget Functions */ static void gtk_file_chooser_button_destroy (GtkWidget *widget); -static gboolean gtk_file_chooser_button_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkWidget *widget); static void gtk_file_chooser_button_show (GtkWidget *widget); static void gtk_file_chooser_button_hide (GtkWidget *widget); static void gtk_file_chooser_button_root (GtkWidget *widget); @@ -350,6 +345,115 @@ G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, gtk_file_chooser_button_file_chooser_iface_init)) +struct DndSelectFolderData +{ + GtkFileSystem *file_system; + GtkFileChooserButton *button; + GtkFileChooserAction action; + GFile *file; + gchar **uris; + guint i; + gboolean selected; +}; + +static void +dnd_select_folder_get_info_cb (GCancellable *cancellable, + GFileInfo *info, + const GError *error, + gpointer user_data) +{ + struct DndSelectFolderData *data = user_data; + GtkFileChooserButton *button = data->button; + GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button); + gboolean cancelled = g_cancellable_is_cancelled (cancellable); + + if (cancellable != priv->dnd_select_folder_cancellable) + { + g_object_unref (data->button); + g_object_unref (data->file); + g_strfreev (data->uris); + g_free (data); + + g_object_unref (cancellable); + return; + } + + priv->dnd_select_folder_cancellable = NULL; + + if (!cancelled && !error && info != NULL) + { + gboolean is_folder; + + is_folder = _gtk_file_info_consider_as_directory (info); + + data->selected = + (((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) || + (data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) && + gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button), data->file, NULL)); + } + else + data->selected = FALSE; + + if (data->selected || data->uris[++data->i] == NULL) + { + g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0); + + g_object_unref (data->button); + g_object_unref (data->file); + g_strfreev (data->uris); + g_free (data); + + g_object_unref (cancellable); + return; + } + + if (data->file) + g_object_unref (data->file); + + data->file = g_file_new_for_uri (data->uris[data->i]); + + priv->dnd_select_folder_cancellable = + _gtk_file_system_get_info (data->file_system, data->file, + "standard::type", + dnd_select_folder_get_info_cb, user_data); + + g_object_unref (cancellable); +} + +static gboolean +gtk_file_chooser_button_drop (GtkDropTarget *target, + const GValue *value, + double x, + double y, + GtkFileChooserButton *button) +{ + GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button); + struct DndSelectFolderData *info; + GFile *file; + + file = g_value_get_object (value); + + info = g_new0 (struct DndSelectFolderData, 1); + info->button = g_object_ref (button); + info->i = 0; + info->uris = g_new0 (char *, 2); + info->selected = FALSE; + info->file_system = priv->fs; + g_object_get (priv->chooser, "action", &info->action, NULL); + + info->file = g_object_ref (file); + + if (priv->dnd_select_folder_cancellable) + g_cancellable_cancel (priv->dnd_select_folder_cancellable); + + priv->dnd_select_folder_cancellable = + _gtk_file_system_get_info (priv->fs, info->file, + "standard::type", + dnd_select_folder_get_info_cb, info); + + return TRUE; +} + static void gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class) { @@ -441,8 +545,7 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button) GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button); GtkWidget *box; GtkWidget *icon; - GdkContentFormatsBuilder *builder; - GtkDropTarget *dest; + GtkDropTarget *target; priv->button = gtk_button_new (); g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked_cb), button); @@ -494,13 +597,9 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button) NULL, NULL); /* DnD */ - builder = gdk_content_formats_builder_new (); - gdk_content_formats_builder_add_gtype (builder, G_TYPE_STRING); - gdk_content_formats_builder_add_gtype (builder, GDK_TYPE_FILE_LIST); - dest = gtk_drop_target_new (gdk_content_formats_builder_free_to_formats (builder), - GDK_ACTION_COPY); - g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_file_chooser_button_drag_drop), button); - gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (dest)); + target = gtk_drop_target_new (G_TYPE_FILE, GDK_ACTION_COPY); + g_signal_connect (target, "drop", G_CALLBACK (gtk_file_chooser_button_drop), button); + gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (target)); } @@ -1045,169 +1144,6 @@ gtk_file_chooser_button_destroy (GtkWidget *widget) GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->destroy (widget); } -struct DndSelectFolderData -{ - GtkFileSystem *file_system; - GtkFileChooserButton *button; - GtkFileChooserAction action; - GFile *file; - gchar **uris; - guint i; - gboolean selected; -}; - -static void -dnd_select_folder_get_info_cb (GCancellable *cancellable, - GFileInfo *info, - const GError *error, - gpointer user_data) -{ - struct DndSelectFolderData *data = user_data; - GtkFileChooserButton *button = data->button; - GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button); - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - - if (cancellable != priv->dnd_select_folder_cancellable) - { - g_object_unref (data->button); - g_object_unref (data->file); - g_strfreev (data->uris); - g_free (data); - - g_object_unref (cancellable); - return; - } - - priv->dnd_select_folder_cancellable = NULL; - - if (!cancelled && !error && info != NULL) - { - gboolean is_folder; - - is_folder = _gtk_file_info_consider_as_directory (info); - - data->selected = - (((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) || - (data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) && - gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button), data->file, NULL)); - } - else - data->selected = FALSE; - - if (data->selected || data->uris[++data->i] == NULL) - { - g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0); - - g_object_unref (data->button); - g_object_unref (data->file); - g_strfreev (data->uris); - g_free (data); - - g_object_unref (cancellable); - return; - } - - if (data->file) - g_object_unref (data->file); - - data->file = g_file_new_for_uri (data->uris[data->i]); - - priv->dnd_select_folder_cancellable = - _gtk_file_system_get_info (data->file_system, data->file, - "standard::type", - dnd_select_folder_get_info_cb, user_data); - - g_object_unref (cancellable); -} - -static void -dnd_select_file (GtkFileChooserButton *button, - GFile *file) -{ - GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button); - struct DndSelectFolderData *info; - - info = g_new0 (struct DndSelectFolderData, 1); - info->button = g_object_ref (button); - info->i = 0; - info->uris = g_new0 (char *, 2); - info->selected = FALSE; - info->file_system = priv->fs; - g_object_get (priv->chooser, "action", &info->action, NULL); - - info->file = g_object_ref (file); - - if (priv->dnd_select_folder_cancellable) - g_cancellable_cancel (priv->dnd_select_folder_cancellable); - - priv->dnd_select_folder_cancellable = - _gtk_file_system_get_info (priv->fs, info->file, - "standard::type", - dnd_select_folder_get_info_cb, info); -} - -static void -got_file (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (data); - GdkDrop *drop = GDK_DROP (source); - const GValue *value; - - value = gdk_drop_read_value_finish (drop, result, NULL); - if (value) - { - GFile *file; - - file = g_value_get_object (value); - dnd_select_file (button, file); - } -} - -static void -got_text (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (data); - GdkDrop *drop = GDK_DROP (source); - char *str; - - str = gdk_drop_read_text_finish (drop, result, NULL); - if (str) - { - GFile *file; - - file = g_file_new_for_uri (str); - dnd_select_file (button, file); - g_object_unref (file); - } - -} - -static gboolean -gtk_file_chooser_button_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkWidget *button) -{ - if (gdk_drop_has_value (drop, G_TYPE_FILE)) - { - gdk_drop_read_value_async (drop, G_TYPE_FILE, G_PRIORITY_DEFAULT, NULL, got_file, button); - return TRUE; - } - else - { - gdk_drop_read_text_async (drop, NULL, got_text, button); - return TRUE; - } - - return FALSE; - -} - static void gtk_file_chooser_button_show (GtkWidget *widget) { diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index 9460663468..1b6a03826f 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -31,7 +31,7 @@ #include "gtkcomboboxtext.h" #include "gtkcssnumbervalueprivate.h" #include "gtkdragsource.h" -#include "gtkdragdest.h" +#include "gtkdroptarget.h" #include "gtkentry.h" #include "gtkfilechooserprivate.h" #include "gtkfilechooserdialog.h" @@ -1839,25 +1839,17 @@ out: g_object_unref (cancellable); } -static void -file_list_drag_data_received_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) +static gboolean +file_list_drag_drop_cb (GtkDropTarget *dest, + const GValue *value, + double x, + double y, + GtkFileChooserWidget *impl) { - GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (user_data); GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl); - GdkDrop *drop = GDK_DROP (source); GSList *files; - const GValue *value; FileListDragData *data; - value = gdk_drop_read_value_finish (drop, result, NULL); - if (value == NULL) - { - gdk_drop_finish (drop, 0); - return; - } - files = g_value_get_boxed (value); data = g_new0 (FileListDragData, 1); @@ -1873,30 +1865,6 @@ file_list_drag_data_received_cb (GObject *source, file_list_drag_data_received_get_info_cb, data); - gdk_drop_finish (drop, gdk_drop_get_actions (drop)); -} - -/* Don't do anything with the drag_drop signal */ -static gboolean -file_list_drag_drop_cb (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkFileChooserWidget *impl) -{ - gdk_drop_read_value_async (drop, GDK_TYPE_FILE_LIST, G_PRIORITY_DEFAULT, NULL, file_list_drag_data_received_cb, impl); - - return TRUE; -} - -/* Disable the normal tree drag motion handler, it makes it look like you're - dropping the dragged item onto a tree item */ -static gboolean -file_list_drag_accept_cb (GtkDropTarget *dest, - GdkDrop *drop, - GtkFileChooserWidget *impl) -{ - g_signal_stop_emission_by_name (dest, "accept"); return TRUE; } @@ -7954,7 +7922,7 @@ post_process_ui (GtkFileChooserWidget *impl) GtkCellRenderer *cell; GList *cells; GFile *file; - GtkDropTarget *dest; + GtkDropTarget *target; /* Setup file list treeview */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); @@ -7966,11 +7934,9 @@ post_process_ui (GtkFileChooserWidget *impl) gdk_content_formats_new_for_gtype (GDK_TYPE_FILE_LIST), GDK_ACTION_COPY | GDK_ACTION_MOVE); - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GDK_TYPE_FILE_LIST), - GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect (dest, "accept", G_CALLBACK (file_list_drag_accept_cb), impl); - g_signal_connect (dest, "drag-drop", G_CALLBACK (file_list_drag_drop_cb), impl); - gtk_widget_add_controller (priv->browse_files_tree_view, GTK_EVENT_CONTROLLER (dest)); + target = gtk_drop_target_new (GDK_TYPE_FILE_LIST, GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect (target, "drop", G_CALLBACK (file_list_drag_drop_cb), impl); + gtk_widget_add_controller (priv->browse_files_tree_view, GTK_EVENT_CONTROLLER (target)); /* File browser treemodel columns are shared between GtkFileChooser implementations, * so we don't set cell renderer attributes in GtkBuilder, but rather keep that diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c index 9400fdd24b..acaf40f10d 100644 --- a/gtk/gtkiconview.c +++ b/gtk/gtkiconview.c @@ -30,7 +30,6 @@ #include "gtkcellrenderertext.h" #include "gtkcombobox.h" #include "gtkcssnodeprivate.h" -#include "gtkdragdest.h" #include "gtkdragsource.h" #include "gtkentry.h" #include "gtkintl.h" @@ -48,7 +47,6 @@ #include "gtkwindow.h" #include "gtkeventcontrollerkey.h" #include "gtkdragsource.h" -#include "gtkdragdest.h" #include "gtkdragicon.h" #include "gtknative.h" @@ -287,19 +285,19 @@ static GdkContentProvider * gtk_icon_view_drag_data_get (GtkIco GtkTreePath *source_row); /* Target side drag signals */ -static void gtk_icon_view_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, - GtkIconView *icon_view); -static void gtk_icon_view_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkIconView *icon_view); -static gboolean gtk_icon_view_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkIconView *icon_view); +static void gtk_icon_view_drag_leave (GtkDropTargetAsync *dest, + GdkDrop *drop, + GtkIconView *icon_view); +static GdkDragAction gtk_icon_view_drag_motion (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkIconView *icon_view); +static gboolean gtk_icon_view_drag_drop (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkIconView *icon_view); static void gtk_icon_view_drag_data_received (GObject *source, GAsyncResult *result, gpointer data); @@ -5828,12 +5826,12 @@ drag_scroll_timeout (gpointer data) } static gboolean -set_destination (GtkIconView *icon_view, - GtkDropTarget *dest, - gint x, - gint y, - GdkDragAction *suggested_action, - GType *target) +set_destination (GtkIconView *icon_view, + GtkDropTargetAsync *dest, + gint x, + gint y, + GdkDragAction *suggested_action, + GType *target) { GtkWidget *widget; GtkTreePath *path = NULL; @@ -5863,7 +5861,7 @@ set_destination (GtkIconView *icon_view, return FALSE; /* no longer a drop site */ } - formats = gtk_drop_target_get_formats (dest); + formats = gtk_drop_target_async_get_formats (dest); *target = gdk_content_formats_match_gtype (formats, formats); if (*target == G_TYPE_INVALID) return FALSE; @@ -6129,9 +6127,9 @@ gtk_icon_view_dnd_finished_cb (GdkDrag *drag, /* Target side drag signals */ static void -gtk_icon_view_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, - GtkIconView *icon_view) +gtk_icon_view_drag_leave (GtkDropTargetAsync *dest, + GdkDrop *drop, + GtkIconView *icon_view) { /* unset any highlight row */ gtk_icon_view_set_drag_dest_item (icon_view, @@ -6141,24 +6139,22 @@ gtk_icon_view_drag_leave (GtkDropTarget *dest, remove_scroll_timeout (icon_view); } -static void -gtk_icon_view_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkIconView *icon_view) +static GdkDragAction +gtk_icon_view_drag_motion (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkIconView *icon_view) { GtkTreePath *path = NULL; GtkIconViewDropPosition pos; GdkDragAction suggested_action = 0; GType target; gboolean empty; + GdkDragAction result; if (!set_destination (icon_view, dest, x, y, &suggested_action, &target)) - { - gdk_drop_status (drop, 0); - return; - } + return 0; gtk_icon_view_get_drag_dest_item (icon_view, &path, &pos); @@ -6168,7 +6164,7 @@ gtk_icon_view_drag_motion (GtkDropTarget *dest, if (path == NULL && !empty) { /* Can't drop here. */ - gdk_drop_status (drop, 0); + result = 0; } else { @@ -6189,20 +6185,22 @@ gtk_icon_view_drag_motion (GtkDropTarget *dest, else { set_status_pending (drop, 0); - gdk_drop_status (drop, suggested_action); } + result = suggested_action; } if (path) gtk_tree_path_free (path); + + return result; } static gboolean -gtk_icon_view_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkIconView *icon_view) +gtk_icon_view_drag_drop (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkIconView *icon_view) { GtkTreePath *path; GdkDragAction suggested_action = 0; @@ -6217,7 +6215,7 @@ gtk_icon_view_drag_drop (GtkDropTarget *dest, if (!icon_view->priv->dest_set) return FALSE; - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag-drop")) + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drop")) return FALSE; if (!set_destination (icon_view, dest, x, y, &suggested_action, &target)) @@ -6324,8 +6322,6 @@ gtk_icon_view_drag_data_received (GObject *source, suggested_action = 0; } - gdk_drop_status (drop, suggested_action); - if (path) gtk_tree_path_free (path); @@ -6407,10 +6403,11 @@ gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view, g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - icon_view->priv->dest = gtk_drop_target_new (gdk_content_formats_ref (formats), actions); + icon_view->priv->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions); g_signal_connect (icon_view->priv->dest, "drag-leave", G_CALLBACK (gtk_icon_view_drag_leave), icon_view); + g_signal_connect (icon_view->priv->dest, "drag-enter", G_CALLBACK (gtk_icon_view_drag_motion), icon_view); g_signal_connect (icon_view->priv->dest, "drag-motion", G_CALLBACK (gtk_icon_view_drag_motion), icon_view); - g_signal_connect (icon_view->priv->dest, "drag-drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view); + g_signal_connect (icon_view->priv->dest, "drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view); gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest)); icon_view->priv->dest_actions = actions; diff --git a/gtk/gtkiconviewprivate.h b/gtk/gtkiconviewprivate.h index c45c3ba3e9..1cfd1aa3d8 100644 --- a/gtk/gtkiconviewprivate.h +++ b/gtk/gtkiconviewprivate.h @@ -17,10 +17,10 @@ #include "gtk/gtkiconview.h" #include "gtk/gtkcssnodeprivate.h" -#include "gtk/gtkdragdest.h" -#include "gtk/gtkgestureclick.h" #include "gtk/gtkeventcontrollermotion.h" #include "gtk/gtkdragsource.h" +#include "gtk/gtkdroptargetasync.h" +#include "gtk/gtkgestureclick.h" #ifndef __GTK_ICON_VIEW_PRIVATE_H__ #define __GTK_ICON_VIEW_PRIVATE_H__ @@ -135,7 +135,7 @@ struct _GtkIconViewPrivate gint press_start_y; GdkContentFormats *source_formats; - GtkDropTarget *dest; + GtkDropTargetAsync *dest; GtkCssNode *dndnode; GdkDrag *drag; diff --git a/gtk/gtklistbox.c b/gtk/gtklistbox.c index 9db8ee9ac0..ffa4a0112c 100644 --- a/gtk/gtklistbox.c +++ b/gtk/gtklistbox.c @@ -25,7 +25,6 @@ #include "gtkbuildable.h" #include "gtkcontainerprivate.h" #include "gtkcssnodeprivate.h" -#include "gtkdragdest.h" #include "gtkgestureclick.h" #include "gtkintl.h" #include "gtkmain.h" diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index fe2f63812f..bc164008bb 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -117,7 +117,7 @@ #include "gtkaccelmapprivate.h" #include "gtkbox.h" #include "gtkdebug.h" -#include "gtkdragdestprivate.h" +#include "gtkdropprivate.h" #include "gtkmain.h" #include "gtkmediafileprivate.h" #include "gtkmodulesprivate.h" @@ -1550,9 +1550,14 @@ handle_pointing_event (GdkEvent *event) event, gdk_crossing_event_get_mode (event), NULL); break; case GDK_DRAG_LEAVE: - old_target = update_pointer_focus_state (toplevel, event, NULL); - gtk_synthesize_crossing_events (GTK_ROOT (toplevel), GTK_CROSSING_DROP, old_target, NULL, - event, GDK_CROSSING_NORMAL, gdk_drag_event_get_drop (event)); + { + GdkDrop *drop = gdk_drag_event_get_drop (event); + old_target = update_pointer_focus_state (toplevel, event, NULL); + gtk_drop_begin_event (drop, GDK_DRAG_LEAVE); + gtk_synthesize_crossing_events (GTK_ROOT (toplevel), GTK_CROSSING_DROP, old_target, NULL, + event, GDK_CROSSING_NORMAL, drop); + gtk_drop_end_event (drop); + } break; case GDK_ENTER_NOTIFY: if (gdk_crossing_event_get_mode (event) == GDK_CROSSING_GRAB || @@ -1586,10 +1591,14 @@ handle_pointing_event (GdkEvent *event) gtk_window_maybe_update_cursor (toplevel, NULL, device); } - else if (type == GDK_DRAG_ENTER || type == GDK_DRAG_MOTION || type == GDK_DROP_START) + else if ((old_target != target) && + (type == GDK_DRAG_ENTER || type == GDK_DRAG_MOTION || type == GDK_DROP_START)) { + GdkDrop *drop = gdk_drag_event_get_drop (event); + gtk_drop_begin_event (drop, type); gtk_synthesize_crossing_events (GTK_ROOT (toplevel), GTK_CROSSING_DROP, old_target, target, event, GDK_CROSSING_NORMAL, gdk_drag_event_get_drop (event)); + gtk_drop_end_event (drop); } else if (type == GDK_TOUCH_BEGIN) gtk_window_set_pointer_focus_grab (toplevel, device, sequence, target); @@ -1786,19 +1795,19 @@ gtk_main_do_event (GdkEvent *event) case GDK_ENTER_NOTIFY: case GDK_LEAVE_NOTIFY: + case GDK_DRAG_ENTER: + case GDK_DRAG_LEAVE: /* Crossing event propagation happens during picking */ break; case GDK_DRAG_MOTION: case GDK_DROP_START: - if (gtk_propagate_event (target_widget, event)) - break; - G_GNUC_FALLTHROUGH; - - case GDK_DRAG_ENTER: - case GDK_DRAG_LEAVE: - /* Crossing event propagation happens during picking */ - gtk_drag_dest_handle_event (target_widget, event); + { + GdkDrop *drop = gdk_drag_event_get_drop (event); + gtk_drop_begin_event (drop, gdk_event_get_event_type (event)); + gtk_propagate_event (target_widget, event); + gtk_drop_end_event (drop); + } break; case GDK_EVENT_LAST: diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index 7886f55dd7..92f7651a2e 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -33,7 +33,7 @@ #include "gtkbuildable.h" #include "gtkbutton.h" #include "gtkcssstylepropertyprivate.h" -#include "gtkdragdest.h" +#include "gtkdroptarget.h" #include "gtkdragicon.h" #include "gtkdropcontrollermotion.h" #include "gtkeventcontrollermotion.h" @@ -793,14 +793,15 @@ static void gtk_notebook_dnd_finished_cb (GdkDrag *drag, static void gtk_notebook_drag_cancel_cb (GdkDrag *drag, GdkDragCancelReason reason, GtkWidget *widget); -static void gtk_notebook_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y); +static GdkDragAction gtk_notebook_drag_motion(GtkDropTarget *dest, + double x, + double y, + GtkNotebook *notebook); static gboolean gtk_notebook_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y); + const GValue *value, + double x, + double y, + GtkNotebook *notebook); /*** GtkContainer Methods ***/ static void gtk_notebook_add (GtkContainer *container, @@ -1430,9 +1431,10 @@ gtk_notebook_init (GtkNotebook *notebook) gtk_widget_set_vexpand (priv->stack_widget, TRUE); gtk_widget_set_parent (priv->stack_widget, GTK_WIDGET (notebook)); - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_NOTEBOOK_PAGE), GDK_ACTION_MOVE); - g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_notebook_drag_motion), NULL); - g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_notebook_drag_drop), NULL); + dest = gtk_drop_target_new (GTK_TYPE_NOTEBOOK_PAGE, GDK_ACTION_MOVE); + gtk_drop_target_set_preload (dest, TRUE); + g_signal_connect (dest, "motion", G_CALLBACK (gtk_notebook_drag_motion), notebook); + g_signal_connect (dest, "drop", G_CALLBACK (gtk_notebook_drag_drop), notebook); gtk_widget_add_controller (GTK_WIDGET (priv->tabs_widget), GTK_EVENT_CONTROLLER (dest)); gesture = gtk_gesture_click_new (); @@ -3294,112 +3296,78 @@ gtk_notebook_switch_page_timeout (gpointer data) return FALSE; } -static void -gtk_notebook_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y) +static gboolean +gtk_notebook_can_drag_from (GtkNotebook *self, + GtkNotebook *other, + GtkNotebookPage *page) +{ + /* always allow dragging inside self */ + if (self == other) + return TRUE; + + /* if the groups don't match, fail */ + if (self->priv->group == 0 || + self->priv->group != other->priv->group) + return FALSE; + + /* Check that the dragged page is not a parent of the notebook + * being dragged into */ + if (GTK_WIDGET (self) == page->child || + gtk_widget_is_ancestor (GTK_WIDGET (self), GTK_WIDGET (page->child)) || + GTK_WIDGET (self) == page->tab_label || + gtk_widget_is_ancestor (GTK_WIDGET (self), GTK_WIDGET (page->tab_label))) + return FALSE; + + return TRUE; +} + +static GdkDragAction +gtk_notebook_drag_motion (GtkDropTarget *dest, + double x, + double y, + GtkNotebook *notebook) { - GtkWidget *tabs = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); - GtkWidget *widget = gtk_widget_get_ancestor (tabs, GTK_TYPE_NOTEBOOK); - GtkNotebook *notebook = GTK_NOTEBOOK (widget); GtkNotebookPrivate *priv = notebook->priv; - GdkContentFormats *formats; + GdkDrag *drag = gdk_drop_get_drag (gtk_drop_target_get_drop (dest)); + GtkNotebook *source; priv->mouse_x = x; priv->mouse_y = y; - formats = gtk_drop_target_get_formats (dest); - if (gdk_content_formats_contain_gtype (formats, GTK_TYPE_NOTEBOOK_PAGE)) - { - GQuark group, source_group; - GtkWidget *source_child; - GdkDrag *drag = gdk_drop_get_drag (drop); + if (!drag) + return 0; - if (!drag) - { - gdk_drop_status (drop, 0); - } - else - { - GtkNotebook *source = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin")); + source = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin")); + g_assert (source->priv->cur_page != NULL); - g_assert (source->priv->cur_page != NULL); - source_child = source->priv->cur_page->child; + if (!gtk_notebook_can_drag_from (notebook, source, source->priv->cur_page)) + return 0; - group = notebook->priv->group; - source_group = source->priv->group; - - if (group != 0 && group == source_group && - !(widget == source_child || - gtk_widget_is_ancestor (widget, source_child))) - { - gdk_drop_status (drop, GDK_ACTION_MOVE); - } - else - { - /* it's a tab, but doesn't share - * ID with this notebook */ - gdk_drop_status (drop, 0); - } - } - } -} - -static void -got_page (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GtkNotebook *notebook = GTK_NOTEBOOK (data); - GdkDrop *drop = GDK_DROP (source); - GdkDrag *drag = gdk_drop_get_drag (drop); - GtkWidget *source_widget; - const GValue *value; - - source_widget = GTK_WIDGET (drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL); - - value = gdk_drop_read_value_finish (drop, result, NULL); - - if (value) - { - GtkNotebookPage *page = g_value_get_object (value); - - do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, page->child); - gdk_drop_finish (drop, GDK_ACTION_MOVE); - } - else - gdk_drop_finish (drop, 0); + return GDK_ACTION_MOVE; } static gboolean gtk_notebook_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y) + const GValue *value, + double x, + double y, + GtkNotebook *self) { - GtkWidget *tabs = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); - GtkWidget *widget = gtk_widget_get_ancestor (tabs, GTK_TYPE_NOTEBOOK); - GtkNotebook *notebook = GTK_NOTEBOOK (widget); - GdkDrag *drag = gdk_drop_get_drag (drop); - GtkWidget *source_widget; + GdkDrag *drag = gdk_drop_get_drag (gtk_drop_target_get_drop (dest)); + GtkNotebook *source; + GtkNotebookPage *page = g_value_get_object (value); - source_widget = GTK_WIDGET (drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL); + source = drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL; - if (GTK_IS_NOTEBOOK (source_widget) && - (gdk_drop_get_actions (drop) & GDK_ACTION_MOVE)) - { - notebook->priv->mouse_x = x; - notebook->priv->mouse_y = y; + if (!gtk_notebook_can_drag_from (self, source, source->priv->cur_page)) + return FALSE; - gdk_drop_read_value_async (drop, GTK_TYPE_NOTEBOOK_PAGE, G_PRIORITY_DEFAULT, NULL, got_page, notebook); + self->priv->mouse_x = x; + self->priv->mouse_y = y; - return TRUE; - } + do_detach_tab (source, self, page->child); - gdk_drop_finish (drop, 0); - - return FALSE; + return TRUE; } /** diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c index 925370a6a4..f62569b5f6 100644 --- a/gtk/gtkplacessidebar.c +++ b/gtk/gtkplacessidebar.c @@ -50,7 +50,7 @@ #include "gtklabel.h" #include "gtkbutton.h" #include "gtklistbox.h" -#include "gtkdragdest.h" +#include "gtkdroptarget.h" #include "gtkseparator.h" #include "gtkentry.h" #include "gtkgesturelongpress.h" @@ -150,8 +150,6 @@ struct _GtkPlacesSidebar { GtkWidget *trash_row; /* DND */ - GSList *drag_list; /* list of GFile */ - gint drag_data_info; gboolean dragging_over; GtkWidget *drag_row; gint drag_row_height; @@ -175,8 +173,6 @@ struct _GtkPlacesSidebar { GtkPlacesOpenFlags open_flags; guint mounting : 1; - guint drag_data_received : 1; - guint drop_occurred : 1; guint show_recent_set : 1; guint show_recent : 1; guint show_desktop_set : 1; @@ -300,13 +296,6 @@ static GMountOperation * get_mount_operation (GtkPlacesSidebar *sidebar); static GMountOperation * get_unmount_operation (GtkPlacesSidebar *sidebar); -/* Identifiers for target types */ -enum { - DND_UNKNOWN, - DND_GTK_SIDEBAR_ROW, - DND_TEXT_URI_LIST -}; - G_DEFINE_TYPE (GtkPlacesSidebar, gtk_places_sidebar, GTK_TYPE_WIDGET); static void @@ -380,18 +369,6 @@ emit_drag_action_requested (GtkPlacesSidebar *sidebar, return ret_action; } -static GdkDragAction -emit_drag_action_ask (GtkPlacesSidebar *sidebar, - GdkDragAction actions) -{ - GdkDragAction ret_action = 0; - - g_signal_emit (sidebar, places_sidebar_signals[DRAG_ACTION_ASK], 0, - actions, &ret_action); - - return ret_action; -} - static void emit_drag_perform_drop (GtkPlacesSidebar *sidebar, GFile *dest_file, @@ -1513,7 +1490,8 @@ update_places (GtkPlacesSidebar *sidebar) static gboolean check_valid_drop_target (GtkPlacesSidebar *sidebar, - GtkSidebarRow *row) + GtkSidebarRow *row, + const GValue *value) { GtkPlacesSidebarPlaceType place_type; GtkPlacesSidebarSectionType section_type; @@ -1522,6 +1500,8 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar, GFile *dest_file; gint drag_action; + g_return_val_if_fail (value != NULL, TRUE); + if (row == NULL) return FALSE; @@ -1560,19 +1540,18 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar, } /* Dragging a bookmark? */ - if (sidebar->drag_data_received && - sidebar->drag_data_info == DND_GTK_SIDEBAR_ROW) + if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW)) { /* Don't allow reordering bookmarks into non-bookmark areas */ valid = section_type == SECTION_BOOKMARKS; } - else + else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) { /* Dragging a file */ if (uri != NULL) { dest_file = g_file_new_for_uri (uri); - drag_action = emit_drag_action_requested (sidebar, dest_file, sidebar->drag_list); + drag_action = emit_drag_action_requested (sidebar, dest_file, g_value_get_boxed (value)); valid = drag_action > 0; g_object_unref (dest_file); @@ -1582,6 +1561,11 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar, valid = FALSE; } } + else + { + g_assert_not_reached (); + valid = TRUE; + } g_free (uri); return valid; @@ -1589,7 +1573,7 @@ check_valid_drop_target (GtkPlacesSidebar *sidebar, static void update_possible_drop_targets (GtkPlacesSidebar *sidebar, - gboolean dragging) + const GValue *value) { GList *rows; GList *l; @@ -1599,55 +1583,18 @@ update_possible_drop_targets (GtkPlacesSidebar *sidebar, for (l = rows; l != NULL; l = l->next) { - sensitive = !dragging || check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (l->data)); + sensitive = value == NULL || check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (l->data), value); gtk_widget_set_sensitive (GTK_WIDGET (l->data), sensitive); } g_list_free (rows); } -static void drag_data_received_callback (GObject *source, - GAsyncResult *result, - gpointer user_data); - -static gboolean -get_drag_data (GtkPlacesSidebar *self, - GtkDropTarget *dest, - GdkDrop *drop, - GtkListBoxRow *row) -{ - GdkContentFormats *formats = gdk_drop_get_formats (drop); - - if (row) - g_object_set_data_full (G_OBJECT (drop), "places-sidebar-row", g_object_ref (row), g_object_unref); - - gdk_drop_read_value_async (drop, - gdk_content_formats_match_gtype (formats, formats), - G_PRIORITY_DEFAULT, - NULL, - drag_data_received_callback, - self->list_box); - - return TRUE; -} - static void -free_drag_data (GtkPlacesSidebar *sidebar) +start_drop_feedback (GtkPlacesSidebar *sidebar, + const GValue *value) { - sidebar->drag_data_received = FALSE; - - if (sidebar->drag_list) - { - g_slist_free_full (sidebar->drag_list, g_object_unref); - sidebar->drag_list = NULL; - } -} - -static void -start_drop_feedback (GtkPlacesSidebar *sidebar) -{ - if (sidebar->drag_data_received && - sidebar->drag_data_info != DND_GTK_SIDEBAR_ROW) + if (value && !G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW)) { gtk_sidebar_row_reveal (GTK_SIDEBAR_ROW (sidebar->new_bookmark_row)); /* If the state is permanent, don't change it. The application controls it. */ @@ -1655,15 +1602,13 @@ start_drop_feedback (GtkPlacesSidebar *sidebar) sidebar->drop_state = DROP_STATE_NEW_BOOKMARK_ARMED; } - update_possible_drop_targets (sidebar, TRUE); + update_possible_drop_targets (sidebar, value); } static void stop_drop_feedback (GtkPlacesSidebar *sidebar) { - update_possible_drop_targets (sidebar, FALSE); - - free_drag_data (sidebar); + update_possible_drop_targets (sidebar, NULL); if (sidebar->drop_state != DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT && sidebar->new_bookmark_row != NULL) @@ -1686,7 +1631,6 @@ stop_drop_feedback (GtkPlacesSidebar *sidebar) } sidebar->dragging_over = FALSE; - sidebar->drag_data_info = DND_UNKNOWN; } static GtkWidget * @@ -1695,20 +1639,19 @@ create_placeholder_row (GtkPlacesSidebar *sidebar) return g_object_new (GTK_TYPE_SIDEBAR_ROW, "placeholder", TRUE, NULL); } -static void -drag_motion_callback (GtkDropTarget *dest, - GdkDrop *drop, - gint x, - gint y, - gpointer user_data) +static GdkDragAction +drag_motion_callback (GtkDropTarget *target, + double x, + double y, + GtkPlacesSidebar *sidebar) { - GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data); - gint action; + GdkDragAction action; GtkListBoxRow *row; GtkPlacesSidebarPlaceType place_type; gchar *drop_target_uri = NULL; gint row_index; gint row_placeholder_index; + const GValue *value; sidebar->dragging_over = TRUE; action = 0; @@ -1716,17 +1659,16 @@ drag_motion_callback (GtkDropTarget *dest, gtk_list_box_drag_unhighlight_row (GTK_LIST_BOX (sidebar->list_box)); - /* Nothing to do if no drag data */ - if (!sidebar->drag_data_received && - !get_drag_data (sidebar, dest, drop, row)) + /* Nothing to do if no value yet */ + value = gtk_drop_target_get_value (target); + if (value == NULL) goto out; /* Nothing to do if the target is not valid drop destination */ - if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (row))) + if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (row), value)) goto out; - if (sidebar->drag_data_received && - sidebar->drag_data_info == DND_GTK_SIDEBAR_ROW) + if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW)) { /* Dragging bookmarks always moves them to another position in the bookmarks list */ action = GDK_ACTION_MOVE; @@ -1783,7 +1725,7 @@ drag_motion_callback (GtkDropTarget *dest, gtk_list_box_prepend (GTK_LIST_BOX (sidebar->list_box), sidebar->row_placeholder); } - else + else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) { gtk_list_box_drag_highlight_row (GTK_LIST_BOX (sidebar->list_box), row); @@ -1795,32 +1737,33 @@ drag_motion_callback (GtkDropTarget *dest, * file move/copy operation itself, or if we should only try to * create bookmarks out of the dragged URIs. */ - if (sidebar->drag_list != NULL) + if (place_type == PLACES_DROP_FEEDBACK) { - if (place_type == PLACES_DROP_FEEDBACK) + action = GDK_ACTION_COPY; + } + else + { + /* uri may be NULL for unmounted volumes, for example, so we don't allow drops there */ + if (drop_target_uri != NULL) { - action = GDK_ACTION_COPY; - } - else - { - /* uri may be NULL for unmounted volumes, for example, so we don't allow drops there */ - if (drop_target_uri != NULL) - { - GFile *dest_file = g_file_new_for_uri (drop_target_uri); + GFile *dest_file = g_file_new_for_uri (drop_target_uri); - action = emit_drag_action_requested (sidebar, dest_file, sidebar->drag_list); + action = emit_drag_action_requested (sidebar, dest_file, g_value_get_boxed (value)); - g_object_unref (dest_file); - } + g_object_unref (dest_file); } } g_free (drop_target_uri); } + else + { + g_assert_not_reached (); + } out: - start_drop_feedback (sidebar); - gdk_drop_status (drop, action); + start_drop_feedback (sidebar, value); + return action; } /* Reorders the bookmark to the specified position */ @@ -1867,51 +1810,26 @@ drop_files_as_bookmarks (GtkPlacesSidebar *sidebar, } } -static void -drag_data_received_callback (GObject *source, - GAsyncResult *result, - gpointer user_data) +static gboolean +drag_drop_callback (GtkDropTarget *target, + const GValue *value, + double x, + double y, + GtkPlacesSidebar *sidebar) { - GdkDrop *drop = GDK_DROP (source); - GtkWidget *list_box = user_data; - GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (gtk_widget_get_ancestor (list_box, GTK_TYPE_PLACES_SIDEBAR)); gint target_order_index; GtkPlacesSidebarPlaceType target_place_type; GtkPlacesSidebarSectionType target_section_type; gchar *target_uri; GtkListBoxRow *target_row; - GdkDragAction real_action; - const GValue *value; + gboolean result; - value = gdk_drop_read_value_finish (drop, result, NULL); - - if (!sidebar->drag_data_received) - { - if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) - { - /* Free spurious drag data from previous drags if present */ - if (sidebar->drag_list != NULL) - g_slist_free_full (sidebar->drag_list, g_object_unref); - sidebar->drag_list = g_slist_copy_deep (g_value_get_boxed (value), (GCopyFunc) g_object_ref, NULL); - sidebar->drag_data_info = DND_TEXT_URI_LIST; - } - else if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW)) - { - sidebar->drag_list = NULL; - sidebar->drag_data_info = DND_GTK_SIDEBAR_ROW; - } - sidebar->drag_data_received = TRUE; - } - - if (!sidebar->drop_occurred) - return; - - target_row = g_object_get_data (G_OBJECT (drop), "places-sidebar-row"); + target_row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (sidebar->list_box), y); if (target_row == NULL) - return; + return FALSE; - if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (target_row))) - return; + if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (target_row), value)) + return FALSE; g_object_get (target_row, "place-type", &target_place_type, @@ -1919,9 +1837,9 @@ drag_data_received_callback (GObject *source, "order-index", &target_order_index, "uri", &target_uri, NULL); - real_action = 0; + result = FALSE; - if (sidebar->drag_data_info == DND_GTK_SIDEBAR_ROW) + if (G_VALUE_HOLDS (value, GTK_TYPE_SIDEBAR_ROW)) { GtkWidget **source_row; /* A bookmark got reordered */ @@ -1934,41 +1852,37 @@ drag_data_received_callback (GObject *source, g_object_get (sidebar->row_placeholder, "order-index", &target_order_index, NULL); reorder_bookmarks (sidebar, GTK_SIDEBAR_ROW (*source_row), target_order_index); - real_action = GDK_ACTION_MOVE; + result = TRUE; + } + else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) + { + /* Dropping URIs! */ + if (target_place_type == PLACES_DROP_FEEDBACK) + { + drop_files_as_bookmarks (sidebar, g_value_get_boxed (value), target_order_index); + } + else + { + GFile *dest_file = g_file_new_for_uri (target_uri); + + emit_drag_perform_drop (sidebar, + dest_file, + g_value_get_boxed (value), + gdk_drop_get_actions (gtk_drop_target_get_drop (target))); + + g_object_unref (dest_file); + } + result = TRUE; } else { - /* Dropping URIs! */ - - /* file transfer requested */ - real_action = gdk_drop_get_actions (drop); - - if (!gdk_drag_action_is_unique (real_action)) - real_action = emit_drag_action_ask (sidebar, real_action); - - if (real_action > 0) - { - if (target_place_type == PLACES_DROP_FEEDBACK) - { - drop_files_as_bookmarks (sidebar, sidebar->drag_list, target_order_index); - } - else - { - GFile *dest_file = g_file_new_for_uri (target_uri); - - emit_drag_perform_drop (sidebar, dest_file, sidebar->drag_list, real_action); - - g_object_unref (dest_file); - } - } + g_assert_not_reached (); } out: - sidebar->drop_occurred = FALSE; - g_object_set_data (G_OBJECT (drop), "places-sidebar-row", NULL); - gdk_drop_finish (drop, real_action); stop_drop_feedback (sidebar); g_free (target_uri); + return result; } static void @@ -2010,28 +1924,7 @@ drag_leave_callback (GtkDropTarget *dest, sidebar->drop_state = DROP_STATE_NORMAL; } - sidebar->drag_data_received = FALSE; sidebar->dragging_over = FALSE; - sidebar->drag_data_info = DND_UNKNOWN; -} - -static gboolean -drag_drop_callback (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - gpointer user_data) -{ - GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data); - gboolean retval = FALSE; - GtkListBoxRow *row; - - row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (sidebar->list_box), y); - sidebar->drop_occurred = TRUE; - - retval = get_drag_data (sidebar, dest, drop, row); - - return retval; } static void @@ -3842,11 +3735,10 @@ shell_shows_desktop_changed (GtkSettings *settings, static void gtk_places_sidebar_init (GtkPlacesSidebar *sidebar) { - GtkDropTarget *dest; + GtkDropTarget *target; gboolean show_desktop; GtkEventController *controller; GtkGesture *gesture; - GdkContentFormatsBuilder *builder; sidebar->cancellable = g_cancellable_new (); @@ -3901,20 +3793,18 @@ gtk_places_sidebar_init (GtkPlacesSidebar *sidebar) gtk_widget_add_controller (GTK_WIDGET (sidebar), GTK_EVENT_CONTROLLER (gesture)); /* DND support */ - builder = gdk_content_formats_builder_new (); - gdk_content_formats_builder_add_gtype (builder, GTK_TYPE_SIDEBAR_ROW); - gdk_content_formats_builder_add_gtype (builder, GDK_TYPE_FILE_LIST); - dest = gtk_drop_target_new (gdk_content_formats_builder_free_to_formats (builder), - GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); - g_signal_connect (dest, "drag-motion", G_CALLBACK (drag_motion_callback), sidebar); - g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop_callback), sidebar); - g_signal_connect (dest, "drag-leave", G_CALLBACK (drag_leave_callback), sidebar); - gtk_widget_add_controller (sidebar->list_box, GTK_EVENT_CONTROLLER (dest)); + target = gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); + gtk_drop_target_set_preload (target, TRUE); + gtk_drop_target_set_gtypes (target, (GType[2]) { GTK_TYPE_SIDEBAR_ROW, GDK_TYPE_FILE_LIST }, 2); + g_signal_connect (target, "enter", G_CALLBACK (drag_motion_callback), sidebar); + g_signal_connect (target, "motion", G_CALLBACK (drag_motion_callback), sidebar); + g_signal_connect (target, "drop", G_CALLBACK (drag_drop_callback), sidebar); + g_signal_connect (target, "leave", G_CALLBACK (drag_leave_callback), sidebar); + gtk_widget_add_controller (sidebar->list_box, GTK_EVENT_CONTROLLER (target)); sidebar->drag_row = NULL; sidebar->row_placeholder = NULL; sidebar->dragging_over = FALSE; - sidebar->drag_data_info = DND_UNKNOWN; gtk_container_add (GTK_CONTAINER (sidebar->swin), sidebar->list_box); @@ -4071,8 +3961,6 @@ gtk_places_sidebar_dispose (GObject *object) sidebar->cancellable = NULL; } - free_drag_data (sidebar); - if (sidebar->bookmarks_manager != NULL) { _gtk_bookmarks_manager_free (sidebar->bookmarks_manager); @@ -5068,7 +4956,7 @@ gtk_places_sidebar_set_drop_targets_visible (GtkPlacesSidebar *sidebar, if (visible) { sidebar->drop_state = DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT; - start_drop_feedback (sidebar); + start_drop_feedback (sidebar, NULL); } else { diff --git a/gtk/gtktext.c b/gtk/gtktext.c index 56f748d312..52af78a211 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -30,6 +30,9 @@ #include "gtkbutton.h" #include "gtkcssnodeprivate.h" #include "gtkdebug.h" +#include "gtkdragicon.h" +#include "gtkdragsource.h" +#include "gtkdroptarget.h" #include "gtkeditable.h" #include "gtkemojichooser.h" #include "gtkemojicompletion.h" @@ -65,9 +68,6 @@ #include "gtkwindow.h" #include "gtknative.h" #include "gtkactionmuxerprivate.h" -#include "gtkdragsource.h" -#include "gtkdragdest.h" -#include "gtkdragicon.h" #include "a11y/gtktextaccessible.h" @@ -190,7 +190,6 @@ struct _GtkTextPrivate int dnd_position; /* In chars, -1 == no DND cursor */ int drag_start_x; int drag_start_y; - int drop_position; /* where the drop should happen */ int insert_pos; int selection_bound; int scroll_offset; @@ -336,20 +335,18 @@ static void gtk_text_state_flags_changed (GtkWidget *widget, static void gtk_text_root (GtkWidget *widget); static gboolean gtk_text_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, + const GValue *value, + double x, + double y, GtkText *text); static gboolean gtk_text_drag_accept (GtkDropTarget *dest, GdkDrop *drop, GtkText *self); -static void gtk_text_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, +static GdkDragAction gtk_text_drag_motion (GtkDropTarget *dest, + double x, + double y, GtkText *text); static void gtk_text_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, GtkText *text); @@ -1712,7 +1709,7 @@ gtk_text_init (GtkText *self) GtkGesture *gesture; GtkEventController *controller; int i; - GtkDropTarget *dest; + GtkDropTarget *target; gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE); gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN); @@ -1734,13 +1731,13 @@ gtk_text_init (GtkText *self) priv->selection_content = g_object_new (GTK_TYPE_TEXT_CONTENT, NULL); GTK_TEXT_CONTENT (priv->selection_content)->self = self; - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (G_TYPE_STRING), - GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect (dest, "accept", G_CALLBACK (gtk_text_drag_accept), self); - g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_text_drag_motion), self); - g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_text_drag_leave), self); - g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_text_drag_drop), self); - gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (dest)); + target = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect (target, "accept", G_CALLBACK (gtk_text_drag_accept), self); + g_signal_connect (target, "enter", G_CALLBACK (gtk_text_drag_motion), self); + g_signal_connect (target, "motion", G_CALLBACK (gtk_text_drag_motion), self); + g_signal_connect (target, "leave", G_CALLBACK (gtk_text_drag_leave), self); + g_signal_connect (target, "drop", G_CALLBACK (gtk_text_drag_drop), self); + gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (target)); /* This object is completely private. No external entity can gain a reference * to it; so we create it here and destroy it in finalize(). @@ -6120,8 +6117,7 @@ gtk_text_selection_bubble_popup_set (GtkText *self) static void gtk_text_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, - GtkText *self) + GtkText *self) { GtkTextPrivate *priv = gtk_text_get_instance_private (self); GtkWidget *widget = GTK_WIDGET (self); @@ -6130,94 +6126,45 @@ gtk_text_drag_leave (GtkDropTarget *dest, gtk_widget_queue_draw (widget); } -static GdkDragAction -gtk_text_get_action (GtkText *self, - GdkDrop *drop) -{ - GtkTextPrivate *priv = gtk_text_get_instance_private (self); - GdkDrag *drag = gdk_drop_get_drag (drop); - GdkDragAction actions; - - actions = gdk_drop_get_actions (drop); - - if (drag == priv->drag && - actions & GDK_ACTION_MOVE) - return GDK_ACTION_MOVE; - - if (actions & GDK_ACTION_COPY) - return GDK_ACTION_COPY; - - if (actions & GDK_ACTION_MOVE) - return GDK_ACTION_MOVE; - - return 0; -} - -static void -got_text (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GdkDrop *drop = GDK_DROP (source); - GtkText *self = GTK_TEXT (data); - GtkTextPrivate *priv = gtk_text_get_instance_private (self); - char *str; - GdkDragAction action; - - str = gdk_drop_read_text_finish (drop, result, NULL); - action = gtk_text_get_action (self, drop); - - if (action && str) - { - int length = -1; - int pos; - - if (priv->truncate_multiline) - length = truncate_multiline (str); - - if (priv->selection_bound == priv->current_pos || - priv->drop_position < priv->selection_bound || - priv->drop_position > priv->current_pos) - { - gtk_text_insert_text (self, str, length, &priv->drop_position); - } - else - { - /* Replacing selection */ - begin_change (self); - gtk_text_delete_selection (self); - pos = MIN (priv->selection_bound, priv->current_pos); - gtk_text_insert_text (self, str, length, &pos); - end_change (self); - } - - gdk_drop_finish (drop, action); - } - else - { - /* Drag and drop didn't happen! */ - gdk_drop_finish (drop, 0); - } - - g_free (str); -} - static gboolean gtk_text_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, + const GValue *value, + double x, + double y, GtkText *self) { GtkTextPrivate *priv = gtk_text_get_instance_private (self); + int drop_position; + int length; + const char *str; - if (priv->editable && gdk_drop_has_value (drop, G_TYPE_STRING)) + if (!priv->editable) + return FALSE; + + drop_position = gtk_text_find_position (self, x + priv->scroll_offset); + + str = g_value_get_string (value); + if (priv->truncate_multiline) + length = truncate_multiline (str); + else + length = -1; + + if (priv->selection_bound == priv->current_pos || + drop_position < priv->selection_bound || + drop_position > priv->current_pos) { - priv->drop_position = gtk_text_find_position (self, x + priv->scroll_offset); - gdk_drop_read_text_async (drop, NULL, got_text, self); + gtk_text_insert_text (self, str, length, &drop_position); } else - gdk_drop_finish (drop, 0); + { + int pos; + /* Replacing selection */ + begin_change (self); + gtk_text_delete_selection (self); + pos = MIN (priv->selection_bound, priv->current_pos); + gtk_text_insert_text (self, str, length, &pos); + end_change (self); + } return TRUE; } @@ -6238,40 +6185,42 @@ gtk_text_drag_accept (GtkDropTarget *dest, return gdk_content_formats_match (gtk_drop_target_get_formats (dest), gdk_drop_get_formats (drop)); } -static void -gtk_text_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkText *self) +static GdkDragAction +gtk_text_drag_motion (GtkDropTarget *target, + double x, + double y, + GtkText *self) { GtkTextPrivate *priv = gtk_text_get_instance_private (self); int new_position, old_position; + if (!priv->editable) + { + gtk_drop_target_reject (target); + return 0; + } + old_position = priv->dnd_position; new_position = gtk_text_find_position (self, x + priv->scroll_offset); - if (priv->editable) + if (priv->selection_bound == priv->current_pos || + new_position < priv->selection_bound || + new_position > priv->current_pos) { - if (priv->selection_bound == priv->current_pos || - new_position < priv->selection_bound || - new_position > priv->current_pos) - { - priv->dnd_position = new_position; - } - else - { - priv->dnd_position = -1; - } + priv->dnd_position = new_position; } else { - /* Entry not editable, or no text */ priv->dnd_position = -1; } if (priv->dnd_position != old_position) gtk_widget_queue_draw (GTK_WIDGET (self)); + + if (priv->drag) + return GDK_ACTION_MOVE; + else + return GDK_ACTION_COPY; } /* We display the cursor when diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index a6cb66ceb5..032bc620e5 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -423,17 +423,16 @@ static GtkTextBuffer* gtk_text_view_create_buffer (GtkTextView *text_view); /* Target side drag signals */ static void gtk_text_view_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, GtkTextView *text_view); -static gboolean gtk_text_view_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, +static GdkDragAction + gtk_text_view_drag_motion (GtkDropTarget *dest, + double x, + double y, GtkTextView *text_view); static gboolean gtk_text_view_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, + const GValue *value, + double x, + double y, GtkTextView *text_view); static gboolean gtk_text_view_popup_menu (GtkWidget *widget); @@ -1752,10 +1751,11 @@ gtk_text_view_init (GtkTextView *text_view) priv->scroll_after_paste = FALSE; - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_TEXT_BUFFER), GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_text_view_drag_leave), text_view); - g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_text_view_drag_motion), text_view); - g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_text_view_drag_drop), text_view); + dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect (dest, "enter", G_CALLBACK (gtk_text_view_drag_motion), text_view); + g_signal_connect (dest, "motion", G_CALLBACK (gtk_text_view_drag_motion), text_view); + g_signal_connect (dest, "leave", G_CALLBACK (gtk_text_view_drag_leave), text_view); + g_signal_connect (dest, "drop", G_CALLBACK (gtk_text_view_drag_drop), text_view); gtk_widget_add_controller (GTK_WIDGET (text_view), GTK_EVENT_CONTROLLER (dest)); controller = gtk_drop_controller_motion_new (); @@ -7788,7 +7788,6 @@ gtk_text_view_start_selection_dnd (GtkTextView *text_view, static void gtk_text_view_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, GtkTextView *text_view) { GtkTextViewPrivate *priv = text_view->priv; @@ -7796,11 +7795,10 @@ gtk_text_view_drag_leave (GtkDropTarget *dest, gtk_text_mark_set_visible (priv->dnd_mark, FALSE); } -static gboolean +static GdkDragAction gtk_text_view_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, + double x, + double y, GtkTextView *text_view) { GtkTextViewPrivate *priv = text_view->priv; @@ -7834,117 +7832,50 @@ gtk_text_view_drag_motion (GtkDropTarget *dest, if (can_accept) { gtk_text_mark_set_visible (priv->dnd_mark, cursor_visible (text_view)); - gdk_drop_status (drop, GDK_ACTION_COPY | GDK_ACTION_MOVE); + if (text_view->priv->drag) + return GDK_ACTION_MOVE; + else + return GDK_ACTION_COPY; } else { - gdk_drop_status (drop, 0); gtk_text_mark_set_visible (priv->dnd_mark, FALSE); + return 0; } - - /* TRUE return means don't propagate the drag motion to parent - * widgets that may also be drop sites. - */ - return TRUE; } -static GdkDragAction -gtk_text_view_get_action (GtkTextView *textview, - GdkDrop *drop) +static gboolean +gtk_text_view_drag_drop (GtkDropTarget *dest, + const GValue *value, + double x, + double y, + GtkTextView *text_view) { - GdkDrag *drag = gdk_drop_get_drag (drop); - GdkDragAction actions; - - actions = gdk_drop_get_actions (drop); - - if (drag == textview->priv->drag && - actions & GDK_ACTION_MOVE) - return GDK_ACTION_MOVE; - - if (actions & GDK_ACTION_COPY) - return GDK_ACTION_COPY; - - if (actions & GDK_ACTION_MOVE) - return GDK_ACTION_MOVE; - - return 0; -} - -static void -got_text (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GdkDrop *drop = GDK_DROP (source); - GtkTextView *text_view = GTK_TEXT_VIEW (data); GtkTextViewPrivate *priv = text_view->priv; GtkTextBuffer *buffer; - char *str; GtkTextIter drop_point; - GdkDragAction action; - - str = gdk_drop_read_text_finish (drop, result, NULL); - if (!str) - { - gdk_drop_finish (drop, 0); - return; - } buffer = get_buffer (text_view); gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark); - action = gtk_text_view_get_action (text_view, drop); + if (!gtk_text_iter_can_insert (&drop_point, priv->editable)) + return FALSE; gtk_text_buffer_begin_user_action (buffer); if (!gtk_text_buffer_insert_interactive (buffer, - &drop_point, (gchar *) str, -1, + &drop_point, (gchar *) g_value_get_string (value), -1, text_view->priv->editable)) gtk_widget_error_bell (GTK_WIDGET (text_view)); - g_free (str); - gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark); gtk_text_buffer_place_cursor (buffer, &drop_point); gtk_text_buffer_end_user_action (buffer); - gdk_drop_finish (drop, action); -} - -static gboolean -gtk_text_view_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkTextView *text_view) -{ - GtkTextViewPrivate *priv = text_view->priv; - GtkTextIter drop_point; - GtkTextBuffer *buffer = NULL; - - gtk_text_mark_set_visible (priv->dnd_mark, FALSE); - - buffer = get_buffer (text_view); - gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark); - - if (!gtk_text_iter_can_insert (&drop_point, priv->editable)) - goto done; - - if (gtk_text_view_get_action (text_view, drop) == 0) - goto done; - - if (gdk_drop_has_value (drop, G_TYPE_STRING)) - { - gdk_drop_read_text_async (drop, NULL, got_text, text_view); - return TRUE; - } - -done: - gdk_drop_finish (drop, 0); return TRUE; } - + static void gtk_text_view_set_hadjustment (GtkTextView *text_view, GtkAdjustment *adjustment) diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index e56c489d9b..a966c8ef0e 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -31,9 +31,9 @@ #include "gtkcssnumbervalueprivate.h" #include "gtkcsscolorvalueprivate.h" #include "gtkcssstylepropertyprivate.h" -#include "gtkdragdest.h" #include "gtkdragsource.h" #include "gtkdragicon.h" +#include "gtkdroptargetasync.h" #include "gtkentryprivate.h" #include "gtksearchentryprivate.h" #include "gtkeventcontrollerkey.h" @@ -311,7 +311,7 @@ struct _TreeViewDragInfo GtkTreeRowReference *source_item; GtkCssNode *cssnode; - GtkDropTarget *dest; + GtkDropTargetAsync *dest; GdkModifierType start_button_mask; guint source_set : 1; @@ -667,8 +667,8 @@ static void gtk_tree_view_key_controller_key_released (GtkEventControllerKey guint keycode, GdkModifierType state, GtkTreeView *tree_view); -static void gtk_tree_view_focus_controller_focus_out (GtkEventController *focus, - GtkTreeView *tree_view); +static void gtk_tree_view_focus_controller_focus_out (GtkEventController *focus, + GtkTreeView *tree_view); static gint gtk_tree_view_focus (GtkWidget *widget, GtkDirectionType direction); @@ -690,22 +690,22 @@ static GdkContentProvider * gtk_tree_view_drag_data_get (GtkTreeView GtkTreePath *source_row); /* Target side drag signals */ -static void gtk_tree_view_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, - GtkTreeView *tree_view); -static void gtk_tree_view_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkTreeView *tree_view); -static gboolean gtk_tree_view_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkTreeView *tree_view); -static void gtk_tree_view_drag_data_received (GObject *source, - GAsyncResult *result, - gpointer data); +static void gtk_tree_view_drag_leave (GtkDropTargetAsync *dest, + GdkDrop *drop, + GtkTreeView *tree_view); +static GdkDragAction gtk_tree_view_drag_motion (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkTreeView *tree_view); +static gboolean gtk_tree_view_drag_drop (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkTreeView *tree_view); +static void gtk_tree_view_drag_data_received (GObject *source, + GAsyncResult *result, + gpointer data); /* tree_model signals */ static gboolean gtk_tree_view_real_move_cursor (GtkTreeView *tree_view, @@ -6859,13 +6859,13 @@ scroll_row_timeout (gpointer data) /* Returns TRUE if event should not be propagated to parent widgets */ static gboolean -set_destination_row (GtkTreeView *tree_view, - GtkDropTarget *dest, +set_destination_row (GtkTreeView *tree_view, + GtkDropTargetAsync *dest, /* coordinates relative to the widget */ - gint x, - gint y, - GdkDragAction *suggested_action, - GType *target) + gint x, + gint y, + GdkDragAction *suggested_action, + GType *target) { GtkTreePath *path = NULL; GtkTreeViewDropPosition pos; @@ -6899,7 +6899,7 @@ set_destination_row (GtkTreeView *tree_view, return FALSE; /* no longer a drop site */ } - formats = gtk_drop_target_get_formats (dest); + formats = gtk_drop_target_async_get_formats (dest); *target = gdk_content_formats_match_gtype (formats, formats); if (*target == G_TYPE_INVALID) return FALSE; @@ -7195,9 +7195,9 @@ gtk_tree_view_drag_data_get (GtkTreeView *tree_view, } static void -gtk_tree_view_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, - GtkTreeView *tree_view) +gtk_tree_view_drag_leave (GtkDropTargetAsync *dest, + GdkDrop *drop, + GtkTreeView *tree_view) { /* unset any highlight row */ gtk_tree_view_set_drag_dest_row (tree_view, @@ -7212,12 +7212,12 @@ gtk_tree_view_drag_leave (GtkDropTarget *dest, } -static void -gtk_tree_view_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkTreeView *tree_view) +static GdkDragAction +gtk_tree_view_drag_motion (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkTreeView *tree_view) { gboolean empty; GtkTreePath *path = NULL; @@ -7226,10 +7226,7 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest, GType target; if (!set_destination_row (tree_view, dest, x, y, &suggested_action, &target)) - { - gdk_drop_status (drop, 0); - return; - } + return 0; tree_view->event_last_x = x; tree_view->event_last_y = y; @@ -7241,8 +7238,7 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest, if (path == NULL && !empty) { - /* Can't drop here. */ - gdk_drop_status (drop, 0); + suggested_action = 0; } else { @@ -7270,21 +7266,22 @@ gtk_tree_view_drag_motion (GtkDropTarget *dest, else { set_status_pending (drop, 0); - gdk_drop_status (drop, suggested_action); } } if (path) gtk_tree_path_free (path); + + return suggested_action; } static gboolean -gtk_tree_view_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkTreeView *tree_view) +gtk_tree_view_drag_drop (GtkDropTargetAsync *dest, + GdkDrop *drop, + double x, + double y, + GtkTreeView *tree_view) { GtkTreePath *path; GdkDragAction suggested_action = 0; @@ -7436,8 +7433,6 @@ gtk_tree_view_drag_data_received (GObject *source, } } - gdk_drop_status (drop, suggested_action); - if (path) gtk_tree_path_free (path); @@ -12869,10 +12864,11 @@ gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view, di = ensure_info (tree_view); di->dest_set = TRUE; - di->dest = gtk_drop_target_new (gdk_content_formats_ref (formats), actions); + di->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions); g_signal_connect (di->dest, "drag-leave", G_CALLBACK (gtk_tree_view_drag_leave), tree_view); + g_signal_connect (di->dest, "drag-enter", G_CALLBACK (gtk_tree_view_drag_motion), tree_view); g_signal_connect (di->dest, "drag-motion", G_CALLBACK (gtk_tree_view_drag_motion), tree_view); - g_signal_connect (di->dest, "drag-drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view); + g_signal_connect (di->dest, "drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view); gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest)); g_object_ref (di->dest); diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 736bc8df15..0a28d8385d 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -5489,7 +5489,7 @@ gtk_widget_has_grab (GtkWidget *widget) { GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget); - g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + if (!GTK_IS_WIDGET (widget)) return FALSE; return priv->has_grab; } diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 58fd464912..2f6b536499 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -40,7 +40,7 @@ #include "gtkcsscolorvalueprivate.h" #include "gtkcssshadowvalueprivate.h" #include "gtkcssstylepropertyprivate.h" -#include "gtkdragdest.h" +#include "gtkdroptargetasync.h" #include "gtkeventcontrollerfocus.h" #include "gtkeventcontrollerkey.h" #include "gtkeventcontrollermotion.h" @@ -280,12 +280,6 @@ typedef struct GtkConstraintSolver *constraint_solver; } GtkWindowPrivate; -#ifdef GDK_WINDOWING_X11 -static const char *dnd_dest_targets [] = { - "application/x-rootwindow-drop" -}; -#endif - enum { SET_FOCUS, ACTIVATE_FOCUS, @@ -1767,6 +1761,18 @@ gtk_window_activate_default_activate (GtkWidget *widget, gtk_window_real_activate_default (GTK_WINDOW (widget)); } +static gboolean +gtk_window_accept_rootwindow_drop (GtkDropTargetAsync *self, + GdkDrop *drop, + double x, + double y, + gpointer unused) +{ + gdk_drop_finish (drop, GDK_ACTION_MOVE); + + return TRUE; +} + static void gtk_window_init (GtkWindow *window) { @@ -1776,9 +1782,7 @@ gtk_window_init (GtkWindow *window) GdkSeat *seat; GtkEventController *motion_controller; GtkEventController *controller; -#ifdef GDK_WINDOWING_X11 - GtkDropTarget *dest; -#endif + GtkDropTargetAsync *target; widget = GTK_WIDGET (window); @@ -1829,10 +1833,10 @@ gtk_window_init (GtkWindow *window) priv->scale = gtk_widget_get_scale_factor (widget); -#ifdef GDK_WINDOWING_X11 - dest = gtk_drop_target_new (gdk_content_formats_new (dnd_dest_targets, G_N_ELEMENTS (dnd_dest_targets)), GDK_ACTION_MOVE); - gtk_widget_add_controller (GTK_WIDGET (window), GTK_EVENT_CONTROLLER (dest)); -#endif + target = gtk_drop_target_async_new (gdk_content_formats_new ((const char*[1]) { "application/x-rootwindow-drop" }, 1), + GDK_ACTION_MOVE); + g_signal_connect (target, "drop", G_CALLBACK (gtk_window_accept_rootwindow_drop), NULL); + gtk_widget_add_controller (GTK_WIDGET (window), GTK_EVENT_CONTROLLER (target)); seat = gdk_display_get_default_seat (gtk_widget_get_display (widget)); g_signal_connect (seat, "device-removed", diff --git a/gtk/meson.build b/gtk/meson.build index ca72c02101..d68b54539c 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -98,6 +98,7 @@ gtk_private_sources = files([ 'gtkcssvalue.c', 'gtkcsswidgetnode.c', 'gtkcustomlayout.c', + 'gtkdrop.c', 'gtkfilechooserembed.c', 'gtkfilechooserentry.c', 'gtkfilechoosererrorstack.c', @@ -207,11 +208,12 @@ gtk_public_sources = files([ 'gtkcontainer.c', 'gtkcssprovider.c', 'gtkdialog.c', - 'gtkdragdest.c', 'gtkdragicon.c', 'gtkdragsource.c', 'gtkdrawingarea.c', 'gtkdropcontrollermotion.c', + 'gtkdroptarget.c', + 'gtkdroptargetasync.c', 'gtkeditable.c', 'gtkemojichooser.c', 'gtkemojicompletion.c', @@ -457,11 +459,12 @@ gtk_public_headers = files([ 'gtkcustomlayout.h', 'gtkdebug.h', 'gtkdialog.h', - 'gtkdragdest.h', 'gtkdragicon.h', 'gtkdragsource.h', 'gtkdrawingarea.h', 'gtkdropcontrollermotion.h', + 'gtkdroptarget.h', + 'gtkdroptargetasync.h', 'gtkeditable.h', 'gtkemojichooser.h', 'gtkentry.h', diff --git a/tests/testdnd.c b/tests/testdnd.c index f48a55b6af..ec1a5c2a08 100644 --- a/tests/testdnd.c +++ b/tests/testdnd.c @@ -287,93 +287,6 @@ static const char * trashcan_open_xpm[] = { GdkPixbuf *trashcan_open; GdkPixbuf *trashcan_closed; -gboolean have_drag; - -static const char *target_table[] = { - "STRING", - "text/plain", - "application/x-rootwindow-drop" -}; - -static guint n_targets = sizeof(target_table) / sizeof(target_table[0]); - -void -target_drag_leave (GtkDropTarget *dest, - GdkDrop *drop, - GtkWidget *widget) -{ - g_print("leave\n"); - have_drag = FALSE; - gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_closed); -} - -gboolean -target_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkWidget *widget) -{ - char *s; - - if (!have_drag) - { - have_drag = TRUE; - gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_open); - } - - s = gdk_content_formats_to_string (gdk_drop_get_formats (drop)); - g_print ("%s\n", s); - - gdk_drop_status (drop, GDK_ACTION_ALL); - - return TRUE; -} - -static void -got_text_in_target (GObject *object, - GAsyncResult *result, - gpointer data) -{ - char *str; - - str = gdk_drop_read_text_finish (GDK_DROP (object), result, NULL); - if (str) - { - g_print ("Received \"%s\" in target\n", str); - g_free (str); - } - - gdk_drop_finish (GDK_DROP (object), GDK_ACTION_MOVE); -} - -gboolean -target_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - GtkWidget *widget) -{ - GdkContentFormats *formats; - const char *format; - - g_print("drop\n"); - have_drag = FALSE; - - gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_closed); - - formats = gdk_drop_get_formats (drop); - format = gdk_content_formats_match_mime_type (formats, formats); - if (format) - { - gdk_drop_read_text_async (drop, NULL, got_text_in_target, widget); - return TRUE; - } - - gdk_drop_status (drop, 0); - - return FALSE; -} static GdkDragAction action_make_unique (GdkDragAction action) @@ -394,31 +307,67 @@ action_make_unique (GdkDragAction action) return 0; } -static void -got_text (GObject *object, - GAsyncResult *result, - gpointer data) +GdkDragAction +trash_drag_enter (GtkDropTarget *dest, + GdkDrop *drop, + double x, + double y, + GtkWidget *widget) { - char *str; + char *s; - str = gdk_drop_read_text_finish (GDK_DROP (object), result, NULL); - if (str) - { - g_print ("Received \"%s\" in label\n", str); - g_free (str); - } + gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_open); + + s = gdk_content_formats_to_string (gdk_drop_get_formats (drop)); + g_print ("trash enter: %s\n", s); + g_free (s); + + return action_make_unique (gdk_drop_get_actions (drop));; } - -void -label_drag_drop (GtkDropTarget *dest, + +GdkDragAction +trash_drag_leave (GtkDropTarget *dest, + GdkDrop *drop, + GtkWidget *widget) +{ + char *s; + + gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_closed); + + s = gdk_content_formats_to_string (gdk_drop_get_formats (drop)); + g_print ("trash leave: %s\n", s); + g_free (s); + + return action_make_unique (gdk_drop_get_actions (drop)); +} + +gboolean +trash_drag_drop (GtkDropTarget *dest, GdkDrop *drop, + double x, + double y, + GtkWidget *widget) +{ + char *s; + + s = gdk_content_formats_to_string (gdk_drop_get_formats (drop)); + g_print ("trash drop: %s\n", s); + g_free (s); + + gdk_drop_finish (drop, action_make_unique (gdk_drop_get_actions (drop))); + + return TRUE; +} + +gboolean +label_drag_drop (GtkDropTarget *dest, + const GValue *value, int x, int y, GtkWidget *widget) { - gdk_drop_read_text_async (drop, NULL, got_text, widget); - GdkDragAction action = action_make_unique (gdk_drop_get_actions (drop)); - gdk_drop_finish (drop, action); + g_print ("Received \"%s\" in label\n", g_value_get_string (value)); + return TRUE; } /* The following is a rather elaborate example demonstrating/testing @@ -443,18 +392,10 @@ popdown_cb (gpointer data) return FALSE; } -gboolean -popup_motion (GtkDropTarget *dest, - GdkDrop *drop) -{ - gdk_drop_status (drop, GDK_ACTION_COPY); - return TRUE; -} - void popup_enter (GtkDropTarget *dest) { -g_print ("popup enter\n"); + g_print ("popup enter\n"); if (!in_popup) { in_popup = TRUE; @@ -483,10 +424,8 @@ g_print ("popup leave\n"); } static gboolean -popup_drop (GtkDropTarget *dest, - GdkDrop *drop) +popup_drop (GtkDropTarget *dest) { - gdk_drop_finish (drop, GDK_ACTION_COPY); popdown_cb (NULL); return TRUE; } @@ -501,12 +440,10 @@ popup_cb (gpointer data) GtkWidget *button; GtkWidget *grid; int i, j; - GdkContentFormats *targets; popup_window = gtk_window_new (); grid = gtk_grid_new (); - targets = gdk_content_formats_new_for_gtype (G_TYPE_STRING); for (i=0; i<3; i++) for (j=0; j<3; j++) @@ -520,16 +457,13 @@ popup_cb (gpointer data) gtk_widget_set_vexpand (button, TRUE); gtk_grid_attach (GTK_GRID (grid), button, i, j, 1, 1); - dest = gtk_drop_target_new (targets, GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect (dest, "accept", G_CALLBACK (popup_motion), NULL); - g_signal_connect (dest, "drag-enter", G_CALLBACK (popup_enter), NULL); - g_signal_connect (dest, "drag-leave", G_CALLBACK (popup_leave), NULL); - g_signal_connect (dest, "drag-drop", G_CALLBACK (popup_drop), NULL); + dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect (dest, "enter", G_CALLBACK (popup_enter), NULL); + g_signal_connect (dest, "leave", G_CALLBACK (popup_leave), NULL); + g_signal_connect (dest, "drop", G_CALLBACK (popup_drop), NULL); gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (dest)); } gtk_container_add (GTK_CONTAINER (popup_window), grid); - gdk_content_formats_unref (targets); - } gtk_widget_show (popup_window); popped_up = TRUE; @@ -550,18 +484,17 @@ popsite_motion (GtkDropTarget *dest, } void -popsite_enter (GtkDropTarget *dest) +popsite_enter (GtkDropControllerMotion *motion) { -g_print ("popsite enter\n"); + g_print ("popsite enter\n"); if (!popup_timer) popup_timer = g_timeout_add (500, popup_cb, NULL); - } void -popsite_leave (GtkDropTarget *dest) +popsite_leave (GtkDropControllerMotion *motion) { -g_print ("popsite leave\n"); + g_print ("popsite leave\n"); if (popup_timer) { g_source_remove (popup_timer); @@ -606,8 +539,9 @@ main (int argc, char **argv) GdkTexture *texture; GdkContentProvider *content; GtkDragSource *source; - GdkContentFormats *targets; GtkDropTarget *dest; + GtkDropTargetAsync *async; + GtkEventController *controller; gboolean done = FALSE; test_init (); @@ -630,9 +564,8 @@ main (int argc, char **argv) label = gtk_label_new ("Drop Here\n"); - targets = gdk_content_formats_new (target_table, n_targets - 1); /* no rootwin */ - dest = gtk_drop_target_new (gdk_content_formats_ref (targets), GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect (dest, "drag-drop", G_CALLBACK (label_drag_drop), NULL); + dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect (dest, "drop", G_CALLBACK (label_drag_drop), NULL); gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest)); gtk_widget_set_hexpand (label, TRUE); @@ -641,22 +574,21 @@ main (int argc, char **argv) label = gtk_label_new ("Popup\n"); - dest = gtk_drop_target_new (targets, GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect (dest, "accept", G_CALLBACK (popsite_motion), NULL); - g_signal_connect (dest, "drag-enter", G_CALLBACK (popsite_enter), NULL); - g_signal_connect (dest, "drag-leave", G_CALLBACK (popsite_leave), NULL); - gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest)); + controller = gtk_drop_controller_motion_new (); + g_signal_connect (controller, "enter", G_CALLBACK (popsite_enter), NULL); + g_signal_connect (controller, "leave", G_CALLBACK (popsite_leave), NULL); + gtk_widget_add_controller (label, controller); gtk_widget_set_hexpand (label, TRUE); gtk_widget_set_vexpand (label, TRUE); gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1); pixmap = gtk_image_new_from_pixbuf (trashcan_closed); - dest = gtk_drop_target_new (NULL, 0); - g_signal_connect (dest, "drag-leave", G_CALLBACK (target_drag_leave), pixmap); - g_signal_connect (dest, "accept", G_CALLBACK (target_drag_motion), pixmap); - g_signal_connect (dest, "drag-drop", G_CALLBACK (target_drag_drop), pixmap); - gtk_widget_add_controller (pixmap, GTK_EVENT_CONTROLLER (dest)); + async = gtk_drop_target_async_new (NULL, 0); + g_signal_connect (async, "drag-enter", G_CALLBACK (trash_drag_enter), pixmap); + g_signal_connect (async, "drag-leave", G_CALLBACK (trash_drag_leave), pixmap); + g_signal_connect (async, "drop", G_CALLBACK (trash_drag_drop), pixmap); + gtk_widget_add_controller (pixmap, GTK_EVENT_CONTROLLER (async)); gtk_widget_set_hexpand (pixmap, TRUE); gtk_widget_set_vexpand (pixmap, TRUE); diff --git a/tests/testdnd2.c b/tests/testdnd2.c index 17b1abcbbb..2294b2539e 100644 --- a/tests/testdnd2.c +++ b/tests/testdnd2.c @@ -128,7 +128,7 @@ static void perform_drop (GdkDrop *drop, GtkWidget *image) { - if (gdk_drop_has_value (drop, GDK_TYPE_TEXTURE)) + if (gdk_content_formats_contain_gtype (gdk_drop_get_formats (drop), GDK_TYPE_TEXTURE)) gdk_drop_read_value_async (drop, GDK_TYPE_TEXTURE, G_PRIORITY_DEFAULT, NULL, got_texture, image); else { @@ -192,39 +192,37 @@ ask_actions (GdkDrop *drop, static gboolean delayed_deny (gpointer data) { - GtkDropTarget *dest = data; + GtkDropTargetAsync *dest = data; GtkWidget *image = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); GdkDrop *drop = GDK_DROP (g_object_get_data (G_OBJECT (image), "drop")); if (drop) { g_print ("denying drop, late\n"); - gtk_drop_target_deny_drop (dest, drop); + gtk_drop_target_async_reject_drop (dest, drop); } return G_SOURCE_REMOVE; } static gboolean -image_drag_motion (GtkDropTarget *dest, - GdkDrop *drop, - gpointer data) +image_drag_accept (GtkDropTargetAsync *dest, + GdkDrop *drop, + gpointer data) { GtkWidget *image = data; g_object_set_data_full (G_OBJECT (image), "drop", g_object_ref (drop), g_object_unref); g_timeout_add (1000, delayed_deny, dest); - gdk_drop_status (drop, gtk_drop_target_get_actions (dest)); - return TRUE; } static gboolean image_drag_drop (GtkDropTarget *dest, GdkDrop *drop, - int x, - int y, + double x, + double y, gpointer data) { GtkWidget *image = data; @@ -330,7 +328,7 @@ make_image (const gchar *icon_name, int hotspot) { GtkWidget *image; GtkDragSource *source; - GtkDropTarget *dest; + GtkDropTargetAsync *dest; GdkContentFormats *formats; GdkContentFormatsBuilder *builder; @@ -352,9 +350,9 @@ make_image (const gchar *icon_name, int hotspot) g_signal_connect (source, "drag-cancel", G_CALLBACK (drag_cancel), NULL); gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source)); - dest = gtk_drop_target_new (formats, GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_ASK); - g_signal_connect (dest, "accept", G_CALLBACK (image_drag_motion), image); - g_signal_connect (dest, "drag-drop", G_CALLBACK (image_drag_drop), image); + dest = gtk_drop_target_async_new (formats, GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_ASK); + g_signal_connect (dest, "accept", G_CALLBACK (image_drag_accept), image); + g_signal_connect (dest, "drop", G_CALLBACK (image_drag_drop), image); gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest)); return image; diff --git a/tests/testdnd3.c b/tests/testdnd3.c index 4b58ad136b..795445f65c 100644 --- a/tests/testdnd3.c +++ b/tests/testdnd3.c @@ -50,12 +50,6 @@ drag_cancel (GtkDragSource *source, drag_end (source, drag); } -typedef struct { - GtkWidget *canvas; - double x; - double y; -} DropData; - typedef struct { double x, y; double angle; @@ -76,32 +70,22 @@ apply_transform (GtkWidget *item) gsk_transform_unref (transform); } -static void -got_data (GObject *source, - GAsyncResult *result, - gpointer user_data) +static gboolean +drag_drop (GtkDropTarget *target, + const GValue *value, + double x, + double y) { - GdkDrop *drop = GDK_DROP (source); - DropData *data = user_data; GtkWidget *item; - const GValue *value; TransformData *transform_data; GtkWidget *canvas; GtkWidget *last_child; - value = gdk_drop_read_value_finish (drop, result, NULL); - if (value == NULL) - { - gdk_drop_finish (drop, 0); - return; - } - item = g_value_get_object (value); - transform_data = g_object_get_data (G_OBJECT (item), "transform-data"); - transform_data->x = data->x; - transform_data->y = data->y; + transform_data->x = x; + transform_data->y = y; canvas = gtk_widget_get_parent (item); last_child = gtk_widget_get_last_child (canvas); @@ -110,26 +94,6 @@ got_data (GObject *source, apply_transform (item); - gdk_drop_finish (drop, GDK_ACTION_MOVE); - - g_free (data); -} - -static gboolean -drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y) -{ - DropData *data; - - data = g_new (DropData, 1); - data->canvas = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); - data->x = x; - data->y = y; - - gdk_drop_read_value_async (drop, GTK_TYPE_WIDGET, G_PRIORITY_DEFAULT, NULL, got_data, data); - return TRUE; } @@ -153,8 +117,8 @@ canvas_new (void) g_signal_connect (source, "drag-cancel", G_CALLBACK (drag_cancel), NULL); gtk_widget_add_controller (canvas, GTK_EVENT_CONTROLLER (source)); - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_WIDGET), GDK_ACTION_MOVE); - g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), NULL); + dest = gtk_drop_target_new (GTK_TYPE_WIDGET, GDK_ACTION_MOVE); + g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), NULL); gtk_widget_add_controller (canvas, GTK_EVENT_CONTROLLER (dest)); return canvas; @@ -186,31 +150,16 @@ set_color (GtkWidget *item, g_free (css); } -static void -got_color (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GdkDrop *drop = GDK_DROP (source); - GtkDropTarget *dest = data; - GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); - const GValue *value; - GdkRGBA *color; - - value = gdk_drop_read_value_finish (drop, result, NULL); - color = g_value_get_boxed (value); - set_color (item, color); - - gdk_drop_finish (drop, GDK_ACTION_COPY); -} - static gboolean item_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y) + const GValue *value, + double x, + double y) { - gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, dest); + GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); + + set_color (item, g_value_get_boxed (value)); + return TRUE; } @@ -261,7 +210,6 @@ canvas_item_new (int i, TransformData *transform_data; GdkRGBA rgba; GtkDropTarget *dest; - GdkContentFormats *formats; GtkGesture *gesture; label = g_strdup_printf ("Item %d", i); @@ -283,11 +231,9 @@ canvas_item_new (int i, g_free (label); g_free (id); - formats = gdk_content_formats_new_for_gtype (GDK_TYPE_RGBA); - dest = gtk_drop_target_new (formats, GDK_ACTION_COPY); - g_signal_connect (dest, "drag-drop", G_CALLBACK (item_drag_drop), NULL); + dest = gtk_drop_target_new (GDK_TYPE_RGBA, GDK_ACTION_COPY); + g_signal_connect (dest, "drop", G_CALLBACK (item_drag_drop), NULL); gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (dest)); - gdk_content_formats_unref (formats); gesture = gtk_gesture_rotate_new (); g_signal_connect (gesture, "angle-changed", G_CALLBACK (angle_changed), NULL); diff --git a/tests/testlist3.c b/tests/testlist3.c index 1e2825de2b..88d1713716 100644 --- a/tests/testlist3.c +++ b/tests/testlist3.c @@ -29,47 +29,30 @@ drag_begin (GtkDragSource *source, g_object_unref (paintable); } -static void -got_row (GObject *src, - GAsyncResult *result, - gpointer data) +static gboolean +drag_drop (GtkDropTarget *dest, + const GValue *value, + double x, + double y, + gpointer data) { - GdkDrop *drop = GDK_DROP (src); GtkWidget *target = data; GtkWidget *source; int pos; - source = g_value_get_object (gdk_drop_read_value_finish (drop, result, NULL)); + source = g_value_get_object (value); if (source == NULL) - { - gdk_drop_finish (drop, 0); - return; - } + return FALSE; pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (target)); if (source == target) - { - gdk_drop_finish (drop, 0); - return; - } + return FALSE; g_object_ref (source); gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (source)), source); gtk_list_box_insert (GTK_LIST_BOX (gtk_widget_get_parent (target)), source, pos); g_object_unref (source); - gdk_drop_finish (drop, GDK_ACTION_MOVE); -} - -static gboolean -drag_drop (GtkDropTarget *dest, - GdkDrop *drop, - int x, - int y, - gpointer data) -{ - gdk_drop_read_value_async (drop, GTK_TYPE_LIST_BOX_ROW, G_PRIORITY_DEFAULT, NULL, got_row, data); - return TRUE; } @@ -96,8 +79,8 @@ create_row (const gchar *text) g_signal_connect (source, "prepare", G_CALLBACK (prepare), row); gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source)); - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (GTK_TYPE_LIST_BOX_ROW), GDK_ACTION_MOVE); - g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), row); + dest = gtk_drop_target_new (GTK_TYPE_LIST_BOX_ROW, GDK_ACTION_MOVE); + g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), row); gtk_widget_add_controller (GTK_WIDGET (row), GTK_EVENT_CONTROLLER (dest)); return row; diff --git a/tests/testnotebookdnd.c b/tests/testnotebookdnd.c index f7050f04a9..d8d8077d63 100644 --- a/tests/testnotebookdnd.c +++ b/tests/testnotebookdnd.c @@ -55,10 +55,6 @@ gchar *tabs4 [] = { NULL }; -static const char *button_targets[] = { - "GTK_NOTEBOOK_TAB" -}; - static GtkNotebook* window_creation_function (GtkNotebook *source_notebook, GtkWidget *child, @@ -93,7 +89,8 @@ on_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint page_num, gpoi static gboolean remove_in_idle (gpointer data) { - GtkWidget *child = data; + GtkNotebookPage *page = data; + GtkWidget *child = gtk_notebook_page_get_child (page); GtkWidget *parent = gtk_widget_get_ancestor (child, GTK_TYPE_NOTEBOOK); GtkWidget *tab_label; @@ -104,44 +101,17 @@ remove_in_idle (gpointer data) return G_SOURCE_REMOVE; } -static void -got_page (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GdkDrop *drop = GDK_DROP (source); - GInputStream *stream; - const char *mime_type; - - stream = gdk_drop_read_finish (drop, result, &mime_type, NULL); - - if (stream) - { - GBytes *bytes; - GtkWidget **child; - - bytes = g_input_stream_read_bytes (stream, sizeof (gpointer), NULL, NULL); - child = (gpointer)g_bytes_get_data (bytes, NULL); - - g_idle_add (remove_in_idle, *child); - - gdk_drop_finish (drop, GDK_ACTION_MOVE); - - g_bytes_unref (bytes); - g_object_unref (stream); - } - else - gdk_drop_finish (drop, 0); -} - static gboolean on_button_drag_drop (GtkDropTarget *dest, - GdkDrop *drop, + const GValue *value, + double x, + double y, gpointer user_data) { - gdk_drop_read_async (drop, (const char *[]) { "GTK_NOTEBOOK_TAB", NULL }, G_PRIORITY_DEFAULT, NULL, got_page, NULL); + GtkNotebookPage *page; - gdk_drop_finish (drop, GDK_ACTION_MOVE); + page = g_value_get_object (value); + g_idle_add (remove_in_idle, page); return TRUE; } @@ -298,8 +268,8 @@ create_trash_button (void) button = gtk_button_new_with_mnemonic ("_Delete"); - dest = gtk_drop_target_new (gdk_content_formats_new (button_targets, G_N_ELEMENTS (button_targets)), GDK_ACTION_MOVE); - g_signal_connect (dest, "drag-drop", G_CALLBACK (on_button_drag_drop), NULL); + dest = gtk_drop_target_new (GTK_TYPE_NOTEBOOK_PAGE, GDK_ACTION_MOVE); + g_signal_connect (dest, "drop", G_CALLBACK (on_button_drag_drop), NULL); gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (dest)); return button; diff --git a/tests/testtreednd.c b/tests/testtreednd.c index c087527ce3..04450938e2 100644 --- a/tests/testtreednd.c +++ b/tests/testtreednd.c @@ -84,32 +84,16 @@ get_dragsource (void) return GTK_WIDGET (tv); } -static void -got_text (GObject *source, - GAsyncResult *result, - gpointer data) -{ - GdkDrop *drop = GDK_DROP (source); - GtkWidget *widget = data; - const GValue *value; - - value = gdk_drop_read_value_finish (drop, result, NULL); - if (value == NULL) - return; - - gtk_label_set_label (GTK_LABEL (widget), g_value_get_string (value)); -} - static void drag_drop (GtkDropTarget *dest, - GdkDrop *drop, + const GValue *value, int x, int y, gpointer dada) { GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)); - gdk_drop_read_value_async (drop, G_TYPE_STRING, G_PRIORITY_DEFAULT, NULL, got_text, widget); + gtk_label_set_label (GTK_LABEL (widget), g_value_get_string (value)); } static GtkWidget * @@ -119,8 +103,8 @@ get_droptarget (void) GtkDropTarget *dest; label = gtk_label_new ("Drop here"); - dest = gtk_drop_target_new (gdk_content_formats_new_for_gtype (G_TYPE_STRING), GDK_ACTION_COPY); - g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), NULL); + dest = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY); + g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), NULL); gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest)); return label; diff --git a/testsuite/gtk/defaultvalue.c b/testsuite/gtk/defaultvalue.c index 29536fe037..ab735dc482 100644 --- a/testsuite/gtk/defaultvalue.c +++ b/testsuite/gtk/defaultvalue.c @@ -150,7 +150,8 @@ test_type (gconstpointer data) /* These are set in init() */ if ((g_type_is_a (type, GDK_TYPE_CLIPBOARD) || - g_type_is_a (type, GDK_TYPE_CONTENT_PROVIDER)) && + g_type_is_a (type, GDK_TYPE_CONTENT_PROVIDER) || + g_type_is_a (type, GTK_TYPE_DROP_TARGET)) && strcmp (pspec->name, "formats") == 0) continue;