Advertisement
Guest User

Untitled

a guest
May 17th, 2015
287
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 12.73 KB | None | 0 0
  1. #include "stdafx.h"
  2. #include "OwnListCtrl.h"
  3. #include "ListCtrlEx.h"
  4.  
  5. #define VK_A 0x41
  6.  
  7. IMPLEMENT_DYNAMIC(CListCtrlEx, CListCtrl)
  8.  
  9. CListCtrlEx::CListCtrlEx() :
  10.     m_nFocusCell(-1)
  11.     , m_nItem(0)
  12.     , m_bFocus(TRUE)
  13. {
  14.     m_pFont = make_shared<CFont>();
  15. }
  16.  
  17. CListCtrlEx::~CListCtrlEx()
  18. {
  19.  
  20. }
  21.  
  22. BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
  23.     ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CListCtrlEx::OnCustomdraw)
  24.     ON_WM_LBUTTONDOWN()
  25.     ON_MESSAGE(WM_FOCUS_CHANGED, &CListCtrlEx::OnFocusChanged)
  26.     ON_MESSAGE(WM_SETFONT, &CListCtrlEx::OnSetFont)
  27.     ON_WM_MEASUREITEM_REFLECT()
  28. END_MESSAGE_MAP()
  29.  
  30. void CListCtrlEx::GetCurrentFont(LOGFONT& lf) const
  31. {
  32.     GetFont()->GetLogFont(&lf);
  33. }
  34.  
  35. void CListCtrlEx::SetupFont(int nSize, const CString& strName)
  36. {
  37.     if (m_pFont.get())
  38.         m_pFont.get()->DeleteObject();
  39.  
  40.     VERIFY(m_pFont.get()->CreatePointFont(nSize, strName));
  41.     SetFont(m_pFont.get());
  42. }
  43.  
  44. LRESULT CListCtrlEx::OnSetFont(WPARAM wParam, LPARAM)
  45. {
  46.     LRESULT res = Default();
  47.  
  48.     CRect rc;
  49.     GetWindowRect(&rc);
  50.  
  51.     WINDOWPOS wp;
  52.     ZeroMemory(&wp, sizeof(wp));
  53.     wp.hwnd  = m_hWnd;
  54.     wp.cx    = rc.Width();
  55.     wp.cy    = rc.Height();
  56.     wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
  57.     SendMessage(WM_WINDOWPOSCHANGED, 0, (LPARAM)&wp);
  58.  
  59.     return res;
  60. }
  61.  
  62. void CListCtrlEx::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
  63. {
  64.     HDC hDC = ::GetDC(NULL);
  65.     HFONT hFontOld = (HFONT)SelectObject(hDC, m_pFont.get()->GetSafeHandle());
  66.     CSize size;
  67.  
  68.     GetTextExtentPoint32(hDC, _T(""), 1, &size);
  69.     lpMeasureItemStruct->itemHeight = size.cy;
  70.     SelectObject(hDC, hFontOld);
  71.     ::ReleaseDC(NULL, hDC);
  72. }
  73.  
  74. void CListCtrlEx::OnCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
  75. {
  76.     NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
  77.     int nRow = (int)pLVCD->nmcd.dwItemSpec;
  78.    
  79.     switch (pLVCD->nmcd.dwDrawStage)
  80.     {
  81.         case CDDS_PREPAINT:
  82.             *pResult |= CDRF_NOTIFYITEMDRAW;
  83.             break;
  84.  
  85.         // до отрисовки элемента
  86.         case CDDS_ITEMPREPAINT:
  87.         {
  88.             if (pLVCD->nmcd.uItemState & CDIS_FOCUS)
  89.             {
  90.                 if (GetNextItem(-1, LVNI_FOCUSED) == nRow)
  91.                 {      
  92.                     // мы хотим нарисовать фокус для ячейки вместо фокуса всего элемента
  93.                     pLVCD->nmcd.uItemState &= ~CDIS_FOCUS;
  94.                     *pResult |= CDRF_NOTIFYPOSTPAINT;                  
  95.                 }
  96.             }
  97.  
  98.             ::SelectObject(pLVCD->nmcd.hdc, (HFONT)m_pFont.get());
  99.             *pResult |= CDRF_NEWFONT;
  100.         }
  101.         break;
  102.  
  103.  
  104.         // после отрисовки полного элемента
  105.         case CDDS_ITEMPOSTPAINT:
  106.         {
  107.             if (GetNextItem(-1, LVNI_FOCUSED) != nRow)
  108.                 break;
  109.  
  110.             // рисуем фокусный прямоугольник для одной ячейки
  111.             CRect rcFocus;
  112.             CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
  113.  
  114.             VERIFY(GetCellRect(nRow, m_nFocusCell, rcFocus));
  115.  
  116.             // выравниваем прямоугольник в соответствии с линиями сетки
  117.             if (GetExtendedStyle() & LVS_EX_GRIDLINES)
  118.             {
  119.                 int cxborder = ::GetSystemMetrics(SM_CXBORDER);
  120.  
  121.                 // колонки после первой видимой колонки должны учитывать границу сетки слева
  122.                 if (GetHeaderCtrl()->OrderToIndex(m_nFocusCell) != 0)
  123.                     rcFocus.left += cxborder;
  124.  
  125.                 rcFocus.bottom -= cxborder;
  126.             }
  127.  
  128.             pDC->DrawFocusRect(rcFocus);
  129.         }
  130.         break;
  131.     }
  132. }
  133.  
  134. BOOL CListCtrlEx::GetCellRect(int nRow, int nCol, CRect& rect)
  135. {
  136.     // Find the top and bottom of the cell-rectangle
  137.     CRect rowRect;
  138.     if (GetItemRect(nRow, rowRect, LVIR_BOUNDS) == FALSE)
  139.         return FALSE;
  140.  
  141.     // Find the left and right of the cell-rectangle using the CHeaderCtrl
  142.     CRect colRect;
  143.     if (GetHeaderCtrl()->GetItemRect(nCol, colRect) == FALSE)
  144.         return FALSE;
  145.  
  146.     // Adjust for scrolling
  147.     colRect.left  -= GetScrollPos(SB_HORZ);
  148.     colRect.right -= GetScrollPos(SB_HORZ);
  149.  
  150.     rect.left   = colRect.left;
  151.     rect.top    = rowRect.top;
  152.     rect.right  = colRect.right;
  153.     rect.bottom = rowRect.bottom;
  154.     return TRUE;
  155. }
  156.  
  157. void CListCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
  158. {
  159.     CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
  160.     int nItem = lpDrawItemStruct->itemID;
  161.    
  162.     // Save dc state
  163.     int nSavedDC = pDC->SaveDC();
  164.    
  165.     // Get rectangles for drawing
  166.     CRect rcBounds, rcLabel;
  167.    
  168.     VERIFY(GetItemRect(nItem, rcBounds, LVIR_BOUNDS));
  169.     VERIFY(GetItemRect(nItem, rcLabel, LVIR_LABEL));
  170.    
  171.     // Should the item be highlighted
  172.     BOOL bHighlight = (GetItemState(nItem, LVIS_SELECTED) == LVIS_SELECTED);
  173.     if (bHighlight)
  174.     {
  175.         pDC->SetBkColor(::GetSysColor(m_bFocus ? COLOR_HIGHLIGHT : COLOR_INACTIVECAPTION));
  176.  
  177.         if (nItem != m_nItem)
  178.         {
  179.             pDC->FillRect(rcBounds, &CBrush(::GetSysColor(m_bFocus ? COLOR_HIGHLIGHT : COLOR_INACTIVECAPTION)));
  180.         }
  181.         else if (m_nFocusCell == 0) // selected the first cell
  182.         {
  183.             CRect rcRightCell;
  184.  
  185.             GetCellRect(nItem, m_nFocusCell + 1, rcRightCell);
  186.             HighLightItem(TRUE, rcBounds, rcRightCell, pDC);
  187.         }
  188.         else if (m_nFocusCell + 1 == GetHeaderCtrl()->GetItemCount()) // selected the last cell
  189.         {
  190.             CRect rcLeftCell;
  191.  
  192.             GetCellRect(nItem, m_nFocusCell - 1, rcLeftCell);
  193.             HighLightItem(FALSE, rcBounds, rcLeftCell, pDC);
  194.         }
  195.         else // selected somewhere else cell
  196.         {
  197.             CRect rcLeftCell;
  198.  
  199.             GetCellRect(nItem, m_nFocusCell - 1, rcLeftCell);
  200.             HighLightItem(FALSE, rcBounds, rcLeftCell, pDC);
  201.  
  202.             CRect rcRightCell;
  203.  
  204.             GetCellRect(nItem, m_nFocusCell + 1, rcRightCell);
  205.             HighLightItem(TRUE, rcBounds, rcRightCell, pDC);
  206.         }
  207.     }
  208.  
  209.     // Perform the drawing of the focus rectangle
  210.     // for the first cell in list control
  211.     if (m_nFocusCell == -1)
  212.         DrawInitialCell(rcBounds, pDC);
  213.  
  214.     CRect rcCol(rcBounds);
  215.  
  216.     // Set clip region
  217.     rcCol.right = rcCol.left + GetColumnWidth(0);
  218.  
  219.     // Labels are offset by a certain amount  
  220.     // This offset is related to the width of a space character
  221.     int offset = pDC->GetTextExtent(_T(" "), 1).cx * 2;
  222.     // Draw item label - Column 0
  223.     rcLabel.left  += offset / 2;
  224.     rcLabel.right -= offset;
  225.    
  226.     CString sLabel = GetItemText(nItem, 0);
  227.  
  228.     if (bHighlight && m_nFocusCell != 0 || bHighlight && nItem != m_nItem)
  229.         pDC->SetTextColor(::GetSysColor(m_bFocus ? COLOR_HIGHLIGHTTEXT : COLOR_INACTIVECAPTIONTEXT));
  230.     else
  231.     {
  232.         pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
  233.         pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
  234.     }
  235.  
  236.     pDC->DrawText(sLabel, rcLabel, DT_LEFT | DT_SINGLELINE |
  237.         DT_NOPREFIX | DT_NOCLIP | DT_END_ELLIPSIS | DT_VCENTER);
  238.    
  239.     // Draw labels for remaining columns
  240.     LV_COLUMN lvc;
  241.     lvc.mask = LVCF_FMT | LVCF_WIDTH;
  242.  
  243.     for (int nColumn = 1; GetColumn(nColumn, &lvc); nColumn++)
  244.     {
  245.         rcCol.left = rcCol.right;
  246.         rcCol.right += lvc.cx;
  247.        
  248.         sLabel = GetItemText(nItem, nColumn);
  249.         if (sLabel.IsEmpty())
  250.             continue;
  251.        
  252.         rcLabel = rcCol;
  253.         rcLabel.left += offset;
  254.         rcLabel.right -= offset;
  255.        
  256.         if (bHighlight && m_nFocusCell != nColumn || bHighlight && nItem != m_nItem)
  257.             pDC->SetTextColor(::GetSysColor(m_bFocus ? COLOR_HIGHLIGHTTEXT : COLOR_INACTIVECAPTIONTEXT));
  258.         else
  259.             pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
  260.  
  261.         pDC->DrawText(sLabel, rcLabel, DT_LEFT | DT_SINGLELINE |
  262.             DT_NOPREFIX | DT_END_ELLIPSIS | DT_VCENTER);
  263.     }
  264.    
  265.     // Restore dc
  266.     pDC->RestoreDC(nSavedDC);
  267. }
  268.  
  269. void CListCtrlEx::HighLightItem(BOOL bRight, const CRect& rcBounds, const CRect& rcCell, CDC* const pDC)
  270. {
  271.     CRect rcHighLight;
  272.  
  273.     rcHighLight.top     = rcBounds.top;
  274.     rcHighLight.bottom  = rcBounds.bottom;
  275.     rcHighLight.right   = (bRight) ? rcBounds.right : rcCell.right;
  276.     rcHighLight.left    = (bRight) ? rcCell.left    : rcBounds.left;
  277.  
  278.     pDC->FillRect(rcHighLight, &CBrush(::GetSysColor(m_bFocus ? COLOR_HIGHLIGHT : COLOR_INACTIVECAPTION)));
  279. }
  280.  
  281. void CListCtrlEx::DrawInitialCell(const CRect& rcBounds, CDC* const pDC)
  282. {
  283.     CRect rect;
  284.  
  285.     rect = rcBounds;
  286.        
  287.     // Draw the focus-rectangle for a single-cell
  288.     VERIFY(GetCellRect(0, 0, rect));
  289.  
  290.     // Adjust rectangle according to grid-lines
  291.     if (GetExtendedStyle() & LVS_EX_GRIDLINES)
  292.         rect.bottom -= ::GetSystemMetrics(SM_CXBORDER);
  293.        
  294.     m_nFocusCell = 0;
  295.     m_nItem = 0;
  296.     pDC->DrawFocusRect(rect);
  297.     SetItemState(0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
  298. }
  299.  
  300. BOOL CListCtrlEx::PreTranslateMessage(MSG* pMsg)
  301. {
  302.     if (pMsg->message == WM_KEYDOWN)
  303.     {
  304.         switch (pMsg->wParam)
  305.         {
  306.         case VK_A:
  307.         {
  308.             if (::GetKeyState(VK_CONTROL) < 0)
  309.             {
  310.                 if (!(GetStyle() & LVS_SINGLESEL))
  311.                     SetItemState(-1, -1, LVIS_SELECTED);
  312.             }
  313.             break;
  314.         }
  315.         case VK_LEFT:
  316.             {
  317.                 if (::GetKeyState(VK_CONTROL) < 0)
  318.                     return TRUE;
  319.  
  320.                 if (::GetKeyState(VK_SHIFT) >= 0 &&
  321.                     !(GetStyle() & LVS_SINGLESEL))
  322.                 {
  323.                     SetItemState(-1, 0, LVIS_SELECTED);
  324.                     SetItemState(m_nItem, LVIS_SELECTED, LVIS_SELECTED);
  325.                 }
  326.  
  327.                 MoveFocusCell(false);
  328.             }
  329.             break;
  330.         case VK_RIGHT:
  331.             {
  332.                 if (::GetKeyState(VK_CONTROL) < 0)
  333.                     return TRUE;
  334.                
  335.                 if (::GetKeyState(VK_SHIFT) >= 0 &&
  336.                     !(GetStyle() & LVS_SINGLESEL))
  337.                 {
  338.                     SetItemState(-1, 0, LVIS_SELECTED);
  339.                     SetItemState(m_nItem, LVIS_SELECTED, LVIS_SELECTED);
  340.                 }
  341.  
  342.                 MoveFocusCell(true);
  343.             }
  344.             break;
  345.         case VK_UP:
  346.             {
  347.                 if (::GetKeyState(VK_CONTROL) < 0)
  348.                     return TRUE;
  349.  
  350.                 if (m_nItem > 0)
  351.                     m_nItem--;
  352.             }
  353.             break;
  354.         case VK_DOWN:
  355.             {
  356.                 if (::GetKeyState(VK_CONTROL) < 0)
  357.                     return TRUE;
  358.  
  359.                 if (m_nItem + 1 < GetItemCount())
  360.                     m_nItem++;
  361.             }
  362.             break;
  363.         case VK_ESCAPE:
  364.         {
  365.             if (!(GetStyle() & LVS_SINGLESEL))
  366.             {
  367.                 if (GetSelectedCount() > 1)
  368.                     VERIFY(SetItemState(-1, 0, LVIS_SELECTED));
  369.             }
  370.         }
  371.         case VK_RETURN:
  372.             return TRUE;
  373.         default:
  374.             break;
  375.         }
  376.     }
  377.  
  378.     return CListCtrl::PreTranslateMessage(pMsg);
  379. }
  380.  
  381. void CListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
  382. {
  383.     LVHITTESTINFO hitinfo = {0};
  384.     hitinfo.flags = nFlags;
  385.     hitinfo.pt = point;
  386.     SubItemHitTest(&hitinfo);
  387.  
  388.     if (hitinfo.iSubItem != -1)
  389.     {
  390.         m_nItem = hitinfo.iItem;
  391.         // Update the focused cell before calling CListCtrl::OnLButtonDown()
  392.         // as it might cause a row-repaint
  393.         m_nFocusCell = hitinfo.iSubItem;
  394.         CListCtrl::OnLButtonDown(nFlags, point);
  395.  
  396.         // CListCtrl::OnLButtonDown() doesn't always cause a row-repaint
  397.         // call our own method to ensure the row is repainted
  398.         UpdateFocusCell(hitinfo.iSubItem);
  399.     }
  400. }
  401.  
  402. // Force redraw of focus row, so the focus cell becomes visible
  403. void CListCtrlEx::UpdateFocusCell(int nCol)
  404. {
  405.     m_nFocusCell = nCol;    // Update focus cell before starting re-draw
  406.     int nFocusRow = GetNextItem(-1, LVNI_FOCUSED);
  407.     if (nFocusRow >= 0)
  408.     {
  409.         CRect itemRect;
  410.         VERIFY(GetItemRect(nFocusRow, itemRect, LVIR_BOUNDS));
  411.         m_nItem = nFocusRow;
  412.         InvalidateRect(itemRect);
  413.         UpdateWindow();
  414.     }
  415. }
  416.  
  417. void CListCtrlEx::MoveFocusCell(bool right)
  418. {
  419.     if (GetItemCount() <= 0)
  420.         return;
  421.    
  422.     // Convert focus-cell to order index
  423.     int nOrderIndex = -1;
  424.     for (int i = 0; i < GetHeaderCtrl()->GetItemCount(); ++i)
  425.     {
  426.         int nCol = GetHeaderCtrl()->OrderToIndex(i);
  427.         if (nCol == m_nFocusCell)
  428.         {
  429.             nOrderIndex = i;
  430.             break;
  431.         }
  432.     }
  433.  
  434.     // Move to the following column
  435.     if (right)
  436.         nOrderIndex++;
  437.     else
  438.         nOrderIndex--;
  439.  
  440.     // Convert order-index to focus cell
  441.     if (nOrderIndex >= 0 && nOrderIndex < GetHeaderCtrl()->GetItemCount())
  442.         m_nFocusCell = GetHeaderCtrl()->OrderToIndex(nOrderIndex);
  443.    
  444.     // Ensure the column is visible
  445.     if (m_nFocusCell >= 0)
  446.         VERIFY(EnsureColumnVisible(m_nFocusCell, false));
  447.  
  448.     if (m_nFocusCell >= 0)
  449.         UpdateFocusCell(m_nFocusCell);
  450. }
  451.  
  452. BOOL CListCtrlEx::EnsureColumnVisible(int nCol, bool bPartialOK)
  453. {
  454.     if (nCol < 0 || nCol >= GetHeaderCtrl()->GetItemCount())
  455.         return FALSE;
  456.  
  457.     CRect rcHeader;
  458.     if (GetHeaderCtrl()->GetItemRect(nCol, rcHeader) == FALSE)
  459.         return FALSE;
  460.  
  461.     CRect rcClient;
  462.     GetClientRect(&rcClient);
  463.  
  464.     int nOffset = GetScrollPos(SB_HORZ);
  465.  
  466.     if (bPartialOK)
  467.     {
  468.         if ((rcHeader.left - nOffset < rcClient.right) && (rcHeader.right - nOffset > 0))
  469.         {
  470.             return TRUE;
  471.         }
  472.     }
  473.  
  474.     int nScrollX = 0;
  475.  
  476.     if ((rcHeader.Width() > rcClient.Width()) || (rcHeader.left - nOffset < 0))
  477.     {
  478.         nScrollX = rcHeader.left - nOffset;
  479.     }
  480.     else if(rcHeader.right - nOffset > rcClient.right)
  481.     {
  482.         nScrollX = rcHeader.right - nOffset - rcClient.right;
  483.     }
  484.  
  485.     if (nScrollX != 0)
  486.     {
  487.         CSize size(nScrollX, 0);
  488.         if (Scroll(size) == FALSE)
  489.             return FALSE;
  490.     }
  491.  
  492.     return TRUE;
  493. }
  494.  
  495. void CListCtrlEx::PreSubclassWindow()
  496. {
  497.     SetExtendedStyle(GetExtendedStyle() | LVS_EX_GRIDLINES);
  498.     SetExtendedStyle(GetExtendedStyle() | LVS_EX_DOUBLEBUFFER); // anti-flickering
  499.  
  500.     CListCtrl::PreSubclassWindow();
  501. }
  502.  
  503. LRESULT CListCtrlEx::OnFocusChanged(WPARAM wParam, LPARAM lParam)
  504. {
  505.     m_bFocus = static_cast<BOOL>(wParam);
  506.  
  507.     CRect rect;
  508.  
  509.     GetClientRect(rect);
  510.     InvalidateRect(rect);
  511.    
  512.     return 1;
  513. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement