summaryrefslogtreecommitdiff
path: root/movieobjects/dmetrack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'movieobjects/dmetrack.cpp')
-rw-r--r--movieobjects/dmetrack.cpp818
1 files changed, 818 insertions, 0 deletions
diff --git a/movieobjects/dmetrack.cpp b/movieobjects/dmetrack.cpp
new file mode 100644
index 0000000..431c87b
--- /dev/null
+++ b/movieobjects/dmetrack.cpp
@@ -0,0 +1,818 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmetrack.h"
+
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects/dmeclip.h"
+#include "movieobjects/dmetrackgroup.h"
+
+#include "movieobjects_interfaces.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// The solo track
+//-----------------------------------------------------------------------------
+DmElementHandle_t CDmeTrack::m_hSoloTrack[ DMECLIP_TYPE_COUNT ] =
+{
+ DMELEMENT_HANDLE_INVALID,
+ DMELEMENT_HANDLE_INVALID,
+ DMELEMENT_HANDLE_INVALID,
+ DMELEMENT_HANDLE_INVALID,
+};
+
+
+//-----------------------------------------------------------------------------
+// CDmeTrack - common container class for clip objects
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeTrack, CDmeTrack );
+
+void CDmeTrack::OnConstruction()
+{
+ m_hOwner = DMELEMENT_HANDLE_INVALID;
+
+ m_Flags.ClearAllFlags();
+ m_Clips.Init( this, "children" );
+ m_Collapsed.InitAndSet( this, "collapsed", true );
+ m_Mute.InitAndSet( this, "mute", false );
+ m_Synched.InitAndSet( this, "synched", true );
+ m_ClipType.InitAndSet( this, "clipType", DMECLIP_UNKNOWN, FATTRIB_HAS_CALLBACK | FATTRIB_HAS_PRE_CALLBACK );
+
+ m_Volume.InitAndSet( this, "volume", 1.0 );
+
+}
+
+void CDmeTrack::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods of IDmElement
+//-----------------------------------------------------------------------------
+void CDmeTrack::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ BaseClass::OnAttributeChanged( pAttribute );
+
+ // Attach callbacks to detected sorted conditions if we're a film clip
+ if ( pAttribute == m_ClipType.GetAttribute() )
+ {
+ if ( m_ClipType == DMECLIP_FILM )
+ {
+ m_Flags.ClearFlag( IS_SORTED );
+ }
+ return;
+ }
+
+ // This gets called when start/end time of children change, or if the array changes
+ // This is a hack, since any OnAttributeChanged call that gets chained here from another element will trigger this
+ // At some point, we'll probably have to start sending more data through OnAttributeChanged, (like an event string or chain path)
+ // or perhaps add a new callback OnElementChanged() with this data
+ if ( pAttribute == m_Clips.GetAttribute() || ( pAttribute->GetOwner() != this ) )
+ {
+ if ( !m_Flags.IsFlagSet( SUPPRESS_DIRTY_ORDERING ) )
+ {
+ m_Flags.ClearFlag( IS_SORTED );
+ }
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Clip type
+//-----------------------------------------------------------------------------
+DmeClipType_t CDmeTrack::GetClipType() const
+{
+ return (DmeClipType_t)m_ClipType.Get();
+}
+
+void CDmeTrack::SetClipType( DmeClipType_t type )
+{
+ m_ClipType = type;
+}
+
+void CDmeTrack::SetCollapsed( bool state )
+{
+ m_Collapsed = state;
+
+}
+
+bool CDmeTrack::IsCollapsed() const
+{
+ return m_Collapsed.Get();
+}
+
+void CDmeTrack::SetMute( bool state )
+{
+ m_Mute = state;
+}
+
+//-----------------------------------------------------------------------------
+// Volume
+//-----------------------------------------------------------------------------
+void CDmeTrack::SetVolume( float state )
+{
+ m_Volume = state;
+}
+float CDmeTrack::GetVolume() const
+{
+ return m_Volume.Get();
+}
+
+// Is this track synched to the film track?
+void CDmeTrack::SetSynched( bool bState )
+{
+ m_Synched = bState;
+}
+
+bool CDmeTrack::IsSynched() const
+{
+ return m_Synched;
+}
+
+bool CDmeTrack::IsMute( bool bCheckSoloing ) const
+{
+ // if we're muted, don't play regardless of whether we're solo
+ CDmeTrack *pSoloTrack = bCheckSoloing ? GetSoloTrack() : NULL;
+ return m_Mute.Get() || ( pSoloTrack != this && pSoloTrack != NULL );
+}
+
+int CDmeTrack::GetClipCount() const
+{
+ return m_Clips.Count();
+}
+
+CDmeClip *CDmeTrack::GetClip( int i ) const
+{
+ return m_Clips[ i ];
+}
+
+const CUtlVector< DmElementHandle_t > &CDmeTrack::GetClips( ) const
+{
+ return m_Clips.Get();
+}
+
+void CDmeTrack::AddClip( CDmeClip *clip )
+{
+ if ( clip->GetClipType() == GetClipType() )
+ {
+ // FIXME: In the case of a non-overlapped track,
+ // we could optimize this to insert the clip in sorted order,
+ // then fix overlaps (fixing overlaps requires a sorted list)
+ Assert( FindClip( clip ) < 0 );
+ m_Clips.AddToTail( clip );
+ }
+}
+
+void CDmeTrack::RemoveClip( int i )
+{
+ // NOTE: Removal shouldn't cause sort order or fixup to become invalid
+ CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING );
+ m_Clips.Remove( i );
+}
+
+bool CDmeTrack::RemoveClip( CDmeClip *clip )
+{
+ Assert( clip->GetClipType() == GetClipType() );
+ int i = FindClip( clip );
+ if ( i != -1 )
+ {
+ RemoveClip( i );
+ return true;
+ }
+ return false;
+}
+
+void CDmeTrack::RemoveAllClips()
+{
+ CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING );
+ m_Clips.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the solo track, if any
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeTrack::GetSoloTrack( DmeClipType_t clipType )
+{
+ return GetElement< CDmeTrack >( m_hSoloTrack[ clipType ] );
+}
+
+void CDmeTrack::SetSoloTrack( DmeClipType_t clipType, CDmeTrack *pTrack )
+{
+ m_hSoloTrack[ clipType ] = pTrack->GetHandle();
+}
+
+bool CDmeTrack::IsSoloTrack() const
+{
+ return m_hSoloTrack[ GetClipType() ] == GetHandle();
+}
+
+CDmeTrack *CDmeTrack::GetSoloTrack() const
+{
+ return GetSoloTrack( GetClipType() );
+}
+
+void CDmeTrack::SetSoloTrack( )
+{
+ m_hSoloTrack[ GetClipType() ] = GetHandle();
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods related to finding clips
+//-----------------------------------------------------------------------------
+int CDmeTrack::FindClip( CDmeClip *clip )
+{
+ Assert( clip->GetClipType() == GetClipType() );
+ int c = m_Clips.Count();
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ if ( m_Clips[ i ] == clip )
+ return i;
+ }
+ return -1;
+}
+
+CDmeClip *CDmeTrack::FindNamedClip( const char *name )
+{
+ int c = m_Clips.Count();
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ CDmeClip *child = m_Clips[ i ];
+ if ( child && !Q_stricmp( child->GetName(), name ) )
+ return child;
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Find clips at, intersecting or within a particular time interval
+//-----------------------------------------------------------------------------
+void CDmeTrack::FindClipsAtTime( DmeTime_t time, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( flags & DMESKIP_INVISIBLE ) && IsCollapsed() )
+ return;
+
+ if ( ( flags & DMESKIP_MUTED ) && IsMute() )
+ return;
+
+ int nClipCount = GetClipCount();
+ for ( int j = 0; j < nClipCount; ++j )
+ {
+ CDmeClip *pSubClip = GetClip( j );
+ if ( !pSubClip )
+ continue;
+ if ( ( flags & DMESKIP_MUTED ) && pSubClip->IsMute() )
+ continue;
+
+ if ( time.IsInRange( pSubClip->GetStartTime(), pSubClip->GetEndTime() ) )
+ {
+ clips.AddToTail( pSubClip );
+ }
+ }
+}
+
+void CDmeTrack::FindClipsIntersectingTime( DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( flags & DMESKIP_INVISIBLE ) && IsCollapsed() )
+ return;
+
+ if ( ( flags & DMESKIP_MUTED ) && IsMute() )
+ return;
+
+ int nClipCount = GetClipCount();
+ for ( int j = 0; j < nClipCount; ++j )
+ {
+ CDmeClip *pSubClip = GetClip( j );
+ if ( !pSubClip )
+ continue;
+ if ( ( flags & DMESKIP_MUTED ) && pSubClip->IsMute() )
+ continue;
+
+ DmeTime_t clipStart = pSubClip->GetStartTime();
+ DmeTime_t clipEnd = pSubClip->GetEndTime();
+ if ( clipEnd >= startTime && clipStart < endTime )
+ {
+ clips.AddToTail( pSubClip );
+ }
+ }
+}
+
+void CDmeTrack::FindClipsWithinTime( DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( flags & DMESKIP_INVISIBLE ) && IsCollapsed() )
+ return;
+
+ if ( ( flags & DMESKIP_MUTED ) && IsMute() )
+ return;
+
+ int nClipCount = GetClipCount();
+ for ( int j = 0; j < nClipCount; ++j )
+ {
+ CDmeClip *pSubClip = GetClip( j );
+ if ( !pSubClip )
+ continue;
+ if ( ( flags & DMESKIP_MUTED ) && pSubClip->IsMute() )
+ continue;
+
+ DmeTime_t clipStart = pSubClip->GetStartTime();
+ DmeTime_t clipEnd = pSubClip->GetEndTime();
+ if ( clipStart >= startTime && clipEnd <= endTime )
+ {
+ clips.AddToTail( pSubClip );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods related to shifting clips
+//-----------------------------------------------------------------------------
+void CDmeTrack::ShiftAllClips( DmeTime_t dt )
+{
+ if ( dt == DmeTime_t( 0 ) )
+ return;
+
+ CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING );
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = m_Clips[ i ];
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+}
+
+void CDmeTrack::ShiftAllClipsAfter( DmeTime_t startTime, DmeTime_t dt, bool bTestStartingTime )
+{
+ if ( dt == DmeTime_t( 0 ) )
+ return;
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ DmeTime_t testTime = bTestStartingTime ? pSubClip->GetStartTime() : pSubClip->GetEndTime();
+ if ( startTime < testTime )
+ {
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+ }
+}
+
+void CDmeTrack::ShiftAllClipsBefore( DmeTime_t endTime, DmeTime_t dt, bool bTestEndingTime )
+{
+ if ( dt == DmeTime_t( 0 ) )
+ return;
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ DmeTime_t testTime = bTestEndingTime ? pSubClip->GetEndTime() : pSubClip->GetStartTime();
+ if ( endTime > testTime )
+ {
+ DmeTime_t startTime = pSubClip->GetStartTime();
+ pSubClip->SetStartTime( startTime + dt );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// A version that works only on film clips
+//-----------------------------------------------------------------------------
+void CDmeTrack::ShiftAllFilmClipsAfter( CDmeClip *pClip, DmeTime_t dt, bool bShiftClip )
+{
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) || ( dt == DmeTime_t( 0 ) ) )
+ return;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = c; --i >= 0; )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( pSubClip == pClip )
+ {
+ if ( bShiftClip )
+ {
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+ return;
+ }
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+
+ // Clip wasn't found!
+ Assert( 0 );
+}
+
+void CDmeTrack::ShiftAllFilmClipsBefore( CDmeClip *pClip, DmeTime_t dt, bool bShiftClip )
+{
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) || ( dt == DmeTime_t( 0 ) ) )
+ return;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+
+ if ( pSubClip == pClip )
+ {
+ if ( bShiftClip )
+ {
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+ return;
+ }
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+
+ // Clip wasn't found!
+ Assert( 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Method to sort clips by start time
+//-----------------------------------------------------------------------------
+struct SortInfo_t
+{
+ DmeTime_t m_startTime;
+ CDmeClip *m_pClip;
+};
+
+static int ClipStartLessFunc( const void * lhs, const void * rhs )
+{
+ SortInfo_t *pInfo1 = (SortInfo_t*)lhs;
+ SortInfo_t *pInfo2 = (SortInfo_t*)rhs;
+ if ( pInfo1->m_startTime == pInfo2->m_startTime )
+ return 0;
+
+ return pInfo1->m_startTime < pInfo2->m_startTime ? -1 : 1;
+}
+
+void CDmeTrack::SortClipsByStartTime( )
+{
+ // If we're not a film clip, then we haven't installed callbacks to make sorting fast.
+ // The IS_SORTED flag is some random state
+ if ( (m_ClipType == DMECLIP_FILM) && m_Flags.IsFlagSet( IS_SORTED ) )
+ return;
+ m_Flags.SetFlag( IS_SORTED );
+
+ int c = GetClipCount();
+ if ( c <= 1 )
+ return;
+
+ DmeTime_t lastTime;
+ SortInfo_t *pSortInfo = (SortInfo_t*)_alloca( c * sizeof(SortInfo_t) );
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip(i);
+ pSortInfo[i].m_startTime = pSubClip ? pSubClip->GetStartTime() : DmeTime_t::InvalidTime();
+ pSortInfo[i].m_pClip = pSubClip;
+ if ( lastTime > pSortInfo[i].m_startTime )
+ {
+ m_Flags.ClearFlag( IS_SORTED );
+ }
+ lastTime = pSortInfo[i].m_startTime;
+ }
+ if ( m_Flags.IsFlagSet( IS_SORTED ) )
+ return;
+
+ m_Flags.SetFlag( IS_SORTED );
+ qsort( pSortInfo, c, sizeof(SortInfo_t), ClipStartLessFunc );
+
+ CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING );
+
+ m_Clips.RemoveAll();
+
+ for ( int i = 0; i < c; ++i )
+ {
+ m_Clips.AddToTail( pSortInfo[i].m_pClip );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Shifts all clips to be non-overlapping
+//-----------------------------------------------------------------------------
+void CDmeTrack::FixOverlaps()
+{
+ int c = GetClipCount();
+ if ( c <= 1 )
+ return;
+
+ SortClipsByStartTime();
+
+ CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING );
+
+ // Cull NULL clips
+ int nActualCount = 0;
+ CDmeClip **pClips = (CDmeClip**)_alloca( c * sizeof(CDmeClip*) );
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pCurr = GetClip( i );
+ if ( pCurr && ((i == 0) || (pClips[i-1] != pCurr)) )
+ {
+ pClips[nActualCount++] = pCurr;
+ }
+ }
+
+ if ( nActualCount <= 1 )
+ return;
+
+ CDmeClip *pPrev = pClips[0];
+ for ( int i = 1; i < nActualCount; ++i )
+ {
+ CDmeClip *pCurr = pClips[i];
+
+ DmeTime_t prevEndTime = pPrev->GetEndTime();
+ DmeTime_t startTime = pCurr->GetStartTime();
+
+ if ( startTime < prevEndTime )
+ {
+ pCurr->SetStartTime( prevEndTime );
+ }
+
+ pPrev = pCurr;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a clip at a particular time
+//-----------------------------------------------------------------------------
+CDmeClip* CDmeTrack::FindFilmClipAtTime( DmeTime_t localTime )
+{
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return NULL;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( pSubClip && pSubClip->GetStartTime() <= localTime && pSubClip->GetEndTime() > localTime )
+ return pSubClip;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Find first clip in a specific time range
+//-----------------------------------------------------------------------------
+CDmeClip* CDmeTrack::FindFirstFilmClipIntesectingTime( DmeTime_t localStartTime, DmeTime_t localEndTime )
+{
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return NULL;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( !pSubClip )
+ continue;
+ if ( ( localStartTime < pSubClip->GetEndTime() ) && ( localEndTime >= pSubClip->GetStartTime() ) )
+ return static_cast<CDmeFilmClip*>( pSubClip );
+ if ( localEndTime <= pSubClip->GetStartTime() )
+ break;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Inserts space in a film track for a film clip
+//-----------------------------------------------------------------------------
+void CDmeTrack::InsertSpaceInFilmTrack( DmeTime_t localStartTime, DmeTime_t localEndTime )
+{
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ CDmeClip *pClip = FindFirstFilmClipIntesectingTime( localStartTime, localEndTime );
+ if ( pClip )
+ {
+ DmeTime_t filmStart = pClip->GetStartTime();
+ DmeTime_t dt = localEndTime - filmStart;
+ ShiftAllFilmClipsAfter( pClip, dt, true );
+ }
+
+ return;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the next/previous clip in a film track
+//-----------------------------------------------------------------------------
+CDmeClip* CDmeTrack::FindPrevFilmClip( CDmeClip *pClip )
+{
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return NULL;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ if ( !pClip )
+ return m_Clips[ m_Clips.Count() - 1 ];
+
+ // FIXME: Could use a binary search here based on time.
+ // Probably doesn't matter though, since there will usually not be a ton of tracks
+ CDmeClip *pPrevClip = NULL;
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( pSubClip == pClip )
+ return pPrevClip;
+
+ pPrevClip = pSubClip;
+ }
+
+ return NULL;
+}
+
+CDmeClip* CDmeTrack::FindNextFilmClip( CDmeClip *pClip )
+{
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return NULL;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ if ( !pClip )
+ return m_Clips[ 0 ];
+
+ CDmeClip *pNextClip = NULL;
+
+ int c = GetClipCount();
+ for ( int i = c; --i >= 0; )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( pSubClip == pClip )
+ return pNextClip;
+
+ pNextClip = pSubClip;
+ }
+
+ return NULL;
+}
+
+
+void CDmeTrack::FindAdjacentFilmClips( CDmeClip *pClip, CDmeClip *&pPrevClip, CDmeClip *&pNextClip )
+{
+ pPrevClip = pNextClip = NULL;
+
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || !pClip || ( m_Clips.Count() == 0 ) )
+ return;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( pSubClip == pClip )
+ {
+ pNextClip = ( i != c-1 ) ? GetClip( i+1 ) : NULL;
+ return;
+ }
+
+ pPrevClip = pSubClip;
+ }
+
+ pPrevClip = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the start/end time of the owning clip in local time
+//-----------------------------------------------------------------------------
+void CDmeTrack::FindAdjacentFilmClips( DmeTime_t localTime, CDmeClip *&pPrevClip, CDmeClip *&pNextClip )
+{
+ pPrevClip = pNextClip = NULL;
+
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( localTime >= pSubClip->GetEndTime() )
+ {
+ pPrevClip = pSubClip;
+ }
+ if ( localTime < pSubClip->GetStartTime() )
+ {
+ pNextClip = pSubClip;
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fills all gaps in a film track with slugs
+//-----------------------------------------------------------------------------
+void CDmeTrack::FillAllGapsWithSlugs( const char *pSlugName, DmeTime_t startTime, DmeTime_t endTime )
+{
+ if ( !IsFilmTrack() )
+ return;
+
+ FixOverlaps();
+
+ // Create temporary slugs to fill in the gaps
+ bool bSlugAdded = false;
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pFilmClip = GetClip(i);
+ DmeTime_t clipStartTime = pFilmClip->GetStartTime();
+ if ( clipStartTime > startTime )
+ {
+ // There's a gap, create a slug
+ CDmeFilmClip *pSlug = CreateSlugClip( pSlugName, startTime, clipStartTime, GetFileId() );
+
+ // This will add the slug to the end; so we don't have to
+ // worry about iterating over it (we've cached off the initial count)
+ AddClip( pSlug );
+
+ bSlugAdded = true;
+ }
+ startTime = pFilmClip->GetEndTime();
+ }
+
+ if ( endTime > startTime )
+ {
+ // There's a gap, create a temporary slug
+ CDmeFilmClip *pSlug = CreateSlugClip( pSlugName, startTime, endTime, GetFileId() );
+
+ // This will add the slug to the end; so we don't have to
+ // worry about iterating over it (we've cached off the initial count)
+ AddClip( pSlug );
+
+ bSlugAdded = true;
+ }
+
+ if ( bSlugAdded )
+ {
+ FixOverlaps();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// helper methods
+//-----------------------------------------------------------------------------
+CDmeTrackGroup *GetParentTrackGroup( CDmeTrack *pTrack )
+{
+ DmAttributeReferenceIterator_t hAttr = g_pDataModel->FirstAttributeReferencingElement( pTrack->GetHandle() );
+ for ( ; hAttr != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; hAttr = g_pDataModel->NextAttributeReferencingElement( hAttr ) )
+ {
+ CDmAttribute *pAttr = g_pDataModel->GetAttribute( hAttr );
+ if ( !pAttr )
+ continue;
+
+ CDmeTrackGroup *pTrackGroup = CastElement< CDmeTrackGroup >( pAttr->GetOwner() );
+ if ( pTrackGroup )
+ return pTrackGroup;
+ }
+ return NULL;
+}