| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- #pragma once
- #include "Shellapi.h"
- // This is just a helper include for CmPlatformImpl.cpp, it's not meant to be used on its own
- namespace CamelotFramework
- {
- /**
- * @brief Called by the OS when various drag and drop actions are performed over a
- * window this control is registered for.
- *
- * @note This class queues all messages receives by the OS (from the core thread), and then executes
- * the queue on sim thread. You should be wary of which methods are allowed to be called from which
- * thread.
- */
- class Win32DropTarget : public IDropTarget
- {
- enum class DropOpType
- {
- DragOver,
- Drop,
- Leave
- };
- enum class DropOpDataType
- {
- FileList,
- None
- };
- struct DropTargetOp
- {
- DropTargetOp(DropOpType _type, const Vector2I& _pos)
- :type(_type), position(_pos), dataType(DropOpDataType::None)
- { }
- DropOpType type;
- Vector2I position;
- DropOpDataType dataType;
- union
- {
- Vector<WString>::type* mFileList;
- };
- };
- public:
- Win32DropTarget(HWND hWnd)
- :mHWnd(hWnd), mRefCount(1), mAcceptDrag(false)
- { }
- ~Win32DropTarget()
- {
- CM_LOCK_MUTEX(mSync);
- for(auto& fileList : mFileLists)
- {
- cm_delete(fileList);
- }
- mFileLists.clear();
- mQueuedDropOps.clear();
- }
- void registerWithOS()
- {
- CoLockObjectExternal(this, TRUE, FALSE);
- HRESULT hr = RegisterDragDrop(mHWnd, this);
- }
- void unregisterWithOS()
- {
- RevokeDragDrop(mHWnd);
- CoLockObjectExternal(this, FALSE, FALSE);
- }
- HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject)
- {
- if(iid == IID_IDropTarget || iid == IID_IUnknown)
- {
- AddRef();
- *ppvObject = this;
- return S_OK;
- }
- else
- {
- *ppvObject = nullptr;
- return E_NOINTERFACE;
- }
- }
- ULONG __stdcall AddRef()
- {
- return InterlockedIncrement(&mRefCount);
- }
- ULONG __stdcall Release()
- {
- LONG count = InterlockedDecrement(&mRefCount);
- if(count == 0)
- {
- cm_delete(this);
- return 0;
- }
- else
- {
- return count;
- }
- }
- HRESULT __stdcall DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
- {
- *pdwEffect = DROPEFFECT_LINK;
- mAcceptDrag = isDataValid(pDataObj);
- if(!mAcceptDrag)
- return S_OK;
- {
- CM_LOCK_MUTEX(mSync);
- mFileLists.push_back(getFileListFromData(pDataObj));
- ScreenToClient(mHWnd, (POINT *)&pt);
- mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Vector2I((int)pt.x, (int)pt.y)));
- DropTargetOp& op = mQueuedDropOps.back();
- op.dataType = DropOpDataType::FileList;
- op.mFileList = mFileLists.back();
- }
-
- return S_OK;
- }
- HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
- {
- *pdwEffect = DROPEFFECT_LINK;
- if(!mAcceptDrag)
- return S_OK;
- {
- CM_LOCK_MUTEX(mSync);
- ScreenToClient(mHWnd, (POINT *)&pt);
- mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Vector2I((int)pt.x, (int)pt.y)));
- DropTargetOp& op = mQueuedDropOps.back();
- op.dataType = DropOpDataType::FileList;
- op.mFileList = mFileLists.back();
- }
-
- return S_OK;
- }
- HRESULT __stdcall DragLeave()
- {
- {
- CM_LOCK_MUTEX(mSync);
- mQueuedDropOps.push_back(DropTargetOp(DropOpType::Leave, Vector2I()));
- DropTargetOp& op = mQueuedDropOps.back();
- op.dataType = DropOpDataType::FileList;
- op.mFileList = mFileLists.back();
- }
- return S_OK;
- }
- HRESULT __stdcall Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
- {
- *pdwEffect = DROPEFFECT_LINK;
- mAcceptDrag = false;
- if(!isDataValid(pDataObj))
- return S_OK;
- {
- CM_LOCK_MUTEX(mSync);
- mFileLists.push_back(getFileListFromData(pDataObj));
- ScreenToClient(mHWnd, (POINT *)&pt);
- mQueuedDropOps.push_back(DropTargetOp(DropOpType::Drop, Vector2I((int)pt.x, (int)pt.y)));
- DropTargetOp& op = mQueuedDropOps.back();
- op.dataType = DropOpDataType::FileList;
- op.mFileList = mFileLists.back();
- }
- return S_OK;
- }
- // Sim thread only
- void registerDropTarget(OSDropTarget* dropTarget)
- {
- mDropTargets.push_back(dropTarget);
- }
- void unregisterDropTarget(OSDropTarget* dropTarget)
- {
- auto findIter = std::find(begin(mDropTargets), end(mDropTargets), dropTarget);
- if(findIter != mDropTargets.end())
- mDropTargets.erase(findIter);
- }
- unsigned int getNumDropTargets() const
- {
- return (unsigned int)mDropTargets.size();
- }
- /**
- * @brief Called every frame by the sim thread. Internal method.
- */
- void update()
- {
- CM_LOCK_MUTEX(mSync);
- for(auto& op: mQueuedDropOps)
- {
- for(auto& target : mDropTargets)
- {
- if(op.type != DropOpType::Leave)
- {
- if(target->_isInside(op.position))
- {
- if(!target->_isActive())
- {
- target->_setFileList(*op.mFileList);
- target->onEnter(op.position.x, op.position.y);
- }
- if(op.type == DropOpType::DragOver)
- target->onDragOver(op.position.x, op.position.y);
- else if(op.type == DropOpType::Drop)
- {
- target->_setFileList(*op.mFileList);
- target->onDrop(op.position.x, op.position.y);
- }
- }
- else
- {
- if(target->_isActive())
- {
- target->onLeave();
- target->_clear();
- target->_setActive(false);
- }
- }
- }
- else
- {
- if(target->_isActive())
- {
- target->onLeave();
- target->_clear();
- target->_setActive(false);
- }
- }
- }
- if(op.type == DropOpType::Leave || op.type == DropOpType::Drop)
- {
- cm_delete(op.mFileList);
- mFileLists.erase(mFileLists.begin());
- }
- }
- mQueuedDropOps.clear();
- }
- private:
- bool isDataValid(IDataObject* data)
- {
- // TODO - Currently only supports file drag and drop, so only CF_HDROP is used
- FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
- return data->QueryGetData(&fmtetc) == S_OK ? true : false;
- }
- Vector<WString>::type* getFileListFromData(IDataObject* data)
- {
- FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
- STGMEDIUM stgmed;
- Vector<WString>::type* files = cm_new<Vector<WString>::type>();
- if(data->GetData(&fmtetc, &stgmed) == S_OK)
- {
- PVOID data = GlobalLock(stgmed.hGlobal);
- HDROP hDrop = (HDROP)data;
- UINT numFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0);
- files->resize(numFiles);
- for(UINT i = 0; i < numFiles; i++)
- {
- UINT numChars = DragQueryFileW(hDrop, i, nullptr, 0) + 1;
- wchar_t* buffer = (wchar_t*)cm_alloc((UINT32)numChars * sizeof(wchar_t));
- DragQueryFileW(hDrop, i, buffer, numChars);
- (*files)[i] = WString(buffer);
- cm_free(buffer);
- }
- GlobalUnlock(stgmed.hGlobal);
- ReleaseStgMedium(&stgmed);
- }
- return files;
- }
- private:
- Vector<OSDropTarget*>::type mDropTargets;
- LONG mRefCount;
- HWND mHWnd;
- bool mAcceptDrag;
- Vector<DropTargetOp>::type mQueuedDropOps;
- Vector<Vector<WString>::type*>::type mFileLists;
- CM_MUTEX(mSync);
- };
- }
|