Click here to Skip to main content
15,896,063 members
Articles / Desktop Programming / ATL

Visual Studio Favorites

Rate me:
Please Sign up or sign in to vote.
2.00/5 (3 votes)
16 Mar 20033 min read 52.8K   710   14  
Add shortcuts, favorites, and more to Visual Studio.
//ConfigTree.cpp : Implementation of CConfigTree

#include "stdafx.h"
#include "ConfigTree.h"
#include "resource.h"
#include <algorithm>

#ifndef GET_X_LPARAM
#define GET_X_LPARAM(lp)                        ((int)(short)LOWORD(lp))
#define GET_Y_LPARAM(lp)                        ((int)(short)HIWORD(lp))
#endif

/////////////////////////////////////////////////////////////////////////////
// CConfigTree

CConfigTree::CConfigTree() : m_hDragImageList(NULL),m_hImageList(NULL),m_imageListHeight(0),m_imageListWidth(0),
				m_hLoadingOnly_ParentItem(NULL),m_hLoadingOnly_LastFolder(NULL),m_hLastAddedItem(NULL),
				m_fIsDragging(false),m_dragTargetItem(NULL),m_dragSourceItem(NULL),m_fInsertBeforeTarget(true),m_fNoDropTarget(true),
				m_nScrollMarginTop(13),m_nScrollMarginBottom(0),m_nScrollDelayInterval(170),m_nScrollTimerInterval(100),m_nScrollTimerNum(0)
{
	SetRectEmpty(&m_dragTargetLine);
	SetRectEmpty(&m_prevDragTargetLine);
	HBITMAP tempbmp=(HBITMAP)LoadImage(_Module.m_hInstResource,
													MAKEINTRESOURCE(IDB_MENUICONS) ,
													IMAGE_BITMAP,
													0, 0,
													LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
	BITMAP	bm={0};
	GetObject(tempbmp,sizeof(bm), &bm);

	m_imageListWidth = bm.bmWidth/TOTAL_NUM_ICONS;
	m_imageListHeight= bm.bmHeight;

	// Create the full-color image list
	// cx, cy = your icon width & height
	m_hImageList = ImageList_Create(m_imageListWidth, 
												m_imageListHeight,
												ILC_MASK | ILC_COLOR32,
												TOTAL_NUM_ICONS,
												0);

	ImageList_AddMasked(m_hImageList, tempbmp, 0xFF00FF);

	DeleteObject(tempbmp);

	// Set up the correct sizes for spacing and whatnot so we don't have to do it over
	// all the bloody time.
	m_menuIconSize.cx = std::_MAX(m_imageListWidth , ::GetSystemMetrics(SM_CXMENUCHECK));
	m_menuIconSize.cy = std::_MAX(m_imageListHeight, ::GetSystemMetrics(SM_CYMENUCHECK));
}

CConfigTree::~CConfigTree()
{
	ImageList_Destroy(m_hImageList);
}	//lint !e1740
	//-- complaining about the handles not getting whacked

// From CConfigurationFile
void CConfigTree::Load()
{
	SendMessage(WM_SETREDRAW,FALSE);
	SendMessage(TVM_SETIMAGELIST,TVSIL_NORMAL,(LPARAM)m_hImageList);
	CConfigurationFile::Load();
	SendMessage(WM_SETREDRAW,TRUE);
	const HTREEITEM root = GetRootItem();
	SetCurrentSelectedItem(root);
	Invalidate();
}

// From CConfigurationFile
bool CConfigTree::AddItem(IXMLDOMElement *pElm,const ITEM_TYPES itemType)
{
	if (itemType!=IT_CONFIG)
	{
		char buffer[MAX_PATH];
		if (pElm!=NULL)
			pElm->AddRef();	// we need a copy of this left around.

		TVINSERTSTRUCT	tv={0};
		tv.hParent = m_hLoadingOnly_ParentItem;
		tv.hInsertAfter = TVI_LAST;
		tv.item.mask = TVIF_PARAM|TVIF_TEXT|TVIF_CHILDREN;
		tv.item.pszText = GetElmText(pElm,buffer);
		tv.item.lParam = (LPARAM)pElm;
		tv.item.cChildren = I_CHILDRENCALLBACK;
		if (itemType>=0 && itemType<TOTAL_NUM_ICONS)
		{
			tv.item.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
			tv.item.iImage = tv.item.iSelectedImage = (int)itemType;
		}
		m_hLastAddedItem = (HTREEITEM)SendMessage(TVM_INSERTITEM,0,(LPARAM)&tv);
		if (itemType==IT_FOLDER)
			m_hLoadingOnly_LastFolder = m_hLoadingOnly_ParentItem = m_hLastAddedItem;	// next added items will go into here;
												// this is popped off by RecursiveLoad, m_hLoadingOnly_LastFolder by PostAddFolderItem
	}
	return true;
}

bool CConfigTree::PostAddFolderItem(IXMLDOMElement * /*pElm*/,const bool /*fHadChildren*/)
{
	TVINSERTSTRUCT	tv={0};
	tv.hParent = m_hLoadingOnly_LastFolder;
	tv.hInsertAfter = TVI_LAST;
	tv.item.mask = TVIF_PARAM|TVIF_TEXT;
	tv.item.pszText = "<< end of folder >>";
	tv.item.lParam = (LPARAM)0;

	tv.item.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
	tv.item.iImage = tv.item.iSelectedImage = (int)IT_HELPERENDOFFOLDER;

	SendMessage(TVM_INSERTITEM,0,(LPARAM)&tv);
	m_hLoadingOnly_LastFolder = GetParentOfItem(m_hLoadingOnly_LastFolder);

	return true;
}

void CConfigTree::UpdateItem(const HTREEITEM item,IXMLDOMElement *pElm,const ITEM_TYPES itemType)
{
	char buffer[MAX_PATH];

	TVITEM tv={0};
	tv.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
	tv.iImage = tv.iSelectedImage = (int)itemType;
	tv.pszText = GetElmText(pElm,buffer);
	tv.lParam = (LPARAM)pElm;
	tv.hItem = item;

	SendMessage(TVM_SETITEM,0,(LPARAM)&tv);

	RECT rect;
	*(HTREEITEM*)&rect = item;
	SendMessage(TVM_GETITEMRECT,FALSE,(LPARAM)&rect);
	InvalidateRect(&rect,TRUE);
}

void CConfigTree::RecursiveLoad(IXMLDOMNode *pChildNode)
{
	CConfigurationFile::RecursiveLoad(pChildNode);
	SendMessage(TVM_EXPAND,TVE_EXPAND,(LPARAM)m_hLoadingOnly_ParentItem);
	m_hLoadingOnly_ParentItem = GetParentOfItem(m_hLoadingOnly_ParentItem);
}

IXMLDOMElement * CConfigTree::GetItemXmlPtr(const HTREEITEM item)
{
	if (item!=NULL)
	{
		TVITEM tv={0};
		tv.mask = TVIF_PARAM|TVIF_HANDLE;
		tv.hItem = item;
		SendMessage(TVM_GETITEM,0,(LPARAM)&tv);
		return (IXMLDOMElement *)tv.lParam;
	}
	else
		return NULL;
}

void CConfigTree::BeginDrag(NMTREEVIEW *pTV)
{
	RECT rect;
	GetWindowRect(&rect);
	m_nScrollMarginBottom = (rect.bottom - rect.top) - m_nScrollMarginTop;

	m_dragSourceItem = pTV->itemNew.hItem;
	SetCurrentSelectedItem(m_dragSourceItem);

	// Check if the item being dragged is a visual hint and not a real object
	// (ie: the end of folder marker). If so, then we don't drag squat.
	if (GetItemXmlPtr(pTV->itemNew.hItem)==NULL)
	{
		m_dragSourceItem=NULL;
		return;
	}

	Invalidate(FALSE);
	UpdateWindow();
	m_hDragImageList = (HIMAGELIST)SendMessage(TVM_CREATEDRAGIMAGE,0,(LPARAM)m_dragSourceItem);

//	ShowCursor(FALSE);
	SetCapture();

	m_fIsDragging = true;

   POINT client={0, 0};
   ClientToScreen (&client);
	const POINT offsetIntoImage={pTV->ptDrag.x + client.x - rect.left ,
										  pTV->ptDrag.y + client.y - rect.top};

	*(HTREEITEM*)&rect = m_dragSourceItem;
	SendMessage(TVM_GETITEMRECT,TRUE,(LPARAM)&rect);	// get just the text position
	rect.left-=m_menuIconSize.cx;	// correct for icon offset and spacing
   const POINT hotSpot={pTV->ptDrag.x - rect.left , pTV->ptDrag.y - rect.top};

	ImageList_BeginDrag(m_hDragImageList, 0, hotSpot.x, hotSpot.y);

	ImageList_DragEnter(m_hWnd, offsetIntoImage.x, offsetIntoImage.y);
}

LRESULT CConfigTree::OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
{
	if (m_fIsDragging)
	{
		KillScrollTimer();

      ImageList_DragShowNolock(TRUE);
      ImageList_DragMove(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));

		TVHITTESTINFO hitinfo={0};
		hitinfo.pt.x=GET_X_LPARAM(lParam);
		hitinfo.pt.y=GET_Y_LPARAM(lParam);
		SendMessage(TVM_HITTEST,0,(LPARAM)&hitinfo);

		if ((hitinfo.hItem!=NULL) && (hitinfo.hItem!=m_dragTargetItem))
		{
			ImageList_DragLeave(m_hWnd);

			HighlightDropTarget(hitinfo.hItem);

			ImageList_DragEnter(m_hWnd, hitinfo.pt.x, hitinfo.pt.y); // re-show the cursor
		}

		// Now check if the cursor is over the top/bottom edge or a collapsed item
		if (hitinfo.pt.y <= m_nScrollMarginTop ||
          hitinfo.pt.y >= m_nScrollMarginBottom ||
          CanItemBeExpanded(hitinfo.hItem)
			 )
		{
			m_nScrollTimerNum = 7;
			SetTimer (m_nScrollTimerNum, m_nScrollDelayInterval);
		}
		return 0;
	}
	else
	{
		bHandled=FALSE;
		return 1;
	}
}

void CConfigTree::HighlightDropTarget(const HTREEITEM hitItem)
{
	EraseDropInsertTargetBar();

	// Don't re-hit test again, you'll screw up the paint.
	if (hitItem==m_dragSourceItem)
	{
		m_dragTargetItem=NULL;
		SetRectEmpty(&m_dragTargetLine);
		m_fNoDropTarget=true;
	}
	else
	{
		m_dragTargetItem=hitItem;

		m_fNoDropTarget=false;

		RECT srcRect;
		*(HTREEITEM*)&srcRect = m_dragSourceItem;
		SendMessage(TVM_GETITEMRECT,FALSE,(LPARAM)&srcRect);

		*(HTREEITEM*)&m_dragTargetLine = m_dragTargetItem;
		SendMessage(TVM_GETITEMRECT,FALSE,(LPARAM)&m_dragTargetLine);
		if (m_dragTargetLine.top < srcRect.top)
		{	// the line goes to the top.
			m_dragTargetLine.bottom=m_dragTargetLine.top+1;
			m_fInsertBeforeTarget=true;
		}
		else
		{	// line goes to the bottom
			m_dragTargetLine.top=m_dragTargetLine.bottom-1;
			m_fInsertBeforeTarget=false;
		}
		m_dragTargetLine.left++;
		m_dragTargetLine.right-=2;
		DrawDropInsertTargetBar();
	}
}

LRESULT CConfigTree::OnLButtonUP(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	if (m_fIsDragging)
	{
		KillScrollTimer();
		EraseDropInsertTargetBar();

		const HTREEITEM hSource=m_dragSourceItem;
				HTREEITEM hTarget=m_dragTargetItem;
				bool		fBeforeTarget=m_fInsertBeforeTarget;
		m_fIsDragging=false;
		m_dragSourceItem=NULL;
		m_dragTargetItem=NULL;
		m_fInsertBeforeTarget=false;
		ImageList_DragLeave(m_hWnd);
      ImageList_EndDrag();
      ReleaseCapture();
		if (m_fNoDropTarget)
		{
			SetCurrentSelectedItem(hSource);
		}
		else
		{
			// Check if the target item is a filler item; if so, then we need to move up or down
			// to the next one.
			if (GetItemXmlPtr(hTarget)==NULL)
			{
				if (fBeforeTarget)
				{
					// this is probably ok?
				}
				else
				{ // after it, so we get the next one and flip the sign
					HTREEITEM possibleTarget = GetNextVisualAfterItem(hTarget); 
					if (possibleTarget==NULL)
					{	// this probably means we shot past the end of the silly thing...
						// we need to check if this is the end of folder item, or not.
						possibleTarget = GetParentOfItem(hTarget);
						if (possibleTarget!=NULL)
							hTarget=possibleTarget;
					}
					else
					{
						hTarget=possibleTarget;	// this will insert before the next visual item
						fBeforeTarget=!fBeforeTarget;
					}
				}
			}
			else
			{	// check if the target is to be inserted after the item, and if the target
				// item has child nodes and if it's expanded.
				if (!fBeforeTarget)
				{
					if (IsItemExpanded(hTarget) && ItemHasChildren(hTarget))
					{
						hTarget=GetFirstChildItem(hTarget);
						fBeforeTarget=true;
					}
				}
			}

			const bool currentlyExpanded = IsItemExpanded(hSource);
			TVINSERTSTRUCT tvi={0};
			char textBuffer[256]="";

			tvi.item.mask = TVIF_HANDLE|TVIF_IMAGE|TVIF_PARAM|TVIF_SELECTEDIMAGE|TVIF_TEXT;
			tvi.item.hItem = hSource;
			tvi.item.pszText = textBuffer;
			tvi.item.cchTextMax = sizeof(textBuffer)-1;

			SendMessage(TVM_GETITEM,0,(LPARAM)&tvi.item);
			tvi.item.mask &= ~TVIF_HANDLE;
			tvi.item.mask |= TVIF_CHILDREN;
			tvi.item.hItem = NULL;
			tvi.item.cChildren = I_CHILDRENCALLBACK;

			tvi.hParent = GetParentOfItem(hTarget);
			tvi.hInsertAfter = hTarget;
			if (fBeforeTarget)
				tvi.hInsertAfter = GetPreviousSiblingOfItem(hTarget);
			if (tvi.hInsertAfter==NULL)
				tvi.hInsertAfter = TVI_FIRST;

			HTREEITEM hNewItem = (HTREEITEM)SendMessage(TVM_INSERTITEM,0,(LPARAM)&tvi);

			if (hNewItem!=NULL)
			{
				IXMLDOMElement *pElm = (IXMLDOMElement *)tvi.item.lParam;	// xml node to move
				if (pElm!=NULL)
					pElm->AddRef();	// so that delete doesn't screw it up
				SendMessage(TVM_DELETEITEM,0,(LPARAM)hSource);

				// tree nodes are moved and will show up correctly, but the xml nodes need to be moved, too
				HTREEITEM newParent = GetParentOfItem(hNewItem);
				HTREEITEM newNextSib = GetNextSiblingOfItem(hNewItem);

				IXMLDOMElement *pNextSib=GetItemXmlPtr(newNextSib);

				IXMLDOMNode *pParentElm = NULL;
				if (newParent!=NULL)
				{
					pParentElm = GetItemXmlPtr(newParent);
					pParentElm->AddRef();	// get_parentNode probably addref's so we should too.
				}
				else
				{
					if (pNextSib!=NULL)
						pNextSib->get_parentNode(&pParentElm);
				}

				// If we are moving to the end of a list in the root, then the parent node will be blank, so we
				// need to go to it.
				if (pParentElm==NULL)
				{	// append to the MenuItems in the doc
					m_pXmlDoc->selectSingleNode(_bstr_t("MenuItems"),&pParentElm);	// this will addref, so the release at the end fixes it
				}

				// if we are moving to the end of a list at any level, pNextSib will be null, which makes insertBefore
				// act just like appendChild
				CComVariant varNextSib(pNextSib);
				if (pNextSib==NULL)
					varNextSib.Clear();
				pParentElm->insertBefore(pElm,varNextSib,NULL);		// this also moves the pElm within the tree

				TVITEM item={0};
				item.mask = TVIF_HANDLE|TVIF_PARAM;
				item.hItem = hNewItem;
				item.lParam = (LPARAM)pElm;
				SendMessage(TVM_SETITEM,0,(LPARAM)&item);

				pParentElm->Release();	// assume we have to do this since get_parentNode will do an addref

				SetCurrentSelectedItem(hNewItem);

				m_hLoadingOnly_ParentItem = hNewItem;
				m_hLoadingOnly_LastFolder=hNewItem;
			   m_hLastAddedItem=hNewItem;
				ChildrenLoad(pElm);

				SendMessage(TVM_EXPAND,currentlyExpanded ? TVE_EXPAND : TVE_COLLAPSE ,(LPARAM)hNewItem);
			}
		}

		Invalidate(TRUE);
		UpdateWindow();
//	ShowCursor(TRUE);
		return 0;
	}
	else
	{
		bHandled=FALSE;
		return 1;
	}
}

///
LRESULT CConfigTree::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	if (wParam==m_nScrollTimerNum)
	{
		bHandled=TRUE;
		KillScrollTimer ();
		bool setScrollTimer=false;
		// Get the current cursor position and window height.
		DWORD dwPos = ::GetMessagePos ();
		POINT point={ GET_X_LPARAM  (dwPos),  GET_Y_LPARAM  (dwPos)};
		ScreenToClient (&point);
		// Scroll the window if the cursor is near the top or bottom.
		if (point.y <= m_nScrollMarginTop)
		{
			const HTREEITEM hFirstVisible = GetFirstVisibleItem();
			const HTREEITEM hTopRoot = GetRootItem();
			if (hFirstVisible!=hTopRoot)
			{
				ImageList_DragShowNolock (FALSE);

				EraseDropInsertTargetBar();

				SendMessage (WM_VSCROLL, MAKEWPARAM (SB_LINEUP, 0), NULL);

				ImageList_DragMove(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));

				if (GetFirstVisibleItem () != hFirstVisible)
					setScrollTimer=true;

				TVHITTESTINFO hitinfo={0};
				hitinfo.pt = point;
				SendMessage(TVM_HITTEST,0,(LPARAM)&hitinfo);

				ImageList_DragLeave(m_hWnd);

				HighlightDropTarget(hitinfo.hItem);

				ImageList_DragEnter(m_hWnd, hitinfo.pt.x, hitinfo.pt.y); // re-show the cursor
			}
		}
		else if (point.y >= m_nScrollMarginBottom)
		{
			const HTREEITEM hFirstVisible = GetFirstVisibleItem ();

			ImageList_DragShowNolock (FALSE);

			EraseDropInsertTargetBar();

         SendMessage (WM_VSCROLL, MAKEWPARAM (SB_LINEDOWN, 0), NULL);

			ImageList_DragMove(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));

			if (GetFirstVisibleItem () != hFirstVisible)
				setScrollTimer=true;

			TVHITTESTINFO hitinfo={0};
			hitinfo.pt = point;
			SendMessage(TVM_HITTEST,0,(LPARAM)&hitinfo);

			ImageList_DragLeave(m_hWnd);

			HighlightDropTarget(hitinfo.hItem);

			ImageList_DragEnter(m_hWnd, hitinfo.pt.x, hitinfo.pt.y); // re-show the cursor
		}

		if (!setScrollTimer)	// scrolling take priority
		{
			TVHITTESTINFO hitinfo={0};
			hitinfo.pt = point;
			SendMessage(TVM_HITTEST,0,(LPARAM)&hitinfo);
			if (CanItemBeExpanded(hitinfo.hItem))
			{
				ImageList_DragShowNolock (FALSE);

// This is not needed when expanding for some reason... causes visual glitch
//				EraseDropInsertTargetBar();

				SendMessage(TVM_EXPAND,TVE_EXPAND,(LPARAM)hitinfo.hItem);

				ImageList_DragMove(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));

//				TVHITTESTINFO hitinfo={0};
				hitinfo.pt = point;
				SendMessage(TVM_HITTEST,0,(LPARAM)&hitinfo);

				ImageList_DragLeave(m_hWnd);

				HighlightDropTarget(hitinfo.hItem);

				ImageList_DragEnter(m_hWnd, hitinfo.pt.x, hitinfo.pt.y); // re-show the cursor

			}			
		}
         

		if (setScrollTimer)
			SetTimer(m_nScrollTimerNum,m_nScrollTimerInterval);
	}
	return 0;
}

// Draw a line just like what IE5 does when you rearrange the favs list.
void CConfigTree::DrawDropInsertTargetBar()
{
	if (!IsRectEmpty(&m_dragTargetLine))
	{
		m_prevDragTargetLine = m_dragTargetLine;
		HDC hdc = GetDC();
	   const int oldrop=SetROP2(hdc,R2_NOT);
	   MoveToEx(hdc,m_dragTargetLine.left   ,m_dragTargetLine.top-2,NULL);
	   LineTo(  hdc,m_dragTargetLine.left   ,m_dragTargetLine.bottom+3);
	   MoveToEx(hdc,m_dragTargetLine.left+1 ,m_dragTargetLine.top-1,NULL);
	   LineTo(  hdc,m_dragTargetLine.left+1 ,m_dragTargetLine.bottom+2);
	   MoveToEx(hdc,m_dragTargetLine.left+2 ,m_dragTargetLine.top,NULL);
	   LineTo(  hdc,m_dragTargetLine.right-1/*2*/,m_dragTargetLine.top);
	   MoveToEx(hdc,m_dragTargetLine.left+2 ,m_dragTargetLine.bottom,NULL);
	   LineTo(  hdc,m_dragTargetLine.right-1/*2*/,m_dragTargetLine.bottom);
	   MoveToEx(hdc,m_dragTargetLine.right-1,m_dragTargetLine.top-1,NULL);
	   LineTo(  hdc,m_dragTargetLine.right-1,m_dragTargetLine.bottom+2);
	   MoveToEx(hdc,m_dragTargetLine.right   ,m_dragTargetLine.top-2,NULL);
	   LineTo(  hdc,m_dragTargetLine.right   ,m_dragTargetLine.bottom+3);
	   SetROP2(hdc,oldrop);     //lint !e534
	   UpdateWindow(); // make sure it's painted. 
		ReleaseDC(hdc);
	}
}

void CConfigTree::EraseDropInsertTargetBar()
{
	if (!IsRectEmpty(&m_prevDragTargetLine))
	{
		RECT cur(m_dragTargetLine);
		m_dragTargetLine = m_prevDragTargetLine;
		DrawDropInsertTargetBar();
		m_dragTargetLine = cur;
		SetRectEmpty(&m_prevDragTargetLine);
	}
}


void CConfigTree::DeleteCurrentSelection()
{
	HTREEITEM sel =  GetCurrentSelectedItem();
	if (sel!=NULL)
	{
		IXMLDOMElement *pElm=GetItemXmlPtr(sel);
		if (pElm!=NULL)
		{
			CComPtr<IXMLDOMNode> pParentElm;
			pElm->get_parentNode(&pParentElm);
			if (pParentElm!=NULL)
				pParentElm->removeChild(pElm,NULL);
		}
		SendMessage(TVM_DELETEITEM,0,(LPARAM)sel);
	}
}


void CConfigTree::AddEmptyItem(const bool autoSelect,const ITEM_TYPES newType /*= IT_FILE*/)
{
	m_hLoadingOnly_ParentItem=NULL;	// will make it at the root
	CComPtr<IXMLDOMNode>		pMenuItemsNode;
	if (S_OK == m_pXmlDoc->selectSingleNode(_bstr_t("MenuItems"),&pMenuItemsNode))
	{
		CComPtr<IXMLDOMElement>		pNewElm;
		m_pXmlDoc->createElement(_bstr_t("Item"),&pNewElm);
		pNewElm->setAttribute(_bstr_t("type"),CComVariant(GetTypeText(newType,false)));

		pMenuItemsNode->appendChild(pNewElm,NULL);
		AddItem(pNewElm,newType);
		if (autoSelect)
			SetCurrentSelectedItem(m_hLastAddedItem);
	}
}

void CConfigTree::DuplicateCurrentSelection(const bool autoSelect)
{
	HTREEITEM sel =  GetCurrentSelectedItem();
	if (sel!=NULL)
	{
		IXMLDOMElement *pElm=GetItemXmlPtr(sel);
		if (pElm!=NULL)
		{
			CComPtr<IXMLDOMNode> pNextSib;
			pElm->get_nextSibling(&pNextSib);	// if no next sib, pNextsib will be null, which is ok

			CComPtr<IXMLDOMNode> pParentElm;
			pElm->get_parentNode(&pParentElm);	// this will always be valid

			CComPtr<IXMLDOMNode> pCloneNode;
			pElm->cloneNode(VARIANT_TRUE,&pCloneNode);	// hmm, where is this attached at? answer: nowhere

			CComVariant varNextSib(pNextSib);
			if (pNextSib==NULL)
				varNextSib.Clear();
			pParentElm->insertBefore(pCloneNode,varNextSib,NULL);

			TVINSERTSTRUCT tvi={0};
			char textBuffer[256]="";

			tvi.item.mask = TVIF_HANDLE|TVIF_IMAGE|TVIF_PARAM|TVIF_SELECTEDIMAGE|TVIF_TEXT;
			tvi.item.hItem = sel;
			tvi.item.pszText = textBuffer;
			tvi.item.cchTextMax = sizeof(textBuffer)-1;

			SendMessage(TVM_GETITEM,0,(LPARAM)&tvi.item);
			tvi.item.mask &= ~TVIF_HANDLE;
			tvi.item.hItem = NULL;
			tvi.item.mask |= TVIF_CHILDREN;
			tvi.item.cChildren = I_CHILDRENCALLBACK;

			// Need to get the correct object pointer to this and insert the sucker.
			IXMLDOMElement *pNewElm = NULL;
			pCloneNode->QueryInterface(&pNewElm);

			tvi.item.lParam = (LPARAM)pNewElm;

			tvi.hParent = GetParentOfItem(sel);
			tvi.hInsertAfter = sel;

			m_hLastAddedItem = (HTREEITEM)SendMessage(TVM_INSERTITEM,0,(LPARAM)&tvi);
			if (autoSelect)
				SetCurrentSelectedItem(m_hLastAddedItem);
		}
	}
}

// Take the current selection, make a folder at it's position, and move it into that new folder
void CConfigTree::InsertFolder(const bool autoSelect)
{
	HTREEITEM sel =  GetCurrentSelectedItem();
	if (sel!=NULL)
	{
moveit:
		IXMLDOMElement *pElm=GetItemXmlPtr(sel);
		if (pElm!=NULL)
		{
			ITEM_TYPES	newType = IT_FOLDER;
			IXMLDOMElement *pFolderElm=NULL;
			m_pXmlDoc->createElement(_bstr_t("Item"),&pFolderElm);
			pFolderElm->setAttribute(_bstr_t("type"),CComVariant(GetTypeText(newType,false)));

			CComPtr<IXMLDOMNode> pParentElm;
			pElm->get_parentNode(&pParentElm);	// this will always be valid

			pParentElm->insertBefore(pFolderElm,CComVariant(pElm),NULL);	// it's ok to be before this, since we're going to move the old node over
			pFolderElm->appendChild(pElm,NULL);	// and now this node is within this folder.

			// That was easy, now to make and move the tree node

			TVINSERTSTRUCT tvi={0};
			char textBuffer[256]="          ";

			tvi.item.mask = TVIF_IMAGE|TVIF_PARAM|TVIF_SELECTEDIMAGE|TVIF_TEXT|TVIF_CHILDREN;
			tvi.item.pszText = textBuffer;
			tvi.item.cchTextMax = sizeof(textBuffer)-1;
			tvi.item.cChildren = I_CHILDRENCALLBACK;

			tvi.item.iImage = tvi.item.iSelectedImage = (int)newType;

			tvi.item.lParam = (LPARAM)pFolderElm;
			pFolderElm->AddRef();

			tvi.hParent = GetParentOfItem(sel);
			tvi.hInsertAfter = sel;	// yeah, before with xml, after with the tree node. That's ok, we're going to move it.

			m_hLastAddedItem = (HTREEITEM)SendMessage(TVM_INSERTITEM,0,(LPARAM)&tvi);	// this will be the parent of the new item we're moving...

			tvi.item.mask = TVIF_HANDLE|TVIF_IMAGE|TVIF_PARAM|TVIF_SELECTEDIMAGE|TVIF_TEXT;
			tvi.item.hItem = sel;

			SendMessage(TVM_GETITEM,0,(LPARAM)&tvi.item);	// lparam will be pElm
			tvi.hParent = m_hLastAddedItem;
			tvi.hInsertAfter = NULL;
			HTREEITEM newSubNode  = (HTREEITEM)SendMessage(TVM_INSERTITEM,0,(LPARAM)&tvi);	// we now have two copies of this item in the tree...

			pElm->AddRef();
			SendMessage(TVM_DELETEITEM,0,(LPARAM)sel);	// need the addref so it doesn't get blasted

			if (autoSelect)
			{
				SendMessage(TVM_EXPAND,TVE_EXPAND,(LPARAM)m_hLastAddedItem);
				SendMessage(TVM_ENSUREVISIBLE,0,(LPARAM)newSubNode);
				SetCurrentSelectedItem(m_hLastAddedItem);
			}
		}
	}
	else	// nothing selected, add to the end of the tree.
	{
		AddEmptyItem(false);	// defaults to a file
		sel = m_hLastAddedItem;
		if (sel!=NULL)
			goto moveit;
	}
}

LPSTR CConfigTree::GetElmText(IXMLDOMElement *pElm, char *pBuffer)
{
	if (pBuffer!=NULL)
	{
		*pBuffer=0;
		if (pElm!=NULL)
		{
			if (IT_BREAK==GetItemType(pElm))
			{
				for (int i=0;i<100;++i)
					pBuffer[i]='-';
				pBuffer[i]=0;
			}
			else
			{
				CComVariant	textVar;
				pElm->getAttribute(_bstr_t("title"),&textVar);

				AtlW2AHelper(pBuffer,textVar);
				if (*pBuffer==0)
				{
					for (int i=0;i<10;++i)
						pBuffer[i]=' ';	// need something so it shows up!!!
					pBuffer[i]=0;
				}
			}
		}
	}
	return pBuffer;
}

void CConfigTree::GetDispInfo(TVITEM *pItem)
{
	if (pItem->mask & TVIF_CHILDREN)
	{
		pItem->cChildren = 0;
		IXMLDOMElement *pElm = GetItemXmlPtr(pItem->hItem);
		if (IT_FOLDER==GetItemType(pElm))
		{
			pItem->cChildren = 1;
		}
		else
		{
			VARIANT_BOOL vbool=FALSE;
			if (S_OK==pElm->hasChildNodes(&vbool))
			{
				if (vbool)
					pItem->cChildren = 1;
			}
		}		
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I work when you don't. Sleep is for wussies.

The answer is no, whatever the question is. You can't have it, you don't need it, and you'd just break it in five minutes if I give it to you anyways.

If you're interested, my web site is at NOPcode.com and has lots of cool stuff with programming and a one-of-a-kind Audio Server.

Comments and Discussions