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.

509 lines
10 KiB

  1. //-----------------------------------------------------------------------------
  2. // File: flexscrollbar.cpp
  3. //
  4. // Desc: Implements CFlexScrollBar (derived from CFlexWnd), a scroll bar
  5. // control similar to a Windows scroll bar.
  6. //
  7. // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
  8. //-----------------------------------------------------------------------------
  9. #include "common.hpp"
  10. CFlexScrollBar::CFlexScrollBar() :
  11. m_nMin(0),
  12. m_nMax(0),
  13. m_nPage(25),
  14. m_nPos(25),
  15. m_bVert(TRUE),
  16. m_hWndNotify(NULL),
  17. m_bValid(FALSE),
  18. m_bCapture(FALSE),
  19. m_bDragging(FALSE),
  20. m_code(SB_ENDSCROLL),
  21. m_rgbBk(RGB(0,0,0)),
  22. m_rgbFill(RGB(0,0,255)),
  23. m_rgbLine(RGB(0,255,255))
  24. {
  25. }
  26. CFlexScrollBar::~CFlexScrollBar()
  27. {
  28. }
  29. CFlexScrollBar *CreateFlexScrollBar(FLEXSCROLLBARCREATESTRUCT *pcs)
  30. {
  31. CFlexScrollBar *psb = new CFlexScrollBar;
  32. if (psb && psb->Create(pcs))
  33. return psb;
  34. delete psb;
  35. return NULL;
  36. }
  37. BOOL CFlexScrollBar::Create(FLEXSCROLLBARCREATESTRUCT *pcs)
  38. {
  39. if (this == NULL)
  40. return FALSE;
  41. if (pcs == NULL)
  42. return FALSE;
  43. if (pcs->dwSize != sizeof(FLEXSCROLLBARCREATESTRUCT))
  44. return FALSE;
  45. if (pcs->min > pcs->max)
  46. return FALSE;
  47. int range = pcs->max - pcs->min;
  48. if (pcs->page > range)
  49. return FALSE;
  50. m_bVert = ( pcs->dwFlags & FSBF_VERT ) == FSBF_VERT;
  51. SetValues(pcs->min, pcs->max, pcs->page, pcs->pos);
  52. m_hWndNotify = pcs->hWndNotify ? pcs->hWndNotify : pcs->hWndParent;
  53. if (!CFlexWnd::Create(pcs->hWndParent, pcs->rect, pcs->bVisible))
  54. return FALSE;
  55. Calc();
  56. // TODO: make sure that creation sends no notifications.
  57. // all initial notifications should be sent here.
  58. return TRUE;
  59. }
  60. int CFlexScrollBar::GetLineAdjust()
  61. {
  62. return 1;
  63. }
  64. int CFlexScrollBar::GetPageAdjust()
  65. {
  66. return m_nPage > 1 ? m_nPage - 1 : 1;
  67. }
  68. void CFlexScrollBar::AdjustPos(int adj, BOOL bForceCalc)
  69. {
  70. int old = m_nPos;
  71. m_nPos += adj;
  72. if (m_nPos < m_nMin)
  73. m_nPos = m_nMin;
  74. if (m_nPos > m_nMax - m_nPage)
  75. m_nPos = m_nMax - m_nPage;
  76. if (m_nPos == old && !bForceCalc)
  77. return;
  78. if (Calc())
  79. Invalidate();
  80. }
  81. BOOL CFlexScrollBar::FailCalc(BOOL bOldValid)
  82. {
  83. m_bValid = FALSE;
  84. if (bOldValid)
  85. Invalidate();
  86. return m_bValid;
  87. }
  88. BOOL CFlexScrollBar::Calc()
  89. {
  90. BOOL bOldValid = m_bValid;
  91. #define FAIL return FailCalc(bOldValid)
  92. if (!m_hWnd)
  93. FAIL;
  94. SRECT zero;
  95. m_rectLineUp = zero;
  96. m_rectPageUp = zero;
  97. m_rectTrack = zero;
  98. m_rectThumb = zero;
  99. m_rectPageDown = zero;
  100. m_rectLineDown = zero;
  101. SPOINT size = GetClientSize();
  102. int ord = m_bVert ? 1 : 0;
  103. int nord = m_bVert ? 0 : 1;
  104. int length = size.a[ord];
  105. int width = size.a[nord];
  106. int arrowlen = width;
  107. if (width < 1 || length < 2)
  108. FAIL;
  109. int tracklen = length - arrowlen * 2;
  110. int trackofs = arrowlen;
  111. BOOL bOverlappingArrows = tracklen < -1;
  112. int overlap = !bOverlappingArrows ? 0 : -tracklen;
  113. SRECT up, down, track, thumb, temp;
  114. if (overlap > 1)
  115. {
  116. int mid = length / 2;
  117. up.lr.a[nord] = width;
  118. up.lr.a[ord] = mid;
  119. down.ul.a[ord] = mid;
  120. down.lr.a[ord] = length;
  121. down.lr.a[nord] = width;
  122. m_rectLineUp = up;
  123. m_rectLineDown = down;
  124. return m_bValid = TRUE;
  125. }
  126. up.lr.a[nord] = width;
  127. up.lr.a[ord] = arrowlen;
  128. down.lr.a[nord] = width;
  129. down.ul.a[ord] = length - arrowlen;
  130. down.lr.a[ord] = length;
  131. m_rectLineUp = up;
  132. m_rectLineDown = down;
  133. int tmin = up.lr.a[ord];
  134. int tmax = down.ul.a[ord];
  135. int trange = tmax - tmin;
  136. int range = m_nMax - m_nMin;
  137. assert(trange > 0);
  138. if (!(range > 0) || !(trange > 0))
  139. return m_bValid = TRUE;
  140. track.ul.a[ord] = tmin;
  141. track.lr.a[nord] = width;
  142. track.lr.a[ord] = tmax;
  143. m_rectTrack = track;
  144. const int minthumblen = 3;
  145. int thumblen = MulDiv(m_nPage, trange, range);
  146. if (thumblen < minthumblen)
  147. thumblen = minthumblen;
  148. int thumbrange = trange - thumblen /*+ 1*/;
  149. int pagerange = range - m_nPage;
  150. if (!(pagerange > 1) || !(thumbrange > 1))
  151. return m_bValid = TRUE;
  152. int logpos = m_bDragging ? m_nPreDragPos : m_nPos;
  153. int thumbpos = MulDiv(logpos - m_nMin, thumbrange, pagerange) + tmin;
  154. if (m_bDragging)
  155. {
  156. SPOINT rp = m_point, rs = m_startpoint;
  157. int rdelta = rp.a[ord] - rs.a[ord];
  158. thumbpos += rdelta;
  159. if (thumbpos < tmin)
  160. thumbpos = tmin;
  161. if (thumbpos > tmax - thumblen)
  162. thumbpos = tmax - thumblen;
  163. m_nThumbPos = MulDiv(thumbpos - tmin, pagerange, thumbrange) + m_nMin;
  164. if (m_nThumbPos < m_nMin)
  165. m_nThumbPos = m_nMin;
  166. if (m_nThumbPos > m_nMax - m_nPage)
  167. m_nThumbPos = m_nMax - m_nPage;
  168. }
  169. thumb.ul.a[ord] = thumbpos;
  170. thumb.lr.a[nord] = width;
  171. thumb.lr.a[ord] = thumbpos + thumblen;
  172. m_rectThumb = thumb;
  173. temp = track;
  174. temp.lr.a[ord] = thumb.ul.a[ord];
  175. if (temp.lr.a[ord] > temp.ul.a[ord])
  176. m_rectPageUp = temp;
  177. temp = track;
  178. temp.ul.a[ord] = thumb.lr.a[ord];
  179. if (temp.lr.a[ord] > temp.ul.a[ord])
  180. m_rectPageDown = temp;
  181. return m_bValid = TRUE;
  182. #undef FAIL
  183. }
  184. void CFlexScrollBar::SetValues(int min, int max, int page, int pos)
  185. {
  186. m_nMin = min < max ? min : max;
  187. m_nMax = max > min ? max : min;
  188. m_nPage = page;
  189. AdjustPos(pos - m_nPos, TRUE);
  190. }
  191. static BOOL UseRect(const RECT &rect)
  192. {
  193. if (rect.left >= rect.right || rect.bottom <= rect.top)
  194. return FALSE;
  195. return TRUE;
  196. }
  197. static void Rectangle(HDC hDC, RECT rect)
  198. {
  199. if (!UseRect(rect))
  200. return;
  201. Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
  202. }
  203. void CFlexScrollBar::OnPaint(HDC hDC)
  204. {
  205. HDC hBDC = NULL, hODC = NULL;
  206. CBitmap *pbm = NULL;
  207. if (!InRenderMode())
  208. {
  209. hODC = hDC;
  210. pbm = CBitmap::Create(GetClientSize(), RGB(0,0,0), hDC);
  211. if (pbm != NULL)
  212. {
  213. hBDC = pbm->BeginPaintInto();
  214. if (hBDC != NULL)
  215. {
  216. hDC = hBDC;
  217. }
  218. }
  219. }
  220. InternalPaint(hDC);
  221. if (!InRenderMode())
  222. {
  223. if (pbm != NULL)
  224. {
  225. if (hBDC != NULL)
  226. {
  227. pbm->EndPaintInto(hBDC);
  228. pbm->Draw(hODC);
  229. }
  230. delete pbm;
  231. }
  232. }
  233. }
  234. void CFlexScrollBar::InternalPaint(HDC hDC)
  235. {
  236. HGDIOBJ hPen, hOldPen, hBrush, hOldBrush;
  237. hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbBk);
  238. if (hPen != NULL)
  239. {
  240. hOldPen = SelectObject(hDC, hPen),
  241. hOldBrush = SelectObject(hDC, GetStockObject(BLACK_BRUSH));
  242. Rectangle(hDC, m_rectPageUp);
  243. Rectangle(hDC, m_rectPageDown);
  244. Rectangle(hDC, m_rectLineUp);
  245. Rectangle(hDC, m_rectLineDown);
  246. SelectObject(hDC, hOldPen);
  247. DeleteObject(hPen);
  248. hBrush = (HGDIOBJ)CreateSolidBrush(m_rgbFill);
  249. if (hBrush != NULL)
  250. {
  251. SelectObject(hDC, (HGDIOBJ)hBrush);
  252. hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbFill);
  253. if (hPen != NULL)
  254. {
  255. SelectObject(hDC, hPen);
  256. Rectangle(hDC, m_rectThumb);
  257. SelectObject(hDC, hOldPen);
  258. DeleteObject(hPen);
  259. }
  260. hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbLine);
  261. if (hPen != NULL)
  262. {
  263. SelectObject(hDC, hPen);
  264. // draw the two arrows for this scrollbar
  265. for (int i = 0; i < 2; i++)
  266. DrawArrow(hDC, i ? m_rectLineUp : m_rectLineDown, m_bVert, i);
  267. #if 0
  268. // draw the two arrows for this scrollbar
  269. for (int i = 0; i < 2; i++)
  270. {
  271. const RECT &rect = i == 0 ? m_rectLineUp : m_rectLineDown;
  272. SRECT srect = rect;
  273. srect.right--;
  274. srect.bottom--;
  275. int ord = m_bVert ? 1 : 0;
  276. int nord = m_bVert ? 0 : 1;
  277. SPOINT p(i ? srect.lr : srect.ul), b(i ? srect.ul : srect.lr);
  278. b.a[ord] += 2 * i - 1;
  279. SPOINT t = p;
  280. t.a[nord] = (p.a[nord] + b.a[nord]) / 2;
  281. SPOINT u;
  282. u.a[ord] = b.a[ord];
  283. u.a[nord] = p.a[nord];
  284. POINT poly[] = { {t.x, t.y}, {u.x, u.y}, {b.x, b.y} };
  285. Polygon(hDC, poly, 3);
  286. }
  287. #endif
  288. SelectObject(hDC, hOldPen);
  289. DeleteObject(hPen);
  290. }
  291. SelectObject(hDC, hOldBrush);
  292. DeleteObject(hBrush);
  293. }
  294. }
  295. }
  296. BOOL InRect(const RECT &rect, POINT point)
  297. {
  298. return UseRect(rect) && PtInRect(&rect, point);
  299. }
  300. LRESULT CFlexScrollBar::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  301. {
  302. switch (msg)
  303. {
  304. case WM_SIZE:
  305. Calc();
  306. Invalidate();
  307. return 0;
  308. // make sure flexwnd doesn't do ANYTHING with our mouse messages
  309. case WM_MOUSEMOVE:
  310. case WM_LBUTTONUP:
  311. case WM_LBUTTONDOWN:
  312. case WM_RBUTTONUP:
  313. case WM_RBUTTONDOWN:
  314. case WM_LBUTTONDBLCLK:
  315. case WM_RBUTTONDBLCLK:
  316. {
  317. POINT point = {int(signed short(LOWORD(lParam))), int(signed short(HIWORD(lParam)))};
  318. m_point = point;
  319. m_code = HitTest(point);
  320. }
  321. case WM_TIMER:
  322. case WM_CAPTURECHANGED:
  323. break;
  324. default:
  325. return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  326. }
  327. switch (msg)
  328. {
  329. case WM_LBUTTONDOWN:
  330. case WM_LBUTTONDBLCLK:
  331. if (m_code == SB_ENDSCROLL)
  332. goto endscroll;
  333. if (m_code == SB_THUMBTRACK)
  334. m_bDragging = TRUE;
  335. else
  336. SetTimer(m_hWnd, 1, 500, NULL);
  337. m_startcode = m_code;
  338. m_startpoint = m_point;
  339. m_nPreDragPos = m_nPos;
  340. m_bCapture = TRUE;
  341. SetCapture();
  342. if (!m_bDragging)
  343. Notify(m_code);
  344. break;
  345. case WM_LBUTTONUP:
  346. case WM_MOUSEMOVE:
  347. if (!m_bDragging)
  348. break;
  349. if (Calc())
  350. {
  351. Invalidate();
  352. // Force repaint the updated scrollbar position. If we don't do this,
  353. // the WM_PAINT message will be pre-empted by the WM_FLEXVSCROLL messages.
  354. // Sometimes this happens during the entire duration of draggin the scroll
  355. // bar. The result is that the scroll bar does not get updated when
  356. // dragging.
  357. SendMessage(m_hWnd, WM_PAINT, 0, 0);
  358. }
  359. Notify(m_startcode);
  360. break;
  361. case WM_TIMER:
  362. if (m_bCapture) switch (wParam)
  363. {
  364. case 1:
  365. KillTimer(m_hWnd, 1);
  366. SetTimer(m_hWnd, 2, 50, NULL);
  367. case 2:
  368. if (m_bDragging)
  369. break;
  370. if (m_code == m_startcode)
  371. Notify(m_code);
  372. break;
  373. }
  374. break;
  375. }
  376. switch (msg)
  377. {
  378. case WM_LBUTTONUP:
  379. case WM_CAPTURECHANGED:
  380. endscroll:
  381. if (m_bCapture)
  382. {
  383. m_bCapture = FALSE;
  384. KillTimer(m_hWnd, 1);
  385. KillTimer(m_hWnd, 2);
  386. ReleaseCapture();
  387. if (m_bDragging)
  388. Notify(SB_THUMBPOSITION);
  389. BOOL bWasDragging = m_bDragging;
  390. m_bDragging = FALSE;
  391. if (bWasDragging)
  392. {
  393. if (Calc())
  394. Invalidate();
  395. }
  396. Notify(SB_ENDSCROLL);
  397. }
  398. break;
  399. }
  400. return 0;
  401. }
  402. void CFlexScrollBar::Notify(int code)
  403. {
  404. if (!m_hWndNotify)
  405. return;
  406. SendMessage(m_hWndNotify, m_bVert ? WM_FLEXVSCROLL : WM_FLEXHSCROLL,
  407. (WPARAM)code, (LPARAM)(LPVOID)this);
  408. }
  409. int CFlexScrollBar::HitTest(POINT point)
  410. {
  411. if (InRect(m_rectLineUp, point))
  412. return SB_LINEUP;
  413. else if (InRect(m_rectLineDown, point))
  414. return SB_LINEDOWN;
  415. else if (InRect(m_rectThumb, point))
  416. return SB_THUMBTRACK;
  417. else if (InRect(m_rectPageUp, point))
  418. return SB_PAGEUP;
  419. else if (InRect(m_rectPageDown, point))
  420. return SB_PAGEDOWN;
  421. else
  422. return SB_ENDSCROLL;
  423. }
  424. void CFlexScrollBar::SetColors(COLORREF bk, COLORREF fill, COLORREF line)
  425. {
  426. m_rgbBk = bk;
  427. m_rgbFill = fill;
  428. m_rgbLine = line;
  429. Invalidate();
  430. }