You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5075 lines
188 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "HeadSpin.h"
#include "max.h"
#include "resource.h"
#include "hsTemplates.h"
#include "plComponent.h"
#include "plComponentReg.h"
#include "plMiscComponents.h"
#include "plAnimComponent.h"
#include "plNotetrackAnim.h"
#include "plGUIComponents.h"
#include "plAudioComponents.h"
#include "MaxMain/plPlasmaRefMsgs.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plCoordinateInterface.h"
#include "pnSceneObject/plDrawInterface.h"
#include "plDrawable/plDrawableSpans.h"
#include "plDrawable/plGeometrySpan.h"
#include "plSurface/plLayerInterface.h"
#include "plSurface/plLayer.h"
#include "plSurface/hsGMaterial.h"
#include "plGImage/plMipmap.h"
#include "plGImage/plDynamicTextMap.h"
#include "plMessage/plLayRefMsg.h"
#include "plMessage/plMatRefMsg.h"
#include "MaxMain/plPluginResManager.h"
#include "plgDispatch.h"
#include "pnMessage/plObjRefMsg.h"
#include "pnMessage/plIntRefMsg.h"
#include "pnMessage/plNodeRefMsg.h"
#include "plScene/plSceneNode.h"
#include "MaxConvert/hsConverterUtils.h"
#include "MaxConvert/hsControlConverter.h"
#include "MaxConvert/hsMaterialConverter.h"
#include "MaxConvert/plLayerConverter.h"
#include "plInterp/plController.h"
#include "plInterp/plAnimEaseTypes.h"
#include "MaxMain/plMaxNode.h"
#include "pnKeyedObject/plKey.h"
// GUIDialog component.
#include "plScene/plPostEffectMod.h"
#include "pfGameGUIMgr/pfGameGUIMgr.h"
#include "pfGameGUIMgr/pfGUIDialogMod.h"
#include "pfGameGUIMgr/pfGUIControlMod.h"
#include "pfGameGUIMgr/pfGUIControlHandlers.h"
#include "pfGameGUIMgr/pfGUIButtonMod.h"
#include "pfGameGUIMgr/pfGUIDraggableMod.h"
#include "pfGameGUIMgr/pfGUIListBoxMod.h"
#include "pfGameGUIMgr/pfGUITextBoxMod.h"
#include "pfGameGUIMgr/pfGUIEditBoxMod.h"
#include "pfGameGUIMgr/pfGUIUpDownPairMod.h"
#include "pfGameGUIMgr/pfGUIKnobCtrl.h"
#include "pfGameGUIMgr/pfGUITagDefs.h"
#include "pfGameGUIMgr/pfGUIDragBarCtrl.h"
#include "pfGameGUIMgr/pfGUICheckBoxCtrl.h"
#include "pfGameGUIMgr/pfGUIRadioGroupCtrl.h"
#include "pfGameGUIMgr/pfGUIDynDisplayCtrl.h"
#include "pfGameGUIMgr/pfGUIMultiLineEditCtrl.h"
#include "pfGameGUIMgr/pfGUIProgressCtrl.h"
#include "pfGameGUIMgr/pfGUIClickMapCtrl.h"
#include "pfGameGUIMgr/pfGUIPopUpMenu.h"
// Location Related
#include "plAgeDescription/plAgeDescription.h"
#include "MaxMain/plMaxCFGFile.h"
#include "MaxMain/plAgeDescInterface.h"
#include "plFile/hsFiles.h"
#include "MaxConvert/plConvert.h"
#include "MaxPlasmaMtls/Layers/plDynamicTextLayer.h"
#include "MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h"
#include "MaxMain/plMaxAccelerators.h"
#include "plPickMaterialMap.h"
#include "plInterp/plController.h"
#include "plAvatar/plMatrixChannel.h"
#include "MaxPlasmaMtls/Layers/plLayerTex.h"
#include "pfGUISkinComp.h"
#include "plResMgr/plLocalization.h"
#include "plPickLocalizationDlg.h"
#include <vector>
#include <string>
void DummyCodeIncludeFuncGUI() {}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// Helper Classes /////////////////////////////////////////////////////////////////////////////
//// Hit Callback for GUI Controls //////////////////////////////////////////////////////////////
class plGUICtrlHitCallback : public HitByNameDlgCallback
{
protected:
INode* fOwner;
IParamBlock2* fPB;
ParamID fNodeListID;
BOOL fRestrict;
hsTArray<Class_ID> fRestrictedIDs;
TCHAR fTitle[ 128 ];
BOOL fSingle;
public:
plGUICtrlHitCallback( INode* owner, IParamBlock2 *pb, ParamID nodeListID, TCHAR *title = nil,
BOOL restricted = FALSE, Class_ID rID = GUI_BUTTON_CLASSID, BOOL single = TRUE )
: fOwner( owner ), fPB( pb ), fNodeListID( nodeListID ), fRestrict( restricted ), fSingle( single )
{
fRestrictedIDs.Append( rID );
strcpy( fTitle, title );
}
plGUICtrlHitCallback( INode* owner, IParamBlock2 *pb, ParamID nodeListID, TCHAR *title,
hsTArray<Class_ID> &rID )
: fOwner( owner ), fPB( pb ), fNodeListID( nodeListID ), fRestrict( true ), fSingle(TRUE)
{
for( int i = 0; i < rID.GetCount(); i++ )
fRestrictedIDs.Append( rID[ i ] );
strcpy( fTitle, title );
}
virtual TCHAR *dialogTitle() { return fTitle; }
virtual TCHAR *buttonText() { return "OK"; }
virtual int filter(INode *node)
{
if( node == fOwner )
return FALSE;
plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent();
// If this is an activator type component
if( comp )
{
if( ( fRestrict && fRestrictedIDs.Find( comp->ClassID() ) != fRestrictedIDs.kMissingIndex )
|| ( !fRestrict && plGUIControlBase::GetGUIComp( comp ) != nil ) )
{
// And this wouldn't create a cyclical reference (Max doesn't like those)
if (comp->TestForLoop(FOREVER, fPB) == REF_FAIL)
return FALSE;
return TRUE;
}
}
else if( fRestrict && fRestrictedIDs.Find( node->ClassID() ) != fRestrictedIDs.kMissingIndex )
{
return TRUE;
}
return FALSE;
}
virtual void proc(INodeTab &nodeTab)
{
if ( nodeTab.Count() > 0 )
{
if( fSingle )
fPB->SetValue( fNodeListID, TimeValue(0), nodeTab[0] );
else
fPB->Append( fNodeListID, nodeTab.Count(), &nodeTab[0] );
}
}
virtual BOOL showHiddenAndFrozen() { return TRUE; }
virtual BOOL singleSelect() { return TRUE; }
};
//// Single GUI Control Dialog Proc /////////////////////////////////////////////////////////////
class plGUISingleCtrlDlgProc : public ParamMap2UserDlgProc
{
protected:
ParamID fNodeID;
int fDlgItem;
TCHAR fTitle[ 128 ];
hsTArray<Class_ID> fClassesToSelect;
ParamMap2UserDlgProc *fProcChain;
public:
int GetHandledDlgItem( void ) const { return fDlgItem; }
static const Class_ID kEndClassList;
plGUISingleCtrlDlgProc( ParamID nodeID, int dlgItem, TCHAR *title, Class_ID *restrict, ParamMap2UserDlgProc *parentProc = nil )
{
fNodeID = nodeID;
fDlgItem = dlgItem;
for( int i = 0; restrict[ i ] != kEndClassList; i++ )
fClassesToSelect.Append( restrict[ i ] );
// fClassToSelect = restrict;
strcpy( fTitle, title );
fProcChain = parentProc;
}
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch ( msg )
{
case WM_INITDIALOG:
{
IParamBlock2 *pb = map->GetParamBlock();
INode *node = pb->GetINode( fNodeID );
TSTR newName( node ? node->GetName() : "Pick" );
::SetWindowText( ::GetDlgItem( hWnd, fDlgItem ), newName );
}
break;
case WM_COMMAND:
if( ( HIWORD( wParam ) == BN_CLICKED ) )
{
if( LOWORD( wParam ) == fDlgItem )
{
IParamBlock2 *pb = map->GetParamBlock();
plGUICtrlHitCallback hitCB( (INode *)pb->GetOwner(), pb, fNodeID, fTitle, fClassesToSelect );
GetCOREInterface()->DoHitByNameDialog( &hitCB );
INode* node = pb->GetINode( fNodeID );
TSTR newName( node ? node->GetName() : "Pick" );
::SetWindowText( ::GetDlgItem(hWnd, fDlgItem ), newName );
map->Invalidate( fNodeID );
::InvalidateRect( hWnd, NULL, TRUE );
return true;
}
}
break;
}
if( fProcChain )
fProcChain->DlgProc( t, map, hWnd, msg, wParam, lParam );
return false;
}
void DeleteThis() {}
};
const Class_ID plGUISingleCtrlDlgProc::kEndClassList = Class_ID();
Class_ID sSkinClassesToSelect[] = { GUI_SKIN_CLASSID, plGUISingleCtrlDlgProc::kEndClassList };
//// Multiple GUI Control Dialog Proc ///////////////////////////////////////////////////////////
class plGUIMultipleCtrlDlgProc : public ParamMap2UserDlgProc
{
protected:
hsTArray<plGUISingleCtrlDlgProc *> fSingleProcs;
hsTArray<ParamMap2UserDlgProc *> fProcs;
public:
plGUIMultipleCtrlDlgProc( plGUISingleCtrlDlgProc **singleProcs, ParamMap2UserDlgProc **procs=nil )
{
for( int i = 0; singleProcs[ i ] != nil; i++ )
fSingleProcs.Append( singleProcs[ i ] );
if ( procs )
{
for( int i = 0; procs[ i ] != nil; i++ )
fProcs.Append( procs[ i ] );
}
}
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
int i;
switch ( msg )
{
case WM_INITDIALOG:
for( i = 0; i < fSingleProcs.GetCount(); i++ )
fSingleProcs[ i ]->DlgProc( t, map, hWnd, msg, wParam, lParam );
for( i = 0; i < fProcs.GetCount(); i++ )
fProcs[ i ]->DlgProc( t, map, hWnd, msg, wParam, lParam );
return true;
case WM_COMMAND:
for( i = 0; i < fSingleProcs.GetCount(); i++ )
{
if( fSingleProcs[ i ]->GetHandledDlgItem() == LOWORD( wParam ) )
{
fSingleProcs[ i ]->DlgProc( t, map, hWnd, msg, wParam, lParam );
break;
}
}
// and now do the procs that want more control
for( i = 0; i < fProcs.GetCount(); i++ )
fProcs[ i ]->DlgProc( t, map, hWnd, msg, wParam, lParam );
return true;
}
return false;
}
void DeleteThis() {}
};
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUITag Component /////////////////////////////////////////////////////////////////////
//
// GUI element that can be dragged anywhere in the 2D viewing plane.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
class plGUITagProc : public ParamMap2UserDlgProc
{
protected:
void ILoadTags( HWND hWnd, IParamBlock2 *pb );
public:
void DeleteThis() {}
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
};
static plGUITagProc gGUITagProc;
// Class that accesses the paramblock below.
class plGUITagComponent : public plComponent
{
public:
plGUITagComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefCurrIDSel = 64 // So we can share it among other components
};
static uint32_t GetTagIDOnNode( plMaxNode *node );
};
//Max desc stuff necessary below.
#define kGUITagClassID Class_ID(0x77276e84, 0x24f360c5)
CLASS_DESC(plGUITagComponent, gGUITagDesc, "GUI ID Tag", "GUITag", COMP_TYPE_GUI, kGUITagClassID )
ParamBlockDesc2 gGUITagBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUITag"), 0, &gGUITagDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_GUITAG, IDS_COMP_GUITAG, 0, 0, &gGUITagProc,
plGUITagComponent::kRefCurrIDSel, _T("currSel"), TYPE_INT, 0, 0,
end,
end
);
void plGUITagProc::ILoadTags( HWND hWnd, IParamBlock2 *pb )
{
int idx, idx2 = 0;
char str[] = "(none)";
SendMessage( hWnd, CB_RESETCONTENT, 0, 0 );
idx2 = idx = SendMessage( hWnd, CB_ADDSTRING, 0, (LPARAM)str );
SendMessage( hWnd, CB_SETITEMDATA, (WPARAM)idx, (LPARAM)0 );
for( uint32_t i = 0; i < pfGameGUIMgr::GetNumTags(); i++ )
{
pfGUITag *tag = pfGameGUIMgr::GetTag( i );
idx = SendMessage( hWnd, CB_ADDSTRING, 0, (LPARAM)tag->fName );
SendMessage( hWnd, CB_SETITEMDATA, (WPARAM)idx, (LPARAM)tag->fID );
if( tag->fID == pb->GetInt( plGUITagComponent::kRefCurrIDSel ) )
idx2 = idx;
}
if( idx2 == 0 && pb->GetInt( plGUITagComponent::kRefCurrIDSel ) != 0 )
{
char str[ 32 ];
sprintf( str, "%d", pb->GetInt( plGUITagComponent::kRefCurrIDSel ) );
SendMessage( hWnd, WM_SETTEXT, 0, (LPARAM)str );
}
else
SendMessage( hWnd, CB_SETCURSEL, idx2, 0 );
}
// Callback enum proc for below
BOOL CALLBACK GetEditCtrlEnumProc( HWND hWnd, LPARAM lParam )
{
char className[ 128 ];
// ICK
GetClassName( hWnd, className, sizeof( className ) - 1 );
if( stricmp( className, "EDIT" ) == 0 )
{
HWND *ptr = (HWND *)lParam;
*ptr = hWnd;
return FALSE;
}
return TRUE;
}
// Small proc that, given the handle of a combo box, returns the handle of the edit window for it
static HWND GetEditCtrlFromComboBox( HWND combo )
{
HWND toReturn;
EnumChildWindows( combo, GetEditCtrlEnumProc, (LPARAM)&toReturn );
return toReturn;
}
// Small proc to only allow numbers in an edit box
static WNDPROC sOriginalProc = nil;
LRESULT CALLBACK SubclassedEditProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_CHAR:
if( !isdigit( (TCHAR)wParam ) )
return 0;
break;
case WM_GETDLGCODE:
return DLGC_WANTALLKEYS;
case WM_KEYDOWN:
if( wParam == VK_RETURN )
{
// Do the same thing as when we lose focus--check our int value and make
// sure it's big enough (don't worry about setting the paramBlock value,
// that'll happen when the control loses focus)
char str[ 32 ];
GetWindowText( hWnd, str, sizeof( str ) - 1 );
int id = atoi( str );
if( id < pfGameGUIMgr::GetHighestTag() + 1 )
{
id = pfGameGUIMgr::GetHighestTag() + 1;
sprintf( str, "%d", id );
SetWindowText( hWnd, str );
}
SendMessage( hWnd, EM_SETSEL, 0, (LPARAM)-1 );
return 0;
}
break;
}
return CallWindowProc( sOriginalProc, hWnd, msg, wParam, lParam );
}
BOOL plGUITagProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
HWND edit;
BOOL dummy1;
switch( msg )
{
case WM_INITDIALOG:
ILoadTags( GetDlgItem( hWnd, IDC_GUI_TAGCOMBO ), pmap->GetParamBlock() );
// Set the edit control of the combo box to only accept number characters
edit = GetEditCtrlFromComboBox( GetDlgItem( hWnd, IDC_GUI_TAGCOMBO ) );
SetWindowLong( edit, GWL_STYLE, GetWindowLong( edit, GWL_STYLE ) | ES_WANTRETURN );
sOriginalProc = (WNDPROC)SetWindowLong( edit, GWL_WNDPROC, (DWORD)SubclassedEditProc );
return true;
case WM_DESTROY:
SetWindowLong( GetDlgItem( hWnd, IDC_GUI_TAGCOMBO ), GWL_WNDPROC, (DWORD)sOriginalProc );
break;
case WM_COMMAND:
if( LOWORD( wParam ) == IDC_GUI_TAGCOMBO )
{
if( HIWORD( wParam ) == CBN_SELCHANGE )
{
int idx = SendDlgItemMessage( hWnd, IDC_GUI_TAGCOMBO, CB_GETCURSEL, 0, 0 );
if( idx == CB_ERR )
{
// Must be a custom one
int id = GetDlgItemInt( hWnd, IDC_GUI_TAGCOMBO, &dummy1, false );
pmap->GetParamBlock()->SetValue( plGUITagComponent::kRefCurrIDSel, 0, id );
}
else
{
pmap->GetParamBlock()->SetValue( plGUITagComponent::kRefCurrIDSel, 0,
SendDlgItemMessage( hWnd, IDC_GUI_TAGCOMBO, CB_GETITEMDATA, idx, 0 ) );
}
}
else if( HIWORD( wParam ) == CBN_KILLFOCUS )
{
plMaxAccelerators::Enable();
// Make sure the number inside is valid
if( SendDlgItemMessage( hWnd, IDC_GUI_TAGCOMBO, CB_GETCURSEL, 0, 0 ) == CB_ERR )
{
int id = GetDlgItemInt( hWnd, IDC_GUI_TAGCOMBO, &dummy1, false );
if( id < pfGameGUIMgr::GetHighestTag() + 1 )
{
id = pfGameGUIMgr::GetHighestTag() + 1;
SetDlgItemInt( hWnd, IDC_GUI_TAGCOMBO, id, false );
}
pmap->GetParamBlock()->SetValue( plGUITagComponent::kRefCurrIDSel, 0, id );
}
}
else if( HIWORD( wParam ) == CBN_SETFOCUS )
{
plMaxAccelerators::Disable();
}
}
break;
}
return false;
}
plGUITagComponent::plGUITagComponent()
{
fClassDesc = &gGUITagDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUITagComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
return true;
}
hsBool plGUITagComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return true;
}
hsBool plGUITagComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return true;
}
uint32_t plGUITagComponent::GetTagIDOnNode( plMaxNode *node )
{
uint32_t i;
for( i = 0; i < node->NumAttachedComponents( false ); i++ )
{
plComponentBase *comp = node->GetAttachedComponent( i, false );
if( comp->ClassID() == kGUITagClassID )
{
plGUITagComponent *tag = (plGUITagComponent *)comp;
return tag->GetParamBlockByID( plComponent::kBlkComp )->GetInt( kRefCurrIDSel );
}
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIColorScheme Component /////////////////////////////////////////////////////////////
//
// Defines the color scheme for a single control or an entire dialog
//
/////////////////////////////////////////////////////////////////////////////////////////////////
class plGUIColorSchemeProc : public ParamMap2UserDlgProc
{
protected:
void ILoadFonts( HWND hWnd, IParamBlock2 *pb );
static int CALLBACK IMyFontEnumProc( const ENUMLOGFONTEX *logFontData, const NEWTEXTMETRICEX *physFontData,
unsigned long fontType, LPARAM lParam );
public:
void DeleteThis() {}
virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map );
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
};
static plGUIColorSchemeProc gGUIColorSchemeProc;
// Class that accesses the paramblock below.
class plGUIColorSchemeComp : public plComponent
{
public:
plGUIColorSchemeComp();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefForeColor = 128, // So we can share it among other components
kRefBackColor,
kRefSelForeColor,
kRefSelBackColor,
kRefUseAlphas,
kRefFontFace,
kRefFontSize,
kRefFontBold,
kRefFontItalic,
kRefForeAlpha,
kRefBackAlpha,
kRefSelForeAlpha,
kRefSelBackAlpha,
kRefFontShadowed
};
static void ConvertScheme( IParamBlock2 *pb, pfGUIColorScheme *destScheme, plErrorMsg *pErrMsg );
};
//Max desc stuff necessary below.
CLASS_DESC(plGUIColorSchemeComp, gGUIColorSchemeDesc, "GUI Color Scheme", "GUIColorScheme", COMP_TYPE_GUI, GUI_COLORSCHEME_CLASSID )
static ParamBlockDesc2 gGUIColorSchemeBk
(
/// Main def
plComponent::kBlkComp, _T("GUIColorScheme"), 0, &gGUIColorSchemeDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp,
1,
plGUIDialogComponent::kSchemeRollout, IDD_COMP_GUISCHEME, IDS_COMP_GUISCHEME, 0, 0, &gGUIColorSchemeProc,
plGUIColorSchemeComp::kRefForeColor, _T("foreColor"), TYPE_RGBA, 0, 0,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_COLORSWATCH, IDC_GUI_FGCOLOR,
p_default, Color( 1.f, 1.f, 1.f ),
end,
plGUIColorSchemeComp::kRefBackColor, _T("backColor"), TYPE_RGBA, 0, 0,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_COLORSWATCH, IDC_GUI_BGCOLOR,
p_default, Color( 0.f, 0.f, 0.f ),
end,
plGUIColorSchemeComp::kRefSelForeColor, _T("selForeColor"), TYPE_RGBA, 0, 0,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_COLORSWATCH, IDC_GUI_SFGCOLOR,
p_default, Color( 1.f, 1.f, 1.f ),
end,
plGUIColorSchemeComp::kRefSelBackColor, _T("selBackColor"), TYPE_RGBA, 0, 0,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_COLORSWATCH, IDC_GUI_SBGCOLOR,
p_default, Color( 0.f, 0.f, 1.f ),
end,
plGUIColorSchemeComp::kRefForeAlpha, _T("foreAlpha"), TYPE_FLOAT, 0, 0,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_GUI_FGAEDIT, IDC_GUI_FGALPHA, 4,
p_range, 0.f, 1.f,
p_default, 1.f,
end,
plGUIColorSchemeComp::kRefBackAlpha, _T("backAlpha"), TYPE_FLOAT, 0, 0,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_GUI_BGAEDIT, IDC_GUI_BGALPHA, 4,
p_range, 0.f, 1.f,
p_default, 1.f,
end,
plGUIColorSchemeComp::kRefSelForeAlpha, _T("selForeAlpha"), TYPE_FLOAT, 0, 0,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_GUI_SFGAEDIT, IDC_GUI_SFGALPHA, 4,
p_range, 0.f, 1.f,
p_default, 1.f,
end,
plGUIColorSchemeComp::kRefSelBackAlpha, _T("selBackAlpha"), TYPE_FLOAT, 0, 0,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_GUI_SBGAEDIT, IDC_GUI_SBGALPHA, 4,
p_range, 0.f, 1.f,
p_default, 1.f,
end,
plGUIColorSchemeComp::kRefUseAlphas, _T("useAlphas"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SINGLECHEKBOX, IDC_GUI_USEALPHAS,
p_enable_ctrls, 4, plGUIColorSchemeComp::kRefForeAlpha, plGUIColorSchemeComp::kRefBackAlpha,
plGUIColorSchemeComp::kRefSelForeAlpha, plGUIColorSchemeComp::kRefSelBackAlpha,
end,
plGUIColorSchemeComp::kRefFontFace, _T("fontFace"), TYPE_STRING, 0, 0,
p_default, _T( "Times New Roman" ),
end,
plGUIColorSchemeComp::kRefFontSize, _T("fontSize"), TYPE_INT, 0, 0,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_GUI_FONTSIZE, IDC_GUI_FONTSIZE_SPIN, SPIN_AUTOSCALE,
p_default, 10,
end,
plGUIColorSchemeComp::kRefFontBold, _T("fontBold"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SINGLECHEKBOX, IDC_GUI_FONTBOLD,
end,
plGUIColorSchemeComp::kRefFontItalic, _T("fontItalic"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SINGLECHEKBOX, IDC_GUI_FONTITALIC,
end,
plGUIColorSchemeComp::kRefFontShadowed, _T("fontShadowed"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SINGLECHEKBOX, IDC_GUI_FONTSHADOWED,
end,
end
);
int CALLBACK plGUIColorSchemeProc::IMyFontEnumProc( const ENUMLOGFONTEX *logFontData, const NEWTEXTMETRICEX *physFontData,
unsigned long fontType, LPARAM lParam )
{
HWND combo = (HWND)lParam;
if( SendMessage( combo, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)logFontData->elfLogFont.lfFaceName ) == CB_ERR )
SendMessage( combo, CB_ADDSTRING, 0, (LPARAM)logFontData->elfLogFont.lfFaceName );
return -1;
}
void plGUIColorSchemeProc::ILoadFonts( HWND hWnd, IParamBlock2 *pb )
{
LOGFONT logFont;
logFont.lfCharSet = DEFAULT_CHARSET;
strcpy( logFont.lfFaceName, "" );
logFont.lfPitchAndFamily = 0;
SendMessage( hWnd, CB_RESETCONTENT, 0, 0 );
HDC hDC = GetDC( nil );
EnumFontFamiliesEx( hDC, &logFont, (FONTENUMPROC)IMyFontEnumProc, (LPARAM)hWnd, 0 );
ReleaseDC( nil, hDC );
SendMessage( hWnd, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)pb->GetStr( plGUIColorSchemeComp::kRefFontFace ) );
}
#define MAXTOCOLORREF( max ) RGB( max.r * 255.f, max.g * 255.f, max.b * 255.f )
void plGUIColorSchemeProc::Update( TimeValue t, Interval &valid, IParamMap2 *pmap )
{
}
BOOL plGUIColorSchemeProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
char str[ 256 ];
HWND placeCtrl;
PAINTSTRUCT paintInfo;
RECT previewRect, r;
HBRUSH bgPattBrush = nil;
Color fgColor, bgColor, selFgColor, selBgColor;
Color hatchColor = Color( 0.4f, 0.4f, 0.4f ), blendedColor, whiteColor = Color( 0.7f, 0.7f, 0.7f );
Color blackColor = Color( 0, 0, 0 ), blendedColor2;
float fgAlpha, bgAlpha, selFgAlpha, selBgAlpha;
char previewString[] = "Preview";
HFONT font;
switch( msg )
{
case WM_INITDIALOG:
ILoadFonts( GetDlgItem( hWnd, IDC_GUI_FONTFACE ), pmap->GetParamBlock() );
return true;
case WM_DESTROY:
break;
case WM_COMMAND:
if( LOWORD( wParam ) == IDC_GUI_FONTFACE )
{
if( HIWORD( wParam ) == CBN_SELCHANGE )
{
int idx = SendDlgItemMessage( hWnd, IDC_GUI_FONTFACE, CB_GETCURSEL, 0, 0 );
SendDlgItemMessage( hWnd, IDC_GUI_FONTFACE, CB_GETLBTEXT, idx, (LPARAM)str );
pmap->GetParamBlock()->SetValue( plGUIColorSchemeComp::kRefFontFace, 0, str );
}
}
break;
case CC_COLOR_CHANGE:
case CC_COLOR_DROP:
placeCtrl = ::GetDlgItem( hWnd, IDC_GUI_SCHEMEPREV );
::GetClientRect( placeCtrl, &previewRect );
::MapWindowPoints( placeCtrl, hWnd, (POINT *)&previewRect, 2 );
::InvalidateRect( hWnd, &previewRect, FALSE );
break;
case WM_PAINT:
fgColor = pmap->GetParamBlock()->GetColor( plGUIColorSchemeComp::kRefForeColor );
bgColor = pmap->GetParamBlock()->GetColor( plGUIColorSchemeComp::kRefBackColor );
selFgColor = pmap->GetParamBlock()->GetColor( plGUIColorSchemeComp::kRefSelForeColor );
selBgColor = pmap->GetParamBlock()->GetColor( plGUIColorSchemeComp::kRefSelBackColor );
fgAlpha = pmap->GetParamBlock()->GetFloat( plGUIColorSchemeComp::kRefForeAlpha );
bgAlpha = pmap->GetParamBlock()->GetFloat( plGUIColorSchemeComp::kRefBackAlpha );
selFgAlpha = pmap->GetParamBlock()->GetFloat( plGUIColorSchemeComp::kRefSelForeAlpha );
selBgAlpha = pmap->GetParamBlock()->GetFloat( plGUIColorSchemeComp::kRefSelBackAlpha );
if( pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefUseAlphas ) == 0 )
fgAlpha = bgAlpha = selFgAlpha = selBgAlpha = 1.f;
placeCtrl = ::GetDlgItem( hWnd, IDC_GUI_SCHEMEPREV );
::GetClientRect( placeCtrl, &previewRect );
::MapWindowPoints( placeCtrl, hWnd, (POINT *)&previewRect, 2 );
::BeginPaint( hWnd, &paintInfo );
::SetBkMode( paintInfo.hdc, TRANSPARENT );
int weight = pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefFontBold ) ? FW_BOLD : FW_NORMAL;
bool italic = pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefFontItalic ) ? true : false;
int nHeight = -MulDiv( pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefFontSize ), GetDeviceCaps( paintInfo.hdc, LOGPIXELSY ), 72 );
const char *face = pmap->GetParamBlock()->GetStr( plGUIColorSchemeComp::kRefFontFace );
font = ::CreateFont( nHeight, 0, 0, 0, weight, italic, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, face );
SelectObject( paintInfo.hdc, font );
// Left side
r = previewRect;
r.right = ( r.right + r.left ) >> 1;
blendedColor = bgColor * bgAlpha + ( whiteColor * ( 1.f - bgAlpha ) );
// doesn't like the Color to DWORD operator, so duplicating it here
#define ColorToDWORD(color) RGB(FLto255(color.r),FLto255(color.g),FLto255(color.b))
::SetBkColor( paintInfo.hdc, ColorToDWORD(blendedColor) );
blendedColor = bgColor * bgAlpha + ( hatchColor * ( 1.f - bgAlpha ) );
bgPattBrush = CreateHatchBrush( HS_DIAGCROSS, MAXTOCOLORREF( blendedColor ) );
::FillRect( paintInfo.hdc, &r, bgPattBrush );
if( pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefFontShadowed ) )
{
blendedColor2 = blackColor * fgAlpha + ( blendedColor * ( 1.f - fgAlpha ) );
::SetTextColor( paintInfo.hdc, MAXTOCOLORREF( blendedColor2 ) );
::OffsetRect( &r, 1, 1 );
::DrawText( paintInfo.hdc, previewString, strlen( previewString ), &r, DT_CENTER | DT_VCENTER );
::OffsetRect( &r, -1, -1 );
}
blendedColor = fgColor * fgAlpha + ( blendedColor * ( 1.f - fgAlpha ) );
::SetTextColor( paintInfo.hdc, MAXTOCOLORREF( blendedColor ) );
::DrawText( paintInfo.hdc, previewString, strlen( previewString ), &r, DT_CENTER | DT_VCENTER );
::DeleteObject( bgPattBrush );
// Right side
r.left = r.right;
r.right = previewRect.right;
blendedColor = selBgColor * selBgAlpha + ( whiteColor * ( 1.f - selBgAlpha ) );
::SetBkColor( paintInfo.hdc, ColorToDWORD(blendedColor) );
blendedColor = selBgColor * selBgAlpha + ( hatchColor * ( 1.f - selBgAlpha ) );
bgPattBrush = CreateHatchBrush( HS_DIAGCROSS, MAXTOCOLORREF( blendedColor ) );
::FillRect( paintInfo.hdc, &r, bgPattBrush );
if( pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefFontShadowed ) )
{
blendedColor2 = blackColor * selFgAlpha + ( blendedColor * ( 1.f - selFgAlpha ) );
::SetTextColor( paintInfo.hdc, MAXTOCOLORREF( blendedColor2 ) );
::OffsetRect( &r, 1, 1 );
::DrawText( paintInfo.hdc, previewString, strlen( previewString ), &r, DT_CENTER | DT_VCENTER );
::OffsetRect( &r, -1, -1 );
}
blendedColor = selFgColor * selFgAlpha + ( blendedColor * ( 1.f - selFgAlpha ) );
::SetTextColor( paintInfo.hdc, MAXTOCOLORREF( blendedColor ) );
::DrawText( paintInfo.hdc, previewString, strlen( previewString ), &r, DT_CENTER | DT_VCENTER );
::DeleteObject( bgPattBrush );
::DeleteObject( font );
::EndPaint( hWnd, &paintInfo );
return true;
}
return false;
}
plGUIColorSchemeComp::plGUIColorSchemeComp()
{
fClassDesc = &gGUIColorSchemeDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIColorSchemeComp::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
return true;
}
hsBool plGUIColorSchemeComp::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return true;
}
hsBool plGUIColorSchemeComp::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
pfGUIControlMod *ctrl = plGUIControlBase::GrabControlFromObject( node );
if( ctrl != nil )
{
pfGUIColorScheme *cs = TRACKED_NEW pfGUIColorScheme;
ConvertScheme( fCompPB, cs, pErrMsg );
ctrl->SetColorScheme( cs );
}
else
{
pErrMsg->Set( true, "GUI Color Scheme Error", "You have applied a GUI color scheme to an object (%s) without a GUI control. This scheme will be ignored.", node->GetName()).Show();
pErrMsg->Set( false );
return false;
}
return true;
}
void SMaxRGBAToPlasmaRGBA( Color maxRGB, hsColorRGBA &plasmaRGBA )
{
plasmaRGBA.Set( maxRGB.r, maxRGB.g, maxRGB.b, 1.f );
}
void plGUIColorSchemeComp::ConvertScheme( IParamBlock2 *pb, pfGUIColorScheme *destScheme, plErrorMsg *pErrMsg )
{
SMaxRGBAToPlasmaRGBA( pb->GetColor( kRefForeColor ), destScheme->fForeColor );
SMaxRGBAToPlasmaRGBA( pb->GetColor( kRefBackColor ), destScheme->fBackColor );
SMaxRGBAToPlasmaRGBA( pb->GetColor( kRefSelForeColor ), destScheme->fSelForeColor );
SMaxRGBAToPlasmaRGBA( pb->GetColor( kRefSelBackColor ), destScheme->fSelBackColor );
destScheme->fForeColor.a = pb->GetFloat( kRefForeAlpha );
destScheme->fBackColor.a = pb->GetFloat( kRefBackAlpha );
destScheme->fSelForeColor.a = pb->GetFloat( kRefSelForeAlpha );
destScheme->fSelBackColor.a = pb->GetFloat( kRefSelBackAlpha );
destScheme->fTransparent = pb->GetInt( kRefUseAlphas ) ? true : false;
destScheme->SetFontFace( pb->GetStr( kRefFontFace ) );
destScheme->fFontSize = pb->GetInt( kRefFontSize );
destScheme->fFontFlags = 0;
if( pb->GetInt( kRefFontBold ) )
destScheme->fFontFlags |= pfGUIColorScheme::kFontBold;
if( pb->GetInt( kRefFontItalic ) )
destScheme->fFontFlags |= pfGUIColorScheme::kFontItalic;
if( pb->GetInt( kRefFontShadowed ) )
destScheme->fFontFlags |= pfGUIColorScheme::kFontShadowed;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIProxy Rollout /////////////////////////////////////////////////////////////////////////
//
// Defines a proxy object to be used when calculating mouse-down locations and dynamic text
// sizing.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
enum plProxyRefs
{
kRefProxyNode = 196,
kRefHideProxy,
kRefBetterHitTests
};
//// DialogProc /////////////////////////////////////////////////////////////////////////////////
class plGUIProxyDlgProc : public ParamMap2UserDlgProc
{
public:
plGUIProxyDlgProc()
{
}
void DeleteThis() {}
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
IParamBlock2 *pblock = map->GetParamBlock();
switch( msg )
{
case WM_COMMAND:
// if( LOWORD( wParam ) == IDC_GUI_CLEAR )
// {
// pblock->Reset( (ParamID)kRefProxyNode );
// return true;
// }
break;
}
return false;
}
};
static plGUIProxyDlgProc sGUIProxyDlgProc;
//// ParamBlock /////////////////////////////////////////////////////////////////////////////////
// Note: we can't make this a real ParamBlock and do P_INCLUDE_PARAMS because, in Discreet's
// amazing method of doing things, we can't INCLUDE more than one ParamBlock in any other PB.
// So either we chain them together here (and thus make them dependent on one another, which
// is lame) or we just make the whole damned thing a #define, which is all P_INCLUDE_PARAMS
// really does anyway.
#define sGUIProxyParamHeader plGUIControlBase::kRollProxy, IDD_COMP_GUIPROXY, IDS_COMP_GUIPROXY, 0, APPENDROLL_CLOSED, &sGUIProxyDlgProc
//static ParamBlockDesc2 sSndEAXPropsParamTemplate
//(
/// Main def
// plComponent::kBlkComp + 1, _T("sndEAXProps"), 0, nil, P_AUTO_UI + P_MULTIMAP + P_AUTO_CONSTRUCT, plComponent::kRefComp,
// 1,
// kSndEAXParams, IDD_COMP_EAXBUFFER, IDS_COMP_EAXBUFFER, 0, 0, nil,
#define sGUIProxyParamTemplate \
\
kRefBetterHitTests, _T("guiBetterHitTests"), TYPE_BOOL, 0, 0, \
p_ui, plGUIControlBase::kRollProxy, TYPE_SINGLECHEKBOX, IDC_GUI_BETTERHIT, \
p_default, false, \
end
// , end
//);
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIDialog Component //////////////////////////////////////////////////////////////////////
//
// Defines a dialog box (i.e. a collection of controls) to be defined with the GUI manager at
// runtime. Acts a lot like a CamView component, but it additionally handles a few other things.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
class plGUIDialogProc : public ParamMap2UserDlgProc
{
protected:
void ILoadPages( HWND hWnd, IParamBlock2 *pb );
public:
void DeleteThis() {}
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
};
static plGUIDialogProc gGUIDialogProc;
//Max desc stuff necessary below.
CLASS_DESC(plGUIDialogComponent, gGUIDialogDesc, "GUI Dialog", "GUIDialog", COMP_TYPE_GUI, GUI_DIALOG_COMP_CLASS_ID )
ParamBlockDesc2 gGUIDialogBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIDialog"), 0, &gGUIDialogDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
3,
plGUIDialogComponent::kMainRollout, IDD_COMP_GUIDIALOG, IDS_COMP_GUIDIALOG, 0, 0, &gGUIDialogProc,
plGUIDialogComponent::kTagIDRollout, IDD_COMP_GUITAG, IDS_COMP_GUITAG, 0, 0, &gGUITagProc,
plGUIDialogComponent::kSchemeRollout, IDD_COMP_GUISCHEME, IDS_COMP_GUISCHEME, 0, 0, &gGUIColorSchemeProc,
&gGUIColorSchemeBk,
plGUIDialogComponent::kRefDialogName, _T("DialogName"), TYPE_STRING, 0, 0,
// p_ui, plGUIDialogComponent::kMainRollout, TYPE_EDITBOX, IDC_GUIDLG_NAME,
end,
plGUIDialogComponent::kRefAgeName, _T("ageName"), TYPE_STRING, 0, 0,
p_default, _T( "GUI" ),
end,
plGUIDialogComponent::kRefIsModal, _T("isModal"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, plGUIDialogComponent::kMainRollout, TYPE_SINGLECHEKBOX, IDC_COMP_GUI_MODAL,
end,
plGUIDialogComponent::kRefVersion, _T("version"), TYPE_INT, 0, 0,
p_ui, plGUIDialogComponent::kMainRollout, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_GUI_VERSION, IDC_GUI_VERSION_SPIN, SPIN_AUTOSCALE,
p_default, 0,
end,
plGUITagComponent::kRefCurrIDSel, _T("currSel"), TYPE_INT, 0, 0,
end,
end
);
plGUIDialogComponent::plGUIDialogComponent( hsBool dontInit )
{
if( !dontInit )
{
fClassDesc = &gGUIDialogDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
fDialogMod = nil;
fProcReceiver = nil;
}
pfGUIDialogMod *plGUIDialogComponent::IMakeDialog( void )
{
return TRACKED_NEW pfGUIDialogMod();
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIDialogComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
TimeValue timeVal( 0 );
Object* obj = node->EvalWorldState( timeVal ).obj;
fDialogMod = nil;
if( obj->CanConvertToType( Class_ID( LOOKAT_CAM_CLASS_ID, 0 ) ) ||
obj->CanConvertToType( Class_ID( SIMPLE_CAM_CLASS_ID, 0 ) ) )
{
// We're applied to a camera. Do our camera stuff
node->SetForceLocal( true );
}
else
{
// We're applied to a normal object.
node->SetNoSpanReSort(true);
node->SetNoSpanSort(true);
}
/// Either way, we mangle our own location component. None of this user-defined-location stuff.
const char *dialogName = fCompPB->GetStr( kRefDialogName );
if( dialogName == nil || *dialogName == 0 )
{
pErrMsg->Set(true, "GUI Dialog Component Error", "No dialog name specified on GUI Dialog component (object: %s)", node->GetName()).Show();
return false;
}
const char *ageName = fCompPB->GetStr(kRefAgeName);
int32_t seqNum = plPageInfoUtils::GetSeqNumFromAgeDesc( ageName, dialogName );
int32_t newNum = plPluginResManager::ResMgr()->VerifySeqNumber( seqNum, ageName, dialogName );
if( newNum != seqNum )
{
if( !fSeqNumValidated )
{
plLocation pageLoc = plPluginResManager::ResMgr()->FindLocation( ageName, dialogName );
int32_t pageSeqNum = pageLoc.GetSequenceNumber();
char errMsg[ 512 ];
sprintf( errMsg, "The sequence number stored by the resource manager (0x%X) for page %s, District, %s does not match\n"
"the sequence number stored in the .age file (0x%X). Forcing it to use the one in the .age file",
pageSeqNum, ageName, dialogName, seqNum );
pErrMsg->Set( true, "PageInfo Convert Error", errMsg ).Show();
pErrMsg->Set( false );
fSeqNumValidated = true;
}
// force the component to use the sequence number in the .age file
//seqNum = newNum;
}
plKey roomKey = plPluginResManager::ResMgr()->NameToLoc( fCompPB->GetStr( kRefAgeName ), fCompPB->GetStr( kRefDialogName ), seqNum );
if( !roomKey )
{
pErrMsg->Set( true, "GUI Dialog Component Error", "GUI Dialog Component %s has a Missing Location. Nuke the files in the dat directory and re-export.",((INode*)node)->GetName()).Show();
return false;
}
node->SetRoomKey( roomKey );
// Also, we make sure this node will never be fogged (affects material convert)
node->SetIsGUI( true );
return true;
}
hsBool plGUIDialogComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
TimeValue timeVal(0);
Object* obj = node->EvalWorldState(timeVal).obj;
GenCamera* cam = nil;
if( obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0)) )
cam = (GenCamera *) obj->ConvertToType(timeVal, Class_ID(LOOKAT_CAM_CLASS_ID, 0));
else
if( obj->CanConvertToType(Class_ID(SIMPLE_CAM_CLASS_ID, 0)) )
cam = (GenCamera *) obj->ConvertToType(timeVal, Class_ID(SIMPLE_CAM_CLASS_ID, 0));
if( !cam )
{
// Not applied to a camera, so applied to a normal object. Since this is valid (we also act
// as a location component), just return
return true;
}
plPostEffectMod* mod = TRACKED_NEW plPostEffectMod;
float hither = cam->GetEnvRange(timeVal, ENV_NEAR_RANGE);
if( hither < 0.5f )
hither = 0.5f;
float yon = cam->GetEnvRange(timeVal, ENV_FAR_RANGE);
mod->SetHither(hither);
mod->SetYon(yon);
// radians
float fov = cam->GetFOV(timeVal);
// convert
int FOVType = cam->GetFOVType();
float fovX, fovY;
switch(FOVType)
{
case 0: // FOV_W
{
fovX = fov;
fovY = fovX *3.f / 4.f;
}
break;
case 1: // FOV_H
{
fovY = fov;
fovX = fovY * 4.f / 3.f;
}
break;
}
fovX *= 180.f / M_PI;
fovY *= 180.f / M_PI;
mod->SetFovX(fovX);
mod->SetFovY(fovY);
// Should already be created from SetupProperties...
// Note: can't just grab the node's room key, 'cause we might not be on the right node!
plKey sceneNodeKey = plPluginResManager::ResMgr()->NameToLoc( fCompPB->GetStr( kRefAgeName ),
fCompPB->GetStr( kRefDialogName ), (uint32_t)-1 );
mod->SetNodeKey( sceneNodeKey );
// node->AddModifier(mod);
// Note: we do NOT add this to the sceneObject, we don't want it actually associated with
// a sceneObject. Instead, we just grab the LocalToWorld() off the sceneObject, since that's
// all we want
hsMatrix44 l2w = node->GetLocalToWorld44();
hsMatrix44 w2l = node->GetWorldToLocal44();
mod->SetWorldToCamera( w2l, l2w );
// Add it to the sceneNode as a generic interface, so it gets loaded with the sceneNode
plLocation nodeLoc = sceneNodeKey->GetUoid().GetLocation();
plKey modKey = hsgResMgr::ResMgr()->NewKey( fCompPB->GetStr( kRefDialogName ), mod, nodeLoc );
hsgResMgr::ResMgr()->AddViaNotify( modKey, TRACKED_NEW plNodeRefMsg( sceneNodeKey, plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric ), plRefFlags::kActiveRef );
// Also add our dialog mod to the scene node in the same way
hsgResMgr::ResMgr()->AddViaNotify( fDialogMod->GetKey(), TRACKED_NEW plNodeRefMsg( sceneNodeKey, plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric ), plRefFlags::kActiveRef );
/// Already created our mod, just gotta fill it out
fDialogMod->SetRenderMod( mod );
fDialogMod->SetName( fCompPB->GetStr( kRefDialogName ) );
if( fCompPB->GetInt( kRefIsModal ) )
fDialogMod->SetFlag( pfGUIDialogMod::kModal );
fDialogMod->SetProcReceiver(fProcReceiver);
fDialogMod->SetVersion( fCompPB->GetInt( kRefVersion ) );
plGUIColorSchemeComp::ConvertScheme( fCompPB, fDialogMod->GetColorScheme(), pErrMsg );
return true;
}
hsBool plGUIDialogComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
TimeValue timeVal(0);
Object* obj = node->EvalWorldState(timeVal).obj;
if( obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0))
|| obj->CanConvertToType(Class_ID(SIMPLE_CAM_CLASS_ID, 0)) )
{
// Don't do this. -mf
// IMakeEveryoneOpaque(node);
// Make a blank dialog modifier, which will be filled in on convert. Do
// this as a separate step so the dialog controls can query and get the dialog
// mod to store a ref to
fDialogMod = IMakeDialog();
// Note: can't just grab the node's room key, 'cause we might not be on the right node!
plKey sceneNodeKey = plPluginResManager::ResMgr()->NameToLoc( fCompPB->GetStr( kRefAgeName ),
fCompPB->GetStr( kRefDialogName ), (uint32_t)-1 );
plLocation nodeLoc = sceneNodeKey->GetUoid().GetLocation();
plKey dlgKey = hsgResMgr::ResMgr()->NewKey( fCompPB->GetStr( kRefDialogName ), fDialogMod, nodeLoc );
fDialogMod->SetSceneNodeKey( sceneNodeKey );
// See if there's a tag to be had
uint32_t id = fCompPB->GetInt( plGUITagComponent::kRefCurrIDSel );
if( id > 0 )
fDialogMod->SetTagID( id );
fProcReceiver = nil;
}
else
{
}
return true;
}
void plGUIDialogComponent::IMakeEveryoneOpaque(plMaxNode* node)
{
plMaxNode* root = (plMaxNode *)node->GetInterface()->GetRootNode();
int i;
for( i = 0; i < root->NumberOfChildren(); i++ )
IMakeEveryoneOpaqueRecur((plMaxNode*)(root->GetChildNode(i)));
}
void plGUIDialogComponent::IMakeEveryoneOpaqueRecur(plMaxNode* node)
{
if( node->CanConvert() )
{
node->SetNoSpanReSort(true);
node->SetNoSpanSort(true);
int i;
for( i = 0; i < node->NumberOfChildren(); i++ )
{
IMakeEveryoneOpaqueRecur((plMaxNode *)(node->GetChildNode(i)));
}
}
}
plKey plGUIDialogComponent::GetModifierKey( void )
{
if( fDialogMod != nil )
return fDialogMod->GetKey();
return nil;
}
bool plGUIDialogComponent::SetNotifyReceiver( plKey key )
{
if( fProcReceiver != nil )
return false;
fProcReceiver = key;
return true;
}
pfGUIDialogMod *plGUIDialogComponent::GetNodeDialog( plMaxNode *childNode )
{
uint32_t i, numComp = childNode->NumAttachedComponents( false );
for( i = 0; i < numComp; i++ )
{
plComponentBase *comp = childNode->GetAttachedComponent( i );
if( comp->ClassID() == GUI_DIALOG_COMP_CLASS_ID )
return ( (plGUIDialogComponent *)comp )->GetModifier();
}
return nil;
}
void plGUIDialogProc::ILoadPages( HWND hWnd, IParamBlock2 *pb )
{
plAgeDescription *aged = plPageInfoUtils::GetAgeDesc( pb->GetStr( plGUIDialogComponent::kRefAgeName ) );
if( aged == nil )
return;
plAgePage *page;
const char *selPageName = pb->GetStr( plGUIDialogComponent::kRefDialogName );
aged->SeekFirstPage();
ComboBox_ResetContent( hWnd );
while( ( page = aged->GetNextPage() ) != nil )
{
int idx = ComboBox_AddString( hWnd, page->GetName() );
if( selPageName && stricmp( page->GetName(), selPageName ) == 0 )
ComboBox_SetCurSel( hWnd, idx );
}
delete aged;
}
BOOL plGUIDialogProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_INITDIALOG:
// Load the age combo box
{
int i, idx, selIdx = 0;
HWND ageCombo = GetDlgItem( hWnd, IDC_GUIDLG_AGE );
hsTArray<char *> ageList;
plAgeDescInterface::BuildAgeFileList( ageList );
ComboBox_ResetContent( ageCombo );
for( i = 0; i < ageList.GetCount(); i++ )
{
char ageName[ _MAX_FNAME ];
_splitpath( ageList[ i ], nil, nil, ageName, nil );
idx = ComboBox_AddString( ageCombo, ageName );
if( stricmp( ageName, pmap->GetParamBlock()->GetStr( plGUIDialogComponent::kRefAgeName ) ) == 0 )
{
selIdx = idx;
}
}
ComboBox_SetCurSel( ageCombo, selIdx );
}
ILoadPages( GetDlgItem( hWnd, IDC_GUIDLG_NAME ), pmap->GetParamBlock() );
return true;
case WM_DESTROY:
break;
case WM_COMMAND:
if( HIWORD( wParam ) == CBN_SELCHANGE )
{
if( LOWORD( wParam ) == IDC_GUIDLG_NAME )
{
int idx = SendDlgItemMessage( hWnd, IDC_GUIDLG_NAME, CB_GETCURSEL, 0, 0 );
if( idx != CB_ERR )
{
char name[ 256 ];
ComboBox_GetLBText( GetDlgItem( hWnd, IDC_GUIDLG_NAME ), idx, name );
pmap->GetParamBlock()->SetValue( plGUIDialogComponent::kRefDialogName, 0, name );
}
}
else if( LOWORD( wParam ) == IDC_GUIDLG_AGE )
{
int idx = SendDlgItemMessage( hWnd, IDC_GUIDLG_AGE, CB_GETCURSEL, 0, 0 );
if( idx != CB_ERR )
{
char name[ 256 ];
ComboBox_GetLBText( GetDlgItem( hWnd, IDC_GUIDLG_AGE ), idx, name );
pmap->GetParamBlock()->SetValue( plGUIDialogComponent::kRefAgeName, 0, name );
}
ILoadPages( GetDlgItem( hWnd, IDC_GUIDLG_NAME ), pmap->GetParamBlock() );
}
}
break;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIControl Component Base Class //////////////////////////////////////////////////////////
//
// Defines a base class for all GUI control components.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
void plGUIControlBase::CollectNonDrawables( INodeTab &nonDrawables )
{
/* if( ICanHaveProxy() )
{
bool hideProxy = fCompPB->GetInt( (ParamID)kRefHideProxy ) ? true : false;
if( hideProxy )
{
INode *node = fCompPB->GetINode( (ParamID)kRefProxyNode );
if( node != nil )
nonDrawables.Append( 1, &node );
}
}
*/
}
pfGUIDialogMod *plGUIControlBase::IGetDialogMod( plMaxNode *node )
{
uint32_t i;
for( i = 0; i < node->NumAttachedComponents( false ); i++ )
{
plComponentBase *comp = node->GetAttachedComponent( i, false );
if( comp->ClassID() == GUI_DIALOG_COMP_CLASS_ID )
{
// Found it!
pfGUIDialogMod *dlgMod = ((plGUIDialogComponent *)comp)->GetModifier();
return dlgMod;
}
}
return nil;
}
hsBool plGUIControlBase::SetupProperties( plMaxNode *pNode, plErrorMsg *pErrMsg )
{
if( INeedsDynamicText() )
{
// If we're going to be using a dynamic text layer, we need to make sure the material
// is unique for every node we're applied to
pNode->SetForceMaterialCopy( true );
}
return true;
}
hsBool plGUIControlBase::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
// Create a new control
fControl = IGetNewControl();
// Add it as a modifier to this node
node->AddModifier( fControl, IGetUniqueName(node) );
// Look for any tag IDs
uint32_t id = plGUITagComponent::GetTagIDOnNode( node );
if( id > 0 )
fControl->SetTagID( id );
// Now add it to our list of converted nodes
uint32_t i = fTargetNodes.Find( node );
if( i == fTargetNodes.kMissingIndex )
{
fTargetNodes.Append( node );
fTargetControls.Append( fControl );
}
else
{
fTargetControls[ i ] = fControl;
}
return true;
}
hsBool plGUIControlBase::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
// Error check--make sure we're in the same room as our parent (can get confusing with the wrong
// parent-child relationships)
if( !node->GetParentNode()->IsRootNode() )
{
plMaxNode *parent = (plMaxNode *)node->GetParentNode();
if( parent->GetRoomKey() != node->GetRoomKey() )
{
pErrMsg->Set( true, "GUI Control Component Error", "The object %s is assigned to a different GUI dialog than its parent. Make sure both this object and its parent belong to the same GUI dialog (this control will be ignored).", node->GetName() ).Show();
pErrMsg->Set( false );
return false;
}
}
pfGUIDialogMod *dialog = IGetDialogMod( node );
if( dialog == nil )
{
pErrMsg->Set( true, "GUI Control Component Error", "The object %s has a GUI control applied but not a GUI Dialog Component. Apply a GUI Dialog Component to this object.", node->GetName() ).Show();
pErrMsg->Set( false );
return false;
}
// Grab fControl from the modifier list on the node, since fControl isn't valid
// between PreConvert() and Convert() (it might get called multiple times, once per node applied)
uint32_t i = fTargetNodes.Find( node );
if( i == fTargetNodes.kMissingIndex )
{
pErrMsg->Set( true, "GUI Control Component Error", "The object %s somehow skipped the GUI control Pre-convert stage. Inform a programmer immediately and seek shelter.", node->GetName() ).Show();
pErrMsg->Set( false );
return false;
}
fControl = fTargetControls[ i ];
dialog->AddControlOnExport( fControl );
if( IHasProcRollout() )
{
// Also common for all controls: process the Procedure rollout--i.e. what kind of control proc do we get?
switch( fCompPB->GetInt( kRefChoice ) )
{
case 0:
// Console command
fControl->SetHandler( TRACKED_NEW pfGUIConsoleCmdProc( fCompPB->GetStr( kRefConsoleCmd ) ) );
break;
case 1:
// Inherit from parent dialog - this is a runtime flag, so we don't bother actually setting
// a handler here, except to ensure it's nil
fControl->SetHandler( nil );
fControl->SetFlag( pfGUIControlMod::kInheritProcFromDlg );
break;
case 2:
fControl->SetHandler( TRACKED_NEW pfGUICloseDlgProc() );
break;
case 3:
// Do nothing. Just set a nil proc, but do NOT inherit from the dialog
fControl->SetHandler( nil );
fControl->ClearFlag( pfGUIControlMod::kInheritProcFromDlg );
break;
}
}
if( INeedsDynamicText() )
{
// We're a control that dynamically creates text, so look for the first dynamic layer
// (and hopefully the ONLY one) and store it on the control
Mtl *maxMaterial = hsMaterialConverter::Instance().GetBaseMtl( node );
hsTArray<plExportMaterialData> *mtlArray = hsMaterialConverter::Instance().CreateMaterialArray( maxMaterial, node, 0 );
uint32_t i, j;
plDynamicTextMap *dynText = nil;
plLayerInterface *layerIFace = nil;
for( i = 0; i < mtlArray->GetCount() && dynText == nil; i++ )
{
hsGMaterial *plasmaMat = (*mtlArray)[ 0 ].fMaterial;
for( j = 0; j < plasmaMat->GetNumLayers(); j++ )
{
layerIFace = plasmaMat->GetLayer( j );
dynText = plDynamicTextMap::ConvertNoRef( layerIFace->GetTexture() );
if( dynText != nil )
break;
}
}
if( dynText == nil )
{
pErrMsg->Set( true, "GUI Component Error", "The object %s needs a Plasma Dynamic Text Layer in its material. "
"This control will not function properly until you apply one.", node->GetName() ).Show();
pErrMsg->Set( false );
}
else
fControl->SetDynTextMap( layerIFace, dynText );
delete mtlArray;
}
if( ICanHaveProxy() )
{
// No proxy objects just yet, just options for better hit testing
if( fCompPB->GetInt( kRefBetterHitTests ) )
fControl->SetFlag( pfGUIControlMod::kBetterHitTesting );
}
return true;
}
pfGUIControlMod *plGUIControlBase::GrabControlFromObject( INode *node )
{
uint32_t i;
plMaxNodeBase *maxNode = (plMaxNodeBase *)node;
for( i = 0; i < maxNode->NumAttachedComponents( false ); i++ )
{
plComponentBase *comp = maxNode->GetAttachedComponent( i, false );
pfGUIControlMod *ctrl = ConvertCompToControl( comp, maxNode );
if( ctrl != nil )
return ctrl;
}
return nil;
}
// Given an INode, gives you a pointer to the GUI component if it actually is one, nil otherwise
plGUIControlBase *plGUIControlBase::GetGUIComp( INode *node )
{
if( node == nil )
return nil;
return GetGUIComp( ( ( plMaxNodeBase *)node )->ConvertToComponent() );
}
plGUIControlBase *plGUIControlBase::GetGUIComp( plComponentBase *comp )
{
if( comp == nil )
return nil;
if( comp->ClassID() == GUI_UPDOWNPAIR_CLASSID ||
comp->ClassID() == GUI_BUTTON_CLASSID ||
comp->ClassID() == GUI_DRAGGABLE_CLASSID ||
comp->ClassID() == GUI_LISTBOX_CLASSID ||
comp->ClassID() == GUI_TEXTBOX_CLASSID ||
comp->ClassID() == GUI_EDITBOX_CLASSID ||
comp->ClassID() == GUI_KNOBCTRL_CLASSID ||
comp->ClassID() == GUI_DRAGBAR_CLASSID ||
comp->ClassID() == GUI_CHECKBOX_CLASSID ||
comp->ClassID() == GUI_RADIOGROUP_CLASSID ||
comp->ClassID() == GUI_DYNDISPLAY_CLASSID ||
comp->ClassID() == GUI_MULTILINE_CLASSID ||
comp->ClassID() == GUI_PROGRESS_CLASSID ||
comp->ClassID() == GUI_CLICKMAP_CLASSID )
{
return (plGUIControlBase *)comp;
}
return nil;
}
pfGUIControlMod *plGUIControlBase::GrabControlMod( INode *node, INode *sceneObjectNode )
{
if( node == nil )
return nil;
plComponentBase *comp = ( ( plMaxNodeBase *)node )->ConvertToComponent();
return ConvertCompToControl( comp, sceneObjectNode );
}
pfGUIControlMod *plGUIControlBase::ConvertCompToControl( plComponentBase *comp, INode *sceneObjectNode )
{
plGUIControlBase *base = GetGUIComp( comp );
if( base != nil )
{
if( sceneObjectNode == nil )
{
// Not good, but if you select a component like this, it better only be applied to one object,
// hence will only have one fTargetControl
if( base->fTargetControls.GetCount() > 0 )
return base->fTargetControls[ 0 ];
}
else
{
uint32_t i = base->fTargetNodes.Find( (plMaxNode *)sceneObjectNode );
if( i == base->fTargetNodes.kMissingIndex )
return nil;
return base->fTargetControls[ i ];
}
}
return nil;
}
const char *plGUIControlBase::ISetSoundIndex( ParamID checkBoxID, ParamID sndCompID, uint8_t guiCtrlEvent, plMaxNode *maxNode )
{
if( fCompPB->GetInt( checkBoxID ) )
{
plMaxNode *sndNode = (plMaxNode *)fCompPB->GetReferenceTarget( sndCompID );
if( sndNode != nil )
{
plComponentBase *comp = sndNode->ConvertToComponent();
if( comp != nil )
{
int idx = plAudioComp::GetSoundModIdx( comp, maxNode );
if( idx != -1 )
{
fControl->SetSoundIndex( guiCtrlEvent, idx );
return nil;
}
else
return "The selected sound component could not be found on GUI control %s. Make sure you have a sound component on the same object selected.";
}
else
return "The selected sound node on GUI control %s could not be converted to a component. Make sure you have a sound component selected.";
}
else
return "The GUI control %s has a sound event enabled but no sound component selected. Make sure you have a sound component on the same object selected.";
}
return nil;
}
//// ParamBlock for Control Proc Rollout ////////////////////////////////////////////////////////
static ParamBlockDesc2 sGUIControlProcParamTemplate
(
/// Main def
plGUIControlBase::kBlkProc, _T("GUIControlProc"), 0, nil, P_AUTO_UI + P_MULTIMAP + P_AUTO_CONSTRUCT, plComponent::kRefComp,
1,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
plGUIControlBase::kRefChoice, _T("which"), TYPE_INT, 0, 0,
p_ui, plGUIControlBase::kRollProc, TYPE_RADIO, 4, IDC_GUI_CONRADIO, IDC_GUI_INHERITRADIO, IDC_GUI_CLOSERADIO, IDC_GUI_NILRADIO,
p_default, 1,
end,
plGUIControlBase::kRefConsoleCmd, _T("ConsoleCmd"), TYPE_STRING, 0, 0,
p_ui, plGUIControlBase::kRollProc, TYPE_EDITBOX, IDC_GUI_CONCMD,
end,
end
);
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIButton Component //////////////////////////////////////////////////////////////////////
//
// Defines a dialog button to be defined with the GUI manager at runtime. Belongs to exactly
// one dialog, defined by parent-child relationship, also at runtime.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
class plGUIButtonProc : public ParamMap2UserDlgProc
{
public:
void DeleteThis() {}
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
};
static plGUIButtonProc gGUIButtonProc;
// Class that accesses the paramblock below.
class plGUIButtonComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIButtonMod; }
virtual bool ICanHaveProxy( void ) { return true; }
public:
plGUIButtonComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefConCmdRadio,
kRefPythonRadio,
kRefConsoleCmd,
kRefAnimate,
kRefAnimation,
kRefMouseOverAnimate,
kRefMouseOverAnimation,
kRefMouseDownSound,
kRefMouseDownSoundComp,
kRefMouseUpSound,
kRefMouseUpSoundComp,
kRefMouseOverSound,
kRefMouseOverSoundComp,
kRefMouseOffSound,
kRefMouseOffSoundComp,
kRefAnimationNode,
kRefAnimationNodeType,
kRefMouseOverAnimationNode,
kRefMouseOverAnimationNodeType,
kRefDraggableChild,
kRefUseDraggableChild,
kRefNotifyType
};
};
BOOL plGUIButtonProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_INITDIALOG:
SendMessage( GetDlgItem( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE ), CB_RESETCONTENT, 0, 0 );
SendMessage( GetDlgItem( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE ), CB_ADDSTRING, 0, (LPARAM)"Button Up" );
SendMessage( GetDlgItem( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE ), CB_ADDSTRING, 0, (LPARAM)"Button Down" );
SendMessage( GetDlgItem( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE ), CB_ADDSTRING, 0, (LPARAM)"Button Down and Up" );
SendMessage( GetDlgItem( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE ), CB_SETCURSEL, pmap->GetParamBlock()->GetInt( plGUIButtonComponent::kRefNotifyType ), 0 );
return true;
case WM_DESTROY:
break;
case WM_COMMAND:
if( LOWORD( wParam ) == IDC_COMBO_BUTTON_NOTIFYTYPE )
{
if( HIWORD( wParam ) == CBN_SELCHANGE )
{
int idx = SendDlgItemMessage( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE, CB_GETCURSEL, 0, 0 );
pmap->GetParamBlock()->SetValue( plGUIButtonComponent::kRefNotifyType, 0, idx );
}
}
break;
}
return false;
}
class plGUIButtonAccessor : public PBAccessor
{
public:
void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t )
{
if( id == plGUIButtonComponent::kRefAnimation ||
id == plGUIButtonComponent::kRefMouseOverAnimation ||
id == plGUIButtonComponent::kRefMouseDownSoundComp ||
id == plGUIButtonComponent::kRefMouseUpSoundComp ||
id == plGUIButtonComponent::kRefMouseOverSoundComp ||
id == plGUIButtonComponent::kRefMouseOffSoundComp ||
id == plGUIButtonComponent::kRefAnimationNode ||
id == plGUIButtonComponent::kRefMouseOverAnimationNode )
{
plGUIButtonComponent *comp = (plGUIButtonComponent *)owner;
comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED );
}
}
};
Class_ID sBtnClassesToSelect[] = { ANIM_COMP_CID, ANIM_GROUP_COMP_CID, plGUISingleCtrlDlgProc::kEndClassList };
Class_ID sBtnSndClassesToSelect[] = { GUI_SOUND_COMPONENT_ID, plGUISingleCtrlDlgProc::kEndClassList };
Class_ID sBtnDragClassesToSelect[] = { GUI_DRAGGABLE_CLASSID, plGUISingleCtrlDlgProc::kEndClassList };
static plGUIButtonAccessor sGUIButtonAccessor;
static plGUISingleCtrlDlgProc sGUIButtonSndAProc( plGUIButtonComponent::kRefMouseDownSoundComp, IDC_GUI_MDOWNSNDCOMP,
"Select the sound to play when the mouse clicks this button", sBtnSndClassesToSelect );
static plGUISingleCtrlDlgProc sGUIButtonSndBProc( plGUIButtonComponent::kRefMouseUpSoundComp, IDC_GUI_MUPSNDCOMP,
"Select the sound to play when the mouse lets up on this button", sBtnSndClassesToSelect );
static plGUISingleCtrlDlgProc sGUIButtonSndCProc( plGUIButtonComponent::kRefMouseOverSoundComp, IDC_GUI_MOVERSNDCOMP,
"Select the sound to play when the mouse moves over this button", sBtnSndClassesToSelect );
static plGUISingleCtrlDlgProc sGUIButtonSndDProc( plGUIButtonComponent::kRefMouseOffSoundComp, IDC_GUI_MOFFSNDCOMP,
"Select the sound to play when the mouse moves off of this button", sBtnSndClassesToSelect );
static plGUISingleCtrlDlgProc sGUIButtonDragChildProc( plGUIButtonComponent::kRefDraggableChild, IDC_GUI_DRAGCHILD,
"Select the draggable to use when the mouse is dragged off of this button", sBtnDragClassesToSelect );
static plGUISingleCtrlDlgProc *sGUIButtonSubProcs[] = { &sGUIButtonSndAProc, &sGUIButtonSndBProc,
&sGUIButtonSndCProc, &sGUIButtonSndDProc,
&sGUIButtonDragChildProc, nil };
static ParamMap2UserDlgProc *sGUIButtonSubSubProcs[] = { &gGUIButtonProc, nil };
static plGUIMultipleCtrlDlgProc sGUIButtonSels( sGUIButtonSubProcs, sGUIButtonSubSubProcs );
static plPlasmaAnimSelectDlgProc sGUIButtonAnimA( plGUIButtonComponent::kRefAnimation, IDC_GUI_COMPSELBTN,
plGUIButtonComponent::kRefAnimationNode, plGUIButtonComponent::kRefAnimationNodeType, IDC_GUI_ANIMNODESEL,
"Select the animation to play when this button is clicked", &sGUIButtonSels );
static plPlasmaAnimSelectDlgProc sGUIButtonProc( plGUIButtonComponent::kRefMouseOverAnimation, IDC_GUI_COMPSELBTN2,
plGUIButtonComponent::kRefMouseOverAnimationNode, plGUIButtonComponent::kRefMouseOverAnimationNodeType, IDC_GUI_ANIMNODESEL2,
"Select the animation to play when the mouse moves over this button", &sGUIButtonAnimA );
#define GUI_SOUND_REF( comp, evt, allCapsEvt ) \
comp##::kRefMouse##evt##Sound, _T( "mouse##evt##Sound" ), TYPE_BOOL, 0, 0, \
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_M##allCapsEvt##SND, \
p_default, FALSE, \
p_enable_ctrls, 1, comp##::kRefMouse##evt##SoundComp, \
end, \
comp##::kRefMouse##evt##SoundComp, _T("mouse##evt##SoundComp"), TYPE_INODE, 0, 0, \
p_accessor, &sGUIButtonAccessor, \
end
//Max desc stuff necessary below.
CLASS_DESC(plGUIButtonComponent, gGUIButtonDesc, "GUI Button", "GUIButton", COMP_TYPE_GUI, GUI_BUTTON_CLASSID )
ParamBlockDesc2 gGUIButtonBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIButton"), 0, &gGUIButtonDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_INCLUDE_PARAMS + P_MULTIMAP, plComponent::kRefComp,
3,
plGUIControlBase::kRollMain, IDD_COMP_GUIBUTTON, IDS_COMP_GUIBUTTON, 0, 0, &sGUIButtonProc,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
sGUIProxyParamHeader,
&sGUIControlProcParamTemplate,
plGUIButtonComponent::kRefAnimate, _T( "animate" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ANIMATE,
p_default, FALSE,
p_enable_ctrls, 1, plGUIButtonComponent::kRefAnimation,
end,
plGUIButtonComponent::kRefAnimation, _T("animation"), TYPE_INODE, 0, 0,
p_prompt, IDS_COMP_GUI_SELECTANIM,
p_accessor, &sGUIButtonAccessor,
end,
plGUIButtonComponent::kRefMouseOverAnimate, _T( "mouseOverAnimate" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_MOUSEOVERANIM,
p_default, FALSE,
p_enable_ctrls, 1, plGUIButtonComponent::kRefMouseOverAnimation,
end,
plGUIButtonComponent::kRefMouseOverAnimation, _T("mouseOverAnimation"), TYPE_INODE, 0, 0,
p_prompt, IDS_COMP_GUI_SELECTMOUSEOVERANIM,
p_accessor, &sGUIButtonAccessor,
end,
GUI_SOUND_REF( plGUIButtonComponent, Down, DOWN ),
GUI_SOUND_REF( plGUIButtonComponent, Up, UP ),
GUI_SOUND_REF( plGUIButtonComponent, Over, OVER ),
GUI_SOUND_REF( plGUIButtonComponent, Off, OFF ),
sGUIProxyParamTemplate,
plGUIButtonComponent::kRefAnimationNode, _T("animationNode"), TYPE_INODE, 0, 0,
p_accessor, &sGUIButtonAccessor,
end,
plGUIButtonComponent::kRefAnimationNodeType, _T("animationNodeType"), TYPE_INT, 0, 0,
p_default, plAnimObjInterface::kUseOwnerNode,
end,
plGUIButtonComponent::kRefMouseOverAnimationNode, _T("moAnimationNode"), TYPE_INODE, 0, 0,
p_accessor, &sGUIButtonAccessor,
end,
plGUIButtonComponent::kRefMouseOverAnimationNodeType, _T("moAnimationNodeType"), TYPE_INT, 0, 0,
p_default, plAnimObjInterface::kUseOwnerNode,
end,
plGUIButtonComponent::kRefUseDraggableChild, _T( "useDragChild" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_USEDRAGCHILD,
p_default, FALSE,
p_enable_ctrls, 1, plGUIButtonComponent::kRefDraggableChild,
end,
plGUIButtonComponent::kRefDraggableChild, _T( "dragChild" ), TYPE_INODE, 0, 0,
p_accessor, &sGUIButtonAccessor,
end,
plGUIButtonComponent::kRefNotifyType, _T("notifyType"), TYPE_INT, 0, 0,
p_default, pfGUIButtonMod::kNotifyOnUp,
end,
end
);
plGUIButtonComponent::plGUIButtonComponent()
{
fClassDesc = &gGUIButtonDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIButtonComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( fCompPB->GetInt( kRefAnimate ) )
{
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) );
if( iface != nil && iface->MightRequireSeparateMaterial() )
{
INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode )
? fCompPB->GetINode( kRefAnimationNode )
: (INode *)node;
if( restrict != nil )
{
node->SetForceMaterialCopy( true );
}
}
}
if( fCompPB->GetInt( kRefMouseOverAnimate ) )
{
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefMouseOverAnimation ) );
if( iface != nil && iface->MightRequireSeparateMaterial() )
{
INode *restrict = ( fCompPB->GetInt( kRefMouseOverAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode )
? fCompPB->GetINode( kRefMouseOverAnimationNode )
: (INode *)node;
if( restrict != nil )
{
node->SetForceMaterialCopy( true );
}
}
}
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIButtonComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUIButtonComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIButtonMod *button = (pfGUIButtonMod *)fControl;
// set the notify type
button->SetNotifyType(fCompPB->GetInt( kRefNotifyType ));
if( fCompPB->GetInt( kRefAnimate ) )
{
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) );
if( iface != nil )
{
INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode )
? fCompPB->GetINode( kRefAnimationNode )
: (INode *)node;
hsTArray<plKey> keys;
if( iface->GetKeyList( restrict, keys ) && keys.GetCount() > 0 )
button->SetAnimationKeys( keys, iface->GetIfaceSegmentName( false ) );
}
}
if( fCompPB->GetInt( kRefMouseOverAnimate ) )
{
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefMouseOverAnimation ) );
if( iface != nil )
{
INode *restrict = ( fCompPB->GetInt( kRefMouseOverAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode )
? fCompPB->GetINode( kRefMouseOverAnimationNode )
: (INode *)node;
hsTArray<plKey> keys;
if( iface->GetKeyList( restrict, keys ) && keys.GetCount() > 0 )
button->SetMouseOverAnimKeys( keys, iface->GetIfaceSegmentName( false ) );
}
}
// Do sound stuff
const char *errMsg1 = ISetSoundIndex( kRefMouseDownSound, kRefMouseDownSoundComp, pfGUIButtonMod::kMouseDown, node );
const char *errMsg2 = ISetSoundIndex( kRefMouseUpSound, kRefMouseUpSoundComp, pfGUIButtonMod::kMouseUp, node );
const char *errMsg3 = ISetSoundIndex( kRefMouseOverSound, kRefMouseOverSoundComp, pfGUIButtonMod::kMouseOver, node );
const char *errMsg4 = ISetSoundIndex( kRefMouseOffSound, kRefMouseOffSoundComp, pfGUIButtonMod::kMouseOff, node );
const char *errMsg = ( errMsg1 != nil ) ? errMsg1 : ( errMsg2 != nil ) ? errMsg2 : ( errMsg3 != nil ) ? errMsg3 : errMsg4;
if( errMsg != nil )
{
pErrMsg->Set( true, "GUI Sound Event Error", errMsg, node->GetName() ).Show();
pErrMsg->Set( false );
}
if( fCompPB->GetInt( kRefUseDraggableChild ) )
{
pfGUIDraggableMod *dragChild = pfGUIDraggableMod::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefDraggableChild ) ) );
if( dragChild != nil )
{
hsgResMgr::ResMgr()->AddViaNotify( dragChild->GetKey(),
new plGenRefMsg( button->GetKey(), plRefMsg::kOnCreate, -1, pfGUIButtonMod::kRefDraggable ), plRefFlags::kActiveRef );
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUICheckBox Component //////////////////////////////////////////////////////////////////////
//
// Defines a dialog button to be defined with the GUI manager at runtime. Belongs to exactly
// one dialog, defined by parent-child relationship, also at runtime.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUICheckBoxComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUICheckBoxCtrl; }
virtual bool ICanHaveProxy( void ) { return true; }
public:
plGUICheckBoxComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefConCmdRadio,
kRefPythonRadio,
kRefConsoleCmd,
kRefAnimate,
kRefAnimation,
kRefAnimationNode,
kRefAnimationNodeType,
kRefMouseDownSound,
kRefMouseDownSoundComp,
kRefMouseUpSound,
kRefMouseUpSoundComp,
kRefMouseOverSound,
kRefMouseOverSoundComp,
kRefMouseOffSound,
kRefMouseOffSoundComp,
};
};
class plGUICheckBoxAccessor : public PBAccessor
{
public:
void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t )
{
if( id == plGUICheckBoxComponent::kRefAnimation ||
id == plGUICheckBoxComponent::kRefMouseDownSoundComp ||
id == plGUICheckBoxComponent::kRefMouseUpSoundComp ||
id == plGUICheckBoxComponent::kRefMouseOverSoundComp ||
id == plGUICheckBoxComponent::kRefMouseOffSoundComp )
{
plGUICheckBoxComponent *comp = (plGUICheckBoxComponent *)owner;
comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED );
}
}
};
static plGUICheckBoxAccessor sGUICheckBoxAccessor;
static plGUISingleCtrlDlgProc sGUICheckSndAProc( plGUICheckBoxComponent::kRefMouseDownSoundComp, IDC_GUI_MDOWNSNDCOMP,
"Select the sound to play when the mouse clicks this button", sBtnSndClassesToSelect );
static plGUISingleCtrlDlgProc sGUICheckSndBProc( plGUICheckBoxComponent::kRefMouseUpSoundComp, IDC_GUI_MUPSNDCOMP,
"Select the sound to play when the mouse lets up on this button", sBtnSndClassesToSelect );
static plGUISingleCtrlDlgProc sGUICheckSndCProc( plGUICheckBoxComponent::kRefMouseOverSoundComp, IDC_GUI_MOVERSNDCOMP,
"Select the sound to play when the mouse moves over this button", sBtnSndClassesToSelect );
static plGUISingleCtrlDlgProc sGUICheckSndDProc( plGUICheckBoxComponent::kRefMouseOffSoundComp, IDC_GUI_MOFFSNDCOMP,
"Select the sound to play when the mouse moves off of this button", sBtnSndClassesToSelect );
static plGUISingleCtrlDlgProc *sGUICheckSubProcs[] = { &sGUICheckSndAProc, &sGUICheckSndBProc,
&sGUICheckSndCProc, &sGUICheckSndDProc, nil };
static plGUIMultipleCtrlDlgProc sGUICheckSels( sGUICheckSubProcs );
static plPlasmaAnimSelectDlgProc sGUICheckBoxProc( plGUICheckBoxComponent::kRefAnimation, IDC_GUI_COMPSELBTN,
plGUICheckBoxComponent::kRefAnimationNode, plGUICheckBoxComponent::kRefAnimationNodeType, IDC_GUI_ANIMNODESEL,
"Select the animation to play when this check box is clicked", &sGUICheckSels );
//Max desc stuff necessary below.
CLASS_DESC(plGUICheckBoxComponent, gGUICheckBoxDesc, "GUI CheckBox", "GUICheckBox", COMP_TYPE_GUI, GUI_CHECKBOX_CLASSID )
ParamBlockDesc2 gGUICheckBoxBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUICheckBox"), 0, &gGUICheckBoxDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
3,
plGUIControlBase::kRollMain, IDD_COMP_GUIBUTTON, IDS_COMP_GUICHECKBOX, 0, 0, &sGUICheckBoxProc,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
sGUIProxyParamHeader,
&sGUIControlProcParamTemplate,
plGUICheckBoxComponent::kRefAnimate, _T( "animate" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ANIMATE,
p_default, FALSE,
p_enable_ctrls, 1, plGUIButtonComponent::kRefAnimation,
end,
plGUICheckBoxComponent::kRefAnimation, _T("animation"), TYPE_INODE, 0, 0,
p_prompt, IDS_COMP_GUI_SELECTANIM,
p_accessor, &sGUICheckBoxAccessor,
end,
sGUIProxyParamTemplate,
plGUICheckBoxComponent::kRefAnimationNode, _T("animationNode"), TYPE_INODE, 0, 0,
p_accessor, &sGUIButtonAccessor,
end,
plGUICheckBoxComponent::kRefAnimationNodeType, _T("animationNodeType"), TYPE_INT, 0, 0,
p_default, plAnimObjInterface::kUseOwnerNode,
end,
GUI_SOUND_REF( plGUICheckBoxComponent, Down, DOWN ),
GUI_SOUND_REF( plGUICheckBoxComponent, Up, UP ),
GUI_SOUND_REF( plGUICheckBoxComponent, Over, OVER ),
GUI_SOUND_REF( plGUICheckBoxComponent, Off, OFF ),
end
);
plGUICheckBoxComponent::plGUICheckBoxComponent()
{
fClassDesc = &gGUICheckBoxDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUICheckBoxComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( fCompPB->GetInt( kRefAnimate ) )
{
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) );
if( iface != nil && iface->MightRequireSeparateMaterial() )
{
INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode )
? fCompPB->GetINode( kRefAnimationNode )
: (INode *)node;
if( restrict != nil )
{
node->SetForceMaterialCopy( true );
}
}
}
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUICheckBoxComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUICheckBoxComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUICheckBoxCtrl *button = (pfGUICheckBoxCtrl *)fControl;
if( fCompPB->GetInt( kRefAnimate ) )
{
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) );
if( iface != nil )
{
INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode )
? fCompPB->GetINode( kRefAnimationNode )
: (INode *)node;
hsTArray<plKey> keys;
if( iface->GetKeyList( restrict, keys ) && keys.GetCount() > 0 )
button->SetAnimationKeys( keys, iface->GetIfaceSegmentName( false ) );
}
}
// Do sound stuff
const char *errMsg1 = ISetSoundIndex( kRefMouseDownSound, kRefMouseDownSoundComp, pfGUICheckBoxCtrl::kMouseDown, node );
const char *errMsg2 = ISetSoundIndex( kRefMouseUpSound, kRefMouseUpSoundComp, pfGUICheckBoxCtrl::kMouseUp, node );
const char *errMsg3 = ISetSoundIndex( kRefMouseOverSound, kRefMouseOverSoundComp, pfGUICheckBoxCtrl::kMouseOver, node );
const char *errMsg4 = ISetSoundIndex( kRefMouseOffSound, kRefMouseOffSoundComp, pfGUICheckBoxCtrl::kMouseOff, node );
const char *errMsg = ( errMsg1 != nil ) ? errMsg1 : ( errMsg2 != nil ) ? errMsg2 : ( errMsg3 != nil ) ? errMsg3 : errMsg4;
if( errMsg != nil )
{
pErrMsg->Set( true, "GUI Sound Event Error", errMsg, node->GetName() ).Show();
pErrMsg->Set( false );
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIDraggable Component ///////////////////////////////////////////////////////////////////
//
// GUI element that can be dragged anywhere in the 2D viewing plane.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIDraggableComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIDraggableMod; }
virtual bool ICanHaveProxy( void ) { return true; }
public:
plGUIDraggableComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefReportDragging,
kRefHideCursor,
kRefAlwaysSnap
};
};
//Max desc stuff necessary below.
CLASS_DESC(plGUIDraggableComponent, gGUIDraggableDesc, "GUI Draggable", "GUIDraggable", COMP_TYPE_GUI, GUI_DRAGGABLE_CLASSID )
ParamBlockDesc2 gGUIDraggableBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIDraggable"), 0, &gGUIDraggableDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
3,
plGUIControlBase::kRollMain, IDD_COMP_GUIDRAGGABLE, IDS_COMP_GUIDRAGGABLE, 0, 0, NULL,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
sGUIProxyParamHeader,
&sGUIControlProcParamTemplate,
sGUIProxyParamTemplate,
plGUIDraggableComponent::kRefReportDragging, _T("reportWhileDragging"), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_REPORTDRAG,
p_default, FALSE,
end,
plGUIDraggableComponent::kRefHideCursor, _T("hideCursorWhileDragging"), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_HIDECURSOR,
p_default, FALSE,
end,
plGUIDraggableComponent::kRefAlwaysSnap, _T("alwaysSnapBackToStart"), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SNAPSTART,
p_default, FALSE,
end,
end
);
plGUIDraggableComponent::plGUIDraggableComponent()
{
fClassDesc = &gGUIDraggableDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIDraggableComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
node->SetForceLocal( true );
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIDraggableComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUIDraggableComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIDraggableMod *ctrl = (pfGUIDraggableMod *)fControl;
if( fCompPB->GetInt( kRefReportDragging ) )
ctrl->SetFlag( pfGUIDraggableMod::kReportDragging );
if( fCompPB->GetInt( kRefHideCursor ) )
ctrl->SetFlag( pfGUIDraggableMod::kHideCursorWhileDragging );
if( fCompPB->GetInt( kRefAlwaysSnap ) )
ctrl->SetFlag( pfGUIDraggableMod::kAlwaysSnapBackToStart );
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIKnobCtrl Component ///////////////////////////////////////////////////////////////////
//
// GUI element that can be dragged anywhere in the 2D viewing plane.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIKnobCtrlComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIKnobCtrl; }
virtual bool ICanHaveProxy( void ) { return true; }
hsBool IGrabAnimationRange( plMaxNode *node, plErrorMsg *pErrMsg, hsMatrix44 &startL2W, hsMatrix44 &endL2W );
public:
plGUIKnobCtrlComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefMinValue,
kRefMaxValue,
kRefStep,
kReverseValues,
kRefOrientation,
kRefMouseMapping,
kRefTriggerOnMouseUp,
kRefAnimation,
kRefAnimationNode,
kRefAnimationNodeType
};
};
static plPlasmaAnimSelectDlgProc sGUIKnobCtrlProc( plGUIKnobCtrlComponent::kRefAnimation, IDC_GUI_COMPSELBTN,
plGUIKnobCtrlComponent::kRefAnimationNode, plGUIKnobCtrlComponent::kRefAnimationNodeType, IDC_GUI_ANIMNODESEL,
"Select the animation to use when displaying this knob control", nil );
//Max desc stuff necessary below.
CLASS_DESC(plGUIKnobCtrlComponent, gGUIKnobCtrlDesc, "GUI Knob Control", "GUIKnobCtrl", COMP_TYPE_GUI, GUI_KNOBCTRL_CLASSID )
ParamBlockDesc2 gGUIKnobCtrlBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIKnobCtrl"), 0, &gGUIKnobCtrlDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
3,
plGUIControlBase::kRollMain, IDD_COMP_GUIKNOB, IDS_COMP_GUIKNOB, 0, 0, &sGUIKnobCtrlProc,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
sGUIProxyParamHeader,
&sGUIControlProcParamTemplate,
plGUIKnobCtrlComponent::kRefMinValue, _T("minValue"), TYPE_FLOAT, 0, 0,
p_default, 0.0f,
p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_LOWER, IDC_GUI_LOWER_SPIN, SPIN_AUTOSCALE,
end,
plGUIKnobCtrlComponent::kRefMaxValue, _T("maxValue"), TYPE_FLOAT, 0, 0,
p_default, 10.0f,
p_range, -10000.f, 10000.f, // WHY do we even need to specify this?
p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_UPPER, IDC_GUI_UPPER_SPIN, SPIN_AUTOSCALE,
end,
plGUIKnobCtrlComponent::kRefStep, _T("step"), TYPE_FLOAT, 0, 0,
p_default, 1.0f,
p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_STEP, IDC_GUI_STEP_SPIN, SPIN_AUTOSCALE,
end,
plGUIKnobCtrlComponent::kReverseValues, _T("reverseValues"), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_REVERSE,
p_default, FALSE,
end,
plGUIKnobCtrlComponent::kRefOrientation, _T("orientation"), TYPE_INT, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_RADIO, 2, IDC_ORIENTATION_RADIO, IDC_ORIENTATION_RADIO2,
p_default, 0,
end,
plGUIKnobCtrlComponent::kRefMouseMapping, _T("mouseMapping"), TYPE_INT, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_RADIO, 3, IDC_GUI_MOUSEMAPREL, IDC_GUI_MOUSEMAPANIM, IDC_GUI_MOUSEMAPSCRN,
p_default, 0,
end,
plGUIKnobCtrlComponent::kRefTriggerOnMouseUp, _T("triggerOnMouseUp"), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_TRIGGERONUP,
p_default, FALSE,
end,
sGUIProxyParamTemplate,
plGUIKnobCtrlComponent::kRefAnimation, _T("animation"), TYPE_INODE, 0, 0,
p_prompt, IDS_COMP_GUI_SELECTANIM,
end,
plGUIKnobCtrlComponent::kRefAnimationNode, _T("animationNode"), TYPE_INODE, 0, 0,
end,
plGUIKnobCtrlComponent::kRefAnimationNodeType, _T("animationNodeType"), TYPE_INT, 0, 0,
p_default, plAnimObjInterface::kUseOwnerNode,
end,
end
);
plGUIKnobCtrlComponent::plGUIKnobCtrlComponent()
{
fClassDesc = &gGUIKnobCtrlDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
hsBool plGUIKnobCtrlComponent::IGrabAnimationRange( plMaxNode *node, plErrorMsg *pErrMsg, hsMatrix44 &startL2W, hsMatrix44 &endL2W )
{
hsBool result = false;
// Get the affine parts and the TM Controller
plSceneObject *obj = node->GetSceneObject();
hsAffineParts * parts = TRACKED_NEW hsAffineParts;
plController* tmc = hsControlConverter::Instance().ConvertTMAnim(obj, node, parts);
if (tmc)
{
plMatrixControllerChannel *channel = TRACKED_NEW plMatrixControllerChannel(tmc, parts);
float length = tmc->GetLength();
startL2W = channel->Value( 0.f );
endL2W = channel->Value( length );
delete channel;
result = true;
}
delete parts; // We copy this over, so no need to keep it around
return result;
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIKnobCtrlComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
node->SetForceLocal( true );
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) );
if( iface != nil && iface->MightRequireSeparateMaterial() )
{
INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode )
? fCompPB->GetINode( kRefAnimationNode )
: (INode *)node;
if( restrict != nil )
{
node->SetForceMaterialCopy( true );
}
}
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIKnobCtrlComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
// For hackery below (see warning below)
#include "plAvatar/plAGMasterMod.h"
hsBool plGUIKnobCtrlComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIKnobCtrl *ctrl = (pfGUIKnobCtrl *)fControl;
ctrl->SetRange( fCompPB->GetFloat( kRefMinValue ), fCompPB->GetFloat( kRefMaxValue ) );
ctrl->SetStep( fCompPB->GetFloat( kRefStep ) );
if( fCompPB->GetInt( kReverseValues ) )
ctrl->SetFlag( pfGUIKnobCtrl::kReverseValues );
// Get the animation to use
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) );
if( iface != nil )
{
INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode )
? fCompPB->GetINode( kRefAnimationNode )
: (INode *)node;
hsTArray<plKey> keys;
if( iface->GetKeyList( restrict, keys ) && keys.GetCount() > 0 )
ctrl->SetAnimationKeys( keys, iface->GetIfaceSegmentName( false ) );
}
else
{
// HACKERY WARNING: Old knobs assumed the animation was just the same one applied to our node,
// so to avoid breaking old formats, if we can't grab an animObjInterface, we just grab the key
// of the master mod of our node, like we would've before
plAGMasterMod *master = node->GetAGMasterMod();
hsTArray<plKey> keys;
keys.Append( master->GetKey() );
ctrl->SetAnimationKeys( keys, ENTIRE_ANIMATION_NAME );
}
if( fCompPB->GetInt( kRefOrientation ) == 1 )
ctrl->SetFlag( pfGUIKnobCtrl::kLeftRightOrientation );
hsMatrix44 startL2W, endL2W;
switch( fCompPB->GetInt( kRefMouseMapping ) )
{
case 0: // Default, normal (old) relative behavior
break;
case 1: // Map to the range of animation positions
if( !IGrabAnimationRange( node, pErrMsg, startL2W, endL2W ) )
{
pErrMsg->Set( true, "Unable to grab animation range for the GUI Knob Control %s. The Map-To-Screen-Range feature will be disabled.", node->GetName() ).Show();
pErrMsg->Set( false );
}
else
{
hsPoint3 startPos = startL2W.GetTranslate();
hsPoint3 endPos = endL2W.GetTranslate();
ctrl->SetScreenRange( startPos, endPos );
ctrl->SetFlag( pfGUIKnobCtrl::kMapToAnimationRange );
}
break;
case 2: // Map to a range on the screen
ctrl->SetFlag( pfGUIKnobCtrl::kMapToScreenRange );
break;
}
if( fCompPB->GetInt( kRefTriggerOnMouseUp ) )
ctrl->SetFlag( pfGUIKnobCtrl::kTriggerOnlyOnMouseUp );
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIListBox Component /////////////////////////////////////////////////////////////////////
//
// GUI element that can be dragged anywhere in the 2D viewing plane.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIListBoxComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIListBoxMod; }
virtual bool INeedsDynamicText( void ) { return true; }
public:
plGUIListBoxComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefUseScroll,
kRefScrollCtrl,
kRefSingleSelect,
kRefXparentBgnd,
kRefDragDropSource,
kRefDisableKeys,
kRefAllow2DElementGrid,
kRefScrollLeftToRight,
kRefScaleWithRes,
kRefPassClicksThrough,
kRefEnableTreeBehavior,
kRefSkin,
kRefHandsOffMultiSelect
};
};
// When one of our parameters that is a ref changes, send out the component ref
// changed message. Normally, messages from component refs are ignored since
// they pass along all the messages of the ref, which generates a lot of false
// converts.
class plGUIListBoxAccessor : public PBAccessor
{
public:
void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t )
{
if( id == plGUIListBoxComponent::kRefScrollCtrl )
{
plGUIListBoxComponent *comp = (plGUIListBoxComponent *)owner;
comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED );
}
}
};
Class_ID sScrollingClassesToSelect[] = { GUI_UPDOWNPAIR_CLASSID, GUI_KNOBCTRL_CLASSID, plGUISingleCtrlDlgProc::kEndClassList };
static plGUIListBoxAccessor sGUIListBoxAccessor;
static plGUISingleCtrlDlgProc sGUIListBoxProc( plGUIListBoxComponent::kRefScrollCtrl, IDC_GUI_COMPSELBTN,
"Select the control to use for scrolling this list box", sScrollingClassesToSelect );
static plGUISingleCtrlDlgProc sGUILBSkinSelectProc( plGUIListBoxComponent::kRefSkin, IDC_GUI_SKIN,
"Select the skin to use for this list box", sSkinClassesToSelect,
&sGUIListBoxProc );
//Max desc stuff necessary below.
CLASS_DESC(plGUIListBoxComponent, gGUIListBoxDesc, "GUI List Box", "GUIListBox", COMP_TYPE_GUI, GUI_LISTBOX_CLASSID )
ParamBlockDesc2 gGUIListBoxBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIListBox"), 0, &gGUIListBoxDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
2,
plGUIControlBase::kRollMain, IDD_COMP_GUILISTBOX, IDS_COMP_GUILISTBOX, 0, 0, &sGUILBSkinSelectProc,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
&sGUIControlProcParamTemplate,
plGUIListBoxComponent::kRefUseScroll, _T( "enableScrolling" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCROLLCTRL,
p_default, FALSE,
p_enable_ctrls, 1, plGUIListBoxComponent::kRefScrollCtrl,
end,
plGUIListBoxComponent::kRefScrollCtrl, _T("scrollControl"), TYPE_INODE, 0, 0,
p_prompt, IDS_COMP_GUI_SELECTSCROLL,
p_accessor, &sGUIListBoxAccessor,
end,
plGUIListBoxComponent::kRefSingleSelect, _T( "singleSelect" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SINGLESEL,
p_default, FALSE,
end,
plGUIListBoxComponent::kRefXparentBgnd, _T( "xparentBgnd" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_XPARENT,
p_default, FALSE,
end,
plGUIListBoxComponent::kRefDragDropSource, _T( "dragDropCapable" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_DRAGDROPSRC,
p_default, FALSE,
end,
plGUIListBoxComponent::kRefDisableKeys, _T( "disableKeys" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_DISABLEKEYS,
p_default, FALSE,
end,
plGUIListBoxComponent::kRefAllow2DElementGrid, _T( "allow2DElementGrid" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ALLOWMULTIROW,
p_default, FALSE,
end,
plGUIListBoxComponent::kRefScrollLeftToRight, _T( "scrollLeftToRight" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCROLLL2R,
p_default, FALSE,
end,
plGUIListBoxComponent::kRefScaleWithRes, _T( "scaleWithRes" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCALERES,
p_default, FALSE,
end,
plGUIListBoxComponent::kRefPassClicksThrough, _T( "passClicksThru" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_PASSTHRU,
p_default, FALSE,
end,
plGUIListBoxComponent::kRefEnableTreeBehavior, _T( "makeLikeATree" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ENABLETREE,
p_default, FALSE,
end,
plGUIListBoxComponent::kRefSkin, _T("skin"), TYPE_INODE, 0, 0,
end,
plGUIListBoxComponent::kRefHandsOffMultiSelect, _T( "handsOffMultiSelect" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_HANDSOFF,
p_default, FALSE,
end,
end
);
plGUIListBoxComponent::plGUIListBoxComponent()
{
fClassDesc = &gGUIListBoxDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIListBoxComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIListBoxComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUIListBoxComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIListBoxMod *ctrl = (pfGUIListBoxMod *)fControl;
if( fCompPB->GetInt( kRefUseScroll ) )
{
// Get the scrolling control to use
pfGUIValueCtrl *scroll = pfGUIValueCtrl::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefScrollCtrl ) ) );
if( scroll != nil )
{
hsgResMgr::ResMgr()->AddViaNotify( scroll->GetKey(), TRACKED_NEW plGenRefMsg( ctrl->GetKey(),
plRefMsg::kOnCreate, -1, pfGUIListBoxMod::kRefScrollCtrl ), plRefFlags::kActiveRef );
}
}
if( fCompPB->GetInt( kRefSingleSelect ) )
ctrl->SetSingleSelect( true );
if( fCompPB->GetInt( kRefXparentBgnd ) )
ctrl->SetFlag( pfGUIListBoxMod::kXparentBgnd );
if( fCompPB->GetInt( kRefDragDropSource ) )
ctrl->SetFlag( pfGUIListBoxMod::kDragAndDropCapable );
if( fCompPB->GetInt( kRefDisableKeys ) )
ctrl->SetFlag( pfGUIListBoxMod::kDisableKeyActions );
if( fCompPB->GetInt( kRefAllow2DElementGrid ) )
ctrl->SetFlag( pfGUIListBoxMod::kAllowMultipleElementsPerRow );
if( fCompPB->GetInt( kRefScrollLeftToRight ) )
ctrl->SetFlag( pfGUIListBoxMod::kScrollLeftToRight );
if( fCompPB->GetInt( kRefScaleWithRes ) )
ctrl->SetFlag( pfGUIControlMod::kScaleTextWithResolution );
if( fCompPB->GetInt( kRefPassClicksThrough ) )
ctrl->SetFlag( pfGUIListBoxMod::kAllowMousePassThrough );
if( fCompPB->GetInt( kRefEnableTreeBehavior ) )
ctrl->SetFlag( pfGUIListBoxMod::kGrowLeavesAndProcessOxygen );
if( fCompPB->GetInt( kRefHandsOffMultiSelect ) )
ctrl->SetFlag( pfGUIListBoxMod::kHandsOffMultiSelect );
INode *sNode = fCompPB->GetINode( kRefSkin );
if( sNode != nil )
{
plComponentBase *comp = ( (plMaxNode *)sNode )->ConvertToComponent();
if( comp != nil )
{
Class_ID nodeID = comp->ClassID();
hsAssert( nodeID == GUI_SKIN_CLASSID, "Bad node param in GUIMenu::Convert()" );
plGUISkinComp *skin = (plGUISkinComp *)comp;
hsgResMgr::ResMgr()->AddViaNotify( skin->GetConvertedSkin()->GetKey(), TRACKED_NEW plGenRefMsg( ctrl->GetKey(), plRefMsg::kOnCreate, -1, pfGUIControlMod::kRefSkin ), plRefFlags::kActiveRef );
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUITextBox Component /////////////////////////////////////////////////////////////////////
//
// GUI element that displays a block of wrapped text.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUITextBoxComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUITextBoxMod; }
virtual bool INeedsDynamicText( void ) { return true; }
public:
plGUITextBoxComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefInitText,
kRefFontSize,
kRefXparentBgnd,
kRefJustify,
kRefScaleWithRes,
kRefUseLocalization,
kRefLocalizationPath
};
};
class plGUITextBoxProc : public ParamMap2UserDlgProc
{
private:
std::vector<std::string> fTranslations;
int fCurLanguage;
void ISetTranslation(int lang, std::string text)
{
while (lang >= fTranslations.size())
fTranslations.push_back("");
fTranslations[lang] = text;
}
protected:
public:
void DeleteThis() {}
BOOL DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
int i;
switch( msg )
{
case WM_INITDIALOG:
// make sure there is a string to get
if ( pmap->GetParamBlock()->GetStr( plGUITextBoxComponent::kRefInitText ) )
{
fTranslations = plLocalization::StringToLocal(pmap->GetParamBlock()->GetStr( plGUITextBoxComponent::kRefInitText ) );
SetDlgItemText( hWnd, IDC_GUI_INITTEXT, fTranslations[0].c_str() );
}
else
// if there is no text, then there is nothing to translate
SetDlgItemText( hWnd, IDC_GUI_INITTEXT, pmap->GetParamBlock()->GetStr( plGUITextBoxComponent::kRefInitText ) );
SendMessage( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), CB_RESETCONTENT, 0, 0 );
for (i=0; i<plLocalization::kNumLanguages; i++)
SendMessage( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), CB_ADDSTRING, 0, (LPARAM)plLocalization::GetLanguageName((plLocalization::Language)i) );
SendMessage( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), CB_SETCURSEL, 0, 0 );
fCurLanguage = 0;
SetDlgItemText( hWnd, IDC_GUI_LOCALIZATION_PATH, pmap->GetParamBlock()->GetStr( plGUITextBoxComponent::kRefLocalizationPath ) );
if ( pmap->GetParamBlock()->GetInt( plGUITextBoxComponent::kRefUseLocalization ) != 0 )
{
// disable standard text, enable loc path
EnableWindow( GetDlgItem( hWnd, IDC_GUI_INITTEXT ), false );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), false );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_LOCALIZATION_PATH ), true );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_SELECT_LOC_PATH ), true );
CheckDlgButton( hWnd, IDC_GUI_USE_LOCALIZATION, BST_CHECKED );
}
else
{
// enable standard text, disable loc path
EnableWindow( GetDlgItem( hWnd, IDC_GUI_INITTEXT ), true );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), true );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_LOCALIZATION_PATH ), false );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_SELECT_LOC_PATH ), false );
CheckDlgButton( hWnd, IDC_GUI_USE_LOCALIZATION, BST_UNCHECKED );
}
return true;
case WM_DESTROY:
break;
case WM_COMMAND:
if( LOWORD( wParam ) == IDC_GUI_INITTEXT )
{
if( HIWORD( wParam ) == EN_CHANGE )
{
int strLen = SendDlgItemMessage( hWnd, IDC_GUI_INITTEXT, WM_GETTEXTLENGTH, 0, 0 );
if( strLen > 0 )
{
char *str = TRACKED_NEW char[ strLen + 1 ];
GetDlgItemText( hWnd, IDC_GUI_INITTEXT, str, strLen + 1 );
str[ strLen ] = 0;
ISetTranslation(fCurLanguage,str);
delete [] str;
std::string translation = plLocalization::LocalToString(fTranslations);
str = TRACKED_NEW char[ translation.length() + 1 ];
strcpy(str,translation.c_str());
str[translation.length()] = 0;
pmap->GetParamBlock()->SetValue( plGUITextBoxComponent::kRefInitText, 0, str );
delete [] str;
}
}
else if( HIWORD( wParam ) == EN_KILLFOCUS )
{
plMaxAccelerators::Enable();
}
else if( HIWORD( wParam ) == EN_SETFOCUS )
{
plMaxAccelerators::Disable();
}
}
else if( LOWORD( wParam ) == IDC_GUI_LOCALIZATION_PATH )
{
if( HIWORD( wParam ) == EN_CHANGE )
{
int strLen = SendDlgItemMessage( hWnd, IDC_GUI_LOCALIZATION_PATH, WM_GETTEXTLENGTH, 0, 0 );
if( strLen > 0 )
{
char *str = TRACKED_NEW char[ strLen + 1 ];
GetDlgItemText( hWnd, IDC_GUI_LOCALIZATION_PATH, str, strLen + 1 );
str[ strLen ] = 0;
pmap->GetParamBlock()->SetValue( plGUITextBoxComponent::kRefLocalizationPath, 0, str );
delete [] str;
}
}
else if( HIWORD( wParam ) == EN_KILLFOCUS )
{
plMaxAccelerators::Enable();
}
else if( HIWORD( wParam ) == EN_SETFOCUS )
{
plMaxAccelerators::Disable();
}
}
else if( LOWORD( wParam ) == IDC_GUI_LANGUAGE )
{
if( HIWORD( wParam ) == CBN_SELCHANGE )
{
int idx = SendDlgItemMessage( hWnd, IDC_GUI_LANGUAGE, CB_GETCURSEL, 0, 0 );
if (idx >= fTranslations.size())
SetDlgItemText( hWnd, IDC_GUI_INITTEXT, "" );
else
SetDlgItemText( hWnd, IDC_GUI_INITTEXT, fTranslations[idx].c_str() );
fCurLanguage = idx;
}
}
else if( LOWORD( wParam ) == IDC_GUI_SELECT_LOC_PATH )
{
char value[512];
GetDlgItemText( hWnd, IDC_GUI_LOCALIZATION_PATH, value, 512 );
plPickLocalizationDlg dlg( value );
if( dlg.DoPick() )
{
pmap->GetParamBlock()->SetValue( plGUITextBoxComponent::kRefLocalizationPath, 0, (char*)dlg.GetValue() );
SetDlgItemText( hWnd, IDC_GUI_LOCALIZATION_PATH, (char*)dlg.GetValue() );
}
}
else if( LOWORD( wParam ) == IDC_GUI_USE_LOCALIZATION )
{
// enable/disable the appropriate values
bool useLoc = ( IsDlgButtonChecked( hWnd, IDC_GUI_USE_LOCALIZATION ) == BST_CHECKED );
pmap->GetParamBlock()->SetValue( plGUITextBoxComponent::kRefUseLocalization, 0, useLoc ? 1 : 0 );
if ( useLoc )
{
// disable standard text, enable loc path
EnableWindow( GetDlgItem( hWnd, IDC_GUI_INITTEXT ), false );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), false );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_LOCALIZATION_PATH ), true );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_SELECT_LOC_PATH ), true );
}
else
{
// enable standard text, disable loc path
EnableWindow( GetDlgItem( hWnd, IDC_GUI_INITTEXT ), true );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), true );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_LOCALIZATION_PATH ), false );
EnableWindow( GetDlgItem( hWnd, IDC_GUI_SELECT_LOC_PATH ), false );
}
}
break;
}
return false;
}
};
static plGUITextBoxProc gGUITextBoxProc;
//Max desc stuff necessary below.
CLASS_DESC(plGUITextBoxComponent, gGUITextBoxDesc, "GUI Text Box", "GUITextBox", COMP_TYPE_GUI, GUI_TEXTBOX_CLASSID )
ParamBlockDesc2 gGUITextBoxBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUITextBox"), 0, &gGUITextBoxDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
2,
plGUIControlBase::kRollMain, IDD_COMP_GUITEXTBOX, IDS_COMP_GUITEXTBOX, 0, 0, &gGUITextBoxProc,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
&sGUIControlProcParamTemplate,
plGUITextBoxComponent::kRefInitText, _T("InitText"), TYPE_STRING, 0, 0,
// p_ui, plGUIControlBase::kRollMain, TYPE_EDITBOX, IDC_GUI_INITTEXT,
end,
plGUITextBoxComponent::kRefXparentBgnd, _T( "xparentBgnd" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_XPARENT,
p_default, FALSE,
end,
plGUITextBoxComponent::kRefJustify, _T("justify"), TYPE_INT, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_RADIO, 3, IDC_JUSTIFYRADIO, IDC_JUSTRADIO2, IDC_JUSTRADIO3,
p_default, 0,
end,
plGUITextBoxComponent::kRefScaleWithRes, _T( "scaleWithRes" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCALERES,
p_default, FALSE,
end,
plGUITextBoxComponent::kRefUseLocalization, _T( "useLocalization" ), TYPE_BOOL, 0, 0,
p_default, FALSE,
end,
plGUITextBoxComponent::kRefLocalizationPath,_T( "localizationPath" ),TYPE_STRING, 0, 0,
end,
end
);
plGUITextBoxComponent::plGUITextBoxComponent()
{
fClassDesc = &gGUITextBoxDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUITextBoxComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUITextBoxComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUITextBoxComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUITextBoxMod *ctrl = (pfGUITextBoxMod *)fControl;
ctrl->SetText( fCompPB->GetStr( kRefInitText ) );
if( fCompPB->GetInt( kRefXparentBgnd ) )
ctrl->SetFlag( pfGUITextBoxMod::kXparentBgnd );
int just = fCompPB->GetInt( kRefJustify );
if( just == 1 )
ctrl->SetFlag( pfGUITextBoxMod::kCenterJustify );
else if( just == 2 )
ctrl->SetFlag( pfGUITextBoxMod::kRightJustify );
if( fCompPB->GetInt( kRefScaleWithRes ) )
ctrl->SetFlag( pfGUIControlMod::kScaleTextWithResolution );
ctrl->SetUseLocalizationPath( fCompPB->GetInt( kRefUseLocalization ) != 0 );
ctrl->SetLocalizationPath( fCompPB->GetStr( kRefLocalizationPath ) );
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIEditBox Component /////////////////////////////////////////////////////////////////////
//
// GUI element that can be dragged anywhere in the 2D viewing plane.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIEditBoxComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIEditBoxMod; }
virtual bool INeedsDynamicText( void ) { return true; }
public:
plGUIEditBoxComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefXparentBgnd,
kRefScaleWithRes
};
};
//Max desc stuff necessary below.
CLASS_DESC(plGUIEditBoxComponent, gGUIEditBoxDesc, "GUI Edit Box", "GUIEditBox", COMP_TYPE_GUI, GUI_EDITBOX_CLASSID )
ParamBlockDesc2 gGUIEditBoxBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIEditBox"), 0, &gGUIEditBoxDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
2,
plGUIControlBase::kRollMain, IDD_COMP_GUIEDITBOX, IDS_COMP_GUIEDITBOX, 0, 0, NULL,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
&sGUIControlProcParamTemplate,
plGUIEditBoxComponent::kRefXparentBgnd, _T( "xparentBgnd" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_XPARENT,
p_default, FALSE,
end,
plGUIEditBoxComponent::kRefScaleWithRes, _T( "scaleWithRes" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCALERES,
p_default, FALSE,
end,
end
);
plGUIEditBoxComponent::plGUIEditBoxComponent()
{
fClassDesc = &gGUIEditBoxDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIEditBoxComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIEditBoxComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUIEditBoxComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIEditBoxMod *ctrl = (pfGUIEditBoxMod *)fControl;
if( fCompPB->GetInt( kRefXparentBgnd ) )
ctrl->SetFlag( pfGUIEditBoxMod::kXparentBgnd );
if( fCompPB->GetInt( kRefScaleWithRes ) )
ctrl->SetFlag( pfGUIControlMod::kScaleTextWithResolution );
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIUpDownPair Component //////////////////////////////////////////////////////////////////
//
// GUI grouping element that uses two buttons to alter a value up and down
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIUpDownPairComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIUpDownPairMod; }
public:
plGUIUpDownPairComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefMinValue,
kRefMaxValue,
kRefStep,
kRefUpControl,
kRefDownControl
};
};
//// Dialog proc ////////////////////////////////////////////////////////////////////////////////
class plGUIUDPairDlgProc : public ParamMap2UserDlgProc
{
protected:
ParamID fUpNodeID, fDownNodeID;
int fUpDlgItem, fDownDlgItem;
TCHAR fTitle[ 128 ];
public:
plGUIUDPairDlgProc( ParamID upNodeID, int upDlgItem, ParamID downNodeID, int downDlgItem, TCHAR *title )
{
fUpNodeID = upNodeID;
fDownNodeID = downNodeID;
fUpDlgItem = upDlgItem;
fDownDlgItem = downDlgItem;
strcpy( fTitle, title );
}
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch ( msg )
{
case WM_INITDIALOG:
{
IParamBlock2 *pb = map->GetParamBlock();
INode *node = pb->GetINode( fUpNodeID );
TSTR newName( node ? node->GetName() : "Pick" );
::SetWindowText( ::GetDlgItem( hWnd, fUpDlgItem ), newName );
node = pb->GetINode( fDownNodeID );
TSTR newName2( node ? node->GetName() : "Pick" );
::SetWindowText( ::GetDlgItem( hWnd, fDownDlgItem ), newName2 );
}
return true;
case WM_COMMAND:
if( ( HIWORD( wParam ) == BN_CLICKED ) )
{
if( LOWORD( wParam ) == fUpDlgItem )
{
IParamBlock2 *pb = map->GetParamBlock();
plGUICtrlHitCallback hitCB( (INode *)pb->GetOwner(), pb, fUpNodeID, fTitle, true, GUI_BUTTON_CLASSID );
GetCOREInterface()->DoHitByNameDialog( &hitCB );
INode* node = pb->GetINode( fUpNodeID );
TSTR newName( node ? node->GetName() : "Pick" );
::SetWindowText( ::GetDlgItem(hWnd, fUpDlgItem ), newName );
map->Invalidate( fUpNodeID );
::InvalidateRect( hWnd, NULL, TRUE );
}
else if( LOWORD( wParam ) == fDownDlgItem )
{
IParamBlock2 *pb = map->GetParamBlock();
plGUICtrlHitCallback hitCB( (INode *)pb->GetOwner(), pb, fDownNodeID, fTitle, true, GUI_BUTTON_CLASSID );
GetCOREInterface()->DoHitByNameDialog( &hitCB );
INode* node = pb->GetINode( fDownNodeID );
TSTR newName( node ? node->GetName() : "Pick" );
::SetWindowText( ::GetDlgItem(hWnd, fDownDlgItem ), newName );
map->Invalidate( fDownDlgItem );
::InvalidateRect( hWnd, NULL, TRUE );
}
}
return true;
}
return false;
}
void DeleteThis() {}
};
// When one of our parameters that is a ref changes, send out the component ref
// changed message. Normally, messages from component refs are ignored since
// they pass along all the messages of the ref, which generates a lot of false
// converts.
class plGUIUDAccessor : public PBAccessor
{
public:
void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t )
{
if( id == plGUIUpDownPairComponent::kRefUpControl
|| id == plGUIUpDownPairComponent::kRefDownControl )
{
plGUIUpDownPairComponent *comp = (plGUIUpDownPairComponent *)owner;
comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED );
}
}
};
static plGUIUDAccessor sGUIUDAccessor;
static plGUIUDPairDlgProc sGUIUDPairDlgProc( plGUIUpDownPairComponent::kRefUpControl, IDC_GUI_COMPSELBTN,
plGUIUpDownPairComponent::kRefDownControl, IDC_GUI_COMPSELBTN2,
"Select the control to use in this pair" );
//Max desc stuff necessary below.
CLASS_DESC(plGUIUpDownPairComponent, gGUIUDPairDesc, "GUI Up/Down Pair", "GUIUDPair", COMP_TYPE_GUI, GUI_UPDOWNPAIR_CLASSID )
ParamBlockDesc2 gGUIUDPairBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIUDPair"), 0, &gGUIUDPairDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
2,
plGUIControlBase::kRollMain, IDD_COMP_GUIUDSCROLL, IDS_COMP_GUIUDSCROLL, 0, 0, &sGUIUDPairDlgProc,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
&sGUIControlProcParamTemplate,
plGUIUpDownPairComponent::kRefUpControl, _T("upControl"), TYPE_INODE, 0, 0,
p_prompt, IDS_COMP_GUI_SELECTUDCTRL,
p_accessor, &sGUIUDAccessor,
end,
plGUIUpDownPairComponent::kRefDownControl, _T("downControl"), TYPE_INODE, 0, 0,
p_prompt, IDS_COMP_GUI_SELECTUDCTRL,
p_accessor, &sGUIUDAccessor,
end,
plGUIUpDownPairComponent::kRefMinValue, _T("minValue"), TYPE_FLOAT, 0, 0,
p_default, 0.0f,
p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_LOWER, IDC_GUI_LOWER_SPIN, SPIN_AUTOSCALE,
end,
plGUIUpDownPairComponent::kRefMaxValue, _T("maxValue"), TYPE_FLOAT, 0, 0,
p_default, 10.0f,
p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_UPPER, IDC_GUI_UPPER_SPIN, SPIN_AUTOSCALE,
end,
plGUIUpDownPairComponent::kRefStep, _T("step"), TYPE_FLOAT, 0, 0,
p_default, 1.0f,
p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_STEP, IDC_GUI_STEP_SPIN, SPIN_AUTOSCALE,
end,
end
);
plGUIUpDownPairComponent::plGUIUpDownPairComponent()
{
fClassDesc = &gGUIUDPairDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIUpDownPairComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIUpDownPairComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUIUpDownPairComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIUpDownPairMod *ctrl = (pfGUIUpDownPairMod *)fControl;
// Get the child controls
pfGUIButtonMod *up = pfGUIButtonMod::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefUpControl ) ) );
pfGUIButtonMod *down = pfGUIButtonMod::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefDownControl ) ) );
ctrl->SetControls( up, down );
ctrl->SetRange( fCompPB->GetFloat( kRefMinValue ), fCompPB->GetFloat( kRefMaxValue ) );
ctrl->SetStep( fCompPB->GetFloat( kRefStep ) );
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIDragBar Component ///////////////////////////////////////////////////////////////////
//
// GUI element that can be dragged anywhere in the 2D viewing plane.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIDragBarComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIDragBarCtrl; }
virtual bool ICanHaveProxy( void ) { return true; }
public:
plGUIDragBarComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
};
};
//Max desc stuff necessary below.
CLASS_DESC(plGUIDragBarComponent, gGUIDragBarDesc, "GUI Dialog Drag Bar", "GUIDragBar", COMP_TYPE_GUI, GUI_DRAGBAR_CLASSID )
ParamBlockDesc2 gGUIDragBarBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIDragBar"), 0, &gGUIDragBarDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
3,
plGUIControlBase::kRollMain, IDD_COMP_GUIDRAGBAR, IDS_COMP_GUIDRAGBAR, 0, 0, NULL,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
sGUIProxyParamHeader,
&sGUIControlProcParamTemplate,
sGUIProxyParamTemplate,
end
);
plGUIDragBarComponent::plGUIDragBarComponent()
{
fClassDesc = &gGUIDragBarDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIDragBarComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
node->SetForceLocal( true );
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIDragBarComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUIDragBarComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIDragBarCtrl *ctrl = (pfGUIDragBarCtrl *)fControl;
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIRadioGroup Component //////////////////////////////////////////////////////////////////
//
// GUI grouping element that ensures that only one of a group of check boxes is checked at any
// one time, and takes on the value of whichever one is currently checked, or -1 if none.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIRadioGroupAccessor;
class plGUIRadioGroupComponent : public plGUIControlBase
{
friend class plGUIRadioGroupAccessor;
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIRadioGroupCtrl; }
public:
plGUIRadioGroupComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefCheckBoxes,
kRefDefaultSel,
kRefAllowNoSel
};
};
//// Dialog proc ////////////////////////////////////////////////////////////////////////////////
class plGUIRadioGroupProc : public ParamMap2UserDlgProc
{
protected:
public:
plGUIRadioGroupProc()
{
}
void SetSpinnerRange( IParamMap2 *pMap )
{
if( pMap == nil )
return;
HWND hWnd = pMap->GetHWnd();
if( hWnd == nil )
return;
ISpinnerControl *spin = GetISpinner( GetDlgItem( hWnd, IDC_GUI_DEFSEL_SPIN ) );
int minValue = pMap->GetParamBlock()->GetInt( plGUIRadioGroupComponent::kRefAllowNoSel ) ? -1 : 0;
int maxValue = pMap->GetParamBlock()->Count( plGUIRadioGroupComponent::kRefCheckBoxes );
spin->SetLimits( minValue, maxValue );
ReleaseISpinner( spin );
}
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch ( msg )
{
case WM_INITDIALOG:
{
SetSpinnerRange( map );
}
return true;
case WM_COMMAND:
if( ( HIWORD( wParam ) == BN_CLICKED ) )
{
if( LOWORD( wParam ) == IDC_GUI_ADDCHECK )
{
IParamBlock2 *pb = map->GetParamBlock();
plGUICtrlHitCallback hitCB( (INode *)pb->GetOwner(), pb, plGUIRadioGroupComponent::kRefCheckBoxes,
"Select a check box to add to this radio group", true, GUI_CHECKBOX_CLASSID, false );
GetCOREInterface()->DoHitByNameDialog( &hitCB );
map->Invalidate( plGUIRadioGroupComponent::kRefCheckBoxes );
}
}
return true;
}
return false;
}
void DeleteThis() {}
};
static plGUIRadioGroupProc sGUIRadioGroupProc;
//Max desc stuff necessary below.
CLASS_DESC(plGUIRadioGroupComponent, gGUIRadioGroupDesc, "GUI Radio Group", "GUIRadioGroup", COMP_TYPE_GUI, GUI_RADIOGROUP_CLASSID )
// When one of our parameters that is a ref changes, send out the component ref
// changed message. Normally, messages from component refs are ignored since
// they pass along all the messages of the ref, which generates a lot of false
// converts.
class plGUIRadioGroupAccessor : public PBAccessor
{
public:
void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t )
{
plGUIRadioGroupComponent *comp = (plGUIRadioGroupComponent *)owner;
IParamBlock2 *pBlock = comp->fCompPB;
if( id == plGUIRadioGroupComponent::kRefCheckBoxes )
{
comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED );
sGUIRadioGroupProc.SetSpinnerRange( pBlock->GetMap( plGUIControlBase::kRollMain ) );
}
else if( id == plGUIRadioGroupComponent::kRefAllowNoSel )
sGUIRadioGroupProc.SetSpinnerRange( pBlock->GetMap( plGUIControlBase::kRollMain ) );
}
void TabChanged( tab_changes changeCode, Tab<PB2Value> *tab, ReferenceMaker *owner, ParamID id, int tabIndex, int count )
{
plGUIRadioGroupComponent *comp = (plGUIRadioGroupComponent *)owner;
IParamBlock2 *pBlock = comp->fCompPB;
if( id == plGUIRadioGroupComponent::kRefCheckBoxes )
{
comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED );
sGUIRadioGroupProc.SetSpinnerRange( pBlock->GetMap( plGUIControlBase::kRollMain ) );
}
}
};
static plGUIRadioGroupAccessor sGUIRadioGroupAccessor;
ParamBlockDesc2 gGUIRadioGroupBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIRadioGroup"), 0, &gGUIRadioGroupDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
2,
plGUIControlBase::kRollMain, IDD_COMP_GUIRADIOGROUP, IDS_COMP_GUIRADIOGROUP, 0, 0, &sGUIRadioGroupProc,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
&sGUIControlProcParamTemplate,
plGUIRadioGroupComponent::kRefCheckBoxes, _T("checkBoxes"), TYPE_INODE_TAB, 0, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_NODELISTBOX, IDC_GUI_CHECKLIST, 0, 0, IDC_GUI_DELCHECK,
p_accessor, &sGUIRadioGroupAccessor,
end,
plGUIRadioGroupComponent::kRefDefaultSel, _T("defaultSelection"), TYPE_INT, 0, 0,
p_default, 0,
p_range, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_INT, IDC_GUI_DEFSEL, IDC_GUI_DEFSEL_SPIN, SPIN_AUTOSCALE,
end,
plGUIRadioGroupComponent::kRefAllowNoSel, _T( "allowNoSel" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ALLOWNONE,
p_default, FALSE,
p_accessor, &sGUIRadioGroupAccessor,
end,
end
);
plGUIRadioGroupComponent::plGUIRadioGroupComponent()
{
fClassDesc = &gGUIRadioGroupDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIRadioGroupComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIRadioGroupComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUIRadioGroupComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIRadioGroupCtrl *ctrl = (pfGUIRadioGroupCtrl *)fControl;
int i;
ctrl->ClearControlList();
for( i = 0; i < fCompPB->Count( kRefCheckBoxes ); i++ )
{
pfGUICheckBoxCtrl *cb = pfGUICheckBoxCtrl::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefCheckBoxes, 0, i ) ) );
if( cb != nil )
ctrl->AddControl( cb );
}
if( fCompPB->GetInt( kRefAllowNoSel ) )
ctrl->SetFlag( pfGUIRadioGroupCtrl::kAllowNoSelection );
ctrl->SetDefaultValue( fCompPB->GetInt( kRefDefaultSel ) );
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIDynDisplay Component //////////////////////////////////////////////////////////////////
//
// GUI element that can be dragged anywhere in the 2D viewing plane.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIDynDisplayComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIDynDisplayCtrl; }
virtual bool IHasProcRollout( void ) { return false; }
public:
plGUIDynDisplayComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefDynLayer
};
};
//// Dialog proc ////////////////////////////////////////////////////////////////////////////////
class plGUIDynDisplayProc : public ParamMap2UserDlgProc
{
protected:
public:
plGUIDynDisplayProc()
{
}
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch ( msg )
{
case WM_INITDIALOG:
{
IParamBlock2 *pb = map->GetParamBlock();
Texmap *tmap = pb->GetTexmap( plGUIDynDisplayComponent::kRefDynLayer );
if( tmap != nil )
SetDlgItemText( hWnd, IDC_GUI_PICKMAT, (const char *)tmap->GetName() );
else
SetDlgItemText( hWnd, IDC_GUI_PICKMAT, "Pick" );
}
return true;
case WM_COMMAND:
if( ( HIWORD( wParam ) == BN_CLICKED ) )
{
if( LOWORD( wParam ) == IDC_GUI_PICKMAT )
{
IParamBlock2 *pb = map->GetParamBlock();
if( plPickMaterialMap::PickTexmap( pb, plGUIDynDisplayComponent::kRefDynLayer ) )
{
Texmap *tmap = pb->GetTexmap( plGUIDynDisplayComponent::kRefDynLayer );
if( tmap != nil )
SetDlgItemText( hWnd, IDC_GUI_PICKMAT, (const char *)tmap->GetName() );
else
SetDlgItemText( hWnd, IDC_GUI_PICKMAT, "Pick" );
map->Invalidate( plGUIDynDisplayComponent::kRefDynLayer );
}
}
}
return true;
}
return false;
}
void DeleteThis() {}
};
static plGUIDynDisplayProc sGUIDynDisplayProc;
//Max desc stuff necessary below.
CLASS_DESC(plGUIDynDisplayComponent, gGUIDynDisplayDesc, "GUI Dynamic Display", "GUIDynDisplay", COMP_TYPE_GUI, GUI_DYNDISPLAY_CLASSID )
ParamBlockDesc2 gGUIDynDisplayBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIDynDisplay"), 0, &gGUIDynDisplayDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp,
1,
plGUIControlBase::kRollMain, IDD_COMP_GUIDYNDISPLAY, IDS_COMP_GUIDYNDISPLAY, 0, 0, &sGUIDynDisplayProc,
plGUIDynDisplayComponent::kRefDynLayer, _T("dynLayer"), TYPE_TEXMAP, 0, 0,
// p_ui, plGUIControlBase::kRollMain, TYPE_TEXMAPBUTTON, IDC_GUI_COMPSELBTN,
end,
end
);
plGUIDynDisplayComponent::plGUIDynDisplayComponent()
{
fClassDesc = &gGUIDynDisplayDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIDynDisplayComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIDynDisplayComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUIDynDisplayComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIDynDisplayCtrl *ctrl = (pfGUIDynDisplayCtrl *)fControl;
Texmap *tmap = fCompPB->GetTexmap( plGUIDynDisplayComponent::kRefDynLayer );
plPlasmaMAXLayer *pLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer( tmap );
if( pLayer == nil /*|| pLayer->ClassID() != DYN_TEXT_LAYER_CLASS_ID */ )
{
pErrMsg->Set(true, "GUI Control Component Error", "The texmap selected for the Dynamic Display Control on object \"%s\" is not a Plasma Dynamic Text Layer. Please fix.", node->GetName() ).Show();
return false;
}
const hsTArray<hsMaterialConverter::DoneMaterialData> &materials = hsMaterialConverter::Instance().DoneMaterials();
uint32_t i,count = pLayer->GetNumConversionTargets();
for( i = 0; i < count; i++ )
{
plLayerInterface *layIface = pLayer->GetConversionTarget( i );
ctrl->AddLayer( layIface );
plDynamicTextMap *map = plDynamicTextMap::ConvertNoRef( layIface->GetTexture() );
if( map != nil )
ctrl->AddMap( map );
uint32_t mat;
bool found = false;
for (mat=0; mat<materials.GetCount(); mat++)
{
hsGMaterial *curMaterial = materials[mat].fHsMaterial;
uint32_t lay;
for (lay=0; lay<curMaterial->GetNumLayers(); lay++)
{
if (layIface->BottomOfStack() == curMaterial->GetLayer(lay))
{
ctrl->AddMaterial(curMaterial);
found = true;
break;
}
}
if (found)
break;
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIMultiLineEdit Component ///////////////////////////////////////////////////////////////
//
// GUI element that can be dragged anywhere in the 2D viewing plane.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIMultiLineEditComp : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIMultiLineEditCtrl; }
virtual bool INeedsDynamicText( void ) { return true; }
public:
plGUIMultiLineEditComp();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefXparentBgnd,
kRefScaleWithRes,
kRefUseScroll,
kRefScrollCtrl,
};
};
static plGUISingleCtrlDlgProc sGUIMultiLineProc( plGUIMultiLineEditComp::kRefScrollCtrl, IDC_GUI_COMPSELBTN,
"Select the control to use for scrolling this multi-line edit box", sScrollingClassesToSelect );
//Max desc stuff necessary below.
CLASS_DESC(plGUIMultiLineEditComp, gGUIMultiLineEditDesc, "GUI Multi-Line Edit Box", "GUIMultiLineEdit", COMP_TYPE_GUI, GUI_MULTILINE_CLASSID )
ParamBlockDesc2 gGUIMultiLineEditBoxBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIMultiLineEdit"), 0, &gGUIMultiLineEditDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
2,
plGUIControlBase::kRollMain, IDD_COMP_GUIMULTILINE, IDS_COMP_GUIMULTILINE, 0, 0, &sGUIMultiLineProc,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
&sGUIControlProcParamTemplate,
plGUIMultiLineEditComp::kRefXparentBgnd, _T( "xparentBgnd" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_XPARENT,
p_default, FALSE,
end,
plGUIMultiLineEditComp::kRefScaleWithRes, _T( "scaleWithRes" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCALERES,
p_default, FALSE,
end,
plGUIMultiLineEditComp::kRefUseScroll, _T( "enableScrolling" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCROLLCTRL,
p_default, FALSE,
p_enable_ctrls, 1, plGUIMultiLineEditComp::kRefScrollCtrl,
end,
plGUIMultiLineEditComp::kRefScrollCtrl, _T("scrollControl"), TYPE_INODE, 0, 0,
p_prompt, IDS_COMP_GUI_SELECTSCROLL,
p_accessor, &sGUIListBoxAccessor,
end,
end
);
plGUIMultiLineEditComp::plGUIMultiLineEditComp()
{
fClassDesc = &gGUIMultiLineEditDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIMultiLineEditComp::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIMultiLineEditComp::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUIMultiLineEditComp::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIMultiLineEditCtrl *ctrl = (pfGUIMultiLineEditCtrl *)fControl;
if( fCompPB->GetInt( kRefXparentBgnd ) )
ctrl->SetFlag( pfGUIControlMod::kXparentBgnd );
if( fCompPB->GetInt( kRefScaleWithRes ) )
ctrl->SetFlag( pfGUIControlMod::kScaleTextWithResolution );
if( fCompPB->GetInt( kRefUseScroll ) )
{
// Get the scrolling control to use
pfGUIValueCtrl *scroll = pfGUIValueCtrl::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefScrollCtrl ) ) );
if( scroll != nil )
{
hsgResMgr::ResMgr()->AddViaNotify( scroll->GetKey(), TRACKED_NEW plGenRefMsg( ctrl->GetKey(),
plRefMsg::kOnCreate, -1, pfGUIMultiLineEditCtrl::kRefScrollCtrl ), plRefFlags::kActiveRef );
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIProgressCtrl Component ///////////////////////////////////////////////////////////////////
//
// GUI element that can be dragged anywhere in the 2D viewing plane.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIProgressCtrlComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIProgressCtrl; }
virtual bool ICanHaveProxy( void ) { return false; }
public:
plGUIProgressCtrlComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefMinValue,
kRefMaxValue,
kRefStep,
kReverseValues,
kRefOrientation,
kRefMouseMapping,
kRefTriggerOnMouseUp,
kRefAnimation,
kRefAnimationNode,
kRefAnimationNodeType,
kRefAnimateSound,
kRefAnimateSoundComp
};
};
class plGUIProgressCtrlAccessor : public PBAccessor
{
public:
void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t )
{
if( id == plGUIProgressCtrlComponent::kRefAnimateSoundComp )
{
plGUIProgressCtrlComponent *comp = (plGUIProgressCtrlComponent *)owner;
comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED );
}
}
};
static plGUIProgressCtrlAccessor sGUIProgressCtrlAccessor;
class plGUISoundDlgProc : public ParamMap2UserDlgProc
{
protected:
ParamID fSoundID;
int fSoundItem;
TCHAR fTitle[ 128 ];
public:
plGUISoundDlgProc( ParamID soundID, int soundItem, TCHAR *title )
{
fSoundID = soundID;
fSoundItem = soundItem;
strcpy( fTitle, title );
}
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch ( msg )
{
case WM_INITDIALOG:
{
IParamBlock2 *pb = map->GetParamBlock();
INode *node = pb->GetINode( fSoundID );
TSTR newName( node ? node->GetName() : "Pick" );
::SetWindowText( ::GetDlgItem( hWnd, fSoundItem ), newName );
}
return true;
case WM_COMMAND:
if( ( HIWORD( wParam ) == BN_CLICKED ) )
{
if( LOWORD( wParam ) == fSoundItem )
{
IParamBlock2 *pb = map->GetParamBlock();
plGUICtrlHitCallback hitCB( (INode *)pb->GetOwner(), pb, fSoundID, fTitle, true, GUI_SOUND_COMPONENT_ID );
GetCOREInterface()->DoHitByNameDialog( &hitCB );
INode* node = pb->GetINode( fSoundID );
TSTR newName( node ? node->GetName() : "Pick" );
::SetWindowText( ::GetDlgItem(hWnd, fSoundItem ), newName );
map->Invalidate( fSoundID );
::InvalidateRect( hWnd, NULL, TRUE );
return true;
}
}
break;
}
return false;
}
void DeleteThis() {}
};
static plGUISoundDlgProc sGUIProgressCtrlSndProc( plGUIProgressCtrlComponent::kRefAnimateSoundComp, IDC_GUI_ANIMSNDCOMP,
"Select the sound to play when this control animates" );
static plPlasmaAnimSelectDlgProc sGUIProgressCtrlProc( plGUIProgressCtrlComponent::kRefAnimation, IDC_GUI_COMPSELBTN,
plGUIProgressCtrlComponent::kRefAnimationNode, plGUIProgressCtrlComponent::kRefAnimationNodeType, IDC_GUI_ANIMNODESEL,
"Select the animation to use when displaying this knob control", &sGUIProgressCtrlSndProc );
//Max desc stuff necessary below.
CLASS_DESC(plGUIProgressCtrlComponent, gGUIProgressCtrlDesc, "GUI Progress Control", "GUIProgressCtrl", COMP_TYPE_GUI, GUI_PROGRESS_CLASSID )
ParamBlockDesc2 gGUIProgressCtrlBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIProgressCtrl"), 0, &gGUIProgressCtrlDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
2,
plGUIControlBase::kRollMain, IDD_COMP_GUIPROGRESS, IDS_COMP_GUIPROGRESS, 0, 0, &sGUIProgressCtrlProc,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
&sGUIControlProcParamTemplate,
plGUIProgressCtrlComponent::kRefMinValue, _T("minValue"), TYPE_FLOAT, 0, 0,
p_default, 0.0f,
p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_LOWER, IDC_GUI_LOWER_SPIN, SPIN_AUTOSCALE,
end,
plGUIProgressCtrlComponent::kRefMaxValue, _T("maxValue"), TYPE_FLOAT, 0, 0,
p_default, 10.0f,
p_range, -10000.f, 10000.f, // WHY do we even need to specify this?
p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_UPPER, IDC_GUI_UPPER_SPIN, SPIN_AUTOSCALE,
end,
plGUIProgressCtrlComponent::kRefStep, _T("step"), TYPE_FLOAT, 0, 0,
p_default, 1.0f,
p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_STEP, IDC_GUI_STEP_SPIN, SPIN_AUTOSCALE,
end,
plGUIProgressCtrlComponent::kReverseValues, _T("reverseValues"), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_REVERSE,
p_default, FALSE,
end,
plGUIProgressCtrlComponent::kRefAnimation, _T("animation"), TYPE_INODE, 0, 0,
p_prompt, IDS_COMP_GUI_SELECTANIM,
end,
plGUIProgressCtrlComponent::kRefAnimationNode, _T("animationNode"), TYPE_INODE, 0, 0,
end,
plGUIProgressCtrlComponent::kRefAnimationNodeType, _T("animationNodeType"), TYPE_INT, 0, 0,
p_default, plAnimObjInterface::kUseOwnerNode,
end,
plGUIProgressCtrlComponent::kRefAnimateSound, _T( "animateSound" ), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ANIMSND,
p_default, FALSE,
p_enable_ctrls, 1, plGUIProgressCtrlComponent::kRefAnimateSoundComp,
end,
plGUIProgressCtrlComponent::kRefAnimateSoundComp, _T("animateSoundComp"), TYPE_INODE, 0, 0,
p_accessor, &sGUIProgressCtrlAccessor,
end,
end
);
plGUIProgressCtrlComponent::plGUIProgressCtrlComponent()
{
fClassDesc = &gGUIProgressCtrlDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIProgressCtrlComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
node->SetForceLocal( true );
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) );
if( iface != nil && iface->MightRequireSeparateMaterial() )
{
INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode )
? fCompPB->GetINode( kRefAnimationNode )
: (INode *)node;
if( restrict != nil )
{
node->SetForceMaterialCopy( true );
}
}
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIProgressCtrlComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
// For hackery below (see warning below)
#include "plAvatar/plAGMasterMod.h"
hsBool plGUIProgressCtrlComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIProgressCtrl *ctrl = (pfGUIProgressCtrl *)fControl;
ctrl->SetRange( fCompPB->GetFloat( kRefMinValue ), fCompPB->GetFloat( kRefMaxValue ) );
ctrl->SetStep( fCompPB->GetFloat( kRefStep ) );
if( fCompPB->GetInt( kReverseValues ) )
ctrl->SetFlag( pfGUIProgressCtrl::kReverseValues );
// Get the animation to use
plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) );
if( iface != nil )
{
INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode )
? fCompPB->GetINode( kRefAnimationNode )
: (INode *)node;
hsTArray<plKey> keys;
if( iface->GetKeyList( restrict, keys ) && keys.GetCount() > 0 )
ctrl->SetAnimationKeys( keys, iface->GetIfaceSegmentName( false ) );
}
else
{
// HACKERY WARNING: Old knobs assumed the animation was just the same one applied to our node,
// so to avoid breaking old formats, if we can't grab an animObjInterface, we just grab the key
// of the master mod of our node, like we would've before
plAGMasterMod *master = node->GetAGMasterMod();
hsTArray<plKey> keys;
keys.Append( master->GetKey() );
ctrl->SetAnimationKeys( keys, ENTIRE_ANIMATION_NAME );
}
const char *errMsg = ISetSoundIndex( kRefAnimateSound, kRefAnimateSoundComp, pfGUIProgressCtrl::kAnimateSound, node );
if( errMsg != nil )
{
pErrMsg->Set( true, "GUI Sound Event Error", errMsg, node->GetName() ).Show();
pErrMsg->Set( false );
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIClickMap Component ///////////////////////////////////////////////////////////////////
//
// GUI element that just keeps track of where on its surface (from 0-1) that it was clicked.
//
/////////////////////////////////////////////////////////////////////////////////////////////////
// Class that accesses the paramblock below.
class plGUIClickMapComponent : public plGUIControlBase
{
protected:
virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIClickMapCtrl; }
virtual bool ICanHaveProxy( void ) { return false; }
public:
plGUIClickMapComponent();
void DeleteThis() { delete this; }
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg);
hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg);
enum
{
kRefReportDragging
};
};
//Max desc stuff necessary below.
CLASS_DESC(plGUIClickMapComponent, gGUIClickMapDesc, "GUI Clickable Map", "GUIClickMap", COMP_TYPE_GUI, GUI_CLICKMAP_CLASSID )
ParamBlockDesc2 gGUIClickMapBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIClickMap"), 0, &gGUIClickMapDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
2,
plGUIControlBase::kRollMain, IDD_COMP_GUICLICKMAP, IDS_COMP_GUICLICKMAP, 0, 0, NULL,
plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil,
&sGUIControlProcParamTemplate,
plGUIClickMapComponent::kRefReportDragging, _T("reportWhileDragging"), TYPE_BOOL, 0, 0,
p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_REPORTDRAG,
p_default, FALSE,
end,
end
);
plGUIClickMapComponent::plGUIClickMapComponent()
{
fClassDesc = &gGUIClickMapDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIClickMapComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
node->SetForceLocal( true );
return plGUIControlBase::SetupProperties( node, pErrMsg );
}
hsBool plGUIClickMapComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
return plGUIControlBase::PreConvert( node, pErrMsg );
}
hsBool plGUIClickMapComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
if( !plGUIControlBase::Convert( node, pErrMsg ) )
return false;
pfGUIClickMapCtrl *ctrl = (pfGUIClickMapCtrl *)fControl;
if( fCompPB->GetInt( kRefReportDragging ) )
ctrl->SetFlag( pfGUIClickMapCtrl::kReportDragging );
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUISkin Component ////////////////////////////////////////////////////////////////////
//
// Defines a skin to use when rendering certain GUI controls (just menus for now)
//
/////////////////////////////////////////////////////////////////////////////////////////////////
class pfGUISkinProc : public ParamMap2UserDlgProc
{
protected:
public:
void DeleteThis() {}
// virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map );
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
};
static pfGUISkinProc gGUISkinProc;
// Component defined in pfGUISkinProc.h
#define kDeclSkinRectValues( ref ) (plGUISkinComp::##ref + 0), _T("f##ref##.left"), TYPE_INT, 0, 0, p_default, 0, end, \
(plGUISkinComp::##ref + 1), _T("f##ref##.top"), TYPE_INT, 0, 0, p_default, 0, end, \
(plGUISkinComp::##ref + 2), _T("f##ref##.width"), TYPE_INT, 0, 0, p_default, 8, end, \
(plGUISkinComp::##ref + 3), _T("f##ref##.height"), TYPE_INT, 0, 0, p_default, 8, end
#define kSetSkinRectValues( pb, ref, l, t, w, h ) { pb->SetValue( ref + 0, 0, (int) l ); \
pb->SetValue( ref + 1, 0, (int) t ); \
pb->SetValue( ref + 2, 0, (int) r ); \
pb->SetValue( ref + 3, 0, (int) b ); }
//Max desc stuff necessary below.
CLASS_DESC(plGUISkinComp, gGUISkinDesc, "GUI Skin", "GUISkin", COMP_TYPE_GUI, GUI_SKIN_CLASSID )
static ParamBlockDesc2 gGUISkinBk
(
/// Main def
plComponent::kBlkComp, _T("GUISkin"), 0, &gGUISkinDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
IDD_COMP_GUISKIN, IDS_COMP_GUISKIN, 0, 0, &gGUISkinProc,
plGUISkinComp::kRefBitmap, _T("bitmap"), TYPE_TEXMAP, 0, 0,
end,
kDeclSkinRectValues( kRefUpLeftCorner ),
kDeclSkinRectValues( kRefTopSpan ),
kDeclSkinRectValues( kRefUpRightCorner ),
kDeclSkinRectValues( kRefRightSpan ),
kDeclSkinRectValues( kRefLowerRightCorner ),
kDeclSkinRectValues( kRefBottomSpan ),
kDeclSkinRectValues( kRefLowerLeftCorner ),
kDeclSkinRectValues( kRefLeftSpan ),
kDeclSkinRectValues( kRefMiddleFill ),
kDeclSkinRectValues( kRefSelectedFill ),
kDeclSkinRectValues( kRefSubMenuArrow ),
kDeclSkinRectValues( kRefSelectedSubMenuArrow ),
kDeclSkinRectValues( kRefTreeButtonClosed ),
kDeclSkinRectValues( kRefTreeButtonOpen ),
plGUISkinComp::kRefItemMargin, _T("itemMargin"), TYPE_INT, 0, 0,
p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_GUI_IMARGIN, IDC_GUI_IMARGIN_SPIN, SPIN_AUTOSCALE,
p_default, 1,
end,
plGUISkinComp::kRefBorderMargin, _T("borderMargin"), TYPE_INT, 0, 0,
p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_GUI_BMARGIN, IDC_GUI_BMARGIN_SPIN, SPIN_AUTOSCALE,
p_default, 4,
end,
end
);
// Editor proc
extern HINSTANCE hInstance;
BOOL pfGUISkinProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
IParamBlock2 *pb = pmap->GetParamBlock();
plGUISkinComp *comp = (plGUISkinComp *)pb->GetOwner();
PBBitmap *bitmap;
plLayerTex *layer = comp->GetSkinBitmap();
ICustButton *bmSelectBtn;
switch( msg )
{
case WM_INITDIALOG:
// Set projection map bitmap name
bmSelectBtn = GetICustButton( GetDlgItem( hWnd, IDC_GUI_SKINBMAP ) );
if( bmSelectBtn != nil )
{
bitmap = ( layer == nil ) ? nil : layer->GetPBBitmap();
if( bitmap != nil )
bmSelectBtn->SetText( (TCHAR *)bitmap->bi.Filename() );
else
bmSelectBtn->SetText( _T( "<none>" ) );
ReleaseICustButton( bmSelectBtn );
}
return true;
case WM_DESTROY:
break;
case WM_COMMAND:
if( LOWORD( wParam ) == IDC_GUI_EDITELEM )
{
bitmap = ( layer == nil ) ? nil : layer->GetPBBitmap();
if( bitmap != nil )
{
pfGUISkinEditProc proc( comp );
DialogBox( hInstance, MAKEINTRESOURCE( IDD_COMP_SKINEDIT ), GetCOREInterface()->GetMAXHWnd(), proc.DlgProc );
}
}
else if( LOWORD( wParam ) == IDC_GUI_SKINBMAP )
{
BOOL selectedNewBitmap = layer->HandleBitmapSelection();
if( selectedNewBitmap )
{
bmSelectBtn = GetICustButton( GetDlgItem( hWnd, IDC_GUI_SKINBMAP ) );
bitmap = layer->GetPBBitmap();
bmSelectBtn->SetText( bitmap != nil ? (TCHAR *)bitmap->bi.Filename() : "");
ReleaseICustButton( bmSelectBtn );
}
return false;
}
break;
}
return false;
}
plKey plGUISkinComp::GetConvertedSkinKey( void ) const
{
if( fConvertedSkin != nil )
return fConvertedSkin->GetKey();
return nil;
}
uint32_t plGUISkinComp::GetNumMtls( void ) const
{
return 1;
}
Texmap *plGUISkinComp::GetMtl( uint32_t idx )
{
return (Texmap *)GetSkinBitmap();
}
//// GetSkinBitmap ///////////////////////////////////////////////////////////
plLayerTex *plGUISkinComp::GetSkinBitmap( void )
{
// If we don't have one, create one
plLayerTex *layer = (plLayerTex *)fCompPB->GetTexmap( kRefBitmap, 0 );
if( layer == nil || layer->ClassID() != LAYER_TEX_CLASS_ID )
{
layer = TRACKED_NEW plLayerTex;
fCompPB->SetValue( kRefBitmap, 0, (Texmap *)layer );
}
if( layer )
{
IParamBlock2* bitmapPB = layer->GetParamBlockByID( plLayerTex::kBlkBitmap );
if( bitmapPB->GetInt(kBmpScaling) != kScalingNone )
bitmapPB->SetValue(kBmpScaling, TimeValue(0), kScalingNone);
}
return layer;
}
plGUISkinComp::plGUISkinComp()
{
fClassDesc = &gGUISkinDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUISkinComp::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
fConvertedSkin = nil;
return true;
}
hsBool plGUISkinComp::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
// Create and assign key here, so other components can grab the key later
if( fConvertedSkin != nil )
return true; // Only convert once, since we don't care what node we're on
Texmap *texture = fCompPB->GetTexmap( kRefBitmap );
if( texture == nil || texture->ClassID() != LAYER_TEX_CLASS_ID || ( (plLayerTex *)texture )->GetPBBitmap() == nil )
{
pErrMsg->Set( true, "GUI Skin Convert Error",
"The GUI skin component %s doesn't have a mipmap associated with it. This skin will not "
"be exported.", GetINode()->GetName() ).CheckAndAsk();
pErrMsg->Set( false );
return true;
}
fConvertedSkin = TRACKED_NEW pfGUISkin();
hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), fConvertedSkin, node->GetLocation() );
return true;
}
hsBool plGUISkinComp::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
// Actually do the work of converting all the skin data
if( fConvertedSkin == nil )
return true; // Eh?
fConvertedSkin->SetMargins( fCompPB->GetInt( kRefItemMargin ), fCompPB->GetInt( kRefBorderMargin ) );
uint32_t i;
for( i = 0; i < pfGUISkin::kNumElements; i++ )
{
ParamID id = ( i * 4 ) + kRefUpLeftCorner;
fConvertedSkin->SetElement( i, fCompPB->GetInt( id + 0 ), fCompPB->GetInt( id + 1 ),
fCompPB->GetInt( id + 2 ), fCompPB->GetInt( id + 3 ) );
}
plLayerTex *layer= (plLayerTex *)fCompPB->GetTexmap( kRefBitmap );
if( layer != nil )
{
PBBitmap *texture = layer->GetPBBitmap();
if( texture != nil )
{
plBitmap *bMap = plLayerConverter::Instance().CreateSimpleTexture( texture->bi.Name(), fConvertedSkin->GetKey()->GetUoid().GetLocation(), 0, plMipmap::kForceNonCompressed | plMipmap::kAlphaChannelFlag | plMipmap::kNoMaxSize );
if( bMap != nil && plMipmap::ConvertNoRef( bMap ) != nil )
{
hsgResMgr::ResMgr()->AddViaNotify( bMap->GetKey(), TRACKED_NEW plGenRefMsg( fConvertedSkin->GetKey(),
plRefMsg::kOnCreate, -1, pfGUISkin::kRefMipmap ), plRefFlags::kActiveRef );
}
}
}
return true;
}
hsBool plGUISkinComp::DeInit(plMaxNode *node, plErrorMsg *pErrMsg)
{
fConvertedSkin = nil;
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
//// plGUIPopUpMenu Component ///////////////////////////////////////////////////////////////////
//
// Defines a pop-up menu, with an auto-anchor to the sceneObject it's attached to
//
/////////////////////////////////////////////////////////////////////////////////////////////////
/*class plGUIMenuProc : public ParamMap2UserDlgProc
{
protected:
void ILoadPages( HWND hWnd, IParamBlock2 *pb );
public:
void DeleteThis() {}
BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
};
static plGUIMenuProc gGUIMenuProc;
*/
static plGUISingleCtrlDlgProc sGUISkinSelectProc( plGUIMenuComponent::kRefSkin, IDC_GUI_SKIN,
"Select the skin to use for this pop-up menu", sSkinClassesToSelect,
&gGUIDialogProc );
//Max desc stuff necessary below.
CLASS_DESC(plGUIMenuComponent, gGUIMenuDesc, "GUI Menu", "GUIMenu", COMP_TYPE_GUI, GUI_MENUANCHOR_CLASSID )
ParamBlockDesc2 gGUIMenuBk
( // KLUDGE: not the defined block ID, but kept for backwards compat.
plComponent::kBlkComp, _T("GUIMenu"), 0, &gGUIMenuDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp,
3,
plGUIMenuComponent::kMainRollout, IDD_COMP_GUIMENUANCHOR, IDS_COMP_GUIMENUANCHOR, 0, 0, &sGUISkinSelectProc,
plGUIMenuComponent::kTagIDRollout, IDD_COMP_GUITAG, IDS_COMP_GUITAG, 0, 0, &gGUITagProc,
plGUIMenuComponent::kSchemeRollout, IDD_COMP_GUISCHEME, IDS_COMP_GUISCHEME, 0, 0, &gGUIColorSchemeProc,
&gGUIColorSchemeBk,
plGUIMenuComponent::kRefDialogName, _T("MenuName"), TYPE_STRING, 0, 0,
// p_ui, plGUIMenuComponent::kMainRollout, TYPE_EDITBOX, IDC_GUIDLG_NAME,
end,
plGUIMenuComponent::kRefAgeName, _T("ageName"), TYPE_STRING, 0, 0,
p_default, _T( "GUI" ),
end,
plGUIMenuComponent::kRefVersion, _T("version"), TYPE_INT, 0, 0,
p_ui, plGUIMenuComponent::kMainRollout, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_GUI_VERSION, IDC_GUI_VERSION_SPIN, SPIN_AUTOSCALE,
p_default, 0,
end,
plGUITagComponent::kRefCurrIDSel, _T("currSel"), TYPE_INT, 0, 0,
end,
plGUIMenuComponent::kRefSkin, _T("skin"), TYPE_INODE, 0, 0,
end,
plGUIMenuComponent::kRefNeverClose, _T("neverClose"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, plGUIMenuComponent::kMainRollout, TYPE_SINGLECHEKBOX, IDC_GUI_NEVERCLOSE,
end,
plGUIMenuComponent::kRefModalOutside, _T("modalOutside"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, plGUIMenuComponent::kMainRollout, TYPE_SINGLECHEKBOX, IDC_GUI_MODALOUTSIDE,
end,
plGUIMenuComponent::kRefOpenOnHover, _T("openSubsOnHover"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, plGUIMenuComponent::kMainRollout, TYPE_SINGLECHEKBOX, IDC_GUI_HOVER,
end,
plGUIMenuComponent::kRefAlignment, _T("alignment"), TYPE_INT, 0, 0,
p_default, 3,
p_ui, plGUIMenuComponent::kMainRollout, TYPE_RADIO, 4, IDC_ALIGNRADIO1, IDC_ALIGNRADIO2, IDC_ALIGNRADIO3, IDC_ALIGNRADIO4,
end,
plGUIMenuComponent::kRefScaleWithScreenRes, _T("maintainSizeAcrossRes"), TYPE_BOOL, 0, 0,
p_default, FALSE,
p_ui, plGUIMenuComponent::kMainRollout, TYPE_SINGLECHEKBOX, IDC_GUI_SCALERES,
end,
end
);
plGUIMenuComponent::plGUIMenuComponent() : plGUIDialogComponent( true )
{
fClassDesc = &gGUIMenuDesc;
fClassDesc->MakeAutoParamBlocks(this);
}
pfGUIDialogMod *plGUIMenuComponent::IMakeDialog( void )
{
return TRACKED_NEW pfGUIPopUpMenu();
}
plKey plGUIMenuComponent::GetConvertedMenuKey( void ) const
{
if( fConvertedMenu == nil )
return nil;
return fConvertedMenu->GetKey();
}
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
// of properties on the MaxNode, as it's still indeterminant.
hsBool plGUIMenuComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg)
{
// return plGUIDialogComponent::SetupProperties( node, pErrMsg );
fConvertedMenu = nil;
return true;
}
hsBool plGUIMenuComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg)
{
pfGUIPopUpMenu *menu = fConvertedMenu;
// hsBool b = plGUIDialogComponent::Convert( node, pErrMsg );
// if( b )
{
// pfGUIPopUpMenu *menu = pfGUIPopUpMenu::ConvertNoRef( fDialogMod );
// hsAssert( menu != nil, "Somehow got a bad poitner in GUIMenu::Convert()" );
INode *sNode = fCompPB->GetINode( kRefSkin );
if( sNode != nil )
{
plComponentBase *comp = ( (plMaxNode *)sNode )->ConvertToComponent();
if( comp != nil )
{
Class_ID nodeID = comp->ClassID();
hsAssert( nodeID == GUI_SKIN_CLASSID, "Bad node param in GUIMenu::Convert()" );
plGUISkinComp *skin = (plGUISkinComp *)comp;
menu->SetSkin( skin->GetConvertedSkin() );
}
}
if( fCompPB->GetInt( kRefNeverClose ) )
menu->SetFlag( pfGUIPopUpMenu::kStayOpenAfterClick );
if( fCompPB->GetInt( kRefModalOutside ) )
menu->SetFlag( pfGUIPopUpMenu::kModalOutsideMenus );
if( fCompPB->GetInt( kRefOpenOnHover ) )
menu->SetFlag( pfGUIPopUpMenu::kOpenSubMenusOnHover );
if( fCompPB->GetInt( kRefScaleWithScreenRes ) )
menu->SetFlag( pfGUIPopUpMenu::kScaleWithResolution );
switch( fCompPB->GetInt( kRefAlignment ) )
{
case 0: menu->SetAlignment( pfGUIPopUpMenu::kAlignUpLeft ); break;
case 1: menu->SetAlignment( pfGUIPopUpMenu::kAlignUpRight ); break;
case 2: menu->SetAlignment( pfGUIPopUpMenu::kAlignDownLeft ); break;
case 3: menu->SetAlignment( pfGUIPopUpMenu::kAlignDownRight ); break;
}
}
// Note: we use the owning dialog of our anchor object as the context, i.e. who translates
// our point at runtime into screen coordinates
menu->SetOriginAnchor( node->GetSceneObject(), plGUIDialogComponent::GetNodeDialog( node ) );
const plLocation &loc = menu->GetKey()->GetUoid().GetLocation();
// Create the rendermod
plPostEffectMod *renderMod = TRACKED_NEW plPostEffectMod;
hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), renderMod, loc );
renderMod->SetHither( 0.5f );
renderMod->SetYon( 200.f );
renderMod->SetNodeKey( fConvertedNode );
float scrnWidth = 20.f;
// fovX should be such that scrnWidth is the projected width at z=100
float fovX = atan( scrnWidth / ( 2.f * 100.f ) ) * 2.f;
float fovY = fovX;// * 3.f / 4.f;
renderMod->SetFovX( fovX * 180.f / M_PI );
renderMod->SetFovY( fovY * 180.f / M_PI );
hsgResMgr::ResMgr()->AddViaNotify( renderMod->GetKey(), TRACKED_NEW plNodeRefMsg( fConvertedNode, plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric ), plRefFlags::kActiveRef );
hsgResMgr::ResMgr()->AddViaNotify( fConvertedNode, TRACKED_NEW plGenRefMsg( renderMod->GetKey(), plRefMsg::kOnCreate, 0, plPostEffectMod::kNodeRef ), plRefFlags::kPassiveRef );
menu->SetRenderMod( renderMod );
menu->SetName( fCompPB->GetStr( kRefDialogName ) );
// Create the dummy scene object to hold the menu
plSceneObject *newObj = TRACKED_NEW plSceneObject;
hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), newObj, loc );
// *#&$(*@&#$ need a coordIface...
plCoordinateInterface *newCI = TRACKED_NEW plCoordinateInterface;
hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), newCI, loc );
hsgResMgr::ResMgr()->AddViaNotify( menu->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier ), plRefFlags::kActiveRef );
hsgResMgr::ResMgr()->AddViaNotify( newCI->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface ), plRefFlags::kActiveRef );
hsgResMgr::ResMgr()->AddViaNotify( renderMod->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier ), plRefFlags::kActiveRef );
newObj->SetSceneNode( fConvertedNode );
menu->SetSceneNodeKey( fConvertedNode );
{
hsMatrix44 l2w, w2l;
l2w.Reset();
l2w.GetInverse( &w2l );
newObj->SetTransform( l2w, w2l );
}
// Should be done now...
return true;
}
hsBool plGUIMenuComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg)
{
// Create and assign key here, so other components can grab the key later
if( fConvertedMenu != nil )
return true; // Only convert once, since we don't care what node we're on
/// Create an entirely new sceneNode for us
int32_t seqNum = plPageInfoUtils::GetSeqNumFromAgeDesc( fCompPB->GetStr( kRefAgeName ), fCompPB->GetStr( kRefDialogName ) );
int32_t newNum = plPluginResManager::ResMgr()->VerifySeqNumber( seqNum, fCompPB->GetStr( kRefAgeName ), fCompPB->GetStr( kRefDialogName ) );
if( newNum != seqNum )
{
if( !fSeqNumValidated )
{
char errMsg[ 512 ];
sprintf( errMsg, "GUI Menu Component %s has an invalid location sequence number (0x%X). Temporarily using a valid one (0x%X).",
node->GetName(), seqNum, newNum );
pErrMsg->Set( true, "PageInfo Convert Error", errMsg ).Show();
pErrMsg->Set( false );
fSeqNumValidated = true;
}
seqNum = newNum;
}
fConvertedNode = plPluginResManager::ResMgr()->NameToLoc( fCompPB->GetStr( kRefAgeName ), fCompPB->GetStr( kRefDialogName ), seqNum );
if( !fConvertedNode )
{
pErrMsg->Set( true, "GUI Menu Component Error", "GUI MenuComponent %s has a Missing Location. Nuke the files in the dat directory and re-export.",((INode*)node)->GetName()).Show();
return false;
}
fConvertedMenu = TRACKED_NEW pfGUIPopUpMenu();
hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), fConvertedMenu, fConvertedNode->GetUoid().GetLocation() );
return true;
// return plGUIDialogComponent::PreConvert( node, pErrMsg );
}
hsBool plGUIMenuComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg)
{
fConvertedMenu = nil;
fConvertedNode = nil;
return true;
}