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 "gdk/gdkprivate.h"
40 typedef struct _GdkDragContextPrivate GdkDragContextPrivate;
44 GDK_DRAG_STATUS_MOTION_WAIT,
45 GDK_DRAG_STATUS_ACTION_WAIT,
56 HRESULT STDMETHODCALLTYPE
57 m_query_interface_target (IDropTarget __RPC_FAR *This,
58 /* [in] */ REFIID riid,
59 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
61 ULONG STDMETHODCALLTYPE
62 m_add_ref_target (IDropTarget __RPC_FAR *This);
64 ULONG STDMETHODCALLTYPE
65 m_release_target (IDropTarget __RPC_FAR *This);
67 HRESULT STDMETHODCALLTYPE
68 m_drag_enter (IDropTarget __RPC_FAR *This,
69 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
70 /* [in] */ DWORD grfKeyState,
72 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
74 HRESULT STDMETHODCALLTYPE
75 m_drag_over (IDropTarget __RPC_FAR *This,
76 /* [in] */ DWORD grfKeyState,
78 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
80 HRESULT STDMETHODCALLTYPE
81 m_drag_leave (IDropTarget __RPC_FAR *This);
83 HRESULT STDMETHODCALLTYPE
84 m_drop (IDropTarget __RPC_FAR *This,
85 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
86 /* [in] */ DWORD grfKeyState,
88 /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
90 HRESULT STDMETHODCALLTYPE
91 m_query_interface_source (IDropSource __RPC_FAR *This,
92 /* [in] */ REFIID riid,
93 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
95 ULONG STDMETHODCALLTYPE
96 m_add_ref_source (IDropSource __RPC_FAR *This);
98 ULONG STDMETHODCALLTYPE
99 m_release_source (IDropSource __RPC_FAR *This);
101 HRESULT STDMETHODCALLTYPE
102 m_query_continue_drag (IDropSource __RPC_FAR *This,
103 /* [in] */ BOOL fEscapePressed,
104 /* [in] */ DWORD grfKeyState);
105 HRESULT STDMETHODCALLTYPE
106 m_give_feedback (IDropSource __RPC_FAR *This,
107 /* [in] */ DWORD dwEffect);
109 #endif /* OLE2_DND */
111 /* Structure that holds information about a drag in progress.
112 * this is used on both source and destination sides.
114 struct _GdkDragContextPrivate {
115 GdkDragContext context;
119 guint16 last_x; /* Coordinates from last event */
122 guint drag_status; /* Current status of drag */
125 GdkDragContext *current_dest_drag = NULL;
129 static GList *contexts;
132 gdk_drag_context_new (void)
134 GdkDragContextPrivate *result;
136 result = g_new0 (GdkDragContextPrivate, 1);
138 result->ref_count = 1;
140 contexts = g_list_prepend (contexts, result);
142 return (GdkDragContext *)result;
149 GdkDragContext *context;
150 } target_drag_context;
154 GdkDragContext *context;
155 } source_drag_context;
157 HRESULT STDMETHODCALLTYPE
158 m_query_interface_target (IDropTarget __RPC_FAR *This,
159 /* [in] */ REFIID riid,
160 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
162 GDK_NOTE (DND, g_print ("m_query_interface_target\n"));
166 g_print ("riid = %.08x-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x",
167 ((gulong *) riid)[0],
168 ((gushort *) riid)[2],
169 ((gushort *) riid)[3],
170 ((guchar *) riid)[8],
171 ((guchar *) riid)[9],
172 ((guchar *) riid)[10],
173 ((guchar *) riid)[11],
174 ((guchar *) riid)[12],
175 ((guchar *) riid)[13],
176 ((guchar *) riid)[14],
177 ((guchar *) riid)[15]);
178 if (IsEqualGUID (riid, &IID_IUnknown))
180 m_add_ref_target (This);
182 g_print ("...IUnknown\n");
185 else if (IsEqualGUID (riid, &IID_IDropTarget))
187 m_add_ref_target (This);
189 g_print ("...IDropTarget\n");
194 g_print ("...Huh?\n");
195 return E_NOINTERFACE;
199 ULONG STDMETHODCALLTYPE
200 m_add_ref_target (IDropTarget __RPC_FAR *This)
202 target_drag_context *ctx = (target_drag_context *) This;
203 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
205 GDK_NOTE (DND, g_print ("m_add_ref_target\n"));
206 gdk_drag_context_ref (ctx->context);
208 return private->ref_count;
211 ULONG STDMETHODCALLTYPE
212 m_release_target (IDropTarget __RPC_FAR *This)
214 target_drag_context *ctx = (target_drag_context *) This;
215 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
217 GDK_NOTE (DND, g_print ("m_release_target\n"));
218 gdk_drag_context_unref (ctx->context);
220 if (private->ref_count == 1)
222 gdk_drag_context_unref (ctx->context);
226 return private->ref_count - 1;
229 HRESULT STDMETHODCALLTYPE
230 m_drag_enter (IDropTarget __RPC_FAR *This,
231 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
232 /* [in] */ DWORD grfKeyState,
233 /* [in] */ POINTL pt,
234 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
236 GDK_NOTE (DND, g_print ("m_drag_enter\n"));
240 HRESULT STDMETHODCALLTYPE
241 m_drag_over (IDropTarget __RPC_FAR *This,
242 /* [in] */ DWORD grfKeyState,
243 /* [in] */ POINTL pt,
244 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
246 GDK_NOTE (DND, g_print ("m_drag_over\n"));
250 HRESULT STDMETHODCALLTYPE
251 m_drag_leave (IDropTarget __RPC_FAR *This)
253 GDK_NOTE (DND, g_print ("m_drag_leave\n"));
257 HRESULT STDMETHODCALLTYPE
258 m_drop (IDropTarget __RPC_FAR *This,
259 /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
260 /* [in] */ DWORD grfKeyState,
261 /* [in] */ POINTL pt,
262 /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
264 GDK_NOTE (DND, g_print ("m_drop\n"));
268 HRESULT STDMETHODCALLTYPE
269 m_query_interface_source (IDropSource __RPC_FAR *This,
270 /* [in] */ REFIID riid,
271 /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
273 GDK_NOTE (DND, g_print ("m_query_interface_source\n"));
277 g_print ("riid = %.02x%.02x%.02x%.02x-%.02x%.02x-%.02x%.02x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x",
278 ((guchar *) riid)[0],
279 ((guchar *) riid)[1],
280 ((guchar *) riid)[2],
281 ((guchar *) riid)[3],
282 ((guchar *) riid)[4],
283 ((guchar *) riid)[5],
284 ((guchar *) riid)[6],
285 ((guchar *) riid)[7],
286 ((guchar *) riid)[8],
287 ((guchar *) riid)[9],
288 ((guchar *) riid)[10],
289 ((guchar *) riid)[11],
290 ((guchar *) riid)[12],
291 ((guchar *) riid)[13],
292 ((guchar *) riid)[14],
293 ((guchar *) riid)[15]);
294 if (IsEqualGUID (riid, &IID_IUnknown))
296 m_add_ref_source (This);
298 g_print ("...IUnknown\n");
301 else if (IsEqualGUID (riid, &IID_IDropSource))
303 m_add_ref_source (This);
305 g_print ("...IDropSource\n");
310 g_print ("...Huh?\n");
311 return E_NOINTERFACE;
315 ULONG STDMETHODCALLTYPE
316 m_add_ref_source (IDropSource __RPC_FAR *This)
318 source_drag_context *ctx = (source_drag_context *) This;
319 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
321 GDK_NOTE (DND, g_print ("m_add_ref_source\n"));
322 gdk_drag_context_ref (ctx->context);
324 return private->ref_count;
327 ULONG STDMETHODCALLTYPE
328 m_release_source (IDropSource __RPC_FAR *This)
330 source_drag_context *ctx = (source_drag_context *) This;
331 GdkDragContextPrivate *private = (GdkDragContextPrivate *) ctx->context;
333 GDK_NOTE (DND, g_print ("m_release_source\n"));
334 gdk_drag_context_unref (ctx->context);
336 if (private->ref_count == 1)
338 gdk_drag_context_unref (ctx->context);
342 return private->ref_count - 1;
345 HRESULT STDMETHODCALLTYPE
346 m_query_continue_drag (IDropSource __RPC_FAR *This,
347 /* [in] */ BOOL fEscapePressed,
348 /* [in] */ DWORD grfKeyState)
350 GDK_NOTE (DND, g_print ("m_query_continue_drag\n"));
354 HRESULT STDMETHODCALLTYPE
355 m_give_feedback (IDropSource __RPC_FAR *This,
356 /* [in] */ DWORD dwEffect)
358 GDK_NOTE (DND, g_print ("m_give_feedback\n"));
362 static IDropTargetVtbl idt_vtbl = {
363 m_query_interface_target,
372 static IDropSourceVtbl ids_vtbl = {
373 m_query_interface_source,
376 m_query_continue_drag,
380 target_drag_context *
381 target_context_new (void)
383 target_drag_context *result;
385 result = g_new0 (target_drag_context, 1);
387 result->idt.lpVtbl = &idt_vtbl;
389 result->context = gdk_drag_context_new ();
394 source_drag_context *
395 source_context_new (void)
397 source_drag_context *result;
399 result = g_new0 (source_drag_context, 1);
401 result->ids.lpVtbl = &ids_vtbl;
403 result->context = gdk_drag_context_new ();
408 #endif /* OLE2_DND */
411 gdk_drag_context_ref (GdkDragContext *context)
413 g_return_if_fail (context != NULL);
415 ((GdkDragContextPrivate *)context)->ref_count++;
419 gdk_drag_context_unref (GdkDragContext *context)
421 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
423 g_return_if_fail (context != NULL);
425 private->ref_count--;
427 GDK_NOTE (DND, g_print ("gdk_drag_context_unref: %d%s\n",
429 (private->ref_count == 0 ? " freeing" : "")));
431 if (private->ref_count == 0)
433 g_dataset_destroy (private);
435 g_list_free (context->targets);
437 if (context->source_window)
438 gdk_window_unref (context->source_window);
440 if (context->dest_window)
441 gdk_window_unref (context->dest_window);
443 contexts = g_list_remove (contexts, private);
450 static GdkDragContext *
451 gdk_drag_context_find (gboolean is_source,
455 GList *tmp_list = contexts;
456 GdkDragContext *context;
460 context = (GdkDragContext *)tmp_list->data;
462 if ((!context->is_source == !is_source) &&
463 ((source_xid == None) || (context->source_window &&
464 (GDK_WINDOW_XWINDOW (context->source_window) == source_xid))) &&
465 ((dest_xid == None) || (context->dest_window &&
466 (GDK_WINDOW_XWINDOW (context->dest_window) == dest_xid))))
469 tmp_list = tmp_list->next;
477 /* From MS Knowledge Base article Q130698 */
479 /* resolve_link() fills the filename and path buffer
480 * with relevant information
481 * hWnd - calling app's window handle.
483 * lpszLinkName - name of the link file passed into the function.
485 * lpszPath - the buffer that will receive the file pathname.
489 resolve_link(HWND hWnd,
490 LPCTSTR lpszLinkName,
492 LPSTR lpszDescription)
498 /* Assume Failure to start with: */
501 *lpszDescription = 0;
503 /* Call CoCreateInstance to obtain the IShellLink interface
504 * pointer. This call fails if CoInitialize is not called, so it is
505 * assumed that CoInitialize has been called.
508 hres = CoCreateInstance (&CLSID_ShellLink,
510 CLSCTX_INPROC_SERVER,
513 if (SUCCEEDED (hres))
517 /* The IShellLink interface supports the IPersistFile
518 * interface. Get an interface pointer to it.
520 hres = psl->lpVtbl->QueryInterface (psl,
523 if (SUCCEEDED (hres))
527 /* Convert the given link name string to wide character string. */
528 MultiByteToWideChar (CP_ACP, 0,
532 hres = ppf->lpVtbl->Load (ppf, wsz, STGM_READ);
533 if (SUCCEEDED (hres))
535 /* Resolve the link by calling the Resolve()
536 * interface function.
538 hres = psl->lpVtbl->Resolve(psl, hWnd,
541 if (SUCCEEDED (hres))
543 hres = psl->lpVtbl->GetPath (psl, lpszPath,
545 (WIN32_FIND_DATA*)&wfd,
548 if (SUCCEEDED (hres) && lpszDescription != NULL)
550 hres = psl->lpVtbl->GetDescription (psl,
554 if (!SUCCEEDED (hres))
559 ppf->lpVtbl->Release (ppf);
561 psl->lpVtbl->Release (psl);
563 return SUCCEEDED (hres);
566 static GdkFilterReturn
567 gdk_dropfiles_filter (GdkXEvent *xev,
571 GdkDragContext *context;
572 GdkDragContextPrivate *private;
573 static GdkAtom text_uri_list_atom = GDK_NONE;
575 MSG *msg = (MSG *) xev;
579 guchar fileName[MAX_PATH], linkedFile[MAX_PATH];
581 if (text_uri_list_atom == GDK_NONE)
582 text_uri_list_atom = gdk_atom_intern ("text/uri-list", FALSE);
584 if (msg->message == WM_DROPFILES)
586 GDK_NOTE (DND, g_print ("WM_DROPFILES: %#x\n", msg->hwnd));
588 context = gdk_drag_context_new ();
589 private = (GdkDragContextPrivate *) context;
590 context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
591 context->is_source = FALSE;
592 context->source_window = (GdkWindow *) &gdk_root_parent;
593 context->dest_window = event->any.window;
594 gdk_window_ref (context->dest_window);
595 /* WM_DROPFILES drops are always file names */
597 g_list_append (NULL, GUINT_TO_POINTER (text_uri_list_atom));
598 current_dest_drag = context;
600 event->dnd.type = GDK_DROP_START;
601 event->dnd.context = current_dest_drag;
602 gdk_drag_context_ref (current_dest_drag);
604 hdrop = (HANDLE) msg->wParam;
605 DragQueryPoint (hdrop, &pt);
606 ClientToScreen (msg->hwnd, &pt);
608 event->dnd.x_root = pt.x;
609 event->dnd.y_root = pt.y;
610 event->dnd.time = msg->time;
612 nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
614 result = g_string_new (NULL);
615 for (i = 0; i < nfiles; i++)
617 g_string_append (result, "file:");
618 DragQueryFile (hdrop, i, fileName, MAX_PATH);
620 /* Resolve shortcuts */
621 if (resolve_link (msg->hwnd, fileName, linkedFile, NULL))
623 g_string_append (result, linkedFile);
624 GDK_NOTE (DND, g_print ("...%s link to %s\n",
625 fileName, linkedFile));
629 g_string_append (result, fileName);
630 GDK_NOTE (DND, g_print ("...%s\n", fileName));
632 g_string_append (result, "\015\012");
634 gdk_sel_prop_store ((GdkWindow *) &gdk_root_parent,
635 text_uri_list_atom, 8, result->str, result->len + 1);
639 return GDK_FILTER_TRANSLATE;
642 return GDK_FILTER_CONTINUE;
646 /*************************************************************
647 ************************** Public API ***********************
648 *************************************************************/
655 hres = OleInitialize (NULL);
657 if (! SUCCEEDED (hres))
658 g_error ("OleInitialize failed");
670 gdk_drag_do_leave (GdkDragContext *context, guint32 time)
672 if (context->dest_window)
674 GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
675 gdk_window_unref (context->dest_window);
676 context->dest_window = NULL;
681 gdk_drag_begin (GdkWindow *window,
685 GdkDragContext *new_context;
687 g_return_val_if_fail (window != NULL, NULL);
689 GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
691 new_context = gdk_drag_context_new ();
692 new_context->is_source = TRUE;
693 new_context->source_window = window;
694 gdk_window_ref (window);
696 tmp_list = g_list_last (targets);
697 new_context->targets = NULL;
700 new_context->targets = g_list_prepend (new_context->targets,
702 tmp_list = tmp_list->prev;
705 new_context->actions = 0;
711 gdk_drag_get_protocol (guint32 xid,
712 GdkDragProtocol *protocol)
714 /* This isn't used */
719 gdk_drag_find_window (GdkDragContext *context,
720 GdkWindow *drag_window,
723 GdkWindow **dest_window,
724 GdkDragProtocol *protocol)
726 GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
727 GdkWindowPrivate *drag_window_private = (GdkWindowPrivate *) drag_window;
731 GDK_NOTE (DND, g_print ("gdk_drag_find_window: %#x +%d+%d\n",
732 (drag_window ? drag_window_private->xwindow : 0),
737 recipient = WindowFromPoint (pt);
738 if (recipient == NULL)
742 *dest_window = gdk_window_lookup (recipient);
744 gdk_window_ref (*dest_window);
745 *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
750 gdk_drag_motion (GdkDragContext *context,
751 GdkWindow *dest_window,
752 GdkDragProtocol protocol,
755 GdkDragAction suggested_action,
756 GdkDragAction possible_actions,
763 gdk_drag_drop (GdkDragContext *context,
766 g_return_if_fail (context != NULL);
768 g_warning ("gdk_drag_drop: not implemented\n");
772 gdk_drag_abort (GdkDragContext *context,
775 g_return_if_fail (context != NULL);
777 gdk_drag_do_leave (context, time);
780 /* Destination side */
783 gdk_drag_status (GdkDragContext *context,
784 GdkDragAction action,
787 GDK_NOTE (DND, g_print ("gdk_drag_status\n"));
791 gdk_drop_reply (GdkDragContext *context,
798 gdk_drop_finish (GdkDragContext *context,
806 gdk_window_register_dnd (GdkWindow *window)
808 GdkWindowPrivate *private = (GdkWindowPrivate *) window;
810 target_drag_context *context;
814 g_return_if_fail (window != NULL);
816 GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %#x\n", private->xwindow));
818 /* We always claim to accept dropped files, but in fact we might not,
819 * of course. This function is called in such a way that it cannot know
820 * whether the window (widget) in question actually accepts files
821 * (in gtk, data of type text/uri-list) or not.
823 gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
824 DragAcceptFiles (private->xwindow, TRUE);
827 /* Register for OLE2 d&d */
828 context = target_context_new ();
829 hres = CoLockObjectExternal ((IUnknown *) &context->idt, TRUE, FALSE);
830 if (!SUCCEEDED (hres))
831 g_warning ("gdk_window_register_dnd: CoLockObjectExternal failed");
834 hres = RegisterDragDrop (private->xwindow, &context->idt);
835 if (hres == DRAGDROP_E_ALREADYREGISTERED)
837 g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
838 CoLockObjectExternal ((IUnknown *) &context->idt, FALSE, FALSE);
840 else if (!SUCCEEDED (hres))
841 g_warning ("gdk_window_register_dnd: RegisterDragDrop failed");
846 /*************************************************************
847 * gdk_drag_get_selection:
848 * Returns the selection atom for the current source window
852 *************************************************************/
855 gdk_drag_get_selection (GdkDragContext *context)
857 if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
858 return gdk_win32_dropfiles_atom;
859 else if (context->protocol == GDK_DRAG_PROTO_OLE2)
860 return gdk_ole2_dnd_atom;