1 /* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * 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 * Lesser General Public License for more details.
13 *
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.
18 */
19
20 /*
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team.
23 */
24
25 /*
26 * GTK+ DirectFB backend
27 * Copyright (C) 2001-2002 convergence integrated media GmbH
28 * Copyright (C) 2002-2004 convergence GmbH
29 * Written by Denis Oliver Kropp <dok@convergence.de> and
30 * Sven Neumann <sven@convergence.de>
31 */
32
33 #include "config.h"
34 #include "gdk.h"
35 #include "gdkdirectfb.h"
36 #include "gdkprivate-directfb.h"
37
38 #include "gdkdnd.h"
39 #include "gdkproperty.h"
40 #include "gdkalias.h"
41
42 typedef struct _GdkDragContextPrivate GdkDragContextPrivate;
43
44 typedef enum
45 {
46 GDK_DRAG_STATUS_DRAG,
47 GDK_DRAG_STATUS_MOTION_WAIT,
48 GDK_DRAG_STATUS_ACTION_WAIT,
49 GDK_DRAG_STATUS_DROP
50 } GtkDragStatus;
51
52 /* Structure that holds information about a drag in progress.
53 * this is used on both source and destination sides.
54 */
55 struct _GdkDragContextPrivate
56 {
57 GdkAtom local_selection;
58
59 guint16 last_x; /* Coordinates from last event */
60 guint16 last_y;
61 guint drag_status : 4; /* current status of drag */
62 };
63
64 /* Drag Contexts */
65
66 static GList *contexts = NULL;
67 static GdkDragContext *current_dest_drag = NULL;
68
69
70 #define GDK_DRAG_CONTEXT_PRIVATE_DATA(ctx) ((GdkDragContextPrivate *) GDK_DRAG_CONTEXT (ctx)->windowing_data)
71
72 static void gdk_drag_context_finalize (GObject *object);
73
G_DEFINE_TYPE(GdkDragContext,gdk_drag_context,G_TYPE_OBJECT)74 G_DEFINE_TYPE (GdkDragContext, gdk_drag_context, G_TYPE_OBJECT)
75
76 static void
77 gdk_drag_context_init (GdkDragContext *dragcontext)
78 {
79 GdkDragContextPrivate *private;
80
81 private = G_TYPE_INSTANCE_GET_PRIVATE (dragcontext,
82 GDK_TYPE_DRAG_CONTEXT,
83 GdkDragContextPrivate);
84
85 dragcontext->windowing_data = private;
86
87 contexts = g_list_prepend (contexts, dragcontext);
88 }
89
90 static void
gdk_drag_context_class_init(GdkDragContextClass * klass)91 gdk_drag_context_class_init (GdkDragContextClass *klass)
92 {
93 GObjectClass *object_class = G_OBJECT_CLASS (klass);
94
95 object_class->finalize = gdk_drag_context_finalize;
96
97 g_type_class_add_private (object_class, sizeof (GdkDragContextPrivate));
98 }
99
100 static void
gdk_drag_context_finalize(GObject * object)101 gdk_drag_context_finalize (GObject *object)
102 {
103 GdkDragContext *context = GDK_DRAG_CONTEXT (object);
104
105 g_list_free (context->targets);
106
107 if (context->source_window)
108 g_object_unref (context->source_window);
109
110 if (context->dest_window)
111 g_object_unref (context->dest_window);
112
113 contexts = g_list_remove (contexts, context);
114
115 G_OBJECT_CLASS (gdk_drag_context_parent_class)->finalize (object);
116 }
117
118 GdkDragContext *
gdk_drag_context_new(void)119 gdk_drag_context_new (void)
120 {
121 return g_object_new (gdk_drag_context_get_type (), NULL);
122 }
123
124 void
gdk_drag_context_ref(GdkDragContext * context)125 gdk_drag_context_ref (GdkDragContext *context)
126 {
127 g_object_ref (context);
128 }
129
130 void
gdk_drag_context_unref(GdkDragContext * context)131 gdk_drag_context_unref (GdkDragContext *context)
132 {
133 g_object_unref (context);
134 }
135
136 static GdkDragContext *
gdk_drag_context_find(gboolean is_source,GdkWindow * source,GdkWindow * dest)137 gdk_drag_context_find (gboolean is_source,
138 GdkWindow *source,
139 GdkWindow *dest)
140 {
141 GdkDragContext *context;
142 GdkDragContextPrivate *private;
143 GList *list;
144
145 for (list = contexts; list; list = list->next)
146 {
147 context = (GdkDragContext *) list->data;
148 private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
149
150 if ((!context->is_source == !is_source) &&
151 ((source == NULL) ||
152 (context->source_window && (context->source_window == source))) &&
153 ((dest == NULL) ||
154 (context->dest_window && (context->dest_window == dest))))
155 return context;
156 }
157
158 return NULL;
159 }
160
161
162 /************************** Public API ***********************/
163
164 void
_gdk_dnd_init(void)165 _gdk_dnd_init (void)
166 {
167 }
168
169 /* Source side */
170
171 static void
local_send_leave(GdkDragContext * context,guint32 time)172 local_send_leave (GdkDragContext *context,
173 guint32 time)
174 {
175 GdkEvent event;
176
177 if ((current_dest_drag != NULL) &&
178 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
179 (current_dest_drag->source_window == context->source_window))
180 {
181 event.dnd.type = GDK_DRAG_LEAVE;
182 event.dnd.window = context->dest_window;
183 /* Pass ownership of context to the event */
184 event.dnd.context = current_dest_drag;
185 event.dnd.send_event = FALSE;
186 event.dnd.time = time;
187
188 current_dest_drag = NULL;
189
190 gdk_event_put (&event);
191 }
192 }
193
194 static void
local_send_enter(GdkDragContext * context,guint32 time)195 local_send_enter (GdkDragContext *context,
196 guint32 time)
197 {
198 GdkDragContextPrivate *private;
199 GdkDragContext *new_context;
200 GdkEvent event;
201
202 private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
203
204 if (!private->local_selection)
205 private->local_selection = gdk_atom_intern ("LocalDndSelection", FALSE);
206
207 if (current_dest_drag != NULL)
208 {
209 g_object_unref (current_dest_drag);
210 current_dest_drag = NULL;
211 }
212
213 new_context = gdk_drag_context_new ();
214 new_context->protocol = GDK_DRAG_PROTO_LOCAL;
215 new_context->is_source = FALSE;
216
217 new_context->source_window = g_object_ref (context->source_window);
218
219 new_context->dest_window = g_object_ref (context->dest_window);
220
221 new_context->targets = g_list_copy (context->targets);
222
223 gdk_window_set_events (new_context->source_window,
224 gdk_window_get_events (new_context->source_window) |
225 GDK_PROPERTY_CHANGE_MASK);
226 new_context->actions = context->actions;
227
228 event.dnd.type = GDK_DRAG_ENTER;
229 event.dnd.window = context->dest_window;
230 event.dnd.send_event = FALSE;
231 event.dnd.context = new_context;
232 event.dnd.time = time;
233
234 current_dest_drag = new_context;
235
236 (GDK_DRAG_CONTEXT_PRIVATE_DATA (new_context))->local_selection =
237 private->local_selection;
238
239 gdk_event_put (&event);
240 }
241
242 static void
local_send_motion(GdkDragContext * context,gint x_root,gint y_root,GdkDragAction action,guint32 time)243 local_send_motion (GdkDragContext *context,
244 gint x_root,
245 gint y_root,
246 GdkDragAction action,
247 guint32 time)
248 {
249 GdkEvent event;
250
251 if ((current_dest_drag != NULL) &&
252 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
253 (current_dest_drag->source_window == context->source_window))
254 {
255 event.dnd.type = GDK_DRAG_MOTION;
256 event.dnd.window = current_dest_drag->dest_window;
257 event.dnd.send_event = FALSE;
258 event.dnd.context = current_dest_drag;
259 event.dnd.time = time;
260 event.dnd.x_root = x_root;
261 event.dnd.y_root = y_root;
262
263 current_dest_drag->suggested_action = action;
264 current_dest_drag->actions = action;
265
266 (GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag))->last_x = x_root;
267 (GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag))->last_y = y_root;
268
269 GDK_DRAG_CONTEXT_PRIVATE_DATA (context)->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
270
271 gdk_event_put (&event);
272 }
273 }
274
275 static void
local_send_drop(GdkDragContext * context,guint32 time)276 local_send_drop (GdkDragContext *context,
277 guint32 time)
278 {
279 GdkEvent event;
280
281 if ((current_dest_drag != NULL) &&
282 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
283 (current_dest_drag->source_window == context->source_window))
284 {
285 GdkDragContextPrivate *private;
286 private = GDK_DRAG_CONTEXT_PRIVATE_DATA (current_dest_drag);
287
288 event.dnd.type = GDK_DROP_START;
289 event.dnd.window = current_dest_drag->dest_window;
290 event.dnd.send_event = FALSE;
291 event.dnd.context = current_dest_drag;
292 event.dnd.time = time;
293 event.dnd.x_root = private->last_x;
294 event.dnd.y_root = private->last_y;
295
296 gdk_event_put (&event);
297 }
298 }
299
300 static void
gdk_drag_do_leave(GdkDragContext * context,guint32 time)301 gdk_drag_do_leave (GdkDragContext *context,
302 guint32 time)
303 {
304 if (context->dest_window)
305 {
306 switch (context->protocol)
307 {
308 case GDK_DRAG_PROTO_LOCAL:
309 local_send_leave (context, time);
310 break;
311
312 default:
313 break;
314 }
315
316 g_object_unref (context->dest_window);
317 context->dest_window = NULL;
318 }
319 }
320
321 GdkDragContext *
gdk_drag_begin(GdkWindow * window,GList * targets)322 gdk_drag_begin (GdkWindow *window,
323 GList *targets)
324 {
325 GList *list;
326 GdkDragContext *new_context;
327
328 g_return_val_if_fail (window != NULL, NULL);
329
330 g_object_ref (window);
331
332 new_context = gdk_drag_context_new ();
333 new_context->is_source = TRUE;
334 new_context->source_window = window;
335 new_context->targets = NULL;
336 new_context->actions = 0;
337
338 for (list = targets; list; list = list->next)
339 new_context->targets = g_list_append (new_context->targets, list->data);
340
341 return new_context;
342 }
343
344 guint32
gdk_drag_get_protocol_for_display(GdkDisplay * display,guint32 xid,GdkDragProtocol * protocol)345 gdk_drag_get_protocol_for_display(GdkDisplay *display, guint32 xid,
346 GdkDragProtocol *protocol)
347 {
348 GdkWindow *window;
349
350 window = gdk_window_lookup ((GdkNativeWindow) xid);
351
352 if (window &&
353 GPOINTER_TO_INT (gdk_drawable_get_data (window, "gdk-dnd-registered")))
354 {
355 *protocol = GDK_DRAG_PROTO_LOCAL;
356 return xid;
357 }
358
359 *protocol = GDK_DRAG_PROTO_NONE;
360 return 0;
361 }
362
363 void
gdk_drag_find_window_for_screen(GdkDragContext * context,GdkWindow * drag_window,GdkScreen * screen,gint x_root,gint y_root,GdkWindow ** dest_window,GdkDragProtocol * protocol)364 gdk_drag_find_window_for_screen (GdkDragContext *context,
365 GdkWindow *drag_window,
366 GdkScreen *screen,
367 gint x_root,
368 gint y_root,
369 GdkWindow **dest_window,
370 GdkDragProtocol *protocol)
371 {
372 GdkWindow *dest;
373
374 g_return_if_fail (context != NULL);
375
376 dest = gdk_window_get_pointer (NULL, &x_root, &y_root, NULL);
377
378 if (context->dest_window != dest)
379 {
380 guint32 recipient;
381
382 /* Check if new destination accepts drags, and which protocol */
383 if ((recipient = gdk_drag_get_protocol (GDK_WINDOW_DFB_ID (dest),
384 protocol)))
385 {
386 *dest_window = gdk_window_lookup ((GdkNativeWindow) recipient);
387 if (dest_window)
388 g_object_ref (*dest_window);
389 }
390 else
391 *dest_window = NULL;
392 }
393 else
394 {
395 *dest_window = context->dest_window;
396 if (*dest_window)
397 g_object_ref (*dest_window);
398
399 *protocol = context->protocol;
400 }
401 }
402
403 gboolean
gdk_drag_motion(GdkDragContext * context,GdkWindow * dest_window,GdkDragProtocol protocol,gint x_root,gint y_root,GdkDragAction suggested_action,GdkDragAction possible_actions,guint32 time)404 gdk_drag_motion (GdkDragContext *context,
405 GdkWindow *dest_window,
406 GdkDragProtocol protocol,
407 gint x_root,
408 gint y_root,
409 GdkDragAction suggested_action,
410 GdkDragAction possible_actions,
411 guint32 time)
412 {
413 GdkDragContextPrivate *private;
414
415 g_return_val_if_fail (context != NULL, FALSE);
416
417 private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
418
419 if (context->dest_window != dest_window)
420 {
421 GdkEvent event;
422
423 /* Send a leave to the last destination */
424 gdk_drag_do_leave (context, time);
425 private->drag_status = GDK_DRAG_STATUS_DRAG;
426
427 /* Check if new destination accepts drags, and which protocol */
428 if (dest_window)
429 {
430 context->dest_window = g_object_ref (dest_window);
431 context->protocol = protocol;
432
433 switch (protocol)
434 {
435 case GDK_DRAG_PROTO_LOCAL:
436 local_send_enter (context, time);
437 break;
438
439 default:
440 break;
441 }
442 context->suggested_action = suggested_action;
443 }
444 else
445 {
446 context->dest_window = NULL;
447 context->action = 0;
448 }
449
450 /* Push a status event, to let the client know that
451 * the drag changed
452 */
453
454 event.dnd.type = GDK_DRAG_STATUS;
455 event.dnd.window = context->source_window;
456 /* We use this to signal a synthetic status. Perhaps
457 * we should use an extra field...
458 */
459 event.dnd.send_event = TRUE;
460 event.dnd.context = context;
461 event.dnd.time = time;
462
463 gdk_event_put (&event);
464 }
465 else
466 {
467 context->suggested_action = suggested_action;
468 }
469
470 /* Send a drag-motion event */
471
472 private->last_x = x_root;
473 private->last_y = y_root;
474
475 if (context->dest_window)
476 {
477 if (private->drag_status == GDK_DRAG_STATUS_DRAG)
478 {
479 switch (context->protocol)
480 {
481 case GDK_DRAG_PROTO_LOCAL:
482 local_send_motion (context,
483 x_root, y_root, suggested_action, time);
484 break;
485
486 case GDK_DRAG_PROTO_NONE:
487 g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
488 break;
489 default:
490 break;
491 }
492 }
493 else
494 return TRUE;
495 }
496
497 return FALSE;
498 }
499
500 void
gdk_drag_drop(GdkDragContext * context,guint32 time)501 gdk_drag_drop (GdkDragContext *context,
502 guint32 time)
503 {
504 g_return_if_fail (context != NULL);
505
506 if (context->dest_window)
507 {
508 switch (context->protocol)
509 {
510 case GDK_DRAG_PROTO_LOCAL:
511 local_send_drop (context, time);
512 break;
513 case GDK_DRAG_PROTO_NONE:
514 g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
515 break;
516 default:
517 break;
518 }
519 }
520 }
521
522 void
gdk_drag_abort(GdkDragContext * context,guint32 time)523 gdk_drag_abort (GdkDragContext *context,
524 guint32 time)
525 {
526 g_return_if_fail (context != NULL);
527
528 gdk_drag_do_leave (context, time);
529 }
530
531 /* Destination side */
532
533 void
gdk_drag_status(GdkDragContext * context,GdkDragAction action,guint32 time)534 gdk_drag_status (GdkDragContext *context,
535 GdkDragAction action,
536 guint32 time)
537 {
538 GdkDragContextPrivate *private;
539 GdkDragContext *src_context;
540 GdkEvent event;
541
542 g_return_if_fail (context != NULL);
543
544 private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
545
546 src_context = gdk_drag_context_find (TRUE,
547 context->source_window,
548 context->dest_window);
549
550 if (src_context)
551 {
552 GdkDragContextPrivate *private;
553
554 private = GDK_DRAG_CONTEXT_PRIVATE_DATA (src_context);
555
556 if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
557 private->drag_status = GDK_DRAG_STATUS_DRAG;
558
559 event.dnd.type = GDK_DRAG_STATUS;
560 event.dnd.window = src_context->source_window;
561 event.dnd.send_event = FALSE;
562 event.dnd.context = src_context;
563 event.dnd.time = time;
564
565 src_context->action = action;
566
567 gdk_event_put (&event);
568 }
569 }
570
571 void
gdk_drop_reply(GdkDragContext * context,gboolean ok,guint32 time)572 gdk_drop_reply (GdkDragContext *context,
573 gboolean ok,
574 guint32 time)
575 {
576 g_return_if_fail (context != NULL);
577 }
578
579 void
gdk_drop_finish(GdkDragContext * context,gboolean success,guint32 time)580 gdk_drop_finish (GdkDragContext *context,
581 gboolean success,
582 guint32 time)
583 {
584 GdkDragContextPrivate *private;
585 GdkDragContext *src_context;
586 GdkEvent event;
587
588 g_return_if_fail (context != NULL);
589
590 private = GDK_DRAG_CONTEXT_PRIVATE_DATA (context);
591
592 src_context = gdk_drag_context_find (TRUE,
593 context->source_window,
594 context->dest_window);
595 if (src_context)
596 {
597 g_object_ref (src_context);
598
599 event.dnd.type = GDK_DROP_FINISHED;
600 event.dnd.window = src_context->source_window;
601 event.dnd.send_event = FALSE;
602 event.dnd.context = src_context;
603
604 gdk_event_put (&event);
605 }
606 }
607
608 gboolean
gdk_drag_drop_succeeded(GdkDragContext * context)609 gdk_drag_drop_succeeded (GdkDragContext *context)
610 {
611 g_warning("gdk_drag_drop_succeeded unimplemented \n");
612 return TRUE;
613 }
614
615 void
gdk_window_register_dnd(GdkWindow * window)616 gdk_window_register_dnd (GdkWindow *window)
617 {
618 g_return_if_fail (window != NULL);
619
620 if (GPOINTER_TO_INT (gdk_drawable_get_data (window, "gdk-dnd-registered")))
621 return;
622
623 gdk_drawable_set_data (window, "gdk-dnd-registered",
624 GINT_TO_POINTER (TRUE), NULL);
625 }
626
627 /*************************************************************
628 * gdk_drag_get_selection:
629 * Returns the selection atom for the current source window
630 * arguments:
631 *
632 * results:
633 *************************************************************/
634
635 GdkAtom
gdk_drag_get_selection(GdkDragContext * context)636 gdk_drag_get_selection (GdkDragContext *context)
637 {
638 g_return_val_if_fail (context != NULL, GDK_NONE);
639
640 if (context->protocol == GDK_DRAG_PROTO_LOCAL)
641 return (GDK_DRAG_CONTEXT_PRIVATE_DATA (context))->local_selection;
642 else
643 return GDK_NONE;
644 }
645
646 #define __GDK_DND_X11_C__
647 #include "gdkaliasdef.c"
648