1
/* GDK - The GIMP Drawing Kit
2
* Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Lesser General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
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
* Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser 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.
21
* Modified by the GTK+ Team and others 1997-2000. 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/.
28
#include "gdk.h" /* For gdk_flush() */
30
#include "gdkproperty.h"
31
#include "gdkinternals.h"
32
#include "gdkprivate-fb.h"
34
typedef struct _GdkDragContextPrivate GdkDragContextPrivate;
38
GDK_DRAG_STATUS_MOTION_WAIT,
39
GDK_DRAG_STATUS_ACTION_WAIT,
43
/* Structure that holds information about a drag in progress.
44
* this is used on both source and destination sides.
46
struct _GdkDragContextPrivate {
47
GdkAtom local_selection;
49
guint16 last_x; /* Coordinates from last event */
52
guint drag_status : 4; /* current status of drag */
57
static GList *contexts;
58
static gpointer parent_class = NULL;
60
#define GDK_DRAG_CONTEXT_PRIVATE_DATA(ctx) ((GdkDragContextPrivate *) GDK_DRAG_CONTEXT (ctx)->windowing_data)
62
GdkDragContext *current_dest_drag = NULL;
65
gdk_drag_context_init (GdkDragContext *dragcontext)
67
dragcontext->windowing_data = g_new0 (GdkDragContextPrivate, 1);
69
contexts = g_list_prepend (contexts, dragcontext);
73
gdk_drag_context_finalize (GObject *object)
75
GdkDragContext *context = GDK_DRAG_CONTEXT (object);
76
GdkDragContextPrivate *private = GDK_DRAG_CONTEXT_PRIVATE_DATA (object);
78
g_list_free (context->targets);
80
if (context->source_window)
81
gdk_window_unref (context->source_window);
83
if (context->dest_window)
84
gdk_window_unref (context->dest_window);
90
context->windowing_data = NULL;
93
contexts = g_list_remove (contexts, context);
95
G_OBJECT_CLASS (parent_class)->finalize (object);
99
gdk_drag_context_class_init (GdkDragContextClass *klass)
101
GObjectClass *object_class = G_OBJECT_CLASS (klass);
103
parent_class = g_type_class_peek_parent (klass);
105
object_class->finalize = gdk_drag_context_finalize;
110
gdk_drag_context_get_type (void)
112
static GType object_type = 0;
116
static const GTypeInfo object_info =
118
sizeof (GdkDragContextClass),
119
(GBaseInitFunc) NULL,
120
(GBaseFinalizeFunc) NULL,
121
(GClassInitFunc) gdk_drag_context_class_init,
122
NULL, /* class_finalize */
123
NULL, /* class_data */
124
sizeof (GdkDragContext),
126
(GInstanceInitFunc) gdk_drag_context_init,
129
object_type = g_type_register_static (G_TYPE_OBJECT,
139
gdk_drag_context_new (void)
141
return (GdkDragContext *)g_object_new (gdk_drag_context_get_type (), NULL);
145
gdk_drag_context_ref (GdkDragContext *context)
147
g_object_ref (context);
151
gdk_drag_context_unref (GdkDragContext *context)
153
g_object_unref (context);
156
static GdkDragContext *
157
gdk_drag_context_find (gboolean is_source,
161
GList *tmp_list = contexts;
163
GdkDragContext *context;
164
GdkDragContextPrivate *private;
168
context = (GdkDragContext *)tmp_list->data;
169
private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
171
if ((!context->is_source == !is_source) &&
172
((source == NULL) || (context->source_window && (context->source_window == source))) &&
173
((dest == NULL) || (context->dest_window && (context->dest_window == dest))))
176
tmp_list = tmp_list->next;
183
/*************************************************************
184
************************** Public API ***********************
185
*************************************************************/
195
local_send_leave (GdkDragContext *context,
200
if ((current_dest_drag != NULL) &&
201
(current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
202
(current_dest_drag->source_window == context->source_window))
204
tmp_event.dnd.type = GDK_DRAG_LEAVE;
205
tmp_event.dnd.window = context->dest_window;
206
/* Pass ownership of context to the event */
207
tmp_event.dnd.context = current_dest_drag;
208
tmp_event.dnd.send_event = FALSE;
209
tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
211
current_dest_drag = NULL;
213
gdk_event_put (&tmp_event);
219
local_send_enter (GdkDragContext *context,
223
GdkDragContextPrivate *private;
224
GdkDragContext *new_context;
226
private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
228
if (!private->local_selection)
229
private->local_selection = gdk_atom_intern ("LocalDndSelection", FALSE);
231
if (current_dest_drag != NULL)
233
gdk_drag_context_unref (current_dest_drag);
234
current_dest_drag = NULL;
237
new_context = gdk_drag_context_new ();
238
new_context->protocol = GDK_DRAG_PROTO_LOCAL;
239
new_context->is_source = FALSE;
241
new_context->source_window = context->source_window;
242
gdk_window_ref (new_context->source_window);
243
new_context->dest_window = context->dest_window;
244
gdk_window_ref (new_context->dest_window);
246
new_context->targets = g_list_copy (context->targets);
248
gdk_window_set_events (new_context->source_window,
249
gdk_window_get_events (new_context->source_window) |
250
GDK_PROPERTY_CHANGE_MASK);
251
new_context->actions = context->actions;
253
tmp_event.dnd.type = GDK_DRAG_ENTER;
254
tmp_event.dnd.window = context->dest_window;
255
tmp_event.dnd.send_event = FALSE;
256
tmp_event.dnd.context = new_context;
257
gdk_drag_context_ref (new_context);
259
tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
261
current_dest_drag = new_context;
263
(GDK_DRAG_CONTEXT_PRIVATE_DATA (new_context))->local_selection =
264
private->local_selection;
266
gdk_event_put (&tmp_event);
270
local_send_motion (GdkDragContext *context,
273
GdkDragAction action,
278
if ((current_dest_drag != NULL) &&
279
(current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
280
(current_dest_drag->source_window == context->source_window))
282
tmp_event.dnd.type = GDK_DRAG_MOTION;
283
tmp_event.dnd.window = current_dest_drag->dest_window;
284
tmp_event.dnd.send_event = FALSE;
285
tmp_event.dnd.context = current_dest_drag;
286
gdk_drag_context_ref (current_dest_drag);
288
tmp_event.dnd.time = time;
290
current_dest_drag->suggested_action = action;
291
current_dest_drag->actions = current_dest_drag->suggested_action;
293
tmp_event.dnd.x_root = x_root;
294
tmp_event.dnd.y_root = y_root;
296
(GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag))->last_x = x_root;
297
(GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag))->last_y = y_root;
299
GDK_DRAG_CONTEXT_PRIVATE_DATA (context)->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
301
gdk_event_put (&tmp_event);
306
local_send_drop (GdkDragContext *context, guint32 time)
310
if ((current_dest_drag != NULL) &&
311
(current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
312
(current_dest_drag->source_window == context->source_window))
314
GdkDragContextPrivate *private;
315
private = GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag);
317
tmp_event.dnd.type = GDK_DROP_START;
318
tmp_event.dnd.window = current_dest_drag->dest_window;
319
tmp_event.dnd.send_event = FALSE;
321
tmp_event.dnd.context = current_dest_drag;
322
gdk_drag_context_ref (current_dest_drag);
324
tmp_event.dnd.time = GDK_CURRENT_TIME;
326
tmp_event.dnd.x_root = private->last_x;
327
tmp_event.dnd.y_root = private->last_y;
329
gdk_event_put (&tmp_event);
335
gdk_drag_do_leave (GdkDragContext *context, guint32 time)
337
if (context->dest_window)
339
switch (context->protocol)
341
case GDK_DRAG_PROTO_LOCAL:
342
local_send_leave (context, time);
348
gdk_window_unref (context->dest_window);
349
context->dest_window = NULL;
354
gdk_drag_begin (GdkWindow *window,
358
GdkDragContext *new_context;
360
g_return_val_if_fail (window != NULL, NULL);
362
new_context = gdk_drag_context_new ();
363
new_context->is_source = TRUE;
364
new_context->source_window = window;
365
gdk_window_ref (window);
367
tmp_list = g_list_last (targets);
368
new_context->targets = NULL;
371
new_context->targets = g_list_prepend (new_context->targets,
373
tmp_list = tmp_list->prev;
376
new_context->actions = 0;
382
gdk_drag_get_protocol_for_display (GdkDisplay *display,
384
GdkDragProtocol *protocol)
388
window = gdk_window_lookup ((GdkNativeWindow) xid);
390
if (GPOINTER_TO_INT (gdk_drawable_get_data (window, "gdk-dnd-registered")))
392
*protocol = GDK_DRAG_PROTO_LOCAL;
396
*protocol = GDK_DRAG_PROTO_NONE;
401
get_toplevel_window_at (GdkWindow *ignore,
406
GdkWindowObject *private;
407
GdkWindowObject *sub;
408
GdkWindowObject *child;
411
private = (GdkWindowObject *)_gdk_parent_root;
413
for (ltmp = private->children; ltmp; ltmp = ltmp->next)
417
if ((GDK_WINDOW (sub) != ignore) &&
418
(GDK_WINDOW_IS_MAPPED (sub)) &&
419
(x_root >= sub->x) &&
420
(x_root < sub->x + GDK_DRAWABLE_IMPL_FBDATA (sub)->width) &&
421
(y_root >= sub->y) &&
422
(y_root < sub->y + GDK_DRAWABLE_IMPL_FBDATA (sub)->height))
424
if (g_object_get_data (G_OBJECT (sub), "gdk-window-child-handler"))
426
/* Managed window, check children */
427
for (ltmp2 = sub->children; ltmp2; ltmp2 = ltmp2->next)
431
if ((GDK_WINDOW (child) != ignore) &&
432
(GDK_WINDOW_IS_MAPPED (child)) &&
433
(x_root >= sub->x + child->x) &&
434
(x_root < sub->x + child->x + GDK_DRAWABLE_IMPL_FBDATA (child)->width) &&
435
(y_root >= sub->y + child->y) &&
436
(y_root < sub->y + child->y + GDK_DRAWABLE_IMPL_FBDATA (child)->height))
437
return GDK_WINDOW (child);
441
return GDK_WINDOW (sub);
449
gdk_drag_find_window_for_screen (GdkDragContext *context,
450
GdkWindow *drag_window,
454
GdkWindow **dest_window,
455
GdkDragProtocol *protocol)
459
g_return_if_fail (context != NULL);
461
dest = get_toplevel_window_at (drag_window, x_root, y_root);
463
dest = _gdk_parent_root;
465
if (context->dest_window != dest)
469
/* Check if new destination accepts drags, and which protocol */
470
if ((recipient = gdk_drag_get_protocol ((guint32)dest, protocol)))
472
*dest_window = gdk_window_lookup ((GdkNativeWindow) recipient);
473
gdk_window_ref (*dest_window);
480
*dest_window = context->dest_window;
482
gdk_window_ref (*dest_window);
483
*protocol = context->protocol;
489
gdk_drag_motion (GdkDragContext *context,
490
GdkWindow *dest_window,
491
GdkDragProtocol protocol,
494
GdkDragAction suggested_action,
495
GdkDragAction possible_actions,
498
GdkDragContextPrivate *private;
500
g_return_val_if_fail (context != NULL, FALSE);
502
private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
504
if (context->dest_window != dest_window)
508
/* Send a leave to the last destination */
509
gdk_drag_do_leave (context, time);
510
private->drag_status = GDK_DRAG_STATUS_DRAG;
512
/* Check if new destination accepts drags, and which protocol */
515
context->dest_window = dest_window;
516
gdk_window_ref (context->dest_window);
517
context->protocol = protocol;
521
case GDK_DRAG_PROTO_LOCAL:
522
local_send_enter (context, time);
528
context->suggested_action = suggested_action;
532
context->dest_window = NULL;
536
/* Push a status event, to let the client know that
540
temp_event.dnd.type = GDK_DRAG_STATUS;
541
temp_event.dnd.window = context->source_window;
542
/* We use this to signal a synthetic status. Perhaps
543
* we should use an extra field...
545
temp_event.dnd.send_event = TRUE;
547
temp_event.dnd.context = context;
548
temp_event.dnd.time = time;
550
gdk_event_put (&temp_event);
554
context->suggested_action = suggested_action;
557
/* Send a drag-motion event */
559
private->last_x = x_root;
560
private->last_y = y_root;
562
if (context->dest_window)
564
if (private->drag_status == GDK_DRAG_STATUS_DRAG)
566
switch (context->protocol)
568
case GDK_DRAG_PROTO_LOCAL:
569
local_send_motion (context, x_root, y_root, suggested_action, time);
572
case GDK_DRAG_PROTO_NONE:
573
g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
587
gdk_drag_drop (GdkDragContext *context,
590
g_return_if_fail (context != NULL);
592
if (context->dest_window)
594
switch (context->protocol)
596
case GDK_DRAG_PROTO_LOCAL:
597
local_send_drop (context, time);
600
case GDK_DRAG_PROTO_NONE:
601
g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
610
gdk_drag_abort (GdkDragContext *context,
613
g_return_if_fail (context != NULL);
615
gdk_drag_do_leave (context, time);
618
/* Destination side */
621
gdk_drag_status (GdkDragContext *context,
622
GdkDragAction action,
625
GdkDragContextPrivate *private;
626
GdkDragContext *src_context;
629
g_return_if_fail (context != NULL);
631
private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
633
src_context = gdk_drag_context_find (TRUE,
634
context->source_window,
635
context->dest_window);
639
GdkDragContextPrivate *private = GDK_DRAG_CONTEXT_PRIVATE_DATA (src_context);
641
if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
642
private->drag_status = GDK_DRAG_STATUS_DRAG;
644
tmp_event.dnd.type = GDK_DRAG_STATUS;
645
tmp_event.dnd.window = context->source_window;
646
tmp_event.dnd.send_event = FALSE;
647
tmp_event.dnd.context = src_context;
648
gdk_drag_context_ref (src_context);
650
tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
652
if (action == GDK_ACTION_DEFAULT)
655
src_context->action = action;
657
gdk_event_put (&tmp_event);
662
gdk_drop_reply (GdkDragContext *context,
666
g_return_if_fail (context != NULL);
670
gdk_drop_finish (GdkDragContext *context,
674
GdkDragContextPrivate *private;
675
GdkDragContext *src_context;
678
g_return_if_fail (context != NULL);
680
private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
682
src_context = gdk_drag_context_find (TRUE,
683
context->source_window,
684
context->dest_window);
687
tmp_event.dnd.type = GDK_DROP_FINISHED;
688
tmp_event.dnd.window = src_context->source_window;
689
tmp_event.dnd.send_event = FALSE;
690
tmp_event.dnd.context = src_context;
691
gdk_drag_context_ref (src_context);
693
gdk_event_put (&tmp_event);
699
gdk_window_register_dnd (GdkWindow *window)
701
g_return_if_fail (window != NULL);
703
if (GPOINTER_TO_INT (gdk_drawable_get_data (window, "gdk-dnd-registered")))
706
gdk_drawable_set_data (window, "gdk-dnd-registered", GINT_TO_POINTER(TRUE), NULL);
709
/*************************************************************
710
* gdk_drag_get_selection:
711
* Returns the selection atom for the current source window
715
*************************************************************/
718
gdk_drag_get_selection (GdkDragContext *context)
720
g_return_val_if_fail (context != NULL, GDK_NONE);
722
if (context->protocol == GDK_DRAG_PROTO_LOCAL)
723
return (GDK_DRAG_CONTEXT_PRIVATE_DATA (context))->local_selection;