//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;
}
}
}
}