[gtk/dnd-gestures: 4/30] Add a GtkDragSource object



commit 9e9f42bfc7e9e308a505cca2ff4de5368775f643
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Dec 30 14:12:08 2019 -0500

    Add a GtkDragSource object
    
    Add an explicit GtkDragSource object, and move the source-side
    DND signals here. The object can be used on the spot, by calling
    gtk_drag_source_drag_begin() yourself, or for static drag-source
    configuration, by calling gtk_drag_source_attach(). In the latter
    case, we set up and conect a drag gesture to trigger the drag_begin
    call.

 gtk/gtkdragsource.c | 753 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 gtk/gtkdragsource.h |  62 +++++
 2 files changed, 814 insertions(+), 1 deletion(-)
---
diff --git a/gtk/gtkdragsource.c b/gtk/gtkdragsource.c
index 8cc461464e..125fdb676a 100644
--- a/gtk/gtkdragsource.c
+++ b/gtk/gtkdragsource.c
@@ -29,8 +29,16 @@
 #include "gtkdnd.h"
 #include "gtkdndprivate.h"
 #include "gtkgesturedrag.h"
+#include "gtkgesturesingleprivate.h"
 #include "gtkimagedefinitionprivate.h"
+#include "gtknative.h"
+#include "gtkwidgetprivate.h"
 #include "gtkintl.h"
+#include "gtkstylecontext.h"
+#include "gtkimageprivate.h"
+#include "gtkdragiconprivate.h"
+#include "gtkprivate.h"
+#include "gtkmarshalers.h"
 
 
 typedef struct _GtkDragSourceSite GtkDragSourceSite;
@@ -117,7 +125,7 @@ gtk_drag_source_site_destroy (gpointer data)
  * gtk_drag_source_set: (method)
  * @widget: a #GtkWidget
  * @start_button_mask: the bitmask of buttons that can start the drag
- * @targets: (allow-none): the targets that the drag will support,
+ * @formats: (allow-none): the formats that the drag will support,
  *     may be %NULL
  * @actions: the bitmask of possible actions for a drag from this widget
  *
@@ -388,3 +396,746 @@ gtk_drag_source_set_icon_paintable (GtkWidget    *widget,
   site->image_def = gtk_image_definition_new_paintable (paintable);
 }
 
+
+/**
+ * SECTION:gtkdragsource
+ * @Short_description: An object to initiate DND operations
+ * @Title: GtkDragSource
+ *
+ * GtkDragSource is an auxiliary object that is used to initiate
+ * Drag-And-Drop operations. It can be set up with the necessary
+ * ingredients for a DND operation ahead of time. This includes
+ * the source for the data that is being transferred, in the form
+ * of a #GdkContentProvider, the desired action, and the icon to
+ * use during the drag operation.
+ *
+ * GtkDragSource can be used in two ways:
+ * - for static drag-source configuration
+ * - for one-off drag operations
+ *
+ * To configure a widget as a permanent source for DND operations,
+ * set up the GtkDragSource, then call gtk_drag_source_attach().
+ * This sets up a drag gesture on the widget that will trigger
+ * DND actions.
+ *
+ * To initiate a on-off drag operation, set up the GtkDragSource,
+ * then call gtk_drag_source_drag_begin(). GTK keeps a reference
+ * on the drag source until the DND operation is done, so you
+ * can unref the source after calling drag_being().
+ *
+ * During the DND operation, GtkDragSource emits signals that
+ * can be used to obtain updates about the status of the operation,
+ * but it is not normally necessary to connect to any signals,
+ * except for one case: when the supported actions include
+ * %GDK_DRAG_MOVE, you need to listen for the
+ * #GtkDragSource::drag-data-deleted signal and delete the
+ * drag data after it has been transferred.
+ */
+
+struct _GtkDragSource
+{
+  GObject parent_instance;
+
+  GdkContentProvider *content;
+  GdkDragAction actions;
+
+  GtkWidget *icon_window;
+  GdkPaintable *paintable;
+  int hot_x;
+  int hot_y;
+
+  GtkGesture *gesture;
+  int start_button_mask;
+
+  GdkDrag *drag;
+  GtkWidget *widget;
+};
+
+struct _GtkDragSourceClass
+{
+  GObjectClass parent_class;
+};
+
+enum {
+  PROP_CONTENT = 1,
+  PROP_ACTIONS,
+  NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+enum {
+  DRAG_BEGIN,
+  DRAG_END,
+  DRAG_FAILED,
+  DRAG_DATA_DELETE,
+  NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS];
+
+G_DEFINE_TYPE (GtkDragSource, gtk_drag_source, G_TYPE_OBJECT);
+
+static void
+gtk_drag_source_init (GtkDragSource *source)
+{
+}
+
+static void
+gtk_drag_source_finalize (GObject *object)
+{
+  GtkDragSource *source = GTK_DRAG_SOURCE (object);
+
+  gtk_drag_source_detach (source);
+
+  g_clear_object (&source->content);
+  g_clear_object (&source->paintable);
+  g_clear_object (&source->icon_window);
+
+  G_OBJECT_CLASS (gtk_drag_source_parent_class)->finalize (object);
+}
+
+static void
+gtk_drag_source_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  GtkDragSource *source = GTK_DRAG_SOURCE (object);
+  
+  switch (prop_id)
+    {
+    case PROP_CONTENT:
+      gtk_drag_source_set_content (source, g_value_get_object (value));
+      break;
+
+    case PROP_ACTIONS:
+      gtk_drag_source_set_actions (source, g_value_get_flags (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_drag_source_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  GtkDragSource *source = GTK_DRAG_SOURCE (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONTENT:
+      g_value_set_object (value, gtk_drag_source_get_content (source));
+      break;
+
+    case PROP_ACTIONS:
+      g_value_set_flags (value, gtk_drag_source_get_actions (source));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_drag_source_class_init (GtkDragSourceClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = gtk_drag_source_finalize;
+  object_class->set_property = gtk_drag_source_set_property;
+  object_class->get_property = gtk_drag_source_get_property;
+
+  /**
+   * GtkDragSource:content:
+   *
+   * The data that is offered by drag operations from this source,
+   * in the form of a #GdkContentProvider.
+   */ 
+  properties[PROP_CONTENT] =
+       g_param_spec_object ("content",
+                            P_("Content"),
+                            P_("The content provider for the dragged data"),
+                           GDK_TYPE_CONTENT_PROVIDER,
+                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GtkDragSource:actions:
+   *
+   * The actions that are supported by drag operations from the source.
+   *
+   * Note that you must handle the #GtkDragSource::drag-data-deleted signal
+   * if the actions include %GDK_ACTION_MOVE.
+   */ 
+  properties[PROP_ACTIONS] =
+       g_param_spec_flags ("actions",
+                           P_("Actions"),
+                           P_("Supported actions"),
+                           GDK_TYPE_DRAG_ACTION, 0,
+                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+
+  /**
+   * GtkDragSource::drag-begin:
+   * @source: the #GtkDragSource
+   *
+   * The ::drag-begin signal is emitted on the drag source when a drag
+   * is started. It can be used to e.g. set a custom drag icon with
+   * gtk_drag_source_set_icon(). But all of the setup can also be
+   * done before calling gtk_drag_source_drag_begin(), so this is not
+   * really necessary.
+   */
+  signals[DRAG_BEGIN] =
+      g_signal_new (I_("drag-begin"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    NULL, NULL,
+                    NULL,
+                    G_TYPE_NONE, 0);
+
+  /**
+   * GtkDragSource::drag-end:
+   * @source: the #GtkDragSource
+   *
+   * The ::drag-end signal is emitted on the drag source when a drag is
+   * finished. A typical reason to connect to this signal is to undo
+   * things done in #GtkDragSource::drag-begin.
+   */ 
+  signals[DRAG_END] =
+      g_signal_new (I_("drag-end"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    NULL, NULL,
+                    NULL,
+                    G_TYPE_NONE, 0);
+
+  /**
+   * GtkDragSource::drag-failed:
+   * @source: the #GtkDragSource
+   * @reason: information on why the drag failed
+   *
+   * The ::drag-failed signal is emitted on the drag source when a drag has
+   * failed. The signal handler may handle a failed drag operation based on
+   * the type of error. It should return %TRUE if the failure has been handled
+   * and the default "drag operation failed" should not be shown.
+   *
+   * Returns: %TRUE if the failed drag operation has been already handled
+   */
+  signals[DRAG_FAILED] =
+      g_signal_new (I_("drag-failed"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    _gtk_boolean_handled_accumulator, NULL,
+                    _gtk_marshal_BOOLEAN__ENUM,
+                    G_TYPE_BOOLEAN, 1,
+                    GDK_TYPE_DRAG_CANCEL_REASON);
+
+  /**
+   * GtkDragSource::drag-data-delete:
+   * @source: the #GtkDragSource
+   *
+   * The ::drag-data-delete signal is emitted on the drag source when a drag
+   * with the action %GDK_ACTION_MOVE is successfully completed. The signal
+   * handler is responsible for deleting the data that has been dropped. What
+   * "delete" means depends on the context of the drag operation.
+   */
+  signals[DRAG_DATA_DELETE] =
+      g_signal_new (I_("drag-data-delete"),
+                    G_TYPE_FROM_CLASS (class),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    NULL, NULL,
+                    NULL,
+                    G_TYPE_NONE, 0);
+}
+
+static void gtk_drag_source_dnd_finished_cb   (GdkDrag             *drag,
+                                               GtkDragSource       *source);
+static void gtk_drag_source_drop_performed_cb (GdkDrag             *drag,
+                                               GtkDragSource       *source);
+static void gtk_drag_source_cancel_cb         (GdkDrag             *drag,
+                                               GdkDragCancelReason  reason,
+                                               GtkDragSource       *source);
+
+static void
+drag_end (GtkDragSource *source)
+{
+  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);
+  g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_cancel_cb, source);
+
+  g_signal_emit (source, signals[DRAG_END], 0);
+
+  g_object_set_data (G_OBJECT (source->drag), I_("gtk-drag-source"), NULL);
+  g_clear_object (&source->drag);
+  source->widget = NULL;
+  g_object_unref (source);
+}
+
+static void
+gtk_drag_source_dnd_finished_cb (GdkDrag       *drag,
+                                 GtkDragSource *source)
+{
+  if (gdk_drag_get_selected_action (drag) == GDK_ACTION_MOVE)
+    g_signal_emit (source, signals[DRAG_DATA_DELETE], 0);
+
+  drag_end (source);
+}
+
+static void
+gtk_drag_source_cancel_cb (GdkDrag             *drag,
+                           GdkDragCancelReason  reason,
+                           GtkDragSource       *source)
+{
+  gboolean success = FALSE;
+
+  g_signal_emit (source, signals[DRAG_FAILED], 0, reason, &success);
+  gdk_drag_drop_done (source->drag, success);
+  drag_end (source);
+}
+
+static void
+gtk_drag_source_drop_performed_cb (GdkDrag       *drag,
+                                   GtkDragSource *source)
+{
+  if (source->icon_window)
+    gtk_widget_hide (source->icon_window);
+}
+
+/**
+ * gtk_drag_source_drag_begin:
+ * @source: a #GtkDragSource
+ * @widget: the widget where the drag operation originates
+ * @device: the device that is driving the drag operation
+ * @x: start point X coordinate
+ * @y: start point Y xoordinate
+ *
+ * Starts a DND operation with @source.
+ *
+ * The start point coordinates are relative to @widget.
+ *
+ * GTK keeps a reference on @source for the duration of
+ * the DND operation, so it is safe to unref @source
+ * after this call.
+ */
+void
+gtk_drag_source_drag_begin (GtkDragSource *source,
+                            GtkWidget     *widget,
+                            GdkDevice     *device,
+                            int            x,
+                            int            y)
+{
+  GtkNative *native;
+  GdkSurface *surface;
+  double px, py;
+  int dx, dy;
+  GtkWidget *icon;
+
+  g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+  g_return_if_fail (source->content != NULL);
+  g_return_if_fail (source->actions != 0);
+
+  if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+    device = gdk_device_get_associated_device (device);
+
+  native = gtk_widget_get_native (widget);
+  surface = gtk_native_get_surface (native);
+
+  gtk_widget_translate_coordinates (widget, GTK_WIDGET (native), x, y, &x, &y);
+  gdk_surface_get_device_position (surface, device, &px, &py, NULL);
+
+  dx = round (px) - x;
+  dy = round (py) - y;
+
+  source->drag = gdk_drag_begin (surface, device, source->content, source->actions, dx, dy);
+  if (source->drag == NULL)
+    {
+      g_print ("no drag :(\n");
+      return;
+    }
+
+  g_object_set_data (G_OBJECT (source->drag), I_("gtk-drag-source"), source);
+  source->widget = widget;
+
+  gtk_widget_reset_controllers (widget);
+
+  /* We hold a ref until ::drag-end is emitted */
+  g_object_ref (source);
+
+  g_signal_emit (source, signals[DRAG_BEGIN], 0);
+
+  if (!source->paintable)
+    {
+      GtkIconTheme *theme;
+ 
+      theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (widget));
+      source->paintable = gtk_icon_theme_load_icon (theme, "text-x-generic", 32, 0, NULL);
+      source->hot_x = 0;
+      source->hot_y = 0;
+    }
+
+  gdk_drag_set_hotspot (source->drag, source->hot_x, source->hot_y);
+  source->icon_window = gtk_drag_icon_new ();
+  g_object_ref_sink (source->icon_window);
+  gtk_drag_icon_set_surface (GTK_DRAG_ICON (source->icon_window),
+                             gdk_drag_get_drag_surface (source->drag));
+
+  icon = gtk_picture_new_for_paintable (source->paintable);
+  gtk_picture_set_can_shrink (GTK_PICTURE (icon), FALSE);
+  gtk_drag_icon_set_widget (GTK_DRAG_ICON (source->icon_window), icon);
+
+  gtk_widget_show (source->icon_window);
+
+  g_signal_connect (source->drag, "drop-performed",
+                    G_CALLBACK (gtk_drag_source_drop_performed_cb), source);
+  g_signal_connect (source->drag, "dnd-finished",
+                    G_CALLBACK (gtk_drag_source_dnd_finished_cb), source);
+  g_signal_connect (source->drag, "cancel",
+                    G_CALLBACK (gtk_drag_source_cancel_cb), source);
+}
+
+/**
+ * gtk_drag_source_new:
+ * @content: (nullable): the #GdkContentProvider to use, or %NULL
+ * @actions: the actions to offer
+ *
+ * Creates a new #GtkDragSource object.
+ *
+ * Returns: the new #GtkDragSource
+ */
+GtkDragSource *
+gtk_drag_source_new (GdkContentProvider *content,
+                     GdkDragAction       actions)
+{
+  return g_object_new (GTK_TYPE_DRAG_SOURCE,
+                       "content", content,
+                       "actions", actions,
+                       NULL);
+}
+
+/**
+ * gtk_drag_source_get_content:
+ * @source: a #GtkDragSource
+ *
+ * Gets the current content provider of a #GtkDragSource.
+ *
+ * Returns: the #GtkContentProvider of @source
+ */
+GdkContentProvider *
+gtk_drag_source_get_content (GtkDragSource *source)
+{
+  g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);
+
+  return source->content;  
+}
+
+/**
+ * gtk_drag_source_set_content:
+ * @source: a #GtkDragSource
+ * @content: (nullable): a #GtkContntProvider, or %NULL
+ *
+ * Sets a content provider on a #GtkDragSource.
+ *
+ * When the data is requested in the cause of a
+ * DND operation, it will be obtained from the
+ * content provider.
+ */
+void
+gtk_drag_source_set_content (GtkDragSource      *source,
+                             GdkContentProvider *content)
+{
+  g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
+
+  if (!g_set_object (&source->content, content))
+    return;
+
+  g_object_notify_by_pspec (G_OBJECT (source), properties[PROP_CONTENT]);
+}
+
+/**
+ * gtk_drag_source_get_actions:
+ * @source: a #GtkDragSource
+ *
+ * Gets the actions that are currently set on the #GtkDragSource.
+ *
+ * Returns: the actions set on @source
+ */
+GdkDragAction
+gtk_drag_source_get_actions (GtkDragSource *source)
+{
+  g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), 0);
+
+  return source->actions;
+}
+
+/**
+ * gtk_drag_source_set_actions:
+ * @source: a #GtkDragSource
+ * @actions: the actions to offer
+ *
+ * Sets the actions on the #GtkDragSource.
+ *
+ * During a DND operation, the actions are offered
+ * to potential drop targets.
+ */
+void
+gtk_drag_source_set_actions (GtkDragSource *source,
+                             GdkDragAction  actions)
+{
+  g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
+
+  if (source->actions == actions)
+    return;
+
+  source->actions = actions;
+
+  g_object_notify_by_pspec (G_OBJECT (source), properties[PROP_ACTIONS]);
+}
+
+/**
+ * gtk_drag_source_set_icon:
+ * @source: a #GtkDragSource
+ * @paintable: (nullable): the #GtkPaintable to use as icon, or %NULL
+ * @hot_x: the hotspot X coordinate on the icon
+ * @hot_y: the hotspot Y coordinate on the icon
+ *
+ * Sets a paintable to use as icon during DND operations.
+ *
+ * The hotspot coordinates determine the point on the icon
+ * that gets aligned with the hotspot of the cursor.
+ *
+ * If @paintable is %NULL, a default icon is used.
+ */
+void
+gtk_drag_source_set_icon (GtkDragSource *source,
+                          GdkPaintable  *paintable,
+                          int            hot_x,
+                          int            hot_y)
+{
+  g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
+
+  g_set_object (&source->paintable, paintable);
+
+  source->hot_x = hot_x;
+  source->hot_y = hot_y;
+}
+
+static void
+source_gesture_begin (GtkGesture       *gesture,
+                      GdkEventSequence *sequence,
+                      GtkDragSource    *source)
+{
+  guint button;
+
+  if (gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)))
+    button = 1;
+  else
+    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+
+  g_assert (button >= 1);
+
+  if ((source->start_button_mask & (GDK_BUTTON1_MASK << (button - 1))) == 0)
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+}
+
+static void
+source_gesture_update (GtkGesture       *gesture,
+                       GdkEventSequence *sequence,
+                       GtkDragSource    *source)
+                       
+{
+  gdouble start_x, start_y, offset_x, offset_y;
+  GtkWidget *widget;
+
+  if (!gtk_gesture_is_recognized (gesture))
+    return;
+
+  widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+
+  gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (gesture), &start_x, &start_y);
+  gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &offset_x, &offset_y);
+
+  if (gtk_drag_check_threshold (widget,
+                                start_x, start_y,
+                                start_x + offset_x, start_y + offset_y))
+    {
+      GdkDevice *device = gtk_gesture_get_device (gesture);
+
+      gtk_drag_source_drag_begin (source, widget, device, start_x, start_y);
+   }
+}
+
+static void
+gesture_removed (gpointer data)
+{
+  GtkDragSource *source = data;
+
+  /* if we get here, the widget we are attached to was destroyed,
+   * and removed our gesture.
+   *
+   * Clean up, and drop the ref we held for being attached.
+   */
+
+  source->gesture = NULL;
+  g_object_unref (source);
+}
+
+/**
+ * gtk_drag_source_attach:
+ * @source: (transfer full): a #GtkDragSource
+ * @widget: the widget to attach @source to
+ * @start_button_mask: mask determining which mouse buttons trigger
+ *
+ * Attaches the @source to a @widget by creating a drag gesture
+ * on @widget that will trigger DND operations with @source.
+ *
+ * The @start_button_mask determines which mouse buttons trigger
+ * a DND operation.
+ *
+ * To undo the effect of this call, use gtk_drag_source_detach().
+ */
+void
+gtk_drag_source_attach (GtkDragSource   *source,
+                        GtkWidget       *widget,
+                        GdkModifierType  start_button_mask)
+{
+  g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (source->gesture == NULL);
+  g_return_if_fail (start_button_mask != 0);
+  g_return_if_fail ((start_button_mask & ~(GDK_BUTTON1_MASK |
+                                           GDK_BUTTON2_MASK |
+                                           GDK_BUTTON3_MASK |
+                                           GDK_BUTTON4_MASK |
+                                           GDK_BUTTON5_MASK)) == 0);
+
+  source->gesture = gtk_gesture_drag_new ();
+  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (source->gesture),
+                                              GTK_PHASE_CAPTURE);
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (source->gesture), 0);
+  g_signal_connect (source->gesture, "begin",
+                    G_CALLBACK (source_gesture_begin), source);
+  g_signal_connect (source->gesture, "update",
+                    G_CALLBACK (source_gesture_update), source);
+  gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (source->gesture));
+
+  source->start_button_mask = start_button_mask;
+
+  g_object_set_data_full (G_OBJECT (source->gesture), "gtk-drag-source",
+                          source, gesture_removed);
+}
+
+/**
+ * gtk_drag_source_detach:
+ * @source: a #GtkDragSource
+ *
+ * Undoes the effect of a prior gtk_drag_source_attach() call.
+ */
+void
+gtk_drag_source_detach (GtkDragSource *source)
+{
+  g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
+
+  if (source->gesture)
+    {
+      GtkWidget *widget;
+
+      g_object_ref (source);
+
+      g_object_set_data (G_OBJECT (source->gesture), "gtk-drag-source", NULL);
+      widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source->gesture));
+      gtk_widget_remove_controller (widget, GTK_EVENT_CONTROLLER (source->gesture));
+
+      g_object_unref (source);
+    }
+}
+
+/**
+ * gtk_drag_get_source:
+ * @drag: a #GdkDrag
+ *
+ * Obtains the #GtkDragSource from which a #GdkDrag originates.
+ *
+ * This function should rarely be needed. Once case where it can
+ * be used is together with gtk_drop_get_drag(), to determine
+ * whether a 'local' drag is coming from the same widget.
+ *
+ * Returns: (transfer none) (nullable): a #GtkDragSource, or %NULL
+ */
+GtkDragSource *
+gtk_drag_get_source (GdkDrag *drag)
+{
+  gpointer data;
+
+  g_return_val_if_fail (GDK_IS_DRAG (drag), NULL);
+
+  data = g_object_get_data (G_OBJECT (drag), I_("gtk-drag-source"));
+
+  if (data)
+    return GTK_DRAG_SOURCE (data);
+
+  return NULL;
+}
+
+/**
+ * gtk_drag_source_get_origin:
+ * @source: a #GtkDragSource
+ *
+ * Returns the widget that an ongoing drag is started from.
+ *
+ * Returns: (nullable): the origin of the current drag operation, or %NULL
+ */
+GtkWidget *
+gtk_drag_source_get_origin (GtkDragSource *source)
+{
+  g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);
+
+  return source->widget;
+}
+
+/**
+ * gtk_drag_source_get_drag:
+ * @source: a #GtkDragSource
+ *
+ * Returns the underlying #GtkDrag object for an ongoing drag.
+ *
+ * Returns: (nullable): the #GdkDrag of the current drag operation, or %NULL
+ */
+GdkDrag *
+gtk_drag_source_get_drag (GtkDragSource *source)
+{
+  g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);
+
+  return source->drag;
+}
+
+/**
+ * gtk_drag_source_drag_cancel:
+ * @source: a #GtkDragSource
+ *
+ * Cancels a currently ongoing drag operation.
+ */
+void
+gtk_drag_source_drag_cancel (GtkDragSource *source)
+{
+  g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
+
+  if (source->drag)
+    {
+      gboolean success = FALSE;
+
+      g_signal_emit (source, signals[DRAG_FAILED], 0, GDK_DRAG_CANCEL_ERROR, &success);
+
+      gdk_drag_drop_done (source->drag, success);
+    }
+}
diff --git a/gtk/gtkdragsource.h b/gtk/gtkdragsource.h
index b77f46db74..22f8c31cd8 100644
--- a/gtk/gtkdragsource.h
+++ b/gtk/gtkdragsource.h
@@ -69,6 +69,68 @@ void gtk_drag_source_set_icon_paintable (GtkWidget       *widget,
                                         GdkPaintable    *paintable);
 
 
+#define GTK_TYPE_DRAG_SOURCE         (gtk_drag_source_get_type ())
+#define GTK_DRAG_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DRAG_SOURCE, GtkDragSource))
+#define GTK_DRAG_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DRAG_SOURCE, 
GtkDragSourceClass))
+#define GTK_IS_DRAG_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DRAG_SOURCE))
+#define GTK_IS_DRAG_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DRAG_SOURCE))
+#define GTK_DRAG_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DRAG_SOURCE, 
GtkDragSourceClass))
+
+typedef struct _GtkDragSource GtkDragSource;
+typedef struct _GtkDragSourceClass GtkDragSourceClass;
+
+GDK_AVAILABLE_IN_ALL
+GType              gtk_drag_source_get_type  (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GtkDragSource      *gtk_drag_source_new        (GdkContentProvider *content,
+                                                GdkDragAction       actions);
+
+GDK_AVAILABLE_IN_ALL
+void                gtk_drag_source_set_content (GtkDragSource     *source,
+                                                 GdkContentProvider *content);
+GDK_AVAILABLE_IN_ALL
+GdkContentProvider *gtk_drag_source_get_content (GtkDragSource     *source);
+
+GDK_AVAILABLE_IN_ALL
+void               gtk_drag_source_set_actions (GtkDragSource     *source,
+                                                GdkDragAction      actions);
+GDK_AVAILABLE_IN_ALL
+GdkDragAction      gtk_drag_source_get_actions (GtkDragSource     *source);
+
+GDK_AVAILABLE_IN_ALL
+void               gtk_drag_source_set_icon    (GtkDragSource     *source,
+                                                GdkPaintable      *paintable,
+                                                int                hot_x,
+                                                int                hot_y);
+GDK_AVAILABLE_IN_ALL
+void               gtk_drag_source_drag_begin  (GtkDragSource     *source,
+                                                GtkWidget         *widget,
+                                                GdkDevice         *device,
+                                                int                x,
+                                                int                y);
+
+GDK_AVAILABLE_IN_ALL
+void               gtk_drag_source_drag_cancel (GtkDragSource     *sourcei);
+
+GDK_AVAILABLE_IN_ALL
+GtkDragSource *    gtk_drag_get_source         (GdkDrag           *drag);
+
+GDK_AVAILABLE_IN_ALL
+GtkWidget *        gtk_drag_source_get_origin  (GtkDragSource     *source);
+
+GDK_AVAILABLE_IN_ALL
+GdkDrag *          gtk_drag_source_get_drag    (GtkDragSource     *source);
+
+GDK_AVAILABLE_IN_ALL
+void               gtk_drag_source_attach      (GtkDragSource     *source,
+                                                GtkWidget         *widget,
+                                                GdkModifierType    start_button_mask);
+
+GDK_AVAILABLE_IN_ALL
+void               gtk_drag_source_detach      (GtkDragSource     *source);
+
+
 G_END_DECLS
 
 #endif /* __GTK_DRAG_SOURCE_H__ */


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