Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

434 lines
13 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: dragdrop.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Stuff for object-oriented direct manipulation, designed first for the shell.
  7. *
  8. * History:
  9. * 08-06-91 darrinm Ported from Win 3.1.
  10. \***************************************************************************/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. PCURSOR xxxQueryDropObject(PWND pwnd, LPDROPSTRUCT lpds);
  14. /***************************************************************************\
  15. * DragObject (API)
  16. *
  17. * Contains the main dragging loop.
  18. *
  19. * History:
  20. * 08-06-91 darrinm Ported from Win 3.1 sources.
  21. \***************************************************************************/
  22. DWORD xxxDragObject(
  23. PWND pwndParent,
  24. PWND pwndFrom, // NULL is valid
  25. UINT wFmt,
  26. ULONG_PTR dwData,
  27. PCURSOR pcur)
  28. {
  29. MSG msg, msgKey;
  30. DWORD result = 0;
  31. BOOL fDrag = TRUE;
  32. LPDROPSTRUCT lpds;
  33. PWND pwndDragging = NULL;
  34. PWND pwndTop;
  35. PCURSOR pcurOld, pcurT;
  36. PWND pwndT;
  37. TL tlpwndT;
  38. TL tlpwndTop;
  39. TL tlpwndDragging;
  40. TL tlPool;
  41. PTHREADINFO pti = PtiCurrent();
  42. CheckLock(pwndParent);
  43. CheckLock(pwndFrom);
  44. CheckLock(pcur);
  45. UserAssert(IsWinEventNotifyDeferredOK());
  46. lpds = (LPDROPSTRUCT)UserAllocPoolWithQuota(2 * sizeof(DROPSTRUCT), TAG_DRAGDROP);
  47. if (lpds == NULL)
  48. return 0;
  49. ThreadLockPool(pti, lpds, &tlPool);
  50. lpds->hwndSource = HW(pwndFrom);
  51. lpds->wFmt = wFmt;
  52. lpds->dwData = dwData;
  53. if (pcur != NULL) {
  54. /*
  55. * No need to DeferWinEventNotify() - pwndFrom is locked
  56. */
  57. pcurOld = zzzSetCursor(pcur);
  58. } else {
  59. pcurOld = pti->pq->spcurCurrent;
  60. }
  61. if (pwndFrom) {
  62. for (pwndTop = pwndFrom; TestwndChild(pwndTop);
  63. pwndTop = pwndTop->spwndParent) ;
  64. ThreadLockWithPti(pti, pwndTop, &tlpwndTop);
  65. xxxUpdateWindow(pwndTop);
  66. ThreadUnlock(&tlpwndTop);
  67. }
  68. xxxWindowEvent(EVENT_SYSTEM_DRAGDROPSTART, pwndFrom, OBJID_WINDOW, INDEXID_CONTAINER, 0);
  69. xxxSetCapture(pwndFrom);
  70. zzzShowCursor(TRUE);
  71. ThreadLockWithPti(pti, pwndDragging, &tlpwndDragging);
  72. while (fDrag && pti->pq->spwndCapture == pwndFrom) {
  73. while (!(xxxPeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) ||
  74. xxxPeekMessage(&msg, NULL, WM_QUEUESYNC, WM_QUEUESYNC, PM_REMOVE) ||
  75. xxxPeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))) {
  76. if (!xxxSleepThread(QS_MOUSE | QS_KEY, 0, TRUE)) {
  77. ThreadUnlock(&tlpwndDragging);
  78. ThreadUnlockAndFreePool(pti, &tlPool);
  79. return 0;
  80. }
  81. }
  82. /*
  83. * Be sure to eliminate any extra keydown messages that are
  84. * being queued up by MOUSE message processing.
  85. */
  86. while (xxxPeekMessage(&msgKey, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
  87. ;
  88. if ( (pti->pq->spwndCapture != pwndFrom) ||
  89. (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) )
  90. {
  91. if (pcurT = SYSCUR(NO))
  92. zzzSetCursor(pcurT);
  93. break;
  94. }
  95. RtlCopyMemory(lpds + 1, lpds, sizeof(DROPSTRUCT));
  96. /*
  97. * in screen coordinates
  98. */
  99. lpds->ptDrop = msg.pt;
  100. pcurT = xxxQueryDropObject(pwndParent, lpds);
  101. /*
  102. * Returning FALSE to a WM_QUERYDROPOBJECT message means drops
  103. * aren't supported and the 'illegal drop target' cursor should be
  104. * displayed. Returning TRUE means the target is valid and the
  105. * regular drag cursor should be displayed. Also, through a bit
  106. * of polymorphic magic one can return a cursor handle to override
  107. * the normal drag cursor.
  108. */
  109. if (pcurT == (PCURSOR)FALSE) {
  110. pcurT = SYSCUR(NO);
  111. lpds->hwndSink = NULL;
  112. } else if (pcurT == (PCURSOR)TRUE) {
  113. pcurT = pcur;
  114. }
  115. if (pcurT != NULL)
  116. zzzSetCursor(pcurT);
  117. /*
  118. * send the WM_DRAGLOOP after the above zzzSetCursor() to allow the
  119. * receiver to change the cursor at WM_DRAGLOOP time with a zzzSetCursor()
  120. */
  121. if (pwndFrom) {
  122. xxxSendMessage(pwndFrom, WM_DRAGLOOP, (pcurT != SYSCUR(NO)),
  123. (LPARAM)lpds);
  124. }
  125. /*
  126. * send these messages internally only
  127. */
  128. if (pwndDragging != RevalidateHwnd(lpds->hwndSink)) {
  129. if (pwndDragging != NULL) {
  130. xxxSendMessage(pwndDragging, WM_DRAGSELECT, FALSE,
  131. (LPARAM)(lpds + 1));
  132. }
  133. pwndDragging = RevalidateHwnd(lpds->hwndSink);
  134. ThreadUnlock(&tlpwndDragging);
  135. ThreadLockWithPti(pti, pwndDragging, &tlpwndDragging);
  136. if (pwndDragging != NULL) {
  137. xxxSendMessage(pwndDragging, WM_DRAGSELECT, TRUE, (LPARAM)lpds);
  138. }
  139. } else {
  140. if (pwndDragging != NULL) {
  141. xxxSendMessage(pwndDragging, WM_DRAGMOVE, 0, (LPARAM)lpds);
  142. }
  143. }
  144. switch (msg.message) {
  145. case WM_LBUTTONUP:
  146. case WM_NCLBUTTONUP:
  147. fDrag = FALSE;
  148. break;
  149. }
  150. }
  151. ThreadUnlock(&tlpwndDragging);
  152. /*
  153. * If the capture has been lost (i.e. fDrag == TRUE), don't do the drop.
  154. */
  155. if (fDrag)
  156. pcurT = SYSCUR(NO);
  157. /*
  158. * before the actual drop, clean up the cursor, as the app may do
  159. * stuff here...
  160. */
  161. xxxReleaseCapture();
  162. zzzShowCursor(FALSE);
  163. zzzSetCursor(pcurOld);
  164. /*
  165. * we either got lbuttonup or enter
  166. */
  167. if (pcurT != SYSCUR(NO)) {
  168. /*
  169. * object allows drop... send drop message
  170. */
  171. pwndT = ValidateHwnd(lpds->hwndSink);
  172. if (pwndT != NULL) {
  173. ThreadLockAlwaysWithPti(pti, pwndT, &tlpwndT);
  174. /*
  175. * Allow this guy to activate.
  176. */
  177. GETPTI(pwndT)->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
  178. TAGMSG1(DBGTAG_FOREGROUND, "xxxDragObject set TIF %#p", GETPTI(pwndT));
  179. result = (DWORD)xxxSendMessage(pwndT, WM_DROPOBJECT,
  180. (WPARAM)HW(pwndFrom), (LPARAM)lpds);
  181. ThreadUnlock(&tlpwndT);
  182. }
  183. }
  184. xxxWindowEvent(EVENT_SYSTEM_DRAGDROPEND, pwndFrom, OBJID_WINDOW, INDEXID_CONTAINER, 0);
  185. ThreadUnlockAndFreePool(pti, &tlPool);
  186. return result;
  187. }
  188. /***************************************************************************\
  189. * QueryDropObject
  190. *
  191. * Determines where in the window heirarchy the "drop" takes place, and
  192. * sends a message to the deepest child window first. If that window does
  193. * not respond, we go up the heirarchy (recursively, for the moment) until
  194. * we either get a window that does respond or the parent doesn't respond.
  195. *
  196. * History:
  197. * 08-06-91 darrinm Ported from Win 3.1 sources.
  198. \***************************************************************************/
  199. PCURSOR xxxQueryDropObject(
  200. PWND pwnd,
  201. LPDROPSTRUCT lpds)
  202. {
  203. PWND pwndT;
  204. PCURSOR pcurT = NULL;
  205. POINT pt;
  206. BOOL fNC;
  207. TL tlpwndT;
  208. CheckLock(pwnd);
  209. /*
  210. * pt is in screen coordinates
  211. */
  212. pt = lpds->ptDrop;
  213. /*
  214. * reject points outside this window or if the window is disabled
  215. */
  216. if (!PtInRect(&pwnd->rcWindow, pt) || TestWF(pwnd, WFDISABLED))
  217. return NULL;
  218. /*
  219. * Check to see if in window region (if it has one)
  220. */
  221. if (pwnd->hrgnClip != NULL) {
  222. if (!GrePtInRegion(pwnd->hrgnClip, pt.x, pt.y))
  223. return NULL;
  224. }
  225. /*
  226. * are we dropping in the nonclient area of the window or on an iconic
  227. * window?
  228. */
  229. if (fNC = (TestWF(pwnd, WFMINIMIZED) || !PtInRect(&pwnd->rcClient, pt))) {
  230. goto SendQueryDrop;
  231. }
  232. /*
  233. * dropping in client area
  234. */
  235. _ScreenToClient(pwnd, &pt);
  236. pwndT = _ChildWindowFromPointEx(pwnd, pt, CWP_SKIPDISABLED | CWP_SKIPINVISIBLE);
  237. _ClientToScreen(pwnd, &pt);
  238. pcurT = NULL;
  239. if (pwndT && pwndT != pwnd) {
  240. ThreadLock(pwndT, &tlpwndT);
  241. pcurT = xxxQueryDropObject(pwndT, lpds);
  242. ThreadUnlock(&tlpwndT);
  243. }
  244. if (pcurT == NULL) {
  245. /*
  246. * there are no children who are in the right place or who want
  247. * drops... convert the point into client coordinates of the
  248. * current window. Because of the recursion, this is already
  249. * done if a child window grabbed the drop.
  250. */
  251. SendQueryDrop:
  252. _ScreenToClient(pwnd, &lpds->ptDrop);
  253. lpds->hwndSink = HWq(pwnd);
  254. /*
  255. * To avoid hanging dropper (sender) app we do a SendMessageTimeout to
  256. * the droppee (receiver)
  257. */
  258. if ((PCURSOR)xxxSendMessageTimeout(pwnd, WM_QUERYDROPOBJECT, fNC,
  259. (LPARAM)lpds, SMTO_ABORTIFHUNG, 3*1000, (PLONG_PTR)&pcurT) == FALSE)
  260. pcurT = (PCURSOR)FALSE;
  261. if (pcurT != (PCURSOR)FALSE && pcurT != (PCURSOR)TRUE)
  262. pcurT = HMValidateHandle((HCURSOR)pcurT, TYPE_CURSOR);
  263. /*
  264. * restore drop point to screen coordinates if this window won't
  265. * take drops
  266. */
  267. if (pcurT == NULL)
  268. lpds->ptDrop = pt;
  269. }
  270. return pcurT;
  271. }
  272. /***************************************************************************\
  273. * xxxDragDetect (API)
  274. *
  275. *
  276. *
  277. * History:
  278. * 08-06-91 darrinm Ported from Win 3.1 sources.
  279. \***************************************************************************/
  280. BOOL xxxDragDetect(
  281. PWND pwnd,
  282. POINT pt)
  283. {
  284. return xxxIsDragging(pwnd, pt, WM_LBUTTONUP);
  285. }
  286. /***************************************************************************\
  287. * xxxIsDragging
  288. *
  289. *
  290. *
  291. * History:
  292. * 05-17-94 johnl Ported from Chicago sources
  293. \***************************************************************************/
  294. BOOL xxxIsDragging(PWND pwnd, POINT ptScreen, UINT uMsg)
  295. {
  296. RECT rc;
  297. MSG msg;
  298. BOOL fDragging;
  299. BOOL fCheck;
  300. TL tlpwndDragging;
  301. PTHREADINFO pti = PtiCurrent();
  302. /*
  303. * Check synchronous mouse state, and punt if the mouse isn't down
  304. * according to the queue.
  305. */
  306. if (!(_GetKeyState((uMsg == WM_LBUTTONUP ? VK_LBUTTON : VK_RBUTTON)) & 0x8000))
  307. return FALSE;
  308. xxxSetCapture(pwnd);
  309. *(LPPOINT)&rc.left = ptScreen;
  310. *(LPPOINT)&rc.right = ptScreen;
  311. InflateRect(&rc, SYSMET(CXDRAG), SYSMET(CYDRAG));
  312. fDragging = FALSE;
  313. fCheck = TRUE;
  314. ThreadLockWithPti(pti, pwnd, &tlpwndDragging);
  315. while (fCheck) {
  316. while ( !(
  317. xxxPeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST,PM_REMOVE) ||
  318. xxxPeekMessage(&msg, NULL, WM_QUEUESYNC, WM_QUEUESYNC,PM_REMOVE) ||
  319. xxxPeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST,PM_REMOVE)
  320. )
  321. && (pti->pq->spwndCapture == pwnd)) {
  322. /*
  323. * If there is no input for half a second (500ms) consider that
  324. * we are dragging. If we don't specify a timeout value, the
  325. * thread may sleep here forever and wouldn't repaint, etc.
  326. */
  327. if (!xxxSleepThread(QS_MOUSE | QS_KEY, 500, TRUE)) {
  328. fDragging = TRUE;
  329. goto Cleanup;
  330. }
  331. }
  332. /*
  333. * Cancel if the button was released or we no longer have the capture.
  334. */
  335. if ( pti->pq->spwndCapture != pwnd || msg.message == uMsg) {
  336. fCheck = FALSE;
  337. } else {
  338. switch (msg.message) {
  339. case WM_MOUSEMOVE:
  340. if (!PtInRect(&rc, msg.pt)) {
  341. fDragging = TRUE;
  342. fCheck = FALSE;
  343. }
  344. break;
  345. case WM_QUEUESYNC:
  346. /*
  347. * CBT Hook needs to know
  348. */
  349. xxxCallHook(HCBT_QS, 0, 0, WH_CBT);
  350. break;
  351. case WM_KEYDOWN:
  352. /*
  353. * <Esc> cancels drag detection
  354. */
  355. if (msg.wParam == VK_ESCAPE)
  356. fCheck = FALSE;
  357. break;
  358. }
  359. }
  360. }
  361. Cleanup:
  362. if (pti->pq->spwndCapture == pwnd)
  363. xxxReleaseCapture();
  364. ThreadUnlock(&tlpwndDragging);
  365. return fDragging ;
  366. }