/trunk/src/XCrashReport/XListCtrl.cpp

https://bitbucket.org/missdeer/wind · C++ · 2260 lines · 1605 code · 365 blank · 290 comment · 325 complexity · 0b86ee986f34e05a077834becff0d586 MD5 · raw file

Large files are truncated click here to view the full file

  1. // XListCtrl.cpp Version 1.3
  2. //
  3. // Author: Hans Dietrich
  4. // hdietrich2@hotmail.com
  5. //
  6. // This code is based on "Neat Stuff to do in List Controls Using Custom Draw"
  7. // by Michael Dunn. See http://www.codeproject.com/listctrl/lvcustomdraw.asp
  8. //
  9. // Thanks to David Patrick for pointing out how to subclass header control
  10. // if CXListCtrl is created via Create() instead of via dialog template.
  11. //
  12. // This software is released into the public domain.
  13. // You are free to use it in any way you like, except
  14. // that you may not sell this source code.
  15. //
  16. // This software is provided "as is" with no expressed
  17. // or implied warranty. I accept no liability for any
  18. // damage or loss of business that this software may cause.
  19. //
  20. ///////////////////////////////////////////////////////////////////////////////
  21. #include "stdafx.h"
  22. #include "XListCtrl.h"
  23. #ifdef _DEBUG
  24. #define new DEBUG_NEW
  25. #undef THIS_FILE
  26. static char THIS_FILE[] = __FILE__;
  27. #endif
  28. UINT NEAR WM_XLISTCTRL_COMBO_SELECTION = ::RegisterWindowMessage(_T("WM_XLISTCTRL_COMBO_SELECTION"));
  29. UINT NEAR WM_XLISTCTRL_CHECKBOX_CLICKED = ::RegisterWindowMessage(_T("WM_XLISTCTRL_CHECKBOX_CLICKED"));
  30. /////////////////////////////////////////////////////////////////////////////
  31. // CXListCtrl
  32. BEGIN_MESSAGE_MAP(CXListCtrl, CListCtrl)
  33. //{{AFX_MSG_MAP(CXListCtrl)
  34. ON_NOTIFY_REFLECT_EX(NM_CLICK, OnClick)
  35. ON_NOTIFY_REFLECT_EX(LVN_COLUMNCLICK, OnColumnClick)
  36. ON_WM_CREATE()
  37. ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
  38. ON_WM_DESTROY()
  39. ON_WM_LBUTTONDOWN()
  40. ON_WM_PAINT()
  41. ON_WM_SYSCOLORCHANGE()
  42. //}}AFX_MSG_MAP
  43. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  44. ON_WM_TIMER()
  45. ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_VK_ESCAPE, OnComboEscape)
  46. ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_VK_RETURN, OnComboReturn)
  47. ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_KEYDOWN, OnComboKeydown)
  48. ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_LBUTTONUP, OnComboLButtonUp)
  49. #endif
  50. #ifndef NO_XLISTCTRL_TOOL_TIPS
  51. ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
  52. ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
  53. #endif
  54. END_MESSAGE_MAP()
  55. ///////////////////////////////////////////////////////////////////////////////
  56. // ctor
  57. CXListCtrl::CXListCtrl()
  58. {
  59. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  60. m_bComboIsClicked = FALSE;
  61. m_nComboItem = 0;
  62. m_nComboSubItem = 0;
  63. m_pListBox = NULL;
  64. m_bFontIsCreated = FALSE;
  65. m_strInitialComboString = _T("");
  66. #endif
  67. m_dwExtendedStyleX = 0;
  68. m_bHeaderIsSubclassed = FALSE;
  69. m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
  70. m_cr3DHighLight = ::GetSysColor(COLOR_3DHIGHLIGHT);
  71. m_cr3DShadow = ::GetSysColor(COLOR_3DSHADOW);
  72. m_crBtnFace = ::GetSysColor(COLOR_BTNFACE);
  73. m_crBtnShadow = ::GetSysColor(COLOR_BTNSHADOW);
  74. m_crBtnText = ::GetSysColor(COLOR_BTNTEXT);
  75. m_crGrayText = ::GetSysColor(COLOR_GRAYTEXT);
  76. m_crHighLight = ::GetSysColor(COLOR_HIGHLIGHT);
  77. m_crHighLightText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
  78. m_crWindow = ::GetSysColor(COLOR_WINDOW);
  79. m_crWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
  80. }
  81. ///////////////////////////////////////////////////////////////////////////////
  82. // dtor
  83. CXListCtrl::~CXListCtrl()
  84. {
  85. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  86. if (m_pListBox)
  87. delete m_pListBox;
  88. #endif
  89. }
  90. ///////////////////////////////////////////////////////////////////////////////
  91. // PreSubclassWindow
  92. void CXListCtrl::PreSubclassWindow()
  93. {
  94. CListCtrl::PreSubclassWindow();
  95. // for Dialog based applications, this is a good place
  96. // to subclass the header control because the OnCreate()
  97. // function does not get called.
  98. SubclassHeaderControl();
  99. }
  100. ///////////////////////////////////////////////////////////////////////////////
  101. // OnCreate
  102. int CXListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
  103. {
  104. if (CListCtrl::OnCreate(lpCreateStruct) == -1)
  105. {
  106. ASSERT(FALSE);
  107. return -1;
  108. }
  109. // When the CXListCtrl object is created via a call to Create(), instead
  110. // of via a dialog box template, we must subclass the header control
  111. // window here because it does not exist when the PreSubclassWindow()
  112. // function is called.
  113. SubclassHeaderControl();
  114. return 0;
  115. }
  116. ///////////////////////////////////////////////////////////////////////////////
  117. // SubclassHeaderControl
  118. void CXListCtrl::SubclassHeaderControl()
  119. {
  120. if (m_bHeaderIsSubclassed)
  121. return;
  122. // if the list control has a header control window, then
  123. // subclass it
  124. // Thanks to Alberto Gattegno and Alon Peleg  and their article
  125. // "A Multiline Header Control Inside a CListCtrl" for easy way
  126. // to determine if the header control exists.
  127. CHeaderCtrl* pHeader = GetHeaderCtrl();
  128. if (pHeader)
  129. {
  130. VERIFY(m_HeaderCtrl.SubclassWindow(pHeader->m_hWnd));
  131. m_bHeaderIsSubclassed = TRUE;
  132. }
  133. }
  134. ///////////////////////////////////////////////////////////////////////////////
  135. // OnClick
  136. BOOL CXListCtrl::OnClick(NMHDR*, LRESULT* pResult)
  137. {
  138. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  139. UnpressComboButton();
  140. #endif
  141. *pResult = 0;
  142. return FALSE; // return FALSE to send message to parent also -
  143. // NOTE: MSDN documentation is incorrect
  144. }
  145. ///////////////////////////////////////////////////////////////////////////////
  146. // OnCustomDraw
  147. void CXListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
  148. {
  149. NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
  150. // Take the default processing unless we set this to something else below.
  151. *pResult = CDRF_DODEFAULT;
  152. // First thing - check the draw stage. If it's the control's prepaint
  153. // stage, then tell Windows we want messages for every item.
  154. if (pLVCD->nmcd.dwDrawStage == CDDS_PREPAINT)
  155. {
  156. *pResult = CDRF_NOTIFYITEMDRAW;
  157. }
  158. else if (pLVCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
  159. {
  160. // This is the notification message for an item. We'll request
  161. // notifications before each subitem's prepaint stage.
  162. *pResult = CDRF_NOTIFYSUBITEMDRAW;
  163. }
  164. else if (pLVCD->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM))
  165. {
  166. // This is the prepaint stage for a subitem. Here's where we set the
  167. // item's text and background colors. Our return value will tell
  168. // Windows to draw the subitem itself, but it will use the new colors
  169. // we set here.
  170. int nItem = static_cast<int> (pLVCD->nmcd.dwItemSpec);
  171. int nSubItem = pLVCD->iSubItem;
  172. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) pLVCD->nmcd.lItemlParam;
  173. ASSERT(pXLCD);
  174. COLORREF crText = m_crWindowText;
  175. COLORREF crBkgnd = m_crWindow;
  176. if (pXLCD)
  177. {
  178. crText = pXLCD[nSubItem].crText;
  179. crBkgnd = pXLCD[nSubItem].crBackground;
  180. if (!pXLCD[0].bEnabled)
  181. crText = m_crGrayText;
  182. }
  183. // store the colors back in the NMLVCUSTOMDRAW struct
  184. pLVCD->clrText = crText;
  185. pLVCD->clrTextBk = crBkgnd;
  186. CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
  187. CRect rect;
  188. GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);
  189. if (pXLCD && (pXLCD[nSubItem].bShowProgress))
  190. {
  191. DrawProgress(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
  192. *pResult = CDRF_SKIPDEFAULT; // We've painted everything.
  193. }
  194. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  195. else if (pXLCD && (pXLCD[nSubItem].bCombo))
  196. {
  197. if (GetItemState(nItem, LVIS_SELECTED))
  198. DrawComboBox(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
  199. else
  200. DrawText(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
  201. *pResult = CDRF_SKIPDEFAULT; // We've painted everything.
  202. }
  203. #endif
  204. else if (pXLCD && (pXLCD[nSubItem].nCheckedState != -1))
  205. {
  206. DrawCheckbox(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
  207. *pResult = CDRF_SKIPDEFAULT; // We've painted everything.
  208. }
  209. else
  210. {
  211. rect.left += DrawImage(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
  212. DrawText(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
  213. *pResult = CDRF_SKIPDEFAULT; // We've painted everything.
  214. }
  215. }
  216. }
  217. ///////////////////////////////////////////////////////////////////////////////
  218. // DrawProgress
  219. void CXListCtrl::DrawProgress(int nItem,
  220. int nSubItem,
  221. CDC *pDC,
  222. COLORREF crText,
  223. COLORREF crBkgnd,
  224. CRect& rect,
  225. XLISTCTRLDATA *pXLCD)
  226. {
  227. UNUSED_ALWAYS(nItem);
  228. ASSERT(pDC);
  229. ASSERT(pXLCD);
  230. rect.bottom -= 1;
  231. rect.left += 1; // leave margin in case row is highlighted
  232. rect.right -= 2;
  233. // draw border
  234. CPen graypen(PS_SOLID, 1, m_crBtnShadow);
  235. CPen *pOldPen = pDC->SelectObject(&graypen);
  236. pDC->MoveTo(rect.left, rect.bottom);
  237. pDC->LineTo(rect.right+1, rect.bottom);
  238. pDC->MoveTo(rect.left, rect.top);
  239. pDC->LineTo(rect.right, rect.top);
  240. pDC->MoveTo(rect.left, rect.top);
  241. pDC->LineTo(rect.left, rect.bottom);
  242. pDC->MoveTo(rect.right, rect.top);
  243. pDC->LineTo(rect.right, rect.bottom);
  244. // fill interior with light gray
  245. CRect InteriorRect;
  246. InteriorRect = rect;
  247. InteriorRect.left += 1;
  248. InteriorRect.top += 1;
  249. pDC->FillSolidRect(InteriorRect, RGB(224,224,224));
  250. // finish drawing border
  251. CPen blackpen(PS_SOLID, 1, RGB(0,0,0));
  252. pDC->SelectObject(&blackpen);
  253. pDC->MoveTo(rect.left+1, rect.top+1);
  254. pDC->LineTo(rect.right, rect.top+1);
  255. pDC->MoveTo(rect.left+1, rect.top+1);
  256. pDC->LineTo(rect.left+1, rect.bottom);
  257. pDC->SelectObject(pOldPen);
  258. if (pXLCD[nSubItem].nProgressPercent > 0)
  259. {
  260. // draw progress bar and text
  261. CRect LeftRect, RightRect;
  262. LeftRect = rect;
  263. LeftRect.left += 2;
  264. LeftRect.top += 2;
  265. RightRect = LeftRect;
  266. int w = (LeftRect.Width() * pXLCD[nSubItem].nProgressPercent) / 100;
  267. LeftRect.right = LeftRect.left + w;
  268. RightRect.left = LeftRect.right + 1;
  269. pDC->FillSolidRect(LeftRect, m_crHighLight);
  270. if (pXLCD[nSubItem].bShowProgressMessage)
  271. {
  272. CString str, format;
  273. format = pXLCD[nSubItem].strProgressMessage;
  274. if (format.IsEmpty())
  275. str.Format(_T("%d%%"), pXLCD[nSubItem].nProgressPercent);
  276. else
  277. str.Format(format, pXLCD[nSubItem].nProgressPercent);
  278. pDC->SetBkMode(TRANSPARENT);
  279. CRect TextRect;
  280. TextRect = rect;
  281. TextRect.DeflateRect(1, 1);
  282. TextRect.top += 1;
  283. CRgn rgn;
  284. rgn.CreateRectRgn(LeftRect.left, LeftRect.top, LeftRect.right, LeftRect.bottom);
  285. pDC->SelectClipRgn(&rgn);
  286. pDC->SetTextColor(crBkgnd);
  287. pDC->DrawText(str, &TextRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  288. rgn.DeleteObject();
  289. rgn.CreateRectRgn(RightRect.left, RightRect.top, RightRect.right, RightRect.bottom);
  290. pDC->SelectClipRgn(&rgn);
  291. pDC->SetTextColor(crText);
  292. pDC->DrawText(str, &TextRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  293. rgn.DeleteObject();
  294. pDC->SelectClipRgn(NULL);
  295. }
  296. }
  297. }
  298. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  299. ///////////////////////////////////////////////////////////////////////////////
  300. // DrawComboBox
  301. void CXListCtrl::DrawComboBox(int nItem,
  302. int nSubItem,
  303. CDC *pDC,
  304. COLORREF crText,
  305. COLORREF crBkgnd,
  306. CRect& rect,
  307. XLISTCTRLDATA *pXLCD)
  308. {
  309. UNUSED_ALWAYS(crText);
  310. UNUSED_ALWAYS(crBkgnd);
  311. ASSERT(pDC);
  312. ASSERT(pXLCD);
  313. #ifdef _DEBUG
  314. DWORD dwExStyle = GetExtendedStyle();
  315. if ((dwExStyle & LVS_EX_FULLROWSELECT) == 0)
  316. {
  317. TRACE(_T("XListCtrl: combo boxes require LVS_EX_FULLROWSELECT style\n"));
  318. ASSERT(FALSE);
  319. }
  320. #endif
  321. rect.bottom += 1; // bottom edge is white, so this doesn't matter
  322. rect.left += 1; // leave margin in case row is highlighted
  323. rect.right -= 2;
  324. // draw border
  325. CPen pen(PS_SOLID, 1, m_crBtnShadow);
  326. CPen *pOldPen = pDC->SelectObject(&pen);
  327. pDC->MoveTo(rect.left, rect.bottom-2);
  328. pDC->LineTo(rect.right, rect.bottom-2);
  329. pDC->MoveTo(rect.left, rect.top);
  330. pDC->LineTo(rect.right, rect.top);
  331. pDC->MoveTo(rect.left, rect.top);
  332. pDC->LineTo(rect.left, rect.bottom-2);
  333. pDC->MoveTo(rect.right, rect.top);
  334. pDC->LineTo(rect.right, rect.bottom-1);
  335. CPen blackpen(PS_SOLID, 1, RGB(0,0,0));
  336. pDC->SelectObject(&blackpen);
  337. // fill interior with white
  338. CRect InteriorRect;
  339. InteriorRect = rect;
  340. InteriorRect.DeflateRect(2, 2);
  341. pDC->FillSolidRect(InteriorRect, RGB(255,255,255));
  342. // set arrow rect
  343. CRect ArrowRect;
  344. ArrowRect = rect;
  345. ArrowRect.right += 1;
  346. ArrowRect.left = ArrowRect.right - ArrowRect.Height();
  347. ArrowRect.DeflateRect(2, 2);
  348. CString str;
  349. str = GetItemText(nItem, nSubItem);
  350. if (str.IsEmpty())
  351. {
  352. // subitem text is empty, try to get from listbox strings
  353. if (pXLCD[nSubItem].psa)
  354. {
  355. int index = 0;
  356. if ((pXLCD[nSubItem].nInitialComboSel >= 0) &&
  357. (pXLCD[nSubItem].psa->GetSize() > pXLCD[nSubItem].nInitialComboSel))
  358. {
  359. index = pXLCD[nSubItem].nInitialComboSel;
  360. str = pXLCD[nSubItem].psa->GetAt(index);
  361. SetItemText(nItem, nSubItem, str);
  362. }
  363. }
  364. }
  365. if (!str.IsEmpty())
  366. {
  367. // draw text
  368. CRect TextRect;
  369. TextRect = rect;
  370. TextRect.top -= 1;
  371. TextRect.left += 2;
  372. TextRect.right = ArrowRect.left - 1;
  373. pDC->SetBkMode(TRANSPARENT);
  374. COLORREF cr = m_crWindowText;
  375. if (!pXLCD[0].bEnabled)
  376. cr = m_crGrayText;
  377. pDC->SetTextColor(cr);
  378. pDC->SetBkColor(m_crWindow);
  379. UINT nFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
  380. pDC->DrawText(str, &TextRect, nFormat);
  381. }
  382. if (!pXLCD[nSubItem].bComboIsClicked)
  383. {
  384. // draw depressed combobox
  385. pDC->DrawEdge(&ArrowRect, EDGE_RAISED, BF_RECT);
  386. ArrowRect.DeflateRect(2, 2);
  387. pDC->FillSolidRect(ArrowRect, m_crBtnFace);
  388. // draw the downarrow using blackpen
  389. int x = ArrowRect.left + 1;
  390. int y = ArrowRect.top + 2;
  391. int k = 5;
  392. for (int i = 0; i < 3; i++)
  393. {
  394. pDC->MoveTo(x, y);
  395. pDC->LineTo(x+k, y);
  396. x++;
  397. y++;
  398. k -= 2;
  399. }
  400. }
  401. else
  402. {
  403. // draw normal combobox
  404. m_rectComboButton = ArrowRect;
  405. CBrush brush(m_cr3DShadow);
  406. pDC->FrameRect(&ArrowRect, &brush);
  407. ArrowRect.DeflateRect(1, 1);
  408. pDC->FillSolidRect(ArrowRect, m_crBtnFace);
  409. // draw the downarrow using blackpen
  410. int x = ArrowRect.left + 3;
  411. int y = ArrowRect.top + 4;
  412. int k = 5;
  413. for (int i = 0; i < 3; i++)
  414. {
  415. pDC->MoveTo(x, y);
  416. pDC->LineTo(x+k, y);
  417. x++;
  418. y++;
  419. k -= 2;
  420. }
  421. // show listbox if not already shown
  422. if (!m_pListBox)
  423. {
  424. // create and populate the combo's listbox
  425. m_pListBox = new CXComboList(this);
  426. ASSERT(m_pListBox);
  427. if (m_pListBox)
  428. {
  429. m_nComboItem = nItem;
  430. m_nComboSubItem = nSubItem;
  431. m_rectComboList = rect;
  432. m_rectComboList.right -= 1;
  433. m_rectComboList.top += rect.Height() - 1;
  434. m_rectComboList.bottom = m_rectComboList.top +
  435. (pXLCD[nSubItem].nComboListHeight) * (rect.Height() - 2);
  436. ClientToScreen(&m_rectComboList);
  437. CString szClassName = AfxRegisterWndClass(CS_CLASSDC|CS_SAVEBITS,
  438. LoadCursor(NULL, IDC_ARROW));
  439. BOOL bSuccess = m_pListBox->CreateEx(0, szClassName, _T(""),
  440. WS_POPUP | WS_VISIBLE /*| WS_VSCROLL*/ | WS_BORDER,
  441. m_rectComboList,
  442. this, 0, NULL);
  443. if (!bSuccess)
  444. {
  445. }
  446. else
  447. {
  448. m_strInitialComboString = _T("");
  449. if (!m_bFontIsCreated)
  450. {
  451. // use font from list control
  452. CFont *font = pDC->GetCurrentFont();
  453. if (font)
  454. {
  455. LOGFONT lf;
  456. font->GetLogFont(&lf);
  457. m_ListboxFont.CreateFontIndirect(&lf);
  458. m_bFontIsCreated = TRUE;
  459. }
  460. }
  461. if (m_bFontIsCreated)
  462. m_pListBox->SetFont(&m_ListboxFont, FALSE);
  463. if (pXLCD[nSubItem].psa)
  464. {
  465. CString s;
  466. for (int i = 0; i < pXLCD[nSubItem].psa->GetSize(); i++)
  467. {
  468. s = pXLCD[nSubItem].psa->GetAt(i);
  469. if (!s.IsEmpty())
  470. m_pListBox->AddString(s);
  471. }
  472. }
  473. int index = 0;
  474. if (str.IsEmpty())
  475. {
  476. // str is empty, try to get from first listbox string
  477. if (m_pListBox->GetCount() > 0)
  478. m_pListBox->GetText(0, str);
  479. SetItemText(nItem, nSubItem, str);
  480. }
  481. else
  482. {
  483. // set listbox selection from subitem text
  484. index = m_pListBox->FindStringExact(-1, str);
  485. if (index == LB_ERR)
  486. index = 0;
  487. }
  488. m_pListBox->SetCurSel(index);
  489. m_pListBox->GetText(index, m_strInitialComboString);
  490. m_pListBox->SetActive(11);
  491. }
  492. }
  493. }
  494. }
  495. pDC->SelectObject(pOldPen);
  496. }
  497. #endif
  498. ///////////////////////////////////////////////////////////////////////////////
  499. // DrawCheckbox
  500. void CXListCtrl::DrawCheckbox(int nItem,
  501. int nSubItem,
  502. CDC *pDC,
  503. COLORREF crText,
  504. COLORREF crBkgnd,
  505. CRect& rect,
  506. XLISTCTRLDATA *pXLCD)
  507. {
  508. ASSERT(pDC);
  509. ASSERT(pXLCD);
  510. GetDrawColors(nItem, nSubItem, crText, crBkgnd);
  511. pDC->FillSolidRect(&rect, crBkgnd);
  512. CRect chkboxrect;
  513. chkboxrect = rect;
  514. chkboxrect.bottom -= 1;
  515. chkboxrect.left += 9; // line up checkbox with header checkbox
  516. chkboxrect.right = chkboxrect.left + chkboxrect.Height(); // width = height
  517. CString str;
  518. str = GetItemText(nItem, nSubItem);
  519. if (str.IsEmpty())
  520. {
  521. // center the checkbox
  522. chkboxrect.left = rect.left + rect.Width()/2 - chkboxrect.Height()/2 - 1;
  523. chkboxrect.right = chkboxrect.left + chkboxrect.Height();
  524. }
  525. // fill rect around checkbox with white
  526. pDC->FillSolidRect(&chkboxrect, m_crWindow);
  527. chkboxrect.left += 1;
  528. // draw border
  529. pDC->DrawEdge(&chkboxrect, EDGE_SUNKEN, BF_RECT);
  530. if (pXLCD[nSubItem].nCheckedState == 1)
  531. {
  532. CPen *pOldPen = NULL;
  533. CPen graypen(PS_SOLID, 1, m_crGrayText);
  534. CPen blackpen(PS_SOLID, 1, RGB(0,0,0));
  535. if (pXLCD[0].bEnabled)
  536. pOldPen = pDC->SelectObject(&blackpen);
  537. else
  538. pOldPen = pDC->SelectObject(&graypen);
  539. // draw the checkmark
  540. int x = chkboxrect.left + 9;
  541. ASSERT(x < chkboxrect.right);
  542. int y = chkboxrect.top + 3;
  543. int i;
  544. for (i = 0; i < 4; i++)
  545. {
  546. pDC->MoveTo(x, y);
  547. pDC->LineTo(x, y+3);
  548. x--;
  549. y++;
  550. }
  551. for (i = 0; i < 3; i++)
  552. {
  553. pDC->MoveTo(x, y);
  554. pDC->LineTo(x, y+3);
  555. x--;
  556. y--;
  557. }
  558. if (pOldPen)
  559. pDC->SelectObject(pOldPen);
  560. }
  561. if (!str.IsEmpty())
  562. {
  563. pDC->SetBkMode(TRANSPARENT);
  564. pDC->SetTextColor(crText);
  565. pDC->SetBkColor(crBkgnd);
  566. CRect textrect;
  567. textrect = rect;
  568. textrect.left = chkboxrect.right + 4;
  569. pDC->DrawText(str, &textrect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
  570. }
  571. }
  572. ///////////////////////////////////////////////////////////////////////////////
  573. // GetDrawColors
  574. void CXListCtrl::GetDrawColors(int nItem,
  575. int nSubItem,
  576. COLORREF& colorText,
  577. COLORREF& colorBkgnd)
  578. {
  579. DWORD dwStyle = GetStyle();
  580. DWORD dwExStyle = GetExtendedStyle();
  581. COLORREF crText = colorText;
  582. COLORREF crBkgnd = colorBkgnd;
  583. if (GetItemState(nItem, LVIS_SELECTED))
  584. {
  585. if (dwExStyle & LVS_EX_FULLROWSELECT)
  586. {
  587. // selected? if so, draw highlight background
  588. crText = m_crHighLightText;
  589. crBkgnd = m_crHighLight;
  590. // has focus? if not, draw gray background
  591. if (m_hWnd != ::GetFocus())
  592. {
  593. if (dwStyle & LVS_SHOWSELALWAYS)
  594. {
  595. crText = m_crWindowText;
  596. crBkgnd = m_crBtnFace;
  597. }
  598. else
  599. {
  600. crText = colorText;
  601. crBkgnd = colorBkgnd;
  602. }
  603. }
  604. }
  605. else // not full row select
  606. {
  607. if (nSubItem == 0)
  608. {
  609. // selected? if so, draw highlight background
  610. crText = m_crHighLightText;
  611. crBkgnd = m_crHighLight;
  612. // has focus? if not, draw gray background
  613. if (m_hWnd != ::GetFocus())
  614. {
  615. if (dwStyle & LVS_SHOWSELALWAYS)
  616. {
  617. crText = m_crWindowText;
  618. crBkgnd = m_crBtnFace;
  619. }
  620. else
  621. {
  622. crText = colorText;
  623. crBkgnd = colorBkgnd;
  624. }
  625. }
  626. }
  627. }
  628. }
  629. colorText = crText;
  630. colorBkgnd = crBkgnd;
  631. }
  632. ///////////////////////////////////////////////////////////////////////////////
  633. // DrawImage
  634. int CXListCtrl::DrawImage(int nItem,
  635. int nSubItem,
  636. CDC* pDC,
  637. COLORREF crText,
  638. COLORREF crBkgnd,
  639. CRect rect,
  640. XLISTCTRLDATA *pXLCD)
  641. {
  642. GetDrawColors(nItem, nSubItem, crText, crBkgnd);
  643. pDC->FillSolidRect(&rect, crBkgnd);
  644. int nWidth = 0;
  645. rect.left += m_HeaderCtrl.GetSpacing();
  646. CImageList* pImageList = GetImageList(LVSIL_SMALL);
  647. if (pImageList)
  648. {
  649. SIZE sizeImage;
  650. sizeImage.cx = sizeImage.cy = 0;
  651. IMAGEINFO info;
  652. int nImage = -1;
  653. if (pXLCD)
  654. nImage = pXLCD[nSubItem].nImage;
  655. if (nImage == -1)
  656. return 0;
  657. if (pImageList->GetImageInfo(nImage, &info))
  658. {
  659. sizeImage.cx = info.rcImage.right - info.rcImage.left;
  660. sizeImage.cy = info.rcImage.bottom - info.rcImage.top;
  661. }
  662. if (nImage >= 0)
  663. {
  664. if (rect.Width() > 0)
  665. {
  666. POINT point;
  667. point.y = rect.CenterPoint().y - (sizeImage.cy >> 1);
  668. point.x = rect.left;
  669. SIZE size;
  670. size.cx = rect.Width() < sizeImage.cx ? rect.Width() : sizeImage.cx;
  671. size.cy = rect.Height() < sizeImage.cy ? rect.Height() : sizeImage.cy;
  672. // save image list background color
  673. COLORREF rgb = pImageList->GetBkColor();
  674. // set image list background color
  675. pImageList->SetBkColor(crBkgnd);
  676. pImageList->DrawIndirect(pDC, nImage, point, size, CPoint(0, 0));
  677. pImageList->SetBkColor(rgb);
  678. nWidth = sizeImage.cx + m_HeaderCtrl.GetSpacing();
  679. }
  680. }
  681. }
  682. return nWidth;
  683. }
  684. ///////////////////////////////////////////////////////////////////////////////
  685. // DrawText
  686. void CXListCtrl::DrawText(int nItem,
  687. int nSubItem,
  688. CDC *pDC,
  689. COLORREF crText,
  690. COLORREF crBkgnd,
  691. CRect& rect,
  692. XLISTCTRLDATA *pXLCD)
  693. {
  694. ASSERT(pDC);
  695. ASSERT(pXLCD);
  696. GetDrawColors(nItem, nSubItem, crText, crBkgnd);
  697. pDC->FillSolidRect(&rect, crBkgnd);
  698. CString str;
  699. str = GetItemText(nItem, nSubItem);
  700. if (!str.IsEmpty())
  701. {
  702. // get text justification
  703. HDITEM hditem;
  704. hditem.mask = HDI_FORMAT;
  705. m_HeaderCtrl.GetItem(nSubItem, &hditem);
  706. int nFmt = hditem.fmt & HDF_JUSTIFYMASK;
  707. UINT nFormat = DT_VCENTER | DT_SINGLELINE;
  708. if (nFmt == HDF_CENTER)
  709. nFormat |= DT_CENTER;
  710. else if (nFmt == HDF_LEFT)
  711. nFormat |= DT_LEFT;
  712. else
  713. nFormat |= DT_RIGHT;
  714. CFont *pOldFont = NULL;
  715. CFont boldfont;
  716. // check if bold specified for subitem
  717. if (pXLCD && pXLCD[nSubItem].bBold)
  718. {
  719. CFont *font = pDC->GetCurrentFont();
  720. if (font)
  721. {
  722. LOGFONT lf;
  723. font->GetLogFont(&lf);
  724. lf.lfWeight = FW_BOLD;
  725. boldfont.CreateFontIndirect(&lf);
  726. pOldFont = pDC->SelectObject(&boldfont);
  727. }
  728. }
  729. pDC->SetBkMode(TRANSPARENT);
  730. pDC->SetTextColor(crText);
  731. pDC->SetBkColor(crBkgnd);
  732. pDC->DrawText(str, &rect, nFormat);
  733. if (pOldFont)
  734. pDC->SelectObject(pOldFont);
  735. }
  736. }
  737. ///////////////////////////////////////////////////////////////////////////////
  738. // GetSubItemRect
  739. BOOL CXListCtrl::GetSubItemRect(int nItem,
  740. int nSubItem,
  741. int nArea,
  742. CRect& rect)
  743. {
  744. ASSERT(nItem >= 0);
  745. ASSERT(nItem < GetItemCount());
  746. if ((nItem < 0) || nItem >= GetItemCount())
  747. return FALSE;
  748. ASSERT(nSubItem >= 0);
  749. ASSERT(nSubItem < GetColumns());
  750. if ((nSubItem < 0) || nSubItem >= GetColumns())
  751. return FALSE;
  752. BOOL bRC = CListCtrl::GetSubItemRect(nItem, nSubItem, nArea, rect);
  753. // if nSubItem == 0, the rect returned by CListCtrl::GetSubItemRect
  754. // is the entire row, so use left edge of second subitem
  755. if (nSubItem == 0)
  756. {
  757. if (GetColumns() > 1)
  758. {
  759. CRect rect1;
  760. bRC = GetSubItemRect(nItem, 1, LVIR_BOUNDS, rect1);
  761. rect.right = rect1.left;
  762. }
  763. }
  764. return bRC;
  765. }
  766. ///////////////////////////////////////////////////////////////////////////////
  767. // OnLButtonDown
  768. void CXListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
  769. {
  770. TRACE(_T("in CXListCtrl::OnLButtonDown\n"));
  771. int nItem = -1;
  772. CRect rect;
  773. int i;
  774. for (i = 0; i < GetItemCount(); i++)
  775. {
  776. if (CListCtrl::GetItemRect(i, &rect, LVIR_BOUNDS))
  777. {
  778. if (rect.PtInRect(point))
  779. {
  780. nItem = i;
  781. break;
  782. }
  783. }
  784. }
  785. if (nItem == -1)
  786. {
  787. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  788. if (m_pListBox)
  789. OnComboEscape(0, 0);
  790. #endif
  791. }
  792. else
  793. {
  794. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  795. if (!pXLCD)
  796. {
  797. return;
  798. }
  799. if (!pXLCD[0].bEnabled)
  800. return;
  801. CRect rect;
  802. int nSubItem = -1;
  803. // check if a subitem checkbox was clicked
  804. for (i = 0; i < GetColumns(); i++)
  805. {
  806. GetSubItemRect(nItem, i, LVIR_BOUNDS, rect);
  807. if (rect.PtInRect(point))
  808. {
  809. nSubItem = i;
  810. break;
  811. }
  812. }
  813. if (nSubItem == -1)
  814. {
  815. // -1 = no checkbox for this subitem
  816. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  817. if (m_pListBox)
  818. {
  819. OnComboEscape(0, 0);
  820. }
  821. #endif
  822. }
  823. else
  824. {
  825. if (pXLCD[nSubItem].nCheckedState >= 0)
  826. {
  827. int nChecked = pXLCD[nSubItem].nCheckedState;
  828. nChecked = (nChecked == 0) ? 1 : 0;
  829. pXLCD[nSubItem].nCheckedState = nChecked;
  830. UpdateSubItem(nItem, nSubItem);
  831. CWnd *pWnd = GetParent();
  832. if (!pWnd)
  833. pWnd = GetOwner();
  834. if (pWnd && ::IsWindow(pWnd->m_hWnd))
  835. pWnd->SendMessage(WM_XLISTCTRL_CHECKBOX_CLICKED,
  836. nItem, nSubItem);
  837. // now update checkbox in header
  838. // -1 = no checkbox in column header
  839. if (GetHeaderCheckedState(nSubItem) != XHEADERCTRL_NO_IMAGE)
  840. {
  841. int nCheckedCount = CountCheckedItems(nSubItem);
  842. if (nCheckedCount == GetItemCount())
  843. SetHeaderCheckedState(nSubItem, XHEADERCTRL_CHECKED_IMAGE);
  844. else
  845. SetHeaderCheckedState(nSubItem, XHEADERCTRL_UNCHECKED_IMAGE);
  846. }
  847. }
  848. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  849. else if (pXLCD[nSubItem].bCombo)
  850. {
  851. if (m_pListBox)
  852. {
  853. m_pListBox->DestroyWindow();
  854. delete m_pListBox;
  855. m_pListBox = NULL;
  856. }
  857. rect.left = rect.right - rect.Height();
  858. if (point.x >= rect.left && point.y <= rect.right)
  859. {
  860. pXLCD[nSubItem].bComboIsClicked = TRUE;
  861. m_bComboIsClicked = TRUE;
  862. m_nComboItem = nItem;
  863. m_nComboSubItem = nSubItem;
  864. UpdateSubItem(nItem, nSubItem);
  865. SetTimer(1, 100, NULL);
  866. }
  867. }
  868. else if (m_pListBox)
  869. {
  870. OnComboEscape(0, 0);
  871. }
  872. #endif
  873. }
  874. }
  875. CListCtrl::OnLButtonDown(nFlags, point);
  876. }
  877. ///////////////////////////////////////////////////////////////////////////////
  878. // OnPaint
  879. void CXListCtrl::OnPaint()
  880. {
  881. Default();
  882. if (GetItemCount() <= 0)
  883. {
  884. CDC* pDC = GetDC();
  885. int nSavedDC = pDC->SaveDC();
  886. CRect rc;
  887. GetWindowRect(&rc);
  888. ScreenToClient(&rc);
  889. CHeaderCtrl* pHC = GetHeaderCtrl();
  890. if (pHC != NULL)
  891. {
  892. CRect rcH;
  893. pHC->GetItemRect(0, &rcH);
  894. rc.top += rcH.bottom;
  895. }
  896. rc.top += 10;
  897. CString strText;
  898. strText = _T("There are no items to show in this view.");
  899. COLORREF crText = m_crWindowText;
  900. COLORREF crBkgnd = m_crWindow;
  901. CBrush brush(crBkgnd);
  902. pDC->FillRect(rc, &brush);
  903. pDC->SetTextColor(crText);
  904. pDC->SetBkColor(crBkgnd);
  905. pDC->SelectStockObject(ANSI_VAR_FONT);
  906. pDC->DrawText(strText, -1, rc, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_NOCLIP);
  907. pDC->RestoreDC(nSavedDC);
  908. ReleaseDC(pDC);
  909. }
  910. }
  911. ///////////////////////////////////////////////////////////////////////////////
  912. // InsertItem
  913. int CXListCtrl::InsertItem(const LVITEM* pItem)
  914. {
  915. ASSERT(pItem->iItem >= 0);
  916. if (pItem->iItem < 0)
  917. return -1;
  918. int index = CListCtrl::InsertItem(pItem);
  919. if (index < 0)
  920. return index;
  921. XLISTCTRLDATA *pXLCD = new XLISTCTRLDATA [GetColumns()];
  922. ASSERT(pXLCD);
  923. if (!pXLCD)
  924. return -1;
  925. pXLCD[0].crText = m_crWindowText;
  926. pXLCD[0].crBackground = m_crWindow;
  927. pXLCD[0].nImage = pItem->iImage;
  928. CListCtrl::SetItemData(index, (DWORD) pXLCD);
  929. return index;
  930. }
  931. ///////////////////////////////////////////////////////////////////////////////
  932. // InsertItem
  933. int CXListCtrl::InsertItem(int nItem, LPCTSTR lpszItem)
  934. {
  935. ASSERT(nItem >= 0);
  936. if (nItem < 0)
  937. return -1;
  938. return InsertItem(nItem,
  939. lpszItem,
  940. m_crWindowText,
  941. m_crWindow);
  942. }
  943. ///////////////////////////////////////////////////////////////////////////////
  944. // InsertItem
  945. int CXListCtrl::InsertItem(int nItem,
  946. LPCTSTR lpszItem,
  947. COLORREF crText,
  948. COLORREF crBackground)
  949. {
  950. ASSERT(nItem >= 0);
  951. if (nItem < 0)
  952. return -1;
  953. int index = CListCtrl::InsertItem(nItem, lpszItem);
  954. if (index < 0)
  955. return index;
  956. XLISTCTRLDATA *pXLCD = new XLISTCTRLDATA [GetColumns()];
  957. ASSERT(pXLCD);
  958. if (!pXLCD)
  959. return -1;
  960. pXLCD[0].crText = crText;
  961. pXLCD[0].crBackground = crBackground;
  962. pXLCD[0].nImage = -1;
  963. CListCtrl::SetItemData(index, (DWORD) pXLCD);
  964. return index;
  965. }
  966. ///////////////////////////////////////////////////////////////////////////////
  967. // SetItem
  968. int CXListCtrl::SetItem(const LVITEM* pItem)
  969. {
  970. ASSERT(pItem->iItem >= 0);
  971. if (pItem->iItem < 0)
  972. return -1;
  973. BOOL rc = CListCtrl::SetItem(pItem);
  974. if (!rc)
  975. return FALSE;
  976. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(pItem->iItem);
  977. if (pXLCD)
  978. {
  979. pXLCD[pItem->iSubItem].nImage = pItem->iImage;
  980. UpdateSubItem(pItem->iItem, pItem->iSubItem);
  981. rc = TRUE;
  982. }
  983. else
  984. {
  985. rc = FALSE;
  986. }
  987. return rc;
  988. }
  989. ///////////////////////////////////////////////////////////////////////////////
  990. // SetItemImage
  991. BOOL CXListCtrl::SetItemImage(int nItem, int nSubItem, int nImage)
  992. {
  993. ASSERT(nItem >= 0);
  994. ASSERT(nItem < GetItemCount());
  995. if ((nItem < 0) || nItem >= GetItemCount())
  996. return FALSE;
  997. ASSERT(nSubItem >= 0);
  998. ASSERT(nSubItem < GetColumns());
  999. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1000. return FALSE;
  1001. BOOL rc = TRUE;
  1002. if (nItem < 0)
  1003. return FALSE;
  1004. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1005. if (pXLCD)
  1006. {
  1007. pXLCD[nSubItem].nImage = nImage;
  1008. }
  1009. UpdateSubItem(nItem, nSubItem);
  1010. return rc;
  1011. }
  1012. ///////////////////////////////////////////////////////////////////////////////
  1013. // SetItemText
  1014. BOOL CXListCtrl::SetItemText(int nItem, int nSubItem, LPCTSTR lpszText)
  1015. {
  1016. ASSERT(nItem >= 0);
  1017. ASSERT(nItem < GetItemCount());
  1018. if ((nItem < 0) || nItem >= GetItemCount())
  1019. return FALSE;
  1020. ASSERT(nSubItem >= 0);
  1021. ASSERT(nSubItem < GetColumns());
  1022. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1023. return FALSE;
  1024. BOOL rc = CListCtrl::SetItemText(nItem, nSubItem, lpszText);
  1025. UpdateSubItem(nItem, nSubItem);
  1026. return rc;
  1027. }
  1028. ///////////////////////////////////////////////////////////////////////////////
  1029. // SetItemText
  1030. //
  1031. // This function will set the text and colors for a subitem. If lpszText
  1032. // is NULL, only the colors will be set. If a color value is -1, the display
  1033. // color will be set to the default Windows color.
  1034. //
  1035. BOOL CXListCtrl::SetItemText(int nItem, int nSubItem, LPCTSTR lpszText,
  1036. COLORREF crText, COLORREF crBackground)
  1037. {
  1038. ASSERT(nItem >= 0);
  1039. ASSERT(nItem < GetItemCount());
  1040. if ((nItem < 0) || nItem >= GetItemCount())
  1041. return FALSE;
  1042. ASSERT(nSubItem >= 0);
  1043. ASSERT(nSubItem < GetColumns());
  1044. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1045. return FALSE;
  1046. BOOL rc = TRUE;
  1047. if (nItem < 0)
  1048. return FALSE;
  1049. if (lpszText)
  1050. rc = CListCtrl::SetItemText(nItem, nSubItem, lpszText);
  1051. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1052. if (pXLCD)
  1053. {
  1054. pXLCD[nSubItem].crText = (crText == -1) ? m_crWindowText : crText;
  1055. pXLCD[nSubItem].crBackground = (crBackground == -1) ? m_crWindow : crBackground;
  1056. }
  1057. UpdateSubItem(nItem, nSubItem);
  1058. return rc;
  1059. }
  1060. ///////////////////////////////////////////////////////////////////////////////
  1061. // DeleteItem
  1062. BOOL CXListCtrl::DeleteItem(int nItem)
  1063. {
  1064. ASSERT(nItem >= 0);
  1065. ASSERT(nItem < GetItemCount());
  1066. if ((nItem < 0) || nItem >= GetItemCount())
  1067. return FALSE;
  1068. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1069. if (pXLCD)
  1070. delete [] pXLCD;
  1071. CListCtrl::SetItemData(nItem, 0);
  1072. return CListCtrl::DeleteItem(nItem);
  1073. }
  1074. ///////////////////////////////////////////////////////////////////////////////
  1075. // DeleteAllItems
  1076. BOOL CXListCtrl::DeleteAllItems()
  1077. {
  1078. int n = GetItemCount();
  1079. for (int i = 0; i < n; i++)
  1080. {
  1081. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(i);
  1082. if (pXLCD)
  1083. delete [] pXLCD;
  1084. CListCtrl::SetItemData(i, 0);
  1085. }
  1086. return CListCtrl::DeleteAllItems();
  1087. }
  1088. ///////////////////////////////////////////////////////////////////////////////
  1089. // OnDestroy
  1090. void CXListCtrl::OnDestroy()
  1091. {
  1092. int n = GetItemCount();
  1093. for (int i = 0; i < n; i++)
  1094. {
  1095. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(i);
  1096. if (pXLCD)
  1097. delete [] pXLCD;
  1098. CListCtrl::SetItemData(i, 0);
  1099. }
  1100. CListCtrl::OnDestroy();
  1101. }
  1102. ///////////////////////////////////////////////////////////////////////////////
  1103. // SetProgress
  1104. //
  1105. // This function creates a progress bar in the specified subitem. The
  1106. // UpdateProgress function may then be called to update the progress
  1107. // percent. If bShowProgressText is TRUE, either the default text
  1108. // of "n%" or the custom percent text (lpszProgressText) will be
  1109. // displayed. If bShowProgressText is FALSE, only the progress bar
  1110. // will be displayed, with no text.
  1111. //
  1112. // Note that the lpszProgressText string should include the format
  1113. // specifier "%d": e.g., "Pct %d%%"
  1114. //
  1115. BOOL CXListCtrl::SetProgress(int nItem,
  1116. int nSubItem,
  1117. BOOL bShowProgressText /*= TRUE*/,
  1118. LPCTSTR lpszProgressText /*= NULL*/)
  1119. {
  1120. ASSERT(nItem >= 0);
  1121. ASSERT(nItem < GetItemCount());
  1122. if ((nItem < 0) || nItem >= GetItemCount())
  1123. return FALSE;
  1124. ASSERT(nSubItem >= 0);
  1125. ASSERT(nSubItem < GetColumns());
  1126. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1127. return FALSE;
  1128. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1129. if (!pXLCD)
  1130. {
  1131. return FALSE;
  1132. }
  1133. pXLCD[nSubItem].bShowProgress = TRUE;
  1134. pXLCD[nSubItem].nProgressPercent = 0;
  1135. pXLCD[nSubItem].bShowProgressMessage = bShowProgressText;
  1136. pXLCD[nSubItem].strProgressMessage = lpszProgressText;
  1137. UpdateSubItem(nItem, nSubItem);
  1138. return TRUE;
  1139. }
  1140. ///////////////////////////////////////////////////////////////////////////////
  1141. // DeleteProgress
  1142. void CXListCtrl::DeleteProgress(int nItem, int nSubItem)
  1143. {
  1144. ASSERT(nItem >= 0);
  1145. ASSERT(nItem < GetItemCount());
  1146. if ((nItem < 0) || nItem >= GetItemCount())
  1147. return;
  1148. ASSERT(nSubItem >= 0);
  1149. ASSERT(nSubItem < GetColumns());
  1150. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1151. return;
  1152. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1153. if (!pXLCD)
  1154. {
  1155. return;
  1156. }
  1157. pXLCD[nSubItem].bShowProgress = FALSE;
  1158. pXLCD[nSubItem].nProgressPercent = 0;
  1159. UpdateSubItem(nItem, nSubItem);
  1160. }
  1161. ///////////////////////////////////////////////////////////////////////////////
  1162. // UpdateProgress
  1163. void CXListCtrl::UpdateProgress(int nItem, int nSubItem, int nPercent)
  1164. {
  1165. ASSERT(nItem >= 0);
  1166. ASSERT(nItem < GetItemCount());
  1167. if ((nItem < 0) || nItem >= GetItemCount())
  1168. return;
  1169. ASSERT(nSubItem >= 0);
  1170. ASSERT(nSubItem < GetColumns());
  1171. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1172. return;
  1173. ASSERT(nPercent >= 0 && nPercent <= 100);
  1174. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1175. if (!pXLCD)
  1176. {
  1177. return;
  1178. }
  1179. pXLCD[nSubItem].nProgressPercent = nPercent;
  1180. UpdateSubItem(nItem, nSubItem);
  1181. }
  1182. ///////////////////////////////////////////////////////////////////////////////
  1183. // SetCheckbox
  1184. BOOL CXListCtrl::SetCheckbox(int nItem, int nSubItem, int nCheckedState)
  1185. {
  1186. ASSERT(nItem >= 0);
  1187. ASSERT(nItem < GetItemCount());
  1188. if ((nItem < 0) || nItem >= GetItemCount())
  1189. return FALSE;
  1190. ASSERT(nSubItem >= 0);
  1191. ASSERT(nSubItem < GetColumns());
  1192. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1193. return FALSE;
  1194. ASSERT(nCheckedState == 0 || nCheckedState == 1 || nCheckedState == -1);
  1195. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1196. if (!pXLCD)
  1197. {
  1198. return FALSE;
  1199. }
  1200. // update checkbox in subitem
  1201. pXLCD[nSubItem].nCheckedState = nCheckedState;
  1202. UpdateSubItem(nItem, nSubItem);
  1203. // now update checkbox in column header
  1204. // -1 = no checkbox in column header
  1205. if (GetHeaderCheckedState(nSubItem) != XHEADERCTRL_NO_IMAGE)
  1206. {
  1207. int nCheckedCount = CountCheckedItems(nSubItem);
  1208. if (nCheckedCount == GetItemCount())
  1209. SetHeaderCheckedState(nSubItem, XHEADERCTRL_CHECKED_IMAGE);
  1210. else
  1211. SetHeaderCheckedState(nSubItem, XHEADERCTRL_UNCHECKED_IMAGE);
  1212. }
  1213. return TRUE;
  1214. }
  1215. ///////////////////////////////////////////////////////////////////////////////
  1216. // GetCheckbox
  1217. int CXListCtrl::GetCheckbox(int nItem, int nSubItem)
  1218. {
  1219. ASSERT(nItem >= 0);
  1220. ASSERT(nItem < GetItemCount());
  1221. if ((nItem < 0) || nItem >= GetItemCount())
  1222. return -1;
  1223. ASSERT(nSubItem >= 0);
  1224. ASSERT(nSubItem < GetColumns());
  1225. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1226. return -1;
  1227. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1228. if (!pXLCD)
  1229. {
  1230. return -1;
  1231. }
  1232. return pXLCD[nSubItem].nCheckedState;
  1233. }
  1234. ///////////////////////////////////////////////////////////////////////////////
  1235. // GetEnabled
  1236. //
  1237. // Note that GetEnabled and SetEnabled only Get/Set the enabled flag from
  1238. // subitem 0, since this is a per-row flag.
  1239. //
  1240. BOOL CXListCtrl::GetEnabled(int nItem)
  1241. {
  1242. ASSERT(nItem >= 0);
  1243. ASSERT(nItem < GetItemCount());
  1244. if ((nItem < 0) || nItem >= GetItemCount())
  1245. return FALSE;
  1246. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1247. if (!pXLCD)
  1248. {
  1249. return FALSE;
  1250. }
  1251. return pXLCD[0].bEnabled;
  1252. }
  1253. ///////////////////////////////////////////////////////////////////////////////
  1254. // SetEnabled
  1255. BOOL CXListCtrl::SetEnabled(int nItem, BOOL bEnable)
  1256. {
  1257. ASSERT(nItem >= 0);
  1258. ASSERT(nItem < GetItemCount());
  1259. if ((nItem < 0) || nItem >= GetItemCount())
  1260. return FALSE;
  1261. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1262. if (!pXLCD)
  1263. {
  1264. return FALSE;
  1265. }
  1266. pXLCD[0].bEnabled = bEnable;
  1267. CRect rect;
  1268. GetItemRect(nItem, &rect, LVIR_BOUNDS);
  1269. InvalidateRect(&rect);
  1270. UpdateWindow();
  1271. return TRUE;
  1272. }
  1273. ///////////////////////////////////////////////////////////////////////////////
  1274. // SetBold
  1275. BOOL CXListCtrl::SetBold(int nItem, int nSubItem, BOOL bBold)
  1276. {
  1277. ASSERT(nItem >= 0);
  1278. ASSERT(nItem < GetItemCount());
  1279. if ((nItem < 0) || nItem >= GetItemCount())
  1280. return FALSE;
  1281. ASSERT(nSubItem >= 0);
  1282. ASSERT(nSubItem < GetColumns());
  1283. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1284. return FALSE;
  1285. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1286. if (!pXLCD)
  1287. {
  1288. return FALSE;
  1289. }
  1290. // update bold flag
  1291. pXLCD[nSubItem].bBold = bBold;
  1292. UpdateSubItem(nItem, nSubItem);
  1293. return TRUE;
  1294. }
  1295. ///////////////////////////////////////////////////////////////////////////////
  1296. // GetBold
  1297. BOOL CXListCtrl::GetBold(int nItem, int nSubItem)
  1298. {
  1299. ASSERT(nItem >= 0);
  1300. ASSERT(nItem < GetItemCount());
  1301. if ((nItem < 0) || nItem >= GetItemCount())
  1302. return FALSE;
  1303. ASSERT(nSubItem >= 0);
  1304. ASSERT(nSubItem < GetColumns());
  1305. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1306. return FALSE;
  1307. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1308. if (!pXLCD)
  1309. {
  1310. return FALSE;
  1311. }
  1312. // update bold flag
  1313. return pXLCD[nSubItem].bBold;
  1314. }
  1315. ///////////////////////////////////////////////////////////////////////////////
  1316. // SetComboBox
  1317. //
  1318. // Note: SetItemText may also be used to set the initial combo selection.
  1319. //
  1320. BOOL CXListCtrl::SetComboBox(int nItem,
  1321. int nSubItem,
  1322. BOOL bEnableCombo,
  1323. CStringArray *psa,
  1324. int nComboListHeight,
  1325. int nInitialComboSel)
  1326. {
  1327. ASSERT(nItem >= 0);
  1328. ASSERT(nItem < GetItemCount());
  1329. if ((nItem < 0) || nItem >= GetItemCount())
  1330. return FALSE;
  1331. ASSERT(nSubItem >= 0);
  1332. ASSERT(nSubItem < GetColumns());
  1333. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1334. return FALSE;
  1335. ASSERT(psa);
  1336. if (!psa)
  1337. return FALSE;
  1338. ASSERT(nComboListHeight > 0);
  1339. ASSERT(nInitialComboSel >= 0 && nInitialComboSel < psa->GetSize());
  1340. if ((nInitialComboSel < 0) || (nInitialComboSel >= psa->GetSize()))
  1341. nInitialComboSel = 0;
  1342. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1343. if (!pXLCD)
  1344. {
  1345. return FALSE;
  1346. }
  1347. // update flag
  1348. pXLCD[nSubItem].bCombo = bEnableCombo;
  1349. if (bEnableCombo)
  1350. {
  1351. pXLCD[nSubItem].psa = psa;
  1352. pXLCD[nSubItem].nComboListHeight = nComboListHeight;
  1353. pXLCD[nSubItem].nInitialComboSel = nInitialComboSel;
  1354. if (pXLCD[nSubItem].psa)
  1355. {
  1356. int index = 0;
  1357. if ((pXLCD[nSubItem].nInitialComboSel >= 0) &&
  1358. (pXLCD[nSubItem].psa->GetSize() > pXLCD[nSubItem].nInitialComboSel))
  1359. {
  1360. index = pXLCD[nSubItem].nInitialComboSel;
  1361. CString str;
  1362. str = pXLCD[nSubItem].psa->GetAt(index);
  1363. SetItemText(nItem, nSubItem, str);
  1364. }
  1365. }
  1366. }
  1367. UpdateSubItem(nItem, nSubItem);
  1368. return TRUE;
  1369. }
  1370. ///////////////////////////////////////////////////////////////////////////////
  1371. // GetComboText
  1372. //
  1373. // Actually this does nothing more than GetItemText()
  1374. //
  1375. CString CXListCtrl::GetComboText(int nItem, int nSubItem)
  1376. {
  1377. ASSERT(nItem >= 0);
  1378. ASSERT(nItem < GetItemCount());
  1379. if ((nItem < 0) || nItem >= GetItemCount())
  1380. return _T("");
  1381. ASSERT(nSubItem >= 0);
  1382. ASSERT(nSubItem < GetColumns());
  1383. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1384. return _T("");
  1385. CString str;
  1386. str = _T("");
  1387. str = GetItemText(nItem, nSubItem);
  1388. return str;
  1389. }
  1390. ///////////////////////////////////////////////////////////////////////////////
  1391. // SetCurSel
  1392. BOOL CXListCtrl::SetCurSel(int nItem)
  1393. {
  1394. return SetItemState(nItem, LVIS_FOCUSED | LVIS_SELECTED,
  1395. LVIS_FOCUSED | LVIS_SELECTED);
  1396. }
  1397. ///////////////////////////////////////////////////////////////////////////////
  1398. // GetCurSel - returns selected item number, or -1 if no item selected
  1399. //
  1400. // Note: for single-selection lists only
  1401. //
  1402. int CXListCtrl::GetCurSel()
  1403. {
  1404. POSITION pos = GetFirstSelectedItemPosition();
  1405. int nSelectedItem = -1;
  1406. if (pos != NULL)
  1407. nSelectedItem = GetNextSelectedItem(pos);
  1408. return nSelectedItem;
  1409. }
  1410. ///////////////////////////////////////////////////////////////////////////////
  1411. // UpdateSubItem
  1412. void CXListCtrl::UpdateSubItem(int nItem, int nSubItem)
  1413. {
  1414. ASSERT(nItem >= 0);
  1415. ASSERT(nItem < GetItemCount());
  1416. if ((nItem < 0) || nItem >= GetItemCount())
  1417. return;
  1418. ASSERT(nSubItem >= 0);
  1419. ASSERT(nSubItem < GetColumns());
  1420. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1421. return;
  1422. CRect rect;
  1423. if (nSubItem == -1)
  1424. {
  1425. GetItemRect(nItem, &rect, LVIR_BOUNDS);
  1426. }
  1427. else
  1428. {
  1429. GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);
  1430. }
  1431. InvalidateRect(&rect);
  1432. UpdateWindow();
  1433. }
  1434. ///////////////////////////////////////////////////////////////////////////////
  1435. // GetColumns
  1436. int CXListCtrl::GetColumns()
  1437. {
  1438. return GetHeaderCtrl()->GetItemCount();
  1439. }
  1440. ///////////////////////////////////////////////////////////////////////////////
  1441. // GetItemData
  1442. //
  1443. // The GetItemData and SetItemData functions allow for app-specific data
  1444. // to be stored, by using an extra field in the XLISTCTRLDATA struct.
  1445. //
  1446. DWORD CXListCtrl::GetItemData(int nItem)
  1447. {
  1448. ASSERT(nItem >= 0);
  1449. ASSERT(nItem < GetItemCount());
  1450. if ((nItem < 0) || nItem >= GetItemCount())
  1451. return 0;
  1452. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1453. if (!pXLCD)
  1454. {
  1455. return 0;
  1456. }
  1457. return pXLCD->dwItemData;
  1458. }
  1459. ///////////////////////////////////////////////////////////////////////////////
  1460. // SetItemData
  1461. BOOL CXListCtrl::SetItemData(int nItem, DWORD dwData)
  1462. {
  1463. ASSERT(nItem >= 0);
  1464. ASSERT(nItem < GetItemCount());
  1465. if ((nItem < 0) || nItem >= GetItemCount())
  1466. return FALSE;
  1467. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1468. if (!pXLCD)
  1469. {
  1470. return FALSE;
  1471. }
  1472. pXLCD->dwItemData = dwData;
  1473. return TRUE;
  1474. }
  1475. ///////////////////////////////////////////////////////////////////////////////
  1476. // GetHeaderCheckedState
  1477. //
  1478. // The GetHeaderCheckedState and SetHeaderCheckedState may be used to toggle
  1479. // the checkbox in a column header.
  1480. // 0 = no checkbox
  1481. // 1 = unchecked
  1482. // 2 = checked
  1483. //
  1484. int CXListCtrl::GetHeaderCheckedState(int nSubItem)
  1485. {
  1486. ASSERT(nSubItem >= 0);
  1487. ASSERT(nSubItem < GetColumns());
  1488. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1489. return -1;
  1490. HDITEM hditem;
  1491. // use the image index (0 or 1) to indicate the checked status
  1492. hditem.mask = HDI_IMAGE;
  1493. m_HeaderCtrl.GetItem(nSubItem, &hditem);
  1494. return hditem.iImage;
  1495. }
  1496. ///////////////////////////////////////////////////////////////////////////////
  1497. // SetHeaderCheckedState
  1498. BOOL CXListCtrl::SetHeaderCheckedState(int nSubItem, int nCheckedState)
  1499. {
  1500. ASSERT(nSubItem >= 0);
  1501. ASSERT(nSubItem < GetColumns());
  1502. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1503. return FALSE;
  1504. ASSERT(nCheckedState == 0 || nCheckedState == 1 || nCheckedState == 2);
  1505. HDITEM hditem;
  1506. hditem.mask = HDI_IMAGE;
  1507. hditem.iImage = nCheckedState;
  1508. m_HeaderCtrl.SetItem(nSubItem, &hditem);
  1509. return TRUE;
  1510. }
  1511. ///////////////////////////////////////////////////////////////////////////////
  1512. // OnColumnClick
  1513. BOOL CXListCtrl::OnColumnClick(NMHDR* pNMHDR, LRESULT* pResult)
  1514. {
  1515. NMLISTVIEW* pnmlv = (NMLISTVIEW*)pNMHDR;
  1516. int nSubItem = pnmlv->iSubItem;
  1517. int nCheckedState = GetHeaderCheckedState(nSubItem);
  1518. // 0 = no checkbox
  1519. if (nCheckedState != XHEADERCTRL_NO_IMAGE)
  1520. {
  1521. nCheckedState = (nCheckedState == 1) ? 2 : 1;
  1522. SetHeaderCheckedState(nSubItem, nCheckedState);
  1523. m_HeaderCtrl.UpdateWindow();
  1524. for (int nItem = 0; nItem < GetItemCount(); nItem++)
  1525. {
  1526. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1527. if (!pXLCD)
  1528. {
  1529. continue;
  1530. }
  1531. if (pXLCD[nSubItem].nCheckedState != -1)
  1532. {
  1533. pXLCD[nSubItem].nCheckedState = nCheckedState - 1;
  1534. UpdateSubItem(nItem, nSubItem);
  1535. }
  1536. }
  1537. }
  1538. *pResult = 0;
  1539. return FALSE; // return FALSE to send message to parent also -
  1540. // NOTE: MSDN documentation is incorrect
  1541. }
  1542. ///////////////////////////////////////////////////////////////////////////////
  1543. // CountCheckedItems
  1544. int CXListCtrl::CountCheckedItems(int nSubItem)
  1545. {
  1546. ASSERT(nSubItem >= 0);
  1547. ASSERT(nSubItem < GetColumns());
  1548. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1549. return 0;
  1550. int nCount = 0;
  1551. for (int nItem = 0; nItem < GetItemCount(); nItem++)
  1552. {
  1553. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1554. if (!pXLCD)
  1555. {
  1556. continue;
  1557. }
  1558. if (pXLCD[nSubItem].nCheckedState == 1)
  1559. nCount++;
  1560. }
  1561. return nCount;
  1562. }
  1563. ///////////////////////////////////////////////////////////////////////////////
  1564. // OnSysColorChange
  1565. void CXListCtrl::OnSysColorChange()
  1566. {
  1567. TRACE(_T("in CXListCtrl::OnSysColorChange\n"));
  1568. CListCtrl::OnSysColorChange();
  1569. m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
  1570. m_cr3DHighLight = ::GetSysColor(COLOR_3DHIGHLIGHT);
  1571. m_cr3DShadow = ::GetSysColor(COLOR_3DSHADOW);
  1572. m_crBtnFace = ::GetSysColor(COLOR_BTNFACE);
  1573. m_crBtnShadow = ::GetSysColor(COLOR_BTNSHADOW);
  1574. m_crBtnText = ::GetSysColor(COLOR_BTNTEXT);
  1575. m_crGrayText = ::GetSysColor(COLOR_GRAYTEXT);
  1576. m_crHighLight = ::GetSysColor(COLOR_HIGHLIGHT);
  1577. m_crHighLightText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
  1578. m_crWindow = ::GetSysColor(COLOR_WINDOW);
  1579. m_crWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
  1580. }
  1581. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  1582. ///////////////////////////////////////////////////////////////////////////////
  1583. // UnpressComboButton
  1584. void CXListCtrl::UnpressComboButton()
  1585. {
  1586. static BOOL bFlag = FALSE;
  1587. if (bFlag)
  1588. return;
  1589. bFlag = TRUE;
  1590. if (m_bComboIsClicked)
  1591. {
  1592. if (m_nComboItem >= 0 && m_nComboItem < GetItemCount())
  1593. {
  1594. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(m_nComboItem);
  1595. if (pXLCD)
  1596. {
  1597. if (m_nComboSubItem >= 0 && m_nComboSubItem < GetColumns())
  1598. {
  1599. pXLCD[m_nComboSubItem].bComboIsClicked = FALSE;
  1600. UpdateSubItem(m_nComboItem, m_nComboSubItem);
  1601. }
  1602. }
  1603. }
  1604. }
  1605. m_bComboIsClicked = FALSE;
  1606. bFlag = FALSE;
  1607. }
  1608. ///////////////////////////////////////////////////////////////////////////////
  1609. // OnTimer
  1610. //
  1611. // Timer usage:
  1612. // 1 - used to check if combo button needs to be unpressed,set in
  1613. // OnLButtonDown (when combo button is clicked)
  1614. // 2 - used to close combo listbox, set in OnComboEscape (user hits Escape
  1615. // or listbox loses focus)
  1616. // 3 - used to get combo listbox selection, then close combo listbox,
  1617. // set in OnComboReturn and OnComboLButtonUp (user hits Enter
  1618. // or clicks on item in listbox)
  1619. // 4 - used to get combo listbox selection, set in OnComboKeydown (for
  1620. // example, user hits arrow key in listbox)
  1621. //
  1622. void CXListCtrl::OnTimer(UINT nIDEvent)
  1623. {
  1624. if (nIDEvent == 1) // timer set when combo button is clicked
  1625. {
  1626. if (m_bComboIsClicked)
  1627. {
  1628. POINT point;
  1629. ::GetCursorPos(&point);
  1630. ScreenToClient(&point);
  1631. if (!m_rectComboButton.PtInRect(point))
  1632. {
  1633. UnpressComboButton();
  1634. }
  1635. }
  1636. else if (m_pListBox)
  1637. {
  1638. m_pListBox->SetActive(11);
  1639. }
  1640. else
  1641. {
  1642. KillTimer(nIDEvent);
  1643. }
  1644. }
  1645. else if (nIDEvent == 2) // close combo listbox
  1646. {
  1647. KillTimer(nIDEvent);
  1648. if (m_pListBox)
  1649. {
  1650. m_pListBox->DestroyWindow();
  1651. delete m_pListBox;
  1652. }
  1653. m_pListBox = NULL;
  1654. }
  1655. else if (nIDEvent == 3) // get combo listbox selection, then close combo listbox
  1656. {
  1657. KillTimer(nIDEvent);
  1658. if (m_pListBox)
  1659. {
  1660. CString str;
  1661. int i = m_pListBox->GetCurSel();
  1662. if (i != LB_ERR)
  1663. {
  1664. m_pListBox->GetText(i, str);
  1665. if ((m_nComboItem >= 0 &&