treedragdrophelper.cpp

来自「管理项目进度工具的原代码」· C++ 代码 · 共 957 行 · 第 1/2 页

CPP
957
字号
			hItem = NULL;

	m_dropPos.htiDrop = hItem;
	m_dropPos.nWhere = nWhere;

	// Highlight the item, or unhighlight all items if the cursor isn't
	// over an item. 
	if (hItem)
	{
		if (nWhere == DD_ON)
		{
			m_tree.SetInsertMark(NULL);
			m_tree.SelectDropTarget(hItem);
		}
		else
		{
			m_tree.SelectDropTarget(NULL);
			m_tree.SetInsertMark(hItem, (nWhere == DD_BELOW));
		}
	}
	else
	{
		m_tree.SetInsertMark(NULL);
		m_tree.SelectDropTarget(NULL);
	}

	// Return the handle of the highlighted item.
	return hItem;
}

void CTreeDragDropHelper::OnTimer(UINT nIDEvent)
{
	TRACE ("CTreeDragDropHelper::OnTimer\n");

	if (nIDEvent == m_nScrollTimer)
	{
		// Reset the timer
		SetTimer(TIMER_SCROLL, SCROLL_INTERVAL);
			
		// Get the current cursor position and window height.
		CPoint point(::GetMessagePos());
		ScreenToClient(m_tree, &point);
			
		CRect rect;
		m_tree.GetClientRect(rect);
		int cy = rect.Height();

		HTREEITEM hFirstVisible = m_tree.GetFirstVisibleItem();
			
		// Scroll the window if the cursor is near the top or bottom.
		m_ddMgr.DragShowNolock(FALSE);

		if (point.y >= 0 && point.y <= SCROLL_MARGIN) 
		{
			m_tree.SelectDropTarget(NULL);
			m_tree.SetInsertMark(NULL);
			m_tree.SendMessage(WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), NULL);
		}
		else if (point.y >= cy - SCROLL_MARGIN && point.y <= cy) 
		{
			m_tree.SelectDropTarget(NULL);
			m_tree.SetInsertMark(NULL);
			m_tree.SendMessage(WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), NULL);
		}

		m_ddMgr.DragShowNolock(TRUE);
				
		// Kill the timer if the window did not scroll
		// redraw the drop target highlight regardless
		if (m_tree.GetFirstVisibleItem() == hFirstVisible)
			SetTimer(TIMER_SCROLL, 0);

		HighlightDropTarget();
	}
	else if (nIDEvent == m_nExpandTimer)
	{
		// If the cursor is hovering over a collapsed item, expand the tree.
		CPoint point(::GetMessagePos());
		m_tree.ScreenToClient(&point);
		
		DDWHERE nWhere;
		HTREEITEM hItem = HitTest(point, nWhere);
		
		if (hItem != NULL && nWhere == DD_ON && m_tree.ItemHasChildren(hItem) &&
			!(m_tree.GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED)) 
		{
			m_ddMgr.DragShowNolock(FALSE);

			m_tree.SelectDropTarget(NULL);
			m_tree.SetInsertMark(NULL);
			m_tree.Expand(hItem, TVE_EXPAND);

			m_ddMgr.DragShowNolock(TRUE);
			HighlightDropTarget();

			SetTimer(TIMER_EXPAND, 0); // kill the timer
			return;
		}
	}
}

VOID CALLBACK CTreeDragDropHelper::TimerProc(HWND /*hwnd*/, UINT /*uMsg*/, UINT idEvent, DWORD /*dwTime*/)
{
	if (s_pTDDH)
		s_pTDDH->OnTimer(idEvent); // pseudo message handler
}

HTREEITEM CTreeDragDropHelper::MoveTree(HTREEITEM hDest, HTREEITEM hSrc, DDWHERE nWhere)
{
	CHoldRedraw hr(m_tree);

	HTREEITEM htiNew = CopyTree(hDest, hSrc, nWhere);
	m_tree.DeleteItem(hSrc);

	return htiNew;
}

void CTreeDragDropHelper::BuildCopy(const HTREEITEM hti, TDDHCOPY* pCopy) const
{
	pCopy->hti = hti;
	pCopy->dwItemData = m_tree.GetItemData(hti);

	HTREEITEM htiChild = m_tree.GetChildItem(hti);

	while (htiChild)
	{
		TDDHCOPY htcChild;
		BuildCopy(htiChild, &htcChild);
		pCopy->childItems.Add(htcChild);

		htiChild = m_tree.GetNextItem(htiChild, TVGN_NEXT);
	}
}

HTREEITEM CTreeDragDropHelper::CopyTree(HTREEITEM hDest, const TDDHCOPY* pSrc, DDWHERE nWhere)
{
	HTREEITEM hSrc = pSrc->hti;

	// Get the attributes of item to be copied.
	int nImage = I_IMAGECALLBACK, nSelectedImage = I_IMAGECALLBACK;

	if (!m_bTreeUsesImageCallback)
	m_tree.GetItemImage(hSrc, nImage, nSelectedImage);

	CString sText = m_tree.GetItemText(hSrc);
	LPSTR szText = m_bTreeUsesTextCallback ? LPSTR_TEXTCALLBACK : (LPSTR)(LPCTSTR)sText;

	DWORD dwItemData = m_tree.GetItemData(hSrc);
	UINT uState = m_tree.GetItemState(hSrc, TVIS_BOLD | TVIS_STATEIMAGEMASK);
	UINT uMask = TVIF_IMAGE | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT;

	HTREEITEM hParent = (nWhere != DD_ON) ? m_tree.GetParentItem(hDest) : hDest;
	HTREEITEM hAfter = NULL;
	
	if (nWhere == DD_ON)
		hAfter = TVI_LAST;
	
	else if (nWhere == DD_BELOW)
		hAfter = hDest;

	else // above
	{
		hAfter = m_tree.GetNextItem(hDest, TVGN_PREVIOUS);

		if (!hAfter)
			hAfter = TVI_FIRST;
	}

	// Create an exact copy of the item at the destination.
	HTREEITEM hNewItem = m_tree.InsertItem(uMask, szText, nImage, nSelectedImage, uState, 
								TVIS_BOLD | TVIS_STATEIMAGEMASK, dwItemData, hParent, hAfter);

	// copy children too
	if (pSrc->childItems.GetSize())
	{
		for (int nChild = 0; nChild < pSrc->childItems.GetSize(); nChild++)
		{
			const TDDHCOPY& htcChild = pSrc->childItems[nChild];
			CopyTree(hNewItem, &htcChild, DD_ON);
		}
	}

	// restore the expanded state
	if (m_tree.GetItemState(hSrc, TVIS_EXPANDED) & TVIS_EXPANDED)
		m_tree.Expand(hNewItem, TVE_EXPAND);
	else
		m_tree.Expand(hNewItem, TVE_COLLAPSE);

	return hNewItem;
}

HTREEITEM CTreeDragDropHelper::CopyTree(HTREEITEM hDest, HTREEITEM hSrc, DDWHERE nWhere)
{
	TDDHCOPY htcSrc;

	BuildCopy(hSrc, &htcSrc);
	return CopyTree(hDest, &htcSrc, nWhere);
}

BOOL CTreeDragDropHelper::CanMoveItem(HTREEITEM hti, TDDH_MOVE nDirection) const
{
	if (!hti)
		return FALSE;

	switch (nDirection)
	{
	case MOVE_DOWN:
		return (m_tree.GetNextItem(hti, TVGN_NEXT) != NULL);

	case MOVE_UP:
		return (m_tree.GetNextItem(hti, TVGN_PREVIOUS) != NULL);

	case MOVE_LEFT:
		{
			// must have a parent which will become its sibling
			// and not the root
			HTREEITEM htiParent = m_tree.GetParentItem(hti);
			return (htiParent && htiParent != TVI_ROOT);
		}
		break;

	case MOVE_RIGHT:
		// must have a prior sibling (which will become the parent)
		return CanMoveItem(hti, MOVE_UP);
	}

	return FALSE;
}

BOOL CTreeDragDropHelper::MoveSelection(TDDH_MOVE nDirection)
{
	switch (nDirection)
	{
	case MOVE_DOWN:
		return MoveSelectionDown();

	case MOVE_UP:
		return MoveSelectionUp();

	case MOVE_LEFT:
		return MoveSelectionLeft();

	case MOVE_RIGHT:
		return MoveSelectionRight();
	}

	return FALSE;
}

BOOL CTreeDragDropHelper::CanMoveSelection(TDDH_MOVE nDirection) const
{
	if (!m_selection.GetCount() || !m_selection.ItemsAreAllSiblings())
		return FALSE;

	// must be able to move all items
	POSITION pos = m_selection.GetFirstItemPos();
	
	while (pos)
	{
		HTREEITEM hti = m_selection.GetNextItem(pos);
		
		if (!CanMoveItem(hti, nDirection))
			return FALSE;
	}

	return TRUE;
}

BOOL CTreeDragDropHelper::MoveSelectionDown()
{
	if (CanMoveSelection(MOVE_DOWN))
	{
		// 0. save off the selection state so we can restore it after
		HTREEITEM htiTreeSel, htiAnchor;
		GetAnchorSel(htiAnchor, htiTreeSel);

		// 1. sort items
		m_selection.SortIfAllSiblings(FALSE);

		// 2. move items as siblings after the item following the last item
		CHTIList selection;
		m_selection.CopySelection(selection);
		m_selection.RemoveAll(TRUE, FALSE);

		HTREEITEM htiNext = m_tree.GetNextItem(selection.GetTail(), TVGN_NEXT);
		POSITION pos = selection.GetHeadPosition();

		while (pos)
		{
			HTREEITEM htiSel = selection.GetNext(pos);
			htiNext = CopyTree(htiNext, htiSel, DD_BELOW);

			UpdateAnchorSel(htiSel, htiNext, htiAnchor, htiTreeSel);

			m_tree.DeleteItem(htiSel);
			m_selection.AddItem(htiNext, FALSE);
		}
		
		// 5. restore tree state
		RestoreAnchorSel(htiAnchor, htiTreeSel);

		return TRUE;
	}

	return FALSE;
}

BOOL CTreeDragDropHelper::MoveSelectionUp()
{
	if (CanMoveSelection(MOVE_UP))
	{
		// 0. save off the selection state so we can restore it after
		HTREEITEM htiTreeSel, htiAnchor;
		GetAnchorSel(htiAnchor, htiTreeSel);

		// 1. sort items
		m_selection.SortIfAllSiblings(FALSE);

		// 2. move first item before its preceding item
		CHTIList selection;
		m_selection.CopySelection(selection);
		m_selection.RemoveAll(TRUE, FALSE);

		POSITION pos = selection.GetHeadPosition();
		HTREEITEM htiFirst = m_selection.GetNextItem(pos);
		HTREEITEM htiPrev = m_tree.GetNextItem(htiFirst, TVGN_PREVIOUS);

		HTREEITEM htiNew = CopyTree(htiPrev, htiFirst, DD_ABOVE);

		if (htiNew)
		{
			UpdateAnchorSel(htiFirst, htiNew, htiAnchor, htiTreeSel);

			m_tree.DeleteItem(htiFirst);
			m_selection.AddItem(htiNew, FALSE);

			// 3. copy rest of siblings after htiNew
			while (pos)
			{
				HTREEITEM htiSel = selection.GetNext(pos);
				htiNew = CopyTree(htiNew, htiSel, DD_BELOW);

				UpdateAnchorSel(htiSel, htiNew, htiAnchor, htiTreeSel);

				m_tree.DeleteItem(htiSel);
				m_selection.AddItem(htiNew, FALSE);
			}

			// 5. restore tree state
			RestoreAnchorSel(htiAnchor, htiTreeSel);

			return TRUE;
		}
	}

	return FALSE;
}

BOOL CTreeDragDropHelper::MoveSelectionLeft()
{
	if (CanMoveSelection(MOVE_LEFT))
	{
		// 0. save off the selection state so we can restore it after
		HTREEITEM htiTreeSel, htiAnchor;
		GetAnchorSel(htiAnchor, htiTreeSel);

		// 1. sort items
		m_selection.SortIfAllSiblings(FALSE);

		CHTIList selection;
		m_selection.CopySelection(selection);
		m_selection.RemoveAll(TRUE, FALSE);

		// 2. copy the items as siblings of the parent of the first item
		HTREEITEM htiPrev = m_tree.GetParentItem(selection.GetHead());
		POSITION pos = selection.GetHeadPosition();

		while (pos)
		{
			HTREEITEM htiSel = selection.GetNext(pos);
			htiPrev = CopyTree(htiPrev, htiSel, DD_BELOW);

			UpdateAnchorSel(htiSel, htiPrev, htiAnchor, htiTreeSel);

			m_tree.DeleteItem(htiSel);
			m_selection.AddItem(htiPrev, FALSE);
		}

		// 5. restore tree state
		RestoreAnchorSel(htiAnchor, htiTreeSel);
		
		return TRUE;
	}

	return FALSE;
}

BOOL CTreeDragDropHelper::MoveSelectionRight()
{
	if (CanMoveSelection(MOVE_RIGHT))
	{
		// 0. save off the selection state so we can restore it after
		HTREEITEM htiTreeSel, htiAnchor;
		GetAnchorSel(htiAnchor, htiTreeSel);

		// 1. sort items
		m_selection.SortIfAllSiblings(FALSE);

		CHTIList selection;
		m_selection.CopySelection(selection);
		m_selection.RemoveAll(TRUE, FALSE);

		// 2. copy the first item as a child of its immediate sibling
		POSITION pos = selection.GetHeadPosition();
		HTREEITEM htiFirst = m_selection.GetNextItem(pos);
		HTREEITEM htiParent = m_tree.GetNextItem(htiFirst, TVGN_PREVIOUS);

		HTREEITEM htiNew = CopyTree(htiParent, htiFirst, DD_ON);

		if (htiNew)
		{
			UpdateAnchorSel(htiFirst, htiNew, htiAnchor, htiTreeSel);

			m_tree.DeleteItem(htiFirst);
			m_selection.AddItem(htiNew, FALSE);

			// 3. then copy the rest of the selection as siblings of the first
			while (pos)
			{
				HTREEITEM htiSel = selection.GetNext(pos);
				htiNew = CopyTree(htiNew, htiSel, DD_BELOW);

				UpdateAnchorSel(htiSel, htiNew, htiAnchor, htiTreeSel);

				m_tree.DeleteItem(htiSel);
				m_selection.AddItem(htiNew, FALSE);
			}

			// 4. expand parent
			m_tree.Expand(htiParent, TVE_EXPAND);

			// 5. restore tree state
			RestoreAnchorSel(htiAnchor, htiTreeSel);

			return TRUE;
		}
	}

	return FALSE;
}

void CTreeDragDropHelper::GetAnchorSel(HTREEITEM& htiAnchor, HTREEITEM& htiTreeSel) const
{
	htiTreeSel = m_tree.GetSelectedItem();
	htiAnchor = m_selection.GetAnchor();
}

void CTreeDragDropHelper::UpdateAnchorSel(HTREEITEM htiPrev, HTREEITEM htiNew, 
										  HTREEITEM& htiAnchor, HTREEITEM& htiTreeSel) const
{
	if (htiTreeSel == htiPrev)
		htiTreeSel = htiNew;
	
	if (htiAnchor == htiPrev)
		htiAnchor = htiNew;
}

void CTreeDragDropHelper::RestoreAnchorSel(HTREEITEM htiAnchor, HTREEITEM htiTreeSel)
{
#ifdef _DEBUG
//	DWORD dwIDAnchor = m_tree.GetItemData(htiAnchor);
//	DWORD dwIDSel = m_tree.GetItemData(htiTreeSel);
#endif

	m_tree.SelectItem(htiTreeSel);
	m_selection.SetAnchor(htiAnchor);
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?