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"
45 #ifdef _MSC_VER /* These aren't in mingw32 */
52 typedef struct _GdkDragContextPrivate GdkDragContextPrivate;
56 GDK_DRAG_STATUS_MOTION_WAIT,
57 GDK_DRAG_STATUS_ACTION_WAIT,
68 #define PRINT_RIID(riid) \
69 g_print ("riid = %.08x-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
70 ((gulong *) riid)[0], \
71 ((gushort *) riid)[2], \
72 ((gushort *) riid)[3], \
73 ((guchar *) riid)[8], \
74 ((guchar *) riid)[9], \
75 ((guchar *) riid)[10], \
76 ((guchar *) riid)[11], \
77 ((guchar *) riid)[12], \
78 ((guchar *) riid)[13], \
79 ((guchar *) riid)[14], \
80 ((guchar *) riid)[15]);
83 HRESULT STDMETHODCALLTYPE
84 m_query_interface_target (IDropTarget __RPC_FAR *This,
85 /* [in] */ REFIID riid,
86 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
88 ULONG STDMETHODCALLTYPE
89 m_add_ref_target (IDropTarget __RPC_FAR *This);
91 ULONG STDMETHODCALLTYPE
92 m_release_target (IDropTarget __RPC_FAR *This);
94 HRESULT STDMETHODCALLTYPE
95 m_drag_enter (IDropTarget __RPC_FAR *This,
96 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
97 /* [in] */ DWORD grfKeyState,
99 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
101 HRESULT STDMETHODCALLTYPE
102 m_drag_over (IDropTarget __RPC_FAR *This,
103 /* [in] */ DWORD grfKeyState,
104 /* [in] */ POINTL pt,
105 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
107 HRESULT STDMETHODCALLTYPE
108 m_drag_leave (IDropTarget __RPC_FAR *This);
110 HRESULT STDMETHODCALLTYPE
111 m_drop (IDropTarget __RPC_FAR *This,
112 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
113 /* [in] */ DWORD grfKeyState,
114 /* [in] */ POINTL pt,
115 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
117 HRESULT STDMETHODCALLTYPE
118 m_query_interface_source (IDropSource __RPC_FAR *This,
119 /* [in] */ REFIID riid,
120 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
122 ULONG STDMETHODCALLTYPE
123 m_add_ref_source (IDropSource __RPC_FAR *This);
125 ULONG STDMETHODCALLTYPE
126 m_release_source (IDropSource __RPC_FAR *This);
128 HRESULT STDMETHODCALLTYPE
129 m_query_continue_drag (IDropSource __RPC_FAR *This,
130 /* [in] */ BOOL fEscapePressed,
131 /* [in] */ DWORD grfKeyState);
132 HRESULT STDMETHODCALLTYPE
133 m_give_feedback (IDropSource __RPC_FAR *This,
134 /* [in] */ DWORD dwEffect);
136 #endif /* OLE2_DND */
138 /* Structure that holds information about a drag in progress.
139 * this is used on both source and destination sides.
141 struct _GdkDragContextPrivate {
142 GdkDragContext context;
146 guint16 last_x; /* Coordinates from last event */
149 guint drag_status; /* Current status of drag */
152 GdkDragContext *current_dest_drag = NULL;
156 static GList *contexts;
159 gdk_drag_context_new (void)
161 GdkDragContextPrivate *result;
163 result = g_new0 (GdkDragContextPrivate, 1);
165 result->ref_count = 1;
167 contexts = g_list_prepend (contexts, result);
169 return (GdkDragContext *)result;
176 GdkDragContext *context;
177 } target_drag_context;
181 GdkDragContext *context;
182 } source_drag_context;
184 HRESULT STDMETHODCALLTYPE
185 m_query_interface_target (IDropTarget __RPC_FAR *This,
186 /* [in] */ REFIID riid,
187 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
189 GDK_NOTE (DND, g_print ("m_query_interface_target\n"));
195 if (IsEqualGUID (riid, &IID_IUnknown))
197 g_print ("...IUnknown\n");
198 m_add_ref_target (This);
202 else if (IsEqualGUID (riid, &IID_IDropTarget))
204 g_print ("...IDropTarget\n");
205 m_add_ref_target (This);
211 g_print ("...Huh?\n");
212 return E_NOINTERFACE;
216 ULONG STDMETHODCALLTYPE
217 m_add_ref_target (IDropTarget __RPC_FAR *This)
219 target_drag_context *ctx = (target_drag_context *) This;
220 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
222 GDK_NOTE (DND, g_print ("m_add_ref_target\n"));
223 gdk_drag_context_ref (ctx->context);
225 return private->ref_count;
228 ULONG STDMETHODCALLTYPE
229 m_release_target (IDropTarget __RPC_FAR *This)
231 target_drag_context *ctx = (target_drag_context *) This;
232 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
234 GDK_NOTE (DND, g_print ("m_release_target\n"));
235 gdk_drag_context_unref (ctx->context);
237 if (private->ref_count == 1)
239 gdk_drag_context_unref (ctx->context);
243 return private->ref_count - 1;
246 HRESULT STDMETHODCALLTYPE
247 m_drag_enter (IDropTarget __RPC_FAR *This,
248 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
249 /* [in] */ DWORD grfKeyState,
250 /* [in] */ POINTL pt,
251 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
253 GDK_NOTE (DND, g_print ("m_drag_enter\n"));
257 HRESULT STDMETHODCALLTYPE
258 m_drag_over (IDropTarget __RPC_FAR *This,
259 /* [in] */ DWORD grfKeyState,
260 /* [in] */ POINTL pt,
261 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
263 GDK_NOTE (DND, g_print ("m_drag_over\n"));
267 HRESULT STDMETHODCALLTYPE
268 m_drag_leave (IDropTarget __RPC_FAR *This)
270 GDK_NOTE (DND, g_print ("m_drag_leave\n"));
274 HRESULT STDMETHODCALLTYPE
275 m_drop (IDropTarget __RPC_FAR *This,
276 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
277 /* [in] */ DWORD grfKeyState,
278 /* [in] */ POINTL pt,
279 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
281 GDK_NOTE (DND, g_print ("m_drop\n"));
285 HRESULT STDMETHODCALLTYPE
286 m_query_interface_source (IDropSource __RPC_FAR *This,
287 /* [in] */ REFIID riid,
288 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
290 GDK_NOTE (DND, g_print ("m_query_interface_source\n"));
295 if (IsEqualGUID (riid, &IID_IUnknown))
297 g_print ("...IUnknown\n");
298 m_add_ref_source (This);
302 else if (IsEqualGUID (riid, &IID_IDropSource))
304 g_print ("...IDropSource\n");
305 m_add_ref_source (This);
311 g_print ("...Huh?\n");
312 return E_NOINTERFACE;
316 ULONG STDMETHODCALLTYPE
317 m_add_ref_source (IDropSource __RPC_FAR *This)
319 source_drag_context *ctx = (source_drag_context *) This;
320 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
322 GDK_NOTE (DND, g_print ("m_add_ref_source\n"));
323 gdk_drag_context_ref (ctx->context);
325 return private->ref_count;
328 ULONG STDMETHODCALLTYPE
329 m_release_source (IDropSource __RPC_FAR *This)
331 source_drag_context *ctx = (source_drag_context *) This;
332 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
334 GDK_NOTE (DND, g_print ("m_release_source\n"));
335 gdk_drag_context_unref (ctx->context);
337 if (private->ref_count == 1)
339 gdk_drag_context_unref (ctx->context);
343 return private->ref_count - 1;
346 HRESULT STDMETHODCALLTYPE
347 m_query_continue_drag (IDropSource __RPC_FAR *This,
348 /* [in] */ BOOL fEscapePressed,
349 /* [in] */ DWORD grfKeyState)
351 GDK_NOTE (DND, g_print ("m_query_continue_drag\n"));
355 HRESULT STDMETHODCALLTYPE
356 m_give_feedback (IDropSource __RPC_FAR *This,
357 /* [in] */ DWORD dwEffect)
359 GDK_NOTE (DND, g_print ("m_give_feedback\n"));
363 static IDropTargetVtbl idt_vtbl = {
364 m_query_interface_target,
373 static IDropSourceVtbl ids_vtbl = {
374 m_query_interface_source,
377 m_query_continue_drag,
381 target_drag_context *
382 target_context_new (void)
384 target_drag_context *result;
386 result = g_new0 (target_drag_context, 1);
388 result->idt.lpVtbl = &idt_vtbl;
390 result->context = gdk_drag_context_new ();
395 source_drag_context *
396 source_context_new (void)
398 source_drag_context *result;
400 result = g_new0 (source_drag_context, 1);
402 result->ids.lpVtbl = &ids_vtbl;
404 result->context = gdk_drag_context_new ();
409 #endif /* OLE2_DND */
412 gdk_drag_context_ref (GdkDragContext *context)
414 g_return_if_fail (context != NULL);
416 ((GdkDragContextPrivate *)context)->ref_count++;
420 gdk_drag_context_unref (GdkDragContext *context)
422 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
424 g_return_if_fail (context != NULL);
426 private->ref_count--;
428 GDK_NOTE (DND, g_print ("gdk_drag_context_unref: %d%s\n",
430 (private->ref_count == 0 ? " freeing" : "")));
432 if (private->ref_count == 0)
434 g_dataset_destroy (private);
436 g_list_free (context->targets);
438 if (context->source_window)
439 gdk_window_unref (context->source_window);
441 if (context->dest_window)
442 gdk_window_unref (context->dest_window);
444 contexts = g_list_remove (contexts, private);
451 static GdkDragContext *
452 gdk_drag_context_find (gboolean is_source,
456 GList *tmp_list = contexts;
457 GdkDragContext *context;
461 context = (GdkDragContext *)tmp_list->data;
463 if ((!context->is_source == !is_source) &&
464 ((source_xid == None) || (context->source_window &&
465 (GDK_WINDOW_XWINDOW (context->source_window) == source_xid))) &&
466 ((dest_xid == None) || (context->dest_window &&
467 (GDK_WINDOW_XWINDOW (context->dest_window) == dest_xid))))
470 tmp_list = tmp_list->next;
480 /* From MS Knowledge Base article Q130698 */
482 /* resolve_link() fills the filename and path buffer
483 * with relevant information
484 * hWnd - calling app's window handle.
486 * lpszLinkName - name of the link file passed into the function.
488 * lpszPath - the buffer that will receive the file pathname.
492 resolve_link(HWND hWnd,
493 LPCTSTR lpszLinkName,
495 LPSTR lpszDescription)
501 /* Assume Failure to start with: */
504 *lpszDescription = 0;
506 /* Call CoCreateInstance to obtain the IShellLink interface
507 * pointer. This call fails if CoInitialize is not called, so it is
508 * assumed that CoInitialize has been called.
511 hres = CoCreateInstance (&CLSID_ShellLink,
513 CLSCTX_INPROC_SERVER,
516 if (SUCCEEDED (hres))
520 /* The IShellLink interface supports the IPersistFile
521 * interface. Get an interface pointer to it.
523 hres = psl->lpVtbl->QueryInterface (psl,
526 if (SUCCEEDED (hres))
530 /* Convert the given link name string to wide character string. */
531 MultiByteToWideChar (CP_ACP, 0,
535 hres = ppf->lpVtbl->Load (ppf, wsz, STGM_READ);
536 if (SUCCEEDED (hres))
538 /* Resolve the link by calling the Resolve()
539 * interface function.
541 hres = psl->lpVtbl->Resolve(psl, hWnd,
544 if (SUCCEEDED (hres))
546 hres = psl->lpVtbl->GetPath (psl, lpszPath,
548 (WIN32_FIND_DATA*)&wfd,
551 if (SUCCEEDED (hres) && lpszDescription != NULL)
553 hres = psl->lpVtbl->GetDescription (psl,
557 if (!SUCCEEDED (hres))
562 ppf->lpVtbl->Release (ppf);
564 psl->lpVtbl->Release (psl);
566 return SUCCEEDED (hres);
571 #define resolve_link(hWnd, lpszLinkName, lpszPath, lpszDescription) FALSE
575 static GdkFilterReturn
576 gdk_dropfiles_filter (GdkXEvent *xev,
580 GdkDragContext *context;
581 GdkDragContextPrivate *private;
582 static GdkAtom text_uri_list_atom = GDK_NONE;
584 MSG *msg = (MSG *) xev;
588 guchar fileName[MAX_PATH], linkedFile[MAX_PATH];
590 if (text_uri_list_atom == GDK_NONE)
591 text_uri_list_atom = gdk_atom_intern ("text/uri-list", FALSE);
593 if (msg->message == WM_DROPFILES)
595 GDK_NOTE (DND, g_print ("WM_DROPFILES: %#x\n", msg->hwnd));
597 context = gdk_drag_context_new ();
598 private = (GdkDragContextPrivate *) context;
599 context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
600 context->is_source = FALSE;
601 context->source_window = (GdkWindow *) gdk_root_parent;
602 context->dest_window = event->any.window;
603 gdk_window_ref (context->dest_window);
604 /* WM_DROPFILES drops are always file names */
606 g_list_append (NULL, GUINT_TO_POINTER (text_uri_list_atom));
607 current_dest_drag = context;
609 event->dnd.type = GDK_DROP_START;
610 event->dnd.context = current_dest_drag;
611 gdk_drag_context_ref (current_dest_drag);
613 hdrop = (HANDLE) msg->wParam;
614 DragQueryPoint (hdrop, &pt);
615 ClientToScreen (msg->hwnd, &pt);
617 event->dnd.x_root = pt.x;
618 event->dnd.y_root = pt.y;
619 event->dnd.time = msg->time;
621 nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
623 result = g_string_new (NULL);
624 for (i = 0; i < nfiles; i++)
626 g_string_append (result, "file:");
627 DragQueryFile (hdrop, i, fileName, MAX_PATH);
629 /* Resolve shortcuts */
630 if (resolve_link (msg->hwnd, fileName, linkedFile, NULL))
632 g_string_append (result, linkedFile);
633 GDK_NOTE (DND, g_print ("...%s link to %s\n",
634 fileName, linkedFile));
638 g_string_append (result, fileName);
639 GDK_NOTE (DND, g_print ("...%s\n", fileName));
641 g_string_append (result, "\015\012");
643 gdk_sel_prop_store ((GdkWindow *) gdk_root_parent,
644 text_uri_list_atom, 8, result->str, result->len + 1);
648 return GDK_FILTER_TRANSLATE;
651 return GDK_FILTER_CONTINUE;
654 /*************************************************************
655 ************************** Public API ***********************
656 *************************************************************/
663 hres = OleInitialize (NULL);
665 if (! SUCCEEDED (hres))
666 g_error ("OleInitialize failed");
681 gdk_drag_do_leave (GdkDragContext *context, guint32 time)
683 if (context->dest_window)
685 GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
686 gdk_window_unref (context->dest_window);
687 context->dest_window = NULL;
692 gdk_drag_begin (GdkWindow *window,
696 GdkDragContext *new_context;
698 g_return_val_if_fail (window != NULL, NULL);
700 GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
702 new_context = gdk_drag_context_new ();
703 new_context->is_source = TRUE;
704 new_context->source_window = window;
705 gdk_window_ref (window);
707 tmp_list = g_list_last (targets);
708 new_context->targets = NULL;
711 new_context->targets = g_list_prepend (new_context->targets,
713 tmp_list = tmp_list->prev;
716 new_context->actions = 0;
722 gdk_drag_get_protocol (guint32 xid,
723 GdkDragProtocol *protocol)
725 /* This isn't used */
730 gdk_drag_find_window (GdkDragContext *context,
731 GdkWindow *drag_window,
734 GdkWindow **dest_window,
735 GdkDragProtocol *protocol)
737 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
738 GdkDrawablePrivate *drag_window_private = (GdkDrawablePrivate*) drag_window;
742 GDK_NOTE (DND, g_print ("gdk_drag_find_window: %#x +%d+%d\n",
743 (drag_window ? drag_window_private->xwindow : 0),
748 recipient = WindowFromPoint (pt);
749 if (recipient == NULL)
753 *dest_window = gdk_window_lookup (recipient);
755 gdk_window_ref (*dest_window);
756 *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
761 gdk_drag_motion (GdkDragContext *context,
762 GdkWindow *dest_window,
763 GdkDragProtocol protocol,
766 GdkDragAction suggested_action,
767 GdkDragAction possible_actions,
774 gdk_drag_drop (GdkDragContext *context,
777 g_return_if_fail (context != NULL);
779 g_warning ("gdk_drag_drop: not implemented\n");
783 gdk_drag_abort (GdkDragContext *context,
786 g_return_if_fail (context != NULL);
788 gdk_drag_do_leave (context, time);
791 /* Destination side */
794 gdk_drag_status (GdkDragContext *context,
795 GdkDragAction action,
798 GDK_NOTE (DND, g_print ("gdk_drag_status\n"));
802 gdk_drop_reply (GdkDragContext *context,
809 gdk_drop_finish (GdkDragContext *context,
815 static GdkFilterReturn
816 gdk_destroy_filter (GdkXEvent *xev,
821 MSG *msg = (MSG *) xev;
823 if (msg->message == WM_DESTROY)
825 IDropTarget *idtp = (IDropTarget *) data;
827 GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %#x\n", msg->hwnd));
828 RevokeDragDrop (msg->hwnd);
829 CoLockObjectExternal (idtp, FALSE, TRUE);
832 return GDK_FILTER_CONTINUE;
836 gdk_window_register_dnd (GdkWindow *window)
838 GdkDrawablePrivate *private = (GdkDrawablePrivate *) window;
840 target_drag_context *context;
844 g_return_if_fail (window != NULL);
846 GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %#x\n", private->xwindow));
848 /* We always claim to accept dropped files, but in fact we might not,
849 * of course. This function is called in such a way that it cannot know
850 * whether the window (widget) in question actually accepts files
851 * (in gtk, data of type text/uri-list) or not.
853 gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
854 DragAcceptFiles (private->xwindow, TRUE);
857 /* Register for OLE2 d&d */
858 context = target_context_new ();
859 hres = CoLockObjectExternal ((IUnknown *) &context->idt, TRUE, FALSE);
860 if (!SUCCEEDED (hres))
861 g_warning ("gdk_window_register_dnd: CoLockObjectExternal failed");
864 hres = RegisterDragDrop (private->xwindow, &context->idt);
865 if (hres == DRAGDROP_E_ALREADYREGISTERED)
867 g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
868 CoLockObjectExternal ((IUnknown *) &context->idt, FALSE, FALSE);
870 else if (!SUCCEEDED (hres))
871 g_warning ("gdk_window_register_dnd: RegisterDragDrop failed");
874 gdk_window_add_filter (window, gdk_destroy_filter, &context->idt);
880 /*************************************************************
881 * gdk_drag_get_selection:
882 * Returns the selection atom for the current source window
886 *************************************************************/
889 gdk_drag_get_selection (GdkDragContext *context)
891 if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
892 return gdk_win32_dropfiles_atom;
893 else if (context->protocol == GDK_DRAG_PROTO_OLE2)
894 return gdk_ole2_dnd_atom;