1 /* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3 * Copyright (C) 1998-1999 Tor Lillqvist
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
23 * file for a list of people on the GTK+ Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
32 /* #define OLE2_DND */
37 #include "gdkproperty.h"
38 #include "gdkprivate.h"
53 static IID IID_IUnknown = {
54 0x00000000, 0x0000, 0x0000, { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } };
55 static IID IID_IDropSource = {
56 0x00000121, 0x0000, 0x0000, { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } };
57 static IID IID_IDropTarget = {
58 0x00000122, 0x0000, 0x0000, { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } };
63 typedef struct _GdkDragContextPrivate GdkDragContextPrivate;
67 GDK_DRAG_STATUS_MOTION_WAIT,
68 GDK_DRAG_STATUS_ACTION_WAIT,
79 #define PRINT_GUID(guid) \
80 g_print ("guid = %.08x-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
81 ((gulong *) guid)[0], \
82 ((gushort *) guid)[2], \
83 ((gushort *) guid)[3], \
84 ((guchar *) guid)[8], \
85 ((guchar *) guid)[9], \
86 ((guchar *) guid)[10], \
87 ((guchar *) guid)[11], \
88 ((guchar *) guid)[12], \
89 ((guchar *) guid)[13], \
90 ((guchar *) guid)[14], \
91 ((guchar *) guid)[15]);
96 /* Structure that holds information about a drag in progress.
97 * this is used on both source and destination sides.
99 struct _GdkDragContextPrivate {
100 GdkDragContext context;
104 guint16 last_x; /* Coordinates from last event */
107 guint drag_status; /* Current status of drag */
110 GdkDragContext *current_dest_drag = NULL;
114 static GList *contexts;
117 gdk_drag_context_new (void)
119 GdkDragContextPrivate *result;
121 result = g_new0 (GdkDragContextPrivate, 1);
123 result->ref_count = 1;
125 contexts = g_list_prepend (contexts, result);
127 return (GdkDragContext *)result;
131 gdk_drag_context_ref (GdkDragContext *context)
133 g_return_if_fail (context != NULL);
135 ((GdkDragContextPrivate *)context)->ref_count++;
139 gdk_drag_context_unref (GdkDragContext *context)
141 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
143 g_return_if_fail (context != NULL);
145 private->ref_count--;
147 GDK_NOTE (DND, g_print ("gdk_drag_context_unref: %d%s\n",
149 (private->ref_count == 0 ? " freeing" : "")));
151 if (private->ref_count == 0)
153 g_dataset_destroy (private);
155 g_list_free (context->targets);
157 if (context->source_window)
158 gdk_window_unref (context->source_window);
160 if (context->dest_window)
161 gdk_window_unref (context->dest_window);
163 contexts = g_list_remove (contexts, private);
170 static GdkDragContext *
171 gdk_drag_context_find (gboolean is_source,
175 GList *tmp_list = contexts;
176 GdkDragContext *context;
180 context = (GdkDragContext *)tmp_list->data;
182 if ((!context->is_source == !is_source) &&
183 ((source_xid == None) || (context->source_window &&
184 (GDK_WINDOW_XWINDOW (context->source_window) == source_xid))) &&
185 ((dest_xid == None) || (context->dest_window &&
186 (GDK_WINDOW_XWINDOW (context->dest_window) == dest_xid))))
189 tmp_list = tmp_list->next;
201 GdkDragContext *context;
202 } target_drag_context;
208 GdkDragContext *context;
209 } source_drag_context;
213 static ULONG STDMETHODCALLTYPE
214 m_add_ref_target (IDropTarget __RPC_FAR *This)
216 target_drag_context *ctx = (target_drag_context *) This;
217 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
219 GDK_NOTE (DND, g_print ("m_add_ref_target\n"));
220 gdk_drag_context_ref (ctx->context);
222 return private->ref_count;
225 static HRESULT STDMETHODCALLTYPE
226 m_query_interface_target (IDropTarget __RPC_FAR *This,
228 void __RPC_FAR *__RPC_FAR *ppvObject)
230 GDK_NOTE (DND, g_print ("m_query_interface_target\n"));
236 if (IsEqualGUID (riid, &IID_IUnknown))
238 g_print ("...IUnknown\n");
239 m_add_ref_target (This);
243 else if (IsEqualGUID (riid, &IID_IDropTarget))
245 g_print ("...IDropTarget\n");
246 m_add_ref_target (This);
252 g_print ("...Huh?\n");
253 return E_NOINTERFACE;
257 static ULONG STDMETHODCALLTYPE
258 m_release_target (IDropTarget __RPC_FAR *This)
260 target_drag_context *ctx = (target_drag_context *) This;
261 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
263 GDK_NOTE (DND, g_print ("m_release_target\n"));
264 gdk_drag_context_unref (ctx->context);
266 if (private->ref_count == 1)
268 gdk_drag_context_unref (ctx->context);
272 return private->ref_count - 1;
275 static HRESULT STDMETHODCALLTYPE
276 m_drag_enter (IDropTarget __RPC_FAR *This,
277 IDataObject __RPC_FAR *pDataObj,
280 DWORD __RPC_FAR *pdwEffect)
282 GDK_NOTE (DND, g_print ("m_drag_enter\n"));
286 static HRESULT STDMETHODCALLTYPE
287 m_drag_over (IDropTarget __RPC_FAR *This,
290 DWORD __RPC_FAR *pdwEffect)
292 GDK_NOTE (DND, g_print ("m_drag_over\n"));
296 static HRESULT STDMETHODCALLTYPE
297 m_drag_leave (IDropTarget __RPC_FAR *This)
299 GDK_NOTE (DND, g_print ("m_drag_leave\n"));
303 static HRESULT STDMETHODCALLTYPE
304 m_drop (IDropTarget __RPC_FAR *This,
305 IDataObject __RPC_FAR *pDataObj,
308 DWORD __RPC_FAR *pdwEffect)
310 GDK_NOTE (DND, g_print ("m_drop\n"));
314 static ULONG STDMETHODCALLTYPE
315 m_add_ref_source (IDropSource __RPC_FAR *This)
317 source_drag_context *ctx = (source_drag_context *) This;
318 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
320 GDK_NOTE (DND, g_print ("m_add_ref_source\n"));
321 gdk_drag_context_ref (ctx->context);
323 return private->ref_count;
326 static HRESULT STDMETHODCALLTYPE
327 m_query_interface_source (IDropSource __RPC_FAR *This,
329 void __RPC_FAR *__RPC_FAR *ppvObject)
331 GDK_NOTE (DND, g_print ("m_query_interface_source\n"));
336 if (IsEqualGUID (riid, &IID_IUnknown))
338 g_print ("...IUnknown\n");
339 m_add_ref_source (This);
343 else if (IsEqualGUID (riid, &IID_IDropSource))
345 g_print ("...IDropSource\n");
346 m_add_ref_source (This);
352 g_print ("...Huh?\n");
353 return E_NOINTERFACE;
357 static ULONG STDMETHODCALLTYPE
358 m_release_source (IDropSource __RPC_FAR *This)
360 source_drag_context *ctx = (source_drag_context *) This;
361 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
363 GDK_NOTE (DND, g_print ("m_release_source\n"));
364 gdk_drag_context_unref (ctx->context);
366 if (private->ref_count == 1)
368 gdk_drag_context_unref (ctx->context);
372 return private->ref_count - 1;
375 static HRESULT STDMETHODCALLTYPE
376 m_query_continue_drag (IDropSource __RPC_FAR *This,
380 GDK_NOTE (DND, g_print ("m_query_continue_drag\n"));
384 static HRESULT STDMETHODCALLTYPE
385 m_give_feedback (IDropSource __RPC_FAR *This,
388 GDK_NOTE (DND, g_print ("m_give_feedback\n"));
392 static HRESULT STDMETHODCALLTYPE
393 m_query_interface_object (IDataObject __RPC_FAR *This,
395 void __RPC_FAR *__RPC_FAR *ppvObject)
400 static ULONG STDMETHODCALLTYPE
401 m_add_ref_object (IDataObject __RPC_FAR *This)
406 static ULONG STDMETHODCALLTYPE
407 m_release_object (IDataObject __RPC_FAR *This)
412 static HRESULT STDMETHODCALLTYPE
413 m_get_data (IDataObject __RPC_FAR *This,
414 FORMATETC *pFormatEtc,
420 static HRESULT STDMETHODCALLTYPE
421 m_get_data_here (IDataObject __RPC_FAR *This,
422 FORMATETC *pFormatEtc,
428 static HRESULT STDMETHODCALLTYPE
429 m_query_get_data (IDataObject __RPC_FAR *This,
430 FORMATETC *pFormatEtc)
435 static HRESULT STDMETHODCALLTYPE
436 m_get_canonical_format_etc (IDataObject __RPC_FAR *This,
437 FORMATETC *pFormatEtcIn,
438 FORMATETC *pFormatEtcOut)
443 static HRESULT STDMETHODCALLTYPE
444 m_set_data (IDataObject __RPC_FAR *This,
445 FORMATETC *pFormatEtc,
452 static HRESULT STDMETHODCALLTYPE
453 m_enum_format_etc (IDataObject __RPC_FAR *This,
455 IEnumFORMATETC **ppEnumFormatEtc)
460 static HRESULT STDMETHODCALLTYPE
461 m_d_advise (IDataObject __RPC_FAR *This,
462 FORMATETC *pFormatetc,
464 IAdviseSink *pAdvSink,
465 DWORD *pdwConnection)
470 static HRESULT STDMETHODCALLTYPE
471 m_d_unadvise (IDataObject __RPC_FAR *This,
477 static HRESULT STDMETHODCALLTYPE
478 m_enum_d_advise (IDataObject __RPC_FAR *This,
479 IEnumSTATDATA **ppenumAdvise)
484 static IDropTargetVtbl idt_vtbl = {
485 m_query_interface_target,
494 static IDropSourceVtbl ids_vtbl = {
495 m_query_interface_source,
498 m_query_continue_drag,
502 static IDataObjectVtbl ido_vtbl = {
503 m_query_interface_object,
509 m_get_canonical_format_etc,
517 #endif /* OLE2_DND */
519 static target_drag_context *
520 target_context_new (void)
522 target_drag_context *result;
524 result = g_new0 (target_drag_context, 1);
527 result->idt.lpVtbl = &idt_vtbl;
530 result->context = gdk_drag_context_new ();
535 static source_drag_context *
536 source_context_new (void)
538 source_drag_context *result;
540 result = g_new0 (source_drag_context, 1);
543 result->ids.lpVtbl = &ids_vtbl;
546 result->context = gdk_drag_context_new ();
553 /* From MS Knowledge Base article Q130698 */
555 /* resolve_link() fills the filename and path buffer
556 * with relevant information
557 * hWnd - calling app's window handle.
559 * lpszLinkName - name of the link file passed into the function.
561 * lpszPath - the buffer that will receive the file pathname.
565 resolve_link(HWND hWnd,
566 LPCTSTR lpszLinkName,
568 LPSTR lpszDescription)
574 /* Assume Failure to start with: */
577 *lpszDescription = 0;
579 /* Call CoCreateInstance to obtain the IShellLink interface
580 * pointer. This call fails if CoInitialize is not called, so it is
581 * assumed that CoInitialize has been called.
584 hres = CoCreateInstance (&CLSID_ShellLink,
586 CLSCTX_INPROC_SERVER,
589 if (SUCCEEDED (hres))
593 /* The IShellLink interface supports the IPersistFile
594 * interface. Get an interface pointer to it.
596 hres = psl->lpVtbl->QueryInterface (psl,
599 if (SUCCEEDED (hres))
603 /* Convert the given link name string to wide character string. */
604 MultiByteToWideChar (CP_ACP, 0,
608 hres = ppf->lpVtbl->Load (ppf, wsz, STGM_READ);
609 if (SUCCEEDED (hres))
611 /* Resolve the link by calling the Resolve()
612 * interface function.
614 hres = psl->lpVtbl->Resolve(psl, hWnd,
617 if (SUCCEEDED (hres))
619 hres = psl->lpVtbl->GetPath (psl, lpszPath,
621 (WIN32_FIND_DATA*)&wfd,
624 if (SUCCEEDED (hres) && lpszDescription != NULL)
626 hres = psl->lpVtbl->GetDescription (psl,
630 if (!SUCCEEDED (hres))
635 ppf->lpVtbl->Release (ppf);
637 psl->lpVtbl->Release (psl);
639 return SUCCEEDED (hres);
644 #define resolve_link(hWnd, lpszLinkName, lpszPath, lpszDescription) FALSE
648 static GdkFilterReturn
649 gdk_dropfiles_filter (GdkXEvent *xev,
653 GdkDragContext *context;
654 GdkDragContextPrivate *private;
655 static GdkAtom text_uri_list_atom = GDK_NONE;
657 MSG *msg = (MSG *) xev;
661 guchar fileName[MAX_PATH], linkedFile[MAX_PATH];
663 if (text_uri_list_atom == GDK_NONE)
664 text_uri_list_atom = gdk_atom_intern ("text/uri-list", FALSE);
666 if (msg->message == WM_DROPFILES)
668 GDK_NOTE (DND, g_print ("WM_DROPFILES: %#x\n", msg->hwnd));
670 context = gdk_drag_context_new ();
671 private = (GdkDragContextPrivate *) context;
672 context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
673 context->is_source = FALSE;
674 context->source_window = gdk_parent_root;
675 context->dest_window = event->any.window;
676 gdk_window_ref (context->dest_window);
677 /* WM_DROPFILES drops are always file names */
679 g_list_append (NULL, GUINT_TO_POINTER (text_uri_list_atom));
680 current_dest_drag = context;
682 event->dnd.type = GDK_DROP_START;
683 event->dnd.context = current_dest_drag;
684 gdk_drag_context_ref (current_dest_drag);
686 hdrop = (HANDLE) msg->wParam;
687 DragQueryPoint (hdrop, &pt);
688 ClientToScreen (msg->hwnd, &pt);
690 event->dnd.x_root = pt.x;
691 event->dnd.y_root = pt.y;
692 event->dnd.time = msg->time;
694 nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
696 result = g_string_new (NULL);
697 for (i = 0; i < nfiles; i++)
699 g_string_append (result, "file:");
700 DragQueryFile (hdrop, i, fileName, MAX_PATH);
702 /* Resolve shortcuts */
703 if (resolve_link (msg->hwnd, fileName, linkedFile, NULL))
705 g_string_append (result, linkedFile);
706 GDK_NOTE (DND, g_print ("...%s link to %s\n",
707 fileName, linkedFile));
711 g_string_append (result, fileName);
712 GDK_NOTE (DND, g_print ("...%s\n", fileName));
714 g_string_append (result, "\015\012");
716 gdk_sel_prop_store (gdk_parent_root, text_uri_list_atom, 8,
717 result->str, result->len + 1);
721 return GDK_FILTER_TRANSLATE;
724 return GDK_FILTER_CONTINUE;
727 /*************************************************************
728 ************************** Public API ***********************
729 *************************************************************/
736 hres = OleInitialize (NULL);
738 if (! SUCCEEDED (hres))
739 g_error ("OleInitialize failed");
744 gdk_win32_dnd_exit (void)
754 gdk_drag_do_leave (GdkDragContext *context, guint32 time)
756 if (context->dest_window)
758 GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
759 gdk_window_unref (context->dest_window);
760 context->dest_window = NULL;
765 gdk_drag_begin (GdkWindow *window,
769 source_drag_context *ctx;
771 g_return_val_if_fail (window != NULL, NULL);
773 GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
775 ctx = source_context_new ();
776 ctx->context->is_source = TRUE;
777 ctx->context->source_window = window;
778 gdk_window_ref (window);
780 tmp_list = g_list_last (targets);
781 ctx->context->targets = NULL;
784 ctx->context->targets = g_list_prepend (ctx->context->targets,
786 tmp_list = tmp_list->prev;
789 ctx->context->actions = 0;
798 gdk_drag_get_protocol (guint32 xid,
799 GdkDragProtocol *protocol)
801 /* This isn't used */
806 gdk_drag_find_window (GdkDragContext *context,
807 GdkWindow *drag_window,
810 GdkWindow **dest_window,
811 GdkDragProtocol *protocol)
813 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
814 GdkDrawablePrivate *drag_window_private = (GdkDrawablePrivate*) drag_window;
818 GDK_NOTE (DND, g_print ("gdk_drag_find_window: %#x +%d+%d\n",
819 (drag_window ? GDK_DRAWABLE_XID (drag_window) : 0),
824 recipient = WindowFromPoint (pt);
825 if (recipient == NULL)
829 *dest_window = gdk_window_lookup (recipient);
831 gdk_window_ref (*dest_window);
832 *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
837 gdk_drag_motion (GdkDragContext *context,
838 GdkWindow *dest_window,
839 GdkDragProtocol protocol,
842 GdkDragAction suggested_action,
843 GdkDragAction possible_actions,
850 gdk_drag_drop (GdkDragContext *context,
853 g_return_if_fail (context != NULL);
855 g_warning ("gdk_drag_drop: not implemented\n");
859 gdk_drag_abort (GdkDragContext *context,
862 g_return_if_fail (context != NULL);
864 gdk_drag_do_leave (context, time);
867 /* Destination side */
870 gdk_drag_status (GdkDragContext *context,
871 GdkDragAction action,
874 GDK_NOTE (DND, g_print ("gdk_drag_status\n"));
878 gdk_drop_reply (GdkDragContext *context,
885 gdk_drop_finish (GdkDragContext *context,
891 static GdkFilterReturn
892 gdk_destroy_filter (GdkXEvent *xev,
897 MSG *msg = (MSG *) xev;
899 if (msg->message == WM_DESTROY)
901 IDropTarget *idtp = (IDropTarget *) data;
903 GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %#x\n", msg->hwnd));
904 RevokeDragDrop (msg->hwnd);
905 CoLockObjectExternal (idtp, FALSE, TRUE);
908 return GDK_FILTER_CONTINUE;
912 gdk_window_register_dnd (GdkWindow *window)
915 target_drag_context *context;
919 g_return_if_fail (window != NULL);
921 GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %#x\n",
922 GDK_DRAWABLE_XID (window)));
924 /* We always claim to accept dropped files, but in fact we might not,
925 * of course. This function is called in such a way that it cannot know
926 * whether the window (widget) in question actually accepts files
927 * (in gtk, data of type text/uri-list) or not.
929 gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
930 DragAcceptFiles (GDK_DRAWABLE_XID (window), TRUE);
933 /* Register for OLE2 d&d */
934 context = target_context_new ();
935 hres = CoLockObjectExternal ((IUnknown *) &context->idt, TRUE, FALSE);
936 if (!SUCCEEDED (hres))
937 OTHER_API_FAILED ("CoLockObjectExternal");
940 hres = RegisterDragDrop (GDK_DRAWABLE_XID (window), &context->idt);
941 if (hres == DRAGDROP_E_ALREADYREGISTERED)
943 g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
944 CoLockObjectExternal ((IUnknown *) &context->idt, FALSE, FALSE);
946 else if (!SUCCEEDED (hres))
947 OTHER_API_FAILED ("RegisterDragDrop");
950 gdk_window_add_filter (window, gdk_destroy_filter, &context->idt);
956 /*************************************************************
957 * gdk_drag_get_selection:
958 * Returns the selection atom for the current source window
962 *************************************************************/
965 gdk_drag_get_selection (GdkDragContext *context)
967 if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
968 return gdk_win32_dropfiles_atom;
969 else if (context->protocol == GDK_DRAG_PROTO_OLE2)
970 return gdk_ole2_dnd_atom;