zntg/addons/dragdrop/dragdrop_windows.c
Sander Schobers 3c89748eac Added drag & drop addon.
- Drop addon is based on WM_DROPFILES, dragdrop addon is based on the OLE IDragDrop interface and thus can registerer more interactions.
- The allg5ui implementation will try to fall back on the drop addon (because the dragdrop addon wouldn't work properly).
- Drop addon is refactored to use the same interface as the dragdrop addon.
2021-06-04 17:17:22 +02:00

177 lines
4.8 KiB
C

#include <Windows.h>
#include <oleidl.h>
#include <stdio.h>
#include "vector.h"
#include "_cgo_export.h"
#define STDULONGMETHODIMP STDMETHODIMP_(ULONG)
#define DRAGDROP_MAXFILEPATHSIZE 32767
typedef struct
{
IDropTargetVtbl *_vtbl;
long _refCount;
uint32_t _handle;
HWND _windowHandle;
} DragDropHandler;
static void screenToClient(HWND windowHandle, POINTL *point)
{
ScreenToClient(windowHandle, (LPPOINT)point);
}
static STDMETHODIMP QueryInterface(IDropTarget *this, REFIID riid, LPVOID *ppvObj)
{
// Always set out parameter to NULL, validating it first.
if (!ppvObj)
return E_INVALIDARG;
*ppvObj = NULL;
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDropTarget))
{
// Increment the reference count and return the pointer.
*ppvObj = (LPVOID)this;
((DragDropHandler *)this)->_vtbl->AddRef(this);
return NOERROR;
}
return E_NOINTERFACE;
}
static STDULONGMETHODIMP AddRef(IDropTarget *this)
{
InterlockedIncrement(&((DragDropHandler *)this)->_refCount);
return ((DragDropHandler *)this)->_refCount;
}
static STDULONGMETHODIMP Release(IDropTarget *this)
{
// Decrement the object's internal counter.
ULONG ulRefCount = InterlockedDecrement(&((DragDropHandler *)this)->_refCount);
if (0 == ((DragDropHandler *)this)->_refCount)
{
GlobalFree(this);
}
return ulRefCount;
}
static STDMETHODIMP DragEnter(IDropTarget *this, __RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect)
{
*pdwEffect = DROPEFFECT_COPY;
wchar_t filePath[DRAGDROP_MAXFILEPATHSIZE];
FORMATETC format;
format.cfFormat = CF_HDROP;
format.ptd = NULL;
format.dwAspect = DVASPECT_CONTENT;
format.lindex = -1;
format.tymed = TYMED_HGLOBAL;
STGMEDIUM medium;
HRESULT result = pDataObj->lpVtbl->GetData(pDataObj, &format, &medium);
if (result != S_OK)
return E_UNEXPECTED;
HDROP drop = (HDROP)GlobalLock(medium.hGlobal);
clearFiles(((DragDropHandler *)this)->_handle);
UINT numberOfFiles = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
for (UINT fileIndex = 0; fileIndex < numberOfFiles; fileIndex++)
{
UINT length = DragQueryFileW(drop, fileIndex, filePath, DRAGDROP_MAXFILEPATHSIZE);
addFile(((DragDropHandler *)this)->_handle, &filePath[0], length);
}
GlobalUnlock(medium.hGlobal);
if (medium.pUnkForRelease != NULL)
medium.pUnkForRelease->lpVtbl->Release(medium.pUnkForRelease);
screenToClient(((DragDropHandler *)this)->_windowHandle, &pt);
onDragEnter(((DragDropHandler *)this)->_handle, pt.x, pt.y);
return S_OK;
}
static STDMETHODIMP DragOver(IDropTarget *this, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect)
{
*pdwEffect = DROPEFFECT_COPY;
screenToClient(((DragDropHandler *)this)->_windowHandle, &pt);
onDragOver(((DragDropHandler *)this)->_handle, pt.x, pt.y);
return S_OK;
}
static STDMETHODIMP DragLeave(IDropTarget *this)
{
onDragLeave(((DragDropHandler *)this)->_handle);
return S_OK;
}
static STDMETHODIMP Drop(IDropTarget *this, __RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect)
{
*pdwEffect = DROPEFFECT_COPY;
screenToClient(((DragDropHandler *)this)->_windowHandle, &pt);
onDrop(((DragDropHandler *)this)->_handle, pt.x, pt.y);
return S_OK;
}
static const IDropTargetVtbl DragDropHandlerVtbl = {
QueryInterface,
AddRef,
Release,
DragEnter,
DragOver,
DragLeave,
Drop,
};
static DragDropHandler *newDragDropHandler(HWND windowHandle, uint32_t handle)
{
DragDropHandler *handler = malloc(sizeof(DragDropHandler));
handler->_vtbl = (IDropTargetVtbl *)&DragDropHandlerVtbl;
handler->_refCount = 0;
handler->_windowHandle = windowHandle;
handler->_handle = handle;
return handler;
}
BOOL dragDropInitialized = FALSE;
typedef vector(DragDropHandler *) DragDropHandlers;
DragDropHandlers handlers;
static void VerifyResult(CHAR *component, HRESULT result)
{
if (result == S_OK)
return;
printf("%s failed (error code: %d)\n", component, result);
}
static void initDragDrop(void)
{
if (dragDropInitialized == TRUE)
return;
HRESULT result = OleInitialize(NULL);
VerifyResult("OleInitialize", result);
vector_init(handlers);
dragDropInitialized = TRUE;
}
uint32_t RegisterHandler(void *window)
{
initDragDrop();
size_t handle = handlers.count + 1;
HWND windowHandle = (HWND)window;
DragDropHandler *handler = newDragDropHandler(windowHandle, handle);
vector_append(DragDropHandler *, handlers, handler);
DragAcceptFiles(windowHandle, TRUE);
HRESULT result = RegisterDragDrop(windowHandle, (IDropTarget *)handler);
VerifyResult("RegisterDragDrop", result);
return (uint32_t)handle;
}