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.

1352 lines
42 KiB

  1. /*++
  2. Copyright (C) 1996-1999 Microsoft Corporation
  3. Module Name:
  4. grphdsp.cpp
  5. Abstract:
  6. <abstract>
  7. --*/
  8. #include "polyline.h"
  9. #include "winhelpr.h"
  10. #include <assert.h>
  11. #include <limits.h>
  12. #define ThreeDPad 1
  13. #define BORDER ThreeDPad
  14. #define TEXT_MARGIN (ThreeDPad + 2)
  15. static HPEN hPenWhite;
  16. static HPEN hPenBlack;
  17. INT
  18. CGraphDisp::RGBToLightness ( COLORREF clrValue )
  19. {
  20. INT iLightness;
  21. INT iRed;
  22. INT iGreen;
  23. INT iBlue;
  24. INT iMax;
  25. INT iMin;
  26. // The complete algorithm for computing lightness is:
  27. // Lightness = (Max(R,G,B)+ Min(R,G,B))/2*225.
  28. // Only need to compute enought to determine whether to draw black or white highlight.
  29. iRed = GetRValue( clrValue );
  30. iGreen = GetGValue (clrValue );
  31. iBlue = GetBValue (clrValue );
  32. if ( iRed > iGreen ) {
  33. iMax = iRed;
  34. iMin = iGreen;
  35. } else {
  36. iMax = iGreen;
  37. iMin = iRed;
  38. }
  39. if ( iBlue > iMax ) {
  40. iMax = iBlue;
  41. } else if ( iBlue < iMin ) {
  42. iMin = iBlue;
  43. }
  44. iLightness = iMin + iMax;
  45. return iLightness;
  46. }
  47. CGraphDisp::CGraphDisp ( void )
  48. : m_pCtrl ( NULL ),
  49. m_pGraph ( NULL ),
  50. m_pHiliteItem ( NULL ),
  51. m_hFontVertical ( NULL ),
  52. m_bBarConfigChanged ( TRUE )
  53. {
  54. }
  55. CGraphDisp::~CGraphDisp ( void )
  56. {
  57. if (m_hFontVertical != NULL)
  58. DeleteObject(m_hFontVertical);
  59. if (m_hPenTimeBar != 0) {
  60. DeleteObject ( m_hPenTimeBar );
  61. m_hPenTimeBar = 0;
  62. }
  63. if (m_hPenGrid != 0) {
  64. DeleteObject ( m_hPenGrid );
  65. m_hPenGrid = 0;
  66. }
  67. }
  68. BOOL
  69. CGraphDisp::Init (
  70. CSysmonControl *pCtrl,
  71. PGRAPHDATA pGraph
  72. )
  73. {
  74. BOOL bRetStatus = TRUE;
  75. m_pCtrl = pCtrl;
  76. m_pGraph = pGraph;
  77. m_clrCurrentGrid = m_pCtrl->clrGrid();
  78. m_clrCurrentTimeBar = m_pCtrl->clrTimeBar();
  79. // Create the highlight, timebar and grid pens.
  80. m_hPenTimeBar = CreatePen(PS_SOLID, 2, m_clrCurrentTimeBar );
  81. // if can't do it, use a stock object (this can't fail)
  82. if (m_hPenTimeBar == NULL)
  83. m_hPenTimeBar = (HPEN)GetStockObject(BLACK_PEN);
  84. m_hPenGrid = CreatePen(PS_SOLID, 1, m_clrCurrentGrid );
  85. // if can't do it, use a stock object (this can't fail)
  86. if (m_hPenGrid == NULL)
  87. m_hPenGrid = (HPEN)GetStockObject(BLACK_PEN);
  88. // Highlight pens are shared among all Sysmon instances.
  89. BEGIN_CRITICAL_SECTION
  90. if (hPenWhite == 0) {
  91. hPenWhite = CreatePen(PS_SOLID, 3, RGB(255,255,255));
  92. hPenBlack = CreatePen(PS_SOLID, 3, RGB(0,0,0));
  93. }
  94. END_CRITICAL_SECTION
  95. return bRetStatus;
  96. }
  97. void CGraphDisp::HiliteItem( PCGraphItem pItem )
  98. {
  99. m_pHiliteItem = pItem;
  100. }
  101. VOID
  102. CGraphDisp::Draw(
  103. HDC hDC,
  104. HDC hAttribDC,
  105. BOOL fMetafile,
  106. BOOL fEntire,
  107. PRECT /* prcUpdate */ )
  108. {
  109. RECT rectFrame;
  110. RECT rectTitle;
  111. CStepper locStepper;
  112. DWORD dwPrevLayout = 0;
  113. DWORD dwNewLayout = 0;
  114. if ( ( m_rect.right > m_rect.left ) && ( m_rect.bottom > m_rect.top ) ) {
  115. if ( NULL != hDC ) {
  116. dwPrevLayout = GetLayout ( hDC );
  117. dwNewLayout = dwPrevLayout;
  118. if ( dwNewLayout & LAYOUT_RTL ) {
  119. dwNewLayout &= ~LAYOUT_RTL;
  120. SetLayout (hDC, dwNewLayout);
  121. }
  122. // Fill plot area
  123. Fill(hDC, m_pCtrl->clrBackPlot(), &m_rectPlot);
  124. rectFrame = m_rectPlot;
  125. // Draw 3D border around plot area
  126. if ( eAppear3D == m_pCtrl->Appearance() ) {
  127. InflateRect(&rectFrame,BORDER,BORDER);
  128. DrawEdge(hDC, &rectFrame, BDR_SUNKENOUTER, BF_RECT);
  129. }
  130. // Select colors for all text
  131. SetBkMode(hDC, TRANSPARENT);
  132. SetTextColor(hDC, m_pCtrl->clrFgnd());
  133. // Draw the scale
  134. if (m_pGraph->Options.bLabelsChecked) {
  135. SelectFont(hDC, m_pCtrl->Font()) ;
  136. m_pGraph->Scale.Draw(hDC);
  137. }
  138. // Draw the main title
  139. if (m_pGraph->Options.pszGraphTitle != NULL) {
  140. SelectFont(hDC, m_pCtrl->Font()) ;
  141. SetTextAlign(hDC, TA_TOP|TA_CENTER);
  142. rectTitle = rectFrame;
  143. rectTitle.top = m_rect.top;
  144. FitTextOut(
  145. hDC,
  146. hAttribDC,
  147. 0,
  148. &rectTitle,
  149. m_pGraph->Options.pszGraphTitle,
  150. lstrlen(m_pGraph->Options.pszGraphTitle),
  151. TA_CENTER,
  152. FALSE );
  153. }
  154. // Draw the Y axis title
  155. if (m_pGraph->Options.pszYaxisTitle != NULL && m_hFontVertical != NULL) {
  156. SelectFont(hDC, m_hFontVertical) ;
  157. SetTextAlign(hDC, TA_TOP|TA_CENTER);
  158. rectTitle = rectFrame;
  159. rectTitle.left = m_rect.left;
  160. FitTextOut(
  161. hDC,
  162. hAttribDC,
  163. 0,
  164. &rectTitle,
  165. m_pGraph->Options.pszYaxisTitle,
  166. lstrlen(m_pGraph->Options.pszYaxisTitle),
  167. TA_CENTER,
  168. TRUE);
  169. }
  170. // setup stepper reset to start
  171. locStepper = m_pGraph->TimeStepper;
  172. locStepper.Reset();
  173. // Set clipping area. Fill executed above, so bFill= FALSE.
  174. StartUpdate(hDC, fMetafile, fEntire, 0, (m_rectPlot.right - m_rectPlot.left), FALSE );
  175. // draw the grid lines
  176. DrawGrid(hDC, 0, m_rectPlot.right - m_rectPlot.left);
  177. m_pCtrl->LockCounterData();
  178. switch (m_pGraph->Options.iDisplayType) {
  179. case LINE_GRAPH:
  180. // Finish and restart update so that wide lines are cropped at the timeline.
  181. FinishUpdate(hDC, fMetafile);
  182. StartUpdate(
  183. hDC,
  184. fMetafile,
  185. FALSE,
  186. 0,
  187. m_pGraph->TimeStepper.Position(),
  188. FALSE );
  189. // Plot points from start of graph to time line
  190. PlotData(hDC, m_pGraph->TimeStepper.StepNum() + m_pGraph->History.nBacklog,
  191. m_pGraph->TimeStepper.StepNum(), &locStepper);
  192. FinishUpdate(hDC, fMetafile);
  193. // Plot points from time line to end of graph
  194. locStepper = m_pGraph->TimeStepper;
  195. // Restart update. Left-hand ends and internal gaps of wide lines are not cropped.
  196. StartUpdate(
  197. hDC,
  198. fMetafile,
  199. FALSE,
  200. locStepper.Position(),
  201. m_rectPlot.right - m_rectPlot.left,
  202. FALSE );
  203. PlotData(hDC, m_pGraph->TimeStepper.StepCount() + m_pGraph->History.nBacklog,
  204. m_pGraph->TimeStepper.StepCount() - m_pGraph->TimeStepper.StepNum(),
  205. &locStepper);
  206. DrawTimeLine(hDC, m_pGraph->TimeStepper.Position());
  207. if ( MIN_TIME_VALUE != m_pGraph->LogViewTempStart )
  208. DrawStartStopLine(hDC, m_pGraph->LogViewStartStepper.Position());
  209. if ( MAX_TIME_VALUE != m_pGraph->LogViewTempStop )
  210. DrawStartStopLine(hDC, m_pGraph->LogViewStopStepper.Position());
  211. break;
  212. case BAR_GRAPH:
  213. PlotBarGraph(hDC, FALSE);
  214. break;
  215. }
  216. FinishUpdate(hDC, fMetafile);
  217. if ( dwNewLayout != dwPrevLayout ) {
  218. SetLayout (hDC, dwPrevLayout);
  219. }
  220. m_pCtrl->UnlockCounterData();
  221. }
  222. }
  223. }
  224. VOID
  225. CGraphDisp::UpdateTimeBar(
  226. HDC hDC,
  227. BOOL bPlotData )
  228. {
  229. INT nBacklog;
  230. INT iUpdateCnt;
  231. INT i;
  232. CStepper locStepper;
  233. nBacklog = m_pGraph->History.nBacklog;
  234. // Work off backlogged sample intervals
  235. while ( nBacklog > 0) {
  236. // If we are going to wrap around, update in two steps
  237. if (nBacklog > m_pGraph->TimeStepper.StepCount()
  238. - m_pGraph->TimeStepper.StepNum()) {
  239. iUpdateCnt = m_pGraph->TimeStepper.StepCount()
  240. - m_pGraph->TimeStepper.StepNum();
  241. } else {
  242. iUpdateCnt = nBacklog;
  243. }
  244. // step to position of current data
  245. locStepper = m_pGraph->TimeStepper;
  246. for (i=0; i<iUpdateCnt; i++)
  247. m_pGraph->TimeStepper.NextPosition();
  248. if ( bPlotData ) {
  249. StartUpdate(
  250. hDC,
  251. FALSE,
  252. FALSE,
  253. locStepper.Position(),
  254. m_pGraph->TimeStepper.Position(),
  255. TRUE );
  256. DrawGrid(hDC, locStepper.Position(), m_pGraph->TimeStepper.Position());
  257. PlotData(hDC, nBacklog, iUpdateCnt, &locStepper);
  258. FinishUpdate ( hDC, FALSE );
  259. }
  260. if (m_pGraph->TimeStepper.StepNum() >= m_pGraph->TimeStepper.StepCount())
  261. m_pGraph->TimeStepper.Reset();
  262. nBacklog -= iUpdateCnt;
  263. }
  264. if ( bPlotData ) {
  265. DrawTimeLine(hDC, m_pGraph->TimeStepper.Position());
  266. }
  267. m_pGraph->History.nBacklog = 0;
  268. }
  269. VOID
  270. CGraphDisp::Update( HDC hDC )
  271. {
  272. DWORD dwPrevLayout = 0;
  273. DWORD dwNewLayout = 0;
  274. m_pCtrl->LockCounterData();
  275. if ( NULL != hDC ) {
  276. dwPrevLayout = GetLayout ( hDC );
  277. dwNewLayout = dwPrevLayout;
  278. if ( dwNewLayout & LAYOUT_RTL ) {
  279. dwNewLayout &= ~LAYOUT_RTL;
  280. SetLayout (hDC, dwNewLayout);
  281. }
  282. if ( ( m_rect.right > m_rect.left ) && ( m_rect.bottom > m_rect.top ) ) {
  283. switch (m_pGraph->Options.iDisplayType) {
  284. case LINE_GRAPH:
  285. // Update the line graph and time bar based on history
  286. // backlog. Reset history backlog to 0, signalling collection
  287. // thread to post another WM_GRAPH_UPDATE message.
  288. UpdateTimeBar ( hDC, TRUE );
  289. break;
  290. case BAR_GRAPH:
  291. PlotBarGraph(hDC, TRUE);
  292. break;
  293. }
  294. }
  295. // If updating histogram or report, update thetimebar step based on
  296. // history backlog. Reset history backlog to 0, signalling collection
  297. // thread to post another WM_GRAPH_UPDATE message.
  298. UpdateTimeBar ( hDC, FALSE );
  299. if ( dwNewLayout != dwPrevLayout ) {
  300. SetLayout (hDC, dwPrevLayout);
  301. }
  302. }
  303. m_pCtrl->UnlockCounterData();
  304. }
  305. void
  306. CGraphDisp::StartUpdate(
  307. HDC hDC,
  308. BOOL fMetafile,
  309. BOOL fEntire,
  310. INT xLeft,
  311. INT xRight,
  312. BOOL bFill )
  313. {
  314. RECT rect;
  315. // Preserve clipping region
  316. if ( FALSE == fMetafile ) {
  317. m_rgnClipSave = CreateRectRgn(0,0,0,0);
  318. if (m_rgnClipSave != NULL) {
  319. if (GetClipRgn(hDC, m_rgnClipSave) != 1) {
  320. DeleteObject(m_rgnClipSave);
  321. m_rgnClipSave = NULL;
  322. }
  323. }
  324. xLeft += m_rectPlot.left;
  325. xRight += m_rectPlot.left;
  326. IntersectClipRect (
  327. hDC,
  328. max ( m_rectPlot.left, xLeft ),
  329. m_rectPlot.top,
  330. min (m_rectPlot.right, xRight + 1), // Extra pixel for TimeBar
  331. m_rectPlot.bottom ) ;
  332. } else if( TRUE == fEntire ){
  333. m_rgnClipSave = NULL;
  334. IntersectClipRect (
  335. hDC,
  336. m_rectPlot.left,
  337. m_rectPlot.top,
  338. m_rectPlot.right,
  339. m_rectPlot.bottom ) ;
  340. }
  341. // Fill performed before this method for metafiles and complete draw.
  342. if ( !fMetafile && bFill ) {
  343. SetRect(
  344. &rect,
  345. max ( m_rectPlot.left, xLeft - 1 ),
  346. m_rectPlot.top - 1,
  347. min (m_rectPlot.right, xRight + 1),
  348. m_rectPlot.bottom);
  349. Fill(hDC, m_pCtrl->clrBackPlot(), &rect);
  350. }
  351. }
  352. void CGraphDisp::FinishUpdate( HDC hDC, BOOL fMetafile )
  353. {
  354. // Restore saved clip region
  355. if ( !fMetafile ) {
  356. if (m_rgnClipSave != NULL) {
  357. SelectClipRgn(hDC, m_rgnClipSave);
  358. DeleteObject(m_rgnClipSave);
  359. m_rgnClipSave = NULL;
  360. }
  361. }
  362. }
  363. void CGraphDisp::DrawGrid(HDC hDC, INT xLeft, INT xRight)
  364. {
  365. INT xPos;
  366. INT nTics;
  367. INT *piScaleTic;
  368. INT i;
  369. if ( (m_pGraph->Options.bVertGridChecked)
  370. || (m_pGraph->Options.bHorzGridChecked) ) {
  371. if ( m_clrCurrentGrid != m_pCtrl->clrGrid() ) {
  372. m_clrCurrentGrid = m_pCtrl->clrGrid();
  373. DeleteObject ( m_hPenGrid );
  374. m_hPenGrid = CreatePen(PS_SOLID, 1, m_clrCurrentGrid );
  375. // if can't do it, use a stock object (this can't fail)
  376. if (m_hPenGrid == NULL)
  377. m_hPenGrid = (HPEN)GetStockObject(BLACK_PEN);
  378. }
  379. }
  380. if (m_pGraph->Options.bVertGridChecked) {
  381. SelectObject(hDC, m_hPenGrid);
  382. m_GridStepper.Reset();
  383. xPos = m_GridStepper.NextPosition();
  384. while (xPos < xLeft)
  385. xPos =m_GridStepper.NextPosition();
  386. while (xPos < xRight) {
  387. MoveToEx(hDC, xPos + m_rectPlot.left, m_rectPlot.bottom, NULL);
  388. LineTo(hDC, xPos + m_rectPlot.left, m_rectPlot.top - 1);
  389. xPos = m_GridStepper.NextPosition();
  390. }
  391. }
  392. if (m_pGraph->Options.bHorzGridChecked) {
  393. xLeft += m_rectPlot.left;
  394. xRight += m_rectPlot.left;
  395. SelectObject(hDC,m_hPenGrid);
  396. nTics = m_pGraph->Scale.GetTicPositions(&piScaleTic);
  397. for (i=1; i<nTics; i++) {
  398. MoveToEx(hDC, xLeft, m_rectPlot.top + piScaleTic[i], NULL);
  399. LineTo(hDC, xRight + 1, m_rectPlot.top + piScaleTic[i]);
  400. }
  401. }
  402. }
  403. BOOL
  404. CGraphDisp::CalcYPosition (
  405. PCGraphItem pItem,
  406. INT iHistIndex,
  407. BOOL bLog,
  408. INT y[3] )
  409. {
  410. BOOL bReturn; // True = good, False = bad.
  411. PDH_STATUS stat;
  412. DWORD dwCtrStat;
  413. double dValue[3];
  414. double dTemp;
  415. INT iVal;
  416. INT nVals = bLog ? 3 : 1;
  417. if (bLog)
  418. stat = pItem->GetLogEntry(iHistIndex, &dValue[1], &dValue[2], &dValue[0], &dwCtrStat);
  419. else
  420. stat = pItem->HistoryValue(iHistIndex, &dValue[0], &dwCtrStat);
  421. if (ERROR_SUCCESS == stat && IsSuccessSeverity(dwCtrStat)) {
  422. for (iVal = 0; iVal < nVals; iVal++) {
  423. dTemp = dValue[iVal] * pItem->Scale();
  424. if (dTemp > m_dMax)
  425. dTemp = m_dMax;
  426. else if (dTemp < m_dMin)
  427. dTemp = m_dMin;
  428. // Plot minimum value as 1 pixel above the bottom of the plot area, since
  429. // clipping and fill regions crop the bottom and right pixels.
  430. y[iVal] = m_rectPlot.bottom - (INT)((dTemp - m_dMin) * m_dPixelScale);
  431. if ( y[iVal] == m_rectPlot.bottom ) {
  432. y[iVal] = m_rectPlot.bottom - 1;
  433. }
  434. }
  435. bReturn = TRUE;
  436. } else {
  437. bReturn = FALSE;
  438. }
  439. return bReturn;
  440. }
  441. void CGraphDisp::PlotData(HDC hDC, INT iHistIndx, INT nSteps, CStepper *pStepper)
  442. {
  443. INT i;
  444. INT x;
  445. INT y[3];
  446. PCGraphItem pItem;
  447. CStepper locStepper;
  448. BOOL bSkip;
  449. BOOL bPrevGood;
  450. BOOL bLog;
  451. BOOL bLogMultiVal;
  452. if (m_pGraph->Options.iVertMax <= m_pGraph->Options.iVertMin)
  453. return;
  454. bSkip = TRUE;
  455. bLog = m_pCtrl->IsLogSource();
  456. bLogMultiVal = bLog && !DisplaySingleLogSampleValue();
  457. // If possible, back up to redraw previous segment
  458. if (pStepper->StepNum() > 0) {
  459. iHistIndx++;
  460. nSteps++;
  461. pStepper->PrevPosition();
  462. }
  463. // Set background color, in case of dashed lines
  464. SetBkMode(hDC, TRANSPARENT);
  465. pItem = m_pCtrl->FirstCounter();
  466. while (pItem != NULL) {
  467. locStepper = *pStepper;
  468. // Skip hilited item the first time
  469. if (!(pItem == m_pHiliteItem && bSkip)) {
  470. INT iPolyIndex = 0;
  471. POINT arrptDataPoints[MAX_GRAPH_SAMPLES] ;
  472. if ( pItem == m_pHiliteItem) {
  473. // Arbitrary 450 (out of 510) chosen as cutoff for white vs. black
  474. if ( 450 > RGBToLightness( m_pCtrl->clrBackPlot() ) )
  475. SelectObject(hDC, hPenWhite);
  476. else
  477. SelectObject(hDC, hPenBlack);
  478. } else {
  479. SelectObject(hDC,pItem->Pen());
  480. }
  481. bPrevGood = FALSE;
  482. // For each GOOD current value:
  483. // If the previous value is good, draw line from previous value to current value.
  484. // If the previous value is bad, MoveTo the current value point.
  485. //
  486. // For the first step, the previous value is false by definition, so the first operation
  487. // is a MoveTo.
  488. //
  489. // Polyline code:
  490. // For each GOOD current value:
  491. // Add the current (good) point to the polyline point array.
  492. // For each BAD current value:
  493. // If the polyline index is > 1 (2 points), draw the polyline and reset the polyline index to 0.
  494. // After all values:
  495. // If the polyline index is > 1 (2 points), draw the polyline.
  496. for (i = 0; i <= nSteps; i++) {
  497. // True = Good current value
  498. if ( CalcYPosition ( pItem, iHistIndx - i, bLog, y ) ) {
  499. x = m_rectPlot.left + locStepper.Position();
  500. // Add point to polyline, since the current value is good.
  501. arrptDataPoints[iPolyIndex].x = x;
  502. arrptDataPoints[iPolyIndex].y = y[0];
  503. iPolyIndex++;
  504. // No polyline optimization for extra Max and Min log points.
  505. if (bLogMultiVal) {
  506. MoveToEx(hDC, x, y[1], NULL);
  507. LineTo(hDC, x, y[2]);
  508. MoveToEx(hDC, x, y[0], NULL);
  509. }
  510. bPrevGood = TRUE;
  511. } else {
  512. // Current value is not good.
  513. bPrevGood = FALSE;
  514. // Current value is not good, so don't add to polyline point array.
  515. if ( iPolyIndex > 1 ) {
  516. // Draw polyline for any existing good points.
  517. Polyline(hDC, arrptDataPoints, iPolyIndex) ;
  518. }
  519. // Reset polyline point index to 0.
  520. iPolyIndex = 0;
  521. }
  522. locStepper.NextPosition();
  523. }
  524. // Draw the final line.
  525. if ( iPolyIndex > 1 ) {
  526. // Draw polyline
  527. Polyline(hDC, arrptDataPoints, iPolyIndex) ;
  528. }
  529. // Exit loop after plotting hilited item
  530. if (pItem == m_pHiliteItem)
  531. break;
  532. }
  533. pItem = pItem->Next();
  534. // After last item, go back to highlighted item
  535. if (pItem == NULL) {
  536. pItem = m_pHiliteItem;
  537. bSkip = FALSE;
  538. }
  539. }
  540. }
  541. void CGraphDisp::PlotBarGraph(HDC hDC, BOOL fUpdate)
  542. {
  543. if ( (m_pGraph->CounterTree.NumCounters() > 0 )
  544. && (m_pGraph->Options.iVertMax > m_pGraph->Options.iVertMin) ) {
  545. CStepper BarStepper;
  546. PCGraphItem pItem;
  547. RECT rectBar;
  548. INT iValue,iPrevValue;
  549. HRESULT hr;
  550. LONG lCtrStat;
  551. double dValue = 0.0;
  552. double dMax;
  553. double dMin;
  554. double dAvg;
  555. double dTemp;
  556. HRGN hrgnRedraw,hrgnTemp;
  557. eReportValueTypeConstant eValueType;
  558. BOOL bLog;
  559. INT iNumCounters = m_pGraph->CounterTree.NumCounters();
  560. BOOL bSkip = TRUE;
  561. INT iHighlightStepNum = 0;
  562. BOOL bLocalUpdate;
  563. HANDLE hPenSave;
  564. bLocalUpdate = fUpdate;
  565. hrgnRedraw = NULL;
  566. eValueType = m_pCtrl->ReportValueType();
  567. // Todo: Move DisplaySingleLogSampleValue() to CSystemMonitor.
  568. bLog = m_pCtrl->IsLogSource();
  569. // Force total redraw if the number of counters has changed in case
  570. // Update is called immediately after.
  571. if ( m_bBarConfigChanged ) {
  572. SetBarConfigChanged ( FALSE );
  573. if ( bLocalUpdate ) {
  574. bLocalUpdate = FALSE;
  575. }
  576. // Clear and fill the entire plot region.
  577. hrgnRedraw = CreateRectRgn(
  578. m_rectPlot.left,
  579. m_rectPlot.top,
  580. m_rectPlot.right,
  581. m_rectPlot.bottom);
  582. if (hrgnRedraw) {
  583. SelectClipRgn(hDC, hrgnRedraw);
  584. Fill(hDC, m_pCtrl->clrBackPlot(), &m_rectPlot);
  585. DrawGrid(hDC, 0, (m_rectPlot.right - m_rectPlot.left));
  586. DeleteObject(hrgnRedraw);
  587. hrgnRedraw = NULL;
  588. }
  589. }
  590. // Intialize stepper for number of bars to plot
  591. BarStepper.Init ( ( m_rectPlot.right - m_rectPlot.left), iNumCounters );
  592. hPenSave = SelectPen ( hDC, GetStockObject(NULL_PEN) );
  593. // Do for all counters
  594. pItem = m_pGraph->CounterTree.FirstCounter();
  595. while ( NULL != pItem ) {
  596. hr = ERROR_SUCCESS;
  597. // Skip highlighted item the first time through
  598. if (!(pItem == m_pHiliteItem && bSkip)) {
  599. // Get display value
  600. if ( sysmonDefaultValue == eValueType ) {
  601. if (bLog) {
  602. hr = pItem->GetStatistics(&dMax, &dMin, &dAvg, &lCtrStat);
  603. } else
  604. hr = pItem->GetValue(&dValue, &lCtrStat);
  605. } else {
  606. if ( sysmonCurrentValue == eValueType ) {
  607. hr = pItem->GetValue(&dValue, &lCtrStat);
  608. } else {
  609. double dAvg;
  610. hr = pItem->GetStatistics(&dMax, &dMin, &dAvg, &lCtrStat);
  611. switch ( eValueType ) {
  612. case sysmonAverage:
  613. dValue = dAvg;
  614. break;
  615. case sysmonMinimum:
  616. dValue = dMin;
  617. break;
  618. case sysmonMaximum:
  619. dValue = dMax;
  620. break;
  621. default:
  622. assert (FALSE);
  623. }
  624. }
  625. }
  626. // Erase bar if the counter value is invalid.
  627. if (SUCCEEDED(hr) && IsSuccessSeverity(lCtrStat)) {
  628. // Convert value to pixel units
  629. dTemp = dValue * pItem->Scale();
  630. if (dTemp > m_dMax)
  631. dTemp = m_dMax;
  632. else if (dTemp < m_dMin)
  633. dTemp = m_dMin;
  634. iValue = m_rectPlot.bottom - (INT)((dTemp - m_dMin) * m_dPixelScale);
  635. if ( iValue == m_rectPlot.bottom ) {
  636. // Draw single pixel for screen visibility.
  637. iValue--;
  638. }
  639. } else {
  640. // The current value is 0. Draw single pixel for screen visibility.
  641. iValue = m_rectPlot.bottom - 1;
  642. }
  643. if ( !bSkip ) {
  644. assert ( pItem == m_pHiliteItem );
  645. BarStepper.StepTo ( iHighlightStepNum );
  646. }
  647. // Setup left and right edges of bar
  648. rectBar.left = m_rectPlot.left + BarStepper.Position();
  649. rectBar.right = m_rectPlot.left + BarStepper.NextPosition();
  650. // If doing an update (never called for log sources) and not drawing the highlighted item
  651. if ( bLocalUpdate && !( ( pItem == m_pHiliteItem ) && !bSkip) ) {
  652. assert ( !m_bBarConfigChanged );
  653. // Get previous plot value
  654. iPrevValue = 0;
  655. hr = pItem->HistoryValue(1, &dValue, (ULONG*)&lCtrStat);
  656. if (SUCCEEDED(hr) && IsSuccessSeverity(lCtrStat)) {
  657. // Convert value to pixel units
  658. dTemp = dValue * pItem->Scale();
  659. if (dTemp > m_dMax)
  660. dTemp = m_dMax;
  661. else if (dTemp < m_dMin)
  662. dTemp = m_dMin;
  663. iPrevValue = m_rectPlot.bottom - (INT)((dTemp - m_dMin) * m_dPixelScale);
  664. if ( iPrevValue == m_rectPlot.bottom ) {
  665. // Single pixel was drawn for screen visibility.
  666. iPrevValue--;
  667. }
  668. } else {
  669. // The previous value was 0. Single pixel was drawn for screen visibility.
  670. iPrevValue = m_rectPlot.bottom - 1;
  671. }
  672. // If bar has grown (smaller y coord)
  673. if (iPrevValue > iValue) {
  674. // Draw the new part
  675. rectBar.bottom = iPrevValue;
  676. rectBar.top = iValue;
  677. if ( pItem == m_pHiliteItem) {
  678. // Arbitrary 450 (out of 510) chosen as cutoff for white vs. black
  679. if ( 450 > RGBToLightness( m_pCtrl->clrBackPlot() ) )
  680. SelectBrush(hDC, GetStockObject(WHITE_BRUSH));
  681. else
  682. SelectBrush(hDC, GetStockObject(BLACK_BRUSH));
  683. } else {
  684. SelectBrush(hDC, pItem->Brush());
  685. }
  686. // Bars are drawn with Null pen, so bottom and right are cropped by 1 pixel.
  687. // Add 1 pixel to compensate.
  688. Rectangle(hDC, rectBar.left, rectBar.top, rectBar.right + 1, rectBar.bottom + 1);
  689. } else if (iPrevValue < iValue) {
  690. // Else if bar has shrunk
  691. // Add part to be erased to redraw region
  692. // Erase to the top of the grid, to eliminate random pixels left over.
  693. rectBar.bottom = iValue;
  694. rectBar.top = m_rectPlot.top; // set to stop of grid rather than to prevValue
  695. hrgnTemp = CreateRectRgn(rectBar.left, rectBar.top, rectBar.right, rectBar.bottom);
  696. if (hrgnRedraw && hrgnTemp) {
  697. CombineRgn(hrgnRedraw,hrgnRedraw,hrgnTemp,RGN_OR);
  698. DeleteObject(hrgnTemp);
  699. } else {
  700. hrgnRedraw = hrgnTemp;
  701. }
  702. }
  703. } else {
  704. // Erase and redraw complete bar
  705. // Erase top first
  706. // Add part to be erased to redraw region
  707. // Erase to the top of the grid, to eliminate random pixels left over.
  708. if ( iValue != m_rectPlot.top ) {
  709. rectBar.bottom = iValue;
  710. rectBar.top = m_rectPlot.top; // set to stop of grid rather than to prevValue
  711. hrgnTemp = CreateRectRgn(rectBar.left, rectBar.top, rectBar.right, rectBar.bottom);
  712. if (hrgnRedraw && hrgnTemp) {
  713. CombineRgn(hrgnRedraw,hrgnRedraw,hrgnTemp,RGN_OR);
  714. DeleteObject(hrgnTemp);
  715. } else {
  716. hrgnRedraw = hrgnTemp;
  717. }
  718. }
  719. // Then draw the bar.
  720. rectBar.bottom = m_rectPlot.bottom;
  721. rectBar.top = iValue;
  722. if ( pItem == m_pHiliteItem) {
  723. // Arbitrary 450 (out of 510) chosen as cutoff for white vs. black
  724. if ( 450 > RGBToLightness( m_pCtrl->clrBackPlot() ) )
  725. SelectBrush(hDC, GetStockObject(WHITE_BRUSH));
  726. else
  727. SelectBrush(hDC, GetStockObject(BLACK_BRUSH));
  728. } else {
  729. SelectBrush(hDC, pItem->Brush());
  730. }
  731. // Bars are drawn with Null pen, so bottom and right are cropped by 1 pixel.
  732. // Add 1 pixel to compensate.
  733. Rectangle(hDC, rectBar.left, rectBar.top, rectBar.right + 1, rectBar.bottom + 1);
  734. } // Update
  735. // Exit loop after plotting highlighted item
  736. if (pItem == m_pHiliteItem)
  737. break;
  738. } else {
  739. if ( bSkip ) {
  740. // Save position of highlighted item the first time through
  741. iHighlightStepNum = BarStepper.StepNum();
  742. }
  743. BarStepper.NextPosition();
  744. }
  745. pItem = pItem->Next();
  746. // After last item, go back to highlighted item
  747. if ( NULL == pItem && NULL != m_pHiliteItem ) {
  748. pItem = m_pHiliteItem;
  749. bSkip = FALSE;
  750. }
  751. } // Do for all counters
  752. // If redraw region accumulated, erase and draw grid lines
  753. if (hrgnRedraw) {
  754. SelectClipRgn(hDC, hrgnRedraw);
  755. Fill(hDC, m_pCtrl->clrBackPlot(), &m_rectPlot);
  756. DrawGrid(hDC, 0, (m_rectPlot.right - m_rectPlot.left));
  757. DeleteObject(hrgnRedraw);
  758. }
  759. SelectObject(hDC, hPenSave);
  760. }
  761. }
  762. void CGraphDisp::SizeComponents(HDC hDC, PRECT pRect)
  763. {
  764. INT iStepNum;
  765. INT iScaleWidth;
  766. INT iTitleHeight;
  767. INT iAxisTitleWidth;
  768. RECT rectScale;
  769. SIZE size;
  770. INT iWidth;
  771. INT i;
  772. static INT aiWidthTable[] = {20,50,100,150,300,500,1000000};
  773. static INT aiTicTable[] = {0,2,4,5,10,20,25};
  774. m_rect = *pRect;
  775. // if no space, return
  776. if (m_rect.right <= m_rect.left || m_rect.bottom - m_rect.top <= 0)
  777. return;
  778. // For now use the horizontal font height for both horizontal and vertical text
  779. // because the GetTextExtentPoint32 is returning the wrong height for vertical text
  780. SelectFont(hDC, m_pCtrl->Font());
  781. GetTextExtentPoint32(hDC, TEXT("Sample"), 6, &size);
  782. if (m_pGraph->Options.pszGraphTitle != NULL) {
  783. //SelectFont(hDC, m_pCtrl->Font()) ;
  784. //GetTextExtentPoint32(hDC, m_pGraph->Options.pszGraphTitle,
  785. // lstrlen(m_pGraph->Options.pszGraphTitle), &size);
  786. iTitleHeight = size.cy + TEXT_MARGIN;
  787. } else
  788. iTitleHeight = 0;
  789. if (m_pGraph->Options.pszYaxisTitle != NULL && m_hFontVertical != NULL) {
  790. //SelectFont(hDC, m_hFontVertical);
  791. //GetTextExtentPoint32(hDC, m_pGraph->Options.pszYaxisTitle,
  792. // lstrlen(m_pGraph->Options.pszYaxisTitle), &size);
  793. iAxisTitleWidth = size.cy + TEXT_MARGIN;
  794. } else
  795. iAxisTitleWidth = 0;
  796. if (m_pGraph->Options.bLabelsChecked) {
  797. //SelectFont(hDC, m_pCtrl->Font());
  798. iScaleWidth = m_pGraph->Scale.GetWidth(hDC);
  799. } else
  800. iScaleWidth = 0;
  801. SetRect(&rectScale, pRect->left + iAxisTitleWidth,
  802. pRect->top + iTitleHeight,
  803. pRect->left + iAxisTitleWidth + iScaleWidth,
  804. pRect->bottom);
  805. m_pGraph->Scale.SetRect(&rectScale); // Just to set grid line positions
  806. SetRect(&m_rectPlot, pRect->left + iScaleWidth + iAxisTitleWidth + BORDER,
  807. pRect->top + iTitleHeight + BORDER,
  808. pRect->right - BORDER,
  809. pRect->bottom - BORDER);
  810. // Reinitialize steppers for new width
  811. iWidth = m_rectPlot.right - m_rectPlot.left;
  812. iStepNum = m_pGraph->TimeStepper.StepNum();
  813. m_pGraph->TimeStepper.Init(iWidth, m_pGraph->History.nMaxSamples - 2);
  814. m_pGraph->TimeStepper.StepTo(iStepNum);
  815. iStepNum = m_pGraph->LogViewStartStepper.StepNum();
  816. m_pGraph->LogViewStartStepper.Init(iWidth, m_pGraph->History.nMaxSamples - 2);
  817. m_pGraph->LogViewStartStepper.StepTo(iStepNum);
  818. iStepNum = m_pGraph->LogViewStopStepper.StepNum();
  819. m_pGraph->LogViewStopStepper.Init(iWidth, m_pGraph->History.nMaxSamples - 2);
  820. m_pGraph->LogViewStopStepper.StepTo(iStepNum);
  821. // Find best grid count for this width
  822. for (i=0; iWidth > aiWidthTable[i]; i++) {};
  823. m_GridStepper.Init(iWidth, aiTicTable[i]);
  824. // Compute conversion factors for plot, hit test.
  825. m_dMin = (double)m_pGraph->Options.iVertMin;
  826. m_dMax = (double)m_pGraph->Options.iVertMax;
  827. m_dPixelScale = (double)(m_rectPlot.bottom - m_rectPlot.top) / (m_dMax - m_dMin);
  828. }
  829. void CGraphDisp::DrawTimeLine(HDC hDC, INT x)
  830. {
  831. HPEN hPenSave;
  832. // No time line for log playback
  833. if (m_pCtrl->IsLogSource())
  834. return;
  835. x += m_rectPlot.left + 1;
  836. if ( m_clrCurrentTimeBar != m_pCtrl->clrTimeBar() ) {
  837. LOGBRUSH lbrush;
  838. m_clrCurrentTimeBar = m_pCtrl->clrTimeBar();
  839. DeleteObject ( m_hPenTimeBar );
  840. // When called from Update(), DrawTimeLine is called after the clipping region
  841. // is deactivated. Create a geometric pen in order to specify flat end cap style.
  842. // This eliminates any extra pixels drawn at the end.
  843. lbrush.lbStyle = BS_SOLID;
  844. lbrush.lbColor = m_clrCurrentTimeBar;
  845. lbrush.lbHatch = 0;
  846. m_hPenTimeBar = ExtCreatePen (
  847. PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT, 2, &lbrush, 0, NULL );
  848. // if can't do it, use a stock object (this can't fail)
  849. if (m_hPenTimeBar == NULL)
  850. m_hPenTimeBar = (HPEN)GetStockObject(BLACK_PEN);
  851. }
  852. hPenSave = SelectPen ( hDC, m_hPenTimeBar );
  853. MoveToEx ( hDC, x, m_rectPlot.top, NULL );
  854. // Specify 1 less pixel. All fills and clip regions clip bottom and
  855. // right pixels, so match their behavior.
  856. LineTo ( hDC, x, m_rectPlot.bottom - 1 );
  857. SelectObject(hDC, hPenSave);
  858. }
  859. void CGraphDisp::DrawStartStopLine(HDC hDC, INT x)
  860. {
  861. HPEN hPenSave;
  862. // Log view start/stop lines only for log playback
  863. if (!m_pCtrl->IsLogSource())
  864. return;
  865. if ( x > 0 && x < ( m_rectPlot.right - m_rectPlot.left ) ) {
  866. x += m_rectPlot.left;
  867. if ( m_clrCurrentGrid != m_pCtrl->clrGrid() ) {
  868. m_clrCurrentGrid = m_pCtrl->clrGrid();
  869. DeleteObject ( m_hPenGrid );
  870. m_hPenGrid = CreatePen(PS_SOLID, 1, m_clrCurrentGrid );
  871. // if can't do it, use a stock object (this can't fail)
  872. if (m_hPenGrid == NULL)
  873. m_hPenGrid = (HPEN)GetStockObject(BLACK_PEN);
  874. }
  875. hPenSave = SelectPen(hDC, m_hPenGrid);
  876. MoveToEx(hDC, x, m_rectPlot.top, NULL);
  877. LineTo(hDC, x, m_rectPlot.bottom);
  878. SelectObject(hDC, hPenSave);
  879. }
  880. }
  881. void CGraphDisp::ChangeFont( HDC hDC )
  882. {
  883. TEXTMETRIC TextMetrics, newTextMetrics;
  884. LOGFONT LogFont;
  885. HFONT hFontOld;
  886. // Select the new font
  887. hFontOld = SelectFont(hDC, m_pCtrl->Font());
  888. // Get attributes
  889. GetTextMetrics(hDC, &TextMetrics);
  890. // Create LOGFONT for vertical font with same attributes
  891. LogFont.lfHeight = TextMetrics.tmHeight;
  892. LogFont.lfWidth = 0;
  893. LogFont.lfOrientation = LogFont.lfEscapement = 90*10;
  894. LogFont.lfWeight = TextMetrics.tmWeight;
  895. LogFont.lfStrikeOut = TextMetrics.tmStruckOut;
  896. LogFont.lfUnderline = TextMetrics.tmUnderlined;
  897. LogFont.lfItalic = TextMetrics.tmItalic;
  898. LogFont.lfCharSet = TextMetrics.tmCharSet;
  899. LogFont.lfPitchAndFamily = (BYTE)(TextMetrics.tmPitchAndFamily & 0xF0);
  900. GetTextFace(hDC, LF_FACESIZE, LogFont.lfFaceName);
  901. // Force a truetype font, because raster fonts can't rotate
  902. LogFont.lfOutPrecision = OUT_TT_ONLY_PRECIS;
  903. LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  904. LogFont.lfQuality = DEFAULT_QUALITY;
  905. // Release the current font
  906. if (m_hFontVertical != NULL)
  907. DeleteObject(m_hFontVertical);
  908. // Create the font and save handle locally
  909. m_hFontVertical = CreateFontIndirect(&LogFont);
  910. SelectFont(hDC, m_hFontVertical);
  911. GetTextMetrics(hDC, &newTextMetrics);
  912. SelectFont(hDC, hFontOld);
  913. }
  914. PCGraphItem
  915. CGraphDisp::GetItemInLineGraph ( SHORT xPos, SHORT yPos )
  916. {
  917. PCGraphItem pItem = NULL;
  918. PCGraphItem pReturn = NULL;
  919. INT iPrevStepNum;
  920. POINT ptPrev;
  921. POINT ptNext;
  922. POINTS ptMouse;
  923. CStepper locStepper;
  924. INT iHistIndex;
  925. BOOL bLog;
  926. BOOL bLogMultiVal;
  927. BOOL bFound = FALSE;
  928. INT yPosPrev[3];
  929. INT yPosNext[3];
  930. pItem = m_pCtrl->FirstCounter();
  931. bLog = m_pCtrl->IsLogSource();
  932. bLogMultiVal = bLog && !DisplaySingleLogSampleValue();
  933. // Items exist?
  934. if (pItem != NULL) {
  935. locStepper = m_pGraph->TimeStepper;
  936. locStepper.Reset();
  937. iPrevStepNum = locStepper.PrevStepNum(xPos - m_rectPlot.left);
  938. locStepper.StepTo(iPrevStepNum);
  939. ptPrev.x = m_rectPlot.left + locStepper.Position();
  940. ptNext.x = m_rectPlot.left + locStepper.NextPosition();
  941. ptMouse.x = xPos;
  942. ptMouse.y = yPos;
  943. // Item within rectangle?
  944. if ( iPrevStepNum > -1 ) {
  945. // Determine the history index of the preceding step.
  946. if ( iPrevStepNum <= m_pGraph->TimeStepper.StepNum() ) {
  947. iHistIndex = m_pGraph->TimeStepper.StepNum() - iPrevStepNum;
  948. } else {
  949. iHistIndex = m_pGraph->TimeStepper.StepNum()
  950. + (m_pGraph->TimeStepper.StepCount() - iPrevStepNum);
  951. }
  952. while ( (pItem != NULL) && !bFound ) {
  953. // Calculate y position of this value to compare against
  954. // y position of hit point.
  955. if ( CalcYPosition ( pItem, iHistIndex, bLog, yPosPrev ) ) {
  956. if ( iPrevStepNum < locStepper.StepCount() ) {
  957. if ( CalcYPosition ( pItem, iHistIndex - 1, bLog, yPosNext ) ) {
  958. ptPrev.y = yPosPrev[0];
  959. ptNext.y = yPosNext[0];
  960. bFound = HitTestLine( ptPrev, ptNext, ptMouse, eHitRegion );
  961. // For log files, also check the vertical line from min to max
  962. // for the closest step.
  963. if ( !bFound && bLogMultiVal ) {
  964. INT iTemp = ptNext.x - ptPrev.x;
  965. iTemp = iTemp / 2;
  966. if ( ptMouse.x <= ( ptPrev.x + iTemp/2 ) ) {
  967. bFound = (( yPosPrev[2] - eHitRegion < yPos )
  968. && ( yPos < yPosPrev[1] + eHitRegion ));
  969. } else {
  970. bFound = (( yPosNext[2] - eHitRegion < yPos )
  971. && ( yPos < yPosNext[1] + eHitRegion ));
  972. }
  973. }
  974. }
  975. } else {
  976. // At the end, so just check the final point.
  977. if ( !bLogMultiVal ) {
  978. bFound = (( yPosPrev[0] - eHitRegion < yPos )
  979. && ( yPos < yPosPrev[0] + eHitRegion ));
  980. } else {
  981. bFound = (( yPosPrev[2] - eHitRegion < yPos )
  982. && ( yPos < yPosPrev[1] + eHitRegion ));
  983. }
  984. }
  985. }
  986. if ( bFound )
  987. pReturn = pItem;
  988. else
  989. pItem = pItem->Next();
  990. }
  991. }
  992. }
  993. return pReturn;
  994. }
  995. PCGraphItem
  996. CGraphDisp::GetItemInBarGraph ( SHORT xPos, SHORT /* yPos */ )
  997. {
  998. PCGraphItem pItem = NULL;
  999. pItem = m_pCtrl->FirstCounter();
  1000. // Items exist?
  1001. if (pItem != NULL) {
  1002. CStepper BarStepper;
  1003. INT iNumCounters = m_pGraph->CounterTree.NumCounters();
  1004. INT iCount;
  1005. INT iHitStep;
  1006. // Intialize stepper for number of bars in plot
  1007. BarStepper.Init ( ( m_rectPlot.right - m_rectPlot.left), iNumCounters );
  1008. iHitStep = BarStepper.PrevStepNum ( xPos - m_rectPlot.left );
  1009. assert ( -1 != iHitStep );
  1010. // Find the counter displayed in the hit step.
  1011. for ( iCount = 0;
  1012. ( iCount < iHitStep ) && ( pItem != NULL );
  1013. iCount++ ) {
  1014. pItem = pItem->Next();
  1015. }
  1016. }
  1017. return pItem;
  1018. }
  1019. PCGraphItem
  1020. CGraphDisp::GetItem( INT xPos, INT yPos )
  1021. {
  1022. PCGraphItem pReturn = NULL;
  1023. if ( ( m_pGraph->Options.iVertMax > m_pGraph->Options.iVertMin)
  1024. && ( yPos >= m_rectPlot.top ) && ( yPos <= m_rectPlot.bottom )
  1025. && ( xPos >= m_rectPlot.left ) && ( xPos <= m_rectPlot.right ) ) {
  1026. m_pCtrl->LockCounterData();
  1027. if ( LINE_GRAPH == m_pGraph->Options.iDisplayType ) {
  1028. assert ( SHRT_MAX >= xPos );
  1029. assert ( SHRT_MAX >= yPos );
  1030. pReturn = GetItemInLineGraph( (SHORT)xPos, (SHORT)yPos );
  1031. } else if ( BAR_GRAPH == m_pGraph->Options.iDisplayType ) {
  1032. assert ( SHRT_MAX >= xPos );
  1033. assert ( SHRT_MAX >= yPos );
  1034. pReturn = GetItemInBarGraph( (SHORT)xPos, (SHORT)yPos );
  1035. }
  1036. m_pCtrl->UnlockCounterData();
  1037. }
  1038. return pReturn;
  1039. }