/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: txtwindw.cpp,v 1.1.2.1 2004/07/09 01:50:20 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

/////////////////////////////////////////////////////////////////////////////
//
//  TXTWINDW.CPP
//
//  TextWindow class implementation.
//
//	A class TextWindow object holds the location, size, background color,
//	...etc for the space on the screen where the text is to be rendered.
//

#include "hxtypes.h" /*Must be included before windows.h for VC6 compiler */

#if defined(_WINDOWS)
#include <windows.h>
#else
/*  Need to include what's necessary for window resources: mac, ppc, unix. */
#endif

#include <stdlib.h>
#include <string.h>

#include "hxwintyp.h" //For struct HXxWindow

#include "rt_types.h"
#include "atocolor.h"
#include "atotime.h"
#include "rt_string.h"
#include "parsing.h"

#include "fontdefs.h" //for CHARSET defines.

#include "hxslist.h" //for base class CHXSimpleList.
#include "hxstack.h" //for class CHXStack
#include "txtattrb.h" //for class TextAttributes
#include "txtcntnr.h" //for class TextAttributes
#include "textline.h" //for class TextLine & TextLineList
#include "textprsr.h" /* for REAL_TEXT_TRANSPARENT_BGCOLOR_MAJOR_VERSION + ... */

#include "txtwindw.h"

#include "fontinfo.h"  //for GetStringWidthInPixels() function.

#include "dict.h" /* For Unix font dict */

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static char HX_THIS_FILE[] = __FILE__;
#endif


/////////////////////////////////////////////////////////////////////////////
// Method:
//	TextWindowBase constructor
// Purpose:
//	Initializes all data members to default values.
//
TextWindowBase::TextWindowBase() :
	  m_width(WINDOW_DIMENSION_UNSPECIFIED)
	, m_height(WINDOW_DIMENSION_UNSPECIFIED)
    
	, m_ulContentMajorVersion(0L)
	, m_ulContentMinorVersion(0L)

	, m_borderSize(DEFAULT_BORDERSIZE)
	, m_upperLeftX(DEFAULT_WINDOWUPPERLEFTX)
	, m_upperLeftY(DEFAULT_WINDOWUPPERLEFTY)

	, m_scrollRate(SCROLLRATE_UNSPECIFIED)
	, m_crawlRate(CRAWLRATE_UNSPECIFIED)
	, m_type(TYPE_UNSPECIFIED)
	, m_scrollType(SCROLLTYPE_UNSPECIFIED)
	, m_backgroundColor(BGCOLOR_RGB_UNSPECIFIED)
        , m_ulBackgroundOpacity(255)
        , m_ulMediaOpacity(255)
        , m_bIsChromaKeySet(FALSE)
        , m_ulChromaKey(0)
        , m_ulChromaKeyTolerance(0)
        , m_ulChromaKeyOpacity(0)
	, m_loop(DOLOOP_UNSPECIFIED)

	, m_numBreakTagsEncountered(0L)

	, m_currentTextLineStartY(0L)
	, m_currentTextLineEndY(0L)
	, m_currentTextLineStartX(0L)
	, m_currentTextLineEndX(0L)
    
	, m_xOfFakeNewLine(0L)

	, m_linkColor(DEFAULT_LINKCOLOR)
	, m_bUnderlineHyperlinks(DEFAULT_DO_UNDERLINE_HYPERLINKS)
	, m_bUseWordwrap(DEFAULT_USE_WORDWRAP)

	, m_bExpandTabs(DEFAULT_EXPAND_TABS)
	, m_kHorizAlign(kHAlignLeft)
	, m_kVertAlign(kVAlignTop)
	, m_bVertAlignWasExplicitlySet(FALSE)
	, m_bRightToLeft(FALSE)
	, m_ulDefaultPointSize(DEFAULT_FONT_PTSIZE)
	, m_defaultTextColor(DEFAULT_TEXT_COLOR)
	, m_defaultTextBgColor(BGCOLOR_RGB_UNSPECIFIED)
	, m_ulDefaultFontWeight(DEFAULT_FONT_WEIGHT)
	, m_bDefaultFontStyleIsItalic(FALSE)
	, m_pDefaultFontFaceString(NULL)
	, m_pDefaultCharsetString(NULL)
	, m_bIsCharsetTranslatedForOS(FALSE)
	, m_dAngleOfEscapement(0.0)
	, m_dAngleOfCharOrientation(0.0)
	, m_bAngleOfCharOrientationWasExplicitlySet(FALSE)
	, m_bUserTextSizingPrefIsSet(FALSE)
	, m_bUserPrefSizeIsRelative(TRUE)
	, m_ulUserPrefTextSizeOrScaleFactorPct(100)

	, m_bDontIgnoreExtraSpaces(DEFAULT_DONT_IGNORE_EXTRA_SPACES)


	, m_ulNumberOfFontColorPushesInsideLinkText(0L)

	, m_bIsLiveSource(DEFAULT_IS_LIVE_SOURCE)

	, m_ulTimeAtStartup(0L)

	, m_ulTimeOfLastClearTag(0L)

	, m_newPktStartXAtTimeZero(INVALID_LONG32)
	, m_newPktStartYAtTimeZero(INVALID_LONG32)
    
	, m_bClearWasJustSent(FALSE)

	, m_bUseXPOSVal(FALSE)

	, m_bUseYPOSVal(FALSE)

	, m_ulInsideCommentTagNestCount(0L)

	, m_ulNumNewlinesStatedInPacketHeader(0L)

	, m_ulDuration(0L)
{    
    m_pTLList = new TextLineList;
    
    m_pFontUndoTagList = new TextLineList;

#if defined(_DEBUG)
    m_ulDebugFlags = 0L;
#endif
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//	TextWindowBase destructor
// Purpose:
//	janitorial function.
//
TextWindowBase::~TextWindowBase()
{
    reset();
    if(m_pTLList)
    {
	m_pTLList->flush();
	delete m_pTLList;
	m_pTLList = NULL;
    }
    if(m_pFontUndoTagList)
    {
	m_pFontUndoTagList->flush();
	delete m_pFontUndoTagList;
	m_pFontUndoTagList = NULL;
    }

    if (m_pDefaultFontFaceString)
    {
	delete [] m_pDefaultFontFaceString;
	m_pDefaultFontFaceString = NULL;
    }

    if (m_pDefaultCharsetString)
    {
	delete [] m_pDefaultCharsetString;
	m_pDefaultCharsetString = NULL;
    }
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//	 TextWindowBase::reset()
//
// Purpose:
//  Clears all text attribute stacks so that the next text that arrives will 
//  get the default font size, color, bold, ...etc, until new attribute tags
//  are received.
//
void TextWindowBase::reset()
{
    TextAttributeStacks::flush();
    TextContainerList::flush();
    if(m_pTLList)
    {
	m_pTLList->flush();
    }

    if(m_pFontUndoTagList)
    {
	m_pFontUndoTagList->flush();
    }
    
    m_numBreakTagsEncountered = 0L;

    m_currentTextLineStartY = m_currentTextLineEndY = 0L;
    m_currentTextLineStartX = m_currentTextLineEndX = 0L;
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::getCommentTagNestCount()
//
// Purpose:
//  Tells whether we're currently parsing inside HTML-style comments, i.e.,
//  <!-- we're inside a comment --> :
ULONG32 TextWindowBase::getCommentTagNestCount()
{
    return m_ulInsideCommentTagNestCount;
}
void TextWindowBase::setInsideCommentTagNestCount(ULONG32 ulCTNC)
{
    m_ulInsideCommentTagNestCount = ulCTNC;
}
ULONG32 TextWindowBase::incrementCommentTagNestCount()
{
    return ++m_ulInsideCommentTagNestCount;
}
ULONG32 TextWindowBase::decrementCommentTagNestCount()
{
    if(m_ulInsideCommentTagNestCount)
	m_ulInsideCommentTagNestCount--;
    return m_ulInsideCommentTagNestCount;
}




/////////////////////////////////////////////////////////////////////////////
// Method:
//	TextWindow constructor
// Purpose:
//	Initializes all data members to default values.
//
TextWindow::TextWindow() :
	  m_pURL(NULL)
	, m_ulLenURLbuf(0L)
	, m_ulTargetOfURL(URL_TARGET_INVALID)
	, m_ulTimeOfLastTimeSync(0L) //In live on encoder side, this needs to
				     // get reset to encoder start time which
				     // may not be zero.
#if defined(_WINDOWS)
	, m_pDeviceContextMemory(NULL)
#if !defined(USE_DIB_SECTION)
	, m_pBmpCompat(NULL)
#else //USE_DIB_SECTION:
	, m_hBitmap(NULL)
	, m_hOldBitmap(NULL)
	, m_LPBITMAPINFO(NULL)
	, m_pBits(NULL)
#endif
#elif defined(_MACINTOSH) || defined(_MAC_UNIX)
	, m_pOffScreenWorld(NULL)
#endif
	, m_visibleWindowWidth(0L)
	, m_visibleWindowHeight(0L)
	 
	, m_lCurrentXOffsetDueToLooping(0L)
	, m_lCurrentYOffsetDueToLooping(0L)

	, m_lCurrentYOffsetForTeleprompter(0L)

	, m_bPriorLineAlreadyCentered(FALSE)

	, m_bHandlingWindowResizing(FALSE)
{
#if defined(_UNIX) && !defined(_MAC_UNIX)
    m_font_dict = new Dict;
#endif

#ifdef _WINDOWS
#if !defined(USE_DIB_SECTION)
    m_hDIB = new CHXDIBits();
#else //USE_DIB_SECTION:
    //nothing to do here.
#endif
    m_pDeviceContextMemory = (void*)CreateCompatibleDC(NULL);
#endif // _WINDOWS

}



/////////////////////////////////////////////////////////////////////////////
// Method:
//	TextWindow destructor
// Purpose:
//	janitorial function.
//
TextWindow::~TextWindow()
{
    clear_all();
#if defined(_WINDOWS)
#if !defined(USE_DIB_SECTION)
    if(m_pBmpCompat)
    {
	DeleteObject((HBITMAP)m_pBmpCompat);
    }

    if (m_hDIB)
    {
	delete m_hDIB;
	m_hDIB = NULL;
    }
#else //USE_DIB_SECTION:
    if(m_hBitmap)
    {
	DeleteObject(m_hBitmap);
    }
    if(m_hOldBitmap  &&  m_pDeviceContextMemory)
    {
	SelectObject((HDC)m_pDeviceContextMemory, m_hOldBitmap);
    }
    m_hOldBitmap = NULL;
    
    if(m_LPBITMAPINFO)
    {
	delete m_LPBITMAPINFO;
	m_LPBITMAPINFO = NULL;
    }
    //XXXEH- clean up m_pBits here?  Or, no need to clean it up...?;
#endif

    if(m_pDeviceContextMemory)
    {
	DeleteDC((HDC)m_pDeviceContextMemory);
    }
#elif defined(_MACINTOSH) || defined(_MAC_UNIX)
    if(m_pOffScreenWorld)
    {
	DisposeGWorld(m_pOffScreenWorld);
	m_pOffScreenWorld=NULL;
    }
#else
    //platform-specific code needed here.
#endif

#if defined(_UNIX) && !defined(_MAC_UNIX)
    HX_DELETE(m_font_dict);
#endif
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//	 TextWindow::clear_all()
//
// Purpose:
//	Erases all text that has already been received from the stream and clears
//	all text attribute stacks so that the next text that arrives will be 
//	rendered with the default font size, color, bold, ...etc, until new
//	attribute tags are received.
//
void TextWindow::clear_all()
{
    reset(); //::TextWindowBase's function.
    
    clearURL();

    m_ulTimeOfLastTimeSync=0L;
}


/////////////////////////////////////////////////////////////////////////////
// Methods:
//	TextWindow::getURL()
//	TextWindow::hasValidURL()
//	TextWindow::setURL(_CHAR* pNewURL, ULONG32 newURLstrlen)
//	TextWindow::clearURL()
//
// Purpose:
//	These functions keep track of
//
// Returns:
//	returns TRUE upon success, FALSE if (sr < MIN_SCROLLRATE) or if
//	(sr > MAX_SCROLLRATE):
//
_CHAR* TextWindow::getURL()
{
    return m_pURL;   //note: this may be NULL.
}

ULONG32 TextWindow::getLenURLbuf()
{
    return m_ulLenURLbuf;
}


BOOL TextWindow::hasValidURL()
{
    return (NULL!=m_pURL  &&  0<m_ulLenURLbuf);
}

//returns FALSE if pNewURL is empty or NULL:
BOOL TextWindow::setURL(_CHAR* pNewURL, ULONG32 newURLstrlen)
{
    if(!pNewURL  ||  !newURLstrlen)
    {
	return FALSE;
    }
    
    if(m_pURL)
    {
	delete [] m_pURL;
	m_pURL = NULL;
    }
    m_ulLenURLbuf = 0L;

    m_pURL = new _CHAR[newURLstrlen+1];
    HX_ASSERT(m_pURL);
    if(!m_pURL)
    {
	return FALSE;
    }

    m_pURL[newURLstrlen] = '\0'; //not needed, but do this anyway.
    m_ulLenURLbuf = newURLstrlen;

    stringCopy(m_pURL, pNewURL, newURLstrlen);
    
    return TRUE;
}

void TextWindow::clearURL()
{
    if(m_pURL)
    {
	delete [] m_pURL;
	m_pURL = NULL;
    }
    m_ulLenURLbuf = 0L;
    m_ulTargetOfURL = URL_TARGET_INVALID;
}




/////////////////////////////////////////////////////////////////////////////
// Method:
//   TextWindowBase::setScrollRate(LONG32 sr)
//
// Purpose:
//  Sets the rate, in pixels per second, that the text will move vertically
//  within the window.  sr can be positive or negative; if positive, the text
//  moves toward the top of the screen.  Affects motion of all text,
//  including text that has already been received from the stream.
//
// Returns:
//  returns TRUE upon success, FALSE if (sr < MIN_SCROLLRATE) or if
//  (sr > MAX_SCROLLRATE):
//
BOOL TextWindowBase::setScrollRate(LONG32 sr)
{
    if(sr>=MIN_SCROLLRATE  &&  sr<=MAX_SCROLLRATE)
    {
	m_scrollRate = sr;
    }
    else
    {
	sr = SCROLLRATE_UNSPECIFIED;
    }
    return (sr == m_scrollRate);
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::setScrollRate(_CHAR* pBuf, ULONG32 bufLen)
//
// Purpose:
//  Sets the rate, in pixels per second, that the text will move vertically
//  within the window based on the conversion of the contents of buf into
//  a LONG32 value.  This value	can be positive or negative; if positive,
//  the text moves toward the top of the screen.  Affects motion of all text,
//   including text that has already been received from the stream.
//
// Returns:
//  returns TRUE upon success, FALSE if the converted value from buf is
//  less than MIN_SCROLLRATE or if it is greater than MAX_SCROLLRATE, or 
//  if buf is otherwise invalid:
//
BOOL TextWindowBase::setScrollRate(_CHAR* pBuf, ULONG32 bufLen)
{	
    BOOL didErrorOccur = FALSE;
    LONG32 tmpScrollRate = string_to_LONG32(pBuf, didErrorOccur);
    if(didErrorOccur)
    {
	m_scrollRate = SCROLLRATE_UNSPECIFIED;
    }
    else
    {
	didErrorOccur = setScrollRate(tmpScrollRate);
    }
    return (!didErrorOccur);
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//   TextWindowBase::setCrawlRate(LONG32 cr)
//
// Purpose:
//  Sets the rate, in pixels per second, that the text will move horizontally
//  within the window.  "cr" can be positive or negative; if positive, the
//  text moves toward the left of the screen.  Affects motion of all text,
//  including text that has already been received from the stream.
//
// Returns:
//  returns TRUE upon success, FALSE if (sr < MIN_CRAWLRATE) or if
//  (sr > MAX_CRAWLRATE):
//
BOOL TextWindowBase::setCrawlRate(LONG32 cr)
{
    if(cr>=MIN_CRAWLRATE  &&  cr<=MAX_CRAWLRATE)
    {
	m_crawlRate = cr;
    }
    return (cr == m_crawlRate);
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//   TextWindowBase::setCrawlRate(_CHAR* pBuf, ULONG32 bufLen)
//
// Purpose:
//  Sets the rate, in pixels per second, that the text will move horizontally
//  within the window based on the conversion of the contents of buf into
//  a LONG32 value.  This value	can be positive or negative; if positive,
//  the text moves toward the left of the screen.  Affects motion of all
//  text, including text that has already been received from the stream.
//
// Returns:
//  returns TRUE upon success, FALSE if the converted value from buf is
//  less than MIN_SCROLLRATE or if it is greater than MAX_SCROLLRATE, or 
//  if buf is otherwise invalid:
//
BOOL TextWindowBase::setCrawlRate(_CHAR* pBuf, ULONG32 bufLen)
{	
    BOOL didErrorOccur = FALSE;
    LONG32 tmpCrawlRate = string_to_LONG32(pBuf, didErrorOccur);
    if(didErrorOccur)
    {
	m_crawlRate = CRAWLRATE_UNSPECIFIED;
    }
    else
    {
	didErrorOccur = setCrawlRate(tmpCrawlRate);
    }
    return (!didErrorOccur);
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::setBackgroundColor
//  (
//	_CHAR* pColorName,
//	ULONG32 colorNameLen
//  )
//
// Purpose:
//  Sets the background color of the window from the value in 
//  pColorName, which can contain any of the following color names:
//  	black, brown, green, cyan, darkblue, purple, teal, gray, red,
//  	lightgreen, yellow, blue, magenta, lightblue, and white.
//  or, alternatively, any color of the form: #RRGGBB where RR, GG, and BB
//  are hex values from 0x00 to 0xFF representing the Red, Green, and Blue
//  components of the color.  These values can be different lengths (from
//  one to six) as follows: #B, #BB, #GBB, #GGBB, #RGGBB, and #RRGGBB, for
//  example, #7CF means a Red compolnent of 0x00, a Green component of
//  0x07, and a Blue component of 0xCF.
//
// Returns:
//  returns FALSE if pColorName contains an unrecognized color or hex value:
//
BOOL TextWindowBase::setBackgroundColor(
	_CHAR* pColorName, ULONG32 colorNameLen)
{
    //first look for red, black, ...etc:
    if(!convertColorNameToCOLORTYPE(pColorName, colorNameLen,
		    m_backgroundColor))
    {	
	//That failed, so look for value of type #RRGGBB:
	return (setBackgroundColorFromHexValString(
			pColorName, colorNameLen) );
    }
    return TRUE;
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::setBackgroundColorFromHexValString
//  (
//  	_CHAR* pColorName,
//  	ULONG32 colorNameLen
//  )
//
// Purpose:
//  Sets the background color of the window from the value in 
//  pColorName, which can contain any color of the form: #RRGGBB where RR,GG,
//  and BB are hex values from 0x00 to 0xFF representing the Red, Green, and
//  Blue components of the color.  These values can be different lengths
//  (from one to six) as follows: #B, #BB, #GBB, #GGBB, #RGGBB, and #RRGGBB,
//  for example, #7CF means a Red compolnent of 0x00, a Green component of
//  0x07, and a Blue component of 0xCF.
//
// Returns:
//  returns FALSE if pColorName contains an invalid hex value:
//
BOOL TextWindowBase::setBackgroundColorFromHexValString
(
    _CHAR* pColorHexVal,
    ULONG32 colorHexValLen
)
{
    return (convertColorValStringToCOLORTYPE(
	    pColorHexVal, colorHexValLen, m_backgroundColor) );
}

// Sets the background opacity. Returns TRUE if ulOpacity <= 255,
// FALSE otherwise. 255 = fully opaque, 0 = fully transparent.
// 255 is the default background opacity value.
BOOL TextWindowBase::setBackgroundOpacity(UINT32 ulOpacity)
{
    BOOL bRet = FALSE;

    if (ulOpacity <= 255)
    {
        m_ulBackgroundOpacity = ulOpacity;
        bRet                  = TRUE;
    }

    return bRet;
}

// Sets the media opacity. Returns TRUE if ulOpacity <= 255,
// FALSE otherwise. 255 = fully opaque, 0 = fully transparent.
// 255 is the default media opacity value.
BOOL TextWindowBase::setMediaOpacity(UINT32 ulOpacity)
{
    BOOL bRet = FALSE;

    if (ulOpacity <= 255)
    {
        m_ulMediaOpacity = ulOpacity;
        bRet             = TRUE;
    }

    return bRet;
}

BOOL TextWindowBase::setChromaKey(UINT32 ulColor)
{
    BOOL bRet = TRUE;

    m_ulChromaKey     = ulColor & 0x00FFFFFF;
    m_bIsChromaKeySet = TRUE;

    return bRet;
}

BOOL TextWindowBase::setChromaKeyTolerance(UINT32 ulTol)
{
    BOOL bRet = TRUE;

    m_ulChromaKeyTolerance = ulTol & 0x00FFFFFF;

    return bRet;
}

BOOL TextWindowBase::setChromaKeyOpacity(UINT32 ulOpacity)
{
    BOOL bRet = FALSE;

    if (ulOpacity <= 255)
    {
        m_ulChromaKeyOpacity = ulOpacity;
        bRet                 = TRUE;
    }

    return bRet;
}

/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::setLinkColor
//  (
//  	_CHAR* pColorName,
//  	ULONG32 colorNameLen
//  )
//
// Purpose:
//  Sets the color of hyperlinked text from the value in 
//  pColorName, which can contain any color of the form: #RRGGBB where RR,GG,
//  and BB are hex values from 0x00 to 0xFF representing the Red, Green, and
//  Blue components of the color.  These values can be different lengths
//  (from one to six) as follows: #B, #BB, #GBB, #GGBB, #RGGBB, and #RRGGBB,
//  for example, #7CF means a Red compolnent of 0x00, a Green component of
//  0x07, and a Blue component of 0xCF.
//
// Returns:
//  returns FALSE if pColorName contains an unrecognized color or hex value:
//
BOOL TextWindowBase::setLinkColor(_CHAR* pColorName, ULONG32 colorNameLen)
{
    //first look for red, black, ...etc:
    if(!convertColorNameToCOLORTYPE(pColorName, colorNameLen,
		    m_linkColor))
    {	
	//That failed, so look for value of type #RRGGBB:
	return (convertColorValStringToCOLORTYPE(
		pColorName, colorNameLen, m_linkColor) );
    }
    return TRUE;
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::setHeight(_CHAR* pBuf, ULONG32 bufLen)
//
// Purpose:
//  Sets the height, in pixels, of the window before it is created.
//  The contents of buf can be a number, like "144".
//  //as of 1997/06/02, it can NO LONGER be a percentage, like "56%".
//
// Returns:
//  returns FALSE, with the height set to DEFAULT_WINDOWWIDTH, if pBuf
//  converts to an invalid ULONG32 value, or if it is out of bounds;
//  //as of 1997/06/02, it can NO LONGER be a percentage, like "56%", so:
//  if pBuf contains a value, in "Y%" form, the % is ignored, so 56% would
//  cause a return value of 56 (pixels).
//
BOOL TextWindowBase::setHeight(_CHAR* pBuf, ULONG32 bufLen)
{	
    BOOL didErrorOccur = FALSE;
    BOOL doPercentOfParentWindowHt = FALSE;

    if(bufLen > 1)
    {
	if(pBuf[bufLen-1] == '%')
	{
	    doPercentOfParentWindowHt = TRUE;
	    pBuf[bufLen-1]='\0';
	}
    }
    m_height = string_to_ULONG32(pBuf, didErrorOccur);
    if(didErrorOccur)
    {
	m_height = DEFAULT_WINDOWHEIGHT;
	return FALSE;
    }

    return (!didErrorOccur);
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::setWidth(_CHAR* pBuf, ULONG32 bufLen)
//
// Purpose:
//  Sets the width, in pixels, of the window before it is created.
//  The contents of buf can be a number, like "144"
//  //as of 1997/06/02, it can NO LONGER be a percentage, like "56%".
//
// Returns:
//  returns FALSE, with width set to DEFAULT_WINDOWWIDTH, if pBuf converts
//  to an invalid ULONG32 value or if it is out of bounds;
//  //as of 1997/06/02, it can NO LONGER be a percentage, like "56%", so:
//  if pBuf contains a value, in "X%" form, the % is ignored, so 56% would
//  cause a return value of 56 (pixels).
//
BOOL TextWindowBase::setWidth(_CHAR* pBuf, ULONG32 bufLen)
{   /*  Can only be set from within <WINDOW > header tag,
     *  before window is created: */
    BOOL didErrorOccur = FALSE;
    BOOL doPercentOfParentWindowWidth = FALSE;
    if(bufLen > 1)
    {
	if(pBuf[bufLen-1] == '%')
	{
	    doPercentOfParentWindowWidth = TRUE;
	    pBuf[bufLen-1]='\0';
	}
    }
    m_width = string_to_ULONG32(pBuf, didErrorOccur);
    if(didErrorOccur)
    {
	m_width = DEFAULT_WINDOWWIDTH;
	return FALSE;
    }

    return (!didErrorOccur);
}


// /Added the following two methods to handle in-line SMIL plain-text data
// URLs which don't have an intrinsic width/height but rely on the SMIL
// layout to provide them with w,h (PR 59951 and others):
HX_RESULT
TextWindowBase::overrideDefaultWindowWidth(LONG32 ulNewWindowWidth)
{
    HX_RESULT hxrslt = HXR_OK;

    if (ulNewWindowWidth <= 0)
    {
	hxrslt = HXR_INVALID_PARAMETER;
    }
    else
    {
	m_width = ulNewWindowWidth;
    }

    return hxrslt;
}

HX_RESULT
TextWindowBase::overrideDefaultWindowHeight(LONG32 ulNewWindowHeight)
{
    HX_RESULT hxrslt = HXR_OK;

    if (ulNewWindowHeight <= 0)
    {
	hxrslt = HXR_INVALID_PARAMETER;
    }
    else
    {
	m_height = ulNewWindowHeight;
    }

    return hxrslt;
}



//Added the following function so author can
// specify whether or not hyperlinks get underlined automatically:
BOOL TextWindowBase::setUnderlineHyperlinks(_CHAR* pBuf, ULONG32 bufLen)
{
    //Can only be set from within <WINDOW > header tag,
    // before window is created:
    BOOL didErrorOccur = FALSE;
    m_bUnderlineHyperlinks = string_to_BOOL(pBuf, bufLen, didErrorOccur);
    if(didErrorOccur)
    {
	m_bUnderlineHyperlinks = DEFAULT_DO_UNDERLINE_HYPERLINKS;
    }
    return (!didErrorOccur);
}

//Added the following function so author can
// specify the content version of the rt file:
BOOL TextWindowBase::setContentVersion(_CHAR* pBuf, ULONG32 bufLen)
{
    //Can only be set in the header:
    BOOL didErrorOccur = FALSE;
    double fVersion = string_to_double(pBuf, didErrorOccur,
	    m_ulContentMajorVersion, m_ulContentMinorVersion);
    if(didErrorOccur)
    {
	m_ulContentMajorVersion = 0L;
	m_ulContentMinorVersion = 0L;
    }
    return (!didErrorOccur);
}

//Added the following function so author can
// specify whether or not wordwrap is performed:
BOOL TextWindowBase::setWordwrap(_CHAR* pBuf, ULONG32 bufLen)
{
    //Can only be set from within <WINDOW > header tag,
    // before window is created:
    BOOL didErrorOccur = FALSE;
    m_bUseWordwrap = string_to_BOOL(pBuf, bufLen, didErrorOccur);		
    if(didErrorOccur)
    {
	m_bUseWordwrap = DEFAULT_USE_WORDWRAP;
    }
    return (!didErrorOccur);
}


//Added the following function to keep track of live src:
BOOL TextWindowBase::setIsLiveSource(_CHAR* pBuf, ULONG32 bufLen)
{
    //Can only be set from within <WINDOW > header tag,
    // before window is created:
    BOOL didErrorOccur = FALSE;
    m_bIsLiveSource = string_to_BOOL(pBuf, bufLen, didErrorOccur);		
    if(didErrorOccur)
    {
	m_bIsLiveSource = DEFAULT_IS_LIVE_SOURCE;
    }
    return (!didErrorOccur);
}


HX_RESULT
TextWindowBase::setDefaultFontFaceString(const char* pszFace)
{
    HX_RESULT hxrslt = HXR_FAILED;
    if (pszFace)
    {
	if (m_pDefaultFontFaceString)
	{
	    delete [] m_pDefaultFontFaceString;
	    m_pDefaultFontFaceString = NULL;
	}

	INT32 lLen = strlen(pszFace);
	if (lLen > 0)
	{
	    m_pDefaultFontFaceString = new char[lLen + 1];
	    if (m_pDefaultFontFaceString)
	    {
		strcpy(m_pDefaultFontFaceString, pszFace); /* Flawfinder: ignore */
	    }
	}
	// /else leave it NULL.
    }

    return hxrslt;
}


HX_RESULT
TextWindowBase::setDefaultCharsetString(const char* pszCharset)
{
    HX_RESULT hxrslt = HXR_FAILED;
    if (pszCharset)
    {
	if (m_pDefaultCharsetString)
	{
	    delete [] m_pDefaultCharsetString;
	    m_pDefaultCharsetString = NULL;
	}

	INT32 lLen = strlen(pszCharset);
	if (lLen > 0)
	{
	    m_pDefaultCharsetString = new char[lLen + 1];
	    if (m_pDefaultCharsetString)
	    {
		// /Allow it to be case-insensitive by converting to lower-case
		const char* pszChSet = pszCharset;
		char* pszDefChSet = m_pDefaultCharsetString;

		for (INT32 lI = 0; lI<lLen; lI++, pszChSet++, pszDefChSet++)
		{
		    if (*pszChSet>='A'  &&  *pszChSet<='Z')
		    {
			*pszDefChSet = *pszChSet-'A'+'a';
		    }
		    else
		    {
			*pszDefChSet = *pszChSet;
		    }
		}
		*pszDefChSet = '\0'; // /NULL terminate it.
	    }
	}
	// /else leave it NULL.
    }

    return hxrslt;
}


HX_RESULT
TextWindowBase::getCharsetULONG32(const char* pszCharset,
				      UINT16 uiMaxLevelSupported,
				      ULONG32& ulCharset /*OUT*/)
{
    HX_RESULT hxrslt = HXR_OK;

    if (!pszCharset)
    {
	hxrslt = HXR_INVALID_PARAMETER;
    }
    else
    {
	TextParser* pTP = new TextParser((TextWindow*)this);
	if (pTP)
	{
	    hxrslt = pTP->convertCharsetNameToCharsetULONG32(pszCharset,
		    strlen(pszCharset), uiMaxLevelSupported,
		    ulCharset);
	}
	else
	{
	    hxrslt = HXR_OUTOFMEMORY;
	}
	HX_DELETE(pTP);
    }

    return hxrslt;
}


HX_RESULT
TextWindowBase::setUserPrefRelativeTextSizing(ULONG32 ulTextSizeScaleFactor)
{
    m_bUserTextSizingPrefIsSet = TRUE;
    m_bUserPrefSizeIsRelative = TRUE;
    m_ulUserPrefTextSizeOrScaleFactorPct = ulTextSizeScaleFactor;
    m_ulDefaultPointSize = (ULONG32)((float)( (double)((float)(m_ulDefaultPointSize)) *
	    ((double)((float)(m_ulUserPrefTextSizeOrScaleFactorPct)) / 100.0) ));

    return HXR_OK;
}

HX_RESULT
TextWindowBase::setUserPrefAbsoluteTextSizing(ULONG32 ulTextPointSize)
{
    m_bUserTextSizingPrefIsSet = TRUE;
    m_bUserPrefSizeIsRelative = FALSE;
    m_ulDefaultPointSize = m_ulUserPrefTextSizeOrScaleFactorPct =
	    ulTextPointSize;

    m_bUserTextSizingPrefIsSet = TRUE;

    return HXR_OK;
}

// /XXXEH- check if is within reasonable bounds before setting:
// /Returns HXR_OK if and only if it does set the base text point
// size to the requested value:
HX_RESULT
TextWindowBase::setDefaultPtSize(ULONG32 ulPtSz)
{
    HX_RESULT hxrslt = HXR_OK;

    if (!m_bUserTextSizingPrefIsSet)
    {
	m_ulDefaultPointSize = ulPtSz;
    }
    else if (m_bUserPrefSizeIsRelative)
    {
	double dPtSz = (double)((float)ulPtSz);
	double dUserPrefTextSizeOrScaleFactorPct =
		(double)((float)m_ulUserPrefTextSizeOrScaleFactorPct);

	// /If relative, then m_ulUserPrefTextSize is a scaling factor:
	m_ulDefaultPointSize = (ULONG32)((float)(
		dPtSz * (dUserPrefTextSizeOrScaleFactorPct / 100.0) ));
    }
    else // /user pref is absolute, so we can't change it:
    {
	hxrslt = HXR_FAILED;
    }

    return hxrslt;
}

// /Scales the default font size by the given scale factor.
// Returns HXR_OK if and only if it set the default text point
// size to the requested value:
HX_RESULT
TextWindowBase::scaleDefaultPtSize(double dScaleFactor)
{
    HX_RESULT hxrslt = HXR_OK;

    if (!m_bUserTextSizingPrefIsSet)
    {
	m_ulDefaultPointSize = (ULONG32)((float)(
	    (double)((float)(m_ulDefaultPointSize)) * dScaleFactor) );
    }
    else if (m_bUserPrefSizeIsRelative)
    {
	double dDefaultPointSize = (double)((float)m_ulDefaultPointSize);
	double dUserPrefTextSizeOrScaleFactorPct =
		(double)((float)m_ulUserPrefTextSizeOrScaleFactorPct);

	// /If relative, then m_ulUserPrefTextSize is a scaling factor:
	m_ulDefaultPointSize = (ULONG32)((float)( dDefaultPointSize *
		(dUserPrefTextSizeOrScaleFactorPct / 100.0) *
		dScaleFactor));  // /e.g., 16pt * "larger" * 1.20 = 23pt
    }
    else // /user pref is absolute, so we can't change it:
    {
	hxrslt = HXR_FAILED;
    }

    return hxrslt;
}


// /Sets default bold-ness (of plain text):
void
TextWindowBase::setDefaultFontWeight(UINT32 ulWeight)
{
    // /Make it 100, 200, ..., 900 only:
    if (ulWeight < 100)
    {
	ulWeight = 100;
    }
    else
    {
	ulWeight %= 1000;
	ulWeight = (ulWeight+50) / 100; // /Add 50 to round up >= x.50
	ulWeight *= 100;
    }
    m_ulDefaultFontWeight = ulWeight;
}




/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::setLoop(_CHAR* pBuf, ULONG32 bufLen)
//
// Purpose:
//  Sets the m_loop member to TRUE or FALSE, depending on the conversion 
//  of the value in buf.  For example, if buf contains "FALSE", this function
//  will set m_loop to FALSE, meaning that the text that the window displays
//  will not loop back and restart displaying prior text when it has all
//  scrolled or crawled out of the window's client area.  If "TRUE" is sent,
//  then this window has the loop property and old text will loop back and 
//  be redisplayed when the stream stops sending new text.
//
// Returns:
//  returns FALSE, with m_loop set to DEFAULT_DOLOOP, if buf converts to
//  an invalid BOOL value:
//
BOOL TextWindowBase::setLoop(_CHAR* pBuf, ULONG32 bufLen)
{	
    BOOL didErrorOccur = FALSE;
    m_loop = string_to_BOOL(pBuf, bufLen, didErrorOccur);		
    if(didErrorOccur)
    {
	m_loop = DEFAULT_DOLOOP;
    }
    return (!didErrorOccur);
}


/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::SetExtraSpacesHandling(_CHAR* pBuf, ULONG32 bufLen)
//
BOOL TextWindowBase::SetExtraSpacesHandling(_CHAR* pBuf, ULONG32 bufLen)
{
    BOOL didErrorOccur = FALSE;    
    m_bDontIgnoreExtraSpaces =
	    string_to_BOOL(pBuf, bufLen, didErrorOccur);
    if(didErrorOccur)
    {
	m_bDontIgnoreExtraSpaces = DEFAULT_DONT_IGNORE_EXTRA_SPACES;
    }
    return (!didErrorOccur);
}


/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::setType(_CHAR* pBuf, ULONG32 bufLen)
//
// Purpose:
//  Sets the window's type, which has particular rendering characteristics
//  and default sizes .etc associated with it.
//  buf can contain one of the following:
//   - "GENERIC" which is a 320x180 window with no text motion.
//   - "TICKERTAPE" which creates a marquee-type window where all text
//  	is displayed on the same line and moves to the left at a set rate,
//	and loops, by default.  Is different from "MARQUEE" because it
//	has "upper" and "lower" text items instead of centering all text
//	vertically.
//   - "SCROLLINGNEWS" which creates a window where text scrolls toward
//  	the top of the screen, with no looping, by default.
//   - "TELEPROMPTER" which creates a window with no set scrollrate but, when
//	a new item of text arrives, it scrolls the currently-displayed text
//	upward to make room for the new text.
//   - "MARQUEE" which creates a marquee-type window where all text
//  	is displayed on the same line and moves to the left at a set rate,
//	and loops, by default.  Is different from "TICKERTAPE" because it
//	centers all text vertically instead of having "upper" and "lower"
//	text items.
//
// Returns:
//  returns FALSE, with m_type set to DEFAULT_TYPE, if buf converts to
//  an invalid type value:
//
BOOL TextWindowBase::setType(_CHAR* pBuf, ULONG32 bufLen)
{   /*  Can only be set from within <WINDOW > header tag,
     *  before window is created: */
    if(!pBuf  ||  !bufLen)
    {
	m_type = DEFAULT_TYPE;
    }
    else
    {
	_CHAR tmpChar = '\0';
	if(pBuf[0] == '\"') //get "rid" of starting quote char:
	{
	    pBuf++;
	    bufLen--;
	}
	if(pBuf[bufLen-1] == '\"')  //get rid of ending quote char:
	{
	    tmpChar = '\"';
	    pBuf[bufLen-1] = '\0';
	    bufLen--;
	}
	convertToUpperCase(pBuf, bufLen);
	
	if(!stringCompare(pBuf, bufLen, "TICKERTAPE", 10))
	{
	    m_type = TYPE_TICKERTAPE;
	}
	else if(!stringCompare(pBuf, bufLen, "SCROLLINGNEWS", 13)  ||
		//(keep this for legacy reasons:)
		!stringCompare(pBuf, bufLen, "MISCELLANEOUSNEWS", 17) ) 
	{
	    m_type = TYPE_SCROLLINGNEWS;
	}
	else if(!stringCompare(pBuf, bufLen, "TELEPROMPTER", 12)  ||
		!stringCompare(pBuf, bufLen, "AUTOSCROLL", 10) )
	{
	    m_type = TYPE_TELEPROMPTER;
	}
	else if(!stringCompare(pBuf, bufLen, "MARQUEE", 7))
	{
	    m_type = TYPE_MARQUEE;
	}
	else
	{
	    m_type = DEFAULT_TYPE;
	}
	
	if(tmpChar == '\"')
	{   //restore the ending quote char:
	    bufLen++;
	    pBuf[bufLen-1] = tmpChar;
	}
    }
    return (DEFAULT_TYPE != m_type); //Error occurred if m_type==DEFAULT_TYPE
}


/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::setDebugFlags(_CHAR* pBuf, BOOL& didErrorOccur)
//
// Purpose:
//  Converts a _CHAR* buffer into m_ulDebugFlags (a ULONG32 value).  pBuf can
//  contain a base-10 number, like 65537, or a hex number that starts with
//  "0x", like 0x00010001 (which equals 65537).
//
// Returns:
//  returns FALSE if fails for any reason.
//
#if defined(_DEBUG)
BOOL TextWindowBase::setDebugFlags(_CHAR* pBuf, ULONG32 bufLen)
{   /*  Can only be set from within <WINDOW > header tag,
     *  before window is created: */
    BOOL didErrorOccur = FALSE;
    BOOL bIsHex = FALSE;
    m_ulDebugFlags = 0L;
    if(bufLen > 2)
    {
	if(pBuf[0] == '0'  &&  (pBuf[1] == 'X'  ||  pBuf[1] == 'x'))
	{
	    bIsHex = TRUE;
	    pBuf = &pBuf[2];
	    bufLen -= 2;

	    _CHAR* pTmp = pBuf;
	    if(!pTmp)
	    {
		return FALSE;
	    }
	    do
	    {
		_CHAR tmpCh = *pTmp;
		if(tmpCh >= '0'  &&  tmpCh <='9')
		{
		    m_ulDebugFlags<<=4;
		    m_ulDebugFlags += tmpCh-'0';
		}
		else if(tmpCh >= 'A'  &&  tmpCh <='F')
		{
		    m_ulDebugFlags<<=4;
		    m_ulDebugFlags += tmpCh-'A' + 0xAL;
		}
		else if(tmpCh >= 'a'  &&  tmpCh <='f')
		{
		    m_ulDebugFlags<<=4;
		    m_ulDebugFlags += tmpCh-'a' + 0xAL;
		}
	    } while(*(++pTmp));
	    return TRUE;
	}
    }
    m_ulDebugFlags = string_to_ULONG32(pBuf, didErrorOccur);
    if(didErrorOccur)
    {
	return FALSE;
    }

    return (!didErrorOccur);
}
#endif


/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::string_to_ULONG32(_CHAR* pBuf, BOOL& didErrorOccur)
//
// Purpose:
//  Converts a _CHAR* buffer into a ULONG32 value.	
//
// Returns:
//  returns the ULONG32 value as converted from buf.  
//  The referenced value, didErrorOccur, is set to FALSE, and the returned
//  value is set to 0L, if an error occurred.
//
ULONG32 TextWindowBase::string_to_ULONG32(_CHAR* pBuf, BOOL& didErrorOccur)
{
    return(ULONG32)(string_to_LONG32(pBuf, didErrorOccur));
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::string_to_LONG32(_CHAR* pBuf, BOOL& didErrorOccur)
//
// Purpose:
//  Converts a _CHAR* buffer into a LONG32 value.	
//
// Returns:
//  returns the LONG32 value as converted from buf.  
//  The referenced value, didErrorOccur, is set to FALSE, and the returned
//  value is set to 0L, if an error occurred.
//
LONG32 TextWindowBase::string_to_LONG32(_CHAR* pBuf, BOOL& didErrorOccur)
{
    didErrorOccur = FALSE;
    if(pBuf)
    {
	ULONG32 bufLen = strlen(pBuf);
	LONG32 tmp = 0L;
	BOOL bStartQuoteWasFound = FALSE;
	BOOL bEndQuoteWasFound = FALSE;

	//This function is in parsing.cpp|h:
	if(lookForStartAndEndQuotesOfString(pBuf, bufLen,
		bStartQuoteWasFound, bEndQuoteWasFound))
	{
	    if(bEndQuoteWasFound)
	    {
		pBuf[bufLen-1] = '\0';
		bufLen--;
	    }
	    if(bStartQuoteWasFound)
	    {
		pBuf++;
		bufLen--;
	    }
	    
	    HX_ASSERT(strlen(pBuf) == bufLen);
	}

#if defined(_CHARsizeInBytesIs1)
	tmp = atol(pBuf);
#else
	tmp = _wtol(pBuf);
#endif
	    
	if(bEndQuoteWasFound)
	{
	    //restore the ending char:
	    bufLen++;
	    pBuf[bufLen-1] = '\"';
	}

	return (tmp);
    }
    didErrorOccur = TRUE;
    return 0L;
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::string_to_double(_CHAR* pBuf, BOOL& didErrorOccur,
//	    ULONG32& ulIntegerPart, ULONG32& ulDecimalPart)
//
// Purpose:
//  Converts a _CHAR* buffer into a double value.	
//
// Returns:
//  returns the LONG32 value as converted from buf.  
//  The referenced value, didErrorOccur, is set to FALSE, and the returned
//  value is set to 0L, if an error occurred.
//
double TextWindowBase::string_to_double(_CHAR* pBuf, BOOL& didErrorOccur,
	ULONG32& ulIntegerPart, ULONG32& ulDecimalPart)
{
    didErrorOccur = FALSE;
    if(pBuf)
    {
	ULONG32 bufLen = strlen(pBuf);
	if(!bufLen)
	{
	    didErrorOccur = TRUE;
	    return 0.0;
	}
	ulIntegerPart = 0L;
	ulDecimalPart = 0L;
	double fRetval = 0.0;
	BOOL bStartQuoteWasFound = FALSE;
	BOOL bEndQuoteWasFound = FALSE;

	//This function is in parsing.cpp|h:
	if(lookForStartAndEndQuotesOfString(pBuf, bufLen,
		bStartQuoteWasFound, bEndQuoteWasFound))
	{
	    if(bEndQuoteWasFound)
	    {
		pBuf[bufLen-1] = '\0';
		bufLen--;
	    }
	    if(bStartQuoteWasFound)
	    {
		pBuf++;
		bufLen--;
	    }
	    
	    HX_ASSERT(strlen(pBuf) == bufLen);
	    if(!bufLen)
	    {
		didErrorOccur = TRUE;
		return 0.0;
	    }
	}

	_CHAR* pBufHigh = pBuf;
	_CHAR* pBufLow = NULL;
	_CHAR* pBufDot = NULL;
	
	//look for decimal point (don't use strchr() because _CHAR
	// may not be same size as char*:
	_CHAR* pBufTmp = pBuf;
	for (ULONG32 ulcnt=0L; ulcnt<bufLen; ulcnt++, pBufTmp++)
	{
	    if('.' == *pBufTmp)
	    {
		*pBufTmp = '\0';
		pBufLow = pBufTmp+1;
		pBufDot = pBufTmp;
		break;
	    }
	}

#if defined(_CHARsizeInBytesIs1)
	if(pBufHigh  &&  *pBufHigh)
	{
	    ulIntegerPart = (ULONG32)atol(pBufHigh);
	}
	if(pBufLow  &&  *pBufLow)
	{
	    ulDecimalPart = (ULONG32)atol(pBufLow);
	}
#else
	if(pBufHigh  &&  *pBufHigh)
	{
	    ulIntegerPart = (ULONG32)_wtol(pBufHigh);
	}
	if(pBufLow  &&  *pBufLow)
	{
	    ulDecimalPart = (ULONG32)_wtol(pBufLow);
	}
#endif
	    
	if(bEndQuoteWasFound)
	{
	    //restore the ending char:
	    bufLen++;
	    pBuf[bufLen-1] = '\"';
	}
	if(pBufDot)
	{
	    //restore the decimal point:
	    *pBufDot = '.';
	}

	fRetval = double(ulIntegerPart);
	if(ulDecimalPart  &&  strlen(pBufLow))
	{
	    fRetval += double(ulDecimalPart) / double(strlen(pBufLow));
	}
	return (fRetval);
    }
    didErrorOccur = TRUE;
    return 0L;
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::string_to_BOOL(
//	    _CHAR* pBuf, ULONG32 bufLen, BOOL& didErrorOccur)
//
// Purpose:
//  Converts a _CHAR* buffer into a BOOL value.	
//
// Returns:
//  returns the BOOL value as converted from buf.  
//  The referenced value, didErrorOccur, is set to FALSE, and the returned
//  value is set to FALSE, if an error occurred
//
BOOL TextWindowBase::string_to_BOOL(
	_CHAR* pBuf, ULONG32 bufLen, BOOL& didErrorOccur)
{
    didErrorOccur = FALSE;
    if(pBuf)
    {
	BOOL bRetVal = FALSE;
	BOOL bStartQuoteWasFound = FALSE;
	BOOL bEndQuoteWasFound = FALSE;

	//This function is in parsing.cpp|h:
	if(lookForStartAndEndQuotesOfString(pBuf, bufLen,
		bStartQuoteWasFound, bEndQuoteWasFound))
	{
	    HX_ASSERT(bStartQuoteWasFound  ||  bEndQuoteWasFound);

	    if(bEndQuoteWasFound)
	    {
		pBuf[bufLen-1] = '\0';
		bufLen--;
	    }
	    if(bStartQuoteWasFound)
	    {
		pBuf++;
		bufLen--;
		convertToUpperCase(pBuf, bufLen);
	    }
	    
	    HX_ASSERT(strlen(pBuf) == bufLen);
	}
	    
	if(!stringCompare(pBuf, bufLen, "TRUE", 4)  ||
		!stringCompare(pBuf, bufLen, "YES", 3)  ||
		!stringCompare(pBuf, bufLen, "USE", 3)  ||
		!stringCompare(pBuf, bufLen, "1", 1))
	{
	    bRetVal = TRUE;
	}
	else if(!stringCompare(pBuf, bufLen, "FALSE", 5)  ||
		!stringCompare(pBuf, bufLen, "NO", 2)  ||
		!stringCompare(pBuf, bufLen, "IGNORE", 6)  ||
		!stringCompare(pBuf, bufLen, "0", 1))
	{
	    bRetVal = FALSE;
	}

	if(bEndQuoteWasFound)
	{
	    //restore the ending char:
	    bufLen++;
	    pBuf[bufLen-1] = '\"';
	}

	return bRetVal;
    }
    didErrorOccur = TRUE;
    return (!didErrorOccur);
}




/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindowBase::parseHeaderTag(
//	    _CHAR* pHeaderTagBuf, ULONG32 headerTagBufLen
//	    , RTFileFormatMarkupParsingMajorVersion)
//
// Purpose:
//  Takes <"WINDOW [someString]"> in pHeaderTagBuf & parses the <WINDOW ..>
//  tag values out of "[someString]" and places those values in the
//  TextWindow's associated variables and then creates the window as
//  specified by these values.
//
// NOTE: parameter 2 is same as strlen(parameter 1) but parameter 1 is
//  assumed to have a terminating '\0' char at the end, i.e., there 
//  should be one more char in it than parameter 2 states.
//
// Returns:
//  returns FALSE if pColorName contains an invalid hex value:
//
BOOL TextWindowBase::parseHeaderTag(
	_CHAR* pHeaderTagBuf, ULONG32 headerTagBufLen
	, ULONG32 ulRTFileFormatMarkupParsingMajorVersion
	, ULONG32 ulRTFileFormatMarkupParsingMinorVersion)
{
    ULONG32 tokenNameStartIndex=0, tokenNameEndIndex=0;
    ULONG32 tokenValueStartIndex=0, tokenValueEndIndex=0;
    ULONG32 indexOfEqualsSign=0, dummyVal=0;
    _CHAR tempCharName, tempCharVal;

    if(!pHeaderTagBuf  ||  headerTagBufLen<1)
    {
	return FALSE;
    }

    //First go until you find "WINDOW"
    tokenNameStartIndex = 0;
    tokenNameStartIndex = skipSpacesTabsAndNewlineChars(pHeaderTagBuf,
	    headerTagBufLen, tokenNameStartIndex);
    if(tokenNameStartIndex==headerTagBufLen)
    {
	return FALSE; //no text was found except spaces, tabs, newline chars.
    }

    tokenNameEndIndex =	findNextSpaceTabOrNewLineChar(
	    pHeaderTagBuf, headerTagBufLen,
	    tokenNameStartIndex, indexOfEqualsSign,
	    //Pre-<WINDOW> tag text is always assumed to be us-ascii:
	    CHARSET__us_ascii);

    
    tempCharName = pHeaderTagBuf[tokenNameEndIndex];
    pHeaderTagBuf[tokenNameEndIndex] = '\0';

    convertToUpperCase(&pHeaderTagBuf[tokenNameStartIndex], 
	    tokenNameEndIndex-tokenNameStartIndex);

    if(stringCompare(
	    &pHeaderTagBuf[tokenNameStartIndex],
	    tokenNameEndIndex-tokenNameStartIndex,
	    "WINDOW", 6))
    {
	return FALSE;
    }

    pHeaderTagBuf[tokenNameEndIndex] = tempCharName; //Restore the orig char.
    tokenNameStartIndex = tokenNameEndIndex;//Start next token at prev end.

    //Find all <WINDOW ..> tag values, e.g. WIDTH=50, and fill in *this's
    //	appropriate fields, most of which are this->textWinAttrib's
    //	data members:
    do
    {
	tokenNameStartIndex = skipSpacesTabsAndNewlineChars(pHeaderTagBuf,
			headerTagBufLen, tokenNameStartIndex);
	if(tokenNameStartIndex==headerTagBufLen)
	{   /*  No more text was found except spaces, tabs, newline chars,
	     *  so quit:  */
	    break; 
	}

	tokenNameEndIndex = findNextSpaceTabOrNewLineChar(
		pHeaderTagBuf, headerTagBufLen,
		tokenNameStartIndex, indexOfEqualsSign,
		//In-tag text is always us-ascii:
		CHARSET__us_ascii);


#if defined(SHOW_CODE_CHANGE_MESSAGES)
#pragma message("EH- in "__FILE__", if tokenValueStartIndex pts to \", \
			then increment it by 1 and search for closing \" and set \
			tokenValueEndIndex to that.")
#endif
	if(indexOfEqualsSign < headerTagBufLen) 
	{   //An '=' was found before a space,tab, or newline, so break the
	    //	token into name and value buffers:
	    if(indexOfEqualsSign+1 == tokenNameEndIndex)
	    {	/*  Need to skip spaces...etc. to find where value part of
		 *  tag starts:  */
		tokenValueStartIndex = skipSpacesTabsAndNewlineChars(
			pHeaderTagBuf, headerTagBufLen, indexOfEqualsSign+1);
		if(tokenValueStartIndex==headerTagBufLen)
		{   /*  No more text was found except spaces, tabs, 
		     *  newline chars, so quit:  */
		    break;
		}
		tokenValueEndIndex = findNextSpaceTabOrNewLineChar(
			pHeaderTagBuf,  headerTagBufLen, 
			tokenValueStartIndex, dummyVal,
			//In-tag text is always us-ascii:
			CHARSET__us_ascii);

	    }
	    else
	    {
		tokenValueStartIndex = indexOfEqualsSign+1;
		tokenValueEndIndex = tokenNameEndIndex;
	    }
	    tokenNameEndIndex = indexOfEqualsSign;
	    if(tokenNameEndIndex == tokenNameStartIndex)
	    {	//Entire token starts with '=', so ignore it as invalid token
		//  and move on to find next token:
		tokenNameStartIndex = tokenNameEndIndex = tokenValueEndIndex;
		continue;
	    }
	}
	else
	{	
	    if(tokenNameEndIndex == headerTagBufLen)
	    {
		break; //no '=' found so token was invalid & no more exist.
	    }
	    /*  Find equals sign and value that follows it; need to skip
	     *  spaces...etc. to find where value part of tag starts:  */
	    indexOfEqualsSign = skipSpacesTabsAndNewlineChars(pHeaderTagBuf,
		    headerTagBufLen, tokenNameEndIndex);
	    if(indexOfEqualsSign==headerTagBufLen)
	    {	/*  No more text was found except spaces, tabs,
		 *  newline chars, so quit:  */
		break;
	    }
	    if(pHeaderTagBuf[indexOfEqualsSign] != '=')
	    {	/*  '=' not next char found so token is invalid; start next
		 *  token here:  */
		tokenNameStartIndex = tokenNameEndIndex = indexOfEqualsSign;
		continue;
	    }
	    tokenValueStartIndex = skipSpacesTabsAndNewlineChars(
		    pHeaderTagBuf, headerTagBufLen, indexOfEqualsSign+1);
	    while(tokenValueStartIndex<headerTagBufLen  &&
		    pHeaderTagBuf[tokenValueStartIndex] == '=')
	    {	
		tokenValueStartIndex = skipSpacesTabsAndNewlineChars(
			pHeaderTagBuf, headerTagBufLen,
			tokenValueStartIndex+1);
	    }
	    if(tokenValueStartIndex == headerTagBufLen)
	    {	/*  No more text was found except '=', spaces, tabs,
		 *  newlines, so quit:  */
		break;
	    }
	    tokenValueEndIndex = findNextSpaceTabOrNewLineChar(pHeaderTagBuf, 
		    headerTagBufLen, tokenValueStartIndex, dummyVal,
		    //In-tag text is always us-ascii:
		    CHARSET__us_ascii);

	}

	/*  Now, create a NULL-terminated, uppercase string out of the
	 *  token name:  */
	tempCharName = pHeaderTagBuf[tokenNameEndIndex];
	pHeaderTagBuf[tokenNameEndIndex] = '\0';
	convertToUpperCase(&pHeaderTagBuf[tokenNameStartIndex], 
		tokenNameEndIndex-tokenNameStartIndex);
	/*  Next, create a NULL-terminated, uppercase string out of the
	 *  token value:  */
	tempCharVal = pHeaderTagBuf[tokenValueEndIndex];
	pHeaderTagBuf[tokenValueEndIndex] = '\0';
	convertToUpperCase(&pHeaderTagBuf[tokenValueStartIndex], 
		tokenValueEndIndex-tokenValueStartIndex);

	ULONG32 tokenValueBufLen = tokenValueEndIndex - tokenValueStartIndex;

	//Ignore erroneous '/' at end of window tag, e.g.: <window n="v"/>
	if(tokenValueBufLen >= 4  &&
		'\"' == pHeaderTagBuf[tokenValueEndIndex-2]  &&
		'/' == pHeaderTagBuf[tokenValueEndIndex-1]
		)
	{
	    tokenValueBufLen--;
	    pHeaderTagBuf[tokenValueEndIndex-1] = '\0';
	}

	//Added the following to fix bug where value's len,
	// not name's len, was being used in stringCompares(), below:
	ULONG32 tokenNameBufLen = tokenNameEndIndex - tokenNameStartIndex;

	switch(pHeaderTagBuf[tokenNameStartIndex])
	{
	    case 'B':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"BACKGROUND", 10))
		{
		    setBackgroundColor(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		else if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"BGCOLOR", 7))
		{
		    setBackgroundColor(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		break;

	    case 'C':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"CRAWLRATE", 9))
		{
		    setCrawlRate(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		break;

	    case 'D':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"DEBUG", 5))
		{
#if defined(_DEBUG)
		    setDebugFlags(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
#endif
		}
		else if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"DURATION", 8))
		{
		    UINT32 ulDuration = 0;
		    if(convertTimeStringToULONG32(
			    &pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen,
    			    ulDuration))
		    {
			m_ulDuration = ulDuration;
		    }
		}
		break;

	    case 'E':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"ENDTIME", 7))
		{
		    UINT32 ulDuration = 0;
		    if(convertTimeStringToULONG32(
			    &pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen,
    			    ulDuration))
		    {
			m_ulDuration = ulDuration;
		    }
		}
		else if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"EXTRASPACES", 11))
		{
		    //Only perform this if FF version is new enough,
		    // (assuming renderer will never be older unless
		    // user chooses to ignore auto-update):
		    if(ulRTFileFormatMarkupParsingMajorVersion > 0  ||
			    ulRTFileFormatMarkupParsingMinorVersion > 1)
		    {
			SetExtraSpacesHandling(
				&pHeaderTagBuf[tokenValueStartIndex],
				tokenValueBufLen);
		    }
		}
		break;

	    case 'H':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"HEIGHT", 6))
		{
		    setHeight(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		break;

	    case 'L':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"LOOP", 4))
		{
		    if(!setLoop(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen))
		    {	
			/*  Invalid value, so restore to unspecified so that
			 *  proper defaulting can occur based on window
			 *  type:  */
			loop(DOLOOP_UNSPECIFIED);
		    }
		}
		//Added the following case so author can
		// specify what color will be of hyperlinked text (i.e., text
		// inside an <A ..>..</A> tag):
		else if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"LINK", 4))
		{
		    setLinkColor(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		else if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"LIVE", 4))
		{
		    setIsLiveSource(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		break;
    
	    case 'S':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"SCROLLTYPE", 10))
		{
#if defined(SHOW_CODE_CHANGE_MESSAGES)
#pragma message("EH- in "__FILE__", finish the SCROLLTYPE handling")
#endif
///			setScrollType(&pHeaderTagBuf[tokenValueStartIndex]);
		}
		else if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"SCROLLRATE", 10))
		{
		    setScrollRate(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		break;
    
	    case 'T':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"TYPE", 4))
		{
		    setType(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		break;

	    //Added the following case so author can
	    // specify whether or not hyperlinks get underlined automatically:
	    case 'U':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"UNDERLINE_HYPERLINKS", 20))
		{
		    setUnderlineHyperlinks(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		break;		
    
	    //Added the following case so author can
	    // specify whether or not hyperlinks get underlined automatically:
	    case 'V':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"VERSION", 7))
		{
		    setContentVersion(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		break;		
    
	    case 'W':
		if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"WIDTH", 5))
		{
		    setWidth(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		//Added the following case so author can
		// specify whether or not wordwrap is performed:
		else if(!stringCompare(
			&pHeaderTagBuf[tokenNameStartIndex],
			tokenNameBufLen,
			"WORDWRAP", 8))
		{
		    setWordwrap(&pHeaderTagBuf[tokenValueStartIndex],
			    tokenValueBufLen);
		}
		break;

    	    default:
		break;
	} //end switch().

	//Restore the chars that were set temporarily to '\0':
	pHeaderTagBuf[tokenNameEndIndex] = tempCharName;
	pHeaderTagBuf[tokenValueEndIndex] = tempCharVal;
	
	//Set startIndex to find next token:
	tokenNameStartIndex = tokenNameEndIndex = tokenValueEndIndex;

    } while(tokenValueEndIndex < headerTagBufLen);

    // /If the content version is not high enough (i.e., 1.6 or greater),
    // then we need to behave as we used to which is to treat "transparent"
    // as BAD_RGB_COLOR:
    if (TRANSPARENT_COLOR == m_backgroundColor  &&
	    ((getMajorContentVersion() <
	    REAL_TEXT_TRANSPARENT_BGCOLOR_MAJOR_VERSION)  ||
	    (getMajorContentVersion() ==
	    REAL_TEXT_TRANSPARENT_BGCOLOR_MAJOR_VERSION  &&
	    getMinorContentVersion() <
	    REAL_TEXT_TRANSPARENT_BGCOLOR_MINOR_VERSION)) )
    {
	// /It's "transparent" but not high enough content version #:
	m_backgroundColor = BAD_RGB_COLOR;
    }

    //For each of *this's variables that are <variable>_UNSPECIFIED, set them
    //	to the appropriate default values based on this->m_type:
    if(TYPE_UNSPECIFIED == m_type)
    {
	m_type = TYPE_GENERIC;
    }

    switch(m_type)
    {
	case TYPE_SCROLLINGNEWS:
	    if(SCROLLTYPE_UNSPECIFIED == m_scrollType)
	    {
		m_scrollType = DEFAULT_SCROLLTYPE_SCROLLINGNEWS;
		if(SCROLLRATE_UNSPECIFIED == m_scrollRate)
			m_scrollRate = DEFAULT_SCROLLRATE_SCROLLINGNEWS;
		if(CRAWLRATE_UNSPECIFIED == m_crawlRate)
			m_crawlRate = DEFAULT_CRAWLRATE_SCROLLINGNEWS;
	    }
	    else//scrolltype was specified so default to appropriate
		// scrollrate & crawlrate if they are not already specified:
	    {
		if(SCROLLRATE_UNSPECIFIED == m_scrollRate)
			m_scrollRate=(m_scrollType & SCROLLTYPE_SETRATE != 0?
					DEFAULT_SCROLLRATE_SCROLLINGNEWS:0);
		if(CRAWLRATE_UNSPECIFIED == m_crawlRate)
			m_crawlRate =(m_scrollType & SCROLLTYPE_SETRATE != 0?
					DEFAULT_CRAWLRATE_SCROLLINGNEWS:0);
	    }
	    if(DOLOOP_UNSPECIFIED == m_loop)
	    {
		m_loop = DEFAULT_DOLOOP_SCROLLINGNEWS;
	    }
	    if(BGCOLOR_RGB_UNSPECIFIED == m_backgroundColor)
	    {
		m_backgroundColor = DEFAULT_BGCOLOR_RGB_SCROLLINGNEWS;
	    }
	    if(WINDOW_DIMENSION_UNSPECIFIED == m_width)
	    {
		m_width = DEFAULT_WINDOWWIDTH_SCROLLINGNEWS;
	    }
	    if(WINDOW_DIMENSION_UNSPECIFIED == m_height)
	    {
		m_height = DEFAULT_WINDOWHEIGHT_SCROLLINGNEWS;
	    }
	    break;

	case TYPE_TICKERTAPE:
#if(DEFAULT_SCROLLRATE_TICKERTAPE)
#error: "Tickertape doesn't work with non-zero scroll rate."
#else
	    m_scrollRate = DEFAULT_SCROLLRATE_TICKERTAPE;
#endif
	    if(SCROLLTYPE_UNSPECIFIED == m_scrollType)
	    {
		m_scrollType = DEFAULT_SCROLLTYPE_TICKERTAPE;
		if(CRAWLRATE_UNSPECIFIED == m_crawlRate)
			m_crawlRate = DEFAULT_CRAWLRATE_TICKERTAPE;
	    }
	    else//scrolltype was specified so default to appropriate
		// crawlrate if it is not already specified:
	    {
		if(CRAWLRATE_UNSPECIFIED == m_crawlRate)
		{
		    m_crawlRate = (m_scrollType & SCROLLTYPE_SETRATE != 0?
			    DEFAULT_CRAWLRATE_TICKERTAPE:0);
		}
	    }

	    if(DOLOOP_UNSPECIFIED == m_loop)
	    {
		m_loop = DEFAULT_DOLOOP_TICKERTAPE;
	    }
	    if(BGCOLOR_RGB_UNSPECIFIED == m_backgroundColor)
	    {
		m_backgroundColor = DEFAULT_BGCOLOR_RGB_TICKERTAPE;
	    }
	    if(WINDOW_DIMENSION_UNSPECIFIED == m_width)
	    {
		m_width = DEFAULT_WINDOWWIDTH_TICKERTAPE;
	    }
	    if(WINDOW_DIMENSION_UNSPECIFIED == m_height)
	    {
		m_height = DEFAULT_WINDOWHEIGHT_TICKERTAPE;
	    }
	    break;

	case TYPE_TELEPROMPTER:
#if(DEFAULT_CRAWLRATE_TELEPROMPTER  ||  DEFAULT_DOLOOP_TELEPROMPTER)
#error: "Teleprompter doesn't work with non-zero scroll or crawl rate or with LOOP set to TRUE."
#else
	    m_crawlRate = DEFAULT_CRAWLRATE_TELEPROMPTER;
	    m_scrollRate = DEFAULT_SCROLLRATE_TELEPROMPTER;
	    m_loop = DEFAULT_DOLOOP_TELEPROMPTER;
#endif
	    if(SCROLLTYPE_UNSPECIFIED == m_scrollType)
	    {
		m_scrollType = DEFAULT_SCROLLTYPE_TELEPROMPTER;
	    }
	    if(BGCOLOR_RGB_UNSPECIFIED == m_backgroundColor)
	    {
		m_backgroundColor = DEFAULT_BGCOLOR_RGB_TELEPROMPTER;
	    }
	    if(WINDOW_DIMENSION_UNSPECIFIED == m_width)
	    {
		m_width = DEFAULT_WINDOWWIDTH_TELEPROMPTER;
	    }
	    if(WINDOW_DIMENSION_UNSPECIFIED == m_height)
	    {
		m_height = DEFAULT_WINDOWHEIGHT_TELEPROMPTER;
	    }
	    break;

	case TYPE_MARQUEE:
#if(DEFAULT_SCROLLRATE_MARQUEE)
#error: "Marquee doesn't work with non-zero scroll rate."
#else
	    m_scrollRate = DEFAULT_SCROLLRATE_MARQUEE;
#endif
	    if(SCROLLTYPE_UNSPECIFIED == m_scrollType)
	    {
		m_scrollType = DEFAULT_SCROLLTYPE_MARQUEE;
		if(CRAWLRATE_UNSPECIFIED == m_crawlRate)
			m_crawlRate = DEFAULT_CRAWLRATE_MARQUEE;
	    }
	    else//scrolltype was specified so default to appropriate
		// crawlrate if it is not already specified:
	    {
		if(CRAWLRATE_UNSPECIFIED == m_crawlRate)
		{
		    m_crawlRate = (m_scrollType & SCROLLTYPE_SETRATE != 0?
			    DEFAULT_CRAWLRATE_MARQUEE:0);
		}
	    }
	    if(DOLOOP_UNSPECIFIED == m_loop)
	    {
		m_loop = DEFAULT_DOLOOP_MARQUEE;
	    }
	    if(BGCOLOR_RGB_UNSPECIFIED == m_backgroundColor)
	    {
		m_backgroundColor = DEFAULT_BGCOLOR_RGB_MARQUEE;
	    }
	    if(WINDOW_DIMENSION_UNSPECIFIED == m_width)
	    {
		m_width = DEFAULT_WINDOWWIDTH_MARQUEE;
	    }
	    if(WINDOW_DIMENSION_UNSPECIFIED == m_height)
	    {
		m_height = DEFAULT_WINDOWHEIGHT_MARQUEE;
	    }
	    break;

	case TYPE_GENERIC:
		//fall through and do default code, below:

	default:
	{
	    if(SCROLLTYPE_UNSPECIFIED == m_scrollType)
	    {
		m_scrollType = DEFAULT_SCROLLTYPE_GENERIC;
		if(SCROLLRATE_UNSPECIFIED == m_scrollRate)
		{
		    m_scrollRate = DEFAULT_SCROLLRATE_GENERIC;
		}
		if(CRAWLRATE_UNSPECIFIED == m_crawlRate)
		{
		    m_crawlRate = DEFAULT_CRAWLRATE_GENERIC;
		}
	    }
	    else//scrolltype was specified so default to appropriate
		    //	scrollrate & crawlrate if they are not already specified:
	    {
		if(SCROLLRATE_UNSPECIFIED == m_scrollRate)
		{
		    m_scrollRate = (m_scrollType & SCROLLTYPE_SETRATE != 0?
			    DEFAULT_SCROLLRATE_GENERIC:0);
		}
		if(CRAWLRATE_UNSPECIFIED == m_crawlRate)
		{
		    m_crawlRate = (m_scrollType & SCROLLTYPE_SETRATE != 0?
			    DEFAULT_CRAWLRATE_GENERIC:0);
		}
	    }
	    if(DOLOOP_UNSPECIFIED == m_loop)
	    {
		m_loop = DEFAULT_DOLOOP_GENERIC;
	    }
	    if(BGCOLOR_RGB_UNSPECIFIED == m_backgroundColor)
	    {
		m_backgroundColor = DEFAULT_BGCOLOR_RGB_GENERIC;
	    }
	    if(WINDOW_DIMENSION_UNSPECIFIED == m_width)
	    {
		m_width = DEFAULT_WINDOWWIDTH_GENERIC;
	    }
	    if(WINDOW_DIMENSION_UNSPECIFIED == m_height)
	    {
		m_height = DEFAULT_WINDOWHEIGHT_GENERIC;
	    }
	}
	break; //end "default:".
    } //end "switch(m_type)".

    if(m_scrollRate < MIN_SCROLLRATE)
    {
	m_scrollRate = MIN_SCROLLRATE;
    }
    if(m_scrollRate > MAX_SCROLLRATE)
    {
	m_scrollRate = MAX_SCROLLRATE;	
    }

    if(m_crawlRate < MIN_CRAWLRATE)
    {
	m_crawlRate = MIN_CRAWLRATE;
    }
    if(m_crawlRate > MAX_CRAWLRATE)
    {
	m_crawlRate = MAX_CRAWLRATE;	
    }

    if(m_width < MIN_WINDOWWIDTH)
    {
	m_width = MIN_WINDOWWIDTH;
    }
    if(m_height < MIN_WINDOWHEIGHT)
    {
	m_height = MIN_WINDOWHEIGHT;	
    }
    return TRUE;
}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  TextWindow::insertAtEndOfList(TextContainer* pTC)
//
// Purpose:
//  inserts the TextContainer* object into *this::TextContainerList.  The
//  TextContianer::TextAttributes members of pTC are assumed to already be
//  set	before this function is called.  The "location" of the text in pTC
//  gets set herein based on m_type and scroll and crawl attributes
//  of *this. The x and y extents of pTC's text are determined in this
//  function based on pTC's font.
//
// Returns:
//  returns FALSE if pTC is NULL or if some other error occurred.
//
BOOL TextWindow::insertAtEndOfList(TextContainer* pTC,
				   BOOL bCalledFromRendererCode,
				   BOOL bDealingWithTabCharWithPre)
{
    TextContainer* pTC_prev;
    LONG32 prev_x, prev_y, prev_xExtent, prev_yExtent;

    if(!pTC)
    {
	return FALSE;
    }
    if(!pTC->getBuffer()  ||  pTC->getTextLengthPlus1()<=1)
    {
	return FALSE; //there's nothing to insert.
    }

    LONG32 xExtentInPixels;

    ULONG32 ulTimeOfEncoderStartup = getTimeAtStartup();
    ULONG32 ulLiveXOffset = (isLiveSource() && !bCalledFromRendererCode)?
	    //NOTE: the casting to double here is ONLY to avoid
	    // overflow and does not need special floating-point
	    // consideration for fixed-point code conversion,
	    // i.e., precision does not need to be maintainted:
	    (ULONG32(((double)m_crawlRate*
	    (double)ulTimeOfEncoderStartup)/1000.0)):
	    2L; //Use 2 to pull text away from edge a bit.
    ULONG32 ulLiveYOffset = (isLiveSource() && !bCalledFromRendererCode)?
	    //NOTE: the casting to double here is ONLY to avoid
	    // overflow and does not need special floating-point
	    // consideration for fixed-point code conversion,
	    // i.e., precision does not need to be maintainted:
	    (ULONG32(((double)m_scrollRate*
	    (double)ulTimeOfEncoderStartup)/1000.0)):
	    0L;

    xExtentInPixels = GetStringWidthInPixels(//this functn is in fontinfo.cpp
	    pTC->getBuffer(), pTC->getTextLengthPlus1()-1,
	    pTC->getFontFace(), pTC->getFontPointSize(), 
	    pTC->isBold(), pTC->isItalicized(), pTC->getFontCharset());
    if(xExtentInPixels < 0)
    {
	xExtentInPixels = -xExtentInPixels;
	//XXXEH- an error occurred in GetStringWidthInPixels(); handle it!!
    }
    else if(0 == xExtentInPixels)
    {
	//XXXEH- an error occurred in GetStringWidthInPixels(); handle it
	// better than the following kludge:
	xExtentInPixels =
		(pTC->getFontPointSize()/2) * (pTC->getTextLengthPlus1()-1);
    }
    pTC->setXExtent(xExtentInPixels);

    pTC->setYExtent(pTC->getFontPointSize());

    //added the following to handle word wrap;
    // word wrap can only happen between two T.C.s where the first
    // ends with a space or the second begins with one:
    BOOL bPrevEndedWithSpaceChar=FALSE;
    //Is DBCS Acts same as BeginsWithSpaceChar as far as wordwrap goes, since
    // typically no spaces separate DBCS "words":
    BOOL bCurBeginsWithSpaceCharOrCharIsDBCSorUNICODE=FALSE;
    _CHAR* pTmpBufCopy = pTC->getBuffer();
    if(' '==pTmpBufCopy[0])//(Valid buf & bufsize check was done above.)
    {
	bCurBeginsWithSpaceCharOrCharIsDBCSorUNICODE=TRUE;
    }
    if (pTC->getFontCharset() & HX_DBCS_CHARSET  ||
	    pTC->getFontCharset() & HX_UNICODE_CHARSET)
    {
	bCurBeginsWithSpaceCharOrCharIsDBCSorUNICODE = TRUE;
    }


    pTC_prev = end();

    ULONG32 newLines = pTC->getNumNewlinesAtStart();

    ULONG32 ulNumNewlinesStatedInPacketHeader =
	    GetNumNewlinesStatedInPacketHeader();
    if(bCalledFromRendererCode) //Should never be called in file format since
	// this is just to deal with the arrival of a packet in the renderer
	// that has <POS ... NEWLINES=n/> in it:
    {
	if(newLines >= ulNumNewlinesStatedInPacketHeader)
	{
	    //ulNumNewlinesStatedInPacketHeader are newlines that are already
	    // accounted for at start of a packet in the <POS X0=.. Y0=.. />
	    // tag, so we don't want to do anything with them other than to
	    // see if they are non-zero (in renderer's parsing code, which
	    // decides whether or not to center the prior line of text if
	    // this is non-zero, and increments the total newlines by it,
	    // so reduce them by it here so the following if((m_newPkt...)..)
	    // doesn't fail if !newLines is erroneous based on this val:
	    newLines -= ulNumNewlinesStatedInPacketHeader;
	}
	else if(ulNumNewlinesStatedInPacketHeader > 0L)
	{
	    newLines = 0L;
	}
    }

    //Check if m_newPktStart[X|Y]AtTimeZero were set to a valid number in
    // the parsing code ("<POS X0= Y0= >" tag was seen):
    // if not zero, then this pTC is from a packet start and prev_x
    // and prev_y should be set to m_newPktStart[X|Y]AtTimeZero, and
    // prior TC placement should be ignored:
    BOOL bXYAtTimeZeroAlreadyFigured = FALSE;
    if((m_newPktStartXAtTimeZero != INVALID_LONG32  ||
	    m_newPktStartYAtTimeZero != INVALID_LONG32)
	    //"[x]<clear/>" could start a packet and be ignored without
	    // this check, where [x] is an element of: {'\n','\r',' ','\t'}
	    &&  ( (!m_bClearWasJustSent  ||
		    (m_bUseXPOSVal  || m_bUseYPOSVal))
	    //Holy cow, this huristics thing is getting out of hand...
	    // what we really need is a complex structure that keeps track
	    // of what tags were seen in what order so the packet header
	    // "state" tags are in the rigth order;
	    // if "<time begin=1/><clear/><pos x=100 y=100/>foo" starts a
	    // packet, the first packet would be
	    //   <0093><RESET><POS X0=100 Y0=100 NEWLINES=0><TIME start=1.000
	    //   end=4294967.295 lc=1.000></0093>foo
	    // and the translation from x,y to X0,y0 in the pos tag would
	    // mean that the pos would get ignored by this if() unless the
	    // following was checked, namely that the clear's time is the
	    // same as the current time and thus the POS tag applies to
	    // text after the clear (lc) and is thus not overridden by the 
	    // clear as it usually is:
	    ||  (m_bClearWasJustSent  &&
		    getTimeOfLastClearTag()==GetLatestSentTimeToRender()) )
	    //"[x]<br/>" could start a packet and be ignored without
	    // this check, where [x] is an element of: {'\n','\r',' ','\t'}
	    &&  !newLines)
    {
	if(m_newPktStartXAtTimeZero != INVALID_LONG32)
	{
	    prev_x = m_newPktStartXAtTimeZero;
	}
	else
	{
	    prev_x = ulLiveXOffset;  //Will still be 2L for non-live.
	}
	if(m_newPktStartYAtTimeZero != INVALID_LONG32)
	{
	    prev_y = m_newPktStartYAtTimeZero;
	}
	else
	{
	    prev_y = ulLiveYOffset; //will still be 0L for non-live.
	}
	prev_yExtent = 0L;
	prev_xExtent = 0L;

	pTC->setXAtTimeZeroUpperLeftCorner(prev_x);
	pTC->setYAtTimeZeroUpperLeftCorner(prev_y);

	m_currentTextLineStartX = prev_x;
	m_currentTextLineEndX = prev_x + pTC->getXExtent();
	m_currentTextLineStartY = (TYPE_MARQUEE==m_type?0L:prev_y);
	m_currentTextLineEndY = prev_y + pTC->getYExtent();
	//Bug fix: we don't want to reset the startX of this
	// line of text to the prev_x if we are in a scroll-&-crawl window
	// that depends on each line starting on the line through (0,0) and
	// having a slope of scrollRate/crawlRate:
	if(m_crawlRate>0L  &&  m_scrollRate>0L)	//XXXEH- no neg scroll,crawl!
	{
	    if(m_newPktStartYAtTimeZero != INVALID_LONG32)
	    {
		float inverseSlope = (float)m_crawlRate / (float)m_scrollRate;
		m_currentTextLineStartX =
			LONG32((float)m_currentTextLineStartY * inverseSlope);
	    }
	}

	bXYAtTimeZeroAlreadyFigured = TRUE;

	m_bUseXPOSVal = m_bUseYPOSVal = FALSE;
    }
    else if(pTC_prev != NULL  &&  !m_bClearWasJustSent) 
    {
	//then there is at least 1 TextContainer in the list
	//  AND clear tag was not just sent so append text
	//  in location past where prev is:
	prev_x = pTC_prev->getXAtTimeZeroUpperLeftCorner();
	prev_y = pTC_prev->getYAtTimeZeroUpperLeftCorner();
	prev_xExtent = pTC_prev->getXExtent();
	prev_yExtent = pTC_prev->getYExtent();

	//added the following to allow for word wrap;
	_CHAR* pTmpPrevBufCopy = pTC_prev->getBuffer();
	ULONG32 tmpPrevBufCopyLen = pTC_prev->getTextLengthPlus1()-1;
	if(pTmpPrevBufCopy  &&  tmpPrevBufCopyLen>0L)
	{
	    if(' '==pTmpPrevBufCopy[tmpPrevBufCopyLen-1])
	    {
		bPrevEndedWithSpaceChar=TRUE;
		//But, hold on a minute: see if it's a trailing byte of DBCS
		// char by seeing if prev TextContainer is DBCS and its 2nd-
		// to-last character is >=DBCS_MIN_LEAD_BYTE_VAL:
		if(tmpPrevBufCopyLen>1)
		{
		    if((pTC_prev->getFontCharset() & HX_DBCS_CHARSET)  &&
			    (UCHAR)pTmpPrevBufCopy[tmpPrevBufCopyLen-2] >=
			    DBCS_MIN_LEAD_BYTE_VAL)
		    {
			bPrevEndedWithSpaceChar=FALSE;
		    }
		}
	    }
	}
	//Now, check if there are two space chars in a row, and, if
	// so, set the last char of pTC-prev to '\0':
/*XXX - code unfinished; find out what ramifications are, first, of
  setting last char of pTC_prev's buf to '\0' without changing its len.
  Also: if(tmpPrevBufCopyLen == 1), don't do this (???):
	if(bPrevEndedWithSpaceChar  &&
		bCurBeginsWithSpaceCharOrCharIsDBCSorUNICODE)
	{
	}
XXX*/
    }
    else
    {
	prev_y = ulLiveYOffset;  //Will still be 0L for non-live.
	prev_xExtent = prev_yExtent = 0;
	prev_x = ulLiveXOffset;  //Will still be 2L for non-live. 
    }

    m_newPktStartXAtTimeZero = m_newPktStartYAtTimeZero = INVALID_LONG32;	

    if(m_bClearWasJustSent  &&  !bXYAtTimeZeroAlreadyFigured)
    {
	m_currentTextLineStartY = m_currentTextLineEndY = 0L;
	//Add 2 to pull text away from edge a bit:
	m_currentTextLineStartX = m_currentTextLineEndX = 2L;
	if(m_crawlRate >= 0)
	{
	    m_currentTextLineStartX = m_currentTextLineEndX =
		    2 + //Add 2 to pull text away from edge a bit.
		    //NOTE: the casting to double here is ONLY to avoid
		    // overflow and does not need special floating-point
		    // consideration for fixed-point code conversion,
		    // i.e., precision does not need to be maintainted:
		    ULONG32((double(m_crawlRate) *
		    double(pTC->getStartTime()))/1000.0);
	    prev_x = m_currentTextLineStartX;
	    prev_xExtent = 0L;
	}
	if(m_scrollRate >= 0)
	{
	    m_currentTextLineStartY = m_currentTextLineEndY =
		    //NOTE: the casting to double here is ONLY to avoid
		    // overflow and does not need special floating-point
		    // consideration for fixed-point code conversion,
		    // i.e., precision does not need to be maintainted:
		    ULONG32((double(m_scrollRate) *
		    double(pTC->getStartTime()))/1000.0);
	    prev_y = m_currentTextLineStartY;
	    prev_yExtent = 0L;
	}
    }

    if(m_crawlRate < 0  ||  m_scrollRate < 0)
    {
#if defined(SHOW_CODE_CHANGE_MESSAGES)
#pragma message("EH- in "__FILE__", \
	Can't do negative crawl or scroll yet...")
#endif
	m_crawlRate = m_scrollRate = 0;
        m_bClearWasJustSent = FALSE;
	return FALSE;
    }

    BOOL bUsingWordwrap = (usingWordwrap()  &&  !bDealingWithTabCharWithPre);

    if(!bXYAtTimeZeroAlreadyFigured  &&  bUsingWordwrap  &&  0L==newLines)
    {
	//Added the following to handle word wrap; if
	// prior TextContainer ended with a space char, then check and
	// see if this new one goes outside the right side of the window, and,
	// if so (and window does not have x-direction-only motion) add a
	// line break:
	if((bPrevEndedWithSpaceChar  ||
		bCurBeginsWithSpaceCharOrCharIsDBCSorUNICODE)  && 
		(0L==getCrawlRate()  ||  0L!=getScrollRate()) )
	{
	    LONG32 xAdditional = 0L;
	    if(0L!=getCrawlRate()  &&  0L!=getScrollRate())
	    {
		//Do line break farther out in x (based on slope of motion):
		float fInverseSlope =
			float(getCrawlRate()) / float(getScrollRate());
		xAdditional = LONG32(
			fInverseSlope * float(prev_y) );
	    }
	    if(prev_x + prev_xExtent + pTC->getXExtent()
		    - xAdditional
		    > getWidth())
	    {
		newLines=1L;  //Forces word wrap here.
		pTC->setNumNewlinesAtStart(newLines);
		pTC->isWordWrapNewLine(TRUE);
	    }
	}
    }

    if(bCalledFromRendererCode  &&  (newLines  ||
	    ulNumNewlinesStatedInPacketHeader
	    //<CLEAR> clears the new line that a </CENTER> tag may have
	    // caused so "...foo</CENTER><CLEAR>..." was not centering
	    // the line ending with "...foo"; now it does with the
	    // addition of this check:
	    ||  m_bClearWasJustSent) )
    {
        //Since we've started a new line, we may need to go
        // through all prior T.C.s in the prev line of text and
        // adjust their x's so they are centered:
        CenterPriorLine();
    }

    if(newLines > 0L  &&  !bXYAtTimeZeroAlreadyFigured)
    {
	//Text follows one or more break tags (i.e., <BR> tag and
	// tags like <LI>, <P>, and </Hn>) so we need to calculate
	// where this next block of text should go based on 
	// (m_crawlRate,m_scrollRate) vector:
	if(0==m_scrollRate  ||  0==m_crawlRate)
	{
	    if(TYPE_TICKERTAPE == m_type  ||
		    SCROLLTYPE_HSCROLLBAR == m_scrollType
		    //added the following check to cause
		    // any window with 0 scrollrate and non-zero crawlrate
		    // to treat a <BR> tag as a horizontal break:
		    ||  (0==m_scrollRate  &&  0!=m_crawlRate)
		    )
	    {	
		//Assumes text is added to the right (at higher x) and line
		// break is treated as a horizontal spacing of
		// BREAK_WIDTH_IN_PIXELS:
		LONG32 yOffset = 0L;
		LONG32 xOffset = 0L;

		if(TYPE_TICKERTAPE == m_type)
		{
		    BOOL prevIsUpperText = TRUE;
		    if(pTC_prev)
		    {
			prevIsUpperText = pTC_prev->isTickerUpperText();
		    }
		    if(!pTC->isTickerUpperText())
		    {	//then is lower text:
			if(prevIsUpperText) //are diff, so add space between:
			{
			    xOffset = TICKER_GOINGLOWERITEM_SPACING;
			}
			/*  Draw lower text lower down, i.e. at higher-Y: */
			yOffset =
				//Fix for PR 23278: use the native rt window
				// m_height and not the (possibly scaled)
				// m_visibleWindowHeight.  Let the scaling
				// be done exclusivly at draw time:
				m_height -
				pTC->getFontPointSize() - 2L;
			if(yOffset < 0L)
			{
			    yOffset = 0L;
			}
		    }
		    else
		    {	//then is upper text:
			if(!prevIsUpperText)//are diff, so add space between:
			{
			    xOffset = TICKER_GOINGUPPERITEM_SPACING;
			}
			else
			{
			    xOffset = 0L;
			}
			yOffset = 0L;

		    }
		}

		//commented out the old line and replaced
		// it with the line following -- now, <BR> and <P> tags
		// means (in horizontally-moving text): start the text
		// that follows at the right edge of the window (not just
		// BREAK_WIDTH_IN_PIXELS to the right, as before). Any number
		// of newLines>1 is now counted as just 1 newline:
		//XXXEH: LONG32 lBreakSpacing=newLines*BREAK_WIDTH_IN_PIXELS;
		LONG32 lBreakSpacing = (newLines>0?1:0)* 
			(getWidth()-(prev_x+prev_xExtent+xOffset));

		//This fixes a "<CLEAR/><BR/>foo" bug where foo was not
		// starting at the right edge of the window but at an
		// already-scrolled spot to the left:
		if(m_bClearWasJustSent)
		{
		    //add it back - was erroneously subtracted in the calc
		    // of lBreakSpacing, above:
		    lBreakSpacing += prev_x;
		}

		//Changed this to while() from if() to fix
		// a bug when a <BR> follows an item that is more than twice
		// the width of the window in xExtent:
		while(lBreakSpacing<BREAK_WIDTH_IN_PIXELS)
		{
		    lBreakSpacing += getWidth();
		}
		pTC->SetBreakSpacingX(lBreakSpacing);

		pTC->setXAtTimeZeroUpperLeftCorner(prev_x + prev_xExtent +
			xOffset + pTC->GetBreakSpacingX() );
	    
		//Added this to vertically-center MARQUEE text:
		if(TYPE_MARQUEE == m_type)
		{
		    yOffset = (m_height - pTC->getYExtent()) / 2;
		}	    

		//Tickertape text doesn't bottom-align with prev text,
		// so don't add prev_y to yOffset when setting pTC's upper
		// left corner Y:
		pTC->setYAtTimeZeroUpperLeftCorner(yOffset);
		
		//Added the following lines to set vars:
		if(m_currentTextLineEndY<pTC->getYExtent())
		{
		    m_currentTextLineEndY = pTC->getYExtent();
		}
	    }
	    /*  All other types and m_scrollTypes assume text is added below
	     *  (at higher y):  */
	    else
	    {	
		//Changed the following from 0 to 2
		// to pull the text away from the edge a bit:
		pTC->setXAtTimeZeroUpperLeftCorner(2
			//Added this to indent list items:
			+ pTC->getLineIndentAmtInPixels()
			);
		
		//Had to add this for the case where a <BR/> or other line
		// break tag immediately follows a <CLEAR/> or is at the
		// start of the file; without this, no line break would occur
		// for the first <BR/> or other such tag:
		if(m_currentTextLineEndY == 0L)
		{
		    m_currentTextLineEndY = pTC->getYExtent();
		}
		pTC->SetBreakSpacingY
		(
		    ((newLines-1) *
			/*  If prev_yExtent is big enough...  */
			(prev_yExtent>MIN_LINE_BREAK_SIZE?
			    prev_yExtent:
			    /*  ...else use predefined amt:  */
			    (prev_yExtent<=0?
				    DEFAULT_LINE_BREAK_SIZE:
				    MIN_LINE_BREAK_SIZE
			    )
			)
		    )
		);

		pTC->setYAtTimeZeroUpperLeftCorner(m_currentTextLineEndY +
			pTC->GetBreakSpacingY());

		//Is start of new line, so set currentTextLine.. vars:
		m_currentTextLineStartY =
			pTC->getYAtTimeZeroUpperLeftCorner();
		m_currentTextLineEndY =	m_currentTextLineStartY +
			pTC->getYExtent(); 
		m_currentTextLineStartX = 
			pTC->getXAtTimeZeroUpperLeftCorner();
		m_currentTextLineEndX =
			m_currentTextLineStartX + pTC->getXExtent();
	    } //end "else".		
	} //end "if(0==m_scrollRate  ||  0==m_crawlRate)".
	
	else if(m_scrollRate>0  &&  m_crawlRate>0)			
	{
	    /*  Both m_scrollRate and m_crawlRate are not zero, so figure
	     *  out where to place this new line of text:  */
	    float x_slopeDiff, y_slopeDiff;
	    float scrollDivCrawlSlope =
		    float(m_scrollRate)/float(m_crawlRate);

	    /*  Since m_scrollRate and m_crawlRate are guaranteed not to be
	     *  zero (from "else if(..)", above) then we don't have to check
	     *  for divide-by-zero errors:  */
	    //Changed the following line so that it uses
	    // m_currentTextLine..X vars instead of prev_xExtent:
	    x_slopeDiff =
		    (float(m_currentTextLineEndX - 
			    m_currentTextLineStartX) / 
		    float(m_crawlRate) );
	    y_slopeDiff =
		    (float(m_currentTextLineEndY - 
			    m_currentTextLineStartY));
	    y_slopeDiff = y_slopeDiff / (float)m_scrollRate;
	    if(x_slopeDiff > y_slopeDiff)
	    {
		//text should be aligned next to prev, vertically:
		pTC->SetBreakSpacingY((newLines-1) *   //do Y first..
			(m_currentTextLineEndY-m_currentTextLineStartY)
			);
		pTC->SetBreakSpacingX(	   //X depends on Y, as set above.
			LONG32(float(pTC->GetBreakSpacingY()) *
				//Fixed multi-<BR> bug
				// below by using 1/scrollDivCrawlSlope
				// instead of scrollDivCrawlSlope:
				(1.0/scrollDivCrawlSlope) ) );

		pTC->setXAtTimeZeroUpperLeftCorner(
			//Changed the following to use
			// m_currentTextLineStartX instead of prev_x because
			// prev_x will be in the middle of a line if the line
			// has more than one TextContainer's text in it:
			m_currentTextLineStartX + 
			LONG32(y_slopeDiff * float(m_crawlRate)) +
			pTC->GetBreakSpacingX() 
			);
		pTC->setYAtTimeZeroUpperLeftCorner(
			prev_y +
			(m_currentTextLineEndY-m_currentTextLineStartY)+
			pTC->GetBreakSpacingY() );
	    }
	    else
	    {
		//text should be aligned next to prev, horizontally:
		pTC->SetBreakSpacingX((newLines-1) * BREAK_WIDTH_IN_PIXELS);
		pTC->SetBreakSpacingY(
			LONG32(float(pTC->GetBreakSpacingX()) * 
				scrollDivCrawlSlope) );

		pTC->setXAtTimeZeroUpperLeftCorner(
			prev_x + prev_xExtent +
			pTC->GetBreakSpacingX()						
			);
		pTC->setYAtTimeZeroUpperLeftCorner(
			prev_y + LONG32(x_slopeDiff * float(m_scrollRate)) +
			pTC->GetBreakSpacingY()
			);
	    }

	    //Is start of new line, so set currentTextLine.. vars:
	    m_currentTextLineStartY =
		    pTC->getYAtTimeZeroUpperLeftCorner();
	    m_currentTextLineEndY =
		    m_currentTextLineStartY + pTC->getYExtent();
	    //Added the following two lines:
	    m_currentTextLineStartX =
		    pTC->getXAtTimeZeroUpperLeftCorner();
	    m_currentTextLineEndX =
		    m_currentTextLineStartX + pTC->getXExtent();
	    
	} //end "else" of "if(0==m_scrollRate  ||  0==m_crawlRate)".
    } //end "if((newLines = pTC->getNumNewlinesAtStart()) > 0L)".
    else if(!bXYAtTimeZeroAlreadyFigured )
    {
	/*  Text does not immediately follow a break tag, so it is added to
	 *  the right (toward higher x) of the previous text:  */
	if(TYPE_TICKERTAPE == m_type)
	{	
	    /*  Special processing has to be done for tickertape text to
	     *  put LowerText lower in Y, and to add spacing between each
	     *  upper and lower item and also between each lower and
	     *  upper item:  */
	    LONG32 yOffset = 0L;
	    LONG32 xOffset = 0L;
	    BOOL prevIsUpperText = TRUE;

	    if(pTC_prev)
	    {
		prevIsUpperText = pTC_prev->isTickerUpperText();
	    }

	    if(!pTC->isTickerUpperText())
	    {	//then is lower text:
		if(prevIsUpperText) //then different, so add space between:
		{
		    xOffset = TICKER_GOINGLOWERITEM_SPACING;
		}
		else
		{
		    xOffset = 0L;
		}
		//Draw lower text lower down, i.e., higher in Y:
		yOffset =
			//Fix for PR 23278: use the native rt window
			// m_height and not the (possibly scaled)
			// m_visibleWindowHeight.  Let the scaling
			// be done exclusivly at draw time:
			m_height -
			pTC->getFontPointSize() - 2;
		if(yOffset < 0L)
		{
		    yOffset = 0L;
		}
	    }
	    else
	    {	//then is upper text:
		if(!prevIsUpperText) //then different, so add space between:
		{
		    xOffset = TICKER_GOINGUPPERITEM_SPACING;

		    //Added this to start a "newline"
		    // wherever a <TU> item starts:
		    pTC->isFakeNewLine(TRUE);
		}
		else
		{
		    xOffset = 0L;
		}
		yOffset = 0L; //Draw upper text at top of bounding box.
	    }

	    if(m_bClearWasJustSent)
	    {
		xOffset = 0L;
	    }

	    pTC->setXAtTimeZeroUpperLeftCorner(
		    prev_x + prev_xExtent + xOffset +
		    (newLines*BREAK_WIDTH_IN_PIXELS));
	    //Tickertape text doesn't bottom-align with prev text, so don't
	    //	add prev_y to yOffset when setting pTC's upper left corner Y:
	    pTC->setYAtTimeZeroUpperLeftCorner(yOffset);

	    //Added this to keep track of where "newline"s
	    // are in crawlrate-only windows:
	    if(pTC->isFakeNewLine())
	    {
		m_xOfFakeNewLine = pTC->getXAtTimeZeroUpperLeftCorner();
	    }
	    else
	    {
		if(pTC->getXAtTimeZeroUpperLeftCorner()-m_xOfFakeNewLine > 
			getWidth())
		{
		    pTC->isFakeNewLine(TRUE);
		    m_xOfFakeNewLine = pTC->getXAtTimeZeroUpperLeftCorner();
		}
	    }
	}
	else 
	{
	    /*  Isn't tickertape, so just add to the right of the previous
	     *  text, and then align the bottom of this text with the bottom
	     *  of the current line of text:  */
	    pTC->setXAtTimeZeroUpperLeftCorner(prev_x + prev_xExtent);

	    //Added this to vertically-center MARQUEE text:
	    LONG32 yOffset = 0L;
	    if(TYPE_MARQUEE == m_type)
	    {
		yOffset = (m_height - pTC->getYExtent()) / 2;
	    }	    

	    //Do this and then will Y-align to prev text in code further on:
	    pTC->setYAtTimeZeroUpperLeftCorner(m_currentTextLineStartY
		    + yOffset);

	    if(pTC_prev != NULL  &&  !m_bClearWasJustSent
		    //added the following conditional
		    // so no y-adjustments happen in crawling-only windows:
		    &&  (!m_crawlRate  ||  m_scrollRate)
		    ) 
	    {
		//the previous TC exists:
		LONG32 yExtentDiff =
			(m_currentTextLineEndY - m_currentTextLineStartY) - 
			pTC->getYExtent();
		if(yExtentDiff > 0)
		{
		    /*  Align bottom of pTC's text so that it is
		     *  at m_currentTextLineEndY:  */
		    pTC->setYAtTimeZeroUpperLeftCorner(
			    pTC->getYAtTimeZeroUpperLeftCorner()
			    + yExtentDiff);
		}
		else if(yExtentDiff < 0)
		{   //Set new bottom Y for the line:
		    m_currentTextLineEndY += (-yExtentDiff);

		    //Bug fix for diagonal-motion text not
		    // always being placed correctly in X:
		    if(m_crawlRate  &&  m_scrollRate)
		    {
			m_currentTextLineEndX += pTC->getXExtent();
		    }

		    //Now we need to move all previous text on the same
		    // line as this one down by (-yExtentDiff), i.e.,
		    // subtract yExtentDiff from each:
#if defined(SHOW_CODE_CHANGE_MESSAGES)
#pragma message("EH- in "__FILE__", need to make sure text that moves \
				    down in Y here gets re-drawn") /* Flawfinder: ignore */
#endif
		    LISTPOSITION pos = GetEndPosition();

		    while(pos)
		    {
			TextContainer* pTC_prior =
				/*  Note: effectively does GetAt(pos--),
				 *  so the first time called it actually
				 *  gets the current ptr and moves pos
				 *  so that the next access will point to
				 *  the previous T.C. in the list:  */
				(TextContainer*)GetPrev(pos);

			HX_ASSERT_VALID_PTR(pTC_prior);
			if(pTC_prior)
			{
			    pTC_prior->setYAtTimeZeroUpperLeftCorner(
				    pTC_prior->
				    getYAtTimeZeroUpperLeftCorner() +
				    (-yExtentDiff) );

			    if(pTC_prior->isStartOfNewLine()  ||
				    (pTC_prior->isFakeNewLine()  &&
				    pTC_prior->getStartTime()==
				    pTC_prior->getTimeOfLastClearTag())	 )
			    {	
				/*  Prior text starts new line so text prior
				 *  to it should not be Y-aligned.  */
				break; 
			    }
			}
		    }
		}
		else
		{
		    ;//nothing to adjust; already aligned with previous text.

		    //Bug fix for diagonal-motion text not
		    // always being placed correctly in X:
		    if(m_crawlRate  &&  m_scrollRate)
		    {
			m_currentTextLineEndX += pTC->getXExtent();
		    }
		}
	    } //end "if(pTC_prev != NULL)".
	    else 
	    {
		//This could also be just a crawlrate-only
		// window, so see if a fake newline should be made:
		if(m_crawlRate  &&  !m_scrollRate)
		{
		    if(pTC->getXAtTimeZeroUpperLeftCorner()-m_xOfFakeNewLine 
			    > getWidth())
		    {
			pTC->isFakeNewLine(TRUE);
			m_xOfFakeNewLine = 
				pTC->getXAtTimeZeroUpperLeftCorner();
		    }
		}

		//  This is the first block of text, so set
		//  m_currentTextLineEndY to its bottomY val:
		m_currentTextLineEndY =
			m_currentTextLineStartY + pTC->getYExtent();
		//Added the following line:
		m_currentTextLineEndX =
			m_currentTextLineStartX + pTC->getXExtent(); 
		
	    } //end "else" of "if(pTC_prev != NULL)".
	} //end "else" of "if(TYPE_TICKERTAPE == m_type)"
    } //end "else if" of "if((newLines = pTC->getNumNewlinesAtStart()) > 0L)"
    else  //we've already set the X and Y's of this and of pTC.
    {
	; 
    } //end of else where bXYAtTimeZeroAlreadyFigured must be TRUE.

    ULONG32 ulCurrentTime = getTimeOfLastTimeSync();
        
    LONG32 xAtTimeZero = pTC->getXAtTimeZeroUpperLeftCorner();
    LONG32 yAtTimeZero = pTC->getYAtTimeZeroUpperLeftCorner();

    if(bDealingWithTabCharWithPre)
    {
	//If only 1 char, then this is a tab-char-only TC so use
	// xAtTimeZero in the following calculations, otherwise
	// use xAtTimeZero+xExtent:
	LONG32 effectiveEndXforTabStopCalculating = xAtTimeZero;
	if(1 != pTC->getTextLengthPlus1()-1)
	{
	    effectiveEndXforTabStopCalculating += pTC->getXExtent();
	}
	//we need to adjust X extent of pTC as well as the
	// m_currentTextLineEndX to where the next "tab stop"
	// is since we're dealing with a tab char in PRE-formated text;
	// tab stops are every 8 characters, which are, as Netscape
	// (but not IE) does it, every: (8 chars * (ptSize/2 pixels)) =
	// (4 * ptSize).  This evaluates to 64 in the default pt size of 16:
	LONG32 nextTabStopDistanceInPixels =
		(4*pTC->getFontPointSize()) - 
		(effectiveEndXforTabStopCalculating %
		(4*pTC->getFontPointSize()) );
	if(0L == nextTabStopDistanceInPixels)
	{
	    nextTabStopDistanceInPixels += (4*pTC->getFontPointSize());
	}
	m_currentTextLineEndX = effectiveEndXforTabStopCalculating + 
		nextTabStopDistanceInPixels;
	pTC->setXExtent(m_currentTextLineEndX-xAtTimeZero);
    }

    LONG32 xAtCurTime = xAtTimeZero -
	    //NOTE: the casting to double here is ONLY to avoid
	    // overflow and does not need special floating-point
	    // consideration for fixed-point code conversion,
	    // i.e., precision does not need to be maintainted:
	    (ULONG32)(((double)m_crawlRate*(double)ulCurrentTime) / 1000.0);
    LONG32 yAtCurTime = yAtTimeZero - 
	    //NOTE: the casting to double here is ONLY to avoid
	    // overflow and does not need special floating-point
	    // consideration for fixed-point code conversion,
	    // i.e., precision does not need to be maintainted:
	    (ULONG32)(((double)m_scrollRate*(double)ulCurrentTime) / 1000.0);
    pTC->setXUpperLeftCorner(xAtCurTime);
    pTC->setYUpperLeftCorner(yAtCurTime);

    m_bClearWasJustSent = FALSE;
    return (TextContainerList::insertAtEndOfList(pTC) );
}



/////////////////////////////////////////////////////////////////////////////
//
//  Is called in response to <RESET> tag being sent.
//  Goes through the list and, if any nodes' bounding boxes have
//  moved outside the window, deletes them.
//  Returns the number of nodes in the list that were deleted:
ULONG32 TextWindow::deleteAllNoLongerVisible()
{
    ULONG32 listSize = size();
    ULONG32 numTCsDeleted = 0L;

    if(listSize)
    {
	//start @ tail so GetPrev() is always valid after a RemoveAt(pos):
	LISTPOSITION pos = GetEndPosition();

	while(pos)
	{
	    TextContainer* pTC = (TextContainer*)GetAt(pos);

	    HX_ASSERT_VALID_PTR(pTC);
	    if(pTC)
	    {
#if defined(SHOW_CODE_CHANGE_MESSAGES)
#pragma message("EH- in "__FILE__", this doesn't handle negative \
		scroll or crawl yet, nor inidividual motion of TCs")
#endif

		BOOL bLastTimeSyncIsMoreRecent =
			IsTimeAMoreRecentThanTimeB(
			m_ulTimeOfLastTimeSync,
			pTC->getEndTime(),
			isLiveSource());

		if (isLiveSource()  &&  !m_ulTimeOfLastTimeSync)
		{
		    //XXXEH: We want to debug if we didn't get our time of
		    // last time sync initialized to the start time of the
		    // encoder (and it's guaranteed not to be 0 in live:
		    HX_ASSERT(0);
		    //0-valued m_ulTimeOfLastTimeSync is invalid if live, so
		    // assume there hasn't been one yet:
		    bLastTimeSyncIsMoreRecent = FALSE;
		}

		if(((pTC->getXLowerRightCorner() < 0  ||
			pTC->getYLowerRightCorner()
			+ getCurrentYOffsetForTeleprompter()
			< 0)
			//Added this to allow looping; this function will
			// still delete the pTC if its endtime is passed:
			&&  !isLooping())
			//added this additional time check:
			||  bLastTimeSyncIsMoreRecent)
		{
		    //Need this check because current YOffset might not
		    // have been adjusted back to 0 yet if this next T.C.
		    // immediately follows a <CLEAR> tag but has not been
		    // seen as valid yet in OnTimeSynch()
		    if(getCurrentYOffsetForTeleprompter() != 0L  &&
			    TYPE_TELEPROMPTER == getType())
		    {
			BOOL bStartTimeIsMoreRecent =
				IsTimeAMoreRecentThanTimeB(
				pTC->getStartTime(),
				m_ulTimeOfLastTimeSync,
				isLiveSource());
			//A little (in)sanity check:
			if(TIME_INVALID == m_ulTimeOfLastTimeSync)
			{
			    HX_ASSERT(bStartTimeIsMoreRecent);
			}

			if (isLiveSource()  &&  !m_ulTimeOfLastTimeSync)
			{
			    //XXXEH: We want to debug if we didn't get our
			    // time of last time sync initialized to the
			    // start time of the encoder (and it's guaranteed
			    // not to be 0 in live:
			    HX_ASSERT(0);
			    //0-valued m_ulTimeOfLastTimeSync is invalid if
			    // live, so assume there hasn't been one yet:
			    bStartTimeIsMoreRecent = TRUE;
			}

			if(bStartTimeIsMoreRecent)
			{
			    GetPrev(pos);
			    continue;
			}
		    }

		    TextContainer* pTempTC = (TextContainer*)GetAt(pos);

		    pos = RemoveAt(pos); //returns pos of next (or prev if at end)

		    HX_ASSERT_VALID_PTR(pTempTC);//Fixes mem leak:
		    if(pTempTC)  
		    {
			delete pTempTC;
			pTempTC = NULL;
		    }

		    LISTPOSITION tailPos = GetEndPosition();
		    if(pos != tailPos)
		    {
			GetPrev(pos);
		    }
		    //else we're already at previous node.

		    numTCsDeleted++;
		    continue;
		}
	    }

	    GetPrev(pos);
	} //end "while(pos)".
    } //end "if(listSize)".	
    return numTCsDeleted;   
}


/////////////////////////////////////////////////////////////////////////////
//
//  When a new line of text is being inserted in the list, if a <CENTER> tag
//  was active in the prior line of text, we'll need to center that prior
//  line of text in x based on the actual width of the renderer's window:
//
//  Returns TRUE if the prior line was centered, FALSE otherwise:
//
BOOL TextWindow::CenterPriorLine()
{
    if(getPriorLineAlreadyCentered())
    {
	setPriorLineAlreadyCentered(FALSE); //-NEXT line isn't yet centered.
	return TRUE; //nothing more to do.
    }
    BOOL bRetVal = FALSE;
    //Now, we need to go through all prior T.C.s in the prev
    // line of text & adjust their x's so they are centered:
#if defined(SHOW_CODE_CHANGE_MESSAGES)
#pragma message("EH- in "__FILE__", need to make sure text that moves \
			over in X here gets re-drawn:")
#endif
    LISTPOSITION pos = GetEndPosition();
    TextContainer* pTC_prior = NULL;
    if(pos)
    {
	pTC_prior =
	    //Note: effectively does GetAt(pos--),
	    // so the first time called it actually
	    // gets the current ptr and moves pos
	    // so that the next access will point to
	    // the previous T.C. in the list:
	    (TextContainer*)GetPrev(pos);
	HX_ASSERT_VALID_PTR(pTC_prior);
    }
    if(pTC_prior)
    {
	if(pTC_prior->isCentered())
	{
	    // BHG changed from getWindowWidth() to getWidth() since we
	    // now scale to window in the draw code!
	    LONG32 xAdjustment = (getWidth() -
		    pTC_prior->getXLowerRightCorner()) / 2;
	    BOOL bFirstTimeThrough = TRUE;
	    do
	    {
		if(bFirstTimeThrough)
		{
		    bFirstTimeThrough = FALSE;
		}
		else
		{
		    pTC_prior = (TextContainer*)GetPrev(pos);
		}

		HX_ASSERT_VALID_PTR(pTC_prior);
		if(pTC_prior)
		{
		    HX_ASSERT(pTC_prior->isCentered());
		    pTC_prior->setXAtTimeZeroUpperLeftCorner(
			    pTC_prior->
			    getXAtTimeZeroUpperLeftCorner() +
			    xAdjustment );

		    bRetVal = TRUE;

		    if(pTC_prior->isStartOfNewLine()  ||
			    (pTC_prior->isFakeNewLine()  &&
			    pTC_prior->getStartTime()==
			    pTC_prior->getTimeOfLastClearTag()) )
		    {	
			//Prior text starts new line so text
			// prior to it has already been adjusted
			break; 
		    }
		}
	    }while(pos);
	} //end if.
    }//end "if(pTC_prior)".	    

    return bRetVal;
}


#if defined(USE_DIB_SECTION)
/////////////////////////////////////////////////////////////////////////////
//
//  Get new space for bitmapinfo struct (usually plus some bytes for color
//  masks):
BOOL TextWindow::AllocNewLPBITMAPINFO(UINT32 ulNumBytes)
{
    if(m_LPBITMAPINFO)
    {	//This SHOULD have been initialized to NULL, so free it:
	delete m_LPBITMAPINFO;
	m_LPBITMAPINFO = NULL;
    }
    m_LPBITMAPINFO = (LPBITMAPINFO) new BYTE[ulNumBytes];
    return (NULL != m_LPBITMAPINFO);
}
#endif //USE_DIB_SECTION.


