#include "stdafx.h" #include "SuperGridCtrl.h" #include "ListEditCtrl.h" #include "coretools.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #ifndef WM_ITEM_SEL #define WM_ITEM_SEL (WM_USER+0x105) #endif CSuperGridCtrl::CSuperGridCtrl() { m_cxImage = m_cyImage = m_bIsDragging = m_CurSubItem = 0; m_nDragTarget=m_nDragItem = -1; m_psTreeLine.CreatePen(PS_SOLID, 1, RGB(192,192,192)); m_psRectangle.CreatePen(PS_SOLID, 1, RGB(198,198,198)); m_psPlusMinus.CreatePen(PS_SOLID, 1, RGB(0,0,0)); m_brushErase.CreateSolidBrush(RGB(255,255,255)); m_himl = NULL; m_bDragStyle = FALSE; m_bEditStyle = FALSE; m_dwCellRGB = RGB(192, 192, 192); } CSuperGridCtrl::~CSuperGridCtrl() { m_psPlusMinus.DeleteObject(); m_psRectangle.DeleteObject(); m_psTreeLine.DeleteObject(); m_brushErase.DeleteObject(); while(m_RootItems.GetCount()) { CTreeItem * root = (CTreeItem*)m_RootItems.RemoveHead(); if(root!=NULL && GetData(root) != NULL) delete GetData(root); if (root) { delete root; root = NULL; } } m_RootItems.RemoveAll(); } BEGIN_MESSAGE_MAP(CSuperGridCtrl, CListCtrl) //{{AFX_MSG_MAP(CSuperGridCtrl) ON_NOTIFY_REFLECT_EX(NM_DBLCLK, OnDblclk) ON_NOTIFY_REFLECT_EX(LVN_ENDLABELEDIT, OnEndlabeledit) ON_WM_CREATE() ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_TIMER() ON_WM_MOUSEWHEEL() ON_WM_HSCROLL() ON_WM_VSCROLL() ON_WM_LBUTTONUP() ON_NOTIFY_REFLECT(LVN_KEYDOWN, OnKeydown) ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBegindrag) ON_WM_MEASUREITEM_REFLECT() ON_WM_DRAWITEM_REFLECT() ON_WM_SYSCOLORCHANGE() //}}AFX_MSG_MAP END_MESSAGE_MAP() BOOL CSuperGridCtrl::PreCreateWindow(CREATESTRUCT& cs) { cs.style |= LVS_REPORT | LVS_SINGLESEL | LVS_SHAREIMAGELISTS | LVS_OWNERDRAWFIXED | LVS_SHOWSELALWAYS; return CListCtrl::PreCreateWindow(cs); } int CSuperGridCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CListCtrl::OnCreate(lpCreateStruct) == -1) return -1; return 0; } #define OFFSET_FIRST 2 * dScale #define OFFSET_OTHER 6 * dScale void CSuperGridCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { if (lpDrawItemStruct->CtlType != ODT_LISTVIEW) return; double dScale = CoreTools::GetDPIScale(); if(lpDrawItemStruct->itemAction == ODA_DRAWENTIRE) { if(m_himl==NULL) { m_himl = (HIMAGELIST)::SendMessage(m_hWnd, LVM_GETIMAGELIST, (WPARAM)(int)(LVSIL_SMALL), 0L); if(m_himl==NULL) return; } LV_ITEM lvi; static _TCHAR szBuff[MAX_PATH]; LPCTSTR pszText; int nItem = lpDrawItemStruct->itemID; CRect rcItem(lpDrawItemStruct->rcItem); lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT | LVIF_PARAM; lvi.iItem = nItem; lvi.iSubItem =0; lvi.pszText = szBuff; lvi.cchTextMax = sizeof(szBuff); lvi.stateMask = 0xFFFF; GetItem(&lvi); CTreeItem *pSelItem = (CTreeItem*)lpDrawItemStruct->itemData; CRect rcLabel; GetItemRect(nItem, rcLabel, LVIR_LABEL); CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); ASSERT(pDC); CRect rcClipBox; pDC->GetClipBox(rcClipBox); COLORREF crBackground, crText; if (lpDrawItemStruct->itemState & ODS_SELECTED) { // Set the text background and foreground colors crBackground = GetSysColor (COLOR_HIGHLIGHT); crText = GetSysColor (COLOR_HIGHLIGHTTEXT); } else { // Set the text background and foreground colors to the standard window // colors crBackground = GetSysColor (COLOR_WINDOW); crText = GetSysColor (COLOR_WINDOWTEXT); } //Get current Icon, you have overridden this I hope :-) int iImage = GetIcon(pSelItem); if(iImage!=-1) { if(lvi.iImage!=iImage) { LV_ITEM lvItem; lvItem.mask = LVIF_IMAGE; lvItem.iImage = iImage; lvItem.iItem = nItem; lvItem.iSubItem = 0; SetItem(&lvItem); lvi.iImage = iImage; } } CRect rc; GetItemRect(nItem, rc, LVIR_BOUNDS); CRect rcIcon; GetItemRect(nItem, rcIcon, LVIR_ICON); //Draw Current image int nOffset = (rcItem.Height() - m_cyImage)/2; int nY = rcItem.bottom - m_cyImage - nOffset; int nWidth = m_cxImage;//def icon size //do not draw icon out side 1.st column. int iTryIndent = GetIndent(pSelItem) * m_cxImage + m_cxImage; if(rc.left + iTryIndent > GetColumnWidth(0)-2/*looks better -2*/) nWidth = (rc.left + iTryIndent) - GetColumnWidth(0); UINT uiFlags = ILD_TRANSPARENT; if( GetItemState(nItem, LVIF_STATE) & LVIS_DROPHILITED)//if dragging show a SelectDropTarget alike effect :) uiFlags |= ILD_BLEND50; int iconLeft(rc.left + (GetIndent(pSelItem) * m_cxImage)); int iconRight(iconLeft + m_cxImage); if((nWidth=m_cxImage-nWidth) >-1)//calc width of icon { CoreTools::drawIcon(m_himl, lvi.iImage, pDC->m_hDC, CPoint(iconLeft, nY), 0); DrawTreeItem(pDC, pSelItem, nItem, rc); } //Draw selection bar (erase old selection too) pDC->SetBkColor(crBackground); CRect rcClip = lpDrawItemStruct->rcItem; rcClip.left += GetIndent(pSelItem) * m_cxImage + m_cxImage + 2; if(rcClip.left > GetColumnWidth(0)) rcClip.left = GetColumnWidth(0); //fill background color ExtTextOut(pDC->GetSafeHdc(), 0, 0, ETO_OPAQUE, rcClip, NULL, 0, NULL); //draw color in first col if any rcClip.right = rcLabel.right; CItemInfo *pItemInfo = GetData(pSelItem); COLORREF clf = pItemInfo->GetItemClr(); if(clf!=-1) { CBrush br(clf); pDC->FillRect(rcClip, &br); } //draw selection rect in 1.st col if selected if ((lpDrawItemStruct->itemState & ODS_SELECTED) && (m_CurSubItem==0)) { CBrush br(GetCellRGB()); pDC->FillRect(rcClip,&br); pDC->DrawFocusRect(rcClip); } //if checkbox style bool bHasCheckBox(false); UINT nStateImageMask = lvi.state & LVIS_STATEIMAGEMASK; if (nStateImageMask) { int nImage = (nStateImageMask>>12) - 1; CImageList *pImageList = GetImageList(LVSIL_STATE); if (pImageList) { int cxIcon,cyIcon=0; ImageList_GetIconSize(pImageList->m_hImageList, &cxIcon, &cyIcon); cxIcon *= dScale; cyIcon *= dScale; CPoint ptIcon(rc.left + (GetIndent(pSelItem) * m_cxImage) + cxIcon, nY); ptIcon.x += cxIcon * .1; if (rc.left + (GetIndent(pSelItem) * m_cxImage) + m_cxImage + cxIcon < GetColumnWidth(0)) { CoreTools::drawIcon(pImageList->GetSafeHandle(), nImage, pDC->GetSafeHdc(), ptIcon, 0); bHasCheckBox = true; } } } //draw 1. item LV_COLUMN lvc; lvc.mask = LVCF_FMT | LVCF_WIDTH; GetColumn(0, &lvc); GetItemRect(nItem, rcItem, LVIR_LABEL); pszText = MakeShortString(pDC, szBuff, lvc.cx - iconRight, 2*OFFSET_FIRST); rcLabel = rcItem; rcLabel.left = iconRight + OFFSET_FIRST; if (bHasCheckBox) rcLabel.left += m_cxImage; rcLabel.right += rcLabel.left; pDC->SetBkColor (crBackground); pDC->SetTextColor (crText); pDC->DrawText(pszText,-1, rcLabel,DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_EXTERNALLEADING); //draw subitems.. for(int nColumn=1; GetColumn(nColumn, &lvc); nColumn++) { rcItem.left=rcItem.right; rcItem.right+=lvc.cx; if (rcItem.left < rcClipBox.right && rcItem.right > rcClipBox.left && rcItem.right > rcItem.left) { //support for colors in each cell COLORREF clf = pItemInfo->GetBkColor(nColumn-1); if(clf!=-1) { CBrush br(clf); pDC->FillRect(rcItem, &br); } int nRetLen = GetItemText(nItem, nColumn, szBuff, sizeof(szBuff)); if(nRetLen==0) pszText=NULL; else pszText=MakeShortString(pDC,szBuff,rcItem.right-rcItem.left,2*OFFSET_OTHER); UINT nJustify=DT_LEFT; if(pszText==szBuff) { switch(lvc.fmt & LVCFMT_JUSTIFYMASK) { case LVCFMT_RIGHT: nJustify=DT_RIGHT; break; case LVCFMT_CENTER: nJustify=DT_CENTER; break; default: break; } } rcLabel=rcItem; rcLabel.left+=OFFSET_OTHER; rcLabel.right-=OFFSET_OTHER; if (lpDrawItemStruct->itemState & ODS_SELECTED && !m_bIsDragging) DrawFocusCell(pDC, lpDrawItemStruct->itemID, nColumn); if(pszText!=NULL) pDC->DrawText(pszText,-1,rcLabel, nJustify | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_EXTERNALLEADING); }//if //draw down arrow if combobox regardless of rcClipBox rgn if (lpDrawItemStruct->itemState & ODS_SELECTED) DrawComboBox(pDC, pSelItem, nItem, nColumn); }//for }//ODA_DRAWENTIRE } void CSuperGridCtrl::DrawComboBox(CDC* pDC, CTreeItem *pSelItem, int nItem, int nColumn) { CItemInfo* pInfo = GetData(pSelItem); CItemInfo::CONTROLTYPE ctrlType; if(pInfo->GetControlType(nColumn-1, ctrlType)) { if(ctrlType == CItemInfo::combobox) { CRect rect; GetSubItemRect(nItem, nColumn, LVIR_BOUNDS, rect); rect.left=rect.right - GetSystemMetrics(SM_CYVSCROLL); pDC->DrawFrameControl(rect, DFC_SCROLL, DFCS_SCROLLDOWN); } } } //this piece of code is borrowed from the wndproc.c file in the odlistvw.exe example from MSDN and was converted to mfc-style void CSuperGridCtrl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { if (lpMeasureItemStruct->CtlType != ODT_LISTVIEW) return; TEXTMETRIC tm; CClientDC dc(this); CFont* pFont = GetFont(); CFont* pOldFont = dc.SelectObject(pFont); dc.GetTextMetrics(&tm); int nItemHeight = tm.tmHeight + tm.tmExternalLeading; lpMeasureItemStruct->itemHeight = nItemHeight + 4; //or should I go for max(nItemheight+4, m_cxImage+2); dc.SelectObject(pOldFont); } //the basic rutine making the ... thing snatched it from some tedious code example some where in MSDN call odlistvw.exe LPCTSTR CSuperGridCtrl::MakeShortString(CDC* pDC, LPCTSTR lpszLong, int nColumnLen, int nOffset) { static const _TCHAR szThreeDots[]=_T("..."); int nStringLen=lstrlen(lpszLong); if(nStringLen==0 || CoreTools::getStringSZ(pDC->GetSafeHdc(), lpszLong).cx + nOffset < nColumnLen) return(lpszLong); static _TCHAR szShort[MAX_PATH]; lstrcpy(szShort,lpszLong); int nAddLen = CoreTools::getStringSZ(pDC->GetSafeHdc(), szThreeDots).cx; for(int i=nStringLen-1; i > 0; i--) { szShort[i]=0; if(CoreTools::getStringSZ(pDC->GetSafeHdc(), szShort).cx + nOffset + nAddLen < nColumnLen) break; } lstrcat(szShort,szThreeDots); return(szShort); } void CSuperGridCtrl::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult) { LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR; switch(pLVKeyDow->wVKey) { case VK_SPACE: { if(GetExtendedStyle() & LVS_EX_CHECKBOXES) { int nIndex = GetSelectedItem(); if(nIndex !=-1) { CTreeItem* pItem = GetTreeItem(nIndex); if(pItem!=NULL) { CItemInfo *pInfo = GetData(pItem); pInfo->SetCheck(!pInfo->GetCheck()); } } } }break; case VK_DELETE: { /*int nItem = GetSelectedItem(); if(nItem!=-1) { CTreeItem* pSelItem = GetTreeItem(nItem); if(pSelItem != NULL) { if(OnDeleteItem(pSelItem, nItem)) DeleteItemEx(pSelItem, nItem); } }*/ } break; case VK_MULTIPLY: { int nIndex = GetSelectedItem(); if(nIndex != -1) { CWaitCursor wait; SetRedraw(0); CTreeItem *pParent = GetTreeItem(nIndex); int nScroll=0; if(OnVKMultiply(pParent, nIndex)) { ExpandAll(pParent, nScroll); } SetRedraw(1); RedrawItems(nIndex, nScroll); EnsureVisible(nScroll, TRUE); } }break; case VK_ADD: { int nIndex = GetSelectedItem(); if(nIndex!=-1) { CWaitCursor wait; CTreeItem *pSelItem = GetTreeItem(nIndex); int nScrollIndex = 0; if(OnVKAdd(pSelItem, nIndex)) { nScrollIndex = Expand(pSelItem, nIndex); } CRect rc; GetItemRect(nIndex, rc, LVIR_BOUNDS); InvalidateRect(rc); UpdateWindow(); EnsureVisible(nScrollIndex, 1); } }break; case VK_SUBTRACT: { int nIndex = GetSelectedItem(); if(nIndex!=-1) { CWaitCursor wait; CTreeItem *pSelItem = GetTreeItem(nIndex); if(OnVkSubTract(pSelItem, nIndex)) { Collapse(pSelItem); } CRect rc; GetItemRect(nIndex, rc, LVIR_BOUNDS); InvalidateRect(rc); UpdateWindow(); } }break; case VK_UP: { int nIndex = GetSelectedItem(); if(nIndex!=-1) { CWnd* pParent = GetParent(); if (pParent != NULL) { CPoint* pPt = new CPoint(nIndex,0); pParent->PostMessage(WM_ITEM_SEL,(WPARAM)m_hWnd,(LPARAM)pPt); } } break; } case VK_DOWN: { int nIndex = GetSelectedItem(); if(nIndex!=-1) { CWnd* pParent = GetParent(); if (pParent != NULL) { CPoint* pPt = new CPoint(nIndex,0); pParent->PostMessage(WM_ITEM_SEL,(WPARAM)m_hWnd,(LPARAM)pPt); } } break; } default :break; } *pResult = 0; } BOOL CSuperGridCtrl::HitTestOnSign(CPoint point, LVHITTESTINFO& ht) { ht.pt = point; // Test which subitem was clicked. SubItemHitTest(&ht); if(ht.iItem!=-1) { //first hittest on checkbox BOOL bHit = FALSE; if(GetExtendedStyle() & LVS_EX_CHECKBOXES) { if (ht.flags == LVHT_ONITEM && (GetStyle() & LVS_OWNERDRAWFIXED))//isn't this allways ownerdrawfixed :-) { CRect rcIcon,rcLabel; GetItemRect(ht.iItem, rcIcon, LVIR_ICON);//has to be between these two ....right :) GetItemRect(ht.iItem, rcLabel, LVIR_LABEL); bHit = PtHitCheckBox(point); //CTreeItem *pItem = GetTreeItem(ht.iItem); //int left = (GetIndent(pItem) * m_cxImage) + m_cxImage; //int right = left + m_cxImage; //if (point.x >= left && point.x <= right) // bHit = TRUE; //else // bHit = FALSE; } else if (ht.flags & LVHT_ONITEMSTATEICON) bHit = TRUE; } CTreeItem* pItem = GetTreeItem(ht.iItem); if(pItem!=NULL) { if(bHit)//if checkbox { //yes I know..have to maintain to sets of checkstates here... //one for listview statemask and one for CTreeItem..but its located here so no harm done SetCheck(ht.iItem,!GetCheck(ht.iItem)); CItemInfo *pInfo = GetData(pItem); pInfo->SetCheck(!pInfo->GetCheck()); } //if haschildren and clicked on + or - then expand/collapse if(ItemHasChildren(pItem)) { //hittest on the plus/sign "button" CRect rcBounds; GetItemRect(ht.iItem, rcBounds, LVIR_BOUNDS); CRectangle rect(this, NULL, GetIndent(pItem), rcBounds); BOOL bRedraw=0;//if OnItemExpanding or OnCollapsing returns false, dont redraw if(rect.HitTest(point)) { SetRedraw(0); int nScrollIndex=0; if(IsCollapsed(pItem)) { if(OnItemExpanding(pItem, ht.iItem)) { nScrollIndex = Expand(pItem, ht.iItem); OnItemExpanded(pItem, ht.iItem); bRedraw=1; } } else { if(OnCollapsing(pItem)) { Collapse(pItem); OnItemCollapsed(pItem); bRedraw=1; } } SetRedraw(1); if(bRedraw) { CRect rc; GetItemRect(ht.iItem, rc, LVIR_BOUNDS); InvalidateRect(rc); UpdateWindow(); EnsureVisible(nScrollIndex, 1); return 0; } } }//has kids }//pItem!=NULL } return 1; } BOOL CSuperGridCtrl::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; if( GetFocus() != this) SetFocus(); // 如果是在checkbox的sytle下,双击checkbox时,不处理 if (PtHitCheckBox(pNMListView->ptAction)) { return TRUE; } LVHITTESTINFO ht; ht.pt = pNMListView->ptAction; SubItemHitTest(&ht); if(OnItemLButtonDown(ht)) { BOOL bSelect=1; bSelect = HitTestOnSign(pNMListView->ptAction, ht); //normal selection if(bSelect && ht.iItem !=-1) { int nIndex = GetSelectedItem(); if(nIndex!=-1) { CTreeItem *pSelItem = GetTreeItem(nIndex); if (pSelItem != NULL) { BOOL bRedraw=0; if(ItemHasChildren(pSelItem)) { SetRedraw(0); int nScrollIndex=0; if(IsCollapsed(pSelItem)) { if(OnItemExpanding(pSelItem, nIndex)) { nScrollIndex = Expand(pSelItem, nIndex); OnItemExpanded(pSelItem, nIndex); bRedraw=1; } } else { if(OnCollapsing(pSelItem)) { Collapse(pSelItem); OnItemCollapsed(pSelItem); bRedraw=1; } } SetRedraw(1); if(bRedraw) { CRect rc; GetItemRect(nIndex,rc,LVIR_BOUNDS); InvalidateRect(rc); UpdateWindow(); EnsureVisible(nScrollIndex,1); } }//ItemHasChildren }//!=NULL } } } *pResult = 1; return TRUE; } void CSuperGridCtrl::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) { if(m_bDragStyle == FALSE) return; NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; if(pNMListView->iItem != -1) { m_nDragItem = pNMListView->iItem; CImageList* pDragImage=NULL; pDragImage = CreateDragImageEx(m_nDragItem);//override this if you want another dragimage or none at all. if(pDragImage) { pDragImage->BeginDrag(0, CPoint(0,0)); pDragImage->DragEnter(this, pNMListView->ptAction); SetCapture(); m_bIsDragging = TRUE; } DELETE_PTR(pDragImage); } *pResult = 0; } //Create dragimage : Icon + the itemtext CImageList *CSuperGridCtrl::CreateDragImageEx(int nItem) { CImageList *pList = new CImageList; //get image index LV_ITEM lvItem; lvItem.mask = LVIF_IMAGE; lvItem.iItem = nItem; lvItem.iSubItem = 0; GetItem(&lvItem); CRect rc; GetItemRect(nItem, &rc, LVIR_BOUNDS); CString str; str = GetItemText(nItem, 0); CFont *pFont = GetFont(); rc.OffsetRect(-rc.left, -rc.top); rc.right = GetColumnWidth(0); pList->Create(rc.Width(), rc.Height(),ILC_COLOR24| ILC_MASK , 1, 1); CDC *pDC = GetDC(); if(pDC) { CDC dc; dc.CreateCompatibleDC(pDC); CBitmap bmpMap; bmpMap.CreateCompatibleBitmap(pDC, rc.Width(), rc.Height()); CBitmap *pOldBmp = dc.SelectObject(&bmpMap); CFont *pOldFont = dc.SelectObject(pFont); dc.FillSolidRect(rc, GetSysColor(COLOR_WINDOW)); CImageList *pImgList = GetImageList(LVSIL_SMALL); if(pImgList) pImgList->Draw(&dc, lvItem.iImage, CPoint(0,0), ILD_TRANSPARENT); dc.TextOut(m_cxImage + 4, 0, str); dc.SelectObject(pOldFont); dc.SelectObject(pOldBmp); //causes an error if the 1st column is hidden so must check the imagelist if(pList->m_hImageList != NULL) pList->Add(&bmpMap, RGB(255,255,255)); else { DELETE_PTR(pList); } ReleaseDC(pDC); } return pList; } void CSuperGridCtrl::OnMouseMove(UINT nFlags, CPoint point) { if(m_bIsDragging) { KillTimer(1); if (CWnd::GetCapture() != this) m_bIsDragging=0; if(nFlags==MK_RBUTTON || nFlags==MK_MBUTTON) m_bIsDragging=0; if(GetKeyState(VK_ESCAPE) < 0) m_bIsDragging=0; if(!m_bIsDragging)//why not put this in a funtion :) { SetItemState (m_nDragTarget, 0, LVIS_DROPHILITED); CImageList::DragLeave(this); CImageList::EndDrag(); ReleaseCapture(); InvalidateRect(NULL); UpdateWindow(); } else { CPoint ptList(point); MapWindowPoints(this, &ptList, 1); CImageList::DragMove(ptList); UINT uHitTest = LVHT_ONITEM; m_nDragTarget = HitTest(ptList, &uHitTest); // try turn off hilight for previous DROPHILITED state int nPrev = GetNextItem(-1,LVNI_DROPHILITED); if(nPrev != m_nDragTarget)//prevents flicker SetItemState(nPrev, 0, LVIS_DROPHILITED); CRect rect; GetClientRect (rect); int cy = rect.Height(); if(m_nDragTarget!=-1) { SetItemState(m_nDragTarget, LVIS_DROPHILITED, LVIS_DROPHILITED); CTreeItem* pTarget = GetTreeItem(m_nDragTarget); if ((point.y >= 0 && point.y <= m_cyImage) || (point.y >= cy - m_cyImage && point.y <= cy) || ( pTarget!=NULL && ItemHasChildren(pTarget) && IsCollapsed(pTarget))) { SetTimer(1,300,NULL); } } } } CListCtrl::OnMouseMove(nFlags, point); } void CSuperGridCtrl::OnTimer(UINT_PTR nIDEvent) { CListCtrl::OnTimer(nIDEvent); if(nIDEvent==1) { if(CWnd::GetCapture()!=this) { SetItemState(m_nDragTarget, 0, LVIS_DROPHILITED); m_bIsDragging=0; CImageList::DragLeave(this); CImageList::EndDrag(); ReleaseCapture(); InvalidateRect(NULL); UpdateWindow(); KillTimer(1); return; } SetTimer(1,300,NULL);//reset timer DWORD dwPos = ::GetMessagePos(); CPoint ptList(LOWORD(dwPos),HIWORD(dwPos)); ScreenToClient(&ptList); CRect rect; GetClientRect(rect); int cy = rect.Height(); // // perform autoscroll if the cursor is near the top or bottom. // if (ptList.y >= 0 && ptList.y <= m_cyImage) { int n = GetTopIndex(); CImageList::DragShowNolock(0); SendMessage(WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), NULL); CImageList::DragShowNolock(1); if (GetTopIndex()== n) KillTimer (1); else { CImageList::DragShowNolock(0); CImageList::DragMove(ptList); CImageList::DragShowNolock(1); return; } } else if (ptList.y >= cy - m_cyImage && ptList.y <= cy) { int n = GetTopIndex(); CImageList::DragShowNolock(0); SendMessage(WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), NULL); CImageList::DragShowNolock(1); if (GetTopIndex()== n) KillTimer (1); else { CImageList::DragShowNolock(0); CImageList::DragMove(ptList); CImageList::DragShowNolock(1); return; } } //Hover test CImageList::DragMove(ptList); UINT uHitTest = LVHT_ONITEM; m_nDragTarget = HitTest(ptList, &uHitTest); if(m_nDragTarget!=-1) { //if the target has children //expand them CTreeItem* pTarget=GetTreeItem(m_nDragTarget); if(pTarget != NULL && ItemHasChildren(pTarget) && IsCollapsed(pTarget) && (m_nDragItem!=-1)) { CImageList::DragShowNolock(0); CTreeItem* pSource = GetTreeItem(m_nDragItem); SetRedraw(0); int nScrollIndex=0; if(ItemHasChildren(pTarget) && IsCollapsed(pTarget)) { if(OnItemExpanding(pTarget, m_nDragTarget)) { nScrollIndex = Expand(pTarget, m_nDragTarget); OnItemExpanded(pTarget, m_nDragTarget); } } m_nDragItem = NodeToIndex(pSource); SetRedraw(1); EnsureVisible(nScrollIndex, 1); InvalidateRect(NULL); UpdateWindow(); CImageList::DragShowNolock(1); KillTimer(1); return; } } KillTimer(1); } } void CSuperGridCtrl::OnLButtonUp(UINT nFlags, CPoint point) { if(m_bIsDragging == TRUE) { KillTimer(1); CImageList::DragLeave(this); CImageList::EndDrag(); ReleaseCapture(); m_bIsDragging = FALSE; SetItemState(m_nDragTarget, 0, LVIS_DROPHILITED); if((m_nDragTarget != -1) && (m_nDragTarget != m_nDragItem) && (m_nDragItem!=-1))//no drop on me self { CTreeItem* pSource = GetTreeItem(m_nDragItem); CTreeItem* pTarget = GetTreeItem(m_nDragTarget); if(IsRoot(pSource)) return; CTreeItem* pParent = GetParentItem(pSource); if(pParent==pTarget) //can't drag child to parent return; if(!IsChildOf(pSource, pTarget))//can't drag parent to child { CWaitCursor wait; SetRedraw(0); if(DoDragDrop(pTarget, pSource)) { UINT uflag = LVIS_SELECTED | LVIS_FOCUSED; SetItemState(m_nDragTarget, uflag, uflag); m_nDragItem=-1; //delete source int nIndex = NodeToIndex(pSource); DeleteItem(nIndex); HideChildren(pSource, TRUE, nIndex); Delete(pSource); InternaleUpdateTree(); SetRedraw(1); InvalidateRect(NULL); UpdateWindow(); }else SetRedraw(1); } } } else CListCtrl::OnLButtonUp(nFlags, point); } //used with the drag/drop operation void CSuperGridCtrl::CopyChildren(CTreeItem* pDest, CTreeItem* pSrc) { if (ItemHasChildren(pSrc)) { POSITION pos = pSrc->m_listChild.GetHeadPosition(); while (pos != NULL) { CTreeItem* pItem = (CTreeItem *)pSrc->m_listChild.GetNext(pos); CItemInfo* lp = CopyData(GetData(pItem)); CTreeItem* pNewItem = InsertItem(pDest, lp); CopyChildren(pNewItem, pItem); } } } //hmmm BOOL CSuperGridCtrl::DoDragDrop(CTreeItem* pTarget, CTreeItem* pSource) { if(pTarget==NULL) return 0; BOOL bUpdate=FALSE; if(!IsCollapsed(pTarget)) bUpdate=TRUE; //children are expanded, want to see update right away //make a copy of the source data CItemInfo* lp = CopyData(GetData(pSource)); //create new node with the source data and make pTarget the parent CTreeItem* pNewParent = InsertItem(pTarget, lp, bUpdate); //if the source has children copy all source data and make the newly create item the parent if(ItemHasChildren(pSource)) CopyChildren(pNewParent, pSource); return 1; } void CSuperGridCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { //its not meself if( GetFocus() != this) SetFocus(); CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar); } void CSuperGridCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if( GetFocus() != this) SetFocus(); CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar); } BOOL CSuperGridCtrl::OnMouseWheel( UINT nFlags, short zDelta, CPoint pt ) { if( GetFocus() != this) SetFocus(); return CListCtrl::OnMouseWheel(nFlags, zDelta, pt); } BOOL CSuperGridCtrl::PreTranslateMessage(MSG* pMsg) { if(pMsg->message == WM_KEYDOWN) { if(GetFocus()==this) { switch( pMsg->wParam ) { case VK_LEFT: { // Decrement the order number. m_CurSubItem--; if(m_CurSubItem < 0) m_CurSubItem = 0; else{ CHeaderCtrl* pHeader = GetHeaderCtrl(); // Make the column visible. // We have to take into account that the header may be reordered. MakeColumnVisible( Header_OrderToIndex( pHeader->m_hWnd, m_CurSubItem)); // Invalidate the item. int iItem = GetSelectedItem(); if( iItem != -1 ) { CRect rcBounds; GetItemRect(iItem, rcBounds, LVIR_BOUNDS); InvalidateRect(&rcBounds); UpdateWindow(); } } } return TRUE; case VK_RIGHT: { // Increment the order number. m_CurSubItem++; CHeaderCtrl* pHeader = GetHeaderCtrl(); int nColumnCount = pHeader->GetItemCount(); // Don't go beyond the last column. if( m_CurSubItem > nColumnCount -1 ) m_CurSubItem = nColumnCount-1; else { MakeColumnVisible(Header_OrderToIndex( pHeader->m_hWnd, m_CurSubItem)); int iItem = GetSelectedItem(); // Invalidate the item. if( iItem != -1 ) { CRect rcBounds; GetItemRect(iItem, rcBounds, LVIR_BOUNDS); InvalidateRect(&rcBounds); UpdateWindow(); } } } return TRUE; case VK_RETURN://edit itemdata { if(m_bEditStyle) { BOOL bResult = OnVkReturn(); if(!bResult) { int iItem = GetSelectedItem(); if( m_CurSubItem != -1 && iItem != -1) { CHeaderCtrl* pHeader = GetHeaderCtrl(); int iSubItem = Header_OrderToIndex(pHeader->m_hWnd, m_CurSubItem); if(iSubItem==0)//that's just me saying all nodes in col 0 are edit-controls, you may modify this { CRect rcItem; GetItemRect(iItem, rcItem, LVIR_LABEL); DWORD dwStyle = WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL|ES_LEFT; CEdit *pEdit = new CListEditCtrl(iItem, iSubItem, GetItemText(iItem, iSubItem)); pEdit->Create(dwStyle, rcItem, this, 0x1233); } else EditLabelEx(iItem, iSubItem); return 1; } } } } break; default: break; } } } return CListCtrl::PreTranslateMessage(pMsg); } #define IDC_EDITCTRL 0x1234 CEdit* CSuperGridCtrl::EditLabelEx(int nItem, int nCol) { CRect rect; int offset = 0; if(!EnsureVisible(nItem, TRUE)) return NULL; GetSubItemRect(nItem, nCol, LVIR_BOUNDS, rect); // Now scroll if we need to expose the column CRect rcClient; GetClientRect(rcClient); if( offset + rect.left < 0 || offset + rect.left > rcClient.right ) { CSize size(offset + rect.left,0); Scroll(size); rect.left -= size.cx; } rect.left += offset; rect.right = rect.left + GetColumnWidth(nCol); if(rect.right > rcClient.right) rect.right = rcClient.right; // Get Column alignment LV_COLUMN lvcol; lvcol.mask = LVCF_FMT; GetColumn(nCol, &lvcol); DWORD dwStyle; if((lvcol.fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT) dwStyle = ES_LEFT; else if((lvcol.fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT) dwStyle = ES_RIGHT; else dwStyle = ES_CENTER; dwStyle |=WS_BORDER|WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL; CEdit *pEdit = new CListEditCtrl(nItem, nCol, GetItemText(nItem, nCol)); pEdit->Create(dwStyle, rect, this, IDC_EDITCTRL); //pEdit->ModifyStyleEx(0,WS_EX_CLIENTEDGE); //funny thing happend here, uncomment this, //and then edit an item, //enter a long text so that the ES_AUTOHSCROLL comes to rescue //yes that's look funny, ???. return pEdit; } BOOL CSuperGridCtrl::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO *plvDispInfo = (LV_DISPINFO*)pNMHDR; LV_ITEM *plvItem = &plvDispInfo->item; if (plvItem->pszText != NULL)//valid text { if(plvItem->iItem != -1) //valid item { CTreeItem*pSelItem = GetTreeItem(plvItem->iItem); if(pSelItem != NULL) { OnUpdateListViewItem(pSelItem, plvItem); } } } *pResult = 1; return TRUE; } int CSuperGridCtrl::GetNumCol() { CHeaderCtrl* pHeader = GetHeaderCtrl(); return pHeader ? pHeader->GetItemCount() : 0; } //Think Rex Myer is spooking here void CSuperGridCtrl::MakeColumnVisible(int nCol) { if(nCol < 0) return; // Get the order array to total the column offset. CHeaderCtrl* pHeader = GetHeaderCtrl(); int nColCount = pHeader->GetItemCount(); ASSERT( nCol < nColCount); int *pOrderarray = new int[nColCount]; Header_GetOrderArray(pHeader->m_hWnd, nColCount, pOrderarray); // Get the column offset int offset = 0; for(int i = 0; pOrderarray[i] != nCol; i++) offset += GetColumnWidth(pOrderarray[i]); int colwidth = GetColumnWidth(nCol); delete[] pOrderarray; CRect rect; GetItemRect(0, &rect, LVIR_BOUNDS); // Now scroll if we need to show the column CRect rcClient; GetClientRect(&rcClient); if(offset + rect.left < 0 || offset + colwidth + rect.left > rcClient.right) { CSize size(offset + rect.left,0); Scroll(size); InvalidateRect(NULL); UpdateWindow(); } } //Think Rex Myer is spooking here int CSuperGridCtrl::IndexToOrder( int iIndex ) { // This translates a column index value to a column order value. CHeaderCtrl* pHeader = GetHeaderCtrl(); int nColCount = pHeader->GetItemCount(); int *pOrderarray = new int[nColCount]; Header_GetOrderArray(pHeader->m_hWnd, nColCount, pOrderarray); for(int i=0; iFillRect(rect, &br); pDC->DrawFocusRect(rect); } } //insert item and return new parent pointer. /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::InsertItem(CTreeItem *pParent, CItemInfo* lpInfo, BOOL bUpdate) { if(pParent==NULL) return NULL; CTreeItem *pItem = NULL; pItem = new CTreeItem(); if(lpInfo==NULL) lpInfo = new CItemInfo; UpdateData(pItem, lpInfo); SetIndent(pItem, GetIndent(pParent)+1); SetParentItem(pItem, pParent); //add as the last child pParent->m_listChild.AddTail(pItem); if(!bUpdate) Hide(pParent, TRUE); else { //calc listview index for the new node int nIndex = NodeToIndex(pItem); CString str = GetData(pItem)->GetItemText(); LV_ITEM lvItem; lvItem.mask = LVIF_TEXT | LVIF_INDENT | LVIF_PARAM; lvItem.pszText = str.GetBuffer(1); //insert item lvItem.iItem = nIndex; lvItem.iSubItem = 0; lvItem.lParam = (LPARAM)pItem; lvItem.iIndent = GetIndent(pItem); CListCtrl::InsertItem(&lvItem); if(lpInfo->GetCheck()) SetCheck(nIndex); //Get subitems int nSize = GetData(pItem)->GetItemCount(); for(int i=0; i < nSize;i++) { CString str = GetData(pItem)->GetSubItem(i); lvItem.mask = LVIF_TEXT; lvItem.iSubItem = i+1; lvItem.pszText = str.GetBuffer(1); SetItem(&lvItem); } InternaleUpdateTree();//better do this } return pItem; } void CSuperGridCtrl::InternaleUpdateTree() { int nItems = GetItemCount(); for(int nItem=0; nItem < nItems; nItem++) { CTreeItem* pItem = GetTreeItem(nItem); SetCurIndex(pItem, nItem); } } int CSuperGridCtrl::NodeToIndex(CTreeItem *pNode) { int nStartIndex=0; POSITION pos = m_RootItems.GetHeadPosition(); while(pos!=NULL) { CTreeItem * root = (CTreeItem*)m_RootItems.GetNext(pos); int ret = _NodeToIndex(root, pNode, nStartIndex); if(ret != -1) return ret; } return -1; } /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::GetRootItem(int nIndex) { POSITION pos = m_RootItems.FindIndex(nIndex); if(pos==NULL) return NULL; return (CTreeItem*)m_RootItems.GetAt(pos); } int CSuperGridCtrl::GetRootIndex(CTreeItem * root) { int nIndex = 0; POSITION pos = m_RootItems.GetHeadPosition(); while(pos != NULL) { CTreeItem * pItem = (CTreeItem*)m_RootItems.GetNext(pos); if(pItem== root) return nIndex; nIndex++; } return -1; } BOOL CSuperGridCtrl::IsRoot(CTreeItem * lpItem) { return m_RootItems.Find(lpItem) != NULL; } void CSuperGridCtrl::DeleteRootItem(CTreeItem * root) { POSITION pos = m_RootItems.Find(root); if(pos!=NULL) { CTreeItem* pRoot=(CTreeItem*)m_RootItems.GetAt(pos); if(pRoot->m_lpNodeInfo!=NULL) DELETE_PTR(pRoot->m_lpNodeInfo); if (pRoot) { delete pRoot; pRoot = NULL; } m_RootItems.RemoveAt(pos); } } /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::InsertRootItem(CItemInfo * lpInfo) { if(lpInfo==NULL) lpInfo = new CItemInfo; CTreeItem* pRoot = NULL; pRoot = new CTreeItem(); CleanMe(pRoot); UpdateData(pRoot, lpInfo); SetIndent(pRoot, 1); SetCurIndex(pRoot, GetItemCount()); SetParentItem(pRoot, NULL); CItemInfo* lp = GetData(pRoot); LV_ITEM lvItem; lvItem.mask = LVIF_TEXT | LVIF_INDENT | LVIF_PARAM; CString strItem = lp->GetItemText(); lvItem.pszText = strItem.GetBuffer(1); lvItem.iItem = GetItemCount(); lvItem.lParam = (LPARAM)pRoot; lvItem.iIndent = 1; lvItem.iSubItem = 0; CListCtrl::InsertItem(&lvItem); int nSize = lp->GetItemCount(); for(int i=0; i < nSize;i++) { CString str = lp->GetSubItem(i); lvItem.mask = LVIF_TEXT; lvItem.iSubItem = i+1; lvItem.pszText = str.GetBuffer(1); SetItem(&lvItem); } m_RootItems.AddTail(pRoot); return pRoot; } void CSuperGridCtrl::DrawTreeItem(CDC* pDC, CTreeItem* pSelItem, int nListItem, const CRect& rcBounds) { int nColWidth = GetColumnWidth(0); int yDown = rcBounds.top; CPen* pPenTreeLine = pDC->SelectObject(&m_psTreeLine); int iIndent = GetIndent(pSelItem); int nHalfImage = (m_cxImage >> 1); int nBottomDown = yDown + nHalfImage + ((rcBounds.Height() - m_cyImage) >> 1); // BOOL bChild = ItemHasChildren(pSelItem); BOOL bCollapsed = IsCollapsed(pSelItem); //draw outline while(1) { CTreeItem* pParent = GetParentItem(pSelItem); if(pParent==NULL)//no more parents, stop break; POSITION pos = pParent->m_listChild.GetTailPosition(); while(pos!=NULL) { CTreeItem *pLastChild = (CTreeItem*)pParent->m_listChild.GetPrev(pos); int nIndex = GetCurIndex(pLastChild); int nCurIndent = GetIndent(pLastChild); if(nListItem > nIndex && iIndent > nCurIndent)//no need to go further in this loop break; //no drawing outside the 1st columns right int xLine = rcBounds.left + nCurIndent * m_cxImage - nHalfImage; if(nIndex == nListItem && nCurIndent==iIndent) { //draw '- int x; pDC->MoveTo(xLine, yDown); pDC->LineTo(xLine, nBottomDown); // - xLine + nHalfImage > nColWidth ? x = nColWidth: x = xLine + nHalfImage; pDC->MoveTo(xLine, nBottomDown); pDC->LineTo(x, nBottomDown); break; } else if(nIndex > nListItem && nCurIndent==iIndent) { //draw |- int x; xLine + nHalfImage > nColWidth ? x = nColWidth : x = xLine + nHalfImage; pDC->MoveTo(xLine, nBottomDown); pDC->LineTo(x, nBottomDown); //- pDC->MoveTo(xLine, yDown); pDC->LineTo(xLine, rcBounds.bottom); break; } else if(nIndex > nListItem && nCurIndent < iIndent) { //draw | pDC->MoveTo(xLine, yDown); pDC->LineTo(xLine, rcBounds.bottom); break; } } pSelItem = pParent;//next } //draw plus/minus sign if(bChild) { CRectangle rect(this, pDC, iIndent, rcBounds); rect.DrawRectangle(this); CPen* pPenPlusMinus = pDC->SelectObject(&m_psPlusMinus); if(bCollapsed) rect.DrawPlus(); else { rect.DrawMinus(); //draw line up to parent folder CPen* pLine = pDC->SelectObject(&m_psTreeLine); int nOffset = (rcBounds.Height() - m_cyImage)/2; pDC->MoveTo(rect.GetLeft()+ m_cxImage, rcBounds.top + m_cyImage+nOffset); pDC->LineTo(rect.GetLeft() + m_cxImage, rcBounds.bottom); pDC->SelectObject(pLine); } pDC->SelectObject(pPenPlusMinus); } pDC->SelectObject(pPenTreeLine); } //walk all over the place setting the hide/show flag of the nodes. //it also deletes items from the listviewctrl. void CSuperGridCtrl::HideChildren(CTreeItem *pItem, BOOL bHide,int nItem) { if(!IsCollapsed(pItem)) if(ItemHasChildren(pItem)) { Hide(pItem, bHide); POSITION pos = pItem->m_listChild.GetHeadPosition(); while (pos != NULL) { HideChildren((CTreeItem *)pItem->m_listChild.GetNext(pos),bHide,nItem+1); DeleteItem(nItem); } } } void CSuperGridCtrl::Collapse(CTreeItem *pItem) { if(pItem != NULL && ItemHasChildren(pItem)) { SetRedraw(0); int nIndex = NodeToIndex(pItem); HideChildren(pItem, TRUE, nIndex+1); InternaleUpdateTree(); SetRedraw(1); } } void CSuperGridCtrl::ExpandAll(CTreeItem *pItem, int& nScroll) { const int nChildren = (int)pItem->m_listChild.GetCount(); if (nChildren > 0) { int nIndex = NodeToIndex(pItem); nScroll = Expand(pItem, nIndex); } POSITION pos = pItem->m_listChild.GetHeadPosition(); while (pos) { CTreeItem *pChild = (CTreeItem*)pItem->m_listChild.GetNext(pos); ExpandAll(pChild, nScroll); } } int CSuperGridCtrl::Expand(CTreeItem* pSelItem, int nIndex) { if(ItemHasChildren(pSelItem) && IsCollapsed(pSelItem)) { LV_ITEM lvItem; lvItem.mask = LVIF_INDENT; lvItem.iItem = nIndex; lvItem.iSubItem = 0; lvItem.lParam=(LPARAM)pSelItem; lvItem.iIndent = GetIndent(pSelItem); SetItem(&lvItem); Hide(pSelItem, FALSE); //expand children POSITION pos = pSelItem->m_listChild.GetHeadPosition(); while(pos != NULL) { CTreeItem* pNextNode = (CTreeItem*)pSelItem->m_listChild.GetNext(pos); CString str = GetData(pNextNode)->GetItemText(); LV_ITEM lvItem; lvItem.mask = LVIF_TEXT | LVIF_INDENT | LVIF_PARAM; lvItem.pszText =str.GetBuffer(1); lvItem.iItem = nIndex + 1; lvItem.iSubItem = 0; lvItem.lParam=(LPARAM)pNextNode; lvItem.iIndent = GetIndent(pSelItem)+1; CListCtrl::InsertItem(&lvItem); if(GetData(pNextNode)->GetCheck()) SetCheck(nIndex + 1); //get subitems int nSize = GetData(pNextNode)->GetItemCount(); for(int i=0; i< nSize;i++) { CString str=GetData(pNextNode)->GetSubItem(i); lvItem.mask = LVIF_TEXT; lvItem.iSubItem = i+1; lvItem.pszText=str.GetBuffer(1); SetItem(&lvItem); } nIndex++; } } InternaleUpdateTree(); return nIndex; } int CSuperGridCtrl::SelectNode(CTreeItem *pLocateNode) { if(IsRoot(pLocateNode)) { UINT uflag = LVIS_SELECTED | LVIS_FOCUSED; SetItemState(0, uflag, uflag); return 0; } int nSelectedItem=-1; CTreeItem* pNode = pLocateNode; CTreeItem* pTopLevelParent=NULL; //Get top parent while(1) { CTreeItem *pParent = GetParentItem(pLocateNode); if(IsRoot(pParent)) break; pLocateNode = pParent; } pTopLevelParent = pLocateNode;//on top of all //Expand the folder if(pTopLevelParent != NULL) { SetRedraw(0); CWaitCursor wait; CTreeItem *pRoot = GetParentItem(pTopLevelParent); if(IsCollapsed(pRoot)) Expand(pRoot,0); ExpandUntil(pTopLevelParent, pNode); UINT uflag = LVIS_SELECTED | LVIS_FOCUSED; nSelectedItem = NodeToIndex(pNode); SetItemState(nSelectedItem, uflag, uflag); SetRedraw(1); EnsureVisible(nSelectedItem, TRUE); } return nSelectedItem; } void CSuperGridCtrl::ExpandUntil(CTreeItem *pItem, CTreeItem* pStopAt) { const int nChildren = (int)pItem->m_listChild.GetCount(); if (nChildren > 0) { POSITION pos = pItem->m_listChild.GetHeadPosition(); while (pos) { CTreeItem *pChild = (CTreeItem*)pItem->m_listChild.GetNext(pos); if(pChild == pStopAt) { int nSize = GetIndent(pChild); CTreeItem** ppParentArray = new CTreeItem*[nSize]; int i=0; while(1) { CTreeItem *pParent = GetParentItem(pChild); if(IsRoot(pParent)) break; pChild = pParent; ppParentArray[i] = pChild; i++; } for(int x=i; x > 0; x--) { CTreeItem *pParent = ppParentArray[x-1]; Expand(pParent, NodeToIndex(pParent)); } delete [] ppParentArray; return; } } } POSITION pos = pItem->m_listChild.GetHeadPosition(); while (pos) { CTreeItem *pChild = (CTreeItem*)pItem->m_listChild.GetNext(pos); ExpandUntil(pChild, pStopAt); } } void CSuperGridCtrl::DeleteItemEx(CTreeItem *pSelItem, int nItem) { SetRedraw(0); DeleteItem(nItem);//delete cur item in listview //delete/hide all children in pSelItem HideChildren(pSelItem, TRUE, nItem); //delete all internal nodes // If root, must delete from m_rootData if(GetParentItem(pSelItem) == NULL ) { DeleteRootItem(pSelItem); } else Delete(pSelItem); InternaleUpdateTree(); if(nItem-1<0)//no more items in list { SetRedraw(1); InvalidateRect(NULL); UpdateWindow(); return; } UINT uflag = LVIS_SELECTED | LVIS_FOCUSED; CRect rcTestIfItemIsValidToSelectOtherWiseSubtrackOneFromItem;//just to get the documention right :) GetItemRect(nItem, rcTestIfItemIsValidToSelectOtherWiseSubtrackOneFromItem ,LVIR_LABEL) ? SetItemState(nItem, uflag, uflag) : SetItemState(nItem-1, uflag, uflag); SetRedraw(1); InvalidateRect(NULL); UpdateWindow(); } void CSuperGridCtrl::CleanMe(CTreeItem *pItem) { // delete child nodes POSITION pos = pItem->m_listChild.GetHeadPosition(); while (pos != NULL) { CTreeItem* pItemData = (CTreeItem*)pItem->m_listChild.GetNext(pos); if(pItemData!=NULL) { if(pItemData->m_lpNodeInfo!=NULL) DELETE_PTR(pItemData->m_lpNodeInfo); pItemData->m_listChild.RemoveAll(); delete(pItemData); } } pItem->m_listChild.RemoveAll(); } /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::GetNext(CTreeItem* pStartAt, CTreeItem* pNode, BOOL bInit, BOOL bDontIncludeHidden) { static BOOL bFound; if (bInit) bFound = FALSE; if (pNode == pStartAt) bFound = TRUE; if(bDontIncludeHidden) { if (!IsCollapsed(pStartAt)) { POSITION pos = pStartAt->m_listChild.GetHeadPosition(); while (pos != NULL) { CTreeItem* pChild = (CTreeItem*)pStartAt->m_listChild.GetNext(pos); if (bFound) return pChild; pChild = GetNext(pChild, pNode, FALSE, TRUE); if (pChild != NULL) return pChild; } } } else { POSITION pos = pStartAt->m_listChild.GetHeadPosition(); while (pos != NULL) { CTreeItem* pChild = (CTreeItem*)pStartAt->m_listChild.GetNext(pos); if (bFound) return pChild; pChild = GetNext(pChild, pNode, FALSE,FALSE); if (pChild != NULL) return pChild; } } // if reached top and last level return original if (bInit) return pNode; else return NULL; } /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::GetPrev(CTreeItem* pStartAt, CTreeItem* pNode, BOOL bInit, BOOL bDontIncludeHidden) { static CTreeItem* pPrev; if (bInit) pPrev = pStartAt; if (pNode == pStartAt) return pPrev; pPrev = pStartAt; if(bDontIncludeHidden) { if (!IsCollapsed(pStartAt)) { POSITION pos = pStartAt->m_listChild.GetHeadPosition(); while (pos != NULL) { CTreeItem* pCur = (CTreeItem*)pStartAt->m_listChild.GetNext(pos); CTreeItem* pChild = GetPrev(pCur,pNode, FALSE,TRUE); if (pChild != NULL) return pChild; } } } else { POSITION pos = pStartAt->m_listChild.GetHeadPosition(); while (pos != NULL) { CTreeItem* pCur = (CTreeItem*)pStartAt->m_listChild.GetNext(pos); CTreeItem* pChild = GetPrev(pCur,pNode, FALSE,FALSE); if (pChild != NULL) return pChild; } } if (bInit) return pPrev; else return NULL; } int CSuperGridCtrl::_NodeToIndex(CTreeItem *pStartpos, CTreeItem* pNode, int& nIndex, BOOL binit) { static BOOL bFound = FALSE; // Account for other root nodes if(GetParentItem(pStartpos) == NULL && GetRootIndex(pStartpos) !=0) nIndex++; if(binit) bFound=FALSE; if(pStartpos==pNode) bFound=TRUE; if(!IsCollapsed(pStartpos)) { POSITION pos = GetHeadPosition(pStartpos); while (pos) { CTreeItem *pChild = GetNextChild(pStartpos, pos); if(bFound) return nIndex; // Craig Schmidt: Cannot set nIndex as return value. Worked find with single root but // the calling function get confused since the return value may indicate // that the next root needs to be searched. Didn'd spend much time on // this so there is probably a better way of doing this. // nIndex = _NodeToIndex(pChild, pNode, nIndex, binit); _NodeToIndex(pChild, pNode, nIndex, binit); nIndex++; } } if(binit && bFound) return nIndex; else return -1; } BOOL CSuperGridCtrl::Delete(CTreeItem* pNode, BOOL bClean) { POSITION pos = m_RootItems.GetHeadPosition(); while(pos!=NULL) { CTreeItem * pRoot = (CTreeItem*)m_RootItems.GetNext(pos); if(_Delete(pRoot, pNode, bClean)) return TRUE; } return FALSE; } BOOL CSuperGridCtrl::_Delete(CTreeItem* pStartAt, CTreeItem* pNode, BOOL bClean) { POSITION pos = pStartAt->m_listChild.GetHeadPosition(); while (pos != NULL) { POSITION posPrev = pos; CTreeItem *pChild = (CTreeItem*)pStartAt->m_listChild.GetNext(pos); if (pChild == pNode) { pStartAt->m_listChild.RemoveAt(posPrev); if(bClean) { if(GetData(pNode)!=NULL) delete GetData(pNode); delete pNode; } return TRUE; } if (_Delete(pChild, pNode) == TRUE) return TRUE; } return FALSE; } BOOL CSuperGridCtrl::IsChildOf(const CTreeItem* pParent, const CTreeItem* pChild) const { if (pChild == pParent) return TRUE; POSITION pos = pParent->m_listChild.GetHeadPosition(); while (pos != NULL) { CTreeItem* pNode = (CTreeItem*)pParent->m_listChild.GetNext(pos); if (IsChildOf(pNode, pChild)) return TRUE; } return FALSE; } void CSuperGridCtrl::OnLButtonDown(UINT nFlags, CPoint point) { if( GetFocus() != this) SetFocus(); LVHITTESTINFO ht; ht.pt = point; SubItemHitTest(&ht); if(OnItemLButtonDown(ht)) { BOOL bSelect=1; bSelect = HitTestOnSign(point, ht); if(bSelect && ht.iItem!=-1) { m_CurSubItem = IndexToOrder(ht.iSubItem); CHeaderCtrl* pHeader = GetHeaderCtrl(); // Make the column fully visible. MakeColumnVisible(Header_OrderToIndex(pHeader->m_hWnd, m_CurSubItem)); CListCtrl::OnLButtonDown(nFlags, point); if(m_bEditStyle) { OnControlLButtonDown(nFlags, point, ht); } //update row anyway for selection bar CRect rc; GetItemRect(ht.iItem, rc, LVIR_BOUNDS); InvalidateRect(rc); UpdateWindow(); CWnd* pParent = GetParent(); if (pParent != NULL) { CPoint* pPt = new CPoint(ht.iItem,ht.iSubItem); pParent->PostMessage(WM_ITEM_SEL,(WPARAM)m_hWnd,(LPARAM)pPt); } } } } void CSuperGridCtrl::OnUpdateListViewItem(CTreeItem* lpItem, LV_ITEM *plvItem) { //default implementation you would go for this 9 out of 10 times CItemInfo *lp = GetData(lpItem); CString str = (CString)plvItem->pszText; if(lp!=NULL) { if(plvItem->iSubItem==0) lp->SetItemText(str); else //subitem data lp->SetSubItemText(plvItem->iSubItem-1, str); UpdateData(lpItem, lp); } SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText); } void CSuperGridCtrl::DeleteAll() { POSITION pos = m_RootItems.GetHeadPosition(); while(pos!=NULL) { CTreeItem * pRoot = (CTreeItem*)m_RootItems.GetNext(pos); if(pRoot!=NULL) DeleteItemEx(pRoot, 0); } } POSITION CSuperGridCtrl::GetRootHeadPosition() const { return m_RootItems.GetHeadPosition(); } POSITION CSuperGridCtrl::GetRootTailPosition() const { return m_RootItems.GetTailPosition(); } /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::GetNextRoot(POSITION& pos) const { return (CTreeItem*)m_RootItems.GetNext(pos); } /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::GetPrevRoot(POSITION& pos) const { return (CTreeItem*)m_RootItems.GetNext(pos); } POSITION CSuperGridCtrl::GetHeadPosition(CTreeItem* pItem) const { return pItem->m_listChild.GetHeadPosition(); } /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::GetNextChild(CTreeItem *pItem, POSITION& pos) const { return (CTreeItem*)pItem->m_listChild.GetNext(pos); } /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::GetPrevChild(CTreeItem *pItem, POSITION& pos) const { return (CTreeItem*)pItem->m_listChild.GetPrev(pos); } POSITION CSuperGridCtrl::GetTailPosition(CTreeItem *pItem) const { return pItem->m_listChild.GetTailPosition(); } void CSuperGridCtrl::AddTail(CTreeItem *pParent, CTreeItem *pChild) { pParent->m_listChild.AddTail(pChild); } inline int StrComp(const CString* pElement1, const CString* pElement2) { return pElement1->Compare(*pElement2); } int CSuperGridCtrl::CompareChildren(const void* p1, const void* p2) { CTreeItem * pChild1 = *(CTreeItem**)p1; CTreeItem * pChild2 = *((CTreeItem**)p2); CItemInfo *pItem1=(*pChild1).m_lpNodeInfo; CItemInfo *pItem2=(*pChild2).m_lpNodeInfo; return StrComp(&(pItem1->GetItemText()), &(pItem2->GetItemText())); } void CSuperGridCtrl::Sort(CTreeItem* pParent, BOOL bSortChildren) { const int nChildren = NumChildren(pParent); if (nChildren > 1) { CTreeItem** ppSortArray = new CTreeItem*[nChildren]; // Fill in array with pointers to our children. POSITION pos = pParent->m_listChild.GetHeadPosition(); for (int i=0; pos; i++) { ASSERT(i < nChildren); ppSortArray[i] = (CTreeItem*)pParent->m_listChild.GetAt(pos); pParent->m_listChild.GetNext(pos); } qsort(ppSortArray, nChildren, sizeof(CTreeItem*), CompareChildren); // reorg children with new sorted list pos = pParent->m_listChild.GetHeadPosition(); for (i=0; pos; i++) { ASSERT(i < nChildren); pParent->m_listChild.SetAt(pos, ppSortArray[i]); pParent->m_listChild.GetNext(pos); } delete [] ppSortArray; } if(bSortChildren) { POSITION pos = pParent->m_listChild.GetHeadPosition(); while (pos) { CTreeItem *pChild = (CTreeItem*)pParent->m_listChild.GetNext(pos); Sort(pChild, TRUE); } } } int CSuperGridCtrl::NumChildren(const CTreeItem *pItem) const { return (int)pItem->m_listChild.GetCount(); } BOOL CSuperGridCtrl::ItemHasChildren(const CTreeItem* pItem) const { BOOL bChildren = pItem->m_listChild.GetCount() != 0; //see if we have a flag int nFlag = pItem->m_bSetChildFlag; if(nFlag!=-1) return 1; else return bChildren; } void CSuperGridCtrl::SetChildrenFlag(CTreeItem *pItem, int nFlag) const { pItem->m_bSetChildFlag = nFlag; } BOOL CSuperGridCtrl::IsCollapsed(const CTreeItem* pItem) const { return pItem->m_bHideChildren;//e.g not visible } void CSuperGridCtrl::Hide(CTreeItem* pItem, BOOL bFlag) { pItem->m_bHideChildren=bFlag; } int CSuperGridCtrl::GetIndent(const CTreeItem* pItem) const { return pItem->m_nIndent; } void CSuperGridCtrl::SetIndent(CTreeItem *pItem, int iIndent) { pItem->m_nIndent = iIndent; } int CSuperGridCtrl::GetCurIndex(const CTreeItem *pItem) const { return pItem->m_nIndex; } void CSuperGridCtrl::SetCurIndex(CTreeItem* pItem, int nIndex) { pItem->m_nIndex = nIndex; } void CSuperGridCtrl::SetParentItem(CTreeItem*pItem, CTreeItem* pParent) { pItem->m_pParent=pParent; } /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::GetParentItem(const CTreeItem* pItem) { return pItem->m_pParent; }; CItemInfo* CSuperGridCtrl::GetData(const CTreeItem* pItem) { return pItem->m_lpNodeInfo; } void CSuperGridCtrl::UpdateData(CTreeItem* pItem, CItemInfo* lpInfo) { pItem->m_lpNodeInfo = lpInfo; } //overrides CItemInfo* CSuperGridCtrl::CopyData(CItemInfo* lpSrc) { ASSERT(FALSE);//debug return NULL; //release } //default implementation for setting icons int CSuperGridCtrl::GetIcon(const CTreeItem* pItem) { int n = GetData(pItem)->GetImage(); if(n!=-1) return n; return 0; } void CSuperGridCtrl::OnControlLButtonDown(UINT nFlags, CPoint point, LVHITTESTINFO& ht) { CTreeItem* pSelItem = GetTreeItem(ht.iItem); if(ht.iSubItem == 0) { POSITION pos = m_listReadOnly.Find(ht.iSubItem); if(pos == NULL) { if(pSelItem->m_bCanEditMe) { CRect rcItem; GetItemRect(ht.iItem, rcItem, LVIR_LABEL); DWORD dwStyle = WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL|ES_LEFT; CEdit *pEdit = new CListEditCtrl(ht.iItem, ht.iSubItem, GetItemText(ht.iItem, ht.iSubItem)); pEdit->Create(dwStyle, rcItem, this, 0x1233); } } } else { POSITION pos = m_listReadOnly.Find(ht.iSubItem); if(pos == NULL) { if(pSelItem->m_bCanEditMe) { EditLabelEx(ht.iItem, ht.iSubItem); } } } } COLORREF CSuperGridCtrl::GetCellRGB() { return m_dwCellRGB; } BOOL CSuperGridCtrl::OnVKMultiply(CTreeItem *pItem, int nIndex) { return 1; } BOOL CSuperGridCtrl::OnVkSubTract(CTreeItem *pItem, int nIndex) { return 1; } BOOL CSuperGridCtrl::OnVKAdd(CTreeItem *pItem, int nIndex) { return 1; } BOOL CSuperGridCtrl::OnDeleteItem(CTreeItem* pItem, int nIndex) { return 1; } BOOL CSuperGridCtrl::OnItemExpanding(CTreeItem *pItem, int iItem) { return 1; } BOOL CSuperGridCtrl::OnItemExpanded(CTreeItem* pItem, int iItem) { return 1; } BOOL CSuperGridCtrl::OnCollapsing(CTreeItem *pItem) { return 1; } BOOL CSuperGridCtrl::OnItemCollapsed(CTreeItem *pItem) { return 1; } BOOL CSuperGridCtrl::OnItemLButtonDown(LVHITTESTINFO& ht) { return 1; } BOOL CSuperGridCtrl::OnVkReturn(void) { return 0; } void CSuperGridCtrl::OnSysColorChange() { CListCtrl::OnSysColorChange(); //nop nothing yet } UINT CSuperGridCtrl::_GetCount(CTreeItem* pItem, UINT& nCount) { POSITION pos = pItem->m_listChild.GetHeadPosition(); while (pos) { CTreeItem *pChild = (CTreeItem*)pItem->m_listChild.GetNext(pos); nCount = _GetCount(pChild, nCount); nCount++; } return nCount; } UINT CSuperGridCtrl::GetCount(void) { UINT nCount=0; UINT _nCount=0; POSITION pos = m_RootItems.GetHeadPosition(); while(pos!=NULL) { CTreeItem * pRoot = (CTreeItem*)m_RootItems.GetNext(pos); nCount += _GetCount(pRoot, _nCount) + 1; } return nCount; } /*CSuperGridCtrl::*/CTreeItem* CSuperGridCtrl::GetTreeItem(int nIndex /*nIndex must be valid of course*/ ) { return reinterpret_cast(GetItemData(nIndex)); } int CSuperGridCtrl::GetSelectedItem(void) const { return GetNextItem(-1, LVNI_ALL | LVNI_SELECTED); } ////////////////////////////////////////////////////////////////////////// // // not much but ... // ////////////////////////////////////////////////////////////////////////// /*CSuperGridCtrl::*/CTreeItem::~CTreeItem() { // delete child nodes POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) { CTreeItem* pItem = (CTreeItem*)m_listChild.GetNext(pos); if(pItem!=NULL) { if(pItem->m_lpNodeInfo!=NULL) DELETE_PTR(pItem->m_lpNodeInfo); delete pItem; } } m_listChild.RemoveAll(); } ////////////////////////////////////////////////////////////////////////// // // Simple class CRectangle for the + - sign, // ////////////////////////////////////////////////////////////////////////// CRectangle::CRectangle(CSuperGridCtrl* pCtrl, CDC* pDC, int iIndent, const CRect& rcBounds) { m_pDC=pDC; int nHalfImage = (pCtrl->m_cxImage >> 1); int nBottomDown = rcBounds.top + nHalfImage + ((rcBounds.Height() - pCtrl->m_cyImage) >> 1); m_right_bottom.cx = (pCtrl->m_cxImage>>1)+2+1; m_right_bottom.cy = (pCtrl->m_cyImage>>1)+2+1; m_left = rcBounds.left + iIndent * pCtrl->m_cxImage - nHalfImage; m_top = nBottomDown - (m_right_bottom.cy >> 1); m_left_top.x = m_left - (m_right_bottom.cx >> 1); m_left_top.y = m_top; m_topdown = nBottomDown; } void CRectangle::DrawRectangle(CSuperGridCtrl* pCtrl) { //erase bkgrnd CRect rc(m_left_top, m_right_bottom); m_pDC->FillRect(rc, &pCtrl->m_brushErase); //draw rectangle CPen* pPenRectangle = m_pDC->SelectObject(&pCtrl->m_psRectangle); m_pDC->Rectangle(rc); m_pDC->SelectObject(pPenRectangle); } CRectangle::~CRectangle() { } BOOL CRectangle::HitTest(CPoint pt) { CRect rc = GetHitTestRect(); return rc.PtInRect(pt); } void CRectangle::DrawPlus(void) { double dScale = CoreTools::GetDPIScale(); m_pDC->MoveTo(m_left, m_topdown-2 * dScale); m_pDC->LineTo(m_left, m_topdown+3 * dScale) ; m_pDC->MoveTo(m_left-2 * dScale, m_topdown); m_pDC->LineTo(m_left+3 * dScale, m_topdown); } void CRectangle::DrawMinus(void) { double dScale = CoreTools::GetDPIScale(); m_pDC->MoveTo(m_left-2 * dScale, m_topdown); m_pDC->LineTo(m_left+3 * dScale, m_topdown); } bool CSuperGridCtrl::PtHitCheckBox(const CPoint &pt) { bool bHasCheckBox(GetExtendedStyle() & LVS_EX_CHECKBOXES); if (!bHasCheckBox) return false; LVHITTESTINFO ht; ht.pt = pt; SubItemHitTest(&ht); CTreeItem *pItem = GetTreeItem(ht.iItem); int left = (GetIndent(pItem) * m_cxImage) + m_cxImage; int right = left + m_cxImage; if (pt.x >= left && pt.x <= right) return TRUE; return FALSE; }