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.

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