//================================================================
// EQUINOX-3D(TM) plugin
//
// Hierarchies
// - Connect / disconnect / reparent Nodes and Branches
// - Separate selected Objects (move them under a new Model)
// - Add Object to same Nodes as...
// - Collapse branches
// - Move BranchMoves / Objects within hierarchies
// - Group objects
// - Expand selected branches
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Jan-07 02:44:21
//
// Copyright (C) 1996-2026 By Gabor Nagy. All rights reserved.
//================================================================
#include <assert.h>
#include <stdio.h>

#include <ELists.h>
#include <EMalloc.h>
#include <EPlugins.h>


#ifndef _E3D_Matrix_h
#include <E3D/Matrix.h>
#endif

#include <E3D/Scene.h>
#include <E3D/Select.h>

#include <Xe/PushButton.h>
#include <Xe/ToggleButton.h>
#include <Xe/Shell.h>
#include <Xe/Dialog.h>

#include <E3D/UI/ModeStack.h>    // E3dFIXME : For E3dp_ResetWorkModes, temporarily
#include <E3D/UI/Panel.h>
#include <E3D/UI/StatusPanel.h>  // For E3dp_PrintMessage



typedef struct
{
	E3dObject*  Object;
	EListA_s(, EObjectAndIndex, AddToModels);
	EListA_s(, EObjectAndIndex, DelFrModels);
} E3dObjectMove;
EListA_t(E3dObjectMove);

typedef struct
{
	E3dNode*  Node;
	E3dNode*  OldParent;EIndex  OldIndex;
	E3dNode*  NewParent;
} E3dBranchMove;
EListA_t(E3dBranchMove);

// Undoable Operator for changing hierarchy connections
//
typedef struct
{
	EOperatorCore;

	EListA_s(, E3dBranchMove, BranchMoves);
	EListA_s(, E3dObjectMove, ObjectMoves);
} E3dOperatorEditHierarchy;


EOperatorClass*  E3dOperatorEditHierarchy_class = NULL;


char*                E_PluginInfo = "Hierarchy manipulation:\n - Collapse branch\n - Connect / disconnect Nodes and Objects";
static EPlugin*      _Plugin = NULL;
static EPluginLock*  _ConnectPickLock = NULL;const char  _ConnectPickStr[] = "ConnectPick";
static EPluginLock*  _DisconnectPickLock = NULL;

static const EBool  _SeparateSelectedObjects_NewModelUnderOldParent = TRUE;


EpIconDef3I(static, Connect);
EpIconDef3I(static, Disconnect);
EpIconDef3I(static, Reparent);


static const char*  _Msg_SelectModelOrObjectToReparent     = "Select Models or Objects to reparent";
static const char*  _Msg_SelectOnlyModelOrObjectToReparent = "Select either only Models or only Objects to reparent";

static const char*  _Msg_SelectObjectToReInstantiate       = "Select Objects to re-instantiate";

static const char*  _Msg_SelectModelOrObjectToConnect  = "Select Models or Objects to connect";
static const char*  _Msg_SelectOnlyModelOrObject       = "Select either only Models or only Objects";


static const char*  _Msg_ConnectStr        = "Connect";
static const char*  _Msg_ConnectModelStr   = "Connect Model";
static const char*  _Msg_ConnectModelsStr  = "Connect Models";
static const char*  _Msg_ConnectObjectStr  = "Connect Object";
static const char*  _Msg_ConnectObjectsStr = "Connect Objects";

static const char*  _Msg_ReparentModelStr   = "Reparent Model";
static const char*  _Msg_ReparentModelsStr  = "Reparent Models";
static const char*  _Msg_ReparentObjectStr  = "Reparent Object";
static const char*  _Msg_ReparentObjectsStr = "Reparent Objects";

static const char*  _Msg_ReparentStr      = "Reparent";
static const char*  _Msg_InstantiateAsStr = "Connect Objects to the same Model as";


static XeWidget      _DialogW = NULL;
static EPluginLock*  _DialogLock = NULL;

static XeDWidget  _MenuButtons[16];static int  _MenuButtons_Count = 0;
static XeDWidget  _ToolBarButtons[16];static int  _ToolBarButtons_Count = 0;


static EBool    _TransformCompensation = TRUE;


#define E3dConnectCONNECT        0x01
#define E3dConnectREPARENT       0x02
#define E3dConnectINSTANTIATEAS  0x04
static uint8_t  _ConnectMode = 0;


enum
{
	CR_TOGGLE_HRC_COMPENSATION = XeCR_CUSTOM0,
};


enum
{
	E3dBA_PICK_CHILD_NODE_FOR_NODE = 1,
	E3dBA_PICK_PARENT_NODE_FOR_NODES,
	E3dBA_PICK_PARENT_MODEL_FOR_OBJECTS,
	E3dBA_PICK_CHILD_OBJECT_FOR_MODELS,
	E3dBA_PICK_NODE_TO_DISCONNECT_OBJECTS_FROM,
	E3dBA_PICK_OBJECT_FOR_ADDOBJECTTO_MODELSOF
};


static const char* _GetToolName(const int PConnectMode, const EIndex PSelectedNodes_Count, const EIndex PSelectedObjects_Count,  const char** PToolBaseNameP, EBool* PAcceptModelsP)
{
	switch(PConnectMode)
	{
		case E3dConnectCONNECT:
			*PToolBaseNameP = _Msg_ConnectStr;
			if(PAcceptModelsP) *PAcceptModelsP = TRUE;
			if(PSelectedNodes_Count == 0)
			{
				if(PSelectedObjects_Count == 1) return(_Msg_ConnectObjectStr);
				else if(PSelectedObjects_Count > 1) return(_Msg_ConnectObjectsStr);
			}
			else if(PSelectedNodes_Count == 1) return(_Msg_ConnectModelStr);
			else if(PSelectedNodes_Count > 1) return(_Msg_ConnectModelsStr);
		return(NULL);

		case E3dConnectREPARENT:
			*PToolBaseNameP = _Msg_ReparentStr;
			if(PAcceptModelsP) *PAcceptModelsP = TRUE;
			if(PSelectedNodes_Count == 0)
			{
				if(PSelectedObjects_Count == 1) return(_Msg_ReparentObjectStr);
				else if(PSelectedObjects_Count > 1) return(_Msg_ReparentObjectsStr);
			}
			else if(PSelectedNodes_Count == 1) return(_Msg_ReparentModelStr);
			else if(PSelectedNodes_Count > 1) return(_Msg_ReparentModelsStr);
		return(_Msg_ReparentStr);

		case E3dConnectINSTANTIATEAS:
			*PToolBaseNameP = _Msg_InstantiateAsStr;
			if(PAcceptModelsP) *PAcceptModelsP = FALSE;
		return(_Msg_InstantiateAsStr);

		default:
			*PToolBaseNameP = NULL;
			if(PAcceptModelsP) *PAcceptModelsP = TRUE;
		return(NULL);
	}
}



static XeString _ModelPickParent(const char* PToolName, int* PButtonActions)
{
	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
	const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

	 XeString  LXeString = XeString_NewVa(TRUE,
		XeFONT, XeFontDEFAULT,  XeCOLOR_RGBA, LLabelRGBAp8Color,  XeTEXT, PToolName,  XeTEXT, " - ",
		E3dp_MMouseButtonS,  XeTEXT, ": pick parent Model  ",
		XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
		XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
		0);
	PButtonActions[1] = E3dBA_PICK_PARENT_NODE_FOR_NODES;
	return(LXeString);
}


static XeString _ModelPickChild(const char* PToolName, int* PButtonActions)
{
	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
	const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

	XeString  LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, PToolName,  XeTEXT, " - ",
												 E3dp_LMouseButtonS,  XeTEXT, ": pick child Model  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
	PButtonActions[0] = E3dBA_PICK_CHILD_NODE_FOR_NODE;
	return(LXeString);
}


static XeString _ModelPickChildOrParent(const char* PToolName, int* PButtonActions)
{
	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
	const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

	XeString  LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, PToolName,  XeTEXT, " - ",
												 E3dp_LMouseButtonS,  XeTEXT, ": pick child Model  ",
												 E3dp_MMouseButtonS,  XeTEXT, ": pick parent Model  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
	PButtonActions[0] = E3dBA_PICK_CHILD_NODE_FOR_NODE;
	PButtonActions[1] = E3dBA_PICK_PARENT_NODE_FOR_NODES;
	return(LXeString);
}


static XeString _InstantiateAsObjectPicked(const char* PToolName, const E3dItem* PPickedItems,  E3dObjectPtrListA* PSelectedObjects,  int* PButtonActions)
{
	const E3dObjItem*  LObjItem = (const E3dObjItem*)PPickedItems;
	XeString           LXeString = NULL;
	E3dObject*         LObject = LObjItem->Object;
	const E3dObject*   LSelectedObject = PSelectedObjects->Items[0];

	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
	const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

	switch(PSelectedObjects->Count)
	{
		case 0:
			LXeString = XeString_NewVa(TRUE,
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, PToolName,  XeTEXT, " - Move pointer over an Object ",
			 XeFONT, XeFontFIXEDWIDTH, XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
			 0);
		break;

		case 1:
			{
				LXeString = XeString_NewVa(TRUE,
				 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, PToolName, XeTEXT, " - ",
				 E3dp_LMouseButtonS, XeTEXT, " Add selected Object ",  E3dp_ObjectS(LSelectedObject->Name),  XeTEXT, " to all Models of ",  E3dp_ObjectS(LObject->Name),  XeTEXT, "  ",
				 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
				 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
				 0);
				PButtonActions[0] = E3dBA_PICK_OBJECT_FOR_ADDOBJECTTO_MODELSOF;
			}
		break;

		default:
			{
				LXeString = XeString_NewVa(TRUE,
				 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, PToolName, XeTEXT, " - ",
				 E3dp_LMouseButtonS,  XeTEXT, " Add selected Objects to all Models of ",  E3dp_ObjectS(LObject->Name), XeTEXT, "  ",
				 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
				 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
				 0);
				PButtonActions[0] = E3dBA_PICK_OBJECT_FOR_ADDOBJECTTO_MODELSOF;
			}
		break;
	}

	return(LXeString);
}


//======================================================================================
// Get actions and status-panel strint for SCHEMATICS view
//======================================================================================
static EChangeFlags _ConnectGetButtonActionsSchematicsView(const E3dItem* PPickedItems,  const int PNumOfPickedItems, E3dNodePtrListA* PSelectedNodes,  E3dObjectPtrListA* PSelectedObjects,  int* PButtonActions, XeString* PXeStringRet)
{
	const E3dObjItem*   LObjItem = (const E3dObjItem*)PPickedItems;
	XeString            LXeString = NULL;
	EChangeFlags        LChanged = 0;
	EBool               LAcceptModels = FALSE;
	const char*         LToolBaseName;const char*  LToolName = _GetToolName(_ConnectMode, PSelectedNodes->Count, PSelectedObjects->Count,  &LToolBaseName, &LAcceptModels);

	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	const uint32_t    LLabelRGBAp8Color = Xe_ForegroundRGBA8pColor,
	                  LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

	if(PNumOfPickedItems)
	{
		switch(LObjItem->Type)
		{
			case E3dItemMODEL:
				if(LAcceptModels)
				{
					E3dNode*   LPickedNode = LObjItem->Node;
					E3dModel*  LPickedModel = E_DynamicCast(LPickedNode, E3dModel);if(LPickedModel)
					{
						switch(PSelectedNodes->Count)
						{
							case 0:
								if(PSelectedObjects->Count == 1)
								{
									const E3dObject*  LSelectedObject = PSelectedObjects->Items[0];
									if(E3dModel_ObjectGetIndex(LPickedModel, PSelectedObjects->Items[0]) > -1)
									{
										LXeString = XeString_NewVa(TRUE,
										 XeFONT, XeFontDEFAULT,                                              XeTEXT, LToolName,  XeTEXT, " - The selected Object ",
										 E3dp_ObjectS(LSelectedObject->Name ? LSelectedObject->Name : ""), " is already on this Model ",
										 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,     XeTEXT, "Esc",
										 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,          XeTEXT, ": cancel",
										 0);
									}
									else
									{
										LXeString = XeString_NewVa(TRUE,
										 XeFONT, XeFontDEFAULT,                                            XeTEXT, LToolName,  XeTEXT, " - ",
										 E3dp_LMouseButtonS,  XeTEXT, ": add selected Object ",  E3dp_ObjectS(LSelectedObject->Name ? LSelectedObject->Name : ""),
										                                                                   XeTEXT, " to this Model  ",
										 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
										 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
										 0);
										PButtonActions[0] = E3dBA_PICK_PARENT_MODEL_FOR_OBJECTS;
									}
								}
								else
								{
									LXeString = XeString_NewVa(TRUE,
									 XeFONT, XeFontDEFAULT,                                            XeTEXT, LToolName,  XeTEXT, " - ",
									 E3dp_LMouseButtonS,  XeFTEXT, ": add %lld selected Objects to this Model  ", (long long)PSelectedObjects->Count,
									 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
									 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
									 0);
									PButtonActions[0] = E3dBA_PICK_PARENT_MODEL_FOR_OBJECTS;
								}
							break;

							case 1:  // One Model is selected
								{
									E3dNode*  LSelectedNode = PSelectedNodes->Items[0];
									if(LSelectedNode == LPickedNode)
									{
										LXeString = XeString_NewVa(TRUE,
										 XeFONT, XeFontDEFAULT,                                           XeTEXT, LToolName,  XeTEXT, " - Pick a different Model   ",
										 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
										 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
										 0);
									}
									else
									{
										if(LPickedNode == LSelectedNode->Parent)
										{
											LXeString = XeString_NewVa(TRUE,
											 XeFONT, XeFontDEFAULT,                                           XeTEXT, LToolName,  XeTEXT, " - This Model is already the parent of the selected Model ",
											                           E3dp_NodeS(LSelectedNode->Name ? LSelectedNode->Name : ""), XeTEXT, "  ",
											 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
											 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
											 0);
										}
										else
										{
											const EIndex  LConnectionLevel1 = E3dNode_HrcIsDescendantOf(LPickedNode, LSelectedNode);
											if(LConnectionLevel1 != E_NOT_CONNECTED)
											{
												if(LPickedNode->Parent == LSelectedNode)
												{
													LXeString = XeString_NewVa(TRUE,
													 XeFONT, XeFontDEFAULT,                                            XeTEXT, LToolName,  XeTEXT, " - This Model is already the child of the selected Model ",
													                           E3dp_NodeS(LSelectedNode->Name ? LSelectedNode->Name : ""), XeTEXT, "  ",
													 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,   XeTEXT, "Esc",
													 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,        XeTEXT, ": cancel",
													 0);
												}
												else
												{
													LXeString = _ModelPickChild(LToolName, PButtonActions);
												}
											}
											else
											{
												const EIndex  LConnectionLevel2 = E3dNode_HrcIsDescendantOf(LSelectedNode, LPickedNode);
												if(LConnectionLevel2 >= 2)
												{
													LXeString = _ModelPickParent(LToolName, PButtonActions);
												}
												else
												{
													if(LPickedNode->Parent == NULL && LSelectedNode->Parent == NULL)
													{
														LXeString = _ModelPickChildOrParent(LToolName, PButtonActions);
													}
													else if(LSelectedNode->Parent == NULL)
													{
														LXeString = _ModelPickChildOrParent(LToolName, PButtonActions);
													}
													else if(LPickedNode->Parent == NULL)
													{
														LXeString = _ModelPickChildOrParent(LToolName, PButtonActions);
													}
													else
													{
														LXeString = _ModelPickChildOrParent(LToolName, PButtonActions);
													}
												}
											}
										}
									}
								}
							break;

							default:  // Multiple Models are selected
								{
									EListAI_s(, E3dNodePtr, LSelectedBranches);E3dNodeHrc_CollectIndependentBranches(PSelectedNodes,  &LSelectedBranches);if(LSelectedBranches.Count > 0)
									{
										if(LSelectedBranches.Count == 1)
										{
											const E3dNode*  LSelectedBranch = LSelectedBranches.Items[0];
											if(E3dNode_HrcIsDescendantOf(LPickedNode, LSelectedBranch) == E_NOT_CONNECTED)
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,                                           XeTEXT, LToolBaseName,  XeTEXT, " - ",
												 E3dp_MMouseButtonS,  XeTEXT, ": make this Model ",  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),
												                           XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, " the new parent of the selected branch ",
												                           E3dp_NodeS(LSelectedBranch->Name ? LSelectedBranch->Name : ""),  XeTEXT, "  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
												PButtonActions[1] = E3dBA_PICK_PARENT_NODE_FOR_NODES;
											}
											else
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,                                            XeTEXT, LToolName,  XeTEXT, " - ",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "This Model is in the selected branch  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
											}
										}
										else if(LSelectedBranches.Count > 1)
										{
											EBool  LPickedNodeValid = TRUE;

											{
												EIndex  LBI = 0;const EIndex  LBN = LSelectedBranches.Count;for(;LBI < LBN;LBI++)
												{
													const E3dNode*  LSelectedBranch = LSelectedBranches.Items[LBI];const EIndex  LDescendantLevel = E3dNode_HrcIsDescendantOf(LPickedNode, LSelectedBranch);if(LDescendantLevel != E_NOT_CONNECTED)
													{
														if(LDescendantLevel == 0)
														{
															LXeString = XeString_NewVa(TRUE,
															 XeFONT, XeFontDEFAULT,                                            XeTEXT, LToolName,  XeTEXT, " - ",
															 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,        XeTEXT, "This Model ", 
															                           E3dp_NodeS(LSelectedBranch->Name ? LSelectedBranch->Name : ""), 
															                           XeCOLOR_RGBA, LLabelRGBAp8Color,        XeTEXT, " is one of the selected Models  ",
															 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,   XeTEXT, "Esc",
															 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,        XeTEXT, ": cancel",
															 0);
														}
														else
														{
															LXeString = XeString_NewVa(TRUE,
															 XeFONT, XeFontDEFAULT,                                           XeTEXT, LToolName,  XeTEXT, " - ",
															 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "This Model is inside a selected branch ",
															                           E3dp_NodeS(LSelectedBranch->Name ? LSelectedBranch->Name : ""),  XeTEXT, "  ",
															 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
															 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
															 0);
														}
														LPickedNodeValid = FALSE;
														break;
													}
												}
											}

											if(LPickedNodeValid)
											{
												EBool  LPassFound = FALSE;EIndex  LBI = 0;const EIndex  LBN = LSelectedBranches.Count;for(;LBI < LBN;LBI++)
												{
													const E3dNode*  LSelectedBranch = LSelectedBranches.Items[LBI];const EIndex  LDescendantLevel = E3dNode_HrcIsDescendantOf(LSelectedBranch, LPickedNode);if(LDescendantLevel == E_NOT_CONNECTED || LDescendantLevel > 1)
													{
														LPassFound = TRUE;
														break;
													}
												}
												if(!LPassFound) LPickedNodeValid = FALSE;
											}

											if(LPickedNodeValid)
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,                                           XeTEXT, LToolName,  XeTEXT, " - ",
												 E3dp_MMouseButtonS,  XeTEXT, ": pick new parent Model for selected Models  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
												PButtonActions[1] = E3dBA_PICK_PARENT_NODE_FOR_NODES;
											}
										}

										EList_FreeA_s(LSelectedBranches);
									}
								}
							break;
						}
						if(PButtonActions[0] || PButtonActions[1] || PButtonActions[2]) { LPickedModel->Highlighted = TRUE;LChanged |= E3dChgSELECTION; }
					}
				}
			break;

			default:
				{
					E3dObject*  LObject = LObjItem->Object;

					if(_ConnectMode == E3dConnectINSTANTIATEAS) LXeString = _InstantiateAsObjectPicked(LToolName, PPickedItems,  PSelectedObjects, PButtonActions);
					else
					{
						if(PSelectedObjects->Count > 0) LXeString = _InstantiateAsObjectPicked(LToolName, PPickedItems,  PSelectedObjects, PButtonActions);
						else
						{
							switch(PSelectedNodes->Count)
							{
								case 0:
									LXeString = XeString_NewVa(TRUE,
									 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - Move pointer over a Model ",
									 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
									 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
									 0);
								break;

								case 1:
									{
										E3dNode*   LNode = PSelectedNodes->Items[0];
										E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
										{
											if(E3dModel_ObjectGetIndex(LModel, LObjItem->Object) > -1)
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - This Object is already on the selected Model ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
											}
											else
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
												 E3dp_LMouseButtonS,  XeTEXT, ": add this Object to the selected Model  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
												PButtonActions[0] = E3dBA_PICK_CHILD_OBJECT_FOR_MODELS;
											}
										}
									}
								break;

								default:
									LXeString = XeString_NewVa(TRUE,
									 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
									 E3dp_LMouseButtonS,  XeTEXT, ": add this Object to the selected Models  ",
									 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
									 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
									 0);
									PButtonActions[0] = E3dBA_PICK_CHILD_OBJECT_FOR_MODELS;
								break;
							}
						}
					}
					if(PButtonActions[0] || PButtonActions[1] || PButtonActions[2]) { LObject->Highlighted = TRUE;LChanged |= E3dChgSELECTION; }
				break;
			}
		}
	}
	else
	{
		switch(PSelectedNodes->Count)
		{
			case 0:
				if(PSelectedObjects->Count == 1)
				{
					LXeString = XeString_NewVa(TRUE,
					 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
					 E3dp_LMouseButtonS,  XeTEXT, ": pick parent Model for selected Object  ",
					 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
					 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
					 0);
				}
				else if(PSelectedObjects->Count > 1)
				{
					LXeString = XeString_NewVa(TRUE,
					 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
					 E3dp_LMouseButtonS,  XeTEXT, ": pick parent Model for selected Objects  ",
					 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
					 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
					 0);
				}
			break;

			default:
				LXeString = XeString_NewVa(TRUE,
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - Move pointer over a Model  ",
				 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
				 0);
			break;
		}
	}


	*PXeStringRet = LXeString;

	return(LChanged);
}


//======================================================================================
// Get actions and status-panel strint for PERSPECTIVE, TOP, FRONT and RIGHT views
//======================================================================================
static EChangeFlags _ConnectGetButtonActions3DView(E3dItem* PPickedItems, int PNumOfPickedItems, E3dNodePtrListA* PSelectedNodes, E3dObjectPtrListA* PSelectedObjects, int* PButtonActions, XeString* PXeStringRet)
{
	E3dObjItem*   LObjItem = (E3dObjItem*)PPickedItems;
	XeString      LXeString = NULL;
	EChangeFlags  LChanged = 0;
	EBool         LAcceptModels = FALSE;
	const char*   LToolBaseName;const char*   LToolName = _GetToolName(_ConnectMode, PSelectedNodes->Count, PSelectedObjects->Count,  &LToolBaseName, &LAcceptModels);

	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
	const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

// On these views, we'll always get a detailed pick (e.g. Model->Mesh->PolyGroup)
//
	if(PNumOfPickedItems)
	{
		switch(LObjItem->Type)
		{
			case E3dItemMODEL:
			case E3dItemPOLYGON:
			case E3dItemSPLINE:
			case E3dItemFACE:
				{
					E3dNode*   LPickedNode = LObjItem->Node;
					E3dModel*  LPickedModel = E_DynamicCast(LPickedNode, E3dModel);if(LPickedModel)
					{
						switch(PSelectedNodes->Count)
						{
							case 0:
								if(PSelectedObjects->Count == 1)
								{
									if(E3dModel_ObjectGetIndex(LPickedModel, PSelectedObjects->Items[0]) > -1)
									{
										LXeString = XeString_NewVa(TRUE,
										 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - The selected Object is already on this Model  ",
										 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
										 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
										 0);
									}
									else
									{
										LXeString = XeString_NewVa(TRUE,
										 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
										 E3dp_LMouseButtonS,  XeTEXT, ": pick parent Model for Object  ",
										 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
										 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
										 0);
										PButtonActions[0] = E3dBA_PICK_PARENT_MODEL_FOR_OBJECTS;
									}
								}
								else
								{
									LXeString = XeString_NewVa(TRUE,
									 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
									 E3dp_LMouseButtonS,  XeTEXT, ": pick parent Model for Objects  ",
									 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
									 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
									 0);
									PButtonActions[0] = E3dBA_PICK_PARENT_MODEL_FOR_OBJECTS;
								}
							break;

							case 1:
								{
									E3dNode*    LSelectedNode = PSelectedNodes->Items[0];E3dModel*  LSelectedModel = E_DynamicCast(LSelectedNode, E3dModel);
									E3dObject*  LPickedObject = LObjItem->Object;
									if(LSelectedNode == LPickedNode)
									{
										LXeString = XeString_NewVa(TRUE,
										 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - Pick a different Model or Object   ",
										 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
										 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
										 0);
									}
									else if(LPickedNode == LSelectedNode->Parent)
									{
										if(E3dModel_ObjectGetIndex(LSelectedModel, LPickedObject) > -1)
										{
											LXeString = XeString_NewVa(TRUE,
											 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - This Model ",
											  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),
											                                                                   XeTEXT, " is already the parent of selected Model ",
											  E3dp_NodeS(LSelectedNode->Name ? LSelectedNode->Name : ""),
											                                                                   XeTEXT, " and this Object ",
											  E3dp_ObjectS(LPickedObject->Name ? LPickedObject->Name : ""),
											                                                                   XeTEXT, " is on the selected Model  ",
											 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
											 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
											 0);
										}
										else
										{
											LXeString = XeString_NewVa(TRUE,
											 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,         XeTEXT, LToolName,  XeTEXT, " - ",
											 E3dp_LMouseButtonS,  XeTEXT, ": add Object ",  E3dp_ObjectS(LPickedObject->Name ? LPickedObject->Name : ""),  XeTEXT, " to selected Model ",
											                   E3dp_NodeS(LSelectedNode->Name ? LSelectedNode->Name : ""),
											 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,   XeTEXT, "   Esc",
											 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,        XeTEXT, ": cancel",
											 0);
											PButtonActions[0] = E3dBA_PICK_PARENT_MODEL_FOR_OBJECTS;
										}
									}
									else if(LSelectedNode == LPickedNode->Parent)
									{
										if(E3dModel_ObjectGetIndex(LSelectedModel, LPickedObject) > -1)
										{
											LXeString = XeString_NewVa(TRUE,
											 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,
											                                                                   XeTEXT, " - This Model ",  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),
											                                                                   XeTEXT, " is a child of selected Model ",  E3dp_NodeS(LSelectedNode->Name ? LSelectedNode->Name : ""),
											                                                                   XeTEXT, " and this Object ",E3dp_ObjectS(LPickedObject->Name ? LPickedObject->Name : ""),
											                                                                   XeTEXT, " is on the selected Model  ",
											 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
											 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
											 0);
										}
										else
										{
											LXeString = XeString_NewVa(TRUE,
											 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
											 E3dp_LMouseButtonS,  XeTEXT, ": add this Object ",  E3dp_ObjectS(LPickedObject->Name ? LPickedObject->Name : ""),  XeTEXT, " to the selected Model ",  E3dp_NodeS(LSelectedNode->Name ? LSelectedNode->Name : ""),
											 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "  Esc",
											 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
											 0);
											PButtonActions[0] = E3dBA_PICK_CHILD_OBJECT_FOR_MODELS;
										}
									}
									else
									{
										const EIndex  LConnectionLevel1 = E3dNode_HrcIsDescendantOf(LPickedNode, LSelectedNode);

										if(LConnectionLevel1 > 1)
										{
											if(E3dModel_ObjectGetIndex(LSelectedModel, LPickedObject) > -1)
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,
												                                                                   XeTEXT, " - This Model ",  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),
												                                                                   XeTEXT, " is a descendant of selected Model ",  E3dp_NodeS(LSelectedNode->Name ? LSelectedNode->Name : ""),
												                                                                   XeTEXT, " and this Object ",  E3dp_ObjectS(LPickedObject->Name ? LPickedObject->Name : ""),
												                                                                   XeTEXT, " is on the selected Model",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "  Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
											}
											else
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,        XeTEXT, LToolName,
												                                                                   XeTEXT, " - This Model ",  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),
												                                                                   XeTEXT, " is a descendant of selected Model ",  E3dp_NodeS(LSelectedNode->Name ? LSelectedNode->Name : ""),
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "  Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
											}
										}
										else
										{
											if(LPickedNode->Parent == NULL && LSelectedNode->Parent == NULL)
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
												 E3dp_LMouseButtonS,  XeTEXT, ": pick child Model  ",
												 E3dp_MMouseButtonS,  XeTEXT, ": pick parent Model  ",
												 E3dp_RMouseButtonS,  XeTEXT, ": pick child Object  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
												PButtonActions[0] = E3dBA_PICK_CHILD_NODE_FOR_NODE;
												PButtonActions[1] = E3dBA_PICK_PARENT_NODE_FOR_NODES;
												PButtonActions[2] = E3dBA_PICK_CHILD_OBJECT_FOR_MODELS;
											}
											else if(LSelectedNode->Parent == NULL)
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
												 E3dp_MMouseButtonS,  XeTEXT, ": pick parent Model  ",
												 E3dp_RMouseButtonS,  XeTEXT, ": pick child Object  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
												PButtonActions[1] = E3dBA_PICK_PARENT_NODE_FOR_NODES;
												PButtonActions[2] = E3dBA_PICK_CHILD_OBJECT_FOR_MODELS;
											}
											else if(LPickedNode->Parent == NULL)
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
												 E3dp_LMouseButtonS,  XeTEXT, ": pick child Model  ",
												 E3dp_RMouseButtonS,  XeTEXT, ": pick child Object  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
												PButtonActions[0] = E3dBA_PICK_CHILD_NODE_FOR_NODE;
												PButtonActions[2] = E3dBA_PICK_CHILD_OBJECT_FOR_MODELS;
											}
											else
											{
												if(E3dModel_ObjectGetIndex(LSelectedModel, LObjItem->Object) > -1)
												{
													LXeString = XeString_NewVa(TRUE,
													 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - Selected Model ",  E3dp_NodeS(LSelectedModel->Name ? LSelectedModel->Name : ""),  XeTEXT, " and this Model are already in hierarchies ",
													 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
													 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
													 0);
												 }
												 else
												 {
printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
													 LXeString = XeString_NewVa(TRUE,
														XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
														E3dp_RMouseButtonS,  XeTEXT, ": pick child Object  ",
														XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
														XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
														0);
													PButtonActions[2] = E3dBA_PICK_CHILD_OBJECT_FOR_MODELS;
												}
											}
										}
									}
								}
							break;

							default:  // Multiple Models are selected
								{
									E3dObject*  LPickedObject = LObjItem->Object;

									EListAI_s(, E3dNodePtr, LSelectedBranches);E3dNodeHrc_CollectIndependentBranches(PSelectedNodes, &LSelectedBranches);if(LSelectedBranches.Count > 0)
									{
										if(LSelectedBranches.Count == 1)
										{
// A single branch is selected
//
											const E3dNode*  LSelectedBranch = LSelectedBranches.Items[0];
											if(E3dNode_HrcIsDescendantOf(LPickedNode, LSelectedBranch) == E_NOT_CONNECTED)
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolBaseName,  XeTEXT, " - ",
												 E3dp_MMouseButtonS,  XeTEXT, ": make this Model ",  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),   XeTEXT, " the parent of the selected branch ",  E3dp_NodeS(LSelectedBranch->Name ? LSelectedBranch->Name : ""),  XeCOLOR_RGBA, LLabelRGBAp8Color, XeTEXT, "  ",
												 E3dp_RMouseButtonS,  XeTEXT, ": add Object ",  E3dp_ObjectS(LPickedObject->Name ? LPickedObject->Name : ""),  XeTEXT, " to the seleceted Models  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
												PButtonActions[1] = E3dBA_PICK_PARENT_NODE_FOR_NODES;
												PButtonActions[2] = E3dBA_PICK_CHILD_OBJECT_FOR_MODELS;
											}
											else
											{
												LXeString = XeString_NewVa(TRUE,
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "This Model ",  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),  XeTEXT, " is in the selected branch ",  E3dp_NodeS(LSelectedBranch->Name ? LSelectedBranch->Name : ""),  XeTEXT, "  ",
												 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
												 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
												 0);
											}
										}
										else
										{
// Multiple branches are selected
//
											EBool  LPickedNodeValid = TRUE;

											{
												EIndex  LBI = 0;for(;LBI < LSelectedBranches.Count;LBI++)
												{
													const E3dNode*  LSelectedBranch = LSelectedBranches.Items[LBI];const EIndex  LDescendantLevel = E3dNode_HrcIsDescendantOf(LPickedNode, LSelectedBranch);if(LDescendantLevel != E_NOT_CONNECTED)
													{
														if(LDescendantLevel == 0)
														{
															LXeString = XeString_NewVa(TRUE,
															 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
															 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "This Model ",  E3dp_NodeS(LSelectedBranch->Name ? LSelectedBranch->Name : ""),
															                                                                   XeTEXT, " is one of the selected Models",
															 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "  Esc",
															 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
															 0);
														}
														else
														{
															LXeString = XeString_NewVa(TRUE,
															 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
															 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "This Model is inside selected branch ",  E3dp_NodeS(LSelectedBranch->Name ? LSelectedBranch->Name : ""),
															 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "  Esc",
															 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
															 0);
														}
														LPickedNodeValid = FALSE;
														break;
													}
												}
											}

											if(LPickedNodeValid)
											{
												EBool  LPassFound = FALSE;EIndex  LBI = 0;const EIndex  LBN = LSelectedBranches.Count;for(;LBI < LBN;LBI++)
												{
													const E3dNode*  LSelectedBranch = LSelectedBranches.Items[LBI];const EIndex  LDescendantLevel = E3dNode_HrcIsDescendantOf(LSelectedBranch, LPickedNode);if(LDescendantLevel == E_NOT_CONNECTED || LDescendantLevel > 1)
													{
														LPassFound = TRUE;
														break;
													}
												}
												if(!LPassFound) LPickedNodeValid = FALSE;
											}

											EBool  LPickedObjectValid = FALSE;

											{
												EIndex  LBI = 0;const EIndex  LBN = LSelectedBranches.Count;for(;LBI < LBN;LBI++)
												{
													const E3dNode*  LSelectedBranch = LSelectedBranches.Items[LBI];const E3dNode*  LNodeAfterBranch = E3dNode_BranchGetNodeAfter(LSelectedBranch);const E3dNode*  LNode = LSelectedBranch;for(;LNode != LNodeAfterBranch;LNode = LNode->Next)
													{
														if(LNode->Selection)
														{
															E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
															{
																if(EList_PointerGetIndex_s(LModel->Objects, LPickedObject) == E_NOT_FOUND)
																{
																	LPickedObjectValid = TRUE;
																	break;
																}
															}
														}
													}
												}
											}

											if(LPickedNodeValid || LPickedObjectValid)
											{
												if(LPickedNodeValid && LPickedObjectValid)
												{
													LXeString = XeString_NewVa(TRUE,
													 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
													 E3dp_MMouseButtonS, XeTEXT, ": make Model ",  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),  XeTEXT, " the new parent of the selected Models  ",
													 E3dp_MMouseButtonS, XeTEXT, ": add Object ",  E3dp_ObjectS(LPickedObject->Name ? LPickedObject->Name : ""), XeTEXT, " to the selected Models",
													 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "  Esc",
													 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
													 0);
													PButtonActions[1] = E3dBA_PICK_PARENT_NODE_FOR_NODES;
													PButtonActions[2] = E3dBA_PICK_CHILD_OBJECT_FOR_MODELS;
												}
												else if(LPickedNodeValid)
												{
													LXeString = XeString_NewVa(TRUE,
													 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
													 E3dp_MMouseButtonS,  XeTEXT, ": make Model ",  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),  XeTEXT, " the new parent of the selected Models  ",
													 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
													 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
													 0);
													PButtonActions[1] = E3dBA_PICK_PARENT_NODE_FOR_NODES;
												}
												else if(LPickedObjectValid)
												{
													LXeString = XeString_NewVa(TRUE,
													 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
													 E3dp_MMouseButtonS,  XeTEXT, ": add Object ",  E3dp_ObjectS(LPickedObject->Name ? LPickedObject->Name : ""),  XeTEXT, " to the selected Models",
													 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "  Esc",
													 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
													 0);
													PButtonActions[2] = E3dBA_PICK_CHILD_OBJECT_FOR_MODELS;
												}
											}
										}

										EList_FreeA_s(LSelectedBranches);
									}
								}
							break;
						}
						if(PButtonActions[0] || PButtonActions[1] || PButtonActions[2]) { LPickedModel->Highlighted = TRUE;LChanged |= E3dChgSELECTION; }
					}
				break;
			}
		}
	}
	else
	{
		switch(PSelectedNodes->Count)
		{
			case 0:
				if(PSelectedObjects->Count == 1)
				{
					LXeString = XeString_NewVa(TRUE,
					 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
					 E3dp_LMouseButtonS,  XeTEXT, ": pick parent Model for selected Object  ",
					 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
					 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
					 0);
				}
				else if(PSelectedObjects->Count > 1)
				{
					LXeString = XeString_NewVa(TRUE,
					 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - ",
					 E3dp_LMouseButtonS,  XeTEXT, ": pick parent Model for selected Objects  ",
					 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
					 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
					 0);
				}
			break;

			default:
				LXeString = XeString_NewVa(TRUE,
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - Move pointer over a Model  ",
				 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
				 0);
			break;
		}
	}


	*PXeStringRet = LXeString;

	return(LChanged);
}


//================================================================
// Add a child Node to a given Node
//================================================================
static void _NodeHrcConnectChild(E3dNode* PParent, E3dNode* PChild, const EIndex PWhere, const EBool PCompensateTransform, const EBool PUpdate)
{
	if(PChild->Parent == NULL)
	{
		E3dScene*  LScene = E3d_Scene;

// Remove this Node / Branch from the Scene as an individual hierarchy, but don't free its RenderData etc.
// That is done in E3dScene_NodeHrcRemove()
//
		E_Object(E3dNode, PChild, LTNode);  // Make sure ObjectRemoveA doesn't free LChild
		ELst_ObjectRemoveA_s_om(LScene,RootNodes, PChild);

		E3dNode_ConnectChild(PParent, PChild, PWhere, PCompensateTransform, PUpdate);
		E_DeleteChk(LTNode);
	}
}


//========================================
// Initialize EditHierarchy Operator
//========================================
static void _EditHierarchy_Init(EOperator* POperator)
{
	E3dOperatorEditHierarchy*  LOperator = (E3dOperatorEditHierarchy*)POperator;

	EList_InitA_s(LOperator->ObjectMoves);
	EList_InitA_s(LOperator->BranchMoves);
}


//========================================
// Free ObjectsEdit Operator
//========================================
static int _EditHierarchy_Free(EOperator* POperator)
{
	E3dOperatorEditHierarchy*  LOperator = (E3dOperatorEditHierarchy*)POperator;

	if(LOperator->ObjectMoves.Items)
	{
		EIndex  LI = 0;const EIndex  LN = LOperator->ObjectMoves.Count;E3dObjectMove*  LObjectMove = LOperator->ObjectMoves.Items;for(;LI < LN;LI++, LObjectMove++)
		{
			if(LObjectMove->AddToModels.Items)
			{
				EIndex  LMI = 0;const EIndex  LMN = LObjectMove->AddToModels.Count;EObjectAndIndex*  LObjecAndIndex = LObjectMove->AddToModels.Items;for(;LMI < LMN;LMI++, LObjecAndIndex++)
				{
					E_DeleteChk(LObjecAndIndex->Object);
				}

				EList_FreeA_s(LObjectMove->AddToModels);
			}

			if(LObjectMove->DelFrModels.Items)
			{
				EIndex  LMI = 0;const EIndex  LMN = LObjectMove->DelFrModels.Count;EObjectAndIndex*  LObjecAndIndex = LObjectMove->DelFrModels.Items;for(;LMI < LMN;LMI++, LObjecAndIndex++)
				{
					E_DeleteChk(LObjecAndIndex->Object);
				}

				EList_FreeA_s(LObjectMove->DelFrModels);
			}
			E_DeleteChk(LObjectMove->Object);
		}

		EList_FreeA_s(LOperator->ObjectMoves);
	}

	if(LOperator->BranchMoves.Items)
	{
		EIndex  LI = 0;const EIndex  LN = LOperator->BranchMoves.Count;E3dBranchMove*  LBranchMove = LOperator->BranchMoves.Items;for(;LI < LN;LI++, LBranchMove++)
		{
			E_DeleteChk(LBranchMove->Node);
			E_DeleteChk(LBranchMove->OldParent);
			E_DeleteChk(LBranchMove->NewParent);
		}

		EList_FreeA_s(LOperator->BranchMoves);
	}


	return(E_SUCCESS);
}


//================================================
// EditObjects Operator proc
//================================================
static EChangeFlags _EditHierarchy_Func(EOps* POps, EOperator* POperator, const int POperation)
{
	E3dScene*                  LScene = E3d_Scene;
	E3dOperatorEditHierarchy*  LOperator = (E3dOperatorEditHierarchy*)POperator;
	EChangeFlags               LChanged = 0;
	EListAI(, E3dNode*, LMaterialInheritanceChangedBranches);
	EListAI(, E3dNode*, LHrcChangedBranches);

	switch(POperation)
	{
		case EOpDO:
			{
				if(LOperator->ObjectMoves.Items)
				{
					EIndex  LI = 0;const EIndex  LN = LOperator->ObjectMoves.Count;E3dObjectMove*  LObjectMove = LOperator->ObjectMoves.Items;for(;LI < LN;LI++, LObjectMove++)
					{
						E3dObject*  LObject = LObjectMove->Object;

//printf("%s() %s:%d <%s>\n", __FUNCTION__, __FILE__, __LINE__, LObject->Name);fflush(stdout);
						{
							EIndex  LMI = 0;const EIndex  LMN = LObjectMove->AddToModels.Count;EObjectAndIndex*  LAddToModel = LObjectMove->AddToModels.Items;for(;LMI < LMN;LMI++, LAddToModel++)
							{
//printf("%s() %s:%d  Add [%s]\n", __FUNCTION__, __FILE__, __LINE__, LAddToModel->Object->Name);fflush(stdout);
								E3dModel*  LModel = (E3dModel*)LAddToModel->Object;
								if(E3dModel_ObjectAppend(LModel, LObject, FALSE)) LChanged |= (E3dChgHIERARCHY|E3dObject_GetChangeFlagsByClass(LObject));

								E3dNode*  LRootNode = (E3dNode*)LModel;while(LRootNode->Parent) LRootNode = LRootNode->Parent;
								EList_PointerAddAChk(LMaterialInheritanceChangedBranches, 8, LRootNode);
							}
						}

						{
//printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
							EIndex  LMI = 0;const EIndex  LMN = LObjectMove->DelFrModels.Count;EObjectAndIndex*  LDelFrModel = LObjectMove->DelFrModels.Items;for(;LMI < LMN;LMI++, LDelFrModel++)
							{
//printf("%s() %s:%d  Del [%s]\n", __FUNCTION__, __FILE__, __LINE__, LDelFrModel->Object->Name);fflush(stdout);
								E3dModel*  LModel = (E3dModel*)LDelFrModel->Object;
								E3dModel_ObjectRemove(LModel, LObject);
								LChanged |= (E3dChgHIERARCHY|E3dObject_GetChangeFlagsByClass(LObject));

								E3dNode*  LRootNode = (E3dNode*)LModel;while(LRootNode->Parent) LRootNode = LRootNode->Parent;
								EList_PointerAddAChk(LMaterialInheritanceChangedBranches, 8, LRootNode);
							}
						}
					}
				}

				if(LOperator->BranchMoves.Items)
				{
					E3dNode**  LRoots;ENewNClr(LOperator->BranchMoves.Count, LRoots);

					{
						EIndex  LBI = 0;const EIndex  LBN = LOperator->BranchMoves.Count;E3dBranchMove*  LBranchMove = LOperator->BranchMoves.Items;for(;LBI < LBN;LBI++, LBranchMove++)
						{
							if(LBranchMove->NewParent == NULL)
							{
								E3dNode*  LRoot = LBranchMove->Node;while(LRoot->Parent) LRoot = LRoot->Parent;
								LRoots[LBI] = LRoot;
							}
						}
					}

					{
						EIndex  LBI = 0;const EIndex  LBN = LOperator->BranchMoves.Count;E3dBranchMove*  LBranchMove = LOperator->BranchMoves.Items;E3dNode*  LPrevRoot = NULL;EIndex  LSceneRootOffset = 0;for(;LBI < LBN;LBI++, LBranchMove++)
						{
							E3dNode*  LNode = LBranchMove->Node;
							E3dNode*  LTNode = NULL;E_Assign(LNode, LTNode);  // Temporary assignment, so LSelectedBranch doesn't get freed by E3dNode_DisconnectFromParent() (we don't add LSelectedBranch to the Scene...) or E3dScene_NodeHrcRemove()
							if(LNode->Parent)
							{
								E3dNode*  LRootNode = LNode->Parent;while(LRootNode->Parent) LRootNode = LRootNode->Parent;

								E3dNode_DisconnectFromParent(LNode, _TransformCompensation);

								E3dNodeHrc_UpdateConnections(LRootNode);
								E3dNodeHrc_UpdateMatrices(LRootNode, FALSE);
							}
							else
							{
								E3dScene_NodeHrcRemove(LScene, LNode, NULL, FALSE);
							}

							LChanged |= E3dNodeHrc_GetTransformChangeFlagsByObjects(LNode) | E3dChgHIERARCHY;

							if(LBranchMove->NewParent)
							{
								_NodeHrcConnectChild(LBranchMove->NewParent, LNode, ELstEND, _TransformCompensation, FALSE);
								E3dNode*  LRootNode = LBranchMove->NewParent;while(LRootNode->Parent) LRootNode = LRootNode->Parent;

								E3dNodeHrc_UpdateConnections(LRootNode);
								E3dNodeHrc_UpdateMatrices(LRootNode, FALSE);
								EList_PointerAddAChk(LHrcChangedBranches, 8, LRootNode);
							}
							else
							{
								E3dNode*  LRoot = LRoots[LBI];const EIndex  LRootIndex = EList_PointerGetIndex_s(LScene->RootNodes, LRoot) + 1;
								if(LRoot == LPrevRoot) LSceneRootOffset++;
								else LSceneRootOffset = 0;

								LPrevRoot = LRoot;

								E3dNodeHrc_UpdateConnections(LNode);
								E3dNodeHrc_UpdateMatrices(LNode, FALSE);
								EList_PointerAddAChk(LHrcChangedBranches, 8, LNode);
								E3dScene_NodeHrcInsert(LScene, LNode, LRootIndex + LSceneRootOffset, FALSE);
							}

							E_DeleteChk(LTNode);
						}
					}

					EFreeChk(LRoots);
				}
			}
		break;

		case EOpUNDO:
			{
				{
					EIndex  LI = 0;const EIndex  LN = LOperator->ObjectMoves.Count;E3dObjectMove*  LObjectMove = LOperator->ObjectMoves.Items;for(;LI < LN;LI++, LObjectMove++)
					{
						E3dObject*  LObject = LObjectMove->Object;

						{
							EIndex  LMI = 0;const EIndex  LMN = LObjectMove->DelFrModels.Count;EObjectAndIndex*  LDelFrModel = LObjectMove->DelFrModels.Items;for(;LMI < LMN;LMI++, LDelFrModel++)
							{
								E3dModel*  LModel = (E3dModel*)LDelFrModel->Object;
								if(E3dModel_ObjectAppend(LModel, LObject, FALSE)) LChanged |= (E3dChgHIERARCHY|E3dObject_GetChangeFlagsByClass(LObject));

								E3dNode*  LRootNode = (E3dNode*)LModel;while(LRootNode->Parent) LRootNode = LRootNode->Parent;
								EList_PointerAddAChk(LMaterialInheritanceChangedBranches, 8, LRootNode);
							}
						}

						{
							EIndex  LMI = 0;const EIndex  LMN = LObjectMove->AddToModels.Count;EObjectAndIndex*  LAddToModel = LObjectMove->AddToModels.Items;for(;LMI < LMN;LMI++, LAddToModel++)
							{
								E3dModel*  LModel = (E3dModel*)LAddToModel->Object;
								E3dModel_ObjectRemove(LModel, LObject);
								LChanged |= (E3dChgHIERARCHY|E3dObject_GetChangeFlagsByClass(LObject));

								E3dNode*  LRootNode = (E3dNode*)LModel;while(LRootNode->Parent) LRootNode = LRootNode->Parent;
								EList_PointerAddAChk(LMaterialInheritanceChangedBranches, 8, LRootNode);
							}
						}
					}
				}

				{
					EIndex  LBI = 0;const EIndex  LBN = LOperator->BranchMoves.Count;E3dBranchMove*  LBranchMove = LOperator->BranchMoves.Items;for(;LBI < LBN;LBI++, LBranchMove++)
					{
						E3dNode*  LNode = LBranchMove->Node;
						E3dNode*  LTNode = NULL;E_Assign(LNode, LTNode);  // Temporary assignment, so LSelectedBranch doesn't get freed by E3dNode_DisconnectFromParent() (we don't add LSelectedBranch to the Scene...) or E3dScene_NodeHrcRemove()
						if(LNode->Parent)
						{
							E3dNode_DisconnectFromParent(LNode, _TransformCompensation);
						}
						else E3dScene_NodeHrcRemove(LScene, LNode, NULL, FALSE);

						LChanged |= E3dNodeHrc_GetTransformChangeFlagsByObjects(LNode) | E3dChgHIERARCHY;

						if(LBranchMove->OldParent)
						{
							_NodeHrcConnectChild(LBranchMove->OldParent, LNode, LBranchMove->OldIndex, _TransformCompensation, FALSE);
						}
						else
						{
							E3dScene_NodeHrcInsert(LScene, LNode, LBranchMove->OldIndex, FALSE);
						}

						E_DeleteChk(LTNode);

						E3dNode*  LRootNode = LNode;while(LRootNode->Parent) LRootNode = LRootNode->Parent;
						E3dNodeHrc_UpdateConnections(LRootNode);
						E3dNodeHrc_UpdateMatrices(LRootNode, FALSE);
						EList_PointerAddAChk(LHrcChangedBranches, 8, LRootNode);
					}
				}
			}
		break;
	}


	if(LMaterialInheritanceChangedBranches)
	{
		EIndex  LBI = 0;for(;LBI < LMaterialInheritanceChangedBranches_Count;LBI++) E3dNodeHrc_UpdateMaterialInheritance(LMaterialInheritanceChangedBranches[LBI]);
	}

	if(LHrcChangedBranches)
	{
		EIndex  LBI = 0;for(;LBI < LHrcChangedBranches_Count;LBI++) E3dNodeHrc_CallNodeInfoUpdateProcs(LHrcChangedBranches[LBI], E3dChgHIERARCHY, FALSE);
	}

	if(LChanged)
	{
//								E3dNodeHrc_UpdateMaterialInheritance(LPickedNode);
	}

	EList_FreeAChk(LMaterialInheritanceChangedBranches);
	EList_FreeAChk(LHrcChangedBranches);

	return(LChanged);
}


//================================================================
// Connect pick callback
//================================================================
static void _PickCB_Connect(E3dPickCallbackStruct* PPickStruct, EPointer PClientData,  const EIndex PNumOfPickedItems, E3dItem* PPickedItems)
{
//printf("%s() %s:%d  %sGREEN%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);
	E3dScene*     LScene = E3d_Scene;
	E3dWindow*    L3DWindow = PPickStruct->E3DWindow;
	XeString      LXeString = NULL;
	EListAI_s(, E3dNodePtr, LSelectedNodes);E3dScene_GetSelectedNodes(LScene, E3dNodeSelNODE|E3dNodeSelBRANCH_ROOT, FALSE,  &LSelectedNodes);
	EListAI_s(, E3dObjectPtr, LSelectedObjects);E3dScene_GetObjects(LScene, E3dItemALL, E3dSELECTED|E3dEXCLUDE_MODEL_SELECTION, FALSE,  &LSelectedObjects);
	int           LButtonActions[3] = { 0, 0, 0 };
	EChangeFlags  LChanged = 0;

	if(E3dScene_UnhighlightAll(LScene, E3dSF_ALL)) LChanged = E3dChgSELECTION;

	if(PPickStruct->Reason == E3dCR_ABORT)
	{
		_ConnectMode = 0;

		EPluginLock_Del(_Plugin, &_ConnectPickLock, FALSE);
		return;
	}
//printf("%s() %s:%d  %sGREEN%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);

	if(LSelectedNodes.Count == 0 && LSelectedObjects.Count == 0) { E3dp_PrintMessage(ELogWARNING, 0, _Msg_SelectModelOrObjectToConnect);return; }
//printf("%s() %s:%d  %sGREEN%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);

	if(LSelectedNodes.Count > 0 && LSelectedObjects.Count > 0)
	{
		E3dp_PrintMessage(ELogWARNING, 0, _Msg_SelectOnlyModelOrObject);

		EList_FreeAChk_s(LSelectedNodes);
		EList_FreeAChk_s(LSelectedObjects);
		return;
	}
//printf("%s() %s:%d  %sGREEN%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);

	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
	const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

	const E3dViewClass*  LViewClass = L3DWindow->ViewMode->Class;if(LViewClass == E3dSchematicsView_class)
	{
		LChanged |= _ConnectGetButtonActionsSchematicsView(PPickedItems, PNumOfPickedItems,  &LSelectedNodes,  &LSelectedObjects,  LButtonActions, &LXeString);
//printf("%s() %d  %08x\n", __FUNCTION__, PNumOfPickedItems, LChanged);fflush(stdout);
	}
	else if(LViewClass == E3dTopView_class || LViewClass == E3dFrontView_class || LViewClass == E3dRightView_class || LViewClass == E3dPerspectiveView_class)
	{
		LChanged |= _ConnectGetButtonActions3DView(PPickedItems, PNumOfPickedItems,  &LSelectedNodes, &LSelectedObjects, LButtonActions,  &LXeString);
//printf("%s() %d  %08x\n", __FUNCTION__, PNumOfPickedItems, LChanged);fflush(stdout);
	}
	else
	{
		LXeString = XeString_NewVa(TRUE,
		 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Connect - Move pointer to a 3D or a schematics view  ",
		 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
		 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
		 0);
	}

	if(LXeString)
	{
		E3dp_PrintCompoundMessage(ELogMESSAGE, 0, LXeString);
		XeString_Free(LXeString);
	}

	if(LChanged == 0)
	{
		EList_FreeAChk_s(LSelectedNodes);
		EList_FreeAChk_s(LSelectedObjects);
		return;
	}

	if(PNumOfPickedItems > 0)
	{
		E3dObjItem*  LObjItem = (E3dObjItem*)PPickedItems;
		int          LAction = 0;

		switch(PPickStruct->Button)
		{
			case Button1:  LAction = LButtonActions[0];break;
			case Button2:  LAction = LButtonActions[1];break;
			case Button3:  LAction = LButtonActions[2];break;
		}

//printf("%s() %s:%d  %sGREEN%s  %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, LAction);fflush(stdout);
		switch(LAction)
		{
			case E3dBA_PICK_CHILD_NODE_FOR_NODE:
				{
					E3dOperatorEditHierarchy*  LOperator = (E3dOperatorEditHierarchy*)EOperator_New(E3dOperatorEditHierarchy_class);

					E3dNode*  LNewParent = LSelectedNodes.Items[0];
					E3dNode*  LChildNode = LObjItem->Node;

					E3dp_PrintMessage(ELogMESSAGE, 0, "Picked child: [%s]", LChildNode->Name);

					ENewNClr(1, LOperator->BranchMoves.Items);LOperator->BranchMoves.Count = LOperator->BranchMoves.CountAllocated = 1;E3dBranchMove*  LBranchMove = LOperator->BranchMoves.Items;
					E_AssignToMember(LChildNode, LBranchMove,Node);
					E_AssignToMember(LChildNode->Parent, LBranchMove,OldParent);
					if(LChildNode->Parent) LBranchMove->OldIndex = E3dNode_GetChildIndex(LChildNode);
					else LBranchMove->OldIndex = EList_PointerGetIndex_s(LScene->RootNodes, LChildNode);
					E_AssignToMember(LNewParent, LBranchMove,NewParent);

					E3dpUndoStack_AddOperator((EOperator*)LOperator, NULL);
					LChanged |= _EditHierarchy_Func(NULL, (EOperator*)LOperator, EOpDO);

					_ConnectMode = 0;
					E3dp_PickEnd();

					E3dSel_NodeSelectBranch(LScene, LNewParent, FALSE, TRUE);
				}
			break;

			case E3dBA_PICK_PARENT_NODE_FOR_NODES:
				{
					EListAI_s(, E3dNodePtr, LSelectedBranches);E3dNodeHrc_CollectIndependentBranches(&LSelectedNodes, &LSelectedBranches);if(LSelectedBranches.Count > 0)
					{
						E3dNode*  LPickedNode = LObjItem->Node;

						E3dp_PrintMessage(ELogMESSAGE, 3000, "Picked parent: [%s]", LPickedNode->Name);

						EIndex  LBranchesN = 0;

						{
							EIndex  LI = 0;const EIndex  LN = LSelectedBranches.Count;for(;LI < LN;LI++)
							{
								E3dNode*  LSelectedBranch = LSelectedBranches.Items[LI];
								const EIndex  LConnectionLevel = E3dNode_HrcIsDescendantOf(LSelectedBranch, LPickedNode);
								if(LConnectionLevel == E_NOT_CONNECTED || LConnectionLevel > 1)
								{
									if(E3dNode_HrcIsDescendantOf(LPickedNode, LSelectedBranch) == E_NOT_CONNECTED) LBranchesN++;
								}
							}
						}

						if(LBranchesN > 0)
						{
							E3dOperatorEditHierarchy*  LOperator = (E3dOperatorEditHierarchy*)EOperator_New(E3dOperatorEditHierarchy_class);

							{
								ENewNClr(LBranchesN, LOperator->BranchMoves.Items);LOperator->BranchMoves.Count = LOperator->BranchMoves.CountAllocated = LBranchesN;

								EIndex  LI = 0;const EIndex  LN = LSelectedBranches.Count;E3dBranchMove*  LBranchMove = LOperator->BranchMoves.Items;for(;LI < LN;LI++)
								{
									E3dNode*  LSelectedBranch = LSelectedBranches.Items[LI];
									const EIndex  LConnectionLevel = E3dNode_HrcIsDescendantOf(LSelectedBranch, LPickedNode);
									if(LConnectionLevel == E_NOT_CONNECTED || LConnectionLevel > 1)
									{
										if(E3dNode_HrcIsDescendantOf(LPickedNode, LSelectedBranch) == E_NOT_CONNECTED)
										{
											E_AssignToMember(LSelectedBranch, LBranchMove,Node);
											E_AssignToMember(LSelectedBranch->Parent, LBranchMove,OldParent);
											if(LSelectedBranch->Parent) LBranchMove->OldIndex = E3dNode_GetChildIndex(LSelectedBranch);
											else LBranchMove->OldIndex = EList_PointerGetIndex_s(LScene->RootNodes, LSelectedBranch);
											E_AssignToMember(LPickedNode, LBranchMove,NewParent);

											LBranchMove++;
										}
									}
								}
							}

							E3dpUndoStack_AddOperator((EOperator*)LOperator, NULL);
							LChanged |= _EditHierarchy_Func(NULL, (EOperator*)LOperator, EOpDO);

							_ConnectMode = 0;
							E3dp_PickEnd();
						}
					}
				}
			break;

			case E3dBA_PICK_OBJECT_FOR_ADDOBJECTTO_MODELSOF:
				if(LObjItem->Object)
				{
					E3dObject*  LPickedObject = LObjItem->Object;
					E3dModel**  LPickedObjectModels = LPickedObject->Models.Items;const EIndex  LPickedObjectModels_Count = LPickedObject->Models.Count;

					EIndex  LObjectsN = 0;

					{
						EIndex  LObjI = 0;const EIndex  LObjN = LSelectedObjects.Count;for(;LObjI < LObjN;LObjI++)
						{
							E3dObject*  LSelectedObject = LSelectedObjects.Items[LObjI];
							if(LSelectedObject != LPickedObject)
							{
								EIndex  LIC = 0;for(;LIC < LPickedObjectModels_Count;LIC++)
								{
									E3dModel*  LModel = LPickedObjectModels[LIC];
									if(EList_PointerGetIndex_s(LModel->Objects, LSelectedObject) == E_NOT_FOUND) { LObjectsN++;break; }
								}
							}
						}
					}

					if(LObjectsN > 0)
					{
						E3dOperatorEditHierarchy*  LOperator = (E3dOperatorEditHierarchy*)EOperator_New(E3dOperatorEditHierarchy_class);

						E3dp_PrintCompoundMessageVa(ELogMESSAGE, 5000,  XeFONT, XeFontDEFAULT, XeTEXT, "Picked Object ",  E3dp_ObjectS(LPickedObject->Name ? LPickedObject->Name : ""),  0);

						ENewNClr(LObjectsN, LOperator->ObjectMoves.Items);LOperator->ObjectMoves.Count = LOperator->ObjectMoves.CountAllocated = LObjectsN;E3dObjectMove*  LObjectMove = LOperator->ObjectMoves.Items;

						EIndex  LObjI = 0;for(;LObjI < LSelectedObjects.Count;LObjI++)
						{
							E3dObject*  LSelectedObject = LSelectedObjects.Items[LObjI];

							if(LSelectedObject != LPickedObject)
							{
								EBool  LObjAdd = FALSE;

								EIndex  LIC = 0;const EIndex  LIN = LPickedObjectModels_Count;for(;LIC < LIN;LIC++)
								{
									E3dModel*  LModel = LPickedObjectModels[LIC];
									if(EList_PointerGetIndex_s(LModel->Objects, LSelectedObject) == E_NOT_FOUND)
									{
										EObjectAndIndex*  LAddToModel = EList_StructAddAClr_s(LObjectMove->AddToModels, 16);
										E_AssignToMember(LModel, LAddToModel,Object);
										LAddToModel->Index = LModel->Objects.Count;
										LObjAdd = TRUE;
									}
								}

								if(LObjAdd)
								{
									E_AssignToMember(LSelectedObject, LObjectMove,Object);
									LObjectMove++;
								}
							}
						}

						E3dpUndoStack_AddOperator((EOperator*)LOperator, NULL);
						LChanged |= _EditHierarchy_Func(NULL, (EOperator*)LOperator, EOpDO);

						_ConnectMode = 0;
						E3dp_PickEnd();
					}
				}
			break;

			case E3dBA_PICK_CHILD_OBJECT_FOR_MODELS:
				switch(LObjItem->Type)
				{
					default:
						if(LObjItem->Object && LSelectedNodes.Count > 0)
						{
							E3dObject*  LPickedObject = LObjItem->Object;

							EIndex  LAddToModelsN = 0;

							{
								EIndex  LI = 0;const EIndex  LN = LSelectedNodes.Count;for(;LI < LN;LI++)
								{
									E3dNode*   LSelectedNode = LSelectedNodes.Items[LI];
									E3dModel*  LSelectedModel = E_DynamicCast(LSelectedNode, E3dModel);if(LSelectedModel)
									{
										const EIndex  LIndex = EList_PointerGetIndex_s(LSelectedModel->Objects, LPickedObject);if(LIndex == E_NOT_FOUND) LAddToModelsN++;
									}
								}
							}

							if(LAddToModelsN)
							{
								E3dOperatorEditHierarchy*  LOperator = (E3dOperatorEditHierarchy*)EOperator_New(E3dOperatorEditHierarchy_class);

								E3dp_PrintCompoundMessageVa(ELogMESSAGE, 5000,  XeFONT, XeFontDEFAULT, XeTEXT, "Picked Object ",  E3dp_ObjectS(LPickedObject->Name ? LPickedObject->Name : ""),  0);

								ENewNClr(1, LOperator->ObjectMoves.Items);LOperator->ObjectMoves.Count = LOperator->ObjectMoves.CountAllocated = 1;
								E3dObjectMove*  LObjectMove = LOperator->ObjectMoves.Items;
								E_AssignToMember(LPickedObject, LObjectMove,Object);


								EIndex  LI = 0;const EIndex  LN = LSelectedNodes.Count;for(;LI < LN;LI++)
								{
									E3dNode*   LSelectedNode = LSelectedNodes.Items[LI];
									E3dModel*  LSelectedModel = E_DynamicCast(LSelectedNode, E3dModel);if(LSelectedModel)
									{
										const EIndex  LIndex = EList_PointerGetIndex_s(LSelectedModel->Objects, LPickedObject);if(LIndex == E_NOT_FOUND)
										{
											EObjectAndIndex*  LAddToModel = EList_StructAddAClr_s(LObjectMove->AddToModels, 16);
											E_AssignToMember(LSelectedModel, LAddToModel,Object);
											LAddToModel->Index = LSelectedModel->Objects.Count;
										}
									}
								}

								if(_ConnectMode == E3dConnectREPARENT)
								{
									EIndex  LMI = 0;for(;LMI < LPickedObject->Models.Count;LMI++)
									{
										E3dModel*  LModel = LPickedObject->Models.Items[LMI];if(EList_PointerGetIndex_s(LSelectedNodes, LModel) == E_NOT_FOUND)
										{
											EObjectAndIndex*  LDelFrModel = EList_StructAddAClr_s(LObjectMove->DelFrModels, 16);
											E_AssignToMember(LModel, LDelFrModel,Object);
											LDelFrModel->Index = EList_PointerGetIndex_s(LModel->Objects, LPickedObject);
										}
									}
								}

								E3dpUndoStack_AddOperator((EOperator*)LOperator, NULL);
								LChanged |= _EditHierarchy_Func(NULL, (EOperator*)LOperator, EOpDO);
							}
						}
					break;
				}
			break;

			case E3dBA_PICK_PARENT_MODEL_FOR_OBJECTS:
printf("%s() %s:%d  %sGREEN%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);
				if(LSelectedObjects.Count > 0)
				{
					E3dNode*   LPickedNode = LObjItem->Node;
					E3dModel*  LPickedModel = E_DynamicCast(LPickedNode, E3dModel);if(LPickedModel)
					{
						EIndex  LObjectsN = 0;

						{
							EIndex  LI = 0;const EIndex  LN = LSelectedObjects.Count;for(;LI < LN;LI++)
							{
								E3dObject*  LObject = LSelectedObjects.Items[LI];
								EBool       LObjectAdd = FALSE;
								EBool       LObjectDel = FALSE;

								const EIndex  LIndex = EList_PointerGetIndex_s(LPickedModel->Objects, LObject);if(LIndex == E_NOT_FOUND)
								{
									LObjectAdd = TRUE;
								}

								if(_ConnectMode == E3dConnectREPARENT)
								{
									EIndex  LMI = 0;for(;LMI < LObject->Models.Count;LMI++)
									{
										E3dModel*  LModel = LObject->Models.Items[LMI];
										if(LModel != LPickedModel) { LObjectDel = TRUE;break; }
									}
								}

								if(LObjectAdd || LObjectDel) LObjectsN++;
							}
						}

						if(LObjectsN > 0)
						{
							E3dp_PrintCompoundMessageVa(ELogMESSAGE, 5000,  XeFONT, XeFontDEFAULT, XeTEXT, "Picked Model ",  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),  0);

							E3dOperatorEditHierarchy*  LOperator = (E3dOperatorEditHierarchy*)EOperator_New(E3dOperatorEditHierarchy_class);

							ENewNClr(LObjectsN, LOperator->ObjectMoves.Items);LOperator->ObjectMoves.Count = LOperator->ObjectMoves.CountAllocated = LObjectsN;
							if(LObjectsN > 1) LOperator->Name = EStrDup("Add Objects to Model");
							else LOperator->Name = EStrDup("Add Object to Model");

							EIndex  LI = 0;const EIndex  LN = LSelectedObjects.Count;E3dObjectMove*  LObjectMove = LOperator->ObjectMoves.Items;for(;LI < LN;)
							{
								E3dObject*  LObject = LSelectedObjects.Items[LI];
								EBool       LObjectAdd = FALSE;
								EBool       LObjectDel = FALSE;

								const EIndex  LIndex = EList_PointerGetIndex_s(LPickedModel->Objects, LObject);if(LIndex == E_NOT_FOUND)
								{
									EObjectAndIndex*  LAddModel = EList_StructAddAClr_s(LObjectMove->AddToModels, 16);
									E_AssignToMember(LPickedModel, LAddModel,Object);
//									LAddModel->Index = LPickedModel->Objects.Count;  // E3dFIXME Add to the end...
									LObjectAdd = TRUE;
								}

								if(_ConnectMode == E3dConnectREPARENT)
								{
									EIndex  LMI = 0;for(;LMI < LObject->Models.Count;LMI++)
									{
										E3dModel*  LModel = LObject->Models.Items[LMI];if(LModel != LPickedModel)
										{
											EObjectAndIndex*  LDelFrModel = EList_StructAddAClr_s(LObjectMove->DelFrModels, 16);
											E_AssignToMember(LModel, LDelFrModel,Object);
											LDelFrModel->Index = EList_PointerGetIndex_s(LModel->Objects, LObject);
											LObjectDel = TRUE;
										}
									}
								}

								if(LObjectAdd || LObjectDel)
								{
									E_AssignToMember(LObject, LObjectMove,Object);
									LI++;LObjectMove++;
								}
							}

							E3dpUndoStack_AddOperator((EOperator*)LOperator, NULL);
							LChanged |= _EditHierarchy_Func(NULL, (EOperator*)LOperator, EOpDO);
						}
						else
						{
							E3dp_PrintCompoundMessageVa(ELogMESSAGE, 5000,
							 XeFONT, XeFontDEFAULT, XeTEXT, "This Model ",  E3dp_NodeS(LPickedNode->Name ? LPickedNode->Name : ""),  XeTEXT, " already has the selected Object(s)",  0);
						}
					}
					_ConnectMode = 0;
					E3dp_PickEnd();
				}
			break;
		}
	}

	EList_FreeAChk_s(LSelectedNodes);
	EList_FreeAChk_s(LSelectedObjects);

	if(LChanged & E3dChgHIERARCHY)
	{
		E3dScene_ObjectsUpdateModelList(LScene);
		E3dScene_LayoutOnSchematics(LScene, FALSE);
	}

	if(LChanged)
	{
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
		E3dTransformPanel_Update(NULL,  E3dDoALL, E3dDoALL, E3dDoALL,  E3dpDO_NOT_CHANGE,  E3dTrPanelALL);
	}
}


//======================================================================================
// Get actions and status-panel strint for PERSPECTIVE, TOP, FRONT and RIGHT views
//======================================================================================
static EChangeFlags _DisconnectGetButtonActions(E3dItem* PPickedItems, const int PNumOfPickedItems, E3dNodePtrListA* PSelectedNodes, E3dObjectPtrListA* PSelectedObjects, int* PButtonActions, XeString* PXeStringRet)
{
	E3dObjItem*   LObjItem = (E3dObjItem*)PPickedItems;
	XeString      LXeString = NULL;
	EChangeFlags  LChanged = 0;

	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
	const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

	switch(LObjItem->Type)
	{
		case E3dItemMODEL:
		case E3dItemPOLYGON:
		case E3dItemSPLINE:
		case E3dItemFACE:
			{
				E3dNode*   LNode = LObjItem->Node;
				E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
				{
					switch(PSelectedObjects->Count)
					{
						case 0:
							LXeString = XeString_NewVa(TRUE,
							 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect Objects -  Select Objects  ",
							 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
							 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
							 0);
						break;

						case 1:
							if(E3dModel_ObjectGetIndex(LModel, PSelectedObjects->Items[0]) > -1)
							{
								LXeString = XeString_NewVa(TRUE,
								 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect Object - ",
								 E3dp_LMouseButtonS, XeTEXT, ": Disconnect selected Object from this Model  ",
								 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
								 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
								 0);
								PButtonActions[0] = E3dBA_PICK_NODE_TO_DISCONNECT_OBJECTS_FROM;
							}
							else
							{
								LXeString = XeString_NewVa(TRUE,
								 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect Object - Selected Object is not on this Model  ",
								 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
								 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
								 0);
							}
						break;

						default:
							{
								EBool  LModelContainsObject = FALSE;

								EIndex  LI = 0;const EIndex  LN = PSelectedObjects->Count;for(;LI < LN;LI++)
								{
									if(E3dModel_ObjectGetIndex(LModel, PSelectedObjects->Items[LI]) > -1)
									{
										LModelContainsObject = TRUE;
										break;
									}
								}

								if(LModelContainsObject)
								{
									LXeString = XeString_NewVa(TRUE,
									 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect - ",
									 E3dp_LMouseButtonS,  XeTEXT, ": Disconnect selected Objects from this Model  ",
									 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
									 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
									 0);
									PButtonActions[0] = E3dBA_PICK_NODE_TO_DISCONNECT_OBJECTS_FROM;
								}
								else
								{
									LXeString = XeString_NewVa(TRUE,
									 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect Object -  None of the selected Objects are on this Model  ",
									 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
									 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
									0);
								}
							}
						break;
					}
				}
				if(PButtonActions[0] || PButtonActions[1] || PButtonActions[2]) { LModel->Highlighted = TRUE;LChanged |= E3dChgSELECTION; }
			}
		break;
	}

	*PXeStringRet = LXeString;

	return(LChanged);
}


//================================================================
// Connect pick callback
//================================================================
static void _PickCB_DisconnectObject(E3dPickCallbackStruct* PPickStruct, EPointer PClientData,  const EIndex PNumOfPickedItems, E3dItem* PPickedItems)
{
	E3dScene*     LScene = E3d_Scene;
	E3dWindow*    L3DWindow = PPickStruct->E3DWindow;
	XeString      LXeString = NULL;
	EListAI_s(, E3dNodePtr, LSelectedNodes);E3dScene_GetSelectedNodes(LScene,  E3dNodeSelNODE|E3dNodeSelBRANCH_ROOT,   FALSE,  &LSelectedNodes);
	EListAI_s(, E3dObjectPtr, LSelectedObjects);E3dScene_GetObjects(LScene, E3dItemALL, E3dSELECTED|E3dEXCLUDE_MODEL_SELECTION, FALSE, &LSelectedObjects);
	int           LButtonActions[3] = { 0, 0, 0 };
	EChangeFlags  LChanged = 0;

	if(E3dScene_UnhighlightAll(LScene, E3dSF_ALL)) LChanged = E3dChgSELECTION;

	if(PPickStruct->Reason == E3dCR_ABORT)
	{
		EPluginLock_Del(_Plugin, &_DisconnectPickLock, FALSE);
		return;
	}

	if(PPickStruct->Button == Button3)
	{
		E3dp_PickEnd();
		EPluginLock_Del(_Plugin, &_DisconnectPickLock, FALSE);
		return;
	}

	if(LSelectedObjects.Count == 0)
	{
		E3dp_PrintMessage(ELogMESSAGE, 0, "Disconnect Objects -  Select Objects");

		EList_FreeAChk_s(LSelectedNodes);
		EList_FreeAChk_s(LSelectedObjects);
		return;
	}

	const E3dpPrefs*  LPrefs = &E3dp_Prefs;
	const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
	const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

	const E3dViewClass*  LViewClass = L3DWindow->ViewMode->Class;

	if(PNumOfPickedItems)
	{
		if(LViewClass == E3dSchematicsView_class || LViewClass == E3dTopView_class || LViewClass == E3dFrontView_class || LViewClass == E3dRightView_class || LViewClass == E3dPerspectiveView_class)
		{
			LChanged |= _DisconnectGetButtonActions(PPickedItems, PNumOfPickedItems, &LSelectedNodes, &LSelectedObjects, LButtonActions, &LXeString);
		}
		else
		{
			if(LSelectedObjects.Count > 1)
			{
				LXeString = XeString_NewVa(TRUE,
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect Objects - Move pointer to a 3D or a schematics view  ",
				 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
				 0);
			}
			else
			{
				LXeString = XeString_NewVa(TRUE,
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect Object - Move pointer to a 3D or a schematics view  ",
				 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
				 0);
			}
		}
	}
	else
	{
		if(LViewClass == E3dSchematicsView_class || LViewClass == E3dTopView_class || LViewClass == E3dFrontView_class || LViewClass == E3dRightView_class || LViewClass == E3dPerspectiveView_class)
		{
			if(LSelectedObjects.Count > 1)
			{
				LXeString = XeString_NewVa(TRUE,
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect Objects - Move pointer over a Model  ",
				 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
				 0);
			}
			else
			{
				LXeString = XeString_NewVa(TRUE,
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect Object - Move pointer over a Model  ",
				 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
				 0);
			}
		}
		else
		{
			if(LSelectedObjects.Count > 1)
			{
				LXeString = XeString_NewVa(TRUE,
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect Objects - Move pointer to a 3D or a schematics view  ",
				 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
				 0);
			}
			else
			{
				LXeString = XeString_NewVa(TRUE,
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Disconnect Object - Move pointer to a 3D or a schematics view  ",
				 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
				 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
				 0);
			}
		}
	}

	if(LXeString)
	{
		E3dp_PrintCompoundMessage(ELogMESSAGE, 0, LXeString);
		XeString_Free(LXeString);
	}

	if(LChanged == 0)
	{
		EList_FreeAChk_s(LSelectedNodes);
		EList_FreeAChk_s(LSelectedObjects);
		return;
	}


	if(PNumOfPickedItems)
	{
		int  LAction = 0;

		switch(PPickStruct->Button)
		{
			case Button1:  LAction = LButtonActions[0];break;
			case Button2:  LAction = LButtonActions[1];break;
			case Button3:  LAction = LButtonActions[2];break;
		}

		switch(LAction)
		{
			case E3dBA_PICK_NODE_TO_DISCONNECT_OBJECTS_FROM:
				{
					E3dObjItem*  LObjItem = (E3dObjItem*)PPickedItems;
					E3dNode*     LNode = LObjItem->Node;
					E3dModel*    LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
					{
						EListAI_s(, E3dObjectPtr, LObjects);const EIndex  LNumOfObjects = E3dScene_GetObjects(LScene, E3dItemANYOBJECT, E3dSELECTED, FALSE,  &LObjects);if(LNumOfObjects > 0)
						{
							EIndex  LObjectsN = 0;

							{
								EIndex  LI = 0;const EIndex  LN = LModel->Objects.Count;for(;LI < LN;LI++)
								{
									E3dObject*  LObject = LModel->Objects.Items[LI];

									if(LObject->Selection && LObject->Models.Count > 1) LObjectsN++;
								}
							}

							if(LObjectsN)
							{
								E3dOperatorEditHierarchy*  LOperator = (E3dOperatorEditHierarchy*)EOperator_New(E3dOperatorEditHierarchy_class);

								ENewNClr(LObjectsN, LOperator->ObjectMoves.Items);LOperator->ObjectMoves.Count = LOperator->ObjectMoves.CountAllocated = LObjectsN;E3dObjectMove*  LObjectMove = LOperator->ObjectMoves.Items;

								{
									EIndex  LI = 0;for(;LI < LModel->Objects.Count;LI++)
									{
										E3dObject*  LObject = LModel->Objects.Items[LI];

										if(LObject->Selection && LObject->Models.Count > 1)
										{
											char*  LObjectName = LObject->Name ? LObject->Name : "";
											char*  LNodeName   = LNode->Name ? LNode->Name : "";

											E_AssignToMember(LObject, LObjectMove,Object);
											EObjectAndIndex*  LDelFrModel = EList_StructAddAClr_s(LObjectMove->DelFrModels, 16);
											E_AssignToMember(LModel, LDelFrModel,Object);
											LDelFrModel->Index = EList_PointerGetIndex_s(LModel->Objects, LObject);
											LObjectMove++;

											E3dp_PrintCompoundMessageVa(ELogMESSAGE, 5000,
											 XeFONT, XeFontDEFAULT,
											 XeCOLOR_RGBA, LLabelRGBAp8Color,  XeTEXT, "Object ",                   E3dp_ObjectS(LObjectName),
											                                   XeTEXT, " disconnected from node ",  E3dp_NodeS(LNodeName),
											 0);
										}
									}
								}

								E3dpUndoStack_AddOperator((EOperator*)LOperator, NULL);
								LChanged |= _EditHierarchy_Func(NULL, (EOperator*)LOperator, EOpDO);

								LChanged |= E3dModel_GetTransformChangeFlagsByObjects(LModel) | E3dChgHIERARCHY;
							}

							EList_FreeA_s(LObjects);
						}
					}
				}
			break;
		}
	}

	if(LChanged & E3dChgHIERARCHY)
	{
		E3dScene_ObjectsUpdateModelList(LScene);
		E3dScene_LayoutOnSchematics(LScene, FALSE);
	}

	if(LChanged)
	{
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
		E3dTransformPanel_Update(NULL,  E3dDoALL, E3dDoALL, E3dDoALL,  E3dpDO_NOT_CHANGE,  E3dTrPanelALL);
	}


// If none of the remaining selected Objects are
// instanced by multiple Models, end mode
//
	if(LSelectedObjects.Count > 0)
	{
		EBool  LFoundMultiInstance = FALSE;

		EIndex  LI = 0;const EIndex  LN = LSelectedObjects.Count;for(;LI < LN;LI++)
		{
		  if(LSelectedObjects.Items[LI]->Models.Count > 1) { LFoundMultiInstance = TRUE;break; }
		}

		if(!LFoundMultiInstance)
		{
			E3dp_PickEnd();
			EPluginLock_Del(_Plugin, &_DisconnectPickLock, FALSE);
		}
	}
}


//========================================
// Dialog box callback
//========================================
static void _DCB(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	switch((EPointerInt)PClientData)
	{
		case CR_TOGGLE_HRC_COMPENSATION:
			if(((XeToggleButtonCallbackStruct*)PCallData)->Set) _TransformCompensation = TRUE;
			else _TransformCompensation = FALSE;
		break;
	}
}


//========================================
// Dialog box callback
//========================================
static void _DCB_Close(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	XeCallbackStruct*  LCBS = (XeCallbackStruct*)PCallData;
	if(LCBS->Reason == XeCR_PROTOCOLS)
	{
		XeWidget_Unmap(_DialogW);
		EPluginLock_Del(_Plugin, &_DialogLock, FALSE);
	}
}


static int _DialogLockDel(EPlugin* PPlugin, EPluginLock* PLock)
{
	XeWidget_Unmap(_DialogW);

	return(E_SUCCESS);
}


//========================================================
// Pop up connect/disconnect settings dialog box
//========================================================
static void _PopupDialog(void)
{
	if(_DialogLock == NULL) _DialogLock = EPluginLock_Add(_Plugin, "HierarchiesSettings", _DialogLockDel);

	if(_DialogW == NULL)
	{
		XeWidget  LW;
		EArg      Args[256];int  ArgCnt;

		EXeStart;
		EXeSetArg(XeNAllowShellResize, TRUE);
		EXeSetArg(XeNCreateButtons, FALSE);
		EXeSetArg(XeNWorkAreaType, XeMATRIX);
		EXeSetArg(XeNWMDecorations, XeWMDecorCLOSE|XeWMDecorBORDER|XeWMDecorRESIZEH|XeWMDecorTITLE);
		EXeSetArg(XeNTitle, "Hierarchy connect/disconnect settings");EXeSetArg(XeNIconName, "Hierarchy");
		XeWidget  LDialogW = XeDialog_New(XeDIALOG_TEMPLATE, "Hierarchy", E3dp_TopShellW,  Args, ArgCnt);if(LDialogW == NULL) return;

		XeAddCallback(LDialogW, XeNWMCallback, _DCB_Close, (EPointer)XeCR_CANCEL);

		_DialogW = LDialogW;

		XeWidget  LTopMatrixW = XeDialog_GetChild(LDialogW, XeDIALOG_WORK_AREA);
		EXeStart;
		EXeSetArg(XeNMarginSizeX, 6);EXeSetArg(XeNMarginSizeY, 4);
		Xe_SetValues(LTopMatrixW,  Args, ArgCnt);

		EXeStart;
		EXeSetArg(XeNShadowThickness, 0);
		EXeSetArg(XeNHighlightThickness, 0);
		EXeSetArg(XeNSet, _TransformCompensation);
		LW = XeWidget_ManagedNew("Transform compensation", XeToggleButton_class, LTopMatrixW,  Args, ArgCnt);
		XeAddCallback(LW, XeNValueChangedCallback, _DCB, (EPointer)CR_TOGGLE_HRC_COMPENSATION);

		XeShell_Raise(_DialogW);
		XeShell_CenterToWidget(_DialogW, E3dp_TopShellW);
	}
	else XeShell_Raise(_DialogW);
}


static int _PickLockDel(EPlugin* PPlugin, EPluginLock* PLock)
{
	E3dp_PickEnd();

	return(E_SUCCESS);
}


//================================================================
// Connect
//================================================================
static void _CB_Connect(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dp_ResetWorkModes();
	if(((XePushButtonCallbackStruct*)PCallData)->Reason == XeCR_ACTIVATE_OPTIONS) { _PopupDialog();return; }

	if(_ConnectMode == 0)
	{
		E3dScene*     LScene = E3d_Scene;
		XeString      LXeString = NULL;
		EListAI_s(, E3dNodePtr, LSelectedNodes);const EIndex  LSelectedNodes_Count = E3dScene_GetSelectedNodes(LScene,  E3dNodeSelNODE|E3dNodeSelBRANCH_ROOT, FALSE,  &LSelectedNodes);
		EListAI_s(, E3dObjectPtr, LSelectedObjects);const EIndex  LSelectedObjects_Count = E3dScene_GetObjects(LScene, E3dItemALL, E3dSELECTED|E3dEXCLUDE_MODEL_SELECTION, FALSE, &LSelectedObjects);

		if(LSelectedNodes_Count == 0 && LSelectedObjects_Count == 0) { E3dp_PrintMessage(ELogWARNING, 0, _Msg_SelectModelOrObjectToConnect);return; }
		if(LSelectedNodes_Count > 0 && LSelectedObjects_Count > 0) E3dp_PrintMessage(ELogWARNING, 0, _Msg_SelectOnlyModelOrObject);

		const char*   LToolBaseName;const char*  LToolName = _GetToolName(E3dConnectCONNECT, LSelectedNodes_Count, LSelectedObjects_Count,  &LToolBaseName, NULL);

		const E3dpPrefs*  LPrefs = &E3dp_Prefs;
		const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
		const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

		if(LSelectedObjects_Count)
		{
			LXeString = XeString_NewVa(TRUE,
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - Move pointer over a Model  ",
			 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
			 0);
		}
		else if(LSelectedNodes_Count)
		{
			LXeString = XeString_NewVa(TRUE,
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, LToolName,  XeTEXT, " - Move pointer over a Model or an Object  ",
			 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
			 0);
		}

		if(E3dp_PickRequest(LXeString, E3dItemMODEL|E3dItemANYOBJECT|E3dItemWORLD_POSITION,  _PickCB_Connect, NULL,  0, TRUE) == E_SUCCESS)
		{
			_ConnectMode = E3dConnectCONNECT;

			if(_ConnectPickLock == NULL) _ConnectPickLock = EPluginLock_Add(_Plugin, _ConnectPickStr, _PickLockDel);
		}

		EList_FreeAChk_s(LSelectedNodes);
		EList_FreeAChk_s(LSelectedObjects);
	}
}


//================================================================
// Disconnect
//================================================================
static void _CB_Disconnect(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dp_ResetWorkModes();
	if(((XePushButtonCallbackStruct*)PCallData)->Reason == XeCR_ACTIVATE_OPTIONS) { _PopupDialog();return; }

	{
		E3dScene*     LScene = E3d_Scene;
		EChangeFlags  LChanged = 0;

		EIndex  LNodesToDisconnectN = 0;

		{
			EIndex  LRI = 0;const EIndex  LRN = LScene->RootNodes.Count;for(;LRI < LRN;LRI++)
			{
				E3dNode*  LRootNode = LScene->RootNodes.Items[LRI];
				E3dNode*  LNode = LRootNode;for(;LNode;LNode = LNode->Next)
				{
					switch(LNode->Selection)
					{
						case E3dNodeSelNODE:
						case E3dNodeSelBRANCH_ROOT:
							if(LNode->Parent) LNodesToDisconnectN++;
						break;
					}
				}
			}
		}


		if(LNodesToDisconnectN)
		{
			E3dOperatorEditHierarchy*  LOperator = (E3dOperatorEditHierarchy*)EOperator_New(E3dOperatorEditHierarchy_class);

			ENewNClr(LNodesToDisconnectN, LOperator->BranchMoves.Items);LOperator->BranchMoves.Count = LOperator->BranchMoves.CountAllocated = LNodesToDisconnectN;E3dBranchMove*  LBranchMove = LOperator->BranchMoves.Items;

			EIndex  LRI = 0;const EIndex  LRN = LScene->RootNodes.Count;for(;LRI < LRN;LRI++)
			{
				E3dNode*  LRootNode = LScene->RootNodes.Items[LRI];
				E3dNode*  LNode = LRootNode;for(;LNode;LNode = LNode->Next)
				{
					switch(LNode->Selection)
					{
						case E3dNodeSelNODE:
						case E3dNodeSelBRANCH_ROOT:
							if(LNode->Parent)
							{
								E_AssignToMember(LNode, LBranchMove,Node);
								E_AssignToMember(LNode->Parent, LBranchMove,OldParent);
								LBranchMove->OldIndex = E3dNode_GetChildIndex(LNode);
								LBranchMove++;
							}
						break;
					}
				}
			}

			E3dpUndoStack_AddOperator((EOperator*)LOperator, NULL);
			LChanged |= _EditHierarchy_Func(NULL, (EOperator*)LOperator, EOpDO);

			if(LChanged & E3dChgHIERARCHY)
			{
				E3dScene_ObjectsUpdateModelList(LScene);
				E3dScene_LayoutOnSchematics(LScene, FALSE);
				E3dWindows_RedrawByChangeFlags(LScene, LChanged);
				E3dTransformPanel_Update(NULL,  E3dDoALL, E3dDoALL, E3dDoALL,  E3dpDO_NOT_CHANGE,  E3dTrPanelALL);
			}

			return;
		}

		const E3dpPrefs*  LPrefs = &E3dp_Prefs;
		const uint32_t    LLabelRGBAp8Color = Xe_ForegroundRGBA8pColor;

		EIndex  LRI = 0;const EIndex  LRN = LScene->RootNodes.Count;for(;LRI < LRN;LRI++)
		{
			E3dNode*  LRootNode = LScene->RootNodes.Items[LRI];
			E3dNode*  LNode = LRootNode;for(;LNode;LNode = LNode->Next)
			{
				switch(LNode->Selection)
				{
					case E3dNodeSelOBJECT:
						{
							E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
							{
								EIndex  LObjI = 0;const EIndex  LObjN = LModel->Objects.Count;for(;LObjI < LObjN;LObjI++)
								{
									E3dObject*  LObject = LModel->Objects.Items[LObjI];
									EBool       LValidDisconnectableObject = FALSE;

									if(LObject->Selection)
									{
										E3dModel*  LSingleInstanceModel = NULL;

										if(LObject->Models.Count > 1)
										{
											XeString	LXeString = XeString_NewVa(TRUE,
											 XeFONT, XeFontDEFAULT,     XeCOLOR_RGBA, LLabelRGBAp8Color,  XeTEXT, "Disconnect - ",
											 E3dp_LMouseButtonS,     XeTEXT, ": pick Model from which to disconnect the selected Object(s)  ",
											 E3dp_RMouseButtonEscS,  XeTEXT, ": cancel",
											 0);

											E3dp_PickRequest(LXeString, E3dItemMODEL,  _PickCB_DisconnectObject, NULL,  0, TRUE);
											if(_DisconnectPickLock == NULL) _DisconnectPickLock = EPluginLock_Add(_Plugin, "DisconnectPick", _PickLockDel);

											LValidDisconnectableObject = TRUE;
											break;
										}
										else LSingleInstanceModel = LObject->Models.Items[0];

										if(LSingleInstanceModel && !LValidDisconnectableObject) E3dp_PrintCompoundMessageVa(ELogWARNING, 5000, XeFONT, XeFontDEFAULT,  XeTEXT, "Disconnect - Object ",  E3dp_ObjectS(LObject->Name ? LObject->Name : ""),  XeTEXT, " is only connected to 1 Model: ",  E3dp_NodeS(LSingleInstanceModel->Name ? LSingleInstanceModel->Name : ""),  0);
									}
								}
								return;
							}
						}
					break;
				}
			}
		}
	}
}


//================================================================
// Reparent
//================================================================
static void _CB_Reparent(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dp_ResetWorkModes();
	if(((XePushButtonCallbackStruct*)PCallData)->Reason == XeCR_ACTIVATE_OPTIONS) { _PopupDialog();return; }

	if(_ConnectMode == 0)
	{
		E3dScene*     LScene = E3d_Scene;
		XeString      LXeString = NULL;
		EListAI_s(, E3dNodePtr, LSelectedNodes);const EIndex  LSelectedNodes_Count = E3dScene_GetSelectedNodes(LScene, E3dNodeSelNODE|E3dNodeSelBRANCH_ROOT, FALSE,  &LSelectedNodes);
		EListAI_s(, E3dObjectPtr, LSelectedObjects);const EIndex  LSelectedObjects_Count = E3dScene_GetObjects(LScene, E3dItemALL, E3dSELECTED|E3dEXCLUDE_MODEL_SELECTION, FALSE, &LSelectedObjects);

		if(LSelectedNodes_Count == 0 && LSelectedObjects_Count == 0) { E3dp_PrintMessage(ELogWARNING, 0, _Msg_SelectModelOrObjectToReparent);return; }

		if(LSelectedNodes_Count > 0 && LSelectedObjects_Count > 0) E3dp_PrintMessage(ELogWARNING, 0, _Msg_SelectOnlyModelOrObjectToReparent);

		const E3dpPrefs*  LPrefs = &E3dp_Prefs;
		const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
		const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

		if(LSelectedObjects_Count)
		{
			LXeString = XeString_NewVa(TRUE,
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Reparent Object(s):  Move pointer over a Model  ",
			 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
			 0);
		}
		else if(LSelectedNodes_Count)
		{
			LXeString = XeString_NewVa(TRUE,
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Reparent Model(s):  Move pointer over a Model or an Object  ",
			 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
			 0);
		}

		if(E3dp_PickRequest(LXeString, E3dItemMODEL|E3dItemANYOBJECT|E3dItemWORLD_POSITION,  _PickCB_Connect, NULL,  0, TRUE) == E_SUCCESS)
		{
			_ConnectMode = E3dConnectREPARENT;

			if(_ConnectPickLock == NULL) _ConnectPickLock = EPluginLock_Add(_Plugin, _ConnectPickStr, _PickLockDel);
		}

		EList_FreeAChk_s(LSelectedNodes);
		EList_FreeAChk_s(LSelectedObjects);
	}
}


//========================================
// Menu callback
//========================================
static void _MCB_SeparateSelectedObjects(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*     LScene = E3d_Scene;
	E3dNode**     LOldRootNodes;ENewN(LScene->RootNodes.Count, LOldRootNodes);
	EChangeFlags  LChanged = 0;

	EListAI(, E3dNode*, LNodesToSkip);

	{
		const EIndex	LRN = LScene->RootNodes.Count;memcpy(LOldRootNodes, LScene->RootNodes.Items, sizeof(E3dNode*) * LRN);
		EIndex  LRI = 0;for(;LRI < LRN;LRI++)
		{
			EBool     LUpdateHrc = FALSE;
			E3dNode*  LRootNode = LOldRootNodes[LRI];
			EIndex    LNextRootIndex = EList_PointerGetIndex_s(LScene->RootNodes, LRootNode);  // We might add new Hierarchies, so we need to get this index here (can't just use LRI)
			E3dNode*  LNode = LRootNode;for(;LNode;LNode = LNode->Next)
			{
				const EIndex  LNodeIndex = EList_PointerGetIndex(LNodesToSkip, LNode);  // Skip Nodes that we just added...

				if(LNodeIndex == E_NOT_FOUND && LNode->Selection == E3dNodeSelOBJECT)
				{
					E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel && LModel->Objects.Count > 1)
					{
//printf("%s() %s:%d [%s]\n", __FUNCTION__, __FILE__, __LINE__, LNode->Name);fflush(stdout);

						EIndex  LObjI = 0;E3dObject**  LObjects = LModel->Objects.Items;for(;LObjI < LModel->Objects.Count;)
						{
							E3dModel*		LNewModel = NULL;
							E3dObject*	LObject = LObjects[LObjI];

//printf("%s() %s:%d  <%s>\n", __FUNCTION__, __FILE__, __LINE__, LObject->Name);fflush(stdout);
							if(LObject->Selection == E3dObjectSelOBJECT)
							{
//printf("%s() %s:%d  <%s>\n", __FUNCTION__, __FILE__, __LINE__, LObject->Name);fflush(stdout);
								if(LNewModel == NULL)
								{
									E_New(E3dModel, LNewModel);

									EObject_SetName(LNewModel, E3dScene_GetUniqueNodeName(LScene, LModel->Name), FALSE);

									E3dTransformSet_Copy(&LModel->TransformSet, &LNewModel->TransformSet);
								}
								E3dModel_ObjectAppend(LNewModel, LObject, FALSE);
								E3dModel_ObjectRemove(LModel, LObject);
								LChanged |= E3dObject_GetTransformChangeFlagsByClass(LObject) | E3dChgHIERARCHY;
							}
							else LObjI++;

							if(LNewModel)
							{
								E3dNode*  LParentNode = LModel->Parent;if(LParentNode && _SeparateSelectedObjects_NewModelUnderOldParent)
								{
									const EIndex  LIndex = E3dNode_GetChildIndex((E3dNode*)LModel);
									E3dNode_ConnectChild(LModel->Parent, (E3dNode*)LNewModel, LIndex + 1, FALSE, FALSE);
									E3dNodeHrc_UpdateConnections(LRootNode);
									LUpdateHrc = TRUE;

									EList_PointerAddAChk(LNodesToSkip, 16, LNewModel);
//printf("%s() %s:%d  Added [%s]\n", __FUNCTION__, __FILE__, __LINE__, LNewModel->Name);fflush(stdout);
								}
								else
								{
									E3dScene_NodeHrcInsert(LScene, (E3dNode*)LNewModel, LNextRootIndex++, TRUE);
									if(LParentNode) E3dMatrix_DecomposeToScaleRotateTranslate(LModel->LocalToWorldMatrix, NULL, LNewModel->TransformSet.EulerRotation.Order,  &LNewModel->TransformSet);
									E3dNodeHrc_UpdateMatrices((E3dNode*)LNewModel, FALSE);
								}
							}
						}
					}
				}
			}

			if(LUpdateHrc)
			{
				E3dNodeHrc_UpdateMatrices(LRootNode, TRUE);
				E3dNodeHrc_UpdateMaterialInheritance(LRootNode);
				E3dNodeHrc_CallNodeInfoUpdateProcs(LRootNode, E3dChgHIERARCHY, FALSE);
			}

			LNodesToSkip_Count = 0;
		}
	}

	EFree(LOldRootNodes);

	EList_FreeAChk(LNodesToSkip);

	if(LChanged)
	{
		E3dScene_ObjectsUpdateModelList(LScene);
		E3dScene_LayoutOnSchematics(LScene, TRUE);
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);
	}
	else E3dp_PrintMessage(ELogWARNING, 7000, "Select Objects that are not the only Objects under their Models");
}


//================================================================
// Add selected Objects to the same Nodes as the one picked
//================================================================
static void _CB_Reinstantiate(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dp_ResetWorkModes();
	if(_ConnectMode == 0)
	{
		E3dScene*     LScene = E3d_Scene;
		XeString      LXeString = NULL;
		EListAI_s(, E3dObjectPtr, LSelectedObjects);E3dScene_GetObjects(LScene, E3dItemALL, E3dSELECTED|E3dEXCLUDE_MODEL_SELECTION, FALSE,  &LSelectedObjects);

		if(LSelectedObjects.Count == 0) { E3dp_PrintMessage(ELogWARNING, 0, _Msg_SelectObjectToReInstantiate);return; }

		{
			const E3dpPrefs*  LPrefs = &E3dp_Prefs;
			const uint32_t    LLabelRGBAp8Color      = Xe_ForegroundRGBA8pColor;
			const uint32_t    LButtonNameRGBAp8Color = LPrefs->ButtonNameRGBAp8Color;

			LXeString = XeString_NewVa(TRUE,
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, "Add Object(s) to the same Nodes as:  Move pointer over an Object  ",
			 XeFONT, XeFontFIXEDWIDTH,  XeCOLOR_RGBA, LButtonNameRGBAp8Color,  XeTEXT, "Esc",
			 XeFONT, XeFontDEFAULT,    XeCOLOR_RGBA, LLabelRGBAp8Color,       XeTEXT, ": cancel",
			 0);
		}

		if(E3dp_PickRequest(LXeString, E3dItemMODEL|E3dItemANYOBJECT|E3dItemWORLD_POSITION,  _PickCB_Connect, NULL,  0, TRUE) == E_SUCCESS)
		{
			_ConnectMode = E3dConnectINSTANTIATEAS;

			if(_ConnectPickLock == NULL) _ConnectPickLock = EPluginLock_Add(_Plugin, _ConnectPickStr, _PickLockDel);
		}

		EList_FreeAChk_s(LSelectedObjects);
	}
}


//================================================================================
// Check if any of the Objects in a hierarchy are instantiated more than once
//================================================================================
static EBool _NodeHrcHasInstancing(E3dNode* PNode)
{
	for(;PNode;PNode = PNode->Next)
	{
		E3dModel*  LModel = E_DynamicCast(PNode, E3dModel);

		if(LModel)
		{
			EIndex  LObjI = 0;const EIndex  LON = LModel->Objects.Count;for(;LObjI < LON;LObjI++)
			{
				E3dObject*  LObject = LModel->Objects.Items[LObjI];

				if(LObject->Models.Count > 1) return(TRUE);
			}
		}
	}

	return(FALSE);
}


//================================================================================
// Collapse branch
//================================================================================
static E3dNode* _BranchCollapse(E3dNode* PBranchRootNode)
{
	E3dNode*   LNodeNextAfterBranch = E3dNode_BranchGetNodeAfter(PBranchRootNode);
	E3dModel*  LBranchRootModel     = E_DynamicCast(PBranchRootNode, E3dModel);

	if(LBranchRootModel)
	{
		E3dNode**  LNodesToRemove = NULL;EIndex  LNumOfNodesToRemove = 0;
		E3dMatrix  LBranchRootWorldToLocalMatrix;

		E3dMatrix_Invert3x4(LBranchRootModel->LocalToWorldMatrix, LBranchRootWorldToLocalMatrix);

// Disconnect children of BranchRoot from the hierarchy
//
		{
			E3dNode*  LNode = PBranchRootNode->Child;do
			{
				ELst_ObjectAdd(&LNodesToRemove, &LNumOfNodesToRemove, LNode);

				LNode = LNode->Next;
			} while(LNode != LNodeNextAfterBranch);
		}

		{
			EIndex  LI = 0;for(;LI < LNumOfNodesToRemove;LI++)
			{
				E3dNode*  LNode = LNodesToRemove[LI];
				E3dNode_DisconnectFromParent(LNode, FALSE);
			}
		}

		EIndex  LI = 0;for(LI = 0;LI < LNumOfNodesToRemove;LI++)
		{
			E3dNode*   LNode = LNodesToRemove[LI];
			E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
			{
				const EIndex  LObjN = LModel->Objects.Count;if(LObjN)
				{
					E3dObject**  LObjects;ENewN(LObjN, LObjects);
					E3dMatrix    LMatrix;

					memcpy(LObjects, LModel->Objects.Items, sizeof(E3dObject*) * LObjN);

					E3dMatrix_Mult4x4_3(LModel->LocalToWorldMatrix, LBranchRootWorldToLocalMatrix, LMatrix);
					const E3dCoordinate  LMatrix3x3Determinant = E3dMatrix_Determinant3x3(LMatrix);

					EIndex  LObjI = 0;for(;LObjI < LObjN;LObjI++)
					{
						E3dObject*  LObject = LObjects[LObjI];

// If LObject is multi-instantiated, copy it before baking in the transforms
//
						if(LObject->Models.Count > 1)
						{
							E3dObject*  LOldObject = LObject;

							if(EObject_IsSubClass(LObject, E3dGeometry))
							{
								LObject = E3dObject_Copy(LObject, E3dCopyOBJECT_UNIQUE_NAME|E3dCopyEDGES|E3dCopyCOMPONENTS, E3d_Scene);

								LObject->Selection = 0;

								E3dGeometry*  LGeometry = (E3dGeometry*)LObject;
								E3dGeometry_Transform(LGeometry, LMatrix);
								if(LMatrix3x3Determinant < 0.0)
								{
									E3dGeometry_Invert(LGeometry, NULL);
								}
							}

							E3dModel_ObjectAppend(LBranchRootModel, LObject, FALSE);
							E3dModel_ObjectRemove(LModel, LOldObject);
						}
						else
						{
							if(EObject_IsSubClass(LObject, E3dGeometry))
							{
								E3dGeometry*  LGeometry = (E3dGeometry*)LObject;

								E3dGeometry_Transform(LGeometry, LMatrix);
								if(LMatrix3x3Determinant < 0.0)
								{
									E3dGeometry_Invert(LGeometry, NULL);
								}
							}

							E3dModel_ObjectAppend(LBranchRootModel, LObject, FALSE);
							E3dModel_ObjectRemove(LModel, LObject);
						}

						if(LObject->Name == NULL) EObject_SetName(LObject, EStrDup(LModel->Name), FALSE);
					}
					EFree(LObjects);
				}
			}
		}

		ELst_ObjectsFree(&LNodesToRemove, &LNumOfNodesToRemove);
	}

	E3dNode_UpdateForDisplay(PBranchRootNode, E3dUpdateALL & ~(E3dUpdateEDGES|E3dUpdateVERTEX_NORMAL));

	return(LNodeNextAfterBranch);
}


//================================================================
// Collapse selected branches into one Node per branch
//================================================================
static void _CB_CollapseBranch(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*  LScene = E3d_Scene;
	EIndex     LBranchesCollapsed = 0;

	EIndex  LRI = 0;const EIndex  LRN = LScene->RootNodes.Count;E3dNode**  LRootNodes = LScene->RootNodes.Items;for(;LRI < LRN;LRI++)
	{
		E3dNode*  LRootNode = LRootNodes[LRI];

		if(_NodeHrcHasInstancing(LRootNode))
		{
			E3dp_PrintMessage(ELogMESSAGE, 5000, "Branch %s has multi-instantiated Objects. These will be duplicated.", LRootNode->Name);
		}

		E3dNode*  LNode = LRootNode;do
		{
			if((LNode->Selection == E3dNodeSelBRANCH_ROOT) && LNode->Child)
			{
				LNode = _BranchCollapse(LNode);
				LBranchesCollapsed++;
			}
			else LNode = LNode->Next;
		} while(LNode);

		E3dNodeHrc_UpdateConnections(LRootNode);
		E3dNodeHrc_UpdateMatrices(LRootNode, TRUE);
		E3dNodeHrc_CallNodeInfoUpdateProcs(LRootNode, E3dChgHIERARCHY, FALSE);
		E3dNodeHrc_UpdateMaterialInheritance(LRootNode);
	}

	if(LBranchesCollapsed == 0) E3dp_PrintMessage(ELogWARNING, 5000, "No branches selected");
	else
	{
		if(LBranchesCollapsed == 1) E3dp_PrintMessage(ELogMESSAGE, 5000, "Collapsed 1 branch");
		else E3dp_PrintMessage(ELogMESSAGE, 5000, "Collapsed %d branches", LBranchesCollapsed);

		E3dScene_ObjectsUpdateModelList(LScene);
		E3dScene_LayoutOnSchematics(LScene, FALSE);
		E3dWindows_RedrawByChangeFlags(LScene, E3dChgHIERARCHY);
		E3dTransformPanel_Update(NULL,  E3dDoALL, E3dDoALL, E3dDoALL,  E3dpDO_NOT_CHANGE,  E3dTrPanelALL);
	}
}



typedef struct
{
	E3dNode*  Parent;
	EListA_s(, E3dNodePtr, Nodes);
	EIndex    FirstNodeChildIndex;
} E3dPNode;
EListA_t(E3dPNode);

static E3dPNode* _FindNode(E3dPNode* PPNode, const EIndex PPNodes_Count, E3dNode* PNode)
{
	E3dNode*  LParent = PNode->Parent;

	EIndex  LI = 0;for(;LI < PPNodes_Count;LI++, PPNode++) if(PPNode->Parent == LParent) return(PPNode);

	return(NULL);
}


//================================================================
// Insert Nodes between the selected Nodes and their parents
//================================================================
static void _CB_AddParent(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*  LScene = E3d_Scene;
	EListAI_s(, E3dPNode, LPNodes);
	E3dNode**  LAffectedRoots = NULL;EIndex  LNumOfAffectedRoots = 0;

// Collect candidate Nodes for grouping
//
	EIndex  LRI = 0;const EIndex  LRN = LScene->RootNodes.Count;E3dNode**  LRootNodes = LScene->RootNodes.Items;for(;LRI < LRN;LRI++)
	{
		E3dNode*  LRootNode = LRootNodes[LRI];

		E3dNode*  LNode = LRootNode;for(;LNode;LNode = LNode->Next)
		{
			if(LNode->Selection == E3dNodeSelNODE || LNode->Selection == E3dNodeSelBRANCH_ROOT)
			{
				E3dPNode*  LPNode = _FindNode(LPNodes.Items, LPNodes.Count, LNode);if(LPNode == NULL)
				{
					LPNode = EList_StructAddA_s(LPNodes, 16);
					EList_InitA_s(LPNode->Nodes);
					E_AssignToMemberNoChkNoFree(LNode->Parent, LPNode,Parent);


					if(LNode->Parent)
					{
						E3dNode*  LChild = LNode->Parent->Child;
						EIndex    LIndex = 0;while(LChild != LNode && LChild->NextSibling) { LChild = LChild->NextSibling;LIndex++; }
						LPNode->FirstNodeChildIndex = LIndex;
					}
					else LPNode->FirstNodeChildIndex = EList_PointerGetIndex_s(LScene->RootNodes, LNode);

					if(LNode->Parent)
					{
						E3dNode*  LRoot = LNode->Parent;while(LRoot->Parent) LRoot = LRoot->Parent;
						ELst_PointerAddChk(&LAffectedRoots, &LNumOfAffectedRoots, LRoot);
					}
				}
				EList_ObjectAddA_s(LPNode->Nodes, 16, LNode);
			}
		}
	}

	if(LPNodes.Count > 0)
	{
		EChangeFlags  LChanged = 0;

		EIndex  LPI = 0;const EIndex  LPN = LPNodes.Count;E3dPNode*  LPNode = LPNodes.Items;for(;LPI < LPN;LPI++, LPNode++)
		{
			E3dNode*      LParent = LPNode->Parent;
			const EIndex  LN = LPNode->Nodes.Count;

			if(LParent)
			{
				EIndex  LI = 0;for(;LI < LN;LI++)
				{
					E3dNode*  LNode = LPNode->Nodes.Items[LI];

					E3dNode_DisconnectFromParent(LNode, FALSE);
				}
			}

			E3dModel*  LNewParent;E_New(E3dModel, LNewParent);EObject_SetName(LNewParent, E3dScene_GetUniqueNodeName(LScene, "Group"), FALSE);
			if(LParent)
			{
				E3dNode_ConnectChild(LParent, (E3dNode*)LNewParent, LPNode->FirstNodeChildIndex, FALSE, FALSE);
			}
			else
			{
				ELst_PointerAddChk(&LAffectedRoots, &LNumOfAffectedRoots, LNewParent);
				E3dScene_NodeHrcInsert(LScene, (E3dNode*)LNewParent, LPNode->FirstNodeChildIndex, FALSE);
			}


			EIndex  LI = 0;for(;LI < LN;LI++)
			{
				E3dNode*  LNode = LPNode->Nodes.Items[LI];

				E3dScene_NodeHrcRemove(LScene, LNode, &LChanged, FALSE);
				E3dNode_ConnectChild((E3dNode*)LNewParent, LNode, ELstEND, FALSE, FALSE);

				E_Delete(LPNode->Nodes.Items[LI]);
			}
			E_DeleteChk(LPNode->Parent);
		}
		EList_FreeA_s(LPNodes);


		if(LAffectedRoots)
		{
			EIndex  LI = 0;for(;LI < LNumOfAffectedRoots;LI++)
			{
				E3dNodeHrc_UpdateConnections(LAffectedRoots[LI]);
				E3dNodeHrc_UpdateMatrices(LAffectedRoots[LI], TRUE);
			}
			EFree(LAffectedRoots);
		}

		E3dScene_LayoutOnSchematics(LScene, FALSE);
		E3dWindows_RedrawByChangeFlags(LScene, E3dChgHIERARCHY);
	}
	else E3dp_PrintMessage(ELogWARNING, 5000, "Select at least one Model.");
}


//================================================================
// Expand selected Nodes in the Schematic view
//================================================================
static void _CB_ExpandAll(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*  LScene = E3d_Scene;
	EIndex     LChanged = 0;

// Collect candidate Nodes for grouping
//
	EIndex  LRI = 0;const EIndex  LRN = LScene->RootNodes.Count;E3dNode**  LRootNodes = LScene->RootNodes.Items;for(;LRI < LRN;LRI++)
	{
		E3dNode*  LRootNode = LRootNodes[LRI];

		E3dNode*  LNode = LRootNode;for(;LNode;LNode = LNode->Next)
		{
			if(LNode->Selection)
			{
				if(!LNode->SchemObject.BranchExpanded) { LNode->SchemObject.BranchExpanded = TRUE;LChanged++; }

				E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
				{
					EIndex  LObjI = 0;const EIndex  LObjN = LModel->Objects.Count;for(;LObjI < LObjN;LObjI++)
					{
						E3dObject*  LObject = LModel->Objects.Items[LObjI];
						if(!LObject->SchemObject.BranchExpanded) { LObject->SchemObject.BranchExpanded = TRUE;LChanged++; }
					}
				}
			}
		}
	}

	if(LChanged)
	{
		E3dScene_LayoutOnSchematics(LScene, FALSE);
		E3dWindows_RedrawByChangeFlags(LScene, E3dChgSCHEMATICS);
	}
}


//================================================================
// Expand selected Nodes in the Schematic view
//================================================================
static void _CB_CollapseAll(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*  LScene = E3d_Scene;
	EIndex     LChanged = 0;

// Collect candidate Nodes for grouping
//
	EIndex  LRI = 0;const EIndex  LRN = LScene->RootNodes.Count;E3dNode**  LRootNodes = LScene->RootNodes.Items;for(;LRI < LRN;LRI++)
	{
		E3dNode*  LRootNode = LRootNodes[LRI];

		E3dNode*  LNode = LRootNode;for(;LNode;LNode = LNode->Next)
		{
			if(LNode->Selection)
			{
				if(LNode->SchemObject.BranchExpanded) { LNode->SchemObject.BranchExpanded = FALSE;LChanged++; }

				E3dModel*  LModel = E_DynamicCast(LNode, E3dModel);if(LModel)
				{
					EIndex  LObjI = 0;const EIndex  LObjN = LModel->Objects.Count;for(;LObjI < LObjN;LObjI++)
					{
						E3dObject*  LObject = LModel->Objects.Items[LObjI];
						if(LObject->SchemObject.BranchExpanded) { LObject->SchemObject.BranchExpanded = FALSE;LChanged++; }
					}
				}
			}
		}
	}

	if(LChanged)
	{
		E3dScene_LayoutOnSchematics(LScene, FALSE);
		E3dWindows_RedrawByChangeFlags(LScene, E3dChgSCHEMATICS);
	}
}


static void _KCB_AddParent(const EKeyCode PKey, const unsigned int PModifiers, const int PKeyEventType, EPointer PClientData)
{
	_CB_AddParent(NULL, NULL, NULL);
}


//================================================================
// Move Node or Object to the 'left' within the hierarchy
//================================================================
static void _CB_MoveLeft(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*  LScene = E3d_Scene;
	E3dItem    LSelectedItem;

	switch(E3dScene_GetSingleSelection(LScene, &LSelectedItem, E3dItemMODEL|E3dItemANYOBJECT, 0))
	{
		case E3dItemMODEL:
			{
				E3dNode*  LNode = LSelectedItem.ObjItem.Node;

				if(LNode->Parent)
				{
					E3dNode*  LParent = LNode->Parent;

					if(LNode->PrevSibling)
					{
						E_Object(E3dNode, LNode->NextSibling, LNextSibling);  // Temporary references, so these won't get freed
						E_Object(E3dNode, LNode->PrevSibling, LPrevSibling);

						E_AssignToMember(LPrevSibling, LNode,NextSibling);

						if(LPrevSibling->PrevSibling)
						{
							E_AssignToMember(LNode, LPrevSibling->PrevSibling,NextSibling);
						}
						else
						{
							E_AssignToMember(LNode, LParent,Child);
						}

						E_AssignToMember(LNextSibling, LPrevSibling,NextSibling);

						E_DeleteChk(LNextSibling);
						E_DeleteChk(LPrevSibling);

						while(LNode->Parent) LNode = LNode->Parent;
						E3dNodeHrc_UpdateConnections(LNode);

						E3dScene_ObjectsUpdateModelList(LScene);
						E3dScene_LayoutOnSchematics(LScene, FALSE);

						E3dWindows_RedrawByChangeFlags(LScene, E3dChgHIERARCHY);
					}
					else E3dp_PrintMessage(ELogWARNING, 5000, "This Model is already the first child of its parent");
				}
				else
				{
// It's a root Node
//
					EIndex  LIndex = EList_PointerGetIndex_s(LScene->RootNodes, LNode);if(LIndex > 0)
					{
						E_Object(E3dNode, LScene->RootNodes.Items[LIndex], LTNode);  // Temporary reference, so LScene->RootNodes[LIndex] doesn't get freed

						E_AssignToMember(LScene->RootNodes.Items[LIndex - 1], LScene,RootNodes.Items[LIndex]);
						E_AssignToMember(LTNode, LScene,RootNodes.Items[LIndex - 1]);
						E_Delete(LTNode);

						E3dScene_ObjectsUpdateModelList(LScene);
						E3dScene_LayoutOnSchematics(LScene, FALSE);

						E3dWindows_RedrawByChangeFlags(LScene, E3dChgHIERARCHY);
					}
					else E3dp_PrintMessage(ELogWARNING, 5000, "This Model is already the first root in the Scene");
				}
			}
		break;

		case E3dItemOBJECT:
			{
				E3dObject*  LObject = LSelectedItem.ObjItem.Object;
				E3dModel*   LModel = LObject->Models.Items[0];
				EIndex      LIndex = EList_PointerGetIndex_s(LModel->Objects, LObject);if(LIndex > 0)
				{
					E_Object(E3dObject, LModel->Objects.Items[LIndex], LTObject);  // Temporary reference, so LModel->Objects[LIndex] doesn't get freed

					E_AssignToMember(LModel->Objects.Items[LIndex - 1], LModel,Objects.Items[LIndex]);
					E_AssignToMember(LTObject, LModel,Objects.Items[LIndex - 1]);
					E_Delete(LTObject);

					E3dScene_ObjectsUpdateModelList(LScene);
					E3dScene_LayoutOnSchematics(LScene, FALSE);

					E3dWindows_RedrawByChangeFlags(LScene, E3dChgHIERARCHY);
				}
				else E3dp_PrintMessage(ELogWARNING, 5000, "This Object is already the first one under its (first) Model");
			}
		break;

		default:
		 E3dp_PrintMessage(ELogWARNING, 5000, "Select a Model or an Object");
		break;
	}
}


//================================================================
// Move Node or Object to the 'right' within the hierarchy
//================================================================
static void _CB_MoveRight(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dScene*  LScene = E3d_Scene;
	E3dItem    LSelectedItem;

	switch(E3dScene_GetSingleSelection(LScene, &LSelectedItem, E3dItemMODEL|E3dItemANYOBJECT, 0))
	{
		case E3dItemMODEL:
			{
				E3dNode*  LNode = LSelectedItem.ObjItem.Node;

				if(LNode->Parent)
				{
					E3dNode*	LParent = LNode->Parent;

					if(LNode->NextSibling)
					{
						E_Object(E3dNode, LNode->NextSibling, LNextSibling);	// Temporary references, so these won't get freed
						E_Object(E3dNode, LNode->PrevSibling, LPrevSibling);

						E_AssignToMember(LNextSibling->NextSibling, LNode,NextSibling);
						E_AssignToMember(LNode, LNextSibling,NextSibling);

						if(LPrevSibling)
						{
							E_AssignToMember(LNextSibling, LPrevSibling,NextSibling);
						}
						else
						{
							E_AssignToMember(LNextSibling, LParent,Child);
						}

						E_DeleteChk(LNextSibling);
						E_DeleteChk(LPrevSibling);

						while(LNode->Parent) LNode = LNode->Parent;
						E3dNodeHrc_UpdateConnections(LNode);

						E3dScene_ObjectsUpdateModelList(LScene);
						E3dScene_LayoutOnSchematics(LScene, FALSE);

						E3dWindows_RedrawByChangeFlags(LScene, E3dChgHIERARCHY);
					}
					else E3dp_PrintMessage(ELogWARNING, 5000, "This Node is already the last child of its parent");
				}
				else
				{
// It's a root Node
//
					EIndex  LIndex = EList_PointerGetIndex_s(LScene->RootNodes, LNode);

					if(LIndex < LScene->RootNodes.Count - 1)
					{
						E_Object(E3dNode, LScene->RootNodes.Items[LIndex], LTNode);  // Temporary reference, so LScene->RootNodes[LIndex] doesn't get freed

						E_AssignToMember(LScene->RootNodes.Items[LIndex + 1], LScene,RootNodes.Items[LIndex]);
						E_AssignToMember(LTNode, LScene,RootNodes.Items[LIndex + 1]);
						E_Delete(LTNode);

						E3dScene_ObjectsUpdateModelList(LScene);
						E3dScene_LayoutOnSchematics(LScene, FALSE);

						E3dWindows_RedrawByChangeFlags(LScene, E3dChgHIERARCHY);
					}
					else E3dp_PrintMessage(ELogWARNING, 5000, "This Model is already the last root in the Scene");
				}
			}
		break;

		case E3dItemOBJECT:
			{
				E3dObject*  LObject = LSelectedItem.ObjItem.Object;
				E3dModel*   LModel = LObject->Models.Items[0];
				EIndex      LIndex = EList_PointerGetIndex_s(LModel->Objects, LObject);

				if(LIndex < LModel->Objects.Count - 1)
				{
					E_Object(E3dObject, LModel->Objects.Items[LIndex], LTObject);  // Temporary reference, so LModel->Objects[LIndex] doesn't get freed

					E_AssignToMember(LModel->Objects.Items[LIndex + 1], LModel,Objects.Items[LIndex]);
					E_AssignToMember(LTObject, LModel,Objects.Items[LIndex + 1]);
					E_Delete(LTObject);

					E3dScene_ObjectsUpdateModelList(LScene);
					E3dScene_LayoutOnSchematics(LScene, FALSE);

					E3dWindows_RedrawByChangeFlags(LScene, E3dChgHIERARCHY);
				}
				else E3dp_PrintMessage(ELogWARNING, 5000, "This Object is already the last one under its (first) Model");
			}
		break;

		default:
			E3dp_PrintMessage(ELogWARNING, 5000, "Select a Model or an Object");
		break;
	}
}


static void _KCB_MoveLeft(const EKeyCode PKey, const unsigned int PModifiers, const int PKeyEventType, EPointer PClientData)
{
	_CB_MoveLeft(NULL, NULL, NULL);
}


static void _KCB_MoveRight(const EKeyCode PKey, const unsigned int PModifiers, const int PKeyEventType, EPointer PClientData)
{
	_CB_MoveRight(NULL, NULL, NULL);
}


//========================================
// Entry point of the plugin
//========================================
int E_PluginInit(EPlugin* PPlugin)
{
	_Plugin = PPlugin;

	{
		static EOperatorClass  LClass[] =
		{
			{
				.Name=        "Edit hierarchies",
				.StructSize=  sizeof(E3dOperatorEditHierarchy),
				.Init=        _EditHierarchy_Init,
				.Free=        _EditHierarchy_Free,
				.Func=        _EditHierarchy_Func
			}
		};
		E3dOperatorEditHierarchy_class  = EOperator_ClassRegister(LClass);
	}

	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Connect Model, branch or Object", '\0', NULL, NULL, TRUE, NULL, _CB_Connect, (EPointer)NULL);
	XePushButton_AddImg3_sp(_ToolBarButtons[_ToolBarButtons_Count++], Connect, "Tool->Hierarchy", "Connect", '\0', NULL, NULL, FALSE, "Connect selected Model(s) to a hierarchy or selected Objects (Geometry, Light etc.) to a Model\nRight mouse button: settings for this tool", _CB_Connect, (EPointer)NULL);

	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Disconnect Model, branch or Object", '\0', NULL, NULL, TRUE, NULL, _CB_Disconnect, (EPointer)NULL);
	XePushButton_AddImg3_sp(_ToolBarButtons[_ToolBarButtons_Count++], Disconnect, "Tool->Hierarchy", "Disconnect", '\0', NULL, NULL, FALSE, "Disconnect selected Model(s) from their parent(s)\nRight mouse button: settings for this tool", _CB_Disconnect, (EPointer)NULL);

	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Reparent Object", '\0', NULL, NULL, TRUE, NULL, _CB_Reparent, (EPointer)NULL);
	XePushButton_AddImg3_sp(_ToolBarButtons[_ToolBarButtons_Count++], Reparent, "Tool->Hierarchy", "Reparent", '\0', NULL, NULL, FALSE, "Reparent: Disconnect selected Models or Objects from their parent and move them under a different Model\nRight mouse button: settings for this tool", _CB_Reparent, (EPointer)NULL);

	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Separate Objects", '\0', NULL, NULL, FALSE, NULL, _MCB_SeparateSelectedObjects, (EPointer)0);

	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Add Object to same Nodes as...", '\0', NULL, NULL, FALSE, NULL, _CB_Reinstantiate, (EPointer)NULL);

	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Collapse branch", '\0', NULL, NULL, FALSE, NULL, _CB_CollapseBranch, (EPointer)0);

	const EKeyModifiers  LCtrlMask = Xe_CommandKeyForCtrl ? E_CommandModifierMask : EControlMask;const char*  LCtrlName = Xe_CommandKeyForCtrl ? "Cmd" : "Ctrl";char                 LAccText[256];

	sprintf(LAccText, "%s+left", LCtrlName);_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Move Model / Object 'left'", '\0', NULL, LAccText, FALSE, "Move Model or Object to the 'left' within the hierarchy", _CB_MoveLeft, (EPointer)NULL);
	E_KeySetCallback("Move Model or Object to the 'left' within the hierarchy",  EKeyLEFT,  LCtrlMask, EKeyPRESS, _KCB_MoveLeft, (EPointer)NULL);

	sprintf(LAccText, "%s+right", LCtrlName);_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Move Model / Object 'right'", '\0', NULL, LAccText, FALSE, "Move Model or Object to the 'right' within the hierarchy", _CB_MoveRight, (EPointer)NULL);
	E_KeySetCallback("Move Model or Object to the 'right' within the hierarchy", EKeyRIGHT, LCtrlMask, EKeyPRESS, _KCB_MoveRight, (EPointer)NULL);

	sprintf(LAccText, "%s+G", LCtrlName);_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Group objects", '\0', NULL, LAccText, FALSE, "Add new parent Model to the selected Model.", _CB_AddParent, (EPointer)0);
	E_KeySetCallback("Group objects", 'g',                                                 LCtrlMask, EKeyPRESS, _KCB_AddParent, (EPointer)NULL);

	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Expand selected", '\0', NULL, NULL, FALSE, "Expand all selected Nodes", _CB_ExpandAll, (EPointer)NULL);
	_MenuButtons[_MenuButtons_Count++] = XePushButton_Add(Xe_App, "Menu->Hierarchy", "Collapse selected", '\0', NULL, NULL, FALSE, "Expand all selected Nodes", _CB_CollapseAll, (EPointer)NULL);
//	E_KeySetCallback("Expand selected", EKeyRIGHT, EControlMask, EKeyPRESS, _KCB_ExpandAll, (EPointer)NULL);

	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(!PExiting || Xe_GUIFreeOnExit)
	{
		{ int  LI = 0;for(;LI < _MenuButtons_Count;LI++) { XeItemFree(_MenuButtons[LI]); } }
		_MenuButtons_Count = 0;

		{ int  LI = 0;for(;LI < _ToolBarButtons_Count;LI++) { XeItemFree(_ToolBarButtons[LI]); } }
		_ToolBarButtons_Count = 0;
	}

	EpIconFree3(Connect);
	EpIconFree3(Disconnect);
	EpIconFree3(Reparent);

	const EKeyModifiers  LCtrlMask = Xe_CommandKeyForCtrl ? E_CommandModifierMask : EControlMask;
	E_KeyRemoveCallback(EKeyLEFT,  LCtrlMask, EKeyPRESS, _KCB_MoveLeft, (EPointer)NULL);
	E_KeyRemoveCallback(EKeyRIGHT, LCtrlMask, EKeyPRESS, _KCB_MoveRight, (EPointer)NULL);
	E_KeyRemoveCallback('g',       LCtrlMask, EKeyPRESS, _KCB_AddParent, (EPointer)NULL);

	if(E3dOperatorEditHierarchy_class) { EOperator_ClassDeactivate(E3dOperatorEditHierarchy_class);E3dOperatorEditHierarchy_class = NULL; }

	return(E_SUCCESS);
}
