source: trunk/third/gtk/gtk/gtkdnd.c @ 17071

Revision 17071, 76.0 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17070, which included commits to RCS files with non-trunk default branches.
RevLine 
[14481]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 Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20/*
21 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22 * file for a list of people on the GTK+ Team.  See the ChangeLog
23 * files for a list of changes.  These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25 */
26
[15780]27#include <stdlib.h>
28
[14481]29#include "gdk/gdkx.h"
30#include "gdk/gdkkeysyms.h"
31
32#include "gtkdnd.h"
33#include "gtkinvisible.h"
34#include "gtkmain.h"
35#include "gtksignal.h"
36#include "gtkwindow.h"
37
38static GSList *drag_widgets = NULL;
39static GSList *source_widgets = NULL;
40
41typedef struct _GtkDragSourceSite GtkDragSourceSite;
42typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
43typedef struct _GtkDragDestSite GtkDragDestSite;
44typedef struct _GtkDragDestInfo GtkDragDestInfo;
45typedef struct _GtkDragAnim GtkDragAnim;
46typedef struct _GtkDragFindData GtkDragFindData;
47
48
49typedef enum
50{
51  GTK_DRAG_STATUS_DRAG,
52  GTK_DRAG_STATUS_WAIT,
53  GTK_DRAG_STATUS_DROP
54} GtkDragStatus;
55
56struct _GtkDragSourceSite
57{
58  GdkModifierType    start_button_mask;
59  GtkTargetList     *target_list;        /* Targets for drag data */
60  GdkDragAction      actions;            /* Possible actions */
61  GdkColormap       *colormap;           /* Colormap for drag icon */
62  GdkPixmap         *pixmap;             /* Icon for drag data */
63  GdkBitmap         *mask;
64
65  /* Stored button press information to detect drag beginning */
66  gint               state;
67  gint               x, y;
68};
69 
70struct _GtkDragSourceInfo
71{
72  GtkWidget         *widget;
73  GtkTargetList     *target_list; /* Targets for drag data */
74  GdkDragAction      possible_actions; /* Actions allowed by source */
75  GdkDragContext    *context;     /* drag context */
76  GtkWidget         *icon_window; /* Window for drag */
77  GtkWidget         *ipc_widget;  /* GtkInvisible for grab, message passing */
78  GdkCursor         *cursor;      /* Cursor for drag */
79  gint hot_x, hot_y;              /* Hot spot for drag */
80  gint button;                    /* mouse button starting drag */
81
82  GtkDragStatus      status;      /* drag status */
83  GdkEvent          *last_event;  /* motion event waiting for response */
84
85  gint               start_x, start_y; /* Initial position */
86  gint               cur_x, cur_y;     /* Current Position */
87
88  GList             *selections;  /* selections we've claimed */
89 
90  GtkDragDestInfo   *proxy_dest;  /* Set if this is a proxy drag */
91
92  guint              drop_timeout;     /* Timeout for aborting drop */
93  guint              destroy_icon : 1; /* If true, destroy icon_window
94                                        */
95};
96
97struct _GtkDragDestSite
98{
99  GtkDestDefaults    flags;
100  GtkTargetList     *target_list;
101  GdkDragAction      actions;
102  GdkWindow         *proxy_window;
103  GdkDragProtocol    proxy_protocol;
104  gboolean           do_proxy : 1;
105  gboolean           proxy_coords : 1;
106  gboolean           have_drag : 1;
107};
108 
109struct _GtkDragDestInfo
110{
111  GtkWidget         *widget;       /* Widget in which drag is in */
112  GdkDragContext    *context;      /* Drag context */
113  GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
114  GtkSelectionData  *proxy_data;   /* Set while retrieving proxied data */
115  gboolean           dropped : 1;     /* Set after we receive a drop */
116  guint32            proxy_drop_time; /* Timestamp for proxied drop */
117  gboolean           proxy_drop_wait : 1; /* Set if we are waiting for a
118                                           * status reply before sending
119                                           * a proxied drop on.
120                                           */
121  gint               drop_x, drop_y; /* Position of drop */
122};
123
124#define DROP_ABORT_TIME 300000
125
126#define ANIM_STEP_TIME 50
127#define ANIM_STEP_LENGTH 50
128#define ANIM_MIN_STEPS 5
129#define ANIM_MAX_STEPS 10
130
131struct _GtkDragAnim
132{
133  GtkDragSourceInfo *info;
134  gint step;
135  gint n_steps;
136};
137
138struct _GtkDragFindData
139{
140  gint x;
141  gint y;
142  GdkDragContext *context;
143  GtkDragDestInfo *info;
144  gboolean found;
145  gboolean toplevel;
146  gboolean (*callback) (GtkWidget *widget, GdkDragContext *context,
147                        gint x, gint y, guint32 time);
148  guint32 time;
149};
150
151/* Enumeration for some targets we handle internally */
152
153enum {
154  TARGET_MOTIF_SUCCESS = 0x40000000,
155  TARGET_MOTIF_FAILURE,
156  TARGET_DELETE
157};
158
159/* Drag icons */
160
161static GdkPixmap   *default_icon_pixmap = NULL;
162static GdkPixmap   *default_icon_mask = NULL;
163static GdkColormap *default_icon_colormap = NULL;
164static gint         default_icon_hot_x;
165static gint         default_icon_hot_y;
166
167/* Forward declarations */
168static void          gtk_drag_get_event_actions (GdkEvent        *event,
169                                                 gint             button,
170                                                 GdkDragAction    actions,
171                                                 GdkDragAction   *suggested_action,
172                                                 GdkDragAction   *possible_actions);
173static GdkCursor *   gtk_drag_get_cursor         (GdkDragAction action);
174static GtkWidget    *gtk_drag_get_ipc_widget     (void);
175static void          gtk_drag_release_ipc_widget (GtkWidget      *widget);
176
177static void          gtk_drag_highlight_paint    (GtkWidget      *widget);
178static gboolean      gtk_drag_highlight_expose   (GtkWidget      *widget,
179                                                  GdkEventExpose *event,
180                                                  gpointer        data);
181
182
183static GdkAtom   gtk_drag_dest_find_target    (GtkWidget          *widget,
184                                               GtkDragDestSite    *site,
185                                               GdkDragContext     *context);
186static void      gtk_drag_selection_received  (GtkWidget          *widget,
187                                               GtkSelectionData   *selection_data,
188                                               guint32             time,
189                                               gpointer            data);
190static void      gtk_drag_find_widget         (GtkWidget          *widget,
191                                               GtkDragFindData    *data);
192static void      gtk_drag_proxy_begin         (GtkWidget          *widget,
193                                               GtkDragDestInfo    *dest_info);
194static void      gtk_drag_dest_info_destroy   (gpointer            data);
195static void      gtk_drag_dest_realized       (GtkWidget          *widget);
196static void      gtk_drag_dest_site_destroy   (gpointer            data);
197static void      gtk_drag_dest_leave          (GtkWidget          *widget,
198                                               GdkDragContext     *context,
199                                               guint               time);
200static gboolean  gtk_drag_dest_motion         (GtkWidget          *widget,
201                                               GdkDragContext     *context,
202                                               gint                x,
203                                               gint                y,
204                                               guint               time);
205static gboolean  gtk_drag_dest_drop           (GtkWidget          *widget,
206                                               GdkDragContext     *context,
207                                               gint                x,
208                                               gint                y,
209                                               guint               time);
210
211static void gtk_drag_source_check_selection    (GtkDragSourceInfo *info,
212                                                GdkAtom            selection,
213                                                guint32            time);
214static void gtk_drag_source_release_selections (GtkDragSourceInfo *info,
215                                                guint32            time);
216static void gtk_drag_drop                      (GtkDragSourceInfo *info,
217                                                guint32            time);
218static void gtk_drag_drop_finished             (GtkDragSourceInfo *info,
219                                                gboolean           success,
220                                                guint              time);
221
222static gint gtk_drag_source_event_cb           (GtkWidget         *widget,
223                                                GdkEvent          *event,
224                                                gpointer           data);
225static void gtk_drag_source_site_destroy       (gpointer           data);
226static void gtk_drag_selection_get             (GtkWidget         *widget,
227                                                GtkSelectionData  *selection_data,
228                                                guint              sel_info,
229                                                guint32            time,
230                                                gpointer           data);
231static gint gtk_drag_anim_timeout              (gpointer           data);
232static void gtk_drag_remove_icon               (GtkDragSourceInfo *info);
233static void gtk_drag_source_info_destroy       (gpointer           data);
234static void gtk_drag_update                    (GtkDragSourceInfo *info,
235                                                gint               x_root,
236                                                gint               y_root,
237                                                GdkEvent          *event);
238static gint gtk_drag_motion_cb                 (GtkWidget         *widget,
239                                                GdkEventMotion    *event,
240                                                gpointer           data);
241static gint gtk_drag_key_cb                    (GtkWidget         *widget,
242                                                GdkEventKey       *event,
243                                                gpointer           data);
244static gint gtk_drag_button_release_cb         (GtkWidget         *widget,
245                                                GdkEventButton    *event,
246                                                gpointer           data);
247static gint gtk_drag_abort_timeout             (gpointer           data);
248
249/************************
250 * Cursor and Icon data *
251 ************************/
252
253#define action_ask_width 16
254#define action_ask_height 16
255static const guchar action_ask_bits[] = {
256  0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7,
257  0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb,
258  0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, };
259
260#define action_ask_mask_width 16
261#define action_ask_mask_height 16
262static const guchar action_ask_mask_bits[] = {
263  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f,
264  0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07,
265  0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, };
266
267#define action_copy_width 16
268#define action_copy_height 16
269static const guchar action_copy_bits[] = {
270  0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb,
271  0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb,
272  0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, };
273
274#define action_copy_mask_width 16
275#define action_copy_mask_height 16
276static const guchar action_copy_mask_bits[] = {
277  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07,
278  0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07,
279  0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, };
280
281#define action_move_width 16
282#define action_move_height 16
283static const guchar action_move_bits[] = {
284  0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff,
285  0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb,
286  0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, };
287
288#define action_move_mask_width 16
289#define action_move_mask_height 16
290static const guchar action_move_mask_bits[] = {
291  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00,
292  0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07,
293  0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, };
294
295#define action_link_width 16
296#define action_link_height 16
297static const guchar action_link_bits[] = {
298  0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7,
299  0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8,
300  0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, };
301
302#define action_link_mask_width 16
303#define action_link_mask_height 16
304static const guchar action_link_mask_bits[] = {
305  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f,
306  0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77,
307  0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, };
308
309#define action_none_width 16
310#define action_none_height 16
311static const guchar action_none_bits[] = {
312  0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff,
313  0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff,
314  0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, };
315
316#define action_none_mask_width 16
317#define action_none_mask_height 16
318static const guchar action_none_mask_bits[] = {
319  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00,
320  0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00,
321  0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, };
322
323#define CURSOR_WIDTH 16
324#define CURSOR_HEIGHT 16
325
326static struct {
327  GdkDragAction action;
328  const char *bits;
329  const char *mask;
330  GdkCursor    *cursor;
331} drag_cursors[] = {
332  { GDK_ACTION_DEFAULT, 0 },
333  { GDK_ACTION_ASK,   action_ask_bits,  action_ask_mask_bits,  NULL },
334  { GDK_ACTION_COPY,  action_copy_bits, action_copy_mask_bits, NULL },
335  { GDK_ACTION_MOVE,  action_move_bits, action_move_mask_bits, NULL },
336  { GDK_ACTION_LINK,  action_link_bits, action_link_mask_bits, NULL },
337  { 0              ,  action_none_bits, action_none_mask_bits, NULL },
338};
339
340static const gint n_drag_cursors = sizeof (drag_cursors) / sizeof (drag_cursors[0]);
341
342/* XPM */
343static const char *drag_default_xpm[] = {
344"32 32 3 1",
345"       c None",
346".      c #000000",
347"+      c #FFFFFF",
348"                                ",
349"                                ",
350"                ..              ",
351"              ..+.              ",
352"             ..++..             ",
353"           ...++++.             ",
354"         ...++++++..            ",
355"       ...+++++++++.            ",
356"     ...+++++++++++..           ",
357"    ..+.++++++++++++..          ",
358"     .++.++++++++++++..         ",
359"     .+++.++++++++++++..        ",
360"     .++++.++++++++++++.        ",
361"     .+++.+++++++++++++..       ",
362"     .++.+++++++++++++++..      ",
363"     .+.+++++++++++++++++..     ",
364"     ..+++++++++++++++++++..    ",
365"     ..++++++++++++++++++++.    ",
366"     .++++++++++++++++++++..    ",
367"     ..+++++++++++++++++..      ",
368"      .++++++++++++++++..       ",
369"      ..+++++++++++++...        ",
370"       .++++++++++++..          ",
371"       ..+++++++++..            ",
372"        .++++++++..             ",
373"        ..++++++..              ",
374"         .+++++..               ",
375"          .++..                 ",
376"           ...                  ",
377"           ..                   ",
378"                                ",
379"                                "};
380
381/*********************
382 * Utility functions *
383 *********************/
384
385/*************************************************************
386 * gtk_drag_get_ipc_widget:
387 *     Return a invisible, off-screen, override-redirect
388 *     widget for IPC.
389 *   arguments:
390 *     
391 *   results:
392 *************************************************************/
393
394static GtkWidget *
395gtk_drag_get_ipc_widget (void)
396{
397  GtkWidget *result;
398 
399  if (drag_widgets)
400    {
401      GSList *tmp = drag_widgets;
402      result = drag_widgets->data;
403      drag_widgets = drag_widgets->next;
404      g_slist_free_1 (tmp);
405    }
406  else
407    {
408      result = gtk_invisible_new ();
409      gtk_widget_show (result);
410    }
411
412  return result;
413}
414
415/***************************************************************
416 * gtk_drag_release_ipc_widget:
417 *     Releases widget retrieved with gtk_drag_get_ipc_widget ()
418 *   arguments:
419 *     widget: the widget to release.
420 *   results:
421 ***************************************************************/
422
423static void
424gtk_drag_release_ipc_widget (GtkWidget *widget)
425{
426  drag_widgets = g_slist_prepend (drag_widgets, widget);
427}
428
429static guint32
430gtk_drag_get_event_time (GdkEvent *event)
431{
432  guint32 tm = GDK_CURRENT_TIME;
433 
434  if (event)
435    switch (event->type)
436      {
437      case GDK_MOTION_NOTIFY:
438        tm = event->motion.time; break;
439      case GDK_BUTTON_PRESS:
440      case GDK_2BUTTON_PRESS:
441      case GDK_3BUTTON_PRESS:
442      case GDK_BUTTON_RELEASE:
443        tm = event->button.time; break;
444      case GDK_KEY_PRESS:
445      case GDK_KEY_RELEASE:
446        tm = event->key.time; break;
447      case GDK_ENTER_NOTIFY:
448      case GDK_LEAVE_NOTIFY:
449        tm = event->crossing.time; break;
450      case GDK_PROPERTY_NOTIFY:
451        tm = event->property.time; break;
452      case GDK_SELECTION_CLEAR:
453      case GDK_SELECTION_REQUEST:
454      case GDK_SELECTION_NOTIFY:
455        tm = event->selection.time; break;
456      case GDK_PROXIMITY_IN:
457      case GDK_PROXIMITY_OUT:
458        tm = event->proximity.time; break;
459      default:                  /* use current time */
460        break;
461      }
462 
463  return tm;
464}
465
466static void
467gtk_drag_get_event_actions (GdkEvent *event,
468                            gint button,
469                            GdkDragAction  actions,
470                            GdkDragAction *suggested_action,
471                            GdkDragAction *possible_actions)
472{
473  *suggested_action = 0;
474  *possible_actions = 0;
475
476  if (event)
477    {
478      GdkModifierType state = 0;
479     
480      switch (event->type)
481        {
482        case GDK_MOTION_NOTIFY:
483          state = event->motion.state;
484          break;
485        case GDK_BUTTON_PRESS:
486        case GDK_2BUTTON_PRESS:
487        case GDK_3BUTTON_PRESS:
488        case GDK_BUTTON_RELEASE:
489          state = event->button.state;
490          break;
491        case GDK_KEY_PRESS:
492        case GDK_KEY_RELEASE:
493          state = event->key.state;
494          break;
495        case GDK_ENTER_NOTIFY:
496        case GDK_LEAVE_NOTIFY:
497          state = event->crossing.state;
498          break;
499        default:
500          break;
501        }
502     
503      if ((button == 2 || button == 3) && (actions & GDK_ACTION_ASK))
504        {
505          *suggested_action = GDK_ACTION_ASK;
506          *possible_actions = actions;
507        }
508      else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
509        {
510          if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
511            {
512              if (actions & GDK_ACTION_LINK)
513                {
514                  *suggested_action = GDK_ACTION_LINK;
515                  *possible_actions = GDK_ACTION_LINK;
516                }
517            }
518          else if (state & GDK_CONTROL_MASK)
519            {
520              if (actions & GDK_ACTION_COPY)
521                {
522                  *suggested_action = GDK_ACTION_COPY;
523                  *possible_actions = GDK_ACTION_COPY;
524                }
525              return;
526            }
527          else
528            {
529              if (actions & GDK_ACTION_MOVE)
530                {
531                  *suggested_action = GDK_ACTION_MOVE;
532                  *possible_actions = GDK_ACTION_MOVE;
533                }
534              return;
535            }
536        }
537      else
538        {
539          *possible_actions = actions;
540
541          if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
542            *suggested_action = GDK_ACTION_ASK;
543          else if (actions & GDK_ACTION_COPY)
544            *suggested_action =  GDK_ACTION_COPY;
545          else if (actions & GDK_ACTION_MOVE)
546            *suggested_action = GDK_ACTION_MOVE;
547          else if (actions & GDK_ACTION_LINK)
548            *suggested_action = GDK_ACTION_LINK;
549        }
550    }
551  else
552    {
553      *possible_actions = actions;
554     
555      if (actions & GDK_ACTION_COPY)
556        *suggested_action =  GDK_ACTION_COPY;
557      else if (actions & GDK_ACTION_MOVE)
558        *suggested_action = GDK_ACTION_MOVE;
559      else if (actions & GDK_ACTION_LINK)
560        *suggested_action = GDK_ACTION_LINK;
561    }
562 
563  return;
564}
565
566static GdkCursor *
567gtk_drag_get_cursor (GdkDragAction action)
568{
569  gint i;
570 
571  for (i = 0 ; i < n_drag_cursors - 1; i++)
572    if (drag_cursors[i].action == action)
573      break;
574
575  if (drag_cursors[i].cursor == NULL)
576    {
577      GdkColor fg, bg;
578
579      GdkPixmap *pixmap =
580        gdk_bitmap_create_from_data (NULL,
581                                     drag_cursors[i].bits,
582                                     CURSOR_WIDTH, CURSOR_HEIGHT);
583      GdkPixmap *mask =
584        gdk_bitmap_create_from_data (NULL,
585                                     drag_cursors[i].mask,
586                                     CURSOR_WIDTH, CURSOR_HEIGHT);
587
588      gdk_color_white (gdk_colormap_get_system (), &bg);
589      gdk_color_black (gdk_colormap_get_system (), &fg);
590     
591      drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0);
592
593      gdk_pixmap_unref (pixmap);
594      gdk_pixmap_unref (mask);
595    }
596
597  return drag_cursors[i].cursor;
598}
599
600/********************
601 * Destination side *
602 ********************/
603
604/*************************************************************
605 * gtk_drag_get_data:
606 *     Get the data for a drag or drop
607 *   arguments:
608 *     context - drag context
609 *     target  - format to retrieve the data in.
610 *     time    - timestamp of triggering event.
611 *     
612 *   results:
613 *************************************************************/
614
615void
616gtk_drag_get_data (GtkWidget      *widget,
617                   GdkDragContext *context,
618                   GdkAtom         target,
619                   guint32         time)
620{
621  GtkWidget *selection_widget;
622
623  g_return_if_fail (widget != NULL);
624  g_return_if_fail (context != NULL);
625
626  selection_widget = gtk_drag_get_ipc_widget ();
627
628  gdk_drag_context_ref (context);
629  gtk_widget_ref (widget);
630 
631  gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
632                      GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget);
633
634  gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
635
636  gtk_selection_convert (selection_widget,
637                         gdk_drag_get_selection (context),
638                         target,
639                         time);
640}
641
642
643/*************************************************************
644 * gtk_drag_get_source_widget:
645 *     Get the widget the was the source of this drag, if
646 *     the drag originated from this application.
647 *   arguments:
648 *     context: The drag context for this drag
649 *   results:
650 *     The source widget, or NULL if the drag originated from
651 *     a different application.
652 *************************************************************/
653
654GtkWidget *
655gtk_drag_get_source_widget (GdkDragContext *context)
656{
657  GSList *tmp_list;
658
659  tmp_list = source_widgets;
660  while (tmp_list)
661    {
662      GtkWidget *ipc_widget = tmp_list->data;
663     
664      if (ipc_widget->window == context->source_window)
665        {
666          GtkDragSourceInfo *info;
667          info = gtk_object_get_data (GTK_OBJECT (ipc_widget), "gtk-info");
668
669          return info ? info->widget : NULL;
670        }
671
672      tmp_list = tmp_list->next;
673    }
674
675  return NULL;
676}
677
678/*************************************************************
679 * gtk_drag_finish:
680 *     Notify the drag source that the transfer of data
681 *     is complete.
682 *   arguments:
683 *     context: The drag context for this drag
684 *     success: Was the data successfully transferred?
685 *     time:    The timestamp to use when notifying the destination.
686 *   results:
687 *************************************************************/
688
689void
690gtk_drag_finish (GdkDragContext *context,
691                 gboolean        success,
692                 gboolean        del,
693                 guint32         time)
694{
695  GdkAtom target = GDK_NONE;
696
697  g_return_if_fail (context != NULL);
698
699  if (success && del)
700    {
701      target = gdk_atom_intern ("DELETE", FALSE);
702    }
703  else if (context->protocol == GDK_DRAG_PROTO_MOTIF)
704    {
705      target = gdk_atom_intern (success ?
706                                  "XmTRANSFER_SUCCESS" :
707                                  "XmTRANSFER_FAILURE",
708                                FALSE);
709    }
710
711  if (target != GDK_NONE)
712    {
713      GtkWidget *selection_widget = gtk_drag_get_ipc_widget ();
714
715      gdk_drag_context_ref (context);
716     
717      gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context);
718      gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received",
719                          GTK_SIGNAL_FUNC (gtk_drag_selection_received),
720                          NULL);
721     
722      gtk_selection_convert (selection_widget,
723                             gdk_drag_get_selection (context),
724                             target,
725                             time);
726    }
727 
728  if (!del)
729    gdk_drop_finish (context, success, time);
730}
731
732/*************************************************************
733 * gtk_drag_highlight_paint:
734 *     Paint a highlight indicating drag status onto the widget.
735 *   arguments:
736 *     widget:
737 *   results:
738 *************************************************************/
739
740static void
741gtk_drag_highlight_paint (GtkWidget  *widget)
742{
743  gint x, y, width, height;
744
745  g_return_if_fail (widget != NULL);
746
747  if (GTK_WIDGET_DRAWABLE (widget))
748    {
749      if (GTK_WIDGET_NO_WINDOW (widget))
750        {
751          x = widget->allocation.x;
752          y = widget->allocation.y;
753          width = widget->allocation.width;
754          height = widget->allocation.height;
755        }
756      else
757        {
758          x = 0;
759          y = 0;
760          gdk_window_get_size (widget->window, &width, &height);
761        }
762     
763      gtk_draw_shadow (widget->style, widget->window,
764                       GTK_STATE_NORMAL, GTK_SHADOW_OUT,
765                       x, y, width, height);
766     
767      gdk_draw_rectangle (widget->window,
768                          widget->style->black_gc,
769                          FALSE,
770                          x, y, width - 1, height - 1);
771    }
772}
773
774/*************************************************************
775 * gtk_drag_highlight_expose:
776 *     Callback for expose_event for highlighted widgets.
777 *   arguments:
778 *     widget:
779 *     event:
780 *     data:
781 *   results:
782 *************************************************************/
783
784static gboolean
785gtk_drag_highlight_expose (GtkWidget      *widget,
786                           GdkEventExpose *event,
787                           gpointer        data)
788{
789  gtk_drag_highlight_paint (widget);
790  return TRUE;
791}
792
793/*************************************************************
794 * gtk_drag_highlight:
795 *     Highlight the given widget in the default manner.
796 *   arguments:
797 *     widget:
798 *   results:
799 *************************************************************/
800
801void
802gtk_drag_highlight (GtkWidget  *widget)
803{
804  gtk_signal_connect_after (GTK_OBJECT (widget), "draw",
805                            GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
806                            NULL);
807  gtk_signal_connect (GTK_OBJECT (widget), "expose_event",
808                      GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
809                      NULL);
810
811  gtk_widget_queue_draw (widget);
812}
813
814/*************************************************************
815 * gtk_drag_unhighlight:
816 *     Refresh the given widget to remove the highlight.
817 *   arguments:
818 *     widget:
819 *   results:
820 *************************************************************/
821
822void
823gtk_drag_unhighlight (GtkWidget *widget)
824{
825  g_return_if_fail (widget != NULL);
826
827  gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
828                                 GTK_SIGNAL_FUNC (gtk_drag_highlight_paint),
829                                 NULL);
830  gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
831                                 GTK_SIGNAL_FUNC (gtk_drag_highlight_expose),
832                                 NULL);
833 
834  gtk_widget_queue_clear (widget);
835}
836
837/*************************************************************
838 * gtk_drag_dest_set:
839 *     Register a drop site, and possibly add default behaviors.
840 *   arguments:
841 *     widget:   
842 *     flags:     Which types of default drag behavior to use
843 *     targets:   Table of targets that can be accepted
844 *     n_targets: Number of of entries in targets
845 *     actions:   
846 *   results:
847 *************************************************************/
848
849void
850gtk_drag_dest_set   (GtkWidget            *widget,
851                     GtkDestDefaults       flags,
852                     const GtkTargetEntry *targets,
853                     gint                  n_targets,
854                     GdkDragAction         actions)
855{
856  GtkDragDestSite *site;
857 
858  g_return_if_fail (widget != NULL);
859
860  /* HACK, do this in the destroy */
861  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
862  if (site)
863    gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
864
865  if (GTK_WIDGET_REALIZED (widget))
866    gtk_drag_dest_realized (widget);
867
868  gtk_signal_connect (GTK_OBJECT (widget), "realize",
869                      GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
870
871  site = g_new (GtkDragDestSite, 1);
872
873  site->flags = flags;
874  site->have_drag = FALSE;
875  if (targets)
876    site->target_list = gtk_target_list_new (targets, n_targets);
877  else
878    site->target_list = NULL;
879
880  site->actions = actions;
881  site->do_proxy = FALSE;
882
883  gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
884                            site, gtk_drag_dest_site_destroy);
885}
886
887/*************************************************************
888 * gtk_drag_dest_set_proxy:
889 *     Set up this widget to proxy drags elsewhere.
890 *   arguments:
891 *     widget:         
892 *     proxy_window:    window to which forward drag events
893 *     protocol:        Drag protocol which the dest widget accepts
894 *     use_coordinates: If true, send the same coordinates to the
895 *                      destination, because it is a embedded
896 *                      subwindow.
897 *   results:
898 *************************************************************/
899
900void
901gtk_drag_dest_set_proxy (GtkWidget      *widget,
902                         GdkWindow      *proxy_window,
903                         GdkDragProtocol protocol,
904                         gboolean        use_coordinates)
905{
906  GtkDragDestSite *site;
907 
908  g_return_if_fail (widget != NULL);
909
910  /* HACK, do this in the destroy */
911  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
912  if (site)
913    gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
914
915  if (GTK_WIDGET_REALIZED (widget))
916    gtk_drag_dest_realized (widget);
917
918  gtk_signal_connect (GTK_OBJECT (widget), "realize",
919                      GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL);
920
921  site = g_new (GtkDragDestSite, 1);
922
923  site->flags = 0;
924  site->have_drag = FALSE;
925  site->target_list = NULL;
926  site->actions = 0;
927  site->proxy_window = proxy_window;
928  if (proxy_window)
929    gdk_window_ref (proxy_window);
930  site->do_proxy = TRUE;
931  site->proxy_protocol = protocol;
932  site->proxy_coords = use_coordinates;
933
934  gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest",
935                            site, gtk_drag_dest_site_destroy);
936}
937
938/*************************************************************
939 * gtk_drag_dest_unset
940 *     Unregister this widget as a drag target.
941 *   arguments:
942 *     widget:
943 *   results:
944 *************************************************************/
945
946void
947gtk_drag_dest_unset (GtkWidget *widget)
948{
949  g_return_if_fail (widget != NULL);
950
951  gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL);
952}
953
954/*************************************************************
955 * gtk_drag_dest_handle_event:
956 *     Called from widget event handling code on Drag events
957 *     for destinations.
958 *
959 *   arguments:
960 *     toplevel: Toplevel widget that received the event
961 *     event:
962 *   results:
963 *************************************************************/
964
965void
966gtk_drag_dest_handle_event (GtkWidget *toplevel,
967                            GdkEvent  *event)
968{
969  GtkDragDestInfo *info;
970  GdkDragContext *context;
971
972  g_return_if_fail (toplevel != NULL);
973  g_return_if_fail (event != NULL);
974
975  context = event->dnd.context;
976
977  info = g_dataset_get_data (context, "gtk-info");
978  if (!info)
979    {
980      info = g_new (GtkDragDestInfo, 1);
981      info->widget = NULL;
982      info->context = event->dnd.context;
983      info->proxy_source = NULL;
984      info->proxy_data = NULL;
985      info->dropped = FALSE;
986      info->proxy_drop_wait = FALSE;
987      g_dataset_set_data_full (context,
988                               "gtk-info",
989                               info,
990                               gtk_drag_dest_info_destroy);
991    }
992
993  /* Find the widget for the event */
994  switch (event->type)
995    {
996    case GDK_DRAG_ENTER:
997      break;
998     
999    case GDK_DRAG_LEAVE:
1000      if (info->widget)
1001        {
1002          gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1003          info->widget = NULL;
1004        }
1005      break;
1006     
1007    case GDK_DRAG_MOTION:
1008    case GDK_DROP_START:
1009      {
1010        GtkDragFindData data;
1011        gint tx, ty;
1012
1013        if (event->type == GDK_DROP_START)
1014          {
1015            info->dropped = TRUE;
1016            /* We send a leave here so that the widget unhighlights
1017             * properly.
1018             */
1019            if (info->widget)
1020              {
1021                gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1022                info->widget = NULL;
1023              }
1024          }
1025
1026        gdk_window_get_origin (toplevel->window, &tx, &ty);
1027
1028        data.x = event->dnd.x_root - tx;
1029        data.y = event->dnd.y_root - ty;
1030        data.context = context;
1031        data.info = info;
1032        data.found = FALSE;
1033        data.toplevel = TRUE;
1034        data.callback = (event->type == GDK_DRAG_MOTION) ?
1035          gtk_drag_dest_motion : gtk_drag_dest_drop;
1036        data.time = event->dnd.time;
1037       
1038        gtk_drag_find_widget (toplevel, &data);
1039
1040        if (info->widget && !data.found)
1041          {
1042            gtk_drag_dest_leave (info->widget, context, event->dnd.time);
1043            info->widget = NULL;
1044          }
1045       
1046        /* Send a reply.
1047         */
1048        if (event->type == GDK_DRAG_MOTION)
1049          {
1050            if (!data.found)
1051              gdk_drag_status (context, 0, event->dnd.time);
1052          }
1053        else if (event->type == GDK_DROP_START && !info->proxy_source)
1054          {
1055            gdk_drop_reply (context, data.found, event->dnd.time);
1056            if ((context->protocol == GDK_DRAG_PROTO_MOTIF) && !data.found)
1057              gtk_drag_finish (context, FALSE, FALSE, event->dnd.time);
1058          }
1059      }
1060      break;
1061
1062    default:
1063      g_assert_not_reached ();
1064    }
1065}
1066
1067/*************************************************************
1068 * gtk_drag_dest_find_target:
1069 *     Decide on a target for the drag.
1070 *   arguments:
1071 *     site:
1072 *     context:
1073 *   results:
1074 *************************************************************/
1075
1076static GdkAtom
1077gtk_drag_dest_find_target (GtkWidget       *widget,
1078                           GtkDragDestSite *site,
1079                           GdkDragContext  *context)
1080{
1081  GList *tmp_target;
1082  GList *tmp_source = NULL;
1083  GtkWidget *source_widget = gtk_drag_get_source_widget (context);
1084
1085  tmp_target = site->target_list->list;
1086  while (tmp_target)
1087    {
1088      GtkTargetPair *pair = tmp_target->data;
1089      tmp_source = context->targets;
1090      while (tmp_source)
1091        {
1092          if (tmp_source->data == GUINT_TO_POINTER (pair->target))
1093            {
1094              if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
1095                  (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
1096                return pair->target;
1097              else
1098                break;
1099            }
1100          tmp_source = tmp_source->next;
1101        }
1102      tmp_target = tmp_target->next;
1103    }
1104
1105  return GDK_NONE;
1106}
1107
1108static void
1109gtk_drag_selection_received (GtkWidget        *widget,
1110                             GtkSelectionData *selection_data,
1111                             guint32           time,
1112                             gpointer          data)
1113{
1114  GdkDragContext *context;
1115  GtkDragDestInfo *info;
1116  GtkWidget *drop_widget;
1117
1118  drop_widget = data;
1119
1120  context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context");
1121  info = g_dataset_get_data (context, "gtk-info");
1122
1123  if (info->proxy_data &&
1124      info->proxy_data->target == selection_data->target)
1125    {
1126      gtk_selection_data_set (info->proxy_data,
1127                              selection_data->type,
1128                              selection_data->format,
1129                              selection_data->data,
1130                              selection_data->length);
1131      gtk_main_quit ();
1132      return;
1133    }
1134
1135  if (selection_data->target == gdk_atom_intern ("DELETE", FALSE))
1136    {
1137      gtk_drag_finish (context, TRUE, FALSE, time);
1138    }
1139  else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) ||
1140           (selection_data->target == gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE)))
1141    {
1142      /* Do nothing */
1143    }
1144  else
1145    {
1146      GtkDragDestSite *site;
1147
1148      site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest");
1149
1150      if (site->target_list)
1151        {
1152          guint target_info;
1153
1154          if (gtk_target_list_find (site->target_list,
1155                                    selection_data->target,
1156                                    &target_info))
1157            {
1158              if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
1159                  selection_data->length >= 0)
1160                gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1161                                         "drag_data_received",
1162                                         context, info->drop_x, info->drop_y,
1163                                         selection_data,
1164                                         target_info, time);
1165            }
1166        }
1167      else
1168        {
1169          gtk_signal_emit_by_name (GTK_OBJECT (drop_widget),
1170                                   "drag_data_received",
1171                                   context, info->drop_x, info->drop_y,
1172                                   selection_data, 0, time);
1173        }
1174     
1175      if (site->flags & GTK_DEST_DEFAULT_DROP)
1176        {
1177
1178          gtk_drag_finish (context,
1179                           (selection_data->length >= 0),
1180                           (context->action == GDK_ACTION_MOVE),
1181                           time);
1182        }
1183     
1184      gtk_widget_unref (drop_widget);
1185    }
1186
1187  gtk_signal_disconnect_by_func (GTK_OBJECT (widget),
1188                                 GTK_SIGNAL_FUNC (gtk_drag_selection_received),
1189                                 data);
1190 
1191  gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL);
1192  gdk_drag_context_unref (context);
1193
1194  gtk_drag_release_ipc_widget (widget);
1195}
1196
[17070]1197static void
1198get_all_children_callback (GtkWidget *widget,
1199                           gpointer   client_data)
1200{
1201  GList **children;
1202
1203  children = (GList**) client_data;
1204  gtk_widget_ref (widget);
1205 
1206  *children = g_list_prepend (*children, widget);
1207}
1208
1209static GList*
1210get_all_children (GtkContainer *container)
1211{
1212  GList *children;
1213
1214  children = NULL;
1215
1216  gtk_container_forall (container,
1217                        get_all_children_callback,
1218                        &children);
1219
1220  return g_list_reverse (children);
1221}
1222
1223
1224
[14481]1225/*************************************************************
1226 * gtk_drag_find_widget:
1227 *     Recursive callback used to locate widgets for
1228 *     DRAG_MOTION and DROP_START events.
1229 *   arguments:
1230 *     
1231 *   results:
1232 *************************************************************/
1233
1234static void
1235gtk_drag_find_widget (GtkWidget       *widget,
1236                      GtkDragFindData *data)
1237{
1238  GtkAllocation new_allocation;
1239  gint x_offset = 0;
1240  gint y_offset = 0;
1241
1242  new_allocation = widget->allocation;
1243
1244  if (data->found || !GTK_WIDGET_MAPPED (widget))
1245    return;
1246
1247  /* Note that in the following code, we only count the
1248   * position as being inside a WINDOW widget if it is inside
1249   * widget->window; points that are outside of widget->window
1250   * but within the allocation are not counted. This is consistent
1251   * with the way we highlight drag targets.
1252   */
1253  if (!GTK_WIDGET_NO_WINDOW (widget))
1254    {
1255      new_allocation.x = 0;
1256      new_allocation.y = 0;
1257    }
1258 
1259  if (widget->parent)
1260    {
1261      GdkWindow *window = widget->window;
1262      while (window != widget->parent->window)
1263        {
1264          gint tx, ty, twidth, theight;
1265          gdk_window_get_size (window, &twidth, &theight);
1266
1267          if (new_allocation.x < 0)
1268            {
1269              new_allocation.width += new_allocation.x;
1270              new_allocation.x = 0;
1271            }
1272          if (new_allocation.y < 0)
1273            {
1274              new_allocation.height += new_allocation.y;
1275              new_allocation.y = 0;
1276            }
1277          if (new_allocation.x + new_allocation.width > twidth)
1278            new_allocation.width = twidth - new_allocation.x;
1279          if (new_allocation.y + new_allocation.height > theight)
1280            new_allocation.height = theight - new_allocation.y;
1281
1282          gdk_window_get_position (window, &tx, &ty);
1283          new_allocation.x += tx;
1284          x_offset += tx;
1285          new_allocation.y += ty;
1286          y_offset += ty;
1287         
1288          window = gdk_window_get_parent (window);
1289        }
1290    }
1291
1292  if (data->toplevel ||
1293      ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
1294       (data->x < new_allocation.x + new_allocation.width) &&
1295       (data->y < new_allocation.y + new_allocation.height)))
1296    {
1297      /* First, check if the drag is in a valid drop site in
1298       * one of our children
1299       */
1300      if (GTK_IS_CONTAINER (widget))
1301        {
1302          GtkDragFindData new_data = *data;
[17070]1303          GList *children = get_all_children (GTK_CONTAINER (widget));
[15780]1304          GList *tmp_list;
1305
[14481]1306          new_data.x -= x_offset;
1307          new_data.y -= y_offset;
1308          new_data.found = FALSE;
1309          new_data.toplevel = FALSE;
[15780]1310
1311          tmp_list = children;
1312          while (tmp_list)
1313            {
1314              GtkWidget *child = tmp_list->data;
1315
1316              if (child->parent == widget)
1317                gtk_drag_find_widget (child, &new_data);
1318             
1319              gtk_widget_unref (child);
1320              tmp_list = tmp_list->next;
1321            }
1322
1323          g_list_free (children);
1324
[14481]1325          data->found = new_data.found;
1326        }
1327
1328      /* If not, and this widget is registered as a drop site, check to
1329       * emit "drag_motion" to check if we are actually in
1330       * a drop site.
1331       */
1332      if (!data->found &&
1333          gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"))
1334        {
1335          data->found = data->callback (widget,
1336                                        data->context,
[15780]1337                                        data->x - x_offset,
1338                                        data->y - y_offset,
[14481]1339                                        data->time);
1340          /* If so, send a "drag_leave" to the last widget */
1341          if (data->found)
1342            {
1343              if (data->info->widget && data->info->widget != widget)
1344                {
1345                  gtk_drag_dest_leave (data->info->widget, data->context, data->time);
1346                }
1347              data->info->widget = widget;
1348            }
1349        }
1350    }
1351}
1352
1353static void
1354gtk_drag_proxy_begin (GtkWidget       *widget,
1355                      GtkDragDestInfo *dest_info)
1356{
1357  GtkDragSourceInfo *source_info;
1358  GList *tmp_list;
1359 
1360  source_info = g_new0 (GtkDragSourceInfo, 1);
1361  source_info->ipc_widget = gtk_drag_get_ipc_widget ();
1362 
1363  source_info->widget = widget;
1364  gtk_widget_ref (source_info->widget);
1365  source_info->context = gdk_drag_begin (source_info->ipc_widget->window,
1366                                         dest_info->context->targets);
1367
1368  source_info->target_list = gtk_target_list_new (NULL, 0);
1369  tmp_list = dest_info->context->targets;
1370  while (tmp_list)
1371    {
1372      gtk_target_list_add (source_info->target_list,
1373                           GPOINTER_TO_UINT (tmp_list->data), 0, 0);
1374      tmp_list = tmp_list->next;
1375    }
1376
1377  source_info->proxy_dest = dest_info;
1378 
1379  g_dataset_set_data (source_info->context, "gtk-info", source_info);
1380 
1381  gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget),
1382                      "selection_get",
1383                      GTK_SIGNAL_FUNC (gtk_drag_selection_get),
1384                      source_info);
1385 
1386  dest_info->proxy_source = source_info;
1387}
1388
1389static void
1390gtk_drag_dest_info_destroy (gpointer data)
1391{
1392  GtkDragDestInfo *info = data;
1393
1394  g_free (info);
1395}
1396
1397static void
1398gtk_drag_dest_realized (GtkWidget *widget)
1399{
1400  GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1401  gdk_window_register_dnd (toplevel->window);
1402}
1403
1404static void
1405gtk_drag_dest_site_destroy (gpointer data)
1406{
1407  GtkDragDestSite *site = data;
1408
1409  if (site->target_list)
1410    gtk_target_list_unref (site->target_list);
1411
1412  g_free (site);
1413}
1414
1415/*
1416 * Default drag handlers
1417 */
1418static void 
1419gtk_drag_dest_leave (GtkWidget      *widget,
1420                     GdkDragContext *context,
1421                     guint           time)
1422{
1423  GtkDragDestSite *site;
1424
1425  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1426  g_return_if_fail (site != NULL);
1427
1428  if (site->do_proxy)
1429    {
1430      GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1431
1432      if (info->proxy_source && !info->dropped)
1433        gdk_drag_abort (info->proxy_source->context, time);
1434     
1435      return;
1436    }
1437  else
1438    {
1439      if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
1440        gtk_drag_unhighlight (widget);
1441
1442      if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
1443        gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave",
1444                                 context, time);
1445     
1446      site->have_drag = FALSE;
1447    }
1448}
1449
1450static gboolean
1451gtk_drag_dest_motion (GtkWidget      *widget,
1452                      GdkDragContext *context,
1453                      gint            x,
1454                      gint            y,
1455                      guint           time)
1456{
1457  GtkDragDestSite *site;
1458  GdkDragAction action = 0;
1459  gboolean retval;
1460
1461  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1462  g_return_val_if_fail (site != NULL, FALSE);
1463
1464  if (site->do_proxy)
1465    {
1466      GdkAtom selection;
1467      GdkEvent *current_event;
1468      GdkWindow *dest_window;
1469      GdkDragProtocol proto;
1470       
1471      GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info");
1472
1473      if (!info->proxy_source)
1474        gtk_drag_proxy_begin (widget, info);
1475
1476      current_event = gtk_get_current_event ();
1477
1478      if (site->proxy_window)
1479        {
1480          dest_window = site->proxy_window;
1481          proto = site->proxy_protocol;
1482        }
1483      else
1484        {
1485          gdk_drag_find_window (info->proxy_source->context,
1486                                NULL,
1487                                current_event->dnd.x_root,
1488                                current_event->dnd.y_root,
1489                                &dest_window, &proto);
1490        }
1491     
1492      gdk_drag_motion (info->proxy_source->context,
1493                       dest_window, proto,
1494                       current_event->dnd.x_root,
1495                       current_event->dnd.y_root,
1496                       context->suggested_action,
1497                       context->actions, time);
1498
1499      if (!site->proxy_window && dest_window)
1500        gdk_window_unref (dest_window);
1501
1502      selection = gdk_drag_get_selection (info->proxy_source->context);
1503      if (selection &&
1504          selection != gdk_drag_get_selection (info->context))
1505        gtk_drag_source_check_selection (info->proxy_source, selection, time);
1506
1507      gdk_event_free (current_event);
1508     
1509      return TRUE;
1510    }
1511
1512  if (site->flags & GTK_DEST_DEFAULT_MOTION)
1513    {
1514      if (context->suggested_action & site->actions)
1515        action = context->suggested_action;
1516      else
1517        {
1518          gint i;
1519         
1520          for (i=0; i<8; i++)
1521            {
1522              if ((site->actions & (1 << i)) &&
1523                  (context->actions & (1 << i)))
1524                {
1525                  action = (1 << i);
1526                  break;
1527                }
1528            }
1529        }
1530     
1531      if (action && gtk_drag_dest_find_target (widget, site, context))
1532        {
1533          if (!site->have_drag)
1534            {
1535              site->have_drag = TRUE;
1536              if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
1537                gtk_drag_highlight (widget);
1538            }
1539         
1540          gdk_drag_status (context, action, time);
1541        }
1542      else
1543        {
1544          gdk_drag_status (context, 0, time);
1545          return TRUE;
1546        }
1547    }
1548
1549  gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion",
1550                           context, x, y, time, &retval);
1551
1552  return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
1553}
1554
1555static gboolean
1556gtk_drag_dest_drop (GtkWidget        *widget,
1557                    GdkDragContext   *context,
1558                    gint              x,
1559                    gint              y,
1560                    guint             time)
1561{
1562  GtkDragDestSite *site;
1563  GtkDragDestInfo *info;
1564
1565  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest");
1566  g_return_val_if_fail (site != NULL, FALSE);
1567
1568  info = g_dataset_get_data (context, "gtk-info");
1569  g_return_val_if_fail (info != NULL, FALSE);
1570
1571  info->drop_x = x;
1572  info->drop_y = y;
1573
1574  if (site->do_proxy)
1575    {
1576      if (info->proxy_source ||
1577          (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN))
1578        {
1579          gtk_drag_drop (info->proxy_source, time);
1580        }
1581      else
1582        {
1583          /* We need to synthesize a motion event, wait for a status,
1584           * and, if we get a good one, do a drop.
1585           */
1586         
1587          GdkEvent *current_event;
1588          GdkAtom selection;
1589          GdkWindow *dest_window;
1590          GdkDragProtocol proto;
1591         
1592          gtk_drag_proxy_begin (widget, info);
1593          info->proxy_drop_wait = TRUE;
1594          info->proxy_drop_time = time;
1595         
1596          current_event = gtk_get_current_event ();
1597
1598          if (site->proxy_window)
1599            {
1600              dest_window = site->proxy_window;
1601              proto = site->proxy_protocol;
1602            }
1603          else
1604            {
1605              gdk_drag_find_window (info->proxy_source->context,
1606                                    NULL,
1607                                    current_event->dnd.x_root,
1608                                    current_event->dnd.y_root,
1609                                    &dest_window, &proto);
1610            }
1611
1612          gdk_drag_motion (info->proxy_source->context,
1613                           dest_window, proto,
1614                           current_event->dnd.x_root,
1615                           current_event->dnd.y_root,
1616                           context->suggested_action,
1617                           context->actions, time);
1618
1619          if (!site->proxy_window && dest_window)
1620            gdk_window_unref (dest_window);
1621
1622          selection = gdk_drag_get_selection (info->proxy_source->context);
1623          if (selection &&
1624              selection != gdk_drag_get_selection (info->context))
1625            gtk_drag_source_check_selection (info->proxy_source, selection, time);
1626
1627          gdk_event_free (current_event);
1628     
1629        }
1630
1631      return TRUE;
1632    }
1633  else
1634    {
1635      gboolean retval;
1636
1637      if (site->flags & GTK_DEST_DEFAULT_DROP)
1638        {
1639          GdkAtom target = gtk_drag_dest_find_target (widget, site, context);
1640     
1641          if (target == GDK_NONE)
1642            return FALSE;
1643         
1644          gtk_drag_get_data (widget, context, target, time);
1645        }
1646
1647      gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop",
1648                               context, x, y, time, &retval);
1649
1650      return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
1651    }
1652}
1653
1654/***************
1655 * Source side *
1656 ***************/
1657
1658/*************************************************************
1659 * gtk_drag_begin: Start a drag operation
1660 *     
1661 *   arguments:
1662 *     widget:   Widget from which drag starts
1663 *     handlers: List of handlers to supply the data for the drag
1664 *     button:   Button user used to start drag
1665 *     time:     Time of event starting drag
1666 *
1667 *   results:
1668 *************************************************************/
1669
1670GdkDragContext *
1671gtk_drag_begin (GtkWidget         *widget,
1672                GtkTargetList     *target_list,
1673                GdkDragAction      actions,
1674                gint               button,
1675                GdkEvent          *event)
1676{
1677  GtkDragSourceInfo *info;
1678  GList *targets = NULL;
1679  GList *tmp_list;
1680  guint32 time = GDK_CURRENT_TIME;
1681  GdkDragAction possible_actions, suggested_action;
1682
1683  g_return_val_if_fail (widget != NULL, NULL);
1684  g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
1685  g_return_val_if_fail (target_list != NULL, NULL);
1686
1687  if (event)
1688    time = gdk_event_get_time (event);
1689
1690  info = g_new0 (GtkDragSourceInfo, 1);
1691  info->ipc_widget = gtk_drag_get_ipc_widget ();
1692  source_widgets = g_slist_prepend (source_widgets, info->ipc_widget);
1693
1694  gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", info);
1695
1696  tmp_list = g_list_last (target_list->list);
1697  while (tmp_list)
1698    {
1699      GtkTargetPair *pair = tmp_list->data;
1700      targets = g_list_prepend (targets,
1701                                GINT_TO_POINTER (pair->target));
1702      tmp_list = tmp_list->prev;
1703    }
1704
1705  info->widget = widget;
1706  gtk_widget_ref (info->widget);
1707 
1708  info->context = gdk_drag_begin (info->ipc_widget->window, targets);
1709  g_list_free (targets);
1710 
1711  g_dataset_set_data (info->context, "gtk-info", info);
1712
1713  info->button = button;
1714  info->target_list = target_list;
1715  gtk_target_list_ref (target_list);
1716
1717  info->possible_actions = actions;
1718
1719  info->cursor = NULL;
1720  info->status = GTK_DRAG_STATUS_DRAG;
1721  info->last_event = NULL;
1722  info->selections = NULL;
1723  info->icon_window = NULL;
1724  info->destroy_icon = FALSE;
1725
1726  gtk_drag_get_event_actions (event, info->button, actions,
1727                              &suggested_action, &possible_actions);
1728 
1729  info->cursor = gtk_drag_get_cursor (suggested_action);
1730
1731  /* Set cur_x, cur_y here so if the "drag_begin" signal shows
1732   * the drag icon, it will be in the right place
1733   */
1734  if (event && event->type == GDK_MOTION_NOTIFY)
1735    {
1736      info->cur_x = event->motion.x_root;
1737      info->cur_y = event->motion.y_root;
1738    }
1739  else
1740    {
1741      gint x, y;
1742      gdk_window_get_pointer (GDK_ROOT_PARENT (), &x, &y, NULL);
1743
1744      info->cur_x = x;
1745      info->cur_y = y;
1746    }
1747
1748  gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin",
1749                           info->context);
1750 
1751  if (event && event->type == GDK_MOTION_NOTIFY)
1752    gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
1753
1754  info->start_x = info->cur_x;
1755  info->start_y = info->cur_y;
1756
1757  gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event",
1758                      GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info);
1759  gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event",
1760                      GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info);
1761  gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_press_event",
1762                      GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1763  gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "key_release_event",
1764                      GTK_SIGNAL_FUNC (gtk_drag_key_cb), info);
1765  gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get",
1766                      GTK_SIGNAL_FUNC (gtk_drag_selection_get), info);
1767
1768  /* We use a GTK grab here to override any grabs that the widget
1769   * we are dragging from might have held
1770   */
1771  gtk_grab_add (info->ipc_widget);
1772  if (gdk_pointer_grab (info->ipc_widget->window, FALSE,
1773                        GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1774                        GDK_BUTTON_RELEASE_MASK, NULL,
1775                        info->cursor, time) == 0)
1776    {
1777      if (gdk_keyboard_grab (info->ipc_widget->window, FALSE, time) != 0)
1778        {
1779          /* FIXME: This should be cleaned up... */
1780          GdkEventButton ev;
1781
1782          ev.time = time;
1783          ev.type = GDK_BUTTON_RELEASE;
1784          ev.button = info->button;
1785
1786          gtk_drag_button_release_cb (widget, &ev, info);
1787
1788          return NULL;
1789        }
1790    }
1791
1792  return info->context;
1793}
1794
1795/*************************************************************
1796 * gtk_drag_source_set:
1797 *     Register a drop site, and possibly add default behaviors.
1798 *   arguments:
1799 *     widget:
1800 *     start_button_mask: Mask of allowed buttons to start drag
1801 *     targets:           Table of targets for this source
1802 *     n_targets:
1803 *     actions:           Actions allowed for this source
1804 *   results:
1805 *************************************************************/
1806
1807void
1808gtk_drag_source_set (GtkWidget            *widget,
1809                     GdkModifierType       start_button_mask,
1810                     const GtkTargetEntry *targets,
1811                     gint                  n_targets,
1812                     GdkDragAction         actions)
1813{
1814  GtkDragSourceSite *site;
1815
1816  g_return_if_fail (widget != NULL);
1817
1818  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1819
1820  gtk_widget_add_events (widget,
1821                         gtk_widget_get_events (widget) |
1822                         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1823                         GDK_BUTTON_MOTION_MASK);
1824
1825  if (site)
1826    {
1827      if (site->target_list)
1828        gtk_target_list_unref (site->target_list);
1829    }
1830  else
1831    {
1832      site = g_new0 (GtkDragSourceSite, 1);
1833     
1834      gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
1835                          GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1836                          site);
1837      gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
1838                          GTK_SIGNAL_FUNC (gtk_drag_source_event_cb),
1839                          site);
1840     
1841      gtk_object_set_data_full (GTK_OBJECT (widget),
1842                                "gtk-site-data",
1843                                site, gtk_drag_source_site_destroy);
1844    }
1845
1846  site->start_button_mask = start_button_mask;
1847
1848  if (targets)
1849    site->target_list = gtk_target_list_new (targets, n_targets);
1850  else
1851    site->target_list = NULL;
1852
1853  site->actions = actions;
1854
1855}
1856
1857/*************************************************************
1858 * gtk_drag_source_unset
1859 *     Unregister this widget as a drag source.
1860 *   arguments:
1861 *     widget:
1862 *   results:
1863 *************************************************************/
1864
1865void
1866gtk_drag_source_unset (GtkWidget        *widget)
1867{
1868  GtkDragSourceSite *site;
1869
1870  g_return_if_fail (widget != NULL);
1871
1872  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1873
1874  if (site)
1875    {
1876      gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site);
1877      gtk_object_set_data (GTK_OBJECT (widget), "gtk-site-data", NULL);
1878    }
1879}
1880
1881/*************************************************************
1882 * gtk_drag_source_set_icon:
1883 *     Set an icon for drags from this source.
1884 *   arguments:
1885 *     colormap: Colormap for this icon
1886 *     pixmap:
1887 *     mask
1888 *   results:
1889 *************************************************************/
1890
1891void
1892gtk_drag_source_set_icon (GtkWidget     *widget,
1893                          GdkColormap   *colormap,
1894                          GdkPixmap     *pixmap,
1895                          GdkBitmap     *mask)
1896{
1897  GtkDragSourceSite *site;
1898
1899  g_return_if_fail (widget != NULL);
1900
1901  site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1902  g_return_if_fail (site != NULL);
1903 
1904  if (site->colormap)
1905    gdk_colormap_unref (site->colormap);
1906  if (site->pixmap)
1907    gdk_pixmap_unref (site->pixmap);
1908  if (site->mask)
1909    gdk_pixmap_unref (site->mask);
1910
1911  site->colormap = colormap;
1912  if (colormap)
1913    gdk_colormap_ref (colormap);
1914
1915  site->pixmap = pixmap;
1916  if (pixmap)
1917    gdk_pixmap_ref (pixmap);
1918
1919  site->mask = mask;
1920  if (mask)
1921    gdk_pixmap_ref (mask);
1922}
1923
1924/*************************************************************
1925 * gtk_drag_set_icon_window:
1926 *     Set a widget as the icon for a drag.
1927 *   arguments:
1928 *     context:
1929 *     widget:
1930 *     hot_x:    Hot spot
1931 *     hot_y:
1932 *   results:
1933 *************************************************************/
1934
1935static void
1936gtk_drag_set_icon_window (GdkDragContext *context,
1937                          GtkWidget      *widget,
1938                          gint            hot_x,
1939                          gint            hot_y,
1940                          gboolean        destroy_on_release)
1941{
1942  GtkDragSourceInfo *info;
1943
1944  g_return_if_fail (context != NULL);
1945  g_return_if_fail (widget != NULL);
1946
1947  info = g_dataset_get_data (context, "gtk-info");
1948  gtk_drag_remove_icon (info);
1949
1950  info->icon_window = widget;
1951  info->hot_x = hot_x;
1952  info->hot_y = hot_y;
1953
1954  if (widget)
1955    {
1956      gtk_widget_set_uposition (widget,
1957                                info->cur_x - info->hot_x,
1958                                info->cur_y - info->hot_y);
1959      gtk_widget_ref (widget);
1960      gdk_window_raise (widget->window);
1961      gtk_widget_show (widget);
1962    }
1963
1964  info->destroy_icon = destroy_on_release;
1965}
1966
1967/*************************************************************
1968 * gtk_drag_set_icon_widget:
1969 *     Set a widget as the icon for a drag.
1970 *   arguments:
1971 *     context:
1972 *     widget:
1973 *     hot_x:    Hot spot
1974 *     hot_y:
1975 *   results:
1976 *************************************************************/
1977
1978void
1979gtk_drag_set_icon_widget (GdkDragContext    *context,
1980                          GtkWidget         *widget,
1981                          gint               hot_x,
1982                          gint               hot_y)
1983{
1984  g_return_if_fail (context != NULL);
1985  g_return_if_fail (widget != NULL);
1986
1987  gtk_drag_set_icon_window (context, widget, hot_x, hot_y, FALSE);
1988}
1989
1990/*************************************************************
1991 * gtk_drag_set_icon_pixmap:
1992 *     Set a widget as the icon for a drag.
1993 *   arguments:
1994 *     context:
1995 *     colormap: Colormap for the icon window.
1996 *     pixmap:   
1997 *     mask:
1998 *     hot_x:    Hot spot
1999 *     hot_y:
2000 *   results:
2001 *************************************************************/
2002
2003void
2004gtk_drag_set_icon_pixmap (GdkDragContext    *context,
2005                          GdkColormap       *colormap,
2006                          GdkPixmap         *pixmap,
2007                          GdkBitmap         *mask,
2008                          gint               hot_x,
2009                          gint               hot_y)
2010{
2011  GtkWidget *window;
2012  gint width, height;
2013     
2014  g_return_if_fail (context != NULL);
2015  g_return_if_fail (colormap != NULL);
2016  g_return_if_fail (pixmap != NULL);
2017
2018  gdk_window_get_size (pixmap, &width, &height);
2019
2020  gtk_widget_push_visual (gdk_colormap_get_visual (colormap));
2021  gtk_widget_push_colormap (colormap);
2022
2023  window = gtk_window_new (GTK_WINDOW_POPUP);
2024  gtk_widget_set_events (window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
2025  gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
2026
2027  gtk_widget_pop_visual ();
2028  gtk_widget_pop_colormap ();
2029
2030  gtk_widget_set_usize (window, width, height);
2031  gtk_widget_realize (window);
2032
2033  gdk_window_set_back_pixmap (window->window, pixmap, FALSE);
2034 
2035  if (mask)
2036    gtk_widget_shape_combine_mask (window, mask, 0, 0);
2037
2038  gtk_drag_set_icon_window (context, window, hot_x, hot_y, TRUE);
2039}
2040
2041/*************************************************************
2042 * gtk_drag_set_icon_default:
2043 *     Set the icon for a drag to the default icon.
2044 *   arguments:
2045 *     context:
2046 *   results:
2047 *************************************************************/
2048
2049void
2050gtk_drag_set_icon_default (GdkDragContext    *context)
2051{
2052  g_return_if_fail (context != NULL);
2053
2054  if (!default_icon_pixmap)
2055    {
2056      default_icon_colormap = gdk_colormap_get_system ();
2057      default_icon_pixmap =
2058        gdk_pixmap_colormap_create_from_xpm_d (NULL,
2059                                               default_icon_colormap,
2060                                               &default_icon_mask,
2061                                               NULL, (gchar **)drag_default_xpm);
2062      default_icon_hot_x = -2;
2063      default_icon_hot_y = -2;
2064    }
2065
2066  gtk_drag_set_icon_pixmap (context,
2067                            default_icon_colormap,
2068                            default_icon_pixmap,
2069                            default_icon_mask,
2070                            default_icon_hot_x,
2071                            default_icon_hot_y);
2072}
2073
2074/*************************************************************
2075 * gtk_drag_set_default_icon:
2076 *     Set a default icon for all drags as a pixmap.
2077 *   arguments:
2078 *     colormap: Colormap for the icon window.
2079 *     pixmap:   
2080 *     mask:
2081 *     hot_x:    Hot spot
2082 *     hot_y:
2083 *   results:
2084 *************************************************************/
2085
2086void
2087gtk_drag_set_default_icon (GdkColormap   *colormap,
2088                           GdkPixmap     *pixmap,
2089                           GdkBitmap     *mask,
2090                           gint           hot_x,
2091                           gint           hot_y)
2092{
2093  g_return_if_fail (colormap != NULL);
2094  g_return_if_fail (pixmap != NULL);
2095 
2096  if (default_icon_colormap)
2097    gdk_colormap_unref (default_icon_colormap);
2098  if (default_icon_pixmap)
2099    gdk_pixmap_unref (default_icon_pixmap);
2100  if (default_icon_mask)
[15780]2101    gdk_pixmap_unref (default_icon_mask);
[14481]2102
2103  default_icon_colormap = colormap;
2104  gdk_colormap_ref (colormap);
2105 
2106  default_icon_pixmap = pixmap;
2107  gdk_pixmap_ref (pixmap);
2108
2109  default_icon_mask = mask;
2110  if (mask)
2111    gdk_pixmap_ref (mask);
2112 
2113  default_icon_hot_x = hot_x;
2114  default_icon_hot_y = hot_y;
2115}
2116
2117
2118/*************************************************************
2119 * gtk_drag_source_handle_event:
2120 *     Called from widget event handling code on Drag events
2121 *     for drag sources.
2122 *
2123 *   arguments:
2124 *     toplevel: Toplevel widget that received the event
2125 *     event:
2126 *   results:
2127 *************************************************************/
2128
2129void
2130gtk_drag_source_handle_event (GtkWidget *widget,
2131                              GdkEvent  *event)
2132{
2133  GtkDragSourceInfo *info;
2134  GdkDragContext *context;
2135
2136  g_return_if_fail (widget != NULL);
2137  g_return_if_fail (event != NULL);
2138
2139  context = event->dnd.context;
2140  info = g_dataset_get_data (context, "gtk-info");
2141  if (!info)
2142    return;
2143
2144  switch (event->type)
2145    {
2146    case GDK_DRAG_STATUS:
2147      {
2148        GdkCursor *cursor;
2149
2150        if (info->proxy_dest)
2151          {
2152            if (!event->dnd.send_event)
2153              {
2154                if (info->proxy_dest->proxy_drop_wait)
2155                  {
2156                    gboolean result = context->action != 0;
2157                   
2158                    /* Aha - we can finally pass the MOTIF DROP on... */
2159                    gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
2160                    if (result)
2161                      gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
2162                    else
2163                      gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
2164                  }
2165                else
2166                  {
2167                    gdk_drag_status (info->proxy_dest->context,
2168                                     event->dnd.context->action,
2169                                     event->dnd.time);
2170                  }
2171              }
2172          }
2173        else
2174          {
2175            cursor = gtk_drag_get_cursor (event->dnd.context->action);
2176            if (info->cursor != cursor)
2177              {
2178                XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window),
2179                                          PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask,
2180                                          ((GdkCursorPrivate *)cursor)->xcursor,
2181                                          event->dnd.time);
2182                info->cursor = cursor;
2183              }
2184           
2185            if (info->last_event)
2186              {
2187                gtk_drag_update (info,
2188                                 info->cur_x, info->cur_y,
2189                                 info->last_event);
2190                info->last_event = NULL;
2191              }
2192          }
2193      }
2194      break;
2195     
2196    case GDK_DROP_FINISHED:
2197      gtk_drag_drop_finished (info, TRUE, event->dnd.time);
2198      break;
2199    default:
2200      g_assert_not_reached ();
2201    }
2202}
2203
2204/*************************************************************
2205 * gtk_drag_source_check_selection:
2206 *     Check if we've set up handlers/claimed the selection
2207 *     for a given drag. If not, add them.
2208 *   arguments:
2209 *     
2210 *   results:
2211 *************************************************************/
2212
2213static void
2214gtk_drag_source_check_selection (GtkDragSourceInfo *info,
2215                                 GdkAtom            selection,
2216                                 guint32            time)
2217{
2218  GList *tmp_list;
2219
2220  tmp_list = info->selections;
2221  while (tmp_list)
2222    {
2223      if (GPOINTER_TO_UINT (tmp_list->data) == selection)
2224        return;
2225      tmp_list = tmp_list->next;
2226    }
2227
2228  gtk_selection_owner_set (info->ipc_widget, selection, time);
2229  info->selections = g_list_prepend (info->selections,
2230                                     GUINT_TO_POINTER (selection));
2231
2232  tmp_list = info->target_list->list;
2233  while (tmp_list)
2234    {
2235      GtkTargetPair *pair = tmp_list->data;
2236
2237      gtk_selection_add_target (info->ipc_widget,
2238                                selection,
2239                                pair->target,
2240                                pair->info);
2241      tmp_list = tmp_list->next;
2242    }
2243 
2244  if (info->context->protocol == GDK_DRAG_PROTO_MOTIF)
2245    {
2246      gtk_selection_add_target (info->ipc_widget,
2247                                selection,
2248                                gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE),
2249                                TARGET_MOTIF_SUCCESS);
2250      gtk_selection_add_target (info->ipc_widget,
2251                                selection,
2252                                gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE),
2253                                TARGET_MOTIF_FAILURE);
2254    }
2255
2256  gtk_selection_add_target (info->ipc_widget,
2257                            selection,
2258                            gdk_atom_intern ("DELETE", FALSE),
2259                            TARGET_DELETE);
2260}
2261
2262/*************************************************************
2263 * gtk_drag_drop_finished:
2264 *     Clean up from the drag, and display snapback, if necessary.
2265 *   arguments:
2266 *     info:
2267 *     success:
2268 *     time:
2269 *   results:
2270 *************************************************************/
2271
2272static void
2273gtk_drag_drop_finished (GtkDragSourceInfo *info,
2274                        gboolean           success,
2275                        guint              time)
2276{
2277  gtk_drag_source_release_selections (info, time);
2278
2279  if (info->proxy_dest)
2280    {
2281      /* The time from the event isn't reliable for Xdnd drags */
2282      gtk_drag_finish (info->proxy_dest->context, success, FALSE,
2283                       info->proxy_dest->proxy_drop_time);
2284      gtk_drag_source_info_destroy (info);
2285    }
2286  else
2287    {
2288      if (success)
2289        {
2290          gtk_drag_source_info_destroy (info);
2291        }
2292      else
2293        {
2294          GtkDragAnim *anim = g_new (GtkDragAnim, 1);
2295          anim->info = info;
2296          anim->step = 0;
2297         
2298          anim->n_steps = MAX (info->cur_x - info->start_x,
2299                               info->cur_y - info->start_y) / ANIM_STEP_LENGTH;
2300          anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS);
2301          if (info->icon_window)
2302            {
2303              gtk_widget_show (info->icon_window);
2304              gdk_window_raise (info->icon_window->window);
2305            }
2306         
2307          /* Mark the context as dead, so if the destination decides
2308           * to respond really late, we still are OK.
2309           */
2310          g_dataset_set_data (info->context, "gtk-info", NULL);
2311          gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim);
2312        }
2313    }
2314}
2315
2316static void
2317gtk_drag_source_release_selections (GtkDragSourceInfo *info,
2318                                    guint32            time)
2319{
2320  GList *tmp_list = info->selections;
2321  while (tmp_list)
2322    {
2323      GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data);
2324      if (gdk_selection_owner_get (selection) == info->ipc_widget->window)
2325        gtk_selection_owner_set (NULL, selection, time);
2326      tmp_list = tmp_list->next;
2327    }
2328
2329  g_list_free (info->selections);
2330  info->selections = NULL;
2331}
2332
2333/*************************************************************
2334 * gtk_drag_drop:
2335 *     Send a drop event.
2336 *   arguments:
2337 *     
2338 *   results:
2339 *************************************************************/
2340
2341static void
2342gtk_drag_drop (GtkDragSourceInfo *info,
2343               guint32            time)
2344{
2345  if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)
2346    {
2347      GtkSelectionData selection_data;
2348      GList *tmp_list;
2349      GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
2350     
2351      tmp_list = info->target_list->list;
2352      while (tmp_list)
2353        {
2354          GtkTargetPair *pair = tmp_list->data;
2355         
2356          if (pair->target == target)
2357            {
2358              selection_data.selection = GDK_NONE;
2359              selection_data.target = target;
2360              selection_data.data = NULL;
2361              selection_data.length = -1;
2362             
2363              gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2364                                       info->context, &selection_data,
2365                                       pair->info,
2366                                       time);
2367             
2368              /* FIXME: Should we check for length >= 0 here? */
2369              gtk_drag_drop_finished (info, TRUE, time);
2370              return;
2371            }
2372          tmp_list = tmp_list->next;
2373        }
2374      gtk_drag_drop_finished (info, FALSE, time);
2375    }
2376  else
2377    {
2378      if (info->icon_window)
2379        gtk_widget_hide (info->icon_window);
2380       
2381      gdk_drag_drop (info->context, time);
2382      info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME,
2383                                            gtk_drag_abort_timeout,
2384                                            info);
2385    }
2386}
2387
2388/*
2389 * Source side callbacks.
2390 */
2391
2392static gint
2393gtk_drag_source_event_cb (GtkWidget      *widget,
2394                          GdkEvent       *event,
2395                          gpointer        data)
2396{
2397  GtkDragSourceSite *site;
2398  site = (GtkDragSourceSite *)data;
2399
2400  switch (event->type)
2401    {
2402    case GDK_BUTTON_PRESS:
2403      if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2404        {
2405          site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
2406          site->x = event->button.x;
2407          site->y = event->button.y;
2408        }
2409      break;
2410     
2411    case GDK_BUTTON_RELEASE:
2412      if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
2413        {
2414          site->state &= ~(GDK_BUTTON1_MASK << (event->button.button - 1));
2415        }
2416      break;
2417     
2418    case GDK_MOTION_NOTIFY:
2419      if (site->state & event->motion.state & site->start_button_mask)
2420        {
2421          /* FIXME: This is really broken and can leave us
2422           * with a stuck grab
2423           */
2424          int i;
2425          for (i=1; i<6; i++)
2426            {
2427              if (site->state & event->motion.state &
2428                  GDK_BUTTON1_MASK << (i - 1))
2429                break;
2430            }
2431         
2432          if (MAX (abs (site->x - event->motion.x),
2433                   abs (site->y - event->motion.y)) > 3)
2434            {
2435              GtkDragSourceInfo *info;
2436              GdkDragContext *context;
2437             
2438              site->state = 0;
2439              context = gtk_drag_begin (widget, site->target_list,
2440                                        site->actions,
2441                                        i, event);
2442
2443             
2444              info = g_dataset_get_data (context, "gtk-info");
2445
2446              if (!info->icon_window)
2447                {
2448                  if (site->pixmap)
2449                    gtk_drag_set_icon_pixmap (context,
2450                                              site->colormap,
2451                                              site->pixmap,
2452                                              site->mask, -2, -2);
2453                  else
2454                    gtk_drag_set_icon_default (context);
2455                }
2456
2457              return TRUE;
2458            }
2459        }
2460      break;
2461     
2462    default:                    /* hit for 2/3BUTTON_PRESS */
2463      break;
2464    }
2465  return FALSE;
2466}
2467
2468static void
2469gtk_drag_source_site_destroy (gpointer data)
2470{
2471  GtkDragSourceSite *site = data;
2472
2473  if (site->target_list)
2474    gtk_target_list_unref (site->target_list);
2475
2476  if (site->pixmap)
2477    gdk_pixmap_unref (site->pixmap);
2478 
2479  if (site->mask)
2480    gdk_pixmap_unref (site->mask);
2481 
2482  g_free (site);
2483}
2484
2485static void
2486gtk_drag_selection_get (GtkWidget        *widget,
2487                        GtkSelectionData *selection_data,
2488                        guint             sel_info,
2489                        guint32           time,
2490                        gpointer          data)
2491{
2492  GtkDragSourceInfo *info = data;
2493  static GdkAtom null_atom = GDK_NONE;
2494  guint target_info;
2495
2496  if (!null_atom)
2497    null_atom = gdk_atom_intern ("NULL", FALSE);
2498
2499  switch (sel_info)
2500    {
2501    case TARGET_DELETE:
2502      gtk_signal_emit_by_name (GTK_OBJECT (info->widget),
2503                               "drag_data_delete",
2504                               info->context);
2505      gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2506      break;
2507    case TARGET_MOTIF_SUCCESS:
2508      gtk_drag_drop_finished (info, TRUE, time);
2509      gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2510      break;
2511    case TARGET_MOTIF_FAILURE:
2512      gtk_drag_drop_finished (info, FALSE, time);
2513      gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
2514      break;
2515    default:
2516      if (info->proxy_dest)
2517        {
2518          /* This is sort of dangerous and needs to be thought
2519           * through better
2520           */
2521          info->proxy_dest->proxy_data = selection_data;
2522          gtk_drag_get_data (info->widget,
2523                             info->proxy_dest->context,
2524                             selection_data->target,
2525                             time);
2526          gtk_main ();
2527          info->proxy_dest->proxy_data = NULL;
2528        }
2529      else
2530        {
2531          if (gtk_target_list_find (info->target_list,
2532                                    selection_data->target,
2533                                    &target_info))
2534            {
2535              gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get",
2536                                       info->context,
2537                                       selection_data,
2538                                       target_info,
2539                                       time);
2540            }
2541        }
2542      break;
2543    }
2544}
2545
2546static gint
2547gtk_drag_anim_timeout (gpointer data)
2548{
2549  GtkDragAnim *anim = data;
2550  gint x, y;
2551  gboolean retval;
2552
2553  GDK_THREADS_ENTER ();
2554
2555  if (anim->step == anim->n_steps)
2556    {
2557      gtk_drag_source_info_destroy (anim->info);
2558      g_free (anim);
2559
2560      retval = FALSE;
2561    }
2562  else
2563    {
2564      x = (anim->info->start_x * (anim->step + 1) +
2565           anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2566      y = (anim->info->start_y * (anim->step + 1) +
2567           anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps;
2568      if (anim->info->icon_window)
2569        gtk_widget_set_uposition (anim->info->icon_window,
2570                                  x - anim->info->hot_x,
2571                                  y - anim->info->hot_y);
2572 
2573      anim->step++;
2574
2575      retval = TRUE;
2576    }
2577
2578  GDK_THREADS_LEAVE ();
2579
2580  return retval;
2581}
2582
2583static void
2584gtk_drag_remove_icon (GtkDragSourceInfo *info)
2585{
2586  if (info->icon_window)
2587    {
2588      gtk_widget_hide (info->icon_window);
2589      if (info->destroy_icon)
2590        gtk_widget_destroy (info->icon_window);
2591
2592      gtk_widget_unref (info->icon_window);
2593      info->icon_window = NULL;
2594    }
2595}
2596
2597static void
2598gtk_drag_source_info_destroy (gpointer data)
2599{
2600  GtkDragSourceInfo *info = data;
2601
2602  gtk_drag_remove_icon (data);
2603
2604  if (!info->proxy_dest)
2605    gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_end",
2606                             info->context);
2607
2608  if (info->widget)
2609    gtk_widget_unref (info->widget);
2610
2611  gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info);
2612  gtk_selection_remove_all (info->ipc_widget);
2613  gtk_object_set_data (GTK_OBJECT (info->ipc_widget), "gtk-info", NULL);
2614  source_widgets = g_slist_remove (source_widgets, info->ipc_widget);
2615  gtk_drag_release_ipc_widget (info->ipc_widget);
2616
2617  gtk_target_list_unref (info->target_list);
2618
2619  g_dataset_set_data (info->context, "gtk-info", NULL);
2620  gdk_drag_context_unref (info->context);
2621
2622  if (info->drop_timeout)
2623    gtk_timeout_remove (info->drop_timeout);
2624
2625  g_free (info);
2626}
2627
2628/*************************************************************
2629 * gtk_drag_update:
2630 *     Function to update the status of the drag when the
2631 *     cursor moves or the modifier changes
2632 *   arguments:
2633 *     info: DragSourceInfo for the drag
2634 *     x_root, y_root: position of darg
2635 *     event: The event that triggered this call
2636 *   results:
2637 *************************************************************/
2638
2639static void
2640gtk_drag_update (GtkDragSourceInfo *info,
2641                 gint               x_root,
2642                 gint               y_root,
2643                 GdkEvent          *event)
2644{
2645  GdkDragAction action;
2646  GdkDragAction possible_actions;
2647  GdkWindow *window = NULL;
2648  GdkWindow *dest_window;
2649  GdkDragProtocol protocol;
2650  GdkAtom selection;
2651  guint32 time = gtk_drag_get_event_time (event);
2652
2653  gtk_drag_get_event_actions (event,
2654                              info->button,
2655                              info->possible_actions,
2656                              &action, &possible_actions);
2657  info->cur_x = x_root;
2658  info->cur_y = y_root;
2659
2660  if (info->icon_window)
2661    {
2662      gdk_window_raise (info->icon_window->window);
2663      gtk_widget_set_uposition (info->icon_window,
2664                                info->cur_x - info->hot_x,
2665                                info->cur_y - info->hot_y);
2666      window = info->icon_window->window;
2667    }
2668 
2669  gdk_drag_find_window (info->context,
2670                        window, x_root, y_root,
2671                        &dest_window, &protocol);
2672
2673  if (gdk_drag_motion (info->context, dest_window, protocol,
2674                       x_root, y_root, action,
2675                       possible_actions,
2676                       time))
2677    {
2678      if (info->last_event)
2679        gdk_event_free ((GdkEvent *)info->last_event);
2680     
2681      info->last_event = gdk_event_copy ((GdkEvent *)event);
2682    }
2683
2684  if (dest_window)
2685    gdk_window_unref (dest_window);
2686
2687  selection = gdk_drag_get_selection (info->context);
2688  if (selection)
2689    gtk_drag_source_check_selection (info, selection, time);
2690}
2691
2692/*************************************************************
2693 * gtk_drag_end:
2694 *     Called when the user finishes to drag, either by
2695 *     releasing the mouse, or by pressing Esc.
2696 *   arguments:
2697 *     widget: GtkInvisible widget for this drag
2698 *     info:
2699 *   results:
2700 *************************************************************/
2701
2702static void
2703gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
2704{
2705  GdkEvent send_event;
2706  GtkWidget *source_widget = info->widget;
2707
2708  gdk_pointer_ungrab (time);
2709  gdk_keyboard_ungrab (time);
2710
2711  gtk_grab_remove (info->ipc_widget);
2712
2713  gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
2714                                 GTK_SIGNAL_FUNC (gtk_drag_button_release_cb),
2715                                 info);
2716  gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
2717                                 GTK_SIGNAL_FUNC (gtk_drag_motion_cb),
2718                                 info);
[15780]2719  gtk_signal_disconnect_by_func (GTK_OBJECT (info->ipc_widget),
2720                                 GTK_SIGNAL_FUNC (gtk_drag_key_cb),
2721                                 info);
[14481]2722
2723  /* Send on a release pair to the the original
2724   * widget to convince it to release its grab. We need to
2725   * call gtk_propagate_event() here, instead of
2726   * gtk_widget_event() because widget like GtkList may
2727   * expect propagation.
2728   */
2729
2730  send_event.button.type = GDK_BUTTON_RELEASE;
2731  send_event.button.window = GDK_ROOT_PARENT ();
2732  send_event.button.send_event = TRUE;
2733  send_event.button.time = time;
2734  send_event.button.x = 0;
2735  send_event.button.y = 0;
2736  send_event.button.pressure = 0.;
2737  send_event.button.xtilt = 0.;
2738  send_event.button.ytilt = 0.;
2739  send_event.button.state = 0;
2740  send_event.button.button = info->button;
2741  send_event.button.source = GDK_SOURCE_PEN;
2742  send_event.button.deviceid = GDK_CORE_POINTER;
2743  send_event.button.x_root = 0;
2744  send_event.button.y_root = 0;
2745
2746  gtk_propagate_event (source_widget, &send_event);
2747}
2748
2749/*************************************************************
2750 * gtk_drag_motion_cb:
2751 *     "motion_notify_event" callback during drag.
2752 *   arguments:
2753 *     
2754 *   results:
2755 *************************************************************/
2756
2757static gint
2758gtk_drag_motion_cb (GtkWidget      *widget,
2759                    GdkEventMotion *event,
2760                    gpointer        data)
2761{
2762  GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2763  gint x_root, y_root;
2764
2765  if (event->is_hint)
2766    {
2767      gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL);
2768      event->x_root = x_root;
2769      event->y_root = y_root;
2770    }
2771
2772  gtk_drag_update (info, event->x_root, event->y_root, (GdkEvent *)event);
2773
2774  return TRUE;
2775}
2776
2777/*************************************************************
2778 * gtk_drag_key_cb:
2779 *     "key_press/release_event" callback during drag.
2780 *   arguments:
2781 *     
2782 *   results:
2783 *************************************************************/
2784
2785static gint
2786gtk_drag_key_cb (GtkWidget         *widget,
2787                 GdkEventKey       *event,
2788                 gpointer           data)
2789{
2790  GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2791  GdkModifierType state;
2792 
2793  if (event->type == GDK_KEY_PRESS)
2794    {
2795      if (event->keyval == GDK_Escape)
2796        {
2797          gtk_drag_end (info, event->time);
2798          gdk_drag_abort (info->context, event->time);
2799          gtk_drag_drop_finished (info, FALSE, event->time);
2800
2801          return TRUE;
2802        }
2803    }
2804
2805  /* Now send a "motion" so that the modifier state is updated */
2806
2807  /* The state is not yet updated in the event, so we need
2808   * to query it here. We could use XGetModifierMapping, but
2809   * that would be overkill.
2810   */
2811  gdk_window_get_pointer (GDK_ROOT_PARENT(), NULL, NULL, &state);
2812
2813  event->state = state;
2814  gtk_drag_update (info, info->cur_x, info->cur_y, (GdkEvent *)event);
2815
2816  return TRUE;
2817}
2818
2819/*************************************************************
2820 * gtk_drag_button_release_cb:
2821 *     "button_release_event" callback during drag.
2822 *   arguments:
2823 *     
2824 *   results:
2825 *************************************************************/
2826
2827static gint
2828gtk_drag_button_release_cb (GtkWidget      *widget,
2829                            GdkEventButton *event,
2830                            gpointer        data)
2831{
2832  GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
2833
2834  if (event->button != info->button)
2835    return FALSE;
2836
2837  gtk_drag_end (info, event->time);
2838
2839  if ((info->context->action != 0) && (info->context->dest_window != NULL))
2840    {
2841      gtk_drag_drop (info, event->time);
2842    }
2843  else
2844    {
2845      gdk_drag_abort (info->context, event->time);
2846      gtk_drag_drop_finished (info, FALSE, event->time);
2847    }
2848
2849  return TRUE;
2850}
2851
2852static gint
2853gtk_drag_abort_timeout (gpointer data)
2854{
2855  GtkDragSourceInfo *info = data;
2856  guint32 time = GDK_CURRENT_TIME;
2857
2858  GDK_THREADS_ENTER ();
2859
2860  if (info->proxy_dest)
2861    time = info->proxy_dest->proxy_drop_time;
2862
2863  info->drop_timeout = 0;
2864  gtk_drag_drop_finished (info, FALSE, time);
2865 
2866  GDK_THREADS_LEAVE ();
2867 
2868  return FALSE;
2869}
Note: See TracBrowser for help on using the repository browser.