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.

806 lines
20 KiB

  1. /*++
  2. Copyright (C) 1996-1999 Microsoft Corporation
  3. Module Name:
  4. intrvbar.cpp
  5. Abstract:
  6. Implementation of the interval bar control.
  7. --*/
  8. //==========================================================================//
  9. // Includes //
  10. //==========================================================================//
  11. #include <windows.h>
  12. #include <assert.h>
  13. #include <limits.h>
  14. #include "globals.h"
  15. #include "winhelpr.h"
  16. #include "utils.h"
  17. #include "intrvbar.h"
  18. //==========================================================================//
  19. // Constants //
  20. //==========================================================================//
  21. #define dwILineClassStyle (CS_HREDRAW | CS_VREDRAW)
  22. #define dwILineWindowStyle (WS_CHILD | WS_VISIBLE)
  23. #define TO_THE_END 0x7FFFFFFFL
  24. #define szILineClass TEXT("IntervalBar")
  25. //==========================================================================//
  26. // Macros //
  27. //==========================================================================//
  28. // Width of the start and stob grab bars
  29. #define ILGrabWidth() \
  30. (10)
  31. #define ILGrabMinimumWidth() \
  32. (6)
  33. // A rectangle is "drawable" if it has both nonzero height and minimum width
  34. #define PRectDrawable(lpRect) \
  35. ((lpRect->right - lpRect->left) >= ILGrabMinimumWidth()) && \
  36. (lpRect->bottom - lpRect->top)
  37. #define RectDrawable(Rect) \
  38. ((Rect.right - Rect.left) >= ILGrabMinimumWidth()) && \
  39. (Rect.bottom - Rect.top)
  40. //==========================================================================//
  41. // Local Functions //
  42. //==========================================================================//
  43. void
  44. CIntervalBar::NotifyChange (
  45. void
  46. )
  47. {
  48. HWND hWndParent ;
  49. hWndParent = WindowParent (m_hWnd) ;
  50. if (hWndParent)
  51. SendMessage (hWndParent, WM_COMMAND,
  52. (WPARAM) WindowID (m_hWnd),
  53. (LPARAM) m_hWnd) ;
  54. }
  55. BOOL
  56. CIntervalBar::GrabRect (
  57. OUT LPRECT lpRect
  58. )
  59. {
  60. switch (m_iMode) {
  61. case ModeLeft:
  62. *lpRect = m_rectLeftGrab ;
  63. return (TRUE) ;
  64. break ;
  65. case ModeRight:
  66. *lpRect = m_rectRightGrab ;
  67. return (TRUE) ;
  68. break ;
  69. case ModeCenter:
  70. *lpRect = m_rectCenterGrab ;
  71. return (TRUE) ;
  72. break ;
  73. case ModeNone:
  74. lpRect->left = 0 ;
  75. lpRect->top = 0 ;
  76. lpRect->right = 0 ;
  77. lpRect->bottom = 0 ;
  78. return (FALSE) ;
  79. break ;
  80. default:
  81. return (FALSE);
  82. }
  83. }
  84. void
  85. CIntervalBar::DrawGrab (
  86. HDC hDC,
  87. LPRECT lpRectGrab,
  88. BOOL bDown
  89. )
  90. {
  91. if (!PRectDrawable(lpRectGrab))
  92. return ;
  93. Fill(hDC, GetSysColor(COLOR_3DFACE), lpRectGrab);
  94. DrawEdge (hDC, lpRectGrab, (bDown ? EDGE_SUNKEN:EDGE_RAISED), BF_RECT);
  95. }
  96. INT
  97. CIntervalBar::ValueToPixel (
  98. INT iValue
  99. )
  100. {
  101. INT xPixel ;
  102. if (m_iEndValue > m_iBeginValue)
  103. xPixel = MulDiv (iValue, m_rectBorder.right, (m_iEndValue - m_iBeginValue)) ;
  104. else
  105. xPixel = 0 ;
  106. return (PinExclusive (xPixel, 0, m_rectBorder.right)) ;
  107. }
  108. INT
  109. CIntervalBar::PixelToValue (
  110. INT xPixel
  111. )
  112. {
  113. INT iValue ;
  114. if (m_rectBorder.right)
  115. iValue = MulDiv (xPixel, (m_iEndValue - m_iBeginValue), m_rectBorder.right) ;
  116. else
  117. iValue = 0 ;
  118. return (PinInclusive (iValue, m_iBeginValue, m_iEndValue)) ;
  119. }
  120. void
  121. CIntervalBar::CalcPositions (
  122. void
  123. )
  124. /*
  125. Effect: Determine and set all of the physical rectangles of ILine,
  126. based on the current size of the ILine window and the
  127. current logical Start, Stop, Begin, and End values.
  128. */
  129. {
  130. INT xStart, xStop ;
  131. INT yHeight ;
  132. GetClientRect (m_hWnd, &m_rectBorder) ;
  133. yHeight = m_rectBorder.bottom ;
  134. xStart = ValueToPixel (m_iStartValue) ;
  135. xStop = ValueToPixel (m_iStopValue) ;
  136. m_rectLeftBk.left = 1 ;
  137. m_rectLeftBk.top = 1 ;
  138. m_rectLeftBk.right = xStart ;
  139. m_rectLeftBk.bottom = yHeight - 1 ;
  140. m_rectLeftGrab.left = xStart ;
  141. m_rectLeftGrab.top = 1 ;
  142. m_rectLeftGrab.right = xStart + ILGrabWidth () ;
  143. m_rectLeftGrab.bottom = yHeight - 1 ;
  144. m_rectRightBk.left = xStop ;
  145. m_rectRightBk.top = 1 ;
  146. m_rectRightBk.right = m_rectBorder.right - 1 ;
  147. m_rectRightBk.bottom = yHeight - 1 ;
  148. m_rectRightGrab.left = xStop - ILGrabWidth () ;
  149. m_rectRightGrab.top = 1 ;
  150. m_rectRightGrab.right = xStop ;
  151. m_rectRightGrab.bottom = yHeight - 1 ;
  152. m_rectCenterGrab.left = m_rectLeftGrab.right ;
  153. m_rectCenterGrab.top = 1 ;
  154. m_rectCenterGrab.right = m_rectRightGrab.left ;
  155. m_rectCenterGrab.bottom = yHeight - 1 ;
  156. if (m_rectLeftGrab.right > m_rectRightGrab.left) {
  157. m_rectLeftGrab.right = m_rectLeftGrab.left + (xStop - xStart) / 2 ;
  158. m_rectRightGrab.left = m_rectLeftGrab.right ;
  159. m_rectCenterGrab.left = 0 ;
  160. m_rectCenterGrab.right = 0 ;
  161. // Ensure that at least one grab bar is visible when End > Begin and the total is
  162. // wide enough. ILGrabMinimumWidth + 2 is the minimum.
  163. // If on the left edge, make the Right grab visible.
  164. // If on the right edge, make the Left grab visible.
  165. // If in the middle, make them both visible.
  166. if ( !RectDrawable(m_rectLeftGrab)
  167. || !RectDrawable(m_rectRightGrab) ) {
  168. INT iWidth = ILGrabMinimumWidth();
  169. if ( !RectDrawable(m_rectRightBk) ) {
  170. // Make the Left grab visible.
  171. m_rectRightGrab.left = m_rectRightGrab.right;
  172. m_rectLeftGrab.right = m_rectRightGrab.right;
  173. m_rectLeftGrab.left = m_rectLeftGrab.right - iWidth;
  174. } else if (!RectDrawable(m_rectLeftBk) ) {
  175. // Make the Right grab visible.
  176. m_rectLeftGrab.right = m_rectLeftGrab.left;
  177. m_rectRightGrab.left = m_rectLeftGrab.left;
  178. m_rectRightGrab.right = m_rectRightGrab.left + iWidth;
  179. } else {
  180. // Make them both visible.
  181. m_rectLeftGrab.left -= iWidth;
  182. m_rectRightGrab.right += iWidth;
  183. }
  184. }
  185. }
  186. }
  187. void
  188. CIntervalBar::Draw (
  189. HDC hDC,
  190. LPRECT // lpRectUpdate
  191. )
  192. /*
  193. Effect: Draw the image of pILine on hDC. Draw at least the
  194. portions within rectUpdate.
  195. Called By: OnPaint, OnMouseMove.
  196. */
  197. {
  198. if (IsWindowEnabled(m_hWnd)) {
  199. FillRect (hDC, &m_rectLeftBk, m_hBrushBk) ;
  200. FillRect (hDC, &m_rectRightBk, m_hBrushBk) ;
  201. //DrawEdge (hDC, &m_rectBorder, BDR_SUNKENINNER, BF_RECT) ;
  202. DrawEdge (hDC, &m_rectBorder, EDGE_SUNKEN, BF_RECT) ;
  203. DrawGrab (hDC, &m_rectLeftGrab, m_iMode == ModeLeft) ;
  204. DrawGrab (hDC, &m_rectRightGrab, m_iMode == ModeRight) ;
  205. DrawGrab (hDC, &m_rectCenterGrab, m_iMode == ModeCenter) ;
  206. }
  207. else {
  208. Fill(hDC, GetSysColor(COLOR_3DFACE), &m_rectBorder);
  209. DrawEdge (hDC, &m_rectBorder, EDGE_SUNKEN, BF_RECT) ;
  210. }
  211. }
  212. void
  213. CIntervalBar::MoveLeftRight (
  214. BOOL bStart,
  215. BOOL bLeft,
  216. INT iMoveAmt
  217. )
  218. {
  219. INT iStart, iStop, iMove ;
  220. iStart = m_iStartValue;
  221. iStop = m_iStopValue;
  222. iMove = iMoveAmt ;
  223. if (bLeft)
  224. iMove = -iMove ;
  225. if (bStart)
  226. {
  227. if (iMoveAmt == TO_THE_END) {
  228. iStart = m_iBeginValue ;
  229. }
  230. else {
  231. iStart += iMove ;
  232. if (iStart >= iStop) {
  233. return;
  234. }
  235. }
  236. SetStart (iStart) ;
  237. }
  238. else {
  239. if (iMoveAmt == TO_THE_END) {
  240. iStop = m_iEndValue ;
  241. }
  242. else {
  243. iStop += iMove ;
  244. if (iStart >= iStop) {
  245. return;
  246. }
  247. }
  248. SetStop (iStop) ;
  249. }
  250. NotifyChange () ;
  251. }
  252. BOOL
  253. CIntervalBar::OnKeyDown (
  254. WPARAM wParam
  255. )
  256. {
  257. BOOL bHandle = TRUE ;
  258. BOOL bStart ;
  259. BOOL bLeftDirection ;
  260. BOOL bShiftKeyDown ;
  261. if (wParam == VK_LEFT || wParam == VK_RIGHT) {
  262. bShiftKeyDown = (GetKeyState (VK_SHIFT) < 0) ;
  263. if (!bShiftKeyDown) {
  264. if (wParam == VK_LEFT) {
  265. // Left Arrow --> move Start Edge Left
  266. bStart = TRUE ;
  267. bLeftDirection = TRUE ;
  268. }
  269. else {
  270. // Right Arrow --> move Stop Edge Right
  271. bStart = FALSE ;
  272. bLeftDirection = FALSE ;
  273. }
  274. }
  275. else {
  276. if (wParam == VK_LEFT) {
  277. // Shift Left Arrow --> move Stop Edge Left
  278. bStart = FALSE ;
  279. bLeftDirection = TRUE ;
  280. }
  281. else {
  282. // Shift Right Arrow --> move Start Edge Right
  283. bStart = TRUE ;
  284. bLeftDirection = FALSE ;
  285. }
  286. }
  287. MoveLeftRight (bStart, bLeftDirection, 1) ;
  288. }
  289. else if (wParam == VK_HOME) {
  290. // move iStart all the way the Left
  291. MoveLeftRight (TRUE, TRUE, TO_THE_END) ;
  292. }
  293. else if (wParam == VK_END) {
  294. // move iStop all the way the right
  295. MoveLeftRight (FALSE, FALSE, TO_THE_END) ;
  296. }
  297. else {
  298. bHandle = FALSE ;
  299. }
  300. return (bHandle) ;
  301. }
  302. void
  303. CIntervalBar::StartGrab (
  304. void
  305. )
  306. {
  307. RECT rectUpdate ;
  308. SetCapture (m_hWnd) ;
  309. GrabRect (&rectUpdate) ;
  310. Update();
  311. }
  312. void
  313. CIntervalBar::EndGrab (
  314. void
  315. )
  316. /*
  317. Internals: Set the mode to null after getting the grab rectangle
  318. so ILGrabRect knows which grab bar to get.
  319. */
  320. {
  321. RECT rectUpdate ;
  322. ReleaseCapture () ;
  323. GrabRect (&rectUpdate) ;
  324. m_iMode = ModeNone ;
  325. Update();
  326. }
  327. //==========================================================================//
  328. // Message Handlers //
  329. //==========================================================================//
  330. CIntervalBar::CIntervalBar (
  331. void
  332. )
  333. {
  334. m_hWnd = NULL;
  335. m_iBeginValue = 0;
  336. m_iEndValue = 100;
  337. m_iStartValue = 0;
  338. m_iStopValue = 100;
  339. m_iMode = ModeNone;
  340. m_hBrushBk = NULL;
  341. }
  342. CIntervalBar::~CIntervalBar (
  343. void
  344. )
  345. {
  346. if (m_hWnd)
  347. DestroyWindow(m_hWnd);
  348. if (m_hBrushBk)
  349. DeleteBrush (m_hBrushBk);
  350. }
  351. BOOL
  352. CIntervalBar::Init (
  353. HWND hWndParent
  354. )
  355. {
  356. #define dwIntervalBarClassStyle (CS_HREDRAW | CS_VREDRAW)
  357. #define dwIntervalBarStyle (WS_CHILD | WS_VISIBLE)
  358. #define szIntervalBarClass TEXT("IntervalBar")
  359. // Register window class once
  360. if (pstrRegisteredClasses[INTRVBAR_WNDCLASS] == NULL) {
  361. WNDCLASS wc ;
  362. wc.style = dwILineClassStyle ;
  363. wc.lpfnWndProc = (WNDPROC)IntervalBarWndProc ;
  364. wc.cbClsExtra = 0 ;
  365. wc.cbWndExtra = sizeof(PCIntervalBar) ;
  366. wc.hInstance = g_hInstance ;
  367. wc.hIcon = NULL ;
  368. wc.hCursor = LoadCursor (NULL, IDC_ARROW) ;
  369. wc.hbrBackground = NULL ;
  370. wc.lpszMenuName = NULL ;
  371. wc.lpszClassName = szIntervalBarClass ;
  372. if (RegisterClass (&wc)) {
  373. pstrRegisteredClasses[INTRVBAR_WNDCLASS] = szIntervalBarClass;
  374. }
  375. else {
  376. return FALSE;
  377. }
  378. }
  379. // Create our window
  380. m_hWnd = CreateWindow (szIntervalBarClass, // class
  381. NULL, // caption
  382. dwIntervalBarStyle, // window style
  383. 0, 0, // position
  384. 0, 0, // size
  385. hWndParent, // parent window
  386. NULL, // menu
  387. g_hInstance, // program instance
  388. (LPVOID) this ); // user-supplied data
  389. if (m_hWnd == NULL) {
  390. return FALSE;
  391. }
  392. m_hBrushBk = CreateSolidBrush (GetSysColor(COLOR_SCROLLBAR)) ;
  393. CalcPositions () ;
  394. return TRUE;
  395. }
  396. void
  397. CIntervalBar::OnLButtonUp (
  398. void
  399. )
  400. {
  401. if (m_iMode == ModeNone)
  402. return ;
  403. EndGrab () ;
  404. }
  405. void
  406. CIntervalBar::OnMouseMove (
  407. POINTS ptsMouse
  408. )
  409. /*
  410. Effect: Handle any actions needed when the mouse moves in the
  411. ILine hWnd's client area or while the mouse is captured.
  412. In particular, if we are tracking one of the grab bars,
  413. determine if the mouse movement represents a logical value
  414. change and move the grab bar accordingly.
  415. Called By: ILineWndProc, in response to a WM_MOUSEMOVE message.
  416. See Also: OnLButtonDown, OnLButtonUp.
  417. Note: This function has multiple return points.
  418. Note: Since we have captured the mouse, we receive mouse msgs
  419. even when the mouse is outside our client area, but still
  420. in client coordinates. Thus we can have negative mouse
  421. coordinates. That is why we convert the lParam of the
  422. mouse msg into a POINTS structure rather than 2 WORDS.
  423. Internals: Remember that an IntervalLine can only take on integral
  424. values in the user-supplied range. Therefore we do our
  425. movement calculation in user values, not pixels. We
  426. determine what the logical value would be for the previous
  427. (last mouse move) and current mouse position. If these
  428. LOGICAL values differ, we attempt an adjustment of the
  429. grab bar by that logical amount. This way the grab
  430. values assume on integral positions and the calculations
  431. are simplified.
  432. If we calculated by pixel movement, and then shifted the
  433. bar into the nearest integal position, we would encounter
  434. rounding problems. In particular, when tracking the center
  435. grab bar, if we moved both start and stop by the same
  436. amount of PIXELS, then converted to LOGICAL values, we
  437. might find our center bar shrinking and growing while
  438. the bar moves.
  439. */
  440. {
  441. INT iMousePrevious, iMouseCurrent ;
  442. INT iMouseMove ;
  443. // Are we tracking?
  444. if (m_iMode == ModeNone)
  445. return ;
  446. // Calc LOGICAL mouse movement
  447. assert ( USHRT_MAX >= m_rectBorder.left );
  448. assert ( USHRT_MAX >= m_rectBorder.right );
  449. ptsMouse.x = PinInclusive (ptsMouse.x,
  450. (SHORT)m_rectBorder.left,
  451. (SHORT)m_rectBorder.right) ;
  452. iMousePrevious = PixelToValue (m_ptsMouse.x) ;
  453. iMouseCurrent = PixelToValue (ptsMouse.x) ;
  454. iMouseMove = iMouseCurrent - iMousePrevious ;
  455. if (!iMouseMove)
  456. return ;
  457. // Move grab bar positions
  458. switch (m_iMode) {
  459. case ModeLeft:
  460. m_iStartValue += iMouseMove ;
  461. m_iStartValue = min (m_iStartValue, m_iStopValue - 1) ;
  462. break ;
  463. case ModeCenter:
  464. // Before we slide the center grab bar we need to see if the
  465. // desired movement amount would send either end out of bounds,
  466. // and reduce the movement accordingly.
  467. if (m_iStartValue + iMouseMove < m_iBeginValue)
  468. iMouseMove = m_iBeginValue - m_iStartValue ;
  469. if (m_iStopValue + iMouseMove > m_iEndValue)
  470. iMouseMove = m_iEndValue - m_iStopValue ;
  471. m_iStartValue += iMouseMove ;
  472. m_iStopValue += iMouseMove ;
  473. break ;
  474. case ModeRight:
  475. m_iStopValue += iMouseMove ;
  476. m_iStopValue = max (m_iStartValue + 1, m_iStopValue) ;
  477. break ;
  478. }
  479. m_iStartValue = PinInclusive (m_iStartValue, m_iBeginValue, m_iEndValue) ;
  480. m_iStopValue = PinInclusive (m_iStopValue, m_iBeginValue, m_iEndValue) ;
  481. Update();
  482. m_ptsMouse = ptsMouse ;
  483. NotifyChange () ;
  484. }
  485. void
  486. CIntervalBar::OnLButtonDown (
  487. POINTS ptsMouse
  488. )
  489. {
  490. POINT ptMouse ;
  491. m_ptsMouse = ptsMouse ;
  492. ptMouse.x = ptsMouse.x ;
  493. ptMouse.y = ptsMouse.y ;
  494. if (PtInRect (&m_rectLeftGrab, ptMouse) ||
  495. PtInRect (&m_rectLeftBk, ptMouse)) {
  496. m_iMode = ModeLeft ;
  497. }
  498. else if (PtInRect (&m_rectRightGrab, ptMouse) ||
  499. PtInRect (&m_rectRightBk, ptMouse)) {
  500. m_iMode = ModeRight ;
  501. }
  502. else if (PtInRect (&m_rectCenterGrab, ptMouse)) {
  503. m_iMode = ModeCenter ;
  504. }
  505. if (m_iMode != ModeNone)
  506. StartGrab();
  507. }
  508. void
  509. CIntervalBar::Update (
  510. void
  511. )
  512. {
  513. HDC hDC;
  514. // Determine pixel pos, draw
  515. CalcPositions () ;
  516. hDC = GetDC (m_hWnd) ;
  517. if ( NULL != hDC ) {
  518. Draw (hDC, &m_rectBorder) ;
  519. ReleaseDC (m_hWnd, hDC) ;
  520. }
  521. }
  522. //==========================================================================//
  523. // Exported Functions //
  524. //==========================================================================//
  525. LRESULT APIENTRY IntervalBarWndProc (
  526. HWND hWnd,
  527. UINT uiMsg,
  528. WPARAM wParam,
  529. LPARAM lParam
  530. )
  531. /*
  532. Note: This function must be declared in the application's
  533. linker-definition file, perfmon.def file.
  534. */
  535. {
  536. PCIntervalBar pIntrvBar;
  537. BOOL bCallDefWindowProc ;
  538. POINTS ptsMouse ;
  539. LRESULT lrsltReturnValue ;
  540. bCallDefWindowProc = FALSE ;
  541. lrsltReturnValue = 0L ;
  542. if (uiMsg == WM_CREATE) {
  543. pIntrvBar = (PCIntervalBar)((CREATESTRUCT*)lParam)->lpCreateParams;
  544. } else {
  545. pIntrvBar = (PCIntervalBar)GetWindowLongPtr (hWnd, 0);
  546. }
  547. switch (uiMsg) {
  548. case WM_CREATE:
  549. SetWindowLongPtr(hWnd, 0, (INT_PTR)pIntrvBar);
  550. break ;
  551. case WM_LBUTTONDOWN:
  552. // See the note in OnMouseMove for why we are using POINTS
  553. SetFocus (hWnd) ;
  554. ptsMouse = MAKEPOINTS (lParam) ;
  555. pIntrvBar->OnLButtonDown (ptsMouse) ;
  556. break ;
  557. case WM_LBUTTONUP:
  558. pIntrvBar->OnLButtonUp () ;
  559. break ;
  560. case WM_SETFOCUS:
  561. case WM_KILLFOCUS:
  562. pIntrvBar->NotifyChange () ;
  563. return 0 ;
  564. case WM_ENABLE:
  565. pIntrvBar->Update();
  566. break;
  567. case WM_MOUSEMOVE:
  568. // See the note in OnMouseMove for why we are using POINTS
  569. ptsMouse = MAKEPOINTS (lParam) ;
  570. pIntrvBar->OnMouseMove (ptsMouse) ;
  571. break ;
  572. case WM_KEYDOWN:
  573. if (!pIntrvBar->OnKeyDown (wParam)) {
  574. bCallDefWindowProc = TRUE ;
  575. }
  576. break ;
  577. case WM_GETDLGCODE:
  578. // We want to handle Arrow keys input. If we don't specify this
  579. // the dialog will not pass arrow keys to us.
  580. return (DLGC_WANTARROWS) ;
  581. break ;
  582. case WM_PAINT:
  583. {
  584. PAINTSTRUCT ps ;
  585. HDC hDC;
  586. hDC = BeginPaint (hWnd, &ps) ;
  587. pIntrvBar->Draw (hDC, &ps.rcPaint) ;
  588. EndPaint (hWnd, &ps) ;
  589. }
  590. break ;
  591. case WM_SIZE:
  592. pIntrvBar->CalcPositions () ;
  593. break;
  594. default:
  595. bCallDefWindowProc = TRUE ;
  596. }
  597. if (bCallDefWindowProc)
  598. lrsltReturnValue = DefWindowProc (hWnd, uiMsg, wParam, lParam) ;
  599. return (lrsltReturnValue) ;
  600. }
  601. void
  602. CIntervalBar::SetRange (
  603. INT iBegin,
  604. INT iEnd
  605. )
  606. {
  607. m_iBeginValue = iBegin;
  608. m_iEndValue = iEnd;
  609. Update();
  610. }
  611. void
  612. CIntervalBar::SetStart (
  613. INT iStart
  614. )
  615. {
  616. m_iStartValue = PinInclusive (iStart, m_iBeginValue, m_iEndValue) ;
  617. Update();
  618. }
  619. void
  620. CIntervalBar::SetStop (
  621. INT iStop
  622. )
  623. {
  624. m_iStopValue = PinInclusive (iStop, m_iBeginValue, m_iEndValue) ;
  625. Update();
  626. }