//================================================================
// EQUINOX-3D(TM) plugin
//
// - Edit selected Object (Geometry, Light etc.)
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Apr-16 01:38:12
//
// Copyright (C) 1996-2026 By Gabor Nagy. All rights reserved.
//================================================================
#include <stdio.h>

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

#include <E3D/Math.h>
#include <E3D/Matrix.h>
#include <E3D/Scene.h>


#include <E3D/UI/Panel.h>
#include <E3D/UI/Panels.h>
#include <E3D/UI/StatusPanel.h>  // For E3dp_PrintMessage

#include <Xe/Label.h>
#include <Xe/Matrix.h>
#include <Xe/Scale.h>
#include <Xe/Separator.h>
#include <Xe/Shell.h>
#include <Xe/PushButton.h>
#include <Xe/TextField.h>
#include <Xe/ToggleButton.h>

#include <Xe/Dialog.h>

#include "Edit.h"

#include "../EQXExt.h"


static EPluginLock*  _ObjectDialogLock = NULL;

static void          _CB_ObjectSelection(ECoreObject* LScn, EPointer PClientData, ECallbackStruct* PCallData);
static EBool         _ObjectSelectionCallbackAdded = FALSE;


static E3dObjectPanel*  _ObjectPanel = NULL;
static XeWidget         _ObjectDialogW = NULL;


static void _VertexAttribute_PaintOnOff(E3dObjectPanel* PPanel)
{
	E3dMesh*  LMesh = E_DynamicCast(PPanel->Object, E3dMesh);if(LMesh)
	{
		XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)PPanel, E3dMesh_class);if(LDrawer)
		{
// Check if a paintable VertexAttribute is displayed
//
			EResource*  LVAttr;
			{
				EIndex  LI = 0;const EIndex  LN = LMesh->VertexAttributes.Count;LVAttr = LMesh->VertexAttributes.Items;for(;LI < LN;LI++, LVAttr++)
				{
					if(LVAttr->UI == E3dUI_ShowAll) break;
					else if(LVAttr->UI == E3dUI_Show0) break;
					else if(LVAttr->UI == E3dUI_Show1) break;
					else if(LVAttr->UI == E3dUI_Show2) break;
					else if(LVAttr->UI == E3dUI_Show3) break;
				}
				if(LI == LN) LVAttr = NULL;
			}

			{
				E3dVertexGeometryGUI*  LGUI = (E3dVertexGeometryGUI*)LDrawer->ResourcesGUI;
				if(LVAttr && E3dVertexAttribute_VisualizationType(LVAttr) == E3dCOLOR) XeWidget_Manage(LGUI->VertexAttributePaintW);
				else XeWidget_Unmanage(LGUI->VertexAttributePaintW);
			}
		}
	}
}


//========================================
// Dialog callback handler
//========================================
void _DCB_VertexAttribute(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*  LObjectPanel = (E3dObjectPanel*)PClientData;
	E3dObject*       LObject = LObjectPanel->Object;

	E3dVertexGeometry*  LVertexGeometry = E3dVertexGeometry_CastTo((EObject*)LObject);if(LVertexGeometry)
	{
		XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LObjectPanel, LObject->Class);if(LDrawer)
		{
//printf("%s() %s:%d  %sGREEN%s  %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, (long long)LVertexGeometry->VertexAttributes.Count);fflush(stdout);
			E3dVertexGeometryGUI*       LResourcesGUI = (E3dVertexGeometryGUI*)LDrawer->ResourcesGUI;
			const EResourceList*        LVertexAttributes = &LVertexGeometry->VertexAttributes;
			const EIndex                LVAStartIndex = E3dVertexGeometry_DefaultVertexAttributes.Count;
			E3dVertexAttributeGUIList*  LVertexAttributeGUIs = &LResourcesGUI->VertexAttributesGUI;

			if(LVertexGeometry->VertexGeometryGPUData)
			{
				E3dVertexAttributeVisualizationListA*  LVis = &LVertexGeometry->VertexGeometryGPUData->VertexAttributeVisualization;

				const int  LResult = E3dVertexAttributeGUI_Callback(PW, PClientData, PCallData, _DCB_VertexAttribute,  (EObject*)LObject, LVertexAttributes, LVAStartIndex,  LResourcesGUI->VertexAttributesMatrixW, LVertexAttributeGUIs, LVis);
//printf("%s() %s:%d  %s%s%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, EResult_ToString(LResult), ECodeNormal);fflush(stdout);

				if(LResult == E_SELECTED || LResult == E_UNSELECTED || LResult == E_REMOVED) _VertexAttribute_PaintOnOff((E3dObjectPanel*)PClientData);
			}
		}
	}
}


//======================================================================
// VertexGeometry (PointSet, Face, Mesh etc.) dialog callback handler
//======================================================================
static void _DCB_VertexGeometry(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*     LPanel = (E3dObjectPanel*)PClientData;
	E3dVertexGeometry*  LVertexGeometry = (E3dVertexGeometry*)LPanel->Object;
	E3dScene*           LScene = E3d_Scene;

	XeComponentClassDrawer*  LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, LVertexGeometry->Class);if(LDrawer)
	{
		E3dVertexGeometryGUI*  LGUI = (E3dVertexGeometryGUI*)LDrawer->ResourcesGUI;

		if(PW == LGUI->AddVertexAttributeW)
		{
			if(!LGUI->AddingVertexAttribute)
			{
				LGUI->AddingVertexAttribute = TRUE;
				XeWidget  LWs[] = { LGUI->AddVertexAttributeTypeW, LGUI->AddVertexAttributeSemanticW, LGUI->AddVertexAttributeOkW, LGUI->AddVertexAttributeCancelW };
				XeWidgets_Manage(LWs, ECount(LWs));
			}
		}
		else if(PW == LGUI->AddVertexAttributeTypeFloatW)
		{
			LGUI->NewAttributeType = EResFLOAT32;
			if(LGUI->NewAttributeSemantic != NULL)
			{
				LGUI->NewAttributeSemantic = NULL;
				XeOptionMenu_SetOptionByValue(LGUI->AddVertexAttributeSemanticW, 0, TRUE);
			}
		}
		else if(PW == LGUI->AddVertexAttributeTypeFloat2W)
		{
			LGUI->NewAttributeType = EResFLOAT32_2;
			if(LGUI->NewAttributeSemantic != NULL)
			{
				LGUI->NewAttributeSemantic = NULL;
				XeOptionMenu_SetOptionByValue(LGUI->AddVertexAttributeSemanticW, 0, TRUE);
			}
		}
		else if(PW == LGUI->AddVertexAttributeTypeFloat3W)
		{
			LGUI->NewAttributeType = EResFLOAT32_3;
			if(LGUI->NewAttributeSemantic == E3dVtxAttribute_Color)
			{
				XeOptionMenu_SetOptionByValue(LGUI->AddVertexAttributeSemanticW, (EPointer)EpRGB, TRUE);
			}
			else if(LGUI->NewAttributeSemantic != NULL)
			{
				LGUI->NewAttributeSemantic = NULL;
				XeOptionMenu_SetOptionByValue(LGUI->AddVertexAttributeSemanticW, 0, TRUE);
			}
		}
		else if(PW == LGUI->AddVertexAttributeTypeFloat4W)
		{
			LGUI->NewAttributeType = EResFLOAT32_4;
			if(LGUI->NewAttributeSemantic == E3dVtxAttribute_Color)
			{
				XeOptionMenu_SetOptionByValue(LGUI->AddVertexAttributeSemanticW, (EPointer)EpRGBA, TRUE);
			}
			else if(LGUI->NewAttributeSemantic != NULL)
			{
				LGUI->NewAttributeSemantic = NULL;
				XeOptionMenu_SetOptionByValue(LGUI->AddVertexAttributeSemanticW, 0, TRUE);
			}
		}
//		else if(PW == LGUI->AddVertexAttributeTypeUInt8_4W) LGUI->NewAttributeType = EResUIINT8_4;
		else if(PW == LGUI->AddVertexAttributeSemanticNoneW) LGUI->NewAttributeSemantic = NULL;
		else if(PW == LGUI->AddVertexAttributeSemanticRGBW)
		{
			LGUI->NewAttributeSemantic = (char*)E3dVtxAttribute_Color;
			if(LGUI->NewAttributeType != EResFLOAT32_3)
			{
				LGUI->NewAttributeType = EResFLOAT32_3;
				XeOptionMenu_SetOptionByValue(LGUI->AddVertexAttributeTypeW, (EPointer)(EPointerInt)LGUI->NewAttributeType, TRUE);
			}
		}
		else if(PW == LGUI->AddVertexAttributeSemanticRGBAW)
		{
			LGUI->NewAttributeSemantic = (char*)E3dVtxAttribute_Color;
			if(LGUI->NewAttributeType != EResFLOAT32_4)
			{
				LGUI->NewAttributeType = EResFLOAT32_4;
				XeOptionMenu_SetOptionByValue(LGUI->AddVertexAttributeTypeW, (EPointer)(EPointerInt)LGUI->NewAttributeType, TRUE);
			}
		}
		else if(PW == LGUI->AddVertexAttributeOkW)
		{
			XeWidget  LWs[] = { LGUI->AddVertexAttributeTypeW, LGUI->AddVertexAttributeSemanticW, LGUI->AddVertexAttributeOkW, LGUI->AddVertexAttributeCancelW };
			XeWidgets_Unmanage(LWs, ECount(LWs));
			LGUI->AddingVertexAttribute = FALSE;

			EResource*  LAttr;if(E3dVertexGeometry_VertexAttributeAdd(LVertexGeometry,  LGUI->NewAttributeSemantic, "NewAttribute", LGUI->NewAttributeType, 1,  &LAttr) == E_SUCCESS)
			{
				E3dVertexGeometry_VertexAttributeClear(LVertexGeometry, LAttr);

//				_Mesh_GUI((ECoreObject*)LMesh, LPanel, LDrawer, E_UPDATE);
				E3dVertexAttributesGUI_Update(&LVertexGeometry->VertexAttributes, E3dVertexGeometry_DefaultVertexAttributes.Count, LGUI->VertexAttributesMatrixW, &LGUI->VertexAttributesGUI, _DCB_VertexAttribute, LPanel);

				if(LGUI->NewAttributeSemantic == E3dVtxAttribute_Color)
				{
					E3dWindows_RedrawByChangeFlags(LScene, E3dChgWIREFRAME);
				}

//printf("%s() %s:%d  %sAddress%s %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, (long long)LAttr->Address);fflush(stdout);
//				const EIndex  LOffset = EResource_Offset(&LVertexGeometry->VertexAttributes,  NULL, "NewAttribute", 0,  NULL,  NULL);
//				LAttr->UI = (char*)E3dUI_Show0;
			}
		}
		else if(PW == LGUI->AddVertexAttributeCancelW)
		{
			XeWidget  LWs[] = { LGUI->AddVertexAttributeTypeW, LGUI->AddVertexAttributeSemanticW, LGUI->AddVertexAttributeOkW, LGUI->AddVertexAttributeCancelW };
			XeWidgets_Unmanage(LWs, ECount(LWs));
			LGUI->AddingVertexAttribute = FALSE;
		}
		else if(PW == LGUI->VertexAttributePaintW)
		{
			EScriptFunction*  LSF = E3dScriptFunction_ByName("PaintVertexAttributesSettings");if(LSF) EScriptFunction_Call(LSF, NULL);
		}
	}
}

//========================================
// VertexAttribute editing UI
//========================================
void _VertexAttributesGUI(E3dVertexGeometryGUI* PGUI, XeWidget PTopMatrixW, E3dVertexGeometry* PVertexGeometry, const EBool PNew, void* PPanel, const int PAction)
{
	switch(PAction)
	{
		case E_UPDATE:
			if(PNew)
			{
				EList_Init_s(PGUI->VertexAttributesGUI);

				EArg  Args[32];int  ArgCnt;

				const XeCoordinate  LSpacingLeft = XeDrawer_LeftIndent;

				{
					XeWidget  LW;

					EXeStart;
					EXeSetArg(XeNSpacingTop, 8);
					EXeSetArg(XeNSpacingLeft, LSpacingLeft);EXeSetArg(XeNSpacingRight, 8);
					EXeSetArg(XeNOrientation, XeHORIZONTAL);
					EXeSetArg(XeNSpacingX, 4);
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
					EXeSetArg(XeNResizableY, FALSE);
					EXeSetArg(XeNSelfResizePolicyX, XeGROW);EXeSetArg(XeNSelfResizePolicyY, XeGROW);
					XeWidget  LMatrixW = XeWidget_ManagedNew("MatrixW", XeMatrixWidget_class, PTopMatrixW,  Args, ArgCnt);

					EXeStart;
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
					XeWidget_ManagedNew("Vertex attributes", XeLabelWidget_class, LMatrixW,  Args, ArgCnt);

					EXeStart;
					EXeSetArg(XeNSpacingLeft, 4);
					EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNMarginSizeY, 0);
					EXeSetArg(XeNResizableX, FALSE);
					EXeSetArg(XeNShadowThickness, 0);EXeSetArg(XeNLabelType, XeIMAGE);EXeSetArg(XeNImageSizeYPtr, &Xe_IconSize);EXeSetImages2(_Xe_Theme->Image_Plus);
					EXeSetArg(XeNTipStringUTF8, "Add a new VertexAttribute.");
					PGUI->AddVertexAttributeW = LW = XeWidget_ManagedNew("Add", XePushButton_class, LMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);

					{
						EXeStart;
						EXeSetArg(XeNSpacingLeft, 4);
						EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);
						EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
						EXeSetArg(XeNButtonColorPtr, &Xe_ButtonRGBAiColor);
						EXeSetArg(XeNFontList, XeMenu_FontList);
						EXeSetArg(XeNLabelFontList, Xe_LabelFontList);
						EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNMarginSizeY, 0);
						EXeSetArg(XeNSpacingY, 0);
						EXeSetArg(XeNCascadeButtonMarginSizeY, 0);
						EXeSetArg(XeNMenuPaneBackgroundColor, &XeOptionMenu_RGBAiColor);
						EXeSetArg(XeNResizableX, FALSE);
						EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
						EXeSetArg(XeNResizableY, FALSE);
						PGUI->AddVertexAttributeTypeW = LW = XeOptionMenu_CreateAndInit(LMatrixW, NULL,  NULL, 0,  (EPointer)EResFLOAT32, "Type", NULL, NULL,  Args, ArgCnt);

						XeWidget  LMenuPaneW = XeOptionMenu_GetPane(PGUI->AddVertexAttributeTypeW);

						EXeStart;
						EXeSetArg(XeNMarginSizeY, 0);EXeSetArg(XeNMarginRight, 3);EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNBackgroundColorPtr, &XeOptionMenu_RGBAiColor);
						EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
						EXeSetArg(XeNArmColorPtr, &XeMenu_ButtonArmRGBAiColor);
						EXeSetArg((char*)XeOptionMenu_ButtonValueResource, (EPointer)EResFLOAT32);
						PGUI->AddVertexAttributeTypeFloatW = LW = XeWidget_ManagedNew("float", XePushButton_class, LMenuPaneW,  Args, ArgCnt);
						XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);

						EXeStart;
						EXeSetArg(XeNMarginSizeY, 0);EXeSetArg(XeNMarginRight, 3);EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNBackgroundColorPtr, &XeOptionMenu_RGBAiColor);
						EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
						EXeSetArg(XeNArmColorPtr, &XeMenu_ButtonArmRGBAiColor);
						EXeSetArg((char*)XeOptionMenu_ButtonValueResource, (EPointer)EResFLOAT32_2);
						PGUI->AddVertexAttributeTypeFloat2W = LW = XeWidget_ManagedNew("float2", XePushButton_class, LMenuPaneW,  Args, ArgCnt);
						XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);

						EXeStart;
						EXeSetArg(XeNMarginSizeY, 0);EXeSetArg(XeNMarginRight, 3);EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNBackgroundColorPtr, &XeOptionMenu_RGBAiColor);
						EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
						EXeSetArg(XeNArmColorPtr, &XeMenu_ButtonArmRGBAiColor);
						EXeSetArg((char*)XeOptionMenu_ButtonValueResource, (EPointer)EResFLOAT32_3);
						PGUI->AddVertexAttributeTypeFloat3W = LW = XeWidget_ManagedNew("float3", XePushButton_class, LMenuPaneW,  Args, ArgCnt);
						XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);

						EXeStart;
						EXeSetArg(XeNMarginSizeY, 0);EXeSetArg(XeNMarginRight, 3);EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNBackgroundColorPtr, &XeOptionMenu_RGBAiColor);
						EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
						EXeSetArg(XeNArmColorPtr, &XeMenu_ButtonArmRGBAiColor);
						EXeSetArg((char*)XeOptionMenu_ButtonValueResource, (EPointer)EResFLOAT32_4);
						PGUI->AddVertexAttributeTypeFloat4W = LW = XeWidget_ManagedNew("float4", XePushButton_class, LMenuPaneW,  Args, ArgCnt);
						XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);
					}

					{
						EXeStart;
						EXeSetArg(XeNSpacingLeft, 4);
						EXeSetArg(XeNBackgroundColorPtr, &Xe_BackgroundRGBAiColor);
						EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
						EXeSetArg(XeNButtonColorPtr, &Xe_ButtonRGBAiColor);
						EXeSetArg(XeNFontList, XeMenu_FontList);
						EXeSetArg(XeNLabelFontList, Xe_LabelFontList);
						EXeSetArg(XeNMarginSizeX, 0);EXeSetArg(XeNMarginSizeY, 0);
						EXeSetArg(XeNSpacingY, 0);
						EXeSetArg(XeNCascadeButtonMarginSizeY, 0);
						EXeSetArg(XeNMenuPaneBackgroundColor, &XeOptionMenu_RGBAiColor);
						EXeSetArg(XeNResizableX, FALSE);
						EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
						EXeSetArg(XeNResizableY, FALSE);
						PGUI->AddVertexAttributeSemanticW = LW = XeOptionMenu_CreateAndInit(LMatrixW, NULL,  NULL, 0,  (EPointer)NULL, "", NULL, NULL,  Args, ArgCnt);

						XeWidget  LMenuPaneW = XeOptionMenu_GetPane(PGUI->AddVertexAttributeSemanticW);

						EXeStart;
						EXeSetArg(XeNMarginSizeY, 0);EXeSetArg(XeNMarginRight, 3);EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNBackgroundColorPtr, &XeOptionMenu_RGBAiColor);
						EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
						EXeSetArg(XeNArmColorPtr, &XeMenu_ButtonArmRGBAiColor);
						EXeSetArg((char*)XeOptionMenu_ButtonValueResource, 0);
						PGUI->AddVertexAttributeSemanticNoneW = LW = XeWidget_ManagedNew("Custom", XePushButton_class, LMenuPaneW,  Args, ArgCnt);
						XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);

						EXeStart;
						EXeSetArg(XeNMarginSizeY, 0);EXeSetArg(XeNMarginRight, 3);EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNBackgroundColorPtr, &XeOptionMenu_RGBAiColor);
						EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
						EXeSetArg(XeNArmColorPtr, &XeMenu_ButtonArmRGBAiColor);
						EXeSetArg((char*)XeOptionMenu_ButtonValueResource, EpRGB);
						PGUI->AddVertexAttributeSemanticRGBW = LW = XeWidget_ManagedNew("RGB", XePushButton_class, LMenuPaneW,  Args, ArgCnt);
						XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);

						EXeStart;
						EXeSetArg(XeNMarginSizeY, 0);EXeSetArg(XeNMarginRight, 3);EXeSetArg(XeNShadowThickness, 0);
						EXeSetArg(XeNBackgroundColorPtr, &XeOptionMenu_RGBAiColor);
						EXeSetArg(XeNForegroundColorPtr, &Xe_ForegroundRGBAiColor);
						EXeSetArg(XeNArmColorPtr, &XeMenu_ButtonArmRGBAiColor);
						EXeSetArg((char*)XeOptionMenu_ButtonValueResource, EpRGBA);
						PGUI->AddVertexAttributeSemanticRGBAW = LW = XeWidget_ManagedNew("RGBA", XePushButton_class, LMenuPaneW,  Args, ArgCnt);
						XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);
					}


					EXeStart;
					EXeSetArg(XeNMarginSizeX, 4);EXeSetArg(XeNMarginSizeY, 0);
					EXeSetArg(XeNResizableY, FALSE);
					EXeSetArg(XeNTipStringUTF8, "Add VertexAttribute.");
					PGUI->AddVertexAttributeOkW = LW = XeWidget_New("Add", XePushButton_class, LMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);

					EXeStart;
					EXeSetArg(XeNMarginSizeX, 4);EXeSetArg(XeNMarginSizeY, 0);
					EXeSetArg(XeNResizableY, FALSE);
					EXeSetArg(XeNTipStringUTF8, "Cancel adding VertexAttribute.");
					PGUI->AddVertexAttributeCancelW = LW = XeWidget_New("Cancel", XePushButton_class, LMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);

					EXeStart;
					EXeSetArg(XeNMarginSizeX, 4);EXeSetArg(XeNMarginSizeY, 0);
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNResizableY, FALSE);
					EXeSetArg(XeNTipStringUTF8, "Paint values of the selected VertexAttribute on the Mesh.");
					PGUI->VertexAttributePaintW = LW = XeWidget_New("Paint", XePushButton_class, LMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNActivateCallback, _DCB_VertexGeometry, (EPointer)PPanel);

					PGUI->NewAttributeType = EResFLOAT32;
					PGUI->NewAttributeSemantic = NULL;
					PGUI->AddingVertexAttribute = FALSE;
				}

				EXeStart;
				EXeSetArg(XeNSpacingTop, 2);
				EXeSetArg(XeNSpacingLeft, LSpacingLeft);
				EXeSetArg(XeNOrientation, XeHORIZONTAL);
				EXeSetArg(XeNColumns, 4);
				EXeSetArg(XeNRadioBehavior, TRUE);EXeSetArg(XeNRadioAlwaysOne, FALSE);
				EXeSetArg(XeNSpacingX, 6);EXeSetArg(XeNSpacingY, 0);
				EXeSetArg(XeNResizableY, FALSE);
				XeWidget  LMatrixW = XeWidget_ManagedNew("VertexAttributesMatrixW", XeMatrixWidget_class, PTopMatrixW,  Args, ArgCnt);
				PGUI->VertexAttributesMatrixW = LMatrixW;

				E3dVertexAttributesGUI_Update(&PVertexGeometry->VertexAttributes, E3dVertexGeometry_DefaultVertexAttributes.Count, PGUI->VertexAttributesMatrixW, &PGUI->VertexAttributesGUI, _DCB_VertexAttribute, PPanel);

				EXeStart;
				EXeSetArg(XeNSpacingTop, 2);
				EXeSetArg(XeNShadowThickness, 2);
				EXeSetArg(XeNShadowType, XeShadowETCHED_IN);
				EXeSetArg(XeNResizableY, FALSE);
				XeWidget_ManagedNew("", XeSeparatorWidget_class, PTopMatrixW,  Args, ArgCnt);
			}
			else
			{
				E3dVertexAttributesGUI_Update(&PVertexGeometry->VertexAttributes, E3dVertexGeometry_DefaultVertexAttributes.Count, PGUI->VertexAttributesMatrixW, &PGUI->VertexAttributesGUI,  _DCB_VertexAttribute, PPanel);
			}
		break;

		case E_FREE:
			{
				EList_Free_s(PGUI->VertexAttributesGUI);
printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
			}
		break;
	}
}



//========================================
// Update PointSet stats label
//========================================
static void _PointSetUpdateStatsLabel(E3dPointSet* PPointSet, XeWidget PStatsValLW)
{
	char  LTmpStr[256];sprintf(LTmpStr, "%lld", (long long)(PPointSet->Vertices.Count));

	XeString  LXeStr = XeString_NewVa(TRUE,  XeFONT, XeFontDEFAULT,  XeALIGNMENT, XeStringAlignRIGHT,  XeTEXT, LTmpStr,  0);if(LXeStr)
	{
		EArg  Args[2];int  ArgCnt;

		EXeStart;
		EXeSetArg(XeNLabelString, LXeStr);
		Xe_SetValues(PStatsValLW,  Args, ArgCnt);

		XeString_Free(LXeStr);
	}
}


typedef struct
{
	E3dVertexGeometryGUICore;

	XeWidget  PointSizeW, StatsValuesLabelW;
} E3dPointSetResourcesGUI;


//========================================
// Dialog box callback handler
//========================================
static void _DCB_PointSet(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	E3dObjectPanel*           LPanel = (E3dObjectPanel*)PClientData;
	E3dPointSet*              LPointSet = (E3dPointSet*)LPanel->Object;
	XeComponentClassDrawer*   LDrawer = XeInfoPanel_GetFolderByClass((XeInfoPanel*)LPanel, E3dPointSet_class);
	E3dPointSetResourcesGUI*  LGUI = (E3dPointSetResourcesGUI*)LDrawer->ResourcesGUI;

	if(PW == LGUI->PointSizeW) LPointSet->PointDiameter = ((XeScaleCallbackStruct*)PCallData)->Value.i32;

	E3dWindows_RedrawByChangeFlags(E3d_Scene, E3dChgWIREFRAME);
}


//================================================================
// GUIPFunc for E3dPointSet
//================================================================
void _PointSet_GUI(ECoreObject* PObject, XeInfoPanel* PPanel, void* PDrawer, const int PAction)
{
	XeComponentClassDrawer*   LDrawer = (XeComponentClassDrawer*)PDrawer;
	E3dPointSetResourcesGUI*  LGUI;
	E3dPointSet*              LPointSet = (E3dPointSet*)PObject;

	switch(PAction)
	{
		case E_UPDATE:
			if(LDrawer->ResourcesGUI)
			{
				LGUI = (E3dPointSetResourcesGUI*)LDrawer->ResourcesGUI;

				XeScale_SetValue(LGUI->PointSizeW, LPointSet->PointDiameter, FALSE);
				_PointSetUpdateStatsLabel((E3dPointSet*)PObject, LGUI->StatsValuesLabelW);

				_VertexAttributesGUI((E3dVertexGeometryGUI*)LGUI, NULL, (E3dVertexGeometry*)PObject, FALSE, PPanel, PAction);
			}
			else
			{
				XeWidget  LTopMatrixW = LDrawer->Drawer.DrawerW;
				char      LTmpStr[256];

				ENew(LGUI);LDrawer->ResourcesGUI = LGUI;

				EArg  Args[16];int  ArgCnt;

				{
					EXeStart;
					EXeSetArg(XeNOrientation, XeHORIZONTAL);
					EXeSetArg(XeNSpacingX, 8);
					EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNAlignmentX, XeAlignBEGINNING);
					EXeSetArg(XeNResizableY, FALSE);
					XeWidget  LMatrixW = XeWidget_ManagedNew("", XeMatrixWidget_class, LTopMatrixW,  Args, ArgCnt);

					EXeStart;
					EXeSetArg(XeNSpacingLeft, XeDrawer_LeftIndent);
					sprintf(LTmpStr, "Points:");
					XeString  LXeStr = XeString_NewVa(TRUE,  XeFONT, XeFontDEFAULT,  XeALIGNMENT, XeStringAlignLEFT,  XeTEXT, LTmpStr,  0);
					if(LXeStr) EXeSetArg(XeNLabelString, LXeStr);
					XeWidget_ManagedNew("", XeLabelWidget_class, LMatrixW, Args, ArgCnt);
					if(LXeStr) XeString_Free(LXeStr);

					EXeStart;
					LGUI->StatsValuesLabelW = XeWidget_ManagedNew("", XeLabelWidget_class, LMatrixW, Args, ArgCnt);

					_PointSetUpdateStatsLabel((E3dPointSet*)PObject, LGUI->StatsValuesLabelW);

					XeWidget  LW;
					EXeStart;
					EXeSetArg(XeNSpacingLeft, XeDrawer_LeftIndent);
					EXeSetArg(XeNTextFieldDigits, 3);
					EXeSetArg(XeNMinimum, 1);EXeSetArg(XeNMaximum, 100);
					EXeSetArg(XeNScaleMaximum, 20);
					EXeSetArg(XeNValue, LPointSet->PointDiameter);
					EXeSetArg(XeNResizableY, FALSE);
					LGUI->PointSizeW = LW = XeWidget_ManagedNew("Point size", XeScaleWidget_class, LTopMatrixW,  Args, ArgCnt);
					XeAddCallback(LW, XeNValueChangedCallback, _DCB_PointSet, (EPointer)PPanel);
				}

// VertexAttributes
//
				_VertexAttributesGUI((E3dVertexGeometryGUI*)LGUI, LTopMatrixW, (E3dVertexGeometry*)PObject, TRUE, PPanel, PAction);
			}
		break;
	}
}


//========================================================================
// Callback for Object changes to update the appropriate ObjectPanel
//========================================================================
static EChangeFlags _ObjectChanged(EInterface* PInterface, EInterfaceCallbackStruct* PCBS)
{
	E3dObjectPanel*  LPanel = (E3dObjectPanel*)PInterface->ClientData;

printf("%s() %s:%d  <%s>\n", __FUNCTION__, __FILE__, __LINE__, LPanel->Object ? LPanel->Object->Name : "");fflush(stdout);
	if(LPanel->Object) E3dObjectPanel_Update(LPanel, LPanel->Object,  NULL, NULL);

// We don't change the Object, so return 0
//
	return(0);
}


static EChangeFlags _ObjectDoneEditing(E3dObjectPanel* PPanel)
{
	EChangeFlags  LChanged = 0;

	if(PPanel->Object)
	{
		if(E3dObject_LockDel(PPanel->Object, &E3dObject_EditLock)) LChanged |= E3dChgLOCK_UNLOCK;

		if(EObject_IsSubClass(PPanel->Object, E3dSpline))
		{
			E3dSpline*    LSpline = (E3dSpline*)(PPanel->Object);
			E3dSplineCV*  LCVs = LSpline->CVs.Items;

			EIndex  LI = 0;const EIndex  LN = LSpline->CVs.Count;for(;LI < LN;LI++) LCVs[LI].CVFlags &= ~E3dCV_ACTIVE;
			LChanged |= E3dChgSELECTION;
		}

		if(EObject_IsSubClass(PPanel->Object, E3dGeometry))
		{
			E3dBaseObject_RemoveOutput((E3dBaseObject*)PPanel->Object, &PPanel->InputInterface);
		}
		E_Delete(PPanel->Object);
		E_DeleteChk(PPanel->SubObject);
	}

	return(LChanged);
}


//========================================
// Close Object dialog-box
//========================================
static void _ObjectDialogClose(E3dObjectPanel* PPanel, const EBool PRedraw)
{
	if(PPanel && PPanel->Object)
	{
		E3dObject*  LObject = PPanel->Object;

		{
			EIndex  LI = 0;for(;LI < LObject->Components_Count;LI++)
			{
				E3dObjectComponent*  LInfo = LObject->Components[LI];
				if(LInfo->Class && LInfo->Class->GUI) LInfo->Class->GUI(LObject, LInfo, PPanel, NULL, E_CLOSE);
			}
		}

		EChangeFlags	LChanged = 0;

		XeWidget_Unmap(_ObjectDialogW);
		LChanged |= _ObjectDoneEditing(PPanel);

		E3dScene*  LScene = E3d_Scene;
		E3dWindows_RedrawByChangeFlags(LScene, LChanged);

		E3dScene_CallbackRemove(LScene, _CB_ObjectSelection, (EPointer)0);_ObjectSelectionCallbackAdded = FALSE;
	}
}


//========================================
// Object dialog-box callback handler
//========================================
static void _DCB_ObjectDialogClose(XeWidget PW, EPointer PClientData, EPointer PCallData)
{
	XeCallbackStruct*  LCBS = (XeCallbackStruct*)PCallData;
	if(LCBS->Reason == XeCR_PROTOCOLS)
	{
		_ObjectDialogClose((E3dObjectPanel*)PClientData, TRUE);
		EPluginLock_Del(_EditInfoPlugin, &_ObjectDialogLock, FALSE);
	}
}


static void _EH_CloseDialogBox(XeWidget PW, XeEvent* PEvent, EPointer PClientData, EBool* PCdr)
{
	switch(PEvent->Type)
	{
		case XeKeyPress:
			{
				const XeKeyEvent*  LEvent = (const XeKeyEvent*)PEvent;
//printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
				if((LEvent->XeKeySym & 0xFFFF) == EKeyESCAPE)  // Esc key
				{
printf("%s() %s:%d  %04llx\n", __FUNCTION__, __FILE__, __LINE__, (unsigned long long)(LEvent->XeKeySym & 0xFFFF));fflush(stdout);
					_ObjectDialogClose((E3dObjectPanel*)PClientData, TRUE);
					EPluginLock_Del(_EditInfoPlugin, &_ObjectDialogLock, FALSE);
				}
			}
		break;

		case XeKeyRelease:
		break;
	}
}


static int _ObjectDialogLockDel(EPlugin* PPlugin, EPluginLock* PLock)
{
	_ObjectDialogClose(_ObjectPanel, FALSE);

	return(E_SUCCESS);
}


//========================================================
// Common dialog-box creator for Mesh, Light etc.
//========================================================
static void _ObjectPopupDialog(E3dModel* PModel, E3dObject* PObject, EObject* PSubObject, EIndex PPointIndex, E3dObjectComponentClass* POpenFolderForThisComponentClass)
{
	char       LTitle[256];
	char*      LClassName = PObject->Class->Name;
	E3dScene*  LScene = E3d_Scene;

	if(strncmp("E3d", LClassName, 3) == 0) LClassName += 3;
	if(strlen(LClassName) < 32) sprintf(LTitle, "%s attributes", LClassName);
	else sprintf(LTitle, "Attributes");

	if(_ObjectDialogLock == NULL) _ObjectDialogLock = EPluginLock_Add(_EditInfoPlugin, "EditObject", _ObjectDialogLockDel);

	E3dObjectPanel*  LPanel;
	if(_ObjectDialogW == NULL)
	{
		EArg  Args[32];int  ArgCnt;

		EXeStart;
		EXeSetArg(XeNAllowShellResize, TRUE);
		EXeSetArg(XeNButtonsStretch, FALSE);
		EXeSetArg(XeNMinSizeX, 260);EXeSetArg(XeNMinSizeY, 100);
		EXeSetArg(XeNWorkAreaType, XeMATRIX);
		EXeSetArg(XeNCreateButtons, FALSE);
		EXeSetArg(XeNWMDecorations, XeWMDecorCLOSE|XeWMDecorBORDER|XeWMDecorRESIZEH|XeWMDecorTITLE);
		EXeSetArg(XeNTitle, LTitle);EXeSetArg(XeNIconName, LClassName);
		XeWidget  LDialogW = XeDialog_New(XeDIALOG_TEMPLATE, LClassName, E3dp_TopShellW,  Args, ArgCnt);if(LDialogW == NULL) return;

		_ObjectDialogW = LDialogW;

		XeWidget  LTopMatrixW = XeDialog_GetChild(LDialogW, XeDIALOG_WORK_AREA);
		EXeStart;
		EXeSetArg(XeNOrientation, XeVERTICAL);
		Xe_SetValues(LTopMatrixW,  Args, ArgCnt);

		_ObjectPanel = LPanel = E3dObjectPanel_New(LTopMatrixW, ~0, PObject, PSubObject, PPointIndex,  POpenFolderForThisComponentClass);
		XeEventHandler_Add(LPanel->NameW, XeKeyPressMask|XeKeyReleaseMask, TRUE, _EH_CloseDialogBox, (EPointer)LPanel);
		XeAddCallback(LDialogW, XeNWMCallback, _DCB_ObjectDialogClose, (EPointer)LPanel);

		XeShell_Raise(LDialogW);
		XeShell_CenterToWidget(LDialogW, E3dp_TopShellW);

		if(E3dObject_LockAdd(PObject, &E3dObject_EditLock)) E3dWindows_RedrawByChangeFlags(LScene, E3dChgLOCK_UNLOCK);

// Initialize InputInterface for _Objecthanged()
//
		{
			EInterface*  LInput = &LPanel->InputInterface;  // EInterface_Init() was called in E3dObjectPanel_New
			LInput->Name = EStrDup("Object change");
			LInput->InputChanged = _ObjectChanged;
			LInput->ClientData = (EPointer)LPanel;  // We use ClientData, rather than Object, because E3dObjectPanel is not an EObject-derivative
//		LInput->Object = (EObject*)LPanel;
			LInput->Internal = TRUE;
		}

		if(EObject_IsSubClass(PObject, E3dGeometry)) E3dBaseObject_AddOutput((E3dBaseObject*)PObject, &LPanel->InputInterface);
	}
	else
	{
// Dialog-box was already created.
//
		XeWidget      LDialogW = _ObjectDialogW;
		EChangeFlags  LChanged = 0;

		LPanel = _ObjectPanel;

		XeDialog_SetValuesVa(LDialogW,  XeNTitle, LTitle,  XeNIconName, LClassName,  NULL);

		if(LPanel->Object == PObject)
		{
//printf("%s() %s:%d <%s> <%s>\n", __FUNCTION__, __FILE__, __LINE__, PObject->Name, PSubObject->Name);fflush(stdout);

// Same Object
//
			E3dObjectPanel_UpdateSubSub(LPanel, PObject, PSubObject, PPointIndex,  NULL, POpenFolderForThisComponentClass);
			XeShell_Raise(LDialogW);
			return;
		}
		else if(LPanel->Object)
		{
// A different Object was being edited on this Panel
//
			LChanged |= _ObjectDoneEditing(LPanel);
		}

		E3dObjectPanel_UpdateSubSub(LPanel, PObject, PSubObject, PPointIndex,  NULL, POpenFolderForThisComponentClass);
		if(E3dObject_LockAdd(PObject, &E3dObject_EditLock)) LChanged |= E3dChgLOCK_UNLOCK;

		E3dWindows_RedrawByChangeFlags(LScene, LChanged);

		if(EObject_IsSubClass(PObject, E3dGeometry)) E3dBaseObject_AddOutput((E3dBaseObject*)PObject, &LPanel->InputInterface);
	}

// Prevent the dialog box stealing keyboard focus, by only raising it, if it's not visible
//
	if(XeShell_DisplayStatus(_ObjectDialogW) != XeDISPLAYED) XeShell_Raise(_ObjectDialogW);

	if(Eqx_Prefs.SelectAndFocusOnName)
	{
		XeDisplay*  LDisplay = Xe_GetDisplay(LPanel->NameW);
		if(!E_IsKeyDown(LDisplay, 0))
		{
			Xe_SetFocus(LPanel->NameW, TRUE, TRUE);
			XeTextField_SelectAll(LPanel->NameW);
		}
	}

	if(!_ObjectSelectionCallbackAdded)
	{
		E3dScene_CallbackAdd(E3d_Scene, E3dChgSELECTION, _CB_ObjectSelection, (EPointer)0);_ObjectSelectionCallbackAdded = TRUE;
	}
}


//========================================
// Edit Object attributes
//========================================
void _Object_Edit(E3dModel* PModel, E3dObject* PObject, const EIndex PLODIndex,  EObject* PSubObject, const EIndex PPointIndex,  E3dObjectComponentClass* POpenFolderForThisComponentClass)
{
//	E3dObject*  LLOD = PObject;if(PLODIndex) LLOD = (E3dObject*)(((E3dGeometry*)PObject)->LODs[PLODIndex - 1]);

	if(EObject_IsSubClass(PObject, E3dGeometry))
	{
//		E3dGeometry*  LGeometry = (E3dGeometry*)PObject;LGeometry->AutoLOD = FALSE;
		_ObjectPopupDialog(PModel, PObject, PSubObject, PPointIndex,  POpenFolderForThisComponentClass);
	}
	else _ObjectPopupDialog(PModel, PObject, NULL, -1,  POpenFolderForThisComponentClass);
}


//========================================
// Scene changed callback
// Called from Edit.c
//========================================
static void _CB_ObjectSelection(ECoreObject* PScn, EPointer PClientData, ECallbackStruct* PCallData)
{
	E3dObjectPanel*  LPanel = _ObjectPanel;

	if(LPanel && LPanel->FollowSelection)
	{
		switch(PCallData->Reason)
		{
			case E3dCR_SPLINE_SEGMENT_SELECTED:
			 {
				 E3dSceneObjectCallbackStruct*  LCBSO = (E3dSceneObjectCallbackStruct*)PCallData;
				 E3dObject*                     LObject = LCBSO->Object;
				 E3dModel*                      LModel = LObject->Models.Items[0];

				 if(EObject_IsSubClass(LObject, E3dFace)) _Object_Edit(LModel, LObject, 0,  LCBSO->SubObject, LCBSO->PointIndex,  NULL);
				 else _Object_Edit(LModel, LObject, 0,  NULL, LCBSO->PointIndex,  NULL);
			 }
			break;

			case E3dCR_SELECTION_CHANGED:
				{
					E3dItem	LSelectedItem;

					switch(E3dScene_GetSingleSelection(E3d_Scene, &LSelectedItem, E3dItemALL, 0))
					{
						case E3dItemOBJECT:
						case E3dItemCAMERA:
						case E3dItemLIGHT:
						case E3dItemPOINTSET:
						case E3dItemSPLINE:
						case E3dItemFACE:
						case E3dItemMESH:
							{
								E3dObjItem*  LObjItem = &LSelectedItem.ObjItem;
								_Object_Edit((E3dModel*)(LObjItem->Node), LObjItem->Object, 0,  NULL, 0,  NULL);
							}
						break;

						case E3dItemPOLYGROUP:
							if(_PolyGroupUI_GetEdited())
							{
								E3dPolyItem*  LPolyItem = &LSelectedItem.PolyItem;

								_PolyGroupUI_PopupDialog(LPolyItem->PolyGroup, NULL, FALSE);
							}
						break;
					}
				}
			break;

			case E3dCR_POINTS_SELECTION:
				{
					E3dSceneObjectCallbackStruct*  LCBSO = (E3dSceneObjectCallbackStruct*)PCallData;
					E3dObject*                     LObject = LCBSO->Object;if(LObject)
					{
						E3dModel*  LModel = LObject->Models.Items[0];

						_Object_Edit(LModel, LObject, 0,  NULL, 0,  NULL);
					}
				}
			break;
		}
	}
}


//========================================================================================
// Return TRUE, if PNode's attributes are being edited (the dialog box is displayed)
//========================================================================================
static EBool _Object_IsEdited(E3dObject* PObject)
{
	if(_ObjectPanel && _ObjectPanel->Object == PObject) return(TRUE);
	return(FALSE);
}


void E3dObjectEdit_Init(void)
{
	E3dObject_EditFunc = _Object_Edit;
	E3dObject_IsEditedFunc = _Object_IsEdited;
}


//========================================
// Clean up EditObject
//========================================
void E3dObjectEdit_Cleanup(void)
{
	E3dScene*  LScene = E3d_Scene;

	E3dObject_EditFunc = NULL;
	E3dObject_IsEditedFunc = NULL;

	if(_ObjectPanel) { E3dObjectPanel_Free(_ObjectPanel);_ObjectPanel = NULL; }

	if(_PolyGroupUI_SceneCallbackAdded)
	{
		E3dScene_CallbackRemove(LScene, _EditCB_ScenePolyGroup, (EPointer)0);_PolyGroupUI_SceneCallbackAdded = FALSE;
	}

//	XeWidget_Free(_ObjectDialogW);  // Don't free top-level shells
}
