2010-01-23 19:44:49 +00:00
/**********************************************************************
Audacity : A Digital Audio Editor
Track . cpp
Dominic Mazzoni
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /**
\ class Track
\ brief Fundamental data object of Audacity , placed in the TrackPanel .
2014-06-03 20:30:19 +00:00
Classes derived form it include the WaveTrack , NoteTrack , LabelTrack
2010-01-23 19:44:49 +00:00
and TimeTrack .
2017-11-09 18:32:29 +00:00
\ class AudioTrack
\ brief A Track that can load / save audio data to / from XML .
\ class PlayableTrack
\ brief An AudioTrack that can be played and stopped .
2010-01-23 19:44:49 +00:00
*/ /*******************************************************************/
2016-03-01 23:31:51 +00:00
# include <algorithm>
# include <numeric>
2015-07-07 03:12:16 +00:00
# include "Track.h"
2010-01-23 19:44:49 +00:00
# include <float.h>
# include <wx/file.h>
# include <wx/textfile.h>
# include <wx/log.h>
2016-03-01 23:31:51 +00:00
# include "TimeTrack.h"
2010-01-23 19:44:49 +00:00
# include "WaveTrack.h"
# include "NoteTrack.h"
2016-03-13 15:08:21 +00:00
# include "LabelTrack.h"
2010-01-23 19:44:49 +00:00
# include "Project.h"
# include "DirManager.h"
2015-08-31 19:50:50 +00:00
# include "Experimental.h"
2017-07-14 15:53:17 +00:00
# include "TrackPanel.h" // for TrackInfo
2010-01-23 19:44:49 +00:00
# ifdef _MSC_VER
//Disable truncation warnings
# pragma warning( disable : 4786 )
# endif
# ifdef __WXDEBUG__
// if we are in a debug build of audacity
/// Define this to do extended (slow) debuging of TrackListIterator
// #define DEBUG_TLI
# endif
2016-08-13 04:38:31 +00:00
Track : : Track ( const std : : shared_ptr < DirManager > & projDirManager )
2010-01-23 19:44:49 +00:00
: vrulerSize ( 36 , 0 ) ,
mDirManager ( projDirManager )
{
mSelected = false ;
mLinked = false ;
mY = 0 ;
2017-06-07 01:40:58 +00:00
mHeight = DefaultHeight ;
2010-01-23 19:44:49 +00:00
mIndex = 0 ;
2013-05-30 23:14:25 +00:00
2010-01-23 19:44:49 +00:00
mMinimized = false ;
mOffset = 0.0 ;
mChannel = MonoChannel ;
}
Track : : Track ( const Track & orig )
2017-06-25 05:14:48 +00:00
: vrulerSize ( orig . vrulerSize )
2010-01-23 19:44:49 +00:00
{
mY = 0 ;
mIndex = 0 ;
Init ( orig ) ;
mOffset = orig . mOffset ;
}
// Copy all the track properties except the actual contents
void Track : : Init ( const Track & orig )
{
2018-01-11 00:28:16 +00:00
mId = orig . mId ;
2010-01-23 19:44:49 +00:00
mDefaultName = orig . mDefaultName ;
mName = orig . mName ;
2016-08-13 04:38:31 +00:00
mDirManager = orig . mDirManager ;
2010-01-23 19:44:49 +00:00
mSelected = orig . mSelected ;
mLinked = orig . mLinked ;
mHeight = orig . mHeight ;
mMinimized = orig . mMinimized ;
mChannel = orig . mChannel ;
}
2015-08-23 23:02:06 +00:00
void Track : : SetSelected ( bool s )
{
mSelected = s ;
}
2010-01-23 19:44:49 +00:00
void Track : : Merge ( const Track & orig )
{
mSelected = orig . mSelected ;
}
Track : : ~ Track ( )
{
}
2016-03-01 23:31:51 +00:00
TrackNodePointer Track : : GetNode ( ) const
2010-01-23 19:44:49 +00:00
{
2017-07-10 15:48:11 +00:00
wxASSERT ( mList . lock ( ) = = NULL | | this = = mNode - > get ( ) ) ;
2010-01-23 19:44:49 +00:00
return mNode ;
}
2017-07-10 15:48:11 +00:00
void Track : : SetOwner
( const std : : weak_ptr < TrackList > & list , TrackNodePointer node )
2010-01-23 19:44:49 +00:00
{
mList = list ;
mNode = node ;
}
int Track : : GetMinimizedHeight ( ) const
{
2017-07-14 15:53:17 +00:00
auto height = TrackInfo : : MinimumTrackHeight ( ) ;
2010-01-23 19:44:49 +00:00
if ( GetLink ( ) ) {
2017-07-14 15:53:17 +00:00
auto halfHeight = height / 2 ;
if ( GetLinked ( ) )
return halfHeight ;
else
return height - halfHeight ;
2010-01-23 19:44:49 +00:00
}
2017-07-14 15:53:17 +00:00
return height ;
2010-01-23 19:44:49 +00:00
}
int Track : : GetIndex ( ) const
{
return mIndex ;
}
void Track : : SetIndex ( int index )
{
mIndex = index ;
}
int Track : : GetY ( ) const
{
return mY ;
}
void Track : : SetY ( int y )
2018-01-12 01:57:37 +00:00
{
2017-08-24 18:31:51 +00:00
auto pList = mList . lock ( ) ;
if ( pList & & ! pList - > mPendingUpdates . empty ( ) ) {
auto orig = pList - > FindById ( GetId ( ) ) ;
if ( orig & & orig ! = this ) {
// delegate, and rely on the update to copy back
orig - > SetY ( y ) ;
pList - > UpdatePendingTracks ( ) ;
return ;
}
}
2018-01-12 01:57:37 +00:00
DoSetY ( y ) ;
}
void Track : : DoSetY ( int y )
2010-01-23 19:44:49 +00:00
{
mY = y ;
}
int Track : : GetHeight ( ) const
{
if ( mMinimized ) {
return GetMinimizedHeight ( ) ;
}
return mHeight ;
}
void Track : : SetHeight ( int h )
{
2017-07-10 15:48:11 +00:00
auto pList = mList . lock ( ) ;
2017-08-24 18:31:51 +00:00
if ( pList & & ! pList - > mPendingUpdates . empty ( ) ) {
auto orig = pList - > FindById ( GetId ( ) ) ;
if ( orig & & orig ! = this ) {
// delegate, and rely on RecalcPositions to copy back
orig - > SetHeight ( h ) ;
return ;
}
}
2018-01-12 01:57:37 +00:00
DoSetHeight ( h ) ;
2017-07-10 15:48:11 +00:00
if ( pList ) {
pList - > RecalcPositions ( mNode ) ;
pList - > ResizingEvent ( mNode ) ;
2016-03-01 23:31:51 +00:00
}
2010-01-23 19:44:49 +00:00
}
2018-01-12 01:57:37 +00:00
void Track : : DoSetHeight ( int h )
{
mHeight = h ;
}
2010-01-23 19:44:49 +00:00
bool Track : : GetMinimized ( ) const
{
return mMinimized ;
}
void Track : : SetMinimized ( bool isMinimized )
{
2017-07-10 15:48:11 +00:00
auto pList = mList . lock ( ) ;
2017-08-24 18:31:51 +00:00
if ( pList & & ! pList - > mPendingUpdates . empty ( ) ) {
auto orig = pList - > FindById ( GetId ( ) ) ;
if ( orig & & orig ! = this ) {
// delegate, and rely on RecalcPositions to copy back
orig - > SetMinimized ( isMinimized ) ;
return ;
}
}
2018-01-12 01:57:37 +00:00
DoSetMinimized ( isMinimized ) ;
2017-07-10 15:48:11 +00:00
if ( pList ) {
pList - > RecalcPositions ( mNode ) ;
pList - > ResizingEvent ( mNode ) ;
2016-03-01 23:31:51 +00:00
}
2010-01-23 19:44:49 +00:00
}
2018-01-12 01:57:37 +00:00
void Track : : DoSetMinimized ( bool isMinimized )
{
mMinimized = isMinimized ;
}
2013-03-10 16:31:07 +00:00
void Track : : SetLinked ( bool l )
{
2017-07-10 15:48:11 +00:00
auto pList = mList . lock ( ) ;
2017-08-24 18:31:51 +00:00
if ( pList & & ! pList - > mPendingUpdates . empty ( ) ) {
auto orig = pList - > FindById ( GetId ( ) ) ;
if ( orig & & orig ! = this ) {
// delegate, and rely on RecalcPositions to copy back
orig - > SetLinked ( l ) ;
return ;
}
}
2018-01-12 01:57:37 +00:00
DoSetLinked ( l ) ;
2017-07-10 15:48:11 +00:00
if ( pList ) {
pList - > RecalcPositions ( mNode ) ;
pList - > ResizingEvent ( mNode ) ;
2016-03-01 23:31:51 +00:00
}
2013-03-10 16:31:07 +00:00
}
2018-01-12 01:57:37 +00:00
void Track : : DoSetLinked ( bool l )
{
mLinked = l ;
}
2010-01-23 19:44:49 +00:00
Track * Track : : GetLink ( ) const
{
2017-07-10 15:48:11 +00:00
auto pList = mList . lock ( ) ;
if ( ! pList )
2016-03-01 23:31:51 +00:00
return nullptr ;
2017-07-10 15:48:11 +00:00
if ( ! pList - > isNull ( mNode ) ) {
2016-03-01 23:31:51 +00:00
if ( mLinked ) {
2018-01-13 01:17:30 +00:00
auto next = pList - > getNext ( mNode ) ;
if ( ! pList - > isNull ( next ) )
2016-03-13 15:08:21 +00:00
return next - > get ( ) ;
2010-01-23 19:44:49 +00:00
}
2018-01-13 01:17:30 +00:00
auto prev = pList - > getPrev ( mNode ) ;
if ( ! pList - > isNull ( prev ) ) {
2016-03-13 15:08:21 +00:00
auto track = prev - > get ( ) ;
2018-01-13 01:17:30 +00:00
if ( track & & track - > GetLinked ( ) )
2016-03-01 23:31:51 +00:00
return track ;
2010-01-23 19:44:49 +00:00
}
}
2016-03-01 23:31:51 +00:00
return nullptr ;
2010-01-23 19:44:49 +00:00
}
2015-04-19 11:27:33 +00:00
bool Track : : IsSyncLockSelected ( ) const
2010-02-12 16:05:02 +00:00
{
2010-09-14 05:52:01 +00:00
# ifdef EXPERIMENTAL_SYNC_LOCK
2010-02-12 16:05:02 +00:00
AudacityProject * p = GetActiveProject ( ) ;
2010-08-11 22:47:26 +00:00
if ( ! p | | ! p - > IsSyncLocked ( ) )
2010-02-12 16:05:02 +00:00
return false ;
2017-07-10 15:48:11 +00:00
auto pList = mList . lock ( ) ;
SyncLockedTracksIterator git ( pList . get ( ) ) ;
2016-02-23 19:06:36 +00:00
Track * t = git . StartWith ( const_cast < Track * > ( this ) ) ;
2010-02-12 16:05:02 +00:00
if ( ! t ) {
2010-08-11 23:56:50 +00:00
// Not in a sync-locked group.
(Sync-Lock)
Commented out the one call to TrackInfo::DrawBordersWithin(). This eliminates all the dark lines within the TrackInfo, in an effort to make the sync-lock icon not look like a button. It leaves some lighter borders, and I think that's an aesthetic improvement, though it make be worse in terms of accessibility. I can also remove the light border above the sync-lock icon, but I think this looks best overall.
In Track::IsSyncLockSelected(), for the "// Not in a sync-locked group." conditional, it returned true if the track was selected. I made it do so only if track kind is Wave or Label. Among other things, this means Time and Note tracks will never show the sync-lock icon. I think this is correct by definition, but Al, please let me know if this will have negative repercussions elsewhere. There are *lots* of calls to that method and I can move the track-type check to the code that draws the sync-lock icon..
Fixed the bug Gale pointed out where, if a WaveTrack is shrunk such that the sync-lock icon is over a TrackInfo control, such as pan slider, it didn't intercept the mouse event, and passed it on to the control. Now, clicking on the sync-lock icon does nothing.
Fixed a bug where the sync-lock icon was redrawn dark when the minimize button is down. Now not redrawn at all in that case.
Added some clarifying comments, especially about the term "Label" as used in TrackPanel.*.
2010-09-09 00:46:40 +00:00
return ( ( this - > GetKind ( ) = = Track : : Wave ) | | ( this - > GetKind ( ) = = Track : : Label ) ) & & GetSelected ( ) ;
2010-02-12 16:05:02 +00:00
}
for ( ; t ; t = git . Next ( ) ) {
if ( t - > GetSelected ( ) )
return true ;
}
# endif
return false ;
}
2017-03-23 15:10:14 +00:00
void Track : : SyncLockAdjust ( double oldT1 , double newT1 )
2010-02-16 20:50:38 +00:00
{
if ( newT1 > oldT1 ) {
// Insert space within the track
if ( oldT1 > GetEndTime ( ) )
2017-03-23 15:10:14 +00:00
return ;
2010-02-16 20:50:38 +00:00
2016-03-02 20:36:44 +00:00
auto tmp = Cut ( oldT1 , GetEndTime ( ) ) ;
2010-02-16 20:50:38 +00:00
2017-03-23 15:10:14 +00:00
Paste ( newT1 , tmp . get ( ) ) ;
2010-02-16 20:50:38 +00:00
}
else if ( newT1 < oldT1 ) {
// Remove from the track
2017-03-23 15:10:14 +00:00
Clear ( newT1 , oldT1 ) ;
2010-02-16 20:50:38 +00:00
}
}
2017-06-27 18:12:23 +00:00
std : : shared_ptr < Track > Track : : FindTrack ( )
2015-07-07 03:12:16 +00:00
{
2017-06-27 18:12:23 +00:00
return Pointer ( this ) ;
2015-07-07 03:12:16 +00:00
}
2017-03-29 15:25:05 +00:00
void PlayableTrack : : Init ( const PlayableTrack & orig )
{
mMute = orig . mMute ;
mSolo = orig . mSolo ;
AudioTrack : : Init ( orig ) ;
}
void PlayableTrack : : Merge ( const Track & orig )
{
auto pOrig = dynamic_cast < const PlayableTrack * > ( & orig ) ;
wxASSERT ( pOrig ) ;
mMute = pOrig - > mMute ;
mSolo = pOrig - > mSolo ;
AudioTrack : : Merge ( * pOrig ) ;
}
2017-03-30 20:00:27 +00:00
// Serialize, not with tags of its own, but as attributes within a tag.
void PlayableTrack : : WriteXMLAttributes ( XMLWriter & xmlFile ) const
{
xmlFile . WriteAttr ( wxT ( " mute " ) , mMute ) ;
xmlFile . WriteAttr ( wxT ( " solo " ) , mSolo ) ;
AudioTrack : : WriteXMLAttributes ( xmlFile ) ;
}
// Return true iff the attribute is recognized.
bool PlayableTrack : : HandleXMLAttribute ( const wxChar * attr , const wxChar * value )
{
const wxString strValue { value } ;
long nValue ;
if ( ! wxStrcmp ( attr , wxT ( " mute " ) ) & &
XMLValueChecker : : IsGoodInt ( strValue ) & & strValue . ToLong ( & nValue ) ) {
mMute = ( nValue ! = 0 ) ;
return true ;
}
else if ( ! wxStrcmp ( attr , wxT ( " solo " ) ) & &
XMLValueChecker : : IsGoodInt ( strValue ) & & strValue . ToLong ( & nValue ) ) {
mSolo = ( nValue ! = 0 ) ;
return true ;
}
return AudioTrack : : HandleXMLAttribute ( attr , value ) ;
}
2010-01-23 19:44:49 +00:00
// TrackListIterator
2018-01-14 19:01:04 +00:00
TrackListIterator : : TrackListIterator ( TrackList * val , TrackNodePointer p )
: l { val }
, cur { p }
{
}
2018-01-14 14:24:21 +00:00
TrackListIterator : : TrackListIterator ( TrackList * val )
2018-01-14 19:01:04 +00:00
: l { val }
2018-01-14 14:24:21 +00:00
, cur { }
2010-01-23 19:44:49 +00:00
{
2018-01-14 14:24:21 +00:00
if ( l )
2018-01-13 01:17:30 +00:00
cur = l - > getBegin ( ) ;
2010-01-23 19:44:49 +00:00
}
Track * TrackListIterator : : StartWith ( Track * val )
{
if ( val = = NULL ) {
return First ( ) ;
}
if ( l = = NULL ) {
return NULL ;
}
2017-07-10 15:48:11 +00:00
if ( val - > mList . lock ( ) = = NULL )
2016-03-01 23:31:51 +00:00
return nullptr ;
2010-01-23 19:44:49 +00:00
2016-03-01 23:31:51 +00:00
cur = val - > GetNode ( ) ;
2016-03-13 15:08:21 +00:00
return cur - > get ( ) ;
2010-01-23 19:44:49 +00:00
}
Track * TrackListIterator : : First ( TrackList * val )
{
if ( val ! = NULL ) {
l = val ;
}
if ( l = = NULL ) {
return NULL ;
}
2018-01-13 01:17:30 +00:00
cur = l - > getBegin ( ) ;
2010-01-23 19:44:49 +00:00
2016-03-01 23:31:51 +00:00
if ( ! l - > isNull ( cur ) ) {
2016-03-13 15:08:21 +00:00
return cur - > get ( ) ;
2010-01-23 19:44:49 +00:00
}
2018-01-13 01:17:30 +00:00
return nullptr ;
2010-01-23 19:44:49 +00:00
}
Track * TrackListIterator : : Last ( bool skiplinked )
{
if ( l = = NULL ) {
return NULL ;
}
2018-01-13 01:17:30 +00:00
cur = l - > getPrev ( l - > getEnd ( ) ) ;
if ( l - > isNull ( cur ) )
return nullptr ;
2010-01-23 19:44:49 +00:00
// With skiplinked set, we won't return the second channel of a linked pair
2018-01-13 01:17:30 +00:00
if ( skiplinked ) {
auto prev = l - > getPrev ( cur ) ;
if ( ! l - > isNull ( prev ) & &
! ( * cur ) - > GetLinked ( ) & &
( * cur ) - > GetLink ( )
)
cur = prev ;
}
2010-01-23 19:44:49 +00:00
2016-03-13 15:08:21 +00:00
return cur - > get ( ) ;
2010-01-23 19:44:49 +00:00
}
2010-08-25 23:24:02 +00:00
Track * TrackListIterator : : Next ( bool skipLinked )
2010-01-23 19:44:49 +00:00
{
2016-03-01 23:31:51 +00:00
# ifdef DEBUG_TLI // if we are debugging this bit
2010-01-23 19:44:49 +00:00
wxASSERT_MSG ( ( ! cur | | ( * l ) . Contains ( ( * cur ) . t ) ) , wxT ( " cur invalid at start of Next(). List changed since iterator created? " ) ) ; // check that cur is in the list
2016-03-01 23:31:51 +00:00
# endif
2010-01-23 19:44:49 +00:00
2016-03-01 23:31:51 +00:00
if ( ! l | | l - > isNull ( cur ) )
return nullptr ;
if ( skipLinked & &
2018-01-13 01:17:30 +00:00
( * cur ) - > GetLinked ( ) )
cur = l - > getNext ( cur ) ;
2010-01-23 19:44:49 +00:00
# ifdef DEBUG_TLI // if we are debugging this bit
wxASSERT_MSG ( ( ! cur | | ( * l ) . Contains ( ( * cur ) . t ) ) , wxT ( " cur invalid after skipping linked tracks. " ) ) ; // check that cur is in the list
# endif
2018-01-13 01:17:30 +00:00
if ( ! l - > isNull ( cur ) )
cur = l - > getNext ( cur ) ;
2010-01-23 19:44:49 +00:00
# ifdef DEBUG_TLI // if we are debugging this bit
wxASSERT_MSG ( ( ! cur | | ( * l ) . Contains ( ( * cur ) . t ) ) , wxT ( " cur invalid after moving to next track. " ) ) ; // check that cur is in the list if it is not null
# endif
2018-01-13 01:17:30 +00:00
if ( ! l - > isNull ( cur ) )
2016-03-13 15:08:21 +00:00
return cur - > get ( ) ;
2010-01-23 19:44:49 +00:00
2018-01-13 01:17:30 +00:00
return nullptr ;
2010-01-23 19:44:49 +00:00
}
Track * TrackListIterator : : Prev ( bool skiplinked )
{
2016-03-01 23:31:51 +00:00
if ( ! l | | l - > isNull ( cur ) )
return nullptr ;
2018-01-13 01:17:30 +00:00
cur = l - > getPrev ( cur ) ;
if ( l - > isNull ( cur ) )
2016-03-01 23:31:51 +00:00
return nullptr ;
2018-01-15 02:36:33 +00:00
2018-01-13 01:17:30 +00:00
if ( skiplinked ) {
auto prev = l - > getPrev ( cur ) ;
if ( ! l - > isNull ( prev ) & & ( * prev ) - > GetLinked ( ) )
2016-03-01 23:31:51 +00:00
cur = prev ;
}
2010-01-23 19:44:49 +00:00
2016-03-13 15:08:21 +00:00
return cur - > get ( ) ;
2010-01-23 19:44:49 +00:00
}
2018-01-14 19:01:04 +00:00
Track * TrackListIterator : : operator * ( ) const
{
if ( ! l | | l - > isNull ( cur ) )
return nullptr ;
else
return cur - > get ( ) ;
}
2016-03-01 21:28:59 +00:00
Track * TrackListIterator : : RemoveCurrent ( )
2010-01-23 19:44:49 +00:00
{
2018-01-13 01:17:30 +00:00
if ( ! l | | l - > isNull ( cur ) )
2016-03-01 23:31:51 +00:00
return nullptr ;
2014-06-03 20:30:19 +00:00
2018-01-13 01:17:30 +00:00
cur = l - > Remove ( cur - > get ( ) ) ;
2010-01-23 19:44:49 +00:00
# ifdef DEBUG_TLI // if we are debugging this bit
wxASSERT_MSG ( ( ! cur | | ( * l ) . Contains ( ( * cur ) . t ) ) , wxT ( " cur invalid after deletion of track. " ) ) ; // check that cur is in the list
# endif
2018-01-13 01:17:30 +00:00
if ( ! l - > isNull ( cur ) )
2016-03-13 15:08:21 +00:00
return cur - > get ( ) ;
2010-01-23 19:44:49 +00:00
2018-01-13 01:17:30 +00:00
return nullptr ;
2010-01-23 19:44:49 +00:00
}
2018-01-14 19:01:04 +00:00
bool TrackListIterator : : operator = = ( const TrackListIterator & other ) const
{
2018-01-14 20:37:21 +00:00
// Order these steps so as not to use operator == on default-constructed
// std::list::iterator -- that crashes in the MSVC 2013 standard library
2018-01-14 19:01:04 +00:00
bool isEnd = ! l | | l - > isNull ( cur ) ;
2018-01-14 20:37:21 +00:00
bool otherIsEnd = ! other . l | | other . l - > isNull ( other . cur ) ;
2018-01-14 19:01:04 +00:00
2018-01-14 20:37:21 +00:00
return ( isEnd = = otherIsEnd & & ( isEnd | | cur = = other . cur ) ) ;
2018-01-14 19:01:04 +00:00
}
2010-01-23 19:44:49 +00:00
//
// TrackListCondIterator (base class for iterators that iterate over all tracks
// that meet a condition)
//
Track * TrackListCondIterator : : StartWith ( Track * val )
{
Track * t = TrackListIterator : : StartWith ( val ) ;
if ( t & & ! this - > Condition ( t ) )
2018-01-13 01:17:30 +00:00
return nullptr ;
2010-01-23 19:44:49 +00:00
return t ;
}
Track * TrackListCondIterator : : First ( TrackList * val )
{
Track * t = TrackListIterator : : First ( val ) ;
while ( t & & ! this - > Condition ( t ) ) {
t = TrackListIterator : : Next ( ) ;
}
return t ;
}
Track * TrackListCondIterator : : Next ( bool skiplinked )
{
while ( Track * t = TrackListIterator : : Next ( skiplinked ) ) {
if ( this - > Condition ( t ) ) {
return t ;
}
}
return NULL ;
}
Track * TrackListCondIterator : : Prev ( bool skiplinked )
{
while ( Track * t = TrackListIterator : : Prev ( skiplinked ) )
{
if ( this - > Condition ( t ) ) {
return t ;
}
}
return NULL ;
}
Track * TrackListCondIterator : : Last ( bool skiplinked )
{
Track * t = TrackListIterator : : Last ( skiplinked ) ;
while ( t & & ! this - > Condition ( t ) ) {
t = TrackListIterator : : Prev ( skiplinked ) ;
}
return t ;
}
// TrackListOfKindIterator
TrackListOfKindIterator : : TrackListOfKindIterator ( int kind , TrackList * val )
: TrackListCondIterator ( val )
{
this - > kind = kind ;
}
bool TrackListOfKindIterator : : Condition ( Track * t )
{
return kind = = Track : : All | | t - > GetKind ( ) = = kind ;
}
//SelectedTrackListOfKindIterator
bool SelectedTrackListOfKindIterator : : Condition ( Track * t )
{
return TrackListOfKindIterator : : Condition ( t ) & & t - > GetSelected ( ) ;
}
// VisibleTrackIterator
//
// Based on TrackListIterator returns only the currently visible tracks.
//
VisibleTrackIterator : : VisibleTrackIterator ( AudacityProject * project )
: TrackListCondIterator ( project - > GetTracks ( ) )
{
mProject = project ;
mPanelRect . SetTop ( mProject - > mViewInfo . vpos ) ;
mPanelRect . SetSize ( mProject - > GetTPTracksUsableArea ( ) ) ;
}
bool VisibleTrackIterator : : Condition ( Track * t )
{
wxRect r ( 0 , t - > GetY ( ) , 1 , t - > GetHeight ( ) ) ;
2017-06-17 05:26:36 +00:00
if ( r . Intersects ( mPanelRect ) )
return true ;
auto partner = t - > GetLink ( ) ;
if ( partner & & t - > GetLinked ( ) )
return Condition ( partner ) ;
2017-06-25 20:00:30 +00:00
return false ;
2010-01-23 19:44:49 +00:00
}
2010-08-11 23:56:50 +00:00
// SyncLockedTracksIterator
2010-01-23 19:44:49 +00:00
//
// Based on TrackListIterator returns only tracks belonging to the group
// in which the starting track is a member.
//
2010-08-11 23:56:50 +00:00
SyncLockedTracksIterator : : SyncLockedTracksIterator ( TrackList * val )
2010-02-12 16:05:02 +00:00
: TrackListIterator ( val ) ,
mInLabelSection ( false )
2010-01-23 19:44:49 +00:00
{
}
2016-11-04 04:10:21 +00:00
namespace {
2017-03-31 00:48:27 +00:00
inline bool IsSyncLockableNonLabelTrack ( const Track * pTrack )
2016-11-04 04:10:21 +00:00
{
2017-03-31 00:48:27 +00:00
return nullptr ! = dynamic_cast < const AudioTrack * > ( pTrack ) ;
2016-11-04 04:10:21 +00:00
}
}
2016-02-23 19:06:36 +00:00
Track * SyncLockedTracksIterator : : StartWith ( Track * member )
2010-01-23 19:44:49 +00:00
{
Track * t = NULL ;
2016-11-04 04:10:21 +00:00
// A sync-locked group consists of any positive number of wave (or note)
// tracks followed by any
2010-02-12 16:05:02 +00:00
// non-negative number of label tracks. Step back through any label tracks,
// and then through the wave tracks above them.
2010-01-23 19:44:49 +00:00
2010-02-12 16:05:02 +00:00
while ( member & & member - > GetKind ( ) = = Track : : Label ) {
member = l - > GetPrev ( member ) ;
2010-01-23 19:44:49 +00:00
}
2016-11-04 04:10:21 +00:00
while ( member & & IsSyncLockableNonLabelTrack ( member ) ) {
2010-01-23 19:44:49 +00:00
t = member ;
member = l - > GetPrev ( member ) ;
}
2010-02-12 16:05:02 +00:00
// Make it current (if t is still NULL there are no wave tracks, so we're
2010-08-11 23:56:50 +00:00
// not in a sync-locked group).
2010-01-23 19:44:49 +00:00
if ( t )
2016-03-01 23:31:51 +00:00
cur = t - > GetNode ( ) ;
2010-01-23 19:44:49 +00:00
2010-02-12 16:05:02 +00:00
mInLabelSection = false ;
2010-01-23 19:44:49 +00:00
return t ;
}
2016-11-04 04:10:21 +00:00
bool SyncLockedTracksIterator : : IsGoodNextTrack ( const Track * t ) const
{
if ( ! t )
return false ;
const bool isLabel = ( t - > GetKind ( ) = = Track : : Label ) ;
const bool isSyncLockable = IsSyncLockableNonLabelTrack ( t ) ;
if ( ! ( isLabel | | isSyncLockable ) ) {
return false ;
}
if ( mInLabelSection & & ! isLabel ) {
return false ;
}
return true ;
}
2010-08-11 23:56:50 +00:00
Track * SyncLockedTracksIterator : : Next ( bool skiplinked )
2010-01-23 19:44:49 +00:00
{
Track * t = TrackListIterator : : Next ( skiplinked ) ;
2010-02-12 16:05:02 +00:00
if ( ! t )
2018-01-13 01:17:30 +00:00
return nullptr ;
2010-02-12 16:05:02 +00:00
2016-11-04 04:10:21 +00:00
if ( ! IsGoodNextTrack ( t ) ) {
2018-01-13 01:17:30 +00:00
cur = l - > getEnd ( ) ;
return nullptr ;
2010-01-23 19:44:49 +00:00
}
2016-11-04 04:10:21 +00:00
mInLabelSection = ( t - > GetKind ( ) = = Track : : Label ) ;
2010-02-12 16:05:02 +00:00
2010-01-23 19:44:49 +00:00
return t ;
}
2010-08-11 23:56:50 +00:00
Track * SyncLockedTracksIterator : : Prev ( bool skiplinked )
2010-01-23 19:44:49 +00:00
{
Track * t = TrackListIterator : : Prev ( skiplinked ) ;
2010-02-12 16:05:02 +00:00
//
2010-08-11 23:56:50 +00:00
// Ways to end a sync-locked group in reverse
2010-02-12 16:05:02 +00:00
//
// Beginning of tracks
if ( ! t )
2018-01-13 01:17:30 +00:00
return nullptr ;
2010-02-12 16:05:02 +00:00
2016-11-04 04:10:21 +00:00
const bool isLabel = ( t - > GetKind ( ) = = Track : : Label ) ;
const bool isSyncLockable = IsSyncLockableNonLabelTrack ( t ) ;
if ( ! ( isLabel | | isSyncLockable ) ) {
2018-01-13 01:17:30 +00:00
cur = l - > getEnd ( ) ;
return nullptr ;
2010-02-12 16:05:02 +00:00
}
2016-11-04 04:10:21 +00:00
if ( ! mInLabelSection & & isLabel ) {
2018-01-13 01:17:30 +00:00
cur = l - > getEnd ( ) ;
return nullptr ;
2010-01-23 19:44:49 +00:00
}
2016-11-04 04:10:21 +00:00
mInLabelSection = isLabel ;
2010-02-12 16:05:02 +00:00
2010-01-23 19:44:49 +00:00
return t ;
}
2010-08-11 23:56:50 +00:00
Track * SyncLockedTracksIterator : : Last ( bool skiplinked )
2010-01-23 19:44:49 +00:00
{
2018-01-13 01:17:30 +00:00
if ( ! l | | l - > isNull ( cur ) )
return nullptr ;
2010-01-23 19:44:49 +00:00
2016-03-13 15:08:21 +00:00
Track * t = cur - > get ( ) ;
2010-01-23 19:44:49 +00:00
2016-11-04 04:10:21 +00:00
while ( const auto next = l - > GetNext ( t , skiplinked ) ) {
if ( ! IsGoodNextTrack ( next ) )
2010-02-12 16:05:02 +00:00
break ;
2010-01-23 19:44:49 +00:00
t = Next ( skiplinked ) ;
}
return t ;
}
// TrackList
//
2017-06-25 05:33:31 +00:00
// The TrackList sends events whenever certain updates occur to the list it
2010-01-23 19:44:49 +00:00
// is managing. Any other classes that may be interested in get these updates
2018-01-31 15:08:42 +00:00
// should use TrackList::Connect() or TrackList::Bind().
2010-01-23 19:44:49 +00:00
//
2018-02-12 23:09:20 +00:00
wxDEFINE_EVENT ( EVT_TRACKLIST_PERMUTED , wxCommandEvent ) ;
wxDEFINE_EVENT ( EVT_TRACKLIST_RESIZING , wxCommandEvent ) ;
wxDEFINE_EVENT ( EVT_TRACKLIST_DELETION , wxCommandEvent ) ;
2010-01-23 19:44:49 +00:00
2018-01-11 00:28:16 +00:00
// same value as in the default constructed TrackId:
long TrackList : : sCounter = - 1 ;
2016-03-13 14:34:44 +00:00
TrackList : : TrackList ( )
2010-01-23 19:44:49 +00:00
: wxEvtHandler ( )
{
}
2017-07-15 01:47:47 +00:00
// Factory function
std : : shared_ptr < TrackList > TrackList : : Create ( )
2016-03-07 11:43:42 +00:00
{
2017-07-15 01:47:47 +00:00
std : : shared_ptr < TrackList > result { safenew TrackList { } } ;
result - > mSelf = result ;
return result ;
2016-03-07 11:43:42 +00:00
}
TrackList & TrackList : : operator = ( TrackList & & that )
{
if ( this ! = & that ) {
2016-03-13 14:34:44 +00:00
this - > Clear ( ) ;
2016-03-07 11:43:42 +00:00
Swap ( that ) ;
}
return * this ;
}
void TrackList : : Swap ( TrackList & that )
{
2017-08-24 18:31:51 +00:00
auto SwapLOTs = [ ] (
ListOfTracks & a , const std : : weak_ptr < TrackList > & aSelf ,
ListOfTracks & b , const std : : weak_ptr < TrackList > & bSelf )
{
a . swap ( b ) ;
for ( auto it = a . begin ( ) , last = a . end ( ) ; it ! = last ; + + it )
( * it ) - > SetOwner ( aSelf , it ) ;
for ( auto it = b . begin ( ) , last = b . end ( ) ; it ! = last ; + + it )
( * it ) - > SetOwner ( bSelf , it ) ;
} ;
SwapLOTs ( * this , mSelf , that , that . mSelf ) ;
SwapLOTs ( this - > mPendingUpdates , mSelf , that . mPendingUpdates , that . mSelf ) ;
mUpdaters . swap ( that . mUpdaters ) ;
2016-03-07 11:43:42 +00:00
}
2010-01-23 19:44:49 +00:00
TrackList : : ~ TrackList ( )
{
2016-06-28 03:38:42 +00:00
Clear ( false ) ;
2010-01-23 19:44:49 +00:00
}
2016-03-01 23:31:51 +00:00
void TrackList : : RecalcPositions ( TrackNodePointer node )
2010-01-23 19:44:49 +00:00
{
2018-01-13 01:17:30 +00:00
if ( isNull ( node ) )
2016-03-01 23:31:51 +00:00
return ;
2018-01-13 01:17:30 +00:00
2010-01-23 19:44:49 +00:00
Track * t ;
int i = 0 ;
int y = 0 ;
2018-01-13 01:17:30 +00:00
auto prev = getPrev ( node ) ;
if ( ! isNull ( prev ) ) {
2016-03-13 15:08:21 +00:00
t = prev - > get ( ) ;
2010-01-23 19:44:49 +00:00
i = t - > GetIndex ( ) + 1 ;
y = t - > GetY ( ) + t - > GetHeight ( ) ;
}
2016-03-01 23:31:51 +00:00
const auto theEnd = end ( ) ;
2018-01-14 19:01:04 +00:00
for ( auto n = TrackListIterator { this , node } ; n ! = theEnd ; + + n ) {
t = * n ;
2010-01-23 19:44:49 +00:00
t - > SetIndex ( i + + ) ;
2018-01-12 01:57:37 +00:00
t - > DoSetY ( y ) ;
2010-01-23 19:44:49 +00:00
y + = t - > GetHeight ( ) ;
}
2017-08-24 18:31:51 +00:00
UpdatePendingTracks ( ) ;
2010-01-23 19:44:49 +00:00
}
2017-06-26 17:40:19 +00:00
void TrackList : : PermutationEvent ( )
{
auto e = std : : make_unique < wxCommandEvent > ( EVT_TRACKLIST_PERMUTED ) ;
// wxWidgets will own the event object
QueueEvent ( e . release ( ) ) ;
}
2017-06-25 05:33:31 +00:00
void TrackList : : DeletionEvent ( )
2010-01-23 19:44:49 +00:00
{
2017-06-25 07:09:04 +00:00
auto e = std : : make_unique < wxCommandEvent > ( EVT_TRACKLIST_DELETION ) ;
// wxWidgets will own the event object
QueueEvent ( e . release ( ) ) ;
2010-01-23 19:44:49 +00:00
}
2017-06-25 05:33:31 +00:00
void TrackList : : ResizingEvent ( TrackNodePointer node )
2010-01-23 19:44:49 +00:00
{
2017-11-04 17:33:11 +00:00
auto e = std : : make_unique < TrackListEvent > ( EVT_TRACKLIST_RESIZING ) ;
e - > mpTrack = * node ;
2017-06-25 07:09:04 +00:00
// wxWidgets will own the event object
QueueEvent ( e . release ( ) ) ;
2010-01-23 19:44:49 +00:00
}
2016-03-01 22:34:45 +00:00
void TrackList : : Permute ( const std : : vector < TrackNodePointer > & permutation )
{
for ( const auto iter : permutation ) {
2018-01-14 19:01:04 +00:00
ListOfTracks : : value_type track = std : : move ( * iter ) ;
2016-03-01 22:34:45 +00:00
erase ( iter ) ;
2016-03-13 15:08:21 +00:00
Track * pTrack = track . get ( ) ;
2018-01-14 19:01:04 +00:00
pTrack - > SetOwner ( mSelf , insert ( ListOfTracks : : end ( ) , std : : move ( track ) ) ) ;
2016-03-01 22:34:45 +00:00
}
2018-01-13 01:17:30 +00:00
auto n = getBegin ( ) ;
2016-03-01 22:34:45 +00:00
RecalcPositions ( n ) ;
2017-06-26 17:40:19 +00:00
PermutationEvent ( ) ;
2016-03-01 22:34:45 +00:00
}
2018-01-11 00:28:16 +00:00
Track * TrackList : : FindById ( TrackId id )
{
// Linear search. Tracks in a project are usually very few.
// Search only the non-pending tracks.
auto it = std : : find_if ( ListOfTracks : : begin ( ) , ListOfTracks : : end ( ) ,
[ = ] ( const ListOfTracks : : value_type & ptr ) { return ptr - > GetId ( ) = = id ; } ) ;
if ( it = = ListOfTracks : : end ( ) )
return { } ;
return it - > get ( ) ;
}
2016-03-13 15:08:21 +00:00
template < typename TrackKind >
Track * TrackList : : Add ( std : : unique_ptr < TrackKind > & & t )
2010-01-23 19:44:49 +00:00
{
2016-03-13 15:08:21 +00:00
Track * pTrack ;
2018-01-14 19:01:04 +00:00
push_back ( ListOfTracks : : value_type ( pTrack = t . release ( ) ) ) ;
2018-01-13 01:17:30 +00:00
auto n = getPrev ( getEnd ( ) ) ;
2017-07-10 15:48:11 +00:00
pTrack - > SetOwner ( mSelf , n ) ;
2018-01-11 00:28:16 +00:00
pTrack - > SetId ( TrackId { + + sCounter } ) ;
2010-01-23 19:44:49 +00:00
RecalcPositions ( n ) ;
2017-06-25 05:33:31 +00:00
ResizingEvent ( n ) ;
2016-03-13 15:08:21 +00:00
return back ( ) . get ( ) ;
2010-01-23 19:44:49 +00:00
}
2016-03-13 15:08:21 +00:00
// Make instantiations for the linker to find
template Track * TrackList : : Add < TimeTrack > ( std : : unique_ptr < TimeTrack > & & ) ;
2016-04-28 07:25:22 +00:00
# if defined(USE_MIDI)
2016-03-13 15:08:21 +00:00
template Track * TrackList : : Add < NoteTrack > ( std : : unique_ptr < NoteTrack > & & ) ;
2016-04-28 07:25:22 +00:00
# endif
2016-03-13 15:08:21 +00:00
template Track * TrackList : : Add < WaveTrack > ( std : : unique_ptr < WaveTrack > & & ) ;
template Track * TrackList : : Add < LabelTrack > ( std : : unique_ptr < LabelTrack > & & ) ;
2017-02-07 18:11:33 +00:00
template Track * TrackList : : Add < Track > ( std : : unique_ptr < Track > & & ) ;
2016-03-13 15:08:21 +00:00
template < typename TrackKind >
Track * TrackList : : AddToHead ( std : : unique_ptr < TrackKind > & & t )
2010-01-23 19:44:49 +00:00
{
2016-03-13 15:08:21 +00:00
Track * pTrack ;
2018-01-14 19:01:04 +00:00
push_front ( ListOfTracks : : value_type ( pTrack = t . release ( ) ) ) ;
2018-01-13 01:17:30 +00:00
auto n = getBegin ( ) ;
2017-07-10 15:48:11 +00:00
pTrack - > SetOwner ( mSelf , n ) ;
2018-01-11 00:28:16 +00:00
pTrack - > SetId ( TrackId { + + sCounter } ) ;
2016-03-01 23:31:51 +00:00
RecalcPositions ( n ) ;
2017-06-25 05:33:31 +00:00
ResizingEvent ( n ) ;
2016-03-13 15:08:21 +00:00
return front ( ) . get ( ) ;
2010-01-23 19:44:49 +00:00
}
2016-03-13 15:08:21 +00:00
// Make instantiations for the linker to find
template Track * TrackList : : AddToHead < TimeTrack > ( std : : unique_ptr < TimeTrack > & & ) ;
template < typename TrackKind >
Track * TrackList : : Add ( std : : shared_ptr < TrackKind > & & t )
2010-01-23 19:44:49 +00:00
{
2016-03-13 15:08:21 +00:00
push_back ( t ) ;
2018-01-13 01:17:30 +00:00
auto n = getPrev ( getEnd ( ) ) ;
2017-07-10 15:48:11 +00:00
t - > SetOwner ( mSelf , n ) ;
2018-01-11 00:28:16 +00:00
t - > SetId ( TrackId { + + sCounter } ) ;
2016-03-13 15:08:21 +00:00
RecalcPositions ( n ) ;
2017-06-25 05:33:31 +00:00
ResizingEvent ( n ) ;
2016-03-13 15:08:21 +00:00
return back ( ) . get ( ) ;
}
// Make instantiations for the linker to find
template Track * TrackList : : Add < Track > ( std : : shared_ptr < Track > & & ) ;
template Track * TrackList : : Add < WaveTrack > ( std : : shared_ptr < WaveTrack > & & ) ;
2018-01-14 19:01:04 +00:00
auto TrackList : : Replace ( Track * t , ListOfTracks : : value_type & & with ) - >
ListOfTracks : : value_type
2016-03-13 15:08:21 +00:00
{
2018-01-14 19:01:04 +00:00
ListOfTracks : : value_type holder ;
2016-03-01 00:54:21 +00:00
if ( t & & with ) {
2016-03-01 23:31:51 +00:00
auto node = t - > GetNode ( ) ;
2017-07-10 15:48:11 +00:00
t - > SetOwner ( { } , { } ) ;
2017-06-23 00:29:32 +00:00
2016-03-13 15:08:21 +00:00
holder = std : : move ( * node ) ;
2016-02-27 22:29:21 +00:00
2016-03-13 15:08:21 +00:00
Track * pTrack = with . get ( ) ;
* node = std : : move ( with ) ;
2017-07-10 15:48:11 +00:00
pTrack - > SetOwner ( mSelf , node ) ;
2018-01-11 00:28:16 +00:00
pTrack - > SetId ( t - > GetId ( ) ) ;
2010-01-23 19:44:49 +00:00
RecalcPositions ( node ) ;
2017-06-01 18:19:52 +00:00
2017-06-25 05:33:31 +00:00
DeletionEvent ( ) ;
ResizingEvent ( node ) ;
2010-01-23 19:44:49 +00:00
}
2016-03-13 15:08:21 +00:00
return holder ;
2010-01-23 19:44:49 +00:00
}
2016-03-01 21:28:59 +00:00
TrackNodePointer TrackList : : Remove ( Track * t )
2010-01-23 19:44:49 +00:00
{
2018-01-13 01:17:30 +00:00
auto result = getEnd ( ) ;
2010-01-23 19:44:49 +00:00
if ( t ) {
2016-03-01 23:31:51 +00:00
auto node = t - > GetNode ( ) ;
2017-07-10 15:48:11 +00:00
t - > SetOwner ( { } , { } ) ;
2010-01-23 19:44:49 +00:00
2018-01-13 01:17:30 +00:00
if ( ! isNull ( node ) ) {
2018-01-14 19:01:04 +00:00
ListOfTracks : : value_type holder = std : : move ( * node ) ;
2017-06-01 18:19:52 +00:00
2018-01-13 01:17:30 +00:00
result = getNext ( node ) ;
erase ( node ) ;
if ( ! isNull ( result ) )
2016-03-01 23:31:51 +00:00
RecalcPositions ( result ) ;
2010-01-23 19:44:49 +00:00
2017-06-25 05:33:31 +00:00
DeletionEvent ( ) ;
2010-01-23 19:44:49 +00:00
}
}
2016-03-01 23:31:51 +00:00
return result ;
2010-01-23 19:44:49 +00:00
}
2016-06-28 03:38:42 +00:00
void TrackList : : Clear ( bool sendEvent )
2010-01-23 19:44:49 +00:00
{
2017-07-14 14:03:11 +00:00
// Null out the back-pointers in tracks, in case there are outstanding
// shared_ptrs to those tracks.
for ( auto pTrack : * this )
pTrack - > SetOwner ( { } , { } ) ;
2017-08-24 18:31:51 +00:00
for ( auto pTrack : mPendingUpdates )
pTrack - > SetOwner ( { } , { } ) ;
2017-07-14 14:03:11 +00:00
2017-06-01 18:19:52 +00:00
ListOfTracks tempList ;
tempList . swap ( * this ) ;
2017-08-24 18:31:51 +00:00
ListOfTracks updating ;
updating . swap ( mPendingUpdates ) ;
mUpdaters . clear ( ) ;
2016-06-28 03:38:42 +00:00
if ( sendEvent )
2017-06-25 05:33:31 +00:00
DeletionEvent ( ) ;
2010-01-23 19:44:49 +00:00
}
void TrackList : : Select ( Track * t , bool selected /* = true */ )
{
if ( t ) {
2016-03-01 23:31:51 +00:00
const auto node = t - > GetNode ( ) ;
2018-01-13 01:17:30 +00:00
if ( ! isNull ( node ) ) {
t - > SetSelected ( selected ) ;
if ( t - > GetLinked ( ) ) {
auto next = getNext ( node ) ;
if ( ! isNull ( next ) )
( * next ) - > SetSelected ( selected ) ;
2010-01-23 19:44:49 +00:00
}
2018-01-13 01:17:30 +00:00
else {
auto prev = getPrev ( node ) ;
if ( ! isNull ( prev ) & & ( * prev ) - > GetLinked ( ) )
( * prev ) - > SetSelected ( selected ) ;
2010-01-23 19:44:49 +00:00
}
}
}
}
2011-04-17 23:54:30 +00:00
/// Return a track in the list that comes after Track t
2010-01-23 19:44:49 +00:00
Track * TrackList : : GetNext ( Track * t , bool linked ) const
{
if ( t ) {
2016-03-01 23:31:51 +00:00
auto node = t - > GetNode ( ) ;
2018-01-13 01:17:30 +00:00
if ( ! isNull ( node ) ) {
if ( linked & & t - > GetLinked ( ) )
node = getNext ( node ) ;
2010-01-23 19:44:49 +00:00
2018-01-13 01:17:30 +00:00
if ( ! isNull ( node ) )
node = getNext ( node ) ;
2014-06-03 20:30:19 +00:00
2018-01-13 01:17:30 +00:00
if ( ! isNull ( node ) )
2016-03-13 15:08:21 +00:00
return node - > get ( ) ;
2010-01-23 19:44:49 +00:00
}
}
2018-01-13 01:17:30 +00:00
return nullptr ;
2010-01-23 19:44:49 +00:00
}
Track * TrackList : : GetPrev ( Track * t , bool linked ) const
{
if ( t ) {
2018-01-13 01:17:30 +00:00
TrackNodePointer prev ;
2016-03-01 23:31:51 +00:00
auto node = t - > GetNode ( ) ;
2018-01-13 01:17:30 +00:00
if ( ! isNull ( node ) ) {
2016-03-01 23:31:51 +00:00
// linked is true and input track second in team?
2018-01-13 01:17:30 +00:00
if ( linked ) {
prev = getPrev ( node ) ;
if ( ! isNull ( prev ) & &
! t - > GetLinked ( ) & & t - > GetLink ( ) )
2010-01-23 19:44:49 +00:00
// Make it the first
2018-01-13 01:17:30 +00:00
node = prev ;
}
2010-01-23 19:44:49 +00:00
2018-01-13 01:17:30 +00:00
prev = getPrev ( node ) ;
if ( ! isNull ( prev ) ) {
2016-03-01 23:31:51 +00:00
// Back up once
2018-01-13 01:17:30 +00:00
node = prev ;
2010-01-23 19:44:49 +00:00
2016-03-01 23:31:51 +00:00
// Back up twice sometimes when linked is true
2018-01-13 01:17:30 +00:00
if ( linked ) {
prev = getPrev ( node ) ;
if ( ! isNull ( prev ) & &
! ( * node ) - > GetLinked ( ) & & ( * node ) - > GetLink ( ) )
node = prev ;
}
2010-01-23 19:44:49 +00:00
2016-03-13 15:08:21 +00:00
return node - > get ( ) ;
2010-01-23 19:44:49 +00:00
}
}
}
2018-01-13 01:17:30 +00:00
return nullptr ;
2010-01-23 19:44:49 +00:00
}
/// For mono track height of track
2010-08-11 23:56:50 +00:00
/// For stereo track combined height of both channels.
2010-01-23 19:44:49 +00:00
int TrackList : : GetGroupHeight ( Track * t ) const
{
int height = t - > GetHeight ( ) ;
t = t - > GetLink ( ) ;
if ( t ) {
height + = t - > GetHeight ( ) ;
}
return height ;
}
bool TrackList : : CanMoveUp ( Track * t ) const
{
return GetPrev ( t , true ) ! = NULL ;
}
bool TrackList : : CanMoveDown ( Track * t ) const
{
return GetNext ( t , true ) ! = NULL ;
}
// This is used when you want to swap the track or pair of
// tracks in s1 with the track or pair of tracks in s2.
// The complication is that the tracks are stored in a single
// linked list, and pairs of tracks are marked only by a flag
// in one of the tracks.
2016-03-01 23:31:51 +00:00
void TrackList : : SwapNodes ( TrackNodePointer s1 , TrackNodePointer s2 )
2010-01-23 19:44:49 +00:00
{
// if a null pointer is passed in, we want to know about it
2016-03-01 23:31:51 +00:00
wxASSERT ( ! isNull ( s1 ) ) ;
wxASSERT ( ! isNull ( s2 ) ) ;
2010-01-23 19:44:49 +00:00
2016-03-01 23:31:51 +00:00
// Deal with first track in each team
Track * link ;
link = ( * s1 ) - > GetLink ( ) ;
bool linked1 = link ! = nullptr ;
if ( linked1 & & ! ( * s1 ) - > GetLinked ( ) ) {
s1 = link - > GetNode ( ) ;
2010-01-23 19:44:49 +00:00
}
2016-03-01 23:31:51 +00:00
link = ( * s2 ) - > GetLink ( ) ;
bool linked2 = link ! = nullptr ;
if ( linked2 & & ! ( * s2 ) - > GetLinked ( ) ) {
s2 = link - > GetNode ( ) ;
2010-01-23 19:44:49 +00:00
}
2016-03-01 23:31:51 +00:00
// Safety check...
if ( s1 = = s2 )
return ;
2010-01-23 19:44:49 +00:00
2016-03-01 23:31:51 +00:00
// Be sure s1 is the earlier iterator
if ( ( * s1 ) - > GetIndex ( ) > = ( * s2 ) - > GetIndex ( ) ) {
std : : swap ( s1 , s2 ) ;
std : : swap ( linked1 , linked2 ) ;
2010-01-23 19:44:49 +00:00
}
2016-03-01 23:31:51 +00:00
// Remove tracks
2018-01-14 19:01:04 +00:00
ListOfTracks : : value_type save11 = std : : move ( * s1 ) , save12 { } ;
2016-03-01 23:31:51 +00:00
s1 = erase ( s1 ) ;
if ( linked1 ) {
wxASSERT ( s1 ! = s2 ) ;
2016-03-13 15:08:21 +00:00
save12 = std : : move ( * s1 ) , s1 = erase ( s1 ) ;
2010-01-23 19:44:49 +00:00
}
2016-03-01 23:31:51 +00:00
const bool same = ( s1 = = s2 ) ;
2010-01-23 19:44:49 +00:00
2018-01-14 19:01:04 +00:00
ListOfTracks : : value_type save21 = std : : move ( * s2 ) , save22 { } ;
2016-03-01 23:31:51 +00:00
s2 = erase ( s2 ) ;
if ( linked2 )
2016-03-13 15:08:21 +00:00
save22 = std : : move ( * s2 ) , s2 = erase ( s2 ) ;
2010-01-23 19:44:49 +00:00
2016-03-01 23:31:51 +00:00
if ( same )
// We invalidated s1!
s1 = s2 ;
// Reinsert them
2016-03-13 15:08:21 +00:00
Track * pTrack ;
2016-03-01 23:31:51 +00:00
if ( save22 )
2017-07-10 15:48:11 +00:00
pTrack = save22 . get ( ) , pTrack - > SetOwner ( mSelf , s1 = insert ( s1 , std : : move ( save22 ) ) ) ;
pTrack = save21 . get ( ) , pTrack - > SetOwner ( mSelf , s1 = insert ( s1 , std : : move ( save21 ) ) ) ;
2010-01-23 19:44:49 +00:00
2016-03-01 23:31:51 +00:00
if ( save12 )
2017-07-10 15:48:11 +00:00
pTrack = save12 . get ( ) , pTrack - > SetOwner ( mSelf , s2 = insert ( s2 , std : : move ( save12 ) ) ) ;
pTrack = save11 . get ( ) , pTrack - > SetOwner ( mSelf , s2 = insert ( s2 , std : : move ( save11 ) ) ) ;
2016-03-01 23:31:51 +00:00
// Now correct the Index in the tracks, and other things
2010-01-23 19:44:49 +00:00
RecalcPositions ( s1 ) ;
2017-06-26 17:40:19 +00:00
PermutationEvent ( ) ;
2010-01-23 19:44:49 +00:00
}
bool TrackList : : MoveUp ( Track * t )
{
if ( t ) {
Track * p = GetPrev ( t , true ) ;
if ( p ) {
2016-03-01 23:31:51 +00:00
SwapNodes ( p - > GetNode ( ) , t - > GetNode ( ) ) ;
2010-01-23 19:44:49 +00:00
return true ;
}
}
return false ;
}
bool TrackList : : MoveDown ( Track * t )
{
if ( t ) {
Track * n = GetNext ( t , true ) ;
if ( n ) {
2016-03-01 23:31:51 +00:00
SwapNodes ( t - > GetNode ( ) , n - > GetNode ( ) ) ;
2010-01-23 19:44:49 +00:00
return true ;
}
}
return false ;
}
2016-05-22 03:19:09 +00:00
bool TrackList : : Contains ( const Track * t ) const
2010-01-23 19:44:49 +00:00
{
2018-01-14 19:01:04 +00:00
return make_iterator_range ( * this ) . contains ( t ) ;
2010-01-23 19:44:49 +00:00
}
2018-01-14 21:55:49 +00:00
bool TrackList : : empty ( ) const
2010-01-23 19:44:49 +00:00
{
2018-01-14 21:55:49 +00:00
return begin ( ) = = end ( ) ;
2010-01-23 19:44:49 +00:00
}
2018-01-14 21:55:49 +00:00
size_t TrackList : : size ( ) const
2010-01-23 19:44:49 +00:00
{
int cnt = 0 ;
2018-01-14 21:55:49 +00:00
if ( ! empty ( ) )
2018-01-13 01:17:30 +00:00
cnt = getPrev ( getEnd ( ) ) - > get ( ) - > GetIndex ( ) + 1 ;
2010-01-23 19:44:49 +00:00
return cnt ;
}
TimeTrack * TrackList : : GetTimeTrack ( )
{
2016-03-01 23:31:51 +00:00
auto iter = std : : find_if ( begin ( ) , end ( ) ,
2018-01-14 19:01:04 +00:00
[ ] ( Track * t ) { return t - > GetKind ( ) = = Track : : Time ; }
2016-03-01 23:31:51 +00:00
) ;
if ( iter = = end ( ) )
return nullptr ;
else
2018-01-14 19:01:04 +00:00
return static_cast < TimeTrack * > ( * iter ) ;
2010-01-23 19:44:49 +00:00
}
2016-02-26 23:10:45 +00:00
const TimeTrack * TrackList : : GetTimeTrack ( ) const
{
return const_cast < TrackList * > ( this ) - > GetTimeTrack ( ) ;
}
2016-09-02 19:53:09 +00:00
unsigned TrackList : : GetNumExportChannels ( bool selectionOnly ) const
2010-01-23 19:44:49 +00:00
{
/* counters for tracks panned different places */
int numLeft = 0 ;
int numRight = 0 ;
2017-11-04 17:34:39 +00:00
//int numMono = 0;
2010-01-23 19:44:49 +00:00
/* track iteration kit */
2016-02-26 23:10:45 +00:00
const Track * tr ;
TrackListConstIterator iter ;
2010-01-23 19:44:49 +00:00
for ( tr = iter . First ( this ) ; tr ! = NULL ; tr = iter . Next ( ) ) {
2013-01-05 00:05:59 +00:00
// Want only unmuted wave tracks.
2017-03-29 15:25:05 +00:00
auto wt = static_cast < const WaveTrack * > ( tr ) ;
if ( ( tr - > GetKind ( ) ! = Track : : Wave ) | |
wt - > GetMute ( ) )
2010-01-23 19:44:49 +00:00
continue ;
// do we only want selected ones?
if ( selectionOnly & & ! ( tr - > GetSelected ( ) ) ) {
//want selected but this one is not
continue ;
}
// Found a left channel
if ( tr - > GetChannel ( ) = = Track : : LeftChannel ) {
numLeft + + ;
}
// Found a right channel
else if ( tr - > GetChannel ( ) = = Track : : RightChannel ) {
numRight + + ;
}
// Found a mono channel, but it may be panned
else if ( tr - > GetChannel ( ) = = Track : : MonoChannel ) {
float pan = ( ( WaveTrack * ) tr ) - > GetPan ( ) ;
// Figure out what kind of channel it should be
if ( pan = = - 1.0 ) { // panned hard left
numLeft + + ;
}
else if ( pan = = 1.0 ) { // panned hard right
numRight + + ;
}
else if ( pan = = 0 ) { // panned dead center
2017-11-04 17:34:39 +00:00
// numMono++;
2010-01-23 19:44:49 +00:00
}
else { // panned somewhere else
numLeft + + ;
numRight + + ;
}
}
}
// if there is stereo content, report 2, else report 1
if ( numRight > 0 | | numLeft > 0 ) {
return 2 ;
}
return 1 ;
}
2016-02-26 23:10:45 +00:00
namespace {
template < typename Array >
2018-01-14 19:01:04 +00:00
Array GetWaveTracks ( TrackListIterator p , const TrackListIterator end ,
2016-03-01 23:31:51 +00:00
bool selectionOnly , bool includeMuted )
2016-02-26 23:10:45 +00:00
{
Array waveTrackArray ;
2010-01-23 19:44:49 +00:00
2016-03-01 23:31:51 +00:00
for ( ; p ! = end ; + + p ) {
2016-03-13 15:08:21 +00:00
const auto & track = * p ;
2017-03-29 15:25:05 +00:00
auto wt = static_cast < const WaveTrack * > ( & * track ) ;
2016-03-01 23:31:51 +00:00
if ( track - > GetKind ( ) = = Track : : Wave & &
2017-03-29 15:25:05 +00:00
( includeMuted | | ! wt - > GetMute ( ) ) & &
2016-03-01 23:31:51 +00:00
( track - > GetSelected ( ) | | ! selectionOnly ) ) {
2018-01-14 19:01:04 +00:00
waveTrackArray . push_back ( Track : : Pointer < WaveTrack > ( track ) ) ;
2016-02-26 23:10:45 +00:00
}
2010-01-23 19:44:49 +00:00
}
2016-02-26 23:10:45 +00:00
return waveTrackArray ;
2010-01-23 19:44:49 +00:00
}
}
2016-02-26 23:10:45 +00:00
WaveTrackArray TrackList : : GetWaveTrackArray ( bool selectionOnly , bool includeMuted )
2010-01-23 19:44:49 +00:00
{
2016-03-31 14:36:13 +00:00
return GetWaveTracks < WaveTrackArray > ( begin ( ) , end ( ) , selectionOnly , includeMuted ) ;
2016-02-26 23:10:45 +00:00
}
2010-01-23 19:44:49 +00:00
2016-02-26 23:10:45 +00:00
WaveTrackConstArray TrackList : : GetWaveTrackConstArray ( bool selectionOnly , bool includeMuted ) const
{
2018-01-14 19:01:04 +00:00
auto list = const_cast < TrackList * > ( this ) ;
return GetWaveTracks < WaveTrackConstArray > (
list - > begin ( ) , list - > end ( ) , selectionOnly , includeMuted ) ;
2010-01-23 19:44:49 +00:00
}
# if defined(USE_MIDI)
NoteTrackArray TrackList : : GetNoteTrackArray ( bool selectionOnly )
{
NoteTrackArray noteTrackArray ;
2016-03-01 23:31:51 +00:00
for ( const auto & track : * this ) {
if ( track - > GetKind ( ) = = Track : : Note & &
( track - > GetSelected ( ) | | ! selectionOnly ) ) {
2018-01-14 19:01:04 +00:00
noteTrackArray . push_back ( Track : : Pointer < NoteTrack > ( track ) ) ;
2010-01-23 19:44:49 +00:00
}
}
return noteTrackArray ;
}
# endif
int TrackList : : GetHeight ( ) const
{
int height = 0 ;
2016-03-01 23:31:51 +00:00
if ( ! empty ( ) ) {
2018-01-13 01:17:30 +00:00
auto track = getPrev ( getEnd ( ) ) - > get ( ) ;
2016-03-01 23:31:51 +00:00
height = track - > GetY ( ) + track - > GetHeight ( ) ;
2010-01-23 19:44:49 +00:00
}
2018-01-12 17:08:09 +00:00
2010-01-23 19:44:49 +00:00
return height ;
}
2016-03-01 23:31:51 +00:00
namespace {
// Abstract the common pattern of the following three member functions
double doubleMin ( double a , double b ) { return std : : min ( a , b ) ; }
double doubleMax ( double a , double b ) { return std : : max ( a , b ) ; }
inline double Accumulate
2018-01-14 21:55:49 +00:00
( const TrackList & list ,
2016-03-01 23:31:51 +00:00
double ( Track : : * memfn ) ( ) const ,
double ( * combine ) ( double , double ) )
{
// Default the answer to zero for empty list
if ( list . empty ( ) ) {
return 0.0 ;
2010-01-23 19:44:49 +00:00
}
2016-03-01 23:31:51 +00:00
// Otherwise accumulate minimum or maximum of track values
auto iter = list . begin ( ) ;
double acc = ( * * iter + + . * memfn ) ( ) ;
return std : : accumulate ( iter , list . end ( ) , acc ,
2018-01-14 21:55:49 +00:00
[ = ] ( double acc , const Track * pTrack ) {
return combine ( acc , ( * pTrack . * memfn ) ( ) ) ;
2016-03-01 23:31:51 +00:00
} ) ;
2010-01-23 19:44:49 +00:00
}
2016-03-01 23:31:51 +00:00
}
2014-06-03 20:30:19 +00:00
2016-03-01 23:31:51 +00:00
double TrackList : : GetMinOffset ( ) const
{
return Accumulate ( * this , & Track : : GetOffset , doubleMin ) ;
2010-01-23 19:44:49 +00:00
}
double TrackList : : GetStartTime ( ) const
{
2016-03-01 23:31:51 +00:00
return Accumulate ( * this , & Track : : GetStartTime , doubleMin ) ;
2010-01-23 19:44:49 +00:00
}
double TrackList : : GetEndTime ( ) const
{
2016-03-01 23:31:51 +00:00
return Accumulate ( * this , & Track : : GetEndTime , doubleMax ) ;
2010-01-23 19:44:49 +00:00
}
2017-08-24 18:31:51 +00:00
std : : shared_ptr < Track >
TrackList : : RegisterPendingChangedTrack ( Updater updater , Track * src )
{
std : : shared_ptr < Track > pTrack ;
if ( src )
// convert from unique_ptr to shared_ptr
pTrack . reset ( src - > Duplicate ( ) . release ( ) ) ;
if ( pTrack ) {
mUpdaters . push_back ( updater ) ;
mPendingUpdates . push_back ( pTrack ) ;
auto n = mPendingUpdates . end ( ) ;
- - n ;
pTrack - > SetOwner ( mSelf , n ) ;
}
return pTrack ;
}
void TrackList : : RegisterPendingNewTrack ( const std : : shared_ptr < Track > & pTrack )
{
auto copy = pTrack ;
Add < Track > ( std : : move ( copy ) ) ;
pTrack - > SetId ( TrackId { } ) ;
}
void TrackList : : UpdatePendingTracks ( )
{
auto pUpdater = mUpdaters . begin ( ) ;
for ( const auto & pendingTrack : mPendingUpdates ) {
// Copy just a part of the track state, according to the update
// function
const auto & updater = * pUpdater ;
auto src = FindById ( pendingTrack - > GetId ( ) ) ;
if ( pendingTrack & & src ) {
if ( updater )
updater ( * pendingTrack , * src ) ;
pendingTrack - > DoSetY ( src - > GetY ( ) ) ;
pendingTrack - > DoSetHeight ( src - > GetHeight ( ) ) ;
pendingTrack - > DoSetMinimized ( src - > GetMinimized ( ) ) ;
pendingTrack - > DoSetLinked ( src - > GetLinked ( ) ) ;
}
+ + pUpdater ;
}
}
void TrackList : : ClearPendingTracks ( ListOfTracks * pAdded )
// NOFAIL-GUARANTEE
{
for ( const auto & pTrack : mPendingUpdates )
pTrack - > SetOwner ( { } , { } ) ;
mPendingUpdates . clear ( ) ;
mUpdaters . clear ( ) ;
if ( pAdded )
pAdded - > clear ( ) ;
for ( auto it = ListOfTracks : : begin ( ) , stop = ListOfTracks : : end ( ) ;
it ! = stop ; ) {
if ( it - > get ( ) - > GetId ( ) = = TrackId { } ) {
if ( pAdded )
pAdded - > push_back ( * it ) ;
it = erase ( it ) ;
}
else
+ + it ;
}
if ( ! empty ( ) )
RecalcPositions ( ListOfTracks : : begin ( ) ) ;
}
bool TrackList : : ApplyPendingTracks ( )
{
bool result = false ;
ListOfTracks additions ;
ListOfTracks updates ;
{
// Always clear, even if one of the update functions throws
auto cleanup = finally ( [ & ] { ClearPendingTracks ( & additions ) ; } ) ;
UpdatePendingTracks ( ) ;
updates . swap ( mPendingUpdates ) ;
}
// Remaining steps must be NOFAIL-GUARANTEE so that this function
// gives STRONG-GUARANTEE
std : : vector < std : : shared_ptr < Track > > reinstated ;
for ( auto & pendingTrack : updates ) {
if ( pendingTrack ) {
auto src = FindById ( pendingTrack - > GetId ( ) ) ;
if ( src )
this - > Replace ( src , std : : move ( pendingTrack ) ) , result = true ;
else
// Perhaps a track marked for pending changes got deleted by
// some other action. Recreate it so we don't lose the
// accumulated changes.
reinstated . push_back ( pendingTrack ) ;
}
}
// If there are tracks to reinstate, append them to the list.
for ( auto & pendingTrack : reinstated )
if ( pendingTrack )
this - > Add ( std : : move ( pendingTrack ) ) , result = true ;
// Put the pending added tracks back into the list, preserving their
// positions.
bool inserted = false ;
ListOfTracks : : iterator first ;
for ( auto & pendingTrack : additions ) {
if ( pendingTrack ) {
auto iter = ListOfTracks : : begin ( ) ;
std : : advance ( iter , pendingTrack - > GetIndex ( ) ) ;
iter = ListOfTracks : : insert ( iter , pendingTrack ) ;
pendingTrack - > SetOwner ( mSelf , iter ) ;
pendingTrack - > SetId ( TrackId { + + sCounter } ) ;
if ( ! inserted ) {
first = iter ;
inserted = true ;
}
}
}
if ( inserted ) {
RecalcPositions ( first ) ;
result = true ;
}
return result ;
}
std : : shared_ptr < Track > TrackList : : FindPendingChangedTrack ( TrackId id ) const
{
// Linear search. Tracks in a project are usually very few.
auto it = std : : find_if ( mPendingUpdates . begin ( ) , mPendingUpdates . end ( ) ,
[ = ] ( const ListOfTracks : : value_type & ptr ) { return ptr - > GetId ( ) = = id ; } ) ;
if ( it = = mPendingUpdates . end ( ) )
return { } ;
return * it ;
}
bool TrackList : : HasPendingTracks ( ) const
{
if ( ! mPendingUpdates . empty ( ) )
return true ;
if ( end ( ) ! = std : : find_if ( begin ( ) , end ( ) , [ ] ( const Track * t ) {
return t - > GetId ( ) = = TrackId { } ;
} ) )
return true ;
return false ;
}