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.

3679 lines
60 KiB

  1. /*++
  2. Copyright (c) 1994-1999 Microsoft Corporation
  3. Module Name :
  4. odlbox.cpp
  5. Abstract:
  6. Owner draw listbox/combobox base classes
  7. Author:
  8. Ronald Meijer (ronaldm)
  9. Project:
  10. Internet Services Manager (cluster edition)
  11. Revision History:
  12. --*/
  13. //
  14. // Include Files
  15. //
  16. #include "stdafx.h"
  17. #include "common.h"
  18. #ifdef _DEBUG
  19. #undef THIS_FILE
  20. static char BASED_CODE THIS_FILE[] = __FILE__;
  21. #endif
  22. #define new DEBUG_NEW
  23. #define BMP_LEFT_OFFSET (1) // Space allotted to the left of bitmap
  24. #define BMP_RIGHT_OFFSET (3) // Space allotted to the right of bitmap
  25. //
  26. // Ellipses are shown when column text doesn't fit in the display
  27. //
  28. const TCHAR g_szEllipses[] = _T("...");
  29. int g_nLenEllipses = (sizeof(g_szEllipses) / sizeof(g_szEllipses[0])) - 1;
  30. //
  31. // Registry value for columns
  32. //
  33. const TCHAR g_szRegColumns[] = _T("Columns");
  34. //
  35. // Column Value Separator
  36. //
  37. const TCHAR g_szColValueSep[] = _T(" ");
  38. void
  39. GetDlgCtlRect(
  40. IN HWND hWndParent,
  41. IN HWND hWndControl,
  42. OUT LPRECT lprcControl
  43. )
  44. /*++
  45. Routine Description:
  46. Get the control rectangle coordinates relative to its parent. This can
  47. then be used in e.g. SetWindowPos()
  48. Arguments:
  49. HWND hWndParent : Parent window handle
  50. HWND hWndControl : Control window handle
  51. LPRECT lprcControl : Control rectangle to be filled in
  52. Return Value:
  53. None
  54. --*/
  55. {
  56. #define MapWindowRect(hwndFrom, hwndTo, lprc)\
  57. MapWindowPoints((hwndFrom), (hwndTo), (POINT *)(lprc), 2)
  58. ::GetWindowRect(hWndControl, lprcControl);
  59. ::MapWindowRect(NULL, hWndParent, lprcControl);
  60. }
  61. void
  62. FitPathToControl(
  63. IN CWnd & wndControl,
  64. IN LPCTSTR lpstrPath
  65. )
  66. /*++
  67. Routine Description:
  68. Display the given path in the given control, using ellipses
  69. to ensure that the path fits within the control.
  70. Arguments:
  71. CWnd & wndControl : Control to display on
  72. LPCTSTR lpstrPath : Path
  73. Return Value:
  74. None
  75. --*/
  76. {
  77. CString strDisplay(lpstrPath);
  78. UINT uLength = strDisplay.GetLength() + 4; // Account for ell.
  79. LPTSTR lp = strDisplay.GetBuffer(uLength);
  80. if (lp)
  81. {
  82. CDC * pDC = wndControl.GetDC();
  83. ASSERT_PTR(pDC);
  84. if (pDC != NULL)
  85. {
  86. CRect rc;
  87. wndControl.GetClientRect(&rc);
  88. pDC->DrawText(lp, uLength, &rc, DT_PATH_ELLIPSIS | DT_MODIFYSTRING);
  89. wndControl.ReleaseDC(pDC);
  90. }
  91. strDisplay.ReleaseBuffer();
  92. wndControl.SetWindowText(strDisplay);
  93. }
  94. }
  95. void
  96. ActivateControl(
  97. IN CWnd & wndControl,
  98. IN BOOL fShow
  99. )
  100. /*++
  101. Routine Description:
  102. Show/hide _AND_ enable/disable control window
  103. Arguments:
  104. CWnd & wndControl : Window in question
  105. BOOL fShow : TRUE to show/enable,
  106. FALSE to hide/disable
  107. Return Value:
  108. None
  109. Notes:
  110. Merely hiding a window does not disable it. Use this function
  111. instead of ShowWindow() to do that.
  112. --*/
  113. {
  114. wndControl.ShowWindow(fShow ? SW_SHOW : SW_HIDE);
  115. wndControl.EnableWindow(fShow);
  116. }
  117. BOOL
  118. VerifyState()
  119. /*++
  120. Routine Description:
  121. Verify keyboard state
  122. Arguments:
  123. None
  124. Return Value:
  125. TRUE if keyboard is in specified state
  126. FALSE otherwise.
  127. --*/
  128. {
  129. SHORT s1, s2;
  130. s1 = GetKeyState(VK_SHIFT);
  131. s2 = GetKeyState(VK_CONTROL);
  132. return (s1 & 0x8000) && (s2 & 0x8000);
  133. }
  134. BOOL
  135. CMappedBitmapButton::LoadMappedBitmaps(
  136. IN UINT nIDBitmapResource,
  137. IN UINT nIDBitmapResourceSel,
  138. IN UINT nIDBitmapResourceFocus,
  139. IN UINT nIDBitmapResourceDisabled
  140. )
  141. /*++
  142. Routine Description:
  143. LoadBitmaps will load one, two, three or all four bitmaps
  144. returns TRUE if all specified images are loaded. This
  145. will map the buttons to the default colours
  146. Arguments:
  147. UINT nIDBitmapResource : Standard button
  148. UINT nIDBitmapResourceSel : Selected button
  149. UINT nIDBitmapResourceFocus : Button with focus
  150. UINT nIDBitmapResourceDisabled : Disabled button
  151. --*/
  152. {
  153. //
  154. // delete old bitmaps (if present)
  155. //
  156. m_bitmap.DeleteObject();
  157. m_bitmapSel.DeleteObject();
  158. m_bitmapFocus.DeleteObject();
  159. m_bitmapDisabled.DeleteObject();
  160. if (!m_bitmap.LoadMappedBitmap(nIDBitmapResource))
  161. {
  162. TRACEEOLID("Failed to load bitmap for normal image.");
  163. return FALSE; // need this one image
  164. }
  165. BOOL bAllLoaded = TRUE;
  166. if (nIDBitmapResourceSel != 0)
  167. {
  168. if (!m_bitmapSel.LoadMappedBitmap(nIDBitmapResourceSel))
  169. {
  170. TRACEEOLID("Failed to load bitmap for selected image.");
  171. bAllLoaded = FALSE;
  172. }
  173. }
  174. if (nIDBitmapResourceFocus != 0)
  175. {
  176. if (!m_bitmapFocus.LoadMappedBitmap(nIDBitmapResourceFocus))
  177. {
  178. bAllLoaded = FALSE;
  179. }
  180. }
  181. if (nIDBitmapResourceDisabled != 0)
  182. {
  183. if (!m_bitmapDisabled.LoadMappedBitmap(nIDBitmapResourceDisabled))
  184. {
  185. bAllLoaded = FALSE;
  186. }
  187. }
  188. return bAllLoaded;
  189. }
  190. CRMCListBoxResources::CRMCListBoxResources(
  191. IN int bmId,
  192. IN int nBitmaps,
  193. IN COLORREF rgbBackground
  194. )
  195. /*++
  196. Routine Description:
  197. Constructor
  198. Arguments:
  199. int bmId : Bitmap resource ID
  200. int nBitmaps : Number of bitmaps
  201. COLORREF rgbBackground : Background colour to mask out
  202. Return Value:
  203. N/A
  204. --*/
  205. : m_idBitmap(bmId),
  206. m_rgbColorTransparent(rgbBackground),
  207. m_nBitmaps(nBitmaps),
  208. m_nBitmapHeight(0),
  209. m_nBitmapWidth(-1), // Set Later
  210. m_fInitialized(FALSE)
  211. {
  212. ASSERT(m_nBitmaps > 0);
  213. GetSysColors();
  214. PrepareBitmaps();
  215. }
  216. CRMCListBoxResources::~CRMCListBoxResources()
  217. /*++
  218. Routine Description:
  219. Destructor
  220. Arguments:
  221. N/A
  222. Return Value:
  223. N/A
  224. --*/
  225. {
  226. UnprepareBitmaps();
  227. }
  228. void
  229. CRMCListBoxResources::UnprepareBitmaps()
  230. /*++
  231. Routine Description:
  232. Free up bitmap resources
  233. Arguments:
  234. N/A
  235. Return Value:
  236. N/A
  237. --*/
  238. {
  239. ASSERT(m_fInitialized);
  240. if (m_fInitialized)
  241. {
  242. CBitmap * pBmp = (CBitmap *)CGdiObject::FromHandle(m_hOldBitmap);
  243. ASSERT_READ_PTR(pBmp);
  244. VERIFY(m_dcFinal.SelectObject(pBmp));
  245. VERIFY(m_dcFinal.DeleteDC());
  246. VERIFY(m_bmpScreen.DeleteObject());
  247. m_fInitialized = FALSE;
  248. }
  249. }
  250. void
  251. CRMCListBoxResources::PrepareBitmaps()
  252. /*++
  253. Routine Description:
  254. Prepare 2 rows of bitmaps. One with the selection colour background,
  255. and one with the ordinary listbox background.
  256. Arguments:
  257. None
  258. Return Value:
  259. None
  260. --*/
  261. {
  262. ASSERT(m_idBitmap);
  263. //
  264. // Clean up if we were already initialised
  265. //
  266. if (m_fInitialized)
  267. {
  268. UnprepareBitmaps();
  269. }
  270. //
  271. // create device contexts compatible with screen
  272. //
  273. CDC dcImage;
  274. CDC dcMasks;
  275. VERIFY(dcImage.CreateCompatibleDC(0));
  276. VERIFY(dcMasks.CreateCompatibleDC(0));
  277. VERIFY(m_dcFinal.CreateCompatibleDC(0));
  278. CBitmap bitmap;
  279. VERIFY(bitmap.LoadBitmap(m_idBitmap));
  280. BITMAP bm;
  281. VERIFY(bitmap.GetObject(sizeof(BITMAP), &bm));
  282. //
  283. // Each bitmap is assumed to be the same size.
  284. //
  285. m_nBitmapWidth = bm.bmWidth / m_nBitmaps;
  286. ASSERT(m_nBitmapWidth > 0);
  287. const int bmWidth = bm.bmWidth;
  288. const int bmHeight = bm.bmHeight;
  289. m_nBitmapHeight = bmHeight;
  290. CBitmap * pOldImageBmp = dcImage.SelectObject(&bitmap);
  291. ASSERT_PTR(pOldImageBmp);
  292. CBitmap bmpMasks;
  293. VERIFY(bmpMasks.CreateBitmap(bmWidth, bmHeight * 2, 1, 1, NULL));
  294. CBitmap * pOldMaskBmp = (CBitmap *)dcMasks.SelectObject(&bmpMasks);
  295. ASSERT_PTR(pOldMaskBmp);
  296. //
  297. // create the foreground and object masks
  298. //
  299. COLORREF crOldBk = dcImage.SetBkColor(m_rgbColorTransparent);
  300. dcMasks.BitBlt(0, 0, bmWidth, bmHeight, &dcImage, 0, 0, SRCCOPY);
  301. dcMasks.BitBlt(0, 0, bmWidth, bmHeight, &dcImage, 0, bmHeight, SRCAND);
  302. dcImage.SetBkColor(crOldBk);
  303. dcMasks.BitBlt(0, bmHeight, bmWidth, bmHeight, &dcMasks, 0, 0, NOTSRCCOPY);
  304. //
  305. // create DC to hold final image
  306. //
  307. VERIFY(m_bmpScreen.CreateCompatibleBitmap(&dcImage, bmWidth, bmHeight * 2));
  308. CBitmap * pOldBmp = (CBitmap*)m_dcFinal.SelectObject(&m_bmpScreen);
  309. ASSERT_PTR(pOldBmp);
  310. m_hOldBitmap = pOldBmp->m_hObject;
  311. CBrush b1, b2;
  312. VERIFY(b1.CreateSolidBrush(m_rgbColorHighlight));
  313. VERIFY(b2.CreateSolidBrush(m_rgbColorWindow));
  314. m_dcFinal.FillRect(CRect(0, 0, bmWidth, bmHeight), &b1);
  315. m_dcFinal.FillRect(CRect(0, bmHeight, bmWidth, bmHeight * 2), &b2);
  316. //
  317. // mask out the object pixels in the destination
  318. //
  319. m_dcFinal.BitBlt(0, 0, bmWidth, bmHeight, &dcMasks, 0, 0, SRCAND);
  320. //
  321. // mask out the background pixels in the image
  322. //
  323. dcImage.BitBlt(0, 0, bmWidth, bmHeight, &dcMasks, 0, bmHeight, SRCAND);
  324. //
  325. // XOR the revised image into the destination
  326. //
  327. m_dcFinal.BitBlt(0, 0, bmWidth, bmHeight, &dcImage, 0, 0, SRCPAINT);
  328. //
  329. // mask out the object pixels in the destination
  330. //
  331. m_dcFinal.BitBlt(0, bmHeight, bmWidth, bmHeight, &dcMasks, 0, 0, SRCAND);
  332. //
  333. // XOR the revised image into the destination
  334. //
  335. m_dcFinal.BitBlt(0, bmHeight, bmWidth, bmHeight, &dcImage, 0, 0, SRCPAINT);
  336. VERIFY(dcMasks.SelectObject(pOldMaskBmp));
  337. VERIFY(dcImage.SelectObject(pOldImageBmp));
  338. //
  339. // The result of all of this mucking about is a bitmap identical with the
  340. // one loaded from the resources but with the lower row of bitmaps having
  341. // their background changed from transparent1 to the window background
  342. // and the upper row having their background changed from transparent2 to
  343. // the highlight colour. A derived CRMCListBox can BitBlt the relevant part
  344. // of the image into an item's device context for a transparent bitmap
  345. // effect which does not take any extra time over a normal BitBlt.
  346. //
  347. m_fInitialized = TRUE;
  348. }
  349. void
  350. CRMCListBoxResources::SysColorChanged()
  351. /*++
  352. Routine Description:
  353. Respond to change in system colours by rebuilding the resources
  354. Arguments:
  355. None
  356. Return Value:
  357. None
  358. --*/
  359. {
  360. //
  361. // Reinitialise bitmaps and syscolors. This should be called from
  362. // the parent of the CRMCListBoxResources object from
  363. // the OnSysColorChange() function.
  364. //
  365. GetSysColors();
  366. PrepareBitmaps();
  367. }
  368. void
  369. CRMCListBoxResources::GetSysColors()
  370. /*++
  371. Routine Description:
  372. Get sytem colours
  373. Arguments:
  374. None
  375. Return Value:
  376. None
  377. --*/
  378. {
  379. m_rgbColorWindow = ::GetSysColor(COLOR_WINDOW);
  380. m_rgbColorHighlight = ::GetSysColor(COLOR_HIGHLIGHT);
  381. m_rgbColorWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
  382. m_rgbColorHighlightText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
  383. }
  384. CRMCListBoxDrawStruct::CRMCListBoxDrawStruct(
  385. IN CDC * pDC,
  386. IN RECT * pRect,
  387. IN BOOL sel,
  388. IN DWORD_PTR item,
  389. IN int itemIndex,
  390. IN const CRMCListBoxResources * pres
  391. )
  392. /*++
  393. Routine Description:
  394. Constructor
  395. Arguments:
  396. CDC * pdc : Device context
  397. RECT * pRect : Rectange to paint into
  398. BOOL sel : TRUE if selected
  399. DWORD item : item
  400. int itemIndex : item index
  401. const CRMCListBoxResources * pres : Pointer to resources
  402. Return Value:
  403. N/A
  404. --*/
  405. : m_pDC(pDC),
  406. m_Sel(sel),
  407. m_ItemData(item),
  408. m_ItemIndex(itemIndex),
  409. m_pResources(pres)
  410. {
  411. m_Rect.CopyRect(pRect);
  412. }
  413. CODLBox::CODLBox()
  414. /*++
  415. Routine Description:
  416. Constructor for CODLBox -- abstract base class for both CRMCComboBox,
  417. and CRMCListBox
  418. Arguments:
  419. None
  420. Return Value:
  421. N/A
  422. --*/
  423. : m_lfHeight(0),
  424. m_pResources(NULL),
  425. m_auTabs(),
  426. m_pWnd(NULL)
  427. {
  428. }
  429. CODLBox::~CODLBox()
  430. /*++
  431. Routine Description:
  432. Destructor
  433. Arguments:
  434. N/A
  435. Return Value:
  436. N/A
  437. --*/
  438. {
  439. }
  440. /* virtual */
  441. BOOL
  442. CODLBox::Initialize()
  443. /*++
  444. Routine Description:
  445. Listbox/combobox is being created
  446. Arguments:
  447. None
  448. Return Value:
  449. TRUE for success, FALSE for failure
  450. --*/
  451. {
  452. //
  453. // Derived control must be attached at this point
  454. //
  455. ASSERT_PTR(m_pWnd);
  456. //
  457. // GetFont returns non NULL when the control is in a dialog box
  458. //
  459. CFont * pFont = m_pWnd->GetFont();
  460. if(pFont == NULL)
  461. {
  462. LOGFONT lf;
  463. CFont fontTmp;
  464. ::GetObject(::GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), &lf);
  465. fontTmp.CreateFontIndirect(&lf);
  466. CalculateTextHeight(&fontTmp);
  467. }
  468. else
  469. {
  470. CalculateTextHeight(pFont);
  471. }
  472. return TRUE;
  473. }
  474. BOOL
  475. CODLBox::ChangeFont(
  476. IN CFont * pFont
  477. )
  478. /*++
  479. Routine Description:
  480. Change the control font the specified font
  481. Arguments:
  482. CFont * pFont : Pointer to the new font to be used
  483. Return Value:
  484. TRUE for success, FALSE for failure
  485. --*/
  486. {
  487. ASSERT_PTR(m_pResources);
  488. ASSERT_PTR(m_pWnd);
  489. if( pFont == NULL || m_pResources == NULL
  490. || m_pWnd == NULL || m_pWnd->m_hWnd == NULL
  491. )
  492. {
  493. TRACEEOLID("Invalid state of the control. Can't change font");
  494. return FALSE;
  495. }
  496. //
  497. // Don't reflect changes immediately
  498. //
  499. m_pWnd->SetRedraw(FALSE);
  500. m_pWnd->SetFont(pFont, TRUE);
  501. CalculateTextHeight(pFont);
  502. int nItems = __GetCount();
  503. int bmHeight = m_pResources->BitmapHeight();
  504. int nHeight = bmHeight > m_lfHeight ? bmHeight : m_lfHeight;
  505. for(int i = 0; i < nItems; ++i)
  506. {
  507. __SetItemHeight(i, nHeight);
  508. }
  509. //
  510. // Now reflect the change visually
  511. //
  512. m_pWnd->SetRedraw(TRUE);
  513. m_pWnd->Invalidate();
  514. return TRUE;
  515. }
  516. void
  517. CODLBox::AttachResources(
  518. IN const CRMCListBoxResources * pRes
  519. )
  520. /*++
  521. Routine Description:
  522. Attach the bitmaps
  523. Arguments:
  524. const CRMCListBoxResources * pRes : pointer to resources to be attached
  525. Return Value:
  526. None
  527. --*/
  528. {
  529. if(pRes != m_pResources)
  530. {
  531. ASSERT_READ_PTR(pRes);
  532. m_pResources = pRes;
  533. if(m_pWnd != NULL && m_pWnd->m_hWnd != NULL)
  534. {
  535. //
  536. // if window was created already, redraw everything.
  537. //
  538. m_pWnd->Invalidate();
  539. }
  540. }
  541. }
  542. /* static */
  543. int
  544. CODLBox::GetRequiredWidth(
  545. IN CDC * pDC,
  546. IN const CRect & rc,
  547. IN LPCTSTR lpstr,
  548. IN int nLength
  549. )
  550. /*++
  551. Routine Description:
  552. Determine required display width of the string
  553. Arguments:
  554. CDC * pDC : Pointer to device context to use
  555. const CRect & rc : Starting rectangle
  556. LPCTSTR lpstr : String whose width is to be displayed
  557. int nLength : Length (in characters of the string
  558. Return Value:
  559. The display width that the string would need to be displayed on the
  560. given device context
  561. --*/
  562. {
  563. #ifdef _DEBUG
  564. pDC->AssertValid();
  565. #endif // _DEBUG
  566. CRect rcTmp(rc);
  567. pDC->DrawText(
  568. lpstr,
  569. nLength,
  570. &rcTmp,
  571. DT_CALCRECT | DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER
  572. );
  573. return rcTmp.Width();
  574. }
  575. /* static */
  576. BOOL
  577. CODLBox::ColumnText(
  578. IN CDC * pDC,
  579. IN int nLeft,
  580. IN int nTop,
  581. IN int nRight,
  582. IN int nBottom,
  583. IN LPCTSTR lpstr
  584. )
  585. /*++
  586. Routine Description:
  587. Display text limited by a rectangle. Use ellipses if the text is too wide
  588. to fit inside the given dimensions.
  589. Arguments:
  590. CDC * pDC : Pointer to display context to use
  591. int nLeft : Left coordinate
  592. int nTop : Top coordinate
  593. int nRight : Right coordinate
  594. int nBottom : Bottom coordinate
  595. LPCTSTR lpstr : String to be displayed
  596. Return Value:
  597. TRUE for success, FALSE for failure
  598. --*/
  599. {
  600. BOOL fSuccess = TRUE;
  601. #ifdef _DEBUG
  602. pDC->AssertValid();
  603. #endif // _DEBUG
  604. CString str;
  605. CRect rc(nLeft, nTop, nRight, nBottom);
  606. int nAvailWidth = rc.Width();
  607. int nLength = ::lstrlen(lpstr);
  608. try
  609. {
  610. if (GetRequiredWidth(pDC, rc, lpstr, nLength) <= nAvailWidth)
  611. {
  612. //
  613. // Sufficient space, display as is.
  614. //
  615. str = lpstr;
  616. }
  617. else
  618. {
  619. //
  620. // Build a string with ellipses until it
  621. // fits
  622. //
  623. LPTSTR lpTmp = str.GetBuffer(nLength + g_nLenEllipses);
  624. while (nLength)
  625. {
  626. ::lstrcpyn(lpTmp, lpstr, nLength);
  627. ::lstrcpy(lpTmp + nLength - 1, g_szEllipses);
  628. if (GetRequiredWidth(pDC, rc, lpTmp,
  629. nLength + g_nLenEllipses) <= nAvailWidth)
  630. {
  631. break;
  632. }
  633. --nLength;
  634. }
  635. str = lpTmp;
  636. }
  637. pDC->DrawText(
  638. str,
  639. &rc,
  640. DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER
  641. );
  642. }
  643. catch(CMemoryException * e)
  644. {
  645. //
  646. // Mem failure
  647. //
  648. fSuccess = FALSE;
  649. e->ReportError();
  650. e->Delete();
  651. }
  652. return fSuccess;
  653. }
  654. void
  655. CODLBox::ComputeMargins(
  656. IN CRMCListBoxDrawStruct & ds,
  657. IN int nCol,
  658. OUT int & nLeft,
  659. OUT int & nRight
  660. )
  661. /*++
  662. Routine Description:
  663. Compute the left and right margins of the given column.
  664. Arguments:
  665. CRMCListBoxDrawStruct & ds : Drawing structure
  666. int nCol : Column whose margins we're interested in
  667. int & nLeft : Left column
  668. int & nRight : Right column
  669. Return Value:
  670. None
  671. --*/
  672. {
  673. nLeft = ds.m_Rect.left;
  674. nRight = ds.m_Rect.right;
  675. //
  676. // Find tab value associated with column index (0-based),
  677. // and adjust left and right
  678. //
  679. ASSERT(nCol <= NumTabs());
  680. if (nCol > 0)
  681. {
  682. if (nCol <= NumTabs())
  683. {
  684. nLeft += m_auTabs[nCol-1];
  685. }
  686. }
  687. if (nCol < NumTabs())
  688. {
  689. nRight = m_auTabs[nCol];
  690. }
  691. }
  692. BOOL
  693. CODLBox::DrawBitmap(
  694. IN CRMCListBoxDrawStruct & ds,
  695. IN int nCol,
  696. IN int nID
  697. )
  698. /*++
  699. Routine Description:
  700. Draw a bitmap in the given column. Bitmap are always placed on the
  701. leftmost side of the column if there is sufficient space.
  702. Arguments:
  703. CRMCListBoxDrawStruct & ds : Drawing structure
  704. int nCol : Column to place bitmap in
  705. int nID : Bitmap ID (offset within the bitmap resources)
  706. Return Value:
  707. None
  708. --*/
  709. {
  710. CDC * pBmpDC = (CDC *)&ds.m_pResources->dcBitMap();
  711. #ifdef _DEBUG
  712. pBmpDC->AssertValid();
  713. #endif // _DEBUG
  714. //
  715. // Select the bitmap with either a selection or
  716. // a regular background
  717. //
  718. int bm_h = ds.m_Sel ? 0 : ds.m_pResources->BitmapHeight();
  719. int bm_w = ds.m_pResources->BitmapWidth() * nID;
  720. int nLeft, nRight;
  721. ComputeMargins(ds, nCol, nLeft, nRight);
  722. nLeft += BMP_LEFT_OFFSET;
  723. //
  724. // Check to make sure there's enough room before
  725. // drawing the bitmap.
  726. //
  727. if (nRight - nLeft >= ds.m_pResources->BitmapWidth())
  728. {
  729. ds.m_pDC->BitBlt(
  730. nLeft,
  731. ds.m_Rect.top,
  732. ds.m_pResources->BitmapWidth(),
  733. ds.m_pResources->BitmapHeight(),
  734. pBmpDC,
  735. bm_w,
  736. bm_h,
  737. SRCCOPY
  738. );
  739. }
  740. return TRUE;
  741. }
  742. BOOL
  743. CODLBox::ColumnText(
  744. IN CRMCListBoxDrawStruct & ds,
  745. IN int nCol,
  746. IN BOOL fSkipBitmap,
  747. IN LPCTSTR lpstr
  748. )
  749. /*++
  750. Routine Description:
  751. Draw column text.
  752. Arguments:
  753. CRMCListBoxDrawStruct & ds : Drawing structure
  754. int nCol : Column to place bitmap in
  755. BOOL fSkipBitmap : If TRUE, increment lefthand column by the width
  756. of a bitmap
  757. LPCTSTR lpstr : String to be displayed. May be truncated as
  758. necessary
  759. Return Value:
  760. TRUE for success, FALSE for failure
  761. --*/
  762. {
  763. int nLeft, nRight;
  764. ComputeMargins(ds, nCol, nLeft, nRight);
  765. //
  766. // Optionally adjust for bitmap
  767. //
  768. if (fSkipBitmap)
  769. {
  770. nLeft += (ds.m_pResources->BitmapWidth() + BMP_RIGHT_OFFSET);
  771. }
  772. return CODLBox::ColumnText(
  773. ds.m_pDC,
  774. nLeft,
  775. ds.m_Rect.top,
  776. nRight,
  777. ds.m_Rect.bottom,
  778. lpstr
  779. );
  780. }
  781. void
  782. CODLBox::CalculateTextHeight(
  783. IN CFont * pFont
  784. )
  785. /*++
  786. Routine Description:
  787. Calculate and set the text height of font
  788. Arguments:
  789. CFont * pFont : Pointer to the font to be used.
  790. Return Value:
  791. None
  792. --*/
  793. {
  794. ASSERT_PTR(m_pWnd);
  795. CClientDC dc(m_pWnd);
  796. CFont * pOldFont = dc.SelectObject(pFont);
  797. TEXTMETRIC tm;
  798. dc.GetTextMetrics(&tm);
  799. m_lfHeight = tm.tmHeight;
  800. dc.SelectObject(pOldFont);
  801. }
  802. int
  803. CODLBox::AddTab(
  804. IN UINT uTab
  805. )
  806. /*++
  807. Routine Description:
  808. Add a tab to the end of the list (e.g the right side of the header)
  809. Arguments:
  810. UINT uTab : Tab value to set
  811. Return Value:
  812. The index of the new tab
  813. --*/
  814. {
  815. return (int)m_auTabs.Add(uTab);
  816. }
  817. int
  818. CODLBox::AddTabFromHeaders(
  819. IN CWnd & wndLeft,
  820. IN CWnd & wndRight
  821. )
  822. /*++
  823. Routine Description:
  824. Add a tab to the end of the list (e.g the right side of the header),
  825. but compute the tab by taking the difference in left-hand coordinat of two
  826. window controls (usually static header text)
  827. Arguments:
  828. CWnd & wndLeft : Left window
  829. CWnd & wndRight : Right window
  830. Return Value:
  831. The index of the new tab
  832. --*/
  833. {
  834. CRect rcLeft, rcRight;
  835. wndLeft.GetWindowRect(&rcLeft);
  836. wndRight.GetWindowRect(&rcRight);
  837. ASSERT(rcRight.left > rcLeft.left);
  838. return AddTab(rcRight.left - rcLeft.left - 1);
  839. }
  840. int
  841. CODLBox::AddTabFromHeaders(
  842. IN UINT idLeft,
  843. IN UINT idRight
  844. )
  845. /*++
  846. Routine Description:
  847. Similar to the function above, but use the control IDs. The parent
  848. window is assumed to be the same as the parent window of the listbox
  849. Arguments:
  850. UINT idLeft : ID of the left control
  851. UINT idRight : ID of the right control
  852. Return Value:
  853. The index of the new tab or -1 in case of failure
  854. --*/
  855. {
  856. ASSERT_PTR(m_pWnd);
  857. if (m_pWnd == NULL)
  858. {
  859. //
  860. // Should have associated window handle by now
  861. //
  862. return -1;
  863. }
  864. CWnd * pLeft = m_pWnd->GetParent()->GetDlgItem(idLeft);
  865. CWnd * pRight = m_pWnd->GetParent()->GetDlgItem(idRight);
  866. ASSERT_READ_PTR(pLeft);
  867. ASSERT_READ_PTR(pRight);
  868. if (!pLeft || !pRight)
  869. {
  870. //
  871. // One or both control IDs were not valid
  872. //
  873. return -1;
  874. }
  875. return AddTabFromHeaders(*pLeft, *pRight);
  876. }
  877. void
  878. CODLBox::InsertTab(
  879. IN int nIndex,
  880. IN UINT uTab
  881. )
  882. /*++
  883. Routine Description:
  884. Insert a tab at the given index
  885. Arguments:
  886. int nIndex : Column index at which the tab is to be inserted
  887. UINT uTab : Tab value to set
  888. Return Value:
  889. None
  890. --*/
  891. {
  892. m_auTabs.InsertAt(nIndex, uTab);
  893. }
  894. void
  895. CODLBox::RemoveTab(
  896. IN int nIndex,
  897. IN int nCount
  898. )
  899. /*++
  900. Routine Description:
  901. Remove one or more tabs
  902. Arguments:
  903. int nIndex : Column index at which to start removing tabs
  904. int nCount : Number of tabs to be removed
  905. Return Value:
  906. None
  907. --*/
  908. {
  909. m_auTabs.RemoveAt(nIndex, nCount);
  910. }
  911. void
  912. CODLBox::RemoveAllTabs()
  913. /*++
  914. Routine Description:
  915. Remove all tabs
  916. Arguments:
  917. None
  918. Return Value:
  919. None
  920. --*/
  921. {
  922. m_auTabs.RemoveAll();
  923. }
  924. void
  925. CODLBox::__DrawItem(
  926. IN LPDRAWITEMSTRUCT lpDIS
  927. )
  928. /*++
  929. Routine Description:
  930. Draw an item. This will draw the focus and selection state, and then
  931. call out to the derived class to draw the item.
  932. Arguments:
  933. LPDRAWITEMSTRUCT lpDIS : The drawitem structure
  934. Return Value:
  935. None
  936. --*/
  937. {
  938. //
  939. // Need to attach resources before creation/adding items
  940. //
  941. ASSERT_PTR(m_pResources);
  942. CDC * pDC = CDC::FromHandle(lpDIS->hDC);
  943. #ifdef _DEBUG
  944. pDC->AssertValid();
  945. #endif // _DEBUG
  946. //
  947. // Draw focus rectangle when no items in listbox
  948. //
  949. if(lpDIS->itemID == (UINT)-1)
  950. {
  951. if(lpDIS->itemAction & ODA_FOCUS)
  952. {
  953. //
  954. // rcItem.bottom seems to be 0 for variable height list boxes
  955. //
  956. lpDIS->rcItem.bottom = m_lfHeight;
  957. pDC->DrawFocusRect(&lpDIS->rcItem);
  958. }
  959. return;
  960. }
  961. else
  962. {
  963. BOOL fSelChange = (lpDIS->itemAction & ODA_SELECT) != 0;
  964. BOOL fFocusChange = (lpDIS->itemAction & ODA_FOCUS) != 0;
  965. BOOL fDrawEntire = (lpDIS->itemAction & ODA_DRAWENTIRE) != 0;
  966. if(fSelChange || fDrawEntire)
  967. {
  968. BOOL fSel = (lpDIS->itemState & ODS_SELECTED) != 0;
  969. COLORREF hlite = (fSel ? (m_pResources->ColorHighlight())
  970. : (m_pResources->ColorWindow()));
  971. COLORREF textcol = (fSel ? (m_pResources->ColorHighlightText())
  972. : (m_pResources->ColorWindowText()));
  973. pDC->SetBkColor(hlite);
  974. pDC->SetTextColor(textcol);
  975. //
  976. // fill the rectangle with the background colour.
  977. //
  978. pDC->ExtTextOut(0, 0, ETO_OPAQUE, &lpDIS->rcItem, NULL, 0, NULL);
  979. CRMCListBoxDrawStruct ds(pDC,
  980. (RECT *)&lpDIS->rcItem,
  981. fSel,
  982. (DWORD_PTR)lpDIS->itemData,
  983. lpDIS->itemID,
  984. m_pResources
  985. );
  986. //
  987. // Now call the draw function of the derived class
  988. //
  989. DrawItemEx(ds);
  990. }
  991. if (fFocusChange || (fDrawEntire && (lpDIS->itemState & ODS_FOCUS)))
  992. {
  993. pDC->DrawFocusRect(&lpDIS->rcItem);
  994. }
  995. }
  996. }
  997. void
  998. CODLBox::__MeasureItem(
  999. IN OUT LPMEASUREITEMSTRUCT lpMIS
  1000. )
  1001. /*++
  1002. Routine Description:
  1003. Provide dimensions of given item
  1004. Arguments:
  1005. LPMEASUREITEMSTRUCT lpMIS : Measure item structure
  1006. Return Value:
  1007. None
  1008. --*/
  1009. {
  1010. ASSERT_PTR(m_pResources);
  1011. // int h = lpMIS->itemHeight;
  1012. int ch = TextHeight();
  1013. int bmHeight = m_pResources->BitmapHeight();
  1014. lpMIS->itemHeight = ch < bmHeight ? bmHeight : ch;
  1015. }
  1016. CRMCListBoxHeader::CRMCListBoxHeader(
  1017. IN DWORD dwStyle
  1018. )
  1019. /*++
  1020. Routine Description:
  1021. Constructor.
  1022. Arguments:
  1023. DWORD dwStyle : Style bits
  1024. Return Value:
  1025. N/A
  1026. --*/
  1027. : m_pHCtrl(NULL),
  1028. m_pListBox(NULL),
  1029. m_dwStyle(dwStyle),
  1030. m_fRespondToColumnWidthChanges(TRUE)
  1031. {
  1032. m_pHCtrl = new CHeaderCtrl;
  1033. }
  1034. CRMCListBoxHeader::~CRMCListBoxHeader()
  1035. /*++
  1036. Routine Description:
  1037. Destructor.
  1038. Arguments:
  1039. N/A
  1040. Return Value:
  1041. N/A
  1042. --*/
  1043. {
  1044. //
  1045. // Kill the header control and the
  1046. // font
  1047. //
  1048. if (m_pHCtrl)
  1049. {
  1050. delete m_pHCtrl;
  1051. }
  1052. //
  1053. // Leave the listbox pointer alone, as we don't
  1054. // own it, but are merely associated with it.
  1055. //
  1056. }
  1057. //
  1058. // Message Map
  1059. //
  1060. BEGIN_MESSAGE_MAP(CRMCListBoxHeader, CStatic)
  1061. //{{AFX_MSG_MAP(CRMCListBoxHeader)
  1062. ON_WM_DESTROY()
  1063. //}}AFX_MSG_MAP
  1064. ON_NOTIFY_RANGE(HDN_ENDTRACK, 0, 0xFFFF, OnHeaderEndTrack)
  1065. ON_NOTIFY_RANGE(HDN_ITEMCHANGED, 0, 0xFFFF, OnHeaderItemChanged)
  1066. ON_NOTIFY_RANGE(HDN_ITEMCLICK, 0, 0xFFFF, OnHeaderItemClick)
  1067. END_MESSAGE_MAP()
  1068. void
  1069. CRMCListBoxHeader::OnDestroy()
  1070. /*++
  1071. Routine Description:
  1072. WM_DESTROY message handler. When the control is being destroyed,
  1073. also destroy the invisible static control.
  1074. Arguments:
  1075. None
  1076. Return Value:
  1077. None
  1078. --*/
  1079. {
  1080. //
  1081. // Destroy optional header control
  1082. //
  1083. if (m_pHCtrl)
  1084. {
  1085. m_pHCtrl->DestroyWindow();
  1086. }
  1087. CStatic::OnDestroy();
  1088. }
  1089. BOOL
  1090. CRMCListBoxHeader::Create(
  1091. IN DWORD dwStyle,
  1092. IN const RECT & rect,
  1093. IN CWnd * pParentWnd,
  1094. IN CHeaderListBox * pListBox,
  1095. IN UINT nID
  1096. )
  1097. /*++
  1098. Routine Description:
  1099. Create the control. This will first create an invisible static window,
  1100. which is to take up the entire area of the listbox. This static window
  1101. then will be the parent to the listbox as well as this header control.
  1102. Arguments:
  1103. DWORD dwStyle : Creation style bits
  1104. const RECT & rect : Rectangle in which the header is to be created
  1105. CWnd * pParentWnd : Parent window
  1106. CHeaderListBox * pListBox : Associated listbox
  1107. UINT nID : Control ID of the header
  1108. Return Value:
  1109. TRUE for success, FALSE for failure
  1110. --*/
  1111. {
  1112. //
  1113. // Make sure the real header control exists by now
  1114. //
  1115. if (m_pHCtrl == NULL)
  1116. {
  1117. return FALSE;
  1118. }
  1119. //
  1120. // Make sure there's an associated listbox
  1121. //
  1122. m_pListBox = pListBox;
  1123. if (m_pListBox == NULL)
  1124. {
  1125. return FALSE;
  1126. }
  1127. //
  1128. // Create the controlling static window as do-nothing window
  1129. //
  1130. if (!CStatic::Create(NULL, WS_VISIBLE | SS_BITMAP | WS_CHILD,
  1131. rect, pParentWnd, 0xFFFF))
  1132. {
  1133. return FALSE;
  1134. }
  1135. //
  1136. // Now create the header control. Its parent
  1137. // window is this static control we just created
  1138. //
  1139. CRect rc(0, 0, 0 ,0);
  1140. dwStyle |= (UseButtons() ? HDS_BUTTONS : 0L);
  1141. VERIFY(m_pHCtrl->Create(dwStyle, rc, this, nID));
  1142. //
  1143. // Place header control as per style bits,
  1144. // compute the desired layout, and move it
  1145. //
  1146. HD_LAYOUT hdl;
  1147. WINDOWPOS wp;
  1148. GetClientRect(&rc);
  1149. hdl.prc = &rc;
  1150. hdl.pwpos = &wp;
  1151. m_pHCtrl->Layout(&hdl);
  1152. m_pHCtrl->SetWindowPos(m_pListBox, wp.x, wp.y,
  1153. wp.cx, wp.cy, wp.flags | SWP_SHOWWINDOW);
  1154. //
  1155. // And move our associated listbox just below it
  1156. //
  1157. ::GetDlgCtlRect(GetParent()->m_hWnd, m_pListBox->m_hWnd, &rc);
  1158. rc.top += wp.cy - 1;
  1159. //
  1160. // Adjust if header is bigger than the entire listbox
  1161. //
  1162. if (rc.top > rc.bottom)
  1163. {
  1164. rc.top = rc.bottom;
  1165. }
  1166. // Fix for theme support. Make listbox and header children of the same static control
  1167. m_pListBox->SetParent(this);
  1168. GetParent()->ClientToScreen(&rc);
  1169. ScreenToClient(&rc);
  1170. m_pListBox->MoveWindow(rc.left, rc.top, rc.Width(), rc.Height());
  1171. //
  1172. // Make sure the header uses the right font
  1173. //
  1174. m_pHCtrl->SetFont(
  1175. CFont::FromHandle((HFONT)::GetStockObject(DEFAULT_GUI_FONT)),
  1176. FALSE
  1177. );
  1178. return TRUE;
  1179. }
  1180. void
  1181. CRMCListBoxHeader::OnHeaderEndTrack(
  1182. IN UINT nId,
  1183. IN NMHDR * pnmh,
  1184. OUT LRESULT * pResult
  1185. )
  1186. /*++
  1187. Routine Description:
  1188. User has finished dragging the column divider. If we're supposed to ensure
  1189. that the last column is a stretch column, turn off the redraw now -- it
  1190. will get turned back on after the column width changes have all been
  1191. completed. This will reduce the flash effect.
  1192. Arguments:
  1193. UINT nId : Control ID
  1194. NMHDR * pnmh : Notification header structure
  1195. LRESULT * pResult : Result. Will be set to 0 if the message was handled
  1196. Return Value:
  1197. None (handled in pResult)
  1198. --*/
  1199. {
  1200. pnmh;
  1201. nId;
  1202. if (DoesRespondToColumnWidthChanges() && UseStretch())
  1203. {
  1204. //
  1205. // This will get turned back on in OnHeaderItemChanged
  1206. //
  1207. SetRedraw(FALSE);
  1208. }
  1209. *pResult = 0;
  1210. }
  1211. void
  1212. CRMCListBoxHeader::SetColumnWidth(
  1213. IN int nCol,
  1214. IN int nWidth
  1215. )
  1216. /*++
  1217. Routine Description:
  1218. Set the given column to the given width
  1219. Arguments:
  1220. int nCol : Column number
  1221. int nWidth : New width
  1222. Return Value:
  1223. None
  1224. --*/
  1225. {
  1226. ASSERT(nCol < QueryNumColumns());
  1227. if (nCol >= QueryNumColumns())
  1228. {
  1229. return;
  1230. }
  1231. TRACEEOLID("Setting width of column " << nCol << " to " << nWidth);
  1232. HD_ITEM hdItem;
  1233. hdItem.mask = HDI_WIDTH;
  1234. hdItem.cxy = nWidth;
  1235. VERIFY(SetItem(nCol, &hdItem));
  1236. }
  1237. void
  1238. CRMCListBoxHeader::OnHeaderItemChanged(
  1239. IN UINT nId,
  1240. IN NMHDR *pnmh,
  1241. OUT LRESULT *pResult
  1242. )
  1243. /*++
  1244. Routine Description:
  1245. Handle change in header column width. Note: we're actually tracking
  1246. the HDN_ITEMCHANGED notification, not the HDN_ENDDRAG one, because
  1247. the latter is sent out before the column widths in the structure have
  1248. changed.
  1249. Arguments:
  1250. UINT nId : Control ID
  1251. NMHDR * pnmh : Notification header structure
  1252. LRESULT * pResult : Result. Will be set to 0 if the message was handled
  1253. Return Value:
  1254. None (handled in pResult)
  1255. --*/
  1256. {
  1257. nId;
  1258. //
  1259. // Adjust tabs in associate listbox if
  1260. // column widths have changed
  1261. //
  1262. HD_NOTIFY * pNotify = (HD_NOTIFY *)pnmh;
  1263. if (DoesRespondToColumnWidthChanges() && pNotify->pitem->mask & HDI_WIDTH)
  1264. {
  1265. ASSERT_PTR(m_pListBox);
  1266. //
  1267. // Stretch the last column
  1268. //
  1269. if (UseStretch())
  1270. {
  1271. //
  1272. // Turn this off, as we don't want
  1273. // to get in an infinite loop
  1274. //
  1275. RespondToColumnWidthChanges(FALSE);
  1276. //
  1277. // Compute available space
  1278. //
  1279. CRect rc;
  1280. GetClientRect(&rc);
  1281. //
  1282. // See how much is taken up by preceding
  1283. // columns
  1284. //
  1285. int nTotalWidth = 0;
  1286. int cColumns = QueryNumColumns();
  1287. int nLastCol = cColumns - 1;
  1288. ASSERT(nLastCol >= 0);
  1289. for (int nCol = 0; nCol < nLastCol; ++nCol)
  1290. {
  1291. int nWidth = GetColumnWidth(nCol);
  1292. //
  1293. // Each column must be at least one pixel wide
  1294. //
  1295. int nMaxWidth = rc.Width() - nTotalWidth - (nLastCol - nCol);
  1296. if (nWidth > nMaxWidth)
  1297. {
  1298. nWidth = nMaxWidth;
  1299. SetColumnWidth(nCol, nWidth);
  1300. }
  1301. nTotalWidth += nWidth;
  1302. }
  1303. //
  1304. // Make sure the last column takes up the rest
  1305. //
  1306. if (rc.Width() > nTotalWidth)
  1307. {
  1308. SetColumnWidth(nLastCol, rc.Width() - nTotalWidth);
  1309. }
  1310. //
  1311. // Turn this back on again
  1312. //
  1313. RespondToColumnWidthChanges(TRUE);
  1314. //
  1315. // Redraw will have been turned off in
  1316. // OnHeaderEndTrack, now that all column
  1317. // movement has completed, turn it back
  1318. // on to draw the control in its current
  1319. // state.
  1320. //
  1321. SetRedraw(TRUE);
  1322. Invalidate();
  1323. }
  1324. //
  1325. // Recompute tabs on associate listbox,
  1326. // and force redraw on it.
  1327. //
  1328. m_pListBox->SetRedraw(FALSE);
  1329. SetTabsFromHeader();
  1330. m_pListBox->SetRedraw(TRUE);
  1331. m_pListBox->Invalidate();
  1332. }
  1333. *pResult = 0;
  1334. }
  1335. void
  1336. CRMCListBoxHeader::OnHeaderItemClick(
  1337. IN UINT nId,
  1338. IN NMHDR *pnmh,
  1339. OUT LRESULT *pResult
  1340. )
  1341. /*++
  1342. Routine Description:
  1343. A button has been clicked in the header control. Pass it on
  1344. to the real parent window.
  1345. Arguments:
  1346. UINT nId : Control ID
  1347. NMHDR * pnmh : Notification header structure
  1348. LRESULT * pResult : Result. Will be set to 0 if the message was handled
  1349. Return Value:
  1350. None (handled in pResult)
  1351. --*/
  1352. {
  1353. //
  1354. // Pass notification on to parent
  1355. //
  1356. ASSERT(GetParent());
  1357. GetParent()->SendMessage(WM_NOTIFY, (WPARAM)nId, (LPARAM)pnmh);
  1358. *pResult = 0;
  1359. }
  1360. void
  1361. CRMCListBoxHeader::SetTabsFromHeader()
  1362. /*++
  1363. Routine Description:
  1364. Set the tabs (which are cumulative) from the header control
  1365. columns (which are not)
  1366. Arguments:
  1367. None
  1368. Return Value:
  1369. None
  1370. --*/
  1371. {
  1372. //
  1373. // Must have the same number of tabs
  1374. // as header columns
  1375. //
  1376. ASSERT_PTR(m_pListBox);
  1377. ASSERT(GetItemCount() == m_pListBox->NumTabs());
  1378. int nTab = 0;
  1379. for (int n = 0; n < m_pListBox->NumTabs(); ++n)
  1380. {
  1381. m_pListBox->SetTab(n, nTab += GetColumnWidth(n));
  1382. }
  1383. }
  1384. int
  1385. CRMCListBoxHeader::GetItemCount() const
  1386. /*++
  1387. Routine Description:
  1388. Get the number of items in the header
  1389. Arguments:
  1390. None
  1391. Return Value:
  1392. The number of items in the header (e.g. the number of columns)
  1393. --*/
  1394. {
  1395. ASSERT_PTR(m_pHCtrl);
  1396. return m_pHCtrl->GetItemCount();
  1397. }
  1398. BOOL
  1399. CRMCListBoxHeader::GetItem(
  1400. IN int nPos,
  1401. OUT HD_ITEM * pHeaderItem
  1402. ) const
  1403. /*++
  1404. Routine Description:
  1405. Get information on specific position (column index)
  1406. Arguments:
  1407. int nPos : Column index
  1408. HD_ITEM * pHeaderItem : Header item information
  1409. Return Value:
  1410. TRUE for success, FALSE for failure (bad column index)
  1411. --*/
  1412. {
  1413. ASSERT_PTR(m_pHCtrl);
  1414. return m_pHCtrl->GetItem(nPos, pHeaderItem);
  1415. }
  1416. int
  1417. CRMCListBoxHeader::GetColumnWidth(
  1418. IN int nPos
  1419. ) const
  1420. /*++
  1421. Routine Description:
  1422. Get column width of a specific column
  1423. Arguments:
  1424. int nPos : Column index
  1425. Return Value:
  1426. The column width of the given colum, or -1 in case of failure (bad
  1427. column index)
  1428. --*/
  1429. {
  1430. HD_ITEM hi;
  1431. hi.mask = HDI_WIDTH;
  1432. if (GetItem(nPos, &hi))
  1433. {
  1434. return hi.cxy;
  1435. }
  1436. return -1;
  1437. }
  1438. BOOL
  1439. CRMCListBoxHeader::SetItem(
  1440. IN int nPos,
  1441. IN HD_ITEM * pHeaderItem
  1442. )
  1443. /*++***
  1444. Routine Description:
  1445. Set information on specific position (column index)
  1446. Arguments:
  1447. int nPos : Column index
  1448. HD_ITEM * pHeaderItem : Header item information
  1449. Return Value:
  1450. TRUE for success, FALSE for failure (bad column index)
  1451. --*/
  1452. {
  1453. ASSERT_PTR(m_pHCtrl);
  1454. ASSERT_PTR(m_pListBox);
  1455. if (!m_pHCtrl->SetItem(nPos, pHeaderItem))
  1456. {
  1457. return FALSE;
  1458. }
  1459. if (pHeaderItem->mask & HDI_WIDTH)
  1460. {
  1461. SetTabsFromHeader();
  1462. }
  1463. return TRUE;
  1464. }
  1465. int
  1466. CRMCListBoxHeader::InsertItem(
  1467. IN int nPos,
  1468. IN HD_ITEM * pHeaderItem
  1469. )
  1470. /*++
  1471. Routine Description:
  1472. insert information in specific position (column index)
  1473. Arguments:
  1474. int nPos : Column index
  1475. HD_ITEM * pHeaderItem : Header item information
  1476. Return Value:
  1477. The new index, or -1 in case of failure.
  1478. --*/
  1479. {
  1480. ASSERT_PTR(m_pHCtrl);
  1481. ASSERT_PTR(m_pListBox);
  1482. int nCol = m_pHCtrl->InsertItem(nPos, pHeaderItem);
  1483. if (nCol != -1)
  1484. {
  1485. //
  1486. // Set 0-width tab, as tabs get recomputed anyway
  1487. //
  1488. m_pListBox->InsertTab(nPos, 0);
  1489. SetTabsFromHeader();
  1490. }
  1491. return nCol;
  1492. }
  1493. BOOL
  1494. CRMCListBoxHeader::DeleteItem(
  1495. IN int nPos
  1496. )
  1497. /*++
  1498. Routine Description:
  1499. Delete the given item (i.e. column)
  1500. Arguments:
  1501. int nPos : Column index
  1502. Return Value:
  1503. TRUE for success, FALSE for failure (bad column index)
  1504. --*/
  1505. {
  1506. ASSERT_PTR(m_pHCtrl);
  1507. ASSERT_PTR(m_pListBox);
  1508. if (!m_pHCtrl->DeleteItem(nPos))
  1509. {
  1510. return FALSE;
  1511. }
  1512. m_pListBox->RemoveTab(nPos, 1);
  1513. return TRUE;
  1514. }
  1515. IMPLEMENT_DYNAMIC(CRMCListBoxHeader, CStatic);
  1516. CRMCListBox::CRMCListBox()
  1517. /*++
  1518. Routine Description:
  1519. Constructor
  1520. Arguments:
  1521. None
  1522. Return Value:
  1523. N/A
  1524. --*/
  1525. : m_fInitialized(FALSE),
  1526. m_fMultiSelect(FALSE)
  1527. {
  1528. }
  1529. CRMCListBox::~CRMCListBox()
  1530. /*++
  1531. Routine Description:
  1532. Destructor
  1533. Arguments:
  1534. N/A
  1535. Return Value:
  1536. N/A
  1537. --*/
  1538. {
  1539. }
  1540. //
  1541. // Message Map
  1542. //
  1543. BEGIN_MESSAGE_MAP(CRMCListBox, CListBox)
  1544. //{{AFX_MSG_MAP(CRMCListBox)
  1545. ON_WM_CREATE()
  1546. ON_WM_DESTROY()
  1547. //}}AFX_MSG_MAP
  1548. END_MESSAGE_MAP()
  1549. /* virtual */
  1550. BOOL
  1551. CRMCListBox::Initialize()
  1552. /*++
  1553. Routine Description:
  1554. This function should be called directly when subclassing an existing
  1555. listbox, otherwise OnCreate will take care of it.
  1556. Arguments:
  1557. None
  1558. Return Value:
  1559. TRUE for success, FALSE for failure
  1560. --*/
  1561. {
  1562. //
  1563. // Make sure we're only initialized once
  1564. //
  1565. if (m_fInitialized)
  1566. {
  1567. return TRUE;
  1568. }
  1569. //
  1570. // Ensure the base class knows our window
  1571. // handle
  1572. //
  1573. AttachWindow(this);
  1574. if (!CODLBox::Initialize())
  1575. {
  1576. return FALSE;
  1577. }
  1578. m_fInitialized = TRUE;
  1579. DWORD dwStyle = GetStyle();
  1580. m_fMultiSelect = (dwStyle & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)) != 0;
  1581. return m_fInitialized;
  1582. }
  1583. void
  1584. CRMCListBox::MeasureItem(
  1585. IN LPMEASUREITEMSTRUCT lpMIS
  1586. )
  1587. /*++
  1588. Routine Description:
  1589. CListBox override to ODL base class
  1590. Arguments:
  1591. LPMEASUREITEMSTRUCT lpMIS : Measure item structure
  1592. Return Value:
  1593. None
  1594. --*/
  1595. {
  1596. CODLBox::__MeasureItem(lpMIS);
  1597. }
  1598. void
  1599. CRMCListBox::DrawItem(
  1600. IN LPDRAWITEMSTRUCT lpDIS
  1601. )
  1602. /*++
  1603. Routine Description:
  1604. CListBox override to ODL base class
  1605. Arguments:
  1606. LPDRAWITEMSTRUCT lpDIS : Drawing item structure
  1607. Return Value:
  1608. None
  1609. --*/
  1610. {
  1611. CODLBox::__DrawItem(lpDIS);
  1612. }
  1613. /* virtual */
  1614. void
  1615. CRMCListBox::DrawItemEx(
  1616. IN CRMCListBoxDrawStruct & dw
  1617. )
  1618. /*++
  1619. Routine Description:
  1620. Do-nothing extended draw function, which should
  1621. be provided by the derived class. This one will
  1622. ASSERT, and should never be called.
  1623. Arguments:
  1624. CRMCListBoxDrawStruct & dw : Draw Structure
  1625. Return Value:
  1626. None
  1627. --*/
  1628. {
  1629. dw;
  1630. ASSERT_MSG("Derived class did not provide DrawItemEx");
  1631. }
  1632. /* virtual */
  1633. int
  1634. CRMCListBox::__GetCount() const
  1635. /*++
  1636. Routine Description:
  1637. Provide GetCount() to ODL base class
  1638. Arguments:
  1639. None
  1640. Return Value:
  1641. Count of items in the listbox
  1642. --*/
  1643. {
  1644. return GetCount();
  1645. }
  1646. /* virtual */
  1647. int
  1648. CRMCListBox::__SetItemHeight(
  1649. IN int nIndex,
  1650. IN UINT cyItemHeight
  1651. )
  1652. /*++
  1653. Routine Description:
  1654. Provide SetItemHeight() to ODL base class
  1655. Arguments:
  1656. None
  1657. Return Value:
  1658. LB_ERR if the index or height is invalid.
  1659. --*/
  1660. {
  1661. return SetItemHeight(nIndex, cyItemHeight);
  1662. }
  1663. int
  1664. CRMCListBox::OnCreate(
  1665. IN LPCREATESTRUCT lpCreateStruct
  1666. )
  1667. /*++
  1668. Routine Description:
  1669. Listbox is being created
  1670. Arguments:
  1671. LPCREATESTRUCT lpCreateStruct : Creation structure
  1672. Return Value:
  1673. -1 for failure, 0 for success
  1674. --*/
  1675. {
  1676. if (CListBox::OnCreate(lpCreateStruct) == -1)
  1677. {
  1678. return -1;
  1679. }
  1680. Initialize();
  1681. return 0;
  1682. }
  1683. int
  1684. CRMCListBox::GetCurSel() const
  1685. /*++
  1686. Routine Description:
  1687. Get the index of the current selected item
  1688. Arguments:
  1689. None
  1690. Return Value:
  1691. On multi-selection listbox, it will return
  1692. the index of an item, iff that is the only
  1693. item selected.
  1694. On single-selection listbox, it behaves as
  1695. normal.
  1696. --*/
  1697. {
  1698. if (IsMultiSelect())
  1699. {
  1700. //
  1701. // We only like it if one item is selected
  1702. //
  1703. int nCurSel = LB_ERR;
  1704. if (CListBox::GetSelCount() == 1)
  1705. {
  1706. if (CListBox::GetSelItems(1, &nCurSel) != 1)
  1707. {
  1708. nCurSel = LB_ERR;
  1709. }
  1710. }
  1711. return nCurSel;
  1712. }
  1713. //
  1714. // Single select listbox
  1715. //
  1716. return CListBox::GetCurSel();
  1717. }
  1718. int
  1719. CRMCListBox::SetCurSel(
  1720. IN int nSelect
  1721. )
  1722. /*++
  1723. Routine Description:
  1724. Select an item. On a multi-select listbox,
  1725. this will deselect everything except the given
  1726. item.
  1727. Arguments:
  1728. int nSelect : Index of the item to be selected, or
  1729. -1 to reset all selections.
  1730. Return Value:
  1731. LB_ERR in case of error.
  1732. --*/
  1733. {
  1734. if (IsMultiSelect())
  1735. {
  1736. //
  1737. // Reset all selections
  1738. //
  1739. int nReturn = SelItemRange(FALSE, 0, GetCount() - 1);
  1740. if (nSelect >= 0)
  1741. {
  1742. //
  1743. // Ensure item is visible
  1744. //
  1745. nReturn = CListBox::SetSel(nSelect, TRUE);
  1746. CListBox::SetCaretIndex(nSelect, 0);
  1747. }
  1748. return nReturn;
  1749. }
  1750. return CListBox::SetCurSel(nSelect);
  1751. }
  1752. int
  1753. CRMCListBox::GetSel(
  1754. IN int nSel
  1755. ) const
  1756. /*++
  1757. Routine Description:
  1758. Determine if the given item is selected or not
  1759. Works for both single and multi-select listboxes
  1760. Arguments:
  1761. int nSel : Item whose state to check
  1762. Return Value:
  1763. LB_ERR in case of error, 0 if the item in question
  1764. is not selected, a positive number if it is.
  1765. --*/
  1766. {
  1767. if (IsMultiSelect())
  1768. {
  1769. return CListBox::GetSel(nSel);
  1770. }
  1771. //
  1772. // Some magic for single select
  1773. //
  1774. if (nSel < 0 || nSel >= CListBox::GetCount())
  1775. {
  1776. return LB_ERR;
  1777. }
  1778. return nSel == CListBox::GetCurSel()
  1779. ? TRUE
  1780. : FALSE;
  1781. }
  1782. int
  1783. CRMCListBox::GetSelCount() const
  1784. /*++
  1785. Routine Description:
  1786. Return count of selected items. Works for both
  1787. single and multi select (in the former case,
  1788. it will return zero or one only)
  1789. Arguments:
  1790. None
  1791. Return Value:
  1792. Count of selected items
  1793. --*/
  1794. {
  1795. if (IsMultiSelect())
  1796. {
  1797. return CListBox::GetSelCount();
  1798. }
  1799. return GetCurSel() != LB_ERR ? 1 : 0;
  1800. }
  1801. void *
  1802. CRMCListBox::GetSelectedListItem(
  1803. OUT int * pnSel OPTIONAL
  1804. )
  1805. /*++
  1806. Routine Description:
  1807. Return the single selected item in the list or NULL
  1808. Arguments:
  1809. int * pnSel : Optionally returns the selected index
  1810. Returns:
  1811. The currently selected (single) item, or NULL
  1812. if 0 or more than one items is selected. Works for
  1813. both multi-select and single select.
  1814. --*/
  1815. {
  1816. void * pItem = NULL;
  1817. int nCurSel = GetCurSel();
  1818. if (nCurSel >= 0)
  1819. {
  1820. //
  1821. // Get item properties
  1822. //
  1823. pItem = GetItemDataPtr(nCurSel);
  1824. if (pnSel)
  1825. {
  1826. *pnSel = nCurSel;
  1827. }
  1828. }
  1829. return pItem;
  1830. }
  1831. void *
  1832. CRMCListBox::GetNextSelectedItem(
  1833. IN OUT int * pnStartingIndex
  1834. )
  1835. /*++
  1836. Routine Description:
  1837. Return the next selected item starting at a specific
  1838. index.
  1839. Arguments:
  1840. int *pnStartingIndex : Starting index (>= 0)
  1841. Return Value:
  1842. Pointer to next selected item, or NULL if there are
  1843. none left.
  1844. The starting index will be updated to reflect the current
  1845. index, LB_ERR if no more selected items remain.
  1846. --*/
  1847. {
  1848. ASSERT_READ_WRITE_PTR(pnStartingIndex);
  1849. if (!pnStartingIndex)
  1850. {
  1851. return NULL;
  1852. }
  1853. ASSERT(*pnStartingIndex >= 0);
  1854. if (*pnStartingIndex < 0)
  1855. {
  1856. *pnStartingIndex = 0;
  1857. }
  1858. if (IsMultiSelect())
  1859. {
  1860. //
  1861. // Multi-select -- loop through
  1862. // until found
  1863. //
  1864. BOOL fFoundItem = FALSE;
  1865. while (*pnStartingIndex < GetCount())
  1866. {
  1867. if (CListBox::GetSel(*pnStartingIndex) > 0)
  1868. {
  1869. ++fFoundItem;
  1870. break;
  1871. }
  1872. ++(*pnStartingIndex);
  1873. }
  1874. if (!fFoundItem)
  1875. {
  1876. *pnStartingIndex = LB_ERR;
  1877. }
  1878. }
  1879. else
  1880. {
  1881. //
  1882. // Single select listbox, so there's no
  1883. // looping through -- either the selected item
  1884. // (if any) is in range or it isn't.
  1885. //
  1886. int nCurSel = CListBox::GetCurSel();
  1887. *pnStartingIndex = (nCurSel >= *pnStartingIndex) ? nCurSel : LB_ERR;
  1888. }
  1889. return (*pnStartingIndex != LB_ERR)
  1890. ? GetItemDataPtr(*pnStartingIndex)
  1891. : NULL;
  1892. }
  1893. BOOL
  1894. CRMCListBox::SelectItem(
  1895. IN void * pItemData
  1896. )
  1897. /*++
  1898. Routine Description:
  1899. Select the listbox item with the given data pointer
  1900. Arguments:
  1901. void * pItemData : Item to search for
  1902. Return Value:
  1903. TRUE if the item was found and selected, FALSE otherwise
  1904. Notes:
  1905. On a multi-select listbox, this will unselect
  1906. all other items in the listbox.
  1907. --*/
  1908. {
  1909. if (pItemData != NULL)
  1910. {
  1911. for (int n = 0; n < GetCount(); ++n)
  1912. {
  1913. if (pItemData == GetItemDataPtr(n))
  1914. {
  1915. SetCurSel(n);
  1916. return TRUE;
  1917. }
  1918. }
  1919. }
  1920. if (!IsMultiSelect())
  1921. {
  1922. //
  1923. // Set no selection
  1924. //
  1925. SetCurSel(-1);
  1926. }
  1927. return FALSE;
  1928. }
  1929. void
  1930. CRMCListBox::InvalidateSelection(
  1931. IN int nSel
  1932. )
  1933. /*++
  1934. Routine Description:
  1935. Force a repaint of the given selection
  1936. Arguments:
  1937. int nSel : Index of the item to be repainted
  1938. Return Value:
  1939. None
  1940. --*/
  1941. {
  1942. CRect rc;
  1943. if (GetItemRect(nSel, &rc) != LB_ERR)
  1944. {
  1945. InvalidateRect(&rc, TRUE);
  1946. }
  1947. }
  1948. IMPLEMENT_DYNAMIC(CRMCListBox,CListBox);
  1949. CHeaderListBox::CHeaderListBox(
  1950. IN DWORD dwStyle,
  1951. IN LPCTSTR lpRegKey OPTIONAL
  1952. )
  1953. /*++
  1954. Routine Description:
  1955. Constructor
  1956. Arguments:
  1957. DWORD dwStyle : Style bits (see HLS_*)
  1958. LPCTSTR lpRegKey : If specified, the registry key where the column
  1959. sizes will be stored.
  1960. Return Value:
  1961. None
  1962. --*/
  1963. : m_strRegKey(),
  1964. m_fInitialized(FALSE)
  1965. {
  1966. m_pHeader = new CRMCListBoxHeader(dwStyle);
  1967. if (lpRegKey)
  1968. {
  1969. GenerateRegistryKey(m_strRegKey, lpRegKey);
  1970. }
  1971. }
  1972. CHeaderListBox::~CHeaderListBox()
  1973. /*++
  1974. Routine Description:
  1975. Destructor
  1976. Arguments:
  1977. N/A
  1978. Return Value:
  1979. N/A
  1980. --*/
  1981. {
  1982. //
  1983. // Clean up header control
  1984. //
  1985. ASSERT_PTR(m_pHeader);
  1986. if (m_pHeader != NULL)
  1987. {
  1988. delete m_pHeader;
  1989. }
  1990. }
  1991. //
  1992. // Message map
  1993. //
  1994. BEGIN_MESSAGE_MAP(CHeaderListBox, CRMCListBox)
  1995. //{{AFX_MSG_MAP(CHeaderListBox)
  1996. ON_WM_CREATE()
  1997. ON_WM_DESTROY()
  1998. //}}AFX_MSG_MAP
  1999. END_MESSAGE_MAP()
  2000. /* virtual */
  2001. BOOL
  2002. CHeaderListBox::Initialize()
  2003. /*++
  2004. Routine Description:
  2005. This function should be called directly when subclassing an existing
  2006. listbox, otherwise OnCreate will take care of it, and this function
  2007. should not be called
  2008. Arguments:
  2009. None
  2010. Return Value:
  2011. TRUE for success, FALSE for failure
  2012. --*/
  2013. {
  2014. //
  2015. // Make sure we're only initialized once
  2016. //
  2017. if (m_fInitialized)
  2018. {
  2019. return TRUE;
  2020. }
  2021. if (!CRMCListBox::Initialize())
  2022. {
  2023. return FALSE;
  2024. }
  2025. //
  2026. // Create header control
  2027. //
  2028. ASSERT_PTR(m_pHeader);
  2029. if (m_pHeader)
  2030. {
  2031. TRACEEOLID("Creating Header");
  2032. //
  2033. // Create it in our location exactly
  2034. //
  2035. CRect rc;
  2036. ::GetDlgCtlRect(GetParent()->m_hWnd, m_hWnd, &rc);
  2037. //
  2038. // Make sure the header control shares the same parent
  2039. // as we do,
  2040. //
  2041. ASSERT(GetParent());
  2042. #ifndef CCS_NOHILITE
  2043. #define CCS_NOHILITE 0x00000010L
  2044. #endif
  2045. DWORD dwStyle = WS_VISIBLE | CCS_TOP | CCS_NODIVIDER | WS_BORDER
  2046. | HDS_HORZ;
  2047. if (!m_pHeader->Create(dwStyle, rc, GetParent(), this, 0xFFFF))
  2048. {
  2049. return FALSE;
  2050. }
  2051. }
  2052. m_fInitialized = TRUE;
  2053. return TRUE;
  2054. }
  2055. int
  2056. CHeaderListBox::QueryColumnWidth(
  2057. IN int nCol
  2058. ) const
  2059. /*++
  2060. Routine Description:
  2061. Get the width of the specified column
  2062. Arguments:
  2063. int nCol : The column
  2064. Return Value:
  2065. The width of the column, or -1 if the column index was out of range
  2066. --*/
  2067. {
  2068. ASSERT(nCol < QueryNumColumns());
  2069. if (nCol >= QueryNumColumns())
  2070. {
  2071. return -1;
  2072. }
  2073. HD_ITEM hdItem;
  2074. hdItem.mask = HDI_WIDTH;
  2075. VERIFY(GetHeaderItem(nCol, &hdItem));
  2076. return hdItem.cxy;
  2077. }
  2078. BOOL
  2079. CHeaderListBox::SetColumnWidth(
  2080. IN int nCol,
  2081. IN int nWidth
  2082. )
  2083. /*++
  2084. Routine Description:
  2085. Set the width of the specified column
  2086. Arguments:
  2087. int nCol : The column
  2088. int nWidth : New width
  2089. Return Value:
  2090. TRUE for success, FALSE for failure
  2091. --*/
  2092. {
  2093. ASSERT(nCol < QueryNumColumns());
  2094. if (nCol >= QueryNumColumns())
  2095. {
  2096. return FALSE;
  2097. }
  2098. TRACEEOLID("Setting width of column " << nCol << " to " << nWidth);
  2099. HD_ITEM hdItem;
  2100. hdItem.mask = HDI_WIDTH;
  2101. hdItem.cxy = nWidth;
  2102. VERIFY(SetHeaderItem(nCol, &hdItem));
  2103. return TRUE;
  2104. }
  2105. BOOL
  2106. CHeaderListBox::SetWidthsFromReg()
  2107. /*++
  2108. Routine Description:
  2109. Attempt to set the column widths from the registry
  2110. value we were initialized with.
  2111. Arguments:
  2112. None
  2113. Return Value:
  2114. TRUE if the column widths were succesfully set from the registry,
  2115. FALSE otherwise
  2116. --*/
  2117. {
  2118. if (m_strRegKey.IsEmpty())
  2119. {
  2120. //
  2121. // No reg key specified
  2122. //
  2123. return FALSE;
  2124. }
  2125. //
  2126. // Try to read the current column sizes from the registry
  2127. //
  2128. CRegKey rkUser;
  2129. if (ERROR_SUCCESS != rkUser.Create(HKEY_CURRENT_USER, m_strRegKey))
  2130. {
  2131. //
  2132. // Path doesn't exist -- no problem.
  2133. //
  2134. return FALSE;
  2135. }
  2136. //
  2137. // Don't auto adjust
  2138. //
  2139. m_pHeader->RespondToColumnWidthChanges(FALSE);
  2140. CRect rc;
  2141. m_pHeader->GetClientRect(&rc);
  2142. CError err;
  2143. try
  2144. {
  2145. TCHAR buf[MAX_PATH];
  2146. DWORD count = MAX_PATH;
  2147. int nTotalWidth = 0;
  2148. err = rkUser.QueryValue(buf, g_szRegColumns, &count);
  2149. if (err.Succeeded() && lstrlen(buf) > 0)
  2150. {
  2151. LPTSTR lpstrValue = buf;
  2152. LPTSTR lpWidth = _tcstok(lpstrValue, g_szColValueSep);
  2153. for (int n = 0; n < QueryNumColumns(); ++n)
  2154. {
  2155. ASSERT_PTR(lpWidth);
  2156. if (lpWidth == NULL)
  2157. {
  2158. err = ERROR_INVALID_PARAMETER;
  2159. break;
  2160. }
  2161. //
  2162. // Sanity check
  2163. //
  2164. int nWidth = _ttoi(lpWidth);
  2165. if (nWidth <= 0 || (nTotalWidth + nWidth > rc.Width()))
  2166. {
  2167. ASSERT_MSG("column width invalid");
  2168. return FALSE;
  2169. }
  2170. nTotalWidth += nWidth;
  2171. VERIFY(SetColumnWidth(n, nWidth));
  2172. lpWidth = _tcstok(NULL, g_szColValueSep);
  2173. }
  2174. }
  2175. }
  2176. catch(CMemoryException * e)
  2177. {
  2178. err = ERROR_NOT_ENOUGH_MEMORY;
  2179. e->Delete();
  2180. }
  2181. //
  2182. // Turn auto-adjust back on
  2183. //
  2184. m_pHeader->RespondToColumnWidthChanges(TRUE);
  2185. // if (err.Win32Error() == ERROR_FILE_NOT_FOUND)
  2186. // {
  2187. // // No problem, it is first run. We will set defaults.
  2188. // return err;
  2189. // }
  2190. return err;
  2191. }
  2192. void
  2193. CHeaderListBox::DistributeColumns()
  2194. /*++
  2195. Routine Description:
  2196. Proportion the column widths of over the entire width of the
  2197. header control while maintaining relative proportions.
  2198. Arguments:
  2199. None
  2200. Return Value:
  2201. None
  2202. --*/
  2203. {
  2204. //
  2205. // Obtain available width
  2206. //
  2207. ASSERT_PTR(m_pHeader);
  2208. CRect rc;
  2209. m_pHeader->GetClientRect(&rc);
  2210. //
  2211. // Get current total width
  2212. //
  2213. int nTotalWeight = 0;
  2214. int nCol;
  2215. for (nCol = 0; nCol < QueryNumColumns(); ++nCol)
  2216. {
  2217. nTotalWeight += QueryColumnWidth(nCol);
  2218. }
  2219. //
  2220. // And spread out the width, maintaining the same
  2221. // proportions
  2222. //
  2223. //
  2224. // Temporarily ignore changes
  2225. //
  2226. m_pHeader->RespondToColumnWidthChanges(FALSE);
  2227. int cColumns = QueryNumColumns();
  2228. for (nCol = 0; nCol < cColumns; ++nCol)
  2229. {
  2230. int nWidth = QueryColumnWidth(nCol);
  2231. nWidth = rc.Width() * nWidth / nTotalWeight;
  2232. VERIFY(SetColumnWidth(nCol, nWidth));
  2233. }
  2234. //
  2235. // Turn changes back on
  2236. //
  2237. m_pHeader->RespondToColumnWidthChanges(TRUE);
  2238. }
  2239. int
  2240. CHeaderListBox::InsertColumn(
  2241. IN int nCol,
  2242. IN int nWeight,
  2243. IN UINT nStringID,
  2244. IN HINSTANCE hResInst
  2245. )
  2246. /*++
  2247. Routine Description:
  2248. Insert column. The width of the column is actually a relative
  2249. "weight" of the column which needs to be adjusted later. The
  2250. return value is the column number or -1 if the column is not inserted.
  2251. Arguments:
  2252. int nCol : Column number
  2253. int nWeight : Relative weight of column
  2254. UINT nStringID : Resource string ID
  2255. Return Value:
  2256. Index of the column, or -1 in case of failure
  2257. --*/
  2258. {
  2259. CString strColName;
  2260. HD_ITEM hdItem;
  2261. HINSTANCE hInst = AfxGetResourceHandle();
  2262. AfxSetResourceHandle(hResInst);
  2263. VERIFY(strColName.LoadString(nStringID));
  2264. AfxSetResourceHandle(hInst);
  2265. hdItem.mask = HDI_FORMAT | HDI_WIDTH | HDI_TEXT;
  2266. hdItem.fmt = HDF_STRING | HDF_LEFT;
  2267. hdItem.pszText = (LPTSTR)(LPCTSTR)strColName;
  2268. hdItem.cchTextMax = strColName.GetLength();
  2269. hdItem.cxy = nWeight;
  2270. return InsertHeaderItem(nCol, &hdItem);
  2271. }
  2272. int
  2273. CHeaderListBox::OnCreate(
  2274. IN LPCREATESTRUCT lpCreateStruct
  2275. )
  2276. /*++
  2277. Routine Description:
  2278. Listbox is being created
  2279. Arguments:
  2280. LPCREATESTRUCT lpCreateStruct : Creation structure
  2281. Return Value:
  2282. 0 for success, -1 for failure
  2283. --*/
  2284. {
  2285. if (CRMCListBox::OnCreate(lpCreateStruct) == -1)
  2286. {
  2287. return -1;
  2288. }
  2289. Initialize();
  2290. return 0;
  2291. }
  2292. BOOL
  2293. CHeaderListBox::EnableWindow(
  2294. IN BOOL bEnable
  2295. )
  2296. /*++
  2297. Routine Description:
  2298. Enable/disable the control.
  2299. Arguments:
  2300. BOOL bEnable : TRUE to enable the control, FALSE to disable
  2301. Return Value:
  2302. Indicates the state before the EnableWindow member function was called.
  2303. The return value is nonzero if the window was previously disabled. The
  2304. return value is 0 if the window was previously enabled or an error
  2305. occurred.
  2306. --*/
  2307. {
  2308. if (m_pHeader)
  2309. {
  2310. m_pHeader->EnableWindow(bEnable);
  2311. }
  2312. return CRMCListBox::EnableWindow(bEnable);
  2313. }
  2314. BOOL
  2315. CHeaderListBox::ShowWindow(
  2316. IN int nCmdShow
  2317. )
  2318. /*++
  2319. Routine Description:
  2320. Show/hide the window
  2321. Arguments:
  2322. int nCmdShow : SW_ flag such as SW_SHOW or SW_HIDE
  2323. Return Value:
  2324. If the window was previously visible, the return value is TRUE. If the
  2325. window was previously hidden, the return value is FALSE.
  2326. --*/
  2327. {
  2328. if (m_pHeader)
  2329. {
  2330. m_pHeader->ShowWindow(nCmdShow);
  2331. }
  2332. return CRMCListBox::ShowWindow(nCmdShow);
  2333. }
  2334. void
  2335. CHeaderListBox::OnDestroy()
  2336. /*++
  2337. Routine Description:
  2338. Handle destruction of the control
  2339. Arguments:
  2340. None
  2341. Return Value:
  2342. None
  2343. --*/
  2344. {
  2345. //
  2346. // Destroy optional header control
  2347. //
  2348. ASSERT_PTR(m_pHeader);
  2349. if (m_pHeader)
  2350. {
  2351. if (!m_strRegKey.IsEmpty())
  2352. {
  2353. //
  2354. // Try to write the current column sizes to the registry
  2355. //
  2356. CError err;
  2357. CRegKey rkUser;
  2358. rkUser.Create(HKEY_CURRENT_USER, m_strRegKey);
  2359. int nWidth;
  2360. TCHAR szValue[32];
  2361. CString strValue;
  2362. try
  2363. {
  2364. for (int n = 0; n < GetHeaderItemCount(); ++n)
  2365. {
  2366. if (n > 0)
  2367. {
  2368. //
  2369. // Put in field separator
  2370. //
  2371. strValue += g_szColValueSep;
  2372. }
  2373. nWidth = m_pHeader->GetColumnWidth(n);
  2374. strValue += ::_itot(nWidth, szValue, 10);
  2375. }
  2376. err = rkUser.SetValue(strValue, g_szRegColumns);
  2377. }
  2378. catch(CMemoryException * e)
  2379. {
  2380. err = ERROR_NOT_ENOUGH_MEMORY;
  2381. e->Delete();
  2382. }
  2383. err.MessageBoxOnFailure();
  2384. }
  2385. m_pHeader->DestroyWindow();
  2386. }
  2387. CRMCListBox::OnDestroy();
  2388. }
  2389. IMPLEMENT_DYNAMIC(CHeaderListBox, CRMCListBox);
  2390. CRMCComboBox::CRMCComboBox()
  2391. /*++
  2392. Routine Description:
  2393. Constructor
  2394. Arguments:
  2395. None
  2396. Return Value:
  2397. N/A
  2398. --*/
  2399. : m_fInitialized(FALSE)
  2400. {
  2401. }
  2402. CRMCComboBox::~CRMCComboBox()
  2403. /*++
  2404. Routine Description:
  2405. Destructor
  2406. Arguments:
  2407. N/A
  2408. Return Value:
  2409. N/A
  2410. --*/
  2411. {
  2412. }
  2413. //
  2414. // Message Map
  2415. //
  2416. BEGIN_MESSAGE_MAP(CRMCComboBox, CComboBox)
  2417. //{{AFX_MSG_MAP(CRMCComboBox)
  2418. ON_WM_CREATE()
  2419. //}}AFX_MSG_MAP
  2420. END_MESSAGE_MAP()
  2421. /* virtual */
  2422. BOOL
  2423. CRMCComboBox::Initialize()
  2424. /*++
  2425. Routine Description:
  2426. This function should be called directly when subclassing an existing
  2427. combobox, otherwise OnCreate will take care of it.
  2428. Arguments:
  2429. None
  2430. Return Value:
  2431. TRUE for success, FALSE for failure
  2432. --*/
  2433. {
  2434. //
  2435. // Make sure we're only initialized once
  2436. //
  2437. if (m_fInitialized)
  2438. {
  2439. return TRUE;
  2440. }
  2441. //
  2442. // Ensure the base class knows our window
  2443. // handle
  2444. //
  2445. AttachWindow(this);
  2446. if (!CODLBox::Initialize())
  2447. {
  2448. return FALSE;
  2449. }
  2450. m_fInitialized = TRUE;
  2451. return TRUE;
  2452. }
  2453. void
  2454. CRMCComboBox::MeasureItem(
  2455. IN LPMEASUREITEMSTRUCT lpMIS
  2456. )
  2457. /*++
  2458. Routine Description:
  2459. CComboBox override to ODL base class
  2460. Arguments:
  2461. LPMEASUREITEMSTRUCT lpMIS : Measure item structure
  2462. Return Value:
  2463. None
  2464. --*/
  2465. {
  2466. CODLBox::__MeasureItem(lpMIS);
  2467. }
  2468. void
  2469. CRMCComboBox::DrawItem(
  2470. IN LPDRAWITEMSTRUCT lpDIS
  2471. )
  2472. /*++
  2473. Routine Description:
  2474. CListBox override to ODL base class
  2475. Arguments:
  2476. LPDRAWITEMSTRUCT lpDIS : Drawing item structure
  2477. Return Value:
  2478. None
  2479. --*/
  2480. {
  2481. CODLBox::__DrawItem(lpDIS);
  2482. }
  2483. /* virtual */
  2484. void
  2485. CRMCComboBox::DrawItemEx(
  2486. IN CRMCListBoxDrawStruct & dw
  2487. )
  2488. /*++
  2489. Routine Description:
  2490. Do-nothing extended draw function, which should
  2491. be provided by the derived class. This one will
  2492. ASSERT, and should never be called.
  2493. Arguments:
  2494. CRMCListBoxDrawStruct & dw : Draw Structure
  2495. Return Value:
  2496. None
  2497. --*/
  2498. {
  2499. dw;
  2500. ASSERT_MSG("Derived class did not provide DrawItemEx");
  2501. }
  2502. int
  2503. CRMCComboBox::OnCreate(
  2504. IN LPCREATESTRUCT lpCreateStruct
  2505. )
  2506. /*++
  2507. Routine Description:
  2508. Combo box is being created
  2509. Arguments:
  2510. LPCREATESTRUCT lpCreateStruct : Creation structure
  2511. Return Value:
  2512. -1 for failure, 0 for success
  2513. --*/
  2514. {
  2515. if (CComboBox::OnCreate(lpCreateStruct) == -1)
  2516. {
  2517. return -1;
  2518. }
  2519. Initialize();
  2520. return 0;
  2521. }
  2522. /* virtual */
  2523. int
  2524. CRMCComboBox::__GetCount() const
  2525. /*++
  2526. Routine Description:
  2527. Provide CComboBox::GetCount() functionality to base class
  2528. Arguments:
  2529. None
  2530. Return Value:
  2531. Get the count of items in the combo box
  2532. --*/
  2533. {
  2534. return GetCount();
  2535. }
  2536. /* virtual */
  2537. int
  2538. CRMCComboBox::__SetItemHeight(
  2539. IN int nIndex,
  2540. IN UINT cyItemHeight
  2541. )
  2542. /*++
  2543. Routine Description:
  2544. Provide CListBox::SetItemHeight() functionality to base class.
  2545. Arguments:
  2546. int nIndex : Index of the item
  2547. UINT cyItemHeight : Height of the item
  2548. Return Value:
  2549. SetItemHeight return value.
  2550. --*/
  2551. {
  2552. return SetItemHeight(nIndex, cyItemHeight);
  2553. }
  2554. IMPLEMENT_DYNAMIC(CRMCComboBox,CComboBox);