mirror of https://github.com/odrling/Aegisub
Support dragging multiple markers at once in the dialogue timing controller
When ctrl is held down and the user clicks on one of the active line's markers, all markers at the same position as the clicked marker (for both active and inactive lines) are now moved along with the clicked marker. Closes #20. Originally committed to SVN as r6461.
This commit is contained in:
parent
32c8cc0974
commit
47cf5c8629
|
@ -50,109 +50,252 @@
|
||||||
#include "selection_controller.h"
|
#include "selection_controller.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
/// @class AudioMarkerDialogueTiming
|
class TimeableLine;
|
||||||
|
|
||||||
|
/// @class DialogueTimingMarker
|
||||||
/// @brief AudioMarker implementation for AudioTimingControllerDialogue
|
/// @brief AudioMarker implementation for AudioTimingControllerDialogue
|
||||||
///
|
///
|
||||||
/// Audio marker intended to live in pairs of two, taking styles depending
|
/// Audio marker intended to live in pairs of two, taking styles depending
|
||||||
/// on which marker in the pair is to the left and which is to the right.
|
/// on which marker in the pair is to the left and which is to the right.
|
||||||
class AudioMarkerDialogueTiming : public AudioMarker {
|
class DialogueTimingMarker : public AudioMarker {
|
||||||
/// The other marker for the dialogue line's pair
|
|
||||||
AudioMarkerDialogueTiming *other;
|
|
||||||
|
|
||||||
/// Current ms position of this marker
|
/// Current ms position of this marker
|
||||||
int position;
|
int position;
|
||||||
|
|
||||||
/// Draw style for the marker
|
/// Draw style for the marker
|
||||||
wxPen style;
|
const Pen *style;
|
||||||
/// Foot style for the marker
|
|
||||||
|
/// Feet style for the marker
|
||||||
FeetStyle feet;
|
FeetStyle feet;
|
||||||
|
|
||||||
/// Draw style for the left marker
|
/// Rendering style of the owning line, needed for sorting
|
||||||
Pen style_left;
|
AudioRenderingStyle type;
|
||||||
/// Draw style for the right marker
|
|
||||||
Pen style_right;
|
|
||||||
|
|
||||||
|
/// The line which owns this marker
|
||||||
public:
|
TimeableLine *line;
|
||||||
// AudioMarker interface
|
|
||||||
int GetPosition() const { return position; }
|
|
||||||
wxPen GetStyle() const { return style; }
|
|
||||||
FeetStyle GetFeet() const { return feet; }
|
|
||||||
bool CanSnap() const { return false; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Specific interface
|
|
||||||
|
|
||||||
/// @brief Move the marker to a new position
|
|
||||||
/// @param new_position The position to move the marker to, in milliseconds
|
|
||||||
///
|
|
||||||
/// If the marker moves to the opposite side of the other marker in the pair,
|
|
||||||
/// the styles of the two markers will be changed to match the new start/end
|
|
||||||
/// relationship of them.
|
|
||||||
void SetPosition(int new_position);
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
///
|
|
||||||
/// Initialises the fields to default values.
|
|
||||||
AudioMarkerDialogueTiming();
|
|
||||||
|
|
||||||
|
|
||||||
/// @brief Initialise a pair of dialogue markers to be a pair
|
|
||||||
/// @param marker1 The first marker in the pair to make
|
|
||||||
/// @param marker2 The second marker in the pair to make
|
|
||||||
///
|
|
||||||
/// This checks that the markers aren't already part of a pair, and then
|
|
||||||
/// sets their "other" field. Positions and styles aren't affected.
|
|
||||||
static void InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2);
|
|
||||||
|
|
||||||
/// Implicit decay to the position of the marker
|
|
||||||
operator int() const { return position; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @class InactiveLineMarker
|
|
||||||
/// @brief Markers for the beginning and ends of inactive lines
|
|
||||||
class InactiveLineMarker : public AudioMarker {
|
|
||||||
int position;
|
|
||||||
Pen style;
|
|
||||||
FeetStyle feet;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int GetPosition() const { return position; }
|
int GetPosition() const { return position; }
|
||||||
wxPen GetStyle() const { return style; }
|
wxPen GetStyle() const { return *style; }
|
||||||
FeetStyle GetFeet() const { return feet; }
|
FeetStyle GetFeet() const { return feet; }
|
||||||
bool CanSnap() const { return true; }
|
bool CanSnap() const { return true; }
|
||||||
|
|
||||||
InactiveLineMarker(int position, bool start)
|
/// Move the marker to a new position
|
||||||
|
/// @param new_position The position to move the marker to, in milliseconds
|
||||||
|
///
|
||||||
|
/// This notifies the owning line of the change, so that it can ensure that
|
||||||
|
/// this marker has the appropriate rendering style.
|
||||||
|
void SetPosition(int new_position);
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
/// @param position Initial position of this marker
|
||||||
|
/// @param style Rendering style of this marker
|
||||||
|
/// @param feet Foot style of this marker
|
||||||
|
/// @param type Type of this marker, used only for sorting
|
||||||
|
/// @param line Line which this is a marker for
|
||||||
|
DialogueTimingMarker(int position, const Pen *style, FeetStyle feet, AudioRenderingStyle type, TimeableLine *line)
|
||||||
: position(position)
|
: position(position)
|
||||||
, style("Colour/Audio Display/Line Boundary Inactive Line", "Audio/Line Boundaries Thickness")
|
, style(style)
|
||||||
, feet(start ? Feet_Right : Feet_Left)
|
, feet(feet)
|
||||||
|
, type(type)
|
||||||
|
, line(line)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DialogueTimingMarker(DialogueTimingMarker const& other, TimeableLine *line)
|
||||||
|
: position(other.position)
|
||||||
|
, style(other.style)
|
||||||
|
, feet(other.feet)
|
||||||
|
, type(other.type)
|
||||||
|
, line(line)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the line which this is a marker for
|
||||||
|
TimeableLine *GetLine() const { return line; }
|
||||||
|
|
||||||
/// Implicit decay to the position of the marker
|
/// Implicit decay to the position of the marker
|
||||||
operator int() const { return position; }
|
operator int() const { return position; }
|
||||||
|
|
||||||
|
/// Comparison operator
|
||||||
|
///
|
||||||
|
/// Compares first on position, then on audio rendering style so that the
|
||||||
|
/// markers for the active line end up after those for the inactive lines.
|
||||||
|
bool operator<(DialogueTimingMarker const& other) const
|
||||||
|
{
|
||||||
|
if (position < other.position) return true;
|
||||||
|
if (position > other.position) return false;
|
||||||
|
return type < other.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Swap the rendering style of this marker with that of the passed marker
|
||||||
|
void SwapStyles(DialogueTimingMarker &other)
|
||||||
|
{
|
||||||
|
std::swap(style, other.style);
|
||||||
|
std::swap(feet, other.feet);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A comparison predicate for pointers to dialogue markers and millisecond positions
|
||||||
|
struct marker_ptr_cmp
|
||||||
|
{
|
||||||
|
bool operator()(const DialogueTimingMarker *lft, const DialogueTimingMarker *rgt) const
|
||||||
|
{
|
||||||
|
return *lft < *rgt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const DialogueTimingMarker *lft, int rgt) const
|
||||||
|
{
|
||||||
|
return *lft < rgt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(int lft, const DialogueTimingMarker *rgt) const
|
||||||
|
{
|
||||||
|
return lft < *rgt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @class TimeableLine
|
||||||
|
/// @brief A single dialogue line which can be timed via AudioTimingControllerDialogue
|
||||||
|
///
|
||||||
|
/// This class provides markers and styling ranges for a single dialogue line,
|
||||||
|
/// both active and inactive. In addition, it can apply changes made via those
|
||||||
|
/// markers to the tracked dialogue line.
|
||||||
|
class TimeableLine {
|
||||||
|
/// The current tracked dialogue line
|
||||||
|
AssDialogue *line;
|
||||||
|
/// The rendering style of this line
|
||||||
|
AudioRenderingStyle style;
|
||||||
|
|
||||||
|
/// One of the markers. Initially the left marker, but the user may change this.
|
||||||
|
DialogueTimingMarker marker1;
|
||||||
|
/// One of the markers. Initially the right marker, but the user may change this.
|
||||||
|
DialogueTimingMarker marker2;
|
||||||
|
|
||||||
|
/// Pointer to whichever marker happens to be on the left
|
||||||
|
DialogueTimingMarker *left_marker;
|
||||||
|
/// Pointer to whichever marker happens to be on the right
|
||||||
|
DialogueTimingMarker *right_marker;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
/// @param style Rendering style to use for this line's time range
|
||||||
|
/// @param style_left The rendering style for the start marker
|
||||||
|
/// @param style_right The rendering style for the end marker
|
||||||
|
TimeableLine(AudioRenderingStyle style, const Pen *style_left, const Pen *style_right)
|
||||||
|
: line(0)
|
||||||
|
, style(style)
|
||||||
|
, marker1(0, style_left, AudioMarker::Feet_Right, style, this)
|
||||||
|
, marker2(0, style_right, AudioMarker::Feet_Left, style, this)
|
||||||
|
, left_marker(&marker1)
|
||||||
|
, right_marker(&marker2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Explicit copy constructor needed due to that the markers have a pointer to this
|
||||||
|
TimeableLine(TimeableLine const& other)
|
||||||
|
: line(other.line)
|
||||||
|
, style(other.style)
|
||||||
|
, marker1(*other.left_marker, this)
|
||||||
|
, marker2(*other.right_marker, this)
|
||||||
|
, left_marker(&marker1)
|
||||||
|
, right_marker(&marker2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the tracked dialogue line
|
||||||
|
AssDialogue *GetLine() const { return line; }
|
||||||
|
|
||||||
|
/// Get the time range for this line
|
||||||
|
operator TimeRange() const { return TimeRange(*left_marker, *right_marker); }
|
||||||
|
|
||||||
|
/// Add this line's style to the style ranges
|
||||||
|
void GetStyleRange(AudioRenderingStyleRanges *ranges) const
|
||||||
|
{
|
||||||
|
ranges->AddRange(*left_marker, *right_marker, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get this line's markers
|
||||||
|
/// @param c Vector to add the markers to
|
||||||
|
void GetMarkers(std::vector<DialogueTimingMarker*> *c) const
|
||||||
|
{
|
||||||
|
c->push_back(left_marker);
|
||||||
|
c->push_back(right_marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the leftmost of the markers
|
||||||
|
DialogueTimingMarker *GetLeftMarker() { return left_marker; }
|
||||||
|
const DialogueTimingMarker *GetLeftMarker() const { return left_marker; }
|
||||||
|
|
||||||
|
/// Get the rightmost of the markers
|
||||||
|
DialogueTimingMarker *GetRightMarker() { return right_marker; }
|
||||||
|
const DialogueTimingMarker *GetRightMarker() const { return right_marker; }
|
||||||
|
|
||||||
|
/// Does this line have a marker in the given range?
|
||||||
|
bool ContainsMarker(TimeRange const& range) const
|
||||||
|
{
|
||||||
|
return range.contains(marker1) || range.contains(marker2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the markers have the correct styles, and correct them if needed
|
||||||
|
void CheckMarkers()
|
||||||
|
{
|
||||||
|
if (*right_marker < *left_marker)
|
||||||
|
{
|
||||||
|
marker1.SwapStyles(marker2);
|
||||||
|
std::swap(left_marker, right_marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply any changes made here to the tracked dialogue line
|
||||||
|
void Apply()
|
||||||
|
{
|
||||||
|
if (line)
|
||||||
|
{
|
||||||
|
line->Start = left_marker->GetPosition();
|
||||||
|
line->End = right_marker->GetPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the dialogue line which this is tracking and reset the markers to
|
||||||
|
/// the line's time range
|
||||||
|
void SetLine(AssDialogue *new_line)
|
||||||
|
{
|
||||||
|
line = new_line;
|
||||||
|
marker1.SetPosition(new_line->Start);
|
||||||
|
marker2.SetPosition(new_line->End);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void DialogueTimingMarker::SetPosition(int new_position) {
|
||||||
|
position = new_position;
|
||||||
|
line->CheckMarkers();
|
||||||
|
}
|
||||||
|
|
||||||
/// @class AudioTimingControllerDialogue
|
/// @class AudioTimingControllerDialogue
|
||||||
/// @brief Default timing mode for dialogue subtitles
|
/// @brief Default timing mode for dialogue subtitles
|
||||||
///
|
///
|
||||||
/// Displays a start and end marker for an active subtitle line, and allows
|
/// Displays a start and end marker for an active subtitle line, and possibly
|
||||||
/// for those markers to be dragged. Dragging the start/end markers changes
|
/// some of the inactive lines. The markers for the active line can be dragged,
|
||||||
/// the audio selection.
|
/// updating the audio selection and the start/end time of that line. In
|
||||||
///
|
/// addition, any markers for inactive lines that start/end at the same time
|
||||||
/// Another later expansion will be to affect the timing of multiple selected
|
/// as the active line starts/ends can optionally be dragged along with the
|
||||||
/// lines at the same time, if they e.g. have end1==start2.
|
/// active line's markers, updating those lines as well.
|
||||||
class AudioTimingControllerDialogue : public AudioTimingController, private SelectionListener<AssDialogue> {
|
class AudioTimingControllerDialogue : public AudioTimingController, private SelectionListener<AssDialogue> {
|
||||||
/// Start and end markers for the active line
|
/// The rendering style for the active line's start marker
|
||||||
AudioMarkerDialogueTiming active_markers[2];
|
Pen style_left;
|
||||||
|
/// The rendering style for the active line's end marker
|
||||||
|
Pen style_right;
|
||||||
|
/// The rendering style for the start and end markers of inactive lines
|
||||||
|
Pen style_inactive;
|
||||||
|
|
||||||
/// Markers for inactive lines
|
/// The currently active line
|
||||||
std::vector<InactiveLineMarker> inactive_markers;
|
TimeableLine active_line;
|
||||||
|
|
||||||
/// Time ranges with inactive lines
|
/// Inactive lines which are currently modifiable
|
||||||
std::vector<std::pair<int, int> > inactive_ranges;
|
std::list<TimeableLine> inactive_lines;
|
||||||
|
|
||||||
|
/// All audio markers for active and inactive lines, sorted by position
|
||||||
|
std::vector<DialogueTimingMarker*> markers;
|
||||||
|
|
||||||
/// Marker provider for video keyframes
|
/// Marker provider for video keyframes
|
||||||
AudioMarkerProviderKeyframes keyframes_provider;
|
AudioMarkerProviderKeyframes keyframes_provider;
|
||||||
|
@ -160,10 +303,9 @@ class AudioTimingControllerDialogue : public AudioTimingController, private Sele
|
||||||
/// Marker provider for video playback position
|
/// Marker provider for video playback position
|
||||||
VideoPositionMarkerProvider video_position_provider;
|
VideoPositionMarkerProvider video_position_provider;
|
||||||
|
|
||||||
/// Has the timing been modified by the user?
|
/// The set of lines which have been modified and need to have their
|
||||||
/// If auto commit is enabled this will only be true very briefly following
|
/// changes applied on commit
|
||||||
/// changes
|
std::set<TimeableLine*> modified_lines;
|
||||||
bool timing_modified;
|
|
||||||
|
|
||||||
/// Commit id for coalescing purposes when in auto commit mode
|
/// Commit id for coalescing purposes when in auto commit mode
|
||||||
int commit_id;
|
int commit_id;
|
||||||
|
@ -181,33 +323,32 @@ class AudioTimingControllerDialogue : public AudioTimingController, private Sele
|
||||||
agi::signal::Connection inactive_line_mode_connection;
|
agi::signal::Connection inactive_line_mode_connection;
|
||||||
agi::signal::Connection inactive_line_comment_connection;
|
agi::signal::Connection inactive_line_comment_connection;
|
||||||
|
|
||||||
/// Get the leftmost of the markers
|
|
||||||
AudioMarkerDialogueTiming *GetLeftMarker();
|
|
||||||
const AudioMarkerDialogueTiming *GetLeftMarker() const;
|
|
||||||
|
|
||||||
/// Get the rightmost of the markers
|
|
||||||
AudioMarkerDialogueTiming *GetRightMarker();
|
|
||||||
const AudioMarkerDialogueTiming *GetRightMarker() const;
|
|
||||||
|
|
||||||
/// Update the audio controller's selection
|
/// Update the audio controller's selection
|
||||||
void UpdateSelection();
|
void UpdateSelection();
|
||||||
|
|
||||||
/// Regenerate markers for inactive lines
|
/// Regenerate the list of timeable inactive lines
|
||||||
void RegenerateInactiveLines();
|
void RegenerateInactiveLines();
|
||||||
|
|
||||||
/// Add the inactive line markers for a single line
|
/// Add a line to the list of timeable inactive lines
|
||||||
/// @param line Line to add markers for. May be NULL.
|
void AddInactiveLine(AssDialogue *diag);
|
||||||
void AddInactiveMarkers(AssDialogue *line);
|
|
||||||
|
|
||||||
/// @brief Set the position of a marker and announce the change to the world
|
/// Regenerate the list of active and inactive line markers
|
||||||
/// @param marker Marker to move
|
void RegenerateMarkers();
|
||||||
/// @param ms New position of the marker
|
|
||||||
void SetMarker(AudioMarkerDialogueTiming *marker, int ms);
|
/// @brief Set the position of markers and announce the change to the world
|
||||||
|
/// @param upd_markers Markers to move
|
||||||
|
/// @param ms New position of the markers
|
||||||
|
void SetMarkers(std::vector<AudioMarker*> const& upd_markers, int ms);
|
||||||
|
|
||||||
/// Snap a position to a nearby marker, if any
|
/// Snap a position to a nearby marker, if any
|
||||||
/// @param position Position to snap
|
/// @param position Position to snap
|
||||||
/// @param snap_range Maximum distance to snap in milliseconds
|
/// @param snap_range Maximum distance to snap in milliseconds
|
||||||
int SnapPosition(int position, int snap_range) const;
|
/// @param exclude Markers which should be excluded from the potential snaps
|
||||||
|
int SnapPosition(int position, int snap_range, std::vector<AudioMarker*> const& exclude) const;
|
||||||
|
|
||||||
|
/// Commit all pending changes to the file
|
||||||
|
/// @param user_triggered Is this a user-initiated commit or an autocommit
|
||||||
|
void DoCommit(bool user_triggered);
|
||||||
|
|
||||||
// SubtitleSelectionListener interface
|
// SubtitleSelectionListener interface
|
||||||
void OnActiveLineChanged(AssDialogue *new_line);
|
void OnActiveLineChanged(AssDialogue *new_line);
|
||||||
|
@ -235,11 +376,10 @@ public:
|
||||||
std::vector<AudioMarker*> OnRightClick(int ms, bool, int sensitivity, int snap_range);
|
std::vector<AudioMarker*> OnRightClick(int ms, bool, int sensitivity, int snap_range);
|
||||||
void OnMarkerDrag(std::vector<AudioMarker*> const& markers, int new_position, int snap_range);
|
void OnMarkerDrag(std::vector<AudioMarker*> const& markers, int new_position, int snap_range);
|
||||||
|
|
||||||
public:
|
/// Constructor
|
||||||
// Specific interface
|
/// @param c Project context
|
||||||
|
|
||||||
/// @brief Constructor
|
|
||||||
AudioTimingControllerDialogue(agi::Context *c);
|
AudioTimingControllerDialogue(agi::Context *c);
|
||||||
|
/// Destructor
|
||||||
~AudioTimingControllerDialogue();
|
~AudioTimingControllerDialogue();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -248,56 +388,13 @@ AudioTimingController *CreateDialogueTimingController(agi::Context *c)
|
||||||
return new AudioTimingControllerDialogue(c);
|
return new AudioTimingControllerDialogue(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// AudioMarkerDialogueTiming
|
|
||||||
void AudioMarkerDialogueTiming::SetPosition(int new_position)
|
|
||||||
{
|
|
||||||
position = new_position;
|
|
||||||
|
|
||||||
if (other)
|
|
||||||
{
|
|
||||||
if (position < other->position)
|
|
||||||
{
|
|
||||||
feet = Feet_Right;
|
|
||||||
other->feet = Feet_Left;
|
|
||||||
style = style_left;
|
|
||||||
other->style = style_right;
|
|
||||||
}
|
|
||||||
else if (position > other->position)
|
|
||||||
{
|
|
||||||
feet = Feet_Left;
|
|
||||||
other->feet = Feet_Right;
|
|
||||||
style = style_right;
|
|
||||||
other->style = style_left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioMarkerDialogueTiming::AudioMarkerDialogueTiming()
|
|
||||||
: other(0)
|
|
||||||
, position(0)
|
|
||||||
, style(*wxTRANSPARENT_PEN)
|
|
||||||
, feet(Feet_None)
|
|
||||||
, style_left("Colour/Audio Display/Line boundary Start", "Audio/Line Boundaries Thickness")
|
|
||||||
, style_right("Colour/Audio Display/Line boundary End", "Audio/Line Boundaries Thickness")
|
|
||||||
{
|
|
||||||
// Nothing more to do
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioMarkerDialogueTiming::InitPair(AudioMarkerDialogueTiming *marker1, AudioMarkerDialogueTiming *marker2)
|
|
||||||
{
|
|
||||||
assert(marker1->other == 0);
|
|
||||||
assert(marker2->other == 0);
|
|
||||||
|
|
||||||
marker1->other = marker2;
|
|
||||||
marker2->other = marker1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// AudioTimingControllerDialogue
|
|
||||||
|
|
||||||
AudioTimingControllerDialogue::AudioTimingControllerDialogue(agi::Context *c)
|
AudioTimingControllerDialogue::AudioTimingControllerDialogue(agi::Context *c)
|
||||||
: keyframes_provider(c, "Audio/Display/Draw/Keyframes in Dialogue Mode")
|
: style_left("Colour/Audio Display/Line boundary Start", "Audio/Line Boundaries Thickness")
|
||||||
|
, style_right("Colour/Audio Display/Line boundary End", "Audio/Line Boundaries Thickness")
|
||||||
|
, style_inactive("Colour/Audio Display/Line Boundary Inactive Line", "Audio/Line Boundaries Thickness")
|
||||||
|
, active_line(AudioStyle_Selected, &style_left, &style_right)
|
||||||
|
, keyframes_provider(c, "Audio/Display/Draw/Keyframes in Dialogue Mode")
|
||||||
, video_position_provider(c)
|
, video_position_provider(c)
|
||||||
, timing_modified(false)
|
|
||||||
, commit_id(-1)
|
, commit_id(-1)
|
||||||
, context(c)
|
, context(c)
|
||||||
, auto_commit(OPT_GET("Audio/Auto/Commit"))
|
, auto_commit(OPT_GET("Audio/Auto/Commit"))
|
||||||
|
@ -307,8 +404,6 @@ AudioTimingControllerDialogue::AudioTimingControllerDialogue(agi::Context *c)
|
||||||
, inactive_line_mode_connection(OPT_SUB("Audio/Inactive Lines Display Mode", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
|
, inactive_line_mode_connection(OPT_SUB("Audio/Inactive Lines Display Mode", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
|
||||||
, inactive_line_comment_connection(OPT_SUB("Audio/Display/Draw/Inactive Comments", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
|
, inactive_line_comment_connection(OPT_SUB("Audio/Display/Draw/Inactive Comments", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
|
||||||
{
|
{
|
||||||
AudioMarkerDialogueTiming::InitPair(&active_markers[0], &active_markers[1]);
|
|
||||||
|
|
||||||
c->selectionController->AddSelectionListener(this);
|
c->selectionController->AddSelectionListener(this);
|
||||||
keyframes_provider.AddMarkerMovedListener(std::tr1::bind(std::tr1::ref(AnnounceMarkerMoved)));
|
keyframes_provider.AddMarkerMovedListener(std::tr1::bind(std::tr1::ref(AnnounceMarkerMoved)));
|
||||||
video_position_provider.AddMarkerMovedListener(std::tr1::bind(std::tr1::ref(AnnounceMarkerMoved)));
|
video_position_provider.AddMarkerMovedListener(std::tr1::bind(std::tr1::ref(AnnounceMarkerMoved)));
|
||||||
|
@ -322,43 +417,16 @@ AudioTimingControllerDialogue::~AudioTimingControllerDialogue()
|
||||||
context->selectionController->RemoveSelectionListener(this);
|
context->selectionController->RemoveSelectionListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetLeftMarker()
|
|
||||||
{
|
|
||||||
return active_markers[0] < active_markers[1] ? &active_markers[0] : &active_markers[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetLeftMarker() const
|
|
||||||
{
|
|
||||||
return &std::min(active_markers[0], active_markers[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker()
|
|
||||||
{
|
|
||||||
return active_markers[0] < active_markers[1] ? &active_markers[1] : &active_markers[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const AudioMarkerDialogueTiming *AudioTimingControllerDialogue::GetRightMarker() const
|
|
||||||
{
|
|
||||||
return &std::max(active_markers[0], active_markers[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const
|
void AudioTimingControllerDialogue::GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const
|
||||||
{
|
{
|
||||||
// The order matters here; later markers are painted on top of earlier
|
// The order matters here; later markers are painted on top of earlier
|
||||||
// markers, so the markers that we want to end up on top need to appear last
|
// markers, so the markers that we want to end up on top need to appear last
|
||||||
|
|
||||||
// Copy inactive line markers in the range
|
// Copy inactive line markers in the range
|
||||||
std::vector<InactiveLineMarker>::const_iterator
|
copy(
|
||||||
a = lower_bound(inactive_markers.begin(), inactive_markers.end(), range.begin()),
|
lower_bound(markers.begin(), markers.end(), range.begin(), marker_ptr_cmp()),
|
||||||
b = upper_bound(inactive_markers.begin(), inactive_markers.end(), range.end());
|
upper_bound(markers.begin(), markers.end(), range.end(), marker_ptr_cmp()),
|
||||||
|
back_inserter(out_markers));
|
||||||
for (; a != b; ++a)
|
|
||||||
out_markers.push_back(&*a);
|
|
||||||
|
|
||||||
if (range.contains(active_markers[0]))
|
|
||||||
out_markers.push_back(&active_markers[0]);
|
|
||||||
if (range.contains(active_markers[1]))
|
|
||||||
out_markers.push_back(&active_markers[1]);
|
|
||||||
|
|
||||||
keyframes_provider.GetMarkers(range, out_markers);
|
keyframes_provider.GetMarkers(range, out_markers);
|
||||||
video_position_provider.GetMarkers(range, out_markers);
|
video_position_provider.GetMarkers(range, out_markers);
|
||||||
|
@ -376,14 +444,10 @@ void AudioTimingControllerDialogue::OnSelectedSetChanged(const Selection &lines_
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::OnFileChanged(int type) {
|
void AudioTimingControllerDialogue::OnFileChanged(int type) {
|
||||||
if (type & AssFile::COMMIT_DIAG_TIME)
|
if (type & AssFile::COMMIT_DIAG_TIME)
|
||||||
{
|
|
||||||
Revert();
|
Revert();
|
||||||
}
|
|
||||||
else if (type & AssFile::COMMIT_DIAG_ADDREM)
|
else if (type & AssFile::COMMIT_DIAG_ADDREM)
|
||||||
{
|
|
||||||
RegenerateInactiveLines();
|
RegenerateInactiveLines();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
wxString AudioTimingControllerDialogue::GetWarningMessage() const
|
wxString AudioTimingControllerDialogue::GetWarningMessage() const
|
||||||
{
|
{
|
||||||
|
@ -398,16 +462,14 @@ TimeRange AudioTimingControllerDialogue::GetIdealVisibleTimeRange() const
|
||||||
|
|
||||||
TimeRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
|
TimeRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
|
||||||
{
|
{
|
||||||
return TimeRange(*GetLeftMarker(), *GetRightMarker());
|
return active_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const
|
void AudioTimingControllerDialogue::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const
|
||||||
{
|
{
|
||||||
ranges.AddRange(*GetLeftMarker(), *GetRightMarker(), AudioStyle_Selected);
|
active_line.GetStyleRange(&ranges);
|
||||||
for (size_t i = 0; i < inactive_ranges.size(); ++i)
|
for_each(inactive_lines.begin(), inactive_lines.end(),
|
||||||
{
|
bind(&TimeableLine::GetStyleRange, std::tr1::placeholders::_1, &ranges));
|
||||||
ranges.AddRange(inactive_ranges[i].first, inactive_ranges[i].second, AudioStyle_Inactive);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::Next()
|
void AudioTimingControllerDialogue::Next()
|
||||||
|
@ -422,25 +484,17 @@ void AudioTimingControllerDialogue::Prev()
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::Commit()
|
void AudioTimingControllerDialogue::Commit()
|
||||||
{
|
{
|
||||||
int new_start_ms = *GetLeftMarker();
|
DoCommit(true);
|
||||||
int new_end_ms = *GetRightMarker();
|
|
||||||
|
|
||||||
// If auto committing is enabled, timing_modified will be true iif it is an
|
|
||||||
// auto commit, as there is never pending changes to commit when the button
|
|
||||||
// is clicked
|
|
||||||
bool user_triggered = !(timing_modified && auto_commit->GetBool());
|
|
||||||
|
|
||||||
// Store back new times
|
|
||||||
if (timing_modified)
|
|
||||||
{
|
|
||||||
Selection sel;
|
|
||||||
context->selectionController->GetSelectedSet(sel);
|
|
||||||
for (Selection::iterator sub = sel.begin(); sub != sel.end(); ++sub)
|
|
||||||
{
|
|
||||||
(*sub)->Start = new_start_ms;
|
|
||||||
(*sub)->End = new_end_ms;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioTimingControllerDialogue::DoCommit(bool user_triggered)
|
||||||
|
{
|
||||||
|
// Store back new times
|
||||||
|
if (modified_lines.size())
|
||||||
|
{
|
||||||
|
for_each(modified_lines.begin(), modified_lines.end(),
|
||||||
|
std::tr1::mem_fn(&TimeableLine::Apply));
|
||||||
|
|
||||||
commit_connection.Block();
|
commit_connection.Block();
|
||||||
if (user_triggered)
|
if (user_triggered)
|
||||||
{
|
{
|
||||||
|
@ -448,23 +502,32 @@ void AudioTimingControllerDialogue::Commit()
|
||||||
commit_id = -1; // never coalesce with a manually triggered commit
|
commit_id = -1; // never coalesce with a manually triggered commit
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
commit_id = context->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME, commit_id, context->selectionController->GetActiveLine());
|
{
|
||||||
|
AssDialogue *amend = modified_lines.size() == 1 ? (*modified_lines.begin())->GetLine() : 0;
|
||||||
|
commit_id = context->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME, commit_id, amend);
|
||||||
|
}
|
||||||
|
|
||||||
commit_connection.Unblock();
|
commit_connection.Unblock();
|
||||||
timing_modified = false;
|
modified_lines.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_triggered && OPT_GET("Audio/Next Line on Commit")->GetBool())
|
if (user_triggered && OPT_GET("Audio/Next Line on Commit")->GetBool())
|
||||||
{
|
{
|
||||||
|
int new_end_ms = *active_line.GetRightMarker();
|
||||||
|
|
||||||
/// @todo Old audio display created a new line if there was no next,
|
/// @todo Old audio display created a new line if there was no next,
|
||||||
/// like the edit box, so maybe add a way to do that which both
|
/// like the edit box, so maybe add a way to do that which both
|
||||||
/// this and the edit box can use
|
/// this and the edit box can use
|
||||||
Next();
|
Next();
|
||||||
if (context->selectionController->GetActiveLine()->End == 0) {
|
|
||||||
|
if (*active_line.GetRightMarker() == 0) {
|
||||||
const int default_duration = OPT_GET("Timing/Default Duration")->GetInt();
|
const int default_duration = OPT_GET("Timing/Default Duration")->GetInt();
|
||||||
active_markers[0].SetPosition(new_end_ms);
|
// Setting right first here so that they don't get switched and the
|
||||||
active_markers[1].SetPosition(new_end_ms + default_duration);
|
// same marker gets set twice
|
||||||
timing_modified = true;
|
active_line.GetRightMarker()->SetPosition(new_end_ms + default_duration);
|
||||||
|
active_line.GetLeftMarker()->SetPosition(new_end_ms);
|
||||||
|
sort(markers.begin(), markers.end(), marker_ptr_cmp());
|
||||||
|
modified_lines.insert(&active_line);
|
||||||
UpdateSelection();
|
UpdateSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,74 +537,78 @@ void AudioTimingControllerDialogue::Revert()
|
||||||
{
|
{
|
||||||
if (AssDialogue *line = context->selectionController->GetActiveLine())
|
if (AssDialogue *line = context->selectionController->GetActiveLine())
|
||||||
{
|
{
|
||||||
if (line->Start != 0 || line->End != 0)
|
active_line.SetLine(line);
|
||||||
{
|
modified_lines.clear();
|
||||||
active_markers[0].SetPosition(line->Start);
|
|
||||||
active_markers[1].SetPosition(line->End);
|
|
||||||
timing_modified = false;
|
|
||||||
AnnounceUpdatedPrimaryRange();
|
AnnounceUpdatedPrimaryRange();
|
||||||
if (inactive_line_mode->GetInt() == 0)
|
if (inactive_line_mode->GetInt() == 0)
|
||||||
AnnounceUpdatedStyleRanges();
|
AnnounceUpdatedStyleRanges();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
RegenerateInactiveLines();
|
RegenerateInactiveLines();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioTimingControllerDialogue::IsNearbyMarker(int ms, int sensitivity) const
|
bool AudioTimingControllerDialogue::IsNearbyMarker(int ms, int sensitivity) const
|
||||||
{
|
{
|
||||||
TimeRange range(ms-sensitivity, ms+sensitivity);
|
assert(sensitivity >= 0);
|
||||||
|
return active_line.ContainsMarker(TimeRange(ms-sensitivity, ms+sensitivity));
|
||||||
return range.contains(active_markers[0]) || range.contains(active_markers[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<AudioMarker*> AudioTimingControllerDialogue::OnLeftClick(int ms, bool ctrl_down, int sensitivity, int snap_range)
|
std::vector<AudioMarker*> AudioTimingControllerDialogue::OnLeftClick(int ms, bool ctrl_down, int sensitivity, int snap_range)
|
||||||
{
|
{
|
||||||
assert(sensitivity >= 0);
|
assert(sensitivity >= 0);
|
||||||
|
assert(snap_range >= 0);
|
||||||
|
|
||||||
int dist_l, dist_r;
|
DialogueTimingMarker *left = active_line.GetLeftMarker();
|
||||||
|
DialogueTimingMarker *right = active_line.GetRightMarker();
|
||||||
|
|
||||||
AudioMarkerDialogueTiming *left = GetLeftMarker();
|
int dist_l = tabs(*left - ms);
|
||||||
AudioMarkerDialogueTiming *right = GetRightMarker();
|
int dist_r = tabs(*right - ms);
|
||||||
|
|
||||||
dist_l = tabs(*left - ms);
|
if (dist_l > sensitivity && dist_r > sensitivity)
|
||||||
dist_r = tabs(*right - ms);
|
|
||||||
|
|
||||||
if (dist_l < dist_r && dist_l <= sensitivity)
|
|
||||||
{
|
{
|
||||||
// Clicked near the left marker:
|
|
||||||
// Insta-move it and start dragging it
|
|
||||||
SetMarker(left, SnapPosition(ms, snap_range));
|
|
||||||
return std::vector<AudioMarker*>(1, left);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dist_r < dist_l && dist_r <= sensitivity)
|
|
||||||
{
|
|
||||||
// Clicked near the right marker:
|
|
||||||
// Only drag it. For insta-move, the user must right-click.
|
|
||||||
return std::vector<AudioMarker*>(1, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clicked far from either marker:
|
// Clicked far from either marker:
|
||||||
// Insta-set the left marker to the clicked position and return the right as the dragged one,
|
// Insta-set the left marker to the clicked position and return the
|
||||||
// such that if the user does start dragging, he will create a new selection from scratch
|
// right as the dragged one, such that if the user does start dragging,
|
||||||
SetMarker(left, SnapPosition(ms, snap_range));
|
// he will create a new selection from scratch
|
||||||
return std::vector<AudioMarker*>(1, right);
|
std::vector<AudioMarker*> ret(1, left);
|
||||||
|
SetMarkers(ret, SnapPosition(ms, snap_range, ret));
|
||||||
|
ret[0] = right;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogueTimingMarker *clicked = dist_l <= dist_r ? left : right;
|
||||||
|
std::vector<AudioMarker*> ret;
|
||||||
|
|
||||||
|
if (ctrl_down)
|
||||||
|
{
|
||||||
|
// The use of GetPosition here is important, as otherwise it'll start
|
||||||
|
// after lines ending at the same time as the active line begins
|
||||||
|
std::vector<DialogueTimingMarker*>::iterator it =
|
||||||
|
lower_bound(markers.begin(), markers.end(), clicked->GetPosition(), marker_ptr_cmp());
|
||||||
|
for(; it != markers.end() && !(*clicked < **it); ++it)
|
||||||
|
ret.push_back(*it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret.push_back(clicked);
|
||||||
|
|
||||||
|
// Left-click within drag range should still move the left marker to the
|
||||||
|
// clicked position, but not the right marker
|
||||||
|
if (clicked == left)
|
||||||
|
SetMarkers(ret, SnapPosition(ms, snap_range, ret));
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<AudioMarker*> AudioTimingControllerDialogue::OnRightClick(int ms, bool, int sensitivity, int snap_range)
|
std::vector<AudioMarker*> AudioTimingControllerDialogue::OnRightClick(int ms, bool, int sensitivity, int snap_range)
|
||||||
{
|
{
|
||||||
AudioMarkerDialogueTiming *right = GetRightMarker();
|
std::vector<AudioMarker*> ret(1, active_line.GetRightMarker());
|
||||||
SetMarker(right, SnapPosition(ms, snap_range));
|
SetMarkers(ret, SnapPosition(ms, snap_range, ret));
|
||||||
return std::vector<AudioMarker*>(1, right);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::OnMarkerDrag(std::vector<AudioMarker*> const& markers, int new_position, int snap_range)
|
void AudioTimingControllerDialogue::OnMarkerDrag(std::vector<AudioMarker*> const& markers, int new_position, int snap_range)
|
||||||
{
|
{
|
||||||
assert(markers.size() == 1);
|
SetMarkers(markers, SnapPosition(new_position, snap_range, markers));
|
||||||
AudioMarker *marker = markers[0];
|
|
||||||
assert(marker == &active_markers[0] || marker == &active_markers[1]);
|
|
||||||
|
|
||||||
SetMarker(static_cast<AudioMarkerDialogueTiming*>(marker), SnapPosition(new_position, snap_range));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::UpdateSelection()
|
void AudioTimingControllerDialogue::UpdateSelection()
|
||||||
|
@ -550,12 +617,38 @@ void AudioTimingControllerDialogue::UpdateSelection()
|
||||||
AnnounceUpdatedStyleRanges();
|
AnnounceUpdatedStyleRanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::SetMarker(AudioMarkerDialogueTiming *marker, int ms)
|
void AudioTimingControllerDialogue::SetMarkers(std::vector<AudioMarker*> const& upd_markers, int ms)
|
||||||
{
|
{
|
||||||
|
// Since we're moving markers, the sorted list of markers will need to be
|
||||||
|
// resorted. To avoid resorting the entire thing, find the subrange that
|
||||||
|
// is effected.
|
||||||
|
int min_ms = ms;
|
||||||
|
int max_ms = ms;
|
||||||
|
for (size_t i = 0; i < upd_markers.size(); ++i)
|
||||||
|
{
|
||||||
|
DialogueTimingMarker *marker = static_cast<DialogueTimingMarker*>(upd_markers[i]);
|
||||||
|
min_ms = std::min<int>(*marker, min_ms);
|
||||||
|
max_ms = std::max<int>(*marker, max_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DialogueTimingMarker*>::iterator
|
||||||
|
begin = lower_bound(markers.begin(), markers.end(), min_ms, marker_ptr_cmp()),
|
||||||
|
end = upper_bound(begin, markers.end(), max_ms, marker_ptr_cmp());
|
||||||
|
|
||||||
|
// Update the markers
|
||||||
|
for (size_t i = 0; i < upd_markers.size(); ++i)
|
||||||
|
{
|
||||||
|
DialogueTimingMarker *marker = static_cast<DialogueTimingMarker*>(upd_markers[i]);
|
||||||
marker->SetPosition(ms);
|
marker->SetPosition(ms);
|
||||||
timing_modified = true;
|
modified_lines.insert(marker->GetLine());
|
||||||
if (auto_commit->GetBool()) Commit();
|
}
|
||||||
|
|
||||||
|
// Resort the range
|
||||||
|
sort(begin, end, marker_ptr_cmp());
|
||||||
|
|
||||||
|
if (auto_commit->GetBool()) DoCommit(false);
|
||||||
UpdateSelection();
|
UpdateSelection();
|
||||||
|
|
||||||
AnnounceMarkerMoved();
|
AnnounceMarkerMoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,9 +668,8 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines()
|
||||||
{
|
{
|
||||||
bool (*predicate)(AssEntry*) = inactive_line_comments->GetBool() ? dialogue : noncomment_dialogue;
|
bool (*predicate)(AssEntry*) = inactive_line_comments->GetBool() ? dialogue : noncomment_dialogue;
|
||||||
|
|
||||||
bool was_empty = inactive_markers.empty();
|
bool was_empty = inactive_lines.empty();
|
||||||
inactive_markers.clear();
|
inactive_lines.clear();
|
||||||
inactive_ranges.clear();
|
|
||||||
|
|
||||||
switch (int mode = inactive_line_mode->GetInt())
|
switch (int mode = inactive_line_mode->GetInt())
|
||||||
{
|
{
|
||||||
|
@ -591,14 +683,14 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines()
|
||||||
std::list<AssEntry*>::iterator prev = current_line;
|
std::list<AssEntry*>::iterator prev = current_line;
|
||||||
while (--prev != context->ass->Line.begin() && !predicate(*prev)) ;
|
while (--prev != context->ass->Line.begin() && !predicate(*prev)) ;
|
||||||
if (prev != context->ass->Line.begin())
|
if (prev != context->ass->Line.begin())
|
||||||
AddInactiveMarkers(static_cast<AssDialogue*>(*prev));
|
AddInactiveLine(static_cast<AssDialogue*>(*prev));
|
||||||
|
|
||||||
if (mode == 2)
|
if (mode == 2)
|
||||||
{
|
{
|
||||||
std::list<AssEntry*>::iterator next =
|
std::list<AssEntry*>::iterator next =
|
||||||
find_if(++current_line, context->ass->Line.end(), predicate);
|
find_if(++current_line, context->ass->Line.end(), predicate);
|
||||||
if (next != context->ass->Line.end())
|
if (next != context->ass->Line.end())
|
||||||
AddInactiveMarkers(static_cast<AssDialogue*>(*next));
|
AddInactiveLine(static_cast<AssDialogue*>(*next));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -608,21 +700,42 @@ void AudioTimingControllerDialogue::RegenerateInactiveLines()
|
||||||
for (std::list<AssEntry*>::const_iterator it = context->ass->Line.begin(); it != context->ass->Line.end(); ++it)
|
for (std::list<AssEntry*>::const_iterator it = context->ass->Line.begin(); it != context->ass->Line.end(); ++it)
|
||||||
{
|
{
|
||||||
if (*it != active_line && predicate(*it))
|
if (*it != active_line && predicate(*it))
|
||||||
AddInactiveMarkers(static_cast<AssDialogue*>(*it));
|
AddInactiveLine(static_cast<AssDialogue*>(*it));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if (was_empty)
|
if (was_empty)
|
||||||
|
{
|
||||||
|
RegenerateMarkers();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sort(inactive_markers.begin(), inactive_markers.end());
|
|
||||||
AnnounceUpdatedStyleRanges();
|
AnnounceUpdatedStyleRanges();
|
||||||
|
|
||||||
|
RegenerateMarkers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTimingControllerDialogue::AddInactiveLine(AssDialogue *diag)
|
||||||
|
{
|
||||||
|
inactive_lines.push_back(TimeableLine(AudioStyle_Inactive, &style_inactive, &style_inactive));
|
||||||
|
inactive_lines.back().SetLine(diag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTimingControllerDialogue::RegenerateMarkers()
|
||||||
|
{
|
||||||
|
markers.clear();
|
||||||
|
|
||||||
|
active_line.GetMarkers(&markers);
|
||||||
|
for_each(inactive_lines.begin(), inactive_lines.end(),
|
||||||
|
bind(&TimeableLine::GetMarkers, std::tr1::placeholders::_1, &markers));
|
||||||
|
sort(markers.begin(), markers.end(), marker_ptr_cmp());
|
||||||
|
|
||||||
AnnounceMarkerMoved();
|
AnnounceMarkerMoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioTimingControllerDialogue::SnapPosition(int position, int snap_range) const
|
int AudioTimingControllerDialogue::SnapPosition(int position, int snap_range, std::vector<AudioMarker*> const& exclude) const
|
||||||
{
|
{
|
||||||
if (snap_range <= 0)
|
if (snap_range <= 0)
|
||||||
return position;
|
return position;
|
||||||
|
@ -633,7 +746,7 @@ int AudioTimingControllerDialogue::SnapPosition(int position, int snap_range) co
|
||||||
GetMarkers(snap_time_range, potential_snaps);
|
GetMarkers(snap_time_range, potential_snaps);
|
||||||
for (AudioMarkerVector::iterator mi = potential_snaps.begin(); mi != potential_snaps.end(); ++mi)
|
for (AudioMarkerVector::iterator mi = potential_snaps.begin(); mi != potential_snaps.end(); ++mi)
|
||||||
{
|
{
|
||||||
if ((*mi)->CanSnap())
|
if ((*mi)->CanSnap() && find(exclude.begin(), exclude.end(), *mi) == exclude.end())
|
||||||
{
|
{
|
||||||
if (!snap_marker)
|
if (!snap_marker)
|
||||||
snap_marker = *mi;
|
snap_marker = *mi;
|
||||||
|
@ -646,10 +759,3 @@ int AudioTimingControllerDialogue::SnapPosition(int position, int snap_range) co
|
||||||
return snap_marker->GetPosition();
|
return snap_marker->GetPosition();
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTimingControllerDialogue::AddInactiveMarkers(AssDialogue *line)
|
|
||||||
{
|
|
||||||
inactive_markers.push_back(InactiveLineMarker(line->Start, true));
|
|
||||||
inactive_markers.push_back(InactiveLineMarker(line->End, false));
|
|
||||||
inactive_ranges.push_back(std::pair<int, int>(line->Start, line->End));
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue