2010-01-23 19:44:49 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
|
|
|
|
Track.h
|
|
|
|
|
|
|
|
Dominic Mazzoni
|
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
#ifndef __AUDACITY_TRACK__
|
|
|
|
#define __AUDACITY_TRACK__
|
|
|
|
|
2019-03-22 15:01:00 +00:00
|
|
|
#include "Audacity.h" // for USE_* macros
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-11-11 17:27:44 +00:00
|
|
|
#include "Experimental.h"
|
|
|
|
|
2016-02-26 23:10:45 +00:00
|
|
|
#include <vector>
|
2016-03-01 23:31:51 +00:00
|
|
|
#include <list>
|
2017-08-24 18:31:51 +00:00
|
|
|
#include <functional>
|
2010-01-23 19:44:49 +00:00
|
|
|
#include <wx/longlong.h>
|
|
|
|
|
2019-05-06 23:00:10 +00:00
|
|
|
#include "ClientData.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "SampleFormat.h"
|
2015-07-07 03:12:16 +00:00
|
|
|
#include "tracks/ui/CommonTrackPanelCell.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "xml/XMLTagHandler.h"
|
|
|
|
|
|
|
|
#ifdef __WXMSW__
|
|
|
|
#pragma warning(disable:4284)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
class wxTextFile;
|
|
|
|
class DirManager;
|
2015-08-24 01:25:01 +00:00
|
|
|
class Track;
|
2017-01-06 18:58:54 +00:00
|
|
|
class AudioTrack;
|
|
|
|
class PlayableTrack;
|
2010-01-23 19:44:49 +00:00
|
|
|
class LabelTrack;
|
|
|
|
class TimeTrack;
|
2015-07-07 03:12:16 +00:00
|
|
|
class TrackControls;
|
|
|
|
class TrackVRulerControls;
|
2017-06-26 20:13:56 +00:00
|
|
|
class TrackPanelResizerCell;
|
2010-01-23 19:44:49 +00:00
|
|
|
class WaveTrack;
|
2016-04-09 19:10:05 +00:00
|
|
|
class NoteTrack;
|
2010-01-23 19:44:49 +00:00
|
|
|
class AudacityProject;
|
2016-01-26 22:37:16 +00:00
|
|
|
class ZoomInfo;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
Changed lifetime management of UIHandle objects, no singletons...
... Rather, construct them during hit tests (also capturing more state sooner
rather than at Click time, and adding some accessors for later use)
This also fixes bug 1677 by other means and avoids similar problems.
A cell may be implemented to re-use a previously hit handle object, not yet
clicked, in a later hit test, by remembering a weak pointer, but TrackPanel
holds the strong pointers that determine when the object is destroyed.
And the objects will surely be destroyed after drag-release, or ESC key.
For now they are also destroyed whenever not dragging, and hit-testing is
re-invoked; that will be changed later, so that the re-use mentioned above
becomes effective, but still they will be destroyed when the pointer moves
from one cell to another.
2017-07-05 20:45:55 +00:00
|
|
|
class SelectHandle;
|
|
|
|
class TimeShiftHandle;
|
|
|
|
|
2018-09-22 16:08:17 +00:00
|
|
|
using TrackArray = std::vector< Track* >;
|
2017-08-24 15:44:36 +00:00
|
|
|
using WaveTrackArray = std::vector < std::shared_ptr< WaveTrack > > ;
|
|
|
|
using WaveTrackConstArray = std::vector < std::shared_ptr < const WaveTrack > >;
|
2018-05-29 00:44:37 +00:00
|
|
|
|
|
|
|
using NoteTrackConstArray = std::vector < std::shared_ptr< const NoteTrack > >;
|
2016-04-09 19:10:05 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
#if defined(USE_MIDI)
|
|
|
|
class NoteTrack;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
class TrackList;
|
2016-03-01 23:31:51 +00:00
|
|
|
|
2017-06-22 23:25:46 +00:00
|
|
|
using ListOfTracks = std::list< std::shared_ptr< Track > >;
|
2016-03-13 15:08:21 +00:00
|
|
|
|
2018-02-23 18:57:58 +00:00
|
|
|
using TrackNodePointer =
|
|
|
|
std::pair< ListOfTracks::iterator, ListOfTracks* >;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-02-28 10:14:21 +00:00
|
|
|
inline bool operator == (const TrackNodePointer &a, const TrackNodePointer &b)
|
|
|
|
{ return a.second == b.second && a.first == b.first; }
|
|
|
|
|
|
|
|
inline bool operator != (const TrackNodePointer &a, const TrackNodePointer &b)
|
|
|
|
{ return !(a == b); }
|
|
|
|
|
2017-01-06 18:55:57 +00:00
|
|
|
enum class TrackKind
|
|
|
|
{
|
|
|
|
None,
|
|
|
|
Wave,
|
|
|
|
#if defined(USE_MIDI)
|
|
|
|
Note,
|
|
|
|
#endif
|
|
|
|
Label,
|
|
|
|
Time,
|
2017-01-06 18:58:54 +00:00
|
|
|
Audio,
|
|
|
|
Playable,
|
2017-01-06 18:55:57 +00:00
|
|
|
All
|
|
|
|
};
|
|
|
|
|
2018-11-03 13:17:54 +00:00
|
|
|
/// Compile-time function on enum values.
|
|
|
|
/// It knows all inheritance relations among Track subclasses
|
|
|
|
/// even where the track types are only forward declared.
|
2017-01-06 18:58:54 +00:00
|
|
|
constexpr bool CompatibleTrackKinds( TrackKind desired, TrackKind actual )
|
|
|
|
{
|
|
|
|
return
|
|
|
|
(desired == actual)
|
|
|
|
||
|
|
|
|
(desired == TrackKind::All)
|
|
|
|
||
|
|
|
|
(desired == TrackKind::Audio && actual == TrackKind::Wave)
|
|
|
|
#ifdef USE_MIDI
|
|
|
|
||
|
|
|
|
(desired == TrackKind::Audio && actual == TrackKind::Note)
|
|
|
|
#endif
|
|
|
|
||
|
|
|
|
(desired == TrackKind::Playable && actual == TrackKind::Wave)
|
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
|
|
||
|
|
|
|
(desired == TrackKind::Playable && actual == TrackKind::Note)
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2018-11-03 13:17:54 +00:00
|
|
|
/// \brief Metaprogramming in TrackTyper lets track_cast work even when the track
|
|
|
|
/// subclasses are visible only as incomplete types
|
2017-01-06 18:58:54 +00:00
|
|
|
namespace TrackTyper {
|
|
|
|
template<typename, TrackKind> struct Pair;
|
|
|
|
using List = std::tuple<
|
|
|
|
Pair<Track, TrackKind::All>,
|
|
|
|
Pair<AudioTrack, TrackKind::Audio>,
|
|
|
|
Pair<PlayableTrack, TrackKind::Playable>,
|
|
|
|
Pair<LabelTrack, TrackKind::Label>,
|
|
|
|
Pair<NoteTrack, TrackKind::Note>,
|
|
|
|
Pair<TimeTrack, TrackKind::Time>,
|
|
|
|
Pair<WaveTrack, TrackKind::Wave>
|
|
|
|
// New classes can be added easily to this list
|
|
|
|
>;
|
|
|
|
template<typename...> struct Lookup;
|
|
|
|
template<typename TrackType, TrackKind Here, typename... Rest>
|
|
|
|
struct Lookup< TrackType, std::tuple< Pair<TrackType, Here>, Rest... > > {
|
|
|
|
static constexpr TrackKind value() {
|
|
|
|
return Here;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
template<typename TrackType, typename NotHere, typename... Rest>
|
|
|
|
struct Lookup< TrackType, std::tuple< NotHere, Rest... > > {
|
|
|
|
static constexpr TrackKind value() {
|
|
|
|
return Lookup< TrackType, std::tuple< Rest... > >::value();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename TrackType> constexpr TrackKind track_kind ()
|
|
|
|
{
|
|
|
|
using namespace TrackTyper;
|
|
|
|
return Lookup< typename std::remove_const<TrackType>::type, List >::value();
|
|
|
|
}
|
|
|
|
|
|
|
|
// forward declarations, so we can make them friends
|
|
|
|
template<typename T>
|
|
|
|
typename std::enable_if< std::is_pointer<T>::value, T >::type
|
|
|
|
track_cast(Track *track);
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
typename std::enable_if<
|
|
|
|
std::is_pointer<T>::value &&
|
|
|
|
std::is_const< typename std::remove_pointer< T >::type >::value,
|
|
|
|
T
|
|
|
|
>::type
|
|
|
|
track_cast(const Track *track);
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
class ViewInfo;
|
|
|
|
|
2018-11-03 13:17:54 +00:00
|
|
|
/// This is an in-session identifier of track objects across undo states
|
|
|
|
///
|
|
|
|
/// It does not persist between sessions
|
|
|
|
/// Default constructed value is not equal to the id of any track that has ever
|
|
|
|
/// been added to a TrackList, or (directly or transitively) copied from such
|
|
|
|
/// (A pending additional track that is not yet applied is not considered added)
|
|
|
|
/// TrackIds are assigned uniquely across projects
|
2018-01-11 00:28:16 +00:00
|
|
|
class TrackId
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TrackId() : mValue(-1) {}
|
|
|
|
explicit TrackId (long value) : mValue(value) {}
|
|
|
|
|
|
|
|
bool operator == (const TrackId &other) const
|
|
|
|
{ return mValue == other.mValue; }
|
|
|
|
|
|
|
|
bool operator != (const TrackId &other) const
|
|
|
|
{ return mValue != other.mValue; }
|
|
|
|
|
|
|
|
// Define this in case you want to key a std::map on TrackId
|
|
|
|
// The operator does not mean anything else
|
|
|
|
bool operator < (const TrackId &other) const
|
|
|
|
{ return mValue < other.mValue; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
long mValue;
|
|
|
|
};
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
class AUDACITY_DLL_API Track /* not final */
|
|
|
|
: public CommonTrackPanelCell, public XMLTagHandler
|
2018-11-19 01:50:24 +00:00
|
|
|
, public std::enable_shared_from_this<Track> // see SharedPointer()
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2016-03-01 23:31:51 +00:00
|
|
|
friend class TrackList;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// To be TrackDisplay
|
2018-01-11 00:28:16 +00:00
|
|
|
private:
|
|
|
|
TrackId mId;
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
protected:
|
2017-07-10 15:48:11 +00:00
|
|
|
std::weak_ptr<TrackList> mList;
|
2016-03-01 23:31:51 +00:00
|
|
|
TrackNodePointer mNode{};
|
2010-01-23 19:44:49 +00:00
|
|
|
int mIndex;
|
|
|
|
int mY;
|
|
|
|
int mHeight;
|
|
|
|
wxString mName;
|
|
|
|
wxString mDefaultName;
|
|
|
|
|
2018-02-05 22:56:46 +00:00
|
|
|
private:
|
2010-01-23 19:44:49 +00:00
|
|
|
bool mSelected;
|
|
|
|
|
2018-02-05 22:56:46 +00:00
|
|
|
protected:
|
2010-01-23 19:44:49 +00:00
|
|
|
bool mLinked;
|
|
|
|
bool mMinimized;
|
|
|
|
|
2018-01-11 00:28:16 +00:00
|
|
|
public:
|
|
|
|
|
2019-05-12 19:53:01 +00:00
|
|
|
using ChannelType = XMLValueChecker::ChannelType;
|
|
|
|
|
|
|
|
static const auto LeftChannel = XMLValueChecker::LeftChannel;
|
|
|
|
static const auto RightChannel = XMLValueChecker::RightChannel;
|
|
|
|
static const auto MonoChannel = XMLValueChecker::MonoChannel;
|
2018-09-20 16:39:05 +00:00
|
|
|
|
2018-01-11 00:28:16 +00:00
|
|
|
TrackId GetId() const { return mId; }
|
|
|
|
private:
|
|
|
|
void SetId( TrackId id ) { mId = id; }
|
2010-01-23 19:44:49 +00:00
|
|
|
public:
|
2014-06-03 20:30:19 +00:00
|
|
|
|
2018-11-19 01:50:24 +00:00
|
|
|
// Given a bare pointer, find a shared_ptr. Undefined results if the track
|
|
|
|
// is not yet managed by a shared_ptr. Undefined results if the track is
|
|
|
|
// not really of the subclass. (That is, trusts the caller and uses static
|
|
|
|
// not dynamic casting.)
|
2017-06-23 00:29:32 +00:00
|
|
|
template<typename Subclass = Track>
|
2018-11-19 01:50:24 +00:00
|
|
|
inline std::shared_ptr<Subclass> SharedPointer()
|
2017-06-23 00:29:32 +00:00
|
|
|
{
|
2018-11-19 01:50:24 +00:00
|
|
|
// shared_from_this is injected into class scope by base class
|
|
|
|
// std::enable_shared_from_this<Track>
|
|
|
|
return std::static_pointer_cast<Subclass>( shared_from_this() );
|
2017-06-23 00:29:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Subclass = const Track>
|
2018-11-19 01:50:24 +00:00
|
|
|
inline auto SharedPointer() const -> typename
|
|
|
|
std::enable_if<
|
|
|
|
std::is_const<Subclass>::value, std::shared_ptr<Subclass>
|
|
|
|
>::type
|
2017-06-23 00:29:32 +00:00
|
|
|
{
|
2018-11-19 01:50:24 +00:00
|
|
|
// shared_from_this is injected into class scope by base class
|
|
|
|
// std::enable_shared_from_this<Track>
|
|
|
|
return std::static_pointer_cast<Subclass>( shared_from_this() );
|
2017-06-23 00:29:32 +00:00
|
|
|
}
|
|
|
|
|
2019-03-23 17:23:46 +00:00
|
|
|
// Static overloads of SharedPointer for when the pointer may be null
|
|
|
|
template<typename Subclass = Track>
|
|
|
|
static inline std::shared_ptr<Subclass> SharedPointer( Track *pTrack )
|
|
|
|
{ return pTrack ? pTrack->SharedPointer<Subclass>() : nullptr; }
|
|
|
|
|
|
|
|
template<typename Subclass = const Track>
|
|
|
|
static inline std::shared_ptr<Subclass> SharedPointer( const Track *pTrack )
|
|
|
|
{ return pTrack ? pTrack->SharedPointer<Subclass>() : nullptr; }
|
|
|
|
|
2018-09-13 17:11:00 +00:00
|
|
|
// Find anything registered with TrackList::RegisterPendingChangedTrack and
|
|
|
|
// not yet cleared or applied; if no such exists, return this track
|
|
|
|
std::shared_ptr<const Track> SubstitutePendingChangedTrack() const;
|
|
|
|
|
2018-10-30 00:35:45 +00:00
|
|
|
// If this track is a pending changed track, return the corresponding
|
|
|
|
// original; else return this track
|
|
|
|
std::shared_ptr<const Track> SubstituteOriginalTrack() const;
|
|
|
|
|
2017-06-19 19:02:45 +00:00
|
|
|
// Cause certain overriding tool modes (Zoom; future ones?) to behave
|
|
|
|
// uniformly in all tracks, disregarding track contents.
|
|
|
|
// Do not further override this...
|
2017-06-29 14:34:57 +00:00
|
|
|
std::vector<UIHandlePtr> HitTest
|
2017-06-29 03:21:20 +00:00
|
|
|
(const TrackPanelMouseState &, const AudacityProject *pProject)
|
2017-07-13 01:23:08 +00:00
|
|
|
final override;
|
2017-06-19 19:02:45 +00:00
|
|
|
|
2018-06-27 02:43:04 +00:00
|
|
|
// Delegates the handling to the related TCP cell
|
2018-11-06 16:02:03 +00:00
|
|
|
std::shared_ptr<TrackPanelCell> ContextMenuDelegate() override;
|
2018-06-27 02:43:04 +00:00
|
|
|
|
2017-06-19 19:02:45 +00:00
|
|
|
public:
|
|
|
|
|
|
|
|
// Rather override this for subclasses:
|
2017-06-29 14:34:57 +00:00
|
|
|
virtual std::vector<UIHandlePtr> DetailedHitTest
|
2017-06-29 03:21:20 +00:00
|
|
|
(const TrackPanelMouseState &,
|
2017-06-19 19:02:45 +00:00
|
|
|
const AudacityProject *pProject, int currentTool, bool bMultiTool)
|
|
|
|
= 0;
|
|
|
|
|
|
|
|
mutable wxSize vrulerSize;
|
2015-07-07 03:12:16 +00:00
|
|
|
|
|
|
|
// Return another, associated TrackPanelCell object that implements the
|
|
|
|
// drop-down, close and minimize buttons, etc.
|
2019-06-05 13:50:45 +00:00
|
|
|
std::shared_ptr<TrackPanelCell> GetTrackControl();
|
|
|
|
std::shared_ptr<const TrackPanelCell> GetTrackControl() const;
|
2015-07-07 03:12:16 +00:00
|
|
|
|
|
|
|
// Return another, associated TrackPanelCell object that implements the
|
|
|
|
// mouse actions for the vertical ruler
|
2018-11-06 16:02:03 +00:00
|
|
|
std::shared_ptr<TrackVRulerControls> GetVRulerControl();
|
|
|
|
std::shared_ptr<const TrackVRulerControls> GetVRulerControl() const;
|
2015-07-07 03:12:16 +00:00
|
|
|
|
2017-06-26 20:13:56 +00:00
|
|
|
// Return another, associated TrackPanelCell object that implements the
|
|
|
|
// click and drag to resize
|
|
|
|
std::shared_ptr<TrackPanelCell> GetResizer();
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// This just returns a constant and can be overriden by subclasses
|
|
|
|
// to specify a different height for the case that the track is minimized.
|
|
|
|
virtual int GetMinimizedHeight() const;
|
2017-02-22 19:23:35 +00:00
|
|
|
int GetActualHeight() const { return mHeight; }
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
int GetIndex() const;
|
|
|
|
void SetIndex(int index);
|
2018-01-12 01:57:37 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
int GetY() const;
|
2018-01-12 01:57:37 +00:00
|
|
|
private:
|
|
|
|
// Always maintain a strictly contiguous layout of tracks.
|
|
|
|
// So client code is not permitted to modify this attribute directly.
|
2010-01-23 19:44:49 +00:00
|
|
|
void SetY(int y);
|
2018-01-12 01:57:37 +00:00
|
|
|
// No need yet to make this virtual
|
|
|
|
void DoSetY(int y);
|
|
|
|
public:
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
int GetHeight() const;
|
2018-01-12 01:57:37 +00:00
|
|
|
void SetHeight(int h);
|
|
|
|
protected:
|
|
|
|
virtual void DoSetHeight(int h);
|
|
|
|
public:
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
bool GetMinimized() const;
|
2018-01-12 01:57:37 +00:00
|
|
|
void SetMinimized(bool isMinimized);
|
|
|
|
protected:
|
|
|
|
virtual void DoSetMinimized(bool isMinimized);
|
2018-09-18 15:05:21 +00:00
|
|
|
|
2018-01-12 01:57:37 +00:00
|
|
|
public:
|
2018-09-18 15:05:21 +00:00
|
|
|
static void FinishCopy (const Track *n, Track *dest);
|
2018-01-12 01:57:37 +00:00
|
|
|
|
2018-09-18 15:05:21 +00:00
|
|
|
// For use when loading a file. Return true if ok, else make repair
|
|
|
|
bool LinkConsistencyCheck();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2019-02-05 14:29:22 +00:00
|
|
|
bool HasOwner() const { return static_cast<bool>(GetOwner());}
|
|
|
|
|
2016-11-02 15:45:03 +00:00
|
|
|
private:
|
|
|
|
std::shared_ptr<TrackList> GetOwner() const { return mList.lock(); }
|
|
|
|
|
2018-09-18 15:05:21 +00:00
|
|
|
Track *GetLink() const;
|
|
|
|
bool GetLinked () const { return mLinked; }
|
2018-09-20 16:28:41 +00:00
|
|
|
|
|
|
|
friend WaveTrack; // WaveTrack needs to call SetLinked when reloading project
|
2018-09-18 15:05:21 +00:00
|
|
|
void SetLinked (bool l);
|
2018-09-20 16:28:41 +00:00
|
|
|
|
|
|
|
void SetChannel(ChannelType c) { mChannel = c; }
|
2018-09-18 15:05:21 +00:00
|
|
|
private:
|
|
|
|
// No need yet to make this virtual
|
|
|
|
void DoSetLinked(bool l);
|
|
|
|
|
2016-03-01 23:31:51 +00:00
|
|
|
TrackNodePointer GetNode() const;
|
2017-07-10 15:48:11 +00:00
|
|
|
void SetOwner
|
|
|
|
(const std::weak_ptr<TrackList> &list, TrackNodePointer node);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// Keep in Track
|
|
|
|
|
|
|
|
protected:
|
2018-09-20 16:39:05 +00:00
|
|
|
ChannelType mChannel;
|
2010-01-23 19:44:49 +00:00
|
|
|
double mOffset;
|
|
|
|
|
2016-08-13 04:38:31 +00:00
|
|
|
mutable std::shared_ptr<DirManager> mDirManager;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
2017-06-07 01:40:58 +00:00
|
|
|
enum : unsigned { DefaultHeight = 150 };
|
|
|
|
|
2016-08-13 04:38:31 +00:00
|
|
|
Track(const std::shared_ptr<DirManager> &projDirManager);
|
2010-01-23 19:44:49 +00:00
|
|
|
Track(const Track &orig);
|
|
|
|
|
|
|
|
virtual ~ Track();
|
2014-06-03 20:30:19 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
void Init(const Track &orig);
|
2016-03-02 19:37:47 +00:00
|
|
|
|
2018-11-19 04:07:05 +00:00
|
|
|
using Holder = std::shared_ptr<Track>;
|
2016-03-02 19:37:47 +00:00
|
|
|
virtual Holder Duplicate() const = 0;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2011-04-26 21:19:59 +00:00
|
|
|
// Called when this track is merged to stereo with another, and should
|
2010-01-23 19:44:49 +00:00
|
|
|
// take on some paramaters of its partner.
|
|
|
|
virtual void Merge(const Track &orig);
|
|
|
|
|
|
|
|
wxString GetName() const { return mName; }
|
2018-02-05 22:56:46 +00:00
|
|
|
void SetName( const wxString &n );
|
2010-01-23 19:44:49 +00:00
|
|
|
wxString GetDefaultName() const { return mDefaultName; }
|
2016-02-23 02:18:11 +00:00
|
|
|
void SetDefaultName( const wxString &n ) { mDefaultName = n; }
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
bool GetSelected() const { return mSelected; }
|
2018-09-18 15:05:21 +00:00
|
|
|
|
2018-01-15 02:36:33 +00:00
|
|
|
virtual void SetSelected(bool s);
|
2018-01-12 01:57:37 +00:00
|
|
|
|
|
|
|
public:
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-09-20 16:39:05 +00:00
|
|
|
virtual ChannelType GetChannel() const { return mChannel;}
|
2015-03-28 18:46:40 +00:00
|
|
|
virtual double GetOffset() const = 0;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
void Offset(double t) { SetOffset(GetOffset() + t); }
|
|
|
|
virtual void SetOffset (double o) { mOffset = o; }
|
|
|
|
|
2017-04-23 11:58:11 +00:00
|
|
|
virtual void SetPan( float ){ ;}
|
2017-04-02 22:07:13 +00:00
|
|
|
virtual void SetPanFromChannelType(){ ;};
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// AS: Note that the dirManager is mutable. This is
|
|
|
|
// mostly to support "Duplicate" of const objects,
|
|
|
|
// but in general, mucking with the dir manager is
|
|
|
|
// separate from the Track.
|
2016-08-13 04:38:31 +00:00
|
|
|
const std::shared_ptr<DirManager> &GetDirManager() const { return mDirManager; }
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-03-31 18:54:55 +00:00
|
|
|
// Create a NEW track and modify this track
|
|
|
|
// Return non-NULL or else throw
|
2017-11-19 03:18:49 +00:00
|
|
|
// May assume precondition: t0 <= t1
|
2017-03-31 18:54:55 +00:00
|
|
|
virtual Holder Cut(double WXUNUSED(t0), double WXUNUSED(t1)) = 0;
|
2016-03-02 20:36:44 +00:00
|
|
|
|
2017-03-31 18:54:55 +00:00
|
|
|
// Create a NEW track and don't modify this track
|
|
|
|
// Return non-NULL or else throw
|
2016-11-26 13:48:08 +00:00
|
|
|
// Note that subclasses may want to distinguish tracks stored in a clipboard
|
|
|
|
// from those stored in a project
|
2017-11-19 03:18:49 +00:00
|
|
|
// May assume precondition: t0 <= t1
|
2016-11-26 13:48:08 +00:00
|
|
|
virtual Holder Copy
|
2017-03-31 18:54:55 +00:00
|
|
|
(double WXUNUSED(t0), double WXUNUSED(t1), bool forClipboard = true) const = 0;
|
2016-03-02 20:36:44 +00:00
|
|
|
|
2017-11-19 03:18:49 +00:00
|
|
|
// May assume precondition: t0 <= t1
|
2017-03-23 15:10:14 +00:00
|
|
|
virtual void Clear(double WXUNUSED(t0), double WXUNUSED(t1)) = 0;
|
2016-03-02 20:36:44 +00:00
|
|
|
|
2017-03-23 15:10:14 +00:00
|
|
|
virtual void Paste(double WXUNUSED(t), const Track * WXUNUSED(src)) = 0;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2010-08-11 22:47:26 +00:00
|
|
|
// This can be used to adjust a sync-lock selected track when the selection
|
2010-02-16 20:50:38 +00:00
|
|
|
// is replaced by one of a different length.
|
2017-03-23 15:10:14 +00:00
|
|
|
virtual void SyncLockAdjust(double oldT1, double newT1);
|
2010-02-16 20:50:38 +00:00
|
|
|
|
2017-11-19 03:18:49 +00:00
|
|
|
// May assume precondition: t0 <= t1
|
2017-03-23 15:10:14 +00:00
|
|
|
virtual void Silence(double WXUNUSED(t0), double WXUNUSED(t1)) = 0;
|
2017-11-19 03:18:49 +00:00
|
|
|
|
|
|
|
// May assume precondition: t0 <= t1
|
2017-03-23 15:10:14 +00:00
|
|
|
virtual void InsertSilence(double WXUNUSED(t), double WXUNUSED(len)) = 0;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-01-06 06:59:17 +00:00
|
|
|
private:
|
2017-01-06 18:55:57 +00:00
|
|
|
virtual TrackKind GetKind() const { return TrackKind::None; }
|
|
|
|
|
2017-01-06 18:58:54 +00:00
|
|
|
template<typename T>
|
|
|
|
friend typename std::enable_if< std::is_pointer<T>::value, T >::type
|
|
|
|
track_cast(Track *track);
|
|
|
|
template<typename T>
|
|
|
|
friend typename std::enable_if<
|
|
|
|
std::is_pointer<T>::value &&
|
|
|
|
std::is_const< typename std::remove_pointer< T >::type >::value,
|
|
|
|
T
|
|
|
|
>::type
|
|
|
|
track_cast(const Track *track);
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool SameKindAs(const Track &track) const
|
|
|
|
{ return GetKind() == track.GetKind(); }
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-01-08 16:24:34 +00:00
|
|
|
template < typename R = void >
|
|
|
|
using Continuation = std::function< R() >;
|
|
|
|
using Fallthrough = Continuation<>;
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
// Variadic template specialized below
|
|
|
|
template< typename ...Params >
|
|
|
|
struct Executor;
|
|
|
|
|
|
|
|
// This specialization grounds the recursion.
|
|
|
|
template< typename R, typename ConcreteType >
|
|
|
|
struct Executor< R, ConcreteType >
|
|
|
|
{
|
|
|
|
enum : unsigned { SetUsed = 0 };
|
|
|
|
// No functions matched, so do nothing.
|
|
|
|
R operator () (const void *) { return R{}; }
|
|
|
|
};
|
|
|
|
|
|
|
|
// And another specialization is needed for void return.
|
|
|
|
template< typename ConcreteType >
|
|
|
|
struct Executor< void, ConcreteType >
|
|
|
|
{
|
|
|
|
enum : unsigned { SetUsed = 0 };
|
|
|
|
// No functions matched, so do nothing.
|
|
|
|
void operator () (const void *) { }
|
|
|
|
};
|
|
|
|
|
|
|
|
// This struct groups some helpers needed to define the recursive cases of
|
|
|
|
// Executor.
|
|
|
|
struct Dispatcher {
|
|
|
|
// This implements the specialization of Executor
|
|
|
|
// for the first recursive case.
|
|
|
|
template< typename R, typename ConcreteType,
|
|
|
|
typename Function, typename ...Functions >
|
|
|
|
struct inapplicable
|
|
|
|
{
|
|
|
|
using Tail = Executor< R, ConcreteType, Functions... >;
|
|
|
|
enum : unsigned { SetUsed = Tail::SetUsed << 1 };
|
|
|
|
|
|
|
|
// Ignore the first, inapplicable function and try others.
|
|
|
|
R operator ()
|
|
|
|
(const Track *pTrack,
|
|
|
|
const Function &, const Functions &...functions)
|
|
|
|
{ return Tail{}( pTrack, functions... ); }
|
|
|
|
};
|
|
|
|
|
|
|
|
// This implements the specialization of Executor
|
|
|
|
// for the second recursive case.
|
|
|
|
template< typename R, typename BaseClass, typename ConcreteType,
|
|
|
|
typename Function, typename ...Functions >
|
|
|
|
struct applicable1
|
|
|
|
{
|
|
|
|
enum : unsigned { SetUsed = 1u };
|
|
|
|
|
|
|
|
// Ignore the remaining functions and call the first only.
|
|
|
|
R operator ()
|
|
|
|
(const Track *pTrack,
|
|
|
|
const Function &function, const Functions &...)
|
|
|
|
{ return function( (BaseClass *)pTrack ); }
|
|
|
|
};
|
|
|
|
|
|
|
|
// This implements the specialization of Executor
|
|
|
|
// for the third recursive case.
|
|
|
|
template< typename R, typename BaseClass, typename ConcreteType,
|
|
|
|
typename Function, typename ...Functions >
|
|
|
|
struct applicable2
|
|
|
|
{
|
|
|
|
using Tail = Executor< R, ConcreteType, Functions... >;
|
|
|
|
enum : unsigned { SetUsed = (Tail::SetUsed << 1) | 1u };
|
|
|
|
|
|
|
|
// Call the first function, which may request dispatch to the further
|
|
|
|
// functions by invoking a continuation.
|
|
|
|
R operator ()
|
|
|
|
(const Track *pTrack, const Function &function,
|
|
|
|
const Functions &...functions)
|
|
|
|
{
|
|
|
|
auto continuation = Continuation<R>{ [&] {
|
|
|
|
return Tail{}( pTrack, functions... );
|
|
|
|
} };
|
|
|
|
return function( (BaseClass *)pTrack, continuation );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// This variadic template chooses among the implementations above.
|
|
|
|
template< typename ... > struct Switch;
|
|
|
|
|
|
|
|
// Ground the recursion.
|
|
|
|
template< typename R, typename ConcreteType >
|
|
|
|
struct Switch< R, ConcreteType >
|
|
|
|
{
|
|
|
|
// No BaseClass of ConcreteType is acceptable to Function.
|
|
|
|
template< typename Function, typename ...Functions >
|
|
|
|
static auto test()
|
|
|
|
-> inapplicable< R, ConcreteType, Function, Functions... >;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Recursive case.
|
|
|
|
template< typename R, typename ConcreteType,
|
|
|
|
typename BaseClass, typename ...BaseClasses >
|
|
|
|
struct Switch< R, ConcreteType, BaseClass, BaseClasses... >
|
|
|
|
{
|
|
|
|
using Retry = Switch< R, ConcreteType, BaseClasses... >;
|
|
|
|
|
|
|
|
// If ConcreteType is not compatible with BaseClass, or if
|
|
|
|
// Function does not accept BaseClass, try other BaseClasses.
|
|
|
|
template< typename Function, typename ...Functions >
|
|
|
|
static auto test( const void * )
|
|
|
|
-> decltype( Retry::template test< Function, Functions... >() );
|
|
|
|
|
|
|
|
// If BaseClass is a base of ConcreteType and Function can take it,
|
|
|
|
// then overload resolution chooses this.
|
|
|
|
// If not, then the sfinae rule makes this overload unavailable.
|
|
|
|
template< typename Function, typename ...Functions >
|
|
|
|
static auto test( std::true_type * )
|
|
|
|
-> decltype(
|
|
|
|
(void) std::declval<Function>()
|
|
|
|
( (BaseClass*)nullptr ),
|
|
|
|
applicable1< R, BaseClass, ConcreteType,
|
|
|
|
Function, Functions... >{}
|
|
|
|
);
|
|
|
|
|
|
|
|
// If BaseClass is a base of ConcreteType and Function can take it,
|
|
|
|
// with a second argument for a continuation,
|
|
|
|
// then overload resolution chooses this.
|
|
|
|
// If not, then the sfinae rule makes this overload unavailable.
|
|
|
|
template< typename Function, typename ...Functions >
|
|
|
|
static auto test( std::true_type * )
|
|
|
|
-> decltype(
|
|
|
|
(void) std::declval<Function>()
|
|
|
|
( (BaseClass*)nullptr,
|
|
|
|
std::declval< Continuation<R> >() ),
|
|
|
|
applicable2< R, BaseClass, ConcreteType,
|
|
|
|
Function, Functions... >{}
|
|
|
|
);
|
|
|
|
|
|
|
|
static constexpr bool Compatible = CompatibleTrackKinds(
|
|
|
|
track_kind<BaseClass>(), track_kind<ConcreteType>() );
|
|
|
|
template< typename Function, typename ...Functions >
|
|
|
|
static auto test()
|
|
|
|
-> decltype(
|
|
|
|
test< Function, Functions... >(
|
|
|
|
(std::integral_constant<bool, Compatible>*)nullptr) );
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
// This specialization is the recursive case for non-const tracks.
|
|
|
|
template< typename R, typename ConcreteType,
|
|
|
|
typename Function, typename ...Functions >
|
|
|
|
struct Executor< R, ConcreteType, Function, Functions... >
|
|
|
|
: decltype(
|
|
|
|
Dispatcher::Switch< R, ConcreteType,
|
|
|
|
Track, AudioTrack, PlayableTrack,
|
|
|
|
WaveTrack, LabelTrack, TimeTrack,
|
|
|
|
NoteTrack >
|
|
|
|
::template test<Function, Functions... >())
|
|
|
|
{};
|
|
|
|
|
|
|
|
// This specialization is the recursive case for const tracks.
|
|
|
|
template< typename R, typename ConcreteType,
|
|
|
|
typename Function, typename ...Functions >
|
|
|
|
struct Executor< R, const ConcreteType, Function, Functions... >
|
|
|
|
: decltype(
|
|
|
|
Dispatcher::Switch< R, ConcreteType,
|
|
|
|
const Track, const AudioTrack, const PlayableTrack,
|
|
|
|
const WaveTrack, const LabelTrack, const TimeTrack,
|
|
|
|
const NoteTrack >
|
|
|
|
::template test<Function, Functions... >())
|
|
|
|
{};
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
// A variadic function taking any number of function objects, each taking
|
|
|
|
// a pointer to Track or a subclass, maybe const-qualified, and maybe a
|
|
|
|
// second argument which is a fall-through continuation.
|
|
|
|
// Each of the function objects (and supplied continuations) returns R.
|
|
|
|
// Call the first in the sequence that accepts the actual type of the track.
|
|
|
|
// If no function accepts the track, do nothing and return R{}
|
|
|
|
// if R is not void.
|
|
|
|
// If one of the functions invokes the call-through, then the next following
|
|
|
|
// applicable funtion is called.
|
|
|
|
template< typename R = void, typename ...Functions >
|
|
|
|
R TypeSwitch(const Functions &...functions)
|
|
|
|
{
|
|
|
|
using WaveExecutor =
|
|
|
|
Executor< R, WaveTrack, Functions... >;
|
|
|
|
using NoteExecutor =
|
|
|
|
Executor< R, NoteTrack, Functions... >;
|
|
|
|
using LabelExecutor =
|
|
|
|
Executor< R, LabelTrack, Functions... >;
|
|
|
|
using TimeExecutor =
|
|
|
|
Executor< R, TimeTrack, Functions... >;
|
|
|
|
using DefaultExecutor =
|
|
|
|
Executor< R, Track >;
|
|
|
|
enum { All = sizeof...( functions ) };
|
|
|
|
|
|
|
|
static_assert(
|
|
|
|
(1u << All) - 1u ==
|
|
|
|
(WaveExecutor::SetUsed |
|
|
|
|
NoteExecutor::SetUsed |
|
|
|
|
LabelExecutor::SetUsed |
|
|
|
|
TimeExecutor::SetUsed),
|
|
|
|
"Uncallable case in Track::TypeSwitch"
|
|
|
|
);
|
|
|
|
|
|
|
|
switch (GetKind()) {
|
|
|
|
case TrackKind::Wave:
|
|
|
|
return WaveExecutor{} (this, functions...);
|
|
|
|
#if defined(USE_MIDI)
|
|
|
|
case TrackKind::Note:
|
|
|
|
return NoteExecutor{} (this, functions...);
|
|
|
|
#endif
|
|
|
|
case TrackKind::Label:
|
|
|
|
return LabelExecutor{}(this, functions...);
|
|
|
|
case TrackKind::Time:
|
|
|
|
return TimeExecutor{} (this, functions...);
|
|
|
|
default:
|
|
|
|
return DefaultExecutor{} (this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is the overload of TypeSwitch (see above) for const tracks, taking
|
|
|
|
// callable arguments that only accept arguments that are pointers to const
|
|
|
|
template< typename R = void, typename ...Functions >
|
|
|
|
R TypeSwitch(const Functions &...functions) const
|
|
|
|
{
|
|
|
|
using WaveExecutor =
|
|
|
|
Executor< R, const WaveTrack, Functions... >;
|
|
|
|
using NoteExecutor =
|
|
|
|
Executor< R, const NoteTrack, Functions... >;
|
|
|
|
using LabelExecutor =
|
|
|
|
Executor< R, const LabelTrack, Functions... >;
|
|
|
|
using TimeExecutor =
|
|
|
|
Executor< R, const TimeTrack, Functions... >;
|
|
|
|
using DefaultExecutor =
|
|
|
|
Executor< R, const Track >;
|
|
|
|
enum { All = sizeof...( functions ) };
|
|
|
|
|
|
|
|
static_assert(
|
|
|
|
(1u << All) - 1u ==
|
|
|
|
(WaveExecutor::SetUsed |
|
|
|
|
NoteExecutor::SetUsed |
|
|
|
|
LabelExecutor::SetUsed |
|
|
|
|
TimeExecutor::SetUsed),
|
|
|
|
"Uncallable case in Track::TypeSwitch"
|
|
|
|
);
|
|
|
|
|
|
|
|
switch (GetKind()) {
|
|
|
|
case TrackKind::Wave:
|
|
|
|
return WaveExecutor{} (this, functions...);
|
|
|
|
#if defined(USE_MIDI)
|
|
|
|
case TrackKind::Note:
|
|
|
|
return NoteExecutor{} (this, functions...);
|
|
|
|
#endif
|
|
|
|
case TrackKind::Label:
|
|
|
|
return LabelExecutor{}(this, functions...);
|
|
|
|
case TrackKind::Time:
|
|
|
|
return TimeExecutor{} (this, functions...);
|
|
|
|
default:
|
|
|
|
return DefaultExecutor{} (this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-24 06:06:47 +00:00
|
|
|
// XMLTagHandler callback methods -- NEW virtual for writing
|
2017-02-22 19:23:35 +00:00
|
|
|
virtual void WriteXML(XMLWriter &xmlFile) const = 0;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// Returns true if an error was encountered while trying to
|
|
|
|
// open the track from XML
|
|
|
|
virtual bool GetErrorOpening() { return false; }
|
|
|
|
|
2015-03-28 18:46:40 +00:00
|
|
|
virtual double GetStartTime() const = 0;
|
|
|
|
virtual double GetEndTime() const = 0;
|
2010-02-12 16:05:02 +00:00
|
|
|
|
2012-08-20 20:26:42 +00:00
|
|
|
// Checks if sync-lock is on and any track in its sync-lock group is selected.
|
2015-04-19 11:27:33 +00:00
|
|
|
bool IsSyncLockSelected() const;
|
2015-07-07 03:12:16 +00:00
|
|
|
|
2018-02-05 22:56:46 +00:00
|
|
|
// Send an event to listeners when state of the track changes
|
|
|
|
// To do: define values for the argument to distinguish different parts
|
|
|
|
// of the state, perhaps with wxNewId
|
|
|
|
void Notify( int code = -1 );
|
|
|
|
|
2016-11-02 15:45:03 +00:00
|
|
|
// An always-true predicate useful for defining iterators
|
|
|
|
bool Any() const;
|
|
|
|
|
|
|
|
// Frequently useful operands for + and -
|
|
|
|
bool IsSelected() const;
|
|
|
|
bool IsSelectedOrSyncLockSelected() const;
|
|
|
|
bool IsLeader() const;
|
|
|
|
bool IsSelectedLeader() const;
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
protected:
|
2018-11-06 16:02:03 +00:00
|
|
|
std::shared_ptr<Track> DoFindTrack() override;
|
2017-06-23 21:33:45 +00:00
|
|
|
|
|
|
|
// These are called to create controls on demand:
|
2018-11-06 16:02:03 +00:00
|
|
|
virtual std::shared_ptr<TrackControls> DoGetControls() = 0;
|
|
|
|
virtual std::shared_ptr<TrackVRulerControls> DoGetVRulerControls() = 0;
|
2017-06-23 21:33:45 +00:00
|
|
|
|
|
|
|
// These hold the controls:
|
|
|
|
std::shared_ptr<TrackControls> mpControls;
|
|
|
|
std::shared_ptr<TrackVRulerControls> mpVRulerContols;
|
2017-06-26 20:13:56 +00:00
|
|
|
std::shared_ptr<TrackPanelResizerCell> mpResizer;
|
Changed lifetime management of UIHandle objects, no singletons...
... Rather, construct them during hit tests (also capturing more state sooner
rather than at Click time, and adding some accessors for later use)
This also fixes bug 1677 by other means and avoids similar problems.
A cell may be implemented to re-use a previously hit handle object, not yet
clicked, in a later hit test, by remembering a weak pointer, but TrackPanel
holds the strong pointers that determine when the object is destroyed.
And the objects will surely be destroyed after drag-release, or ESC key.
For now they are also destroyed whenever not dragging, and hit-testing is
re-invoked; that will be changed later, so that the re-use mentioned above
becomes effective, but still they will be destroyed when the pointer moves
from one cell to another.
2017-07-05 20:45:55 +00:00
|
|
|
|
|
|
|
std::weak_ptr<SelectHandle> mSelectHandle;
|
|
|
|
std::weak_ptr<TimeShiftHandle> mTimeShiftHandle;
|
2010-01-23 19:44:49 +00:00
|
|
|
};
|
|
|
|
|
2017-05-23 17:06:50 +00:00
|
|
|
class AUDACITY_DLL_API AudioTrack /* not final */ : public Track
|
2017-01-08 08:36:12 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
AudioTrack(const std::shared_ptr<DirManager> &projDirManager)
|
|
|
|
: Track{ projDirManager } {}
|
|
|
|
AudioTrack(const Track &orig) : Track{ orig } {}
|
2017-03-30 20:00:27 +00:00
|
|
|
|
|
|
|
// Serialize, not with tags of its own, but as attributes within a tag.
|
2017-12-08 11:26:09 +00:00
|
|
|
void WriteXMLAttributes(XMLWriter &WXUNUSED(xmlFile)) const {}
|
2017-03-30 20:00:27 +00:00
|
|
|
|
|
|
|
// Return true iff the attribute is recognized.
|
|
|
|
bool HandleXMLAttribute(const wxChar * /*attr*/, const wxChar * /*value*/)
|
|
|
|
{ return false; }
|
2017-01-08 08:36:12 +00:00
|
|
|
};
|
|
|
|
|
2017-05-23 17:06:50 +00:00
|
|
|
class AUDACITY_DLL_API PlayableTrack /* not final */ : public AudioTrack
|
2017-01-08 08:36:12 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
PlayableTrack(const std::shared_ptr<DirManager> &projDirManager)
|
|
|
|
: AudioTrack{ projDirManager } {}
|
|
|
|
PlayableTrack(const Track &orig) : AudioTrack{ orig } {}
|
2017-03-29 15:25:05 +00:00
|
|
|
|
|
|
|
bool GetMute () const { return mMute; }
|
|
|
|
bool GetSolo () const { return mSolo; }
|
2018-02-05 22:56:46 +00:00
|
|
|
void SetMute (bool m);
|
|
|
|
void SetSolo (bool s);
|
2017-03-29 15:25:05 +00:00
|
|
|
|
|
|
|
void Init( const PlayableTrack &init );
|
|
|
|
void Merge( const Track &init ) override;
|
|
|
|
|
2017-03-30 20:00:27 +00:00
|
|
|
// Serialize, not with tags of its own, but as attributes within a tag.
|
|
|
|
void WriteXMLAttributes(XMLWriter &xmlFile) const;
|
|
|
|
|
|
|
|
// Return true iff the attribute is recognized.
|
|
|
|
bool HandleXMLAttribute(const wxChar *attr, const wxChar *value);
|
|
|
|
|
2017-03-29 15:25:05 +00:00
|
|
|
protected:
|
|
|
|
bool mMute { false };
|
|
|
|
bool mSolo { false };
|
2017-01-08 08:36:12 +00:00
|
|
|
};
|
|
|
|
|
2017-01-06 18:58:54 +00:00
|
|
|
// Functions to encapsulate the checked down-casting of track pointers,
|
|
|
|
// eliminating possibility of error -- and not quietly casting away const
|
|
|
|
// typical usage:
|
|
|
|
// if (auto wt = track_cast<WaveTrack*>(track)) { ... }
|
|
|
|
template<typename T>
|
|
|
|
inline typename std::enable_if< std::is_pointer<T>::value, T >::type
|
|
|
|
track_cast(Track *track)
|
|
|
|
{
|
|
|
|
using BareType = typename std::remove_pointer< T >::type;
|
|
|
|
if (track &&
|
|
|
|
CompatibleTrackKinds( track_kind<BareType>(), track->GetKind() ))
|
|
|
|
return reinterpret_cast<T>(track);
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overload for const pointers can cast only to other const pointer types
|
|
|
|
template<typename T>
|
|
|
|
inline typename std::enable_if<
|
|
|
|
std::is_pointer<T>::value &&
|
|
|
|
std::is_const< typename std::remove_pointer< T >::type >::value,
|
|
|
|
T
|
|
|
|
>::type
|
|
|
|
track_cast(const Track *track)
|
|
|
|
{
|
|
|
|
using BareType = typename std::remove_pointer< T >::type;
|
|
|
|
if (track &&
|
|
|
|
CompatibleTrackKinds( track_kind<BareType>(), track->GetKind() ))
|
|
|
|
return reinterpret_cast<T>(track);
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-10-30 14:51:04 +00:00
|
|
|
template < typename TrackType > struct TrackIterRange;
|
|
|
|
|
2016-11-02 15:45:03 +00:00
|
|
|
// new track iterators can eliminate the need to cast the result
|
|
|
|
template <
|
|
|
|
typename TrackType // Track or a subclass, maybe const-qualified
|
|
|
|
> class TrackIter
|
2018-12-22 14:35:22 +00:00
|
|
|
: public ValueIterator< TrackType *, std::bidirectional_iterator_tag >
|
2016-11-02 15:45:03 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
// Type of predicate taking pointer to const TrackType
|
|
|
|
// TODO C++14: simplify away ::type
|
|
|
|
using FunctionType = std::function< bool(
|
|
|
|
typename std::add_pointer<
|
|
|
|
typename std::add_const<
|
|
|
|
typename std::remove_pointer<
|
|
|
|
TrackType
|
|
|
|
>::type
|
|
|
|
>::type
|
|
|
|
>::type
|
|
|
|
) >;
|
|
|
|
|
|
|
|
template<typename Predicate = FunctionType>
|
|
|
|
TrackIter( TrackNodePointer begin, TrackNodePointer iter,
|
|
|
|
TrackNodePointer end, const Predicate &pred = {} )
|
|
|
|
: mBegin( begin ), mIter( iter ), mEnd( end ), mPred( pred )
|
|
|
|
{
|
|
|
|
// Establish the class invariant
|
|
|
|
if (this->mIter != this->mEnd && !this->valid())
|
|
|
|
this->operator ++ ();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an iterator that replaces the predicate, advancing to the first
|
|
|
|
// position at or after the old position that satisfies the new predicate,
|
|
|
|
// or to the end.
|
|
|
|
template < typename Predicate2 >
|
|
|
|
TrackIter Filter( const Predicate2 &pred2 ) const
|
|
|
|
{
|
|
|
|
return { this->mBegin, this->mIter, this->mEnd, pred2 };
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an iterator that refines the subclass (and not removing const),
|
|
|
|
// advancing to the first position at or after the old position that
|
|
|
|
// satisfies the type constraint, or to the end
|
|
|
|
template < typename TrackType2 >
|
|
|
|
auto Filter() const
|
|
|
|
-> typename std::enable_if<
|
|
|
|
std::is_base_of< TrackType, TrackType2 >::value &&
|
|
|
|
(!std::is_const<TrackType>::value ||
|
|
|
|
std::is_const<TrackType2>::value),
|
|
|
|
TrackIter< TrackType2 >
|
|
|
|
>::type
|
|
|
|
{
|
|
|
|
return { this->mBegin, this->mIter, this->mEnd, this->mPred };
|
|
|
|
}
|
|
|
|
|
|
|
|
const FunctionType &GetPredicate() const
|
|
|
|
{ return this->mPred; }
|
|
|
|
|
|
|
|
// Unlike with STL iterators, this class gives well defined behavior when
|
|
|
|
// you increment an end iterator: you get the same.
|
|
|
|
TrackIter &operator ++ ()
|
|
|
|
{
|
|
|
|
// Maintain the class invariant
|
|
|
|
if (this->mIter != this->mEnd) do
|
|
|
|
++this->mIter.first;
|
|
|
|
while (this->mIter != this->mEnd && !this->valid() );
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
TrackIter operator ++ (int)
|
|
|
|
{
|
|
|
|
TrackIter result { *this };
|
|
|
|
this-> operator ++ ();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unlike with STL iterators, this class gives well defined behavior when
|
|
|
|
// you decrement past the beginning of a range: you wrap and get an end
|
|
|
|
// iterator.
|
|
|
|
TrackIter &operator -- ()
|
|
|
|
{
|
|
|
|
// Maintain the class invariant
|
|
|
|
do {
|
|
|
|
if (this->mIter == this->mBegin)
|
|
|
|
// Go circularly
|
|
|
|
this->mIter = this->mEnd;
|
|
|
|
else
|
|
|
|
--this->mIter.first;
|
|
|
|
} while (this->mIter != this->mEnd && !this->valid() );
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
TrackIter operator -- (int)
|
|
|
|
{
|
|
|
|
TrackIter result { *this };
|
|
|
|
this->operator -- ();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unlike with STL iterators, this class gives well defined behavior when
|
|
|
|
// you dereference an end iterator: you get a null pointer.
|
|
|
|
TrackType *operator * () const
|
|
|
|
{
|
|
|
|
if (this->mIter == this->mEnd)
|
|
|
|
return nullptr;
|
|
|
|
else
|
|
|
|
// Other methods guarantee that the cast is correct
|
|
|
|
// (provided no operations on the TrackList invalidated
|
|
|
|
// underlying iterators or replaced the tracks there)
|
|
|
|
return static_cast< TrackType * >( &**this->mIter.first );
|
|
|
|
}
|
|
|
|
|
|
|
|
// This might be called operator + ,
|
|
|
|
// but that might wrongly suggest constant time when the iterator is not
|
|
|
|
// random access.
|
|
|
|
TrackIter advance( long amount ) const
|
|
|
|
{
|
|
|
|
auto copy = *this;
|
|
|
|
std::advance( copy, amount );
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend inline bool operator == (TrackIter a, TrackIter b)
|
|
|
|
{
|
|
|
|
// Assume the predicate is not stateful. Just compare the iterators.
|
|
|
|
return
|
|
|
|
a.mIter == b.mIter
|
|
|
|
// Assume this too:
|
|
|
|
// && a.mBegin == b.mBegin && a.mEnd == b.mEnd
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend inline bool operator != (TrackIter a, TrackIter b)
|
|
|
|
{
|
|
|
|
return !(a == b);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool valid() const
|
|
|
|
{
|
|
|
|
// assume mIter != mEnd
|
|
|
|
const auto pTrack = track_cast< TrackType * >( &**this->mIter.first );
|
|
|
|
if (!pTrack)
|
|
|
|
return false;
|
|
|
|
return !this->mPred || this->mPred( pTrack );
|
|
|
|
}
|
|
|
|
|
2018-10-30 15:33:31 +00:00
|
|
|
// This friendship is needed in TrackIterRange::StartingWith and
|
|
|
|
// TrackIterRange::EndingAfter()
|
2018-10-30 14:51:04 +00:00
|
|
|
friend TrackIterRange< TrackType >;
|
|
|
|
|
2016-11-02 15:45:03 +00:00
|
|
|
// The class invariant is that mIter == mEnd, or else, mIter != mEnd and
|
|
|
|
// **mIter is of the appropriate subclass and mPred(&**mIter) is true.
|
|
|
|
TrackNodePointer mBegin, mIter, mEnd;
|
|
|
|
FunctionType mPred;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <
|
|
|
|
typename TrackType // Track or a subclass, maybe const-qualified
|
|
|
|
> struct TrackIterRange
|
|
|
|
: public IteratorRange< TrackIter< TrackType > >
|
|
|
|
{
|
|
|
|
TrackIterRange
|
|
|
|
( const TrackIter< TrackType > &begin,
|
|
|
|
const TrackIter< TrackType > &end )
|
|
|
|
: IteratorRange< TrackIter< TrackType > >
|
|
|
|
( begin, end )
|
|
|
|
{}
|
|
|
|
|
|
|
|
// Conjoin the filter predicate with another predicate
|
|
|
|
// Read + as "and"
|
|
|
|
template< typename Predicate2 >
|
|
|
|
TrackIterRange operator + ( const Predicate2 &pred2 ) const
|
|
|
|
{
|
|
|
|
const auto &pred1 = this->first.GetPredicate();
|
|
|
|
using Function = typename TrackIter<TrackType>::FunctionType;
|
|
|
|
const auto &newPred = pred1
|
|
|
|
? Function{ [=] (typename Function::argument_type track) {
|
|
|
|
return pred1(track) && pred2(track);
|
|
|
|
} }
|
|
|
|
: Function{ pred2 };
|
|
|
|
return {
|
|
|
|
this->first.Filter( newPred ),
|
|
|
|
this->second.Filter( newPred )
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Specify the added conjunct as a pointer to member function
|
2018-10-30 13:32:18 +00:00
|
|
|
// Read + as "and"
|
2016-11-02 15:45:03 +00:00
|
|
|
template< typename R, typename C >
|
|
|
|
TrackIterRange operator + ( R ( C ::* pmf ) () const ) const
|
|
|
|
{
|
|
|
|
return this->operator + ( std::mem_fn( pmf ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conjoin the filter predicate with the negation of another predicate
|
|
|
|
// Read - as "and not"
|
|
|
|
template< typename Predicate2 >
|
|
|
|
TrackIterRange operator - ( const Predicate2 &pred2 ) const
|
|
|
|
{
|
|
|
|
using ArgumentType =
|
|
|
|
typename TrackIterRange::iterator::FunctionType::argument_type;
|
|
|
|
auto neg = [=] (ArgumentType track) { return !pred2( track ); };
|
|
|
|
return this->operator + ( neg );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Specify the negated conjunct as a pointer to member function
|
2018-10-30 13:32:18 +00:00
|
|
|
// Read - as "and not"
|
2016-11-02 15:45:03 +00:00
|
|
|
template< typename R, typename C >
|
|
|
|
TrackIterRange operator - ( R ( C ::* pmf ) () const ) const
|
|
|
|
{
|
|
|
|
return this->operator + ( std::not1( std::mem_fn( pmf ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
template< typename TrackType2 >
|
|
|
|
TrackIterRange< TrackType2 > Filter() const
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
this-> first.template Filter< TrackType2 >(),
|
|
|
|
this->second.template Filter< TrackType2 >()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
TrackIterRange StartingWith( const Track *pTrack ) const
|
|
|
|
{
|
2018-10-30 15:33:31 +00:00
|
|
|
auto newBegin = this->find( pTrack );
|
|
|
|
// More careful construction is needed so that the independent
|
|
|
|
// increment and decrement of each iterator in the NEW pair
|
|
|
|
// has the expected behavior at boundaries of the range
|
2016-11-02 15:45:03 +00:00
|
|
|
return {
|
2018-10-30 15:33:31 +00:00
|
|
|
{ newBegin.mIter, newBegin.mIter, this->second.mEnd,
|
|
|
|
this->first.GetPredicate() },
|
|
|
|
{ newBegin.mIter, this->second.mEnd, this->second.mEnd,
|
|
|
|
this->second.GetPredicate() }
|
2016-11-02 15:45:03 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
TrackIterRange EndingAfter( const Track *pTrack ) const
|
|
|
|
{
|
2018-10-30 14:51:04 +00:00
|
|
|
const auto newEnd = this->reversal().find( pTrack ).base();
|
|
|
|
// More careful construction is needed so that the independent
|
|
|
|
// increment and decrement of each iterator in the NEW pair
|
|
|
|
// has the expected behavior at boundaries of the range
|
2016-11-02 15:45:03 +00:00
|
|
|
return {
|
2018-10-30 14:51:04 +00:00
|
|
|
{ this->first.mBegin, this->first.mIter, newEnd.mIter,
|
|
|
|
this->first.GetPredicate() },
|
|
|
|
{ this->first.mBegin, newEnd.mIter, newEnd.mIter,
|
|
|
|
this->second.GetPredicate() }
|
2016-11-02 15:45:03 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exclude one given track
|
|
|
|
TrackIterRange Excluding ( const TrackType *pExcluded ) const
|
|
|
|
{
|
|
|
|
return this->operator - (
|
|
|
|
[=](const Track *pTrack){ return pExcluded == pTrack; } );
|
|
|
|
}
|
2017-01-08 16:24:34 +00:00
|
|
|
|
|
|
|
// See Track::TypeSwitch
|
|
|
|
template< typename ...Functions >
|
|
|
|
void Visit(const Functions &...functions)
|
|
|
|
{
|
|
|
|
for (auto track : *this)
|
|
|
|
track->TypeSwitch(functions...);
|
|
|
|
}
|
|
|
|
|
|
|
|
// See Track::TypeSwitch
|
|
|
|
// Visit until flag is false, or no more tracks
|
|
|
|
template< typename Flag, typename ...Functions >
|
|
|
|
void VisitWhile(Flag &flag, const Functions &...functions)
|
|
|
|
{
|
|
|
|
if ( flag ) for (auto track : *this) {
|
|
|
|
track->TypeSwitch(functions...);
|
|
|
|
if (!flag)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-11-02 15:45:03 +00:00
|
|
|
};
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-10-28 01:23:20 +00:00
|
|
|
struct TrackListEvent : public wxCommandEvent
|
2017-11-04 17:33:11 +00:00
|
|
|
{
|
2018-02-05 22:56:46 +00:00
|
|
|
explicit
|
|
|
|
TrackListEvent(
|
|
|
|
wxEventType commandType,
|
|
|
|
const std::weak_ptr<Track> &pTrack = {}, int code = -1)
|
2018-10-28 01:23:20 +00:00
|
|
|
: wxCommandEvent{ commandType }
|
2018-02-05 22:56:46 +00:00
|
|
|
, mpTrack{ pTrack }
|
|
|
|
, mCode{ code }
|
|
|
|
{}
|
2017-11-04 17:33:11 +00:00
|
|
|
|
|
|
|
TrackListEvent( const TrackListEvent& ) = default;
|
|
|
|
|
|
|
|
wxEvent *Clone() const override { return new TrackListEvent(*this); }
|
|
|
|
|
|
|
|
std::weak_ptr<Track> mpTrack;
|
2018-02-05 22:56:46 +00:00
|
|
|
int mCode;
|
2017-11-04 17:33:11 +00:00
|
|
|
};
|
|
|
|
|
2018-02-05 22:56:46 +00:00
|
|
|
// Posted when the set of selected tracks changes.
|
|
|
|
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
|
|
|
|
EVT_TRACKLIST_SELECTION_CHANGE, TrackListEvent);
|
|
|
|
|
|
|
|
// Posted when certain fields of a track change.
|
|
|
|
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
|
|
|
|
EVT_TRACKLIST_TRACK_DATA_CHANGE, TrackListEvent);
|
|
|
|
|
2017-06-26 17:40:19 +00:00
|
|
|
// Posted when tracks are reordered but otherwise unchanged.
|
2018-02-12 23:09:20 +00:00
|
|
|
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
|
2018-02-05 22:56:46 +00:00
|
|
|
EVT_TRACKLIST_PERMUTED, TrackListEvent);
|
2017-06-26 17:40:19 +00:00
|
|
|
|
2018-02-05 22:56:46 +00:00
|
|
|
// Posted when some track changed its height.
|
|
|
|
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
|
|
|
|
EVT_TRACKLIST_RESIZING, TrackListEvent);
|
|
|
|
|
|
|
|
// Posted when a track has been added to a tracklist.
|
|
|
|
// Also posted when one track replaces another
|
2018-02-12 23:09:20 +00:00
|
|
|
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
|
2018-02-05 22:56:46 +00:00
|
|
|
EVT_TRACKLIST_ADDITION, TrackListEvent);
|
2017-06-25 05:33:31 +00:00
|
|
|
|
|
|
|
// Posted when a track has been deleted from a tracklist.
|
2017-06-01 18:19:52 +00:00
|
|
|
// Also posted when one track replaces another
|
2018-02-12 23:09:20 +00:00
|
|
|
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
|
2018-02-05 22:56:46 +00:00
|
|
|
EVT_TRACKLIST_DELETION, TrackListEvent);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-12-19 17:43:20 +00:00
|
|
|
/** \brief TrackList is a flat linked list of tracks supporting Add, Remove,
|
|
|
|
* Clear, and Contains, plus serialization of the list of tracks.
|
|
|
|
*/
|
2019-05-06 23:00:10 +00:00
|
|
|
class TrackList final
|
|
|
|
: public wxEvtHandler
|
|
|
|
, public ListOfTracks
|
2018-11-19 22:04:26 +00:00
|
|
|
, public std::enable_shared_from_this<TrackList>
|
2019-05-06 23:00:10 +00:00
|
|
|
, public ClientData::Base
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2019-02-12 00:30:13 +00:00
|
|
|
// privatize this, make you use Add instead:
|
|
|
|
using ListOfTracks::push_back;
|
|
|
|
|
2016-12-17 15:05:15 +00:00
|
|
|
// privatize this, make you use Swap instead:
|
|
|
|
using ListOfTracks::swap;
|
|
|
|
|
2018-01-11 04:58:36 +00:00
|
|
|
// Disallow copy
|
2017-07-15 01:47:47 +00:00
|
|
|
TrackList(const TrackList &that) = delete;
|
2018-01-11 04:58:36 +00:00
|
|
|
TrackList &operator= (const TrackList&) = delete;
|
|
|
|
|
|
|
|
// Allow move
|
|
|
|
TrackList(TrackList &&that) : TrackList() { Swap(that); }
|
|
|
|
TrackList& operator= (TrackList&&);
|
2017-07-15 01:47:47 +00:00
|
|
|
|
2017-07-15 02:30:02 +00:00
|
|
|
void clear() = delete;
|
|
|
|
|
2017-07-15 01:47:47 +00:00
|
|
|
public:
|
2019-05-06 23:00:10 +00:00
|
|
|
static TrackList &Get( AudacityProject &project );
|
|
|
|
static const TrackList &Get( const AudacityProject &project );
|
|
|
|
|
2018-11-19 22:04:26 +00:00
|
|
|
// Create an empty TrackList
|
|
|
|
// Don't call directly -- use Create() instead
|
|
|
|
TrackList();
|
|
|
|
|
2017-07-15 01:47:47 +00:00
|
|
|
// Create an empty TrackList
|
|
|
|
static std::shared_ptr<TrackList> Create();
|
|
|
|
|
2016-03-07 11:43:42 +00:00
|
|
|
// Move is defined in terms of Swap
|
|
|
|
void Swap(TrackList &that);
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// Destructor
|
|
|
|
virtual ~TrackList();
|
|
|
|
|
2016-11-02 15:45:03 +00:00
|
|
|
// Iteration
|
|
|
|
|
2018-01-14 19:01:04 +00:00
|
|
|
// Hide the inherited begin() and end()
|
2017-04-09 20:58:41 +00:00
|
|
|
using iterator = TrackIter<Track>;
|
|
|
|
using const_iterator = TrackIter<const Track>;
|
2018-01-14 19:01:04 +00:00
|
|
|
using value_type = Track *;
|
2017-04-09 20:58:41 +00:00
|
|
|
iterator begin() { return Any().begin(); }
|
|
|
|
iterator end() { return Any().end(); }
|
|
|
|
const_iterator begin() const { return Any().begin(); }
|
|
|
|
const_iterator end() const { return Any().end(); }
|
2018-01-14 19:01:04 +00:00
|
|
|
const_iterator cbegin() const { return begin(); }
|
|
|
|
const_iterator cend() const { return end(); }
|
|
|
|
|
2016-11-02 15:45:03 +00:00
|
|
|
// Turn a pointer into an iterator (constant time).
|
|
|
|
template < typename TrackType = Track >
|
|
|
|
auto Find(Track *pTrack)
|
|
|
|
-> TrackIter< TrackType >
|
|
|
|
{
|
|
|
|
if (!pTrack || pTrack->GetOwner().get() != this)
|
|
|
|
return EndIterator<TrackType>();
|
|
|
|
else
|
|
|
|
return MakeTrackIterator<TrackType>( pTrack->GetNode() );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn a pointer into an iterator (constant time).
|
|
|
|
template < typename TrackType = const Track >
|
|
|
|
auto Find(const Track *pTrack) const
|
|
|
|
-> typename std::enable_if< std::is_const<TrackType>::value,
|
|
|
|
TrackIter< TrackType >
|
|
|
|
>::type
|
|
|
|
{
|
|
|
|
if (!pTrack || pTrack->GetOwner().get() != this)
|
|
|
|
return EndIterator<TrackType>();
|
|
|
|
else
|
|
|
|
return MakeTrackIterator<TrackType>( pTrack->GetNode() );
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the track is not an audio track, or not one of a group of channels,
|
|
|
|
// return the track itself; else return the first channel of its group --
|
|
|
|
// in either case as an iterator that will only visit other leader tracks.
|
|
|
|
// (Generalizing away from the assumption of at most stereo)
|
|
|
|
TrackIter< Track > FindLeader( Track *pTrack );
|
|
|
|
|
|
|
|
TrackIter< const Track >
|
|
|
|
FindLeader( const Track *pTrack ) const
|
|
|
|
{
|
|
|
|
return const_cast<TrackList*>(this)->
|
|
|
|
FindLeader( const_cast<Track*>(pTrack) ).Filter< const Track >();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template < typename TrackType = Track >
|
|
|
|
auto Any()
|
|
|
|
-> TrackIterRange< TrackType >
|
|
|
|
{
|
|
|
|
return Tracks< TrackType >();
|
|
|
|
}
|
|
|
|
|
|
|
|
template < typename TrackType = const Track >
|
|
|
|
auto Any() const
|
|
|
|
-> typename std::enable_if< std::is_const<TrackType>::value,
|
|
|
|
TrackIterRange< TrackType >
|
|
|
|
>::type
|
|
|
|
{
|
|
|
|
return Tracks< TrackType >();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Abbreviating some frequently used cases
|
|
|
|
template < typename TrackType = Track >
|
|
|
|
auto Selected()
|
|
|
|
-> TrackIterRange< TrackType >
|
|
|
|
{
|
|
|
|
return Tracks< TrackType >( &Track::IsSelected );
|
|
|
|
}
|
|
|
|
|
|
|
|
template < typename TrackType = const Track >
|
|
|
|
auto Selected() const
|
|
|
|
-> typename std::enable_if< std::is_const<TrackType>::value,
|
|
|
|
TrackIterRange< TrackType >
|
|
|
|
>::type
|
|
|
|
{
|
|
|
|
return Tracks< TrackType >( &Track::IsSelected );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template < typename TrackType = Track >
|
|
|
|
auto Leaders()
|
|
|
|
-> TrackIterRange< TrackType >
|
|
|
|
{
|
|
|
|
return Tracks< TrackType >( &Track::IsLeader );
|
|
|
|
}
|
|
|
|
|
|
|
|
template < typename TrackType = const Track >
|
|
|
|
auto Leaders() const
|
|
|
|
-> typename std::enable_if< std::is_const<TrackType>::value,
|
|
|
|
TrackIterRange< TrackType >
|
|
|
|
>::type
|
|
|
|
{
|
|
|
|
return Tracks< TrackType >( &Track::IsLeader );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template < typename TrackType = Track >
|
|
|
|
auto SelectedLeaders()
|
|
|
|
-> TrackIterRange< TrackType >
|
|
|
|
{
|
|
|
|
return Tracks< TrackType >( &Track::IsSelectedLeader );
|
|
|
|
}
|
|
|
|
|
|
|
|
template < typename TrackType = const Track >
|
|
|
|
auto SelectedLeaders() const
|
|
|
|
-> typename std::enable_if< std::is_const<TrackType>::value,
|
|
|
|
TrackIterRange< TrackType >
|
|
|
|
>::type
|
|
|
|
{
|
|
|
|
return Tracks< TrackType >( &Track::IsSelectedLeader );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename TrackType>
|
|
|
|
static auto SingletonRange( TrackType *pTrack )
|
|
|
|
-> TrackIterRange< TrackType >
|
|
|
|
{
|
|
|
|
return pTrack->GetOwner()->template Any<TrackType>()
|
|
|
|
.StartingWith( pTrack ).EndingAfter( pTrack );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static TrackIterRange< Track >
|
|
|
|
SyncLockGroup( Track *pTrack );
|
|
|
|
|
|
|
|
static TrackIterRange< const Track >
|
|
|
|
SyncLockGroup( const Track *pTrack )
|
|
|
|
{
|
|
|
|
return SyncLockGroup(const_cast<Track*>(pTrack)).Filter<const Track>();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-11-19 04:07:05 +00:00
|
|
|
Track *DoAddToHead(const std::shared_ptr<Track> &t);
|
|
|
|
Track *DoAdd(const std::shared_ptr<Track> &t);
|
|
|
|
|
2016-11-02 15:45:03 +00:00
|
|
|
template< typename TrackType, typename InTrackType >
|
|
|
|
static TrackIterRange< TrackType >
|
|
|
|
Channels_( TrackIter< InTrackType > iter1 )
|
|
|
|
{
|
|
|
|
// Assume iterator filters leader tracks
|
|
|
|
if (*iter1) {
|
|
|
|
return {
|
|
|
|
iter1.Filter( &Track::Any )
|
|
|
|
.template Filter<TrackType>(),
|
|
|
|
(++iter1).Filter( &Track::Any )
|
|
|
|
.template Filter<TrackType>()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
// empty range
|
|
|
|
return {
|
|
|
|
iter1.template Filter<TrackType>(),
|
|
|
|
iter1.template Filter<TrackType>()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
// Find an iterator range of channels including the given track.
|
|
|
|
template< typename TrackType >
|
|
|
|
static auto Channels( TrackType *pTrack )
|
|
|
|
-> TrackIterRange< TrackType >
|
|
|
|
{
|
|
|
|
return Channels_<TrackType>( pTrack->GetOwner()->FindLeader(pTrack) );
|
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
friend class Track;
|
|
|
|
|
2016-03-01 22:34:45 +00:00
|
|
|
/// For use in sorting: assume each iterator points into this list, no duplications
|
|
|
|
void Permute(const std::vector<TrackNodePointer> &permutation);
|
|
|
|
|
2018-01-11 00:28:16 +00:00
|
|
|
Track *FindById( TrackId id );
|
|
|
|
|
|
|
|
/// Add a Track, giving it a fresh id
|
2016-03-13 15:08:21 +00:00
|
|
|
template<typename TrackKind>
|
2018-11-19 04:07:05 +00:00
|
|
|
TrackKind *AddToHead( const std::shared_ptr< TrackKind > &t )
|
|
|
|
{ return static_cast< TrackKind* >( DoAddToHead( t ) ); }
|
2018-01-11 00:28:16 +00:00
|
|
|
|
2016-03-13 15:08:21 +00:00
|
|
|
template<typename TrackKind>
|
2018-11-19 04:07:05 +00:00
|
|
|
TrackKind *Add( const std::shared_ptr< TrackKind > &t )
|
|
|
|
{ return static_cast< TrackKind* >( DoAdd( t ) ); }
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-09-19 18:32:41 +00:00
|
|
|
/** \brief Define a group of channels starting at the given track
|
|
|
|
*
|
|
|
|
* @param track and (groupSize - 1) following tracks must be in this
|
|
|
|
* list. They will be disassociated from any groups they already belong to.
|
|
|
|
* @param groupSize must be at least 1.
|
|
|
|
* @param resetChannels if true, disassociated channels will be marked Mono.
|
|
|
|
*/
|
|
|
|
void GroupChannels(
|
|
|
|
Track &track, size_t groupSize, bool resetChannels = true );
|
|
|
|
|
2016-03-13 15:08:21 +00:00
|
|
|
/// Replace first track with second track, give back a holder
|
2018-01-11 00:28:16 +00:00
|
|
|
/// Give the replacement the same id as the replaced
|
2018-11-19 18:17:28 +00:00
|
|
|
ListOfTracks::value_type Replace(
|
|
|
|
Track * t, const ListOfTracks::value_type &with);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
/// Remove this Track or all children of this TrackList.
|
2016-03-01 23:31:51 +00:00
|
|
|
/// Return an iterator to what followed the removed track.
|
2016-03-01 21:28:59 +00:00
|
|
|
TrackNodePointer Remove(Track *t);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
/// Make the list empty
|
2016-06-28 03:38:42 +00:00
|
|
|
void Clear(bool sendEvent = true);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2018-09-11 15:45:05 +00:00
|
|
|
int GetGroupHeight(const Track * t) const;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
bool CanMoveUp(Track * t) const;
|
|
|
|
bool CanMoveDown(Track * t) const;
|
|
|
|
|
|
|
|
bool MoveUp(Track * t);
|
|
|
|
bool MoveDown(Track * t);
|
|
|
|
bool Move(Track * t, bool up) { return up ? MoveUp(t) : MoveDown(t); }
|
|
|
|
|
|
|
|
/// Mainly a test function. Uses a linear search, so could be slow.
|
2016-05-22 03:19:09 +00:00
|
|
|
bool Contains(const Track * t) const;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-06-28 04:31:18 +00:00
|
|
|
// Return non-null only if the weak pointer is not, and the track is
|
|
|
|
// owned by this list; constant time.
|
|
|
|
template <typename Subclass>
|
|
|
|
std::shared_ptr<Subclass> Lock(const std::weak_ptr<Subclass> &wTrack)
|
|
|
|
{
|
|
|
|
auto pTrack = wTrack.lock();
|
2017-07-10 15:53:23 +00:00
|
|
|
if (pTrack) {
|
|
|
|
auto pList = pTrack->mList.lock();
|
|
|
|
if (pTrack && this == pList.get())
|
|
|
|
return pTrack;
|
|
|
|
}
|
2017-06-28 04:31:18 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2018-01-14 21:55:49 +00:00
|
|
|
bool empty() const;
|
|
|
|
size_t size() const;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
double GetStartTime() const;
|
|
|
|
double GetEndTime() const;
|
|
|
|
|
|
|
|
double GetMinOffset() const;
|
|
|
|
int GetHeight() const;
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
#if LEGACY_PROJECT_FILE_SUPPORT
|
2010-01-23 19:44:49 +00:00
|
|
|
// File I/O
|
2016-02-24 06:06:47 +00:00
|
|
|
bool Load(wxTextFile * in, DirManager * dirManager) override;
|
|
|
|
bool Save(wxTextFile * out, bool overwrite) override;
|
2010-01-23 19:44:49 +00:00
|
|
|
#endif
|
|
|
|
|
2016-03-01 23:31:51 +00:00
|
|
|
private:
|
2016-11-02 15:45:03 +00:00
|
|
|
|
|
|
|
// Visit all tracks satisfying a predicate, mutative access
|
|
|
|
template <
|
|
|
|
typename TrackType = Track,
|
|
|
|
typename Pred =
|
|
|
|
typename TrackIterRange< TrackType >::iterator::FunctionType
|
|
|
|
>
|
|
|
|
auto Tracks( const Pred &pred = {} )
|
|
|
|
-> TrackIterRange< TrackType >
|
|
|
|
{
|
|
|
|
auto b = getBegin(), e = getEnd();
|
|
|
|
return { { b, b, e, pred }, { b, e, e, pred } };
|
|
|
|
}
|
|
|
|
|
|
|
|
// Visit all tracks satisfying a predicate, const access
|
|
|
|
template <
|
|
|
|
typename TrackType = const Track,
|
|
|
|
typename Pred =
|
|
|
|
typename TrackIterRange< TrackType >::iterator::FunctionType
|
|
|
|
>
|
|
|
|
auto Tracks( const Pred &pred = {} ) const
|
|
|
|
-> typename std::enable_if< std::is_const<TrackType>::value,
|
|
|
|
TrackIterRange< TrackType >
|
|
|
|
>::type
|
|
|
|
{
|
|
|
|
auto b = const_cast<TrackList*>(this)->getBegin();
|
|
|
|
auto e = const_cast<TrackList*>(this)->getEnd();
|
|
|
|
return { { b, b, e, pred }, { b, e, e, pred } };
|
|
|
|
}
|
|
|
|
|
2016-12-30 00:31:04 +00:00
|
|
|
Track *GetPrev(Track * t, bool linked = false) const;
|
|
|
|
Track *GetNext(Track * t, bool linked = false) const;
|
|
|
|
|
2016-11-02 15:45:03 +00:00
|
|
|
std::pair<Track *, Track *> FindSyncLockGroup(Track *pMember) const;
|
|
|
|
|
|
|
|
template < typename TrackType >
|
|
|
|
TrackIter< TrackType >
|
|
|
|
MakeTrackIterator( TrackNodePointer iter ) const
|
|
|
|
{
|
|
|
|
auto b = const_cast<TrackList*>(this)->getBegin();
|
|
|
|
auto e = const_cast<TrackList*>(this)->getEnd();
|
|
|
|
return { b, iter, e };
|
|
|
|
}
|
|
|
|
|
|
|
|
template < typename TrackType >
|
|
|
|
TrackIter< TrackType >
|
|
|
|
EndIterator() const
|
|
|
|
{
|
|
|
|
auto e = const_cast<TrackList*>(this)->getEnd();
|
|
|
|
return { e, e, e };
|
|
|
|
}
|
|
|
|
|
|
|
|
TrackIterRange< Track > EmptyRange() const;
|
|
|
|
|
2016-03-01 23:31:51 +00:00
|
|
|
bool isNull(TrackNodePointer p) const
|
2018-02-23 18:57:58 +00:00
|
|
|
{ return (p.second == this && p.first == ListOfTracks::end())
|
|
|
|
|| (p.second == &mPendingUpdates && p.first == mPendingUpdates.end()); }
|
2018-01-13 01:17:30 +00:00
|
|
|
TrackNodePointer getEnd() const
|
2018-02-23 18:57:58 +00:00
|
|
|
{ return { const_cast<TrackList*>(this)->ListOfTracks::end(),
|
|
|
|
const_cast<TrackList*>(this)}; }
|
2018-01-13 01:17:30 +00:00
|
|
|
TrackNodePointer getBegin() const
|
2018-02-23 18:57:58 +00:00
|
|
|
{ return { const_cast<TrackList*>(this)->ListOfTracks::begin(),
|
|
|
|
const_cast<TrackList*>(this)}; }
|
2018-01-13 01:17:30 +00:00
|
|
|
|
|
|
|
// Move an iterator to the next node, if any; else stay at end
|
|
|
|
TrackNodePointer getNext(TrackNodePointer p) const
|
|
|
|
{
|
|
|
|
if ( isNull(p) )
|
|
|
|
return p;
|
|
|
|
auto q = p;
|
2018-02-23 18:57:58 +00:00
|
|
|
++q.first;
|
|
|
|
return q;
|
2018-01-13 01:17:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Move an iterator to the previous node, if any; else wrap to end
|
|
|
|
TrackNodePointer getPrev(TrackNodePointer p) const
|
|
|
|
{
|
2018-02-23 18:57:58 +00:00
|
|
|
if (p == getBegin())
|
2018-01-13 01:17:30 +00:00
|
|
|
return getEnd();
|
|
|
|
else {
|
|
|
|
auto q = p;
|
2018-02-23 18:57:58 +00:00
|
|
|
--q.first;
|
|
|
|
return q;
|
2018-01-13 01:17:30 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-01 23:31:51 +00:00
|
|
|
|
|
|
|
void RecalcPositions(TrackNodePointer node);
|
2018-02-05 22:56:46 +00:00
|
|
|
void SelectionEvent( const std::shared_ptr<Track> &pTrack );
|
2017-06-26 17:40:19 +00:00
|
|
|
void PermutationEvent();
|
2018-02-05 22:56:46 +00:00
|
|
|
void DataEvent( const std::shared_ptr<Track> &pTrack, int code );
|
2017-06-25 05:33:31 +00:00
|
|
|
void DeletionEvent();
|
2018-02-05 22:56:46 +00:00
|
|
|
void AdditionEvent(TrackNodePointer node);
|
2017-06-25 05:33:31 +00:00
|
|
|
void ResizingEvent(TrackNodePointer node);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-03-01 23:31:51 +00:00
|
|
|
void SwapNodes(TrackNodePointer s1, TrackNodePointer s2);
|
2017-07-10 15:48:11 +00:00
|
|
|
|
2018-01-11 00:28:16 +00:00
|
|
|
// Nondecreasing during the session.
|
|
|
|
// Nonpersistent.
|
|
|
|
// Used to assign ids to added tracks.
|
|
|
|
static long sCounter;
|
2017-08-24 18:31:51 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
using Updater = std::function< void(Track &dest, const Track &src) >;
|
|
|
|
// Start a deferred update of the project.
|
|
|
|
// The return value is a duplicate of the given track.
|
|
|
|
// While ApplyPendingTracks or ClearPendingTracks is not yet called,
|
|
|
|
// there may be other direct changes to the project that push undo history.
|
|
|
|
// Meanwhile the returned object can accumulate other changes for a deferred
|
|
|
|
// push, and temporarily shadow the actual project track for display purposes.
|
|
|
|
// The Updater function, if not null, merges state (from the actual project
|
|
|
|
// into the pending track) which is not meant to be overridden by the
|
|
|
|
// accumulated pending changes.
|
|
|
|
// To keep the display consistent, the Y and Height values, minimized state,
|
|
|
|
// and Linked state must be copied, and this will be done even if the
|
|
|
|
// Updater does not do it.
|
|
|
|
// Pending track will have the same TrackId as the actual.
|
|
|
|
// Pending changed tracks will not occur in iterations.
|
|
|
|
std::shared_ptr<Track> RegisterPendingChangedTrack(
|
|
|
|
Updater updater,
|
|
|
|
Track *src
|
|
|
|
);
|
|
|
|
|
|
|
|
// Like the previous, but for a NEW track, not a replacement track. Caller
|
|
|
|
// supplies the track, and there are no updates.
|
|
|
|
// Pending track will have an unassigned TrackId.
|
|
|
|
// Pending changed tracks WILL occur in iterations, always after actual
|
|
|
|
// tracks, and in the sequence that they were added. They can be
|
|
|
|
// distinguished from actual tracks by TrackId.
|
|
|
|
void RegisterPendingNewTrack( const std::shared_ptr<Track> &pTrack );
|
|
|
|
|
|
|
|
// Invoke the updaters of pending tracks. Pass any exceptions from the
|
|
|
|
// updater functions.
|
|
|
|
void UpdatePendingTracks();
|
|
|
|
|
|
|
|
// Forget pending track additions and changes;
|
|
|
|
// if requested, give back the pending added tracks.
|
|
|
|
void ClearPendingTracks( ListOfTracks *pAdded = nullptr );
|
|
|
|
|
|
|
|
// Change the state of the project.
|
|
|
|
// Strong guarantee for project state in case of exceptions.
|
|
|
|
// Will always clear the pending updates.
|
|
|
|
// Return true if the state of the track list really did change.
|
|
|
|
bool ApplyPendingTracks();
|
|
|
|
|
|
|
|
bool HasPendingTracks() const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Need to put pending tracks into a list so that GetLink() works
|
|
|
|
ListOfTracks mPendingUpdates;
|
|
|
|
// This is in correspondence with mPendingUpdates
|
|
|
|
std::vector< Updater > mUpdaters;
|
2010-01-23 19:44:49 +00:00
|
|
|
};
|
|
|
|
|
2019-01-22 15:59:44 +00:00
|
|
|
class AUDACITY_DLL_API TrackFactory final
|
|
|
|
: public ClientData::Base
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2019-01-22 15:59:44 +00:00
|
|
|
public:
|
|
|
|
static TrackFactory &Get( AudacityProject &project );
|
|
|
|
static const TrackFactory &Get( const AudacityProject &project );
|
|
|
|
static TrackFactory &Reset( AudacityProject &project );
|
|
|
|
static void Destroy( AudacityProject &project );
|
|
|
|
|
2016-08-13 04:38:31 +00:00
|
|
|
TrackFactory(const std::shared_ptr<DirManager> &dirManager, const ZoomInfo *zoomInfo):
|
2010-01-23 19:44:49 +00:00
|
|
|
mDirManager(dirManager)
|
2016-01-26 22:37:16 +00:00
|
|
|
, mZoomInfo(zoomInfo)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-01-22 15:59:44 +00:00
|
|
|
private:
|
2016-08-13 04:38:31 +00:00
|
|
|
const std::shared_ptr<DirManager> mDirManager;
|
2016-01-26 22:37:16 +00:00
|
|
|
const ZoomInfo *const mZoomInfo;
|
2010-01-23 19:44:49 +00:00
|
|
|
friend class AudacityProject;
|
|
|
|
friend class BenchmarkDialog;
|
|
|
|
|
|
|
|
public:
|
|
|
|
// These methods are defined in WaveTrack.cpp, NoteTrack.cpp,
|
|
|
|
// LabelTrack.cpp, and TimeTrack.cpp respectively
|
2018-11-19 04:07:05 +00:00
|
|
|
std::shared_ptr<WaveTrack> DuplicateWaveTrack(const WaveTrack &orig);
|
|
|
|
std::shared_ptr<WaveTrack> NewWaveTrack(sampleFormat format = (sampleFormat)0,
|
2010-01-23 19:44:49 +00:00
|
|
|
double rate = 0);
|
2018-11-19 04:07:05 +00:00
|
|
|
std::shared_ptr<LabelTrack> NewLabelTrack();
|
|
|
|
std::shared_ptr<TimeTrack> NewTimeTrack();
|
2010-01-23 19:44:49 +00:00
|
|
|
#if defined(USE_MIDI)
|
2018-11-19 04:07:05 +00:00
|
|
|
std::shared_ptr<NoteTrack> NewNoteTrack();
|
2010-01-23 19:44:49 +00:00
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|