Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
9.3 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "datautil.h"
  4. #include "idlcomm.h"
  5. #include "idldrop.h"
  6. STDAPI_(BOOL) DoesDropTargetSupportDAD(IDropTarget *pdtgt)
  7. {
  8. IDropTargetWithDADSupport* pdt;
  9. if (pdtgt && SUCCEEDED(pdtgt->QueryInterface(IID_IDropTargetWithDADSupport, (void**)&pdt)))
  10. {
  11. pdt->Release();
  12. return TRUE;
  13. }
  14. return FALSE;
  15. }
  16. CIDLDropTarget::CIDLDropTarget(HWND hwnd) : m_cRef(1), m_hwnd(hwnd)
  17. {
  18. }
  19. CIDLDropTarget::~CIDLDropTarget()
  20. {
  21. // if we hit this a lot maybe we should just release it
  22. AssertMsg(m_pdtobj == NULL, TEXT("didn't get matching DragLeave."));
  23. if (m_pidl)
  24. ILFree(m_pidl);
  25. }
  26. HRESULT CIDLDropTarget::_Init(LPCITEMIDLIST pidl)
  27. {
  28. ASSERT(m_pidl == NULL);
  29. return pidl ? SHILClone(pidl, &m_pidl) : S_OK;
  30. }
  31. HWND CIDLDropTarget::_GetWindow()
  32. {
  33. return m_hwnd;
  34. }
  35. STDMETHODIMP CIDLDropTarget::QueryInterface(REFIID riid, void **ppv)
  36. {
  37. static const QITAB qit[] = {
  38. QITABENT(CIDLDropTarget, IUnknown),
  39. QITABENT(CIDLDropTarget, IDropTarget),
  40. QITABENT(CIDLDropTarget, IDropTargetWithDADSupport),
  41. { 0 },
  42. };
  43. return QISearch(this, qit, riid, ppv);
  44. }
  45. STDMETHODIMP_(ULONG) CIDLDropTarget::AddRef()
  46. {
  47. return InterlockedIncrement(&m_cRef);
  48. }
  49. STDMETHODIMP_(ULONG) CIDLDropTarget::Release()
  50. {
  51. if (InterlockedDecrement(&m_cRef))
  52. return m_cRef;
  53. delete this;
  54. return 0;
  55. }
  56. STDAPI GetClipFormatFlags(IDataObject *pdtobj, DWORD *pdwData, DWORD *pdwEffectPreferred);
  57. STDMETHODIMP CIDLDropTarget::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  58. {
  59. ASSERT(m_pdtobj == NULL); // DragDrop protocol requires DragLeave, this should be empty
  60. // init our registerd data formats
  61. IDLData_InitializeClipboardFormats();
  62. m_grfKeyStateLast = grfKeyState;
  63. m_dwEffectLastReturned = *pdwEffect;
  64. IUnknown_Set((IUnknown **)&m_pdtobj, (IUnknown *)pDataObj);
  65. GetClipFormatFlags(m_pdtobj, &m_dwData, &m_dwEffectPreferred);
  66. return S_OK;
  67. }
  68. // subclasses can prevetn us from assigning in the dwEffect by not passing in pdwEffect
  69. STDMETHODIMP CIDLDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  70. {
  71. m_grfKeyStateLast = grfKeyState;
  72. if (pdwEffect)
  73. *pdwEffect = m_dwEffectLastReturned;
  74. return S_OK;
  75. }
  76. STDMETHODIMP CIDLDropTarget::DragLeave()
  77. {
  78. IUnknown_Set((IUnknown **)&m_pdtobj, NULL);
  79. return S_OK;
  80. }
  81. STDMETHODIMP CIDLDropTarget::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  82. {
  83. return E_NOTIMPL;
  84. }
  85. struct {
  86. UINT uID;
  87. DWORD dwEffect;
  88. } const c_IDEffects[] = {
  89. DDIDM_COPY, DROPEFFECT_COPY,
  90. DDIDM_MOVE, DROPEFFECT_MOVE,
  91. DDIDM_CONTENTS_DESKCOMP, DROPEFFECT_LINK,
  92. DDIDM_LINK, DROPEFFECT_LINK,
  93. DDIDM_SCRAP_COPY, DROPEFFECT_COPY,
  94. DDIDM_SCRAP_MOVE, DROPEFFECT_MOVE,
  95. DDIDM_DOCLINK, DROPEFFECT_LINK,
  96. DDIDM_CONTENTS_COPY, DROPEFFECT_COPY,
  97. DDIDM_CONTENTS_MOVE, DROPEFFECT_MOVE,
  98. DDIDM_CONTENTS_LINK, DROPEFFECT_LINK,
  99. DDIDM_CONTENTS_DESKIMG, DROPEFFECT_LINK,
  100. DDIDM_SYNCCOPYTYPE, DROPEFFECT_COPY, // (order is important)
  101. DDIDM_SYNCCOPY, DROPEFFECT_COPY,
  102. DDIDM_OBJECT_COPY, DROPEFFECT_COPY,
  103. DDIDM_OBJECT_MOVE, DROPEFFECT_MOVE,
  104. };
  105. //
  106. // Pops up the "Copy, Link, Move" context menu, so that the user can
  107. // choose one of them.
  108. //
  109. // in:
  110. // pdwEffect drop effects allowed
  111. // dwDefaultEffect default drop effect
  112. // hkeyBase/hkeyProgID extension hkeys
  113. // hmenuReplace replaces POPUP_NONDEFAULTDD. Can only contain:
  114. // DDIDM_MOVE, DDIDM_COPY, DDIDM_LINK menu ids
  115. // pt in screen
  116. // Returns:
  117. // S_OK -- Menu item is processed by one of extensions or canceled
  118. // S_FALSE -- Menu item is selected
  119. //
  120. HRESULT CIDLDropTarget::DragDropMenu(DWORD dwDefaultEffect,
  121. IDataObject *pdtobj,
  122. POINTL pt, DWORD *pdwEffect,
  123. HKEY hkeyProgID, HKEY hkeyBase,
  124. UINT idMenu, DWORD grfKeyState)
  125. {
  126. DRAGDROPMENUPARAM ddm = { dwDefaultEffect, pdtobj, { pt.x, pt.y},
  127. pdwEffect,
  128. hkeyProgID, hkeyBase, idMenu, 0, grfKeyState };
  129. return DragDropMenuEx(&ddm);
  130. }
  131. HRESULT CIDLDropTarget::DragDropMenuEx(DRAGDROPMENUPARAM *pddm)
  132. {
  133. HRESULT hr = E_OUTOFMEMORY; // assume error
  134. DWORD dwEffectOut = 0; // assume no-ope.
  135. HMENU hmenu = SHLoadPopupMenu(HINST_THISDLL, pddm->idMenu);
  136. if (hmenu)
  137. {
  138. UINT idCmd;
  139. UINT idCmdFirst = DDIDM_EXTFIRST;
  140. HDXA hdxa = HDXA_Create();
  141. HDCA hdca = DCA_Create();
  142. if (hdxa && hdca)
  143. {
  144. // PERF (toddb): Even though pddm->hkeyBase does not have the same value as
  145. // pddm->hkeyProgID they can both be the same registry key (HKCR\FOLDER, for example).
  146. // As a result we sometimes enumerate this key twice looking for the same data. As
  147. // this is sometimes a slow operation we should avoid this. The comparision
  148. // done below was never valid on NT and might not be valid on win9x.
  149. //
  150. // Add extended menu for "Base" class.
  151. //
  152. if (pddm->hkeyBase && pddm->hkeyBase != pddm->hkeyProgID)
  153. DCA_AddItemsFromKey(hdca, pddm->hkeyBase, STRREG_SHEX_DDHANDLER);
  154. //
  155. // Enumerate the DD handlers and let them append menu items.
  156. //
  157. if (pddm->hkeyProgID)
  158. DCA_AddItemsFromKey(hdca, pddm->hkeyProgID, STRREG_SHEX_DDHANDLER);
  159. idCmdFirst = HDXA_AppendMenuItems(hdxa, pddm->pdtobj, 1,
  160. &pddm->hkeyProgID, m_pidl, hmenu, 0,
  161. DDIDM_EXTFIRST, DDIDM_EXTLAST, 0, hdca);
  162. }
  163. // eliminate menu options that are not allowed by dwEffect
  164. for (int nItem = 0; nItem < ARRAYSIZE(c_IDEffects); ++nItem)
  165. {
  166. if (GetMenuState(hmenu, c_IDEffects[nItem].uID, MF_BYCOMMAND)!=(UINT)-1)
  167. {
  168. if (!(c_IDEffects[nItem].dwEffect & *(pddm->pdwEffect)))
  169. {
  170. RemoveMenu(hmenu, c_IDEffects[nItem].uID, MF_BYCOMMAND);
  171. }
  172. else if (c_IDEffects[nItem].dwEffect == pddm->dwDefEffect)
  173. {
  174. SetMenuDefaultItem(hmenu, c_IDEffects[nItem].uID, MF_BYCOMMAND);
  175. }
  176. }
  177. }
  178. //
  179. // If this dragging is caused by the left button, simply choose
  180. // the default one, otherwise, pop up the context menu. If there
  181. // is no key state info and the original effect is the same as the
  182. // current effect, choose the default one, otherwise pop up the
  183. // context menu.
  184. //
  185. if ((m_grfKeyStateLast & MK_LBUTTON) ||
  186. (!m_grfKeyStateLast && (*(pddm->pdwEffect) == pddm->dwDefEffect)) )
  187. {
  188. idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
  189. // This one MUST be called here. Please read its comment block.
  190. DAD_DragLeave();
  191. if (m_hwnd)
  192. SetForegroundWindow(m_hwnd);
  193. }
  194. else
  195. {
  196. // Note that SHTrackPopupMenu calls DAD_DragLeave().
  197. idCmd = SHTrackPopupMenu(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  198. pddm->pt.x, pddm->pt.y, 0, m_hwnd, NULL);
  199. }
  200. // We also need to call this here to release the dragged image.
  201. DAD_SetDragImage(NULL, NULL);
  202. // Check if the user selected one of add-in menu items.
  203. if (idCmd == 0)
  204. {
  205. hr = S_OK; // Canceled by the user, return S_OK
  206. }
  207. else if (InRange(idCmd, DDIDM_EXTFIRST, DDIDM_EXTLAST))
  208. {
  209. // Yes. Let the context menu handler process it.
  210. CMINVOKECOMMANDINFOEX ici = {
  211. SIZEOF(CMINVOKECOMMANDINFOEX),
  212. 0L,
  213. m_hwnd,
  214. (LPSTR)MAKEINTRESOURCE(idCmd - DDIDM_EXTFIRST),
  215. NULL, NULL,
  216. SW_NORMAL,
  217. };
  218. // record if shift or control was being held down
  219. SetICIKeyModifiers(&ici.fMask);
  220. // We may not want to ignore the error code. (Can happen when you use the context menu
  221. // to create new folders, but I don't know if that can happen here.).
  222. HDXA_LetHandlerProcessCommandEx(hdxa, &ici, NULL);
  223. hr = S_OK;
  224. }
  225. else
  226. {
  227. for (nItem = 0; nItem < ARRAYSIZE(c_IDEffects); ++nItem)
  228. {
  229. if (idCmd == c_IDEffects[nItem].uID)
  230. {
  231. dwEffectOut = c_IDEffects[nItem].dwEffect;
  232. break;
  233. }
  234. }
  235. // if hmenuReplace had menu commands other than DDIDM_COPY,
  236. // DDIDM_MOVE, DDIDM_LINK, and that item was selected,
  237. // this assert will catch it. (dwEffectOut is 0 in this case)
  238. ASSERT(nItem < ARRAYSIZE(c_IDEffects));
  239. hr = S_FALSE;
  240. }
  241. if (hdca)
  242. DCA_Destroy(hdca);
  243. if (hdxa)
  244. HDXA_Destroy(hdxa);
  245. DestroyMenu(hmenu);
  246. pddm->idCmd = idCmd;
  247. }
  248. *(pddm->pdwEffect) = dwEffectOut;
  249. return hr;
  250. }