/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : LevelEdit                                                    *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Tools/LevelEdit/InstancesPage.cpp            $*
 *                                                                                             *
 *                       Author:: Patrick Smith                                                *
 *                                                                                             *
 *                     $Modtime:: 5/11/01 9:25a                                               $*
 *                                                                                             *
 *                    $Revision:: 14                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "stdafx.h"
#include "leveledit.h"
#include "instancespage.h"
#include "nodemgr.h"
#include "node.h"
#include "utils.h"
#include "cameramgr.h"
#include "gotoobjectdialog.h"
#include "sceneeditor.h"
#include "icons.h"
#include "nodecategories.h"
#include "definition.h"
#include "definitionfactory.h"
#include "definitionfactorymgr.h"
#include "preset.h"


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


///////////////////////////////////////////////////////////////////////
//	Local prototypes
///////////////////////////////////////////////////////////////////////
int CALLBACK InstancesListSortCallback (LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);

#ifndef ListView_SetCheckState
   #define ListView_SetCheckState(hwndLV, i, fCheck) \
      ListView_SetItemState(hwndLV, i, \
      INDEXTOSTATEIMAGEMASK((fCheck)+1), LVIS_STATEIMAGEMASK)
#endif

#ifndef ListView_SetOverlay
   #define ListView_SetOverlay(hwndLV, i, overlay) \
      ListView_SetItemState(hwndLV, i, \
      INDEXTOOVERLAYMASK(overlay), LVIS_OVERLAYMASK)
#endif


///////////////////////////////////////////////////////////////////////
//	Local structures
///////////////////////////////////////////////////////////////////////
typedef enum
{
	TYPE_NAVIGATOR,
	TYPE_FACTORY,
	TYPE_NODE,
} ITEM_TYPE;

typedef struct
{
	ITEM_TYPE	type;
	StringClass	name;
	union
	{
		uint32		class_id;
		NodeClass *	node;
	};

} ITEM_DATA;


/////////////////////////////////////////////////////////////////////////////
// Local constants
/////////////////////////////////////////////////////////////////////////////
const int TOOLBAR_HEIGHT		= 36;
const int TOOLBAR_V_SPACING	= 5;
const int TOOLBAR_V_BORDER		= TOOLBAR_V_SPACING * 2;
const int TOOLBAR_H_SPACING	= 5;
const int TOOLBAR_H_BORDER		= TOOLBAR_H_SPACING * 2;

//const int COLUMN_CHECK			= 0;
//const int COLUMN_PLUS			= 1;
const int COLUMN_NAME			= 0;

/////////////////////////////////////////////////////////////////////////////
//
// InstancesPageClass
//
/////////////////////////////////////////////////////////////////////////////
InstancesPageClass::InstancesPageClass (CWnd *parent_wnd)
	:	m_ClassID (0),
		CDialog(InstancesPageClass::IDD)
{
	//{{AFX_DATA_INIT(InstancesPageClass)
	//}}AFX_DATA_INIT
	Create (InstancesPageClass::IDD, parent_wnd);
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// ~InstancesPageClass
//
/////////////////////////////////////////////////////////////////////////////
InstancesPageClass::~InstancesPageClass (void)
{
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// DoDataExchange
//
/////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::DoDataExchange (CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(InstancesPageClass)
	DDX_Control(pDX, IDC_INSTANCE_LIST, m_ListCtrl);
	//}}AFX_DATA_MAP
	return ;
}


BEGIN_MESSAGE_MAP(InstancesPageClass, CDialog)
	//{{AFX_MSG_MAP(InstancesPageClass)
	ON_WM_SIZE()
	ON_WM_DESTROY()
	ON_COMMAND(IDC_EDIT, OnEdit)
	ON_COMMAND(IDC_GOTO, OnGoto)
	ON_COMMAND(IDC_SHOWALL, OnShowAll)
	ON_COMMAND(IDC_SELECT, OnSelect)
	ON_COMMAND(IDC_DELETE, OnDelete)
	ON_NOTIFY(LVN_DELETEITEM, IDC_INSTANCE_LIST, OnDeleteitemInstanceList)
	ON_NOTIFY(NM_DBLCLK, IDC_INSTANCE_LIST, OnDblclkInstanceList)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_INSTANCE_LIST, OnItemchangedInstanceList)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


#ifdef _DEBUG
void InstancesPageClass::AssertValid() const
{
	CDialog::AssertValid();
}

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


/////////////////////////////////////////////////////////////////////////////
//
// OnSize
//
/////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::OnSize
(
	UINT	nType,
	int	cx,
	int	cy
) 
{	
	// Allow the base class to process this message
	CDialog::OnSize (nType, cx, cy);

	if (::IsWindow (m_ListCtrl) && (cx > 0) && (cy > 0)) {

		// Get the bounding rectangle of the form window
		CRect parentrect;
		GetWindowRect (&parentrect);
		
		// Get the bounding rectangle of the toolbar
		CRect toolbar_rect;
		m_Toolbar.GetWindowRect (&toolbar_rect);
		ScreenToClient (&toolbar_rect);

		// Move the toolbar so it is in its correct position
		m_Toolbar.SetWindowPos (NULL,
										TOOLBAR_H_SPACING,
										(cy - TOOLBAR_V_SPACING) - toolbar_rect.Height (),
										cx - TOOLBAR_H_BORDER,
										toolbar_rect.Height (),
										SWP_NOZORDER);

		// Get the bounding rectnagle of the list ctrl
		RECT list_rect;
		m_ListCtrl.GetWindowRect (&list_rect);
				
		CRect client_rect = list_rect;
		ScreenToClient (&client_rect);
		int list_height = ((cy - TOOLBAR_V_BORDER) - toolbar_rect.Height ()) - client_rect.top;

		// Resize the tab control to fill the entire contents of the client area
		m_ListCtrl.SetWindowPos (	NULL,
											0,
											0,
											cx-((list_rect.left - parentrect.left) << 1),
											list_height,
											SWP_NOZORDER | SWP_NOMOVE);	
	}

	return ;	
}


/////////////////////////////////////////////////////////////////////////////
//
// OnDestroy
//
/////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::OnDestroy (void) 
{
	//
	// Free the state image list we associated with the control
	//
	CImageList *imagelist = m_ListCtrl.GetImageList (LVSIL_STATE);
	m_ListCtrl.SetImageList (NULL, LVSIL_STATE);
	SAFE_DELETE (imagelist);

	//
	// Remove the main image list we associated with the control
	//
	m_ListCtrl.SetImageList (NULL, LVSIL_SMALL);
	m_ListCtrl.DeleteAllItems ();

	::RemoveProp (m_ListCtrl, "TRANS_ACCS");
	::RemoveProp (m_hWnd, "TRANS_ACCS");

	CDialog::OnDestroy ();
	return ;	
}


/////////////////////////////////////////////////////////////////////////////
//
// OnEdit
//
/////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::OnEdit (void)
{
	// Show the settings dialog for the currently selected item
	NodeClass *node = Get_Item_Node (m_ListCtrl.GetNextItem (-1, LVNI_ALL | LVNI_SELECTED));
	if (node != NULL) {
		node->Show_Settings_Dialog ();
	}

	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnGoto
//
/////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::OnGoto (void)
{
	NodeClass *selected_node = NULL;

	//
	//	Try to find the first selected node (if any)
	//
	int index = m_ListCtrl.GetNextItem (-1, LVNI_SELECTED | LVNI_ALL);
	if (index >= 0) {
		selected_node = Get_Item_Node (index);
	}

	//
	// Show the 'goto' dialog...
	//
	GotoObjectDialogClass dialog (selected_node);
	dialog.DoModal ();
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// OnShowAll
//
/////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::OnShowAll (void)
{
	//
	//	Show all the nodes
	//
	for (	NodeClass *node = NodeMgrClass::Get_First ();
			node != NULL;
			node = NodeMgrClass::Get_Next (node))
	{
		node->Hide (false);
	}

	//
	//	Fixup the UI
	//
	for (int index = 0; index < m_ListCtrl.GetItemCount (); index ++) {
		ITEM_DATA *item_data = (ITEM_DATA *)m_ListCtrl.GetItemData (index);
		if (item_data != NULL && item_data->type != TYPE_NAVIGATOR) {
			ListView_SetCheckState (m_ListCtrl, index, true);
		}
	}

	//
	// Make sure the main view is updated
	//
	::Refresh_Main_View ();	
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  WindowProc
//
////////////////////////////////////////////////////////////////////////////
LRESULT
InstancesPageClass::WindowProc
(
    UINT		message,
    WPARAM	wParam,
    LPARAM	lParam
)
{
	//
	// Is this the message we are expecting?
	//
	if (message == WM_USER+102) {			
		int index = (int)lParam;

		//
		//	Should we toggle the checkmark for this entry?
		//
		ITEM_DATA *item_data = (ITEM_DATA *)m_ListCtrl.GetItemData (index);
		if (item_data != NULL && item_data->type != TYPE_NAVIGATOR) {

			//
			//	Either hide or show the selected nodes
			//
			bool hidden = (ListView_GetCheckState (m_ListCtrl, index) == false);
			Hide_Nodes (index, !hidden);
			ListView_SetCheckState (m_ListCtrl, index, hidden);
		}
	}

	return CDialog::WindowProc(message, wParam, lParam);
}


////////////////////////////////////////////////////////////////////////////
//
//  CheckBoxSubclassProc
//
////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK
CheckBoxSubclassProc
(
	HWND		hwnd,
	UINT		message,
	WPARAM	wparam,
	LPARAM	lparam
)
{
	WNDPROC pold_proc = (WNDPROC)::GetProp (hwnd, "OLDPROC");
	
	if (message == WM_LBUTTONUP) {
		
		//
		//	Find out where the user clicked
		//
		LVHITTESTINFO hittest = { 0 };
		hittest.pt.x = LOWORD (lparam);
		hittest.pt.y = HIWORD (lparam);
		::SendMessage (hwnd, LVM_HITTEST, 0, (LPARAM)&hittest);
		
		//
		//	Did the user click one of the checkboxes?
		//
		if (hittest.flags & LVHT_ONITEMSTATEICON) {

			//
			//	Notify the instances page that the user wants to toggle the checkbox
			//
			::PostMessage (::GetParent (hwnd), WM_USER+102, 0, (LPARAM)hittest.iItem);
		}

	} else if (message == WM_DESTROY) {
		::SetWindowLong (hwnd, GWL_WNDPROC, (LONG)pold_proc);
		::RemoveProp (hwnd, "OLDPROC");
	}

	if (pold_proc != NULL) {
		return ::CallWindowProc (pold_proc, hwnd, message, wparam, lparam);
	} else {
		return ::DefWindowProc (hwnd, message, wparam, lparam);
	}			
}


////////////////////////////////////////////////////////////////////////////
//
//  OnDblclkInstanceList
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::OnDblclkInstanceList
(
	NMHDR *	pNMHDR,
	LRESULT* pResult
) 
{
	// Determine what client-coord location was double-clicked on
	DWORD mouse_pos = ::GetMessagePos ();
	POINT hit_point = { GET_X_LPARAM (mouse_pos), GET_Y_LPARAM (mouse_pos) };
	m_ListCtrl.ScreenToClient (&hit_point);

	// Goto the node that was double-clicked on (if possible)
	UINT flags = 0;
	int index = m_ListCtrl.HitTest (hit_point, &flags);
	if ((index >= 0) && ((flags & LVHT_ONITEMLABEL) || (flags & LVHT_ONITEMICON))) {
		
		ITEM_DATA *item_data = (ITEM_DATA *)m_ListCtrl.GetItemData (index);		
		if (item_data != NULL) {

			
			if (item_data->type == TYPE_NAVIGATOR) {
				
				//
				//	Determine which class id we should display
				//
				int class_id = 0;				
				if (m_ClassIDStack.Count () > 0) {
					class_id = m_ClassIDStack[m_ClassIDStack.Count ()-1];
					m_ClassIDStack.Delete (m_ClassIDStack.Count ()-1);
				}

				Populate_List (class_id);

			} else if (item_data->type == TYPE_FACTORY) {
				
				//
				//	Fill the list with children of the given class id
				//
				m_ClassIDStack.Add (m_ClassID);
				Populate_List (item_data->class_id);
			} else {
				
				NodeClass *node = Get_Item_Node (index);
				if (node != NULL) {
					
					//
					//	If this node is a terrain, then display the subojects,
					// otherwise snap the camera to the node
					//
					if (node->Get_Sub_Node_Count () > 0) {
						m_ClassIDStack.Add (m_ClassID);
						Populate_List (node);
					} else {
						::Get_Camera_Mgr ()->Goto_Node (node);
					}
				}
			}
		}		
	}
	
	(*pResult) = 0;
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  OnSelect
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::OnSelect (void)
{
	//
	//	Clear all selected nodes
	//
	::Get_Scene_Editor ()->Set_Selection (NULL);

	//
	//	Toggle the selection state of all the nodes in the selection set
	//
	int index = -1;
	while ((index = m_ListCtrl.GetNextItem (index, LVNI_ALL | LVNI_SELECTED)) >= 0) {
		NodeClass *node = Get_Item_Node (index);
		if (node != NULL) {
			::Get_Scene_Editor ()->Toggle_Selection (node);
		}
	}
	
	::Refresh_Main_View ();
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  OnDelete
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::OnDelete (void) 
{
	CString message = "Are you sure you wish to delete the selected node(s)?";
	CString title;
	title.LoadString (IDS_DELETE_CONFIRM_TITLE);
	
	//
	// Ask the user if they really want to delete the selected item
	//
	if (MessageBox (message, title, MB_ICONEXCLAMATION | MB_YESNO) == IDYES) {

		//
		//	Build a list of all the nodes we need to delete
		//
		DynamicVectorClass<NodeClass *> delete_list;
		int index = -1;
		while ((index = m_ListCtrl.GetNextItem (index, LVNI_ALL | LVNI_SELECTED)) >= 0) {
			NodeClass *node = Get_Item_Node (index);
			if (node != NULL) {
				delete_list.Add (node);
			}
		}

		//
		//	Delete all the selected nodes
		//
		for (index = 0; index < delete_list.Count (); index ++) {
			::Get_Scene_Editor ()->Delete_Node (delete_list[index]);
		}
		
		::Refresh_Main_View ();
	}
	
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  OnInitDialog
//
////////////////////////////////////////////////////////////////////////////
BOOL
InstancesPageClass::OnInitDialog (void)
{
	CDialog::OnInitDialog ();

	m_Toolbar.CreateEx (this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP, CRect(0, 0, 0, 0), 101);
	m_Toolbar.SetOwner (this);
	m_Toolbar.LoadToolBar (IDR_INSTANCES_TOOLBAR);
	m_Toolbar.SetBarStyle (m_Toolbar.GetBarStyle () | CBRS_TOOLTIPS | CBRS_FLYBY);

	//
	// Configure the toolbar
	//
	CRect parentrect;
	GetWindowRect (&parentrect);
	m_Toolbar.SetWindowPos (NULL, 0, 0, parentrect.Width () - TOOLBAR_H_BORDER, TOOLBAR_HEIGHT, SWP_NOZORDER | SWP_NOMOVE);	
	m_Toolbar.Enable_Button (IDC_DELETE, false);
	m_Toolbar.Enable_Button (IDC_SELECT, false);
	m_Toolbar.Enable_Button (IDC_EDIT, false);

	//
	//	Configure the list control's extended styles
	//
	//m_ListCtrl.SetExtendedStyle (m_ListCtrl.GetExtendedStyle () | LVS_EX_SUBITEMIMAGES | LVS_EX_FULLROWSELECT);

	//
	// Create a state imagelist for the list control to use for checkboxes
	//
	CImageList *imagelist = new CImageList;
	imagelist->Create (MAKEINTRESOURCE (IDB_CHECKBOX_STATES1), 13, 0, RGB (255, 0, 255));
	m_ListCtrl.SetImageList (imagelist, LVSIL_STATE);
	
	//
	//	Setup the columns of the list control
	//
	CRect client_rect;
	m_ListCtrl.GetClientRect (&client_rect);
	m_ListCtrl.InsertColumn (COLUMN_NAME, "Name");
	m_ListCtrl.SetColumnWidth (COLUMN_NAME, client_rect.Width () - 5);

	//
	// Pass the general use imagelist onto the list control
	//
	m_ListCtrl.SetImageList (::Get_Global_Image_List (), LVSIL_SMALL);

	//
	//	Subclass the list control so we can handle the checkstates
	//
	LONG oldproc = ::SetWindowLong (m_ListCtrl, GWL_WNDPROC, (LONG)CheckBoxSubclassProc);
	::SetProp (m_ListCtrl, "OLDPROC", (HANDLE)oldproc);

	//
	//	Populate the list control
	//
	Reset_List ();

	SetProp (m_ListCtrl, "TRANS_ACCS", (HANDLE)1);
	SetProp (m_hWnd, "TRANS_ACCS", (HANDLE)1);
	return TRUE;
}


/////////////////////////////////////////////////////////////////////////////
//
// Insert_Factory
//
/////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Insert_Factory (LPCTSTR name, int class_id)
{
	//
	//	Add an entry to the list control for the factory
	//
	int index = m_ListCtrl.InsertItem (0xFF, name, FOLDER_ICON);
	if (index >= 0) {

		//
		//	Set the text and the icon for this entry
		//
		//m_ListCtrl.SetItem (index, COLUMN_NAME, LVIF_TEXT | LVIF_IMAGE, name, FOLDER_ICON, 0, 0, 0);

		//
		//	Allocate a new wrapper if we need to
		//
		ITEM_DATA *item_data = (ITEM_DATA *)m_ListCtrl.GetItemData (index);
		if (item_data == NULL) {
			item_data = new ITEM_DATA;
		}

		//
		//	Set a flag in the wrapper so we know what kind of data
		// this list item contains.
		//
		item_data->type		= TYPE_FACTORY;
		item_data->class_id	= class_id;
		item_data->name		= name;
		m_ListCtrl.SetItemData (index, (DWORD)item_data);
		m_ListCtrl.SetCheck (index, TRUE);
	}
	
	return ;	
}


/////////////////////////////////////////////////////////////////////////////
//
// Insert_Node
//
/////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Insert_Node (NodeClass *node)
{
	//
	//	Add an entry to the list control for the node
	//
	int index = m_ListCtrl.InsertItem (0xFF, node->Get_Name (), node->Get_Icon_Index ());
	if (index >= 0) {

		//
		//	Set the text and the icon for this entry
		//
		//m_ListCtrl.SetItem (index, COLUMN_NAME, LVIF_TEXT | LVIF_IMAGE, node->Get_Name (), node->Get_Icon_Index (), 0, 0, 0);
		
		//
		//	Allocate a new wrapper if we need to
		//
		ITEM_DATA *item_data = (ITEM_DATA *)m_ListCtrl.GetItemData (index);
		if (item_data == NULL) {
			item_data = new ITEM_DATA;
		}

		//
		//	Set a flag in the wrapper so we know what kind of data
		// this list item contains.
		//
		SAFE_ADD_REF (node);
		item_data->type	= TYPE_NODE;
		item_data->node	= node;
		item_data->name	= node->Get_Name ();
		m_ListCtrl.SetItemData (index, (DWORD)item_data);
		ListView_SetCheckState (m_ListCtrl, index, (node->Is_Hidden () == false));
	}

	return ;	
}


/////////////////////////////////////////////////////////////////////////////
//
// Insert_Navigator
//
/////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Insert_Navigator (void)
{
	//
	//	Add an entry to the list control for the navigator
	//
	int index = m_ListCtrl.InsertItem (0xFF, "..", NAVIGATOR_ICON);
	if (index >= 0) {

		//
		//	Set the text and the icon for this entry
		//
		//m_ListCtrl.SetItem (index, COLUMN_NAME, LVIF_TEXT | LVIF_IMAGE, "..", NAVIGATOR_ICON, 0, 0, 0);
		
		//
		//	Allocate a new wrapper if we need to
		//
		ITEM_DATA *item_data = (ITEM_DATA *)m_ListCtrl.GetItemData (index);
		if (item_data == NULL) {
			item_data = new ITEM_DATA;
		}

		//
		//	Set a flag in the wrapper so we know what kind of data
		// this list item contains.
		//
		item_data->type	= TYPE_NAVIGATOR;
		item_data->node	= NULL;
		item_data->name	= "..";
		m_ListCtrl.SetItemData (index, (DWORD)item_data);
	}

	return ;	
}


////////////////////////////////////////////////////////////////////////////
//
//  Add_Node
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Add_Node (NodeClass *node)
{
	WWASSERT (node != NULL);

	PresetClass *preset = node->Get_Preset ();
	if (preset != NULL) {

		DefinitionClass *definition = preset->Get_Definition ();
		WWASSERT (definition != NULL);
		if (definition != NULL) {

			//
			//	Should the node be inserted into the current view?
			//
			if (definition->Get_Class_ID () == m_ClassID) {
				m_ListCtrl.SetRedraw (FALSE);
				Insert_Node (node);
				m_ListCtrl.SortItems (InstancesListSortCallback, 0L);
				m_ListCtrl.SetRedraw (TRUE);
			}			
		}
	}

	Update_Overlays ();
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  Remove_Node
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Remove_Node (NodeClass *node)
{
	WWASSERT (node != NULL);

	//
	//	Attempt to find this node in the current view
	//
	for (int index = 0; index < m_ListCtrl.GetItemCount (); index ++) {
		NodeClass *curr_node = Get_Item_Node (index);
		
		//
		//	If this is the node we are looking for, then remove it from
		// the view.
		//
		if (curr_node == node) {
			m_ListCtrl.DeleteItem (index);
			break;
		}
	}

	Update_Overlays ();
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Get_Item_Node
//
/////////////////////////////////////////////////////////////////////////////
NodeClass *
InstancesPageClass::Get_Item_Node (int index)
{
	NodeClass *node = NULL;

	//
	//	If this item represents a node, then return the node
	//	pointer to the caller.
	//
	ITEM_DATA *item_data = (ITEM_DATA *)m_ListCtrl.GetItemData (index);
	if ((item_data != NULL) && (item_data->type == TYPE_NODE)) {
		node = item_data->node;
	}

	return node;
}


////////////////////////////////////////////////////////////////////////////
//
//  Set_Node_Check
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Set_Node_Check
(
	int	index,
	bool	onoff
)
{
	// First off, show/hide the node
	/*NodeClass *node = Get_Item_Node (index);
	if (node != NULL) {
		node->Hide (!onoff);	
	}*/

	// Update the UI for this node
	//ListView_SetCheckState (m_ListCtrl, index, onoff);
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  Reset_List
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Reset_List (void)
{
	m_ClassIDStack.Delete_All ();
	Populate_List ((uint32)0);
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  Populate_List
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Populate_List (uint32 class_id)
{
	m_ListCtrl.SetRedraw (FALSE);
	m_ListCtrl.DeleteAllItems ();

	if (class_id == 0) {
	
		//
		//	Add all the node categories to the list control
		//
		for (int index = 0; index < PRESET_CATEGORY_COUNT; index ++) {
			Insert_Factory (PRESET_CATEGORIES[index].name, PRESET_CATEGORIES[index].clsid);
		}		

	} else {
		Insert_Navigator ();

		//
		//	Fill all the node instances into the list control
		//
		for (	NodeClass *node = NodeMgrClass::Find_First (class_id);
				node != NULL;
				node = NodeMgrClass::Find_Next (node, class_id))
		{
			//
			//	Add this node to the list control
			//
			Insert_Node (node);
		}

		//
		//	Fill all the factory instances into the list control
		//
		for (	DefinitionFactoryClass *factory = DefinitionFactoryMgrClass::Get_First (class_id);
				factory != NULL;
				factory = DefinitionFactoryMgrClass::Get_Next (factory, class_id))
		{
			//
			//	Add this node to the list control
			//
			if (factory->Get_Class_ID () != class_id) {
				Insert_Factory (factory->Get_Name (), factory->Get_Class_ID ());
			}
		}

		//
		//	Sort all the items
		//
		m_ListCtrl.SortItems (InstancesListSortCallback, 0L);
	}

	m_ListCtrl.SetRedraw (TRUE);

	//
	//	Save this class ID for later
	//
	m_ClassID = class_id;
	
	//
	//	Make sure the toolbar is up to date
	//
	Update_Overlays ();
	Update_Button_States ();
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  Populate_List
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Populate_List (NodeClass *node)
{
	m_ListCtrl.SetRedraw (FALSE);
	m_ListCtrl.DeleteAllItems ();

	if (node != NULL) {
		Insert_Navigator ();

		//
		//	Add all the sub-nodes to the list control
		//
		for (int index = 0; index < node->Get_Sub_Node_Count (); index ++) {
			NodeClass *sub_node = node->Get_Sub_Node (index);
			if (sub_node != NULL) {
				
				//
				//	Add this node to the list control
				//			
				Insert_Node (sub_node);
			}
		}
	}

	//
	//	Sort all the items
	//
	m_ListCtrl.SortItems (InstancesListSortCallback, 0L);
	m_ListCtrl.SetRedraw (TRUE);

	//
	//	Make sure the toolbar is up to date
	//
	Update_Overlays ();
	Update_Button_States ();
	return ;
}



////////////////////////////////////////////////////////////////////////////
//
//  OnDeleteitemInstanceList
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::OnDeleteitemInstanceList
(
	NMHDR *		pNMHDR,
	LRESULT *	pResult
) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	(*pResult) = 0;

	// Get the node associated with this entry
	NodeClass *node = Get_Item_Node (pNMListView->iItem);
	MEMBER_RELEASE (node);

	// Free the item data structure associated with this entry
	ITEM_DATA *item_data = (ITEM_DATA *)m_ListCtrl.GetItemData (pNMListView->iItem);
	SAFE_DELETE (item_data);	
	m_ListCtrl.SetItemData (pNMListView->iItem, 0L);
	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  InstancesListSortCallback
//
////////////////////////////////////////////////////////////////////////////
int CALLBACK
InstancesListSortCallback
(
	LPARAM lParam1,
	LPARAM lParam2,
   LPARAM lParamSort
)
{
	int retval = 0;

	ITEM_DATA *item_data1 = (ITEM_DATA *)lParam1;
	ITEM_DATA *item_data2 = (ITEM_DATA *)lParam2;
	if (item_data1 != NULL && item_data2 != NULL) {
		
		//
		//	Do the types match?
		//
		if (item_data1->type == item_data2->type) {
			
			//
			//	Do a simple name comparison
			//
			retval = ::lstrcmpi (item_data1->name, item_data2->name);

		} else {

			//
			//	Sort based on type
			//
			retval = (item_data1->type - item_data2->type);
		}
	}
	
	return retval;
}


////////////////////////////////////////////////////////////////////////////
//
//  OnItemchangedInstanceList
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::OnItemchangedInstanceList
(
	NMHDR *	pNMHDR,
	LRESULT* pResult
)
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	*pResult = 0;

	if (pNMListView->uChanged & LVIF_STATE) {

		//
		//	Did the selection set change?
		//
		if (pNMListView->uNewState & LVIS_SELECTED || pNMListView->uOldState & LVIS_SELECTED) {			
			Update_Button_States ();
		}
	}

	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  Update_Button_States
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Update_Button_States (void)
{
	//
	//	Determine if there are any nodes in the current selection set.
	//
	bool enable	= false;
	int index	= -1;
	while (!enable && (index = m_ListCtrl.GetNextItem (index, LVNI_ALL | LVNI_SELECTED)) >= 0) {
		enable |= (Get_Item_Node (index) != NULL);
	}

	//
	// Enable or disable the edit button depending on
	// whether the selected item is a category or a node
	//
	m_Toolbar.Enable_Button (IDC_DELETE, enable);
	m_Toolbar.Enable_Button (IDC_EDIT, enable);
	m_Toolbar.Enable_Button (IDC_SELECT, enable);
	return ;
}

////////////////////////////////////////////////////////////////////////////
//
//  Hide_Node
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Hide_Node (NodeClass *node, uint32 class_id, bool hide)
{
	PresetClass *preset = node->Get_Preset ();
	if (preset != NULL) {
		DefinitionClass *definition = preset->Get_Definition ();
		if (definition != NULL) {
			
			//
			//	Did this node come from the selected factory?
			//
			uint32 curr_class_id	= definition->Get_Class_ID ();
			uint32 superclass_id	= ::SuperClassID_From_ClassID (curr_class_id);
			if (curr_class_id == class_id || superclass_id == class_id) {
				
				//
				//	Change this node's display state
				//							
				node->Hide (hide);
			}
		}
	}

	//
	//	Now, check all this nodes children
	//
	for (int index = 0; index < node->Get_Sub_Node_Count (); index ++) {
		NodeClass *sub_node = node->Get_Sub_Node (index);
		if (sub_node != NULL) {
			Hide_Node (sub_node, class_id, hide);
		}
	}

	return ;
}


////////////////////////////////////////////////////////////////////////////
//
//  Hide_Nodes
//
////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Hide_Nodes (int index, bool hide)
{
	ITEM_DATA *item_data = (ITEM_DATA *)m_ListCtrl.GetItemData (index);		
	if (item_data != NULL) {
		
		if (item_data->type == TYPE_FACTORY) {

			//
			//	Loop over all the nodes in the world
			//
			for (	NodeClass *node = NodeMgrClass::Get_First ();
					node != NULL;
					node = NodeMgrClass::Get_Next (node))
			{				
				//
				//	Hide this node (if necessary)
				//
				Hide_Node (node, item_data->class_id, hide);
			}
			
		} else if (item_data->type == TYPE_NODE) {
						
			//
			//	Change this node's display state
			//
			item_data->node->Hide (hide);
		}
	}

	//
	// Make sure the main view is updated
	//
	::Refresh_Main_View ();		
	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Update_Overlays
//
/////////////////////////////////////////////////////////////////////////////
void
InstancesPageClass::Update_Overlays (void)
{
	//
	//	Loop over all the entries in the current view
	//
	for (int index = 0; index < m_ListCtrl.GetItemCount (); index ++) {
		
		//
		//	Get information about this entry
		//
		ITEM_DATA *item_data = (ITEM_DATA *)m_ListCtrl.GetItemData (index);
		if (item_data != NULL) {
			
			bool needs_overlay = false;

			//
			//	Check to see if this entry has any sub-entries
			//
			if (item_data->type == TYPE_FACTORY) {
				needs_overlay = Does_Factory_Have_Children (item_data->class_id);				
			} else if (item_data->type == TYPE_NODE && (item_data->node->Get_Sub_Node_Count () > 0)) {
				needs_overlay = true;
			}

			//
			//	Set the appropriate overlay
			//
			ListView_SetOverlay (m_ListCtrl, index, needs_overlay ? 1 : 0);
		}
	}

	return ;
}


/////////////////////////////////////////////////////////////////////////////
//
// Does_Factory_Have_Children
//
/////////////////////////////////////////////////////////////////////////////
bool
InstancesPageClass::Does_Factory_Have_Children (uint32 factory_id)
{
	bool retval = false;

	//
	//	Check to see if there are any instances of this factory
	// in the world
	//
	for (	NodeClass *node = NodeMgrClass::Get_First ();
			node != NULL && retval == false;
			node = NodeMgrClass::Get_Next (node))
	{
		PresetClass *preset = node->Get_Preset ();
		if (preset != NULL) {
			DefinitionClass *definition = preset->Get_Definition ();
			if (definition != NULL) {
				
				//
				//	Did this node come from the specified factory?
				//
				uint32 class_id		= definition->Get_Class_ID ();
				uint32 superclass_id	= ::SuperClassID_From_ClassID (class_id);
				if (class_id == factory_id || superclass_id == factory_id) {
					retval = true;
				}
			}
		}
	}

	//
	//	Check to see if this factory has any sub-factories
	//
	if (retval == false) {
		DefinitionFactoryClass *factory = DefinitionFactoryMgrClass::Get_First (factory_id);
		if (factory != NULL && factory->Get_Class_ID () != factory_id) {
			retval = true;
		}
	}

	return retval;
}
