/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/08
// RCS-ID:      $Id: res_templ.cpp,v 1.18 2001/04/21 11:46:10 georgetasker Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "minimal.cpp"
    #pragma interface "minimal.cpp"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#include "res_templ.h"
#include "ui_lookrenderer.h"

#include <stdlib.h> // atoi(..)
#include "myutils.h"

#include "wx/colordlg.h"
#include "wx/fontdlg.h"

/***** Implementation for class ResGroupData *****/

ResGroupData::ResGroupData()

	: mpGroupInfo( NULL )
{}

ResGroupData::~ResGroupData()
{
	if ( mpGroupInfo )
	{
		mpGroupInfo->DestroyValues( GetData() );

		mpGroupInfo = NULL;
	}
}

void ResGroupData::SetGroupInfo( wxDMAttrGroupInfo* pInfo )
{
	if ( mpGroupInfo ) 
		
		CleanUp();

	// data-array is prepared immediately after setting meta-info

	mpGroupInfo = pInfo;

    size_t i;
	for( i = 0; i != pInfo->Count(); ++i )

		mAttrData.Add( NULL );
	
	mpGroupInfo->InitValues( GetData() );
}

wxDMAttrGroupInfo* ResGroupData::GetGroupInfo()
{
	return mpGroupInfo;
}


void* ResGroupData::GetData()
{
	return (void*)this;
}

void ResGroupData::CopyDataTo( ResGroupData& other )
{
	if ( mpGroupInfo == NULL )
	
		other.CleanUp();
	else
	{
		other.SetGroupInfo( mpGroupInfo );

		mpGroupInfo->CopyValues( other.GetData(), this->GetData() );
	}
}

/*** protected methods ***/

void ResGroupData::CleanUp()
{
	if ( !mpGroupInfo ) return;

	mpGroupInfo->DestroyValues( GetData() );

	mAttrData.Clear();

	mpGroupInfo = NULL;
}

/***** Implementation for class ResIntAttrInfo *****/

wxString ResIntAttrInfo::GetStringValue( void* pGroupData )
{
	int i = (int)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	return IntToStr( i );
}

void ResIntAttrInfo::SetStringValue( const wxString& value, void* pGroupData )
{
	int& i = (int&)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	i = atoi( value.c_str() );
}

bool ResIntAttrInfo::ValidateStringValue( const wxString& value, const wxString* pErrMsg )
{
	return TRUE; // TBD:: checks
}

void ResIntAttrInfo::InitValue( void* pGroupData )
{
	int& i = (int&)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	i = atoi( mDefaultValue.c_str() );
}

void ResIntAttrInfo::CopyValue( void* pDestData, void* pSrcData )
{
	int src = (int&)
		((ResGroupData*)pSrcData)->mAttrData[ (size_t)GetUserData() ];

	int& dest = (int&)
		((ResGroupData*)pDestData)->mAttrData[ (size_t)GetUserData() ];

	dest = src;
}

/***** Implementation for class ResBoolAttrInfo *****/

wxString ResBoolAttrInfo::GetStringValue( void* pGroupData )
{
	bool b = 
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ] != 0;

	return b ? "1" : "0" ;
}

void ResBoolAttrInfo::SetStringValue( const wxString& value, void* pGroupData )
{
	bool& b = (bool&)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	b = value == "1";
}

bool ResBoolAttrInfo::ValidateStringValue( const wxString& value, const wxString* pErrMsg )
{
	return TRUE; // TBD:: checks
}

void ResBoolAttrInfo::InitValue( void* pGroupData )
{
	bool& b = (bool&)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	b = mDefaultValue == "1";
}

void ResBoolAttrInfo::CopyValue( void* pDestData, void* pSrcData )
{
	bool src = (bool&)
		((ResGroupData*)pSrcData)->mAttrData[ (size_t)GetUserData() ];

	bool& dest = (bool&)
		((ResGroupData*)pDestData)->mAttrData[ (size_t)GetUserData() ];

	dest = src;
}

bool ResBoolAttrInfo::ShownAsBooleanField()
{
	return TRUE;
}

/***** Implementation for class ResStringAttrInfo *****/

IMPLEMENT_CLASS( ResStringAttrInfo, wxDMAttrInfo )

wxString ResStringAttrInfo::GetStringValue( void* pGroupData )
{
	wxString* pStr = (wxString*)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	return *pStr;
}

void ResStringAttrInfo::SetStringValue( const wxString& value, void* pGroupData )
{
	wxString* pStr = (wxString*)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	*pStr = value;
}

bool ResStringAttrInfo::ValidateStringValue( const wxString& value, const wxString* pErrMsg )
{
	return TRUE; // TBD:: checks
}

void ResStringAttrInfo::InitValue( void* pGroupData )
{
	wxString*& pStr = (wxString*&)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	pStr = new wxString( mDefaultValue );
}

void ResStringAttrInfo::DestroyValue( void* pGroupData )
{
	wxString* pStr = (wxString*)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	delete pStr;
}

void ResStringAttrInfo::CopyValue( void* pDestData, void* pSrcData )
{
	wxString* pSrcStr = (wxString*&)
		((ResGroupData*)pSrcData)->mAttrData[ (size_t)GetUserData() ];

	wxString*& pDestStr = (wxString*&)
		((ResGroupData*)pDestData)->mAttrData[ (size_t)GetUserData() ];

	*pDestStr = *pSrcStr;
}

/***** Implementation for class ResColorAttrInfo *****/

IMPLEMENT_CLASS( ResColorAttrInfo, wxDMAttrInfo )

wxString ResColorAttrInfo::GetStringValue( void* pGroupData )
{
	int rgb = (int)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	if ( rgb == 0x1 )

		return "";
	else
		return RGBIntToStr( rgb );
}

void ResColorAttrInfo::SetStringValue( const wxString& value, void* pGroupData )
{
	int& rgb = (int&)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	if ( value.Length() == 0 )

		rgb = 0x1;
	else
		rgb = StrToRGbInt( value );
}

bool ResColorAttrInfo::ValidateStringValue( const wxString& value, const wxString* pErrMsg )
{
	return TRUE; // TBD::
}

bool ResColorAttrInfo::HasValueSelectionDialog()
{
	return TRUE;
}

bool ResColorAttrInfo::ShowValueSelectonDialog( void* pGroupData )
{
	int& rgb = (int&)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	wxColor c( ::GetIntRedValue(rgb), ::GetIntGreenValue(rgb), ::GetIntBlueValue(rgb) );
	wxColourData cdata;	cdata.SetColour( c );
	cdata.SetChooseFull(TRUE);
  
	wxColourDialog dlg( NULL, &cdata );
	dlg.Center( wxBOTH );

	if ( dlg.ShowModal() == wxID_OK )
	{
		c = dlg.GetColourData().GetColour();

		rgb = MakeRGBInt( c.Red(), c.Green(), c.Blue() );

		return TRUE;
	}
	else
		return FALSE;
}

void ResColorAttrInfo::InitValue( void* pGroupData )
{
	SetStringValue( mDefaultValue, pGroupData );
}

void ResColorAttrInfo::CopyValue( void* pDestData, void* pSrcData )
{
	int srcRgb = (int&)
		((ResGroupData*)pSrcData)->mAttrData[ (size_t)GetUserData() ];

	int& destRgb = (int&)
		((ResGroupData*)pDestData)->mAttrData[ (size_t)GetUserData() ];

	destRgb = srcRgb;
}

wxColour ResColorAttrInfo::GetColorValue( void* pGroupData )
{
	int rgb = (int)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	return wxColour( GetIntRedValue( rgb ),
					 GetIntGreenValue( rgb ),
					 GetIntBlueValue( rgb )  );
}

/***** Implementation for class ResFontAttrInfo *****/

IMPLEMENT_CLASS( ResFontAttrInfo, ResStringAttrInfo )

bool ResFontAttrInfo::HasValueSelectionDialog()
{
	return TRUE;
}

bool ResFontAttrInfo::ShowValueSelectonDialog( void* pGroupData )
{
	wxString s = GetStringValue( pGroupData );

	wxFont* fnt = CreateFontObject( pGroupData );

	wxFontData data;
	data.SetInitialFont( *fnt );

	wxFontDialog dialog( NULL, &data);
	dialog.Center( wxBOTH );

	if ( dialog.ShowModal() == wxID_OK )
	{
		delete fnt;
		wxFontData retData = dialog.GetFontData();
		wxFont selFont = retData.GetChosenFont();

		s = selFont.GetFaceName() + ",";
		s += IntToStr( selFont.GetPointSize() ) + ",";

		int style = selFont.GetStyle();
		switch( style )
		{
			case wxSLANT  : s += "slant"; break;
			case wxITALIC : s += "italic"; break;
			default		  : s += "normal"; break;
		}

		s += ",";

		int weight = selFont.GetWeight();
		switch( weight )
		{
			case wxLIGHT : s += "light"; break;
			case wxBOLD  : s += "bold"; break;
			default		 : s += "normal"; break;
		}

		if ( selFont.GetUnderlined() )

			s += ",1,";
		else
			s += ",0,";


		int family = selFont.GetFamily();
		switch( family )
		{
			case wxDECORATIVE : s += "decorative"; break;
			case wxROMAN      : s += "roman"; break;
			case wxSCRIPT     : s += "script"; break;
			case wxSWISS      : s += "swiss"; break;
			case wxMODERN     : s += "modern"; break;
			default		      : s += "default"; break;
		}

		SetStringValue( s, pGroupData );

		return TRUE;
	}

	delete fnt;
	return FALSE;
}

static wxString my_get_next_word( char*& cur )
{
	char* start = cur;

	while( *cur && *cur != ',' )
		++cur;

	char ch = *cur;
	*cur = '\0';

	wxString word = start;

	*cur = ch;
	++cur; // skip ','

	return word;
}

wxFont* ResFontAttrInfo::CreateFontObject( void* pGroupData )
{
	// FOR NOW:: simplified code of wxXmlResourceHandler::GetFont(...)

	wxString s = GetStringValue( pGroupData );

	if ( s.Length() == 0 )

		return new wxFont();

	char* cur = (char*)(const char*)s.c_str();

	wxString face      = my_get_next_word( cur );
	wxString size      = my_get_next_word( cur );
	wxString style     = my_get_next_word( cur );
	wxString weight    = my_get_next_word( cur );
	wxString underline = my_get_next_word( cur );
	wxString family    = my_get_next_word( cur );

    long isize = size.Length() ? StrToInt( size ) : 12;

    int istyle = wxNORMAL, iweight = wxNORMAL;  

    if (style == wxT("italic")) 
		istyle = wxITALIC;
    else 
	if (style == wxT("slant")) 
		istyle = wxSLANT;

    if (weight == wxT("bold")) 
		iweight = wxBOLD;
    else 
	if (weight == wxT("light")) 
		iweight = wxLIGHT;

    int ifamily = wxDEFAULT;
    
	if (family == wxT("decorative"))  ifamily = wxDECORATIVE;
    else if (family == wxT("roman"))  ifamily = wxROMAN;
    else if (family == wxT("script")) ifamily = wxSCRIPT;
    else if (family == wxT("swiss"))  ifamily = wxSWISS;
    else if (family == wxT("modern")) ifamily = wxMODERN;

    bool underlined = "underline" == "1";

    return new wxFont( isize, ifamily, istyle, iweight, underlined, face );
}

void ResFontAttrInfo::SetValuesToRecord( Record& rec, void* pGroupData )
{
	wxString s = GetStringValue( pGroupData );
	char* cur = (char*)(const char*)s.c_str();

	rec.RemoveAllFields();
	rec.SetName( "font" );
	rec.AddRecord( "face",      my_get_next_word( cur ) );
	rec.AddRecord( "size",      my_get_next_word( cur ) );
	rec.AddRecord( "style",     my_get_next_word( cur ) );
	rec.AddRecord( "weight",    my_get_next_word( cur ) );
	rec.AddRecord( "underline", my_get_next_word( cur ) );
	rec.AddRecord( "family",    my_get_next_word( cur ) );
}

void ResFontAttrInfo::GetValuesFromRecord( Record& rec, void* pGroupData )
{
	wxString s = rec["face"]      + "," +
		         rec["size"]      + "," +
				 rec["style"]     + "," +
				 rec["weight"]    + "," +
				 rec["underline"] + "," +
				 rec["family"];

	SetStringValue( s, pGroupData );
}

/***** Implementation for class ResRestrictedStringAttrInfo *****/

ResRestrictedStringAttrInfo::ResRestrictedStringAttrInfo()

	: mShownAsBool( FALSE )
{}

wxString ResRestrictedStringAttrInfo::GetStringValue( void* pGroupData )
{
	int index = (int)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	return mValues[index];
}

void ResRestrictedStringAttrInfo::SetStringValue( const wxString& value, void* pGroupData )
{
	int& index = (int&)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

    size_t i;
	for( i = 0; i != mValues.GetCount(); ++i )

		if ( mValues[i] == value )
		{
			index = (int)i;
			break;
		}
}

bool ResRestrictedStringAttrInfo::ValidateStringValue( const wxString& value, const wxString* pErrMsg )
{
	return TRUE; // TBD:: check
}

bool ResRestrictedStringAttrInfo::HasRestrictedValues( void* pGroupData )
{
	return TRUE;
}

bool ResRestrictedStringAttrInfo::SetRestrictedValues( wxStringList& list )
{
	mValues = list;

	return TRUE;
}

void ResRestrictedStringAttrInfo::GetRestrictedValues( wxStringList& toLst, void* pGroupData )
{
	toLst = mValues;
}

void ResRestrictedStringAttrInfo::InitValue( void* pGroupData )
{
	SetStringValue( mDefaultValue, pGroupData );
}

void ResRestrictedStringAttrInfo::CopyValue( void* pDestData, void* pSrcData )
{
	int srcIndex = (int&)
		((ResGroupData*)pSrcData)->mAttrData[ (size_t)GetUserData() ];

	int& destIndex = (int&)
		((ResGroupData*)pDestData)->mAttrData[ (size_t)GetUserData() ];

	destIndex = srcIndex;
}

bool ResRestrictedStringAttrInfo::ShownAsBooleanField()
{
	return mShownAsBool;
}

void ResRestrictedStringAttrInfo::SetShownAsBoolean( bool on )
{
	mShownAsBool = on;
}

/***** Implementation for class ResStringListAttrInfo *****/

IMPLEMENT_CLASS( ResStringListAttrInfo, wxDMAttrInfo )

wxString ResStringListAttrInfo::GetStringValue( void* pGroupData )
{
	return "";
}

void ResStringListAttrInfo::SetStringValue( const wxString& value, void* pGroupData )
{
	// nothing
}

bool ResStringListAttrInfo::ValidateStringValue( const wxString& value, const wxString* pErrMsg )
{
	return TRUE;
}

bool ResStringListAttrInfo::HasMultipleValues( void* pGroupData )
{
	return TRUE;
}

void ResStringListAttrInfo::SetMultipleValues( wxStringList& fromList, void* pGroupData )
{
	wxStringList* pLst = (wxStringList*)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	*pLst = fromList;
}

void ResStringListAttrInfo::GetMultipleValues( wxStringList& toList, void* pGroupData )
{
	wxStringList* pLst = (wxStringList*)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	toList = *pLst;
}

void ResStringListAttrInfo::InitValue( void* pGroupData )
{
	wxStringList*& pLst = (wxStringList*&)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	pLst = new wxStringList();
}

void ResStringListAttrInfo::DestroyValue( void* pGroupData )
{
	wxStringList* pLst = (wxStringList*)
		((ResGroupData*)pGroupData)->mAttrData[ (size_t)GetUserData() ];

	delete pLst;
}

void ResStringListAttrInfo::CopyValue( void* pDestData, void* pSrcData )
{
	wxStringList* pSrcLst = (wxStringList*&)
		((ResGroupData*)pSrcData)->mAttrData[ (size_t)GetUserData() ];

	wxStringList*& pDestLst = (wxStringList*&)
		((ResGroupData*)pDestData)->mAttrData[ (size_t)GetUserData() ];

	*pDestLst = *pSrcLst;
}

/***** Implementation for class ResPropInfo *****/

ResPropInfo::ResPropInfo()

	: mType(-1),
	  mVisibileOnly( FALSE )
{}

ResPropInfo::~ResPropInfo()
{}

ResPropSetInfo::~ResPropSetInfo()
{
    size_t i;
	for( i = 0; i != mProps.Count(); ++i )

		if ( mProps[i]->mType != RESPROP_PROPSET )
		
			delete mProps[i];
}

/***** Implementation for class ResPropInfo *****/

bool ResPropInfo::HasRestrictedValue( const wxString& value )
{
    size_t i;
	for( i = 0; i != mRestrValues.GetCount(); ++i )

		if ( mRestrValues[i] == value )

			return TRUE;

	return FALSE;
}

wxString ResPropInfo::TranslateToDispValue( const wxString& value )
{
    size_t i;
	for( i = 0; i != mRestrValues.GetCount(); ++i )

		if ( mRestrValues[i] == value )

			return mRestrDispValues[i];

	return "";
}

wxString ResPropInfo::TranslateFromDispValue( const wxString& dispValue )
{
    size_t i;
	for( i = 0; i != mRestrDispValues.GetCount(); ++i )

		if ( mRestrDispValues[i] == dispValue )

			return mRestrValues[i];

	return "";
}

/***** Implementation for class WidgetResTemplate *****/

WidgetResTemplate::WidgetResTemplate()

	: mTextPropName( "label" )
{}

WidgetResTemplate::~WidgetResTemplate()
{
}

/***** Implementation for class ResTemplateManager *****/

ResTemplateManager::ResTemplateManager()

	: mClassHash( wxKEY_STRING )
{
}

ResTemplateManager::~ResTemplateManager()
{
	WX_CLEAR_ARRAY( mWTemplList );
	WX_CLEAR_ARRAY( mPropSets );
}

bool ResTemplateManager::InitGroupData( ResPropSetInfo& set, ResGroupData& data )
{
	Record dummy;

	SetGroupValuesRecursive( data, set, dummy, TRUE );

	return TRUE;
}

bool ResTemplateManager::LoadGroupData( ResPropSetInfo& set, Record& fromRec, ResGroupData& data )
{
	SetGroupValuesRecursive( data, set, fromRec, FALSE );

	return TRUE;
}

bool ResTemplateManager::StoreGroupData( ResPropSetInfo& set, Record& toRec, ResGroupData& data )
{
	GetGroupValuesRecursive( data, set, toRec );

	return TRUE;
}

bool ResTemplateManager::IsKnownClass( const wxString& wClass )
{
	return mClassHash.Get( wClass ) != NULL;
}

WidgetResTemplate* ResTemplateManager::FindWidgetTemplateForClass( const wxString& wClass )
{
	WidgetResTemplate* pTempl =  (WidgetResTemplate*)mClassHash.Get( wClass );

	if ( !pTempl )

		return &mUnknownWidgetTempl;
	else
		return pTempl;
}

#define FLAG_IS_SET( rec, flag, expr )			\
	{											\
		FieldBase* fld = rec.FindField( flag ); \
		if ( fld && fld->GetValue() == "true" ) \
			expr = TRUE;						\
		else									\
			expr = FALSE;						\
	}											

#define SET_PROP_VALUE( var, prop, dflt ) \
										  \
	if ( pFld = rec.FindField( prop ) )   \
										  \
		var = pFld->GetValue();			  \
	else								  \
	    var = dflt;						  \


WidgetResTemplate* ResTemplateManager::DoLoadWidget( Record& rec, const wxString& actualClass, Record* pParentRec )
{
	bool isUnknown = rec.GetName() == "unknown_widget";

	WidgetResTemplate* pTempl = 
		( isUnknown ) ? &mUnknownWidgetTempl 
					  : new WidgetResTemplate();

	pTempl->mTag = rec[ "tag" ];

	pTempl->mCanHaveChildren = FALSE;
	pTempl->mInheritsBg = TRUE;
	pTempl->mInheritsFg = TRUE;
	pTempl->mInheritsFont = TRUE;

	FLAG_IS_SET( rec, "inheritsBg",        pTempl->mInheritsBg )
	FLAG_IS_SET( rec, "inheritsFg",        pTempl->mInheritsFg )
	FLAG_IS_SET( rec, "inheritsFont",	   pTempl->mInheritsFont )
	FLAG_IS_SET( rec, "can_have_children", pTempl->mCanHaveChildren )

	pTempl->mDisplayNameExpr = rec[ "display_name_expr" ];

	// FIXME:: optimize all these slow FindField()'s

	FieldBase* pFld = NULL;

	SET_PROP_VALUE( pTempl->mPreviewerClass, "previewer_class", "WidgetGlyph" )
	SET_PROP_VALUE( pTempl->mNodeClass,		 "node_class",		"WidgetNode" )
	SET_PROP_VALUE( pTempl->mWidgetClass,	 "widget_class",	"wxWindow" )

	SET_PROP_VALUE( pTempl->mBasedOnClass,	 "based_on_class", pTempl->mWidgetClass )
	SET_PROP_VALUE( pTempl->mTextPropName,	 "text_property",	"" )
	SET_PROP_VALUE( pTempl->mAlignPropName,	 "align_property",	"" )

	if ( pFld = rec.FindField( "toolbar_icon" ) )
	{
		pTempl->mToolBarBitmap = 
			wxUILookRenderer::RecordToBitmap( (Record*)pFld );

		int w = pTempl->mToolBarBitmap.GetWidth();
		int h = pTempl->mToolBarBitmap.GetHeight();
	}

	pTempl->mpWidgetAttrGroup = new wxDMAttrGroupInfo();
	pTempl->mpExtraAttrGroup = NULL;

	LoadPropSetInfo( rec, pTempl->mPropSet, rec.GetRecordByName( "props" ) );

	// append extra properties from given parent record, if any

	if ( pParentRec && ( pFld = pParentRec->FindField( "props" ) ) )
	
		LoadPropSetInfo( *pParentRec, pTempl->mPropSet, *(Record*)pFld );

	int curIdx = 0;

	if ( pFld = rec.FindField( "extra_props" ) )
	{
		LoadPropSetInfo( rec, pTempl->mExtraPropSet, *(Record*)pFld );

		pTempl->mExtraPropsTagName = rec[ "extra_props_tag_name" ];

		pTempl->mpExtraAttrGroup = new wxDMAttrGroupInfo();

		CreateGroupRecursive( *pTempl->mpExtraAttrGroup , pTempl->mExtraPropSet, curIdx );

		pTempl->mpExtraAttrGroup->SetName( rec[ "extra_props_name" ] );

	}

	curIdx = 0;

	CreateGroupRecursive( *pTempl->mpWidgetAttrGroup, pTempl->mPropSet, curIdx );

	pTempl->mpWidgetAttrGroup->SetName( "Properties" );

	if ( actualClass.Length() )
	
		pTempl->mWidgetClass = actualClass;

	if ( !isUnknown )
	{
		mWTemplList.Add( pTempl );
		mClassHash.Put( pTempl->mWidgetClass, (wxObject*)pTempl );
	}

	return pTempl;
}

bool ResTemplateManager::LoadResourceTemplates( const wxString& fname )
{
	WX_CLEAR_ARRAY( mWTemplList );
	WX_CLEAR_ARRAY( mPropSets );

	TextFileRecordSet rset( fname );

	rset.MoveFirst();

    // If we immediately detect EOF, then the config file was either missing
    // or damaged.  If it was missing, then it was created with just an 
    // EOF character in it by the TextFileRecordSet() call above.
    if (rset.IsEof())
        return FALSE;

	while( !rset.IsEof() )
	{
		Record& rec = rset.GetRecord();

		if ( rec.GetName() == "alias" )
		{
			int savedPos = rset.GetRecordPos();

			bool found = rset.FindFirst( "widget_class", rec[ "referenced_class" ] );

			wxString wClass = rec[ "widget_class" ];

			wxASSERT_MSG( found, "Cannot resolve alias " +  wClass );

			DoLoadWidget( rset.GetRecord(), wClass, &rec );

			rset.SetRecordPos( savedPos );
		}
		else
		if ( rec.GetName() == "prop_set" )
		{
			ResPropSetInfo* pSet = new ResPropSetInfo();

			mPropSets.Add( pSet );

			LoadPropSetInfo( rec, *pSet, rec.GetRecordByName( "props" ) );
		}
		else
		if ( rec.GetName() == "widget" || rec.GetName() == "unknown_widget" )
		{
			DoLoadWidget( rec, "", NULL );
		}
		else
		{
			int avoidCompilerWarning = 0;
			wxASSERT_MSG(avoidCompilerWarning, "Unknown tag : " + rec.GetName() ); // DBG:: bogus record name
		}

		rset.MoveNext();
	}

	return TRUE;
}

WidgetResTemplateListT& ResTemplateManager::GetTemplates()
{
	return mWTemplList;
}

WidgetResTemplate& ResTemplateManager::GetUnknownWidgetTemplate()
{
	return mUnknownWidgetTempl;
}

/*** protected methods ***/

void ResTemplateManager::CreateGroupRecursive( wxDMAttrGroupInfo& group, 
											   ResPropSetInfo& set, 
											   int& curIndex )
{
    size_t i;
	for( i = 0; i != set.mProps.Count(); ++i )
	{
		ResPropInfo& pr = *set.mProps[i];

		wxDMAttrInfo* pAttr = NULL; int type = pr.GetType();

		if ( type == RESPROP_NUMBER )
							
			pAttr = new ResIntAttrInfo();
		else
		if ( type == RESPROP_BOOLEAN )
							
			pAttr = new ResBoolAttrInfo();
		else
		if ( type == RESPROP_STRING )
		
			pAttr = new ResStringAttrInfo();
		else
		if ( type == RESPROP_COLOR )

			pAttr = new ResColorAttrInfo();
		else
		if ( type == RESPROP_FONT )

			pAttr = new ResFontAttrInfo();
		else
		if ( type == RESPROP_RESTRICTED_STRING ||
			 type == RESPROP_RESTRICTED_STRING_AS_BOOLEAN )
		{
			pAttr = new ResRestrictedStringAttrInfo();

			pAttr->SetRestrictedValues( pr.mRestrDispValues );

			if ( type == RESPROP_RESTRICTED_STRING_AS_BOOLEAN )

				((ResRestrictedStringAttrInfo*)pAttr)->SetShownAsBoolean( TRUE );
		}
		else
		if ( type == RESPROP_STRING_LIST )
		
			pAttr = new ResStringListAttrInfo();
		else
		if ( type == RESPROP_PROPSET )
		{
			/* recursive invocation */

			ResPropSetInfo* pNested = (ResPropSetInfo*)&pr;
			CreateGroupRecursive( group, *pNested, curIndex );
		}
		else
		if ( type == RESPORP_CONCAT_PROPS )
		{
			// nothing to do with these during creation
		}
		else
		{
			int avoidCompilerWarning = 0;
			wxASSERT(avoidCompilerWarning); // DBG:: bogus type of property
		}

		if ( pAttr )
		{
			pAttr->SetUserData( (void*)curIndex );
			pAttr->SetName( pr.mName );
			pAttr->SetDisplayName( pr.mDispName );
			pAttr->SetDefaultValue( pr.mDefaultValue );
			group.Add( pAttr );

			++curIndex;
		}
	}
}

void ResTemplateManager::SetGroupValuesRecursive( ResGroupData& groupData,
												  ResPropSetInfo& set,
												  Record& rec, bool init )
{
	if ( set.mGluedWith != "" )
	{
		wxString value = ""; StrListT lst;

		if ( rec.FindField( set.mName ) )

			value = rec[ set.mName ];

		ExtractConcatenatedValues( value, lst, set.mGluedWith[0u] );

        size_t i;
		for( i = 0; i != set.mProps.Count(); ++i )
		{
			ResPropInfo& pr = *set.mProps[i];

			wxDMAttrInfo* pAttr = groupData.GetGroupInfo()->GetAttrByName( pr.mName ); 

			if ( i < (unsigned int)lst.Count() )
				pAttr->SetStringValue( lst[i], &groupData );
			else
				pAttr->SetStringValue( pr.mDefaultValue, &groupData );
		}

		return;
	}

    size_t i;
	for( i = 0; i != set.mProps.Count(); ++i )
	{
		ResPropInfo& pr = *set.mProps[i];

		wxDMAttrInfo* pAttr = groupData.GetGroupInfo()->GetAttrByName( pr.mName ); 
		int type = pr.GetType();
		void* pValue = NULL;

		if ( !pr.mVisibileOnly &&
			 ( type == RESPROP_NUMBER  || 
			   type == RESPROP_STRING  ||
			   type == RESPROP_BOOLEAN || 
			   type == RESPROP_COLOR   ||
			   type == RESPROP_FONT    ||
			   type == RESPROP_RESTRICTED_STRING ||
			   type == RESPROP_RESTRICTED_STRING_AS_BOOLEAN )
		   )
		{					
			wxString value = init ? pr.mInitValue : pr.mDefaultValue;

			FieldBase* pField = rec.FindField( pr.mName );

			if ( pField )
			{
				if ( pAttr->IsKindOf( CLASSINFO( ResFontAttrInfo ) ) &&
					 pField->GetType() == FT_RECORD )
				{
					// <font> is actually a composite field...

					((ResFontAttrInfo*)pAttr)->
						GetValuesFromRecord( *(Record*)pField, &groupData );

					value = pAttr->GetStringValue( &groupData );
				}
				else
					value = pField->GetValue();
			}

			if ( type == RESPROP_RESTRICTED_STRING ||
				 type == RESPROP_RESTRICTED_STRING_AS_BOOLEAN )
			
				value = pr.TranslateToDispValue( value );

			pAttr->SetStringValue( value, &groupData );
		}
		else
		if ( type == RESPROP_STRING_LIST )
		{
			wxStringList lst;

            size_t i;
			for( i = 0; i != rec.GetFieldCount(); ++i )
			{
				FieldBase* pField = &rec.GetFieldByIndex(i);

				if ( pField->GetType() == FT_RECORD &&
					 pField->GetName() == "item" )
				
					lst.Add( ((Record*)pField)->GetStringValue() );
			}

			if ( init )

				ExtractConcatenatedValues( pr.mInitValue, lst );

			pAttr->SetMultipleValues( lst, &groupData );
		}
		else
		if ( type == RESPROP_PROPSET )
		{
			/* recursive invocation */

			ResPropSetInfo* pNested = (ResPropSetInfo*)&pr;

			SetGroupValuesRecursive( groupData, *pNested, rec, init );
		}
		else
		if ( type == RESPORP_CONCAT_PROPS )
		{
			ResPropSetInfo* pNested = (ResPropSetInfo*)&pr;

			// redistribut values concatenated

			wxString concValue = init ? pr.mInitValue : pr.mDefaultValue;

			FieldBase* pField = rec.FindField( pr.mName );

			if ( pField )

				concValue = pField->GetValue();

			StrListT lst; ExtractConcatenatedValues( concValue, lst );

            size_t n;
			for( n = 0; n != (unsigned int)lst.Count(); ++n )

				lst[n].Trim( TRUE ).Trim( FALSE );

            size_t i;
			for( i = 0; i != (unsigned int)pNested->mProps.Count(); ++i )
			{	 
				ResPropInfo& prop = *pNested->mProps[i];

                size_t k;
				for( k = 0; k != (unsigned int)lst.Count(); ++k )
				
					if ( prop.HasRestrictedValue( lst[k] ) )
					{
						size_t idx = groupData.GetGroupInfo()->GetAttrIndex( prop.mName );
						wxASSERT( idx != (size_t)(-1) ); // DBG::

						wxString displayValue = prop.TranslateToDispValue( lst[k] );

						groupData.GetGroupInfo()->GetAttr( idx )->
							SetStringValue( displayValue, &groupData );

						break;
					}
			}
		}
		else
		if ( !pr.mVisibileOnly )
		{
			int avoidCompilerWarning = 0;
			wxASSERT(avoidCompilerWarning); // DBG:: bogus type of property
		}
	}
}

void ResTemplateManager::GetGroupValuesRecursive( ResGroupData& groupData,
												  ResPropSetInfo& set,
												  Record& rec )
{
	if ( set.mGluedWith != "" )
	{
		wxString value;

        size_t i;
		for( i = 0; i != set.mProps.Count(); ++i )
		{
			ResPropInfo& pr = *set.mProps[i];

			wxDMAttrInfo* pAttr = groupData.GetGroupInfo()->GetAttrByName( pr.mName ); 

			if ( i != 0 ) value += set.mGluedWith;

			value += pAttr->GetStringValue( &groupData );
		}

		rec.AddRecord( set.mName, value );

		return;
	}

    size_t i;
	for( i = 0; i != set.mProps.Count(); ++i )
	{
		ResPropInfo& pr = *set.mProps[i];

		wxDMAttrInfo* pAttr = groupData.GetGroupInfo()->GetAttrByName( pr.mName ); 
		int type = pr.GetType();
		void* pValue = NULL;

		if ( type == RESPROP_NUMBER  || 
			 type == RESPROP_BOOLEAN || 
			 type == RESPROP_STRING  ||
			 type == RESPROP_COLOR   ||
			 type == RESPROP_FONT    ||
			 type == RESPROP_RESTRICTED_STRING ||
			 type == RESPROP_RESTRICTED_STRING_AS_BOOLEAN )
		{
			wxString value = pAttr->GetStringValue( &groupData );

			if ( type == RESPROP_RESTRICTED_STRING ||
				 type == RESPROP_RESTRICTED_STRING_AS_BOOLEAN )

				value = pr.TranslateFromDispValue( value );

			if ( pr.mVisibileOnly == FALSE )
			{
				// FIXME ME:: FOR NOW:: according to wxXml-res format, only "name" goes as 
				//						attribute values, all the rest goes as child-element nodes

				if ( type == RESPROP_STRING &&  
					 ( pr.mName == "name" ||
					   pr.mName == "subclass" )
				   )
				{
					if ( value.Length() )

						rec.AddStringField( pr.mName, value );
				}
				else
				if ( value != pr.mDefaultValue )
				{
					if ( pAttr->IsKindOf( CLASSINFO( ResFontAttrInfo ) ) )
					{
						Record* pFontRec = new Record();

						((ResFontAttrInfo*)pAttr)->SetValuesToRecord( *pFontRec, &groupData );

						rec.AddField( pFontRec );
					}
					else
						rec.AddRecord( pr.mName, value );
				}
			}

		}
		if ( type == RESPROP_STRING_LIST && pr.mVisibileOnly == FALSE )
		{
			// HACK:: hardcoded

			wxStringList lst;
			pAttr->GetMultipleValues( lst, &groupData );

            size_t i;
			for( i = 0; i != lst.GetCount(); ++i )
			
				rec.AddRecord( "item", lst[i] );
		}
		else
		if ( type == RESPROP_PROPSET )
		{
			ResPropSetInfo* pNested = (ResPropSetInfo*)&pr;
			GetGroupValuesRecursive( groupData, *pNested, rec );
		}
		else
		if ( type == RESPORP_CONCAT_PROPS )
		{
			ResPropSetInfo* pNested = (ResPropSetInfo*)&pr;

			// collect values distributed among multiple properties

			wxString concValue;
			size_t nValues = 0;

			// TBD:: default value

            size_t i;
			for( i = 0; i != pNested->mProps.Count(); ++i )
			{	 
				ResPropInfo& prop = *pNested->mProps[i];

				wxDMAttrInfo* pAttr = groupData.GetGroupInfo()->GetAttrByName( prop.mName );

				wxString value = pAttr->GetStringValue( &groupData );
				value = prop.TranslateFromDispValue( value );

				if ( pAttr && value != "" )
				{
					if ( nValues > 0 ) concValue += "|";

					concValue += value;

					++nValues;
				}
			}

			if ( concValue != "" )

				rec.AddRecord( pr.mName, concValue );
		}
	}
}

void ResTemplateManager::LoadPropSetInfo( Record& rec, ResPropSetInfo& set, 
										  Record& propsRec )
{
	wxASSERT_MSG( &propsRec != NULL, "'props' field not found in widget descriptor!" ); 

	set.mName = rec[ "name" ];
	set.mType = RESPROP_PROPSET;

	if ( rec.FindField( "glued_with" ) )

		set.mGluedWith = rec[ "glued_with" ];

	for( size_t i = 0; i != propsRec.GetFieldCount(); ++i )
	{
		FieldBase* pField = &propsRec.GetFieldByIndex(i);

		if ( pField->GetType() == FT_RECORD )
		{
			ResPropInfo* pProp = new ResPropInfo();

			pProp->mName = pField->GetName();
			pProp->mDispName = pProp->mName;

			// referes to atomic property

			Record& r = *(Record*)pField;

			wxString type = r[0]; size_t idx = 0;

			// first, property type declarator expected

			if ( type == "NUMBER" ) pProp->mType = RESPROP_NUMBER;
				else
			if ( type == "BOOLEAN" ) pProp->mType = RESPROP_BOOLEAN;
				else
			if ( type == "STRING" ) pProp->mType = RESPROP_STRING;
				else
			if ( type == "COLOR") pProp->mType = RESPROP_COLOR;
				else
			if ( type == "FONT") pProp->mType = RESPROP_FONT;
				else
			if ( type == "STRING_LIST" ) pProp->mType = RESPROP_STRING_LIST;
				else
			if ( type == "FLAG" || type == "BOOLEAN_FLAG"  ) 
			{
				// list of retricted display values expected

				pProp->mType = RESPROP_RESTRICTED_STRING;

				if ( type == "BOOLEAN_FLAG" )

					pProp->mType = RESPROP_RESTRICTED_STRING_AS_BOOLEAN;

				++idx;
				StrListT lst;
				r.GetRecordByIndex(idx).CopyStringValues( lst );

				::StrListToStringList( pProp->mRestrDispValues, lst );

				if ( r.FieldAt(2).GetType() == FT_RECORD )
				{
					// optional list of actual values if present

					++idx; lst.Clear();
					r.GetRecordByIndex(idx).CopyStringValues( lst );

					::StrListToStringList( pProp->mRestrValues, lst );
				}
				else
					// otherwise actual values are equal to display values

					pProp->mRestrValues = pProp->mRestrDispValues;
			}

			++idx;

			// default value of the property, if any

			if ( idx < r.GetFieldCount() )
			{
				pProp->mDefaultValue = r[idx]; 
				pProp->mInitValue = r[idx];
				++idx;
			}

			// _VISIBLE_ONLY_ flag, if any

			for( size_t j = idx; j < r.GetFieldCount(); ++j )
			{
				StringValue& sv = *(StringValue*)&r.GetFieldByIndex(j);
				wxString value = sv.GetValue().Lower();

				if ( sv.GetName() == "visibleOnly" && value == "true" )
				
					pProp->mVisibileOnly = TRUE;
				else
				if ( sv.GetName() == "initValue" )

					pProp->mInitValue = sv.GetValue();
				else
				if ( sv.GetName() == "displayName" )

					pProp->mDispName = sv.GetValue();
			}

			set.mProps.Add( pProp );
		}
		else
		{
			StringValue& strField = *(StringValue*)pField;

			if ( strField.GetName() == "" )
			{
				// referes to the nested property-set

                size_t k;
				for( k = 0; k != mPropSets.Count(); ++k )

					if ( mPropSets[k]->mName == strField.GetValue() )
					{
						set.mProps.Add( mPropSets[k] );
						break;
					}
			}
			else
			{
				// referes to concatenated properties

				ResPropSetInfo* pConcSet = new ResPropSetInfo();
				pConcSet->mName = strField.GetName();
				pConcSet->mType = RESPORP_CONCAT_PROPS;

				wxString concProps = strField.GetValue(); StrListT lst;

				ExtractConcatenatedValues( concProps, lst );

                size_t n;
				for( n = 0; n != (unsigned int)lst.Count(); ++n )
				{
					wxString name = lst[n];

					ResPropInfo* pFound = SearchPropertyTree( set, name );

					if ( pFound )

						pConcSet->mProps.Add( pFound );
				}

				set.mProps.Add( pConcSet );
			}
		}
	}
}

ResPropInfo* ResTemplateManager::SearchPropertyTree( ResPropSetInfo& set, 
													 const wxString& propName )
{
    size_t i;
	for( i = 0; i != set.mProps.Count(); ++i )
	{
		ResPropInfo* pInfo = set.mProps[i];

		if ( pInfo->mType == RESPROP_PROPSET )
		{
			ResPropInfo* pFound = SearchPropertyTree( *(ResPropSetInfo*)pInfo, propName );

			if ( pFound ) return pFound;
		}
		else
		if ( pInfo->mType == RESPORP_CONCAT_PROPS )
		{
			// nothing
		}
		else
		{
			// atomic property

			if ( pInfo->mName == propName )

				return pInfo;
		}
	}

	return NULL;
}
