BsWin32DropTarget.h 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. #pragma once
  2. #include "Shellapi.h"
  3. // This is just a helper include for BsPlatformImpl.cpp, it's not meant to be used on its own
  4. namespace BansheeEngine
  5. {
  6. /** @cond INTERNAL */
  7. /** @addtogroup Platform
  8. * @{
  9. */
  10. /**
  11. * Called by the OS when various drag and drop actions are performed over a window this control is registered for.
  12. *
  13. * @note
  14. * This class queues all messages receives by the OS (from the core thread), and then executes the queue on sim thread.
  15. * You should be wary of which methods are allowed to be called from which thread.
  16. */
  17. class Win32DropTarget : public IDropTarget
  18. {
  19. /** Type of drag and drop event. */
  20. enum class DropOpType
  21. {
  22. DragOver,
  23. Drop,
  24. Leave
  25. };
  26. /** Type of data that a drag and drop operation contains. */
  27. enum class DropOpDataType
  28. {
  29. FileList,
  30. None
  31. };
  32. /** Structure describing a drag and drop operation. */
  33. struct DropTargetOp
  34. {
  35. DropTargetOp(DropOpType _type, const Vector2I& _pos)
  36. :type(_type), position(_pos), dataType(DropOpDataType::None)
  37. { }
  38. DropOpType type;
  39. Vector2I position;
  40. DropOpDataType dataType;
  41. union
  42. {
  43. Vector<WString>* mFileList;
  44. };
  45. };
  46. public:
  47. Win32DropTarget(HWND hWnd)
  48. :mHWnd(hWnd), mRefCount(1), mAcceptDrag(false)
  49. { }
  50. ~Win32DropTarget()
  51. {
  52. BS_LOCK_MUTEX(mSync);
  53. for(auto& fileList : mFileLists)
  54. {
  55. bs_delete(fileList);
  56. }
  57. mFileLists.clear();
  58. mQueuedDropOps.clear();
  59. }
  60. /** Registers the drop target with the operating system. Monitoring for drag and drop operations starts. */
  61. void registerWithOS()
  62. {
  63. CoLockObjectExternal(this, TRUE, FALSE);
  64. HRESULT hr = RegisterDragDrop(mHWnd, this);
  65. }
  66. /** Unregisters the drop target with the operating system. Monitoring for drag and drop operations stops. */
  67. void unregisterWithOS()
  68. {
  69. RevokeDragDrop(mHWnd);
  70. CoLockObjectExternal(this, FALSE, FALSE);
  71. }
  72. /** COM requirement. Returns instance of an interface of the provided type. */
  73. HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject) override
  74. {
  75. if(iid == IID_IDropTarget || iid == IID_IUnknown)
  76. {
  77. AddRef();
  78. *ppvObject = this;
  79. return S_OK;
  80. }
  81. else
  82. {
  83. *ppvObject = nullptr;
  84. return E_NOINTERFACE;
  85. }
  86. }
  87. /** COM requirement. Increments objects reference count. */
  88. ULONG __stdcall AddRef() override
  89. {
  90. return InterlockedIncrement(&mRefCount);
  91. }
  92. /** COM requirement. Decreases the objects reference count and deletes the object if its zero. */
  93. ULONG __stdcall Release() override
  94. {
  95. LONG count = InterlockedDecrement(&mRefCount);
  96. if(count == 0)
  97. {
  98. bs_delete(this);
  99. return 0;
  100. }
  101. else
  102. {
  103. return count;
  104. }
  105. }
  106. /**
  107. * Called by the OS when user enters the drop target area while dragging an object.
  108. *
  109. * @note Called on core thread.
  110. */
  111. HRESULT __stdcall DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
  112. {
  113. *pdwEffect = DROPEFFECT_LINK;
  114. mAcceptDrag = isDataValid(pDataObj);
  115. if(!mAcceptDrag)
  116. return S_OK;
  117. {
  118. BS_LOCK_MUTEX(mSync);
  119. mFileLists.push_back(getFileListFromData(pDataObj));
  120. ScreenToClient(mHWnd, (POINT *)&pt);
  121. mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Vector2I((int)pt.x, (int)pt.y)));
  122. DropTargetOp& op = mQueuedDropOps.back();
  123. op.dataType = DropOpDataType::FileList;
  124. op.mFileList = mFileLists.back();
  125. }
  126. return S_OK;
  127. }
  128. /**
  129. * Called by the OS while user continues to drag an object over the drop target.
  130. *
  131. * @note Called on core thread.
  132. */
  133. HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
  134. {
  135. *pdwEffect = DROPEFFECT_LINK;
  136. if(!mAcceptDrag)
  137. return S_OK;
  138. {
  139. BS_LOCK_MUTEX(mSync);
  140. ScreenToClient(mHWnd, (POINT *)&pt);
  141. mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Vector2I((int)pt.x, (int)pt.y)));
  142. DropTargetOp& op = mQueuedDropOps.back();
  143. op.dataType = DropOpDataType::FileList;
  144. op.mFileList = mFileLists.back();
  145. }
  146. return S_OK;
  147. }
  148. /**
  149. * Called by the OS when user leaves the drop target.
  150. *
  151. * @note Called on core thread.
  152. */
  153. HRESULT __stdcall DragLeave() override
  154. {
  155. {
  156. BS_LOCK_MUTEX(mSync);
  157. mQueuedDropOps.push_back(DropTargetOp(DropOpType::Leave, Vector2I()));
  158. DropTargetOp& op = mQueuedDropOps.back();
  159. op.dataType = DropOpDataType::FileList;
  160. op.mFileList = mFileLists.back();
  161. }
  162. return S_OK;
  163. }
  164. /**
  165. * Called by the OS when the user ends the drag operation while over the drop target.
  166. *
  167. * @note Called on core thread.
  168. */
  169. HRESULT __stdcall Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override
  170. {
  171. *pdwEffect = DROPEFFECT_LINK;
  172. mAcceptDrag = false;
  173. if(!isDataValid(pDataObj))
  174. return S_OK;
  175. {
  176. BS_LOCK_MUTEX(mSync);
  177. mFileLists.push_back(getFileListFromData(pDataObj));
  178. ScreenToClient(mHWnd, (POINT *)&pt);
  179. mQueuedDropOps.push_back(DropTargetOp(DropOpType::Drop, Vector2I((int)pt.x, (int)pt.y)));
  180. DropTargetOp& op = mQueuedDropOps.back();
  181. op.dataType = DropOpDataType::FileList;
  182. op.mFileList = mFileLists.back();
  183. }
  184. return S_OK;
  185. }
  186. /**
  187. * Registers a new drop target to monitor.
  188. *
  189. * @note Sim thread only.
  190. */
  191. void registerDropTarget(OSDropTarget* dropTarget)
  192. {
  193. mDropTargets.push_back(dropTarget);
  194. }
  195. /**
  196. * Unregisters an existing drop target and stops monitoring it.
  197. *
  198. * @note Sim thread only.
  199. */
  200. void unregisterDropTarget(OSDropTarget* dropTarget)
  201. {
  202. auto findIter = std::find(begin(mDropTargets), end(mDropTargets), dropTarget);
  203. if(findIter != mDropTargets.end())
  204. mDropTargets.erase(findIter);
  205. }
  206. /**
  207. * Gets the total number of monitored drop targets.
  208. *
  209. * @note Sim thread only.
  210. */
  211. unsigned int getNumDropTargets() const
  212. {
  213. return (unsigned int)mDropTargets.size();
  214. }
  215. /** Called every frame by the sim thread. Internal method. */
  216. void update()
  217. {
  218. BS_LOCK_MUTEX(mSync);
  219. for(auto& op: mQueuedDropOps)
  220. {
  221. for(auto& target : mDropTargets)
  222. {
  223. if(op.type != DropOpType::Leave)
  224. {
  225. if(target->_isInside(op.position))
  226. {
  227. if(!target->_isActive())
  228. {
  229. target->_setFileList(*op.mFileList);
  230. target->onEnter(op.position.x, op.position.y);
  231. }
  232. if(op.type == DropOpType::DragOver)
  233. target->onDragOver(op.position.x, op.position.y);
  234. else if(op.type == DropOpType::Drop)
  235. {
  236. target->_setFileList(*op.mFileList);
  237. target->onDrop(op.position.x, op.position.y);
  238. }
  239. }
  240. else
  241. {
  242. if(target->_isActive())
  243. {
  244. target->onLeave();
  245. target->_clear();
  246. target->_setActive(false);
  247. }
  248. }
  249. }
  250. else
  251. {
  252. if(target->_isActive())
  253. {
  254. target->onLeave();
  255. target->_clear();
  256. target->_setActive(false);
  257. }
  258. }
  259. }
  260. if(op.type == DropOpType::Leave || op.type == DropOpType::Drop)
  261. {
  262. while (!mFileLists.empty())
  263. {
  264. bool done = mFileLists[0] == op.mFileList;
  265. bs_delete(mFileLists[0]);
  266. mFileLists.erase(mFileLists.begin());
  267. if (done)
  268. break;
  269. }
  270. }
  271. }
  272. mQueuedDropOps.clear();
  273. }
  274. private:
  275. /** Check if we support the data in the provided drag and drop data object. */
  276. bool isDataValid(IDataObject* data)
  277. {
  278. // TODO - Currently only supports file drag and drop, so only CF_HDROP is used
  279. FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  280. return data->QueryGetData(&fmtetc) == S_OK ? true : false;
  281. }
  282. /** Gets a file list from data. Caller must ensure that the data actually contains a file list. */
  283. Vector<WString>* getFileListFromData(IDataObject* data)
  284. {
  285. FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  286. STGMEDIUM stgmed;
  287. Vector<WString>* files = bs_new<Vector<WString>>();
  288. if(data->GetData(&fmtetc, &stgmed) == S_OK)
  289. {
  290. PVOID data = GlobalLock(stgmed.hGlobal);
  291. HDROP hDrop = (HDROP)data;
  292. UINT numFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0);
  293. files->resize(numFiles);
  294. for(UINT i = 0; i < numFiles; i++)
  295. {
  296. UINT numChars = DragQueryFileW(hDrop, i, nullptr, 0) + 1;
  297. wchar_t* buffer = (wchar_t*)bs_alloc((UINT32)numChars * sizeof(wchar_t));
  298. DragQueryFileW(hDrop, i, buffer, numChars);
  299. (*files)[i] = WString(buffer);
  300. bs_free(buffer);
  301. }
  302. GlobalUnlock(stgmed.hGlobal);
  303. ReleaseStgMedium(&stgmed);
  304. }
  305. return files;
  306. }
  307. private:
  308. Vector<OSDropTarget*> mDropTargets;
  309. LONG mRefCount;
  310. HWND mHWnd;
  311. bool mAcceptDrag;
  312. Vector<DropTargetOp> mQueuedDropOps;
  313. Vector<Vector<WString>*> mFileLists;
  314. BS_MUTEX(mSync);
  315. };
  316. /** @} */
  317. /** @endcond */
  318. }