|
|
@@ -21,42 +21,109 @@
|
|
|
#include <windows.h>
|
|
|
#include <windowsx.h>
|
|
|
#include <shellapi.h>
|
|
|
-
|
|
|
-#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR))
|
|
|
+#include <OleIdl.h>
|
|
|
|
|
|
namespace Atomic
|
|
|
{
|
|
|
- class UIDragDropWindows : public Object
|
|
|
- {
|
|
|
|
|
|
- OBJECT(UIDragDropWindows);
|
|
|
+ // global weakptr to dragAndDrop system as it is sometimes accessed outside of an Object method
|
|
|
+ static WeakPtr<UIDragDrop> dragAndDrop_;
|
|
|
+
|
|
|
+ // Win32 proc hook so we can catch close and unregister drag and drop
|
|
|
+ static LRESULT CALLBACK Atomic_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
|
|
+ class CDropTarget : public IDropTarget
|
|
|
+ {
|
|
|
public:
|
|
|
- /// Construct.
|
|
|
- UIDragDropWindows(Context* context);
|
|
|
- virtual ~UIDragDropWindows();
|
|
|
+ // IUnknown implementation
|
|
|
+ HRESULT __stdcall QueryInterface(REFIID iid, void ** ppvObject);
|
|
|
+ ULONG __stdcall AddRef(void);
|
|
|
+ ULONG __stdcall Release(void);
|
|
|
|
|
|
- void HandleUpdate(StringHash eventType, VariantMap& eventData);
|
|
|
+ void UpdateMousePos();
|
|
|
|
|
|
- static WNDPROC sdlWndProc_;
|
|
|
+ // IDropTarget implementation
|
|
|
+ HRESULT __stdcall DragEnter(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
|
|
|
+ HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
|
|
|
+ HRESULT __stdcall DragLeave(void);
|
|
|
+ HRESULT __stdcall Drop(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
|
|
|
|
|
|
- HWND hwnd_;
|
|
|
+ void OpenFilesFromDataObject(IDataObject * pdto);
|
|
|
+
|
|
|
+ // Constructor
|
|
|
+ CDropTarget(HWND hwnd);
|
|
|
+ ~CDropTarget();
|
|
|
|
|
|
private:
|
|
|
|
|
|
+ // internal helper function
|
|
|
+ DWORD DropEffect(DWORD grfKeyState, POINTL pt, DWORD dwAllowed);
|
|
|
+ bool QueryDataObject(IDataObject *pDataObject);
|
|
|
+
|
|
|
+ // Private member variables
|
|
|
+ LONG m_lRefCount;
|
|
|
+ HWND m_hWnd;
|
|
|
+ bool m_fAllowDrop;
|
|
|
+
|
|
|
+ IDataObject *m_pDataObject;
|
|
|
+
|
|
|
};
|
|
|
|
|
|
- WNDPROC UIDragDropWindows::sdlWndProc_ = NULL;
|
|
|
- static WeakPtr<UIDragDrop> dragAndDrop_;
|
|
|
+ CDropTarget::CDropTarget(HWND hwnd)
|
|
|
+ {
|
|
|
+ m_lRefCount = 1;
|
|
|
+ m_hWnd = hwnd;
|
|
|
+ m_fAllowDrop = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ CDropTarget::~CDropTarget()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ HRESULT __stdcall CDropTarget::QueryInterface(REFIID iid, void ** ppvObject)
|
|
|
+ {
|
|
|
+ if (iid == IID_IDropTarget || iid == IID_IUnknown)
|
|
|
+ {
|
|
|
+ AddRef();
|
|
|
+ *ppvObject = this;
|
|
|
+ return S_OK;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ *ppvObject = 0;
|
|
|
+ return E_NOINTERFACE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ULONG __stdcall CDropTarget::AddRef(void)
|
|
|
+ {
|
|
|
+ return InterlockedIncrement(&m_lRefCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ ULONG __stdcall CDropTarget::Release(void)
|
|
|
+ {
|
|
|
+ LONG count = InterlockedDecrement(&m_lRefCount);
|
|
|
+
|
|
|
+ if (count == 0)
|
|
|
+ {
|
|
|
+ delete this;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- void UpdateMousePos(HWND hwnd)
|
|
|
+ void CDropTarget::UpdateMousePos()
|
|
|
{
|
|
|
- if (!hwnd || dragAndDrop_.Null())
|
|
|
+ if (!m_hWnd || dragAndDrop_.Null())
|
|
|
return;
|
|
|
|
|
|
POINT p;
|
|
|
GetCursorPos(&p);
|
|
|
- ScreenToClient(hwnd, &p);
|
|
|
+ ScreenToClient(m_hWnd, &p);
|
|
|
using namespace Atomic::MouseMove;
|
|
|
|
|
|
Atomic::VariantMap mvEventData;
|
|
|
@@ -79,75 +146,223 @@ namespace Atomic
|
|
|
|
|
|
}
|
|
|
|
|
|
- LRESULT CALLBACK Atomic_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
+
|
|
|
+ bool CDropTarget::QueryDataObject(IDataObject *pDataObject)
|
|
|
+ {
|
|
|
+ FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT,
|
|
|
+ -1, TYMED_HGLOBAL };
|
|
|
+
|
|
|
+
|
|
|
+ // does the data object support CF_HDROP using a HGLOBAL?
|
|
|
+ return pDataObject->QueryGetData(&fmte) == S_OK ? true : false;
|
|
|
+ }
|
|
|
+
|
|
|
+ DWORD CDropTarget::DropEffect(DWORD grfKeyState, POINTL pt, DWORD dwAllowed)
|
|
|
+ {
|
|
|
+ DWORD dwEffect = 0;
|
|
|
+
|
|
|
+ // 1. check "pt" -> do we allow a drop at the specified coordinates?
|
|
|
+
|
|
|
+ // 2. work out that the drop-effect should be based on grfKeyState
|
|
|
+ if (grfKeyState & MK_CONTROL)
|
|
|
+ {
|
|
|
+ dwEffect = dwAllowed & DROPEFFECT_COPY;
|
|
|
+ }
|
|
|
+ else if (grfKeyState & MK_SHIFT)
|
|
|
+ {
|
|
|
+ dwEffect = dwAllowed & DROPEFFECT_MOVE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. no key-modifiers were specified (or drop effect not allowed), so
|
|
|
+ // base the effect on those allowed by the dropsource
|
|
|
+ if (dwEffect == 0)
|
|
|
+ {
|
|
|
+ if (dwAllowed & DROPEFFECT_COPY) dwEffect = DROPEFFECT_COPY;
|
|
|
+ if (dwAllowed & DROPEFFECT_MOVE) dwEffect = DROPEFFECT_MOVE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return dwEffect;
|
|
|
+ }
|
|
|
+
|
|
|
+ HRESULT __stdcall CDropTarget::DragEnter(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
|
|
|
{
|
|
|
+
|
|
|
if (dragAndDrop_.Null())
|
|
|
+ return S_OK;
|
|
|
+
|
|
|
+ // does the dataobject contain data we want?
|
|
|
+ m_fAllowDrop = QueryDataObject(pDataObject);
|
|
|
+
|
|
|
+ if (m_fAllowDrop)
|
|
|
{
|
|
|
- return CallWindowProc(UIDragDropWindows::sdlWndProc_, hwnd, msg, wParam, lParam);
|
|
|
+ // get the dropeffect based on keyboard state
|
|
|
+ *pdwEffect = DropEffect(grfKeyState, pt, *pdwEffect);
|
|
|
+
|
|
|
+ SetFocus(m_hWnd);
|
|
|
+
|
|
|
+ dragAndDrop_->FileDragEntered();
|
|
|
+ UpdateMousePos();
|
|
|
+
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ *pdwEffect = DROPEFFECT_NONE;
|
|
|
}
|
|
|
|
|
|
- switch (msg) {
|
|
|
+ return S_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ HRESULT __stdcall CDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
|
|
|
+ {
|
|
|
+ if (m_fAllowDrop)
|
|
|
+ {
|
|
|
+ *pdwEffect = DropEffect(grfKeyState, pt, *pdwEffect);
|
|
|
+ UpdateMousePos();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ *pdwEffect = DROPEFFECT_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return S_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ HRESULT __stdcall CDropTarget::DragLeave(void)
|
|
|
+ {
|
|
|
+ return S_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ void CDropTarget::OpenFilesFromDataObject(IDataObject *pdto)
|
|
|
+ {
|
|
|
+ FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT,
|
|
|
+ -1, TYMED_HGLOBAL };
|
|
|
+
|
|
|
+ STGMEDIUM stgm;
|
|
|
|
|
|
- case WM_DROPFILES:
|
|
|
+ if (SUCCEEDED(pdto->GetData(&fmte, &stgm)))
|
|
|
{
|
|
|
+ PVOID data = GlobalLock(stgm.hGlobal);
|
|
|
|
|
|
- //implement the IDropTarget interface and then register the HWND using RegisterDragDrop() to start receiving notifications. Be sure to call RevokeDragDrop() before exiting the app.
|
|
|
+ HDROP hdrop = reinterpret_cast<HDROP>(stgm.hGlobal);
|
|
|
|
|
|
- UINT i;
|
|
|
- HDROP drop = (HDROP)wParam;
|
|
|
- UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
|
|
|
+ UINT cFiles = DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0);
|
|
|
|
|
|
- if (count)
|
|
|
+ for (UINT i = 0; i < cFiles; i++)
|
|
|
{
|
|
|
- dragAndDrop_->FileDragEntered();
|
|
|
-
|
|
|
- UpdateMousePos(hwnd);
|
|
|
-
|
|
|
- for (i = 0; i < count; ++i) {
|
|
|
- UINT size = DragQueryFile(drop, i, NULL, 0) + 1;
|
|
|
- LPTSTR buffer = SDL_stack_alloc(TCHAR, size);
|
|
|
- if (buffer) {
|
|
|
- if (DragQueryFile(drop, i, buffer, size)) {
|
|
|
-
|
|
|
- char *file = WIN_StringToUTF8(buffer);
|
|
|
- dragAndDrop_->FileDragAddFile(file);
|
|
|
- SDL_free(file);
|
|
|
- }
|
|
|
- SDL_stack_free(buffer);
|
|
|
- }
|
|
|
+ TCHAR szFile[MAX_PATH];
|
|
|
+ UINT cch = DragQueryFile(hdrop, i, szFile, MAX_PATH);
|
|
|
+
|
|
|
+ if (cch > 0 && cch < MAX_PATH)
|
|
|
+ {
|
|
|
+ dragAndDrop_->FileDragAddFile(szFile);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- dragAndDrop_->FileDragConclude();
|
|
|
+ DragFinish(hdrop);
|
|
|
|
|
|
- }
|
|
|
+ GlobalUnlock(stgm.hGlobal);
|
|
|
|
|
|
- // sdlWndProc will handle this
|
|
|
- //DragFinish(drop);
|
|
|
- break;
|
|
|
+ ReleaseStgMedium(&stgm);
|
|
|
+
|
|
|
+ dragAndDrop_->FileDragConclude();
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- };
|
|
|
+ HRESULT __stdcall CDropTarget::Drop(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
|
|
|
+ {
|
|
|
+ if (dragAndDrop_.Null())
|
|
|
+ return S_OK;
|
|
|
|
|
|
- return CallWindowProc(UIDragDropWindows::sdlWndProc_, hwnd, msg, wParam, lParam);
|
|
|
+ UpdateMousePos();
|
|
|
+
|
|
|
+ if (m_fAllowDrop)
|
|
|
+ {
|
|
|
+ OpenFilesFromDataObject(pDataObject);
|
|
|
+
|
|
|
+ *pdwEffect = DropEffect(grfKeyState, pt, *pdwEffect);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ *pdwEffect = DROPEFFECT_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return S_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ static void RegisterDropWindow(HWND hwnd, IDropTarget **ppDropTarget)
|
|
|
+ {
|
|
|
+ OleInitialize(0);
|
|
|
+
|
|
|
+ CDropTarget *pDropTarget = new CDropTarget(hwnd);
|
|
|
+
|
|
|
+ // acquire a strong lock
|
|
|
+ CoLockObjectExternal(pDropTarget, TRUE, FALSE);
|
|
|
+
|
|
|
+ // tell OLE that the window is a drop target
|
|
|
+ HRESULT result = RegisterDragDrop(hwnd, pDropTarget);
|
|
|
+
|
|
|
+ *ppDropTarget = pDropTarget;
|
|
|
+ }
|
|
|
+
|
|
|
+ static void UnregisterDropWindow(HWND hwnd, IDropTarget *pDropTarget)
|
|
|
+ {
|
|
|
+ // remove drag+drop
|
|
|
+ HRESULT result = RevokeDragDrop(hwnd);
|
|
|
+
|
|
|
+ // remove the strong lock
|
|
|
+ CoLockObjectExternal(pDropTarget, FALSE, TRUE);
|
|
|
+
|
|
|
+ // release our own reference
|
|
|
+ pDropTarget->Release();
|
|
|
+
|
|
|
+ OleUninitialize();
|
|
|
}
|
|
|
|
|
|
+ // UIDragDropWindows
|
|
|
+
|
|
|
+ class UIDragDropWindows : public Object
|
|
|
+ {
|
|
|
+ OBJECT(UIDragDropWindows);
|
|
|
+
|
|
|
+ public:
|
|
|
+ /// Construct.
|
|
|
+ UIDragDropWindows(Context* context);
|
|
|
+ virtual ~UIDragDropWindows();
|
|
|
+
|
|
|
+ void Shutdown();
|
|
|
+
|
|
|
+ static WNDPROC sdlWndProc_;
|
|
|
+
|
|
|
+ private:
|
|
|
+
|
|
|
+ IDropTarget* dropTarget_;
|
|
|
+ HWND hwnd_;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ WNDPROC UIDragDropWindows::sdlWndProc_ = NULL;
|
|
|
+
|
|
|
UIDragDropWindows::UIDragDropWindows(Context* context) : Object(context),
|
|
|
- hwnd_(NULL)
|
|
|
+ dropTarget_(NULL), hwnd_(NULL)
|
|
|
{
|
|
|
SDL_Window* window = (SDL_Window*)GetSubsystem<Graphics>()->GetSDLWindow();
|
|
|
|
|
|
SDL_SysWMinfo info;
|
|
|
SDL_VERSION(&info.version);
|
|
|
|
|
|
- if (SDL_GetWindowWMInfo(window, &info)) {
|
|
|
+ if (SDL_GetWindowWMInfo(window, &info))
|
|
|
+ {
|
|
|
|
|
|
hwnd_ = info.info.win.window;
|
|
|
- // hook the wnd proc so we can handle drag/drop ourselves
|
|
|
+
|
|
|
+ // hook the wnd proc so we can catch close and deregister drag and drop
|
|
|
sdlWndProc_ = (WNDPROC)GetWindowLongPtr(hwnd_, GWLP_WNDPROC);
|
|
|
SetWindowLongPtr(hwnd_, GWLP_WNDPROC, (LONG_PTR)Atomic_WindowProc);
|
|
|
- }
|
|
|
|
|
|
- SubscribeToEvent(E_UPDATE, HANDLER(UIDragDropWindows, HandleUpdate));
|
|
|
+ RegisterDropWindow(hwnd_, &dropTarget_);
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -156,16 +371,15 @@ namespace Atomic
|
|
|
|
|
|
}
|
|
|
|
|
|
- void UIDragDropWindows::HandleUpdate(StringHash eventType, VariantMap & eventData)
|
|
|
+ void UIDragDropWindows::Shutdown()
|
|
|
{
|
|
|
-
|
|
|
- if (!hwnd_ || dragAndDrop_.Null())
|
|
|
+ if (!hwnd_ || !dropTarget_)
|
|
|
return;
|
|
|
|
|
|
- if (GetActiveWindow() == hwnd_)
|
|
|
- return;
|
|
|
+ UnregisterDropWindow(hwnd_, dropTarget_);
|
|
|
|
|
|
- UpdateMousePos(hwnd_);
|
|
|
+ // restore SDL winproc
|
|
|
+ SetWindowLongPtr(hwnd_, GWLP_WNDPROC, (LONG_PTR)sdlWndProc_);
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -176,4 +390,24 @@ namespace Atomic
|
|
|
|
|
|
}
|
|
|
|
|
|
-}
|
|
|
+ LRESULT CALLBACK Atomic_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
+ {
|
|
|
+ if (dragAndDrop_.Null())
|
|
|
+ {
|
|
|
+ return CallWindowProc(UIDragDropWindows::sdlWndProc_, hwnd, msg, wParam, lParam);
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (msg) {
|
|
|
+
|
|
|
+ case WM_CLOSE:
|
|
|
+ {
|
|
|
+ dragAndDrop_->GetSubsystem<UIDragDropWindows>()->Shutdown();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ return CallWindowProc(UIDragDropWindows::sdlWndProc_, hwnd, msg, wParam, lParam);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|