47 constexpr int FrameThickness{ 1 };
60 int height,
bool dB,
bool outer,
68 float sign = (value >= 0 ? 1 : -1);
72 value = (db + dBr) / dBr;
102 value = (max - value) / (max -
min);
103 return (
int) (value * (height - 1) + 0.5);
111 double sign = (value >= 0 ? 1 : -1);
112 return DB_TO_LINEAR((fabs(value) * dBRange) - dBRange) * sign;
116 bool dB,
double dBRange,
float zoomMin,
float zoomMax)
118 wxASSERT(height > 0);
121 height == 1 ? (zoomMin + zoomMax) / 2 :
122 zoomMax - (yy / (
float)(height - 1)) * (zoomMax - zoomMin);
139 auto &dc = context.
dc;
145 dc.SetPen(*wxBLACK_PEN);
147 rect.x + 2, rect.y + 6,
148 rect.x + 8, rect.y + 6);
150 rect.x + 2, rect.y + 6,
151 rect.x + 6, rect.y + 2);
153 rect.x + 2, rect.y + 6,
154 rect.x + 6, rect.y + 10);
156 rect.x + 2, rect.y + rect.height - 8,
157 rect.x + 8, rect.y + rect.height - 8);
159 rect.x + 2, rect.y + rect.height - 8,
160 rect.x + 6, rect.y + rect.height - 4);
162 rect.x + 2, rect.y + rect.height - 8,
163 rect.x + 6, rect.y + rect.height - 12);
168 static const wxString ellipsis =
"\u2026";
170 if (dc.GetTextExtent(text).GetWidth() <= maxWidth)
175 auto right =
static_cast<int>(text.Length() - 2);
177 while (left <= right)
179 auto middle = (left + right) / 2;
180 auto str = text.SubString(0, middle).Trim() + ellipsis;
181 auto strWidth = dc.GetTextExtent(
str).GetWidth();
182 if (strWidth < maxWidth)
187 else if (strWidth > maxWidth)
196 return text.SubString(0, right).Trim() + ellipsis;
198 return wxEmptyString;
207 bool hasClipRect = dc.GetClippingBox(clipRect);
214 auto drawingRect = rect;
218 drawingRect.SetLeft(std::max(rect.GetLeft(), clipRect.GetLeft() - clipFrameRadius - 1));
219 drawingRect.SetRight(
std::min(rect.GetRight(), clipRect.GetRight() + clipFrameRadius + 1));
228 drawingRect.height + clipFrameRadius };
229 dc.SetBrush(*wxTRANSPARENT_BRUSH);
231 dc.DrawRoundedRectangle(strokeRect, clipFrameRadius);
234 AColor::UseThemeColour(&dc, highlight ? clrClipAffordanceActiveBrush : clrClipAffordanceInactiveBrush, clrClipAffordanceOutlinePen);
235 dc.DrawRoundedRectangle(
240 drawingRect.height + clipFrameRadius
244 auto titleRect = hasClipRect ?
267 const auto playbackSpeed = 100 / clipStretchRatio;
274 if (fabs(playbackSpeed - 100.) < .95)
276 fullText = wxString::Format(
277 "%.1f%% speed", playbackSpeed > 100 ?
278 std::max(playbackSpeed, 100.1) :
280 else if (playbackSpeed < 1)
283 wxString::Format(
"%.1f%% speed", std::max(playbackSpeed, 0.1));
285 const auto roundedPlaybackSpeed =
287 fullText = wxString::Format(
"%d%% speed", roundedPlaybackSpeed);
304 wxDC& dc,
const wxRect& titleRect,
const wxString&
title)
306 if(titleRect.IsEmpty())
308 const auto hAlign = wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft ?
314 if (!truncatedTitle.empty())
317 truncatedTitle, titleRect,
318 (hAlign == HAlign::left ? wxALIGN_LEFT : wxALIGN_RIGHT) |
319 wxALIGN_CENTER_VERTICAL);
320 return ClipTitle{ truncatedTitle, hAlign };
328 wxDC& dc,
const wxRect& titleRect,
const wxString&
title)
334 wxDC& dc,
const wxRect& titleRect,
const wxString&
title,
335 double clipStretchRatio)
338 if (!clipTitle.has_value())
343 constexpr auto minSpaceBetweenTitleAndSpeed = 12;
344 const auto remainingWidth = std::max(
345 titleRect.GetWidth() - dc.GetTextExtent(clipTitle->text).GetWidth() -
346 minSpaceBetweenTitleAndSpeed,
348 const auto truncatedText =
355 (clipTitle->alignment == HAlign::left ? wxALIGN_RIGHT :
357 wxALIGN_CENTER_VERTICAL);
364 dc.SetBrush(*wxTRANSPARENT_BRUSH);
368 clipRect.GetLeft(), clipRect.GetTop(),
369 clipRect.GetLeft(), clipRect.GetBottom());
371 clipRect.GetRight(), clipRect.GetTop(),
372 clipRect.GetRight(), clipRect.GetBottom());
389 dc.DrawRectangle(wxRect(
392 dc.DrawRectangle(wxRect(
393 clipRect.GetRight() + 1, clipRect.GetTop(),
402 dc.DrawRectangle(rect);
421 const auto dc = &context.
dc;
426 int gridW = syncLockBitmap.GetWidth() - 6;
427 int gridH = syncLockBitmap.GetHeight() - 8;
430 int blockX = (rect.x / gridW) % 5;
433 int xOffset = rect.x % gridW;
434 if (xOffset < 0) xOffset += gridW;
438 bool extraCol =
false;
439 if (syncLockBitmap.GetWidth() - gridW > xOffset) {
442 blockX = (blockX - 1) % 5;
445 if (blockX < 0) blockX += 5;
448 while (xx < rect.width) {
449 int width = syncLockBitmap.GetWidth() - xOffset;
450 if (xx + width > rect.width)
451 width = rect.width - xx;
458 int blockY = (rect.y / gridH) % 5;
461 int yOffset = rect.y % gridH;
462 if (yOffset < 0) yOffset += gridH;
466 bool extraRow =
false;
467 if (syncLockBitmap.GetHeight() - gridH > yOffset) {
470 blockY = (blockY - 1) % 5;
473 if (blockY < 0) blockY += 5;
476 while (yy < rect.height)
478 int height = syncLockBitmap.GetHeight() - yOffset;
479 if (yy + height > rect.height)
480 height = rect.height - yy;
483 if ((blockX == 0 && blockY == 0) || (blockX == 2 && blockY == 1) ||
484 (blockX == 4 && blockY == 2) || (blockX == 1 && blockY == 3) ||
485 (blockX == 3 && blockY == 4))
489 if (width != syncLockBitmap.GetWidth() || height != syncLockBitmap.GetHeight()) {
490 wxBitmap subSyncLockBitmap =
491 syncLockBitmap.GetSubBitmap(wxRect(xOffset, yOffset, width, height));
492 dc->DrawBitmap(subSyncLockBitmap, rect.x + xx, rect.y + yy,
true);
495 dc->DrawBitmap(syncLockBitmap, rect.x + xx, rect.y + yy,
true);
507 yy += gridH - yOffset;
510 blockY = (blockY + 1) % 5;
521 xx += gridW - xOffset;
524 blockX = (blockX + 1) % 5;
551 : zoomInfo { zoomInfo }
557 , minorTick { GetMinorTick() }
558 , noteDuration { minorTick.
duration }
560 , notesInBeat { CalculateNotesInBeat() }
565 wxDC& dc,
const wxRect& rect,
const wxPen& beatSepearatorPen,
const wxPen& barSeparatorPen)
const
567 dc.SetPen (beatSepearatorPen);
570 const auto minorTick = GetMinorTick();
572 const auto [firstNote, lastNote] = GetBoundaries(
573 rect, rect, noteWidth);
575 for (
auto noteIndex = firstNote; noteIndex < lastNote; ++noteIndex)
577 const auto position = GetPositionInRect (noteIndex, rect, noteDuration);
579 if (position < rect.GetLeft () || position >= rect.GetRight ())
582 dc.SetPen(IsFirstInMajorTick(noteIndex) ? barSeparatorPen : beatSepearatorPen);
583 dc.DrawLine (position, rect.GetTop (), position, rect.GetBottom () + 1);
588 wxDC& dc,
const wxRect& subRect,
const wxRect& fullRect,
const wxBrush& strongBeatBrush,
589 const wxBrush& weakBeatBrush)
const
591 if (!UseAlternatingColors ())
593 dc.SetBrush(strongBeatBrush);
594 dc.DrawRectangle(subRect);
598 auto [firstIndex, lastIndex] =
599 GetBoundaries (subRect, fullRect, noteWidth);
602 firstIndex = (firstIndex / notesInBeat) * notesInBeat;
604 const auto beatDuration = noteDuration;
606 const auto top = fullRect.GetTop ();
607 const auto height = fullRect.GetHeight ();
609 bool strongBeat = (firstIndex / notesInBeat) % 2 == 0;
610 for (
auto index = firstIndex; index < lastIndex; index += notesInBeat, strongBeat = !strongBeat)
612 const auto left = std::max<int> (
613 GetPositionInRect(index, fullRect, beatDuration),
615 const auto right = std::min<int> (
616 GetPositionInRect(index + notesInBeat, fullRect, beatDuration),
619 const auto& brush = strongBeat ? strongBeatBrush : weakBeatBrush;
632 assert(track.
GetOwner()->GetOwner());
634 return *track.
GetOwner()->GetOwner();
639 if (UseAlternatingColors())
640 return minorTick.
lower / 4;
647 const auto notesInMajorTick =
650 if (notesInMajorTick == 0)
653 return noteIndex % notesInMajorTick == 0;
658 return minorTick.
lower >= 4 && minorTick.
upper == 1;
666 std::pair<int64_t, int64_t>
GetBoundaries(
const wxRect& subRect,
const wxRect& fullRect,
double width)
const
668 const auto offset = subRect.x - fullRect.x;
682 auto minorMinorLength =
685 const auto nextSubdivision = subdivision.minor.duration == 0.0 ?
692 tick.duration *= 2.0;
693 minorMinorLength *= 2.0;
695 if (nextSubdivision.lower >= tick.lower)
696 return nextSubdivision;
706 const Track *track,
const wxBrush &selBrush,
const wxBrush &unselBrush,
709 const auto dc = &context.
dc;
711 const auto &selectedRegion = *artist->pSelectedRegion;
712 const auto& zoomInfo = *artist->pZoomInfo;
716 const double sel0 = useSelection ? selectedRegion.t0() : 0.0;
717 const double sel1 = useSelection ? selectedRegion.t1() : 0.0;
719 BeatsGridlinePainter gridlinePainter(zoomInfo, *track);
721 dc->SetPen(*wxTRANSPARENT_PEN);
723 auto drawBgRect = [dc, &gridlinePainter, artist, &rect](
724 const wxBrush& regularBrush,
725 const wxBrush& beatStrongBrush,
726 const wxBrush& beatWeakBrush,
const wxRect& subRect)
728 if (!gridlinePainter.enabled)
731 dc->SetBrush(regularBrush);
732 dc->DrawRectangle(subRect);
736 gridlinePainter.DrawBackground(
737 *dc, subRect, rect, beatStrongBrush, beatWeakBrush);
744 wxRect before = rect;
749 if (before.GetRight() > rect.GetRight()) {
750 before.width = rect.width;
753 if (before.width > 0) {
754 drawBgRect(unselBrush, artist->beatStrongBrush, artist->beatWeakBrush, before);
756 within.x = 1 + before.GetRight();
760 if (
within.GetRight() > rect.GetRight()) {
771 drawBgRect(selBrush, artist->beatStrongSelBrush, artist->beatWeakSelBrush,
within);
776 unselBrush, artist->beatStrongBrush, artist->beatWeakBrush,
within);
780 after.x = 1 +
within.GetRight();
787 after.width = 1 + rect.GetRight() - after.x;
790 unselBrush, artist->beatStrongBrush, artist->beatWeakBrush, after);
795 unselBrush, artist->beatStrongBrush, artist->beatWeakBrush, rect);
798 if (gridlinePainter.enabled)
799 gridlinePainter.DrawSeparators(*dc, rect, artist->beatSepearatorPen, artist->barSepearatorPen);
803 const wxRect& rect,
const Track* track)
805 const auto dc = &context.
dc;
807 const auto& selectedRegion = *artist->pSelectedRegion;
809 if (selectedRegion.isPoint())
811 const auto& zoomInfo = *artist->pZoomInfo;
812 auto x =
static_cast<int>(zoomInfo.
TimeToPosition(selectedRegion.t0(), rect.x));
813 if (x >= rect.GetLeft() && x <= rect.GetRight())
816 AColor::Line(*dc, x, rect.GetTop(), x, rect.GetBottom());
static const wxPoint2DDouble outer[]
TimeDisplayModeSetting TimeDisplayModePreference
declares abstract base class Track, TrackList, and iterators over TrackList
int GetWaveYPos(float value, float min, float max, int height, bool dB, bool outer, float dBr, bool clip)
float FromDB(float value, double dBRange)
float ValueOfPixel(int yy, int height, bool offset, bool dB, double dBRange, float zoomMin, float zoomMax)
static constexpr int ClipSelectionStrokeSize
bool within(A a, B b, DIST d)
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
static void CursorColor(wxDC *dc)
static void UseThemeColour(wxDC *dc, int iBrush, int iPen=-1, int alpha=255)
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
static ProjectTimeRuler & Get(AudacityProject &project)
BeatsFormat & GetBeatsFormat()
static bool IsSelectedOrSyncLockSelected(const Track *pTrack)
wxImage & Image(int iIndex)
static bool IsPassThroughMode(double stretchRatio)
static TrackArtist * Get(TrackPanelDrawingContext &)
Abstract base class for an object holding data associated with points on a time axis.
bool GetSelected() const
Selectedness is always the same for all channels of a group.
std::shared_ptr< TrackList > GetOwner() const
double GetAbsoluteOffset(double offset) const
double TimeRangeToPixelWidth(double timeRange) const
int64 TimeToPosition(double time, int64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
AUDACITY_DLL_API void DrawSyncLockTiles(TrackPanelDrawingContext &context, const wxRect &rect)
AUDACITY_DLL_API wxString TruncateText(wxDC &dc, const wxString &text, const int maxWidth)
AUDACITY_DLL_API void DrawClipFolded(wxDC &dc, const wxRect &rect)
AUDACITY_DLL_API void DrawClipEdges(wxDC &dc, const wxRect &clipRect, bool selected=false)
AUDACITY_DLL_API wxRect DrawClipAffordance(wxDC &dc, const wxRect &affordanceRect, bool highlight=false, bool selected=false)
AUDACITY_DLL_API void DrawBackgroundWithSelection(TrackPanelDrawingContext &context, const wxRect &rect, const Track *track, const wxBrush &selBrush, const wxBrush &unselBrush, bool useSelection=true)
AUDACITY_DLL_API void DrawCursor(TrackPanelDrawingContext &context, const wxRect &rect, const Track *track)
AUDACITY_DLL_API void DrawNegativeOffsetTrackArrows(TrackPanelDrawingContext &context, const wxRect &rect)
AUDACITY_DLL_API bool DrawAudioClipTitle(wxDC &dc, const wxRect &titleRect, const wxString &title, double clipStretchRatio)
static constexpr int ClipFrameRadius
AUDACITY_DLL_API bool DrawClipTitle(wxDC &dc, const wxRect &titleRect, const wxString &title)
constexpr size_t npos(-1)
wxString GetPlaybackSpeedFullText(double clipStretchRatio)
constexpr double minSubdivisionWidth
std::optional< ClipTitle > DoDrawAudioTitle(wxDC &dc, const wxRect &titleRect, const wxString &title)
wxRect GetAffordanceTitleRect(const wxRect &rect)
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
void DrawBackground(wxDC &dc, const wxRect &subRect, const wxRect &fullRect, const wxBrush &strongBeatBrush, const wxBrush &weakBeatBrush) const
const double noteDuration
std::pair< int64_t, int64_t > GetBoundaries(const wxRect &subRect, const wxRect &fullRect, double width) const
const BeatsFormat & beatsRulerFormat
double GetPositionInRect(int64_t index, const wxRect &rect, double duration) const
const BeatsFormat::Tick minorTick
BeatsFormat::Tick GetMinorTick() const
const ZoomInfo & zoomInfo
const BeatsFormat::Tick majorTick
void DrawSeparators(wxDC &dc, const wxRect &rect, const wxPen &beatSepearatorPen, const wxPen &barSeparatorPen) const
BeatsGridlinePainter(const ZoomInfo &zoomInfo, const Track &track) noexcept
bool UseAlternatingColors() const
int64_t CalculateNotesInBeat() const
const int64_t notesInBeat
bool IsFirstInMajorTick(int64_t noteIndex) const
const AudacityProject & GetProject(const Track &track) const