gtkdnd.c 105 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2
 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3
4
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
7
8
9
10
 * 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
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16
17
 */

18
/*
19
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20
21
22
23
24
 * 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/. 
 */

25
#include "config.h"
26

27
28
#include "gtkdnd.h"
#include "gtkdndprivate.h"
29
#include "gtksettingsprivate.h"
30

31
#include <math.h>
32
33
34
#include <stdlib.h>
#include <string.h>

Matthias Clasen's avatar
Matthias Clasen committed
35
#include "gdk/gdk.h"
36

37
38
39
40
#ifdef GDK_WINDOWING_X11
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include "gdk/x11/gdkx.h"
Matthias Clasen's avatar
Matthias Clasen committed
41
42
43
#ifdef XINPUT_2
#include <X11/extensions/XInput2.h>
#endif
44
45
#endif

LRN's avatar
LRN committed
46
47
48
49
#ifdef GDK_WINDOWING_WIN32
#include <gdk/win32/gdkwin32.h>
#endif

50
51
52
53
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/wayland/gdkwayland.h>
#endif

54
#include "gtkdragdest.h"
55
56
#include "gtkgesturedrag.h"
#include "gtkgesturesingle.h"
57
#include "gtkicontheme.h"
58
59
#include "gtkimageprivate.h"
#include "gtkintl.h"
60
#include "gtkmain.h"
61
#include "gtkplug.h"
62
#include "gtktooltipprivate.h"
Owen Taylor's avatar
Owen Taylor committed
63
#include "gtkwindow.h"
64
#include "gtkrender.h"
65
#include "gtkselectionprivate.h"
66
#include "gtkwindowgroup.h"
67
#include "gtkwindowprivate.h"
68
#include "gtkwidgetprivate.h"
69

70
71
72
73
74
75

/**
 * SECTION:gtkdnd
 * @Short_description: Functions for controlling drag and drop handling
 * @Title: Drag and Drop
 *
76
77
 * GTK+ has a rich set of functions for doing inter-process communication
 * via the drag-and-drop metaphor.
78
 *
79
80
81
 * As well as the functions listed here, applications may need to use some
 * facilities provided for [Selections][gtk3-Selections]. Also, the Drag and
 * Drop API makes use of signals in the #GtkWidget class.
82
83
84
 */


85
static GSList *source_widgets = NULL;
86
87
88
89
90

typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
typedef struct _GtkDragDestInfo GtkDragDestInfo;


Owen Taylor's avatar
Owen Taylor committed
91
92
typedef enum 
{
93
94
95
96
97
  GTK_DRAG_STATUS_DRAG,
  GTK_DRAG_STATUS_WAIT,
  GTK_DRAG_STATUS_DROP
} GtkDragStatus;

Owen Taylor's avatar
Owen Taylor committed
98
99
struct _GtkDragSourceInfo 
{
100
101
  GtkWidget         *widget;
  GtkTargetList     *target_list; /* Targets for drag data */
102
  GdkDragAction      possible_actions; /* Actions allowed by source */
103
  GdkDragContext    *context;     /* drag context */
104
  GtkWidget         *icon_window; /* Window for drag */
105
  GtkWidget         *icon_widget; /* Widget for drag */
106
  GtkWidget         *ipc_widget;  /* GtkInvisible for grab, message passing */
107
108
109
  GdkCursor         *cursor;      /* Cursor for drag */
  gint hot_x, hot_y;              /* Hot spot for drag */
  gint button;                    /* mouse button starting drag */
Owen Taylor's avatar
Owen Taylor committed
110

111
  GtkDragStatus      status;      /* drag status */
112
  GdkEvent          *last_event;  /* pending event */
Owen Taylor's avatar
Owen Taylor committed
113

114
115
  gint               start_x, start_y; /* Initial position */
  gint               cur_x, cur_y;     /* Current Position */
116
  GdkScreen         *cur_screen;       /* Current screen for pointer */
Owen Taylor's avatar
Owen Taylor committed
117

118
  guint32            grab_time;   /* timestamp for initial grab */
119
  GList             *selections;  /* selections we've claimed */
120

121
  GtkDragDestInfo   *proxy_dest;  /* Set if this is a proxy drag */
Owen Taylor's avatar
Owen Taylor committed
122

123
  guint              update_idle;      /* Idle function to update the drag */
Owen Taylor's avatar
Owen Taylor committed
124
  guint              drop_timeout;     /* Timeout for aborting drop */
125
  guint              destroy_icon : 1; /* If true, destroy icon_widget */
126
  guint              have_grab    : 1; /* Do we still have the pointer grab */
127
128
};

129
130
131
132
133
134
135
struct _GtkDragDestInfo
{
  GtkWidget         *widget;              /* Widget in which drag is in */
  GdkDragContext    *context;             /* Drag context */
  GtkDragSourceInfo *proxy_source;        /* Set if this is a proxy drag */
  GtkSelectionData  *proxy_data;          /* Set while retrieving proxied data */
  guint32            proxy_drop_time;     /* Timestamp for proxied drop */
136
  guint              proxy_drop_wait : 1; /* Set if we are waiting for a
137
138
139
140
141
                                           * status reply before sending
                                           * a proxied drop on.
                                           */
  guint              dropped : 1;         /* Set after we receive a drop */
  gint               drop_x, drop_y;      /* Position of drop */
142
143
};

144
#define DROP_ABORT_TIME 300000
145

146
147
148
149
150
typedef gboolean (* GtkDragDestCallback) (GtkWidget      *widget,
                                          GdkDragContext *context,
                                          gint            x,
                                          gint            y,
                                          guint32         time);
151
152
153

/* Enumeration for some targets we handle internally */

Owen Taylor's avatar
Owen Taylor committed
154
enum {
Matthias Clasen's avatar
Fix DND    
Matthias Clasen committed
155
  TARGET_DELETE = 0x40000002
156
157
158
};

/* Forward declarations */
159
static void          gtk_drag_get_event_actions (const GdkEvent  *event,
160
161
162
163
                                                 gint             button,
                                                 GdkDragAction    actions,
                                                 GdkDragAction   *suggested_action,
                                                 GdkDragAction   *possible_actions);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
164
165
static GdkCursor *   gtk_drag_get_cursor         (GtkWidget      *widget,
                                                  GdkDisplay     *display,
166
167
                                                  GdkDragAction   action,
                                                  GtkDragSourceInfo *info);
168
static void          gtk_drag_update_cursor      (GtkDragSourceInfo *info);
Matthias Clasen's avatar
Matthias Clasen committed
169
170
static GtkWidget    *gtk_drag_get_ipc_widget            (GtkWidget *widget);
static GtkWidget    *gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen);
Owen Taylor's avatar
Owen Taylor committed
171
static void          gtk_drag_release_ipc_widget (GtkWidget      *widget);
172

173
static void     gtk_drag_selection_received     (GtkWidget        *widget,
174
175
176
                                                 GtkSelectionData *selection_data,
                                                 guint             time,
                                                 gpointer          data);
177
178
179
180
181
182
183
static gboolean gtk_drag_find_widget            (GtkWidget        *widget,
                                                 GdkDragContext   *context,
                                                 GtkDragDestInfo  *info,
                                                 gint              x,
                                                 gint              y,
                                                 guint32           time,
                                                 GtkDragDestCallback callback);
184
static void     gtk_drag_proxy_begin            (GtkWidget        *widget,
185
186
                                                 GtkDragDestInfo  *dest_info,
                                                 guint32           time);
187
static void     gtk_drag_dest_leave             (GtkWidget        *widget,
188
189
                                                 GdkDragContext   *context,
                                                 guint             time);
190
static gboolean gtk_drag_dest_motion            (GtkWidget        *widget,
191
192
193
194
                                                 GdkDragContext   *context,
                                                 gint              x,
                                                 gint              y,
                                                 guint             time);
195
static gboolean gtk_drag_dest_drop              (GtkWidget        *widget,
196
197
198
199
                                                 GdkDragContext   *context,
                                                 gint              x,
                                                 gint              y,
                                                 guint             time);
200
201
static void     gtk_drag_dest_set_widget        (GtkDragDestInfo  *info,
                                                 GtkWidget        *widget);
Owen Taylor's avatar
Owen Taylor committed
202
203

static GtkDragDestInfo *  gtk_drag_get_dest_info     (GdkDragContext *context,
204
                                                      gboolean        create);
Owen Taylor's avatar
Owen Taylor committed
205
static GtkDragSourceInfo *gtk_drag_get_source_info   (GdkDragContext *context,
206
                                                      gboolean        create);
Owen Taylor's avatar
Owen Taylor committed
207
static void               gtk_drag_clear_source_info (GdkDragContext *context);
208
209

static void gtk_drag_source_check_selection    (GtkDragSourceInfo *info, 
210
211
                                                GdkAtom            selection,
                                                guint32            time);
212
static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
213
                                                guint32            time);
214
static void gtk_drag_drop                      (GtkDragSourceInfo *info,
215
                                                guint32            time);
216
static void gtk_drag_drop_finished             (GtkDragSourceInfo *info,
217
218
                                                GtkDragResult      result,
                                                guint              time);
219
static void gtk_drag_cancel_internal           (GtkDragSourceInfo *info,
220
221
                                                GtkDragResult      result,
                                                guint32            time);
222
223

static void gtk_drag_selection_get             (GtkWidget         *widget, 
224
225
226
227
                                                GtkSelectionData  *selection_data,
                                                guint              sel_info,
                                                guint32            time,
                                                gpointer           data);
228
static void gtk_drag_remove_icon               (GtkDragSourceInfo *info);
Owen Taylor's avatar
Owen Taylor committed
229
static void gtk_drag_source_info_destroy       (GtkDragSourceInfo *info);
230
231
232
233

static void gtk_drag_context_drop_performed_cb (GdkDragContext    *context,
                                                guint              time,
                                                GtkDragSourceInfo *info);
234
235
236
static void gtk_drag_context_cancel_cb         (GdkDragContext      *context,
                                                GdkDragCancelReason  reason,
                                                GtkDragSourceInfo   *info);
237
238
239
240
241
static void gtk_drag_context_action_cb         (GdkDragContext    *context,
                                                GdkDragAction      action,
                                                GtkDragSourceInfo *info);
static void gtk_drag_context_dnd_finished_cb   (GdkDragContext    *context,
                                                GtkDragSourceInfo *info);
242
243
static void gtk_drag_add_update_idle           (GtkDragSourceInfo *info);

244
static void gtk_drag_update                    (GtkDragSourceInfo *info,
245
246
247
248
                                                GdkScreen         *screen,
                                                gint               x_root,
                                                gint               y_root,
                                                const GdkEvent    *event);
249
static gboolean gtk_drag_motion_cb             (GtkWidget         *widget, 
250
251
                                                GdkEventMotion    *event, 
                                                gpointer           data);
252
static gboolean gtk_drag_key_cb                (GtkWidget         *widget, 
253
254
                                                GdkEventKey       *event, 
                                                gpointer           data);
255
static gboolean gtk_drag_grab_broken_event_cb  (GtkWidget          *widget,
256
257
                                                GdkEventGrabBroken *event,
                                                gpointer            data);
258
static void     gtk_drag_grab_notify_cb        (GtkWidget         *widget,
259
260
                                                gboolean           was_grabbed,
                                                gpointer           data);
261
static gboolean gtk_drag_button_release_cb     (GtkWidget         *widget, 
262
263
                                                GdkEventButton    *event, 
                                                gpointer           data);
264
static gboolean gtk_drag_abort_timeout         (gpointer           data);
265

Cosimo Cecchi's avatar
Cosimo Cecchi committed
266
static void     set_icon_helper (GdkDragContext    *context,
Benjamin Otte's avatar
Benjamin Otte committed
267
                                 GtkImageDefinition*def,
Cosimo Cecchi's avatar
Cosimo Cecchi committed
268
                                 gint               hot_x,
Matthias Clasen's avatar
Matthias Clasen committed
269
                                 gint               hot_y);
270

271
272
273
274
/************************
 * Cursor and Icon data *
 ************************/

Owen Taylor's avatar
Owen Taylor committed
275
static struct {
276
  GdkDragAction action;
277
278
  const gchar  *name;
  GdkPixbuf    *pixbuf;
279
280
  GdkCursor    *cursor;
} drag_cursors[] = {
Matthias Clasen's avatar
Matthias Clasen committed
281
  { GDK_ACTION_DEFAULT, NULL },
282
  { GDK_ACTION_ASK,   "dnd-ask",  NULL, NULL },
283
284
285
286
  { GDK_ACTION_COPY,  "copy", NULL, NULL },
  { GDK_ACTION_MOVE,  "move", NULL, NULL },
  { GDK_ACTION_LINK,  "alias", NULL, NULL },
  { 0              ,  "no-drop", NULL, NULL },
287
288
289
290
291
292
};

/*********************
 * Utility functions *
 *********************/

Owen Taylor's avatar
Owen Taylor committed
293
static GtkWidget *
Matthias Clasen's avatar
Matthias Clasen committed
294
gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen)
295
296
{
  GtkWidget *result;
297
  GSList *drag_widgets = g_object_get_data (G_OBJECT (screen), 
298
                                            "gtk-dnd-ipc-widgets");
299
300
301
302
303
304
  
  if (drag_widgets)
    {
      GSList *tmp = drag_widgets;
      result = drag_widgets->data;
      drag_widgets = drag_widgets->next;
305
      g_object_set_data (G_OBJECT (screen),
306
307
                         I_("gtk-dnd-ipc-widgets"),
                         drag_widgets);
308
309
310
311
      g_slist_free_1 (tmp);
    }
  else
    {
Matthias Clasen's avatar
Matthias Clasen committed
312
313
314
      result = gtk_window_new (GTK_WINDOW_POPUP);
      gtk_window_set_screen (GTK_WINDOW (result), screen);
      gtk_window_resize (GTK_WINDOW (result), 1, 1);
Matthias Clasen's avatar
Matthias Clasen committed
315
      gtk_window_move (GTK_WINDOW (result), -99, -99);
316
      gtk_widget_show (result);
Matthias Clasen's avatar
Matthias Clasen committed
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
    }  

  return result;
}

static GtkWidget *
gtk_drag_get_ipc_widget (GtkWidget *widget)
{
  GtkWidget *result;
  GtkWidget *toplevel;

  result = gtk_drag_get_ipc_widget_for_screen (gtk_widget_get_screen (widget));
  
  toplevel = gtk_widget_get_toplevel (widget);
  
  if (GTK_IS_WINDOW (toplevel))
    {
334
335
      if (gtk_window_has_group (GTK_WINDOW (toplevel)))
        gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
Matthias Clasen's avatar
Matthias Clasen committed
336
                                     GTK_WINDOW (result));
337
    }
Owen Taylor's avatar
Owen Taylor committed
338

339
340
341
  return result;
}

Matthias Clasen's avatar
Matthias Clasen committed
342
#if defined (GDK_WINDOWING_X11)
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360

/*
 * We want to handle a handful of keys during DND, e.g. Escape to abort.
 * Grabbing the keyboard has the unfortunate side-effect of preventing
 * useful things such as using Alt-Tab to cycle between windows or
 * switching workspaces. Therefore, we just grab the few keys we are
 * interested in. Note that we need to put the grabs on the root window
 * in order for them to still work when the focus is moved to another
 * app/workspace.
 *
 * GDK needs a little help to successfully deliver root key events...
 */

static GdkFilterReturn
root_key_filter (GdkXEvent *xevent,
                 GdkEvent  *event,
                 gpointer   data)
{
361
  XEvent *ev = (XEvent *) xevent;
362
363
364
365

  if ((ev->type == KeyPress || ev->type == KeyRelease) &&
      ev->xkey.root == ev->xkey.window)
    ev->xkey.window = (Window)data;
366
367
368
369
370
371
372
373
374
375
376
377
  else if (ev->type == GenericEvent)
    {
      XGenericEventCookie *cookie;
      XIDeviceEvent *dev;

      cookie = &ev->xcookie;
      dev = (XIDeviceEvent *) cookie->data;

      if (dev->evtype == XI_KeyPress ||
          dev->evtype == XI_KeyRelease)
        dev->event = (Window)data;
    }
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412

  return GDK_FILTER_CONTINUE;
}

typedef struct {
  gint keysym;
  gint modifiers;
} GrabKey;

static GrabKey grab_keys[] = {
  { XK_Escape, 0 },
  { XK_space, 0 },
  { XK_KP_Space, 0 },
  { XK_Return, 0 },
  { XK_KP_Enter, 0 },
  { XK_Up, 0 },
  { XK_Up, Mod1Mask },
  { XK_Down, 0 },
  { XK_Down, Mod1Mask },
  { XK_Left, 0 },
  { XK_Left, Mod1Mask },
  { XK_Right, 0 },
  { XK_Right, Mod1Mask },
  { XK_KP_Up, 0 },
  { XK_KP_Up, Mod1Mask },
  { XK_KP_Down, 0 },
  { XK_KP_Down, Mod1Mask },
  { XK_KP_Left, 0 },
  { XK_KP_Left, Mod1Mask },
  { XK_KP_Right, 0 },
  { XK_KP_Right, Mod1Mask }
};

static void
grab_dnd_keys (GtkWidget *widget,
413
               GdkDevice *device,
414
415
416
               guint32    time)
{
  guint i;
417
  GdkDisplay *display;
418
419
  GdkWindow *window, *root;
  gint keycode;
Matthias Clasen's avatar
Matthias Clasen committed
420
421
422
423
424
425
426
427
#ifdef XINPUT_2
  gint deviceid;
  XIGrabModifiers mods;
  gint num_mods;
  XIEventMask evmask;
  unsigned char mask[(XI_LASTEVENT + 7)/8];
  gboolean using_xi2;

428
  window = gtk_widget_get_window (widget);
Matthias Clasen's avatar
Matthias Clasen committed
429
430
  if (!GDK_IS_X11_WINDOW (window))
    {
431
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
Matthias Clasen's avatar
Matthias Clasen committed
432
433
434
435
436
      gdk_device_grab (device,
                       gtk_widget_get_window (widget),
                       GDK_OWNERSHIP_APPLICATION, FALSE,
                       GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
                       NULL, time);
437
      G_GNUC_END_IGNORE_DEPRECATIONS;
Matthias Clasen's avatar
Matthias Clasen committed
438
439
440
      return;
    }

441
442
  deviceid = gdk_x11_device_get_id (device);

443
  if (GDK_IS_X11_DEVICE_XI2 (device))
444
445
446
447
    using_xi2 = TRUE;
  else
    using_xi2 = FALSE;
#endif
Matthias Clasen's avatar
Matthias Clasen committed
448

449
  display = gtk_widget_get_display (widget);
450
451
  root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));

452
  gdk_x11_display_error_trap_push (display);
453
454
455
456

  for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
    {
      keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
457
458
      if (keycode == NoSymbol)
        continue;
Matthias Clasen's avatar
Matthias Clasen committed
459
460
461
462
463
464
465
466
467
468
469
470

#ifdef XINPUT_2
      if (using_xi2)
        {
          memset (mask, 0, sizeof (mask));
          XISetMask (mask, XI_KeyPress);
          XISetMask (mask, XI_KeyRelease);

          evmask.deviceid = deviceid;
          evmask.mask_len = sizeof (mask);
          evmask.mask = mask;

Bastien Nocera's avatar
Bastien Nocera committed
471
472
          num_mods = 1;
          mods.modifiers = grab_keys[i].modifiers;
Matthias Clasen's avatar
Matthias Clasen committed
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492

          XIGrabKeycode (GDK_WINDOW_XDISPLAY (window),
                         deviceid,
                         keycode,
                         GDK_WINDOW_XID (root),
                         GrabModeAsync,
                         GrabModeAsync,
                         False,
                         &evmask,
                         num_mods,
                         &mods);
        }
      else
#endif
        XGrabKey (GDK_WINDOW_XDISPLAY (window),
                  keycode, grab_keys[i].modifiers,
                  GDK_WINDOW_XID (root),
                  FALSE,
                  GrabModeAsync,
                  GrabModeAsync);
493
494
    }

495
496
  gdk_display_flush (display);
  gdk_x11_display_error_trap_pop_ignored (display);
497

Benjamin Otte's avatar
Benjamin Otte committed
498
  gdk_window_add_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
499
500
}

501
static void
502
ungrab_dnd_keys (GtkWidget *widget,
503
                 GdkDevice *device,
504
505
506
507
                 guint32    time)
{
  guint i;
  GdkWindow *window, *root;
508
  GdkDisplay *display;
509
  gint keycode;
Matthias Clasen's avatar
Matthias Clasen committed
510
511
512
513
514
515
#ifdef XINPUT_2
  XIGrabModifiers mods;
  gint num_mods;
  gint deviceid;
  gboolean using_xi2;

516
  window = gtk_widget_get_window (widget);
Matthias Clasen's avatar
Matthias Clasen committed
517
518
  if (!GDK_IS_X11_WINDOW (window))
    {
519
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
Matthias Clasen's avatar
Matthias Clasen committed
520
      gdk_device_ungrab (device, time);
521
      G_GNUC_END_IGNORE_DEPRECATIONS;
Matthias Clasen's avatar
Matthias Clasen committed
522
523
524
      return;
    }

525
526
  deviceid = gdk_x11_device_get_id (device);

527
  if (GDK_IS_X11_DEVICE_XI2 (device))
528
529
530
531
532
    using_xi2 = TRUE;
  else
    using_xi2 = FALSE;
#endif

533
  display = gtk_widget_get_display (widget);
534
535
  root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));

Benjamin Otte's avatar
Benjamin Otte committed
536
  gdk_window_remove_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
537

538
  gdk_x11_display_error_trap_push (display);
539
540
541
542

  for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
    {
      keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
543
544
      if (keycode == NoSymbol)
        continue;
Matthias Clasen's avatar
Matthias Clasen committed
545
546
547
548

#ifdef XINPUT_2
      if (using_xi2)
        {
Bastien Nocera's avatar
Bastien Nocera committed
549
550
          num_mods = 1;
          mods.modifiers = grab_keys[i].modifiers;
Matthias Clasen's avatar
Matthias Clasen committed
551
552
553
554
555
556
557
558
559
560
561
562
563

          XIUngrabKeycode (GDK_WINDOW_XDISPLAY (window),
                           deviceid,
                           keycode,
                           GDK_WINDOW_XID (root),
                           num_mods,
                           &mods);
        }
      else
#endif
        XUngrabKey (GDK_WINDOW_XDISPLAY (window),
                    keycode, grab_keys[i].modifiers,
                    GDK_WINDOW_XID (root));
564
565
    }

566
567
  gdk_display_flush (display);
  gdk_x11_display_error_trap_pop_ignored (display);
568
569
}

Matthias Clasen's avatar
Matthias Clasen committed
570
#else /* !GDK_WINDOWING_X11 */
571
572
573

static void
grab_dnd_keys (GtkWidget *widget,
574
               GdkDevice *device,
575
576
               guint32    time)
{
577
  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
578
579
  gdk_device_grab (device,
                   gtk_widget_get_window (widget),
580
581
582
                   GDK_OWNERSHIP_APPLICATION, FALSE,
                   GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
                   NULL, time);
583
  G_GNUC_END_IGNORE_DEPRECATIONS;
584
585
586
587
}

static void
ungrab_dnd_keys (GtkWidget *widget,
588
                 GdkDevice *device,
589
590
                 guint32    time)
{
591
  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
592
  gdk_device_ungrab (device, time);
593
  G_GNUC_END_IGNORE_DEPRECATIONS;
594
595
}

596
#endif /* GDK_WINDOWING_X11 */
597

598
/*
599
 * gtk_drag_release_ipc_widget:
600
601
602
603
 * @widget: the widget to release
 *
 * Releases widget retrieved with gtk_drag_get_ipc_widget().
 */
604
605
606
static void
gtk_drag_release_ipc_widget (GtkWidget *widget)
{
Matthias Clasen's avatar
Matthias Clasen committed
607
  GtkWindow *window = GTK_WINDOW (widget);
608
  GdkScreen *screen = gtk_widget_get_screen (widget);
609
  GdkDragContext *context = g_object_get_data (G_OBJECT (widget), "drag-context");
610
  GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
611
                                            "gtk-dnd-ipc-widgets");
612
613
614
615
616
617
618
619
620
621
622
  GdkDevice *pointer, *keyboard;

  if (context)
    {
      pointer = gdk_drag_context_get_device (context);
      keyboard = gdk_device_get_associated_device (pointer);

      if (keyboard)
        ungrab_dnd_keys (widget, keyboard, GDK_CURRENT_TIME);
    }

623
624
625
  if (gtk_window_has_group (window))
    gtk_window_group_remove_window (gtk_window_get_group (window),
                                    window);
626
  drag_widgets = g_slist_prepend (drag_widgets, widget);
627
  g_object_set_data (G_OBJECT (screen),
628
629
                     I_("gtk-dnd-ipc-widgets"),
                     drag_widgets);
630
631
}

632
633
634
635
636
637
638
639
640
static guint32
gtk_drag_get_event_time (GdkEvent *event)
{
  guint32 tm = GDK_CURRENT_TIME;
  
  if (event)
    switch (event->type)
      {
      case GDK_MOTION_NOTIFY:
641
        tm = event->motion.time; break;
642
643
644
645
      case GDK_BUTTON_PRESS:
      case GDK_2BUTTON_PRESS:
      case GDK_3BUTTON_PRESS:
      case GDK_BUTTON_RELEASE:
646
        tm = event->button.time; break;
647
648
      case GDK_KEY_PRESS:
      case GDK_KEY_RELEASE:
649
        tm = event->key.time; break;
650
651
      case GDK_ENTER_NOTIFY:
      case GDK_LEAVE_NOTIFY:
652
        tm = event->crossing.time; break;
653
      case GDK_PROPERTY_NOTIFY:
654
        tm = event->property.time; break;
655
656
657
      case GDK_SELECTION_CLEAR:
      case GDK_SELECTION_REQUEST:
      case GDK_SELECTION_NOTIFY:
658
        tm = event->selection.time; break;
659
660
      case GDK_PROXIMITY_IN:
      case GDK_PROXIMITY_OUT:
661
662
663
        tm = event->proximity.time; break;
      default:                  /* use current time */
        break;
664
665
666
667
668
      }
  
  return tm;
}

669
static void
670
gtk_drag_get_event_actions (const GdkEvent *event,
671
672
673
674
                            gint            button,
                            GdkDragAction   actions,
                            GdkDragAction  *suggested_action,
                            GdkDragAction  *possible_actions)
675
{
676
677
  *suggested_action = 0;
  *possible_actions = 0;
Owen Taylor's avatar
Owen Taylor committed
678

679
680
681
682
683
  if (event)
    {
      GdkModifierType state = 0;
      
      switch (event->type)
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
        {
        case GDK_MOTION_NOTIFY:
          state = event->motion.state;
          break;
        case GDK_BUTTON_PRESS:
        case GDK_2BUTTON_PRESS:
        case GDK_3BUTTON_PRESS:
        case GDK_BUTTON_RELEASE:
          state = event->button.state;
          break;
        case GDK_KEY_PRESS:
        case GDK_KEY_RELEASE:
          state = event->key.state;
          break;
        case GDK_ENTER_NOTIFY:
        case GDK_LEAVE_NOTIFY:
          state = event->crossing.state;
          break;
        default:
          break;
        }
705

706
      if ((button == GDK_BUTTON_MIDDLE || button == GDK_BUTTON_SECONDARY) && (actions & GDK_ACTION_ASK))
707
708
709
710
        {
          *suggested_action = GDK_ACTION_ASK;
          *possible_actions = actions;
        }
711
      else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
        {
          if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
            {
              if (actions & GDK_ACTION_LINK)
                {
                  *suggested_action = GDK_ACTION_LINK;
                  *possible_actions = GDK_ACTION_LINK;
                }
            }
          else if (state & GDK_CONTROL_MASK)
            {
              if (actions & GDK_ACTION_COPY)
                {
                  *suggested_action = GDK_ACTION_COPY;
                  *possible_actions = GDK_ACTION_COPY;
                }
            }
          else
            {
              if (actions & GDK_ACTION_MOVE)
                {
                  *suggested_action = GDK_ACTION_MOVE;
                  *possible_actions = GDK_ACTION_MOVE;
                }
            }
        }
738
      else
739
740
741
742
743
744
745
746
747
748
749
750
        {
          *possible_actions = actions;

          if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
            *suggested_action = GDK_ACTION_ASK;
          else if (actions & GDK_ACTION_COPY)
            *suggested_action =  GDK_ACTION_COPY;
          else if (actions & GDK_ACTION_MOVE)
            *suggested_action = GDK_ACTION_MOVE;
          else if (actions & GDK_ACTION_LINK)
            *suggested_action = GDK_ACTION_LINK;
        }
751
    }
Owen Taylor's avatar
Owen Taylor committed
752
753
754
755
756
  else
    {
      *possible_actions = actions;
      
      if (actions & GDK_ACTION_COPY)
757
        *suggested_action =  GDK_ACTION_COPY;
Owen Taylor's avatar
Owen Taylor committed
758
      else if (actions & GDK_ACTION_MOVE)
759
        *suggested_action = GDK_ACTION_MOVE;
Owen Taylor's avatar
Owen Taylor committed
760
      else if (actions & GDK_ACTION_LINK)
761
        *suggested_action = GDK_ACTION_LINK;
Owen Taylor's avatar
Owen Taylor committed
762
    }
763
764
}

765
766
767
768
769
770
771
772
static void
ensure_drag_cursor_pixbuf (int i)
{
  if (drag_cursors[i].pixbuf == NULL)
    {
      char *path = g_strconcat ("/org/gtk/libgtk/cursor/",  drag_cursors[i].name, ".png", NULL);
      GInputStream *stream = g_resources_open_stream (path, 0, NULL);
      if (stream != NULL)
773
774
775
776
        {
          drag_cursors[i].pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
          g_object_unref (stream);
        }
777
778
779
780
      g_free (path);
    }
}

Owen Taylor's avatar
Owen Taylor committed
781
static GdkCursor *
Cosimo Cecchi's avatar
Cosimo Cecchi committed
782
783
gtk_drag_get_cursor (GtkWidget         *widget,
                     GdkDisplay        *display,
784
785
                     GdkDragAction      action,
                     GtkDragSourceInfo *info)
786
{
Matthias Clasen's avatar
Matthias Clasen committed
787
  gint i;
788
789

  /* reconstruct the cursors for each new drag (thus !info),
Matthias Clasen's avatar
Matthias Clasen committed
790
791
   * to catch cursor theme changes
   */
792
793
  if (!info)
    {
Benjamin Otte's avatar
Benjamin Otte committed
794
      for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
Matthias Clasen's avatar
Matthias Clasen committed
795
        g_clear_object (&drag_cursors[i].cursor);
796
    }
Matthias Clasen's avatar
Matthias Clasen committed
797

Benjamin Otte's avatar
Benjamin Otte committed
798
  for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
799
800
    if (drag_cursors[i].action == action)
      break;
801

802
803
  if (drag_cursors[i].cursor != NULL)
    {
804
      if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
Matthias Clasen's avatar
Matthias Clasen committed
805
        g_clear_object (&drag_cursors[i].cursor);
806
    }
Matthias Clasen's avatar
Matthias Clasen committed
807

808
809
  if (drag_cursors[i].cursor == NULL)
    drag_cursors[i].cursor = gdk_cursor_new_from_name (display, drag_cursors[i].name);
Matthias Clasen's avatar
Matthias Clasen committed
810

811
  if (drag_cursors[i].cursor == NULL)
812
813
814
815
    {
      ensure_drag_cursor_pixbuf (i);
      drag_cursors[i].cursor = gdk_cursor_new_from_pixbuf (display, drag_cursors[i].pixbuf, 0, 0);
    }
816

817
818
819
  return drag_cursors[i].cursor;
}

820
821
822
823
824
825
826
827
828
static void
gtk_drag_update_cursor (GtkDragSourceInfo *info)
{
  GdkCursor *cursor;
  gint i;

  if (!info->have_grab)
    return;

Benjamin Otte's avatar
Benjamin Otte committed
829
  for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
830
    if (info->cursor == drag_cursors[i].cursor)
831
      break;
832

Benjamin Otte's avatar
Benjamin Otte committed
833
  if (i == G_N_ELEMENTS (drag_cursors))
834
835
    return;

Cosimo Cecchi's avatar
Cosimo Cecchi committed
836
  cursor = gtk_drag_get_cursor (info->widget,
837
                                gdk_cursor_get_display (info->cursor),
838
                                drag_cursors[i].action, info);
839

840
841
  if (cursor != info->cursor)
    {
842
843
844
      GdkDevice *pointer;

      pointer = gdk_drag_context_get_device (info->context);
845
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
846
847
      gdk_device_grab (pointer,
                       gtk_widget_get_window (info->ipc_widget),
848
849
850
                       GDK_OWNERSHIP_APPLICATION, FALSE,
                       GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
                       cursor, info->grab_time);
851
      G_GNUC_END_IGNORE_DEPRECATIONS;
852
853
854
855
      info->cursor = cursor;
    }
}

856
857
858
859
/********************
 * Destination side *
 ********************/

860
/**
861
 * gtk_drag_get_data: (method)
862
 * @widget: the widget that will receive the
863
 *   #GtkWidget::drag-data-received signal
864
 * @context: the drag context
865
 * @target: the target (form of the data) to retrieve
866
 * @time_: a timestamp for retrieving the data. This will
Matthias Clasen's avatar
Matthias Clasen committed
867
868
 *   generally be the time received in a #GtkWidget::drag-motion
 *   or #GtkWidget::drag-drop signal
869
 *
870
871
872
873
874
875
876
877
 * Gets the data associated with a drag. When the data
 * is received or the retrieval fails, GTK+ will emit a
 * #GtkWidget::drag-data-received signal. Failure of the retrieval
 * is indicated by the length field of the @selection_data
 * signal parameter being negative. However, when gtk_drag_get_data()
 * is called implicitely because the %GTK_DEST_DEFAULT_DROP was set,
 * then the widget will not receive notification of failed
 * drops.
878
 */
879
void
880
gtk_drag_get_data (GtkWidget      *widget,
881
882
883
                   GdkDragContext *context,
                   GdkAtom         target,
                   guint32         time_)
884
885
{
  GtkWidget *selection_widget;
Owen Taylor's avatar
Owen Taylor committed
886

887
888
  g_return_if_fail (GTK_IS_WIDGET (widget));
  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
Owen Taylor's avatar
Owen Taylor committed
889

Matthias Clasen's avatar
Matthias Clasen committed
890
  selection_widget = gtk_drag_get_ipc_widget (widget);
Owen Taylor's avatar
Owen Taylor committed
891

Manish Singh's avatar
Manish Singh committed
892
893
  g_object_ref (context);
  g_object_ref (widget);
894

895
  g_signal_connect (selection_widget, "selection-received",
896
                    G_CALLBACK (gtk_drag_selection_received), widget);
Owen Taylor's avatar
Owen Taylor committed
897

Matthias Clasen's avatar
Matthias Clasen committed
898
  g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
Owen Taylor's avatar
Owen Taylor committed
899

900
  gtk_selection_convert (selection_widget,
901
902
903
                         gdk_drag_get_selection (context),
                         target,
                         time_);
904
905
}

Matthias Clasen's avatar
Matthias Clasen committed
906
/**
907
 * gtk_drag_get_source_widget:
Matthias Clasen's avatar
Matthias Clasen committed
908
909
910
911
 * @context: a (destination side) drag context
 *
 * Determines the source widget for a drag.
 *
912
 * Returns: (nullable) (transfer none): if the drag is occurring
Matthias Clasen's avatar
Matthias Clasen committed
913
914
915
 *     within a single application, a pointer to the source widget.
 *     Otherwise, %NULL.
 */
Owen Taylor's avatar
Owen Taylor committed
916
GtkWidget *
917
918
919
gtk_drag_get_source_widget (GdkDragContext *context)
{
  GSList *tmp_list;
Owen Taylor's avatar
Owen Taylor committed
920

921
922
  g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
  
923
924
925
926
  tmp_list = source_widgets;
  while (tmp_list)
    {
      GtkWidget *ipc_widget = tmp_list->data;
927

928
      if (gtk_widget_get_window (ipc_widget) == gdk_drag_context_get_source_window (context))
929
930
931
        {
          GtkDragSourceInfo *info;
          info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
Owen Taylor's avatar
Owen Taylor committed
932

933
934
          return info ? info->widget : NULL;
        }
Owen Taylor's avatar
Owen Taylor committed
935

936
937
      tmp_list = tmp_list->next;
    }
Owen Taylor's avatar
Owen Taylor committed
938

939
940
941
  return NULL;
}

942
/**
943
 * gtk_drag_finish:
944
 * @context: the drag context
945
946
947
 * @success: a flag indicating whether the drop was successful
 * @del: a flag indicating whether the source should delete the
 *   original data. (This should be %TRUE for a move)
948
 * @time_: the timestamp from the #GtkWidget::drag-drop signal
949
950
951
952
 *
 * Informs the drag source that the drop is finished, and
 * that the data of the drag will no longer be required.
 */
953
954
void 
gtk_drag_finish (GdkDragContext *context,
955
956
957
                 gboolean        success,
                 gboolean        del,
                 guint32         time)
958
959
{
  GdkAtom target = GDK_NONE;
Owen Taylor's avatar
Owen Taylor committed
960

961
  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
Owen Taylor's avatar
Owen Taylor committed
962

963
  if (success && del)
964
    {
965
      target = gdk_atom_intern_static_string ("DELETE");
966
    }
Owen Taylor's avatar
Owen Taylor committed
967

968
969
  if (target != GDK_NONE)
    {
970
      GtkWidget *selection_widget = gtk_drag_get_ipc_widget_for_screen (gdk_window_get_screen (gdk_drag_context_get_source_window (context)));
Owen Taylor's avatar
Owen Taylor committed
971

Manish Singh's avatar
Manish Singh committed
972
      g_object_ref (context);
973
      
Matthias Clasen's avatar
Matthias Clasen committed
974
      g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
975
      g_signal_connect (selection_widget, "selection-received",
976
977
                        G_CALLBACK (gtk_drag_selection_received),
                        NULL);
978
979
      
      gtk_selection_convert (selection_widget,
980
981
982
                             gdk_drag_get_selection (context),
                             target,
                             time);
983
984
    }
  
985
  if (!(success && del))
986
987
988
    gdk_drop_finish (context, success, time);
}

989
/**
990
 * gtk_drag_highlight: (method)
991
 * @widget: a widget to highlight
992
 *
993
994
995
 * Highlights a widget as a currently hovered drop target.
 * To end the highlight, call gtk_drag_unhighlight().
 * GTK+ calls this automatically if %GTK_DEST_DEFAULT_HIGHLIGHT is set.
996
 */
997
void
998
999
gtk_drag_highlight (GtkWidget  *widget)
{
1000
  g_return_if_fail (GTK_IS_WIDGET (widget));
Manish Singh's avatar
Manish Singh committed
1001

1002
  gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
1003
1004
}

1005
/**
1006
 * gtk_drag_unhighlight: (method)
1007
 * @widget: a widget to remove the highlight from
1008
 *
1009
1010
 * Removes a highlight set by gtk_drag_highlight() from
 * a widget.
1011
 */
1012
void
Owen Taylor's avatar
Owen Taylor committed
1013
gtk_drag_unhighlight (GtkWidget *widget)
1014
{
1015
  g_return_if_fail (GTK_IS_WIDGET (widget));
Manish Singh's avatar
Manish Singh committed
1016

1017
  gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
1018
1019
}

1020
/*
1021
 * _gtk_drag_dest_handle_event:
1022
1023
 * @toplevel: Toplevel widget that received the event
 * @event: the event to handle
1024
 *
1025
1026
1027
 * Called from widget event handling code on Drag events
 * for destinations.
 */
1028
void
1029
_gtk_drag_dest_handle_event (GtkWidget *toplevel,
1030
                             GdkEvent  *event)