Leaked source code of windows server 2003
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.

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