gtkdnd.c 104 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>

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"
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 */
110

111
  GtkDragStatus      status;      /* drag status */
112
  GdkEvent          *last_event;  /* pending event */
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 */
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 */
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 */

154
enum {
Matthias Clasen's avatar
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);
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

266
static void     set_icon_helper (GdkDragContext    *context,
267
                                 GtkImageDefinition*def,
268
                                 gint               hot_x,
269
                                 gint               hot_y);
270

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

275
static struct {
276
  GdkDragAction action;
277 278
  const gchar  *name;
  GdkPixbuf    *pixbuf;
279 280
  GdkCursor    *cursor;
} drag_cursors[] = {
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 *
 *********************/

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

339 340 341
  return result;
}

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;
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);
429 430
  if (!GDK_IS_X11_WINDOW (window))
    {
431
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
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;
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
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;
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;

471 472
          num_mods = 1;
          mods.modifiers = grab_keys[i].modifiers;
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;
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);
517 518
  if (!GDK_IS_X11_WINDOW (window))
    {
519
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
520
      gdk_device_ungrab (device, time);
521
      G_GNUC_END_IGNORE_DEPRECATIONS;
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;
545 546 547 548

#ifdef XINPUT_2
      if (using_xi2)
        {
549 550
          num_mods = 1;
          mods.modifiers = grab_keys[i].modifiers;
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
}

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

781
static GdkCursor *
782 783
gtk_drag_get_cursor (GtkWidget         *widget,
                     GdkDisplay        *display,
784 785
                     GdkDragAction      action,
                     GtkDragSourceInfo *info)
786
{
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;

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

887 888
  g_return_if_fail (GTK_IS_WIDGET (widget));
  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
889

Matthias Clasen's avatar
Matthias Clasen committed
890
  selection_widget = gtk_drag_get_ipc_widget (widget);
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);
897

898
  g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
899

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

906
/**
907
 * gtk_drag_get_source_widget:
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
913 914 915
 *     within a single application, a pointer to the source widget.
 *     Otherwise, %NULL.
 */
916
GtkWidget *
917 918 919
gtk_drag_get_source_widget (GdkDragContext *context)
{
  GSList *tmp_list;
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");
932

933 934
          return info ? info->widget : NULL;
        }
935

936 937
      tmp_list = tmp_list->next;
    }
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;
960

961
  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
962

963
  if (success && del)
964
    {
965
      target = gdk_atom_intern_static_string ("DELETE");
966
    }
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)));
971

Manish Singh's avatar
Manish Singh committed
972
      g_object_ref (context);
973
      
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)
1031 1032 1033
{
  GtkDragDestInfo *info;
  GdkDragContext *context;
1034

1035 1036
  g_return_if_fail (toplevel != NULL);
  g_return_if_fail (event != NULL);
1037

1038
  context = event->dnd.context;
1039

Owen Taylor's avatar
Owen Taylor committed
1040
  info = gtk_drag_get_dest_info (context, TRUE);
1041

1042 1043 1044 1045 1046 1047 1048 1049
  /* Find the widget for the event */
  switch (event->type)
    {
    case GDK_DRAG_ENTER:
      break;
      
    case GDK_DRAG_LEAVE:
      if (info->widget)
1050 1051
        {
          gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1052
          gtk_drag_dest_set_widget (info, NULL);
1053
        }
1054 1055 1056 1057
      break;
      
    case GDK_DRAG_MOTION:
    case GDK_DROP_START:
1058
      {
1059
        GdkWindow *window;
1060
        gint tx, ty;
1061
        gboolean found;
1062

1063 1064 1065 1066 1067 1068 1069 1070 1071
        if (event->type == GDK_DROP_START)
          {
            info->dropped = TRUE;
            /* We send a leave here so that the widget unhighlights
             * properly.
             */
            if (info->widget)
              {
                gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1072
                gtk_drag_dest_set_widget (info, NULL);
1073 1074
              }
          }
1075

1076 1077
        window = gtk_widget_get_window (toplevel);

1078
#ifdef GDK_WINDOWING_X11
1079 1080 1081 1082 1083 1084 1085 1086 1087
        /* Hackaround for: http://bugzilla.gnome.org/show_bug.cgi?id=136112
         *
         * Currently gdk_window_get_position doesn't provide reliable
         * information for embedded windows, so we call the much more
         * expensive gdk_window_get_origin().
         */
        if (GTK_IS_PLUG (toplevel))
          gdk_window_get_origin (window, &tx, &ty);
        else
1088
#endif /* GDK_WINDOWING_X11 */
1089
          gdk_window_get_position (window, &tx, &ty);
1090

1091
        found = gtk_drag_find_widget (toplevel,
1092 1093 1094 1095 1096 1097 1098 1099 1100
                                      context,
                                      info,
                                      event->dnd.x_root - tx,
                                      event->dnd.y_root - ty,
                                      event->dnd.time,
                                      (event->type == GDK_DRAG_MOTION) ?
                                      gtk_drag_dest_motion :
                                      gtk_drag_dest_drop);

1101 1102 1103
        if (info->widget && !found)
          {
            gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1104
            gtk_drag_dest_set_widget (info, NULL);
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
          }
        
        /* Send a reply.
         */
        if (event->type == GDK_DRAG_MOTION)
          {
            if (!found)
              gdk_drag_status (context, 0, event->dnd.time);
          }
        else if (event->type == GDK_DROP_START && !info->proxy_source)
          {
            gdk_drop_reply (context, found, event->dnd.time);
          }
1118 1119 1120
      }
      break;

1121
    default:
Owen Taylor's avatar
Owen Taylor committed
1122
      g_assert_not_reached ();
1123 1124 1125 1126
    }
}

static void
Owen Taylor's avatar
Owen Taylor committed
1127
gtk_drag_selection_received (GtkWidget        *widget,
1128 1129 1130
                             GtkSelectionData *selection_data,
                             guint             time,
                             gpointer          data)
1131 1132 1133 1134
{
  GdkDragContext *context;
  GtkDragDestInfo *info;
  GtkWidget *drop_widget;
1135
  GdkAtom target;
1136

1137
  drop_widget = data;
1138

Manish Singh's avatar
Manish Singh committed
1139
  context = g_object_get_data (G_OBJECT (widget), "drag-context");
Owen Taylor's avatar
Owen Taylor committed
1140
  info = gtk_drag_get_dest_info (context, FALSE);
1141

1142
  if (info->proxy_data && 
1143
      gtk_selection_data_get_target (info->proxy_data) == gtk_selection_data_get_target (selection_data))
1144 1145
    {
      gtk_selection_data_set (info->proxy_data,
1146 1147 1148 1149
                              gtk_selection_data_get_data_type (selection_data),
                              gtk_selection_data_get_format (selection_data),
                              gtk_selection_data_get_data (selection_data),
                              gtk_selection_data_get_length (selection_data));
Owen Taylor's avatar
Owen Taylor committed
1150
      gtk_main_quit ();
1151 1152
      return;
    }
1153

1154 1155
  target = gtk_selection_data_get_target (selection_data);
  if (target == gdk_atom_intern_static_string ("DELETE"))
1156 1157 1158
    {
      gtk_drag_finish (context, TRUE, FALSE, time);
    }
1159
  else if ((target == gdk_atom_intern_static_string ("XmTRANSFER_SUCCESS")) ||
1160
           (target == gdk_atom_intern_static_string ("XmTRANSFER_FAILURE")))
1161 1162 1163 1164 1165 1166
    {
      /* Do nothing */
    }
  else
    {
      GtkDragDestSite *site;
1167

Manish Singh's avatar
Manish Singh committed
1168
      site = g_object_get_data (G_OBJECT (drop_widget), "gtk-drag-dest");
1169

1170
      if (site && site->target_list)
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
        {
          guint target_info;

          if (gtk_target_list_find (site->target_list, 
                                    target,
                                    &target_info))
            {
              if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
                  gtk_selection_data_get_length (selection_data) >= 0)
                g_signal_emit_by_name (drop_widget,
                                       "drag-data-received",
                                       context, info->drop_x, info->drop_y,
                                       selection_data,
                                       target_info, time);
            }
        }
1187
      else
1188 1189 1190 1191 1192 1193 1194
        {
          g_signal_emit_by_name (drop_widget,
                                 "drag-data-received",
                                 context, info->drop_x, info->drop_y,
                                 selection_data,
                                 0, time);
        }
1195
      
1196
      if (site && site->flags & GTK_DEST_DEFAULT_DROP)
1197
        {
1198

1199 1200 1201 1202 1203
          gtk_drag_finish (context, 
                           (gtk_selection_data_get_length (selection_data) >= 0),
                           (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
                           time);
        }
1204
      
Manish Singh's avatar
Manish Singh committed
1205
      g_object_unref (drop_widget);
1206
    }
1207

Manish Singh's avatar
Manish Singh committed
1208
  g_signal_handlers_disconnect_by_func (widget,
1209 1210
                                        gtk_drag_selection_received,
                                        data);
1211
  
1212
  g_object_set_data (G_OBJECT (widget), I_("drag-context"), NULL);
Manish Singh's avatar
Manish Singh committed
1213
  g_object_unref (context);
1214

1215 1216 1217
  gtk_drag_release_ipc_widget (widget);
}

1218 1219 1220 1221 1222 1223 1224 1225
static gboolean
gtk_drag_find_widget (GtkWidget           *widget,
                      GdkDragContext      *context,
                      GtkDragDestInfo     *info,
                      gint                 x,
                      gint                 y,
                      guint32              time,
                      GtkDragDestCallback  callback)
1226
{
1227 1228 1229
  if (!gtk_widget_get_mapped (widget) ||
      !gtk_widget_get_sensitive (widget))
    return FALSE;
1230

1231 1232 1233 1234 1235 1236 1237
  /* Get the widget at the pointer coordinates and travel up
   * the widget hierarchy from there.
   */
  widget = _gtk_widget_find_at_coords (gtk_widget_get_window (widget),
                                       x, y, &x, &y);
  if (!widget)
    return FALSE;
1238

1239
  while (widget)
1240
    {
1241 1242 1243
      GtkWidget *parent;
      GList *hierarchy = NULL;
      gboolean found = FALSE;
1244

1245
      if (!gtk_widget_get_mapped (widget))
1246
        return FALSE;
1247

1248 1249 1250 1251 1252 1253
      if (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_INSENSITIVE)
        {
          widget = gtk_widget_get_parent (widget);
          continue;
        }

1254 1255
      /* need to reference the entire hierarchy temporarily in case the
       * ::drag-motion/::drag-drop callbacks change the widget hierarchy.