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
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.
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.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
34 * Comment from the old OLE2 DND code that is being merged in. Note
35 * that this comment might not fully reflect reality as the code
36 * obviously will have to be modified in this merge. Especially the
37 * talk about supporting other than UTF-8 text is bogus, that will not
40 * Support for OLE-2 drag and drop added at Archaeopteryx Software, 2001
41 * For more information, contact Stephan R.A. Deibel (sdeibel@archaeopteryx.com)
43 * Notes on implementation:
45 * This is a first pass at OLE2 support. It only supports text and unicode text
46 * data types, and file list dnd (which is handled seperately as it predates OLE2
47 * both in this implementation and on Windows in general).
49 * As such, the data type conversion from gdk selection targets to OLE2 CF_* data
50 * type specifiers is partially hardwired. Fixing this is complicated by (a) the
51 * fact that the widget's declared selection types aren't accessible in calls here
52 * that need to declare the corresponding OLE2 data types, and (b) there isn't a
53 * 1-1 correspondence between gdk target types and OLE2 types. The former needs
54 * some redesign in gtk dnd (something a gdk/gtk expert should do; I have tried
55 * and failed!). As an example of the latter: gdk STRING, TEXT, COMPOUND_TEXT map
56 * to CF_TEXT, CF_OEMTEXT, and CF_UNICODETEXT but as a group and with conversions
57 * necessary for various combinations. Currently, the code here (and in
58 * gdkdnd-win32.c) can handle gdk STRING and TEXT but not COMPOUND_TEXT, and OLE2
59 * CF_TEXT and CF_UNICODETEXT but not CF_OEMTEXT. The necessary conversions are
60 * supplied by the implementation here.
62 * Note that in combination with another hack originated by Archaeopteryx
63 * Software, the text conversions here may go to utf-8 unicode as the standard
64 * within-gtk target or to single-byte ascii when the USE_ACP_TEXT compilation
65 * flag is TRUE. This mode was added to support applications that aren't using
66 * utf-8 across the gtk/gdk API but instead use single-byte ascii according to
67 * the current Windows code page. See gdkim-win32.c for more info on that.
74 #include "gdkproperty.h"
75 #include "gdkinternals.h"
76 #include "gdkprivate-win32.h"
78 #include "gdkwin32dnd.h"
79 #include "gdk/gdkdndprivate.h"
87 #include <glib/gstdio.h>
91 GDK_DRAG_STATUS_MOTION_WAIT,
92 GDK_DRAG_STATUS_ACTION_WAIT,
96 struct _GdkWin32DragContext
98 GdkDragContext context;
100 guint drag_status : 4; /* Current status of drag */
101 guint drop_failed : 1; /* Whether the drop was unsuccessful */
103 POINT ole2_dnd_last_pt; /* Coordinates from last event */
104 DWORD ole2_dnd_last_key_state; /* Key state from last event */
105 gboolean ole2_dnd_being_finalized;
106 gint ole2_dnd_ref_count;
107 IUnknown *ole2_dnd_iface;
110 struct _GdkWin32DragContextClass
112 GdkDragContextClass parent_class;
115 static GList *contexts;
116 static GdkDragContext *current_dest_drag = NULL;
117 static gboolean use_ole2_dnd = FALSE;
119 G_DEFINE_TYPE (GdkWin32DragContext, gdk_win32_drag_context, GDK_TYPE_DRAG_CONTEXT)
122 gdk_win32_drag_context_init (GdkWin32DragContext *context)
126 contexts = g_list_prepend (contexts, context);
130 context->ole2_dnd_being_finalized = FALSE;
131 context->ole2_dnd_ref_count = 1;
132 context->ole2_dnd_iface = NULL;
135 GDK_NOTE (DND, g_print ("gdk_drag_context_init %p\n", context));
139 gdk_win32_drag_context_finalize (GObject *object)
141 GdkDragContext *context;
142 GdkWin32DragContext *context_win32;
144 GDK_NOTE (DND, g_print ("gdk_drag_context_finalize %p\n", object));
146 g_return_if_fail (GDK_IS_WIN32_DRAG_CONTEXT (object));
148 context = GDK_DRAG_CONTEXT (object);
149 context_win32 = GDK_WIN32_DRAG_CONTEXT (object);
153 contexts = g_list_remove (contexts, context);
155 if (context == current_dest_drag)
156 current_dest_drag = NULL;
160 if (context_win32->ole2_dnd_iface)
162 context_win32->ole2_dnd_being_finalized = TRUE;
163 context_win32->ole2_dnd_iface->lpVtbl->Release (context_win32->ole2_dnd_iface);
164 context_win32->ole2_dnd_iface = NULL;
168 G_OBJECT_CLASS (gdk_win32_drag_context_parent_class)->finalize (object);
173 static GdkDragContext *
174 gdk_drag_context_new (void)
176 GdkWin32DragContext *context_win32;
177 GdkDragContext *context;
179 context_win32 = g_object_new (GDK_TYPE_WIN32_DRAG_CONTEXT, NULL);
180 context = GDK_DRAG_CONTEXT(context_win32);
185 static GdkDragContext *
186 gdk_drag_context_find (gboolean is_source,
190 GList *tmp_list = contexts;
191 GdkDragContext *context;
195 context = (GdkDragContext *)tmp_list->data;
197 if ((!context->is_source == !is_source) &&
198 ((source == NULL) || (context->source_window && (context->source_window == source))) &&
199 ((dest == NULL) || (context->dest_window && (context->dest_window == dest))))
202 tmp_list = tmp_list->next;
208 #define PRINT_GUID(guid) \
209 g_print ("%.08lx-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
210 ((gulong *) guid)[0], \
211 ((gushort *) guid)[2], \
212 ((gushort *) guid)[3], \
213 ((guchar *) guid)[8], \
214 ((guchar *) guid)[9], \
215 ((guchar *) guid)[10], \
216 ((guchar *) guid)[11], \
217 ((guchar *) guid)[12], \
218 ((guchar *) guid)[13], \
219 ((guchar *) guid)[14], \
220 ((guchar *) guid)[15]);
223 static FORMATETC *formats;
228 GdkDragContext *context;
229 } target_drag_context;
233 GdkDragContext *context;
234 } source_drag_context;
239 GdkDragContext *context;
248 static source_drag_context *pending_src_context = NULL;
249 static IDataObject *dnd_data = NULL;
251 static enum_formats *enum_formats_new (void);
253 /* map windows -> target drag contexts. The table
254 * owns a ref to both objects.
256 static GHashTable* target_ctx_for_window = NULL;
258 static ULONG STDMETHODCALLTYPE
259 idroptarget_addref (LPDROPTARGET This)
261 target_drag_context *ctx = (target_drag_context *) This;
262 GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
264 int ref_count = ++context_win32->ole2_dnd_ref_count;
266 GDK_NOTE (DND, g_print ("idroptarget_addref %p %d\n", This, ref_count));
267 g_object_ref (G_OBJECT (ctx->context));
272 static HRESULT STDMETHODCALLTYPE
273 idroptarget_queryinterface (LPDROPTARGET This,
278 g_print ("idroptarget_queryinterface %p ", This);
284 if (IsEqualGUID (riid, &IID_IUnknown))
286 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
287 idroptarget_addref (This);
291 else if (IsEqualGUID (riid, &IID_IDropTarget))
293 GDK_NOTE (DND, g_print ("...IDropTarget S_OK\n"));
294 idroptarget_addref (This);
300 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
301 return E_NOINTERFACE;
305 static ULONG STDMETHODCALLTYPE
306 idroptarget_release (LPDROPTARGET This)
308 target_drag_context *ctx = (target_drag_context *) This;
309 GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
311 int ref_count = --context_win32->ole2_dnd_ref_count;
313 GDK_NOTE (DND, g_print ("idroptarget_release %p %d\n", This, ref_count));
315 if (!context_win32->ole2_dnd_being_finalized)
316 g_object_unref (G_OBJECT (ctx->context));
327 cf_to_atom (CLIPFORMAT cf)
334 return _text_uri_list;
340 return _text_uri_list;
342 if (cf == _cf_html_format || cf == _cf_text_html)
351 get_suggested_action (DWORD grfKeyState)
353 /* This is the yucky Windows standard: Force link action if both
354 * Control and Alt are down, copy if Control is down alone, move if
355 * Alt is down alone, or use default of move within the app or copy
356 * when origin of the drag is in another app.
358 if (grfKeyState & MK_CONTROL && grfKeyState & MK_SHIFT)
359 return GDK_ACTION_LINK; /* Link action not supported */
360 else if (grfKeyState & MK_CONTROL)
361 return GDK_ACTION_COPY;
362 else if (grfKeyState & MK_ALT)
363 return GDK_ACTION_MOVE;
364 #if 0 /* Default is always copy for now */
365 else if (_dnd_source_state == GDK_WIN32_DND_DRAGGING)
366 return GDK_ACTION_MOVE;
369 return GDK_ACTION_COPY;
370 /* Any way to determine when to add in DROPEFFECT_SCROLL? */
373 /* Process pending events -- we don't want to service non-GUI events
374 * forever so do one iteration and then do more only if there's a
378 process_pending_events ()
380 g_main_context_iteration (NULL, FALSE);
381 while (_gdk_event_queue_find_first (_gdk_display))
382 g_main_context_iteration (NULL, FALSE);
386 drop_effect_for_action (GdkDragAction action)
390 case GDK_ACTION_MOVE:
391 return DROPEFFECT_MOVE;
392 case GDK_ACTION_LINK:
393 return DROPEFFECT_LINK;
394 case GDK_ACTION_COPY:
395 return DROPEFFECT_COPY;
397 return DROPEFFECT_NONE;
402 dnd_event_put (GdkEventType type,
403 GdkDragContext *context,
405 gboolean to_dest_window)
409 e = gdk_event_new (type);
412 e->dnd.window = context->dest_window;
414 e->dnd.window = context->source_window;
415 e->dnd.send_event = FALSE;
416 e->dnd.context = g_object_ref (context);
417 e->dnd.time = GDK_CURRENT_TIME;
418 e->dnd.x_root = pt.x + _gdk_offset_x;
419 e->dnd.y_root = pt.x + _gdk_offset_y;
421 if (e->dnd.window != NULL)
422 g_object_ref (e->dnd.window);
424 gdk_event_set_device (e, gdk_drag_context_get_device (context));
426 GDK_NOTE (EVENTS, _gdk_win32_print_event (e));
431 static HRESULT STDMETHODCALLTYPE
432 idroptarget_dragenter (LPDROPTARGET This,
433 LPDATAOBJECT pDataObj,
438 target_drag_context *ctx = (target_drag_context *) This;
440 GDK_NOTE (DND, g_print ("idroptarget_dragenter %p S_OK\n", This));
442 ctx->context->suggested_action = get_suggested_action (grfKeyState);
443 dnd_event_put (GDK_DRAG_ENTER, ctx->context, pt, TRUE);
444 process_pending_events ();
445 *pdwEffect = drop_effect_for_action (ctx->context->action);
447 /* Assume that target can accept the data: In fact it may fail but
448 * we are not really set up to query the target!
453 static HRESULT STDMETHODCALLTYPE
454 idroptarget_dragover (LPDROPTARGET This,
459 target_drag_context *ctx = (target_drag_context *) This;
461 GDK_NOTE (DND, g_print ("idroptarget_dragover %p S_OK\n", This));
463 ctx->context->suggested_action = get_suggested_action (grfKeyState);
464 dnd_event_put (GDK_DRAG_MOTION, ctx->context, pt, TRUE);
465 process_pending_events ();
466 *pdwEffect = drop_effect_for_action (ctx->context->action);
471 static HRESULT STDMETHODCALLTYPE
472 idroptarget_dragleave (LPDROPTARGET This)
474 target_drag_context *ctx = (target_drag_context *) This;
475 POINTL pt = { 0, 0 };
477 GDK_NOTE (DND, g_print ("idroptarget_dragleave %p S_OK\n", This));
479 dnd_event_put (GDK_DRAG_LEAVE, ctx->context, pt, TRUE);
480 process_pending_events ();
485 static HRESULT STDMETHODCALLTYPE
486 idroptarget_drop (LPDROPTARGET This,
487 LPDATAOBJECT pDataObj,
492 target_drag_context *ctx = (target_drag_context *) This;
494 GDK_NOTE (DND, g_print ("idroptarget_drop %p ", This));
496 if (pDataObj == NULL)
498 GDK_NOTE (DND, g_print ("E_POINTER\n"));
504 ctx->context->suggested_action = get_suggested_action (grfKeyState);
505 dnd_event_put (GDK_DROP_START, ctx->context, pt, TRUE);
506 process_pending_events ();
510 /* Notify OLE of copy or move */
511 if (_dnd_target_state != GDK_WIN32_DND_DROPPED)
512 *pdwEffect = DROPEFFECT_NONE;
514 *pdwEffect = drop_effect_for_action (ctx->context->action);
516 GDK_NOTE (DND, g_print ("S_OK\n"));
521 static ULONG STDMETHODCALLTYPE
522 idropsource_addref (LPDROPSOURCE This)
524 source_drag_context *ctx = (source_drag_context *) This;
525 GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
527 int ref_count = ++context_win32->ole2_dnd_ref_count;
529 GDK_NOTE (DND, g_print ("idropsource_addref %p %d\n", This, ref_count));
530 g_object_ref (G_OBJECT (ctx->context));
535 static HRESULT STDMETHODCALLTYPE
536 idropsource_queryinterface (LPDROPSOURCE This,
541 g_print ("idropsource_queryinterface %p ", This);
547 if (IsEqualGUID (riid, &IID_IUnknown))
549 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
550 idropsource_addref (This);
554 else if (IsEqualGUID (riid, &IID_IDropSource))
556 GDK_NOTE (DND, g_print ("...IDropSource S_OK\n"));
557 idropsource_addref (This);
563 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
564 return E_NOINTERFACE;
568 static ULONG STDMETHODCALLTYPE
569 idropsource_release (LPDROPSOURCE This)
571 source_drag_context *ctx = (source_drag_context *) This;
572 GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
574 int ref_count = --context_win32->ole2_dnd_ref_count;
576 GDK_NOTE (DND, g_print ("idropsource_release %p %d\n", This, ref_count));
578 if (!context_win32->ole2_dnd_being_finalized)
579 g_object_unref (G_OBJECT (ctx->context));
587 /* Emit GDK events for any changes in mouse events or control key
588 * state since the last recorded state. Return true if any events
589 * have been emitted and false otherwise.
592 send_change_events (GdkDragContext *context,
594 gboolean esc_pressed)
596 GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
598 gboolean changed = FALSE;
599 HWND hwnd = GDK_WINDOW_HWND (context->source_window);
603 if (!API_CALL (GetCursorPos, (&pt)))
606 if (!API_CALL (ScreenToClient, (hwnd, &pt)))
609 if (pt.x != context_win32->ole2_dnd_last_pt.x || pt.y != context_win32->ole2_dnd_last_pt.y ||
610 key_state != context_win32->ole2_dnd_last_key_state)
612 lparam = MAKELPARAM (pt.x, pt.y);
614 if (pt.x != context_win32->ole2_dnd_last_pt.x || pt.y != context_win32->ole2_dnd_last_pt.y)
616 GDK_NOTE (DND, g_print ("Sending WM_MOUSEMOVE (%ld,%ld)\n", pt.x, pt.y));
617 SendMessage (hwnd, WM_MOUSEMOVE, wparam, lparam);
620 if ((key_state & MK_LBUTTON) != (context_win32->ole2_dnd_last_key_state & MK_LBUTTON))
622 if (key_state & MK_LBUTTON)
623 SendMessage (hwnd, WM_LBUTTONDOWN, wparam, lparam);
625 SendMessage (hwnd, WM_LBUTTONUP, wparam, lparam);
627 if ((key_state & MK_MBUTTON) != (context_win32->ole2_dnd_last_key_state & MK_MBUTTON))
629 if (key_state & MK_MBUTTON)
630 SendMessage (hwnd, WM_MBUTTONDOWN, wparam, lparam);
632 SendMessage (hwnd, WM_MBUTTONUP, wparam, lparam);
634 if ((key_state & MK_RBUTTON) != (context_win32->ole2_dnd_last_key_state & MK_RBUTTON))
636 if (key_state & MK_RBUTTON)
637 SendMessage (hwnd, WM_RBUTTONDOWN, wparam, lparam);
639 SendMessage (hwnd, WM_RBUTTONUP, wparam, lparam);
641 if ((key_state & MK_CONTROL) != (context_win32->ole2_dnd_last_key_state & MK_CONTROL))
643 if (key_state & MK_CONTROL)
644 SendMessage (hwnd, WM_KEYDOWN, VK_CONTROL, 0);
646 SendMessage (hwnd, WM_KEYUP, VK_CONTROL, 0);
648 if ((key_state & MK_SHIFT) != (context_win32->ole2_dnd_last_key_state & MK_SHIFT))
650 if (key_state & MK_CONTROL)
651 SendMessage (hwnd, WM_KEYDOWN, VK_SHIFT, 0);
653 SendMessage (hwnd, WM_KEYUP, VK_SHIFT, 0);
657 context_win32->ole2_dnd_last_key_state = key_state;
658 context_win32->ole2_dnd_last_pt = pt;
663 GDK_NOTE (DND, g_print ("Sending a escape key down message to %p\n", hwnd));
664 SendMessage (hwnd, WM_KEYDOWN, VK_ESCAPE, 0);
671 static HRESULT STDMETHODCALLTYPE
672 idropsource_querycontinuedrag (LPDROPSOURCE This,
676 source_drag_context *ctx = (source_drag_context *) This;
678 GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p ", This));
680 if (send_change_events (ctx->context, grfKeyState, fEscapePressed))
681 process_pending_events ();
683 if (_dnd_source_state == GDK_WIN32_DND_DROPPED)
685 GDK_NOTE (DND, g_print ("DRAGDROP_S_DROP\n"));
686 return DRAGDROP_S_DROP;
688 else if (_dnd_source_state == GDK_WIN32_DND_NONE)
690 GDK_NOTE (DND, g_print ("DRAGDROP_S_CANCEL\n"));
691 return DRAGDROP_S_CANCEL;
695 GDK_NOTE (DND, g_print ("S_OK\n"));
700 static HRESULT STDMETHODCALLTYPE
701 idropsource_givefeedback (LPDROPSOURCE This,
704 source_drag_context *ctx = (source_drag_context *) This;
705 GdkDragAction suggested_action;
707 GDK_NOTE (DND, g_print ("idropsource_givefeedback %p DRAGDROP_S_USEDEFAULTCURSORS\n", This));
709 if (dwEffect == DROPEFFECT_MOVE)
710 suggested_action = GDK_ACTION_MOVE;
712 suggested_action = GDK_ACTION_COPY;
713 ctx->context->action = suggested_action;
715 if (dwEffect == DROPEFFECT_NONE)
717 if (ctx->context->dest_window != NULL)
719 g_object_unref (ctx->context->dest_window);
720 ctx->context->dest_window = NULL;
725 if (ctx->context->dest_window == NULL)
726 ctx->context->dest_window = g_object_ref (_gdk_root);
729 return DRAGDROP_S_USEDEFAULTCURSORS;
732 static ULONG STDMETHODCALLTYPE
733 idataobject_addref (LPDATAOBJECT This)
735 data_object *dobj = (data_object *) This;
736 int ref_count = ++dobj->ref_count;
738 GDK_NOTE (DND, g_print ("idataobject_addref %p %d\n", This, ref_count));
743 static HRESULT STDMETHODCALLTYPE
744 idataobject_queryinterface (LPDATAOBJECT This,
749 g_print ("idataobject_queryinterface %p ", This);
755 if (IsEqualGUID (riid, &IID_IUnknown))
757 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
758 idataobject_addref (This);
762 else if (IsEqualGUID (riid, &IID_IDataObject))
764 GDK_NOTE (DND, g_print ("...IDataObject S_OK\n"));
765 idataobject_addref (This);
771 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
772 return E_NOINTERFACE;
776 static ULONG STDMETHODCALLTYPE
777 idataobject_release (LPDATAOBJECT This)
779 data_object *dobj = (data_object *) This;
780 int ref_count = --dobj->ref_count;
782 GDK_NOTE (DND, g_print ("idataobject_release %p %d\n", This, ref_count));
791 query (LPDATAOBJECT This,
792 LPFORMATETC pFormatEtc)
797 return DV_E_FORMATETC;
799 if (pFormatEtc->lindex != -1)
802 if ((pFormatEtc->tymed & TYMED_HGLOBAL) == 0)
805 if ((pFormatEtc->dwAspect & DVASPECT_CONTENT) == 0)
806 return DV_E_DVASPECT;
808 for (i = 0; i < nformats; i++)
809 if (pFormatEtc->cfFormat == formats[i].cfFormat)
812 return DV_E_FORMATETC;
815 static FORMATETC *active_pFormatEtc = NULL;
816 static STGMEDIUM *active_pMedium = NULL;
818 static HRESULT STDMETHODCALLTYPE
819 idataobject_getdata (LPDATAOBJECT This,
820 LPFORMATETC pFormatEtc,
823 data_object *ctx = (data_object *) This;
828 GDK_NOTE (DND, g_print ("idataobject_getdata %p %s ",
829 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
831 /* Check whether we can provide requested format */
832 hr = query (This, pFormatEtc);
836 /* Append a GDK_SELECTION_GET event and then hope the app sets the
837 * property associated with the _gdk_ole2_dnd atom
840 active_pFormatEtc = pFormatEtc;
841 active_pMedium = pMedium;
843 target = GDK_TARGET_STRING;
845 e.type = GDK_SELECTION_REQUEST;
846 e.selection.window = ctx->context->source_window;
847 e.selection.send_event = FALSE; /* ??? */
848 /* FIXME: Should really both selection and property be _gdk_ole2_dnd? */
849 e.selection.selection = _gdk_ole2_dnd;
851 e.selection.target = _utf8_string;
852 e.selection.property = _gdk_ole2_dnd;
853 e.selection.time = GDK_CURRENT_TIME;
855 g_object_ref (e.selection.window);
857 GDK_NOTE (EVENTS, _gdk_win32_print_event (&e));
859 process_pending_events ();
861 active_pFormatEtc = NULL;
862 active_pMedium = NULL;
864 if (pMedium->hGlobal == NULL) {
871 static HRESULT STDMETHODCALLTYPE
872 idataobject_getdatahere (LPDATAOBJECT This,
873 LPFORMATETC pFormatEtc,
876 GDK_NOTE (DND, g_print ("idataobject_getdatahere %p %s E_UNEXPECTED\n",
877 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
882 static HRESULT STDMETHODCALLTYPE
883 idataobject_querygetdata (LPDATAOBJECT This,
884 LPFORMATETC pFormatEtc)
888 hr = query (This, pFormatEtc);
890 #define CASE(x) case x: g_print (#x)
892 g_print ("idataobject_querygetdata %p %s \n",
893 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat));
896 CASE (DV_E_FORMATETC);
899 CASE (DV_E_DVASPECT);
901 default: g_print ("%#lx", hr);
908 static HRESULT STDMETHODCALLTYPE
909 idataobject_getcanonicalformatetc (LPDATAOBJECT This,
910 LPFORMATETC pFormatEtcIn,
911 LPFORMATETC pFormatEtcOut)
913 GDK_NOTE (DND, g_print ("idataobject_getcanonicalformatetc %p E_UNEXPECTED\n", This));
918 static HRESULT STDMETHODCALLTYPE
919 idataobject_setdata (LPDATAOBJECT This,
920 LPFORMATETC pFormatEtc,
924 GDK_NOTE (DND, g_print ("idataobject_setdata %p %s E_UNEXPECTED\n",
925 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
930 static HRESULT STDMETHODCALLTYPE
931 idataobject_enumformatetc (LPDATAOBJECT This,
933 LPENUMFORMATETC *ppEnumFormatEtc)
935 GDK_NOTE (DND, g_print ("idataobject_enumformatetc %p ", This));
937 if (dwDirection != DATADIR_GET)
939 GDK_NOTE (DND, g_print ("E_NOTIMPL\n"));
943 *ppEnumFormatEtc = &enum_formats_new ()->ief;
945 GDK_NOTE (DND, g_print ("%p S_OK\n", *ppEnumFormatEtc));
950 static HRESULT STDMETHODCALLTYPE
951 idataobject_dadvise (LPDATAOBJECT This,
952 LPFORMATETC pFormatetc,
954 LPADVISESINK pAdvSink,
955 DWORD *pdwConnection)
957 GDK_NOTE (DND, g_print ("idataobject_dadvise %p E_NOTIMPL\n", This));
962 static HRESULT STDMETHODCALLTYPE
963 idataobject_dunadvise (LPDATAOBJECT This,
966 GDK_NOTE (DND, g_print ("idataobject_dunadvise %p E_NOTIMPL\n", This));
971 static HRESULT STDMETHODCALLTYPE
972 idataobject_enumdadvise (LPDATAOBJECT This,
973 LPENUMSTATDATA *ppenumAdvise)
975 GDK_NOTE (DND, g_print ("idataobject_enumdadvise %p OLE_E_ADVISENOTSUPPORTED\n", This));
977 return OLE_E_ADVISENOTSUPPORTED;
980 static ULONG STDMETHODCALLTYPE
981 ienumformatetc_addref (LPENUMFORMATETC This)
983 enum_formats *en = (enum_formats *) This;
984 int ref_count = ++en->ref_count;
986 GDK_NOTE (DND, g_print ("ienumformatetc_addref %p %d\n", This, ref_count));
991 static HRESULT STDMETHODCALLTYPE
992 ienumformatetc_queryinterface (LPENUMFORMATETC This,
997 g_print ("ienumformatetc_queryinterface %p", This);
1003 if (IsEqualGUID (riid, &IID_IUnknown))
1005 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
1006 ienumformatetc_addref (This);
1010 else if (IsEqualGUID (riid, &IID_IEnumFORMATETC))
1012 GDK_NOTE (DND, g_print ("...IEnumFORMATETC S_OK\n"));
1013 ienumformatetc_addref (This);
1019 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
1020 return E_NOINTERFACE;
1024 static ULONG STDMETHODCALLTYPE
1025 ienumformatetc_release (LPENUMFORMATETC This)
1027 enum_formats *en = (enum_formats *) This;
1028 int ref_count = --en->ref_count;
1030 GDK_NOTE (DND, g_print ("ienumformatetc_release %p %d\n", This, ref_count));
1038 static HRESULT STDMETHODCALLTYPE
1039 ienumformatetc_next (LPENUMFORMATETC This,
1044 enum_formats *en = (enum_formats *) This;
1047 GDK_NOTE (DND, g_print ("ienumformatetc_next %p %d %ld ", This, en->ix, celt));
1050 for (i = 0; i < celt; i++)
1052 if (en->ix >= nformats)
1054 elts[i] = formats[en->ix++];
1061 GDK_NOTE (DND, g_print ("%s\n", (n == celt) ? "S_OK" : "S_FALSE"));
1069 static HRESULT STDMETHODCALLTYPE
1070 ienumformatetc_skip (LPENUMFORMATETC This,
1073 enum_formats *en = (enum_formats *) This;
1075 GDK_NOTE (DND, g_print ("ienumformatetc_skip %p %d %ld S_OK\n", This, en->ix, celt));
1082 static HRESULT STDMETHODCALLTYPE
1083 ienumformatetc_reset (LPENUMFORMATETC This)
1085 enum_formats *en = (enum_formats *) This;
1087 GDK_NOTE (DND, g_print ("ienumformatetc_reset %p S_OK\n", This));
1094 static HRESULT STDMETHODCALLTYPE
1095 ienumformatetc_clone (LPENUMFORMATETC This,
1096 LPENUMFORMATETC *ppEnumFormatEtc)
1098 enum_formats *en = (enum_formats *) This;
1101 GDK_NOTE (DND, g_print ("ienumformatetc_clone %p S_OK\n", This));
1103 new = enum_formats_new ();
1107 *ppEnumFormatEtc = &new->ief;
1112 static IDropTargetVtbl idt_vtbl = {
1113 idroptarget_queryinterface,
1115 idroptarget_release,
1116 idroptarget_dragenter,
1117 idroptarget_dragover,
1118 idroptarget_dragleave,
1122 static IDropSourceVtbl ids_vtbl = {
1123 idropsource_queryinterface,
1125 idropsource_release,
1126 idropsource_querycontinuedrag,
1127 idropsource_givefeedback
1130 static IDataObjectVtbl ido_vtbl = {
1131 idataobject_queryinterface,
1133 idataobject_release,
1134 idataobject_getdata,
1135 idataobject_getdatahere,
1136 idataobject_querygetdata,
1137 idataobject_getcanonicalformatetc,
1138 idataobject_setdata,
1139 idataobject_enumformatetc,
1140 idataobject_dadvise,
1141 idataobject_dunadvise,
1142 idataobject_enumdadvise
1145 static IEnumFORMATETCVtbl ief_vtbl = {
1146 ienumformatetc_queryinterface,
1147 ienumformatetc_addref,
1148 ienumformatetc_release,
1149 ienumformatetc_next,
1150 ienumformatetc_skip,
1151 ienumformatetc_reset,
1152 ienumformatetc_clone
1156 static target_drag_context *
1157 target_context_new (GdkWindow *window)
1159 GdkDragContext *context;
1160 GdkWin32DragContext *context_win32;
1161 target_drag_context *result;
1163 GdkDeviceManager *device_manager;
1165 context = gdk_drag_context_new ();
1166 context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
1168 result = g_new0 (target_drag_context, 1);
1169 result->context = context;
1170 result->idt.lpVtbl = &idt_vtbl;
1172 result->context->protocol = GDK_DRAG_PROTO_OLE2;
1173 result->context->is_source = FALSE;
1175 device_manager = gdk_display_get_device_manager (_gdk_display);
1176 device = gdk_device_manager_get_client_pointer (device_manager);
1177 gdk_drag_context_set_device (result->context, device);
1179 result->context->source_window = NULL;
1181 result->context->dest_window = window;
1182 g_object_ref (window);
1184 /* FIXME: context->targets? */
1185 result->context->actions = GDK_ACTION_DEFAULT | GDK_ACTION_COPY | GDK_ACTION_MOVE;
1186 result->context->suggested_action = GDK_ACTION_MOVE;
1187 result->context->action = GDK_ACTION_MOVE;
1189 context_win32->ole2_dnd_iface = (IUnknown *) &result->idt;
1190 idroptarget_addref (&result->idt);
1192 GDK_NOTE (DND, g_print ("target_context_new: %p\n", result));
1197 static source_drag_context *
1198 source_context_new (GdkWindow *window,
1201 GdkDragContext *context;
1202 GdkWin32DragContext *context_win32;
1203 source_drag_context *result;
1205 GdkDeviceManager *device_manager;
1207 context = gdk_drag_context_new ();
1208 context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
1210 result = g_new0 (source_drag_context, 1);
1211 result->context = context;
1212 result->ids.lpVtbl = &ids_vtbl;
1214 result->context->protocol = GDK_DRAG_PROTO_OLE2;
1215 result->context->is_source = TRUE;
1217 device_manager = gdk_display_get_device_manager (_gdk_display);
1218 device = gdk_device_manager_get_client_pointer (device_manager);
1219 gdk_drag_context_set_device (result->context, device);
1221 result->context->source_window = window;
1222 g_object_ref (window);
1224 result->context->dest_window = NULL;
1225 result->context->targets = g_list_copy (targets);
1227 context_win32->ole2_dnd_iface = (IUnknown *) &result->ids;
1228 idropsource_addref (&result->ids);
1230 GDK_NOTE (DND, g_print ("source_context_new: %p\n", result));
1235 static data_object *
1236 data_object_new (GdkDragContext *context)
1238 data_object *result;
1240 result = g_new0 (data_object, 1);
1242 result->ido.lpVtbl = &ido_vtbl;
1243 result->ref_count = 1;
1244 result->context = context;
1246 GDK_NOTE (DND, g_print ("data_object_new: %p\n", result));
1251 static enum_formats *
1252 enum_formats_new (void)
1254 enum_formats *result;
1256 result = g_new0 (enum_formats, 1);
1258 result->ief.lpVtbl = &ief_vtbl;
1259 result->ref_count = 1;
1266 _gdk_win32_ole2_dnd_property_change (GdkAtom type,
1273 HGLOBAL hdata = NULL;
1275 if (active_pFormatEtc == NULL || active_pMedium == NULL)
1278 /* Set up the data buffer for wide character text request */
1279 if (active_pFormatEtc->cfFormat == CF_UNICODETEXT)
1284 wdata = g_utf8_to_utf16 ((const char *) data, -1, NULL, &wlen, NULL);
1285 hdata = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (wlen + 1) * 2);
1288 wchar_t *ptr = (wchar_t *) GlobalLock(hdata);
1289 memcpy (ptr, wdata, (wlen + 1) * 2);
1290 GlobalUnlock(hdata);
1295 g_warning ("Only text handled for now");
1298 active_pMedium->tymed = TYMED_HGLOBAL;
1299 active_pMedium->hGlobal = hdata;
1300 active_pMedium->pUnkForRelease = 0;
1304 /* From MS Knowledge Base article Q130698 */
1307 resolve_link (HWND hWnd,
1311 WIN32_FILE_ATTRIBUTE_DATA wfad;
1313 IShellLinkW *pslW = NULL;
1314 IPersistFile *ppf = NULL;
1316 /* Check if the file is empty first because IShellLink::Resolve for
1317 * some reason succeeds with an empty file and returns an empty
1318 * "link target". (#524151)
1320 if (!GetFileAttributesExW (link, GetFileExInfoStandard, &wfad) ||
1321 (wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
1324 /* Assume failure to start with: */
1327 /* Call CoCreateInstance to obtain the IShellLink interface
1328 * pointer. This call fails if CoInitialize is not called, so it is
1329 * assumed that CoInitialize has been called.
1332 hr = CoCreateInstance (&CLSID_ShellLink,
1334 CLSCTX_INPROC_SERVER,
1340 /* The IShellLink interface supports the IPersistFile
1341 * interface. Get an interface pointer to it.
1343 hr = pslW->lpVtbl->QueryInterface (pslW,
1350 /* Load the file. */
1351 hr = ppf->lpVtbl->Load (ppf, link, STGM_READ);
1356 /* Resolve the link by calling the Resolve()
1357 * interface function.
1359 hr = pslW->lpVtbl->Resolve (pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
1364 wchar_t wtarget[MAX_PATH];
1366 hr = pslW->lpVtbl->GetPath (pslW, wtarget, MAX_PATH, NULL, 0);
1368 *lpszPath = g_utf16_to_utf8 (wtarget, -1, NULL, NULL, NULL);
1372 ppf->lpVtbl->Release (ppf);
1375 pslW->lpVtbl->Release (pslW);
1377 return SUCCEEDED (hr);
1382 /* Check for filenames like C:\Users\tml\AppData\Local\Temp\d5qtkvvs.bmp */
1384 filename_looks_tempish (const char *filename)
1389 gboolean retval = FALSE;
1391 dirname = g_path_get_dirname (filename);
1394 q = g_get_tmp_dir ();
1397 ((G_IS_DIR_SEPARATOR (*p) && G_IS_DIR_SEPARATOR (*q)) ||
1398 g_ascii_tolower (*p) == g_ascii_tolower (*q)))
1410 close_it (gpointer data)
1412 close (GPOINTER_TO_INT (data));
1419 static GdkFilterReturn
1420 gdk_dropfiles_filter (GdkXEvent *xev,
1424 GdkDragContext *context;
1426 MSG *msg = (MSG *) xev;
1430 gchar *fileName, *linkedFile;
1432 GdkDeviceManager *device_manager;
1434 if (msg->message == WM_DROPFILES)
1436 GDK_NOTE (DND, g_print ("WM_DROPFILES: %p\n", msg->hwnd));
1438 context = gdk_drag_context_new ();
1439 context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
1440 context->is_source = FALSE;
1442 device_manager = gdk_display_get_device_manager (_gdk_display);
1443 device = gdk_device_manager_get_client_pointer (device_manager);
1444 gdk_drag_context_set_device (context, device);
1446 context->source_window = _gdk_root;
1447 g_object_ref (context->source_window);
1449 context->dest_window = event->any.window;
1450 g_object_ref (context->dest_window);
1452 /* WM_DROPFILES drops are always file names */
1454 g_list_append (NULL, _text_uri_list);
1455 context->actions = GDK_ACTION_COPY;
1456 context->suggested_action = GDK_ACTION_COPY;
1457 current_dest_drag = context;
1459 event->dnd.type = GDK_DROP_START;
1460 event->dnd.context = current_dest_drag;
1461 gdk_event_set_device (event, gdk_drag_context_get_device (current_dest_drag));
1463 hdrop = (HANDLE) msg->wParam;
1464 DragQueryPoint (hdrop, &pt);
1465 ClientToScreen (msg->hwnd, &pt);
1467 event->dnd.x_root = pt.x + _gdk_offset_x;
1468 event->dnd.y_root = pt.y + _gdk_offset_y;
1469 event->dnd.time = _gdk_win32_get_next_tick (msg->time);
1471 nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
1473 result = g_string_new (NULL);
1474 for (i = 0; i < nfiles; i++)
1477 wchar_t wfn[MAX_PATH];
1479 DragQueryFileW (hdrop, i, wfn, MAX_PATH);
1480 fileName = g_utf16_to_utf8 (wfn, -1, NULL, NULL, NULL);
1482 /* Resolve shortcuts */
1483 if (resolve_link (msg->hwnd, wfn, &linkedFile))
1485 uri = g_filename_to_uri (linkedFile, NULL, NULL);
1488 g_string_append (result, uri);
1489 GDK_NOTE (DND, g_print ("... %s link to %s: %s\n",
1490 fileName, linkedFile, uri));
1494 fileName = linkedFile;
1498 uri = g_filename_to_uri (fileName, NULL, NULL);
1501 g_string_append (result, uri);
1502 GDK_NOTE (DND, g_print ("... %s: %s\n", fileName, uri));
1508 /* Awful hack to recognize temp files corresponding to
1509 * images dragged from Firefox... Open the file right here
1510 * so that it is less likely that Firefox manages to delete
1511 * it before the GTK+-using app (typically GIMP) has opened
1514 * Not compiled in for now, because it means images dragged
1515 * from Firefox would stay around in the temp folder which
1516 * is not what Firefox intended. I don't feel comfortable
1517 * with that, both from a geenral sanity point of view, and
1518 * from a privacy point of view. It's better to wait for
1519 * Firefox to fix the problem, for instance by deleting the
1520 * temp file after a longer delay, or to wait until we
1521 * implement the OLE2_DND...
1523 if (filename_looks_tempish (fileName))
1525 int fd = g_open (fileName, _O_RDONLY|_O_BINARY, 0);
1528 GDK_NOTE (DND, g_print ("Could not open %s, maybe an image dragged from Firefox that it already deleted\n", fileName));
1532 GDK_NOTE (DND, g_print ("Opened %s as %d so that Firefox won't delete it\n", fileName, fd));
1533 g_timeout_add_seconds (1, close_it, GINT_TO_POINTER (fd));
1539 g_string_append (result, "\015\012");
1541 _gdk_dropfiles_store (result->str);
1542 g_string_free (result, FALSE);
1546 return GDK_FILTER_TRANSLATE;
1549 return GDK_FILTER_CONTINUE;
1553 add_format (GArray *fmts,
1560 fmt.dwAspect = DVASPECT_CONTENT;
1562 fmt.tymed = TYMED_HGLOBAL;
1564 g_array_append_val (fmts, fmt);
1569 _gdk_dnd_init (void)
1571 if (getenv ("GDK_WIN32_USE_EXPERIMENTAL_OLE2_DND"))
1572 use_ole2_dnd = TRUE;
1579 hr = OleInitialize (NULL);
1581 if (! SUCCEEDED (hr))
1582 g_error ("OleInitialize failed");
1584 fmts = g_array_new (FALSE, FALSE, sizeof (FORMATETC));
1586 /* The most important presumably */
1587 add_format (fmts, CF_UNICODETEXT);
1589 /* Used for GTK+ internal DND, I think was the intent? Anyway, code below assumes
1590 * this is at index 1.
1592 add_format (fmts, CF_GDIOBJFIRST);
1594 add_format (fmts, CF_HDROP);
1596 add_format (fmts, _cf_png);
1597 add_format (fmts, CF_DIB);
1599 add_format (fmts, _cf_url);
1600 add_format (fmts, _cf_html_format);
1601 add_format (fmts, _cf_text_html);
1603 nformats = fmts->len;
1604 formats = (FORMATETC*) g_array_free (fmts, FALSE);
1606 target_ctx_for_window = g_hash_table_new (g_direct_hash, g_direct_equal);
1611 _gdk_win32_dnd_exit (void)
1622 local_send_leave (GdkDragContext *context,
1625 GdkEvent *tmp_event;
1627 GDK_NOTE (DND, g_print ("local_send_leave: context=%p current_dest_drag=%p\n",
1629 current_dest_drag));
1631 if ((current_dest_drag != NULL) &&
1632 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1633 (current_dest_drag->source_window == context->source_window))
1635 tmp_event = gdk_event_new (GDK_DRAG_LEAVE);
1637 tmp_event->dnd.window = g_object_ref (context->dest_window);
1638 /* Pass ownership of context to the event */
1639 tmp_event->dnd.send_event = FALSE;
1640 tmp_event->dnd.context = g_object_ref (current_dest_drag);
1641 tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1642 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
1644 current_dest_drag = NULL;
1646 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1647 gdk_event_put (tmp_event);
1648 gdk_event_free (tmp_event);
1653 local_send_enter (GdkDragContext *context,
1656 GdkEvent *tmp_event;
1657 GdkDragContext *new_context;
1659 GdkDeviceManager *device_manager;
1661 GDK_NOTE (DND, g_print ("local_send_enter: context=%p current_dest_drag=%p\n",
1663 current_dest_drag));
1665 if (current_dest_drag != NULL)
1667 g_object_unref (G_OBJECT (current_dest_drag));
1668 current_dest_drag = NULL;
1671 new_context = gdk_drag_context_new ();
1672 new_context->protocol = GDK_DRAG_PROTO_LOCAL;
1673 new_context->is_source = FALSE;
1675 device_manager = gdk_display_get_device_manager (_gdk_display);
1676 device = gdk_device_manager_get_client_pointer (device_manager);
1677 gdk_drag_context_set_device (new_context, device);
1679 new_context->source_window = context->source_window;
1680 g_object_ref (new_context->source_window);
1682 new_context->dest_window = context->dest_window;
1683 g_object_ref (new_context->dest_window);
1685 new_context->targets = g_list_copy (context->targets);
1687 gdk_window_set_events (new_context->source_window,
1688 gdk_window_get_events (new_context->source_window) |
1689 GDK_PROPERTY_CHANGE_MASK);
1690 new_context->actions = context->actions;
1692 tmp_event = gdk_event_new (GDK_DRAG_ENTER);
1693 tmp_event->dnd.window = g_object_ref (context->dest_window);
1694 tmp_event->dnd.send_event = FALSE;
1695 tmp_event->dnd.context = g_object_ref (new_context);
1696 tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1697 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
1699 current_dest_drag = new_context;
1701 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1702 gdk_event_put (tmp_event);
1703 gdk_event_free (tmp_event);
1707 local_send_motion (GdkDragContext *context,
1710 GdkDragAction action,
1713 GdkEvent *tmp_event;
1714 GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
1716 GDK_NOTE (DND, g_print ("local_send_motion: context=%p (%d,%d) current_dest_drag=%p\n",
1717 context, x_root, y_root,
1718 current_dest_drag));
1720 if ((current_dest_drag != NULL) &&
1721 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1722 (current_dest_drag->source_window == context->source_window))
1724 GdkWin32DragContext *current_dest_drag_win32;
1726 tmp_event = gdk_event_new (GDK_DRAG_MOTION);
1727 tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
1728 tmp_event->dnd.send_event = FALSE;
1729 tmp_event->dnd.context = g_object_ref (current_dest_drag);
1730 tmp_event->dnd.time = time;
1731 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
1733 current_dest_drag->suggested_action = action;
1735 tmp_event->dnd.x_root = x_root;
1736 tmp_event->dnd.y_root = y_root;
1738 current_dest_drag_win32 = GDK_WIN32_DRAG_CONTEXT (current_dest_drag);
1739 current_dest_drag_win32->ole2_dnd_last_pt.x = x_root - _gdk_offset_x;
1740 current_dest_drag_win32->ole2_dnd_last_pt.y = y_root - _gdk_offset_y;
1742 context_win32->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1744 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1745 gdk_event_put (tmp_event);
1746 gdk_event_free (tmp_event);
1751 local_send_drop (GdkDragContext *context,
1754 GdkEvent *tmp_event;
1756 GDK_NOTE (DND, g_print ("local_send_drop: context=%p current_dest_drag=%p\n",
1758 current_dest_drag));
1760 if ((current_dest_drag != NULL) &&
1761 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1762 (current_dest_drag->source_window == context->source_window))
1764 GdkWin32DragContext *context_win32;
1766 /* Pass ownership of context to the event */
1767 tmp_event = gdk_event_new (GDK_DROP_START);
1768 tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
1769 tmp_event->dnd.send_event = FALSE;
1770 tmp_event->dnd.context = g_object_ref (current_dest_drag);
1771 tmp_event->dnd.time = GDK_CURRENT_TIME;
1772 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
1774 context_win32 = GDK_WIN32_DRAG_CONTEXT (current_dest_drag);
1775 tmp_event->dnd.x_root = context_win32->ole2_dnd_last_pt.x + _gdk_offset_x;
1776 tmp_event->dnd.y_root = context_win32->ole2_dnd_last_pt.y + _gdk_offset_y;
1778 current_dest_drag = NULL;
1780 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1781 gdk_event_put (tmp_event);
1782 gdk_event_free (tmp_event);
1788 gdk_drag_do_leave (GdkDragContext *context,
1791 if (context->dest_window)
1793 GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
1797 if (context->protocol == GDK_DRAG_PROTO_LOCAL)
1798 local_send_leave (context, time);
1801 g_object_unref (context->dest_window);
1802 context->dest_window = NULL;
1807 _gdk_win32_window_drag_begin (GdkWindow *window,
1813 GdkDragContext *new_context;
1815 GdkDeviceManager *device_manager;
1817 g_return_val_if_fail (window != NULL, NULL);
1819 new_context = gdk_drag_context_new ();
1821 device_manager = gdk_display_get_device_manager (_gdk_display);
1822 device = gdk_device_manager_get_client_pointer (device_manager);
1823 gdk_drag_context_set_device (new_context, device);
1825 new_context->is_source = TRUE;
1827 new_context->source_window = window;
1828 g_object_ref (window);
1829 gdk_drag_context_set_device (new_context, device);
1831 new_context->targets = g_list_copy (targets);
1832 new_context->actions = 0;
1838 source_drag_context *ctx;
1840 g_return_val_if_fail (window != NULL, NULL);
1842 GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
1844 ctx = source_context_new (window, targets);
1846 _dnd_source_state = GDK_WIN32_DND_PENDING;
1848 pending_src_context = ctx;
1849 g_object_ref (ctx->context);
1851 return ctx->context;
1856 _gdk_win32_dnd_do_dragdrop (void)
1860 GdkDragContext* drag_ctx;
1861 GdkWin32DragContext *drag_ctx_win32;
1862 BYTE kbd_state[256];
1872 if (pending_src_context == NULL)
1875 drag_ctx = pending_src_context->context;
1876 drag_ctx_win32 = GDK_WIN32_DRAG_CONTEXT (drag_ctx);
1878 dobj = data_object_new (drag_ctx);
1880 API_CALL (GetCursorPos, (&drag_ctx_win32->ole2_dnd_last_pt));
1881 API_CALL (ScreenToClient, (GDK_WINDOW_HWND (drag_ctx->source_window), &drag_ctx_win32->ole2_dnd_last_pt));
1882 drag_ctx_win32->ole2_dnd_last_key_state = 0;
1883 API_CALL (GetKeyboardState, (kbd_state));
1885 if (kbd_state[VK_CONTROL])
1886 drag_ctx_win32->ole2_dnd_last_key_state |= MK_CONTROL;
1887 if (kbd_state[VK_SHIFT])
1888 drag_ctx_win32->ole2_dnd_last_key_state |= MK_SHIFT;
1889 if (kbd_state[VK_LBUTTON])
1890 drag_ctx_win32->ole2_dnd_last_key_state |= MK_LBUTTON;
1891 if (kbd_state[VK_MBUTTON])
1892 drag_ctx_win32->ole2_dnd_last_key_state |= MK_MBUTTON;
1893 if (kbd_state[VK_RBUTTON])
1894 drag_ctx_win32->ole2_dnd_last_key_state |= MK_RBUTTON;
1897 global = GlobalAlloc (GMEM_FIXED, sizeof (ctx));
1899 memcpy (&global, ctx, sizeof (ctx));
1901 medium.tymed = TYMED_HGLOBAL;
1902 medium.hGlobal = global;
1903 medium.pUnkForRelease = NULL;
1905 /* FIXME I wish I remember what I was thinking of here, i.e. what
1906 * the formats[1] signifies, i.e. the CF_GDIOBJFIRST FORMATETC?
1908 dobj->ido.lpVtbl->SetData (&dobj->ido, &formats[1], &medium, TRUE);
1911 /* Start dragging with mainloop inside the OLE2 API. Exits only when done */
1913 GDK_NOTE (DND, g_print ("Calling DoDragDrop\n"));
1915 _gdk_win32_begin_modal_call ();
1916 hr = DoDragDrop (&dobj->ido, &pending_src_context->ids,
1917 DROPEFFECT_COPY | DROPEFFECT_MOVE,
1919 _gdk_win32_end_modal_call ();
1921 GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n",
1922 (hr == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
1923 (hr == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
1924 (hr == E_UNEXPECTED ? "E_UNEXPECTED" :
1925 g_strdup_printf ("%#.8lx", hr))))));
1927 /* Delete dnd selection after successful move */
1928 if (hr == DRAGDROP_S_DROP && dwEffect == DROPEFFECT_MOVE)
1932 tmp_event.type = GDK_SELECTION_REQUEST;
1933 tmp_event.selection.window = drag_ctx->source_window;
1934 tmp_event.selection.send_event = FALSE;
1935 tmp_event.selection.selection = _gdk_ole2_dnd;
1936 tmp_event.selection.target = _delete;
1937 tmp_event.selection.property = _gdk_ole2_dnd; /* ??? */
1938 tmp_event.selection.time = GDK_CURRENT_TIME; /* ??? */
1939 g_object_ref (tmp_event.selection.window);
1941 GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
1942 gdk_event_put (&tmp_event);
1946 // Send a GDK_DROP_FINISHED to the source window
1950 if ( pending_src_context != NULL && pending_src_context->context != NULL
1951 && pending_src_context->context->source_window != NULL )
1952 push_dnd_event (GDK_DROP_FINISHED, pending_src_context->context, ptl, FALSE);
1955 dobj->ido.lpVtbl->Release (&dobj->ido);
1956 if (pending_src_context != NULL)
1958 pending_src_context->ids.lpVtbl->Release (&pending_src_context->ids);
1959 pending_src_context = NULL;
1964 /* Untested, may not work ...
1965 * ... but as of this writing is only used by exlusive X11 gtksocket.c
1968 _gdk_win32_window_get_drag_protocol (GdkWindow *window,
1971 GdkDragProtocol protocol = GDK_DRAG_PROTO_NONE;
1973 if (gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
1975 if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
1978 protocol = GDK_DRAG_PROTO_OLE2;
1980 protocol = GDK_DRAG_PROTO_LOCAL;
1997 } find_window_enum_arg;
1999 static BOOL CALLBACK
2000 find_window_enum_proc (HWND hwnd,
2005 find_window_enum_arg *a = (find_window_enum_arg *) lparam;
2007 if (hwnd == a->ignore)
2010 if (!IsWindowVisible (hwnd))
2014 ClientToScreen (hwnd, &tl);
2015 GetClientRect (hwnd, &rect);
2018 ClientToScreen (hwnd, &br);
2020 if (a->x >= tl.x && a->y >= tl.y && a->x < br.x && a->y < br.y)
2030 gdk_win32_drag_context_find_window (GdkDragContext *context,
2031 GdkWindow *drag_window,
2035 GdkDragProtocol *protocol)
2037 GdkWindow *dest_window, *dw;
2038 find_window_enum_arg a;
2040 a.x = x_root - _gdk_offset_x;
2041 a.y = y_root - _gdk_offset_y;
2042 a.ignore = drag_window ? GDK_WINDOW_HWND (drag_window) : NULL;
2045 EnumWindows (find_window_enum_proc, (LPARAM) &a);
2047 if (a.result == NULL)
2051 dw = gdk_win32_handle_table_lookup (a.result);
2054 dest_window = gdk_window_get_toplevel (dw);
2055 g_object_ref (dest_window);
2058 dest_window = gdk_win32_window_foreign_new_for_display (_gdk_display, a.result);
2061 *protocol = GDK_DRAG_PROTO_OLE2;
2062 else if (context->source_window)
2063 *protocol = GDK_DRAG_PROTO_LOCAL;
2065 *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
2069 g_print ("gdk_drag_find_window: %p %+d%+d: %p: %p %s\n",
2070 (drag_window ? GDK_WINDOW_HWND (drag_window) : NULL),
2073 (dest_window ? GDK_WINDOW_HWND (dest_window) : NULL),
2074 _gdk_win32_drag_protocol_to_string (*protocol)));
2080 gdk_win32_drag_context_drag_motion (GdkDragContext *context,
2081 GdkWindow *dest_window,
2082 GdkDragProtocol protocol,
2085 GdkDragAction suggested_action,
2086 GdkDragAction possible_actions,
2089 GdkWin32DragContext *context_win32;
2091 g_return_val_if_fail (context != NULL, FALSE);
2093 context->actions = possible_actions;
2095 GDK_NOTE (DND, g_print ("gdk_drag_motion: %s suggested=%s, possible=%s\n"
2096 " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2097 _gdk_win32_drag_protocol_to_string (protocol),
2098 _gdk_win32_drag_action_to_string (suggested_action),
2099 _gdk_win32_drag_action_to_string (possible_actions),
2101 _gdk_win32_drag_action_to_string (context->actions),
2102 _gdk_win32_drag_action_to_string (context->suggested_action),
2103 _gdk_win32_drag_action_to_string (context->action)));
2105 context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
2109 if (context->dest_window == dest_window)
2111 GdkDragContext *dest_context;
2113 dest_context = gdk_drag_context_find (FALSE,
2114 context->source_window,
2118 dest_context->actions = context->actions;
2120 context->suggested_action = suggested_action;
2124 GdkEvent *tmp_event;
2126 /* Send a leave to the last destination */
2127 gdk_drag_do_leave (context, time);
2128 context_win32->drag_status = GDK_DRAG_STATUS_DRAG;
2130 /* Check if new destination accepts drags, and which protocol */
2133 context->dest_window = dest_window;
2134 g_object_ref (context->dest_window);
2135 context->protocol = protocol;
2139 case GDK_DRAG_PROTO_LOCAL:
2140 local_send_enter (context, time);
2146 context->suggested_action = suggested_action;
2150 context->dest_window = NULL;
2151 context->action = 0;
2154 /* Push a status event, to let the client know that
2157 tmp_event = gdk_event_new (GDK_DRAG_STATUS);
2158 tmp_event->dnd.window = g_object_ref (context->source_window);
2159 /* We use this to signal a synthetic status. Perhaps
2160 * we should use an extra field...
2162 tmp_event->dnd.send_event = TRUE;
2164 tmp_event->dnd.context = g_object_ref (context);
2165 tmp_event->dnd.time = time;
2166 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
2168 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2169 gdk_event_put (tmp_event);
2170 gdk_event_free (tmp_event);
2173 /* Send a drag-motion event */
2175 context_win32->ole2_dnd_last_pt.x = x_root - _gdk_offset_x;
2176 context_win32->ole2_dnd_last_pt.y = y_root - _gdk_offset_y;
2178 if (context->dest_window)
2180 if (context_win32->drag_status == GDK_DRAG_STATUS_DRAG)
2182 switch (context->protocol)
2184 case GDK_DRAG_PROTO_LOCAL:
2185 local_send_motion (context, x_root, y_root, suggested_action, time);
2188 case GDK_DRAG_PROTO_NONE:
2189 g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
2198 GDK_NOTE (DND, g_print (" returning TRUE\n"
2199 " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2201 _gdk_win32_drag_action_to_string (context->actions),
2202 _gdk_win32_drag_action_to_string (context->suggested_action),
2203 _gdk_win32_drag_action_to_string (context->action)));
2209 GDK_NOTE (DND, g_print (" returning FALSE\n"
2210 " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2212 _gdk_win32_drag_action_to_string (context->actions),
2213 _gdk_win32_drag_action_to_string (context->suggested_action),
2214 _gdk_win32_drag_action_to_string (context->action)));
2219 gdk_win32_drag_context_drag_drop (GdkDragContext *context,
2222 g_return_if_fail (context != NULL);
2224 GDK_NOTE (DND, g_print ("gdk_drag_drop\n"));
2228 if (context->dest_window &&
2229 context->protocol == GDK_DRAG_PROTO_LOCAL)
2230 local_send_drop (context, time);
2234 _dnd_source_state = GDK_WIN32_DND_DROPPED;
2239 gdk_win32_drag_context_drag_abort (GdkDragContext *context,
2242 g_return_if_fail (context != NULL);
2244 GDK_NOTE (DND, g_print ("gdk_drag_abort\n"));
2247 _dnd_source_state = GDK_WIN32_DND_NONE;
2250 /* Destination side */
2253 gdk_win32_drag_context_drag_status (GdkDragContext *context,
2254 GdkDragAction action,
2257 GdkDragContext *src_context;
2258 GdkEvent *tmp_event;
2260 g_return_if_fail (context != NULL);
2262 GDK_NOTE (DND, g_print ("gdk_drag_status: %s\n"
2263 " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2264 _gdk_win32_drag_action_to_string (action),
2266 _gdk_win32_drag_action_to_string (context->actions),
2267 _gdk_win32_drag_action_to_string (context->suggested_action),
2268 _gdk_win32_drag_action_to_string (context->action)));
2270 context->action = action;
2274 src_context = gdk_drag_context_find (TRUE,
2275 context->source_window,
2276 context->dest_window);
2280 GdkWin32DragContext *src_context_win32 = GDK_WIN32_DRAG_CONTEXT (src_context);
2282 if (src_context_win32->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
2283 src_context_win32->drag_status = GDK_DRAG_STATUS_DRAG;
2285 tmp_event = gdk_event_new (GDK_DRAG_STATUS);
2286 tmp_event->dnd.window = g_object_ref (context->source_window);
2287 tmp_event->dnd.send_event = FALSE;
2288 tmp_event->dnd.context = g_object_ref (src_context);
2289 tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2290 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
2292 if (action == GDK_ACTION_DEFAULT)
2295 src_context->action = action;
2297 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2298 gdk_event_put (tmp_event);
2299 gdk_event_free (tmp_event);
2305 gdk_win32_drag_context_drop_reply (GdkDragContext *context,
2309 g_return_if_fail (context != NULL);
2311 GDK_NOTE (DND, g_print ("gdk_drop_reply\n"));
2314 if (context->dest_window)
2316 if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
2317 _gdk_dropfiles_store (NULL);
2322 gdk_win32_drag_context_drop_finish (GdkDragContext *context,
2326 GdkDragContext *src_context;
2327 GdkEvent *tmp_event;
2329 g_return_if_fail (context != NULL);
2331 GDK_NOTE (DND, g_print ("gdk_drop_finish\n"));
2335 src_context = gdk_drag_context_find (TRUE,
2336 context->source_window,
2337 context->dest_window);
2340 tmp_event = gdk_event_new (GDK_DROP_FINISHED);
2341 tmp_event->dnd.window = g_object_ref (src_context->source_window);
2342 tmp_event->dnd.send_event = FALSE;
2343 tmp_event->dnd.context = g_object_ref (src_context);
2344 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
2346 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2347 gdk_event_put (tmp_event);
2348 gdk_event_free (tmp_event);
2353 gdk_drag_do_leave (context, time);
2356 _dnd_target_state = GDK_WIN32_DND_DROPPED;
2358 _dnd_target_state = GDK_WIN32_DND_FAILED;
2364 static GdkFilterReturn
2365 gdk_destroy_filter (GdkXEvent *xev,
2369 MSG *msg = (MSG *) xev;
2371 if (msg->message == WM_DESTROY)
2373 IDropTarget *idtp = (IDropTarget *) data;
2375 GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %p\n", msg->hwnd));
2377 idtp->lpVtbl->Release (idtp);
2379 RevokeDragDrop (msg->hwnd);
2380 CoLockObjectExternal ((IUnknown*) idtp, FALSE, TRUE);
2382 return GDK_FILTER_CONTINUE;
2388 _gdk_win32_window_register_dnd (GdkWindow *window)
2390 target_drag_context *ctx;
2393 g_return_if_fail (window != NULL);
2395 if (gdk_window_get_window_type (window) == GDK_WINDOW_OFFSCREEN)
2398 if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
2401 g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
2403 GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %p\n", GDK_WINDOW_HWND (window)));
2407 /* We always claim to accept dropped files, but in fact we might not,
2408 * of course. This function is called in such a way that it cannot know
2409 * whether the window (widget) in question actually accepts files
2410 * (in gtk, data of type text/uri-list) or not.
2412 gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
2413 DragAcceptFiles (GDK_WINDOW_HWND (window), TRUE);
2417 /* Return if window is already setup for DND. */
2418 if (g_hash_table_lookup (target_ctx_for_window, GDK_WINDOW_HWND (window)) != NULL)
2421 /* Register for OLE2 d&d : similarly, claim to accept all supported
2422 * data types because we cannot know from here what the window
2425 /* FIXME: This of course won't work with user-extensible data types! */
2426 ctx = target_context_new (window);
2428 hr = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
2429 if (!SUCCEEDED (hr))
2430 OTHER_API_FAILED ("CoLockObjectExternal");
2433 hr = RegisterDragDrop (GDK_WINDOW_HWND (window), &ctx->idt);
2434 if (hr == DRAGDROP_E_ALREADYREGISTERED)
2436 g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
2437 CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSE, FALSE);
2439 else if (!SUCCEEDED (hr))
2440 OTHER_API_FAILED ("RegisterDragDrop");
2443 g_object_ref (window);
2444 g_hash_table_insert (target_ctx_for_window, GDK_WINDOW_HWND (window), ctx);
2451 gdk_win32_drag_context_drop_status (GdkDragContext *context)
2453 GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
2455 return ! context_win32->drop_failed;
2459 gdk_win32_drag_context_get_selection (GdkDragContext *context)
2461 switch (context->protocol)
2463 case GDK_DRAG_PROTO_LOCAL:
2465 case GDK_DRAG_PROTO_WIN32_DROPFILES:
2466 return _gdk_win32_dropfiles;
2467 case GDK_DRAG_PROTO_OLE2:
2468 return _gdk_ole2_dnd;
2475 gdk_win32_drag_context_class_init (GdkWin32DragContextClass *klass)
2477 GObjectClass *object_class = G_OBJECT_CLASS (klass);
2478 GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
2480 object_class->finalize = gdk_win32_drag_context_finalize;
2482 context_class->find_window = gdk_win32_drag_context_find_window;
2483 context_class->drag_status = gdk_win32_drag_context_drag_status;
2484 context_class->drag_motion = gdk_win32_drag_context_drag_motion;
2485 context_class->drag_abort = gdk_win32_drag_context_drag_abort;
2486 context_class->drag_drop = gdk_win32_drag_context_drag_drop;
2487 context_class->drop_reply = gdk_win32_drag_context_drop_reply;
2488 context_class->drop_finish = gdk_win32_drag_context_drop_finish;
2489 context_class->drop_status = gdk_win32_drag_context_drop_status;
2490 context_class->get_selection = gdk_win32_drag_context_get_selection;