/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: c_rtrndr.cpp,v 1.1.2.1 2004/07/09 01:50:49 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 ***** */

/////////////////////////////////////////////////////////////////////////////
//
//  C_RTRNDR.CPP
//
// Method:
//  ULONG32 RealTextRenderer::OnHeader(void* pData,	ULONG32 dataLength);
// Purpose:
//  Gets the header information from the pData string and fills 
//  m_pTextWindow's fields appropriately. 
// Returns:
//  Returns the index of pData where the <WINDOW ..> tag's 
//  '>' char is, plus 1.  Returns dataLength if failure to find
//  valid <WINDOW..> tag.
//
//
// Method:
//  void RealTextRenderer::OnData(void* pData, ULONG32 dataLength,
//				  BOOL bFromOnPacket)
// Purpose:
//  This function receives the latest packet's raw data (in pData) and
//  inserts it into the (*m_pTextWindow)::TextContaierList with the latest
//  render attribute in (*m_pTextWindow)::TextAttributeStacks.
//
//
// Method:
//  BOOL RealTextRenderer::OnTimeSynch(ULONG32 ulCurTime);
// Purpose:
//  For each TextContainer of (*m_pTextWindow)::TextContainerList, move its
//  text's location by an amount determined by the crawlrate and scrollrate
//  and  the difference between the current time and the last time the text
//  was rendered.  If any TextContainer's text has moved, invalidate the 
//  area that needs updating.
//  When finished re-rendering all text that has moved, update the window so
//  that the changes will appear.
//  Note: "ulCurTime" is the number of milliseconds since the stream started.
//
//

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

#if defined(_WINDOWS)
#include <windows.h>
#endif

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

#include "hxstack.h"
#include "hxslist.h"

#include "rt_types.h" //for _CHAR, RED_GREEN_OR_BLUE, COLORTYPE

#include "fontdefs.h"
#include "txtattrb.h"
#include "txtcntnr.h"
#include "textline.h"

#include "hxstrutl.h"

#include "txtwindw.h" //for class TextWindow.
#include "parsing.h"  //for parsing helper functions.
#include "atocolor.h" //for string-to-COLORTYPE conversion functions.
#include "rt_string.h" //for stringCompare().
#include "atotime.h"
#include "textprsr.h"  //added parent class TextParser.
#include "fontinfo.h" //for GetCharacterWidth().

#include "c_rtrndr.h"

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

#if defined(_DEBUG)
// /#define XXXEH_TESTING_DATAURLPLAINTEXT_HANDLING
#endif


#define MIN_ACCEPTABLE_PLAINTEXT_CHAR_WIDTH 3
#define MIN_ACCEPTABLE_PLAINTEXT_HEIGHT	    5


BOOL
ListOfPacketDataIDsReceived::HaveAlreadySeenThisData(ULONG32 ulPacketDataID)
{
    if(Find((void *)ulPacketDataID, NULL) != NULL)
    {
	return TRUE;
    }
    else
    {
	//Insert it at the START of the list (because Find() will start
	// at the start of the list and will find more-recent IDs there
	// and thus calls to HaveAlreadySeenThisData() will be quicker
	// since chances are we've seen ulPacketDataID very recently if
	// at all:
	AddHead((void *)ulPacketDataID);
	return FALSE;
    }
}




///////////////////////////////////////////////////////////////////////////// 
//  Returns 1 + (index of pData where the <WINDOW ..> tag's '>' char is).
//		returns dataLength if failure to find valid <WINDOW..> tag.
ULONG32 RealTextRenderer::OnHeader(void* pData, ULONG32 dataLength)
{
    //Now parse pData:

#if !defined(_CHARsizeInBytesIs1)
#error this code needs to be updated...
#endif

    _CHAR* pData_CHAR = (_CHAR*)pData;

    if(!m_pTextWindow)
    {
	return dataLength;
    }

    /*	Find the first '<' and then find the first '>' or end-of-data, and
     *  send the contents found to m_pTextWindow->parseHeaderTag(): */
    _CHAR* pHeaderTagBuf;
    LONG32 headerTagBufLen;

    LONG32 indexOfLeftBracket = -1;
    LONG32 indexOfRightBracket = -1;
    LONG32 indx;
    LONG32 len = LONG32(dataLength);
    for(indx=0; indx<len; indx++)
    {
/*XXXEH- for now, we have to assume that the first text encountered is not
  DBCS text; it and all text inside tags must be us-ascii charset:
	//added the following to handle DBCS chars:
	if((UCHAR)pData_CHAR[indx] >= DBCS_MIN_LEAD_BYTE_VAL)
	{
	    indx++; //skip this and the following trail byte.
	    continue;
	}
*/	
	if(pData_CHAR[indx] == '<')
	{
	    indexOfLeftBracket = indx;
	    break;
	}
    }
    if(indexOfLeftBracket != -1)
    {
	for(indx++; indx<len; indx++)
	{
/*XXXEH- for now, we have to assume that the first text encountered is not
  DBCS text; it and all text inside tags must be us-ascii charset:
	    //added the following to handle DBCS chars:
	    if((UCHAR)pData_CHAR[indx] >= DBCS_MIN_LEAD_BYTE_VAL)
	    {
		indx++; //skip this and the following trail byte.
		continue;
	    }
*/
	    if(pData_CHAR[indx] == '>')
	    {
		indexOfRightBracket = indx;
		break;
	    }	
	}
    }
    if(-1 == indexOfLeftBracket  ||  -1 == indexOfRightBracket  ||
	    ((indexOfRightBracket-indexOfLeftBracket)-1) <
	    LONG32(int(strlen("WINDOW"))) )
    {
	char* pszWrtstr = new char[64];
	if (!pszWrtstr)
	{
	    return 0;
	}
    	strcpy(pszWrtstr, "WINDOW type="); /* Flawfinder: ignore */
	//Added this to allow <HTML> to be the first tag:
	if( ((indexOfRightBracket-indexOfLeftBracket)-1) >=
		LONG32(int(strlen("HTML"))) )
	{
	    if(('H' == pData_CHAR[indexOfLeftBracket]  ||
		    'h' == pData_CHAR[indexOfLeftBracket])  &&
		    ('T' == pData_CHAR[indexOfLeftBracket+1]  ||
		    't' == pData_CHAR[indexOfLeftBracket+1])  &&
		    ('M' == pData_CHAR[indexOfLeftBracket+2]  ||
		    'm' == pData_CHAR[indexOfLeftBracket+2])  &&
		    ('L' == pData_CHAR[indexOfLeftBracket+3]  ||
		    'l' == pData_CHAR[indexOfLeftBracket+3])    )
	    {	//XXXEH- need to handle type=html in parseHeaderTag():
	    	strcat(pszWrtstr, "HTML"); /* Flawfinder: ignore */
	    }
	}
	else
	{
	    //Added this to allow files with no header tag:
	    // No '<' was found (or no valid header tag was found),
	    //Need to set default vals to type
	    // generic's default vals here:
	    //XXXEH- need to handle type=plaintext in parseHeaderTag():
	    strcat(pszWrtstr, "plaintext"); /* Flawfinder: ignore */
	}
	m_pTextWindow->parseHeaderTag((_CHAR*)pszWrtstr, strlen(pszWrtstr),
		m_ulRTMarkupParsingMajorVersion,
		m_ulRTMarkupParsingMinorVersion);
	delete [] pszWrtstr;
	pszWrtstr = NULL;
	if(-1 == indexOfRightBracket)
	{   //XXXEH- should dataLength be returned here?!:
	    return 0L; //signals that no header tag was found (and
			//that the text starts at byte zero of file).
	}
	else
	{
	    return (indexOfRightBracket+1);
	}
    }

    //	Next, parse the header's tag after copying it into pHeaderTagBuf:
    headerTagBufLen = indexOfRightBracket-indexOfLeftBracket-1;
    pHeaderTagBuf = new _CHAR[headerTagBufLen+1];//Add 1 for terminating '\0'
    HX_ASSERT_VALID_PTR(pHeaderTagBuf);
    if(NULL == pHeaderTagBuf)
    {
	return dataLength;  //return end-of-pData index to signal error.		
    }

    for(indx=0; indx<headerTagBufLen; indx++)
    {
	pHeaderTagBuf[indx] = pData_CHAR[indx+indexOfLeftBracket+1];
    }
    pHeaderTagBuf[headerTagBufLen] = '\0';

    /*  Now parse the header to get the WINDOW tag, and, if it is
     *  found, get the "NAME=value" pairs and assign the TextWindow's
     *  appropriate objects' data to these requested values:  */
    if(!m_pTextWindow->parseHeaderTag(pHeaderTagBuf, headerTagBufLen,
	    m_ulRTMarkupParsingMajorVersion,
	    m_ulRTMarkupParsingMinorVersion))
    {	/*  Returned FALSE because of invalid header tag:  */
	delete [] pHeaderTagBuf;
	pHeaderTagBuf = NULL;
	return dataLength; //return end-of-pData index to signal error.							
    }

    delete [] pHeaderTagBuf;
    pHeaderTagBuf = NULL;

    return (indexOfRightBracket+1);
}


// /XXXEH- add this to hxmisc at some point; hxsmil/smlparse.cpp uses it too:
// /This is needed for calculating the max possible size that a plain-
// text window might grow to so that a guess at the max number of plain-
// text characters to be accepted can be calculated for fixing PR 78150:
void
RealTextRenderer::GetSystemScreenInfo(UINT32& rulScreenHeight,
				 UINT32& rulScreenWidth,
				 UINT32& rulScreenBitDepth)
{
#if defined(_WINDOWS)
    rulScreenHeight = (UINT32)GetSystemMetrics(SM_CYSCREEN);
    rulScreenWidth = (UINT32)GetSystemMetrics(SM_CXSCREEN);
    HDC hDCMain = GetDC(NULL); // /Get screen DC.
    if (hDCMain)
    {
	rulScreenBitDepth = (UINT32)GetDeviceCaps(hDCMain, BITSPIXEL);
	ReleaseDC(NULL, hDCMain);
    }
/* The following relies on X Windows, and we don't want to do that, so
   just use the numbers in the #else, below:
#elif defined(_UNIX) && (!(defined(_BEOS)))
    // /Pass NULL string to XOpenDisplay to get default display, which
    // is the one that we're playing to:
    Display* pDisplay = XOpenDisplay(NULL);
    if (pDisplay)
    {
	Screen* pScreen = XDefaultScreenOfDisplay(pDisplay);
	rulScreenHeight = (UINT32)HeightOfScreen(pScreen);
	rulScreenWidth = (UINT32)WidthOfScreen(pScreen);
	rulScreenBitDepth = (UINT32)DefaultDepthOfScreen(pScreen);
    }
 */
#elif defined(_MACINTOSH)
    // /XXXEH- note: on a Mac, you can have multiple display devices and our
    // player can actually play to more than one at once, and can be dragged
    // from one to the other while running.  Dynamic re-evaluation is needed.
    GDHandle mainGD = ::GetMainDevice();
    rulScreenHeight = (UINT32)((**mainGD).gdRect.bottom - (**mainGD).gdRect.top);
    rulScreenWidth = (UINT32)((**mainGD).gdRect.right - (**mainGD).gdRect.left);
    PixMapHandle pmh = (**mainGD).gdPMap;
    if (pmh)
    {
	rulScreenBitDepth = (UINT32)((**pmh).pixelSize);
    }
#else
// /    HX_ASSERT(0  &&  "need screen info from this OS");
    rulScreenHeight = 1600;
    rulScreenWidth  = 1200;
    rulScreenBitDepth = 32;
#endif
}

/////////////////////////////////////////////////////////////////////////////
// Method:
//  void RealTextRenderer::OnData(void* pData, ULONG32 dataLength,
//				  BOOL bFromOnPacket)
// Purpose:
//  This function receives the latest packet's raw data (in pData) and
//  inserts it into the m_pTextWindow::TextContaierList with the latest
//  render attribute in m_pTextWindow::TextAttributeStacks.
//
void RealTextRenderer::OnData(void* pData, ULONG32 dataLength, BOOL bFromOnPacket)
{
    //Look through the data for the first valid markup tag.  Any text found
    //	before that gets put in a new TextContainer object and is inserted
    //	into the TextContainerList part of the TextWindow object.  If that
    //	list is empty, a new TextContainer is added to it with the default
    //	characteristics for the text.  If a valid markup tag is found before
    //	the end of the pData is reached, the text that follows it, up to the
    //	next tag, goes into a new TextContainer object that is added to the
    //	list and has the text-rendering characteristic specified in that tag:
	
#if !defined(_CHARsizeInBytesIs1)
#error this code needs to be updated...
#endif
    _CHAR* pData_CHAR = (_CHAR*)pData;

    BOOL bDataCHARwasAllocd = FALSE;

    TextContainer* pTC = NULL;

  // /Part of fix for PR 59951: if this is an in-line <text> source in a
  // SMIL 2.0+ file, then we want to treat this as plain text which has
  // no file header:
  // /Handle .txt files this way too, now:
  if (isPlainText())
  {
	// /Helps fix PR 78150: if m_ulPlainTextDataLen is too large for
	// the window, then we can safely ignore the rest.  This prevents
	// system-overload problems when someone renames, say, a 40MB mpg file
	// to .txt.  We can't use the point size nor the window width and
	// height to calculate this because those might be animated (changed)
	// on the fly by SMIL2 renderer:
	if (dataLength > 0)
	{
	    UINT32 rulScreenHeight = 0;
	    UINT32 rulScreenWidth = 0;
	    UINT32 rulScreenBitDepth = 0;
	    // /Could change dynamically, so redo each pkt:
	    GetSystemScreenInfo(rulScreenHeight, rulScreenWidth,
		    rulScreenBitDepth);

	    ULONG32 ulMaxCharsOnOneLine = (rulScreenWidth /
		    MIN_ACCEPTABLE_PLAINTEXT_CHAR_WIDTH) *2 /* *2 for safety*/;
	    ULONG32 ulMaxRowsOfPlainText = (rulScreenHeight /
		    MIN_ACCEPTABLE_PLAINTEXT_HEIGHT) *2 /* *2 for safety*/;
	    ULONG32 ulMaxBytesToBeSentByFF =
		    m_ulMaxPlainTextBytesToBeSentByFF;
	    m_ulMaxAllowedPlainTextCharsOnThisSystem =
		    ulMaxCharsOnOneLine * ulMaxRowsOfPlainText;
	    if (m_ulMaxAllowedPlainTextCharsOnThisSystem >
		    ulMaxBytesToBeSentByFF)
	    {
		m_ulMaxAllowedPlainTextCharsOnThisSystem = ulMaxBytesToBeSentByFF;
	    }

	    // /If no wordwrap and CR & LF chars are treated literally (i.e.,
	    // not as spaces), then the above calculation may not be correct
	    // if there is a really long line followed by a CR or LF followed
	    // by another line of text that should be drawn because it's not
	    // past the bottom of the window yet.  In all other cases, the
	    // above calculation is sufficient for fixing PR 78150 without
	    // breaking valid content:
	    if (!m_pTextWindow->usingWordwrap()  &&
		    !m_pTextWindow->wasVertAlignExplicitlySet() )
	    {
		if (m_ulNoWrdWrpNoVertAlignPlainTxtCRsAndLFsSoFar >
			ulMaxRowsOfPlainText)
		{
		    goto cleanup; /* [X] tested. */
		}
		else
		{
		    _CHAR* pTmp = pData_CHAR;
		    ULONG32 ulCount = dataLength;
		    _CHAR pPrevChar = '\0';
		    BOOL bNewlineFoundInPacket = FALSE;
		    do
		    {
			if ('\n' == *pTmp  &&  '\r' == pPrevChar)
			{
			    pPrevChar = *pTmp;  // /maintain original '\n'.
#if defined(_MACINTOSH)
			    // /Fixes PR 81110: replace all "\r\n"s with " \r"; that way,
			    // on the Mac, \r\n will display as an unnoticed space plus a
			    // single newline without the '\n' displaying as a box.  Also,
			    // this is much more efficient than removing the \n since that
			    // would require walking the entire buffer and shifting chars:
			    *(pTmp-1) = ' ';
			    *pTmp     = '\r';
#endif /* _MACINTOSH*/			    
			    
			    continue; // /[\r\n] == 1 newline
			}
			if ('\r' == *pTmp  ||  '\n' == *pTmp)
			{
			    bNewlineFoundInPacket = TRUE;
			    m_ulNoWrdWrpNoVertAlignPlainTxtCharsSinceLastCRorLF = 0;
			    m_ulNoWrdWrpNoVertAlignPlainTxtCRsAndLFsSoFar++;
			    
			    pPrevChar = *pTmp;  // /maintain original '\n'.
			    // /More fix for PR 81110: if we see a '\n' alone, treat it
			    // as a '\r':
			    if ('\n' == *pTmp)
			    {
				*pTmp = '\r';
			    }			    
			}
			else
			{
			    m_ulNoWrdWrpNoVertAlignPlainTxtCharsSinceLastCRorLF++;
			    pPrevChar = *pTmp;
			}
		    } while (ulCount--  &&  *(pTmp++));

		    // /We can toss this whole packet if we didn't find a newline
		    // char AND if we already have enough in the current line to
		    // go past the width of the screen at min reasonable char width:
		    if (!bNewlineFoundInPacket  &&
			    (m_ulNoWrdWrpNoVertAlignPlainTxtCharsSinceLastCRorLF -
			    dataLength) > ulMaxCharsOnOneLine)
		    {
			goto cleanup; /* [ ] tested. */
		    }
		}
	    }
	    else if (!m_pTextWindow->usingWordwrap())
	    {
		m_ulMaxAllowedPlainTextCharsOnThisSystem = ulMaxCharsOnOneLine;
		if (m_ulPlainTextDataLen > ulMaxCharsOnOneLine)
		{
		    goto cleanup; /* [X] tested. */
		}
	    }
	    else // /Using wordwrap:
	    { 
		if (m_ulPlainTextDataLen >
			m_ulMaxAllowedPlainTextCharsOnThisSystem)
		{ 
		    goto cleanup; /* [X] tested. */
		} 
#if defined(_MACINTOSH)
		_CHAR* pTmp = pData_CHAR;
		ULONG32 ulCount = dataLength;
		_CHAR pPrevChar = '\0';
		do
		{ 
		    if ('\n' == *pTmp)
		    {
			// /Fixes PR 81110 wordWrap="true" version (see above for
			// description in non-wordwrap version):
			if ('\r' == pPrevChar)
			{
			    *(pTmp-1) = ' '; // / '\r\n' ==> ' \r'
			}
			pPrevChar = *pTmp; // /maintain original '\n'.
			
			// /Convert '\n' to '\r':
			*pTmp = '\r';
		    }
		    else
		    {
			pPrevChar = *pTmp;
		    }
		} while (ulCount--  &&  *(pTmp++));
#endif /* _MACINTOSH*/			    
	    }
	}

	BOOL bIsFirstOnPacketData = FALSE;
	if (0 == m_ulPlainTextDataLen)
	{
	    bIsFirstOnPacketData = bFromOnPacket;
	    setPlainTextData(pData_CHAR);
	}
	else if (bFromOnPacket)
	{
	    // /If this is from an OnPacket() call, tack the data onto what's
	    // there so far:
	    appendPlainTextData(pData_CHAR);
	}
	if (0 == m_ulPlainTextDataLen)
	{
	    HX_ASSERT(m_ulPlainTextDataLen);
	    return; // /No text to show.
	}

	// /Better fix for PR 59981 (+ helps fix PR 58784 & PR58791):
	if (!bFromOnPacket  ||  bIsFirstOnPacketData)
	{
	    // /This is a reformatting call, not new data:
	    m_pTextWindow->SetLatestSentTimeToRender(0);
	    m_pTextWindow->SetLatestSentTimeToStopRendering(
		    (ULONG32)ULONG_MAX);
	    m_pTextWindow->m_bClearWasJustSent = TRUE;
	    m_pTextWindow->clearNumBreakTagsEncountered();

	    pData_CHAR = new _CHAR[m_ulPlainTextDataLen+1];
	    HX_ASSERT(pData_CHAR);
	    if (pData_CHAR)
	    {
		bDataCHARwasAllocd = TRUE;
		strcpy((char*)pData_CHAR, m_pPlainTextData); /* Flawfinder: ignore */
		m_pTextWindow->TextContainerList::flush(); // /Get rid of prior text.
	    }
	}

	pTC = new TextContainer(m_pPlainTextData, m_ulPlainTextDataLen);

	if (pTC)
	{
	    // /This new pTC includes *all* packets' text so far, so remove
	    // prior one so we don't 
	    ULONG32 ulNumDeleted = m_pTextWindow->TextContainerList::flush();
	    HX_ASSERT(ulNumDeleted==(bIsFirstOnPacketData? 0 : 1));

	    if (!m_pTextWindow->insertAtEndOfList(pTC, TRUE, FALSE))
	    {
		delete pTC;
	    }
	    // /Now, since we've reset the text back to its original encoding
	    // (character set), we need to reset that flag to FALSE:
	    m_pTextWindow->setCharsetTranslatedForOS(FALSE);
	}

#if defined(XXXEH_TESTING_DATAURLPLAINTEXT_HANDLING)
FILE* f1 = ::fopen("c:\\LogDataURLhandling.txt", "a+"); ::fprintf(f1, "OnData(), pData_CHAR = {{{%s}}}\n", pData_CHAR);
::fclose(f1);
#endif
  }
  else // /Not plain text:
  {
    if(!pData_CHAR  ||  dataLength<1)
    {
	return; //there's nothing to do.
    }

    ULONG32 ulOriginalDataLength = dataLength;
    if('\0' == pData_CHAR[dataLength-1])
    {
	dataLength -= 1;
    }

    BOOL bIsLiveSource = m_pTextWindow->isLiveSource();

    //This will be set to FALSE as soon as we see some non-markup-tag text:
    SetWeAreInsidePacketOpaqueHeader(TRUE);
    
    //Find the first '<' or end-of-data, whichever comes first, and put
    //	any raw text found before it into the TextContainer list:

    LONG32 startIndex=0L;

    //Changed this to skip newlines only:
    //First, skip all newline characters at start of this string only
    // if this is the very first packet received (and is not live):
    IncrementCurrentPacketNum();
    //XXXXXEH- if the first n packets were LOST, we shouldn't do the
    // following (if n>=1), right?!?:
    if(GetCurrentPacketNum() <= 1L  &&  !bIsLiveSource  &&  !isPlainText())
    {
	for(startIndex=0L; startIndex<(LONG32)dataLength; startIndex++)
	{
	    if(pData_CHAR[startIndex] != '\n'  &&
		    pData_CHAR[startIndex] != '\r')
	    {
	        break;
	    }
	}
    }

    if((LONG32)dataLength == startIndex)
    {	//All that were found were newline chars, so quit:

	if (bDataCHARwasAllocd  &&  pData_CHAR)
	{
	    delete [] pData_CHAR;
	}
	return; 
    }

    //Reset the ptr and dataLength to where the first
    // non-newline is:
    dataLength = dataLength-startIndex;
    pData_CHAR = &(pData_CHAR[startIndex]);
    startIndex = 0L;

    //TEXTPRSR_DIFF:
    //Added this so wordwrap calculating could be removed
    // from the renderer end; the FF now sends "<WR>"s whenever the text
    // that follows starts a new line due to wordwrapping (as calculated by
    // the file format in a platform-independent way):
    BOOL bIsWordWrapNewLine = FALSE;

    //TEXTPRSR_DIFF:
    //At start of packet, 
    m_pTextWindow->SetNewPktStartXAtTimeZero(INVALID_LONG32);
    m_pTextWindow->SetNewPktStartYAtTimeZero(INVALID_LONG32);


    LONG32 len = LONG32(dataLength);
    //Got rid of this and replaced with 
    // m_pTextWindow's functions to keep track of this because there
    // was a bug if a <BR> was the last thing in a packet, using just
    // the following local variable meant that this info got lost
    // when this function was done, so the next packet's data would
    // not end up with the proper number of line breaks before it:
    ///LONG32 numBreakTagsEncountered = 0L;
    
    BOOL bSomeCharsFoundSinceLastBreakTag = FALSE;

    //For bug #6906:
    BOOL bSomeCharsFoundSinceLastPosTag = FALSE;
    BOOL bUserPosTagFoundSinceLastTextContainer = FALSE;

    ULONG32 ulCurCharset;

    //TEXTPRSR_DIFF >>
    BOOL bStartOfPacketNewlinesIsZero = FALSE;
    //<<TEXTPRSR_DIFF.

    ULONG32 ulNumPREtagNewlineCharsFound = 0L;

    do //Now find the next '<', starting at pData_CHAR[startIndex]:
    {
	ulCurCharset = m_pTextWindow->peekAtCharsetStack();
	LONG32 indexOfLeftBracket = -1;
	LONG32 indexOfRightBracket = -1;
	BOOL bSlashFoundAtEndOfTag = FALSE;
	BOOL bIgnoringNewlineChars = FALSE;
	BOOL bDealingWithTabCharWithPre = FALSE;
	LONG32 lIndexOfTabTag = -1;
	BOOL bNextTabInPlainTextIsAfterNextWhitespaceChar = FALSE;
	LONG32 indx;
	for(indx=startIndex; indx<len; indx++,
		bSomeCharsFoundSinceLastPosTag=TRUE,
		bSomeCharsFoundSinceLastBreakTag=TRUE)
	{
	    _CHAR ch = pData_CHAR[indx];

	    //added the following to handle DBCS chars:
	    if((ulCurCharset & HX_DBCS_CHARSET)  &&
		    (UCHAR)ch >= DBCS_MIN_LEAD_BYTE_VAL)
	    {
		indx++; //skip this and the following trail byte.
		continue;
	    }

	    if('<' == ch  &&  !isPlainText())
	    {
		indexOfLeftBracket = indx;
		bSomeCharsFoundSinceLastBreakTag=FALSE; //Fixes bug #6903.
		bSomeCharsFoundSinceLastPosTag=FALSE; //Helps fix bug #6906.
		break;
	    }
	    //Convert any tab chars outside a tag to spaces if we're not
	    // currently between a <PRE> and a </PRE>, else leave '\t' alone
	    // and let TextWindow::insertAtEndOfList() calculate where the
	    // next tab stop is:
	    BOOL bTabCharHandled = FALSE;
	    if('\t' == ch  ||  '\v' == ch
		    ||  '\0' == ch) //added this for safety.
	    {
		pData_CHAR[indx] = ' ';
		//treat tab char as a tab, not a space, inside PRE text:
		if(('\t' == ch)  &&  (m_pTextWindow->peekAtIsPreStack()  ||
			isPlainText()) )
		{
		    // pretend we've found the start and end of a tag so a
		    // new TC will be created with this char and the
		    // next will start just after this char.
		    {
			bTabCharHandled = TRUE;
			indexOfLeftBracket = indx;
			indexOfRightBracket = indexOfLeftBracket;
			bDealingWithTabCharWithPre = TRUE;
			lIndexOfTabTag = indx;
			bSomeCharsFoundSinceLastBreakTag = TRUE;
			bSomeCharsFoundSinceLastPosTag = TRUE;
			break; //indexOf[Left&Right]Bracket vars are set
				// to act as a fake tag just after this '\t'.
		    }
		}
	    }
	    //Reduce any string of newlines into either one or zero spaces;
	    // one space if !bSomeCharsFoundSinceLastBreakTag, 0 otherwise:
	    // UNLESS we're currently between a <PRE> and a </PRE>. in which
	    // case leave them alone.
	    // If we are ignoring extra spaces, then do this for spaces and
	    // tab chars, too:
	    else if(('\n' == ch  ||  '\r' == ch)  ||
		    (m_pTextWindow->IgnoreExtraSpaces()  &&  
		      ((!bTabCharHandled && ('\t' == ch  ||  '\v' == ch))  ||
		      ' ' == ch
		      )
		    )
		   )
	    {
		LONG32 firstNewlineIndex = indx;
		pData_CHAR[firstNewlineIndex] = ' ';
		indexOfLeftBracket = indx;
		if(indexOfLeftBracket+1==len
			//TEXTPRSR_DIFF>>
			// FF version of this code, in textprsr.cpp, doesn't
			// have to make sure that it won't go past the end
			// of the buffer because it always has a '\0' beyond
			// the dataLength given, whereas here we may have a
			// '\0' as the last element:
			&&  len<(LONG32)ulOriginalDataLength)
			//<<END TEXTPRSR_DIFF.
		{
		    //If we're at the end of the packet and it ends with a
		    // space or tab char and we're ignoring extra spaces,
		    // then we want to make sure NOT to ignore this last
		    // space if it's solo:
		    if('\n' != ch  &&  '\r' != ch  &&
			    m_pTextWindow->IgnoreExtraSpaces())
		    {
			indexOfLeftBracket++;
		    }
		}
		LONG32 newLineCharCount=1L;
		BOOL bNonSpaceTabNewlineCharWasLastFound = FALSE;
		for(indx++; indx<len; indx++, newLineCharCount++)
		{
		    _CHAR ch2 = pData_CHAR[indx];
		    if('\n' == ch2  ||  '\r'== ch2)
		    {
			pData_CHAR[indx] = ' ';
			if(m_pTextWindow->peekAtIsPreStack()  ||  isPlainText())
			// pretend we've found the start and end of a tag so
			// a new TC will be created with this char and the
			// next will start just after this char:
			{
			    //See if this is a PC "\r\n" newline:
			    if('\r' == pData_CHAR[indx-1]  &&  '\n' == ch2)
			    {
				indx++;
			    }
			    ulNumPREtagNewlineCharsFound++;
			}
		    }
		    else if(m_pTextWindow->IgnoreExtraSpaces()  &&
			    !m_pTextWindow->peekAtIsPreStack()  &&
			    (!bTabCharHandled && ('\t' == ch2  ||
				    '\v' == ch2)) )
		    {
			;
		    }
		    else if(m_pTextWindow->IgnoreExtraSpaces()  &&
			    !m_pTextWindow->peekAtIsPreStack()  &&
			    ' ' == ch2)
		    {
			;
		    }
		    else
		    {
			if('<'!=ch2)
			{
			    bNonSpaceTabNewlineCharWasLastFound = TRUE;
			}
			break;
		    }
		}
		indx--;	 //back up one for outer for loop.

		if(bSomeCharsFoundSinceLastBreakTag)
		{
//XXXXXEH-is fix for bug 4881 here?!?  If UNIX uses '\n' instead of "\r\n",
// and we don't handle that count==1 here, what in the Wide Wide World Of
// Sports happens??  (Needs checking in same code in textlib/textprsr.cpp)
		    //Doesn't follow a new line, so just reduce to 1 space:
		    if(newLineCharCount >= 2L)
		    {
			indexOfRightBracket =
				firstNewlineIndex + newLineCharCount - 1L;
			indexOfLeftBracket = firstNewlineIndex + 1L;
			bIgnoringNewlineChars = TRUE;
			bSomeCharsFoundSinceLastBreakTag = TRUE;
			break; //indexOf[Left&Right]Bracket vars are set
				// to act as a fake tag after the first one.
		    }
		}
		else 
		//we want to ignore all newline characters found
		// because they were preceded by a line break tag, so
		// pretend we've found the start and end of a tag so a new
		// TC will be created ending just before this char and the
		// next will start just after this char.
		{
		    indexOfLeftBracket = firstNewlineIndex;
		    indexOfRightBracket =
			    firstNewlineIndex + newLineCharCount - 1L;
		    bIgnoringNewlineChars = TRUE;
		    bSomeCharsFoundSinceLastBreakTag =
			    bSomeCharsFoundSinceLastPosTag =
			    bNonSpaceTabNewlineCharWasLastFound;
		    break; //indexOf[Left&Right]Bracket vars are set.
			    // to act as a fake tag where newlines are..
		}
	    }
	}

	if(-1L != indexOfLeftBracket  &&  -1L == indexOfRightBracket)
	{
	    //Check if we're inside an HTML-style ("<!-- ... -->") comment,
	    // and then ignore all '>'s until we see one preceeded by "--",
	    // i.e., only "-->" ends a comment:
	    if(len-indexOfLeftBracket >= 4)
	    {
		if(!stringCompare(&pData_CHAR[indexOfLeftBracket], 4,
			"<!--", 4))
		{
		    m_pTextWindow->incrementCommentTagNestCount();
		}
	    }

	     //find the closing '>':
	    for(indx++; indx<len; indx++)
	    {
		_CHAR ch = pData_CHAR[indx];

/*XXXEH- for now, we have to assume that the text encountered is not
  DBCS text; all text inside tags must be us-ascii charset:
		//added the following to handle DBCS chars:
		if((UCHAR)ch >= DBCS_MIN_LEAD_BYTE_VAL)
		{
		    indx++; //skip this and the following trail byte.
		    continue;
		}
*/
		if('>' == ch)
		{
		    //Check if we're inside an HTML-style ("<!-- ... -->")
		    // comment, which could contain a '>' (which should be
		    // ignored) before the closing "-->":
		    if(m_pTextWindow->getCommentTagNestCount())
		    {
			if(indx-startIndex >= 2)
			{
			    if(!stringCompare(&pData_CHAR[indx-2], 3,
				    "-->", 3))
			    {
				if(m_pTextWindow->
					decrementCommentTagNestCount() > 0L)
				{
				    continue; //we're still inside a nested
						// comment.
				}
			    }
			    else
			    {
				continue;
			    }
			}
		    }

		    indexOfRightBracket = indx;
		    //Added this to make this XML-compatible:
		    if(indexOfRightBracket>0)
		    {
			if(pData_CHAR[indexOfRightBracket-1] == '/')
			{
			    bSlashFoundAtEndOfTag = TRUE;
			    pData_CHAR[indexOfRightBracket-1] = ' ';
			}
		    }
		    break;
		}
		else if('<' == ch  &&
			m_pTextWindow->getCommentTagNestCount())
		{
		    //Check if we're a comment nested inside another
		    // ("<!-- ... -->") comment:
		    if(len-indx >= 4)
		    {
			if(!stringCompare(&pData_CHAR[indx], 4,
				"<!--", 4))
			{
			    m_pTextWindow->incrementCommentTagNestCount();
			}
		    }
		}

		//Convert any newline or tab chars inside tag to spaces:
		if('\n' == ch  ||  '\r' == ch  ||
				'\t' == ch  ||  '\v' == ch
				||  '\0' == ch)
		{
		    pData_CHAR[indx] = ' ';	 
		    //Note: don't need to track #of conversions inside tag
		}
	    }
	    if(-1 == indexOfRightBracket)
	    {	//No valid end-of-tag found, so ignore all text from
		//  indexOfLeftBracket on; this is done by putting a '\0'
		//  at pData_CHAR[indexOfLeftBracket]:
		pData_CHAR[indexOfLeftBracket] = '\0';
		len = indexOfLeftBracket - startIndex;
	    }
	}
	
	LONG32 tempLen;
	BOOL bPreTabOrNewlineCharOnly = FALSE;
	if(indexOfLeftBracket != -1)
	{
	    tempLen = indexOfLeftBracket - startIndex;
	    pData_CHAR[indexOfLeftBracket] = '\0';
	    if(!tempLen)
	    {
		//Special case where a tab char was the only text found
		// between tags, and, since we're faking like a PRE tab
		// is a tag, tempLen ended up 0 and the while loop, below,
		// was not getting entered:
		if(bDealingWithTabCharWithPre)
		{
		    bPreTabOrNewlineCharOnly = TRUE;
		}
	    }
	}
	else
	{
	    tempLen = len - startIndex;
	}


	ulCurCharset = m_pTextWindow->peekAtCharsetStack();

	//Added the following to allow for
	// word wrap; break the text up wherever there is a
	// space char.  (Also, Changed "if(tempLen.." to
	// "while(tempLen.."
	ULONG32 tmpStartIndex = startIndex;
	while(tempLen > 0  ||
		bPreTabOrNewlineCharOnly)
	{
	    ULONG32 theFollowingSpaceCharIndex = tempLen;
	    char savedChar = '\0';
	    _CHAR* pCurText = &pData_CHAR[tmpStartIndex];

	    if(m_pTextWindow->usingWordwrap()  &&
		    !m_pTextWindow->peekAtIsPreStack())
	    {
		//Allow wordwrap to happen in DBCS/UNICODE between chars, not
		// just where spaces are, because each character represents
		// a "word" and they are rarely separated by spaces:
		//XXXEH- NOTE: I assume that UNICODE characters that have a
		// zero-valued first byte are really SBCS characters and
		// should not get wordwrapped (because they may be in the
		// middle of a SB word):
		if(ulCurCharset & HX_UNICODE_CHARSET  ||
			ulCurCharset & HX_DBCS_CHARSET)
		{
		    UINT16 sCharWidthInPixels = 0;
		    UINT16 sChar = '\0';
		    LONG32 lNumBytesOfChar = 1L;
		    if(ulCurCharset & HX_UNICODE_CHARSET  ||
			    (ulCurCharset & HX_DBCS_CHARSET  &&
			    (UCHAR)(*pCurText)>= DBCS_MIN_LEAD_BYTE_VAL) )
		    {
			//Is a 2-byte character, so deal with it,
			// making sure there's a trail byte
			lNumBytesOfChar++;
			HX_ASSERT(tempLen>=lNumBytesOfChar);
			UCHAR tmp = *pCurText;
			sChar = (((UINT16)tmp)<<8 | (UCHAR)pCurText[1]);
		    }
		    else
		    {
			sChar = (INT16)((UCHAR)(*pCurText));
		    }

/*  XXXEH-need while loop on each char until we find which character
    exceeds the window's right edge and only then end the text blob...:
		    sCharWidthInPixels = GetCharacterWidth(
			    sChar,
			    m_pTextWindow->peekAtFontFaceStack(),
			    m_pTextWindow->peekAtPointSizeStack(),
			    //XXXEH- isBold and isItalicized need peek..()
			    // functions written for them; assuming they are
			    // TRUE only means wordwrap will happen a few
			    // pixels early, if that:
			    TRUE, TRUE,
			    ulCurCharset);

		    BOOL windowWidthExceeded = 
			    m_pTextWindow->getCurrentTextLineEndX() +
			    sCharWidthInPixels >
			    m_pTextWindow->getWidth();

    XXXEH- ...but for now, just make each (2-byte)DBCS/UNICODE character be in its
    own TextContainer, although this can be less efficient:
*/
		    //If we are going from DB char to DB char or from DB char
		    // to SB char, or from SB char to DB char, or SB char is
		    // a space, newline, or tab, or is a UNICODE character
		    // with a non-zero first byte, or is a UNICODE space, tab,
		    // or newline, then we can do a wordwrap
		    // after this char, otherwise we should not:
		    BOOL bNextCharExists = tempLen>lNumBytesOfChar;
		    //Initialize to opposite of what cur character is:
		    BOOL bNextCharIsTwoByte = (lNumBytesOfChar==1);
		    if(bNextCharExists)
		    {
			bNextCharIsTwoByte =
				(ulCurCharset & HX_UNICODE_CHARSET  &&
				((UCHAR)(pCurText[lNumBytesOfChar])!=0))  ||
			    (ulCurCharset & HX_DBCS_CHARSET  &&
			    (UCHAR)(pCurText[lNumBytesOfChar])>=
			    DBCS_MIN_LEAD_BYTE_VAL);
		    }
		    BOOL bIsUNICODEcharThatCanBeWordwrapped = FALSE;
		    if(ulCurCharset & HX_UNICODE_CHARSET)
		    {
			if(*pCurText!=0)
			{
			    bIsUNICODEcharThatCanBeWordwrapped = TRUE;
			}
			else
			{
			    UCHAR ch = pCurText[1]; 
			    if(' '==ch  ||  '\n'==ch  ||
				    '\r'==ch  ||  '\t'==ch)
			    {
				bIsUNICODEcharThatCanBeWordwrapped = TRUE;
			    }
			}
		    }

		    if((!(1==lNumBytesOfChar)  ||  bNextCharIsTwoByte)  ||
			    (1==lNumBytesOfChar  &&  (' '==*pCurText  ||
			    '\n'==*pCurText  ||  '\r'==*pCurText  ||
			    '\t'==*pCurText))  ||
			    bIsUNICODEcharThatCanBeWordwrapped )
		    {
			//End the text blob here so that wordwrap can occur
			// if this character extends off the right edge
			// of the window:
			theFollowingSpaceCharIndex = lNumBytesOfChar-1;
			savedChar = pCurText[theFollowingSpaceCharIndex+1];
			pCurText[theFollowingSpaceCharIndex+1] = '\0';
		    }
		    //If we're in a bunch of single-byte DBCS chars, then we
		    // need to find the end of them (or the first space, tab,
		    // newline therein), and allow for wordwrap there:
		    else if(1==lNumBytesOfChar  &&  tempLen)
		    {
			_CHAR* pTmpText = pCurText;
			LONG32 lTmpTextLen = tempLen;
			BOOL bSpaceTabOrNewlineFound=FALSE;
			do
			{
			    lNumBytesOfChar = (UCHAR)(*pTmpText)>=
				    DBCS_MIN_LEAD_BYTE_VAL? 2:1;
			    if(1==lNumBytesOfChar)
			    {
				if(' '==*pTmpText  ||  '\n'==*pTmpText  ||
					'\r'==*pTmpText  ||  '\t'==*pTmpText)
				{
				    bSpaceTabOrNewlineFound = TRUE;
				    break;
				}
			    }
			    else
			    {
				lTmpTextLen++;
				pTmpText--;
				break;
			    }
			    pTmpText++;
			    lTmpTextLen--;
			}while(lTmpTextLen);
			if(lTmpTextLen)
			{
			    //then we encountered a double-byte char or a
			    // space, tab, or newline char, so ok to wordwrap
			    // here:
			    theFollowingSpaceCharIndex = tempLen-lTmpTextLen;
			    savedChar=pCurText[theFollowingSpaceCharIndex+1];
			    pCurText[theFollowingSpaceCharIndex+1] = '\0';
			}
		    }
		}
		else //This is SBCS:
		{
		    ULONG32 firstNonSpaceCharIndex =
			    skipSpacesTabsAndNewlineChars(
			    pCurText, tempLen, 0);
		    ULONG32 dummyVar;
		    if(firstNonSpaceCharIndex < (ULONG32)tempLen)
		    {
			theFollowingSpaceCharIndex =
				findNextSpaceTabOrNewLineChar(
				pCurText, tempLen,
				firstNonSpaceCharIndex, dummyVar, ulCurCharset);
			if(theFollowingSpaceCharIndex+1 < (ULONG32)tempLen)
			{
			    savedChar = pCurText[theFollowingSpaceCharIndex+1];
			    if (lIndexOfTabTag >
				    (LONG32)theFollowingSpaceCharIndex  &&
				    isPlainText())
			    {
				bNextTabInPlainTextIsAfterNextWhitespaceChar = TRUE;
			    }
			    pCurText[theFollowingSpaceCharIndex+1] = '\0';
			}
			else
			{
			    lIndexOfTabTag = -1;
			    bNextTabInPlainTextIsAfterNextWhitespaceChar = FALSE;
			}
		    }
		}
	    }

	    if(!bPreTabOrNewlineCharOnly)
	    {
		//Now, look for "&lt;" or "&gt;" and, if found, end the
		// TextContainer at the end of the "&lt;" or "&gt;" and replace
		// it with '<' or '>'; also, look for "&#n;"
		// (where n>8 && n<=255) and translate into the ASCII char
		// with that value, and look for "&nbsp;" for ' ' and "&amp;"
		// for '&':
		ULONG32 ulIndexOfAmpersand = findNextChar('&',
			pCurText,
			theFollowingSpaceCharIndex,
			0L,
			ulCurCharset);
		ULONG32 ulIndexOfLastCharInTCsBuf=theFollowingSpaceCharIndex;
		ULONG32 theOriginalFollowingSpaceCharIndex =
			theFollowingSpaceCharIndex;
		if(theFollowingSpaceCharIndex > ulIndexOfAmpersand  &&
			theFollowingSpaceCharIndex - ulIndexOfAmpersand >= 4  &&
			// /Leave "&amp;"...etc. alone for plain text:
			!isPlainText())
		{
		    FindEscapeSequenceChar(pCurText,
			    ulIndexOfLastCharInTCsBuf, 
			    theFollowingSpaceCharIndex, ulIndexOfAmpersand,
			    ulCurCharset);
		}

		//Create a new one and add it to the list:
		pTC = new TextContainer(pCurText,
			ulIndexOfLastCharInTCsBuf+1>(ULONG32)tempLen?
			(ULONG32)tempLen:ulIndexOfLastCharInTCsBuf+1);
		//First, restore pData_CHAR:
		if(theOriginalFollowingSpaceCharIndex+1 < (ULONG32)tempLen)
		{
		    pCurText[theOriginalFollowingSpaceCharIndex+1] =
			    savedChar;
		}
		//then, reset tempLen and tmpStartIndex:
		{
		    tempLen -= theFollowingSpaceCharIndex+1;
		    tmpStartIndex += theFollowingSpaceCharIndex+1;
		}
	    }
	    else //is a tab char inside PRE so send a space char:
	    {
		//Create a new one and add it to the list:
		pTC = new TextContainer(" ", 1);
		bPreTabOrNewlineCharOnly = FALSE; //so while loop will quit.
	    }

	    HX_ASSERT(pTC);
	    if(!pTC)
	    {
		if (bDataCHARwasAllocd  &&  pData_CHAR)
		{
		    delete [] pData_CHAR;
		}
		return; //mem alloc error occurred, so quit.
	    }

	    //TEXTPRSR_DIFF >>:
	    //We've seen some text since the packet header so we can deal
	    // with <CENTER> and other tags properly, knowing that any such
	    // tags we see from here on in the packet are NOT in this
	    // packet's opaque data's header:
	    SetWeAreInsidePacketOpaqueHeader(FALSE);
	    //<< END TEXTPRSR_DIFF.

	    pTC->setNumNewlinesAtStart(
		    m_pTextWindow->getNumBreakTagsEncountered());
	    m_pTextWindow->clearNumBreakTagsEncountered();

	    //TEXTPRSR_DIFF >>:
	    bStartOfPacketNewlinesIsZero=FALSE;
	    //Added this so wordwrap calculating could be
	    // removed from the renderer end; the FF now sends "<WR>"s
	    // whenever the text that follows starts a new line due to
	    // wordwrapping (as calculated by the file format in a
	    // platform-independent way):
	    pTC->isWordWrapNewLine(bIsWordWrapNewLine);
	    bIsWordWrapNewLine = FALSE;
	    //<< END TEXTPRSR_DIFF.

	    //This has to be kept track of because insertAtEndOfList(),
	    // below, resets m_bClearWasJustSent to FALSE:
	    BOOL bClearWasJustSent = m_pTextWindow->m_bClearWasJustSent;

	    BOOL bRenderTimeIsSameOrMoreRecent =
		    IsTimeASameOrMoreRecentThanTimeB(
		    m_pTextWindow->GetLatestSentTimeToRender(),
		    m_pTextWindow->GetLatestSentTimeToStopRendering(),
		    bIsLiveSource);

	    //Reset the current endtime so all text
	    // that follows a <CLEAR> has an endtime equal to that of
	    // the end of the stream:
	    if(bClearWasJustSent
		    //This fixes a bug where the author wanted something
		    // to end at a certain time after the <CLEAR> but
		    // couldn't without a <time> tag after the <CLEAR>,
		    // e.g., "<time start=10 end=20><CLEAR>foo" should
		    // behave as follows: foo appears at 10 seconds and
		    // goes away at 20; however:
		    // "<time end=10>blah <time start=10><CLEAR>foo" should
		    // have blah appear up to 10 seconds and foo appear from
		    // 10 seconds until the end of the stream (or until the
		    // next CLEAR tag's start time).
		    &&  bRenderTimeIsSameOrMoreRecent)
	    {
		//If liveSource, start time of stream may not be at time 0
		// and ULONG_MAX may be *earlier* than cur time (since time
		// val is ULONG32 and may wrap); we want to set this to
		// "infinity" (which is 0xfffffffe):
		m_pTextWindow->SetLatestSentTimeToStopRendering(
			(ULONG32)ULONG_MAX);
		if(bIsLiveSource)
		{
		    m_pTextWindow->SetLatestSentTimeToStopRendering(
			    TIME_INFINITY);
		}
	    }

	    //Set begin and end render times of this new TextContainer
	    //  to the begin/end values of the most recent <TIME ..> tag:
           pTC->setBeginTime(
                   m_pTextWindow->GetLatestSentTimeToRender());
           pTC->setEndTime(
                   m_pTextWindow->GetLatestSentTimeToStopRendering());

	   if(m_pTextWindow->hasValidURL())
	    {
		pTC->copyIntoHrefBuf(m_pTextWindow->getURL(),
			m_pTextWindow->getLenURLbuf(),
			m_pTextWindow->getTargetOfURL() );
	    }

	    m_pTextWindow->setTextAttributesToTopsOfStacksVals(*pTC);
	    
	    if(bDealingWithTabCharWithPre  &&
		    !bSomeCharsFoundSinceLastBreakTag)
	    {
		//If all we've got is a tab char in the TC, don't
		// paint any bg color:
		pTC->setTextBackgroundColor(DEFAULT_TEXT_BGCOLOR);
	    }

	    //TEXTPRSR_DIFF >>:
	    // Do this first since insertAtEndOfList() checks time of last
	    // clear to see if it should pay attention to the POS tag or not:
	    if(bClearWasJustSent)
	    {
#if defined(XXXEH_TESTING_DATAURLPLAINTEXT_HANDLING)
FILE* f1 = ::fopen("c:\\LogDataURLhandling.txt", "a+");
::fprintf(f1, "\tOnData(), TimeOfLastClearTag was set\n");
::fclose(f1);
#endif
		m_pTextWindow->setTimeOfLastClearTag();
	    }
	    //<< END TEXTPRSR_DIFF:

	    bUserPosTagFoundSinceLastTextContainer = FALSE;

	    //2nd parameter, "TRUE", means this is being called from within
	    // the renderer's code (and not from live (rtlive) or the
	    // file format (rtffplin):
	    if(!m_pTextWindow->insertAtEndOfList(pTC,
		    //TEXTPRSR_DIFF:
		    TRUE,
		    // /In plain text, handling word wrap and tabs means that
		    // x y[tab] will be broken into "x " and "y" and tab
		    // should *not* be applied to space between "x " and "y":
		    bNextTabInPlainTextIsAfterNextWhitespaceChar?
		    FALSE : bDealingWithTabCharWithPre) )
	    {
		delete pTC;
	    }
	    else
	    {
		//TEXTPRSR_DIFF >>:
		//Convert to proper chars if charset is incosistent with OS,
		// but only with new content; don't "break" old content
		// --(XXXEH!?).  Also: XXXEH: docs need updating: version="1.2"
		if(m_pTextWindow->getMajorContentVersion() >
			REAL_TEXT_MAC_CHARSET_HANDLING_CONTENT_MAJOR_VERSION
			||
			(m_pTextWindow->getMajorContentVersion() ==
			REAL_TEXT_MAC_CHARSET_HANDLING_CONTENT_MAJOR_VERSION
			&&
			m_pTextWindow->getMinorContentVersion() >=
			REAL_TEXT_MAC_CHARSET_HANDLING_CONTENT_MINOR_VERSION)
			)
		{
		    pTC->ConvertNativeCharsetChars(TRUE /* in renderer.*/,
			    REALTEXT_MAX_CHARSET_LEVEL_SUPPORTED);
		}
		//Make sure packet-start "<..NEWLINES=..>" is erased for all
		// subsequent TextContainers in this packet that get inserted
		// into the list:
		m_pTextWindow->SetNumNewlinesStatedInPacketHeader(0L);
		//Don't adjust start time if looping because it will be
		// later than it should be if some looping has already
		// happened (which means *this's x,y at time 0 are higher
		// than their original vals, on which "adjustStart...()"
		// depends):
		if(!m_pTextWindow->isLooping())
		{
		//<< END TEXTPRSR_DIFF:

		    //Added this to adjust start & end times based on visibility
		    // in window, e.g., the start time of
		    // pTC might be 0 but it doesn't scroll into the window
		    // until 4300msec, so this calculates and adjusts to that:
		    BOOL bShouldNeverBeFalse =
			    pTC->adjustStartAndEndTimes(m_pTextWindow);
		    HX_ASSERT(bShouldNeverBeFalse);
		//TEXTPRSR_DIFF >>:
		}
		//<< END TEXTPRSR_DIFF.
		
		if(m_pTextWindow->isLooping()
			//TEXTPRSR_DIFF >>:
			&&  pTC->getEndTime() )	//XXXEH- Why not in FF???
			//<< END TEXTPRSR_DIFF.
		{
		    //We don't want to use the end time calculated in 
		    // adjustStartAndEndTimes() above because looping means
		    // the end time may be "infinite" since it could loop
		    // "forever"; however, if there is an active <TIME end=t>
		    // tag, use t instead of "infinity".
		    // NOTE: as soon as another CLEAR tag is seen, this
		    // T.C.'s endTime will be reset to time of the CLEAR:
		    pTC->setEndTime(m_pTextWindow->
			    GetLatestSentTimeToStopRendering());
		}

		if(bIsLiveSource  &&  TIME_INVALID == pTC->getEndTime())
		{
		    //Make end time be "infinity":
		    pTC->setEndTime(TIME_INFINITY);
		}

		//Moved this from below "CLEAR" parsing
		// code so that the adjustment would be made after the pTC's
		// official start time is known:
		if(bClearWasJustSent)
		{
		    ULONG32 ulTmpStartTime = pTC->getStartTime();
		    ULONG32 ulTmpEndTime = pTC->getEndTime();
		    m_pTextWindow->SetLatestSentTimeToRender(ulTmpStartTime);

		    //	is just after a <CLEAR> tag, so clear everything in
		    //	TextContainerList whose m_endTime is greater
		    //	than the latestSentTimeToRender:
		    m_pTextWindow->MarkAllForClear(  //(the T.C.List version)
			    bIsLiveSource);
		    //Now, since pTC is already inserted in the T.C.List, its
		    // endTime may have been adjusted in MarkAllForClear(),
		    // so let's restore it:
		    pTC->setStartTime(ulTmpStartTime);
		    pTC->setEndTime(ulTmpEndTime);

		    m_pTextWindow->setTimeOfLastClearTag();

		    //follows a CLEAR tag but has no "newlines" before it,
		    // so we can't set num newlines to non-zero, so this
		    // tells us we've started a newline with no prior spacing
		    pTC->isFakeNewLine(TRUE);
		}

		//TEXTPRSR_DIFF >>: (but is for renderer only):
		//These are needed so we can figure out how much looping has
		// occured since the last <CLEAR> tag became effective (NOT
		// when the <CLEAR> was parsed, i.e., NOW, which is probably
		// earlier):
		pTC->setTimeOfLastClearTag(
			m_pTextWindow->getTimeOfLastClearTag());
		//<< END TEXTPRSR_DIFF.
	    }
	} //end "while(tempLen > 0)".

	//Now, for PRE tag newlines found after pTC's text, increment
	// the num newlines to perform on the next TC:
	if(ulNumPREtagNewlineCharsFound)
	{
	    for(ULONG32 ix=0L; ix<ulNumPREtagNewlineCharsFound; ix++)
	    {
		m_pTextWindow->incrementNumBreakTagsEncountered();
	    }
	    ulNumPREtagNewlineCharsFound = 0L;
	}

	if(-1 == indexOfLeftBracket  ||  -1 == indexOfRightBracket)
	{
	    break; //We're done with pData.
	}

	if(bIgnoringNewlineChars  ||  bDealingWithTabCharWithPre)
	{
	    bIgnoringNewlineChars = bDealingWithTabCharWithPre = FALSE;
	    startIndex = indexOfRightBracket+1;//where next tag-search starts
	    if(startIndex >= len)
	    {
		break;
	    }
	    continue;
	}

	pData_CHAR[indexOfLeftBracket] = '<'; //retore the char.


	/*  Now, find out what's in the markup tag, first temporarily
	 *  NULL-terminating it where the tag ends, then converting it
	 *  to uppercase: */
	pData_CHAR[indexOfRightBracket] = '\0';
	_CHAR* pTagContents = &pData_CHAR[indexOfLeftBracket+1];
	ULONG32 tagContentsLen = indexOfRightBracket-indexOfLeftBracket-1;
	if(bSlashFoundAtEndOfTag) //XML-style end-of-tag "/" was found:
	{
	    tagContentsLen--;
	}
	convertToUpperCase(pTagContents, tagContentsLen);
	switch(pTagContents[0])
	{
	    case '/': //tag is the end of a binary tag, e.g. "</B>":
	    {
		if(tagContentsLen > 1)
		{
		    HandleEndTag(pTagContents, tagContentsLen,
			    bSomeCharsFoundSinceLastBreakTag,
			    bSomeCharsFoundSinceLastPosTag,
			    bUserPosTagFoundSinceLastTextContainer,
			    //TEXTPRSR_DIFF >>:
			    //these 3 are ignored by HandleEndTag() if we're
			    // calling this from renderer:
			    0L, 0L, 0L,
			    //FALSE means we're calling this function from renderer,
			    // (not from file format or live encoder):
			    FALSE);
			    //<< END TEXTPRSR_DIFF.
		}//end "if(tagContentsLen > 1)".
	    }//end "case '/':"
	    break;

	    case 'A':
		if(tagContentsLen > 2)
		{
		    if(' '==pTagContents[1])
		    {   
			//we've found "A " so far...
			_CHAR* pLval;
			_CHAR* pRval;
			_CHAR* pRestOfData = &pTagContents[2];
			ULONG32 restOfDataLen = tagContentsLen-2;
			ULONG32 nextTokenStartIndex, nextTokenEndIndex;
			ULONG32 nextValStartIndex, nextValEndIndex;
			do
			{
			    if(GetNextTokenLvalueRvaluePair(pRestOfData, restOfDataLen,
				    nextTokenStartIndex, nextTokenEndIndex,
				    nextValStartIndex, nextValEndIndex) )
			    {
				pLval = &pRestOfData[nextTokenStartIndex];
				pRestOfData[nextTokenEndIndex]='\0';
				pRval = &pRestOfData[nextValStartIndex];
				pRestOfData[nextValEndIndex]='\0';
			    }
			    else
			    {
				break;
			    }

			    if(!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "HREF", 4)  &&  
				    nextValEndIndex-nextValStartIndex > 0L)
			    {
				m_pTextWindow->setURL(pRval,
					nextValEndIndex-nextValStartIndex);
				//Added the following to set
				// the text that follows to the hyperlink color:
				m_pTextWindow->pushTextColorStack(
					m_pTextWindow->getLinkColor(), TRUE);
				m_pTextWindow->setNumberOfFontColorPushesInsideLinkText(0L);
				if(m_pTextWindow->usingUnderlineHyperlinks())
				{
				    m_pTextWindow->pushIsUnderlinedStack(TRUE);
				}
			    }
			    else if(!stringCompare(pLval,
				    nextTokenEndIndex-nextTokenStartIndex,
				    "TARGET", 6))
			    {
				//Redo this in case it was in quotes:
				convertToUpperCase(pRval,
					nextValEndIndex-nextValStartIndex);
				if(!stringCompare(pRval,
				    nextValEndIndex-nextValStartIndex,
				    "_PLAYER", 7))
				{
				    m_pTextWindow->setTargetOfURL(
					    URL_TARGET_PLAYER);
				}
				else if(!stringCompare(pRval,
				    nextValEndIndex-nextValStartIndex,
				    "_BROWSER", 7))
				{
				    m_pTextWindow->setTargetOfURL(
					    URL_TARGET_BROWSER);
				}
				else
				{
				    m_pTextWindow->setTargetOfURL(
					    URL_TARGET_INVALID);
				}
			    }

			    if(nextValEndIndex < restOfDataLen)
			    {
				if(0L == nextValEndIndex)
				{
				    break; //leave the do loop.
				}
				pRestOfData = &pRestOfData[nextValEndIndex+1];
				restOfDataLen -= (nextValEndIndex+1);
			    }
			    else
			    {
				restOfDataLen = 0L;
			    }

			} while(restOfDataLen > 0L);
		    }
		}
		break;

	    case 'B':
		if(!stringCompare(pTagContents, tagContentsLen,
			"B", 1)) //start of bold tag
		{
		    m_pTextWindow->pushIsBoldStack(TRUE);
		}
		//or maybe a line break tag:
		else if(!stringCompare(pTagContents, tagContentsLen,
			"BR", 2))
		{
		    //If we got <pos ..>x<br/>, where "x" contains no
		    // plain text (other than spaces, tabs, newlines),
		    // then don't do a line break at all, but rather
		    // update the NewPktStartYAtTimeZero() value:
		    if(bUserPosTagFoundSinceLastTextContainer  &&
			    !bSomeCharsFoundSinceLastPosTag)
		    {
			LONG32 lCurY =  m_pTextWindow->
				GetNewPktStartYAtTimeZero();
			HX_ASSERT(INVALID_LONG32 != lCurY);
			m_pTextWindow->
				SetNewPktStartYAtTimeZero(
				lCurY + DEFAULT_LINE_BREAK_SIZE);
		    }
		    else
		    {
			m_pTextWindow->incrementNumBreakTagsEncountered();
		    }
		    bSomeCharsFoundSinceLastBreakTag=0L;
		}
		break;

	    case 'C':
		// /XXXEH- NOTE: I discovered that this same code in textlib's
		// textprsr.cpp uses 5 for the second parameter of
		// stringCompare() which means that <clear /> works there but
		// not here where there can't be a bleepin' thing after the
		// word "clear" except a "/>".  I don't want to fix that here
		// because that will millions of existing rtrenderers will
		// fail to clear when they're supposed to if authors think
		// that just works because they're using some "fixed" version:
		// format
		if(!stringCompare(pTagContents, tagContentsLen,
			"CLEAR", 5)) 
		{
		    //TEXTPRSR_DIFF >>:
		    //All <clear> tags should begin a packet in live, now,
		    // so they will have been translated to <time lc...>:
//XXXXXEH-		    HX_ASSERT(!bIsLiveSource);
		    m_pTextWindow->m_bClearWasJustSent = TRUE;
		    //<< END TEXTPRSR_DIFF.
		    //Moved this out of txtwindw.cpp's
		    // insertAtEndOfList() so, if "<BR/><CLEAR>foo" is seen,
		    // the <BR/> will be ignored, but if <CLEAR><BR/>foo" is
		    // seen, the <BR/> will be honored:
		    m_pTextWindow->clearNumBreakTagsEncountered();
		    m_pTextWindow->setTimeOfLastClearTag();
		}
		else if(!stringCompare(pTagContents, tagContentsLen,
			"CENTER", 6))
		{   
		    m_pTextWindow->pushIsCenteredStack(TRUE);
		    //Do a line break, but only if 0 line breaks so far,
		    // and not if there is no raw text yet, i.e., not if
		    // this <CENTER> tag starts the data part of the file,
		    // nor if a <CLEAR> tag was just sent:
		    if(!m_pTextWindow->getNumBreakTagsEncountered()  &&
			    !m_pTextWindow->m_bClearWasJustSent  &&
			    m_pTextWindow->size()>0L  &&
			    //TEXTPRSR_DIFF >>:
			    //Don't do newline if NEWLINES=0 was in pkt hdr
			    // and we haven't seen any text since the latest
			    // packet arrived, i.e., and we are seeing this
			    // <CENTER> tag inside the packet header:
			    (bStartOfPacketNewlinesIsZero  ||
				    !WeAreInsidePacketOpaqueHeader())
			    //<< END TEXTPRSR_DIFF.
			    )
		    {
			//If we got <pos ..>x<center>, where "x" contains no
			// plain text (other than spaces, tabs, newlines),
			// then don't do a line break at all:
			if(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag)
			{
			    ;
			}
			else
			{
			    m_pTextWindow->
				    incrementNumBreakTagsEncountered();
			}
			bSomeCharsFoundSinceLastBreakTag=0L;
		    }
		}
		break;


	    //TEXTPRSR_DIFF >>: (but is for renderer only); this
	    // "<DATA ID=>" gets set in TextLine's OutputPacketHeaderString()
	    // and is the unique identifier of the data in this packet, data
	    // which could have been sent already in a prior packet.  The
	    // renderer must decide based on this ID if it has seen this data
	    // already and, if so, return and not parse the rest of this data:
	    case 'D':
		if(tagContentsLen >= 9) //9==min length (e.g. "DATA ID=8").
		{
		    if('A' == pTagContents[1]) 
		    {
			if(pTagContents[2]=='T'  &&  pTagContents[3]=='A'  &&
				pTagContents[4]==' ')
			{   //we've found "DATA " so far...
			    _CHAR* pLval;
			    _CHAR* pRval;
			    _CHAR* pRestOfData = &pTagContents[4];
			    ULONG32 restOfDataLen = tagContentsLen-4;
			    ULONG32 nextTokenStartIndex, nextTokenEndIndex;
			    ULONG32 nextValStartIndex, nextValEndIndex;
			    do
			    {
				if(GetNextTokenLvalueRvaluePair(pRestOfData,
					restOfDataLen,
					nextTokenStartIndex, nextTokenEndIndex,
					nextValStartIndex, nextValEndIndex) )
				{
				    pLval = &pRestOfData[nextTokenStartIndex];
				    pRestOfData[nextTokenEndIndex]='\0';
				    pRval = &pRestOfData[nextValStartIndex];
				    pRestOfData[nextValEndIndex]='\0';
				}
				else
				{
				    break;
				}

				_CHAR* tmpPtr = pRval;
				ULONG32 tokenValLen =
					nextValEndIndex-nextValStartIndex;

				BOOL bErr = FALSE;

				if(!stringCompare(pLval, 
					nextTokenEndIndex -
					nextTokenStartIndex,
					"ID", 2))
				{
				    ULONG32 ulPacketDataID = 0L;
				    //tmpPtr is always NULL-terminated
				    // at this pt:
				    ulPacketDataID =
					m_pTextWindow->
					string_to_ULONG32(tmpPtr, bErr);
				    if(!bErr)
				    {
					//This function checks the list to
					// see if this DataID has already
					// been seen and, if not, it adds it
					// to the list:
					if(HaveAlreadySeenThisData(
						ulPacketDataID))
					{
					    //We've seen this data already,
					    // so don't parse it again:
					    goto cleanup;
					}
				    }
				    //else ignore the tag; is invalid DATA
				} //end of "if( ..."ID"...)".

				if(nextValEndIndex < restOfDataLen)
				{
				    if(0L == nextValEndIndex)
				    {
					break; //leave the do loop.
				    }
				    pRestOfData = &pRestOfData[nextValEndIndex+1];
				    restOfDataLen -= (nextValEndIndex+1);
				}
				else
				{
				    restOfDataLen = 0L;
				}

			    } while(restOfDataLen > 0L);
			} //end "DATA " tag parsing.
		    }
		}
		break;
	    //<< END TEXTPRSR_DIFF.

	    case 'F':
		if(tagContentsLen > 5)
		{
		    if(pTagContents[1]=='O'  &&  pTagContents[2]=='N'  &&
			    pTagContents[3]=='T'  &&  pTagContents[4]==' ')
		    {   //we've found "FONT " so far...
			_CHAR* pLval;
			_CHAR* pRval;
			_CHAR* pRestOfData = &pTagContents[5];
			ULONG32 restOfDataLen = tagContentsLen-5;
			ULONG32 nextTokenStartIndex, nextTokenEndIndex;
			ULONG32 nextValStartIndex, nextValEndIndex;

			//Added the following variable to
			// keep track of all attributes that get pushed in
			// the following do loop so that the fontStack can
			// tell us what to do with a subsequent </FONT> tag;
			// E.g., "<FONT size=+2 color=red>" could get parsed
			// below and we need to keep track of the fact that
			// +2 and red were pushed on their respective stacks
			// at the same time so that a subsequent </FONT> can
			// force a pop of BOTH the size and the color stacks:
			ULONG32 ulCurrentStacksPushed = 0L;
			do
			{
			    if(GetNextTokenLvalueRvaluePair(pRestOfData,
				    restOfDataLen,
				    nextTokenStartIndex, nextTokenEndIndex,
				    nextValStartIndex, nextValEndIndex) )
			    {
				pLval = &pRestOfData[nextTokenStartIndex];
				pRestOfData[nextTokenEndIndex]='\0';
				pRval = &pRestOfData[nextValStartIndex];
				pRestOfData[nextValEndIndex]='\0';
			    }
			    else
			    {
				break;
			    }

			    _CHAR* tmpPtr = pRval;
			    ULONG32 tokenValLen =
				    nextValEndIndex-nextValStartIndex;

			    if(!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "SIZE", 4)  &&
				    //Don't use if size already pushed:
				    !(ulCurrentStacksPushed & FONT_SIZE))
			    {
				LONG32 lTmp = 0L;
				if(1L == tokenValLen)
				{
				    _CHAR ch = tmpPtr[0];
				    if(ch >= '0'  &&  ch <= '9')
				    {
					lTmp = LONG32(ch - '0');
					if(!lTmp)
					{
					    lTmp = 1L;
					}
					else if(lTmp > 7L)
					{
					    lTmp = 7L;
					}
				    }
				}
				//"+0" exists so default val could be had
				// without using </FONT> to get back to it:
				if(lTmp==3L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"+0", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
					    DEFAULT_FONT_PTSIZE, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==4L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"+1", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
					    FONT_SIZE_PLUS1, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==5L  ||  
					!stringCompare(tmpPtr, tokenValLen, 
					"+2", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
						    FONT_SIZE_PLUS2, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==6L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"+3", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
						    FONT_SIZE_PLUS3, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==7L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"+4", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
						    FONT_SIZE_PLUS4, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==2L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"-1", 2))
				{
				    m_pTextWindow->pushFontPointSizeStack(
						    FONT_SIZE_MINUS1, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(lTmp==1L  ||  
					!stringCompare(tmpPtr, tokenValLen,
					"-2", 2)
					)
				{
				    m_pTextWindow->pushFontPointSizeStack(
						    FONT_SIZE_MINUS2, FALSE);
				    ulCurrentStacksPushed |= FONT_SIZE;
				}
				else if(tokenValLen>=2)
				{
				    INT32 i = atoi(tmpPtr);
				    if(i < -2)
				    {
					m_pTextWindow->
						pushFontPointSizeStack(
						FONT_SIZE_MINUS2, FALSE);
					ulCurrentStacksPushed |= FONT_SIZE;
				    }
				    else if(i > 4)
				    {
					m_pTextWindow->
						pushFontPointSizeStack(
						FONT_SIZE_PLUS4, FALSE);
					ulCurrentStacksPushed |= FONT_SIZE;
				    }
				    //this "else" should only get entered
				    // if "-0" was seen; we have to push
				    // something for each <FONT> tag so that
				    // the next </FONT> tag doesn't cause
				    // an erroneous pop of the prior <FONT>
				    // tag's push:
				    else
				    {
					m_pTextWindow->
						pushFontPointSizeStack(
						DEFAULT_FONT_PTSIZE, FALSE);
					ulCurrentStacksPushed |= FONT_SIZE;
				    }
				}
			    } //end of "else if( ..."SIZE" ...)".

			    else if(!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "COLOR", 5)  &&
				    //Don't use if color already pushed:
				    !(ulCurrentStacksPushed & FONT_COLOR))
			    {
				COLORTYPE colortype_retVal;
				BOOL isAValidColor = FALSE;
				if(tokenValLen >= 1  &&  '#'==tmpPtr[0]  ||  
					(tokenValLen>=2  &&
					'\"'==tmpPtr[0]  &&  '#'==tmpPtr[1]) )
				{
				    //Color was presented as "#RRGGBB" in hex:
				    isAValidColor = 
					    convertColorValStringToCOLORTYPE(
					    tmpPtr, tokenValLen,
					    colortype_retVal);
				}
				//  Returns FALSE if colorName contains an
				//  unrecognized color:
				else
				{	//Color was named, e.g., "darkblue":
				    isAValidColor = convertColorNameToCOLORTYPE(
					    tmpPtr, tokenValLen, colortype_retVal);
				}
				if(isAValidColor)
				{
				    //Is A legal color name, so push it on stack:
				    m_pTextWindow->pushTextColorStack(
					    colortype_retVal, FALSE);
				    ulCurrentStacksPushed |= FONT_COLOR;
				    //Added the following to see
				    // if we're inside an <A> tag so that we can
				    // properly back out font colors after hyperlink
				    // text is done:
				    if(m_pTextWindow->hasValidURL())
				    {
					m_pTextWindow->
					        incrementNumberOfFontColorPushesInsideLinkText();
				    }
				}
				//else ignore the tag; is invalid color.
			    }  //end of "else if( ..."COLOR" ...)".
			    
			    else if(!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "BGCOLOR", 7)  &&
				    //Don't use if bgcolor already pushed:
				    !(ulCurrentStacksPushed & FONT_BGCOLOR))
			    {
				COLORTYPE colortype_retVal;
				BOOL isAValidColor = FALSE;
				if(tokenValLen >= 1  &&  '#'==tmpPtr[0]  ||  
					(tokenValLen>=2  &&
					'\"'==tmpPtr[0]  &&  '#'==tmpPtr[1]) )
				{
				    //Color was presented as "#RRGGBB" in hex:
				    isAValidColor = 
					    convertColorValStringToCOLORTYPE(
					    tmpPtr, tokenValLen,
					    colortype_retVal);
				}
				//  Returns FALSE if colorName contains an
				//  unrecognized color:
				else
				{	//Color was named, e.g., "darkblue":
				    isAValidColor = convertColorNameToCOLORTYPE(
					    tmpPtr, tokenValLen, colortype_retVal);
				}
				if(isAValidColor)
				{
				    //Is A legal color name, so push it on stack:
				    m_pTextWindow->pushTextBackgroundColorStack(
					    colortype_retVal, FALSE);
				    ulCurrentStacksPushed |= FONT_BGCOLOR;
				}
				//else ignore the tag; is invalid color.
			    }  //end of "else if( ..."BGCOLOR" ...)".

			    else if(!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "CHARSET", 7)  &&  tokenValLen>1  &&
				    //Don't use if charset already pushed:
				    !(ulCurrentStacksPushed & FONT_CHARSET))
			    {
				//  Returns FALSE if tmpPtr contains an 
				//  unrecognized charset:
				ULONG32 ulCharset_retVal = CHARSET__default;
				if('\"' == tmpPtr[0])
				{
				    tmpPtr=tmpPtr+1;
				    tokenValLen--;
				}
				if('\"' == tmpPtr[tokenValLen-1])
				{
				    tokenValLen--;
				}

				if (HXR_OK ==
					convertCharsetNameToCharsetULONG32(
					tmpPtr, tokenValLen,
					REALTEXT_MAX_CHARSET_LEVEL_SUPPORTED,
					ulCharset_retVal))
				{
				    //Is a legal charset name, so push it on stack:
				    m_pTextWindow->pushFontCharsetStack(
					    ulCharset_retVal, FALSE);
				    ulCurrentStacksPushed |= FONT_CHARSET;
				}
				//else ignore the tag; is invalid charset.
			    } //end of "else if( ..."CHARSET" ...)".

			    //Added this else-if to handle
			    // font "faces", e.g., "helvetica":
			    else if( (!stringCompare(pLval, 
				    nextTokenEndIndex-nextTokenStartIndex, 
				    "FACE", 4)  ||
					!stringCompare(pLval, 
					nextTokenEndIndex-nextTokenStartIndex, 
					"NAME", 4)
				    )  &&  tokenValLen>1  &&
				    //Don't use if face has already been pushed:
				    !(ulCurrentStacksPushed & FONT_FACE) )
			    {
				//XXXEH- Replace the
				// following hack with a function to do this:
				{//hack start:
				    if('\"' == tmpPtr[0])
				    {
					tmpPtr=tmpPtr+1;
					tokenValLen--;
				    }
				    if('\"' == tmpPtr[tokenValLen-1])
				    {
					tmpPtr[tokenValLen-1] = '\0'; 
					tokenValLen--;
				    }
				    tmpPtr[tokenValLen] = '\0';
				    ///convertToUpperCase(tmpPtr, tokenValLen);
				    {
					//Is an allowed font face name, so push it on
					// the stack:
					ULONG32 faceIndx =
						getFontFaceIndexFromString(
						tmpPtr, tokenValLen,
						m_pTextWindow->
						    getMajorContentVersion(),
						m_pTextWindow->
						    getMinorContentVersion()
						);
					//TEXTPRSR_DIFF >>
					if(!IsFaceRecognizedByThisVersionOfFileFormat(
						faceIndx))
					{
					    faceIndx =DEFAULT_FONT_FACE_INDX;
					}
					//<< END TEXTPRSR_DIFF.
					m_pTextWindow->pushFontFaceStack(
						faceIndx);
					ulCurrentStacksPushed |= FONT_FACE;
				    }
				}//hack end.
			    } //end of "else if( ..."FACE" ...)".

			    
			    if(nextValEndIndex < restOfDataLen)
			    {
				if(0L == nextValEndIndex)
				{
				    break; //leave the do loop.
				}
				pRestOfData = &pRestOfData[nextValEndIndex+1];
				restOfDataLen -= (nextValEndIndex+1);
			    }
			    else
			    {
				restOfDataLen = 0L;
			    }

			} while(restOfDataLen > 0L);
			//The following code sets up the
			// font stack to keep track of ALL parameters
			// pushed in the above do-while loop so that a
			// subsequent </FONT> can pop all involoved stacks
			// simultaneously:
			if(0L != ulCurrentStacksPushed)
			{
			    m_pTextWindow->pushFontStack(
				    ulCurrentStacksPushed);
			}
		    } //end of "if(pTagContents[1]==...) <==i.e., if "FONT".
		}
		break;

	    case 'H':
		//Added the following to allow better
		// handling of HTML text imported as RealText:
		//XXXEH- for now, treat as 2 line breaks, but in future,
		// also draw a line (horizontal rule):
		if(!stringCompare(pTagContents, tagContentsLen,
			"HR", 2)) 
		{
		    //Added the surrounding if() so that
		    // this code doesn't get called if horizontal motion:
		    if(m_pTextWindow->getCrawlRate() == 0  ||
			    m_pTextWindow->getScrollRate() != 0)
		    {
			//If we got <pos ..>x<hr/>, where "x" contains no
			// plain text (other than spaces, tabs, newlines),
			// then don't do a line break at all, but rather
			// update the NewPktStartYAtTimeZero() value:
			if(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag)
			{
			    LONG32 lCurY = m_pTextWindow->
				    GetNewPktStartYAtTimeZero();
			    HX_ASSERT(INVALID_LONG32 != lCurY);
			    m_pTextWindow->
				    SetNewPktStartYAtTimeZero(
				    lCurY + (2*DEFAULT_LINE_BREAK_SIZE));
			}
			else
			{
			   m_pTextWindow->incrementNumBreakTagsEncountered();
			   m_pTextWindow->incrementNumBreakTagsEncountered();
			}
			bSomeCharsFoundSinceLastBreakTag=0L;
		    }
		}


	    case 'I':
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "I", 1)) //start of italics tag
		    {
			m_pTextWindow->pushIsItalicizedStack(TRUE);
		    }
		    break;

	    case 'L':
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "LOOP", 4))
		    {
			m_pTextWindow->loop(TRUE);
		    }
		    //Added the following to allow better
		    // handling of HTML text imported as RealText:
		    //XXXEH- for now, treat as line break tag, but in future,
		    // add indent based on depth of nested ULs and OLs:
		    else if(!stringCompare(pTagContents, tagContentsLen,
			    "LI", 2)) 
		    {
			//Added the surrounding if() so that
			// this code doesn't get called if horizontal motion:
			if(m_pTextWindow->getCrawlRate() == 0  ||
				m_pTextWindow->getScrollRate() != 0)
			{
			    //If we got <pos ..>x<center>, where "x" contains 
			    // no plain text (other than spaces, tabs,
			    // newlines), then don't do a line break at all:
			    if(bUserPosTagFoundSinceLastTextContainer  &&
				    !bSomeCharsFoundSinceLastPosTag)
			    {
				;
			    }
			    else
			    {
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
			    }
			    bSomeCharsFoundSinceLastBreakTag=0L;
			}
		    }
		    break;

	    case 'O':
		    //Added the following to allow better
		    // handling of HTML text imported as RealText:
		    //XXXEH- for now, treat as line break tag, but in future,
		    // add indent based on depth of nested ULs and OLs:
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "OL", 2)) 
		    {
			UINT16 curIndentAmtInPixels =
				m_pTextWindow->
				peekAtLineIndentAmtInPixelsStack();
			m_pTextWindow->pushLineIndentAmtInPixelsStack(
				curIndentAmtInPixels +
				ORDERED_LIST_INDENT_AMT);
			//Added the surrounding if() so that
			// this code doesn't get called if horizontal motion:
			if(m_pTextWindow->getCrawlRate() == 0  ||
				m_pTextWindow->getScrollRate() != 0)
			{

			    BOOL bFollowsPosTag =
				    (bUserPosTagFoundSinceLastTextContainer
				    &&  !bSomeCharsFoundSinceLastPosTag);
			    //Do a line break, but only if 0 line breaks so
			    // far, and not if there is no raw text yet,
			    // i.e., not if this <OL> tag starts the data
			    // part of the file, nor if a <CLEAR> tag was
			    // just sent:
			    if((!m_pTextWindow->getNumBreakTagsEncountered()
				    &&
				    !m_pTextWindow->m_bClearWasJustSent  &&
				    m_pTextWindow->size()>0L  &&
				    //TEXTPRSR_DIFF >>:
				    //Don't do newline if NEWLINES=0 was in pkt hdr
				    // and we haven't seen any text since the latest
				    // packet arrived, i.e., and we are seeing this
				    // <UL> tag inside the packet header:
				    (bStartOfPacketNewlinesIsZero  ||
					    !WeAreInsidePacketOpaqueHeader()))
				    //<< END TEXTPRSR_DIFF.
				    ||  bFollowsPosTag
				    )
			    {
				//If we got <pos ..>x<ol>, where "x" contains 
				// no plain text (other than spaces, tabs,
				// newlines), then don't do a line break at
				// all, but DO add ORDERED_LIST_INDENT_AMT
				// amount to the NewPktStartXAtTimeZero()
				// value:
				if(bFollowsPosTag  &&  !IsBeta1Player())
				{
				    LONG32 lCurX = m_pTextWindow->
					    GetNewPktStartXAtTimeZero();
				    HX_ASSERT(INVALID_LONG32 != lCurX);
				    m_pTextWindow->
					    SetNewPktStartXAtTimeZero(
					    lCurX +
					    (ORDERED_LIST_INDENT_AMT));
				}
				else
				{
				    m_pTextWindow->
					  incrementNumBreakTagsEncountered();
				}
				bSomeCharsFoundSinceLastBreakTag=0L;
			    }
			}
		    }
		    break;

			    
	    case 'P':
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "P", 1)) //start of paragraph tag
		    {
			//If we got <pos ..>x<p>, where "x" contains no
			// plain text (other than spaces, tabs, newlines),
			// then don't do a line break at all, but rather
			// update the NewPktStartYAtTimeZero() value:
			if(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag)
			{
			    LONG32 lCurY =  m_pTextWindow->
				    GetNewPktStartYAtTimeZero();
			    HX_ASSERT(INVALID_LONG32 != lCurY);
			    m_pTextWindow->
				    SetNewPktStartYAtTimeZero(
				    lCurY + (2*DEFAULT_LINE_BREAK_SIZE));
			}
			else
			{
			    m_pTextWindow->
				    incrementNumBreakTagsEncountered();
			    m_pTextWindow->
				    incrementNumBreakTagsEncountered();
			}
			bSomeCharsFoundSinceLastBreakTag=0L;
		    }
		    else if(!stringCompare(pTagContents, tagContentsLen,
			    "PRE", 3)) //start of PRE tag
		    {
			m_pTextWindow->pushIsPreStack();

			//Acts like a <P> tag as far as newlines go:
			//Do a line break, but only if 0 line breaks so far,
			// and not if there is no raw text yet, i.e., not if
			// this <PRE> tag starts the data part of the file,
			// nor if a <CLEAR> tag was just sent:
			if(!m_pTextWindow->getNumBreakTagsEncountered()  &&
				!m_pTextWindow->m_bClearWasJustSent  &&
				m_pTextWindow->size()>0L  &&
				//TEXTPRSR_DIFF >>:
				//Don't do newline if NEWLINES=0 was in pkt hdr
				// and we haven't seen any text since the latest
				// packet arrived, i.e., and we are seeing this
				// <PRE> tag inside the packet header:
				(bStartOfPacketNewlinesIsZero  ||
					!WeAreInsidePacketOpaqueHeader())
				//<< END TEXTPRSR_DIFF.
				)
			{
			    //If we got <pos ..>x<pre>, where "x" contains
			    // no plain text (other than spaces, tabs,
			    // newlines), then don't do a line break at all:
			    if(bUserPosTagFoundSinceLastTextContainer  &&
				    !bSomeCharsFoundSinceLastPosTag)
			    {
				;
			    }
			    else
			    {
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
			    }
			    bSomeCharsFoundSinceLastBreakTag=0L;
			}

			//Set cur font face to courier:
			//[XXXXXEH- Do we want to do this if we are inside
			// the packet header?  The packet header will have
			// the current FONT (courier) if so, so we don't
			// really need to do this, or the next </font> might
			// not work, but it seems to work(!?)...]
			m_pTextWindow->pushFontFaceStack(
				COURIERTT_FONT_FACE_INDX);
			m_pTextWindow->pushFontStack(FONT_FACE);
		    }
		    else if(tagContentsLen >= 7) //7==min length (e.g.,
						// "POS X=8" is 7 chars long)
		    {
			if('O' == pTagContents[1]) 
			{
			    if(pTagContents[2]=='S'  &&
				    pTagContents[3]==' ')
			    {   //we've found "POS " so far...
				_CHAR* pLval;
				_CHAR* pRval;
				_CHAR* pRestOfData = &pTagContents[4];
				ULONG32 restOfDataLen = tagContentsLen-4;
				ULONG32 nextTokenStartIndex,
					nextTokenEndIndex;
				ULONG32 nextValStartIndex, nextValEndIndex;
				do
				{
				    if(GetNextTokenLvalueRvaluePair(
					    pRestOfData,
					    restOfDataLen,
					    nextTokenStartIndex,
					    nextTokenEndIndex,
					    nextValStartIndex,
					    nextValEndIndex) )
				    {
					pLval = &pRestOfData[
						nextTokenStartIndex];
					pRestOfData[nextTokenEndIndex]='\0';
					pRval = &pRestOfData[
						nextValStartIndex];
					pRestOfData[nextValEndIndex]='\0';
				    }
				    else
				    {
					break;
				    }

				    _CHAR* tmpPtr = pRval;
				    ULONG32 tokenValLen =
					    nextValEndIndex-
					    nextValStartIndex;

				    BOOL bErr = FALSE;

				    BOOL bIsX0orY0 = TRUE;

				    if((bIsX0orY0 = !stringCompare(pLval, 
					    nextTokenEndIndex -
					    nextTokenStartIndex,
					    "X0", 2))  ||
					    //Only allow user to set POS X=n
					    // if window has no motion to it:
					    (!m_pTextWindow->getScrollRate()
					    &&
					    !m_pTextWindow->getCrawlRate()
					    &&
					    !stringCompare(pLval,
					    nextTokenEndIndex -
					    nextTokenStartIndex,
					    "X", 1) ) )
				    {
					//In case
					//     <POS X0="15"/> <POS X="23">foo
					// arrives at the start of a packet,
					// make sure to clear the pkt start Y
					// so the X="23" will be used instead
					// of 15.  The problem was that the
					// space char separating the 2 didn't
					// become a TextContainer in the list
					// and thus, when "foo" was inserted,
					// the ...XAtTimeZero was still valid
					// and was used as a result and the
					// 23 was ignored:
					if(!bIsX0orY0)
					{
					    m_pTextWindow->
						    m_bUseXPOSVal = TRUE;
					}
					
					ULONG32 ulXAtTimeZero = 0L;
					//tmpPtr is always NULL-terminated
					// at this pt:
					ulXAtTimeZero =
					    m_pTextWindow->
					    string_to_ULONG32(tmpPtr, bErr);
					if(!bErr)
					{
					    m_pTextWindow->
						   SetNewPktStartXAtTimeZero(
						   (LONG32)ulXAtTimeZero);
					    //Fixes bug #6906; if
					    // "<BR/><POS .../>foo" is seen,
					    // the <BR/> will be ignored, but
					    // if <POS ../><BR/>foo" is seen,
					    // the <BR/> will be honored:
					    m_pTextWindow->
					      clearNumBreakTagsEncountered();
				    bUserPosTagFoundSinceLastTextContainer
						    = !bIsX0orY0;
					    bSomeCharsFoundSinceLastPosTag =
						    FALSE;
					}
					//else ignore the tag; is invalid X
				    } //end of "if( ..."X0"...)".

				    else if((bIsX0orY0 =!stringCompare(pLval,
					    nextTokenEndIndex -
					    nextTokenStartIndex,
					    "Y0", 2))  ||
					    //Only allow user to set POS X=n
					    // if window has no motion to it:
					    (!m_pTextWindow->getScrollRate()
					    &&
					    !m_pTextWindow->getCrawlRate()
					    &&
					    !stringCompare(pLval,
					    nextTokenEndIndex -
					    nextTokenStartIndex,
					    "Y", 1) ) )
				    {
					//In case
					//     <POS Y0="20"/> <POS Y="10">foo
					// arrives at the start of a packet,
					// make sure to clear the pkt start Y
					// so the Y="10" will be used instead
					// of 20.  The problem was that the
					// space char separating the 2 didn't
					// become a TextContainer in the list
					// and thus, when "foo" was inserted,
					// the ...YAtTimeZero was still valid
					// and was used as a result and the
					// 10 was ignored:
					if(!bIsX0orY0)
					{
					    m_pTextWindow->
						    m_bUseYPOSVal = TRUE;
					}
					
					ULONG32 ulYAtTimeZero = 0L;
					//tmpPtr is always NULL-terminated
					// at this pt:
					ulYAtTimeZero =
					    m_pTextWindow->
					    string_to_ULONG32(tmpPtr, bErr);
					if(!bErr)
					{
					    m_pTextWindow->
						   SetNewPktStartYAtTimeZero(
						   (LONG32)ulYAtTimeZero);
					    //Fixes bug #6906; if
					    // "<BR/><POS .../>foo" is seen,
					    // the <BR/> will be ignored, but
					    // if <POS ../><BR/>foo" is seen,
					    // the <BR/> will be honored:
					    m_pTextWindow->
					      clearNumBreakTagsEncountered();
				    bUserPosTagFoundSinceLastTextContainer
						    = !bIsX0orY0;
					    bSomeCharsFoundSinceLastPosTag =
						    FALSE;
					}
					//else ignore the tag; is invalid Y.
				    } //end of "else if( ..."Y0" ...)".
				    //TEXTPRSR_DIFF >> (Should only be
				    // performed on renderer end since this
				    // is an internal tag that is inserted
				    // into the packet header):
				    else if(!stringCompare(pLval, 
					    nextTokenEndIndex -
					    nextTokenStartIndex,
					    "NEWLINES", 8))
				    {
					ULONG32 ulNumNewlines =
					    m_pTextWindow->
					    string_to_ULONG32(tmpPtr, bErr);
					if(!bErr)
					{
					    //This is needed in case some
					    // tag that causes a newline to
					    // occur is present before this
					    // NEWLINES is seen, e.g., the
					    // packet header is:
					    // <PRE><POS X=2 Y=0 NEWLINES=2>
					    // should only do 2 newlines, not
					    // 2 (for PRE) + 2 (for NEWLINES)
					    m_pTextWindow->
					      clearNumBreakTagsEncountered();
					    m_pTextWindow->
						SetNumNewlinesStatedInPacketHeader(
						ulNumNewlines);
					    while(ulNumNewlines--)
					    {
						m_pTextWindow->
					  incrementNumBreakTagsEncountered();
					    }
					    //Since "<POS NEWLINES..>" should
					    // always be at the start of a
					    // packet, the following should
					    // never be TRUE:
					    HX_ASSERT(!bSomeCharsFoundSinceLastBreakTag);
					    bSomeCharsFoundSinceLastBreakTag=
						    0L;
					    bStartOfPacketNewlinesIsZero = FALSE;
					}
					else
					{
					    bStartOfPacketNewlinesIsZero = TRUE;
					    m_pTextWindow->
						SetNumNewlinesStatedInPacketHeader(0L);
					}
				    }
				    //<< END TEXTPRSR_DIFF.

				    if(nextValEndIndex < restOfDataLen)
				    {
					if(0L == nextValEndIndex)
					{
					    break; //leave the do loop.
					}
					pRestOfData = &pRestOfData[
						nextValEndIndex+1];
					restOfDataLen -= (nextValEndIndex+1);
				    }
				    else
				    {
					restOfDataLen = 0L;
				    }

				} while(restOfDataLen > 0L);
			    } //end "POS " tag parsing.
			}
		    }
		    break;

	    case 'R':
		    //added the following case so
		    // that a forced deletion of all prior-streamed
		    // data could be made (noting that <CLEAR> should
		    // do this but doesn't until the FF is changed
		    // to handle packet sending, and thus seeking,
		    // at the expected time)  This is to remain
		    // undocumented:
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "RESET", 5))
		    {
			//go through list and delete everything:
			m_pTextWindow->deleteAllNoLongerVisible();
		    }
		    break;


	    case 'S':
		    if(!stringCompare(pTagContents, tagContentsLen,
			    "S", 1)) //start of strike-out tag
		    {
			m_pTextWindow->pushIsStruckOutStack(TRUE);
		    }
		    break;

	    case 'T':
		if(tagContentsLen >= 2)
		{
		    BOOL isTUtag, isTLtag;
		    isTUtag = isTLtag = FALSE;
		    if('U' == pTagContents[1])
		    {	
			//changed this so, for example, 
			//  <TUblah> won't work but <TU blah> will:
			if(tagContentsLen > 2)
			{
			    if(' ' ==  pTagContents[2]  ||
				    '\t'  == pTagContents[2]  ||
				    '\n'  == pTagContents[2]  ||
				    '\r'  == pTagContents[2])
			    {
				//is <TU>, or "TextUpper" tag for tickertape:
				isTUtag = TRUE;
				m_pTextWindow->isTickerUpperText(isTUtag);
			    }
			}
			else  //tagContentsLen == 2, so is <TU>:
			{
			    //is <TU>, or "TextUpper" tag for tickertape:
			    isTUtag = TRUE;
			    m_pTextWindow->isTickerUpperText(isTUtag); 
			}
		    }	
		    else if('L' == pTagContents[1]) 
		    {	
			//changed this so, for example, 
			//  <TLblah> won't work but <TL blah> will:
			if(tagContentsLen > 2)
			{
			    if(' ' ==  pTagContents[2]  ||
				    '\t'  == pTagContents[2]  ||
				    '\n'  == pTagContents[2]  ||
				    '\r'  == pTagContents[2])
			    {
				//is <TL>, or "TextLower" tag for tickertape:
				isTLtag = TRUE;
				m_pTextWindow->isTickerUpperText(!isTLtag); 
			    }
			}
			else  //tagContentsLen == 2, so is <TL>:
			{
			    //is <TL>, or "TextLower" tag for tickertape:
			    isTLtag = TRUE;
			    m_pTextWindow->isTickerUpperText(!isTLtag); 
			}
		    }
		    else if('I' == pTagContents[1]  &&  tagContentsLen >= 10) 
		    {	//is possibly a <TIME ..> tag; min length for TIME
			//  is 10: e.g., <TIME end=1> (10 chars)
			if(pTagContents[2]=='M'  &&  pTagContents[3]=='E'  &&
				pTagContents[4]==' ')
			{   //we've found "TIME " so far...
			    _CHAR* pLval;
			    _CHAR* pRval;
			    _CHAR* pRestOfData = &pTagContents[5];
			    ULONG32 restOfDataLen = tagContentsLen-5;
			    ULONG32 nextTokenStartIndex, nextTokenEndIndex;
			    ULONG32 nextValStartIndex, nextValEndIndex;
			    do
			    {
				if(GetNextTokenLvalueRvaluePair(pRestOfData,
					restOfDataLen,
					nextTokenStartIndex,
					nextTokenEndIndex,
					nextValStartIndex, nextValEndIndex) )
				{
				    pLval =&pRestOfData[nextTokenStartIndex];
				    pRestOfData[nextTokenEndIndex]='\0';
				    pRval = &pRestOfData[nextValStartIndex];
				    pRestOfData[nextValEndIndex]='\0';
				}
				else
				{
				    break;
				}

				_CHAR* tmpPtr = pRval;
				ULONG32 tokenValLen =
					nextValEndIndex-nextValStartIndex;

				if(!stringCompare(pLval, 
					nextTokenEndIndex-nextTokenStartIndex, 
					"BEGIN", 5)
					//be consistent w/RA:
					|| !stringCompare(pLval, 
					nextTokenEndIndex-nextTokenStartIndex, 
					"START", 5)
					)
				{
				    ULONG32 timeValInMillisec = 0L;
				    if(convertTimeStringToULONG32(
					    tmpPtr, tokenValLen, 
					    timeValInMillisec))
				    {
					m_pTextWindow->SetLatestSentTimeToRender(
						timeValInMillisec);
				    }
				    //else ignore the tag; is invalid BEGIN time.
				} //end of "if( ..."BEGIN" ...||..."START"...)".

				if(!stringCompare(pLval, 
					nextTokenEndIndex-nextTokenStartIndex, 
					"END", 3))
				{
				    ULONG32 timeValInMillisec = 0L;
				    if(convertTimeStringToULONG32(
					    tmpPtr, tokenValLen, 
					    timeValInMillisec))
				    {
					//TEXTPRSR_DIFF>>
					//XXXXXEH- ignore end if it's earlier
//#error:				// than cur time and we're live:
					//<< END TEXTPRSR_DIFF.
					if(bIsLiveSource  &&
						TIME_INVALID ==
						timeValInMillisec)
					{
					    timeValInMillisec =
						    TIME_INFINITY;
					}
					m_pTextWindow->
						SetLatestSentTimeToStopRendering(
						timeValInMillisec);
				    }
				    //else ignore the tag; is invalid END time.
				} //end of "else if( ..."END" ...)".

				//TEXTPRSR_DIFF >> (but is for renderer
				// only; this "LC=" gets set in TextLine's
				// OutputPacketHeaderString() and is the
				// time of the last <CLEAR> so, if we lost
				// a packet, we can clear prior TCs):
				else if(!stringCompare(pLval, 
				       nextTokenEndIndex-nextTokenStartIndex, 
					"LC", 2))
				{
				    ULONG32 timeValInMillisec = 0L;
				    if(convertTimeStringToULONG32(
					    tmpPtr, tokenValLen, 
					    timeValInMillisec))
				    {
					if(bIsLiveSource)
					{
					    //If we haven't seen a packet
					    // yet, then the initialized val
					    // of 0 for TimeOfLastClearTag is
					    // wrong (in a wallclock, non-0-
					    // based timeline) and should be
					    // reset to cur LC time:
					    if(0L == m_pTextWindow->
						getTimeOfLastClearTag()
						&&  GetCurrentPacketNum()<2L)
					    {
						m_pTextWindow->
						    setTimeOfLastClearTag(
						    timeValInMillisec);
					    }
					}
					if(IsTimeAMoreRecentThanTimeB(
						    timeValInMillisec,
						    m_pTextWindow->
						     getTimeOfLastClearTag(),
						    bIsLiveSource) )
					{
					    //Then treat this as if a <CLEAR>
					    // was sent:
					    m_pTextWindow->
						m_bClearWasJustSent = TRUE;
					    //Moved this code here so,
					    // if "<BR/><CLEAR>foo" is seen,
					    // the <BR/> will be ignored, but
					    // if <CLEAR><BR/>foo" is seen,
					    // the <BR/> will be honored:
					    m_pTextWindow->
					      clearNumBreakTagsEncountered();
					}
				    }
				    //else ignore tag; is invalid END time.
				} //end of "else if( ..."LC" ...)".
				// << END TEXTPRSR_DIFF.

				if(nextValEndIndex < restOfDataLen)
				{
				    if(0L == nextValEndIndex)
				    {
					break; //leave the do loop.
				    }
				    pRestOfData = &pRestOfData[nextValEndIndex+1];
				    restOfDataLen -= (nextValEndIndex+1);
				}
				else
				{
				    restOfDataLen = 0L;
				}

			    } while(restOfDataLen > 0L);
			} //end "TIME " tag parsing.
		    }
		    //Added the following to allow better
		    // handling of HTML text imported as RealText:
		    //XXXEH- for now, just treat as line break tag, and
		    //just look at first 2-3 chars (ignoring optional <TR>-tag
		    //parameters like "align=") so "TR" or "TR " are valid:
		    else if(!stringCompare(pTagContents, tagContentsLen,
			    "TR", 2)  ||
			    (tagContentsLen>=3  &&  
			    !stringCompare(pTagContents, 3, "TR ", 3)) )
		    {
			//Added the surrounding if() so that
			// this code doesn't get called if horizontal motion:
			if(m_pTextWindow->getCrawlRate() == 0  ||
				m_pTextWindow->getScrollRate() != 0)
			{
			    //If we got <pos ..>x<tr>, where "x" contains
			    // no plain text (other than spaces, tabs,
			    // newlines), then don't do a line break at all:
			    if(bUserPosTagFoundSinceLastTextContainer  &&
				    !bSomeCharsFoundSinceLastPosTag)
			    {
				;
			    }
			    else
			    {
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
				m_pTextWindow->
					incrementNumBreakTagsEncountered();
			    }
			    bSomeCharsFoundSinceLastBreakTag=0L;
			}
		    }

		    //Now, see if there are any additional attributes for
		    // ticker's upper/lower text:
		    if(tagContentsLen > 2  &&  (isTUtag  ||  isTLtag))
		    {
			ULONG32 nameTokenStartIndex, nameTokenEndIndex,
				indexOfEqualsSign;
			ULONG32 valueTokenStartIndex, valueTokenEndIndex,
				dummy;
			_CHAR tmpChar1, tmpChar2;
			ULONG32 nameTokenLen, valTokenLen;
			nameTokenStartIndex =
				skipSpacesTabsAndNewlineChars(
				pTagContents, tagContentsLen,
				2); //start at [2] and skip spaces.
			if(nameTokenStartIndex == tagContentsLen)
			{   //only spaces were found so rest of tag is empty:
			    break; 
			}
			nameTokenEndIndex = findNextSpaceTabOrNewLineChar(
				pTagContents, tagContentsLen, 
				nameTokenStartIndex, indexOfEqualsSign,
				//In-tag text is always us-ascii:
				CHARSET__us_ascii);
			if(indexOfEqualsSign < tagContentsLen-1) 
			{   //"(name)=(one or more chars)" was found:
			    if(nameTokenEndIndex == indexOfEqualsSign+1)
			    {	/*  "(name)=(1 or more space chars)(val)" was
				 *  found:  */
				valueTokenStartIndex = 
					skipSpacesTabsAndNewlineChars(
					pTagContents, tagContentsLen, 
					nameTokenEndIndex);
				if(valueTokenStartIndex == tagContentsLen)
				{
				    break;
				}
			    }
			    else //"(name)=(val)" was found:
			    {
				nameTokenEndIndex = indexOfEqualsSign;
				valueTokenStartIndex = indexOfEqualsSign+1;
			    }
			    valueTokenEndIndex = 
				    findNextSpaceTabOrNewLineChar(
				    pTagContents, tagContentsLen, 
				    valueTokenStartIndex, dummy,
				    //In-tag text is always us-ascii:
				    CHARSET__us_ascii);
			    /*  Put a '\0' at the character just past where
			     *  the (name) part is:  */
			    tmpChar1 = pTagContents[nameTokenEndIndex];
			    pTagContents[nameTokenEndIndex] = '\0';
			    nameTokenLen = 
				    nameTokenEndIndex - nameTokenStartIndex;
			    if(!stringCompare(
				    &pTagContents[nameTokenStartIndex],
				    nameTokenLen,
				    "COLOR", 5))
			    {
				COLORTYPE colortype_retVal;

				tmpChar2 = pTagContents[valueTokenEndIndex];
				pTagContents[valueTokenEndIndex] = '\0';
				
				//Added this so "#RRGGBB"
				// works as well as "colorname" does:
				BOOL isAValidColor = FALSE;
				char* pTemp_Ptr =
					&pTagContents[valueTokenStartIndex];
				valTokenLen = valueTokenEndIndex - 
					valueTokenStartIndex;
				if(valTokenLen>=1  &&  '#'==pTemp_Ptr[0]  ||
					(valTokenLen>=2  &&
					'\"'==pTemp_Ptr[0]  && 
					'#'==pTemp_Ptr[1])  )
				{
				    //Color was presented as "#RRGGBB" in hex:
				    isAValidColor = 
					    convertColorValStringToCOLORTYPE(
					    pTemp_Ptr, valTokenLen, 
					    colortype_retVal);
				}
				//  Returns FALSE if colorName contains an
				//  unrecognized color:
				else
				{	//Color was named, e.g., "darkblue":
				    isAValidColor = convertColorNameToCOLORTYPE(
					    pTemp_Ptr, valTokenLen, 
					    colortype_retVal);
				}
				if(isAValidColor)
				{
				    //  Is a legal color name, so push it on
				    //  the appropriate stack:
				    if(isTUtag)
				    {
					m_pTextWindow->
						pushTickerUpperColorStack(
						colortype_retVal);
				    }
				    else
				    {
					m_pTextWindow->
						pushTickerLowerColorStack(
						colortype_retVal);
				    }
				}
				//else ignore the tag; is invalid color.

				//Restore the orig char:
				pTagContents[valueTokenEndIndex] = tmpChar2; 
			    }
			    //Restore it from '\0':
			    pTagContents[nameTokenEndIndex] = tmpChar1; 
			}
			else
			{
#if defined(SHOW_CODE_CHANGE_MESSAGES)
#pragma message("EH- in "__FILE__", \
\"(name)(SPACES)=(0 or more SPACES)(value)\"-parsing is not \
yet handled.")
#endif
			    ;
			}
		    }
		}
		break;

	    case 'U':
		if(!stringCompare(pTagContents, tagContentsLen,
			"U", 1)) //start of underlined tag
		{
		    m_pTextWindow->pushIsUnderlinedStack(TRUE);
		}
		//Added the following to allow better
		// handling of HTML text imported as RealText:
		//XXXEH- for now, treat as line break tag, but in future,
		// add indent based on depth of nested ULs and OLs:
		else if(!stringCompare(pTagContents, tagContentsLen,
			"UL", 2)) 
		{
		    //added the following:
		    UINT16 curIndentAmtInPixels =
			    m_pTextWindow->peekAtLineIndentAmtInPixelsStack();
		    m_pTextWindow->pushLineIndentAmtInPixelsStack(
			    curIndentAmtInPixels +
			    UNORDERED_LIST_INDENT_AMT);
		    //Added the surrounding if() so that
		    // this code doesn't get called if horizontal motion:
		    if(m_pTextWindow->getCrawlRate() == 0  ||
			    m_pTextWindow->getScrollRate() != 0)
		    {
			BOOL bFollowsPosTag =
				(bUserPosTagFoundSinceLastTextContainer  &&
				!bSomeCharsFoundSinceLastPosTag);
			//Do a line break, but only if 0 line breaks so far,
			// and not if there is no raw text yet, i.e., not if
			// this <UL> tag starts the data part of the file,
			// nor if a <CLEAR> tag was just sent:
			if((!m_pTextWindow->getNumBreakTagsEncountered()  &&
				!m_pTextWindow->m_bClearWasJustSent  &&
				m_pTextWindow->size()>0L  &&
				//TEXTPRSR_DIFF >>:
				//Don't do newline if NEWLINES=0 was in pkt hdr
				// and we haven't seen any text since the latest
				// packet arrived, i.e., and we are seeing this
				// <UL> tag inside the packet header:
				(bStartOfPacketNewlinesIsZero  ||
					!WeAreInsidePacketOpaqueHeader()))
				//<< END TEXTPRSR_DIFF.
				||  bFollowsPosTag
				)
			{
			    //If we got <pos ..>x<ul>, where "x" contains 
			    // no plain text (other than spaces, tabs,
			    // newlines), then don't do a line break at
			    // all, but DO add UNORDERED_LIST_INDENT_AMT
			    // amount to the NewPktStartXAtTimeZero()
			    // value:
			    if(bFollowsPosTag  &&  !IsBeta1Player())
			    {
				LONG32 lCurX = m_pTextWindow->
					GetNewPktStartXAtTimeZero();
				HX_ASSERT(INVALID_LONG32 != lCurX);
				m_pTextWindow->
					SetNewPktStartXAtTimeZero(
					lCurX +
					(UNORDERED_LIST_INDENT_AMT));
			    }
			    else
			    {
				m_pTextWindow->
				      incrementNumBreakTagsEncountered();
			    }
			    bSomeCharsFoundSinceLastBreakTag=0L;
			}
		    }
		}
		break;

	    //TEXTPRSR_DIFF >>:
	    case 'W':
		//Added this case; this is a system-level
		// tag that should only be sent by the FileFormat when it
		// thinks the text that follows this tag starts a new line
		// due to word wrapping (which is no longer calculated at the
		// renderer end):
		if(!stringCompare(pTagContents, tagContentsLen,
			"WR", 2)) //line break due to wordwrap tag
		{
		    //If we got <pos ..>x<wr>, where "x" contains
		    // no plain text (other than spaces, tabs,
		    // newlines), then there has been an error at the
		    // file format end since it should never put a <wr/>
		    // after any tag without some plain text inbetween:
		    HX_ASSERT(!(bUserPosTagFoundSinceLastTextContainer  &&
			    !bSomeCharsFoundSinceLastPosTag));
		    m_pTextWindow->incrementNumBreakTagsEncountered();
		    bIsWordWrapNewLine = TRUE;
		    bSomeCharsFoundSinceLastBreakTag=0L;
		}
		break;
	    // << END TEXTPRSR_DIFF.


	    default:
		break;
	}

	pData_CHAR[indexOfRightBracket] = '>'; //restore the char.
	
	startIndex = indexOfRightBracket+1;  //where next tag-search starts.

    } while (startIndex < len);

    //TEXTPRSR_DIFF >>: (I don't think textprsr wants this, however):
    //If there is a residual URL push left around from the very end
    // of the packet, clean up (because the start of the next packet
    // will have the url again in its header):
    if(m_pTextWindow  &&  m_pTextWindow->hasValidURL())
    {
	m_pTextWindow->clearURL();
	//The following causes us to go back to the pre-hyperlink text's
	// font color:
	m_pTextWindow->popTextColorStack();
	if(m_pTextWindow->usingUnderlineHyperlinks())
	{
	    m_pTextWindow->popIsUnderlinedStack();
	}
	//The following handles backing out after hyperlink text is done:
	ULONG32 numColorPushes = 
		m_pTextWindow->
		getNumberOfFontColorPushesInsideLinkText();
	for(; numColorPushes >0; numColorPushes--)
	{
	    m_pTextWindow->popTextColorStack();
	}
	m_pTextWindow->
		setNumberOfFontColorPushesInsideLinkText(0L);
    }
    //<< END TEXTPRSR_DIFF.

    //TEXTPRSR_DIFF >>:
    //In case the packet ended with a <CLEAR> (followed by either
    // nothing or only by markup tags) and we have to wait
    // a while for the next packet (this could happen in a live
    // stream), we want to make sure the existing TextContainers
    // get their endtimes updated to the time of this CLEAR:
    if(m_pTextWindow  &&  m_pTextWindow->m_bClearWasJustSent)
    {
	//Is just after a <CLEAR> tag, so clear everything in
	// TextContainerList whose m_endTime is greater
	// than the latestSentTimeToRender:
	m_pTextWindow->MarkAllForClear(  //(the T.C.List version)
		bIsLiveSource);

	m_pTextWindow->setTimeOfLastClearTag();

	//NOTE: m_bClearWasJustSent is still TRUE and the code, above, that
	// reacts to it being TRUE will get executed the next time a
	// TextContainer is created (from a future packet).
    }
    //<< END TEXTPRSR_DIFF.

    //TEXTPRSR_DIFF >>: (textprsr should NOT do this, however):
    //In case this packet ended with </CENTER> or any other newline char, we
    // need to center the last line of text now because we don't know how
    // long it might be before more text arrives (at which point the
    // centering of prior line would normally occur):
    if(m_pTextWindow->getNumBreakTagsEncountered())
    {
	//Note: this only centers text that has the isCentered() property:
	m_pTextWindow->CenterPriorLine();
	m_pTextWindow->setPriorLineAlreadyCentered(TRUE);

    }
    else
    {
	m_pTextWindow->setPriorLineAlreadyCentered(FALSE);
    }
    //<< END TEXTPRSR_DIFF.
  } // /End of [else is not plain text].

cleanup:
    if (bDataCHARwasAllocd  &&  pData_CHAR)
    {
	delete [] pData_CHAR;
    }

}



/////////////////////////////////////////////////////////////////////////////
// Method:
//  BOOL
//  RealTextRenderer::OnTimeSynch(ULONG32 time)
//
// Purpose:
//  For each TextContainer of m_pTextWindow::TextContainerList, move its text
//  location by an amount determined by the crawlrate and scrollrate and 
//  the difference between the current time and the last time the text was
//  rendered.  If any TextContainer's text has moved, invalidate the 
//  area that needs updating.
//  When finished re-rendering all text that has moved, update the window so
//  that the changes will appear.
//
// Note: "time" is the number of milliseconds since the stream started.
//
BOOL RealTextRenderer::OnTimeSynch(ULONG32 ulCurTime)
{
    //This helps us determine whether or not we can do a ScrollRect()
    // if there is a scrollrate or crawlrate to the window:
    m_bSomeonesBeginOrEndTimeWasCrossedOver = FALSE;
    
    //For special scroll-window operation, we need to know if a loop
    // just happened so we can force a refresh of the entire window
    // rather than scrolling the emptyness to the right of the visible
    // part of the window:
    m_bHorizontalLoopJustOccurred = FALSE;

    BOOL windowNeedsUpdating = FALSE;

    BOOL bIsLiveSource = m_pTextWindow->isLiveSource();

    BOOL bDoLoopNow = FALSE;
    if(m_pTextWindow->isLooping())
    {
	bDoLoopNow = TRUE;
    }

    //Calculate the scroll &/or crawl amount needed from the TextWindow's
    // scrollRate, crawlRate, and m_lastKnownTime of each of the 
    // TextContainers, (which tell the last time that the data in the
    // respective TextContainer's buffer was rendered in this function).
    LONG32 scrollrate = m_pTextWindow->getScrollRate();
    LONG32 crawlrate = m_pTextWindow->getCrawlRate();
    ULONG32 listSize = m_pTextWindow->textCntnrListSize();


    //If this is the first time we've been here, make sure the background
    // gets painted even if there is no text to be shown yet:
    // WE CAN NO LONGER ASSUME THAT THE FIRST TIME SYNCH WILL BE AT 0!!!
    if(m_bIsFirstTimeSynch)
    {
        windowNeedsUpdating = TRUE;
	m_bIsFirstTimeSynch = FALSE;
    }

    //Renamed the following vars to be consistent
    // with time-zero-based times instead of begin-time times:
//#error  handle 0-based time for live:
    double timeSinceTimeZeroIn_sec = 0.0;
    timeSinceTimeZeroIn_sec =
	    (double(ulCurTime) / 1000.0);

    if(listSize)
    {
	LISTPOSITION pos = m_pTextWindow->GetStartPosition();

	while(pos)
	{
	    TextContainer* curTextContainerPtr =
		    (TextContainer*)m_pTextWindow->GetAt(pos);

	    HX_ASSERT_VALID_PTR(curTextContainerPtr);
	    if(!curTextContainerPtr)
	    {
		m_pTextWindow->GetNext(pos);
		continue;
	    }

#if defined(SHOW_CODE_CHANGE_MESSAGES)
#pragma message("EH- in "__FILE__", \
		    Delete curT.C.Ptr from list if getEndTime() < ulCurTime")
#endif

	    BOOL bBeginTimeIsInFuture =
		    IsTimeAMoreRecentThanTimeB
		    (curTextContainerPtr->getBeginTime(),
		    ulCurTime,
		    bIsLiveSource);
	    BOOL bEndTimeIsInThePast =
		    IsTimeAMoreRecentThanTimeB
		    (ulCurTime,
		    curTextContainerPtr->getEndTime(),
		    bIsLiveSource);

	    if(bBeginTimeIsInFuture  ||  bEndTimeIsInThePast)
	    {
		//Added this to fix bug where text persists after
		// it has reached its end time; this forces a draw of the background
		// (only once) after the endtime of the T.C. has been passed:
		if(curTextContainerPtr->getBeginTime() != TIME_INVALID  &&
			bEndTimeIsInThePast)
		{
		    if(!curTextContainerPtr->getFinalDrawWasDone())
		    {
			//We have just crossed over this text's end time so
			// we have drawn it for the last time (and thus the
			// window can't be auto-scrolled if it has motion,
			// but must be manually updated so that this text
			// gets removed), but only if this text didn't "die"
			// by moving out of the window (i.e., only if it
			// was explicitly told to die by the author via a
			// <time end=t/> or <clear/> tag):
			if( (!crawlrate  &&  !scrollrate)
			    ||
			    (
				( (crawlrate  &&
				curTextContainerPtr->getXLowerRightCorner()
					>= 1)  &&
					curTextContainerPtr->
					getYLowerRightCorner() >= 1
				)
			    )
			    ||
			    (
				( (scrollrate  &&
				curTextContainerPtr->getYLowerRightCorner()
					>= 1)  &&
					curTextContainerPtr->
					getXLowerRightCorner() >= 1
				)
			    ) )
			{
			    m_bSomeonesBeginOrEndTimeWasCrossedOver = TRUE;
			}
			curTextContainerPtr->textHasChangedSinceLastDraw(TRUE);
			curTextContainerPtr->setFinalDrawWasDone(TRUE);
			windowNeedsUpdating = TRUE;
		    }
		    else
		    {
			curTextContainerPtr->textShouldBeDrawn(FALSE);
		    }
		}

		m_pTextWindow->GetNext(pos);
		continue;
	    }
	    
	    //Text is still active, so draw it in next OnPaint():
	    curTextContainerPtr->textShouldBeDrawn(TRUE);

	    ULONG32 lastKnownTime = curTextContainerPtr->getLastKnownTime();
	    LONG32 beginTime = curTextContainerPtr->getBeginTime();

	    if(TIME_INVALID==lastKnownTime)
	    {
		//We have just crossed over this text's begin time so we
		// will be drawing it for the first time (and thus the window
		// can't be auto-scrolled if it has motion, but must be
		// manually updated so that this text gets added):
		m_bSomeonesBeginOrEndTimeWasCrossedOver = TRUE;
	    }

	    BOOL textHasMoved = FALSE;
	    BOOL isTickerUpperText =curTextContainerPtr->isTickerUpperText();

	    LONG32 totalScrollAmtInPixels =
		    LONG32(timeSinceTimeZeroIn_sec * double(scrollrate));
	    LONG32 totalCrawlAmtInPixels = 
		    LONG32(timeSinceTimeZeroIn_sec * double(crawlrate));

	    LONG32 prevUpperLeftY =
		    curTextContainerPtr->getYUpperLeftCorner();
	    LONG32 timeZeroUpperLeftY =
		    curTextContainerPtr->getYAtTimeZeroUpperLeftCorner();
	    curTextContainerPtr->setLastKnownY(prevUpperLeftY);
	    curTextContainerPtr->setYUpperLeftCorner(
		    timeZeroUpperLeftY - totalScrollAmtInPixels);
	    LONG32 prevUpperLeftX =
		    curTextContainerPtr->getXUpperLeftCorner();
	    LONG32 timeZeroUpperLeftX =
		    curTextContainerPtr->getXAtTimeZeroUpperLeftCorner();
	    curTextContainerPtr->setLastKnownX(prevUpperLeftX);
	    curTextContainerPtr->setXUpperLeftCorner(
		    timeZeroUpperLeftX - totalCrawlAmtInPixels);

	    if(!bIsLiveSource)
	    {
		HX_ASSERT(curTextContainerPtr->getBeginTime() <= ulCurTime &&
			curTextContainerPtr->getEndTime() >= ulCurTime);
	    }

	    if(m_pTextWindow->isLooping())	    
	    {
		//See if this is the first T.C. following a <CLEAR> tag and,
		// if so, reset the CurrentXOffsetDueToLooping to 0L:
		if(!curTextContainerPtr->getAmountLoopedSoFar()  &&
			curTextContainerPtr->getStartTime() ==
				curTextContainerPtr->getTimeOfLastClearTag()  &&
			curTextContainerPtr->getLastKnownTime() == TIME_INVALID)
		{
		    m_pTextWindow->setCurrentXOffsetDueToLooping(0L);
		}				    

		if((timeZeroUpperLeftX-totalCrawlAmtInPixels +
			curTextContainerPtr->getXExtent()
			+ m_pTextWindow->getCurrentXOffsetDueToLooping()
			>= 0L)  &&  crawlrate
			)
		{
		    bDoLoopNow = FALSE; //It's still "visible".
		}
		if((timeZeroUpperLeftY-totalScrollAmtInPixels +
			curTextContainerPtr->getYExtent()
			+ m_pTextWindow->getCurrentYOffsetDueToLooping()
			>= 0L)  &&  scrollrate
			)
		{
		    bDoLoopNow = FALSE;  //It's still "visible".
		}
	    }

	    if(TYPE_TELEPROMPTER == m_pTextWindow->getType())
	    {
		//First, check if this T.C. is the first one after a
		// CLEAR tag whose time was just exceeded by ulCurTime;
		// if so, reset currentYOffset... to 0 since we're
		// drawing the post-CLEAR text in a as-yet-unscrolled
		// window:
		if(TIME_INVALID==lastKnownTime)
		{
		    if(IsTimeASameOrMoreRecentThanTimeB(
			    ulCurTime,
			    curTextContainerPtr->getTimeOfLastClearTag(),
			    bIsLiveSource) )
		    {
			m_pTextWindow->
				setCurrentYOffsetForTeleprompter(0L);
		    }
		}

		LONG32 currentYOffsetForTeleprompter = 
			m_pTextWindow->getCurrentYOffsetForTeleprompter();
		LONG32 lowerRightY = timeZeroUpperLeftY
			+ curTextContainerPtr->getYExtent() - 1;

		if(lowerRightY + currentYOffsetForTeleprompter < 0L)
		{
///#error XXXEH- Code unfinished:
		    //kill this pTC.
		    curTextContainerPtr->setEndTime(ulCurTime);
		    m_bSomeonesBeginOrEndTimeWasCrossedOver = TRUE;
		}
		else
		{   //If this pTC is partly or fully past the bottom edge of
		    // the window, then we need to adjust the YOffset so that
		    // this guy will show up just inside the bottom edge:
		    if(lowerRightY + currentYOffsetForTeleprompter
			    > m_pTextWindow->getHeight() )
		    {
			m_pTextWindow->setCurrentYOffsetForTeleprompter(
				m_pTextWindow->getHeight() -
				lowerRightY
				- 2L); //-2 so decenders/underlines show up.

			textHasMoved = TRUE;
			m_bSomeonesBeginOrEndTimeWasCrossedOver = TRUE;
		    }
		    //If text has yet to be drawn but should be drawn now,
		    // force it to be drawn by claiming that it has "moved":
		    else if(TIME_INVALID==lastKnownTime)
		    {
			textHasMoved = TRUE;
			m_bSomeonesBeginOrEndTimeWasCrossedOver = TRUE;
		    }
		    else
		    {
			textHasMoved = FALSE;
		    }
		}
	    }
	    else
	    {
		if((timeZeroUpperLeftX-totalCrawlAmtInPixels +
			curTextContainerPtr->getXExtent()
			+ m_pTextWindow->getCurrentXOffsetDueToLooping()
			>= 0L)  &&  (prevUpperLeftX != 
			    timeZeroUpperLeftX - totalCrawlAmtInPixels)
			)
		{
		    textHasMoved = TRUE;
		}
		else if((timeZeroUpperLeftY-totalScrollAmtInPixels +
			curTextContainerPtr->getYExtent()
			+ m_pTextWindow->getCurrentYOffsetDueToLooping()
			>= 0L)  &&  (prevUpperLeftY != 
			    timeZeroUpperLeftY - totalScrollAmtInPixels)
			)
		{
		    textHasMoved = TRUE;
		}
#if defined(SHOW_CODE_CHANGE_MESSAGES)
#pragma message("EH- in "__FILE__", \
		    need to add a ( >= MOTION_GRANULARITY pixels) \
		    check to this:")
#pragma message("EH- in "__FILE__", unfinished code \
		    can't do negative scroll or crawl in OnTimeSynch() yet:")
#endif
		//Text has yet to be drawn, so force it to be drawn by
		// claiming that it has moved:
		else if(TIME_INVALID==lastKnownTime)
		{
		    textHasMoved = TRUE;
		}
		else
		{
		    textHasMoved = FALSE;
		}
	    } //end else of "if(TYPE_TELEPROMPTER...)".

	    if(textHasMoved)
	    {
		curTextContainerPtr->textHasChangedSinceLastDraw(TRUE);
		windowNeedsUpdating = TRUE;
	    }

	    curTextContainerPtr->setLastKnownTime(ulCurTime);

	    m_pTextWindow->GetNext(pos);
	} //end "while(pos)".
    } //end "if(listSize)".	

    //Added this to fix LOOPing in tickertape; if
    // no TextContainers are "visible" (to be drawn), then see if they're
    // all negative, and, if so and if isLooping(), add enough in X to all
    // so that they reappear in the window crawling in from the right side:
    if(m_pTextWindow->isLooping()  &&  listSize  &&
	    (TYPE_TICKERTAPE==m_pTextWindow->getType()  ||
		    TYPE_MARQUEE==m_pTextWindow->getType())  &&
	    bDoLoopNow  &&  crawlrate>0)  //XXXEH- neg crawlrate...
    {
	LISTPOSITION pos = m_pTextWindow->GetStartPosition();

	LONG32 totalCrawlAmountInPixels = 
		LONG32(timeSinceTimeZeroIn_sec * double(crawlrate));

#if !defined(SOME_BIG_NUMBER)
#define SOME_BIG_NUMBER	    0x7FFFFFFF
#endif
#if !defined(SOME_BIG_NEGATIVE_NUMBER)
#define SOME_BIG_NEGATIVE_NUMBER   0x80000001
#endif

	
	LONG32 lLowestXAtCurTimeFound = SOME_BIG_NUMBER;
	//We need to know the highest x+xentent found so that we can
	// figure out the total x-extent of all time-visible text so
	// we can figure out where to show it, i.e., at windowWidth +
	// this value (noting that this val should be negative):
	LONG32 lHighestXAtCurTimeFound = SOME_BIG_NEGATIVE_NUMBER;

	BOOL bDoLoopCalc = TRUE;

	BOOL bLoopHasAlreadyHappenedSinceLastCLEAR = FALSE;
	
	LONG32 lWindowNativeWidth = m_pTextWindow->getWidth();

	while(pos)
	{
	    TextContainer* curTextContainerPtr =
		    (TextContainer*)m_pTextWindow->GetAt(pos);

	    HX_ASSERT_VALID_PTR(curTextContainerPtr);
	    if(!curTextContainerPtr)
	    {
		m_pTextWindow->GetNext(pos);
		continue;
	    }

	    BOOL bBeginTimeIsInFuture =
		    IsTimeAMoreRecentThanTimeB(
		    curTextContainerPtr->getBeginTime(),
		    ulCurTime,
		    bIsLiveSource);
	    BOOL bEndTimeIsInThePast =
		    IsTimeAMoreRecentThanTimeB(
		    ulCurTime,
		    curTextContainerPtr->getEndTime(),
		    bIsLiveSource);

	    if(bBeginTimeIsInFuture  ||  bEndTimeIsInThePast)
	    {
		m_pTextWindow->GetNext(pos);
		continue;
	    }
	    
	    LONG32 curXatCurTime =
		    curTextContainerPtr->getXUpperLeftCorner();
	    LONG32 curXPlusXExtent = curXatCurTime +
		    curTextContainerPtr->getXExtent();

	    //XXXEH- do I need this check?  I don't think we'll ever get to
	    // this point if the following is TRUE for any T.C., because
	    // windowNeedsUpdating is not based on bounds checking in X,
	    // only on time:
	    //bDoLoopNow==TRUE means this was already checked above:
	    HX_ASSERT(curXPlusXExtent <= 0L);
	    if(curXPlusXExtent > 0L)
	    {
		bDoLoopCalc = FALSE;
		break; //Don't loop yet - something still can be drawn.
	    }
	    if(curXatCurTime < lLowestXAtCurTimeFound)
	    {
		lLowestXAtCurTimeFound = curXatCurTime;
		bLoopHasAlreadyHappenedSinceLastCLEAR =
			(curTextContainerPtr->
			getAmountLoopedSoFar() != 0);
	    }
	    if(curXPlusXExtent > lHighestXAtCurTimeFound)
	    {
		lHighestXAtCurTimeFound = curXPlusXExtent;
	    }
	    m_pTextWindow->GetNext(pos);
	}

	if(!bLoopHasAlreadyHappenedSinceLastCLEAR  &&  bDoLoopCalc)
	{
	    m_pTextWindow->setCurrentXOffsetDueToLooping(0L);
	}

	if(bDoLoopCalc  &&  lLowestXAtCurTimeFound<SOME_BIG_NUMBER)
	{
	    m_bHorizontalLoopJustOccurred = TRUE;
	    HX_ASSERT(lHighestXAtCurTimeFound > lLowestXAtCurTimeFound);
	    //Avoid possible mod by zero:
	    if(lHighestXAtCurTimeFound <= lLowestXAtCurTimeFound)
	    {
		lHighestXAtCurTimeFound = lLowestXAtCurTimeFound+1;
	    }
	    LONG32 Xoffset = lHighestXAtCurTimeFound %
		    (lWindowNativeWidth +
		    (lHighestXAtCurTimeFound-lLowestXAtCurTimeFound) );
		    ///(lHighestXAtCurTimeFound - lLowestXAtCurTimeFound);
	    m_pTextWindow->setCurrentXOffsetDueToLooping(
		    //Add to current value because this keeps track of
		    // total amount added since time zero (or the last
		    // <CLEAR> if there was one) because the
		    // <POS X0= Y0= > tag in the header of each packet
		    // is based on the amount of motion since time0 (or
		    // the last <CLEAR>):
		    lWindowNativeWidth - lLowestXAtCurTimeFound + Xoffset);

	    //add lLowestX0PlusXExtentFound+windowWidth to each T.C.'s X0:
	    pos = m_pTextWindow->GetStartPosition();

	    while(pos)
	    {
		TextContainer* curTextContainerPtr =
			(TextContainer*)m_pTextWindow->GetAt(pos);

		HX_ASSERT_VALID_PTR(curTextContainerPtr);
		if(!curTextContainerPtr)
		{
		    m_pTextWindow->GetNext(pos);
		    continue;
		}

		BOOL bBeginTimeIsInFuture =
			IsTimeAMoreRecentThanTimeB(
			curTextContainerPtr->getBeginTime(),
			ulCurTime,
			bIsLiveSource);
		BOOL bEndTimeIsInThePast =
			IsTimeAMoreRecentThanTimeB(
		        ulCurTime,
		        curTextContainerPtr->getEndTime(),
		        bIsLiveSource);
		BOOL bLastClearTimeIsInFuture =
			IsTimeAMoreRecentThanTimeB(
			curTextContainerPtr->getTimeOfLastClearTag(),
		        ulCurTime,
		        bIsLiveSource);
		if(	//We only want to adjust ones that haven't
			// shown up yet if they follow a <CLEAR> that hasn't
			// become active yet:
			(bBeginTimeIsInFuture  &&  bLastClearTimeIsInFuture)
			||  bEndTimeIsInThePast)
		{
		    m_pTextWindow->GetNext(pos);
		    continue;
		}

		curTextContainerPtr->setAmountLoopedSoFar(
			m_pTextWindow->getCurrentXOffsetDueToLooping());

		curTextContainerPtr->setFinalDrawWasDone(FALSE);
		curTextContainerPtr->textHasChangedSinceLastDraw(TRUE);

		windowNeedsUpdating = TRUE;

		m_pTextWindow->GetNext(pos);
	    }
	}
    }

    //Added the following to force a delete of all
    // text that is no longer visible in time or space (if the text
    // feed is live) so that it won't needlesly buffer text to which it
    // can never seek back:
    if(
	    (ulCurTime-TimeOfLastListPurge())>
	    TIME_BETWEEN_LIVE_LIST_PURGES_msec)
    {
	//Note: this won't delete looping text that hasn't yet been hit
	// by a CLEAR:
	m_pTextWindow->deleteAllNoLongerVisible();
	TimeOfLastListPurge(ulCurTime);
    }

    return windowNeedsUpdating;
}


BOOL
RealTextRenderer::IsFaceRecognizedByThisVersionOfFileFormat(
	ULONG32 ulFaceIndx)
{
    if(m_ulRTMarkupParsingMajorVersion >
	    REAL_TEXT_MARKUP_PARSING_MAJOR_VERSION  ||
	    (m_ulRTMarkupParsingMajorVersion ==
	    REAL_TEXT_MARKUP_PARSING_MAJOR_VERSION  &&
	    m_ulRTMarkupParsingMinorVersion >=
	    REAL_TEXT_MARKUP_PARSING_MINOR_VERSION) )
    {
	//File format is the same as or newer than the renderer:
	return TRUE;
    }
    //0.2 is beta-2 release (new faces were added between 0.1 and 0.2)
    if(0 == m_ulRTMarkupParsingMajorVersion  &&
	    m_ulRTMarkupParsingMinorVersion < 2)
    {
	if(ulFaceIndx != TIMES_FONT_FACE_INDX  &&
		ulFaceIndx != COURIERTT_FONT_FACE_INDX  &&
		ulFaceIndx != SYSTEM_FONT_FACE_INDX  &&
		ulFaceIndx != ARIAL_FONT_FACE_INDX)
	{
	    //File format doesn't know this font face, so we should pretend
	    // we don't, either:
	    return FALSE;
	}
    }
    //XXXXXEH- in textlib/fontdefs.h, make sure to put a note there warning
		// to update this function if any additional faces are added!

    return TRUE; //is a recognized font face.
}



// /This method takes the original in-line SMIL source data, if that's what
// this stream is, and stores it so it can be used in place of onPacket data
// (which may have been altered by rtffplin which can alter the original text
// if it contains markup tags and/or whitespace chars):
HX_RESULT
RealTextRenderer::setPlainTextData(const char* pTextData)
{
    HX_RESULT hxretval = HXR_OK;

    m_ulPlainTextDataLen = pTextData? (ULONG32)strlen(pTextData) : 0;

    HX_ASSERT(isPlainText());
    if (!isPlainText()  ||  !pTextData  ||  m_ulPlainTextDataLen<1)
    {
	hxretval = HXR_UNEXPECTED;
	goto cleanup;
    }

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

    m_ulPlainTextDataBufferSize = m_ulPlainTextDataLen;
    // /If we know it's going to grow to be bigger, alloc it all now, up
    // to max size; if no wordwrap AND newline chars are treated as spaces,
    // we can ignore almost all of the text past the first few buffers' worth:
    if (m_pTextWindow->usingWordwrap()  &&
	    // /If not explicitly set, newlines (CR, LF) are treated literally:
	    !m_pTextWindow->wasVertAlignExplicitlySet())
    {
	ULONG32 ulFileSize = getSourceFileSize();
	if (ulFileSize > m_ulPlainTextDataBufferSize)
	{
	    m_ulPlainTextDataBufferSize = ulFileSize;
	}
	if (getSourceFileSize() > getMaxPlainTextBytesToBeSentByFF())
	{
	    m_ulPlainTextDataBufferSize = getMaxPlainTextBytesToBeSentByFF();
	}
	if (m_ulPlainTextDataBufferSize >
		m_ulMaxAllowedPlainTextCharsOnThisSystem)
	{
	    m_ulPlainTextDataBufferSize =
		    m_ulMaxAllowedPlainTextCharsOnThisSystem;
	}
    }

    m_pPlainTextData = new char[m_ulPlainTextDataBufferSize+1];

    if (!m_pPlainTextData)
    {
	hxretval = HXR_OUTOFMEMORY;
	goto cleanup;
    }

    strcpy(m_pPlainTextData, pTextData); /* Flawfinder: ignore */

cleanup:
    return hxretval;
}

// /This method allows for case where data comes in multiple OnPacket()s:
HX_RESULT
RealTextRenderer::appendPlainTextData(const char* pTextData)
{
    HX_RESULT hxretval = HXR_OK;

    char* newPlainTextData = NULL;

    ULONG32 ulProirLen = m_ulPlainTextDataLen;
    ULONG32 ulAdditionalLen = pTextData? (ULONG32)strlen(pTextData) : 0;
    
    m_ulPlainTextDataLen += ulAdditionalLen;

    HX_ASSERT(isPlainText());
    if (!isPlainText()  ||  !pTextData  ||  ulAdditionalLen<1  ||
	    !m_pPlainTextData)
    {
	hxretval = HXR_UNEXPECTED;
	goto cleanup;
    }

    if (m_ulPlainTextDataLen > m_ulPlainTextDataBufferSize)
    {
	// /Reallocate buffer:
	newPlainTextData = new char[m_ulPlainTextDataLen+1];
	m_ulPlainTextDataBufferSize = m_ulPlainTextDataLen;
	if (!newPlainTextData)
	{
	    hxretval = HXR_UNEXPECTED;
	    HX_ASSERT(newPlainTextData);
	    goto cleanup;
	}
	if (ulProirLen > 0)
	{
	    strcpy(newPlainTextData, m_pPlainTextData); /* Flawfinder: ignore */
	    SafeStrCat(newPlainTextData, pTextData, m_ulPlainTextDataLen);
	}
	else
	{
	    strcpy(newPlainTextData, pTextData); /* Flawfinder: ignore */
	}
	delete [] m_pPlainTextData;

	m_pPlainTextData = newPlainTextData;
    }
    else // /Just append to existing buffer:
    {
	// /Don't strcat from the start or it gets really slow at very large
	// sizes (20KB range) since strcat has to dig through to find the end
	// of the string each time.  We'll just tell it where (prior) end is:
	memcpy(&(m_pPlainTextData[ulProirLen]), pTextData, ulAdditionalLen); /* Flawfinder: ignore */
	m_pPlainTextData[m_ulPlainTextDataLen] = '\0';
    }

cleanup:
    return hxretval;
}







