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.

1274 lines
36 KiB

  1. //____________________________________________________________________________
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997 - 1999
  5. //
  6. // File: dd.cpp
  7. //
  8. // Contents:
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 8/3/1997 RaviR Created
  15. //____________________________________________________________________________
  16. //
  17. #include "stdafx.h"
  18. #include "AMCDoc.h" // AMC Console Document
  19. #include "amcview.h"
  20. #include "TreeCtrl.h"
  21. #include "cclvctl.h"
  22. #include "amcpriv.h"
  23. #include "mainfrm.h"
  24. #include "rsltitem.h"
  25. #include "conview.h"
  26. /***************************************************************************\
  27. *
  28. * CLASS: CMMCDropSource
  29. *
  30. * PURPOSE: Implements everything required for drop source:
  31. * a) com object to register with OLE
  32. * b) static method to perform drag&drop operation
  33. *
  34. * USAGE: All you need is to invoke CMMCDropSource::ScDoDragDrop static method
  35. *
  36. \***************************************************************************/
  37. class CMMCDropSource :
  38. public IDropSource,
  39. public CComObjectRoot
  40. {
  41. public:
  42. BEGIN_COM_MAP(CMMCDropSource)
  43. COM_INTERFACE_ENTRY(IDropSource)
  44. END_COM_MAP()
  45. // IDropSource methods
  46. STDMETHOD(QueryContinueDrag)( BOOL fEscapePressed, DWORD grfKeyState );
  47. STDMETHOD(GiveFeedback)( DWORD dwEffect );
  48. // method to perform D&D
  49. static SC ScDoDragDrop(IDataObject *pDataObject, bool bCopyAllowed, bool bMoveAllowed);
  50. };
  51. /***************************************************************************\
  52. *
  53. * CLASS: CMMCDropTarget
  54. *
  55. * PURPOSE: Implements com object to be osed by OLE for drop target operations
  56. *
  57. * USAGE: Used by CMMCViewDropTarget, which creates and registers it with OLE
  58. * to be invoked on OLE D&D opeartions on the window (target)
  59. *
  60. \***************************************************************************/
  61. class CMMCDropTarget :
  62. public CTiedComObject<CMMCViewDropTarget>,
  63. public IDropTarget,
  64. public CComObjectRoot
  65. {
  66. public:
  67. typedef CMMCViewDropTarget MyTiedObject;
  68. BEGIN_COM_MAP(CMMCDropTarget)
  69. COM_INTERFACE_ENTRY(IDropTarget)
  70. END_COM_MAP()
  71. // IDropTarget methods
  72. STDMETHOD(DragEnter)( IDataObject * pDataObject, DWORD grfKeyState,
  73. POINTL pt, DWORD * pdwEffect );
  74. STDMETHOD(DragOver)( DWORD grfKeyState, POINTL pt, DWORD * pdwEffect );
  75. STDMETHOD(DragLeave)(void);
  76. STDMETHOD(Drop)( IDataObject * pDataObject, DWORD grfKeyState,
  77. POINTL pt, DWORD * pdwEffect );
  78. private:
  79. // implementation helpers
  80. SC ScDropOnTarget(bool bHitTestOnly, IDataObject * pDataObject, POINTL pt, bool& bCopyOperation);
  81. SC ScRemoveDropTargetHiliting();
  82. static SC ScAddMenuString(CMenu& menu, DWORD id, UINT idString);
  83. SC ScDisplayDropMenu(POINTL pt, DWORD dwEffectsAvailable, DWORD& dwSelected);
  84. DWORD CalculateEffect(DWORD dwEffectsAvailable, DWORD grfKeyState, bool bCopyPreferred);
  85. private:
  86. IDataObjectPtr m_spDataObject; // cached data object
  87. bool m_bRightDrag; // operation is right click drag
  88. bool m_bCopyByDefault; // if default operation is copy (not move)
  89. };
  90. //////////////////////////////////////////////////////////////////////////////
  91. ////////// CAMCTreeView methods for supporting d&d ///////////////////////////
  92. //////////////////////////////////////////////////////////////////////////////
  93. /***************************************************************************\
  94. *
  95. * METHOD: CAMCTreeView::ScDropOnTarget
  96. *
  97. * PURPOSE: called to hittest or perform drop operation
  98. *
  99. * PARAMETERS:
  100. * bool bHitTestOnly [in] - HitTest / drop
  101. * IDataObject * pDataObject [in] - data object to copy/move
  102. * CPoint point [in] - current cursor position
  103. * bool& bCopyOperation [in/out]
  104. * [in] - operation to perform (HitTest == false)
  105. * [out] - default op. (HitTest == true)
  106. *
  107. * RETURNS:
  108. * SC - result code
  109. *
  110. \***************************************************************************/
  111. SC CAMCTreeView::ScDropOnTarget(bool bHitTestOnly, IDataObject * pDataObject, CPoint point, bool& bCopyOperation)
  112. {
  113. DECLARE_SC(sc, TEXT("CAMCTreeView::ScDropOnTarget"));
  114. // 1. see where it falls
  115. CTreeCtrl& ctc = GetTreeCtrl();
  116. UINT flags;
  117. HTREEITEM htiDrop = ctc.HitTest(point, &flags);
  118. if (flags & TVHT_NOWHERE)
  119. return sc = S_FALSE; // not an error, but no paste;
  120. // 2. if we missed the tree item...
  121. if (!htiDrop)
  122. {
  123. // really mad if it was a paste
  124. if (! bHitTestOnly)
  125. MessageBeep(0);
  126. return sc = S_FALSE; // not an error, but no paste;
  127. }
  128. // 3. get the target node
  129. HNODE hNode = (HNODE) ctc.GetItemData(htiDrop);
  130. INodeCallback* pNC = GetNodeCallback();
  131. sc = ScCheckPointers(pNC, E_UNEXPECTED);
  132. if (sc)
  133. return sc;
  134. // 4. ask what snapin thinks about this paste
  135. bool bGetDataObjectFromClipboard = false;
  136. bool bPasteAllowed = false;
  137. bool bIsCopyDefaultOperation = false;
  138. sc = pNC->QueryPaste(hNode, /*bScope*/ true, /*LVDATA*/ NULL,
  139. pDataObject, bPasteAllowed, bIsCopyDefaultOperation);
  140. if (sc)
  141. return sc;
  142. if (!bPasteAllowed)
  143. return sc = S_FALSE; // not an error, but no paste;
  144. // 5. visual effect
  145. ctc.SelectDropTarget(htiDrop);
  146. // 6. OK so far. If it was a test - we passed
  147. if (bHitTestOnly)
  148. {
  149. bCopyOperation = bIsCopyDefaultOperation;
  150. return sc;
  151. }
  152. // 7. do paste NOW
  153. sc = pNC->Drop(hNode, /*bScope*/TRUE, /*LVDATA*/NULL, pDataObject, !bCopyOperation);
  154. if (sc)
  155. return sc;
  156. return sc;
  157. }
  158. /***************************************************************************\
  159. *
  160. * METHOD: CAMCTreeView::RemoveDropTargetHiliting
  161. *
  162. * PURPOSE: called to remove hiliting put for drop target
  163. *
  164. * PARAMETERS:
  165. *
  166. * RETURNS:
  167. * void
  168. *
  169. \***************************************************************************/
  170. void CAMCTreeView::RemoveDropTargetHiliting()
  171. {
  172. CTreeCtrl& ctc = GetTreeCtrl();
  173. ctc.SelectDropTarget(NULL);
  174. }
  175. /***************************************************************************\
  176. *
  177. * METHOD: CAMCTreeView::OnBeginRDrag
  178. *
  179. * PURPOSE: called when the drag operation is initiated with right mouse button
  180. *
  181. * PARAMETERS:
  182. * NMHDR* pNMHDR
  183. * LRESULT* pResult
  184. *
  185. * RETURNS:
  186. * void
  187. *
  188. \***************************************************************************/
  189. void CAMCTreeView::OnBeginRDrag(NMHDR* pNMHDR, LRESULT* pResult)
  190. {
  191. OnBeginDrag(pNMHDR, pResult);
  192. }
  193. /***************************************************************************\
  194. *
  195. * METHOD: CAMCTreeView::OnBeginDrag
  196. *
  197. * PURPOSE: called when the drag operation is initiated with
  198. *
  199. * PARAMETERS:
  200. * NMHDR* pNMHDR
  201. * LRESULT* pResult
  202. *
  203. * RETURNS:
  204. * SC - result code
  205. *
  206. \***************************************************************************/
  207. void CAMCTreeView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
  208. {
  209. DECLARE_SC(sc, TEXT("CAMCTreeView::OnBeginDrag"));
  210. // 1. parameter check
  211. sc = ScCheckPointers( pNMHDR, pResult );
  212. if (sc)
  213. return;
  214. *pResult = 0;
  215. // 2. get node calback
  216. CTreeCtrl& ctc = GetTreeCtrl();
  217. NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
  218. HNODE hNode = (HNODE) ctc.GetItemData(pNMTreeView->itemNew.hItem);
  219. INodeCallback* pNC = GetNodeCallback();
  220. sc = ScCheckPointers( pNC, E_UNEXPECTED );
  221. if (sc)
  222. return;
  223. // 3. get data object
  224. IDataObjectPtr spDO;
  225. bool bCopyAllowed = false;
  226. bool bMoveAllowed = false;
  227. sc = pNC->GetDragDropDataObject(hNode, TRUE, 0, 0, &spDO, bCopyAllowed, bMoveAllowed);
  228. if ( sc != S_OK || spDO == NULL)
  229. return;
  230. // 4. start d&d
  231. sc = CMMCDropSource::ScDoDragDrop(spDO, bCopyAllowed, bMoveAllowed);
  232. if (sc)
  233. return;
  234. }
  235. //////////////////////////////////////////////////////////////////////////////
  236. ////////// CAMCListView methods for supporting d&d ///////////////////////////
  237. //////////////////////////////////////////////////////////////////////////////
  238. // helpers
  239. INodeCallback* CAMCListView::GetNodeCallback()
  240. {
  241. ASSERT (m_pAMCView != NULL);
  242. return (m_pAMCView->GetNodeCallback());
  243. }
  244. HNODE CAMCListView::GetScopePaneSelNode()
  245. {
  246. ASSERT (m_pAMCView != NULL);
  247. return (m_pAMCView->GetSelectedNode());
  248. }
  249. /***************************************************************************\
  250. *
  251. * METHOD: CAMCListView::ScDropOnTarget
  252. *
  253. * PURPOSE: called to hittest or perform drop operation
  254. *
  255. * PARAMETERS:
  256. * bool bHitTestOnly [in] - HitTest / drop
  257. * IDataObject * pDataObject [in] - data object to copy/move
  258. * CPoint point [in] - current cursor position
  259. * bool& bCopyOperation [in/out]
  260. * [in] - operation to perform (HitTest == false)
  261. * [out] - default op. (HitTest == true)
  262. *
  263. * RETURNS:
  264. * SC - result code
  265. *
  266. \***************************************************************************/
  267. SC CAMCListView::ScDropOnTarget(bool bHitTestOnly, IDataObject * pDataObject, CPoint point, bool& bCopyOperation)
  268. {
  269. DECLARE_SC(sc, TEXT("CAMCListView::ScDropOnTarget"));
  270. // 1. sanity check
  271. sc = ScCheckPointers( m_pAMCView , E_UNEXPECTED );
  272. if (sc)
  273. return sc;
  274. // 2. see if view does not have a paste tabu (listpads do)
  275. if (!m_pAMCView->CanDoDragDrop())
  276. return sc = (bHitTestOnly ? S_FALSE : E_FAIL); // not an error if testing
  277. // 3. HitTest the target
  278. HNODE hNode = NULL;
  279. bool bScope = false;
  280. LPARAM lvData = NULL;
  281. int iDrop = -1;
  282. sc = ScGetDropTarget(point, hNode, bScope, lvData, iDrop);
  283. if (sc.IsError() || (sc == SC(S_FALSE)))
  284. return sc;
  285. // 4. get the callback
  286. INodeCallback* pNC = GetNodeCallback();
  287. sc = ScCheckPointers(pNC, E_UNEXPECTED);
  288. if (sc)
  289. return sc;
  290. // 5. ask what snapin thinks about this paste
  291. const bool bGetDataObjectFromClipboard = false;
  292. bool bPasteAllowed = false;
  293. bool bIsCopyDefaultOperation = false;
  294. sc = pNC->QueryPaste(hNode, bScope, lvData,
  295. pDataObject, bPasteAllowed, bIsCopyDefaultOperation);
  296. if (sc)
  297. return sc;
  298. if (!bPasteAllowed)
  299. return sc = S_FALSE; // not an error, but no paste;
  300. // 6. visual effect
  301. SelectDropTarget(iDrop);
  302. // 7. OK so far. If it was a test - we passed
  303. if (bHitTestOnly)
  304. {
  305. bCopyOperation = bIsCopyDefaultOperation;
  306. return sc;
  307. }
  308. // 8. do paste NOW
  309. sc = pNC->Drop(hNode, bScope, lvData, pDataObject, !bCopyOperation);
  310. if (sc)
  311. return sc;
  312. return sc;
  313. }
  314. /***************************************************************************\
  315. *
  316. * METHOD: CAMCListView::RemoveDropTargetHiliting
  317. *
  318. * PURPOSE: called to remove target hiliting
  319. *
  320. * PARAMETERS:
  321. *
  322. * RETURNS:
  323. * void
  324. *
  325. \***************************************************************************/
  326. void CAMCListView::RemoveDropTargetHiliting()
  327. {
  328. SelectDropTarget(-1);
  329. }
  330. //+-------------------------------------------------------------------
  331. //
  332. // Member: CAMCListView::ScGetDropTarget
  333. //
  334. // Synopsis: Get the drop target item (result item or scope item).
  335. //
  336. // Arguments: [point] - where the drop is done.
  337. // [hNode] - Owner node of result pane.
  338. // [bScope] - scope or result selected.
  339. // [lvData] - If result the LPARAM of result item.
  340. // [iDrop] - index of the lv item that is drop target.
  341. //
  342. // Returns: SC, S_FALSE means no drop target item.
  343. //
  344. //--------------------------------------------------------------------
  345. SC CAMCListView::ScGetDropTarget(const CPoint& point, HNODE& hNode, bool& bScope, LPARAM& lvData, int& iDrop)
  346. {
  347. DECLARE_SC(sc, _T("CAMCListView::ScGetDropTarget"));
  348. hNode = NULL;
  349. bScope = false;
  350. lvData = NULL;
  351. iDrop = -1;
  352. CListCtrl& lc = GetListCtrl();
  353. UINT flags;
  354. iDrop = lc.HitTest(point, &flags);
  355. // background is drop target.
  356. if (iDrop < 0)
  357. {
  358. hNode = GetScopePaneSelNode();
  359. bScope = true;
  360. return sc;
  361. }
  362. // Need to change this to LVIS_DROPHILITED.
  363. if (lc.GetItemState(iDrop, LVIS_SELECTED) & LVIS_SELECTED)
  364. {
  365. HWND hWnd = ::GetForegroundWindow();
  366. if (hWnd && (hWnd == m_hWnd))
  367. return (sc = S_FALSE);
  368. }
  369. /*
  370. * virtual list? lvData is the item index, hNode is the item selected
  371. * in the scope pane
  372. */
  373. if (m_bVirtual)
  374. {
  375. hNode = GetScopePaneSelNode();
  376. lvData = iDrop;
  377. bScope = false;
  378. }
  379. else
  380. {
  381. LPARAM lParam = lc.GetItemData(iDrop);
  382. ASSERT (lParam != 0);
  383. if (lParam == 0)
  384. return (sc = S_FALSE);
  385. CResultItem* pri = CResultItem::FromHandle(lParam);
  386. if (pri == NULL)
  387. return (sc = S_FALSE);
  388. if (pri->IsScopeItem())
  389. {
  390. hNode = pri->GetScopeNode();
  391. bScope = true;
  392. }
  393. else
  394. {
  395. hNode = GetScopePaneSelNode();
  396. lvData = lParam;
  397. }
  398. }
  399. sc = ScCheckPointers(hNode, E_UNEXPECTED);
  400. if (sc)
  401. return sc;
  402. return (sc);
  403. }
  404. /***************************************************************************\
  405. *
  406. * METHOD: CAMCListView::OnBeginRDrag
  407. *
  408. * PURPOSE: called when the drag operation is initiated with right mouse button
  409. *
  410. * PARAMETERS:
  411. * NMHDR* pNMHDR
  412. * LRESULT* pResult
  413. *
  414. * RETURNS:
  415. * void
  416. *
  417. \***************************************************************************/
  418. void CAMCListView::OnBeginRDrag(NMHDR* pNMHDR, LRESULT* pResult)
  419. {
  420. OnBeginDrag(pNMHDR, pResult);
  421. }
  422. /***************************************************************************\
  423. *
  424. * METHOD: CAMCListView::OnBeginDrag
  425. *
  426. * PURPOSE: called when the drag operation is initiated
  427. *
  428. * PARAMETERS:
  429. * NMHDR* pNMHDR
  430. * LRESULT* pResult
  431. *
  432. * RETURNS:
  433. * void
  434. *
  435. \***************************************************************************/
  436. void CAMCListView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
  437. {
  438. DECLARE_SC(sc, TEXT("CAMCListView::OnBeginDrag"));
  439. // 1. parameter check
  440. sc = ScCheckPointers( pNMHDR, pResult );
  441. if (sc)
  442. return;
  443. *pResult = 0;
  444. // 2. sanity check
  445. sc = ScCheckPointers( m_pAMCView, E_UNEXPECTED );
  446. if (sc)
  447. return;
  448. // 3. see if view does not have a paste tabu (listpads do)
  449. if (!m_pAMCView->CanDoDragDrop())
  450. return;
  451. // 4. get selected items
  452. CListCtrl& lc = GetListCtrl();
  453. UINT cSel = lc.GetSelectedCount();
  454. if (cSel <= 0)
  455. {
  456. sc = E_UNEXPECTED;
  457. return;
  458. }
  459. // 5. get node callback
  460. HNODE hNode = GetScopePaneSelNode();
  461. long iSel = lc.GetNextItem(-1, LVIS_SELECTED);
  462. LONG_PTR lvData = m_bVirtual ? iSel : lc.GetItemData(iSel);
  463. INodeCallback* pNC = GetNodeCallback();
  464. sc = ScCheckPointers( pNC, E_UNEXPECTED );
  465. if (sc)
  466. return;
  467. // 6. retrieve data object
  468. IDataObjectPtr spDO;
  469. bool bCopyAllowed = false;
  470. bool bMoveAllowed = false;
  471. sc = pNC->GetDragDropDataObject(hNode, FALSE, (cSel > 1), lvData, &spDO, bCopyAllowed, bMoveAllowed);
  472. if ( sc != S_OK || spDO == NULL)
  473. {
  474. /*
  475. * Problem:
  476. * If snapin does not return dataobject then drag & drop is not possible.
  477. * Assume focus in on tree, user downclick's and drags a result item
  478. * At this time common control sends LVN_ITEMCHANGED to mmc. MMC translates
  479. * this to MMCN_SELECT and tells snapin that result item is selected.
  480. * Now when user releases the mouse the tree item still has focus & selection,
  481. * but the snapin thinks result item is selected (also verbs correspond to
  482. * result item).
  483. *
  484. * Solution:
  485. * So we will set focus to the result pane.
  486. */
  487. sc = m_pAMCView->ScSetFocusToPane(CConsoleView::ePane_Results);
  488. return;
  489. }
  490. // 7. do d&d
  491. sc = CMMCDropSource::ScDoDragDrop(spDO, bCopyAllowed, bMoveAllowed);
  492. if (sc)
  493. return;
  494. /*
  495. * Problem:
  496. * If a result item is dropped into another result item or another
  497. * scope item in list-view then focus disappears from that item.
  498. * But there should be always an item selected after any de-select.
  499. * We cannot change the focus to result pane because if focus is already
  500. * in result pane, this change focus does nothing and no item is selected.
  501. * So we change the focus to scope pane. For this we first change the focus
  502. * to result pane and then to scope pane. Because if tree already has focus,
  503. * setting focus to tree does nothing (CAMCView::ScOnTreeViewActivated).
  504. *
  505. * Solution:
  506. * So we change the focus to result pane and then to scope pane.
  507. */
  508. sc = m_pAMCView->ScSetFocusToPane(CConsoleView::ePane_Results);
  509. if (sc)
  510. return;
  511. sc = m_pAMCView->ScSetFocusToPane(CConsoleView::ePane_ScopeTree);
  512. if (sc)
  513. return;
  514. return;
  515. }
  516. //////////////////////////////////////////////////////////////////////////////
  517. ///////////////////// CMMCViewDropTarget methods /////////////////////////////
  518. //////////////////////////////////////////////////////////////////////////////
  519. /***************************************************************************\
  520. *
  521. * METHOD: CMMCViewDropTarget::CMMCViewDropTarget
  522. *
  523. * PURPOSE: constructor
  524. *
  525. * PARAMETERS:
  526. *
  527. \***************************************************************************/
  528. CMMCViewDropTarget::CMMCViewDropTarget() : m_hwndOwner(0)
  529. {
  530. }
  531. /***************************************************************************\
  532. *
  533. * METHOD: CMMCViewDropTarget::~CMMCViewDropTarget
  534. *
  535. * PURPOSE: destructor. Revokes drop target for derived view class
  536. *
  537. \***************************************************************************/
  538. CMMCViewDropTarget::~CMMCViewDropTarget()
  539. {
  540. DECLARE_SC(sc, TEXT("CViewDropTarget::~CViewDropTarget"));
  541. if (m_hwndOwner != NULL)
  542. sc = RevokeDragDrop(m_hwndOwner);
  543. }
  544. /***************************************************************************\
  545. *
  546. * METHOD: CMMCViewDropTarget::ScRegisterAsDropTarget
  547. *
  548. * PURPOSE: This method is called by the derived class after the view window
  549. * is created. Method registers the drop target for the window.
  550. *
  551. * PARAMETERS:
  552. * HWND hWnd [in] - drop target view handle
  553. *
  554. * RETURNS:
  555. * SC - result code
  556. *
  557. \***************************************************************************/
  558. SC CMMCViewDropTarget::ScRegisterAsDropTarget(HWND hWnd)
  559. {
  560. DECLARE_SC(sc, TEXT("CMMCViewDropTarget::ScRegister"));
  561. // 1. parameter check
  562. if (hWnd == NULL)
  563. return sc = E_INVALIDARG;
  564. // 2. sanity check - should not come here twice
  565. if (m_spTarget != NULL)
  566. return sc = E_UNEXPECTED;
  567. // 3. create a drop target com object
  568. IDropTargetPtr spTarget;
  569. sc = ScCreateTarget(&spTarget);
  570. if (sc)
  571. return sc;
  572. // 4. recheck
  573. sc = ScCheckPointers(spTarget, E_UNEXPECTED);
  574. if (sc)
  575. return sc;
  576. // 5. register with OLE
  577. sc = RegisterDragDrop(hWnd, spTarget);
  578. if (sc)
  579. return sc;
  580. // 6. store info into members
  581. m_spTarget.Attach( spTarget.Detach() );
  582. m_hwndOwner = hWnd;
  583. return sc;
  584. }
  585. /***************************************************************************\
  586. *
  587. * METHOD: CMMCViewDropTarget::ScCreateTarget
  588. *
  589. * PURPOSE: helper. creates tied com object to regiter with OLE
  590. *
  591. * PARAMETERS:
  592. * IDropTarget **ppTarget [out] tied com object
  593. *
  594. * RETURNS:
  595. * SC - result code
  596. *
  597. \***************************************************************************/
  598. SC CMMCViewDropTarget::ScCreateTarget(IDropTarget **ppTarget)
  599. {
  600. DECLARE_SC(sc, TEXT("CMMCViewDropTarget::ScCreateTarget"));
  601. // 1. check parameters
  602. sc = ScCheckPointers(ppTarget);
  603. if (sc)
  604. return sc;
  605. // 2. init out parameter
  606. *ppTarget = NULL;
  607. // 3. create com object to register as target
  608. IDropTargetPtr spDropTarget;
  609. sc = CTiedComObjectCreator<CMMCDropTarget>::ScCreateAndConnect(*this, spDropTarget);
  610. if (sc)
  611. return sc;
  612. sc = ScCheckPointers(spDropTarget, E_UNEXPECTED);
  613. if (sc)
  614. return sc;
  615. // 4. pass reference to client
  616. *ppTarget = spDropTarget.Detach();
  617. return sc;
  618. }
  619. /*---------------------------------------------------------------------------*\
  620. | class CMMCDropSource methods |
  621. \*---------------------------------------------------------------------------*/
  622. /***************************************************************************\
  623. *
  624. * METHOD: CMMCDropSource::QueryContinueDrag
  625. *
  626. * PURPOSE: implements IDropSource::QueryContinueDrag interface used bu OLE
  627. *
  628. * PARAMETERS:
  629. * BOOL fEscapePressed [in] - ESC was pressed
  630. * DWORD grfKeyState [in] - mouse & control button state
  631. *
  632. * RETURNS:
  633. * HRESULT - error or S_OK(continue), DRAGDROP_S_CANCEL(cancel), DRAGDROP_S_DROP(drop)
  634. *
  635. \***************************************************************************/
  636. STDMETHODIMP CMMCDropSource::QueryContinueDrag( BOOL fEscapePressed, DWORD grfKeyState )
  637. {
  638. DECLARE_SC(sc, TEXT("CMMCDropSource::QueryContinueDrag"));
  639. // 1. quit on cancel
  640. if (fEscapePressed)
  641. return (sc = DRAGDROP_S_CANCEL).ToHr();
  642. // 2. inspect mouse buttons
  643. DWORD mButtons = (grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON) );
  644. if ( mButtons == 0 ) // all released?
  645. return (sc = DRAGDROP_S_DROP).ToHr();
  646. // 3. quit also if more than one mouse button is pressed
  647. if ( mButtons != MK_LBUTTON && mButtons != MK_RBUTTON && mButtons != MK_MBUTTON )
  648. return (sc = DRAGDROP_S_CANCEL).ToHr();
  649. // 4. else just continue ...
  650. return sc.ToHr();
  651. }
  652. /***************************************************************************\
  653. *
  654. * METHOD: CMMCDropSource::GiveFeedback
  655. *
  656. * PURPOSE: Gives feedback for d&d operations
  657. *
  658. * PARAMETERS:
  659. * DWORD dwEffect
  660. *
  661. * RETURNS:
  662. * DRAGDROP_S_USEDEFAULTCURSORS
  663. *
  664. \***************************************************************************/
  665. STDMETHODIMP CMMCDropSource::GiveFeedback( DWORD dwEffect )
  666. {
  667. // nothing special by now
  668. return DRAGDROP_S_USEDEFAULTCURSORS;
  669. }
  670. /***************************************************************************\
  671. *
  672. * METHOD: CMMCDropSource::ScDoDragDrop
  673. *
  674. * PURPOSE: Performs DragAndDrop operation
  675. * This is static method to be used to initiate drag and drop
  676. *
  677. * PARAMETERS:
  678. * IDataObject *pDataObject [in] data object to copy/move
  679. * bool bCopyAllowed [in] if copy is allowed
  680. * bool bMoveAllowed [in] if move is allowed
  681. *
  682. * RETURNS:
  683. * SC - result code
  684. *
  685. \***************************************************************************/
  686. SC CMMCDropSource::ScDoDragDrop(IDataObject *pDataObject, bool bCopyAllowed, bool bMoveAllowed)
  687. {
  688. DECLARE_SC(sc, TEXT("CMMCDropSource::ScDoDragDrop"));
  689. // 1. cocreate com object for OLE
  690. typedef CComObject<CMMCDropSource> ComCMMCDropSource;
  691. ComCMMCDropSource *pSource;
  692. sc = ComCMMCDropSource::CreateInstance(&pSource);
  693. if (sc)
  694. return sc;
  695. // 2. recheck
  696. sc = ScCheckPointers(pSource, E_UNEXPECTED);
  697. if (sc)
  698. return sc;
  699. // 3. QI for IDropSource interface
  700. IDropSourcePtr spDropSource = pSource;
  701. sc = ScCheckPointers(spDropSource, E_UNEXPECTED);
  702. if (sc)
  703. {
  704. delete pSource;
  705. return sc;
  706. }
  707. // 4. perform DragDrop
  708. DWORD dwEffect = DROPEFFECT_NONE;
  709. const DWORD dwEffectAvailable = (bCopyAllowed ? DROPEFFECT_COPY : 0)
  710. |(bMoveAllowed ? DROPEFFECT_MOVE : 0);
  711. sc = DoDragDrop(pDataObject, spDropSource, dwEffectAvailable, &dwEffect);
  712. if (sc)
  713. return sc;
  714. return sc;
  715. }
  716. /*---------------------------------------------------------------------------*\
  717. | class CMMCDropTarget methods |
  718. \*---------------------------------------------------------------------------*/
  719. /***************************************************************************\
  720. *
  721. * METHOD: CMMCDropTarget::DragEnter
  722. *
  723. * PURPOSE: Invoked by OLE when d&d cursor enters the window for which
  724. * this target was registered.
  725. *
  726. * PARAMETERS:
  727. * IDataObject * pDataObject [in] - data object to copy/move
  728. * DWORD grfKeyState [in] - current key state
  729. * POINTL pt [in] - current cursor position
  730. * DWORD * pdwEffect [out] - operations supported
  731. *
  732. * RETURNS:
  733. * HRESULT - result code
  734. *
  735. \***************************************************************************/
  736. STDMETHODIMP CMMCDropTarget::DragEnter( IDataObject * pDataObject, DWORD grfKeyState,
  737. POINTL pt, DWORD * pdwEffect )
  738. {
  739. DECLARE_SC(sc, TEXT("CMMCDropTarget::DragEnter"));
  740. // 1. cache for drag over
  741. m_spDataObject = pDataObject;
  742. // 2. parameter check
  743. sc = ScCheckPointers(pDataObject, pdwEffect);
  744. if (sc)
  745. return sc.ToHr();
  746. // 3. let it happen - will do more exact filtering on DragOver
  747. *pdwEffect = DROPEFFECT_MOVE | DROPEFFECT_COPY;
  748. return sc.ToHr();
  749. }
  750. /***************************************************************************\
  751. *
  752. * METHOD: CMMCDropTarget::DragOver
  753. *
  754. * PURPOSE: called continuosly while cursor is drgged over the window
  755. *
  756. * PARAMETERS:
  757. * DWORD grfKeyState [in] - current key state
  758. * POINTL pt [in] - current cursor position
  759. * DWORD * pdwEffect [out] - operations supported
  760. *
  761. * RETURNS:
  762. * HRESULT - result code
  763. *
  764. \***************************************************************************/
  765. STDMETHODIMP CMMCDropTarget::DragOver( DWORD grfKeyState, POINTL pt, DWORD * pdwEffect )
  766. {
  767. DECLARE_SC(sc, TEXT("CMMCDropTarget::DragOver"));
  768. // 1. parameter check
  769. sc = ScCheckPointers(pdwEffect);
  770. if (sc)
  771. return sc.ToHr();
  772. // 2. sanity check
  773. sc = ScCheckPointers(m_spDataObject, E_UNEXPECTED);
  774. if (sc)
  775. return sc.ToHr();
  776. bool bCopyByDefault = false; // initially we are to move
  777. // 3. ask the view to estimate what can be done in this position
  778. sc = ScDropOnTarget( true /*bHitTestOnly*/, m_spDataObject, pt, bCopyByDefault );
  779. if ( sc == S_OK )
  780. *pdwEffect = CalculateEffect( *pdwEffect, grfKeyState, bCopyByDefault );
  781. else
  782. *pdwEffect = DROPEFFECT_NONE; // no-op on failure or S_FALSE
  783. return sc.ToHr();
  784. }
  785. /***************************************************************************\
  786. *
  787. * METHOD: CMMCDropTarget::DragLeave
  788. *
  789. * PURPOSE: called when cursor leave the window
  790. *
  791. * PARAMETERS:
  792. * void
  793. *
  794. * RETURNS:
  795. * HRESULT - result code
  796. *
  797. \***************************************************************************/
  798. STDMETHODIMP CMMCDropTarget::DragLeave(void)
  799. {
  800. DECLARE_SC(sc, TEXT("DragLeave"));
  801. // 1. release data object
  802. m_spDataObject = NULL;
  803. // 2. ask the view to remove hiliting it put on target
  804. sc = ScRemoveDropTargetHiliting();
  805. if (sc)
  806. sc.TraceAndClear();
  807. return S_OK;
  808. }
  809. /***************************************************************************\
  810. *
  811. * METHOD: CMMCDropTarget::Drop
  812. *
  813. * PURPOSE: Called when data is dropped on target
  814. *
  815. * PARAMETERS:
  816. * IDataObject * pDataObject [in] - data object to copy/move
  817. * DWORD grfKeyState [in] - current key state
  818. * POINTL pt [in] - current cursor position
  819. * DWORD * pdwEffect [out] - operation performed
  820. *
  821. * RETURNS:
  822. * HRESULT - result code
  823. *
  824. \***************************************************************************/
  825. STDMETHODIMP CMMCDropTarget::Drop( IDataObject * pDataObject, DWORD grfKeyState,
  826. POINTL pt, DWORD * pdwEffect )
  827. {
  828. DECLARE_SC(sc, TEXT("CMMCDropTarget::DragEnter"));
  829. // 1. release in case we have anything
  830. m_spDataObject = NULL;
  831. // 2. parameter check
  832. sc = ScCheckPointers(pDataObject, pdwEffect);
  833. if (sc)
  834. return sc.ToHr();
  835. // 3. init operation with cached value
  836. bool bCopyOperation = m_bCopyByDefault;
  837. // 4. see what operation to perform
  838. if (m_bRightDrag)
  839. {
  840. // 4.1. give user the choice
  841. DWORD dwSelected = ( m_bCopyByDefault ? DROPEFFECT_COPY : DROPEFFECT_MOVE );
  842. sc = ScDisplayDropMenu( pt, *pdwEffect, dwSelected );
  843. if (sc)
  844. return sc.ToHr();
  845. *pdwEffect = dwSelected;
  846. }
  847. else
  848. {
  849. // 4.2. inspect keyboard
  850. *pdwEffect = CalculateEffect(*pdwEffect, grfKeyState, bCopyOperation);
  851. }
  852. // 5. perform
  853. if (*pdwEffect != DROPEFFECT_NONE) // not canceled yet?
  854. {
  855. // now the final decision - copy or move
  856. bCopyOperation = ( *pdwEffect & DROPEFFECT_COPY );
  857. // Let it happen. Drop.
  858. sc = ScDropOnTarget( false /*bHitTestOnly*/, pDataObject, pt, bCopyOperation );
  859. if ( sc != S_OK )
  860. *pdwEffect = DROPEFFECT_NONE;
  861. }
  862. // 6. remove hiliting. reuse DragLeave (don't care about the results)
  863. DragLeave();
  864. return sc.ToHr();
  865. }
  866. /***************************************************************************\
  867. *
  868. * METHOD: CMMCDropTarget::ScDropOnTarget
  869. *
  870. * PURPOSE: helper, forwarding calls to the view
  871. * Called as a request to hittest / perform drop operation
  872. *
  873. * PARAMETERS:
  874. * bool bHitTestOnly [in] - HitTest / drop
  875. * IDataObject * pDataObject [in] - data object to copy/move
  876. * POINTL pt [in] - current cursor position
  877. * bool& bCopyOperation [in/out]
  878. * [in] - operation to perform (HitTest == false)
  879. * [out] - default op. (HitTest == true)
  880. * RETURNS:
  881. * SC - result code
  882. *
  883. \***************************************************************************/
  884. SC CMMCDropTarget::ScDropOnTarget(bool bHitTestOnly, IDataObject * pDataObject, POINTL pt, bool& bCopyOperation)
  885. {
  886. DECLARE_SC(sc, TEXT("CMMCDropTarget::ScDropOnTarget"));
  887. // 1. get tied object - view
  888. CMMCViewDropTarget *pTarget = NULL;
  889. sc = ScGetTiedObject(pTarget);
  890. if (sc)
  891. return sc;
  892. // 2. recheck
  893. sc = ScCheckPointers(pTarget, E_UNEXPECTED);
  894. if (sc)
  895. return sc;
  896. // 3. calculate client coordinates
  897. CPoint point(pt.x, pt.y);
  898. ScreenToClient(pTarget->GetWindowHandle(), &point);
  899. // 4. forward to the view
  900. sc = pTarget->ScDropOnTarget( bHitTestOnly, pDataObject, point, bCopyOperation );
  901. if ( sc != S_OK )
  902. ScRemoveDropTargetHiliting(); // remove hiliting if missed the traget
  903. if (sc)
  904. return sc;
  905. return sc;
  906. }
  907. /***************************************************************************\
  908. *
  909. * METHOD: CMMCDropTarget::ScRemoveDropTargetHiliting
  910. *
  911. * PURPOSE: helper, forwarding calls to the view
  912. * Called to cancel visual effects on target
  913. *
  914. * PARAMETERS:
  915. *
  916. * RETURNS:
  917. * SC - result code
  918. *
  919. \***************************************************************************/
  920. SC CMMCDropTarget::ScRemoveDropTargetHiliting()
  921. {
  922. DECLARE_SC(sc, TEXT("CMMCDropTarget::ScRemoveDropTargetHiliting"));
  923. // `. get tied object - view
  924. CMMCViewDropTarget *pTarget = NULL;
  925. sc = ScGetTiedObject(pTarget);
  926. if (sc)
  927. return sc;
  928. sc = ScCheckPointers(pTarget, E_UNEXPECTED);
  929. if (sc)
  930. return sc;
  931. // 2. forward to the view
  932. pTarget->RemoveDropTargetHiliting();
  933. return sc;
  934. }
  935. /***************************************************************************\
  936. *
  937. * METHOD: CMMCDropTarget::ScAddMenuString
  938. *
  939. * PURPOSE: Helper. Adds resource string to paste context menu
  940. *
  941. * PARAMETERS:
  942. * CMenu& menu [in] - menu to modify
  943. * DWORD id [in] - menu command
  944. * UINT idString [in] - id of resource string
  945. *
  946. * RETURNS:
  947. * SC - result code
  948. *
  949. \***************************************************************************/
  950. SC CMMCDropTarget::ScAddMenuString(CMenu& menu, DWORD id, UINT idString)
  951. {
  952. DECLARE_SC(sc, TEXT("CMMCDropTarget::ScAddMenuString"));
  953. // 1. load the string
  954. CString strItem;
  955. bool bOK = LoadString( strItem, idString );
  956. if ( !bOK )
  957. return sc = E_FAIL;
  958. // 2. add to the menu
  959. bOK = menu.AppendMenu( MF_STRING, id, strItem );
  960. if ( !bOK )
  961. return sc = E_FAIL;
  962. return sc;
  963. }
  964. /***************************************************************************\
  965. *
  966. * METHOD: CMMCDropTarget::ScDisplayDropMenu
  967. *
  968. * PURPOSE: Helper. Displays paste context menu
  969. *
  970. * PARAMETERS:
  971. * POINTL pt [in] - point where the menu should appear
  972. * DWORD dwEffectsAvailable [in] - available commands
  973. * DWORD& dwSelected [in] - selected command
  974. *
  975. * RETURNS:
  976. * SC - result code
  977. *
  978. \***************************************************************************/
  979. SC CMMCDropTarget::ScDisplayDropMenu(POINTL pt, DWORD dwEffectsAvailable, DWORD& dwSelected)
  980. {
  981. DECLARE_SC(sc, TEXT("CMMCDropTarget::ScDisplayDropMenu"));
  982. CMenu menu;
  983. // 0. create the menu
  984. bool bOK = menu.CreatePopupMenu();
  985. if ( !bOK )
  986. return sc = E_FAIL;
  987. // 1. add choice for copy
  988. if ( dwEffectsAvailable & DROPEFFECT_COPY )
  989. {
  990. sc = ScAddMenuString(menu, DROPEFFECT_COPY, IDS_DragDrop_CopyHere);
  991. if (sc)
  992. return sc;
  993. }
  994. // 2. add choice for move
  995. if ( dwEffectsAvailable & DROPEFFECT_MOVE )
  996. {
  997. sc = ScAddMenuString(menu, DROPEFFECT_MOVE, IDS_DragDrop_MoveHere);
  998. if (sc)
  999. return sc;
  1000. }
  1001. // 3. add separator if copy or paste was added
  1002. if ( dwEffectsAvailable & ( DROPEFFECT_COPY | DROPEFFECT_MOVE ) )
  1003. {
  1004. bool bOK = menu.AppendMenu( MF_SEPARATOR );
  1005. if ( !bOK )
  1006. return sc = E_FAIL;
  1007. }
  1008. // 4. always add choice for cancel
  1009. sc = ScAddMenuString(menu, DROPEFFECT_NONE, IDS_DragDrop_Cancel);
  1010. if (sc)
  1011. return sc;
  1012. // 5. set the default item
  1013. if ( dwSelected != DROPEFFECT_NONE )
  1014. {
  1015. bool bOK = menu.SetDefaultItem( dwSelected );
  1016. if ( !bOK )
  1017. return sc = E_FAIL;
  1018. }
  1019. // 6. find the tied object
  1020. CMMCViewDropTarget *pTarget = NULL;
  1021. sc = ScGetTiedObject(pTarget);
  1022. if (sc)
  1023. return sc;
  1024. sc = ScCheckPointers(pTarget, E_UNEXPECTED);
  1025. if (sc)
  1026. return sc;
  1027. // 7. display the menu
  1028. dwSelected = menu.TrackPopupMenu(TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
  1029. pt.x, pt.y, CWnd::FromHandlePermanent( pTarget->GetWindowHandle() ) );
  1030. return sc;
  1031. }
  1032. /***************************************************************************\
  1033. *
  1034. * METHOD: CMMCDropTarget::CalculateEffect
  1035. *
  1036. * PURPOSE: Helper. calculates drop effect by combining:
  1037. * a) available operations
  1038. * b) the default operation
  1039. * c) keyboard key combination
  1040. *
  1041. * PARAMETERS:
  1042. * DWORD dwEffectsAvailable [in] available operations
  1043. * DWORD grfKeyState [in] keyboard / mouse state
  1044. * bool bCopyPreferred [in] default operation
  1045. *
  1046. * RETURNS:
  1047. * SC - result code
  1048. *
  1049. \***************************************************************************/
  1050. DWORD CMMCDropTarget::CalculateEffect(DWORD dwEffectsAvailable, DWORD grfKeyState, bool bCopyPreferred)
  1051. {
  1052. const bool bShiftPressed = (grfKeyState & MK_SHIFT);
  1053. const bool bControlPressed = (grfKeyState & MK_CONTROL);
  1054. const bool bRightClickDrag = (grfKeyState & MK_RBUTTON);
  1055. m_bRightDrag = bRightClickDrag;
  1056. m_bCopyByDefault = bCopyPreferred;
  1057. if (!bRightClickDrag) // affected by keyboard only in non-right-drag
  1058. {
  1059. // do nothing if user holds on shift+control
  1060. if ( bShiftPressed && bControlPressed )
  1061. return DROPEFFECT_NONE;
  1062. // modify by user interactive preferences
  1063. if ( bShiftPressed )
  1064. {
  1065. // if user cannot get what he wants to - indicate it
  1066. if ( !(dwEffectsAvailable & DROPEFFECT_MOVE) )
  1067. return DROPEFFECT_NONE;
  1068. bCopyPreferred = false;
  1069. }
  1070. else if ( bControlPressed )
  1071. {
  1072. // if user cannot get what he wants to - indicate it
  1073. if ( !(dwEffectsAvailable & DROPEFFECT_COPY) )
  1074. return DROPEFFECT_NONE;
  1075. bCopyPreferred = true;
  1076. }
  1077. }
  1078. // return preferred, if available
  1079. if ( bCopyPreferred && (dwEffectsAvailable & DROPEFFECT_COPY) )
  1080. return DROPEFFECT_COPY;
  1081. if ( !bCopyPreferred && (dwEffectsAvailable & DROPEFFECT_MOVE) )
  1082. return DROPEFFECT_MOVE;
  1083. // preferred not available - return what is available
  1084. if ( dwEffectsAvailable & DROPEFFECT_COPY )
  1085. {
  1086. m_bCopyByDefault = true;
  1087. return DROPEFFECT_COPY;
  1088. }
  1089. else if ( dwEffectsAvailable & DROPEFFECT_MOVE )
  1090. {
  1091. m_bCopyByDefault = false;
  1092. return DROPEFFECT_MOVE;
  1093. }
  1094. return DROPEFFECT_NONE;
  1095. }