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.

477 lines
11 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 "precomp.h"
  15. #include "cdopti.h"
  16. #include "cdoptimp.h"
  17. #define LONG2POINT(l, pt) ((pt).x = (SHORT)LOWORD(l), \
  18. (pt).y = (SHORT)HIWORD(l))
  19. #define DF_ACTUALLYDRAG 0x0001
  20. #define DF_DEFERRED 0x0002
  21. #define INITLINESPERSECOND 36
  22. #define VERTCHANGENUMLINES 25
  23. #define TIMERID 238
  24. #define TIMERLEN 250
  25. #define TIMERLEN2 50
  26. #define DX_INSERT 16
  27. #define DY_INSERT 16
  28. typedef struct DRAGPROP
  29. {
  30. WNDPROC lpfnDefProc;
  31. HWND hwndDrag;
  32. UINT uFlags;
  33. DWORD dwState;
  34. CCDOpt * pCDOpt;
  35. } DRAGPROP, *PDRAGPROP;
  36. TCHAR szDragListMsgString[] = TEXT(SJE_DRAGLISTMSGSTRING);
  37. TCHAR szDragProp[] = TEXT("DragMultiProp");
  38. STDMETHODIMP_(UINT) CCDOpt::InitDragMultiList(void)
  39. {
  40. if (!m_uDragListMsg) {
  41. m_uDragListMsg = RegisterWindowMessage(szDragListMsgString);
  42. if (!m_uDragListMsg) {
  43. return 0;
  44. }
  45. }
  46. return m_uDragListMsg;
  47. }
  48. STDMETHODIMP_(LRESULT) CCDOpt::DragList(HWND hLB, UINT uMsg, WPARAM wParam, LPARAM lParam)
  49. {
  50. PDRAGPROP pDragProp;
  51. DRAGMULTILISTINFO sNotify;
  52. WNDPROC lpfnDefProc;
  53. BOOL bDragging;
  54. pDragProp = (PDRAGPROP)GetProp(hLB, szDragProp);
  55. bDragging = pDragProp->hwndDrag == hLB;
  56. // Save this in case anything happens to pDragProp before we return.
  57. lpfnDefProc = pDragProp->lpfnDefProc;
  58. switch (uMsg) {
  59. case WM_DESTROY:
  60. if (bDragging)
  61. SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L); // cancel drag
  62. // Restore the window proc just in case.
  63. SubclassWindow( hLB, lpfnDefProc );
  64. if (pDragProp) {
  65. LocalFree((HLOCAL)pDragProp);
  66. RemoveProp(hLB, szDragProp);
  67. }
  68. break;
  69. case WM_LBUTTONDOWN:
  70. {
  71. POINT pt;
  72. int nItem;
  73. if (bDragging) // nested button-down
  74. SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L); // cancel drag
  75. SetFocus(hLB);
  76. LONG2POINT(lParam, pt);
  77. ClientToScreen(hLB, &pt);
  78. nItem = LBMultiItemFromPt(hLB, pt, FALSE);
  79. if ( nItem >= 0 ) {
  80. //
  81. // We can only allow dragging if the item is selected.
  82. // If the item is not selected - pass the message on.
  83. //
  84. if ( ListBox_GetSel( hLB, nItem ) <= 0 ) {
  85. return CallWindowProc( lpfnDefProc, hLB, uMsg,
  86. wParam, lParam );
  87. }
  88. pDragProp->dwState = (wParam & MK_CONTROL) ? DG_COPY : DG_MOVE;
  89. sNotify.uNotification = DG_BEGINDRAG;
  90. goto QueryParent;
  91. }
  92. else {
  93. goto FakeDrag;
  94. }
  95. }
  96. case WM_TIMER:
  97. if (wParam != TIMERID) {
  98. break;
  99. }
  100. {
  101. POINT CursorPos;
  102. GetCursorPos( &CursorPos );
  103. ScreenToClient( hLB, &CursorPos );
  104. lParam = MAKELPARAM((WORD)CursorPos.x, (WORD)CursorPos.y);
  105. }
  106. // Fall through
  107. case WM_MOUSEMOVE:
  108. if (bDragging) {
  109. HWND hwndParent;
  110. LRESULT lRet;
  111. // We may be just simulating a drag, but not actually doing
  112. // anything.
  113. if (!(pDragProp->uFlags&DF_ACTUALLYDRAG)) {
  114. return(0L);
  115. }
  116. if ( pDragProp->uFlags & DF_DEFERRED ) {
  117. pDragProp->uFlags &= ~DF_DEFERRED;
  118. KillTimer(hLB, TIMERID);
  119. SetTimer(hLB, TIMERID, TIMERLEN2, NULL);
  120. }
  121. sNotify.uNotification = DG_DRAGGING;
  122. QueryParent:
  123. hwndParent = GetParent( hLB );
  124. sNotify.hWnd = hLB;
  125. sNotify.dwState = pDragProp->dwState;
  126. LONG2POINT( lParam, sNotify.ptCursor );
  127. ClientToScreen( hLB, &sNotify.ptCursor );
  128. lRet = SendMessage( hwndParent, m_uDragListMsg, GetDlgCtrlID(hLB),
  129. (LPARAM)(LPDRAGMULTILISTINFO)&sNotify );
  130. if ( uMsg == WM_LBUTTONDOWN ) {
  131. // Some things may not be draggable
  132. if (lRet) {
  133. SetTimer(hLB, TIMERID, TIMERLEN, NULL);
  134. pDragProp->uFlags = DF_DEFERRED | DF_ACTUALLYDRAG;
  135. }
  136. else {
  137. FakeDrag:
  138. pDragProp->uFlags = 0;
  139. }
  140. // Set capture and change mouse cursor
  141. pDragProp->hwndDrag = hLB;
  142. SetCapture( hLB );
  143. }
  144. // Don't call the def proc, since it may try to change the
  145. // selection or set timers or things like that.
  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 an
  152. // acceptable place where mouse is now to decide drop or not
  153. if (bDragging) {
  154. HWND hwndParent;
  155. pDragProp->hwndDrag = NULL;
  156. KillTimer(hLB, TIMERID);
  157. ReleaseCapture();
  158. SetCursor(LoadCursor(NULL, IDC_ARROW));
  159. hwndParent = GetParent(hLB);
  160. sNotify.uNotification = (uMsg == WM_LBUTTONUP)
  161. ? DG_DROPPED : DG_CANCELDRAG;
  162. sNotify.hWnd = hLB;
  163. sNotify.dwState = pDragProp->dwState;
  164. LONG2POINT( lParam, sNotify.ptCursor );
  165. ClientToScreen( hLB, &sNotify.ptCursor );
  166. SendMessage( hwndParent, m_uDragListMsg, GetDlgCtrlID(hLB),
  167. (LPARAM)(LPDRAGMULTILISTINFO)&sNotify);
  168. //
  169. // If we didn't actually do any dragging just fake a button
  170. // click at the current location.
  171. //
  172. if ( pDragProp->uFlags & DF_DEFERRED ) {
  173. CallWindowProc(lpfnDefProc, hLB, WM_LBUTTONDOWN, wParam, lParam);
  174. CallWindowProc(lpfnDefProc, hLB, uMsg, wParam, lParam);
  175. }
  176. // We need to make sure to return 0 in case this is from a
  177. // keyboard message.
  178. return 0L;
  179. }
  180. break;
  181. case WM_GETDLGCODE:
  182. if (bDragging)
  183. return (CallWindowProc(lpfnDefProc, hLB, uMsg, wParam, lParam)
  184. | DLGC_WANTMESSAGE);
  185. break;
  186. case WM_KEYDOWN:
  187. if (wParam == VK_ESCAPE) {
  188. SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L);
  189. }
  190. case WM_CHAR:
  191. case WM_KEYUP:
  192. // We don't want the listbox processing this if we are dragging.
  193. if (bDragging)
  194. return 0L;
  195. break;
  196. default:
  197. break;
  198. }
  199. return CallWindowProc(lpfnDefProc, hLB, uMsg, wParam, lParam);
  200. }
  201. LRESULT CALLBACK CCDOpt::DragListProc(HWND hLB, UINT uMsg, WPARAM wParam, LPARAM lParam)
  202. {
  203. PDRAGPROP pDragProp = (PDRAGPROP)GetProp(hLB, szDragProp);
  204. return(pDragProp->pCDOpt->DragList(hLB, uMsg, wParam, lParam));
  205. }
  206. STDMETHODIMP_(BOOL) CCDOpt::MakeMultiDragList(HWND hLB)
  207. {
  208. PDRAGPROP pDragProp;
  209. if (!m_uDragListMsg) {
  210. return FALSE;
  211. }
  212. //
  213. // Check that we have not already subclassed this window.
  214. //
  215. if (GetProp(hLB, szDragProp)) {
  216. return TRUE;
  217. }
  218. pDragProp = (PDRAGPROP)LocalAlloc(LPTR, sizeof(DRAGPROP));
  219. if (pDragProp == NULL ) {
  220. return FALSE;
  221. }
  222. pDragProp->pCDOpt = this;
  223. SetProp(hLB, szDragProp, (HANDLE)pDragProp);
  224. pDragProp->lpfnDefProc = SubclassWindow( hLB, DragListProc );
  225. return TRUE;
  226. }
  227. STDMETHODIMP_(int) CCDOpt::LBMultiItemFromPt(HWND hLB, POINT pt, BOOL bAutoScroll)
  228. {
  229. static LONG dwLastScroll = 0;
  230. RECT rc;
  231. DWORD dwNow;
  232. int nItem;
  233. WORD wScrollDelay, wActualDelay;
  234. ScreenToClient(hLB, &pt);
  235. GetClientRect(hLB, &rc);
  236. nItem = ListBox_GetTopIndex( hLB );
  237. //
  238. // Is the point in the LB client area?
  239. //
  240. if ( PtInRect(&rc, pt) ) {
  241. //
  242. // Check each visible item in turn.
  243. //
  244. for ( ; ; ++nItem) {
  245. if ( LB_ERR == ListBox_GetItemRect( hLB, nItem, &rc) ) {
  246. break;
  247. }
  248. if ( PtInRect(&rc, pt) ) {
  249. return nItem;
  250. }
  251. }
  252. }
  253. else {
  254. //
  255. // If we want autoscroll and the point is directly above or below the
  256. // LB, determine the direction and if it is time to scroll yet.
  257. //
  258. if ( bAutoScroll && (UINT)pt.x < (UINT)rc.right ) {
  259. if (pt.y <= 0) {
  260. --nItem;
  261. }
  262. else {
  263. ++nItem;
  264. pt.y = rc.bottom - pt.y;
  265. }
  266. wScrollDelay = (WORD)(1000 / (INITLINESPERSECOND - pt.y/VERTCHANGENUMLINES));
  267. dwNow = GetTickCount();
  268. wActualDelay = (WORD)(dwNow - dwLastScroll);
  269. if (wActualDelay > wScrollDelay) {
  270. //
  271. // This will the actual number of scrolls per second to be
  272. // much closer to the required number.
  273. //
  274. if (wActualDelay > wScrollDelay * 2)
  275. dwLastScroll = dwNow;
  276. else
  277. dwLastScroll += wScrollDelay;
  278. ListBox_SetTopIndex( hLB, nItem );
  279. }
  280. }
  281. }
  282. return -1;
  283. }
  284. STDMETHODIMP_(void) CCDOpt::DrawMultiInsert(HWND hwndParent, HWND hLB, int nItem)
  285. {
  286. static POINT ptLastInsert;
  287. static INT nLastInsert = -1;
  288. RECT rc;
  289. //
  290. // Erase the old mark if necessary
  291. //
  292. if ( nLastInsert >= 0 && nItem != nLastInsert ) {
  293. rc.left = ptLastInsert.x;
  294. rc.top = ptLastInsert.y;
  295. rc.right = rc.left + DX_INSERT;
  296. rc.bottom = rc.top + DY_INSERT;
  297. //
  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. //
  305. // Draw a new mark if necessary
  306. //
  307. if ( nItem != nLastInsert && nItem >= 0 ) {
  308. static HICON hInsert = NULL;
  309. if ( !hInsert ) {
  310. hInsert = LoadIcon(m_hInst, MAKEINTRESOURCE(IDR_INSERT));
  311. }
  312. if ( hInsert ) {
  313. HDC hDC;
  314. int iItemHeight;
  315. GetWindowRect( hLB, &rc );
  316. ScreenToClient( hLB, (LPPOINT)&rc );
  317. ptLastInsert.x = rc.left - DX_INSERT;
  318. iItemHeight = ListBox_GetItemHeight( hLB, nItem );
  319. nLastInsert = nItem;
  320. nItem -= ListBox_GetTopIndex( hLB );
  321. ptLastInsert.y = (nItem * iItemHeight) - DY_INSERT / 2;
  322. ClientToScreen(hLB, &ptLastInsert);
  323. ScreenToClient(hwndParent, &ptLastInsert);
  324. hDC = GetDC(hwndParent);
  325. DrawIcon(hDC, ptLastInsert.x, ptLastInsert.y, hInsert);
  326. ReleaseDC(hwndParent, hDC);
  327. }
  328. }
  329. }