Show object.cpp syntax highlighted
/***************************************************************************
object.cpp - Base object class for windowing toolkit
-------------------
begin : Sun Nov 17 2002
copyright : (C) 2002 by Mattias Jansson
email : mattias@realityrift.com
***************************************************************************
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the NeoEngine, NeoWTK, object.cpp
The Initial Developer of the Original Code is Mattias Jansson.
Portions created by Mattias Jansson are Copyright (C) 2002
Reality Rift Studios. All Rights Reserved.
***************************************************************************/
#include "object.h"
#include "msg.h"
#include "msghook.h"
#include "attribute.h"
#include <neoengine/input.h>
#include <neoengine/logstream.h>
#ifdef HAVE_NEOCHUNKIO
# include "widgetlib.h"
# include <neochunkio/stdstring.h>
using namespace NeoChunkIO;
#endif
using namespace std;
using namespace NeoEngine;
namespace NeoWTK
{
/*! Counter for object IDs */
unsigned int g_uiObjectIDCounter = 0;
Object::Object( Object *pkParent, Object *pkObject, bool bDuplicateChildren ) :
RenderEntity(),
InputEntity( pkParent ),
InputGroup( 0 )
{
m_pkParent = pkParent;
m_eLastMouse = OUTSIDE;
m_uiID = ++g_uiObjectIDCounter;
if( pkObject )
{
if( bDuplicateChildren )
{
vector<Object*>::iterator ppkChild = pkObject->m_vpkChildren.begin();
vector<Object*>::iterator ppkEnd = pkObject->m_vpkChildren.end();
for( ; ppkChild != ppkEnd; ++ppkChild )
(*ppkChild)->Duplicate( this, 0 );
}
SetPosition( pkObject->GetPosition() );
SetSize( pkObject->GetSize() );
SetName( pkObject->GetName() );
// FIXME: Duplicate hooks?
}
if( m_pkParent )
{
m_pkParent->AttachObject( this );
UpdateWorldCache();
}
}
Object::~Object()
{
while( m_vpkChildren.size() )
delete m_vpkChildren[0];
if( m_pkParent )
m_pkParent->DetachObject( this ), m_pkParent = 0;
vector<MsgHook*>::iterator ppkHook = m_vpkMsgHook.begin();
vector<MsgHook*>::iterator ppkEnd = m_vpkMsgHook.end();
for( ; ppkHook != ppkEnd; ++ppkHook )
delete( *ppkHook );
}
void Object::AttachObject( Object *pkObject )
{
if( !pkObject )
return;
vector<Object*>::iterator ppkChild = m_vpkChildren.begin();
vector<Object*>::iterator ppkEnd = m_vpkChildren.end();
for( ; ppkChild != ppkEnd; ++ppkChild )
if( *ppkChild == pkObject )
return;
if( pkObject->m_pkParent )
{
Object *pkOldParent = pkObject->m_pkParent;
pkObject->m_pkParent = this;
if( pkOldParent != this )
pkOldParent->DetachObject( pkObject );
}
else
pkObject->m_pkParent = this;
pkObject->UpdateWorldCache( true );
m_vpkChildren.push_back( pkObject );
}
void Object::DetachObject( Object *pkObject )
{
vector<Object*>::iterator ppkChild = m_vpkChildren.begin();
vector<Object*>::iterator ppkEnd = m_vpkChildren.end();
for( ; ppkChild != ppkEnd; ++ppkChild )
if( *ppkChild == pkObject )
{
m_vpkChildren.erase( ppkChild );
return;
}
}
void Object::AttachToObject( Object *pkParent )
{
//Will call DetachObject on old parent
if( pkParent )
pkParent->AttachObject( this );
else if( m_pkParent )
{
m_pkParent->DetachObject( this );
UpdateWorldCache( true );
}
m_pkParent = pkParent;
}
void Object::UpdateWorldCache( bool bRecurse )
{
if( m_pkParent )
m_kWorldPos = m_pkParent->GetWorldPosition() + m_kPos;
else
m_kWorldPos = m_kPos;
if( bRecurse )
{
vector<Object*>::iterator ppkChild = m_vpkChildren.begin();
vector<Object*>::iterator ppkEnd = m_vpkChildren.end();
for( ; ppkChild != ppkEnd; ++ppkChild )
(*ppkChild)->UpdateWorldCache( true );
}
}
const Coord &Object::GetPosition() const
{
return m_kPos;
}
const Coord &Object::GetWorldPosition()
{
return m_kWorldPos;
}
const Coord &Object::GetSize() const
{
return m_kSize;
}
const Coord &Object::SetPosition( const Coord &rkPos )
{
if( rkPos != m_kPos )
{
m_kPos = rkPos;
UpdateWorldCache( true );
Msg *pkMsg = new Msg( Msg::MOVE, this, 0 );
ProcessMsg( pkMsg );
delete pkMsg;
}
return m_kPos;
}
const Coord &Object::SetSize( const Coord &rkSize )
{
if( rkSize != m_kSize )
{
m_kSize = rkSize;
Msg *pkMsg = new Msg( Msg::RESIZE, this, 0 );
ProcessMsg( pkMsg );
delete pkMsg;
}
return m_kSize;
}
void Object::SetName( const HashString &rstrName )
{
m_strName = rstrName;
}
const HashString &Object::GetName() const
{
return m_strName;
}
Object *Object::GetParent()
{
return m_pkParent;
}
Object *Object::GetRoot()
{
return( m_pkParent ? m_pkParent->GetRoot() : this );
}
Object *Object::GetObject( const std::string &rstrName )
{
string::size_type iPath = 0;
string strFirst = rstrName.substr( 0, ( iPath = rstrName.find_first_of( '.' ) ) );
string strLast = ( iPath != string::npos ) ? rstrName.substr( iPath + 1, string::npos ) : "";
if( strFirst == "parent" )
return( strLast.length() ? GetParent()->GetObject( strLast ) : GetParent() );
else if( strFirst == "root" )
return( strLast.length() ? GetRoot()->GetObject( strLast ) : GetRoot() );
vector<Object*>::iterator ppkChild = m_vpkChildren.begin();
vector<Object*>::iterator ppkEnd = m_vpkChildren.end();
HashString strName = strFirst;
for( ; ppkChild != ppkEnd; ++ppkChild )
if( (*ppkChild)->GetName() == strName )
{
if( strLast.length() )
return (*ppkChild)->GetObject( strLast );
else
return( *ppkChild );
}
return 0;
}
const vector< Object* > &Object::GetChildObjects() const
{
return m_vpkChildren;
}
bool Object::Render( Frustum *pkFrustum, bool bForce )
{
if( !RenderEntity::Render( pkFrustum, bForce ) )
return false;
vector<Object*>::iterator ppkChild = m_vpkChildren.begin();
vector<Object*>::iterator ppkEnd = m_vpkChildren.end();
for( ; ppkChild != ppkEnd; ++ppkChild )
if( (*ppkChild)->IsActive() )
(*ppkChild)->Render( pkFrustum );
return true;
}
unsigned int Object::ProcessMsg( Msg *pkMsg )
{
bool bRecurse = true;
#ifdef NEOWTK_DEBUG
neolog << LogLevel( DEBUG ) << "[wtk] object [" << GetName() << "] got message [" << pkMsg->GetIDAsString() << "]" << endl;
#endif
if( pkMsg->m_eID == Msg::MOUSE_MOVE )
{
Coord kMouse( pkMsg->m_uiData[0], pkMsg->m_uiData[1] );
HITTESTRESULT eCurMouse = HitTest( kMouse );
if( ( m_eLastMouse == OUTSIDE ) && ( eCurMouse != OUTSIDE ) )
{
//Send mouse enter message
Msg *pkNewMsg = new Msg( Msg::MOUSE_ENTER, this, 0 );
pkNewMsg->m_uiData[0] = kMouse.x;
pkNewMsg->m_uiData[1] = kMouse.y;
ProcessMsg( pkNewMsg );
delete pkNewMsg;
//Check if mouse buttons pressed
int iButtonMask = pkMsg->m_uiData[2];
if( iButtonMask )
{
for( int iButton = 0; iButton < 8; ++iButton )
{
if( iButtonMask & ( 1 << iButton ) )
{
//Setting receiver will cause message to not be sent to children
pkNewMsg = new Msg( Msg::MOUSE_DOWN, this, this );
pkNewMsg->m_uiData[0] = iButton;
pkNewMsg->m_uiData[1] = 1;
pkNewMsg->m_uiData[2] = kMouse.x;
pkNewMsg->m_uiData[3] = kMouse.y;
ProcessMsg( pkNewMsg );
delete pkNewMsg;
}
}
}
}
else if( ( m_eLastMouse != OUTSIDE ) && ( eCurMouse == OUTSIDE ) )
{
//Send mouse leave message
Msg *pkNewMsg = new Msg( Msg::MOUSE_LEAVE, this, 0 );
pkNewMsg->m_uiData[0] = kMouse.x;
pkNewMsg->m_uiData[1] = kMouse.y;
ProcessMsg( pkNewMsg );
delete pkNewMsg;
//Check if mouse buttons pressed
int iButtonMask = pkMsg->m_uiData[2];
if( iButtonMask )
{
for( int iButton = 0; iButton < 8; ++iButton )
{
if( iButtonMask & ( 1 << iButton ) )
{
//Setting receiver will cause message to not be sent to children
pkNewMsg = new Msg( Msg::MOUSE_UP, this, this );
pkNewMsg->m_uiData[0] = iButton;
pkNewMsg->m_uiData[1] = 1;
pkNewMsg->m_uiData[2] = kMouse.x;
pkNewMsg->m_uiData[3] = kMouse.y;
ProcessMsg( pkNewMsg );
delete pkNewMsg;
}
}
}
}
m_eLastMouse = eCurMouse;
// ####################################################################################
// ### We now require child objects to be completely contained within parent object ###
// ### we can disregard and not send mouse move messages if coordinate outside area ###
// ####################################################################################
if( eCurMouse == OUTSIDE )
bRecurse = false;
}
if( ( pkMsg->m_eID == Msg::MOUSE_DOWN ) || ( pkMsg->m_eID == Msg::MOUSE_UP ) )
{
Coord kMouse( pkMsg->m_uiData[2], pkMsg->m_uiData[3] );
HITTESTRESULT eCurMouse = HitTest( kMouse );
if( eCurMouse == OUTSIDE )
bRecurse = false;
}
//Process hooks
{
vector<MsgHook*>::iterator ppkHook = m_vpkMsgHook.begin();
vector<MsgHook*>::iterator ppkEnd = m_vpkMsgHook.end();
for( ; ppkHook != ppkEnd; ++ppkHook )
{
if( (*ppkHook)->IsHook( pkMsg ) )
(*ppkHook)->m_pkProcessor->ProcessHook( *ppkHook, pkMsg );
}
}
//Send message on to child nodes
if( bRecurse )
{
vector<Object*>::iterator ppkChild = m_vpkChildren.begin();
vector<Object*>::iterator ppkEnd = m_vpkChildren.end();
for( ; ppkChild != ppkEnd; ++ppkChild )
if( (*ppkChild)->IsActive() && ( !pkMsg->m_pkReceiver || ( pkMsg->m_pkReceiver == (*ppkChild) ) ) )
(*ppkChild)->ProcessMsg( pkMsg );
}
return 0;
}
void Object::AddHook( MsgHook *pkMsgHook )
{
vector<MsgHook*>::iterator ppkHook = m_vpkMsgHook.begin();
vector<MsgHook*>::iterator ppkEnd = m_vpkMsgHook.end();
for( ; ppkHook != ppkEnd; ++ppkHook )
if( *ppkHook == pkMsgHook )
return;
m_vpkMsgHook.push_back( pkMsgHook );
}
void Object::RemoveHook( MsgHook *pkMsgHook )
{
vector<MsgHook*>::iterator ppkHook = m_vpkMsgHook.begin();
vector<MsgHook*>::iterator ppkEnd = m_vpkMsgHook.end();
for( ; ppkHook != ppkEnd; ++ppkHook )
if( *ppkHook == pkMsgHook )
{
m_vpkMsgHook.erase( ppkHook );
return;
}
}
void Object::Input( const InputEvent *pkEvent )
{
if( pkEvent->m_iType == IE_MOUSEMOVE )
{
// ####################################################################################
// ### We now require child objects to be completely contained within parent object ###
// ### we can disregard and not send mouse move messages if coordinate outside area ###
// ####################################################################################
Coord kMouse( pkEvent->m_aArgs[0].m_iData, pkEvent->m_aArgs[1].m_iData );
HITTESTRESULT eCurMouse = HitTest( kMouse );
if( ( eCurMouse != OUTSIDE ) || ( m_eLastMouse != OUTSIDE ) )
{
//Send mouse move message
Msg *pkMsg = new Msg( Msg::MOUSE_MOVE, this, 0 );
pkMsg->m_uiData[0] = pkEvent->m_aArgs[0].m_iData; // Coord x
pkMsg->m_uiData[1] = pkEvent->m_aArgs[1].m_iData; // Coord y
pkMsg->m_uiData[2] = pkEvent->m_aArgs[3].m_iData; // Button mask
ProcessMsg( pkMsg );
delete pkMsg;
}
}
else if( pkEvent->m_iType == IE_MOUSEDOWN )
{
Coord kMouse( pkEvent->m_aArgs[1].m_iData, pkEvent->m_aArgs[2].m_iData );
HITTESTRESULT eCurMouse = HitTest( kMouse );
if( eCurMouse != OUTSIDE )
{
//Send mouse down message
Msg *pkMsg = new Msg( Msg::MOUSE_DOWN, this, 0 );
//Store mouse button ID and indicate we are inside
pkMsg->m_uiData[0] = pkEvent->m_aArgs[0].m_iData;
pkMsg->m_uiData[1] = 0;
pkMsg->m_uiData[2] = kMouse.x;
pkMsg->m_uiData[3] = kMouse.y;
ProcessMsg( pkMsg );
delete pkMsg;
}
}
else if( pkEvent->m_iType == IE_MOUSEUP )
{
Coord kMouse( pkEvent->m_aArgs[1].m_iData, pkEvent->m_aArgs[2].m_iData );
HITTESTRESULT eCurMouse = HitTest( kMouse );
if( eCurMouse != OUTSIDE )
{
//Send mouse up message
Msg *pkMsg = new Msg( Msg::MOUSE_UP, this, 0 );
//Store mouse button ID and indicate we are inside
pkMsg->m_uiData[0] = pkEvent->m_aArgs[0].m_iData;
pkMsg->m_uiData[1] = 0;
pkMsg->m_uiData[2] = kMouse.x;
pkMsg->m_uiData[3] = kMouse.y;
ProcessMsg( pkMsg );
delete pkMsg;
}
}
else if( pkEvent->m_iType == IE_KEYDOWN )
{
Msg *pkMsg = new Msg( Msg::KEYBOARD_DOWN, this, 0 );
pkMsg->m_uiData[0] = pkEvent->m_aArgs[0].m_iData;
pkMsg->m_uiData[1] = pkEvent->m_aArgs[1].m_iData;
ProcessMsg( pkMsg );
delete pkMsg;
}
InputGroup::Distribute( pkEvent );
}
Object::HITTESTRESULT Object::HitTest( const Coord &rkCoord )
{
const Coord &rkWorld = GetWorldPosition();
if( ( rkCoord.x < rkWorld.x ) || ( rkCoord.y < rkWorld.y ) ||
( rkCoord.x > ( rkWorld.x + m_kSize.x ) ) || ( rkCoord.y > ( rkWorld.y + m_kSize.y ) ) )
return OUTSIDE;
if( ( rkCoord.x == rkWorld.x ) || ( rkCoord.y == rkWorld.y ) ||
( rkCoord.x == ( rkWorld.x + m_kSize.x ) ) || ( rkCoord.y == ( rkWorld.y + m_kSize.y ) ) )
return EDGE;
return INSIDE;
}
void Object::Update( float fDeltaTime )
{
vector<Object*>::iterator ppkChild = m_vpkChildren.begin();
vector<Object*>::iterator ppkEnd = m_vpkChildren.end();
for( ; ppkChild != ppkEnd; ++ppkChild )
if( (*ppkChild)->IsActive() )
(*ppkChild)->Update( fDeltaTime );
}
Object *Object::Duplicate( Object *pkParent, Object *pkObject )
{
if( !pkObject )
{
//Constructor will duplicate stuff for us
pkObject = new Object( pkParent, this );
}
else
{
//Duplicate children that does not exist
vector<Object*>::iterator ppkChild = m_vpkChildren.begin();
vector<Object*>::iterator ppkEnd = m_vpkChildren.end();
for( ; ppkChild != ppkEnd; ++ppkChild )
{
if( !pkObject->GetObject( (*ppkChild)->GetName() ) )
(*ppkChild)->Duplicate( pkObject, 0 );
}
pkObject->SetPosition( m_kPos );
pkObject->SetSize( m_kSize );
pkObject->SetName( m_strName );
}
return pkObject;
}
void Object::SetAttribute( const AttributeBase &rkAttribute )
{
if( rkAttribute.m_strName == "name" )
{
const Attribute< string > *pkData = dynamic_cast< const Attribute< string >* >( &rkAttribute );
if( pkData )
{
SetName( pkData->m_kData );
return;
}
}
else if( rkAttribute.m_strName == "size" )
{
const Attribute< Coord > *pkData = dynamic_cast< const Attribute< Coord >* >( &rkAttribute );
if( pkData )
{
SetSize( pkData->m_kData );
return;
}
}
else if( rkAttribute.m_strName == "pos" )
{
const Attribute< Coord > *pkData = dynamic_cast< const Attribute< Coord >* >( &rkAttribute );
if( pkData )
{
SetPosition( pkData->m_kData );
return;
}
}
neolog << LogLevel( WARNING ) << "[Object::SetAttribute] unknown or invalid attribute [" << rkAttribute.m_strName << "]" << endl;
}
#ifdef HAVE_NEOCHUNKIO
int ObjectChunk::ParseData( unsigned int uiFlags, FileManager *pkFileManager )
{
Chunk *pkChunk = 0;
if( !m_pkObject )
m_pkObject = new Object( 0 );
if( ( pkChunk = FindChunk( "ref", NeoChunkIO::ChunkType::STRING, -1 ) ) && WidgetLibrary::s_pkRefRoot )
{
//Locate reference object
Object *pkRef = WidgetLibrary::s_pkRefRoot->GetObject( dynamic_cast< StringChunk* >( pkChunk )->m_strData );
if( pkRef )
pkRef->Duplicate( 0, m_pkObject );
else
neolog << LogLevel( WARNING ) << "[ObjectChunk::ParseData] invalid ref to ["
<< dynamic_cast< StringChunk* >( pkChunk )->m_strData <<"]" << endl;
}
if( ( pkChunk = FindChunk( "pos", NeoWTK::ChunkType::COORD, -1 ) ) )
m_pkObject->SetPosition( dynamic_cast< CoordChunk* >( pkChunk )->m_kCoord );
if( ( pkChunk = FindChunk( "size", NeoWTK::ChunkType::COORD, -1 ) ) )
m_pkObject->SetSize( dynamic_cast< CoordChunk* >( pkChunk )->m_kCoord );
if( ( pkChunk = FindChunk( "name", NeoChunkIO::ChunkType::STRING, -1 ) ) )
m_pkObject->SetName( dynamic_cast< StringChunk* >( pkChunk )->m_strData );
vector< Chunk* > vpkHooks;
if( ( FindChunks( "", ChunkType::MSGHOOK, &vpkHooks, false, true ) ) > 0 )
{
vector< Chunk* >::iterator ppkHook = vpkHooks.begin();
vector< Chunk* >::iterator ppkHookEnd = vpkHooks.end();
for( ; ppkHook != ppkHookEnd; ++ppkHook )
{
MsgHookChunk *pkHook = dynamic_cast< MsgHookChunk* >( *ppkHook );
m_pkObject->AddHook( pkHook->m_pkHook );
pkHook->m_pkHook = 0;
}
}
if( !( uiFlags & NOCHILDREN ) )
AttachChildren();
if( !( uiFlags & NOATTRIBUTES ) )
SetAttributes();
return 1;
}
void ObjectChunk::AttachChildren()
{
//Locate child objects
vector< Chunk* > vpkChildren;
if( FindChunks( "", NeoWTK::ChunkType::OBJECT, &vpkChildren, false, true ) > 0 )
{
vector< Chunk* >::iterator ppkChunk = vpkChildren.begin();
vector< Chunk* >::iterator ppkChunkEnd = vpkChildren.end();
for( ; ppkChunk != ppkChunkEnd; ++ppkChunk )
{
ObjectChunk *pkObjChunk = dynamic_cast< ObjectChunk* >( *ppkChunk );
if( pkObjChunk->m_pkObject )
m_pkObject->AttachObject( pkObjChunk->m_pkObject );
pkObjChunk->m_pkObject = 0;
}
}
}
void ObjectChunk::SetAttributes()
{
vector< Chunk* > vpkAttributes;
if( FindChunks( "", NeoWTK::ChunkType::ATTRIBUTE, &vpkAttributes, false, true ) )
{
vector< Chunk* >::iterator ppkChunk = vpkAttributes.begin();
vector< Chunk* >::iterator ppkChunkEnd = vpkAttributes.end();
for( ; ppkChunk != ppkChunkEnd; ++ppkChunk )
{
AttributeChunk *pkAttribute = dynamic_cast< AttributeChunk* >( *ppkChunk );
if( pkAttribute && pkAttribute->m_pkData )
{
//Get path to object
string::size_type iPath = 0;
string strFull = pkAttribute->m_pkData->m_strName.m_strData;
string strPath = strFull.substr( 0, ( iPath = strFull.find_last_of( '.' ) ) );
string strName = ( iPath != string::npos ) ? strFull.substr( iPath + 1, string::npos ) : "";
if( strName.length() )
{
Object *pkPathObject = m_pkObject->GetObject( strPath );
if( pkPathObject )
{
pkAttribute->m_pkData->m_strName = strName;
pkPathObject->SetAttribute( *pkAttribute->m_pkData );
pkAttribute->m_pkData->m_strName = strFull;
}
}
else
m_pkObject->SetAttribute( *pkAttribute->m_pkData );
}
}
}
}
#endif
}; /*! namespace NeoWTK */
See more files for this project here