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/.
41 typedef struct _GdkDragContextPrivate GdkDragContextPrivate;
45 GDK_DRAG_STATUS_MOTION_WAIT,
46 GDK_DRAG_STATUS_ACTION_WAIT,
57 HRESULT STDMETHODCALLTYPE
58 m_query_interface_target (IDropTarget __RPC_FAR *This,
59 /* [in] */ REFIID riid,
60 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
62 ULONG STDMETHODCALLTYPE
63 m_add_ref_target (IDropTarget __RPC_FAR *This);
65 ULONG STDMETHODCALLTYPE
66 m_release_target (IDropTarget __RPC_FAR *This);
68 HRESULT STDMETHODCALLTYPE
69 m_drag_enter (IDropTarget __RPC_FAR *This,
70 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
71 /* [in] */ DWORD grfKeyState,
73 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
75 HRESULT STDMETHODCALLTYPE
76 m_drag_over (IDropTarget __RPC_FAR *This,
77 /* [in] */ DWORD grfKeyState,
79 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
81 HRESULT STDMETHODCALLTYPE
82 m_drag_leave (IDropTarget __RPC_FAR *This);
84 HRESULT STDMETHODCALLTYPE
85 m_drop (IDropTarget __RPC_FAR *This,
86 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
87 /* [in] */ DWORD grfKeyState,
89 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
91 HRESULT STDMETHODCALLTYPE
92 m_query_interface_source (IDropSource __RPC_FAR *This,
93 /* [in] */ REFIID riid,
94 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
96 ULONG STDMETHODCALLTYPE
97 m_add_ref_source (IDropSource __RPC_FAR *This);
99 ULONG STDMETHODCALLTYPE
100 m_release_source (IDropSource __RPC_FAR *This);
102 HRESULT STDMETHODCALLTYPE
103 m_query_continue_drag (IDropSource __RPC_FAR *This,
104 /* [in] */ BOOL fEscapePressed,
105 /* [in] */ DWORD grfKeyState);
106 HRESULT STDMETHODCALLTYPE
107 m_give_feedback (IDropSource __RPC_FAR *This,
108 /* [in] */ DWORD dwEffect);
110 #endif /* OLE2_DND */
112 /* Structure that holds information about a drag in progress.
113 * this is used on both source and destination sides.
115 struct _GdkDragContextPrivate {
116 GdkDragContext context;
120 guint16 last_x; /* Coordinates from last event */
123 guint drag_status; /* Current status of drag */
126 GdkDragContext *current_dest_drag = NULL;
130 static GList *contexts;
133 gdk_drag_context_new (void)
135 GdkDragContextPrivate *result;
137 result = g_new0 (GdkDragContextPrivate, 1);
139 result->ref_count = 1;
141 contexts = g_list_prepend (contexts, result);
143 return (GdkDragContext *)result;
150 GdkDragContext *context;
151 } target_drag_context;
155 GdkDragContext *context;
156 } source_drag_context;
158 HRESULT STDMETHODCALLTYPE
159 m_query_interface_target (IDropTarget __RPC_FAR *This,
160 /* [in] */ REFIID riid,
161 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
163 GDK_NOTE (DND, g_print ("m_query_interface_target\n"));
167 g_print ("riid = %.08x-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x",
168 ((gulong *) riid)[0],
169 ((gushort *) riid)[2],
170 ((gushort *) riid)[3],
171 ((guchar *) riid)[8],
172 ((guchar *) riid)[9],
173 ((guchar *) riid)[10],
174 ((guchar *) riid)[11],
175 ((guchar *) riid)[12],
176 ((guchar *) riid)[13],
177 ((guchar *) riid)[14],
178 ((guchar *) riid)[15]);
179 if (IsEqualGUID (riid, &IID_IUnknown))
181 m_add_ref_target (This);
183 g_print ("...IUnknown\n");
186 else if (IsEqualGUID (riid, &IID_IDropTarget))
188 m_add_ref_target (This);
190 g_print ("...IDropTarget\n");
195 g_print ("...Huh?\n");
196 return E_NOINTERFACE;
200 ULONG STDMETHODCALLTYPE
201 m_add_ref_target (IDropTarget __RPC_FAR *This)
203 target_drag_context *ctx = (target_drag_context *) This;
204 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
206 GDK_NOTE (DND, g_print ("m_add_ref_target\n"));
207 gdk_drag_context_ref (ctx->context);
209 return private->ref_count;
212 ULONG STDMETHODCALLTYPE
213 m_release_target (IDropTarget __RPC_FAR *This)
215 target_drag_context *ctx = (target_drag_context *) This;
216 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
218 GDK_NOTE (DND, g_print ("m_release_target\n"));
219 gdk_drag_context_unref (ctx->context);
221 if (private->ref_count == 1)
223 gdk_drag_context_unref (ctx->context);
227 return private->ref_count - 1;
230 HRESULT STDMETHODCALLTYPE
231 m_drag_enter (IDropTarget __RPC_FAR *This,
232 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
233 /* [in] */ DWORD grfKeyState,
234 /* [in] */ POINTL pt,
235 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
237 GDK_NOTE (DND, g_print ("m_drag_enter\n"));
241 HRESULT STDMETHODCALLTYPE
242 m_drag_over (IDropTarget __RPC_FAR *This,
243 /* [in] */ DWORD grfKeyState,
244 /* [in] */ POINTL pt,
245 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
247 GDK_NOTE (DND, g_print ("m_drag_over\n"));
251 HRESULT STDMETHODCALLTYPE
252 m_drag_leave (IDropTarget __RPC_FAR *This)
254 GDK_NOTE (DND, g_print ("m_drag_leave\n"));
258 HRESULT STDMETHODCALLTYPE
259 m_drop (IDropTarget __RPC_FAR *This,
260 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
261 /* [in] */ DWORD grfKeyState,
262 /* [in] */ POINTL pt,
263 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
265 GDK_NOTE (DND, g_print ("m_drop\n"));
269 HRESULT STDMETHODCALLTYPE
270 m_query_interface_source (IDropSource __RPC_FAR *This,
271 /* [in] */ REFIID riid,
272 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
274 GDK_NOTE (DND, g_print ("m_query_interface_source\n"));
278 g_print ("riid = %.02x%.02x%.02x%.02x-%.02x%.02x-%.02x%.02x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x",
279 ((guchar *) riid)[0],
280 ((guchar *) riid)[1],
281 ((guchar *) riid)[2],
282 ((guchar *) riid)[3],
283 ((guchar *) riid)[4],
284 ((guchar *) riid)[5],
285 ((guchar *) riid)[6],
286 ((guchar *) riid)[7],
287 ((guchar *) riid)[8],
288 ((guchar *) riid)[9],
289 ((guchar *) riid)[10],
290 ((guchar *) riid)[11],
291 ((guchar *) riid)[12],
292 ((guchar *) riid)[13],
293 ((guchar *) riid)[14],
294 ((guchar *) riid)[15]);
295 if (IsEqualGUID (riid, &IID_IUnknown))
297 m_add_ref_source (This);
299 g_print ("...IUnknown\n");
302 else if (IsEqualGUID (riid, &IID_IDropSource))
304 m_add_ref_source (This);
306 g_print ("...IDropSource\n");
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;
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);
567 static GdkFilterReturn
568 gdk_dropfiles_filter (GdkXEvent *xev,
572 GdkDragContext *context;
573 GdkDragContextPrivate *private;
574 static GdkAtom text_uri_list_atom = GDK_NONE;
576 MSG *msg = (MSG *) xev;
580 guchar fileName[MAX_PATH], linkedFile[MAX_PATH];
582 if (text_uri_list_atom == GDK_NONE)
583 text_uri_list_atom = gdk_atom_intern ("text/uri-list", FALSE);
585 if (msg->message == WM_DROPFILES)
587 GDK_NOTE (DND, g_print ("WM_DROPFILES: %#x\n", msg->hwnd));
589 context = gdk_drag_context_new ();
590 private = (GdkDragContextPrivate *) context;
591 context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
592 context->is_source = FALSE;
593 context->source_window = (GdkWindow *) &gdk_root_parent;
594 context->dest_window = event->any.window;
595 gdk_window_ref (context->dest_window);
596 /* WM_DROPFILES drops are always file names */
598 g_list_append (NULL, GUINT_TO_POINTER (text_uri_list_atom));
599 current_dest_drag = context;
601 event->dnd.type = GDK_DROP_START;
602 event->dnd.context = current_dest_drag;
603 gdk_drag_context_ref (current_dest_drag);
605 hdrop = (HANDLE) msg->wParam;
606 DragQueryPoint (hdrop, &pt);
607 ClientToScreen (msg->hwnd, &pt);
609 event->dnd.x_root = pt.x;
610 event->dnd.y_root = pt.y;
611 event->dnd.time = msg->time;
613 nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
615 result = g_string_new (NULL);
616 for (i = 0; i < nfiles; i++)
618 g_string_append (result, "file:");
619 DragQueryFile (hdrop, i, fileName, MAX_PATH);
621 /* Resolve shortcuts */
622 if (resolve_link (msg->hwnd, fileName, linkedFile, NULL))
624 g_string_append (result, linkedFile);
625 GDK_NOTE (DND, g_print ("...%s link to %s\n",
626 fileName, linkedFile));
630 g_string_append (result, fileName);
631 GDK_NOTE (DND, g_print ("...%s\n", fileName));
633 g_string_append (result, "\015\012");
635 gdk_sel_prop_store ((GdkWindow *) &gdk_root_parent,
636 text_uri_list_atom, 8, result->str, result->len + 1);
640 return GDK_FILTER_TRANSLATE;
643 return GDK_FILTER_CONTINUE;
647 /*************************************************************
648 ************************** Public API ***********************
649 *************************************************************/
656 hres = OleInitialize (NULL);
658 if (! SUCCEEDED (hres))
659 g_error ("OleInitialize failed");
671 gdk_drag_do_leave (GdkDragContext *context, guint32 time)
673 if (context->dest_window)
675 GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
676 gdk_window_unref (context->dest_window);
677 context->dest_window = NULL;
682 gdk_drag_begin (GdkWindow *window,
686 GdkDragContext *new_context;
688 g_return_val_if_fail (window != NULL, NULL);
690 GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
692 new_context = gdk_drag_context_new ();
693 new_context->is_source = TRUE;
694 new_context->source_window = window;
695 gdk_window_ref (window);
697 tmp_list = g_list_last (targets);
698 new_context->targets = NULL;
701 new_context->targets = g_list_prepend (new_context->targets,
703 tmp_list = tmp_list->prev;
706 new_context->actions = 0;
712 gdk_drag_get_protocol (guint32 xid,
713 GdkDragProtocol *protocol)
715 /* This isn't used */
720 gdk_drag_find_window (GdkDragContext *context,
721 GdkWindow *drag_window,
724 GdkWindow **dest_window,
725 GdkDragProtocol *protocol)
727 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
728 GdkWindowPrivate *drag_window_private = (GdkWindowPrivate *) drag_window;
732 GDK_NOTE (DND, g_print ("gdk_drag_find_window: %#x +%d+%d\n",
733 (drag_window ? drag_window_private->xwindow : 0),
738 recipient = WindowFromPoint (pt);
739 if (recipient == NULL)
743 *dest_window = gdk_window_lookup (recipient);
745 gdk_window_ref (*dest_window);
746 *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
751 gdk_drag_motion (GdkDragContext *context,
752 GdkWindow *dest_window,
753 GdkDragProtocol protocol,
756 GdkDragAction suggested_action,
757 GdkDragAction possible_actions,
764 gdk_drag_drop (GdkDragContext *context,
767 g_return_if_fail (context != NULL);
769 g_warning ("gdk_drag_drop: not implemented\n");
773 gdk_drag_abort (GdkDragContext *context,
776 g_return_if_fail (context != NULL);
778 gdk_drag_do_leave (context, time);
781 /* Destination side */
784 gdk_drag_status (GdkDragContext *context,
785 GdkDragAction action,
788 GDK_NOTE (DND, g_print ("gdk_drag_status\n"));
792 gdk_drop_reply (GdkDragContext *context,
799 gdk_drop_finish (GdkDragContext *context,
807 gdk_window_register_dnd (GdkWindow *window)
809 GdkWindowPrivate *private = (GdkWindowPrivate *) window;
811 target_drag_context *context;
815 g_return_if_fail (window != NULL);
817 GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %#x\n", private->xwindow));
819 /* We always claim to accept dropped files, but in fact we might not,
820 * of course. This function is called in such a way that it cannot know
821 * whether the window (widget) in question actually accepts files
822 * (in gtk, data of type text/uri-list) or not.
824 gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
825 DragAcceptFiles (private->xwindow, TRUE);
828 /* Register for OLE2 d&d */
829 context = target_context_new ();
830 hres = CoLockObjectExternal ((IUnknown *) &context->idt, TRUE, FALSE);
831 if (!SUCCEEDED (hres))
832 g_warning ("gdk_window_register_dnd: CoLockObjectExternal failed");
835 hres = RegisterDragDrop (private->xwindow, &context->idt);
836 if (hres == DRAGDROP_E_ALREADYREGISTERED)
838 g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
839 CoLockObjectExternal ((IUnknown *) &context->idt, FALSE, FALSE);
841 else if (!SUCCEEDED (hres))
842 g_warning ("gdk_window_register_dnd: RegisterDragDrop failed");
847 /*************************************************************
848 * gdk_drag_get_selection:
849 * Returns the selection atom for the current source window
853 *************************************************************/
856 gdk_drag_get_selection (GdkDragContext *context)
858 if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
859 return gdk_win32_dropfiles_atom;
860 else if (context->protocol == GDK_DRAG_PROTO_OLE2)
861 return gdk_ole2_dnd_atom;