CmWin32DropTarget.h 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. #pragma once
  2. #include "Shellapi.h"
  3. // This is just a helper include for CmPlatformImpl.cpp, it's not meant to be used on its own
  4. namespace CamelotFramework
  5. {
  6. /**
  7. * @brief Called by the OS when various drag and drop actions are performed over a
  8. * window this control is registered for.
  9. *
  10. * @note This class queues all messages receives by the OS (from the core thread), and then executes
  11. * the queue on sim thread. You should be wary of which methods are allowed to be called from which
  12. * thread.
  13. */
  14. class Win32DropTarget : public IDropTarget
  15. {
  16. enum class DropOpType
  17. {
  18. DragOver,
  19. Drop,
  20. Leave
  21. };
  22. enum class DropOpDataType
  23. {
  24. FileList,
  25. None
  26. };
  27. struct DropTargetOp
  28. {
  29. DropTargetOp(DropOpType _type, const Vector2I& _pos)
  30. :type(_type), position(_pos), dataType(DropOpDataType::None)
  31. { }
  32. DropOpType type;
  33. Vector2I position;
  34. DropOpDataType dataType;
  35. union
  36. {
  37. Vector<WString>::type* mFileList;
  38. };
  39. };
  40. public:
  41. Win32DropTarget(HWND hWnd)
  42. :mHWnd(hWnd), mRefCount(1), mAcceptDrag(false)
  43. { }
  44. ~Win32DropTarget()
  45. {
  46. CM_LOCK_MUTEX(mSync);
  47. for(auto& fileList : mFileLists)
  48. {
  49. cm_delete(fileList);
  50. }
  51. mFileLists.clear();
  52. mQueuedDropOps.clear();
  53. }
  54. void registerWithOS()
  55. {
  56. CoLockObjectExternal(this, TRUE, FALSE);
  57. HRESULT hr = RegisterDragDrop(mHWnd, this);
  58. }
  59. void unregisterWithOS()
  60. {
  61. RevokeDragDrop(mHWnd);
  62. CoLockObjectExternal(this, FALSE, FALSE);
  63. }
  64. HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject)
  65. {
  66. if(iid == IID_IDropTarget || iid == IID_IUnknown)
  67. {
  68. AddRef();
  69. *ppvObject = this;
  70. return S_OK;
  71. }
  72. else
  73. {
  74. *ppvObject = nullptr;
  75. return E_NOINTERFACE;
  76. }
  77. }
  78. ULONG __stdcall AddRef()
  79. {
  80. return InterlockedIncrement(&mRefCount);
  81. }
  82. ULONG __stdcall Release()
  83. {
  84. LONG count = InterlockedDecrement(&mRefCount);
  85. if(count == 0)
  86. {
  87. cm_delete(this);
  88. return 0;
  89. }
  90. else
  91. {
  92. return count;
  93. }
  94. }
  95. HRESULT __stdcall DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
  96. {
  97. *pdwEffect = DROPEFFECT_LINK;
  98. mAcceptDrag = isDataValid(pDataObj);
  99. if(!mAcceptDrag)
  100. return S_OK;
  101. {
  102. CM_LOCK_MUTEX(mSync);
  103. mFileLists.push_back(getFileListFromData(pDataObj));
  104. ScreenToClient(mHWnd, (POINT *)&pt);
  105. mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Vector2I((int)pt.x, (int)pt.y)));
  106. DropTargetOp& op = mQueuedDropOps.back();
  107. op.dataType = DropOpDataType::FileList;
  108. op.mFileList = mFileLists.back();
  109. }
  110. return S_OK;
  111. }
  112. HRESULT __stdcall DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
  113. {
  114. *pdwEffect = DROPEFFECT_LINK;
  115. if(!mAcceptDrag)
  116. return S_OK;
  117. {
  118. CM_LOCK_MUTEX(mSync);
  119. ScreenToClient(mHWnd, (POINT *)&pt);
  120. mQueuedDropOps.push_back(DropTargetOp(DropOpType::DragOver, Vector2I((int)pt.x, (int)pt.y)));
  121. DropTargetOp& op = mQueuedDropOps.back();
  122. op.dataType = DropOpDataType::FileList;
  123. op.mFileList = mFileLists.back();
  124. }
  125. return S_OK;
  126. }
  127. HRESULT __stdcall DragLeave()
  128. {
  129. {
  130. CM_LOCK_MUTEX(mSync);
  131. mQueuedDropOps.push_back(DropTargetOp(DropOpType::Leave, Vector2I()));
  132. DropTargetOp& op = mQueuedDropOps.back();
  133. op.dataType = DropOpDataType::FileList;
  134. op.mFileList = mFileLists.back();
  135. }
  136. return S_OK;
  137. }
  138. HRESULT __stdcall Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
  139. {
  140. *pdwEffect = DROPEFFECT_LINK;
  141. mAcceptDrag = false;
  142. if(!isDataValid(pDataObj))
  143. return S_OK;
  144. {
  145. CM_LOCK_MUTEX(mSync);
  146. mFileLists.push_back(getFileListFromData(pDataObj));
  147. ScreenToClient(mHWnd, (POINT *)&pt);
  148. mQueuedDropOps.push_back(DropTargetOp(DropOpType::Drop, Vector2I((int)pt.x, (int)pt.y)));
  149. DropTargetOp& op = mQueuedDropOps.back();
  150. op.dataType = DropOpDataType::FileList;
  151. op.mFileList = mFileLists.back();
  152. }
  153. return S_OK;
  154. }
  155. // Sim thread only
  156. void registerDropTarget(OSDropTarget* dropTarget)
  157. {
  158. mDropTargets.push_back(dropTarget);
  159. }
  160. void unregisterDropTarget(OSDropTarget* dropTarget)
  161. {
  162. auto findIter = std::find(begin(mDropTargets), end(mDropTargets), dropTarget);
  163. if(findIter != mDropTargets.end())
  164. mDropTargets.erase(findIter);
  165. }
  166. unsigned int getNumDropTargets() const
  167. {
  168. return (unsigned int)mDropTargets.size();
  169. }
  170. /**
  171. * @brief Called every frame by the sim thread. Internal method.
  172. */
  173. void update()
  174. {
  175. CM_LOCK_MUTEX(mSync);
  176. for(auto& op: mQueuedDropOps)
  177. {
  178. for(auto& target : mDropTargets)
  179. {
  180. if(op.type != DropOpType::Leave)
  181. {
  182. if(target->_isInside(op.position))
  183. {
  184. if(!target->_isActive())
  185. {
  186. target->_setFileList(*op.mFileList);
  187. target->onEnter(op.position.x, op.position.y);
  188. }
  189. if(op.type == DropOpType::DragOver)
  190. target->onDragOver(op.position.x, op.position.y);
  191. else if(op.type == DropOpType::Drop)
  192. {
  193. target->_setFileList(*op.mFileList);
  194. target->onDrop(op.position.x, op.position.y);
  195. }
  196. }
  197. else
  198. {
  199. if(target->_isActive())
  200. {
  201. target->onLeave();
  202. target->_clear();
  203. target->_setActive(false);
  204. }
  205. }
  206. }
  207. else
  208. {
  209. if(target->_isActive())
  210. {
  211. target->onLeave();
  212. target->_clear();
  213. target->_setActive(false);
  214. }
  215. }
  216. }
  217. if(op.type == DropOpType::Leave || op.type == DropOpType::Drop)
  218. {
  219. cm_delete(op.mFileList);
  220. mFileLists.erase(mFileLists.begin());
  221. }
  222. }
  223. mQueuedDropOps.clear();
  224. }
  225. private:
  226. bool isDataValid(IDataObject* data)
  227. {
  228. // TODO - Currently only supports file drag and drop, so only CF_HDROP is used
  229. FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  230. return data->QueryGetData(&fmtetc) == S_OK ? true : false;
  231. }
  232. Vector<WString>::type* getFileListFromData(IDataObject* data)
  233. {
  234. FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  235. STGMEDIUM stgmed;
  236. Vector<WString>::type* files = cm_new<Vector<WString>::type>();
  237. if(data->GetData(&fmtetc, &stgmed) == S_OK)
  238. {
  239. PVOID data = GlobalLock(stgmed.hGlobal);
  240. HDROP hDrop = (HDROP)data;
  241. UINT numFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0);
  242. files->resize(numFiles);
  243. for(UINT i = 0; i < numFiles; i++)
  244. {
  245. UINT numChars = DragQueryFileW(hDrop, i, nullptr, 0) + 1;
  246. wchar_t* buffer = (wchar_t*)cm_alloc((UINT32)numChars * sizeof(wchar_t));
  247. DragQueryFileW(hDrop, i, buffer, numChars);
  248. (*files)[i] = WString(buffer);
  249. cm_free(buffer);
  250. }
  251. GlobalUnlock(stgmed.hGlobal);
  252. ReleaseStgMedium(&stgmed);
  253. }
  254. return files;
  255. }
  256. private:
  257. Vector<OSDropTarget*>::type mDropTargets;
  258. LONG mRefCount;
  259. HWND mHWnd;
  260. bool mAcceptDrag;
  261. Vector<DropTargetOp>::type mQueuedDropOps;
  262. Vector<Vector<WString>::type*>::type mFileLists;
  263. CM_MUTEX(mSync);
  264. };
  265. }