Logo Search packages:      
Sourcecode: helix-player version File versions

c_rtrndr.cpp

/* ***** 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;
}








Generated by  Doxygen 1.6.0   Back to index