Audacity  2.3.1
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 *******************************************************************//***************************************************************//***************************************************************//******************************************************************/
56 
57 #include "../Audacity.h"
58 #include "Ruler.h"
59 
60 
61 #include "../AllThemeResources.h"
62 #include "../NumberScale.h"
63 #include "../Theme.h"
64 #include "../TimeTrack.h"
65 #include "../ViewInfo.h"
66 
67 using std::min;
68 using std::max;
69 
70 //wxColour Ruler::mTickColour{ 153, 153, 153 };
71 
72 //
73 // Ruler
74 //
75 
77  : mpNumberScale{}
78 {
79  mMin = mHiddenMin = 0.0;
80  mMax = mHiddenMax = 100.0;
81  mOrientation = wxHORIZONTAL;
82  mSpacing = 6;
83  mHasSetSpacing = false;
84  mFormat = RealFormat;
85  mFlip = false;
86  mLog = false;
87  mLabelEdges = false;
88  mUnits = wxT("");
89 
90  mLeft = -1;
91  mTop = -1;
92  mRight = -1;
93  mBottom = -1;
94  mbTicksOnly = true;
95  mbTicksAtExtremes = false;
96  mTickColour = wxColour( theTheme.Colour( clrTrackPanelText ));
97  mPen.SetColour(mTickColour);
98  mDbMirrorValue = 0.0;
99 
100  // Note: the font size is now adjusted automatically whenever
101  // Invalidate is called on a horizontal Ruler, unless the user
102  // calls SetFonts manually. So the defaults here are not used
103  // often.
104 
105  int fontSize = 10;
106 #ifdef __WXMSW__
107  fontSize = 8;
108 #endif
109 
110  mMinorMinorFont = std::make_unique<wxFont>(fontSize - 1, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
111  mMinorFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
112  mMajorFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
113 
114  mUserFonts = false;
115 
116  mLengthOld = 0;
117  mLength = 0;
118  mUserBitLen = 0;
119 
120  mValid = false;
121 
122  mCustom = false;
123  mbMinor = true;
124 
125  mGridLineLength = 0;
126  mMajorGrid = false;
127  mMinorGrid = false;
128 
129  mTwoTone = false;
130 
131  mUseZoomInfo = NULL;
132 }
133 
135 {
136  Invalidate(); // frees up our arrays
137 }
138 
139 void Ruler::SetTwoTone(bool twoTone)
140 {
141  mTwoTone = twoTone;
142 }
143 
145 {
146  // IntFormat, RealFormat, RealLogFormat, TimeFormat, or LinearDBFormat
147 
148  if (mFormat != format) {
149  mFormat = format;
150 
151  Invalidate();
152  }
153 }
154 
155 void Ruler::SetLog(bool log)
156 {
157  // Logarithmic
158 
159  if (mLog != log) {
160  mLog = log;
161 
162  Invalidate();
163  }
164 }
165 
166 void Ruler::SetUnits(const wxString &units)
167 {
168  // Specify the name of the units (like "dB") if you
169  // want numbers like "1.6" formatted as "1.6 dB".
170 
171  if (mUnits != units) {
172  mUnits = units;
173 
174  Invalidate();
175  }
176 }
177 
178 void Ruler::SetOrientation(int orient)
179 {
180  // wxHORIZONTAL || wxVERTICAL
181 
182  if (mOrientation != orient) {
183  mOrientation = orient;
184 
185  if (mOrientation == wxVERTICAL && !mHasSetSpacing)
186  mSpacing = 2;
187 
188  Invalidate();
189  }
190 }
191 
192 void Ruler::SetRange(double min, double max)
193 {
194  SetRange(min, max, min, max);
195 }
196 
197 void Ruler::SetRange
198  (double min, double max, double hiddenMin, double hiddenMax)
199 {
200  // For a horizontal ruler,
201  // min is the value in the center of pixel "left",
202  // max is the value in the center of pixel "right".
203 
204  // In the special case of a time ruler,
205  // hiddenMin and hiddenMax are values that would be shown with the fisheye
206  // turned off. In other cases they equal min and max respectively.
207 
208  if (mMin != min || mMax != max ||
209  mHiddenMin != hiddenMin || mHiddenMax != hiddenMax) {
210  mMin = min;
211  mMax = max;
212  mHiddenMin = hiddenMin;
213  mHiddenMax = hiddenMax;
214 
215  Invalidate();
216  }
217 }
218 
219 void Ruler::SetSpacing(int spacing)
220 {
221  mHasSetSpacing = true;
222 
223  if (mSpacing != spacing) {
224  mSpacing = spacing;
225 
226  Invalidate();
227  }
228 }
229 
230 void Ruler::SetLabelEdges(bool labelEdges)
231 {
232  // If this is true, the edges of the ruler will always
233  // receive a label. If not, the nearest round number is
234  // labeled (which may or may not be the edge).
235 
236  if (mLabelEdges != labelEdges) {
237  mLabelEdges = labelEdges;
238 
239  Invalidate();
240  }
241 }
242 
243 void Ruler::SetFlip(bool flip)
244 {
245  // If this is true, the orientation of the tick marks
246  // is reversed from the default; eg. above the line
247  // instead of below
248 
249  if (mFlip != flip) {
250  mFlip = flip;
251 
252  Invalidate();
253  }
254 }
255 
256 void Ruler::SetMinor(bool value)
257 {
258  mbMinor = value;
259 }
260 
261 void Ruler::SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont)
262 {
263  *mMinorMinorFont = minorMinorFont;
264  *mMinorFont = minorFont;
265  *mMajorFont = majorFont;
266 
267  // Won't override these fonts
268  mUserFonts = true;
269 
270  Invalidate();
271 }
272 
274 {
275  if (!pScale) {
276  if (mpNumberScale) {
277  mpNumberScale.reset();
278  Invalidate();
279  }
280  }
281  else {
282  if (!mpNumberScale || *mpNumberScale != *pScale) {
283  mpNumberScale = std::make_unique<NumberScale>(*pScale);
284  Invalidate();
285  }
286  }
287 }
288 
289 void Ruler::OfflimitsPixels(int start, int end)
290 {
291  if (!mUserBits) {
292  if (mOrientation == wxHORIZONTAL)
293  mLength = mRight-mLeft;
294  else
295  mLength = mBottom-mTop;
296  if( mLength < 0 )
297  return;
298  mUserBits.reinit(static_cast<size_t>(mLength+1), true);
299  mUserBitLen = mLength+1;
300  }
301 
302  if (end < start)
303  std::swap( start, end );
304 
305  if (start < 0)
306  start = 0;
307  if (end > mLength)
308  end = mLength;
309 
310  for(int i = start; i <= end; i++)
311  mUserBits[i] = 1;
312 }
313 
314 void Ruler::SetBounds(int left, int top, int right, int bottom)
315 {
316  if (mLeft != left || mTop != top ||
317  mRight != right || mBottom != bottom) {
318  mLeft = left;
319  mTop = top;
320  mRight = right;
321  mBottom = bottom;
322 
323  Invalidate();
324  }
325 }
326 
328 {
329  mValid = false;
330 
331  if (mOrientation == wxHORIZONTAL)
332  mLength = mRight-mLeft;
333  else
334  mLength = mBottom-mTop;
335 
336  mBits.reset();
337  if (mUserBits && mLength+1 != mUserBitLen) {
338  mUserBits.reset();
339  mUserBitLen = 0;
340  }
341 }
342 
344 {
345  // Given the dimensions of the ruler, the range of values it
346  // has to display, and the format (i.e. Int, Real, Time),
347  // figure out how many units are in one Minor tick, and
348  // in one Major tick.
349  //
350  // The goal is to always put tick marks on nice round numbers
351  // that are easy for humans to grok. This is the most tricky
352  // with time.
353 
354  double d;
355 
356  // As a heuristic, we want at least 22 pixels between each
357  // minor tick. We want to show numbers like "-48"
358  // in that space.
359  // If vertical, we don't need as much space.
360  double units = ((mOrientation == wxHORIZONTAL) ? 22 : 16) * fabs(UPP);
361 
362  mDigits = 0;
363 
364  switch(mFormat) {
365  case LinearDBFormat:
366  if (units < 0.001) {
367  mMinor = 0.001;
368  mMajor = 0.005;
369  return;
370  }
371  if (units < 0.01) {
372  mMinor = 0.01;
373  mMajor = 0.05;
374  return;
375  }
376  if (units < 0.1) {
377  mMinor = 0.1;
378  mMajor = 0.5;
379  return;
380  }
381  if (units < 1.0) {
382  mMinor = 1.0;
383  mMajor = 6.0;
384  return;
385  }
386  if (units < 3.0) {
387  mMinor = 3.0;
388  mMajor = 12.0;
389  return;
390  }
391  if (units < 6.0) {
392  mMinor = 6.0;
393  mMajor = 24.0;
394  return;
395  }
396  if (units < 12.0) {
397  mMinor = 12.0;
398  mMajor = 48.0;
399  return;
400  }
401  if (units < 24.0) {
402  mMinor = 24.0;
403  mMajor = 96.0;
404  return;
405  }
406  d = 20.0;
407  for(;;) {
408  if (units < d) {
409  mMinor = d;
410  mMajor = d*5.0;
411  return;
412  }
413  d *= 5.0;
414  if (units < d) {
415  mMinor = d;
416  mMajor = d*5.0;
417  return;
418  }
419  d *= 2.0;
420  }
421  break;
422 
423  case IntFormat:
424  d = 1.0;
425  for(;;) {
426  if (units < d) {
427  mMinor = d;
428  mMajor = d*5.0;
429  return;
430  }
431  d *= 5.0;
432  if (units < d) {
433  mMinor = d;
434  mMajor = d*2.0;
435  return;
436  }
437  d *= 2.0;
438  }
439  break;
440 
441  case TimeFormat:
442  if (units > 0.5) {
443  if (units < 1.0) { // 1 sec
444  mMinor = 1.0;
445  mMajor = 5.0;
446  return;
447  }
448  if (units < 5.0) { // 5 sec
449  mMinor = 5.0;
450  mMajor = 15.0;
451  return;
452  }
453  if (units < 10.0) {
454  mMinor = 10.0;
455  mMajor = 30.0;
456  return;
457  }
458  if (units < 15.0) {
459  mMinor = 15.0;
460  mMajor = 60.0;
461  return;
462  }
463  if (units < 30.0) {
464  mMinor = 30.0;
465  mMajor = 60.0;
466  return;
467  }
468  if (units < 60.0) { // 1 min
469  mMinor = 60.0;
470  mMajor = 300.0;
471  return;
472  }
473  if (units < 300.0) { // 5 min
474  mMinor = 300.0;
475  mMajor = 900.0;
476  return;
477  }
478  if (units < 600.0) { // 10 min
479  mMinor = 600.0;
480  mMajor = 1800.0;
481  return;
482  }
483  if (units < 900.0) { // 15 min
484  mMinor = 900.0;
485  mMajor = 3600.0;
486  return;
487  }
488  if (units < 1800.0) { // 30 min
489  mMinor = 1800.0;
490  mMajor = 3600.0;
491  return;
492  }
493  if (units < 3600.0) { // 1 hr
494  mMinor = 3600.0;
495  mMajor = 6*3600.0;
496  return;
497  }
498  if (units < 6*3600.0) { // 6 hrs
499  mMinor = 6*3600.0;
500  mMajor = 24*3600.0;
501  return;
502  }
503  if (units < 24*3600.0) { // 1 day
504  mMinor = 24*3600.0;
505  mMajor = 7*24*3600.0;
506  return;
507  }
508 
509  mMinor = 24.0 * 7.0 * 3600.0; // 1 week
510  mMajor = 24.0 * 7.0 * 3600.0;
511  }
512 
513  // Otherwise fall through to RealFormat
514  // (fractions of a second should be dealt with
515  // the same way as for RealFormat)
516 
517  case RealFormat:
518  d = 0.000001;
519  // mDigits is number of digits after the decimal point.
520  mDigits = 6;
521  for(;;) {
522  if (units < d) {
523  mMinor = d;
524  mMajor = d*5.0;
525  return;
526  }
527  d *= 5.0;
528  if (units < d) {
529  mMinor = d;
530  mMajor = d*2.0;
531  return;
532  }
533  d *= 2.0;
534  mDigits--;
535  // More than 10 digit numbers? Something is badly wrong.
536  // Probably units is coming in with too high a value.
537  wxASSERT( mDigits >= -10 );
538  if( mDigits < -10 )
539  break;
540  }
541  mMinor = d;
542  mMajor = d * 2.0;
543  break;
544 
545  case RealLogFormat:
546  d = 0.000001;
547  // mDigits is number of digits after the decimal point.
548  mDigits = 6;
549  for(;;) {
550  if (units < d) {
551  mMinor = d;
552  mMajor = d*5.0;
553  return;
554  }
555  d *= 5.0;
556  if (units < d) {
557  mMinor = d;
558  mMajor = d*2.0;
559  return;
560  }
561  d *= 2.0;
562  mDigits--;
563  // More than 10 digit numbers? Something is badly wrong.
564  // Probably units is coming in with too high a value.
565  wxASSERT( mDigits >= -10 );
566  if( mDigits < -10 )
567  break;
568  }
569  mDigits++;
570  mMinor = d;
571  mMajor = d * 2.0;
572  break;
573  }
574 }
575 
576 wxString Ruler::LabelString(double d, bool major)
577 {
578  // Given a value, turn it into a string according
579  // to the current ruler format. The number of digits of
580  // accuracy depends on the resolution of the ruler,
581  // i.e. how far zoomed in or out you are.
582 
583  wxString s;
584 
585  // Replace -0 with 0
586  if (d < 0.0 && (d+mMinor > 0.0) && ( mFormat != RealLogFormat ))
587  d = 0.0;
588 
589  switch(mFormat) {
590  case IntFormat:
591  s.Printf(wxT("%d"), (int)floor(d+0.5));
592  break;
593  case LinearDBFormat:
594  if (mMinor >= 1.0)
595  s.Printf(wxT("%d"), (int)floor(d+0.5));
596  else {
597  int precision = -log10(mMinor);
598  s.Printf(wxT("%.*f"), precision, d);
599  }
600  break;
601  case RealFormat:
602  if (mMinor >= 1.0)
603  s.Printf(wxT("%d"), (int)floor(d+0.5));
604  else {
605  s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d);
606  }
607  break;
608  case RealLogFormat:
609  if (mMinor >= 1.0)
610  s.Printf(wxT("%d"), (int)floor(d+0.5));
611  else {
612  s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d);
613  }
614  break;
615  case TimeFormat:
616  if (major) {
617  if (d < 0) {
618  s = wxT("-");
619  d = -d;
620  }
621 
622  #if ALWAYS_HH_MM_SS
623  int secs = (int)(d + 0.5);
624  if (mMinor >= 1.0) {
625  s.Printf(wxT("%d:%02d:%02d"), secs/3600, (secs/60)%60, secs%60);
626  }
627  else {
628  wxString t1, t2, format;
629  t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60);
630  format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits);
631  t2.Printf(format, fmod(d, 60.0));
632  s += t1 + t2;
633  }
634  break;
635  #endif
636 
637  if (mMinor >= 3600.0) {
638  int hrs = (int)(d / 3600.0 + 0.5);
639  wxString h;
640  h.Printf(wxT("%d:00:00"), hrs);
641  s += h;
642  }
643  else if (mMinor >= 60.0) {
644  int minutes = (int)(d / 60.0 + 0.5);
645  wxString m;
646  if (minutes >= 60)
647  m.Printf(wxT("%d:%02d:00"), minutes/60, minutes%60);
648  else
649  m.Printf(wxT("%d:00"), minutes);
650  s += m;
651  }
652  else if (mMinor >= 1.0) {
653  int secs = (int)(d + 0.5);
654  wxString t;
655  if (secs >= 3600)
656  t.Printf(wxT("%d:%02d:%02d"), secs/3600, (secs/60)%60, secs%60);
657  else if (secs >= 60)
658  t.Printf(wxT("%d:%02d"), secs/60, secs%60);
659  else
660  t.Printf(wxT("%d"), secs);
661  s += t;
662  }
663  else {
664 // Commented out old and incorrect code for avoiding the 40mins and 60 seconds problem
665 // It was causing Bug 463 - Incorrect Timeline numbering (where at high zoom and long tracks,
666 // numbers did not change.
667 #if 0
668  // The casting to float is working around an issue where 59 seconds
669  // would show up as 60 when using g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3.
670  int secs = (int)(float)(d);
671  wxString t1, t2, format;
672 
673  if (secs >= 3600)
674  t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60);
675  else if (secs >= 60)
676  t1.Printf(wxT("%d:"), secs/60);
677 
678  if (secs >= 60)
679  format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits);
680  else
681  format.Printf(wxT("%%%d.%dlf"), mDigits+3, mDigits);
682  // The casting to float is working around an issue where 59 seconds
683  // would show up as 60 when using g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3.
684  t2.Printf(format, fmod((float)d, (float)60.0));
685 #else
686  // For d in the range of hours, d is just very slightly below the value it should
687  // have, because of using a double, which in turn yields values like 59:59:999999
688  // mins:secs:nanosecs when we want 1:00:00:000000
689  // so adjust by less than a nano second per hour to get nicer number formatting.
690  double dd = d * 1.000000000000001;
691  int secs = (int)(dd);
692  wxString t1, t2, format;
693 
694  if (secs >= 3600)
695  t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60);
696  else if (secs >= 60)
697  t1.Printf(wxT("%d:"), secs/60);
698 
699  if (secs >= 60)
700  format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits);
701  else
702  format.Printf(wxT("%%%d.%dlf"), mDigits+3, mDigits);
703  // dd will be reduced to just the seconds and fractional part.
704  dd = dd - secs + (secs%60);
705  // truncate to appropriate number of digits, so that the print formatting
706  // doesn't round up 59.9999999 to 60.
707  double multiplier = pow( 10, mDigits);
708  dd = ((int)(dd * multiplier))/multiplier;
709  t2.Printf(format, dd);
710 #endif
711  s += t1 + t2;
712  }
713  }
714  else {
715  }
716  }
717 
718  if (mUnits != wxT(""))
719  s = (s + mUnits);
720 
721  return s;
722 }
723 
724 void Ruler::Tick(int pos, double d, bool major, bool minor)
725 {
726  wxString l;
727  wxCoord strW, strH, strD, strL;
728  int strPos, strLen, strLeft, strTop;
729 
730  // FIXME: We don't draw a tick if off end of our label arrays
731  // But we shouldn't have an array of labels.
732  if( mNumMinorMinor >= mLength )
733  return;
734  if( mNumMinor >= mLength )
735  return;
736  if( mNumMajor >= mLength )
737  return;
738 
739  Label *label;
740  if (major)
741  label = &mMajorLabels[mNumMajor++];
742  else if (minor)
743  label = &mMinorLabels[mNumMinor++];
744  else
745  label = &mMinorMinorLabels[mNumMinorMinor++];
746 
747  label->value = d;
748  label->pos = pos;
749  label->lx = mLeft - 1000; // don't display
750  label->ly = mTop - 1000; // don't display
751  label->text = wxT("");
752 
753  mDC->SetFont(major? *mMajorFont: minor? *mMinorFont : *mMinorMinorFont);
754  // Bug 521. dB view for waveforms needs a 2-sided scale.
755  if(( mDbMirrorValue > 1.0 ) && ( -d > mDbMirrorValue ))
756  d = -2*mDbMirrorValue - d;
757  l = LabelString(d, major);
758  mDC->GetTextExtent(l, &strW, &strH, &strD, &strL);
759 
760  if (mOrientation == wxHORIZONTAL) {
761  strLen = strW;
762  strPos = pos - strW/2;
763  if (strPos < 0)
764  strPos = 0;
765  if (strPos + strW >= mLength)
766  strPos = mLength - strW;
767  strLeft = mLeft + strPos;
768  if (mFlip) {
769  strTop = mTop + 4;
770  mMaxHeight = max(mMaxHeight, strH + 4);
771  }
772  else {
773  strTop =-strH-mLead;
774  mMaxHeight = max(mMaxHeight, strH + 6);
775  }
776  }
777  else {
778  strLen = strH;
779  strPos = pos - strH/2;
780  if (strPos < 0)
781  strPos = 0;
782  if (strPos + strH >= mLength)
783  strPos = mLength - strH;
784  strTop = mTop + strPos;
785  if (mFlip) {
786  strLeft = mLeft + 5;
787  mMaxWidth = max(mMaxWidth, strW + 5);
788  }
789  else
790  strLeft =-strW-6;
791  }
792 
793 
794  // FIXME: we shouldn't even get here if strPos < 0.
795  // Ruler code currently does not handle very small or
796  // negative sized windows (i.e. don't draw) properly.
797  if( strPos < 0 )
798  return;
799 
800  // See if any of the pixels we need to draw this
801  // label is already covered
802 
803  int i;
804  for(i=0; i<strLen; i++)
805  if (mBits[strPos+i])
806  return;
807 
808  // If not, position the label and give it text
809 
810  label->lx = strLeft;
811  label->ly = strTop;
812  label->text = l;
813 
814  // And mark these pixels, plus some surrounding
815  // ones (the spacing between labels), as covered
816  int leftMargin = mSpacing;
817  if (strPos < leftMargin)
818  leftMargin = strPos;
819  strPos -= leftMargin;
820  strLen += leftMargin;
821 
822  int rightMargin = mSpacing;
823  if (strPos + strLen > mLength - mSpacing)
824  rightMargin = mLength - strPos - strLen;
825  strLen += rightMargin;
826 
827  for(i=0; i<strLen; i++)
828  mBits[strPos+i] = 1;
829 
830  wxRect r(strLeft, strTop, strW, strH);
831  mRect.Union(r);
832 
833 }
834 
835 void Ruler::TickCustom(int labelIdx, bool major, bool minor)
836 {
837  //This should only used in the mCustom case
838  // Many code comes from 'Tick' method: this should
839  // be optimized.
840 
841  int pos;
842  wxString l;
843  wxCoord strW, strH, strD, strL;
844  int strPos, strLen, strLeft, strTop;
845 
846  // FIXME: We don't draw a tick if of end of our label arrays
847  // But we shouldn't have an array of labels.
848  if( mNumMinor >= mLength )
849  return;
850  if( mNumMajor >= mLength )
851  return;
852 
853  Label *label;
854  if (major)
855  label = &mMajorLabels[labelIdx];
856  else if (minor)
857  label = &mMinorLabels[labelIdx];
858  else
859  label = &mMinorMinorLabels[labelIdx];
860 
861  label->value = 0.0;
862  pos = label->pos; // already stored in label class
863  l = label->text;
864  label->lx = mLeft - 1000; // don't display
865  label->ly = mTop - 1000; // don't display
866 
867  mDC->SetFont(major? *mMajorFont: minor? *mMinorFont : *mMinorMinorFont);
868 
869  mDC->GetTextExtent(l, &strW, &strH, &strD, &strL);
870 
871  if (mOrientation == wxHORIZONTAL) {
872  strLen = strW;
873  strPos = pos - strW/2;
874  if (strPos < 0)
875  strPos = 0;
876  if (strPos + strW >= mLength)
877  strPos = mLength - strW;
878  strLeft = mLeft + strPos;
879  if (mFlip) {
880  strTop = mTop + 4;
881  mMaxHeight = max(mMaxHeight, strH + 4);
882  }
883  else {
884 
885  strTop = mTop- mLead+4;// More space was needed...
886  mMaxHeight = max(mMaxHeight, strH + 6);
887  }
888  }
889  else {
890  strLen = strH;
891  strPos = pos - strH/2;
892  if (strPos < 0)
893  strPos = 0;
894  if (strPos + strH >= mLength)
895  strPos = mLength - strH;
896  strTop = mTop + strPos;
897  if (mFlip) {
898  strLeft = mLeft + 5;
899  mMaxWidth = max(mMaxWidth, strW + 5);
900  }
901  else {
902 
903  strLeft =-strW-6;
904  }
905  }
906 
907 
908  // FIXME: we shouldn't even get here if strPos < 0.
909  // Ruler code currently does not handle very small or
910  // negative sized windows (i.e. don't draw) properly.
911  if( strPos < 0 )
912  return;
913 
914  // See if any of the pixels we need to draw this
915  // label is already covered
916 
917  int i;
918  for(i=0; i<strLen; i++)
919  if (mBits[strPos+i])
920  return;
921 
922  // If not, position the label
923 
924  label->lx = strLeft;
925  label->ly = strTop;
926 
927  // And mark these pixels, plus some surrounding
928  // ones (the spacing between labels), as covered
929  int leftMargin = mSpacing;
930  if (strPos < leftMargin)
931  leftMargin = strPos;
932  strPos -= leftMargin;
933  strLen += leftMargin;
934 
935  int rightMargin = mSpacing;
936  if (strPos + strLen > mLength - mSpacing)
937  rightMargin = mLength - strPos - strLen;
938  strLen += rightMargin;
939 
940  for(i=0; i<strLen; i++)
941  mBits[strPos+i] = 1;
942 
943 
944  wxRect r(strLeft, strTop, strW, strH);
945  mRect.Union(r);
946 
947 }
948 
950 {
951  Update(NULL);
952 }
953 
954 void Ruler::Update(const TimeTrack* timetrack)// Envelope *speedEnv, long minSpeed, long maxSpeed )
955 {
956  const ZoomInfo *zoomInfo = NULL;
957  if (!mLog && mOrientation == wxHORIZONTAL)
958  zoomInfo = mUseZoomInfo;
959 
960  // This gets called when something has been changed
961  // (i.e. we've been invalidated). Recompute all
962  // tick positions and font size.
963 
964  int i;
965  int j;
966 
967  if (!mUserFonts) {
968  int fontSize = 4;
969  wxCoord strW, strH, strD, strL;
970  wxString exampleText = wxT("0.9"); //ignored for height calcs on all platforms
971  int desiredPixelHeight;
972 
973 
974  static const int MinPixelHeight = 10; // 8;
975  static const int MaxPixelHeight =
976 #ifdef __WXMAC__
977  10
978 #else
979  12
980 #endif
981  ;
982 
983  if (mOrientation == wxHORIZONTAL)
984  desiredPixelHeight = mBottom - mTop - 5; // height less ticks and 1px gap
985  else
986  desiredPixelHeight = MaxPixelHeight;
987 
988  desiredPixelHeight =
989  std::max(MinPixelHeight, std::min(MaxPixelHeight,
990  desiredPixelHeight));
991 
992  // Keep making the font bigger until it's too big, then subtract one.
993  mDC->SetFont(wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
994  mDC->GetTextExtent(exampleText, &strW, &strH, &strD, &strL);
995  while ((strH - strD - strL) <= desiredPixelHeight && fontSize < 40) {
996  fontSize++;
997  mDC->SetFont(wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
998  mDC->GetTextExtent(exampleText, &strW, &strH, &strD, &strL);
999  }
1000  fontSize--;
1001  mDC->SetFont(wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
1002  mDC->GetTextExtent(exampleText, &strW, &strH, &strD, &strL);
1003  mLead = strL;
1004 
1005  mMajorFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
1006 
1007  mMinorFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1008 
1009  mMinorMinorFont = std::make_unique<wxFont>(fontSize - 1, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1010  }
1011 
1012  // If ruler is being resized, we could end up with it being too small.
1013  // Values of mLength of zero or below cause bad array allocations and
1014  // division by zero. So...
1015  // IF too small THEN bail out and don't draw.
1016  if( mLength <= 0 )
1017  return;
1018 
1019  if (mOrientation == wxHORIZONTAL) {
1020  mMaxWidth = mLength;
1021  mMaxHeight = 0;
1022  mRect = wxRect(0,0, mLength,0);
1023  }
1024  else {
1025  mMaxWidth = 0;
1026  mMaxHeight = mLength;
1027  mRect = wxRect(0,0, 0,mLength);
1028  }
1029 
1030  // FIXME: Surely we do not need to allocate storage for the labels?
1031  // We can just recompute them as we need them? Yes, but only if
1032  // mCustom is false!!!!
1033 
1034  auto size = static_cast<size_t>(mLength + 1);
1035  if(!mCustom) {
1036  mNumMajor = 0;
1037  mNumMinor = 0;
1038  mNumMinorMinor = 0;
1039  if (mLength!=mLengthOld) {
1040  mMajorLabels.reinit(size);
1041  mMinorLabels.reinit(size);
1042  mMinorMinorLabels.reinit(size);
1043  mLengthOld = mLength;
1044  }
1045  }
1046 
1047  mBits.reinit(size);
1048  if (mUserBits)
1049  for(i=0; i<=mLength; i++)
1050  mBits[i] = mUserBits[i];
1051  else
1052  for(i=0; i<=mLength; i++)
1053  mBits[i] = 0;
1054 
1055  // *************** Label calculation routine **************
1056  if(mCustom == true) {
1057 
1058  // SET PARAMETER IN MCUSTOM CASE
1059  // Works only with major labels
1060 
1061  int numLabel = mNumMajor;
1062 
1063  i = 0;
1064  while((i<numLabel) && (i<=mLength)) {
1065 
1066  TickCustom(i, true, false);
1067  i++;
1068  }
1069 
1070  } else if(mLog==false) {
1071 
1072  // Use the "hidden" min and max to determine the tick size.
1073  // That may make a difference with fisheye.
1074  // Otherwise you may see the tick size for the whole ruler change
1075  // when the fisheye approaches start or end.
1076  double UPP = (mHiddenMax-mHiddenMin)/mLength; // Units per pixel
1077  FindLinearTickSizes(UPP);
1078 
1079  // Left and Right Edges
1080  if (mLabelEdges) {
1081  Tick(0, mMin, true, false);
1082  Tick(mLength, mMax, true, false);
1083  }
1084 
1085  // Zero (if it's in the middle somewhere)
1086  if (mMin * mMax < 0.0) {
1087  int mid;
1088  if (zoomInfo != NULL)
1089  mid = (int)(zoomInfo->TimeToPosition(0.0, mLeftOffset));
1090  else
1091  mid = (int)(mLength*(mMin / (mMin - mMax)) + 0.5);
1092  const int iMaxPos = (mOrientation == wxHORIZONTAL) ? mRight : mBottom - 5;
1093  if (mid >= 0 && mid < iMaxPos)
1094  Tick(mid, 0.0, true, false);
1095  }
1096 
1097  double sg = UPP > 0.0? 1.0: -1.0;
1098 
1099  int nDroppedMinorLabels=0;
1100  // Major and minor ticks
1101  for (int jj = 0; jj < 2; ++jj) {
1102  const double denom = jj == 0 ? mMajor : mMinor;
1103  i = -1; j = 0;
1104  double d, warpedD, nextD;
1105 
1106  double prevTime = 0.0, time = 0.0;
1107  if (zoomInfo != NULL) {
1108  j = zoomInfo->TimeToPosition(mMin);
1109  prevTime = zoomInfo->PositionToTime(--j);
1110  time = zoomInfo->PositionToTime(++j);
1111  d = (prevTime + time) / 2.0;
1112  }
1113  else
1114  d = mMin - UPP / 2;
1115  if (timetrack)
1116  warpedD = timetrack->ComputeWarpedLength(0.0, d);
1117  else
1118  warpedD = d;
1119  // using ints doesn't work, as
1120  // this will overflow and be negative at high zoom.
1121  double step = floor(sg * warpedD / denom);
1122  while (i <= mLength) {
1123  i++;
1124  if (zoomInfo)
1125  {
1126  prevTime = time;
1127  time = zoomInfo->PositionToTime(++j);
1128  nextD = (prevTime + time) / 2.0;
1129  // wxASSERT(time >= prevTime);
1130  }
1131  else
1132  nextD = d + UPP;
1133  if (timetrack)
1134  warpedD += timetrack->ComputeWarpedLength(d, nextD);
1135  else
1136  warpedD = nextD;
1137  d = nextD;
1138 
1139  if (floor(sg * warpedD / denom) > step) {
1140  step = floor(sg * warpedD / denom);
1141  bool major = jj == 0;
1142  Tick(i, sg * step * denom, major, !major);
1143  if( !major && mMinorLabels[mNumMinor-1].text.IsEmpty() ){
1144  nDroppedMinorLabels++;
1145  }
1146  }
1147  }
1148  }
1149 
1150  // If we've dropped minor labels through overcrowding, then don't show
1151  // any of them. We're allowed though to drop ones which correspond to the
1152  // major numbers.
1153  if( nDroppedMinorLabels > (mNumMajor+ (mLabelEdges ? 2:0)) ){
1154  // Old code dropped the labels AND their ticks, like so:
1155  // mNumMinor = 0;
1156  // Nowadays we just drop the labels.
1157  for(i=0; i<mNumMinor; i++)
1158  mMinorLabels[i].text = "";
1159  }
1160 
1161  // Left and Right Edges
1162  if (mLabelEdges) {
1163  Tick(0, mMin, true, false);
1164  Tick(mLength, mMax, true, false);
1165  }
1166  }
1167  else {
1168  // log case
1169 
1170  NumberScale numberScale(mpNumberScale
1171  ? *mpNumberScale
1173  );
1174 
1175  mDigits=2; //TODO: implement dynamic digit computation
1176  double loLog = log10(mMin);
1177  double hiLog = log10(mMax);
1178  int loDecade = (int) floor(loLog);
1179 
1180  double val;
1181  double startDecade = pow(10., (double)loDecade);
1182 
1183  // Major ticks are the decades
1184  double decade = startDecade;
1185  double delta=hiLog-loLog, steps=fabs(delta);
1186  double step = delta>=0 ? 10 : 0.1;
1187  double rMin=std::min(mMin, mMax), rMax=std::max(mMin, mMax);
1188  for(i=0; i<=steps; i++)
1189  { // if(i!=0)
1190  { val = decade;
1191  if(val >= rMin && val < rMax) {
1192  const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
1193  Tick(pos, val, true, false);
1194  }
1195  }
1196  decade *= step;
1197  }
1198 
1199  // Minor ticks are multiples of decades
1200  decade = startDecade;
1201  float start, end, mstep;
1202  if (delta > 0)
1203  { start=2; end=10; mstep=1;
1204  }else
1205  { start=9; end=1; mstep=-1;
1206  }
1207  steps++;
1208  for(i=0; i<=steps; i++) {
1209  for(j=start; j!=end; j+=mstep) {
1210  val = decade * j;
1211  if(val >= rMin && val < rMax) {
1212  const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
1213  Tick(pos, val, false, true);
1214  }
1215  }
1216  decade *= step;
1217  }
1218 
1219  // MinorMinor ticks are multiples of decades
1220  decade = startDecade;
1221  if (delta > 0)
1222  { start= 10; end=100; mstep= 1;
1223  }else
1224  { start=100; end= 10; mstep=-1;
1225  }
1226  steps++;
1227  for (i = 0; i <= steps; i++) {
1228  // PRL: Bug1038. Don't label 1.6, rounded, as a duplicate tick for "2"
1229  if (!(mFormat == IntFormat && decade < 10.0)) {
1230  for (int f = start; f != (int)(end); f += mstep) {
1231  if ((int)(f / 10) != f / 10.0f) {
1232  val = decade * f / 10;
1233  if (val >= rMin && val < rMax) {
1234  const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
1235  Tick(pos, val, false, false);
1236  }
1237  }
1238  }
1239  }
1240  decade *= step;
1241  }
1242  }
1243 
1244  int displacementx=0, displacementy=0;
1245  if (!mFlip) {
1246  if (mOrientation==wxHORIZONTAL) {
1247  int d=mTop+mRect.GetHeight()+5;
1248  mRect.Offset(0,d);
1249  mRect.Inflate(0,5);
1250  displacementx=0;
1251  displacementy=d;
1252  }
1253  else {
1254  int d=mLeft-mRect.GetLeft()+5;
1255  mRect.Offset(d,0);
1256  mRect.Inflate(5,0);
1257  displacementx=d;
1258  displacementy=0;
1259  }
1260  }
1261  else {
1262  if (mOrientation==wxHORIZONTAL) {
1263  mRect.Inflate(0,5);
1264  displacementx=0;
1265  displacementy=0;
1266  }
1267  }
1268  for(i=0; i<mNumMajor; i++) {
1269  mMajorLabels[i].lx+= displacementx;
1270  mMajorLabels[i].ly+= displacementy;
1271  }
1272  for(i=0; i<mNumMinor; i++) {
1273  mMinorLabels[i].lx+= displacementx;
1274  mMinorLabels[i].ly+= displacementy;
1275  }
1276  for(i=0; i<mNumMinorMinor; i++) {
1277  mMinorMinorLabels[i].lx+= displacementx;
1278  mMinorMinorLabels[i].ly+= displacementy;
1279  }
1280  mMaxWidth = mRect.GetWidth ();
1281  mMaxHeight= mRect.GetHeight();
1282  mValid = true;
1283 }
1284 
1285 void Ruler::Draw(wxDC& dc)
1286 {
1287  Draw( dc, NULL);
1288 }
1289 
1290 void Ruler::Draw(wxDC& dc, const TimeTrack* timetrack)
1291 {
1292  mDC = &dc;
1293  if( mLength <=0 )
1294  return;
1295 
1296  if (!mValid)
1297  Update(timetrack);
1298 
1299  mDC->SetTextForeground( mTickColour );
1300 #ifdef EXPERIMENTAL_THEMING
1301  mDC->SetPen(mPen);
1302 #else
1303  mDC->SetPen(*wxBLACK_PEN);
1304 #endif
1305 
1306  // Draws a long line the length of the ruler.
1307  if( !mbTicksOnly )
1308  {
1309  if (mOrientation == wxHORIZONTAL) {
1310  if (mFlip)
1311  mDC->DrawLine(mLeft, mTop, mRight+1, mTop);
1312  else
1313  mDC->DrawLine(mLeft, mBottom, mRight+1, mBottom);
1314  }
1315  else {
1316  if (mFlip)
1317  mDC->DrawLine(mLeft, mTop, mLeft, mBottom+1);
1318  else
1319  {
1320  // These calculations appear to be wrong, and to never have been used (so not tested) prior to MixerBoard.
1321  // mDC->DrawLine(mRect.x-mRect.width, mTop, mRect.x-mRect.width, mBottom+1);
1322  const int nLineX = mRight - 1;
1323  mDC->DrawLine(nLineX, mTop, nLineX, mBottom+1);
1324  }
1325  }
1326  }
1327 
1328  int i;
1329 
1330  mDC->SetFont(*mMajorFont);
1331 
1332  // We may want to not show the ticks at the extremes,
1333  // though still showing the labels.
1334  // This gives a better look when the ruler is on a bevelled
1335  // button, since otherwise the tick is drawn on the bevel.
1336  int iMaxPos = (mOrientation==wxHORIZONTAL)? mRight : mBottom-5;
1337 
1338  for(i=0; i<mNumMajor; i++) {
1339  int pos = mMajorLabels[i].pos;
1340 
1341  if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos)))
1342  {
1343  if (mOrientation == wxHORIZONTAL) {
1344  if (mFlip)
1345  mDC->DrawLine(mLeft + pos, mTop,
1346  mLeft + pos, mTop + 4);
1347  else
1348  mDC->DrawLine(mLeft + pos, mBottom - 4,
1349  mLeft + pos, mBottom);
1350  }
1351  else {
1352  if (mFlip)
1353  mDC->DrawLine(mLeft, mTop + pos,
1354  mLeft + 4, mTop + pos);
1355  else
1356  mDC->DrawLine(mRight - 4, mTop + pos,
1357  mRight, mTop + pos);
1358  }
1359  }
1360 
1361  mMajorLabels[i].Draw(*mDC, mTwoTone, mTickColour);
1362  }
1363 
1364  if(mbMinor == true) {
1365  mDC->SetFont(*mMinorFont);
1366  for(i=0; i<mNumMinor; i++) {
1367  int pos = mMinorLabels[i].pos;
1368  if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos)))
1369  {
1370  if (mOrientation == wxHORIZONTAL)
1371  {
1372  if (mFlip)
1373  mDC->DrawLine(mLeft + pos, mTop,
1374  mLeft + pos, mTop + 2);
1375  else
1376  mDC->DrawLine(mLeft + pos, mBottom - 2,
1377  mLeft + pos, mBottom);
1378  }
1379  else
1380  {
1381  if (mFlip)
1382  mDC->DrawLine(mLeft, mTop + pos,
1383  mLeft + 2, mTop + pos);
1384  else
1385  mDC->DrawLine(mRight - 2, mTop + pos,
1386  mRight, mTop + pos);
1387  }
1388  }
1389  mMinorLabels[i].Draw(*mDC, mTwoTone, mTickColour);
1390  }
1391  }
1392 
1393  mDC->SetFont(*mMinorMinorFont);
1394 
1395  for(i=0; i<mNumMinorMinor; i++) {
1396  if (mMinorMinorLabels[i].text != wxT(""))
1397  {
1398  int pos = mMinorMinorLabels[i].pos;
1399 
1400  if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos)))
1401  {
1402  if (mOrientation == wxHORIZONTAL)
1403  {
1404  if (mFlip)
1405  mDC->DrawLine(mLeft + pos, mTop,
1406  mLeft + pos, mTop + 2);
1407  else
1408  mDC->DrawLine(mLeft + pos, mBottom - 2,
1409  mLeft + pos, mBottom);
1410  }
1411  else
1412  {
1413  if (mFlip)
1414  mDC->DrawLine(mLeft, mTop + pos,
1415  mLeft + 2, mTop + pos);
1416  else
1417  mDC->DrawLine(mRight - 2, mTop + pos,
1418  mRight, mTop + pos);
1419  }
1420  }
1422  }
1423  }
1424 }
1425 
1426 // ********** Draw grid ***************************
1427 void Ruler::DrawGrid(wxDC& dc, int length, bool minor, bool major, int xOffset, int yOffset)
1428 {
1429  mGridLineLength = length;
1430  mMajorGrid = major;
1431  mMinorGrid = minor;
1432  mDC = &dc;
1433 
1434  Update();
1435 
1436  int gridPos;
1437  wxPen gridPen;
1438 
1439  if(mbMinor && (mMinorGrid && (mGridLineLength != 0 ))) {
1440  gridPen.SetColour(178, 178, 178); // very light grey
1441  mDC->SetPen(gridPen);
1442  for(int i=0; i<mNumMinor; i++) {
1443  gridPos = mMinorLabels[i].pos;
1444  if(mOrientation == wxHORIZONTAL) {
1445  if((gridPos != 0) && (gridPos != mGridLineLength))
1446  mDC->DrawLine(gridPos+xOffset, yOffset, gridPos+xOffset, mGridLineLength+yOffset);
1447  }
1448  else {
1449  if((gridPos != 0) && (gridPos != mGridLineLength))
1450  mDC->DrawLine(xOffset, gridPos+yOffset, mGridLineLength+xOffset, gridPos+yOffset);
1451  }
1452  }
1453  }
1454 
1455  if(mMajorGrid && (mGridLineLength != 0 )) {
1456  gridPen.SetColour(127, 127, 127); // light grey
1457  mDC->SetPen(gridPen);
1458  for(int i=0; i<mNumMajor; i++) {
1459  gridPos = mMajorLabels[i].pos;
1460  if(mOrientation == wxHORIZONTAL) {
1461  if((gridPos != 0) && (gridPos != mGridLineLength))
1462  mDC->DrawLine(gridPos+xOffset, yOffset, gridPos+xOffset, mGridLineLength+yOffset);
1463  }
1464  else {
1465  if((gridPos != 0) && (gridPos != mGridLineLength))
1466  mDC->DrawLine(xOffset, gridPos+yOffset, mGridLineLength+xOffset, gridPos+yOffset);
1467  }
1468  }
1469 
1470  int zeroPosition = GetZeroPosition();
1471  if(zeroPosition > 0) {
1472  // Draw 'zero' grid line in black
1473  mDC->SetPen(*wxBLACK_PEN);
1474  if(mOrientation == wxHORIZONTAL) {
1475  if(zeroPosition != mGridLineLength)
1476  mDC->DrawLine(zeroPosition+xOffset, yOffset, zeroPosition+xOffset, mGridLineLength+yOffset);
1477  }
1478  else {
1479  if(zeroPosition != mGridLineLength)
1480  mDC->DrawLine(xOffset, zeroPosition+yOffset, mGridLineLength+xOffset, zeroPosition+yOffset);
1481  }
1482  }
1483  }
1484 }
1485 
1486 int Ruler::FindZero(Label * label, const int len)
1487 {
1488  int i = 0;
1489  double d = 1.0; // arbitrary
1490 
1491  do {
1492  d = label[i].value;
1493  i++;
1494  } while( (i < len) && (d != 0.0) );
1495 
1496  if(d == 0.0)
1497  return (label[i - 1].pos) ;
1498  else
1499  return -1;
1500 }
1501 
1503 {
1504  int zero;
1505  if((zero = FindZero(mMajorLabels.get(), mNumMajor)) < 0)
1506  zero = FindZero(mMinorLabels.get(), mNumMinor);
1507  // PRL: don't consult minor minor??
1508  return zero;
1509 }
1510 
1511 void Ruler::GetMaxSize(wxCoord *width, wxCoord *height)
1512 {
1513  if (!mValid) {
1514  wxScreenDC sdc;
1515  mDC = &sdc;
1516  Update(NULL);
1517  }
1518 
1519  if (width)
1520  *width = mRect.GetWidth(); //mMaxWidth;
1521 
1522  if (height)
1523  *height = mRect.GetHeight(); //mMaxHeight;
1524 }
1525 
1526 
1527 void Ruler::SetCustomMode(bool value) { mCustom = value; }
1528 
1529 void Ruler::SetCustomMajorLabels(wxArrayString *label, size_t numLabel, int start, int step)
1530 {
1531  mNumMajor = numLabel;
1532  mMajorLabels.reinit(numLabel);
1533 
1534  for(size_t i = 0; i<numLabel; i++) {
1535  mMajorLabels[i].text = label->Item(i);
1536  mMajorLabels[i].pos = start + i*step;
1537  }
1538  //Remember: DELETE majorlabels....
1539 }
1540 
1541 void Ruler::SetCustomMinorLabels(wxArrayString *label, size_t numLabel, int start, int step)
1542 {
1543  mNumMinor = numLabel;
1544  mMinorLabels.reinit(numLabel);
1545 
1546  for(size_t i = 0; i<numLabel; i++) {
1547  mMinorLabels[i].text = label->Item(i);
1548  mMinorLabels[i].pos = start + i*step;
1549  }
1550  //Remember: DELETE majorlabels....
1551 }
1552 
1553 void Ruler::Label::Draw(wxDC&dc, bool twoTone, wxColour c) const
1554 {
1555  if (text != wxT("")) {
1556  bool altColor = twoTone && value < 0.0;
1557 
1558 #ifdef EXPERIMENTAL_THEMING
1559  dc.SetTextForeground(altColor ? theTheme.Colour( clrTextNegativeNumbers) : c);
1560 #else
1561  dc.SetTextForeground(altColor ? *wxBLUE : *wxBLACK);
1562 #endif
1563 
1564  dc.DrawText(text, lx, ly);
1565  }
1566 }
1567 
1568 void Ruler::SetUseZoomInfo(int leftOffset, const ZoomInfo *zoomInfo)
1569 {
1570  mLeftOffset = leftOffset;
1571  mUseZoomInfo = zoomInfo;
1572 }
1573 
1574 //
1575 // RulerPanel
1576 //
1577 
1578 BEGIN_EVENT_TABLE(RulerPanel, wxPanelWrapper)
1579  EVT_ERASE_BACKGROUND(RulerPanel::OnErase)
1580  EVT_PAINT(RulerPanel::OnPaint)
1581  EVT_SIZE(RulerPanel::OnSize)
1583 
1585 
1586 RulerPanel::RulerPanel(wxWindow* parent, wxWindowID id,
1587  wxOrientation orientation,
1588  const wxSize &bounds,
1589  const Range &range,
1591  const wxString &units,
1592  const Options &options,
1593  const wxPoint& pos /*= wxDefaultPosition*/,
1594  const wxSize& size /*= wxDefaultSize*/):
1595  wxPanelWrapper(parent, id, pos, size)
1596 {
1597  ruler.SetBounds( 0, 0, bounds.x, bounds.y );
1598  ruler.SetOrientation(orientation);
1599  ruler.SetRange( range.first, range.second );
1600  ruler.SetLog( options.log );
1601  ruler.SetFormat(format);
1602  ruler.SetUnits( units );
1603  ruler.SetFlip( options.flip );
1604  ruler.SetLabelEdges( options.labelEdges );
1605  ruler.mbTicksAtExtremes = options.ticksAtExtremes;
1606  if (orientation == wxVERTICAL) {
1607  wxCoord w;
1608  ruler.GetMaxSize(&w, NULL);
1609  SetMinSize(wxSize(w, 150)); // height needed for wxGTK
1610  }
1611  else if (orientation == wxHORIZONTAL) {
1612  wxCoord h;
1613  ruler.GetMaxSize(NULL, &h);
1614  SetMinSize(wxSize(wxDefaultCoord, h));
1615  }
1616  if (options.hasTickColour)
1617  ruler.SetTickColour( options.tickColour );
1618 }
1619 
1621 {
1622 }
1623 
1624 void RulerPanel::OnErase(wxEraseEvent & WXUNUSED(evt))
1625 {
1626  // Ignore it to prevent flashing
1627 }
1628 
1629 void RulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
1630 {
1631  wxPaintDC dc(this);
1632 
1633 #if defined(__WXMSW__)
1634  dc.Clear();
1635 #endif
1636 
1637  ruler.Draw(dc);
1638 }
1639 
1640 void RulerPanel::OnSize(wxSizeEvent & WXUNUSED(evt))
1641 {
1642  Refresh();
1643 }
1644 
1645 // LL: We're overloading DoSetSize so that we can update the ruler bounds immediately
1646 // instead of waiting for a wxEVT_SIZE to come through. This is needed by (at least)
1647 // FreqWindow since it needs to have an updated ruler before RulerPanel gets the
1648 // size event.
1649 void RulerPanel::DoSetSize(int x, int y,
1650  int width, int height,
1651  int sizeFlags)
1652 {
1653  wxPanelWrapper::DoSetSize(x, y, width, height, sizeFlags);
1654 
1655  int w, h;
1656  GetClientSize(&w, &h);
1657 
1658  ruler.SetBounds(0, 0, w-1, h-1);
1659 }
bool mLabelEdges
Definition: Ruler.h:227
RulerFormat
Definition: Ruler.h:33
bool mMajorGrid
Definition: Ruler.h:233
void SetLog(bool log)
Definition: Ruler.cpp:155
void Draw(wxDC &dc)
Definition: Ruler.cpp:1285
void SetSpacing(int spacing)
Definition: Ruler.cpp:219
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:209
bool mHasSetSpacing
Definition: Ruler.h:226
double ComputeWarpedLength(double t0, double t1) const
Compute the duration (in seconds at playback) of the specified region of the track.
Definition: TimeTrack.cpp:164
std::unique_ptr< wxFont > mMinorMinorFont
Definition: Ruler.h:182
void Tick(int pos, double d, bool major, bool minor)
Definition: Ruler.cpp:724
wxString mUnits
Definition: Ruler.h:236
wxPen mPen
Definition: Ruler.h:174
int mNumMinor
Definition: Ruler.h:211
float ValueToPosition(float val) const
Definition: NumberScale.h:251
int mLengthOld
Definition: Ruler.h:179
bool mbMinor
Definition: Ruler.h:232
std::unique_ptr< wxFont > mMinorFont
Definition: Ruler.h:182
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:113
ArrayOf< Label > mMajorLabels
Definition: Ruler.h:210
bool mFlip
Definition: Ruler.h:230
An array of these created by the Ruler is used to determine what and where text annotations to the nu...
Definition: Ruler.h:199
void TickCustom(int labelIdx, bool major, bool minor)
Definition: Ruler.cpp:835
bool mUserFonts
Definition: Ruler.h:183
void SetBounds(int left, int top, int right, int bottom)
Definition: Ruler.cpp:314
ArrayOf< int > mUserBits
Definition: Ruler.h:193
double mDbMirrorValue
Definition: Ruler.h:225
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:1511
wxString label
Definition: Tags.cpp:733
double mMax
Definition: Ruler.h:185
ArrayOf< int > mBits
Definition: Ruler.h:194
double PositionToTime(wxInt64 position, wxInt64 origin=0, bool ignoreFisheye=false) const
Definition: ViewInfo.cpp:49
int mMaxWidth
Definition: Ruler.h:176
double mMin
Definition: Ruler.h:185
void SetNumberScale(const NumberScale *pScale)
Definition: Ruler.cpp:273
bool mValid
Definition: Ruler.h:197
void DoSetSize(int x, int y, int width, int height, int sizeFlags=wxSIZE_AUTO) override
Definition: Ruler.cpp:1649
int lx
Definition: Ruler.h:203
RulerPanel class allows you to work with a Ruler like any other wxWindow.
Definition: Ruler.h:244
std::unique_ptr< NumberScale > mpNumberScale
Definition: Ruler.h:241
bool mMinorGrid
Definition: Ruler.h:234
~RulerPanel()
Definition: Ruler.cpp:1620
wxRect mRect
Definition: Ruler.h:170
void Invalidate()
Definition: Ruler.cpp:327
void SetOrientation(int orient)
Definition: Ruler.cpp:178
void Draw(wxDC &dc, bool twoTone, wxColour c) const
Definition: Ruler.cpp:1553
int mTop
Definition: Ruler.h:177
wxString LabelString(double d, bool major)
Definition: Ruler.cpp:576
bool mCustom
Definition: Ruler.h:231
void FindLinearTickSizes(double UPP)
Definition: Ruler.cpp:343
int FindZero(Label *label, int len)
Definition: Ruler.cpp:1486
void SetTwoTone(bool twoTone)
Definition: Ruler.cpp:139
A kind of Track used to 'warp time'.
Definition: TimeTrack.h:29
wxColour mTickColour
Definition: Ruler.h:173
int mRight
Definition: Ruler.h:177
void SetCustomMinorLabels(wxArrayString *label, size_t numLabel, int start, int step)
Definition: Ruler.cpp:1541
int format
Definition: ExportPCM.cpp:56
double mMinor
Definition: Ruler.h:189
bool mLog
Definition: Ruler.h:229
void SetLabelEdges(bool labelEdges)
Definition: Ruler.cpp:230
int ly
Definition: Ruler.h:203
int mLeftOffset
Definition: Ruler.h:239
~Ruler()
Definition: Ruler.cpp:134
int mBottom
Definition: Ruler.h:177
void SetFlip(bool flip)
Definition: Ruler.cpp:243
RulerFormat mFormat
Definition: Ruler.h:228
bool mbTicksOnly
Definition: Ruler.h:168
wxInt64 TimeToPosition(double time, wxInt64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Definition: ViewInfo.cpp:59
int mMaxHeight
Definition: Ruler.h:176
void SetUnits(const wxString &units)
Definition: Ruler.cpp:166
int min(int a, int b)
ArrayOf< Label > mMinorMinorLabels
Definition: Ruler.h:214
const ZoomInfo * mUseZoomInfo
Definition: Ruler.h:238
double mHiddenMin
Definition: Ruler.h:186
bool mTwoTone
Definition: Ruler.h:237
int mUserBitLen
Definition: Ruler.h:195
int mLead
Definition: Ruler.h:177
IMPLEMENT_CLASS(ControlToolBar, ToolBar)
int mLength
Definition: Ruler.h:178
wxDC * mDC
Definition: Ruler.h:180
void DrawGrid(wxDC &dc, int length, bool minor=true, bool major=true, int xOffset=0, int yOffset=0)
Definition: Ruler.cpp:1427
void SetFormat(RulerFormat format)
Definition: Ruler.cpp:144
void SetRange(double min, double max)
Definition: Ruler.cpp:192
int mNumMajor
Definition: Ruler.h:209
Used to display a Ruler.
Definition: Ruler.h:30
double value
Definition: Ruler.h:201
double mHiddenMax
Definition: Ruler.h:186
int mGridLineLength
Definition: Ruler.h:235
int mNumMinorMinor
Definition: Ruler.h:213
void SetUseZoomInfo(int leftOffset, const ZoomInfo *zoomInfo)
Definition: Ruler.cpp:1568
std::pair< double, double > Range
Definition: Ruler.h:248
int mDigits
Definition: Ruler.h:191
int mLeft
Definition: Ruler.h:177
Ruler()
Definition: Ruler.cpp:76
void SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont)
Definition: Ruler.cpp:261
int mOrientation
Definition: Ruler.h:223
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1225
bool mbTicksAtExtremes
Definition: Ruler.h:169
int GetZeroPosition()
Definition: Ruler.cpp:1502
END_EVENT_TABLE()
void OfflimitsPixels(int start, int end)
Definition: Ruler.cpp:289
void SetCustomMajorLabels(wxArrayString *label, size_t numLabel, int start, int step)
Definition: Ruler.cpp:1529
static void OnSize(wxSizeEvent &evt)
Definition: VSTEffect.cpp:2765
wxString text
Definition: Ruler.h:204
double mMajor
Definition: Ruler.h:188
void OnPaint(wxPaintEvent &evt)
Definition: Ruler.cpp:1629
std::unique_ptr< wxFont > mMajorFont
Definition: Ruler.h:182
int pos
Definition: Ruler.h:202
void Update()
Definition: Ruler.cpp:949
void OnSize(wxSizeEvent &evt)
Definition: Ruler.cpp:1640
void SetCustomMode(bool value)
Definition: Ruler.cpp:1527
void OnErase(wxEraseEvent &evt)
Definition: Ruler.cpp:1624
int mSpacing
Definition: Ruler.h:224
void SetMinor(bool value)
Definition: Ruler.cpp:256
ArrayOf< Label > mMinorLabels
Definition: Ruler.h:212