mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 13:41:07 +00:00
1d838f586c
There are sure regressions but basic stuff seems to be working again after all the API breakage done with comments like "Win32 and Quartz need to be ported still."
2468 lines
63 KiB
C
2468 lines
63 KiB
C
/* GDK - The GIMP Drawing Kit
|
|
* Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
* Copyright (C) 2001 Archaeopteryx Software Inc.
|
|
* Copyright (C) 1998-2002 Tor Lillqvist
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/*
|
|
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
|
* file for a list of people on the GTK+ Team. See the ChangeLog
|
|
* files for a list of changes. These files are distributed with
|
|
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <string.h>
|
|
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
|
|
/*
|
|
* Comment from the old OLE2 DND code that is being merged in. Note
|
|
* that this comment might not fully reflect reality as the code
|
|
* obviously will have to be modified in this merge. Especially the
|
|
* talk about supporting other than UTF-8 text is bogus, that will not
|
|
* happen.
|
|
*
|
|
* Support for OLE-2 drag and drop added at Archaeopteryx Software, 2001
|
|
* For more information, contact Stephan R.A. Deibel (sdeibel@archaeopteryx.com)
|
|
*
|
|
* Notes on implementation:
|
|
*
|
|
* This is a first pass at OLE2 support. It only supports text and unicode text
|
|
* data types, and file list dnd (which is handled seperately as it predates OLE2
|
|
* both in this implementation and on Windows in general).
|
|
*
|
|
* As such, the data type conversion from gdk selection targets to OLE2 CF_* data
|
|
* type specifiers is partially hardwired. Fixing this is complicated by (a) the
|
|
* fact that the widget's declared selection types aren't accessible in calls here
|
|
* that need to declare the corresponding OLE2 data types, and (b) there isn't a
|
|
* 1-1 correspondence between gdk target types and OLE2 types. The former needs
|
|
* some redesign in gtk dnd (something a gdk/gtk expert should do; I have tried
|
|
* and failed!). As an example of the latter: gdk STRING, TEXT, COMPOUND_TEXT map
|
|
* to CF_TEXT, CF_OEMTEXT, and CF_UNICODETEXT but as a group and with conversions
|
|
* necessary for various combinations. Currently, the code here (and in
|
|
* gdkdnd-win32.c) can handle gdk STRING and TEXT but not COMPOUND_TEXT, and OLE2
|
|
* CF_TEXT and CF_UNICODETEXT but not CF_OEMTEXT. The necessary conversions are
|
|
* supplied by the implementation here.
|
|
*
|
|
* Note that in combination with another hack originated by Archaeopteryx
|
|
* Software, the text conversions here may go to utf-8 unicode as the standard
|
|
* within-gtk target or to single-byte ascii when the USE_ACP_TEXT compilation
|
|
* flag is TRUE. This mode was added to support applications that aren't using
|
|
* utf-8 across the gtk/gdk API but instead use single-byte ascii according to
|
|
* the current Windows code page. See gdkim-win32.c for more info on that.
|
|
*
|
|
*/
|
|
|
|
#define INITGUID
|
|
|
|
#include "gdkdnd.h"
|
|
#include "gdkproperty.h"
|
|
#include "gdkinternals.h"
|
|
#include "gdkprivate-win32.h"
|
|
#include "gdkwin32.h"
|
|
#include "gdkwin32dnd.h"
|
|
#include "gdk/gdkdndprivate.h"
|
|
|
|
#include <ole2.h>
|
|
|
|
#include <shlobj.h>
|
|
#include <shlguid.h>
|
|
|
|
#include <gdk/gdk.h>
|
|
#include <glib/gstdio.h>
|
|
|
|
typedef struct _GdkDragContextPrivateWin32 GdkDragContextPrivateWin32;
|
|
|
|
typedef enum {
|
|
GDK_DRAG_STATUS_DRAG,
|
|
GDK_DRAG_STATUS_MOTION_WAIT,
|
|
GDK_DRAG_STATUS_ACTION_WAIT,
|
|
GDK_DRAG_STATUS_DROP
|
|
} GdkDragStatus;
|
|
|
|
/* Structure that holds information about a drag in progress.
|
|
* this is used on both source and destination sides.
|
|
*/
|
|
struct _GdkDragContextPrivateWin32 {
|
|
gboolean being_finalized;
|
|
gint ref_count;
|
|
IUnknown *iface;
|
|
DWORD last_key_state;
|
|
POINT last_pt; /* Coordinates from last event */
|
|
guint drag_status : 4; /* Current status of drag */
|
|
guint drop_failed : 1; /* Whether the drop was unsuccessful */
|
|
};
|
|
|
|
#define PRIVATE_DATA(context) (GDK_WIN32_DRAG_CONTEXT (context)->windowing_data)
|
|
|
|
static GList *contexts;
|
|
static GdkDragContext *current_dest_drag = NULL;
|
|
|
|
struct _GdkWin32DragContext
|
|
{
|
|
GdkDragContext context;
|
|
|
|
GdkDragContextPrivateWin32 *windowing_data;
|
|
};
|
|
|
|
struct _GdkWin32DragContextClass
|
|
{
|
|
GdkDragContextClass parent_class;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GdkWin32DragContext, gdk_win32_drag_context, GDK_TYPE_DRAG_CONTEXT)
|
|
|
|
static gboolean use_ole2_dnd = FALSE;
|
|
|
|
static void
|
|
gdk_win32_drag_context_init (GdkWin32DragContext *dragcontext)
|
|
{
|
|
GdkDragContextPrivateWin32 *private;
|
|
|
|
private = G_TYPE_INSTANCE_GET_PRIVATE (dragcontext,
|
|
GDK_TYPE_DRAG_CONTEXT,
|
|
GdkDragContextPrivateWin32);
|
|
|
|
dragcontext->windowing_data = private;
|
|
|
|
if (!use_ole2_dnd)
|
|
{
|
|
contexts = g_list_prepend (contexts, dragcontext);
|
|
}
|
|
else
|
|
{
|
|
private->being_finalized = FALSE;
|
|
private->ref_count = 1;
|
|
private->iface = NULL;
|
|
}
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_drag_context_init %p\n", dragcontext));
|
|
}
|
|
|
|
static void
|
|
gdk_win32_drag_context_finalize (GObject *object)
|
|
{
|
|
GdkDragContext *context = GDK_DRAG_CONTEXT (object);
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_drag_context_finalize %p\n", object));
|
|
|
|
g_list_free (context->targets);
|
|
|
|
if (context->source_window)
|
|
g_object_unref (context->source_window);
|
|
|
|
if (context->dest_window)
|
|
g_object_unref (context->dest_window);
|
|
|
|
if (!use_ole2_dnd)
|
|
{
|
|
contexts = g_list_remove (contexts, context);
|
|
|
|
if (context == current_dest_drag)
|
|
current_dest_drag = NULL;
|
|
}
|
|
else
|
|
{
|
|
GdkDragContextPrivateWin32 *private = PRIVATE_DATA (context);
|
|
if (private->iface)
|
|
{
|
|
private->being_finalized = TRUE;
|
|
private->iface->lpVtbl->Release (private->iface);
|
|
private->iface = NULL;
|
|
}
|
|
}
|
|
|
|
G_OBJECT_CLASS (gdk_win32_drag_context_parent_class)->finalize (object);
|
|
}
|
|
|
|
/* Drag Contexts */
|
|
|
|
GdkDragContext *
|
|
gdk_drag_context_new (void)
|
|
{
|
|
return g_object_new (GDK_TYPE_DRAG_CONTEXT, NULL);
|
|
}
|
|
|
|
static GdkDragContext *
|
|
gdk_drag_context_find (gboolean is_source,
|
|
GdkWindow *source,
|
|
GdkWindow *dest)
|
|
{
|
|
GList *tmp_list = contexts;
|
|
GdkDragContext *context;
|
|
GdkDragContextPrivateWin32 *private;
|
|
|
|
while (tmp_list)
|
|
{
|
|
context = (GdkDragContext *)tmp_list->data;
|
|
private = PRIVATE_DATA (context);
|
|
|
|
if ((!context->is_source == !is_source) &&
|
|
((source == NULL) || (context->source_window && (context->source_window == source))) &&
|
|
((dest == NULL) || (context->dest_window && (context->dest_window == dest))))
|
|
return context;
|
|
|
|
tmp_list = tmp_list->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#define PRINT_GUID(guid) \
|
|
g_print ("%.08lx-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
|
|
((gulong *) guid)[0], \
|
|
((gushort *) guid)[2], \
|
|
((gushort *) guid)[3], \
|
|
((guchar *) guid)[8], \
|
|
((guchar *) guid)[9], \
|
|
((guchar *) guid)[10], \
|
|
((guchar *) guid)[11], \
|
|
((guchar *) guid)[12], \
|
|
((guchar *) guid)[13], \
|
|
((guchar *) guid)[14], \
|
|
((guchar *) guid)[15]);
|
|
|
|
|
|
static FORMATETC *formats;
|
|
static int nformats;
|
|
|
|
typedef struct {
|
|
IDropTarget idt;
|
|
GdkDragContext *context;
|
|
} target_drag_context;
|
|
|
|
typedef struct {
|
|
IDropSource ids;
|
|
GdkDragContext *context;
|
|
} source_drag_context;
|
|
|
|
typedef struct {
|
|
IDataObject ido;
|
|
int ref_count;
|
|
GdkDragContext *context;
|
|
} data_object;
|
|
|
|
typedef struct {
|
|
IEnumFORMATETC ief;
|
|
int ref_count;
|
|
int ix;
|
|
} enum_formats;
|
|
|
|
static source_drag_context *pending_src_context = NULL;
|
|
static IDataObject *dnd_data = NULL;
|
|
|
|
static enum_formats *enum_formats_new (void);
|
|
|
|
/* map windows -> target drag contexts. The table
|
|
* owns a ref to both objects.
|
|
*/
|
|
static GHashTable* target_ctx_for_window = NULL;
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
idroptarget_addref (LPDROPTARGET This)
|
|
{
|
|
target_drag_context *ctx = (target_drag_context *) This;
|
|
GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
|
|
int ref_count = ++private->ref_count;
|
|
|
|
GDK_NOTE (DND, g_print ("idroptarget_addref %p %d\n", This, ref_count));
|
|
g_object_ref (G_OBJECT (ctx->context));
|
|
|
|
return ref_count;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idroptarget_queryinterface (LPDROPTARGET This,
|
|
REFIID riid,
|
|
LPVOID *ppvObject)
|
|
{
|
|
GDK_NOTE (DND, {
|
|
g_print ("idroptarget_queryinterface %p ", This);
|
|
PRINT_GUID (riid);
|
|
});
|
|
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualGUID (riid, &IID_IUnknown))
|
|
{
|
|
GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
|
|
idroptarget_addref (This);
|
|
*ppvObject = This;
|
|
return S_OK;
|
|
}
|
|
else if (IsEqualGUID (riid, &IID_IDropTarget))
|
|
{
|
|
GDK_NOTE (DND, g_print ("...IDropTarget S_OK\n"));
|
|
idroptarget_addref (This);
|
|
*ppvObject = This;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
idroptarget_release (LPDROPTARGET This)
|
|
{
|
|
target_drag_context *ctx = (target_drag_context *) This;
|
|
GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
|
|
int ref_count = --private->ref_count;
|
|
|
|
GDK_NOTE (DND, g_print ("idroptarget_release %p %d\n", This, ref_count));
|
|
|
|
if (!private->being_finalized)
|
|
g_object_unref (G_OBJECT (ctx->context));
|
|
|
|
if (ref_count == 0)
|
|
g_free (This);
|
|
|
|
return ref_count;
|
|
}
|
|
|
|
#if 0
|
|
|
|
static GdkAtom
|
|
cf_to_atom (CLIPFORMAT cf)
|
|
{
|
|
switch (cf)
|
|
{
|
|
case CF_UNICODETEXT:
|
|
return _utf8_string;
|
|
case CF_HDROP:
|
|
return _text_uri_list;
|
|
case CF_DIB:
|
|
return _image_bmp;
|
|
}
|
|
|
|
if (cf == _cf_url)
|
|
return _text_uri_list;
|
|
|
|
if (cf == _cf_html_format || cf == _cf_text_html)
|
|
return _text_html;
|
|
|
|
return GDK_NONE;
|
|
}
|
|
|
|
#endif
|
|
|
|
static GdkDragAction
|
|
get_suggested_action (DWORD grfKeyState)
|
|
{
|
|
/* This is the yucky Windows standard: Force link action if both
|
|
* Control and Alt are down, copy if Control is down alone, move if
|
|
* Alt is down alone, or use default of move within the app or copy
|
|
* when origin of the drag is in another app.
|
|
*/
|
|
if (grfKeyState & MK_CONTROL && grfKeyState & MK_SHIFT)
|
|
return GDK_ACTION_LINK; /* Link action not supported */
|
|
else if (grfKeyState & MK_CONTROL)
|
|
return GDK_ACTION_COPY;
|
|
else if (grfKeyState & MK_ALT)
|
|
return GDK_ACTION_MOVE;
|
|
#if 0 /* Default is always copy for now */
|
|
else if (_dnd_source_state == GDK_WIN32_DND_DRAGGING)
|
|
return GDK_ACTION_MOVE;
|
|
#endif
|
|
else
|
|
return GDK_ACTION_COPY;
|
|
/* Any way to determine when to add in DROPEFFECT_SCROLL? */
|
|
}
|
|
|
|
/* Process pending events -- we don't want to service non-GUI events
|
|
* forever so do one iteration and then do more only if there's a
|
|
* pending GDK event.
|
|
*/
|
|
static void
|
|
process_pending_events ()
|
|
{
|
|
g_main_context_iteration (NULL, FALSE);
|
|
while (_gdk_event_queue_find_first (_gdk_display))
|
|
g_main_context_iteration (NULL, FALSE);
|
|
}
|
|
|
|
static DWORD
|
|
drop_effect_for_action (GdkDragAction action)
|
|
{
|
|
switch (action)
|
|
{
|
|
case GDK_ACTION_MOVE:
|
|
return DROPEFFECT_MOVE;
|
|
case GDK_ACTION_LINK:
|
|
return DROPEFFECT_LINK;
|
|
case GDK_ACTION_COPY:
|
|
return DROPEFFECT_COPY;
|
|
default:
|
|
return DROPEFFECT_NONE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dnd_event_put (GdkEventType type,
|
|
GdkDragContext *context,
|
|
const POINTL pt,
|
|
gboolean to_dest_window)
|
|
{
|
|
GdkEvent *e;
|
|
|
|
e = gdk_event_new (type);
|
|
|
|
if (to_dest_window)
|
|
e->dnd.window = context->dest_window;
|
|
else
|
|
e->dnd.window = context->source_window;
|
|
e->dnd.send_event = FALSE;
|
|
e->dnd.context = g_object_ref (context);
|
|
e->dnd.time = GDK_CURRENT_TIME;
|
|
e->dnd.x_root = pt.x + _gdk_offset_x;
|
|
e->dnd.y_root = pt.x + _gdk_offset_y;
|
|
|
|
if (e->dnd.window != NULL)
|
|
g_object_ref (e->dnd.window);
|
|
|
|
gdk_event_set_device (e, gdk_drag_context_get_device (context));
|
|
|
|
GDK_NOTE (EVENTS, _gdk_win32_print_event (e));
|
|
gdk_event_put (e);
|
|
gdk_event_free (e);
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idroptarget_dragenter (LPDROPTARGET This,
|
|
LPDATAOBJECT pDataObj,
|
|
DWORD grfKeyState,
|
|
POINTL pt,
|
|
LPDWORD pdwEffect)
|
|
{
|
|
target_drag_context *ctx = (target_drag_context *) This;
|
|
|
|
GDK_NOTE (DND, g_print ("idroptarget_dragenter %p S_OK\n", This));
|
|
|
|
ctx->context->suggested_action = get_suggested_action (grfKeyState);
|
|
dnd_event_put (GDK_DRAG_ENTER, ctx->context, pt, TRUE);
|
|
process_pending_events ();
|
|
*pdwEffect = drop_effect_for_action (ctx->context->action);
|
|
|
|
/* Assume that target can accept the data: In fact it may fail but
|
|
* we are not really set up to query the target!
|
|
*/
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idroptarget_dragover (LPDROPTARGET This,
|
|
DWORD grfKeyState,
|
|
POINTL pt,
|
|
LPDWORD pdwEffect)
|
|
{
|
|
target_drag_context *ctx = (target_drag_context *) This;
|
|
|
|
GDK_NOTE (DND, g_print ("idroptarget_dragover %p S_OK\n", This));
|
|
|
|
ctx->context->suggested_action = get_suggested_action (grfKeyState);
|
|
dnd_event_put (GDK_DRAG_MOTION, ctx->context, pt, TRUE);
|
|
process_pending_events ();
|
|
*pdwEffect = drop_effect_for_action (ctx->context->action);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idroptarget_dragleave (LPDROPTARGET This)
|
|
{
|
|
target_drag_context *ctx = (target_drag_context *) This;
|
|
POINTL pt = { 0, 0 };
|
|
|
|
GDK_NOTE (DND, g_print ("idroptarget_dragleave %p S_OK\n", This));
|
|
|
|
dnd_event_put (GDK_DRAG_LEAVE, ctx->context, pt, TRUE);
|
|
process_pending_events ();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idroptarget_drop (LPDROPTARGET This,
|
|
LPDATAOBJECT pDataObj,
|
|
DWORD grfKeyState,
|
|
POINTL pt,
|
|
LPDWORD pdwEffect)
|
|
{
|
|
target_drag_context *ctx = (target_drag_context *) This;
|
|
|
|
GDK_NOTE (DND, g_print ("idroptarget_drop %p ", This));
|
|
|
|
if (pDataObj == NULL)
|
|
{
|
|
GDK_NOTE (DND, g_print ("E_POINTER\n"));
|
|
return E_POINTER;
|
|
}
|
|
|
|
dnd_data = pDataObj;
|
|
|
|
ctx->context->suggested_action = get_suggested_action (grfKeyState);
|
|
dnd_event_put (GDK_DROP_START, ctx->context, pt, TRUE);
|
|
process_pending_events ();
|
|
|
|
dnd_data = NULL;
|
|
|
|
/* Notify OLE of copy or move */
|
|
if (_dnd_target_state != GDK_WIN32_DND_DROPPED)
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
else
|
|
*pdwEffect = drop_effect_for_action (ctx->context->action);
|
|
|
|
GDK_NOTE (DND, g_print ("S_OK\n"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
idropsource_addref (LPDROPSOURCE This)
|
|
{
|
|
source_drag_context *ctx = (source_drag_context *) This;
|
|
GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
|
|
int ref_count = ++private->ref_count;
|
|
|
|
GDK_NOTE (DND, g_print ("idropsource_addref %p %d\n", This, ref_count));
|
|
g_object_ref (G_OBJECT (ctx->context));
|
|
|
|
return ref_count;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idropsource_queryinterface (LPDROPSOURCE This,
|
|
REFIID riid,
|
|
LPVOID *ppvObject)
|
|
{
|
|
GDK_NOTE (DND, {
|
|
g_print ("idropsource_queryinterface %p ", This);
|
|
PRINT_GUID (riid);
|
|
});
|
|
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualGUID (riid, &IID_IUnknown))
|
|
{
|
|
GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
|
|
idropsource_addref (This);
|
|
*ppvObject = This;
|
|
return S_OK;
|
|
}
|
|
else if (IsEqualGUID (riid, &IID_IDropSource))
|
|
{
|
|
GDK_NOTE (DND, g_print ("...IDropSource S_OK\n"));
|
|
idropsource_addref (This);
|
|
*ppvObject = This;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
idropsource_release (LPDROPSOURCE This)
|
|
{
|
|
source_drag_context *ctx = (source_drag_context *) This;
|
|
GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx->context);
|
|
int ref_count = --private->ref_count;
|
|
|
|
GDK_NOTE (DND, g_print ("idropsource_release %p %d\n", This, ref_count));
|
|
|
|
if (!private->being_finalized)
|
|
g_object_unref (G_OBJECT (ctx->context));
|
|
|
|
if (ref_count == 0)
|
|
g_free (This);
|
|
|
|
return ref_count;
|
|
}
|
|
|
|
/* Emit GDK events for any changes in mouse events or control key
|
|
* state since the last recorded state. Return true if any events
|
|
* have been emitted and false otherwise.
|
|
*/
|
|
static gboolean
|
|
send_change_events (GdkDragContext *ctx,
|
|
DWORD key_state,
|
|
gboolean esc_pressed)
|
|
{
|
|
GdkDragContextPrivateWin32 *private = PRIVATE_DATA (ctx);
|
|
POINT pt;
|
|
gboolean changed = FALSE;
|
|
HWND hwnd = GDK_WINDOW_HWND (ctx->source_window);
|
|
LPARAM lparam;
|
|
WPARAM wparam;
|
|
|
|
if (!API_CALL (GetCursorPos, (&pt)))
|
|
return FALSE;
|
|
|
|
if (!API_CALL (ScreenToClient, (hwnd, &pt)))
|
|
return FALSE;
|
|
|
|
if (pt.x != private->last_pt.x || pt.y != private->last_pt.y ||
|
|
key_state != private->last_key_state)
|
|
{
|
|
lparam = MAKELPARAM (pt.x, pt.y);
|
|
wparam = key_state;
|
|
if (pt.x != private->last_pt.x || pt.y != private->last_pt.y)
|
|
{
|
|
GDK_NOTE (DND, g_print ("Sending WM_MOUSEMOVE (%ld,%ld)\n", pt.x, pt.y));
|
|
SendMessage (hwnd, WM_MOUSEMOVE, wparam, lparam);
|
|
}
|
|
|
|
if ((key_state & MK_LBUTTON) != (private->last_key_state & MK_LBUTTON))
|
|
{
|
|
if (key_state & MK_LBUTTON)
|
|
SendMessage (hwnd, WM_LBUTTONDOWN, wparam, lparam);
|
|
else
|
|
SendMessage (hwnd, WM_LBUTTONUP, wparam, lparam);
|
|
}
|
|
if ((key_state & MK_MBUTTON) != (private->last_key_state & MK_MBUTTON))
|
|
{
|
|
if (key_state & MK_MBUTTON)
|
|
SendMessage (hwnd, WM_MBUTTONDOWN, wparam, lparam);
|
|
else
|
|
SendMessage (hwnd, WM_MBUTTONUP, wparam, lparam);
|
|
}
|
|
if ((key_state & MK_RBUTTON) != (private->last_key_state & MK_RBUTTON))
|
|
{
|
|
if (key_state & MK_RBUTTON)
|
|
SendMessage (hwnd, WM_RBUTTONDOWN, wparam, lparam);
|
|
else
|
|
SendMessage (hwnd, WM_RBUTTONUP, wparam, lparam);
|
|
}
|
|
if ((key_state & MK_CONTROL) != (private->last_key_state & MK_CONTROL))
|
|
{
|
|
if (key_state & MK_CONTROL)
|
|
SendMessage (hwnd, WM_KEYDOWN, VK_CONTROL, 0);
|
|
else
|
|
SendMessage (hwnd, WM_KEYUP, VK_CONTROL, 0);
|
|
}
|
|
if ((key_state & MK_SHIFT) != (private->last_key_state & MK_SHIFT))
|
|
{
|
|
if (key_state & MK_CONTROL)
|
|
SendMessage (hwnd, WM_KEYDOWN, VK_SHIFT, 0);
|
|
else
|
|
SendMessage (hwnd, WM_KEYUP, VK_SHIFT, 0);
|
|
}
|
|
|
|
changed = TRUE;
|
|
private->last_key_state = key_state;
|
|
private->last_pt = pt;
|
|
}
|
|
|
|
if (esc_pressed)
|
|
{
|
|
GDK_NOTE (DND, g_print ("Sending a escape key down message to %p\n", hwnd));
|
|
SendMessage (hwnd, WM_KEYDOWN, VK_ESCAPE, 0);
|
|
changed = TRUE;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idropsource_querycontinuedrag (LPDROPSOURCE This,
|
|
BOOL fEscapePressed,
|
|
DWORD grfKeyState)
|
|
{
|
|
source_drag_context *ctx = (source_drag_context *) This;
|
|
|
|
GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p ", This));
|
|
|
|
if (send_change_events (ctx->context, grfKeyState, fEscapePressed))
|
|
process_pending_events ();
|
|
|
|
if (_dnd_source_state == GDK_WIN32_DND_DROPPED)
|
|
{
|
|
GDK_NOTE (DND, g_print ("DRAGDROP_S_DROP\n"));
|
|
return DRAGDROP_S_DROP;
|
|
}
|
|
else if (_dnd_source_state == GDK_WIN32_DND_NONE)
|
|
{
|
|
GDK_NOTE (DND, g_print ("DRAGDROP_S_CANCEL\n"));
|
|
return DRAGDROP_S_CANCEL;
|
|
}
|
|
else
|
|
{
|
|
GDK_NOTE (DND, g_print ("S_OK\n"));
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idropsource_givefeedback (LPDROPSOURCE This,
|
|
DWORD dwEffect)
|
|
{
|
|
source_drag_context *ctx = (source_drag_context *) This;
|
|
GdkDragAction suggested_action;
|
|
|
|
GDK_NOTE (DND, g_print ("idropsource_givefeedback %p DRAGDROP_S_USEDEFAULTCURSORS\n", This));
|
|
|
|
if (dwEffect == DROPEFFECT_MOVE)
|
|
suggested_action = GDK_ACTION_MOVE;
|
|
else
|
|
suggested_action = GDK_ACTION_COPY;
|
|
ctx->context->action = suggested_action;
|
|
|
|
if (dwEffect == DROPEFFECT_NONE)
|
|
{
|
|
if (ctx->context->dest_window != NULL)
|
|
{
|
|
g_object_unref (ctx->context->dest_window);
|
|
ctx->context->dest_window = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ctx->context->dest_window == NULL)
|
|
ctx->context->dest_window = g_object_ref (_gdk_root);
|
|
}
|
|
|
|
return DRAGDROP_S_USEDEFAULTCURSORS;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
idataobject_addref (LPDATAOBJECT This)
|
|
{
|
|
data_object *dobj = (data_object *) This;
|
|
int ref_count = ++dobj->ref_count;
|
|
|
|
GDK_NOTE (DND, g_print ("idataobject_addref %p %d\n", This, ref_count));
|
|
|
|
return ref_count;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idataobject_queryinterface (LPDATAOBJECT This,
|
|
REFIID riid,
|
|
LPVOID *ppvObject)
|
|
{
|
|
GDK_NOTE (DND, {
|
|
g_print ("idataobject_queryinterface %p ", This);
|
|
PRINT_GUID (riid);
|
|
});
|
|
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualGUID (riid, &IID_IUnknown))
|
|
{
|
|
GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
|
|
idataobject_addref (This);
|
|
*ppvObject = This;
|
|
return S_OK;
|
|
}
|
|
else if (IsEqualGUID (riid, &IID_IDataObject))
|
|
{
|
|
GDK_NOTE (DND, g_print ("...IDataObject S_OK\n"));
|
|
idataobject_addref (This);
|
|
*ppvObject = This;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
idataobject_release (LPDATAOBJECT This)
|
|
{
|
|
data_object *dobj = (data_object *) This;
|
|
int ref_count = --dobj->ref_count;
|
|
|
|
GDK_NOTE (DND, g_print ("idataobject_release %p %d\n", This, ref_count));
|
|
|
|
if (ref_count == 0)
|
|
g_free (This);
|
|
|
|
return ref_count;
|
|
}
|
|
|
|
static HRESULT
|
|
query (LPDATAOBJECT This,
|
|
LPFORMATETC pFormatEtc)
|
|
{
|
|
int i;
|
|
|
|
if (!pFormatEtc)
|
|
return DV_E_FORMATETC;
|
|
|
|
if (pFormatEtc->lindex != -1)
|
|
return DV_E_LINDEX;
|
|
|
|
if ((pFormatEtc->tymed & TYMED_HGLOBAL) == 0)
|
|
return DV_E_TYMED;
|
|
|
|
if ((pFormatEtc->dwAspect & DVASPECT_CONTENT) == 0)
|
|
return DV_E_DVASPECT;
|
|
|
|
for (i = 0; i < nformats; i++)
|
|
if (pFormatEtc->cfFormat == formats[i].cfFormat)
|
|
return S_OK;
|
|
|
|
return DV_E_FORMATETC;
|
|
}
|
|
|
|
static FORMATETC *active_pFormatEtc = NULL;
|
|
static STGMEDIUM *active_pMedium = NULL;
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idataobject_getdata (LPDATAOBJECT This,
|
|
LPFORMATETC pFormatEtc,
|
|
LPSTGMEDIUM pMedium)
|
|
{
|
|
data_object *ctx = (data_object *) This;
|
|
GdkAtom target;
|
|
HRESULT hr;
|
|
GdkEvent e;
|
|
|
|
GDK_NOTE (DND, g_print ("idataobject_getdata %p %s ",
|
|
This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
|
|
|
|
/* Check whether we can provide requested format */
|
|
hr = query (This, pFormatEtc);
|
|
if (hr != S_OK)
|
|
return hr;
|
|
|
|
/* Append a GDK_SELECTION_GET event and then hope the app sets the
|
|
* property associated with the _gdk_ole2_dnd atom
|
|
*/
|
|
|
|
active_pFormatEtc = pFormatEtc;
|
|
active_pMedium = pMedium;
|
|
|
|
target = GDK_TARGET_STRING;
|
|
|
|
e.type = GDK_SELECTION_REQUEST;
|
|
e.selection.window = ctx->context->source_window;
|
|
e.selection.send_event = FALSE; /* ??? */
|
|
/* FIXME: Should really both selection and property be _gdk_ole2_dnd? */
|
|
e.selection.selection = _gdk_ole2_dnd;
|
|
/* FIXME: Target? */
|
|
e.selection.target = _utf8_string;
|
|
e.selection.property = _gdk_ole2_dnd;
|
|
e.selection.time = GDK_CURRENT_TIME;
|
|
|
|
g_object_ref (e.selection.window);
|
|
|
|
GDK_NOTE (EVENTS, _gdk_win32_print_event (&e));
|
|
gdk_event_put (&e);
|
|
process_pending_events ();
|
|
|
|
active_pFormatEtc = NULL;
|
|
active_pMedium = NULL;
|
|
|
|
if (pMedium->hGlobal == NULL) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idataobject_getdatahere (LPDATAOBJECT This,
|
|
LPFORMATETC pFormatEtc,
|
|
LPSTGMEDIUM pMedium)
|
|
{
|
|
GDK_NOTE (DND, g_print ("idataobject_getdatahere %p %s E_UNEXPECTED\n",
|
|
This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
|
|
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idataobject_querygetdata (LPDATAOBJECT This,
|
|
LPFORMATETC pFormatEtc)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = query (This, pFormatEtc);
|
|
|
|
#define CASE(x) case x: g_print (#x)
|
|
GDK_NOTE (DND, {
|
|
g_print ("idataobject_querygetdata %p %s \n",
|
|
This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat));
|
|
switch (hr)
|
|
{
|
|
CASE (DV_E_FORMATETC);
|
|
CASE (DV_E_LINDEX);
|
|
CASE (DV_E_TYMED);
|
|
CASE (DV_E_DVASPECT);
|
|
CASE (S_OK);
|
|
default: g_print ("%#lx", hr);
|
|
}
|
|
});
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idataobject_getcanonicalformatetc (LPDATAOBJECT This,
|
|
LPFORMATETC pFormatEtcIn,
|
|
LPFORMATETC pFormatEtcOut)
|
|
{
|
|
GDK_NOTE (DND, g_print ("idataobject_getcanonicalformatetc %p E_UNEXPECTED\n", This));
|
|
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idataobject_setdata (LPDATAOBJECT This,
|
|
LPFORMATETC pFormatEtc,
|
|
LPSTGMEDIUM pMedium,
|
|
BOOL fRelease)
|
|
{
|
|
GDK_NOTE (DND, g_print ("idataobject_setdata %p %s E_UNEXPECTED\n",
|
|
This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
|
|
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idataobject_enumformatetc (LPDATAOBJECT This,
|
|
DWORD dwDirection,
|
|
LPENUMFORMATETC *ppEnumFormatEtc)
|
|
{
|
|
GDK_NOTE (DND, g_print ("idataobject_enumformatetc %p ", This));
|
|
|
|
if (dwDirection != DATADIR_GET)
|
|
{
|
|
GDK_NOTE (DND, g_print ("E_NOTIMPL\n"));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
*ppEnumFormatEtc = &enum_formats_new ()->ief;
|
|
|
|
GDK_NOTE (DND, g_print ("%p S_OK\n", *ppEnumFormatEtc));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idataobject_dadvise (LPDATAOBJECT This,
|
|
LPFORMATETC pFormatetc,
|
|
DWORD advf,
|
|
LPADVISESINK pAdvSink,
|
|
DWORD *pdwConnection)
|
|
{
|
|
GDK_NOTE (DND, g_print ("idataobject_dadvise %p E_NOTIMPL\n", This));
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idataobject_dunadvise (LPDATAOBJECT This,
|
|
DWORD dwConnection)
|
|
{
|
|
GDK_NOTE (DND, g_print ("idataobject_dunadvise %p E_NOTIMPL\n", This));
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
idataobject_enumdadvise (LPDATAOBJECT This,
|
|
LPENUMSTATDATA *ppenumAdvise)
|
|
{
|
|
GDK_NOTE (DND, g_print ("idataobject_enumdadvise %p OLE_E_ADVISENOTSUPPORTED\n", This));
|
|
|
|
return OLE_E_ADVISENOTSUPPORTED;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
ienumformatetc_addref (LPENUMFORMATETC This)
|
|
{
|
|
enum_formats *en = (enum_formats *) This;
|
|
int ref_count = ++en->ref_count;
|
|
|
|
GDK_NOTE (DND, g_print ("ienumformatetc_addref %p %d\n", This, ref_count));
|
|
|
|
return ref_count;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
ienumformatetc_queryinterface (LPENUMFORMATETC This,
|
|
REFIID riid,
|
|
LPVOID *ppvObject)
|
|
{
|
|
GDK_NOTE (DND, {
|
|
g_print ("ienumformatetc_queryinterface %p", This);
|
|
PRINT_GUID (riid);
|
|
});
|
|
|
|
*ppvObject = NULL;
|
|
|
|
if (IsEqualGUID (riid, &IID_IUnknown))
|
|
{
|
|
GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
|
|
ienumformatetc_addref (This);
|
|
*ppvObject = This;
|
|
return S_OK;
|
|
}
|
|
else if (IsEqualGUID (riid, &IID_IEnumFORMATETC))
|
|
{
|
|
GDK_NOTE (DND, g_print ("...IEnumFORMATETC S_OK\n"));
|
|
ienumformatetc_addref (This);
|
|
*ppvObject = This;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
ienumformatetc_release (LPENUMFORMATETC This)
|
|
{
|
|
enum_formats *en = (enum_formats *) This;
|
|
int ref_count = --en->ref_count;
|
|
|
|
GDK_NOTE (DND, g_print ("ienumformatetc_release %p %d\n", This, ref_count));
|
|
|
|
if (ref_count == 0)
|
|
g_free (This);
|
|
|
|
return ref_count;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
ienumformatetc_next (LPENUMFORMATETC This,
|
|
ULONG celt,
|
|
LPFORMATETC elts,
|
|
ULONG *nelt)
|
|
{
|
|
enum_formats *en = (enum_formats *) This;
|
|
int i, n;
|
|
|
|
GDK_NOTE (DND, g_print ("ienumformatetc_next %p %d %ld ", This, en->ix, celt));
|
|
|
|
n = 0;
|
|
for (i = 0; i < celt; i++)
|
|
{
|
|
if (en->ix >= nformats)
|
|
break;
|
|
elts[i] = formats[en->ix++];
|
|
n++;
|
|
}
|
|
|
|
if (nelt != NULL)
|
|
*nelt = n;
|
|
|
|
GDK_NOTE (DND, g_print ("%s\n", (n == celt) ? "S_OK" : "S_FALSE"));
|
|
|
|
if (n == celt)
|
|
return S_OK;
|
|
else
|
|
return S_FALSE;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
ienumformatetc_skip (LPENUMFORMATETC This,
|
|
ULONG celt)
|
|
{
|
|
enum_formats *en = (enum_formats *) This;
|
|
|
|
GDK_NOTE (DND, g_print ("ienumformatetc_skip %p %d %ld S_OK\n", This, en->ix, celt));
|
|
|
|
en->ix += celt;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
ienumformatetc_reset (LPENUMFORMATETC This)
|
|
{
|
|
enum_formats *en = (enum_formats *) This;
|
|
|
|
GDK_NOTE (DND, g_print ("ienumformatetc_reset %p S_OK\n", This));
|
|
|
|
en->ix = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
ienumformatetc_clone (LPENUMFORMATETC This,
|
|
LPENUMFORMATETC *ppEnumFormatEtc)
|
|
{
|
|
enum_formats *en = (enum_formats *) This;
|
|
enum_formats *new;
|
|
|
|
GDK_NOTE (DND, g_print ("ienumformatetc_clone %p S_OK\n", This));
|
|
|
|
new = enum_formats_new ();
|
|
|
|
new->ix = en->ix;
|
|
|
|
*ppEnumFormatEtc = &new->ief;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static IDropTargetVtbl idt_vtbl = {
|
|
idroptarget_queryinterface,
|
|
idroptarget_addref,
|
|
idroptarget_release,
|
|
idroptarget_dragenter,
|
|
idroptarget_dragover,
|
|
idroptarget_dragleave,
|
|
idroptarget_drop
|
|
};
|
|
|
|
static IDropSourceVtbl ids_vtbl = {
|
|
idropsource_queryinterface,
|
|
idropsource_addref,
|
|
idropsource_release,
|
|
idropsource_querycontinuedrag,
|
|
idropsource_givefeedback
|
|
};
|
|
|
|
static IDataObjectVtbl ido_vtbl = {
|
|
idataobject_queryinterface,
|
|
idataobject_addref,
|
|
idataobject_release,
|
|
idataobject_getdata,
|
|
idataobject_getdatahere,
|
|
idataobject_querygetdata,
|
|
idataobject_getcanonicalformatetc,
|
|
idataobject_setdata,
|
|
idataobject_enumformatetc,
|
|
idataobject_dadvise,
|
|
idataobject_dunadvise,
|
|
idataobject_enumdadvise
|
|
};
|
|
|
|
static IEnumFORMATETCVtbl ief_vtbl = {
|
|
ienumformatetc_queryinterface,
|
|
ienumformatetc_addref,
|
|
ienumformatetc_release,
|
|
ienumformatetc_next,
|
|
ienumformatetc_skip,
|
|
ienumformatetc_reset,
|
|
ienumformatetc_clone
|
|
};
|
|
|
|
|
|
static target_drag_context *
|
|
target_context_new (GdkWindow *window)
|
|
{
|
|
target_drag_context *result;
|
|
GdkDragContextPrivateWin32 *private;
|
|
GdkDevice *device;
|
|
GdkDeviceManager *device_manager;
|
|
|
|
result = g_new0 (target_drag_context, 1);
|
|
|
|
result->idt.lpVtbl = &idt_vtbl;
|
|
|
|
result->context = gdk_drag_context_new ();
|
|
result->context->protocol = GDK_DRAG_PROTO_OLE2;
|
|
result->context->is_source = FALSE;
|
|
|
|
device_manager = gdk_display_get_device_manager (_gdk_display);
|
|
device = gdk_device_manager_get_client_pointer (device_manager);
|
|
gdk_drag_context_set_device (result->context, device);
|
|
|
|
result->context->source_window = NULL;
|
|
|
|
result->context->dest_window = window;
|
|
g_object_ref (window);
|
|
|
|
/* FIXME: result->context->targets? */
|
|
|
|
result->context->actions = GDK_ACTION_DEFAULT | GDK_ACTION_COPY | GDK_ACTION_MOVE;
|
|
result->context->suggested_action = GDK_ACTION_MOVE;
|
|
result->context->action = GDK_ACTION_MOVE;
|
|
|
|
private = PRIVATE_DATA(result->context);
|
|
private->iface = (IUnknown *) &result->idt;
|
|
idroptarget_addref (&result->idt);
|
|
|
|
GDK_NOTE (DND, g_print ("target_context_new: %p\n", result));
|
|
|
|
return result;
|
|
}
|
|
|
|
static source_drag_context *
|
|
source_context_new (GdkWindow *window,
|
|
GList *targets)
|
|
{
|
|
source_drag_context *result;
|
|
GdkDragContextPrivateWin32 *private;
|
|
GdkDevice *device;
|
|
GdkDeviceManager *device_manager;
|
|
|
|
result = g_new0 (source_drag_context, 1);
|
|
|
|
result->ids.lpVtbl = &ids_vtbl;
|
|
|
|
result->context = gdk_drag_context_new ();
|
|
result->context->protocol = GDK_DRAG_PROTO_OLE2;
|
|
result->context->is_source = TRUE;
|
|
|
|
device_manager = gdk_display_get_device_manager (_gdk_display);
|
|
device = gdk_device_manager_get_client_pointer (device_manager);
|
|
gdk_drag_context_set_device (result->context, device);
|
|
|
|
result->context->source_window = window;
|
|
g_object_ref (window);
|
|
|
|
result->context->dest_window = NULL;
|
|
result->context->targets = g_list_copy (targets);
|
|
|
|
private = PRIVATE_DATA(result->context);
|
|
private->iface = (IUnknown *) &result->ids;
|
|
idropsource_addref (&result->ids);
|
|
|
|
GDK_NOTE (DND, g_print ("source_context_new: %p\n", result));
|
|
|
|
return result;
|
|
}
|
|
|
|
static data_object *
|
|
data_object_new (GdkDragContext *context)
|
|
{
|
|
data_object *result;
|
|
|
|
result = g_new0 (data_object, 1);
|
|
|
|
result->ido.lpVtbl = &ido_vtbl;
|
|
result->ref_count = 1;
|
|
result->context = context;
|
|
|
|
GDK_NOTE (DND, g_print ("data_object_new: %p\n", result));
|
|
|
|
return result;
|
|
}
|
|
|
|
static enum_formats *
|
|
enum_formats_new (void)
|
|
{
|
|
enum_formats *result;
|
|
|
|
result = g_new0 (enum_formats, 1);
|
|
|
|
result->ief.lpVtbl = &ief_vtbl;
|
|
result->ref_count = 1;
|
|
result->ix = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
_gdk_win32_ole2_dnd_property_change (GdkAtom type,
|
|
gint format,
|
|
const guchar *data,
|
|
gint nelements)
|
|
{
|
|
if (use_ole2_dnd)
|
|
{
|
|
HGLOBAL hdata = NULL;
|
|
|
|
if (active_pFormatEtc == NULL || active_pMedium == NULL)
|
|
return;
|
|
|
|
/* Set up the data buffer for wide character text request */
|
|
if (active_pFormatEtc->cfFormat == CF_UNICODETEXT)
|
|
{
|
|
gunichar2 *wdata;
|
|
glong wlen;
|
|
|
|
wdata = g_utf8_to_utf16 ((const char *) data, -1, NULL, &wlen, NULL);
|
|
hdata = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (wlen + 1) * 2);
|
|
if (hdata)
|
|
{
|
|
wchar_t *ptr = (wchar_t *) GlobalLock(hdata);
|
|
memcpy (ptr, wdata, (wlen + 1) * 2);
|
|
GlobalUnlock(hdata);
|
|
}
|
|
g_free (wdata);
|
|
}
|
|
else
|
|
g_warning ("Only text handled for now");
|
|
|
|
/* Pack up data */
|
|
active_pMedium->tymed = TYMED_HGLOBAL;
|
|
active_pMedium->hGlobal = hdata;
|
|
active_pMedium->pUnkForRelease = 0;
|
|
}
|
|
}
|
|
|
|
/* From MS Knowledge Base article Q130698 */
|
|
|
|
static gboolean
|
|
resolve_link (HWND hWnd,
|
|
wchar_t *link,
|
|
gchar **lpszPath)
|
|
{
|
|
WIN32_FILE_ATTRIBUTE_DATA wfad;
|
|
HRESULT hr;
|
|
IShellLinkW *pslW = NULL;
|
|
IPersistFile *ppf = NULL;
|
|
|
|
/* Check if the file is empty first because IShellLink::Resolve for
|
|
* some reason succeeds with an empty file and returns an empty
|
|
* "link target". (#524151)
|
|
*/
|
|
if (!GetFileAttributesExW (link, GetFileExInfoStandard, &wfad) ||
|
|
(wfad.nFileSizeHigh == 0 && wfad.nFileSizeLow == 0))
|
|
return FALSE;
|
|
|
|
/* Assume failure to start with: */
|
|
*lpszPath = 0;
|
|
|
|
/* Call CoCreateInstance to obtain the IShellLink interface
|
|
* pointer. This call fails if CoInitialize is not called, so it is
|
|
* assumed that CoInitialize has been called.
|
|
*/
|
|
|
|
hr = CoCreateInstance (&CLSID_ShellLink,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
&IID_IShellLinkW,
|
|
(LPVOID *)&pslW);
|
|
|
|
if (SUCCEEDED (hr))
|
|
{
|
|
/* The IShellLink interface supports the IPersistFile
|
|
* interface. Get an interface pointer to it.
|
|
*/
|
|
hr = pslW->lpVtbl->QueryInterface (pslW,
|
|
&IID_IPersistFile,
|
|
(LPVOID *) &ppf);
|
|
}
|
|
|
|
if (SUCCEEDED (hr))
|
|
{
|
|
/* Load the file. */
|
|
hr = ppf->lpVtbl->Load (ppf, link, STGM_READ);
|
|
}
|
|
|
|
if (SUCCEEDED (hr))
|
|
{
|
|
/* Resolve the link by calling the Resolve()
|
|
* interface function.
|
|
*/
|
|
hr = pslW->lpVtbl->Resolve (pslW, hWnd, SLR_ANY_MATCH | SLR_NO_UI);
|
|
}
|
|
|
|
if (SUCCEEDED (hr))
|
|
{
|
|
wchar_t wtarget[MAX_PATH];
|
|
|
|
hr = pslW->lpVtbl->GetPath (pslW, wtarget, MAX_PATH, NULL, 0);
|
|
if (SUCCEEDED (hr))
|
|
*lpszPath = g_utf16_to_utf8 (wtarget, -1, NULL, NULL, NULL);
|
|
}
|
|
|
|
if (ppf)
|
|
ppf->lpVtbl->Release (ppf);
|
|
|
|
if (pslW)
|
|
pslW->lpVtbl->Release (pslW);
|
|
|
|
return SUCCEEDED (hr);
|
|
}
|
|
|
|
#if 0
|
|
|
|
/* Check for filenames like C:\Users\tml\AppData\Local\Temp\d5qtkvvs.bmp */
|
|
static gboolean
|
|
filename_looks_tempish (const char *filename)
|
|
{
|
|
char *dirname;
|
|
char *p;
|
|
const char *q;
|
|
gboolean retval = FALSE;
|
|
|
|
dirname = g_path_get_dirname (filename);
|
|
|
|
p = dirname;
|
|
q = g_get_tmp_dir ();
|
|
|
|
while (*p && *q &&
|
|
((G_IS_DIR_SEPARATOR (*p) && G_IS_DIR_SEPARATOR (*q)) ||
|
|
g_ascii_tolower (*p) == g_ascii_tolower (*q)))
|
|
p++, q++;
|
|
|
|
if (!*p && !*q)
|
|
retval = TRUE;
|
|
|
|
g_free (dirname);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static gboolean
|
|
close_it (gpointer data)
|
|
{
|
|
close (GPOINTER_TO_INT (data));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
static GdkFilterReturn
|
|
gdk_dropfiles_filter (GdkXEvent *xev,
|
|
GdkEvent *event,
|
|
gpointer data)
|
|
{
|
|
GdkDragContext *context;
|
|
GString *result;
|
|
MSG *msg = (MSG *) xev;
|
|
HANDLE hdrop;
|
|
POINT pt;
|
|
gint nfiles, i;
|
|
gchar *fileName, *linkedFile;
|
|
GdkDevice *device;
|
|
GdkDeviceManager *device_manager;
|
|
|
|
if (msg->message == WM_DROPFILES)
|
|
{
|
|
GDK_NOTE (DND, g_print ("WM_DROPFILES: %p\n", msg->hwnd));
|
|
|
|
context = gdk_drag_context_new ();
|
|
context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
|
|
context->is_source = FALSE;
|
|
|
|
device_manager = gdk_display_get_device_manager (_gdk_display);
|
|
device = gdk_device_manager_get_client_pointer (device_manager);
|
|
gdk_drag_context_set_device (context, device);
|
|
|
|
context->source_window = _gdk_root;
|
|
g_object_ref (context->source_window);
|
|
|
|
context->dest_window = event->any.window;
|
|
g_object_ref (context->dest_window);
|
|
|
|
/* WM_DROPFILES drops are always file names */
|
|
context->targets =
|
|
g_list_append (NULL, _text_uri_list);
|
|
context->actions = GDK_ACTION_COPY;
|
|
context->suggested_action = GDK_ACTION_COPY;
|
|
current_dest_drag = context;
|
|
|
|
event->dnd.type = GDK_DROP_START;
|
|
event->dnd.context = current_dest_drag;
|
|
gdk_event_set_device (event, gdk_drag_context_get_device (current_dest_drag));
|
|
|
|
hdrop = (HANDLE) msg->wParam;
|
|
DragQueryPoint (hdrop, &pt);
|
|
ClientToScreen (msg->hwnd, &pt);
|
|
|
|
event->dnd.x_root = pt.x + _gdk_offset_x;
|
|
event->dnd.y_root = pt.y + _gdk_offset_y;
|
|
event->dnd.time = _gdk_win32_get_next_tick (msg->time);
|
|
|
|
nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
|
|
|
|
result = g_string_new (NULL);
|
|
for (i = 0; i < nfiles; i++)
|
|
{
|
|
gchar *uri;
|
|
wchar_t wfn[MAX_PATH];
|
|
|
|
DragQueryFileW (hdrop, i, wfn, MAX_PATH);
|
|
fileName = g_utf16_to_utf8 (wfn, -1, NULL, NULL, NULL);
|
|
|
|
/* Resolve shortcuts */
|
|
if (resolve_link (msg->hwnd, wfn, &linkedFile))
|
|
{
|
|
uri = g_filename_to_uri (linkedFile, NULL, NULL);
|
|
if (uri != NULL)
|
|
{
|
|
g_string_append (result, uri);
|
|
GDK_NOTE (DND, g_print ("... %s link to %s: %s\n",
|
|
fileName, linkedFile, uri));
|
|
g_free (uri);
|
|
}
|
|
g_free (fileName);
|
|
fileName = linkedFile;
|
|
}
|
|
else
|
|
{
|
|
uri = g_filename_to_uri (fileName, NULL, NULL);
|
|
if (uri != NULL)
|
|
{
|
|
g_string_append (result, uri);
|
|
GDK_NOTE (DND, g_print ("... %s: %s\n", fileName, uri));
|
|
g_free (uri);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/* Awful hack to recognize temp files corresponding to
|
|
* images dragged from Firefox... Open the file right here
|
|
* so that it is less likely that Firefox manages to delete
|
|
* it before the GTK+-using app (typically GIMP) has opened
|
|
* it.
|
|
*
|
|
* Not compiled in for now, because it means images dragged
|
|
* from Firefox would stay around in the temp folder which
|
|
* is not what Firefox intended. I don't feel comfortable
|
|
* with that, both from a geenral sanity point of view, and
|
|
* from a privacy point of view. It's better to wait for
|
|
* Firefox to fix the problem, for instance by deleting the
|
|
* temp file after a longer delay, or to wait until we
|
|
* implement the OLE2_DND...
|
|
*/
|
|
if (filename_looks_tempish (fileName))
|
|
{
|
|
int fd = g_open (fileName, _O_RDONLY|_O_BINARY, 0);
|
|
if (fd == -1)
|
|
{
|
|
GDK_NOTE (DND, g_print ("Could not open %s, maybe an image dragged from Firefox that it already deleted\n", fileName));
|
|
}
|
|
else
|
|
{
|
|
GDK_NOTE (DND, g_print ("Opened %s as %d so that Firefox won't delete it\n", fileName, fd));
|
|
g_timeout_add_seconds (1, close_it, GINT_TO_POINTER (fd));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
g_free (fileName);
|
|
g_string_append (result, "\015\012");
|
|
}
|
|
_gdk_dropfiles_store (result->str);
|
|
g_string_free (result, FALSE);
|
|
|
|
DragFinish (hdrop);
|
|
|
|
return GDK_FILTER_TRANSLATE;
|
|
}
|
|
else
|
|
return GDK_FILTER_CONTINUE;
|
|
}
|
|
|
|
static void
|
|
add_format (GArray *fmts,
|
|
CLIPFORMAT cf)
|
|
{
|
|
FORMATETC fmt;
|
|
|
|
fmt.cfFormat = cf;
|
|
fmt.ptd = NULL;
|
|
fmt.dwAspect = DVASPECT_CONTENT;
|
|
fmt.lindex = -1;
|
|
fmt.tymed = TYMED_HGLOBAL;
|
|
|
|
g_array_append_val (fmts, fmt);
|
|
}
|
|
|
|
|
|
void
|
|
_gdk_dnd_init (void)
|
|
{
|
|
if (getenv ("GDK_WIN32_USE_EXPERIMENTAL_OLE2_DND"))
|
|
use_ole2_dnd = TRUE;
|
|
|
|
if (use_ole2_dnd)
|
|
{
|
|
HRESULT hr;
|
|
GArray *fmts;
|
|
|
|
hr = OleInitialize (NULL);
|
|
|
|
if (! SUCCEEDED (hr))
|
|
g_error ("OleInitialize failed");
|
|
|
|
fmts = g_array_new (FALSE, FALSE, sizeof (FORMATETC));
|
|
|
|
/* The most important presumably */
|
|
add_format (fmts, CF_UNICODETEXT);
|
|
|
|
/* Used for GTK+ internal DND, I think was the intent? Anyway, code below assumes
|
|
* this is at index 1.
|
|
*/
|
|
add_format (fmts, CF_GDIOBJFIRST);
|
|
|
|
add_format (fmts, CF_HDROP);
|
|
|
|
add_format (fmts, _cf_png);
|
|
add_format (fmts, CF_DIB);
|
|
|
|
add_format (fmts, _cf_url);
|
|
add_format (fmts, _cf_html_format);
|
|
add_format (fmts, _cf_text_html);
|
|
|
|
nformats = fmts->len;
|
|
formats = (FORMATETC*) g_array_free (fmts, FALSE);
|
|
|
|
target_ctx_for_window = g_hash_table_new (g_direct_hash, g_direct_equal);
|
|
}
|
|
}
|
|
|
|
void
|
|
_gdk_win32_dnd_exit (void)
|
|
{
|
|
if (use_ole2_dnd)
|
|
{
|
|
OleUninitialize ();
|
|
}
|
|
}
|
|
|
|
/* Source side */
|
|
|
|
static void
|
|
local_send_leave (GdkDragContext *context,
|
|
guint32 time)
|
|
{
|
|
GdkEvent *tmp_event;
|
|
|
|
GDK_NOTE (DND, g_print ("local_send_leave: context=%p current_dest_drag=%p\n",
|
|
context,
|
|
current_dest_drag));
|
|
|
|
if ((current_dest_drag != NULL) &&
|
|
(current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
|
|
(current_dest_drag->source_window == context->source_window))
|
|
{
|
|
tmp_event = gdk_event_new (GDK_DRAG_LEAVE);
|
|
|
|
tmp_event->dnd.window = g_object_ref (context->dest_window);
|
|
/* Pass ownership of context to the event */
|
|
tmp_event->dnd.send_event = FALSE;
|
|
tmp_event->dnd.context = g_object_ref (current_dest_drag);
|
|
tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
|
|
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
|
|
|
|
current_dest_drag = NULL;
|
|
|
|
GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
|
|
gdk_event_put (tmp_event);
|
|
gdk_event_free (tmp_event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
local_send_enter (GdkDragContext *context,
|
|
guint32 time)
|
|
{
|
|
GdkEvent *tmp_event;
|
|
GdkDragContextPrivateWin32 *private;
|
|
GdkDragContext *new_context;
|
|
GdkDevice *device;
|
|
GdkDeviceManager *device_manager;
|
|
|
|
GDK_NOTE (DND, g_print ("local_send_enter: context=%p current_dest_drag=%p\n",
|
|
context,
|
|
current_dest_drag));
|
|
|
|
private = PRIVATE_DATA (context);
|
|
|
|
if (current_dest_drag != NULL)
|
|
{
|
|
g_object_unref (G_OBJECT (current_dest_drag));
|
|
current_dest_drag = NULL;
|
|
}
|
|
|
|
new_context = gdk_drag_context_new ();
|
|
new_context->protocol = GDK_DRAG_PROTO_LOCAL;
|
|
new_context->is_source = FALSE;
|
|
|
|
device_manager = gdk_display_get_device_manager (_gdk_display);
|
|
device = gdk_device_manager_get_client_pointer (device_manager);
|
|
gdk_drag_context_set_device (new_context, device);
|
|
|
|
new_context->source_window = context->source_window;
|
|
g_object_ref (new_context->source_window);
|
|
|
|
new_context->dest_window = context->dest_window;
|
|
g_object_ref (new_context->dest_window);
|
|
|
|
new_context->targets = g_list_copy (context->targets);
|
|
|
|
gdk_window_set_events (new_context->source_window,
|
|
gdk_window_get_events (new_context->source_window) |
|
|
GDK_PROPERTY_CHANGE_MASK);
|
|
new_context->actions = context->actions;
|
|
|
|
tmp_event = gdk_event_new (GDK_DRAG_ENTER);
|
|
tmp_event->dnd.window = g_object_ref (context->dest_window);
|
|
tmp_event->dnd.send_event = FALSE;
|
|
tmp_event->dnd.context = g_object_ref (new_context);
|
|
tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
|
|
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
|
|
|
|
current_dest_drag = new_context;
|
|
|
|
GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
|
|
gdk_event_put (tmp_event);
|
|
gdk_event_free (tmp_event);
|
|
}
|
|
|
|
static void
|
|
local_send_motion (GdkDragContext *context,
|
|
gint x_root,
|
|
gint y_root,
|
|
GdkDragAction action,
|
|
guint32 time)
|
|
{
|
|
GdkEvent *tmp_event;
|
|
|
|
GDK_NOTE (DND, g_print ("local_send_motion: context=%p (%d,%d) current_dest_drag=%p\n",
|
|
context, x_root, y_root,
|
|
current_dest_drag));
|
|
|
|
if ((current_dest_drag != NULL) &&
|
|
(current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
|
|
(current_dest_drag->source_window == context->source_window))
|
|
{
|
|
tmp_event = gdk_event_new (GDK_DRAG_MOTION);
|
|
tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
|
|
tmp_event->dnd.send_event = FALSE;
|
|
tmp_event->dnd.context = g_object_ref (current_dest_drag);
|
|
tmp_event->dnd.time = time;
|
|
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
|
|
|
|
current_dest_drag->suggested_action = action;
|
|
|
|
tmp_event->dnd.x_root = x_root;
|
|
tmp_event->dnd.y_root = y_root;
|
|
|
|
PRIVATE_DATA (current_dest_drag)->last_pt.x = x_root - _gdk_offset_x;
|
|
PRIVATE_DATA (current_dest_drag)->last_pt.y = y_root - _gdk_offset_y;
|
|
|
|
PRIVATE_DATA (context)->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
|
|
|
|
GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
|
|
gdk_event_put (tmp_event);
|
|
gdk_event_free (tmp_event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
local_send_drop (GdkDragContext *context,
|
|
guint32 time)
|
|
{
|
|
GdkEvent *tmp_event;
|
|
|
|
GDK_NOTE (DND, g_print ("local_send_drop: context=%p current_dest_drag=%p\n",
|
|
context,
|
|
current_dest_drag));
|
|
|
|
if ((current_dest_drag != NULL) &&
|
|
(current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
|
|
(current_dest_drag->source_window == context->source_window))
|
|
{
|
|
GdkDragContextPrivateWin32 *private;
|
|
private = PRIVATE_DATA (current_dest_drag);
|
|
|
|
/* Pass ownership of context to the event */
|
|
tmp_event = gdk_event_new (GDK_DROP_START);
|
|
tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
|
|
tmp_event->dnd.send_event = FALSE;
|
|
tmp_event->dnd.context = g_object_ref (current_dest_drag);
|
|
tmp_event->dnd.time = GDK_CURRENT_TIME;
|
|
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
|
|
|
|
tmp_event->dnd.x_root = private->last_pt.x + _gdk_offset_x;
|
|
tmp_event->dnd.y_root = private->last_pt.y + _gdk_offset_y;
|
|
|
|
current_dest_drag = NULL;
|
|
|
|
GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
|
|
gdk_event_put (tmp_event);
|
|
gdk_event_free (tmp_event);
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
gdk_drag_do_leave (GdkDragContext *context,
|
|
guint32 time)
|
|
{
|
|
if (context->dest_window)
|
|
{
|
|
GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
|
|
|
|
if (!use_ole2_dnd)
|
|
{
|
|
if (context->protocol == GDK_DRAG_PROTO_LOCAL)
|
|
local_send_leave (context, time);
|
|
}
|
|
|
|
g_object_unref (context->dest_window);
|
|
context->dest_window = NULL;
|
|
}
|
|
}
|
|
|
|
GdkDragContext *
|
|
_gdk_win32_window_drag_begin (GdkWindow *window,
|
|
GdkDevice *device,
|
|
GList *targets)
|
|
{
|
|
if (!use_ole2_dnd)
|
|
{
|
|
GdkDragContext *new_context;
|
|
GdkDevice *device;
|
|
GdkDeviceManager *device_manager;
|
|
|
|
g_return_val_if_fail (window != NULL, NULL);
|
|
|
|
new_context = gdk_drag_context_new ();
|
|
|
|
device_manager = gdk_display_get_device_manager (_gdk_display);
|
|
device = gdk_device_manager_get_client_pointer (device_manager);
|
|
gdk_drag_context_set_device (new_context, device);
|
|
|
|
new_context->is_source = TRUE;
|
|
|
|
new_context->source_window = window;
|
|
g_object_ref (window);
|
|
gdk_drag_context_set_device (new_context, device);
|
|
|
|
new_context->targets = g_list_copy (targets);
|
|
new_context->actions = 0;
|
|
|
|
return new_context;
|
|
}
|
|
else
|
|
{
|
|
source_drag_context *ctx;
|
|
|
|
g_return_val_if_fail (window != NULL, NULL);
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
|
|
|
|
ctx = source_context_new (window, targets);
|
|
|
|
_dnd_source_state = GDK_WIN32_DND_PENDING;
|
|
|
|
pending_src_context = ctx;
|
|
g_object_ref (ctx->context);
|
|
|
|
return ctx->context;
|
|
}
|
|
}
|
|
|
|
void
|
|
_gdk_win32_dnd_do_dragdrop (void)
|
|
{
|
|
if (use_ole2_dnd)
|
|
{
|
|
GdkDragContext* drag_ctx;
|
|
GdkDragContextPrivateWin32 *private;
|
|
BYTE kbd_state[256];
|
|
data_object *dobj;
|
|
HRESULT hr;
|
|
DWORD dwEffect;
|
|
#if 0
|
|
HGLOBAL global;
|
|
STGMEDIUM medium;
|
|
#endif
|
|
|
|
if (pending_src_context == NULL)
|
|
return;
|
|
|
|
drag_ctx = pending_src_context->context;
|
|
private = PRIVATE_DATA (drag_ctx);
|
|
|
|
dobj = data_object_new (drag_ctx);
|
|
|
|
API_CALL (GetCursorPos, (&private->last_pt));
|
|
API_CALL (ScreenToClient, (GDK_WINDOW_HWND (drag_ctx->source_window), &private->last_pt));
|
|
private->last_key_state = 0;
|
|
API_CALL (GetKeyboardState, (kbd_state));
|
|
|
|
if (kbd_state[VK_CONTROL])
|
|
private->last_key_state |= MK_CONTROL;
|
|
if (kbd_state[VK_SHIFT])
|
|
private->last_key_state |= MK_SHIFT;
|
|
if (kbd_state[VK_LBUTTON])
|
|
private->last_key_state |= MK_LBUTTON;
|
|
if (kbd_state[VK_MBUTTON])
|
|
private->last_key_state |= MK_MBUTTON;
|
|
if (kbd_state[VK_RBUTTON])
|
|
private->last_key_state |= MK_RBUTTON;
|
|
|
|
#if 0
|
|
global = GlobalAlloc (GMEM_FIXED, sizeof (ctx));
|
|
|
|
memcpy (&global, ctx, sizeof (ctx));
|
|
|
|
medium.tymed = TYMED_HGLOBAL;
|
|
medium.hGlobal = global;
|
|
medium.pUnkForRelease = NULL;
|
|
|
|
/* FIXME I wish I remember what I was thinking of here, i.e. what
|
|
* the formats[1] signifies, i.e. the CF_GDIOBJFIRST FORMATETC?
|
|
*/
|
|
dobj->ido.lpVtbl->SetData (&dobj->ido, &formats[1], &medium, TRUE);
|
|
#endif
|
|
|
|
/* Start dragging with mainloop inside the OLE2 API. Exits only when done */
|
|
|
|
GDK_NOTE (DND, g_print ("Calling DoDragDrop\n"));
|
|
|
|
_gdk_win32_begin_modal_call ();
|
|
hr = DoDragDrop (&dobj->ido, &pending_src_context->ids,
|
|
DROPEFFECT_COPY | DROPEFFECT_MOVE,
|
|
&dwEffect);
|
|
_gdk_win32_end_modal_call ();
|
|
|
|
GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n",
|
|
(hr == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
|
|
(hr == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
|
|
(hr == E_UNEXPECTED ? "E_UNEXPECTED" :
|
|
g_strdup_printf ("%#.8lx", hr))))));
|
|
|
|
/* Delete dnd selection after successful move */
|
|
if (hr == DRAGDROP_S_DROP && dwEffect == DROPEFFECT_MOVE)
|
|
{
|
|
GdkEvent tmp_event;
|
|
|
|
tmp_event.type = GDK_SELECTION_REQUEST;
|
|
tmp_event.selection.window = drag_ctx->source_window;
|
|
tmp_event.selection.send_event = FALSE;
|
|
tmp_event.selection.selection = _gdk_ole2_dnd;
|
|
tmp_event.selection.target = _delete;
|
|
tmp_event.selection.property = _gdk_ole2_dnd; /* ??? */
|
|
tmp_event.selection.time = GDK_CURRENT_TIME; /* ??? */
|
|
g_object_ref (tmp_event.selection.window);
|
|
|
|
GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
|
|
gdk_event_put (&tmp_event);
|
|
}
|
|
|
|
#if 0
|
|
// Send a GDK_DROP_FINISHED to the source window
|
|
GetCursorPos (&pt);
|
|
ptl.x = pt.x;
|
|
ptl.y = pt.y;
|
|
if ( pending_src_context != NULL && pending_src_context->context != NULL
|
|
&& pending_src_context->context->source_window != NULL )
|
|
push_dnd_event (GDK_DROP_FINISHED, pending_src_context->context, ptl, FALSE);
|
|
#endif
|
|
|
|
dobj->ido.lpVtbl->Release (&dobj->ido);
|
|
if (pending_src_context != NULL)
|
|
{
|
|
pending_src_context->ids.lpVtbl->Release (&pending_src_context->ids);
|
|
pending_src_context = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
GdkNativeWindow
|
|
_gdk_win32_display_get_drag_protocol (GdkDisplay *display,
|
|
GdkNativeWindow xid,
|
|
GdkDragProtocol *protocol,
|
|
guint *version)
|
|
{
|
|
GdkWindow *window;
|
|
|
|
window = gdk_win32_window_lookup_for_display (display, xid);
|
|
if (window &&
|
|
gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
|
|
{
|
|
if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
|
|
{
|
|
if (use_ole2_dnd)
|
|
*protocol = GDK_DRAG_PROTO_OLE2;
|
|
else
|
|
*protocol = GDK_DRAG_PROTO_LOCAL;
|
|
|
|
/* even X11 code not always intializes it */
|
|
*version = 0;
|
|
|
|
return xid;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static GdkWindow *
|
|
gdk_win32_drag_context_find_window (GdkDragContext *context,
|
|
GdkWindow *drag_window,
|
|
GdkScreen *screen,
|
|
gint x_root,
|
|
gint y_root,
|
|
GdkDragProtocol *protocol)
|
|
{
|
|
GdkWindow *dest_window;
|
|
POINT pt;
|
|
HWND hwnd;
|
|
|
|
pt.x = x_root - _gdk_offset_x;
|
|
pt.y = y_root - _gdk_offset_y;
|
|
|
|
hwnd = WindowFromPoint (pt);
|
|
|
|
if (hwnd == NULL)
|
|
dest_window = NULL;
|
|
else
|
|
{
|
|
dest_window = gdk_win32_handle_table_lookup (hwnd);
|
|
if (dest_window)
|
|
g_object_ref (dest_window);
|
|
else
|
|
dest_window = gdk_win32_window_foreign_new_for_display (_gdk_display, hwnd);
|
|
|
|
if (use_ole2_dnd)
|
|
*protocol = GDK_DRAG_PROTO_OLE2;
|
|
else if (context->source_window)
|
|
*protocol = GDK_DRAG_PROTO_LOCAL;
|
|
else
|
|
*protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
|
|
}
|
|
|
|
GDK_NOTE (DND,
|
|
g_print ("gdk_drag_find_window: %p %+d%+d: %p: %p %s\n",
|
|
(drag_window ? GDK_WINDOW_HWND (drag_window) : NULL),
|
|
x_root, y_root,
|
|
hwnd,
|
|
(dest_window ? GDK_WINDOW_HWND (dest_window) : NULL),
|
|
_gdk_win32_drag_protocol_to_string (*protocol)));
|
|
|
|
return dest_window;
|
|
}
|
|
|
|
static gboolean
|
|
gdk_win32_drag_context_drag_motion (GdkDragContext *context,
|
|
GdkWindow *dest_window,
|
|
GdkDragProtocol protocol,
|
|
gint x_root,
|
|
gint y_root,
|
|
GdkDragAction suggested_action,
|
|
GdkDragAction possible_actions,
|
|
guint32 time)
|
|
{
|
|
GdkDragContextPrivateWin32 *private;
|
|
|
|
g_return_val_if_fail (context != NULL, FALSE);
|
|
|
|
context->actions = possible_actions;
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_drag_motion: %s suggested=%s, possible=%s\n"
|
|
" context=%p:{actions=%s,suggested=%s,action=%s}\n",
|
|
_gdk_win32_drag_protocol_to_string (protocol),
|
|
_gdk_win32_drag_action_to_string (suggested_action),
|
|
_gdk_win32_drag_action_to_string (possible_actions),
|
|
context,
|
|
_gdk_win32_drag_action_to_string (context->actions),
|
|
_gdk_win32_drag_action_to_string (context->suggested_action),
|
|
_gdk_win32_drag_action_to_string (context->action)));
|
|
|
|
private = PRIVATE_DATA (context);
|
|
|
|
if (!use_ole2_dnd)
|
|
{
|
|
if (context->dest_window == dest_window)
|
|
{
|
|
GdkDragContext *dest_context;
|
|
|
|
dest_context = gdk_drag_context_find (FALSE,
|
|
context->source_window,
|
|
dest_window);
|
|
|
|
if (dest_context)
|
|
dest_context->actions = context->actions;
|
|
|
|
context->suggested_action = suggested_action;
|
|
}
|
|
else
|
|
{
|
|
GdkEvent *tmp_event;
|
|
|
|
/* Send a leave to the last destination */
|
|
gdk_drag_do_leave (context, time);
|
|
private->drag_status = GDK_DRAG_STATUS_DRAG;
|
|
|
|
/* Check if new destination accepts drags, and which protocol */
|
|
if (dest_window)
|
|
{
|
|
context->dest_window = dest_window;
|
|
g_object_ref (context->dest_window);
|
|
context->protocol = protocol;
|
|
|
|
switch (protocol)
|
|
{
|
|
case GDK_DRAG_PROTO_LOCAL:
|
|
local_send_enter (context, time);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
context->suggested_action = suggested_action;
|
|
}
|
|
else
|
|
{
|
|
context->dest_window = NULL;
|
|
context->action = 0;
|
|
}
|
|
|
|
/* Push a status event, to let the client know that
|
|
* the drag changed
|
|
*/
|
|
tmp_event = gdk_event_new (GDK_DRAG_STATUS);
|
|
tmp_event->dnd.window = g_object_ref (context->source_window);
|
|
/* We use this to signal a synthetic status. Perhaps
|
|
* we should use an extra field...
|
|
*/
|
|
tmp_event->dnd.send_event = TRUE;
|
|
|
|
tmp_event->dnd.context = g_object_ref (context);
|
|
tmp_event->dnd.time = time;
|
|
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
|
|
|
|
GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
|
|
gdk_event_put (tmp_event);
|
|
gdk_event_free (tmp_event);
|
|
}
|
|
|
|
/* Send a drag-motion event */
|
|
|
|
private->last_pt.x = x_root - _gdk_offset_x;
|
|
private->last_pt.y = y_root - _gdk_offset_y;
|
|
|
|
if (context->dest_window)
|
|
{
|
|
if (private->drag_status == GDK_DRAG_STATUS_DRAG)
|
|
{
|
|
switch (context->protocol)
|
|
{
|
|
case GDK_DRAG_PROTO_LOCAL:
|
|
local_send_motion (context, x_root, y_root, suggested_action, time);
|
|
break;
|
|
|
|
case GDK_DRAG_PROTO_NONE:
|
|
g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GDK_NOTE (DND, g_print (" returning TRUE\n"
|
|
" context=%p:{actions=%s,suggested=%s,action=%s}\n",
|
|
context,
|
|
_gdk_win32_drag_action_to_string (context->actions),
|
|
_gdk_win32_drag_action_to_string (context->suggested_action),
|
|
_gdk_win32_drag_action_to_string (context->action)));
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
GDK_NOTE (DND, g_print (" returning FALSE\n"
|
|
" context=%p:{actions=%s,suggested=%s,action=%s}\n",
|
|
context,
|
|
_gdk_win32_drag_action_to_string (context->actions),
|
|
_gdk_win32_drag_action_to_string (context->suggested_action),
|
|
_gdk_win32_drag_action_to_string (context->action)));
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gdk_win32_drag_context_drag_drop (GdkDragContext *context,
|
|
guint32 time)
|
|
{
|
|
g_return_if_fail (context != NULL);
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_drag_drop\n"));
|
|
|
|
if (!use_ole2_dnd)
|
|
{
|
|
if (context->dest_window &&
|
|
context->protocol == GDK_DRAG_PROTO_LOCAL)
|
|
local_send_drop (context, time);
|
|
}
|
|
else
|
|
{
|
|
_dnd_source_state = GDK_WIN32_DND_DROPPED;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_win32_drag_context_drag_abort (GdkDragContext *context,
|
|
guint32 time)
|
|
{
|
|
g_return_if_fail (context != NULL);
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_drag_abort\n"));
|
|
|
|
if (use_ole2_dnd)
|
|
_dnd_source_state = GDK_WIN32_DND_NONE;
|
|
}
|
|
|
|
/* Destination side */
|
|
|
|
static void
|
|
gdk_win32_drag_context_drag_status (GdkDragContext *context,
|
|
GdkDragAction action,
|
|
guint32 time)
|
|
{
|
|
GdkDragContextPrivateWin32 *private;
|
|
GdkDragContext *src_context;
|
|
GdkEvent *tmp_event;
|
|
|
|
g_return_if_fail (context != NULL);
|
|
|
|
private = PRIVATE_DATA (context);
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_drag_status: %s\n"
|
|
" context=%p:{actions=%s,suggested=%s,action=%s}\n",
|
|
_gdk_win32_drag_action_to_string (action),
|
|
context,
|
|
_gdk_win32_drag_action_to_string (context->actions),
|
|
_gdk_win32_drag_action_to_string (context->suggested_action),
|
|
_gdk_win32_drag_action_to_string (context->action)));
|
|
|
|
context->action = action;
|
|
|
|
if (!use_ole2_dnd)
|
|
{
|
|
src_context = gdk_drag_context_find (TRUE,
|
|
context->source_window,
|
|
context->dest_window);
|
|
|
|
if (src_context)
|
|
{
|
|
GdkDragContextPrivateWin32 *private = PRIVATE_DATA (src_context);
|
|
|
|
if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
|
|
private->drag_status = GDK_DRAG_STATUS_DRAG;
|
|
|
|
tmp_event = gdk_event_new (GDK_DRAG_STATUS);
|
|
tmp_event->dnd.window = g_object_ref (context->source_window);
|
|
tmp_event->dnd.send_event = FALSE;
|
|
tmp_event->dnd.context = g_object_ref (src_context);
|
|
tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
|
|
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
|
|
|
|
if (action == GDK_ACTION_DEFAULT)
|
|
action = 0;
|
|
|
|
src_context->action = action;
|
|
|
|
GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
|
|
gdk_event_put (tmp_event);
|
|
gdk_event_free (tmp_event);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_win32_drag_context_drop_reply (GdkDragContext *context,
|
|
gboolean ok,
|
|
guint32 time)
|
|
{
|
|
g_return_if_fail (context != NULL);
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_drop_reply\n"));
|
|
|
|
if (!use_ole2_dnd)
|
|
if (context->dest_window)
|
|
{
|
|
if (context->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
|
|
_gdk_dropfiles_store (NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_win32_drag_context_drop_finish (GdkDragContext *context,
|
|
gboolean success,
|
|
guint32 time)
|
|
{
|
|
GdkDragContextPrivateWin32 *private;
|
|
GdkDragContext *src_context;
|
|
GdkEvent *tmp_event;
|
|
|
|
g_return_if_fail (context != NULL);
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_drop_finish\n"));
|
|
|
|
private = PRIVATE_DATA (context);
|
|
|
|
if (!use_ole2_dnd)
|
|
{
|
|
src_context = gdk_drag_context_find (TRUE,
|
|
context->source_window,
|
|
context->dest_window);
|
|
if (src_context)
|
|
{
|
|
tmp_event = gdk_event_new (GDK_DROP_FINISHED);
|
|
tmp_event->dnd.window = g_object_ref (src_context->source_window);
|
|
tmp_event->dnd.send_event = FALSE;
|
|
tmp_event->dnd.context = g_object_ref (src_context);
|
|
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
|
|
|
|
GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
|
|
gdk_event_put (tmp_event);
|
|
gdk_event_free (tmp_event);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gdk_drag_do_leave (context, time);
|
|
|
|
if (success)
|
|
_dnd_target_state = GDK_WIN32_DND_DROPPED;
|
|
else
|
|
_dnd_target_state = GDK_WIN32_DND_FAILED;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
|
|
static GdkFilterReturn
|
|
gdk_destroy_filter (GdkXEvent *xev,
|
|
GdkEvent *event,
|
|
gpointer data)
|
|
{
|
|
MSG *msg = (MSG *) xev;
|
|
|
|
if (msg->message == WM_DESTROY)
|
|
{
|
|
IDropTarget *idtp = (IDropTarget *) data;
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_destroy_filter: WM_DESTROY: %p\n", msg->hwnd));
|
|
#if 0
|
|
idtp->lpVtbl->Release (idtp);
|
|
#endif
|
|
RevokeDragDrop (msg->hwnd);
|
|
CoLockObjectExternal ((IUnknown*) idtp, FALSE, TRUE);
|
|
}
|
|
return GDK_FILTER_CONTINUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
void
|
|
_gdk_win32_window_register_dnd (GdkWindow *window)
|
|
{
|
|
target_drag_context *ctx;
|
|
HRESULT hr;
|
|
|
|
g_return_if_fail (window != NULL);
|
|
|
|
if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
|
|
return;
|
|
else
|
|
g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
|
|
|
|
GDK_NOTE (DND, g_print ("gdk_window_register_dnd: %p\n", GDK_WINDOW_HWND (window)));
|
|
|
|
if (!use_ole2_dnd)
|
|
{
|
|
/* We always claim to accept dropped files, but in fact we might not,
|
|
* of course. This function is called in such a way that it cannot know
|
|
* whether the window (widget) in question actually accepts files
|
|
* (in gtk, data of type text/uri-list) or not.
|
|
*/
|
|
gdk_window_add_filter (window, gdk_dropfiles_filter, NULL);
|
|
DragAcceptFiles (GDK_WINDOW_HWND (window), TRUE);
|
|
}
|
|
else
|
|
{
|
|
/* Return if window is already setup for DND. */
|
|
if (g_hash_table_lookup (target_ctx_for_window, GDK_WINDOW_HWND (window)) != NULL)
|
|
return;
|
|
|
|
/* Register for OLE2 d&d : similarly, claim to accept all supported
|
|
* data types because we cannot know from here what the window
|
|
* actually accepts.
|
|
*/
|
|
/* FIXME: This of course won't work with user-extensible data types! */
|
|
ctx = target_context_new (window);
|
|
|
|
hr = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
|
|
if (!SUCCEEDED (hr))
|
|
OTHER_API_FAILED ("CoLockObjectExternal");
|
|
else
|
|
{
|
|
hr = RegisterDragDrop (GDK_WINDOW_HWND (window), &ctx->idt);
|
|
if (hr == DRAGDROP_E_ALREADYREGISTERED)
|
|
{
|
|
g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
|
|
CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSE, FALSE);
|
|
}
|
|
else if (!SUCCEEDED (hr))
|
|
OTHER_API_FAILED ("RegisterDragDrop");
|
|
else
|
|
{
|
|
g_object_ref (window);
|
|
g_hash_table_insert (target_ctx_for_window, GDK_WINDOW_HWND (window), ctx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gdk_win32_drag_context_drop_status (GdkDragContext *context)
|
|
{
|
|
return ! PRIVATE_DATA (context)->drop_failed;
|
|
}
|
|
|
|
static GdkAtom
|
|
gdk_win32_drag_context_get_selection (GdkDragContext *context)
|
|
{
|
|
switch (context->protocol)
|
|
{
|
|
case GDK_DRAG_PROTO_LOCAL:
|
|
return _local_dnd;
|
|
case GDK_DRAG_PROTO_WIN32_DROPFILES:
|
|
return _gdk_win32_dropfiles;
|
|
case GDK_DRAG_PROTO_OLE2:
|
|
return _gdk_ole2_dnd;
|
|
default:
|
|
return GDK_NONE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_win32_drag_context_class_init (GdkWin32DragContextClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
|
|
|
|
gdk_win32_drag_context_parent_class = g_type_class_peek_parent (klass);
|
|
|
|
object_class->finalize = gdk_win32_drag_context_finalize;
|
|
|
|
context_class->find_window = gdk_win32_drag_context_find_window;
|
|
context_class->drag_status = gdk_win32_drag_context_drag_status;
|
|
context_class->drag_motion = gdk_win32_drag_context_drag_motion;
|
|
context_class->drag_abort = gdk_win32_drag_context_drag_abort;
|
|
context_class->drag_drop = gdk_win32_drag_context_drag_drop;
|
|
context_class->drop_reply = gdk_win32_drag_context_drop_reply;
|
|
context_class->drop_finish = gdk_win32_drag_context_drop_finish;
|
|
context_class->drop_status = gdk_win32_drag_context_drop_status;
|
|
context_class->get_selection = gdk_win32_drag_context_get_selection;
|
|
|
|
g_type_class_add_private (object_class, sizeof (GdkDragContextPrivateWin32));
|
|
}
|