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>

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
737
738
739
740
741

  /* 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;
	  }
    }
 
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
755
756
757
	{
	  gdk_cursor_unref (drag_cursors[i].cursor);
	  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
779
780
781

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