/*******************************************************************

Copyright (c) 2004 Open Mobile Alliance, Ltd.  All Rights Reserved.

This software is made available under the usage and distribution terms 
covered by the Common Public License 1.0 and by using this software you
are bound by the terms and conditions of the same.

EXCEPT AS EXPRESSLY SET FORTH IN THE COMMON PUBLIC LICENSE,
THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
   
Each recipient is solely responsible for determining the 
appropriateness of using and distributing this program and assumes 
all risks associated with its exercise of rights under the Common 
Public License, including but not limited to the risks and 
costs of program errors, compliance with applicable laws, 
damage to or loss of data, programs or equipment, and 
unavailability or interruption of operations.

********************************************************************/
// TreePane.cpp : implementation file
//

#include "stdafx.h"
#include "scts3devman.h"
#include "TreePane.h"
#include "DataPane.h"

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

/////////////////////////////////////////////////////////////////////////////
// CTreePane dialog


CTreePane::CTreePane(SctsDMTree *_tree,CWnd* pParent /*=NULL*/)
	: CDialog(CTreePane::IDD, pParent),tree(_tree)
{
	//{{AFX_DATA_INIT(CTreePane)
	m_inherit = FALSE;
	//}}AFX_DATA_INIT
	newNode=false;
	update=false;
	dataPaneData=0;
	useDataPane=false;
}


void CTreePane::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CTreePane)
	DDX_Control(pDX, IDC_EDIT, m_edit);
	DDX_Control(pDX, IDC_TREENODES, m_tree);
	DDX_Control(pDX, IDC_TREEACL, m_acl);
	DDX_Control(pDX, IDC_TREEPROP, m_properties);
	DDX_Check(pDX, IDC_TREEACLINHERIT, m_inherit);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CTreePane, CDialog)
	//{{AFX_MSG_MAP(CTreePane)
	ON_NOTIFY(TVN_SELCHANGED, IDC_TREENODES, OnSelchangedTreenodes)
	ON_NOTIFY(NM_CLICK, IDC_TREEPROP, OnClickTreeprop)
	ON_EN_KILLFOCUS(IDC_EDIT, OnKillfocusEdit)
	ON_BN_CLICKED(IDC_TREEACLINHERIT, OnTreeaclinherit)
	ON_BN_CLICKED(IDC_TREEACLADD, OnTreeacladd)
	ON_NOTIFY(NM_CLICK, IDC_TREEACL, OnClickTreeacl)
	ON_NOTIFY(TVN_SELCHANGING, IDC_TREENODES, OnSelchangingTreenodes)
	ON_BN_CLICKED(IDC_TREEDISCARD, OnTreediscard)
	ON_BN_CLICKED(IDC_TREEAPPLY, OnTreeapply)
	ON_WM_SIZE()
	ON_COMMAND(ID_TREE_INSERTGET, OnTreeInsertget)
	ON_NOTIFY(NM_DBLCLK, IDC_TREEPROP, OnDblclkTreeprop)
	ON_COMMAND(ID_TREE_TESTNODE, OnTreeTestnode)
	ON_WM_SHOWWINDOW()
	ON_COMMAND(ID_TREE_ADDNODE, OnTreeapply)
	ON_COMMAND(ID_TREE_DELETENODE, OnTreediscard)
	ON_COMMAND(ID_TREE_INSERTGETSTRUCT, OnTreeInsertgetstruct)
	ON_COMMAND(ID_TREE_INSERTGETSTRUCTDATA, OnTreeInsertgetstructdata)
	ON_COMMAND(ID_TREE_INSERTGETTNDS, OnTreeInsertgettnds)
	ON_COMMAND(ID_TREE_INSERTEXEC, OnTreeInsertexec)
	ON_COMMAND(ID_TREE_INSERTEXECCORRELATOR, OnTreeInsertexeccorrelator)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTreePane message handlers
void CTreePane::UploadNodeACL(SctsDMNode *node)
{
	char c;
	char *startPos,*endPos;
	int item,subItem;

	while (node && !node->acl) node=node->parent;
	if (!node) return;
	startPos=node->acl;
	while (startPos[0])
	{
		if (!(startPos=strchr(startPos,'='))) break;
		/* Get the correct sub item */
		switch (*(startPos-1))
		{
		/* Ad'd' */
		case 'd':
			subItem=1;
			break;
		/* Delel'e' and Replac'e' */
		case 'e':
			if (*(startPos-2)=='t') 
				subItem=2;
			else
				subItem=5;
			break;
		/* Exe'c' */
		case 'c':
			subItem=3;
			break;
		/* Ge't' */
		case 't':
			subItem=4;
			break;
		default:
			SCTSASSERT(false);
		}
		do
		{
			startPos++;
			endPos=startPos+strcspn(startPos,"+&\0");
			c=*endPos;
			*endPos=0;
			/* Check if server identifier has already been added */
			item=m_acl.GetItemCount();
			while (item--)
			{
				if (m_acl.GetItemText(item,0)==startPos) break;
			}
			if (item==-1) item=m_acl.InsertItem(0,startPos);
			m_acl.SetItemText(item,subItem,"X");
			*endPos=c;
			startPos=endPos;
		}
		while (*startPos=='+');
	}
}

void CTreePane::UploadNode(SctsDMNode *node)
{
	bool aclEnable=true;
	bool addEnable;
	bool deleteEnable=true;
	int i;
	char buffer[20];
	for (i=0;i<8;++i) m_properties.SetItemText(i,1,"");
	m_acl.DeleteAllItems();
	if (!node) return;
	if (!node->name || !node->name[0])
	{
		return;
	}
	if (!node->parent && !node->next && !node->child)
	{
		return;
	}
	/* Upload node properties */
	if (node->name) m_properties.SetItemText(0,1,node->name);
	if (node->format) m_properties.SetItemText(1,1,SctsDMFormatString[node->format]);
	if (node->mimeType) m_properties.SetItemText(2,1,node->mimeType);
	if (node->title) m_properties.SetItemText(3,1,node->title);
	if (node->timeStamp) m_properties.SetItemText(4,1,node->timeStamp);
	if (node->version) m_properties.SetItemText(5,1,itoa(node->version,buffer,10));
	if (node->size) m_properties.SetItemText(6,1,itoa(node->size,buffer,10));
	useDataPane=false;
	if (node->data && node->format!=DMFORMAT_NULL)
	{
		if (node->format==DMFORMAT_BIN || node->size>100)
		{
			useDataPane=true;
			dataPaneDataSize=node->size;
			if (dataPaneData) sctsFree(dataPaneData);
			dataPaneData=(char *)sctsMalloc(node->size+1);
			if (dataPaneData)
			{
				sctsMemcpy(dataPaneData,node->data,node->size);
				dataPaneData[dataPaneDataSize]=0;
				m_properties.SetItemText(7,1,"Double-click to view.");
			}
			else
			{
				m_properties.SetItemText(7,1,"");
			}
		}
		else
		{
			m_properties.SetItemText(7,1,(char *)node->data);
		}
	}
	
	/* Upload node acl */
	m_inherit=(node->acl==0);
	UpdateData(FALSE);
	UploadNodeACL(node);

	/* Disable all windows */
	m_properties.EnableWindow(FALSE);
	m_acl.EnableWindow(FALSE);
	GetDlgItem(IDC_TREEACLINHERIT)->EnableWindow(FALSE);
	GetDlgItem(IDC_TREEACLADD)->ShowWindow(SW_HIDE);
	GetDlgItem(IDC_TREEACLREMOVE)->ShowWindow(SW_HIDE);

	/* Determine access rights */
	addEnable=(node->format==DMFORMAT_NODE);
	if (tree->IsServerTree())
	{
		/* Allow properties to be editable based on Replace ACL */
		if (node->VerifyACL(SctsDMDevice::GetSctsServerID(),SCTS_REPLACE)) m_properties.EnableWindow(TRUE);
		/* Allow ACL to be editable only if server has access */
		aclEnable=false;
		if (node->format==DMFORMAT_NODE) aclEnable=node->VerifyACL(SctsDMDevice::GetSctsServerID(),SCTS_REPLACE);
		if (!aclEnable && node->parent) aclEnable=node->parent->VerifyACL(SctsDMDevice::GetSctsServerID(),SCTS_REPLACE);
		/* Check if nodes can be Added/Deleted */
		if (addEnable) addEnable=node->VerifyACL(SctsDMDevice::GetSctsServerID(),SCTS_ADD);
		deleteEnable=node->VerifyACL(SctsDMDevice::GetSctsServerID(),SCTS_DELETE);
	}
	else
	{
		m_properties.EnableWindow(TRUE);
	}

	if (aclEnable && node->parent) GetDlgItem(IDC_TREEACLINHERIT)->EnableWindow(TRUE);
	if (aclEnable && node->acl) 
	{
		GetDlgItem(IDC_TREEACLADD)->ShowWindow(SW_SHOW);
		GetDlgItem(IDC_TREEACLREMOVE)->ShowWindow(SW_SHOW);
		m_acl.EnableWindow(TRUE);
	}
	if (!addEnable) GetDlgItem(IDC_TREEAPPLY)->EnableWindow(FALSE);
	if (node->permanent) GetDlgItem(IDC_TREEDISCARD)->EnableWindow(FALSE);
}

BOOL CTreePane::OnInitDialog() 
{
	LOGFONT logFont;
	RECT rect;
	CDialog::OnInitDialog();
	
	CFont *font=GetDlgItem(IDC_TREELBL1)->GetFont();
	CFont tempFont;
	font->GetLogFont(&logFont);
	logFont.lfWeight=FW_BOLD;
	tempFont.CreateFontIndirect(&logFont);
	GetDlgItem(IDC_TREEFRM)->SetFont(&tempFont);
	GetDlgItem(IDC_TREEACLFRM)->SetFont(&tempFont);
	GetDlgItem(IDC_TREEPROPFRM)->SetFont(&tempFont);
	
	/* Create properties list */
	m_properties.SetExtendedStyle(LVS_EX_FULLROWSELECT | m_properties.GetExtendedStyle());
	m_properties.GetClientRect(&rect);
	m_properties.InsertColumn(0,"Property",LVCFMT_LEFT,60);
	m_properties.InsertColumn(1,"Value",LVCFMT_LEFT,rect.right-rect.left-60);
	m_properties.InsertItem(0,"Name");
	m_properties.InsertItem(1,"Format");
	m_properties.InsertItem(2,"Type");
	m_properties.InsertItem(3,"Title");
	m_properties.InsertItem(4,"TStamp");
	m_properties.InsertItem(5,"VerNo");
	m_properties.InsertItem(6,"Size");
	m_properties.InsertItem(7,"Data");

	/* Create acl list */
	m_acl.SetExtendedStyle(LVS_EX_FULLROWSELECT | m_acl.GetExtendedStyle());
	m_acl.GetClientRect(&rect);
	m_acl.InsertColumn(0,"Server ID",LVCFMT_LEFT,rect.right-rect.left-100);
	m_acl.InsertColumn(1,"A",LVCFMT_LEFT,20);
	m_acl.InsertColumn(2,"D",LVCFMT_LEFT,20);
	m_acl.InsertColumn(3,"E",LVCFMT_LEFT,20);
	m_acl.InsertColumn(4,"G",LVCFMT_LEFT,20);
	m_acl.InsertColumn(5,"R",LVCFMT_LEFT,20);


	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}


void CTreePane::OnSelchangedTreenodes(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	SctsDMNode *node=(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem());
	ShowApplyDiscard(FALSE);
	UploadNode(node);
	newNode=false;
	*pResult = 0;
}

void CTreePane::OnClickTreeprop(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	if ((pNMListView->iItem!=0 || newNode) && pNMListView->iItem!=6 && pNMListView->iSubItem==1 && (pNMListView->iItem!=7 || !useDataPane))
	{
		CRect rect;
		m_properties.GetSubItemRect(pNMListView->iItem,1,LVIR_LABEL,rect);
		m_edit.SetWindowText(m_properties.GetItemText(pNMListView->iItem,1));
		m_edit.SetSel(0,-1);
		m_edit.ShowWindow(SW_SHOW);
		m_properties.ClientToScreen(&rect);
		ScreenToClient(&rect);
		m_edit.MoveWindow(&rect);
		m_edit.SetFocus();
		editedItem=pNMListView->iItem;
		ShowApplyDiscard(TRUE);
	}
	
	*pResult = 0;
}

void CTreePane::OnKillfocusEdit() 
{
	CString value;
	m_edit.GetWindowText(value);
	m_edit.ShowWindow(SW_HIDE);
	/* Check if the edit box is being used by the Add ACL button */
	if (editedItem==-1)
	{
		int i;
		/* Validate ACL */
		if (strchr((LPCTSTR)value,'+') || strchr((LPCTSTR)value,'&') || strchr((LPCTSTR)value,' ') || strchr((LPCTSTR)value,'='))
		{
			MessageBox("Invalid server ID.","Node ACL",MB_ICONERROR);
			m_acl.DeleteItem(0);
			m_acl.SetFocus();
			return;
		}
		i=m_acl.GetItemCount();
		while (--i)
		{
			if (value==m_acl.GetItemText(i,0))
			{
				MessageBox("Server ID already in ACL list.","Node ACL",MB_ICONERROR);
				m_acl.DeleteItem(0);
				m_acl.SetFocus();
				return;
			}
		}
		m_acl.SetItemText(0,0,value);
		return;
	}

	/* Validate format */
	if (editedItem==1)
	{
		SctsDMNodeFormat format=DMFORMAT_CHR;
		if (!value.IsEmpty())
		{
			while(format != DMFORMAT_NONE)
			{
				if (value==SctsDMFormatString[format]) break;
				format=(SctsDMNodeFormat)(format+1);
			}
		}
		if (format==DMFORMAT_NONE)
		{
			MessageBox("Invalid format value.","Node Properties",MB_ICONERROR);
			m_properties.SetFocus();
			return;
		}
	}
	if (editedItem==7)
	{
		char buffer[10];
		if (value.IsEmpty()) m_properties.SetItemText(1,1,"null");
		m_properties.SetItemText(6,1,itoa(value.GetLength(),buffer,10));
	}
	m_properties.SetItemText(editedItem,1,value);
}

BOOL CTreePane::PreTranslateMessage(MSG* pMsg) 
{
	/* Disable ESC  and Enter */
	if (pMsg->message==WM_KEYDOWN && (pMsg->wParam==VK_ESCAPE || pMsg->wParam==VK_RETURN)) return TRUE;
	if (pMsg->message==WM_RBUTTONDOWN && pMsg->hwnd==m_tree.m_hWnd && !update)
	{
		CMenu menu;
		VERIFY(menu.LoadMenu(IDR_MENUS));
		CMenu *treeMenu=menu.GetSubMenu(0);
		ASSERT(treeMenu != NULL);
		HTREEITEM item;
		POINT point={pMsg->lParam&0xFFFF, pMsg->lParam>>16};
		item=m_tree.HitTest(point);
		m_tree.ClientToScreen(&point);
		treeMenu->EnableMenuItem(ID_TREE_INSERTGET,(tree->IsServerTree() && !((SctsDMServerTree *)tree)->newDevice)?MF_ENABLED:MF_GRAYED);
		treeMenu->EnableMenuItem(ID_TREE_INSERTGETSTRUCT,(tree->IsServerTree() && !((SctsDMServerTree *)tree)->newDevice)?MF_ENABLED:MF_GRAYED);
		treeMenu->EnableMenuItem(ID_TREE_INSERTGETSTRUCTDATA,(tree->IsServerTree() && !((SctsDMServerTree *)tree)->newDevice)?MF_ENABLED:MF_GRAYED);
        treeMenu->EnableMenuItem(ID_TREE_INSERTGETTNDS,(tree->IsServerTree() && !((SctsDMServerTree *)tree)->newDevice)?MF_ENABLED:MF_GRAYED);
        treeMenu->EnableMenuItem(ID_TREE_INSERTEXEC,(tree->IsServerTree() && !((SctsDMServerTree *)tree)->newDevice)?MF_ENABLED:MF_GRAYED);
        treeMenu->EnableMenuItem(ID_TREE_INSERTEXECCORRELATOR,(tree->IsServerTree() && !((SctsDMServerTree *)tree)->newDevice)?MF_ENABLED:MF_GRAYED);
		treeMenu->EnableMenuItem(ID_TREE_ADDNODE,GetDlgItem(IDC_TREEAPPLY)->IsWindowEnabled()?MF_ENABLED:MF_GRAYED);
		treeMenu->EnableMenuItem(ID_TREE_DELETENODE,GetDlgItem(IDC_TREEDISCARD)->IsWindowEnabled()?MF_ENABLED:MF_GRAYED);
		treeMenu->EnableMenuItem(ID_TREE_TESTNODE,(tree->IsServerTree() && item && ((SctsDMNode *)m_tree.GetItemData(item))->format==DMFORMAT_NODE)?MF_ENABLED:MF_GRAYED);
		if (item)
		{
			m_tree.SelectItem(item);
			treeMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,point.x,point.y , this);
		}
	}
	return CDialog::PreTranslateMessage(pMsg);
}

void CTreePane::OnTreeaclinherit() 
{
	UpdateData(TRUE);
	m_acl.DeleteAllItems();
	if (m_inherit) 
	{
		SctsDMNode *node=(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem());
		if (!newNode) node=node->parent;
		UploadNodeACL(node);
	}
	m_acl.EnableWindow(m_inherit==FALSE);
	GetDlgItem(IDC_TREEACLADD)->ShowWindow(m_inherit?SW_HIDE:SW_SHOW);
	GetDlgItem(IDC_TREEACLREMOVE)->ShowWindow(m_inherit?SW_HIDE:SW_SHOW);
	ShowApplyDiscard(TRUE);
}

void CTreePane::OnTreeacladd() 
{
	CRect rect;
	m_acl.InsertItem(0,"");
	m_acl.GetSubItemRect(0,0,LVIR_LABEL,rect);
	m_edit.SetWindowText("");
	m_edit.SetSel(0,-1);
	m_edit.ShowWindow(SW_SHOW);
	m_acl.ClientToScreen(&rect);
	ScreenToClient(&rect);
	m_edit.MoveWindow(&rect);
	m_edit.SetFocus();
	editedItem=-1;
	ShowApplyDiscard(TRUE);
}

void CTreePane::OnClickTreeacl(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	char set[2]="X";
	int item,subItem;
	item=pNMListView->iItem;
	subItem=pNMListView->iSubItem;
	if (!subItem) return;
	if (!m_acl.GetItemText(item,subItem).IsEmpty()) set[0]=0;
	m_acl.SetItemText(item,subItem,set);
	ShowApplyDiscard(TRUE);
	*pResult = 0;
}

void CTreePane::ShowApplyDiscard(BOOL show)
{
	GetDlgItem(IDC_TREEAPPLY)->EnableWindow(TRUE);
	GetDlgItem(IDC_TREEDISCARD)->EnableWindow(TRUE);
	if (update)
	{
		if (show) return;
		GetDlgItem(IDC_TREEAPPLY)->SetWindowText("&Add Node");
		GetDlgItem(IDC_TREEDISCARD)->SetWindowText("&Delete Node");
		update=false;
	}
	else
	{
		if (!show) return;
		GetDlgItem(IDC_TREEAPPLY)->SetWindowText("&Apply Changes");
		GetDlgItem(IDC_TREEDISCARD)->SetWindowText("&Discard Changes");
		update=true;
	}
}

void CTreePane::OnTreeapply() 
{
	if (!update)
	{
		CRect rect;
		int i;

		/* Clear data pane */
		if (useDataPane) 
		{
			sctsFree(dataPaneData);
			dataPaneData=0;
			useDataPane=false;
		}

		/* Disable tree */
		m_tree.EnableWindow(FALSE);

		/* Clear properties */
		for (i=0;i<8;++i) m_properties.SetItemText(i,1,"");

		/* Display inherited ACL */
		m_inherit=TRUE;
		UpdateData(FALSE);
		m_acl.DeleteAllItems();
		UploadNodeACL(((SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem())));
		m_acl.EnableWindow(FALSE);
		GetDlgItem(IDC_TREEACLADD)->ShowWindow(SW_HIDE);
		GetDlgItem(IDC_TREEACLREMOVE)->ShowWindow(SW_HIDE);

		/* Display the edit box */
		m_properties.EnableWindow();
		m_properties.GetSubItemRect(0,1,LVIR_LABEL,rect);
		m_edit.SetWindowText("");
		m_edit.ShowWindow(SW_SHOW);
		m_properties.ClientToScreen(&rect);
		ScreenToClient(&rect);
		m_edit.MoveWindow(&rect);
		m_edit.SetFocus();
		editedItem=0;
		newNode=true;

		ShowApplyDiscard(TRUE);
		return;
	}

	int cnt;
	UpdateData(TRUE);
	SctsDMNodeFormat format=DMFORMAT_CHR;
	CString mimeType;
	CString title;
	CString temp;
	SctsDMCommand *command;
	bool sendReplace=false;
	char *data;
	int dataSize;
	SctsDMNode *node=(newNode?0:(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem()));
	/* Compare the node data with the UI data and determine what commands need to be sent */
	/* Validate format */
	temp=m_properties.GetItemText(1,1);
	if (!temp.IsEmpty())
	{
		while(format != DMFORMAT_NONE)
		{
			if (temp==SctsDMFormatString[format]) break;
			format=(SctsDMNodeFormat)(format+1);
		}
	}
	if (format==DMFORMAT_NONE)
	{
		MessageBox("Invalid format value.","Node Properties",MB_ICONERROR);
		return;
	}
	sendReplace=(node && format!=node->format);
	/* Check if type has changed */
	mimeType=m_properties.GetItemText(2,1);
	if (node && !sendReplace)
	{
		sendReplace=(mimeType!=(node->mimeType?node->mimeType:""));
	}
	/* Check if data has changed */
	if (useDataPane)
	{
		data=dataPaneData;
		dataSize=dataPaneDataSize;
	}
	else
	{
		temp=m_properties.GetItemText(7,1);
		data=(char *)(LPCTSTR)temp;
		dataSize=temp.GetLength();
	}
	if (node && !sendReplace)
	{
		sendReplace=(node->size!=dataSize || (dataSize && sctsMemcmp(node->data,data,dataSize)));
	}
	if (sendReplace) 
	{
		tree->ReplaceDMNodeData(node,tree->IsServerTree()?SctsDMDevice::GetSctsServerID():SIDX,dataSize?data:0,dataSize,SctsDMFormatString[format],mimeType.IsEmpty()?0:(char *)(LPCTSTR)mimeType,tree->IsServerTree()?&command:0);
		if (useDataPane)
		{
			sctsFree(dataPaneData);
			dataPaneData=0;
			useDataPane=false;
		}
	}
	/* Update the ACL */
	cnt=m_acl.GetItemCount();
	CString newACL="";
	if (!m_inherit && cnt)
	{
		CString newACLPart;
		int i,j;
		for (i=1;i<6;++i)
		{
			switch (i)
			{
			case 1:
				newACLPart="Add=";
				break;
			case 2:
				newACLPart="Delete=";
				break;
			case 3:
				newACLPart="Exec=";
				break;
			case 4:
				newACLPart="Get=";
				break;
			case 5:
				newACLPart="Replace=";
				break;
			}
			for (j=0;j<cnt;++j)
			{
				if (!m_acl.GetItemText(j,i).IsEmpty()) newACLPart+=m_acl.GetItemText(j,0)+"+";
			}
			if (newACLPart.Right(1)=="+") 
			{
				newACL+=newACLPart;
				newACL.SetAt(newACL.GetLength()-1,'&');
			}
		}
		if (!newACL.IsEmpty()) newACL=newACL.Left(newACL.GetLength()-1);
	}
	if (node && newACL!=(node->acl?node->acl:""))
		tree->ReplaceDMNodeProp(node,tree->IsServerTree()?SctsDMDevice::GetSctsServerID():SIDX,DMPROP_ACL,newACL.GetLength()?(char *)(LPCTSTR)newACL:0,tree->IsServerTree()?&command:0);
	/* Check if title has changed */
	title=m_properties.GetItemText(3,1);
	if (node && title!=(node->title?node->title:""))
		tree->ReplaceDMNodeProp(node,tree->IsServerTree()?SctsDMDevice::GetSctsServerID():SIDX,DMPROP_TITLE,title.GetLength()?(char *)(LPCTSTR)title:0,tree->IsServerTree()?&command:0);
	if (!node)
	{
		/* Validate node name */
		char *_temp;
		CString name;
		node=(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem());
		name=_temp=tree->GetPath(node);
		sctsFree(_temp);
		name+="/"+m_properties.GetItemText(0,1);
		if (tree->AddDMNode((char *)(LPCTSTR)name,tree->IsServerTree()?SctsDMDevice::GetSctsServerID():SIDX,SctsDMFormatString[format],false,dataSize?data:0,mimeType.IsEmpty()?0:(char *)(LPCTSTR)mimeType,dataSize,&node,tree->IsServerTree()?&command:0)!=200)
		{
			MessageBox("Invalid node properties.","Add Node",MB_ICONERROR);
			return;
		}
		if (useDataPane)
		{
			sctsFree(dataPaneData);
			dataPaneData=0;
			useDataPane=false;
		}
		temp=m_properties.GetItemText(3,1);
		if (!temp.IsEmpty()) node->title=sctsStrdup((LPCTSTR)temp);
		temp=m_properties.GetItemText(4,1);
		if (!temp.IsEmpty()) node->timeStamp=sctsStrdup((LPCTSTR)temp);
		temp=m_properties.GetItemText(5,1);
		if (!temp.IsEmpty()) node->version=atoi((LPCTSTR)temp);
		m_tree.SelectItem((HTREEITEM)node->uiData);
		/* Enable the tree */
		m_tree.EnableWindow(TRUE);
	}
	else
		UploadNode(node);
	ShowApplyDiscard(FALSE);
}

void CTreePane::OnSelchangingTreenodes(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	BOOL enable=TRUE;
	m_edit.ShowWindow(SW_HIDE);
	if (!pNMTreeView->itemNew.hItem) enable=FALSE;
	GetDlgItem(IDC_TREEAPPLY)->EnableWindow(enable);
	GetDlgItem(IDC_TREEDISCARD)->EnableWindow(enable);
	*pResult = 0;
}

void CTreePane::OnTreediscard() 
{
	SctsDMNode *node=(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem());
	if (!update)
	{
		SctsDMCommand *command=0;
		SctsDMNode *parent;
		if (tree->IsServerTree())
		{
			/* Prevent deletion of permanent objects on the server side */
            SctsDMDevice *unkDev = 0;
            SctsDMServerDevice *srvDev = (SctsDMServerDevice *)tree;
            unkDev = (SctsDMDevice *)srvDev;
            SctsDMDevice::MDAccType::iterator it = unkDev->accounts.find(node->name);
            if (it != unkDev->accounts.end())
            {
                if (tree->FindDMNode((char *)it->second.accPath.c_str()) == node)
                    return;
            }
			if (node->parent==tree->root && !sctsStrcmp(node->name,"DevDetail")) return;
			if (node->parent==tree->root && !sctsStrcmp(node->name,"DevInfo")) return;
            it = unkDev->accounts.begin();
            while (it != unkDev->accounts.end())
            {
                std::string pathElement = (it->second).accPath;
                parent = tree->FindDMNode((char *)pathElement.c_str());
			    if (node->IsParent(parent)) return;
                ++it;
            }
			parent=tree->FindDMNode("./DevDetail");
			if (node->IsParent(parent)) return;
			parent=tree->FindDMNode("./DevInfo");
			if (node->IsParent(parent)) return;	
		}
		if (tree->IsServerTree() && ((SctsDMServerDevice *)tree)->tester->testNode)
		{
			char *temp=0;
			temp=tree->GetPath(node);
			SCTSASSERT(temp);
			if (!sctsStrcmp(temp,((SctsDMServerDevice *)tree)->tester->testNode))
			{
				sctsFree(temp);
				return;
			}
			sctsFree(temp);
		}
		tree->DeleteDMNode(node,SctsDMDevice::GetSctsServerID(),tree->IsServerTree()?&command:0);
	}
	else
	{
		if (newNode)
		{
			/* Enable the tree */
			m_tree.EnableWindow(TRUE);
			newNode=false;
		}
		if (useDataPane)
		{
			sctsFree(dataPaneData);
			dataPaneData=0;
			useDataPane=false;
		}
		UploadNode(node);
		ShowApplyDiscard(FALSE);
	}
}

void CTreePane::OnSize(UINT nType, int cx, int cy) 
{
	RECT rect;
	CWnd *temp;
	int width;
	int height;
	int i;
	int shiftx;
	int shifty;
	CDialog::OnSize(nType, cx, cy);
	if (!m_tree.m_hWnd) return;
	GetClientRect(&rect);
	width=rect.right;
	height=rect.bottom;
	
	temp=GetDlgItem(IDC_TREECHANGE);
	temp->GetWindowRect(&rect);
	rect.top=height-rect.bottom+rect.top-10;
	rect.bottom=height-10;
	rect.left=10;
	rect.right=width-10;
	temp->MoveWindow(&rect);
	shifty=(rect.bottom+rect.top)/2;
	height=rect.top-10;
	
	temp=GetDlgItem(IDC_TREEAPPLY);
	temp->GetWindowRect(&rect);
	rect.bottom=(rect.bottom-rect.top)/2;
	rect.top=shifty-rect.bottom;
	rect.bottom+=shifty;
	rect.right=rect.right-rect.left;
	rect.left=width/2-rect.right-5;
	rect.right+=rect.left;
	temp->MoveWindow(&rect);

	temp=GetDlgItem(IDC_TREEDISCARD);
	rect.right=rect.right-rect.left;
	rect.left=width/2+5;
	rect.right+=rect.left;
	temp->MoveWindow(&rect);
	
	temp=GetDlgItem(IDC_TREEPROPFRM);
	temp->GetWindowRect(&rect);
	ScreenToClient(&rect);
	shifty=height/2-rect.bottom-5;
	shiftx=width-rect.right-10;
	rect.left+=shiftx;
	rect.right+=shiftx;
	rect.top+=shifty;
	rect.bottom+=shifty;
	temp->MoveWindow(&rect);
	
	for (i=IDC_TREEPROP;i>=IDC_TREEACLFRM;--i)
	{
		temp=GetDlgItem(i);
		temp->GetWindowRect(&rect);
		ScreenToClient(&rect);
		rect.left+=shiftx;
		rect.right+=shiftx;
		rect.top+=shifty;
		rect.bottom+=shifty;
		temp->MoveWindow(&rect);
	}

	temp=GetDlgItem(IDC_TREEFRM);
	rect.right=rect.left-10;
	rect.left=10;
	rect.top=10;
	rect.bottom=height;
	temp->MoveWindow(&rect);
	
	temp=GetDlgItem(IDC_TREENODES);
	rect.right-=10;
	rect.left=20;
	rect.top=30;
	rect.bottom-=10;
	temp->MoveWindow(&rect);

	RedrawWindow();
}

void CTreePane::OnDblclkTreeprop(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	if (pNMListView->iItem==7)
	{
		CString data=m_properties.GetItemText(7,1);
		char buffer[10];
		char *nodeData=useDataPane?dataPaneData:(char *)(LPCTSTR)data;
		SctsDMNodeFormat nodeFormat;
		dataPaneDataSize=useDataPane?dataPaneDataSize:data.GetLength();
		CDataPane dataPane(nodeData,nodeFormat,dataPaneDataSize);
		if (dataPane.DoModal()==TRUE)
		{
			if (nodeFormat==DMFORMAT_CHR && dataPaneDataSize<100)
			{
				m_properties.SetItemText(7,1,nodeData);
				sctsFree(nodeData);
				useDataPane=false;
			}
			else
			{
				m_properties.SetItemText(7,1,"Double-click to view.");
				if (dataPaneData) sctsFree(dataPaneData);
				dataPaneData=nodeData;
				useDataPane=true;
			}
			m_properties.SetItemText(6,1,itoa(dataPaneDataSize,buffer,10));
			m_properties.SetItemText(1,1,SctsDMFormatString[nodeFormat]);
			ShowApplyDiscard(TRUE);
		}

	}
	*pResult = 0;
}

void CTreePane::OnTreeInsertget() 
{
	((SctsDMServerTree *)tree)->InsertCommand(new SctsDMCommand(SCTS_GET,(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem())));
}

void CTreePane::OnTreeTestnode() 
{
	sctsFree(((SctsDMServerDevice *)tree)->tester->testNode);
	((SctsDMServerDevice *)tree)->tester->testNode=tree->GetPath((SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem()));			
}

void CTreePane::OnShowWindow(BOOL bShow, UINT nStatus) 
{
	HTREEITEM hitem;
	CDialog::OnShowWindow(bShow, nStatus);
	if (bShow && (hitem=m_tree.GetSelectedItem()))
		UploadNode((SctsDMNode *)m_tree.GetItemData(hitem));
}

void CTreePane::OnTreeInsertgetstruct() 
{
	((SctsDMServerTree *)tree)->InsertCommand(new SctsDMCommand(SCTS_GET,(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem()),DMPROP_STRUCT));
}

void CTreePane::OnTreeInsertgetstructdata() 
{
	((SctsDMServerTree *)tree)->InsertCommand(new SctsDMCommand(SCTS_GET,(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem()),DMPROP_STRUCTDATA));
}

void CTreePane::OnTreeInsertgettnds() 
{
	((SctsDMServerTree *)tree)->InsertCommand(new SctsDMCommand(SCTS_GET,(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem()),DMPROP_TNDS));
}

void CTreePane::OnTreeInsertexec() 
{
	((SctsDMServerTree *)tree)->InsertCommand(new SctsDMCommand(SCTS_EXEC,(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem())));
}

void CTreePane::OnTreeInsertexeccorrelator() 
{
    SctsDMCommand::SctsDMCommandSpecific execCmdSpecific;
    execCmdSpecific.correlator = "0123456789";
    ((SctsDMServerTree *)tree)->InsertCommand(new SctsDMCommand(SCTS_EXEC,(SctsDMNode *)m_tree.GetItemData(m_tree.GetSelectedItem()),DMPROP_NONE,false,&execCmdSpecific));
}
