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