gtkdnd.c 123 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 11
 * 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
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17 18 19
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

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

27
#include "config.h"
28

29
#include <math.h>
30 31 32
#include <stdlib.h>
#include <string.h>

Matthias Clasen's avatar
Matthias Clasen committed
33
#include "gdk/gdk.h"
34

35 36 37 38 39 40
#ifdef GDK_WINDOWING_X11
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include "gdk/x11/gdkx.h"
#endif

41
#include "gtkdnd.h"
42 43
#include "gtkiconfactory.h"
#include "gtkicontheme.h"
44
#include "gtkimage.h"
45 46
#include "gtkinvisible.h"
#include "gtkmain.h"
47
#include "gtkplug.h"
48
#include "gtkstock.h"
49
#include "gtktooltip.h"
Owen Taylor's avatar
Owen Taylor committed
50
#include "gtkwindow.h"
Matthias Clasen's avatar
Matthias Clasen committed
51
#include "gtkintl.h"
52
#include "gtkdndcursors.h"
53
#include "gtkselectionprivate.h"
54

55
static GSList *source_widgets = NULL;
56 57 58 59 60 61 62 63

typedef struct _GtkDragSourceSite GtkDragSourceSite;
typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
typedef struct _GtkDragDestSite GtkDragDestSite;
typedef struct _GtkDragDestInfo GtkDragDestInfo;
typedef struct _GtkDragAnim GtkDragAnim;


Owen Taylor's avatar
Owen Taylor committed
64 65
typedef enum 
{
66 67 68 69 70
  GTK_DRAG_STATUS_DRAG,
  GTK_DRAG_STATUS_WAIT,
  GTK_DRAG_STATUS_DROP
} GtkDragStatus;

Owen Taylor's avatar
Owen Taylor committed
71 72
struct _GtkDragSourceSite 
{
73 74 75
  GdkModifierType    start_button_mask;
  GtkTargetList     *target_list;        /* Targets for drag data */
  GdkDragAction      actions;            /* Possible actions */
76 77 78 79 80 81 82

  /* Drag icon */
  GtkImageType icon_type;
  union
  {
    GtkImagePixbufData pixbuf;
    GtkImageStockData stock;
83
    GtkImageIconNameData name;
84 85
  } icon_data;

86 87 88 89
  /* Stored button press information to detect drag beginning */
  gint               state;
  gint               x, y;
};
Owen Taylor's avatar
Owen Taylor committed
90
  
Owen Taylor's avatar
Owen Taylor committed
91 92
struct _GtkDragSourceInfo 
{
93 94
  GtkWidget         *widget;
  GtkTargetList     *target_list; /* Targets for drag data */
95
  GdkDragAction      possible_actions; /* Actions allowed by source */
96 97
  GdkDragContext    *context;	  /* drag context */
  GtkWidget         *icon_window; /* Window for drag */
98
  GtkWidget         *fallback_icon; /* Window for drag used on other screens */
99 100 101 102
  GtkWidget         *ipc_widget;  /* GtkInvisible for grab, message passing */
  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
103

104
  GtkDragStatus      status;	  /* drag status */
105
  GdkEvent          *last_event;  /* pending event */
Owen Taylor's avatar
Owen Taylor committed
106

107 108
  gint               start_x, start_y; /* Initial position */
  gint               cur_x, cur_y;     /* Current Position */
109
  GdkScreen         *cur_screen;       /* Current screen for pointer */
Owen Taylor's avatar
Owen Taylor committed
110

111
  guint32            grab_time;   /* timestamp for initial grab */
112 113 114
  GList             *selections;  /* selections we've claimed */
  
  GtkDragDestInfo   *proxy_dest;  /* Set if this is a proxy drag */
Owen Taylor's avatar
Owen Taylor committed
115

116
  guint              update_idle;      /* Idle function to update the drag */
Owen Taylor's avatar
Owen Taylor committed
117
  guint              drop_timeout;     /* Timeout for aborting drop */
118 119
  guint              destroy_icon : 1; /* If true, destroy icon_window */
  guint              have_grab : 1;    /* Do we still have the pointer grab */
120 121
  GdkPixbuf         *icon_pixbuf;
  GdkCursor         *drag_cursors[6];
122 123
};

124
struct _GtkDragDestSite
Owen Taylor's avatar
Owen Taylor committed
125
{
126 127 128 129 130
  GtkDestDefaults    flags;
  GtkTargetList     *target_list;
  GdkDragAction      actions;
  GdkWindow         *proxy_window;
  GdkDragProtocol    proxy_protocol;
131
  guint              do_proxy     : 1;
132
  guint              proxy_coords : 1;
133
  guint              have_drag    : 1;
134
  guint              track_motion : 1;
135
};
136 137 138 139 140 141 142 143

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 */
144
  guint              proxy_drop_wait : 1; /* Set if we are waiting for a
145 146 147 148 149
                                           * 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 */
150 151
};

152
#define DROP_ABORT_TIME 300000
153 154 155 156 157 158

#define ANIM_STEP_TIME 50
#define ANIM_STEP_LENGTH 50
#define ANIM_MIN_STEPS 5
#define ANIM_MAX_STEPS 10

Owen Taylor's avatar
Owen Taylor committed
159 160
struct _GtkDragAnim 
{
161 162 163 164 165
  GtkDragSourceInfo *info;
  gint step;
  gint n_steps;
};

166 167 168 169 170
typedef gboolean (* GtkDragDestCallback) (GtkWidget      *widget,
                                          GdkDragContext *context,
                                          gint            x,
                                          gint            y,
                                          guint32         time);
171 172 173

/* Enumeration for some targets we handle internally */

Owen Taylor's avatar
Owen Taylor committed
174
enum {
175
  TARGET_MOTIF_SUCCESS = 0x40000000,
176 177 178 179 180
  TARGET_MOTIF_FAILURE,
  TARGET_DELETE
};

/* Forward declarations */
Owen Taylor's avatar
Owen Taylor committed
181 182 183 184 185
static void          gtk_drag_get_event_actions (GdkEvent        *event, 
					         gint             button,
					         GdkDragAction    actions,
					         GdkDragAction   *suggested_action,
					         GdkDragAction   *possible_actions);
186
static GdkCursor *   gtk_drag_get_cursor         (GdkDisplay     *display,
187 188
						  GdkDragAction   action,
						  GtkDragSourceInfo *info);
189
static void          gtk_drag_update_cursor      (GtkDragSourceInfo *info);
Matthias Clasen's avatar
Matthias Clasen committed
190 191
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
192
static void          gtk_drag_release_ipc_widget (GtkWidget      *widget);
193

194 195
static void     gtk_drag_selection_received     (GtkWidget        *widget,
						 GtkSelectionData *selection_data,
196
						 guint             time,
197
						 gpointer          data);
198 199 200 201 202 203 204
static gboolean gtk_drag_find_widget            (GtkWidget        *widget,
                                                 GdkDragContext   *context,
                                                 GtkDragDestInfo  *info,
                                                 gint              x,
                                                 gint              y,
                                                 guint32           time,
                                                 GtkDragDestCallback callback);
205 206 207 208 209
static void     gtk_drag_proxy_begin            (GtkWidget        *widget,
						 GtkDragDestInfo  *dest_info,
						 guint32           time);
static void     gtk_drag_dest_realized          (GtkWidget        *widget);
static void     gtk_drag_dest_hierarchy_changed (GtkWidget        *widget,
Owen Taylor's avatar
Owen Taylor committed
210
						 GtkWidget        *previous_toplevel);
211 212 213 214 215 216 217 218 219 220 221 222 223 224
static void     gtk_drag_dest_site_destroy      (gpointer          data);
static void     gtk_drag_dest_leave             (GtkWidget        *widget,
						 GdkDragContext   *context,
						 guint             time);
static gboolean gtk_drag_dest_motion            (GtkWidget        *widget,
						 GdkDragContext   *context,
						 gint              x,
						 gint              y,
						 guint             time);
static gboolean gtk_drag_dest_drop              (GtkWidget        *widget,
						 GdkDragContext   *context,
						 gint              x,
						 gint              y,
						 guint             time);
Owen Taylor's avatar
Owen Taylor committed
225 226 227 228 229 230

static GtkDragDestInfo *  gtk_drag_get_dest_info     (GdkDragContext *context,
						      gboolean        create);
static GtkDragSourceInfo *gtk_drag_get_source_info   (GdkDragContext *context,
						      gboolean        create);
static void               gtk_drag_clear_source_info (GdkDragContext *context);
231 232 233 234 235 236 237 238 239

static void gtk_drag_source_check_selection    (GtkDragSourceInfo *info, 
					        GdkAtom            selection,
					        guint32            time);
static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
						guint32            time);
static void gtk_drag_drop                      (GtkDragSourceInfo *info,
						guint32            time);
static void gtk_drag_drop_finished             (GtkDragSourceInfo *info,
240
						GtkDragResult      result,
241
						guint              time);
242
static void gtk_drag_cancel                    (GtkDragSourceInfo *info,
243
						GtkDragResult      result,
244
						guint32            time);
245

246
static gboolean gtk_drag_source_event_cb       (GtkWidget         *widget,
247 248 249 250 251 252 253 254
						GdkEvent          *event,
						gpointer           data);
static void gtk_drag_source_site_destroy       (gpointer           data);
static void gtk_drag_selection_get             (GtkWidget         *widget, 
						GtkSelectionData  *selection_data,
						guint              sel_info,
						guint32            time,
						gpointer           data);
255
static gboolean gtk_drag_anim_timeout          (gpointer           data);
256
static void gtk_drag_remove_icon               (GtkDragSourceInfo *info);
Owen Taylor's avatar
Owen Taylor committed
257
static void gtk_drag_source_info_destroy       (GtkDragSourceInfo *info);
258 259
static void gtk_drag_add_update_idle           (GtkDragSourceInfo *info);

260
static void gtk_drag_update                    (GtkDragSourceInfo *info,
261
						GdkScreen         *screen,
262 263 264
						gint               x_root,
						gint               y_root,
						GdkEvent          *event);
265
static gboolean gtk_drag_motion_cb             (GtkWidget         *widget, 
266 267
					        GdkEventMotion    *event, 
					        gpointer           data);
268
static gboolean gtk_drag_key_cb                (GtkWidget         *widget, 
269 270
					        GdkEventKey       *event, 
					        gpointer           data);
271 272 273
static gboolean gtk_drag_grab_broken_event_cb  (GtkWidget          *widget,
						GdkEventGrabBroken *event,
						gpointer            data);
274 275 276
static void     gtk_drag_grab_notify_cb        (GtkWidget         *widget,
						gboolean           was_grabbed,
						gpointer           data);
277
static gboolean gtk_drag_button_release_cb     (GtkWidget         *widget, 
278 279
					        GdkEventButton    *event, 
					        gpointer           data);
280
static gboolean gtk_drag_abort_timeout         (gpointer           data);
281

282 283 284 285 286 287 288
static void     set_icon_stock_pixbuf          (GdkDragContext    *context,
						const gchar       *stock_id,
						GdkPixbuf         *pixbuf,
						gint               hot_x,
						gint               hot_y,
						gboolean           force_window);

289 290 291 292
/************************
 * Cursor and Icon data *
 ************************/

Owen Taylor's avatar
Owen Taylor committed
293
static struct {
294
  GdkDragAction action;
295 296 297
  const gchar  *name;
  const guint8 *data;
  GdkPixbuf    *pixbuf;
298 299
  GdkCursor    *cursor;
} drag_cursors[] = {
Matthias Clasen's avatar
Matthias Clasen committed
300
  { GDK_ACTION_DEFAULT, NULL },
301 302 303 304 305
  { GDK_ACTION_ASK,   "dnd-ask",  dnd_cursor_ask,  NULL, NULL },
  { GDK_ACTION_COPY,  "dnd-copy", dnd_cursor_copy, NULL, NULL },
  { GDK_ACTION_MOVE,  "dnd-move", dnd_cursor_move, NULL, NULL },
  { GDK_ACTION_LINK,  "dnd-link", dnd_cursor_link, NULL, NULL },
  { 0              ,  "dnd-none", dnd_cursor_none, NULL, NULL },
306 307
};

Owen Taylor's avatar
Owen Taylor committed
308
static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
309 310 311 312 313

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

Manish Singh's avatar
Manish Singh committed
314
static void
315 316 317 318 319
set_can_change_screen (GtkWidget *widget,
		       gboolean   can_change_screen)
{
  can_change_screen = can_change_screen != FALSE;
  
Matthias Clasen's avatar
Matthias Clasen committed
320
  g_object_set_data (G_OBJECT (widget), I_("gtk-dnd-can-change-screen"),
321 322 323 324 325 326 327 328 329 330
		     GUINT_TO_POINTER (can_change_screen));
}

static gboolean
get_can_change_screen (GtkWidget *widget)
{
  return g_object_get_data (G_OBJECT (widget), "gtk-dnd-can-change-screen") != NULL;

}

Owen Taylor's avatar
Owen Taylor committed
331
static GtkWidget *
Matthias Clasen's avatar
Matthias Clasen committed
332
gtk_drag_get_ipc_widget_for_screen (GdkScreen *screen)
333 334
{
  GtkWidget *result;
335 336
  GSList *drag_widgets = g_object_get_data (G_OBJECT (screen), 
					    "gtk-dnd-ipc-widgets");
337 338 339 340 341 342
  
  if (drag_widgets)
    {
      GSList *tmp = drag_widgets;
      result = drag_widgets->data;
      drag_widgets = drag_widgets->next;
343
      g_object_set_data (G_OBJECT (screen),
Matthias Clasen's avatar
Matthias Clasen committed
344
			 I_("gtk-dnd-ipc-widgets"),
345
			 drag_widgets);
346 347 348 349
      g_slist_free_1 (tmp);
    }
  else
    {
Matthias Clasen's avatar
Matthias Clasen committed
350 351 352 353
      result = gtk_window_new (GTK_WINDOW_POPUP);
      gtk_window_set_screen (GTK_WINDOW (result), screen);
      gtk_window_resize (GTK_WINDOW (result), 1, 1);
      gtk_window_move (GTK_WINDOW (result), -100, -100);
354
      gtk_widget_show (result);
Matthias Clasen's avatar
Matthias Clasen committed
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
    }  

  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))
    {
372 373
      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
374
                                     GTK_WINDOW (result));
375
    }
Owen Taylor's avatar
Owen Taylor committed
376

377 378 379
  return result;
}

380 381 382 383 384 385
/* FIXME: modifying the XEvent window as in root_key_filter() isn't
 * going to work with XGE/XI2, since the actual event to handle would
 * be allocated/freed before GDK gets to translate the event.
 * Active grabs on the keyboard are used instead at the moment...
 */
#if defined (GDK_WINDOWING_X11) && !defined (XINPUT_2)
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

/*
 * 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)
{
404
  XEvent *ev = (XEvent *) xevent;
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443

  if ((ev->type == KeyPress || ev->type == KeyRelease) &&
      ev->xkey.root == ev->xkey.window)
    ev->xkey.window = (Window)data;

  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,
444
               GdkDevice *device,
445 446 447 448 449 450
               guint32    time)
{
  guint i;
  GdkWindow *window, *root;
  gint keycode;

451
  window = gtk_widget_get_window (widget);
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
  root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));

  gdk_error_trap_push ();

  for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
    {
      keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
      XGrabKey (GDK_WINDOW_XDISPLAY (window),
   	        keycode, grab_keys[i].modifiers,
	        GDK_WINDOW_XID (root),
	        FALSE,
	        GrabModeAsync,
	        GrabModeAsync);
    }

  gdk_flush ();
  gdk_error_trap_pop ();

Benjamin Otte's avatar
Benjamin Otte committed
470
  gdk_window_add_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
471 472
}

473
static void
474
ungrab_dnd_keys (GtkWidget *widget,
475
                 GdkDevice *device,
476 477 478 479 480 481
                 guint32    time)
{
  guint i;
  GdkWindow *window, *root;
  gint keycode;

482
  window = gtk_widget_get_window (widget);
483 484
  root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));

Benjamin Otte's avatar
Benjamin Otte committed
485
  gdk_window_remove_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500

  gdk_error_trap_push ();

  for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
    {
      keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
      XUngrabKey (GDK_WINDOW_XDISPLAY (window),
      	          keycode, grab_keys[i].modifiers,
                  GDK_WINDOW_XID (root));
    }

  gdk_flush ();
  gdk_error_trap_pop ();
}

501
#else /* GDK_WINDOWING_X11 && !XINPUT_2 */
502 503 504

static void
grab_dnd_keys (GtkWidget *widget,
505
               GdkDevice *device,
506 507
               guint32    time)
{
508 509
  gdk_device_grab (device,
                   gtk_widget_get_window (widget),
510 511 512
                   GDK_OWNERSHIP_APPLICATION, FALSE,
                   GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
                   NULL, time);
513 514 515 516
}

static void
ungrab_dnd_keys (GtkWidget *widget,
517
                 GdkDevice *device,
518 519
                 guint32    time)
{
520
  gdk_device_ungrab (device, time);
521 522
}

523
#endif /* GDK_WINDOWING_X11 */
524 525


Owen Taylor's avatar
Owen Taylor committed
526
/***************************************************************
527
 * gtk_drag_release_ipc_widget:
Owen Taylor's avatar
Owen Taylor committed
528
 *     Releases widget retrieved with gtk_drag_get_ipc_widget ()
529 530 531
 *   arguments:
 *     widget: the widget to release.
 *   results:
Owen Taylor's avatar
Owen Taylor committed
532
 ***************************************************************/
533 534 535 536

static void
gtk_drag_release_ipc_widget (GtkWidget *widget)
{
Matthias Clasen's avatar
Matthias Clasen committed
537
  GtkWindow *window = GTK_WINDOW (widget);
538
  GdkScreen *screen = gtk_widget_get_screen (widget);
539
  GdkDragContext *context = g_object_get_data (G_OBJECT (widget), "drag-context");
540 541
  GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
					    "gtk-dnd-ipc-widgets");
542 543 544 545 546 547 548 549 550 551 552
  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);
    }

553 554 555
  if (gtk_window_has_group (window))
    gtk_window_group_remove_window (gtk_window_get_group (window),
                                    window);
556
  drag_widgets = g_slist_prepend (drag_widgets, widget);
557
  g_object_set_data (G_OBJECT (screen),
Matthias Clasen's avatar
Matthias Clasen committed
558
		     I_("gtk-dnd-ipc-widgets"),
559
		     drag_widgets);
560 561
}

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
static guint32
gtk_drag_get_event_time (GdkEvent *event)
{
  guint32 tm = GDK_CURRENT_TIME;
  
  if (event)
    switch (event->type)
      {
      case GDK_MOTION_NOTIFY:
	tm = event->motion.time; break;
      case GDK_BUTTON_PRESS:
      case GDK_2BUTTON_PRESS:
      case GDK_3BUTTON_PRESS:
      case GDK_BUTTON_RELEASE:
	tm = event->button.time; break;
      case GDK_KEY_PRESS:
      case GDK_KEY_RELEASE:
	tm = event->key.time; break;
      case GDK_ENTER_NOTIFY:
      case GDK_LEAVE_NOTIFY:
	tm = event->crossing.time; break;
      case GDK_PROPERTY_NOTIFY:
	tm = event->property.time; break;
      case GDK_SELECTION_CLEAR:
      case GDK_SELECTION_REQUEST:
      case GDK_SELECTION_NOTIFY:
	tm = event->selection.time; break;
      case GDK_PROXIMITY_IN:
      case GDK_PROXIMITY_OUT:
	tm = event->proximity.time; break;
      default:			/* use current time */
	break;
      }
  
  return tm;
}

599 600 601 602 603 604
static void
gtk_drag_get_event_actions (GdkEvent *event, 
			    gint button, 
			    GdkDragAction  actions,
			    GdkDragAction *suggested_action,
			    GdkDragAction *possible_actions)
605
{
606 607
  *suggested_action = 0;
  *possible_actions = 0;
Owen Taylor's avatar
Owen Taylor committed
608

609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
  if (event)
    {
      GdkModifierType state = 0;
      
      switch (event->type)
	{
	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;
Owen Taylor's avatar
Owen Taylor committed
628 629
	case GDK_ENTER_NOTIFY:
	case GDK_LEAVE_NOTIFY:
630 631
	  state = event->crossing.state;
	  break;
Owen Taylor's avatar
Owen Taylor committed
632 633 634
	default:
	  break;
	}
635

Owen Taylor's avatar
Owen Taylor committed
636
      if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
637 638 639 640 641
	{
	  *suggested_action = GDK_ACTION_ASK;
	  *possible_actions = actions;
	}
      else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
642 643
	{
	  if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
644 645 646 647 648 649 650
	    {
	      if (actions & GDK_ACTION_LINK)
		{
		  *suggested_action = GDK_ACTION_LINK;
		  *possible_actions = GDK_ACTION_LINK;
		}
	    }
651
	  else if (state & GDK_CONTROL_MASK)
652 653 654 655 656 657 658 659
	    {
	      if (actions & GDK_ACTION_COPY)
		{
		  *suggested_action = GDK_ACTION_COPY;
		  *possible_actions = GDK_ACTION_COPY;
		}
	      return;
	    }
660
	  else
661 662 663 664 665 666 667 668
	    {
	      if (actions & GDK_ACTION_MOVE)
		{
		  *suggested_action = GDK_ACTION_MOVE;
		  *possible_actions = GDK_ACTION_MOVE;
		}
	      return;
	    }
669 670 671
	}
      else
	{
672
	  *possible_actions = actions;
Owen Taylor's avatar
Owen Taylor committed
673

674 675 676 677
	  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;
678
	  else if (actions & GDK_ACTION_MOVE)
679
	    *suggested_action = GDK_ACTION_MOVE;
680
	  else if (actions & GDK_ACTION_LINK)
681
	    *suggested_action = GDK_ACTION_LINK;
682 683
	}
    }
Owen Taylor's avatar
Owen Taylor committed
684 685 686 687 688 689 690 691 692 693 694
  else
    {
      *possible_actions = actions;
      
      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;
    }
695 696 697 698 699 700 701 702
}

static gboolean
gtk_drag_can_use_rgba_cursor (GdkDisplay *display, 
			      gint        width,
			      gint        height)
{
  guint max_width, max_height;
703
  
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
  if (!gdk_display_supports_cursor_color (display))
    return FALSE;

  if (!gdk_display_supports_cursor_alpha (display))
    return FALSE;

  gdk_display_get_maximal_cursor_size (display, 
                                       &max_width,
                                       &max_height);
  if (width > max_width || height > max_height)
    {
       /* can't use rgba cursor (too large) */
      return FALSE;
    }

  return TRUE;
720 721
}

Owen Taylor's avatar
Owen Taylor committed
722
static GdkCursor *
723 724 725
gtk_drag_get_cursor (GdkDisplay        *display,
		     GdkDragAction      action,
		     GtkDragSourceInfo *info)
726
{
Matthias Clasen's avatar
Matthias Clasen committed
727
  gint i;
728 729 730 731 732 733 734 735 736

  /* reconstruct the cursors for each new drag (thus !info),
   * to catch cursor theme changes 
   */ 
  if (!info)
    {
      for (i = 0 ; i < n_drag_cursors - 1; i++)
	if (drag_cursors[i].cursor != NULL)
	  {
737
	    g_object_unref (drag_cursors[i].cursor);
738 739 740 741
	    drag_cursors[i].cursor = NULL;
	  }
    }
 
742 743 744
  for (i = 0 ; i < n_drag_cursors - 1; i++)
    if (drag_cursors[i].action == action)
      break;
745 746 747 748 749

  if (drag_cursors[i].pixbuf == NULL)
    drag_cursors[i].pixbuf = 
      gdk_pixbuf_new_from_inline (-1, drag_cursors[i].data, FALSE, NULL);

750 751
  if (drag_cursors[i].cursor != NULL)
    {
752
      if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
753
	{
754
	  g_object_unref (drag_cursors[i].cursor);
755 756 757
	  drag_cursors[i].cursor = NULL;
	}
    }
758 759 760 761
  
  if (drag_cursors[i].cursor == NULL)
    drag_cursors[i].cursor = gdk_cursor_new_from_name (display, drag_cursors[i].name);
  
762
  if (drag_cursors[i].cursor == NULL)
763 764 765
    drag_cursors[i].cursor = gdk_cursor_new_from_pixbuf (display, drag_cursors[i].pixbuf, 0, 0);

  if (info && info->icon_pixbuf) 
766
    {
767 768 769 770
      gint cursor_width, cursor_height;
      gint icon_width, icon_height;
      gint width, height;
      GdkPixbuf *cursor_pixbuf, *pixbuf;
771
      gint hot_x, hot_y;
Manish Singh's avatar
Manish Singh committed
772
      gint icon_x, icon_y, ref_x, ref_y;
773 774 775 776 777 778

      if (info->drag_cursors[i] != NULL)
        {
          if (display == gdk_cursor_get_display (info->drag_cursors[i]))
	    return info->drag_cursors[i];
	  
779
	  g_object_unref (info->drag_cursors[i]);
780 781
	  info->drag_cursors[i] = NULL;
        }
Owen Taylor's avatar
Owen Taylor committed
782

783 784
      icon_x = info->hot_x;
      icon_y = info->hot_y;
Matthias Clasen's avatar
Matthias Clasen committed
785 786 787 788
      icon_width = gdk_pixbuf_get_width (info->icon_pixbuf);
      icon_height = gdk_pixbuf_get_height (info->icon_pixbuf);

      hot_x = hot_y = 0;
789 790
      cursor_pixbuf = gdk_cursor_get_image (drag_cursors[i].cursor);
      if (!cursor_pixbuf)
791
	cursor_pixbuf = g_object_ref (drag_cursors[i].pixbuf);
792 793 794 795 796 797 798
      else
	{
	  if (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"))
	    hot_x = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"));
	  
	  if (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"))
	    hot_y = atoi (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"));
799

800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
#if 0	  
	  /* The code below is an attempt to let cursor themes
	   * determine the attachment of the icon to enable things
	   * like the following:
	   *
	   *    +-----+
           *    |     |
           *    |     ||
           *    +-----+|
           *        ---+
           * 
           * It does not work since Xcursor doesn't allow to attach
           * any additional information to cursors in a retrievable
           * way  (there are comments, but no way to get at them
           * short of searching for the actual cursor file).
           * If this code ever gets used, the icon_window placement
           * must be changed to recognize these placement options
           * as well. Note that this code ignores info->hot_x/y.
           */ 
819 820 821
	  for (j = 0; j < 10; j++)
	    {
	      const gchar *opt;
822
	      gchar key[32];
823
	      gchar **toks;
824
	      GtkAnchorType icon_anchor;
Owen Taylor's avatar
Owen Taylor committed
825

826 827 828 829 830 831 832 833 834 835 836 837 838
	      g_snprintf (key, 32, "comment%d", j);
	      opt = gdk_pixbuf_get_option (cursor_pixbuf, key);
	      if (opt && g_str_has_prefix ("icon-attach:", opt))
		{
		  toks = g_strsplit (opt + strlen ("icon-attach:"), "'", -1);
		  if (g_strv_length (toks) != 3)
		    {
		      g_strfreev (toks);
		      break;
		    }
		  icon_anchor = atoi (toks[0]);
		  icon_x = atoi (toks[1]);
		  icon_y = atoi (toks[2]);
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
		  
		  switch (icon_anchor)
		    {
		    case GTK_ANCHOR_NORTH:
		    case GTK_ANCHOR_CENTER:
		    case GTK_ANCHOR_SOUTH:
		      icon_x += icon_width / 2;
		      break;
		    case GTK_ANCHOR_NORTH_EAST:
		    case GTK_ANCHOR_EAST:
		    case GTK_ANCHOR_SOUTH_EAST:
		      icon_x += icon_width;
		      break;
		    default: ;
		    }
		  
		  switch (icon_anchor)
		    {
		    case GTK_ANCHOR_WEST:
		    case GTK_ANCHOR_CENTER:
		    case GTK_ANCHOR_EAST:
		      icon_y += icon_height / 2;
		      break;
		    case GTK_ANCHOR_SOUTH_WEST:
		    case GTK_ANCHOR_SOUTH:
		    case GTK_ANCHOR_SOUTH_EAST:
		      icon_x += icon_height;
		      break;
		    default: ;
		    }
869 870 871 872 873

		  g_strfreev (toks);
		  break;
		}
	    }
874
#endif
875
	}
Matthias Clasen's avatar
Matthias Clasen committed
876

877 878
      cursor_width = gdk_pixbuf_get_width (cursor_pixbuf);
      cursor_height = gdk_pixbuf_get_height (cursor_pixbuf);
Matthias Clasen's avatar
Matthias Clasen committed
879
      
880 881 882 883 884 885 886
      ref_x = MAX (hot_x, icon_x);
      ref_y = MAX (hot_y, icon_y);
      width = ref_x + MAX (cursor_width - hot_x, icon_width - icon_x);
      height = ref_y + MAX (cursor_height - hot_y, icon_height - icon_y);
         
      if (gtk_drag_can_use_rgba_cursor (display, width, height))
	{
887 888 889
	  /* Composite cursor and icon so that both hotspots
	   * end up at (ref_x, ref_y)
	   */
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
	  pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
				   width, height); 
	  
	  gdk_pixbuf_fill (pixbuf, 0xff000000);
	  
	  gdk_pixbuf_composite (info->icon_pixbuf, pixbuf,
				ref_x - icon_x, ref_y - icon_y, 
				icon_width, icon_height,
				ref_x - icon_x, ref_y - icon_y, 
				1.0, 1.0, 
				GDK_INTERP_BILINEAR, 255);
	  
	  gdk_pixbuf_composite (cursor_pixbuf, pixbuf,
				ref_x - hot_x, ref_y - hot_y, 
				cursor_width, cursor_height,
				ref_x - hot_x, ref_y - hot_y,
				1.0, 1.0, 
				GDK_INTERP_BILINEAR, 255);
	  
	  info->drag_cursors[i] = 
910
	    gdk_cursor_new_from_pixbuf (display, pixbuf, ref_x, ref_y);
911 912 913 914 915 916 917 918 919 920
	  
	  g_object_unref (pixbuf);
	}
      
      g_object_unref (cursor_pixbuf);
      
      if (info->drag_cursors[i] != NULL)
	return info->drag_cursors[i];
    }
 
921 922 923
  return drag_cursors[i].cursor;
}

924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
static void
gtk_drag_update_cursor (GtkDragSourceInfo *info)
{
  GdkCursor *cursor;
  gint i;

  if (!info->have_grab)
    return;

  for (i = 0 ; i < n_drag_cursors - 1; i++)
    if (info->cursor == drag_cursors[i].cursor ||
	info->cursor == info->drag_cursors[i])
      break;
  
  if (i == n_drag_cursors)
    return;

  cursor = gtk_drag_get_cursor (gdk_cursor_get_display (info->cursor), 
942
				drag_cursors[i].action, info);
943 944 945
  
  if (cursor != info->cursor)
    {
946 947 948
      GdkDevice *pointer;

      pointer = gdk_drag_context_get_device (info->context);
949 950
      gdk_device_grab (pointer,
                       gtk_widget_get_window (info->ipc_widget),
951 952 953
                       GDK_OWNERSHIP_APPLICATION, FALSE,
                       GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
                       cursor, info->grab_time);
954 955 956 957
      info->cursor = cursor;
    }
}

958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979
/********************
 * Destination side *
 ********************/

/*************************************************************
 * gtk_drag_get_data:
 *     Get the data for a drag or drop
 *   arguments:
 *     context - drag context
 *     target  - format to retrieve the data in.
 *     time    - timestamp of triggering event.
 *     
 *   results:
 *************************************************************/

void 
gtk_drag_get_data (GtkWidget      *widget,
		   GdkDragContext *context,
		   GdkAtom         target,
		   guint32         time)
{
  GtkWidget *selection_widget;
Owen Taylor's avatar
Owen Taylor committed
980

981 982
  g_return_if_fail (GTK_IS_WIDGET (widget));
  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
Owen Taylor's avatar
Owen Taylor committed
983

Matthias Clasen's avatar
Matthias Clasen committed
984
  selection_widget = gtk_drag_get_ipc_widget (widget);
Owen Taylor's avatar
Owen Taylor committed
985

Manish Singh's avatar
Manish Singh committed
986 987
  g_object_ref (context);
  g_object_ref (widget);
988
  
989
  g_signal_connect (selection_widget, "selection-received",
Manish Singh's avatar
Manish Singh committed
990
		    G_CALLBACK (gtk_drag_selection_received), widget);
Owen Taylor's avatar
Owen Taylor committed
991

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

994
  gtk_selection_convert (selection_widget,
Owen Taylor's avatar
Owen Taylor committed
995
			 gdk_drag_get_selection (context),
996 997 998 999
			 target,
			 time);
}

1000

Matthias Clasen's avatar
Matthias Clasen committed
1001
/**
1002
 * gtk_drag_get_source_widget:
Matthias Clasen's avatar
Matthias Clasen committed
1003 1004 1005 1006 1007 1008 1009 1010
 * @context: a (destination side) drag context
 *
 * Determines the source widget for a drag.
 *
 * Return value: (transfer none): if the drag is occurring
 *     within a single application, a pointer to the source widget.
 *     Otherwise, %NULL.
 */
Owen Taylor's avatar
Owen Taylor committed
1011
GtkWidget *
1012 1013 1014
gtk_drag_get_source_widget (GdkDragContext *context)
{
  GSList *tmp_list;
Owen Taylor's avatar
Owen Taylor committed
1015

1016 1017
  g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
  
1018 1019 1020 1021
  tmp_list = source_widgets;
  while (tmp_list)
    {
      GtkWidget *ipc_widget = tmp_list->data;
1022

1023
      if (gtk_widget_get_window (ipc_widget) == gdk_drag_context_get_source_window (context))
1024 1025
	{
	  GtkDragSourceInfo *info;
Manish Singh's avatar
Manish Singh committed
1026
	  info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
Owen Taylor's avatar
Owen Taylor committed
1027

1028 1029
	  return info ? info->widget : NULL;
	}
Owen Taylor's avatar
Owen Taylor committed
1030

1031 1032
      tmp_list = tmp_list->next;
    }
Owen Taylor's avatar
Owen Taylor committed
1033

1034 1035 1036
  return NULL;
}

1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
/*************************************************************
 * gtk_drag_finish:
 *     Notify the drag source that the transfer of data
 *     is complete.
 *   arguments:
 *     context: The drag context for this drag
 *     success: Was the data successfully transferred?
 *     time:    The timestamp to use when notifying the destination.
 *   results:
 *************************************************************/

void 
gtk_drag_finish (GdkDragContext *context,
		 gboolean        success,
1051
		 gboolean        del,
1052 1053 1054
		 guint32         time)
{
  GdkAtom target = GDK_NONE;
Owen Taylor's avatar
Owen Taylor committed
1055

1056
  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
Owen Taylor's avatar
Owen Taylor committed
1057

1058
  if (success && del)
1059
    {
1060
      target = gdk_atom_intern_static_string ("DELETE");
1061
    }
1062
  else if (gdk_drag_context_get_protocol (context) == GDK_DRAG_PROTO_MOTIF)
1063
    {
1064 1065 1066
      target = gdk_atom_intern_static_string (success ? 
					      "XmTRANSFER_SUCCESS" : 
					      "XmTRANSFER_FAILURE");
1067
    }
Owen Taylor's avatar
Owen Taylor committed
1068

1069 1070
  if (target != GDK_NONE)
    {
1071
      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
1072

Manish Singh's avatar
Manish Singh committed
1073
      g_object_ref (context);
1074
      
Matthias Clasen's avatar
Matthias Clasen committed
1075
      g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
1076
      g_signal_connect (selection_widget, "selection-received",
Manish Singh's avatar
Manish Singh committed
1077 1078
			G_CALLBACK (gtk_drag_selection_received),
			NULL);
1079 1080
      
      gtk_selection_convert (selection_widget,
Owen Taylor's avatar
Owen Taylor committed
1081
			     gdk_drag_get_selection (context),
1082 1083 1084 1085
			     target,
			     time);
    }
  
1086
  if (!(success && del))
1087 1088 1089 1090
    gdk_drop_finish (context, success, time);
}

/*************************************************************
1091
 * gtk_drag_highlight_draw:
Owen Taylor's avatar
Owen Taylor committed
1092
 *     Callback for expose_event for highlighted widgets.
1093 1094
 *   arguments:
 *     widget:
Owen Taylor's avatar
Owen Taylor committed
1095 1096
 *     event:
 *     data:
1097 1098 1099
 *   results:
 *************************************************************/

Owen Taylor's avatar
Owen Taylor committed
1100
static gboolean
1101 1102 1103
gtk_drag_highlight_draw (GtkWidget *widget,
			 cairo_t   *cr,
			 gpointer   data)
1104
{
1105 1106
  int width = gtk_widget_get_allocated_width (widget);
  int height = gtk_widget_get_allocated_height (widget);
1107

1108
  gtk_paint_shadow (gtk_widget_get_style (widget), cr,
1109 1110 1111
                    GTK_STATE_NORMAL, GTK_SHADOW_OUT,
                    widget, "dnd",
                    0, 0, width, height);
1112

1113 1114 1115 1116 1117 1118
  cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
  cairo_set_line_width (cr, 1.0);
  cairo_rectangle (cr,
                   0.5, 0.5,
                   width - 1, height - 1);
  cairo_stroke (cr);
1119

Owen Taylor's avatar
Owen Taylor committed
1120
  return FALSE;
1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
}

/*************************************************************
 * gtk_drag_highlight:
 *     Highlight the given widget in the default manner.
 *   arguments:
 *     widget:
 *   results:
 *************************************************************/

void 
gtk_drag_highlight (GtkWidget  *widget)
{
1134
  g_return_if_fail (GTK_IS_WIDGET (widget));
Manish Singh's avatar
Manish Singh committed
1135

1136 1137
  g_signal_connect_after (widget, "draw",
			  G_CALLBACK (gtk_drag_highlight_draw),
Manish Singh's avatar
Manish Singh committed
1138
			  NULL);
Owen Taylor's avatar
Owen Taylor committed
1139

1140
  gtk_widget_queue_draw (widget);
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
}

/*************************************************************
 * gtk_drag_unhighlight:
 *     Refresh the given widget to remove the highlight.
 *   arguments:
 *     widget:
 *   results:
 *************************************************************/

void 
Owen Taylor's avatar
Owen Taylor committed
1152
gtk_drag_unhighlight (GtkWidget *widget)
1153
{
1154
  g_return_if_fail (GTK_IS_WIDGET (widget));
Manish Singh's avatar
Manish Singh committed
1155 1156

  g_signal_handlers_disconnect_by_func (widget,
1157
					gtk_drag_highlight_draw,
Manish Singh's avatar
Manish Singh committed
1158
					NULL);
1159
  
Manish Singh's avatar
Manish Singh committed
1160
  gtk_widget_queue_draw (widget);
1161 1162
}

Owen Taylor's avatar
Owen Taylor committed
1163 1164 1165 1166 1167 1168 1169 1170 1171
static void
gtk_drag_dest_set_internal (GtkWidget       *widget,
			    GtkDragDestSite *site)
{
  GtkDragDestSite *old_site;
  
  g_return_if_fail (widget != NULL);

  /* HACK, do this in the destroy */
Manish Singh's avatar
Manish Singh committed
1172
  old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
Owen Taylor's avatar
Owen Taylor committed
1173
  if (old_site)
Manish Singh's avatar
Manish Singh committed
1174 1175 1176 1177 1178 1179 1180
    {
      g_signal_handlers_disconnect_by_func (widget,
					    gtk_drag_dest_realized,
					    old_site);
      g_signal_handlers_disconnect_by_func (widget,
					    gtk_drag_dest_hierarchy_changed,
					    old_site);
1181 1182

      site->track_motion = old_site->track_motion;
Manish Singh's avatar
Manish Singh committed
1183
    }
Owen Taylor's avatar
Owen Taylor committed
1184

1185
  if (gtk_widget_get_realized (widget))
Owen Taylor's avatar
Owen Taylor committed
1186 1187
    gtk_drag_dest_realized (widget);

Manish Singh's avatar
Manish Singh committed
1188 1189
  g_signal_connect (widget, "realize",
		    G_CALLBACK (gtk_drag_dest_realized), site);
1190
  g_signal_connect (widget, "hierarchy-changed",
Manish Singh's avatar
Manish Singh committed
1191
		    G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
Owen Taylor's avatar
Owen Taylor committed
1192

Matthias Clasen's avatar
Matthias Clasen committed
1193
  g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
Manish Singh's avatar
Manish Singh committed
1194
			  site, gtk_drag_dest_site_destroy);
Owen Taylor's avatar
Owen Taylor committed
1195 1196
}

1197
/**
1198
 * gtk_drag_dest_set:
1199 1200
 * @widget: a #GtkWidget
 * @flags: which types of default drag behavior to use
1201
 * @targets: (allow-none) (array length=n_targets): a pointer to an array of #GtkTargetEntry<!-- -->s
1202 1203 1204
 *     indicating the drop types that this @widget will accept, or %NULL.
 *     Later you can access the list with gtk_drag_dest_get_target_list()
 *     and gtk_drag_dest_find_target().
1205
 * @n_targets: the number of entries in @targets
1206 1207 1208 1209 1210 1211
 * @actions: a bitmask of possible actions for a drop onto this @widget.
 *
 * Sets a widget as a potential drop destination, and adds default behaviors.
 *
 * The default behaviors listed in @flags have an effect similar
 * to installing default handlers for the widget's drag-and-drop signals
1212 1213 1214 1215
 * (#GtkWidget:drag-motion, #GtkWidget:drag-drop, ...). They all exist
 * for convenience. When passing #GTK_DEST_DEFAULT_ALL for instance it is
 * sufficient to connect to the widget's #GtkWidget::drag-data-received
 * signal to get primitive, but consistent drag-and-drop support.
1216
 *
1217 1218 1219 1220 1221 1222 1223 1224
 * Things become more complicated when you try to preview the dragged data,
 * as described in the documentation for #GtkWidget:drag-motion. The default
 * behaviors described by @flags make some assumptions, that can conflict
 * with your own signal handlers. For instance #GTK_DEST_DEFAULT_DROP causes
 * invokations of gdk_drag_status() in the context of #GtkWidget:drag-motion,
 * and invokations of gtk_drag_finish() in #GtkWidget:drag-data-received.
 * Especially the later is dramatic, when your own #GtkWidget:drag-motion
 * handler calls gtk_drag_get_data() to inspect the dragged data.
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
 *
 * There's no way to set a default action here, you can use the
 * #GtkWidget:drag-motion callback for that. Here's an example which selects
 * the action to use depending on whether the control key is pressed or not:
 * |[
 * static void
 * drag_motion (GtkWidget *widget,
 *              GdkDragContext *context,
 *              gint x,
 *              gint y,
 *              guint time)
 * {
 *   GdkModifierType mask;
 *
 *   gdk_window_get_pointer (gtk_widget_get_window (widget),
 *                           NULL, NULL, &mask);
 *   if (mask & GDK_CONTROL_MASK)
 *     gdk_drag_status (context, GDK_ACTION_COPY, time);
 *   else
 *     gdk_drag_status (context, GDK_ACTION_MOVE, time);
 * }
 * ]|
1247 1248 1249 1250 1251 1252 1253
 */
void
gtk_drag_dest_set (GtkWidget            *widget,
		   GtkDestDefaults       flags,
		   const GtkTargetEntry *targets,
		   gint                  n_targets,
		   GdkDragAction         actions)
1254 1255 1256
{
  GtkDragDestSite *site;
  
1257
  g_return_if_fail (GTK_IS_WIDGET (widget));
Owen Taylor's avatar
Owen Taylor committed
1258

1259
  site = g_slice_new0 (GtkDragDestSite);
Owen Taylor's avatar
Owen Taylor committed
1260

1261 1262
  site->flags = flags;
  site->have_drag = FALSE;
1263 1264 1265 1266
  if (targets)
    site->target_list = gtk_target_list_new (targets, n_targets);
  else
    site->target_list = NULL;
1267
  site->actions = actions;
1268
  site->do_proxy = FALSE;
1269
  site->proxy_window = NULL;
1270
  site->track_motion = FALSE;
Owen Taylor's avatar
Owen Taylor committed
1271

Owen Taylor's avatar
Owen Taylor committed
1272
  gtk_drag_dest_set_internal (widget, site);
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
}

/*************************************************************
 * gtk_drag_dest_set_proxy:
 *     Set up this widget to proxy drags elsewhere.
 *   arguments:
 *     widget:          
 *     proxy_window:    window to which forward drag events
 *     protocol:        Drag protocol which the dest widget accepts
 *     use_coordinates: If true, send the same coordinates to the
 *                      destination, because it is a embedded 
 *                      subwindow.
 *   results:
 *************************************************************/

void 
gtk_drag_dest_set_proxy (GtkWidget      *widget,
			 GdkWindow      *proxy_window,
			 GdkDragProtocol protocol,
			 gboolean        use_coordinates)
{
  GtkDragDestSite *site;
  
1296 1297
  g_return_if_fail (GTK_IS_WIDGET (widget));
  g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
Owen Taylor's avatar
Owen Taylor committed
1298