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.

1342 lines
37 KiB

  1. /*
  2. ==============================================================================
  3. Application:
  4. Microsoft Windows NT (TM) Performance Monitor
  5. File:
  6. legend.c - legend window routines.
  7. This file contains code creating the legend window, which is
  8. a child of the graph windows. The legend window displays a
  9. legend line for each line in the associated graph. It also
  10. includes an area called the label, which are headers for those
  11. lines.
  12. Copyright 1992, Microsoft Corporation. All Rights Reserved.
  13. ==============================================================================
  14. */
  15. //==========================================================================//
  16. // Includes //
  17. //==========================================================================//
  18. #include <stdio.h> // for sprintf
  19. #include "perfmon.h"
  20. #include "legend.h" // external declarations for this file
  21. #include "owndraw.h"
  22. #include "alert.h" // for EditAlert
  23. #include "grafdata.h" // for EditChart
  24. #include "pmemory.h" // for MemoryXXX (mallloc-type) routines
  25. #include "utils.h"
  26. #include "valuebar.h" // for StatusTimer
  27. #include "playback.h" // for PlayingBackLog()
  28. #include "perfmops.h" // for COnvertDecimalPoint
  29. #define eScaleValueSpace TEXT(">9999999999.0")
  30. //==========================================================================//
  31. // Constants //
  32. //==========================================================================//
  33. #define dwGraphLegendClassStyle (CS_HREDRAW | CS_VREDRAW)
  34. #define iGraphLegendClassExtra (0)
  35. #define iGraphLegendWindowExtra (sizeof (PLEGEND))
  36. #define dwGraphLegendWindowStyle (WS_CHILD | WS_VISIBLE)
  37. #define xLegendBorderWidth (xDlgBorderWidth)
  38. #define yLegendBorderHeight (yDlgBorderHeight)
  39. #define iLabelLen 30
  40. #define iLegendColMax 1000
  41. #define LEFTORIENTATION 1
  42. #define CENTERORIENTATION 2
  43. #define RIGHTORIENTATION 3
  44. #define LegendColorCol 0
  45. #define LegendScaleCol 1
  46. #define LegendCounterCol 2
  47. #define LegendInstanceCol 3
  48. #define LegendParentCol 4
  49. #define LegendObjectCol 5
  50. #define LegendSystemCol 6
  51. #define iLegendNumCols 7
  52. #define iLegendMaxVisibleItems 8
  53. #define dwGraphLegendItemsWindowClass TEXT("ListBox")
  54. #define dwGraphLegendItemsWindowStyle \
  55. (LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | LBS_OWNERDRAWFIXED | \
  56. WS_VISIBLE | WS_CHILD | WS_VSCROLL)
  57. //==========================================================================//
  58. // Typedefs //
  59. //==========================================================================//
  60. typedef struct LEGENDCOLSTRUCT {
  61. TCHAR szLabel [iLabelLen] ;
  62. int xMinWidth ;
  63. int xMaxWidth ;
  64. int xWidth ;
  65. int xPos ;
  66. int iOrientation ;
  67. } LEGENDCOL ;
  68. typedef LEGENDCOL *PLEGENDCOL ;
  69. typedef struct LEGENDTRUCT {
  70. HWND hWndItems ;
  71. HFONT hFontItems ;
  72. HFONT hFontLabels ;
  73. int xMinWidth ;
  74. int yLabelHeight ;
  75. int yItemHeight ;
  76. int iNumItemsVisible ;
  77. LEGENDCOL aCols [iLegendNumCols] ;
  78. int iLineType ;
  79. PLINE pCurrentLine ; // current selected line
  80. } LEGEND ;
  81. typedef LEGEND *PLEGEND ;
  82. //==========================================================================//
  83. // Macros //
  84. //==========================================================================//
  85. #define LabelTopMargin() (2)
  86. #define LegendLeftMargin() (3 + ThreeDPad)
  87. #define LegendItemTopMargin() (yBorderHeight)
  88. #define IsLegendLabelVisible() (TRUE)
  89. #define ColCanGrow(pCol) \
  90. (pCol->xMaxWidth > pCol->xWidth)
  91. #define LegendHorzMargin() (3)
  92. //==========================================================================//
  93. // Local Data //
  94. //==========================================================================//
  95. PLEGEND pGraphLegendData ;
  96. PLEGEND pAlertLegendData ;
  97. #define LegendData(hWnd) \
  98. ((hWnd == hWndGraphLegend) ? pGraphLegendData : pAlertLegendData )
  99. //==========================================================================//
  100. // Local Functions //
  101. //==========================================================================//
  102. void
  103. LegendSetCurrentLine (
  104. PLEGEND pLegend,
  105. int iIndex
  106. )
  107. {
  108. pLegend->pCurrentLine =
  109. (PLINESTRUCT) LBData (pLegend->hWndItems, iIndex) ;
  110. if (pLegend->pCurrentLine == (PLINESTRUCT) LB_ERR) {
  111. pLegend->pCurrentLine = NULL ;
  112. }
  113. }
  114. PLEGEND
  115. AllocateLegendData (
  116. HWND hWnd,
  117. BOOL bChartLegend
  118. )
  119. {
  120. PLEGEND pLegend ;
  121. pLegend = MemoryAllocate (sizeof (LEGEND)) ;
  122. if (bChartLegend) {
  123. hWndGraphLegend = hWnd ;
  124. pGraphLegendData = pLegend ;
  125. } else {
  126. hWndAlertLegend = hWnd ;
  127. pAlertLegendData = pLegend ;
  128. }
  129. return (pLegend) ;
  130. }
  131. void
  132. DrawLegendLabel (
  133. PLEGEND pLegend,
  134. HDC hDC,
  135. HWND hWnd
  136. )
  137. {
  138. int i ;
  139. int xPos ;
  140. RECT rect ;
  141. RECT rectLB ;
  142. INT LocalThreeDPad = ThreeDPad - 1 ;
  143. // SetBkColor (hDC, crLightGray) ;
  144. SetBkColor (hDC, ColorBtnFace) ;
  145. SelectFont (hDC, pLegend->hFontLabels) ;
  146. GetClientRect (hWnd, &rect) ;
  147. // Fill (hDC, crLightGray, &rect) ;
  148. ExtTextOut (hDC, rect.left, rect.top,
  149. ETO_OPAQUE, &(rect), NULL, 0, NULL ) ;
  150. GetWindowRect (pLegend->hWndItems, &rectLB) ;
  151. ScreenRectToClient (hWnd, &rectLB) ;
  152. ThreeDConcave1 (hDC,
  153. rectLB.left - LocalThreeDPad,
  154. rectLB.top - LocalThreeDPad,
  155. rectLB.right + LocalThreeDPad,
  156. rectLB.bottom + LocalThreeDPad) ;
  157. for (i = 0; i < iLegendNumCols; i++) {
  158. rect.left = pLegend->aCols[i].xPos ;
  159. rect.top = yBorderHeight ;
  160. rect.right = rect.left + pLegend->aCols[i].xWidth - LegendHorzMargin () ;
  161. rect.bottom = pLegend->yLabelHeight ;
  162. switch (pLegend->aCols[i].iOrientation) {
  163. case LEFTORIENTATION:
  164. SetTextAlign (hDC, TA_LEFT) ;
  165. xPos = rect.left ;
  166. break ;
  167. case CENTERORIENTATION:
  168. SetTextAlign (hDC, TA_CENTER) ;
  169. xPos = (rect.left + rect.right) / 2 ;
  170. break ;
  171. case RIGHTORIENTATION:
  172. SetTextAlign (hDC, TA_RIGHT) ;
  173. xPos = rect.right ;
  174. break ;
  175. default:
  176. xPos = rect.left ;
  177. break ;
  178. }
  179. ExtTextOut (hDC,
  180. xPos, rect.top,
  181. ETO_OPAQUE,
  182. &rect,
  183. pLegend->aCols[i].szLabel,
  184. lstrlen (pLegend->aCols[i].szLabel),
  185. NULL) ;
  186. }
  187. }
  188. void
  189. DrawColorCol (
  190. PLEGEND pLegend,
  191. PLINE pLine,
  192. int iCol,
  193. HDC hDC,
  194. int yPos
  195. )
  196. /*
  197. Effect: Draw the "color" column of a legend entry. The color
  198. column displays a small sample of the line drawing.
  199. For charts, the sample is a line of the correct style,
  200. color, and width. Since we are using wide lines,
  201. the round endcaps of the lines will breach the column.
  202. Therefore we need to set the clip region. We could
  203. remove this clipping if we find a way to change the
  204. end cap design.
  205. For alerts, the sample is a small circle which echos
  206. the design in the alert log itself.
  207. */
  208. {
  209. HBRUSH hBrush, hBrushPrevious ;
  210. RECT rect ;
  211. int yMiddle ;
  212. int iCircle ;
  213. rect.left = pLegend->aCols[iCol].xPos - LegendLeftMargin () + 2 ;
  214. rect.top = yPos + 1 ;
  215. rect.right = rect.left + pLegend->aCols[iCol].xWidth - LegendHorzMargin () ;
  216. rect.bottom = yPos + pLegend->yItemHeight - 1 ;
  217. yMiddle = rect.top + (rect.bottom - rect.top) / 2 ;
  218. iCircle = rect.bottom - rect.top - 2 ;
  219. switch (pLegend->iLineType) {
  220. case LineTypeChart:
  221. if (pLine->Visual.iWidth == 1) {
  222. // simple case with thin pen
  223. // hBrush = SelectBrush (hDC, hbLightGray) ;
  224. hBrush = SelectBrush (hDC, hBrushFace) ;
  225. Rectangle (hDC, rect.left, rect.top, rect.right, rect.bottom) ;
  226. HLine (hDC, pLine->hPen,
  227. rect.left + 1, rect.right - 1, yMiddle) ;
  228. SelectBrush (hDC, hBrush) ;
  229. } else {
  230. // thicker pen width, have to set ClipRect so
  231. // it will not draw otherside the rect.
  232. SaveDC (hDC) ;
  233. // hBrush = SelectBrush (hDC, hbLightGray) ;
  234. hBrush = SelectBrush (hDC, hBrushFace) ;
  235. Rectangle (hDC, rect.left, rect.top, rect.right, rect.bottom) ;
  236. IntersectClipRect (hDC,
  237. rect.left + 1,
  238. rect.top + 1,
  239. rect.right - 1,
  240. rect.bottom - 1) ;
  241. HLine (hDC, pLine->hPen,
  242. rect.left + 1, rect.right - 1, yMiddle) ;
  243. SelectBrush (hDC, hBrush) ;
  244. RestoreDC (hDC, -1) ;
  245. }
  246. break ;
  247. case LineTypeAlert:
  248. hBrushPrevious = SelectBrush (hDC, pLine->hBrush) ;
  249. Ellipse (hDC,
  250. rect.left + 2,
  251. rect.top + 2,
  252. rect.left + 2 + iCircle,
  253. rect.top + 2 + iCircle) ;
  254. SelectBrush (hDC, hBrushPrevious) ;
  255. break ;
  256. }
  257. }
  258. void
  259. DrawLegendCol (
  260. PLEGEND pLegend,
  261. int iCol,
  262. HDC hDC,
  263. int yPos,
  264. LPTSTR lpszValue
  265. )
  266. /*
  267. Effect: Draw the value lpszValue for the column iCol on hDC.
  268. Assert: The foreground and background text colors of hDC are
  269. properly set.
  270. */
  271. {
  272. RECT rect ;
  273. int xPos ;
  274. rect.left = pLegend->aCols[iCol].xPos - LegendLeftMargin () ;
  275. rect.top = yPos ;
  276. rect.right = rect.left + pLegend->aCols[iCol].xWidth - LegendHorzMargin () ;
  277. rect.bottom = yPos + pLegend->yItemHeight ;
  278. // SetTextAlign (hDC, TA_TOP) ;
  279. switch (pLegend->aCols[iCol].iOrientation) {
  280. case LEFTORIENTATION:
  281. SetTextAlign (hDC, TA_LEFT) ;
  282. xPos = rect.left ;
  283. break ;
  284. case CENTERORIENTATION:
  285. SetTextAlign (hDC, TA_CENTER) ;
  286. xPos = (rect.left + rect.right) / 2 ;
  287. break ;
  288. case RIGHTORIENTATION:
  289. SetTextAlign (hDC, TA_RIGHT) ;
  290. xPos = rect.right ;
  291. break ;
  292. default:
  293. xPos = rect.left ;
  294. break ;
  295. }
  296. ExtTextOut (hDC,
  297. xPos, rect.top + LegendItemTopMargin (),
  298. ETO_OPAQUE | ETO_CLIPPED,
  299. &rect,
  300. lpszValue,
  301. lstrlen (lpszValue),
  302. NULL) ;
  303. }
  304. void
  305. DrawLegendItem (
  306. PLEGEND pLegend,
  307. PLINESTRUCT pLine,
  308. int yPos,
  309. HDC hDC
  310. )
  311. {
  312. TCHAR szValue [256] ;
  313. TCHAR szInstance [256] ;
  314. TCHAR szParent [256] ;
  315. TCHAR szNoName[4] ;
  316. szNoName[0] = szNoName[1] = szNoName[2] = TEXT('-') ;
  317. szNoName[3] = TEXT('\0') ;
  318. //=============================//
  319. // Determine Instance, Parent //
  320. //=============================//
  321. // It's possible that there will be no instance, therefore
  322. // the lnInstanceName would be NULL.
  323. if (pLine->lnObject.NumInstances > 0) {
  324. // Test for the parent object instance name title index.
  325. // If there is one, it implies that there will be a valid
  326. // Parent Object Name and a valid Parent Object Instance Name.
  327. // If the Parent Object title index is 0 then
  328. // just display the instance name.
  329. lstrcpy (szInstance, pLine->lnInstanceName) ;
  330. if (pLine->lnInstanceDef.ParentObjectTitleIndex &&
  331. pLine->lnPINName) {
  332. // Get the Parent Object Name.
  333. lstrcpy (szParent, pLine->lnPINName) ;
  334. } else {
  335. lstrcpy (szParent, szNoName) ;
  336. }
  337. } else {
  338. lstrcpy (szInstance, szNoName) ;
  339. lstrcpy (szParent, szNoName) ;
  340. }
  341. //=============================//
  342. // Draw Color //
  343. //=============================//
  344. DrawColorCol (pLegend, pLine, LegendColorCol, hDC, yPos) ;
  345. //=============================//
  346. // Draw Scale/Value //
  347. //=============================//
  348. if (pLegend->iLineType == LineTypeChart) {
  349. if (pLine->eScale < (FLOAT) 1.0) {
  350. TSPRINTF (szValue, TEXT("%7.7f"), pLine->eScale) ;
  351. } else {
  352. TSPRINTF (szValue, TEXT("%10.3f"), pLine->eScale) ;
  353. }
  354. ConvertDecimalPoint (szValue) ;
  355. } else {
  356. FLOAT tempAlertValue ;
  357. tempAlertValue = pLine->eAlertValue ;
  358. if (tempAlertValue < (FLOAT) 0.0) {
  359. tempAlertValue = -tempAlertValue ;
  360. }
  361. if (tempAlertValue >= (FLOAT) 10000.0) {
  362. if (tempAlertValue < (FLOAT) 1.0E+8) {
  363. TSPRINTF (szValue, TEXT("%c%10.0f"),
  364. pLine->bAlertOver ? TEXT('>') : TEXT('<'),
  365. pLine->eAlertValue) ;
  366. } else {
  367. TSPRINTF (szValue, TEXT("%c%10.3e"),
  368. pLine->bAlertOver ? TEXT('>') : TEXT('<'),
  369. pLine->eAlertValue) ;
  370. }
  371. ConvertDecimalPoint (szValue) ;
  372. } else {
  373. TSPRINTF (szValue, TEXT("%c%10.4f"),
  374. pLine->bAlertOver ? TEXT('>') : TEXT('<'),
  375. pLine->eAlertValue) ;
  376. ConvertDecimalPoint (szValue) ;
  377. }
  378. }
  379. SetTextAlign (hDC, TA_TOP) ;
  380. DrawLegendCol (pLegend, LegendScaleCol,
  381. hDC, yPos, szValue) ;
  382. //=============================//
  383. // Draw Counter //
  384. //=============================//
  385. DrawLegendCol (pLegend, LegendCounterCol,
  386. hDC, yPos, pLine->lnCounterName) ;
  387. //=============================//
  388. // Draw Instance //
  389. //=============================//
  390. DrawLegendCol (pLegend, LegendInstanceCol,
  391. hDC, yPos, szInstance) ;
  392. //=============================//
  393. // Draw Parent //
  394. //=============================//
  395. DrawLegendCol (pLegend, LegendParentCol,
  396. hDC, yPos, szParent) ;
  397. //=============================//
  398. // Draw Object //
  399. //=============================//
  400. DrawLegendCol (pLegend, LegendObjectCol,
  401. hDC, yPos, pLine->lnObjectName) ;
  402. //=============================//
  403. // Draw System //
  404. //=============================//
  405. DrawLegendCol (pLegend, LegendSystemCol,
  406. hDC, yPos, pLine->lnSystemName) ;
  407. }
  408. //==========================================================================//
  409. // Message Handlers //
  410. //==========================================================================//
  411. BOOL
  412. OnLegendCreate (
  413. HWND hWnd,
  414. LPCREATESTRUCT lpCS
  415. )
  416. {
  417. PLEGEND pLegend ;
  418. HDC hDC ;
  419. int iCol ;
  420. BOOL bChartLegend ;
  421. bChartLegend = (lpCS->lpCreateParams) == (LPVOID) TRUE ;
  422. pLegend = AllocateLegendData (hWnd, bChartLegend) ;
  423. if (!pLegend)
  424. return (FALSE) ;
  425. if (bChartLegend) {
  426. pLegend->iLineType = LineTypeChart ;
  427. } else {
  428. pLegend->iLineType = LineTypeAlert ;
  429. }
  430. pLegend->hFontItems = hFontScales ;
  431. pLegend->hFontLabels = hFontScalesBold ;
  432. hDC = GetDC (hWnd) ;
  433. if (!hDC)
  434. return (FALSE);
  435. //=============================//
  436. // Load Labels //
  437. //=============================//
  438. StringLoad (IDS_LABELCOLOR, pLegend->aCols[LegendColorCol].szLabel) ;
  439. if (pLegend->iLineType == LineTypeChart)
  440. StringLoad (IDS_LABELSCALE,
  441. pLegend->aCols[LegendScaleCol].szLabel) ;
  442. else
  443. StringLoad (IDS_LABELVALUE,
  444. pLegend->aCols[LegendScaleCol].szLabel) ;
  445. StringLoad (IDS_LABELCOUNTER, pLegend->aCols[LegendCounterCol].szLabel) ;
  446. StringLoad (IDS_LABELINSTANCE, pLegend->aCols[LegendInstanceCol].szLabel) ;
  447. StringLoad (IDS_LABELPARENT, pLegend->aCols[LegendParentCol].szLabel) ;
  448. StringLoad (IDS_LABELOBJECT, pLegend->aCols[LegendObjectCol].szLabel) ;
  449. StringLoad (IDS_LABELSYSTEM, pLegend->aCols[LegendSystemCol].szLabel) ;
  450. //=============================//
  451. // Label dimensions //
  452. //=============================//
  453. SelectFont (hDC, pLegend->hFontLabels) ;
  454. pLegend->yLabelHeight = FontHeight (hDC, TRUE) + 2 * LabelTopMargin () ;
  455. //=============================//
  456. // Column dimensions //
  457. //=============================//
  458. for (iCol = 0; iCol < iLegendNumCols; iCol++) {
  459. pLegend->aCols[iCol].iOrientation = LEFTORIENTATION ;
  460. pLegend->aCols[iCol].xMinWidth =
  461. TextWidth (hDC, pLegend->aCols[iCol].szLabel) + LegendHorzMargin () ;
  462. }
  463. SelectFont (hDC, pLegend->hFontItems) ;
  464. pLegend->yItemHeight = FontHeight (hDC, TRUE) + 2 * LegendItemTopMargin () ;
  465. pLegend->aCols[LegendColorCol].xMaxWidth = max (3 * xScrollWidth,
  466. pLegend->aCols[LegendColorCol].xMinWidth) ;
  467. pLegend->aCols[LegendScaleCol].xMaxWidth = TextWidth (hDC, eScaleValueSpace) ;
  468. pLegend->aCols[LegendCounterCol].xMaxWidth = iLegendColMax ;
  469. pLegend->aCols[LegendInstanceCol].xMaxWidth = iLegendColMax ;
  470. pLegend->aCols[LegendParentCol].xMaxWidth = iLegendColMax ;
  471. pLegend->aCols[LegendObjectCol].xMaxWidth = iLegendColMax ;
  472. pLegend->aCols[LegendSystemCol].xMaxWidth = iLegendColMax ;
  473. pLegend->aCols[LegendColorCol].iOrientation = LEFTORIENTATION ;
  474. pLegend->aCols[LegendScaleCol].iOrientation = RIGHTORIENTATION ;
  475. ReleaseDC (hWnd, hDC) ;
  476. //=============================//
  477. // Create Legend Items Listbox //
  478. //=============================//
  479. pLegend->hWndItems =
  480. CreateWindow (TEXT("ListBox"), // window class
  481. NULL, // window caption
  482. dwGraphLegendItemsWindowStyle, // window style
  483. 0, 0, 0, 0, // window size and pos
  484. hWnd, // parent window
  485. NULL, // menu
  486. hInstance, // program instance
  487. (LPVOID) TRUE) ; // user-supplied data
  488. return TRUE;
  489. }
  490. void
  491. static
  492. OnSize (
  493. HWND hWnd,
  494. int xWidth,
  495. int yHeight
  496. )
  497. /*
  498. Effect: Perform all actions necessary when the legend window is
  499. being resized. In particular, set the width and starting
  500. position for each column of the legend list and labels.
  501. Internals: What we do is determine the amount of space in the
  502. width that is greater than the minimum, and allocate
  503. that space among each of the columns that is willing to
  504. be larger than minimum. If there is not enough room for
  505. the minimum, don't bother with calculations.
  506. */
  507. {
  508. PLEGEND pLegend ;
  509. PLEGENDCOL pCol ;
  510. int xMin ;
  511. int xSlack ;
  512. int iColsToGrow ;
  513. int iCol ;
  514. pLegend = LegendData (hWnd) ;
  515. //=============================//
  516. // Resize Legend Items //
  517. //=============================//
  518. if (IsLegendLabelVisible ())
  519. MoveWindow (pLegend->hWndItems,
  520. LegendLeftMargin (), pLegend->yLabelHeight + ThreeDPad,
  521. xWidth - 2 * LegendLeftMargin (),
  522. yHeight - pLegend->yLabelHeight - yLegendBorderHeight,
  523. TRUE) ;
  524. else
  525. MoveWindow (pLegend->hWndItems,
  526. 0, 0,
  527. xWidth, yHeight,
  528. TRUE) ;
  529. //=============================//
  530. // Allocate width to Columns //
  531. //=============================//
  532. xMin = LegendMinWidth (hWnd) ;
  533. xSlack = max (xWidth - xMin, 0) ;
  534. iColsToGrow = 0 ;
  535. for (iCol = 0; iCol < iLegendNumCols; iCol++) {
  536. pCol = &(pLegend->aCols[iCol]) ;
  537. pCol->xWidth = pCol->xMinWidth ;
  538. if (ColCanGrow (pCol))
  539. iColsToGrow++ ;
  540. }
  541. for (iCol = 0; iCol < iLegendNumCols; iCol++) {
  542. pCol = &(pLegend->aCols[iCol]) ;
  543. if (ColCanGrow (pCol)) {
  544. if ((pCol->xWidth + xSlack / iColsToGrow) > pCol->xMaxWidth) {
  545. pCol->xWidth = pCol->xMaxWidth ;
  546. xSlack -= (pCol->xMaxWidth - pCol->xMinWidth) ;
  547. iColsToGrow-- ;
  548. }
  549. }
  550. }
  551. for (iCol = 0; iCol < iLegendNumCols; iCol++) {
  552. pCol = &(pLegend->aCols[iCol]) ;
  553. if (ColCanGrow (pCol))
  554. pCol->xWidth += xSlack / iColsToGrow ;
  555. }
  556. if (pLegend->aCols[LegendCounterCol].xWidth <
  557. pLegend->aCols[LegendCounterCol].xMaxWidth) {
  558. // cut some from the other columns and give them to CounterCol
  559. if (pLegend->aCols[LegendColorCol].xWidth - xScrollWidth >=
  560. pLegend->aCols[LegendColorCol].xMinWidth) {
  561. pLegend->aCols[LegendColorCol].xWidth -= xScrollWidth ;
  562. pLegend->aCols[LegendCounterCol].xWidth += xScrollWidth ;
  563. }
  564. if (pLegend->aCols[LegendInstanceCol].xWidth - xScrollWidth >=
  565. pLegend->aCols[LegendInstanceCol].xMinWidth) {
  566. pLegend->aCols[LegendInstanceCol].xWidth -= xScrollWidth ;
  567. pLegend->aCols[LegendCounterCol].xWidth += xScrollWidth ;
  568. }
  569. if (pLegend->aCols[LegendParentCol].xWidth - xScrollWidth >=
  570. pLegend->aCols[LegendParentCol].xMinWidth) {
  571. pLegend->aCols[LegendParentCol].xWidth -= xScrollWidth ;
  572. pLegend->aCols[LegendCounterCol].xWidth += xScrollWidth ;
  573. }
  574. }
  575. //=============================//
  576. // Set Column positions //
  577. //=============================//
  578. pLegend->aCols[0].xPos = LegendLeftMargin () ;
  579. for (iCol = 1; iCol < iLegendNumCols; iCol++) {
  580. pLegend->aCols[iCol].xPos =
  581. pLegend->aCols[iCol - 1].xPos + pLegend->aCols[iCol - 1].xWidth ;
  582. if (iCol == LegendCounterCol) {
  583. // add some space between Scale/Value col & Counter col
  584. pLegend->aCols[LegendCounterCol].xPos += LegendLeftMargin () ;
  585. }
  586. }
  587. }
  588. void
  589. static
  590. OnPaint (
  591. HWND hWnd
  592. )
  593. {
  594. PLEGEND pLegend ;
  595. HDC hDC ;
  596. PAINTSTRUCT ps ;
  597. hDC = BeginPaint (hWnd, &ps) ;
  598. if (IsLegendLabelVisible ()) {
  599. pLegend = LegendData (hWnd) ;
  600. DrawLegendLabel (pLegend, hDC, hWnd) ;
  601. }
  602. if (LBNumItems (pLegend->hWndItems) == 0) {
  603. WindowInvalidate(pLegend->hWndItems) ;
  604. }
  605. EndPaint (hWnd, &ps) ;
  606. }
  607. void
  608. OnDrawLegendItem (
  609. HWND hWnd,
  610. LPDRAWITEMSTRUCT lpDI
  611. )
  612. {
  613. HFONT hFontPrevious ;
  614. HDC hDC ;
  615. PLEGEND pLegend ;
  616. PLINESTRUCT pLine ;
  617. int iLBIndex ;
  618. COLORREF preBkColor = 0;
  619. COLORREF preTextColor = 0;
  620. BOOL ResetColor = FALSE ;
  621. hDC = lpDI->hDC ;
  622. iLBIndex = DIIndex (lpDI) ;
  623. pLegend = LegendData (hWnd) ;
  624. if (iLBIndex == -1)
  625. pLine = NULL ;
  626. else
  627. pLine = (PLINESTRUCT) LBData (pLegend->hWndItems, iLBIndex) ;
  628. //=============================//
  629. // Set DC attributes //
  630. //=============================//
  631. if (DISelected (lpDI) || pLine == NULL) {
  632. preTextColor = SetTextColor (hDC, GetSysColor (COLOR_HIGHLIGHTTEXT)) ;
  633. preBkColor = SetBkColor (hDC, GetSysColor (COLOR_HIGHLIGHT)) ;
  634. ResetColor = TRUE ;
  635. }
  636. ExtTextOut (hDC, lpDI->rcItem.left, lpDI->rcItem.top,
  637. ETO_OPAQUE, &(lpDI->rcItem), NULL, 0, NULL ) ;
  638. //=============================//
  639. // Draw Legend Item //
  640. //=============================//
  641. hFontPrevious = SelectFont (hDC, pLegend->hFontItems) ;
  642. if (pLine)
  643. DrawLegendItem (pLegend, pLine, lpDI->rcItem.top, hDC) ;
  644. SelectFont (hDC, hFontPrevious) ;
  645. //=============================//
  646. // Draw Focus //
  647. //=============================//
  648. if (DIFocus (lpDI))
  649. DrawFocusRect (hDC, &(lpDI->rcItem)) ;
  650. if (ResetColor == TRUE) {
  651. preTextColor = SetTextColor (hDC, preTextColor) ;
  652. preBkColor = SetBkColor (hDC, preBkColor) ;
  653. }
  654. }
  655. void
  656. static
  657. OnMeasureItem (
  658. HWND hWnd,
  659. LPMEASUREITEMSTRUCT lpMI
  660. )
  661. {
  662. PLEGEND pLegend ;
  663. pLegend = LegendData (hWnd) ;
  664. lpMI->itemHeight = pLegend->yItemHeight ;
  665. }
  666. void
  667. static
  668. OnDestroy (
  669. HWND hWnd
  670. )
  671. {
  672. PLEGEND pLegend ;
  673. pLegend = LegendData (hWnd) ;
  674. MemoryFree (pLegend) ;
  675. }
  676. void
  677. static
  678. OnDoubleClick (
  679. HWND hWnd
  680. )
  681. {
  682. PLEGEND pLegend ;
  683. pLegend = LegendData (hWnd) ;
  684. if (!pLegend)
  685. return ;
  686. switch (pLegend->iLineType) {
  687. case LineTypeChart:
  688. EditChart (hWndMain) ;
  689. break ;
  690. case LineTypeAlert:
  691. EditAlert (hWndAlert) ;
  692. break ;
  693. }
  694. }
  695. void
  696. static
  697. OnSetFocus (
  698. HWND hWnd
  699. )
  700. {
  701. PLEGEND pLegend ;
  702. pLegend = LegendData (hWnd) ;
  703. SetFocus (pLegend->hWndItems) ;
  704. }
  705. void
  706. static
  707. OnSelectionChanged (
  708. HWND hWnd
  709. )
  710. {
  711. PLEGEND pLegend ;
  712. PGRAPHSTRUCT pGraph ;
  713. int iIndex ;
  714. pLegend = LegendData (hWnd) ;
  715. // set the new selected line
  716. iIndex = (int)LBSelection (pLegend->hWndItems) ;
  717. if (iIndex == LB_ERR)
  718. return ;
  719. LegendSetCurrentLine (pLegend, iIndex) ;
  720. if (pLegend->iLineType == LineTypeChart) {
  721. pGraph = GraphData (hWndGraph) ;
  722. if (!PlayingBackLog()) {
  723. // update the min, max, & avg of the current line
  724. UpdateValueBarData (pGraph) ;
  725. }
  726. // update the valuebar display
  727. StatusTimer (hWndGraphStatus, TRUE) ;
  728. // change the Current highlighted line if necessary
  729. if (pGraph && pGraph->HighLightOnOff) {
  730. WindowInvalidate (hWndGraphDisplay) ;
  731. }
  732. }
  733. }
  734. //==========================================================================//
  735. // Exported Functions //
  736. //==========================================================================//
  737. LRESULT
  738. APIENTRY
  739. GraphLegendWndProc (
  740. HWND hWnd,
  741. UINT wMsg,
  742. WPARAM wParam,
  743. LPARAM lParam
  744. )
  745. {
  746. BOOL bCallDefProc ;
  747. LRESULT lReturnValue ;
  748. bCallDefProc = FALSE ;
  749. lReturnValue = 0L ;
  750. switch (wMsg) {
  751. case WM_DELETEITEM:
  752. break ;
  753. case WM_COMMAND:
  754. switch (HIWORD (wParam)) { // switch
  755. case LBN_DBLCLK:
  756. OnDoubleClick (hWnd) ;
  757. break ;
  758. case LBN_SELCHANGE:
  759. OnSelectionChanged (hWnd) ;
  760. break ;
  761. default:
  762. break ;
  763. } // switch
  764. break ;
  765. case WM_CREATE:
  766. OnLegendCreate (hWnd, (LPCREATESTRUCT) lParam) ;
  767. break ;
  768. case WM_DESTROY:
  769. OnDestroy (hWnd) ;
  770. break ;
  771. case WM_DRAWITEM:
  772. OnDrawLegendItem (hWnd, (LPDRAWITEMSTRUCT) lParam) ;
  773. break ;
  774. case WM_MEASUREITEM:
  775. OnMeasureItem (hWnd, (LPMEASUREITEMSTRUCT) lParam) ;
  776. break ;
  777. case WM_PAINT:
  778. OnPaint (hWnd) ;
  779. break ;
  780. case WM_SIZE:
  781. OnSize (hWnd, LOWORD (lParam), HIWORD (lParam)) ;
  782. break ;
  783. case WM_SETFOCUS:
  784. OnSetFocus (hWnd) ;
  785. break ;
  786. default:
  787. bCallDefProc = TRUE ;
  788. }
  789. if (bCallDefProc)
  790. lReturnValue = DefWindowProc (hWnd, wMsg, wParam, lParam) ;
  791. return (lReturnValue);
  792. }
  793. int
  794. LegendMinWidth (
  795. HWND hWnd
  796. )
  797. {
  798. PLEGEND pLegend ;
  799. int iCol ;
  800. int xMinWidth ;
  801. pLegend = LegendData (hWnd) ;
  802. xMinWidth = 0 ;
  803. for (iCol = 0; iCol < iLegendNumCols; iCol++) {
  804. xMinWidth += pLegend->aCols[iCol].xMinWidth ;
  805. }
  806. return (xMinWidth) ;
  807. }
  808. int
  809. LegendMinHeight (
  810. HWND hWnd
  811. )
  812. {
  813. PLEGEND pLegend ;
  814. pLegend = LegendData (hWnd) ;
  815. if (IsLegendLabelVisible ())
  816. return (pLegend->yLabelHeight + pLegend->yItemHeight) ;
  817. else
  818. return (pLegend->yItemHeight) ;
  819. }
  820. int
  821. LegendHeight (
  822. HWND hWnd,
  823. int yGraphHeight
  824. )
  825. /*
  826. Effect: Return the best height for the Legend window given the
  827. number of items and label visibility of the legend, as
  828. well as the overall height of the graph.
  829. */
  830. {
  831. PLEGEND pLegend ;
  832. int yPreferredHeight ;
  833. pLegend = LegendData (hWnd) ;
  834. yPreferredHeight = yLegendBorderHeight +
  835. pLegend->yItemHeight *
  836. PinInclusive (LBNumItems (pLegend->hWndItems),
  837. 1, iLegendMaxVisibleItems) ;
  838. if (IsLegendLabelVisible ())
  839. yPreferredHeight += pLegend->yLabelHeight ;
  840. return (min (yPreferredHeight, yGraphHeight / 2)) ;
  841. }
  842. int
  843. LegendFullHeight (
  844. HWND hWnd,
  845. HDC hDC
  846. )
  847. /*
  848. Effect: Return the best height for the Legend window given the
  849. number of items and label visibility of the legend, as
  850. well as the overall height of the graph.
  851. */
  852. {
  853. PLEGEND pLegend ;
  854. int yPreferredHeight ;
  855. pLegend = LegendData (hWnd) ;
  856. yPreferredHeight = yLegendBorderHeight +
  857. pLegend->yItemHeight *
  858. LBNumItems (pLegend->hWndItems) ;
  859. if (IsLegendLabelVisible ())
  860. yPreferredHeight += pLegend->yLabelHeight ;
  861. return (yPreferredHeight) ;
  862. }
  863. HWND
  864. CreateGraphLegendWindow (
  865. HWND hWndGraph
  866. )
  867. {
  868. return (CreateWindow (szGraphLegendClass, // class
  869. NULL, // caption
  870. dwGraphLegendWindowStyle, // window style
  871. 0, 0, // position
  872. 0, 0, // size
  873. hWndGraph, // parent window
  874. NULL, // menu
  875. hInstance, // program instance
  876. (LPVOID) TRUE)) ; // user-supplied data
  877. }
  878. BOOL
  879. GraphLegendInitializeApplication (void)
  880. /*
  881. Called By: GraphInitializeApplication only
  882. */
  883. {
  884. WNDCLASS wc ;
  885. wc.style = dwGraphLegendClassStyle ;
  886. wc.lpfnWndProc = GraphLegendWndProc ;
  887. wc.hInstance = hInstance ;
  888. wc.cbClsExtra = iGraphLegendClassExtra ;
  889. wc.cbWndExtra = iGraphLegendWindowExtra ;
  890. wc.hIcon = NULL ;
  891. wc.hCursor = LoadCursor (NULL, IDC_ARROW) ;
  892. // wc.hbrBackground = hbLightGray ;
  893. wc.hbrBackground = hBrushFace ;
  894. wc.lpszMenuName = NULL ;
  895. wc.lpszClassName = szGraphLegendClass ;
  896. return (RegisterClass (&wc)) ;
  897. }
  898. BOOL
  899. LegendAddItem (
  900. HWND hWnd,
  901. PLINESTRUCT pLine
  902. )
  903. /*
  904. Effect: Add a legend entry for the line pLine. Don't select
  905. the line as current entry here, because this will cause
  906. the scroll bar to unneccesarily appear for a moment.
  907. If you want this line to be the current line, resize
  908. the legend first, then set as current line.
  909. See Also: ChartInsertLine, AlertInsertLine.
  910. */
  911. {
  912. PLEGEND pLegend ;
  913. pLegend = LegendData (hWnd) ;
  914. LBAdd (pLegend->hWndItems, pLine) ;
  915. return (TRUE) ;
  916. }
  917. void
  918. LegendDeleteItem (
  919. HWND hWndLegend,
  920. PLINE pLine
  921. )
  922. {
  923. PLEGEND pLegend ;
  924. int iIndex ;
  925. int iNextIndex ;
  926. int iNumItems ;
  927. PGRAPHSTRUCT pGraph ;
  928. pLegend = LegendData (hWndLegend) ;
  929. if (!pLegend)
  930. return ;
  931. iNumItems = LBNumItems (pLegend->hWndItems) ;
  932. iIndex = (int)LBFind (pLegend->hWndItems, pLine) ;
  933. if (iIndex != LB_ERR) {
  934. LBDelete (pLegend->hWndItems, iIndex) ;
  935. }
  936. // no need to do anything if iNumItems is 1 to begin with
  937. if (iNumItems != LB_ERR && iNumItems > 1) {
  938. if (iIndex == iNumItems - 1) {
  939. // deleting the last line, then set selection
  940. // to the previous legend line
  941. iNextIndex = iIndex - 1 ;
  942. } else {
  943. // set the selection to the next legend line
  944. iNextIndex = iIndex ;
  945. }
  946. LBSetSelection (pLegend->hWndItems, iNextIndex) ;
  947. LegendSetCurrentLine (pLegend, iNextIndex) ;
  948. if (pLegend->iLineType == LineTypeChart) {
  949. // update the min, max, & avg of the current line
  950. pGraph = GraphData (hWndGraph) ;
  951. if (!PlayingBackLog()) {
  952. // update the min, max, & avg of the current line
  953. UpdateValueBarData (pGraph) ;
  954. }
  955. // update the valuebar display
  956. StatusTimer (hWndGraphStatus, TRUE) ;
  957. }
  958. }
  959. }
  960. PLINE
  961. LegendCurrentLine (
  962. HWND hWndLegend
  963. )
  964. {
  965. PLEGEND pLegend ;
  966. pLegend = LegendData (hWndLegend) ;
  967. if (!pLegend)
  968. return (NULL) ;
  969. return (pLegend->pCurrentLine) ;
  970. }
  971. int
  972. LegendNumItems (
  973. HWND hWndLegend
  974. )
  975. {
  976. PLEGEND pLegend ;
  977. pLegend = LegendData (hWndLegend) ;
  978. return (LBNumItems (pLegend->hWndItems)) ;
  979. }
  980. void
  981. LegendSetSelection (
  982. HWND hWndLegend,
  983. int iIndex
  984. )
  985. {
  986. PLEGEND pLegend ;
  987. pLegend = LegendData (hWndLegend) ;
  988. LBSetSelection (pLegend->hWndItems, iIndex) ;
  989. LegendSetCurrentLine (pLegend, iIndex) ;
  990. }
  991. #ifdef KEEP_PRINT
  992. void
  993. PrintLegend (
  994. HDC hDC,
  995. PGRAPHSTRUCT pGraph,
  996. HWND hWndLegend,
  997. RECT rectLegend
  998. )
  999. {
  1000. PLEGEND pLegend ;
  1001. int yItemHeight ;
  1002. HFONT hFontItems ;
  1003. PLINE pLine ;
  1004. int iIndex ;
  1005. int iIndexNum ;
  1006. pLegend = LegendData (hWndLegend) ;
  1007. yItemHeight = pLegend->yItemHeight ;
  1008. hFontItems = pLegend->hFontItems ;
  1009. pLegend->hFontItems = hFontPrinterScales ;
  1010. SelectFont (hDC, pLegend->hFontItems) ;
  1011. pLegend->yItemHeight = FontHeight (hDC, TRUE) ;
  1012. iIndexNum = LBNumItems (pLegend->hWndItems) ;
  1013. for (iIndex = 0; iIndex < iIndexNum; iIndex++) {
  1014. pLine = (PLINE) LBData (pLegend->hWndItems, iIndex) ;
  1015. DrawLegendItem (pLegend, pLine, iIndex * pLegend->yItemHeight, hDC) ;
  1016. }
  1017. pLegend->hFontItems = hFontItems ;
  1018. pLegend->yItemHeight = yItemHeight ;
  1019. SelectBrush (hDC, GetStockObject (HOLLOW_BRUSH)) ;
  1020. SelectPen (hDC, GetStockObject (BLACK_PEN)) ;
  1021. Rectangle (hDC, 0, 0,
  1022. rectLegend.right - rectLegend.left,
  1023. rectLegend.bottom - rectLegend.top) ;
  1024. }
  1025. #endif
  1026. void
  1027. ClearLegend (
  1028. HWND hWndLegend
  1029. )
  1030. {
  1031. PLEGEND pLegend ;
  1032. pLegend = LegendData (hWndLegend) ;
  1033. if (!pLegend)
  1034. return ;
  1035. LBReset (pLegend->hWndItems) ;
  1036. pLegend->pCurrentLine = NULL ;
  1037. }
  1038. void
  1039. LegendSetRedraw (
  1040. HWND hWndLegend,
  1041. BOOL bRedraw
  1042. )
  1043. {
  1044. PLEGEND pLegend ;
  1045. pLegend = LegendData (hWndLegend) ;
  1046. if (!pLegend)
  1047. return ;
  1048. LBSetRedraw (pLegend->hWndItems, bRedraw) ;
  1049. }