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.

2541 lines
69 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  5. //
  6. // File: drag.cpp
  7. //
  8. // Contents: Api's for doing drag'n'drop
  9. //
  10. // Classes: CPoint
  11. // CDragOperation
  12. // CDropTarget
  13. //
  14. // History: dd-mmm-yy Author Comment
  15. // 08-Nov-94 alexgo converted to PrivDragDrop rpc
  16. // for Drag Drop protocol
  17. // 20-Oct-94 alexgo added Win3.1 style drag drop
  18. // for Chicago/NT shell
  19. // 30-Sep-94 ricksa Drag/Drop optimization.
  20. // 18-Jul-94 ricksa made cursors work in shared WOW
  21. // 21-Apr-94 ricksa made drag/drop handle WM_CANCELMODE
  22. // 04-Apr-94 ricksa rewrote DoDragDrop loop
  23. // 11-Jan-94 alexgo added VDATEHEAP to every function
  24. // 29-Dec-93 alexgo converted to RPC alogirithm for
  25. // getting IDropTarget, etc.
  26. // 06-Dec-93 alexgo commented, formatted
  27. // 93/94 Johann Posch (JohannP) created Drag/Drop for Ole 16 bit
  28. //
  29. // Notes:
  30. //
  31. // RPC Drag Drop algorithm:
  32. //
  33. // During a drag drop operation, the user is moving the mouse around
  34. // the screen, passing over many windows. For each window the mouse
  35. // is over, we need to determine if the window is a drop target.
  36. // If it is, then we remote the IDropTarget interface to the DropSource
  37. // so that the correct visual feedbacks can be given.
  38. //
  39. // To accomplish this, RegisterDragDrop adds two properties to the
  40. // drop target window: a public property, EndPoint ID (provided to
  41. // us by compobj), and a private property (available only to the calling
  42. // process), the IDropTarget pointer.
  43. //
  44. // During the DoDragDrop loop, we ask compobj to test each window for
  45. // the EndpointID property. If it is there, compobj (via
  46. // GetInterfaceFromWindowProp), then we will rpc to the drop target
  47. // process, get the IDropTarget pointer and marshal it back to the
  48. // drop source process. We also install a custom message filter to
  49. // ensure that messages (particularly mouse move messages) are handled
  50. // correctly.
  51. //
  52. // RevokeDragDrop simply removes the above mentioned properties from
  53. // the window handle.
  54. //
  55. // Because in Win32, you can always switch windows and mouse capture
  56. // depends on having the mouse button down, drag/drop processing
  57. // is changed slightly. Whenever, the user does an operation that
  58. // would switch windows, the clipboard window that we use for capture
  59. // will get a WM_CANCELMODE. It will notify the drag operation and
  60. // the drag operation will proceed as if the user aborted the operation.
  61. //
  62. //
  63. // Win 3.1 DragDrop algorithm:
  64. //
  65. // Win3.1 apps can register a window as a drop target via DragAcceptFiles.
  66. // This API sets the WS_EX_ACCEPTFILES bit in the window style.
  67. //
  68. // In Win3.1, these apps would get a WM_DROPFILES message when
  69. // files where dropped on them. An hglobal with the filenames is
  70. // sent in the wparam of WM_DROPFILES.
  71. //
  72. // In Chicago and NT3.5, CF_HDROP is a new clipboard format that is
  73. // identical to the data sent in WM_DROPFILES. If we see this format
  74. // available in a data object passed to DoDragDrop, then we enter
  75. // into our Win31 compatibility mode (which affects finding a drop
  76. // target).
  77. //
  78. // When finding a drop target for a given window, we check to
  79. // see if a window in the hierarchy is registered as a Win31 drop
  80. // target. If so, then we create a wrapper drop target. This wrapper
  81. // drop target will forward calls to the real drop target (if available).
  82. //
  83. // With Win3.1 drag drop, we can do a COPY. If the OLE target indicates
  84. // that no OLE drop can be performed (by returning DROPEFFECT_NONE),
  85. // then we substitute in DROPEFFECT_COPY.
  86. //
  87. // On Drop, if the OLE target chooses not to accept the drop, then
  88. // we will post the window a WM_DROPFILES message with the hglobal
  89. // obtained from IDataObject::GetData(CF_HDROP).
  90. //
  91. //--------------------------------------------------------------------------
  92. #include <le2int.h>
  93. // Note: Enable including native user APIs
  94. // for stack switching
  95. #include <userapis.h>
  96. #pragma SEG(drag)
  97. #include <getif.hxx>
  98. #include <dragopt.h>
  99. #include <resource.h>
  100. #include "enumgen.h"
  101. #include "clipbrd.h"
  102. #include "drag.h"
  103. NAME_SEG(Drag)
  104. ASSERTDATA
  105. ATOM g_aEndPointAtom;
  106. // DROPFILES is the structure of data contained in the CF_HDROP format.
  107. // However, this is private to the shell, so it is not declared in any
  108. // header files.
  109. typedef struct _DROPFILES {
  110. DWORD pFiles; // offset of file list
  111. POINTL pt; // drop point (client coords)
  112. DWORD fNC; // is it on NonClient area
  113. // and pt is in screen coords
  114. DWORD fWide; // WIDE character switch
  115. } DROPFILES, FAR * LPDROPFILES;
  116. #define WM_NCMOUSEFIRST 0x00A0
  117. #define WM_NCMOUSELAST 0x00A9
  118. // From ido.cpp to create shared memory formats
  119. HANDLE CreateSharedDragFormats(IDataObject *pIDataObject);
  120. #define VK_ALT VK_MENU
  121. static const struct {
  122. int keyCode;
  123. WPARAM keyFlag;
  124. } vKeyMap [] = {
  125. { VK_LBUTTON, MK_LBUTTON },
  126. { VK_RBUTTON, MK_RBUTTON },
  127. { VK_MBUTTON, MK_MBUTTON },
  128. { VK_ALT , MK_ALT },
  129. { VK_SHIFT , MK_SHIFT },
  130. { VK_CONTROL, MK_CONTROL }
  131. };
  132. // This is the default cursor object for 32 bit apps. Only one such object
  133. // is needed for 32 bit apps. 16 bit apps need one per shared WOW application
  134. // that is running.
  135. CDragDefaultCursors *cddcDefault32 = NULL;
  136. extern ATOM g_aDropTarget;
  137. extern ATOM g_aDropTargetMarshalHwnd;
  138. //+-------------------------------------------------------------------------
  139. //
  140. // Member: DragDropProcessUninitialize
  141. //
  142. // Synopsis: Does any Unitialization necessary at OleUninitialize time.
  143. // for the last Unitialize for the Process
  144. //
  145. // Returns: none
  146. //
  147. // Algorithm:
  148. // History: dd-mmm-yy Author Comment
  149. // 18-Jul-94 rogerg Created
  150. //
  151. // Note: We need a per thread default cursor object in WOW because
  152. // of the clean up that WOW does. For 32 bit apps, we just use
  153. // one for the entire process.
  154. //
  155. //--------------------------------------------------------------------------
  156. void DragDropProcessUninitialize(void)
  157. {
  158. if (NULL != cddcDefault32)
  159. {
  160. delete cddcDefault32;
  161. cddcDefault32 = NULL;
  162. }
  163. }
  164. //+-------------------------------------------------------------------------
  165. //
  166. // Member: CDragDefaultCursors::GetDefaultCursorObject, static
  167. //
  168. // Synopsis: Get appropriate pointer to default cursor object
  169. //
  170. // Returns: NULL - error occurred
  171. // ~NULL - pointer to appropriate default cursor table
  172. //
  173. // Algorithm: If we are in a 32 bit app, just get a pointer to the
  174. // single cursor table. In 16 bit, get the per thread cursor
  175. // table. If there is none, then allocate and initialize it.
  176. //
  177. // History: dd-mmm-yy Author Comment
  178. // 18-Jul-94 Ricksa Created
  179. //
  180. // Note: We need a per thread default cursor object in WOW because
  181. // of the clean up that WOW does. For 32 bit apps, we just use
  182. // one for the entire process.
  183. //
  184. //--------------------------------------------------------------------------
  185. CDragDefaultCursors *CDragDefaultCursors::GetDefaultCursorObject(void)
  186. {
  187. if (!IsWOWThread())
  188. {
  189. // If we aren't in WOW, we can use the single common default cursor
  190. // object. We make sure that it is initialized before we use it.
  191. if (NULL == cddcDefault32)
  192. {
  193. cddcDefault32 = new CDragDefaultCursors;
  194. if (cddcDefault32)
  195. {
  196. if (!cddcDefault32->Init())
  197. {
  198. delete cddcDefault32;
  199. cddcDefault32 = NULL;
  200. }
  201. }
  202. }
  203. return cddcDefault32;
  204. }
  205. COleTls tls;
  206. // We are in WOW. Get the cursor object if it has already been allocated
  207. CDragDefaultCursors *pccdc16 = (CDragDefaultCursors *) tls->pDragCursors;
  208. if (pccdc16 == NULL)
  209. {
  210. // No cursor table so allocate it -- Please note that we take advantage
  211. // of the fact that this object has only the default constructor by
  212. // simply allocating it rather than "newing" it. The point is that
  213. // we need to free the memory at thread release time and this happens
  214. // in code that doesn't know about the the object.
  215. pccdc16 = (CDragDefaultCursors *)
  216. PrivMemAlloc(sizeof(CDragDefaultCursors));
  217. if (pccdc16 != NULL)
  218. {
  219. // Successfully allocated so initialize it
  220. if (!pccdc16->Init())
  221. {
  222. PrivMemFree(pccdc16);
  223. return NULL;
  224. }
  225. tls->pDragCursors = pccdc16;
  226. }
  227. }
  228. return pccdc16;
  229. }
  230. //+-------------------------------------------------------------------------
  231. //
  232. // Function: CDragDefaultCursors::Init
  233. //
  234. // Synopsis: Initialize object by loading all the default cursors.
  235. //
  236. // History: dd-mmm-yy Author Comment
  237. // 19-Apr-94 Ricksa Created
  238. //
  239. // Note: We continue the Win16 practice of ignoring possible failure
  240. // cases when loading the cursors although we do put in a
  241. // debug verification that they all loaded.
  242. //
  243. //--------------------------------------------------------------------------
  244. BOOL CDragDefaultCursors::Init(void)
  245. {
  246. // Make sure table is set to NULLs.
  247. memset(&ahcursorDefaults[0][0], 0, sizeof(ahcursorDefaults));
  248. // Load cursors for operation
  249. if ( !(ahcursorDefaults[NO_SCROLL] [NO_DROP]
  250. = LoadCursor (g_hmodOLE2, MAKEINTRESOURCE(CURNONE))) )
  251. return FALSE;
  252. if (!(ahcursorDefaults[NO_SCROLL] [MOVE_DROP] =
  253. LoadCursor (g_hmodOLE2, MAKEINTRESOURCE(CURMOVE))) )
  254. return FALSE;
  255. if (!(ahcursorDefaults[NO_SCROLL] [COPY_DROP] =
  256. LoadCursor (g_hmodOLE2, MAKEINTRESOURCE(CURCOPY))) )
  257. return FALSE;
  258. if (!(ahcursorDefaults[NO_SCROLL] [LINK_DROP] =
  259. LoadCursor(g_hmodOLE2, MAKEINTRESOURCE(CURLINK))) )
  260. return FALSE;
  261. // Load cursors for operation
  262. ahcursorDefaults[SCROLL] [NO_DROP] =
  263. ahcursorDefaults[NO_SCROLL] [NO_DROP];
  264. ahcursorDefaults[SCROLL] [MOVE_DROP] =
  265. ahcursorDefaults[NO_SCROLL] [MOVE_DROP];
  266. ahcursorDefaults[SCROLL] [COPY_DROP] =
  267. ahcursorDefaults[NO_SCROLL] [COPY_DROP];
  268. ahcursorDefaults[SCROLL] [LINK_DROP] =
  269. ahcursorDefaults[NO_SCROLL] [LINK_DROP];
  270. #if DBG == 1
  271. // For debug, verify that cursors were loaded correctly
  272. for (int i = 0; i < 2; i++)
  273. {
  274. for (int j = 0; j < 4; j++)
  275. {
  276. AssertSz((ahcursorDefaults[i] [j] != NULL),
  277. "Drag/Drop cursor initialization failed!");
  278. }
  279. }
  280. #endif // DBG == 1
  281. return TRUE;
  282. }
  283. //+-------------------------------------------------------------------------
  284. //
  285. // Function: CDragDefaultCursors::SetCursor
  286. //
  287. // Synopsis: Set cursor to appropriate value
  288. //
  289. // Algorithm: We use the input effect to calculate the appropriate offset
  290. // into the table for the cursor to use.
  291. //
  292. // History: dd-mmm-yy Author Comment
  293. // 19-Apr-94 Ricksa Created
  294. //
  295. // Note: We use the table approach so we to make consistent behavior
  296. // between scroll and non-scroll cursors.
  297. //
  298. //--------------------------------------------------------------------------
  299. void CDragDefaultCursors::SetCursor(DWORD dwEffect)
  300. {
  301. // Get Scroll index
  302. int iScroll = (dwEffect & DROPEFFECT_SCROLL) ? SCROLL : NO_SCROLL;
  303. int iCursorType = NO_DROP;
  304. if (dwEffect & DROPEFFECT_LINK)
  305. {
  306. iCursorType = LINK_DROP;
  307. }
  308. else if (dwEffect & DROPEFFECT_COPY)
  309. {
  310. iCursorType = COPY_DROP;
  311. }
  312. else if (dwEffect & DROPEFFECT_MOVE)
  313. {
  314. iCursorType = MOVE_DROP;
  315. }
  316. ::SetCursor(ahcursorDefaults[iScroll] [iCursorType]);
  317. }
  318. //
  319. // Drag/Drop Operation Statics
  320. //
  321. LONG CDragOperation::s_wScrollInt = -1;
  322. //+-------------------------------------------------------------------------
  323. //
  324. // Function: GetControlKeysState
  325. //
  326. // Synopsis: queries the current status of the control keys
  327. //
  328. // Arguments: [fAll] -- if true, the just query the keys, not mouse
  329. // buttons too
  330. //
  331. // Returns: the MK flags for each key pressed
  332. //
  333. // Algorithm: Get key state either for all keys and mouse buttons in
  334. // the vKeyMap table or simply for the key portion of the table
  335. // and translate it to the WPARAM form as returned in mouse
  336. // messages.
  337. //
  338. // History: dd-mmm-yy Author Comment
  339. // 06-Dec-93 alexgo 32bit port
  340. //
  341. //--------------------------------------------------------------------------
  342. WORD GetControlKeysState(BOOL fAll)
  343. {
  344. WORD grfKeyState = 0;
  345. int i = (fAll) ? 0 : 3;
  346. for (; i < sizeof(vKeyMap) / sizeof(vKeyMap[0]); i++)
  347. {
  348. if (GetKeyState(vKeyMap[i].keyCode) < 0) // Key down
  349. {
  350. grfKeyState |= vKeyMap[i].keyFlag;
  351. }
  352. }
  353. return grfKeyState;
  354. }
  355. //+-------------------------------------------------------------------------
  356. //
  357. // Function: GetControlKeysStateOfParam
  358. //
  359. // Synopsis: gets the key/button state of wparam (used with mouse messages)
  360. //
  361. // Arguments: [wParam] -- the wParam to parse apart
  362. //
  363. // Returns: the key's set in wParam
  364. //
  365. // Algorithm: First determine if keys we are interested in are set
  366. // in the wParam message. Then go check the state of the
  367. // ALT key and record that in the key state. We then return
  368. // that to the caller.
  369. //
  370. // History: dd-mmm-yy Author Comment
  371. // 06-Dec-93 alexgo 32bit port
  372. //
  373. //--------------------------------------------------------------------------
  374. WORD GetControlKeysStateOfParam(WPARAM wParam)
  375. {
  376. // Check all the buttons we are interested in at once.
  377. WORD grfKeyState = (WORD) wParam
  378. & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_SHIFT | MK_CONTROL);
  379. // get the alt key
  380. if (GetKeyState(VK_ALT) < 0) // Key down
  381. {
  382. grfKeyState |= MK_ALT;
  383. }
  384. return grfKeyState;
  385. }
  386. //+-------------------------------------------------------------------------
  387. //
  388. // Function: IsWin31DropTarget
  389. //
  390. // Synopsis: determines whether the given hwnd is a valid drop target
  391. // for Win31 style drag drop
  392. //
  393. // Effects:
  394. //
  395. // Arguments: [hwnd] -- the window to check
  396. //
  397. // Requires:
  398. //
  399. // Returns: TRUE/
  400. // FALSE
  401. //
  402. // Signals:
  403. //
  404. // Modifies:
  405. //
  406. // Algorithm: checks the WS_EX_ACCEPTFILES style bit. If this bit is
  407. // set and the window is not disabled, then it is a valid
  408. // Win3.1 drop target.
  409. //
  410. // History: dd-mmm-yy Author Comment
  411. // 25-Jan-95 alexgo added check for WS_DISABLED
  412. // 20-Oct-94 alexgo author
  413. //
  414. // Notes:
  415. //
  416. //--------------------------------------------------------------------------
  417. BOOL IsWin31DropTarget( HWND hwnd )
  418. {
  419. LONG exstyle;
  420. exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
  421. if( (exstyle & WS_EX_ACCEPTFILES) )
  422. {
  423. LONG style;
  424. style = GetWindowLong(hwnd, GWL_STYLE);
  425. if( !(style & WS_DISABLED) )
  426. {
  427. return TRUE;
  428. }
  429. }
  430. return FALSE;
  431. }
  432. //+-------------------------------------------------------------------------
  433. //
  434. // Function: UseWin31DragDrop
  435. //
  436. // Synopsis: tests the given data object to see if enough data is offered
  437. // to perform Win3.1 style drag drop
  438. //
  439. // Effects:
  440. //
  441. // Arguments: [pDataObject] -- pointer to the data object
  442. //
  443. // Requires: pdataobj must not be NULL
  444. //
  445. // Returns: TRUE/FALSE
  446. //
  447. // Signals:
  448. //
  449. // Modifies:
  450. //
  451. // Algorithm: does an IDataObject::QueryGetData for CF_HDROP
  452. //
  453. // History: dd-mmm-yy Author Comment
  454. // 30-Oct-94 alexgo author
  455. //
  456. // Notes:
  457. //
  458. //--------------------------------------------------------------------------
  459. BOOL UseWin31DragDrop(IDataObject *pDataObject)
  460. {
  461. FORMATETC formatetc;
  462. INIT_FORETC(formatetc);
  463. formatetc.cfFormat = CF_HDROP;
  464. formatetc.tymed = TYMED_HGLOBAL;
  465. if( pDataObject->QueryGetData(&formatetc) == NOERROR )
  466. {
  467. return TRUE;
  468. }
  469. else
  470. {
  471. return FALSE;
  472. }
  473. }
  474. //+-------------------------------------------------------------------------
  475. //
  476. // Function: IsNCDrop
  477. //
  478. // Synopsis: are we dropping into the non-client area of the window or
  479. // on an iconic window?
  480. //
  481. // Effects: *DOES A SEND MESSAGE*!!!
  482. //
  483. // Arguments: [hwnd] -- the window to ask
  484. // [pt] -- the point in screen coords
  485. //
  486. // Requires:
  487. //
  488. // Returns: TRUE/FALSE (TRUE if in non-client area)
  489. //
  490. // Signals:
  491. //
  492. // Modifies:
  493. //
  494. // Algorithm:
  495. //
  496. // History: dd-mmm-yy Author Comment
  497. // 25-Jan-95 alexgo borrowed from Win95 shell sources
  498. //
  499. // Notes:
  500. //
  501. //--------------------------------------------------------------------------
  502. BOOL IsNCDrop(HWND hwnd, POINT pt)
  503. {
  504. return (!IsIconic(hwnd) &&
  505. HTCLIENT!=SendMessage(hwnd, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)));
  506. }
  507. //+-------------------------------------------------------------------------
  508. //
  509. // Member: GetDropTarget
  510. //
  511. // Synopsis: Gets the IDropTarget * from the closest window in the
  512. // hierachy up from the given window (if available, of
  513. // course ;-)
  514. //
  515. // Arguments: [hwndCur] -- the window to the cursor is currently over
  516. // [hwndDropTarget] -- the window that contains a valid DropTarget
  517. //
  518. // Returns: Result of drag enter operation at Target
  519. //
  520. // Algorithm: Loop calling PrivDragDrop until we get a drop target or
  521. // we run out of windows that are parent to the window that
  522. // the mouse is currently on.
  523. //
  524. // If a window in the hierarchy has registered itself for
  525. // Win3.1 drag drop, then we create a drop target wrapper
  526. // (CDropTarget) to handle the Win3.1 protocol. Note
  527. // that a window hierarchy may be both OLE *and* Win3.1
  528. // targets.
  529. //
  530. // History: dd-mmm-yy Author Comment
  531. // 08-Nov-94 alexgo converted to use PrivDragDrop
  532. // 20-Oct-94 alexgo added Win31 drop target support
  533. // 30-Sep-94 ricksa Drag/Drop optimization.
  534. // 21-Jul-94 alexgo removed GetDropTargetFromWindow
  535. // optimization and put that functionality
  536. // in GetInterfaceFromWindowProp (to
  537. // help make clipboard faster).
  538. // 06-Apr-94 Ricksa Modified to call GetDropTargetFromWindow
  539. // to optimize local calls
  540. // 11-Jan-94 alexgo changed name from GetTopStm to
  541. // GetDropTarget, converted to the RPC-style
  542. // drag drop, added a VDATEHEAP macro
  543. // 06-Dec-93 alexgo commented
  544. //
  545. //--------------------------------------------------------------------------
  546. HRESULT CDragOperation::GetDropTarget(HWND hwnd31,HWND hwndDropTarget)
  547. {
  548. IDropTarget *ptarget = NULL;
  549. DDInfo hDDInfo = NULL;
  550. VDATEHEAP();
  551. DDDebugOut((DEB_ITRACE, "%p _IN GetDropTarget ( %x,%x)\n", this, hwnd31,hwndDropTarget));
  552. _pDropTarget = NULL;
  553. HRESULT hr = E_FAIL;
  554. if (hwndDropTarget)
  555. {
  556. HWND hwndClipWindow;
  557. Assert(GetProp(hwndDropTarget, (LPCWSTR)g_aDropTarget));
  558. // If the DropTarget hasn't been marshaled, Marshal it now.
  559. if (hwndClipWindow = (HWND) GetProp(hwndDropTarget,(LPCWSTR) g_aDropTargetMarshalHwnd))
  560. {
  561. SSSendMessage(hwndClipWindow,WM_OLE_CLIPBRD_MARSHALDROPTARGET,0,(LPARAM) hwndDropTarget);
  562. }
  563. hr = PrivDragDrop(hwndDropTarget,
  564. DRAGOP_ENTER,
  565. _DOBuffer,
  566. _pDataObject,
  567. _grfKeyState,
  568. _cpt.GetPOINTL(),
  569. _pdwEffect,
  570. NULL,
  571. &hDDInfo);
  572. if (hr != NOERROR)
  573. {
  574. hwndDropTarget = NULL;
  575. }
  576. }
  577. Assert( (NULL == hwnd31) || IsWin31DropTarget(hwnd31));
  578. if( hwndDropTarget || hwnd31 )
  579. {
  580. ptarget = new CDropTarget(hwnd31, hwndDropTarget, *_pdwEffect, this, hDDInfo);
  581. if( ptarget == NULL )
  582. {
  583. hr = E_OUTOFMEMORY;
  584. }
  585. else
  586. {
  587. hr = NOERROR;
  588. }
  589. // if we have a Win31 drop target AND the OLE drop target returned
  590. // DROPEFFECT_NONE, then we should return DROPEFFECT_COPY
  591. if( hr == NOERROR && *_pdwEffect == DROPEFFECT_NONE && hwnd31 )
  592. {
  593. *_pdwEffect = DROPEFFECT_COPY;
  594. }
  595. _pDropTarget = ptarget;
  596. }
  597. DDDebugOut((DEB_ITRACE, "%p OUT GetDropTarget ( %lx ) [ %p ]\n",
  598. this, hr, _pDropTarget));
  599. return hr;
  600. }
  601. //+-------------------------------------------------------------------------
  602. //
  603. // Function: CDragOperation::CDragOperation
  604. //
  605. // Synopsis: Initialize the object to start the operation
  606. //
  607. // Arguments: [pDataObject] - pointer to data object to drop
  608. // [pDropSource] - pointer to source for drop operation
  609. // [dwOKEffects] - effects allowed in drag operation
  610. // [pdwEffect] - how operation affected source data
  611. // [hr] - whether constructor succeeded
  612. //
  613. // Algorithm: Initialize data in object. Make sure that static data
  614. // is initialized. Wait for first mouse message to begin.
  615. //
  616. // History: dd-mmm-yy Author Comment
  617. // 20-Oct-94 alexgo added support for Win31 drag drop
  618. // 04-Apr-94 Ricksa Created
  619. //
  620. //--------------------------------------------------------------------------
  621. CDragOperation::CDragOperation(
  622. LPDATAOBJECT pDataObject,
  623. LPDROPSOURCE pDropSource,
  624. DWORD dwOKEffects,
  625. DWORD FAR *pdwEffect,
  626. HRESULT& hr)
  627. :
  628. _pDataObject(pDataObject),
  629. _DOBuffer(NULL),
  630. _pDropSource(pDropSource),
  631. _pDropTarget(NULL),
  632. _pRealDropTarget(NULL),
  633. _hFormats(NULL),
  634. _dwOKEffects(dwOKEffects),
  635. _pdwEffect(pdwEffect),
  636. _fEscapePressed(FALSE),
  637. _curOld(GetCursor()),
  638. _hwndLast((HWND) -1),
  639. _grfKeyState(0),
  640. _hrDragResult(S_OK),
  641. _fReleasedCapture(FALSE),
  642. _pcddcDefault(NULL),
  643. _fUseWin31(FALSE)
  644. {
  645. VDATEHEAP();
  646. // Set the default scroll interval
  647. if (s_wScrollInt < 0)
  648. {
  649. InitScrollInt();
  650. }
  651. hr = GetMarshalledInterfaceBuffer(IID_IDataObject, pDataObject,
  652. &_DOBuffer);
  653. if( hr != NOERROR )
  654. {
  655. Assert(NULL == _DOBuffer);
  656. return;
  657. }
  658. // Get appropriate default cursor table object
  659. if ((_pcddcDefault = CDragDefaultCursors::GetDefaultCursorObject()) == NULL)
  660. {
  661. // Some error occurred while we were trying to initialize the
  662. // so return an error. This should be highly unusual.
  663. DDDebugOut((DEB_ERROR,
  664. "CDragDefaultCursors::GetDefaultCursorObject Failed!\n"));
  665. hr = E_FAIL;
  666. return;
  667. }
  668. // We will use the clipboard window to capture the mouse but we
  669. // must have a clipboard window so we make sure it is created
  670. // if it is not already there.
  671. hr = ClipSetCaptureForDrag(this);
  672. if (FAILED(hr))
  673. {
  674. return;
  675. }
  676. _hFormats = CreateSharedDragFormats(pDataObject);
  677. // it's OK for _hFormats to be NULL (indicates an empty or non-existant
  678. // formatetc enumertor
  679. // For following peek
  680. MSG msg;
  681. // Busy wait until a mouse or escape message is in the queue
  682. while (!PeekMessage(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
  683. {
  684. // Note: all keyboard messages except escape are tossed. This is
  685. // fairly reasonable since the user has to be holding the left
  686. // mouse button down at this point. They can't really be doing
  687. // too much data input one handed.
  688. if ((PeekMessage(&msg, 0, WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE)
  689. || PeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYDOWN, PM_REMOVE))
  690. && msg.wParam == VK_ESCAPE)
  691. {
  692. _fEscapePressed = TRUE;
  693. break;
  694. }
  695. }
  696. // get mouse pos and key state
  697. if (!_fEscapePressed)
  698. {
  699. _cpt.Set(msg.pt.x, msg.pt.y);
  700. _grfKeyState = GetControlKeysStateOfParam(msg.wParam);
  701. }
  702. else
  703. {
  704. // We ask the cursor for its position since we didn't get a
  705. // position from the mouse.
  706. GetCursorPos(_cpt.GetAddressOfPOINT());
  707. _grfKeyState = GetControlKeysState(TRUE);
  708. }
  709. // Check to see if we need to do Win3.1 style drag drop.
  710. // If we do, then set a flag so we can construct a fake drop target as
  711. // needed
  712. if( UseWin31DragDrop(pDataObject) )
  713. {
  714. _fUseWin31 = TRUE;
  715. }
  716. }
  717. //+-------------------------------------------------------------------------
  718. //
  719. // Function: ~CDragOperation
  720. //
  721. // Synopsis: Clean up object
  722. //
  723. // Algorithm: Release mouse capture. Restore ole cursor. Remove enum
  724. // formats.
  725. //
  726. // History: dd-mmm-yy Author Comment
  727. // 04-Apr-94 Ricksa Created
  728. //
  729. //--------------------------------------------------------------------------
  730. CDragOperation::~CDragOperation(void)
  731. {
  732. VDATEHEAP();
  733. AssertSz((_pDropTarget == NULL), "CDragOperation::~CDragOperation");
  734. // Stop the mouse capture
  735. ReleaseCapture();
  736. // Restore the cursor if it got changed
  737. SetCursor(_curOld);
  738. // Close the handle to the shared memory
  739. if (_hFormats)
  740. {
  741. CloseHandle(_hFormats);
  742. _hFormats = NULL;
  743. }
  744. if( _DOBuffer )
  745. {
  746. ReleaseMarshalledInterfaceBuffer(_DOBuffer);
  747. }
  748. }
  749. //+-------------------------------------------------------------------------
  750. //
  751. // Function: CDragOperation::InitScrollInt
  752. //
  753. // Synopsis: Initialize the scroll interval
  754. //
  755. // Algorithm: Look in profile for defined interval. If none set, then
  756. // default to zero.
  757. //
  758. // History: dd-mmm-yy Author Comment
  759. // 04-Apr-94 Ricksa Created
  760. //
  761. //--------------------------------------------------------------------------
  762. void CDragOperation::InitScrollInt(void)
  763. {
  764. DWORD dw;
  765. OLECHAR szBuffer[20];
  766. s_wScrollInt = DD_DEFSCROLLDELAY;
  767. dw = sizeof(szBuffer);
  768. if (ERROR_SUCCESS == RegQueryValueEx(HKEY_CURRENT_USER,
  769. OLESTR("Control Panel\\Mouse\\DragScrollDelay"),
  770. NULL,
  771. NULL,
  772. (LPBYTE)szBuffer,
  773. &dw))
  774. {
  775. s_wScrollInt = wcstol(szBuffer, NULL, 0);
  776. }
  777. }
  778. //+-------------------------------------------------------------------------
  779. //
  780. // Function: CDragOperation::UpdateTarget
  781. //
  782. // Synopsis: Update the target window based on mouse location
  783. //
  784. // Returns: TRUE - continue drag operation
  785. // FALSE - error or time to drop
  786. //
  787. // Algorithm: First, we query the source to see if it wants to continue
  788. // with the drop. If so, we get current window for mouse. If
  789. // it is different than the previous window check to see whether
  790. // the targets are different. If they are different, then notify
  791. // the current target that we are leaving and then notify the
  792. // new target that we have arrived.
  793. //
  794. // History: dd-mmm-yy Author Comment
  795. // 04-Apr-94 Ricksa Created
  796. // 10-Jul-94 AlexT Allow same IDropTarget on different HWNDs
  797. //
  798. //--------------------------------------------------------------------------
  799. BOOL CDragOperation::UpdateTarget(void)
  800. {
  801. VDATEHEAP();
  802. DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::UpdateTarget ( )\n", this));
  803. // Assume this operation will continue the drag drop
  804. BOOL fResult = TRUE;
  805. HRESULT hr;
  806. LPDROPTARGET lpCurDropTarget = NULL,
  807. lpOldDropTarget = NULL;
  808. HWND hWndTemp = NULL;
  809. HWND hwndCur = WindowFromPoint(_cpt.GetPOINT());
  810. // Query continue can return telling us one of four things:
  811. // (1) Keep going (S_OK), (2) Drop operation should occur
  812. // (DRAGDROP_S_DROP), (3) Drop operation is canceled
  813. // (DRAGDROP_S_CANCEL) or (4) An unexpected error has occurred.
  814. HRESULT hrQuery = _pDropSource->QueryContinueDrag(_fEscapePressed,
  815. _grfKeyState);
  816. if (FAILED(hrQuery) || (hrQuery == ResultFromScode(DRAGDROP_S_CANCEL)))
  817. {
  818. // Unexpected error or the operation has been cancelled so give up.
  819. _hrDragResult = hrQuery;
  820. fResult = FALSE;
  821. goto UpdateTarget_exit;
  822. }
  823. // walk up the window list to find the actual pointer values for the current
  824. // and old IDropTarget interfaces
  825. if (hwndCur != _hwndLast)
  826. {
  827. hWndTemp = _hwndLast;
  828. BOOL fChangedWin31 = FALSE;
  829. HWND hWndOldDrop = NULL;
  830. HWND hWndNewDrop = NULL;
  831. HWND hWndWin31Drop = NULL;
  832. LPDROPTARGET lpRealDropTarget = NULL;
  833. HANDLE hTemp = NULL;
  834. DWORD dwCurrentProcessId = 0;
  835. if (hWndTemp != (HWND)-1)
  836. GetWindowThreadProcessId(hWndTemp, &dwCurrentProcessId);
  837. DWORD dwTempProcessID = dwCurrentProcessId;
  838. while (hWndTemp && !lpRealDropTarget && hWndTemp != (HWND)-1 && dwTempProcessID == dwCurrentProcessId)
  839. {
  840. if (lpRealDropTarget = (IDropTarget *)GetProp(hWndTemp, (LPCWSTR)g_aDropTarget))
  841. {
  842. hWndOldDrop = hWndTemp;
  843. }
  844. hWndTemp = GetParent(hWndTemp);
  845. if (hWndTemp)
  846. {
  847. GetWindowThreadProcessId(hWndTemp, &dwTempProcessID);
  848. }
  849. }
  850. hWndTemp = hwndCur;
  851. if (hWndTemp != (HWND)-1)
  852. GetWindowThreadProcessId(hWndTemp, &dwCurrentProcessId);
  853. dwTempProcessID = dwCurrentProcessId;
  854. while (hWndTemp && dwTempProcessID == dwCurrentProcessId)
  855. {
  856. // If we haven't found the DropTarget yet, check this window.
  857. if (!lpCurDropTarget)
  858. {
  859. if (lpCurDropTarget = (IDropTarget *)GetProp(hWndTemp, (LPCWSTR)g_aDropTarget))
  860. {
  861. hWndNewDrop = hWndTemp;
  862. }
  863. }
  864. // if the current window is a win31 drop target, update the win31 window
  865. // handle in our DropTarget Class. NOTE: Beware, this code relies on the
  866. // fact that we can party on the CDropTarget Class directly, knowing that
  867. // the class is reconstructed below as a result of the GetDropTarget()
  868. // when the real IDropTarget ptrs change.
  869. if (!fChangedWin31 &&
  870. IsWin31DropTarget(hWndTemp) &&
  871. _fUseWin31)
  872. {
  873. fChangedWin31 = TRUE;
  874. hWndWin31Drop = hWndTemp;
  875. if (_pDropTarget)
  876. {
  877. ((CDropTarget*)_pDropTarget)->_hwnd31 = hWndTemp;
  878. }
  879. }
  880. // if have a droptarget, and handle Win31 break.
  881. if (lpCurDropTarget && (!_fUseWin31 || fChangedWin31))
  882. {
  883. break;
  884. }
  885. hWndTemp = GetParent(hWndTemp);
  886. if (hWndTemp)
  887. {
  888. GetWindowThreadProcessId(hWndTemp, &dwTempProcessID);
  889. }
  890. }
  891. // only update the drop target if the target has actually changed.
  892. // HACK ALERT: We must explicitly check _hwndLast for -1 because Excel does not
  893. // use OLE drag drop internally. When the cursor is moved outside the Overlapped
  894. // Excel window, DoDragDrop is called. At this point _pRealDropTarget == NULL
  895. // and lpCurDropTarget == NULL, and the no-smoking cursor does not appear.
  896. // the _pRealDropTarget==NULL relies on the fact that lpCurDropTarget==NULL. This
  897. // is true because the first case would short-circuit the rest of the condition
  898. // otherwise
  899. if ( (lpCurDropTarget != _pRealDropTarget) ||
  900. (_hwndLast == (HWND)-1) ||
  901. (hWndNewDrop != hWndOldDrop) ||
  902. (_pRealDropTarget == NULL))
  903. {
  904. DDDebugOut((DEB_ITRACE, "%p lpCurDropTarget != lpOldDropTarget\n", this));
  905. // The window that we are working on has changed
  906. _hwndLast = hwndCur;
  907. _pRealDropTarget = lpCurDropTarget;
  908. //Allow the owner of the window to take foreground if it tries to.
  909. if (dwCurrentProcessId)
  910. AllowSetForegroundWindow(dwCurrentProcessId);
  911. // Assume that neither current or previous window are drop aware
  912. BOOL fCurAndLastNotDropAware = TRUE;
  913. if (_pDropTarget != NULL)
  914. {
  915. // There was a previous drop target
  916. // Last window was drag/drop aware
  917. fCurAndLastNotDropAware = FALSE;
  918. // Tell the drop target we are leaving & release it
  919. _pDropTarget->DragLeave();
  920. _pDropTarget->Release();
  921. _pDropTarget = NULL;
  922. }
  923. // Set up effects for query of target
  924. *_pdwEffect = _dwOKEffects;
  925. hr = GetDropTarget(hWndWin31Drop,hWndNewDrop);
  926. if (_pDropTarget != NULL)
  927. {
  928. // This window is drop awarre
  929. fCurAndLastNotDropAware = FALSE;
  930. // Errors from this call are ignored. We interpret them
  931. // as the drop being disallowed. Since we don't really
  932. // use this information here but in the DragOver call
  933. // we make shortly, we just use this call to notify
  934. // the application that we are beginning a drag operation.
  935. if (!HandleFeedBack(hr))
  936. {
  937. goto UpdateTarget_exit;
  938. }
  939. }
  940. else
  941. {
  942. // Tell the source that nothing happened
  943. // only use DROPEFFECT_NONE if there is no new drop target.
  944. hr = _pDropSource->GiveFeedback(*_pdwEffect = DROPEFFECT_NONE);
  945. if (hr != NOERROR)
  946. {
  947. if (DRAGDROP_S_USEDEFAULTCURSORS == GetScode(hr))
  948. {
  949. _pcddcDefault->SetCursorNone();
  950. }
  951. else
  952. {
  953. // Unexpected error -- we will give up drag/drop.
  954. DDDebugOut((DEB_ERROR,
  955. "CDragOperation::UpdateTarget 1st GiveFeedback FAILED %x\n",
  956. hr));
  957. _hrDragResult = hr;
  958. fResult = FALSE;
  959. goto UpdateTarget_exit;
  960. }
  961. }
  962. }
  963. if (fCurAndLastNotDropAware)
  964. {
  965. // Neither new or old window know about drag/drop so set
  966. // cursor accordingly.
  967. _pcddcDefault->SetCursorNone();
  968. }
  969. }
  970. else
  971. {
  972. // The window that we are working on has changed
  973. _hwndLast = hwndCur;
  974. }
  975. }
  976. if (hrQuery != NOERROR)
  977. {
  978. // Query asked for a drop
  979. fResult = FALSE;
  980. _hrDragResult = hrQuery;
  981. }
  982. UpdateTarget_exit:
  983. DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::UpdateTarget ( %lx )\n",
  984. this, fResult));
  985. return fResult;
  986. }
  987. //+-------------------------------------------------------------------------
  988. //
  989. // Function: CDragOperation::HandleFeedBack
  990. //
  991. // Synopsis: Handle feedback and update of cursor
  992. //
  993. // Arguments: [hr] - hresult from previous operation on drop target.
  994. //
  995. // Returns: TRUE - continue drag operation
  996. // FALSE - error
  997. //
  998. // Algorithm: If previous operation on the target failed, map this to a
  999. // disallowed drop. Then ask the source for feedback. If it
  1000. // so requests, then update the cursor. If an unexpected
  1001. // error occurs, let caller know that loop should break.
  1002. //
  1003. // History: dd-mmm-yy Author Comment
  1004. // 19-Apr-94 Ricksa Created
  1005. //
  1006. //--------------------------------------------------------------------------
  1007. BOOL CDragOperation::HandleFeedBack(HRESULT hr)
  1008. {
  1009. VDATEHEAP();
  1010. DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::HandleFeedBack ( %x )\n",
  1011. this, hr));
  1012. BOOL fResult = TRUE;
  1013. if (hr != NOERROR)
  1014. {
  1015. // target not responding for some reason; treat
  1016. // as if drop not possible, but don't preserve
  1017. // the reason why.
  1018. *_pdwEffect = DROPEFFECT_NONE;
  1019. }
  1020. // If bogus return from drag over, then make sure results are appropriate.
  1021. // However, if we are in a WOW we need to do things a little differently
  1022. // to maintain complete compatability with Win 3.1. In 16-bit OLE 2.0,
  1023. // the *_pdwEffect value is not changed when displaying feedback (i.e.,
  1024. // the result of the & is not stored back into *_pdwEffect in Win 3.1...
  1025. // in straight NT we do). Not storing the results back into *_pdwEffect
  1026. // when InWow() is a hack specifically for Visio, and even more
  1027. // specifically, for dragging from Visio's palette of "items" to an
  1028. // Excel spreadsheet.
  1029. if (IsWOWThread())
  1030. {
  1031. hr = _pDropSource->GiveFeedback( *_pdwEffect & (_dwOKEffects | DROPEFFECT_SCROLL));
  1032. }
  1033. else
  1034. {
  1035. *_pdwEffect &= (_dwOKEffects | DROPEFFECT_SCROLL);
  1036. hr = _pDropSource->GiveFeedback(*_pdwEffect);
  1037. }
  1038. if(hr != NOERROR)
  1039. {
  1040. // Either we want to change the cursor or some unexpected
  1041. // error has occurred.
  1042. if (DRAGDROP_S_USEDEFAULTCURSORS == GetScode(hr))
  1043. {
  1044. _pcddcDefault->SetCursor(*_pdwEffect);
  1045. }
  1046. else
  1047. {
  1048. DDDebugOut((DEB_ERROR,
  1049. "CDragOperation::HandleFeedBack Feedback FAILED %x\n", hr));
  1050. fResult = FALSE;
  1051. _hrDragResult = hr;
  1052. }
  1053. }
  1054. DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::HandleFeedBack ( %lx )\n",
  1055. this, fResult));
  1056. return fResult;
  1057. }
  1058. //+-------------------------------------------------------------------------
  1059. //
  1060. // Function: CDragOperation::DragOver
  1061. //
  1062. // Synopsis: Tell the target we are dragging over and process the result
  1063. //
  1064. // Returns: TRUE - continue drag operation
  1065. // FALSE - error or time to drop
  1066. //
  1067. // Algorithm: Call the target's drag over if there is one and then
  1068. // get the sources feedback to update the cursor accordingly.
  1069. //
  1070. // History: dd-mmm-yy Author Comment
  1071. // 04-Apr-94 Ricksa Created
  1072. //
  1073. //--------------------------------------------------------------------------
  1074. BOOL CDragOperation::DragOver(void)
  1075. {
  1076. VDATEHEAP();
  1077. DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::DragOver ( )\n", this));
  1078. // Default the result of the function to continue the loop for
  1079. // drag and drop.
  1080. BOOL fResult = TRUE;
  1081. // Local holder for errors.
  1082. HRESULT hr;
  1083. if (_pDropTarget != NULL)
  1084. {
  1085. // Keep effect in a local variable to save indirections
  1086. // in this routine.
  1087. *_pdwEffect = _dwOKEffects;
  1088. hr = _pDropTarget->DragOver(_grfKeyState, _cpt.GetPOINTL(), _pdwEffect);
  1089. // Get feedback from source & update cursor if necessary
  1090. fResult = HandleFeedBack(hr);
  1091. }
  1092. DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::DragOver ( %lx )\n",
  1093. this, fResult));
  1094. return fResult;
  1095. }
  1096. //+-------------------------------------------------------------------------
  1097. //
  1098. // Function: CDragOperation::HandleMessages
  1099. //
  1100. // Synopsis: Handle windows messages
  1101. //
  1102. // Returns: TRUE - continue drag operation
  1103. // FALSE - error or time to drop
  1104. //
  1105. // Algorithm: Check for any windows message. If the message is a mouse
  1106. // message then record the new position of the mouse. If it
  1107. // is a key message, the record whether escape has been pushed.
  1108. // If this is any other message, then dispatch it. Repeat this
  1109. // process until the scroll interval has been exceeded.
  1110. //
  1111. // History: dd-mmm-yy Author Comment
  1112. // 04-Apr-94 Ricksa Created
  1113. //
  1114. //--------------------------------------------------------------------------
  1115. BOOL CDragOperation::HandleMessages(void)
  1116. {
  1117. VDATEHEAP();
  1118. DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::HandleMessages ( )\n",
  1119. this));
  1120. // Message buffer
  1121. MSG msg;
  1122. // Default result of function to continue
  1123. BOOL fResult = TRUE;
  1124. // Capture all messages (i.e. modal loop).
  1125. // Process all input messages, dispatch other messages
  1126. //
  1127. // Note:we must NOT loop here until a hardware message comes in
  1128. // scrolling will not work.
  1129. // * yielding is important since other apps need to run
  1130. // * look for mouse messages first since these are the most
  1131. // impotant
  1132. // Flag for whether we peeked a message
  1133. BOOL fMsg;
  1134. //
  1135. // Sundown - The SetTimer return value can be truncated.
  1136. // We are passing NULL as HWND and Win32 will returned
  1137. // a value not greater than 4GB...
  1138. // If a check is required we could consider a temporary
  1139. // UINT_PTR value and do an ASSERT on its value...
  1140. //
  1141. UINT uTimer = (UINT)SetTimer(NULL, 0, s_wScrollInt, NULL);
  1142. do
  1143. {
  1144. fMsg = FALSE;
  1145. // Note: the order of peek is important - further messages can show up
  1146. // in the last peek
  1147. // If we looked for mouse messages first, we might never pick up
  1148. // WM_QUIT or keyboard messages (because by the time we finished
  1149. // processing the mouse message another might be on the queue).
  1150. // So, we check for WM_QUIT and keyboard messages first.
  1151. if (PeekMessage(&msg, 0, WM_QUIT, WM_QUIT, PM_REMOVE | PM_NOYIELD) ||
  1152. PeekMessage(&msg, 0, WM_KEYFIRST, WM_KEYLAST,
  1153. PM_REMOVE | PM_NOYIELD) ||
  1154. PeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYUP,
  1155. PM_REMOVE | PM_NOYIELD) ||
  1156. PeekMessage(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) ||
  1157. PeekMessage(&msg, 0, WM_NCMOUSEFIRST, WM_NCMOUSELAST,
  1158. PM_REMOVE | PM_NOYIELD) ||
  1159. PeekMessage(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
  1160. {
  1161. fMsg = TRUE;
  1162. if (msg.message == WM_QUIT)
  1163. {
  1164. // Quit message so we are done.
  1165. PostQuitMessage((int) msg.wParam);
  1166. // We are going exiting so the error doesn't matter too much
  1167. _hrDragResult = ResultFromScode(E_UNSPEC);
  1168. // Make sure we break out of the loop
  1169. fResult = FALSE;
  1170. }
  1171. else if ((msg.message >= WM_KEYFIRST &&
  1172. msg.message <= WM_KEYLAST) ||
  1173. (msg.message >= WM_SYSKEYDOWN &&
  1174. msg.message <= WM_SYSKEYUP))
  1175. {
  1176. // Pull all keyboard messages from the queue - this keeps
  1177. // the keyboard state in sync with the user's actions
  1178. // We use a do/while so that we process the message we've
  1179. // already peeked.
  1180. do
  1181. {
  1182. // We only really pay attention to the escape key and dump
  1183. // any other key board messages.
  1184. if ((msg.message == WM_KEYDOWN
  1185. || msg.message == WM_SYSKEYDOWN)
  1186. && msg.wParam == VK_ESCAPE)
  1187. {
  1188. // Esc pressed: Cancel
  1189. _fEscapePressed = TRUE;
  1190. }
  1191. }
  1192. while (PeekMessage(&msg, 0, WM_KEYFIRST, WM_KEYLAST,
  1193. PM_REMOVE | PM_NOYIELD) ||
  1194. PeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYUP,
  1195. PM_REMOVE | PM_NOYIELD));
  1196. DWORD grfKeyState; // temp variable for key state
  1197. // get the key state don't change the button states!!
  1198. grfKeyState = GetControlKeysState(FALSE) |
  1199. (_grfKeyState &
  1200. (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
  1201. // if the keyboard state is unchanged, then don't exit
  1202. // this loop (as that will result in DragOver being called).
  1203. // If we call DragOver for each keyboard message, then
  1204. // performance is unacceptably slow.
  1205. if ((grfKeyState == _grfKeyState) && !_fEscapePressed)
  1206. {
  1207. fMsg = FALSE;
  1208. }
  1209. else
  1210. {
  1211. DDDebugOut((DEB_ITRACE, "Updating key state\n"));
  1212. _grfKeyState = grfKeyState;
  1213. }
  1214. }
  1215. else if (msg.message >= WM_MOUSEFIRST &&
  1216. msg.message <= WM_MOUSELAST)
  1217. {
  1218. // we may not have the focus (e.g. if we are the Chicago
  1219. // shell). Therefore, we won't ever get any WM_KEYDOWN
  1220. // messages. Double check the esc key status here
  1221. if( GetKeyState(VK_ESCAPE) < 0 )
  1222. {
  1223. _fEscapePressed = TRUE;
  1224. }
  1225. // We got a mouse move message - we skip all the mouse messages
  1226. // till we get to the last one. The point here is that
  1227. // because of the length of DragOver calls, we can get behind
  1228. // in processing messages which causes odd things to happen
  1229. // on the screen.
  1230. if (WM_MOUSEMOVE == msg.message)
  1231. {
  1232. MSG msg2;
  1233. // Keep processing mouse move messages till there
  1234. // aren't any more.
  1235. // if PeekMessage returns true update the original msg.
  1236. while(PeekMessage(&msg2, 0, WM_MOUSEMOVE, WM_MOUSEMOVE,
  1237. PM_REMOVE))
  1238. {
  1239. msg = msg2;
  1240. }
  1241. }
  1242. // Record position of the mouse
  1243. _cpt.Set(msg.pt.x, msg.pt.y);
  1244. // set mouse button state here
  1245. _grfKeyState = GetControlKeysStateOfParam(msg.wParam);
  1246. }
  1247. else if (msg.message >= WM_NCMOUSEFIRST &&
  1248. msg.message <= WM_NCMOUSELAST)
  1249. {
  1250. // Nothing we need to do for these NC mouse actions
  1251. NULL;
  1252. }
  1253. else if ( (msg.message == WM_TIMER) && (msg.wParam == uTimer) )
  1254. {
  1255. // Our timer was triggered. We need to recheck the keyboard
  1256. // state just in case it has changed. This is important for
  1257. // the Chicago shell--if it doesn't have focus, then we won't
  1258. // get any WM_KEYDOWN message (just mouse moves).
  1259. _grfKeyState = GetControlKeysState(FALSE) | (_grfKeyState &
  1260. (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
  1261. if( GetKeyState(VK_ESCAPE) < 0 )
  1262. {
  1263. _fEscapePressed = TRUE;
  1264. }
  1265. // go ahead and fall out of the loop so we call DragOver
  1266. // (our timeout expired).
  1267. }
  1268. else
  1269. {
  1270. // Dispatch all other messages
  1271. DispatchMessage(&msg);
  1272. fMsg = FALSE;
  1273. }
  1274. }
  1275. else
  1276. {
  1277. WaitMessage();
  1278. }
  1279. // we have to leave the loop periodicially since apps
  1280. // might rely on on it the DragOver is called freqeuntly.
  1281. } while (!fMsg);
  1282. // Get rid of the timer we created for the loop
  1283. KillTimer(NULL, uTimer);
  1284. DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::HandleMessages ( %lx )\n",
  1285. this, fResult));
  1286. return fResult;
  1287. }
  1288. //+-------------------------------------------------------------------------
  1289. //
  1290. // Function: CDragOperation::CompleteDrop
  1291. //
  1292. // Synopsis: Complete the drag/drop operation
  1293. //
  1294. // Returns: Result of operation
  1295. //
  1296. // Algorithm: If there is a target and we have decided to drop, then
  1297. // drop. Otherwise, release the target and return whatever
  1298. // the other result of the operation was.
  1299. //
  1300. // History: dd-mmm-yy Author Comment
  1301. // 04-Apr-94 Ricksa Created
  1302. //
  1303. //--------------------------------------------------------------------------
  1304. HRESULT CDragOperation::CompleteDrop(void)
  1305. {
  1306. VDATEHEAP();
  1307. DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::CompleteDrop ( )\n",
  1308. this));
  1309. // Stop the mouse capture in case a dialog box is thrown up.
  1310. ReleaseCapture();
  1311. if (_pDropTarget != NULL)
  1312. {
  1313. // Caller is Drag/Drop aware
  1314. // and indicated it might accept drop
  1315. // The drop source replies DRAG_S_DROP if the user has
  1316. // released the left mouse button. However, we may be over
  1317. // a drop target which has refused a drop (via the feedback
  1318. // DROPEFFECT_NONE). Thus, both the drop source and drop
  1319. // target need to agree before we commit the drop.
  1320. if ((DRAGDROP_S_DROP == GetScode(_hrDragResult))
  1321. && (*_pdwEffect != DROPEFFECT_NONE))
  1322. {
  1323. // We are going to try to drop
  1324. *_pdwEffect = _dwOKEffects;
  1325. HRESULT hr = _pDropTarget->Drop(_pDataObject, _grfKeyState,
  1326. _cpt.GetPOINTL(), _pdwEffect);
  1327. if (FAILED(hr))
  1328. {
  1329. // If drop actually failed in the last stage, let the
  1330. // caller know that this happened.
  1331. _hrDragResult = hr;
  1332. }
  1333. }
  1334. else
  1335. {
  1336. *_pdwEffect = DROPEFFECT_NONE;
  1337. _pDropTarget->DragLeave();
  1338. }
  1339. _pDropTarget->Release();
  1340. _pDropTarget = NULL;
  1341. }
  1342. else
  1343. {
  1344. *_pdwEffect = DROPEFFECT_NONE;
  1345. }
  1346. DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::CompleteDrop ( %lx )\n",
  1347. this, _hrDragResult));
  1348. return _hrDragResult;
  1349. }
  1350. //+-------------------------------------------------------------------------
  1351. //
  1352. // Function: RegisterDragDrop
  1353. //
  1354. // Synopsis: Registers a drop target
  1355. //
  1356. // Arguments: [hwnd] -- a handle to the drop target window
  1357. // [pDropTarget] -- the IDropTarget interface for the window
  1358. //
  1359. // Returns: HRESULT
  1360. //
  1361. // Algorithm: We ask compobj (via AssignEndpoinProperty) to put an
  1362. // endpoint ID publicly available on the window handle. Then
  1363. // we put the IDropTarget pointer on the window as a private
  1364. // property (see the notes at the beginning of this file).
  1365. //
  1366. // History: dd-mmm-yy Author Comment
  1367. // 06-Apr-94 ricksa Added tracing
  1368. // 16-Jan-94 alexgo pDropTarget is now AddRef'ed
  1369. // 11-Jan-94 alexgo added VDATEHEAP, converted to RPC-style
  1370. // drag drop.
  1371. // 06-Dec-93 alexgo commented
  1372. //
  1373. // Notes: By AddRef'ing the pDropTarget pointer, we are changing
  1374. // the semantics of the 16bit code (which did not do an
  1375. // AddRef).
  1376. //
  1377. //--------------------------------------------------------------------------
  1378. #pragma SEG(RegisterDragDrop)
  1379. STDAPI RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget)
  1380. {
  1381. HRESULT hresult = NOERROR;
  1382. BOOL fDelayDrop = FALSE;
  1383. VDATEHEAP();
  1384. OLETRACEIN((API_RegisterDragDrop, PARAMFMT("hwnd= %h, pDropTarget= %p"),
  1385. hwnd, pDropTarget));
  1386. DDDebugOut((DEB_ITRACE, "%p _IN RegisterDragDrop ( %lx %p )\n",
  1387. NULL, hwnd, pDropTarget));
  1388. if (!IsValidInterface(pDropTarget))
  1389. {
  1390. hresult = E_INVALIDARG;
  1391. }
  1392. else if (!IsWindow(hwnd))
  1393. {
  1394. hresult = DRAGDROP_E_INVALIDHWND;
  1395. }
  1396. else
  1397. {
  1398. CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDropTarget,(IUnknown **)&pDropTarget);
  1399. if (GetProp(hwnd, (LPCWSTR)g_aDropTarget))
  1400. {
  1401. hresult = DRAGDROP_E_ALREADYREGISTERED;
  1402. }
  1403. else if (!SetProp(hwnd, (LPCWSTR)g_aDropTarget, (HANDLE)pDropTarget))
  1404. {
  1405. hresult = E_OUTOFMEMORY;
  1406. }
  1407. else
  1408. {
  1409. DWORD dwAssignAptID;
  1410. Win4Assert(NOERROR == hresult);
  1411. // HACK: We need to add this atom every time RegisterDragDrop
  1412. // is called because 16-bit Word does not call RevokeDragDrop
  1413. // and user will automatically clean-up this atom if Word is the
  1414. // first app run, and then exited before another app calls
  1415. // RegisterDragDrop.
  1416. g_aEndPointAtom = GlobalAddAtom(ENDPOINT_PROP_NAME);
  1417. // See if Delayed Drop can be set up.
  1418. fDelayDrop = FALSE;
  1419. if (g_aDropTargetMarshalHwnd && IsApartmentInitialized())
  1420. {
  1421. HWND hwndClipboard = GetPrivateClipboardWindow(CLIP_CREATEIFNOTTHERE);
  1422. if (hwndClipboard)
  1423. {
  1424. fDelayDrop = SetProp(hwnd,(LPCWSTR) g_aDropTargetMarshalHwnd,hwndClipboard);
  1425. }
  1426. }
  1427. // if can't delay marshal then marshal immediately.
  1428. if (!fDelayDrop)
  1429. {
  1430. hresult = AssignEndpointProperty(hwnd);
  1431. }
  1432. if (NOERROR == hresult)
  1433. {
  1434. pDropTarget->AddRef();
  1435. }
  1436. else
  1437. {
  1438. // We don't free h. It's not a handle at all.
  1439. HANDLE h = RemoveProp(hwnd, (LPCWSTR)g_aDropTarget);
  1440. }
  1441. }
  1442. }
  1443. DDDebugOut((DEB_ITRACE, "%p OUT RegisterDragDrop ( %lx )\n",
  1444. NULL, hresult));
  1445. OLETRACEOUT((API_RegisterDragDrop, hresult));
  1446. return hresult;
  1447. }
  1448. //+-------------------------------------------------------------------------
  1449. //
  1450. // Function: RevokeDragDrop
  1451. //
  1452. // Synopsis: Unregisters a window as a drop target
  1453. //
  1454. // Arguments: [hwnd] -- the window to unregister
  1455. //
  1456. // Returns: HRESULT
  1457. //
  1458. // Algorithm: Removes the two window properties set by
  1459. // RegisterDragDrop
  1460. //
  1461. // History: dd-mmm-yy Author Comment
  1462. // 06-Apr-94 ricksa added tracing
  1463. // 16-Jan-94 alexgo added a Release to the drag drop
  1464. // pointer to match the AddRef in
  1465. // RegisterDragDrop.
  1466. // 11-Jan-94 alexgo converted to RPC-style drag drop,
  1467. // added VDATEHEAP macro
  1468. // 06-Dec-93 alexgo commented
  1469. //
  1470. // Notes: the DropTarget->Release call changes the semantics of
  1471. // this function from the 16bit version (see Notes: for
  1472. // RegisterDragDrop).
  1473. //
  1474. //--------------------------------------------------------------------------
  1475. #pragma SEG(RevokeDragDrop)
  1476. STDAPI RevokeDragDrop(HWND hwnd)
  1477. {
  1478. HRESULT hr = NOERROR;
  1479. LPDROPTARGET pDropTarget;
  1480. BOOL fReleaseDropTarget = TRUE;
  1481. VDATEHEAP();
  1482. OLETRACEIN((API_RevokeDragDrop, PARAMFMT("hwnd= %h"), hwnd));
  1483. DDDebugOut((DEB_ITRACE, "%p _IN RevokeDragDrop ( %lx )\n", NULL, hwnd));
  1484. if (!IsWindow(hwnd))
  1485. {
  1486. hr = DRAGDROP_E_INVALIDHWND;
  1487. }
  1488. else if ((pDropTarget = (LPDROPTARGET)RemoveProp(hwnd, (LPCWSTR)g_aDropTarget)) == NULL)
  1489. {
  1490. hr = DRAGDROP_E_NOTREGISTERED;
  1491. }
  1492. else
  1493. {
  1494. fReleaseDropTarget = TRUE;
  1495. if (GetProp(hwnd, (LPCWSTR) g_aEndPointAtom)) // see if there is an endpoint.
  1496. {
  1497. DWORD dwAssignAptID;
  1498. // Ask compobj to remove the endpoint ID it placed on the window.
  1499. if(SUCCEEDED(UnAssignEndpointProperty(hwnd,&dwAssignAptID)))
  1500. {
  1501. // Note: AptID == ThreadID in Apartment model.
  1502. if( (dwAssignAptID != GetCurrentThreadId()) && (IsApartmentInitialized()) )
  1503. {
  1504. fReleaseDropTarget = FALSE;
  1505. }
  1506. }
  1507. Win4Assert(NULL == GetProp(hwnd,(LPCWSTR) g_aDropTargetMarshalHwnd));
  1508. }
  1509. else
  1510. {
  1511. HWND hwndClipbrd;
  1512. hwndClipbrd = (HWND) RemoveProp(hwnd,(LPCWSTR) g_aDropTargetMarshalHwnd);
  1513. Win4Assert(hwndClipbrd);
  1514. fReleaseDropTarget = (IsApartmentInitialized() && (hwndClipbrd != GetPrivateClipboardWindow(CLIP_QUERY )) )
  1515. ? FALSE : TRUE;
  1516. }
  1517. // Release our reference to the object since we are no longer using it.
  1518. // NOTE: AddRef came from RegisterDragDrop
  1519. // Warning: Only call Release if we are in the same thread that Registered the DropTarget
  1520. // Or we are FreeThreading.
  1521. // This mirrors the atom added in RegisterDragDrop
  1522. GlobalDeleteAtom(g_aEndPointAtom);
  1523. if (fReleaseDropTarget)
  1524. {
  1525. pDropTarget->Release();
  1526. hr = NOERROR; // Always return NOERROR even if UnAssignEndPoint Failed
  1527. }
  1528. else
  1529. {
  1530. LEDebugOut((DEB_WARN, "WARNING:Revoke Called on Different Thread than Register!!\n"));
  1531. hr = RPC_E_WRONG_THREAD;
  1532. }
  1533. }
  1534. DDDebugOut((DEB_ITRACE, "%p OUT RegisterDragDrop ( %lx )\n", NULL, hr));
  1535. OLETRACEOUT((API_RevokeDragDrop, hr));
  1536. return hr;
  1537. }
  1538. //+-------------------------------------------------------------------------
  1539. //
  1540. // Function: DoDragDrop
  1541. //
  1542. // Synopsis: The main drag'n'drop loop
  1543. //
  1544. // Effects:
  1545. //
  1546. // Arguments: [pDataObject] -- the object to drag
  1547. // [pDropSource] -- the drop source
  1548. // [dwOKEffects] -- effects flags (stuff to draw)
  1549. // [pdwEffect] -- what actually happened in
  1550. // the drag drop attempt
  1551. //
  1552. // Requires:
  1553. //
  1554. // Returns:
  1555. //
  1556. // Signals:
  1557. //
  1558. // Modifies:
  1559. //
  1560. // Algorithm: See the notes at the beginning of the file
  1561. //
  1562. // History: dd-mmm-yy Author Comment
  1563. // 25-Nov-96 gopalk Fail the call if OleInitialize has not
  1564. // been called
  1565. // 05-Dec-94 JohannP added stack switching for WIN95
  1566. // 11-Jan-94 alexgo added VDATEHEAP macro, converted to
  1567. // the RPC-style drag drop.
  1568. // 31-Dec-93 erikgav chicago port
  1569. // 06-Dec-93 alexgo formatted
  1570. //
  1571. // Notes: Under Win95 SSAPI(DoDragDrop) gets expanded to SSDoDragDrop.
  1572. // This function is called by DoDragDrop (in stkswtch.cxx)
  1573. // which switches to the 16 bit stack first.
  1574. // IMPORTANT: this function has to be executed on the 16 bit
  1575. // since call back via USER might occur.
  1576. //--------------------------------------------------------------------------
  1577. #pragma SEG(DoDragDrop)
  1578. STDAPI SSAPI(DoDragDrop)(LPDATAOBJECT pDataObject, LPDROPSOURCE pDropSource,
  1579. DWORD dwOKEffects, DWORD *pdwEffect)
  1580. {
  1581. OLETRACEIN((API_DoDragDrop,
  1582. PARAMFMT("pDataObject=%p, pDropSource=%p, dwOKEffects=%x, pdwEffect=%p"),
  1583. pDataObject, pDropSource, dwOKEffects, pdwEffect));
  1584. DDDebugOut((DEB_ITRACE, "%p _IN DoDragDrop (%p %p %lx %p )\n",
  1585. NULL, pDataObject, pDropSource, dwOKEffects, pdwEffect));
  1586. HRESULT hr = NOERROR;
  1587. #ifndef _MAC
  1588. // Validation checks
  1589. VDATEHEAP();
  1590. CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDataObject,(IUnknown **)&pDataObject);
  1591. CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDropSource,(IUnknown **)&pDropSource);
  1592. if(!IsValidPtrOut(pdwEffect, sizeof(DWORD)) ||
  1593. !IsValidInterface(pDropSource) ||
  1594. !IsValidInterface(pDataObject))
  1595. hr = E_INVALIDARG;
  1596. // Check if the thread has called oleinitialize
  1597. if(!IsOleInitialized())
  1598. hr = CO_E_NOTINITIALIZED;
  1599. if(hr == NOERROR) {
  1600. // Create the object that does all the work.
  1601. CDragOperation drgop(pDataObject, pDropSource, dwOKEffects, pdwEffect, hr);
  1602. // Did the constructor succeeded?
  1603. if(SUCCEEDED(hr)) {
  1604. // Loop till worker object tells us to stop
  1605. for(;;) {
  1606. // Update target based on new window position
  1607. if(!drgop.UpdateTarget()) {
  1608. // Error so we are done
  1609. break;
  1610. }
  1611. // Notify
  1612. if(!drgop.DragOver()) {
  1613. break;
  1614. }
  1615. // Handle any messages we get in the mean time
  1616. if(!drgop.HandleMessages()) {
  1617. break;
  1618. }
  1619. } // end for loop
  1620. hr = drgop.CompleteDrop();
  1621. }
  1622. }
  1623. #endif // !_MAC
  1624. DDDebugOut((DEB_ITRACE, "%p OUT DoDragDrop ( %lx )\n", NULL, hr));
  1625. OLETRACEOUT((API_DoDragDrop, hr));
  1626. return hr;
  1627. }
  1628. //+-------------------------------------------------------------------------
  1629. //
  1630. // Member: CDropTarget::CDropTarget
  1631. //
  1632. // Synopsis: constructor for the CDropTarget class
  1633. //
  1634. // Effects:
  1635. //
  1636. // Arguments: [hwnd31] -- the hwnd of the Win3.1 drop target
  1637. // may be NULL
  1638. // [hwndOLE] -- the hwnd of the OLE drop target
  1639. // [dwEffectLast] -- the last effect given the the current
  1640. // drop target that we are to emulate
  1641. // [pdo] -- a pointer to the main drag drop class
  1642. // [hDDInfo] -- handle to cached drag drag info
  1643. //
  1644. //
  1645. // Requires: hwnd31 *must* be a handle to a valid Win3.1 drop source
  1646. //
  1647. // Returns: void
  1648. //
  1649. // Signals:
  1650. //
  1651. // Modifies:
  1652. //
  1653. // Derivation:
  1654. //
  1655. // Algorithm: initializes variables
  1656. //
  1657. // History: dd-mmm-yy Author Comment
  1658. // 20-Oct-94 alexgo author
  1659. // 08-Jan-95
  1660. //
  1661. // Notes: there are two ways of determining if a given hwnd is
  1662. // a valid Win3.1 drop target:
  1663. // 1. send a WM_QUERYDROPOBJECT message for a TRUE/FALSE
  1664. // reply
  1665. // 2. check the extended style bits for WS_EX_ACCEPTFILES
  1666. //
  1667. // if ptarget is non-NULL, then the specific window to which
  1668. // it belongs is *not* guaranteed to be the same window as
  1669. // hwndtarget. hwndtarget is the window that is registered as
  1670. // a Win3.1 target. All that is guaranteed is that the ole
  1671. // target and hwndtarget are in the same window hierarchy.
  1672. //
  1673. //--------------------------------------------------------------------------
  1674. CDropTarget::CDropTarget( HWND hwnd31, HWND hwndOLE, DWORD dwEffectLast,
  1675. CDragOperation *pdo, DDInfo hDDInfo )
  1676. {
  1677. _crefs = 1;
  1678. _hwndOLE = hwndOLE;
  1679. _hwnd31 = hwnd31;
  1680. _hDDInfo = hDDInfo;
  1681. _dwEffectLast = dwEffectLast;
  1682. _pdo = pdo; // pointer to the current drag operation class
  1683. #if DBG ==1
  1684. // now do some checking (see Notes above)
  1685. if( hwnd31 )
  1686. {
  1687. LONG exstyle;
  1688. exstyle = GetWindowLong(hwnd31, GWL_EXSTYLE);
  1689. // strictly speaking, an app could process the WM_QUERYDROPOBJECT
  1690. // message itself (and thus, not set the extended style bits).
  1691. // However, this should be considered an application bug; the
  1692. // documentation states that apps should call DragAcceptFiles,
  1693. // which will set the WS_EX_ACCEPTFILES bit
  1694. Assert( (exstyle & WS_EX_ACCEPTFILES) );
  1695. }
  1696. #endif // DBG ==1
  1697. }
  1698. //+-------------------------------------------------------------------------
  1699. //
  1700. // Member: CDropTarget::~CDropTarget
  1701. //
  1702. // Synopsis: frees the cached drag drop info handle
  1703. //
  1704. // Effects:
  1705. //
  1706. // Arguments: void
  1707. //
  1708. // Requires:
  1709. //
  1710. // Returns: void
  1711. //
  1712. // Signals:
  1713. //
  1714. // Modifies:
  1715. //
  1716. // Derivation:
  1717. //
  1718. // Algorithm:
  1719. //
  1720. // History: dd-mmm-yy Author Comment
  1721. // 08-Jan-95 alexgo author
  1722. //
  1723. // Notes:
  1724. //
  1725. //--------------------------------------------------------------------------
  1726. CDropTarget::~CDropTarget()
  1727. {
  1728. if( _hDDInfo )
  1729. {
  1730. FreeDragDropInfo(_hDDInfo);
  1731. }
  1732. }
  1733. //+-------------------------------------------------------------------------
  1734. //
  1735. // Member: CDropTarget::QueryInterface
  1736. //
  1737. // Synopsis: returns available interfaces on this object
  1738. //
  1739. // Effects:
  1740. //
  1741. // Arguments: [riid] -- the requested interface
  1742. // [ppv] -- where to put the interface
  1743. //
  1744. // Requires:
  1745. //
  1746. // Returns: E_UNEXPECTED
  1747. //
  1748. // Signals:
  1749. //
  1750. // Modifies:
  1751. //
  1752. // Derivation: IDropTarget
  1753. //
  1754. // Algorithm: CDropTarget is only used internally by OLE's drag drop code.
  1755. // It should never do a QI.
  1756. //
  1757. // History: dd-mmm-yy Author Comment
  1758. // 20-Oct-94 alexgo author
  1759. //
  1760. // Notes:
  1761. //
  1762. //--------------------------------------------------------------------------
  1763. STDMETHODIMP CDropTarget::QueryInterface( REFIID riid, LPVOID * ppv )
  1764. {
  1765. (void)riid; // unused;
  1766. (void)ppv; // unused;
  1767. AssertSz(0, "Unexpected QI to CDropTarget");
  1768. return E_UNEXPECTED;
  1769. }
  1770. //+-------------------------------------------------------------------------
  1771. //
  1772. // Member: CDropTarget::AddRef
  1773. //
  1774. // Synopsis: increments the reference count
  1775. //
  1776. // Effects:
  1777. //
  1778. // Arguments: void
  1779. //
  1780. // Requires:
  1781. //
  1782. // Returns: ULONG, the new reference count
  1783. //
  1784. // Signals:
  1785. //
  1786. // Modifies:
  1787. //
  1788. // Derivation: IDropTarget
  1789. //
  1790. // Algorithm:
  1791. //
  1792. // History: dd-mmm-yy Author Comment
  1793. // 20-Oct-94 alexgo author
  1794. //
  1795. // Notes:
  1796. //
  1797. //--------------------------------------------------------------------------
  1798. STDMETHODIMP_(ULONG) CDropTarget::AddRef( void )
  1799. {
  1800. VDATEHEAP();
  1801. DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::AddRef ( )\n", this));
  1802. _crefs++;
  1803. DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::AddRef ( %ld )\n", this,
  1804. _crefs));
  1805. return _crefs;
  1806. }
  1807. //+-------------------------------------------------------------------------
  1808. //
  1809. // Member: CDropTarget::Release
  1810. //
  1811. // Synopsis: decrements the reference count
  1812. //
  1813. // Effects: may delete 'this' object
  1814. //
  1815. // Arguments: void
  1816. //
  1817. // Requires:
  1818. //
  1819. // Returns: ULONG, the new reference count
  1820. //
  1821. // Signals:
  1822. //
  1823. // Modifies:
  1824. //
  1825. // Derivation: IDropTarget
  1826. //
  1827. // Algorithm:
  1828. //
  1829. // History: dd-mmm-yy Author Comment
  1830. // 20-Oct-94 alexgo author
  1831. //
  1832. // Notes:
  1833. //
  1834. //--------------------------------------------------------------------------
  1835. STDMETHODIMP_(ULONG) CDropTarget::Release( void )
  1836. {
  1837. ULONG crefs;
  1838. VDATEHEAP();
  1839. DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::Release ( )\n", this));
  1840. crefs = --_crefs;
  1841. if( crefs == 0)
  1842. {
  1843. DDDebugOut((DEB_ITRACE, "DELETING CDropTarget %p\n", this));
  1844. delete this;
  1845. }
  1846. DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::Release ( %ld )\n",
  1847. this, crefs));
  1848. return crefs;
  1849. }
  1850. //+-------------------------------------------------------------------------
  1851. //
  1852. // Member: CDropTarget::DragEnter
  1853. //
  1854. // Synopsis: sets the window up for drag drop
  1855. //
  1856. // Effects:
  1857. //
  1858. // Arguments: [pDataObject] -- the data object to drop
  1859. // [grfKeyState] -- the current keyboard state
  1860. // [pt] -- the cursor point
  1861. // [pdwEffect] -- where to return the drag drop effect
  1862. //
  1863. // Requires:
  1864. //
  1865. // Returns: HRESULT
  1866. //
  1867. // Signals:
  1868. //
  1869. // Modifies:
  1870. //
  1871. // Derivation: IDropTarget
  1872. //
  1873. // Algorithm: should never be called. DragEnter is always called
  1874. // via GetDropTarget
  1875. //
  1876. // History: dd-mmm-yy Author Comment
  1877. // 08-Nov-93 alexgo eliminated
  1878. // 20-Oct-94 alexgo author
  1879. //
  1880. // Notes:
  1881. //
  1882. //--------------------------------------------------------------------------
  1883. STDMETHODIMP CDropTarget::DragEnter( IDataObject * pDataObject,
  1884. DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect )
  1885. {
  1886. AssertSz(0, "DragEnter unexpectedly called!");
  1887. return E_UNEXPECTED;
  1888. }
  1889. //+-------------------------------------------------------------------------
  1890. //
  1891. // Member: CDropTarget::DragOver
  1892. //
  1893. // Synopsis: called while the mouse is over a given window
  1894. //
  1895. // Effects:
  1896. //
  1897. // Arguments: [grfKeyState] -- the state of the keyboard
  1898. // [ptl] -- the position of the cursor
  1899. // [pdwEffect] -- the drag drop effect
  1900. //
  1901. // Requires:
  1902. //
  1903. // Returns: NOERROR
  1904. //
  1905. // Signals:
  1906. //
  1907. // Modifies:
  1908. //
  1909. // Derivation: IDropTarget
  1910. //
  1911. // Algorithm: If an OLE target is available, then we forward the call.
  1912. // If the target says DROPEFFECT_NONE, then we go ahead
  1913. // and return DROPEFFECT_COPY if a Win31 target window is
  1914. // available.
  1915. //
  1916. // If there is no OLE target and we have a Win3.1 target,
  1917. // then we go ahead and return DROPEFFECT_COPY.
  1918. //
  1919. // History: dd-mmm-yy Author Comment
  1920. // 08-Nov-94 alexgo converted to PrivDragDrop protocol
  1921. // 20-Oct-94 alexgo author
  1922. //
  1923. // Notes:
  1924. //
  1925. //--------------------------------------------------------------------------
  1926. STDMETHODIMP CDropTarget::DragOver( DWORD grfKeyState, POINTL ptl,
  1927. DWORD *pdwEffect)
  1928. {
  1929. HRESULT hresult = NOERROR;
  1930. VDATEHEAP();
  1931. DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::DragOver ( %lx , %lx "
  1932. ", %lx )\n", this, grfKeyState, &ptl, *pdwEffect));
  1933. if( _hwndOLE )
  1934. {
  1935. hresult = PrivDragDrop(_hwndOLE, DRAGOP_OVER, NULL, NULL, grfKeyState,
  1936. ptl, pdwEffect, NULL, &_hDDInfo);
  1937. _dwEffectLast = *pdwEffect;
  1938. if( _hwnd31 )
  1939. {
  1940. // we only want to stomp on the effect if the DragOver call
  1941. // succeeded. If the call failed, then just assume that a
  1942. // Win3.1 drop would fail as well.
  1943. if( hresult == NOERROR && *pdwEffect == DROPEFFECT_NONE )
  1944. {
  1945. *pdwEffect = DROPEFFECT_COPY;
  1946. }
  1947. }
  1948. }
  1949. else if ( _hwnd31 )
  1950. {
  1951. *pdwEffect = DROPEFFECT_COPY;
  1952. }
  1953. DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::DragOver ( %lx ) [ "
  1954. "%lx ]\n", this, hresult, *pdwEffect));
  1955. return hresult;
  1956. }
  1957. //+-------------------------------------------------------------------------
  1958. //
  1959. // Member: CDropTarget::DragLeave
  1960. //
  1961. // Synopsis: called when the cursor leaves the current target window
  1962. //
  1963. // Effects:
  1964. //
  1965. // Arguments: void
  1966. //
  1967. // Requires:
  1968. //
  1969. // Returns: NOERROR
  1970. //
  1971. // Signals:
  1972. //
  1973. // Modifies:
  1974. //
  1975. // Derivation: IDropTarget
  1976. //
  1977. // Algorithm: Forwards the DragLeave call to the OLE-drop target
  1978. // (if it exists).
  1979. //
  1980. // History: dd-mmm-yy Author Comment
  1981. // 08-Nov-94 alexgo converted to PrivDragDrop protocol
  1982. // 20-Oct-94 alexgo author
  1983. //
  1984. // Notes:
  1985. //
  1986. //--------------------------------------------------------------------------
  1987. STDMETHODIMP CDropTarget::DragLeave()
  1988. {
  1989. HRESULT hresult = NOERROR;
  1990. static POINTL ptl = {0, 0};
  1991. VDATEHEAP();
  1992. DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::DragLeave ( )\n",
  1993. this));
  1994. if( _hwndOLE )
  1995. {
  1996. hresult = PrivDragDrop(_hwndOLE, DRAGOP_LEAVE, NULL, NULL, NULL,
  1997. ptl, NULL, NULL, &_hDDInfo);
  1998. }
  1999. DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::DragLeave ( %lx )\n",
  2000. this, hresult));
  2001. return hresult;
  2002. }
  2003. //+-------------------------------------------------------------------------
  2004. //
  2005. // Member: CDropTarget::Drop
  2006. //
  2007. // Synopsis: called if the user lets go of the mouse button while
  2008. // over a drop target
  2009. //
  2010. // Effects:
  2011. //
  2012. // Arguments: [pDataObject] -- the data object to use
  2013. // [grfKeyState] -- the keyboard state
  2014. // [ptl] -- the current mouse position
  2015. // [pdwEffect] -- where to return cursor effect feedback
  2016. //
  2017. // Requires:
  2018. //
  2019. // Returns: HRESULT
  2020. //
  2021. // Signals:
  2022. //
  2023. // Modifies:
  2024. //
  2025. // Derivation: IDropTarget
  2026. //
  2027. // Algorithm: If there is an OLE-target available, then we first forward
  2028. // the drop request to it. If the call fails
  2029. // (or DROPEFFECT_NONE is returned), then we try the Win31
  2030. // drop by posting a WM_DROPFILES message to the Win31 target
  2031. // window (ifit exists).
  2032. //
  2033. // History: dd-mmm-yy Author Comment
  2034. // 08-Nov-94 alexgo converted to PrivDragDrop protocol
  2035. // 20-Oct-94 alexgo author
  2036. //
  2037. // Notes:
  2038. //
  2039. //--------------------------------------------------------------------------
  2040. STDMETHODIMP CDropTarget::Drop( IDataObject *pDataObject,
  2041. DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect )
  2042. {
  2043. STGMEDIUM medium;
  2044. FORMATETC formatetc;
  2045. HRESULT hresult = E_FAIL;
  2046. IFBuffer DOBuffer = _pdo->GetDOBuffer();
  2047. VDATEHEAP();
  2048. DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::Drop ( %p , %lx , ",
  2049. "%p , %lx )\n", this, pDataObject, grfKeyState, &ptl, *pdwEffect));
  2050. // we don't forward Drop calls to the target if the last effect
  2051. // is DROPEFFECT_NONE. It is important that we check for this because
  2052. // to DoDragDrop 'normally' would not call Drop if the last effect
  2053. // was DROPEFFECT_NONE. However, this target wrapper will stomp
  2054. // pdwEffect and return DROPEFFECT_COPY instead of DROPEFFECT_NONE.
  2055. if( _hwndOLE && _dwEffectLast != DROPEFFECT_NONE )
  2056. {
  2057. hresult = PrivDragDrop(_hwndOLE, DRAGOP_DROP, DOBuffer, pDataObject,
  2058. grfKeyState, ptl, pdwEffect,
  2059. GetPrivateClipboardWindow(CLIP_QUERY), &_hDDInfo);
  2060. }
  2061. else if( _hwndOLE )
  2062. {
  2063. // if the 'real' drop effect is NONE, then we need to call
  2064. // DragLeave here before going on to post the WM_DROPFILES
  2065. // message. Otherwise, the app that is both an OLE and Win31
  2066. // and has been returning DROPEFFECT_NONE will never get a
  2067. // Drop or DragLeave call (which is necessary to terminate
  2068. // the OLE2 drag protocol). Capone in particular is sensitive
  2069. // to this.
  2070. *pdwEffect = DROPEFFECT_NONE;
  2071. hresult = DragLeave();
  2072. }
  2073. if( (hresult != NOERROR || *pdwEffect == DROPEFFECT_NONE) &&
  2074. (hresult != S_FALSE) &&
  2075. (_hwnd31) )
  2076. {
  2077. medium.tymed = TYMED_NULL;
  2078. INIT_FORETC(formatetc);
  2079. formatetc.cfFormat = CF_HDROP;
  2080. formatetc.tymed = TYMED_HGLOBAL;
  2081. hresult = pDataObject->GetData(&formatetc, &medium);
  2082. if( hresult == NOERROR )
  2083. {
  2084. // we need to fixup the mouse point coordinates in the CF_HDROP
  2085. // data. The point point should be in client coordinates
  2086. // (whereas IDropTarget::Drop takes screen coordinates)
  2087. DROPFILES *pdf = (DROPFILES *)GlobalLock(medium.hGlobal);
  2088. POINT pt;
  2089. pt.x = ptl.x;
  2090. pt.y = ptl.y;
  2091. if( pdf )
  2092. {
  2093. // we also need to set the non-client (NC) flag of the
  2094. // dropfile data. This lets the app do different behaviour
  2095. // depending on whether the drop point is in the client or
  2096. // non-client area (Word6, for example, opens the file if on
  2097. // non-client area, otherwise makes a package object).
  2098. pdf->fNC = IsNCDrop(_hwnd31, pt);
  2099. if( ScreenToClient(_hwnd31, &pt) )
  2100. {
  2101. pdf->pt.x = pt.x;
  2102. pdf->pt.y = pt.y;
  2103. }
  2104. else
  2105. {
  2106. LEDebugOut((DEB_WARN, "WARNING: CF_HDROP pt coords"
  2107. "not updated!!\n"));
  2108. ; // don't do anything
  2109. }
  2110. GlobalUnlock(medium.hGlobal);
  2111. }
  2112. else
  2113. {
  2114. LEDebugOut((DEB_WARN, "WARNING: OUT OF MEMORY!\n"));
  2115. ; // don't do anything
  2116. }
  2117. if( PostMessage(_hwnd31, WM_DROPFILES,
  2118. (WPARAM)medium.hGlobal, 0) )
  2119. {
  2120. *pdwEffect = DROPEFFECT_COPY;
  2121. }
  2122. else
  2123. {
  2124. // PostMessage failed, so free the data
  2125. ReleaseStgMedium(&medium);
  2126. *pdwEffect = DROPEFFECT_NONE;
  2127. }
  2128. }
  2129. }
  2130. DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::Drop ( %lx ) [ %lx ]\n",
  2131. this, hresult, *pdwEffect));
  2132. return hresult;
  2133. }