//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= // // Purpose: // //============================================================================= #include "movieobjects/dmetrackgroup.h" #include #include "tier0/dbg.h" #include "datamodel/dmelementfactoryhelper.h" #include "movieobjects/dmetrack.h" #include "movieobjects/dmeclip.h" #include "movieobjects_interfaces.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // CDmeTrackGroup - contains a list of tracks //----------------------------------------------------------------------------- IMPLEMENT_ELEMENT_FACTORY( DmeTrackGroup, CDmeTrackGroup ); void CDmeTrackGroup::OnConstruction() { m_Tracks.Init( this, "tracks", FATTRIB_MUSTCOPY | FATTRIB_HAS_CALLBACK ); m_bIsVisible.InitAndSet( this, "visible", true ); m_bMute.Init( this, "mute" ); m_flDisplayScale.InitAndSet( this, "displayScale", 1.0f ); m_bMinimized.InitAndSet( this, "minimized", true ); m_nMaxTrackCount = INT_MAX; m_Volume.InitAndSet( this, "volume", 1.0 ); m_bForceMultiTrack.InitAndSet( this, "forcemultitrack", false ); } void CDmeTrackGroup::OnDestruction() { // NOTE: The track owner handles may still be pointing to us when we get destructed, // but their handles will be invalid, so GetTrackGroup on a track // will correctly return NULL. } //----------------------------------------------------------------------------- // Max track count //----------------------------------------------------------------------------- void CDmeTrackGroup::SetMaxTrackCount( int nCount ) { m_nMaxTrackCount = nCount; } //----------------------------------------------------------------------------- // Mute //----------------------------------------------------------------------------- void CDmeTrackGroup::SetMute( bool state ) { m_bMute = state; } bool CDmeTrackGroup::IsMute( ) const { return m_bMute; } //----------------------------------------------------------------------------- // Volume //----------------------------------------------------------------------------- void CDmeTrackGroup::SetVolume( float state ) { m_Volume = state; } float CDmeTrackGroup::GetVolume() const { return m_Volume.Get(); } //----------------------------------------------------------------------------- // Owning clip //----------------------------------------------------------------------------- CDmeClip *CDmeTrackGroup::GetOwnerClip() { CDmeClip *pFindClip = FindReferringElement< CDmeClip >( this, "subClipTrackGroup" ); if ( !pFindClip ) { pFindClip = FindReferringElement< CDmeClip >( this, "trackGroups" ); } return pFindClip; } //----------------------------------------------------------------------------- // Are we a film track group? //----------------------------------------------------------------------------- bool CDmeTrackGroup::IsFilmTrackGroup() { CDmeClip *pOwnerClip = GetOwnerClip(); if ( pOwnerClip ) return pOwnerClip->GetFilmTrackGroup() == this; return m_nMaxTrackCount == 1; } //----------------------------------------------------------------------------- // Is a particular clip typed able to be added? //----------------------------------------------------------------------------- bool CDmeTrackGroup::IsSubClipTypeAllowed( DmeClipType_t type ) { if ( IsFilmTrackGroup() ) { if ( type != DMECLIP_FILM ) return false; } else { if ( type == DMECLIP_FILM ) return false; } CDmeClip *pOwnerClip = GetOwnerClip(); Assert( pOwnerClip ); if ( !pOwnerClip ) return true; return pOwnerClip->IsSubClipTypeAllowed( type ); } //----------------------------------------------------------------------------- // Track addition/removal //----------------------------------------------------------------------------- void CDmeTrackGroup::AddTrack( CDmeTrack *pTrack ) { // FIXME: Should check if track with same name already exists??? if ( GetTrackIndex( pTrack ) < 0 ) { // Tracks can only exist in one track group Assert( GetTrackIndex( pTrack ) >= 0 ); m_Tracks.AddToTail( pTrack ); Assert( m_nMaxTrackCount >= m_Tracks.Count() ); } } CDmeTrack* CDmeTrackGroup::AddTrack( const char *pTrackName, DmeClipType_t trackType ) { CDmeTrack *pTrack = CreateElement< CDmeTrack >( pTrackName, GetFileId() ); pTrack->SetClipType( trackType ); pTrack->SetCollapsed( false ); m_Tracks.AddToTail( pTrack ); Assert( m_nMaxTrackCount >= m_Tracks.Count() ); return pTrack; } CDmeTrack* CDmeTrackGroup::FindOrAddTrack( const char *pTrackName, DmeClipType_t trackType ) { CDmeTrack *pTrack = FindTrack( pTrackName ); if ( pTrack ) { // If we found it, but it's the wrong type, no dice if ( pTrack->GetClipType() != trackType ) return NULL; } else { pTrack = AddTrack( pTrackName, trackType ); } return pTrack; } void CDmeTrackGroup::RemoveTrack( int nIndex ) { m_Tracks.Remove( nIndex ); } void CDmeTrackGroup::RemoveTrack( CDmeTrack *pTrack ) { int i = GetTrackIndex( pTrack ); if ( i >= 0 ) { m_Tracks.Remove( i ); } } void CDmeTrackGroup::RemoveTrack( const char *pTrackName ) { if ( !pTrackName ) { pTrackName = DMETRACK_DEFAULT_NAME; } int c = m_Tracks.Count(); for ( int i = c; --i >= 0; ) { if ( !Q_strcmp( m_Tracks[i]->GetName(), pTrackName ) ) { m_Tracks.Remove( i ); return; } } } //----------------------------------------------------------------------------- // Track finding //----------------------------------------------------------------------------- CDmeTrack *CDmeTrackGroup::FindTrack( const char *pTrackName ) const { if ( !pTrackName ) { pTrackName = DMETRACK_DEFAULT_NAME; } int c = m_Tracks.Count(); for ( int i = 0 ; i < c; ++i ) { CDmeTrack *pTrack = m_Tracks[i]; if ( !pTrack ) continue; if ( !Q_strcmp( pTrack->GetName(), pTrackName ) ) return pTrack; } return NULL; } int CDmeTrackGroup::GetTrackIndex( CDmeTrack *pTrack ) const { int nTracks = m_Tracks.Count(); for ( int i = 0 ; i < nTracks; ++i ) { if ( pTrack == m_Tracks[i] ) return i; } return -1; } //----------------------------------------------------------------------------- // Creates the film track group [for internal use only] //----------------------------------------------------------------------------- CDmeTrack *CDmeTrackGroup::CreateFilmTrack() { Assert( GetTrackCount() == 0 ); return AddTrack( "Film", DMECLIP_FILM ); } //----------------------------------------------------------------------------- // Returns the film track, if any //----------------------------------------------------------------------------- CDmeTrack *CDmeTrackGroup::GetFilmTrack() { if ( !IsFilmTrackGroup() ) return NULL; if ( GetTrackCount() > 0 ) { Assert( GetTrackCount() == 1 ); return m_Tracks[0]; } return NULL; } //----------------------------------------------------------------------------- // Adding/removing clips from tracks //----------------------------------------------------------------------------- CDmeTrack *CDmeTrackGroup::AddClip( CDmeClip *pClip, const char *pTrackName ) { DmeClipType_t type = pClip->GetClipType(); if ( !pTrackName ) { pTrackName = DMETRACK_DEFAULT_NAME; } CDmeTrack *pTrack = FindOrAddTrack( pTrackName, type ); if ( pTrack ) { pTrack->AddClip( pClip ); } return pTrack; } bool CDmeTrackGroup::RemoveClip( CDmeClip *pClip ) { CDmeTrack *pTrack = FindTrackForClip( pClip ); if ( pTrack ) return pTrack->RemoveClip( pClip ); return false; } //----------------------------------------------------------------------------- // Changing clip track //----------------------------------------------------------------------------- CDmeTrack *CDmeTrackGroup::ChangeTrack( CDmeClip *pClip, const char *pNewTrack ) { // Add, then remove, to avoid refcount problems // Don't remove if it wasn't added for some reason. CDmeTrack *pOldTrack = FindTrackForClip( pClip ); CDmeTrack *pTrack = AddClip( pClip, pNewTrack ); if ( pTrack && pOldTrack ) { pOldTrack->RemoveClip( pClip ); } return pTrack; } //----------------------------------------------------------------------------- // Finding clips in tracks //----------------------------------------------------------------------------- CDmeTrack *CDmeTrackGroup::FindTrackForClip( CDmeClip *pClip ) const { int nTrackIndex = -1; if ( !FindTrackForClip( pClip, &nTrackIndex, NULL ) ) return NULL; return GetTrack( nTrackIndex ); } bool CDmeTrackGroup::FindTrackForClip( CDmeClip *pClip, int *pTrackIndex, int *pClipIndex ) const { DmeClipType_t type = pClip->GetClipType(); int c = GetTrackCount(); for ( int i = 0; i < c; ++i ) { CDmeTrack *pTrack = GetTrack( i ); if ( !pTrack ) continue; if ( pTrack->GetClipType() != type ) continue; int nClipCount = pTrack->GetClipCount(); for ( int j = 0; j < nClipCount; ++j ) { if ( pTrack->GetClip( j ) == pClip ) { if ( pTrackIndex ) { *pTrackIndex = i; } if ( pClipIndex ) { *pClipIndex = j; } return true; } } } return false; } //----------------------------------------------------------------------------- // Finding clips in tracks by time //----------------------------------------------------------------------------- void CDmeTrackGroup::FindClipsAtTime( DmeClipType_t clipType, DmeTime_t time, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const { if ( ( flags & DMESKIP_INVISIBLE ) && ( !IsVisible() || IsMinimized() ) ) return; if ( ( flags & DMESKIP_MUTED ) && IsMute() ) return; int c = GetTrackCount(); for ( int i = 0; i < c; ++i ) { CDmeTrack *pTrack = GetTrack( i ); if ( !pTrack ) continue; if ( ( clipType != DMECLIP_UNKNOWN ) && ( pTrack->GetClipType() != clipType ) ) continue; pTrack->FindClipsAtTime( time, flags, clips ); } } void CDmeTrackGroup::FindClipsIntersectingTime( DmeClipType_t clipType, DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const { if ( ( flags & DMESKIP_INVISIBLE ) && ( !IsVisible() || IsMinimized() ) ) return; if ( ( flags & DMESKIP_MUTED ) && IsMute() ) return; int c = GetTrackCount(); for ( int i = 0; i < c; ++i ) { CDmeTrack *pTrack = GetTrack( i ); if ( !pTrack ) continue; if ( ( clipType != DMECLIP_UNKNOWN ) && ( pTrack->GetClipType() != clipType ) ) continue; pTrack->FindClipsIntersectingTime( startTime, endTime, flags, clips ); } } void CDmeTrackGroup::FindClipsWithinTime( DmeClipType_t clipType, DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const { if ( ( flags & DMESKIP_INVISIBLE ) && ( !IsVisible() || IsMinimized() ) ) return; if ( ( flags & DMESKIP_MUTED ) && IsMute() ) return; int c = GetTrackCount(); for ( int i = 0; i < c; ++i ) { CDmeTrack *pTrack = GetTrack( i ); if ( !pTrack ) continue; if ( ( clipType != DMECLIP_UNKNOWN ) && ( pTrack->GetClipType() != clipType ) ) continue; pTrack->FindClipsWithinTime( startTime, endTime, flags, clips ); } } //----------------------------------------------------------------------------- // Removes empty tracks //----------------------------------------------------------------------------- void CDmeTrackGroup::RemoveEmptyTracks() { int tc = GetTrackCount(); for ( int i = tc; --i >= 0; ) { CDmeTrack *pTrack = GetTrack( i ); if ( pTrack->GetClipCount() == 0 ) { RemoveTrack( i ); } } } //----------------------------------------------------------------------------- // Sort tracks by track type, then alphabetically //----------------------------------------------------------------------------- static int TrackLessFunc( const void * lhs, const void * rhs ) { CDmeTrack *pInfo1 = *(CDmeTrack**)lhs; CDmeTrack *pInfo2 = *(CDmeTrack**)rhs; if ( pInfo1->GetClipType() < pInfo2->GetClipType() ) return -1; if ( pInfo1->GetClipType() > pInfo2->GetClipType() ) return 1; return Q_strcmp( pInfo1->GetName(), pInfo2->GetName() ); } void CDmeTrackGroup::SortTracksByType() { int tc = GetTrackCount(); if ( tc == 0 ) return; CDmeTrack **ppTrack = (CDmeTrack**)_alloca( tc * sizeof(CDmeTrack*) ); for ( int i = 0; i < tc; ++i ) { ppTrack[i] = GetTrack(i); } qsort( ppTrack, tc, sizeof(CDmeTrack*), TrackLessFunc ); m_Tracks.RemoveAll(); for ( int i = 0; i < tc; ++i ) { m_Tracks.AddToTail( ppTrack[i] ); } } //----------------------------------------------------------------------------- // Returns the flattened clip count //----------------------------------------------------------------------------- int CDmeTrackGroup::GetSubClipCount() const { int nCount = 0; DMETRACKGROUP_FOREACH_CLIP_START( this, pTrack, pClip ) ++nCount; DMETRACKGROUP_FOREACH_CLIP_END() return nCount; } void CDmeTrackGroup::GetSubClips( CDmeClip **ppClips ) { int nCount = 0; DMETRACKGROUP_FOREACH_CLIP_START( this, pTrack, pClip ) ppClips[nCount++] = pClip; DMETRACKGROUP_FOREACH_CLIP_END() } bool CDmeTrackGroup::GetForceMultiTrack() const { return const_cast< CDmeTrackGroup * >( this )->IsFilmTrackGroup() && m_bForceMultiTrack; } void CDmeTrackGroup::SetForceMultiTrack( bool bForce ) { m_bForceMultiTrack = bForce; } //----------------------------------------------------------------------------- // helper methods //----------------------------------------------------------------------------- CDmeFilmClip *GetParentClip( CDmeTrackGroup *pTrackGroup ) { DmAttributeReferenceIterator_t hAttr = g_pDataModel->FirstAttributeReferencingElement( pTrackGroup->GetHandle() ); for ( ; hAttr != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; hAttr = g_pDataModel->NextAttributeReferencingElement( hAttr ) ) { CDmAttribute *pAttr = g_pDataModel->GetAttribute( hAttr ); if ( !pAttr ) continue; CDmeFilmClip *pFilmClip = CastElement< CDmeFilmClip >( pAttr->GetOwner() ); if ( pFilmClip ) return pFilmClip; } return NULL; }