Index: ps/trunk/source/ps/Errors.cpp =================================================================== --- ps/trunk/source/ps/Errors.cpp (revision 768) +++ ps/trunk/source/ps/Errors.cpp (revision 769) @@ -1,24 +1,80 @@ // Auto-generated by errorlist.pl - do not edit #include "precompiled.h" #include "Errors.h" // Slightly hacky section to redeclare things that are declared // elsewhere - trust the compiler to handle them identically +class PSERROR_GUI : public PSERROR {}; +class PSERROR_Scripting : public PSERROR {}; +class PSERROR_Scripting_DefineType : public PSERROR_Scripting {}; +class PSERROR_Scripting_LoadFile : public PSERROR_Scripting {}; class PSERROR_Xeromyces : public PSERROR {}; + +class PSERROR_GUI_JSOpenFailed : public PSERROR_GUI { public: PSERROR_GUI_JSOpenFailed(); }; +class PSERROR_GUI_TextureLoadFailed : public PSERROR_GUI { public: PSERROR_GUI_TextureLoadFailed(); }; +class PSERROR_Scripting_CallFunctionFailed : public PSERROR_Scripting { public: PSERROR_Scripting_CallFunctionFailed(); }; +class PSERROR_Scripting_ContextCreationFailed : public PSERROR_Scripting { public: PSERROR_Scripting_ContextCreationFailed(); }; +class PSERROR_Scripting_ConversionFailed : public PSERROR_Scripting { public: PSERROR_Scripting_ConversionFailed(); }; +class PSERROR_Scripting_CreateObjectFailed : public PSERROR_Scripting { public: PSERROR_Scripting_CreateObjectFailed(); }; +class PSERROR_Scripting_DefineConstantFailed : public PSERROR_Scripting { public: PSERROR_Scripting_DefineConstantFailed(); }; +class PSERROR_Scripting_DefineType_AlreadyExists : public PSERROR_Scripting_DefineType { public: PSERROR_Scripting_DefineType_AlreadyExists(); }; +class PSERROR_Scripting_DefineType_CreationFailed : public PSERROR_Scripting_DefineType { public: PSERROR_Scripting_DefineType_CreationFailed(); }; +class PSERROR_Scripting_GlobalObjectCreationFailed : public PSERROR_Scripting { public: PSERROR_Scripting_GlobalObjectCreationFailed(); }; +class PSERROR_Scripting_LoadFile_EvalErrors : public PSERROR_Scripting_LoadFile { public: PSERROR_Scripting_LoadFile_EvalErrors(); }; +class PSERROR_Scripting_LoadFile_OpenFailed : public PSERROR_Scripting_LoadFile { public: PSERROR_Scripting_LoadFile_OpenFailed(); }; +class PSERROR_Scripting_NativeFunctionSetupFailed : public PSERROR_Scripting { public: PSERROR_Scripting_NativeFunctionSetupFailed(); }; +class PSERROR_Scripting_RegisterFunctionFailed : public PSERROR_Scripting { public: PSERROR_Scripting_RegisterFunctionFailed(); }; +class PSERROR_Scripting_RuntimeCreationFailed : public PSERROR_Scripting { public: PSERROR_Scripting_RuntimeCreationFailed(); }; +class PSERROR_Scripting_StandardClassSetupFailed : public PSERROR_Scripting { public: PSERROR_Scripting_StandardClassSetupFailed(); }; +class PSERROR_Scripting_TypeDoesNotExist : public PSERROR_Scripting { public: PSERROR_Scripting_TypeDoesNotExist(); }; class PSERROR_Xeromyces_XMLOpenFailed : public PSERROR_Xeromyces { public: PSERROR_Xeromyces_XMLOpenFailed(); }; class PSERROR_Xeromyces_XMLParseError : public PSERROR_Xeromyces { public: PSERROR_Xeromyces_XMLParseError(); }; // The relevant bits of this file: -PSERROR_Xeromyces_XMLOpenFailed::PSERROR_Xeromyces_XMLOpenFailed() { magic=0x50534552; code=0; } -PSERROR_Xeromyces_XMLParseError::PSERROR_Xeromyces_XMLParseError() { magic=0x50534552; code=1; } +PSERROR_GUI_JSOpenFailed::PSERROR_GUI_JSOpenFailed() { magic=0x45725221; code=0; } +PSERROR_GUI_TextureLoadFailed::PSERROR_GUI_TextureLoadFailed() { magic=0x45725221; code=1; } +PSERROR_Scripting_DefineType_AlreadyExists::PSERROR_Scripting_DefineType_AlreadyExists() { magic=0x45725221; code=2; } +PSERROR_Scripting_DefineType_CreationFailed::PSERROR_Scripting_DefineType_CreationFailed() { magic=0x45725221; code=3; } +PSERROR_Scripting_LoadFile_EvalErrors::PSERROR_Scripting_LoadFile_EvalErrors() { magic=0x45725221; code=4; } +PSERROR_Scripting_LoadFile_OpenFailed::PSERROR_Scripting_LoadFile_OpenFailed() { magic=0x45725221; code=5; } +PSERROR_Scripting_CallFunctionFailed::PSERROR_Scripting_CallFunctionFailed() { magic=0x45725221; code=6; } +PSERROR_Scripting_ContextCreationFailed::PSERROR_Scripting_ContextCreationFailed() { magic=0x45725221; code=7; } +PSERROR_Scripting_ConversionFailed::PSERROR_Scripting_ConversionFailed() { magic=0x45725221; code=8; } +PSERROR_Scripting_CreateObjectFailed::PSERROR_Scripting_CreateObjectFailed() { magic=0x45725221; code=9; } +PSERROR_Scripting_DefineConstantFailed::PSERROR_Scripting_DefineConstantFailed() { magic=0x45725221; code=10; } +PSERROR_Scripting_GlobalObjectCreationFailed::PSERROR_Scripting_GlobalObjectCreationFailed() { magic=0x45725221; code=11; } +PSERROR_Scripting_NativeFunctionSetupFailed::PSERROR_Scripting_NativeFunctionSetupFailed() { magic=0x45725221; code=12; } +PSERROR_Scripting_RegisterFunctionFailed::PSERROR_Scripting_RegisterFunctionFailed() { magic=0x45725221; code=13; } +PSERROR_Scripting_RuntimeCreationFailed::PSERROR_Scripting_RuntimeCreationFailed() { magic=0x45725221; code=14; } +PSERROR_Scripting_StandardClassSetupFailed::PSERROR_Scripting_StandardClassSetupFailed() { magic=0x45725221; code=15; } +PSERROR_Scripting_TypeDoesNotExist::PSERROR_Scripting_TypeDoesNotExist() { magic=0x45725221; code=16; } +PSERROR_Xeromyces_XMLOpenFailed::PSERROR_Xeromyces_XMLOpenFailed() { magic=0x45725221; code=17; } +PSERROR_Xeromyces_XMLParseError::PSERROR_Xeromyces_XMLParseError() { magic=0x45725221; code=18; } const wchar_t* GetErrorString(int code) { switch (code) { - case 0: return L"Xeromyces_XMLOpenFailed"; break; - case 1: return L"Xeromyces_XMLParseError"; break; + case 0: return L"GUI_JSOpenFailed"; break; + case 1: return L"GUI_TextureLoadFailed"; break; + case 2: return L"Scripting_DefineType_AlreadyExists"; break; + case 3: return L"Scripting_DefineType_CreationFailed"; break; + case 4: return L"Scripting_LoadFile_EvalErrors"; break; + case 5: return L"Scripting_LoadFile_OpenFailed"; break; + case 6: return L"Scripting_CallFunctionFailed"; break; + case 7: return L"Scripting_ContextCreationFailed"; break; + case 8: return L"Scripting_ConversionFailed"; break; + case 9: return L"Scripting_CreateObjectFailed"; break; + case 10: return L"Scripting_DefineConstantFailed"; break; + case 11: return L"Scripting_GlobalObjectCreationFailed"; break; + case 12: return L"Scripting_NativeFunctionSetupFailed"; break; + case 13: return L"Scripting_RegisterFunctionFailed"; break; + case 14: return L"Scripting_RuntimeCreationFailed"; break; + case 15: return L"Scripting_StandardClassSetupFailed"; break; + case 16: return L"Scripting_TypeDoesNotExist"; break; + case 17: return L"Xeromyces_XMLOpenFailed"; break; + case 18: return L"Xeromyces_XMLParseError"; break; } return L"Unrecognised error"; } Index: ps/trunk/source/ps/Xeromyces.h =================================================================== --- ps/trunk/source/ps/Xeromyces.h (revision 768) +++ ps/trunk/source/ps/Xeromyces.h (revision 769) @@ -1,46 +1,45 @@ -/* $Id: Xeromyces.h,v 1.3 2004/07/15 19:08:28 philip Exp $ +/* $Id: Xeromyces.h,v 1.4 2004/07/17 17:06:21 philip Exp $ Xeromyces file-loading interface. Automatically creates and caches relatively efficient binary representations of XML files. - Philip Taylor (philip@zaynar.demon.co.uk / @wildfiregames.com) */ #ifndef _XEROMYCES_H_ #define _XEROMYCES_H_ ERROR_GROUP(PSERROR, Xeromyces); ERROR_TYPE(PSERROR_Xeromyces, XMLOpenFailed); ERROR_TYPE(PSERROR_Xeromyces, XMLParseError); #include "ps/XeroXMB.h" #include "lib/res/h_mgr.h" class CXeromyces : public XMBFile { public: CXeromyces(); ~CXeromyces(); // Load from an XML file (with invisible XMB caching). - // Throws a const char* if stuff breaks. + // Throws a PSERROR_Xeromyces upon failure. void Load(const char* filename); - // Call once when shutting down the program. + // Call once when shutting down the program, to unload Xerces. static void Terminate(); - private: bool ReadXMBFile(const char* filename, bool CheckCRC, unsigned long CRC); XMBFile* XMB; Handle XMBFileHandle; // if it's being read from disk char* XMBBuffer; // if it's being read from RAM static int XercesLoaded; // for once-only initialisation }; #endif // _XEROMYCES_H_ Index: ps/trunk/source/ps/Errors.h =================================================================== --- ps/trunk/source/ps/Errors.h (revision 768) +++ ps/trunk/source/ps/Errors.h (revision 769) @@ -1,18 +1,19 @@ #ifndef _ERRORS_H_ #define _ERRORS_H_ #include // for wchar_t class PSERROR { public: - int magic; // = 0x50534552, so the exception handler can recognise that it's a PSERROR - int code; + int magic; // = 0x45725221, so the exception handler can recognise + // that it's a PSERROR and not some other random object. + int code; // unique (but arbitrary) code, for translation tables etc }; #define ERROR_GROUP(a,b) class a##_##b : public a {} #define ERROR_TYPE(a,b) class a##_##b : public a { public: a##_##b(); } const wchar_t* GetErrorString(int code); #endif Index: ps/trunk/source/simulation/BaseEntity.cpp =================================================================== --- ps/trunk/source/simulation/BaseEntity.cpp (revision 768) +++ ps/trunk/source/simulation/BaseEntity.cpp (revision 769) @@ -1,125 +1,125 @@ #include "precompiled.h" #include "BaseEntity.h" #include "ObjectManager.h" #include "CStr.h" #include "ps/Xeromyces.h" CBaseEntity::CBaseEntity() { m_base = NULL; m_base.associate( this, "super" ); m_name.associate( this, "name" ); m_speed.associate( this, "speed" ); m_turningRadius.associate( this, "turningRadius" ); m_bound_circle = NULL; m_bound_box = NULL; } CBaseEntity::~CBaseEntity() { if( m_bound_box ) delete( m_bound_box ); if( m_bound_circle ) delete( m_bound_circle ); } bool CBaseEntity::loadXML( CStr filename ) { CXeromyces XeroFile; try { XeroFile.Load(filename); } - catch (...) - { + catch (PSERROR_Xeromyces) { + // Fail return false; } // Define all the elements and attributes used in the XML file #define EL(x) int el_##x = XeroFile.getElementID(#x) #define AT(x) int at_##x = XeroFile.getAttributeID(#x) EL(entity); EL(name); EL(actor); EL(speed); EL(turningradius); EL(size); EL(footprint); EL(boundsoffset); AT(radius); AT(width); AT(height); AT(x); AT(y); #undef AT #undef EL XMBElement Root = XeroFile.getRoot(); assert(Root.getNodeName() == el_entity); XMBElementList RootChildren = Root.getChildNodes(); for (int i = 0; i < RootChildren.Count; ++i) { XMBElement Child = RootChildren.item(i); int ChildName = Child.getNodeName(); if (ChildName == el_name) { m_name = (CStr)Child.getText(); } else if (ChildName == el_actor) { m_actorObject = g_ObjMan.FindObject( (CStr)Child.getText() ); } else if (ChildName == el_speed) { m_speed = CStr(Child.getText()).ToFloat(); } else if (ChildName == el_turningradius) { m_turningRadius = CStr(Child.getText()).ToFloat(); } else if (ChildName == el_size) { if( !m_bound_circle ) m_bound_circle = new CBoundingCircle(); CStr radius (Child.getAttributes().getNamedItem(at_radius)); m_bound_circle->setRadius( radius.ToFloat() ); m_bound_type = CBoundingObject::BOUND_CIRCLE; } else if (ChildName == el_footprint) { if( !m_bound_box ) m_bound_box = new CBoundingBox(); CStr width (Child.getAttributes().getNamedItem(at_width)); CStr height (Child.getAttributes().getNamedItem(at_height)); m_bound_box->setDimensions( width.ToFloat(), height.ToFloat() ); m_bound_type = CBoundingObject::BOUND_OABB; } else if (ChildName == el_boundsoffset) { CStr x (Child.getAttributes().getNamedItem(at_x)); CStr y (Child.getAttributes().getNamedItem(at_y)); if( !m_bound_circle ) m_bound_circle = new CBoundingCircle(); if( !m_bound_box ) m_bound_box = new CBoundingBox(); m_bound_circle->m_offset.x = x.ToFloat(); m_bound_circle->m_offset.y = y.ToFloat(); m_bound_box->m_offset.x = x.ToFloat(); m_bound_box->m_offset.y = y.ToFloat(); } } return true; } Index: ps/trunk/source/simulation/scripting/JSInterface_Entity.cpp =================================================================== --- ps/trunk/source/simulation/scripting/JSInterface_Entity.cpp (revision 768) +++ ps/trunk/source/simulation/scripting/JSInterface_Entity.cpp (revision 769) @@ -1,137 +1,137 @@ #include "precompiled.h" #include "JSInterface_Entity.h" #include "scripting/JSInterface_BaseEntity.h" #include "scripting/JSInterface_Vector3D.h" #include "EntityHandles.h" #include "Entity.h" #include "EntityManager.h" #include "BaseEntityCollection.h" #include "CConsole.h" JSClass JSI_Entity::JSI_class = { "Entity", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JSI_Entity::getProperty, JSI_Entity::setProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JSI_Entity::finalize, NULL, NULL, NULL, NULL }; JSPropertySpec JSI_Entity::JSI_props[] = { { 0 } }; JSFunctionSpec JSI_Entity::JSI_methods[] = { { "toString", JSI_Entity::toString, 0, 0, 0 }, { 0 } }; JSBool JSI_Entity::getProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) { HEntity* e = (HEntity*)JS_GetPrivate( cx, obj ); if( !e ) { *vp = JSVAL_NULL; return( JS_TRUE ); } CStr propName = g_ScriptingHost.ValueToString( id ); if( (*e)->m_properties.find( propName ) != (*e)->m_properties.end() ) { *vp = *((*e)->m_properties[propName]); return( JS_TRUE ); } return( JS_TRUE ); } JSBool JSI_Entity::setProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) { HEntity* e = (HEntity*)JS_GetPrivate( cx, obj ); CStr propName = g_ScriptingHost.ValueToString( id ); if( (*e)->m_properties.find( propName ) != (*e)->m_properties.end() ) { *((*e)->m_properties[propName]) = *vp; (*e)->rebuild( propName ); return( JS_TRUE ); } return( JS_TRUE ); } JSBool JSI_Entity::construct( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval ) { assert( argc >= 2 ); CBaseEntity* baseEntity; CVector3D position; float orientation = 0.0f; JSObject* jsBaseEntity = JSVAL_TO_OBJECT( argv[0] ); if( JSVAL_IS_OBJECT( argv[0] ) && ( JS_GetClass( jsBaseEntity ) == &JSI_BaseEntity::JSI_class ) ) { baseEntity = (CBaseEntity*)JS_GetPrivate( cx, jsBaseEntity ); } else { CStr templateName; try { templateName = g_ScriptingHost.ValueToString( argv[0] ); } - catch( ... ) + catch( PSERROR_Scripting_ConversionFailed ) { *rval = JSVAL_NULL; return( JS_TRUE ); } baseEntity = g_EntityTemplateCollection.getTemplate( templateName ); } if( !baseEntity ) { *rval = JSVAL_NULL; return( JS_TRUE ); } JSObject* jsVector3D = JSVAL_TO_OBJECT( argv[1] ); if( JSVAL_IS_OBJECT( argv[1] ) && ( JS_GetClass( jsVector3D ) == &JSI_Vector3D::JSI_class ) ) position = *( ( (JSI_Vector3D::Vector3D_Info*)JS_GetPrivate( cx, jsVector3D ) )->vector ); if( argc >= 3 ) { try { orientation = (float)g_ScriptingHost.ValueToDouble( argv[2] ); } - catch( ... ) + catch( PSERROR_Scripting_ConversionFailed ) { orientation = 0.0f; } } HEntity* handle = new HEntity( g_EntityManager.create( baseEntity, position, orientation ) ); CMessage message( CMessage::EMSG_INIT ); (*handle)->dispatch( &message ); JSObject* entity = JS_NewObject( cx, &JSI_Entity::JSI_class, NULL, NULL ); JS_SetPrivate( cx, entity, handle ); *rval = OBJECT_TO_JSVAL( entity ); return( JS_TRUE ); } void JSI_Entity::finalize( JSContext* cx, JSObject* obj ) { delete( (HEntity*)JS_GetPrivate( cx, obj ) ); } void JSI_Entity::init() { g_ScriptingHost.DefineCustomObjectType( &JSI_class, construct, 2, JSI_props, JSI_methods, NULL, NULL ); } JSBool JSI_Entity::toString( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) { HEntity* e = (HEntity*)JS_GetPrivate( cx, obj ); char buffer[256]; snprintf( buffer, 256, "[object Entity: \"%s\" (%s)]", (const TCHAR*)(*e)->m_name, (const TCHAR*)(*e)->m_base->m_name ); buffer[255] = 0; *rval = STRING_TO_JSVAL( JS_NewStringCopyZ( cx, buffer ) ); return( JS_TRUE ); } Index: ps/trunk/source/simulation/EntityProperties.cpp =================================================================== --- ps/trunk/source/simulation/EntityProperties.cpp (revision 768) +++ ps/trunk/source/simulation/EntityProperties.cpp (revision 769) @@ -1,370 +1,370 @@ #include "precompiled.h" #include "EntityProperties.h" #include "BaseEntityCollection.h" #include "scripting/JSInterface_BaseEntity.h" #undef new // to avoid confusing warnings void CProperty::associate( IPropertyOwner* owner, const CStr& name ) { m_owner = owner; owner->m_properties[name] = this; m_updateFn = NULL; } void CProperty::associate( IPropertyOwner* owner, const CStr& name, void (IPropertyOwner::*updateFn)() ) { m_owner = owner; owner->m_properties[name] = this; m_updateFn = updateFn; } CProperty& CProperty::operator=( jsval value ) { set( value ); return( *this ); } CProperty_i32::CProperty_i32() { modifier = NULL; } CProperty_i32::~CProperty_i32() { if( modifier ) delete( modifier ); } inline CProperty_i32& CProperty_i32::operator =( i32 value ) { if( !modifier ) modifier = new SProperty_NumericModifier(); *modifier = (float)value; data = value; return( *this ); } void CProperty_i32::set( jsval value ) { if( !modifier ) modifier = new SProperty_NumericModifier(); try { *modifier = (float)g_ScriptingHost.ValueToInt( value ); } - catch( ... ) + catch( PSERROR_Scripting_ConversionFailed ) { *modifier = 0; } } bool CProperty_i32::rebuild( CProperty* parent, bool triggerFn ) { CProperty_i32* _parent = (CProperty_i32*)parent; i32 newvalue = 0; if( _parent ) newvalue = *_parent; if( modifier ) { newvalue = (i32)(newvalue * modifier->multiplicative); newvalue += (i32)modifier->additive; } if( data == newvalue ) return( false ); // No change. data = newvalue; if( triggerFn && m_updateFn ) (m_owner->*m_updateFn)(); return( true ); } inline CProperty_i32::operator i32() { return( data ); } CProperty_i32::operator jsval() { return( INT_TO_JSVAL( data ) ); } CProperty_float::CProperty_float() { modifier = NULL; } CProperty_float::~CProperty_float() { if( modifier ) delete modifier; } CProperty_float& CProperty_float::operator =( const float& value ) { if( !modifier ) modifier = new SProperty_NumericModifier(); *modifier = value; data = value; return( *this ); } void CProperty_float::set( const jsval value ) { if( !modifier ) modifier = new SProperty_NumericModifier(); try { *modifier = (float)g_ScriptingHost.ValueToDouble( value ); } - catch( ... ) + catch( PSERROR_Scripting_ConversionFailed ) { *modifier = 0.0f; } } bool CProperty_float::rebuild( CProperty* parent, bool triggerFn ) { CProperty_float* _parent = (CProperty_float*)parent; float newvalue = 0; if( _parent ) newvalue = *_parent; if( modifier ) { newvalue *= modifier->multiplicative; newvalue += modifier->additive; } if( data == newvalue ) return( false ); // No change. data = newvalue; if( triggerFn && m_updateFn ) (m_owner->*m_updateFn)(); return( true ); } CProperty_float::operator float() { return( data ); } CProperty_float::operator jsval() { return( DOUBLE_TO_JSVAL( JS_NewDouble( g_ScriptingHost.getContext(), (jsdouble)data ) ) ); } CProperty_float::operator bool() { return( data != 0.0f); } float CProperty_float::operator+( float value ) { return( data + value ); } float CProperty_float::operator-( float value ) { return( data - value ); } float CProperty_float::operator*( float value ) { return( data * value ); } float CProperty_float::operator/( float value ) { return( data / value ); } bool CProperty_float::operator<( float value ) { return( data < value ); } bool CProperty_float::operator>( float value ) { return( data > value ); } bool CProperty_float::operator==( float value ) { return( data == value ); } CProperty_CStr::CProperty_CStr() { modifier = NULL; } CProperty_CStr::~CProperty_CStr() { if( modifier ) delete( modifier ); } CProperty_CStr& CProperty_CStr::operator=( const CStr& value ) { if( !modifier ) modifier = new SProperty_StringModifier(); *modifier = value; m_String = value; return( *this ); } void CProperty_CStr::set( jsval value ) { if( !modifier ) modifier = new SProperty_StringModifier(); try { *modifier = g_ScriptingHost.ValueToString( value ); } - catch( ... ) + catch( PSERROR_Scripting_ConversionFailed ) { *modifier = CStr(); m_String.clear(); } } bool CProperty_CStr::rebuild( CProperty* parent, bool triggerFn ) { CProperty_CStr* _parent = (CProperty_CStr*)parent; CStr newvalue = ""; if( _parent ) newvalue = *_parent; if( modifier ) newvalue = modifier->replacement; if( *this == newvalue ) return( false ); // No change. m_String = newvalue; if( triggerFn && m_updateFn ) (m_owner->*m_updateFn)(); return( true ); } CProperty_CStr::operator jsval() { return( STRING_TO_JSVAL( JS_NewStringCopyZ( g_ScriptingHost.getContext(), m_String.c_str() ) ) ); } CProperty_CVector3D& CProperty_CVector3D::operator =( const CVector3D& value ) { *( (CVector3D*)this ) = value; return( *this ); } void CProperty_CVector3D::set( jsval value ) { JSObject* vector3d = JSVAL_TO_OBJECT( value ); if( !JSVAL_IS_OBJECT( value ) || ( JS_GetClass( vector3d ) != &JSI_Vector3D::JSI_class ) ) { X = 0.0f; Y = 0.0f; Z = 0.0f; } else { CVector3D* copy = ( (JSI_Vector3D::Vector3D_Info*)JS_GetPrivate( g_ScriptingHost.getContext(), vector3d ) )->vector; X = copy->X; Y = copy->Y; Z = copy->Z; } } bool CProperty_CVector3D::rebuild( CProperty* parent, bool triggerFn ) { if( triggerFn && m_updateFn ) (m_owner->*m_updateFn)(); return( false ); // Vector properties aren't inheritable. } CProperty_CVector3D::operator jsval() { JSObject* vector3d = JS_NewObject( g_ScriptingHost.getContext(), &JSI_Vector3D::JSI_class, NULL, NULL ); JS_SetPrivate( g_ScriptingHost.getContext(), vector3d, new JSI_Vector3D::Vector3D_Info( this, m_owner, m_updateFn ) ); return( OBJECT_TO_JSVAL( vector3d ) ); } CProperty_CBaseEntityPtr& CProperty_CBaseEntityPtr::operator =( CBaseEntity* value ) { data = value; return( *this ); } void CProperty_CBaseEntityPtr::set( jsval value ) { JSObject* baseEntity = JSVAL_TO_OBJECT( value ); if( JSVAL_IS_OBJECT( value ) && ( JS_GetClass( baseEntity ) == &JSI_BaseEntity::JSI_class ) ) data = (CBaseEntity*)JS_GetPrivate( g_ScriptingHost.getContext(), baseEntity ); } bool CProperty_CBaseEntityPtr::rebuild( CProperty* parent, bool triggerFn ) { if( triggerFn && m_updateFn ) (m_owner->*m_updateFn)(); return( false ); // CBaseEntity* properties aren't inheritable. } CProperty_CBaseEntityPtr::operator jsval() { JSObject* baseEntity = JS_NewObject( g_ScriptingHost.getContext(), &JSI_BaseEntity::JSI_class, NULL, NULL ); JS_SetPrivate( g_ScriptingHost.getContext(), baseEntity, data ); return( OBJECT_TO_JSVAL( baseEntity ) ); } CProperty_CBaseEntityPtr::operator bool() { return( data != NULL ); } CProperty_CBaseEntityPtr::operator CBaseEntity*() { return( data ); } CBaseEntity& CProperty_CBaseEntityPtr::operator *() const { return( *data ); } CBaseEntity* CProperty_CBaseEntityPtr::operator ->() const { return( data ); } void IPropertyOwner::rebuild( CStr propertyName ) { CProperty* thisProperty = m_properties[propertyName]; CProperty* baseProperty = NULL; if( m_base ) { if( m_base->m_properties.find( propertyName ) != m_base->m_properties.end() ) baseProperty = m_base->m_properties[propertyName]; } if( thisProperty->rebuild( baseProperty ) ) { std::vector::iterator it; for( it = m_inheritors.begin(); it != m_inheritors.end(); it++ ) (*it)->rebuild( propertyName ); } } void IPropertyOwner::rebuild() { STL_HASH_MAP::iterator property; if( m_base ) { for( property = m_properties.begin(); property != m_properties.end(); property++ ) { CProperty* baseProperty = NULL; if( m_base->m_properties.find( property->first ) != m_base->m_properties.end() ) baseProperty = m_base->m_properties[property->first]; (property->second)->rebuild( baseProperty, false ); } } else { for( property = m_properties.begin(); property != m_properties.end(); property++ ) (property->second)->rebuild( NULL, false ); } std::vector::iterator it; for( it = m_inheritors.begin(); it != m_inheritors.end(); it++ ) (*it)->rebuild(); } Index: ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp (revision 768) +++ ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp (revision 769) @@ -1,370 +1,372 @@ -// $Id: JSInterface_IGUIObject.cpp,v 1.5 2004/07/12 15:52:53 philip Exp $ +// $Id: JSInterface_IGUIObject.cpp,v 1.6 2004/07/17 17:05:10 philip Exp $ #include "precompiled.h" #include "JSInterface_IGUIObject.h" #include "JSInterface_GUITypes.h" JSClass JSI_IGUIObject::JSI_class = { "GUIObject", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JSI_IGUIObject::getProperty, JSI_IGUIObject::setProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, NULL, NULL, NULL, NULL }; JSPropertySpec JSI_IGUIObject::JSI_props[] = { { 0 } }; JSFunctionSpec JSI_IGUIObject::JSI_methods[] = { { "toString", JSI_IGUIObject::toString, 0, 0, 0 }, { "getByName", JSI_IGUIObject::getByName, 1, 0, 0 }, { 0 } }; JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp) { CStr propName = JS_GetStringBytes(JS_ValueToString(cx, id)); // Skip some things which are known to be functions rather than properties. // ("constructor" *must* be here, else it'll try to GetSettingType before // the private IGUIObject* has been set (and thus crash). The others are // just for efficiency.) if (propName == (CStr)"constructor" || propName == (CStr)"toString" || propName == (CStr)"getByName" ) return JS_TRUE; IGUIObject* e = (IGUIObject*)JS_GetPrivate(cx, obj); // Handle the "parent" property specially if (propName == (CStr)"parent") { IGUIObject* parent = e->GetParent(); if (parent) { // If the object isn't parentless, return a new object JSObject* entity = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL); JS_SetPrivate(cx, entity, parent); *vp = OBJECT_TO_JSVAL(entity); } else { // Return null if there's no parent *vp = JSVAL_NULL; } return JS_TRUE; } // Also handle "name" specially else if (propName == (CStr)"name") { *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, e->GetName())); return JS_TRUE; } else { EGUISettingType Type; if (e->GetSettingType(propName, Type) != PS_OK) { - // Possibly a function, but they should have been individually - // handled above, so complain about it + // Potentially a function, but they should have been + // individually handled above, so complain about it JS_ReportError(cx, "Invalid GUIObject property '%s'", propName.c_str()); return JS_FALSE; } // (All the cases are in {...} to avoid scoping problems) switch (Type) { case GUIST_bool: { bool value; GUI::GetSetting(e, propName, value); *vp = value ? JSVAL_TRUE : JSVAL_FALSE; break; } case GUIST_int: { int value; GUI::GetSetting(e, propName, value); *vp = INT_TO_JSVAL(value); break; } case GUIST_float: { float value; GUI::GetSetting(e, propName, value); // Create a garbage-collectable double *vp = DOUBLE_TO_JSVAL(JS_NewDouble(cx, value) ); break; } case GUIST_CColor: { CColor colour; GUI::GetSetting(e, propName, colour); JSObject* obj = JS_NewObject(cx, &JSI_GUIColor::JSI_class, NULL, NULL); // Attempt to minimise ugliness through macrosity #define P(x) jsval x = DOUBLE_TO_JSVAL(JS_NewDouble(cx, colour.x)); JS_SetProperty(cx, obj, #x, &x) P(r); P(g); P(b); P(a); #undef P *vp = OBJECT_TO_JSVAL(obj); break; } case GUIST_CClientArea: { CClientArea area; GUI::GetSetting(e, propName, area); JSObject* obj = JS_NewObject(cx, &JSI_GUISize::JSI_class, NULL, NULL); + #define P(x, y, z) jsval z = INT_TO_JSVAL(area.x.y); JS_SetProperty(cx, obj, #z, &z) P(pixel, left, left); P(pixel, top, top); P(pixel, right, right); P(pixel, bottom, bottom); P(percent, left, rleft); P(percent, top, rtop); P(percent, right, rright); P(percent, bottom, rbottom); #undef P + *vp = OBJECT_TO_JSVAL(obj); break; } case GUIST_CGUIString: { CGUIString value; GUI::GetSetting(e, propName, value); // Create a garbage-collectable copy of the string *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value.GetRawString().c_str() )); break; } case GUIST_CStr: { CStr value; GUI::GetSetting(e, propName, value); // Create a garbage-collectable copy of the string *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value.c_str() )); break; } default: JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str()); assert(! "This shouldn't happen"); return JS_FALSE; } return JS_TRUE; } // Automatically falls through to methods return JS_TRUE; } JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp) { IGUIObject* e = (IGUIObject*)JS_GetPrivate(cx, obj); CStr propName = g_ScriptingHost.ValueToString(id); if (propName == (CStr)"name") { CStr propValue = JS_GetStringBytes(JS_ValueToString(cx, *vp)); e->SetName(propValue); } else { EGUISettingType Type; if (e->GetSettingType(propName, Type) != PS_OK) { JS_ReportError(cx, "Invalid setting '%s'", propName.c_str()); return JS_TRUE; } try { switch (Type) { case GUIST_CStr: case GUIST_CGUIString: e->SetSetting(propName, JS_GetStringBytes(JS_ValueToString(cx, *vp)) ); break; case GUIST_int: { int32 value; if (JS_ValueToInt32(cx, *vp, &value) == JS_TRUE) GUI::SetSetting(e, propName, value); else { JS_ReportError(cx, "Cannot convert value to int"); return JS_FALSE; } break; } case GUIST_float: { jsdouble value; if (JS_ValueToNumber(cx, *vp, &value) == JS_TRUE) GUI::SetSetting(e, propName, (float)value); else { JS_ReportError(cx, "Cannot convert value to float"); return JS_FALSE; } break; } case GUIST_bool: { JSBool value; if (JS_ValueToBoolean(cx, *vp, &value) == JS_TRUE) GUI::SetSetting(e, propName, value||0); // ||0 to avoid int-to-bool compiler warnings else { JS_ReportError(cx, "Cannot convert value to bool"); return JS_FALSE; } break; } case GUIST_CClientArea: { if (JSVAL_IS_STRING(*vp)) { e->SetSetting(propName, JS_GetStringBytes(JS_ValueToString(cx, *vp)) ); } else if (JSVAL_IS_OBJECT(*vp) && JS_GetClass(JSVAL_TO_OBJECT(*vp)) == &JSI_GUISize::JSI_class) { CClientArea area; GUI::GetSetting(e, propName, area); JSObject* obj = JSVAL_TO_OBJECT(*vp); jsval t; int32 s; #define PROP(x) JS_GetProperty(cx, obj, #x, &t); \ JS_ValueToInt32(cx, t, &s); \ area.pixel.x = s PROP(left); PROP(top); PROP(right); PROP(bottom); #undef PROP GUI::SetSetting(e, propName, area); } else { JS_ReportError(cx, "Size only accepts strings or GUISize objects"); return JS_FALSE; } break; } case GUIST_CColor: { if (JSVAL_IS_STRING(*vp)) { e->SetSetting(propName, JS_GetStringBytes(JS_ValueToString(cx, *vp)) ); } else if (JSVAL_IS_OBJECT(*vp) && JS_GetClass(JSVAL_TO_OBJECT(*vp)) == &JSI_GUIColor::JSI_class) { CColor colour; JSObject* obj = JSVAL_TO_OBJECT(*vp); jsval t; double s; #define PROP(x) JS_GetProperty(cx, obj, #x, &t); \ JS_ValueToNumber(cx, t, &s); \ colour.x = (float)s PROP(r); PROP(g); PROP(b); PROP(a); #undef PROP GUI::SetSetting(e, propName, colour); } else { JS_ReportError(cx, "Color only accepts strings or GUIColor objects"); return JS_FALSE; } break; } default: JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str()); break; } } // Catch failed calls to SetSetting (the string and templated versions) catch (PS_RESULT) { JS_ReportError(cx, "Invalid value for setting '%s'", propName.c_str()); return JS_FALSE; } } return JS_TRUE; } JSBool JSI_IGUIObject::construct(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval) { if (argc == 0) { JS_ReportError(cx, "GUIObject has no default constructor"); return JS_FALSE; } assert(argc == 1); // Store the IGUIObject in the JS object's 'private' area IGUIObject* guiObject = (IGUIObject*)JSVAL_TO_PRIVATE(argv[0]); JS_SetPrivate(cx, obj, guiObject); return JS_TRUE; } JSBool JSI_IGUIObject::getByName(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval) { assert(argc == 1); CStr objectName = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); IGUIObject* guiObject = g_GUI.FindObjectByName(objectName); if (!guiObject) { // Not found - return null *rval = JSVAL_NULL; return JS_TRUE; } JSObject* entity = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL); JS_SetPrivate(cx, entity, guiObject); *rval = OBJECT_TO_JSVAL(entity); return JS_TRUE; } void JSI_IGUIObject::init() { g_ScriptingHost.DefineCustomObjectType(&JSI_class, construct, 1, JSI_props, JSI_methods, NULL, NULL); } JSBool JSI_IGUIObject::toString(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { IGUIObject* e = (IGUIObject*)JS_GetPrivate( cx, obj ); char buffer[256]; snprintf(buffer, 256, "[GUIObject: %s]", (const TCHAR*)e->GetName()); buffer[255] = 0; *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buffer)); return JS_TRUE; } Index: ps/trunk/source/gui/CGUI.cpp =================================================================== --- ps/trunk/source/gui/CGUI.cpp (revision 768) +++ ps/trunk/source/gui/CGUI.cpp (revision 769) @@ -1,1586 +1,1639 @@ /* CGUI by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" // Types - when including them into the engine. #include "CButton.h" #include "CText.h" #include "CCheckBox.h" #include "CRadioButton.h" #include "ps/Xeromyces.h" #include "Prometheus.h" #include "input.h" #include "OverlayText.h" // TODO Gee: Whatever include CRect/CPos/CSize #include "Overlay.h" #include "scripting/ScriptingHost.h" #include #include #include // namespaces used using namespace std; #include "ps/CLogger.h" #define XERO_TIME // Class for global JavaScript object JSClass GUIClass = { "GUIClass", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, }; extern int g_xres, g_yres; // TODO Gee: how to draw overlays? void render(COverlayText* overlaytext) { } //------------------------------------------------------------------- // called from main loop when (input) events are received. // event is passed to other handlers if false is returned. // trampoline: we don't want to make the implementation (in CGUI) static //------------------------------------------------------------------- int gui_handler(const SDL_Event* ev) { return g_GUI.HandleEvent(ev); } int CGUI::HandleEvent(const SDL_Event* ev) { if(ev->type == SDL_MOUSEMOTION) { m_MousePos = CPos(ev->motion.x, ev->motion.y); GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::HandleMessage, SGUIMessage(GUIM_MOUSE_MOTION)); } // TODO Gee: temp-stuff // char buf[30]; // sprintf(buf, "type = %d", ev->type); //TEMPmessage = buf; // Update m_MouseButtons. (BUTTONUP is handled later.) if (ev->type == SDL_MOUSEBUTTONDOWN) { // (0,1,2) = (LMB,RMB,MMB) if (ev->button.button < 3) m_MouseButtons |= (1 << ev->button.button); } // JW: (pre|post)process omitted; what're they for? why would we need any special button_released handling? // Only one object can be hovered IGUIObject *pNearest = NULL; try { // TODO Gee: Optimizations needed! // these two recursive function are quite overhead heavy. // pNearest will after this point at the hovered object, possibly NULL GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::ChooseMouseOverAndClosest, pNearest); if (ev->type == SDL_MOUSEMOTION && pNearest) pNearest->ScriptEvent("mousemove"); // Now we'll call UpdateMouseOver on *all* objects, // we'll input the one hovered, and they will each // update their own data and send messages accordingly GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest); if (ev->type == SDL_MOUSEBUTTONDOWN) { switch (ev->button.button) { case SDL_BUTTON_LEFT: if (pNearest) { pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_PRESS_LEFT)); pNearest->ScriptEvent("mouseleftpress"); } break; case SDL_BUTTON_WHEELDOWN: // wheel down if (pNearest) { pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_WHEEL_DOWN)); pNearest->ScriptEvent("mousewheeldown"); } break; case SDL_BUTTON_WHEELUP: // wheel up if (pNearest) { pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_WHEEL_UP)); pNearest->ScriptEvent("mousewheelup"); } break; default: break; } } else if (ev->type == SDL_MOUSEBUTTONUP) { if (ev->button.button == SDL_BUTTON_LEFT) { if (pNearest) { pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_RELEASE_LEFT)); pNearest->ScriptEvent("mouseleftrelease"); } } // Reset all states on all visible objects GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::ResetStates); // It will have reset the mouse over of the current hovered, so we'll // have to restore that if (pNearest) pNearest->m_MouseHovering = true; } } catch (PS_RESULT e) { UNUSED(e); // TODO Gee: Handle } // JW: what's the difference between mPress and mDown? what's the code below responsible for? /* // Generally if just mouse is clicked if (m_pInput->mDown(NEMM_BUTTON1) && pNearest) { pNearest->HandleMessage(GUIM_MOUSE_DOWN_LEFT); } */ // BUTTONUP's effect on m_MouseButtons is handled after // everything else, so that e.g. 'press' handlers (activated // on button up) see which mouse button had been pressed. if (ev->type == SDL_MOUSEBUTTONUP) { // (0,1,2) = (LMB,RMB,MMB) if (ev->button.button < 3) m_MouseButtons &= ~(1 << ev->button.button); } return EV_PASS; } //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- CGUI::CGUI() : m_InternalNameNumber(0), m_MouseButtons(0) { m_BaseObject = new CGUIDummyObject; m_BaseObject->SetGUI(this); // Construct the parent object for all GUI JavaScript things m_ScriptObject = (void*)JS_NewObject(g_ScriptingHost.getContext(), &GUIClass, NULL, NULL); assert(m_ScriptObject != NULL); // How should it handle errors? JS_AddRoot(g_ScriptingHost.getContext(), &m_ScriptObject); // This will make this invisible, not add //m_BaseObject->SetName(BASE_OBJECT_NAME); } CGUI::~CGUI() { if (m_BaseObject) delete m_BaseObject; if (m_ScriptObject) // Let it be garbage-collected JS_RemoveRoot(g_ScriptingHost.getContext(), &m_ScriptObject); } //------------------------------------------------------------------- // Functions //------------------------------------------------------------------- IGUIObject *CGUI::ConstructObject(const CStr& str) { if (m_ObjectTypes.count(str) > 0) return (*m_ObjectTypes[str])(); else { // TODO Gee: Report in log return NULL; } } void CGUI::Initialize() { // Add base types! // You can also add types outside the GUI to extend the flexibility of the GUI. // Prometheus though will have all the object types inserted from here. AddObjectType("empty", &CGUIDummyObject::ConstructObject); AddObjectType("button", &CButton::ConstructObject); AddObjectType("text", &CText::ConstructObject); AddObjectType("checkbox", &CCheckBox::ConstructObject); AddObjectType("radiobutton", &CRadioButton::ConstructObject); } void CGUI::Process() { /* // TODO Gee: check if m_pInput is valid, otherwise return /// assert(m_pInput); // Pre-process all objects try { GUI::RecurseObject(0, m_BaseObject, &IGUIObject::HandleMessage, GUIM_PREPROCESS); } catch (PS_RESULT e) { return; } // Check mouse over try { // Only one object can be hovered // check which one it is, if any ! IGUIObject *pNearest = NULL; GUI::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::ChooseMouseOverAndClosest, pNearest); // Now we'll call UpdateMouseOver on *all* objects, // we'll input the one hovered, and they will each // update their own data and send messages accordingly GUI::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest); // If pressed if (m_pInput->mPress(NEMM_BUTTON1) && pNearest) { pNearest->HandleMessage(GUIM_MOUSE_PRESS_LEFT); } else // If released if (m_pInput->mRelease(NEMM_BUTTON1) && pNearest) { pNearest->HandleMessage(GUIM_MOUSE_RELEASE_LEFT); } // Generally if just mouse is clicked if (m_pInput->mDown(NEMM_BUTTON1) && pNearest) { pNearest->HandleMessage(GUIM_MOUSE_DOWN_LEFT); } } catch (PS_RESULT e) { return; } // Post-process all objects try { GUI::RecurseObject(0, m_BaseObject, &IGUIObject::HandleMessage, GUIM_POSTPROCESS); } catch (PS_RESULT e) { return; } */ } void CGUI::Draw() { // Clear the depth buffer, so the GUI is // drawn on top of everything else glClear(GL_DEPTH_BUFFER_BIT); glPushMatrix(); glLoadIdentity(); // Adapt (origio) to being in top left corner and down // just like the mouse position glTranslatef(0.0f, (GLfloat)g_yres, -1000.0f); glScalef(1.0f, -1.f, 1.0f); try { // Recurse IGUIObject::Draw() with restriction: hidden // meaning all hidden objects won't call Draw (nor will it recurse its children) GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::Draw); } catch (PS_RESULT e) { UNUSED(e); glPopMatrix(); // TODO Gee: Report error. return; } glPopMatrix(); } void CGUI::DrawSprite(const CStr& SpriteName, const float &Z, const CRect &Rect, const CRect &Clipping) { // This is not an error, it's just a choice not to draw any sprite. if (SpriteName == CStr()) return; - const char * buf = SpriteName; - bool DoClipping = (Clipping != CRect()); CGUISprite Sprite; // Fetch real sprite from name if (m_Sprites.count(SpriteName) == 0) { // TODO Gee: Report error return; } else Sprite = m_Sprites[SpriteName]; glPushMatrix(); glTranslatef(0.0f, 0.0f, Z); // Iterate all images and request them being drawn be the // CRenderer std::vector::const_iterator cit; for (cit=Sprite.m_Images.begin(); cit!=Sprite.m_Images.end(); ++cit) { if (cit->m_Texture) { // TODO: Handle the GL state in a nicer way glEnable(GL_TEXTURE_2D); glDisable(GL_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); int fmt; tex_info(cit->m_Texture, NULL, NULL, &fmt, NULL, NULL); if (fmt == GL_RGBA || fmt == GL_BGRA) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); } else { glDisable(GL_BLEND); } tex_bind(cit->m_Texture); CRect real = cit->m_Size.GetClientArea(Rect); // Get the screen position/size of a single tiling of the texture CRect TexSize = cit->m_TextureSize.GetClientArea(real); float TexLeft = (float)(TexSize.left - real.left) / (float)TexSize.GetWidth(); float TexRight = TexLeft + (float)real.GetWidth() / (float)TexSize.GetWidth(); // 'Bottom' is actually the top in screen-space (I think), // because the GUI puts (0,0) at the top-left float TexBottom = (float)(TexSize.bottom - real.bottom) / (float)TexSize.GetHeight(); float TexTop = TexBottom + (float)real.GetHeight() / (float)TexSize.GetHeight(); glBegin(GL_QUADS); glTexCoord2f(TexRight, TexBottom); glVertex3f((float)real.right, (float)real.bottom, cit->m_DeltaZ); glTexCoord2f(TexLeft, TexBottom); glVertex3f((float)real.left, (float)real.bottom, cit->m_DeltaZ); glTexCoord2f(TexLeft, TexTop); glVertex3f((float)real.left, (float)real.top, cit->m_DeltaZ); glTexCoord2f(TexRight, TexTop); glVertex3f((float)real.right, (float)real.top, cit->m_DeltaZ); glEnd(); glDisable(GL_TEXTURE_2D); } else { glColor3f(cit->m_BackColor.r, cit->m_BackColor.g, cit->m_BackColor.b); CRect real = cit->m_Size.GetClientArea(Rect); glBegin(GL_QUADS); glVertex3f((float)real.right, (float)real.bottom, cit->m_DeltaZ); glVertex3f((float)real.left, (float)real.bottom, cit->m_DeltaZ); glVertex3f((float)real.left, (float)real.top, cit->m_DeltaZ); glVertex3f((float)real.right, (float)real.top, cit->m_DeltaZ); glEnd(); } } glPopMatrix(); } void CGUI::Destroy() { // We can use the map to delete all // now we don't want to cancel all if one Destroy fails map_pObjects::iterator it; for (it = m_pAllObjects.begin(); it != m_pAllObjects.end(); ++it) { try { it->second->Destroy(); } catch (PS_RESULT e) { UNUSED(e); // TODO Gee: Handle } delete it->second; } // Clear all m_pAllObjects.clear(); m_Sprites.clear(); } void CGUI::UpdateResolution() { // Update ALL cached GUI<>::RecurseObject(0, m_BaseObject, &IGUIObject::UpdateCachedSize ); } void CGUI::AddObject(IGUIObject* pObject) { try { // Add CGUI pointer GUI::RecurseObject(0, pObject, &IGUIObject::SetGUI, this); // Add child to base object m_BaseObject->AddChild(pObject); // Cache tree GUI<>::RecurseObject(0, pObject, &IGUIObject::UpdateCachedSize); // Loaded GUI::RecurseObject(0, pObject, &IGUIObject::HandleMessage, SGUIMessage(GUIM_LOAD)); GUI::RecurseObject(0, pObject, &IGUIObject::ScriptEvent, "load"); } catch (PS_RESULT e) { throw e; } } void CGUI::UpdateObjects() { // We'll fill a temporary map until we know everything // succeeded map_pObjects AllObjects; try { // Fill freshly GUI< map_pObjects >::RecurseObject(0, m_BaseObject, &IGUIObject::AddToPointersMap, AllObjects ); } catch (PS_RESULT e) { // Throw the same error throw e; } // Else actually update the real one m_pAllObjects = AllObjects; } bool CGUI::ObjectExists(const CStr& Name) const { return m_pAllObjects.count(Name) != 0; } IGUIObject* CGUI::FindObjectByName(const CStr& Name) const { map_pObjects::const_iterator it = m_pAllObjects.find(Name); if (it == m_pAllObjects.end()) return NULL; else return it->second; } // private struct used only in GenerateText(...) struct SGenerateTextImage { int m_YFrom, // The images starting location in Y m_YTo, // The images end location in Y m_Indentation; // The image width in other words // Some help functions // TODO Gee: CRect => CPoint ? void SetupSpriteCall(const bool &Left, SGUIText::SSpriteCall &SpriteCall, const int &width, const int &y, const CSize &Size, const CStr& TextureName, const int &BufferZone) { // TODO Gee: Temp hardcoded values SpriteCall.m_Area.top = y+BufferZone; SpriteCall.m_Area.bottom = y+BufferZone + Size.cy; if (Left) { SpriteCall.m_Area.left = BufferZone; SpriteCall.m_Area.right = Size.cx+BufferZone; } else { SpriteCall.m_Area.left = width-BufferZone - Size.cx; SpriteCall.m_Area.right = width-BufferZone; } SpriteCall.m_TextureName = TextureName; m_YFrom = SpriteCall.m_Area.top-BufferZone; m_YTo = SpriteCall.m_Area.bottom+BufferZone; m_Indentation = Size.cx+BufferZone*2; } }; SGUIText CGUI::GenerateText(const CGUIString &string, /*const CColor &Color, */ const CStr& Font, const int &Width, const int &BufferZone) { SGUIText Text; // object we're generating if (string.m_Words.size() == 0) return Text; int x=BufferZone, y=BufferZone; // drawing pointer int from=0; bool done=false; // Images on the left or the right side. vector Images[2]; int pos_last_img=-1; // Position in the string where last img (either left or right) were encountered. // in order to avoid duplicate processing. // Easier to read. bool WordWrapping = (Width != 0); // Go through string word by word for (int i=0; i<(int)string.m_Words.size()-1 && !done; ++i) { // Pre-process each line one time, so we know which floating images // will be added for that line. // Generated stuff is stored in Feedback. CGUIString::SFeedback Feedback; // Preliminary line_height, used for word-wrapping with floating images. int prelim_line_height=0; // Width and height of all text calls generated. string.GenerateTextCall(Feedback, Font, /*CColor(),*/ string.m_Words[i], string.m_Words[i+1]); // Loop through our images queues, to see if images has been added. // Check if this has already been processed. // Also, floating images are only applicable if Word-Wrapping is on if (WordWrapping && i > pos_last_img) { // Loop left/right for (int j=0; j<2; ++j) { for (vector::const_iterator it = Feedback.m_Images[j].begin(); it != Feedback.m_Images[j].end(); ++it) { SGUIText::SSpriteCall SpriteCall; SGenerateTextImage Image; // Y is if no other floating images is above, y. Else it is placed // after the last image, like a stack downwards. int _y; if (Images[j].size() > 0) _y = max(y, Images[j].back().m_YTo); else _y = y; // TODO Gee: CSize temp CSize size; size.cx = 100; size.cy = 100; Image.SetupSpriteCall((j==CGUIString::SFeedback::Left), SpriteCall, Width, _y, size, CStr("white-border"), BufferZone); // Check if image is the lowest thing. Text.m_Size.cy = max(Text.m_Size.cy, Image.m_YTo); Images[j].push_back(Image); Text.m_SpriteCalls.push_back(SpriteCall); } } } pos_last_img = max(pos_last_img, i); x += Feedback.m_Size.cx; prelim_line_height = max(prelim_line_height, Feedback.m_Size.cy); // If Width is 0, then there's no word-wrapping, disable NewLine. if ((WordWrapping && (x > Width-BufferZone || Feedback.m_NewLine)) || i == string.m_Words.size()-2) { // Change from to i, but first keep a copy of its value. int temp_from = from; from = i; static const int From=0, To=1; //int width_from=0, width_to=width; int width_range[2]; width_range[From] = BufferZone; width_range[To] = Width - BufferZone; // Floating images are only appicable if word-wrapping is enabled. if (WordWrapping) { // Decide width of the line. We need to iterate our floating images. // this won't be exact because we're assuming the line_height // will be as our preliminary calculation said. But that may change, // although we'd have to add a couple of more loops to try straightening // this problem out, and it is very unlikely to happen noticably if one // stuctures his text in a stylistically pure fashion. Even if not, it // is still quite unlikely it will happen. // Loop through left and right side, from and to. for (int j=0; j<2; ++j) { for (vector::const_iterator it = Images[j].begin(); it != Images[j].end(); ++it) { // We're working with two intervals here, the image's and the line height's. // let's find the union of these two. int union_from, union_to; union_from = max(y, it->m_YFrom); union_to = min(y+prelim_line_height, it->m_YTo); // The union is not ø if (union_to > union_from) { if (j == From) width_range[From] = max(width_range[From], it->m_Indentation); else width_range[To] = min(width_range[To], Width - it->m_Indentation); } } } } // Reset X for the next loop x = width_range[From]; // Now we'll do another loop to figure out the height of // the line (the height of the largest character). This // couldn't be determined in the first loop (main loop) // because it didn't regard images, so we don't know // if all characters processed, will actually be involved // in that line. int line_height=0; for (int j=temp_from; j<=i; ++j) { // We don't want to use Feedback now, so we'll have to use // another. CGUIString::SFeedback Feedback2; string.GenerateTextCall(Feedback2, Font, /*CColor(),*/ string.m_Words[j], string.m_Words[j+1]); // Append X value. x += Feedback2.m_Size.cx; if (WordWrapping && x > width_range[To] && j!=temp_from && !Feedback2.m_NewLine) break; // Let line_height be the maximum m_Height we encounter. line_height = max(line_height, Feedback2.m_Size.cy); if (WordWrapping && Feedback2.m_NewLine) break; } // Reset x once more x = width_range[From]; // Move down, because font drawing starts from the baseline y += line_height; // Do the real processing now for (int j=temp_from; j<=i; ++j) { // We don't want to use Feedback now, so we'll have to use // another. CGUIString::SFeedback Feedback2; // Defaults string.GenerateTextCall(Feedback2, Font, /*Color, */ string.m_Words[j], string.m_Words[j+1]); // Iterate all and set X/Y values // Since X values are not set, we need to make an internal // iteration with an increment that will append the internal // x, that is what x_pointer is for. int x_pointer=0; vector::iterator it; for (it = Feedback2.m_TextCalls.begin(); it != Feedback2.m_TextCalls.end(); ++it) { it->m_Pos = CPos(x + x_pointer, y + line_height - it->m_Size.cy); x_pointer += it->m_Size.cx; if (it->m_pSpriteCall) { it->m_pSpriteCall->m_Area = it->m_pSpriteCall->m_Area + it->m_Pos; } } // Append X value. x += Feedback2.m_Size.cx; Text.m_Size.cx = max(Text.m_Size.cx, x+BufferZone); // The first word overrides the width limit, that we // do in those cases, are just draw that word even // though it'll extend the object. if (WordWrapping) // only if word-wrapping is applicable { if (Feedback2.m_NewLine) { from = j+1; break; } else if (x > width_range[To] && j==temp_from) { from = j+1; // do not break, since we want it to be added to m_TextCalls } else if (x > width_range[To]) { from = j; break; } } // Add the whole Feedback2.m_TextCalls to our m_TextCalls. Text.m_TextCalls.insert(Text.m_TextCalls.end(), Feedback2.m_TextCalls.begin(), Feedback2.m_TextCalls.end()); Text.m_SpriteCalls.insert(Text.m_SpriteCalls.end(), Feedback2.m_SpriteCalls.begin(), Feedback2.m_SpriteCalls.end()); if (j == string.m_Words.size()-2) done = true; } // Reset X x = 0; // Update height of all Text.m_Size.cy = max(Text.m_Size.cy, y+BufferZone); // Now if we entered as from = i, then we want // i being one minus that, so that it will become // the same i in the next loop. The difference is that // we're on a new line now. i = from-1; } } return Text; } void CGUI::DrawText(const SGUIText &Text, const CColor &DefaultColor, const CPos &pos, const float &z) { glEnable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glDisable(GL_ALPHA_TEST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); Handle font = 0; CStr LastFontName; for (vector::const_iterator it = Text.m_TextCalls.begin(); it != Text.m_TextCalls.end(); ++it) { if (it->m_pSpriteCall) continue; // Switch fonts when necessary, but remember the last one used if (it->m_Font != LastFontName) { if (font) unifont_unload(font); font = unifont_load(it->m_Font); unifont_bind(font); LastFontName = it->m_Font; } CColor color = it->m_UseCustomColor ? it->m_Color : DefaultColor; glPushMatrix(); glTranslatef((float)pos.x+it->m_Pos.x, (float)pos.y+it->m_Pos.y, (float)z); glColor4f(color.r, color.g, color.b, color.a); glwprintf(L"%hs", it->m_String.c_str()); glPopMatrix(); } if (font) unifont_unload(font); for (vector::const_iterator it=Text.m_SpriteCalls.begin(); it!=Text.m_SpriteCalls.end(); ++it) { DrawSprite(it->m_TextureName, z, it->m_Area + pos); } } void CGUI::ReportParseError(const CStr& str, ...) { // Print header if (m_Errors==0) { /// g_nemLog("*** GUI Tree Creation Errors"); } // Important, set ParseError to true ++m_Errors; /* TODO Gee: (MEGA) char buffer[512]; va_list args; // get arguments va_start(args, str); vsprintf(buffer, str.c_str(), args); va_end(args); */ /// g_nemLog(" %s", buffer); } /** * @callgraph */ void CGUI::LoadXMLFile(const string &Filename) { // Reset parse error // we can later check if this has increased m_Errors = 0; CXeromyces XeroFile; try { XeroFile.Load(Filename.c_str()); } - catch (PSERROR_Xeromyces& err) { - UNUSED(err); + catch (PSERROR_Xeromyces) + { // Fail silently return; } XMBElement node = XeroFile.getRoot(); // Check root element's (node) name so we know what kind of // data we'll be expecting std::string root_name = XeroFile.getElementString(node.getNodeName()); - if (root_name == "objects") + try { - Xeromyces_ReadRootObjects(node, &XeroFile); - // Re-cache all values so these gets cached too. - //UpdateResolution(); - } - else - if (root_name == "sprites") - { - Xeromyces_ReadRootSprites(node, &XeroFile); - } - else - if (root_name == "styles") - { - Xeromyces_ReadRootStyles(node, &XeroFile); - } - else - if (root_name == "setup") - { - Xeromyces_ReadRootSetup(node, &XeroFile); + if (root_name == "objects") + { + Xeromyces_ReadRootObjects(node, &XeroFile); + + // Re-cache all values so these gets cached too. + //UpdateResolution(); + } + else + if (root_name == "sprites") + { + Xeromyces_ReadRootSprites(node, &XeroFile); + } + else + if (root_name == "styles") + { + Xeromyces_ReadRootStyles(node, &XeroFile); + } + else + if (root_name == "setup") + { + Xeromyces_ReadRootSetup(node, &XeroFile); + } + else + { + // TODO Gee: Output in log + } } - else + catch (PSERROR_GUI) { - // TODO Gee: Output in log + LOG(ERROR, "Errors loading GUI file %s", Filename.c_str()); + return; } // Now report if any other errors occured if (m_Errors > 0) { /// g_console.submit("echo GUI Tree Creation Reports %d errors", m_Errors); } } //=================================================================== // XML Reading Xeromyces Specific Sub-Routines //=================================================================== void CGUI::Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile) { int el_script = pFile->getElementID("script"); // Iterate main children // they should all be or