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

smlelem.cpp

/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: smlelem.cpp,v 1.5.12.1 2004/07/09 01:58:01 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 ***** */

// system
#include <time.h>
// include
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "ihxpckts.h"
#include "hxxml.h"
#include "smiltype.h"
// pncont
#include "hxstring.h"
#include "hxslist.h"
#include "hxstack.h"
#include "chxpckts.h"
// pnmisc
#include "nptime.h"
#include "smpte.h"
#include "hxwinver.h"
#include "hxstrutl.h"  /* for new_string(). */
// rnxmllib
#include "hxxmlprs.h"
// rmasmil
#include "smlparse.h"
#include "smltime.h"
#include "smlprstime.h"
#include "animattr.h"
#include "smlelem.h"
// pndebug
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE           
static const char HX_THIS_FILE[] = __FILE__;
#endif
 
/*
 * CSmilElement methods
 */

CSmilElement::CSmilElement(SMILNode* pNode):
    m_pNode(pNode),
    m_lBeginOffset(0),
    m_bBeginOffsetSet(FALSE),
    m_bNegBeginOffsetAlreadyUsed(FALSE),
    m_bCurBeginIsOffsetFromSyncBase(FALSE),
    m_ulBeginOffsetFromSyncBase((UINT32)-1),
    m_ulClipBegin((UINT32)-1),
    m_ulAuthoredClipBegin((UINT32)-1),
    m_ulClipEnd((UINT32)-1),
    m_ulDelay((UINT32)-1),
    m_ulDuration((UINT32)-1),
    m_ulOriginalDuration((UINT32)-1),
    m_ulDurationInAddTrack((UINT32)-1),
    m_bCurEndClippedByParent(FALSE),
    m_ulMaxDuration((UINT32)-1),
    m_ulMaxActiveDur((UINT32)-1),
    m_ulMinActiveDur(0),
    m_ulAuthoredDur((UINT32)-1),
    m_bHasExplicitDur(FALSE),
    m_bHasExplicitEnd(FALSE),
    m_bDurationIncludesDelayBeyondSyncbase(FALSE),
    m_bAddDurationAlreadyDone(FALSE),
    m_bUseMediaDurForMinDur(FALSE),
    m_bUseMediaDurForMaxDur(FALSE),
    m_lEndOffset(0),
    m_bEndOffsetSet(FALSE),
    m_ulAnticipatedPauseDur(0),
    m_ulEndSync((UINT32)-1),
    m_fRepeatValue(1.0),
    m_ulTimestamp(0),
    m_bInsertedIntoTimeline(FALSE),
    m_bHasBeenScheduled(FALSE),
    m_bIsPausedInExcl(FALSE),
    m_bIsDeferredInExcl(FALSE),
    m_ulTimeDeferralOccurred((UINT32)-1),
    m_bIsStoppedInExcl(FALSE),
    m_ulStopTimeInExcl((UINT32)-1),
    m_bCurrentSourceIsLive(FALSE),
    m_bRendererInitialized(FALSE),
    m_bIsRestarting(FALSE),
    m_bAwaitingSyncAncestorBeginNotification(FALSE),
    m_ulLongSyncArcBeginInGroupTime((UINT32)-1),
    m_bIndefiniteDuration(FALSE),
    m_bIndefiniteEnd(FALSE),
    m_bIndefiniteBegin(FALSE),
    m_restartBehavior(SmilRestartAlways),
    m_restartDefaultBehavior(SmilRestartInherit),
    // /Defaults to "default" which means use val of syncToleranceDefault:
    m_ulSyncTolerance((UINT32)-1),
    // /Defaults to "inherit"; if there is no parent timed element, then
    // it is implementation-dependent (but should be less than or = 2s):
    m_ulSyncToleranceDefault((UINT32)-1),
    m_syncBehavior(SmilSyncBehaviorDefault),
    m_syncBehaviorDefault(SmilSyncBehaviorInherit),
    m_ulReadIndex(0),
    m_nBeginEventSourceTag(SMILEventSourceNone),
    m_nEndEventSourceTag(SMILEventSourceNone),
    m_nEndsyncEventSourceTag(SMILEventSourceNone),
    m_lBeginEventClockValue(0),
    m_lEndEventClockValue(0),
    m_pHandler(0),
    m_pTimelineElement(NULL),
    m_pBeginTimeList(NULL),
    m_pEndTimeList(NULL),
    m_bWallClockBegin(FALSE),
    m_bWallClockEnd(FALSE),
    m_bHasAtLeastOneEventBasedBegin(FALSE),
    m_bHasAtLeastOneNonEventBasedBegin(FALSE),
    m_eFill(FillDefault),
    m_eFillDefault(FillDefaultInherit),
    m_eActualFill(FillDefault),
    m_eErase(EraseWhenDone),
    m_ulRemoveTime(((UINT32) -1)),
    m_bUsesExternalMediaMarkerFile(FALSE),
    m_bClipBeginUsesMarker(FALSE),
    m_bClipBeginMarkerResolved(FALSE),
    m_bWaitingOnClipBeginToResolve(FALSE),
    m_pszClipBeginMarkerName(NULL),
    m_pszClipBeginExternalMarkerFileName(NULL),
    m_bClipEndUsesMarker(FALSE),
    m_bClipEndMarkerResolved(FALSE),
    m_bWaitingOnClipEndToResolve(FALSE),
    m_pszClipEndMarkerName(NULL),
    m_pszClipEndExternalMarkerFileName(NULL),
    m_eAccessErrorBehavior(AccessErrorBehaviorInherit)
{
    // /XXXEH- we may need to set m_bIndefiniteBegin to TRUE if
    // hasAncestor(SMILExcl, this), but setInitialDelay() fix seems to be
    // all we need (...<==famous last words).
    m_pHyperlinks = new CHXSimpleList;
    getParentRestartDefault();
}

CSmilElement::~CSmilElement()
{
    if (m_pBeginTimeList)
    {
      CHXSimpleList::Iterator ndx = m_pBeginTimeList->Begin();
      for (; ndx != m_pBeginTimeList->End(); ++ndx)
      {
          SmilTimeValue* pTV = (SmilTimeValue*)(*ndx);
          HX_DELETE(pTV);
      }
      HX_DELETE(m_pBeginTimeList);
    }
    if (m_pEndTimeList)
    {
      CHXSimpleList::Iterator ndx = m_pEndTimeList->Begin();
      for (; ndx != m_pEndTimeList->End(); ++ndx)
      {
          SmilTimeValue* pTV = (SmilTimeValue*)(*ndx);
          HX_DELETE(pTV);
      }
      HX_DELETE(m_pEndTimeList);
    }


    HX_DELETE(m_pHyperlinks);
    HX_DELETE(m_pTimelineElement);
    HX_VECTOR_DELETE(m_pszClipBeginMarkerName);
    HX_VECTOR_DELETE(m_pszClipBeginExternalMarkerFileName);
    HX_VECTOR_DELETE(m_pszClipEndMarkerName);
    HX_VECTOR_DELETE(m_pszClipEndExternalMarkerFileName);
}

void
CSmilElement::addDuration(UINT32 ulDuration)
{
    // default add duration just attempts to add it to it's parent
    m_ulDuration = ulDuration;
    if(m_pNode->m_pParent && m_pNode->m_pParent->m_pElement)
    {
      m_pNode->m_pParent->m_pElement->addDuration(m_ulDuration);
      m_bAddDurationAlreadyDone = TRUE;
    }
}

void
CSmilElement::addElement(CSmilElement* pElement,
    UINT32& ulBeginOffset, UINT32& ulDuration)
{
}


void
CSmilElement::prepForRestart(BOOL bParentIsRestarting, LONG32 lWhen)
{
    m_bInsertedIntoTimeline = FALSE;
    m_bRendererInitialized = FALSE;
    m_bBeginOffsetSet = FALSE;
    // /Reset these, too, or hiding of site will happen too early next time:
    m_bCurBeginIsOffsetFromSyncBase = TRUE;
    // /This seems to work fine in case where begin="x; y" where x is a
    // clock value and y is an event-arc that resolves at/to a time prior
    // to x.  (The other option is to set m_bCurBeginIsOffsetFromSyncBase
    // to FALSE when !m_bBeginOffsetSet):
    m_ulBeginOffsetFromSyncBase = 0;

    m_bNegBeginOffsetAlreadyUsed = FALSE;

    m_ulClipBegin = m_ulAuthoredClipBegin;

    m_bAddDurationAlreadyDone = FALSE;


    // /XXXEH- TODO: test this!:
    m_bDurationIncludesDelayBeyondSyncbase = FALSE;

    m_bIsPausedInExcl = FALSE;
    m_bIsDeferredInExcl = FALSE;
    m_ulTimeDeferralOccurred = (UINT32)-1;
    m_bIsStoppedInExcl = FALSE;
    m_ulAnticipatedPauseDur = 0;

    // /If we restart a source, the next play may be a different file, even
    // live one time and on-demand the next, so reset this and let next call
    // to TrackDurationSet() tell us if it's live this time:
    m_bCurrentSourceIsLive = FALSE;

    // /Adding this helps fix a lot of restart problems, including part of
    // PR 62408 and par version of PR 50660.  The playToAssoc was never
    // removed when the track ended the first time, so the doc renderer
    // (m_pHandler) used to see this track as already added and would
    // erroneously add this restart as a repeat track:
    m_bIsRestarting = TRUE;

    // /Helps fix restart problems related to PR 55253:
    if (bParentIsRestarting)
    {
      LONG32 lNextResolvedEndTime = 0;
      BOOL bHasFutureResolvedEnd = FALSE;
      if (m_bHasExplicitEnd)
      {
          SmilTimeValue* pNextResolvedEndTimeValue = NULL;
          HX_RESULT rettimeval = HXR_OK;
          // /lWhen must be in syncBase time space, not group time space:
          rettimeval = getNextResolvedTimeValue(pNextResolvedEndTimeValue,
                SMILTIME_NEGATIVE_INFINITY, lWhen, SmilEndTimeList, NULL);

          if (SUCCEEDED(rettimeval)  &&  NULL != pNextResolvedEndTimeValue)
          {
            HX_RESULT rtval = pNextResolvedEndTimeValue->
                  getEffectiveResolvedTime(lNextResolvedEndTime);
            if (SUCCEEDED(rtval))
            {
                bHasFutureResolvedEnd = TRUE;
                if (lWhen < lNextResolvedEndTime)
                {
                  m_ulDuration = (UINT32)(lNextResolvedEndTime - lWhen);
                }
            }
          }
      }
      if (m_bHasExplicitDur)
      {
          if (!bHasFutureResolvedEnd)
          {
            m_ulDuration = m_ulAuthoredDur;
          }
          else if (lNextResolvedEndTime - lWhen > (LONG32)m_ulAuthoredDur)
          {
            m_ulDuration = m_ulAuthoredDur;
          }
      }
      else
      {
          // /Let SetDelay() and insertTimeline... deal with new duration:
          m_ulDuration = (UINT32)-1;
      }
    }

    // /Reset the delay since it will be new, obviously, for this restart;
    // helps fix restart problems related to PR 56233:
    m_ulDelay = (UINT32)-1;

    // /Note: we don't want to or need to reset the delay here.  A restart
    // based on either a syncArc or eventArc will take care of that.

    if (m_pTimelineElement)
    {
      m_pTimelineElement->prepForRestart();
    }

    // /Now, call this function for all descendants as well in case this is
    // a time container (or a media object with timeline-element children);
    // (Helps fix restart problems related to PR 55253)
    SMILNode* pChild = m_pNode->getFirstChild();
    while (pChild)
    {
      CSmilElement* pElement = pChild->m_pElement;
      if (pElement  &&  pElement->m_pHandler  &&
            pElement->m_bInsertedIntoTimeline)
      {
          pElement->prepForRestart(TRUE, lWhen);
      }
      pChild = m_pNode->getNextChild();
    }
}


void
CSmilElement::prepForPauseInExcl(LONG32 lCurTime)
{
    m_bIsPausedInExcl = TRUE;

    // /If we're being set to paused state, then we need to adjust our hide
    // event (removeTime) in concert with adjusting when we will end:
    BOOL bHasFutureResolvedExplicitEnd = FALSE;

    if (m_bHasExplicitEnd)
    {
      SmilTimeValue* pNextResolvedEndTimeValue = NULL;
      HX_RESULT rettimeval = HXR_OK;
      // /lCurTime must be in syncBase time space, not group time space:
      rettimeval = getNextResolvedTimeValue(pNextResolvedEndTimeValue,
            SMILTIME_NEGATIVE_INFINITY, lCurTime, SmilEndTimeList, NULL);

      if (SUCCEEDED(rettimeval)  &&  NULL != pNextResolvedEndTimeValue)
      {
          LONG32 lNextResolvedEndTime = SMILTIME_NEGATIVE_INFINITY;
          HX_RESULT rtval = pNextResolvedEndTimeValue->
                getEffectiveResolvedTime(lNextResolvedEndTime);
          if (SUCCEEDED(rtval))
          {
            HX_ASSERT(lCurTime <= lNextResolvedEndTime);
            bHasFutureResolvedExplicitEnd = TRUE;
            updateRemoveTime(lNextResolvedEndTime);
          }
      }
    }

    // /If we don't have explicit end, then we'll end when we get resumed
    // plus the remainder of our duration:
    if (!bHasFutureResolvedExplicitEnd)
    {
      updateRemoveTime(SMILTIME_PAUSED_INDEFINITELY);
    }
}

void
CSmilElement::prepForResumeInExcl()
{
    HX_ASSERT(m_bIsPausedInExcl);

    LISTPOSITION lPos = NULL;
    HX_ASSERT(m_pBeginTimeList);
    if (m_pBeginTimeList)
    {
      lPos = m_pBeginTimeList->GetHeadPosition();
    }
    while (lPos)
    {
      SmilTimeValue* pTimeVal =
            (SmilTimeValue*)m_pBeginTimeList->GetAt(lPos);

      // /There should be only one resumeEvent:
      if (pTimeVal  &&  pTimeVal->isResumeEvent())
      {
          // /Fixes PR 62408: remove resumeEvent from element's begin
          // list since it was an internally-generated single-use begin
          // value and we don't want it to re-resolve when (if) the
          // original pausing element restarts and then raises another
          // resumeEvent when it ends again:
          m_pBeginTimeList->RemoveAt(lPos);
          break;
      }
      m_pBeginTimeList->GetNext(lPos);
    }

    m_bIsPausedInExcl = FALSE;
}

void
CSmilElement::prepForStopInExcl(LONG32 lCurTime)
{
    m_bIsStoppedInExcl = TRUE;
    m_ulStopTimeInExcl = (ULONG32)lCurTime;
}

void
CSmilElement::prepForDeferralInExcl(UINT32 lCurTime)
{
    m_ulTimeDeferralOccurred = lCurTime;
    m_bIsDeferredInExcl = TRUE;
}



HX_RESULT
CSmilElement::setBeginTime(CSmilParser* pParser)
{
    HX_RESULT ret = HXR_OK;
 
    BOOL bBeginTimeHasBeenSet = FALSE;

    UINT32 ulOffset = 0;
    SmilTimeValue* pNextResolvedTimeValue = NULL;

    LISTPOSITION lPos = NULL;

    // /We need this so we can go through
    CHXSimpleList* pListOfAllResolvedBeginTimes = new CHXSimpleList;
    // /Looks at all resolved begin times in the list and
    // returns the SmilTimeValue with the closest time at or
    // later than the time passed in:
    HX_RESULT rettimeval = HXR_OK;
    rettimeval = getNextResolvedTimeValue(pNextResolvedTimeValue,
          SMILTIME_NEGATIVE_INFINITY, SMILTIME_NEGATIVE_INFINITY,
          SmilBeginTimeList, pListOfAllResolvedBeginTimes);
    // /NOTE: ret!=HXR_OK is NOT an error condition; it means
    // that no value in the begin time list was found that was
    // resolved.

    BOOL bIsDescendantOfExcl = pParser->hasAncestor(SMILExcl, m_pNode);

    SMILNode* pSyncAncestor = pParser->getSyncAncestor(m_pNode);
    HX_ASSERT(pSyncAncestor);

    // make sure that we do not have an illegal begin value
    // if we have a seq for a parent.
    // /Note: we want to use our sync ancestor, not our parent, because
    // our parent could be a non-time-container like <a>, <switch>,
    // or <priorityClass>:
    if ((pSyncAncestor  &&  SMILSeq == pSyncAncestor->m_tag  &&
      // /OK if parent seq was created to expand for repeat purposes:
      !m_pNode->m_bRepeatHandled)  &&
      ( (!SUCCEEDED(rettimeval)  ||  NULL==pNextResolvedTimeValue)  ||
      (pNextResolvedTimeValue->m_type != SmilTimeOffset && 
      pNextResolvedTimeValue->m_type != SmilTimeClockValue) ) )
    {
      // only offsets are legal values...
      ret = HXR_INVALID_PARAMETER;
      goto cleanup;
    }

    // /For excl descendants, we don't want to declare that a begin time
    // has been set, because we need to figure out whether or not to
    // schedule it based on when other excl children begin (which we may
    // not know yet):

    if (SUCCEEDED(rettimeval) && NULL != pNextResolvedTimeValue)
    {
      if (pListOfAllResolvedBeginTimes)
      {
          // /Go through this list and insert each into the pending list:
          LISTPOSITION lPosTmp =
                pListOfAllResolvedBeginTimes->GetHeadPosition();
          while (lPosTmp)
          {
            SmilTimeValue* pTmpVal = (SmilTimeValue*)
                  pListOfAllResolvedBeginTimes->GetAt(lPosTmp);
            if (pTmpVal  &&  pTmpVal->isTimeResolved()  &&
                  pTmpVal->m_pElement)
            {
                if (pTmpVal != pNextResolvedTimeValue  ||
                      bIsDescendantOfExcl)
                {
                  // /(?XXXEH- TODO: insert into list based on resolved
                  // time?  note that begin="20s;0s" works OK, so...?)
                  if (pParser->EstablishBeginTimeList())
                  {
                      CHXSimpleList* pList =
                            pParser->GetPendingBeginTimeList();
                      // /Don't add if it's a duplicate:
                      if (!pParser->isDuplicateEntry(pList, pTmpVal))
                      {
                        pList->AddTail(pTmpVal);
                      }
                  }
                }
            }
            else
            {
                HX_ASSERT(0  &&  "timeval invalid");
            }
            pListOfAllResolvedBeginTimes->GetNext(lPosTmp);
          } // /end while(lPos...).
      }
      HX_DELETE(pListOfAllResolvedBeginTimes);
      
      if (!bIsDescendantOfExcl)
      {
          bBeginTimeHasBeenSet = TRUE;
          HX_ASSERT(pNextResolvedTimeValue->isTimeResolved());
          switch (pNextResolvedTimeValue->m_type)
          {
            case SmilTimeOffset:
            case SmilTimeClockValue:
            {
                // /XXXEH- is this what we really want to do?
                // Alternatively, we could have resolved-to "base" time
                // as 0 and then m_lOffset is added to get resolved-to
                // "offset" time:
                m_lBeginOffset = pNextResolvedTimeValue->getTimeOffset();
                m_bBeginOffsetSet = TRUE;
                // /Handle this now for time containers who never get
                // called with a TrackDurationSet():
                m_bCurBeginIsOffsetFromSyncBase = TRUE;
                m_ulBeginOffsetFromSyncBase = m_lBeginOffset;

#if defined(_DEBUG) // /XXXEH- testing!:
                LONG32 lResolvedToTime = 0;
                HX_RESULT tmprslt =
                      pNextResolvedTimeValue->getEffectiveResolvedTime(
                      lResolvedToTime);
                HX_ASSERT(tmprslt==HXR_OK  &&
                      m_lBeginOffset == lResolvedToTime);
#endif
            }
            break;

            case SmilTimeEvent:
#if defined(ENABLE_SYNC_TO_PREV)
            case SmilTimeSyncToPrev:
#endif
            case SmilTimeSyncBase:
            {
                // /NOTE: this's time must be *resolved* already which
                // shouldn't happen yet:
                HX_ASSERT(!pNextResolvedTimeValue->isTimeResolved());
            }
            break;

                case SmilTimeMediaMarker:
                {
                HX_ASSERT(!pNextResolvedTimeValue->isTimeResolved());
                }
                break;

            case SmilTimeWallclock:
            {
                // /XXXEH- wouldn't it be better to use
                // getEffectiveResolvedTime() here(?) so as to treat all time
                // types consistently?
                m_lBeginOffset = pNextResolvedTimeValue->getTimeOffset();
                m_bBeginOffsetSet = TRUE;
                // /Handle this now for time containers who never get
                // called with a TrackDurationSet():
                m_bCurBeginIsOffsetFromSyncBase = TRUE;
                m_ulBeginOffsetFromSyncBase = m_lBeginOffset;

                m_bWallClockBegin = TRUE;
            }
            break;

            default:
                HX_ASSERT(0);
                break;
          } // /end of "switch(pNextResolvedTimeValue->m_type)".
      } // /end of "if (!bIsDescendantOfExcl)".
    }

    // /Now, go through all unresolved begin times and add this element to
    // the appropriate list(s) for each unresolved time value found:
    if (m_pBeginTimeList)
    {
      lPos = m_pBeginTimeList->GetHeadPosition();
    }

    if (NULL == lPos) // /list is empty.
    {
      goto cleanup;
    }

    while (lPos  &&  HXR_OK == ret)
    {
      SmilTimeValue* pTmpVal = (SmilTimeValue*)m_pBeginTimeList->GetAt(lPos);

      if (pTmpVal  &&  !pTmpVal->isTimeResolved())
      {
          switch (pTmpVal->m_type)
          {
          case SmilTimeOffset:
          case SmilTimeClockValue:
            {
                // /We shouldn't ever get here because clock times are
                // always resolved:
                HX_ASSERT(pTmpVal->isTimeResolved());
            }
            break;
          case SmilTimeSyncBase:
            {
                pParser->addBeginTimeSyncElement(this);
                if (!bBeginTimeHasBeenSet)
                {
                  m_BeginEventSourceID = pTmpVal->m_idRef;
                  m_nBeginEventSourceTag = pTmpVal->m_position;
                  // /NOTE: this is unresolved:
                  m_lBeginEventClockValue =
                        pTmpVal->getTimeOffset();
                }
                // /Fixes BUG-20010521_syncArcToElementWith...smil
                // where begin="0s;x.end" and x.end was being ignored:
                else
                {
                  if (!m_BeginEventSourceID.GetLength())
                  {
                      m_BeginEventSourceID = pTmpVal->m_idRef;
                      m_nBeginEventSourceTag = pTmpVal->m_position;
                      // /NOTE: this is unresolved:
                      m_lBeginEventClockValue = pTmpVal->getTimeOffset();
                  }
                  else
                  {
                      HX_ASSERT(0  &&
                        "XXXEHodge: need to handle multiple sync-arc begins");
                      // /XXXEH- TODO: if m_BeginEventSourceID is already
                      // set due to another sync-arc begin time, we need
                      // to have a list of m_BeginEventSourceID's & ...etc.
                      // Currently, just the first one wins.  We could
                      // treat this sync-arc as a beginEvent event-arc...
                      // (We also need to handle this in SetEndTime().)
                  }

                }
            }
            break;
#if defined(ENABLE_SYNC_TO_PREV)
          case SmilTimeSyncToPrev:
            {
                // /XXXEH- shouldn't we treat this just like we treat the
                // sync base case above by doing the following?:
                //          pParser->addBeginTimeSyncElement(this);
                // /XXXEH- make sure we handle begin="3s; prev.end" where
                // both are valid times; the following doesn't handle this
                // case:
                if (!bBeginTimeHasBeenSet)
                {
                  SMILNode* pSibling =
                        m_pNode->m_pParent->getFirstChild();
                  // /Spec says to use parent time container's begin if
                  // m_pNode is the first child (and has no prev
                  // sibling):
                  SMILNode* pLastNode = m_pNode->m_pParent;
                  while (pSibling)
                  {
                      if (pSibling->m_id == m_pNode->m_id)
                      {
                        // we found our boy...
                        break;
                      }
                      pLastNode = pSibling;
                      // /XXXEH- TODO: skip over children that aren't
                      // *timed* elements, e.g., <a> tags; maybe we
                      // are supposed to use a non-timed-prev-element's
                      // last timed child, if any, and, if none exists,
                      // use it's prev timed sibling???:
                      pSibling = m_pNode->m_pParent->getNextChild();
                  }
                  HX_ASSERT(pSibling && pLastNode);
                  if (pSibling && pLastNode)
                  {
                      // /First, if we had no prev sibling, then
                      // we need to use our parent time container's
                      // begin time as our sync base:
                      if (pLastNode == m_pNode->m_pParent)
                      {
                        SMILNode* pFirstTimeContainerAncestor =
                              pParser->getSyncAncestor(m_pNode);
                        // /There should ALWAYS be a time container
                        // ancestor, even if it's the implied seq
                        // that we create when no time container
                        // exists as the 1st timed child of the body:
                        HX_ASSERT(pFirstTimeContainerAncestor);
                        if (pFirstTimeContainerAncestor)
                        {
#if XXXEH_
/* /Need to figure out if this is earlier than any current times and, if
so, call insertElementWithPendingBeginOrEnd() with the earlier time, else
call insertElementWithPendingBeginOrEnd() with this (pTmpVal) time:
*/
#endif
                            // /For parent as prev, we can just treat
                            // this as a SmilTimeOffset value since
                            // they're essentially the same thing:
                            pLastNode = pFirstTimeContainerAncestor;
                            m_lBeginOffset= pTmpVal->getTimeOffset();
                            pTmpVal->m_type = SmilTimeOffset;
                            pTmpVal->setIsTimeResolved(TRUE);
                            m_bBeginOffsetSet = TRUE;
                        }
                        // /else give up; can't do anything here.
                      }
                      else // /Use prev sibling time:
                      {
                        m_BeginEventSourceID = pLastNode->m_id;
                        // /XXXEH- wouldn't it be better to use
                        // getEffectiveResolvedTime() here(?) so as
                        // to treat all time types consistently?
                        m_lBeginEventClockValue =
                              pTmpVal->getTimeOffset();
                        m_nBeginEventSourceTag =
                              pTmpVal->m_position;
                      }
                  }
                  else
                  {
                      ret = HXR_FAIL;
                  }
                }
            }
            break;
#endif
          case SmilTimeEvent:
            {
                pParser->addBeginEventElement(pTmpVal);
            }
                break;
            case SmilTimeMediaMarker:
                {
                    pParser->addBeginMediaMarkerSyncElement(pTmpVal);
                }
                break;
            case SmilTimeWallclock:
            {
                // /We shouldn't ever get here because wallclock times
                // are always resolved:
                HX_ASSERT(pTmpVal->isTimeResolved());
            }
            break;
          default:
            HX_ASSERT(0);
            break;
          } // /end switch(pTmpVal->m_type).
      }
      m_pBeginTimeList->GetNext(lPos);
    } // /end while (lPos ...).

    // else time must already be set.
cleanup:
    return ret;
}

HX_RESULT
CSmilElement::setEndTime(CSmilParser* pParser)
{
    HX_RESULT ret = HXR_OK;

    BOOL bEndTimeHasBeenSet = FALSE;

    UINT32 ulOffset = 0;
    SmilTimeValue* pNextResolvedTimeValue = NULL;

    LISTPOSITION lPos = NULL;

    CHXSimpleList* pListOfAllResolvedEndTimes = new CHXSimpleList;
    // /Looks at all resolved end times in the list and
    // returns the SmilTimeValue with the closest time at or
    // later than the time passed in:
    HX_RESULT rettimeval = HXR_OK;
    rettimeval = getNextResolvedTimeValue(pNextResolvedTimeValue,
          SMILTIME_NEGATIVE_INFINITY, SMILTIME_NEGATIVE_INFINITY,
          SmilEndTimeList, pListOfAllResolvedEndTimes);
    // /NOTE: ret!=HXR_OK is NOT an error condition; it means
    // that no value in the end time list was found that was
    // resolved.
    
    if (SUCCEEDED(rettimeval) && NULL != pNextResolvedTimeValue)
    {
      if (pListOfAllResolvedEndTimes)
      {
          // /Go through this list and insert each into the pending list:
          LISTPOSITION lPosTmp =
                pListOfAllResolvedEndTimes->GetHeadPosition();
          while (lPosTmp)
          {
            SmilTimeValue* pTmpVal = (SmilTimeValue*)
                  pListOfAllResolvedEndTimes->GetAt(lPosTmp);
            if (pTmpVal  &&  pTmpVal->isTimeResolved()  &&
                  pTmpVal->m_pElement)
            {
                if (pTmpVal != pNextResolvedTimeValue)
                {
                  // /(?XXXEH- TODO: insert into list based on resolved
                  // time?  end="20s;0s" works OK, so...?)
                  if (pParser->EstablishEndTimeList())
                  {
                      CHXSimpleList* pList =
                            pParser->GetPendingEndTimeList();
                      // /Don't add if it's a duplicate:
                      if (!pParser->isDuplicateEntry(pList, pTmpVal))
                      {
                        pList->AddTail(pTmpVal);
                      }
                  }
                }
            }
            else
            {
                HX_ASSERT(0  &&  "timeval invalid");
            }
            pListOfAllResolvedEndTimes->GetNext(lPosTmp);
          } // /end while(lPos...).
      }
      HX_DELETE(pListOfAllResolvedEndTimes);

      bEndTimeHasBeenSet = TRUE;
      HX_ASSERT(pNextResolvedTimeValue->isTimeResolved());
      switch (pNextResolvedTimeValue->m_type)
      {
      case SmilTimeOffset:
      case SmilTimeClockValue:
          {
            // /XXXEH- is this what we really want to do?
            // Alternatively, we could have resolved-to "base" time
            // as 0 and then m_lOffset is added to get resolved-to
            // "offset" time:
            m_lEndOffset = pNextResolvedTimeValue->getTimeOffset();
            m_bEndOffsetSet = TRUE;
#if defined(_DEBUG) // /XXXEH- testing!:
            LONG32 lResolvedToTime = 0;
            HX_RESULT tmprslt =
                  pNextResolvedTimeValue->getEffectiveResolvedTime(
                  lResolvedToTime);
            HX_ASSERT(tmprslt==HXR_OK  &&
                  m_lEndOffset == lResolvedToTime);
#endif
          }
          break;
      case SmilTimeSyncBase:
#if defined(ENABLE_SYNC_TO_PREV)
      case SmilTimeSyncToPrev:
#endif
      case SmilTimeEvent:
          {
            // /NOTE: this's time must be *resolved* already which
            // shouldn't happen yet:
            HX_ASSERT(!pNextResolvedTimeValue->isTimeResolved());
          }
          break;
      case SmilTimeMediaMarker:
          {
            //XXXEH- TODO: handle media markers:
            HX_ASSERT(0);
            //pParser->addEndMediaMarkerSyncElement(this);
          }
          break;
      case SmilTimeWallclock:
          {
            // /XXXEH- wouldn't it be better to use
            // getEffectiveResolvedTime() here(?) so as to treat all time
            // types consistently?
            m_lEndOffset = pNextResolvedTimeValue->getTimeOffset();
            m_bEndOffsetSet = TRUE;
            m_bWallClockEnd = TRUE;
          }
          break;
      case SmilTimeNone:
          break;
      } // /end switch(pNextResolvedTimeValue->m_type).
    }

    // /Now, go through all unresolved end times and add this element to
    // the appropriate list(s) for each unresolved time value found:
    if (m_pEndTimeList)
    {
      lPos = m_pEndTimeList->GetHeadPosition();
    }

    if (NULL == lPos) // /list is empty.
    {
      goto cleanup;
    }

    while (lPos  &&  HXR_OK == ret)
    {
      SmilTimeValue* pTmpVal = (SmilTimeValue*)m_pEndTimeList->GetAt(lPos);

      if (pTmpVal  &&  !pTmpVal->isTimeResolved())
      {
          switch (pTmpVal->m_type)
          {
          case SmilTimeOffset:
          case SmilTimeClockValue:
            {
                // /We shouldn't ever get here because clock times are
                // always resolved:
                HX_ASSERT(pTmpVal->isTimeResolved());
            }
            break;
          case SmilTimeSyncBase:
            {
                pParser->addEndTimeSyncElement(this);
                // /XXXEH- make sure we handle end="3s; prev.end" where
                // both are valid times; the following doesn't handle this
                // case:
                if (!bEndTimeHasBeenSet)
                {
                  m_EndEventSourceID = pTmpVal->m_idRef;
                  m_nEndEventSourceTag = pTmpVal->m_position;
                  // /XXXEH- wouldn't it be better to use
                  // getEffectiveResolvedTime() here(?) so as to treat
                  // all time types consistently?
                  m_lEndEventClockValue =
                        pTmpVal->getTimeOffset();
                }
            }
            break;
#if defined(ENABLE_SYNC_TO_PREV)
          case SmilTimeSyncToPrev:
            {
                // /XXXEH- shouldn't we treat this just like we treat the
                // sync base case above by doing the following?:
                //          pParser->addEndTimeSyncElement(this);
                // /XXXEH- make sure we handle end="3s; prev.end" where
                // both are valid times; the following doesn't handle this
                // case:
                if (!bEndTimeHasBeenSet)
                {
                  SMILNode* pSibling =
                        m_pNode->m_pParent->getFirstChild();
                  SMILNode* pLastNode = pSibling;
                  while (pSibling)
                  {
                      if (pSibling->m_id == m_pNode->m_id)
                      {
                        // we found our boy...
                        break;
                      }
                      pLastNode = pSibling;
                      pSibling = m_pNode->m_pParent->getNextChild();
                  }
                  HX_ASSERT(pSibling && pLastNode);
                  if (pSibling && pLastNode)
                  {
                      m_EndEventSourceID = pLastNode->m_id;
                      // /XXXEH- wouldn't it be better to use
                      // getEffectiveResolvedTime() here(?) so as to
                      // treat all time types consistently?
                      m_lEndEventClockValue =
                            pTmpVal->getTimeOffset();
                      m_nEndEventSourceTag =
                            pTmpVal->m_position;
                  }
                  else
                  {
                      ret = HXR_FAIL;
                  }
                }
            }
            break;
#endif
          case SmilTimeEvent:
            {
                pParser->addEndEventElement(pTmpVal);
            }
                break;
            case SmilTimeMediaMarker:
                {
                    pParser->addEndMediaMarkerSyncElement(pTmpVal);
                }
                break;
          case SmilTimeWallclock:
            {
                // /We shouldn't ever get here because wallclock times
                // are always resolved:
                HX_ASSERT(pTmpVal->isTimeResolved());
            }
            break;
          case SmilTimeNone:
            break;
          } // /end switch(pTmpVal->m_type).
      }
      m_pEndTimeList->GetNext(lPos);
    } // /end while (lPos ...).
    // else time must allready be set.
cleanup:
    return ret;
}

HX_RESULT
CSmilElement::getEndTimeValue(REF(SmilTimeValue*) pValue)
{
    HX_RESULT ret = HXR_OK;
    // This function just returns the first value in the end-time list,
    // even if that value is not yet resolved to a clock time:
    // If you want to use the "right" value for a given time t, then
    // call "getNextResolvedTimeValue()"
    if (m_pEndTimeList)
    {
      LISTPOSITION pos = m_pEndTimeList->GetHeadPosition();
      if (pos)
      {
          pValue = (SmilTimeValue*)m_pEndTimeList->GetAt(pos);
      }
    }
    return ret;
}

HX_RESULT
CSmilElement::getBeginTimeValue(REF(SmilTimeValue*) pValue)
{
    HX_RESULT ret = HXR_OK;
    // This function just returns the first value in the begin-time list,
    // even if that value is not yet resolved to a clock time:
    // If you want to use the "right" value for a given time t, then
    // call "getNextResolvedTimeValue()"
    if (m_pBeginTimeList)
    {
      LISTPOSITION pos = m_pBeginTimeList->GetHeadPosition();
      if (pos)
      {
          pValue = (SmilTimeValue*)m_pBeginTimeList->GetAt(pos);
      }
    }
    return ret;
}


// /Returns HXR_OK except if the element has not ever been inserted in the
// timeline, in which case the return value is HXR_NOT_INITIALIZED.
// In this case, the value placed into ulActualStartTime will remain whatever
// is set in the delay value of the element.  If the element has an
// indefinite begin time, SMIL_TIME_INFINITY is returned in ulActualStartTime
// with HXR_OK as the return value.  Note that SMIL_TIME_INFINITY is not
// the same value as ((UINT32)-1) which is a possible return value and means
// that the value is not initialized:
HX_RESULT
CSmilElement::getCurrentScheduledStartTime(REF(ULONG32) ulActualStartTime)
{
    HX_RESULT ret = HXR_OK;
    
    ulActualStartTime = m_ulDelay;

    BOOL bIsMediaObject = CSmilParser::isMediaObject(m_pNode);

    if (!m_bHasBeenScheduled && bIsMediaObject)
    {
      ret = HXR_NOT_INITIALIZED;
    }
    else if (m_bIndefiniteBegin)
    {
      ulActualStartTime = SMILTIME_INFINITY;
    }
    else if (!bIsMediaObject && ulActualStartTime == ((UINT32) -1))
    {
      ret = HXR_NOT_INITIALIZED;
    }
// /#define XXXEHODGE_DEBUG_PRINTF_CURRENTSCHEDULEDSTARTTIME
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_PRINTF_CURRENTSCHEDULEDSTARTTIME)
    {
      static BOOL bFirstTime = TRUE;
      FILE* f1 = ::fopen("c:\\smil2.txt", bFirstTime?"w":"a+");
      ::fprintf(f1, "\n[%s]->getCurrentScheduledStartTime() "
            "ulActualStopTime := %lu (delay=%lu, dur=%lu, "
            "beginOffsetFromSyncBase=%lu)  \n", (const char*)m_pNode->m_id,
            ulActualStartTime, m_ulDelay, m_ulDuration, m_ulBeginOffsetFromSyncBase);
      ::fclose(f1);
      bFirstTime=FALSE;
    }
#endif
    return ret;
}

// /Returns HXR_OK except if the element has not ever been inserted in the
// timeline, in which case the return value is HXR_NOT_INITIALIZED.
// In this case, the value placed into ulActualStopTime will remain whatever
// is set in the delay value of the element.  If the element has an
// indefinite end time, SMIL_TIME_INFINITY is returned in ulActualStopTime
// with HXR_OK as the return value.  Note that SMIL_TIME_INFINITY is not
// the same value as ((UINT32)-1) which is a possible return value and means
// that the value is not initialized:
HX_RESULT
CSmilElement::getCurrentScheduledStopTime(REF(ULONG32) ulActualStopTime)
{
    HX_RESULT ret = HXR_OK;

    // /Fixes PR 63733: initialize to invalid time in case caller did not:
    ulActualStopTime = (UINT32)-1;

    if (m_bIndefiniteDuration  ||  m_bIndefiniteEnd)
    {
      ulActualStopTime = SMILTIME_INFINITY;
    }
    // /Helps fix PR 53531 (unwinding after string of pauses) when
    // m_ulDuration has not been updated to reflect the paused time so far:
    else if (isPausedInExcl())
    {
#if XXXEH_HANDLE_CASE_WHERE_RESOLVED_END_IS_NOW_IN_EFFECT
#else
      ulActualStopTime = SMILTIME_PAUSED_INDEFINITELY;
#endif
    }
    else if (isStoppedInExcl())
    {
      ulActualStopTime = m_ulStopTimeInExcl;
    }
    else if (isDeferredInExcl())
    {
      ulActualStopTime = SMILTIME_DEFERRED_INDEFINITELY;
    }
    else if (m_ulDelay    != ((UINT32) -1) &&
             m_ulDuration != ((UINT32) -1))
    {
        if (m_bBeginOffsetSet)
        {
          LONG32 lBeginOffsetPast0 = m_lBeginOffset;
          if (m_lBeginOffset < 0)
          {
            lBeginOffsetPast0 = 0;
          }
            ulActualStopTime = m_ulDelay + m_ulDuration - lBeginOffsetPast0;
        }
        else
        {
            if (m_bCurBeginIsOffsetFromSyncBase)
            {
            ulActualStopTime = m_ulDelay + m_ulDuration;
            // /Adding this if() check fixes hide-site problems exposed
            // in the repro case for PR 53531 once 53531 had been fixed;
            // this makes sure that the beginOffsetFromSyncBase is
            // accounted for only when it should be. Fixes
            // BUG-20010619_lastExclChildGetsItsOffsetFromSyncbaseIgnored.smil
            if (m_bDurationIncludesDelayBeyondSyncbase)
            {
                ulActualStopTime -= m_ulBeginOffsetFromSyncBase;
            }
            }
            else
            {
                ulActualStopTime = m_ulDelay + m_ulDuration;
            }
        }
    }
    else if (m_ulDelay != (UINT32)-1  &&  m_bCurrentSourceIsLive)
    {
      // /Fix for PR 57230:
      // live sources that have 0 dur, which means they play
      // forever, should have infinity, not 0, used as
      // their dur when calculating whether or not they're
      // visible at the current time:
      ulActualStopTime = (UINT32)SMILTIME_INFINITY;
    }
    else
    {
        ret = HXR_NOT_INITIALIZED;
    }

// /#define XXXEHODGE_DEBUG_PRINTF_CURRENTSCHEDULEDSTOPTIME
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_PRINTF_CURRENTSCHEDULEDSTOPTIME)
    {
      static BOOL bFirstTime = TRUE;
      FILE* f1 = ::fopen("c:\\smil2.txt", bFirstTime?"w":"a+");
      ::fprintf(f1, "\n[%s]->getCurrentScheduledStopTime() "
            "ulActualStopTime := %lu (delay=%lu, dur=%lu, "
            "beginOffsetFromSyncBase=%lu)  \n", (const char*)m_pNode->m_id,
            ulActualStopTime, m_ulDelay, m_ulDuration, m_ulBeginOffsetFromSyncBase);
      ::fclose(f1);
      bFirstTime=FALSE;
    }
#endif

    return ret;
}


// /This function takes a time as input and returns in pValue the begin
// or end time (depending on listType) that makes the most
// sense given the time passed in; it assumes times that are
// resolvable have already been resolved (and thus their m_bIsResolved
// is TRUE), and it thus ignores unresolved times.  It returns
// HXR_FAILED and sets pValue to NULL if no now-or-future resolved begin
// time is found.
// NOTE!!!: this will NOT return the currently-active time if the element
// is already playing at lCurTime since it would be in the past.  The
// currently-active begin and end times can be found in this->m_lBeginOffset
// or this->m_lEndOffset, respectively, and that those values are only
// valid if m_bBeginOffsetSet and m_bEndOffsetSet, respectively, are TRUE:
// Also Note: pValue, as well as the two lists, is|are filled with either
// begin or end time|times, depending on the listType value:
HX_RESULT
CSmilElement::getNextResolvedTimeValue(REF(SmilTimeValue*) pValue,
                        INT32 lCurTimeInGroupTime,
                        INT32 lCurTimeInSyncBaseTime,
                        SmilTimingListType listType,
                        CHXSimpleList* pListOfAllResolvedTimes)
{
    HX_RESULT retval = HXR_OK;
    SmilTimeValue* pTmpVal = NULL;
    SmilTimeValue* pTimeValFoundSoFar = NULL;
    CHXSimpleList* pWhichList = NULL;

    LONG32 lCurTimeInSameTimeSpace = lCurTimeInSyncBaseTime;
    LONG32 lCurUsableTimeInGroupTime = lCurTimeInGroupTime;
    // /If group time is not known, use sync-base time:
    if (SMILTIME_NEGATIVE_INFINITY == lCurTimeInGroupTime)
    {
      lCurUsableTimeInGroupTime = lCurTimeInSyncBaseTime;
    }

    LISTPOSITION lPos = NULL;
    if (SmilBeginTimeList == listType) // /get begin time:
    {
      pWhichList = m_pBeginTimeList;
    }
    else if (SmilEndTimeList == listType)// /get end time:
    {
      pWhichList = m_pEndTimeList;
    }
    else
    {
      HX_ASSERT(SmilBeginTimeList == listType  ||
            SmilEndTimeList == listType);
    }
    if (NULL == pWhichList)
    {
      retval = HXR_FAILED;
      goto cleanup;
    }

    lPos = pWhichList->GetHeadPosition();

    if (NULL == lPos) // /list is empty.
    {
      retval = HXR_FAILED;
      goto cleanup;
    }

    // /The following loop simply finds the time that is nearest
    // to, but not earlier than, lCurTime:
    // *Which* curTime depends on the type; events resolve to group times,
    // while clockValues resolve to syncBase times:
    while (lPos  &&  HXR_OK == retval)
    {
      pTmpVal = (SmilTimeValue*)pWhichList->GetAt(lPos);

      if (pTmpVal)
      {
          if (!pTmpVal->isTimeResolved())
          {
            pWhichList->GetNext(lPos);
            continue;
          }

          // /It's resolved, so insert it:
          if (pListOfAllResolvedTimes)
          {
            pListOfAllResolvedTimes->AddTail(pTmpVal);
          }

          LONG32 lResolvedToTimeOfTimeValFoundSoFar =SMILTIME_INFINITY;
          HX_RESULT tmprslt = HXR_OK;
          if (NULL != pTimeValFoundSoFar)
          {
            // /We want to use pTimeValFoundSoFar, not pTmpVal here!
            // Fixes case where there are two or more resolved times and
            // the first, lexically, is higher than any of the others:
            tmprslt = pTimeValFoundSoFar->getEffectiveResolvedTime(
                  lResolvedToTimeOfTimeValFoundSoFar);
            HX_ASSERT(HXR_OK == tmprslt);
            if (!SUCCEEDED(tmprslt))
            {
                HX_ASSERT(SUCCEEDED(tmprslt));
                continue;
            }
          }
          switch (pTmpVal->getTimeType())
          {
            case SmilTimeMediaMarker:
            case SmilTimeWallclock:
            case SmilTimeEvent:
                lCurTimeInSameTimeSpace = lCurUsableTimeInGroupTime;
                break;

            case SmilTimeOffset:
            case SmilTimeClockValue:
            case SmilTimeSyncBase:
            default:
                lCurTimeInSameTimeSpace = lCurTimeInSyncBaseTime;
                break;
          }
          switch (pTmpVal->getTimeType())
          {
            case SmilTimeNone:
                break; // /Skip this time.

            case SmilTimeWallclock:
            case SmilTimeOffset:
            case SmilTimeClockValue:
            {
                LONG32 lEffectiveResolvedToTime = 0;
                tmprslt = pTmpVal->getEffectiveResolvedTime(
                      lEffectiveResolvedToTime);
#if defined(_DEBUG)     // /XXXEH- testing!: 
                LONG32 lTimeOffset = pTmpVal->getTimeOffset();
                HX_ASSERT(tmprslt==HXR_OK  &&
                      lTimeOffset == lEffectiveResolvedToTime);
#endif
// /XXXEH- TODO: add bool (& time val?) that says if (& when) this begin time
// was already used; if already used, don't use it again, and seeks and
// repeats must erase this bool (& time val); this is especially needed
// if we start treating all times as events, i.e., consistently:
                // /Only use times that are now or later:
                if (lEffectiveResolvedToTime >= lCurTimeInSameTimeSpace)
                {
                  if (NULL == pTimeValFoundSoFar  ||
                        lEffectiveResolvedToTime <
                        lResolvedToTimeOfTimeValFoundSoFar)
                  {
                      pTimeValFoundSoFar = pTmpVal;
                  }
                }
            }
            break;

            case SmilTimeMediaMarker:
            case SmilTimeSyncBase:
#if defined(ENABLE_SYNC_TO_PREV)
            case SmilTimeSyncToPrev:
#endif
            case SmilTimeEvent:
            {
// /XXXEH- TODO: finish this, and make sure m_lResolvedToTime and m_lWhenTimeWasResolved are being set:
                LONG32 lEffectiveResolvedToTime = 0;
                HX_RESULT tmprslt2 = pTmpVal->
                      getEffectiveResolvedTime(
                      lEffectiveResolvedToTime);
                HX_ASSERT(SUCCEEDED(tmprslt2));// /Should be resovled.
                if (SUCCEEDED(tmprslt2)  &&
                      lEffectiveResolvedToTime >= lCurTimeInSameTimeSpace)
                {
                  LONG32 lTimeOffset = pTmpVal->getTimeOffset();
                  if (lEffectiveResolvedToTime <
// /XXXEH- TODO: add +offset since this is an event-based time; need
// "timecmp(x,y)" function that compares two SmilTimeValues (taking into
// account that negative offsets shouldn't be compared if this is an event
// time) and returns -1 if x is greater, 0 if same, +1 if y is later:
// /XXXEH- TODO: argue the following with W3C SYMM Smil Boston Working
// Group: if we treat event-based times with negative offset as "now"
// w/clip-begin, then why not same for sync-base (and other) values whose
// times are in the past when they first become resolved (e.g., clipped by
// parent, or sync-arc to event-arc'd time)?  For example:
// <par>
//   <ref id="x" begin="foo.activate" />
//   <par begin="10s">
//     <!-- resolves 3s after it's intended value: -->
//     <ref begin="x.begin-3s"/>
                        lResolvedToTimeOfTimeValFoundSoFar)
                  {
                      pTimeValFoundSoFar = pTmpVal;
                  }
                  // /XXXEH- TODO: verify that this is how the
                  // SMIL Boston draft ends up dealing with the
                  // begin="x.focusInEvent-5s; x.focusInEvent-3s"
                  // issue:
                  // /Take the one with the earliest offset if
                  // the resolved time is the same, so
                  // "x.focusInEvent-5s" beats out "x.focusInEvent",
                  // and "x.focusInEvent" beats out "x.focusInEvent+2s"
                  // /XXXEH- I think restart might play into this
                  // so that x.focusInEvent-5s would be used over
                  // x.focusInEvent-3s if and only if (restart=="never"
                  // OR (restart!="never" AND this's dur <=2s)):
                  else if (lEffectiveResolvedToTime ==
                        lResolvedToTimeOfTimeValFoundSoFar  &&
                        lTimeOffset <
                        pTimeValFoundSoFar->getTimeOffset())
                  {
                      pTimeValFoundSoFar = pTmpVal;
                  }
                }
            }
            break;
          }
      }
      pWhichList->GetNext(lPos);
    } // /end while(lPos...).

    pValue = pTimeValFoundSoFar;

    if (NULL == pValue)
    {
      retval = HXR_FAILED;
    }

cleanup:
    return retval;
}

HX_RESULT
CSmilElement::resolveEventTimeValues(INT32 lCurTime,
                        const char* pEventName,
                        const char* pEventElementId,
                        SmilTimingListType listType,
                        REF(BOOL) bATimeWasResolved)
{
    HX_RESULT retval = HXR_OK;
    SmilTimeValue* pValue = NULL;
    CHXSimpleList* pWhichList = NULL;
    bATimeWasResolved = FALSE;
    LISTPOSITION lPos = NULL;

    if (NULL == pEventName  ||
          // /pEventElementId can be NULL but not non-NULL and empty:
          (pEventElementId  &&  '\0' == *pEventElementId) )
    {
      retval = HXR_FAILED;
      goto cleanup;
    }

    if (SmilBeginTimeList == listType) // /get begin time:
    {
      pWhichList = m_pBeginTimeList;
    }
    else if (SmilEndTimeList == listType)// /get end time:
    {
      pWhichList = m_pEndTimeList;
    }
    else
    {
      HX_ASSERT(SmilBeginTimeList == listType  ||
            SmilEndTimeList == listType);
    }
    if (NULL == pWhichList)
    {
      retval = HXR_FAILED;
      goto cleanup;
    }

    // /XXXEH- is there any reason we should start from tail instead?
    lPos = pWhichList->GetHeadPosition();

    if (NULL == lPos) // /list is empty.
    {
      retval = HXR_FAILED;
      goto cleanup;
    }

    while (lPos  &&  HXR_OK == retval)
    {
      pValue = (SmilTimeValue*)pWhichList->GetAt(lPos);

      // /If this begin or end time is a time event based on this event
      // from the element with pEventElementId, then we can (and do)
      // resolve it here:
      if(NULL!= pValue  &&  (SmilTimeEvent == pValue->m_type  ||
            (pValue->m_bTreatSyncArcAsEvent  &&
            pValue->isSyncBaseTimeVal()) ) )
      {
          // /Now, see if we've got an element that is waiting for this
          // event from the pEventElement:
          if (!pValue->getIdRef()  ||  !pValue->getEventName())
          {
            HX_ASSERT(pValue->getIdRef()  && pValue->getEventName());
            pWhichList->GetNext(lPos);
            continue;
          }
          if ((!pEventElementId  ||  strcmp(pValue->getIdRef(),
                pEventElementId) == 0)  &&
                strcmp(pValue->getEventName(), pEventName) == 0 )
          {
            BOOL bIsResolvedAlready = pValue->isTimeResolved();
            INT32 lProirResolvedToTime = (bIsResolvedAlready?
                  pValue->getResolvedToTimeWithoutOffset():-1);
            pValue->setIsTimeResolved(TRUE);
            pValue->setResolvedToTime(lCurTime);
            pValue->setWhenTimeWasResolved(lCurTime);
            // /[Optimization]: In case we get called twice with the
            // same event at the same resolved time, let's not force
            // a stop/start of the element:
            if (!bIsResolvedAlready  ||  lCurTime !=lProirResolvedToTime)
            {
                bATimeWasResolved = TRUE;
            }
          }
      }
      pWhichList->GetNext(lPos);
    } // /end while(lPos...).

cleanup:
    return retval;
}


// /This method goes through the m_pEndTimeList to see if there's an
// event-arc end time or other unresolved end time; this is important
// for use in the algorithm for determining whether or not something
// should restart (see SMIL 2.0 Spec Timing Module's getNextInterval()
// pseudocode):
BOOL
CSmilElement::hasUnresolvedEndTime()
{
    BOOL bHasUnresolvedEndTime = FALSE;

    SmilTimeValue* pValue = NULL;
    LISTPOSITION lPos = NULL;

    if (NULL == m_pEndTimeList)
    {
      goto cleanup;
    }

    // /XXXEH- is there any reason we should start from tail instead?
    lPos = m_pEndTimeList->GetHeadPosition();

    if (NULL == lPos) // /list is empty.
    {
      goto cleanup;
    }

    while (lPos  &&  !bHasUnresolvedEndTime)
    {
      pValue = (SmilTimeValue*)m_pEndTimeList->GetAt(lPos);

      if(NULL == pValue)
      {
          m_pEndTimeList->GetNext(lPos);
          continue;
      }
      if (SmilTimeEvent == pValue->m_type)
      {
          bHasUnresolvedEndTime = TRUE;
      }
      // /XXXEH- TODO: need to go through sync-arc element and see
      // if *it* has any unresolved time(s) (begin or end, depending
      // on this sync-arc time's position value) and, if not, then
      // DON'T claim bHasUnresolvedEndTime is TRUE:
      else if (pValue->m_bTreatSyncArcAsEvent  &&
            pValue->isSyncBaseTimeVal())
      {
          if (!pValue->isTimeResolved())
          {
            bHasUnresolvedEndTime = TRUE;
          }
      }
      m_pEndTimeList->GetNext(lPos);
    } // /end while(lPos...).

cleanup:
    return bHasUnresolvedEndTime;
}



// /This method returns the pure duration from the media's begin to its
// end, w/o any delay included:
ULONG32
CSmilElement::getPureDuration()
{
    HX_ASSERT(!m_bDurationIncludesDelayBeyondSyncbase  ||
          m_bCurBeginIsOffsetFromSyncBase);
    if (m_bDurationIncludesDelayBeyondSyncbase)
    {
      return (m_ulDuration - m_ulBeginOffsetFromSyncBase);
    }
    else
    {
      return m_ulDuration;
    }
/*
    return (m_bDurationIncludesDelayBeyondSyncbase?
          m_ulDuration-m_ulBeginOffsetFromSyncBase : m_ulDuration);
*/
}


HX_RESULT
CSmilElement::resolveSyncArcTimeValues(INT32 lResolvedToTime,
                        const char* pSyncBaseElementId,
                        SmilTimingListType listType,
                        REF(BOOL) bATimeWasResolved,
                        BOOL bMoveNewlyResolvedsToPendingTimesList,
                        CSmilParser* pParser)
{
    HX_RESULT retval = HXR_OK;
    SmilTimeValue* pValue = NULL;
    CHXSimpleList* pWhichList = NULL;
    bATimeWasResolved = FALSE;
    LISTPOSITION lPos = NULL;

    if (NULL == pSyncBaseElementId)
    {
      retval = HXR_FAILED;
      goto cleanup;
    }

    if (SmilBeginTimeList == listType) // /get begin time:
    {
      pWhichList = m_pBeginTimeList;
    }
    else if (SmilEndTimeList == listType)// /get end time:
    {
      pWhichList = m_pEndTimeList;
    }

    else
    {
      HX_ASSERT(SmilBeginTimeList == listType  ||
            SmilEndTimeList == listType);
    }
    if (NULL == pWhichList)
    {
      retval = HXR_FAILED;
      goto cleanup;
    }

    // /XXXEH- is there any reason we should start from tail instead?
    lPos = pWhichList->GetHeadPosition();

    if (NULL == lPos) // /list is empty.
    {
      retval = HXR_FAILED;
      goto cleanup;
    }

    while (lPos  &&  HXR_OK == retval)
    {
      pValue = (SmilTimeValue*)pWhichList->GetAt(lPos);

      // /If this begin or end time is a time event based on this event
      // from the element with pSyncBaseElementId, then we can (and do)
      // resolve it here:
      if(NULL!= pValue  &&  pValue->isSyncBaseTimeVal())
      {
          // /Now, see if we've got an element that is waiting for this
          // event from the pEventElement:
          if (!pValue->getIdRef())
          {
            HX_ASSERT(pValue->getIdRef());
            pWhichList->GetNext(lPos);
            continue;
          }
          if (strcmp(pValue->getIdRef(), pSyncBaseElementId) == 0)
          {
            // /Helps fix SMIL 2.0 Interop Timing #15.13:
            // if we're resuming a paused element and its duration thus
            // got extended, don't re-resolve (and re-insert) other
            // elements who are sync-arc'd to the *begin* time of this
            // resumed element:
            if (pValue->isTimeResolved()  &&  lResolvedToTime ==
                  pValue->getResolvedToTimeWithoutOffset()  &&
                  SmilBeginTimeList == listType)
            {
                bMoveNewlyResolvedsToPendingTimesList = FALSE;
            }

            pValue->setIsTimeResolved(TRUE);
            pValue->setResolvedToTime(lResolvedToTime);

            // /XXXEH- this shouldn't matter what we put here, but
            // we *should* fill this with the current time:
            pValue->setWhenTimeWasResolved(0);
            bATimeWasResolved = TRUE;

            if (pParser  &&  bMoveNewlyResolvedsToPendingTimesList)
            {
                if (SmilBeginTimeList == listType)
                {
                  if (pParser->EstablishBeginTimeList())
                  {
                      CHXSimpleList* pList =
                            pParser->GetPendingBeginTimeList();
                      // /Don't add if it's a duplicate:
                      if (!pParser->isDuplicateEntry(pList, pValue))
                      {
                        pList->AddTail(pValue);
                      }
                  }
                }
                else
                {
                  if (pParser->EstablishEndTimeList())
                  {
                      CHXSimpleList* pList =
                            pParser->GetPendingEndTimeList();
                      // /Don't add if it's a duplicate:
                      if (!pParser->isDuplicateEntry(pList, pValue))
                      {
                        pList->AddTail(pValue);
                      }
                  }
                }
            }
          }
      }
      pWhichList->GetNext(lPos);
    } // /end while(lPos...).

cleanup:
    return retval;
}

// /Returns HXR_FAILED if element or its parent don't exist.  The spec says
// that "always" is what is to be used for restart and restartDefault for the
// element if it has no parent or the parent has no restartDefault value:
HX_RESULT
CSmilElement::getParentRestartDefault()
{
    HX_RESULT ret = HXR_OK;
    if (NULL == m_pNode)
    {
      ret = HXR_FAILED;
    }
    else
    {
      SMILNode* pParent = m_pNode->m_pParent;
      if (!pParent  ||  !pParent->m_pElement)
      {
          ret = HXR_FAILED;
      }
      else
      {
          switch (pParent->m_pElement->m_restartDefaultBehavior)
          {
            case SmilRestartNever:
            case SmilRestartWhenNotActive:
            case SmilRestartAlways:
                // /Both restartDefault and restart behavior are assigned
                // to parent's restartDefault behavior:
                m_restartDefaultBehavior =
                      m_restartBehavior =
                      pParent->m_pElement->m_restartDefaultBehavior;
                break;

            case SmilRestartInherit:
                m_restartDefaultBehavior =
                      m_restartBehavior = SmilRestartAlways;
                break;

            default:
                HX_ASSERT(0);
                break;
          }
      }
    }

    return ret;
}

void CSmilElement::updateRemoveTime(UINT32 ulRemoveTime)
{
    m_ulRemoveTime = ulRemoveTime;
    if (m_pHandler)
    {
        m_pHandler->handleRemoveTimeUpdate(this, ulRemoveTime);
    }
}

void CSmilElement::handleXMMF(const char* pszID, const char* pszXMMFile, const char* pszSrc)
{
    if (m_pHandler)
    {
        m_pHandler->handleExternalMediaMarkerFile(this,
                                                  pszID,
                                                  pszXMMFile,
                                                  pszSrc);
    }
}

BOOL CSmilElement::hasEventBasedBeginTime()
{
    BOOL bRet = FALSE;

    if (m_pBeginTimeList)
    {
        LISTPOSITION pos = m_pBeginTimeList->GetHeadPosition();
        while (pos)
        {
            SmilTimeValue* pValue =
                (SmilTimeValue*) m_pBeginTimeList->GetNext(pos);
            if (pValue &&
                pValue->m_type == SmilTimeEvent)
            {
                bRet = TRUE;
                break;
            }
        }
    }
    // We also have to check our sync ancestor because
    // if our sync ancestor has an event based begin
    // time, then so do we. However, if we already
    // know we have an event based time, then we don't
    // need to check
    if (!bRet)
    {
        CSmilElement* pSyncAnc = getSyncAncestorElement();
        if (pSyncAnc)
        {
            bRet = pSyncAnc->hasEventBasedBeginTime();
        }
    }

    return bRet;
}

CSmilElement* CSmilElement::getSyncAncestorElement()
{
    CSmilElement* pRet = NULL;

    if (m_pNode)
    {
        BOOL bLegalMediaObjectChild = FALSE;
        if (m_pNode->m_tag == SMILArea         ||
            m_pNode->m_tag == SMILAnchor       ||
            m_pNode->m_tag == SMILAnimate      ||
            m_pNode->m_tag == SMILSet          ||
            m_pNode->m_tag == SMILAnimateColor ||
            m_pNode->m_tag == SMILAnimateMotion)
        {
            bLegalMediaObjectChild = TRUE;
        }
        SMILNode* pAnc = m_pNode->m_pParent;
        while (pAnc)
        {
            if (pAnc->m_tag == SMILPar  ||
                pAnc->m_tag == SMILExcl ||
                pAnc->m_tag == SMILSeq)
            {
                pRet = pAnc->m_pElement;
                break;
            }
            else if ((pAnc->m_tag == SMILRef        ||
                      pAnc->m_tag == SMILText       ||
                      pAnc->m_tag == SMILImg        ||
                      pAnc->m_tag == SMILAudio      ||
                      pAnc->m_tag == SMILVideo      ||
                      pAnc->m_tag == SMILAnimation  ||
                      pAnc->m_tag == SMILTextstream ||
                      pAnc->m_tag == SMILBrush      ||
                      pAnc->m_tag == SMILPrefetch) &&
                     bLegalMediaObjectChild)
            {
                pRet = pAnc->m_pElement;
                break;
            }
            pAnc = pAnc->m_pParent;
        }
    }

    return pRet;
}

BOOL CSmilElement::hasEventBasedEndTime()
{
    BOOL bRet = FALSE;

    if (m_pEndTimeList)
    {
        LISTPOSITION pos = m_pEndTimeList->GetHeadPosition();
        while (pos)
        {
            SmilTimeValue* pValue =
                (SmilTimeValue*) m_pEndTimeList->GetNext(pos);
            if (pValue &&
                pValue->m_type == SmilTimeEvent)
            {
                bRet = TRUE;
                break;
            }
        }
    }

    return bRet;
}

HX_RESULT   
CSmilElement::GetElementProperties(REF(IHXValues*) pProperties)
{
    HX_RESULT           rc = HXR_OK;
    BOOL          bDone = FALSE;
    ElementWithinTag    elementWithinTag = WithinUnknown;
    SMILNode*           pNode = m_pNode;

    HX_ASSERT(pNode);
    while (pNode->m_pParent && !bDone)
    { 
      switch (pNode->m_pParent->m_tag)
      {
      case SMILPar:
          if (elementWithinTag == WithinSeq)
          {
            elementWithinTag = WithinSeqInPar;
          }
          else
          {
            elementWithinTag = WithinPar;
          }
          bDone = TRUE;
          break;
      case SMILSeq:
          elementWithinTag = WithinSeq;
          break;
      default:
          break;
      }

      pNode = pNode->m_pParent;
    }

    pProperties = new CHXHeader();
    pProperties->AddRef();

    pProperties->SetPropertyULONG32("ElementWithinTag", elementWithinTag);
    pProperties->SetPropertyULONG32("Delay", m_ulDelay);

    if (m_bHasExplicitDur)
    {
      pProperties->SetPropertyULONG32("Duration", m_ulDuration);
    }

    return rc;
}


void
CSmilElement::checkElementFillBehavior()
{
    if (m_pTimelineElement)
    {
      m_pTimelineElement->checkElementFillBehavior();
    }
}



/*
 * CSmilRootLayout methods
 */
CSmilRootLayout::CSmilRootLayout(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_dWidth               = 0.0;
    m_eWidthType           = CSS2TypeAuto;
    m_dHeight              = 0.0;
    m_eWidthType           = CSS2TypeAuto;
    m_ulBackgroundColor    = 0;
    m_eBackgroundColorType = CSS2TypeTransparent;
    m_eResizeBehavior      = ResizeZoom;
    m_eContextWindow       = ContextWindowAuto;
}

CSmilRootLayout::~CSmilRootLayout()
{
}

HX_RESULT
CSmilRootLayout::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleRootLayout(this);
    }
    return rc;
}


/*
 * CSmilRegion methods
 */

CSmilRegion::CSmilRegion(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_Rect.m_dLeft         = 0.0;
    m_Rect.m_eLeftType     = CSS2TypeAuto;
    m_Rect.m_dTop          = 0.0;
    m_Rect.m_eTopType      = CSS2TypeAuto;
    m_Rect.m_dRight        = 0.0;
    m_Rect.m_eRightType    = CSS2TypeAuto;
    m_Rect.m_dBottom       = 0.0;
    m_Rect.m_eBottomType   = CSS2TypeAuto;
    m_Rect.m_dWidth        = 0.0;
    m_Rect.m_eWidthType    = CSS2TypeAuto;
    m_Rect.m_dHeight       = 0.0;
    m_Rect.m_eHeightType   = CSS2TypeAuto;
    m_lZIndex              = 0;
    m_eZIndexType          = CSS2TypeAuto;
    m_eFit                 = FitHidden;
    m_eShowBackground      = ShowBackgroundAlways;
    m_ulBackgroundColor    = 0xFF000000;
    m_eBackgroundColorType = CSS2TypeTransparent;
    m_dSoundLevel          = 100.0;
    m_bRegionNameSpecified = FALSE;
}

CSmilRegion::~CSmilRegion()
{
}

HX_RESULT
CSmilRegion::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleRegion(this);
    }
    return rc;
}

CSmilRegPoint::CSmilRegPoint(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_RegPoint.m_dLeft       = 0.0;
    m_RegPoint.m_eLeftType   = CSS2TypeAuto;
    m_RegPoint.m_dTop        = 0.0;
    m_RegPoint.m_eTopType    = CSS2TypeAuto;
    m_RegPoint.m_dRight      = 0.0;
    m_RegPoint.m_eRightType  = CSS2TypeAuto;
    m_RegPoint.m_dBottom     = 0.0;
    m_RegPoint.m_eBottomType = CSS2TypeAuto;
    m_RegPoint.m_eRegAlign   = RegAlignTopLeft;
}

CSmilRegPoint::~CSmilRegPoint()
{
}

HX_RESULT
CSmilRegPoint::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleRegPoint(this);
    }
    return rc;
}

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)

CSmilViewport::CSmilViewport(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_ulBackgroundColor    = 0;
    m_eBackgroundColorType = CSS2TypeTransparent;
    m_dWidth               = 0.0;
    m_eWidthType           = CSS2TypeAuto;
    m_dHeight              = 0.0;
    m_eHeightType          = CSS2TypeAuto;
    m_eOpen                = ViewportOpenOnStart;
    m_eClose               = ViewportCloseOnRequest;
    m_eResizeBehavior      = ResizeZoom;
    m_eContextWindow       = ContextWindowAuto;
}

CSmilViewport::~CSmilViewport()
{
}

HX_RESULT
CSmilViewport::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleViewport(this);
    }
    return rc;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */

/*
 * CSmilMeta methods
 */
CSmilMeta::CSmilMeta(SMILNode* pNode):
    CSmilElement(pNode)
{
}

CSmilMeta::~CSmilMeta()
{
}

HX_RESULT
CSmilMeta::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleMeta(this);
    }
    return rc;
}

/*
 * CSmilMetadata methods
 */
CSmilMetadata::CSmilMetadata(SMILNode* pNode):
    CSmilElement(pNode)
{
}

CSmilMetadata::~CSmilMetadata()
{
}

HX_RESULT
CSmilMetadata::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleMetadata(this);
    }
    return rc;
}

/*
 * CSmilRendererPreFetch methods
 */
CSmilRendererPreFetch::CSmilRendererPreFetch(SMILNode* pNode):
    CSmilElement(pNode)
{
}

CSmilRendererPreFetch::~CSmilRendererPreFetch()
{
}

HX_RESULT
CSmilRendererPreFetch::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleRendererPreFetch(this);
    }
    return rc;
}

/*
 * CSmilEndLayout methods
 */
CSmilEndLayout::CSmilEndLayout():
    CSmilElement(NULL)
{
}

CSmilEndLayout::~CSmilEndLayout()
{
}


HX_RESULT
CSmilEndLayout::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleEndLayout(this);
    }
    return rc;
}

/*
 * CSmilMetaValues methods
 */
CSmilMetaValues::CSmilMetaValues():
    CSmilElement(NULL),
    m_pValues(0)
{
}

CSmilMetaValues::~CSmilMetaValues()
{
    HX_RELEASE(m_pValues);
}


/*
 * CSmilCustomTest methods:
 */
CSmilCustomTest::CSmilCustomTest(SMILNode* pNode) :
    CSmilElement(pNode)
    , m_bDefaultState(FALSE)
    , m_bOverrideVisible(FALSE)
{
}

CSmilCustomTest::~CSmilCustomTest()
{
}


/*
 * CSmilSource methods
 */

CSmilSource::CSmilSource(SMILNode* pNode):
    CSmilElement(pNode)
{
    m_ulColor                       = 0;
    m_eColorType                    = CSS2TypeTransparent;
    m_Rect.m_dLeft                  = 0.0;
    m_Rect.m_eLeftType              = CSS2TypeAuto;
    m_Rect.m_dTop                   = 0.0;
    m_Rect.m_eTopType               = CSS2TypeAuto;
    m_Rect.m_dRight                 = 0.0;
    m_Rect.m_eRightType             = CSS2TypeAuto;
    m_Rect.m_dBottom                = 0.0;
    m_Rect.m_eBottomType            = CSS2TypeAuto;
    m_Rect.m_dWidth                 = 0.0;
    m_Rect.m_eWidthType             = CSS2TypeAuto;
    m_Rect.m_dHeight                = 0.0;
    m_Rect.m_eHeightType            = CSS2TypeAuto;
    m_lZIndex                       = 0;
    m_eZIndexType                   = CSS2TypeAuto;
    m_eFit                          = FitHidden;
    m_bFitSpecified                 = FALSE;
    m_ulBackgroundColor             = 0;
    m_eBackgroundColorType          = CSS2TypeTransparent;
    m_bRegPointIsPredef             = TRUE;
    m_ePredefRegPoint               = RegAlignTopLeft;
    m_eRegAlign                     = RegAlignTopLeft;
    m_bRegAlignSpecified            = FALSE;
    m_bBackgroundOpacitySpecified   = FALSE;
    m_ulBackgroundOpacity           = 255;
    m_bMediaOpacitySpecified        = FALSE;
    m_ulMediaOpacity                = 255;
    m_bChromaKeySpecified           = FALSE;
    m_ulChromaKey                   = 0;
    m_ulChromaKeyTolerance          = 0;
    m_ulChromaKeyOpacity            = 0;
    m_bAudioDeviceReflushHintNeeded = FALSE;
    m_eHandledBy                    = HandledByAuto;
    m_bBackgroundColorSpecified     = FALSE;
    m_ulLinkTargetDestnLevel_pct    = 100;
    m_ulLinkTargetSourceLevel_pct   = 100;
    m_ulPrefetchAmount              = 0;
    m_typeOfPrefetchAmount          = PrefetchUnknown;

}

CSmilSource::~CSmilSource()
{
}

HX_RESULT
CSmilSource::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleSource(this);
    }
    return rc;
}

void
CSmilSource::setRange(const char* pRange)
{
    // parse smpte/npt ranges:
    //    smpte:12:03:14.21-12:15:26.03
    //        npt:3:02.5-4:38.6
    //

    char* pPtr = (char *)strstr(pRange, "npt:");
    if(pPtr)
    {
      pPtr += 4;  // point to beginning of range
      char* pHyphen = (char *)strchr(pRange, '-');    // find '-'
      if(pHyphen > pPtr)
      {
          NPTime beginTime((const char*)CHXString(pPtr, pHyphen-pPtr));
          m_ulAuthoredClipBegin = (UINT32)beginTime;
          m_ulClipBegin = m_ulAuthoredClipBegin;
          if(strlen(pHyphen+1) > 0)
          {
            NPTime endTime((const char*)CHXString(pHyphen+1));
            m_ulClipEnd = (UINT32)endTime;
          }
      }
      else if(pHyphen)
      {
          NPTime endTime((const char*)CHXString(pHyphen+1));
          m_ulClipEnd = (UINT32)endTime;
      }
    }
    else
    {
      pPtr = (char *)strstr(pRange, "smpte:");
      if(pPtr)
      {
          pPtr += 6;    // point to beginning of range
          char* pHyphen = (char *)strchr(pRange, '-');      // find '-'
          if(pHyphen > pPtr)
          {
            SMPTETimeCode beginTime = 
                ((const char*)CHXString(pPtr, pHyphen-pPtr));
            m_ulAuthoredClipBegin = (UINT32)beginTime;
            m_ulClipBegin = m_ulAuthoredClipBegin;
            if(strlen(pHyphen+1) > 0)
            {
                SMPTETimeCode endTime =
                  ((const char*)CHXString(pPtr, pHyphen-pPtr));
                m_ulClipEnd = (UINT32)endTime;
            }
          }
          else if(pHyphen)
          {
            SMPTETimeCode endTime((const char*)CHXString(pHyphen+1));
            m_ulClipEnd = (UINT32)endTime;
          }
      }
    }
}

/*
 * CSmilSourceUpdate methods
 */

CSmilSourceUpdate::CSmilSourceUpdate():
    CSmilElement(NULL)
{
    m_updateTag = UpdateUnknown;
    m_ulUpdatedDuration = 0;
    m_bDurationIsPureOfDelay = TRUE;
    m_ulUpdatedDelay = 0;
}

CSmilSourceUpdate::~CSmilSourceUpdate()
{
}

HX_RESULT
CSmilSourceUpdate::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleSourceUpdate(this);
    }
    return rc;
}

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

// CSmilAnimateElement methods
CSmilAnimateElement::CSmilAnimateElement(SMILNode* pNode)
    : CSmilElement(pNode)
{
    // Set defaults
    m_ulSimpleDuration          = ((UINT32) -1);
    m_ulActiveDuration          = ((UINT32) -1);
    m_ulADNoSpeed               = ((UINT32) -1);
    m_pTargetElementID          = NULL;
    m_eTargetElementTag         = SMILUnknown;
    m_ulNumValues               = 0;
    m_ppValue                   = NULL;
    m_ucAttributeName           = kAttrNameUnknown;
    m_ucCalcMode                = kCalcModeLinear;
    m_ucAccumulate              = kAccumulateNone;
    m_ucAdditive                = kAdditiveReplace;
    m_ucAnimationType           = kAnimTypeValues;
    m_dRepeatCount              = 1.0;
    m_ulRepeatDur               = ((UINT32) -1);
    m_bRepeatDurIsIndefinite    = FALSE;
    m_dAccelerate               = 0.0;
    m_dDecelerate               = 0.0;
    m_bAutoReverse              = FALSE;
    m_dSpeed                    = 1.0;
    m_bIndefiniteSimpleDuration = FALSE;
    m_bIndefiniteActiveDuration = FALSE;
    m_bCancelAnimation          = FALSE;
#if defined(XXXMEH_SPLINE_ANIMATION)
    m_ulNumKeyTimes             = 0;
    m_pdKeyTime                 = NULL;
    m_ulNumKeySplines           = 0;
    m_pKeySpline                = NULL;
    m_ulNumPathCmds             = 0;
    m_ppPathCmd                 = NULL;
#endif
}

CSmilAnimateElement::~CSmilAnimateElement()
{
    HX_DELETE(m_pTargetElementID);
    if (m_ppValue)
    {
        for (UINT32 i = 0; i < m_ulNumValues; i++)
        {
            HX_DELETE(m_ppValue[i]);
        }
        HX_VECTOR_DELETE(m_ppValue);
    }
#if defined(XXXMEH_SPLINE_ANIMATION)
    HX_VECTOR_DELETE(m_pdKeyTime);
    HX_VECTOR_DELETE(m_pKeySpline);
    if (m_ppPathCmd && m_ulNumPathCmds)
    {
        for (UINT32 i = 0; i < m_ulNumPathCmds; i++)
        {
            HX_DELETE(m_ppPathCmd[i]);
        }
    }
    HX_VECTOR_DELETE(m_ppPathCmd);
#endif
}

HX_RESULT CSmilAnimateElement::handleElement()
{
    HX_RESULT retVal = HXR_OK;

    if(m_pHandler)
    {
      retVal = m_pHandler->handleAnimate(this);
    }

    return retVal;
}

HX_RESULT CSmilAnimateElement::getCurrentScheduledStopTime(REF(ULONG32) ulActualStopTime)
{
    HX_RESULT ret = HXR_OK;

    if (m_ulDelay != ((UINT32) -1))
    {
        if (m_bIndefiniteActiveDuration)
        {
            ulActualStopTime = SMILTIME_INFINITY;
        }
        else
        {
            ulActualStopTime = m_ulDelay + m_ulActiveDuration;
        }
    }
    else
    {
        ret = HXR_NOT_INITIALIZED;
    }

    return ret;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */

CSmilParamElement::CSmilParamElement(SMILNode* pNode)
    : CSmilElement(pNode)
{
    m_pName     = NULL;
    m_pValue    = NULL;
    m_eDelivery = DeliveryClient;
}

CSmilParamElement::~CSmilParamElement()
{
    HX_RELEASE(m_pName);
    HX_RELEASE(m_pValue);
}

/*
 * CSmilParElement methods
 */

CSmilParElement::CSmilParElement(SMILNode* pNode):
    CSmilElement(pNode)
{
}

CSmilParElement::~CSmilParElement()
{
}

/*
 * CSmilSeqElement methods
 */
CSmilSeqElement::CSmilSeqElement(SMILNode*  pNode):
    CSmilElement(pNode)
{
}

CSmilSeqElement::~CSmilSeqElement()
{
}

/*
 * CSmilExclElement methods
 */

CSmilExclElement::CSmilExclElement(SMILNode* pNode):
    CSmilElement(pNode)
    , m_pPauseStack(NULL)
{
}

CSmilExclElement::~CSmilExclElement()
{
    HX_DELETE(m_pPauseStack);
}



/*
 * CSmilPriorityClassElement methods
 */

CSmilPriorityClassElement::CSmilPriorityClassElement(SMILNode* pNode):
      CSmilElement(pNode)
      , m_pauseDisplay(SMILPriorityClassPauseDisplayShow)
      , m_peers(SMILPriorityClassStop)
      , m_higher(SMILPriorityClassPause)
      , m_lower(SMILPriorityClassDefer)
{
}

CSmilPriorityClassElement::~CSmilPriorityClassElement()
{
}



/*
 * CSmilAAnchor methods
 */

CSmilAAnchorElement::CSmilAAnchorElement(SMILNode* pNode):
    CSmilElement(pNode),
    m_show("replace")
    , m_ulSourceLevel_pct(100)
    , m_ulDestinationLevel_pct(100)
    // /XXXEH- TODO: make sure that, if show="new", this changes to play:
    , m_sourcePlaystate(SMILLinkPlaystatePause)
    , m_destinationPlaystate(SMILLinkPlaystatePlay)
    , m_bExternal(FALSE)
    , m_actuate("onRequest")
    , m_accesskey("") // /(an ISO10646 character.  Default unspecified)
    , m_ulTabindex ((UINT32)-1)
    , m_target("")
    , m_bTargetIsARegion(FALSE)
    , m_bTimeValueSet(FALSE)
    , m_lAnchorBeginOffset(0)
    , m_bAnchorBeginOffsetSet(FALSE)
    , m_lAnchorEndOffset(0)
    , m_bAnchorEndOffsetSet(FALSE)
    , m_ulAnchorDuration((UINT32)-1)
    , m_ulAnchorMinActiveDur(0)
    , m_ulAnchorMaxActiveDur((UINT32)-1)
{
}

CSmilAAnchorElement::~CSmilAAnchorElement()
{
}

BOOL
CSmilAAnchorElement::isCurrentLink(UINT32 ulTimeOffset,
                           UINT32 ulXOffset, UINT32 ulYOffset,
                           HXxRect regionRect,
                           // /These 3 are ignored for <a> links:
                           BOOL bResizeBehaviorIsZoom,
                           double dZoomX, double dZoomY)
{
    BOOL rc = TRUE;
    ULONG32 ulBeginRelativeToSyncBase = (UINT32)-1;

    // coords only - check for out of bounds region
    UINT32 ulRegionWidth = regionRect.right - regionRect.left;
    UINT32 ulRegionHeight = regionRect.bottom - regionRect.top;

    if(ulXOffset > ulRegionWidth ||
      ulYOffset > ulRegionHeight)
    {
      if (ANCHOR_POS_DONTCARE != ulXOffset  &&
            ANCHOR_POS_DONTCARE != ulYOffset)
      {
          rc = FALSE;
          goto exit;
      }
    }

    if(m_href.GetLength() == 0)
    {
      rc = FALSE;
    }
    // /In SMIL 2.0, "a" links can also be timed, so check time offset:
    else if (!isLinkActiveAtTime(ulTimeOffset, ulBeginRelativeToSyncBase))
    {
      rc = FALSE;
    }

exit:
    return rc;
}


BOOL
CSmilAAnchorElement::isLinkActiveAtTime(UINT32 ulTimeOffset,
                              REF(ULONG32)rulAnchorRelativeStartTime)
{
    BOOL bIsActiveAtTimeOffset = TRUE;
    rulAnchorRelativeStartTime = (UINT32)-1;
    if(m_bTimeValueSet)
    {
      bIsActiveAtTimeOffset = FALSE;

      BOOL bValidDelay = m_ulDelay != (UINT32)-1;

      if(m_bAnchorBeginOffsetSet  ||  bValidDelay)
      {
          if((m_bAnchorBeginOffsetSet  &&
                (INT32)ulTimeOffset >= m_lAnchorBeginOffset)  ||
                (bValidDelay  &&  ulTimeOffset >= m_ulDelay) ) 
          {
            // /For PR 67170 fix, caller needs anchor's begin time to tell
            // which of multiple, active onLoad anchors to use after a seek:
            if (m_bAnchorBeginOffsetSet  &&
                (INT32)ulTimeOffset >= m_lAnchorBeginOffset)
            {
                rulAnchorRelativeStartTime = m_lAnchorBeginOffset;
            }
            else
            {
                rulAnchorRelativeStartTime = m_ulDelay;
            }

            LONG32 lAnchorEndTime = -1;
            if (m_bAnchorEndOffsetSet)
            {
                lAnchorEndTime = m_lAnchorEndOffset;
            }
            else if ((UINT32)-1 != m_ulAnchorDuration)
            {
                lAnchorEndTime = (INT32)m_ulAnchorDuration;
                if (m_bAnchorBeginOffsetSet)
                {
                  lAnchorEndTime += m_lAnchorBeginOffset;
                }
            }

            if(-1 != lAnchorEndTime)
            {
                if((INT32)ulTimeOffset <= lAnchorEndTime)
                {
                  bIsActiveAtTimeOffset = TRUE;
                }
            }
            else
            {
                bIsActiveAtTimeOffset = TRUE;
            }
          }
      }
      else if(m_bAnchorEndOffsetSet)
      {
          if((INT32)ulTimeOffset <= m_lAnchorEndOffset)
          {
            bIsActiveAtTimeOffset = TRUE;
          }
      }
    }
    return bIsActiveAtTimeOffset;
}


BOOL
CSmilAnchorElement::isLinkActiveAtTime(UINT32 ulTimeOffset,
                               REF(ULONG32)rulAnchorRelativeStartTime)
{
    BOOL bIsActiveAtTimeOffset = TRUE;
    if(m_bTimeValueSet)
    {
      bIsActiveAtTimeOffset = FALSE;

      //[SMIL 1.0 compliance] the following variable and its use, below,
      // helps fix PR 26471.  We may have had only our delay set:
      BOOL bValidDelay = m_ulDelay != (UINT32)-1;

      if(m_bBeginOffsetSet  ||  bValidDelay)
      {
          // /First, we need to make sure we're in the same time-coordinate
          // system as our syncbase (parent):
          ULONG32 ulTimeOffsetFromSyncBase = m_ulDelay;
          if (m_bCurBeginIsOffsetFromSyncBase)
          {
            ulTimeOffsetFromSyncBase =
                  m_ulBeginOffsetFromSyncBase;
          }

          if ((m_bBeginOffsetSet  &&
            (INT32)ulTimeOffset >= m_lBeginOffset)  ||
            (bValidDelay  &&  ulTimeOffset >= ulTimeOffsetFromSyncBase) )
          {
            // /For PR 67170 fix, caller needs anchor's begin time to tell
            // which of multiple, active onLoad anchors to use after a seek:
            if (m_bBeginOffsetSet  &&
                (INT32)ulTimeOffset >= m_lBeginOffset)
            {
                rulAnchorRelativeStartTime = m_lBeginOffset;
            }
            else
            {
                rulAnchorRelativeStartTime = ulTimeOffsetFromSyncBase;
            }

            LONG32 lAnchorEndTime = -1;
            if (m_bEndOffsetSet)
            {
                lAnchorEndTime = m_lEndOffset;
            }
            else if ((UINT32)-1 != m_ulDuration)
            {
                lAnchorEndTime = (INT32)m_ulDuration;
                if (m_bBeginOffsetSet)
                {
                  lAnchorEndTime += m_lBeginOffset;
                }
            }

            if(-1 != lAnchorEndTime)
            {
                if((INT32)ulTimeOffset <= lAnchorEndTime)
                {
                  bIsActiveAtTimeOffset = TRUE;
                }
            }
            else
            {
                bIsActiveAtTimeOffset = TRUE;
            }
          }
      }
      else if(m_bEndOffsetSet)
      {
          if((INT32)ulTimeOffset <= m_lEndOffset)
          {
            bIsActiveAtTimeOffset = TRUE;
          }
      }
    }
    return bIsActiveAtTimeOffset;
}


void
CSmilAAnchorElement::rescale(double dXScale, double dYScale,
                       BOOL bResetOriginalCoords)
{
    // nothing to do
}


/*
 * CSmilAnchor methods
 */

CSmilAnchorElement::CSmilAnchorElement(SMILNode* pNode):
    CSmilAAnchorElement(pNode),
    m_zIndex(0),
    m_bCoordsSet(FALSE),
    m_ulLeftX(0),
    m_ulOriginalLeftX(0),
    m_bLeftXIsPercent(FALSE),
    m_ulTopY(0),
    m_ulOriginalTopY(0),
    m_bTopYIsPercent(FALSE),
    m_ulRightX((UINT32)-1),
    m_ulOriginalRightX((UINT32)-1),
    m_bRightXIsPercent(FALSE),
    m_ulBottomY((UINT32)-1),
    m_ulOriginalBottomY((UINT32)-1),
    m_bBottomYIsPercent(FALSE),
    m_ulRadius((UINT32)-1),
    m_ulOriginalRadius((UINT32)-1),
    m_bRadiusIsPercent(FALSE)
    , m_pVertexArray(NULL)
    , m_pOriginalVertexArray(NULL)
    , m_uiNumPoints(0)
{
}

CSmilAnchorElement::~CSmilAnchorElement()
{
    deleteVertexArrays();
}

void CSmilAnchorElement::deleteVertexArrays()
{
    if (m_pVertexArray)
    {
      delete [] m_pVertexArray;
      m_pVertexArray = NULL;
    }
    if (m_pOriginalVertexArray)
    {
      delete [] m_pOriginalVertexArray;
      m_pOriginalVertexArray = NULL;
    }
}

void
CSmilAnchorElement::rescale(double dXScale, double dYScale,
                       BOOL bResetOriginalCoords)
{
    if(bResetOriginalCoords)
    {
      m_ulLeftX = m_ulOriginalLeftX;
      m_ulRightX = m_ulOriginalRightX;
      m_ulTopY = m_ulOriginalTopY;
      m_ulBottomY = m_ulOriginalBottomY;
      m_ulRadius = m_ulOriginalRadius;

      if (m_pVertexArray)// /is poly shape:
      {
          for (int i=0; i < m_uiNumPoints; i++)
          {
            m_pVertexArray[i].m_lX = m_pOriginalVertexArray[i].m_lX;
            m_pVertexArray[i].m_lY = m_pOriginalVertexArray[i].m_lY;
          }
      }
    }
    else if (!m_pVertexArray)
    {
      if(!m_bLeftXIsPercent)
      {
          m_ulLeftX = (UINT32)(dXScale * (double)m_ulLeftX);
      }
      if(!m_bRightXIsPercent)
      {
          m_ulRightX = (UINT32)(dXScale * (double)m_ulRightX);
      }
      if(!m_bTopYIsPercent)
      {
          m_ulTopY = (UINT32)(dYScale * (double)m_ulTopY);
      }
      if(!m_bBottomYIsPercent)
      {
          m_ulBottomY = (UINT32)(dYScale * (double)m_ulBottomY);
      }
      if(!m_bRadiusIsPercent)
      {
          // /XXXEH- TODO: don't just average x&y scaling, but
          // make an elipse by keeping an x-radius and a y-radius val
          // and then use a formula for an elipse in the hit-test code
          // in isCurrentLink():
          m_ulRadius = (UINT32)(((dXScale+dYScale)/2.0) *
                (double)m_ulRadius);
      }
    }
    else // /is "poly" (polygon) shape:
    {
      for (int i=0; i < m_uiNumPoints; i++)
      {
          m_pVertexArray[i].m_lX =
                (LONG32)((double)((float)m_pVertexArray[i].m_lX) * dXScale);
          m_pVertexArray[i].m_lY =
                (LONG32)((double)((float)m_pVertexArray[i].m_lY) * dYScale);
      }
    }
}

BOOL
CSmilAnchorElement::isCurrentLink(UINT32 ulTimeOffset,
                           UINT32 ulXOffset, UINT32 ulYOffset,
                           HXxRect regionRect,
                           BOOL bResizeBehaviorIsZoom,
                           double dZoomX, double dZoomY)
{
    BOOL bRetval = TRUE;

    UINT32 ulRegionWidth = regionRect.right - regionRect.left;
    UINT32 ulRegionHeight = regionRect.bottom - regionRect.top;

    if(m_href.GetLength() == 0)
    {
      return FALSE;
    }

    ULONG32 ulBeginRelativeToSyncBase = (UINT32)-1;
    // first check time offset:
    if (!isLinkActiveAtTime(ulTimeOffset, ulBeginRelativeToSyncBase))
    {
      return FALSE;
    }

    if(m_bCoordsSet  &&  (!m_shape.GetLength()  ||  m_shape=="rect"))
    {
      UINT32 ulLeftX = m_ulLeftX;
      UINT32 ulRightX = m_ulRightX;
      UINT32 ulTopY = m_ulTopY;
      UINT32 ulBottomY = m_ulBottomY;

      // /Doing this resizing of absolute (not percent) coordinate values
      // fixes PR 77231; the hotspot should scale along with the media
      // when the player is resized:
      // (NOTE: This only fixes "rect" version of PR 77231):
      if (bResizeBehaviorIsZoom  &&  dZoomX != 1.0  && dZoomY != 1.0)
      {
          if (!m_bLeftXIsPercent)
          {
            ulLeftX *= dZoomX;
          }
          if (!m_bRightXIsPercent)
          {
            ulRightX *= dZoomX;
          }
          if (!m_bTopYIsPercent)
          {
            ulTopY *= dZoomX;
          }
          if (!m_bBottomYIsPercent)
          {
            ulBottomY *= dZoomX;
          }
      }

      // normalize to pixel coords
      if(m_bLeftXIsPercent)
      {
          ulLeftX = (UINT32)(((double)m_ulLeftX/100.0) * ulRegionWidth);
      }
      if(m_bRightXIsPercent)
      {
          ulRightX = (UINT32)(((double)m_ulRightX/100.0) * ulRegionWidth);
      }
      if(m_bTopYIsPercent)
      {
          ulTopY = (UINT32)(((double)m_ulTopY/100.0) * ulRegionHeight);
      }
      if(m_bBottomYIsPercent)
      {
          ulBottomY = (UINT32)(((double)m_ulBottomY/100.0) * ulRegionHeight);
      }

      if(ulXOffset >= ulLeftX &&
         ulXOffset <= ulRightX &&
         ulYOffset >= ulTopY &&
         ulYOffset <= ulBottomY)
      {
          return TRUE;
      }
      else
      {
          if (ANCHOR_POS_DONTCARE != ulXOffset  &&
                ANCHOR_POS_DONTCARE != ulYOffset)
          {
            return FALSE;
          }
          return TRUE;
      }
    }
    else if(m_bCoordsSet  &&  (m_shape=="circle"))
    {
      UINT32 ulCenterX = m_ulLeftX;
      UINT32 ulCenterY = m_ulTopY;
      UINT32 ulRadius = m_ulRadius;

      // /Doing this resizing of absolute (not percent) coordinate values
      // fixes "circle" version of PR 77231; the hotspot should scale along
      // with the media when the player is resized:
      if (bResizeBehaviorIsZoom  &&  dZoomX != 1.0  && dZoomY != 1.0)
      {
          if (!m_bLeftXIsPercent)
          {
            ulCenterX *= dZoomX;
          }
          if (!m_bTopYIsPercent)
          {
            ulCenterY *= dZoomX;
          }
          if (!m_bRadiusIsPercent)
          {
            ulRadius *= dZoomY;
          }
      }

      // normalize to pixel coords
      if(m_bLeftXIsPercent)
      {
          ulCenterX = (UINT32)(((double)ulCenterX/100.0) * ulRegionWidth);
      }
      if(m_bTopYIsPercent)
      {
          ulCenterY = (UINT32)(((double)ulCenterY/100.0) * ulRegionHeight);
      }
      if(m_bRadiusIsPercent)
      {
          ulRadius = (UINT32)(((double)m_ulRadius/100.0) * ulRegionHeight);
      }

      double dXDistanceToCenter = double( (ulXOffset>ulCenterX)?
            ulXOffset-ulCenterX : ulCenterX-ulXOffset);
      double dYDistanceToCenter = double( (ulYOffset>ulCenterY)?
            ulYOffset-ulCenterY : ulCenterY-ulYOffset);
      // /Compare squared distances rather than doing expensive sqare root:
      double dDistanceToCenterSquared =
            (dXDistanceToCenter*dXDistanceToCenter) +
            (dYDistanceToCenter*dYDistanceToCenter);
      double dRadiusSquared = double(ulRadius*ulRadius);
      // /It's within the circle if it's within the radius:
      if (dDistanceToCenterSquared <= dRadiusSquared)
      {
          return TRUE;
      }
      else
      {
          if (ANCHOR_POS_DONTCARE != ulXOffset  &&
                ANCHOR_POS_DONTCARE != ulYOffset)
          {
            return FALSE;
          }
          return TRUE;
      }
    }
    else if (m_bCoordsSet  &&  (m_shape=="poly"))
    {
      bRetval = isPointInPolygon(ulXOffset, ulYOffset, regionRect,
            bResizeBehaviorIsZoom? dZoomX : 1.0,
            bResizeBehaviorIsZoom? dZoomY : 1.0);
    }
    else if(ulXOffset > ulRegionWidth ||
          ulYOffset > ulRegionHeight)         
    // assume time OK, coords OK if within bounds of region
    {
      if (ANCHOR_POS_DONTCARE != ulXOffset  &&
            ANCHOR_POS_DONTCARE != ulYOffset)
      {
          return FALSE;
      }
    }

    return bRetval;
}


BOOL
CSmilAnchorElement::isPointInPolygon(INT32 lXOffset, INT32 lYOffset,
                             HXxRect regionRect,
                             double dZoomX, double dZoomY)
{
    INT32 xnew,ynew;
    INT32 xold,yold;
    INT32 x1,y1;
    INT32 x2,y2;
    BOOL    bIsInside=FALSE;

    UINT32 ulRegionWidth = regionRect.right - regionRect.left;
    UINT32 ulRegionHeight = regionRect.bottom - regionRect.top;

    // We must have at least 3 points to have a polygon
    if (m_uiNumPoints >= 3)
    {
        xold = m_pVertexArray[m_uiNumPoints-1].m_lX;
        yold = m_pVertexArray[m_uiNumPoints-1].m_lY;

      // /Convert percentages to coords:
      if(m_pVertexArray[m_uiNumPoints-1].m_bXIsPercent)
      {
          xold = (UINT32)(((double)xold/100.0) * ulRegionWidth);
      }
      // /NOTE: to fix PR 77231 (poly version) it would have been nice to
      // just apply 1/dZoomX and 1/dZoomY to lXOffset and lYOffset but we
      // can't because some vertices could be percentages while others not,
      // so we're stuck multiplying each non-percent value, one at a time:
      else if (dZoomX != 1.0)
      {
          xold *= dZoomX; // /Helps fix poly version of PR 77231.
      }
      if(m_pVertexArray[m_uiNumPoints-1].m_bYIsPercent)
      {
          yold = (UINT32)(((double)yold/100.0) * ulRegionHeight);
      }
      else if (dZoomY != 1.0)
      {
          yold *= dZoomY; // /Helps fix poly version of PR 77231.
      }

        for (UINT16 i=0; i < m_uiNumPoints; i++)
        {
            xnew = m_pVertexArray[i].m_lX;
            ynew = m_pVertexArray[i].m_lY;

          // /Convert percentages to coords:
          if(m_pVertexArray[i].m_bXIsPercent)
          {
            xnew = (UINT32)(((double)xnew/100.0) * ulRegionWidth);
          }
          else if (dZoomX != 1.0)
          {
            xnew *= dZoomX; // /Helps fix poly version of PR 77231.
          }
          if(m_pVertexArray[i].m_bYIsPercent)
          {
            ynew = (UINT32)(((double)ynew/100.0) * ulRegionHeight);
          }
          else if (dZoomY != 1.0)
          {
            ynew *= dZoomY; // /Helps fix poly version of PR 77231.
          }

            if (xnew > xold)
            {
                x1 = xold;
                x2 = xnew;
                y1 = yold;
                y2 = ynew;
            }
            else
            {
                x1 = xnew;
                x2 = xold;
                y1 = ynew;
                y2 = yold;
            }

            if ((xnew < lXOffset) == (lXOffset <= xold) &&
                    ((LONG32)lYOffset - (LONG32)y1) * (LONG32)(x2-x1) < 
                    ((LONG32)y2 - (LONG32)y1) * (LONG32)(lXOffset - x1))
            {
                bIsInside = !bIsInside;
            }

            xold = xnew;
            yold = ynew;
        }
    }

    return bIsInside;
}


HX_RESULT
CSmilAnchorElement::convertRawPolyData(const char* pCoords)
{
    HX_RESULT   retval = HXR_OK;

    const char*   pchData = pCoords;
    UINT16  uiNumValues=0;
    UINT16  uiLength=0;
    char*   szBuffer=NULL;
    UINT16  uiX=0;
    UINT16  uiY=0;

    if (!pCoords  ||  strlen(pCoords) < 3)
    {
      retval = HXR_INVALID_PARAMETER;
    }
    else
    {
      UINT16 uiNumCommas = 0;
      // /First, get number of points:
      while (*pchData)
      {
          if (',' == *pchData)
          {
            uiNumCommas++;
          }
          pchData++;
      };

      // /There *must* be an odd # of commas in order to have x,y pairs:
      if (0 == (uiNumCommas % 2))
      {
          retval = HXR_INVALID_PARAMETER;
      }
      // /Can't have poly with fewer than 3 pts.  It's not an error, but
      // it's useless so we ignore it:
      else if (uiNumCommas < 5)
      {
          retval = HXR_OK;
      }
      else
      {
          deleteVertexArrays();

          // /Get polygon values: (x1,y1,x2,y2,...xn,yn)
          m_uiNumPoints = (uiNumCommas+1) / 2;
          m_pVertexArray = new CSmilAnchorVertex[m_uiNumPoints];
          m_pOriginalVertexArray = new CSmilAnchorVertex[m_uiNumPoints];

          if (!m_pVertexArray  ||  !m_pOriginalVertexArray)
          {
            retval = HXR_OUTOFMEMORY;
          }
          else
          {
            retval = HXR_OK;
            int i = 0;
            char* pCoordCopy = new_string(pCoords);
            char* pTok = strtok(pCoordCopy, ",");
            for(i=0; i<m_uiNumPoints,pTok; ++i)
            {
                char* pEndPtr = 0;
                // /First, get x coord:
                double dVal = strtod(pTok, &pEndPtr);
                m_pOriginalVertexArray[i].m_lX = m_pVertexArray[i].m_lX =
                      (INT32)((float)dVal);
                m_pOriginalVertexArray[i].m_bXIsPercent =
                      m_pVertexArray[i].m_bXIsPercent =
                      (*pEndPtr == '%') ? TRUE: FALSE;

                pTok = strtok(NULL, ",");

                // /Next, get y coord:
                dVal = strtod(pTok, &pEndPtr);
                m_pOriginalVertexArray[i].m_lY = m_pVertexArray[i].m_lY =
                      (INT32)((float)dVal);
                m_pOriginalVertexArray[i].m_bYIsPercent =
                      m_pVertexArray[i].m_bYIsPercent =
                      (*pEndPtr == '%') ? TRUE: FALSE;

                pTok = strtok(NULL, ",");
            } // end for(i...
            delete [] pCoordCopy;
          }
      }
    }

    return retval;
}





/*
 * CSmilAddGroup methods
 */

CSmilAddGroup::CSmilAddGroup():
    CSmilElement(NULL),
    m_nGroup(0),
    m_pValues(NULL),
    m_nTotalTracks(0),
    m_nInitTracks(0),
    m_ulDuration((UINT32)-1)
{
}

CSmilAddGroup::~CSmilAddGroup()
{
    HX_RELEASE(m_pValues);
}

HX_RESULT
CSmilAddGroup::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
      rc = m_pHandler->handleAddGroup(this);
    }
    return rc;
}

#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)

CSmilTransition::CSmilTransition(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_dStartProgress = 0.0;
    m_dEndProgress   = 1.0;
    m_eDirection     = TransitionDirectionForward;
    m_ulFadeColor    = 0x00000000; // black
    m_ulHorzRepeat   = 1;
    m_ulVertRepeat   = 1;
    m_ulBorderWidth  = 0;
    m_ulBorderColor  = 0;
    m_bBlendBorder   = FALSE;
}

CSmilTransition::~CSmilTransition()
{
}

HX_RESULT CSmilTransition::handleElement()
{
    HX_RESULT retVal = HXR_OK;

    if(m_pHandler)
    {
        retVal = m_pHandler->handleTransition(this);
    }

    return retVal;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */

CSmilBodyElement::CSmilBodyElement(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_eAccessErrorBehavior = AccessErrorBehaviorContinue;
}

CSmilBodyElement::~CSmilBodyElement()
{
}

Generated by  Doxygen 1.6.0   Back to index