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.

534 lines
12 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: DragDrop.c
  3. *
  4. * An attempt to implement dragging and dropping between Multi-selection
  5. * listboxes.
  6. *
  7. * Created: dd-mm-93
  8. * Author: Stephen Estrop [StephenE]
  9. *
  10. * Copyright (c) 1993 Microsoft Corporation
  11. \**************************************************************************/
  12. #define NOOLE
  13. #define NODRAGLIST
  14. #include <windows.h>
  15. #include <windowsx.h>
  16. #include "resource.h"
  17. #include "dragdrop.h"
  18. #define LONG2POINT(l, pt) ((pt).x = (SHORT)LOWORD(l), \
  19. (pt).y = (SHORT)HIWORD(l))
  20. #define DF_ACTUALLYDRAG 0x0001
  21. #define DF_DEFERRED 0x0002
  22. #define INITLINESPERSECOND 36
  23. #define VERTCHANGENUMLINES 25
  24. #define TIMERID 238
  25. #define TIMERLEN 250
  26. #define TIMERLEN2 50
  27. #define DX_INSERT 16
  28. #define DY_INSERT 16
  29. typedef struct {
  30. WNDPROC lpfnDefProc;
  31. HWND hwndDrag;
  32. UINT uFlags;
  33. DWORD dwState;
  34. } DRAGPROP, *PDRAGPROP;
  35. UINT uDragListMsg = 0L;
  36. TCHAR szDragListMsgString[] = TEXT(SJE_DRAGLISTMSGSTRING);
  37. TCHAR szDragProp[] = TEXT("DragMultiProp");
  38. extern HINSTANCE g_hInst;
  39. /******************************Public*Routine******************************\
  40. * InitDragMultiList
  41. *
  42. *
  43. *
  44. * History:
  45. * dd-mm-94 - StephenE - Created
  46. *
  47. \**************************************************************************/
  48. UINT WINAPI
  49. InitDragMultiList(
  50. void
  51. )
  52. {
  53. if (!uDragListMsg) {
  54. uDragListMsg = RegisterWindowMessage(szDragListMsgString);
  55. if (!uDragListMsg) {
  56. return 0;
  57. }
  58. }
  59. return uDragListMsg;
  60. }
  61. /******************************Public*Routine******************************\
  62. * DragListSubclassProc
  63. *
  64. *
  65. *
  66. * History:
  67. * dd-mm-93 - StephenE - Created
  68. *
  69. \**************************************************************************/
  70. LRESULT CALLBACK
  71. DragListSubclassProc(
  72. HWND hLB,
  73. UINT uMsg,
  74. WPARAM wParam,
  75. LPARAM lParam
  76. )
  77. {
  78. PDRAGPROP pDragProp;
  79. DRAGMULTILISTINFO sNotify;
  80. WNDPROC lpfnDefProc;
  81. BOOL bDragging;
  82. pDragProp = (PDRAGPROP)GetProp(hLB, szDragProp);
  83. bDragging = pDragProp->hwndDrag == hLB;
  84. // Save this in case anything happens to pDragProp before we return.
  85. lpfnDefProc = pDragProp->lpfnDefProc;
  86. switch (uMsg) {
  87. case WM_DESTROY:
  88. if (bDragging)
  89. SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L); // cancel drag
  90. // Restore the window proc just in case.
  91. SubclassWindow( hLB, lpfnDefProc );
  92. if (pDragProp) {
  93. LocalFree((HLOCAL)pDragProp);
  94. RemoveProp(hLB, szDragProp);
  95. }
  96. break;
  97. case WM_LBUTTONDOWN:
  98. {
  99. POINT pt;
  100. int nItem;
  101. if (bDragging) // nested button-down
  102. SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L); // cancel drag
  103. SetFocus(hLB);
  104. LONG2POINT(lParam, pt);
  105. ClientToScreen(hLB, &pt);
  106. nItem = LBMultiItemFromPt(hLB, pt, FALSE);
  107. if ( nItem >= 0 ) {
  108. //
  109. // We can only allow dragging if the item is selected.
  110. // If the item is not selected - pass the message on.
  111. //
  112. if ( ListBox_GetSel( hLB, nItem ) <= 0 ) {
  113. return CallWindowProc( lpfnDefProc, hLB, uMsg,
  114. wParam, lParam );
  115. }
  116. pDragProp->dwState = (wParam & MK_CONTROL) ? DL_COPY : DL_MOVE;
  117. sNotify.uNotification = DL_BEGINDRAG;
  118. goto QueryParent;
  119. }
  120. else {
  121. goto FakeDrag;
  122. }
  123. }
  124. case WM_TIMER:
  125. if (wParam != TIMERID) {
  126. break;
  127. }
  128. {
  129. POINT CursorPos;
  130. GetCursorPos( &CursorPos );
  131. ScreenToClient( hLB, &CursorPos );
  132. lParam = MAKELPARAM((WORD)CursorPos.x, (WORD)CursorPos.y);
  133. }
  134. // Fall through
  135. case WM_MOUSEMOVE:
  136. if (bDragging) {
  137. HWND hwndParent;
  138. DWORD dwRet;
  139. // We may be just simulating a drag, but not actually doing
  140. // anything.
  141. if (!(pDragProp->uFlags&DF_ACTUALLYDRAG)) {
  142. return(0L);
  143. }
  144. if ( pDragProp->uFlags & DF_DEFERRED ) {
  145. pDragProp->uFlags &= ~DF_DEFERRED;
  146. KillTimer(hLB, TIMERID);
  147. SetTimer(hLB, TIMERID, TIMERLEN2, NULL);
  148. }
  149. sNotify.uNotification = DL_DRAGGING;
  150. QueryParent:
  151. hwndParent = GetParent( hLB );
  152. sNotify.hWnd = hLB;
  153. sNotify.dwState = pDragProp->dwState;
  154. LONG2POINT( lParam, sNotify.ptCursor );
  155. ClientToScreen( hLB, &sNotify.ptCursor );
  156. dwRet = SendMessage( hwndParent, uDragListMsg, GetDlgCtrlID(hLB),
  157. (LPARAM)(LPDRAGMULTILISTINFO)&sNotify );
  158. if ( uMsg == WM_LBUTTONDOWN ) {
  159. // Some things may not be draggable
  160. if (dwRet) {
  161. SetTimer(hLB, TIMERID, TIMERLEN, NULL);
  162. pDragProp->uFlags = DF_DEFERRED | DF_ACTUALLYDRAG;
  163. }
  164. else {
  165. FakeDrag:
  166. pDragProp->uFlags = 0;
  167. }
  168. // Set capture and change mouse cursor
  169. pDragProp->hwndDrag = hLB;
  170. SetCapture( hLB );
  171. }
  172. // Don't call the def proc, since it may try to change the
  173. // selection or set timers or things like that.
  174. return 0L;
  175. }
  176. break;
  177. case WM_RBUTTONDOWN:
  178. case WM_LBUTTONUP:
  179. // if we are capturing mouse - release it and check for an
  180. // acceptable place where mouse is now to decide drop or not
  181. if (bDragging) {
  182. HWND hwndParent;
  183. pDragProp->hwndDrag = NULL;
  184. KillTimer(hLB, TIMERID);
  185. ReleaseCapture();
  186. SetCursor(LoadCursor(NULL, IDC_ARROW));
  187. hwndParent = GetParent(hLB);
  188. sNotify.uNotification = (uMsg == WM_LBUTTONUP)
  189. ? DL_DROPPED : DL_CANCELDRAG;
  190. sNotify.hWnd = hLB;
  191. sNotify.dwState = pDragProp->dwState;
  192. LONG2POINT( lParam, sNotify.ptCursor );
  193. ClientToScreen( hLB, &sNotify.ptCursor );
  194. SendMessage( hwndParent, uDragListMsg, GetDlgCtrlID(hLB),
  195. (LPARAM)(LPDRAGMULTILISTINFO)&sNotify);
  196. //
  197. // If we didn't actually do any dragging just fake a button
  198. // click at the current location.
  199. //
  200. if ( pDragProp->uFlags & DF_DEFERRED ) {
  201. CallWindowProc(lpfnDefProc, hLB, WM_LBUTTONDOWN, wParam, lParam);
  202. CallWindowProc(lpfnDefProc, hLB, uMsg, wParam, lParam);
  203. }
  204. // We need to make sure to return 0 in case this is from a
  205. // keyboard message.
  206. return 0L;
  207. }
  208. break;
  209. case WM_GETDLGCODE:
  210. if (bDragging)
  211. return (CallWindowProc(lpfnDefProc, hLB, uMsg, wParam, lParam)
  212. | DLGC_WANTMESSAGE);
  213. break;
  214. case WM_KEYDOWN:
  215. if (wParam == VK_ESCAPE) {
  216. SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L);
  217. }
  218. case WM_CHAR:
  219. case WM_KEYUP:
  220. // We don't want the listbox processing this if we are dragging.
  221. if (bDragging)
  222. return 0L;
  223. break;
  224. default:
  225. break;
  226. }
  227. return CallWindowProc(lpfnDefProc, hLB, uMsg, wParam, lParam);
  228. }
  229. /******************************Public*Routine******************************\
  230. * MakeMultiDragList
  231. *
  232. *
  233. *
  234. * History:
  235. * dd-mm-93 - StephenE - Created
  236. *
  237. \**************************************************************************/
  238. BOOL WINAPI
  239. MakeMultiDragList(
  240. HWND hLB
  241. )
  242. {
  243. PDRAGPROP pDragProp;
  244. if (!uDragListMsg) {
  245. return FALSE;
  246. }
  247. //
  248. // Check that we have not already subclassed this window.
  249. //
  250. if (GetProp(hLB, szDragProp)) {
  251. return TRUE;
  252. }
  253. pDragProp = (PDRAGPROP)LocalAlloc(LPTR, sizeof(DRAGPROP));
  254. if (pDragProp == NULL ) {
  255. return FALSE;
  256. }
  257. SetProp(hLB, szDragProp, (HANDLE)pDragProp);
  258. pDragProp->lpfnDefProc = SubclassWindow( hLB, DragListSubclassProc );
  259. return TRUE;
  260. }
  261. /******************************Public*Routine******************************\
  262. * LBMultiItemFromPt
  263. *
  264. *
  265. *
  266. * History:
  267. * dd-mm-93 - StephenE - Created
  268. *
  269. \**************************************************************************/
  270. int WINAPI
  271. LBMultiItemFromPt(
  272. HWND hLB,
  273. POINT pt,
  274. BOOL bAutoScroll
  275. )
  276. {
  277. static LONG dwLastScroll = 0;
  278. RECT rc;
  279. DWORD dwNow;
  280. int nItem;
  281. WORD wScrollDelay, wActualDelay;
  282. ScreenToClient(hLB, &pt);
  283. GetClientRect(hLB, &rc);
  284. nItem = ListBox_GetTopIndex( hLB );
  285. //
  286. // Is the point in the LB client area?
  287. //
  288. if ( PtInRect(&rc, pt) ) {
  289. //
  290. // Check each visible item in turn.
  291. //
  292. for ( ; ; ++nItem) {
  293. if ( LB_ERR == ListBox_GetItemRect( hLB, nItem, &rc) ) {
  294. break;
  295. }
  296. if ( PtInRect(&rc, pt) ) {
  297. return nItem;
  298. }
  299. }
  300. }
  301. else {
  302. //
  303. // If we want autoscroll and the point is directly above or below the
  304. // LB, determine the direction and if it is time to scroll yet.
  305. //
  306. if ( bAutoScroll && (UINT)pt.x < (UINT)rc.right ) {
  307. if (pt.y <= 0) {
  308. --nItem;
  309. }
  310. else {
  311. ++nItem;
  312. pt.y = rc.bottom - pt.y;
  313. }
  314. wScrollDelay = (WORD)(1000 / (INITLINESPERSECOND - pt.y/VERTCHANGENUMLINES));
  315. dwNow = GetTickCount();
  316. wActualDelay = (WORD)(dwNow - dwLastScroll);
  317. if (wActualDelay > wScrollDelay) {
  318. //
  319. // This will the actual number of scrolls per second to be
  320. // much closer to the required number.
  321. //
  322. if (wActualDelay > wScrollDelay * 2)
  323. dwLastScroll = dwNow;
  324. else
  325. dwLastScroll += wScrollDelay;
  326. ListBox_SetTopIndex( hLB, nItem );
  327. }
  328. }
  329. }
  330. return -1;
  331. }
  332. /******************************Public*Routine******************************\
  333. * DrawInsert
  334. *
  335. *
  336. *
  337. * History:
  338. * dd-mm-93 - StephenE - Created
  339. *
  340. \**************************************************************************/
  341. VOID WINAPI
  342. DrawMultiInsert(
  343. HWND hwndParent,
  344. HWND hLB,
  345. int nItem
  346. )
  347. {
  348. static POINT ptLastInsert;
  349. static INT nLastInsert = -1;
  350. RECT rc;
  351. //
  352. // Erase the old mark if necessary
  353. //
  354. if ( nLastInsert >= 0 && nItem != nLastInsert ) {
  355. rc.left = ptLastInsert.x;
  356. rc.top = ptLastInsert.y;
  357. rc.right = rc.left + DX_INSERT;
  358. rc.bottom = rc.top + DY_INSERT;
  359. //
  360. // Need to update immediately in case the insert rects overlap.
  361. //
  362. InvalidateRect( hwndParent, &rc, TRUE );
  363. UpdateWindow( hwndParent );
  364. nLastInsert = -1;
  365. }
  366. //
  367. // Draw a new mark if necessary
  368. //
  369. if ( nItem != nLastInsert && nItem >= 0 ) {
  370. static HICON hInsert = NULL;
  371. if ( !hInsert ) {
  372. hInsert = LoadIcon( g_hInst, MAKEINTRESOURCE(IDR_INSERT));
  373. }
  374. if ( hInsert ) {
  375. HDC hDC;
  376. int iItemHeight;
  377. GetWindowRect( hLB, &rc );
  378. ScreenToClient( hLB, (LPPOINT)&rc );
  379. ptLastInsert.x = rc.left - DX_INSERT;
  380. iItemHeight = ListBox_GetItemHeight( hLB, nItem );
  381. nLastInsert = nItem;
  382. nItem -= ListBox_GetTopIndex( hLB );
  383. ptLastInsert.y = (nItem * iItemHeight) - DY_INSERT / 2;
  384. ClientToScreen(hLB, &ptLastInsert);
  385. ScreenToClient(hwndParent, &ptLastInsert);
  386. hDC = GetDC(hwndParent);
  387. DrawIcon(hDC, ptLastInsert.x, ptLastInsert.y, hInsert);
  388. ReleaseDC(hwndParent, hDC);
  389. }
  390. }
  391. }