Notifications
Clear all

[Closed] [SDK] Register Dialog for docking

the MXS provides struct (cui) methods to Register Dialog (Rollout or Rollout Floater):

cui.RegisterDialogBar
cui.UnegisterDialogBar

I can’t find anything about this mechanics in MAX SDK. Does anyone has an idea what it does do? I’m sure I miss something and it must some methods to do the same via SDK

thanks

26 Replies

What I guess is – the system creates CUIFrameWindow and adds original Rollout (Floater) as a child. Anything smarter?

here’s my sdk version of meshtocode which i did specifically to create a dockable toolbar

#include "pgTools.h"
//#include "types.h"
#include MAXSCRIPT_INCLUDE
#include "utilfunc.h"
#include "common.h"
#include "MaxIcon.h"

#define MESHTOCODE_UTIL_CLASS_ID			Class_ID(PG_CLASS_ID, 0x25ce684f)
#define MESHTOCODE_UTIL_INTERFACE_ID		Interface_ID(PG_CLASS_ID, 0x70111fa8)

#define TBITEM(type, pIcon, cmd) \
		ToolButtonItem(type,pIcon,GetCUIFrameMgr()->GetImageSize(),GetCUIFrameMgr()->GetImageSize(),GetCUIFrameMgr()->GetButtonWidth(),GetCUIFrameMgr()->GetButtonHeight(),cmd,0)
#define TBMACRO(md) \
		ToolMacroItem(0, GetCUIFrameMgr()->GetButtonHeight(), md)

//#define DISABLE_FB_INTERFACE

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

class CUIFramePtr 
{
public:
	ICUIFrame* frame;
	CUIFramePtr( HWND hwnd )						{ frame = (hwnd == NULL ? NULL : GetICUIFrame(hwnd)); }
	CUIFramePtr( ICUIFrame* frame )					{ this->frame = frame; }
	~CUIFramePtr()									{ if( frame != NULL ) ReleaseICUIFrame(frame); }
	ICUIFrame* operator->()							{ return frame; }
	ICUIFrame* operator=( ICUIFrame* ptr )			{ frame = ptr; return frame; }
	BOOL operator==( ICUIFrame* ptr )				{ return frame==ptr; }
	BOOL operator!=( ICUIFrame* ptr )				{ return frame!=ptr; }
};

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

class ToolbarPtr 
{
public:

	ICustToolbar* toolbar;
	ToolbarPtr( HWND hwnd )							{ toolbar = (hwnd == NULL ? NULL : GetICustToolbar(hwnd)); }
	ToolbarPtr( ICustToolbar* toolbar )				{ this->toolbar = toolbar; }
	~ToolbarPtr()									{ if( toolbar!=NULL ) ReleaseICustToolbar( toolbar ); }
	ICustToolbar* operator->()						{ return toolbar; }
	ICustToolbar* operator=( ICustToolbar* ptr )	{ toolbar=ptr; return toolbar; }
	BOOL operator==( ICustToolbar* ptr )			{ return toolbar==ptr; }
	BOOL operator!=( ICustToolbar* ptr )			{ return toolbar!=ptr; }

// adds a spacer that can be used as the first entry as separator tool is bust in that respect

	void AddSpacer(int width)						{ toolbar->AddTool(ToolOtherItem(_T("static"),width,16,0));	}						
};

class MaxNodeToCode
{
	virtual BOOL Enables(INode* selection) = 0;
	virtual void WriteToFile(FILE* fstream, cstr& varname, INode* node, BOOL assignSMG) = 0;
	virtual tstr& FilterStr() = 0;
	virtual tstr& DropDownStr() = 0;
};

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

static HIMAGELIST m2cppImages = NULL;	 

//**************************************************************************************
// MeshToCodeUtil
//*****************************************************************************************

class MeshToCodeUtil : public UtilityObj 
{
public:

	IUtil		*iu;
	Interface	*ip;
	HWND		hPanel;			// utility rollout panel
	HWND		toolHWND;	// our floating tool

// Constructor/Destructor

	MeshToCodeUtil() : iu(NULL), ip(NULL), toolHWND(NULL), hPanel(NULL) { }
	~MeshToCodeUtil()	{}

	void Init(HWND hWnd)	{}
	void Destroy(HWND hWnd) {}
	void DeleteThis()	{}		

// stuff for the utility panel

	void BeginEditParams(Interface *ip,IUtil *iu);
	void EndEditParams(Interface *ip,IUtil *iu);

// tool window handling routines

	void CreateNewWindow();	
	void DestroyWindow();

//	INT_PTR NotifyHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

	ICUIFrame* GetICUIFrame() { return ::GetICUIFrame(GetParent(toolHWND)); }
	BOOL ToolWindowIsOpen() { return GetParent(toolHWND) ? TRUE : FALSE; }

	void DoMeshToCode(BOOL assignSMG);
	void WriteMeshToCPPFile(FILE* fstream, cstr& varname, Mesh& mesh, BOOL assignSMG);
};

// our util global static

static MeshToCodeUtil m2cObj;

//*****************************************************************************************
// the class descriptor
//*****************************************************************************************

class MeshToCodeUtilClassDesc : public ClassDesc2 
{
public:
	int 			IsPublic()					{ return TRUE; }
	void *			Create(BOOL loading=FALSE)	{ return &m2cObj; }
	const TCHAR *	ClassName()					{ return GetString(IDS_MESHTOCODE_CLASS_NAME); }
	SClass_ID		SuperClassID()				{ return UTILITY_CLASS_ID; }
	Class_ID		ClassID()					{ return MESHTOCODE_UTIL_CLASS_ID; }
	const TCHAR* 	Category()					{ return GetString(IDS_CATEGORY); }
	const TCHAR*	InternalName()				{ return _T("MeshToCodeUtil"); }	
	HINSTANCE		HInstance()					{ return hInstance; }	// returns owning module handle
};

static MeshToCodeUtilClassDesc MeshToCodeUtilDesc;
ClassDesc2* GetMeshToCodeUtilDesc() { return &MeshToCodeUtilDesc; }

//*****************************************************************************************
// function publishing, make some basic functionality available to mxs mostly so we can call our
// utility from the ui via a macroscript without switching to the utility panel first !
//**************************************************************************************

#ifndef DISABLE_FB_INTERFACE

class MeshToCodeUtilObjFPInterface : public FPStaticInterface 
{
public:
	DECLARE_DESCRIPTOR(MeshToCodeUtilObjFPInterface);

	enum functionID { kOpenWindow, kCloseWindow, kIsOpen };
	
	BEGIN_FUNCTION_MAP			
		VFN_0(kOpenWindow,  OpenWindow)
		VFN_0(kCloseWindow, CloseWindow)
		FN_0(kIsOpen, TYPE_BOOL, IsOpen)
	END_FUNCTION_MAP
	
	void OpenWindow()	{ m2cObj.CreateNewWindow(); }
	void CloseWindow()	{ m2cObj.DestroyWindow(); }
	bool IsOpen()		{ return IsWindowOpen(); }

	static bool IsWindowOpen()			{ return windowOpen; }
	static void SetWindowOpen(bool val) { windowOpen = val; GetCUIFrameMgr()->SetMacroButtonStates(false); }

private:

	static bool windowOpen;
};

//**************************************************************************************
// create the global static instance

static MeshToCodeUtilObjFPInterface theMeshToCodeUtilObjFPInterface(MESHTOCODE_UTIL_INTERFACE_ID, _T("MeshToCodeUtilOps"), 
							IDS_TESTING_CLASS_NAME, NULL, FP_CORE,
							MeshToCodeUtilObjFPInterface::kOpenWindow, _T("openWindow"), -1, TYPE_VOID, FP_NO_REDRAW, 0,
							MeshToCodeUtilObjFPInterface::kCloseWindow, _T("closeWindow"), -1, TYPE_VOID, FP_NO_REDRAW, 0,
							MeshToCodeUtilObjFPInterface::kIsOpen, _T("isOpen"), -1, TYPE_BOOL, FP_NO_REDRAW, 0, p_end);

bool MeshToCodeUtilObjFPInterface::windowOpen = false; 

#endif

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

static INT_PTR CALLBACK MeshToCodeUtilPanelProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) 
	{
		case WM_INITDIALOG:

			m2cObj.Init(hWnd); // can we delete these ?
			break;

		case WM_DESTROY:

			m2cObj.Destroy(hWnd); // can we delete these ?
			break;

		case WM_COMMAND:

			switch (LOWORD(wParam)) 
			{
				case IDC_NEW_FLOATER_BTN:

					m2cObj.CreateNewWindow();
					break;

				case IDOK:
				case IDCANCEL:

					DestroyWindow(hWnd);
					break;
			}
			break;

		case WM_LBUTTONDOWN:
		case WM_LBUTTONUP:
		case WM_MOUSEMOVE:

			m2cObj.ip->RollupMouseMessage(hWnd,msg,wParam,lParam); 
			break;

		default:
			return FALSE;
	}
	return TRUE;
}

//**************************************************************************************
// MsgHandler
//**************************************************************************************

class MsgHandler : public CUIFrameMsgHandler 
{
public:
	int ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam);
};	

static MsgHandler theMsgHandler;

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

#define TBID_ASSIGN_SMG_CHK 20001
#define TBID_MESH_TO_CPP_BTN 20002

int MsgHandler::ProcessMessage(UINT message, WPARAM wParam, LPARAM lParam) 
{
	switch(message)
	{
		case WM_CLOSE:
		{
			m2cObj.DestroyWindow();
			return TRUE;
		}

		case WM_COMMAND:

			switch (LOWORD(wParam)) 
			{
				case TBID_MESH_TO_CPP_BTN:
				{
					m2cObj.DoMeshToCode(IsDlgButtonChecked(m2cObj.toolHWND,TBID_ASSIGN_SMG_CHK));
					return TRUE;
				}
				default: 
					return FALSE;
			}
	}
	return FALSE;
}

//**************************************************************************************
// PropmanUtil, code for our utility starts here
//*****************************************************************************************

void MeshToCodeUtil::BeginEditParams(Interface *ip,IUtil *iu) 
{
	this->iu = iu;
	this->ip = ip;
	hPanel = ip->AddRollupPage(hInstance,MAKEINTRESOURCE(IDD_MESHTOCODE_PANEL),MeshToCodeUtilPanelProc, 
									GetString(IDS_PARAMS), 0);
}
	
//********************************************************************************************

void MeshToCodeUtil::EndEditParams(Interface *ip,IUtil *iu) 
{
	this->iu = NULL;
	ip->DeleteRollupPage(hPanel);
	hPanel = NULL;
}

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

void MeshToCodeUtil::CreateNewWindow()
{
	if(ToolWindowIsOpen()) return;

	ip = GetCOREInterface();	// catches any script calls
	LoadImages(m2cppImages,IDB_MESHTOCPP,16,16,1);
	HWND parentHWND = ip->GetMAXHWnd();

// create the frame window

	HWND frameHWND = CreateCUIFrameWindow(parentHWND, _T("Mesh To C++"), 0, 0, 250, 100);
	CUIFramePtr theFrame(frameHWND);
	theFrame->SetContentType(CUI_TOOLBAR);
	theFrame->SetPosType(CUI_HORIZ_DOCK | CUI_VERT_DOCK | CUI_FLOATABLE | CUI_SM_HANDLES);

// create the tool window

	toolHWND = CreateWindow(CUSTTOOLBARWINDOWCLASS, NULL, WS_CHILD|WS_VISIBLE,0,0,250,100, frameHWND, NULL, hInstance,NULL);

	ToolbarPtr theToolBar(toolHWND);
	theToolBar->LinkToCUIFrame(frameHWND, &theMsgHandler);
	theToolBar->SetBottomBorder(FALSE);
	theToolBar->SetTopBorder(FALSE);
	theToolBar->SetImage(m2cppImages);
	theToolBar.AddSpacer(8);	
	theToolBar->AddTool(ToolOtherItem(_T("button"),80,16,TBID_ASSIGN_SMG_CHK,WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX,
						CENTER_TOOL_VERTICALLY,_T("Assign SMG")));
	theToolBar->AddTool(ToolButtonItem(CTB_PUSHBUTTON,0,0,0,0,16,16,23,22,TBID_MESH_TO_CPP_BTN));

// resize our frame to fit tool bar

	SIZE sz;
	theToolBar->GetFloatingCUIFrameSize(&sz);
	SetWindowPos(frameHWND, HWND_TOP,0,0,sz.cx,sz.cy,SWP_NOMOVE|SWP_NOREDRAW|SWP_NOZORDER);


	GetCUIFrameMgr()->DockCUIWindow (frameHWND, CUI_TOP_DOCK);
	GetCUIFrameMgr()->RecalcLayout(TRUE);

#ifndef DISABLE_FB_INTERFACE
	MeshToCodeUtilObjFPInterface::SetWindowOpen(true);
#endif
}

//**************************************************************************************
// destroy the tool window

void MeshToCodeUtil::DestroyWindow()
{
	HWND parent = GetParent(toolHWND);
	if(parent)  
	{	
		GetCUIFrameMgr()->RecalcLayout(TRUE);
		::DestroyWindow(parent); 
	}
	toolHWND = NULL;
#ifndef DISABLE_FB_INTERFACE
	MeshToCodeUtilObjFPInterface::SetWindowOpen(false);
#endif
}

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

class filefilter
{
	tstr fstr;

public:	

	void append(tstr text, tstr ext)
	{
		fstr += text;
		fstr.push_back('\0');
		fstr += ext;
		fstr.push_back('\0');
	}
	tstr& getfilter() { return fstr; }
};

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

void MeshToCodeUtil::DoMeshToCode(BOOL assignSMG)
{
	//mprintf("TBID_ASSIGN_SMG_CHK checked = %d \n",assignSMG);
	
	int numSelNodes = ip->GetSelNodeCount();
	HWND frameHWND = GetParent(toolHWND);
	if(numSelNodes != 1)
	{
		MessageBox(frameHWND, _T("You must select an object with a mesh."),  _T("Mesh To Cpp"), MB_OK);
		return;
	}

	INode*  theNode =  ip->GetSelNode(0);
	if(!CanConvertToTriMesh(theNode))
	{
		MessageBox(frameHWND, _T("You must select an object with a mesh."),  _T("Mesh To Cpp"), MB_OK);
		return;
	}
	
	tstr filename(theNode->GetName());
	filename += _T(".cpp");
	tstr initdir(ip->GetDir(APP_EXPORT_DIR));
	tstr defext(_T("cpp"));
	filefilter filter;
	filter.append(_T("Source Files (*.cpp)"), _T("*.cpp"));
	filter.append(_T("Include Files (*.h)"), _T("*.h"));
	
	if(!DoOpenSaveDialog(frameHWND, initdir, filename, filter.getfilter(), defext, 1, false))
		return;

	FILE* fstream = _tfopen(filename.c_str(), _T("w"));
	if(!fstream)
	{
		MessageBox(frameHWND, _T("Unable to open file for writing."),  _T("Mesh To Cpp"), MB_OK);
		return;
	}
	
	BOOL deleteIt = FALSE;
	TriObject* meshObj = GetTriObject(theNode,ip->GetTime(),deleteIt);
	if(!meshObj)
		return;
	cstr varname = widetochar(theNode->GetName());

	Mesh& mesh = meshObj->GetMesh();

	WriteMeshToCPPFile(fstream, varname, mesh, assignSMG);	

	if (deleteIt) meshObj->DeleteMe();

	fclose(fstream);
}
	
//********************************************************************************************

#define VERTPREC 0.0001f
#define UVPREC 0.00001f
inline float round(float f,float prec) 
{ 
	float inv = 1.0f/prec; 
	return (float)(floor(f * inv + 0.5f)/inv); 
}

inline Point3 roundPoint(Point3& p, float prec) { return Point3(round(p.x,prec), round(p.y,prec), round(p.z,prec)); }

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

void MeshToCodeUtil::WriteMeshToCPPFile(FILE* fstream, cstr& varname, Mesh& mesh, BOOL assignSMG)
{
	int nverts = mesh.numVerts;
	int nfaces = mesh.numFaces;

	fprintf(fstream, "static void Build%sMesh(Mesh& mesh, float size)\n{\n\tmesh.setNumVerts(%d);\n\tmesh.setNumFaces(%d);\n\n", 
		varname.c_str(), nverts, nfaces);

// geometry verts

	Point3* verts = mesh.verts;		// pointer to the verts
	for(int v = 0; v < nverts; ++v)
	{
		Point3 vert = roundPoint(verts[v], VERTPREC); 
		fprintf(fstream, "\tmesh.setVert(%d,size*Point3(%.4ff,%.4ff,%.4ff));\n", v, vert.x, vert.y, vert.z);
	}

// faces

	fprintf(fstream, "\n");
	Face* faces = mesh.faces;		// pointer to the faces
	for(int f = 0; f < nfaces; ++f)
	{
		Face& face = faces[f];
		fprintf(fstream, "\tmesh.faces[%d].setVerts(%d, %d, %d);\n", f, face.v[0], face.v[1], face.v[2]);
		fprintf(fstream, "\tmesh.faces[%d].setEdgeVisFlags(%d,%d,%d);\n", f, face.getEdgeVis(0) ? 1 : 0, 
						face.getEdgeVis(1) ? 1 : 0, face.getEdgeVis(2)) ? 1 : 0;
		if(assignSMG)
			fprintf(fstream, "\tmesh.faces[%d].setSmGroup(%d);\n", f, face.getSmGroup());
	}

// uvw coords

	int ntverts = mesh.numTVerts;
	if(ntverts > 0)
	{
		fprintf(fstream, "\n\tmesh.setNumTVerts(%d);\n\tmesh.setNumTVFaces(%d);\n\n", ntverts, nfaces);

		Point3* tverts = mesh.tVerts;
		for(int v = 0; v < ntverts; ++v)
		{
			Point3 tvert = roundPoint(tverts[v], UVPREC); 
			fprintf(fstream, "\tmesh.setTVert(%d,Point3(%.5ff,%.5ff,%.5ff));\n", v, tvert.x, tvert.y, tvert.z);
		}
		
		fprintf(fstream, "\n");

		TVFace* tfaces = mesh.tvFace;
		for(int f = 0; f < nfaces; ++f)
		{
			TVFace& tface = tfaces[f];
			fprintf(fstream, "\tmesh.tvFace[%d].setTVerts(%d, %d, %d);\n", f,tface.t[0],tface.t[1],tface.t[2]);
		}
	}

// colour per vertex

	int ncverts = mesh.numCVerts;
	if(ncverts > 0)
	{
		fprintf(fstream, "\n\tmesh.setNumVertCol(%d);\n\tmesh.setNumVCFaces(%d);\n\n", ncverts, nfaces);
		Point3* cverts = mesh.vertCol;
		for(int v = 0; v < ncverts; ++v)
		{
			Point3 cvert = roundPoint(cverts[v], UVPREC); 
			fprintf(fstream, "\tmesh.setMapVert(0, %d,Point3(%.5ff,%.5ff,%.5ff));\n", v, cvert.x, cvert.y, cvert.z);
		}
		fprintf(fstream, "\n");
		TVFace* cfaces = mesh.vcFace;
		for(int f = 0; f < nfaces; ++f)
		{
			TVFace& cface = cfaces[f];
			fprintf(fstream, "\tmesh.mapFaces(0)[%d].setTVerts(%d, %d, %d);\n", f,cface.t[0],cface.t[1],cface.t[2]);
		}
	}

// mapping channel 2

	if(mesh.mapSupport(2))
	{
		int nmverts = mesh.getNumMapVerts(2);  
		fprintf(fstream, "\n\tmesh.setMapSupport(2);\n\tmesh.setNumMapVerts(2,%d);\n\tmesh.setNumMapFaces(2,%d);\n\n", 
					nmverts, nfaces);
		Point3* mverts = mesh.mapVerts(2);
		for(int v = 0; v < nmverts; ++v)
		{
			Point3 mvert = roundPoint(mverts[v], UVPREC); 
			fprintf(fstream, "\tmesh.setMapVert(2, %d,Point3(%.5ff,%.5ff,%.5ff));\n", v, mvert.x, mvert.y, mvert.z);
		}
		fprintf(fstream, "\n");
		TVFace* mfaces = mesh.mapFaces(2);
		for(int f = 0; f < nfaces; ++f)
		{
			TVFace& mface = mfaces[f];
			fprintf(fstream, "\tmesh.mapFaces(2)[%d].setTVerts(%d, %d, %d);\n", f,mface.t[0],mface.t[1],mface.t[2]);
		}
	}
	fprintf(fstream, "\n\tmesh.InvalidateGeomCache();\n\tmesh.EnableEdgeList(TRUE);\n}\n");

	
	//MeshToCodeUtilOps.OpenWindow()
}

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

thank you KLVNK. I more or less understand what to do with custom tool bar. I am not clear about dockable dialogs.
Check the situation in the max … To make a Rollout dialog dockable we have to Register it. But look at Scene Explorer dialog. It’s registered already but stays in Dialog state (not Tool Window state). It keeps Minimize and Maximize ability, as well as can be docked. Why can I not set a Rollout Dialog in the same state?

I want to have my MAX Dialog behaves like a Track View, or Scene Explorer, or Layer Explorer windows… Be ‘minimizable, maximizable, dockable, floatable’ …

I couldn’t find anything about how to doing it with the SDK.

However, for .Net forms (like the Scene or Layer Explorer), there is a class WinFormsCUIFrame in the ManagedServices assembly with some methods to work with this.

(
	form = dotnetobject "System.Windows.Forms.Form"
	CUIFrame = dotnetobject "ManagedServices.WinFormsCUIFrame" form
)

Thanks! It’s an option… All size change messages i probably can catch.

this is almost exactly what i want to do but with MAX Script Dialog

Thanks, Jorge, once again. This is not what I need, but now I know for sure that this is possible.

It must be possible at least with native windows, like the Track View Editor. I don’t know about MXS rollouts. Perhaps they have some kind of limitation?

I’ve tried re-styling the registered Rollouts, but couldn’t get them to work.

Page 1 / 3