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/.
35 #include "gdkproperty.h"
36 #include "gdkprivate.h"
43 #ifdef _MSC_VER /* These aren't in mingw32 */
50 typedef struct _GdkDragContextPrivate GdkDragContextPrivate;
54 GDK_DRAG_STATUS_MOTION_WAIT,
55 GDK_DRAG_STATUS_ACTION_WAIT,
66 #define PRINT_RIID(riid) \
67 g_print ("riid = %.08x-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
68 ((gulong *) riid)[0], \
69 ((gushort *) riid)[2], \
70 ((gushort *) riid)[3], \
71 ((guchar *) riid)[8], \
72 ((guchar *) riid)[9], \
73 ((guchar *) riid)[10], \
74 ((guchar *) riid)[11], \
75 ((guchar *) riid)[12], \
76 ((guchar *) riid)[13], \
77 ((guchar *) riid)[14], \
78 ((guchar *) riid)[15]);
81 HRESULT STDMETHODCALLTYPE
82 m_query_interface_target (IDropTarget __RPC_FAR *This,
83 /* [in] */ REFIID riid,
84 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
86 ULONG STDMETHODCALLTYPE
87 m_add_ref_target (IDropTarget __RPC_FAR *This);
89 ULONG STDMETHODCALLTYPE
90 m_release_target (IDropTarget __RPC_FAR *This);
92 HRESULT STDMETHODCALLTYPE
93 m_drag_enter (IDropTarget __RPC_FAR *This,
94 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
95 /* [in] */ DWORD grfKeyState,
97 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
99 HRESULT STDMETHODCALLTYPE
100 m_drag_over (IDropTarget __RPC_FAR *This,
101 /* [in] */ DWORD grfKeyState,
102 /* [in] */ POINTL pt,
103 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
105 HRESULT STDMETHODCALLTYPE
106 m_drag_leave (IDropTarget __RPC_FAR *This);
108 HRESULT STDMETHODCALLTYPE
109 m_drop (IDropTarget __RPC_FAR *This,
110 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
111 /* [in] */ DWORD grfKeyState,
112 /* [in] */ POINTL pt,
113 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
115 HRESULT STDMETHODCALLTYPE
116 m_query_interface_source (IDropSource __RPC_FAR *This,
117 /* [in] */ REFIID riid,
118 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
120 ULONG STDMETHODCALLTYPE
121 m_add_ref_source (IDropSource __RPC_FAR *This);
123 ULONG STDMETHODCALLTYPE
124 m_release_source (IDropSource __RPC_FAR *This);
126 HRESULT STDMETHODCALLTYPE
127 m_query_continue_drag (IDropSource __RPC_FAR *This,
128 /* [in] */ BOOL fEscapePressed,
129 /* [in] */ DWORD grfKeyState);
130 HRESULT STDMETHODCALLTYPE
131 m_give_feedback (IDropSource __RPC_FAR *This,
132 /* [in] */ DWORD dwEffect);
134 #endif /* OLE2_DND */
136 /* Structure that holds information about a drag in progress.
137 * this is used on both source and destination sides.
139 struct _GdkDragContextPrivate {
140 GdkDragContext context;
144 guint16 last_x; /* Coordinates from last event */
147 guint drag_status; /* Current status of drag */
150 GdkDragContext *current_dest_drag = NULL;
154 static GList *contexts;
157 gdk_drag_context_new (void)
159 GdkDragContextPrivate *result;
161 result = g_new0 (GdkDragContextPrivate, 1);
163 result->ref_count = 1;
165 contexts = g_list_prepend (contexts, result);
167 return (GdkDragContext *)result;
174 GdkDragContext *context;
175 } target_drag_context;
179 GdkDragContext *context;
180 } source_drag_context;
182 HRESULT STDMETHODCALLTYPE
183 m_query_interface_target (IDropTarget __RPC_FAR *This,
184 /* [in] */ REFIID riid,
185 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
187 GDK_NOTE (DND, g_print ("m_query_interface_target\n"));
193 if (IsEqualGUID (riid, &IID_IUnknown))
195 g_print ("...IUnknown\n");
196 m_add_ref_target (This);
200 else if (IsEqualGUID (riid, &IID_IDropTarget))
202 g_print ("...IDropTarget\n");
203 m_add_ref_target (This);
209 g_print ("...Huh?\n");
210 return E_NOINTERFACE;
214 ULONG STDMETHODCALLTYPE
215 m_add_ref_target (IDropTarget __RPC_FAR *This)
217 target_drag_context *ctx = (target_drag_context *) This;
218 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
220 GDK_NOTE (DND, g_print ("m_add_ref_target\n"));
221 gdk_drag_context_ref (ctx->context);
223 return private->ref_count;
226 ULONG STDMETHODCALLTYPE
227 m_release_target (IDropTarget __RPC_FAR *This)
229 target_drag_context *ctx = (target_drag_context *) This;
230 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
232 GDK_NOTE (DND, g_print ("m_release_target\n"));
233 gdk_drag_context_unref (ctx->context);
235 if (private->ref_count == 1)
237 gdk_drag_context_unref (ctx->context);
241 return private->ref_count - 1;
244 HRESULT STDMETHODCALLTYPE
245 m_drag_enter (IDropTarget __RPC_FAR *This,
246 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
247 /* [in] */ DWORD grfKeyState,
248 /* [in] */ POINTL pt,
249 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
251 GDK_NOTE (DND, g_print ("m_drag_enter\n"));
255 HRESULT STDMETHODCALLTYPE
256 m_drag_over (IDropTarget __RPC_FAR *This,
257 /* [in] */ DWORD grfKeyState,
258 /* [in] */ POINTL pt,
259 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
261 GDK_NOTE (DND, g_print ("m_drag_over\n"));
265 HRESULT STDMETHODCALLTYPE
266 m_drag_leave (IDropTarget __RPC_FAR *This)
268 GDK_NOTE (DND, g_print ("m_drag_leave\n"));
272 HRESULT STDMETHODCALLTYPE
273 m_drop (IDropTarget __RPC_FAR *This,
274 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
275 /* [in] */ DWORD grfKeyState,
276 /* [in] */ POINTL pt,
277 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
279 GDK_NOTE (DND, g_print ("m_drop\n"));
283 HRESULT STDMETHODCALLTYPE
284 m_query_interface_source (IDropSource __RPC_FAR *This,
285 /* [in] */ REFIID riid,
286 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
288 GDK_NOTE (DND, g_print ("m_query_interface_source\n"));
293 if (IsEqualGUID (riid, &IID_IUnknown))
295 g_print ("...IUnknown\n");
296 m_add_ref_source (This);
300 else if (IsEqualGUID (riid, &IID_IDropSource))
302 g_print ("...IDropSource\n");
303 m_add_ref_source (This);
309 g_print ("...Huh?\n");
310 return E_NOINTERFACE;
314 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 ULONG STDMETHODCALLTYPE
327 m_release_source (IDropSource __RPC_FAR *This)
329 source_drag_context *ctx = (source_drag_context *) This;
330 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
332 GDK_NOTE (DND, g_print ("m_release_source\n"));
333 gdk_drag_context_unref (ctx->context);
335 if (private->ref_count == 1)
337 gdk_drag_context_unref (ctx->context);
341 return private->ref_count - 1;
344 HRESULT STDMETHODCALLTYPE
345 m_query_continue_drag (IDropSource __RPC_FAR *This,
346 /* [in] */ BOOL fEscapePressed,
347 /* [in] */ DWORD grfKeyState)
349 GDK_NOTE (DND, g_print ("m_query_continue_drag\n"));
353 HRESULT STDMETHODCALLTYPE
354 m_give_feedback (IDropSource __RPC_FAR *This,
355 /* [in] */ DWORD dwEffect)
357 GDK_NOTE (DND, g_print ("m_give_feedback\n"));
361 static IDropTargetVtbl idt_vtbl = {
362 m_query_interface_target,
371 static IDropSourceVtbl ids_vtbl = {
372 m_query_interface_source,
375 m_query_continue_drag,
379 target_drag_context *
380 target_context_new (void)
382 target_drag_context *result;
384 result = g_new0 (target_drag_context, 1);
386 result->idt.lpVtbl = &idt_vtbl;
388 result->context = gdk_drag_context_new ();
393 source_drag_context *
394 source_context_new (void)
396 source_drag_context *result;
398 result = g_new0 (source_drag_context, 1);
400 result->ids.lpVtbl = &ids_vtbl;
402 result->context = gdk_drag_context_new ();
407 #endif /* OLE2_DND */
410 gdk_drag_context_ref (GdkDragContext *context)
412 g_return_if_fail (context != NULL);
414 ((GdkDragContextPrivate *)context)->ref_count++;
418 gdk_drag_context_unref (GdkDragContext *context)
420 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
422 g_return_if_fail (context != NULL);
424 private->ref_count--;
426 GDK_NOTE (DND, g_print ("gdk_drag_context_unref: %d%s\n",
428 (private->ref_count == 0 ? " freeing" : "")));
430 if (private->ref_count == 0)
432 g_dataset_destroy (private);
434 g_list_free (context->targets);
436 if (context->source_window)
437 gdk_window_unref (context->source_window);
439 if (context->dest_window)
440 gdk_window_unref (context->dest_window);
442 contexts = g_list_remove (contexts, private);
449 static GdkDragContext *
450 gdk_drag_context_find (gboolean is_source,
454 GList *tmp_list = contexts;
455 GdkDragContext *context;
459 context = (GdkDragContext *)tmp_list->data;
461 if ((!context->is_source == !is_source) &&
462 ((source_xid == None) || (context->source_window &&
463 (GDK_WINDOW_XWINDOW (context->source_window) == source_xid))) &&
464 ((dest_xid == None) || (context->dest_window &&
465 (GDK_WINDOW_XWINDOW (context->dest_window) == dest_xid))))
468 tmp_list = tmp_list->next;
478 /* From MS Knowledge Base article Q130698 */
480 /* resolve_link() fills the filename and path buffer
481 * with relevant information
482 * hWnd - calling app's window handle.
484 * lpszLinkName - name of the link file passed into the function.
486 * lpszPath - the buffer that will receive the file pathname.
490 resolve_link(HWND hWnd,
491 LPCTSTR lpszLinkName,
493 LPSTR lpszDescription)
499 /* Assume Failure to start with: */
502 *lpszDescription = 0;
504 /* Call CoCreateInstance to obtain the IShellLink interface
505 * pointer. This call fails if CoInitialize is not called, so it is
506 * assumed that CoInitialize has been called.
509 hres = CoCreateInstance (&CLSID_ShellLink,
511 CLSCTX_INPROC_SERVER,
514 if (SUCCEEDED (hres))
518 /* The IShellLink interface supports the IPersistFile
519 * interface. Get an interface pointer to it.
521 hres = psl->lpVtbl->QueryInterface (psl,
524 if (SUCCEEDED (hres))
528 /* Convert the given link name string to wide character string. */
529 MultiByteToWideChar (CP_ACP, 0,
533 hres = ppf->lpVtbl->Load (ppf, wsz, STGM_READ);
534 if (SUCCEEDED (hres))
536 /* Resolve the link by calling the Resolve()
537 * interface function.
539 hres = psl->lpVtbl->Resolve(psl, hWnd,
542 if (SUCCEEDED (hres))
544 hres = psl->lpVtbl->GetPath (psl, lpszPath,
546 (WIN32_FIND_DATA*)&wfd,
549 if (SUCCEEDED (hres) && lpszDescription != NULL)
551 hres = psl->lpVtbl->GetDescription (psl,
555 if (!SUCCEEDED (hres))
560 ppf->lpVtbl->Release (ppf);
562 psl->lpVtbl->Release (psl);
564 return SUCCEEDED (hres);
569 #define resolve_link(hWnd, lpszLinkName, lpszPath, lpszDescription) FALSE
573 static GdkFilterReturn
574 gdk_dropfiles_filter (GdkXEvent *xev,
578 GdkDragContext *context;
579 GdkDragContextPrivate *private;
580 static GdkAtom text_uri_list_atom = GDK_NONE;
582 MSG *msg = (MSG *) xev;
586 guchar fileName[MAX_PATH], linkedFile[MAX_PATH];
588 if (text_uri_list_atom == GDK_NONE)
589 text_uri_list_atom = gdk_atom_intern ("text/uri-list", FALSE);
591 if (msg->message == WM_DROPFILES)
593 GDK_NOTE (DND, g_print ("WM_DROPFILES: %#x\n", msg->hwnd));
595 context = gdk_drag_context_new ();
596 private = (GdkDragContextPrivate *) context;
597 context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
598 context->is_source = FALSE;
599 context->source_window = (GdkWindow *) &gdk_root_parent;
600 context->dest_window = event->any.window;
601 gdk_window_ref (context->dest_window);
602 /* WM_DROPFILES drops are always file names */
604 g_list_append (NULL, GUINT_TO_POINTER (text_uri_list_atom));
605 current_dest_drag = context;
607 event->dnd.type = GDK_DROP_START;
608 event->dnd.context = current_dest_drag;
609 gdk_drag_context_ref (current_dest_drag);
611 hdrop = (HANDLE) msg->wParam;
612 DragQueryPoint (hdrop, &pt);
613 ClientToScreen (msg->hwnd, &pt);
615 event->dnd.x_root = pt.x;
616 event->dnd.y_root = pt.y;
617 event->dnd.time = msg->time;
619 nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
621 result = g_string_new (NULL);
622 for (i = 0; i < nfiles; i++)
624 g_string_append (result, "file:");
625 DragQueryFile (hdrop, i, fileName, MAX_PATH);
627 /* Resolve shortcuts */
628 if (resolve_link (msg->hwnd, fileName, linkedFile, NULL))
630 g_string_append (result, linkedFile);
631 GDK_NOTE (DND, g_print ("...%s link to %s\n",
632 fileName, linkedFile));
636 g_string_append (result, fileName);
637 GDK_NOTE (DND, g_print ("...%s\n", fileName));
639 g_string_append (result, "\015\012");
641 gdk_sel_prop_store ((GdkWindow *) &gdk_root_parent,
642 text_uri_list_atom, 8, result->str, result->len + 1);
646 return GDK_FILTER_TRANSLATE;
649 return GDK_FILTER_CONTINUE;
652 /*************************************************************
653 ************************** Public API ***********************
654 *************************************************************/
661 hres = OleInitialize (NULL);
663 if (! SUCCEEDED (hres))
664 g_error ("OleInitialize failed");
679 gdk_drag_do_leave (GdkDragContext *context, guint32 time)
681 if (context->dest_window)
683 GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
684 gdk_window_unref (context->dest_window);
685 context->dest_window = NULL;
690 gdk_drag_begin (GdkWindow *window,
694 GdkDragContext *new_context;
696 g_return_val_if_fail (window != NULL, NULL);
698 GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
700 new_context = gdk_drag_context_new ();
701 new_context->is_source = TRUE;
702 new_context->source_window = window;
703 gdk_window_ref (window);
705 tmp_list = g_list_last (targets);
706 new_context->targets = NULL;
709 new_context->targets = g_list_prepend (new_context->targets,
711 tmp_list = tmp_list->prev;
714 new_context->actions = 0;
720 gdk_drag_get_protocol (guint32 xid,
721 GdkDragProtocol *protocol)
723 /* This isn't used */
728 gdk_drag_find_window (GdkDragContext *context,
729 GdkWindow *drag_window,
732 GdkWindow **dest_window,
733 GdkDragProtocol *protocol)
735 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
736 GdkDrawablePrivate *drag_window_private = (GdkDrawablePrivate*) drag_window;
740 GDK_NOTE (DND, g_print ("gdk_drag_find_window: %#x +%d+%d\n",
741 (drag_window ? drag_window_private->xwindow : 0),
746 recipient = WindowFromPoint (pt);
747 if (recipient == NULL)
751 *dest_window = gdk_window_lookup (recipient);
753 gdk_window_ref (*dest_window);
754 *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
759 gdk_drag_motion (GdkDragContext *context,
760 GdkWindow *dest_window,
761 GdkDragProtocol protocol,
764 GdkDragAction suggested_action,
765 GdkDragAction possible_actions,
772 gdk_drag_drop (GdkDragContext *context,
775 g_return_if_fail (context != NULL);
777 g_warning ("gdk_drag_drop: not implemented\n");
781 gdk_drag_abort (GdkDragContext *context,
784 g_return_if_fail (context != NULL);
786 gdk_drag_do_leave (context, time);
789 /* Destination side */
792 gdk_drag_status (GdkDragContext *context,
793 GdkDragAction action,
796 GDK_NOTE (DND, g_print ("gdk_drag_status\n"));
800 gdk_drop_reply (GdkDragContext *context,
807 gdk_drop_finish (GdkDragContext *context,
813 static GdkFilterReturn
814 gdk_destroy_filter (GdkXEvent *xev,
819 MSG *msg = (MSG *) xev;
821 if (msg->message == WM_DESTROY)
823 IDropTarget *idtp = (IDropTarget *) data;
825 GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %#x\n", msg->hwnd));
826 RevokeDragDrop (msg->hwnd);
827 CoLockObjectExternal (idtp, FALSE, TRUE);
830 return GDK_FILTER_CONTINUE;
834 gdk_window_register_dnd (GdkWindow *window)
836 GdkDrawablePrivate *private = (GdkDrawablePrivate *) window;
838 target_drag_context *context;
842 g_return_if_fail (window != NULL);
844 GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %#x\n", private->xwindow));
846 /* We always claim to accept dropped files, but in fact we might not,
847 * of course. This function is called in such a way that it cannot know
848 * whether the window (widget) in question actually accepts files
849 * (in gtk, data of type text/uri-list) or not.
851 gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
852 DragAcceptFiles (private->xwindow, TRUE);
855 /* Register for OLE2 d&d */
856 context = target_context_new ();
857 hres = CoLockObjectExternal ((IUnknown *) &context->idt, TRUE, FALSE);
858 if (!SUCCEEDED (hres))
859 g_warning ("gdk_window_register_dnd: CoLockObjectExternal failed");
862 hres = RegisterDragDrop (private->xwindow, &context->idt);
863 if (hres == DRAGDROP_E_ALREADYREGISTERED)
865 g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
866 CoLockObjectExternal ((IUnknown *) &context->idt, FALSE, FALSE);
868 else if (!SUCCEEDED (hres))
869 g_warning ("gdk_window_register_dnd: RegisterDragDrop failed");
872 gdk_window_add_filter (window, gdk_destroy_filter, &context->idt);
878 /*************************************************************
879 * gdk_drag_get_selection:
880 * Returns the selection atom for the current source window
884 *************************************************************/
887 gdk_drag_get_selection (GdkDragContext *context)
889 if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
890 return gdk_win32_dropfiles_atom;
891 else if (context->protocol == GDK_DRAG_PROTO_OLE2)
892 return gdk_ole2_dnd_atom;