// ▼ コメント BGN 2003.03.27 (moralog)
// 拾ってきたコード。
// CTreeViewEx
// http://www5.wisnet.ne.jp/~mercury/program/windows/lib/treeviewex.html
// ▲ コメント END

// TreeViewExt.cpp : implementation file
//

#include "stdafx.h"
#include "TreeViewEx.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CTreeViewEx

IMPLEMENT_DYNCREATE(CTreeViewEx, CTreeView)

CTreeViewEx::CTreeViewEx()
	: m_pDragImage(NULL), 
	  m_hDragItem(NULL), 
	  m_hDropItem(NULL),
	  m_hOldDropItem(NULL),
	  m_bLDragging(false),
	  m_idTimer(0)
{
	m_bKeepIndentLevel = TRUE;
}

CTreeViewEx::~CTreeViewEx()
{
}

BEGIN_MESSAGE_MAP(CTreeViewEx, CTreeView)
	//{{AFX_MSG_MAP(CTreeViewEx)
	ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBeginDrag)
	ON_NOTIFY_REFLECT(TVN_BEGINLABELEDIT, OnBeginLabelEdit)
	ON_NOTIFY_REFLECT(TVN_ENDLABELEDIT, OnEndLabelEdit)
	ON_WM_DESTROY()
	ON_WM_TIMER()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTreeViewEx drawing

void CTreeViewEx::OnDraw(CDC* pDC)
{
	CDocument* pDoc = GetDocument();
	// TODO: add draw code here
}

/////////////////////////////////////////////////////////////////////////////
// CTreeViewEx diagnostics

#ifdef _DEBUG
void CTreeViewEx::AssertValid() const
{
	CTreeView::AssertValid();
}

void CTreeViewEx::Dump(CDumpContext& dc) const
{
	CTreeView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CTreeViewEx message handlers
void CTreeViewEx::OnTimer(UINT nIDEvent) 
{
	if( nIDEvent == m_idTimer ) {
		CTreeCtrl& theTree = GetTreeCtrl();
		HTREEITEM htiFloat = theTree.GetDropHilightItem();
		if( htiFloat && htiFloat == m_hDropItem ) {
			if( theTree.ItemHasChildren( htiFloat ) )
				theTree.Expand( htiFloat, TVE_EXPAND );
		}
	}

	CTreeView::OnTimer(nIDEvent);
}

void CTreeViewEx::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

	m_hDragItem = GetTreeCtrl().HitTest(pNMTreeView->ptDrag, NULL);//pNMTreeView->itemNew.hItem;
	m_hDropItem = NULL;

	m_pDragImage = GetTreeCtrl().CreateDragImage( m_hDragItem );
	if( !m_pDragImage )
		return;

	m_bLDragging = true;

	CPoint pt(0,0);
	
	IMAGEINFO ii;
	m_pDragImage->GetImageInfo( 0, &ii );
	pt.x = (ii.rcImage.right - ii.rcImage.left) / 2;
	pt.y = (ii.rcImage.bottom - ii.rcImage.top) / 2;

	m_pDragImage->BeginDrag( 0, pt );
	pt = pNMTreeView->ptDrag;
	ClientToScreen( &pt );
	m_pDragImage->DragEnter(NULL,pt);
	
	SetCapture();

	*pResult = 0;
}

int CTreeViewEx::GetIndentLevel(HTREEITEM hItem)
{
	int iIndent = 0;
	while ((hItem = GetTreeCtrl().GetParentItem(hItem)) != NULL) {
		iIndent++;
	}
	return iIndent;
}

void CTreeViewEx::OnLButtonUp(UINT nFlags, CPoint point) 
{
	CTreeView::OnLButtonUp(nFlags, point);

	if( m_bLDragging )
	{
		CTreeCtrl& theTree = GetTreeCtrl();

		m_bLDragging = false;

		CImageList::DragLeave(this);
		CImageList::EndDrag();

		ReleaseCapture();

		delete m_pDragImage;

		theTree.SelectDropTarget(NULL);
		m_hOldDropItem = NULL;

		if( m_hDragItem == m_hDropItem )
			return;

		HTREEITEM htiParent = m_hDropItem;
		while( (htiParent = theTree.GetParentItem(htiParent)) != NULL )
		{
			if( htiParent == m_hDragItem ) return;
		}

		HTREEITEM htiPosition = TVI_LAST;

		// インデントレベルを固定する場合の処理
		if (m_bKeepIndentLevel) {
			int nDragIndent = GetIndentLevel(m_hDragItem);
			int nDropIndent = GetIndentLevel(m_hDropItem);
			if (nDragIndent == nDropIndent) {
				// add after the drop item, as child of drop item's parent
				htiPosition = m_hDropItem;
				m_hDropItem = theTree.GetParentItem(m_hDropItem);
			} else if (nDragIndent == (nDropIndent + 1)) {
				// if dropped on 'upper' indentation, add as last child of drop item
				htiPosition = TVI_LAST;
			} else {
				// invalid drop target
				return;
			}
		}

		theTree.Expand(m_hDropItem, TVE_EXPAND);

		HTREEITEM htiNew = MoveBranch(m_hDragItem, m_hDropItem, htiPosition);
		SelectItem(htiNew);

		if (m_idTimer) {
			KillTimer(m_idTimer);
			m_idTimer = 0;
		}
	}
}

void CTreeViewEx::OnMouseMove(UINT nFlags, CPoint point) 
{
	CTreeView::OnMouseMove(nFlags, point);

	HTREEITEM hti;
	UINT      flags;

	if (m_bLDragging) {
		CTreeCtrl& theTree = GetTreeCtrl();
		POINT pt = point;
		ClientToScreen( &pt );
		CImageList::DragMove(pt);
		
		hti = theTree.HitTest(point,&flags);
		if (hti != NULL) {
			CImageList::DragShowNolock(FALSE);

			if (m_hOldDropItem == NULL) {
				m_hOldDropItem = theTree.GetDropHilightItem();
			}

			theTree.SelectDropTarget(hti);

			m_hDropItem = hti;

			if (m_idTimer && hti == m_hOldDropItem) {
				KillTimer(m_idTimer);
				m_idTimer = 0;
			}
			
			if (!m_idTimer) {
				m_idTimer = SetTimer( 1000, 2000, NULL );
			}

			CImageList::DragShowNolock(TRUE);
		}
	}
}

void CTreeViewEx::SelectItem(HTREEITEM hItem)
{
	GetTreeCtrl().SelectItem(hItem);
}

void CTreeViewEx::DeleteItem(HTREEITEM hItem)
{
	GetTreeCtrl().DeleteItem(hItem);
}

// ノードの複製
// アイテムのコピーハンドラ
HTREEITEM CTreeViewEx::CopyItem(
	HTREEITEM	hSrcNode,
	HTREEITEM	hNewParent,
	HTREEITEM	hAfter
)
{
	CTreeCtrl& theTree = GetTreeCtrl();

// ▼ コピーハンドラの追加 BGN 2003.03.19 (moralog)
	// コピーハンドラの起動
	// ツリーの操作によって起こるデータの変更のきっかけを与える
	BOOL	bProcess = OnCopyItem( hSrcNode, hNewParent, hAfter );
	if ( bProcess )
	{
		TVINSERTSTRUCT insert;
		::ZeroMemory(&insert, sizeof(TVINSERTSTRUCT));

		HTREEITEM hSrcNodeNew = NULL;
		CString text;

		insert.item.hItem = hSrcNode;
		insert.item.mask = TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
		theTree.GetItem(&(insert.item));
		text = theTree.GetItemText( hSrcNode );

		insert.item.cchTextMax = text.GetLength();
		insert.item.pszText = text.LockBuffer();

		insert.hParent = hNewParent;
		insert.hInsertAfter = hAfter;
		insert.item.mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT;
		hSrcNodeNew = theTree.InsertItem(&insert);
		text.UnlockBuffer();

		theTree.SetItemData( hSrcNodeNew, theTree.GetItemData( hSrcNode ) );
		theTree.SetItemState( hSrcNodeNew, theTree.GetItemState( hSrcNode, TVIS_STATEIMAGEMASK ), TVIS_STATEIMAGEMASK );

		// 追加後の処理ハンドラ
		OnCopiedItem( hSrcNodeNew, hSrcNode, hNewParent, hAfter );

		return hSrcNodeNew;
	}
	return( NULL );
/*
// ▽ oridinal BGN
	TVINSERTSTRUCT insert;
	::ZeroMemory(&insert, sizeof(TVINSERTSTRUCT));

	HTREEITEM htiNew = NULL;
	CString text;

	insert.item.hItem = hti;
	insert.item.mask = TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
	theTree.GetItem(&(insert.item));
	text = theTree.GetItemText( hti );

	insert.item.cchTextMax = text.GetLength();
	insert.item.pszText = text.LockBuffer();

	insert.hParent = htiNewParent;
	insert.hInsertAfter = htiAfter;
	insert.item.mask = TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT;
	htiNew = theTree.InsertItem(&insert);
	text.UnlockBuffer();

	theTree.SetItemData( htiNew, theTree.GetItemData( hti ) );
	theTree.SetItemState( htiNew, theTree.GetItemState( hti, TVIS_STATEIMAGEMASK ), TVIS_STATEIMAGEMASK );

	return htiNew;
// △ oridinal END
*/
// ▲ コピーハンドラの追加 END
}

HTREEITEM CTreeViewEx::CopyBranch(HTREEITEM hti, HTREEITEM htiNewParent, HTREEITEM htiAfter)
{
	CTreeCtrl& theTree = GetTreeCtrl();

	HTREEITEM htiChild = NULL;

	HTREEITEM htiNew = CopyItem(hti, htiNewParent, htiAfter);
	htiChild = theTree.GetChildItem(hti);
	while (htiChild != NULL) {
		CopyBranch(htiChild, htiNew);
		htiChild = theTree.GetNextSiblingItem(htiChild);
	}

	return htiNew;
}

HTREEITEM CTreeViewEx::MoveBranch(HTREEITEM m_hDragItem, HTREEITEM m_hDropItem, HTREEITEM htiPosition)
{
	HTREEITEM htiNew = CopyBranch(m_hDragItem, m_hDropItem, htiPosition);
	DeleteItem(m_hDragItem);
	return(htiNew);
}

void CTreeViewEx::OnDestroy() 
{
// ▼ 属性解放の削除 BGN 2003.03.19 (moralog)
// 派生クラスではImageListをメンバ変数として持つことにする。
// よってdeleteは必要ない。
// ▽ oridinal BGN
//	CImageList* pImage = GetTreeCtrl().GetImageList( TVSIL_NORMAL );
//	delete pImage;
// △ oridinal END
// ▲ 属性解放の削除 END

	if (m_idTimer ) {
		KillTimer(m_idTimer);
		m_idTimer = 0;
	}

	CTreeView::OnDestroy();
}

void CTreeViewEx::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) 
{
	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;

	// Limit text to 127 characters
	GetTreeCtrl().GetEditControl()->LimitText(127);

	*pResult = 0;
}

void CTreeViewEx::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) 
{
	// Set result to TRUE to accept the changes
	*pResult = 0;
}

BOOL CTreeViewEx::PreTranslateMessage(MSG* pMsg) 
{
	if (pMsg->message == WM_KEYDOWN) {
		// ラベル編集中に[Enter][DEL][ESC]の操作を有効にする
		if (GetTreeCtrl().GetEditControl()
		&&  (pMsg->wParam == VK_RETURN
		  || pMsg->wParam == VK_DELETE
		  || pMsg->wParam == VK_ESCAPE
		  || GetKeyState(VK_CONTROL))) {
			::TranslateMessage(pMsg);
			::DispatchMessage(pMsg);
			return TRUE;				// DO NOT process further
		}

		// F2 で選択されている項目のラベル編集開始
		if (GetTreeCtrl().GetEditControl() == NULL
		&&  GetTreeCtrl().GetSelectedItem() != NULL
		&&  pMsg->wParam == VK_F2) {
			GetTreeCtrl().EditLabel(GetTreeCtrl().GetSelectedItem());
			return TRUE;
		}
	}

#if 0
	// 強制的にラベルの変更を禁止する処理
	if( pMsg->message == WM_LBUTTONDOWN ) {
		UINT		flag = TVHT_ONITEMLABEL;
		CPoint		pt = pMsg->pt;

		ScreenToClient(&pt);

		if (GetTreeCtrl().HitTest(pt, &flag) == GetTreeCtrl().GetSelectedItem()) {
			GetTreeCtrl().SetFocus();	//We must set focus to the tree view control
										//becouse it can have no focus
			return TRUE ;   // DO NOT process further
		}
	}
#endif

	return CTreeView::PreTranslateMessage(pMsg);
}
