//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2002. // ------------------------------------------------------------------------- // File name: treectrlex.cpp // Version: v1.00 // Created: 1/8/2002 by Timur. // Compilers: Visual Studio.NET // Description: // ------------------------------------------------------------------------- // History: // //////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "TreeCtrlEx.h" // CTreeCtrlEx IMPLEMENT_DYNAMIC(CTreeCtrlEx, CTreeCtrl) CTreeCtrlEx::CTreeCtrlEx() { // When this tree is initialized, no dragging of course m_bLDragging = false; m_pDragImage = NULL; m_hitemDrag = NULL; m_hitemDrop = NULL; m_dropCursor = LoadCursor(NULL,IDC_ARROW); m_noDropCursor = LoadCursor(NULL,IDC_NO); } CTreeCtrlEx::~CTreeCtrlEx() { } BEGIN_MESSAGE_MAP(CTreeCtrlEx, CTreeCtrl) ON_NOTIFY_REFLECT(TVN_BEGINRDRAG, OnBeginDrag) ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_WM_LBUTTONDOWN() END_MESSAGE_MAP() // CTreeCtrlEx message handlers ////////////////////////////////////////////////////////////////////////// // CopyItem - Copies an item to a new location // Returns - Handle of the new item // hItem - Item to be copied // htiNewParent - Handle of the parent for new item // htiAfter - Item after which the new item should be created HTREEITEM CTreeCtrlEx::CopyItem( HTREEITEM hItem, HTREEITEM htiNewParent, HTREEITEM htiAfter /*= TVI_LAST*/ ) { TV_INSERTSTRUCT tvstruct; HTREEITEM hNewItem; CString sText; // get information of the source item tvstruct.item.hItem = hItem; tvstruct.item.mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; GetItem(&tvstruct.item); sText = GetItemText( hItem ); tvstruct.item.cchTextMax = sText.GetLength(); tvstruct.item.pszText = sText.LockBuffer(); // Insert the item at proper location tvstruct.hParent = htiNewParent; tvstruct.hInsertAfter = htiAfter; tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT; hNewItem = InsertItem(&tvstruct); sText.ReleaseBuffer(); // Now copy item data and item state. SetItemData( hNewItem, GetItemData( hItem )); SetItemState( hNewItem, GetItemState( hItem, TVIS_STATEIMAGEMASK | TVIS_EXPANDED ), TVIS_STATEIMAGEMASK | TVIS_EXPANDED ); // Call virtual function to allow further processing in derived class OnItemCopied( hItem, hNewItem ); return hNewItem; } ////////////////////////////////////////////////////////////////////////// void CTreeCtrlEx::OnItemCopied(HTREEITEM /*hItem*/, HTREEITEM /*hNewItem*/ ) { // Virtual function } ////////////////////////////////////////////////////////////////////////// // CopyBranch - Copies all items in a branch to a new location // Returns - The new branch node // htiBranch - The node that starts the branch // htiNewParent - Handle of the parent for new branch // htiAfter - Item after which the new branch should be created HTREEITEM CTreeCtrlEx::CopyBranch( HTREEITEM htiBranch, HTREEITEM htiNewParent, HTREEITEM htiAfter /*= TVI_LAST*/ ) { HTREEITEM hChild; HTREEITEM hNewItem = CopyItem( htiBranch, htiNewParent, htiAfter ); SetItemState(hNewItem, GetItemState(htiBranch, 0xffffffff), 0xffffffff); hChild = GetChildItem(htiBranch); while( hChild != NULL) { // recursively transfer all the items CopyBranch(hChild, hNewItem); hChild = GetNextSiblingItem( hChild ); } return hNewItem; } ////////////////////////////////////////////////////////////////////////// void CTreeCtrlEx::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; *pResult = 0; m_hitemDrag = pNMTreeView->itemNew.hItem; m_hitemDrop = NULL; if (!IsDropSource(m_hitemDrag)) return; m_pDragImage = CreateDragImage(m_hitemDrag); // get the image list for dragging // CreateDragImage() returns NULL if no image list // associated with the tree view control if( !m_pDragImage ) return; m_bLDragging = true; // Calculate the offset to the hotspot CPoint offsetPt(8,8); // Initialize a default offset CPoint dragPt = pNMTreeView->ptDrag; // Get the Drag point UINT nHitFlags = 0; HTREEITEM htiHit = HitTest(dragPt, &nHitFlags); if (NULL != htiHit) { // The drag point has Hit an item in the tree CRect itemRect; // Get the text bounding rectangle if (GetItemRect(htiHit, &itemRect, TRUE)) { // Calculate the new offset offsetPt.y = dragPt.y - itemRect.top; offsetPt.x = dragPt.x - (itemRect.left - GetIndent()); } /* if (GetItemRect(htiHit, &itemRect, FALSE)) { // Count indent levels HTREEITEM htiParent = htiHit; int nIndentCnt = 0; while (htiParent != NULL) { htiParent = GetParentItem(htiParent); nIndentCnt++; } if (!(GetStyle() & TVS_LINESATROOT)) nIndentCnt--; // Calculate the new offset offsetPt.y = dragPt.y - itemRect.top; offsetPt.x = dragPt.x - (nIndentCnt * GetIndent()) + GetScrollPos(SB_HORZ); CImageList* pImageListState = GetImageList(TVSIL_STATE); UINT nState = GetItemState( htiHit, LVIS_STATEIMAGEMASK ); if (pImageListState && nState) { (nState>>=12)--; IMAGEINFO ImageInfo; //State Image list pImageListState->GetImageInfo(nState,&ImageInfo); offsetPt.x -= (ImageInfo.rcImage.right-ImageInfo.rcImage.left); } } */ } /* CImageList* pList = GetImageList(TVSIL_STATE); if (pList) { IMAGEINFO info; pList->GetImageInfo(1, &info); offsetPt.x -= info.rcImage.right-info.rcImage.left; } */ // Begin the Drag operation using the Drag image and the calculated hotspot offset m_pDragImage->BeginDrag(0, offsetPt); //m_pDragImage->BeginDrag(0, CPoint(-15,-15)); POINT pt = pNMTreeView->ptDrag; ClientToScreen( &pt ); m_pDragImage->DragEnter(NULL, pt); SetCapture(); } ////////////////////////////////////////////////////////////////////////// void CTreeCtrlEx::OnMouseMove(UINT nFlags, CPoint point) { HTREEITEM hitem; UINT flags; if (m_bLDragging) { POINT pt = point; ClientToScreen( &pt ); CImageList::DragMove(pt); hitem = HitTest(point, &flags); if (m_hitemDrop != hitem) { CImageList::DragShowNolock(FALSE); SelectDropTarget(hitem); m_hitemDrop = hitem; CImageList::DragShowNolock(TRUE); } if(hitem) hitem = GetDropTarget(hitem); if (hitem) SetCursor(m_dropCursor); else SetCursor(m_noDropCursor); } CTreeCtrl::OnMouseMove(nFlags, point); } ////////////////////////////////////////////////////////////////////////// void CTreeCtrlEx::OnLButtonUp(UINT nFlags, CPoint point) { CTreeCtrl::OnLButtonUp(nFlags, point); if (m_bLDragging) { m_bLDragging = false; CImageList::DragLeave(this); CImageList::EndDrag(); ReleaseCapture(); delete m_pDragImage; // Remove drop target highlighting SelectDropTarget(NULL); m_hitemDrop = GetDropTarget(m_hitemDrop); if(m_hitemDrop == NULL) return; if( m_hitemDrag == m_hitemDrop ) return; Expand( m_hitemDrop, TVE_EXPAND ) ; HTREEITEM htiNew = CopyBranch( m_hitemDrag, m_hitemDrop, TVI_LAST ); DeleteItem(m_hitemDrag); SelectItem( htiNew ); } } void CTreeCtrlEx::OnLButtonDown(UINT nFlags, CPoint point) { // Set focus on tree when clicked. SetFocus(); CTreeCtrl::OnLButtonDown(nFlags, point); } BOOL CTreeCtrlEx::PreTranslateMessage(MSG* pMsg) { if( pMsg->message == WM_KEYDOWN ) { bool bHandledHere = false; // When an item is being edited make sure the edit control // receives certain important key strokes if (GetKeyState( VK_CONTROL)) { bHandledHere = true; } CString m_Test; switch(pMsg->wParam) { case VK_RETURN: //GetEditControl()->GetWindowText(m_Test); //if(SetItemText(m_CurItem.p_Item, m_Test) == NULL) //TRACE("Unable to Change Item Text(Enter Message)!!!\n"); bHandledHere = true; break; case VK_DELETE: //GetEditControl()->SetWindowText(""); bHandledHere = true; break; case VK_ESCAPE: break; bHandledHere = true; } if (bHandledHere) { ::TranslateMessage(pMsg); ::DispatchMessage(pMsg); return TRUE; // DO NOT process further } } if (pMsg->message == WM_KEYDOWN && m_bLDragging) { if (pMsg->wParam == VK_ESCAPE) { m_bLDragging = false; CImageList::DragLeave(NULL); CImageList::EndDrag(); ReleaseCapture(); SelectDropTarget(NULL); delete m_pDragImage; m_pDragImage = NULL; } return TRUE; // DO NOT process further } return CTreeCtrl::PreTranslateMessage(pMsg); } ////////////////////////////////////////////////////////////////////////// BOOL CTreeCtrlEx::IsDropSource(HTREEITEM hItem) { if (m_bOnlyLeafsDrag) { if (GetParentItem(hItem) == TVI_ROOT) return FALSE; } return TRUE; // all other items are valid sources } ////////////////////////////////////////////////////////////////////////// HTREEITEM CTreeCtrlEx::GetDropTarget(HTREEITEM hItem) { if (hItem != m_hitemDrag && hItem != GetParentItem(m_hitemDrag)) { if (m_bOnlyLeafsDrag) { if (GetParentItem(hItem) != TVI_ROOT) return GetParentItem(hItem); } HTREEITEM htiParent = hItem; while((htiParent = GetParentItem( htiParent )) != NULL ) { if( htiParent == m_hitemDrag ) return NULL; } return hItem; } return NULL; } ////////////////////////////////////////////////////////////////////////// HTREEITEM CTreeCtrlEx::GetNextItem( HTREEITEM hItem, UINT nCode ) { return CTreeCtrl::GetNextItem( hItem, nCode ); } ////////////////////////////////////////////////////////////////////////// HTREEITEM CTreeCtrlEx::GetNextItem(HTREEITEM hItem) { HTREEITEM hti = NULL; if (ItemHasChildren(hItem)) hti = GetChildItem(hItem); // return first child if (hti == NULL) { // return next sibling item // Go up the tree to find a parent's sibling if needed. while ((hti = GetNextSiblingItem(hItem)) == NULL) { if ((hItem = GetParentItem(hItem)) == NULL) return NULL; } } return hti; } ////////////////////////////////////////////////////////////////////////// HTREEITEM CTreeCtrlEx::FindNextItem(TV_ITEM* pItem, HTREEITEM hItem) { ASSERT(::IsWindow(m_hWnd)); TV_ITEM hNextItem; //Clear Item data ZeroMemory(&hNextItem, sizeof(hNextItem)); //The mask is used to retrieve the data to compare hNextItem.mask = pItem->mask; hNextItem.hItem = (hItem) ? GetNextItem(hItem) : GetRootItem(); //Prepare to compare pszText //Testing pItem->pszText protects the code from a client setting the //TVIF_TEXT bit but passing in a NULL pointer. if((pItem->mask & TVIF_TEXT) && pItem->pszText) { hNextItem.cchTextMax = strlen(pItem->pszText); if(hNextItem.cchTextMax) hNextItem.pszText = new char[++hNextItem.cchTextMax]; } while(hNextItem.hItem) { if(CompareItems(pItem, hNextItem)) { //Copy all the information into pItem and return memcpy(pItem, &hNextItem, sizeof(TV_ITEM)); //Free resources if(hNextItem.pszText) delete hNextItem.pszText; return pItem->hItem; } //The mask is used to retrieve the data to compare and must be //reset before calling Compare hNextItem.mask = pItem->mask; hNextItem.hItem = GetNextItem(hNextItem.hItem); } //Set hItem in pItem pItem->hItem = NULL; //Free resources if(hNextItem.pszText) delete hNextItem.pszText; return NULL; } ////////////////////////////////////////////////////////////////////////// BOOL CTreeCtrlEx::CompareItems(TV_ITEM* pItem, TV_ITEM& tvTempItem) { //This call uses the .mask setting to just retrieve the values //that the client wants to compare. //Get all the data passed in by pItem GetItem(&tvTempItem); //Reset the mask so I can keep track of the matching attributes tvTempItem.mask = 0; if((pItem->mask & TVIF_STATE) && (pItem->state == tvTempItem.state)) tvTempItem.mask |= TVIF_STATE; if((pItem->mask & TVIF_IMAGE) && (pItem->iImage == tvTempItem.iImage)) tvTempItem.mask |= TVIF_IMAGE; if((pItem->mask & TVIF_PARAM) && (pItem->lParam == tvTempItem.lParam)) tvTempItem.mask |= TVIF_PARAM; if((pItem->mask & TVIF_TEXT) && pItem->pszText && tvTempItem.pszText && //Don't compare if either is NULL !strcmp(pItem->pszText, tvTempItem.pszText)) tvTempItem.mask |= TVIF_TEXT; if((pItem->mask & TVIF_CHILDREN) && (pItem->cChildren == tvTempItem.cChildren)) tvTempItem.mask |= TVIF_CHILDREN; if((pItem->mask & TVIF_SELECTEDIMAGE) && (pItem->iSelectedImage == tvTempItem.iSelectedImage)) tvTempItem.mask |= TVIF_SELECTEDIMAGE; //If by this point these two values are the same. //tvTempItem.hItem is the desired item return (pItem->mask == tvTempItem.mask); }