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/.
46 typedef struct _GdkDragContextPrivate GdkDragContextPrivate;
50 GDK_DRAG_STATUS_MOTION_WAIT,
51 GDK_DRAG_STATUS_ACTION_WAIT,
62 HRESULT STDMETHODCALLTYPE
63 m_query_interface_target (IDropTarget __RPC_FAR *This,
64 /* [in] */ REFIID riid,
65 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
67 ULONG STDMETHODCALLTYPE
68 m_add_ref_target (IDropTarget __RPC_FAR *This);
70 ULONG STDMETHODCALLTYPE
71 m_release_target (IDropTarget __RPC_FAR *This);
73 HRESULT STDMETHODCALLTYPE
74 m_drag_enter (IDropTarget __RPC_FAR *This,
75 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
76 /* [in] */ DWORD grfKeyState,
78 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
80 HRESULT STDMETHODCALLTYPE
81 m_drag_over (IDropTarget __RPC_FAR *This,
82 /* [in] */ DWORD grfKeyState,
84 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
86 HRESULT STDMETHODCALLTYPE
87 m_drag_leave (IDropTarget __RPC_FAR *This);
89 HRESULT STDMETHODCALLTYPE
90 m_drop (IDropTarget __RPC_FAR *This,
91 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
92 /* [in] */ DWORD grfKeyState,
94 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
96 HRESULT STDMETHODCALLTYPE
97 m_query_interface_source (IDropSource __RPC_FAR *This,
98 /* [in] */ REFIID riid,
99 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
101 ULONG STDMETHODCALLTYPE
102 m_add_ref_source (IDropSource __RPC_FAR *This);
104 ULONG STDMETHODCALLTYPE
105 m_release_source (IDropSource __RPC_FAR *This);
107 HRESULT STDMETHODCALLTYPE
108 m_query_continue_drag (IDropSource __RPC_FAR *This,
109 /* [in] */ BOOL fEscapePressed,
110 /* [in] */ DWORD grfKeyState);
111 HRESULT STDMETHODCALLTYPE
112 m_give_feedback (IDropSource __RPC_FAR *This,
113 /* [in] */ DWORD dwEffect);
115 #endif /* OLE2_DND */
117 /* Structure that holds information about a drag in progress.
118 * this is used on both source and destination sides.
120 struct _GdkDragContextPrivate {
121 GdkDragContext context;
125 guint16 last_x; /* Coordinates from last event */
128 guint drag_status; /* Current status of drag */
131 GdkDragContext *current_dest_drag = NULL;
135 static GList *contexts;
138 gdk_drag_context_new (void)
140 GdkDragContextPrivate *result;
142 result = g_new0 (GdkDragContextPrivate, 1);
144 result->ref_count = 1;
146 contexts = g_list_prepend (contexts, result);
148 return (GdkDragContext *)result;
155 GdkDragContext *context;
156 } target_drag_context;
160 GdkDragContext *context;
161 } source_drag_context;
163 HRESULT STDMETHODCALLTYPE
164 m_query_interface_target (IDropTarget __RPC_FAR *This,
165 /* [in] */ REFIID riid,
166 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
168 GDK_NOTE (DND, g_print ("m_query_interface_target\n"));
172 g_print ("riid = %.08x-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x",
173 ((gulong *) riid)[0],
174 ((gushort *) riid)[2],
175 ((gushort *) riid)[3],
176 ((guchar *) riid)[8],
177 ((guchar *) riid)[9],
178 ((guchar *) riid)[10],
179 ((guchar *) riid)[11],
180 ((guchar *) riid)[12],
181 ((guchar *) riid)[13],
182 ((guchar *) riid)[14],
183 ((guchar *) riid)[15]);
184 if (IsEqualGUID (riid, &IID_IUnknown))
186 m_add_ref_target (This);
188 g_print ("...IUnknown\n");
191 else if (IsEqualGUID (riid, &IID_IDropTarget))
193 m_add_ref_target (This);
195 g_print ("...IDropTarget\n");
200 g_print ("...Huh?\n");
201 return E_NOINTERFACE;
205 ULONG STDMETHODCALLTYPE
206 m_add_ref_target (IDropTarget __RPC_FAR *This)
208 target_drag_context *ctx = (target_drag_context *) This;
209 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
211 GDK_NOTE (DND, g_print ("m_add_ref_target\n"));
212 gdk_drag_context_ref (ctx->context);
214 return private->ref_count;
217 ULONG STDMETHODCALLTYPE
218 m_release_target (IDropTarget __RPC_FAR *This)
220 target_drag_context *ctx = (target_drag_context *) This;
221 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
223 GDK_NOTE (DND, g_print ("m_release_target\n"));
224 gdk_drag_context_unref (ctx->context);
226 if (private->ref_count == 1)
228 gdk_drag_context_unref (ctx->context);
232 return private->ref_count - 1;
235 HRESULT STDMETHODCALLTYPE
236 m_drag_enter (IDropTarget __RPC_FAR *This,
237 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
238 /* [in] */ DWORD grfKeyState,
239 /* [in] */ POINTL pt,
240 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
242 GDK_NOTE (DND, g_print ("m_drag_enter\n"));
246 HRESULT STDMETHODCALLTYPE
247 m_drag_over (IDropTarget __RPC_FAR *This,
248 /* [in] */ DWORD grfKeyState,
249 /* [in] */ POINTL pt,
250 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
252 GDK_NOTE (DND, g_print ("m_drag_over\n"));
256 HRESULT STDMETHODCALLTYPE
257 m_drag_leave (IDropTarget __RPC_FAR *This)
259 GDK_NOTE (DND, g_print ("m_drag_leave\n"));
263 HRESULT STDMETHODCALLTYPE
264 m_drop (IDropTarget __RPC_FAR *This,
265 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
266 /* [in] */ DWORD grfKeyState,
267 /* [in] */ POINTL pt,
268 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
270 GDK_NOTE (DND, g_print ("m_drop\n"));
274 HRESULT STDMETHODCALLTYPE
275 m_query_interface_source (IDropSource __RPC_FAR *This,
276 /* [in] */ REFIID riid,
277 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
279 GDK_NOTE (DND, g_print ("m_query_interface_source\n"));
283 g_print ("riid = %.02x%.02x%.02x%.02x-%.02x%.02x-%.02x%.02x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x",
284 ((guchar *) riid)[0],
285 ((guchar *) riid)[1],
286 ((guchar *) riid)[2],
287 ((guchar *) riid)[3],
288 ((guchar *) riid)[4],
289 ((guchar *) riid)[5],
290 ((guchar *) riid)[6],
291 ((guchar *) riid)[7],
292 ((guchar *) riid)[8],
293 ((guchar *) riid)[9],
294 ((guchar *) riid)[10],
295 ((guchar *) riid)[11],
296 ((guchar *) riid)[12],
297 ((guchar *) riid)[13],
298 ((guchar *) riid)[14],
299 ((guchar *) riid)[15]);
300 if (IsEqualGUID (riid, &IID_IUnknown))
302 m_add_ref_source (This);
304 g_print ("...IUnknown\n");
307 else if (IsEqualGUID (riid, &IID_IDropSource))
309 m_add_ref_source (This);
311 g_print ("...IDropSource\n");
316 g_print ("...Huh?\n");
317 return E_NOINTERFACE;
321 ULONG STDMETHODCALLTYPE
322 m_add_ref_source (IDropSource __RPC_FAR *This)
324 source_drag_context *ctx = (source_drag_context *) This;
325 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
327 GDK_NOTE (DND, g_print ("m_add_ref_source\n"));
328 gdk_drag_context_ref (ctx->context);
330 return private->ref_count;
333 ULONG STDMETHODCALLTYPE
334 m_release_source (IDropSource __RPC_FAR *This)
336 source_drag_context *ctx = (source_drag_context *) This;
337 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
339 GDK_NOTE (DND, g_print ("m_release_source\n"));
340 gdk_drag_context_unref (ctx->context);
342 if (private->ref_count == 1)
344 gdk_drag_context_unref (ctx->context);
348 return private->ref_count - 1;
351 HRESULT STDMETHODCALLTYPE
352 m_query_continue_drag (IDropSource __RPC_FAR *This,
353 /* [in] */ BOOL fEscapePressed,
354 /* [in] */ DWORD grfKeyState)
356 GDK_NOTE (DND, g_print ("m_query_continue_drag\n"));
360 HRESULT STDMETHODCALLTYPE
361 m_give_feedback (IDropSource __RPC_FAR *This,
362 /* [in] */ DWORD dwEffect)
364 GDK_NOTE (DND, g_print ("m_give_feedback\n"));
368 static IDropTargetVtbl idt_vtbl = {
369 m_query_interface_target,
378 static IDropSourceVtbl ids_vtbl = {
379 m_query_interface_source,
382 m_query_continue_drag,
386 target_drag_context *
387 target_context_new (void)
389 target_drag_context *result;
391 result = g_new0 (target_drag_context, 1);
393 result->idt.lpVtbl = &idt_vtbl;
395 result->context = gdk_drag_context_new ();
400 source_drag_context *
401 source_context_new (void)
403 source_drag_context *result;
405 result = g_new0 (source_drag_context, 1);
407 result->ids.lpVtbl = &ids_vtbl;
409 result->context = gdk_drag_context_new ();
414 #endif /* OLE2_DND */
417 gdk_drag_context_ref (GdkDragContext *context)
419 g_return_if_fail (context != NULL);
421 ((GdkDragContextPrivate *)context)->ref_count++;
425 gdk_drag_context_unref (GdkDragContext *context)
427 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
429 g_return_if_fail (context != NULL);
431 private->ref_count--;
433 GDK_NOTE (DND, g_print ("gdk_drag_context_unref: %d%s\n",
435 (private->ref_count == 0 ? " freeing" : "")));
437 if (private->ref_count == 0)
439 g_dataset_destroy (private);
441 g_list_free (context->targets);
443 if (context->source_window)
444 gdk_window_unref (context->source_window);
446 if (context->dest_window)
447 gdk_window_unref (context->dest_window);
449 contexts = g_list_remove (contexts, private);
456 static GdkDragContext *
457 gdk_drag_context_find (gboolean is_source,
461 GList *tmp_list = contexts;
462 GdkDragContext *context;
466 context = (GdkDragContext *)tmp_list->data;
468 if ((!context->is_source == !is_source) &&
469 ((source_xid == None) || (context->source_window &&
470 (GDK_WINDOW_XWINDOW (context->source_window) == source_xid))) &&
471 ((dest_xid == None) || (context->dest_window &&
472 (GDK_WINDOW_XWINDOW (context->dest_window) == dest_xid))))
475 tmp_list = tmp_list->next;
485 /* From MS Knowledge Base article Q130698 */
487 /* resolve_link() fills the filename and path buffer
488 * with relevant information
489 * hWnd - calling app's window handle.
491 * lpszLinkName - name of the link file passed into the function.
493 * lpszPath - the buffer that will receive the file pathname.
497 resolve_link(HWND hWnd,
498 LPCTSTR lpszLinkName,
500 LPSTR lpszDescription)
506 /* Assume Failure to start with: */
509 *lpszDescription = 0;
511 /* Call CoCreateInstance to obtain the IShellLink interface
512 * pointer. This call fails if CoInitialize is not called, so it is
513 * assumed that CoInitialize has been called.
516 hres = CoCreateInstance (&CLSID_ShellLink,
518 CLSCTX_INPROC_SERVER,
521 if (SUCCEEDED (hres))
525 /* The IShellLink interface supports the IPersistFile
526 * interface. Get an interface pointer to it.
528 hres = psl->lpVtbl->QueryInterface (psl,
531 if (SUCCEEDED (hres))
535 /* Convert the given link name string to wide character string. */
536 MultiByteToWideChar (CP_ACP, 0,
540 hres = ppf->lpVtbl->Load (ppf, wsz, STGM_READ);
541 if (SUCCEEDED (hres))
543 /* Resolve the link by calling the Resolve()
544 * interface function.
546 hres = psl->lpVtbl->Resolve(psl, hWnd,
549 if (SUCCEEDED (hres))
551 hres = psl->lpVtbl->GetPath (psl, lpszPath,
553 (WIN32_FIND_DATA*)&wfd,
556 if (SUCCEEDED (hres) && lpszDescription != NULL)
558 hres = psl->lpVtbl->GetDescription (psl,
562 if (!SUCCEEDED (hres))
567 ppf->lpVtbl->Release (ppf);
569 psl->lpVtbl->Release (psl);
571 return SUCCEEDED (hres);
576 #define resolve_link(hWnd, lpszLinkName, lpszPath, lpszDescription) FALSE
580 static GdkFilterReturn
581 gdk_dropfiles_filter (GdkXEvent *xev,
585 GdkDragContext *context;
586 GdkDragContextPrivate *private;
587 static GdkAtom text_uri_list_atom = GDK_NONE;
589 MSG *msg = (MSG *) xev;
593 guchar fileName[MAX_PATH], linkedFile[MAX_PATH];
595 if (text_uri_list_atom == GDK_NONE)
596 text_uri_list_atom = gdk_atom_intern ("text/uri-list", FALSE);
598 if (msg->message == WM_DROPFILES)
600 GDK_NOTE (DND, g_print ("WM_DROPFILES: %#x\n", msg->hwnd));
602 context = gdk_drag_context_new ();
603 private = (GdkDragContextPrivate *) context;
604 context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
605 context->is_source = FALSE;
606 context->source_window = (GdkWindow *) &gdk_root_parent;
607 context->dest_window = event->any.window;
608 gdk_window_ref (context->dest_window);
609 /* WM_DROPFILES drops are always file names */
611 g_list_append (NULL, GUINT_TO_POINTER (text_uri_list_atom));
612 current_dest_drag = context;
614 event->dnd.type = GDK_DROP_START;
615 event->dnd.context = current_dest_drag;
616 gdk_drag_context_ref (current_dest_drag);
618 hdrop = (HANDLE) msg->wParam;
619 DragQueryPoint (hdrop, &pt);
620 ClientToScreen (msg->hwnd, &pt);
622 event->dnd.x_root = pt.x;
623 event->dnd.y_root = pt.y;
624 event->dnd.time = msg->time;
626 nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
628 result = g_string_new (NULL);
629 for (i = 0; i < nfiles; i++)
631 g_string_append (result, "file:");
632 DragQueryFile (hdrop, i, fileName, MAX_PATH);
634 /* Resolve shortcuts */
635 if (resolve_link (msg->hwnd, fileName, linkedFile, NULL))
637 g_string_append (result, linkedFile);
638 GDK_NOTE (DND, g_print ("...%s link to %s\n",
639 fileName, linkedFile));
643 g_string_append (result, fileName);
644 GDK_NOTE (DND, g_print ("...%s\n", fileName));
646 g_string_append (result, "\015\012");
648 gdk_sel_prop_store ((GdkWindow *) &gdk_root_parent,
649 text_uri_list_atom, 8, result->str, result->len + 1);
653 return GDK_FILTER_TRANSLATE;
656 return GDK_FILTER_CONTINUE;
659 /*************************************************************
660 ************************** Public API ***********************
661 *************************************************************/
668 hres = OleInitialize (NULL);
670 if (! SUCCEEDED (hres))
671 g_error ("OleInitialize failed");
686 gdk_drag_do_leave (GdkDragContext *context, guint32 time)
688 if (context->dest_window)
690 GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
691 gdk_window_unref (context->dest_window);
692 context->dest_window = NULL;
697 gdk_drag_begin (GdkWindow *window,
701 GdkDragContext *new_context;
703 g_return_val_if_fail (window != NULL, NULL);
705 GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
707 new_context = gdk_drag_context_new ();
708 new_context->is_source = TRUE;
709 new_context->source_window = window;
710 gdk_window_ref (window);
712 tmp_list = g_list_last (targets);
713 new_context->targets = NULL;
716 new_context->targets = g_list_prepend (new_context->targets,
718 tmp_list = tmp_list->prev;
721 new_context->actions = 0;
727 gdk_drag_get_protocol (guint32 xid,
728 GdkDragProtocol *protocol)
730 /* This isn't used */
735 gdk_drag_find_window (GdkDragContext *context,
736 GdkWindow *drag_window,
739 GdkWindow **dest_window,
740 GdkDragProtocol *protocol)
742 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
743 GdkWindowPrivate *drag_window_private = (GdkWindowPrivate *) drag_window;
747 GDK_NOTE (DND, g_print ("gdk_drag_find_window: %#x +%d+%d\n",
748 (drag_window ? drag_window_private->xwindow : 0),
753 recipient = WindowFromPoint (pt);
754 if (recipient == NULL)
758 *dest_window = gdk_window_lookup (recipient);
760 gdk_window_ref (*dest_window);
761 *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
766 gdk_drag_motion (GdkDragContext *context,
767 GdkWindow *dest_window,
768 GdkDragProtocol protocol,
771 GdkDragAction suggested_action,
772 GdkDragAction possible_actions,
779 gdk_drag_drop (GdkDragContext *context,
782 g_return_if_fail (context != NULL);
784 g_warning ("gdk_drag_drop: not implemented\n");
788 gdk_drag_abort (GdkDragContext *context,
791 g_return_if_fail (context != NULL);
793 gdk_drag_do_leave (context, time);
796 /* Destination side */
799 gdk_drag_status (GdkDragContext *context,
800 GdkDragAction action,
803 GDK_NOTE (DND, g_print ("gdk_drag_status\n"));
807 gdk_drop_reply (GdkDragContext *context,
814 gdk_drop_finish (GdkDragContext *context,
821 gdk_window_register_dnd (GdkWindow *window)
823 GdkWindowPrivate *private = (GdkWindowPrivate *) window;
825 target_drag_context *context;
829 g_return_if_fail (window != NULL);
831 GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %#x\n", private->xwindow));
833 /* We always claim to accept dropped files, but in fact we might not,
834 * of course. This function is called in such a way that it cannot know
835 * whether the window (widget) in question actually accepts files
836 * (in gtk, data of type text/uri-list) or not.
838 gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
839 DragAcceptFiles (private->xwindow, TRUE);
842 /* Register for OLE2 d&d */
843 context = target_context_new ();
844 hres = CoLockObjectExternal ((IUnknown *) &context->idt, TRUE, FALSE);
845 if (!SUCCEEDED (hres))
846 g_warning ("gdk_window_register_dnd: CoLockObjectExternal failed");
849 hres = RegisterDragDrop (private->xwindow, &context->idt);
850 if (hres == DRAGDROP_E_ALREADYREGISTERED)
852 g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
853 CoLockObjectExternal ((IUnknown *) &context->idt, FALSE, FALSE);
855 else if (!SUCCEEDED (hres))
856 g_warning ("gdk_window_register_dnd: RegisterDragDrop failed");
861 /*************************************************************
862 * gdk_drag_get_selection:
863 * Returns the selection atom for the current source window
867 *************************************************************/
870 gdk_drag_get_selection (GdkDragContext *context)
872 if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
873 return gdk_win32_dropfiles_atom;
874 else if (context->protocol == GDK_DRAG_PROTO_OLE2)
875 return gdk_ole2_dnd_atom;