diff --git a/aegisub/src/audio_controller.cpp b/aegisub/src/audio_controller.cpp index 8815c5fc1..96411bcf0 100644 --- a/aegisub/src/audio_controller.cpp +++ b/aegisub/src/audio_controller.cpp @@ -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 &labels) const +{ + if (timing_controller.get()) timing_controller->GetLabels(range, labels); +} double AudioController::GetVolume() const { diff --git a/aegisub/src/audio_controller.h b/aegisub/src/audio_controller.h index cb9ca225c..c31e2e93b 100644 --- a/aegisub/src/audio_controller.h +++ b/aegisub/src/audio_controller.h @@ -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 &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 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 &labels) const; + /// @brief Get the playback audio volume /// @return The amplification factor for the audio diff --git a/aegisub/src/audio_display.cpp b/aegisub/src/audio_display.cpp index 6154c0e93..f61022b3f 100644 --- a/aegisub/src/audio_display.cpp +++ b/aegisub/src/audio_display.cpp @@ -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 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 subregions; - int x = updrect.x; - for (std::vector::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::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 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); diff --git a/aegisub/src/audio_timing.h b/aegisub/src/audio_timing.h index 6b3de988f..4dad031c9 100644 --- a/aegisub/src/audio_timing.h +++ b/aegisub/src/audio_timing.h @@ -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 AnnounceMarkerMoved; + + /// A label has been updated in some way. + agi::signal::Signal 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) }; diff --git a/aegisub/src/audio_timing_dialogue.cpp b/aegisub/src/audio_timing_dialogue.cpp index 0f1c658ae..bf514cefc 100644 --- a/aegisub/src/audio_timing_dialogue.cpp +++ b/aegisub/src/audio_timing_dialogue.cpp @@ -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 &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();