Audacity 3.2.0
Ruler.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Ruler.cpp
6
7 Dominic Mazzoni
8
9*******************************************************************//******************************************************************/
41
42
43#include "Ruler.h"
44
45#include <wx/dcclient.h>
46#include <wx/dcscreen.h>
47
48#include "AColor.h"
49#include "AllThemeResources.h"
50#include "Envelope.h"
51#include "NumberScale.h"
52#include "Theme.h"
53#include "ZoomInfo.h"
54
55#include "RulerUpdater.h"
56
57using std::min;
58using std::max;
59
60//wxColour Ruler::mTickColour{ 153, 153, 153 };
61
62//
63// Ruler
64//
65
67{
68 mbTicksOnly = true;
69 mbTicksAtExtremes = false;
70 mTickColour = wxColour( theTheme.Colour( clrTrackPanelText ));
71 mPen.SetColour(mTickColour);
72
73 // Note: the font size is now adjusted automatically whenever
74 // Invalidate is called on a horizontal Ruler, unless the user
75 // calls SetFonts manually. So the defaults here are not used
76 // often.
77
78 int fontSize = 10;
79#ifdef __WXMSW__
80 fontSize = 8;
81#endif
82
83 mbMinor = true;
84
85 mTwoTone = false;
86
89
90 mTickLengths = { 4, 2, 2 };
91}
92
94{
95 Invalidate(); // frees up our arrays
96}
97
98void Ruler::SetTwoTone(bool twoTone)
99{
100 mTwoTone = twoTone;
101}
102
103void Ruler::SetFormat(const RulerFormat *pFormat)
104{
105 if (mRulerStruct.mpRulerFormat != pFormat) {
106 mRulerStruct.mpRulerFormat = pFormat;
107 Invalidate();
108 }
109}
110
112{
113 if (mpUpdater != pUpdater) {
115 Invalidate();
116 }
117}
118
120{
121 // Specify the name of the units (like "dB") if you
122 // want numbers like "1.6" formatted as "1.6 dB".
123
124 if (mRulerStruct.mUnits != units) {
125 mRulerStruct.mUnits = units;
126
127 Invalidate();
128 }
129}
130
131void Ruler::SetDbMirrorValue( const double d )
132{
133 if (mRulerStruct.mDbMirrorValue != d) {
135
136 Invalidate();
137 }
138}
139
140void Ruler::SetOrientation(int orient)
141{
142 // wxHORIZONTAL || wxVERTICAL
143
144 if (mRulerStruct.mOrientation != orient) {
145 mRulerStruct.mOrientation = orient;
146
147 Invalidate();
148 }
149}
150
151void Ruler::SetRange(double min, double max)
152{
153 SetRange(min, max, min, max);
154}
155
157 (double min, double max, double hiddenMin, double hiddenMax)
158{
159 // For a horizontal ruler,
160 // min is the value in the center of pixel "left",
161 // max is the value in the center of pixel "right".
162
163 // In the special case of a time ruler,
164 // hiddenMin and hiddenMax are values that would be shown with the fisheye
165 // turned off. In other cases they equal min and max respectively.
166
167 if (mRulerStruct.mMin != min || mRulerStruct.mMax != max ||
168 mRulerStruct.mHiddenMin != hiddenMin || mRulerStruct.mHiddenMax != hiddenMax) {
170 mRulerStruct.mMax = max;
171 mRulerStruct.mHiddenMin = hiddenMin;
172 mRulerStruct.mHiddenMax = hiddenMax;
173
174 Invalidate();
175 }
176}
177
178void Ruler::SetLabelEdges(bool labelEdges)
179{
180 // If this is true, the edges of the ruler will always
181 // receive a label. If not, the nearest round number is
182 // labeled (which may or may not be the edge).
183
184 if (mRulerStruct.mLabelEdges != labelEdges) {
185 mRulerStruct.mLabelEdges = labelEdges;
186
187 Invalidate();
188 }
189}
190
191void Ruler::SetFlip(bool flip)
192{
193 // If this is true, the orientation of the tick marks
194 // is reversed from the default; eg. above the line
195 // instead of below
196
197 if (mRulerStruct.mFlip != flip) {
198 mRulerStruct.mFlip = flip;
199
200 Invalidate();
201 }
202}
203
204void Ruler::SetMinor(bool value)
205{
206 mbMinor = value;
207}
208
209namespace {
211 wxCoord &height, wxCoord &lead, wxDC &dc, const wxFont &font )
212{
213 wxCoord strW, strH, strD, strL;
214 static const wxString exampleText = wxT("0.9"); //ignored for height calcs on all platforms
215 dc.SetFont( font );
216 dc.GetTextExtent(exampleText, &strW, &strH, &strD, &strL);
217 height = strH - strD - strL;
218 lead = strL;
219}
220
222 wxCoord &height, wxCoord &lead,
223 wxDC &dc, int fontSize, wxFontWeight weight = wxFONTWEIGHT_NORMAL )
224{
225 const wxFont font{ fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, weight };
226 FindFontHeights( height, lead, dc, font );
227}
228}
229
230void Ruler::SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont)
231{
232 // Won't override these fonts
233
234 mpUserFonts = std::make_unique<RulerStruct::Fonts>(
235 RulerStruct::Fonts{ majorFont, minorFont, minorMinorFont, 0 } );
236
237 wxScreenDC dc;
238 wxCoord height;
239 FindFontHeights( height, mpUserFonts->lead, dc, majorFont );
240
241 mRulerStruct.mpFonts.reset();
242 mRulerStruct.mpFonts.reset();
243 Invalidate();
244}
245
247{
248 if ( mRulerStruct.mNumberScale != scale ) {
250 Invalidate();
251 }
252}
253
255{
256 bool inv = false;
257 if (mTickLengths.majorLength != tLengths.majorLength) {
259 inv = true;
260 }
261 if (mTickLengths.minorLength != tLengths.minorLength) {
263 inv = true;
264 }
267 inv = true;
268 }
269
270 if (inv) Invalidate();
271}
272
273void Ruler::OfflimitsPixels(int start, int end)
274{
275 int length = mRulerStruct.mLength;
276 if (mRulerStruct.mOrientation == wxHORIZONTAL)
278 else
280 if( length < 0 )
281 return;
282
283 auto size = static_cast<size_t>( length + 1 );
284 if ( mUserBits.size() < size ) {
285 mRulerStruct.mLength = length;
286 mUserBits.resize( size, false );
287 }
288
289 if (end < start)
290 std::swap( start, end );
291
292 if (start < 0)
293 start = 0;
296
297 for(int i = start; i <= end; i++)
298 mUserBits[i] = true;
299
300 Invalidate();
301}
302
303void Ruler::SetBounds(int left, int top, int right, int bottom)
304{
305 if (mRulerStruct.mLeft != left || mRulerStruct.mTop != top ||
306 mRulerStruct.mRight != right || mRulerStruct.mBottom != bottom) {
307 mRulerStruct.mLeft = left;
308 mRulerStruct.mTop = top;
309 mRulerStruct.mRight = right;
310 mRulerStruct.mBottom = bottom;
311
312 Invalidate();
313 }
314}
315
317{
318 if (mRulerStruct.mOrientation == wxHORIZONTAL)
320 else
322
323 mpCache.reset();
324 // Bug 2316 we must preserve off-limit pixels.
325 // mUserBits.clear();
326}
327
331 wxRect mRect;
332};
333
334static constexpr int MinPixelHeight =
335#ifdef __WXMSW__
336 12;
337#else
338 10;
339#endif
340
341static constexpr int MaxPixelHeight =
342#ifdef __WXMSW__
343 14;
344#elif __WXMAC__
345 10;
346#else
347 12;
348#endif
349
350
351void Ruler::ChooseFonts( wxDC &dc ) const
352{
353 const RulerStruct::Fonts* pUserFonts = mpUserFonts.get();
354 int desiredPixelHeight = mRulerStruct.mOrientation == wxHORIZONTAL
355 ? mRulerStruct.mBottom - mRulerStruct.mTop - 5 // height less ticks and 1px gap
357
359 return;
360
361 if (pUserFonts) {
362 mRulerStruct.mpFonts = std::make_unique<RulerStruct::Fonts>(*pUserFonts);
363 return;
364 }
365
366 mRulerStruct.mpFonts = std::make_unique<RulerStruct::Fonts>(RulerStruct::Fonts{ {}, {}, {}, 0 });
367 auto& fonts = *(mRulerStruct.mpFonts);
368
369 int fontSize = 4;
370
371 desiredPixelHeight =
372 std::max(MinPixelHeight, std::min(MaxPixelHeight, -desiredPixelHeight));
373
374 // Keep making the font bigger until it's too big, then subtract one.
375 wxCoord height;
376 FindFontHeights(height, fonts.lead, dc, fontSize, wxFONTWEIGHT_BOLD);
377 while (height <= desiredPixelHeight && fontSize < 40) {
378 fontSize++;
379 FindFontHeights(height, fonts.lead, dc, fontSize, wxFONTWEIGHT_BOLD);
380 }
381 fontSize--;
382 FindFontHeights(height, fonts.lead, dc, fontSize);
383
384 fonts.major = wxFont{ fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD };
385 fonts.minor = wxFont{ fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL };
386 fonts.minorMinor = wxFont{ fontSize - 1, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL };
387
388}
389
391 wxDC &dc, const Envelope* envelope )
392 const // Envelope *speedEnv, long minSpeed, long maxSpeed )
393{
394 if ( mpCache )
395 return;
396
397 // This gets called when something has been changed
398 // (i.e. we've been invalidated). Recompute all
399 // tick positions and font size.
400
401 ChooseFonts( dc );
402 mpCache = std::make_unique< Cache >();
403 auto &cache = *mpCache;
404
405 // If ruler is being resized, we could end up with it being too small.
406 // Values of mLength of zero or below cause bad array allocations and
407 // division by zero. So...
408 // IF too small THEN bail out and don't draw.
409 if( mRulerStruct.mLength <= 0 )
410 return;
411
412 if (mRulerStruct.mOrientation == wxHORIZONTAL)
413 cache.mRect = { 0, 0, mRulerStruct.mLength, 0 };
414 else
415 cache.mRect = { 0, 0, 0, mRulerStruct.mLength };
416
417 cache.mBits = mUserBits;
418 cache.mBits.resize( static_cast<size_t>(mRulerStruct.mLength + 1), false );
419
421 cache.mMajorLabels, cache.mMinorLabels, cache.mMinorMinorLabels,
422 cache.mBits, cache.mRect
423 };
424 if (mpUpdater)
425 mpUpdater->Update(dc, envelope, allOutputs, mRulerStruct);
426}
427
428auto Ruler::GetFonts() const -> RulerStruct::Fonts
429{
430 if ( !mRulerStruct.mpFonts ) {
431 wxScreenDC dc;
432 ChooseFonts( dc );
433 }
434
435 return *(mRulerStruct.mpFonts);
436}
437
438void Ruler::Draw(wxDC& dc) const
439{
440 Draw( dc, NULL);
441}
442
443void Ruler::Draw(wxDC& dc, const Envelope* envelope) const
444{
445 if(mRulerStruct.mLength <=0 )
446 return;
447
448 UpdateCache( dc, envelope );
449 auto &cache = *mpCache;
450
451 dc.SetTextForeground( mTickColour );
452#ifdef EXPERIMENTAL_THEMING
453 dc.SetPen(mPen);
454#else
455 dc.SetPen(*wxBLACK_PEN);
456#endif
457
458 // Draws a long line the length of the ruler.
459 if( !mbTicksOnly )
460 {
461 if (mRulerStruct.mOrientation == wxHORIZONTAL) {
464 else
466 }
467 else {
470 else
471 {
472 // These calculations appear to be wrong, and to never have been used (so not tested) prior to MixerBoard.
473 // AColor::Line(dc, mRect.x-mRect.width, mTop, mRect.x-mRect.width, mBottom);
474 const int nLineX = mRulerStruct.mRight - 1;
475 AColor::Line(dc, nLineX, mRulerStruct.mTop, nLineX, mRulerStruct.mBottom);
476 }
477 }
478 }
479
480 dc.SetFont( mRulerStruct.mpFonts->major );
481
482 // We may want to not show the ticks at the extremes,
483 // though still showing the labels.
484 // This gives a better look when the ruler is on a bevelled
485 // button, since otherwise the tick is drawn on the bevel.
486 int iMaxPos = (mRulerStruct.mOrientation==wxHORIZONTAL)? mRulerStruct.mRight : mRulerStruct.mBottom-5;
487
488 auto drawLabel = [this, iMaxPos, &dc]( const RulerUpdater::Label &label, int length ){
489 int pos = label.pos;
490
491 if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos)))
492 {
493 if (mRulerStruct.mOrientation == wxHORIZONTAL) {
496 mRulerStruct.mLeft + pos, mRulerStruct.mTop + length);
497 else
500 }
501 else {
504 mRulerStruct.mLeft + length, mRulerStruct.mTop + pos);
505 else
508 }
509 }
510
512 };
513
514 for( const auto &label : cache.mMajorLabels )
515 drawLabel( label, mTickLengths.majorLength );
516
517 if( mbMinor ) {
518 dc.SetFont( mRulerStruct.mpFonts->minor );
519 for( const auto &label : cache.mMinorLabels )
520 drawLabel( label, mTickLengths.minorLength );
521 }
522
523 dc.SetFont( mRulerStruct.mpFonts->minorMinor );
524
525 for( const auto &label : cache.mMinorMinorLabels )
526 if ( label.text )
527 drawLabel( label, mTickLengths.minorMinorLength );
528}
529
530// ********** Draw grid ***************************
531void Ruler::DrawGrid(wxDC& dc,
532 const int gridLineLength,
533 const bool minorGrid, const bool majorGrid, int xOffset, int yOffset)
534 const
535{
536 UpdateCache( dc, nullptr );
537 auto &cache = *mpCache;
538
539 int gridPos;
540 wxPen gridPen;
541
542 if(mbMinor && (minorGrid && (gridLineLength != 0 ))) {
543 gridPen.SetColour(178, 178, 178); // very light grey
544 dc.SetPen(gridPen);
545 for( const auto &label : cache.mMinorLabels ) {
546 gridPos = label.pos;
547 if(mRulerStruct.mOrientation == wxHORIZONTAL) {
548 if((gridPos != 0) && (gridPos != gridLineLength))
549 AColor::Line(dc, gridPos+xOffset, yOffset, gridPos+xOffset, gridLineLength-1+yOffset);
550 }
551 else {
552 if((gridPos != 0) && (gridPos != gridLineLength))
553 AColor::Line(dc, xOffset, gridPos+yOffset, gridLineLength-1+xOffset, gridPos+yOffset);
554 }
555 }
556 }
557
558 if(majorGrid && (gridLineLength != 0 )) {
559 gridPen.SetColour(127, 127, 127); // light grey
560 dc.SetPen(gridPen);
561 for( const auto &label : cache.mMajorLabels ) {
562 gridPos = label.pos;
563 if(mRulerStruct.mOrientation == wxHORIZONTAL) {
564 if((gridPos != 0) && (gridPos != gridLineLength))
565 AColor::Line(dc, gridPos+xOffset, yOffset, gridPos+xOffset, gridLineLength-1+yOffset);
566 }
567 else {
568 if((gridPos != 0) && (gridPos != gridLineLength))
569 AColor::Line(dc, xOffset, gridPos+yOffset, gridLineLength-1+xOffset, gridPos+yOffset);
570 }
571 }
572
573 int zeroPosition = GetZeroPosition();
574 if(zeroPosition > 0) {
575 // Draw 'zero' grid line in black
576 dc.SetPen(*wxBLACK_PEN);
577 if(mRulerStruct.mOrientation == wxHORIZONTAL) {
578 if(zeroPosition != gridLineLength)
579 AColor::Line(dc, zeroPosition+xOffset, yOffset, zeroPosition+xOffset, gridLineLength-1+yOffset);
580 }
581 else {
582 if(zeroPosition != gridLineLength)
583 AColor::Line(dc, xOffset, zeroPosition+yOffset, gridLineLength-1+xOffset, zeroPosition+yOffset);
584 }
585 }
586 }
587}
588
589int Ruler::FindZero( const RulerUpdater::Labels &labels ) const
590{
591 auto begin = labels.begin(), end = labels.end(),
592 iter = std::find_if( begin, end, []( const RulerUpdater::Label &label ){
593 return label.value == 0.0;
594 } );
595
596 if ( iter == end )
597 return -1;
598 else
599 return iter->pos;
600}
601
603{
604 wxASSERT( mpCache );
605 auto &cache = *mpCache;
606 int zero;
607 if( (zero = FindZero( cache.mMajorLabels ) ) < 0)
608 zero = FindZero( cache.mMinorLabels );
609 // PRL: don't consult minor minor??
610 return zero;
611}
612
613void Ruler::GetMaxSize(wxCoord *width, wxCoord *height)
614{
615 if ( !mpCache ) {
616 wxScreenDC sdc;
617 UpdateCache( sdc, nullptr );
618 }
619
620 auto &cache = *mpCache;
621 if (width)
622 *width = cache.mRect.GetWidth();
623
624 if (height)
625 *height = cache.mRect.GetHeight();
626}
wxT("CloseDown"))
int min(int a, int b)
int format
Definition: ExportPCM.cpp:53
static constexpr int MaxPixelHeight
Definition: Ruler.cpp:341
static constexpr int MinPixelHeight
Definition: Ruler.cpp:334
TranslatableString label
Definition: TagsEditor.cpp:164
THEME_API Theme theTheme
Definition: Theme.cpp:82
static CustomUpdaterValue updater
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:187
Piecewise linear or piecewise exponential function from double to double.
Definition: Envelope.h:72
wxColour mTickColour
Definition: Ruler.h:150
RulerStruct mRulerStruct
Definition: Ruler.h:147
void OfflimitsPixels(int start, int end)
Definition: Ruler.cpp:273
RulerStruct::Fonts GetFonts() const
Definition: Ruler.cpp:428
wxPen mPen
Definition: Ruler.h:152
std::unique_ptr< Cache > mpCache
Definition: Ruler.h:160
void SetFlip(bool flip)
Definition: Ruler.cpp:191
int FindZero(const RulerUpdater::Labels &labels) const
Definition: Ruler.cpp:589
RulerUpdater::Bits mUserBits
Definition: Ruler.h:158
void UpdateCache(wxDC &dc, const Envelope *envelope) const
Definition: Ruler.cpp:390
TickLengths mTickLengths
Definition: Ruler.h:148
void DrawGrid(wxDC &dc, int length, bool minor=true, bool major=true, int xOffset=0, int yOffset=0) const
Definition: Ruler.cpp:531
Ruler(const RulerUpdater &updater, const RulerFormat &format)
Definition: Ruler.cpp:66
bool mbTicksOnly
Definition: Ruler.h:142
const RulerUpdater * mpUpdater
Definition: Ruler.h:156
void SetUpdater(const RulerUpdater *pUpdater)
Definition: Ruler.cpp:111
std::unique_ptr< RulerStruct::Fonts > mpUserFonts
Definition: Ruler.h:154
void SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont)
Definition: Ruler.cpp:230
void ChooseFonts(wxDC &dc) const
Definition: Ruler.cpp:351
void SetNumberScale(const NumberScale &scale)
Definition: Ruler.cpp:246
void SetOrientation(int orient)
Definition: Ruler.cpp:140
void SetFormat(const RulerFormat *pFormat)
Definition: Ruler.cpp:103
void Draw(wxDC &dc) const
Definition: Ruler.cpp:438
void SetTickLengths(const TickLengths &tLengths)
Definition: Ruler.cpp:254
void SetLabelEdges(bool labelEdges)
Definition: Ruler.cpp:178
bool mbTicksAtExtremes
Definition: Ruler.h:143
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:613
int GetZeroPosition() const
Definition: Ruler.cpp:602
void SetBounds(int left, int top, int right, int bottom)
Definition: Ruler.cpp:303
void SetUnits(const TranslatableString &units)
Definition: Ruler.cpp:119
void SetRange(double min, double max)
Definition: Ruler.cpp:151
void SetDbMirrorValue(const double d)
Definition: Ruler.cpp:131
~Ruler()
Definition: Ruler.cpp:93
bool mbMinor
Definition: Ruler.h:168
void Invalidate()
Definition: Ruler.cpp:316
void SetTwoTone(bool twoTone)
Definition: Ruler.cpp:98
bool mTwoTone
Definition: Ruler.h:169
void SetMinor(bool value)
Definition: Ruler.cpp:204
Used to update a Ruler.
Definition: RulerUpdater.h:58
std::vector< bool > Bits
Definition: RulerUpdater.h:76
std::vector< Label > Labels
Definition: RulerUpdater.h:74
wxColour & Colour(int iIndex)
Holds a msgid for the translation catalog; may also bind format arguments.
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
std::optional< LogWindowUpdater > pUpdater
Definition: LogWindow.cpp:53
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:752
void FindFontHeights(wxCoord &height, wxCoord &lead, wxDC &dc, int fontSize, wxFontWeight weight=wxFONTWEIGHT_NORMAL)
Definition: Ruler.cpp:221
wxRect mRect
Definition: Ruler.cpp:331
RulerUpdater::Bits mBits
Definition: Ruler.cpp:329
RulerUpdater::Labels mMinorLabels
Definition: Ruler.cpp:330
RulerUpdater::Labels mMinorMinorLabels
Definition: Ruler.cpp:330
RulerUpdater::Labels mMajorLabels
Definition: Ruler.cpp:330
int minorMinorLength
Definition: Ruler.h:32
std::unique_ptr< Fonts > mpFonts
Definition: RulerUpdater.h:52
double mMax
Definition: RulerUpdater.h:34
double mHiddenMax
Definition: RulerUpdater.h:35
TranslatableString mUnits
Definition: RulerUpdater.h:53
double mDbMirrorValue
Definition: RulerUpdater.h:49
int mOrientation
Definition: RulerUpdater.h:37
NumberScale mNumberScale
Definition: RulerUpdater.h:55
bool mLabelEdges
Definition: RulerUpdater.h:39
const RulerFormat * mpRulerFormat
Definition: RulerUpdater.h:41
double mMin
Definition: RulerUpdater.h:32
double mHiddenMin
Definition: RulerUpdater.h:33
An array of these created by the Updater is used to determine what and where text annotations to the ...
Definition: RulerUpdater.h:60