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

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 "gtkimageprivate.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"
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;
};
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 */
103

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

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

293
static struct {
294
  GdkDragAction action;
295 296 297
  const gchar  *name;
  const guint8 *data;
  GdkPixbuf    *pixbuf;
298 299
  GdkCursor    *cursor;
} drag_cursors[] = {
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;
  
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;

}

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

722
static GdkCursor *
723 724 725
gtk_drag_get_cursor (GdkDisplay        *display,
		     GdkDragAction      action,
		     GtkDragSourceInfo *info)
726
{
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;
        }
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;
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
/********************
 * Destination side *
 ********************/

962 963 964 965 966
/**
 * gtk_drag_get_data: (method)
 * @widget: a #GtkWidget
 * @context: drag context
 * @target: format to retrieve the data in.
967
 * @time_: timestamp of triggering event.
968 969 970
 *
 * Get the data for a drag or drop
 */
971
void
972 973 974
gtk_drag_get_data (GtkWidget      *widget,
		   GdkDragContext *context,
		   GdkAtom         target,
975
		   guint32         time_)
976 977
{
  GtkWidget *selection_widget;
978

979 980
  g_return_if_fail (GTK_IS_WIDGET (widget));
  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
981

Matthias Clasen's avatar
Matthias Clasen committed
982
  selection_widget = gtk_drag_get_ipc_widget (widget);
983

Manish Singh's avatar
Manish Singh committed
984 985
  g_object_ref (context);
  g_object_ref (widget);
986

987
  g_signal_connect (selection_widget, "selection-received",
Manish Singh's avatar
Manish Singh committed
988
		    G_CALLBACK (gtk_drag_selection_received), widget);
989

990
  g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
991

992
  gtk_selection_convert (selection_widget,
Owen Taylor's avatar
Owen Taylor committed
993
			 gdk_drag_get_selection (context),
994
			 target,
995
			 time_);
996 997
}

998

999
/**
1000
 * gtk_drag_get_source_widget:
1001 1002 1003 1004 1005 1006 1007 1008
 * @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.
 */
1009
GtkWidget *
1010 1011 1012
gtk_drag_get_source_widget (GdkDragContext *context)
{
  GSList *tmp_list;
1013

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

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

1026 1027
	  return info ? info->widget : NULL;
	}
1028

1029 1030
      tmp_list = tmp_list->next;
    }
1031

1032 1033 1034
  return NULL;
}

1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
/*************************************************************
 * 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,
1049
		 gboolean        del,
1050 1051 1052
		 guint32         time)
{
  GdkAtom target = GDK_NONE;
1053

1054
  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
1055

1056
  if (success && del)
1057
    {
1058
      target = gdk_atom_intern_static_string ("DELETE");
1059
    }
1060
  else if (gdk_drag_context_get_protocol (context) == GDK_DRAG_PROTO_MOTIF)
1061
    {
1062 1063 1064
      target = gdk_atom_intern_static_string (success ? 
					      "XmTRANSFER_SUCCESS" : 
					      "XmTRANSFER_FAILURE");
1065
    }
1066

1067 1068
  if (target != GDK_NONE)
    {
1069
      GtkWidget *selection_widget = gtk_drag_get_ipc_widget_for_screen (gdk_window_get_screen (gdk_drag_context_get_source_window (context)));
1070

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

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

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

1107 1108 1109 1110 1111 1112 1113 1114
  context = gtk_widget_get_style_context (widget);

  gtk_style_context_save (context);
  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DND);

  gtk_render_frame (context, cr, 0, 0, width, height);

  gtk_style_context_restore (context);
1115

1116 1117 1118 1119 1120 1121
  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);
1122

Owen Taylor's avatar
Owen Taylor committed
1123
  return FALSE;
1124 1125
}

1126 1127 1128 1129 1130 1131
 /**
 * gtk_drag_highlight: (method)
 * @widget: a #GtkWidget
 *
 * Highlight the given widget in the default manner.
 */
1132 1133 1134
void 
gtk_drag_highlight (GtkWidget  *widget)
{
1135
  g_return_if_fail (GTK_IS_WIDGET (widget));
Manish Singh's avatar
Manish Singh committed
1136

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

1141
  gtk_widget_queue_draw (widget);
1142 1143
}

1144 1145 1146 1147 1148 1149
 /**
 * gtk_drag_unhighlight: (method)
 * @widget: a #GtkWidget
 *
 * Refresh the given widget to remove the highlight.
 */
1150
void 
Owen Taylor's avatar
Owen Taylor committed
1151
gtk_drag_unhighlight (GtkWidget *widget)
1152
{
1153
  g_return_if_fail (GTK_IS_WIDGET (widget));
Manish Singh's avatar
Manish Singh committed
1154 1155

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

Owen Taylor's avatar
Owen Taylor committed
1162 1163 1164 1165 1166 1167 1168 1169 1170
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
1171
  old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
Owen Taylor's avatar
Owen Taylor committed
1172
  if (old_site)
Manish Singh's avatar
Manish Singh committed
1173 1174 1175 1176 1177 1178 1179
    {
      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);
1180 1181

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

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

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

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

1196
/**
1197
 * gtk_drag_dest_set: (method)
1198 1199
 * @widget: a #GtkWidget
 * @flags: which types of default drag behavior to use
1200
 * @targets: (allow-none) (array length=n_targets): a pointer to an array of #GtkTargetEntry<!-- -->s
1201 1202 1203
 *     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().
1204
 * @n_targets: the number of entries in @targets
1205 1206 1207 1208 1209 1210
 * @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
1211 1212 1213 1214
 * (#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.
1215
 *
1216 1217 1218 1219 1220 1221 1222 1223
 * 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.
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
 *
 * 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);
 * }
 * ]|
1246 1247 1248 1249 1250 1251 1252
 */
void
gtk_drag_dest_set (GtkWidget            *widget,
		   GtkDestDefaults       flags,
		   const GtkTargetEntry *targets,
		   gint                  n_targets,
		   GdkDragAction         actions)
1253 1254 1255
{
  GtkDragDestSite *site;
  
1256
  g_return_if_fail (GTK_IS_WIDGET (widget));
1257

1258
  site = g_slice_new0 (GtkDragDestSite);
1259

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

Owen Taylor's avatar
Owen Taylor committed
1271
  gtk_drag_dest_set_internal (widget, site);
1272 1273
}

1274
/**
1275
 * gtk_drag_dest_set_proxy: (method)
1276 1277 1278 1279 1280 1281 1282 1283 1284
 * @widget: a #GtkWidget
 * @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.
 *
 * Set up this widget to proxy drags elsewhere.
 */
1285 1286 1287 1288 1289 1290 1291 1292
void 
gtk_drag_dest_set_proxy (GtkWidget      *widget,
			 GdkWindow      *proxy_window,
			 GdkDragProtocol protocol,
			 gboolean        use_coordinates)
{
  GtkDragDestSite *site;
  
1293 1294
  g_return_if_fail (GTK_IS_WIDGET (widget));
  g_return_if_fail (!proxy_window || GDK_IS_WINDOW (proxy_window));
1295

1296
  site = g_slice_new (GtkDragDestSite);
1297

1298 1299 1300 1301 1302
  site->flags = 0;
  site->have_drag = FALSE;
  site->target_list = NULL;
  site->actions = 0;
  site->proxy_window = proxy_window;
1303
  if (proxy_window)
Manish Singh's avatar
Manish Singh committed
1304
    g_object_ref (proxy_window);
1305
  site->do_proxy = TRUE;
1306 1307
  site->proxy_protocol = protocol;
  site->proxy_coords = use_coordinates;
1308
  site->track_motion = FALSE;
1309

Owen Taylor's avatar
Owen Taylor committed
1310
  gtk_drag_dest_set_internal (widget, site);
1311 1312
}

1313
 /**
1314
 * gtk_drag_dest_unset: (method)
1315 1316 1317 1318
 * @widget: a #GtkWidget
 *
 * Unregister this widget as a drag target.
 */
1319
void 
Owen Taylor's avatar
Owen Taylor committed
1320
gtk_drag_dest_unset (GtkWidget *widget)
1321
{
1322 1323
  GtkDragDestSite *old_site;

1324
  g_return_if_fail (GTK_IS_WIDGET (widget));
1325

1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337
  old_site = g_object_get_data (G_OBJECT (widget),
                                "gtk-drag-dest");
  if (old_site)
    {
      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);
    }

1338
  g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
1339 1340
}

Havoc Pennington's avatar
Havoc Pennington committed
1341
/**
1342
 * gtk_drag_dest_get_target_list: (method)
Havoc Pennington's avatar
Havoc Pennington committed
1343 1344 1345 1346 1347
 * @widget: a #GtkWidget
 * 
 * Returns the list of targets this widget can accept from
 * drag-and-drop.
 * 
1348
 * Return value: (transfer none): the #GtkTargetList, or %NULL if none
Havoc Pennington's avatar
Havoc Pennington committed
1349 1350 1351 1352 1353
 **/
GtkTargetList*
gtk_drag_dest_get_target_list (GtkWidget *widget)
{
  GtkDragDestSite *site;
1354 1355

  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
Havoc Pennington's avatar
Havoc Pennington committed
1356
  
Manish Singh's avatar
Manish Singh committed
1357
  site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
Havoc Pennington's avatar
Havoc Pennington committed
1358 1359 1360 1361 1362

  return site ? site->target_list : NULL;  
}

/**
1363
 * gtk_drag_dest_set_target_list: (method)
Havoc Pennington's avatar
Havoc Pennington committed
1364
 * @widget: a #GtkWidget that's a drag destination
1365
 * @target_list: (allow-none): list of droppable targets, or %NULL for none
Havoc Pennington's avatar
Havoc Pennington committed
1366 1367 1368 1369 1370 1371 1372 1373 1374 1375
 * 
 * Sets the target types that this widget can accept from drag-and-drop.
 * The widget must first be made into a drag destination with
 * gtk_drag_dest_set().
 **/
void
gtk_drag_dest_set_target_list (GtkWidget      *widget,
                               GtkTargetList  *target_list)
{
  GtkDragDestSite *site;
Manish Singh's avatar
Manish Singh committed
1376

1377 1378
  g_return_if_fail (GTK_IS_WIDGET (widget));
  
Manish Singh's avatar
Manish Singh committed
1379
  site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
Havoc Pennington's avatar
Havoc Pennington committed
1380
  
1381
  if (!site)
Havoc Pennington's avatar
Havoc Pennington committed
1382
    {
1383 1384
      g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
                 "to make the widget into a drag destination");
Havoc Pennington's avatar
Havoc Pennington committed
1385 1386 1387 1388
      return;
    }

  if (target_list)
1389
    gtk_target_list_ref (target_list);
Havoc Pennington's avatar
Havoc Pennington committed
1390 1391 1392 1393 1394 1395 1396
  
  if (site->target_list)
    gtk_target_list_unref (site->target_list);

  site->target_list = target_list;
}

1397
/**
1398
 * gtk_drag_dest_add_text_targets: (method)
1399 1400 1401
 * @widget: a #GtkWidget that's a drag destination
 *
 * Add the text targets supported by #GtkSelection to
1402 1403 1404 1405
 * the target list of the drag destination. The targets
 * are added with @info = 0. If you need another value, 
 * use gtk_target_list_add_text_targets() and
 * gtk_drag_dest_set_target_list().
1406 1407 1408 1409 1410 1411 1412 1413 1414
 * 
 * Since: 2.6
 **/
void
gtk_drag_dest_add_text_targets (GtkWidget *widget)
{
  GtkTargetList *target_list;

  target_list = gtk_drag_dest_get_target_list (widget);
1415 1416 1417
  if (target_list)
    gtk_target_list_ref (target_list);
  else
1418
    target_list = gtk_target_list_new (NULL, 0);
1419
  gtk_target_list_add_text_targets (target_list, 0);
1420
  gtk_drag_dest_set_target_list (widget, target_list);
1421
  gtk_target_list_unref (target_list);
1422
}
Havoc Pennington's avatar
Havoc Pennington committed
1423

1424
/**
1425
 * gtk_drag_dest_add_image_targets: (method)
1426 1427 1428
 * @widget: a #GtkWidget that's a drag destination
 *
 * Add the image targets supported by #GtkSelection to
1429 1430 1431 1432
 * the target list of the drag destination. The targets
 * are added with @info = 0. If you need another value, 
 * use gtk_target_list_add_image_targets() and
 * gtk_drag_dest_set_target_list().
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445
 * 
 * Since: 2.6
 **/
void
gtk_drag_dest_add_image_targets (GtkWidget *widget)
{
  GtkTargetList *target_list;

  target_list = gtk_drag_dest_get_target_list (widget);
  if (target_list)
    gtk_target_list_ref (target_list);
  else
    target_list = gtk_target_list_new (NULL, 0);