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.

2343 lines
68 KiB

  1. #include "shellprv.h"
  2. #include "defview.h"
  3. #include "lvutil.h"
  4. #include "ids.h"
  5. #include "idlcomm.h"
  6. #pragma hdrstop
  7. #include "datautil.h"
  8. #include "apithk.h"
  9. BOOL DAD_IsDraggingImage(void);
  10. void DAD_SetDragCursor(int idCursor);
  11. BOOL DAD_IsDragging();
  12. #define MONITORS_MAX 16 // Is this really the max?
  13. #define DCID_NULL 0
  14. #define DCID_NO 1
  15. #define DCID_MOVE 2
  16. #define DCID_COPY 3
  17. #define DCID_LINK 4
  18. #define DCID_MAX 5
  19. #define TF_DRAGIMAGES 0x02000000
  20. #define DRAGDROP_ALPHA 120
  21. #define MAX_WIDTH_ALPHA 200
  22. #define MAX_HEIGHT_ALPHA 200
  23. #define CIRCULAR_ALPHA // Circular Alpha Blending Centered on Center of image
  24. class CDragImages : public IDragSourceHelper, IDropTargetHelper
  25. {
  26. public:
  27. // IUnknown methods
  28. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  29. STDMETHODIMP_(ULONG) AddRef() { return 2; }; // One global Com object per process
  30. STDMETHODIMP_(ULONG) Release() { return 1; }; // One global Com object per process
  31. // IDragSourceHelper methods
  32. STDMETHODIMP InitializeFromBitmap(LPSHDRAGIMAGE pshdi, IDataObject* pdtobj);
  33. STDMETHODIMP InitializeFromWindow(HWND hwnd, POINT* ppt, IDataObject* pdtobj);
  34. // IDropTargetHelper methods
  35. STDMETHODIMP DragEnter(HWND hwndTarget, IDataObject* pdtobj, POINT* ppt, DWORD dwEffect);
  36. STDMETHODIMP DragLeave();
  37. STDMETHODIMP DragOver(POINT* ppt, DWORD dwEffect);
  38. STDMETHODIMP Drop(IDataObject* pdtobj, POINT* ppt, DWORD dwEffect);
  39. STDMETHODIMP Show(BOOL fShow);
  40. // These are public so the DAD_* routines can access.
  41. BOOL IsDragging() { return (Initialized() && _Single.bDragging); };
  42. BOOL IsDraggingImage() { return (Initialized() && _fImage && _Single.bDragging); };
  43. BOOL IsDraggingLayeredWindow() { return _shdi.hbmpDragImage != NULL; };
  44. BOOL SetDragImage(HIMAGELIST himl, int index, POINT * pptOffset);
  45. void SetDragCursor(int idCursor);
  46. HWND GetTarget() { return _hwndTarget; }
  47. BOOL Initialized();
  48. DWORD GetThread() { return _idThread; };
  49. void FreeDragData();
  50. void ThreadDetach();
  51. void ProcessDetach();
  52. // for drag source feedback communication
  53. void SetDropEffectCursor(int idCur);
  54. CDragImages() {};
  55. private:
  56. ~CDragImages();
  57. void _InitDragData();
  58. BOOL _IsLayeredSupported();
  59. HRESULT _SaveToDataObject(IDataObject* pdtobj);
  60. HRESULT _LoadFromDataObject(IDataObject* pdtobj);
  61. HRESULT _LoadLayerdBitmapBits(HGLOBAL hGlobal);
  62. HRESULT _SaveLayerdBitmapBits(HGLOBAL* phGlobal);
  63. BOOL _ShowDragImageInterThread(HWND hwndLock, BOOL * pfShow);
  64. // MultiRectDragging
  65. void _MultipleDragShow(BOOL bShow);
  66. void _MultipleDragStart(HWND hwndLock, LPRECT aRect, int nRects, POINT ptStart, POINT ptOffset);
  67. void _MultipleDragMove(POINT ptNew);
  68. HRESULT _SetLayerdDragging(LPSHDRAGIMAGE pshdi);
  69. HRESULT _SetMultiItemDragging(HWND hwndLV, int cItems, POINT *pptOffset);
  70. HRESULT _SetMultiRectDragging(int cItems, LPRECT prect, POINT *pptOffset);
  71. // Merged Cursors
  72. HBITMAP CreateColorBitmap(int cx, int cy);
  73. void _DestroyCachedCursors();
  74. HRESULT _GetCursorLowerRight(HCURSOR hcursor, int * px, int * py, POINT *pptHotSpot);
  75. int _MapCursorIDToImageListIndex(int idCur);
  76. int _AddCursorToImageList(HCURSOR hcur, LPCTSTR idMerge, POINT *pptHotSpot);
  77. BOOL _MergeIcons(HCURSOR hcursor, LPCTSTR idMerge, HBITMAP *phbmImage, HBITMAP *phbmMask, POINT* pptHotSpot);
  78. HCURSOR _SetCursorHotspot(HCURSOR hcur, POINT *ptHot);
  79. // Helper Routines
  80. BOOL _CreateDragWindow();
  81. BOOL _PreProcessDragBitmap(void** ppvBits);
  82. BOOL _IsTooBigForAlpha();
  83. // Member Variables
  84. SHDRAGIMAGE _shdi;
  85. HWND _hwndTarget;
  86. HWND _hwnd; // The HWND of the Layered Window
  87. HDC _hdcDragImage;
  88. HBITMAP _hbmpOld;
  89. BOOL _fLayeredSupported;
  90. BOOL _fCursorDataInited;
  91. POINT _ptDebounce;
  92. // Legacy drag support
  93. BOOL _fImage;
  94. POINT _ptOffset;
  95. DWORD _idThread;
  96. HIMAGELIST _himlCursors;
  97. UINT _cRev;
  98. int _aindex[DCID_MAX]; // will be initialized.
  99. HCURSOR _ahcur[DCID_MAX];
  100. POINT _aptHotSpot[DCID_MAX];
  101. int _idCursor;
  102. // _Single struct is used between DAD_Enter and DAD_Leave
  103. struct
  104. {
  105. // Common part
  106. BOOL bDragging;
  107. BOOL bLocked;
  108. HWND hwndLock;
  109. BOOL bSingle; // Single imagelist dragging.
  110. DWORD idThreadEntered;
  111. // Multi-rect dragging specific part
  112. struct
  113. {
  114. BOOL bShown;
  115. LPRECT pRect;
  116. int nRects;
  117. POINT ptOffset;
  118. POINT ptNow;
  119. } _Multi;
  120. } _Single;
  121. // following fields are used only when fImage==FALSE
  122. RECT* _parc; // cItems
  123. UINT _cItems; // This is a sentinal. Needs to be the last item.
  124. };
  125. CDragImages::~CDragImages()
  126. {
  127. FreeDragData();
  128. }
  129. //
  130. // Read 'Notes' in CDropSource_GiveFeedback for detail about this
  131. // g_fDraggingOverSource flag, which is TRUE only if we are dragging
  132. // over the source window itself with left mouse button
  133. // (background and large/small icon mode only).
  134. //
  135. UINT g_cRev = 0;
  136. CDragImages* g_pdiDragImages = NULL;
  137. BOOL g_fDraggingOverSource = FALSE;
  138. STDAPI CDragImages_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppvOut)
  139. {
  140. ASSERT(pUnkOuter == NULL); //Who's trying to aggregate us?
  141. if (!g_pdiDragImages)
  142. g_pdiDragImages = new CDragImages();
  143. if (g_pdiDragImages && ppvOut) // ppvOut test for internal create usage
  144. return g_pdiDragImages->QueryInterface(riid, ppvOut);
  145. return E_OUTOFMEMORY;
  146. }
  147. STDMETHODIMP CDragImages::QueryInterface(REFIID riid, void **ppv)
  148. {
  149. static const QITAB qit[] = {
  150. QITABENT(CDragImages, IDragSourceHelper),
  151. QITABENT(CDragImages, IDropTargetHelper),
  152. { 0 },
  153. };
  154. return QISearch(this, qit, riid, ppv);
  155. }
  156. #define UM_KILLYOURSELF WM_USER
  157. LRESULT CALLBACK DragWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  158. {
  159. if (uMsg == UM_KILLYOURSELF)
  160. {
  161. DestroyWindow(hwnd);
  162. return 0;
  163. }
  164. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  165. }
  166. BOOL CDragImages::_CreateDragWindow()
  167. {
  168. if (_hwnd == NULL)
  169. {
  170. WNDCLASS wc = {0};
  171. wc.hInstance = g_hinst;
  172. wc.lpfnWndProc = DragWndProc;
  173. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  174. wc.lpszClassName = TEXT("SysDragImage");
  175. wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); // NULL;
  176. SHRegisterClass(&wc);
  177. _hwnd = CreateWindowEx(WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW,
  178. TEXT("SysDragImage"), TEXT("Drag"), WS_POPUPWINDOW,
  179. 0, 0, 50, 50, NULL, NULL, g_hinst, NULL);
  180. if (!_hwnd)
  181. return FALSE;
  182. //
  183. // This window should not be mirrored so that the image contents won't be flipped. [samera]
  184. //
  185. SetWindowBits(_hwnd, GWL_EXSTYLE, RTL_MIRRORED_WINDOW, 0);
  186. }
  187. return TRUE;
  188. }
  189. BOOL CDragImages::Initialized()
  190. {
  191. return _fCursorDataInited;
  192. }
  193. void CDragImages::FreeDragData()
  194. {
  195. if (_hwnd)
  196. {
  197. SendMessage(_hwnd, UM_KILLYOURSELF, 0, 0);
  198. _hwnd = NULL;
  199. }
  200. _fCursorDataInited = FALSE;
  201. // Make sure we destroy the cursors on an invalidate.
  202. if (_himlCursors)
  203. _DestroyCachedCursors();
  204. // Do we have an array?
  205. if (_parc)
  206. {
  207. delete _parc;
  208. _parc = NULL;
  209. }
  210. if (_fImage)
  211. ImageList_EndDrag();
  212. if (_hbmpOld)
  213. {
  214. SelectObject(_hdcDragImage, _hbmpOld);
  215. _hbmpOld = NULL;
  216. }
  217. if (_hdcDragImage)
  218. {
  219. DeleteDC(_hdcDragImage);
  220. _hdcDragImage = NULL;
  221. }
  222. if (_shdi.hbmpDragImage)
  223. DeleteObject(_shdi.hbmpDragImage);
  224. ZeroMemory(&_Single, sizeof(_Single));
  225. ZeroMemory(&_shdi, sizeof(_shdi));
  226. _ptOffset.x = 0;
  227. _ptOffset.y = 0;
  228. _ptDebounce.x = 0;
  229. _ptDebounce.y = 0;
  230. _hwndTarget = _hwnd = NULL;
  231. _fCursorDataInited = _fLayeredSupported = FALSE;
  232. _fImage = FALSE;
  233. _idThread = 0;
  234. _himlCursors = NULL;
  235. _cRev = 0;
  236. _idCursor = 0;
  237. }
  238. void CDragImages::_InitDragData()
  239. {
  240. _idThread = GetCurrentThreadId();
  241. if (_himlCursors && _cRev != g_cRev)
  242. _DestroyCachedCursors();
  243. if (_himlCursors == NULL)
  244. {
  245. UINT uFlags = ILC_MASK | ILC_SHARED;
  246. if (IS_BIDI_LOCALIZED_SYSTEM())
  247. uFlags |= ILC_MIRROR;
  248. //
  249. // if this is not a palette device, use a DDB for the imagelist
  250. // this is important when displaying high-color cursors
  251. //
  252. HDC hdc = GetDC(NULL);
  253. if (!(GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE))
  254. {
  255. uFlags |= ILC_COLORDDB;
  256. }
  257. ReleaseDC(NULL, hdc);
  258. _himlCursors = ImageList_Create(GetSystemMetrics(SM_CXCURSOR),
  259. GetSystemMetrics(SM_CYCURSOR),
  260. uFlags, 1, 0);
  261. _cRev = g_cRev;
  262. // We need to initialize s_cursors._aindex[*]
  263. _MapCursorIDToImageListIndex(-1);
  264. }
  265. _fCursorDataInited = TRUE;
  266. }
  267. BOOL AreAllMonitorsAtLeast(int iBpp)
  268. {
  269. DISPLAY_DEVICE DisplayDevice;
  270. BOOL fAreAllMonitorsAtLeast = TRUE;
  271. for (int iEnum = 0; fAreAllMonitorsAtLeast && iEnum < MONITORS_MAX; iEnum++)
  272. {
  273. ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
  274. DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
  275. if (EnumDisplayDevices(NULL, iEnum, &DisplayDevice, 0) &&
  276. (DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP))
  277. {
  278. HDC hdc = CreateDC(NULL, (LPTSTR)DisplayDevice.DeviceName, NULL, NULL);
  279. if (hdc)
  280. {
  281. int iBits = GetDeviceCaps(hdc, BITSPIXEL);
  282. if (iBits < iBpp)
  283. fAreAllMonitorsAtLeast = FALSE;
  284. DeleteDC(hdc);
  285. }
  286. }
  287. }
  288. return fAreAllMonitorsAtLeast;
  289. }
  290. BOOL CDragImages::_IsLayeredSupported()
  291. {
  292. // For the first rev, we will only support Layered drag images
  293. // when the Color depth is greater than 65k colors.
  294. // We should ask everytime....
  295. _fLayeredSupported = AreAllMonitorsAtLeast(16);
  296. if (_fLayeredSupported)
  297. {
  298. BOOL bDrag;
  299. if (SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &bDrag, 0))
  300. {
  301. _fLayeredSupported = BOOLIFY(bDrag);
  302. }
  303. if (_fLayeredSupported)
  304. _fLayeredSupported = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("NewDragImages"), FALSE, TRUE);
  305. }
  306. return _fLayeredSupported;
  307. }
  308. //
  309. // initialize the static drag image manager from a structure
  310. // this is implemented for WindowLess controls that can act as a
  311. // drag source.
  312. //
  313. HRESULT CDragImages::_SetLayerdDragging(LPSHDRAGIMAGE pshdi)
  314. {
  315. // We don't support being initialized from a bitmap when Layered Windows are not supported
  316. HRESULT hr;
  317. if (_IsLayeredSupported())
  318. {
  319. RIP(IsValidHANDLE(pshdi->hbmpDragImage));
  320. _shdi = *pshdi; // Keep a copy of this.
  321. _idCursor = -1; // Initialize this... This is an arbitraty place and can be put
  322. // anywhere before the first Setcursor call
  323. _InitDragData();
  324. hr = S_OK;
  325. }
  326. else
  327. hr = E_FAIL;
  328. return hr;
  329. }
  330. STDMETHODIMP CDragImages::InitializeFromBitmap(LPSHDRAGIMAGE pshdi, IDataObject* pdtobj)
  331. {
  332. FreeDragData();
  333. HRESULT hr = _SetLayerdDragging(pshdi);
  334. if (SUCCEEDED(hr))
  335. {
  336. hr = _SaveToDataObject(pdtobj);
  337. if (FAILED(hr))
  338. FreeDragData();
  339. }
  340. return hr;
  341. }
  342. BOOL ListView_HasMask(HWND hwnd)
  343. {
  344. HIMAGELIST himl = ListView_GetImageList(hwnd, LVSIL_NORMAL);
  345. return himl && (ImageList_GetFlags(himl) & ILC_MASK);
  346. }
  347. //
  348. // initialize the static drag image manager from an HWND that
  349. // can process the RegisteredWindowMessage(DI_GETDRAGIMAGE)
  350. //
  351. STDMETHODIMP CDragImages::InitializeFromWindow(HWND hwnd, POINT* ppt, IDataObject* pdtobj)
  352. {
  353. HRESULT hr = E_FAIL;
  354. FreeDragData();
  355. if (_IsLayeredSupported())
  356. {
  357. // Register the message that gets us the Bitmap from the control.
  358. static int g_msgGetDragImage = 0;
  359. if (g_msgGetDragImage == 0)
  360. g_msgGetDragImage = RegisterWindowMessage(DI_GETDRAGIMAGE);
  361. // Can this HWND generate a drag image for me?
  362. if (g_msgGetDragImage && SendMessage(hwnd, g_msgGetDragImage, 0, (LPARAM)&_shdi))
  363. {
  364. // Yes; Now we select that into the window
  365. hr = _SetLayerdDragging(&_shdi);
  366. }
  367. }
  368. if (FAILED(hr))
  369. {
  370. TCHAR szClassName[50];
  371. if (GetClassName(hwnd, szClassName, ARRAYSIZE(szClassName)))
  372. {
  373. if (lstrcmpi(szClassName, WC_LISTVIEW) == 0)
  374. {
  375. POINT ptOffset = {0,0};
  376. if (ppt)
  377. ptOffset = *ppt;
  378. int cItems = ListView_GetSelectedCount(hwnd);
  379. if (cItems >= 1)
  380. {
  381. if ((cItems == 1) && ListView_HasMask(hwnd))
  382. {
  383. POINT ptTemp;
  384. HIMAGELIST himl = ListView_CreateDragImage(hwnd, ListView_GetNextItem(hwnd, -1, LVNI_SELECTED), &ptTemp);
  385. if (himl)
  386. {
  387. ClientToScreen(hwnd, &ptTemp);
  388. ptOffset.x -= ptTemp.x;
  389. // Since the listview is mirrored, then mirror the selected
  390. // icon coord. This would result in negative offset so let's
  391. // compensate. [samera]
  392. if (IS_WINDOW_RTL_MIRRORED(hwnd))
  393. ptOffset.x *= -1;
  394. ptOffset.y -= ptTemp.y;
  395. SetDragImage(himl, 0, &ptOffset);
  396. ImageList_Destroy(himl);
  397. hr = S_OK;
  398. }
  399. }
  400. else
  401. {
  402. hr = _SetMultiItemDragging(hwnd, cItems, &ptOffset);
  403. }
  404. }
  405. }
  406. else if (lstrcmpi(szClassName, WC_TREEVIEW) == 0)
  407. {
  408. HIMAGELIST himlDrag = TreeView_CreateDragImage(hwnd, NULL);
  409. if (himlDrag)
  410. {
  411. SetDragImage(himlDrag, 0, NULL);
  412. ImageList_Destroy(himlDrag);
  413. hr = S_OK;
  414. }
  415. }
  416. }
  417. }
  418. if (SUCCEEDED(hr))
  419. {
  420. // ignore failure here as this will still work in process due to the globals
  421. // fonts folder depends on this
  422. _SaveToDataObject(pdtobj);
  423. }
  424. return hr;
  425. }
  426. //
  427. // create the drag window in the layered window case, or to begin drawing the
  428. // Multi Rect or icon drag images.
  429. //
  430. STDMETHODIMP CDragImages::DragEnter(HWND hwndTarget, IDataObject* pdtobj, POINT* ppt, DWORD dwEffect)
  431. {
  432. HRESULT hr = _LoadFromDataObject(pdtobj);
  433. if (SUCCEEDED(hr))
  434. {
  435. _hwndTarget = hwndTarget ? hwndTarget : GetDesktopWindow();
  436. SetDragCursor(-1);
  437. _Single.bDragging = TRUE;
  438. _Single.bSingle = _fImage;
  439. _Single.hwndLock = _hwndTarget;
  440. _Single.bLocked = FALSE;
  441. _Single.idThreadEntered = GetCurrentThreadId();
  442. _ptDebounce.x = 0;
  443. _ptDebounce.y = 0;
  444. if (_shdi.hbmpDragImage)
  445. {
  446. TraceMsg(TF_DRAGIMAGES, "CDragImages::DragEnter : Creating Drag Window");
  447. // At this point the information has been read from the data object.
  448. // Reconstruct the HWND if necessary
  449. if (_CreateDragWindow() && _hdcDragImage)
  450. {
  451. POINT ptSrc = {0, 0};
  452. POINT pt;
  453. SetWindowPos(_hwnd, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE |
  454. SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
  455. GetMsgPos(&pt);
  456. pt.x -= _shdi.ptOffset.x;
  457. pt.y -= _shdi.ptOffset.y;
  458. BLENDFUNCTION blend;
  459. blend.BlendOp = AC_SRC_OVER;
  460. blend.BlendFlags = 0;
  461. blend.AlphaFormat = AC_SRC_ALPHA;
  462. blend.SourceConstantAlpha = 0xFF /*DRAGDROP_ALPHA*/;
  463. HDC hdc = GetDC(_hwnd);
  464. if (hdc)
  465. {
  466. DWORD fULWType = ULW_ALPHA;
  467. // Should have been preprocess already
  468. UpdateLayeredWindow(_hwnd, hdc, &pt, &(_shdi.sizeDragImage),
  469. _hdcDragImage, &ptSrc, _shdi.crColorKey,
  470. &blend, fULWType);
  471. ReleaseDC(_hwnd, hdc);
  472. }
  473. hr = S_OK;
  474. }
  475. }
  476. else
  477. {
  478. // These are in Client Cordinates, not screen coords. Translate:
  479. POINT pt = *ppt;
  480. RECT rc;
  481. GetWindowRect(_hwndTarget, &rc);
  482. pt.x -= rc.left;
  483. pt.y -= rc.top;
  484. if (_fImage)
  485. {
  486. // Avoid the flicker by always pass even coords
  487. ImageList_DragEnter(hwndTarget, pt.x & ~1, pt.y & ~1);
  488. hr = S_OK;
  489. }
  490. else
  491. {
  492. _MultipleDragStart(hwndTarget, _parc, _cItems, pt, _ptOffset);
  493. hr = S_OK;
  494. }
  495. }
  496. //
  497. // We should always show the image whenever this function is called.
  498. //
  499. Show(TRUE);
  500. }
  501. return hr;
  502. }
  503. //
  504. // kill the Layered Window, or to stop painting the icon or rect drag images
  505. //
  506. STDMETHODIMP CDragImages::DragLeave()
  507. {
  508. TraceMsg(TF_DRAGIMAGES, "CDragImages::DragLeave");
  509. if (Initialized())
  510. {
  511. if (_hwnd)
  512. {
  513. FreeDragData();
  514. }
  515. else if (_Single.bDragging &&
  516. _Single.idThreadEntered == GetCurrentThreadId())
  517. {
  518. Show(FALSE);
  519. if (_fImage)
  520. {
  521. ImageList_DragLeave(_Single.hwndLock);
  522. }
  523. _Single.bDragging = FALSE;
  524. DAD_SetDragImage((HIMAGELIST)-1, NULL);
  525. }
  526. _ptDebounce.x = 0;
  527. _ptDebounce.y = 0;
  528. }
  529. return S_OK;
  530. }
  531. // move the Layered window or to rerender the icon or rect images within
  532. // the Window they are over.
  533. //
  534. STDMETHODIMP CDragImages::DragOver(POINT* ppt, DWORD dwEffect)
  535. {
  536. if (Initialized())
  537. {
  538. TraceMsg(TF_DRAGIMAGES, "CDragImages::DragOver pt {%d, %d}", ppt->x, ppt->y);
  539. // Avoid the flicker by always pass even coords
  540. ppt->x &= ~1;
  541. ppt->y &= ~1;
  542. if (_ptDebounce.x != ppt->x || _ptDebounce.y != ppt->y)
  543. {
  544. _ptDebounce.x = ppt->x;
  545. _ptDebounce.y = ppt->y;
  546. if (IsDraggingLayeredWindow())
  547. {
  548. POINT pt;
  549. GetCursorPos(&pt);
  550. pt.x -= _shdi.ptOffset.x;
  551. pt.y -= _shdi.ptOffset.y;
  552. SetWindowPos(_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE |
  553. SWP_NOSIZE | SWP_SHOWWINDOW);
  554. UpdateLayeredWindow(_hwnd, NULL, &pt, NULL, NULL, NULL, 0,
  555. NULL, 0);
  556. }
  557. else
  558. {
  559. // These are in Client Cordinates, not screen coords. Translate:
  560. POINT pt = *ppt;
  561. RECT rc;
  562. GetWindowRect(_hwndTarget, &rc);
  563. pt.x -= rc.left;
  564. pt.y -= rc.top;
  565. if (_fImage)
  566. {
  567. ImageList_DragMove(pt.x, pt.y);
  568. }
  569. else
  570. {
  571. _MultipleDragMove(pt);
  572. }
  573. }
  574. }
  575. }
  576. return S_OK;
  577. }
  578. // do any cleanup after a drop (Currently calls DragLeave)
  579. //
  580. STDMETHODIMP CDragImages::Drop(IDataObject* pdtobj, POINT* ppt, DWORD dwEffect)
  581. {
  582. return DragLeave();
  583. }
  584. // initialize the static drag image manager from a structure
  585. // this is implemented for WindowLess controls that can act as a
  586. // drag source.
  587. //
  588. void CDragImages::SetDragCursor(int idCursor)
  589. {
  590. //
  591. // Ignore if we are dragging over ourselves.
  592. //
  593. if (IsDraggingImage())
  594. {
  595. POINT ptHotSpot;
  596. if (_himlCursors && (idCursor != -1))
  597. {
  598. int iIndex = _MapCursorIDToImageListIndex(idCursor);
  599. if (iIndex != -1)
  600. {
  601. ImageList_GetDragImage(NULL, &ptHotSpot);
  602. ptHotSpot.x -= _aptHotSpot[idCursor].x;
  603. ptHotSpot.y -= _aptHotSpot[idCursor].y;
  604. if (ptHotSpot.x < 0)
  605. {
  606. ptHotSpot.x = 0;
  607. }
  608. if (ptHotSpot.y < 0)
  609. {
  610. ptHotSpot.y = 0;
  611. }
  612. ImageList_SetDragCursorImage(_himlCursors, iIndex, ptHotSpot.x, ptHotSpot.y);
  613. }
  614. else
  615. {
  616. // You passed a bad Cursor ID.
  617. ASSERT(0);
  618. }
  619. }
  620. _idCursor = idCursor;
  621. }
  622. }
  623. // init our state from the hGlobal so we can draw
  624. HRESULT CDragImages::_LoadLayerdBitmapBits(HGLOBAL hGlobal)
  625. {
  626. HRESULT hr = E_FAIL;
  627. if (!Initialized())
  628. {
  629. ASSERT(_shdi.hbmpDragImage == NULL);
  630. ASSERT(_hdcDragImage == NULL);
  631. HDC hdcScreen = GetDC(NULL);
  632. if (hdcScreen)
  633. {
  634. void *pvDragStuff = (void*)GlobalLock(hGlobal);
  635. if (pvDragStuff)
  636. {
  637. CopyMemory(&_shdi, pvDragStuff, sizeof(SHDRAGIMAGE));
  638. BITMAPINFO bmi = {0};
  639. // Create a buffer to read the bits into
  640. bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  641. bmi.bmiHeader.biWidth = _shdi.sizeDragImage.cx;
  642. bmi.bmiHeader.biHeight = _shdi.sizeDragImage.cy;
  643. bmi.bmiHeader.biPlanes = 1;
  644. bmi.bmiHeader.biBitCount = 32;
  645. bmi.bmiHeader.biCompression = BI_RGB;
  646. // Next create a DC and an HBITMAP.
  647. _hdcDragImage = CreateCompatibleDC(hdcScreen);
  648. if (_hdcDragImage)
  649. {
  650. void *pvBits;
  651. _shdi.hbmpDragImage = CreateDIBSection(_hdcDragImage, &bmi, DIB_RGB_COLORS, &pvBits, NULL, NULL);
  652. if (_shdi.hbmpDragImage)
  653. {
  654. _hbmpOld = (HBITMAP)SelectObject(_hdcDragImage, _shdi.hbmpDragImage);
  655. // then Set the bits into the Bitmap
  656. RGBQUAD* pvStart = (RGBQUAD*)((BYTE*)pvDragStuff + sizeof(SHDRAGIMAGE));
  657. DWORD dwCount = _shdi.sizeDragImage.cx * _shdi.sizeDragImage.cy * sizeof(RGBQUAD);
  658. CopyMemory((RGBQUAD*)pvBits, (RGBQUAD*)pvStart, dwCount);
  659. hr = S_OK; // success!
  660. }
  661. }
  662. GlobalUnlock(hGlobal);
  663. }
  664. ReleaseDC(NULL, hdcScreen);
  665. }
  666. }
  667. return hr;
  668. }
  669. // Writes the written information into phGlobal to recreate the drag image
  670. HRESULT CDragImages::_SaveLayerdBitmapBits(HGLOBAL* phGlobal)
  671. {
  672. HRESULT hr = E_FAIL;
  673. if (Initialized())
  674. {
  675. ASSERT(_shdi.hbmpDragImage);
  676. DWORD cbImageSize = _shdi.sizeDragImage.cx * _shdi.sizeDragImage.cy * sizeof(RGBQUAD);
  677. *phGlobal = GlobalAlloc(GPTR, cbImageSize + sizeof(SHDRAGIMAGE));
  678. if (*phGlobal)
  679. {
  680. void *pvDragStuff = GlobalLock(*phGlobal);
  681. CopyMemory(pvDragStuff, &_shdi, sizeof(SHDRAGIMAGE));
  682. void *pvBits;
  683. hr = _PreProcessDragBitmap(&pvBits) ? S_OK : E_FAIL;
  684. if (SUCCEEDED(hr))
  685. {
  686. RGBQUAD* pvStart = (RGBQUAD*)((BYTE*)pvDragStuff + sizeof(SHDRAGIMAGE));
  687. DWORD dwCount = _shdi.sizeDragImage.cx * _shdi.sizeDragImage.cy * sizeof(RGBQUAD);
  688. CopyMemory((RGBQUAD*)pvStart, (RGBQUAD*)pvBits, dwCount);
  689. }
  690. GlobalUnlock(*phGlobal);
  691. }
  692. }
  693. return hr;
  694. }
  695. BOOL CDragImages::_IsTooBigForAlpha()
  696. {
  697. BOOL fTooBig = FALSE;
  698. int dSelectionArea = _shdi.sizeDragImage.cx * _shdi.sizeDragImage.cy;
  699. // The number here is "It just feels right" or
  700. // about 3 Thumbnail icons linned up next to each other.
  701. if ( dSelectionArea > 0x10000 )
  702. fTooBig = TRUE;
  703. return fTooBig;
  704. }
  705. BOOL IsColorKey(RGBQUAD rgbPixel, COLORREF crKey)
  706. {
  707. // COLORREF is backwards to RGBQUAD
  708. return InRange( rgbPixel.rgbBlue, ((crKey & 0xFF0000) >> 16) - 5, ((crKey & 0xFF0000) >> 16) + 5) &&
  709. InRange( rgbPixel.rgbGreen, ((crKey & 0x00FF00) >> 8) - 5, ((crKey & 0x00FF00) >> 8) + 5) &&
  710. InRange( rgbPixel.rgbRed, ((crKey & 0x0000FF) >> 0) - 5, ((crKey & 0x0000FF) >> 0) + 5);
  711. }
  712. #ifdef RADIAL
  713. int QuickRoot(int n, int iNum)
  714. {
  715. int iRoot = iNum;
  716. for (int i=10; i > 0; i--)
  717. {
  718. int iOld = iRoot;
  719. iRoot = (iRoot + iNum/iRoot)/2;
  720. if (iRoot == iOld)
  721. break;
  722. }
  723. return iRoot;
  724. }
  725. #endif
  726. BOOL CDragImages::_PreProcessDragBitmap(void** ppvBits)
  727. {
  728. BOOL fRet = FALSE;
  729. ASSERT(_hdcDragImage == NULL);
  730. _hdcDragImage = CreateCompatibleDC(NULL);
  731. if (_hdcDragImage)
  732. {
  733. ULONG* pul;
  734. HBITMAP hbmpResult = NULL;
  735. HBITMAP hbmpOld;
  736. HDC hdcSource = NULL;
  737. BITMAPINFO bmi = {0};
  738. HBITMAP hbmp = _shdi.hbmpDragImage;
  739. bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  740. bmi.bmiHeader.biWidth = _shdi.sizeDragImage.cx;
  741. bmi.bmiHeader.biHeight = _shdi.sizeDragImage.cy;
  742. bmi.bmiHeader.biPlanes = 1;
  743. bmi.bmiHeader.biBitCount = 32;
  744. bmi.bmiHeader.biCompression = BI_RGB;
  745. hdcSource = CreateCompatibleDC(_hdcDragImage);
  746. if (hdcSource)
  747. {
  748. hbmpResult = CreateDIBSection(_hdcDragImage,
  749. &bmi,
  750. DIB_RGB_COLORS,
  751. ppvBits,
  752. NULL,
  753. 0);
  754. if (hbmpResult)
  755. {
  756. _hbmpOld = (HBITMAP)SelectObject(_hdcDragImage, hbmpResult);
  757. hbmpOld = (HBITMAP)SelectObject(hdcSource, hbmp);
  758. BitBlt(_hdcDragImage, 0, 0, _shdi.sizeDragImage.cx, _shdi.sizeDragImage.cy,
  759. hdcSource, 0, 0, SRCCOPY);
  760. pul = (ULONG*)*ppvBits;
  761. int iOffsetX = _shdi.ptOffset.x;
  762. int iOffsetY = _shdi.ptOffset.y;
  763. int iDenomX = max(_shdi.sizeDragImage.cx - iOffsetX, iOffsetX);
  764. int iDenomY = max(_shdi.sizeDragImage.cy - iOffsetY, iOffsetY);
  765. BOOL fRadialFade = TRUE;
  766. // If both are less than the max, then no radial fade.
  767. if (_shdi.sizeDragImage.cy <= MAX_HEIGHT_ALPHA && _shdi.sizeDragImage.cx <= MAX_WIDTH_ALPHA)
  768. fRadialFade = FALSE;
  769. for (int Y = 0; Y < _shdi.sizeDragImage.cy; Y++)
  770. {
  771. int y = _shdi.sizeDragImage.cy - Y; // Bottom up DIB.
  772. for (int x = 0; x < _shdi.sizeDragImage.cx; x++)
  773. {
  774. RGBQUAD* prgb = (RGBQUAD*)&pul[Y * _shdi.sizeDragImage.cx + x];
  775. if (_shdi.crColorKey != CLR_NONE &&
  776. IsColorKey(*prgb, _shdi.crColorKey))
  777. {
  778. // Write a pre-multiplied value of 0:
  779. *((DWORD*)prgb) = 0;
  780. }
  781. else
  782. {
  783. int Alpha = prgb->rgbReserved;
  784. if (_shdi.crColorKey != CLR_NONE)
  785. {
  786. Alpha = DRAGDROP_ALPHA;
  787. }
  788. else
  789. {
  790. Alpha -= (Alpha / 3);
  791. }
  792. if (fRadialFade && Alpha > 0)
  793. {
  794. // This does not generate a smooth curve, but this is just
  795. // an effect, not trying to be accurate here.
  796. // 3 devides per pixel
  797. int ddx = (x < iOffsetX)? iOffsetX - x : x - iOffsetX;
  798. int ddy = (y < iOffsetY)? iOffsetY - y : y - iOffsetY;
  799. __int64 iAlphaX = (100000l - (((__int64)ddx * 100000l) / (iDenomX )));
  800. __int64 iAlphaY = (100000l - (((__int64)ddy * 100000l) / (iDenomY )));
  801. ASSERT (iAlphaX >= 0);
  802. ASSERT (iAlphaY >= 0);
  803. __int64 iDenom = 100000;
  804. iDenom *= 100000;
  805. Alpha = (int) ((Alpha * iAlphaX * iAlphaY * 100000) / (iDenom* 141428));
  806. }
  807. ASSERT(Alpha <= 0xFF);
  808. prgb->rgbReserved = (BYTE)Alpha;
  809. prgb->rgbRed = ((prgb->rgbRed * Alpha) + 128) / 255;
  810. prgb->rgbGreen = ((prgb->rgbGreen * Alpha) + 128) / 255;
  811. prgb->rgbBlue = ((prgb->rgbBlue * Alpha) + 128) / 255;
  812. }
  813. }
  814. }
  815. DeleteObject(hbmp);
  816. _shdi.hbmpDragImage = hbmpResult;
  817. fRet = TRUE;
  818. if (hbmpOld)
  819. SelectObject(hdcSource, hbmpOld);
  820. }
  821. DeleteObject(hdcSource);
  822. }
  823. }
  824. return fRet;
  825. }
  826. CLIPFORMAT _GetDragContentsCF()
  827. {
  828. static UINT s_cfDragContents = 0;
  829. if (0 == s_cfDragContents)
  830. s_cfDragContents = RegisterClipboardFormat(CFSTR_DRAGCONTEXT);
  831. return (CLIPFORMAT) s_cfDragContents;
  832. }
  833. CLIPFORMAT _GetDragImageBitssCF()
  834. {
  835. static UINT s_cfDragImageBitss = 0;
  836. if (0 == s_cfDragImageBitss)
  837. s_cfDragImageBitss = RegisterClipboardFormat(TEXT("DragImageBits"));
  838. return (CLIPFORMAT) s_cfDragImageBitss;
  839. }
  840. // persist our state into the data object. so on the target side they can grab this
  841. // data out and render the thing being dragged
  842. HRESULT CDragImages::_SaveToDataObject(IDataObject *pdtobj)
  843. {
  844. HRESULT hr = E_FAIL; // one form of the saves below must succeed
  845. if (Initialized())
  846. {
  847. STGMEDIUM medium = {0};
  848. medium.tymed = TYMED_ISTREAM;
  849. if (SUCCEEDED(CreateStreamOnHGlobal(NULL, TRUE, &medium.pstm)))
  850. {
  851. // Set the header .
  852. DragContextHeader hdr = {0};
  853. hdr.fImage = _fImage;
  854. hdr.fLayered = IsDraggingLayeredWindow();
  855. hdr.ptOffset = _ptOffset;
  856. //First Write the drag context header
  857. ULONG ulWritten;
  858. if (SUCCEEDED(medium.pstm->Write(&hdr, sizeof(hdr), &ulWritten)) &&
  859. (ulWritten == sizeof(hdr)))
  860. {
  861. if (hdr.fLayered)
  862. {
  863. STGMEDIUM mediumBits = {0};
  864. // Set the medium.
  865. mediumBits.tymed = TYMED_HGLOBAL;
  866. // Write out layered window information
  867. hr = _SaveLayerdBitmapBits(&mediumBits.hGlobal);
  868. if (SUCCEEDED(hr))
  869. {
  870. FORMATETC fmte = {_GetDragImageBitssCF(), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  871. // Set the medium in the data.
  872. hr = pdtobj->SetData(&fmte, &mediumBits, TRUE);
  873. if (FAILED(hr))
  874. ReleaseStgMedium(&mediumBits); // cleanup
  875. }
  876. }
  877. else if (hdr.fImage)
  878. {
  879. // write an image
  880. HIMAGELIST himl = ImageList_GetDragImage(NULL, NULL);
  881. if (ImageList_Write(himl, medium.pstm))
  882. {
  883. hr = S_OK; // success
  884. }
  885. }
  886. else
  887. {
  888. // multi rect
  889. if (SUCCEEDED(medium.pstm->Write(&_cItems, sizeof(_cItems), &ulWritten)) &&
  890. (ulWritten == sizeof(_cItems)))
  891. {
  892. // Write the rects into the stream
  893. if (SUCCEEDED(medium.pstm->Write(_parc, sizeof(_parc[0]) * _cItems, &ulWritten)) &&
  894. (ulWritten == (sizeof(_parc[0]) * _cItems)))
  895. {
  896. hr = S_OK; // success
  897. }
  898. }
  899. }
  900. if (SUCCEEDED(hr))
  901. {
  902. // Set the seek pointer at the beginning.
  903. medium.pstm->Seek(g_li0, STREAM_SEEK_SET, NULL);
  904. // Set the Formatetc
  905. FORMATETC fmte = {_GetDragContentsCF(), NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM};
  906. // Set the medium in the data.
  907. hr = pdtobj->SetData(&fmte, &medium, TRUE);
  908. }
  909. }
  910. if (FAILED(hr))
  911. ReleaseStgMedium(&medium);
  912. }
  913. }
  914. return hr;
  915. }
  916. // Gets the information to rebuild the drag images from the data object
  917. HRESULT CDragImages::_LoadFromDataObject(IDataObject *pdtobj)
  918. {
  919. // Check if we have a drag context
  920. HRESULT hr;
  921. // NULL pdtobj is for the old DAD_DragEnterXXX() APIs...
  922. // we hope this in the same process
  923. if (Initialized() || !pdtobj)
  924. {
  925. hr = S_OK; // already loaded
  926. }
  927. else
  928. {
  929. // Set the format we are interested in
  930. FORMATETC fmte = {_GetDragContentsCF(), NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM};
  931. //if the data object has the format we are interested in
  932. // then Get the data
  933. STGMEDIUM medium = {0};
  934. hr = pdtobj->GetData(&fmte, &medium);
  935. if (SUCCEEDED(hr)) // if no pstm, bag out.
  936. {
  937. // Set the seek pointer at the beginning. PARANOIA: This is for people
  938. // Who don't set the seek for me.
  939. medium.pstm->Seek(g_li0, STREAM_SEEK_SET, NULL);
  940. //First Read the drag context header
  941. DragContextHeader hdr;
  942. if (SUCCEEDED(IStream_Read(medium.pstm, &hdr, sizeof(hdr))))
  943. {
  944. if (hdr.fLayered)
  945. {
  946. STGMEDIUM mediumBits;
  947. FORMATETC fmte = {_GetDragImageBitssCF(), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  948. hr = pdtobj->GetData(&fmte, &mediumBits);
  949. if (SUCCEEDED(hr))
  950. {
  951. hr = _LoadLayerdBitmapBits(mediumBits.hGlobal);
  952. ReleaseStgMedium(&mediumBits);
  953. }
  954. }
  955. else if (hdr.fImage)
  956. {
  957. // single image
  958. HIMAGELIST himl = ImageList_Read(medium.pstm);
  959. if (himl)
  960. {
  961. DAD_SetDragImage(himl, &(hdr.ptOffset));
  962. ImageList_Destroy(himl);
  963. hr = S_OK;
  964. }
  965. }
  966. else
  967. {
  968. // multi rect
  969. int cItems;
  970. if (SUCCEEDED(IStream_Read(medium.pstm, &cItems, sizeof(cItems))))
  971. {
  972. RECT *prect = (RECT *)LocalAlloc(LPTR, sizeof(*prect) * cItems);
  973. if (prect)
  974. {
  975. if (SUCCEEDED(IStream_Read(medium.pstm, prect, sizeof(*prect) * cItems)))
  976. {
  977. hr = _SetMultiRectDragging(cItems, prect, &hdr.ptOffset);
  978. }
  979. LocalFree(prect);
  980. }
  981. }
  982. }
  983. }
  984. if (SUCCEEDED(hr))
  985. _InitDragData();
  986. // Set the seek pointer at the beginning. Just cleaning up...
  987. medium.pstm->Seek(g_li0, STREAM_SEEK_SET, NULL);
  988. // Release the stg medium.
  989. ReleaseStgMedium(&medium);
  990. }
  991. }
  992. return hr;
  993. }
  994. // Shows or hides the drag images. NOTE: Doesn't do anything in the layered window case.
  995. // We don't need to because this function is specifically for drawing to a locked window.
  996. STDMETHODIMP CDragImages::Show(BOOL bShow)
  997. {
  998. BOOL fOld = bShow;
  999. TraceMsg(TF_DRAGIMAGES, "CDragImages::Show(%s)", bShow? TEXT("true") : TEXT("false"));
  1000. if (!Initialized() || !_Single.bDragging)
  1001. {
  1002. return S_FALSE;
  1003. }
  1004. // No point in showing and hiding a Window. This causes unnecessary flicker.
  1005. if (_hwnd)
  1006. {
  1007. return S_OK;
  1008. }
  1009. // If we're going across thread boundaries we have to try a context switch
  1010. if (GetCurrentThreadId() != GetWindowThreadProcessId(_Single.hwndLock, NULL) &&
  1011. _ShowDragImageInterThread(_Single.hwndLock, &fOld))
  1012. return fOld;
  1013. fOld = _Single.bLocked;
  1014. //
  1015. // If we are going to show the drag image, lock the target window.
  1016. //
  1017. if (bShow && !_Single.bLocked)
  1018. {
  1019. TraceMsg(TF_DRAGIMAGES, "CDragImages::Show : Shown and not locked");
  1020. UpdateWindow(_Single.hwndLock);
  1021. LockWindowUpdate(_Single.hwndLock);
  1022. _Single.bLocked = TRUE;
  1023. }
  1024. if (_Single.bSingle)
  1025. {
  1026. TraceMsg(TF_DRAGIMAGES, "CDragImages::Show : Calling ImageList_DragShowNoLock");
  1027. ImageList_DragShowNolock(bShow);
  1028. }
  1029. else
  1030. {
  1031. TraceMsg(TF_DRAGIMAGES, "CDragImages::Show : MultiDragShow");
  1032. _MultipleDragShow(bShow);
  1033. }
  1034. //
  1035. // If we have just hide the drag image, unlock the target window.
  1036. //
  1037. if (!bShow && _Single.bLocked)
  1038. {
  1039. TraceMsg(TF_DRAGIMAGES, "CDragImages::Show : hiding image, unlocking");
  1040. LockWindowUpdate(NULL);
  1041. _Single.bLocked = FALSE;
  1042. }
  1043. return fOld ? S_OK : S_FALSE;
  1044. }
  1045. // tell the drag source to hide or unhide the drag image to allow
  1046. // the destination to do drawing (unlock the screen)
  1047. //
  1048. // in:
  1049. // bShow FALSE - hide the drag image, allow drawing
  1050. // TRUE - show the drag image, no drawing allowed after this
  1051. // Helper function for DAD_ShowDragImage - handles the inter-thread case.
  1052. // We need to handle this case differently because LockWindowUpdate calls fail
  1053. // if they are on the wrong thread.
  1054. BOOL CDragImages::_ShowDragImageInterThread(HWND hwndLock, BOOL * pfShow)
  1055. {
  1056. TCHAR szClassName[50];
  1057. if (GetClassName(hwndLock, szClassName, ARRAYSIZE(szClassName)))
  1058. {
  1059. UINT uMsg = 0;
  1060. ULONG_PTR dw = 0;
  1061. if (lstrcmpi(szClassName, TEXT("SHELLDLL_DefView")) == 0)
  1062. uMsg = WM_DSV_SHOWDRAGIMAGE;
  1063. if (lstrcmpi(szClassName, TEXT("CabinetWClass")) == 0)
  1064. uMsg = CWM_SHOWDRAGIMAGE;
  1065. if (uMsg)
  1066. {
  1067. SendMessageTimeout(hwndLock, uMsg, 0, *pfShow, SMTO_ABORTIFHUNG, 1000, &dw);
  1068. *pfShow = (dw != 0);
  1069. return TRUE;
  1070. }
  1071. }
  1072. return FALSE;
  1073. }
  1074. void CDragImages::ThreadDetach()
  1075. {
  1076. if (_idThread == GetCurrentThreadId())
  1077. FreeDragData();
  1078. }
  1079. void CDragImages::ProcessDetach()
  1080. {
  1081. FreeDragData();
  1082. }
  1083. BOOL CDragImages::SetDragImage(HIMAGELIST himl, int index, POINT * pptOffset)
  1084. {
  1085. if (himl)
  1086. {
  1087. // We are setting
  1088. if (Initialized())
  1089. return FALSE;
  1090. _fImage = TRUE;
  1091. if (pptOffset)
  1092. {
  1093. // Avoid the flicker by always pass even coords
  1094. _ptOffset.x = (pptOffset->x & ~1);
  1095. _ptOffset.y = (pptOffset->y & ~1);
  1096. }
  1097. ImageList_BeginDrag(himl, index, _ptOffset.x, _ptOffset.y);
  1098. _InitDragData();
  1099. }
  1100. else
  1101. {
  1102. FreeDragData();
  1103. }
  1104. return TRUE;
  1105. }
  1106. //=====================================================================
  1107. // Multile Drag show
  1108. //=====================================================================
  1109. void CDragImages::_MultipleDragShow(BOOL bShow)
  1110. {
  1111. HDC hDC;
  1112. int nRect;
  1113. RECT rc, rcClip;
  1114. if ((bShow && _Single._Multi.bShown) || (!bShow && !_Single._Multi.bShown))
  1115. return;
  1116. _Single._Multi.bShown = bShow;
  1117. // clip to window, NOT SM_CXSCREEN/SM_CYSCREEN (multiple monitors)
  1118. GetWindowRect(_Single.hwndLock, &rcClip);
  1119. rcClip.right -= rcClip.left;
  1120. rcClip.bottom -= rcClip.top;
  1121. hDC = GetDCEx(_Single.hwndLock, NULL, DCX_WINDOW | DCX_CACHE |
  1122. DCX_LOCKWINDOWUPDATE | DCX_CLIPSIBLINGS);
  1123. for (nRect = _Single._Multi.nRects - 1; nRect >= 0; --nRect)
  1124. {
  1125. rc = _Single._Multi.pRect[nRect];
  1126. OffsetRect(&rc, _Single._Multi.ptNow.x - _Single._Multi.ptOffset.x,
  1127. _Single._Multi.ptNow.y - _Single._Multi.ptOffset.y);
  1128. if ((rc.top < rcClip.bottom) && (rc.bottom > 0) &&
  1129. (rc.left < rcClip.right) && (rc.right > 0))
  1130. {
  1131. DrawFocusRect(hDC, &rc);
  1132. }
  1133. }
  1134. ReleaseDC(_Single.hwndLock, hDC);
  1135. }
  1136. void CDragImages::_MultipleDragStart(HWND hwndLock, LPRECT aRect, int nRects, POINT ptStart, POINT ptOffset)
  1137. {
  1138. _Single._Multi.bShown = FALSE;
  1139. _Single._Multi.pRect = aRect;
  1140. _Single._Multi.nRects = nRects;
  1141. _Single._Multi.ptOffset = ptOffset;
  1142. _Single._Multi.ptNow = ptStart;
  1143. }
  1144. void CDragImages::_MultipleDragMove(POINT ptNew)
  1145. {
  1146. if ((_Single._Multi.ptNow.x == ptNew.x) &&
  1147. (_Single._Multi.ptNow.y == ptNew.y))
  1148. {
  1149. // nothing has changed. bail
  1150. return;
  1151. }
  1152. if (_Single._Multi.bShown)
  1153. {
  1154. HDC hDC;
  1155. int nRect;
  1156. RECT rc, rcClip;
  1157. int dx1 = _Single._Multi.ptNow.x - _Single._Multi.ptOffset.x;
  1158. int dy1 = _Single._Multi.ptNow.y - _Single._Multi.ptOffset.y;
  1159. int dx2 = ptNew.x - _Single._Multi.ptNow.x;
  1160. int dy2 = ptNew.y - _Single._Multi.ptNow.y;
  1161. // clip to window, NOT SM_CXSCREEN/SM_CYSCREEN (multiple monitors)
  1162. GetWindowRect(_Single.hwndLock, &rcClip);
  1163. rcClip.right -= rcClip.left;
  1164. rcClip.bottom -= rcClip.top;
  1165. hDC = GetDCEx(_Single.hwndLock, NULL, DCX_WINDOW | DCX_CACHE |
  1166. DCX_LOCKWINDOWUPDATE | DCX_CLIPSIBLINGS);
  1167. for (nRect = _Single._Multi.nRects - 1; nRect >= 0; --nRect)
  1168. {
  1169. rc = _Single._Multi.pRect[nRect];
  1170. // hide pass
  1171. OffsetRect(&rc, dx1, dy1);
  1172. if ((rc.top < rcClip.bottom) && (rc.bottom > 0) &&
  1173. (rc.left < rcClip.right) && (rc.right > 0))
  1174. {
  1175. DrawFocusRect(hDC, &rc);
  1176. }
  1177. // show pass
  1178. OffsetRect(&rc, dx2, dy2);
  1179. if ((rc.top < rcClip.bottom) && (rc.bottom > 0) &&
  1180. (rc.left < rcClip.right) && (rc.right > 0))
  1181. {
  1182. DrawFocusRect(hDC, &rc);
  1183. }
  1184. }
  1185. ReleaseDC(_Single.hwndLock, hDC);
  1186. }
  1187. _Single._Multi.ptNow = ptNew;
  1188. }
  1189. HRESULT CDragImages::_SetMultiRectDragging(int cItems, LPRECT prect, POINT *pptOffset)
  1190. {
  1191. if (!Initialized())
  1192. {
  1193. // Multiple item drag
  1194. _cItems = cItems;
  1195. _parc = new RECT[2 * _cItems];
  1196. if (_parc)
  1197. {
  1198. for (int i = 0; i < cItems; i++)
  1199. _parc[i] = prect[i];
  1200. // Avoid the flicker by always pass even coords
  1201. _ptOffset.x = (pptOffset->x & ~1);
  1202. _ptOffset.y = (pptOffset->y & ~1);
  1203. _InitDragData();
  1204. }
  1205. }
  1206. return S_OK;
  1207. }
  1208. #define ListView_IsIconView(hwndLV) ((GetWindowLong(hwndLV, GWL_STYLE) & (UINT)LVS_TYPEMASK) == (UINT)LVS_ICON)
  1209. HRESULT CDragImages::_SetMultiItemDragging(HWND hwndLV, int cItems, POINT *pptOffset)
  1210. {
  1211. HRESULT hr = E_FAIL;
  1212. if (!Initialized())
  1213. {
  1214. // Multiple item drag
  1215. ASSERT(NULL == _parc);
  1216. _parc = new RECT[2 * cItems];
  1217. if (_parc)
  1218. {
  1219. POINT ptTemp;
  1220. int iLast, iNext;
  1221. int cxScreens, cyScreens;
  1222. LPRECT prcNext;
  1223. RECT rc;
  1224. _cItems = 0;
  1225. ASSERT(_fImage == FALSE);
  1226. //
  1227. // If this is a mirrored Window, then lead edge is going
  1228. // to be the far end in screen coord. So let's compute
  1229. // as the original code, and later in _MultipleDragMove
  1230. // we will compensate.
  1231. //
  1232. GetWindowRect( hwndLV , &rc );
  1233. ptTemp.x = rc.left;
  1234. ptTemp.y = rc.top;
  1235. //
  1236. // Reflect the shift the if the window is RTL mirrored.
  1237. //
  1238. if (IS_WINDOW_RTL_MIRRORED(hwndLV))
  1239. {
  1240. ptTemp.x = -ptTemp.x;
  1241. pptOffset->x = ((rc.right-rc.left)-pptOffset->x);
  1242. }
  1243. cxScreens = GetSystemMetrics(SM_CXVIRTUALSCREEN);
  1244. cyScreens = GetSystemMetrics(SM_CYVIRTUALSCREEN);
  1245. // for pre-Nashville platforms
  1246. if (!cxScreens || !cyScreens)
  1247. {
  1248. cxScreens = GetSystemMetrics(SM_CXSCREEN);
  1249. cyScreens = GetSystemMetrics(SM_CYSCREEN);
  1250. }
  1251. for (iNext = cItems - 1, iLast = -1, prcNext = _parc; iNext >= 0; --iNext)
  1252. {
  1253. iLast = ListView_GetNextItem(hwndLV, iLast, LVNI_SELECTED);
  1254. if (iLast != -1)
  1255. {
  1256. ListView_GetItemRect(hwndLV, iLast, &prcNext[0], LVIR_ICON);
  1257. OffsetRect(&prcNext[0], ptTemp.x, ptTemp.y);
  1258. if (((prcNext[0].left - pptOffset->x) < cxScreens) &&
  1259. ((pptOffset->x - prcNext[0].right) < cxScreens) &&
  1260. ((prcNext[0].top - pptOffset->y) < cyScreens))
  1261. {
  1262. ListView_GetItemRect(hwndLV, iLast, &prcNext[1], LVIR_LABEL);
  1263. OffsetRect(&prcNext[1], ptTemp.x, ptTemp.y);
  1264. if ((pptOffset->y - prcNext[1].bottom) < cxScreens)
  1265. {
  1266. //
  1267. // Fix 24857: Ask JoeB why we are drawing a bar instead of
  1268. // a text rectangle.
  1269. //
  1270. prcNext[1].top = (prcNext[1].top + prcNext[1].bottom)/2;
  1271. prcNext[1].bottom = prcNext[1].top + 2;
  1272. prcNext += 2;
  1273. _cItems += 2;
  1274. }
  1275. }
  1276. }
  1277. }
  1278. // Avoid the flicker by always pass even coords
  1279. _ptOffset.x = (pptOffset->x & ~1);
  1280. _ptOffset.y = (pptOffset->y & ~1);
  1281. _InitDragData();
  1282. hr = S_OK;
  1283. }
  1284. }
  1285. return hr;
  1286. }
  1287. //=====================================================================
  1288. // Cursor Merging
  1289. //=====================================================================
  1290. void CDragImages::_DestroyCachedCursors()
  1291. {
  1292. if (_himlCursors)
  1293. {
  1294. ImageList_Destroy(_himlCursors);
  1295. _himlCursors = NULL;
  1296. }
  1297. HCURSOR hcursor = GetCursor();
  1298. for (int i = 0; i < ARRAYSIZE(_ahcur); i++)
  1299. {
  1300. if (_ahcur[i])
  1301. {
  1302. if (_ahcur[i] == hcursor)
  1303. {
  1304. //
  1305. // Stuff in some random cursor so that we don't try to
  1306. // destroy the current cursor (and leak it too).
  1307. //
  1308. SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));
  1309. }
  1310. DestroyCursor(_ahcur[i]);
  1311. _ahcur[i] = NULL;
  1312. }
  1313. }
  1314. }
  1315. HBITMAP CDragImages::CreateColorBitmap(int cx, int cy)
  1316. {
  1317. HDC hdc = GetDC(NULL);
  1318. HBITMAP hbm = CreateCompatibleBitmap(hdc, cx, cy);
  1319. ReleaseDC(NULL, hdc);
  1320. return hbm;
  1321. }
  1322. #define CreateMonoBitmap( cx, cy) CreateBitmap(cx, cy, 1, 1, NULL)
  1323. typedef WORD CURMASK;
  1324. #define _BitSizeOf(x) (sizeof(x)*8)
  1325. HRESULT CDragImages::_GetCursorLowerRight(HCURSOR hcursor, int * px, int * py, POINT *pptHotSpot)
  1326. {
  1327. ICONINFO iconinfo;
  1328. HRESULT hr = E_FAIL;
  1329. if (GetIconInfo(hcursor, &iconinfo))
  1330. {
  1331. CURMASK CurMask[16*8];
  1332. BITMAP bm;
  1333. int i;
  1334. int xFine = 16;
  1335. GetObject(iconinfo.hbmMask, sizeof(bm), (LPTSTR)&bm);
  1336. GetBitmapBits(iconinfo.hbmMask, sizeof(CurMask), CurMask);
  1337. pptHotSpot->x = iconinfo.xHotspot;
  1338. pptHotSpot->y = iconinfo.yHotspot;
  1339. if (iconinfo.hbmColor)
  1340. {
  1341. i = (int)(bm.bmWidth * bm.bmHeight / _BitSizeOf(CURMASK) - 1);
  1342. }
  1343. else
  1344. {
  1345. i = (int)(bm.bmWidth * (bm.bmHeight/2) / _BitSizeOf(CURMASK) - 1);
  1346. }
  1347. if ( i >= sizeof(CurMask))
  1348. {
  1349. i = sizeof(CurMask) -1;
  1350. }
  1351. // this assumes that the first pixel encountered on this bottom
  1352. // up/right to left search will be reasonably close to the rightmost pixel
  1353. // which for all of our cursors is correct, but it not necessarly correct.
  1354. // also, it assumes the cursor has a good mask... not like the IBeam XOR only
  1355. // cursor
  1356. for (; i >= 0; i--)
  1357. {
  1358. if (CurMask[i] != 0xFFFF)
  1359. {
  1360. // this is only accurate to 16 pixels... which is a big gap..
  1361. // so let's try to be a bit more accurate.
  1362. int j;
  1363. DWORD dwMask;
  1364. for (j = 0; j < 16; j++, xFine--)
  1365. {
  1366. if (j < 8)
  1367. {
  1368. dwMask = (1 << (8 + j));
  1369. }
  1370. else
  1371. {
  1372. dwMask = (1 << (j - 8));
  1373. }
  1374. if (!(CurMask[i] & dwMask))
  1375. break;
  1376. }
  1377. ASSERT(j < 16);
  1378. break;
  1379. }
  1380. }
  1381. if (iconinfo.hbmColor)
  1382. {
  1383. DeleteObject(iconinfo.hbmColor);
  1384. }
  1385. if (iconinfo.hbmMask)
  1386. {
  1387. DeleteObject(iconinfo.hbmMask);
  1388. }
  1389. // Compute the pointer height
  1390. // use width in both directions because the cursor is square, but the
  1391. // height might be doubleheight if it's mono
  1392. *py = ((i + 1) * _BitSizeOf(CURMASK)) / (int)bm.bmWidth;
  1393. *px = ((i * _BitSizeOf(CURMASK)) % (int)bm.bmWidth) + xFine + 2; // hang it off a little
  1394. hr = S_OK;
  1395. }
  1396. return hr;
  1397. }
  1398. // this will draw iiMerge's image over iiMain on main's lower right.
  1399. BOOL CDragImages::_MergeIcons(HCURSOR hcursor, LPCTSTR idMerge, HBITMAP *phbmImage, HBITMAP *phbmMask, POINT* pptHotSpot)
  1400. {
  1401. *phbmImage = NULL;
  1402. *phbmMask = NULL;
  1403. BOOL fRet = FALSE;
  1404. int xDraw;
  1405. int yDraw;
  1406. // find the lower corner of the cursor and put it there.
  1407. // do this whether or not we have an idMerge because it will set the hotspot
  1408. if (SUCCEEDED(_GetCursorLowerRight(hcursor, &xDraw, &yDraw, pptHotSpot)))
  1409. {
  1410. int xBitmap;
  1411. int yBitmap;
  1412. int xCursor = GetSystemMetrics(SM_CXCURSOR);
  1413. int yCursor = GetSystemMetrics(SM_CYCURSOR);
  1414. HBITMAP hbmp;
  1415. if (idMerge != (LPCTSTR)-1)
  1416. {
  1417. hbmp = (HBITMAP)LoadImage(HINST_THISDLL, idMerge, IMAGE_BITMAP, 0, 0, 0);
  1418. if (hbmp)
  1419. {
  1420. BITMAP bm;
  1421. GetObject(hbmp, sizeof(bm), &bm);
  1422. xBitmap = bm.bmWidth;
  1423. yBitmap = bm.bmHeight/2;
  1424. if (xDraw + xBitmap > xCursor)
  1425. xDraw = xCursor - xBitmap;
  1426. if (yDraw + yBitmap > yCursor)
  1427. yDraw = yCursor - yBitmap;
  1428. }
  1429. }
  1430. else
  1431. hbmp = NULL;
  1432. HDC hdcCursor = CreateCompatibleDC(NULL);
  1433. HBITMAP hbmMask = CreateMonoBitmap(xCursor, yCursor);
  1434. HBITMAP hbmImage = CreateColorBitmap(xCursor, yCursor);
  1435. if (hdcCursor && hbmMask && hbmImage)
  1436. {
  1437. HBITMAP hbmTemp = (HBITMAP)SelectObject(hdcCursor, hbmImage);
  1438. DrawIconEx(hdcCursor, 0, 0, hcursor, 0, 0, 0, NULL, DI_NORMAL);
  1439. HDC hdcBitmap;
  1440. if (hbmp)
  1441. {
  1442. hdcBitmap = CreateCompatibleDC(NULL);
  1443. SelectObject(hdcBitmap, hbmp);
  1444. //blt the two bitmaps onto the color and mask bitmaps for the cursor
  1445. BitBlt(hdcCursor, xDraw, yDraw, xBitmap, yBitmap, hdcBitmap, 0, 0, SRCCOPY);
  1446. }
  1447. SelectObject(hdcCursor, hbmMask);
  1448. DrawIconEx(hdcCursor, 0, 0, hcursor, 0, 0, 0, NULL, DI_MASK);
  1449. if (hbmp)
  1450. {
  1451. BitBlt(hdcCursor, xDraw, yDraw, xBitmap, yBitmap, hdcBitmap, 0, yBitmap, SRCCOPY);
  1452. // select back in the old bitmaps
  1453. SelectObject(hdcBitmap, hbmTemp);
  1454. DeleteDC(hdcBitmap);
  1455. DeleteObject(hbmp);
  1456. }
  1457. // select back in the old bitmaps
  1458. SelectObject(hdcCursor, hbmTemp);
  1459. }
  1460. if (hdcCursor)
  1461. DeleteDC(hdcCursor);
  1462. *phbmImage = hbmImage;
  1463. *phbmMask = hbmMask;
  1464. fRet = (hbmImage && hbmMask);
  1465. }
  1466. return fRet;
  1467. }
  1468. // this will take a cursor index and load
  1469. int CDragImages::_AddCursorToImageList(HCURSOR hcur, LPCTSTR idMerge, POINT *pptHotSpot)
  1470. {
  1471. int iIndex;
  1472. HBITMAP hbmImage, hbmMask;
  1473. // merge in the plus or link arrow if it's specified
  1474. if (_MergeIcons(hcur, idMerge, &hbmImage, &hbmMask, pptHotSpot))
  1475. {
  1476. iIndex = ImageList_Add(_himlCursors, hbmImage, hbmMask);
  1477. }
  1478. else
  1479. {
  1480. iIndex = -1;
  1481. }
  1482. if (hbmImage)
  1483. DeleteObject(hbmImage);
  1484. if (hbmMask)
  1485. DeleteObject(hbmMask);
  1486. return iIndex;
  1487. }
  1488. int _MapEffectToId(DWORD dwEffect)
  1489. {
  1490. int idCursor;
  1491. // DebugMsg(DM_TRACE, "sh TR - DAD_GiveFeedBack dwEffect=%x", dwEffect);
  1492. switch (dwEffect & (DROPEFFECT_COPY|DROPEFFECT_LINK|DROPEFFECT_MOVE))
  1493. {
  1494. case 0:
  1495. idCursor = DCID_NO;
  1496. break;
  1497. case DROPEFFECT_COPY:
  1498. idCursor = DCID_COPY;
  1499. break;
  1500. case DROPEFFECT_LINK:
  1501. idCursor = DCID_LINK;
  1502. break;
  1503. case DROPEFFECT_MOVE:
  1504. idCursor = DCID_MOVE;
  1505. break;
  1506. default:
  1507. // if it's a right drag, we can have any effect... we'll
  1508. // default to the arrow without merging in anything
  1509. idCursor = DCID_MOVE;
  1510. break;
  1511. }
  1512. return idCursor;
  1513. }
  1514. int CDragImages::_MapCursorIDToImageListIndex(int idCur)
  1515. {
  1516. const static struct
  1517. {
  1518. BOOL fSystem;
  1519. LPCTSTR idRes;
  1520. LPCTSTR idMerge;
  1521. }
  1522. c_acurmap[DCID_MAX] =
  1523. {
  1524. { FALSE, MAKEINTRESOURCE(IDC_NULL), (LPCTSTR)-1},
  1525. { TRUE, IDC_NO, (LPCTSTR)-1 },
  1526. { TRUE, IDC_ARROW, (LPCTSTR)-1 },
  1527. { TRUE, IDC_ARROW, MAKEINTRESOURCE(IDB_PLUS_MERGE) },
  1528. { TRUE, IDC_ARROW, MAKEINTRESOURCE(IDB_LINK_MERGE) },
  1529. };
  1530. ASSERT(idCur >= -1 && idCur < (int)ARRAYSIZE(c_acurmap));
  1531. // -1 means "Initialize the image list index array".
  1532. if (idCur == -1)
  1533. {
  1534. for (int i = 0; i < ARRAYSIZE(c_acurmap); i++)
  1535. {
  1536. _aindex[i] = -1;
  1537. }
  1538. idCur = 0; // fall through to return -1
  1539. }
  1540. else
  1541. {
  1542. if (_aindex[idCur] == -1)
  1543. {
  1544. HINSTANCE hinst = c_acurmap[idCur].fSystem ? NULL : HINST_THISDLL;
  1545. HCURSOR hcur = LoadCursor(hinst, c_acurmap[idCur].idRes);
  1546. if (hcur)
  1547. {
  1548. _aindex[idCur] = _AddCursorToImageList(hcur, c_acurmap[idCur].idMerge, &_aptHotSpot[idCur]);
  1549. }
  1550. }
  1551. }
  1552. return _aindex[idCur];
  1553. }
  1554. HCURSOR CDragImages::_SetCursorHotspot(HCURSOR hcur, POINT *ptHot)
  1555. {
  1556. ICONINFO iconinfo = { 0 };
  1557. HCURSOR hcurHotspot;
  1558. GetIconInfo(hcur, &iconinfo);
  1559. iconinfo.xHotspot = ptHot->x;
  1560. iconinfo.yHotspot = ptHot->y;
  1561. iconinfo.fIcon = FALSE;
  1562. hcurHotspot = (HCURSOR)CreateIconIndirect(&iconinfo);
  1563. if (iconinfo.hbmColor)
  1564. {
  1565. DeleteObject(iconinfo.hbmColor);
  1566. }
  1567. if (iconinfo.hbmMask)
  1568. {
  1569. DeleteObject(iconinfo.hbmMask);
  1570. }
  1571. return hcurHotspot;
  1572. }
  1573. void CDragImages::SetDropEffectCursor(int idCur)
  1574. {
  1575. if (_himlCursors && (idCur != -1))
  1576. {
  1577. if (!_ahcur[idCur])
  1578. {
  1579. int iIndex = _MapCursorIDToImageListIndex(idCur);
  1580. if (iIndex != -1)
  1581. {
  1582. HCURSOR hcurColor = ImageList_GetIcon(_himlCursors, iIndex, 0);
  1583. //
  1584. // On non C1_COLORCURSOR displays, CopyImage() will enforce
  1585. // monochrome. So on color cursor displays, we'll get colored
  1586. // dragdrop pix.
  1587. //
  1588. HCURSOR hcurScreen = (HCURSOR)CopyImage(hcurColor, IMAGE_CURSOR,
  1589. 0, 0, LR_COPYRETURNORG | LR_DEFAULTSIZE);
  1590. HCURSOR hcurFinal = _SetCursorHotspot(hcurScreen, &_aptHotSpot[idCur]);
  1591. if ((hcurScreen != hcurColor) && hcurColor)
  1592. {
  1593. DestroyCursor(hcurColor);
  1594. }
  1595. if (hcurFinal)
  1596. {
  1597. if (hcurScreen)
  1598. {
  1599. DestroyCursor(hcurScreen);
  1600. }
  1601. }
  1602. else
  1603. {
  1604. hcurFinal = hcurScreen;
  1605. }
  1606. _ahcur[idCur] = hcurFinal;
  1607. }
  1608. }
  1609. if (_ahcur[idCur])
  1610. {
  1611. //
  1612. // This code assumes that SetCursor is pretty quick if it is
  1613. // already set.
  1614. //
  1615. SetCursor(_ahcur[idCur]);
  1616. }
  1617. }
  1618. }
  1619. //=====================================================================
  1620. // CDropSource
  1621. //=====================================================================
  1622. class CDropSource : public IDropSource
  1623. {
  1624. private:
  1625. LONG _cRef;
  1626. DWORD _grfInitialKeyState;
  1627. IDataObject* _pdtobj;
  1628. public:
  1629. explicit CDropSource(IDataObject *pdtobj);
  1630. virtual ~CDropSource();
  1631. // IUnknown methods
  1632. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  1633. STDMETHODIMP_(ULONG) AddRef();
  1634. STDMETHODIMP_(ULONG) Release();
  1635. // IDropSource methods
  1636. STDMETHODIMP GiveFeedback(DWORD dwEffect);
  1637. STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState);
  1638. };
  1639. void DAD_ShowCursor(BOOL fShow)
  1640. {
  1641. static BOOL s_fCursorHidden = FALSE;
  1642. if (fShow)
  1643. {
  1644. if (s_fCursorHidden)
  1645. {
  1646. ShowCursor(TRUE);
  1647. s_fCursorHidden = FALSE;
  1648. }
  1649. }
  1650. else
  1651. {
  1652. if (!s_fCursorHidden)
  1653. {
  1654. ShowCursor(FALSE);
  1655. s_fCursorHidden = TRUE;
  1656. }
  1657. }
  1658. }
  1659. CDropSource::CDropSource(IDataObject *pdtobj) : _cRef(1), _pdtobj(pdtobj), _grfInitialKeyState(0)
  1660. {
  1661. _pdtobj->AddRef();
  1662. // Tell the data object that we're entering the drag loop.
  1663. DataObj_SetDWORD(_pdtobj, g_cfInDragLoop, 1);
  1664. }
  1665. CDropSource::~CDropSource()
  1666. {
  1667. DAD_ShowCursor(TRUE); // just in case
  1668. _pdtobj->Release();
  1669. }
  1670. //
  1671. // Create an instance of CDropSource
  1672. //
  1673. STDMETHODIMP CDropSource_CreateInstance(IDropSource **ppdsrc, IDataObject *pdtobj)
  1674. {
  1675. *ppdsrc = new CDropSource(pdtobj);
  1676. return *ppdsrc ? S_OK : E_OUTOFMEMORY;
  1677. }
  1678. STDMETHODIMP CDropSource::QueryInterface(REFIID riid, void **ppvObj)
  1679. {
  1680. static const QITAB qit[] = {
  1681. QITABENT(CDropSource, IDropSource),
  1682. { 0 },
  1683. };
  1684. return QISearch(this, qit, riid, ppvObj);
  1685. }
  1686. STDMETHODIMP_(ULONG) CDropSource::AddRef()
  1687. {
  1688. return InterlockedIncrement(&_cRef);
  1689. }
  1690. STDMETHODIMP_(ULONG) CDropSource::Release()
  1691. {
  1692. if (InterlockedDecrement(&_cRef))
  1693. return _cRef;
  1694. delete this;
  1695. return 0;
  1696. }
  1697. STDMETHODIMP CDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
  1698. {
  1699. HRESULT hr = S_OK;
  1700. if (fEscapePressed)
  1701. {
  1702. hr = DRAGDROP_S_CANCEL;
  1703. }
  1704. else
  1705. {
  1706. // initialize ourself with the drag begin button
  1707. if (_grfInitialKeyState == 0)
  1708. _grfInitialKeyState = (grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
  1709. // If the window is hung for a while, the drag operation can happen before
  1710. // the first call to this function, so grfInitialKeyState will be 0. If this
  1711. // happened, then we did a drop. No need to assert...
  1712. //ASSERT(this->grfInitialKeyState);
  1713. if (!(grfKeyState & _grfInitialKeyState))
  1714. {
  1715. //
  1716. // A button is released.
  1717. //
  1718. hr = DRAGDROP_S_DROP;
  1719. }
  1720. else if (_grfInitialKeyState != (grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)))
  1721. {
  1722. //
  1723. // If the button state is changed (except the drop case, which we handle
  1724. // above, cancel the drag&drop.
  1725. //
  1726. hr = DRAGDROP_S_CANCEL;
  1727. }
  1728. }
  1729. if (hr != S_OK)
  1730. {
  1731. SetCursor(LoadCursor(NULL, IDC_ARROW));
  1732. DAD_ShowCursor(TRUE);
  1733. DAD_SetDragCursor(DCID_NULL);
  1734. // Tell the data object that we're leaving the drag loop.
  1735. if (_pdtobj)
  1736. DataObj_SetDWORD(_pdtobj, g_cfInDragLoop, 0);
  1737. }
  1738. return hr;
  1739. }
  1740. STDMETHODIMP CDropSource::GiveFeedback(DWORD dwEffect)
  1741. {
  1742. int idCursor = _MapEffectToId(dwEffect);
  1743. //
  1744. // OLE does not give us DROPEFFECT_MOVE even though our IDT::DragOver
  1745. // returns it, if we haven't set that bit when we have called DoDragDrop.
  1746. // Instead of arguing whether or not this is a bug or by-design of OLE,
  1747. // we work around it. It is important to note that this hack around
  1748. // g_fDraggingOverSource is purely visual hack. It won't affect the
  1749. // actual drag&drop operations at all (DV_AlterEffect does it all).
  1750. //
  1751. // - SatoNa
  1752. //
  1753. if (idCursor == DCID_NO && g_fDraggingOverSource)
  1754. {
  1755. idCursor = DCID_MOVE;
  1756. }
  1757. //
  1758. // No need to merge the cursor, if we are not dragging over to
  1759. // one of shell windows.
  1760. //
  1761. if (DAD_IsDraggingImage())
  1762. {
  1763. // Feedback for single (image) dragging
  1764. DAD_ShowCursor(FALSE);
  1765. DAD_SetDragCursor(idCursor);
  1766. }
  1767. else if (DAD_IsDragging() && g_pdiDragImages)
  1768. {
  1769. // Feedback for multiple (rectangles) dragging
  1770. g_pdiDragImages->SetDropEffectCursor(idCursor);
  1771. DAD_ShowCursor(TRUE);
  1772. return NOERROR;
  1773. }
  1774. else
  1775. {
  1776. DAD_ShowCursor(TRUE);
  1777. }
  1778. return DRAGDROP_S_USEDEFAULTCURSORS;
  1779. }
  1780. //=====================================================================
  1781. // DAD
  1782. //=====================================================================
  1783. void FixupDragPoint(HWND hwnd, POINT* ppt)
  1784. {
  1785. if (hwnd)
  1786. {
  1787. RECT rc = {0};
  1788. GetWindowRect(hwnd, &rc);
  1789. ppt->x += rc.left;
  1790. ppt->y += rc.top;
  1791. }
  1792. }
  1793. BOOL DAD_InitDragImages()
  1794. {
  1795. if (!g_pdiDragImages)
  1796. CDragImages_CreateInstance(NULL, IID_IDragSourceHelper, NULL);
  1797. return g_pdiDragImages != NULL;
  1798. }
  1799. STDAPI_(BOOL) DAD_ShowDragImage(BOOL bShow)
  1800. {
  1801. if (DAD_InitDragImages())
  1802. return g_pdiDragImages->Show(bShow) == S_OK ? TRUE : FALSE;
  1803. return FALSE;
  1804. }
  1805. BOOL DAD_IsDragging()
  1806. {
  1807. if (DAD_InitDragImages())
  1808. return g_pdiDragImages->IsDragging();
  1809. return FALSE;
  1810. }
  1811. void DAD_SetDragCursor(int idCursor)
  1812. {
  1813. if (DAD_InitDragImages())
  1814. g_pdiDragImages->SetDragCursor(idCursor);
  1815. }
  1816. STDAPI_(BOOL) DAD_DragEnterEx3(HWND hwndTarget, const POINTL ptStart, IDataObject *pdtobj)
  1817. {
  1818. RECT rc;
  1819. GetWindowRect(hwndTarget, &rc);
  1820. // If hwndTarget is RTL mirrored, then measure the
  1821. // the client point from the visual right edge
  1822. // (near edge in RTL mirrored windows). [samera]
  1823. POINT pt;
  1824. if (IS_WINDOW_RTL_MIRRORED(hwndTarget))
  1825. pt.x = rc.right - ptStart.x;
  1826. else
  1827. pt.x = ptStart.x - rc.left;
  1828. pt.y = ptStart.y - rc.top;
  1829. return DAD_DragEnterEx2(hwndTarget, pt, pdtobj);
  1830. }
  1831. STDAPI_(BOOL) DAD_DragEnterEx2(HWND hwndTarget, const POINT ptStart, IDataObject *pdtobj)
  1832. {
  1833. BOOL bRet = FALSE;
  1834. if (DAD_InitDragImages())
  1835. {
  1836. POINT pt = ptStart;
  1837. FixupDragPoint(hwndTarget, &pt);
  1838. bRet = SUCCEEDED(g_pdiDragImages->DragEnter(hwndTarget, pdtobj, &pt, NULL));
  1839. }
  1840. return bRet;
  1841. }
  1842. STDAPI_(BOOL) DAD_DragEnterEx(HWND hwndTarget, const POINT ptStart)
  1843. {
  1844. return DAD_DragEnterEx2(hwndTarget, ptStart, NULL);
  1845. }
  1846. STDAPI_(BOOL) DAD_DragEnter(HWND hwndTarget)
  1847. {
  1848. POINT ptStart;
  1849. GetCursorPos(&ptStart);
  1850. if (hwndTarget)
  1851. ScreenToClient(hwndTarget, &ptStart);
  1852. return DAD_DragEnterEx(hwndTarget, ptStart);
  1853. }
  1854. STDAPI_(BOOL) DAD_DragMoveEx(HWND hwndTarget, const POINTL ptStart)
  1855. {
  1856. RECT rc;
  1857. GetWindowRect(hwndTarget, &rc);
  1858. // If hwndTarget is RTL mirrored, then measure the
  1859. // the client point from the visual right edge
  1860. // (near edge in RTL mirrored windows). [samera]
  1861. POINT pt;
  1862. if (IS_WINDOW_RTL_MIRRORED(hwndTarget))
  1863. pt.x = rc.right - ptStart.x;
  1864. else
  1865. pt.x = ptStart.x - rc.left;
  1866. pt.y = ptStart.y - rc.top;
  1867. return DAD_DragMove(pt);
  1868. }
  1869. STDAPI_(BOOL) DAD_DragMove(POINT pt)
  1870. {
  1871. if (DAD_InitDragImages())
  1872. {
  1873. FixupDragPoint(g_pdiDragImages->GetTarget(), &pt);
  1874. return g_pdiDragImages->DragOver(&pt, 0);
  1875. }
  1876. return FALSE;
  1877. }
  1878. STDAPI_(BOOL) DAD_SetDragImage(HIMAGELIST him, POINT *pptOffset)
  1879. {
  1880. if (DAD_InitDragImages() && !g_pdiDragImages->IsDraggingLayeredWindow())
  1881. {
  1882. //
  1883. // DAD_SetDragImage(-1, NULL) means "clear the drag image only
  1884. // if the image is set by this thread"
  1885. //
  1886. if (him == (HIMAGELIST)-1)
  1887. {
  1888. BOOL fThisThreadHasImage = FALSE;
  1889. ENTERCRITICAL;
  1890. if (g_pdiDragImages->Initialized() && g_pdiDragImages->GetThread() == GetCurrentThreadId())
  1891. {
  1892. fThisThreadHasImage = TRUE;
  1893. }
  1894. LEAVECRITICAL;
  1895. if (fThisThreadHasImage)
  1896. {
  1897. g_pdiDragImages->FreeDragData();
  1898. return TRUE;
  1899. }
  1900. return FALSE;
  1901. }
  1902. return g_pdiDragImages->SetDragImage(him, 0, pptOffset);
  1903. }
  1904. return TRUE;
  1905. }
  1906. //
  1907. // This function returns TRUE, if we are dragging an image. It means
  1908. // you have called either DAD_SetDragImage (with him != NULL) or
  1909. // DAD_SetDragImageFromListview.
  1910. //
  1911. BOOL DAD_IsDraggingImage(void)
  1912. {
  1913. if (DAD_InitDragImages())
  1914. return g_pdiDragImages->IsDraggingImage();
  1915. return FALSE;
  1916. }
  1917. STDAPI_(BOOL) DAD_DragLeave()
  1918. {
  1919. if (DAD_InitDragImages())
  1920. return g_pdiDragImages->DragLeave();
  1921. return FALSE;
  1922. }
  1923. STDAPI_(void) DAD_ProcessDetach(void)
  1924. {
  1925. if (g_pdiDragImages)
  1926. {
  1927. g_pdiDragImages->ProcessDetach();
  1928. g_pdiDragImages->Release();
  1929. }
  1930. }
  1931. STDAPI_(void) DAD_ThreadDetach(void)
  1932. {
  1933. if (g_pdiDragImages)
  1934. g_pdiDragImages->ThreadDetach();
  1935. }
  1936. // called from defview on SPI_SETCURSORS (user changed the system cursors)
  1937. STDAPI_(void) DAD_InvalidateCursors(void)
  1938. {
  1939. g_cRev++;
  1940. }
  1941. STDAPI_(BOOL) DAD_SetDragImageFromWindow(HWND hwnd, POINT* ppt, IDataObject* pdtobj)
  1942. {
  1943. if (DAD_InitDragImages())
  1944. return S_OK == g_pdiDragImages->InitializeFromWindow(hwnd, ppt, pdtobj);
  1945. return FALSE;
  1946. }
  1947. // shell32.dll export, but only used by print queue window code
  1948. //
  1949. STDAPI_(BOOL) DAD_SetDragImageFromListView(HWND hwndLV, POINT ptOffset)
  1950. {
  1951. // really a nop, as this does not have access to the data object
  1952. return DAD_InitDragImages();
  1953. }
  1954. // wrapper around OLE DoDragDrop(), will create drag source on demand and supports
  1955. // drag images for you
  1956. STDAPI SHDoDragDrop(HWND hwnd, IDataObject *pdtobj, IDropSource *pdsrc, DWORD dwEffect, DWORD *pdwEffect)
  1957. {
  1958. IDropSource *pdsrcRelease = NULL;
  1959. if (pdsrc == NULL)
  1960. {
  1961. CDropSource_CreateInstance(&pdsrcRelease, pdtobj);
  1962. pdsrc = pdsrcRelease;
  1963. }
  1964. // if there is no drag contents clipboard format present, try to add it
  1965. FORMATETC fmte = {_GetDragContentsCF(), NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM};
  1966. if (S_OK != pdtobj->QueryGetData(&fmte))
  1967. {
  1968. if (DAD_InitDragImages())
  1969. g_pdiDragImages->InitializeFromWindow(hwnd, NULL, pdtobj);
  1970. }
  1971. HRESULT hr = DoDragDrop(pdtobj, pdsrc, dwEffect, pdwEffect);
  1972. if (pdsrcRelease)
  1973. pdsrcRelease->Release();
  1974. return hr;
  1975. }