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.

408 lines
9.0 KiB

  1. #include "ctlspriv.h"
  2. #define DF_ACTUALLYDRAG 0x0001
  3. #define DF_DEFERRED 0x0002
  4. #define INITLINESPERSECOND 6
  5. #define VERTCHANGENUMLINES 25
  6. #define TIMERID 238
  7. #define TIMERLEN 50
  8. #define DX_INSERT 16
  9. #define DY_INSERT 16
  10. typedef struct {
  11. HWND hwndDrag;
  12. UINT uFlags;
  13. } DRAGPROP, *PDRAGPROP;
  14. UINT uDragListMsg = 0;
  15. #ifndef WINNT
  16. #pragma data_seg(DATASEG_READONLY)
  17. #endif
  18. const TCHAR szDragListMsgString[] = DRAGLISTMSGSTRING;
  19. #ifndef WINNT
  20. #pragma data_seg()
  21. #endif
  22. BOOL PtInLBItem(HWND hLB, int nItem, POINT pt, int xInflate, int yInflate)
  23. {
  24. RECT rc;
  25. if (nItem < 0)
  26. nItem = (int)SendMessage(hLB, LB_GETCURSEL, 0, 0L);
  27. if (SendMessage(hLB, LB_GETITEMRECT, nItem, (LPARAM)(LPRECT)&rc) == LB_ERR)
  28. return(FALSE);
  29. InflateRect(&rc, xInflate, yInflate);
  30. return(PtInRect(&rc, pt));
  31. }
  32. /*
  33. * DragListSubclassProc
  34. * --------------------
  35. *
  36. * Window procedure for subclassed list boxes
  37. */
  38. LRESULT CALLBACK DragListSubclassProc(HWND hLB, UINT uMsg, WPARAM wParam,
  39. LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  40. {
  41. PDRAGPROP pDragProp;
  42. DRAGLISTINFO sNotify;
  43. BOOL bDragging;
  44. POINT pt;
  45. pDragProp = (PDRAGPROP)dwRefData;
  46. bDragging = pDragProp->hwndDrag == hLB;
  47. switch (uMsg)
  48. {
  49. case WM_NCDESTROY:
  50. if (bDragging)
  51. SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L); /* cancel drag */
  52. RemoveWindowSubclass(hLB, DragListSubclassProc, 0);
  53. if (pDragProp)
  54. LocalFree((HLOCAL)pDragProp);
  55. break;
  56. case WM_LBUTTONDOWN:
  57. {
  58. int nItem;
  59. if (bDragging) /* nested button-down */
  60. SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L); /* cancel drag */
  61. SetFocus(hLB);
  62. pt.x = GET_X_LPARAM(lParam);
  63. pt.y = GET_Y_LPARAM(lParam);
  64. ClientToScreen(hLB, &pt);
  65. nItem = LBItemFromPt(hLB, pt, FALSE);
  66. if (nItem >= 0)
  67. {
  68. SendMessage(hLB, LB_SETCURSEL, nItem, 0L);
  69. if (GetWindowLong(hLB, GWL_STYLE) & LBS_NOTIFY)
  70. SendMessage(GetParent(hLB), WM_COMMAND,
  71. GET_WM_COMMAND_MPS(GetDlgCtrlID(hLB), hLB, LBN_SELCHANGE));
  72. sNotify.uNotification = DL_BEGINDRAG;
  73. goto QueryParent;
  74. }
  75. else
  76. goto FakeDrag;
  77. }
  78. case WM_TIMER:
  79. if (wParam != TIMERID)
  80. break;
  81. lParam = GetMessagePosClient(hLB, &pt);
  82. // fall through
  83. case WM_MOUSEMOVE:
  84. if (bDragging)
  85. {
  86. HWND hwndParent;
  87. LRESULT lResult;
  88. /* We may be just simulating a drag, but not actually doing
  89. * anything.
  90. */
  91. if (!(pDragProp->uFlags&DF_ACTUALLYDRAG))
  92. return(0L);
  93. /* We don't want to do any dragging until the user has dragged
  94. * outside of the current selection.
  95. */
  96. if (pDragProp->uFlags & DF_DEFERRED)
  97. {
  98. pt.x = GET_X_LPARAM(lParam);
  99. pt.y = GET_Y_LPARAM(lParam);
  100. if (PtInLBItem(hLB, -1, pt, 0, 4))
  101. return 0;
  102. pDragProp->uFlags &= ~DF_DEFERRED;
  103. }
  104. sNotify.uNotification = DL_DRAGGING;
  105. QueryParent:
  106. hwndParent = GetParent(hLB);
  107. sNotify.hWnd = hLB;
  108. sNotify.ptCursor.x = GET_X_LPARAM(lParam);
  109. sNotify.ptCursor.y = GET_Y_LPARAM(lParam);
  110. ClientToScreen(hLB, &sNotify.ptCursor);
  111. lResult = SendMessage(hwndParent, uDragListMsg, GetDlgCtrlID(hLB),
  112. (LPARAM)(LPDRAGLISTINFO)&sNotify);
  113. if (uMsg == WM_LBUTTONDOWN)
  114. {
  115. /* Some things may not be draggable
  116. */
  117. if (lResult)
  118. {
  119. SetTimer(hLB, TIMERID, TIMERLEN, NULL);
  120. pDragProp->uFlags = DF_DEFERRED | DF_ACTUALLYDRAG;
  121. }
  122. else
  123. {
  124. FakeDrag:
  125. pDragProp->uFlags = 0;
  126. }
  127. /* Set capture and change mouse cursor
  128. */
  129. pDragProp->hwndDrag = hLB;
  130. SetCapture(hLB);
  131. }
  132. else
  133. {
  134. switch (lResult)
  135. {
  136. case DL_STOPCURSOR:
  137. SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_NO)));
  138. break;
  139. case DL_COPYCURSOR:
  140. SetCursor(LoadCursor(HINST_THISDLL, MAKEINTRESOURCE(IDC_COPY)));
  141. break;
  142. case DL_MOVECURSOR:
  143. SetCursor(LoadCursor(HINST_THISDLL, MAKEINTRESOURCE(IDC_MOVE)));
  144. break;
  145. default:
  146. break;
  147. }
  148. }
  149. /* Don't call the def proc, since it may try to change the
  150. * selection or set timers or things like that.
  151. */
  152. return(0L);
  153. }
  154. break;
  155. case WM_RBUTTONDOWN:
  156. case WM_LBUTTONUP:
  157. /* if we are capturing mouse - release it and check for acceptable place
  158. * where mouse is now to decide drop or not
  159. */
  160. if (bDragging)
  161. {
  162. HWND hwndParent;
  163. pDragProp->hwndDrag = NULL;
  164. KillTimer(hLB, TIMERID);
  165. ReleaseCapture();
  166. SetCursor(LoadCursor(NULL, IDC_ARROW));
  167. hwndParent = GetParent(hLB);
  168. sNotify.uNotification = uMsg==WM_LBUTTONUP ? DL_DROPPED : DL_CANCELDRAG;
  169. sNotify.hWnd = hLB;
  170. sNotify.ptCursor.x = GET_X_LPARAM(lParam);
  171. sNotify.ptCursor.y = GET_Y_LPARAM(lParam);
  172. ClientToScreen(hLB, &sNotify.ptCursor);
  173. SendMessage(hwndParent, uDragListMsg, GetDlgCtrlID(hLB),
  174. (LPARAM)(LPDRAGLISTINFO)&sNotify);
  175. /* We need to make sure to return 0 in case this is from a
  176. * keyboard message.
  177. */
  178. return(0L);
  179. }
  180. break;
  181. case WM_GETDLGCODE:
  182. if (bDragging)
  183. {
  184. return (DefSubclassProc(hLB, uMsg, wParam, lParam) |
  185. DLGC_WANTMESSAGE);
  186. }
  187. break;
  188. case WM_KEYDOWN:
  189. if (wParam == VK_ESCAPE)
  190. {
  191. SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L);
  192. }
  193. // fall through
  194. case WM_CHAR:
  195. case WM_KEYUP:
  196. /* We don't want the listbox processing this if we are dragging.
  197. */
  198. if (bDragging)
  199. return(0L);
  200. break;
  201. default:
  202. break;
  203. }
  204. return(DefSubclassProc(hLB, uMsg, wParam, lParam));
  205. }
  206. BOOL WINAPI MakeDragList(HWND hLB)
  207. {
  208. PDRAGPROP pDragProp;
  209. if (!uDragListMsg)
  210. uDragListMsg = RegisterWindowMessage(szDragListMsgString);
  211. /* Check that we have not already subclassed this window.
  212. */
  213. if (GetWindowSubclass(hLB, DragListSubclassProc, 0, NULL))
  214. return(TRUE);
  215. pDragProp = (PDRAGPROP)LocalAlloc(LPTR, sizeof(DRAGPROP));
  216. if (!pDragProp)
  217. return(FALSE);
  218. if (!SetWindowSubclass(hLB, DragListSubclassProc, 0, (DWORD_PTR)pDragProp))
  219. {
  220. LocalFree((HLOCAL)pDragProp);
  221. return(FALSE);
  222. }
  223. return(TRUE);
  224. }
  225. int WINAPI LBItemFromPt(HWND hLB, POINT pt, BOOL bAutoScroll)
  226. {
  227. static LONG dwLastScroll = 0;
  228. RECT rc;
  229. DWORD dwNow;
  230. int nItem;
  231. WORD wScrollDelay, wActualDelay;
  232. ScreenToClient(hLB, &pt);
  233. GetClientRect(hLB, &rc);
  234. nItem = (int)SendMessage(hLB, LB_GETTOPINDEX, 0, 0L);
  235. /* Is the point in the LB client area?
  236. */
  237. if (PtInRect(&rc, pt))
  238. {
  239. /* Check each visible item in turn.
  240. */
  241. for ( ; ; ++nItem)
  242. {
  243. if (SendMessage(hLB, LB_GETITEMRECT, nItem, (LPARAM)(LPRECT)&rc)
  244. == LB_ERR)
  245. break;
  246. if (PtInRect(&rc, pt))
  247. return(nItem);
  248. }
  249. }
  250. else
  251. {
  252. /* If we want autoscroll and the point is directly above or below the
  253. * LB, determine the direction and if it is time to scroll yet.
  254. */
  255. if (bAutoScroll && (UINT)pt.x<(UINT)rc.right)
  256. {
  257. if (pt.y <= 0)
  258. {
  259. --nItem;
  260. }
  261. else
  262. {
  263. ++nItem;
  264. pt.y = rc.bottom - pt.y;
  265. }
  266. wScrollDelay = (WORD)(1000 /
  267. (INITLINESPERSECOND - pt.y/VERTCHANGENUMLINES));
  268. dwNow = GetTickCount();
  269. wActualDelay = (WORD)(dwNow - dwLastScroll);
  270. if (wActualDelay > wScrollDelay)
  271. {
  272. /* This will the actual number of scrolls per second to be
  273. * much closer to the required number.
  274. */
  275. if (wActualDelay > wScrollDelay * 2)
  276. dwLastScroll = dwNow;
  277. else
  278. dwLastScroll += wScrollDelay;
  279. SendMessage(hLB, LB_SETTOPINDEX, nItem, 0L);
  280. }
  281. }
  282. }
  283. return(-1);
  284. }
  285. void WINAPI DrawInsert(HWND hwndParent, HWND hLB, int nItem)
  286. {
  287. static POINT ptLastInsert;
  288. static int nLastInsert = -1;
  289. RECT rc;
  290. /* Erase the old mark if necessary
  291. */
  292. if (nLastInsert>=0 && nItem!=nLastInsert)
  293. {
  294. rc.left = ptLastInsert.x;
  295. rc.top = ptLastInsert.y;
  296. rc.right = rc.left + DX_INSERT;
  297. rc.bottom = rc.top + DY_INSERT;
  298. /* Need to update immediately in case the insert rects overlap.
  299. */
  300. InvalidateRect(hwndParent, &rc, TRUE);
  301. UpdateWindow(hwndParent);
  302. nLastInsert = -1;
  303. }
  304. /* Draw a new mark if necessary
  305. */
  306. if (nItem!=nLastInsert && nItem>=0)
  307. {
  308. HICON hInsert = NULL;
  309. if (!hInsert)
  310. hInsert = LoadIcon(HINST_THISDLL, MAKEINTRESOURCE(IDI_INSERT));
  311. if (hInsert)
  312. {
  313. HDC hDC;
  314. GetWindowRect(hLB, &rc);
  315. ScreenToClient(hLB, (LPPOINT)&rc);
  316. ptLastInsert.x = rc.left - DX_INSERT;
  317. SendMessage(hLB, LB_GETITEMRECT, nItem, (LPARAM)(LPRECT)&rc);
  318. ptLastInsert.y = rc.top - DY_INSERT/2;
  319. nLastInsert = nItem;
  320. ClientToScreen(hLB, &ptLastInsert);
  321. ScreenToClient(hwndParent, &ptLastInsert);
  322. hDC = GetDC(hwndParent);
  323. DrawIcon(hDC, ptLastInsert.x, ptLastInsert.y, hInsert);
  324. ReleaseDC(hwndParent, hDC);
  325. }
  326. }
  327. }