// ProjectTreeView.cpp : implementation of the CProjectTreeView class
//

#include "stdafx.h"
#include "AsCfgBuilder.h"
#include "ProjectDoc.h"
#include "ProjectTreeView.h"
#include "XmlBaseElement.h"
#include "XmlClassInfo.h"
#include "MemDC.h"

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


#define ICON_INDEX_CLASS	0
#define ICON_INDEX_MEMBER	1
#define ICON_INDEX_LINK		2
#define ICON_INDEX_LOCK		3

/////////////////////////////////////////////////////////////////////////////
// CProjectTreeView

IMPLEMENT_DYNCREATE(CProjectTreeView, CTreeView)

BEGIN_MESSAGE_MAP(CProjectTreeView, CTreeView)
	//{{AFX_MSG_MAP(CProjectTreeView)
	ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelchanged)
	ON_NOTIFY_REFLECT(TVN_ENDLABELEDIT, OnEndLabelEdit)
	ON_COMMAND(ID_STRUCTURE_APPENDCLASSELEMENT, OnMnuAppendClassElement)
	ON_UPDATE_COMMAND_UI(ID_STRUCTURE_APPENDCLASSELEMENT, OnUpdateMnuAppendClassElement)
	ON_COMMAND(ID_STRUCTURE_APPENDMEMBERELEMENT, OnMnuAppendMemberElement)
	ON_UPDATE_COMMAND_UI(ID_STRUCTURE_APPENDMEMBERELEMENT, OnUpdateMnuAppendMemberElement)
	ON_COMMAND(ID_STRUCTURE_REMOVEELEMENT, OnMnuRemoveElement)
	ON_UPDATE_COMMAND_UI(ID_STRUCTURE_REMOVEELEMENT, OnUpdateMnuRemoveElement)
	ON_WM_RBUTTONDOWN()
	ON_WM_ERASEBKGND()
	ON_WM_SIZE()
	ON_WM_PAINT()
	ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag)
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, CTreeView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, CTreeView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, CTreeView::OnFilePrintPreview)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CProjectTreeView construction/destruction

CProjectTreeView::CProjectTreeView():
	displayItemId_(false),
	isDragging_(false),
	hDragItem_(NULL),
	dragImageList_(NULL),
	scrollMargin_(10),
	scrollInterval_(200),
	delayInterval_(500)

{

}

CProjectTreeView::~CProjectTreeView()
{
}

BOOL CProjectTreeView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return CTreeView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CProjectTreeView drawing

void CProjectTreeView::OnDraw(CDC* pDC)
{
	CProjectDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	
}


/////////////////////////////////////////////////////////////////////////////
// CProjectTreeView printing

BOOL CProjectTreeView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// default preparation
	return DoPreparePrinting(pInfo);
}

void CProjectTreeView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add extra initialization before printing
}

void CProjectTreeView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add cleanup after printing
}

void CProjectTreeView::OnInitialUpdate()
{
	CTreeView::OnInitialUpdate();

	ModifyStyle(0, /*TVS_EDITLABELS | */TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS);

	if (!imageList_.GetSafeHandle()) {
		imageList_.Create(16, 16, ILC_COLOR32 | ILC_MASK, 4, 4);
		imageList_.Add(AfxGetApp()->LoadIcon(IDI_CLASS));
		imageList_.Add(AfxGetApp()->LoadIcon(IDI_CLASS_MEMBER));
		imageList_.Add(AfxGetApp()->LoadIcon(IDI_LINK));
		imageList_.Add(AfxGetApp()->LoadIcon(IDI_LOCK));

		GetTreeCtrl().SetImageList(&imageList_, TVSIL_NORMAL);

		BuildTree();
	}

}

/////////////////////////////////////////////////////////////////////////////
// CProjectTreeView diagnostics

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

void CProjectTreeView::Dump(CDumpContext& dc) const
{
	CTreeView::Dump(dc);
}

CProjectDoc* CProjectTreeView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CProjectDoc)));
	return (CProjectDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CProjectTreeView message handlers

void CProjectTreeView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
	HTREEITEM selItem;
	CXmlBaseElement* selElement;

	switch ((enUpdateViewReason)lHint) {
		case enUpdateViewReason_DocLoaded:
			BuildTree();
			break;

		case enUpdateViewReason_ItemNameChanged:
			selElement = GetSelectedItem();
			selItem = GetTreeCtrl().GetSelectedItem();
			if (selElement)
				GetTreeCtrl().SetItemText(selItem, FormatNodeLabel(selElement));
			break;

		case enUpdateViewReason_ItemChanged:
			UpdateAllStates();
	}
	
	
}

void CProjectTreeView::BuildTree()
{
	CProjectDoc* doc;

	doc = GetDocument();

	Clear();
	if (doc->GetDisplayRootNode()) {
		CreateNode(doc->GetProject()->GetRootElement());
	} else {
		ClassMemberMap* members;
		ClassMemberMap::iterator it;
		CXmlBaseElement* member;

		members = doc->GetProject()->GetRootElement()->GetMembers();

		for (it = members->begin(); it != members->end(); it++) {
			member = it->second;

			CreateNode(member);
		}
	}
}

void CProjectTreeView::Clear()
{
	GetTreeCtrl().DeleteAllItems();
	treeItems_.clear();
}

HTREEITEM CProjectTreeView::CreateNode(CXmlBaseElement* xmlElement, HTREEITEM parentNode) 
{
	HTREEITEM newTreeNode;
	int iconIndex;
	cmString nodeCaption;
	
	if (xmlElement->GetDataType() == enDataType_Class) {
		CXmlClassInfo* classInfo;

		classInfo = dynamic_cast<CXmlClassInfo*>(xmlElement);

		if (classInfo->GetLockedByInheritance())
			iconIndex = ICON_INDEX_LOCK;
		else if (classInfo->GetDefinitionInheritance())
			iconIndex = ICON_INDEX_LINK;
		else
			iconIndex = ICON_INDEX_CLASS;
	} else {
		iconIndex = ICON_INDEX_MEMBER;
	}
	
	nodeCaption = FormatNodeLabel(xmlElement);

	newTreeNode = GetTreeCtrl().InsertItem(nodeCaption, iconIndex, iconIndex, parentNode);
	//UpdateNodeState(newTreeNode);

	GetTreeCtrl().RedrawWindow();

	treeItems_[newTreeNode] = xmlElement->GetId();
	
	// create members nodes
	//
	if (xmlElement->GetDataType() == enDataType_Class) {
		ClassMemberMap* members;
		ClassMemberMap::iterator it;
		CXmlBaseElement* member;

		members = dynamic_cast<CXmlClassInfo*>(xmlElement)->GetMembers();

		for (it = members->begin(); it != members->end(); it++) {
			member = it->second;

			CreateNode(member, newTreeNode);
		}
		//GetTreeCtrl().Expand(newTreeNode, TVE_EXPAND);
	}

	return newTreeNode;
}

void CProjectTreeView::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	
	TreeItemToIdMap::iterator it;
	
	it = treeItems_.find(GetTreeCtrl().GetSelectedItem());
	if (it != treeItems_.end()) {
		CProjectDoc* doc;

		doc = GetDocument();
		doc->SetSelectedItemId(it->second);	

		doc->UpdateAllViews(this, enUpdateViewReason_SelectionChanged);

		RedrawWindow();
	}
	

	
	*pResult = 0;
}

void CProjectTreeView::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) 
{
	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
	
	if (pTVDispInfo->item.mask == TVIF_TEXT) {
		cmString id;
		CXmlBaseElement* selElement;
		HTREEITEM selItem;

		selItem = GetTreeCtrl().GetSelectedItem();
		id = treeItems_[selItem];
		GetDocument()->SetSelectedItemId(id);
		
		selElement = GetDocument()->GetElementById(id);
		
		selElement->SetName(pTVDispInfo->item.pszText);
		GetDocument()->UpdateAllViews(this, enUpdateViewReason_ItemNameChanged);
		
		GetTreeCtrl().SetItemText(selItem, pTVDispInfo->item.pszText);
	}

	*pResult = 0;
}

void CProjectTreeView::OnMnuAppendClassElement() 
{
	CXmlBaseElement* selElement;

	selElement = GetSelectedItem();
	if (selElement && (selElement->GetDataType() == enDataType_Class))
		AppendNode(enDataType_Class, selElement, GetTreeItemById(selElement->GetId()));
	else
		AppendNode(enDataType_Class, GetDocument()->GetProject()->GetRootElement(), GetTreeCtrl().GetRootItem());
}

void CProjectTreeView::OnUpdateMnuAppendClassElement(CCmdUI* pCmdUI) 
{
	CXmlBaseElement* selElement;

	selElement = GetSelectedItem();
	if (selElement)
		pCmdUI->Enable(selElement->GetDataType() == enDataType_Class);
	else
		pCmdUI->Enable(FALSE);
}

void CProjectTreeView::OnMnuAppendMemberElement() 
{
	CXmlBaseElement* selElement;

	selElement = GetSelectedItem();
	if (selElement && (selElement->GetDataType() == enDataType_Class))
		AppendNode(enDataType_String, selElement, GetTreeItemById(selElement->GetId()));
	else
		AppendNode(enDataType_String, GetDocument()->GetProject()->GetRootElement(), GetTreeCtrl().GetRootItem());
}

void CProjectTreeView::OnUpdateMnuAppendMemberElement(CCmdUI* pCmdUI) 
{
	CXmlBaseElement* selElement;

	selElement = GetSelectedItem();
	if (selElement)
		pCmdUI->Enable(selElement->GetDataType() == enDataType_Class);
	else
		pCmdUI->Enable(FALSE);
}

void CProjectTreeView::OnMnuRemoveElement() 
{
	HTREEITEM selNode;
	
	selNode = GetTreeCtrl().GetSelectedItem();
	
	if (selNode)
		RemoveItem(selNode);

	GetTreeCtrl().Select(GetTreeCtrl().GetParentItem(selNode), TVGN_CARET | TVGN_DROPHILITE);
}

void CProjectTreeView::OnUpdateMnuRemoveElement(CCmdUI* pCmdUI) 
{
	CXmlBaseElement* selElement;

	selElement = GetSelectedItem();
	if (selElement && selElement->GetId() != GetDocument()->GetProject()->GetRootElement()->GetId())
		pCmdUI->Enable(TRUE);
	else
		pCmdUI->Enable(FALSE);
}



void CProjectTreeView::OnRButtonDown(UINT nFlags, CPoint point) 
{
	
	CTreeView::OnRButtonDown(nFlags, point);
}

BOOL CProjectTreeView::PreTranslateMessage(MSG* pMsg) 
{
	if (pMsg->message == WM_RBUTTONDOWN) {
		CMenu* structMenu;
		UINT flags;
		HTREEITEM selItem;
		CPoint tmpPoint(pMsg->pt);
		CRect rc;

		// select item
		//
		ScreenToClient(&tmpPoint);
		selItem = GetTreeCtrl().HitTest(tmpPoint, &flags);
		GetTreeCtrl().Select(selItem, TVGN_CARET | TVGN_DROPHILITE);
		
		// get menu top-left point
		//
		if (GetTreeCtrl().GetItemRect( selItem, rc, TRUE)) {
			tmpPoint.x = rc.right;
			tmpPoint.y = rc.top;

			ClientToScreen(&tmpPoint);
		} else {
			tmpPoint = pMsg->pt;
		}
		
		// display menu
		//
		CWnd* mainWindow = AfxGetMainWnd();
		CMenu* topLevelMenu = mainWindow->GetMenu();

		structMenu = topLevelMenu->GetSubMenu(2);
		structMenu->TrackPopupMenu( TPM_LEFTALIGN | TPM_TOPALIGN, tmpPoint.x, tmpPoint.y, mainWindow);

		return TRUE;
	}
		
	return CTreeView::PreTranslateMessage(pMsg);
}

CXmlBaseElement* CProjectTreeView::GetSelectedItem()
{
	HTREEITEM selItem;
	cmString id;
	CXmlBaseElement* selElement;

	selItem = GetTreeCtrl().GetSelectedItem();
	if (selItem) {
		id = treeItems_[selItem];
		selElement = GetDocument()->GetElementById(id);
	} else {
		selElement = GetDocument()->GetProject()->GetRootElement();
	}

	return selElement;
}

CXmlBaseElement* CProjectTreeView::GetItem(HTREEITEM hItem)
{
	CXmlBaseElement* ret;
	cmString id;

	id = treeItems_[hItem];
	ret = GetDocument()->GetElementById(id);

	return ret;
}

void CProjectTreeView::AppendNode(enDataType dataType, CXmlBaseElement* parentXmlElement, HTREEITEM parentTreeItem)
{
	if (parentXmlElement == NULL)
		return;

	if (parentXmlElement->GetDataType() != enDataType_Class)
		return;

	CXmlClassInfo* classInfo;
	CXmlBaseElement* newElement;
	
	classInfo = dynamic_cast<CXmlClassInfo*>(parentXmlElement);
	
	newElement = classInfo->AddMember(dataType);

	CreateNode(newElement, parentTreeItem);
}

HTREEITEM CProjectTreeView::GetTreeItemById(const cmString& id)
{
	TreeItemToIdMap::iterator it;

	for (it = treeItems_.begin(); it != treeItems_.end(); it++) {
		if (it->second == id)
			return it->first;
	}

	return NULL;
}

void CProjectTreeView::UpdateAllStates()
{
	TreeItemToIdMap::iterator it;

	for (it = treeItems_.begin(); it != treeItems_.end(); it++)
		UpdateNode(it->first);
}

void CProjectTreeView::UpdateNode(HTREEITEM item)
{
	cmString id;
	CXmlClassInfo* classInfo;
	CXmlBaseElement* element;

	id = treeItems_[item];
	element = GetDocument()->GetElementById(id);
	classInfo = dynamic_cast<CXmlClassInfo*>(element);

	if (classInfo) {
		int iconIndex;
		
		if(classInfo->GetLockedByInheritance())
			iconIndex = ICON_INDEX_LOCK;
		else if (classInfo->GetDefinitionInheritance())
			iconIndex = ICON_INDEX_LINK;
		else
			iconIndex = ICON_INDEX_CLASS;
		
		

		GetTreeCtrl().SetItemImage(GetTreeItemById(classInfo->GetId()), iconIndex, iconIndex);
	}

	CString curItemText;
	cmString newItemText;

	curItemText = GetTreeCtrl().GetItemText(item);
	newItemText = FormatNodeLabel(element);
	if ((const TCHAR*)curItemText != newItemText)
		GetTreeCtrl().SetItemText(item, newItemText); 
}

cmString CProjectTreeView::FormatNodeLabel(CXmlBaseElement* element)
{
	cmString ret;

	if (element) {
		ret = element->GetName() + _T(" As ") + CXmlBaseElement::DataType2String(element->GetDataType());
		if (displayItemId_)
			ret += _T(" [") + element->GetId() + _T("]");
	}

	return ret;
}

void CProjectTreeView::DisplayItemId(bool display)
{
	displayItemId_ = display;

	UpdateAllLabels();
}
BOOL CProjectTreeView::OnEraseBkgnd(CDC* pDC) 
{
	return TRUE;
	
	return CTreeView::OnEraseBkgnd(pDC);
}

void CProjectTreeView::OnSize(UINT nType, int cx, int cy) 
{
	CTreeView::OnSize(nType, cx, cy);
	
	GetClientRect(clientRect_);
	
}

void CProjectTreeView::OnPaint() 
{
	CPaintDC dc(this); // device context for painting
	CMemDC memDC(&dc);//, &clientRect_);
	
	CWnd::DefWindowProc(WM_PAINT, (WPARAM)memDC.m_hDC, 0);
}

void CProjectTreeView::UpdateAllLabels()
{
	UpdateNodeLabel(GetTreeCtrl().GetRootItem());
}

void CProjectTreeView::UpdateNodeLabel(HTREEITEM node)
{
	CXmlBaseElement* element;
	TreeItemToIdMap::iterator it;

	it = treeItems_.find(node);
	assert(it != treeItems_.end());
	if (it == treeItems_.end())
		return;

	element = GetDocument()->GetElementById(it->second);
	
	GetTreeCtrl().SetItemText(node, FormatNodeLabel(element));

	// update labels on children
	//
	if (!GetTreeCtrl().ItemHasChildren(node))
		return;

	HTREEITEM child;

	child = GetTreeCtrl().GetChildItem(node);
	while(child) {
		UpdateNodeLabel(child);
	
		child = GetTreeCtrl().GetNextSiblingItem(child);
	}
}
void CProjectTreeView::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	
	//
	// Do nothing if the user is attempting to drag a top-level item.
	//
	HTREEITEM hItem = pNMTreeView->itemNew.hItem;
	if (GetTreeCtrl().GetParentItem (hItem) == NULL)
		return;

	//
	// Create a drag image. If the assertion fails, you probably forgot
	// to assign an image list to the control with SetImageList. Create-
	// DragImage will not work if the control hasn't been assigned an
	// image list!
	//
	dragImageList_ = GetTreeCtrl().CreateDragImage(hItem);
	ASSERT (dragImageList_ != NULL);

	if (dragImageList_ != NULL) { // Just to be sure
		//
		// Compute the coordinates of the "hot spot"--the location of the
		// cursor relative to the upper left corner of the item rectangle.
		//
		CRect rect;
		GetTreeCtrl().GetItemRect (hItem, rect, TRUE);
		CPoint point (pNMTreeView->ptDrag.x, pNMTreeView->ptDrag.y);
		CPoint hotSpot = point;
		hotSpot.x -= rect.left;
		hotSpot.y -= rect.top;

		//
		// Convert the client coordinates in "point" to coordinates relative
		// to the upper left corner of the control window.
		//
		CPoint client (0, 0);
		ClientToScreen (&client);
		GetWindowRect (rect);
		point.x += client.x - rect.left;
		point.y += client.y - rect.top;

		//
		// Capture the mouse and begin dragging.
		//
		SetCapture ();
		dragImageList_->BeginDrag (0, hotSpot);
		dragImageList_->DragEnter (this, point);
		hDragItem_ = hItem;
		isDragging_ = true;
	}

	*pResult = 0;
}

//DEL void CProjectTreeView::BeginDrag (NM_TREEVIEW *item)
//DEL {
//DEL 	CRect rc;
//DEL 	CPoint pt;
//DEL 
//DEL 	GetCursorPos(&pt);
//DEL 
//DEL 	isDragging_ = true;
//DEL 	hDragItem_ = item->itemNew.hItem;
//DEL 
//DEL 	TRACE(_T("BeginDrag\n"));
//DEL 
//DEL 	// create an image to use for dragging.
//DEL 	//
//DEL 	dragImageList_ = GetTreeCtrl().CreateDragImage(hDragItem_);
//DEL 	
//DEL 	// get the bounding rectangle of the item being dragged.
//DEL 	//
//DEL 	GetTreeCtrl().GetItemRect(hDragItem_, rc, TRUE);
//DEL 	
//DEL 	// start dragging the image.
//DEL 	//
//DEL 	dragImageList_->BeginDrag(0, CPoint(0, 0));
//DEL 	dragImageList_->DragEnter(this, pt);
//DEL 	
//DEL 	// capture the mouse.
//DEL 	//
//DEL 	GetTreeCtrl().SetCapture();
//DEL }

void CProjectTreeView::OnMouseMove(UINT nFlags, CPoint point) 
{
	//HTREEITEM hTarget;
	//UINT flags;

	if (isDragging_ && dragImageList_ != NULL) {
		//
		// Stop the scroll timer if it's running.
		//
		KillTimer (1);

		//
		// Erase the old drag image and draw a new one.
		//
		dragImageList_->DragMove (point);

		//
		// Highlight the drop target if the cursor is over an item.
		//
		HTREEITEM hItem = HighlightDropTarget (point);

		//
		// Modify the cursor to provide visual feedback to the user.
		// Note: It's important to do this AFTER the call to DragMove.
		//
		::SetCursor (hItem == NULL ?
			AfxGetApp ()->LoadStandardCursor (IDC_NO) :
			(HCURSOR) ::GetClassLong (m_hWnd, GCL_HCURSOR));

		//
		// Set a timer if the cursor is at the top or bottom of the window,
		// or if it's over a collapsed item.
		//
		CRect rect;
		GetClientRect (rect);
		int cy = rect.Height ();

		if ((point.y >= 0 && point.y <= scrollMargin_) ||
			(point.y >= cy - scrollMargin_ && point.y <= cy) ||
			(hItem != NULL && GetTreeCtrl().ItemHasChildren (hItem) &&
			!IsItemExpanded (hItem)))

			SetTimer (1, delayInterval_, NULL);
	}

	CTreeView::OnMouseMove(nFlags, point);
}

void CProjectTreeView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	
	if (isDragging_ && dragImageList_) {
		// Stop the scroll timer if it's running.
		//
		KillTimer (1);

		// Terminate the dragging operation and release the mouse.
		//
		dragImageList_->DragLeave (this);
		dragImageList_->EndDrag ();
		::ReleaseCapture ();
		isDragging_ = false;
		GetTreeCtrl().SelectDropTarget(NULL);

		// Delete the image list created by CreateDragImage.
		//
		delete dragImageList_;
		dragImageList_ = NULL;

		// Get the HTREEITEM of the drop target and exit now if it's NULL.
		//
		UINT nFlags;
		HTREEITEM hItem = GetTreeCtrl().HitTest (point, &nFlags);
		if (hItem == NULL)
			return;

		// Display an error message if the move is illegal.
		//
		if (hItem == hDragItem_) {
			MessageBox (_T ("An item can't be dropped onto itself"));
			return;
		}
		
		if (hItem == GetTreeCtrl().GetParentItem (hDragItem_)) {
			MessageBox (_T ("An item can't be dropped onto its parent"));
			return;
		}
		
		if (IsChildOf (hItem, hDragItem_)) {
			MessageBox (_T ("An item can't be dropped onto one of its children"));
			return;
		}

		CXmlBaseElement* dragSource;
		CXmlClassInfo* dragSourceParent;
		CXmlClassInfo* dropTarget;
		
		dragSource = GetItem(hDragItem_);
		dragSourceParent = dynamic_cast<CXmlClassInfo*>(GetItem(GetTreeCtrl().GetParentItem(hDragItem_)));
		dropTarget = dynamic_cast<CXmlClassInfo*>(GetItem(hItem));
		if (dropTarget == NULL) {
			MessageBox (_T ("A member element can be dropped only onto classes"));
			return;
		}
				
		// move the XmlBaseElement to the target
		//
		dragSourceParent->DetachMember(dragSource->GetId());
		dropTarget->AttachMember(dragSource);

		// delete drag source node
		//
		GetTreeCtrl().DeleteItem(hDragItem_);
		
		// create new subtree
		//
		HTREEITEM newItem;

		newItem = CreateNode(dragSource, hItem);
		GetTreeCtrl().SortChildren(hItem);
		GetTreeCtrl().SelectItem(newItem);
		if (IsItemExpanded(hItem))
			GetTreeCtrl().EnsureVisible(newItem);

		//
		// Move the dragged item and its subitems (if any) to the drop point.
		//
		/***
		
		
		newItem = MoveTree (hItem, hDragItem_);
		
		// update node2id
		//
		treeItems_.erase(hDragItem_);
		treeItems_[newItem] = dragSource->GetId();

		***/

		hDragItem_ = NULL;
	
	}

	CTreeView::OnLButtonUp(nFlags, point);
}

//DEL void CProjectTreeView::DropItem()
//DEL {
//DEL 	/***
//DEL 	HTREEITEM hParent, hTarget;
//DEL 	
//DEL 	TRACE(_T("DropItem\n"));
//DEL 
//DEL 	// get the handle to the drop target.
//DEL 	//
//DEL 	hTarget = GetTreeCtrl().GetDropHilightItem();
//DEL 
//DEL 	// get the parent of the drop target.
//DEL 	//
//DEL 	hParent = GetTreeCtrl().GetParentItem(hTarget);
//DEL 
//DEL 	// add the drag item under target
//DEL 	//
//DEL 	CXmlBaseElement* dragElement;
//DEL 	CXmlClassInfo* dragParent;
//DEL 	CXmlClassInfo* targetElement;
//DEL 	HTREEITEM hDragParent;
//DEL 	HTREEITEM hContainerNode = hTarget;
//DEL 
//DEL 	hDragParent = GetTreeCtrl().GetParentItem(hDragItem_);
//DEL 	dragElement = GetItem(hDragItem_);
//DEL 	dragParent = dynamic_cast<CXmlClassInfo*>(GetItem(hDragParent));
//DEL 	targetElement = dynamic_cast<CXmlClassInfo*>(GetItem(hTarget));
//DEL 	if (targetElement == NULL) { // if the user drops over a member element we take the parent class
//DEL 		targetElement = dynamic_cast<CXmlClassInfo*>(GetItem(hParent));
//DEL 		hContainerNode = hParent;
//DEL 	}
//DEL 
//DEL 	// TODO: manage failure when name is duplicated
//DEL 	//
//DEL 
//DEL 	if (targetElement->GetId() == dragParent->GetId())
//DEL 		return;
//DEL 
//DEL 	// add member
//DEL 	//
//DEL 	CXmlBaseElement* newElement;
//DEL 	HTREEITEM newItem;
//DEL 
//DEL 	newElement = targetElement->AddMember(*dragElement);
//DEL 	newItem = CreateNode(newElement, hContainerNode);
//DEL 	GetTreeCtrl().SortChildren(hContainerNode);
//DEL 	if (newElement->GetDataType() == enDataType_Class) 
//DEL 		GetTreeCtrl().SetItemImage(newItem, ICON_INDEX_CLASS, ICON_INDEX_CLASS);
//DEL 	else
//DEL 		GetTreeCtrl().SetItemImage(newItem, ICON_INDEX_MEMBER, ICON_INDEX_MEMBER);
//DEL 	
//DEL 	RemoveItem(hDragItem_);
//DEL 	hDragItem_ = NULL;
//DEL 
//DEL 	// select new node
//DEL 	//
//DEL 	GetTreeCtrl().SelectItem(newItem);
//DEL 
//DEL 	RedrawWindow();
//DEL 
//DEL   ***/
//DEL }

//DEL void CProjectTreeView::EndDrag()
//DEL {
//DEL 	TRACE(_T("EndDrag\n"));
//DEL 	// inform the image list that dragging has stopped.
//DEL 	//
//DEL 	dragImageList_->EndDrag();
//DEL 	delete dragImageList_;
//DEL 	dragImageList_ = NULL;
//DEL 
//DEL 	// release the mouse capture.
//DEL 	//
//DEL 	ReleaseCapture();
//DEL 
//DEL 	isDragging_ = false; 	
//DEL }

void CProjectTreeView::RemoveItem(HTREEITEM item)
{
	HTREEITEM parentNode;
	HTREEITEM selNode;
	CXmlBaseElement* selElement;
	CXmlClassInfo* parentClass;

	selElement = GetItem(item);
	if (selElement == NULL)
		return;

	selNode = GetTreeItemById(selElement->GetId());
	parentNode = GetTreeCtrl().GetParentItem(selNode);
	
	// remove item from internal map
	//
	treeItems_.erase(item);

	// delete item from treeCtrl
	//
	GetTreeCtrl().DeleteItem(selNode);
	
	parentClass = dynamic_cast<CXmlClassInfo*>(GetDocument()->GetElementById(treeItems_[parentNode]));
	parentClass->RemoveMemberById(selElement->GetId());
}

bool CProjectTreeView::IsChildOf(HTREEITEM hItem1, HTREEITEM hItem2)
{
	HTREEITEM hParent = hItem1;
	while ((hParent = GetTreeCtrl().GetParentItem (hParent)) != NULL) {
		if (hParent == hItem2)
			return true;
	}
	return false;
}

HTREEITEM CProjectTreeView::MoveTree(HTREEITEM hDest, HTREEITEM hSrc)
{
	HTREEITEM newItem;

	newItem = CopyTree (hDest, hSrc);
	GetTreeCtrl().DeleteItem(hSrc);

	return newItem;
}

HTREEITEM CProjectTreeView::CopyTree(HTREEITEM hDest, HTREEITEM hSrc)
{
	//
	// Get the attributes of item to be copied.
	//
	int nImage, nSelectedImage;
	GetTreeCtrl().GetItemImage (hSrc, nImage, nSelectedImage);
	CString string = GetTreeCtrl().GetItemText (hSrc);

	//
	// Create an exact copy of the item at the destination.
	//
	HTREEITEM hNewItem = GetTreeCtrl().InsertItem (string, nImage, nSelectedImage, hDest);

	//
	// If the item has subitems, copy them, too.
	//
	if (GetTreeCtrl().ItemHasChildren (hSrc))
		CopyChildren (hNewItem, hSrc);

	//
	// Select the newly added item.
	//
	GetTreeCtrl().SelectItem(hNewItem);

	return hNewItem;
}

void CProjectTreeView::CopyChildren(HTREEITEM hDest, HTREEITEM hSrc)
{
	//
	// Get the first subitem.
	//
	HTREEITEM hItem = GetTreeCtrl().GetChildItem (hSrc);
	ASSERT (hItem != NULL);

	//
	// Create a copy of it at the destination.
	//
	int nImage, nSelectedImage;
	GetTreeCtrl().GetItemImage (hItem, nImage, nSelectedImage);
	CString string = GetTreeCtrl().GetItemText (hItem);
	HTREEITEM hNewItem = GetTreeCtrl().InsertItem (string, nImage, nSelectedImage, hDest);

	//
	// If the subitem has subitems, copy them, too.
	//
	if (GetTreeCtrl().ItemHasChildren (hItem))
		CopyChildren (hNewItem, hItem);

	//
	// Do the same for other subitems of hSrc.
	//
	while ((hItem = GetTreeCtrl().GetNextSiblingItem (hItem)) != NULL) {
		GetTreeCtrl().GetItemImage (hItem, nImage, nSelectedImage);
		string = GetTreeCtrl().GetItemText (hItem);
		hNewItem = GetTreeCtrl().InsertItem (string, nImage, nSelectedImage, hDest);
		if (GetTreeCtrl().ItemHasChildren (hItem))
			CopyChildren (hNewItem, hItem);
	}
}

BOOL CProjectTreeView::IsItemExpanded(HTREEITEM hItem)
{
	return GetTreeCtrl().GetItemState (hItem, TVIS_EXPANDED) & TVIS_EXPANDED;
}

HTREEITEM CProjectTreeView::HighlightDropTarget(CPoint point)
{
	//
	// Find out which item (if any) the cursor is over.
	//
	UINT nFlags;
	HTREEITEM hItem = GetTreeCtrl().HitTest (point, &nFlags);

	//
	// Highlight the item, or unhighlight all items if the cursor isn't
	// over an item.
	//
	dragImageList_->DragShowNolock (FALSE);
	GetTreeCtrl().SelectDropTarget(hItem);
	dragImageList_->DragShowNolock (TRUE);

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

void CProjectTreeView::OnTimer(UINT nIDEvent) 
{
	CTreeView::OnTimer(nIDEvent);

	//
	// Reset the timer.
	//
	SetTimer (1, scrollInterval_, NULL);

	//
	// Get the current cursor position and window height.
	//
	DWORD dwPos = ::GetMessagePos ();
	CPoint point (LOWORD (dwPos), HIWORD (dwPos));
	ScreenToClient (&point);

	CRect rect;
	GetClientRect (rect);
	int cy = rect.Height ();

	//
	// Scroll the window if the cursor is near the top or bottom.
	//
	if (point.y >= 0 && point.y <= scrollMargin_) {
		HTREEITEM hFirstVisible = GetTreeCtrl().GetFirstVisibleItem ();
		dragImageList_->DragShowNolock (FALSE);
		SendMessage (WM_VSCROLL, MAKEWPARAM (SB_LINEUP, 0), NULL);
		dragImageList_->DragShowNolock (TRUE);

		//
		// Kill the timer if the window did not scroll, or redraw the
		// drop target highlight if the window did scroll.
		//
		if (GetTreeCtrl().GetFirstVisibleItem () == hFirstVisible)
			KillTimer (1);
		else {
			HighlightDropTarget (point);
			return;
		}
	}
	else if (point.y >= cy - scrollMargin_ && point.y <= cy) {
		HTREEITEM hFirstVisible = GetTreeCtrl().GetFirstVisibleItem ();
		dragImageList_->DragShowNolock (FALSE);
		SendMessage(WM_VSCROLL, MAKEWPARAM (SB_LINEDOWN, 0), NULL);
		dragImageList_->DragShowNolock (TRUE);

		//
		// Kill the timer if the window did not scroll, or redraw the
		// drop target highlight if the window did scroll.
		//
		if (GetTreeCtrl().GetFirstVisibleItem () == hFirstVisible)
			KillTimer (1);
		else {
			HighlightDropTarget (point);
			return;
		}
	}

	//
	// If the cursor is hovering over a collapsed item, expand the tree.
	//
	UINT nFlags;
	HTREEITEM hItem = GetTreeCtrl().HitTest (point, &nFlags);

	if (hItem != NULL && GetTreeCtrl().ItemHasChildren (hItem) && !IsItemExpanded (hItem) && isDragging_) {
		dragImageList_->DragShowNolock (FALSE);
		GetTreeCtrl().Expand (hItem, TVE_EXPAND);
		dragImageList_->DragShowNolock (TRUE);
		KillTimer (1);
		return;
	}
}
