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

txtwindw.cpp

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

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

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

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

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

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

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

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

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

#include "txtwindw.h"

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

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

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


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

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

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

      , m_numBreakTagsEncountered(0L)

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

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

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

      , m_bDontIgnoreExtraSpaces(DEFAULT_DONT_IGNORE_EXTRA_SPACES)


      , m_ulNumberOfFontColorPushesInsideLinkText(0L)

      , m_bIsLiveSource(DEFAULT_IS_LIVE_SOURCE)

      , m_ulTimeAtStartup(0L)

      , m_ulTimeOfLastClearTag(0L)

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

      , m_bUseXPOSVal(FALSE)

      , m_bUseYPOSVal(FALSE)

      , m_ulInsideCommentTagNestCount(0L)

      , m_ulNumNewlinesStatedInPacketHeader(0L)

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

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



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

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

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



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

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

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



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




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

      , m_lCurrentYOffsetForTeleprompter(0L)

      , m_bPriorLineAlreadyCentered(FALSE)

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

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

}



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

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

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

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



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

    m_ulTimeOfLastTimeSync=0L;
}


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

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


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

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

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

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

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

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




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



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



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



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



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



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

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

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

    return bRet;
}

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

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

    return bRet;
}

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

    m_ulChromaKey     = ulColor & 0x00FFFFFF;
    m_bIsChromaKeySet = TRUE;

    return bRet;
}

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

    m_ulChromaKeyTolerance = ulTol & 0x00FFFFFF;

    return bRet;
}

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

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

    return bRet;
}

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



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

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

    return (!didErrorOccur);
}



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

    return (!didErrorOccur);
}


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

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

    return hxrslt;
}

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

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

    return hxrslt;
}



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

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

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


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


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

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

    return hxrslt;
}


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

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

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

    return hxrslt;
}


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

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

    return hxrslt;
}


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

    return HXR_OK;
}

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

    m_bUserTextSizingPrefIsSet = TRUE;

    return HXR_OK;
}

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

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

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

    return hxrslt;
}

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

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

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

    return hxrslt;
}


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




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


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


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


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

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

    return (!didErrorOccur);
}
#endif


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



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

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

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

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



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

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

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

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

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



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

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

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

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

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




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

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

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

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

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

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

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

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

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

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


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

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

      }

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

      ULONG32 tokenValueBufLen = tokenValueEndIndex - tokenValueStartIndex;

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

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

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

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

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

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

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

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

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

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

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

    } while(tokenValueEndIndex < headerTagBufLen);

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

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

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

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

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

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

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

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

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

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

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

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



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

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

    LONG32 xExtentInPixels;

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

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

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

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


    pTC_prev = end();

    ULONG32 newLines = pTC->getNumNewlinesAtStart();

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

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

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

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

      bXYAtTimeZeroAlreadyFigured = TRUE;

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

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

    m_newPktStartXAtTimeZero = m_newPktStartYAtTimeZero = INVALID_LONG32;     

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

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

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

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

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

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

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

                }
            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          if(m_bClearWasJustSent)
          {
            xOffset = 0L;
          }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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

                numTCsDeleted++;
                continue;
            }
          }

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


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

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

                bRetVal = TRUE;

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

    return bRetVal;
}


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



Generated by  Doxygen 1.6.0   Back to index