GridListCtrl.cpp
上传用户:zdjx198
上传日期:2007-01-02
资源大小:95k
文件大小:15k
源码类别:

ListView/ListBox

开发平台:

Visual C++

  1. // GridListCtrl.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include "gridlist.h"
  5. #include "GridListCtrl.h"
  6. #ifdef _DEBUG
  7. #define new DEBUG_NEW
  8. #undef THIS_FILE
  9. static char THIS_FILE[] = __FILE__;
  10. #endif
  11. /////////////////////////////////////////////////////////////////////////////
  12. // CGridListCtrl
  13. CGridListCtrl::CGridListCtrl()
  14. {
  15. m_CurSubItem = -1;
  16. }
  17. CGridListCtrl::~CGridListCtrl()
  18. {
  19. }
  20. BEGIN_MESSAGE_MAP(CGridListCtrl, CListCtrl)
  21. //{{AFX_MSG_MAP(CGridListCtrl)
  22. ON_WM_LBUTTONDOWN()
  23. ON_WM_HSCROLL()
  24. ON_WM_VSCROLL()
  25. //}}AFX_MSG_MAP
  26. ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
  27. END_MESSAGE_MAP()
  28. /////////////////////////////////////////////////////////////////////////////
  29. // CGridListCtrl message handlers
  30. BOOL CGridListCtrl::PrepareControl(WORD wStyle)
  31. {
  32. m_wStyle = wStyle;
  33. ASSERT( m_hWnd );
  34.     DWORD dwStyle = GetWindowLong(m_hWnd, GWL_STYLE); 
  35. dwStyle &= ~(LVS_TYPEMASK);
  36. dwStyle &= ~(LVS_EDITLABELS);
  37.  
  38. // Make sure we have report view and send edit label messages.
  39.     SetWindowLong( m_hWnd, GWL_STYLE, dwStyle | LVS_REPORT );
  40. // Enable the full row selection and the drag drop of headers.
  41. DWORD styles = LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP ;
  42. // Use macro since this is new and not in MFC.
  43. ListView_SetExtendedListViewStyleEx(m_hWnd, styles, styles );
  44. return TRUE;
  45. }
  46. void CGridListCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
  47. {
  48. LVHITTESTINFO ht;
  49. ht.pt = point;
  50. // Test for which subitem was clicked.
  51. // Use macro since this is new and not in MFC.
  52. int rval = ListView_SubItemHitTest( m_hWnd, &ht );
  53. // Store the old column number and set the new column value.
  54. int oldsubitem = m_CurSubItem;
  55. m_CurSubItem = IndexToOrder( ht.iSubItem );
  56. CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
  57. // Make the column fully visible.
  58. // We have to take into account that the columns may be reordered
  59. MakeColumnVisible( Header_OrderToIndex( pHeader->m_hWnd, m_CurSubItem ) );
  60. // Store old state of the item.
  61. int state = GetItemState( ht.iItem, LVIS_FOCUSED );
  62. // Call default left button click is here just before we might bail.
  63. // Also updates the state of the item.
  64. CListCtrl::OnLButtonDown(nFlags, point);
  65. // Bail if the state from before was not focused or the 
  66. // user has not already clicked on this cell.
  67. if( !state 
  68. || m_CurSubItem == -1 
  69. || oldsubitem != m_CurSubItem ) return;
  70. int doedit = 0;
  71. // If we are in column 0 make sure that the user clicked on 
  72. // the item label.
  73. if( 0 == ht.iSubItem )
  74. {
  75. if( ht.flags & LVHT_ONITEMLABEL ) doedit = 1;
  76. }
  77. else
  78. {
  79. doedit = 1;
  80. }
  81. if( !doedit ) return;
  82.     // Send Notification to parent of ListView ctrl
  83. CString str;
  84. str = GetItemText( ht.iItem, ht.iSubItem );
  85.     LV_DISPINFO dispinfo;
  86. dispinfo.hdr.hwndFrom = m_hWnd;
  87. dispinfo.hdr.idFrom = GetDlgCtrlID();
  88. dispinfo.hdr.code = LVN_BEGINLABELEDIT;
  89. dispinfo.item.mask = LVIF_TEXT;
  90. dispinfo.item.iItem = ht.iItem;
  91. dispinfo.item.iSubItem = ht.iSubItem;
  92. dispinfo.item.pszText = (LPTSTR)((LPCTSTR)str);
  93. dispinfo.item.cchTextMax = str.GetLength();
  94. GetParent()->SendMessage( WM_NOTIFY, GetDlgCtrlID(), 
  95. (LPARAM)&dispinfo );
  96. }
  97. BOOL CGridListCtrl::PositionControl( CWnd * pWnd, int iItem, int iSubItem )
  98. {
  99. ASSERT( pWnd && pWnd->m_hWnd );
  100. ASSERT( iItem >= 0 );
  101. // Make sure that the item is visible
  102. if( !EnsureVisible( iItem, TRUE ) ) return NULL;
  103. // Make sure that nCol is valid
  104. CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
  105. int nColumnCount = pHeader->GetItemCount();
  106. ASSERT( iSubItem >= 0 && iSubItem < nColumnCount );
  107. if( iSubItem >= nColumnCount || 
  108. // We have to take into account that the header may be reordered
  109. GetColumnWidth(Header_OrderToIndex( pHeader->m_hWnd,iSubItem)) < 5 )
  110. {
  111. return 0;
  112. }
  113. // Get the header order array to sum the column widths up to the selected cell.
  114. int *orderarray = new int[ nColumnCount ];
  115. Header_GetOrderArray( pHeader->m_hWnd, nColumnCount, orderarray );
  116. int offset = 0;
  117. int i;
  118. for( i = 0; orderarray[i] != iSubItem; i++ )
  119. offset += GetColumnWidth( orderarray[i] );
  120. int colwidth = GetColumnWidth( iSubItem );
  121. delete[] orderarray;
  122. CRect rect;
  123. GetItemRect( iItem, &rect, LVIR_BOUNDS );
  124. // Scroll if we need to expose the column
  125. CRect rcClient;
  126. GetClientRect( &rcClient );
  127. if( offset + rect.left < 0 || offset + colwidth + rect.left > rcClient.right )
  128.         {
  129. CSize size;
  130. size.cx = offset + rect.left;
  131. size.cy = 0;
  132. Scroll( size );
  133. rect.left -= size.cx;
  134.         }
  135. rect.left += offset+4;
  136. rect.right = rect.left + colwidth - 3 ;
  137. // The right end of the control should not go past the edge 
  138. // of the grid control.
  139. if( rect.right > rcClient.right) 
  140. rect.right = rcClient.right;
  141. pWnd->MoveWindow( &rect );
  142. return 1;
  143. }
  144. void CGridListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) 
  145. {
  146. // This function is called by the control in different 
  147. // stages during the control drawing process.
  148. NMLVCUSTOMDRAW *pCD = (NMLVCUSTOMDRAW*)pNMHDR;
  149. // By default set the return value to do the default behavior.
  150. *pResult = 0;
  151. switch( pCD->nmcd.dwDrawStage )
  152. {
  153. case  CDDS_PREPAINT:  // First stage (for the whole control)
  154. // Tell the control we want to receive drawing messages  
  155. // for drawing items.
  156. *pResult = CDRF_NOTIFYITEMDRAW;
  157. // The next stage is handled in the default:
  158. break;
  159. case CDDS_ITEMPREPAINT | CDDS_SUBITEM: // Stage three (called for each subitem of the focused item)
  160. {
  161. // We don't want to draw anything here, but we need to respond 
  162. // of DODEFAULT will be the next stage.
  163. // Tell the control we want to handle drawing after the subitem 
  164. // is drawn.
  165. *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NOTIFYPOSTPAINT;
  166. }
  167. break;
  168. case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM: // Stage four (called for each subitem of the focused item)
  169. {
  170. // We do the drawing here (well maybe).
  171. // This is actually after the control has done its drawing
  172. // on the subitem.  Since drawing a cell is near instantaneous
  173. // the user won't notice.
  174. int subitem = pCD->iSubItem;
  175. // Only do our own drawing if this subitem has focus at the item level.
  176. if( (pCD->nmcd.uItemState & CDIS_FOCUS) )
  177. {
  178. // If this subitem is the subitem with the current focus,
  179. // draw it.  Otherwise let the control draw it.  
  180. CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
  181. // We have to take into account the possibility that the 
  182. // columns may be reordered.
  183. if( subitem == Header_OrderToIndex( pHeader->m_hWnd,  m_CurSubItem ) )
  184. {
  185. // POSTERASE
  186. CDC* pDC = CDC::FromHandle(pCD->nmcd.hdc);
  187. // Calculate the offset of the text from the right and left of the cell.
  188. int offset = pDC->GetTextExtent(_T(" "), 1 ).cx*2;
  189. // The rect for the cell gives correct left and right values.
  190. CRect rect = pCD->nmcd.rc;
  191. CRect bounds;
  192. GetItemRect( pCD->nmcd.dwItemSpec, &bounds, LVIR_BOUNDS );
  193. // Get the top and bottom from the item itself.
  194. rect.top = bounds.top;
  195. rect.bottom = bounds.bottom;
  196. // Adjust rectangle for horizontal scroll and first column label
  197. {
  198. if( subitem == 0 )
  199. {
  200. CRect lrect;
  201. GetItemRect( pCD->nmcd.dwItemSpec, &lrect, LVIR_LABEL );
  202. rect.left = lrect.left;
  203. rect.right = lrect.right;
  204. }
  205. else
  206. {
  207. rect.right += bounds.left;
  208. rect.left  += bounds.left;
  209. }
  210. }
  211. // Clear the background with button face color
  212. pDC->FillRect(rect, &CBrush(::GetSysColor(COLOR_3DFACE)));
  213. // PREPAINT
  214. CString str;
  215. str = GetItemText( pCD->nmcd.dwItemSpec, pCD->iSubItem );
  216. // Deflate the rect by the horizontal offset.
  217. rect.DeflateRect( offset, 0 );
  218. // You could also make this column alignment sensitive here.
  219. pDC->DrawText( str, rect, 
  220. DT_SINGLELINE|DT_NOPREFIX|DT_LEFT|DT_VCENTER|DT_END_ELLIPSIS);
  221. // POSTPAINT
  222. // Draw rounded edge
  223. rect.InflateRect( offset, 0 );
  224. pDC->Draw3dRect( &rect, ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DFACE) );
  225. rect.DeflateRect( 1, 1 );
  226. pDC->Draw3dRect( &rect, ::GetSysColor(COLOR_3DDKSHADOW), ::GetSysColor(COLOR_3DHILIGHT) );
  227. // Tell the control that we handled the drawing for this subitem.
  228. *pResult = CDRF_SKIPDEFAULT;
  229. }
  230. }
  231. }
  232. break;
  233. default: // Stage two handled here. (called for each item)
  234. if( !(pCD->nmcd.uItemState & CDIS_FOCUS) )
  235. {
  236. // If this item does not have focus, let the 
  237. // control draw the whole item.
  238. *pResult = CDRF_DODEFAULT;
  239. }
  240. else
  241. {
  242. // If this item has focus, tell the control we want
  243. // to handle subitem drawing.
  244. *pResult = CDRF_NOTIFYSUBITEMDRAW;
  245. }
  246. break;
  247. }
  248. }
  249. void CGridListCtrl::MakeColumnVisible(int nCol)
  250. {
  251. if( nCol < 0 )
  252. return;
  253. // Get the order array to total the column offset.
  254. CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
  255. int colcount = pHeader->GetItemCount();
  256. ASSERT( nCol < colcount );
  257. int *orderarray = new int[ colcount ];
  258. Header_GetOrderArray( pHeader->m_hWnd, colcount, orderarray );
  259. // Get the column offset
  260. int offset = 0;
  261. for( int i = 0; orderarray[i] != nCol; i++ )
  262. offset += GetColumnWidth( orderarray[i] );
  263. int colwidth = GetColumnWidth( nCol );
  264. delete[] orderarray;
  265. CRect rect;
  266. GetItemRect( 0, &rect, LVIR_BOUNDS );
  267. // Now scroll if we need to expose the column
  268. CRect rcClient;
  269. GetClientRect( &rcClient );
  270. if( offset + rect.left < 0 || offset + colwidth + rect.left > rcClient.right )
  271.         {
  272. CSize size;
  273. size.cx = offset + rect.left;
  274. size.cy = 0;
  275. Scroll( size );
  276. rect.left -= size.cx;
  277.         }
  278. }
  279. BOOL CGridListCtrl::PreTranslateMessage(MSG* pMsg) 
  280.     {
  281.     if(pMsg->message == WM_KEYDOWN)
  282.         {
  283.         // Handle the keystrokes for the left and right keys
  284.         // to move the cell selection left and right.
  285.         // Handle F2 to commence edit mode from the keyboard.
  286.         // Only handle these if the grid control has the focus.
  287.         // (Messages also come through here for the edit control
  288.         // and we don't want them.
  289.         if( this == GetFocus() )
  290.             {
  291.             switch( pMsg->wParam )
  292.                 {
  293.                 case VK_LEFT:
  294.                     {
  295.                     // Decrement the order number.
  296.                     m_CurSubItem--;
  297.                     if( m_CurSubItem < -1 ) 
  298.                         {
  299.                         // This indicates that the whole row is selected and F2 means nothing.
  300.                         m_CurSubItem = -1;
  301.                         }
  302.                     else
  303.                         {
  304.                         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
  305.                         // Make the column visible.
  306.                         // We have to take into account that the header
  307.                         // may be reordered.
  308.                         MakeColumnVisible( Header_OrderToIndex( pHeader->m_hWnd, m_CurSubItem ) );
  309.                         // Invalidate the item.
  310.                         int iItem = GetNextItem( -1, LVNI_FOCUSED );
  311.                         if( iItem != -1 )
  312.                             {
  313.                             CRect rcBounds;
  314.                             GetItemRect(iItem, rcBounds, LVIR_BOUNDS);
  315.                             InvalidateRect( &rcBounds );
  316.                             }
  317.                         }
  318.                     }
  319.                     return TRUE;
  320.                 case VK_RIGHT:
  321.                     {
  322.                     // Increment the order number.
  323.                     m_CurSubItem++;
  324.                     CHeaderCtrl* pHeader = (CHeaderCtrl*) GetDlgItem(0);
  325.                     int nColumnCount = pHeader->GetItemCount();
  326.                     // Don't go beyond the last column.
  327.                     if( m_CurSubItem > nColumnCount -1 ) 
  328.                         {
  329.                         m_CurSubItem = nColumnCount-1;
  330.                         }
  331.                     else
  332.                         {
  333.                         // We have to take into account that the header
  334.                         // may be reordered.
  335.                         MakeColumnVisible( Header_OrderToIndex( pHeader->m_hWnd, m_CurSubItem ) );
  336.                         int iItem = GetNextItem( -1, LVNI_FOCUSED );
  337.                         // Invalidate the item.
  338.                         if( iItem != -1 )
  339.                             {
  340.                             CRect rcBounds;
  341.                             GetItemRect(iItem, rcBounds, LVIR_BOUNDS);
  342.                             InvalidateRect( &rcBounds );
  343.                             }
  344.                         }
  345.                     }
  346.                     return TRUE;
  347.                 case VK_F2: // Enter nondestructive edit mode.
  348.                     {
  349.                     int iItem = GetNextItem( -1, LVNI_FOCUSED );
  350.                     if( m_CurSubItem != -1 && iItem != -1 )
  351.                         {
  352.                         // Send Notification to parent of ListView ctrl
  353.                         CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
  354.                         CString str;
  355.                         // We have to take into account that the header
  356.                         // may be reordered.
  357.                         str = GetItemText( iItem, Header_OrderToIndex( pHeader->m_hWnd, m_CurSubItem ) );
  358.                         LV_DISPINFO dispinfo;
  359.                         dispinfo.hdr.hwndFrom = m_hWnd;
  360.                         dispinfo.hdr.idFrom = GetDlgCtrlID();
  361.                         dispinfo.hdr.code = LVN_BEGINLABELEDIT;
  362.                         
  363.                         dispinfo.item.mask = LVIF_TEXT;
  364.                         dispinfo.item.iItem = iItem;
  365.                         // We have to take into account that the header
  366.                         // may be reordered.
  367.                         dispinfo.item.iSubItem = Header_OrderToIndex( pHeader->m_hWnd, m_CurSubItem );
  368.                         dispinfo.item.pszText = (LPTSTR)((LPCTSTR)str);
  369.                         dispinfo.item.cchTextMax = str.GetLength();
  370.                         // Send message to the parent that we are ready to edit.
  371.                         GetParent()->SendMessage( WM_NOTIFY, GetDlgCtrlID(), 
  372.                             (LPARAM)&dispinfo );
  373.                         }
  374.                     }
  375.                     break;
  376.                 default:
  377.                     break;
  378.                 }
  379.             }
  380.         }
  381.     
  382.     return CListCtrl::PreTranslateMessage(pMsg);
  383.     }
  384. int CGridListCtrl::IndexToOrder( int iIndex )
  385. {
  386. // Since the control only provide the OrderToIndex macro,
  387. // we have to provide the IndexToOrder.  This translates
  388. // a column index value to a column order value.
  389. CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
  390. int colcount = pHeader->GetItemCount();
  391. int *orderarray = new int[ colcount ];
  392. Header_GetOrderArray( pHeader->m_hWnd, colcount, orderarray );
  393. int i;
  394. for( i=0; i<colcount; i++ )
  395. {
  396. if( orderarray[i] == iIndex )
  397. return i;
  398. }
  399. return -1;
  400. }
  401. void CGridListCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
  402. {
  403. if( GetFocus() != this ) SetFocus();
  404. CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
  405. }
  406. void CGridListCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
  407. {
  408. if( GetFocus() != this ) SetFocus();
  409. CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
  410. }