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.

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