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.

1713 lines
44 KiB

  1. /*++
  2. Copyright (C) 1992-1999 Microsoft Corporation
  3. Module Name:
  4. legend.cpp
  5. Abstract:
  6. This file contains code creating the legend window, which is
  7. a child of the graph windows. The legend window displays a
  8. legend line for each line in the associated graph. It also
  9. includes an area called the label, which are headers for those
  10. lines.
  11. --*/
  12. //==========================================================================//
  13. // Includes //
  14. //==========================================================================//
  15. #include "polyline.h"
  16. #include <uxtheme.h>
  17. #include <strsafe.h>
  18. #include "grphitem.h"
  19. #include "legend.h"
  20. #include "winhelpr.h"
  21. #include "owndraw.h"
  22. #include "unihelpr.h"
  23. #define eScaleValueSpace L">9999999999.0"
  24. #define szGraphLegendClass L"PerfLegend"
  25. #define szGraphLegendClassA "PerfLegend"
  26. LRESULT APIENTRY HdrWndProc (HWND, UINT, WPARAM, LPARAM);
  27. //==========================================================================//
  28. // Constants //
  29. //==========================================================================//
  30. enum Orientation
  31. {
  32. LEFTORIENTATION = TA_LEFT,
  33. CENTERORIENTATION = TA_CENTER,
  34. RIGHTORIENTATION = TA_RIGHT
  35. };
  36. enum ColumnType
  37. {
  38. eLegendColorCol = 0,
  39. eLegendScaleCol = 1,
  40. eLegendCounterCol = 2,
  41. eLegendInstanceCol = 3,
  42. eLegendParentCol = 4,
  43. eLegendObjectCol = 5,
  44. eLegendSystemCol = 6,
  45. eLegendExtraCol = 7 // If control wider than combined columns
  46. };
  47. enum SortType
  48. {
  49. NO_SORT,
  50. INCREASING_SORT,
  51. DECREASING_SORT
  52. };
  53. enum WindowType
  54. {
  55. LIST_WND = 1000,
  56. HDR_WND
  57. };
  58. #define NULL_WIDTH -1
  59. #define dwGraphLegendClassStyle (CS_HREDRAW | CS_VREDRAW)
  60. #define iGraphLegendClassExtra (0)
  61. #define iGraphLegendWindowExtra (sizeof (PLEGEND))
  62. #define dwGraphLegendWindowStyle (WS_CHILD | WS_VISIBLE)
  63. #define ThreeDPad 2
  64. #define iMaxVisibleItems 8
  65. #define dwGraphLegendItemsWindowClass L"ListBox"
  66. #define dwGraphLegendItemsWindowStyle \
  67. (LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | LBS_OWNERDRAWFIXED | \
  68. WS_VISIBLE | WS_CHILD | WS_VSCROLL)
  69. #define WM_DELAYED_SELECT WM_USER + 100
  70. #define LegendBottomMargin() (ThreeDPad)
  71. #define LegendLeftMargin() (ThreeDPad)
  72. #define LegendHorzMargin() (10)
  73. typedef struct {
  74. PCGraphItem pGItem;
  75. LPCWSTR pszKey;
  76. } SORT_ITEM, *PSORT_ITEM;
  77. //==========================================================================//
  78. // Local Variables //
  79. //==========================================================================//
  80. static INT xBorderWidth = GetSystemMetrics(SM_CXBORDER);
  81. static INT yBorderHeight = GetSystemMetrics(SM_CYBORDER);
  82. #define MAX_COL_HEADER_LEN 32
  83. #define MAX_COL_CHARS 64
  84. static WCHAR aszColHeader[iLegendNumCols][MAX_COL_HEADER_LEN];
  85. //
  86. // Sorting function
  87. //
  88. INT __cdecl
  89. LegendSortFunc(
  90. const void *elem1,
  91. const void *elem2
  92. )
  93. {
  94. return lstrcmp(((PSORT_ITEM)elem1)->pszKey, ((PSORT_ITEM)elem2)->pszKey);
  95. }
  96. //
  97. // Constructor
  98. //
  99. CLegend::CLegend ( void )
  100. : m_pCtrl ( NULL ),
  101. m_hWnd ( NULL ),
  102. m_hWndHeader ( NULL ),
  103. m_DefaultWndProc ( NULL ),
  104. m_hWndItems ( NULL ),
  105. m_hFontItems ( NULL ),
  106. m_hFontLabels ( NULL ),
  107. m_iNumItemsVisible ( 0 ),
  108. m_pCurrentItem ( NULL ),
  109. m_iSortDir ( NO_SORT ),
  110. m_parrColWidthFraction( NULL )
  111. {
  112. m_fMetafile = FALSE;
  113. m_aCols[0].xWidth = -1;
  114. }
  115. //
  116. // Destructor
  117. //
  118. CLegend::~CLegend (void )
  119. {
  120. // Restore default window proc
  121. // so we don't get called post-mortum
  122. if (m_hWndHeader != NULL) {
  123. SetWindowLongPtr(m_hWndHeader, GWLP_WNDPROC, (INT_PTR)m_DefaultWndProc);
  124. }
  125. if (m_hWnd != NULL) {
  126. DestroyWindow(m_hWnd);
  127. }
  128. if ( NULL != m_parrColWidthFraction ) {
  129. delete m_parrColWidthFraction;
  130. }
  131. }
  132. //
  133. // Initialization
  134. //
  135. BOOL CLegend::Init ( PSYSMONCTRL pCtrl, HWND hWndParent )
  136. {
  137. INT iCol ;
  138. HD_ITEM hdi;
  139. HDC hDC;
  140. BOOL fComputeWidths;
  141. WNDCLASS wc ;
  142. LONG lExStyles;
  143. // Save pointer to parent control
  144. m_pCtrl = pCtrl;
  145. BEGIN_CRITICAL_SECTION
  146. // Register window class once
  147. if (pstrRegisteredClasses[LEGEND_WNDCLASS] == NULL) {
  148. wc.style = dwGraphLegendClassStyle ;
  149. wc.lpfnWndProc = GraphLegendWndProc ;
  150. wc.hInstance = g_hInstance ;
  151. wc.cbClsExtra = iGraphLegendClassExtra ;
  152. wc.cbWndExtra = iGraphLegendWindowExtra ;
  153. wc.hIcon = NULL ;
  154. wc.hCursor = LoadCursor (NULL, IDC_ARROW) ;
  155. wc.hbrBackground = NULL ;
  156. wc.lpszMenuName = NULL ;
  157. wc.lpszClassName = szGraphLegendClass ;
  158. if (RegisterClass (&wc)) {
  159. pstrRegisteredClasses[LEGEND_WNDCLASS] = szGraphLegendClass;
  160. }
  161. // Ensure controls are initialized
  162. InitCommonControls();
  163. // Load the column header strings just once also
  164. for (iCol=0; iCol<iLegendNumCols; iCol++) {
  165. LoadString(g_hInstance, (IDS_LEGEND_BASE + iCol), aszColHeader[iCol], MAX_COL_HEADER_LEN);
  166. }
  167. }
  168. END_CRITICAL_SECTION
  169. if (pstrRegisteredClasses[LEGEND_WNDCLASS] == NULL)
  170. return FALSE;
  171. // Create our window
  172. m_hWnd = CreateWindow (szGraphLegendClass, // class
  173. NULL, // caption
  174. dwGraphLegendWindowStyle, // window style
  175. 0, 0, // position
  176. 0, 0, // size
  177. hWndParent, // parent window
  178. NULL, // menu
  179. g_hInstance, // program instance
  180. (LPVOID) this ); // user-supplied data
  181. if (m_hWnd == NULL)
  182. return FALSE;
  183. // Turn off layout mirroring if it is enabled
  184. lExStyles = GetWindowLong(m_hWnd, GWL_EXSTYLE);
  185. if ( 0 != ( lExStyles & WS_EX_LAYOUTRTL ) ) {
  186. lExStyles &= ~WS_EX_LAYOUTRTL;
  187. SetWindowLong(m_hWnd, GWL_EXSTYLE, lExStyles);
  188. }
  189. // Turn off XP window theme for the owner drawn list header and cells.
  190. SetWindowTheme (m_hWnd, L" ", L" ");
  191. m_hWndHeader = CreateWindow(WC_HEADER,
  192. NULL,
  193. WS_CHILD | WS_BORDER | HDS_BUTTONS | HDS_HORZ,
  194. 0, 0, 0, 0,
  195. m_hWnd,
  196. (HMENU)HDR_WND,
  197. g_hInstance,
  198. (LPVOID) NULL);
  199. if (m_hWndHeader == NULL)
  200. return FALSE;
  201. // Turn off XP window theme for the owner drawn list header and cells.
  202. SetWindowTheme (m_hWndHeader, L" ", L" ");
  203. // Insert our own window procedure for special processing
  204. m_DefaultWndProc = (WNDPROC)SetWindowLongPtr(m_hWndHeader, GWLP_WNDPROC, (INT_PTR)HdrWndProc);
  205. // Create Legend Items Listbox
  206. m_hWndItems = CreateWindow (L"ListBox", // window class
  207. NULL, // window caption
  208. dwGraphLegendItemsWindowStyle, // window style
  209. 0, 0, 0, 0, // window size and pos
  210. m_hWnd, // parent window
  211. (HMENU)LIST_WND, // child ID
  212. g_hInstance, // program instance
  213. (LPVOID) TRUE) ; // user-supplied data
  214. if (m_hWndItems == NULL)
  215. return FALSE;
  216. // Turn off XP window theme for the owner drawn list header and cells.
  217. SetWindowTheme (m_hWndItems, L" ", L" ");
  218. // Set up DC for text measurements
  219. hDC = GetDC (m_hWndHeader);
  220. if ( NULL != hDC ) {
  221. // Compute initial sizes based on font
  222. ChangeFont(hDC);
  223. }
  224. // Set column widths and header labels
  225. m_aCols[0].xPos = 0;
  226. fComputeWidths = (m_aCols[0].xWidth == -1);
  227. for (iCol = 0; iCol < iLegendNumCols; iCol++)
  228. {
  229. // If width not loaded, calculate one based on label
  230. if ( fComputeWidths && NULL != hDC ) {
  231. m_aCols[iCol].xWidth = TextWidth (hDC, aszColHeader[iCol]) + 2 * LegendHorzMargin () ;
  232. }
  233. m_aCols[iCol].iOrientation = LEFTORIENTATION;
  234. if (iCol > 0) {
  235. m_aCols[iCol].xPos = m_aCols[iCol-1].xPos + m_aCols[iCol-1].xWidth;
  236. }
  237. hdi.mask = HDI_FORMAT | HDI_WIDTH;
  238. hdi.pszText = NULL;
  239. hdi.cxy = m_aCols[iCol].xWidth;
  240. hdi.fmt = HDF_OWNERDRAW | HDF_LEFT;
  241. Header_InsertItem(m_hWndHeader, iCol, &hdi);
  242. }
  243. if ( NULL != hDC ) {
  244. ReleaseDC ( m_hWndHeader, hDC );
  245. }
  246. return TRUE;
  247. }
  248. HRESULT CLegend::LoadFromStream(LPSTREAM pIStream)
  249. {
  250. HRESULT hr;
  251. ULONG bc;
  252. INT iCol;
  253. LEGEND_DATA LegendData;
  254. HD_ITEM hdi;
  255. hr = pIStream->Read(&LegendData, sizeof(LegendData), &bc);
  256. if (FAILED(hr))
  257. return hr;
  258. if (bc != sizeof(LegendData))
  259. return E_FAIL;
  260. hdi.mask = HDI_WIDTH;
  261. for (iCol=0; iCol<iLegendNumCols; iCol++) {
  262. m_aCols[iCol].xWidth = LegendData.xColWidth[iCol];
  263. if (iCol > 0) {
  264. m_aCols[iCol].xPos = m_aCols[iCol-1].xPos + m_aCols[iCol-1].xWidth;
  265. }
  266. hdi.cxy = m_aCols[iCol].xWidth;
  267. Header_SetItem(m_hWndHeader, iCol, &hdi);
  268. }
  269. m_iSortCol = LegendData.iSortCol;
  270. m_iSortDir = LegendData.iSortDir;
  271. return NOERROR;
  272. }
  273. HRESULT
  274. CLegend::SaveToStream(LPSTREAM pIStream)
  275. {
  276. HRESULT hr;
  277. INT iCol;
  278. LEGEND_DATA LegendData;
  279. for (iCol=0; iCol<iLegendNumCols; iCol++) {
  280. LegendData.xColWidth[iCol] = m_aCols[iCol].xWidth;
  281. }
  282. LegendData.iSortCol = m_iSortCol;
  283. LegendData.iSortDir = m_iSortDir;
  284. hr = pIStream->Write(&LegendData, sizeof(LegendData), NULL);
  285. if (FAILED(hr))
  286. return hr;
  287. return NOERROR;
  288. }
  289. HRESULT
  290. CLegend::LoadFromPropertyBag (
  291. IPropertyBag* pIPropBag,
  292. IErrorLog* pIErrorLog )
  293. {
  294. HRESULT hr = S_OK;
  295. LPWSTR pszData = NULL;
  296. INT iBufSizeCurrent = 0;
  297. INT iBufSize;
  298. INT iDataIndex;
  299. DOUBLE dValue = 0;
  300. WCHAR* pNextData;
  301. WCHAR* pDataEnd;
  302. hr = IntegerFromPropertyBag ( pIPropBag, pIErrorLog, L"LegendSortDirection", m_iSortDir );
  303. hr = IntegerFromPropertyBag ( pIPropBag, pIErrorLog, L"LegendSortColumn", m_iSortCol );
  304. iBufSize = iBufSizeCurrent;
  305. hr = StringFromPropertyBag (
  306. pIPropBag,
  307. pIErrorLog,
  308. L"LegendColumnWidths",
  309. pszData,
  310. iBufSize );
  311. if ( SUCCEEDED(hr) &&
  312. iBufSize > iBufSizeCurrent ) {
  313. // Width data exists.
  314. if ( NULL != pszData ) {
  315. delete [] pszData;
  316. pszData = NULL;
  317. }
  318. pszData = new WCHAR[ iBufSize ];
  319. if ( NULL == pszData ) {
  320. hr = E_OUTOFMEMORY;
  321. } else {
  322. *pszData = L'\0';
  323. iBufSizeCurrent = iBufSize;
  324. hr = StringFromPropertyBag (
  325. pIPropBag,
  326. pIErrorLog,
  327. L"LegendColumnWidths",
  328. pszData,
  329. iBufSize );
  330. }
  331. if ( SUCCEEDED(hr) ) {
  332. m_parrColWidthFraction = new DOUBLE[iLegendNumCols];
  333. if ( NULL != m_parrColWidthFraction ) {
  334. pNextData = pszData;
  335. pDataEnd = pszData + lstrlen(pszData);
  336. for ( iDataIndex = 0; SUCCEEDED(hr) && iDataIndex < iLegendNumCols; iDataIndex++ ) {
  337. if ( pNextData < pDataEnd ) {
  338. hr = GetNextValue ( pNextData, dValue );
  339. if ( SUCCEEDED(hr) ) {
  340. m_parrColWidthFraction[iDataIndex] = dValue;
  341. }
  342. } else {
  343. hr = E_FAIL;
  344. }
  345. }
  346. if ( FAILED(hr) ) {
  347. //
  348. // If failed to get at least one column value, then use default values.
  349. //
  350. delete [] m_parrColWidthFraction;
  351. m_parrColWidthFraction = NULL;
  352. }
  353. } else {
  354. hr = E_OUTOFMEMORY;
  355. }
  356. }
  357. }
  358. if (pszData != NULL) {
  359. delete [] pszData;
  360. }
  361. return NOERROR;
  362. }
  363. HRESULT
  364. CLegend::SaveToPropertyBag (
  365. IPropertyBag* pIPropBag,
  366. BOOL /* fClearDirty */,
  367. BOOL /* fSaveAllProps */ )
  368. {
  369. HRESULT hr = NOERROR;
  370. WCHAR szData[MAX_COL_CHARS*iLegendNumCols];
  371. WCHAR* pszTemp;
  372. INT iIndex;
  373. VARIANT vValue;
  374. INT xWidth;
  375. xWidth = m_Rect.right - m_Rect.left - 2 * LegendLeftMargin();
  376. // Continue even if error, using defaults in those cases.
  377. szData[0] = L'\0';
  378. for ( iIndex = 0; SUCCEEDED(hr) && iIndex < iLegendNumCols; iIndex++ ) {
  379. DOUBLE dFractionWidth;
  380. dFractionWidth = ( (DOUBLE)m_aCols[iIndex].xWidth ) / xWidth;
  381. if ( iIndex > 0 ) {
  382. StringCchCat (szData, MAX_COL_CHARS * iLegendNumCols, L"\t" );
  383. }
  384. VariantInit( &vValue );
  385. vValue.vt = VT_R8;
  386. vValue.dblVal = dFractionWidth;
  387. hr = VariantChangeTypeEx( &vValue, &vValue, LCID_SCRIPT, VARIANT_NOUSEROVERRIDE, VT_BSTR );
  388. pszTemp = vValue.bstrVal;
  389. StringCchCat(szData, MAX_COL_CHARS * iLegendNumCols, pszTemp );
  390. VariantClear( &vValue );
  391. }
  392. if ( SUCCEEDED( hr ) ){
  393. hr = StringToPropertyBag ( pIPropBag, L"LegendColumnWidths", szData );
  394. }
  395. hr = IntegerToPropertyBag ( pIPropBag, L"LegendSortDirection", m_iSortCol );
  396. hr = IntegerToPropertyBag ( pIPropBag, L"LegendSortColumn", m_iSortDir );
  397. return NOERROR;
  398. }
  399. //
  400. // Get list index of item
  401. //
  402. INT CLegend::GetItemIndex(PCGraphItem pGItem)
  403. {
  404. INT nItems;
  405. INT i;
  406. nItems = LBNumItems(m_hWndItems);
  407. for (i=0; i<nItems; i++)
  408. {
  409. if (pGItem == (PCGraphItem)LBData(m_hWndItems, i))
  410. return i;
  411. }
  412. return LB_ERR;
  413. }
  414. //
  415. // Select list item
  416. //
  417. BOOL CLegend::SelectItem(PCGraphItem pGItem)
  418. {
  419. INT iIndex;
  420. // Don't reselect the current selection
  421. // This is our parent echoing the change
  422. if (pGItem == m_pCurrentItem)
  423. return TRUE;
  424. iIndex = GetItemIndex(pGItem);
  425. if (iIndex == LB_ERR)
  426. return FALSE;
  427. LBSetSelection (m_hWndItems, iIndex) ;
  428. m_pCurrentItem = pGItem;
  429. return TRUE;
  430. }
  431. //
  432. // Add new item to legend
  433. //
  434. BOOL CLegend::AddItem (PCGraphItem pItem)
  435. {
  436. INT iHigh,iLow,iMid = 0;
  437. INT iComp;
  438. LPCWSTR pszItemKey = NULL;
  439. LPCWSTR pszItemKey2 = NULL;
  440. PCGraphItem pListItem;
  441. BOOL bSorted = TRUE;
  442. if (m_iSortDir == NO_SORT) {
  443. bSorted = FALSE;
  444. }
  445. else {
  446. //
  447. // If we need to sort, we must sort based upon a sortable
  448. // column. So check to make sure we have a sortable column.
  449. // If we don't have a sortable column, just add the item
  450. //
  451. pszItemKey = GetSortKey(pItem);
  452. if (pszItemKey == NULL) {
  453. bSorted = FALSE;
  454. }
  455. }
  456. if (bSorted == TRUE) {
  457. //
  458. // Binary search search for insertion point
  459. //
  460. iLow = 0;
  461. iHigh = LBNumItems(m_hWndItems);
  462. iMid = (iHigh + iLow) / 2;
  463. while (iLow < iHigh) {
  464. pListItem = (PCGraphItem)LBData(m_hWndItems, iMid);
  465. pszItemKey2 = GetSortKey(pListItem);
  466. //
  467. // pszItemKey2 should not be NULL if we come this point.
  468. // But if somehow it is NULL, then add the item
  469. //
  470. if (pszItemKey2 == NULL) {
  471. bSorted = FALSE;
  472. break;
  473. }
  474. iComp = lstrcmp(pszItemKey, pszItemKey2);
  475. if (m_iSortDir == DECREASING_SORT) {
  476. iComp = -iComp;
  477. }
  478. if (iComp > 0) {
  479. iLow = iMid + 1;
  480. }
  481. else {
  482. iHigh = iMid;
  483. }
  484. iMid = (iHigh + iLow) / 2;
  485. }
  486. }
  487. if (bSorted == TRUE) {
  488. LBInsert (m_hWndItems, iMid, pItem) ;
  489. }
  490. else {
  491. LBAdd(m_hWndItems, pItem);
  492. }
  493. return TRUE;
  494. }
  495. //
  496. // Delete item from legend
  497. //
  498. void CLegend::DeleteItem (PCGraphItem pItem)
  499. {
  500. INT iIndex ;
  501. // Calling procedure checks for NULL pItem
  502. assert ( NULL != pItem );
  503. iIndex = GetItemIndex (pItem) ;
  504. if (iIndex != LB_ERR) {
  505. LBDelete (m_hWndItems, iIndex) ;
  506. // If deleted the current item
  507. // select the next one (or prev if no next)
  508. if (pItem == m_pCurrentItem) {
  509. if (iIndex == LBNumItems(m_hWndItems))
  510. iIndex--;
  511. if (iIndex >= 0)
  512. m_pCurrentItem = (PCGraphItem)LBData(m_hWndItems, iIndex);
  513. else
  514. m_pCurrentItem = NULL;
  515. LBSetSelection (m_hWndItems, iIndex) ;
  516. m_pCtrl->SelectCounter(m_pCurrentItem);
  517. }
  518. }
  519. }
  520. //
  521. // Clear all items from legend
  522. //
  523. void CLegend::Clear ( void )
  524. {
  525. LBReset (m_hWndItems) ;
  526. m_pCurrentItem = NULL ;
  527. }
  528. //
  529. // Get currently selected item
  530. //
  531. PCGraphItem CLegend::CurrentItem ( void )
  532. {
  533. return (m_pCurrentItem) ;
  534. }
  535. //
  536. // Get legend window
  537. //
  538. HWND CLegend::Window ( void )
  539. {
  540. return m_hWnd;
  541. }
  542. //
  543. // Draw the header for a column
  544. //
  545. void
  546. CLegend::DrawColHeader(
  547. INT iCol,
  548. HDC hDC,
  549. HDC hAttribDC,
  550. RECT& rRect,
  551. BOOL bItemState )
  552. {
  553. HFONT hFontPrev;
  554. RECT rc = rRect;
  555. if ( m_fMetafile ) {
  556. if ( eAppear3D == m_pCtrl->Appearance() ) {
  557. DrawEdge(hDC, &rc, EDGE_RAISED, BF_RECT);
  558. } else {
  559. Rectangle (hDC, rc.left, rc.top,
  560. rc.right, rc.bottom );
  561. }
  562. }
  563. if ( iCol < iLegendNumCols ) {
  564. rc.top += yBorderHeight + 1; // Extra pixel so that tops of letters don't get clipped.
  565. rc.bottom -= yBorderHeight;
  566. rc.left += 6 * xBorderWidth;
  567. rc.right -= 6 * xBorderWidth;
  568. if ( bItemState )
  569. OffsetRect(&rc, xBorderWidth, yBorderHeight);
  570. SetTextColor (hDC, m_pCtrl->clrFgnd()) ;
  571. SetBkColor(hDC, m_pCtrl->clrBackCtl()) ;
  572. SetTextAlign (hDC, m_aCols[iCol].iOrientation) ;
  573. hFontPrev = (HFONT)SelectFont(hDC, m_pCtrl->Font());
  574. FitTextOut (
  575. hDC,
  576. hAttribDC,
  577. 0,
  578. &rc,
  579. aszColHeader[iCol],
  580. lstrlen(aszColHeader[iCol]),
  581. m_aCols[iCol].iOrientation, FALSE );
  582. SelectFont (hDC, hFontPrev);
  583. }
  584. }
  585. //
  586. // Draw the headers for all columns
  587. //
  588. void
  589. CLegend::DrawHeader(
  590. HDC hDC,
  591. HDC hAttribDC,
  592. RECT& /* rUpdateRect */ )
  593. {
  594. INT iCol;
  595. RECT rectCol;
  596. INT iSumColWidths;
  597. iSumColWidths = 0;
  598. for ( iCol = 0; iCol < iLegendNumCols; iCol++ ) {
  599. INT iColWidth;
  600. Header_GetItemRect( m_hWndHeader, iCol, &rectCol );
  601. iColWidth = rectCol.right - rectCol.left;
  602. if ( 0 < iColWidth ) {
  603. iSumColWidths += iColWidth;
  604. OffsetRect ( &rectCol, m_Rect.left, m_Rect.top );
  605. // Don't draw past the legend bounds.
  606. if ( rectCol.bottom > m_Rect.bottom ) {
  607. break;
  608. } else if ( rectCol.left >= m_Rect.right ) {
  609. break;
  610. } else if ( m_Rect.right < rectCol.right ) {
  611. rectCol.right = m_Rect.right;
  612. }
  613. DrawColHeader( iCol, hDC, hAttribDC, rectCol, FALSE );
  614. }
  615. }
  616. // Handle extra width past last column
  617. if ( iSumColWidths < ( m_Rect.right - m_Rect.left ) ) {
  618. rectCol.left = m_Rect.left + iSumColWidths;
  619. rectCol.right = m_Rect.right;
  620. DrawColHeader( iLegendNumCols, hDC, hAttribDC, rectCol, FALSE );
  621. }
  622. }
  623. //
  624. // Draw the color column for a legend item
  625. //
  626. void
  627. CLegend::DrawColorCol (
  628. PCGraphItem pItem,
  629. INT iCol,
  630. HDC hDC,
  631. HDC hAttribDC,
  632. INT yPos)
  633. {
  634. RECT rect ;
  635. HRGN hRgnOld;
  636. INT iRgn;
  637. INT yMiddle;
  638. if ( 0 < m_aCols[iCol].xWidth ) {
  639. rect.left = m_aCols[iCol].xPos + LegendLeftMargin () ;
  640. rect.top = yPos + 1 ;
  641. rect.right = rect.left + m_aCols[iCol].xWidth - 2 * LegendLeftMargin () ;
  642. rect.bottom = yPos + m_yItemHeight - 1 ;
  643. if( m_fMetafile ) {
  644. OffsetRect ( &rect, m_Rect.left, m_Rect.top );
  645. // Handle clipping.
  646. if ( rect.bottom > m_Rect.bottom ) {
  647. return;
  648. } else if ( rect.left >= m_Rect.right ) {
  649. return;
  650. } else if ( m_Rect.right < rect.right ) {
  651. rect.right = m_Rect.right;
  652. }
  653. }
  654. yMiddle = (rect.top + rect.bottom) / 2;
  655. if ( m_fMetafile ) {
  656. Line (hDC, pItem->Pen(),
  657. rect.left + 1, yMiddle, rect.right - 1, yMiddle) ;
  658. } else {
  659. if ( NULL != hAttribDC && NULL != hDC ) {
  660. hRgnOld = CreateRectRgn(0,0,0,0);
  661. if ( NULL != hRgnOld ) {
  662. iRgn = GetClipRgn(hAttribDC, hRgnOld);
  663. if ( -1 != iRgn ) {
  664. if ( ERROR != IntersectClipRect (hDC, rect.left + 1, rect.top + 1,
  665. rect.right - 1, rect.bottom - 1) ) {
  666. Line (hDC, pItem->Pen(),
  667. rect.left + 1, yMiddle, rect.right - 1, yMiddle) ;
  668. }
  669. // Old clip region is for the ListBox item window, so can't
  670. // use this for printing.
  671. if ( 1 == iRgn ) {
  672. SelectClipRgn(hDC, hRgnOld);
  673. }
  674. }
  675. DeleteObject(hRgnOld);
  676. }
  677. }
  678. }
  679. }
  680. }
  681. void
  682. CLegend::DrawCol (
  683. INT iCol,
  684. HDC hDC,
  685. HDC hAttribDC,
  686. INT yPos,
  687. LPCWSTR lpszValue)
  688. /*
  689. Effect: Draw the value lpszValue for the column iCol on hDC.
  690. Assert: The foreground and background text colors of hDC are
  691. properly set.
  692. */
  693. {
  694. static WCHAR szMissing[4] = L"---";
  695. RECT rect ;
  696. INT xPos ;
  697. BOOL bNeedEllipses = FALSE;
  698. INT cChars = 0;
  699. WCHAR achBuf[MAX_COL_CHARS + sizeof(ELLIPSES)/sizeof(WCHAR) + 1];
  700. if ( 0 < m_aCols[iCol].xWidth ) {
  701. rect.left = m_aCols[iCol].xPos + LegendLeftMargin() ;
  702. rect.top = yPos ;
  703. rect.right = rect.left + m_aCols[iCol].xWidth - 3 * LegendLeftMargin() ;
  704. rect.bottom = yPos + m_yItemHeight ;
  705. if( m_fMetafile ) {
  706. OffsetRect ( &rect, m_Rect.left, m_Rect.top );
  707. // Don't draw past the legend bounds.
  708. if ( rect.bottom > m_Rect.bottom ) {
  709. return;
  710. } else if ( rect.left >= m_Rect.right ) {
  711. return;
  712. } else if ( m_Rect.right < rect.right ) {
  713. rect.right = m_Rect.right;
  714. }
  715. DrawEdge(hDC, &rect, BDR_SUNKENOUTER, BF_RECT);
  716. }
  717. switch (m_aCols[iCol].iOrientation)
  718. { // switch
  719. case LEFTORIENTATION:
  720. SetTextAlign (hDC, TA_LEFT) ;
  721. xPos = rect.left ;
  722. break ;
  723. case CENTERORIENTATION:
  724. SetTextAlign (hDC, TA_CENTER) ;
  725. xPos = (rect.left + rect.right) / 2 ;
  726. break ;
  727. case RIGHTORIENTATION:
  728. SetTextAlign (hDC, TA_RIGHT) ;
  729. xPos = rect.right ;
  730. break ;
  731. default:
  732. xPos = rect.left ;
  733. break ;
  734. } // switch
  735. if (lpszValue[0] == 0)
  736. lpszValue = szMissing;
  737. bNeedEllipses = NeedEllipses (
  738. hAttribDC,
  739. lpszValue,
  740. lstrlen(lpszValue),
  741. rect.right - rect.left,
  742. m_xEllipses,
  743. &cChars );
  744. if ( bNeedEllipses ) {
  745. cChars = min(cChars,MAX_COL_CHARS);
  746. wcsncpy(achBuf, lpszValue, cChars);
  747. achBuf[cChars] = L'\0';
  748. StringCchCat(achBuf,
  749. MAX_COL_CHARS + sizeof(ELLIPSES)/sizeof(WCHAR) + 1,
  750. ELLIPSES);
  751. lpszValue = achBuf;
  752. cChars = lstrlen(lpszValue);
  753. }
  754. ExtTextOut (hDC, xPos, rect.top + yBorderHeight, ETO_OPAQUE | ETO_CLIPPED,
  755. &rect, lpszValue, cChars, NULL) ;
  756. }
  757. }
  758. //
  759. // Draw one legend line
  760. //
  761. void
  762. CLegend::DrawItem (
  763. PCGraphItem pItem,
  764. INT yPos,
  765. HDC hDC,
  766. HDC hAttribDC)
  767. {
  768. LPWSTR pszName = NULL;
  769. WCHAR szBuf[MAX_PATH + 1];
  770. INT iMinWidth = 3;
  771. INT iPrecision = 3;
  772. szBuf[0] = L'\0';
  773. // Draw Color
  774. DrawColorCol (pItem, eLegendColorCol, hDC, hAttribDC, yPos) ;
  775. // Draw Scale
  776. #if PDH_MIN_SCALE != -7
  777. // display a message if the scale format string gets out of sync with
  778. // the PDH limits
  779. #pragma message ("\nLEGEND.CPP: the scale format statement does not match the PDH\n")
  780. #endif
  781. if ( pItem->Scale() < (FLOAT) 1.0 ) {
  782. iMinWidth = 7;
  783. iPrecision = 7;
  784. } else {
  785. iMinWidth = 3;
  786. iPrecision = 3;
  787. }
  788. FormatNumber ( pItem->Scale(), szBuf, MAX_PATH, iMinWidth, iPrecision );
  789. SetTextAlign (hDC, TA_TOP) ;
  790. DrawCol ( eLegendScaleCol, hDC, hAttribDC, yPos, szBuf) ;
  791. // Draw Counter
  792. DrawCol ( eLegendCounterCol, hDC, hAttribDC, yPos, pItem->Counter()->Name()) ;
  793. // Draw Instance
  794. pszName = pItem->Instance()->GetInstanceName();
  795. DrawCol ( eLegendInstanceCol, hDC, hAttribDC, yPos, pszName) ;
  796. // Draw Parent
  797. pszName = pItem->Instance()->GetParentName();
  798. DrawCol (eLegendParentCol, hDC, hAttribDC, yPos, pszName) ;
  799. // Draw Object
  800. DrawCol (eLegendObjectCol, hDC, hAttribDC, yPos, pItem->Object()->Name()) ;
  801. // Draw System
  802. DrawCol (eLegendSystemCol, hDC, hAttribDC, yPos, pItem->Machine()->Name()) ;
  803. }
  804. //
  805. // Resize parts of legend
  806. //
  807. void CLegend::SizeComponents (LPRECT pRect)
  808. {
  809. INT xWidth;
  810. INT yHeight;
  811. m_Rect = *pRect;
  812. xWidth = pRect->right - pRect->left;
  813. yHeight = pRect->bottom - pRect->top;
  814. // If no space, hide window and leave
  815. if (xWidth == 0 || yHeight == 0) {
  816. WindowShow(m_hWnd, FALSE);
  817. return;
  818. }
  819. // If loaded from property bag, set column sizes.
  820. if ( NULL != m_parrColWidthFraction ) {
  821. INT iColTotalWidth;
  822. INT iCol;
  823. HD_ITEM hdi;
  824. hdi.mask = HDI_WIDTH;
  825. iColTotalWidth = xWidth - 2 * LegendLeftMargin();
  826. for ( iCol = 0; iCol < iLegendNumCols; iCol++ ) {
  827. m_aCols[iCol].xWidth = (INT)(m_parrColWidthFraction[iCol] * iColTotalWidth);
  828. hdi.cxy = m_aCols[iCol].xWidth;
  829. Header_SetItem(m_hWndHeader, iCol, &hdi);
  830. }
  831. AdjustColumnWidths ();
  832. delete m_parrColWidthFraction;
  833. m_parrColWidthFraction = NULL;
  834. }
  835. // Show window to assigned position
  836. MoveWindow(m_hWnd, pRect->left, pRect->top, xWidth, yHeight, FALSE);
  837. WindowShow(m_hWnd, TRUE);
  838. // Set the size, position, and visibility of the header control.
  839. SetWindowPos(m_hWndHeader, HWND_TOP, 0, 0, xWidth, m_yHeaderHeight, SWP_SHOWWINDOW);
  840. // Resize legend items window
  841. MoveWindow (m_hWndItems,
  842. LegendLeftMargin (), m_yHeaderHeight + ThreeDPad,
  843. xWidth - 2 * LegendLeftMargin (),
  844. yHeight - m_yHeaderHeight - ThreeDPad - LegendBottomMargin(),
  845. TRUE) ;
  846. }
  847. //
  848. // Repaint legend area
  849. //
  850. void CLegend::OnPaint ( void )
  851. { // OnPaint
  852. HDC hDC ;
  853. RECT rectFrame;
  854. PAINTSTRUCT ps ;
  855. hDC = BeginPaint (m_hWnd, &ps) ;
  856. if ( eAppear3D == m_pCtrl->Appearance() ) {
  857. // Draw 3D border
  858. GetClientRect(m_hWnd, &rectFrame);
  859. //rectFrame.bottom -= ThreeDPad;
  860. //rectFrame.right -= ThreeDPad;
  861. DrawEdge(hDC, &rectFrame, BDR_SUNKENOUTER, BF_RECT);
  862. }
  863. if (LBNumItems (m_hWndItems) == 0) {
  864. WindowInvalidate(m_hWndItems) ;
  865. }
  866. EndPaint (m_hWnd, &ps) ;
  867. } // OnPaint
  868. //
  869. // Handle user drawn header
  870. //
  871. void CLegend::OnDrawHeader(LPDRAWITEMSTRUCT lpDI)
  872. {
  873. INT iCol = DIIndex(lpDI);
  874. HDC hDC = lpDI->hDC;
  875. RECT rc = lpDI->rcItem;
  876. BOOL bItemState = lpDI->itemState;
  877. // The screen DC is used for the attribute DC.
  878. DrawColHeader( iCol, hDC, hDC, rc, bItemState );
  879. }
  880. //
  881. // Handle user drawn item message
  882. //
  883. void CLegend::OnDrawItem (LPDRAWITEMSTRUCT lpDI)
  884. {
  885. HFONT hFontPrevious ;
  886. HDC hDC ;
  887. PCGraphItem pItem ;
  888. INT iLBIndex ;
  889. COLORREF preBkColor = m_pCtrl->clrBackCtl();
  890. COLORREF preTextColor = m_pCtrl->clrFgnd();
  891. BOOL ResetColor = FALSE ;
  892. hDC = lpDI->hDC ;
  893. iLBIndex = DIIndex (lpDI) ;
  894. if (iLBIndex == -1)
  895. pItem = NULL ;
  896. else
  897. pItem = (PCGraphItem) LBData (m_hWndItems, iLBIndex) ;
  898. // If only a focus change, flip focus rect and leave
  899. if (lpDI->itemAction == ODA_FOCUS) {
  900. DrawFocusRect (hDC, &(lpDI->rcItem)) ;
  901. return;
  902. }
  903. // If item is selected use highlight colors
  904. if (DISelected (lpDI) || pItem == NULL) {
  905. preTextColor = SetTextColor (hDC, GetSysColor (COLOR_HIGHLIGHTTEXT)) ;
  906. preBkColor = SetBkColor (hDC, GetSysColor (COLOR_HIGHLIGHT)) ;
  907. ResetColor = TRUE;
  908. } // Else set BkColor to BackColorLegend selected by the user.
  909. // Clear area
  910. ExtTextOut (hDC, lpDI->rcItem.left, lpDI->rcItem.top,
  911. ETO_OPAQUE, &(lpDI->rcItem), NULL, 0, NULL ) ;
  912. // Draw Legend Item
  913. if (pItem) {
  914. hFontPrevious = SelectFont (hDC, m_pCtrl->Font()) ;
  915. // The screen DC is used as the attribute DC
  916. DrawItem (pItem, lpDI->rcItem.top, hDC, hDC) ;
  917. SelectFont (hDC, hFontPrevious) ;
  918. }
  919. // Draw Focus rect
  920. if (DIFocus (lpDI))
  921. DrawFocusRect (hDC, &(lpDI->rcItem)) ;
  922. // Restore original colors
  923. if (ResetColor == TRUE) {
  924. SetTextColor (hDC, preTextColor) ;
  925. SetBkColor (hDC, preBkColor) ;
  926. }
  927. }
  928. void CLegend::OnMeasureItem (LPMEASUREITEMSTRUCT lpMI) {
  929. lpMI->itemHeight = m_yItemHeight ;
  930. } // OnMeasureItem
  931. void CLegend::OnDblClick ( void )
  932. {
  933. m_pCtrl->DblClickCounter ( m_pCurrentItem );
  934. }
  935. //
  936. // Handle selection change message
  937. //
  938. void CLegend::OnSelectionChanged ( void )
  939. {
  940. INT iIndex ;
  941. PCGraphItem pGItem;
  942. // Get the new selection
  943. iIndex = LBSelection (m_hWndItems) ;
  944. pGItem = (PCGraphItem) LBData (m_hWndItems, iIndex) ;
  945. // if it's bad, reselect the current one
  946. // else request parent control to select new item
  947. if (pGItem == (PCGraphItem)LB_ERR) {
  948. SelectItem(m_pCurrentItem);
  949. }
  950. else {
  951. m_pCurrentItem = pGItem;
  952. m_pCtrl->SelectCounter(pGItem);
  953. }
  954. }
  955. void CLegend::AdjustColumnWidths (
  956. INT iCol
  957. )
  958. {
  959. INT i;
  960. // Adjust positions of following columns
  961. for (i=iCol+1; i < iLegendNumCols; i++) {
  962. m_aCols[i].xPos = m_aCols[i - 1].xPos + m_aCols[i - 1].xWidth ;
  963. }
  964. }
  965. void CLegend::OnColumnWidthChanged (
  966. HD_NOTIFY *phdn
  967. )
  968. {
  969. INT iCol = phdn->iItem;
  970. INT xWidth = phdn->pitem->cxy;
  971. // Update column width
  972. m_aCols[iCol].xWidth = xWidth;
  973. AdjustColumnWidths ( iCol );
  974. // Force update
  975. WindowInvalidate(m_hWndItems) ;
  976. }
  977. LPCWSTR CLegend::GetSortKey (
  978. PCGraphItem pItem
  979. )
  980. {
  981. static WCHAR chNullName = 0;
  982. switch (m_iSortCol) {
  983. case eLegendCounterCol:
  984. return pItem->Counter()->Name();
  985. case eLegendInstanceCol:
  986. if (pItem->Instance()->HasParent())
  987. return wcschr(pItem->Instance()->Name(), L'/') + 1;
  988. else
  989. return pItem->Instance()->Name();
  990. case eLegendParentCol:
  991. if (pItem->Instance()->HasParent())
  992. return pItem->Instance()->Name();
  993. else
  994. return &chNullName;
  995. case eLegendObjectCol:
  996. return pItem->Object()->Name();
  997. case eLegendSystemCol:
  998. return pItem->Machine()->Name();
  999. }
  1000. return NULL;
  1001. }
  1002. void
  1003. CLegend::OnColumnClicked (
  1004. HD_NOTIFY *phdn
  1005. )
  1006. {
  1007. INT i;
  1008. INT iCol = phdn->iItem;
  1009. INT nItems = LBNumItems (m_hWndItems);
  1010. PSORT_ITEM pSortItem;
  1011. PSORT_ITEM pSortItems;
  1012. BOOL bResort = FALSE;
  1013. if (nItems <= 0)
  1014. return;
  1015. // Can't sort on color or scale factor
  1016. if (iCol == eLegendColorCol || iCol == eLegendScaleCol) {
  1017. m_iSortDir = NO_SORT;
  1018. return;
  1019. }
  1020. // If repeat click, reverse sort direction
  1021. if (iCol == m_iSortCol) {
  1022. bResort = TRUE;
  1023. m_iSortDir = (m_iSortDir == INCREASING_SORT) ?
  1024. DECREASING_SORT : INCREASING_SORT;
  1025. } else {
  1026. m_iSortCol = iCol;
  1027. m_iSortDir = INCREASING_SORT;
  1028. }
  1029. // Allocate array for sorting
  1030. pSortItems = new SORT_ITEM [nItems];
  1031. if (pSortItems == NULL) {
  1032. return;
  1033. }
  1034. // Build array of GraphItem/Key pairs
  1035. pSortItem = pSortItems;
  1036. for (i=0; i<nItems; i++,pSortItem++) {
  1037. pSortItem->pGItem = (PCGraphItem)LBData(m_hWndItems, i);
  1038. pSortItem->pszKey = GetSortKey(pSortItem->pGItem);
  1039. }
  1040. // For resort, just reload in reverse order.
  1041. if ( !bResort ) {
  1042. // Sort by key value
  1043. qsort( (PVOID)pSortItems, nItems, sizeof(SORT_ITEM), &LegendSortFunc );
  1044. }
  1045. // Disable drawing while rebuilding list
  1046. LBSetRedraw(m_hWndItems, FALSE);
  1047. // Clear list box
  1048. LBReset (m_hWndItems) ;
  1049. // Reload in sorted order
  1050. if ( !bResort && m_iSortDir == INCREASING_SORT) {
  1051. for (i=0; i<nItems; i++) {
  1052. LBAdd (m_hWndItems, pSortItems[i].pGItem);
  1053. }
  1054. } else {
  1055. for (i=nItems - 1; i>=0; i--) {
  1056. LBAdd (m_hWndItems, pSortItems[i].pGItem);
  1057. }
  1058. }
  1059. LBSetRedraw(m_hWndItems, TRUE);
  1060. delete [] pSortItems;
  1061. }
  1062. //
  1063. // Window procedure
  1064. //
  1065. LRESULT APIENTRY GraphLegendWndProc (HWND hWnd, UINT uiMsg, WPARAM wParam,
  1066. LPARAM lParam)
  1067. {
  1068. CLegend *pLegend;
  1069. BOOL bCallDefProc ;
  1070. LRESULT lReturnValue ;
  1071. RECT rect;
  1072. pLegend = (PLEGEND)GetWindowLongPtr(hWnd,0);
  1073. bCallDefProc = FALSE ;
  1074. lReturnValue = 0L ;
  1075. switch (uiMsg)
  1076. {
  1077. case WM_CREATE:
  1078. pLegend = (PLEGEND)((CREATESTRUCT*)lParam)->lpCreateParams;
  1079. SetWindowLongPtr(hWnd,0,(INT_PTR)pLegend);
  1080. break;
  1081. case WM_DESTROY:
  1082. pLegend->m_hWnd = NULL;
  1083. break ;
  1084. case WM_LBUTTONDBLCLK:
  1085. case WM_LBUTTONDOWN:
  1086. SendMessage(GetParent(hWnd), uiMsg, wParam, lParam);
  1087. break;
  1088. case WM_COMMAND:
  1089. switch (HIWORD (wParam))
  1090. { // switch
  1091. case LBN_DBLCLK:
  1092. pLegend->OnDblClick () ;
  1093. break ;
  1094. case LBN_SELCHANGE:
  1095. pLegend->OnSelectionChanged () ;
  1096. break ;
  1097. case LBN_SETFOCUS:
  1098. pLegend->m_pCtrl->Activate();
  1099. break;
  1100. default:
  1101. bCallDefProc = TRUE ;
  1102. } // switch
  1103. break ;
  1104. case WM_NOTIFY:
  1105. switch (((LPNMHDR)lParam)->code)
  1106. {
  1107. case HDN_ENDTRACK:
  1108. pLegend->OnColumnWidthChanged((HD_NOTIFY*) lParam);
  1109. break;
  1110. case HDN_ITEMCLICK:
  1111. pLegend->OnColumnClicked((HD_NOTIFY*) lParam);
  1112. pLegend->m_pCtrl->Activate();
  1113. break;
  1114. }
  1115. return FALSE;
  1116. break;
  1117. case WM_DRAWITEM:
  1118. switch (((LPDRAWITEMSTRUCT)lParam)->CtlID) {
  1119. case LIST_WND:
  1120. pLegend->OnDrawItem((LPDRAWITEMSTRUCT) lParam) ;
  1121. break;
  1122. case HDR_WND:
  1123. pLegend->OnDrawHeader((LPDRAWITEMSTRUCT) lParam) ;
  1124. break;
  1125. }
  1126. break ;
  1127. case WM_MEASUREITEM:
  1128. pLegend->OnMeasureItem ((LPMEASUREITEMSTRUCT) lParam) ;
  1129. break ;
  1130. case WM_DELETEITEM:
  1131. break ;
  1132. case WM_ERASEBKGND:
  1133. GetClientRect(hWnd, &rect);
  1134. Fill((HDC)wParam, pLegend->m_pCtrl->clrBackCtl(), &rect);
  1135. return TRUE;
  1136. case WM_PAINT:
  1137. pLegend->OnPaint () ;
  1138. break ;
  1139. case WM_SETFOCUS:
  1140. SetFocus(pLegend->m_hWndItems);
  1141. break ;
  1142. default:
  1143. bCallDefProc = TRUE ;
  1144. }
  1145. if (bCallDefProc)
  1146. lReturnValue = DefWindowProc (hWnd, uiMsg, wParam, lParam) ;
  1147. return (lReturnValue);
  1148. }
  1149. LRESULT APIENTRY
  1150. HdrWndProc (
  1151. HWND hWnd,
  1152. UINT uiMsg,
  1153. WPARAM wParam,
  1154. LPARAM lParam
  1155. )
  1156. {
  1157. RECT rect;
  1158. CLegend *pLegend;
  1159. // Parent window carries the Legend object pointer
  1160. pLegend = (PLEGEND)GetWindowLongPtr(GetParent(hWnd),0);
  1161. if (uiMsg == WM_ERASEBKGND) {
  1162. GetClientRect(hWnd, &rect);
  1163. Fill((HDC)wParam, pLegend->m_pCtrl->clrBackCtl(), &rect);
  1164. return TRUE;
  1165. }
  1166. // Do the default processing
  1167. #ifdef STRICT
  1168. return CallWindowProc(pLegend->m_DefaultWndProc, hWnd, uiMsg, wParam, lParam);
  1169. #else
  1170. return CallWindowProc((FARPROC)pLegend->m_DefaultWndProc, hWnd, uiMsg, wParam, lParam);
  1171. #endif
  1172. }
  1173. //
  1174. // Compute minimum width of legend
  1175. //
  1176. INT CLegend::MinWidth ( void )
  1177. {
  1178. return 0 ;
  1179. }
  1180. //
  1181. // Compute minimum height of legend
  1182. //
  1183. INT CLegend::MinHeight ( INT yMaxHeight )
  1184. {
  1185. INT yHeight = m_yHeaderHeight + m_yItemHeight + 2*ThreeDPad
  1186. + LegendBottomMargin();
  1187. return (yMaxHeight >= yHeight) ? yHeight : 0;
  1188. }
  1189. //
  1190. // Compute prefered height of legend
  1191. //
  1192. INT CLegend::Height (INT yMaxHeight)
  1193. {
  1194. INT nItems;
  1195. INT nPrefItems;
  1196. // Determine preferred number of items to show
  1197. nPrefItems = PinInclusive(LBNumItems(m_hWndItems), 1, iMaxVisibleItems);
  1198. // Determine number of items that will fit
  1199. nItems = (yMaxHeight - m_yHeaderHeight - 2*ThreeDPad - LegendBottomMargin())
  1200. / m_yItemHeight;
  1201. // Use smaller number
  1202. nItems = min(nItems, nPrefItems);
  1203. // If no items will fit, return zero
  1204. if (nItems == 0)
  1205. return 0;
  1206. // Return height of legend with nItems
  1207. return m_yHeaderHeight + 2*ThreeDPad + nItems * m_yItemHeight
  1208. + LegendBottomMargin();
  1209. }
  1210. #ifdef KEEP_PRINT
  1211. void CLegend::Print (HDC hDC, RECT rectLegend)
  1212. {
  1213. INT yItemHeight ;
  1214. HFONT hFontItems ;
  1215. PCGraphItem pItem ;
  1216. INT iIndex ;
  1217. INT iIndexNum ;
  1218. yItemHeight = m_yItemHeight ;
  1219. hFontItems = m_hFontItems ;
  1220. m_hFontItems = hFontPrinterScales ;
  1221. SelectFont (hDC, m_hFontItems) ;
  1222. m_yItemHeight = FontHeight (hDC, TRUE) ;
  1223. iIndexNum = LBNumItems (m_hWndItems);
  1224. for (iIndex = 0; iIndex < iIndexNum; iIndex++)
  1225. {
  1226. pItem = (PCGraphItem) LBData (m_hWndItems, iIndex) ;
  1227. DrawItem (pItem, iIndex * m_yItemHeight, hDC) ;
  1228. }
  1229. m_hFontItems = hFontItems ;
  1230. m_yItemHeight = yItemHeight ;
  1231. SelectBrush (hDC, GetStockObject (HOLLOW_BRUSH)) ;
  1232. SelectPen (hDC, GetStockObject (BLACK_PEN)) ;
  1233. Rectangle (hDC, 0, 0,
  1234. rectLegend.right - rectLegend.left,
  1235. rectLegend.bottom - rectLegend.top) ;
  1236. }
  1237. #endif
  1238. void
  1239. CLegend::ChangeFont(
  1240. HDC hDC
  1241. )
  1242. {
  1243. HD_LAYOUT hdl;
  1244. WINDOWPOS wp;
  1245. RECT rectLayout;
  1246. // Assign font to header
  1247. SetFont(m_hWndHeader, m_pCtrl->Font());
  1248. // Get prefered height of header control
  1249. // (use arbitrary rect for allowed area)
  1250. rectLayout.left = 0;
  1251. rectLayout.top = 0;
  1252. rectLayout.right = 32000;
  1253. rectLayout.bottom = 32000;
  1254. wp.cy = 0;
  1255. hdl.prc = &rectLayout;
  1256. hdl.pwpos = &wp;
  1257. Header_Layout(m_hWndHeader, &hdl);
  1258. m_yHeaderHeight = wp.cy + 2 * yBorderHeight;
  1259. // Set up DC for font measurements
  1260. SelectFont (hDC, m_pCtrl->Font()) ;
  1261. // Compute height of legend line
  1262. SelectFont (hDC, m_hFontItems) ;
  1263. m_yItemHeight = FontHeight (hDC, TRUE) + 2 * yBorderHeight;
  1264. LBSetItemHeight(m_hWndItems, m_yItemHeight);
  1265. // Compute width of "..."
  1266. m_xEllipses = TextWidth (hDC, ELLIPSES) ;
  1267. }
  1268. void
  1269. CLegend::Render(
  1270. HDC hDC,
  1271. HDC hAttribDC,
  1272. BOOL /*fMetafile*/,
  1273. BOOL /*fEntire*/,
  1274. LPRECT prcUpdate )
  1275. {
  1276. PCGraphItem pItem ;
  1277. INT iIndex ;
  1278. INT iIndexNum ;
  1279. RECT rectPaint;
  1280. HFONT hFontPrevious;
  1281. // if no space assigned, return
  1282. if (m_Rect.top == m_Rect.bottom)
  1283. return;
  1284. // if no painting needed, return
  1285. if (!IntersectRect(&rectPaint, &m_Rect, prcUpdate))
  1286. return;
  1287. m_fMetafile = TRUE;
  1288. hFontPrevious = SelectFont (hDC, m_pCtrl->Font()) ;
  1289. DrawHeader ( hDC, hAttribDC, rectPaint );
  1290. SelectFont (hDC, hFontPrevious) ;
  1291. iIndexNum = LBNumItems (m_hWndItems);
  1292. hFontPrevious = SelectFont (hDC, m_pCtrl->Font()) ;
  1293. for (iIndex = 0; iIndex < iIndexNum; iIndex++) {
  1294. pItem = (PCGraphItem) LBData (m_hWndItems, iIndex) ;
  1295. DrawItem (
  1296. pItem,
  1297. m_yHeaderHeight + ( iIndex * m_yItemHeight ),
  1298. hDC,
  1299. hAttribDC) ;
  1300. }
  1301. SelectFont (hDC, hFontPrevious) ;
  1302. m_fMetafile = FALSE;
  1303. SelectBrush (hDC, GetStockObject (HOLLOW_BRUSH)) ;
  1304. SelectPen (hDC, GetStockObject (BLACK_PEN)) ;
  1305. if ( eAppear3D == m_pCtrl->Appearance() ) {
  1306. // Draw 3D border
  1307. DrawEdge(hDC, &m_Rect, BDR_SUNKENOUTER, BF_RECT);
  1308. }
  1309. }
  1310. HRESULT
  1311. CLegend::GetNextValue (
  1312. WCHAR*& pszNext,
  1313. DOUBLE& rdValue )
  1314. {
  1315. HRESULT hr = NOERROR;
  1316. WCHAR szValue[MAX_COL_CHARS + 1];
  1317. INT iLen;
  1318. VARIANT vValue;
  1319. rdValue = -1.0;
  1320. iLen = wcscspn (pszNext, L"\t");
  1321. //
  1322. // Change tab character to null.
  1323. //
  1324. pszNext[iLen] = L'\0';
  1325. hr = StringCchCopy ( szValue, MAX_COL_CHARS + 1, pszNext );
  1326. if ( SUCCEEDED ( hr ) ) {
  1327. VariantInit( &vValue );
  1328. vValue.vt = VT_BSTR;
  1329. vValue.bstrVal = SysAllocString ( szValue );
  1330. hr = VariantChangeTypeEx( &vValue, &vValue, LCID_SCRIPT, VARIANT_NOUSEROVERRIDE, VT_R8 );
  1331. if ( SUCCEEDED(hr) ) {
  1332. rdValue = vValue.dblVal;
  1333. }
  1334. VariantClear( &vValue );
  1335. }
  1336. pszNext += iLen + 1 ;
  1337. return hr;
  1338. }