]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkdnd-win32.c
Merge branch 'gdk-backend-wayland'
[~andy/gtk] / gdk / win32 / gdkdnd-win32.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 2001 Archaeopteryx Software Inc.
4  * Copyright (C) 1998-2002 Tor Lillqvist
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /*
23  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
24  * file for a list of people on the GTK+ Team.  See the ChangeLog
25  * files for a list of changes.  These files are distributed with
26  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27  */
28
29 #include "config.h"
30 #include <string.h>
31
32 #include <io.h>
33 #include <fcntl.h>
34
35 /*
36  * Comment from the old OLE2 DND code that is being merged in. Note
37  * that this comment might not fully reflect reality as the code
38  * obviously will have to be modified in this merge. Especially the
39  * talk about supporting other than UTF-8 text is bogus, that will not
40  * happen.
41  *
42  * Support for OLE-2 drag and drop added at Archaeopteryx Software, 2001
43  * For more information, contact Stephan R.A. Deibel (sdeibel@archaeopteryx.com)
44  *
45  * Notes on implementation:
46  *
47  * This is a first pass at OLE2 support. It only supports text and unicode text
48  * data types, and file list dnd (which is handled seperately as it predates OLE2
49  * both in this implementation and on Windows in general).
50  *
51  * As such, the data type conversion from gdk selection targets to OLE2 CF_* data
52  * type specifiers is partially hardwired. Fixing this is complicated by (a) the
53  * fact that the widget's declared selection types aren't accessible in calls here
54  * that need to declare the corresponding OLE2 data types, and (b) there isn't a
55  * 1-1 correspondence between gdk target types and OLE2 types. The former needs
56  * some redesign in gtk dnd (something a gdk/gtk expert should do; I have tried
57  * and failed!). As an example of the latter: gdk STRING, TEXT, COMPOUND_TEXT map
58  * to CF_TEXT, CF_OEMTEXT, and CF_UNICODETEXT but as a group and with conversions
59  * necessary for various combinations. Currently, the code here (and in
60  * gdkdnd-win32.c) can handle gdk STRING and TEXT but not COMPOUND_TEXT, and OLE2
61  * CF_TEXT and CF_UNICODETEXT but not CF_OEMTEXT. The necessary conversions are
62  * supplied by the implementation here.
63  *
64  * Note that in combination with another hack originated by Archaeopteryx
65  * Software, the text conversions here may go to utf-8 unicode as the standard
66  * within-gtk target or to single-byte ascii when the USE_ACP_TEXT compilation
67  * flag is TRUE. This mode was added to support applications that aren't using
68  * utf-8 across the gtk/gdk API but instead use single-byte ascii according to
69  * the current Windows code page. See gdkim-win32.c for more info on that.
70  *
71  */
72  
73 #define INITGUID
74
75 #include "gdkdnd.h"
76 #include "gdkproperty.h"
77 #include "gdkinternals.h"
78 #include "gdkprivate-win32.h"
79 #include "gdkwin32.h"
80 #include "gdkwin32dnd.h"
81 #include "gdk/gdkdndprivate.h"
82
83 #include <ole2.h>
84
85 #include <shlobj.h>
86 #include <shlguid.h>
87
88 #include <gdk/gdk.h>
89 #include <glib/gstdio.h>
90
91 typedef struct _GdkDragContextPrivateWin32 GdkDragContextPrivateWin32;
92
93 typedef enum {
94   GDK_DRAG_STATUS_DRAG,
95   GDK_DRAG_STATUS_MOTION_WAIT,
96   GDK_DRAG_STATUS_ACTION_WAIT,
97   GDK_DRAG_STATUS_DROP
98 } GdkDragStatus;
99
100 /* Structure that holds information about a drag in progress.
101  * this is used on both source and destination sides.
102  */
103 struct _GdkDragContextPrivateWin32 {
104   gboolean being_finalized;
105   gint ref_count;
106   IUnknown *iface;
107   DWORD last_key_state;
108   POINT last_pt;                /* Coordinates from last event */
109   guint drag_status : 4;        /* Current status of drag */
110   guint drop_failed : 1;        /* Whether the drop was unsuccessful */
111 };
112
113 #define PRIVATE_DATA(context) (GDK_WIN32_DRAG_CONTEXT (context)->windowing_data)
114
115 static GList *contexts;
116 static GdkDragContext *current_dest_drag = NULL;
117
118 struct _GdkWin32DragContext
119 {
120   GdkDragContext context;
121   
122   GdkDragContextPrivateWin32 *windowing_data;
123 };
124
125 struct _GdkWin32DragContextClass
126 {
127   GdkDragContextClass parent_class;
128 };
129
130 G_DEFINE_TYPE (GdkWin32DragContext, gdk_win32_drag_context, GDK_TYPE_DRAG_CONTEXT)
131
132 static gboolean use_ole2_dnd = FALSE;
133
134 static void
135 gdk_win32_drag_context_init (GdkWin32DragContext *dragcontext)
136 {
137   GdkDragContextPrivateWin32 *private;
138
139   private = G_TYPE_INSTANCE_GET_PRIVATE (dragcontext,
140                                          GDK_TYPE_DRAG_CONTEXT,
141                                          GdkDragContextPrivateWin32);
142
143   dragcontext->windowing_data = private;
144
145   if (!use_ole2_dnd)
146     {
147       contexts = g_list_prepend (contexts, dragcontext);
148     }
149   else
150     {
151       private->being_finalized = FALSE;
152       private->ref_count = 1;
153       private->iface = NULL;
154     }
155
156   GDK_NOTE (DND, g_print ("gdk_drag_context_init %p\n", dragcontext));
157 }
158
159 static void
160 gdk_win32_drag_context_finalize (GObject *object)
161 {
162   GdkDragContext *context = GDK_DRAG_CONTEXT (object);
163
164   GDK_NOTE (DND, g_print ("gdk_drag_context_finalize %p\n", object));
165
166   g_list_free (context->targets);
167
168   if (context->source_window)
169     g_object_unref (context->source_window);
170
171   if (context->dest_window)
172     g_object_unref (context->dest_window);
173
174   if (!use_ole2_dnd)
175     {
176       contexts = g_list_remove (contexts, context);
177
178       if (context == current_dest_drag)
179         current_dest_drag = NULL;
180     }
181   else
182     {
183       GdkDragContextPrivateWin32 *private = PRIVATE_DATA (context);
184       if (private->iface)
185         {
186           private->being_finalized = TRUE;
187           private->iface->lpVtbl->Release (private->iface);
188           private->iface = NULL;
189         }
190     }
191
192   G_OBJECT_CLASS (gdk_win32_drag_context_parent_class)->finalize (object);
193 }
194
195 /* Drag Contexts */
196
197 GdkDragContext *
198 gdk_drag_context_new (void)
199 {
200   return g_object_new (GDK_TYPE_DRAG_CONTEXT, NULL);
201 }
202
203 static GdkDragContext *
204 gdk_drag_context_find (gboolean   is_source,
205                        GdkWindow *source,
206                        GdkWindow *dest)
207 {
208   GList *tmp_list = contexts;
209   GdkDragContext *context;
210   GdkDragContextPrivateWin32 *private;
211
212   while (tmp_list)
213     {
214       context = (GdkDragContext *)tmp_list->data;
215       private = PRIVATE_DATA (context);
216
217       if ((!context->is_source == !is_source) &&
218           ((source == NULL) || (context->source_window && (context->source_window == source))) &&
219           ((dest == NULL) || (context->dest_window && (context->dest_window == dest))))
220         return context;
221
222       tmp_list = tmp_list->next;
223     }
224
225   return NULL;
226 }
227
228 #define PRINT_GUID(guid) \
229   g_print ("%.08lx-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
230            ((gulong *)  guid)[0], \
231            ((gushort *) guid)[2], \
232            ((gushort *) guid)[3], \
233            ((guchar *)  guid)[8], \
234            ((guchar *)  guid)[9], \
235            ((guchar *)  guid)[10], \
236            ((guchar *)  guid)[11], \
237            ((guchar *)  guid)[12], \
238            ((guchar *)  guid)[13], \
239            ((guchar *)  guid)[14], \
240            ((guchar *)  guid)[15]);
241
242
243 static FORMATETC *formats;
244 static int nformats;
245
246 typedef struct {
247   IDropTarget idt;
248   GdkDragContext *context;
249 } target_drag_context;
250
251 typedef struct {
252   IDropSource ids;
253   GdkDragContext *context;
254 } source_drag_context;
255
256 typedef struct {
257   IDataObject ido;
258   int ref_count;
259   GdkDragContext *context;
260 } data_object;
261
262 typedef struct {
263   IEnumFORMATETC ief;
264   int ref_count;
265   int ix;
266 } enum_formats;
267
268 static source_drag_context *pending_src_context = NULL;
269 static IDataObject *dnd_data = NULL;
270
271 static enum_formats *enum_formats_new (void);
272
273 /* map windows -> target drag contexts. The table
274  * owns a ref to both objects.
275  */
276 static GHashTable* target_ctx_for_window = NULL;
277
278 static ULONG STDMETHODCALLTYPE
279 idroptarget_addref (LPDROPTARGET This)
280 {
281   target_drag_context *ctx = (target_drag_context *) This;
282   GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
283   int ref_count = ++private->ref_count;
284
285   GDK_NOTE (DND, g_print ("idroptarget_addref %p %d\n", This, ref_count));
286   g_object_ref (G_OBJECT (ctx->context));
287
288   return ref_count;
289 }
290
291 static HRESULT STDMETHODCALLTYPE
292 idroptarget_queryinterface (LPDROPTARGET This,
293                             REFIID       riid,
294                             LPVOID      *ppvObject)
295 {
296   GDK_NOTE (DND, {
297       g_print ("idroptarget_queryinterface %p ", This);
298       PRINT_GUID (riid);
299     });
300
301   *ppvObject = NULL;
302
303   if (IsEqualGUID (riid, &IID_IUnknown))
304     {
305       GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
306       idroptarget_addref (This);
307       *ppvObject = This;
308       return S_OK;
309     }
310   else if (IsEqualGUID (riid, &IID_IDropTarget))
311     {
312       GDK_NOTE (DND, g_print ("...IDropTarget S_OK\n"));
313       idroptarget_addref (This);
314       *ppvObject = This;
315       return S_OK;
316     }
317   else
318     {
319       GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
320       return E_NOINTERFACE;
321     }
322 }
323
324 static ULONG STDMETHODCALLTYPE
325 idroptarget_release (LPDROPTARGET This)
326 {
327   target_drag_context *ctx = (target_drag_context *) This;
328   GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
329   int ref_count = --private->ref_count;
330
331   GDK_NOTE (DND, g_print ("idroptarget_release %p %d\n", This, ref_count));
332
333   if (!private->being_finalized)
334     g_object_unref (G_OBJECT (ctx->context));
335
336   if (ref_count == 0)
337     g_free (This);
338
339   return ref_count;
340 }
341
342 #if 0
343
344 static GdkAtom
345 cf_to_atom (CLIPFORMAT cf)
346 {
347   switch (cf)
348     {
349     case CF_UNICODETEXT:
350       return _utf8_string;
351     case CF_HDROP:
352       return _text_uri_list;
353     case CF_DIB:
354       return _image_bmp;
355     }
356
357   if (cf == _cf_url)
358     return _text_uri_list;
359
360   if (cf == _cf_html_format || cf == _cf_text_html)
361     return _text_html;
362
363   return GDK_NONE;
364 }
365
366 #endif
367
368 static GdkDragAction
369 get_suggested_action (DWORD grfKeyState)
370 {
371   /* This is the yucky Windows standard: Force link action if both
372    * Control and Alt are down, copy if Control is down alone, move if
373    * Alt is down alone, or use default of move within the app or copy
374    * when origin of the drag is in another app.
375    */
376   if (grfKeyState & MK_CONTROL && grfKeyState & MK_SHIFT)
377     return GDK_ACTION_LINK; /* Link action not supported */
378   else if (grfKeyState & MK_CONTROL)
379     return GDK_ACTION_COPY;
380   else if (grfKeyState & MK_ALT)
381     return GDK_ACTION_MOVE;
382 #if 0 /* Default is always copy for now */
383   else if (_dnd_source_state == GDK_WIN32_DND_DRAGGING)
384     return GDK_ACTION_MOVE;
385 #endif
386   else
387     return GDK_ACTION_COPY;
388   /* Any way to determine when to add in DROPEFFECT_SCROLL? */
389 }
390
391 /* Process pending events -- we don't want to service non-GUI events
392  * forever so do one iteration and then do more only if there's a
393  * pending GDK event.
394  */
395 static void
396 process_pending_events ()
397 {
398   g_main_context_iteration (NULL, FALSE);
399   while (_gdk_event_queue_find_first (_gdk_display))
400     g_main_context_iteration (NULL, FALSE);
401 }
402
403 static DWORD
404 drop_effect_for_action (GdkDragAction action)
405 {
406   switch (action)
407     {
408     case GDK_ACTION_MOVE:
409       return DROPEFFECT_MOVE;
410     case GDK_ACTION_LINK:
411       return DROPEFFECT_LINK;
412     case GDK_ACTION_COPY:
413       return DROPEFFECT_COPY;
414     default:
415       return DROPEFFECT_NONE;
416     }
417 }
418
419 static void
420 dnd_event_put (GdkEventType    type,
421                GdkDragContext *context,
422                const POINTL    pt,
423                gboolean        to_dest_window)
424 {
425   GdkEvent *e;
426
427   e = gdk_event_new (type);
428
429   if (to_dest_window)
430     e->dnd.window = context->dest_window;
431   else
432     e->dnd.window = context->source_window;
433   e->dnd.send_event = FALSE;
434   e->dnd.context = g_object_ref (context);
435   e->dnd.time = GDK_CURRENT_TIME;
436   e->dnd.x_root = pt.x + _gdk_offset_x;
437   e->dnd.y_root = pt.x + _gdk_offset_y;
438
439   if (e->dnd.window != NULL)
440     g_object_ref (e->dnd.window);
441
442   gdk_event_set_device (e, gdk_drag_context_get_device (context));
443
444   GDK_NOTE (EVENTS, _gdk_win32_print_event (e));
445   gdk_event_put (e);
446   gdk_event_free (e);
447 }
448
449 static HRESULT STDMETHODCALLTYPE
450 idroptarget_dragenter (LPDROPTARGET This,
451                        LPDATAOBJECT pDataObj,
452                        DWORD        grfKeyState,
453                        POINTL       pt,
454                        LPDWORD      pdwEffect)
455 {
456   target_drag_context *ctx = (target_drag_context *) This;
457
458   GDK_NOTE (DND, g_print ("idroptarget_dragenter %p S_OK\n", This));
459
460   ctx->context->suggested_action = get_suggested_action (grfKeyState);
461   dnd_event_put (GDK_DRAG_ENTER, ctx->context, pt, TRUE);
462   process_pending_events ();
463   *pdwEffect = drop_effect_for_action (ctx->context->action);
464
465   /* Assume that target can accept the data: In fact it may fail but
466    * we are not really set up to query the target!
467    */
468   return S_OK;
469 }
470
471 static HRESULT STDMETHODCALLTYPE
472 idroptarget_dragover (LPDROPTARGET This,
473                       DWORD        grfKeyState,
474                       POINTL       pt,
475                       LPDWORD      pdwEffect)
476 {
477   target_drag_context *ctx = (target_drag_context *) This;
478
479   GDK_NOTE (DND, g_print ("idroptarget_dragover %p S_OK\n", This));
480
481   ctx->context->suggested_action = get_suggested_action (grfKeyState);
482   dnd_event_put (GDK_DRAG_MOTION, ctx->context, pt, TRUE);
483   process_pending_events ();
484   *pdwEffect = drop_effect_for_action (ctx->context->action);
485
486   return S_OK;
487 }
488
489 static HRESULT STDMETHODCALLTYPE
490 idroptarget_dragleave (LPDROPTARGET This)
491 {
492   target_drag_context *ctx = (target_drag_context *) This;
493   POINTL pt = { 0, 0 };
494
495   GDK_NOTE (DND, g_print ("idroptarget_dragleave %p S_OK\n", This));
496
497   dnd_event_put (GDK_DRAG_LEAVE, ctx->context, pt, TRUE);
498   process_pending_events ();
499
500   return S_OK;
501 }
502
503 static HRESULT STDMETHODCALLTYPE
504 idroptarget_drop (LPDROPTARGET This,
505                   LPDATAOBJECT pDataObj,
506                   DWORD        grfKeyState,
507                   POINTL       pt,
508                   LPDWORD      pdwEffect)
509 {
510   target_drag_context *ctx = (target_drag_context *) This;
511
512   GDK_NOTE (DND, g_print ("idroptarget_drop %p ", This));
513
514   if (pDataObj == NULL)
515     {
516       GDK_NOTE (DND, g_print ("E_POINTER\n"));
517       return E_POINTER;
518     }
519
520   dnd_data = pDataObj;
521
522   ctx->context->suggested_action = get_suggested_action (grfKeyState);
523   dnd_event_put (GDK_DROP_START, ctx->context, pt, TRUE);
524   process_pending_events ();
525
526   dnd_data = NULL;
527
528   /* Notify OLE of copy or move */
529   if (_dnd_target_state != GDK_WIN32_DND_DROPPED)
530     *pdwEffect = DROPEFFECT_NONE;
531   else
532     *pdwEffect = drop_effect_for_action (ctx->context->action);
533
534   GDK_NOTE (DND, g_print ("S_OK\n"));
535
536   return S_OK;
537 }
538
539 static ULONG STDMETHODCALLTYPE
540 idropsource_addref (LPDROPSOURCE This)
541 {
542   source_drag_context *ctx = (source_drag_context *) This;
543   GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
544   int ref_count = ++private->ref_count;
545
546   GDK_NOTE (DND, g_print ("idropsource_addref %p %d\n", This, ref_count));
547   g_object_ref (G_OBJECT (ctx->context));
548
549   return ref_count;
550 }
551
552 static HRESULT STDMETHODCALLTYPE
553 idropsource_queryinterface (LPDROPSOURCE This,
554                             REFIID       riid,
555                             LPVOID      *ppvObject)
556 {
557   GDK_NOTE (DND, {
558       g_print ("idropsource_queryinterface %p ", This);
559       PRINT_GUID (riid);
560     });
561
562   *ppvObject = NULL;
563
564   if (IsEqualGUID (riid, &IID_IUnknown))
565     {
566       GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
567       idropsource_addref (This);
568       *ppvObject = This;
569       return S_OK;
570     }
571   else if (IsEqualGUID (riid, &IID_IDropSource))
572     {
573       GDK_NOTE (DND, g_print ("...IDropSource S_OK\n"));
574       idropsource_addref (This);
575       *ppvObject = This;
576       return S_OK;
577     }
578   else
579     {
580       GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
581       return E_NOINTERFACE;
582     }
583 }
584
585 static ULONG STDMETHODCALLTYPE
586 idropsource_release (LPDROPSOURCE This)
587 {
588   source_drag_context *ctx = (source_drag_context *) This;
589   GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
590   int ref_count = --private->ref_count;
591
592   GDK_NOTE (DND, g_print ("idropsource_release %p %d\n", This, ref_count));
593
594   if (!private->being_finalized)
595     g_object_unref (G_OBJECT (ctx->context));
596
597   if (ref_count == 0)
598     g_free (This);
599
600   return ref_count;
601 }
602
603 /* Emit GDK events for any changes in mouse events or control key
604  * state since the last recorded state. Return true if any events
605  * have been emitted and false otherwise.
606  */
607 static gboolean
608 send_change_events (GdkDragContext *ctx,
609                     DWORD           key_state,
610                     gboolean        esc_pressed)
611 {
612   GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx);
613   POINT pt;
614   gboolean changed = FALSE;
615   HWND hwnd = GDK_WINDOW_HWND (ctx->source_window);
616   LPARAM lparam;
617   WPARAM wparam;
618
619   if (!API_CALL (GetCursorPos, (&pt)))
620     return FALSE;
621
622   if (!API_CALL (ScreenToClient, (hwnd, &pt)))
623     return FALSE;
624
625   if (pt.x != private->last_pt.x || pt.y != private->last_pt.y ||
626       key_state != private->last_key_state)
627     {
628       lparam = MAKELPARAM (pt.x, pt.y);
629       wparam = key_state;
630       if (pt.x != private->last_pt.x || pt.y != private->last_pt.y)
631         {
632           GDK_NOTE (DND, g_print ("Sending WM_MOUSEMOVE (%ld,%ld)\n", pt.x, pt.y));
633           SendMessage (hwnd, WM_MOUSEMOVE, wparam, lparam);
634         }
635
636       if ((key_state & MK_LBUTTON) != (private->last_key_state & MK_LBUTTON))
637         {
638           if (key_state & MK_LBUTTON)
639             SendMessage (hwnd, WM_LBUTTONDOWN, wparam, lparam);
640           else
641             SendMessage (hwnd, WM_LBUTTONUP, wparam, lparam);
642         }
643       if ((key_state & MK_MBUTTON) != (private->last_key_state & MK_MBUTTON))
644         {
645           if (key_state & MK_MBUTTON)
646             SendMessage (hwnd, WM_MBUTTONDOWN, wparam, lparam);
647           else
648             SendMessage (hwnd, WM_MBUTTONUP, wparam, lparam);
649         }
650       if ((key_state & MK_RBUTTON) != (private->last_key_state & MK_RBUTTON))
651         {
652           if (key_state & MK_RBUTTON)
653             SendMessage (hwnd, WM_RBUTTONDOWN, wparam, lparam);
654           else
655             SendMessage (hwnd, WM_RBUTTONUP, wparam, lparam);
656         }
657       if ((key_state & MK_CONTROL) != (private->last_key_state & MK_CONTROL))
658         {
659           if (key_state & MK_CONTROL)
660             SendMessage (hwnd, WM_KEYDOWN, VK_CONTROL, 0);
661           else
662             SendMessage (hwnd, WM_KEYUP, VK_CONTROL, 0);
663         }
664       if ((key_state & MK_SHIFT) != (private->last_key_state & MK_SHIFT))
665         {
666           if (key_state & MK_CONTROL)
667             SendMessage (hwnd, WM_KEYDOWN, VK_SHIFT, 0);
668           else
669             SendMessage (hwnd, WM_KEYUP, VK_SHIFT, 0);
670         }
671
672       changed = TRUE;
673       private->last_key_state = key_state;
674       private->last_pt = pt;
675     }
676
677   if (esc_pressed)
678     {
679       GDK_NOTE (DND, g_print ("Sending a escape key down message to %p\n", hwnd));
680       SendMessage (hwnd, WM_KEYDOWN, VK_ESCAPE, 0);
681       changed = TRUE;
682     }
683
684   return changed;
685 }
686
687 static HRESULT STDMETHODCALLTYPE
688 idropsource_querycontinuedrag (LPDROPSOURCE This,
689                                BOOL         fEscapePressed,
690                                DWORD        grfKeyState)
691 {
692   source_drag_context *ctx = (source_drag_context *) This;
693
694   GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p ", This));
695
696   if (send_change_events (ctx->context, grfKeyState, fEscapePressed))
697     process_pending_events ();
698
699   if (_dnd_source_state == GDK_WIN32_DND_DROPPED)
700     {
701       GDK_NOTE (DND, g_print ("DRAGDROP_S_DROP\n"));
702       return DRAGDROP_S_DROP;
703     }
704   else if (_dnd_source_state == GDK_WIN32_DND_NONE)
705     {
706       GDK_NOTE (DND, g_print ("DRAGDROP_S_CANCEL\n"));
707       return DRAGDROP_S_CANCEL;
708     }
709   else
710     {
711       GDK_NOTE (DND, g_print ("S_OK\n"));
712       return S_OK;
713     }
714 }
715
716 static HRESULT STDMETHODCALLTYPE
717 idropsource_givefeedback (LPDROPSOURCE This,
718                           DWORD        dwEffect)
719 {
720   source_drag_context *ctx = (source_drag_context *) This;
721   GdkDragAction suggested_action;
722
723   GDK_NOTE (DND, g_print ("idropsource_givefeedback %p DRAGDROP_S_USEDEFAULTCURSORS\n", This));
724
725   if (dwEffect == DROPEFFECT_MOVE)
726     suggested_action = GDK_ACTION_MOVE;
727   else
728     suggested_action = GDK_ACTION_COPY;
729   ctx->context->action = suggested_action;
730
731   if (dwEffect == DROPEFFECT_NONE)
732     {
733       if (ctx->context->dest_window != NULL)
734         {
735           g_object_unref (ctx->context->dest_window);
736           ctx->context->dest_window = NULL;
737         }
738     }
739   else
740     {
741       if (ctx->context->dest_window == NULL)
742         ctx->context->dest_window = g_object_ref (_gdk_root);
743     }
744
745   return DRAGDROP_S_USEDEFAULTCURSORS;
746 }
747
748 static ULONG STDMETHODCALLTYPE
749 idataobject_addref (LPDATAOBJECT This)
750 {
751   data_object *dobj = (data_object *) This;
752   int ref_count = ++dobj->ref_count;
753
754   GDK_NOTE (DND, g_print ("idataobject_addref %p %d\n", This, ref_count));
755
756   return ref_count;
757 }
758
759 static HRESULT STDMETHODCALLTYPE
760 idataobject_queryinterface (LPDATAOBJECT This,
761                             REFIID       riid,
762                             LPVOID      *ppvObject)
763 {
764   GDK_NOTE (DND, {
765       g_print ("idataobject_queryinterface %p ", This);
766       PRINT_GUID (riid);
767     });
768
769   *ppvObject = NULL;
770
771   if (IsEqualGUID (riid, &IID_IUnknown))
772     {
773       GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
774       idataobject_addref (This);
775       *ppvObject = This;
776       return S_OK;
777     }
778   else if (IsEqualGUID (riid, &IID_IDataObject))
779     {
780       GDK_NOTE (DND, g_print ("...IDataObject S_OK\n"));
781       idataobject_addref (This);
782       *ppvObject = This;
783       return S_OK;
784     }
785   else
786     {
787       GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
788       return E_NOINTERFACE;
789     }
790 }
791
792 static ULONG STDMETHODCALLTYPE
793 idataobject_release (LPDATAOBJECT This)
794 {
795   data_object *dobj = (data_object *) This;
796   int ref_count = --dobj->ref_count;
797
798   GDK_NOTE (DND, g_print ("idataobject_release %p %d\n", This, ref_count));
799
800   if (ref_count == 0)
801     g_free (This);
802
803   return ref_count;
804 }
805
806 static HRESULT
807 query (LPDATAOBJECT This,
808        LPFORMATETC  pFormatEtc)
809 {
810   int i;
811
812   if (!pFormatEtc)
813     return DV_E_FORMATETC;
814
815   if (pFormatEtc->lindex != -1)
816     return DV_E_LINDEX;
817
818   if ((pFormatEtc->tymed & TYMED_HGLOBAL) == 0)
819     return DV_E_TYMED;
820
821   if ((pFormatEtc->dwAspect & DVASPECT_CONTENT) == 0)
822     return DV_E_DVASPECT;
823
824   for (i = 0; i < nformats; i++)
825     if (pFormatEtc->cfFormat == formats[i].cfFormat)
826       return S_OK;
827
828   return DV_E_FORMATETC;
829 }
830
831 static FORMATETC *active_pFormatEtc = NULL;
832 static STGMEDIUM *active_pMedium = NULL;
833
834 static HRESULT STDMETHODCALLTYPE
835 idataobject_getdata (LPDATAOBJECT This,
836                      LPFORMATETC  pFormatEtc,
837                      LPSTGMEDIUM  pMedium)
838 {
839   data_object *ctx = (data_object *) This;
840   GdkAtom target;
841   HRESULT hr;
842   GdkEvent e;
843
844   GDK_NOTE (DND, g_print ("idataobject_getdata %p %s ",
845                           This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
846
847   /* Check whether we can provide requested format */
848   hr = query (This, pFormatEtc);
849   if (hr != S_OK)
850     return hr;
851
852   /* Append a GDK_SELECTION_GET event and then hope the app sets the
853    * property associated with the _gdk_ole2_dnd atom
854    */
855
856   active_pFormatEtc = pFormatEtc;
857   active_pMedium = pMedium;
858
859   target = GDK_TARGET_STRING;
860
861   e.type = GDK_SELECTION_REQUEST;
862   e.selection.window = ctx->context->source_window;
863   e.selection.send_event = FALSE; /* ??? */
864   /* FIXME: Should really both selection and property be _gdk_ole2_dnd? */
865   e.selection.selection = _gdk_ole2_dnd;
866   /* FIXME: Target? */
867   e.selection.target = _utf8_string;
868   e.selection.property = _gdk_ole2_dnd;
869   e.selection.time = GDK_CURRENT_TIME;
870
871   g_object_ref (e.selection.window);
872
873   GDK_NOTE (EVENTS, _gdk_win32_print_event (&e));
874   gdk_event_put (&e);
875   process_pending_events ();
876
877   active_pFormatEtc = NULL;
878   active_pMedium = NULL;
879
880   if (pMedium->hGlobal == NULL) {
881     return E_UNEXPECTED;
882   }
883
884   return S_OK;
885 }
886
887 static HRESULT STDMETHODCALLTYPE
888 idataobject_getdatahere (LPDATAOBJECT This,
889                          LPFORMATETC  pFormatEtc,
890                          LPSTGMEDIUM  pMedium)
891 {
892   GDK_NOTE (DND, g_print ("idataobject_getdatahere %p %s E_UNEXPECTED\n",
893                           This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
894
895   return E_UNEXPECTED;
896 }
897
898 static HRESULT STDMETHODCALLTYPE
899 idataobject_querygetdata (LPDATAOBJECT This,
900                           LPFORMATETC  pFormatEtc)
901 {
902   HRESULT hr;
903
904   hr = query (This, pFormatEtc);
905
906 #define CASE(x) case x: g_print (#x)
907   GDK_NOTE (DND, {
908       g_print ("idataobject_querygetdata %p %s \n",
909                This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat));
910       switch (hr)
911         {
912         CASE (DV_E_FORMATETC);
913         CASE (DV_E_LINDEX);
914         CASE (DV_E_TYMED);
915         CASE (DV_E_DVASPECT);
916         CASE (S_OK);
917         default: g_print ("%#lx", hr);
918         }
919     });
920
921   return hr;
922 }
923
924 static HRESULT STDMETHODCALLTYPE
925 idataobject_getcanonicalformatetc (LPDATAOBJECT This,
926                                    LPFORMATETC  pFormatEtcIn,
927                                    LPFORMATETC  pFormatEtcOut)
928 {
929   GDK_NOTE (DND, g_print ("idataobject_getcanonicalformatetc %p E_UNEXPECTED\n", This));
930
931   return E_UNEXPECTED;
932 }
933
934 static HRESULT STDMETHODCALLTYPE
935 idataobject_setdata (LPDATAOBJECT This,
936                      LPFORMATETC  pFormatEtc,
937                      LPSTGMEDIUM  pMedium,
938                      BOOL         fRelease)
939 {
940   GDK_NOTE (DND, g_print ("idataobject_setdata %p %s E_UNEXPECTED\n",
941                           This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
942
943   return E_UNEXPECTED;
944 }
945
946 static HRESULT STDMETHODCALLTYPE
947 idataobject_enumformatetc (LPDATAOBJECT     This,
948                            DWORD            dwDirection,
949                            LPENUMFORMATETC *ppEnumFormatEtc)
950 {
951   GDK_NOTE (DND, g_print ("idataobject_enumformatetc %p ", This));
952
953   if (dwDirection != DATADIR_GET)
954     {
955       GDK_NOTE (DND, g_print ("E_NOTIMPL\n"));
956       return E_NOTIMPL;
957     }
958
959   *ppEnumFormatEtc = &enum_formats_new ()->ief;
960
961   GDK_NOTE (DND, g_print ("%p S_OK\n", *ppEnumFormatEtc));
962
963   return S_OK;
964 }
965
966 static HRESULT STDMETHODCALLTYPE
967 idataobject_dadvise (LPDATAOBJECT This,
968                      LPFORMATETC  pFormatetc,
969                      DWORD        advf,
970                      LPADVISESINK pAdvSink,
971                      DWORD       *pdwConnection)
972 {
973   GDK_NOTE (DND, g_print ("idataobject_dadvise %p E_NOTIMPL\n", This));
974
975   return E_NOTIMPL;
976 }
977
978 static HRESULT STDMETHODCALLTYPE
979 idataobject_dunadvise (LPDATAOBJECT This,
980                        DWORD         dwConnection)
981 {
982   GDK_NOTE (DND, g_print ("idataobject_dunadvise %p E_NOTIMPL\n", This));
983
984   return E_NOTIMPL;
985 }
986
987 static HRESULT STDMETHODCALLTYPE
988 idataobject_enumdadvise (LPDATAOBJECT    This,
989                          LPENUMSTATDATA *ppenumAdvise)
990 {
991   GDK_NOTE (DND, g_print ("idataobject_enumdadvise %p OLE_E_ADVISENOTSUPPORTED\n", This));
992
993   return OLE_E_ADVISENOTSUPPORTED;
994 }
995
996 static ULONG STDMETHODCALLTYPE
997 ienumformatetc_addref (LPENUMFORMATETC This)
998 {
999   enum_formats *en = (enum_formats *) This;
1000   int ref_count = ++en->ref_count;
1001
1002   GDK_NOTE (DND, g_print ("ienumformatetc_addref %p %d\n", This, ref_count));
1003
1004   return ref_count;
1005 }
1006
1007 static HRESULT STDMETHODCALLTYPE
1008 ienumformatetc_queryinterface (LPENUMFORMATETC This,
1009                                REFIID          riid,
1010                                LPVOID         *ppvObject)
1011 {
1012   GDK_NOTE (DND, {
1013       g_print ("ienumformatetc_queryinterface %p", This);
1014       PRINT_GUID (riid);
1015     });
1016
1017   *ppvObject = NULL;
1018
1019   if (IsEqualGUID (riid, &IID_IUnknown))
1020     {
1021       GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
1022       ienumformatetc_addref (This);
1023       *ppvObject = This;
1024       return S_OK;
1025     }
1026   else if (IsEqualGUID (riid, &IID_IEnumFORMATETC))
1027     {
1028       GDK_NOTE (DND, g_print ("...IEnumFORMATETC S_OK\n"));
1029       ienumformatetc_addref (This);
1030       *ppvObject = This;
1031       return S_OK;
1032     }
1033   else
1034     {
1035       GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
1036       return E_NOINTERFACE;
1037     }
1038 }
1039
1040 static ULONG STDMETHODCALLTYPE
1041 ienumformatetc_release (LPENUMFORMATETC This)
1042 {
1043   enum_formats *en = (enum_formats *) This;
1044   int ref_count = --en->ref_count;
1045
1046   GDK_NOTE (DND, g_print ("ienumformatetc_release %p %d\n", This, ref_count));
1047
1048   if (ref_count == 0)
1049     g_free (This);
1050
1051   return ref_count;
1052 }
1053
1054 static HRESULT STDMETHODCALLTYPE
1055 ienumformatetc_next (LPENUMFORMATETC This,
1056                      ULONG           celt,
1057                      LPFORMATETC     elts,
1058                      ULONG          *nelt)
1059 {
1060   enum_formats *en = (enum_formats *) This;
1061   int i, n;
1062
1063   GDK_NOTE (DND, g_print ("ienumformatetc_next %p %d %ld ", This, en->ix, celt));
1064
1065   n = 0;
1066   for (i = 0; i < celt; i++)
1067     {
1068       if (en->ix >= nformats)
1069         break;
1070       elts[i] = formats[en->ix++];
1071       n++;
1072     }
1073
1074   if (nelt != NULL)
1075     *nelt = n;
1076
1077   GDK_NOTE (DND, g_print ("%s\n", (n == celt) ? "S_OK" : "S_FALSE"));
1078
1079   if (n == celt)
1080     return S_OK;
1081   else
1082     return S_FALSE;
1083 }
1084
1085 static HRESULT STDMETHODCALLTYPE
1086 ienumformatetc_skip (LPENUMFORMATETC This,
1087                      ULONG           celt)
1088 {
1089   enum_formats *en = (enum_formats *) This;
1090
1091   GDK_NOTE (DND, g_print ("ienumformatetc_skip %p %d %ld S_OK\n", This, en->ix, celt));
1092
1093   en->ix += celt;
1094
1095   return S_OK;
1096 }
1097
1098 static HRESULT STDMETHODCALLTYPE
1099 ienumformatetc_reset (LPENUMFORMATETC This)
1100 {
1101   enum_formats *en = (enum_formats *) This;
1102
1103   GDK_NOTE (DND, g_print ("ienumformatetc_reset %p S_OK\n", This));
1104
1105   en->ix = 0;
1106
1107   return S_OK;
1108 }
1109
1110 static HRESULT STDMETHODCALLTYPE
1111 ienumformatetc_clone (LPENUMFORMATETC  This,
1112                       LPENUMFORMATETC *ppEnumFormatEtc)
1113 {
1114   enum_formats *en = (enum_formats *) This;
1115   enum_formats *new;
1116
1117   GDK_NOTE (DND, g_print ("ienumformatetc_clone %p S_OK\n", This));
1118
1119   new = enum_formats_new ();
1120
1121   new->ix = en->ix;
1122
1123   *ppEnumFormatEtc = &new->ief;
1124
1125   return S_OK;
1126 }
1127
1128 static IDropTargetVtbl idt_vtbl = {
1129   idroptarget_queryinterface,
1130   idroptarget_addref,
1131   idroptarget_release,
1132   idroptarget_dragenter,
1133   idroptarget_dragover,
1134   idroptarget_dragleave,
1135   idroptarget_drop
1136 };
1137
1138 static IDropSourceVtbl ids_vtbl = {
1139   idropsource_queryinterface,
1140   idropsource_addref,
1141   idropsource_release,
1142   idropsource_querycontinuedrag,
1143   idropsource_givefeedback
1144 };
1145
1146 static IDataObjectVtbl ido_vtbl = {
1147   idataobject_queryinterface,
1148   idataobject_addref,
1149   idataobject_release,
1150   idataobject_getdata,
1151   idataobject_getdatahere,
1152   idataobject_querygetdata,
1153   idataobject_getcanonicalformatetc,
1154   idataobject_setdata,
1155   idataobject_enumformatetc,
1156   idataobject_dadvise,
1157   idataobject_dunadvise,
1158   idataobject_enumdadvise
1159 };
1160
1161 static IEnumFORMATETCVtbl ief_vtbl = {
1162   ienumformatetc_queryinterface,
1163   ienumformatetc_addref,
1164   ienumformatetc_release,
1165   ienumformatetc_next,
1166   ienumformatetc_skip,
1167   ienumformatetc_reset,
1168   ienumformatetc_clone
1169 };
1170
1171
1172 static target_drag_context *
1173 target_context_new (GdkWindow *window)
1174 {
1175   target_drag_context *result;
1176   GdkDragContextPrivateWin32 *private;
1177   GdkDevice *device;
1178   GdkDeviceManager *device_manager;
1179
1180   result = g_new0 (target_drag_context, 1);
1181
1182   result->idt.lpVtbl = &idt_vtbl;
1183
1184   result->context = gdk_drag_context_new ();
1185   result->context->protocol = GDK_DRAG_PROTO_OLE2;
1186   result->context->is_source = FALSE;
1187
1188   device_manager = gdk_display_get_device_manager (_gdk_display);
1189   device = gdk_device_manager_get_client_pointer (device_manager);
1190   gdk_drag_context_set_device (result->context, device);
1191
1192   result->context->source_window = NULL;
1193
1194   result->context->dest_window = window;
1195   g_object_ref (window);
1196
1197   /* FIXME: result->context->targets? */
1198
1199   result->context->actions = GDK_ACTION_DEFAULT | GDK_ACTION_COPY | GDK_ACTION_MOVE;
1200   result->context->suggested_action = GDK_ACTION_MOVE;
1201   result->context->action = GDK_ACTION_MOVE;
1202
1203   private = PRIVATE_DATA(result->context);
1204   private->iface = (IUnknown *) &result->idt;
1205   idroptarget_addref (&result->idt);
1206
1207   GDK_NOTE (DND, g_print ("target_context_new: %p\n", result));
1208
1209   return result;
1210 }
1211
1212 static source_drag_context *
1213 source_context_new (GdkWindow *window,
1214                     GList     *targets)
1215 {
1216   source_drag_context *result;
1217   GdkDragContextPrivateWin32 *private;
1218   GdkDevice *device;
1219   GdkDeviceManager *device_manager;
1220
1221   result = g_new0 (source_drag_context, 1);
1222
1223   result->ids.lpVtbl = &ids_vtbl;
1224
1225   result->context = gdk_drag_context_new ();
1226   result->context->protocol = GDK_DRAG_PROTO_OLE2;
1227   result->context->is_source = TRUE;
1228
1229   device_manager = gdk_display_get_device_manager (_gdk_display);
1230   device = gdk_device_manager_get_client_pointer (device_manager);
1231   gdk_drag_context_set_device (result->context, device);
1232
1233   result->context->source_window = window;
1234   g_object_ref (window);
1235
1236   result->context->dest_window = NULL;
1237   result->context->targets = g_list_copy (targets);
1238
1239   private = PRIVATE_DATA(result->context);
1240   private->iface = (IUnknown *) &result->ids;
1241   idropsource_addref (&result->ids);
1242
1243   GDK_NOTE (DND, g_print ("source_context_new: %p\n", result));
1244
1245   return result;
1246 }
1247
1248 static data_object *
1249 data_object_new (GdkDragContext *context)
1250 {
1251   data_object *result;
1252
1253   result = g_new0 (data_object, 1);
1254
1255   result->ido.lpVtbl = &ido_vtbl;
1256   result->ref_count = 1;
1257   result->context = context;
1258
1259   GDK_NOTE (DND, g_print ("data_object_new: %p\n", result));
1260
1261   return result;
1262 }
1263
1264 static enum_formats *
1265 enum_formats_new (void)
1266 {
1267   enum_formats *result;
1268
1269   result = g_new0 (enum_formats, 1);
1270
1271   result->ief.lpVtbl = &ief_vtbl;
1272   result->ref_count = 1;
1273   result->ix = 0;
1274
1275   return result;
1276 }
1277
1278 void
1279 _gdk_win32_ole2_dnd_property_change (GdkAtom       type,
1280                                      gint          format,
1281                                      const guchar *data,
1282                                      gint          nelements)
1283 {
1284   if (use_ole2_dnd)
1285     {
1286       HGLOBAL hdata = NULL;
1287
1288       if (active_pFormatEtc == NULL || active_pMedium == NULL)
1289         return;
1290
1291       /* Set up the data buffer for wide character text request */
1292       if (active_pFormatEtc->cfFormat == CF_UNICODETEXT)
1293         {
1294           gunichar2 *wdata;
1295           glong wlen;
1296
1297           wdata = g_utf8_to_utf16 ((const char *) data, -1, NULL, &wlen, NULL);
1298           hdata = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (wlen + 1) * 2);
1299           if (hdata)
1300             {
1301               wchar_t *ptr = (wchar_t *) GlobalLock(hdata);
1302               memcpy (ptr, wdata, (wlen + 1) * 2);
1303               GlobalUnlock(hdata);
1304             }
1305           g_free (wdata);
1306         }
1307       else
1308         g_warning ("Only text handled for now");
1309
1310       /* Pack up data */
1311       active_pMedium->tymed = TYMED_HGLOBAL;
1312       active_pMedium->hGlobal = hdata;
1313       active_pMedium->pUnkForRelease = 0;
1314     }
1315 }
1316
1317 /* From MS Knowledge Base article Q130698 */
1318
1319 static gboolean
1320 resolve_link (HWND     hWnd,
1321               wchar_t *link,
1322               gchar  **lpszPath)
1323 {
1324   WIN32_FILE_ATTRIBUTE_DATA wfad;
1325   HRESULT hr;
1326   IShellLinkW *pslW = NULL;
1327   IPersistFile *ppf = NULL;
1328
1329   /* Check if the file is empty first because IShellLink::Resolve for
1330    * some reason succeeds with an empty file and returns an empty
1331    * "link target". (#524151)
1332    */
1333     if (!GetFileAttributesExW (link, GetFileExInfoStandard, &wfad) ||
1334         (wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
1335       return FALSE;
1336
1337   /* Assume failure to start with: */
1338   *lpszPath = 0;
1339
1340   /* Call CoCreateInstance to obtain the IShellLink interface
1341    * pointer. This call fails if CoInitialize is not called, so it is
1342    * assumed that CoInitialize has been called.
1343    */
1344
1345   hr = CoCreateInstance (&CLSID_ShellLink,
1346                          NULL,
1347                          CLSCTX_INPROC_SERVER,
1348                          &IID_IShellLinkW,
1349                          (LPVOID *)&pslW);
1350
1351   if (SUCCEEDED (hr))
1352    {
1353      /* The IShellLink interface supports the IPersistFile
1354       * interface. Get an interface pointer to it.
1355       */
1356      hr = pslW->lpVtbl->QueryInterface (pslW,
1357                                         &IID_IPersistFile,
1358                                         (LPVOID *) &ppf);
1359    }
1360
1361   if (SUCCEEDED (hr))
1362     {
1363       /* Load the file. */
1364       hr = ppf->lpVtbl->Load (ppf, link, STGM_READ);
1365     }
1366
1367   if (SUCCEEDED (hr))
1368     {
1369       /* Resolve the link by calling the Resolve()
1370        * interface function.
1371        */
1372       hr = pslW->lpVtbl->Resolve (pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
1373     }
1374
1375   if (SUCCEEDED (hr))
1376     {
1377       wchar_t wtarget[MAX_PATH];
1378
1379       hr = pslW->lpVtbl->GetPath (pslW, wtarget, MAX_PATH, NULL, 0);
1380       if (SUCCEEDED (hr))
1381         *lpszPath = g_utf16_to_utf8 (wtarget, -1, NULL, NULL, NULL);
1382     }
1383
1384   if (ppf)
1385     ppf->lpVtbl->Release (ppf);
1386
1387   if (pslW)
1388     pslW->lpVtbl->Release (pslW);
1389
1390   return SUCCEEDED (hr);
1391 }
1392
1393 #if 0
1394
1395 /* Check for filenames like C:\Users\tml\AppData\Local\Temp\d5qtkvvs.bmp */
1396 static gboolean
1397 filename_looks_tempish (const char *filename)
1398 {
1399   char *dirname;
1400   char *p;
1401   const char *q;
1402   gboolean retval = FALSE;
1403
1404   dirname = g_path_get_dirname (filename);
1405
1406   p = dirname;
1407   q = g_get_tmp_dir ();
1408
1409   while (*p && *q &&
1410          ((G_IS_DIR_SEPARATOR (*p) && G_IS_DIR_SEPARATOR (*q)) ||
1411           g_ascii_tolower (*p) == g_ascii_tolower (*q)))
1412     p++, q++;
1413
1414   if (!*p && !*q)
1415     retval = TRUE;
1416
1417   g_free (dirname);
1418
1419   return retval;
1420 }
1421
1422 static gboolean
1423 close_it (gpointer data)
1424 {
1425   close (GPOINTER_TO_INT (data));
1426
1427   return FALSE;
1428 }
1429
1430 #endif
1431
1432 static GdkFilterReturn
1433 gdk_dropfiles_filter (GdkXEvent *xev,
1434                       GdkEvent  *event,
1435                       gpointer   data)
1436 {
1437   GdkDragContext *context;
1438   GString *result;
1439   MSG *msg = (MSG *) xev;
1440   HANDLE hdrop;
1441   POINT pt;
1442   gint nfiles, i;
1443   gchar *fileName, *linkedFile;
1444   GdkDevice *device;
1445   GdkDeviceManager *device_manager;
1446
1447   if (msg->message == WM_DROPFILES)
1448     {
1449       GDK_NOTE (DND, g_print ("WM_DROPFILES: %p\n", msg->hwnd));
1450
1451       context = gdk_drag_context_new ();
1452       context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
1453       context->is_source = FALSE;
1454
1455       device_manager = gdk_display_get_device_manager (_gdk_display);
1456       device = gdk_device_manager_get_client_pointer (device_manager);
1457       gdk_drag_context_set_device (context, device);
1458
1459       context->source_window = _gdk_root;
1460       g_object_ref (context->source_window);
1461
1462       context->dest_window = event->any.window;
1463       g_object_ref (context->dest_window);
1464
1465       /* WM_DROPFILES drops are always file names */
1466       context->targets =
1467         g_list_append (NULL, _text_uri_list);
1468       context->actions = GDK_ACTION_COPY;
1469       context->suggested_action = GDK_ACTION_COPY;
1470       current_dest_drag = context;
1471
1472       event->dnd.type = GDK_DROP_START;
1473       event->dnd.context = current_dest_drag;
1474       gdk_event_set_device (event, gdk_drag_context_get_device (current_dest_drag));
1475
1476       hdrop = (HANDLE) msg->wParam;
1477       DragQueryPoint (hdrop, &pt);
1478       ClientToScreen (msg->hwnd, &pt);
1479
1480       event->dnd.x_root = pt.x + _gdk_offset_x;
1481       event->dnd.y_root = pt.y + _gdk_offset_y;
1482       event->dnd.time = _gdk_win32_get_next_tick (msg->time);
1483
1484       nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
1485
1486       result = g_string_new (NULL);
1487       for (i = 0; i < nfiles; i++)
1488         {
1489           gchar *uri;
1490           wchar_t wfn[MAX_PATH];
1491
1492           DragQueryFileW (hdrop, i, wfn, MAX_PATH);
1493           fileName = g_utf16_to_utf8 (wfn, -1, NULL, NULL, NULL);
1494
1495           /* Resolve shortcuts */
1496           if (resolve_link (msg->hwnd, wfn, &linkedFile))
1497             {
1498               uri = g_filename_to_uri (linkedFile, NULL, NULL);
1499               if (uri != NULL)
1500                 {
1501                   g_string_append (result, uri);
1502                   GDK_NOTE (DND, g_print ("... %s link to %s: %s\n",
1503                                           fileName, linkedFile, uri));
1504                   g_free (uri);
1505                 }
1506               g_free (fileName);
1507               fileName = linkedFile;
1508             }
1509           else
1510             {
1511               uri = g_filename_to_uri (fileName, NULL, NULL);
1512               if (uri != NULL)
1513                 {
1514                   g_string_append (result, uri);
1515                   GDK_NOTE (DND, g_print ("... %s: %s\n", fileName, uri));
1516                   g_free (uri);
1517                 }
1518             }
1519
1520 #if 0
1521           /* Awful hack to recognize temp files corresponding to
1522            * images dragged from Firefox... Open the file right here
1523            * so that it is less likely that Firefox manages to delete
1524            * it before the GTK+-using app (typically GIMP) has opened
1525            * it.
1526            *
1527            * Not compiled in for now, because it means images dragged
1528            * from Firefox would stay around in the temp folder which
1529            * is not what Firefox intended. I don't feel comfortable
1530            * with that, both from a geenral sanity point of view, and
1531            * from a privacy point of view. It's better to wait for
1532            * Firefox to fix the problem, for instance by deleting the
1533            * temp file after a longer delay, or to wait until we
1534            * implement the OLE2_DND...
1535            */
1536           if (filename_looks_tempish (fileName))
1537             {
1538               int fd = g_open (fileName, _O_RDONLY|_O_BINARY, 0);
1539               if (fd == -1)
1540                 {
1541                   GDK_NOTE (DND, g_print ("Could not open %s, maybe an image dragged from Firefox that it already deleted\n", fileName));
1542                 }
1543               else
1544                 {
1545                   GDK_NOTE (DND, g_print ("Opened %s as %d so that Firefox won't delete it\n", fileName, fd));
1546                   g_timeout_add_seconds (1, close_it, GINT_TO_POINTER (fd));
1547                 }
1548             }
1549 #endif
1550
1551           g_free (fileName);
1552           g_string_append (result, "\015\012");
1553         }
1554       _gdk_dropfiles_store (result->str);
1555       g_string_free (result, FALSE);
1556
1557       DragFinish (hdrop);
1558
1559       return GDK_FILTER_TRANSLATE;
1560     }
1561   else
1562     return GDK_FILTER_CONTINUE;
1563 }
1564
1565 static void
1566 add_format (GArray *fmts,
1567             CLIPFORMAT cf)
1568 {
1569   FORMATETC fmt;
1570
1571   fmt.cfFormat = cf;
1572   fmt.ptd = NULL;
1573   fmt.dwAspect = DVASPECT_CONTENT;
1574   fmt.lindex = -1;
1575   fmt.tymed = TYMED_HGLOBAL;
1576
1577   g_array_append_val (fmts, fmt);
1578 }
1579
1580
1581 void
1582 _gdk_dnd_init (void)
1583 {
1584   if (getenv ("GDK_WIN32_USE_EXPERIMENTAL_OLE2_DND"))
1585     use_ole2_dnd = TRUE;
1586
1587   if (use_ole2_dnd)
1588     {
1589       HRESULT hr;
1590       GArray *fmts;
1591
1592       hr = OleInitialize (NULL);
1593
1594       if (! SUCCEEDED (hr))
1595         g_error ("OleInitialize failed");
1596
1597       fmts = g_array_new (FALSE, FALSE, sizeof (FORMATETC));
1598
1599       /* The most important presumably */
1600       add_format (fmts, CF_UNICODETEXT);
1601
1602       /* Used for GTK+ internal DND, I think was the intent? Anyway, code below assumes
1603        * this is at index 1.
1604        */
1605       add_format (fmts, CF_GDIOBJFIRST);
1606
1607       add_format (fmts, CF_HDROP);
1608
1609       add_format (fmts, _cf_png);
1610       add_format (fmts, CF_DIB);
1611
1612       add_format (fmts, _cf_url);
1613       add_format (fmts, _cf_html_format);
1614       add_format (fmts, _cf_text_html);
1615
1616       nformats = fmts->len;
1617       formats = (FORMATETC*) g_array_free (fmts, FALSE);
1618
1619       target_ctx_for_window = g_hash_table_new (g_direct_hash, g_direct_equal);
1620     }
1621 }
1622
1623 void
1624 _gdk_win32_dnd_exit (void)
1625 {
1626   if (use_ole2_dnd)
1627     {
1628       OleUninitialize ();
1629     }
1630 }
1631
1632 /* Source side */
1633
1634 static void
1635 local_send_leave (GdkDragContext *context,
1636                   guint32         time)
1637 {
1638   GdkEvent *tmp_event;
1639
1640   GDK_NOTE (DND, g_print ("local_send_leave: context=%p current_dest_drag=%p\n",
1641                           context,
1642                           current_dest_drag));
1643
1644   if ((current_dest_drag != NULL) &&
1645       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1646       (current_dest_drag->source_window == context->source_window))
1647     {
1648       tmp_event = gdk_event_new (GDK_DRAG_LEAVE);
1649
1650       tmp_event->dnd.window = g_object_ref (context->dest_window);
1651       /* Pass ownership of context to the event */
1652       tmp_event->dnd.send_event = FALSE;
1653       tmp_event->dnd.context = g_object_ref (current_dest_drag);
1654       tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1655       gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
1656
1657       current_dest_drag = NULL;
1658
1659       GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1660       gdk_event_put (tmp_event);
1661       gdk_event_free (tmp_event);
1662     }
1663 }
1664
1665 static void
1666 local_send_enter (GdkDragContext *context,
1667                   guint32         time)
1668 {
1669   GdkEvent *tmp_event;
1670   GdkDragContextPrivateWin32 *private;
1671   GdkDragContext *new_context;
1672   GdkDevice *device;
1673   GdkDeviceManager *device_manager;
1674
1675   GDK_NOTE (DND, g_print ("local_send_enter: context=%p current_dest_drag=%p\n",
1676                           context,
1677                           current_dest_drag));
1678
1679   private = PRIVATE_DATA (context);
1680
1681   if (current_dest_drag != NULL)
1682     {
1683       g_object_unref (G_OBJECT (current_dest_drag));
1684       current_dest_drag = NULL;
1685     }
1686
1687   new_context = gdk_drag_context_new ();
1688   new_context->protocol = GDK_DRAG_PROTO_LOCAL;
1689   new_context->is_source = FALSE;
1690
1691   device_manager = gdk_display_get_device_manager (_gdk_display);
1692   device = gdk_device_manager_get_client_pointer (device_manager);
1693   gdk_drag_context_set_device (new_context, device);
1694
1695   new_context->source_window = context->source_window;
1696   g_object_ref (new_context->source_window);
1697
1698   new_context->dest_window = context->dest_window;
1699   g_object_ref (new_context->dest_window);
1700
1701   new_context->targets = g_list_copy (context->targets);
1702
1703   gdk_window_set_events (new_context->source_window,
1704                          gdk_window_get_events (new_context->source_window) |
1705                          GDK_PROPERTY_CHANGE_MASK);
1706   new_context->actions = context->actions;
1707
1708   tmp_event = gdk_event_new (GDK_DRAG_ENTER);
1709   tmp_event->dnd.window = g_object_ref (context->dest_window);
1710   tmp_event->dnd.send_event = FALSE;
1711   tmp_event->dnd.context = g_object_ref (new_context);
1712   tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1713   gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
1714
1715   current_dest_drag = new_context;
1716
1717   GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1718   gdk_event_put (tmp_event);
1719   gdk_event_free (tmp_event);
1720 }
1721
1722 static void
1723 local_send_motion (GdkDragContext *context,
1724                    gint            x_root,
1725                    gint            y_root,
1726                    GdkDragAction   action,
1727                    guint32         time)
1728 {
1729   GdkEvent *tmp_event;
1730
1731   GDK_NOTE (DND, g_print ("local_send_motion: context=%p (%d,%d) current_dest_drag=%p\n",
1732                           context, x_root, y_root,
1733                           current_dest_drag));
1734
1735   if ((current_dest_drag != NULL) &&
1736       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1737       (current_dest_drag->source_window == context->source_window))
1738     {
1739       tmp_event = gdk_event_new (GDK_DRAG_MOTION);
1740       tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
1741       tmp_event->dnd.send_event = FALSE;
1742       tmp_event->dnd.context = g_object_ref (current_dest_drag);
1743       tmp_event->dnd.time = time;
1744       gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
1745
1746       current_dest_drag->suggested_action = action;
1747
1748       tmp_event->dnd.x_root = x_root;
1749       tmp_event->dnd.y_root = y_root;
1750
1751       PRIVATE_DATA (current_dest_drag)->last_pt.x = x_root - _gdk_offset_x;
1752       PRIVATE_DATA (current_dest_drag)->last_pt.y = y_root - _gdk_offset_y;
1753
1754       PRIVATE_DATA (context)->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1755
1756       GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1757       gdk_event_put (tmp_event);
1758       gdk_event_free (tmp_event);
1759     }
1760 }
1761
1762 static void
1763 local_send_drop (GdkDragContext *context,
1764                  guint32         time)
1765 {
1766   GdkEvent *tmp_event;
1767
1768   GDK_NOTE (DND, g_print ("local_send_drop: context=%p current_dest_drag=%p\n",
1769                           context,
1770                           current_dest_drag));
1771
1772   if ((current_dest_drag != NULL) &&
1773       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1774       (current_dest_drag->source_window == context->source_window))
1775     {
1776       GdkDragContextPrivateWin32 *private;
1777       private = PRIVATE_DATA (current_dest_drag);
1778
1779       /* Pass ownership of context to the event */
1780       tmp_event = gdk_event_new (GDK_DROP_START);
1781       tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
1782       tmp_event->dnd.send_event = FALSE;
1783       tmp_event->dnd.context = g_object_ref (current_dest_drag);
1784       tmp_event->dnd.time = GDK_CURRENT_TIME;
1785       gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
1786
1787       tmp_event->dnd.x_root = private->last_pt.x + _gdk_offset_x;
1788       tmp_event->dnd.y_root = private->last_pt.y + _gdk_offset_y;
1789
1790       current_dest_drag = NULL;
1791
1792       GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1793       gdk_event_put (tmp_event);
1794       gdk_event_free (tmp_event);
1795     }
1796
1797 }
1798
1799 static void
1800 gdk_drag_do_leave (GdkDragContext *context,
1801                    guint32         time)
1802 {
1803   if (context->dest_window)
1804     {
1805       GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
1806
1807       if (!use_ole2_dnd)
1808         {
1809           if (context->protocol == GDK_DRAG_PROTO_LOCAL)
1810             local_send_leave (context, time);
1811         }
1812
1813       g_object_unref (context->dest_window);
1814       context->dest_window = NULL;
1815     }
1816 }
1817
1818 GdkDragContext *
1819 _gdk_win32_window_drag_begin (GdkWindow *window,
1820                               GdkDevice *device,
1821                               GList     *targets)
1822 {
1823   if (!use_ole2_dnd)
1824     {
1825       GdkDragContext *new_context;
1826       GdkDevice *device;
1827       GdkDeviceManager *device_manager;
1828
1829       g_return_val_if_fail (window != NULL, NULL);
1830
1831       new_context = gdk_drag_context_new ();
1832
1833       device_manager = gdk_display_get_device_manager (_gdk_display);
1834       device = gdk_device_manager_get_client_pointer (device_manager);
1835       gdk_drag_context_set_device (new_context, device);
1836
1837       new_context->is_source = TRUE;
1838
1839       new_context->source_window = window;
1840       g_object_ref (window);
1841       gdk_drag_context_set_device (new_context, device);
1842
1843       new_context->targets = g_list_copy (targets);
1844       new_context->actions = 0;
1845
1846       return new_context;
1847     }
1848   else
1849     {
1850       source_drag_context *ctx;
1851
1852       g_return_val_if_fail (window != NULL, NULL);
1853
1854       GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
1855
1856       ctx = source_context_new (window, targets);
1857
1858       _dnd_source_state = GDK_WIN32_DND_PENDING;
1859
1860       pending_src_context = ctx;
1861       g_object_ref (ctx->context);
1862
1863       return ctx->context;
1864     }
1865 }
1866
1867 void
1868 _gdk_win32_dnd_do_dragdrop (void)
1869 {
1870   if (use_ole2_dnd)
1871     {
1872       GdkDragContext* drag_ctx;
1873       GdkDragContextPrivateWin32 *private;
1874       BYTE kbd_state[256];
1875       data_object *dobj;
1876       HRESULT hr;
1877       DWORD dwEffect;
1878 #if 0
1879       HGLOBAL global;
1880       STGMEDIUM medium;
1881 #endif
1882
1883       if (pending_src_context == NULL)
1884         return;
1885
1886       drag_ctx = pending_src_context->context;
1887       private = PRIVATE_DATA (drag_ctx);
1888
1889       dobj = data_object_new (drag_ctx);
1890
1891       API_CALL (GetCursorPos, (&private->last_pt));
1892       API_CALL (ScreenToClient, (GDK_WINDOW_HWND (drag_ctx->source_window), &private->last_pt));
1893       private->last_key_state = 0;
1894       API_CALL (GetKeyboardState, (kbd_state));
1895
1896       if (kbd_state[VK_CONTROL])
1897         private->last_key_state |= MK_CONTROL;
1898       if (kbd_state[VK_SHIFT])
1899         private->last_key_state |= MK_SHIFT;
1900       if (kbd_state[VK_LBUTTON])
1901         private->last_key_state |= MK_LBUTTON;
1902       if (kbd_state[VK_MBUTTON])
1903         private->last_key_state |= MK_MBUTTON;
1904       if (kbd_state[VK_RBUTTON])
1905         private->last_key_state |= MK_RBUTTON;
1906
1907 #if 0
1908       global = GlobalAlloc (GMEM_FIXED, sizeof (ctx));
1909
1910       memcpy (&global, ctx, sizeof (ctx));
1911
1912       medium.tymed = TYMED_HGLOBAL;
1913       medium.hGlobal = global;
1914       medium.pUnkForRelease = NULL;
1915
1916       /* FIXME I wish I remember what I was thinking of here, i.e. what
1917        * the formats[1] signifies, i.e. the CF_GDIOBJFIRST FORMATETC?
1918        */
1919       dobj->ido.lpVtbl->SetData (&dobj->ido, &formats[1], &medium, TRUE);
1920 #endif
1921
1922       /* Start dragging with mainloop inside the OLE2 API. Exits only when done */
1923
1924       GDK_NOTE (DND, g_print ("Calling DoDragDrop\n"));
1925
1926       _gdk_win32_begin_modal_call ();
1927       hr = DoDragDrop (&dobj->ido, &pending_src_context->ids,
1928                        DROPEFFECT_COPY | DROPEFFECT_MOVE,
1929                        &dwEffect);
1930       _gdk_win32_end_modal_call ();
1931
1932       GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n",
1933                               (hr == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
1934                                (hr == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
1935                                 (hr == E_UNEXPECTED ? "E_UNEXPECTED" :
1936                                  g_strdup_printf ("%#.8lx", hr))))));
1937
1938       /* Delete dnd selection after successful move */
1939       if (hr == DRAGDROP_S_DROP && dwEffect == DROPEFFECT_MOVE)
1940         {
1941           GdkEvent tmp_event;
1942
1943           tmp_event.type = GDK_SELECTION_REQUEST;
1944           tmp_event.selection.window = drag_ctx->source_window;
1945           tmp_event.selection.send_event = FALSE;
1946           tmp_event.selection.selection = _gdk_ole2_dnd;
1947           tmp_event.selection.target = _delete;
1948           tmp_event.selection.property = _gdk_ole2_dnd; /* ??? */
1949           tmp_event.selection.time = GDK_CURRENT_TIME; /* ??? */
1950           g_object_ref (tmp_event.selection.window);
1951
1952           GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
1953           gdk_event_put (&tmp_event);
1954         }
1955
1956 #if 0
1957       // Send a GDK_DROP_FINISHED to the source window
1958       GetCursorPos (&pt);
1959       ptl.x = pt.x;
1960       ptl.y = pt.y;
1961       if ( pending_src_context != NULL && pending_src_context->context != NULL
1962            && pending_src_context->context->source_window != NULL )
1963         push_dnd_event (GDK_DROP_FINISHED, pending_src_context->context, ptl, FALSE);
1964 #endif
1965
1966       dobj->ido.lpVtbl->Release (&dobj->ido);
1967       if (pending_src_context != NULL)
1968         {
1969           pending_src_context->ids.lpVtbl->Release (&pending_src_context->ids);
1970           pending_src_context = NULL;
1971         }
1972     }
1973 }
1974
1975 /* Untested, may not work ...
1976  * ... but as of this writing is only used by exlusive X11 gtksocket.c
1977  */
1978 GdkDragProtocol
1979 _gdk_win32_window_get_drag_protocol (GdkWindow *window,
1980                                      GdkWindow **target)
1981 {
1982   GdkDragProtocol protocol = GDK_DRAG_PROTO_NONE;
1983
1984   if (gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
1985     {
1986       if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
1987         {
1988           if (use_ole2_dnd)
1989             protocol = GDK_DRAG_PROTO_OLE2;
1990           else
1991             protocol = GDK_DRAG_PROTO_LOCAL;
1992         }
1993     }
1994
1995   if (target)
1996     {
1997       *target = NULL;
1998     }
1999
2000   return protocol;
2001 }
2002
2003 static GdkWindow *
2004 gdk_win32_drag_context_find_window (GdkDragContext  *context,
2005                                  GdkWindow       *drag_window,
2006                                  GdkScreen       *screen,
2007                                  gint             x_root,
2008                                  gint             y_root,
2009                                  GdkDragProtocol *protocol)
2010 {
2011   GdkWindow *dest_window;
2012   POINT pt;
2013   HWND hwnd;
2014
2015   pt.x = x_root - _gdk_offset_x;
2016   pt.y = y_root - _gdk_offset_y;
2017
2018   hwnd = WindowFromPoint (pt);
2019
2020   if (hwnd == NULL)
2021     dest_window = NULL;
2022   else
2023     {
2024       dest_window = gdk_win32_handle_table_lookup (hwnd);
2025       if (dest_window)
2026         g_object_ref (dest_window);
2027       else
2028         dest_window = gdk_win32_window_foreign_new_for_display (_gdk_display, hwnd);
2029
2030       if (use_ole2_dnd)
2031         *protocol = GDK_DRAG_PROTO_OLE2;
2032       else if (context->source_window)
2033         *protocol = GDK_DRAG_PROTO_LOCAL;
2034       else
2035         *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
2036     }
2037
2038   GDK_NOTE (DND,
2039             g_print ("gdk_drag_find_window: %p %+d%+d: %p: %p %s\n",
2040                      (drag_window ? GDK_WINDOW_HWND (drag_window) : NULL),
2041                      x_root, y_root,
2042                      hwnd,
2043                      (dest_window ? GDK_WINDOW_HWND (dest_window) : NULL),
2044                      _gdk_win32_drag_protocol_to_string (*protocol)));
2045
2046   return dest_window;
2047 }
2048
2049 static gboolean
2050 gdk_win32_drag_context_drag_motion (GdkDragContext *context,
2051                  GdkWindow      *dest_window,
2052                  GdkDragProtocol protocol,
2053                  gint            x_root,
2054                  gint            y_root,
2055                  GdkDragAction   suggested_action,
2056                  GdkDragAction   possible_actions,
2057                  guint32         time)
2058 {
2059   GdkDragContextPrivateWin32 *private;
2060
2061   g_return_val_if_fail (context != NULL, FALSE);
2062
2063   context->actions = possible_actions;
2064
2065   GDK_NOTE (DND, g_print ("gdk_drag_motion: %s suggested=%s, possible=%s\n"
2066                           " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2067                           _gdk_win32_drag_protocol_to_string (protocol),
2068                           _gdk_win32_drag_action_to_string (suggested_action),
2069                           _gdk_win32_drag_action_to_string (possible_actions),
2070                           context,
2071                           _gdk_win32_drag_action_to_string (context->actions),
2072                           _gdk_win32_drag_action_to_string (context->suggested_action),
2073                           _gdk_win32_drag_action_to_string (context->action)));
2074
2075   private = PRIVATE_DATA (context);
2076
2077   if (!use_ole2_dnd)
2078     {
2079       if (context->dest_window == dest_window)
2080         {
2081           GdkDragContext *dest_context;
2082
2083           dest_context = gdk_drag_context_find (FALSE,
2084                                                 context->source_window,
2085                                                 dest_window);
2086
2087           if (dest_context)
2088             dest_context->actions = context->actions;
2089
2090           context->suggested_action = suggested_action;
2091         }
2092       else
2093         {
2094           GdkEvent *tmp_event;
2095
2096           /* Send a leave to the last destination */
2097           gdk_drag_do_leave (context, time);
2098           private->drag_status = GDK_DRAG_STATUS_DRAG;
2099
2100           /* Check if new destination accepts drags, and which protocol */
2101           if (dest_window)
2102             {
2103               context->dest_window = dest_window;
2104               g_object_ref (context->dest_window);
2105               context->protocol = protocol;
2106
2107               switch (protocol)
2108                 {
2109                 case GDK_DRAG_PROTO_LOCAL:
2110                   local_send_enter (context, time);
2111                   break;
2112
2113                 default:
2114                   break;
2115                 }
2116               context->suggested_action = suggested_action;
2117             }
2118           else
2119             {
2120               context->dest_window = NULL;
2121               context->action = 0;
2122             }
2123
2124           /* Push a status event, to let the client know that
2125            * the drag changed
2126            */
2127     tmp_event = gdk_event_new (GDK_DRAG_STATUS);
2128           tmp_event->dnd.window = g_object_ref (context->source_window);
2129           /* We use this to signal a synthetic status. Perhaps
2130            * we should use an extra field...
2131            */
2132           tmp_event->dnd.send_event = TRUE;
2133
2134           tmp_event->dnd.context = g_object_ref (context);
2135           tmp_event->dnd.time = time;
2136     gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
2137
2138           GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2139           gdk_event_put (tmp_event);
2140     gdk_event_free (tmp_event);
2141         }
2142
2143       /* Send a drag-motion event */
2144
2145       private->last_pt.x = x_root - _gdk_offset_x;
2146       private->last_pt.y = y_root - _gdk_offset_y;
2147
2148       if (context->dest_window)
2149         {
2150           if (private->drag_status == GDK_DRAG_STATUS_DRAG)
2151             {
2152               switch (context->protocol)
2153                 {
2154                 case GDK_DRAG_PROTO_LOCAL:
2155                   local_send_motion (context, x_root, y_root, suggested_action, time);
2156                   break;
2157
2158                 case GDK_DRAG_PROTO_NONE:
2159                   g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
2160                   break;
2161
2162                 default:
2163                   break;
2164                 }
2165             }
2166           else
2167             {
2168               GDK_NOTE (DND, g_print (" returning TRUE\n"
2169                                       " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2170                                       context,
2171                                       _gdk_win32_drag_action_to_string (context->actions),
2172                                       _gdk_win32_drag_action_to_string (context->suggested_action),
2173                                       _gdk_win32_drag_action_to_string (context->action)));
2174               return TRUE;
2175             }
2176         }
2177     }
2178
2179   GDK_NOTE (DND, g_print (" returning FALSE\n"
2180                           " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2181                           context,
2182                           _gdk_win32_drag_action_to_string (context->actions),
2183                           _gdk_win32_drag_action_to_string (context->suggested_action),
2184                           _gdk_win32_drag_action_to_string (context->action)));
2185   return FALSE;
2186 }
2187
2188 static void
2189 gdk_win32_drag_context_drag_drop (GdkDragContext *context,
2190                guint32         time)
2191 {
2192   g_return_if_fail (context != NULL);
2193
2194   GDK_NOTE (DND, g_print ("gdk_drag_drop\n"));
2195
2196   if (!use_ole2_dnd)
2197     {
2198       if (context->dest_window &&
2199           context->protocol == GDK_DRAG_PROTO_LOCAL)
2200         local_send_drop (context, time);
2201     }
2202   else
2203     {
2204       _dnd_source_state = GDK_WIN32_DND_DROPPED;
2205     }
2206 }
2207
2208 static void
2209 gdk_win32_drag_context_drag_abort (GdkDragContext *context,
2210                 guint32         time)
2211 {
2212   g_return_if_fail (context != NULL);
2213
2214   GDK_NOTE (DND, g_print ("gdk_drag_abort\n"));
2215
2216   if (use_ole2_dnd)
2217     _dnd_source_state = GDK_WIN32_DND_NONE;
2218 }
2219
2220 /* Destination side */
2221
2222 static void
2223 gdk_win32_drag_context_drag_status (GdkDragContext *context,
2224                  GdkDragAction   action,
2225                  guint32         time)
2226 {
2227   GdkDragContextPrivateWin32 *private;
2228   GdkDragContext *src_context;
2229   GdkEvent *tmp_event;
2230
2231   g_return_if_fail (context != NULL);
2232
2233   private = PRIVATE_DATA (context);
2234
2235   GDK_NOTE (DND, g_print ("gdk_drag_status: %s\n"
2236                           " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2237                           _gdk_win32_drag_action_to_string (action),
2238                           context,
2239                           _gdk_win32_drag_action_to_string (context->actions),
2240                           _gdk_win32_drag_action_to_string (context->suggested_action),
2241                           _gdk_win32_drag_action_to_string (context->action)));
2242
2243   context->action = action;
2244
2245   if (!use_ole2_dnd)
2246     {
2247       src_context = gdk_drag_context_find (TRUE,
2248                                            context->source_window,
2249                                            context->dest_window);
2250
2251       if (src_context)
2252         {
2253           GdkDragContextPrivateWin32 *private = PRIVATE_DATA (src_context);
2254
2255           if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
2256             private->drag_status = GDK_DRAG_STATUS_DRAG;
2257
2258           tmp_event = gdk_event_new (GDK_DRAG_STATUS);
2259           tmp_event->dnd.window = g_object_ref (context->source_window);
2260           tmp_event->dnd.send_event = FALSE;
2261           tmp_event->dnd.context = g_object_ref (src_context);
2262           tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2263     gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
2264
2265           if (action == GDK_ACTION_DEFAULT)
2266             action = 0;
2267
2268           src_context->action = action;
2269
2270           GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2271           gdk_event_put (tmp_event);
2272     gdk_event_free (tmp_event);
2273         }
2274     }
2275 }
2276
2277 static void
2278 gdk_win32_drag_context_drop_reply (GdkDragContext *context,
2279                 gboolean        ok,
2280                 guint32         time)
2281 {
2282   g_return_if_fail (context != NULL);
2283
2284   GDK_NOTE (DND, g_print ("gdk_drop_reply\n"));
2285
2286   if (!use_ole2_dnd)
2287     if (context->dest_window)
2288       {
2289         if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
2290           _gdk_dropfiles_store (NULL);
2291       }
2292 }
2293
2294 static void
2295 gdk_win32_drag_context_drop_finish (GdkDragContext *context,
2296                  gboolean        success,
2297                  guint32         time)
2298 {
2299   GdkDragContextPrivateWin32 *private;
2300   GdkDragContext *src_context;
2301   GdkEvent *tmp_event;
2302
2303   g_return_if_fail (context != NULL);
2304
2305   GDK_NOTE (DND, g_print ("gdk_drop_finish\n"));
2306
2307   private = PRIVATE_DATA (context);
2308
2309   if (!use_ole2_dnd)
2310     {
2311       src_context = gdk_drag_context_find (TRUE,
2312                                            context->source_window,
2313                                            context->dest_window);
2314       if (src_context)
2315         {
2316     tmp_event = gdk_event_new (GDK_DROP_FINISHED);
2317           tmp_event->dnd.window = g_object_ref (src_context->source_window);
2318           tmp_event->dnd.send_event = FALSE;
2319           tmp_event->dnd.context = g_object_ref (src_context);
2320     gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
2321
2322           GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2323           gdk_event_put (tmp_event);
2324     gdk_event_free (tmp_event);
2325         }
2326     }
2327   else
2328     {
2329       gdk_drag_do_leave (context, time);
2330
2331       if (success)
2332         _dnd_target_state = GDK_WIN32_DND_DROPPED;
2333       else
2334         _dnd_target_state = GDK_WIN32_DND_FAILED;
2335     }
2336 }
2337
2338 #if 0
2339
2340 static GdkFilterReturn
2341 gdk_destroy_filter (GdkXEvent *xev,
2342                     GdkEvent  *event,
2343                     gpointer   data)
2344 {
2345   MSG *msg = (MSG *) xev;
2346
2347   if (msg->message == WM_DESTROY)
2348     {
2349       IDropTarget *idtp = (IDropTarget *) data;
2350
2351       GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %p\n", msg->hwnd));
2352 #if 0
2353       idtp->lpVtbl->Release (idtp);
2354 #endif
2355       RevokeDragDrop (msg->hwnd);
2356       CoLockObjectExternal ((IUnknown*) idtp, FALSE, TRUE);
2357     }
2358   return GDK_FILTER_CONTINUE;
2359 }
2360
2361 #endif
2362
2363 void
2364 _gdk_win32_window_register_dnd (GdkWindow *window)
2365 {
2366   target_drag_context *ctx;
2367   HRESULT hr;
2368
2369   g_return_if_fail (window != NULL);
2370
2371   if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
2372     return;
2373   else
2374     g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
2375
2376   GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %p\n", GDK_WINDOW_HWND (window)));
2377
2378   if (!use_ole2_dnd)
2379     {
2380       /* We always claim to accept dropped files, but in fact we might not,
2381        * of course. This function is called in such a way that it cannot know
2382        * whether the window (widget) in question actually accepts files
2383        * (in gtk, data of type text/uri-list) or not.
2384        */
2385       gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
2386       DragAcceptFiles (GDK_WINDOW_HWND (window), TRUE);
2387     }
2388   else
2389     {
2390       /* Return if window is already setup for DND. */
2391       if (g_hash_table_lookup (target_ctx_for_window, GDK_WINDOW_HWND (window)) != NULL)
2392         return;
2393
2394       /* Register for OLE2 d&d : similarly, claim to accept all supported
2395        * data types because we cannot know from here what the window
2396        * actually accepts.
2397        */
2398       /* FIXME: This of course won't work with user-extensible data types! */
2399       ctx = target_context_new (window);
2400
2401       hr = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
2402       if (!SUCCEEDED (hr))
2403         OTHER_API_FAILED ("CoLockObjectExternal");
2404       else
2405         {
2406           hr = RegisterDragDrop (GDK_WINDOW_HWND (window), &ctx->idt);
2407           if (hr == DRAGDROP_E_ALREADYREGISTERED)
2408             {
2409               g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
2410               CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSE, FALSE);
2411             }
2412           else if (!SUCCEEDED (hr))
2413             OTHER_API_FAILED ("RegisterDragDrop");
2414           else
2415             {
2416               g_object_ref (window);
2417               g_hash_table_insert (target_ctx_for_window, GDK_WINDOW_HWND (window), ctx);
2418             }
2419         }
2420     }
2421 }
2422
2423 static gboolean
2424 gdk_win32_drag_context_drop_status (GdkDragContext *context)
2425 {
2426   return ! PRIVATE_DATA (context)->drop_failed;
2427 }
2428
2429 static GdkAtom
2430 gdk_win32_drag_context_get_selection (GdkDragContext *context)
2431 {
2432   switch (context->protocol)
2433     {
2434     case GDK_DRAG_PROTO_LOCAL:
2435       return _local_dnd;
2436     case GDK_DRAG_PROTO_WIN32_DROPFILES:
2437       return _gdk_win32_dropfiles;
2438     case GDK_DRAG_PROTO_OLE2:
2439       return _gdk_ole2_dnd;
2440     default:
2441       return GDK_NONE;
2442     }
2443 }
2444
2445 static void
2446 gdk_win32_drag_context_class_init (GdkWin32DragContextClass *klass)
2447 {
2448   GObjectClass *object_class = G_OBJECT_CLASS (klass);
2449   GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
2450
2451   gdk_win32_drag_context_parent_class = g_type_class_peek_parent (klass);
2452
2453   object_class->finalize = gdk_win32_drag_context_finalize;
2454
2455   context_class->find_window = gdk_win32_drag_context_find_window;
2456   context_class->drag_status = gdk_win32_drag_context_drag_status;
2457   context_class->drag_motion = gdk_win32_drag_context_drag_motion;
2458   context_class->drag_abort = gdk_win32_drag_context_drag_abort;
2459   context_class->drag_drop = gdk_win32_drag_context_drag_drop;
2460   context_class->drop_reply = gdk_win32_drag_context_drop_reply;
2461   context_class->drop_finish = gdk_win32_drag_context_drop_finish;
2462   context_class->drop_status = gdk_win32_drag_context_drop_status;
2463   context_class->get_selection = gdk_win32_drag_context_get_selection;
2464
2465   g_type_class_add_private (object_class, sizeof (GdkDragContextPrivateWin32));
2466 }