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"
80 #include "gdkwin32dnd.h"
81 #include "gdk/gdkdndprivate.h"
89 #include <glib/gstdio.h>
91 typedef struct _GdkDragContextPrivateWin32 GdkDragContextPrivateWin32;
95 GDK_DRAG_STATUS_MOTION_WAIT,
96 GDK_DRAG_STATUS_ACTION_WAIT,
100 /* Structure that holds information about a drag in progress.
101 * this is used on both source and destination sides.
103 struct _GdkDragContextPrivateWin32 {
104 gboolean being_finalized;
107 DWORD last_key_state;
108 POINT last_pt; /* Coordinates from last event */
109 guint drag_status : 4; /* Current status of drag */
110 guint drop_failed : 1; /* Whether the drop was unsuccessful */
113 #define PRIVATE_DATA(context) (GDK_WIN32_DRAG_CONTEXT (context)->windowing_data)
115 static GList *contexts;
116 static GdkDragContext *current_dest_drag = NULL;
118 struct _GdkWin32DragContext
120 GdkDragContext context;
122 GdkDragContextPrivateWin32 *windowing_data;
125 struct _GdkWin32DragContextClass
127 GdkDragContextClass parent_class;
130 G_DEFINE_TYPE (GdkWin32DragContext, gdk_win32_drag_context, GDK_TYPE_DRAG_CONTEXT)
132 static gboolean use_ole2_dnd = FALSE;
135 gdk_win32_drag_context_init (GdkWin32DragContext *dragcontext)
137 GdkDragContextPrivateWin32 *private;
139 private = G_TYPE_INSTANCE_GET_PRIVATE (dragcontext,
140 GDK_TYPE_DRAG_CONTEXT,
141 GdkDragContextPrivateWin32);
143 dragcontext->windowing_data = private;
147 contexts = g_list_prepend (contexts, dragcontext);
151 private->being_finalized = FALSE;
152 private->ref_count = 1;
153 private->iface = NULL;
156 GDK_NOTE (DND, g_print ("gdk_drag_context_init %p\n", dragcontext));
160 gdk_win32_drag_context_finalize (GObject *object)
162 GdkDragContext *context = GDK_DRAG_CONTEXT (object);
164 GDK_NOTE (DND, g_print ("gdk_drag_context_finalize %p\n", object));
166 g_list_free (context->targets);
168 if (context->source_window)
169 g_object_unref (context->source_window);
171 if (context->dest_window)
172 g_object_unref (context->dest_window);
176 contexts = g_list_remove (contexts, context);
178 if (context == current_dest_drag)
179 current_dest_drag = NULL;
183 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (context);
186 private->being_finalized = TRUE;
187 private->iface->lpVtbl->Release (private->iface);
188 private->iface = NULL;
192 G_OBJECT_CLASS (gdk_win32_drag_context_parent_class)->finalize (object);
198 gdk_drag_context_new (void)
200 return g_object_new (GDK_TYPE_DRAG_CONTEXT, NULL);
203 static GdkDragContext *
204 gdk_drag_context_find (gboolean is_source,
208 GList *tmp_list = contexts;
209 GdkDragContext *context;
210 GdkDragContextPrivateWin32 *private;
214 context = (GdkDragContext *)tmp_list->data;
215 private = PRIVATE_DATA (context);
217 if ((!context->is_source == !is_source) &&
218 ((source == NULL) || (context->source_window && (context->source_window == source))) &&
219 ((dest == NULL) || (context->dest_window && (context->dest_window == dest))))
222 tmp_list = tmp_list->next;
228 #define PRINT_GUID(guid) \
229 g_print ("%.08lx-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
230 ((gulong *) guid)[0], \
231 ((gushort *) guid)[2], \
232 ((gushort *) guid)[3], \
233 ((guchar *) guid)[8], \
234 ((guchar *) guid)[9], \
235 ((guchar *) guid)[10], \
236 ((guchar *) guid)[11], \
237 ((guchar *) guid)[12], \
238 ((guchar *) guid)[13], \
239 ((guchar *) guid)[14], \
240 ((guchar *) guid)[15]);
243 static FORMATETC *formats;
248 GdkDragContext *context;
249 } target_drag_context;
253 GdkDragContext *context;
254 } source_drag_context;
259 GdkDragContext *context;
268 static source_drag_context *pending_src_context = NULL;
269 static IDataObject *dnd_data = NULL;
271 static enum_formats *enum_formats_new (void);
273 /* map windows -> target drag contexts. The table
274 * owns a ref to both objects.
276 static GHashTable* target_ctx_for_window = NULL;
278 static ULONG STDMETHODCALLTYPE
279 idroptarget_addref (LPDROPTARGET This)
281 target_drag_context *ctx = (target_drag_context *) This;
282 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
283 int ref_count = ++private->ref_count;
285 GDK_NOTE (DND, g_print ("idroptarget_addref %p %d\n", This, ref_count));
286 g_object_ref (G_OBJECT (ctx->context));
291 static HRESULT STDMETHODCALLTYPE
292 idroptarget_queryinterface (LPDROPTARGET This,
297 g_print ("idroptarget_queryinterface %p ", This);
303 if (IsEqualGUID (riid, &IID_IUnknown))
305 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
306 idroptarget_addref (This);
310 else if (IsEqualGUID (riid, &IID_IDropTarget))
312 GDK_NOTE (DND, g_print ("...IDropTarget S_OK\n"));
313 idroptarget_addref (This);
319 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
320 return E_NOINTERFACE;
324 static ULONG STDMETHODCALLTYPE
325 idroptarget_release (LPDROPTARGET This)
327 target_drag_context *ctx = (target_drag_context *) This;
328 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
329 int ref_count = --private->ref_count;
331 GDK_NOTE (DND, g_print ("idroptarget_release %p %d\n", This, ref_count));
333 if (!private->being_finalized)
334 g_object_unref (G_OBJECT (ctx->context));
345 cf_to_atom (CLIPFORMAT cf)
352 return _text_uri_list;
358 return _text_uri_list;
360 if (cf == _cf_html_format || cf == _cf_text_html)
369 get_suggested_action (DWORD grfKeyState)
371 /* This is the yucky Windows standard: Force link action if both
372 * Control and Alt are down, copy if Control is down alone, move if
373 * Alt is down alone, or use default of move within the app or copy
374 * when origin of the drag is in another app.
376 if (grfKeyState & MK_CONTROL && grfKeyState & MK_SHIFT)
377 return GDK_ACTION_LINK; /* Link action not supported */
378 else if (grfKeyState & MK_CONTROL)
379 return GDK_ACTION_COPY;
380 else if (grfKeyState & MK_ALT)
381 return GDK_ACTION_MOVE;
382 #if 0 /* Default is always copy for now */
383 else if (_dnd_source_state == GDK_WIN32_DND_DRAGGING)
384 return GDK_ACTION_MOVE;
387 return GDK_ACTION_COPY;
388 /* Any way to determine when to add in DROPEFFECT_SCROLL? */
391 /* Process pending events -- we don't want to service non-GUI events
392 * forever so do one iteration and then do more only if there's a
396 process_pending_events ()
398 g_main_context_iteration (NULL, FALSE);
399 while (_gdk_event_queue_find_first (_gdk_display))
400 g_main_context_iteration (NULL, FALSE);
404 drop_effect_for_action (GdkDragAction action)
408 case GDK_ACTION_MOVE:
409 return DROPEFFECT_MOVE;
410 case GDK_ACTION_LINK:
411 return DROPEFFECT_LINK;
412 case GDK_ACTION_COPY:
413 return DROPEFFECT_COPY;
415 return DROPEFFECT_NONE;
420 dnd_event_put (GdkEventType type,
421 GdkDragContext *context,
423 gboolean to_dest_window)
427 e = gdk_event_new (type);
430 e->dnd.window = context->dest_window;
432 e->dnd.window = context->source_window;
433 e->dnd.send_event = FALSE;
434 e->dnd.context = g_object_ref (context);
435 e->dnd.time = GDK_CURRENT_TIME;
436 e->dnd.x_root = pt.x + _gdk_offset_x;
437 e->dnd.y_root = pt.x + _gdk_offset_y;
439 if (e->dnd.window != NULL)
440 g_object_ref (e->dnd.window);
442 gdk_event_set_device (e, gdk_drag_context_get_device (context));
444 GDK_NOTE (EVENTS, _gdk_win32_print_event (e));
449 static HRESULT STDMETHODCALLTYPE
450 idroptarget_dragenter (LPDROPTARGET This,
451 LPDATAOBJECT pDataObj,
456 target_drag_context *ctx = (target_drag_context *) This;
458 GDK_NOTE (DND, g_print ("idroptarget_dragenter %p S_OK\n", This));
460 ctx->context->suggested_action = get_suggested_action (grfKeyState);
461 dnd_event_put (GDK_DRAG_ENTER, ctx->context, pt, TRUE);
462 process_pending_events ();
463 *pdwEffect = drop_effect_for_action (ctx->context->action);
465 /* Assume that target can accept the data: In fact it may fail but
466 * we are not really set up to query the target!
471 static HRESULT STDMETHODCALLTYPE
472 idroptarget_dragover (LPDROPTARGET This,
477 target_drag_context *ctx = (target_drag_context *) This;
479 GDK_NOTE (DND, g_print ("idroptarget_dragover %p S_OK\n", This));
481 ctx->context->suggested_action = get_suggested_action (grfKeyState);
482 dnd_event_put (GDK_DRAG_MOTION, ctx->context, pt, TRUE);
483 process_pending_events ();
484 *pdwEffect = drop_effect_for_action (ctx->context->action);
489 static HRESULT STDMETHODCALLTYPE
490 idroptarget_dragleave (LPDROPTARGET This)
492 target_drag_context *ctx = (target_drag_context *) This;
493 POINTL pt = { 0, 0 };
495 GDK_NOTE (DND, g_print ("idroptarget_dragleave %p S_OK\n", This));
497 dnd_event_put (GDK_DRAG_LEAVE, ctx->context, pt, TRUE);
498 process_pending_events ();
503 static HRESULT STDMETHODCALLTYPE
504 idroptarget_drop (LPDROPTARGET This,
505 LPDATAOBJECT pDataObj,
510 target_drag_context *ctx = (target_drag_context *) This;
512 GDK_NOTE (DND, g_print ("idroptarget_drop %p ", This));
514 if (pDataObj == NULL)
516 GDK_NOTE (DND, g_print ("E_POINTER\n"));
522 ctx->context->suggested_action = get_suggested_action (grfKeyState);
523 dnd_event_put (GDK_DROP_START, ctx->context, pt, TRUE);
524 process_pending_events ();
528 /* Notify OLE of copy or move */
529 if (_dnd_target_state != GDK_WIN32_DND_DROPPED)
530 *pdwEffect = DROPEFFECT_NONE;
532 *pdwEffect = drop_effect_for_action (ctx->context->action);
534 GDK_NOTE (DND, g_print ("S_OK\n"));
539 static ULONG STDMETHODCALLTYPE
540 idropsource_addref (LPDROPSOURCE This)
542 source_drag_context *ctx = (source_drag_context *) This;
543 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
544 int ref_count = ++private->ref_count;
546 GDK_NOTE (DND, g_print ("idropsource_addref %p %d\n", This, ref_count));
547 g_object_ref (G_OBJECT (ctx->context));
552 static HRESULT STDMETHODCALLTYPE
553 idropsource_queryinterface (LPDROPSOURCE This,
558 g_print ("idropsource_queryinterface %p ", This);
564 if (IsEqualGUID (riid, &IID_IUnknown))
566 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
567 idropsource_addref (This);
571 else if (IsEqualGUID (riid, &IID_IDropSource))
573 GDK_NOTE (DND, g_print ("...IDropSource S_OK\n"));
574 idropsource_addref (This);
580 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
581 return E_NOINTERFACE;
585 static ULONG STDMETHODCALLTYPE
586 idropsource_release (LPDROPSOURCE This)
588 source_drag_context *ctx = (source_drag_context *) This;
589 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
590 int ref_count = --private->ref_count;
592 GDK_NOTE (DND, g_print ("idropsource_release %p %d\n", This, ref_count));
594 if (!private->being_finalized)
595 g_object_unref (G_OBJECT (ctx->context));
603 /* Emit GDK events for any changes in mouse events or control key
604 * state since the last recorded state. Return true if any events
605 * have been emitted and false otherwise.
608 send_change_events (GdkDragContext *ctx,
610 gboolean esc_pressed)
612 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx);
614 gboolean changed = FALSE;
615 HWND hwnd = GDK_WINDOW_HWND (ctx->source_window);
619 if (!API_CALL (GetCursorPos, (&pt)))
622 if (!API_CALL (ScreenToClient, (hwnd, &pt)))
625 if (pt.x != private->last_pt.x || pt.y != private->last_pt.y ||
626 key_state != private->last_key_state)
628 lparam = MAKELPARAM (pt.x, pt.y);
630 if (pt.x != private->last_pt.x || pt.y != private->last_pt.y)
632 GDK_NOTE (DND, g_print ("Sending WM_MOUSEMOVE (%ld,%ld)\n", pt.x, pt.y));
633 SendMessage (hwnd, WM_MOUSEMOVE, wparam, lparam);
636 if ((key_state & MK_LBUTTON) != (private->last_key_state & MK_LBUTTON))
638 if (key_state & MK_LBUTTON)
639 SendMessage (hwnd, WM_LBUTTONDOWN, wparam, lparam);
641 SendMessage (hwnd, WM_LBUTTONUP, wparam, lparam);
643 if ((key_state & MK_MBUTTON) != (private->last_key_state & MK_MBUTTON))
645 if (key_state & MK_MBUTTON)
646 SendMessage (hwnd, WM_MBUTTONDOWN, wparam, lparam);
648 SendMessage (hwnd, WM_MBUTTONUP, wparam, lparam);
650 if ((key_state & MK_RBUTTON) != (private->last_key_state & MK_RBUTTON))
652 if (key_state & MK_RBUTTON)
653 SendMessage (hwnd, WM_RBUTTONDOWN, wparam, lparam);
655 SendMessage (hwnd, WM_RBUTTONUP, wparam, lparam);
657 if ((key_state & MK_CONTROL) != (private->last_key_state & MK_CONTROL))
659 if (key_state & MK_CONTROL)
660 SendMessage (hwnd, WM_KEYDOWN, VK_CONTROL, 0);
662 SendMessage (hwnd, WM_KEYUP, VK_CONTROL, 0);
664 if ((key_state & MK_SHIFT) != (private->last_key_state & MK_SHIFT))
666 if (key_state & MK_CONTROL)
667 SendMessage (hwnd, WM_KEYDOWN, VK_SHIFT, 0);
669 SendMessage (hwnd, WM_KEYUP, VK_SHIFT, 0);
673 private->last_key_state = key_state;
674 private->last_pt = pt;
679 GDK_NOTE (DND, g_print ("Sending a escape key down message to %p\n", hwnd));
680 SendMessage (hwnd, WM_KEYDOWN, VK_ESCAPE, 0);
687 static HRESULT STDMETHODCALLTYPE
688 idropsource_querycontinuedrag (LPDROPSOURCE This,
692 source_drag_context *ctx = (source_drag_context *) This;
694 GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p ", This));
696 if (send_change_events (ctx->context, grfKeyState, fEscapePressed))
697 process_pending_events ();
699 if (_dnd_source_state == GDK_WIN32_DND_DROPPED)
701 GDK_NOTE (DND, g_print ("DRAGDROP_S_DROP\n"));
702 return DRAGDROP_S_DROP;
704 else if (_dnd_source_state == GDK_WIN32_DND_NONE)
706 GDK_NOTE (DND, g_print ("DRAGDROP_S_CANCEL\n"));
707 return DRAGDROP_S_CANCEL;
711 GDK_NOTE (DND, g_print ("S_OK\n"));
716 static HRESULT STDMETHODCALLTYPE
717 idropsource_givefeedback (LPDROPSOURCE This,
720 source_drag_context *ctx = (source_drag_context *) This;
721 GdkDragAction suggested_action;
723 GDK_NOTE (DND, g_print ("idropsource_givefeedback %p DRAGDROP_S_USEDEFAULTCURSORS\n", This));
725 if (dwEffect == DROPEFFECT_MOVE)
726 suggested_action = GDK_ACTION_MOVE;
728 suggested_action = GDK_ACTION_COPY;
729 ctx->context->action = suggested_action;
731 if (dwEffect == DROPEFFECT_NONE)
733 if (ctx->context->dest_window != NULL)
735 g_object_unref (ctx->context->dest_window);
736 ctx->context->dest_window = NULL;
741 if (ctx->context->dest_window == NULL)
742 ctx->context->dest_window = g_object_ref (_gdk_root);
745 return DRAGDROP_S_USEDEFAULTCURSORS;
748 static ULONG STDMETHODCALLTYPE
749 idataobject_addref (LPDATAOBJECT This)
751 data_object *dobj = (data_object *) This;
752 int ref_count = ++dobj->ref_count;
754 GDK_NOTE (DND, g_print ("idataobject_addref %p %d\n", This, ref_count));
759 static HRESULT STDMETHODCALLTYPE
760 idataobject_queryinterface (LPDATAOBJECT This,
765 g_print ("idataobject_queryinterface %p ", This);
771 if (IsEqualGUID (riid, &IID_IUnknown))
773 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
774 idataobject_addref (This);
778 else if (IsEqualGUID (riid, &IID_IDataObject))
780 GDK_NOTE (DND, g_print ("...IDataObject S_OK\n"));
781 idataobject_addref (This);
787 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
788 return E_NOINTERFACE;
792 static ULONG STDMETHODCALLTYPE
793 idataobject_release (LPDATAOBJECT This)
795 data_object *dobj = (data_object *) This;
796 int ref_count = --dobj->ref_count;
798 GDK_NOTE (DND, g_print ("idataobject_release %p %d\n", This, ref_count));
807 query (LPDATAOBJECT This,
808 LPFORMATETC pFormatEtc)
813 return DV_E_FORMATETC;
815 if (pFormatEtc->lindex != -1)
818 if ((pFormatEtc->tymed & TYMED_HGLOBAL) == 0)
821 if ((pFormatEtc->dwAspect & DVASPECT_CONTENT) == 0)
822 return DV_E_DVASPECT;
824 for (i = 0; i < nformats; i++)
825 if (pFormatEtc->cfFormat == formats[i].cfFormat)
828 return DV_E_FORMATETC;
831 static FORMATETC *active_pFormatEtc = NULL;
832 static STGMEDIUM *active_pMedium = NULL;
834 static HRESULT STDMETHODCALLTYPE
835 idataobject_getdata (LPDATAOBJECT This,
836 LPFORMATETC pFormatEtc,
839 data_object *ctx = (data_object *) This;
844 GDK_NOTE (DND, g_print ("idataobject_getdata %p %s ",
845 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
847 /* Check whether we can provide requested format */
848 hr = query (This, pFormatEtc);
852 /* Append a GDK_SELECTION_GET event and then hope the app sets the
853 * property associated with the _gdk_ole2_dnd atom
856 active_pFormatEtc = pFormatEtc;
857 active_pMedium = pMedium;
859 target = GDK_TARGET_STRING;
861 e.type = GDK_SELECTION_REQUEST;
862 e.selection.window = ctx->context->source_window;
863 e.selection.send_event = FALSE; /* ??? */
864 /* FIXME: Should really both selection and property be _gdk_ole2_dnd? */
865 e.selection.selection = _gdk_ole2_dnd;
867 e.selection.target = _utf8_string;
868 e.selection.property = _gdk_ole2_dnd;
869 e.selection.time = GDK_CURRENT_TIME;
871 g_object_ref (e.selection.window);
873 GDK_NOTE (EVENTS, _gdk_win32_print_event (&e));
875 process_pending_events ();
877 active_pFormatEtc = NULL;
878 active_pMedium = NULL;
880 if (pMedium->hGlobal == NULL) {
887 static HRESULT STDMETHODCALLTYPE
888 idataobject_getdatahere (LPDATAOBJECT This,
889 LPFORMATETC pFormatEtc,
892 GDK_NOTE (DND, g_print ("idataobject_getdatahere %p %s E_UNEXPECTED\n",
893 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
898 static HRESULT STDMETHODCALLTYPE
899 idataobject_querygetdata (LPDATAOBJECT This,
900 LPFORMATETC pFormatEtc)
904 hr = query (This, pFormatEtc);
906 #define CASE(x) case x: g_print (#x)
908 g_print ("idataobject_querygetdata %p %s \n",
909 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat));
912 CASE (DV_E_FORMATETC);
915 CASE (DV_E_DVASPECT);
917 default: g_print ("%#lx", hr);
924 static HRESULT STDMETHODCALLTYPE
925 idataobject_getcanonicalformatetc (LPDATAOBJECT This,
926 LPFORMATETC pFormatEtcIn,
927 LPFORMATETC pFormatEtcOut)
929 GDK_NOTE (DND, g_print ("idataobject_getcanonicalformatetc %p E_UNEXPECTED\n", This));
934 static HRESULT STDMETHODCALLTYPE
935 idataobject_setdata (LPDATAOBJECT This,
936 LPFORMATETC pFormatEtc,
940 GDK_NOTE (DND, g_print ("idataobject_setdata %p %s E_UNEXPECTED\n",
941 This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
946 static HRESULT STDMETHODCALLTYPE
947 idataobject_enumformatetc (LPDATAOBJECT This,
949 LPENUMFORMATETC *ppEnumFormatEtc)
951 GDK_NOTE (DND, g_print ("idataobject_enumformatetc %p ", This));
953 if (dwDirection != DATADIR_GET)
955 GDK_NOTE (DND, g_print ("E_NOTIMPL\n"));
959 *ppEnumFormatEtc = &enum_formats_new ()->ief;
961 GDK_NOTE (DND, g_print ("%p S_OK\n", *ppEnumFormatEtc));
966 static HRESULT STDMETHODCALLTYPE
967 idataobject_dadvise (LPDATAOBJECT This,
968 LPFORMATETC pFormatetc,
970 LPADVISESINK pAdvSink,
971 DWORD *pdwConnection)
973 GDK_NOTE (DND, g_print ("idataobject_dadvise %p E_NOTIMPL\n", This));
978 static HRESULT STDMETHODCALLTYPE
979 idataobject_dunadvise (LPDATAOBJECT This,
982 GDK_NOTE (DND, g_print ("idataobject_dunadvise %p E_NOTIMPL\n", This));
987 static HRESULT STDMETHODCALLTYPE
988 idataobject_enumdadvise (LPDATAOBJECT This,
989 LPENUMSTATDATA *ppenumAdvise)
991 GDK_NOTE (DND, g_print ("idataobject_enumdadvise %p OLE_E_ADVISENOTSUPPORTED\n", This));
993 return OLE_E_ADVISENOTSUPPORTED;
996 static ULONG STDMETHODCALLTYPE
997 ienumformatetc_addref (LPENUMFORMATETC This)
999 enum_formats *en = (enum_formats *) This;
1000 int ref_count = ++en->ref_count;
1002 GDK_NOTE (DND, g_print ("ienumformatetc_addref %p %d\n", This, ref_count));
1007 static HRESULT STDMETHODCALLTYPE
1008 ienumformatetc_queryinterface (LPENUMFORMATETC This,
1013 g_print ("ienumformatetc_queryinterface %p", This);
1019 if (IsEqualGUID (riid, &IID_IUnknown))
1021 GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
1022 ienumformatetc_addref (This);
1026 else if (IsEqualGUID (riid, &IID_IEnumFORMATETC))
1028 GDK_NOTE (DND, g_print ("...IEnumFORMATETC S_OK\n"));
1029 ienumformatetc_addref (This);
1035 GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
1036 return E_NOINTERFACE;
1040 static ULONG STDMETHODCALLTYPE
1041 ienumformatetc_release (LPENUMFORMATETC This)
1043 enum_formats *en = (enum_formats *) This;
1044 int ref_count = --en->ref_count;
1046 GDK_NOTE (DND, g_print ("ienumformatetc_release %p %d\n", This, ref_count));
1054 static HRESULT STDMETHODCALLTYPE
1055 ienumformatetc_next (LPENUMFORMATETC This,
1060 enum_formats *en = (enum_formats *) This;
1063 GDK_NOTE (DND, g_print ("ienumformatetc_next %p %d %ld ", This, en->ix, celt));
1066 for (i = 0; i < celt; i++)
1068 if (en->ix >= nformats)
1070 elts[i] = formats[en->ix++];
1077 GDK_NOTE (DND, g_print ("%s\n", (n == celt) ? "S_OK" : "S_FALSE"));
1085 static HRESULT STDMETHODCALLTYPE
1086 ienumformatetc_skip (LPENUMFORMATETC This,
1089 enum_formats *en = (enum_formats *) This;
1091 GDK_NOTE (DND, g_print ("ienumformatetc_skip %p %d %ld S_OK\n", This, en->ix, celt));
1098 static HRESULT STDMETHODCALLTYPE
1099 ienumformatetc_reset (LPENUMFORMATETC This)
1101 enum_formats *en = (enum_formats *) This;
1103 GDK_NOTE (DND, g_print ("ienumformatetc_reset %p S_OK\n", This));
1110 static HRESULT STDMETHODCALLTYPE
1111 ienumformatetc_clone (LPENUMFORMATETC This,
1112 LPENUMFORMATETC *ppEnumFormatEtc)
1114 enum_formats *en = (enum_formats *) This;
1117 GDK_NOTE (DND, g_print ("ienumformatetc_clone %p S_OK\n", This));
1119 new = enum_formats_new ();
1123 *ppEnumFormatEtc = &new->ief;
1128 static IDropTargetVtbl idt_vtbl = {
1129 idroptarget_queryinterface,
1131 idroptarget_release,
1132 idroptarget_dragenter,
1133 idroptarget_dragover,
1134 idroptarget_dragleave,
1138 static IDropSourceVtbl ids_vtbl = {
1139 idropsource_queryinterface,
1141 idropsource_release,
1142 idropsource_querycontinuedrag,
1143 idropsource_givefeedback
1146 static IDataObjectVtbl ido_vtbl = {
1147 idataobject_queryinterface,
1149 idataobject_release,
1150 idataobject_getdata,
1151 idataobject_getdatahere,
1152 idataobject_querygetdata,
1153 idataobject_getcanonicalformatetc,
1154 idataobject_setdata,
1155 idataobject_enumformatetc,
1156 idataobject_dadvise,
1157 idataobject_dunadvise,
1158 idataobject_enumdadvise
1161 static IEnumFORMATETCVtbl ief_vtbl = {
1162 ienumformatetc_queryinterface,
1163 ienumformatetc_addref,
1164 ienumformatetc_release,
1165 ienumformatetc_next,
1166 ienumformatetc_skip,
1167 ienumformatetc_reset,
1168 ienumformatetc_clone
1172 static target_drag_context *
1173 target_context_new (GdkWindow *window)
1175 target_drag_context *result;
1176 GdkDragContextPrivateWin32 *private;
1178 GdkDeviceManager *device_manager;
1180 result = g_new0 (target_drag_context, 1);
1182 result->idt.lpVtbl = &idt_vtbl;
1184 result->context = gdk_drag_context_new ();
1185 result->context->protocol = GDK_DRAG_PROTO_OLE2;
1186 result->context->is_source = FALSE;
1188 device_manager = gdk_display_get_device_manager (_gdk_display);
1189 device = gdk_device_manager_get_client_pointer (device_manager);
1190 gdk_drag_context_set_device (result->context, device);
1192 result->context->source_window = NULL;
1194 result->context->dest_window = window;
1195 g_object_ref (window);
1197 /* FIXME: result->context->targets? */
1199 result->context->actions = GDK_ACTION_DEFAULT | GDK_ACTION_COPY | GDK_ACTION_MOVE;
1200 result->context->suggested_action = GDK_ACTION_MOVE;
1201 result->context->action = GDK_ACTION_MOVE;
1203 private = PRIVATE_DATA(result->context);
1204 private->iface = (IUnknown *) &result->idt;
1205 idroptarget_addref (&result->idt);
1207 GDK_NOTE (DND, g_print ("target_context_new: %p\n", result));
1212 static source_drag_context *
1213 source_context_new (GdkWindow *window,
1216 source_drag_context *result;
1217 GdkDragContextPrivateWin32 *private;
1219 GdkDeviceManager *device_manager;
1221 result = g_new0 (source_drag_context, 1);
1223 result->ids.lpVtbl = &ids_vtbl;
1225 result->context = gdk_drag_context_new ();
1226 result->context->protocol = GDK_DRAG_PROTO_OLE2;
1227 result->context->is_source = TRUE;
1229 device_manager = gdk_display_get_device_manager (_gdk_display);
1230 device = gdk_device_manager_get_client_pointer (device_manager);
1231 gdk_drag_context_set_device (result->context, device);
1233 result->context->source_window = window;
1234 g_object_ref (window);
1236 result->context->dest_window = NULL;
1237 result->context->targets = g_list_copy (targets);
1239 private = PRIVATE_DATA(result->context);
1240 private->iface = (IUnknown *) &result->ids;
1241 idropsource_addref (&result->ids);
1243 GDK_NOTE (DND, g_print ("source_context_new: %p\n", result));
1248 static data_object *
1249 data_object_new (GdkDragContext *context)
1251 data_object *result;
1253 result = g_new0 (data_object, 1);
1255 result->ido.lpVtbl = &ido_vtbl;
1256 result->ref_count = 1;
1257 result->context = context;
1259 GDK_NOTE (DND, g_print ("data_object_new: %p\n", result));
1264 static enum_formats *
1265 enum_formats_new (void)
1267 enum_formats *result;
1269 result = g_new0 (enum_formats, 1);
1271 result->ief.lpVtbl = &ief_vtbl;
1272 result->ref_count = 1;
1279 _gdk_win32_ole2_dnd_property_change (GdkAtom type,
1286 HGLOBAL hdata = NULL;
1288 if (active_pFormatEtc == NULL || active_pMedium == NULL)
1291 /* Set up the data buffer for wide character text request */
1292 if (active_pFormatEtc->cfFormat == CF_UNICODETEXT)
1297 wdata = g_utf8_to_utf16 ((const char *) data, -1, NULL, &wlen, NULL);
1298 hdata = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (wlen + 1) * 2);
1301 wchar_t *ptr = (wchar_t *) GlobalLock(hdata);
1302 memcpy (ptr, wdata, (wlen + 1) * 2);
1303 GlobalUnlock(hdata);
1308 g_warning ("Only text handled for now");
1311 active_pMedium->tymed = TYMED_HGLOBAL;
1312 active_pMedium->hGlobal = hdata;
1313 active_pMedium->pUnkForRelease = 0;
1317 /* From MS Knowledge Base article Q130698 */
1320 resolve_link (HWND hWnd,
1324 WIN32_FILE_ATTRIBUTE_DATA wfad;
1326 IShellLinkW *pslW = NULL;
1327 IPersistFile *ppf = NULL;
1329 /* Check if the file is empty first because IShellLink::Resolve for
1330 * some reason succeeds with an empty file and returns an empty
1331 * "link target". (#524151)
1333 if (!GetFileAttributesExW (link, GetFileExInfoStandard, &wfad) ||
1334 (wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
1337 /* Assume failure to start with: */
1340 /* Call CoCreateInstance to obtain the IShellLink interface
1341 * pointer. This call fails if CoInitialize is not called, so it is
1342 * assumed that CoInitialize has been called.
1345 hr = CoCreateInstance (&CLSID_ShellLink,
1347 CLSCTX_INPROC_SERVER,
1353 /* The IShellLink interface supports the IPersistFile
1354 * interface. Get an interface pointer to it.
1356 hr = pslW->lpVtbl->QueryInterface (pslW,
1363 /* Load the file. */
1364 hr = ppf->lpVtbl->Load (ppf, link, STGM_READ);
1369 /* Resolve the link by calling the Resolve()
1370 * interface function.
1372 hr = pslW->lpVtbl->Resolve (pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
1377 wchar_t wtarget[MAX_PATH];
1379 hr = pslW->lpVtbl->GetPath (pslW, wtarget, MAX_PATH, NULL, 0);
1381 *lpszPath = g_utf16_to_utf8 (wtarget, -1, NULL, NULL, NULL);
1385 ppf->lpVtbl->Release (ppf);
1388 pslW->lpVtbl->Release (pslW);
1390 return SUCCEEDED (hr);
1395 /* Check for filenames like C:\Users\tml\AppData\Local\Temp\d5qtkvvs.bmp */
1397 filename_looks_tempish (const char *filename)
1402 gboolean retval = FALSE;
1404 dirname = g_path_get_dirname (filename);
1407 q = g_get_tmp_dir ();
1410 ((G_IS_DIR_SEPARATOR (*p) && G_IS_DIR_SEPARATOR (*q)) ||
1411 g_ascii_tolower (*p) == g_ascii_tolower (*q)))
1423 close_it (gpointer data)
1425 close (GPOINTER_TO_INT (data));
1432 static GdkFilterReturn
1433 gdk_dropfiles_filter (GdkXEvent *xev,
1437 GdkDragContext *context;
1439 MSG *msg = (MSG *) xev;
1443 gchar *fileName, *linkedFile;
1445 GdkDeviceManager *device_manager;
1447 if (msg->message == WM_DROPFILES)
1449 GDK_NOTE (DND, g_print ("WM_DROPFILES: %p\n", msg->hwnd));
1451 context = gdk_drag_context_new ();
1452 context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
1453 context->is_source = FALSE;
1455 device_manager = gdk_display_get_device_manager (_gdk_display);
1456 device = gdk_device_manager_get_client_pointer (device_manager);
1457 gdk_drag_context_set_device (context, device);
1459 context->source_window = _gdk_root;
1460 g_object_ref (context->source_window);
1462 context->dest_window = event->any.window;
1463 g_object_ref (context->dest_window);
1465 /* WM_DROPFILES drops are always file names */
1467 g_list_append (NULL, _text_uri_list);
1468 context->actions = GDK_ACTION_COPY;
1469 context->suggested_action = GDK_ACTION_COPY;
1470 current_dest_drag = context;
1472 event->dnd.type = GDK_DROP_START;
1473 event->dnd.context = current_dest_drag;
1474 gdk_event_set_device (event, gdk_drag_context_get_device (current_dest_drag));
1476 hdrop = (HANDLE) msg->wParam;
1477 DragQueryPoint (hdrop, &pt);
1478 ClientToScreen (msg->hwnd, &pt);
1480 event->dnd.x_root = pt.x + _gdk_offset_x;
1481 event->dnd.y_root = pt.y + _gdk_offset_y;
1482 event->dnd.time = _gdk_win32_get_next_tick (msg->time);
1484 nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
1486 result = g_string_new (NULL);
1487 for (i = 0; i < nfiles; i++)
1490 wchar_t wfn[MAX_PATH];
1492 DragQueryFileW (hdrop, i, wfn, MAX_PATH);
1493 fileName = g_utf16_to_utf8 (wfn, -1, NULL, NULL, NULL);
1495 /* Resolve shortcuts */
1496 if (resolve_link (msg->hwnd, wfn, &linkedFile))
1498 uri = g_filename_to_uri (linkedFile, NULL, NULL);
1501 g_string_append (result, uri);
1502 GDK_NOTE (DND, g_print ("... %s link to %s: %s\n",
1503 fileName, linkedFile, uri));
1507 fileName = linkedFile;
1511 uri = g_filename_to_uri (fileName, NULL, NULL);
1514 g_string_append (result, uri);
1515 GDK_NOTE (DND, g_print ("... %s: %s\n", fileName, uri));
1521 /* Awful hack to recognize temp files corresponding to
1522 * images dragged from Firefox... Open the file right here
1523 * so that it is less likely that Firefox manages to delete
1524 * it before the GTK+-using app (typically GIMP) has opened
1527 * Not compiled in for now, because it means images dragged
1528 * from Firefox would stay around in the temp folder which
1529 * is not what Firefox intended. I don't feel comfortable
1530 * with that, both from a geenral sanity point of view, and
1531 * from a privacy point of view. It's better to wait for
1532 * Firefox to fix the problem, for instance by deleting the
1533 * temp file after a longer delay, or to wait until we
1534 * implement the OLE2_DND...
1536 if (filename_looks_tempish (fileName))
1538 int fd = g_open (fileName, _O_RDONLY|_O_BINARY, 0);
1541 GDK_NOTE (DND, g_print ("Could not open %s, maybe an image dragged from Firefox that it already deleted\n", fileName));
1545 GDK_NOTE (DND, g_print ("Opened %s as %d so that Firefox won't delete it\n", fileName, fd));
1546 g_timeout_add_seconds (1, close_it, GINT_TO_POINTER (fd));
1552 g_string_append (result, "\015\012");
1554 _gdk_dropfiles_store (result->str);
1555 g_string_free (result, FALSE);
1559 return GDK_FILTER_TRANSLATE;
1562 return GDK_FILTER_CONTINUE;
1566 add_format (GArray *fmts,
1573 fmt.dwAspect = DVASPECT_CONTENT;
1575 fmt.tymed = TYMED_HGLOBAL;
1577 g_array_append_val (fmts, fmt);
1582 _gdk_dnd_init (void)
1584 if (getenv ("GDK_WIN32_USE_EXPERIMENTAL_OLE2_DND"))
1585 use_ole2_dnd = TRUE;
1592 hr = OleInitialize (NULL);
1594 if (! SUCCEEDED (hr))
1595 g_error ("OleInitialize failed");
1597 fmts = g_array_new (FALSE, FALSE, sizeof (FORMATETC));
1599 /* The most important presumably */
1600 add_format (fmts, CF_UNICODETEXT);
1602 /* Used for GTK+ internal DND, I think was the intent? Anyway, code below assumes
1603 * this is at index 1.
1605 add_format (fmts, CF_GDIOBJFIRST);
1607 add_format (fmts, CF_HDROP);
1609 add_format (fmts, _cf_png);
1610 add_format (fmts, CF_DIB);
1612 add_format (fmts, _cf_url);
1613 add_format (fmts, _cf_html_format);
1614 add_format (fmts, _cf_text_html);
1616 nformats = fmts->len;
1617 formats = (FORMATETC*) g_array_free (fmts, FALSE);
1619 target_ctx_for_window = g_hash_table_new (g_direct_hash, g_direct_equal);
1624 _gdk_win32_dnd_exit (void)
1635 local_send_leave (GdkDragContext *context,
1638 GdkEvent *tmp_event;
1640 GDK_NOTE (DND, g_print ("local_send_leave: context=%p current_dest_drag=%p\n",
1642 current_dest_drag));
1644 if ((current_dest_drag != NULL) &&
1645 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1646 (current_dest_drag->source_window == context->source_window))
1648 tmp_event = gdk_event_new (GDK_DRAG_LEAVE);
1650 tmp_event->dnd.window = g_object_ref (context->dest_window);
1651 /* Pass ownership of context to the event */
1652 tmp_event->dnd.send_event = FALSE;
1653 tmp_event->dnd.context = g_object_ref (current_dest_drag);
1654 tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1655 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
1657 current_dest_drag = NULL;
1659 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1660 gdk_event_put (tmp_event);
1661 gdk_event_free (tmp_event);
1666 local_send_enter (GdkDragContext *context,
1669 GdkEvent *tmp_event;
1670 GdkDragContextPrivateWin32 *private;
1671 GdkDragContext *new_context;
1673 GdkDeviceManager *device_manager;
1675 GDK_NOTE (DND, g_print ("local_send_enter: context=%p current_dest_drag=%p\n",
1677 current_dest_drag));
1679 private = PRIVATE_DATA (context);
1681 if (current_dest_drag != NULL)
1683 g_object_unref (G_OBJECT (current_dest_drag));
1684 current_dest_drag = NULL;
1687 new_context = gdk_drag_context_new ();
1688 new_context->protocol = GDK_DRAG_PROTO_LOCAL;
1689 new_context->is_source = FALSE;
1691 device_manager = gdk_display_get_device_manager (_gdk_display);
1692 device = gdk_device_manager_get_client_pointer (device_manager);
1693 gdk_drag_context_set_device (new_context, device);
1695 new_context->source_window = context->source_window;
1696 g_object_ref (new_context->source_window);
1698 new_context->dest_window = context->dest_window;
1699 g_object_ref (new_context->dest_window);
1701 new_context->targets = g_list_copy (context->targets);
1703 gdk_window_set_events (new_context->source_window,
1704 gdk_window_get_events (new_context->source_window) |
1705 GDK_PROPERTY_CHANGE_MASK);
1706 new_context->actions = context->actions;
1708 tmp_event = gdk_event_new (GDK_DRAG_ENTER);
1709 tmp_event->dnd.window = g_object_ref (context->dest_window);
1710 tmp_event->dnd.send_event = FALSE;
1711 tmp_event->dnd.context = g_object_ref (new_context);
1712 tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1713 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
1715 current_dest_drag = new_context;
1717 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1718 gdk_event_put (tmp_event);
1719 gdk_event_free (tmp_event);
1723 local_send_motion (GdkDragContext *context,
1726 GdkDragAction action,
1729 GdkEvent *tmp_event;
1731 GDK_NOTE (DND, g_print ("local_send_motion: context=%p (%d,%d) current_dest_drag=%p\n",
1732 context, x_root, y_root,
1733 current_dest_drag));
1735 if ((current_dest_drag != NULL) &&
1736 (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
1737 (current_dest_drag->source_window == context->source_window))
1739 tmp_event = gdk_event_new (GDK_DRAG_MOTION);
1740 tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
1741 tmp_event->dnd.send_event = FALSE;
1742 tmp_event->dnd.context = g_object_ref (current_dest_drag);
1743 tmp_event->dnd.time = time;
1744 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
1746 current_dest_drag->suggested_action = action;
1748 tmp_event->dnd.x_root = x_root;
1749 tmp_event->dnd.y_root = y_root;
1751 PRIVATE_DATA (current_dest_drag)->last_pt.x = x_root - _gdk_offset_x;
1752 PRIVATE_DATA (current_dest_drag)->last_pt.y = y_root - _gdk_offset_y;
1754 PRIVATE_DATA (context)->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1756 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1757 gdk_event_put (tmp_event);
1758 gdk_event_free (tmp_event);
1763 local_send_drop (GdkDragContext *context,
1766 GdkEvent *tmp_event;
1768 GDK_NOTE (DND, g_print ("local_send_drop: context=%p current_dest_drag=%p\n",
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 GdkDragContextPrivateWin32 *private;
1777 private = PRIVATE_DATA (current_dest_drag);
1779 /* Pass ownership of context to the event */
1780 tmp_event = gdk_event_new (GDK_DROP_START);
1781 tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
1782 tmp_event->dnd.send_event = FALSE;
1783 tmp_event->dnd.context = g_object_ref (current_dest_drag);
1784 tmp_event->dnd.time = GDK_CURRENT_TIME;
1785 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
1787 tmp_event->dnd.x_root = private->last_pt.x + _gdk_offset_x;
1788 tmp_event->dnd.y_root = private->last_pt.y + _gdk_offset_y;
1790 current_dest_drag = NULL;
1792 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
1793 gdk_event_put (tmp_event);
1794 gdk_event_free (tmp_event);
1800 gdk_drag_do_leave (GdkDragContext *context,
1803 if (context->dest_window)
1805 GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
1809 if (context->protocol == GDK_DRAG_PROTO_LOCAL)
1810 local_send_leave (context, time);
1813 g_object_unref (context->dest_window);
1814 context->dest_window = NULL;
1819 _gdk_win32_window_drag_begin (GdkWindow *window,
1825 GdkDragContext *new_context;
1827 GdkDeviceManager *device_manager;
1829 g_return_val_if_fail (window != NULL, NULL);
1831 new_context = gdk_drag_context_new ();
1833 device_manager = gdk_display_get_device_manager (_gdk_display);
1834 device = gdk_device_manager_get_client_pointer (device_manager);
1835 gdk_drag_context_set_device (new_context, device);
1837 new_context->is_source = TRUE;
1839 new_context->source_window = window;
1840 g_object_ref (window);
1841 gdk_drag_context_set_device (new_context, device);
1843 new_context->targets = g_list_copy (targets);
1844 new_context->actions = 0;
1850 source_drag_context *ctx;
1852 g_return_val_if_fail (window != NULL, NULL);
1854 GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
1856 ctx = source_context_new (window, targets);
1858 _dnd_source_state = GDK_WIN32_DND_PENDING;
1860 pending_src_context = ctx;
1861 g_object_ref (ctx->context);
1863 return ctx->context;
1868 _gdk_win32_dnd_do_dragdrop (void)
1872 GdkDragContext* drag_ctx;
1873 GdkDragContextPrivateWin32 *private;
1874 BYTE kbd_state[256];
1883 if (pending_src_context == NULL)
1886 drag_ctx = pending_src_context->context;
1887 private = PRIVATE_DATA (drag_ctx);
1889 dobj = data_object_new (drag_ctx);
1891 API_CALL (GetCursorPos, (&private->last_pt));
1892 API_CALL (ScreenToClient, (GDK_WINDOW_HWND (drag_ctx->source_window), &private->last_pt));
1893 private->last_key_state = 0;
1894 API_CALL (GetKeyboardState, (kbd_state));
1896 if (kbd_state[VK_CONTROL])
1897 private->last_key_state |= MK_CONTROL;
1898 if (kbd_state[VK_SHIFT])
1899 private->last_key_state |= MK_SHIFT;
1900 if (kbd_state[VK_LBUTTON])
1901 private->last_key_state |= MK_LBUTTON;
1902 if (kbd_state[VK_MBUTTON])
1903 private->last_key_state |= MK_MBUTTON;
1904 if (kbd_state[VK_RBUTTON])
1905 private->last_key_state |= MK_RBUTTON;
1908 global = GlobalAlloc (GMEM_FIXED, sizeof (ctx));
1910 memcpy (&global, ctx, sizeof (ctx));
1912 medium.tymed = TYMED_HGLOBAL;
1913 medium.hGlobal = global;
1914 medium.pUnkForRelease = NULL;
1916 /* FIXME I wish I remember what I was thinking of here, i.e. what
1917 * the formats[1] signifies, i.e. the CF_GDIOBJFIRST FORMATETC?
1919 dobj->ido.lpVtbl->SetData (&dobj->ido, &formats[1], &medium, TRUE);
1922 /* Start dragging with mainloop inside the OLE2 API. Exits only when done */
1924 GDK_NOTE (DND, g_print ("Calling DoDragDrop\n"));
1926 _gdk_win32_begin_modal_call ();
1927 hr = DoDragDrop (&dobj->ido, &pending_src_context->ids,
1928 DROPEFFECT_COPY | DROPEFFECT_MOVE,
1930 _gdk_win32_end_modal_call ();
1932 GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n",
1933 (hr == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
1934 (hr == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
1935 (hr == E_UNEXPECTED ? "E_UNEXPECTED" :
1936 g_strdup_printf ("%#.8lx", hr))))));
1938 /* Delete dnd selection after successful move */
1939 if (hr == DRAGDROP_S_DROP && dwEffect == DROPEFFECT_MOVE)
1943 tmp_event.type = GDK_SELECTION_REQUEST;
1944 tmp_event.selection.window = drag_ctx->source_window;
1945 tmp_event.selection.send_event = FALSE;
1946 tmp_event.selection.selection = _gdk_ole2_dnd;
1947 tmp_event.selection.target = _delete;
1948 tmp_event.selection.property = _gdk_ole2_dnd; /* ??? */
1949 tmp_event.selection.time = GDK_CURRENT_TIME; /* ??? */
1950 g_object_ref (tmp_event.selection.window);
1952 GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
1953 gdk_event_put (&tmp_event);
1957 // Send a GDK_DROP_FINISHED to the source window
1961 if ( pending_src_context != NULL && pending_src_context->context != NULL
1962 && pending_src_context->context->source_window != NULL )
1963 push_dnd_event (GDK_DROP_FINISHED, pending_src_context->context, ptl, FALSE);
1966 dobj->ido.lpVtbl->Release (&dobj->ido);
1967 if (pending_src_context != NULL)
1969 pending_src_context->ids.lpVtbl->Release (&pending_src_context->ids);
1970 pending_src_context = NULL;
1975 /* Untested, may not work ...
1976 * ... but as of this writing is only used by exlusive X11 gtksocket.c
1979 _gdk_win32_window_get_drag_protocol (GdkWindow *window,
1982 GdkDragProtocol protocol = GDK_DRAG_PROTO_NONE;
1984 if (gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
1986 if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
1989 protocol = GDK_DRAG_PROTO_OLE2;
1991 protocol = GDK_DRAG_PROTO_LOCAL;
2004 gdk_win32_drag_context_find_window (GdkDragContext *context,
2005 GdkWindow *drag_window,
2009 GdkDragProtocol *protocol)
2011 GdkWindow *dest_window;
2015 pt.x = x_root - _gdk_offset_x;
2016 pt.y = y_root - _gdk_offset_y;
2018 hwnd = WindowFromPoint (pt);
2024 dest_window = gdk_win32_handle_table_lookup (hwnd);
2026 g_object_ref (dest_window);
2028 dest_window = gdk_win32_window_foreign_new_for_display (_gdk_display, hwnd);
2031 *protocol = GDK_DRAG_PROTO_OLE2;
2032 else if (context->source_window)
2033 *protocol = GDK_DRAG_PROTO_LOCAL;
2035 *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
2039 g_print ("gdk_drag_find_window: %p %+d%+d: %p: %p %s\n",
2040 (drag_window ? GDK_WINDOW_HWND (drag_window) : NULL),
2043 (dest_window ? GDK_WINDOW_HWND (dest_window) : NULL),
2044 _gdk_win32_drag_protocol_to_string (*protocol)));
2050 gdk_win32_drag_context_drag_motion (GdkDragContext *context,
2051 GdkWindow *dest_window,
2052 GdkDragProtocol protocol,
2055 GdkDragAction suggested_action,
2056 GdkDragAction possible_actions,
2059 GdkDragContextPrivateWin32 *private;
2061 g_return_val_if_fail (context != NULL, FALSE);
2063 context->actions = possible_actions;
2065 GDK_NOTE (DND, g_print ("gdk_drag_motion: %s suggested=%s, possible=%s\n"
2066 " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2067 _gdk_win32_drag_protocol_to_string (protocol),
2068 _gdk_win32_drag_action_to_string (suggested_action),
2069 _gdk_win32_drag_action_to_string (possible_actions),
2071 _gdk_win32_drag_action_to_string (context->actions),
2072 _gdk_win32_drag_action_to_string (context->suggested_action),
2073 _gdk_win32_drag_action_to_string (context->action)));
2075 private = PRIVATE_DATA (context);
2079 if (context->dest_window == dest_window)
2081 GdkDragContext *dest_context;
2083 dest_context = gdk_drag_context_find (FALSE,
2084 context->source_window,
2088 dest_context->actions = context->actions;
2090 context->suggested_action = suggested_action;
2094 GdkEvent *tmp_event;
2096 /* Send a leave to the last destination */
2097 gdk_drag_do_leave (context, time);
2098 private->drag_status = GDK_DRAG_STATUS_DRAG;
2100 /* Check if new destination accepts drags, and which protocol */
2103 context->dest_window = dest_window;
2104 g_object_ref (context->dest_window);
2105 context->protocol = protocol;
2109 case GDK_DRAG_PROTO_LOCAL:
2110 local_send_enter (context, time);
2116 context->suggested_action = suggested_action;
2120 context->dest_window = NULL;
2121 context->action = 0;
2124 /* Push a status event, to let the client know that
2127 tmp_event = gdk_event_new (GDK_DRAG_STATUS);
2128 tmp_event->dnd.window = g_object_ref (context->source_window);
2129 /* We use this to signal a synthetic status. Perhaps
2130 * we should use an extra field...
2132 tmp_event->dnd.send_event = TRUE;
2134 tmp_event->dnd.context = g_object_ref (context);
2135 tmp_event->dnd.time = time;
2136 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
2138 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2139 gdk_event_put (tmp_event);
2140 gdk_event_free (tmp_event);
2143 /* Send a drag-motion event */
2145 private->last_pt.x = x_root - _gdk_offset_x;
2146 private->last_pt.y = y_root - _gdk_offset_y;
2148 if (context->dest_window)
2150 if (private->drag_status == GDK_DRAG_STATUS_DRAG)
2152 switch (context->protocol)
2154 case GDK_DRAG_PROTO_LOCAL:
2155 local_send_motion (context, x_root, y_root, suggested_action, time);
2158 case GDK_DRAG_PROTO_NONE:
2159 g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
2168 GDK_NOTE (DND, g_print (" returning TRUE\n"
2169 " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2171 _gdk_win32_drag_action_to_string (context->actions),
2172 _gdk_win32_drag_action_to_string (context->suggested_action),
2173 _gdk_win32_drag_action_to_string (context->action)));
2179 GDK_NOTE (DND, g_print (" returning FALSE\n"
2180 " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2182 _gdk_win32_drag_action_to_string (context->actions),
2183 _gdk_win32_drag_action_to_string (context->suggested_action),
2184 _gdk_win32_drag_action_to_string (context->action)));
2189 gdk_win32_drag_context_drag_drop (GdkDragContext *context,
2192 g_return_if_fail (context != NULL);
2194 GDK_NOTE (DND, g_print ("gdk_drag_drop\n"));
2198 if (context->dest_window &&
2199 context->protocol == GDK_DRAG_PROTO_LOCAL)
2200 local_send_drop (context, time);
2204 _dnd_source_state = GDK_WIN32_DND_DROPPED;
2209 gdk_win32_drag_context_drag_abort (GdkDragContext *context,
2212 g_return_if_fail (context != NULL);
2214 GDK_NOTE (DND, g_print ("gdk_drag_abort\n"));
2217 _dnd_source_state = GDK_WIN32_DND_NONE;
2220 /* Destination side */
2223 gdk_win32_drag_context_drag_status (GdkDragContext *context,
2224 GdkDragAction action,
2227 GdkDragContextPrivateWin32 *private;
2228 GdkDragContext *src_context;
2229 GdkEvent *tmp_event;
2231 g_return_if_fail (context != NULL);
2233 private = PRIVATE_DATA (context);
2235 GDK_NOTE (DND, g_print ("gdk_drag_status: %s\n"
2236 " context=%p:{actions=%s,suggested=%s,action=%s}\n",
2237 _gdk_win32_drag_action_to_string (action),
2239 _gdk_win32_drag_action_to_string (context->actions),
2240 _gdk_win32_drag_action_to_string (context->suggested_action),
2241 _gdk_win32_drag_action_to_string (context->action)));
2243 context->action = action;
2247 src_context = gdk_drag_context_find (TRUE,
2248 context->source_window,
2249 context->dest_window);
2253 GdkDragContextPrivateWin32 *private = PRIVATE_DATA (src_context);
2255 if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
2256 private->drag_status = GDK_DRAG_STATUS_DRAG;
2258 tmp_event = gdk_event_new (GDK_DRAG_STATUS);
2259 tmp_event->dnd.window = g_object_ref (context->source_window);
2260 tmp_event->dnd.send_event = FALSE;
2261 tmp_event->dnd.context = g_object_ref (src_context);
2262 tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2263 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
2265 if (action == GDK_ACTION_DEFAULT)
2268 src_context->action = action;
2270 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2271 gdk_event_put (tmp_event);
2272 gdk_event_free (tmp_event);
2278 gdk_win32_drag_context_drop_reply (GdkDragContext *context,
2282 g_return_if_fail (context != NULL);
2284 GDK_NOTE (DND, g_print ("gdk_drop_reply\n"));
2287 if (context->dest_window)
2289 if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
2290 _gdk_dropfiles_store (NULL);
2295 gdk_win32_drag_context_drop_finish (GdkDragContext *context,
2299 GdkDragContextPrivateWin32 *private;
2300 GdkDragContext *src_context;
2301 GdkEvent *tmp_event;
2303 g_return_if_fail (context != NULL);
2305 GDK_NOTE (DND, g_print ("gdk_drop_finish\n"));
2307 private = PRIVATE_DATA (context);
2311 src_context = gdk_drag_context_find (TRUE,
2312 context->source_window,
2313 context->dest_window);
2316 tmp_event = gdk_event_new (GDK_DROP_FINISHED);
2317 tmp_event->dnd.window = g_object_ref (src_context->source_window);
2318 tmp_event->dnd.send_event = FALSE;
2319 tmp_event->dnd.context = g_object_ref (src_context);
2320 gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
2322 GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
2323 gdk_event_put (tmp_event);
2324 gdk_event_free (tmp_event);
2329 gdk_drag_do_leave (context, time);
2332 _dnd_target_state = GDK_WIN32_DND_DROPPED;
2334 _dnd_target_state = GDK_WIN32_DND_FAILED;
2340 static GdkFilterReturn
2341 gdk_destroy_filter (GdkXEvent *xev,
2345 MSG *msg = (MSG *) xev;
2347 if (msg->message == WM_DESTROY)
2349 IDropTarget *idtp = (IDropTarget *) data;
2351 GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %p\n", msg->hwnd));
2353 idtp->lpVtbl->Release (idtp);
2355 RevokeDragDrop (msg->hwnd);
2356 CoLockObjectExternal ((IUnknown*) idtp, FALSE, TRUE);
2358 return GDK_FILTER_CONTINUE;
2364 _gdk_win32_window_register_dnd (GdkWindow *window)
2366 target_drag_context *ctx;
2369 g_return_if_fail (window != NULL);
2371 if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
2374 g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
2376 GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %p\n", GDK_WINDOW_HWND (window)));
2380 /* We always claim to accept dropped files, but in fact we might not,
2381 * of course. This function is called in such a way that it cannot know
2382 * whether the window (widget) in question actually accepts files
2383 * (in gtk, data of type text/uri-list) or not.
2385 gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
2386 DragAcceptFiles (GDK_WINDOW_HWND (window), TRUE);
2390 /* Return if window is already setup for DND. */
2391 if (g_hash_table_lookup (target_ctx_for_window, GDK_WINDOW_HWND (window)) != NULL)
2394 /* Register for OLE2 d&d : similarly, claim to accept all supported
2395 * data types because we cannot know from here what the window
2398 /* FIXME: This of course won't work with user-extensible data types! */
2399 ctx = target_context_new (window);
2401 hr = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
2402 if (!SUCCEEDED (hr))
2403 OTHER_API_FAILED ("CoLockObjectExternal");
2406 hr = RegisterDragDrop (GDK_WINDOW_HWND (window), &ctx->idt);
2407 if (hr == DRAGDROP_E_ALREADYREGISTERED)
2409 g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
2410 CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSE, FALSE);
2412 else if (!SUCCEEDED (hr))
2413 OTHER_API_FAILED ("RegisterDragDrop");
2416 g_object_ref (window);
2417 g_hash_table_insert (target_ctx_for_window, GDK_WINDOW_HWND (window), ctx);
2424 gdk_win32_drag_context_drop_status (GdkDragContext *context)
2426 return ! PRIVATE_DATA (context)->drop_failed;
2430 gdk_win32_drag_context_get_selection (GdkDragContext *context)
2432 switch (context->protocol)
2434 case GDK_DRAG_PROTO_LOCAL:
2436 case GDK_DRAG_PROTO_WIN32_DROPFILES:
2437 return _gdk_win32_dropfiles;
2438 case GDK_DRAG_PROTO_OLE2:
2439 return _gdk_ole2_dnd;
2446 gdk_win32_drag_context_class_init (GdkWin32DragContextClass *klass)
2448 GObjectClass *object_class = G_OBJECT_CLASS (klass);
2449 GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
2451 gdk_win32_drag_context_parent_class = g_type_class_peek_parent (klass);
2453 object_class->finalize = gdk_win32_drag_context_finalize;
2455 context_class->find_window = gdk_win32_drag_context_find_window;
2456 context_class->drag_status = gdk_win32_drag_context_drag_status;
2457 context_class->drag_motion = gdk_win32_drag_context_drag_motion;
2458 context_class->drag_abort = gdk_win32_drag_context_drag_abort;
2459 context_class->drag_drop = gdk_win32_drag_context_drag_drop;
2460 context_class->drop_reply = gdk_win32_drag_context_drop_reply;
2461 context_class->drop_finish = gdk_win32_drag_context_drop_finish;
2462 context_class->drop_status = gdk_win32_drag_context_drop_status;
2463 context_class->get_selection = gdk_win32_drag_context_get_selection;
2465 g_type_class_add_private (object_class, sizeof (GdkDragContextPrivateWin32));