// ========================================================================== // Class Implementation : OXTreeCtrl // ========================================================================== // Source file :OXTreeCtrl.cpp // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "OXTreeCtrl.h" #include "UTBStrOp.h" #include "UTB64Bit.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[]=__FILE__; #endif #define _OX_MAX_ITEM_TEXT 300 ///////////////////////////////////////////////////////////////////////////// // COXTreeCtrl IMPLEMENT_DYNAMIC(COXTreeCtrl, CListCtrl) COXTreeCtrl::COXTreeCtrl(): m_ptLastClick(0,0),m_wndCal(),m_bInit(FALSE) { m_nIndent= TV_MININDENT; m_dwExStyle=0; m_dwTCStyle=0; m_xtiRoot.SetExpand(); m_pActiveEditWnd=NULL; m_nEditColumn=-1; m_nItemHeight=0; m_nItemsCount=0; m_bClick=FALSE; m_bCanEdit=FALSE; m_nLastIndex=-1; m_nLastColumn=-1; m_bCreatingDragImage=FALSE; m_hOldDropTarget=NULL; m_bFirstTimeHooking=TRUE; m_nRedrawFlag=0; m_nTimerCheckKeyboardInput=0; m_sSearchMask=_T(""); m_clrHorizontalGrid = 0; // black is the default m_clrVerticalGrid = 0; // black is the default m_lpfnDefaultCompare = NULL; } COXTreeCtrl::~COXTreeCtrl() { m_mapCompareFunctions.RemoveAll(); } BEGIN_MESSAGE_MAP(COXTreeCtrl, CListCtrl) //{{AFX_MSG_MAP(COXTreeCtrl) ON_WM_LBUTTONDOWN() ON_WM_LBUTTONDBLCLK() ON_WM_RBUTTONDOWN() ON_WM_MBUTTONDOWN() ON_NOTIFY_REFLECT_EX(NM_KILLFOCUS, OnKillfocus) ON_NOTIFY_REFLECT_EX(NM_SETFOCUS, OnSetfocus) ON_NOTIFY_REFLECT_EX(LVN_BEGINLABELEDIT, OnBeginlabeledit) ON_NOTIFY_REFLECT_EX(LVN_ENDLABELEDIT, OnEndlabeledit) ON_WM_HSCROLL() ON_WM_VSCROLL() ON_WM_PARENTNOTIFY() ON_WM_TIMER() ON_WM_DESTROY() ON_WM_SIZE() ON_WM_KEYDOWN() //}}AFX_MSG_MAP ON_WM_MEASUREITEM_REFLECT() ON_WM_STYLECHANGED() #ifndef OX_TREECTRL_NOITEMTIPS ON_MESSAGE(WM_DOYOUNEEDITEMTIP, OnNeedItemTip) ON_MESSAGE(WM_USER_ADJUSTLASTCOLUMN, OnUserAdjustLastColumn) #endif END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COXTreeCtrl message handlers void COXTreeCtrl::DrawItem(LPDRAWITEMSTRUCT lpDIS) // --- In : // --- Out : // --- Returns : //--- Effect :draw item { int itemID=lpDIS->itemID; if(itemID == -1) return; COXTreeItem *xti=(COXTreeItem*)CListCtrl::GetItemData(itemID); if( xti == NULL ) return; int nLevel=xti->GetItemLevel(); CRect rcItem(lpDIS->rcItem); CDC dc; dc.Attach(lpDIS->hDC); CRect rcGrid=rcItem; rcGrid.left += GetItemIndent(xti); switch(lpDIS->itemAction) { case ODA_SELECT: case ODA_DRAWENTIRE: { if(m_bCreatingDragImage) { COLORREF clrOldForground, clrOldBackground; rcItem.left=0; LV_COLUMN lvc; lvc.mask=LVCF_FMT|LVCF_WIDTH; int nCol=0; CFont *pOldFont=NULL; VERIFY(GetColumn(nCol, &lvc)); rcItem.right=rcItem.left + lvc.cx; if(dc.RectVisible(rcItem)) { DrawItemImages(&dc,xti,itemID,rcItem,0,FALSE); clrOldForground=dc.SetTextColor(xti->IsDisabled() ? ::GetSysColor(COLOR_3DSHADOW) : (xti->HasColor(nCol) ? xti->GetItemColor(nCol) : GetTextColor())); if (xti->HasBackColor(nCol)) clrOldBackground=dc.SetBkColor((!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : xti->GetItemBackColor(nCol))); else clrOldBackground=dc.SetBkColor((!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : GetTextBkColor())); CString sTxt=GetItemText((HTREEITEM) xti,nCol); if(xti->HasFont(nCol)) pOldFont=dc.SelectObject(xti->GetItemFont(nCol)); UINT nEllipsisFormat; VERIFY(GetItemDrawEllipsis((HTREEITEM)xti,nEllipsisFormat,nCol)); // draw item's text DrawItemText(&dc,sTxt,rcItem,lvc.fmt,FALSE, (!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : GetTextBkColor()), nEllipsisFormat); dc.SetTextColor(clrOldForground); dc.SetBkColor(clrOldBackground); if(xti->HasFont(nCol)) { dc.SelectObject(pOldFont); } } } else { BOOL bShowSelAlways=GetShowSelAlways(); BOOL bFocus=(::GetFocus() == m_hWnd); BOOL bShowSel=bShowSelAlways | bFocus; BOOL bDropHilited=GetItemState(GetItemFromIndex(itemID), TVIS_DROPHILITED)==TVIS_DROPHILITED; BOOL bShowItemSel=bShowSel && (((lpDIS->itemState & ODS_SELECTED)==ODS_SELECTED) || bDropHilited); COLORREF clrOldForground, clrOldBackground; int nOffset=GetItemIndent(xti); int nFocusRectOffset=rcItem.left + nOffset; CRect rcLines=rcItem; rcLines.right=rcLines.left + nOffset; DrawLines(&dc,rcLines,xti,nLevel); rcItem.left += nOffset; LV_COLUMN lvc; lvc.mask=LVCF_FMT|LVCF_WIDTH; int nCol=0; int nColCnt=GetColumnsCount(); BOOL bRowSel=(BOOL)(m_dwExStyle & TVOXS_ROWSEL); for(nCol=0; nColIsDisabled() ? ::GetSysColor(COLOR_3DSHADOW) : (xti->HasColor(nCol) ? xti->GetItemColor(nCol) : GetTextColor()))); COLORREF clrBackground = bShowItemSel && (nCol==0 || bRowSel) ? (GetFocus()==this ? ::GetSysColor(COLOR_HIGHLIGHT) : ::GetSysColor(COLOR_BTNFACE)) : (!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : ((xti->HasBackColor(nCol) ? xti->GetItemBackColor(nCol) : GetTextBkColor()))); clrOldBackground=dc.SetBkColor(clrBackground); CString sTxt=GetItemText((HTREEITEM) xti,nCol); if(xti->HasFont(nCol)) pOldFont=dc.SelectObject(xti->GetItemFont(nCol)); UINT nEllipsisFormat; VERIFY(GetItemDrawEllipsis((HTREEITEM)xti,nEllipsisFormat,nCol)); dc.FillSolidRect(rcItem, clrBackground); // Draw item's text if (nCol != 0) rcItem.left += 1; DrawItemText(&dc,sTxt,rcItem,lvc.fmt, (lpDIS->itemState & ODS_FOCUS) && bFocus && nCol==0 && !bRowSel, bRowSel && bShowItemSel ? (GetFocus()==this ? ::GetSysColor(COLOR_HIGHLIGHT) : ::GetSysColor(COLOR_BTNFACE)) : (!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : ((xti->HasBackColor(nCol) ? xti->GetItemBackColor(nCol) : GetTextBkColor()))),nEllipsisFormat); dc.SetTextColor(clrOldForground); dc.SetBkColor(clrOldBackground); if(xti->HasFont(nCol)) { dc.SelectObject(pOldFont); } if(HasGrid(TVOXS_VGRID)) { CPen pen; pen.CreatePen(PS_SOLID, 1, m_clrVerticalGrid); CPen* pOldPen = (CPen*) dc.SelectObject(&pen); dc.MoveTo(rcItem.right-1,rcItem.top); dc.LineTo(rcItem.right-1,rcItem.bottom); dc.SelectObject(pOldPen); } } rcItem.left =rcItem.right; } if(HasGrid(TVOXS_HGRID)) { CPen pen; pen.CreatePen(PS_SOLID, 1, m_clrHorizontalGrid); CPen* pOldPen = (CPen*) dc.SelectObject(&pen); // draw horizontal grid line if(GetDrawGridFullLength()) { CRect clientRect; GetClientRect(clientRect); dc.MoveTo(clientRect.left,rcGrid.bottom-1); dc.LineTo(clientRect.right,rcGrid.bottom-1); } else { dc.MoveTo(rcGrid.left,rcGrid.bottom-1); dc.LineTo(rcGrid.right,rcGrid.bottom-1); } dc.SelectObject(pOldPen); } if((lpDIS->itemState & ODS_FOCUS) && bFocus && bRowSel && !(m_dwExStyle & TVOXS_NOFOCUSRECT)) { rcGrid.left=nFocusRectOffset; dc.DrawFocusRect(rcGrid); } } } break; default: ASSERT(FALSE); } dc.Detach(); } void COXTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point) { if(GetFocus()!=this) { SetFocus(); } m_ptLastClick=point; UINT nFl; int idx=HitTest(point,&nFl); HTREEITEM hti=GetItemFromIndex(idx); ASSERT(hti); COXTreeItem *xti=GetXItem(hti); ASSERT(xti); m_bCanEdit=FALSE; if(idx!=-1) { if (nFl & TVHT_ONITEMBUTTON) { Expand(hti,TVE_TOGGLE); return; } if(nFl & TVHT_ONITEM) { CListCtrl::OnLButtonDown(nFlags,point); } else { if(GetPickAnywhere()) { CRect rcItem; CListCtrl::GetItemRect(idx,&rcItem,LVIR_BOUNDS); rcItem.left+=GetItemIndent(xti); if(rcItem.PtInRect(point)) { if((GetKeyState(VK_SHIFT)&0x8000) || (GetKeyState(VK_CONTROL)&0x8000)) { VERIFY(CListCtrl::GetItemPosition(idx,&point)); } CListCtrl::OnLButtonDown(LVHT_ONITEM,point); } } } // try to detect subitem to edit label CRect rcEdit; int nCol=GetEditColumn(xti,&rcEdit); m_nLastIndex=idx; m_nLastColumn=nCol; } else { if((GetExStyle()&TVOXS_MULTISEL)!=0) { CWnd::OnLButtonDown(nFlags, point); } m_nLastIndex=-1; m_nLastColumn=-1; } m_bClick=!m_bClick; } HTREEITEM COXTreeCtrl::InsertItem(LPCTSTR lpszItem, HTREEITEM hParent, HTREEITEM hInsertAfter) { if(hParent==NULL) hParent=TVI_ROOT; COXTreeItem *pParent=GetXItem(hParent); if(!pParent) return NULL; COXTreeItem *pInsAfter; if(hInsertAfter == TVI_FIRST) { pInsAfter=(COXTreeItem *) TVI_FIRST; } else if(hInsertAfter == TVI_SORT) { pInsAfter=pParent->FindItemToInsertAfter(lpszItem); } else { pInsAfter=GetXItem(hInsertAfter); } COXTreeItem * pNewItem=new COXTreeItem(lpszItem); ASSERT(pParent); BOOL bDrawBtn=pParent->NeedDrawButton(); pParent->AddChild(pNewItem,pInsAfter); AddItemToCtrl(pNewItem); if(pParent->NeedDrawButton() != bDrawBtn && HasButtons()) { // parent button's state changed, redraw parent int idx=GetItemIndexInternal(pParent); if(idx != -1) CListCtrl::RedrawItems(idx,idx); } m_nItemsCount++; return (HTREEITEM)pNewItem; } HTREEITEM COXTreeCtrl::InsertItem(LPTV_INSERTSTRUCT lpTVIS) { if(lpTVIS->hParent==NULL) lpTVIS->hParent=TVI_ROOT; COXTreeItem *pParent=GetXItem(lpTVIS->hParent); if(!pParent) return NULL; COXTreeItem *pInsAfter; if(lpTVIS->hInsertAfter == TVI_FIRST) { pInsAfter=(COXTreeItem *) TVI_FIRST; } else if(lpTVIS->hInsertAfter == TVI_SORT) { pInsAfter=pParent->FindItemToInsertAfter(lpTVIS->item.pszText); } else { pInsAfter=GetXItem(lpTVIS->hInsertAfter); } COXTreeItem * pNewItem= new COXTreeItem(lpTVIS->item.pszText); BOOL bDrawBtn=pParent->NeedDrawButton(); pParent->AddChild(pNewItem,pInsAfter); lpTVIS->item.mask |= TVIF_HANDLE; lpTVIS->item.hItem=(HTREEITEM) pNewItem; VERIFY(SetItem(&(lpTVIS->item))); AddItemToCtrl(pNewItem); if(pParent->NeedDrawButton() != bDrawBtn && HasButtons()) { // parent button's state changed, redraw parent int idx=GetItemIndexInternal(pParent); if(idx != -1) CListCtrl::RedrawItems(idx,idx); } m_nItemsCount++; return (HTREEITEM) pNewItem; } HTREEITEM COXTreeCtrl::InsertItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam, HTREEITEM hParent, HTREEITEM hInsertAfter) { TV_INSERTSTRUCT tvis; tvis.hParent=hParent; tvis.hInsertAfter=hInsertAfter; tvis.item.mask=nMask; tvis.item.state=nState; tvis.item.stateMask=nStateMask; tvis.item.pszText=(LPTSTR)lpszItem; tvis.item.iImage=nImage; tvis.item.iSelectedImage=nSelectedImage; tvis.item.lParam=lParam; return InsertItem(&tvis); } HTREEITEM COXTreeCtrl::InsertItem(LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent/*=TVI_ROOT*/, HTREEITEM hInsertAfter/*=TVI_LAST*/) { TV_INSERTSTRUCT tvis; tvis.hParent=hParent; tvis.hInsertAfter=hInsertAfter; tvis.item.mask=TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT; tvis.item.pszText=(LPTSTR)lpszItem; tvis.item.iImage=nImage; tvis.item.iSelectedImage=nSelectedImage; return InsertItem(&tvis); } int COXTreeCtrl::GetItemIndex(HTREEITEM hItem) const { return GetItemIndexInternal(GetXItem(hItem)); } int COXTreeCtrl::GetItemIndexInternal(COXTreeItem *pItem) const { if(pItem == &m_xtiRoot) return -1; LV_FINDINFO lvfi; lvfi.flags=LVFI_PARAM; lvfi.lParam=(LPARAM)pItem; return CListCtrl::FindItem(&lvfi); } COXTreeItem* COXTreeCtrl::GetXItem(HTREEITEM hti) const { if(hti == TVI_ROOT) return &m_xtiRoot; if(hti == TVI_FIRST || hti == TVI_LAST) return NULL; #ifdef _DEBUG if(hti) ASSERT(_CrtIsValidPointer(hti,sizeof(COXTreeItem),FALSE)); #endif return (COXTreeItem*)hti; } void COXTreeCtrl::OnLButtonDblClk(UINT nFlags, CPoint point) { if(GetFocus() != this) SetFocus(); UINT flgs = nFlags; int idx = HitTest(point,&flgs); if (idx != -1) if (GetPickAnywhere() || (flgs & (TVHT_ONITEM | TVHT_ONITEMBUTTON))) { Expand((HTREEITEM )CListCtrl::GetItemData(idx),TVE_TOGGLE); CListCtrl::OnLButtonDblClk(nFlags,point); } m_bClick=FALSE; } void COXTreeCtrl::OnRButtonDown(UINT nFlags, CPoint point) { UINT flgs = nFlags; int idx = HitTest(point,&flgs); if (idx != -1) if (GetPickAnywhere() || (flgs & (TVHT_ONITEM | TVHT_ONITEMBUTTON))) CListCtrl::OnRButtonDown(nFlags,point); } void COXTreeCtrl::OnMButtonDown(UINT nFlags, CPoint point) { UINT flgs = nFlags; int idx = HitTest(point,&flgs); if (idx != -1) if (GetPickAnywhere() || (flgs & (TVHT_ONITEM | TVHT_ONITEMBUTTON))) CListCtrl::OnMButtonDown(nFlags,point); } BOOL COXTreeCtrl::Expand(HTREEITEM hItem, UINT nCode) { COXTreeItem *pItem=GetXItem(hItem); ASSERT(pItem); if(SendItemExpandingNotify(hItem,nCode)) { return FALSE;// parent voted against! } SetRedraw(FALSE); pItem->Expand(nCode,this); SetRedraw(TRUE); CListCtrl::RedrawItems(GetItemIndex(hItem),GetItemIndex(hItem)); SendItemExpandedNotify(hItem,nCode); return TRUE; } int COXTreeCtrl::SetItemAtPos(int pos,COXTreeItem *pItem) { int idx=CListCtrl::InsertItem(pos,LPSTR_TEXTCALLBACK); if(idx == -1) return -1; CListCtrl::SetItemData(idx,(DWORD)PtrToUlong (pItem)); for(int i=1;i < pItem->GetSubitemsCount();i++) { LPCTSTR lpszText=GetItemText((HTREEITEM)pItem,i); if(lpszText) { CListCtrl::SetItemText(pos,i,LPSTR_TEXTCALLBACK); } } TV_ITEM tvi; ::ZeroMemory((void*)&tvi,sizeof(tvi)); if(pItem->m_tvi.iImage>=0) { tvi.mask|=TVIF_IMAGE; tvi.iImage=pItem->m_tvi.iImage; } if(pItem->m_tvi.iSelectedImage>=0) { tvi.mask|=TVIF_SELECTEDIMAGE; tvi.iSelectedImage=pItem->m_tvi.iSelectedImage; } if((pItem->m_tvi.state & INDEXTOSTATEIMAGEMASK(0xF))!=0) { tvi.state=(pItem->m_tvi.state & INDEXTOSTATEIMAGEMASK(0xF)); tvi.stateMask=tvi.state; tvi.mask|=TVIF_STATE; } if(tvi.mask!=0) { tvi.hItem=pItem->m_tvi.hItem; tvi.mask|=TVIF_HANDLE; VERIFY(SetItem(&tvi)); } return idx; } BOOL COXTreeCtrl::AddItemToCtrl(COXTreeItem *pItem) // --- In : item handle // --- Out : // --- Returns :Nonzero if successful; otherwise 0 //--- Effect : { ASSERT(pItem!=NULL); if(pItem->pxParent!=NULL && pItem->pxParent->m_tvi.cChildren==0) pItem->pxParent->m_tvi.cChildren=1; if (pItem->IsAnyParentCollapsed()) return TRUE; if(pItem->IsHidden()) return TRUE; COXTreeItem *pPrevItem=pItem->GetPrevInTree(); while(pPrevItem != &m_xtiRoot && !pPrevItem->IsVisible()) { pPrevItem=pPrevItem->GetPrevInTree(); if(!pPrevItem) { ASSERT(FALSE); return FALSE; } } ASSERT(pPrevItem); int nIndex; if(pPrevItem == &m_xtiRoot) nIndex=0;//will be first item! else { nIndex=GetItemIndexInternal(pPrevItem) + 1; ASSERT(nIndex); } BOOL bRet=(-1 != SetItemAtPos(nIndex,pItem)); return bRet; } void COXTreeCtrl::DeleteChildrenItems(COXTreeItem *pItem) // --- In :handle of the item // --- Out : // --- Returns : //--- Effect :delete children of specified item { COXTreeItem *xti=pItem->pxFirstChild; while(xti) { DeleteChildrenItems(xti); int idx=GetItemIndexInternal(xti); if(idx!=-1) { CListCtrl::DeleteItem(idx); } xti=xti->pxNext; } pItem->DeleteChildren(); pItem->m_tvi.cChildren=0; } void COXTreeCtrl::DrawItemText(CDC *pDC,LPCTSTR sText,CRect& rcTxt,UINT nJustify, BOOL bDrawFocus,COLORREF clrFillOutside, UINT nEllipsisMode) { CRect rcText=rcTxt; CRect rcFill=rcTxt; // check rect need to be drawn if(!pDC->RectVisible(&rcText)) return; // check text if(!sText || sText == LPSTR_TEXTCALLBACK) return; COLORREF clrBack=pDC->GetBkColor(); if(!rcFill.IsRectEmpty()) pDC->FillSolidRect(rcFill,clrFillOutside); pDC->SetBkColor(clrBack); UINT nFormat=DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER; nFormat|=nEllipsisMode; rcText.DeflateRect(2,0); rcFill=rcText; pDC->DrawText(sText,rcFill,nFormat|DT_CALCRECT); rcFill.top=rcTxt.top; rcFill.bottom=rcTxt.bottom; rcFill.InflateRect(2,0); int nTextSize=rcFill.Width(); switch(nJustify & LVCFMT_JUSTIFYMASK) { case LVCFMT_LEFT: nFormat|=DT_LEFT; rcFill.left=__max(rcFill.left,rcTxt.left); rcFill.right=__min(rcFill.right,rcTxt.right); break; case LVCFMT_RIGHT: nFormat|=DT_RIGHT; rcFill.right=rcTxt.right; rcFill.left=__max(rcFill.right-nTextSize,rcTxt.left); break; case LVCFMT_CENTER: nFormat|=DT_CENTER; rcFill.left=__max(rcTxt.left+((rcTxt.Width()-nTextSize)/2),rcTxt.left); rcFill.right=__min(rcTxt.right-((rcTxt.Width()-nTextSize)/2)+ (rcTxt.Width()-nTextSize)%2,rcTxt.right); break; default: ASSERT(FALSE); break; } if(!rcFill.IsRectEmpty()) { pDC->FillSolidRect(rcFill,clrBack); } // Draw the text pDC->SetBkMode(TRANSPARENT); pDC->DrawText(sText,rcText,nFormat); if(bDrawFocus && !(m_dwExStyle & TVOXS_NOFOCUSRECT)) { pDC->DrawFocusRect(rcFill); } return; } BOOL COXTreeCtrl::GetShowSelAlways() const { return ((GetStyle() & TVS_SHOWSELALWAYS) == TVS_SHOWSELALWAYS); } BOOL COXTreeCtrl::OnKillfocus(NMHDR* pNMHDR, LRESULT* pResult) // --- In : // --- Out : // --- Returns : //--- Effect : { UNREFERENCED_PARAMETER(pNMHDR); RefreshFocusItem(); *pResult=0; return FALSE; } void COXTreeCtrl::RefreshFocusItem() { int nFocusItem=CListCtrl::GetNextItem(-1,LVNI_SELECTED); while(nFocusItem != -1) { RedrawItems(nFocusItem,nFocusItem); nFocusItem=CListCtrl::GetNextItem(nFocusItem,LVNI_SELECTED); } } BOOL COXTreeCtrl::OnSetfocus(NMHDR* pNMHDR, LRESULT* pResult) { UNREFERENCED_PARAMETER(pNMHDR); RefreshFocusItem(); *pResult=0; m_bClick=FALSE; return FALSE; } CImageList* COXTreeCtrl::SetImageList(CImageList* pImageList, int nImageList) { switch(nImageList) { case TVSIL_NORMAL: nImageList=LVSIL_SMALL; break; case TVSIL_STATE: nImageList=LVSIL_STATE; break; default: TRACE(_T("COXTreeCtrl::SetImageList: unknown image list type!\n")); return NULL; } return CListCtrl::SetImageList(pImageList,nImageList); } BOOL COXTreeCtrl::HasButtons() const { return (m_dwTCStyle & TVS_HASBUTTONS); } BOOL COXTreeCtrl::HasLines() const { return (m_dwTCStyle & TVS_HASLINES); } BOOL COXTreeCtrl::HasLinesAtRoot() const { return (m_dwTCStyle & TVS_LINESATROOT); } int COXTreeCtrl::GetItemStateIndex(int nItem) const { LV_ITEM lvI; lvI.mask=LVIF_IMAGE | LVIF_STATE; lvI.iItem=nItem; lvI.iSubItem =0; lvI.state=0; lvI.stateMask=INDEXTOSTATEIMAGEMASK(0xF); if (CListCtrl::GetItem(&lvI)) return STATEIMAGEMASKTOINDEX(lvI.state) - 1; else return -1; } void COXTreeCtrl::DrawLines(CDC *pDC, LPRECT rLines, COXTreeItem *pItem, int nLev) { int nLevel=nLev; CPen penBtn(PS_SOLID,1,::GetSysColor(COLOR_3DSHADOW)); pDC->ExtTextOut(0,0,ETO_OPAQUE,rLines,_T(""),0,NULL); CRect rcLines(rLines); if(!rcLines.IsRectEmpty()) { if (pItem != NULL && pItem->HasBackColor()) pDC->FillSolidRect(rcLines,(!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : pItem->GetItemBackColor())); else { if (IsPropertiesWnd()) pDC->FillSolidRect(rcLines, ::GetSysColor(COLOR_BTNFACE)); else pDC->FillSolidRect(rcLines,(!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : GetTextBkColor())); } } CRect rcStep; rcStep=rcLines; rcStep.left=rcStep.right - m_nIndent; COXTreeItem *xti=pItem; int nLineMode; if(xti->IsLastUnhidden() && GetItemIndexInternal(xti)==0) nLineMode=0; // first & last node at the same time else if(GetItemIndexInternal(xti)==0) nLineMode=4; // first node else if(xti->IsLastUnhidden()) nLineMode=2; // last node else nLineMode=3; // there are node(s) below if(HasLines()) DrawLine(pDC,&m_penLines,rcStep,nLineMode); CPen *pOld=pDC->SelectObject(&penBtn); if(HasButtons() && pItem->NeedDrawButton()) { CRect rcButton; CalcBtnRect(&rcButton,rcStep); DrawButton(pDC,rcButton,pItem->IsExpanded()); } pDC->SelectObject(pOld); if(HasLines()) { while(nLevel > 0) { xti=xti->pxParent; rcStep.OffsetRect(-m_nIndent,0); if(!xti->IsLastUnhidden()) DrawLine(pDC,&m_penLines,rcStep,1); nLevel--; } } } void COXTreeCtrl::DrawLine(CDC *pDC, CPen* pPen, CRect& rc, int nMode) { CPen *penOld=pDC->SelectObject(pPen); switch(nMode) { case 0: pDC->MoveTo(rc.left + rc.Width()/2 - 1,rc.top + rc.Height()/2); pDC->LineTo(rc.right,rc.top + rc.Height()/2); break; case 1: pDC->MoveTo(rc.left + rc.Width()/2,rc.top - 1); pDC->LineTo(rc.left + rc.Width()/2,rc.bottom); break; case 2: pDC->MoveTo(rc.left + rc.Width()/2,rc.top - 1); pDC->LineTo(rc.left + rc.Width()/2,rc.top + rc.Height()/2); pDC->LineTo(rc.right,rc.top + rc.Height()/2); break; case 3: pDC->MoveTo(rc.left + rc.Width()/2,rc.top - 1); pDC->LineTo(rc.left + rc.Width()/2,rc.bottom); pDC->MoveTo(rc.left + rc.Width()/2,rc.top + rc.Height()/2); pDC->LineTo(rc.right,rc.top + rc.Height()/2); break; case 4: pDC->MoveTo(rc.left + rc.Width()/2,rc.bottom + 1); pDC->LineTo(rc.left + rc.Width()/2,rc.top + rc.Height()/2); pDC->LineTo(rc.right,rc.top + rc.Height()/2); break; default: ASSERT(FALSE); } if(penOld!=NULL) { pDC->SelectObject(penOld); } } void COXTreeCtrl::DrawButton(CDC *pDC,CRect& rcButton,BOOL bExpand) { pDC->Rectangle(rcButton); CRect rcInnerSpace=rcButton; rcInnerSpace.DeflateRect(1,1); if(!rcInnerSpace.IsRectEmpty()) { pDC->FillSolidRect(rcInnerSpace,(!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : GetTextBkColor())); } CPen penPlusMinus; penPlusMinus.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_WINDOWTEXT)); CPen* pOldPen = pDC->SelectObject(&penPlusMinus); pDC->MoveTo(rcButton.left + 2,rcButton.top + rcButton.Height()/2); pDC->LineTo(rcButton.right-2,rcButton.top + rcButton.Height()/2); if(!bExpand) { pDC->MoveTo(rcButton.left + rcButton.Width()/2,rcButton.top +2); pDC->LineTo(rcButton.left + rcButton.Width()/2,rcButton.bottom -2); } pDC->SelectObject(pOldPen); } void COXTreeCtrl::CalcBtnRect(CRect *prcButton,CRect& rcStep) { (*prcButton)=rcStep; int nSide=__max((rcStep.Height()*5)/10,9); int offx =(rcStep.Width() - nSide + 1)/2; int offy=(rcStep.Height() - nSide + 1)/2; prcButton->top += offy; prcButton->bottom=prcButton->top +nSide; prcButton->left += offx; prcButton->right=prcButton->left + nSide; } int COXTreeCtrl::HitTest(CPoint pt, UINT* pFlags) const { UINT nListFlags; int idx=CListCtrl::HitTest(pt,&nListFlags); if(!pFlags) return idx; (*pFlags)=0; if(idx != -1) { CRect rcItem; COXTreeItem *xti=(COXTreeItem *)CListCtrl::GetItemData(idx); if(CListCtrl::GetItemRect(idx,&rcItem,LVIR_BOUNDS)) { int nIndent=-1; CRect rcButton,rcText,rcImage,rcStateImage; GetItemPartRect(rButton,xti,&rcButton,&nIndent); GetItemPartRect(rImage,xti,&rcImage,&nIndent); GetItemPartRect(rStateImage,xti,&rcStateImage,&nIndent); GetItemPartRect(rText,xti,&rcText,&nIndent); *pFlags=TVHT_ONITEM; if(rcText.PtInRect(pt)) (*pFlags) |= TVHT_ONITEMLABEL; else if(rcImage.PtInRect(pt)) (*pFlags) |= TVHT_ONITEMICON; else if(rcStateImage.PtInRect(pt)) (*pFlags) |= TVHT_ONITEMSTATEICON; else if(rcButton.PtInRect(pt)) (*pFlags) |= TVHT_ONITEMBUTTON; else if(rcItem.PtInRect(pt)) (*pFlags) |= TVHT_TORIGHT; else { (*pFlags) |= TVHT_NOWHERE; (*pFlags) &= ~TVHT_ONITEM; } } } if ((*pFlags)==0) { if (nListFlags&LVHT_ABOVE) *pFlags=TVHT_ABOVE; else if (nListFlags&LVHT_BELOW) *pFlags=TVHT_BELOW; else if (nListFlags&LVHT_TOLEFT) *pFlags=TVHT_TOLEFT; else if (nListFlags&LVHT_TORIGHT) *pFlags=TVHT_TORIGHT; else if (nListFlags&LVHT_NOWHERE) *pFlags=TVHT_NOWHERE; } return idx; } HTREEITEM COXTreeCtrl::HitTest(TVHITTESTINFO* pHitTestInfo) const { ASSERT(pHitTestInfo!=NULL); pHitTestInfo->hItem=NULL; int nIndex=HitTest(pHitTestInfo->pt,&pHitTestInfo->flags); if(nIndex>=0) { pHitTestInfo->hItem=GetItemFromIndex(nIndex); } return pHitTestInfo->hItem; } BOOL COXTreeCtrl::Init() { if(m_bInit) return TRUE; // create line pen LOGBRUSH logBrush; logBrush.lbColor=GetSysColor(COLOR_3DSHADOW); logBrush.lbStyle=BS_SOLID; logBrush.lbHatch=HS_HORIZONTAL; if (!m_penLines.CreatePen(PS_COSMETIC | PS_ALTERNATE, 1, &logBrush, 0, NULL)) { if (!m_penLines.CreatePen(PS_COSMETIC, 1, &logBrush, 0, NULL)) { TRACE(_T("COXTreeCtrl::Init: Error creating the lines Pen\n")); return FALSE; } } CRect rClient; GetClientRect(&rClient); // insert at least one column initially if(CListCtrl::InsertColumn(0,_T(""),LVCFMT_LEFT,rClient.Width()) == -1) { TRACE(_T("COXTreeCtrl::Init: Error inserting column\n")); return FALSE; } VERIFY(SetBkColor(::GetSysColor(COLOR_WINDOW))); VERIFY(SetTextBkColor(::GetSysColor(COLOR_WINDOW))); VERIFY(SetTextColor(::GetSysColor(COLOR_WINDOWTEXT))); // subclass header control HWND hWnd=GetHeaderCtrlHandle(); if(hWnd!=NULL) { if(!::IsWindow(m_wndHdr.GetSafeHwnd())) VERIFY(m_wndHdr.SubclassWindow(hWnd)); else ASSERT(m_wndHdr.GetSafeHwnd()==hWnd); } m_bInit=TRUE; return TRUE; } void COXTreeCtrl::PreSubclassWindow() { _AFX_THREAD_STATE* pThreadState=AfxGetThreadState(); // hook not already in progress if(pThreadState->m_pWndInit==NULL) { VERIFY(Init()); } CListCtrl::PreSubclassWindow(); DWORD dwStyle=CListCtrl::GetStyle(); ASSERT(dwStyle & LVS_REPORT); ASSERT(dwStyle & LVS_OWNERDRAWFIXED); ASSERT(!(dwStyle & LVS_SORTASCENDING)); ASSERT(!(dwStyle & LVS_SORTDESCENDING)); if(dwStyle & LVS_SHOWSELALWAYS) m_dwTCStyle |= TVS_SHOWSELALWAYS; if(dwStyle & LVS_EDITLABELS) m_dwTCStyle |= TVS_EDITLABELS; if(dwStyle & LVS_NOCOLUMNHEADER) m_dwExStyle &= ~TVOXS_COLUMNHDR; else m_dwExStyle |= TVOXS_COLUMNHDR; if(dwStyle & LVS_SINGLESEL) m_dwExStyle &= ~TVOXS_MULTISEL; else m_dwExStyle |= TVOXS_MULTISEL; if(dwStyle & LVS_NOSCROLL) m_dwExStyle |= TVOXS_NOSCROLL; else m_dwExStyle &= ~TVOXS_NOSCROLL; if(dwStyle & LVS_NOSORTHEADER) m_dwExStyle |= TVOXS_NOSORTHEADER; else m_dwExStyle &= ~TVOXS_NOSORTHEADER; if(dwStyle & LVS_SHAREIMAGELISTS) m_dwExStyle |= TVOXS_SHAREIMAGELISTS; else m_dwExStyle &= ~TVOXS_SHAREIMAGELISTS; #ifndef OX_TREECTRL_NOITEMTIPS m_dwExStyle |= TVOXS_ITEMTIPS; #endif } int COXTreeCtrl::GetItemIndent(COXTreeItem *pItem) const { if (IsPropertiesWnd()) { // We have a properies window if((HasLines() || HasButtons()) && HasLinesAtRoot()) return m_nIndent; else return 0; } int nLevel=pItem->GetItemLevel(); int nIndent; if((HasLines() || HasButtons()) && HasLinesAtRoot()) nIndent=m_nIndent *nLevel; else nIndent=m_nIndent *(nLevel-1); return nIndent; } void COXTreeCtrl::GetItemPartRect(COXTreeCtrl::eItemPartRect eType, COXTreeItem *pItem,CRect *rTarget, int *nIndent) const { ASSERT_POINTER(pItem,COXTreeItem); if((*nIndent) == -1) (*nIndent)=GetItemIndent(pItem); int nIndex=GetItemIndexInternal(pItem); CRect rcItem; CListCtrl::GetItemRect(nIndex,&rcItem,LVIR_BOUNDS); CImageList *pImgList=CListCtrl::GetImageList(LVSIL_SMALL); CImageList *pStateImgList=CListCtrl::GetImageList(LVSIL_STATE); CRect rcLastIndent=rcItem; if(*nIndent) { rcLastIndent.right=rcItem.left + *nIndent; rcLastIndent.left=rcLastIndent.right - m_nIndent; } else { rcLastIndent.SetRectEmpty(); } switch(eType) { case rButton://button rect { if(!HasButtons()) { rTarget->SetRectEmpty(); } else { (*rTarget)=rcLastIndent; int nSide=(rcLastIndent.Height()*5)/10; int offx =(rcLastIndent.Width() - nSide)/2; int offy=(rcLastIndent.Height() - nSide)/2; rTarget->top += offy; rTarget->bottom -= offy; rTarget->left += offx; rTarget->right -= offx; } } break; case rImage:// item image rect { if(pImgList == NULL) { // no image list rTarget->SetRectEmpty(); return; } int nStateOffsetX; IMAGEINFO imgInfo; if(pStateImgList && GetItemStateIndex(nIndex) != -1) { pStateImgList->GetImageInfo(0,&imgInfo); nStateOffsetX=imgInfo.rcImage.right - imgInfo.rcImage.left; } else nStateOffsetX=0; pImgList->GetImageInfo(0,&imgInfo); CRect rcImage(imgInfo.rcImage); rTarget->left=rcItem.left + *nIndent + nStateOffsetX; rTarget->top=rcItem.bottom - rcImage.Height(); rTarget->right=rTarget->left + rcImage.Width(); rTarget->bottom=rcItem.bottom; } break; case rStateImage: { if(pStateImgList == NULL) rTarget->SetRectEmpty(); else { IMAGEINFO imgInfo; pStateImgList->GetImageInfo(0,&imgInfo); CRect rcStateImage(imgInfo.rcImage); rTarget->left=rcItem.left + *nIndent; rTarget->top=rcItem.bottom - rcStateImage.Height(); rTarget->right=rTarget->left + rcStateImage.Width(); rTarget->bottom=rcItem.bottom; } } break; case rText: { CSize szText=GetColTextExtent(nIndex,0); CRect rcText; LV_COLUMN lvc; memset(&lvc,0,sizeof(LV_COLUMN)); lvc.mask=LVCF_FMT; ASSERT(GetColumn(0, &lvc)); int nImgWidth=0; IMAGEINFO imgInfo; if(pImgList) { pImgList->GetImageInfo(0,&imgInfo); CRect rcImg(imgInfo.rcImage); nImgWidth += rcImg.Width(); } if(pStateImgList && GetItemStateIndex(nIndex) != -1) { pStateImgList->GetImageInfo(0,&imgInfo); CRect rcImg(imgInfo.rcImage); nImgWidth += rcImg.Width(); } rcText.top=rcItem.top; rcText.bottom=rcItem.bottom; szText.cx=__min(szText.cx,GetColumnWidth(0)-nImgWidth-(*nIndent)); int fmt=lvc.fmt & LVCFMT_JUSTIFYMASK; switch(fmt) { case LVCFMT_LEFT: { rcText.left=rcItem.left + (*nIndent) + nImgWidth; rcText.right=rcText.left + szText.cx; } break; case LVCFMT_RIGHT: { rcText.right=rcItem.left + GetColumnWidth(0); rcText.left=rcText.right - szText.cx; } break; case LVCFMT_CENTER: { int dx=(GetColumnWidth(0)-nImgWidth-(*nIndent)-szText.cx)/2; if(dx < 0) dx=0; rcText.left=rcItem.left + (*nIndent) + nImgWidth + dx; rcText.right=rcText.left + GetColumnWidth(0) - dx; } break; default: ASSERT(FALSE); } (*rTarget)=rcText; } break; default: // unknown kind of rect passed! ASSERT(FALSE); break; } } CSize COXTreeCtrl::GetColTextExtent(int nIndex,int nCol) const // --- In : // --- Out : // --- Returns :dimension of specified item's text //--- Effect : { ASSERT(nCol <= GetColumnsCount());//column number must be valid! COXTreeItem *xti=(COXTreeItem*)CListCtrl::GetItemData(nIndex); ASSERT_POINTER(xti,COXTreeItem); CString sText=GetItemText((HTREEITEM) xti,nCol); CWnd* pWnd=(CWnd*)this; CDC *pDC=pWnd->GetDC(); CFont * pOldFont=NULL; if(xti->HasFont(nCol)) pOldFont=pDC->SelectObject(xti->GetItemFont(nCol)); CSize sz=pDC->GetTextExtent(sText); sz.cx=__min(sz.cx,GetColumnWidth(nCol)); if(xti->HasFont(nCol)) pDC->SelectObject(pOldFont); pWnd->ReleaseDC(pDC); return sz; } int COXTreeCtrl::GetColumnsCount() const { int cnt=0; LV_COLUMN lvc; memset(&lvc, 0, sizeof(LV_COLUMN)); lvc.mask=LVCF_FMT; while(GetColumn(cnt, &lvc)) cnt++; return cnt; } void COXTreeCtrl::DrawItemImages(CDC *pDC, COXTreeItem* xti, int nItem, CRect& rcItem, int nCol, BOOL bRowSel) { UNREFERENCED_PARAMETER(nItem); CImageList *pStateImgList=CListCtrl::GetImageList(LVSIL_STATE); CImageList *pImgList=CListCtrl::GetImageList(LVSIL_SMALL); IMAGEINFO imi; CRect rcErase; CRect rcImage; CPoint ptStart; if(pStateImgList && nCol == 0) { int idx=STATEIMAGEMASKTOINDEX(xti->m_tvi.state)-1; if(idx != -1) { // ask the image size pStateImgList->GetImageInfo(idx,&imi); // clear the entry image rect rcErase=rcItem; rcImage=imi.rcImage; rcErase.right=rcErase.left + rcImage.Width(); /* The COXTreeCtrl did not paint checkboxes properly when the control lost focus, in this situation all check boxes would be painted with the background of COLOR_BTNFACE. pDC->FillSolidRect( &rcErase,((GetFocus()!=this || !IsWindowEnabled()) ? ::GetSysColor(COLOR_BTNFACE) : GetTextBkColor()));*/ ptStart=rcItem.TopLeft(); if(rcImage.Height()Draw(pDC,idx,ptStart,ILD_TRANSPARENT); // Move left of the item rect to draw text later rcItem.left += CRect(imi.rcImage).Width(); } } if(nCol == 0) { TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); // retreive item's images and state tvi.mask=TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_HANDLE | TVIF_STATE; tvi.stateMask=TVIS_SELECTED; tvi.hItem=(HTREEITEM)xti; if(!GetItem(&tvi)) return; if(pImgList) { int nImg; if(tvi.state & TVIS_SELECTED) { nImg=tvi.iSelectedImage; } else { nImg=tvi.iImage; } if(nImg==-1) { return; } // ask the image size pImgList->GetImageInfo(nImg,&imi); // clear the entry image rect rcErase=rcItem; rcImage=imi.rcImage; rcErase.right=rcErase.left + rcImage.Width(); if (xti->HasBackColor(nCol)) pDC->FillSolidRect(&rcErase, (!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : xti->GetItemBackColor(nCol))); else pDC->FillSolidRect(&rcErase, (!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : GetTextBkColor())); ptStart=rcItem.TopLeft(); if(rcImage.Height()Draw(pDC,nImg,ptStart,ILD_TRANSPARENT); rcItem.left += CRect(imi.rcImage).Width(); } } else { int nImg=xti->GetSubItemImage(nCol); if(nImg >= 0) { ASSERT(nImg < pImgList->GetImageCount()); COLORREF clrBack=CLR_NONE; pImgList->GetImageInfo(nImg,&imi); // ask the image size pImgList->GetImageInfo(nImg,&imi); // clear the entry image rect rcErase=rcItem; rcImage=imi.rcImage; rcErase.right=rcErase.left + CRect(imi.rcImage).Width(); // if entry row selection mode set, set subitem image // background color if(bRowSel) { if (xti->HasBackColor(nCol)) { clrBack=pImgList->SetBkColor( (GetFocus()!=this || !IsWindowEnabled()) ? ::GetSysColor(COLOR_BTNFACE) : xti->GetItemBackColor(nCol)); COLORREF clrBkDC=pDC->GetBkColor(); pDC->FillSolidRect(&rcErase, (GetFocus()!=this || !IsWindowEnabled()) ? ::GetSysColor(COLOR_BTNFACE) : xti->GetItemBackColor(nCol)); pDC->SetBkColor(clrBkDC); } else { clrBack=pImgList->SetBkColor( (GetFocus()!=this || !IsWindowEnabled()) ? ::GetSysColor(COLOR_BTNFACE) : ::GetSysColor(COLOR_HIGHLIGHT)); COLORREF clrBkDC=pDC->GetBkColor(); pDC->FillSolidRect(&rcErase, (GetFocus()!=this || !IsWindowEnabled()) ? ::GetSysColor(COLOR_BTNFACE) : ::GetSysColor(COLOR_HIGHLIGHT)); pDC->SetBkColor(clrBkDC); } } else { if (xti->HasBackColor(nCol)) pDC->FillSolidRect(&rcErase, (!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : xti->GetItemBackColor(nCol))); else pDC->FillSolidRect(&rcErase,(!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : GetTextBkColor())); } ptStart=rcItem.TopLeft(); if(rcImage.Height()Draw(pDC,nImg,ptStart,bRowSel ? ILD_FOCUS : ILD_TRANSPARENT); if(bRowSel) { pImgList->SetBkColor(clrBack); } rcItem.left += CRect(imi.rcImage).Width(); } } } BOOL COXTreeCtrl::SetItem(TV_ITEM *pTVI) { // user must provide a valid handle ASSERT(pTVI->mask & TVIF_HANDLE); COXTreeItem *xti=GetXItem(pTVI->hItem); ASSERT(xti); UINT uMaskCb=0;// mask for send TVN_SETDISPINFO if(pTVI->mask & TVIF_IMAGE) { xti->m_tvi.iImage=pTVI->iImage; if(pTVI->iImage == I_IMAGECALLBACK) uMaskCb |= TVIF_IMAGE; } if(pTVI->mask & TVIF_TEXT) { if(pTVI->pszText == LPSTR_TEXTCALLBACK) { if(xti->m_tvi.pszText && xti->m_tvi.pszText != LPSTR_TEXTCALLBACK) delete [] xti->m_tvi.pszText; xti->m_tvi.pszText=LPSTR_TEXTCALLBACK; uMaskCb |= TVIF_TEXT; } else { // allocate space for new item text and copy string TCHAR * pChar=new TCHAR[_tcslen(pTVI->pszText) + 1]; UTBStr::tcscpy(pChar, _tcslen(pTVI->pszText) + 1, pTVI->pszText); // The user specified new text for item if(xti->m_tvi.pszText && xti->m_tvi.pszText != LPSTR_TEXTCALLBACK) delete [] xti->m_tvi.pszText; xti->m_tvi.pszText=pChar; } } if (pTVI->mask & TVIF_STATE) { xti->m_tvi.state &= ~(pTVI->stateMask); xti->m_tvi.state |= pTVI->state; if (pTVI->stateMask & TVIS_STATEIMAGEMASK) { xti->m_tvi.state |= (pTVI->state & TVIS_STATEIMAGEMASK); } if (pTVI->stateMask & TVIS_OVERLAYMASK) { xti->m_tvi.state |= (pTVI->state & TVIS_OVERLAYMASK) ? TVIS_OVERLAYMASK : 0; } if (pTVI->stateMask & TVIS_CUT) { xti->m_tvi.state |= (pTVI->state & TVIS_CUT) ? TVIS_CUT : 0; } if (pTVI->stateMask & TVIS_DROPHILITED) { xti->m_tvi.state |= (pTVI->state & TVIS_DROPHILITED) ? TVIS_DROPHILITED : 0; } if(pTVI->stateMask & TVIS_EXPANDED) { xti->m_tvi.state |= (pTVI->state & TVIS_EXPANDED) ? TVIS_EXPANDED : 0; } } if(pTVI->mask & TVIF_PARAM) { xti->m_tvi.lParam=pTVI->lParam; } if(pTVI->mask & TVIF_SELECTEDIMAGE) { xti->m_tvi.iSelectedImage=pTVI->iSelectedImage; if(pTVI->iSelectedImage == I_IMAGECALLBACK) uMaskCb |= TVIF_SELECTEDIMAGE; } if(pTVI->mask & TVIF_CHILDREN) { xti->m_tvi.cChildren=pTVI->cChildren; } if(uMaskCb != 0) { // If any callback detected, send TVN_SETDISPINFO notification to parent pTVI->mask=uMaskCb; SendSetDispInfoNotify(pTVI); } // Now translate TVIF_xxx to LVIF_xxx int nItem=GetItemIndexInternal(xti); if(nItem != -1) // item currently in list ctrl { LV_ITEM lvi; memset(&lvi, 0, sizeof(LV_ITEM)); lvi.iItem=nItem; lvi.iSubItem=0; if (pTVI->mask & TVIF_IMAGE) { lvi.mask |= LVIF_IMAGE; lvi.iImage=pTVI->iImage; } if (pTVI->mask & TVIF_STATE) { lvi.mask |= LVIF_STATE; if (pTVI->stateMask & TVIS_STATEIMAGEMASK) { lvi.state |= (pTVI->state & TVIS_STATEIMAGEMASK); lvi.stateMask |= LVIS_STATEIMAGEMASK; } if (pTVI->stateMask & TVIS_SELECTED) { lvi.state |= (pTVI->state & TVIS_SELECTED) ? LVIS_SELECTED : 0; lvi.stateMask |= LVIS_SELECTED; } if (pTVI->stateMask & TVIS_OVERLAYMASK) { lvi.state |= (pTVI->state & TVIS_OVERLAYMASK) ? LVIS_OVERLAYMASK : 0; lvi.stateMask |= LVIS_OVERLAYMASK; } if (pTVI->stateMask & TVIS_CUT) { lvi.state |= (pTVI->state & TVIS_CUT) ? LVIS_CUT : 0; lvi.stateMask |= LVIS_CUT; } if (pTVI->stateMask & TVIS_DROPHILITED) { lvi.state |= (pTVI->state & TVIS_DROPHILITED) ? LVIS_DROPHILITED : 0; lvi.stateMask |= LVIS_DROPHILITED; } if(pTVI->stateMask & TVIS_EXPANDED) { UINT nCode=(pTVI->state & TVIS_EXPANDED)? TVE_EXPAND : TVE_COLLAPSE; Expand(pTVI->hItem,nCode); } } if(!CListCtrl::SetItem(&lvi)) return FALSE; } if((m_hOldDropTarget!=pTVI->hItem) && (nItem!=-1) && (pTVI->mask&TVIF_STATE) && (pTVI->stateMask&TVIS_DROPHILITED) && (pTVI->state&TVIS_DROPHILITED)) { if(m_hOldDropTarget!=NULL) { SetItemState(m_hOldDropTarget,0,TVIS_DROPHILITED); } m_hOldDropTarget=pTVI->hItem; UpdateWindow(); } return TRUE; } void COXTreeCtrl::SendSetDispInfoNotify(TV_ITEM* pTVI) { TV_DISPINFO di; di.hdr.code =TVN_SETDISPINFO; di.hdr.hwndFrom=GetSafeHwnd(); di.hdr.idFrom =GetDlgCtrlID(); di.item=*pTVI; //Now send TVN_SETDISPINFO notification message to parent GetParent()->SendMessage(WM_NOTIFY,di.hdr.idFrom,(LPARAM)&di); } BOOL COXTreeCtrl::SetItem(HTREEITEM hItem, UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam) { UNREFERENCED_PARAMETER(nStateMask); UNREFERENCED_PARAMETER(nState); TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); tvi.mask=nMask; tvi.hItem=hItem; tvi.pszText=(LPTSTR)lpszItem; tvi.iImage=nImage; tvi.iSelectedImage=nSelectedImage; tvi.lParam=lParam; return SetItem(&tvi); } BOOL COXTreeCtrl::GetItem(TV_ITEM *pTVI) const { COXTreeItem *xti=GetXItem(pTVI->hItem); ASSERT(xti); UINT uMaskCb=0;// mask for send TVN_GEIDISPINFO if nesessary if(pTVI->mask & TVIF_TEXT) { if(xti->m_tvi.pszText == LPSTR_TEXTCALLBACK) { uMaskCb |= TVIF_TEXT; } else if(xti->m_tvi.pszText) { UTBStr::tcsncpy(pTVI->pszText, pTVI->cchTextMax, xti->m_tvi.pszText,pTVI->cchTextMax); } } if(pTVI->mask & TVIF_IMAGE) { if(xti->m_tvi.iImage == I_IMAGECALLBACK) uMaskCb |= TVIF_IMAGE; pTVI->iImage=xti->m_tvi.iImage; } if(pTVI->mask & TVIF_PARAM) pTVI->lParam=xti->m_tvi.lParam; if(pTVI->mask & TVIF_SELECTEDIMAGE) { if(xti->m_tvi.iSelectedImage == I_IMAGECALLBACK) uMaskCb |= TVIF_SELECTEDIMAGE; pTVI->iSelectedImage=xti->m_tvi.iSelectedImage; } if (pTVI->mask & TVIF_STATE) { int idx=GetItemIndexInternal(xti); xti->m_tvi.state &= ~(TVIS_SELECTED | LVIS_FOCUSED | TVIS_EXPANDED | TVIS_EXPANDEDONCE); if(idx != -1) { UINT uStt=CListCtrl::GetItemState(idx,LVIS_SELECTED | LVIS_FOCUSED); if(uStt & LVIS_SELECTED) xti->m_tvi.state |= TVIS_SELECTED; if(uStt & LVIS_FOCUSED) xti->m_tvi.state |= LVIS_FOCUSED; } if(xti->IsExpanded()) xti->m_tvi.state |= TVIS_EXPANDED; if(xti->IsExpandedOnce()) xti->m_tvi.state |= TVIS_EXPANDEDONCE; pTVI->state=(xti->m_tvi.state & pTVI->stateMask); } if(pTVI->mask & TVIF_CHILDREN) { pTVI->cChildren=xti->m_tvi.cChildren; } if(uMaskCb != 0) { pTVI->mask=uMaskCb; pTVI->lParam=xti->m_tvi.lParam; SendGetDispInfoNotify(pTVI); } return TRUE; } void COXTreeCtrl::SendGetDispInfoNotify(TV_ITEM *pTVI) const { TV_DISPINFO di; di.hdr.code =TVN_GETDISPINFO; di.hdr.hwndFrom=GetSafeHwnd(); di.hdr.idFrom =GetDlgCtrlID(); di.item=*pTVI; //Now send TVN_GETDISPINFO notification message to parent GetParent()->SendMessage(WM_NOTIFY,di.hdr.idFrom,(LPARAM)&di); // save the changed item info *pTVI=di.item; } CString COXTreeCtrl::GetItemText(HTREEITEM hItem,int nCol) const { static TCHAR t[_OX_MAX_ITEM_TEXT]; ASSERT(nCol >= 0); // make sure that the string buffer is empty t[0] = 0; if(nCol == 0) { TV_ITEM item; item.hItem=hItem; item.mask=TVIF_TEXT; item.cchTextMax=sizeof(t)/sizeof(TCHAR); item.pszText=t; GetItem(&item); return t; } else { COXTreeItem * xti=GetXItem(hItem); if(xti) return xti->GetSubItemText(nCol); } return _T(""); } BOOL COXTreeCtrl::GetItemImage(HTREEITEM hItem,int &nImg,int &nSelImg,int nCol) { if(nCol == 0) { TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); tvi.mask=TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_HANDLE; tvi.hItem=hItem; if(!GetItem(&tvi)) return FALSE; nImg=tvi.iImage; nSelImg=tvi.iSelectedImage; return TRUE; } else { COXTreeItem* xti=GetXItem(hItem); if(!xti) return FALSE; int nIm=xti->GetSubItemImage(nCol); if(nIm == -1) return FALSE; nImg=nIm; nSelImg=0; return TRUE; } } BOOL COXTreeCtrl::SetItemImage(HTREEITEM hItem, int nImage, int nSelectedImage, int nCol) { if(nCol == 0) { TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); tvi.mask=TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_HANDLE; tvi.hItem=hItem; tvi.iImage=nImage; tvi.iSelectedImage=nSelectedImage; return SetItem(&tvi); } else { if(nImage!=nSelectedImage) { TRACE(_T("COXTreeCtrl::SetItemImage: the selected image is not supported for subitems, ignored\n")); } return SetSubItem(hItem,nCol,OX_SUBITEM_IMAGE,NULL,nImage); } } BOOL COXTreeCtrl::GetItemDrawEllipsis(HTREEITEM hItem, UINT& nEllipsisFormat, int nCol/*=0*/) const { COXTreeItem* xti=GetXItem(hItem); if(!xti) return FALSE; nEllipsisFormat=xti->GetDrawEllipsis(nCol); return TRUE; } BOOL COXTreeCtrl::SetItemDrawEllipsis(HTREEITEM hItem, UINT nEllipsisFormat, int nCol/*=0*/) { COXTreeItem* xti=GetXItem(hItem); if(!xti) return FALSE; xti->SetDrawEllipsis(nEllipsisFormat,nCol); return TRUE; } BOOL COXTreeCtrl::ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags/*=0*/) { DWORD dwLCStyleAdd=0;// list control style to add DWORD dwLCStyleRemove=0;// list control style to remove if(dwRemove & TVS_SHOWSELALWAYS) dwLCStyleRemove |= LVS_SHOWSELALWAYS; if(dwRemove & TVS_EDITLABELS) dwLCStyleRemove |= LVS_EDITLABELS; if(dwAdd & TVS_SHOWSELALWAYS) dwLCStyleAdd |= LVS_SHOWSELALWAYS; if(dwAdd & TVS_EDITLABELS) dwLCStyleAdd |= LVS_EDITLABELS; // add common window styles dwLCStyleAdd |= (dwAdd & 0xffff0000); dwLCStyleRemove |= (dwRemove & 0xffff0000); if(dwLCStyleRemove || dwLCStyleAdd) if(!CListCtrl::ModifyStyle(dwLCStyleRemove,dwLCStyleAdd,nFlags)) return FALSE; DWORD dwStyleOld=m_dwTCStyle; m_dwTCStyle |= dwAdd; m_dwTCStyle &= ~dwRemove; if(dwStyleOld != m_dwTCStyle) { Invalidate(); } return TRUE; } DWORD COXTreeCtrl::GetStyle() const { DWORD dwStyle=m_dwTCStyle | (IsWindowVisible()? 0x10000000L :0); return dwStyle; } BOOL COXTreeCtrl::ModifyExStyle(DWORD dwExStyleRemove,DWORD dwExStyleAdd, UINT nFlags/*=0*/) { DWORD dwLCStyleAdd=0;// list control style to add DWORD dwLCStyleRemove=0;// list control style to remove if(dwExStyleAdd & TVOXS_MULTISEL) dwLCStyleRemove |= LVS_SINGLESEL; if(dwExStyleRemove & TVOXS_MULTISEL) dwLCStyleAdd |= LVS_SINGLESEL; if(dwExStyleAdd & TVOXS_COLUMNHDR) dwLCStyleRemove |= LVS_NOCOLUMNHEADER; if(dwExStyleRemove & TVOXS_COLUMNHDR) dwLCStyleAdd |= LVS_NOCOLUMNHEADER; if(dwExStyleAdd & TVOXS_NOSCROLL) dwLCStyleAdd |= LVS_NOSCROLL; if(dwExStyleRemove & TVOXS_NOSCROLL) dwLCStyleRemove |= LVS_NOSCROLL; if(dwExStyleAdd & TVOXS_NOSORTHEADER) dwLCStyleAdd |= LVS_NOSORTHEADER; if(dwExStyleRemove & TVOXS_NOSORTHEADER) dwLCStyleRemove |= LVS_NOSORTHEADER; if(dwExStyleAdd & TVOXS_SHAREIMAGELISTS) dwLCStyleAdd |= LVS_SHAREIMAGELISTS; if(dwExStyleRemove & TVOXS_SHAREIMAGELISTS) dwLCStyleRemove |= LVS_SHAREIMAGELISTS; if(dwLCStyleRemove || dwLCStyleAdd) if(!CListCtrl::ModifyStyle(dwLCStyleRemove,dwLCStyleAdd,nFlags)) return FALSE; if((dwExStyleAdd&TVOXS_ITEMTIPS) && !(m_dwExStyle&TVOXS_ITEMTIPS)) { #ifndef OX_TREECTRL_NOITEMTIPS m_ItemTip.Attach(this); #endif } if((dwExStyleRemove&TVOXS_ITEMTIPS) && (m_dwExStyle&TVOXS_ITEMTIPS)) { #ifndef OX_TREECTRL_NOITEMTIPS m_ItemTip.Detach(); #endif } DWORD dwOld=m_dwExStyle; m_dwExStyle |= dwExStyleAdd; m_dwExStyle &= ~dwExStyleRemove; if(m_dwExStyle != dwOld) Invalidate(); return TRUE; } DWORD COXTreeCtrl::GetExStyle() const { return m_dwExStyle; } int COXTreeCtrl::InsertColumn(int nCol, const LV_COLUMN* pColumn) { BOOL bRet= CListCtrl::InsertColumn(nCol,pColumn); return bRet; } int COXTreeCtrl::InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat, int nWidth, int nSubItem) { BOOL bRet=CListCtrl::InsertColumn(nCol,lpszColumnHeading,nFormat, nWidth,nSubItem); return bRet; } BOOL COXTreeCtrl::SetSubItem(HTREEITEM hItem, int nColumn, UINT uFlags, LPCTSTR lpszText, int nImage, CFont *pFont, COLORREF clr, COLORREF clrBack) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return FALSE; if(nColumn <= 0) return FALSE; BOOL bRet=xti->SetSubItem(nColumn,uFlags,lpszText,nImage,pFont,clr,clrBack); int idx=GetItemIndexInternal(xti); if(bRet && idx != -1) { CListCtrl::RedrawItems(idx,idx); } return bRet; } BOOL COXTreeCtrl::HideItem(HTREEITEM hItem,BOOL bHide) { ASSERT(hItem != TVI_ROOT); if (hItem==TVI_ROOT) return FALSE; COXTreeItem *xti=GetXItem(hItem); if(!xti) return FALSE; if(xti->IsHidden() == bHide) return TRUE; // already hidden SetRedraw(FALSE); BOOL bResult=TRUE; HTREEITEM hPrevVisibleItem=GetPrevVisibleItem(hItem); HTREEITEM hPrevSiblingItem=GetPrevSiblingItem(hItem); xti->SetHidden(bHide); int idx=GetItemIndexInternal(xti); if(idx == -1 && bHide) { bResult=TRUE; } else { BOOL bItemExp=xti->IsExpanded(); if(bHide && !bItemExp) { bResult=CListCtrl::DeleteItem(idx); } else if(bHide && bItemExp) { xti->RemoveChildrenFromCtrl(this); bResult=CListCtrl::DeleteItem(idx); } else if(xti->IsVisible()) { if(!AddItemToCtrl(xti)) { bResult=FALSE; } else { idx=GetItemIndexInternal(xti); ASSERT(idx != -1); if(xti->IsExpanded()) { xti->AddChildrenToCtrl(this,idx + 1); } } } } if(bResult && hPrevVisibleItem!=NULL && hPrevSiblingItem!=NULL) RedrawTreeItems(hPrevSiblingItem,hPrevVisibleItem); SetRedraw(TRUE); return bResult; } BOOL COXTreeCtrl::DeleteItem(HTREEITEM hItem) { HTREEITEM hParentItem=GetParentItem(hItem); TV_ITEM tvi; COXTreeItem *xti=GetXItem(hItem); if(xti != &m_xtiRoot && !HideItem(hItem)) return FALSE; if(xti == &m_xtiRoot) { ::ZeroMemory(&tvi, sizeof(tvi)); tvi.hItem=xti->m_tvi.hItem; SendDeleteItemNotify(&tvi); xti->DeleteChildren(); m_nItemsCount=0; return CListCtrl::DeleteAllItems(); } if(hParentItem!=NULL) { COXTreeItem *xtiParent=GetXItem(hParentItem); ASSERT(xtiParent!=NULL); if(xtiParent->GetChildrenCount()<=1 && xtiParent->m_tvi.cChildren==1) { xtiParent->m_tvi.cChildren=0; if(xtiParent!=&m_xtiRoot) { xtiParent->m_bExpand=FALSE; xtiParent->m_bExpandedOnce=FALSE; } } } memset(&tvi,0,sizeof(tvi)); tvi=xti->m_tvi; tvi.mask |= TVIF_HANDLE; tvi.hItem=(HTREEITEM) xti; SendDeleteItemNotify(&tvi); xti->RemoveChildrenFromCtrl(this); // item should have been already deleted from list ASSERT(GetItemIndexInternal(xti)==-1); delete xti; ASSERT(m_nItemsCount); m_nItemsCount--; if (hParentItem != NULL) { COXTreeItem* pParent = GetXItem(hParentItem); if (pParent->GetChildrenCount() == 0) RedrawTreeItems(hParentItem, hParentItem); } return TRUE; } BOOL COXTreeCtrl::DeleteAllItems() { ASSERT(&m_xtiRoot); return DeleteItem(GetRootItem()); } BOOL COXTreeCtrl::DeleteColumn(int nCol) { if(nCol==0) { if(!DeleteAllItems()) { return FALSE; } while(CListCtrl::DeleteColumn(0)); return TRUE; } if(DeleteSubitems(GetRootItem(),nCol)) { return CListCtrl::DeleteColumn(nCol); } else { return FALSE; } } BOOL COXTreeCtrl::DeleteSubitems(HTREEITEM hParentItem, int nCol) { BOOL bSuccess=TRUE; HTREEITEM hItem=GetNextItem(hParentItem,TVGN_CHILD); while(hItem!=NULL) { COXTreeItem* pItem=GetXItem(hItem); ASSERT(pItem!=NULL); pItem->DeleteSubitem(nCol); if(!DeleteSubitems(hItem,nCol)) { bSuccess=FALSE; break; } hItem=GetNextItem(hItem,TVGN_NEXT); } return (bSuccess); } UINT COXTreeCtrl::GetVisibleCount() const { return CListCtrl::GetItemCount(); } UINT COXTreeCtrl::GetCount() const { return m_nItemsCount; } HTREEITEM COXTreeCtrl::GetNextItem(HTREEITEM hItem, UINT nCode) { COXTreeItem * xti=GetXItem(hItem); int idx; switch(nCode) { case TVGN_CHILD: if(!xti) return NULL; return (HTREEITEM)(xti->pxFirstChild); case TVGN_FIRSTVISIBLE: return GetFirstVisibleItem(); case TVGN_NEXTVISIBLE: return GetNextVisibleItem(hItem,FALSE); case TVGN_PREVIOUSVISIBLE: return GetPrevVisibleItem(hItem,FALSE); case TVGN_DROPHILITE: { if(!xti) idx=-1; else idx=GetItemIndexInternal(xti); int idNext=CListCtrl::GetNextItem(idx,LVNI_DROPHILITED); if(idNext == -1 || idNext >= CListCtrl::GetItemCount()) return NULL; return (HTREEITEM) CListCtrl::GetItemData(idNext); } case TVGN_NEXT: if(!xti) return NULL; return (HTREEITEM)(xti->pxNext); case TVGN_CARET: idx=CListCtrl::GetNextItem(-1,LVNI_FOCUSED); if(idx == -1) { return NULL; } else { HTREEITEM hti=GetItemFromIndex(idx); if(hti && GetItemState(hti,LVIS_SELECTED)==LVIS_SELECTED) { return hti; } else { return NULL; } } case TVGN_PARENT: if(!xti) return NULL; return (HTREEITEM) (xti->pxParent); case TVGN_PREVIOUS: if(!xti) return NULL; return (HTREEITEM) (xti->pxPrev); case TVGN_ROOT: if(!xti) return NULL; return (HTREEITEM)(xti->pxParent ? xti->pxParent->pxFirstChild : NULL); case TVGN_FIRSTSELECTED: idx=CListCtrl::GetNextItem(-1,LVNI_SELECTED); if(idx == -1) return NULL; return (HTREEITEM) CListCtrl::GetItemData(idx); case TVGN_NEXTSELECTED: if(!xti) return NULL; idx=GetItemIndexInternal(xti); if(idx == -1) return NULL; idx=CListCtrl::GetNextItem(idx,LVNI_SELECTED); if(idx == -1) return NULL; return (HTREEITEM) CListCtrl::GetItemData(idx); default: ASSERT(FALSE); return NULL; } } HTREEITEM COXTreeCtrl::GetFirstVisibleItem() { int idx=CListCtrl::GetTopIndex(); if(idx == -1 || idx >= CListCtrl::GetItemCount()) return NULL; return (HTREEITEM) CListCtrl::GetItemData(idx); } HTREEITEM COXTreeCtrl::GetNextVisibleItem(HTREEITEM hItem, BOOL bCurrentlyViewable/*=FALSE*/) { COXTreeItem *xti=GetXItem(hItem); // walk down throuth tree structure and find the next item in list ctrl if(!xti) { return NULL; } CRect rect; GetClientRect(rect); HTREEITEM hItemFirstVisible=GetFirstVisibleItem(); ASSERT(hItemFirstVisible!=NULL); CRect rectFistVisible; GetItemRect(hItemFirstVisible,rectFistVisible,FALSE); rect.top=rectFistVisible.top; xti=xti->GetNextInTree(); for(; xti ; xti=xti->GetNextInTree()) { int idx=GetItemIndexInternal(xti); if(idx != -1) { if(bCurrentlyViewable) { CRect rectItem; CListCtrl::GetItemRect(idx,rectItem,LVIR_BOUNDS); if((rectItem.top>=rect.top && rectItem.top<=rect.bottom) || (rectItem.bottom>=rect.top && rectItem.bottom<=rect.bottom)) { return (HTREEITEM) CListCtrl::GetItemData(idx); } break; } else { return (HTREEITEM) CListCtrl::GetItemData(idx); } } } return NULL; } HTREEITEM COXTreeCtrl::GetPrevVisibleItem(HTREEITEM hItem, BOOL bCurrentlyViewable/*=FALSE*/) { COXTreeItem *xti=GetXItem(hItem); // walk up throuth tree structure and find the item in list ctrl if(!xti) return NULL; CRect rect; GetClientRect(rect); HTREEITEM hItemFirstVisible=GetFirstVisibleItem(); if(hItemFirstVisible==NULL) return NULL; CRect rectFistVisible; GetItemRect(hItemFirstVisible,rectFistVisible,FALSE); rect.top=rectFistVisible.top; xti=xti->GetPrevInTree(); for(; xti ; xti=xti->GetPrevInTree()) { int idx=GetItemIndexInternal(xti); if(idx != -1) { if(bCurrentlyViewable) { CRect rectItem; CListCtrl::GetItemRect(idx,rectItem,LVIR_BOUNDS); if((rectItem.top>=rect.top && rectItem.top<=rect.bottom) || (rectItem.bottom>=rect.top && rectItem.bottom<=rect.bottom)) { return (HTREEITEM) CListCtrl::GetItemData(idx); } break; } else { return (HTREEITEM) CListCtrl::GetItemData(idx); } } } return NULL; } HTREEITEM COXTreeCtrl::GetFirstHiddenItem() { COXTreeItem *xti=&m_xtiRoot;// start form root item xti=xti->GetNextInTree(); // now iterate the tree and find first hidden item for(; xti ; xti=xti->GetNextInTree()) { if(xti->IsHidden()) return (HTREEITEM) xti; } return NULL; } HTREEITEM COXTreeCtrl::GetNextHiddenItem(HTREEITEM hItem) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return NULL; xti=xti->GetNextInTree(); // now iterate the tree and stop when a hidden item found for(; xti ; xti=xti->GetNextInTree()) { if(xti->IsHidden()) return (HTREEITEM) xti; } return NULL; } HTREEITEM COXTreeCtrl::GetPrevHiddenItem(HTREEITEM hItem) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return NULL; xti=xti->GetPrevInTree(); // now iterate the tree and stop when a hidden item found for(; xti ; xti=xti->GetPrevInTree()) { if(xti->IsHidden()) return (HTREEITEM) xti; } return NULL; } HTREEITEM COXTreeCtrl::GetSelectedItem() { return GetNextItem(TVI_ROOT,TVGN_FIRSTSELECTED); } CImageList* COXTreeCtrl::GetImageList(UINT nImage) { UINT nListImg; switch(nImage) { case TVSIL_NORMAL: nListImg=LVSIL_SMALL; break; case TVSIL_STATE: nListImg=LVSIL_STATE; default: ASSERT(FALSE); return NULL; } return CListCtrl::GetImageList(nListImg); } UINT COXTreeCtrl::GetIndent() const { return m_nIndent; } void COXTreeCtrl::SetIndent(UINT nIndent) { nIndent=nIndent>TV_MININDENT ? nIndent : TV_MININDENT; m_nIndent=nIndent; RedrawWindow(); } BOOL COXTreeCtrl::ItemHasChildren(HTREEITEM hItem) const { COXTreeItem *xti=GetXItem(hItem); if(!xti) return FALSE; return xti->ItemHasChildren(); } HTREEITEM COXTreeCtrl::GetChildItem(HTREEITEM hItem) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return NULL; return (HTREEITEM) (xti->pxFirstChild); } HTREEITEM COXTreeCtrl::GetNextSiblingItem(HTREEITEM hItem) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return NULL; return (HTREEITEM) (xti->pxNext); } HTREEITEM COXTreeCtrl::GetPrevSiblingItem(HTREEITEM hItem) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return NULL; return (HTREEITEM) (xti->pxPrev); } HTREEITEM COXTreeCtrl::GetParentItem(HTREEITEM hItem) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return NULL; return (HTREEITEM) (xti->pxParent); } HTREEITEM COXTreeCtrl::GetDropHilightItem() { int idx=CListCtrl::GetNextItem(-1,LVNI_DROPHILITED); if(idx == -1 || idx >= CListCtrl::GetItemCount()) return NULL; return (HTREEITEM) CListCtrl::GetItemData(idx); } HTREEITEM COXTreeCtrl::GetRootItem() const { return (HTREEITEM)(&m_xtiRoot); } UINT COXTreeCtrl::GetItemState(HTREEITEM hItem, UINT nStateMask) const { TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); tvi.mask=TVIF_HANDLE | TVIF_STATE; tvi.hItem=hItem; tvi.stateMask=nStateMask; VERIFY(GetItem(&tvi)); return tvi.state; } BOOL COXTreeCtrl::SetItemState(HTREEITEM hItem, UINT nState, UINT nStateMask) { TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); tvi.mask=TVIF_HANDLE | TVIF_STATE; tvi.hItem=hItem; tvi.stateMask=nStateMask; tvi.state=nState; return SetItem(&tvi); } DWORD COXTreeCtrl::GetItemData(HTREEITEM hItem) const { TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); tvi.mask=TVIF_HANDLE | TVIF_PARAM; tvi.hItem=hItem; VERIFY(GetItem(&tvi)); return (DWORD)tvi.lParam; } BOOL COXTreeCtrl::SetItemData(HTREEITEM hItem, DWORD dwData) { TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); tvi.mask=TVIF_HANDLE | TVIF_PARAM; tvi.hItem=hItem; tvi.lParam=dwData; return SetItem(&tvi); } BOOL COXTreeCtrl::GetItemRect(HTREEITEM hItem, LPRECT lpRect, BOOL bTextOnly) const { COXTreeItem *xti=GetXItem(hItem); if(!xti) return FALSE; int idx=GetItemIndexInternal(xti); if(idx == -1 || idx >= CListCtrl::GetItemCount()) return FALSE; CRect r; if(bTextOnly) { int nIndent=-1; GetItemPartRect(rText,xti,&r,&nIndent); ::SetRect(lpRect,r.left,r.top,r.right,r.bottom); return TRUE; } return CListCtrl::GetItemRect(idx,lpRect,LVIR_BOUNDS); } BOOL COXTreeCtrl::SetItemColor(HTREEITEM hItem,COLORREF clr,int nCol) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return FALSE; int idx=GetItemIndexInternal(xti); if(!nCol) { xti->SetColor(clr); } else { COXTreeSubItem *pSubItem=xti->GetSubItem(nCol); if(!pSubItem) return FALSE; pSubItem->SetColor(clr); } if(idx != -1) CListCtrl::RedrawItems(idx,idx); return TRUE; } BOOL COXTreeCtrl::SetItemBackColor(HTREEITEM hItem,COLORREF clr,int nCol) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return FALSE; int idx=GetItemIndexInternal(xti); if(!nCol) { xti->SetBackColor(clr); } else { COXTreeSubItem *pSubItem=xti->GetSubItem(nCol); if(!pSubItem) return FALSE; pSubItem->SetBackColor(clr); } if(idx != -1) CListCtrl::RedrawItems(idx,idx); return TRUE; } COLORREF COXTreeCtrl::GetItemColor(HTREEITEM hItem,int nCol) const { COXTreeItem *xti=GetXItem(hItem); if(!xti) return 0; if(xti->HasColor(nCol)) return xti->GetItemColor(nCol); return ::GetSysColor(COLOR_BTNTEXT); } CFont* COXTreeCtrl::GetItemFont(HTREEITEM hItem,int nCol) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return NULL; return xti->GetItemFont(nCol); } BOOL COXTreeCtrl::SetItemFont(HTREEITEM hItem,CFont *pFont,int nCol) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return FALSE; BOOL bRes=xti->SetFont(nCol,pFont); int idx=GetItemIndexInternal(xti); if(bRes && idx != -1 && idx < CListCtrl::GetItemCount()) CListCtrl::RedrawItems(idx,idx); return bRes; } BOOL COXTreeCtrl::OnBeginlabeledit(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pDispInfo= (LV_DISPINFO*) pNMHDR; *pResult = 0; // We need to fire TVN_BEGINLABELEDIT Notification NMTVDISPINFO tvdi; ::memset(&tvdi, 0, sizeof(NMTVDISPINFO)); tvdi.hdr.hwndFrom = pNMHDR->hwndFrom; tvdi.hdr.idFrom = pNMHDR->idFrom; tvdi.hdr.code = pNMHDR->code; tvdi.item.mask = TVIF_HANDLE | TVIF_PARAM; tvdi.item.hItem = (HTREEITEM)(INT_PTR) pDispInfo->item.iItem; tvdi.item.lParam = pDispInfo->item.lParam; *pResult = GetParent()->SendMessage(TVN_BEGINLABELEDIT, pNMHDR->idFrom, (LPARAM) &tvdi); int nItem=pDispInfo->item.iItem; HWND hWndEdit=(HWND)::SendMessage(m_hWnd, LVM_GETEDITCONTROL, 0, 0L); if(m_pActiveEditWnd!=NULL && m_pActiveEditWnd->GetSafeHwnd()) return FALSE; ASSERT(nItem != -1); COXTreeItem *xti=(COXTreeItem *) CListCtrl::GetItemData(nItem); ASSERT(xti); if(xti->IsDisabled()) { if(pResult != NULL) *pResult = TRUE; return TRUE; } CRect rcEdit; int nCol=GetEditColumn(xti,&rcEdit); if(nCol == -1) { // No editable column found if(pResult != NULL) *pResult = TRUE; return TRUE; } if(xti->GetEditMode(nCol)==OXET_NOEDIT) { // No editable column found if(pResult != NULL) *pResult = TRUE; return TRUE; } m_nEditColumn=nCol; m_pActiveEditWnd=StartEditSubItem(hWndEdit,xti,rcEdit,nCol,pResult); if (GetParent() != NULL && GetParent()->SendMessage(WM_USER_QUERY_PROPERTIESWND)) return FALSE; else return TRUE; } int COXTreeCtrl::GetEditColumn(COXTreeItem *xti,CRect * rcColText) // --- In : // --- Out : // --- Returns : specified item's column to edit //--- Effect : { // fill rcItem with entry item rect CRect rcItem; int nIndex=GetItemIndexInternal(xti); if(nIndex == -1) return -1; CListCtrl::GetItemRect(nIndex,&rcItem,LVIR_BOUNDS); CImageList *pImgList=CListCtrl::GetImageList(LVSIL_SMALL); CImageList *pStateImgList=CListCtrl::GetImageList(LVSIL_STATE); // Now find the column int nColumnsCount=GetColumnsCount(); int nCol=-1; for(int colnum=0; colnum < nColumnsCount; colnum++) { int nColWidth=GetColumnWidth(colnum); if(m_ptLastClick.x >= rcItem.left && m_ptLastClick.x <= (rcItem.left + nColWidth)) { nCol=colnum; break; } rcItem.left += nColWidth; } if(nCol == -1) return -1;// no column found if(nCol && !xti->GetSubItem(nCol)) return -1; // this item has no subitems at given column rcItem.right=rcItem.left + GetColumnWidth(nCol); int nIndent=nCol == 0 ? GetItemIndent(xti) : 0; // CSize szText=GetColTextExtent(nIndex,nCol); CSize szText=CSize(rcItem.Width(),rcItem.Height()); CRect rcText; LV_COLUMN lvc; memset(&lvc, 0, sizeof(LV_COLUMN)); lvc.mask=LVCF_FMT; VERIFY(GetColumn(nCol, &lvc)); int nImgWidth=0; IMAGEINFO imgInfo; int nImg=xti->GetItemImage(nCol); if(pImgList && nImg != -1) { pImgList->GetImageInfo(0,&imgInfo); CRect rcImg(imgInfo.rcImage); nImgWidth += rcImg.Width(); } if(pStateImgList && nCol == 0 && GetItemStateIndex(nIndex) != -1) { pStateImgList->GetImageInfo(0,&imgInfo); CRect rcImg(imgInfo.rcImage); nImgWidth += rcImg.Width(); } rcText.top=rcItem.top; rcText.bottom=rcItem.bottom; szText.cx=__min(szText.cx,GetColumnWidth(nCol)-nImgWidth-nIndent); int fmt=lvc.fmt & LVCFMT_JUSTIFYMASK; switch(fmt) { case LVCFMT_LEFT: { rcText.left=rcItem.left + nIndent + nImgWidth; rcText.right=rcText.left + szText.cx; } break; case LVCFMT_RIGHT: { rcText.right=rcItem.left + GetColumnWidth(nCol); rcText.left=rcText.right - szText.cx; } break; case LVCFMT_CENTER: { rcItem.left += nIndent + nImgWidth; int nCenter=rcItem.left + (rcItem.right - rcItem.left)/2; rcText.left=nCenter - szText.cx/2; rcText.right=nCenter + szText.cx/2; } break; default: ASSERT(FALSE); } (*rcColText)=rcText; if(rcText.Width() < 6) rcText.InflateRect(4,4); if(!rcText.PtInRect(m_ptLastClick)) return -1; return nCol; } CEdit* COXTreeCtrl::EditLabel(HTREEITEM hti, int nCol/*=0*/) { if(!(GetStyle()&TVS_EDITLABELS)) return NULL; COXTreeItem* xti=GetXItem(hti); if(!xti) return NULL; int nIndex=GetItemIndex(hti); if(nIndex<0) return NULL; CRect rcItem; if(!GetItemRect(hti,&rcItem,TRUE)) return NULL; int nColumnsCount=GetColumnsCount(); if(nCol>=nColumnsCount) return NULL; if(nCol>0) rcItem.left=0; for(int colnum=0; colnum < nCol; colnum++) rcItem.left += GetColumnWidth(colnum); if(nCol>0) rcItem.right=rcItem.left + GetColumnWidth(nCol); m_ptLastClick=rcItem.CenterPoint()-CPoint(GetScrollPos(SB_HORZ),0); if(GetFocus() != this) SetFocus(); return (CEdit*)CWnd::FromHandle((HWND)SendMessage(LVM_EDITLABEL,nIndex)); } CWnd* COXTreeCtrl::StartEditSubItem(HWND hWndEdit,COXTreeItem *xti,CRect& rcEdit, int nCol,LRESULT* pResult) // --- In : // --- Out : // --- Returns : //--- Effect : { CRect rcClient; GetClientRect(&rcClient); int nEditType=xti->GetEditMode(nCol); switch(nEditType) { case OXET_COMBO: { int nLeft=-GetScrollPos(SB_HORZ); for(int i=0;i < nCol;i++) nLeft += GetColumnWidth(i); CRect rcCombo=rcEdit; rcCombo.left=nLeft; CImageList *pImgList=GetImageList(TVSIL_NORMAL); rcCombo.right=rcCombo.left + GetColumnWidth(nCol); if(xti->GetItemImage(nCol) != -1 &&pImgList) { IMAGEINFO ii; pImgList->GetImageInfo(0,&ii); rcCombo.left += CRect(ii.rcImage).Width(); } if(rcCombo.right > rcClient.right) { // perform scrolling CSize szScroll; szScroll.cx=rcCombo.right - rcClient.right; szScroll.cy=0; Scroll(szScroll); rcCombo.right -= szScroll.cx; rcCombo.left -= szScroll.cx; } rcCombo.bottom += rcEdit.Height()*5; (*pResult)=1; m_wndCombo.Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST|WS_VSCROLL|WS_HSCROLL, rcCombo,this,100); m_wndCombo.SetFont(GetFont()); m_wndCombo.Init((HTREEITEM) xti,GetItemIndexInternal(xti),nCol); m_wndCombo.SetFocus(); } return &m_wndCombo; case OXET_EDIT: { m_wndEdit.Init(); LV_COLUMN lvc; lvc.mask=LVCF_FMT; VERIFY(CListCtrl::GetColumn(nCol,&lvc)); DWORD dwAlign=ES_LEFT; CPoint pos; switch(lvc.fmt & LVCFMT_JUSTIFYMASK) { case LVCFMT_LEFT: pos=rcEdit.TopLeft(); dwAlign=ES_LEFT; break; case LVCFMT_RIGHT: pos.x=rcEdit.right; pos.y=rcEdit.top; dwAlign=ES_RIGHT; break; case LVCFMT_CENTER: pos.x=rcEdit.left + rcEdit.Width()/2; pos.y=rcEdit.top; dwAlign=ES_CENTER; break; } // scroll control if column been edited is not visible if(pos.x < 0 || pos.x > rcClient.right) { // perform scrolling CSize szScroll; szScroll.cx=pos.x - rcClient.right; szScroll.cy=0; Scroll(szScroll); pos.x -= szScroll.cx; } m_wndEdit.SubclassWindow(hWndEdit); m_wndEdit.KeepPos(pos,dwAlign); if ( m_dwExStyle & TVOXS_KEEPCOLUMNSIZE ) m_wndEdit.KeepColumnSize(CSize(rcEdit.right,rcEdit.bottom)); else m_wndEdit.KeepBounds(CSize(rcClient.Width(),rcClient.Height())); m_wndEdit.SetWindowText(GetItemText((HTREEITEM)xti,nCol)); return &m_wndEdit; } case OXET_CALENDAR: { int nLeft=-GetScrollPos(SB_HORZ); for(int i=0;i < nCol;i++) nLeft += GetColumnWidth(i); CRect rcCal=rcEdit; rcCal.left=nLeft; CImageList *pImgList=GetImageList(TVSIL_NORMAL); rcCal.right=rcCal.left + GetColumnWidth(nCol); if(xti->GetItemImage(nCol) != -1 &&pImgList) { IMAGEINFO ii; pImgList->GetImageInfo(0,&ii); rcCal.left += CRect(ii.rcImage).Width(); } if(rcCal.right > rcClient.right) { // perform scrolling CSize szScroll; szScroll.cx=rcCal.right - rcClient.right; szScroll.cy=0; Scroll(szScroll); rcCal.right -= szScroll.cx; rcCal.left -= szScroll.cx; } (*pResult)=1; m_wndCal.Create(WS_CHILD | WS_VISIBLE |WS_BORDER,rcCal,this,7); m_wndCal.Init((HTREEITEM)xti,GetItemIndexInternal(xti),nCol); m_wndCal.SetFocus(); return &m_wndCal; } default: return NULL; } } BOOL COXTreeCtrl::IsItemHidden(HTREEITEM hItem) const { COXTreeItem *xti=GetXItem(hItem); if(!xti) return FALSE; return xti->IsHidden(); } BOOL COXTreeCtrl::DisableItem(HTREEITEM hItem,BOOL bDisable) { COXTreeItem *xti=GetXItem(hItem); if(xti->IsDisabled() == bDisable) return TRUE; // already disabled or undisabled xti->SetDisabled(bDisable); int idx=GetItemIndexInternal(xti); if(idx == -1) return TRUE; CListCtrl::RedrawItems(idx,idx); return TRUE; } BOOL COXTreeCtrl::IsItemDisabled(HTREEITEM hItem) const { COXTreeItem *xti=GetXItem(hItem); if(!xti) return FALSE; return xti->IsDisabled(); } BOOL COXTreeCtrl::IsItemDescendant(HTREEITEM hParentItem, HTREEITEM hItem) { ASSERT(hParentItem!=NULL); ASSERT(hItem!=NULL); if(hParentItem==NULL || hItem==NULL) { return FALSE; } HTREEITEM hDirectParent=GetParentItem(hItem); if(hDirectParent==NULL) { return FALSE; } else { return (hParentItem==hDirectParent || IsItemDescendant(hParentItem,hDirectParent)); } } void COXTreeCtrl::SendDeleteItemNotify(TV_ITEM* ptvi) { NM_TREEVIEW nmt; memset(&nmt,0,sizeof(nmt)); nmt.itemOld=*ptvi; nmt.hdr.code=TVN_DELETEITEM; nmt.hdr.idFrom=GetDlgCtrlID(); nmt.hdr.hwndFrom=GetSafeHwnd(); GetParent()->SendMessage(WM_NOTIFY,nmt.hdr.idFrom , (LPARAM)&nmt); } BOOL COXTreeCtrl::SendItemExpandingNotify(HTREEITEM hItem,UINT nCode) { COXTreeItem *xti=GetXItem(hItem); ASSERT(xti); NM_TREEVIEW nmt; memset(&nmt, 0, sizeof(nmt)); nmt.hdr.idFrom=GetDlgCtrlID(); nmt.hdr.hwndFrom=GetSafeHwnd(); nmt.hdr.code=TVN_ITEMEXPANDING; nmt.action=nCode; nmt.itemNew=xti->m_tvi; nmt.itemNew.mask |= TVIF_HANDLE; nmt.itemNew.hItem=(HTREEITEM) xti; return (BOOL)(GetParent()->SendMessage(WM_NOTIFY,nmt.hdr.idFrom,(LPARAM)(&nmt))); } void COXTreeCtrl::SendItemExpandedNotify(HTREEITEM hItem,UINT nCode) { COXTreeItem *xti=GetXItem(hItem); ASSERT(xti); NM_TREEVIEW nmt; memset(&nmt, 0, sizeof(nmt)); nmt.hdr.idFrom=GetDlgCtrlID(); nmt.hdr.hwndFrom=GetSafeHwnd(); nmt.hdr.code=TVN_ITEMEXPANDED; nmt.action=nCode; nmt.itemNew=xti->m_tvi; nmt.itemNew.mask |= TVIF_HANDLE; nmt.itemNew.hItem=(HTREEITEM) xti; GetParent()->SendMessage(WM_NOTIFY,nmt.hdr.idFrom,(LPARAM)(&nmt)); } BOOL COXTreeCtrl::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pDispInfo=(LV_DISPINFO*)pNMHDR; if ((pDispInfo->item.pszText != NULL) && (pDispInfo->item.iItem != -1)) { HTREEITEM hItem=(HTREEITEM) CListCtrl::GetItemData(pDispInfo->item.iItem); VERIFY(SetItemText(hItem,pDispInfo->item.pszText,m_nEditColumn)); return FALSE; } *pResult=0; return FALSE; } BOOL COXTreeCtrl::SetItemText(HTREEITEM hItem, LPCTSTR lpszItem ,int nCol) { COXTreeItem *xti=GetXItem(hItem); if(!xti) return FALSE; if(nCol == 0) { TV_ITEM tvi; memset(&tvi,0,sizeof(tvi)); tvi.mask=TVIF_HANDLE | TVIF_TEXT; tvi.hItem=hItem; tvi.pszText=(LPTSTR)lpszItem; return SetItem(&tvi); } return xti->SetSubItemText(lpszItem,nCol); } BOOL COXTreeCtrl::GetPickAnywhere() const { return (m_dwExStyle & TVOXS_PICKANYWHERE); } BOOL COXTreeCtrl::GetDrawGridFullLength() const { return (m_dwExStyle & TVOXS_FLGRID); } BOOL COXTreeCtrl::HasGrid(UINT nGridType) const { return (m_dwExStyle & nGridType); } BOOL COXTreeCtrl::ResizeColToFit(int nCol) { if(nCol >= GetColumnsCount()) return FALSE; int nWidth=GetFullColumnWidth(nCol); return SetColumnWidth(nCol,nWidth); } int COXTreeCtrl::GetFullColumnWidth(int nCol) { if(GetItemCount()==0) return 6; int nImgWidth=0,nStateImgWidth=0; CImageList* pImgList=CListCtrl::GetImageList(LVSIL_SMALL); CImageList* pStateImgList=CListCtrl::GetImageList(LVSIL_STATE); // calc image width if(pImgList && pImgList->GetImageCount() > 0) { IMAGEINFO imi; VERIFY(pImgList->GetImageInfo(0,&imi)); nImgWidth=imi.rcImage.right - imi.rcImage.left; } // calc state image width if(pStateImgList && pStateImgList->GetImageCount() > 0) { IMAGEINFO imi; VERIFY(pStateImgList->GetImageInfo(0,&imi)); nStateImgWidth=imi.rcImage.right - imi.rcImage.left; } int nIndex=CListCtrl::GetNextItem(-1,LVNI_ALL); int nMaxWidth=0; CDC *pDC=GetDC(); // look throuth currently visible items and find max width while(nIndex != -1) { int nWidth=0; COXTreeItem *xti=GetXItem((HTREEITEM) CListCtrl::GetItemData(nIndex)); if(nCol == 0) { // add item indent nWidth += GetItemIndent(xti); // add item's state image width, if any if(pStateImgList) { int idx=STATEIMAGEMASKTOINDEX(xti->m_tvi.state)-1; if(idx != -1) nWidth += nStateImgWidth; } // add main image width nWidth += nImgWidth; } else { if(xti->GetSubItemImage(nCol) != -1) nWidth += nImgWidth; } // finally calc and add item's text width CFont *pFont=xti->HasFont(nCol) ? xti->GetItemFont(nCol) : GetFont(); CFont *pOldFont=pDC->SelectObject(pFont); CSize szText=pDC->GetTextExtent(GetItemText((HTREEITEM) xti,nCol)); pDC->SelectObject(pOldFont); nWidth += szText.cx + 6; if(nWidth > nMaxWidth) nMaxWidth=nWidth; // retreive next item nIndex=CListCtrl::GetNextItem(nIndex,LVNI_ALL); } ReleaseDC(pDC); return nMaxWidth; } void COXTreeCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if(GetFocus() != this) SetFocus(); CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar); } void COXTreeCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) // --- In : // --- Out : // --- Returns : //--- Effect : { if(GetFocus() != this) SetFocus(); CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar); } void COXTreeCtrl::SetEditMode(HTREEITEM hItem,UINT uMode,CStringArray& saTextEx,int nCol) { COXTreeItem *xti=GetXItem(hItem); ASSERT(xti); xti->SetEditMode(uMode,saTextEx,nCol); } void COXTreeCtrl::SetPlainEditMode(HTREEITEM hItem,int nCol) { CStringArray sArray; SetEditMode(hItem,OXET_EDIT,sArray,nCol); } void COXTreeCtrl::SetNoEditMode(HTREEITEM hItem,int nCol) { CStringArray sArray; SetEditMode(hItem,OXET_NOEDIT,sArray,nCol); } void COXTreeCtrl::SetItemTextEx(HTREEITEM hItem,CStringArray& saTextEx,int nCol) { COXTreeItem* xti=GetXItem(hItem); ASSERT(xti); if(xti) xti->SetTextEx(saTextEx,nCol); } CStringArray& COXTreeCtrl::GetItemTextEx(HTREEITEM hItem,int nCol) { COXTreeItem *xti=GetXItem(hItem); ASSERT(xti); return xti->GetTextEx(nCol); } void COXTreeCtrl::PostNcDestroy() { m_bInit=FALSE; CListCtrl::PostNcDestroy(); } void COXTreeCtrl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { if(m_nItemHeight) { lpMeasureItemStruct->itemHeight=max(m_nItemHeight, lpMeasureItemStruct->itemHeight); } else { m_nItemHeight=lpMeasureItemStruct->itemHeight; } } UINT COXTreeCtrl::GetItemHeight() const { return m_nItemHeight; } void COXTreeCtrl::SetItemHeight(UINT nHeight) { m_nItemHeight=nHeight; //// CRect rect; GetWindowRect(rect); WINDOWPOS wp; wp.hwnd=m_hWnd; wp.cx=rect.Width(); wp.cy=rect.Height(); wp.flags=SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER; SendMessage(WM_WINDOWPOSCHANGED,0,(LPARAM)&wp); //// } BOOL COXTreeCtrl::EnsureVisible(HTREEITEM hti) { COXTreeItem* xti=GetXItem(hti); if(!xti || xti==&m_xtiRoot) { return FALSE; } if(!xti->IsVisible()) { if(xti->IsHidden()) { xti->SetHidden(FALSE); } CPtrArray arrItemsToExpand; HTREEITEM htiParent=GetParentItem(hti); COXTreeItem* xtiParent; BOOL bFlag=FALSE; while(!bFlag && htiParent!=NULL) { xtiParent=GetXItem(htiParent); ASSERT(xtiParent); if(xtiParent==&m_xtiRoot || (xtiParent->IsVisible() && xtiParent->IsExpanded())) { bFlag=TRUE; } else { if(xtiParent->IsHidden()) { xtiParent->SetHidden(FALSE); } if(!xtiParent->IsExpanded()) { arrItemsToExpand.Add(htiParent); } htiParent=GetParentItem(htiParent); } } if(arrItemsToExpand.GetSize()>0) { for(int nIndex=(int)arrItemsToExpand.GetSize()-1; nIndex>=0; nIndex--) Expand((HTREEITEM)arrItemsToExpand[nIndex],TVE_EXPAND); } } int nIndex=GetItemIndex(hti); if(nIndex<0) { return FALSE; } return CListCtrl::EnsureVisible(nIndex,FALSE); } BOOL COXTreeCtrl::EnsureFirstVisible(HTREEITEM hti) { if(!EnsureVisible(hti)) { return FALSE; } HTREEITEM hItemFirstVisible=GetFirstVisibleItem(); if(hItemFirstVisible==hti) { return TRUE; } int nIndex=GetItemIndex(hti); int nIndexFirstVisible=GetItemIndex(hItemFirstVisible); ASSERT(nIndex!=nIndexFirstVisible); CRect rect; GetItemRect(hti,rect,FALSE); CSize sizeScroll(0,(nIndex-nIndexFirstVisible)*rect.Height()); CListCtrl::Scroll(sizeScroll); return TRUE; } BOOL COXTreeCtrl::Select(HTREEITEM hti, UINT nCode) { if(nCode==TVGN_FIRSTVISIBLE) { return FALSE; } if(hti==NULL) { switch(nCode) { case TVGN_CARET: { HTREEITEM hItem=GetNextItem(TVI_ROOT,TVGN_FIRSTSELECTED); while(hItem) { SetItemState(hItem,0,TVIS_SELECTED); hItem=GetNextItem(hItem,TVGN_NEXTSELECTED); } break; } case TVGN_DROPHILITE: { if(m_hOldDropTarget!=NULL) { SetItemState(m_hOldDropTarget,0,TVIS_DROPHILITED); UpdateWindow(); m_hOldDropTarget = NULL; } } } return TRUE; } COXTreeItem* xti=GetXItem(hti); if(xti==&m_xtiRoot && nCode==TVGN_DROPHILITE) { if(m_hOldDropTarget!=NULL) { SetItemState(m_hOldDropTarget,0,TVIS_DROPHILITED); UpdateWindow(); m_hOldDropTarget = NULL; } } if(!xti || xti==&m_xtiRoot) { return FALSE; } int nIndex=GetItemIndexInternal(xti); BOOL bResult=FALSE; switch(nCode) { case TVGN_CARET: { bResult=SetItemState(hti,TVIS_SELECTED,TVIS_SELECTED) & CListCtrl::SetItemState(nIndex,LVIS_FOCUSED,LVIS_FOCUSED); break; } case TVGN_DROPHILITE: { bResult=SetItemState(hti,TVIS_DROPHILITED,TVIS_DROPHILITED); break; } } return bResult; } BOOL COXTreeCtrl::SelectItem(HTREEITEM hti) { return Select(hti,TVGN_CARET); } BOOL COXTreeCtrl::SelectDropTarget(HTREEITEM hti) { return Select(hti,TVGN_DROPHILITE); } BOOL COXTreeCtrl::SelectSetFirstVisible(HTREEITEM hti) { return Select(hti,TVGN_FIRSTVISIBLE); } CImageList* COXTreeCtrl::CreateDragImage(HTREEITEM hti, BOOL bCreateMultipleImage) { int nIndex=GetItemIndex(hti); if(nIndex<0) { return NULL; } m_bCreatingDragImage=TRUE; CImageList* m_pDragImageList=new CImageList; UINT nCount=GetSelectedCount(); nCount=(nCount>0 && bCreateMultipleImage) ? nCount : 1; CRect rectItem; GetItemRect(hti,&rectItem,FALSE); UINT nItemHeight=rectItem.Height(); rectItem.bottom=nItemHeight*nCount; int cyScreen=GetSystemMetrics(SM_CYSCREEN); rectItem.bottom=rectItem.bottom>cyScreen ? cyScreen : rectItem.bottom; rectItem.top=rectItem.left=0; rectItem.right=GetColumnWidth(0); m_pDragImageList->Create(rectItem.Width(),rectItem.Height(),TRUE,0,1); CClientDC dcClient(this); CDC dc; dc.CreateCompatibleDC(&dcClient); CBitmap bitmap; bitmap.CreateCompatibleBitmap(&dcClient,rectItem.Width(),rectItem.Height()); CBitmap* pOldBitmap=dc.SelectObject(&bitmap); CFont* pOldFont=dc.SelectObject(GetFont()); HTREEITEM hSelectedItem=NULL; if(nCount>1) { hSelectedItem=GetNextItem(TVI_ROOT,TVGN_FIRSTSELECTED); ASSERT(hSelectedItem); nIndex=GetItemIndex(hSelectedItem); rectItem.bottom=rectItem.top+nItemHeight; } while(TRUE) { DRAWITEMSTRUCT dis; dis.CtlType=ODT_LISTVIEW; dis.CtlID=0; dis.itemID=nIndex; dis.itemAction=ODA_DRAWENTIRE; dis.itemState=ODS_DEFAULT; dis.hwndItem=NULL; dis.hDC=dc; dis.rcItem=rectItem; dis.itemData=GetXItem(hti)->m_tvi.lParam; DrawItem(&dis); if(nCount==1) { break; } else { hSelectedItem=GetNextItem(hSelectedItem,TVGN_NEXTSELECTED); if(hSelectedItem) { nIndex=GetItemIndex(hSelectedItem); rectItem.top+=nItemHeight; rectItem.bottom+=nItemHeight; } else { break; } } } if(pOldBitmap) { dc.SelectObject(pOldBitmap); } if(pOldFont) { dc.SelectObject(pOldFont); } m_pDragImageList->Add(&bitmap,(!IsWindowEnabled() ? ::GetSysColor(COLOR_BTNFACE) : GetTextBkColor())); m_bCreatingDragImage=FALSE; return m_pDragImageList; } BOOL COXTreeCtrl::SortChildren(HTREEITEM hti, int nCol/*=0*/, BOOL bAscending/*=TRUE*/, PFNTVCOMPARE lpfnCompare/*=NULL*/, LPARAM lParam/*=NULL*/) { COXTreeHeader* pHeader=(COXTreeHeader*)GetDlgItem(0); ASSERT(pHeader); pHeader->SortColumn(nCol,bAscending ? 1 : -1); CWaitCursor waitCursor; SetRedraw(FALSE); hti=hti==NULL ? GetRootItem() : hti; COXTreeItem* xti=GetXItem(hti); ASSERT(xti); BOOL bOldExpand=xti->IsExpanded(); if(bOldExpand) { xti->SetExpand(FALSE); COXTreeItem *xtiTemp=xti->pxFirstChild; while(xtiTemp) { if(!xtiTemp->IsHidden()) { int nIndex=GetItemIndexInternal(xtiTemp); ASSERT(nIndex!=-1); xtiTemp->RemoveChildrenFromCtrl(this); CListCtrl::DeleteItem(nIndex); } xtiTemp=xtiTemp->pxNext; } } if (lpfnCompare != NULL) xti->SortChildren(nCol,(hti==GetRootItem() ? FALSE : TRUE), bAscending,lpfnCompare,lParam); else { // Find out if a compare function for that column // has been specified. PFNTVCOMPARE lpfnColumnCompare = NULL; m_mapCompareFunctions.Lookup(nCol, lpfnColumnCompare); if (lpfnColumnCompare != NULL) xti->SortChildren(nCol,(hti==GetRootItem() ? FALSE : TRUE), bAscending,lpfnColumnCompare,lParam); else // default compare function xti->SortChildren(nCol,(hti==GetRootItem() ? FALSE : TRUE), bAscending,m_lpfnDefaultCompare,lParam); } if(bOldExpand) { xti->SetExpand(TRUE); COXTreeItem *xtiTemp=xti->pxFirstChild; int pos=GetItemIndexInternal(xti) + 1; while(xtiTemp) { if(!xtiTemp->IsHidden()) { SetItemAtPos(pos,xtiTemp); pos ++; pos=xtiTemp->AddChildrenToCtrl(this,pos); } xtiTemp=xtiTemp->pxNext; } } SetRedraw(TRUE); waitCursor.Restore(); return TRUE; } BOOL COXTreeCtrl::SortChildrenCB(LPTV_SORTCB pSort, int nCol/*=0*/) { return SortChildren(pSort->hParent,nCol,TRUE,pSort->lpfnCompare,pSort->lParam); } HTREEITEM COXTreeCtrl::GetItemFromIndex(int nIndex) const { ASSERT(nIndex>=-1); if(nIndex==-1) { return GetRootItem(); } return (HTREEITEM)((CListCtrl*)this)->GetItemData(nIndex); } LRESULT COXTreeCtrl::OnNeedItemTip(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); #ifndef OX_TREECTRL_NOITEMTIPS LPNEEDITEMTIPINFO pnitInfo=(LPNEEDITEMTIPINFO)lParam; int nResult=pnitInfo->result; if(nResult!=ID_NIT_WRONGFORMAT && nResult!=ID_NIT_OUTOFCONTROLBORDER) { CPoint point; point.x=pnitInfo->point.x; point.y=pnitInfo->point.y; UINT nFl; int idx=HitTest(point,&nFl); HTREEITEM hti=GetItemFromIndex(idx); ASSERT(hti); COXTreeItem *xti=GetXItem(hti); ASSERT(xti); if(idx==-1) { pnitInfo->result=ID_NIT_OUTOFCONTROLBORDER; return 0; } pnitInfo->row=idx; CRect rcItem; CListCtrl::GetItemRect(idx,&rcItem,LVIR_BOUNDS); // Now find the column int nCol=0; while(TRUE) { int nColWidth=GetColumnWidth(nCol); if(nColWidth<0) { pnitInfo->result=ID_NIT_OUTOFCONTROLBORDER; return 0; } rcItem.right=rcItem.left+nColWidth; if(rcItem.PtInRect(point)) { break; } rcItem.left=rcItem.right; nCol++; } if(nCol && !xti->GetSubItem(nCol)) { // this item has no subitems at given column pnitInfo->result=ID_NIT_CUSTOMREJECT; return 0; } pnitInfo->col=nCol; CImageList *pImgList=CListCtrl::GetImageList(LVSIL_SMALL); CImageList *pStateImgList=CListCtrl::GetImageList(LVSIL_STATE); LV_COLUMN lvc; memset(&lvc, 0, sizeof(LV_COLUMN)); lvc.mask=LVCF_FMT; VERIFY(GetColumn(nCol, &lvc)); int nImgWidth=0; IMAGEINFO imgInfo; int nImg=xti->GetItemImage(nCol); if(pImgList && nImg!=-1) { pImgList->GetImageInfo(0,&imgInfo); CRect rcImg(imgInfo.rcImage); nImgWidth += rcImg.Width(); } if(pStateImgList && nCol==0 && GetItemStateIndex(GetItemIndexInternal(xti))!=-1) { pStateImgList->GetImageInfo(0,&imgInfo); CRect rcImg(imgInfo.rcImage); nImgWidth += rcImg.Width(); } rcItem.left+=nImgWidth; rcItem.left+=nCol==0 ? GetItemIndent(xti) : 0; if(!rcItem.PtInRect(point)) { pnitInfo->result=ID_NIT_OUTOFITEMBORDER; return 0; } pnitInfo->clrText=xti->IsDisabled() ? ::GetSysColor(COLOR_3DSHADOW) : xti->HasColor(nCol) ? xti->GetItemColor(nCol) : GetTextColor(); // pnitInfo->clrBackground=GetTextBkColor(); pnitInfo->clrBackground=ID_OX_COLOR_NONE; CString sText=GetItemText((HTREEITEM)xti,nCol); if(sText.IsEmpty()) { pnitInfo->result=ID_NIT_NOTHIDDEN; return 0; } lstrcpyn(pnitInfo->itemText,sText,pnitInfo->sizeText); pnitInfo->pFont=(LPARAM)GetFont(); if(xti->HasFont(nCol)) { pnitInfo->pFont=(LPARAM)xti->GetItemFont(nCol); } int nOffset=2; rcItem.DeflateRect(nOffset,0); CClientDC dc(this); CFont* pOldFont=pnitInfo->pFont==NULL ? NULL : dc.SelectObject((CFont*)pnitInfo->pFont); CRect rectText(0, 0, 0, 0); dc.DrawText(sText,&rectText,DT_CALCRECT|DT_LEFT|DT_SINGLELINE); if(pOldFont) { dc.SelectObject(pOldFont); } CRect rectClient; GetClientRect(&rectClient); int fmt=lvc.fmt&LVCFMT_JUSTIFYMASK; int nTextWidth=rectText.Width(); switch(fmt) { case LVCFMT_LEFT: { rectText.left=rcItem.left; rectText.right=rectText.left+nTextWidth; pnitInfo->alignment=DT_LEFT; break; } case LVCFMT_RIGHT: { rectText.right=rcItem.right; rectText.left=rectText.right-nTextWidth; pnitInfo->alignment=DT_RIGHT; break; } case LVCFMT_CENTER: { int nCenter=rcItem.left+(rcItem.right-rcItem.left)/2; rectText.left=nCenter-nTextWidth/2; rectText.right=nCenter+nTextWidth/2; rectText.right+=nTextWidth%2; pnitInfo->alignment=DT_CENTER; break; } default: ASSERT(FALSE); } if(rectText.Width()<=rcItem.Width() && rectText.right<=rectClient.right && rectText.left>=rectClient.left) { pnitInfo->result=ID_NIT_NOTHIDDEN; return 0; } if(rcItem.Width()rectItem.left=rcItem.left; pnitInfo->rectItem.right=rcItem.right; pnitInfo->rectItem.top=rcItem.top; pnitInfo->rectItem.bottom=rcItem.bottom; pnitInfo->offset=nOffset; pnitInfo->result=ID_NIT_SUCCESS; } #else UNREFERENCED_PARAMETER(lParam); #endif return 0; } void COXTreeCtrl::OnTimer(UINT nIDEvent) { if(nIDEvent==m_nTimerCheckKeyboardInput) { m_sSearchMask=_T(""); return; } CListCtrl::OnTimer(nIDEvent); } void COXTreeCtrl::OnDestroy() { if(m_nTimerCheckKeyboardInput!=NULL) KillTimer(m_nTimerCheckKeyboardInput); CListCtrl::OnDestroy(); } void COXTreeCtrl::OnSize(UINT nType, int cx, int cy) { CListCtrl::OnSize(nType, cx, cy); if (m_dwExStyle & TVOXS_EXTENDCOLUMNS) PostMessage(WM_USER_ADJUSTLASTCOLUMN); } LRESULT COXTreeCtrl::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) { // TODO: Add your specialized code here and/or call the base class #ifndef OX_TREECTRL_NOITEMTIPS if(m_bFirstTimeHooking && (m_dwExStyle&TVOXS_ITEMTIPS)==TVOXS_ITEMTIPS && m_ItemTip.GetHookedWnd()!=this) { m_bFirstTimeHooking=FALSE; m_ItemTip.Attach(this); } #endif return CListCtrl::DefWindowProc(message, wParam, lParam); } BOOL COXTreeCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwExStyle/*=0*/) { DWORD dwWndStyle=dwStyle&0xffff0000; m_dwTCStyle=dwStyle&0x0000ffff; m_dwExStyle=dwExStyle; DWORD dwListStyle=LVS_REPORT|LVS_OWNERDRAWFIXED; if(m_dwTCStyle & TVS_SHOWSELALWAYS) dwListStyle |= LVS_SHOWSELALWAYS; if(m_dwTCStyle & TVS_EDITLABELS) dwListStyle |= LVS_EDITLABELS; if(!(m_dwExStyle & TVOXS_MULTISEL)) dwListStyle |= LVS_SINGLESEL; if(!(m_dwExStyle & TVOXS_COLUMNHDR)) dwListStyle |= LVS_NOCOLUMNHEADER; if(m_dwExStyle & TVOXS_NOSCROLL) { dwListStyle |= LVS_NOSCROLL; } else dwWndStyle |= WS_VSCROLL | WS_HSCROLL; if(m_dwExStyle & TVOXS_NOSORTHEADER) dwListStyle |= LVS_NOSORTHEADER; if(m_dwExStyle & TVOXS_SHAREIMAGELISTS) dwListStyle |= LVS_SHAREIMAGELISTS; if(!CListCtrl::Create(dwListStyle|dwWndStyle,rect,pParentWnd,nID)) { return FALSE; } DWORD dwAddStyle=::GetWindowLongPtr(m_hWnd,GWL_STYLE); m_dwTCStyle|=(0xFFFF0000&dwAddStyle); return Init(); } BOOL COXTreeCtrl::IsHeaderSorting() const { return ((m_dwExStyle&TVOXS_NOSORTHEADER)!=TVOXS_NOSORTHEADER && (m_dwExStyle&TVOXS_COLUMNHDR)==TVOXS_COLUMNHDR); } int COXTreeCtrl::GetSortCol() const { COXTreeHeader* pHeader=(COXTreeHeader*)GetDlgItem(0); ASSERT(pHeader); return pHeader->GetSortCol(); } int COXTreeCtrl::GetSortOrder() const { COXTreeHeader* pHeader=(COXTreeHeader*)GetDlgItem(0); ASSERT(pHeader); return pHeader->GetSortOrder(); } void COXTreeCtrl::OnParentNotify(UINT message, LPARAM lParam) { CListCtrl::OnParentNotify(message,lParam); if(LOWORD(message) == WM_CREATE) { HWND hWnd=GetHeaderCtrlHandle(); if(hWnd==(HWND)lParam) { if(!::IsWindow(m_wndHdr.GetSafeHwnd())) VERIFY(m_wndHdr.SubclassWindow(hWnd)); else ASSERT(m_wndHdr.GetSafeHwnd()==hWnd); } } } HWND COXTreeCtrl::GetHeaderCtrlHandle() { ASSERT_VALID(this); if (m_hWnd == NULL) // ... This control has not been created yet return NULL; // Get the first child of the list control // Normally the list only has one child window : the header control HWND hHeaderWnd=::GetDlgItem(m_hWnd, 0); if(hHeaderWnd != NULL) { // Make extra sure we actually have a header ctrl const int nMaxClassNameLength=50; TCHAR szClass[nMaxClassNameLength + 1]; ::GetClassName(hHeaderWnd, szClass, nMaxClassNameLength); if(_tcscmp(szClass, _T("SysHeader32")) == 0) { return hHeaderWnd; } else { return NULL; } } else { TRACE(_T("COXTreeCtrl::GetHeaderCtrlHandle : No child window found\n")); return NULL; } } BOOL COXTreeCtrl::CopyItem(HTREEITEM hItemToCopy, HTREEITEM hParent/*=NULL*/, HTREEITEM hInsertAfter/*=TVI_LAST*/, BOOL bCopyDescendants/*=TRUE*/, HTREEITEM* pNewItem/*=NULL*/, COXTreeCtrl* /*pDestinationTree=NULL*/) { ASSERT(hItemToCopy!=NULL); // define if we can copy an item: we cannot copy an item with its descendants // to itself or its descendants BOOL bCanCopy=TRUE; if(bCopyDescendants) { HTREEITEM hTestItem=hParent; while(hTestItem!=NULL) { if(hTestItem==hItemToCopy) { bCanCopy=FALSE; break; } hTestItem=GetParentItem(hTestItem); } } if(!bCanCopy) return FALSE; COXTreeItem* pxParent=GetXItem(hParent); ASSERT(pxParent!=NULL); COXTreeItem* pxItemToCopy=GetXItem(hItemToCopy); ASSERT(pxItemToCopy!=NULL); COXTreeItem* pxInsertAfter=NULL; if(hInsertAfter==TVI_FIRST || hInsertAfter==TVI_LAST) pxInsertAfter=(COXTreeItem*)hInsertAfter; else pxInsertAfter=GetXItem(hInsertAfter); COXTreeItem* pTempNewItem=pxParent->CopyChild(pxItemToCopy,pxInsertAfter,bCopyDescendants); if (pNewItem) *pNewItem=(HTREEITEM) pTempNewItem; BOOL bWasHidden=IsItemHidden(hParent); if(!bWasHidden) { // lock the control updates SetRedraw(FALSE); // hiding of item will remove it from list control while // saving internal logic HideItem(hParent,TRUE); // restore the visibility of hidden item HideItem(hParent,FALSE); // unlock the control updates SetRedraw(TRUE); } return TRUE; } BOOL COXTreeCtrl::MoveItem(HTREEITEM hItemToMove, HTREEITEM hParent/*=NULL*/, HTREEITEM hInsertAfter/*=TVI_LAST*/, COXTreeCtrl* pDestinationTree/*=NULL*/) { if(hItemToMove==hInsertAfter) return TRUE; // define if we can move item: we cannot move dragged item to its // descendants BOOL bCanMove=TRUE; HTREEITEM hTestItem=hParent; while(hTestItem!=NULL) { if(hTestItem==hItemToMove) { bCanMove=FALSE; break; } hTestItem=GetParentItem(hTestItem); } if(!bCanMove) return FALSE; COXTreeItem* pxParent; if (pDestinationTree != NULL) pxParent = pDestinationTree->GetXItem(hParent); else pxParent = GetXItem(hParent); ASSERT(pxParent!=NULL); COXTreeItem* pxItemToMove=GetXItem(hItemToMove); ASSERT(pxItemToMove!=NULL); COXTreeItem* pxInsertAfter=NULL; if(hInsertAfter==TVI_FIRST || hInsertAfter==TVI_LAST) pxInsertAfter=(COXTreeItem*)hInsertAfter; else pxInsertAfter=GetXItem(hInsertAfter); // lock the control updates SetRedraw(FALSE); if (pDestinationTree != NULL) pDestinationTree->SetRedraw(FALSE); // hiding of item will remove it from list control while // saving internal logic HideItem(hItemToMove,TRUE); // remove the moved item and its descendants out of internal chain pxItemToMove->Unlink(); // insert moved item and its descendants pxParent->AddChild(pxItemToMove,pxInsertAfter); // show the reallocated item if (pDestinationTree != NULL) pDestinationTree->HideItem(hItemToMove,FALSE); else HideItem(hItemToMove,FALSE); // unlock the control updates SetRedraw(TRUE); if (pDestinationTree != NULL) pDestinationTree->SetRedraw(TRUE); return TRUE; } BOOL COXTreeCtrl::RedrawTreeItems(HTREEITEM hItemFirst, HTREEITEM hItemLast) { if(hItemFirst==NULL || GetItemIndex(hItemFirst)==-1 || hItemLast==NULL || GetItemIndex(hItemLast)==-1) return FALSE; CListCtrl::RedrawItems(GetItemIndex(hItemFirst),GetItemIndex(hItemLast)); return TRUE; } void COXTreeCtrl::OnStyleChanged( int nStyleType, LPSTYLESTRUCT lpStyleStruct ) { if (nStyleType==GWL_STYLE) m_dwTCStyle=(lpStyleStruct->styleNew & 0xFFFF0000) | (m_dwTCStyle & 0x0000FFFF); } BOOL COXTreeCtrl::PreTranslateMessage( MSG* pMsg ) { return CListCtrl::PreTranslateMessage(pMsg); } void COXTreeCtrl::SetHorizontalGridColor(COLORREF clr) { m_clrHorizontalGrid = clr; } void COXTreeCtrl::SetVerticalGridColor(COLORREF clr) { m_clrVerticalGrid = clr; } BOOL COXTreeCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) { // TODO: Add your specialized code here and/or call the base class if (m_dwExStyle & TVOXS_EXTENDCOLUMNS) { HD_NOTIFY *pHDN = (HD_NOTIFY*) lParam; if ((pHDN->hdr.code == HDN_BEGINTRACKA || pHDN->hdr.code == HDN_BEGINTRACKW) && pHDN->iItem == GetColumnsCount() - 1) { *pResult = TRUE; // disable tracking return TRUE; } else if (pHDN->hdr.code == HDN_ITEMCHANGINGA || pHDN->hdr.code == HDN_ITEMCHANGINGW || pHDN->hdr.code == HDN_TRACKA || pHDN->hdr.code == HDN_TRACKW) { // Adjust the last column PostMessage(WM_USER_ADJUSTLASTCOLUMN); } } return CListCtrl::OnNotify(wParam, lParam, pResult); } LRESULT COXTreeCtrl::OnUserAdjustLastColumn(WPARAM /*wParam*/, LPARAM /*lParam*/) { if (!m_bInit) return 0; // Make the last column ends where the window ends CRect rectClient; GetClientRect(rectClient); int iCount = GetColumnsCount();//m_wndHdr.GetItemCount(); if (iCount <= 0) return 0; // Loop through all columns and make sure none is larger than the window int iAccumulatedWidth = 0; for (int i = 0; i < iCount; i++) { int iCurrentWidth = GetColumnWidth(i); if (iCurrentWidth + iAccumulatedWidth > rectClient.Width()) { // The current column is too wide iCurrentWidth = rectClient.Width() - iAccumulatedWidth; if (iCurrentWidth < 0) iCurrentWidth = 0; SetColumnWidth(i, iCurrentWidth); } else if (i == iCount - 1 && iCurrentWidth + iAccumulatedWidth < rectClient.Width()) { // This is the last column and it is not wide enough iCurrentWidth = rectClient.Width() - iAccumulatedWidth; SetColumnWidth(i, iCurrentWidth); } iAccumulatedWidth += iCurrentWidth; } // Send the the message to the parent CWnd* pParent = GetParent(); if (pParent!= NULL) pParent->SendMessage(WM_USER_ADJUSTLASTCOLUMN, 0, 0); return 0; } void COXTreeCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if (nChar == VK_CONTROL || nChar == VK_MENU) return; // Navigation support if (nChar == VK_UP || nChar == VK_DOWN || nChar == VK_HOME || nChar == VK_END || nChar == VK_PRIOR || nChar == VK_NEXT) { CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags); return; } else if (nChar == VK_RIGHT) { // Try to expand the selected item HTREEITEM hSelectedItem = GetSelectedItem(); if (hSelectedItem != NULL) { COXTreeItem* pItem = GetXItem(hSelectedItem); if (!pItem->IsExpanded()) Expand(hSelectedItem, TVE_EXPAND); else if (pItem->pxFirstChild != NULL) { // Already expanded, so select the first child SelectItem(NULL); SelectItem((HTREEITEM) pItem->pxFirstChild); } } } else if (nChar == VK_LEFT) { // Try to collapse the selected item HTREEITEM hSelectedItem = GetSelectedItem(); if (hSelectedItem != NULL) { COXTreeItem* pItem = GetXItem(hSelectedItem); if (pItem->IsExpanded()) Expand(hSelectedItem, TVE_COLLAPSE); else if (pItem->pxParent != NULL) { // Already collapsed, so select parent SelectItem(NULL); SelectItem((HTREEITEM) pItem->pxParent); } } } int nCount=GetVisibleCount(); if(nCount>1) { if(m_nTimerCheckKeyboardInput!=NULL) KillTimer(m_nTimerCheckKeyboardInput); TCHAR chSymbol[2]; chSymbol[0]=(TCHAR)nChar; chSymbol[1]=0; CString sNewSymbol=chSymbol; m_sSearchMask+=sNewSymbol; CString sMask=m_sSearchMask; sMask.MakeUpper(); int nStartIndex=CListCtrl::GetNextItem(-1,LVNI_ALL|LVNI_FOCUSED); HTREEITEM hti; CString sText; int nIndex=nStartIndex; BOOL bIncludeCurrent=FALSE; if(nStartIndex!=-1 && sMask.GetLength()>1) { nIndex--; bIncludeCurrent=TRUE; } while(TRUE) { nIndex++; if(nIndex>=nCount) { nIndex=0; if(nStartIndex==-1) { break; } } if(nIndex==nStartIndex) { if(!bIncludeCurrent) break; else bIncludeCurrent=FALSE; } hti=GetItemFromIndex(nIndex); ASSERT(hti); sText=GetItemText(hti); sText.MakeUpper(); if(!sText.IsEmpty() && sText.Find(sMask)==0) { SelectItem(NULL); SelectItem(hti); EnsureVisible(hti); break; } } // timer for keyboard input m_nTimerCheckKeyboardInput= (UINT)SetTimer(IDT_OXTREECTRL_CHECKFORKEYBOARDINPUT, ID_OXTREECTRL_CHECKFORKEYBOARDINPUT_DELAY,NULL); if(m_nTimerCheckKeyboardInput==0) { TRACE(_T("COXTreeCtrl::OnKeydown: Unable to set timer for keyboard input\n")); } } } BOOL COXTreeCtrl::IsPropertiesWnd() const { if (GetParent() != NULL && GetParent()->SendMessage(WM_USER_QUERY_PROPERTIESWND)) return TRUE; else return FALSE; } void COXTreeCtrl::SetColumnCompareFunction(int nCol, PFNTVCOMPARE lpfnDefaultCompare) { m_mapCompareFunctions.SetAt(nCol, lpfnDefaultCompare); }