{
    switch (type) {
    case HTTPBody::Element::Type::Data:
    case HTTPBody::Element::Type::File:
    case HTTPBody::Element::Type::Blob:
        return true;
    }

    return false;
}

/*
 *
 */

#include "config.h"
#include "SessionState.h"

#include "WebCoreArgumentCoders.h"

namespace WebKit {

bool isValidEnum(WebCore::ShouldOpenExternalURLsPolicy policy)
{
    switch (policy) {
    case WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow:
    case WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes:
    case WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow:
        return true;
    }
    return false;
}

void HTTPBody::Element::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder.encodeEnum(type);
    encoder << data;
    encoder << filePath;
    encoder << fileStart;
    encoder << fileLength;
    encoder << expectedFileModificationTime;
    encoder << blobURLString;
}

static bool isValidEnum(HTTPBody::Element::Type type)

bool HTTPBody::Element::decode(IPC::ArgumentDecoder& decoder, Element& result)
{
    if (!decoder.decodeEnum(result.type) || !isValidEnum(result.type))
        return false;
    if (!decoder.decode(result.data))
        return false;
    if (!decoder.decode(result.filePath))
        return false;
    if (!decoder.decode(result.fileStart))
        return false;
    if (!decoder.decode(result.fileLength))
        return false;
    if (!decoder.decode(result.expectedFileModificationTime))
        return false;
    if (!decoder.decode(result.blobURLString))
        return false;

    return true;
}

void HTTPBody::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << contentType;
    encoder << elements;
}

bool HTTPBody::decode(IPC::ArgumentDecoder& decoder, HTTPBody& result)
{
    if (!decoder.decode(result.contentType))
        return false;
    if (!decoder.decode(result.elements))
        return false;

    return true;
}

void FrameState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << urlString;
    encoder << originalURLString;
    encoder << referrer;
    encoder << target;

    encoder << documentState;
    encoder << stateObjectData;

    encoder << documentSequenceNumber;
    encoder << itemSequenceNumber;

    encoder << scrollPosition;
    encoder << pageScaleFactor;

    encoder << httpBody;

#if PLATFORM(IOS)
    encoder << exposedContentRect;
    encoder << unobscuredContentRect;
    encoder << minimumLayoutSizeInScrollViewCoordinates;
    encoder << contentSize;
    encoder << scaleIsInitial;
#endif

    encoder << children;
}

bool FrameState::decode(IPC::ArgumentDecoder& decoder, FrameState& result)
{
    if (!decoder.decode(result.urlString))
        return false;
    if (!decoder.decode(result.originalURLString))
        return false;
    if (!decoder.decode(result.referrer))
        return false;
    if (!decoder.decode(result.target))
        return false;

    if (!decoder.decode(result.documentState))
        return false;
    if (!decoder.decode(result.stateObjectData))
        return false;

    if (!decoder.decode(result.documentSequenceNumber))
        return false;
    if (!decoder.decode(result.itemSequenceNumber))
        return false;

    if (!decoder.decode(result.scrollPosition))
        return false;
    if (!decoder.decode(result.pageScaleFactor))
        return false;

    if (!decoder.decode(result.httpBody))
        return false;

#if PLATFORM(IOS)
    if (!decoder.decode(result.exposedContentRect))
        return false;
    if (!decoder.decode(result.unobscuredContentRect))
        return false;
    if (!decoder.decode(result.minimumLayoutSizeInScrollViewCoordinates))
        return false;
    if (!decoder.decode(result.contentSize))
        return false;
    if (!decoder.decode(result.scaleIsInitial))
        return false;
#endif

    if (!decoder.decode(result.children))
        return false;

    return true;
}

void PageState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << title;
    encoder << mainFrameState;
    encoder.encodeEnum(shouldOpenExternalURLsPolicy);
}

bool PageState::decode(IPC::ArgumentDecoder& decoder, PageState& result)
{
    if (!decoder.decode(result.title))
        return false;
    if (!decoder.decode(result.mainFrameState))
        return false;
    if (!decoder.decodeEnum(result.shouldOpenExternalURLsPolicy) || !isValidEnum(result.shouldOpenExternalURLsPolicy))
        return false;

    return true;
}

void BackForwardListItemState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << identifier;
    encoder << pageState;
}

bool BackForwardListItemState::decode(IPC::ArgumentDecoder& decoder, BackForwardListItemState& result)
{
    if (!decoder.decode(result.identifier))
        return false;

    if (!decoder.decode(result.pageState))
        return false;

    return true;
}

void BackForwardListState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << items;
    encoder << currentIndex;
}

bool BackForwardListState::decode(IPC::ArgumentDecoder& decoder, BackForwardListState& result)
{
    if (!decoder.decode(result.items))
        return false;
    if (!decoder.decode(result.currentIndex))
        return false;

    return true;
}

} // namespace WebKit


/*
 *
 */

#include "config.h"
#include "SessionState.h"

#include "WebCoreArgumentCoders.h"

namespace WebKit {

bool isValidEnum(WebCore::ShouldOpenExternalURLsPolicy policy)
{
    switch (policy) {
    case WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow:
    case WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes:
    case WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow:
        return true;
    }
    return false;
}

void HTTPBody::Element::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder.encodeEnum(type);
    encoder << data;
    encoder << filePath;
    encoder << fileStart;
    encoder << fileLength;
    encoder << expectedFileModificationTime;
    encoder << blobURLString;
}

static bool isValidEnum(HTTPBody::Element::Type type)
{
    switch (type) {
    case HTTPBody::Element::Type::Data:
    case HTTPBody::Element::Type::File:
    case HTTPBody::Element::Type::Blob:
        return true;
    }

    return false;
}

bool HTTPBody::Element::decode(IPC::ArgumentDecoder& decoder, Element& result)
{
    if (!decoder.decodeEnum(result.type) || !isValidEnum(result.type))
        return false;
    if (!decoder.decode(result.data))
        return false;
    if (!decoder.decode(result.filePath))
        return false;
    if (!decoder.decode(result.fileStart))
        return false;
    if (!decoder.decode(result.fileLength))
        return false;
    if (!decoder.decode(result.expectedFileModificationTime))
        return false;
    if (!decoder.decode(result.blobURLString))
        return false;

    return true;
}

void HTTPBody::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << contentType;
    encoder << elements;
}

bool HTTPBody::decode(IPC::ArgumentDecoder& decoder, HTTPBody& result)
{
    if (!decoder.decode(result.contentType))
        return false;
    if (!decoder.decode(result.elements))
        return false;

    return true;
}

void FrameState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << urlString;
    encoder << originalURLString;
    encoder << referrer;
    encoder << target;

    encoder << documentState;
    encoder << stateObjectData;

    encoder << documentSequenceNumber;
    encoder << itemSequenceNumber;

    encoder << scrollPosition;
    encoder << pageScaleFactor;

    encoder << httpBody;

#if PLATFORM(IOS)
    encoder << exposedContentRect;
    encoder << unobscuredContentRect;
    encoder << minimumLayoutSizeInScrollViewCoordinates;
    encoder << contentSize;
    encoder << scaleIsInitial;
#endif

    encoder << children;
}

bool FrameState::decode(IPC::ArgumentDecoder& decoder, FrameState& result)
{
    if (!decoder.decode(result.urlString))
        return false;
    if (!decoder.decode(result.originalURLString))
        return false;
    if (!decoder.decode(result.referrer))
        return false;
    if (!decoder.decode(result.target))
        return false;

    if (!decoder.decode(result.documentState))
        return false;
    if (!decoder.decode(result.stateObjectData))
        return false;

    if (!decoder.decode(result.documentSequenceNumber))
        return false;
    if (!decoder.decode(result.itemSequenceNumber))
        return false;

    if (!decoder.decode(result.scrollPosition))
        return false;
    if (!decoder.decode(result.pageScaleFactor))
        return false;

    if (!decoder.decode(result.httpBody))
        return false;

#if PLATFORM(IOS)
    if (!decoder.decode(result.exposedContentRect))
        return false;
    if (!decoder.decode(result.unobscuredContentRect))
        return false;
    if (!decoder.decode(result.minimumLayoutSizeInScrollViewCoordinates))
        return false;
    if (!decoder.decode(result.contentSize))
        return false;
    if (!decoder.decode(result.scaleIsInitial))
        return false;
#endif

    if (!decoder.decode(result.children))
        return false;

    return true;
}

void PageState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << title;
    encoder << mainFrameState;
    encoder.encodeEnum(shouldOpenExternalURLsPolicy);
}

bool PageState::decode(IPC::ArgumentDecoder& decoder, PageState& result)
{
    if (!decoder.decode(result.title))
        return false;
    if (!decoder.decode(result.mainFrameState))
        return false;
    if (!decoder.decodeEnum(result.shouldOpenExternalURLsPolicy) || !isValidEnum(result.shouldOpenExternalURLsPolicy))
        return false;

    ret

}

void BackForwardListItemState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << identifier;
    encoder << pageState;
}

bool BackForwardListItemState::decode(IPC::ArgumentDecoder& decoder, BackForwardListItemState& result)
{
    if (!decoder.decode(result.identifier))
        return false;

    if (!decoder.decode(result.pageState))
        return false;

    return true;
}

void BackForwardListState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << items;
    encoder << currentIndex;
}

bool BackForwardListState::decode(IPC::ArgumentDecoder& decoder, BackForwardListState& result)
{
    if (!decoder.decode(result.items))
        return false;
    if (!decoder.decode(result.currentIndex))
        return false;

    return true;
}

} // namespace WebKit


switch (type) {
    case HTTPBody::Element::Type::Data:
    case HTTPBody::Element::Type::File:
    case HTTPBody::Element::Type::Blob:
        return true;
    }

/*
 *
 */

#include "config.h"
#include "SessionState.h"

#include "WebCoreArgumentCoders.h"

namespace WebKit {

bool isValidEnum(WebCore::ShouldOpenExternalURLsPolicy policy)
{
    switch (policy) {
    case WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow:
    case WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes:
    case WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow:
        return true;
    }
    return false;
}

void HTTPBody::Element::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder.encodeEnum(type);
    encoder << data;
    encoder << filePath;
    encoder << fileStart;
    encoder << fileLength;
    encoder << expectedFileModificationTime;
    encoder << blobURLString;
}

static bool isValidEnum(HTTPBody::Element::Type type)
{

    return false;
}

bool HTTPBody::Element::decode(IPC::ArgumentDecoder& decoder, Element& result)
{
    if (!decoder.decodeEnum(result.type) || !isValidEnum(result.type))
        return false;
    if (!decoder.decode(result.data))
        return false;
    if (!decoder.decode(result.filePath))
        return false;
    if (!decoder.decode(result.fileStart))
        return false;
    if (!decoder.decode(result.fileLength))
        return false;
    if (!decoder.decode(result.expectedFileModificationTime))
        return false;
    if (!decoder.decode(result.blobURLString))
        return false;

    return true;
}

void HTTPBody::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << contentType;
    encoder << elements;
}

bool HTTPBody::decode(IPC::ArgumentDecoder& decoder, HTTPBody& result)
{
    if (!decoder.decode(result.contentType))
        return false;
    if (!decoder.decode(result.elements))
        return false;

    return true;
}

void FrameState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << urlString;
    encoder << originalURLString;
    encoder << referrer;
    encoder << target;

    encoder << documentState;
    encoder << stateObjectData;

    encoder << documentSequenceNumber;
    encoder << itemSequenceNumber;

    encoder << scrollPosition;
    encoder << pageScaleFactor;

    encoder << httpBody;

#if PLATFORM(IOS)
    encoder << exposedContentRect;
    encoder << unobscuredContentRect;
    encoder << minimumLayoutSizeInScrollViewCoordinates;
    encoder << contentSize;
    encoder << scaleIsInitial;
#endif

    encoder << children;
}

bool FrameState::decode(IPC::ArgumentDecoder& decoder, FrameState& result)
{
    if (!decoder.decode(result.urlString))
        return false;
    if (!decoder.decode(result.originalURLString))
        return false;
    if (!decoder.decode(result.referrer))
        return false;
    if (!decoder.decode(result.target))
        return false;

    if (!decoder.decode(result.documentState))
        return false;
    if (!decoder.decode(result.stateObjectData))
        return false;

    if (!decoder.decode(result.documentSequenceNumber))
        return false;
    if (!decoder.decode(result.itemSequenceNumber))
        return false;

    if (!decoder.decode(result.scrollPosition))
        return false;
    if (!decoder.decode(result.pageScaleFactor))
        return false;

    if (!decoder.decode(result.httpBody))
        return false;

#if PLATFORM(IOS)
    if (!decoder.decode(result.exposedContentRect))
        return false;
    if (!decoder.decode(result.unobscuredContentRect))
        return false;
    if (!decoder.decode(result.minimumLayoutSizeInScrollViewCoordinates))
        return false;
    if (!decoder.decode(result.contentSize))
        return false;
    if (!decoder.decode(result.scaleIsInitial))
        return false;
#endif

    if (!decoder.decode(result.children))
        return false;

    return true;
}

void PageState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << title;
    encoder << mainFrameState;
    encoder.encodeEnum(shouldOpenExternalURLsPolicy);
}

bool PageState::decode(IPC::ArgumentDecoder& decoder, PageState& result)
{
    if (!decoder.decode(result.title))
        return false;
    if (!decoder.decode(result.mainFrameState))
        return false;
    if (!decoder.decodeEnum(result.shouldOpenExternalURLsPolicy) || !isValidEnum(result.shouldOpenExternalURLsPolicy))
        return false;

    return true;
}

void BackForwardListItemState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << identifier;
    encoder << pageState;
}

bool BackForwardListItemState::decode(IPC::ArgumentDecoder& decoder, BackForwardListItemState& result)
{
    if (!decoder.decode(result.identifier))
        return false;

    if (!decoder.decode(result.pageState))
        return false;

    return true;
}

void BackForwardListState::encode(IPC::ArgumentEncoder& encoder) const
{
    encoder << items;
    encoder << currentIndex;
}

bool BackForwardListState::decode(IPC::ArgumentDecoder& decoder, BackForwardListState& result)
{
    if (!decoder.decode(result.items))
        return false;
    if (!decoder.decode(result.currentIndex))
        return false;

    return true;
}

} // namespace WebKit


#include <core/defines.h>
#include <gel/scripting/checksum.h>
#include <gel/scripting/script.h>
#include <gel/scripting/struct.h>
#include <gel/scripting/array.h>
#include <gel/scripting/vecpair.h>
#include <gfx/NxFontMan.h>
#include <gfx/NxFont.h>
#include <gfx/2D/ScreenElement2.h>
#include <gfx/2D/ScreenElemMan.h>
#include <gfx/2D/TextElement.h>
#include <gfx/2D/BlurEffect.h>
#include <gfx/2D/Window.h>

#define BLUR_EFFECT_ON 1

namespace Front
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextElement::CTextElement() :
	m_shadow_rgba(0, 0, 0, 128)
{
	//Ryan("I am a new CTextElement\n");
	m_font_checksum = 0;
	mp_font = NULL;
	mp_text = NULL;
	mp_blur_effect = NULL;
	m_use_shadow = false;
	create_text_instances(1);

	m_override_encoded_rgba = false;
	m_previous_override_rgba_state = false;

	SetType(CScreenElement::TYPE_TEXT_ELEMENT);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextElement::~CTextElement()
{
	DetachBlurEffect();
	destroy_text_instances();
	if (mp_text)
		delete mp_text;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetProperties(Script::CStruct *pProps)
{
	CScreenElement::SetProperties(pProps);

	uint32 font_crc;
	if (pProps->GetChecksum(CRCD(0x2f6bf72d,"font"), &font_crc))
		SetFont(font_crc);
	Dbg_MsgAssert(m_font_checksum, ("no font loaded"));

	const char *p_text;
	if (pProps->GetText(CRCD(0xc4745838,"text"), &p_text))
		SetText(p_text);

	Dbg_MsgAssert(m_id != Obj::CBaseManager::vNO_OBJECT_ID, ("what, no ID"));

	if (pProps->ContainsFlag(CRCD(0xd1653c6c,"blur_effect")))
		AttachBlurEffect();
	if (pProps->ContainsFlag(CRCD(0x4b73e4f8,"no_blur_effect")))
		DetachBlurEffect();

	Image::RGBA blur_rgba;
	if (resolve_rgba(pProps, CRCD(0x4f0e1182,"blur_rgba"), &blur_rgba) && mp_blur_effect)
		mp_blur_effect->SetRGBA(blur_rgba);

	Script::CPair shadow_offs;
	if (pProps->GetPair(CRCD(0x2a1fe0cc,"shadow_offs"), &shadow_offs))
	{
		SetShadowOff(shadow_offs.mX, shadow_offs.mY);
	}

	if (pProps->ContainsFlag(CRCD(0x8a897dd2,"shadow")))
		SetShadowState(true);
	if (pProps->ContainsFlag(CRCD(0x95b5b9a0,"no_shadow")))
		SetShadowState(false);

	resolve_rgba(pProps, CRCD(0x1e7bb1f5,"shadow_rgba"), &m_shadow_rgba);

	if ( pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ) )
	{
		Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
		m_previous_override_rgba_state = m_override_encoded_rgba;
	}
	if ( pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ) )
	{
		Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
		m_override_encoded_rgba = m_previous_override_rgba_state;
	}

	if (pProps->ContainsFlag(CRCD(0xb7686f92,"override_encoded_rgba")))
		m_override_encoded_rgba = true;
	if (pProps->ContainsFlag(CRCD(0xd25bc6cd,"dont_override_encoded_rgba")))
		m_override_encoded_rgba = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetMorph(Script::CStruct *pProps)
{
	if (mp_blur_effect)
	{
		mp_blur_effect->SetMorph(pProps);
	}

	CScreenElement::SetMorph(pProps);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetFont(uint32 font_checksum)
{
	m_font_checksum = font_checksum;
	mp_font = Nx::CFontManager::sGetFont(m_font_checksum);
	Dbg_MsgAssert(Nx::CFontManager::sTestFontLoaded(m_font_checksum), ("font %s isn't loaded", Script::FindChecksumName(m_font_checksum)));
	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetText(const char *pText)
{
	int new_length = strlen(pText) + 1;
	Dbg_MsgAssert(new_length < vMAX_TEXT_LENGTH, ("string too long %d", new_length));

	// if the old string is not big enough, then we need to allocate a new one
	// otherwize, overwrite it, as allocations are slow
	if (!mp_text || (int)(strlen(mp_text)) < new_length-1)		  // note, does not account for actual allocated size, but will work is string are same size
	{
		if (mp_text)
			delete mp_text;
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mp_text = new char[new_length];
		Mem::Manager::sHandle().PopContext();
	}
	// copy pText to mp_text, replacing '\m' tags with '\b' tags
	const char *p_in = pText;
	char *p_out = mp_text;
	while(*p_in)
	{
		if (*p_in == '\\' && *(p_in+1) == '\\')
		{
			// output "\\" tag unchanged
			*p_out++ = *p_in++;
			*p_out++ = *p_in++;
		}
		// ignore sticky space
		else if (*p_in == '\\' && *(p_in+1) == '_')
		{
			p_in++;
			p_in++;
			*p_out++ = ' ';
		}
		else if (*p_in == '\\' && *(p_in+1) == 'm')
		{
			*p_out++ = *p_in++;
			*p_out++ = 'b';
			p_in++;
			*p_out++ = Nx::CFontManager::sMapMetaCharacterToButton(p_in++);
		}
		else
		{
			*p_out++ = *p_in++;
		}
	}
	*p_out = '\0';

	if( mp_font )
	{
		float w, h;
		mp_font->QueryString(mp_text, w, h);	// width and height in pixels

		if (!(m_object_flags & vFORCED_DIMS))
		{
			SetDims(w, h);
		}
	}

	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CTextElement::GetLength()
{
	return strlen( mp_text );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTextElement::Concatenate( const char *pText )
{
	int old_length = 0;
	if ( mp_text )
		old_length += strlen( mp_text );
	int length  = old_length + strlen( pText );

	if ( length + 1 >= vMAX_TEXT_LENGTH )
	{
		// new string would be too long...
		return false;
	}

	if ( length > 0 )
	{
		char new_string[vMAX_TEXT_LENGTH];

		// initialize string
		strcpy( new_string, "" );
		if ( mp_text )
			strcpy( new_string, mp_text );

		// add new text
		strcat( new_string, pText );

		SetText( new_string );
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTextElement::Backspace()
{
	if ( !mp_text )
		return false;

	int length = strlen( mp_text );

	if ( length == 0 )
		return false;

	// copy string
	char new_string[vMAX_TEXT_LENGTH];
	strcpy( new_string, mp_text );

	// take care of escaped backslash char (ASCII code 92)
	if ( length > 1 && new_string[length - 1] == 92 && new_string[length - 2] == 92 )
		new_string[length - 2] = '\0';

	// terminate
	new_string[length - 1] = '\0';
	SetText( new_string );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::AttachBlurEffect()
{
	#if BLUR_EFFECT_ON
	if (!mp_blur_effect)
	{
		destroy_text_instances();

		// create blur effect according to specs
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mp_blur_effect = new CBlurEffect();
		mp_blur_effect->SetRGBA( m_local_props.GetRGBA() );
		Mem::Manager::sHandle().PopContext();

		// get number of CText objects needed
		// create 'em
		create_text_instances(mp_blur_effect->GetNumEntries() + 1);
	}
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

{
	if (mp_blur_effect)
	{
		destroy_text_instances();
		delete mp_blur_effect;
		mp_blur_effect = NULL;
		create_text_instances(1);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetShadowState(bool shadowOn)
{
	if (shadowOn && !m_use_shadow)
	{
		m_use_shadow = true;
		create_text_instances(m_num_tab_entries, true);
	}
	else if (!shadowOn && m_use_shadow)
	{
		m_use_shadow = false;
		destroy_text_instances(true);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetShadowOff(float offX, float offY)
{
	m_shadow_off_x = offX;
	m_shadow_off_y = offY;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::update()
{
	//Dbg_Message("I am drawing text element at (%.1f,%.1f), scale %.1f\n", m_summed_props.ulx, m_summed_props.uly, m_summed_props.scalex);
#if 0
	if( mp_font )
	{
		mp_font->BeginText( *((uint32 *) &m_local_props.GetRGBA() ), m_summed_props.scalex);
		mp_font->DrawString(mp_text, m_summed_props.ulx, m_summed_props.uly);
		mp_font->EndText();
	}
#else

	if (!mp_text) return;

	bool offscreen = false;
	// HACK
	if (m_summed_props.GetScreenUpperLeftY() < -200.0f || m_summed_props.GetScreenUpperLeftY() > 648.0f)
		offscreen = true;

	if (m_num_tab_entries)
	{
		Image::RGBA true_rgba = m_local_props.GetRGBA();
		if (m_summed_props.alpha >= .0001f)
			true_rgba.a = (uint8) ((float) m_local_props.GetRGBA().a * m_summed_props.alpha);
		else
			true_rgba.a = 0;

		/*
			-pass current pos, scale, alpha (local) to blur effect
			-for each entry in table
				-get pos, scale, alpa (in local, relative to current)
		*/

		mpp_text_req_tab[0]->SetFont(mp_font);
		mpp_text_req_tab[0]->SetPos(m_summed_props.GetScreenUpperLeftX(), m_summed_props.GetScreenUpperLeftY());
		mpp_text_req_tab[0]->SetRGBA(true_rgba, m_override_encoded_rgba);
		mpp_text_req_tab[0]->SetScale(m_summed_props.GetScaleX(), m_summed_props.GetScaleY());
		mpp_text_req_tab[0]->SetString(mp_text);
		if (m_object_flags & CScreenElement::v3D_POS)
		{
			mpp_text_req_tab[0]->SetZValue(m_summed_props.GetScreenPosZ());
			mpp_text_req_tab[0]->SetHidden( ( ( m_object_flags & CScreenElement::v3D_CULLED ) || IsHidden() ) );
		} else {
			mpp_text_req_tab[0]->SetPriority(m_z_priority);
			mpp_text_req_tab[0]->SetHidden( ( offscreen || IsHidden() ) );
		}

#if 0  // Garrett: Can't find window
		// Update the clip window.  This should only be done at init time, instead.
		CWindowElement *p_window = get_window();
		mpp_text_req_tab[0]->SetWindow(p_window->GetClipWindow());
#endif

		if (m_use_shadow)
		{
			Image::RGBA true_rgba = m_shadow_rgba;
			if (m_summed_props.alpha >= .0001f)
				true_rgba.a = (uint8) ((float) m_shadow_rgba.a * m_summed_props.alpha);
			else
				true_rgba.a = 0;
			mpp_shadow_req_tab[0]->SetFont(mp_font);
			mpp_shadow_req_tab[0]->SetPos(m_summed_props.GetScreenUpperLeftX() + m_shadow_off_x, m_summed_props.GetScreenUpperLeftY() + m_shadow_off_y);
			mpp_shadow_req_tab[0]->SetRGBA(true_rgba, m_override_encoded_rgba);
			mpp_shadow_req_tab[0]->SetScale(m_summed_props.GetScaleX(), m_summed_props.GetScaleY());
			mpp_shadow_req_tab[0]->SetString(mp_text);
			if (m_object_flags & CScreenElement::v3D_POS)
			{
				mpp_shadow_req_tab[0]->SetZValue(m_summed_props.GetScreenPosZ());
				mpp_shadow_req_tab[0]->SetHidden( ( ( m_object_flags & CScreenElement::v3D_CULLED ) || IsHidden() ) );
			} else {
				mpp_shadow_req_tab[0]->SetPriority(m_z_priority - .005f);
				mpp_shadow_req_tab[0]->SetHidden( ( offscreen || IsHidden() ) );
			}
		}
	}
	if(mp_blur_effect)
	{
		/*
			-pass current center pos, scale, alpha (local) to blur effect
			-for each entry in table
				-get center pos, scale, alpa (in local, relative to current)
		*/

		Dbg_MsgAssert(!(m_object_flags & CScreenElement::v3D_POS), ("Can't use motion blur on 3D positioned text"));

		mp_blur_effect->Update();

		// work out absolute center of this element
		float center_x = m_summed_props.GetScreenUpperLeftX() + m_summed_props.GetScaleX() * m_base_w / 2.0f;
		float center_y = m_summed_props.GetScreenUpperLeftY() + m_summed_props.GetScaleY() * m_base_h / 2.0f;

		for (int i = 1; i < m_num_tab_entries; i++)
		{
			ConcatProps &blur_entry = mp_blur_effect->GetInfo(i-1);

			Image::RGBA true_rgba = mp_blur_effect->GetRGBA();
			true_rgba.a = (uint8) ((float) true_rgba.a * blur_entry.alpha);

			float draw_pos_x = center_x - (blur_entry.GetScreenPosX() + m_base_w * blur_entry.GetScaleX() / 2.0f) * m_summed_props.GetScaleX();
			float draw_pos_y = center_y - (blur_entry.GetScreenPosY() + m_base_h * blur_entry.GetScaleY() / 2.0f) * m_summed_props.GetScaleY();
			float draw_scale_x = m_summed_props.GetScaleX() * blur_entry.GetScaleX();
			float draw_scale_y = m_summed_props.GetScaleY() * blur_entry.GetScaleY();

			mpp_text_req_tab[i]->SetPos(draw_pos_x, draw_pos_y);
			mpp_text_req_tab[i]->SetRGBA(true_rgba, m_override_encoded_rgba);
			mpp_text_req_tab[i]->SetScale(draw_scale_x, draw_scale_y);
			mpp_text_req_tab[i]->SetPriority(m_z_priority - i * .01f);

			mpp_text_req_tab[i]->SetFont(mp_font);
			mpp_text_req_tab[i]->SetString(mp_text);
			mpp_text_req_tab[i]->SetHidden( ( offscreen || IsHidden() ) );

			if (m_use_shadow)
			{
				Image::RGBA true_rgba = m_shadow_rgba;
				if (m_summed_props.alpha >= .0001f)
					true_rgba.a = (uint8) ((float) m_shadow_rgba.a * m_summed_props.alpha);
				else
					true_rgba.a = 0;
				mpp_shadow_req_tab[i]->SetPos(draw_pos_x + m_shadow_off_x * draw_scale_x,
											draw_pos_y + m_shadow_off_y * draw_scale_y);
				mpp_shadow_req_tab[i]->SetRGBA(true_rgba, m_override_encoded_rgba);
				mpp_shadow_req_tab[i]->SetScale(draw_scale_x, draw_scale_y);
				mpp_shadow_req_tab[i]->SetPriority(m_z_priority - i * .01f - .005f);

				mpp_shadow_req_tab[i]->SetFont(mp_font);
				mpp_shadow_req_tab[i]->SetString(mp_text);
				mpp_shadow_req_tab[i]->SetHidden( ( offscreen || IsHidden() ) );
			}
		}
	}
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::create_text_instances(int numEntries, bool shadow_only)
{
	Dbg_Assert(!mpp_shadow_req_tab);

	if (m_use_shadow)
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mpp_shadow_req_tab = new Nx::CText*[numEntries];
		Mem::Manager::sHandle().PopContext();
		for (int i = 0; i < numEntries; i++)
		{
			mpp_shadow_req_tab[i] = Nx::CTextMan::sGetTextInstance();
		}
	}

	if (!shadow_only)
	{
		Dbg_Assert(!m_num_tab_entries);

		m_num_tab_entries = numEntries;
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mpp_text_req_tab = new Nx::CText*[numEntries];
		Mem::Manager::sHandle().PopContext();
		for (int i = 0; i < numEntries; i++)
		{
			mpp_text_req_tab[i] = Nx::CTextMan::sGetTextInstance();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::destroy_text_instances(bool shadow_only)
{
	if (mpp_shadow_req_tab)
	{
		for (int i = 0; i < m_num_tab_entries; i++)
		{
			Nx::CTextMan::sFreeTextInstance(mpp_shadow_req_tab[i]);
		}
		delete mpp_shadow_req_tab;
		mpp_shadow_req_tab = NULL;
	}

	if (!shadow_only)
	{
		Dbg_Assert(m_num_tab_entries);

		for (int i = 0; i < m_num_tab_entries; i++)
		{
			Nx::CTextMan::sFreeTextInstance(mpp_text_req_tab[i]);
		}
		delete mpp_text_req_tab;
		m_num_tab_entries = 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextBlockElement::CTextBlockElement() :
	m_shadow_rgba(0, 0, 0, 128)
{
	//Ryan("I am a new CTextBlockElement\n");
	m_font = 0;
	m_internal_scale = 1.0f;
    m_line_spacing_scale = 1.0f;
	m_total_height = 0.0f;
	m_total_out_lines = 0;

	mp_blur_effect = NULL;
	m_allow_expansion = false;

	m_override_encoded_rgba = false;
	m_previous_override_rgba_state = false;

	SetType(CScreenElement::TYPE_TEXT_BLOCK_ELEMENT);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextBlockElement::~CTextBlockElement()
{
	if (mp_blur_effect)
		delete mp_blur_effect;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::SetProperties(Script::CStruct *pProps)
{
	CScreenElement::SetProperties(pProps);

	// must find this before setting font
	if (pProps->GetFloat(CRCD(0x1fe341d2,"internal_scale"), &m_internal_scale))
		m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;

    pProps->GetFloat(CRCD(0xe3fa22fc,"line_spacing"), &m_line_spacing_scale);

	uint32 font_crc;
	if (pProps->GetChecksum(CRCD(0x2f6bf72d,"font"), &font_crc))
		SetFont(font_crc);
	Dbg_MsgAssert(m_font, ("no font loaded"));

	if (resolve_just(pProps, CRCD(0x67e093e4,"internal_just"), &m_internal_just_x, &m_internal_just_y))
		m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;

	const char *pp_line_tab[32];
	Script::CArray *p_text_array;

	if (pProps->ContainsFlag( CRCD(0x322839a2,"allow_expansion") ))
		m_allow_expansion = true;

	if (pProps->GetText(CRCD(0xc4745838,"text"), pp_line_tab))
	{
		SetText(pp_line_tab, 1);
	}
	else if (pProps->GetArray(CRCD(0xc4745838,"text"), &p_text_array))
	{
		int num_entries = p_text_array->GetSize();
		for (int i = 0; i < num_entries; i++)
			pp_line_tab[i] = p_text_array->GetString(i);

		SetText(pp_line_tab, num_entries);
	}

	/*
		Property changes to be forwared to children follow:
	*/

	if (pProps->ContainsFlag(CRCD(0xd1653c6c,"blur_effect")) && !mp_blur_effect)
	{
		#if BLUR_EFFECT_ON
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mp_blur_effect = new CBlurEffect();
		mp_blur_effect->SetRGBA( m_local_props.GetRGBA() );
		Mem::Manager::sHandle().PopContext();
		#endif
	}
	else if (pProps->ContainsFlag(CRCD(0x4b73e4f8,"no_blur_effect")) && mp_blur_effect)
	{
		delete mp_blur_effect;
		mp_blur_effect = NULL;
	}

	Image::RGBA blur_rgba;
	if (resolve_rgba(pProps, CRCD(0x4f0e1182,"blur_rgba"), &blur_rgba) && mp_blur_effect)
	{
		mp_blur_effect->SetRGBA(blur_rgba);
	}

	Script::CPair shadow_offs;
	if (pProps->GetPair(CRCD(0x2a1fe0cc,"shadow_offs"), &shadow_offs))
	{
		m_shadow_off_x = shadow_offs.mX;
		m_shadow_off_y = shadow_offs.mY;
	}

	if (pProps->ContainsFlag(CRCD(0x8a897dd2,"shadow")))
		m_use_shadow = true;
	if (pProps->ContainsFlag(CRCD(0x95b5b9a0,"no_shadow")))
		m_use_shadow = false;

	resolve_rgba(pProps, CRCD(0x1e7bb1f5,"shadow_rgba"), &m_shadow_rgba);

	if ( pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ) )
	{
		Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
		m_previous_override_rgba_state = m_override_encoded_rgba;
	}
	if ( pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ) )
	{
		Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
		m_override_encoded_rgba = m_previous_override_rgba_state;
	}

	if (pProps->ContainsFlag(CRCD(0xb7686f92,"override_encoded_rgba")))
		m_override_encoded_rgba = true;
	if (pProps->ContainsFlag(CRCD(0xd25bc6cd,"dont_override_encoded_rgba")))
		m_override_encoded_rgba = false;

	forward_properties_to_children();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::SetMorph(Script::CStruct *pProps)
{
	if (mp_blur_effect)
	{
		// Distribute blur effect settings to children, account for different
		// vertical positions of children relative to center of this

		// cheap hack, update self so that contained elements have proper position
		update();

		Tmr::Time target_time;
		const CBlurEffect::Props &blur_target = mp_blur_effect->SetMorph(pProps, &target_time);
		// hook up blur effect to children

		CScreenElementPtr p_child = CScreenElement::GetFirstChild();
		while(p_child)
		{

			Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
			Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));

			// "child blur target"
			CBlurEffect::Props cbt = blur_target;

			// find center of child relative to center of this
			// (we know we'll be getting top of child, since that's the just this class sets)
			float child_center_x, child_center_y;
			p_child->GetLocalPos(&child_center_x, &child_center_y);
			child_center_y += p_child->GetBaseH() / 2.0f;
			float disp_y = m_base_h / 2.0f - child_center_y;
			cbt.maxDisplacementY += blur_target.bottomScaleY * disp_y - disp_y;

			CBlurEffect *p_child_blur = ((CTextElement *) ((CScreenElement *) p_child))->GetBlurEffect();
			Dbg_Assert(p_child_blur);
			p_child_blur->SetAllTargetProps(cbt, target_time);
			p_child = p_child->GetNextSibling();
		}
	}

	CScreenElement::SetMorph(pProps);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::SetFont(uint32 font_checksum)
{
	Dbg_MsgAssert(!m_font, ("font already set"));
	m_font = font_checksum;
	Dbg_MsgAssert(Nx::CFontManager::sTestFontLoaded(m_font), ("font %s isn't loaded", Script::FindChecksumName(m_font)));

	// how many lines will fit?

	Nx::CFont* p_font = Nx::CFontManager::sGetFont(m_font);
	Dbg_MsgAssert(p_font, ("no font found"));

	// see if word wraps past end of element
	float word_w, word_h;
	p_font->QueryString("AAA", word_w, word_h);	// width and height in pixels

	m_num_visible_lines = (int) m_base_h / (int) (word_h * m_internal_scale);
	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::SetText(const char **ppTextLines, int numLines)
{
	Dbg_MsgAssert(m_object_flags & vFORCED_DIMS, ("no dimensions have been set"));
	Dbg_MsgAssert(numLines < 256, ("large number of lines, probably not good"));

	CScreenElementManager* p_manager = CScreenElementManager::Instance();

	// destroy any children that are present
	SetChildLockState( UNLOCK );
	CScreenElementPtr p_child = GetFirstChild();
	while ( p_child )
	{
		CScreenElementPtr p_next = p_child->GetNextSibling();
		p_manager->DestroyElement(p_child->GetID());
		p_child = p_next;
	}
	Dbg_Assert( !GetFirstChild() );

	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
	mpp_parsed_lines = new char*[MAX_LINES];
	// (Mick) in order to avoid doing multiple allocations, we just
	// allocate a single array big enough for all the lines
	mpp_parsed_lines[0] = new char[MAX_CHARS * MAX_LINES];
	Mem::Manager::sHandle().PopContext();
	// then calculate the pointers for all the other lines, as an offset to this
	for ( int l = 1; l < MAX_LINES; l++ )
	{
		mpp_parsed_lines[l] = mpp_parsed_lines[0] + MAX_CHARS*l;
	}
	for( int i = 0; i < MAX_LINES; i++ )
	{
		mpp_parsed_lines[i][0] = '\0';
	}

	m_out_char = 0;
	m_virtual_out_line = 0;
	m_out_line = 0;
	m_current_line_width = 0.0f;

	// reset number of visible lines
	if ( m_allow_expansion )
	{
		m_base_h = 0.0f;
		m_num_visible_lines = 0;
	}

	for ( int in_line = 0; in_line < numLines; in_line++ )
		read_in_text_line( ppTextLines[in_line] );

	// want to handle the "no text" case
	if (numLines)
	{
		m_virtual_out_line++;
		m_out_line++;
	}

	// we only to want to use the N most recent lines,
	// where N is the number of lines that will fit
	int buf_use_line = 0;
	m_total_out_lines = m_out_line;
	if ( m_virtual_out_line >= m_num_visible_lines )
	{
		// expand the element if we're supposed to
		if ( m_allow_expansion )
		{
			Nx::CFont* p_font = Nx::CFontManager::sGetFont(m_font);
			Dbg_MsgAssert(p_font, ("no font found"));

			// change the height of the element
			float word_w, word_h;
			p_font->QueryString("AAA", word_w, word_h );	// width and height in pixels
			m_base_h = m_virtual_out_line * (word_h * m_internal_scale * m_line_spacing_scale );

			m_num_visible_lines = (int) m_base_h / (int) (word_h * m_internal_scale * m_line_spacing_scale );

			// re-parse the text now that m_num_visible_lines has changed
// Mick: no need for this, since they are fixed size, we just overwrite them
			m_out_char = 0;
			m_virtual_out_line = 0;
			m_out_line = 0;
			m_current_line_width = 0.0f;
			for (int i = 0; i < numLines; i++)
				read_in_text_line(ppTextLines[i]);

			// printf("changing m_total_out_lines from %i to %i\n", m_total_out_lines, m_num_visible_lines);
			m_total_out_lines = m_num_visible_lines;
		}
		else
		{
			Dbg_MsgAssert( m_num_visible_lines, ("Text block element too small for one line") );
            buf_use_line = m_virtual_out_line % m_num_visible_lines;
			m_total_out_lines = m_num_visible_lines;
		}
	}

	// create new children
	m_total_height = 0.0f;
	for (int i = 0; i < m_total_out_lines; i++)
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		CTextElement *p_new_elem = new CTextElement();
		Mem::Manager::sHandle().PopContext();
		p_manager->RegisterObject(*p_new_elem);
		p_manager->SetParent(this, p_new_elem);
		p_new_elem->SetFont(m_font);
		p_new_elem->SetText(&mpp_parsed_lines[buf_use_line][0]);
		p_new_elem->SetScale(m_internal_scale, m_internal_scale);

		m_total_height += p_new_elem->GetBaseH() * m_internal_scale * m_line_spacing_scale;
		buf_use_line++;
		if (buf_use_line >= m_num_visible_lines)
			buf_use_line = 0;
	}

	// delete the lines, now we've used them
// Mick: Now we just delete the array of chars, and the array of pointers
	delete mpp_parsed_lines[0];
	delete mpp_parsed_lines;

	SetChildLockState(LOCK);

	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::SetText(const char *pTextLine)
{
	SetText(&pTextLine, 1);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTextBlockElement::GetText( char* p_text, int size )
{
	strcpy( p_text, "" );
	int total_length = 0;

	CScreenElementPtr p_child = GetFirstChild();
	while( p_child )
	{
		Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
		Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));

		CScreenElementPtr p_next = p_child->GetNextSibling();
		CTextElement* p_text_element = (CTextElement*)p_child.Convert();

		char* p_text_element_string = p_text_element->GetText();
		total_length += strlen( p_text_element_string );

		if ( p_next )
			total_length += 1;

		if ( total_length < size )
		{
			strcat( p_text, p_text_element_string );

			// add a space between lines if this isn't the last line
			if ( p_next )
				strcat( p_text, " " );
		}
		else
		{
			Dbg_MsgAssert( 0, ( "TextBlockElement::GetText - text too long" ) );
			return false;
		}
		p_child = p_next;
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CTextBlockElement::GetLength()
{
	// printf( "CTextBlockElement::GetLength called on %s\n", Script::FindChecksumName( m_id ) );
	int total_length = 0;

	CScreenElementPtr p_child = GetFirstChild();
	while( p_child )
	{
		Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
		Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));

		CScreenElementPtr p_next = p_child->GetNextSibling();
		CTextElement* p_text_element = (CTextElement*)p_child.Convert();
		total_length += p_text_element->GetLength();

		if ( p_next )
			total_length += 1;

		p_child = p_next;
	}
	return total_length;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTextBlockElement::Backspace()
{
	int length = GetLength();

	if ( length == 0 )
		return false;

	Dbg_MsgAssert( length < MAX_EDITABLE_TEXT_BLOCK_LENGTH, ( "Backspace failed - string too long" ) );
	char new_string[MAX_EDITABLE_TEXT_BLOCK_LENGTH];
	GetText( new_string, MAX_EDITABLE_TEXT_BLOCK_LENGTH );

	if ( !new_string )
		return false;

	// take care of escaped backslash char (ASCII code 92)
	if ( length > 1 && new_string[length - 1] == 92 && new_string[length - 2] == 92 )
		new_string[length - 2] = '\0';

	// terminate
	new_string[length - 1] = '\0';
	SetText( new_string );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
	Called from read_in_text_line()
*/
CTextBlockElement::EParseResult CTextBlockElement::parse_next_word(char *pWordBuf, const char **ppSource)
{
	bool keep_scanning = true;
	EParseResult result = NORMAL;

	char *p_out = pWordBuf;

	while (keep_scanning)
	{
		switch(**ppSource)
		{
			case ' ':
				*p_out++ = **ppSource;
				(*ppSource)++;
				keep_scanning = false;
				break;
			case '\\':
			{
				(*ppSource)++;
				if (**ppSource == '_')
				{
					*p_out++ = ' ';
					(*ppSource)++;
				}
				else if (**ppSource == 'n')
				{
					(*ppSource)++;
					result = NEXT_LINE;
					keep_scanning = false;
				}
				else
				{
					// is \?, where ? is some character
					// output both
					*p_out++ = '\\';
					*p_out++ = **ppSource;
					(*ppSource)++;
				}
				break;
			} // end case
			case '\0':
				result = END_OF_BUFFER;
				keep_scanning = false;
				break;
			default:
			{
				*p_out++ = **ppSource;
				(*ppSource)++;
				break;
			}
		} // end switch

		Dbg_Assert((int) p_out - (int) pWordBuf < MAX_CHARS - 1);
	}

	*p_out++ = '\0';
	return result;
}

/*
	Called from SetText()
*/
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::read_in_text_line(const char *pText)
{
	Nx::CFont* p_font = Nx::CFontManager::sGetFont(m_font);
	Dbg_MsgAssert(p_font, ("no font found"));

	const char *p_in = pText;

	char p_word[MAX_CHARS];

	EParseResult last_result = NORMAL;

	while ( last_result != END_OF_BUFFER )
	{
		EParseResult result = parse_next_word( p_word, &p_in );

		// see if word wraps past end of element
		float word_w, word_h;
		p_font->QueryString(p_word, word_w, word_h);	// width and height in pixels
		word_w *= m_internal_scale;

		// if adding this word makes the line too long
		// or we had a newline char or similar
		// or this word will overflow the buffer (unlikely, but possible)
		if ( ( m_current_line_width > 0.0f && m_current_line_width + word_w > m_base_w )
			 ||  ( last_result == NEXT_LINE )
			 ||  ( m_out_char + strlen( p_word ) >= MAX_CHARS )
		   )
		{

			// if last character on line is a space, do away with it
			if ( mpp_parsed_lines[m_out_line][m_out_char-1] == ' ' )
				mpp_parsed_lines[m_out_line][m_out_char-1] = '\0';

			// Wrap to next	line.
			// Once we fill all visible lines, we start recycling the buffer,
			// keeping only the most recent lines, hence the virtual line stuff.
			m_virtual_out_line++;
			m_out_line++;
			if ( m_out_line >= m_num_visible_lines )
				m_out_line = 0;
			m_out_char = 0;
			m_current_line_width = 0.0f;
		}

		Dbg_Assert(m_out_line < MAX_LINES); // need to increase MAX_LINES if triggered
		Dbg_MsgAssert(m_out_char + strlen(p_word) < MAX_CHARS, ("overflow, max allowed = %d, needed = %d\n%s%s\n", MAX_CHARS, m_out_char + strlen(p_word),&mpp_parsed_lines[m_out_line][0],p_word));
		char *p_out = &mpp_parsed_lines[m_out_line][0] + m_out_char;
		strcpy(p_out, p_word);
		m_out_char += strlen(p_word);
		m_current_line_width += word_w;

		last_result = result;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::update()
{
	if (m_object_flags & CScreenElement::vCHANGED_STATIC_PROPS)
	{
		Dbg_Assert(m_total_height > .00001f || !m_total_out_lines);

		// position children
		float y = (m_internal_just_y + 1.0f) * (m_base_h - m_total_height) / 2.0f;
		CScreenElementPtr p_child = GetFirstChild();
		for (int i = 0; i < m_total_out_lines; i++)
		{
			Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
			Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));

			p_child->SetJust(m_internal_just_x, -1.0f);
			p_child->SetRGBA( m_local_props.GetRGBA() );
			p_child->SetPos((m_internal_just_x + 1.0f) * m_base_w / 2.0f, y);

			y += p_child->GetBaseH() * m_internal_scale * m_line_spacing_scale;
			p_child = p_child->GetNextSibling();
		}
		forward_properties_to_children();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::forward_properties_to_children()
{
	CScreenElementPtr p_child = CScreenElement::GetFirstChild();
	while(p_child)
	{

		Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
			Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));

		if (mp_blur_effect)
			((CTextElement *) ((CScreenElement *) p_child))->AttachBlurEffect();
		else
			((CTextElement *) ((CScreenElement *) p_child))->DetachBlurEffect();
		if (m_use_shadow)
			((CTextElement *) ((CScreenElement *) p_child))->SetShadowState(true);
		else
			((CTextElement *) ((CScreenElement *) p_child))->SetShadowState(false);
		((CTextElement *) ((CScreenElement *) p_child))->SetShadowOff(m_shadow_off_x, m_shadow_off_y);
		((CTextElement *) ((CScreenElement *) p_child))->SetShadowRGBA(m_shadow_rgba);
		((CTextElement *) ((CScreenElement *) p_child))->SetEncodedRGBAOverride(m_override_encoded_rgba);
		p_child = p_child->GetNextSibling();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// if enforce_max_width is true, the function will fail if a single word is
// bigger than the width of a line.  That is, if the text would go outside
// the specified width and there's no way to wrap, concatenation will fail.
// Only the end of the string is checked.  The text already in the element
// is assumed to be safe.  Thus it's possible to have a textblock element go
// outside it's box by using SetText directly, rather than using concatenate
bool CTextBlockElement::Concatenate( const char* pText, bool enforce_max_width, bool last_line )
{
#ifdef __NOPT_ASSERT__
	int length = GetLength();
	Dbg_MsgAssert( length < (int)( MAX_EDITABLE_TEXT_BLOCK_LENGTH - strlen( pText ) ), ( "TextBlock too long to concatenate" ) );
#endif

	// setup new, unwrapped string
	char* p_new_string = new char[MAX_EDITABLE_TEXT_BLOCK_LENGTH];
	GetText( p_new_string, Front::MAX_EDITABLE_TEXT_BLOCK_LENGTH );
	strcat( p_new_string, pText );

	// enforce max width but allow them to add a space.
	if ( (enforce_max_width && strcmp( pText, " " ) != 0) || last_line)
	{
		Nx::CFont* p_font = Nx::CFontManager::sGetFont( m_font );
		Dbg_MsgAssert( p_font, ( "no font found" ) );

		// parse through the new text word by word
		const char *p_in = p_new_string;
		char p_word[MAX_CHARS];

		// skip up to the last word
		EParseResult last_result = NORMAL;
		while ( last_result != END_OF_BUFFER )
		{
			last_result = parse_next_word( p_word, &p_in );
		}

		// check that each word is not longer than the max width
		float word_w, word_h;
		p_font->QueryString( p_word, word_w, word_h );	// width and height in pixels
		word_w *= m_internal_scale;
		if ( word_w > m_base_w )
		{
			delete p_new_string;
			return false;
		}
	}

	SetText( p_new_string );
	delete p_new_string;
	return true;
}

}


{
	if ( !mp_text )
		return false;

	int length = strlen( mp_text );

	if ( length == 0 )
		return false;

	// copy string
	char new_string[vMAX_TEXT_LENGTH];
	strcpy( new_string, mp_text );

	// take care of escaped backslash char (ASCII code 92)
	if ( length > 1 && new_string[length - 1] == 92 && new_string[length - 2] == 92 )
		new_string[length - 2] = '\0';

	// terminate
	new_string[length - 1] = '\0';
	SetText( new_string );
	return true;
}

#include <core/defines.h>
#include <gel/scripting/checksum.h>
#include <gel/scripting/script.h>
#include <gel/scripting/struct.h>
#include <gel/scripting/array.h>
#include <gel/scripting/vecpair.h>
#include <gfx/NxFontMan.h>
#include <gfx/NxFont.h>
#include <gfx/2D/ScreenElement2.h>
#include <gfx/2D/ScreenElemMan.h>
#include <gfx/2D/TextElement.h>
#include <gfx/2D/BlurEffect.h>
#include <gfx/2D/Window.h>

#define BLUR_EFFECT_ON 1

namespace Front
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextElement::CTextElement() :
	m_shadow_rgba(0, 0, 0, 128)
{
	//Ryan("I am a new CTextElement\n");
	m_font_checksum = 0;
	mp_font = NULL;
	mp_text = NULL;
	mp_blur_effect = NULL;
	m_use_shadow = false;
	create_text_instances(1);

	m_override_encoded_rgba = false;
	m_previous_override_rgba_state = false;

	SetType(CScreenElement::TYPE_TEXT_ELEMENT);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextElement::~CTextElement()
{
	DetachBlurEffect();
	destroy_text_instances();
	if (mp_text)
		delete mp_text;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetProperties(Script::CStruct *pProps)
{
	CScreenElement::SetProperties(pProps);

	uint32 font_crc;
	if (pProps->GetChecksum(CRCD(0x2f6bf72d,"font"), &font_crc))
		SetFont(font_crc);
	Dbg_MsgAssert(m_font_checksum, ("no font loaded"));

	const char *p_text;
	if (pProps->GetText(CRCD(0xc4745838,"text"), &p_text))
		SetText(p_text);

	Dbg_MsgAssert(m_id != Obj::CBaseManager::vNO_OBJECT_ID, ("what, no ID"));

	if (pProps->ContainsFlag(CRCD(0xd1653c6c,"blur_effect")))
		AttachBlurEffect();
	if (pProps->ContainsFlag(CRCD(0x4b73e4f8,"no_blur_effect")))
		DetachBlurEffect();

	Image::RGBA blur_rgba;
	if (resolve_rgba(pProps, CRCD(0x4f0e1182,"blur_rgba"), &blur_rgba) && mp_blur_effect)
		mp_blur_effect->SetRGBA(blur_rgba);

	Script::CPair shadow_offs;
	if (pProps->GetPair(CRCD(0x2a1fe0cc,"shadow_offs"), &shadow_offs))
	{
		SetShadowOff(shadow_offs.mX, shadow_offs.mY);
	}

	if (pProps->ContainsFlag(CRCD(0x8a897dd2,"shadow")))
		SetShadowState(true);
	if (pProps->ContainsFlag(CRCD(0x95b5b9a0,"no_shadow")))
		SetShadowState(false);

	resolve_rgba(pProps, CRCD(0x1e7bb1f5,"shadow_rgba"), &m_shadow_rgba);

	if ( pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ) )
	{
		Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
		m_previous_override_rgba_state = m_override_encoded_rgba;
	}
	if ( pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ) )
	{
		Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
		m_override_encoded_rgba = m_previous_override_rgba_state;
	}

	if (pProps->ContainsFlag(CRCD(0xb7686f92,"override_encoded_rgba")))
		m_override_encoded_rgba = true;
	if (pProps->ContainsFlag(CRCD(0xd25bc6cd,"dont_override_encoded_rgba")))
		m_override_encoded_rgba = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetMorph(Script::CStruct *pProps)
{
	if (mp_blur_effect)
	{
		mp_blur_effect->SetMorph(pProps);
	}

	CScreenElement::SetMorph(pProps);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetFont(uint32 font_checksum)
{
	m_font_checksum = font_checksum;
	mp_font = Nx::CFontManager::sGetFont(m_font_checksum);
	Dbg_MsgAssert(Nx::CFontManager::sTestFontLoaded(m_font_checksum), ("font %s isn't loaded", Script::FindChecksumName(m_font_checksum)));
	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetText(const char *pText)
{
	int new_length = strlen(pText) + 1;
	Dbg_MsgAssert(new_length < vMAX_TEXT_LENGTH, ("string too long %d", new_length));

	// if the old string is not big enough, then we need to allocate a new one
	// otherwize, overwrite it, as allocations are slow
	if (!mp_text || (int)(strlen(mp_text)) < new_length-1)		  // note, does not account for actual allocated size, but will work is string are same size
	{
		if (mp_text)
			delete mp_text;
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mp_text = new char[new_length];
		Mem::Manager::sHandle().PopContext();
	}
	// copy pText to mp_text, replacing '\m' tags with '\b' tags
	const char *p_in = pText;
	char *p_out = mp_text;
	while(*p_in)
	{
		if (*p_in == '\\' && *(p_in+1) == '\\')
		{
			// output "\\" tag unchanged
			*p_out++ = *p_in++;
			*p_out++ = *p_in++;
		}
		// ignore sticky space
		else if (*p_in == '\\' && *(p_in+1) == '_')
		{
			p_in++;
			p_in++;
			*p_out++ = ' ';
		}
		else if (*p_in == '\\' && *(p_in+1) == 'm')
		{
			*p_out++ = *p_in++;
			*p_out++ = 'b';
			p_in++;
			*p_out++ = Nx::CFontManager::sMapMetaCharacterToButton(p_in++);
		}
		else
		{
			*p_out++ = *p_in++;
		}
	}
	*p_out = '\0';

	if( mp_font )
	{
		float w, h;
		mp_font->QueryString(mp_text, w, h);	// width and height in pixels

		if (!(m_object_flags & vFORCED_DIMS))
		{
			SetDims(w, h);
		}
	}

	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CTextElement::GetLength()
{
	return strlen( mp_text );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTextElement::Concatenate( const char *pText )
{
	int old_length = 0;
	if ( mp_text )
		old_length += strlen( mp_text );
	int length  = old_length + strlen( pText );

	if ( length + 1 >= vMAX_TEXT_LENGTH )
	{
		// new string would be too long...
		return false;
	}

	if ( length > 0 )
	{
		char new_string[vMAX_TEXT_LENGTH];

		// initialize string
		strcpy( new_string, "" );
		if ( mp_text )
			strcpy( new_string, mp_text );

		// add new text
		strcat( new_string, pText );

		SetText( new_string );
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTextElement::Backspace()