XListCtrl.cpp 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704
  1. // XListCtrl.cpp Version 1.4 - article available at www.codeproject.com
  2. //
  3. // Author: Hans Dietrich
  4. // hdietrich@gmail.com
  5. //
  6. // History
  7. // Version 1.4 - 2006 September 1
  8. // - See article for changes
  9. //
  10. // Version 1.3 - 2005 February 9
  11. // - See article for changes
  12. //
  13. // Version 1.0 - 2002 February 4
  14. // - Initial public release
  15. //
  16. // License:
  17. // This software is released into the public domain. You are free to use
  18. // it in any way you like, except that you may not sell this source code.
  19. //
  20. // This software is provided "as is" with no expressed or implied warranty.
  21. // I accept no liability for any damage or loss of business that this
  22. // software may cause.
  23. //
  24. ///////////////////////////////////////////////////////////////////////////////
  25. #include "stdafx.h"
  26. #include "XListCtrl.h"
  27. #include "SortCStringArray.h"
  28. #ifdef _DEBUG
  29. #define new DEBUG_NEW
  30. #undef THIS_FILE
  31. static char THIS_FILE[] = __FILE__;
  32. #endif
  33. XLISTCTRLLIBDLLEXPORT UINT WM_XLISTCTRL_COMBO_SELECTION = ::RegisterWindowMessage(_T("WM_XLISTCTRL_COMBO_SELECTION"));
  34. XLISTCTRLLIBDLLEXPORT UINT WM_XLISTCTRL_EDIT_END = ::RegisterWindowMessage(_T("WM_XLISTCTRL_EDIT_END"));
  35. XLISTCTRLLIBDLLEXPORT UINT WM_XLISTCTRL_CHECKBOX_CLICKED = ::RegisterWindowMessage(_T("WM_XLISTCTRL_CHECKBOX_CLICKED"));
  36. /////////////////////////////////////////////////////////////////////////////
  37. // CXListCtrl
  38. BEGIN_MESSAGE_MAP(CXListCtrl, CListCtrl)
  39. //{{AFX_MSG_MAP(CXListCtrl)
  40. ON_NOTIFY_REFLECT_EX(NM_CLICK, OnClick)
  41. ON_NOTIFY_REFLECT_EX(LVN_COLUMNCLICK, OnColumnClick)
  42. ON_WM_CREATE()
  43. ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
  44. ON_WM_DESTROY()
  45. ON_WM_LBUTTONDOWN()
  46. ON_WM_PAINT()
  47. ON_WM_SYSCOLORCHANGE()
  48. ON_WM_ERASEBKGND()
  49. ON_WM_KEYDOWN()
  50. ON_WM_RBUTTONDOWN()
  51. ON_WM_NCLBUTTONDOWN()
  52. //}}AFX_MSG_MAP
  53. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  54. ON_WM_TIMER()
  55. ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_VK_ESCAPE, OnComboEscape)
  56. ON_REGISTERED_MESSAGE(WM_XCOMBOLIST_COMPLETE, OnComboComplete)
  57. #endif
  58. #ifndef NO_XLISTCTRL_TOOL_TIPS
  59. ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
  60. ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
  61. #endif
  62. ON_REGISTERED_MESSAGE(WM_XEDIT_KILL_FOCUS, OnXEditKillFocus)
  63. ON_REGISTERED_MESSAGE(WM_XEDIT_VK_ESCAPE, OnXEditEscape)
  64. END_MESSAGE_MAP()
  65. ///////////////////////////////////////////////////////////////////////////////
  66. // ctor
  67. CXListCtrl::CXListCtrl()
  68. {
  69. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  70. m_bComboIsClicked = FALSE;
  71. m_nComboItem = 0;
  72. m_nComboSubItem = 0;
  73. m_pCombo = NULL;
  74. m_bFontIsCreated = FALSE;
  75. #endif
  76. m_dwExtendedStyleX = 0;
  77. m_bHeaderIsSubclassed = FALSE;
  78. m_bUseEllipsis = TRUE; //+++
  79. m_bListModified = FALSE; //+++
  80. m_bInitialCheck = FALSE;
  81. m_strInitialString = _T("");
  82. m_nPadding = 5; //+++
  83. m_pEdit = NULL; //+++
  84. m_nEditItem = 0; //+++
  85. m_nEditSubItem = 0; //+++
  86. GetColors();
  87. }
  88. ///////////////////////////////////////////////////////////////////////////////
  89. // dtor
  90. CXListCtrl::~CXListCtrl()
  91. {
  92. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  93. if (m_pCombo)
  94. delete m_pCombo;
  95. #endif
  96. if (m_pEdit)
  97. delete m_pEdit;
  98. }
  99. ///////////////////////////////////////////////////////////////////////////////
  100. // PreSubclassWindow
  101. void CXListCtrl::PreSubclassWindow()
  102. {
  103. CListCtrl::PreSubclassWindow();
  104. // for Dialog based applications, this is a good place
  105. // to subclass the header control because the OnCreate()
  106. // function does not get called.
  107. SubclassHeaderControl();
  108. }
  109. ///////////////////////////////////////////////////////////////////////////////
  110. // OnCreate
  111. int CXListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
  112. {
  113. if (CListCtrl::OnCreate(lpCreateStruct) == -1)
  114. {
  115. ASSERT(FALSE);
  116. return -1;
  117. }
  118. // When the CXListCtrl object is created via a call to Create(), instead
  119. // of via a dialog box template, we must subclass the header control
  120. // window here because it does not exist when the PreSubclassWindow()
  121. // function is called.
  122. SubclassHeaderControl();
  123. return 0;
  124. }
  125. ///////////////////////////////////////////////////////////////////////////////
  126. // SubclassHeaderControl
  127. void CXListCtrl::SubclassHeaderControl()
  128. {
  129. if (m_bHeaderIsSubclassed)
  130. return;
  131. // if the list control has a header control window, then
  132. // subclass it
  133. // Thanks to Alberto Gattegno and Alon Peleg  and their article
  134. // "A Multiline Header Control Inside a CListCtrl" for easy way
  135. // to determine if the header control exists.
  136. CHeaderCtrl* pHeader = GetHeaderCtrl();
  137. if (pHeader)
  138. {
  139. VERIFY(m_HeaderCtrl.SubclassWindow(pHeader->m_hWnd));
  140. m_bHeaderIsSubclassed = TRUE;
  141. m_HeaderCtrl.SetListCtrl(this);
  142. }
  143. }
  144. ///////////////////////////////////////////////////////////////////////////////
  145. // OnClick
  146. BOOL CXListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
  147. {
  148. XLISTCTRL_TRACE(_T("in CXListCtrl::OnClick\n"));
  149. pNMHDR = pNMHDR;
  150. *pResult = 0;
  151. return FALSE; // return FALSE to send message to parent also -
  152. // NOTE: MSDN documentation is incorrect
  153. }
  154. ///////////////////////////////////////////////////////////////////////////////
  155. // OnCustomDraw
  156. void CXListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
  157. {
  158. NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
  159. // Take the default processing unless we set this to something else below.
  160. *pResult = CDRF_DODEFAULT;
  161. // First thing - check the draw stage. If it's the control's prepaint
  162. // stage, then tell Windows we want messages for every item.
  163. if (pLVCD->nmcd.dwDrawStage == CDDS_PREPAINT)
  164. {
  165. *pResult = CDRF_NOTIFYITEMDRAW;
  166. }
  167. else if (pLVCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
  168. {
  169. // This is the notification message for an item. We'll request
  170. // notifications before each subitem's prepaint stage.
  171. *pResult = CDRF_NOTIFYSUBITEMDRAW;
  172. }
  173. else if (pLVCD->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM))
  174. {
  175. // This is the prepaint stage for a subitem. Here's where we set the
  176. // item's text and background colors. Our return value will tell
  177. // Windows to draw the subitem itself, but it will use the new colors
  178. // we set here.
  179. int nItem = static_cast<int> (pLVCD->nmcd.dwItemSpec);
  180. int nSubItem = pLVCD->iSubItem;
  181. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) pLVCD->nmcd.lItemlParam;
  182. ASSERT(pXLCD);
  183. COLORREF crText = m_crWindowText;
  184. COLORREF crBkgnd = m_crWindow;
  185. if (pXLCD)
  186. {
  187. crText = pXLCD[nSubItem].crText;
  188. crBkgnd = pXLCD[nSubItem].crBackground;
  189. if (!pXLCD[0].bEnabled)
  190. crText = m_crGrayText;
  191. }
  192. // store the colors back in the NMLVCUSTOMDRAW struct
  193. pLVCD->clrText = crText;
  194. pLVCD->clrTextBk = crBkgnd;
  195. CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
  196. CRect rect;
  197. GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);
  198. if (pXLCD && (pXLCD[nSubItem].bShowProgress))
  199. {
  200. DrawProgress(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
  201. *pResult = CDRF_SKIPDEFAULT; // We've painted everything.
  202. }
  203. else if (pXLCD && (pXLCD[nSubItem].nCheckedState != -1))
  204. {
  205. DrawCheckbox(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
  206. *pResult = CDRF_SKIPDEFAULT; // We've painted everything.
  207. }
  208. else
  209. {
  210. rect.left += DrawImage(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
  211. DrawText(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
  212. *pResult = CDRF_SKIPDEFAULT; // We've painted everything.
  213. }
  214. }
  215. }
  216. ///////////////////////////////////////////////////////////////////////////////
  217. // DrawProgress
  218. void CXListCtrl::DrawProgress(int nItem,
  219. int nSubItem,
  220. CDC *pDC,
  221. COLORREF crText,
  222. COLORREF /*crBkgnd*/,
  223. CRect& rect,
  224. XLISTCTRLDATA *pXLCD)
  225. {
  226. UNUSED_ALWAYS(nItem);
  227. ASSERT(pDC);
  228. ASSERT(pXLCD);
  229. if (rect.IsRectEmpty())
  230. {
  231. return;
  232. }
  233. rect.bottom -= 1;
  234. rect.left += 1; // leave margin in case row is highlighted
  235. // fill interior with light gray
  236. pDC->FillSolidRect(rect, RGB(224,224,224));
  237. // draw border
  238. pDC->Draw3dRect(&rect, RGB(0,0,0), m_crBtnShadow);
  239. if (pXLCD[nSubItem].nProgressPercent > 0)
  240. {
  241. // draw progress bar and text
  242. CRect LeftRect, RightRect;
  243. LeftRect = rect;
  244. LeftRect.left += 1;
  245. LeftRect.top += 1;
  246. LeftRect.bottom -= 1;
  247. RightRect = LeftRect;
  248. int w = (LeftRect.Width() * pXLCD[nSubItem].nProgressPercent) / 100;
  249. LeftRect.right = LeftRect.left + w - 1;
  250. RightRect.left = LeftRect.right;
  251. pDC->FillSolidRect(LeftRect, m_crHighLight);
  252. if (pXLCD[nSubItem].bShowProgressMessage)
  253. {
  254. CString str, format;
  255. format = pXLCD[nSubItem].strProgressMessage;
  256. if (format.IsEmpty())
  257. str.Format(_T("%d%%"), pXLCD[nSubItem].nProgressPercent);
  258. else
  259. str.Format(format, pXLCD[nSubItem].nProgressPercent);
  260. pDC->SetBkMode(TRANSPARENT);
  261. CRect TextRect;
  262. TextRect = rect;
  263. TextRect.DeflateRect(1, 1);
  264. CRgn rgn;
  265. rgn.CreateRectRgn(LeftRect.left, LeftRect.top, LeftRect.right,
  266. LeftRect.bottom);
  267. pDC->SelectClipRgn(&rgn);
  268. pDC->SetTextColor(m_crHighLightText);//crBkgnd);
  269. pDC->DrawText(str, &TextRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  270. rgn.DeleteObject();
  271. rgn.CreateRectRgn(RightRect.left, RightRect.top, RightRect.right,
  272. RightRect.bottom);
  273. pDC->SelectClipRgn(&rgn);
  274. pDC->SetTextColor(crText);
  275. pDC->DrawText(str, &TextRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  276. rgn.DeleteObject();
  277. pDC->SelectClipRgn(NULL);
  278. }
  279. }
  280. }
  281. ///////////////////////////////////////////////////////////////////////////////
  282. // DrawCheckbox
  283. void CXListCtrl::DrawCheckbox(int nItem,
  284. int nSubItem,
  285. CDC *pDC,
  286. COLORREF crText,
  287. COLORREF crBkgnd,
  288. CRect& rect,
  289. XLISTCTRLDATA *pXLCD)
  290. {
  291. ASSERT(pDC);
  292. ASSERT(pXLCD);
  293. if (rect.IsRectEmpty())
  294. {
  295. return;
  296. }
  297. GetDrawColors(nItem, nSubItem, crText, crBkgnd);
  298. pDC->FillSolidRect(&rect, crBkgnd);
  299. CRect chkboxrect;
  300. chkboxrect = rect;
  301. chkboxrect.bottom -= 1;
  302. chkboxrect.left += 9; // line up checkbox with header checkbox
  303. chkboxrect.right = chkboxrect.left + chkboxrect.Height(); // width = height
  304. CString str;
  305. str = GetItemText(nItem, nSubItem);
  306. if (str.IsEmpty())
  307. {
  308. // center the checkbox
  309. chkboxrect.left = rect.left + rect.Width()/2 - chkboxrect.Height()/2 - 1;
  310. chkboxrect.right = chkboxrect.left + chkboxrect.Height();
  311. }
  312. // fill rect around checkbox with white
  313. pDC->FillSolidRect(&chkboxrect, m_crWindow);
  314. // draw border
  315. CBrush brush(RGB(51,102,153));
  316. pDC->FrameRect(&chkboxrect, &brush);
  317. if (pXLCD[nSubItem].nCheckedState == 1)
  318. {
  319. CPen *pOldPen = NULL;
  320. CPen graypen(PS_SOLID, 1, m_crGrayText);
  321. CPen blackpen(PS_SOLID, 1, RGB(51,153,51));
  322. if (pXLCD[0].bEnabled)
  323. pOldPen = pDC->SelectObject(&blackpen);
  324. else
  325. pOldPen = pDC->SelectObject(&graypen);
  326. // draw the checkmark
  327. int x = chkboxrect.left + 9;
  328. ASSERT(x < chkboxrect.right);
  329. int y = chkboxrect.top + 3;
  330. int i;
  331. for (i = 0; i < 4; i++)
  332. {
  333. pDC->MoveTo(x, y);
  334. pDC->LineTo(x, y+3);
  335. x--;
  336. y++;
  337. }
  338. for (i = 0; i < 3; i++)
  339. {
  340. pDC->MoveTo(x, y);
  341. pDC->LineTo(x, y+3);
  342. x--;
  343. y--;
  344. }
  345. if (pOldPen)
  346. pDC->SelectObject(pOldPen);
  347. }
  348. if (!str.IsEmpty())
  349. {
  350. pDC->SetBkMode(TRANSPARENT);
  351. pDC->SetTextColor(crText);
  352. pDC->SetBkColor(crBkgnd);
  353. CRect textrect;
  354. textrect = rect;
  355. textrect.left = chkboxrect.right + 4;
  356. UINT nFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE; //+++
  357. if (m_bUseEllipsis)
  358. nFormat |= DT_END_ELLIPSIS;
  359. pDC->DrawText(str, &textrect, nFormat);
  360. }
  361. }
  362. ///////////////////////////////////////////////////////////////////////////////
  363. // GetDrawColors
  364. void CXListCtrl::GetDrawColors(int nItem,
  365. int nSubItem,
  366. COLORREF& colorText,
  367. COLORREF& colorBkgnd)
  368. {
  369. DWORD dwStyle = GetStyle();
  370. DWORD dwExStyle = GetExtendedStyle();
  371. COLORREF crText = colorText;
  372. COLORREF crBkgnd = colorBkgnd;
  373. if (GetItemState(nItem, LVIS_SELECTED))
  374. {
  375. if (dwExStyle & LVS_EX_FULLROWSELECT)
  376. {
  377. // selected? if so, draw highlight background
  378. crText = m_crHighLightText;
  379. crBkgnd = m_crHighLight;
  380. // has focus? if not, draw gray background
  381. if (m_hWnd != ::GetFocus())
  382. {
  383. if (dwStyle & LVS_SHOWSELALWAYS)
  384. {
  385. crText = m_crWindowText;
  386. crBkgnd = m_crBtnFace;
  387. }
  388. else
  389. {
  390. crText = colorText;
  391. crBkgnd = colorBkgnd;
  392. }
  393. }
  394. }
  395. else // not full row select
  396. {
  397. if (nSubItem == 0)
  398. {
  399. // selected? if so, draw highlight background
  400. crText = m_crHighLightText;
  401. crBkgnd = m_crHighLight;
  402. // has focus? if not, draw gray background
  403. if (m_hWnd != ::GetFocus())
  404. {
  405. if (dwStyle & LVS_SHOWSELALWAYS)
  406. {
  407. crText = m_crWindowText;
  408. crBkgnd = m_crBtnFace;
  409. }
  410. else
  411. {
  412. crText = colorText;
  413. crBkgnd = colorBkgnd;
  414. }
  415. }
  416. }
  417. }
  418. }
  419. colorText = crText;
  420. colorBkgnd = crBkgnd;
  421. }
  422. ///////////////////////////////////////////////////////////////////////////////
  423. // DrawImage
  424. int CXListCtrl::DrawImage(int nItem,
  425. int nSubItem,
  426. CDC* pDC,
  427. COLORREF crText,
  428. COLORREF crBkgnd,
  429. CRect rect,
  430. XLISTCTRLDATA *pXLCD)
  431. {
  432. if (rect.IsRectEmpty())
  433. {
  434. return 0;
  435. }
  436. GetDrawColors(nItem, nSubItem, crText, crBkgnd);
  437. pDC->FillSolidRect(&rect, crBkgnd);
  438. int nWidth = 0;
  439. rect.left += m_HeaderCtrl.GetSpacing();
  440. CImageList* pImageList = GetImageList(LVSIL_SMALL);
  441. if (pImageList)
  442. {
  443. SIZE sizeImage;
  444. sizeImage.cx = sizeImage.cy = 0;
  445. IMAGEINFO info;
  446. int nImage = -1;
  447. if (pXLCD)
  448. nImage = pXLCD[nSubItem].nImage;
  449. if (nImage == -1)
  450. return 0;
  451. if (pImageList->GetImageInfo(nImage, &info))
  452. {
  453. sizeImage.cx = info.rcImage.right - info.rcImage.left;
  454. sizeImage.cy = info.rcImage.bottom - info.rcImage.top;
  455. }
  456. if (nImage >= 0)
  457. {
  458. if (rect.Width() > 0)
  459. {
  460. POINT point;
  461. point.y = rect.CenterPoint().y - (sizeImage.cy >> 1);
  462. point.x = rect.left;
  463. SIZE size;
  464. size.cx = rect.Width() < sizeImage.cx ? rect.Width() : sizeImage.cx;
  465. size.cy = rect.Height() < sizeImage.cy ? rect.Height() : sizeImage.cy;
  466. // save image list background color
  467. COLORREF rgb = pImageList->GetBkColor();
  468. // set image list background color
  469. pImageList->SetBkColor(crBkgnd);
  470. pImageList->DrawIndirect(pDC, nImage, point, size, CPoint(0, 0));
  471. pImageList->SetBkColor(rgb);
  472. nWidth = sizeImage.cx + m_HeaderCtrl.GetSpacing();
  473. }
  474. }
  475. }
  476. return nWidth;
  477. }
  478. ///////////////////////////////////////////////////////////////////////////////
  479. // DrawText
  480. void CXListCtrl::DrawText(int nItem,
  481. int nSubItem,
  482. CDC *pDC,
  483. COLORREF crText,
  484. COLORREF crBkgnd,
  485. CRect& rect,
  486. XLISTCTRLDATA *pXLCD)
  487. {
  488. ASSERT(pDC);
  489. ASSERT(pXLCD);
  490. if (rect.IsRectEmpty())
  491. {
  492. return;
  493. }
  494. GetDrawColors(nItem, nSubItem, crText, crBkgnd);
  495. pDC->FillSolidRect(&rect, crBkgnd);
  496. CString str;
  497. str = GetItemText(nItem, nSubItem);
  498. if (!str.IsEmpty())
  499. {
  500. // get text justification
  501. HDITEM hditem;
  502. hditem.mask = HDI_FORMAT;
  503. m_HeaderCtrl.GetItem(nSubItem, &hditem);
  504. int nFmt = hditem.fmt & HDF_JUSTIFYMASK;
  505. UINT nFormat = DT_VCENTER | DT_SINGLELINE;
  506. if (m_bUseEllipsis) //+++
  507. nFormat |= DT_END_ELLIPSIS;
  508. if (nFmt == HDF_CENTER)
  509. nFormat |= DT_CENTER;
  510. else if (nFmt == HDF_LEFT)
  511. nFormat |= DT_LEFT;
  512. else
  513. nFormat |= DT_RIGHT;
  514. CFont *pOldFont = NULL;
  515. CFont boldfont;
  516. // check if bold specified for subitem
  517. if (pXLCD && pXLCD[nSubItem].bBold)
  518. {
  519. CFont *font = pDC->GetCurrentFont();
  520. if (font)
  521. {
  522. LOGFONT lf;
  523. font->GetLogFont(&lf);
  524. lf.lfWeight = FW_BOLD;
  525. boldfont.CreateFontIndirect(&lf);
  526. pOldFont = pDC->SelectObject(&boldfont);
  527. }
  528. }
  529. pDC->SetBkMode(TRANSPARENT);
  530. pDC->SetTextColor(crText);
  531. pDC->SetBkColor(crBkgnd);
  532. rect.DeflateRect(m_nPadding, 0); //+++
  533. pDC->DrawText(str, &rect, nFormat);
  534. rect.InflateRect(m_nPadding, 0); //+++
  535. if (pOldFont)
  536. pDC->SelectObject(pOldFont);
  537. }
  538. }
  539. ///////////////////////////////////////////////////////////////////////////////
  540. // GetSubItemRect
  541. BOOL CXListCtrl::GetSubItemRect(int nItem,
  542. int nSubItem,
  543. int nArea,
  544. CRect& rect)
  545. {
  546. ASSERT(nItem >= 0);
  547. ASSERT(nItem < GetItemCount());
  548. if ((nItem < 0) || nItem >= GetItemCount())
  549. return FALSE;
  550. ASSERT(nSubItem >= 0);
  551. ASSERT(nSubItem < GetColumns());
  552. if ((nSubItem < 0) || nSubItem >= GetColumns())
  553. return FALSE;
  554. BOOL bRC = CListCtrl::GetSubItemRect(nItem, nSubItem, nArea, rect);
  555. // if nSubItem == 0, the rect returned by CListCtrl::GetSubItemRect
  556. // is the entire row, so use left edge of second subitem
  557. if (nSubItem == 0)
  558. {
  559. if (GetColumns() > 1)
  560. {
  561. CRect rect1;
  562. bRC = GetSubItemRect(nItem, 1, LVIR_BOUNDS, rect1);
  563. rect.right = rect1.left;
  564. }
  565. }
  566. //+++
  567. if (nSubItem == 0)
  568. {
  569. if (GetColumns() > 1)
  570. {
  571. CRect rect1;
  572. // in case 2nd col width = 0
  573. for (int i = 1; i < GetColumns(); i++)
  574. {
  575. bRC = GetSubItemRect(nItem, i, LVIR_BOUNDS, rect1);
  576. if (rect1.Width() > 0)
  577. {
  578. rect.right = rect1.left;
  579. break;
  580. }
  581. }
  582. }
  583. }
  584. return bRC;
  585. }
  586. ///////////////////////////////////////////////////////////////////////////////
  587. // OnLButtonDown
  588. void CXListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
  589. {
  590. XLISTCTRL_TRACE(_T("in CXListCtrl::OnLButtonDown\n"));
  591. int nItem = -1;
  592. //+++
  593. LVHITTESTINFO lvhit;
  594. lvhit.pt = point;
  595. SubItemHitTest(&lvhit);
  596. if (lvhit.flags & LVHT_ONITEMLABEL)
  597. {
  598. XLISTCTRL_TRACE(_T("lvhit.iItem=%d lvhit.iSubItem=%d ~~~~~\n"), lvhit.iItem, lvhit.iSubItem);
  599. nItem = lvhit.iItem;
  600. }
  601. if (nItem == -1)
  602. {
  603. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  604. if (m_pCombo)
  605. OnComboEscape(0, 0);
  606. #endif
  607. }
  608. else
  609. {
  610. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  611. if (!pXLCD)
  612. {
  613. return;
  614. }
  615. if (!pXLCD[0].bEnabled)
  616. return;
  617. CRect rect;
  618. int nSubItem = -1;
  619. // check if a subitem checkbox was clicked
  620. for (int i = 0; i < GetColumns(); i++)
  621. {
  622. GetSubItemRect(nItem, i, LVIR_BOUNDS, rect);
  623. if (rect.PtInRect(point))
  624. {
  625. nSubItem = i;
  626. break;
  627. }
  628. }
  629. if (nSubItem == -1)
  630. {
  631. // -1 = no checkbox for this subitem
  632. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  633. if (m_pCombo)
  634. {
  635. OnComboEscape(0, 0);
  636. }
  637. #endif
  638. }
  639. else
  640. {
  641. if (pXLCD[nSubItem].nCheckedState >= 0)
  642. {
  643. int nChecked = pXLCD[nSubItem].nCheckedState;
  644. nChecked = (nChecked == 0) ? 1 : 0;
  645. pXLCD[nSubItem].nCheckedState = nChecked;
  646. pXLCD[nSubItem].bModified = TRUE;
  647. m_bListModified = TRUE;
  648. UpdateSubItem(nItem, nSubItem);
  649. CWnd *pWnd = GetParent();
  650. if (!pWnd)
  651. pWnd = GetOwner();
  652. if (pWnd && ::IsWindow(pWnd->m_hWnd))
  653. pWnd->SendMessage(WM_XLISTCTRL_CHECKBOX_CLICKED,
  654. nItem, nSubItem);
  655. // now update checkbox in header
  656. // -1 = no checkbox in column header
  657. if (GetHeaderCheckedState(nSubItem) != XHEADERCTRL_NO_IMAGE)
  658. {
  659. int nCheckedCount = CountCheckedItems(nSubItem);
  660. if (nCheckedCount == GetItemCount())
  661. SetHeaderCheckedState(nSubItem, XHEADERCTRL_CHECKED_IMAGE);
  662. else
  663. SetHeaderCheckedState(nSubItem, XHEADERCTRL_UNCHECKED_IMAGE);
  664. }
  665. }
  666. else if (pXLCD[nSubItem].bCombo)
  667. {
  668. CListCtrl::OnLButtonDown(nFlags, point);
  669. DrawComboBox(nItem, nSubItem);
  670. }
  671. else if (pXLCD[nSubItem].bEdit)
  672. {
  673. CListCtrl::OnLButtonDown(nFlags, point);
  674. DrawEdit(nItem, nSubItem);
  675. }
  676. }
  677. }
  678. CListCtrl::OnLButtonDown(nFlags, point);
  679. }
  680. ///////////////////////////////////////////////////////////////////////////////
  681. // OnRButtonDown - added so we can ignore disabled items
  682. void CXListCtrl::OnRButtonDown(UINT nFlags, CPoint point)
  683. {
  684. XLISTCTRL_TRACE(_T("in CXListCtrl::OnRButtonDown\n"));
  685. int nItem = -1;
  686. //+++
  687. LVHITTESTINFO lvhit;
  688. lvhit.pt = point;
  689. SubItemHitTest(&lvhit);
  690. if (lvhit.flags & LVHT_ONITEMLABEL)
  691. {
  692. XLISTCTRL_TRACE(_T("lvhit.iItem=%d lvhit.iSubItem=%d ~~~~~\n"), lvhit.iItem, lvhit.iSubItem);
  693. nItem = lvhit.iItem;
  694. }
  695. if (nItem != -1)
  696. {
  697. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  698. if (!pXLCD)
  699. {
  700. return;
  701. }
  702. if (!pXLCD[0].bEnabled)
  703. return;
  704. }
  705. CListCtrl::OnRButtonDown(nFlags, point);
  706. }
  707. ///////////////////////////////////////////////////////////////////////////////
  708. // OnNcLButtonDown
  709. void CXListCtrl::OnNcLButtonDown(UINT nHitTest, CPoint point)
  710. {
  711. TRACE(_T("in CXListCtrl::OnNcLButtonDown\n"));
  712. if (m_pEdit)
  713. {
  714. OnXEditKillFocus(0, 0);
  715. }
  716. CListCtrl::OnNcLButtonDown(nHitTest, point);
  717. }
  718. ///////////////////////////////////////////////////////////////////////////////
  719. // OnPaint
  720. void CXListCtrl::OnPaint()
  721. {
  722. Default();
  723. if (GetItemCount() <= 0)
  724. {
  725. CDC* pDC = GetDC();
  726. int nSavedDC = pDC->SaveDC();
  727. CRect rc;
  728. GetWindowRect(&rc);
  729. ScreenToClient(&rc);
  730. CHeaderCtrl* pHC = GetHeaderCtrl();
  731. if (pHC != NULL)
  732. {
  733. CRect rcH;
  734. pHC->GetItemRect(0, &rcH);
  735. rc.top += rcH.bottom;
  736. }
  737. rc.top += 10;
  738. CString strText;
  739. strText = _T("There are no items to show in this view.");
  740. COLORREF crText = m_crWindowText;
  741. COLORREF crBkgnd = m_crWindow;
  742. CBrush brush(crBkgnd);
  743. pDC->FillRect(rc, &brush);
  744. pDC->SetTextColor(crText);
  745. pDC->SetBkColor(crBkgnd);
  746. pDC->SelectStockObject(ANSI_VAR_FONT);
  747. pDC->DrawText(strText, -1, rc, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_NOCLIP);
  748. pDC->RestoreDC(nSavedDC);
  749. ReleaseDC(pDC);
  750. }
  751. }
  752. ///////////////////////////////////////////////////////////////////////////////
  753. // InsertItem
  754. int CXListCtrl::InsertItem(const LVITEM* pItem)
  755. {
  756. ASSERT(pItem->iItem >= 0);
  757. if (pItem->iItem < 0)
  758. return -1;
  759. int index = CListCtrl::InsertItem(pItem);
  760. if (index < 0)
  761. return index;
  762. XLISTCTRLDATA *pXLCD = new XLISTCTRLDATA [GetColumns()];
  763. ASSERT(pXLCD);
  764. if (!pXLCD)
  765. return -1;
  766. pXLCD[0].crText = m_crWindowText;
  767. pXLCD[0].crBackground = m_crWindow;
  768. pXLCD[0].nImage = pItem->iImage;
  769. pXLCD[0].dwItemData = pItem->lParam; //+++
  770. CListCtrl::SetItemData(index, (DWORD) pXLCD);
  771. return index;
  772. }
  773. ///////////////////////////////////////////////////////////////////////////////
  774. // InsertItem
  775. int CXListCtrl::InsertItem(int nItem, LPCTSTR lpszItem)
  776. {
  777. ASSERT(nItem >= 0);
  778. if (nItem < 0)
  779. return -1;
  780. return InsertItem(nItem,
  781. lpszItem,
  782. m_crWindowText,
  783. m_crWindow);
  784. }
  785. ///////////////////////////////////////////////////////////////////////////////
  786. // InsertItem
  787. int CXListCtrl::InsertItem(int nItem,
  788. LPCTSTR lpszItem,
  789. COLORREF crText,
  790. COLORREF crBackground)
  791. {
  792. ASSERT(nItem >= 0);
  793. if (nItem < 0)
  794. return -1;
  795. int index = CListCtrl::InsertItem(nItem, lpszItem);
  796. if (index < 0)
  797. return index;
  798. XLISTCTRLDATA *pXLCD = new XLISTCTRLDATA [GetColumns()];
  799. ASSERT(pXLCD);
  800. if (!pXLCD)
  801. return -1;
  802. pXLCD[0].crText = crText;
  803. pXLCD[0].crBackground = crBackground;
  804. pXLCD[0].nImage = -1;
  805. CListCtrl::SetItemData(index, (DWORD) pXLCD);
  806. return index;
  807. }
  808. ///////////////////////////////////////////////////////////////////////////////
  809. // SetItem
  810. int CXListCtrl::SetItem(const LVITEM* pItem)
  811. {
  812. ASSERT(pItem->iItem >= 0);
  813. if (pItem->iItem < 0)
  814. return -1;
  815. BOOL rc = CListCtrl::SetItem(pItem);
  816. if (!rc)
  817. return FALSE;
  818. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(pItem->iItem);
  819. if (pXLCD)
  820. {
  821. pXLCD[pItem->iSubItem].nImage = pItem->iImage;
  822. UpdateSubItem(pItem->iItem, pItem->iSubItem);
  823. rc = TRUE;
  824. }
  825. else
  826. {
  827. rc = FALSE;
  828. }
  829. return rc;
  830. }
  831. ///////////////////////////////////////////////////////////////////////////////
  832. // SetItemImage
  833. BOOL CXListCtrl::SetItemImage(int nItem, int nSubItem, int nImage)
  834. {
  835. ASSERT(nItem >= 0);
  836. ASSERT(nItem < GetItemCount());
  837. if ((nItem < 0) || nItem >= GetItemCount())
  838. return FALSE;
  839. ASSERT(nSubItem >= 0);
  840. ASSERT(nSubItem < GetColumns());
  841. if ((nSubItem < 0) || nSubItem >= GetColumns())
  842. return FALSE;
  843. BOOL rc = TRUE;
  844. if (nItem < 0)
  845. return FALSE;
  846. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  847. if (pXLCD)
  848. {
  849. pXLCD[nSubItem].nImage = nImage;
  850. }
  851. UpdateSubItem(nItem, nSubItem);
  852. return rc;
  853. }
  854. ///////////////////////////////////////////////////////////////////////////////
  855. // SetItemText
  856. BOOL CXListCtrl::SetItemText(int nItem, int nSubItem, LPCTSTR lpszText)
  857. {
  858. ASSERT(nItem >= 0);
  859. ASSERT(nItem < GetItemCount());
  860. if ((nItem < 0) || nItem >= GetItemCount())
  861. return FALSE;
  862. ASSERT(nSubItem >= 0);
  863. ASSERT(nSubItem < GetColumns());
  864. if ((nSubItem < 0) || nSubItem >= GetColumns())
  865. return FALSE;
  866. BOOL rc = CListCtrl::SetItemText(nItem, nSubItem, lpszText);
  867. UpdateSubItem(nItem, nSubItem);
  868. return rc;
  869. }
  870. ///////////////////////////////////////////////////////////////////////////////
  871. // SetItemText
  872. //
  873. // This function will set the text and colors for a subitem. If lpszText
  874. // is NULL, only the colors will be set. If a color value is -1, the display
  875. // color will be set to the default Windows color.
  876. //
  877. BOOL CXListCtrl::SetItemText(int nItem, int nSubItem, LPCTSTR lpszText,
  878. COLORREF crText, COLORREF crBackground)
  879. {
  880. ASSERT(nItem >= 0);
  881. ASSERT(nItem < GetItemCount());
  882. if ((nItem < 0) || nItem >= GetItemCount())
  883. return FALSE;
  884. ASSERT(nSubItem >= 0);
  885. ASSERT(nSubItem < GetColumns());
  886. if ((nSubItem < 0) || nSubItem >= GetColumns())
  887. return FALSE;
  888. BOOL rc = TRUE;
  889. if (nItem < 0)
  890. return FALSE;
  891. if (lpszText)
  892. rc = CListCtrl::SetItemText(nItem, nSubItem, lpszText);
  893. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  894. if (pXLCD)
  895. {
  896. pXLCD[nSubItem].crText = (crText == -1) ? m_crWindowText : crText;
  897. pXLCD[nSubItem].crBackground = (crBackground == -1) ? m_crWindow : crBackground;
  898. }
  899. UpdateSubItem(nItem, nSubItem);
  900. return rc;
  901. }
  902. ///////////////////////////////////////////////////////////////////////////////
  903. // DeleteItem
  904. BOOL CXListCtrl::DeleteItem(int nItem)
  905. {
  906. ASSERT(nItem >= 0);
  907. ASSERT(nItem < GetItemCount());
  908. if ((nItem < 0) || nItem >= GetItemCount())
  909. return FALSE;
  910. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  911. if (pXLCD)
  912. delete [] pXLCD;
  913. CListCtrl::SetItemData(nItem, 0);
  914. return CListCtrl::DeleteItem(nItem);
  915. }
  916. ///////////////////////////////////////////////////////////////////////////////
  917. // DeleteAllItems
  918. BOOL CXListCtrl::DeleteAllItems()
  919. {
  920. int n = GetItemCount();
  921. for (int i = 0; i < n; i++)
  922. {
  923. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(i);
  924. if (pXLCD)
  925. delete [] pXLCD;
  926. CListCtrl::SetItemData(i, 0);
  927. }
  928. return CListCtrl::DeleteAllItems();
  929. }
  930. ///////////////////////////////////////////////////////////////////////////////
  931. // OnDestroy
  932. void CXListCtrl::OnDestroy()
  933. {
  934. int n = GetItemCount();
  935. for (int i = 0; i < n; i++)
  936. {
  937. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(i);
  938. if (pXLCD)
  939. delete [] pXLCD;
  940. CListCtrl::SetItemData(i, 0);
  941. }
  942. m_bHeaderIsSubclassed = FALSE;
  943. CListCtrl::OnDestroy();
  944. }
  945. ///////////////////////////////////////////////////////////////////////////////
  946. // SetEdit
  947. BOOL CXListCtrl::SetEdit(int nItem, int nSubItem)
  948. {
  949. ASSERT(nItem >= 0);
  950. ASSERT(nItem < GetItemCount());
  951. if ((nItem < 0) || nItem >= GetItemCount())
  952. return FALSE;
  953. ASSERT(nSubItem >= 0);
  954. ASSERT(nSubItem < GetColumns());
  955. if ((nSubItem < 0) || nSubItem >= GetColumns())
  956. return FALSE;
  957. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  958. if (!pXLCD)
  959. {
  960. return FALSE;
  961. }
  962. pXLCD[nSubItem].bEdit = TRUE;
  963. UpdateSubItem(nItem, nSubItem);
  964. return TRUE;
  965. }
  966. ///////////////////////////////////////////////////////////////////////////////
  967. // SetProgress
  968. //
  969. // This function creates a progress bar in the specified subitem. The
  970. // UpdateProgress function may then be called to update the progress
  971. // percent. If bShowProgressText is TRUE, either the default text
  972. // of "n%" or the custom percent text (lpszProgressText) will be
  973. // displayed. If bShowProgressText is FALSE, only the progress bar
  974. // will be displayed, with no text.
  975. //
  976. // Note that the lpszProgressText string should include the format
  977. // specifier "%d": e.g., "Pct %d%%"
  978. //
  979. BOOL CXListCtrl::SetProgress(int nItem,
  980. int nSubItem,
  981. BOOL bShowProgressText /*= TRUE*/,
  982. LPCTSTR lpszProgressText /*= NULL*/)
  983. {
  984. ASSERT(nItem >= 0);
  985. ASSERT(nItem < GetItemCount());
  986. if ((nItem < 0) || nItem >= GetItemCount())
  987. return FALSE;
  988. ASSERT(nSubItem >= 0);
  989. ASSERT(nSubItem < GetColumns());
  990. if ((nSubItem < 0) || nSubItem >= GetColumns())
  991. return FALSE;
  992. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  993. if (!pXLCD)
  994. {
  995. return FALSE;
  996. }
  997. pXLCD[nSubItem].bShowProgress = TRUE;
  998. pXLCD[nSubItem].nProgressPercent = 0;
  999. pXLCD[nSubItem].bShowProgressMessage = bShowProgressText;
  1000. pXLCD[nSubItem].strProgressMessage = lpszProgressText;
  1001. UpdateSubItem(nItem, nSubItem);
  1002. return TRUE;
  1003. }
  1004. ///////////////////////////////////////////////////////////////////////////////
  1005. // DeleteProgress
  1006. void CXListCtrl::DeleteProgress(int nItem, int nSubItem)
  1007. {
  1008. ASSERT(nItem >= 0);
  1009. ASSERT(nItem < GetItemCount());
  1010. if ((nItem < 0) || nItem >= GetItemCount())
  1011. return;
  1012. ASSERT(nSubItem >= 0);
  1013. ASSERT(nSubItem < GetColumns());
  1014. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1015. return;
  1016. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1017. if (!pXLCD)
  1018. {
  1019. return;
  1020. }
  1021. pXLCD[nSubItem].bShowProgress = FALSE;
  1022. pXLCD[nSubItem].nProgressPercent = 0;
  1023. UpdateSubItem(nItem, nSubItem);
  1024. }
  1025. ///////////////////////////////////////////////////////////////////////////////
  1026. // UpdateProgress
  1027. void CXListCtrl::UpdateProgress(int nItem, int nSubItem, int nPercent)
  1028. {
  1029. ASSERT(nItem >= 0);
  1030. ASSERT(nItem < GetItemCount());
  1031. if ((nItem < 0) || nItem >= GetItemCount())
  1032. return;
  1033. ASSERT(nSubItem >= 0);
  1034. ASSERT(nSubItem < GetColumns());
  1035. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1036. return;
  1037. ASSERT(nPercent >= 0 && nPercent <= 100);
  1038. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1039. if (!pXLCD)
  1040. {
  1041. return;
  1042. }
  1043. pXLCD[nSubItem].nProgressPercent = nPercent;
  1044. UpdateSubItem(nItem, nSubItem);
  1045. }
  1046. ///////////////////////////////////////////////////////////////////////////////
  1047. // SetCheckbox
  1048. BOOL CXListCtrl::SetCheckbox(int nItem, int nSubItem, int nCheckedState)
  1049. {
  1050. ASSERT(nItem >= 0);
  1051. ASSERT(nItem < GetItemCount());
  1052. if ((nItem < 0) || nItem >= GetItemCount())
  1053. return FALSE;
  1054. ASSERT(nSubItem >= 0);
  1055. ASSERT(nSubItem < GetColumns());
  1056. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1057. return FALSE;
  1058. ASSERT(nCheckedState == 0 || nCheckedState == 1 || nCheckedState == -1);
  1059. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1060. if (!pXLCD)
  1061. {
  1062. return FALSE;
  1063. }
  1064. // update checkbox in subitem
  1065. pXLCD[nSubItem].nCheckedState = nCheckedState;
  1066. UpdateSubItem(nItem, nSubItem);
  1067. // now update checkbox in column header
  1068. // -1 = no checkbox in column header
  1069. if (GetHeaderCheckedState(nSubItem) != XHEADERCTRL_NO_IMAGE)
  1070. {
  1071. int nCheckedCount = CountCheckedItems(nSubItem);
  1072. if (nCheckedCount == GetItemCount())
  1073. SetHeaderCheckedState(nSubItem, XHEADERCTRL_CHECKED_IMAGE);
  1074. else
  1075. SetHeaderCheckedState(nSubItem, XHEADERCTRL_UNCHECKED_IMAGE);
  1076. }
  1077. return TRUE;
  1078. }
  1079. ///////////////////////////////////////////////////////////////////////////////
  1080. // GetCheckbox
  1081. int CXListCtrl::GetCheckbox(int nItem, int nSubItem)
  1082. {
  1083. ASSERT(nItem >= 0);
  1084. ASSERT(nItem < GetItemCount());
  1085. if ((nItem < 0) || nItem >= GetItemCount())
  1086. return -1;
  1087. ASSERT(nSubItem >= 0);
  1088. ASSERT(nSubItem < GetColumns());
  1089. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1090. return -1;
  1091. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1092. if (!pXLCD)
  1093. {
  1094. return -1;
  1095. }
  1096. return pXLCD[nSubItem].nCheckedState;
  1097. }
  1098. ///////////////////////////////////////////////////////////////////////////////
  1099. // GetEnabled
  1100. //
  1101. // Note that GetEnabled and SetEnabled only Get/Set the enabled flag from
  1102. // subitem 0, since this is a per-row flag.
  1103. //
  1104. BOOL CXListCtrl::GetEnabled(int nItem)
  1105. {
  1106. ASSERT(nItem >= 0);
  1107. ASSERT(nItem < GetItemCount());
  1108. if ((nItem < 0) || nItem >= GetItemCount())
  1109. return FALSE;
  1110. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1111. if (!pXLCD)
  1112. {
  1113. return FALSE;
  1114. }
  1115. return pXLCD[0].bEnabled;
  1116. }
  1117. ///////////////////////////////////////////////////////////////////////////////
  1118. // SetEnabled
  1119. BOOL CXListCtrl::SetEnabled(int nItem, BOOL bEnable)
  1120. {
  1121. ASSERT(nItem >= 0);
  1122. ASSERT(nItem < GetItemCount());
  1123. if ((nItem < 0) || nItem >= GetItemCount())
  1124. return FALSE;
  1125. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1126. if (!pXLCD)
  1127. {
  1128. return FALSE;
  1129. }
  1130. pXLCD[0].bEnabled = bEnable;
  1131. CRect rect;
  1132. GetItemRect(nItem, &rect, LVIR_BOUNDS);
  1133. InvalidateRect(&rect);
  1134. UpdateWindow();
  1135. return TRUE;
  1136. }
  1137. ///////////////////////////////////////////////////////////////////////////////
  1138. // SetBold
  1139. BOOL CXListCtrl::SetBold(int nItem, int nSubItem, BOOL bBold)
  1140. {
  1141. ASSERT(nItem >= 0);
  1142. ASSERT(nItem < GetItemCount());
  1143. if ((nItem < 0) || nItem >= GetItemCount())
  1144. return FALSE;
  1145. ASSERT(nSubItem >= 0);
  1146. ASSERT(nSubItem < GetColumns());
  1147. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1148. return FALSE;
  1149. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1150. if (!pXLCD)
  1151. {
  1152. return FALSE;
  1153. }
  1154. // update bold flag
  1155. pXLCD[nSubItem].bBold = bBold;
  1156. UpdateSubItem(nItem, nSubItem);
  1157. return TRUE;
  1158. }
  1159. ///////////////////////////////////////////////////////////////////////////////
  1160. // GetBold
  1161. BOOL CXListCtrl::GetBold(int nItem, int nSubItem)
  1162. {
  1163. ASSERT(nItem >= 0);
  1164. ASSERT(nItem < GetItemCount());
  1165. if ((nItem < 0) || nItem >= GetItemCount())
  1166. return FALSE;
  1167. ASSERT(nSubItem >= 0);
  1168. ASSERT(nSubItem < GetColumns());
  1169. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1170. return FALSE;
  1171. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1172. if (!pXLCD)
  1173. {
  1174. return FALSE;
  1175. }
  1176. // return bold flag
  1177. return pXLCD[nSubItem].bBold;
  1178. }
  1179. ///////////////////////////////////////////////////////////////////////////////
  1180. // GetModified
  1181. BOOL CXListCtrl::GetModified(int nItem, int nSubItem) //+++
  1182. {
  1183. ASSERT(nItem >= 0);
  1184. ASSERT(nItem < GetItemCount());
  1185. if ((nItem < 0) || nItem >= GetItemCount())
  1186. return FALSE;
  1187. ASSERT(nSubItem >= 0);
  1188. ASSERT(nSubItem < GetColumns());
  1189. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1190. return FALSE;
  1191. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1192. if (!pXLCD)
  1193. {
  1194. return FALSE;
  1195. }
  1196. // return modified flag
  1197. return pXLCD[nSubItem].bModified;
  1198. }
  1199. ///////////////////////////////////////////////////////////////////////////////
  1200. // SetModified
  1201. void CXListCtrl::SetModified(int nItem, int nSubItem, BOOL bModified) //+++
  1202. {
  1203. ASSERT(nItem >= 0);
  1204. ASSERT(nItem < GetItemCount());
  1205. if ((nItem < 0) || nItem >= GetItemCount())
  1206. return;
  1207. ASSERT(nSubItem >= 0);
  1208. ASSERT(nSubItem < GetColumns());
  1209. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1210. return;
  1211. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1212. if (pXLCD)
  1213. {
  1214. // set modified flag
  1215. pXLCD[nSubItem].bModified = bModified;
  1216. }
  1217. }
  1218. ///////////////////////////////////////////////////////////////////////////////
  1219. // GetItemCheckedState
  1220. int CXListCtrl::GetItemCheckedState(int nItem, int nSubItem) //+++
  1221. {
  1222. ASSERT(nItem >= 0);
  1223. ASSERT(nItem < GetItemCount());
  1224. if ((nItem < 0) || nItem >= GetItemCount())
  1225. return -1;
  1226. ASSERT(nSubItem >= 0);
  1227. ASSERT(nSubItem < GetColumns());
  1228. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1229. return -1;
  1230. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1231. if (!pXLCD)
  1232. {
  1233. return -1;
  1234. }
  1235. // return checked state
  1236. return pXLCD[nSubItem].nCheckedState;
  1237. }
  1238. ///////////////////////////////////////////////////////////////////////////////
  1239. // SetItemCheckedState
  1240. void CXListCtrl::SetItemCheckedState(int nItem, int nSubItem, int nCheckedState) //+++
  1241. {
  1242. ASSERT(nItem >= 0);
  1243. ASSERT(nItem < GetItemCount());
  1244. if ((nItem < 0) || nItem >= GetItemCount())
  1245. return;
  1246. ASSERT(nSubItem >= 0);
  1247. ASSERT(nSubItem < GetColumns());
  1248. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1249. return;
  1250. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1251. if (pXLCD)
  1252. {
  1253. // update checked state
  1254. pXLCD[nSubItem].nCheckedState = nCheckedState;
  1255. UpdateSubItem(nItem, nSubItem);
  1256. }
  1257. }
  1258. ///////////////////////////////////////////////////////////////////////////////
  1259. // GetItemColors
  1260. BOOL CXListCtrl::GetItemColors(int nItem, //+++
  1261. int nSubItem,
  1262. COLORREF& crText,
  1263. COLORREF& crBackground)
  1264. {
  1265. crText = RGB(0,0,0);
  1266. crBackground = RGB(0,0,0);
  1267. ASSERT(nItem >= 0);
  1268. ASSERT(nItem < GetItemCount());
  1269. if ((nItem < 0) || nItem >= GetItemCount())
  1270. return FALSE;
  1271. ASSERT(nSubItem >= 0);
  1272. ASSERT(nSubItem < GetColumns());
  1273. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1274. return FALSE;
  1275. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1276. if (!pXLCD)
  1277. {
  1278. return FALSE;
  1279. }
  1280. crText = pXLCD[nSubItem].crText;
  1281. crBackground = pXLCD[nSubItem].crBackground;
  1282. return TRUE;
  1283. }
  1284. ///////////////////////////////////////////////////////////////////////////////
  1285. // SetItemColors
  1286. void CXListCtrl::SetItemColors(int nItem, //+++
  1287. int nSubItem,
  1288. COLORREF crText,
  1289. COLORREF crBackground)
  1290. {
  1291. ASSERT(nItem >= 0);
  1292. ASSERT(nItem < GetItemCount());
  1293. if ((nItem < 0) || nItem >= GetItemCount())
  1294. return;
  1295. ASSERT(nSubItem >= 0);
  1296. ASSERT(nSubItem < GetColumns());
  1297. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1298. return;
  1299. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1300. if (pXLCD)
  1301. {
  1302. pXLCD[nSubItem].crText = crText;
  1303. pXLCD[nSubItem].crBackground = crBackground;
  1304. }
  1305. }
  1306. ///////////////////////////////////////////////////////////////////////////////
  1307. // SetComboBox
  1308. //
  1309. // Note: SetItemText may also be used to set the initial combo selection.
  1310. //
  1311. BOOL CXListCtrl::SetComboBox(int nItem,
  1312. int nSubItem,
  1313. BOOL bEnableCombo,
  1314. CStringArray *psa, // should not be allocated on stack
  1315. int nComboListHeight,
  1316. int nInitialComboSel,
  1317. BOOL bSort /*= FALSE*/)
  1318. {
  1319. ASSERT(nItem >= 0);
  1320. ASSERT(nItem < GetItemCount());
  1321. if ((nItem < 0) || nItem >= GetItemCount())
  1322. return FALSE;
  1323. ASSERT(nSubItem >= 0);
  1324. ASSERT(nSubItem < GetColumns());
  1325. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1326. return FALSE;
  1327. ASSERT(psa);
  1328. if (!psa)
  1329. return FALSE;
  1330. ASSERT(nComboListHeight > 0);
  1331. ASSERT(nInitialComboSel >= 0 && nInitialComboSel < psa->GetSize());
  1332. if ((nInitialComboSel < 0) || (nInitialComboSel >= psa->GetSize()))
  1333. nInitialComboSel = 0;
  1334. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1335. if (!pXLCD)
  1336. {
  1337. return FALSE;
  1338. }
  1339. // update flag
  1340. pXLCD[nSubItem].bCombo = bEnableCombo;
  1341. if (bEnableCombo)
  1342. {
  1343. // sort CStringArray before setting initial selection
  1344. if (bSort)
  1345. CSortCStringArray::SortCStringArray(psa);
  1346. pXLCD[nSubItem].psa = psa;
  1347. pXLCD[nSubItem].nComboListHeight = nComboListHeight;
  1348. pXLCD[nSubItem].nInitialComboSel = nInitialComboSel;
  1349. pXLCD[nSubItem].bSort = bSort;
  1350. CString str = _T("");
  1351. if ((pXLCD[nSubItem].nInitialComboSel >= 0) &&
  1352. (pXLCD[nSubItem].psa->GetSize() > pXLCD[nSubItem].nInitialComboSel))
  1353. {
  1354. int index = pXLCD[nSubItem].nInitialComboSel;
  1355. str = pXLCD[nSubItem].psa->GetAt(index);
  1356. }
  1357. SetItemText(nItem, nSubItem, str);
  1358. }
  1359. UpdateSubItem(nItem, nSubItem);
  1360. return TRUE;
  1361. }
  1362. ///////////////////////////////////////////////////////////////////////////////
  1363. // GetComboText
  1364. //
  1365. // Actually this does nothing more than GetItemText()
  1366. //
  1367. CString CXListCtrl::GetComboText(int nItem, int nSubItem)
  1368. {
  1369. ASSERT(nItem >= 0);
  1370. ASSERT(nItem < GetItemCount());
  1371. if ((nItem < 0) || nItem >= GetItemCount())
  1372. return _T("");
  1373. ASSERT(nSubItem >= 0);
  1374. ASSERT(nSubItem < GetColumns());
  1375. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1376. return _T("");
  1377. CString str;
  1378. str = _T("");
  1379. str = GetItemText(nItem, nSubItem);
  1380. return str;
  1381. }
  1382. ///////////////////////////////////////////////////////////////////////////////
  1383. // SetCurSel
  1384. BOOL CXListCtrl::SetCurSel(int nItem, BOOL bEnsureVisible /*= FALSE*/)
  1385. {
  1386. BOOL bRet = SetItemState(nItem, LVIS_FOCUSED | LVIS_SELECTED,
  1387. LVIS_FOCUSED | LVIS_SELECTED);
  1388. //+++
  1389. if (bEnsureVisible)
  1390. EnsureVisible(nItem, FALSE);
  1391. return bRet;
  1392. }
  1393. ///////////////////////////////////////////////////////////////////////////////
  1394. // GetCurSel - returns selected item number, or -1 if no item selected
  1395. //
  1396. // Note: for single-selection lists only
  1397. //
  1398. int CXListCtrl::GetCurSel()
  1399. {
  1400. POSITION pos = GetFirstSelectedItemPosition();
  1401. int nSelectedItem = -1;
  1402. if (pos != NULL)
  1403. nSelectedItem = GetNextSelectedItem(pos);
  1404. return nSelectedItem;
  1405. }
  1406. ///////////////////////////////////////////////////////////////////////////////
  1407. // UpdateSubItem
  1408. void CXListCtrl::UpdateSubItem(int nItem, int nSubItem)
  1409. {
  1410. ASSERT(nItem >= 0);
  1411. ASSERT(nItem < GetItemCount());
  1412. if ((nItem < 0) || nItem >= GetItemCount())
  1413. return;
  1414. ASSERT(nSubItem >= 0);
  1415. ASSERT(nSubItem < GetColumns());
  1416. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1417. return;
  1418. CRect rect;
  1419. if (nSubItem == -1)
  1420. {
  1421. GetItemRect(nItem, &rect, LVIR_BOUNDS);
  1422. }
  1423. else
  1424. {
  1425. GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);
  1426. }
  1427. rect.InflateRect(2, 2);
  1428. InvalidateRect(&rect);
  1429. UpdateWindow();
  1430. }
  1431. ///////////////////////////////////////////////////////////////////////////////
  1432. // GetColumns
  1433. int CXListCtrl::GetColumns()
  1434. {
  1435. return GetHeaderCtrl()->GetItemCount();
  1436. }
  1437. ///////////////////////////////////////////////////////////////////////////////
  1438. // GetItemData
  1439. //
  1440. // The GetItemData and SetItemData functions allow for app-specific data
  1441. // to be stored, by using an extra field in the XLISTCTRLDATA struct.
  1442. //
  1443. DWORD CXListCtrl::GetItemData(int nItem)
  1444. {
  1445. ASSERT(nItem >= 0);
  1446. ASSERT(nItem < GetItemCount());
  1447. if ((nItem < 0) || nItem >= GetItemCount())
  1448. return 0;
  1449. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1450. if (!pXLCD)
  1451. {
  1452. return 0;
  1453. }
  1454. return pXLCD->dwItemData;
  1455. }
  1456. ///////////////////////////////////////////////////////////////////////////////
  1457. // SetItemData
  1458. BOOL CXListCtrl::SetItemData(int nItem, DWORD dwData)
  1459. {
  1460. ASSERT(nItem >= 0);
  1461. ASSERT(nItem < GetItemCount());
  1462. if ((nItem < 0) || nItem >= GetItemCount())
  1463. return FALSE;
  1464. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1465. if (!pXLCD)
  1466. {
  1467. return FALSE;
  1468. }
  1469. pXLCD->dwItemData = dwData;
  1470. return TRUE;
  1471. }
  1472. ///////////////////////////////////////////////////////////////////////////////
  1473. // GetHeaderCheckedState
  1474. //
  1475. // The GetHeaderCheckedState and SetHeaderCheckedState may be used to toggle
  1476. // the checkbox in a column header.
  1477. // 0 = no checkbox
  1478. // 1 = unchecked
  1479. // 2 = checked
  1480. //
  1481. int CXListCtrl::GetHeaderCheckedState(int nSubItem)
  1482. {
  1483. ASSERT(nSubItem >= 0);
  1484. ASSERT(nSubItem < GetColumns());
  1485. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1486. return -1;
  1487. HDITEM hditem;
  1488. // use the image index (0 or 1) to indicate the checked status
  1489. hditem.mask = HDI_IMAGE;
  1490. m_HeaderCtrl.GetItem(nSubItem, &hditem);
  1491. return hditem.iImage;
  1492. }
  1493. ///////////////////////////////////////////////////////////////////////////////
  1494. // SetHeaderCheckedState
  1495. BOOL CXListCtrl::SetHeaderCheckedState(int nSubItem, int nCheckedState)
  1496. {
  1497. ASSERT(nSubItem >= 0);
  1498. ASSERT(nSubItem < GetColumns());
  1499. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1500. return FALSE;
  1501. ASSERT(nCheckedState == 0 || nCheckedState == 1 || nCheckedState == 2);
  1502. HDITEM hditem;
  1503. hditem.mask = HDI_IMAGE;
  1504. hditem.iImage = nCheckedState;
  1505. m_HeaderCtrl.SetItem(nSubItem, &hditem);
  1506. return TRUE;
  1507. }
  1508. ///////////////////////////////////////////////////////////////////////////////
  1509. // OnColumnClick
  1510. BOOL CXListCtrl::OnColumnClick(NMHDR* pNMHDR, LRESULT* pResult)
  1511. {
  1512. XLISTCTRL_TRACE(_T("in CXListCtrl::OnColumnClick\n"));
  1513. NMLISTVIEW* pnmlv = (NMLISTVIEW*)pNMHDR;
  1514. int nSubItem = pnmlv->iSubItem;
  1515. int nCheckedState = GetHeaderCheckedState(nSubItem);
  1516. // 0 = no checkbox
  1517. if (nCheckedState != XHEADERCTRL_NO_IMAGE)
  1518. {
  1519. nCheckedState = (nCheckedState == 1) ? 2 : 1;
  1520. SetHeaderCheckedState(nSubItem, nCheckedState);
  1521. m_HeaderCtrl.UpdateWindow();
  1522. for (int nItem = 0; nItem < GetItemCount(); nItem++)
  1523. {
  1524. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1525. if (!pXLCD)
  1526. {
  1527. continue;
  1528. }
  1529. if (pXLCD[nSubItem].nCheckedState != -1)
  1530. {
  1531. pXLCD[nSubItem].nCheckedState = nCheckedState - 1;
  1532. pXLCD[nSubItem].bModified = TRUE;
  1533. m_bListModified = TRUE;
  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. // GetColors
  1565. void CXListCtrl::GetColors()
  1566. {
  1567. m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
  1568. m_cr3DHighLight = ::GetSysColor(COLOR_3DHIGHLIGHT);
  1569. m_cr3DShadow = ::GetSysColor(COLOR_3DSHADOW);
  1570. m_crActiveCaption = ::GetSysColor(COLOR_ACTIVECAPTION);
  1571. m_crBtnFace = ::GetSysColor(COLOR_BTNFACE);
  1572. m_crBtnShadow = ::GetSysColor(COLOR_BTNSHADOW);
  1573. m_crBtnText = ::GetSysColor(COLOR_BTNTEXT);
  1574. m_crGrayText = ::GetSysColor(COLOR_GRAYTEXT);
  1575. m_crHighLight = ::GetSysColor(COLOR_HIGHLIGHT);
  1576. m_crHighLightText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
  1577. m_crInactiveCaption = ::GetSysColor(COLOR_INACTIVECAPTION);
  1578. m_crInactiveCaptionText = ::GetSysColor(COLOR_INACTIVECAPTIONTEXT);
  1579. m_crWindow = ::GetSysColor(COLOR_WINDOW);
  1580. m_crWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
  1581. }
  1582. ///////////////////////////////////////////////////////////////////////////////
  1583. // OnSysColorChange
  1584. void CXListCtrl::OnSysColorChange()
  1585. {
  1586. XLISTCTRL_TRACE(_T("in CXListCtrl::OnSysColorChange\n"));
  1587. CListCtrl::OnSysColorChange();
  1588. GetColors();
  1589. }
  1590. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  1591. ///////////////////////////////////////////////////////////////////////////////
  1592. // OnTimer
  1593. //
  1594. // Timer usage:
  1595. // 1 - Unlock window updates - used to avoid flashing after combo is created
  1596. // used to check if combo button needs to be unpressed,set in
  1597. // OnLButtonDown (when combo button is clicked)
  1598. // 2 - used to close combo listbox, set in OnComboEscape (user hits Escape
  1599. // or listbox loses focus)
  1600. // 3 - used to get combo listbox selection, then close combo listbox,
  1601. // set in OnComboReturn and OnComboLButtonUp (user hits Enter
  1602. // or clicks on item in listbox)
  1603. // 4 - used to get combo listbox selection, set in OnComboKeydown (for
  1604. // example, user hits arrow key in listbox)
  1605. //
  1606. void CXListCtrl::OnTimer(UINT nIDEvent)
  1607. {
  1608. if (nIDEvent == 1) // timer set when combo is created
  1609. {
  1610. KillTimer(nIDEvent);
  1611. UnlockWindowUpdate();
  1612. }
  1613. else if (nIDEvent == 2) // close combo listbox
  1614. {
  1615. KillTimer(nIDEvent);
  1616. XLISTCTRL_TRACE(_T("timer 2 ~~~~~\n"));
  1617. if (m_pCombo)
  1618. {
  1619. UpdateSubItem(m_nComboItem, m_nComboSubItem);
  1620. m_pCombo->DestroyWindow();
  1621. delete m_pCombo;
  1622. }
  1623. m_pCombo = NULL;
  1624. }
  1625. else if (nIDEvent == 3) // get combo listbox selection, then close combo listbox
  1626. {
  1627. KillTimer(nIDEvent);
  1628. XLISTCTRL_TRACE(_T("timer 3 ~~~~~\n"));
  1629. if (m_pCombo)
  1630. {
  1631. CString str;
  1632. int i = m_pCombo->GetCurSel();
  1633. if (i != LB_ERR)
  1634. {
  1635. m_pCombo->GetLBText(i, str);
  1636. if ((m_nComboItem >= 0 && m_nComboItem < GetItemCount()) &&
  1637. (m_nComboSubItem >= 0 && m_nComboSubItem < GetColumns()))
  1638. {
  1639. SetItemText(m_nComboItem, m_nComboSubItem, str);
  1640. UpdateSubItem(m_nComboItem, m_nComboSubItem);
  1641. if (str != m_strInitialString)
  1642. {
  1643. // string is not the same, mark item as modified
  1644. XLISTCTRLDATA *pXLCD =
  1645. (XLISTCTRLDATA *) CListCtrl::GetItemData(m_nComboItem);
  1646. if (pXLCD)
  1647. {
  1648. pXLCD[m_nComboSubItem].bModified = TRUE;
  1649. m_bListModified = TRUE;
  1650. }
  1651. }
  1652. CWnd *pWnd = GetParent();
  1653. if (!pWnd)
  1654. pWnd = GetOwner();
  1655. if (pWnd && ::IsWindow(pWnd->m_hWnd))
  1656. pWnd->SendMessage(WM_XLISTCTRL_COMBO_SELECTION,
  1657. m_nComboItem, m_nComboSubItem);
  1658. }
  1659. }
  1660. m_pCombo->DestroyWindow();
  1661. delete m_pCombo;
  1662. }
  1663. m_pCombo = NULL;
  1664. }
  1665. else if (nIDEvent == 4) // get combo listbox selection
  1666. {
  1667. KillTimer(nIDEvent);
  1668. XLISTCTRL_TRACE(_T("timer 4 ~~~~~\n"));
  1669. if (m_pCombo)
  1670. {
  1671. CString str;
  1672. int i = m_pCombo->GetCurSel();
  1673. if (i != LB_ERR)
  1674. {
  1675. m_pCombo->GetLBText(i, str);
  1676. if ((m_nComboItem >= 0 && m_nComboItem < GetItemCount()) &&
  1677. (m_nComboSubItem >= 0 && m_nComboSubItem < GetColumns()))
  1678. {
  1679. SetItemText(m_nComboItem, m_nComboSubItem, str);
  1680. UpdateSubItem(m_nComboItem, m_nComboSubItem);
  1681. }
  1682. }
  1683. }
  1684. }
  1685. CListCtrl::OnTimer(nIDEvent);
  1686. }
  1687. ///////////////////////////////////////////////////////////////////////////////
  1688. // OnComboEscape
  1689. LRESULT CXListCtrl::OnComboEscape(WPARAM, LPARAM)
  1690. {
  1691. XLISTCTRL_TRACE(_T("in CXListCtrl::OnComboEscape\n"));
  1692. SetTimer(2, 50, NULL);
  1693. //UpdateSubItem(m_nComboItem, m_nComboSubItem);
  1694. return 0;
  1695. }
  1696. ///////////////////////////////////////////////////////////////////////////////
  1697. // OnComboReturn
  1698. LRESULT CXListCtrl::OnComboComplete(WPARAM, LPARAM)
  1699. {
  1700. XLISTCTRL_TRACE(_T("in CXListCtrl::OnComboComplete\n"));
  1701. SetTimer(3, 50, NULL);
  1702. return 0;
  1703. }
  1704. #endif // #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  1705. #ifndef NO_XLISTCTRL_TOOL_TIPS
  1706. ///////////////////////////////////////////////////////////////////////////////
  1707. // OnToolHitTest
  1708. int CXListCtrl::OnToolHitTest(CPoint point, TOOLINFO * pTI) const
  1709. {
  1710. LVHITTESTINFO lvhitTestInfo;
  1711. lvhitTestInfo.pt = point;
  1712. int nItem = ListView_SubItemHitTest(this->m_hWnd, &lvhitTestInfo);
  1713. int nSubItem = lvhitTestInfo.iSubItem;
  1714. //XLISTCTRL_TRACE(_T("in CToolTipListCtrl::OnToolHitTest: %d,%d\n"), nItem, nSubItem);
  1715. UINT nFlags = lvhitTestInfo.flags;
  1716. // nFlags is 0 if the SubItemHitTest fails
  1717. // Therefore, 0 & <anything> will equal false
  1718. if (nFlags & LVHT_ONITEMLABEL)
  1719. {
  1720. // If it did fall on a list item,
  1721. // and it was also hit one of the
  1722. // item specific subitems we wish to show tool tips for
  1723. // get the client (area occupied by this control
  1724. RECT rcClient;
  1725. GetClientRect(&rcClient);
  1726. // fill in the TOOLINFO structure
  1727. pTI->hwnd = m_hWnd;
  1728. pTI->uId = (UINT) (nItem * 1000 + nSubItem + 1);
  1729. pTI->lpszText = LPSTR_TEXTCALLBACK;
  1730. pTI->rect = rcClient;
  1731. return pTI->uId; // By returning a unique value per listItem,
  1732. // we ensure that when the mouse moves over another
  1733. // list item, the tooltip will change
  1734. }
  1735. else
  1736. {
  1737. //Otherwise, we aren't interested, so let the message propagate
  1738. return -1;
  1739. }
  1740. }
  1741. ///////////////////////////////////////////////////////////////////////////////
  1742. // OnToolTipText
  1743. BOOL CXListCtrl::OnToolTipText(UINT /*id*/, NMHDR * pNMHDR, LRESULT * pResult)
  1744. {
  1745. UINT nID = pNMHDR->idFrom;
  1746. //XLISTCTRL_TRACE(_T("in CXListCtrl::OnToolTipText: id=%d\n"), nID);
  1747. // check if this is the automatic tooltip of the control
  1748. if (nID == 0)
  1749. return TRUE; // do not allow display of automatic tooltip,
  1750. // or our tooltip will disappear
  1751. // handle both ANSI and UNICODE versions of the message
  1752. TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
  1753. TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
  1754. *pResult = 0;
  1755. // get the mouse position
  1756. const MSG* pMessage;
  1757. pMessage = GetCurrentMessage();
  1758. ASSERT(pMessage);
  1759. CPoint pt;
  1760. pt = pMessage->pt; // get the point from the message
  1761. ScreenToClient(&pt); // convert the point's coords to be relative to this control
  1762. // see if the point falls onto a list item
  1763. LVHITTESTINFO lvhitTestInfo;
  1764. lvhitTestInfo.pt = pt;
  1765. int nItem = SubItemHitTest(&lvhitTestInfo);
  1766. int nSubItem = lvhitTestInfo.iSubItem;
  1767. UINT nFlags = lvhitTestInfo.flags;
  1768. // nFlags is 0 if the SubItemHitTest fails
  1769. // Therefore, 0 & <anything> will equal false
  1770. if (nFlags & LVHT_ONITEMLABEL)
  1771. {
  1772. // If it did fall on a list item,
  1773. // and it was also hit one of the
  1774. // item specific subitems we wish to show tooltips for
  1775. CString strToolTip;
  1776. strToolTip = _T("");
  1777. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1778. if (pXLCD)
  1779. {
  1780. strToolTip = pXLCD[nSubItem].strToolTip;
  1781. }
  1782. if (!strToolTip.IsEmpty())
  1783. {
  1784. // If there was a CString associated with the list item,
  1785. // copy it's text (up to 80 characters worth, limitation
  1786. // of the TOOLTIPTEXT structure) into the TOOLTIPTEXT
  1787. // structure's szText member
  1788. #ifndef _UNICODE
  1789. if (pNMHDR->code == TTN_NEEDTEXTA)
  1790. lstrcpyn(pTTTA->szText, strToolTip, 80);
  1791. else
  1792. _mbstowcsz(pTTTW->szText, strToolTip, 80);
  1793. #else
  1794. if (pNMHDR->code == TTN_NEEDTEXTA)
  1795. _wcstombsz(pTTTA->szText, strToolTip, 80);
  1796. else
  1797. lstrcpyn(pTTTW->szText, strToolTip, 80);
  1798. #endif
  1799. return FALSE; // we found a tool tip,
  1800. }
  1801. }
  1802. return FALSE; // we didn't handle the message, let the
  1803. // framework continue propagating the message
  1804. }
  1805. ///////////////////////////////////////////////////////////////////////////////
  1806. // SetItemToolTipText
  1807. BOOL CXListCtrl::SetItemToolTipText(int nItem, int nSubItem, LPCTSTR lpszToolTipText)
  1808. {
  1809. ASSERT(nItem >= 0);
  1810. ASSERT(nItem < GetItemCount());
  1811. if ((nItem < 0) || nItem >= GetItemCount())
  1812. return FALSE;
  1813. ASSERT(nSubItem >= 0);
  1814. ASSERT(nSubItem < GetColumns());
  1815. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1816. return FALSE;
  1817. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1818. if (!pXLCD)
  1819. {
  1820. return FALSE;
  1821. }
  1822. pXLCD[nSubItem].strToolTip = lpszToolTipText;
  1823. return TRUE;
  1824. }
  1825. ///////////////////////////////////////////////////////////////////////////////
  1826. // GetItemToolTipText
  1827. CString CXListCtrl::GetItemToolTipText(int nItem, int nSubItem)
  1828. {
  1829. CString strToolTip;
  1830. strToolTip = _T("");
  1831. ASSERT(nItem >= 0);
  1832. ASSERT(nItem < GetItemCount());
  1833. if ((nItem < 0) || nItem >= GetItemCount())
  1834. return strToolTip;
  1835. ASSERT(nSubItem >= 0);
  1836. ASSERT(nSubItem < GetColumns());
  1837. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1838. return strToolTip;
  1839. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1840. if (pXLCD)
  1841. {
  1842. strToolTip = pXLCD[nSubItem].strToolTip;
  1843. }
  1844. return strToolTip;
  1845. }
  1846. ///////////////////////////////////////////////////////////////////////////////
  1847. // DeleteAllToolTips
  1848. void CXListCtrl::DeleteAllToolTips()
  1849. {
  1850. int nRow = GetItemCount();
  1851. int nCol = GetColumns();
  1852. for (int nItem = 0; nItem < nRow; nItem++)
  1853. {
  1854. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1855. if (pXLCD)
  1856. for (int nSubItem = 0; nSubItem < nCol; nSubItem++)
  1857. pXLCD[nSubItem].strToolTip = _T("");
  1858. }
  1859. }
  1860. #endif
  1861. #ifndef DO_NOT_INCLUDE_XCOMBOLIST
  1862. ///////////////////////////////////////////////////////////////////////////////
  1863. // DrawComboBox
  1864. void CXListCtrl::DrawComboBox(int nItem, int nSubItem)
  1865. {
  1866. ASSERT(nItem >= 0);
  1867. ASSERT(nItem < GetItemCount());
  1868. if ((nItem < 0) || nItem >= GetItemCount())
  1869. return;
  1870. ASSERT(nSubItem >= 0);
  1871. ASSERT(nSubItem < GetColumns());
  1872. if ((nSubItem < 0) || nSubItem >= GetColumns())
  1873. return;
  1874. // Make sure that nSubItem is valid
  1875. ASSERT(GetColumnWidth(nSubItem) >= 5);
  1876. if (GetColumnWidth(nSubItem) < 5)
  1877. return;
  1878. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  1879. if (!pXLCD)
  1880. {
  1881. ASSERT(FALSE);
  1882. return;
  1883. }
  1884. if (!pXLCD[0].bEnabled)
  1885. return;
  1886. #ifdef _DEBUG
  1887. DWORD dwExStyle = GetExtendedStyle();
  1888. if ((dwExStyle & LVS_EX_FULLROWSELECT) == 0)
  1889. {
  1890. XLISTCTRL_TRACE(_T("XListCtrl: combo boxes require LVS_EX_FULLROWSELECT style\n"));
  1891. ASSERT(FALSE);
  1892. }
  1893. #endif
  1894. // Get the column offset
  1895. int offset = 0;
  1896. for (int i = 0; i < nSubItem; i++)
  1897. offset += GetColumnWidth(i);
  1898. CRect rect;
  1899. GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);
  1900. CRect rectClient;
  1901. GetClientRect(&rectClient);
  1902. m_pCombo = new CXCombo(this);
  1903. ASSERT(m_pCombo);
  1904. if (m_pCombo)
  1905. {
  1906. m_nComboItem = nItem;
  1907. m_nComboSubItem = nSubItem;
  1908. rect.top -= 1;
  1909. m_rectComboList = rect;
  1910. ClientToScreen(&m_rectComboList);
  1911. m_rectComboList.left += 1;
  1912. DWORD dwStyle = CBS_DROPDOWNLIST | WS_POPUP | WS_VISIBLE ;
  1913. BOOL bSuccess = m_pCombo->CreateEx(WS_EX_CONTROLPARENT,
  1914. ADVCOMBOBOXCTRL_CLASSNAME,
  1915. _T(""),
  1916. dwStyle,
  1917. m_rectComboList,
  1918. this,
  1919. 0,
  1920. NULL);
  1921. if (bSuccess)
  1922. {
  1923. LockWindowUpdate();
  1924. if (pXLCD[nSubItem].psa)
  1925. {
  1926. // add strings to combo
  1927. CString s = _T("");
  1928. try
  1929. {
  1930. for (int i = 0; i < pXLCD[nSubItem].psa->GetSize(); i++)
  1931. {
  1932. s = pXLCD[nSubItem].psa->GetAt(i);
  1933. if (!s.IsEmpty())
  1934. m_pCombo->AddString(s);
  1935. }
  1936. }
  1937. catch(...)
  1938. {
  1939. TRACE(_T("ERROR - exception in CXListCtrl::DrawComboBox\n"));
  1940. TRACE(_T("==> Attempting to access psa pointer; string array must be a class\n"));
  1941. TRACE(_T("==> variable, a global variable, or allocated on the heap.\n"));
  1942. }
  1943. }
  1944. m_pCombo->SetDefaultVisibleItems(pXLCD[nSubItem].nComboListHeight);
  1945. int index = 0;
  1946. CString str = _T("");
  1947. // Note that strings in combo are sorted in CXListCtrl::SetComboBox()
  1948. if (pXLCD[nSubItem].psa)
  1949. {
  1950. try
  1951. {
  1952. if ((pXLCD[nSubItem].nInitialComboSel >= 0) &&
  1953. (m_pCombo->GetCount() > pXLCD[nSubItem].nInitialComboSel))
  1954. {
  1955. index = pXLCD[nSubItem].nInitialComboSel;
  1956. m_pCombo->GetLBText(index, str);
  1957. XLISTCTRL_TRACE(_T("nInitialComboSel=%d str=<%s>\n"), index, str);
  1958. SetItemText(nItem, nSubItem, str);
  1959. pXLCD[nSubItem].nInitialComboSel = -1; // default after first time
  1960. }
  1961. }
  1962. catch(...)
  1963. {
  1964. TRACE(_T("ERROR - exception in CXListCtrl::DrawComboBox\n"));
  1965. TRACE(_T("==> Attempting to access psa pointer; string array must be a class\n"));
  1966. TRACE(_T("==> variable, a global variable, or allocated on the heap.\n"));
  1967. }
  1968. }
  1969. if (str.IsEmpty())
  1970. str = GetItemText(nItem, nSubItem);
  1971. if (str.IsEmpty())
  1972. {
  1973. // str is empty, try to get from first listbox string
  1974. if (m_pCombo->GetCount() > 0)
  1975. {
  1976. m_pCombo->GetLBText(0, str);
  1977. index = 0;
  1978. }
  1979. SetItemText(nItem, nSubItem, str);
  1980. }
  1981. else
  1982. {
  1983. // set listbox selection from subitem text
  1984. index = m_pCombo->FindStringExact(-1, str);
  1985. XLISTCTRL_TRACE(_T("FindStringExact returned %d\n"), index);
  1986. if (index == LB_ERR)
  1987. index = 0;
  1988. }
  1989. m_pCombo->SetCurSel(index);
  1990. m_pCombo->GetLBText(index, m_strInitialString);
  1991. SetTimer(1, 50, NULL);
  1992. }
  1993. m_pCombo->Invalidate();
  1994. m_pCombo->RedrawWindow();
  1995. m_pCombo->BringWindowToTop();
  1996. }
  1997. }
  1998. #endif
  1999. ///////////////////////////////////////////////////////////////////////////////
  2000. // DrawEdit - Start edit of a sub item label
  2001. // nItem - The row index of the item to edit
  2002. // nSubItem - The column of the sub item.
  2003. void CXListCtrl::DrawEdit(int nItem, int nSubItem)
  2004. {
  2005. XLISTCTRL_TRACE(_T("in CXListCtrl::DrawEdit\n"));
  2006. ASSERT(nItem >= 0);
  2007. ASSERT(nItem < GetItemCount());
  2008. if ((nItem < 0) || nItem >= GetItemCount())
  2009. return;
  2010. ASSERT(nSubItem >= 0);
  2011. ASSERT(nSubItem < GetColumns());
  2012. if ((nSubItem < 0) || nSubItem >= GetColumns())
  2013. return;
  2014. // Make sure that nSubItem is valid
  2015. ASSERT(GetColumnWidth(nSubItem) >= 5);
  2016. if (GetColumnWidth(nSubItem) < 5)
  2017. return;
  2018. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
  2019. if (!pXLCD)
  2020. {
  2021. ASSERT(FALSE);
  2022. return;
  2023. }
  2024. if (!pXLCD[0].bEnabled)
  2025. return;
  2026. #ifdef _DEBUG
  2027. DWORD dwExStyle = GetExtendedStyle();
  2028. if ((dwExStyle & LVS_EX_FULLROWSELECT) == 0)
  2029. {
  2030. XLISTCTRL_TRACE(_T("XListCtrl: edit boxes require LVS_EX_FULLROWSELECT style\n"));
  2031. ASSERT(FALSE);
  2032. }
  2033. #endif
  2034. // make sure that the item is visible
  2035. if (!EnsureVisible(nItem, TRUE))
  2036. return;
  2037. // get the column offset
  2038. int offset = 0;
  2039. for (int i = 0; i < nSubItem; i++)
  2040. offset += GetColumnWidth(i);
  2041. CRect rect;
  2042. GetItemRect(nItem, &rect, LVIR_BOUNDS);
  2043. // now scroll if we need to expose the column
  2044. CRect rectClient;
  2045. GetClientRect(&rectClient);
  2046. if (offset + rect.left < 0 || offset + rect.left > rectClient.right)
  2047. {
  2048. CSize size;
  2049. size.cx = offset + rect.left;
  2050. size.cy = 0;
  2051. Scroll(size);
  2052. rect.left -= size.cx;
  2053. }
  2054. // Get Column alignment
  2055. LV_COLUMN lvcol;
  2056. lvcol.mask = LVCF_FMT;
  2057. GetColumn(nSubItem, &lvcol);
  2058. DWORD dwStyle = 0;
  2059. if ((lvcol.fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
  2060. dwStyle = ES_LEFT;
  2061. else if ((lvcol.fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
  2062. dwStyle = ES_RIGHT;
  2063. else dwStyle = ES_CENTER;
  2064. dwStyle |= WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_LEFT /*| WS_BORDER*/ ;
  2065. //rect.top -= 2;
  2066. rect.bottom += 1;
  2067. rect.left += offset + 2;
  2068. rect.right = rect.left + GetColumnWidth(nSubItem) - 4;
  2069. if (rect.right > rectClient.right)
  2070. rect.right = rectClient.right;
  2071. m_strInitialString = GetItemText(nItem, nSubItem);
  2072. ASSERT(m_pEdit == NULL);
  2073. m_pEdit = new CXEdit(this, m_strInitialString);
  2074. if (m_pEdit)
  2075. {
  2076. BOOL bSuccess = m_pEdit->Create(dwStyle, rect, this, 99);
  2077. m_nEditItem = nItem;
  2078. m_nEditSubItem = nSubItem;
  2079. if (bSuccess)
  2080. {
  2081. m_pEdit->SetFocus();
  2082. }
  2083. }
  2084. }
  2085. ///////////////////////////////////////////////////////////////////////////////
  2086. // OnXEditEscape
  2087. LRESULT CXListCtrl::OnXEditEscape(WPARAM, LPARAM)
  2088. {
  2089. XLISTCTRL_TRACE(_T("in CXListCtrl::OnXEditEscape\n"));
  2090. if (m_pEdit && ::IsWindow(m_pEdit->m_hWnd))
  2091. {
  2092. m_pEdit->DestroyWindow();
  2093. delete m_pEdit;
  2094. }
  2095. m_pEdit = NULL;
  2096. // restore original string
  2097. SetItemText(m_nEditItem, m_nEditSubItem, m_strInitialString);
  2098. UpdateSubItem(m_nEditItem, m_nEditSubItem);
  2099. return 0;
  2100. }
  2101. ///////////////////////////////////////////////////////////////////////////////
  2102. // OnXEditKillFocus
  2103. LRESULT CXListCtrl::OnXEditKillFocus(WPARAM, LPARAM)
  2104. {
  2105. XLISTCTRL_TRACE(_T("in CXListCtrl::OnXEditKillFocus\n"));
  2106. CString str = _T("");
  2107. if (m_pEdit && ::IsWindow(m_pEdit->m_hWnd))
  2108. {
  2109. m_pEdit->GetWindowText(str);
  2110. if (str.IsEmpty())
  2111. {
  2112. // restore original string
  2113. str = m_strInitialString;
  2114. }
  2115. m_pEdit->DestroyWindow();
  2116. delete m_pEdit;
  2117. }
  2118. m_pEdit = NULL;
  2119. // set new string in subitem
  2120. SetItemText(m_nEditItem, m_nEditSubItem, str);
  2121. if (str != m_strInitialString)
  2122. {
  2123. XLISTCTRL_TRACE(_T("m_strInitialString=<%s>\n"), m_strInitialString);
  2124. // string is not the same, mark item as modified
  2125. XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(m_nEditItem);
  2126. if (pXLCD)
  2127. {
  2128. pXLCD[m_nEditSubItem].bModified = TRUE;
  2129. m_bListModified = TRUE;
  2130. }
  2131. }
  2132. UpdateSubItem(m_nEditItem, m_nEditSubItem);
  2133. CWnd *pWnd = GetParent();
  2134. if (!pWnd)
  2135. pWnd = GetOwner();
  2136. if (pWnd && ::IsWindow(pWnd->m_hWnd))
  2137. pWnd->SendMessage(WM_XLISTCTRL_EDIT_END,
  2138. m_nEditItem, m_nEditSubItem);
  2139. return 0;
  2140. }
  2141. ///////////////////////////////////////////////////////////////////////////////
  2142. // OnEraseBkgnd
  2143. BOOL CXListCtrl::OnEraseBkgnd(CDC* pDC)
  2144. {
  2145. CRect rectClip, rectTop, rectBottom, rectRight;
  2146. int nItemCount = GetItemCount();
  2147. if (!nItemCount) // Empty XListCtrl, nothing to do, CListCtrl will
  2148. return CListCtrl::OnEraseBkgnd(pDC); // erase the Background
  2149. if (pDC->GetClipBox(&rectClip) == ERROR)
  2150. {
  2151. ASSERT(false);
  2152. return CListCtrl::OnEraseBkgnd(pDC);
  2153. }
  2154. int nFirstRow = GetTopIndex();
  2155. int nLastRow = nFirstRow + GetCountPerPage();
  2156. nLastRow = min (nLastRow, nItemCount - 1); // Last Item displayed in Ctrl
  2157. CListCtrl::GetSubItemRect(nFirstRow, 0, LVIR_BOUNDS, rectTop);
  2158. CListCtrl::GetSubItemRect(nLastRow, 0, LVIR_BOUNDS, rectBottom);
  2159. CRect rectEraseTop = rectClip;
  2160. rectEraseTop.bottom = rectTop.top;
  2161. pDC->FillSolidRect(rectEraseTop, m_crWindow);
  2162. CRect rectEraseBottom = rectClip;
  2163. rectEraseBottom.top = rectBottom.bottom;
  2164. pDC->FillSolidRect(rectEraseBottom, m_crWindow);
  2165. CRect rectEraseRight = rectClip;
  2166. rectEraseRight.top = rectTop.top;
  2167. rectEraseRight.bottom = rectBottom.bottom;
  2168. rectEraseRight.left = rectTop.right;
  2169. pDC->FillSolidRect(rectEraseRight, m_crWindow);
  2170. return TRUE;
  2171. //return CListCtrl::OnEraseBkgnd(pDC);
  2172. }
  2173. ///////////////////////////////////////////////////////////////////////////////
  2174. // FindDataItem
  2175. //+++
  2176. int CXListCtrl::FindDataItem(DWORD dwData)
  2177. {
  2178. for (int nItem = 0; nItem < GetItemCount(); nItem++)
  2179. {
  2180. if (GetItemData(nItem) == dwData)
  2181. return nItem;
  2182. }
  2183. return -1;
  2184. }
  2185. ///////////////////////////////////////////////////////////////////////////////
  2186. // OnKeyDown - check for disabled items
  2187. void CXListCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  2188. {
  2189. XLISTCTRL_TRACE(_T("in CXListCtrl::OnKeyDown\n"));
  2190. int nOldItem = GetCurSel();
  2191. CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
  2192. int nNewItem = GetCurSel();
  2193. if (nNewItem >= 0)
  2194. {
  2195. int nTrial = nNewItem;
  2196. BOOL bEnabled = GetEnabled(nTrial);
  2197. if (!bEnabled)
  2198. {
  2199. // item is disabled, try another
  2200. int nCount = GetItemCount();
  2201. if (nChar == VK_DOWN || nChar == VK_NEXT)
  2202. {
  2203. int nDirection = 1;
  2204. while (!bEnabled)
  2205. {
  2206. nTrial += 1 * nDirection;
  2207. if (nTrial >= nCount)
  2208. {
  2209. // at the end, back up
  2210. nTrial = nCount;
  2211. nDirection = -1;
  2212. continue;
  2213. }
  2214. else if (nTrial < 0)
  2215. {
  2216. // at beginning - must have been backing up
  2217. nTrial = nOldItem;
  2218. break;
  2219. }
  2220. bEnabled = GetEnabled(nTrial);
  2221. }
  2222. }
  2223. else if (nChar == VK_UP || nChar == VK_PRIOR)
  2224. {
  2225. int nDirection = -1;
  2226. while (!bEnabled)
  2227. {
  2228. nTrial += 1 * nDirection;
  2229. if (nTrial < 0)
  2230. {
  2231. // at the beginning, go forward
  2232. nTrial = 0;
  2233. nDirection = 1;
  2234. continue;
  2235. }
  2236. else if (nTrial >= nCount)
  2237. {
  2238. // at end - must have been going forward
  2239. nTrial = nOldItem;
  2240. break;
  2241. }
  2242. bEnabled = GetEnabled(nTrial);
  2243. }
  2244. }
  2245. else
  2246. {
  2247. // don't know how user got here, just go back to previous
  2248. nTrial = nOldItem;
  2249. }
  2250. }
  2251. SetCurSel(nTrial, TRUE); // set new selection, scroll into view
  2252. }
  2253. }