Add support for labels in the audio display

Originally committed to SVN as r5589.
This commit is contained in:
Thomas Goyne 2011-09-15 05:16:26 +00:00
parent 3e708eab10
commit 3f05fe6b3e
5 changed files with 131 additions and 65 deletions

View File

@ -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
{

View File

@ -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

View File

@ -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);

View File

@ -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)
};

View File

@ -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();