gtkdragsource.c 22.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

#include "config.h"

#include "gtkdragsource.h"

#include "gtkgesturedrag.h"
Matthias Clasen's avatar
Matthias Clasen committed
30
#include "gtkgesturesingleprivate.h"
31
#include "gtkimagedefinitionprivate.h"
Matthias Clasen's avatar
Matthias Clasen committed
32
33
#include "gtknative.h"
#include "gtkwidgetprivate.h"
34
#include "gtkintl.h"
Matthias Clasen's avatar
Matthias Clasen committed
35
36
#include "gtkstylecontext.h"
#include "gtkimageprivate.h"
37
#include "gtkdragicon.h"
Matthias Clasen's avatar
Matthias Clasen committed
38
39
#include "gtkprivate.h"
#include "gtkmarshalers.h"
40
41
#include "gtkicontheme.h"
#include "gtkpicture.h"
42
#include "gtksettingsprivate.h"
Matthias Clasen's avatar
Matthias Clasen committed
43
#include "gtkgesturesingle.h"
44

Matthias Clasen's avatar
Matthias Clasen committed
45
46
/**
 * SECTION:gtkdragsource
Matthias Clasen's avatar
Matthias Clasen committed
47
 * @Short_description: Event controller to initiate DND operations
Matthias Clasen's avatar
Matthias Clasen committed
48
49
50
51
52
53
54
 * @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
Matthias Clasen's avatar
Matthias Clasen committed
55
56
57
 * use during the drag operation. After setting it up, the drag
 * source must be added to a widget as an event controller, using
 * gtk_widget_add_controller().
Matthias Clasen's avatar
Matthias Clasen committed
58
 *
Matthias Clasen's avatar
Matthias Clasen committed
59
60
61
62
63
64
65
66
67
 * Setting up the content provider and icon ahead of time only
 * makes sense when the data does not change. More commonly, you
 * will want to set them up just in time. To do so, #GtkDragSource
 * has #GtkDragSource::prepare and #GtkDragSource::drag-begin signals.
 * The ::prepare signal is emitted before a drag is started, and
 * can be used to set the content provider and actions that the
 * drag should be started with. The ::drag-begin signal is emitted
 * after the #GdkDrag object has been created, and can be used
 * to set up the drag icon.
Matthias Clasen's avatar
Matthias Clasen committed
68
69
70
71
72
 *
 * 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
73
 * %GDK_ACTION_MOVE, you need to listen for the
Matthias Clasen's avatar
Matthias Clasen committed
74
75
 * #GtkDragSource::drag-end signal and delete the
 * data after it has been transferred.
Matthias Clasen's avatar
Matthias Clasen committed
76
77
78
79
 */

struct _GtkDragSource
{
Matthias Clasen's avatar
Matthias Clasen committed
80
  GtkGestureSingle parent_instance;
Matthias Clasen's avatar
Matthias Clasen committed
81
82
83
84
85
86
87
88

  GdkContentProvider *content;
  GdkDragAction actions;

  GdkPaintable *paintable;
  int hot_x;
  int hot_y;

89
90
  double start_x;
  double start_y;
Matthias Clasen's avatar
Matthias Clasen committed
91
92
93
94
95
96

  GdkDrag *drag;
};

struct _GtkDragSourceClass
{
Matthias Clasen's avatar
Matthias Clasen committed
97
  GtkGestureSingleClass parent_class;
98

99
100
101
  GdkContentProvider *(* prepare) (GtkDragSource *source,
                                  double         x,
                                  double         y);
Matthias Clasen's avatar
Matthias Clasen committed
102
103
104
105
106
107
108
109
110
111
112
};

enum {
  PROP_CONTENT = 1,
  PROP_ACTIONS,
  NUM_PROPERTIES
};

static GParamSpec *properties[NUM_PROPERTIES];

enum {
113
  PREPARE,
Matthias Clasen's avatar
Matthias Clasen committed
114
115
  DRAG_BEGIN,
  DRAG_END,
116
  DRAG_CANCEL,
Matthias Clasen's avatar
Matthias Clasen committed
117
118
119
120
121
  NUM_SIGNALS
};

static guint signals[NUM_SIGNALS];

122
123
124
125
126
127
static void gtk_drag_source_dnd_finished_cb   (GdkDrag             *drag,
                                               GtkDragSource       *source);
static void gtk_drag_source_cancel_cb         (GdkDrag             *drag,
                                               GdkDragCancelReason  reason,
                                               GtkDragSource       *source);

128
129
130
static GdkContentProvider *gtk_drag_source_prepare (GtkDragSource *source,
                                                    double         x,
                                                    double         y);
131

132
133
static void gtk_drag_source_drag_begin (GtkDragSource *source);

Matthias Clasen's avatar
Matthias Clasen committed
134
G_DEFINE_TYPE (GtkDragSource, gtk_drag_source, GTK_TYPE_GESTURE_SINGLE);
Matthias Clasen's avatar
Matthias Clasen committed
135
136
137
138

static void
gtk_drag_source_init (GtkDragSource *source)
{
139
  source->actions = GDK_ACTION_COPY;
Matthias Clasen's avatar
Matthias Clasen committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
}

static void
gtk_drag_source_finalize (GObject *object)
{
  GtkDragSource *source = GTK_DRAG_SOURCE (object);

  g_clear_object (&source->content);
  g_clear_object (&source->paintable);

  G_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);
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
199
200
static gboolean
gtk_drag_source_filter_event (GtkEventController *controller,
Matthias Clasen's avatar
Matthias Clasen committed
201
                              GdkEvent           *event)
Matthias Clasen's avatar
Matthias Clasen committed
202
203
204
205
206
207
208
209
{
  /* Let touchpad swipe events go through, only if they match n-points  */
  if (gdk_event_get_event_type (event) == GDK_TOUCHPAD_SWIPE)
    {
      guint n_points;
      guint n_fingers;

      g_object_get (G_OBJECT (controller), "n-points", &n_points, NULL);
Matthias Clasen's avatar
Matthias Clasen committed
210
      n_fingers = gdk_touchpad_event_get_n_fingers (event);
Matthias Clasen's avatar
Matthias Clasen committed
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238

      if (n_fingers == n_points)
        return FALSE;
      else
        return TRUE;
    }

  return GTK_EVENT_CONTROLLER_CLASS (gtk_drag_source_parent_class)->filter_event (controller, event);
}

static void
gtk_drag_source_begin (GtkGesture       *gesture,
                       GdkEventSequence *sequence)
{
  GtkDragSource *source = GTK_DRAG_SOURCE (gesture);
  GdkEventSequence *current;

  current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));

  gtk_gesture_get_point (gesture, current, &source->start_x, &source->start_y);
}

static void
gtk_drag_source_update (GtkGesture       *gesture,
                        GdkEventSequence *sequence)
{
  GtkDragSource *source = GTK_DRAG_SOURCE (gesture);
  GtkWidget *widget;
239
  double x, y;
Matthias Clasen's avatar
Matthias Clasen committed
240
241
242
243

  if (!gtk_gesture_is_recognized (gesture))
    return;

244
245
  gtk_gesture_get_point (gesture, sequence, &x, &y);

Matthias Clasen's avatar
Matthias Clasen committed
246
247
  widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));

248
  if (gtk_drag_check_threshold (widget, source->start_x, source->start_y, x, y))
Matthias Clasen's avatar
Matthias Clasen committed
249
    {
250
251
      gtk_drag_source_drag_begin (source);
    }
Matthias Clasen's avatar
Matthias Clasen committed
252
253
}

Matthias Clasen's avatar
Matthias Clasen committed
254
255
256
257
static void
gtk_drag_source_class_init (GtkDragSourceClass *class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (class);
Matthias Clasen's avatar
Matthias Clasen committed
258
259
  GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
  GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (class);
Matthias Clasen's avatar
Matthias Clasen committed
260
261
262
263
264

  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;

Matthias Clasen's avatar
Matthias Clasen committed
265
266
267
268
269
270
  controller_class->filter_event = gtk_drag_source_filter_event;

  gesture_class->begin = gtk_drag_source_begin;
  gesture_class->update = gtk_drag_source_update;
  gesture_class->end = NULL;

271
272
  class->prepare = gtk_drag_source_prepare;

Matthias Clasen's avatar
Matthias Clasen committed
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
  /**
   * 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.
   *
Matthias Clasen's avatar
Matthias Clasen committed
291
   * Note that you must handle the #GtkDragSource::drag-end signal
Matthias Clasen's avatar
Matthias Clasen committed
292
293
294
295
296
297
   * if the actions include %GDK_ACTION_MOVE.
   */ 
  properties[PROP_ACTIONS] =
       g_param_spec_flags ("actions",
                           P_("Actions"),
                           P_("Supported actions"),
298
                           GDK_TYPE_DRAG_ACTION, GDK_ACTION_COPY,
Matthias Clasen's avatar
Matthias Clasen committed
299
300
301
302
                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);

  g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);

Matthias Clasen's avatar
Matthias Clasen committed
303
304
305
306
307
308
  /**
   * GtkDragSource::prepare:
   * @source: the #GtkDragSource
   * @x: the X coordinate of the drag starting point
   * @y: the Y coordinate fo the drag starting point
   *
309
310
311
   * The ::prepare signal is emitted when a drag is about to be initiated.
   * It returns the * #GdkContentProvider to use for the drag that is about
   * to start. The default handler for this signal returns the value of
312
   * the #GtkDragSource:content property, so if you set up that property
313
   * ahead of time, you don't need to connect to this signal.
Matthias Clasen's avatar
Matthias Clasen committed
314
   *
315
   * Returns: (transfer full) (nullable): a #GdkContentProvider, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
316
   */
317
318
319
320
  signals[PREPARE] =
      g_signal_new (I_("prepare"),
                    G_TYPE_FROM_CLASS (class),
                    G_SIGNAL_RUN_LAST,
321
322
                    G_STRUCT_OFFSET (GtkDragSourceClass, prepare),
                    g_signal_accumulator_first_wins, NULL,
323
                    NULL,
324
                    GDK_TYPE_CONTENT_PROVIDER, 2,
325
326
                    G_TYPE_DOUBLE, G_TYPE_DOUBLE);

Matthias Clasen's avatar
Matthias Clasen committed
327
328
329
  /**
   * GtkDragSource::drag-begin:
   * @source: the #GtkDragSource
330
   * @drag: the #GdkDrag object
Matthias Clasen's avatar
Matthias Clasen committed
331
332
333
   *
   * 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
334
   * gtk_drag_source_set_icon().
Matthias Clasen's avatar
Matthias Clasen committed
335
336
337
338
339
340
341
342
   */
  signals[DRAG_BEGIN] =
      g_signal_new (I_("drag-begin"),
                    G_TYPE_FROM_CLASS (class),
                    G_SIGNAL_RUN_LAST,
                    0,
                    NULL, NULL,
                    NULL,
343
344
                    G_TYPE_NONE, 1,
                    GDK_TYPE_DRAG);
Matthias Clasen's avatar
Matthias Clasen committed
345
346
347
348

  /**
   * GtkDragSource::drag-end:
   * @source: the #GtkDragSource
349
   * @drag: the #GdkDrag object
350
351
   * @delete_data: %TRUE if the drag was performing %GDK_ACTION_MOVE,
   *    and the data should be deleted
Matthias Clasen's avatar
Matthias Clasen committed
352
353
354
   *
   * 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
Matthias Clasen's avatar
Matthias Clasen committed
355
   * things done in #GtkDragSource::prepare or #GtkDragSource::drag-begin.
Matthias Clasen's avatar
Matthias Clasen committed
356
357
358
359
360
361
362
363
   */ 
  signals[DRAG_END] =
      g_signal_new (I_("drag-end"),
                    G_TYPE_FROM_CLASS (class),
                    G_SIGNAL_RUN_LAST,
                    0,
                    NULL, NULL,
                    NULL,
364
365
366
                    G_TYPE_NONE, 2,
                    GDK_TYPE_DRAG,
                    G_TYPE_BOOLEAN);
Matthias Clasen's avatar
Matthias Clasen committed
367
368

  /**
369
   * GtkDragSource::drag-cancel:
Matthias Clasen's avatar
Matthias Clasen committed
370
   * @source: the #GtkDragSource
371
   * @drag: the #GdkDrag object
Matthias Clasen's avatar
Matthias Clasen committed
372
373
   * @reason: information on why the drag failed
   *
374
   * The ::drag-cancel signal is emitted on the drag source when a drag has
Matthias Clasen's avatar
Matthias Clasen committed
375
376
   * 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
377
   * and the default "drag operation failed" animation should not be shown.
Matthias Clasen's avatar
Matthias Clasen committed
378
379
380
   *
   * Returns: %TRUE if the failed drag operation has been already handled
   */
381
382
  signals[DRAG_CANCEL] =
      g_signal_new (I_("drag-cancel"),
Matthias Clasen's avatar
Matthias Clasen committed
383
384
385
386
                    G_TYPE_FROM_CLASS (class),
                    G_SIGNAL_RUN_LAST,
                    0,
                    _gtk_boolean_handled_accumulator, NULL,
387
388
389
                    _gtk_marshal_BOOLEAN__OBJECT_ENUM,
                    G_TYPE_BOOLEAN, 2,
                    GDK_TYPE_DRAG,
Matthias Clasen's avatar
Matthias Clasen committed
390
391
392
                    GDK_TYPE_DRAG_CANCEL_REASON);
}

393
static GdkContentProvider *
394
395
396
397
gtk_drag_source_prepare (GtkDragSource *source,
                         double         x,
                         double         y)
{
398
399
400
401
402
403
404
  if (source->actions == 0)
    return NULL;

  if (source->content == NULL)
    return NULL;

  return g_object_ref (source->content);
405
406
}

Matthias Clasen's avatar
Matthias Clasen committed
407
static void
Matthias Clasen's avatar
Matthias Clasen committed
408
409
drag_end (GtkDragSource *source,
          gboolean       success)
Matthias Clasen's avatar
Matthias Clasen committed
410
{
411
412
  gboolean delete_data;

Matthias Clasen's avatar
Matthias Clasen committed
413
414
415
  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);

416
417
418
  delete_data = success && gdk_drag_get_selected_action (source->drag) == GDK_ACTION_MOVE;

  g_signal_emit (source, signals[DRAG_END], 0, source->drag, delete_data);
Matthias Clasen's avatar
Matthias Clasen committed
419

Matthias Clasen's avatar
Matthias Clasen committed
420
  gdk_drag_drop_done (source->drag, success);
Matthias Clasen's avatar
Matthias Clasen committed
421
422
423
424
425
426
427
  g_clear_object (&source->drag);
}

static void
gtk_drag_source_dnd_finished_cb (GdkDrag       *drag,
                                 GtkDragSource *source)
{
Matthias Clasen's avatar
Matthias Clasen committed
428
  drag_end (source, TRUE);
Matthias Clasen's avatar
Matthias Clasen committed
429
430
431
432
433
434
435
436
437
}

static void
gtk_drag_source_cancel_cb (GdkDrag             *drag,
                           GdkDragCancelReason  reason,
                           GtkDragSource       *source)
{
  gboolean success = FALSE;

438
  g_signal_emit (source, signals[DRAG_CANCEL], 0, source->drag, reason, &success);
439
  drag_end (source, success);
Matthias Clasen's avatar
Matthias Clasen committed
440
441
}

442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
static void
gtk_drag_source_ensure_icon (GtkDragSource *self,
                             GdkDrag       *drag)
{
  GdkContentProvider *provider;
  GtkWidget *icon, *child;
  GdkContentFormats *formats;
  const GType *types;
  gsize i, n_types;

  icon = gtk_drag_icon_get_for_drag (drag);
  /* If an icon has been set already, we don't need to set one. */
  if (gtk_drag_icon_get_child (GTK_DRAG_ICON (icon)))
    return;

457
458
459
460
461
462
463
464
465
  if (self->paintable)
    {
      gtk_drag_icon_set_from_paintable (drag,
                                        self->paintable,
                                        self->hot_x,
                                        self->hot_y);
      return;
    }

466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
  gdk_drag_set_hotspot (drag, -2, -2);

  provider = gdk_drag_get_content (drag);
  formats = gdk_content_provider_ref_formats (provider);
  types = gdk_content_formats_get_gtypes (formats, &n_types);
  for (i = 0; i < n_types; i++)
    {
      GValue value = G_VALUE_INIT;

      g_value_init (&value, types[i]);
      if (gdk_content_provider_get_value (provider, &value, NULL))
        {
          child = gtk_drag_icon_create_widget_for_value (&value);

          if (child)
            {
              gtk_drag_icon_set_child (GTK_DRAG_ICON (icon), child);
              g_value_unset (&value);
              gdk_content_formats_unref (formats);
              return;
            }
        }
      g_value_unset (&value);
    }

  gdk_content_formats_unref (formats);
  child = gtk_image_new_from_icon_name ("text-x-generic");
  gtk_image_set_icon_size (GTK_IMAGE (child), GTK_ICON_SIZE_LARGE);
  gtk_drag_icon_set_child (GTK_DRAG_ICON (icon), child);
}

497
498
static void
gtk_drag_source_drag_begin (GtkDragSource *source)
Matthias Clasen's avatar
Matthias Clasen committed
499
{
500
501
  GtkWidget *widget;
  GdkDevice *device;
502
  double x, y;
Matthias Clasen's avatar
Matthias Clasen committed
503
504
505
506
  GtkNative *native;
  GdkSurface *surface;
  double px, py;
  int dx, dy;
507
  GdkContentProvider *content = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
508

509
510
  widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source));
  device = gtk_gesture_get_device (GTK_GESTURE (source));
Matthias Clasen's avatar
Matthias Clasen committed
511
512
513
514
515
516
517

  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);

518
  gtk_widget_translate_coordinates (widget, GTK_WIDGET (native), source->start_x, source->start_y, &x, &y);
Matthias Clasen's avatar
Matthias Clasen committed
519
520
  gdk_surface_get_device_position (surface, device, &px, &py, NULL);

521
522
  dx = round (px - x);
  dy = round (py - y);
Matthias Clasen's avatar
Matthias Clasen committed
523

524
525
  g_signal_emit (source, signals[PREPARE], 0, source->start_x, source->start_y, &content);
  if (!content)
526
527
    return;

528
529
530
  source->drag = gdk_drag_begin (surface, device, content, source->actions, dx, dy);

  g_object_unref (content);
531

Matthias Clasen's avatar
Matthias Clasen committed
532
  if (source->drag == NULL)
533
    return;
Matthias Clasen's avatar
Matthias Clasen committed
534
535
536

  gtk_widget_reset_controllers (widget);

537
  g_signal_emit (source, signals[DRAG_BEGIN], 0, source->drag);
Matthias Clasen's avatar
Matthias Clasen committed
538

539
  gtk_drag_source_ensure_icon (source, source->drag);
Matthias Clasen's avatar
Matthias Clasen committed
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554

  g_signal_connect (source->drag, "dnd-finished",
                    G_CALLBACK (gtk_drag_source_dnd_finished_cb), source);
  g_signal_connect (source->drag, "cancel",
                    G_CALLBACK (gtk_drag_source_cancel_cb), source);
}

/**
 * gtk_drag_source_new:
 *
 * Creates a new #GtkDragSource object.
 *
 * Returns: the new #GtkDragSource
 */
GtkDragSource *
555
gtk_drag_source_new (void)
Matthias Clasen's avatar
Matthias Clasen committed
556
{
557
  return g_object_new (GTK_TYPE_DRAG_SOURCE, NULL);
Matthias Clasen's avatar
Matthias Clasen committed
558
559
560
561
562
563
564
565
}

/**
 * gtk_drag_source_get_content:
 * @source: a #GtkDragSource
 *
 * Gets the current content provider of a #GtkDragSource.
 *
566
 * Returns: (transfer none): the #GdkContentProvider of @source
Matthias Clasen's avatar
Matthias Clasen committed
567
568
569
570
571
572
 */
GdkContentProvider *
gtk_drag_source_get_content (GtkDragSource *source)
{
  g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);

573
  return source->content;
Matthias Clasen's avatar
Matthias Clasen committed
574
575
576
577
578
}

/**
 * gtk_drag_source_set_content:
 * @source: a #GtkDragSource
579
 * @content: (nullable): a #GdkContentProvider, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
580
581
582
583
584
585
 *
 * 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.
Matthias Clasen's avatar
Matthias Clasen committed
586
587
588
589
590
 *
 * This function can be called before a drag is started,
 * or in a handler for the #GtkDragSource::prepare signal.
 *
 * You may consider setting the content provider back to
591
 * %NULL in a #GtkDragSource::drag-end signal handler.
Matthias Clasen's avatar
Matthias Clasen committed
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
 */
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
Matthias Clasen's avatar
Matthias Clasen committed
629
630
 * to potential drop targets. If @actions include
 * %GDK_ACTION_MOVE, you need to listen to the
631
 * #GtkDragSource::drag-end signal and handle
Matthias Clasen's avatar
Matthias Clasen committed
632
633
634
635
 * @delete_data being %TRUE.
 *
 * This function can be called before a drag is started,
 * or in a handler for the #GtkDragSource::prepare signal.
Matthias Clasen's avatar
Matthias Clasen committed
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
 */
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
654
 * @paintable: (nullable): the #GdkPaintable to use as icon, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
655
656
657
658
659
660
661
662
663
 * @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.
Matthias Clasen's avatar
Matthias Clasen committed
664
665
666
 *
 * This function can be called before a drag is started, or in
 * a #GtkDragSource::prepare or #GtkDragSource::drag-begin signal handler.
Matthias Clasen's avatar
Matthias Clasen committed
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
 */
void
gtk_drag_source_set_icon (GtkDragSource *source,
                          GdkPaintable  *paintable,
                          int            hot_x,
                          int            hot_y)
{
  g_return_if_fail (GTK_IS_DRAG_SOURCE (source));

  g_set_object (&source->paintable, paintable);

  source->hot_x = hot_x;
  source->hot_y = hot_y;
}

/**
 * gtk_drag_source_get_drag:
 * @source: a #GtkDragSource
 *
686
 * Returns the underlying #GdkDrag object for an ongoing drag.
Matthias Clasen's avatar
Matthias Clasen committed
687
 *
688
 * Returns: (nullable) (transfer none): the #GdkDrag of the current drag operation, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
 */
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;

713
      g_signal_emit (source, signals[DRAG_CANCEL], 0, source->drag, GDK_DRAG_CANCEL_ERROR, &success);
714
      drag_end (source, FALSE);
Matthias Clasen's avatar
Matthias Clasen committed
715
716
    }
}
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738

/**
 * gtk_drag_check_threshold: (method)
 * @widget: a #GtkWidget
 * @start_x: X coordinate of start of drag
 * @start_y: Y coordinate of start of drag
 * @current_x: current X coordinate
 * @current_y: current Y coordinate
 * 
 * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
 * at (@current_x, @current_y) has passed the GTK drag threshold, and thus
 * should trigger the beginning of a drag-and-drop operation.
 *
 * Returns: %TRUE if the drag threshold has been passed.
 */
gboolean
gtk_drag_check_threshold (GtkWidget *widget,
                          int        start_x,
                          int        start_y,
                          int        current_x,
                          int        current_y)
{
Benjamin Otte's avatar
Benjamin Otte committed
739
  int drag_threshold;
740
741
742
743
744
745
746
747

  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);

  drag_threshold = gtk_settings_get_dnd_drag_threshold (gtk_widget_get_settings (widget));

  return (ABS (current_x - start_x) > drag_threshold ||
          ABS (current_y - start_y) > drag_threshold);
}