gtkdnd.c 111 KB
Newer Older
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 30 31

#include <stdlib.h>
#include <string.h>

32 33
#include "gdkconfig.h"

34
#include "gdk/gdkkeysyms.h"
35 36

#include "gtkdnd.h"
37 38
#include "gtkiconfactory.h"
#include "gtkicontheme.h"
39
#include "gtkimage.h"
40 41
#include "gtkinvisible.h"
#include "gtkmain.h"
42
#include "gtkplug.h"
43
#include "gtkstock.h"
Owen Taylor's avatar
Owen Taylor committed
44
#include "gtkwindow.h"
Matthias Clasen's avatar
Matthias Clasen committed
45
#include "gtkintl.h"
46
#include "gtkalias.h"
47

48
static GSList *source_widgets = NULL;
49 50 51 52 53 54 55 56 57

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


Owen Taylor's avatar
Owen Taylor committed
58 59
typedef enum 
{
60 61 62 63 64
  GTK_DRAG_STATUS_DRAG,
  GTK_DRAG_STATUS_WAIT,
  GTK_DRAG_STATUS_DROP
} GtkDragStatus;

Owen Taylor's avatar
Owen Taylor committed
65 66
struct _GtkDragSourceSite 
{
67 68 69
  GdkModifierType    start_button_mask;
  GtkTargetList     *target_list;        /* Targets for drag data */
  GdkDragAction      actions;            /* Possible actions */
70 71 72 73 74 75 76 77

  /* Drag icon */
  GtkImageType icon_type;
  union
  {
    GtkImagePixmapData pixmap;
    GtkImagePixbufData pixbuf;
    GtkImageStockData stock;
78
    GtkImageIconNameData name;
79
  } icon_data;
Havoc Pennington's avatar
Havoc Pennington committed
80
  GdkBitmap *icon_mask;
81

82
  GdkColormap       *colormap;	         /* Colormap for drag icon */
Owen Taylor's avatar
Owen Taylor committed
83

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

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

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

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

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

Owen Taylor's avatar
Owen Taylor committed
124 125
struct _GtkDragDestSite 
{
126 127 128 129 130
  GtkDestDefaults    flags;
  GtkTargetList     *target_list;
  GdkDragAction      actions;
  GdkWindow         *proxy_window;
  GdkDragProtocol    proxy_protocol;
131 132 133
  guint              do_proxy : 1;
  guint              proxy_coords : 1;
  guint              have_drag : 1;
134
  guint              track_motion : 1;
135
};
Owen Taylor's avatar
Owen Taylor committed
136
  
Owen Taylor's avatar
Owen Taylor committed
137 138
struct _GtkDragDestInfo 
{
139 140 141 142
  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 */
143
  guint              dropped : 1;     /* Set after we receive a drop */
144
  guint32            proxy_drop_time; /* Timestamp for proxied drop */
145
  guint              proxy_drop_wait : 1; /* Set if we are waiting for a
146 147 148 149 150 151
					   * status reply before sending
					   * a proxied drop on.
					   */
  gint               drop_x, drop_y; /* Position of drop */
};

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

Owen Taylor's avatar
Owen Taylor committed
166 167
struct _GtkDragFindData 
{
168 169 170 171 172
  gint x;
  gint y;
  GdkDragContext *context;
  GtkDragDestInfo *info;
  gboolean found;
173
  gboolean toplevel;
174 175 176 177 178 179 180
  gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
			gint x, gint y, guint32 time);
  guint32 time;
};

/* Enumeration for some targets we handle internally */

Owen Taylor's avatar
Owen Taylor committed
181
enum {
182
  TARGET_MOTIF_SUCCESS = 0x40000000,
183 184 185 186 187 188 189 190 191 192 193 194 195
  TARGET_MOTIF_FAILURE,
  TARGET_DELETE
};

/* Drag icons */

static GdkPixmap   *default_icon_pixmap = NULL;
static GdkPixmap   *default_icon_mask = NULL;
static GdkColormap *default_icon_colormap = NULL;
static gint         default_icon_hot_x;
static gint         default_icon_hot_y;

/* Forward declarations */
Owen Taylor's avatar
Owen Taylor committed
196 197 198 199 200
static void          gtk_drag_get_event_actions (GdkEvent        *event, 
					         gint             button,
					         GdkDragAction    actions,
					         GdkDragAction   *suggested_action,
					         GdkDragAction   *possible_actions);
201
static GdkCursor *   gtk_drag_get_cursor         (GdkDisplay     *display,
202 203
						  GdkDragAction   action,
						  GtkDragSourceInfo *info);
204
static void          gtk_drag_update_cursor      (GtkDragSourceInfo *info);
205
static GtkWidget    *gtk_drag_get_ipc_widget     (GdkScreen	 *screen);
Owen Taylor's avatar
Owen Taylor committed
206
static void          gtk_drag_release_ipc_widget (GtkWidget      *widget);
207

Owen Taylor's avatar
Owen Taylor committed
208 209 210
static gboolean      gtk_drag_highlight_expose   (GtkWidget      *widget,
					  	  GdkEventExpose *event,
						  gpointer        data);
211

212 213
static void     gtk_drag_selection_received     (GtkWidget        *widget,
						 GtkSelectionData *selection_data,
214
						 guint             time,
215 216 217 218 219 220 221 222
						 gpointer          data);
static void     gtk_drag_find_widget            (GtkWidget        *widget,
						 GtkDragFindData  *data);
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
223
						 GtkWidget        *previous_toplevel);
224 225 226 227 228 229 230 231 232 233 234 235 236 237
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
238 239 240 241 242 243

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);
244 245 246 247 248 249 250 251 252 253 254

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,
						gboolean           success,
						guint              time);
255 256
static void gtk_drag_cancel                    (GtkDragSourceInfo *info,
						guint32            time);
257

258
static gboolean gtk_drag_source_event_cb       (GtkWidget         *widget,
259 260 261 262 263 264 265 266
						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);
267
static gboolean gtk_drag_anim_timeout          (gpointer           data);
268
static void gtk_drag_remove_icon               (GtkDragSourceInfo *info);
Owen Taylor's avatar
Owen Taylor committed
269
static void gtk_drag_source_info_destroy       (GtkDragSourceInfo *info);
270 271
static void gtk_drag_add_update_idle           (GtkDragSourceInfo *info);

272
static void gtk_drag_update                    (GtkDragSourceInfo *info,
273
						GdkScreen         *screen,
274 275 276
						gint               x_root,
						gint               y_root,
						GdkEvent          *event);
277
static gboolean gtk_drag_motion_cb             (GtkWidget         *widget, 
278 279
					        GdkEventMotion    *event, 
					        gpointer           data);
280
static gboolean gtk_drag_key_cb                (GtkWidget         *widget, 
281 282
					        GdkEventKey       *event, 
					        gpointer           data);
283 284 285 286
static gboolean gtk_drag_grab_broken_event_cb  (GtkWidget          *widget,
						GdkEventGrabBroken *event,
						gpointer            data);
static gboolean gtk_drag_button_release_cb     (GtkWidget         *widget, 
287 288
					        GdkEventButton    *event, 
					        gpointer           data);
289
static gboolean gtk_drag_abort_timeout         (gpointer           data);
290 291 292 293 294

/************************
 * Cursor and Icon data *
 ************************/

295
#include "gtkdndcursors.h"
296

Owen Taylor's avatar
Owen Taylor committed
297
static struct {
298
  GdkDragAction action;
299 300 301
  const gchar  *name;
  const guint8 *data;
  GdkPixbuf    *pixbuf;
302 303
  GdkCursor    *cursor;
} drag_cursors[] = {
Matthias Clasen's avatar
Matthias Clasen committed
304
  { GDK_ACTION_DEFAULT, NULL },
305 306 307 308 309
  { 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 },
310 311
};

Owen Taylor's avatar
Owen Taylor committed
312
static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
313 314 315 316 317

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

Manish Singh's avatar
Manish Singh committed
318
static void
319 320 321 322 323
set_can_change_screen (GtkWidget *widget,
		       gboolean   can_change_screen)
{
  can_change_screen = can_change_screen != FALSE;
  
Matthias Clasen's avatar
Matthias Clasen committed
324
  g_object_set_data (G_OBJECT (widget), I_("gtk-dnd-can-change-screen"),
325 326 327 328 329 330 331 332 333 334
		     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;

}

335 336 337 338 339 340 341 342 343
/*************************************************************
 * gtk_drag_get_ipc_widget:
 *     Return a invisible, off-screen, override-redirect
 *     widget for IPC.
 *   arguments:
 *     
 *   results:
 *************************************************************/

Owen Taylor's avatar
Owen Taylor committed
344
static GtkWidget *
345
gtk_drag_get_ipc_widget (GdkScreen *screen)
346 347
{
  GtkWidget *result;
348 349
  GSList *drag_widgets = g_object_get_data (G_OBJECT (screen), 
					    "gtk-dnd-ipc-widgets");
350 351 352 353 354 355
  
  if (drag_widgets)
    {
      GSList *tmp = drag_widgets;
      result = drag_widgets->data;
      drag_widgets = drag_widgets->next;
356
      g_object_set_data (G_OBJECT (screen),
Matthias Clasen's avatar
Matthias Clasen committed
357
			 I_("gtk-dnd-ipc-widgets"),
358
			 drag_widgets);
359 360 361 362
      g_slist_free_1 (tmp);
    }
  else
    {
363
      result = gtk_invisible_new_for_screen (screen);
364 365
      gtk_widget_show (result);
    }
Owen Taylor's avatar
Owen Taylor committed
366

367 368 369
  return result;
}

Owen Taylor's avatar
Owen Taylor committed
370
/***************************************************************
371
 * gtk_drag_release_ipc_widget:
Owen Taylor's avatar
Owen Taylor committed
372
 *     Releases widget retrieved with gtk_drag_get_ipc_widget ()
373 374 375
 *   arguments:
 *     widget: the widget to release.
 *   results:
Owen Taylor's avatar
Owen Taylor committed
376
 ***************************************************************/
377 378 379 380

static void
gtk_drag_release_ipc_widget (GtkWidget *widget)
{
381 382 383
  GdkScreen *screen = gtk_widget_get_screen (widget);
  GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
					    "gtk-dnd-ipc-widgets");
384
  drag_widgets = g_slist_prepend (drag_widgets, widget);
385
  g_object_set_data (G_OBJECT (screen),
Matthias Clasen's avatar
Matthias Clasen committed
386
		     I_("gtk-dnd-ipc-widgets"),
387
		     drag_widgets);
388 389
}

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
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;
}

427 428 429 430 431 432
static void
gtk_drag_get_event_actions (GdkEvent *event, 
			    gint button, 
			    GdkDragAction  actions,
			    GdkDragAction *suggested_action,
			    GdkDragAction *possible_actions)
433
{
434 435
  *suggested_action = 0;
  *possible_actions = 0;
Owen Taylor's avatar
Owen Taylor committed
436

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
  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
456 457
	case GDK_ENTER_NOTIFY:
	case GDK_LEAVE_NOTIFY:
458 459
	  state = event->crossing.state;
	  break;
Owen Taylor's avatar
Owen Taylor committed
460 461 462
	default:
	  break;
	}
463

Owen Taylor's avatar
Owen Taylor committed
464
      if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
465 466 467 468 469
	{
	  *suggested_action = GDK_ACTION_ASK;
	  *possible_actions = actions;
	}
      else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
470 471
	{
	  if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
472 473 474 475 476 477 478
	    {
	      if (actions & GDK_ACTION_LINK)
		{
		  *suggested_action = GDK_ACTION_LINK;
		  *possible_actions = GDK_ACTION_LINK;
		}
	    }
479
	  else if (state & GDK_CONTROL_MASK)
480 481 482 483 484 485 486 487
	    {
	      if (actions & GDK_ACTION_COPY)
		{
		  *suggested_action = GDK_ACTION_COPY;
		  *possible_actions = GDK_ACTION_COPY;
		}
	      return;
	    }
488
	  else
489 490 491 492 493 494 495 496
	    {
	      if (actions & GDK_ACTION_MOVE)
		{
		  *suggested_action = GDK_ACTION_MOVE;
		  *possible_actions = GDK_ACTION_MOVE;
		}
	      return;
	    }
497 498 499
	}
      else
	{
500
	  *possible_actions = actions;
Owen Taylor's avatar
Owen Taylor committed
501

502 503 504 505
	  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;
506
	  else if (actions & GDK_ACTION_MOVE)
507
	    *suggested_action = GDK_ACTION_MOVE;
508
	  else if (actions & GDK_ACTION_LINK)
509
	    *suggested_action = GDK_ACTION_LINK;
510 511
	}
    }
Owen Taylor's avatar
Owen Taylor committed
512 513 514 515 516 517 518 519 520 521 522
  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;
    }
523 524 525 526 527 528 529 530
}

static gboolean
gtk_drag_can_use_rgba_cursor (GdkDisplay *display, 
			      gint        width,
			      gint        height)
{
  guint max_width, max_height;
531
  
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
  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;
548 549
}

Owen Taylor's avatar
Owen Taylor committed
550
static GdkCursor *
551 552 553
gtk_drag_get_cursor (GdkDisplay        *display,
		     GdkDragAction      action,
		     GtkDragSourceInfo *info)
554
{
Matthias Clasen's avatar
Matthias Clasen committed
555
  gint i;
556 557 558 559 560 561 562 563 564 565 566 567 568 569

  /* 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)
	  {
	    gdk_cursor_unref (drag_cursors[i].cursor);
	    drag_cursors[i].cursor = NULL;
	  }
    }
 
570 571 572
  for (i = 0 ; i < n_drag_cursors - 1; i++)
    if (drag_cursors[i].action == action)
      break;
573 574 575 576 577

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

578 579
  if (drag_cursors[i].cursor != NULL)
    {
580
      if (display != gdk_cursor_get_display (drag_cursors[i].cursor))
581 582 583 584 585
	{
	  gdk_cursor_unref (drag_cursors[i].cursor);
	  drag_cursors[i].cursor = NULL;
	}
    }
586 587 588 589
  
  if (drag_cursors[i].cursor == NULL)
    drag_cursors[i].cursor = gdk_cursor_new_from_name (display, drag_cursors[i].name);
  
590
  if (drag_cursors[i].cursor == NULL)
591 592 593
    drag_cursors[i].cursor = gdk_cursor_new_from_pixbuf (display, drag_cursors[i].pixbuf, 0, 0);

  if (info && info->icon_pixbuf) 
594
    {
595 596 597 598
      gint cursor_width, cursor_height;
      gint icon_width, icon_height;
      gint width, height;
      GdkPixbuf *cursor_pixbuf, *pixbuf;
599
      gint hot_x, hot_y;
Manish Singh's avatar
Manish Singh committed
600
      gint icon_x, icon_y, ref_x, ref_y;
601 602 603 604 605 606 607 608 609

      if (info->drag_cursors[i] != NULL)
        {
          if (display == gdk_cursor_get_display (info->drag_cursors[i]))
	    return info->drag_cursors[i];
	  
	  gdk_cursor_unref (info->drag_cursors[i]);
	  info->drag_cursors[i] = NULL;
        }
Owen Taylor's avatar
Owen Taylor committed
610

611 612
      icon_x = info->hot_x;
      icon_y = info->hot_y;
Matthias Clasen's avatar
Matthias Clasen committed
613 614 615 616
      icon_width = gdk_pixbuf_get_width (info->icon_pixbuf);
      icon_height = gdk_pixbuf_get_height (info->icon_pixbuf);

      hot_x = hot_y = 0;
617 618
      cursor_pixbuf = gdk_cursor_get_image (drag_cursors[i].cursor);
      if (!cursor_pixbuf)
619
	cursor_pixbuf = g_object_ref (drag_cursors[i].pixbuf);
620 621 622 623 624 625 626
      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"));
627

628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
#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.
           */ 
647 648 649
	  for (j = 0; j < 10; j++)
	    {
	      const gchar *opt;
650
	      gchar key[32];
651
	      gchar **toks;
652
	      GtkAnchorType icon_anchor;
Owen Taylor's avatar
Owen Taylor committed
653

654 655 656 657 658 659 660 661 662 663 664 665 666
	      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]);
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
		  
		  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: ;
		    }
697 698 699 700 701

		  g_strfreev (toks);
		  break;
		}
	    }
702
#endif
703
	}
Matthias Clasen's avatar
Matthias Clasen committed
704

705 706
      cursor_width = gdk_pixbuf_get_width (cursor_pixbuf);
      cursor_height = gdk_pixbuf_get_height (cursor_pixbuf);
Matthias Clasen's avatar
Matthias Clasen committed
707
      
708 709 710 711 712 713 714
      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))
	{
715 716 717
	  /* Composite cursor and icon so that both hotspots
	   * end up at (ref_x, ref_y)
	   */
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
	  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] = 
738
	    gdk_cursor_new_from_pixbuf (display, pixbuf, ref_x, ref_y);
739 740 741 742 743 744 745 746 747 748
	  
	  g_object_unref (pixbuf);
	}
      
      g_object_unref (cursor_pixbuf);
      
      if (info->drag_cursors[i] != NULL)
	return info->drag_cursors[i];
    }
 
749 750 751
  return drag_cursors[i].cursor;
}

752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
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), 
770
				drag_cursors[i].action, info);
771 772 773 774 775 776 777 778 779 780 781 782
  
  if (cursor != info->cursor)
    {
      gdk_pointer_grab (info->ipc_widget->window, FALSE,
			GDK_POINTER_MOTION_MASK |
			GDK_BUTTON_RELEASE_MASK,
			NULL,
			cursor, info->grab_time);
      info->cursor = cursor;
    }
}

783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
/********************
 * 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
805

806 807 808
  g_return_if_fail (GTK_IS_WIDGET (widget));
  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
  g_return_if_fail (!context->is_source);
Owen Taylor's avatar
Owen Taylor committed
809

810
  selection_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
Owen Taylor's avatar
Owen Taylor committed
811

Manish Singh's avatar
Manish Singh committed
812 813
  g_object_ref (context);
  g_object_ref (widget);
814
  
Manish Singh's avatar
Manish Singh committed
815 816
  g_signal_connect (selection_widget, "selection_received",
		    G_CALLBACK (gtk_drag_selection_received), widget);
Owen Taylor's avatar
Owen Taylor committed
817

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

820
  gtk_selection_convert (selection_widget,
Owen Taylor's avatar
Owen Taylor committed
821
			 gdk_drag_get_selection (context),
822 823 824 825
			 target,
			 time);
}

826 827 828 829 830 831 832 833 834 835 836 837

/*************************************************************
 * gtk_drag_get_source_widget:
 *     Get the widget the was the source of this drag, if
 *     the drag originated from this application.
 *   arguments:
 *     context: The drag context for this drag
 *   results:
 *     The source widget, or NULL if the drag originated from
 *     a different application.
 *************************************************************/

Owen Taylor's avatar
Owen Taylor committed
838
GtkWidget *
839 840 841
gtk_drag_get_source_widget (GdkDragContext *context)
{
  GSList *tmp_list;
Owen Taylor's avatar
Owen Taylor committed
842

843 844 845
  g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
  g_return_val_if_fail (!context->is_source, NULL);
  
846 847 848 849 850 851 852 853
  tmp_list = source_widgets;
  while (tmp_list)
    {
      GtkWidget *ipc_widget = tmp_list->data;
      
      if (ipc_widget->window == context->source_window)
	{
	  GtkDragSourceInfo *info;
Manish Singh's avatar
Manish Singh committed
854
	  info = g_object_get_data (G_OBJECT (ipc_widget), "gtk-info");
Owen Taylor's avatar
Owen Taylor committed
855

856 857
	  return info ? info->widget : NULL;
	}
Owen Taylor's avatar
Owen Taylor committed
858

859 860
      tmp_list = tmp_list->next;
    }
Owen Taylor's avatar
Owen Taylor committed
861

862 863 864
  return NULL;
}

865 866 867 868 869 870 871 872 873 874 875 876 877 878
/*************************************************************
 * 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,
879
		 gboolean        del,
880 881 882
		 guint32         time)
{
  GdkAtom target = GDK_NONE;
Owen Taylor's avatar
Owen Taylor committed
883

884 885
  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
  g_return_if_fail (!context->is_source);
Owen Taylor's avatar
Owen Taylor committed
886

887
  if (success && del)
888
    {
889
      target = gdk_atom_intern_static_string ("DELETE");
890 891 892
    }
  else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
    {
893 894 895
      target = gdk_atom_intern_static_string (success ? 
					      "XmTRANSFER_SUCCESS" : 
					      "XmTRANSFER_FAILURE");
896
    }
Owen Taylor's avatar
Owen Taylor committed
897

898 899
  if (target != GDK_NONE)
    {
900
      GtkWidget *selection_widget = gtk_drag_get_ipc_widget (gdk_drawable_get_screen (context->source_window));
Owen Taylor's avatar
Owen Taylor committed
901

Manish Singh's avatar
Manish Singh committed
902
      g_object_ref (context);
903
      
Matthias Clasen's avatar
Matthias Clasen committed
904
      g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
Manish Singh's avatar
Manish Singh committed
905 906 907
      g_signal_connect (selection_widget, "selection_received",
			G_CALLBACK (gtk_drag_selection_received),
			NULL);
908 909
      
      gtk_selection_convert (selection_widget,
Owen Taylor's avatar
Owen Taylor committed
910
			     gdk_drag_get_selection (context),
911 912 913 914
			     target,
			     time);
    }
  
915
  if (!(success && del))
916 917 918 919
    gdk_drop_finish (context, success, time);
}

/*************************************************************
Owen Taylor's avatar
Owen Taylor committed
920 921
 * gtk_drag_highlight_expose:
 *     Callback for expose_event for highlighted widgets.
922 923
 *   arguments:
 *     widget:
Owen Taylor's avatar
Owen Taylor committed
924 925
 *     event:
 *     data:
926 927 928
 *   results:
 *************************************************************/

Owen Taylor's avatar
Owen Taylor committed
929 930 931 932
static gboolean
gtk_drag_highlight_expose (GtkWidget      *widget,
			   GdkEventExpose *event,
			   gpointer        data)
933
{
934
  gint x, y, width, height;
Owen Taylor's avatar
Owen Taylor committed
935
  
936
  if (GTK_WIDGET_DRAWABLE (widget))
937
    {
938 939
      cairo_t *cr;
      
940 941 942 943 944 945 946 947 948 949 950
      if (GTK_WIDGET_NO_WINDOW (widget))
	{
	  x = widget->allocation.x;
	  y = widget->allocation.y;
	  width = widget->allocation.width;
	  height = widget->allocation.height;
	}
      else
	{
	  x = 0;
	  y = 0;
Manish Singh's avatar
Manish Singh committed
951
	  gdk_drawable_get_size (widget->window, &width, &height);
952 953
	}
      
Manish Singh's avatar
Manish Singh committed
954 955 956 957
      gtk_paint_shadow (widget->style, widget->window,
		        GTK_STATE_NORMAL, GTK_SHADOW_OUT,
		        NULL, widget, "dnd",
			x, y, width, height);
958 959 960 961 962 963 964 965 966

      cr = gdk_cairo_create (widget->window);
      cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
      cairo_set_line_width (cr, 1.0);
      cairo_rectangle (cr,
		       x + 0.5, y + 0.5,
		       width - 1, height - 1);
      cairo_stroke (cr);
      cairo_destroy (cr);
967
    }
968

Owen Taylor's avatar
Owen Taylor committed
969
  return FALSE;
970 971 972 973 974 975 976 977 978 979 980 981 982
}

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

void 
gtk_drag_highlight (GtkWidget  *widget)
{
983
  g_return_if_fail (GTK_IS_WIDGET (widget));
Manish Singh's avatar
Manish Singh committed
984 985 986 987

  g_signal_connect_after (widget, "expose_event",
			  G_CALLBACK (gtk_drag_highlight_expose),
			  NULL);
Owen Taylor's avatar
Owen Taylor committed
988

989
  gtk_widget_queue_draw (widget);
990 991 992 993 994 995 996 997 998 999 1000
}

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

void 
Owen Taylor's avatar
Owen Taylor committed
1001
gtk_drag_unhighlight (GtkWidget *widget)
1002
{
1003
  g_return_if_fail (GTK_IS_WIDGET (widget));
Manish Singh's avatar
Manish Singh committed
1004 1005 1006 1007

  g_signal_handlers_disconnect_by_func (widget,
					gtk_drag_highlight_expose,
					NULL);
1008
  
Manish Singh's avatar
Manish Singh committed
1009
  gtk_widget_queue_draw (widget);
1010 1011
}

Owen Taylor's avatar
Owen Taylor committed
1012 1013 1014 1015 1016 1017 1018 1019 1020
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
1021
  old_site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
Owen Taylor's avatar
Owen Taylor committed
1022
  if (old_site)
Manish Singh's avatar
Manish Singh committed
1023 1024 1025 1026 1027 1028 1029
    {
      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);
1030 1031

      site->track_motion = old_site->track_motion;
Manish Singh's avatar
Manish Singh committed
1032
    }
Owen Taylor's avatar
Owen Taylor committed
1033 1034 1035 1036

  if (GTK_WIDGET_REALIZED (widget))
    gtk_drag_dest_realized (widget);

Manish Singh's avatar
Manish Singh committed
1037 1038 1039 1040
  g_signal_connect (widget, "realize",
		    G_CALLBACK (gtk_drag_dest_realized), site);
  g_signal_connect (widget, "hierarchy_changed",
		    G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
Owen Taylor's avatar
Owen Taylor committed
1041

Matthias Clasen's avatar
Matthias Clasen committed
1042
  g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
Manish Singh's avatar
Manish Singh committed
1043
			  site, gtk_drag_dest_site_destroy);
Owen Taylor's avatar
Owen Taylor committed
1044 1045 1046
}
			    

1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
/*************************************************************
 * gtk_drag_dest_set:
 *     Register a drop site, and possibly add default behaviors.
 *   arguments:
 *     widget:    
 *     flags:     Which types of default drag behavior to use
 *     targets:   Table of targets that can be accepted
 *     n_targets: Number of of entries in targets
 *     actions:   
 *   results:
 *************************************************************/

void 
1060