47 constexpr int FrameThickness{ 1 };
58 if (affordanceRect.IsEmpty() ||
title.empty())
67 int height,
bool dB,
bool outer,
75 float sign = (value >= 0 ? 1 : -1);
79 value = (db + dBr) / dBr;
109 value = (max - value) / (max -
min);
110 return (
int) (value * (height - 1) + 0.5);
118 double sign = (value >= 0 ? 1 : -1);
119 return DB_TO_LINEAR((fabs(value) * dBRange) - dBRange) * sign;
123 bool dB,
double dBRange,
float zoomMin,
float zoomMax)
125 wxASSERT(height > 0);
128 height == 1 ? (zoomMin + zoomMax) / 2 :
129 zoomMax - (yy / (
float)(height - 1)) * (zoomMax - zoomMin);
146 auto &dc = context.
dc;
152 dc.SetPen(*wxBLACK_PEN);
154 rect.x + 2, rect.y + 6,
155 rect.x + 8, rect.y + 6);
157 rect.x + 2, rect.y + 6,
158 rect.x + 6, rect.y + 2);
160 rect.x + 2, rect.y + 6,
161 rect.x + 6, rect.y + 10);
163 rect.x + 2, rect.y + rect.height - 8,
164 rect.x + 8, rect.y + rect.height - 8);
166 rect.x + 2, rect.y + rect.height - 8,
167 rect.x + 6, rect.y + rect.height - 4);
169 rect.x + 2, rect.y + rect.height - 8,
170 rect.x + 6, rect.y + rect.height - 12);
175 static const wxString ellipsis =
"\u2026";
177 if (dc.GetTextExtent(text).GetWidth() <= maxWidth)
182 auto right =
static_cast<int>(text.Length() - 2);
184 while (left <= right)
186 auto middle = (left + right) / 2;
187 auto str = text.SubString(0, middle).Trim() + ellipsis;
188 auto strWidth = dc.GetTextExtent(
str).GetWidth();
189 if (strWidth < maxWidth)
194 else if (strWidth > maxWidth)
203 return text.SubString(0, right).Trim() + ellipsis;
205 return wxEmptyString;
210 wxDC& dc,
const wxRect& clipRect, std::optional<wxRect>& clippedClipRect)
213 const auto hasClipRect = dc.GetClippingBox(tmp);
215 clippedClipRect = tmp;
224 wxDC& dc,
const wxRect& clipRect,
bool highlight,
bool selected)
229 std::optional<wxRect> clippedClipRect;
230 const auto affordanceRect =
238 auto drawingRect = clipRect;
242 drawingRect.SetLeft(std::max(
244 clippedClipRect->GetLeft() - clipFrameRadius - 1));
247 clippedClipRect->GetRight() + clipFrameRadius + 1));
256 drawingRect.height + clipFrameRadius };
257 dc.SetBrush(*wxTRANSPARENT_BRUSH);
259 dc.DrawRoundedRectangle(strokeRect, clipFrameRadius);
262 AColor::UseThemeColour(&dc, highlight ? clrClipAffordanceActiveBrush : clrClipAffordanceInactiveBrush, clrClipAffordanceOutlinePen);
263 dc.DrawRoundedRectangle(
268 drawingRect.height + clipFrameRadius
272 return affordanceRect;
278 wxDC& dc,
const wxRect& affordanceRect,
const wxString&
title)
280 const auto alignLeft =
281 wxTheApp->GetLayoutDirection() == wxLayout_LeftToRight;
282 const auto width = dc.GetTextExtent(
title).GetWidth();
284 alignLeft ? affordanceRect.GetLeft() : affordanceRect.GetRight() - width;
285 return { left, affordanceRect.GetTop(), width, affordanceRect.GetHeight() };
290 wxDC& dc,
const wxRect& affordanceRect,
const wxString&
title)
292 if (affordanceRect.IsEmpty())
294 else if (
title.empty())
297 if (truncatedTitle.empty())
299 const auto titleRect =
301 const auto alignLeft =
302 wxTheApp->GetLayoutDirection() == wxLayout_LeftToRight;
304 truncatedTitle, titleRect,
305 (alignLeft ? wxALIGN_LEFT : wxALIGN_RIGHT) | wxALIGN_CENTER_VERTICAL);
311 dc.SetBrush(*wxTRANSPARENT_BRUSH);
315 clipRect.GetLeft(), clipRect.GetTop(),
316 clipRect.GetLeft(), clipRect.GetBottom());
318 clipRect.GetRight(), clipRect.GetTop(),
319 clipRect.GetRight(), clipRect.GetBottom());
336 dc.DrawRectangle(wxRect(
339 dc.DrawRectangle(wxRect(
340 clipRect.GetRight() + 1, clipRect.GetTop(),
349 dc.DrawRectangle(rect);
368 const auto dc = &context.
dc;
373 int gridW = syncLockBitmap.GetWidth() - 6;
374 int gridH = syncLockBitmap.GetHeight() - 8;
377 int blockX = (rect.x / gridW) % 5;
380 int xOffset = rect.x % gridW;
381 if (xOffset < 0) xOffset += gridW;
385 bool extraCol =
false;
386 if (syncLockBitmap.GetWidth() - gridW > xOffset) {
389 blockX = (blockX - 1) % 5;
392 if (blockX < 0) blockX += 5;
395 while (xx < rect.width) {
396 int width = syncLockBitmap.GetWidth() - xOffset;
397 if (xx + width > rect.width)
398 width = rect.width - xx;
405 int blockY = (rect.y / gridH) % 5;
408 int yOffset = rect.y % gridH;
409 if (yOffset < 0) yOffset += gridH;
413 bool extraRow =
false;
414 if (syncLockBitmap.GetHeight() - gridH > yOffset) {
417 blockY = (blockY - 1) % 5;
420 if (blockY < 0) blockY += 5;
423 while (yy < rect.height)
425 int height = syncLockBitmap.GetHeight() - yOffset;
426 if (yy + height > rect.height)
427 height = rect.height - yy;
430 if ((blockX == 0 && blockY == 0) || (blockX == 2 && blockY == 1) ||
431 (blockX == 4 && blockY == 2) || (blockX == 1 && blockY == 3) ||
432 (blockX == 3 && blockY == 4))
436 if (width != syncLockBitmap.GetWidth() || height != syncLockBitmap.GetHeight()) {
437 wxBitmap subSyncLockBitmap =
438 syncLockBitmap.GetSubBitmap(wxRect(xOffset, yOffset, width, height));
439 dc->DrawBitmap(subSyncLockBitmap, rect.x + xx, rect.y + yy,
true);
442 dc->DrawBitmap(syncLockBitmap, rect.x + xx, rect.y + yy,
true);
454 yy += gridH - yOffset;
457 blockY = (blockY + 1) % 5;
468 xx += gridW - xOffset;
471 blockX = (blockX + 1) % 5;
484 assert(track.
GetOwner()->GetOwner());
486 return *track.
GetOwner()->GetOwner();
509 : zoomInfo { zoomInfo }
514 , minorTick { GetMinorTick() }
515 , noteDuration { minorTick.
duration }
517 , notesInBeat { CalculateNotesInBeat() }
522 wxDC& dc,
const wxRect& rect,
const wxPen& beatSepearatorPen,
const wxPen& barSeparatorPen)
const
524 dc.SetPen (beatSepearatorPen);
526 const auto [firstNote, lastNote] = GetBoundaries(
527 rect, rect, noteWidth);
529 for (
auto noteIndex = firstNote; noteIndex < lastNote; ++noteIndex)
531 const auto position = GetPositionInRect (noteIndex, rect, noteDuration);
533 if (position < rect.GetLeft () || position >= rect.GetRight ())
536 dc.SetPen(IsFirstInMajorTick(noteIndex) ? barSeparatorPen : beatSepearatorPen);
537 dc.DrawLine (position, rect.GetTop (), position, rect.GetBottom ());
542 wxDC& dc,
const wxRect& subRect,
const wxRect& fullRect,
const wxBrush& strongBeatBrush,
543 const wxBrush& weakBeatBrush)
const
545 if (!UseAlternatingColors ())
547 dc.SetBrush(strongBeatBrush);
548 dc.DrawRectangle(subRect);
552 auto [firstIndex, lastIndex] =
553 GetBoundaries (subRect, fullRect, noteWidth);
556 firstIndex = (firstIndex / notesInBeat) * notesInBeat;
558 const auto beatDuration = noteDuration;
560 const auto top = fullRect.GetTop ();
561 const auto height = fullRect.GetHeight ();
563 bool strongBeat = (firstIndex / notesInBeat) % 2 == 0;
564 for (
auto index = firstIndex; index < lastIndex; index += notesInBeat, strongBeat = !strongBeat)
566 const auto left = std::max<int> (
567 GetPositionInRect(index, fullRect, beatDuration),
569 const auto right = std::min<int> (
570 GetPositionInRect(index + notesInBeat, fullRect, beatDuration),
573 const auto& brush = strongBeat ? strongBeatBrush : weakBeatBrush;
576 dc.DrawRectangle (left, top, right - left + 1, height);
583 if (UseAlternatingColors())
584 return minorTick.
lower / 4;
591 const auto notesInMajorTick =
594 if (notesInMajorTick == 0)
597 return noteIndex % notesInMajorTick == 0;
602 return minorTick.
lower >= 4 && minorTick.
upper == 1;
610 std::pair<int64_t, int64_t>
GetBoundaries(
const wxRect& subRect,
const wxRect& fullRect,
double width)
const
612 const auto offset = subRect.x - fullRect.x;
626 auto minorMinorLength =
629 const auto nextSubdivision = subdivision.minor.duration == 0.0 ?
636 tick.duration *= 2.0;
637 minorMinorLength *= 2.0;
639 if (nextSubdivision.lower >= tick.lower)
640 return nextSubdivision;
650 const Channel &channel,
const wxBrush &selBrush,
const wxBrush &unselBrush,
651 bool useSelection,
bool useBeatsAlternateColor)
653 const auto dc = &context.
dc;
655 const auto &selectedRegion = *artist->pSelectedRegion;
656 const auto& zoomInfo = *artist->pZoomInfo;
660 const double sel0 = useSelection ? selectedRegion.t0() : 0.0;
661 const double sel1 = useSelection ? selectedRegion.t1() : 0.0;
666 auto &track = *pTrack;
667 BeatsGridlinePainter gridlinePainter(zoomInfo,
GetProject(track));
669 dc->SetPen(*wxTRANSPARENT_PEN);
671 const auto& beatStrongBrush = artist->beatStrongBrush[useBeatsAlternateColor];
672 const auto& beatStrongSelBrush = artist->beatStrongSelBrush[useBeatsAlternateColor];
673 const auto& beatWeakBrush = artist->beatWeakBrush[useBeatsAlternateColor];
674 const auto& beatWeakSelBrush = artist->beatWeakSelBrush[useBeatsAlternateColor];
675 const auto& beatSepearatorPen = artist->beatSepearatorPen[useBeatsAlternateColor];
676 const auto& barSepearatorPen = artist->barSepearatorPen[useBeatsAlternateColor];
678 auto drawBgRect = [dc, &gridlinePainter, &rect](
679 const wxBrush& regularBrush,
680 const wxBrush& beatStrongBrush,
681 const wxBrush& beatWeakBrush,
const wxRect& subRect)
683 if (!gridlinePainter.enabled)
686 dc->SetBrush(regularBrush);
687 dc->DrawRectangle(subRect);
691 gridlinePainter.DrawBackground(
692 *dc, subRect, rect, beatStrongBrush, beatWeakBrush);
698 wxRect before = rect;
703 if (before.GetRight() > rect.GetRight()) {
704 before.width = rect.width;
707 if (before.width > 0) {
708 drawBgRect(unselBrush, beatStrongBrush, beatWeakBrush, before);
710 within.x = 1 + before.GetRight();
714 if (
within.GetRight() > rect.GetRight()) {
724 if (track.GetSelected()) {
725 drawBgRect(selBrush, beatStrongSelBrush, beatWeakSelBrush,
within);
730 unselBrush, beatStrongBrush, beatWeakBrush,
within);
734 after.x = 1 +
within.GetRight();
741 after.width = 1 + rect.GetRight() - after.x;
744 unselBrush, beatStrongBrush, beatWeakBrush, after);
749 unselBrush, beatStrongBrush, beatWeakBrush, rect);
752 if (gridlinePainter.enabled)
753 gridlinePainter.DrawSeparators(*dc, rect, beatSepearatorPen, barSepearatorPen);
757 const wxRect& rect,
const Track* track)
759 const auto dc = &context.
dc;
761 const auto& selectedRegion = *artist->pSelectedRegion;
763 if (selectedRegion.isPoint())
765 const auto& zoomInfo = *artist->pZoomInfo;
766 auto x =
static_cast<int>(zoomInfo.
TimeToPosition(selectedRegion.t0(), rect.x));
767 if (x >= rect.GetLeft() && x <= rect.GetRight())
770 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 SnapGuidePen(wxDC *dc)
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...
ChannelGroup & GetChannelGroup()
Channel object's lifetime is assumed to be nested in its Track's.
static ProjectTimeRuler & Get(AudacityProject &project)
BeatsFormat & GetBeatsFormat()
static bool IsSelectedOrSyncLockSelected(const Track &track)
wxImage & Image(int iIndex)
static TrackArtist * Get(TrackPanelDrawingContext &)
Abstract base class for an object holding data associated with points on a time axis.
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 DrawBackgroundWithSelection(TrackPanelDrawingContext &context, const wxRect &rect, const Channel &channel, const wxBrush &selBrush, const wxBrush &unselBrush, bool useSelection=true, bool useBeatsAlternateColor=false)
Helper: draws background with selection rect.
AUDACITY_DLL_API void DrawSyncLockTiles(TrackPanelDrawingContext &context, const wxRect &rect)
AUDACITY_DLL_API bool DrawClipTitle(wxDC &dc, const wxRect &affordanceRect, const wxString &title)
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 void DrawSnapLines(wxDC *dc, wxInt64 snap0, wxInt64 snap1)
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 wxRect DrawClipAffordance(wxDC &dc, const wxRect &clipRect, bool highlight=false, bool selected=false)
static constexpr int ClipFrameRadius
constexpr double minSubdivisionWidth
const AudacityProject & GetProject(const Track &track)
wxString GetTruncatedTitle(wxDC &dc, const wxString &title, const wxRect &affordanceRect)
wxRect GetClipAffordanceRect(wxDC &dc, const wxRect &clipRect, std::optional< wxRect > &clippedClipRect)
wxRect GetClipTruncatedTitleRect(wxDC &dc, const wxRect &affordanceRect, const wxString &title)
wxRect GetAffordanceTitleRect(const wxRect &rect)
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
BeatsGridlinePainter(const ZoomInfo &zoomInfo, const AudacityProject &project) noexcept
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
bool UseAlternatingColors() const
int64_t CalculateNotesInBeat() const
const int64_t notesInBeat
bool IsFirstInMajorTick(int64_t noteIndex) const