| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- #pragma once
- #include "Shellapi.h"
- // This is just a helper include for BsPlatformImpl.cpp, it's not meant to be used on its own
- namespace BansheeEngine
- {
- /** @cond INTERNAL */
- /** @addtogroup Platform
- * @{
- */
- /**
- * 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
- {
- /** Type of drag and drop event. */
- enum class DropOpType
- {
- DragOver,
- Drop,
- Leave
- };
- /** Type of data that a drag and drop operation contains. */
- enum class DropOpDataType
- {
- FileList,
- None
- };
- /** Structure describing a drag and drop operation. */
- struct DropTargetOp
- {
- DropTargetOp(DropOpType _type, const Vector2I& _pos)
- :type(_type), position(_pos), dataType(DropOpDataType::None)
- { }
- DropOpType type;
- Vector2I position;
- DropOpDataType dataType;
- union
- {
- Vector<WString>* mFileList;
- };
- };
- public:
- Win32DropTarget(HWND hWnd)
- :mHWnd(hWnd), mRefCount(1), mAcceptDrag(false)
- { }
- ~Win32DropTarget()
- {
- BS_LOCK_MUTEX(mSync);
- for(auto& fileList : mFileLists)
- {
- bs_delete(fileList);
- }
- mFileLists.clear();
- mQueuedDropOps.clear();
- }
- /** Registers the drop target with the operating system. Monitoring for drag and drop operations starts. */
- void registerWithOS()
- {
- CoLockObjectExternal(this, TRUE, FALSE);
- HRESULT hr = RegisterDragDrop(mHWnd, this);
- }
- /** Unregisters the drop target with the operating system. Monitoring for drag and drop operations stops. */
- void unregisterWithOS()
- {
- RevokeDragDrop(mHWnd);
- CoLockObjectExternal(this, FALSE, FALSE);
- }
- /** COM requirement. Returns instance of an interface of the provided type. */
- HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject) override
- {
- if(iid == IID_IDropTarget || iid == IID_IUnknown)
- {
- AddRef();
- *ppvObject = this;
- return S_OK;
- }
- else
- {
- *ppvObject = nullptr;
- return E_NOINTERFACE;
- }
- }
- /** COM requirement. Increments objects reference count. */
- ULONG __stdcall AddRef() override
- {
- return InterlockedIncrement(&mRefCount);
- }
- /** COM requirement. Decreases the objects reference count and deletes the object if its zero. */
- ULONG __stdcall Release() override
- {
- LONG count = InterlockedDecrement(&mRefCount);
- if(count == 0)
- {
- bs_delete(this);
- return 0;
- }
- else
- {
- return count;
- }
- }
- /**
- * Called by the OS when user enters the drop target area while dragging an object.
- *
- * @note Called on core thread.
- */
- HRESULT __stdcall DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
- {
- *pdwEffect = DROPEFFECT_LINK;
- mAcceptDrag = isDataValid(pDataObj);
- if(!mAcceptDrag)
- return S_OK;
- {
- BS_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;
- }
- /**
- * Called by the OS while user continues to drag an object over the drop target.
- *
- * @note Called on core thread.
- */
- HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
- {
- *pdwEffect = DROPEFFECT_LINK;
- if(!mAcceptDrag)
- return S_OK;
- {
- BS_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;
- }
- /**
- * Called by the OS when user leaves the drop target.
- *
- * @note Called on core thread.
- */
- HRESULT __stdcall DragLeave() override
- {
- {
- BS_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;
- }
- /**
- * Called by the OS when the user ends the drag operation while over the drop target.
- *
- * @note Called on core thread.
- */
- HRESULT __stdcall Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
- {
- *pdwEffect = DROPEFFECT_LINK;
- mAcceptDrag = false;
- if(!isDataValid(pDataObj))
- return S_OK;
- {
- BS_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;
- }
- /**
- * Registers a new drop target to monitor.
- *
- * @note Sim thread only.
- */
- void registerDropTarget(OSDropTarget* dropTarget)
- {
- mDropTargets.push_back(dropTarget);
- }
- /**
- * Unregisters an existing drop target and stops monitoring it.
- *
- * @note Sim thread only.
- */
- void unregisterDropTarget(OSDropTarget* dropTarget)
- {
- auto findIter = std::find(begin(mDropTargets), end(mDropTargets), dropTarget);
- if(findIter != mDropTargets.end())
- mDropTargets.erase(findIter);
- }
- /**
- * Gets the total number of monitored drop targets.
- *
- * @note Sim thread only.
- */
- unsigned int getNumDropTargets() const
- {
- return (unsigned int)mDropTargets.size();
- }
- /** Called every frame by the sim thread. Internal method. */
- void update()
- {
- BS_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)
- {
- while (!mFileLists.empty())
- {
- bool done = mFileLists[0] == op.mFileList;
- bs_delete(mFileLists[0]);
- mFileLists.erase(mFileLists.begin());
- if (done)
- break;
- }
- }
- }
- mQueuedDropOps.clear();
- }
- private:
- /** Check if we support the data in the provided drag and drop data object. */
- 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;
- }
- /** Gets a file list from data. Caller must ensure that the data actually contains a file list. */
- Vector<WString>* getFileListFromData(IDataObject* data)
- {
- FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
- STGMEDIUM stgmed;
- Vector<WString>* files = bs_new<Vector<WString>>();
- 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*)bs_alloc((UINT32)numChars * sizeof(wchar_t));
- DragQueryFileW(hDrop, i, buffer, numChars);
- (*files)[i] = WString(buffer);
- bs_free(buffer);
- }
- GlobalUnlock(stgmed.hGlobal);
- ReleaseStgMedium(&stgmed);
- }
- return files;
- }
- private:
- Vector<OSDropTarget*> mDropTargets;
- LONG mRefCount;
- HWND mHWnd;
- bool mAcceptDrag;
- Vector<DropTargetOp> mQueuedDropOps;
- Vector<Vector<WString>*> mFileLists;
- BS_MUTEX(mSync);
- };
- /** @} */
- /** @endcond */
- }
|