forked from mia/Aegisub
Add support for labels in the audio display
Originally committed to SVN as r5589.
This commit is contained in:
parent
3e708eab10
commit
3f05fe6b3e
5 changed files with 131 additions and 65 deletions
|
@ -325,6 +325,7 @@ void AudioController::SetTimingController(AudioTimingController *new_controller)
|
|||
if (timing_controller.get() != new_controller) {
|
||||
timing_controller.reset(new_controller);
|
||||
timing_controller->AddMarkerMovedListener(std::tr1::bind(std::tr1::ref(AnnounceMarkerMoved)));
|
||||
timing_controller->AddLabelChangedListener(std::tr1::bind(std::tr1::ref(AnnounceLabelChanged)));
|
||||
timing_controller->AddUpdatedPrimaryRangeListener(&AudioController::OnTimingControllerUpdatedPrimaryRange, this);
|
||||
timing_controller->AddUpdatedStyleRangesListener(&AudioController::OnTimingControllerUpdatedStyleRanges, this);
|
||||
}
|
||||
|
@ -436,6 +437,10 @@ void AudioController::GetMarkers(const SampleRange &range, AudioMarkerVector &ma
|
|||
if (timing_controller.get()) timing_controller->GetMarkers(range, markers);
|
||||
}
|
||||
|
||||
void AudioController::GetLabels(const SampleRange &range, std::vector<AudioLabel> &labels) const
|
||||
{
|
||||
if (timing_controller.get()) timing_controller->GetLabels(range, labels);
|
||||
}
|
||||
|
||||
double AudioController::GetVolume() const
|
||||
{
|
||||
|
|
|
@ -126,6 +126,32 @@ public:
|
|||
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
|
||||
};
|
||||
|
||||
/// @class AudioLabelProvider
|
||||
/// @brief Abstract interface for audio label providers
|
||||
class AudioLabelProvider {
|
||||
protected:
|
||||
/// One or more of the labels provided by this object have changed
|
||||
agi::signal::Signal<> AnnounceLabelChanged;
|
||||
public:
|
||||
/// A label for a range of samples on the audio display
|
||||
struct AudioLabel {
|
||||
/// Text of the label
|
||||
wxString text;
|
||||
/// Range which this label applies to
|
||||
SampleRange range;
|
||||
};
|
||||
|
||||
/// Virtual destructor, does nothing
|
||||
virtual ~AudioLabelProvider() { }
|
||||
|
||||
/// @brief Get labels in a sample range
|
||||
/// @param range Range of samples to get labels for
|
||||
/// @param[out] out Vector which should be filled with the labels
|
||||
virtual void GetLabels(SampleRange const& range, std::vector<AudioLabel> &out) const = 0;
|
||||
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceLabelChanged, AddLabelChangedListener)
|
||||
};
|
||||
|
||||
/// @class AudioController
|
||||
/// @brief Manage an open audio stream and UI state for it
|
||||
///
|
||||
|
@ -148,7 +174,7 @@ public:
|
|||
/// providers or players owned by a controller. If some operation that isn't
|
||||
/// possible in the existing design is needed, the controller should be
|
||||
/// extended in some way to allow it.
|
||||
class AudioController : public wxEvtHandler, public AudioMarkerProvider {
|
||||
class AudioController : public wxEvtHandler, public AudioMarkerProvider, public AudioLabelProvider {
|
||||
private:
|
||||
/// A new audio stream was opened (and any previously open was closed)
|
||||
agi::signal::Signal<AudioProvider*> AnnounceAudioOpen;
|
||||
|
@ -299,6 +325,11 @@ public:
|
|||
/// @param markers Vector to fill found markers into
|
||||
void GetMarkers(const SampleRange &range, AudioMarkerVector &markers) const;
|
||||
|
||||
/// @brief Get all labels inside a range
|
||||
/// @param range The sample range to retrieve labels for
|
||||
/// @param labels Vector to fill found labels into
|
||||
void GetLabels(const SampleRange &range, std::vector<AudioLabel> &labels) const;
|
||||
|
||||
|
||||
/// @brief Get the playback audio volume
|
||||
/// @return The amplification factor for the audio
|
||||
|
|
|
@ -790,9 +790,8 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
|||
int selection_start = AbsoluteXFromSamples(sel_samples.begin());
|
||||
int selection_end = AbsoluteXFromSamples(sel_samples.end());
|
||||
|
||||
wxRegionIterator region(GetUpdateRegion());
|
||||
wxPoint client_org = GetClientAreaOrigin();
|
||||
while (region)
|
||||
for (wxRegionIterator region(GetUpdateRegion()); region; ++region)
|
||||
{
|
||||
wxRect updrect = region.GetRect();
|
||||
// Work around wxMac issue, client border offsets update rectangles but does
|
||||
|
@ -804,68 +803,99 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
|||
redraw_scrollbar |= scrollbar_bounds.Intersects(updrect);
|
||||
redraw_timeline |= timeline_bounds.Intersects(updrect);
|
||||
|
||||
if (audio_bounds.Intersects(updrect))
|
||||
if (!audio_bounds.Intersects(updrect))
|
||||
{
|
||||
int p1, p2, p3, p4;
|
||||
// p1 -> p2 = before selection
|
||||
// p2 -> p3 = in selection
|
||||
// p3 -> p4 = after selection
|
||||
p1 = scroll_left + updrect.x;
|
||||
p2 = selection_start;
|
||||
p3 = selection_end;
|
||||
p4 = p1 + updrect.width;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<RedrawSubregion> subregions;
|
||||
// p1 -> p2 = before selection
|
||||
// p2 -> p3 = in selection
|
||||
// p3 -> p4 = after selection
|
||||
int p1 = scroll_left + updrect.x;
|
||||
int p2 = selection_start;
|
||||
int p3 = selection_end;
|
||||
int p4 = p1 + updrect.width;
|
||||
|
||||
if (p1 < p2)
|
||||
subregions.push_back(RedrawSubregion(p1, std::min(p2, p4), false));
|
||||
if (p4 > p2 && p1 < p3)
|
||||
subregions.push_back(RedrawSubregion(std::max(p1, p2), std::min(p3, p4), true));
|
||||
if (p4 > p3)
|
||||
subregions.push_back(RedrawSubregion(std::max(p1, p3), p4, false));
|
||||
std::vector<RedrawSubregion> subregions;
|
||||
|
||||
int x = updrect.x;
|
||||
for (std::vector<RedrawSubregion>::iterator sr = subregions.begin(); sr != subregions.end(); ++sr)
|
||||
if (p1 < p2)
|
||||
subregions.push_back(RedrawSubregion(p1, std::min(p2, p4), false));
|
||||
if (p4 > p2 && p1 < p3)
|
||||
subregions.push_back(RedrawSubregion(std::max(p1, p2), std::min(p3, p4), true));
|
||||
if (p4 > p3)
|
||||
subregions.push_back(RedrawSubregion(std::max(p1, p3), p4, false));
|
||||
|
||||
int x = updrect.x;
|
||||
for (std::vector<RedrawSubregion>::iterator sr = subregions.begin(); sr != subregions.end(); ++sr)
|
||||
{
|
||||
audio_renderer->Render(dc, wxPoint(x, audio_top), sr->x1, sr->x2 - sr->x1, sr->selected);
|
||||
x += sr->x2 - sr->x1;
|
||||
}
|
||||
|
||||
const int foot_size = 6;
|
||||
SampleRange updrectsamples(
|
||||
SamplesFromRelativeX(updrect.x - foot_size),
|
||||
SamplesFromRelativeX(updrect.x + updrect.width + foot_size));
|
||||
|
||||
// Draw markers on top of it all
|
||||
AudioMarkerVector markers;
|
||||
controller->GetMarkers(updrectsamples, markers);
|
||||
wxDCPenChanger pen_retainer(dc, wxPen());
|
||||
wxDCBrushChanger brush_retainer(dc, wxBrush());
|
||||
for (AudioMarkerVector::iterator marker_i = markers.begin(); marker_i != markers.end(); ++marker_i)
|
||||
{
|
||||
const AudioMarker *marker = *marker_i;
|
||||
dc.SetPen(marker->GetStyle());
|
||||
int marker_x = RelativeXFromSamples(marker->GetPosition());
|
||||
dc.DrawLine(marker_x, audio_top, marker_x, audio_top+audio_height);
|
||||
dc.SetBrush(wxBrush(marker->GetStyle().GetColour()));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
if (marker->GetFeet() & AudioMarker::Feet_Left)
|
||||
{
|
||||
audio_renderer->Render(dc, wxPoint(x, audio_top), sr->x1, sr->x2 - sr->x1, sr->selected);
|
||||
x += sr->x2 - sr->x1;
|
||||
wxPoint foot_top[3] = { wxPoint(-foot_size, 0), wxPoint(0, 0), wxPoint(0, foot_size) };
|
||||
wxPoint foot_bot[3] = { wxPoint(-foot_size, 0), wxPoint(0, -foot_size), wxPoint(0, 0) };
|
||||
dc.DrawPolygon(3, foot_top, marker_x, audio_top);
|
||||
dc.DrawPolygon(3, foot_bot, marker_x, audio_top+audio_height);
|
||||
}
|
||||
|
||||
// Draw markers on top of it all
|
||||
AudioMarkerVector markers;
|
||||
const int foot_size = 6;
|
||||
SampleRange updrectsamples(
|
||||
SamplesFromRelativeX(updrect.x - foot_size),
|
||||
SamplesFromRelativeX(updrect.x + updrect.width + foot_size));
|
||||
controller->GetMarkers(updrectsamples, markers);
|
||||
wxDCPenChanger pen_retainer(dc, wxPen());
|
||||
wxDCBrushChanger brush_retainer(dc, wxBrush());
|
||||
for (AudioMarkerVector::iterator marker_i = markers.begin(); marker_i != markers.end(); ++marker_i)
|
||||
if (marker->GetFeet() & AudioMarker::Feet_Right)
|
||||
{
|
||||
const AudioMarker *marker = *marker_i;
|
||||
dc.SetPen(marker->GetStyle());
|
||||
int marker_x = RelativeXFromSamples(marker->GetPosition());
|
||||
dc.DrawLine(marker_x, audio_top, marker_x, audio_top+audio_height);
|
||||
dc.SetBrush(wxBrush(marker->GetStyle().GetColour()));
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
if (marker->GetFeet() & AudioMarker::Feet_Left)
|
||||
{
|
||||
wxPoint foot_top[3] = { wxPoint(-foot_size, 0), wxPoint(0, 0), wxPoint(0, foot_size) };
|
||||
wxPoint foot_bot[3] = { wxPoint(-foot_size, 0), wxPoint(0, -foot_size), wxPoint(0, 0) };
|
||||
dc.DrawPolygon(3, foot_top, marker_x, audio_top);
|
||||
dc.DrawPolygon(3, foot_bot, marker_x, audio_top+audio_height);
|
||||
}
|
||||
if (marker->GetFeet() & AudioMarker::Feet_Right)
|
||||
{
|
||||
wxPoint foot_top[3] = { wxPoint(foot_size, 0), wxPoint(0, 0), wxPoint(0, foot_size) };
|
||||
wxPoint foot_bot[3] = { wxPoint(foot_size, 0), wxPoint(0, -foot_size), wxPoint(0, 0) };
|
||||
dc.DrawPolygon(3, foot_top, marker_x, audio_top);
|
||||
dc.DrawPolygon(3, foot_bot, marker_x, audio_top+audio_height);
|
||||
}
|
||||
wxPoint foot_top[3] = { wxPoint(foot_size, 0), wxPoint(0, 0), wxPoint(0, foot_size) };
|
||||
wxPoint foot_bot[3] = { wxPoint(foot_size, 0), wxPoint(0, -foot_size), wxPoint(0, 0) };
|
||||
dc.DrawPolygon(3, foot_top, marker_x, audio_top);
|
||||
dc.DrawPolygon(3, foot_bot, marker_x, audio_top+audio_height);
|
||||
}
|
||||
}
|
||||
|
||||
region++;
|
||||
// Draw labels
|
||||
std::vector<AudioLabelProvider::AudioLabel> labels;
|
||||
controller->GetLabels(updrectsamples, labels);
|
||||
if (!labels.empty())
|
||||
{
|
||||
wxDCFontChanger fc(dc);
|
||||
wxFont font = dc.GetFont();
|
||||
font.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
dc.SetFont(font);
|
||||
dc.SetTextForeground(*wxWHITE);
|
||||
for (size_t i = 0; i < labels.size(); ++i)
|
||||
{
|
||||
wxSize extent = dc.GetTextExtent(labels[i].text);
|
||||
int left = RelativeXFromSamples(labels[i].range.begin());
|
||||
int width = AbsoluteXFromSamples(labels[i].range.length());
|
||||
|
||||
// If it doesn't fit, truncate
|
||||
if (width < extent.GetWidth())
|
||||
{
|
||||
dc.SetClippingRegion(left, audio_top + 4, width, extent.GetHeight());
|
||||
dc.DrawText(labels[i].text, left, audio_top + 4);
|
||||
dc.DestroyClippingRegion();
|
||||
}
|
||||
// Otherwise center in the range
|
||||
else
|
||||
{
|
||||
dc.DrawText(labels[i].text, left + (width - extent.GetWidth()) / 2, audio_top + 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (track_cursor_pos >= 0)
|
||||
|
@ -873,7 +903,7 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
|||
wxDCPenChanger penchanger(dc, wxPen(*wxWHITE));
|
||||
dc.DrawLine(track_cursor_pos-scroll_left, audio_top, track_cursor_pos-scroll_left, audio_top+audio_height);
|
||||
|
||||
if (!track_cursor_label.IsEmpty())
|
||||
if (!track_cursor_label.empty())
|
||||
{
|
||||
wxDCFontChanger fc(dc);
|
||||
wxFont font = dc.GetFont();
|
||||
|
@ -887,11 +917,13 @@ void AudioDisplay::OnPaint(wxPaintEvent& event)
|
|||
|
||||
int old_bg_mode = dc.GetBackgroundMode();
|
||||
dc.SetBackgroundMode(wxTRANSPARENT);
|
||||
// Draw border
|
||||
dc.SetTextForeground(wxColour(64, 64, 64));
|
||||
dc.DrawText(track_cursor_label, label_pos.x+1, label_pos.y+1);
|
||||
dc.DrawText(track_cursor_label, label_pos.x+1, label_pos.y-1);
|
||||
dc.DrawText(track_cursor_label, label_pos.x-1, label_pos.y+1);
|
||||
dc.DrawText(track_cursor_label, label_pos.x-1, label_pos.y-1);
|
||||
// Draw fill
|
||||
dc.SetTextForeground(*wxWHITE);
|
||||
dc.DrawText(track_cursor_label, label_pos.x, label_pos.y);
|
||||
dc.SetBackgroundMode(old_bg_mode);
|
||||
|
|
|
@ -51,7 +51,7 @@ class AudioController;
|
|||
///
|
||||
/// The timing controller must then be sent the marker drag events as well as
|
||||
/// clicks in empty areas of the audio display.
|
||||
class AudioTimingController : public AudioMarkerProvider {
|
||||
class AudioTimingController : public AudioMarkerProvider, public AudioLabelProvider {
|
||||
protected:
|
||||
/// The primary playback range has changed, usually as a result of user interaction.
|
||||
agi::signal::Signal<> AnnounceUpdatedPrimaryRange;
|
||||
|
@ -61,6 +61,9 @@ protected:
|
|||
|
||||
/// A marker has been updated in some way.
|
||||
agi::signal::Signal<AudioMarker*> AnnounceMarkerMoved;
|
||||
|
||||
/// A label has been updated in some way.
|
||||
agi::signal::Signal<AudioLabel*> AnnounceLabelChanged;
|
||||
public:
|
||||
/// @brief Get any warning message to show in the audio display
|
||||
/// @return The warning message to show, may be empty if there is none
|
||||
|
@ -144,6 +147,7 @@ public:
|
|||
DEFINE_SIGNAL_ADDERS(AnnounceUpdatedPrimaryRange, AddUpdatedPrimaryRangeListener)
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceUpdatedStyleRanges, AddUpdatedStyleRangesListener)
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
|
||||
DEFINE_SIGNAL_ADDERS(AnnounceLabelChanged, AddLabelChangedListener)
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -172,7 +172,8 @@ public:
|
|||
wxString GetWarningMessage() const;
|
||||
SampleRange GetIdealVisibleSampleRange() const;
|
||||
SampleRange GetPrimaryPlaybackRange() const;
|
||||
bool HasLabels() const;
|
||||
bool HasLabels() const { return false; }
|
||||
void GetLabels(SampleRange const& range, std::vector<AudioLabel> &out) const { }
|
||||
void Next();
|
||||
void Prev();
|
||||
void Commit();
|
||||
|
@ -348,13 +349,6 @@ SampleRange AudioTimingControllerDialogue::GetPrimaryPlaybackRange() const
|
|||
|
||||
|
||||
|
||||
bool AudioTimingControllerDialogue::HasLabels() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioTimingControllerDialogue::Next()
|
||||
{
|
||||
selection_controller->NextLine();
|
||||
|
|
Loading…
Reference in a new issue