Code Search for Developers
 
 
  

object.cpp from NeoEngineNG at Krugle


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

NeoEngineNG

NeoenEngine NG (Next Generation) is the evolution of neoengine one,it\'s a different development from NeoEngine2, it\'s a direct inherits from NeoEngine one.\n

Project homepage: http://sourceforge.net/projects/neoengineng
Programming language(s): C,C++
License: other

  Makefile.am
  SConscript
  area.cpp
  area.h
  attribute.cpp
  attribute.h
  base.cpp
  base.h
  borderarea.cpp
  borderarea.h
  button.cpp
  button.h
  chunktype.h
  coord.cpp
  coord.h
  core.cpp
  core.h
  dll.cpp
  editbox.cpp
  editbox.h
  msg.cpp
  msg.h
  msghook.cpp
  msghook.h
  neowtk-static.dev
  neowtk.cbp
  neowtk.dev
  neowtk.dsp
  neowtk.layout
  neowtk.vcproj
  object.cpp
  object.h
  textarea.cpp
  textarea.h
  widgetlib.cpp
  widgetlib.h