XHeaderCtrl.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. // XHeaderCtrl.cpp Version 1.4
  2. //
  3. // Author: Hans Dietrich
  4. // hdietrich@gmail.com
  5. //
  6. // This code is based on "Outlook 98-Style FlatHeader Control"
  7. // by Maarten Hoeben.
  8. //
  9. // See http://www.codeguru.com/listview/FlatHeader.shtml
  10. //
  11. // License:
  12. // This software is released into the public domain. You are free to use
  13. // it in any way you like, except that you may not sell this source code.
  14. //
  15. // This software is provided "as is" with no expressed or implied warranty.
  16. // I accept no liability for any damage or loss of business that this
  17. // software may cause.
  18. //
  19. ///////////////////////////////////////////////////////////////////////////////
  20. #include "stdafx.h"
  21. #include "XHeaderCtrl.h"
  22. #include "memdc.h"
  23. #ifdef _DEBUG
  24. #define new DEBUG_NEW
  25. #undef THIS_FILE
  26. static char THIS_FILE[] = __FILE__;
  27. #endif
  28. /////////////////////////////////////////////////////////////////////////////
  29. // CXHeaderCtrl
  30. IMPLEMENT_DYNCREATE(CXHeaderCtrl, CHeaderCtrl)
  31. BEGIN_MESSAGE_MAP(CXHeaderCtrl, CHeaderCtrl)
  32. //{{AFX_MSG_MAP(CXHeaderCtrl)
  33. ON_WM_ERASEBKGND()
  34. ON_WM_PAINT()
  35. ON_WM_SYSCOLORCHANGE()
  36. ON_WM_LBUTTONDBLCLK()
  37. //}}AFX_MSG_MAP
  38. ON_MESSAGE(HDM_INSERTITEMA, OnInsertItem)
  39. ON_MESSAGE(HDM_INSERTITEMW, OnInsertItem)
  40. ON_MESSAGE(HDM_DELETEITEM, OnDeleteItem)
  41. ON_MESSAGE(HDM_SETIMAGELIST, OnSetImageList)
  42. ON_MESSAGE(HDM_LAYOUT, OnLayout)
  43. END_MESSAGE_MAP()
  44. ///////////////////////////////////////////////////////////////////////////////
  45. // ctor
  46. CXHeaderCtrl::CXHeaderCtrl()
  47. {
  48. m_cr3DHighLight = ::GetSysColor(COLOR_3DHIGHLIGHT);
  49. m_cr3DShadow = ::GetSysColor(COLOR_3DSHADOW);
  50. m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
  51. m_crBtnText = ::GetSysColor(COLOR_BTNTEXT);
  52. m_pListCtrl = NULL; //+++
  53. m_nFormat = DT_DEFAULT; //+++
  54. m_rgbText = m_crBtnText; //+++
  55. m_bDividerLines = TRUE; //+++
  56. m_bDoubleBuffer = TRUE;
  57. m_iSpacing = 6;
  58. m_sizeArrow.cx = 8;
  59. m_sizeArrow.cy = 8;
  60. m_sizeImage.cx = 0;
  61. m_sizeImage.cy = 0;
  62. m_bStaticBorder = FALSE;
  63. m_nDontDropCursor = 0;
  64. m_bResizing = FALSE;
  65. m_nClickFlags = 0;
  66. }
  67. ///////////////////////////////////////////////////////////////////////////////
  68. // dtor
  69. CXHeaderCtrl::~CXHeaderCtrl()
  70. {
  71. }
  72. ///////////////////////////////////////////////////////////////////////////////
  73. // ModifyProperty
  74. BOOL CXHeaderCtrl::ModifyProperty(WPARAM wParam, LPARAM lParam)
  75. {
  76. switch(wParam)
  77. {
  78. case FH_PROPERTY_SPACING:
  79. m_iSpacing = (int)lParam;
  80. break;
  81. case FH_PROPERTY_ARROW:
  82. m_sizeArrow.cx = LOWORD(lParam);
  83. m_sizeArrow.cy = HIWORD(lParam);
  84. break;
  85. case FH_PROPERTY_STATICBORDER:
  86. m_bStaticBorder = (BOOL)lParam;
  87. break;
  88. case FH_PROPERTY_DONTDROPCURSOR:
  89. m_nDontDropCursor = (UINT)lParam;
  90. break;
  91. default:
  92. return FALSE;
  93. }
  94. Invalidate();
  95. return TRUE;
  96. }
  97. ///////////////////////////////////////////////////////////////////////////////
  98. // DrawCtrl
  99. void CXHeaderCtrl::DrawCtrl(CDC* pDC)
  100. {
  101. CRect rectClip;
  102. if (pDC->GetClipBox(&rectClip) == ERROR)
  103. return;
  104. CRect rectClient, rectItem;
  105. GetClientRect(&rectClient);
  106. pDC->FillSolidRect(rectClip, m_cr3DFace);
  107. int iItems = GetItemCount();
  108. ASSERT(iItems >= 0);
  109. CPen penHighLight(PS_SOLID, 1, m_cr3DHighLight);
  110. CPen penShadow(PS_SOLID, 1, m_cr3DShadow);
  111. CPen* pPen = pDC->GetCurrentPen();
  112. CFont* pFont = pDC->SelectObject(GetFont());
  113. pDC->SetBkColor(m_cr3DFace);
  114. pDC->SetTextColor(m_crBtnText);
  115. int iWidth = 0;
  116. for (int i = 0; i < iItems; i++)
  117. {
  118. int iItem = OrderToIndex(i);
  119. TCHAR szText[FLATHEADER_TEXT_MAX];
  120. HDITEM hditem;
  121. hditem.mask = HDI_WIDTH|HDI_FORMAT|HDI_TEXT|HDI_IMAGE|HDI_BITMAP;
  122. hditem.pszText = szText;
  123. hditem.cchTextMax = sizeof(szText);
  124. VERIFY(GetItem(iItem, &hditem));
  125. VERIFY(GetItemRect(iItem, rectItem));
  126. if (rectItem.right >= rectClip.left || rectItem.left <= rectClip.right)
  127. {
  128. if (hditem.fmt & HDF_OWNERDRAW)
  129. {
  130. DRAWITEMSTRUCT disItem;
  131. disItem.CtlType = ODT_BUTTON;
  132. disItem.CtlID = GetDlgCtrlID();
  133. disItem.itemID = iItem;
  134. disItem.itemAction = ODA_DRAWENTIRE;
  135. disItem.itemState = 0;
  136. disItem.hwndItem = m_hWnd;
  137. disItem.hDC = pDC->m_hDC;
  138. disItem.rcItem = rectItem;
  139. disItem.itemData = 0;
  140. DrawItem(&disItem);
  141. }
  142. else
  143. {
  144. rectItem.DeflateRect(m_iSpacing, 0);
  145. DrawItem(pDC, rectItem, &hditem);
  146. rectItem.InflateRect(m_iSpacing, 0);
  147. //if (m_nClickFlags & MK_LBUTTON && m_iHotIndex == iItem && m_hdhtiHotItem.flags & HHT_ONHEADER)
  148. // pDC->InvertRect(rectItem);
  149. }
  150. if (i < iItems-1)
  151. {
  152. // draw divider lines
  153. if (m_bDividerLines) //+++
  154. {
  155. pDC->SelectObject(&penShadow);
  156. pDC->MoveTo(rectItem.right-1, rectItem.top+2);
  157. pDC->LineTo(rectItem.right-1, rectItem.bottom-2);
  158. pDC->SelectObject(&penHighLight);
  159. pDC->MoveTo(rectItem.right, rectItem.top+2);
  160. pDC->LineTo(rectItem.right, rectItem.bottom-2);
  161. }
  162. }
  163. }
  164. iWidth += hditem.cxy;
  165. }
  166. if (iWidth > 0)
  167. {
  168. rectClient.right = rectClient.left + iWidth;
  169. pDC->Draw3dRect(rectClient, m_cr3DHighLight, m_cr3DShadow);
  170. }
  171. pDC->SelectObject(pFont);
  172. pDC->SelectObject(pPen);
  173. penHighLight.DeleteObject();
  174. penShadow.DeleteObject();
  175. }
  176. ///////////////////////////////////////////////////////////////////////////////
  177. // DrawItem
  178. void CXHeaderCtrl::DrawItem(LPDRAWITEMSTRUCT)
  179. {
  180. ASSERT(FALSE); // must override for self draw header controls
  181. }
  182. ///////////////////////////////////////////////////////////////////////////////
  183. // DrawItem
  184. void CXHeaderCtrl::DrawItem(CDC* pDC, CRect rect, LPHDITEM lphdi)
  185. {
  186. ASSERT(lphdi->mask & HDI_FORMAT);
  187. int iWidth = 0;
  188. CBitmap* pBitmap = NULL;
  189. BITMAP BitmapInfo;
  190. if (lphdi->fmt & HDF_BITMAP)
  191. {
  192. ASSERT(lphdi->mask & HDI_BITMAP);
  193. ASSERT(lphdi->hbm);
  194. pBitmap = CBitmap::FromHandle(lphdi->hbm);
  195. if (pBitmap)
  196. VERIFY(pBitmap->GetObject(sizeof(BITMAP), &BitmapInfo));
  197. }
  198. rect.left += ((iWidth = DrawImage(pDC, rect, lphdi, FALSE)) != 0) ?
  199. iWidth + m_iSpacing : 0;
  200. rect.right -= ((iWidth = DrawBitmap(pDC, rect, lphdi, pBitmap, &BitmapInfo, TRUE)) != 0) ?
  201. iWidth + m_iSpacing : 0;
  202. DrawText(pDC, rect, lphdi);
  203. }
  204. ///////////////////////////////////////////////////////////////////////////////
  205. // DrawImage
  206. int CXHeaderCtrl::DrawImage(CDC* pDC, CRect rect, LPHDITEM lphdi, BOOL bRight)
  207. {
  208. int iWidth = 0;
  209. if (lphdi->iImage != XHEADERCTRL_NO_IMAGE)
  210. {
  211. CImageList* pImageList = GetImageList();
  212. if (pImageList && (rect.Width() > 0))
  213. {
  214. POINT point;
  215. point.y = rect.CenterPoint().y - ((m_sizeImage.cy+1) >> 1);
  216. if (bRight)
  217. point.x = rect.right - m_sizeImage.cx;
  218. else
  219. point.x = rect.left;
  220. SIZE size;
  221. size.cx = rect.Width() < m_sizeImage.cx ? rect.Width() : m_sizeImage.cx;
  222. size.cy = m_sizeImage.cy;
  223. // save image list background color
  224. COLORREF rgb = pImageList->GetBkColor();
  225. // set image list background color to same as header control
  226. pImageList->SetBkColor(pDC->GetBkColor());
  227. pImageList->DrawIndirect(pDC, lphdi->iImage, point, size, CPoint(0, 0));
  228. pImageList->SetBkColor(rgb);
  229. iWidth = m_sizeImage.cx;
  230. }
  231. else if (rect.Width() > 0)
  232. {
  233. // no image list, just draw checkbox
  234. CRect chkboxrect = rect;
  235. // center the checkbox
  236. int h = 13; // height and width are the same
  237. chkboxrect.right = chkboxrect.left + h;
  238. chkboxrect.top = rect.top + (rect.Height() - h) / 2;
  239. chkboxrect.bottom = chkboxrect.top + h;
  240. // fill rect inside checkbox with white
  241. COLORREF rgbBackground = pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
  242. pDC->FillSolidRect(&chkboxrect, ::GetSysColor(COLOR_WINDOW));
  243. // draw border
  244. CBrush brush(RGB(51,102,153));
  245. pDC->FrameRect(&chkboxrect, &brush);
  246. if (lphdi->iImage == XHEADERCTRL_CHECKED_IMAGE)
  247. {
  248. CPen blackpen(PS_SOLID, 1, RGB(51,153,51));
  249. CPen * pOldPen = pDC->SelectObject(&blackpen);
  250. // draw the checkmark
  251. int x = chkboxrect.left + 9;
  252. ASSERT(x < chkboxrect.right);
  253. int y = chkboxrect.top + 3;
  254. int i = 0;
  255. for (i = 0; i < 4; i++)
  256. {
  257. pDC->MoveTo(x, y);
  258. pDC->LineTo(x, y+3);
  259. x--;
  260. y++;
  261. }
  262. for (i = 0; i < 3; i++)
  263. {
  264. pDC->MoveTo(x, y);
  265. pDC->LineTo(x, y+3);
  266. x--;
  267. y--;
  268. }
  269. if (pOldPen)
  270. pDC->SelectObject(pOldPen);
  271. }
  272. pDC->SetBkColor(rgbBackground);
  273. iWidth = chkboxrect.Width();
  274. }
  275. else
  276. {
  277. // width = 0, do nothing
  278. }
  279. }
  280. return iWidth;
  281. }
  282. ///////////////////////////////////////////////////////////////////////////////
  283. // DrawBitmap
  284. int CXHeaderCtrl::DrawBitmap(CDC* pDC,
  285. CRect rect,
  286. LPHDITEM lphdi,
  287. CBitmap* pBitmap,
  288. BITMAP* pBitmapInfo,
  289. BOOL bRight)
  290. {
  291. UNUSED_ALWAYS(lphdi);
  292. int iWidth = 0;
  293. if (pBitmap)
  294. {
  295. iWidth = pBitmapInfo->bmWidth;
  296. if (iWidth <= rect.Width() && rect.Width() > 0)
  297. {
  298. POINT point;
  299. point.y = rect.CenterPoint().y - (pBitmapInfo->bmHeight >> 1);
  300. if (bRight)
  301. point.x = rect.right - iWidth;
  302. else
  303. point.x = rect.left;
  304. CDC dc;
  305. if (dc.CreateCompatibleDC(pDC) == TRUE)
  306. {
  307. CBitmap * pOldBitmap = dc.SelectObject(pBitmap);
  308. iWidth = pDC->BitBlt(
  309. point.x, point.y,
  310. pBitmapInfo->bmWidth, pBitmapInfo->bmHeight,
  311. &dc,
  312. 0, 0,
  313. SRCCOPY
  314. ) ? iWidth:0;
  315. dc.SelectObject(pOldBitmap);
  316. }
  317. else
  318. iWidth = 0;
  319. }
  320. else
  321. iWidth = 0;
  322. }
  323. return iWidth;
  324. }
  325. ///////////////////////////////////////////////////////////////////////////////
  326. // DrawText
  327. int CXHeaderCtrl::DrawText(CDC* pDC, CRect rect, LPHDITEM lphdi)
  328. {
  329. CSize size = pDC->GetTextExtent(lphdi->pszText);
  330. #if 0 // -----------------------------------------------------------
  331. pDC->SetTextColor(RGB(0,0,255));
  332. if (rect.Width() > 0 && lphdi->mask & HDI_TEXT && lphdi->fmt & HDF_STRING)
  333. {
  334. size = pDC->GetTextExtent(lphdi->pszText);
  335. // always center column headers
  336. pDC->DrawText(lphdi->pszText, -1, rect,
  337. DT_CENTER|DT_END_ELLIPSIS|DT_SINGLELINE|DT_VCENTER);
  338. }
  339. #endif // -----------------------------------------------------------
  340. //+++
  341. if (rect.Width() > 0 && lphdi->mask & HDI_TEXT && lphdi->fmt & HDF_STRING)
  342. {
  343. pDC->SetTextColor(m_rgbText);
  344. UINT nFormat = m_nFormat;
  345. if (nFormat == DT_DEFAULT)
  346. {
  347. // default to whatever alignment the column is set to
  348. if (lphdi->fmt & LVCFMT_CENTER)
  349. nFormat = DT_CENTER;
  350. else if (lphdi->fmt & LVCFMT_RIGHT)
  351. nFormat = DT_RIGHT;
  352. else
  353. nFormat = DT_LEFT;
  354. }
  355. pDC->DrawText(lphdi->pszText, -1, rect,
  356. nFormat | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER);
  357. }
  358. size.cx = rect.Width() > size.cx ? size.cx : rect.Width();
  359. return size.cx > 0 ? size.cx : 0;
  360. }
  361. ///////////////////////////////////////////////////////////////////////////////
  362. // OnInsertItem
  363. LRESULT CXHeaderCtrl::OnInsertItem(WPARAM, LPARAM)
  364. {
  365. return Default();
  366. }
  367. ///////////////////////////////////////////////////////////////////////////////
  368. // OnDeleteItem
  369. LRESULT CXHeaderCtrl::OnDeleteItem(WPARAM, LPARAM)
  370. {
  371. return Default();
  372. }
  373. ///////////////////////////////////////////////////////////////////////////////
  374. // OnSetImageList
  375. LRESULT CXHeaderCtrl::OnSetImageList(WPARAM, LPARAM lParam)
  376. {
  377. CImageList* pImageList;
  378. pImageList = CImageList::FromHandle((HIMAGELIST)lParam);
  379. IMAGEINFO info;
  380. if (pImageList->GetImageInfo(0, &info))
  381. {
  382. m_sizeImage.cx = info.rcImage.right - info.rcImage.left;
  383. m_sizeImage.cy = info.rcImage.bottom - info.rcImage.top;
  384. }
  385. return Default();
  386. }
  387. ///////////////////////////////////////////////////////////////////////////////
  388. // OnLayout
  389. LRESULT CXHeaderCtrl::OnLayout(WPARAM, LPARAM lParam)
  390. {
  391. LPHDLAYOUT lphdlayout = (LPHDLAYOUT)lParam;
  392. if (m_bStaticBorder)
  393. lphdlayout->prc->right += GetSystemMetrics(SM_CXBORDER)*2;
  394. return CHeaderCtrl::DefWindowProc(HDM_LAYOUT, 0, lParam);
  395. }
  396. ///////////////////////////////////////////////////////////////////////////////
  397. // OnSysColorChange
  398. void CXHeaderCtrl::OnSysColorChange()
  399. {
  400. XLISTCTRL_TRACE(_T("in CXHeaderCtrl::OnSysColorChange\n"));
  401. CHeaderCtrl::OnSysColorChange();
  402. m_cr3DHighLight = ::GetSysColor(COLOR_3DHIGHLIGHT);
  403. m_cr3DShadow = ::GetSysColor(COLOR_3DSHADOW);
  404. m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
  405. m_crBtnText = ::GetSysColor(COLOR_BTNTEXT);
  406. }
  407. ///////////////////////////////////////////////////////////////////////////////
  408. // OnEraseBkgnd
  409. BOOL CXHeaderCtrl::OnEraseBkgnd(CDC* pDC)
  410. {
  411. UNUSED_ALWAYS(pDC);
  412. return TRUE;
  413. }
  414. ///////////////////////////////////////////////////////////////////////////////
  415. // OnPaint
  416. void CXHeaderCtrl::OnPaint()
  417. {
  418. CPaintDC dc(this);
  419. if (m_bDoubleBuffer)
  420. {
  421. CMemDC MemDC(&dc);
  422. DrawCtrl(&MemDC);
  423. }
  424. else
  425. DrawCtrl(&dc);
  426. }
  427. void CXHeaderCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
  428. {
  429. XLISTCTRL_TRACE(_T("in CXHeaderCtrl::OnLButtonDblClk\n"));
  430. SendMessage(WM_LBUTTONDOWN, nFlags, MAKELPARAM(point.x, point.y));
  431. //CHeaderCtrl::OnLButtonDown(nFlags, point);
  432. }