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