[gtk/dnd-gestures-2: 31/66] Add a GtkDropTarget object



commit 032ba316859816d628c5672aa66d05a67f5ba53f
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Dec 31 01:54:43 2019 -0500

    Add a GtkDropTarget object
    
    Add an explicit GtkDropTarget object, and move the destination-side
    DND signals here. The object is used by connecting to its signals
    and attaching it to a widget with gtk_drop_target_attach().

 gtk/gtkdnd.c             |  89 +++---
 gtk/gtkdndprivate.h      |   4 +-
 gtk/gtkdragdest.c        | 699 ++++++++++++++++++++++++++++++++++++++++++++---
 gtk/gtkdragdest.h        |  69 ++++-
 gtk/gtkdragdestprivate.h |  46 ++++
 gtk/gtkdragsource.c      |  10 +-
 6 files changed, 838 insertions(+), 79 deletions(-)
---
diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c
index 20bcb774e1..dc6e5d8cfe 100644
--- a/gtk/gtkdnd.c
+++ b/gtk/gtkdnd.c
@@ -26,7 +26,7 @@
 
 #include "gtkdndprivate.h"
 
-#include "gtkdragdest.h"
+#include "gtkdragdestprivate.h"
 #include "gtkimageprivate.h"
 #include "gtkintl.h"
 #include "gtkmain.h"
@@ -111,6 +111,7 @@ static GtkDragDestInfo *  gtk_drag_get_dest_info     (GdkDrop        *drop,
  * Destination side *
  ********************/
 
+
 typedef struct {
   GdkDrop *drop;
   GtkWidget *widget;
@@ -124,6 +125,9 @@ gtk_drag_get_data_finish (GtkDragGetData *data,
 {
   GtkDragDestSite *site;
   GtkSelectionData sdata;
+  GdkContentFormats *target_list = NULL;
+  GdkDragAction actions = 0;
+  GtkDestDefaults flags = 0;
 
   site = g_object_get_data (G_OBJECT (data->widget), "gtk-drag-dest");
 
@@ -133,30 +137,30 @@ gtk_drag_get_data_finish (GtkDragGetData *data,
   sdata.length = size;
   sdata.data = bytes ? bytes : (guchar *)g_strdup ("");
   sdata.display = gtk_widget_get_display (data->widget);
-  
-  if (site && site->target_list)
+
+  if (site)
+    {
+      target_list = gtk_drop_target_get_formats (site->dest);
+      actions = gtk_drop_target_get_actions (site->dest);
+      flags = gtk_drop_target_get_defaults (site->dest);
+    }
+
+  if (target_list)
     {
-      if (gdk_content_formats_contain_mime_type (site->target_list, data->mime_type))
+      if (gdk_content_formats_contain_mime_type (target_list, data->mime_type))
         {
-          if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
-              size >= 0)
-            g_signal_emit_by_name (data->widget,
-                                   "drag-data-received",
-                                   data->drop,
-                                   &sdata);
+          if (!(flags & GTK_DEST_DEFAULT_DROP) || size >= 0)
+            gtk_drop_target_emit_drag_data_received (site->dest, data->drop, &sdata);
         }
     }
   else
     {
-      g_signal_emit_by_name (data->widget,
-                             "drag-data-received",
-                             data->drop,
-                             &sdata);
+      gtk_drop_target_emit_drag_data_received (site->dest, data->drop, &sdata);
     }
   
-  if (site && site->flags & GTK_DEST_DEFAULT_DROP)
+  if (flags & GTK_DEST_DEFAULT_DROP)
     {
-      GdkDragAction action = site->actions & gdk_drop_get_actions (data->drop);
+      GdkDragAction action = actions & gdk_drop_get_actions (data->drop);
 
       if (size == 0)
         action = 0;
@@ -535,16 +539,20 @@ gtk_drag_dest_leave (GtkWidget      *widget,
                      guint           time)
 {
   GtkDragDestSite *site;
+  GtkDestDefaults flags;
+  gboolean track_motion;
 
   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
   g_return_if_fail (site != NULL);
 
-  if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
+  flags = gtk_drop_target_get_defaults (site->dest);
+  track_motion = gtk_drop_target_get_track_motion (site->dest);
+
+  if ((flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
     gtk_drag_unhighlight (widget);
 
-  if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
-      site->track_motion)
-    g_signal_emit_by_name (widget, "drag-leave", drop, time);
+  if (!(flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag || track_motion)
+    gtk_drop_target_emit_drag_leave (site->dest, drop, time);
   
   site->have_drag = FALSE;
 }
@@ -557,43 +565,52 @@ gtk_drag_dest_motion (GtkWidget *widget,
                       guint      time)
 {
   GtkDragDestSite *site;
+  GdkDragAction dest_actions;
+  GtkDestDefaults flags;
+  gboolean track_motion;
   gboolean retval;
 
   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
   g_return_val_if_fail (site != NULL, FALSE);
 
-  if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
+  dest_actions = gtk_drop_target_get_actions (site->dest);
+  flags = gtk_drop_target_get_defaults (site->dest);
+  track_motion = gtk_drop_target_get_track_motion (site->dest);
+
+  if (track_motion || flags & GTK_DEST_DEFAULT_MOTION)
     {
       GdkDragAction actions;
+      GdkAtom target;
       
       actions = gdk_drop_get_actions (drop);
 
-      if ((actions & site->actions) == 0)
+      if ((dest_actions & actions) == 0)
         actions = 0;
 
-      if (actions && gtk_drag_dest_find_target (widget, drop, NULL))
+      target = gtk_drop_target_match (site->dest, drop);
+
+      if (actions && target)
         {
           if (!site->have_drag)
             {
               site->have_drag = TRUE;
-              if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
+              if (flags & GTK_DEST_DEFAULT_HIGHLIGHT)
                 gtk_drag_highlight (widget);
             }
 
-          gdk_drop_status (drop, site->actions);
+          gdk_drop_status (drop, dest_actions);
         }
       else
         {
           gdk_drop_status (drop, 0);
-          if (!site->track_motion)
+          if (!track_motion)
             return TRUE;
         }
     }
 
-  g_signal_emit_by_name (widget, "drag-motion",
-                         drop, x, y, &retval);
+  retval = gtk_drop_target_emit_drag_motion (site->dest, drop, x, y);
 
-  return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
+  return (flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
 }
 
 static gboolean
@@ -605,17 +622,22 @@ gtk_drag_dest_drop (GtkWidget *widget,
 {
   GtkDragDestSite *site;
   GtkDragDestInfo *info;
+  GtkDestDefaults flags;
   gboolean retval;
 
   site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
   g_return_val_if_fail (site != NULL, FALSE);
 
+  flags = gtk_drop_target_get_defaults (site->dest);
+
   info = gtk_drag_get_dest_info (drop, FALSE);
   g_return_val_if_fail (info != NULL, FALSE);
 
-  if (site->flags & GTK_DEST_DEFAULT_DROP)
+  if (flags & GTK_DEST_DEFAULT_DROP)
     {
-      GdkAtom target = gtk_drag_dest_find_target (widget, drop, NULL);
+      GdkAtom target;
+
+      target = gtk_drop_target_match (site->dest, drop);
 
       if (target == NULL)
         {
@@ -626,8 +648,7 @@ gtk_drag_dest_drop (GtkWidget *widget,
         gtk_drag_get_data (widget, drop, target);
     }
 
-  g_signal_emit_by_name (widget, "drag-drop",
-                         drop, x, y, &retval);
+  retval = gtk_drop_target_emit_drag_drop (site->dest, drop, x, y);
 
-  return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
+  return (flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
 }
diff --git a/gtk/gtkdndprivate.h b/gtk/gtkdndprivate.h
index 45448a17e2..65dce27d7e 100644
--- a/gtk/gtkdndprivate.h
+++ b/gtk/gtkdndprivate.h
@@ -29,13 +29,11 @@
 typedef struct _GtkDragDestSite GtkDragDestSite;
 struct _GtkDragDestSite
 {
+  GtkDropTarget     *dest;
   GtkDestDefaults    flags;
-  GdkContentFormats *target_list;
-  GdkDragAction      actions;
   guint              do_proxy     : 1;
   guint              proxy_coords : 1;
   guint              have_drag    : 1;
-  guint              track_motion : 1;
 };
 
 G_BEGIN_DECLS
diff --git a/gtk/gtkdragdest.c b/gtk/gtkdragdest.c
index da2205e09d..f158b3acf2 100644
--- a/gtk/gtkdragdest.c
+++ b/gtk/gtkdragdest.c
@@ -25,11 +25,15 @@
 #include "config.h"
 
 #include "gtkdragdest.h"
+#include "gtkdragdestprivate.h"
 
 #include "gtkdnd.h"
 #include "gtkdndprivate.h"
 #include "gtkintl.h"
 #include "gtknative.h"
+#include "gtktypebuiltins.h"
+#include "gtkeventcontroller.h"
+#include "gtkmarshalers.h"
 
 
 static void
@@ -56,8 +60,7 @@ gtk_drag_dest_site_destroy (gpointer data)
 {
   GtkDragDestSite *site = data;
 
-  if (site->target_list)
-    gdk_content_formats_unref (site->target_list);
+  g_clear_object (&site->dest);
 
   g_slice_free (GtkDragDestSite, site);
 }
@@ -71,23 +74,16 @@ gtk_drag_dest_set_internal (GtkWidget       *widget,
   old_site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest"));
   if (old_site)
     {
-      g_signal_handlers_disconnect_by_func (widget,
-                                            gtk_drag_dest_realized,
-                                            old_site);
-      g_signal_handlers_disconnect_by_func (widget,
-                                            gtk_drag_dest_hierarchy_changed,
-                                            old_site);
-
-      site->track_motion = old_site->track_motion;
+      g_signal_handlers_disconnect_by_func (widget, gtk_drag_dest_realized, old_site);
+      g_signal_handlers_disconnect_by_func (widget, gtk_drag_dest_hierarchy_changed, old_site);
+      gtk_drop_target_set_track_motion (site->dest, gtk_drop_target_get_track_motion (old_site->dest));
     }
 
   if (gtk_widget_get_realized (widget))
     gtk_drag_dest_realized (widget);
 
-  g_signal_connect (widget, "realize",
-                    G_CALLBACK (gtk_drag_dest_realized), site);
-  g_signal_connect (widget, "notify::root",
-                    G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
+  g_signal_connect (widget, "realize", G_CALLBACK (gtk_drag_dest_realized), site);
+  g_signal_connect (widget, "notify::root", G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
 
   g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
                           site, gtk_drag_dest_site_destroy);
@@ -142,7 +138,7 @@ gtk_drag_dest_set_internal (GtkWidget       *widget,
  * }
  * ]|
  */
-void
+GtkDropTarget *
 gtk_drag_dest_set (GtkWidget         *widget,
                    GtkDestDefaults    flags,
                    GdkContentFormats *targets,
@@ -150,20 +146,16 @@ gtk_drag_dest_set (GtkWidget         *widget,
 {
   GtkDragDestSite *site;
 
-  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
 
   site = g_slice_new0 (GtkDragDestSite);
 
-  site->flags = flags;
+  site->dest = gtk_drop_target_new (flags, targets, actions);
   site->have_drag = FALSE;
-  if (targets)
-    site->target_list = gdk_content_formats_ref (targets);
-  else
-    site->target_list = NULL;
-  site->actions = actions;
-  site->track_motion = FALSE;
 
   gtk_drag_dest_set_internal (widget, site);
+
+  return site->dest;
 }
 
 /**
@@ -213,7 +205,7 @@ gtk_drag_dest_get_target_list (GtkWidget *widget)
 
   site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest"));
 
-  return site ? site->target_list : NULL;
+  return site ? gtk_drop_target_get_formats (site->dest) : NULL;
 }
 
 /**
@@ -242,13 +234,7 @@ gtk_drag_dest_set_target_list (GtkWidget     *widget,
       return;
     }
 
-  if (target_list)
-    gdk_content_formats_ref (target_list);
-
-  if (site->target_list)
-    gdk_content_formats_unref (site->target_list);
-
-  site->target_list = target_list;
+  gtk_drop_target_set_formats (site->dest, target_list);
 }
 
 /**
@@ -350,7 +336,7 @@ gtk_drag_dest_set_track_motion (GtkWidget *widget,
 
   g_return_if_fail (site != NULL);
 
-  site->track_motion = track_motion != FALSE;
+  gtk_drop_target_set_track_motion (site->dest, track_motion);
 }
 
 /**
@@ -373,7 +359,7 @@ gtk_drag_dest_get_track_motion (GtkWidget *widget)
   site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest"));
 
   if (site)
-    return site->track_motion;
+    return gtk_drop_target_get_track_motion (site->dest);
 
   return FALSE;
 }
@@ -414,3 +400,650 @@ gtk_drag_dest_find_target (GtkWidget         *widget,
                                               gdk_drop_get_formats (drop));
 }
 
+
+/**
+ * SECTION:gtkdragdest
+ * @Short_description: An object 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, connect to its signals, and then attach
+ * it to the widgtet with gtk_drop_target_attach().
+ */
+
+struct _GtkDropTarget
+{
+  GObject parent_instance;
+
+  GdkContentFormats *formats;
+  GdkDragAction actions;
+  GtkDestDefaults defaults;
+  gboolean track_motion;
+
+  GtkWidget *widget;
+  GdkDrop *drop;
+};
+
+struct _GtkDropTargetClass
+{
+  GObjectClass parent_class;
+};
+
+enum {
+  PROP_FORMATS = 1,
+  PROP_ACTIONS,
+  PROP_DEFAULTS,
+  PROP_TRACK_MOTION,
+  NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+enum {
+  DRAG_LEAVE,
+  DRAG_MOTION,
+  DRAG_DROP,
+  DRAG_DATA_RECEIVED,
+  NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS];
+
+G_DEFINE_TYPE (GtkDropTarget, gtk_drop_target, G_TYPE_OBJECT);
+
+static void
+gtk_drop_target_init (GtkDropTarget *dest)
+{
+  dest->defaults = GTK_DEST_DEFAULT_ALL;
+}
+
+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;
+
+    case PROP_DEFAULTS:
+      gtk_drop_target_set_defaults (dest, g_value_get_flags (value));
+      break;
+
+    case PROP_TRACK_MOTION:
+      gtk_drop_target_set_track_motion (dest, 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 *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_DEFAULTS:
+      g_value_set_flags (value, gtk_drop_target_get_defaults (dest));
+      break;
+
+    case PROP_TRACK_MOTION:
+      g_value_set_boolean (value, gtk_drop_target_get_track_motion (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);
+
+  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;
+
+  /**
+   * 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 are supported
+   */ 
+  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);
+
+  /**
+   * GtkDropTargets:defaults:
+   *
+   * Flags that determine the default behavior 
+   */
+  properties[PROP_DEFAULTS] =
+       g_param_spec_flags ("defaults", P_("Defaults"), P_("Defaults"),
+                           GTK_TYPE_DEST_DEFAULTS, GTK_DEST_DEFAULT_ALL,
+                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GtkDropTarget:track-motion:
+   *
+   * Whether the drop target should emit #GtkDropTarget::drag-motion signals
+   * unconditionally
+   */
+  properties[PROP_TRACK_MOTION] =
+       g_param_spec_boolean ("track-motion", P_("Track motion"), P_("Track motion"),
+                             FALSE,
+                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+
+  /**
+   * GtkDropTarget::drag-leave:
+   * @dest: the #GtkDropTarget
+   *
+   * The ::drag-leave signal is emitted on the drop site when the cursor
+   * leaves the widget. A typical reason to connect to this signal is to
+   * undo things done in #GtkDropTarget::drag-motion, e.g. undo highlighting.
+   *
+   * Likewise, the #GtkWidget::drag-leave signal is also emitted before the 
+   * #GtkDropTarget::drag-drop signal, for instance to allow cleaning up of
+   * a preview item created in the #GtkDropTarget::drag-motion signal handler.
+   */
+  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, 0);
+
+ /**
+   * GtkWidget::drag-motion:
+   * @dest: the #GtkDropTarget
+   * @x: the x coordinate of the current cursor position
+   * @y: the y coordinate of the current cursor position
+   *
+   * The ::drag-motion 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().
+   *
+   * If the decision whether the drop will be accepted or rejected can't be
+   * made based solely on the cursor position and the type of the data, the
+   * handler may inspect the dragged data by calling one of the #GdkDrop
+   * read functions, and defer the gdk_drag_status() call to when it has
+   * received th data.
+   *
+   * Note that you must pass #GTK_DEST_DEFAULT_DROP,
+   * #GTK_DEST_DEFAULT_MOTION or #GTK_DEST_DEFAULT_ALL to gtk_drag_dest_set()
+   * when using the ::drag-motion signal that way.
+   *
+   * Also note that there is no drag-enter signal. The drag receiver has to
+   * keep track of whether he has received any drag-motion signals since the
+   * last #GtkWidget::drag-leave and if not, treat the drag-motion signal as
+   * an "enter" signal. Upon an "enter", the handler will typically highlight
+   * the drop site with gtk_drag_highlight().
+   *
+   * Returns: whether the cursor position is in a drop zone
+   */
+  signals[DRAG_MOTION] =
+      g_signal_new (I_("drag-motion"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    NULL, NULL,
+                    NULL,
+                    G_TYPE_BOOLEAN, 2,
+                    G_TYPE_INT, G_TYPE_INT);
+
+  /**
+   * GtkDropTarget::drag-drop:
+   * @dest: the #GtkDropTarget
+   * @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.
+   *
+   * 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, 2,
+                    G_TYPE_INT, G_TYPE_INT);
+
+  signals[DRAG_DATA_RECEIVED] =
+      g_signal_new (I_("drag-data-received"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    NULL, NULL,
+                    _gtk_marshal_VOID__BOXED,
+                    G_TYPE_NONE, 1,
+                    GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE);
+  g_signal_set_va_marshaller (signals[DRAG_DATA_RECEIVED],
+                              G_TYPE_FROM_CLASS (class),
+                              _gtk_marshal_VOID__BOXEDv);
+}
+
+/**
+ * gtk_drop_target_new:
+ * @defaults: flags determining the default behaviour
+ * @formats: (nullable): the supported data formats
+ * @actions: the supported actions
+ *
+ * Creates a new #GtkDropTarget object
+ *
+ * Returns: the new #GtkDropTarget
+ */
+GtkDropTarget *
+gtk_drop_target_new (GtkDestDefaults    defaults,
+                     GdkContentFormats *formats,
+                     GdkDragAction      actions)
+{
+  return g_object_new (GTK_TYPE_DROP_TARGET,
+                       "defaults", defaults,
+                       "formats", formats,
+                       "actions", actions,
+                       NULL);
+}
+
+/**
+ * gtk_drop_target_set_formats:
+ * @dest: a #GtkDropTarget
+ * @formats: (nullable): the supported data formats
+ *
+ * Sets th 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.
+ *
+ * Returns: 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:
+ * @dst: 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_set_defaults:
+ * @dest: a #GtkDropTarget
+ * @defaults: flags determining the default behaviour
+ *
+ * Sets the flags determining the behavior of the drop target.
+ */
+void
+gtk_drop_target_set_defaults (GtkDropTarget   *dest,
+                              GtkDestDefaults  defaults)
+{
+  g_return_if_fail (GTK_IS_DROP_TARGET (dest));
+  
+  if (dest->defaults == defaults)
+    return;
+
+  dest->defaults = defaults;
+
+  g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_DEFAULTS]);
+}
+
+/**
+ * gtk_drop_target_get_defaults:
+ * @dest: a #GtkDropTarget
+ *
+ * Gets the flags determining the behavior of the drop target.
+ *
+ * Returns: flags determining the behaviour of the drop target
+ */
+GtkDestDefaults
+gtk_drop_target_get_defaults (GtkDropTarget *dest)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), GTK_DEST_DEFAULT_ALL);
+
+  return dest->defaults;
+}
+
+/**
+ * gtk_drop_target_set_track_motion:
+ * @dest: a #GtkDropTarget
+ * @track_motion: whether to accept all targets
+ *
+ * Tells the drop target to emit #GtkDropTarget::drag-motion and
+ * #GtkDropTarget::drag-leave events regardless of the targets and
+ * the %GTK_DEST_DEFAULT_MOTION flag.
+ *
+ * This may be used when a drop target wants to do generic
+ * actions regardless of the targets that the source offers.
+ */
+void
+gtk_drop_target_set_track_motion (GtkDropTarget *dest,
+                                  gboolean       track_motion)
+{
+  g_return_if_fail (GTK_IS_DROP_TARGET (dest));
+
+  if (dest->track_motion == track_motion)
+    return;
+
+  dest->track_motion = track_motion;
+
+  g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_TRACK_MOTION]);
+}
+
+/**
+ * gtk_drop_target_get_track_motion:
+ * @dest: a #GtkDropTarget
+ *
+ * Gets the value of the #GtkDropTarget::track-motion property.
+ *
+ * Returns: whether to accept all targets
+ */
+gboolean
+gtk_drop_target_get_track_motion (GtkDropTarget *dest)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), FALSE);
+
+  return dest->track_motion;
+}
+
+/**
+ * gtk_drop_target_attach:
+ * @dest: (transfer full): a #GtkDropTarget
+ * @widget the widget to attach @dest to
+ *
+ * Attaches the @dest to widget and makes it accept drops
+ * on the widgets.
+ *
+ * To undo the effect of this call, use gtk_drop_target_detach().
+ */
+void
+gtk_drop_target_attach (GtkDropTarget *dest,
+                        GtkWidget     *widget)
+{
+  GtkDragDestSite *site;
+
+  g_return_if_fail (GTK_IS_DROP_TARGET (dest));
+  g_return_if_fail (dest->widget == NULL);
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  dest->widget = widget;
+
+  site = g_slice_new0 (GtkDragDestSite);
+
+  site->dest = dest;
+  site->have_drag = FALSE;
+
+  gtk_drag_dest_set_internal (widget, site);
+}
+
+/**
+ * gtk_drop_target_detach:
+ * @dest: a #GtkDropTarget
+ *
+ * Undoes the effect of a prior gtk_drop_target_attach() call.
+ */
+void
+gtk_drop_target_detach (GtkDropTarget *dest)
+{
+  g_return_if_fail (GTK_IS_DROP_TARGET (dest));
+
+  if (dest->widget)
+    {
+      gtk_drag_dest_unset (dest->widget);
+      dest->widget = NULL;
+    }
+}
+
+/**
+ * gtk_drop_target_get_target:
+ * @dest: a #GtkDropTarget
+ *
+ * Gts the widget that the drop target is attached to.
+ *
+ * Returns: (nullable): get the widget that @dest is attached to
+ */
+GtkWidget *
+gtk_drop_target_get_target (GtkDropTarget *dest)
+{
+  g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), NULL);
+
+  return dest->widget;
+}
+
+/**
+ * gtk_drop_target_get_drop:
+ * @dest: a #GtkDropTarget
+ *
+ * Returns the underlying #GtkDrop object for an ongoing drag.
+ *
+ * Returns: (nullable): 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;
+}
+
+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.
+ *
+ * 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;
+
+  return gtk_drop_target_match (dest, dest->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);
+}
+
+void
+gtk_drop_target_emit_drag_leave (GtkDropTarget    *dest,
+                                 GdkDrop          *drop,
+                                 guint             time)
+{
+  set_drop (dest, drop);
+  g_signal_emit (dest, signals[DRAG_LEAVE], 0, time);
+  set_drop (dest, NULL);
+}
+
+gboolean
+gtk_drop_target_emit_drag_motion (GtkDropTarget    *dest,
+                                  GdkDrop          *drop,
+                                  int               x,
+                                  int               y)
+{
+  gboolean result = FALSE;
+
+  set_drop (dest, drop);
+  g_signal_emit (dest, signals[DRAG_MOTION], 0, x, y, &result);
+
+  return result;
+}
+
+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, x, y, &result);
+
+  return result;
+}
+
+void
+gtk_drop_target_emit_drag_data_received (GtkDropTarget    *dest,
+                                         GdkDrop          *drop,
+                                         GtkSelectionData *sdata)
+{
+  set_drop (dest, drop);
+  g_signal_emit (dest, signals[DRAG_DATA_RECEIVED], 0, sdata);
+}
diff --git a/gtk/gtkdragdest.h b/gtk/gtkdragdest.h
index f241f514a7..e126298bf9 100644
--- a/gtk/gtkdragdest.h
+++ b/gtk/gtkdragdest.h
@@ -37,6 +37,8 @@
 
 G_BEGIN_DECLS
 
+typedef struct _GtkDropTarget GtkDropTarget;
+
 /**
  * GtkDestDefaults:
  * @GTK_DEST_DEFAULT_MOTION: If set for a widget, GTK+, during a drag over this
@@ -46,7 +48,7 @@ G_BEGIN_DECLS
  * @GTK_DEST_DEFAULT_HIGHLIGHT: If set for a widget, GTK+ will draw a highlight on
  *   this widget as long as a drag is over this widget and the widget drag format
  *   and action are acceptable.
- * @GTK_DEST_DEFAULT_DROP: If set for a widget, when a drop occurs, GTK+ will
+ * @GTK_DEST_DEFAULT_OP: If set for a widget, when a drop occurs, GTK+ will
  *   will check if the drag matches this widget’s list of possible formats and
  *   actions. If so, GTK+ will call gtk_drag_get_data() on behalf of the widget.
  *   Whether or not the drop is successful, GTK+ will call gdk_drag_finish(). If
@@ -67,10 +69,10 @@ typedef enum {
 } GtkDestDefaults;
 
 GDK_AVAILABLE_IN_ALL
-void gtk_drag_dest_set   (GtkWidget            *widget,
-                          GtkDestDefaults       flags,
-                          GdkContentFormats    *targets,
-                          GdkDragAction         actions);
+GtkDropTarget *gtk_drag_dest_set   (GtkWidget            *widget,
+                                    GtkDestDefaults       flags,
+                                    GdkContentFormats    *targets,
+                                    GdkDragAction         actions);
 
 GDK_AVAILABLE_IN_ALL
 void gtk_drag_dest_unset (GtkWidget          *widget);
@@ -98,6 +100,63 @@ GDK_AVAILABLE_IN_ALL
 gboolean       gtk_drag_dest_get_track_motion  (GtkWidget *widget);
 
 
+#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            (GtkDestDefaults    defaults,
+                                                     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
+void               gtk_drop_target_set_defaults     (GtkDropTarget     *dest,
+                                                     GtkDestDefaults    defaults);
+GDK_AVAILABLE_IN_ALL
+GtkDestDefaults    gtk_drop_target_get_defaults     (GtkDropTarget     *dest);
+
+GDK_AVAILABLE_IN_ALL
+void               gtk_drop_target_set_track_motion (GtkDropTarget     *dest,
+                                                     gboolean           track_motion);
+GDK_AVAILABLE_IN_ALL
+gboolean           gtk_drop_target_get_track_motion (GtkDropTarget     *dest);
+
+GDK_AVAILABLE_IN_ALL
+void               gtk_drop_target_attach           (GtkDropTarget     *dest,
+                                                     GtkWidget         *widget);
+GDK_AVAILABLE_IN_ALL
+void               gtk_drop_target_detach           (GtkDropTarget     *dest);
+
+GDK_AVAILABLE_IN_ALL
+GdkDrop           *gtk_drop_target_get_drop         (GtkDropTarget     *dest);
+
+GDK_AVAILABLE_IN_ALL
+GtkWidget         *gtk_drop_target_get_target       (GtkDropTarget     *dest);
+
+GDK_AVAILABLE_IN_ALL
+const char        *gtk_drop_target_find_mimetype    (GtkDropTarget     *dest);
+
+
 G_END_DECLS
 
 #endif /* __GTK_DRAG_DEST_H__ */
diff --git a/gtk/gtkdragdestprivate.h b/gtk/gtkdragdestprivate.h
new file mode 100644
index 0000000000..deb772763f
--- /dev/null
+++ b/gtk/gtkdragdestprivate.h
@@ -0,0 +1,46 @@
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2020 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_DRAG_DEST_PRIVATE_H__
+#define __GTK_DRAG_DEST_PRIVATE_H__
+
+#include "gtkdragdest.h"
+
+G_BEGIN_DECLS
+
+void gtk_drop_target_emit_drag_data_received (GtkDropTarget    *dest,
+                                              GdkDrop          *drop,
+                                              GtkSelectionData *sdata);
+void gtk_drop_target_emit_drag_leave         (GtkDropTarget    *dest,
+                                              GdkDrop          *drop,
+                                              guint             time);
+gboolean gtk_drop_target_emit_drag_motion    (GtkDropTarget    *dest,
+                                              GdkDrop          *drop,
+                                              int               x,
+                                              int               y);
+gboolean gtk_drop_target_emit_drag_drop      (GtkDropTarget    *dest,
+                                              GdkDrop          *drop,
+                                              int               x,
+                                              int               y);
+
+const char * gtk_drop_target_match           (GtkDropTarget *dest,
+                                              GdkDrop       *drop);
+
+G_END_DECLS
+
+#endif
diff --git a/gtk/gtkdragsource.c b/gtk/gtkdragsource.c
index 1f4930b3a9..653f112b68 100644
--- a/gtk/gtkdragsource.c
+++ b/gtk/gtkdragsource.c
@@ -310,7 +310,8 @@ static void gtk_drag_source_cancel_cb         (GdkDrag             *drag,
                                                GtkDragSource       *source);
 
 static void
-drag_end (GtkDragSource *source)
+drag_end (GtkDragSource *source,
+          gboolean       success)
 {
   g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_drop_performed_cb, source);
   g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_dnd_finished_cb, source);
@@ -318,6 +319,8 @@ drag_end (GtkDragSource *source)
 
   g_signal_emit (source, signals[DRAG_END], 0);
 
+  gdk_drag_drop_done (source->drag, success);
+
   g_object_set_data (G_OBJECT (source->drag), I_("gtk-drag-source"), NULL);
   g_clear_object (&source->drag);
   source->widget = NULL;
@@ -330,8 +333,7 @@ gtk_drag_source_dnd_finished_cb (GdkDrag       *drag,
 {
   if (gdk_drag_get_selected_action (drag) == GDK_ACTION_MOVE)
     g_signal_emit (source, signals[DRAG_DATA_DELETE], 0);
-
-  drag_end (source);
+  drag_end (source, TRUE);
 }
 
 static void
@@ -342,7 +344,7 @@ gtk_drag_source_cancel_cb (GdkDrag             *drag,
   gboolean success = FALSE;
 
   g_signal_emit (source, signals[DRAG_FAILED], 0, reason, &success);
-  drag_end (source);
+  drag_end (source, FALSE);
 }
 
 static void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]