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.

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