Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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