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, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
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/.
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
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)
45 * Notes on implementation:
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).
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.
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.
76 #include "gdkproperty.h"
77 #include "gdkinternals.h"
78 #include "gdkprivate-win32.h"
86 #include <glib/gstdio.h>
88 typedef struct _GdkDragContextPrivateWin32 GdkDragContextPrivateWin32;
92 GDK_DRAG_STATUS_MOTION_WAIT,
93 GDK_DRAG_STATUS_ACTION_WAIT,
97 /* Structure that holds information about a drag in progress.
98 * this is used on both source and destination sides.
100 struct _GdkDragContextPrivateWin32 {
102 gboolean being_finalized;
105 DWORD last_key_state;
106 POINT last_pt; /* Coordinates from last event */
107 guint drag_status : 4; /* Current status of drag */
108 guint drop_failed : 1; /* Whether the drop was unsuccessful */
111 #define PRIVATE_DATA(context) ((GdkDragContextPrivateWin32 *) GDK_DRAG_CONTEXT (context)->windowing_data)
113 static GList *contexts;
114 static GdkDragContext *current_dest_drag = NULL;
116 static void gdk_drag_context_init (GdkDragContext *dragcontext);
117 static void gdk_drag_context_class_init (GdkDragContextClass *klass);
118 static void gdk_drag_context_finalize (GObject *object);
120 static gpointer parent_class = NULL;
122 static gboolean use_ole2_dnd = FALSE;
124 G_DEFINE_TYPE (GdkDragContext, gdk_drag_context, G_TYPE_OBJECT)
127 gdk_drag_context_init (GdkDragContext *dragcontext)
129 GdkDragContextPrivateWin32 *private;
131 private = G_TYPE_INSTANCE_GET_PRIVATE (dragcontext,
132 GDK_TYPE_DRAG_CONTEXT,
133 GdkDragContextPrivateWin32);
135 dragcontext->windowing_data = private;
139 contexts = g_list_prepend (contexts, dragcontext);
143 private->being_finalized = FALSE;
144 private->ref_count = 1;
145 private->iface = NULL;
148 GDK_NOTE (DND, g_print ("gdk_drag_context_init %p\n", dragcontext));
152 gdk_drag_context_class_init (GdkDragContextClass *klass)
154 GObjectClass *object_class = G_OBJECT_CLASS (klass);
156 parent_class = g_type_class_peek_parent (klass);
158 object_class->finalize = gdk_drag_context_finalize;
160 g_type_class_add_private (object_class, sizeof (GdkDragContextPrivateWin32));
164 gdk_drag_context_finalize (GObject *object)
166 GdkDragContext *context = GDK_DRAG_CONTEXT (object);
168 GDK_NOTE (DND, g_print ("gdk_drag_context_finalize %p\n", object));
170 g_list_free (context->targets);
172 if (context->source_window)
173 g_object_unref (context->source_window);
175 if (context->dest_window)
176 g_object_unref (context->dest_window);
180 contexts = g_list_remove (contexts, context);
182 if (context == current_dest_drag)
183 current_dest_drag = NULL;
187 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (context);
190 private->being_finalized = TRUE;
191 private->iface->lpVtbl->Release (private->iface);
192 private->iface = NULL;
196 G_OBJECT_CLASS (parent_class)->finalize (object);
202 gdk_drag_context_new (void)
204 return g_object_new (GDK_TYPE_DRAG_CONTEXT, NULL);
208 gdk_drag_context_get_device (GdkDragContext *context)
210 GdkDragContextPrivateWin32 *private;
212 g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
214 private = PRIVATE_DATA (context);
216 return private->device;
220 gdk_drag_context_set_device (GdkDragContext *context,
223 GdkDragContextPrivateWin32 *private;
225 g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
226 g_return_if_fail (GDK_IS_DEVICE (device));
228 private = PRIVATE_DATA (context);
232 g_object_unref (private->device);
233 private->device = NULL;
237 private->device = g_object_ref (device);
240 static GdkDragContext *
241 gdk_drag_context_find (gboolean is_source,
245 GList *tmp_list = contexts;
246 GdkDragContext *context;
247 GdkDragContextPrivateWin32 *private;
251 context = (GdkDragContext *)tmp_list->data;
252 private = PRIVATE_DATA (context);
254 if ((!context->is_source == !is_source) &&
255 ((source == NULL) || (context->source_window && (context->source_window == source))) &&
256 ((dest == NULL) || (context->dest_window && (context->dest_window == dest))))
259 tmp_list = tmp_list->next;
265 #define PRINT_GUID(guid) \
266 g_print ("%.08lx-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
267 ((gulong *) guid)[0], \
268 ((gushort *) guid)[2], \
269 ((gushort *) guid)[3], \
270 ((guchar *) guid)[8], \
271 ((guchar *) guid)[9], \
272 ((guchar *) guid)[10], \
273 ((guchar *) guid)[11], \
274 ((guchar *) guid)[12], \
275 ((guchar *) guid)[13], \
276 ((guchar *) guid)[14], \
277 ((guchar *) guid)[15]);
280 static FORMATETC *formats;
285 GdkDragContext *context;
286 } target_drag_context;
290 GdkDragContext *context;
291 } source_drag_context;
296 GdkDragContext *context;
305 static source_drag_context *pending_src_context = NULL;
306 static IDataObject *dnd_data = NULL;
308 static enum_formats *enum_formats_new (void);
310 /* map windows -> target drag contexts. The table
311 * owns a ref to both objects.
313 static GHashTable* target_ctx_for_window = NULL;
315 static ULONG STDMETHODCALLTYPE
316 idroptarget_addref (LPDROPTARGET This)
318 target_drag_context *ctx = (target_drag_context *) This;
319 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
320 int ref_count = ++private->ref_count;
322 GDK_NOTE (DND, g_print ("idroptarget_addref %p %d\n", This, ref_count));
323 g_object_ref (G_OBJECT (ctx->context));
328 static HRESULT STDMETHODCALLTYPE
329 idroptarget_queryinterface (LPDROPTARGET This,
334 g_print ("idroptarget_queryinterface %p ", This);
340 if (IsEqualGUID (riid, &IID_IUnknown))
342 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
343 idroptarget_addref (This);
347 else if (IsEqualGUID (riid, &IID_IDropTarget))
349 GDK_NOTE (DND, g_print ("...IDropTarget S_OK\n"));
350 idroptarget_addref (This);
356 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
357 return E_NOINTERFACE;
361 static ULONG STDMETHODCALLTYPE
362 idroptarget_release (LPDROPTARGET This)
364 target_drag_context *ctx = (target_drag_context *) This;
365 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
366 int ref_count = --private->ref_count;
368 GDK_NOTE (DND, g_print ("idroptarget_release %p %d\n", This, ref_count));
370 if (!private->being_finalized)
371 g_object_unref (G_OBJECT (ctx->context));
382 cf_to_atom (CLIPFORMAT cf)
389 return _text_uri_list;
395 return _text_uri_list;
397 if (cf == _cf_html_format || cf == _cf_text_html)
406 get_suggested_action (DWORD grfKeyState)
408 /* This is the yucky Windows standard: Force link action if both
409 * Control and Alt are down, copy if Control is down alone, move if
410 * Alt is down alone, or use default of move within the app or copy
411 * when origin of the drag is in another app.
413 if (grfKeyState & MK_CONTROL && grfKeyState & MK_SHIFT)
414 return GDK_ACTION_LINK; /* Link action not supported */
415 else if (grfKeyState & MK_CONTROL)
416 return GDK_ACTION_COPY;
417 else if (grfKeyState & MK_ALT)
418 return GDK_ACTION_MOVE;
419 #if 0 /* Default is always copy for now */
420 else if (_dnd_source_state == GDK_WIN32_DND_DRAGGING)
421 return GDK_ACTION_MOVE;
424 return GDK_ACTION_COPY;
425 /* Any way to determine when to add in DROPEFFECT_SCROLL? */
428 /* Process pending events -- we don't want to service non-GUI events
429 * forever so do one iteration and then do more only if there's a
433 process_pending_events ()
435 g_main_context_iteration (NULL, FALSE);
436 while (_gdk_event_queue_find_first (_gdk_display))
437 g_main_context_iteration (NULL, FALSE);
441 drop_effect_for_action (GdkDragAction action)
445 case GDK_ACTION_MOVE:
446 return DROPEFFECT_MOVE;
447 case GDK_ACTION_LINK:
448 return DROPEFFECT_LINK;
449 case GDK_ACTION_COPY:
450 return DROPEFFECT_COPY;
452 return DROPEFFECT_NONE;
457 dnd_event_put (GdkEventType type,
458 GdkDragContext *context,
460 gboolean to_dest_window)
464 e = gdk_event_new (type);
467 e->dnd.window = context->dest_window;
469 e->dnd.window = context->source_window;
470 e->dnd.send_event = FALSE;
471 e->dnd.context = g_object_ref (context);
472 e->dnd.time = GDK_CURRENT_TIME;
473 e->dnd.x_root = pt.x + _gdk_offset_x;
474 e->dnd.y_root = pt.x + _gdk_offset_y;
476 if (e->dnd.window != NULL)
477 g_object_ref (e->dnd.window);
479 gdk_event_set_device (e, gdk_drag_context_get_device (context));
481 GDK_NOTE (EVENTS, _gdk_win32_print_event (e));
486 static HRESULT STDMETHODCALLTYPE
487 idroptarget_dragenter (LPDROPTARGET This,
488 LPDATAOBJECT pDataObj,
493 target_drag_context *ctx = (target_drag_context *) This;
495 GDK_NOTE (DND, g_print ("idroptarget_dragenter %p S_OK\n", This));
497 ctx->context->suggested_action = get_suggested_action (grfKeyState);
498 dnd_event_put (GDK_DRAG_ENTER, ctx->context, pt, TRUE);
499 process_pending_events ();
500 *pdwEffect = drop_effect_for_action (ctx->context->action);
502 /* Assume that target can accept the data: In fact it may fail but
503 * we are not really set up to query the target!
508 static HRESULT STDMETHODCALLTYPE
509 idroptarget_dragover (LPDROPTARGET This,
514 target_drag_context *ctx = (target_drag_context *) This;
516 GDK_NOTE (DND, g_print ("idroptarget_dragover %p S_OK\n", This));
518 ctx->context->suggested_action = get_suggested_action (grfKeyState);
519 dnd_event_put (GDK_DRAG_MOTION, ctx->context, pt, TRUE);
520 process_pending_events ();
521 *pdwEffect = drop_effect_for_action (ctx->context->action);
526 static HRESULT STDMETHODCALLTYPE
527 idroptarget_dragleave (LPDROPTARGET This)
529 target_drag_context *ctx = (target_drag_context *) This;
530 POINTL pt = { 0, 0 };
532 GDK_NOTE (DND, g_print ("idroptarget_dragleave %p S_OK\n", This));
534 dnd_event_put (GDK_DRAG_LEAVE, ctx->context, pt, TRUE);
535 process_pending_events ();
540 static HRESULT STDMETHODCALLTYPE
541 idroptarget_drop (LPDROPTARGET This,
542 LPDATAOBJECT pDataObj,
547 target_drag_context *ctx = (target_drag_context *) This;
549 GDK_NOTE (DND, g_print ("idroptarget_drop %p ", This));
551 if (pDataObj == NULL)
553 GDK_NOTE (DND, g_print ("E_POINTER\n"));
559 ctx->context->suggested_action = get_suggested_action (grfKeyState);
560 dnd_event_put (GDK_DROP_START, ctx->context, pt, TRUE);
561 process_pending_events ();
565 /* Notify OLE of copy or move */
566 if (_dnd_target_state != GDK_WIN32_DND_DROPPED)
567 *pdwEffect = DROPEFFECT_NONE;
569 *pdwEffect = drop_effect_for_action (ctx->context->action);
571 GDK_NOTE (DND, g_print ("S_OK\n"));
576 static ULONG STDMETHODCALLTYPE
577 idropsource_addref (LPDROPSOURCE This)
579 source_drag_context *ctx = (source_drag_context *) This;
580 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
581 int ref_count = ++private->ref_count;
583 GDK_NOTE (DND, g_print ("idropsource_addref %p %d\n", This, ref_count));
584 g_object_ref (G_OBJECT (ctx->context));
589 static HRESULT STDMETHODCALLTYPE
590 idropsource_queryinterface (LPDROPSOURCE This,
595 g_print ("idropsource_queryinterface %p ", This);
601 if (IsEqualGUID (riid, &IID_IUnknown))
603 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
604 idropsource_addref (This);
608 else if (IsEqualGUID (riid, &IID_IDropSource))
610 GDK_NOTE (DND, g_print ("...IDropSource S_OK\n"));
611 idropsource_addref (This);
617 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
618 return E_NOINTERFACE;
622 static ULONG STDMETHODCALLTYPE
623 idropsource_release (LPDROPSOURCE This)
625 source_drag_context *ctx = (source_drag_context *) This;
626 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
627 int ref_count = --private->ref_count;
629 GDK_NOTE (DND, g_print ("idropsource_release %p %d\n", This, ref_count));
631 if (!private->being_finalized)
632 g_object_unref (G_OBJECT (ctx->context));
640 /* Emit GDK events for any changes in mouse events or control key
641 * state since the last recorded state. Return true if any events
642 * have been emitted and false otherwise.
645 send_change_events (GdkDragContext *ctx,
647 gboolean esc_pressed)
649 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx);
651 gboolean changed = FALSE;
652 HWND hwnd = GDK_WINDOW_HWND (ctx->source_window);
656 if (!API_CALL (GetCursorPos, (&pt)))
659 if (!API_CALL (ScreenToClient, (hwnd, &pt)))
662 if (pt.x != private->last_pt.x || pt.y != private->last_pt.y ||
663 key_state != private->last_key_state)
665 lparam = MAKELPARAM (pt.x, pt.y);
667 if (pt.x != private->last_pt.x || pt.y != private->last_pt.y)
669 GDK_NOTE (DND, g_print ("Sending WM_MOUSEMOVE (%ld,%ld)\n", pt.x, pt.y));
670 SendMessage (hwnd, WM_MOUSEMOVE, wparam, lparam);
673 if ((key_state & MK_LBUTTON) != (private->last_key_state & MK_LBUTTON))
675 if (key_state & MK_LBUTTON)
676 SendMessage (hwnd, WM_LBUTTONDOWN, wparam, lparam);
678 SendMessage (hwnd, WM_LBUTTONUP, wparam, lparam);
680 if ((key_state & MK_MBUTTON) != (private->last_key_state & MK_MBUTTON))
682 if (key_state & MK_MBUTTON)
683 SendMessage (hwnd, WM_MBUTTONDOWN, wparam, lparam);
685 SendMessage (hwnd, WM_MBUTTONUP, wparam, lparam);
687 if ((key_state & MK_RBUTTON) != (private->last_key_state & MK_RBUTTON))
689 if (key_state & MK_RBUTTON)
690 SendMessage (hwnd, WM_RBUTTONDOWN, wparam, lparam);
692 SendMessage (hwnd, WM_RBUTTONUP, wparam, lparam);
694 if ((key_state & MK_CONTROL) != (private->last_key_state & MK_CONTROL))
696 if (key_state & MK_CONTROL)
697 SendMessage (hwnd, WM_KEYDOWN, VK_CONTROL, 0);
699 SendMessage (hwnd, WM_KEYUP, VK_CONTROL, 0);
701 if ((key_state & MK_SHIFT) != (private->last_key_state & MK_SHIFT))
703 if (key_state & MK_CONTROL)
704 SendMessage (hwnd, WM_KEYDOWN, VK_SHIFT, 0);
706 SendMessage (hwnd, WM_KEYUP, VK_SHIFT, 0);
710 private->last_key_state = key_state;
711 private->last_pt = pt;
716 GDK_NOTE (DND, g_print ("Sending a escape key down message to %p\n", hwnd));
717 SendMessage (hwnd, WM_KEYDOWN, VK_ESCAPE, 0);
724 static HRESULT STDMETHODCALLTYPE
725 idropsource_querycontinuedrag (LPDROPSOURCE This,
729 source_drag_context *ctx = (source_drag_context *) This;
731 GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p ", This));
733 if (send_change_events (ctx->context, grfKeyState, fEscapePressed))
734 process_pending_events ();
736 if (_dnd_source_state == GDK_WIN32_DND_DROPPED)
738 GDK_NOTE (DND, g_print ("DRAGDROP_S_DROP\n"));
739 return DRAGDROP_S_DROP;
741 else if (_dnd_source_state == GDK_WIN32_DND_NONE)
743 GDK_NOTE (DND, g_print ("DRAGDROP_S_CANCEL\n"));
744 return DRAGDROP_S_CANCEL;
748 GDK_NOTE (DND, g_print ("S_OK\n"));
753 static HRESULT STDMETHODCALLTYPE
754 idropsource_givefeedback (LPDROPSOURCE This,
757 source_drag_context *ctx = (source_drag_context *) This;
758 GdkDragAction suggested_action;
760 GDK_NOTE (DND, g_print ("idropsource_givefeedback %p DRAGDROP_S_USEDEFAULTCURSORS\n", This));
762 if (dwEffect == DROPEFFECT_MOVE)
763 suggested_action = GDK_ACTION_MOVE;
765 suggested_action = GDK_ACTION_COPY;
766 ctx->context->action = suggested_action;
768 if (dwEffect == DROPEFFECT_NONE)
770 if (ctx->context->dest_window != NULL)
772 g_object_unref (ctx->context->dest_window);
773 ctx->context->dest_window = NULL;
778 if (ctx->context->dest_window == NULL)
779 ctx->context->dest_window = g_object_ref (_gdk_root);
782 return DRAGDROP_S_USEDEFAULTCURSORS;
785 static ULONG STDMETHODCALLTYPE
786 idataobject_addref (LPDATAOBJECT This)
788 data_object *dobj = (data_object *) This;
789 int ref_count = ++dobj->ref_count;
791 GDK_NOTE (DND, g_print ("idataobject_addref %p %d\n", This, ref_count));
796 static HRESULT STDMETHODCALLTYPE
797 idataobject_queryinterface (LPDATAOBJECT This,
802 g_print ("idataobject_queryinterface %p ", This);
808 if (IsEqualGUID (riid, &IID_IUnknown))
810 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
811 idataobject_addref (This);
815 else if (IsEqualGUID (riid, &IID_IDataObject))
817 GDK_NOTE (DND, g_print ("...IDataObject S_OK\n"));
818 idataobject_addref (This);
824 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
825 return E_NOINTERFACE;
829 static ULONG STDMETHODCALLTYPE
830 idataobject_release (LPDATAOBJECT This)
832 data_object *dobj = (data_object *) This;
833 int ref_count = --dobj->ref_count;
835 GDK_NOTE (DND, g_print ("idataobject_release %p %d\n", This, ref_count));
844 query (LPDATAOBJECT This,
845 LPFORMATETC pFormatEtc)
850 return DV_E_FORMATETC;
852 if (pFormatEtc->lindex != -1)
855 if ((pFormatEtc->tymed & TYMED_HGLOBAL) == 0)
858 if ((pFormatEtc->dwAspect & DVASPECT_CONTENT) == 0)
859 return DV_E_DVASPECT;
861 for (i = 0; i < nformats; i++)
862 if (pFormatEtc->cfFormat == formats[i].cfFormat)
865 return DV_E_FORMATETC;
868 static FORMATETC *active_pFormatEtc = NULL;
869 static STGMEDIUM *active_pMedium = NULL;
871 static HRESULT STDMETHODCALLTYPE
872 idataobject_getdata (LPDATAOBJECT This,
873 LPFORMATETC pFormatEtc,
876 data_object *ctx = (data_object *) This;
881 GDK_NOTE (DND, g_print ("idataobject_getdata %p %s ",
882 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
884 /* Check whether we can provide requested format */
885 hr = query (This, pFormatEtc);
889 /* Append a GDK_SELECTION_GET event and then hope the app sets the
890 * property associated with the _gdk_ole2_dnd atom
893 active_pFormatEtc = pFormatEtc;
894 active_pMedium = pMedium;
896 target = GDK_TARGET_STRING;
898 e.type = GDK_SELECTION_REQUEST;
899 e.selection.window = ctx->context->source_window;
900 e.selection.send_event = FALSE; /* ??? */
901 /* FIXME: Should really both selection and property be _gdk_ole2_dnd? */
902 e.selection.selection = _gdk_ole2_dnd;
904 e.selection.target = _utf8_string;
905 e.selection.property = _gdk_ole2_dnd;
906 e.selection.time = GDK_CURRENT_TIME;
908 g_object_ref (e.selection.window);
910 GDK_NOTE (EVENTS, _gdk_win32_print_event (&e));
912 process_pending_events ();
914 active_pFormatEtc = NULL;
915 active_pMedium = NULL;
917 if (pMedium->hGlobal == NULL) {
924 static HRESULT STDMETHODCALLTYPE
925 idataobject_getdatahere (LPDATAOBJECT This,
926 LPFORMATETC pFormatEtc,
929 GDK_NOTE (DND, g_print ("idataobject_getdatahere %p %s E_UNEXPECTED\n",
930 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
935 static HRESULT STDMETHODCALLTYPE
936 idataobject_querygetdata (LPDATAOBJECT This,
937 LPFORMATETC pFormatEtc)
941 hr = query (This, pFormatEtc);
943 #define CASE(x) case x: g_print (#x)
945 g_print ("idataobject_querygetdata %p %s \n",
946 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat));
949 CASE (DV_E_FORMATETC);
952 CASE (DV_E_DVASPECT);
954 default: g_print ("%#lx", hr);
961 static HRESULT STDMETHODCALLTYPE
962 idataobject_getcanonicalformatetc (LPDATAOBJECT This,
963 LPFORMATETC pFormatEtcIn,
964 LPFORMATETC pFormatEtcOut)
966 GDK_NOTE (DND, g_print ("idataobject_getcanonicalformatetc %p E_UNEXPECTED\n", This));
971 static HRESULT STDMETHODCALLTYPE
972 idataobject_setdata (LPDATAOBJECT This,
973 LPFORMATETC pFormatEtc,
977 GDK_NOTE (DND, g_print ("idataobject_setdata %p %s E_UNEXPECTED\n",
978 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
983 static HRESULT STDMETHODCALLTYPE
984 idataobject_enumformatetc (LPDATAOBJECT This,
986 LPENUMFORMATETC *ppEnumFormatEtc)
988 GDK_NOTE (DND, g_print ("idataobject_enumformatetc %p ", This));
990 if (dwDirection != DATADIR_GET)
992 GDK_NOTE (DND, g_print ("E_NOTIMPL\n"));
996 *ppEnumFormatEtc = &enum_formats_new ()->ief;
998 GDK_NOTE (DND, g_print ("%p S_OK\n", *ppEnumFormatEtc));
1003 static HRESULT STDMETHODCALLTYPE
1004 idataobject_dadvise (LPDATAOBJECT This,
1005 LPFORMATETC pFormatetc,
1007 LPADVISESINK pAdvSink,
1008 DWORD *pdwConnection)
1010 GDK_NOTE (DND, g_print ("idataobject_dadvise %p E_NOTIMPL\n", This));
1015 static HRESULT STDMETHODCALLTYPE
1016 idataobject_dunadvise (LPDATAOBJECT This,
1019 GDK_NOTE (DND, g_print ("idataobject_dunadvise %p E_NOTIMPL\n", This));
1024 static HRESULT STDMETHODCALLTYPE
1025 idataobject_enumdadvise (LPDATAOBJECT This,
1026 LPENUMSTATDATA *ppenumAdvise)
1028 GDK_NOTE (DND, g_print ("idataobject_enumdadvise %p OLE_E_ADVISENOTSUPPORTED\n", This));
1030 return OLE_E_ADVISENOTSUPPORTED;
1033 static ULONG STDMETHODCALLTYPE
1034 ienumformatetc_addref (LPENUMFORMATETC This)
1036 enum_formats *en = (enum_formats *) This;
1037 int ref_count = ++en->ref_count;
1039 GDK_NOTE (DND, g_print ("ienumformatetc_addref %p %d\n", This, ref_count));
1044 static HRESULT STDMETHODCALLTYPE
1045 ienumformatetc_queryinterface (LPENUMFORMATETC This,
1050 g_print ("ienumformatetc_queryinterface %p", This);
1056 if (IsEqualGUID (riid, &IID_IUnknown))
1058 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
1059 ienumformatetc_addref (This);
1063 else if (IsEqualGUID (riid, &IID_IEnumFORMATETC))
1065 GDK_NOTE (DND, g_print ("...IEnumFORMATETC S_OK\n"));
1066 ienumformatetc_addref (This);
1072 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
1073 return E_NOINTERFACE;
1077 static ULONG STDMETHODCALLTYPE
1078 ienumformatetc_release (LPENUMFORMATETC This)
1080 enum_formats *en = (enum_formats *) This;
1081 int ref_count = --en->ref_count;
1083 GDK_NOTE (DND, g_print ("ienumformatetc_release %p %d\n", This, ref_count));
1091 static HRESULT STDMETHODCALLTYPE
1092 ienumformatetc_next (LPENUMFORMATETC This,
1097 enum_formats *en = (enum_formats *) This;
1100 GDK_NOTE (DND, g_print ("ienumformatetc_next %p %d %ld ", This, en->ix, celt));
1103 for (i = 0; i < celt; i++)
1105 if (en->ix >= nformats)
1107 elts[i] = formats[en->ix++];
1114 GDK_NOTE (DND, g_print ("%s\n", (n == celt) ? "S_OK" : "S_FALSE"));
1122 static HRESULT STDMETHODCALLTYPE
1123 ienumformatetc_skip (LPENUMFORMATETC This,
1126 enum_formats *en = (enum_formats *) This;
1128 GDK_NOTE (DND, g_print ("ienumformatetc_skip %p %d %ld S_OK\n", This, en->ix, celt));
1135 static HRESULT STDMETHODCALLTYPE
1136 ienumformatetc_reset (LPENUMFORMATETC This)
1138 enum_formats *en = (enum_formats *) This;
1140 GDK_NOTE (DND, g_print ("ienumformatetc_reset %p S_OK\n", This));
1147 static HRESULT STDMETHODCALLTYPE
1148 ienumformatetc_clone (LPENUMFORMATETC This,
1149 LPENUMFORMATETC *ppEnumFormatEtc)
1151 enum_formats *en = (enum_formats *) This;
1154 GDK_NOTE (DND, g_print ("ienumformatetc_clone %p S_OK\n", This));
1156 new = enum_formats_new ();
1160 *ppEnumFormatEtc = &new->ief;
1165 static IDropTargetVtbl idt_vtbl = {
1166 idroptarget_queryinterface,
1168 idroptarget_release,
1169 idroptarget_dragenter,
1170 idroptarget_dragover,
1171 idroptarget_dragleave,
1175 static IDropSourceVtbl ids_vtbl = {
1176 idropsource_queryinterface,
1178 idropsource_release,
1179 idropsource_querycontinuedrag,
1180 idropsource_givefeedback
1183 static IDataObjectVtbl ido_vtbl = {
1184 idataobject_queryinterface,
1186 idataobject_release,
1187 idataobject_getdata,
1188 idataobject_getdatahere,
1189 idataobject_querygetdata,
1190 idataobject_getcanonicalformatetc,
1191 idataobject_setdata,
1192 idataobject_enumformatetc,
1193 idataobject_dadvise,
1194 idataobject_dunadvise,
1195 idataobject_enumdadvise
1198 static IEnumFORMATETCVtbl ief_vtbl = {
1199 ienumformatetc_queryinterface,
1200 ienumformatetc_addref,
1201 ienumformatetc_release,
1202 ienumformatetc_next,
1203 ienumformatetc_skip,
1204 ienumformatetc_reset,
1205 ienumformatetc_clone
1209 static target_drag_context *
1210 target_context_new (GdkWindow *window)
1212 target_drag_context *result;
1213 GdkDragContextPrivateWin32 *private;
1215 GdkDeviceManager *device_manager;
1217 result = g_new0 (target_drag_context, 1);
1219 result->idt.lpVtbl = &idt_vtbl;
1221 result->context = gdk_drag_context_new ();
1222 result->context->protocol = GDK_DRAG_PROTO_OLE2;
1223 result->context->is_source = FALSE;
1225 device_manager = gdk_display_get_device_manager (_gdk_display);
1226 device = gdk_device_manager_get_client_pointer (device_manager);
1227 gdk_drag_context_set_device (result->context, device);
1229 result->context->source_window = NULL;
1231 result->context->dest_window = window;
1232 g_object_ref (window);
1234 /* FIXME: result->context->targets? */
1236 result->context->actions = GDK_ACTION_DEFAULT | GDK_ACTION_COPY | GDK_ACTION_MOVE;
1237 result->context->suggested_action = GDK_ACTION_MOVE;
1238 result->context->action = GDK_ACTION_MOVE;
1240 private = result->context->windowing_data;
1241 private->iface = (IUnknown *) &result->idt;
1242 idroptarget_addref (&result->idt);
1244 GDK_NOTE (DND, g_print ("target_context_new: %p\n", result));
1249 static source_drag_context *
1250 source_context_new (GdkWindow *window,
1253 source_drag_context *result;
1254 GdkDragContextPrivateWin32 *private;
1256 GdkDeviceManager *device_manager;
1258 result = g_new0 (source_drag_context, 1);
1260 result->ids.lpVtbl = &ids_vtbl;
1262 result->context = gdk_drag_context_new ();
1263 result->context->protocol = GDK_DRAG_PROTO_OLE2;
1264 result->context->is_source = TRUE;
1266 device_manager = gdk_display_get_device_manager (_gdk_display);
1267 device = gdk_device_manager_get_client_pointer (device_manager);
1268 gdk_drag_context_set_device (result->context, device);
1270 result->context->source_window = window;
1271 g_object_ref (window);
1273 result->context->dest_window = NULL;
1274 result->context->targets = g_list_copy (targets);
1276 private = result->context->windowing_data;
1277 private->iface = (IUnknown *) &result->ids;
1278 idropsource_addref (&result->ids);
1280 GDK_NOTE (DND, g_print ("source_context_new: %p\n", result));
1285 static data_object *
1286 data_object_new (GdkDragContext *context)
1288 data_object *result;
1290 result = g_new0 (data_object, 1);
1292 result->ido.lpVtbl = &ido_vtbl;
1293 result->ref_count = 1;
1294 result->context = context;
1296 GDK_NOTE (DND, g_print ("data_object_new: %p\n", result));
1301 static enum_formats *
1302 enum_formats_new (void)
1304 enum_formats *result;
1306 result = g_new0 (enum_formats, 1);
1308 result->ief.lpVtbl = &ief_vtbl;
1309 result->ref_count = 1;
1316 _gdk_win32_ole2_dnd_property_change (GdkAtom type,
1323 HGLOBAL hdata = NULL;
1325 if (active_pFormatEtc == NULL || active_pMedium == NULL)
1328 /* Set up the data buffer for wide character text request */
1329 if (active_pFormatEtc->cfFormat == CF_UNICODETEXT)
1334 wdata = g_utf8_to_utf16 ((const char *) data, -1, NULL, &wlen, NULL);
1335 hdata = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (wlen + 1) * 2);
1338 wchar_t *ptr = (wchar_t *) GlobalLock(hdata);
1339 memcpy (ptr, wdata, (wlen + 1) * 2);
1340 GlobalUnlock(hdata);
1345 g_warning ("Only text handled for now");
1348 active_pMedium->tymed = TYMED_HGLOBAL;
1349 active_pMedium->hGlobal = hdata;
1350 active_pMedium->pUnkForRelease = 0;
1354 /* From MS Knowledge Base article Q130698 */
1357 resolve_link (HWND hWnd,
1361 WIN32_FILE_ATTRIBUTE_DATA wfad;
1363 IShellLinkW *pslW = NULL;
1364 IPersistFile *ppf = NULL;
1366 /* Check if the file is empty first because IShellLink::Resolve for
1367 * some reason succeeds with an empty file and returns an empty
1368 * "link target". (#524151)
1370 if (!GetFileAttributesExW (link, GetFileExInfoStandard, &wfad) ||
1371 (wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
1374 /* Assume failure to start with: */
1377 /* Call CoCreateInstance to obtain the IShellLink interface
1378 * pointer. This call fails if CoInitialize is not called, so it is
1379 * assumed that CoInitialize has been called.
1382 hr = CoCreateInstance (&CLSID_ShellLink,
1384 CLSCTX_INPROC_SERVER,
1390 /* The IShellLink interface supports the IPersistFile
1391 * interface. Get an interface pointer to it.
1393 hr = pslW->lpVtbl->QueryInterface (pslW,
1400 /* Load the file. */
1401 hr = ppf->lpVtbl->Load (ppf, link, STGM_READ);
1406 /* Resolve the link by calling the Resolve()
1407 * interface function.
1409 hr = pslW->lpVtbl->Resolve (pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
1414 wchar_t wtarget[MAX_PATH];
1416 hr = pslW->lpVtbl->GetPath (pslW, wtarget, MAX_PATH, NULL, 0);
1418 *lpszPath = g_utf16_to_utf8 (wtarget, -1, NULL, NULL, NULL);
1422 ppf->lpVtbl->Release (ppf);
1425 pslW->lpVtbl->Release (pslW);
1427 return SUCCEEDED (hr);
1432 /* Check for filenames like C:\Users\tml\AppData\Local\Temp\d5qtkvvs.bmp */
1434 filename_looks_tempish (const char *filename)
1439 gboolean retval = FALSE;
1441 dirname = g_path_get_dirname (filename);
1444 q = g_get_tmp_dir ();
1447 ((G_IS_DIR_SEPARATOR (*p) && G_IS_DIR_SEPARATOR (*q)) ||
1448 g_ascii_tolower (*p) == g_ascii_tolower (*q)))
1460 close_it (gpointer data)
1462 close (GPOINTER_TO_INT (data));
1469 static GdkFilterReturn
1470 gdk_dropfiles_filter (GdkXEvent *xev,
1474 GdkDragContext *context;
1476 MSG *msg = (MSG *) xev;
1480 gchar *fileName, *linkedFile;
1482 GdkDeviceManager *device_manager;
1484 if (msg->message == WM_DROPFILES)
1486 GDK_NOTE (DND, g_print ("WM_DROPFILES: %p\n", msg->hwnd));
1488 context = gdk_drag_context_new ();
1489 context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
1490 context->is_source = FALSE;
1492 device_manager = gdk_display_get_device_manager (_gdk_display);
1493 device = gdk_device_manager_get_client_pointer (device_manager);
1494 gdk_drag_context_set_device (context, device);
1496 context->source_window = _gdk_root;
1497 g_object_ref (context->source_window);
1499 context->dest_window = event->any.window;
1500 g_object_ref (context->dest_window);
1502 /* WM_DROPFILES drops are always file names */
1504 g_list_append (NULL, _text_uri_list);
1505 context->actions = GDK_ACTION_COPY;
1506 context->suggested_action = GDK_ACTION_COPY;
1507 current_dest_drag = context;
1509 event->dnd.type = GDK_DROP_START;
1510 event->dnd.context = current_dest_drag;
1511 gdk_event_set_device (event, gdk_drag_context_get_device (current_dest_drag));
1513 hdrop = (HANDLE) msg->wParam;
1514 DragQueryPoint (hdrop, &pt);
1515 ClientToScreen (msg->hwnd, &pt);
1517 event->dnd.x_root = pt.x + _gdk_offset_x;
1518 event->dnd.y_root = pt.y + _gdk_offset_y;
1519 event->dnd.time = _gdk_win32_get_next_tick (msg->time);
1521 nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
1523 result = g_string_new (NULL);
1524 for (i = 0; i < nfiles; i++)
1527 wchar_t wfn[MAX_PATH];
1529 DragQueryFileW (hdrop, i, wfn, MAX_PATH);
1530 fileName = g_utf16_to_utf8 (wfn, -1, NULL, NULL, NULL);
1532 /* Resolve shortcuts */
1533 if (resolve_link (msg->hwnd, wfn, &linkedFile))
1535 uri = g_filename_to_uri (linkedFile, NULL, NULL);
1538 g_string_append (result, uri);
1539 GDK_NOTE (DND, g_print ("... %s link to %s: %s\n",
1540 fileName, linkedFile, uri));
1544 fileName = linkedFile;
1548 uri = g_filename_to_uri (fileName, NULL, NULL);
1551 g_string_append (result, uri);
1552 GDK_NOTE (DND, g_print ("... %s: %s\n", fileName, uri));
1558 /* Awful hack to recognize temp files corresponding to
1559 * images dragged from Firefox... Open the file right here
1560 * so that it is less likely that Firefox manages to delete
1561 * it before the GTK+-using app (typically GIMP) has opened
1564 * Not compiled in for now, because it means images dragged
1565 * from Firefox would stay around in the temp folder which
1566 * is not what Firefox intended. I don't feel comfortable
1567 * with that, both from a geenral sanity point of view, and
1568 * from a privacy point of view. It's better to wait for
1569 * Firefox to fix the problem, for instance by deleting the
1570 * temp file after a longer delay, or to wait until we
1571 * implement the OLE2_DND...
1573 if (filename_looks_tempish (fileName))
1575 int fd = g_open (fileName, _O_RDONLY|_O_BINARY, 0);
1578 GDK_NOTE (DND, g_print ("Could not open %s, maybe an image dragged from Firefox that it already deleted\n", fileName));
1582 GDK_NOTE (DND, g_print ("Opened %s as %d so that Firefox won't delete it\n", fileName, fd));
1583 g_timeout_add_seconds (1, close_it, GINT_TO_POINTER (fd));
1589 g_string_append (result, "\015\012");
1591 _gdk_dropfiles_store (result->str);
1592 g_string_free (result, FALSE);
1596 return GDK_FILTER_TRANSLATE;
1599 return GDK_FILTER_CONTINUE;
1603 add_format (GArray *fmts,
1610 fmt.dwAspect = DVASPECT_CONTENT;
1612 fmt.tymed = TYMED_HGLOBAL;
1614 g_array_append_val (fmts, fmt);
1619 _gdk_dnd_init (void)
1621 if (getenv ("GDK_WIN32_USE_EXPERIMENTAL_OLE2_DND"))
1622 use_ole2_dnd = TRUE;
1629 hr = OleInitialize (NULL);
1631 if (! SUCCEEDED (hr))
1632 g_error ("OleInitialize failed");
1634 fmts = g_array_new (FALSE, FALSE, sizeof (FORMATETC));
1636 /* The most important presumably */
1637 add_format (fmts, CF_UNICODETEXT);
1639 /* Used for GTK+ internal DND, I think was the intent? Anyway, code below assumes
1640 * this is at index 1.
1642 add_format (fmts, CF_GDIOBJFIRST);
1644 add_format (fmts, CF_HDROP);
1646 add_format (fmts, _cf_png);
1647 add_format (fmts, CF_DIB);
1649 add_format (fmts, _cf_url);
1650 add_format (fmts, _cf_html_format);
1651 add_format (fmts, _cf_text_html);
1653 nformats = fmts->len;
1654 formats = (FORMATETC*) g_array_free (fmts, FALSE);
1656 target_ctx_for_window = g_hash_table_new (g_direct_hash, g_direct_equal);
1661 _gdk_win32_dnd_exit (void)
1672 local_send_leave (GdkDragContext *context,
1675 GdkEvent *tmp_event;
1677 GDK_NOTE (DND, g_print ("local_send_leave: context=%p current_dest_drag=%p\n",
1679 current_dest_drag));
1681 if ((current_dest_drag != NULL) &&
1682 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1683 (current_dest_drag->source_window == context->source_window))
1685 tmp_event = gdk_event_new (GDK_DRAG_LEAVE);
1687 tmp_event->dnd.window = g_object_ref (context->dest_window);
1688 /* Pass ownership of context to the event */
1689 tmp_event->dnd.send_event = FALSE;
1690 tmp_event->dnd.context = g_object_ref (current_dest_drag);
1691 tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1692 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
1694 current_dest_drag = NULL;
1696 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1697 gdk_event_put (tmp_event);
1698 gdk_event_free (tmp_event);
1703 local_send_enter (GdkDragContext *context,
1706 GdkEvent *tmp_event;
1707 GdkDragContextPrivateWin32 *private;
1708 GdkDragContext *new_context;
1710 GdkDeviceManager *device_manager;
1712 GDK_NOTE (DND, g_print ("local_send_enter: context=%p current_dest_drag=%p\n",
1714 current_dest_drag));
1716 private = PRIVATE_DATA (context);
1718 if (current_dest_drag != NULL)
1720 g_object_unref (G_OBJECT (current_dest_drag));
1721 current_dest_drag = NULL;
1724 new_context = gdk_drag_context_new ();
1725 new_context->protocol = GDK_DRAG_PROTO_LOCAL;
1726 new_context->is_source = FALSE;
1728 device_manager = gdk_display_get_device_manager (_gdk_display);
1729 device = gdk_device_manager_get_client_pointer (device_manager);
1730 gdk_drag_context_set_device (new_context, device);
1732 new_context->source_window = context->source_window;
1733 g_object_ref (new_context->source_window);
1735 new_context->dest_window = context->dest_window;
1736 g_object_ref (new_context->dest_window);
1738 new_context->targets = g_list_copy (context->targets);
1740 gdk_window_set_events (new_context->source_window,
1741 gdk_window_get_events (new_context->source_window) |
1742 GDK_PROPERTY_CHANGE_MASK);
1743 new_context->actions = context->actions;
1745 tmp_event = gdk_event_new (GDK_DRAG_ENTER);
1746 tmp_event->dnd.window = g_object_ref (context->dest_window);
1747 tmp_event->dnd.send_event = FALSE;
1748 tmp_event->dnd.context = g_object_ref (new_context);
1749 tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1750 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
1752 current_dest_drag = new_context;
1754 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1755 gdk_event_put (tmp_event);
1756 gdk_event_free (tmp_event);
1760 local_send_motion (GdkDragContext *context,
1763 GdkDragAction action,
1766 GdkEvent *tmp_event;
1768 GDK_NOTE (DND, g_print ("local_send_motion: context=%p (%d,%d) current_dest_drag=%p\n",
1769 context, x_root, y_root,
1770 current_dest_drag));
1772 if ((current_dest_drag != NULL) &&
1773 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1774 (current_dest_drag->source_window == context->source_window))
1776 tmp_event = gdk_event_new (GDK_DRAG_MOTION);
1777 tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
1778 tmp_event->dnd.send_event = FALSE;
1779 tmp_event->dnd.context = g_object_ref (current_dest_drag);
1780 tmp_event->dnd.time = time;
1781 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
1783 current_dest_drag->suggested_action = action;
1785 tmp_event->dnd.x_root = x_root;
1786 tmp_event->dnd.y_root = y_root;
1788 PRIVATE_DATA (current_dest_drag)->last_pt.x = x_root - _gdk_offset_x;
1789 PRIVATE_DATA (current_dest_drag)->last_pt.y = y_root - _gdk_offset_y;
1791 PRIVATE_DATA (context)->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1793 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1794 gdk_event_put (tmp_event);
1795 gdk_event_free (tmp_event);
1800 local_send_drop (GdkDragContext *context,
1803 GdkEvent *tmp_event;
1805 GDK_NOTE (DND, g_print ("local_send_drop: context=%p current_dest_drag=%p\n",
1807 current_dest_drag));
1809 if ((current_dest_drag != NULL) &&
1810 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1811 (current_dest_drag->source_window == context->source_window))
1813 GdkDragContextPrivateWin32 *private;
1814 private = PRIVATE_DATA (current_dest_drag);
1816 /* Pass ownership of context to the event */
1817 tmp_event = gdk_event_new (GDK_DROP_START);
1818 tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
1819 tmp_event->dnd.send_event = FALSE;
1820 tmp_event->dnd.context = g_object_ref (current_dest_drag);
1821 tmp_event->dnd.time = GDK_CURRENT_TIME;
1822 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
1824 tmp_event->dnd.x_root = private->last_pt.x + _gdk_offset_x;
1825 tmp_event->dnd.y_root = private->last_pt.y + _gdk_offset_y;
1827 current_dest_drag = NULL;
1829 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1830 gdk_event_put (tmp_event);
1831 gdk_event_free (tmp_event);
1837 gdk_drag_do_leave (GdkDragContext *context,
1840 if (context->dest_window)
1842 GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
1846 if (context->protocol == GDK_DRAG_PROTO_LOCAL)
1847 local_send_leave (context, time);
1850 g_object_unref (context->dest_window);
1851 context->dest_window = NULL;
1856 gdk_drag_begin (GdkWindow *window,
1861 GdkDragContext *new_context;
1863 GdkDeviceManager *device_manager;
1865 g_return_val_if_fail (window != NULL, NULL);
1867 new_context = gdk_drag_context_new ();
1869 device_manager = gdk_display_get_device_manager (_gdk_display);
1870 device = gdk_device_manager_get_client_pointer (device_manager);
1871 gdk_drag_context_set_device (new_context, device);
1873 new_context->is_source = TRUE;
1875 new_context->source_window = window;
1876 g_object_ref (window);
1878 new_context->targets = g_list_copy (targets);
1879 new_context->actions = 0;
1885 source_drag_context *ctx;
1887 g_return_val_if_fail (window != NULL, NULL);
1889 GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
1891 ctx = source_context_new (window, targets);
1893 _dnd_source_state = GDK_WIN32_DND_PENDING;
1895 pending_src_context = ctx;
1896 g_object_ref (ctx->context);
1898 return ctx->context;
1903 _gdk_win32_dnd_do_dragdrop (void)
1907 GdkDragContext* drag_ctx;
1908 GdkDragContextPrivateWin32 *private;
1909 BYTE kbd_state[256];
1918 if (pending_src_context == NULL)
1921 drag_ctx = pending_src_context->context;
1922 private = PRIVATE_DATA (drag_ctx);
1924 dobj = data_object_new (drag_ctx);
1926 API_CALL (GetCursorPos, (&private->last_pt));
1927 API_CALL (ScreenToClient, (GDK_WINDOW_HWND (drag_ctx->source_window), &private->last_pt));
1928 private->last_key_state = 0;
1929 API_CALL (GetKeyboardState, (kbd_state));
1931 if (kbd_state[VK_CONTROL])
1932 private->last_key_state |= MK_CONTROL;
1933 if (kbd_state[VK_SHIFT])
1934 private->last_key_state |= MK_SHIFT;
1935 if (kbd_state[VK_LBUTTON])
1936 private->last_key_state |= MK_LBUTTON;
1937 if (kbd_state[VK_MBUTTON])
1938 private->last_key_state |= MK_MBUTTON;
1939 if (kbd_state[VK_RBUTTON])
1940 private->last_key_state |= MK_RBUTTON;
1943 global = GlobalAlloc (GMEM_FIXED, sizeof (ctx));
1945 memcpy (&global, ctx, sizeof (ctx));
1947 medium.tymed = TYMED_HGLOBAL;
1948 medium.hGlobal = global;
1949 medium.pUnkForRelease = NULL;
1951 /* FIXME I wish I remember what I was thinking of here, i.e. what
1952 * the formats[1] signifies, i.e. the CF_GDIOBJFIRST FORMATETC?
1954 dobj->ido.lpVtbl->SetData (&dobj->ido, &formats[1], &medium, TRUE);
1957 /* Start dragging with mainloop inside the OLE2 API. Exits only when done */
1959 GDK_NOTE (DND, g_print ("Calling DoDragDrop\n"));
1961 _gdk_win32_begin_modal_call ();
1962 hr = DoDragDrop (&dobj->ido, &pending_src_context->ids,
1963 DROPEFFECT_COPY | DROPEFFECT_MOVE,
1965 _gdk_win32_end_modal_call ();
1967 GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n",
1968 (hr == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
1969 (hr == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
1970 (hr == E_UNEXPECTED ? "E_UNEXPECTED" :
1971 g_strdup_printf ("%#.8lx", hr))))));
1973 /* Delete dnd selection after successful move */
1974 if (hr == DRAGDROP_S_DROP && dwEffect == DROPEFFECT_MOVE)
1978 tmp_event.type = GDK_SELECTION_REQUEST;
1979 tmp_event.selection.window = drag_ctx->source_window;
1980 tmp_event.selection.send_event = FALSE;
1981 tmp_event.selection.selection = _gdk_ole2_dnd;
1982 tmp_event.selection.target = _delete;
1983 tmp_event.selection.property = _gdk_ole2_dnd; /* ??? */
1984 tmp_event.selection.time = GDK_CURRENT_TIME; /* ??? */
1985 g_object_ref (tmp_event.selection.window);
1987 GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
1988 gdk_event_put (&tmp_event);
1992 // Send a GDK_DROP_FINISHED to the source window
1996 if ( pending_src_context != NULL && pending_src_context->context != NULL
1997 && pending_src_context->context->source_window != NULL )
1998 push_dnd_event (GDK_DROP_FINISHED, pending_src_context->context, ptl, FALSE);
2001 dobj->ido.lpVtbl->Release (&dobj->ido);
2002 if (pending_src_context != NULL)
2004 pending_src_context->ids.lpVtbl->Release (&pending_src_context->ids);
2005 pending_src_context = NULL;
2011 gdk_drag_get_protocol_for_display (GdkDisplay *display,
2012 GdkNativeWindow xid,
2013 GdkDragProtocol *protocol)
2017 window = gdk_window_lookup (xid);
2019 gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
2021 if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
2024 *protocol = GDK_DRAG_PROTO_OLE2;
2026 *protocol = GDK_DRAG_PROTO_LOCAL;
2036 gdk_drag_find_window_for_screen (GdkDragContext *context,
2037 GdkWindow *drag_window,
2041 GdkWindow **dest_window,
2042 GdkDragProtocol *protocol)
2047 pt.x = x_root - _gdk_offset_x;
2048 pt.y = y_root - _gdk_offset_y;
2050 hwnd = WindowFromPoint (pt);
2053 *dest_window = NULL;
2056 *dest_window = gdk_win32_handle_table_lookup (hwnd);
2058 g_object_ref (*dest_window);
2060 *dest_window = gdk_window_foreign_new_for_display (_gdk_display, hwnd);
2063 *protocol = GDK_DRAG_PROTO_OLE2;
2064 else if (context->source_window)
2065 *protocol = GDK_DRAG_PROTO_LOCAL;
2067 *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
2071 g_print ("gdk_drag_find_window: %p %+d%+d: %p: %p %s\n",
2072 (drag_window ? GDK_WINDOW_HWND (drag_window) : NULL),
2075 (*dest_window ? GDK_WINDOW_HWND (*dest_window) : NULL),
2076 _gdk_win32_drag_protocol_to_string (*protocol)));
2080 gdk_drag_motion (GdkDragContext *context,
2081 GdkWindow *dest_window,
2082 GdkDragProtocol protocol,
2085 GdkDragAction suggested_action,
2086 GdkDragAction possible_actions,
2089 GdkDragContextPrivateWin32 *private;
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 private = PRIVATE_DATA (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 private->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 private->last_pt.x = x_root - _gdk_offset_x;
2176 private->last_pt.y = y_root - _gdk_offset_y;
2178 if (context->dest_window)
2180 if (private->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_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_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_drag_status (GdkDragContext *context,
2254 GdkDragAction action,
2257 GdkDragContextPrivateWin32 *private;
2258 GdkDragContext *src_context;
2259 GdkEvent *tmp_event;
2261 g_return_if_fail (context != NULL);
2263 private = PRIVATE_DATA (context);
2265 GDK_NOTE (DND, g_print ("gdk_drag_status: %s\n"
2266 " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2267 _gdk_win32_drag_action_to_string (action),
2269 _gdk_win32_drag_action_to_string (context->actions),
2270 _gdk_win32_drag_action_to_string (context->suggested_action),
2271 _gdk_win32_drag_action_to_string (context->action)));
2273 context->action = action;
2277 src_context = gdk_drag_context_find (TRUE,
2278 context->source_window,
2279 context->dest_window);
2283 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (src_context);
2285 if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
2286 private->drag_status = GDK_DRAG_STATUS_DRAG;
2288 tmp_event = gdk_event_new (GDK_DRAG_STATUS);
2289 tmp_event->dnd.window = g_object_ref (context->source_window);
2290 tmp_event->dnd.send_event = FALSE;
2291 tmp_event->dnd.context = g_object_ref (src_context);
2292 tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2293 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
2295 if (action == GDK_ACTION_DEFAULT)
2298 src_context->action = action;
2300 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2301 gdk_event_put (tmp_event);
2302 gdk_event_free (tmp_event);
2308 gdk_drop_reply (GdkDragContext *context,
2312 g_return_if_fail (context != NULL);
2314 GDK_NOTE (DND, g_print ("gdk_drop_reply\n"));
2317 if (context->dest_window)
2319 if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
2320 _gdk_dropfiles_store (NULL);
2325 gdk_drop_finish (GdkDragContext *context,
2329 GdkDragContextPrivateWin32 *private;
2330 GdkDragContext *src_context;
2331 GdkEvent *tmp_event;
2333 g_return_if_fail (context != NULL);
2335 GDK_NOTE (DND, g_print ("gdk_drop_finish\n"));
2337 private = PRIVATE_DATA (context);
2341 src_context = gdk_drag_context_find (TRUE,
2342 context->source_window,
2343 context->dest_window);
2346 tmp_event = gdk_event_new (GDK_DROP_FINISHED);
2347 tmp_event->dnd.window = g_object_ref (src_context->source_window);
2348 tmp_event->dnd.send_event = FALSE;
2349 tmp_event->dnd.context = g_object_ref (src_context);
2350 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
2352 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2353 gdk_event_put (tmp_event);
2354 gdk_event_free (tmp_event);
2359 gdk_drag_do_leave (context, time);
2362 _dnd_target_state = GDK_WIN32_DND_DROPPED;
2364 _dnd_target_state = GDK_WIN32_DND_FAILED;
2370 static GdkFilterReturn
2371 gdk_destroy_filter (GdkXEvent *xev,
2375 MSG *msg = (MSG *) xev;
2377 if (msg->message == WM_DESTROY)
2379 IDropTarget *idtp = (IDropTarget *) data;
2381 GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %p\n", msg->hwnd));
2383 idtp->lpVtbl->Release (idtp);
2385 RevokeDragDrop (msg->hwnd);
2386 CoLockObjectExternal ((IUnknown*) idtp, FALSE, TRUE);
2388 return GDK_FILTER_CONTINUE;
2394 gdk_window_register_dnd (GdkWindow *window)
2396 target_drag_context *ctx;
2399 g_return_if_fail (window != NULL);
2401 if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
2404 g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
2406 GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %p\n", GDK_WINDOW_HWND (window)));
2410 /* We always claim to accept dropped files, but in fact we might not,
2411 * of course. This function is called in such a way that it cannot know
2412 * whether the window (widget) in question actually accepts files
2413 * (in gtk, data of type text/uri-list) or not.
2415 gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
2416 DragAcceptFiles (GDK_WINDOW_HWND (window), TRUE);
2420 /* Return if window is already setup for DND. */
2421 if (g_hash_table_lookup (target_ctx_for_window, GDK_WINDOW_HWND (window)) != NULL)
2424 /* Register for OLE2 d&d : similarly, claim to accept all supported
2425 * data types because we cannot know from here what the window
2428 /* FIXME: This of course won't work with user-extensible data types! */
2429 ctx = target_context_new (window);
2431 hr = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
2432 if (!SUCCEEDED (hr))
2433 OTHER_API_FAILED ("CoLockObjectExternal");
2436 hr = RegisterDragDrop (GDK_WINDOW_HWND (window), &ctx->idt);
2437 if (hr == DRAGDROP_E_ALREADYREGISTERED)
2439 g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
2440 CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSE, FALSE);
2442 else if (!SUCCEEDED (hr))
2443 OTHER_API_FAILED ("RegisterDragDrop");
2446 g_object_ref (window);
2447 g_hash_table_insert (target_ctx_for_window, GDK_WINDOW_HWND (window), ctx);
2454 gdk_drag_get_selection (GdkDragContext *context)
2456 switch (context->protocol)
2458 case GDK_DRAG_PROTO_LOCAL:
2460 case GDK_DRAG_PROTO_WIN32_DROPFILES:
2461 return _gdk_win32_dropfiles;
2462 case GDK_DRAG_PROTO_OLE2:
2463 return _gdk_ole2_dnd;
2470 gdk_drag_drop_succeeded (GdkDragContext *context)
2472 GdkDragContextPrivateWin32 *private;
2474 g_return_val_if_fail (context != NULL, FALSE);
2476 private = PRIVATE_DATA (context);
2478 /* FIXME: Can we set drop_failed when the drop has failed? */
2479 return !private->drop_failed;