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.

3617 lines
60 KiB

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