//================================================================================
// Xe GUI API
//
// Drawers (pull-down panels, with an open/close arrow)
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Jan-07 23:18:17
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================================
#ifndef _XeDrawer_h
#define _XeDrawer_h

#ifndef _EClass_h
#include <EClass.h>
#endif

#include <Xe/PushButton.h>

#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif

// For XeDrawer_NewVa
//
#define XeDrawerCREATE_TOP_MATRIX       0x0001
#define XeDrawerCREATE_SEPARATOR        0x0002
#define XeDrawerOPEN                    0x0004  // Open the drawer at creation
#define XeDrawerBUTTON_IN_H_MATRIX      0x0008  // Create a horizontal XeMatrix for the open/close button to allow for other Widgets next to it
#define XeDrawerACTIVE_INACTIVE_TOGGLE  0x0010
#define XeDrawerACTIVE                  0x0020
#define XeDrawerCREATE_IMAGE            0x0040
#define XeDrawerUNMANAGED               0x0080

//----------------------------------------
// For Folder panels
//----------------------------------------
typedef struct
{
	char            Name[64];
	XeWidget        ParentW, ButtonMatrixW, OpenCloseButtonW, ActiveButtonW, TopMatrixW, LabelW, DrawerW, SeparatorW, CloseButtonW;
	XeCallbackFunc  OpenCloseCallback;EPointer  OpenCloseCallbackClientData;
	EBool           Open;
} XeDrawer;

#ifndef XeInfoPanel_DEF
#define XeInfoPanel_DEF  1
typedef struct XeInfoPanel_S   XeInfoPanel;
typedef struct XeInfoPanel_S*  XeInfoPanelPtr;
EListA_t(XeInfoPanelPtr);
#endif

typedef struct
{
	XeInfoPanel*  Panel;      // Parent panel
	ECoreClass*   Class;
	XeDrawer      Drawer;
	void*         ResourcesGUI;
	EBool         Show;
} XeComponentClassDrawer, *XeComponentClassDrawerPtr;
EList_t(XeComponentClassDrawerPtr);

#define XeInfoPanelCore\
	XeWidget                    NameMatrixW, FollowSelectionW, ApplyChangesToAllSelectedW;\
	XeWidget                    ClassFoldersParentW;\
	EList_s(, XeComponentClassDrawerPtr, ClassFolders);           /* One for each static Class (inheritance)             */\
	EList_s(, XeComponentClassDrawerPtr, ComponentClassFolders);  /* One for each dynamic Object Component (composition  */\
	EBool                       FollowSelection,                  /* Update panel when the selection changes             */\
	                            ApplyChangesToAllSelected         /* Apply changes made by the user to all selected, compatible Objects  */

struct XeInfoPanel_S
{
	XeInfoPanelCore;
};



//========================================
// Create/update static class folders
//========================================
#define XeInfoPanel_ClassFoldersUpdate(pPanel, pType, pObject, pCreateDrawers, pOpenStaticSubclassFolder) do\
{\
	XeInfoPanel*  lPanel = pPanel;\
	if(pObject)\
	{\
		pType##Class*  lClasses[EMAX_CLASS_DEPTH];\
		int            lClassN = 0;\
\
		pType##Class*	 lTClass = pObject->Class;while(lTClass)\
		{\
			lClasses[lClassN] = lTClass;\
			lTClass = lTClass->SuperClass;\
			lClassN++;\
		}\
\
		{\
			int  lClassI = 0;for(;lClassI < lClassN;lClassI++)\
			{\
				pType##Class*  lClass = lClasses[lClassN - lClassI - 1];if(lClass->GUI)\
				{\
					XeComponentClassDrawer*  lClassFolder = XeInfoPanel_GetFolderByClass(lPanel, lClass);if(lClassFolder == NULL)\
					{\
						EArg  Args[16];int  ArgCnt;\
\
						ENewClr(lClassFolder);\
\
						lClassFolder->Class = (ECoreClass*)lClass;\
						XeDrawer*  lFolder = &lClassFolder->Drawer;\
\
						if(pCreateDrawers)\
						{\
							if(EStr_NEqual(lClass->Name, "E3d", 3)) XeDrawer_NewVa(lPanel->ClassFoldersParentW, lFolder, lClass->Name + 3, 0, XeDrawerCREATE_SEPARATOR|XeDrawerBUTTON_IN_H_MATRIX|XeDrawerUNMANAGED,  NULL);\
							else XeDrawer_NewVa(lPanel->ClassFoldersParentW, lFolder, lClass->Name, 0, XeDrawerCREATE_SEPARATOR|XeDrawerBUTTON_IN_H_MATRIX|XeDrawerUNMANAGED,  NULL);\
						}\
						else lFolder->ParentW = lPanel->ClassFoldersParentW;\
\
						EXeStart;\
						EXeSetArg(XeNOrientation, XeVERTICAL);\
						EXeSetArg(XeNSpacingY, 0);\
						EXeSetArg(XeNResizableY, FALSE);\
						lFolder->DrawerW = XeWidget_New("LFolderW", XeMatrixWidget_class, lFolder->ParentW,  Args, ArgCnt);\
\
						EList_PointerAdd_s(lPanel->ClassFolders, lClassFolder);\
\
/* Make sure the static-class folders stay before the dynamic-class folders	*/\
\
						if(lPanel->ComponentClassFolders.Items)\
						{\
							XeWidget  lBeforeW = lPanel->ComponentClassFolders.Items[0]->Drawer.SeparatorW;if(lBeforeW)\
							{\
								XeCompositeRec*  lMenuW = (XeCompositeRec*)lPanel->ClassFoldersParentW;\
								XeWidget*  lChildrenW = lMenuW->Children;const int  lNChildren = lMenuW->Children_Count;if(lNChildren > 1)\
								{\
									if(lFolder->SeparatorW)\
									{\
										int  lI = 0;for(;lI < lNChildren;lI++)\
										{\
											if(lChildrenW[lI] == lBeforeW)\
											{\
												memmove(lChildrenW + lI + 1, lChildrenW + lI, sizeof(XeWidget) * (lNChildren - lI - 1));\
												lChildrenW[lI] = lFolder->SeparatorW;\
												break;\
											}\
										}\
									}\
\
									if(lFolder->ButtonMatrixW)\
									{\
										int  lI = 0;for(;lI < lNChildren;lI++)\
										{\
											if(lChildrenW[lI] == lBeforeW)\
											{\
												memmove(lChildrenW + lI + 1, lChildrenW + lI, sizeof(XeWidget) * (lNChildren - lI - 1));\
												lChildrenW[lI] = lFolder->ButtonMatrixW;\
												break;\
											}\
										}\
									}\
\
									{\
										int  lI = 0;for(;lI < lNChildren;lI++)\
										{\
											if(lChildrenW[lI] == lBeforeW)\
											{\
												memmove(lChildrenW + lI + 1, lChildrenW + lI, sizeof(XeWidget) * (lNChildren - lI - 1));\
												lChildrenW[lI] = lFolder->DrawerW;\
												break;\
											}\
										}\
									}\
								}\
							}\
						}\
					}\
				}\
			}\
		}\
\
\
/* Manage/unmanage static subclass folders	*/\
		{\
			const EIndex  lCN = lPanel->ClassFolders.Count;\
\
/* Turn on folders for the appropriate classes	*/\
\
			{\
				EIndex  lCI = 0;for(;lCI < lCN;lCI++)\
				{\
					XeComponentClassDrawer*  lClassFolder = lPanel->ClassFolders.Items[lCI];\
\
					lClassFolder->Show = FALSE;\
					int  lClassI = 0;for(;lClassI < lClassN;lClassI++)\
					{\
						pType##Class*  lClass = lClasses[lClassN - lClassI - 1];\
\
						if((lClassFolder->Class == (ECoreClass*)lClass))\
						{\
							lClassFolder->Show = TRUE;\
\
							if(lClass->GUI) lClass->GUI((ECoreObject*)(pObject), lPanel, lClassFolder, E_UPDATE);\
\
							break;\
						}\
					}\
				}\
			}\
\
\
			{\
				XeChangeManage*  lChildren;ENewN(lPanel->ClassFolders.Count * 3, lChildren);\
				XeChangeManage*  lChild = lChildren;\
\
				EIndex  lCI = 0;for(;lCI < lCN;lCI++)\
				{\
					XeComponentClassDrawer*  lClassFolder = lPanel->ClassFolders.Items[lCI];\
					XeDrawer*                lFolder = &lClassFolder->Drawer;\
\
					if(lClassFolder->Show)\
					{\
						if(pOpenStaticSubclassFolder && lClassFolder->Class == (ECoreClass*)pObject->Class)\
						{\
							XeDrawer_OpenClose(&lClassFolder->Drawer, TRUE);\
						}\
\
						if(lFolder->SeparatorW)    { lChild->W = lFolder->SeparatorW;   lChild->Manage = TRUE;lChild++; }\
						if(lFolder->ButtonMatrixW) { lChild->W = lFolder->ButtonMatrixW;lChild->Manage = TRUE;lChild++; }\
						if(lFolder->DrawerW)       { lChild->W = lFolder->DrawerW;      lChild->Manage = lFolder->Open;lChild++; }\
					}\
					else\
					{\
/* Not a superclass of lClass, or one of the GUI functions wants us not to show this subclass folder	*/\
\
/*					lChild->W = lFolder->TopMatrixW;lChild->Manage = FALSE;lChild++;	*/\
						if(lFolder->ButtonMatrixW) { lChild->W = lFolder->ButtonMatrixW;lChild->Manage = FALSE;lChild++; }\
						if(lFolder->DrawerW) { lChild->W = lFolder->DrawerW;lChild->Manage = FALSE;lChild++; }\
						if(lFolder->SeparatorW) { lChild->W = lFolder->SeparatorW;lChild->Manage = FALSE;lChild++; }\
					}\
				}\
\
				if(lChild - lChildren) XeWidgets_ChangeManage(lChildren, lChild - lChildren);\
				EFree(lChildren);\
			}\
		}\
	}\
} while(0)


//========================================
// Create/update dynamic class folders
//========================================
#define XeInfoPanel_ComponentClassFoldersCreate(pPanel, pParentW,  pType,  pClasses, pNClasses,  pObject, pOpenFolderForThisComponentClass) do\
{\
	XeInfoPanel*             lPanel = (XeInfoPanel*)(pPanel);\
	pType*                   lObject = pObject;\
	pType##ComponentClass**  lClasses = pClasses;\
	const EIndex             lClassN = pNClasses;\
\
	if(lPanel->ComponentClassFolders.Items == NULL)\
	{\
		ENewNClr(lClassN, lPanel->ComponentClassFolders.Items);\
		lPanel->ComponentClassFolders.Count = lClassN;\
	}\
	EIndex  lClassI = 0;for(;lClassI < lClassN;lClassI++)\
	{\
		pType##ComponentClass*  lClass = (pType##ComponentClass*)lClasses[lClassI];\
		EArg                    Args[32];int  ArgCnt;\
\
		if(lPanel->ComponentClassFolders.Items[lClassI] == NULL) ENew(lPanel->ComponentClassFolders.Items[lClassI]);\
		XeComponentClassDrawer*  lClassFolder = lPanel->ComponentClassFolders.Items[lClassI];EClr_p(lClassFolder);\
		lClassFolder->Panel = lPanel;\
		lClassFolder->Class = (ECoreClass*)lClass;\
\
/*printf("%s() %s:%d  {%s} %08x\n", __FUNCTION__, __FILE__, __LINE__, lClass->Name, lClass->InternalFlags);fflush(stdout);*/\
		if(lClass->StructSize && (lClass->InternalFlags & EComponentVISIBLE) != 0)\
		{\
			XeDrawer*     lFolder = &lClassFolder->Drawer;\
/*		unsigned int  lFolderFlags = XeDrawerCREATE_TOP_MATRIX|XeDrawerCREATE_SEPARATOR|XeDrawerBUTTON_IN_H_MATRIX|XeDrawerCREATE_IMAGE;*/\
			unsigned int  lFolderFlags = XeDrawerCREATE_SEPARATOR|XeDrawerBUTTON_IN_H_MATRIX|XeDrawerCREATE_IMAGE;\
			char          lTmpStr[256];\
\
			if(lClass == pOpenFolderForThisComponentClass) lFolderFlags |= XeDrawerOPEN;\
			if(lClass->Icon) lFolderFlags |= XeDrawerCREATE_IMAGE;\
			if(lClass->ClassFlags & EClassOBJECT_ACTIVE_INACTIVE) lFolderFlags |= XeDrawerACTIVE_INACTIVE_TOGGLE;\
\
			XeDrawer_NewVa(pParentW, lFolder, lClass->Name, 0, lFolderFlags,  XeNImage, lClass->Icon, Xe_ClassDropDownPanelImageSizeXPtr, Xe_ClassDropDownPanelImageSizeYPtr,  NULL);\
\
			EXeStart;\
			EXeSetArg(XeNOrientation, XeVERTICAL);\
			EXeSetArg(XeNSpacingY, 0);\
			if(lFolder->Open) lFolder->DrawerW = XeWidget_ManagedNew("DrawerW", XeMatrixWidget_class, lFolder->ParentW,  Args, ArgCnt);\
			else lFolder->DrawerW = XeWidget_New("DrawerW", XeMatrixWidget_class, lFolder->ParentW,  Args, ArgCnt);\
\
			pType##Component*  lInfo = (pType##Component*)pType##Component_ByClass(lObject, lClass, FALSE);\
			if(lInfo && lClass->GUI)\
			{\
				lClass->GUI(lObject, lInfo, lPanel, lClassFolder, E_UPDATE);\
				if((lInfo->InternalFlags & EComponentVISIBLE) == 0)\
				{\
					XeWidget  lWs[3];\
					lWs[0] = lFolder->ButtonMatrixW;\
					lWs[1] = lFolder->DrawerW;\
					lWs[2] = lFolder->SeparatorW;\
					XeWidgets_Unmanage(lWs, 3);\
				}\
			}\
			else\
			{\
/*\
				{\
					XeWidget  lWs[1];\
					LWs[0] = lFolder->ParentW;\
					XeWidgets_Unmanage(lWs, 1);\
				}\
*/\
\
				{\
					XeWidget  lWs[3];\
					lWs[0] = lFolder->ButtonMatrixW;\
					lWs[1] = lFolder->DrawerW;\
					lWs[2] = lFolder->SeparatorW;\
					XeWidgets_Unmanage(lWs, 3);\
				}\
			}\
\
/* Add a blank, windowless Widget, so the PushButton won't stretch, but the Close button stays on the right side	*/\
			EXeStart;\
			EXeSetArg(XeNResizableY, FALSE);\
			XeWidget_ManagedWindowlessNew("Nuttin", XeCoreWidget_class, Xe_Parent(lFolder->OpenCloseButtonW),  Args, ArgCnt);\
\
			EXeStart;\
			EXeSetArg(XeNSpacingRight, 2);\
			EXeSetArg(XeNShadowThickness, 0);\
			EXeSetArg(XeNLabelAlignmentX, XeAlignBEGINNING);\
			EXeSetArg(XeNLabelType, XeIMAGE);\
			EXeSetArg(XeNResizableX, FALSE);EXeSetArg(XeNResizableY, FALSE);\
			EXeSetArg(XeNUserData0, lClass);\
			EXeSetImages3(_Image_CloseButton);\
\
			EXeSetArg(XeNBackgroundGradient, FALSE);\
			sprintf(lTmpStr, "Remove %s attributes", lClass->Name);\
			XeToolTip8(lTmpStr);\
			XeWidget  lW;\
			lFolder->CloseButtonW = lW = XeWidget_ManagedNew("X", XePushButton_class, Xe_Parent(lFolder->OpenCloseButtonW),  Args, ArgCnt);\
			XeAddCallback(lW, XeNActivateCallback, _DCB_ComponentRemove, (EPointer)lPanel);\
		}\
	}\
} while(0)


//========================================
// Update dynamic class folders
//========================================
#define XeInfoPanel_ComponentClassFoldersUpdate(pPanel, pType,  pClasses, pNClasses,  pObject,  pUpdateFolderForThisComponentClass, pOpenFolderForThisComponentClass) do\
{\
	XeInfoPanel*             lPanel = (XeInfoPanel*)(pPanel);\
	pType##ComponentClass**  lClasses = pClasses;\
	const EIndex             lClassN = pNClasses;\
\
	if(pNClasses)\
	{\
		XeChangeManage*  lChildren;ENewN(lClassN * 3, lChildren);\
		XeChangeManage*  lChild = lChildren;\
		pType*           lObject = pObject;\
		EIndex  lClassI = 0;for(;lClassI < lClassN;lClassI++)\
		{\
			pType##ComponentClass*  lClass = lClasses[lClassI];if(pUpdateFolderForThisComponentClass == NULL || lClass == pUpdateFolderForThisComponentClass)\
			{\
				XeDrawer*  lFolder = &lPanel->ComponentClassFolders.Items[lClassI]->Drawer;\
\
				if(lClass->StructSize && lFolder && (lClass->InternalFlags & EComponentVISIBLE) != 0)\
				{\
					pType##Component*  lInfo = pType##Component_ByClass(lObject, lClass, FALSE);if(lInfo && (lInfo->InternalFlags & EComponentVISIBLE) != 0)\
					{\
						if(lClass == pOpenFolderForThisComponentClass) XeDrawer_OpenClose(lFolder, TRUE);\
/*					lChild->W = lFolder->ParentW;lChild->Manage = TRUE;lChild++;*/\
						lChild->W = lFolder->ButtonMatrixW;lChild->Manage = TRUE;lChild++;\
						lChild->W = lFolder->SeparatorW;lChild->Manage = TRUE;lChild++;\
						lChild->W = lFolder->DrawerW;lChild->Manage = lFolder->Open;lChild++;\
\
						if(lClass->GUI) lClass->GUI(lObject, lInfo, lPanel, lPanel->ComponentClassFolders.Items[lClassI], E_UPDATE);\
\
						if(lInfo->InternalFlags & EComponentREMOVABLE) XeWidget_Manage(lFolder->CloseButtonW);\
						else XeWidget_Unmanage(lFolder->CloseButtonW);\
					 }\
					 else\
					 {\
/*					lChild->W = lFolder->ParentW;lChild->Manage = FALSE;lChild++;*/\
						lChild->W = lFolder->ButtonMatrixW;lChild->Manage = FALSE;lChild++;\
						lChild->W = lFolder->DrawerW;lChild->Manage = FALSE;lChild++;\
						lChild->W = lFolder->SeparatorW;lChild->Manage = FALSE;lChild++;\
					}\
				}\
			}\
		}\
\
		if(lChild - lChildren) XeWidgets_ChangeManage(lChildren, lChild - lChildren);\
		EFree(lChildren);\
	}\
} while(0)




extern XeWidget  XeDrawer_NewVa(XeWidget PParentW, XeDrawer* PFolder, const char* PName, const int PShadowThickness, const uint32_t PFlags, ...);
extern XeWidget  _XeDrawer_NewVa(char* PTitle, XeWidget PParentW,  EArg* PArgs, const int PArgCnt);
extern void      XeDrawer_Free(XeWidget PDrawerW);
extern void      XeDrawer_OpenClose(XeDrawer* PFolder, const EBool POpen);
extern void      XeDrawerCB_OpenClose(XeWidget PW, EPointer PClientData, EPointer PCallData);


//----------------------------------------
// InfoPanel.c
//----------------------------------------
extern XeInfoPanel*             XeInfoPanel_New(const unsigned int PStructSize);
extern XeComponentClassDrawer*  XeInfoPanel_GetFolderByClass(const XeInfoPanel* PPanel, const void* PClass);


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif


#endif	// _XeDrawer_h
