Audacity  2.2.2
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 *******************************************************************//***************************************************************//***************************************************************//***************************************************************//******************************************************************/
65 
66 #include "../Audacity.h"
67 #include "Ruler.h"
68 
69 #include <math.h>
70 
71 #include <wx/app.h>
72 #include <wx/dcscreen.h>
73 #include <wx/dcmemory.h>
74 #include <wx/dcbuffer.h>
75 #include <wx/settings.h>
76 #include <wx/menu.h>
77 #include <wx/menuitem.h>
78 #include <wx/tooltip.h>
79 
80 #include "AButton.h"
81 #include "../AColor.h"
82 #include "../AudioIO.h"
83 #include "../Internat.h"
84 #include "../Project.h"
85 #include "../toolbars/ControlToolBar.h"
86 #include "../Theme.h"
87 #include "../AllThemeResources.h"
88 #include "../Experimental.h"
89 #include "../TimeTrack.h"
90 #include "../TrackPanel.h"
91 #include "../TrackPanelCellIterator.h"
92 #include "../NumberScale.h"
93 #include "../Prefs.h"
94 #include "../Snap.h"
95 #include "../tracks/ui/Scrubbing.h"
96 #include "../prefs/PlaybackPrefs.h"
97 #include "../prefs/TracksPrefs.h"
98 #include "../prefs/TracksBehaviorsPrefs.h"
99 #include "../widgets/Grabber.h"
100 
101 //#define SCRUB_ABOVE
102 
103 using std::min;
104 using std::max;
105 
106 #define SELECT_TOLERANCE_PIXEL 4
107 
108 #define PLAY_REGION_TRIANGLE_SIZE 6
109 #define PLAY_REGION_RECT_WIDTH 1
110 #define PLAY_REGION_RECT_HEIGHT 3
111 #define PLAY_REGION_GLOBAL_OFFSET_Y 7
112 
113 //wxColour Ruler::mTickColour{ 153, 153, 153 };
114 
115 //
116 // Ruler
117 //
118 
120  : mpNumberScale{}
121 {
122  mMin = mHiddenMin = 0.0;
123  mMax = mHiddenMax = 100.0;
124  mOrientation = wxHORIZONTAL;
125  mSpacing = 6;
126  mHasSetSpacing = false;
127  mFormat = RealFormat;
128  mFlip = false;
129  mLog = false;
130  mLabelEdges = false;
131  mUnits = wxT("");
132 
133  mLeft = -1;
134  mTop = -1;
135  mRight = -1;
136  mBottom = -1;
137  mbTicksOnly = true;
138  mbTicksAtExtremes = false;
139  mTickColour = wxColour( theTheme.Colour( clrTrackPanelText ));
140  mPen.SetColour(mTickColour);
141 
142  // Note: the font size is now adjusted automatically whenever
143  // Invalidate is called on a horizontal Ruler, unless the user
144  // calls SetFonts manually. So the defaults here are not used
145  // often.
146 
147  int fontSize = 10;
148 #ifdef __WXMSW__
149  fontSize = 8;
150 #endif
151 
152  mMinorMinorFont = std::make_unique<wxFont>(fontSize - 1, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
153  mMinorFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
154  mMajorFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
155 
156  mUserFonts = false;
157 
158  mLengthOld = 0;
159  mLength = 0;
160  mUserBitLen = 0;
161 
162  mValid = false;
163 
164  mCustom = false;
165  mbMinor = true;
166 
167  mGridLineLength = 0;
168  mMajorGrid = false;
169  mMinorGrid = false;
170 
171  mTwoTone = false;
172 
173  mUseZoomInfo = NULL;
174 }
175 
177 {
178  Invalidate(); // frees up our arrays
179 }
180 
181 void Ruler::SetTwoTone(bool twoTone)
182 {
183  mTwoTone = twoTone;
184 }
185 
187 {
188  // IntFormat, RealFormat, RealLogFormat, TimeFormat, or LinearDBFormat
189 
190  if (mFormat != format) {
191  mFormat = format;
192 
193  Invalidate();
194  }
195 }
196 
197 void Ruler::SetLog(bool log)
198 {
199  // Logarithmic
200 
201  if (mLog != log) {
202  mLog = log;
203 
204  Invalidate();
205  }
206 }
207 
208 void Ruler::SetUnits(const wxString &units)
209 {
210  // Specify the name of the units (like "dB") if you
211  // want numbers like "1.6" formatted as "1.6 dB".
212 
213  if (mUnits != units) {
214  mUnits = units;
215 
216  Invalidate();
217  }
218 }
219 
220 void Ruler::SetOrientation(int orient)
221 {
222  // wxHORIZONTAL || wxVERTICAL
223 
224  if (mOrientation != orient) {
225  mOrientation = orient;
226 
227  if (mOrientation == wxVERTICAL && !mHasSetSpacing)
228  mSpacing = 2;
229 
230  Invalidate();
231  }
232 }
233 
234 void Ruler::SetRange(double min, double max)
235 {
236  SetRange(min, max, min, max);
237 }
238 
239 void Ruler::SetRange
240  (double min, double max, double hiddenMin, double hiddenMax)
241 {
242  // For a horizontal ruler,
243  // min is the value in the center of pixel "left",
244  // max is the value in the center of pixel "right".
245 
246  // In the special case of a time ruler,
247  // hiddenMin and hiddenMax are values that would be shown with the fisheye
248  // turned off. In other cases they equal min and max respectively.
249 
250  if (mMin != min || mMax != max ||
251  mHiddenMin != hiddenMin || mHiddenMax != hiddenMax) {
252  mMin = min;
253  mMax = max;
254  mHiddenMin = hiddenMin;
255  mHiddenMax = hiddenMax;
256 
257  Invalidate();
258  }
259 }
260 
261 void Ruler::SetSpacing(int spacing)
262 {
263  mHasSetSpacing = true;
264 
265  if (mSpacing != spacing) {
266  mSpacing = spacing;
267 
268  Invalidate();
269  }
270 }
271 
272 void Ruler::SetLabelEdges(bool labelEdges)
273 {
274  // If this is true, the edges of the ruler will always
275  // receive a label. If not, the nearest round number is
276  // labeled (which may or may not be the edge).
277 
278  if (mLabelEdges != labelEdges) {
279  mLabelEdges = labelEdges;
280 
281  Invalidate();
282  }
283 }
284 
285 void Ruler::SetFlip(bool flip)
286 {
287  // If this is true, the orientation of the tick marks
288  // is reversed from the default; eg. above the line
289  // instead of below
290 
291  if (mFlip != flip) {
292  mFlip = flip;
293 
294  Invalidate();
295  }
296 }
297 
298 void Ruler::SetMinor(bool value)
299 {
300  mbMinor = value;
301 }
302 
303 void Ruler::SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont)
304 {
305  *mMinorMinorFont = minorMinorFont;
306  *mMinorFont = minorFont;
307  *mMajorFont = majorFont;
308 
309  // Won't override these fonts
310  mUserFonts = true;
311 
312  Invalidate();
313 }
314 
316 {
317  if (!pScale) {
318  if (mpNumberScale) {
319  mpNumberScale.reset();
320  Invalidate();
321  }
322  }
323  else {
324  if (!mpNumberScale || *mpNumberScale != *pScale) {
325  mpNumberScale = std::make_unique<NumberScale>(*pScale);
326  Invalidate();
327  }
328  }
329 }
330 
331 void Ruler::OfflimitsPixels(int start, int end)
332 {
333  if (!mUserBits) {
334  if (mOrientation == wxHORIZONTAL)
335  mLength = mRight-mLeft;
336  else
337  mLength = mBottom-mTop;
338  if( mLength < 0 )
339  return;
340  mUserBits.reinit(static_cast<size_t>(mLength+1), true);
341  mUserBitLen = mLength+1;
342  }
343 
344  if (end < start)
345  std::swap( start, end );
346 
347  if (start < 0)
348  start = 0;
349  if (end > mLength)
350  end = mLength;
351 
352  for(int i = start; i <= end; i++)
353  mUserBits[i] = 1;
354 }
355 
356 void Ruler::SetBounds(int left, int top, int right, int bottom)
357 {
358  if (mLeft != left || mTop != top ||
359  mRight != right || mBottom != bottom) {
360  mLeft = left;
361  mTop = top;
362  mRight = right;
363  mBottom = bottom;
364 
365  Invalidate();
366  }
367 }
368 
370 {
371  mValid = false;
372 
373  if (mOrientation == wxHORIZONTAL)
374  mLength = mRight-mLeft;
375  else
376  mLength = mBottom-mTop;
377 
378  mBits.reset();
379  if (mUserBits && mLength+1 != mUserBitLen) {
380  mUserBits.reset();
381  mUserBitLen = 0;
382  }
383 }
384 
386 {
387  // Given the dimensions of the ruler, the range of values it
388  // has to display, and the format (i.e. Int, Real, Time),
389  // figure out how many units are in one Minor tick, and
390  // in one Major tick.
391  //
392  // The goal is to always put tick marks on nice round numbers
393  // that are easy for humans to grok. This is the most tricky
394  // with time.
395 
396  double d;
397 
398  // As a heuristic, we want at least 16 pixels
399  // between each minor tick
400  double units = 16 * fabs(UPP);
401 
402  mDigits = 0;
403 
404  switch(mFormat) {
405  case LinearDBFormat:
406  if (units < 0.001) {
407  mMinor = 0.001;
408  mMajor = 0.005;
409  return;
410  }
411  if (units < 0.01) {
412  mMinor = 0.01;
413  mMajor = 0.05;
414  return;
415  }
416  if (units < 0.1) {
417  mMinor = 0.1;
418  mMajor = 0.5;
419  return;
420  }
421  if (units < 1.0) {
422  mMinor = 1.0;
423  mMajor = 6.0;
424  return;
425  }
426  if (units < 3.0) {
427  mMinor = 3.0;
428  mMajor = 12.0;
429  return;
430  }
431  if (units < 6.0) {
432  mMinor = 6.0;
433  mMajor = 24.0;
434  return;
435  }
436  if (units < 12.0) {
437  mMinor = 12.0;
438  mMajor = 48.0;
439  return;
440  }
441  if (units < 24.0) {
442  mMinor = 24.0;
443  mMajor = 96.0;
444  return;
445  }
446  d = 20.0;
447  for(;;) {
448  if (units < d) {
449  mMinor = d;
450  mMajor = d*5.0;
451  return;
452  }
453  d *= 5.0;
454  if (units < d) {
455  mMinor = d;
456  mMajor = d*5.0;
457  return;
458  }
459  d *= 2.0;
460  }
461  break;
462 
463  case IntFormat:
464  d = 1.0;
465  for(;;) {
466  if (units < d) {
467  mMinor = d;
468  mMajor = d*5.0;
469  return;
470  }
471  d *= 5.0;
472  if (units < d) {
473  mMinor = d;
474  mMajor = d*2.0;
475  return;
476  }
477  d *= 2.0;
478  }
479  break;
480 
481  case TimeFormat:
482  if (units > 0.5) {
483  if (units < 1.0) { // 1 sec
484  mMinor = 1.0;
485  mMajor = 5.0;
486  return;
487  }
488  if (units < 5.0) { // 5 sec
489  mMinor = 5.0;
490  mMajor = 15.0;
491  return;
492  }
493  if (units < 10.0) {
494  mMinor = 10.0;
495  mMajor = 30.0;
496  return;
497  }
498  if (units < 15.0) {
499  mMinor = 15.0;
500  mMajor = 60.0;
501  return;
502  }
503  if (units < 30.0) {
504  mMinor = 30.0;
505  mMajor = 60.0;
506  return;
507  }
508  if (units < 60.0) { // 1 min
509  mMinor = 60.0;
510  mMajor = 300.0;
511  return;
512  }
513  if (units < 300.0) { // 5 min
514  mMinor = 300.0;
515  mMajor = 900.0;
516  return;
517  }
518  if (units < 600.0) { // 10 min
519  mMinor = 600.0;
520  mMajor = 1800.0;
521  return;
522  }
523  if (units < 900.0) { // 15 min
524  mMinor = 900.0;
525  mMajor = 3600.0;
526  return;
527  }
528  if (units < 1800.0) { // 30 min
529  mMinor = 1800.0;
530  mMajor = 3600.0;
531  return;
532  }
533  if (units < 3600.0) { // 1 hr
534  mMinor = 3600.0;
535  mMajor = 6*3600.0;
536  return;
537  }
538  if (units < 6*3600.0) { // 6 hrs
539  mMinor = 6*3600.0;
540  mMajor = 24*3600.0;
541  return;
542  }
543  if (units < 24*3600.0) { // 1 day
544  mMinor = 24*3600.0;
545  mMajor = 7*24*3600.0;
546  return;
547  }
548 
549  mMinor = 24.0 * 7.0 * 3600.0; // 1 week
550  mMajor = 24.0 * 7.0 * 3600.0;
551  }
552 
553  // Otherwise fall through to RealFormat
554  // (fractions of a second should be dealt with
555  // the same way as for RealFormat)
556 
557  case RealFormat:
558  d = 0.000001;
559  // mDigits is number of digits after the decimal point.
560  mDigits = 6;
561  for(;;) {
562  if (units < d) {
563  mMinor = d;
564  mMajor = d*5.0;
565  return;
566  }
567  d *= 5.0;
568  if (units < d) {
569  mMinor = d;
570  mMajor = d*2.0;
571  return;
572  }
573  d *= 2.0;
574  mDigits--;
575  // More than 10 digit numbers? Something is badly wrong.
576  // Probably units is coming in with too high a value.
577  wxASSERT( mDigits >= -10 );
578  if( mDigits < -10 )
579  break;
580  }
581  mMinor = d;
582  mMajor = d * 2.0;
583  break;
584 
585  case RealLogFormat:
586  d = 0.000001;
587  // mDigits is number of digits after the decimal point.
588  mDigits = 6;
589  for(;;) {
590  if (units < d) {
591  mMinor = d;
592  mMajor = d*5.0;
593  return;
594  }
595  d *= 5.0;
596  if (units < d) {
597  mMinor = d;
598  mMajor = d*2.0;
599  return;
600  }
601  d *= 2.0;
602  mDigits--;
603  // More than 10 digit numbers? Something is badly wrong.
604  // Probably units is coming in with too high a value.
605  wxASSERT( mDigits >= -10 );
606  if( mDigits < -10 )
607  break;
608  }
609  mDigits++;
610  mMinor = d;
611  mMajor = d * 2.0;
612  break;
613  }
614 }
615 
616 wxString Ruler::LabelString(double d, bool major)
617 {
618  // Given a value, turn it into a string according
619  // to the current ruler format. The number of digits of
620  // accuracy depends on the resolution of the ruler,
621  // i.e. how far zoomed in or out you are.
622 
623  wxString s;
624 
625  // Replace -0 with 0
626  if (d < 0.0 && (d+mMinor > 0.0) && ( mFormat != RealLogFormat ))
627  d = 0.0;
628 
629  switch(mFormat) {
630  case IntFormat:
631  s.Printf(wxT("%d"), (int)floor(d+0.5));
632  break;
633  case LinearDBFormat:
634  if (mMinor >= 1.0)
635  s.Printf(wxT("%d"), (int)floor(d+0.5));
636  else {
637  int precision = -log10(mMinor);
638  s.Printf(wxT("%.*f"), precision, d);
639  }
640  break;
641  case RealFormat:
642  if (mMinor >= 1.0)
643  s.Printf(wxT("%d"), (int)floor(d+0.5));
644  else {
645  s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d);
646  }
647  break;
648  case RealLogFormat:
649  if (mMinor >= 1.0)
650  s.Printf(wxT("%d"), (int)floor(d+0.5));
651  else {
652  s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d);
653  }
654  break;
655  case TimeFormat:
656  if (major) {
657  if (d < 0) {
658  s = wxT("-");
659  d = -d;
660  }
661 
662  #if ALWAYS_HH_MM_SS
663  int secs = (int)(d + 0.5);
664  if (mMinor >= 1.0) {
665  s.Printf(wxT("%d:%02d:%02d"), secs/3600, (secs/60)%60, secs%60);
666  }
667  else {
668  wxString t1, t2, format;
669  t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60);
670  format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits);
671  t2.Printf(format, fmod(d, 60.0));
672  s += t1 + t2;
673  }
674  break;
675  #endif
676 
677  if (mMinor >= 3600.0) {
678  int hrs = (int)(d / 3600.0 + 0.5);
679  wxString h;
680  h.Printf(wxT("%d:00:00"), hrs);
681  s += h;
682  }
683  else if (mMinor >= 60.0) {
684  int minutes = (int)(d / 60.0 + 0.5);
685  wxString m;
686  if (minutes >= 60)
687  m.Printf(wxT("%d:%02d:00"), minutes/60, minutes%60);
688  else
689  m.Printf(wxT("%d:00"), minutes);
690  s += m;
691  }
692  else if (mMinor >= 1.0) {
693  int secs = (int)(d + 0.5);
694  wxString t;
695  if (secs >= 3600)
696  t.Printf(wxT("%d:%02d:%02d"), secs/3600, (secs/60)%60, secs%60);
697  else if (secs >= 60)
698  t.Printf(wxT("%d:%02d"), secs/60, secs%60);
699  else
700  t.Printf(wxT("%d"), secs);
701  s += t;
702  }
703  else {
704 // Commented out old and incorrect code for avoiding the 40mins and 60 seconds problem
705 // It was causing Bug 463 - Incorrect Timeline numbering (where at high zoom and long tracks,
706 // numbers did not change.
707 #if 0
708  // The casting to float is working around an issue where 59 seconds
709  // would show up as 60 when using g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3.
710  int secs = (int)(float)(d);
711  wxString t1, t2, format;
712 
713  if (secs >= 3600)
714  t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60);
715  else if (secs >= 60)
716  t1.Printf(wxT("%d:"), secs/60);
717 
718  if (secs >= 60)
719  format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits);
720  else
721  format.Printf(wxT("%%%d.%dlf"), mDigits+3, mDigits);
722  // The casting to float is working around an issue where 59 seconds
723  // would show up as 60 when using g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3.
724  t2.Printf(format, fmod((float)d, (float)60.0));
725 #else
726  // For d in the range of hours, d is just very slightly below the value it should
727  // have, because of using a double, which in turn yields values like 59:59:999999
728  // mins:secs:nanosecs when we want 1:00:00:000000
729  // so adjust by less than a nano second per hour to get nicer number formatting.
730  double dd = d * 1.000000000000001;
731  int secs = (int)(dd);
732  wxString t1, t2, format;
733 
734  if (secs >= 3600)
735  t1.Printf(wxT("%d:%02d:"), secs/3600, (secs/60)%60);
736  else if (secs >= 60)
737  t1.Printf(wxT("%d:"), secs/60);
738 
739  if (secs >= 60)
740  format.Printf(wxT("%%0%d.%dlf"), mDigits+3, mDigits);
741  else
742  format.Printf(wxT("%%%d.%dlf"), mDigits+3, mDigits);
743  // dd will be reduced to just the seconds and fractional part.
744  dd = dd - secs + (secs%60);
745  // truncate to appropriate number of digits, so that the print formatting
746  // doesn't round up 59.9999999 to 60.
747  double multiplier = pow( 10, mDigits);
748  dd = ((int)(dd * multiplier))/multiplier;
749  t2.Printf(format, dd);
750 #endif
751  s += t1 + t2;
752  }
753  }
754  else {
755  }
756  }
757 
758  if (mUnits != wxT(""))
759  s = (s + mUnits);
760 
761  return s;
762 }
763 
764 void Ruler::Tick(int pos, double d, bool major, bool minor)
765 {
766  wxString l;
767  wxCoord strW, strH, strD, strL;
768  int strPos, strLen, strLeft, strTop;
769 
770  // FIXME: We don't draw a tick if of end of our label arrays
771  // But we shouldn't have an array of labels.
772  if( mNumMinorMinor >= mLength )
773  return;
774  if( mNumMinor >= mLength )
775  return;
776  if( mNumMajor >= mLength )
777  return;
778 
779  Label *label;
780  if (major)
781  label = &mMajorLabels[mNumMajor++];
782  else if (minor)
783  label = &mMinorLabels[mNumMinor++];
784  else
785  label = &mMinorMinorLabels[mNumMinorMinor++];
786 
787  label->value = d;
788  label->pos = pos;
789  label->lx = mLeft - 1000; // don't display
790  label->ly = mTop - 1000; // don't display
791  label->text = wxT("");
792 
793  mDC->SetFont(major? *mMajorFont: minor? *mMinorFont : *mMinorMinorFont);
794  l = LabelString(d, major);
795  mDC->GetTextExtent(l, &strW, &strH, &strD, &strL);
796 
797  if (mOrientation == wxHORIZONTAL) {
798  strLen = strW;
799  strPos = pos - strW/2;
800  if (strPos < 0)
801  strPos = 0;
802  if (strPos + strW >= mLength)
803  strPos = mLength - strW;
804  strLeft = mLeft + strPos;
805  if (mFlip) {
806  strTop = mTop + 4;
807  mMaxHeight = max(mMaxHeight, strH + 4);
808  }
809  else {
810  strTop =-strH-mLead;
811  mMaxHeight = max(mMaxHeight, strH + 6);
812  }
813  }
814  else {
815  strLen = strH;
816  strPos = pos - strH/2;
817  if (strPos < 0)
818  strPos = 0;
819  if (strPos + strH >= mLength)
820  strPos = mLength - strH;
821  strTop = mTop + strPos;
822  if (mFlip) {
823  strLeft = mLeft + 5;
824  mMaxWidth = max(mMaxWidth, strW + 5);
825  }
826  else
827  strLeft =-strW-6;
828  }
829 
830 
831  // FIXME: we shouldn't even get here if strPos < 0.
832  // Ruler code currently does not handle very small or
833  // negative sized windows (i.e. don't draw) properly.
834  if( strPos < 0 )
835  return;
836 
837  // See if any of the pixels we need to draw this
838  // label is already covered
839 
840  int i;
841  for(i=0; i<strLen; i++)
842  if (mBits[strPos+i])
843  return;
844 
845  // If not, position the label and give it text
846 
847  label->lx = strLeft;
848  label->ly = strTop;
849  label->text = l;
850 
851  // And mark these pixels, plus some surrounding
852  // ones (the spacing between labels), as covered
853  int leftMargin = mSpacing;
854  if (strPos < leftMargin)
855  leftMargin = strPos;
856  strPos -= leftMargin;
857  strLen += leftMargin;
858 
859  int rightMargin = mSpacing;
860  if (strPos + strLen > mLength - mSpacing)
861  rightMargin = mLength - strPos - strLen;
862  strLen += rightMargin;
863 
864  for(i=0; i<strLen; i++)
865  mBits[strPos+i] = 1;
866 
867  wxRect r(strLeft, strTop, strW, strH);
868  mRect.Union(r);
869 
870 }
871 
872 void Ruler::TickCustom(int labelIdx, bool major, bool minor)
873 {
874  //This should only used in the mCustom case
875  // Many code comes from 'Tick' method: this should
876  // be optimized.
877 
878  int pos;
879  wxString l;
880  wxCoord strW, strH, strD, strL;
881  int strPos, strLen, strLeft, strTop;
882 
883  // FIXME: We don't draw a tick if of end of our label arrays
884  // But we shouldn't have an array of labels.
885  if( mNumMinor >= mLength )
886  return;
887  if( mNumMajor >= mLength )
888  return;
889 
890  Label *label;
891  if (major)
892  label = &mMajorLabels[labelIdx];
893  else if (minor)
894  label = &mMinorLabels[labelIdx];
895  else
896  label = &mMinorMinorLabels[labelIdx];
897 
898  label->value = 0.0;
899  pos = label->pos; // already stored in label class
900  l = label->text;
901  label->lx = mLeft - 1000; // don't display
902  label->ly = mTop - 1000; // don't display
903 
904  mDC->SetFont(major? *mMajorFont: minor? *mMinorFont : *mMinorMinorFont);
905 
906  mDC->GetTextExtent(l, &strW, &strH, &strD, &strL);
907 
908  if (mOrientation == wxHORIZONTAL) {
909  strLen = strW;
910  strPos = pos - strW/2;
911  if (strPos < 0)
912  strPos = 0;
913  if (strPos + strW >= mLength)
914  strPos = mLength - strW;
915  strLeft = mLeft + strPos;
916  if (mFlip) {
917  strTop = mTop + 4;
918  mMaxHeight = max(mMaxHeight, strH + 4);
919  }
920  else {
921 
922  strTop = mTop- mLead+4;// More space was needed...
923  mMaxHeight = max(mMaxHeight, strH + 6);
924  }
925  }
926  else {
927  strLen = strH;
928  strPos = pos - strH/2;
929  if (strPos < 0)
930  strPos = 0;
931  if (strPos + strH >= mLength)
932  strPos = mLength - strH;
933  strTop = mTop + strPos;
934  if (mFlip) {
935  strLeft = mLeft + 5;
936  mMaxWidth = max(mMaxWidth, strW + 5);
937  }
938  else {
939 
940  strLeft =-strW-6;
941  }
942  }
943 
944 
945  // FIXME: we shouldn't even get here if strPos < 0.
946  // Ruler code currently does not handle very small or
947  // negative sized windows (i.e. don't draw) properly.
948  if( strPos < 0 )
949  return;
950 
951  // See if any of the pixels we need to draw this
952  // label is already covered
953 
954  int i;
955  for(i=0; i<strLen; i++)
956  if (mBits[strPos+i])
957  return;
958 
959  // If not, position the label
960 
961  label->lx = strLeft;
962  label->ly = strTop;
963 
964  // And mark these pixels, plus some surrounding
965  // ones (the spacing between labels), as covered
966  int leftMargin = mSpacing;
967  if (strPos < leftMargin)
968  leftMargin = strPos;
969  strPos -= leftMargin;
970  strLen += leftMargin;
971 
972  int rightMargin = mSpacing;
973  if (strPos + strLen > mLength - mSpacing)
974  rightMargin = mLength - strPos - strLen;
975  strLen += rightMargin;
976 
977  for(i=0; i<strLen; i++)
978  mBits[strPos+i] = 1;
979 
980 
981  wxRect r(strLeft, strTop, strW, strH);
982  mRect.Union(r);
983 
984 }
985 
987 {
988  Update(NULL);
989 }
990 
991 void Ruler::Update(const TimeTrack* timetrack)// Envelope *speedEnv, long minSpeed, long maxSpeed )
992 {
993  const ZoomInfo *zoomInfo = NULL;
994  if (!mLog && mOrientation == wxHORIZONTAL)
995  zoomInfo = mUseZoomInfo;
996 
997  // This gets called when something has been changed
998  // (i.e. we've been invalidated). Recompute all
999  // tick positions and font size.
1000 
1001  int i;
1002  int j;
1003 
1004  if (!mUserFonts) {
1005  int fontSize = 4;
1006  wxCoord strW, strH, strD, strL;
1007  wxString exampleText = wxT("0.9"); //ignored for height calcs on all platforms
1008  int desiredPixelHeight;
1009 
1010 
1011  static const int MinPixelHeight = 10; // 8;
1012  static const int MaxPixelHeight =
1013 #ifdef __WXMAC__
1014  10
1015 #else
1016  12
1017 #endif
1018  ;
1019 
1020  if (mOrientation == wxHORIZONTAL)
1021  desiredPixelHeight = mBottom - mTop - 5; // height less ticks and 1px gap
1022  else
1023  desiredPixelHeight = MaxPixelHeight;
1024 
1025  desiredPixelHeight =
1026  std::max(MinPixelHeight, std::min(MaxPixelHeight,
1027  desiredPixelHeight));
1028 
1029  // Keep making the font bigger until it's too big, then subtract one.
1030  mDC->SetFont(wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
1031  mDC->GetTextExtent(exampleText, &strW, &strH, &strD, &strL);
1032  while ((strH - strD - strL) <= desiredPixelHeight && fontSize < 40) {
1033  fontSize++;
1034  mDC->SetFont(wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
1035  mDC->GetTextExtent(exampleText, &strW, &strH, &strD, &strL);
1036  }
1037  fontSize--;
1038  mDC->SetFont(wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
1039  mDC->GetTextExtent(exampleText, &strW, &strH, &strD, &strL);
1040  mLead = strL;
1041 
1042  mMajorFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
1043 
1044  mMinorFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1045 
1046  mMinorMinorFont = std::make_unique<wxFont>(fontSize - 1, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1047  }
1048 
1049  // If ruler is being resized, we could end up with it being too small.
1050  // Values of mLength of zero or below cause bad array allocations and
1051  // division by zero. So...
1052  // IF too small THEN bail out and don't draw.
1053  if( mLength <= 0 )
1054  return;
1055 
1056  if (mOrientation == wxHORIZONTAL) {
1057  mMaxWidth = mLength;
1058  mMaxHeight = 0;
1059  mRect = wxRect(0,0, mLength,0);
1060  }
1061  else {
1062  mMaxWidth = 0;
1063  mMaxHeight = mLength;
1064  mRect = wxRect(0,0, 0,mLength);
1065  }
1066 
1067  // FIXME: Surely we do not need to allocate storage for the labels?
1068  // We can just recompute them as we need them? Yes, but only if
1069  // mCustom is false!!!!
1070 
1071  auto size = static_cast<size_t>(mLength + 1);
1072  if(!mCustom) {
1073  mNumMajor = 0;
1074  mNumMinor = 0;
1075  mNumMinorMinor = 0;
1076  if (mLength!=mLengthOld) {
1077  mMajorLabels.reinit(size);
1078  mMinorLabels.reinit(size);
1079  mMinorMinorLabels.reinit(size);
1080  mLengthOld = mLength;
1081  }
1082  }
1083 
1084  mBits.reinit(size);
1085  if (mUserBits)
1086  for(i=0; i<=mLength; i++)
1087  mBits[i] = mUserBits[i];
1088  else
1089  for(i=0; i<=mLength; i++)
1090  mBits[i] = 0;
1091 
1092  // *************** Label calculation routine **************
1093  if(mCustom == true) {
1094 
1095  // SET PARAMETER IN MCUSTOM CASE
1096  // Works only with major labels
1097 
1098  int numLabel = mNumMajor;
1099 
1100  i = 0;
1101  while((i<numLabel) && (i<=mLength)) {
1102 
1103  TickCustom(i, true, false);
1104  i++;
1105  }
1106 
1107  } else if(mLog==false) {
1108 
1109  // Use the "hidden" min and max to determine the tick size.
1110  // That may make a difference with fisheye.
1111  // Otherwise you may see the tick size for the whole ruler change
1112  // when the fisheye approaches start or end.
1113  double UPP = (mHiddenMax-mHiddenMin)/mLength; // Units per pixel
1114  FindLinearTickSizes(UPP);
1115 
1116  // Left and Right Edges
1117  if (mLabelEdges) {
1118  Tick(0, mMin, true, false);
1119  Tick(mLength, mMax, true, false);
1120  }
1121 
1122  // Zero (if it's in the middle somewhere)
1123  if (mMin * mMax < 0.0) {
1124  int mid;
1125  if (zoomInfo != NULL)
1126  mid = (int)(zoomInfo->TimeToPosition(0.0, mLeftOffset));
1127  else
1128  mid = (int)(mLength*(mMin / (mMin - mMax)) + 0.5);
1129  const int iMaxPos = (mOrientation == wxHORIZONTAL) ? mRight : mBottom - 5;
1130  if (mid >= 0 && mid < iMaxPos)
1131  Tick(mid, 0.0, true, false);
1132  }
1133 
1134  double sg = UPP > 0.0? 1.0: -1.0;
1135 
1136  // Major and minor ticks
1137  for (int jj = 0; jj < 2; ++jj) {
1138  const double denom = jj == 0 ? mMajor : mMinor;
1139  i = -1; j = 0;
1140  double d, warpedD, nextD;
1141 
1142  double prevTime = 0.0, time = 0.0;
1143  if (zoomInfo != NULL) {
1144  j = zoomInfo->TimeToPosition(mMin);
1145  prevTime = zoomInfo->PositionToTime(--j);
1146  time = zoomInfo->PositionToTime(++j);
1147  d = (prevTime + time) / 2.0;
1148  }
1149  else
1150  d = mMin - UPP / 2;
1151  if (timetrack)
1152  warpedD = timetrack->ComputeWarpedLength(0.0, d);
1153  else
1154  warpedD = d;
1155  // using ints doesn't work, as
1156  // this will overflow and be negative at high zoom.
1157  double step = floor(sg * warpedD / denom);
1158  while (i <= mLength) {
1159  i++;
1160  if (zoomInfo)
1161  {
1162  prevTime = time;
1163  time = zoomInfo->PositionToTime(++j);
1164  nextD = (prevTime + time) / 2.0;
1165  // wxASSERT(time >= prevTime);
1166  }
1167  else
1168  nextD = d + UPP;
1169  if (timetrack)
1170  warpedD += timetrack->ComputeWarpedLength(d, nextD);
1171  else
1172  warpedD = nextD;
1173  d = nextD;
1174 
1175  if (floor(sg * warpedD / denom) > step) {
1176  step = floor(sg * warpedD / denom);
1177  bool major = jj == 0;
1178  Tick(i, sg * step * denom, major, !major);
1179  }
1180  }
1181  }
1182 
1183  // Left and Right Edges
1184  if (mLabelEdges) {
1185  Tick(0, mMin, true, false);
1186  Tick(mLength, mMax, true, false);
1187  }
1188  }
1189  else {
1190  // log case
1191 
1192  NumberScale numberScale(mpNumberScale
1193  ? *mpNumberScale
1195  );
1196 
1197  mDigits=2; //TODO: implement dynamic digit computation
1198  double loLog = log10(mMin);
1199  double hiLog = log10(mMax);
1200  int loDecade = (int) floor(loLog);
1201 
1202  double val;
1203  double startDecade = pow(10., (double)loDecade);
1204 
1205  // Major ticks are the decades
1206  double decade = startDecade;
1207  double delta=hiLog-loLog, steps=fabs(delta);
1208  double step = delta>=0 ? 10 : 0.1;
1209  double rMin=std::min(mMin, mMax), rMax=std::max(mMin, mMax);
1210  for(i=0; i<=steps; i++)
1211  { // if(i!=0)
1212  { val = decade;
1213  if(val >= rMin && val < rMax) {
1214  const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
1215  Tick(pos, val, true, false);
1216  }
1217  }
1218  decade *= step;
1219  }
1220 
1221  // Minor ticks are multiples of decades
1222  decade = startDecade;
1223  float start, end, mstep;
1224  if (delta > 0)
1225  { start=2; end=10; mstep=1;
1226  }else
1227  { start=9; end=1; mstep=-1;
1228  }
1229  steps++;
1230  for(i=0; i<=steps; i++) {
1231  for(j=start; j!=end; j+=mstep) {
1232  val = decade * j;
1233  if(val >= rMin && val < rMax) {
1234  const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
1235  Tick(pos, val, false, true);
1236  }
1237  }
1238  decade *= step;
1239  }
1240 
1241  // MinorMinor ticks are multiples of decades
1242  decade = startDecade;
1243  if (delta > 0)
1244  { start= 10; end=100; mstep= 1;
1245  }else
1246  { start=100; end= 10; mstep=-1;
1247  }
1248  steps++;
1249  for (i = 0; i <= steps; i++) {
1250  // PRL: Bug1038. Don't label 1.6, rounded, as a duplicate tick for "2"
1251  if (!(mFormat == IntFormat && decade < 10.0)) {
1252  for (int f = start; f != (int)(end); f += mstep) {
1253  if ((int)(f / 10) != f / 10.0f) {
1254  val = decade * f / 10;
1255  if (val >= rMin && val < rMax) {
1256  const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
1257  Tick(pos, val, false, false);
1258  }
1259  }
1260  }
1261  }
1262  decade *= step;
1263  }
1264  }
1265 
1266  int displacementx=0, displacementy=0;
1267  if (!mFlip) {
1268  if (mOrientation==wxHORIZONTAL) {
1269  int d=mTop+mRect.GetHeight()+5;
1270  mRect.Offset(0,d);
1271  mRect.Inflate(0,5);
1272  displacementx=0;
1273  displacementy=d;
1274  }
1275  else {
1276  int d=mLeft-mRect.GetLeft()+5;
1277  mRect.Offset(d,0);
1278  mRect.Inflate(5,0);
1279  displacementx=d;
1280  displacementy=0;
1281  }
1282  }
1283  else {
1284  if (mOrientation==wxHORIZONTAL) {
1285  mRect.Inflate(0,5);
1286  displacementx=0;
1287  displacementy=0;
1288  }
1289  }
1290  for(i=0; i<mNumMajor; i++) {
1291  mMajorLabels[i].lx+= displacementx;
1292  mMajorLabels[i].ly+= displacementy;
1293  }
1294  for(i=0; i<mNumMinor; i++) {
1295  mMinorLabels[i].lx+= displacementx;
1296  mMinorLabels[i].ly+= displacementy;
1297  }
1298  for(i=0; i<mNumMinorMinor; i++) {
1299  mMinorMinorLabels[i].lx+= displacementx;
1300  mMinorMinorLabels[i].ly+= displacementy;
1301  }
1302  mMaxWidth = mRect.GetWidth ();
1303  mMaxHeight= mRect.GetHeight();
1304  mValid = true;
1305 }
1306 
1307 void Ruler::Draw(wxDC& dc)
1308 {
1309  Draw( dc, NULL);
1310 }
1311 
1312 void Ruler::Draw(wxDC& dc, const TimeTrack* timetrack)
1313 {
1314  mDC = &dc;
1315  if( mLength <=0 )
1316  return;
1317 
1318  if (!mValid)
1319  Update(timetrack);
1320 
1321  mDC->SetTextForeground( mTickColour );
1322 #ifdef EXPERIMENTAL_THEMING
1323  mDC->SetPen(mPen);
1324 #else
1325  mDC->SetPen(*wxBLACK_PEN);
1326 #endif
1327 
1328  // Draws a long line the length of the ruler.
1329  if( !mbTicksOnly )
1330  {
1331  if (mOrientation == wxHORIZONTAL) {
1332  if (mFlip)
1333  mDC->DrawLine(mLeft, mTop, mRight+1, mTop);
1334  else
1335  mDC->DrawLine(mLeft, mBottom, mRight+1, mBottom);
1336  }
1337  else {
1338  if (mFlip)
1339  mDC->DrawLine(mLeft, mTop, mLeft, mBottom+1);
1340  else
1341  {
1342  // These calculations appear to be wrong, and to never have been used (so not tested) prior to MixerBoard.
1343  // mDC->DrawLine(mRect.x-mRect.width, mTop, mRect.x-mRect.width, mBottom+1);
1344  const int nLineX = mRight - 1;
1345  mDC->DrawLine(nLineX, mTop, nLineX, mBottom+1);
1346  }
1347  }
1348  }
1349 
1350  int i;
1351 
1352  mDC->SetFont(*mMajorFont);
1353 
1354  // We may want to not show the ticks at the extremes,
1355  // though still showing the labels.
1356  // This gives a better look when the ruler is on a bevelled
1357  // button, since otherwise the tick is drawn on the bevel.
1358  int iMaxPos = (mOrientation==wxHORIZONTAL)? mRight : mBottom-5;
1359 
1360  for(i=0; i<mNumMajor; i++) {
1361  int pos = mMajorLabels[i].pos;
1362 
1363  if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos)))
1364  {
1365  if (mOrientation == wxHORIZONTAL) {
1366  if (mFlip)
1367  mDC->DrawLine(mLeft + pos, mTop,
1368  mLeft + pos, mTop + 4);
1369  else
1370  mDC->DrawLine(mLeft + pos, mBottom - 4,
1371  mLeft + pos, mBottom);
1372  }
1373  else {
1374  if (mFlip)
1375  mDC->DrawLine(mLeft, mTop + pos,
1376  mLeft + 4, mTop + pos);
1377  else
1378  mDC->DrawLine(mRight - 4, mTop + pos,
1379  mRight, mTop + pos);
1380  }
1381  }
1382 
1383  mMajorLabels[i].Draw(*mDC, mTwoTone, mTickColour);
1384  }
1385 
1386  if(mbMinor == true) {
1387  mDC->SetFont(*mMinorFont);
1388  for(i=0; i<mNumMinor; i++) {
1389  int pos = mMinorLabels[i].pos;
1390  if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos)))
1391  {
1392  if (mOrientation == wxHORIZONTAL)
1393  {
1394  if (mFlip)
1395  mDC->DrawLine(mLeft + pos, mTop,
1396  mLeft + pos, mTop + 2);
1397  else
1398  mDC->DrawLine(mLeft + pos, mBottom - 2,
1399  mLeft + pos, mBottom);
1400  }
1401  else
1402  {
1403  if (mFlip)
1404  mDC->DrawLine(mLeft, mTop + pos,
1405  mLeft + 2, mTop + pos);
1406  else
1407  mDC->DrawLine(mRight - 2, mTop + pos,
1408  mRight, mTop + pos);
1409  }
1410  }
1411  mMinorLabels[i].Draw(*mDC, mTwoTone, mTickColour);
1412  }
1413  }
1414 
1415  mDC->SetFont(*mMinorMinorFont);
1416 
1417  for(i=0; i<mNumMinorMinor; i++) {
1418  if (mMinorMinorLabels[i].text != wxT(""))
1419  {
1420  int pos = mMinorMinorLabels[i].pos;
1421 
1422  if( mbTicksAtExtremes || ((pos!=0)&&(pos!=iMaxPos)))
1423  {
1424  if (mOrientation == wxHORIZONTAL)
1425  {
1426  if (mFlip)
1427  mDC->DrawLine(mLeft + pos, mTop,
1428  mLeft + pos, mTop + 2);
1429  else
1430  mDC->DrawLine(mLeft + pos, mBottom - 2,
1431  mLeft + pos, mBottom);
1432  }
1433  else
1434  {
1435  if (mFlip)
1436  mDC->DrawLine(mLeft, mTop + pos,
1437  mLeft + 2, mTop + pos);
1438  else
1439  mDC->DrawLine(mRight - 2, mTop + pos,
1440  mRight, mTop + pos);
1441  }
1442  }
1444  }
1445  }
1446 }
1447 
1448 // ********** Draw grid ***************************
1449 void Ruler::DrawGrid(wxDC& dc, int length, bool minor, bool major, int xOffset, int yOffset)
1450 {
1451  mGridLineLength = length;
1452  mMajorGrid = major;
1453  mMinorGrid = minor;
1454  mDC = &dc;
1455 
1456  Update();
1457 
1458  int gridPos;
1459  wxPen gridPen;
1460 
1461  if(mbMinor && (mMinorGrid && (mGridLineLength != 0 ))) {
1462  gridPen.SetColour(178, 178, 178); // very light grey
1463  mDC->SetPen(gridPen);
1464  for(int i=0; i<mNumMinor; i++) {
1465  gridPos = mMinorLabels[i].pos;
1466  if(mOrientation == wxHORIZONTAL) {
1467  if((gridPos != 0) && (gridPos != mGridLineLength))
1468  mDC->DrawLine(gridPos+xOffset, yOffset, gridPos+xOffset, mGridLineLength+yOffset);
1469  }
1470  else {
1471  if((gridPos != 0) && (gridPos != mGridLineLength))
1472  mDC->DrawLine(xOffset, gridPos+yOffset, mGridLineLength+xOffset, gridPos+yOffset);
1473  }
1474  }
1475  }
1476 
1477  if(mMajorGrid && (mGridLineLength != 0 )) {
1478  gridPen.SetColour(127, 127, 127); // light grey
1479  mDC->SetPen(gridPen);
1480  for(int i=0; i<mNumMajor; i++) {
1481  gridPos = mMajorLabels[i].pos;
1482  if(mOrientation == wxHORIZONTAL) {
1483  if((gridPos != 0) && (gridPos != mGridLineLength))
1484  mDC->DrawLine(gridPos+xOffset, yOffset, gridPos+xOffset, mGridLineLength+yOffset);
1485  }
1486  else {
1487  if((gridPos != 0) && (gridPos != mGridLineLength))
1488  mDC->DrawLine(xOffset, gridPos+yOffset, mGridLineLength+xOffset, gridPos+yOffset);
1489  }
1490  }
1491 
1492  int zeroPosition = GetZeroPosition();
1493  if(zeroPosition > 0) {
1494  // Draw 'zero' grid line in black
1495  mDC->SetPen(*wxBLACK_PEN);
1496  if(mOrientation == wxHORIZONTAL) {
1497  if(zeroPosition != mGridLineLength)
1498  mDC->DrawLine(zeroPosition+xOffset, yOffset, zeroPosition+xOffset, mGridLineLength+yOffset);
1499  }
1500  else {
1501  if(zeroPosition != mGridLineLength)
1502  mDC->DrawLine(xOffset, zeroPosition+yOffset, mGridLineLength+xOffset, zeroPosition+yOffset);
1503  }
1504  }
1505  }
1506 }
1507 
1508 int Ruler::FindZero(Label * label, const int len)
1509 {
1510  int i = 0;
1511  double d = 1.0; // arbitrary
1512 
1513  do {
1514  d = label[i].value;
1515  i++;
1516  } while( (i < len) && (d != 0.0) );
1517 
1518  if(d == 0.0)
1519  return (label[i - 1].pos) ;
1520  else
1521  return -1;
1522 }
1523 
1525 {
1526  int zero;
1527  if((zero = FindZero(mMajorLabels.get(), mNumMajor)) < 0)
1528  zero = FindZero(mMinorLabels.get(), mNumMinor);
1529  // PRL: don't consult minor minor??
1530  return zero;
1531 }
1532 
1533 void Ruler::GetMaxSize(wxCoord *width, wxCoord *height)
1534 {
1535  if (!mValid) {
1536  wxScreenDC sdc;
1537  mDC = &sdc;
1538  Update(NULL);
1539  }
1540 
1541  if (width)
1542  *width = mRect.GetWidth(); //mMaxWidth;
1543 
1544  if (height)
1545  *height = mRect.GetHeight(); //mMaxHeight;
1546 }
1547 
1548 
1549 void Ruler::SetCustomMode(bool value) { mCustom = value; }
1550 
1551 void Ruler::SetCustomMajorLabels(wxArrayString *label, size_t numLabel, int start, int step)
1552 {
1553  mNumMajor = numLabel;
1554  mMajorLabels.reinit(numLabel);
1555 
1556  for(size_t i = 0; i<numLabel; i++) {
1557  mMajorLabels[i].text = label->Item(i);
1558  mMajorLabels[i].pos = start + i*step;
1559  }
1560  //Remember: DELETE majorlabels....
1561 }
1562 
1563 void Ruler::SetCustomMinorLabels(wxArrayString *label, size_t numLabel, int start, int step)
1564 {
1565  mNumMinor = numLabel;
1566  mMinorLabels.reinit(numLabel);
1567 
1568  for(size_t i = 0; i<numLabel; i++) {
1569  mMinorLabels[i].text = label->Item(i);
1570  mMinorLabels[i].pos = start + i*step;
1571  }
1572  //Remember: DELETE majorlabels....
1573 }
1574 
1575 void Ruler::Label::Draw(wxDC&dc, bool twoTone, wxColour c) const
1576 {
1577  if (text != wxT("")) {
1578  bool altColor = twoTone && value < 0.0;
1579 
1580 #ifdef EXPERIMENTAL_THEMING
1581  dc.SetTextForeground(altColor ? theTheme.Colour( clrTextNegativeNumbers) : c);
1582 #else
1583  dc.SetTextForeground(altColor ? *wxBLUE : *wxBLACK);
1584 #endif
1585 
1586  dc.DrawText(text, lx, ly);
1587  }
1588 }
1589 
1590 void Ruler::SetUseZoomInfo(int leftOffset, const ZoomInfo *zoomInfo)
1591 {
1592  mLeftOffset = leftOffset;
1593  mUseZoomInfo = zoomInfo;
1594 }
1595 
1596 //
1597 // RulerPanel
1598 //
1599 
1600 BEGIN_EVENT_TABLE(RulerPanel, wxPanelWrapper)
1601  EVT_ERASE_BACKGROUND(RulerPanel::OnErase)
1602  EVT_PAINT(RulerPanel::OnPaint)
1603  EVT_SIZE(RulerPanel::OnSize)
1605 
1607 
1608 RulerPanel::RulerPanel(wxWindow* parent, wxWindowID id,
1609  wxOrientation orientation,
1610  const wxSize &bounds,
1611  const Range &range,
1613  const wxString &units,
1614  const Options &options,
1615  const wxPoint& pos /*= wxDefaultPosition*/,
1616  const wxSize& size /*= wxDefaultSize*/):
1617  wxPanelWrapper(parent, id, pos, size)
1618 {
1619  ruler.SetBounds( 0, 0, bounds.x, bounds.y );
1620  ruler.SetOrientation(orientation);
1621  ruler.SetRange( range.first, range.second );
1622  ruler.SetLog( options.log );
1623  ruler.SetFormat(format);
1624  ruler.SetUnits( units );
1625  ruler.SetFlip( options.flip );
1626  ruler.SetLabelEdges( options.labelEdges );
1627  ruler.mbTicksAtExtremes = options.ticksAtExtremes;
1628  if (orientation == wxVERTICAL) {
1629  wxCoord w;
1630  ruler.GetMaxSize(&w, NULL);
1631  SetMinSize(wxSize(w, 150)); // height needed for wxGTK
1632  }
1633  else if (orientation == wxHORIZONTAL) {
1634  wxCoord h;
1635  ruler.GetMaxSize(NULL, &h);
1636  SetMinSize(wxSize(wxDefaultCoord, h));
1637  }
1638  if (options.hasTickColour)
1639  ruler.SetTickColour( options.tickColour );
1640 }
1641 
1643 {
1644 }
1645 
1646 void RulerPanel::OnErase(wxEraseEvent & WXUNUSED(evt))
1647 {
1648  // Ignore it to prevent flashing
1649 }
1650 
1651 void RulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
1652 {
1653  wxPaintDC dc(this);
1654 
1655 #if defined(__WXMSW__)
1656  dc.Clear();
1657 #endif
1658 
1659  ruler.Draw(dc);
1660 }
1661 
1662 void RulerPanel::OnSize(wxSizeEvent & WXUNUSED(evt))
1663 {
1664  Refresh();
1665 }
1666 
1667 // LL: We're overloading DoSetSize so that we can update the ruler bounds immediately
1668 // instead of waiting for a wxEVT_SIZE to come through. This is needed by (at least)
1669 // FreqWindow since it needs to have an updated ruler before RulerPanel gets the
1670 // size event.
1671 void RulerPanel::DoSetSize(int x, int y,
1672  int width, int height,
1673  int sizeFlags)
1674 {
1675  wxPanelWrapper::DoSetSize(x, y, width, height, sizeFlags);
1676 
1677  int w, h;
1678  GetClientSize(&w, &h);
1679 
1680  ruler.SetBounds(0, 0, w-1, h-1);
1681 }
1682 
1683 
1684 /*********************************************************************/
1685 enum : int {
1689 
1691  BottomMargin = 2, // for bottom bevel and bottom line
1693 
1695 };
1696 
1697 enum {
1700 };
1701 
1702 inline int IndicatorHeightForWidth(int width)
1703 {
1704  return ((width / 2) * 3) / 2;
1705 }
1706 
1707 inline int IndicatorWidthForHeight(int height)
1708 {
1709  // Not an exact inverse of the above, with rounding, but good enough
1710  return std::max(static_cast<int>(IndicatorSmallWidth),
1711  (((height) * 2) / 3) * 2
1712  );
1713 }
1714 
1716 {
1717  return std::max((int)(ScrubHeight - TopMargin),
1718  (int)(IndicatorMediumWidth));
1719 }
1720 
1721 inline int IndicatorBigWidth()
1722 {
1724 }
1725 
1726 /**********************************************************************
1727 
1728 QuickPlayRulerOverlay.
1729 Graphical helper for AdornedRulerPanel.
1730 
1731 **********************************************************************/
1732 
1734 
1735 // This is an overlay drawn on the ruler. It draws the little triangle or
1736 // the double-headed arrow.
1737 class QuickPlayRulerOverlay final : public Overlay
1738 {
1739 public:
1741  virtual ~QuickPlayRulerOverlay();
1742 
1743  void Update(wxCoord xx) { mNewQPIndicatorPos = xx; }
1744 
1745 private:
1746  AdornedRulerPanel *GetRuler() const;
1747 
1748  std::pair<wxRect, bool> DoGetRectangle(wxSize size) override;
1749  void Draw(OverlayPanel &panel, wxDC &dc) override;
1750 
1752  int mOldQPIndicatorPos { -1 }, mNewQPIndicatorPos { -1 };
1753 };
1754 
1755 /**********************************************************************
1756 
1757  QuickPlayIndicatorOverlay.
1758  Graphical helper for AdornedRulerPanel.
1759 
1760  **********************************************************************/
1761 
1762 // This is an overlay drawn on a different window, the track panel.
1763 // It draws the pale guide line that follows mouse movement.
1765 {
1767 
1768 public:
1770 
1771  virtual ~QuickPlayIndicatorOverlay();
1772 
1773  void Update(int x, bool snapped = false, bool previewScrub = false);
1774 
1775 private:
1776  std::pair<wxRect, bool> DoGetRectangle(wxSize size) override;
1777  void Draw(OverlayPanel &panel, wxDC &dc) override;
1778 
1780 
1781  std::unique_ptr<QuickPlayRulerOverlay> mPartner
1782  { std::make_unique<QuickPlayRulerOverlay>(*this) };
1783 
1784  int mOldQPIndicatorPos { -1 }, mNewQPIndicatorPos { -1 };
1785  bool mOldQPIndicatorSnapped {}, mNewQPIndicatorSnapped {};
1786  bool mOldPreviewingScrub {}, mNewPreviewingScrub {};
1787 };
1788 
1789 /**********************************************************************
1790 
1791  Implementation of QuickPlayRulerOverlay.
1792 
1793  **********************************************************************/
1794 
1796 : mPartner(partner)
1797 {
1798  GetRuler()->AddOverlay(this);
1799 }
1800 
1802 {
1803  auto ruler = GetRuler();
1804  if (ruler)
1805  ruler->RemoveOverlay(this);
1806 }
1807 
1809 {
1810  return mPartner.mProject->GetRulerPanel();
1811 }
1812 
1813 std::pair<wxRect, bool> QuickPlayRulerOverlay::DoGetRectangle(wxSize /*size*/)
1814 {
1815  const auto x = mOldQPIndicatorPos;
1816  if (x >= 0) {
1817  // These dimensions are always sufficient, even if a little
1818  // excessive for the small triangle:
1819  const int width = IndicatorBigWidth() * 3 / 2;
1820  //const auto height = IndicatorHeightForWidth(width);
1821 
1822  const int indsize = width / 2;
1823 
1824  auto xx = x - indsize;
1825  auto yy = 0;
1826  return {
1827  { xx, yy,
1828  indsize * 2 + 1,
1829  mPartner.mProject->GetRulerPanel()->GetSize().GetHeight() },
1830  (x != mNewQPIndicatorPos)
1831  };
1832  }
1833  else
1834  return { {}, mNewQPIndicatorPos >= 0 };
1835 }
1836 
1837 void QuickPlayRulerOverlay::Draw(OverlayPanel & /*panel*/, wxDC &dc)
1838 {
1840  if (mOldQPIndicatorPos >= 0) {
1841  auto ruler = GetRuler();
1842  const auto &scrubber = mPartner.mProject->GetScrubber();
1843  auto scrub =
1844  ruler->mMouseEventState == AdornedRulerPanel::mesNone &&
1846  (scrubber.HasStartedScrubbing()));
1847  auto seek = scrub && (scrubber.Seeks() || scrubber.TemporarilySeeks());
1848  auto width = scrub ? IndicatorBigWidth() : IndicatorSmallWidth;
1849  ruler->DoDrawIndicator(&dc, mOldQPIndicatorPos, true, width, scrub, seek);
1850  }
1851 }
1852 
1853 /**********************************************************************
1854 
1855  Implementation of QuickPlayIndicatorOverlay.
1856 
1857  **********************************************************************/
1858 
1860  : mProject(project)
1861 {
1862  auto tp = mProject->GetTrackPanel();
1863  tp->AddOverlay(this);
1864 }
1865 
1867 {
1868  auto tp = mProject->GetTrackPanel();
1869  if (tp)
1870  tp->RemoveOverlay(this);
1871 }
1872 
1873 void QuickPlayIndicatorOverlay::Update(int x, bool snapped, bool previewScrub)
1874 {
1875  mNewQPIndicatorPos = x;
1876  mPartner->Update(x);
1877  mNewQPIndicatorSnapped = snapped;
1878  mNewPreviewingScrub = previewScrub;
1879 }
1880 
1881 std::pair<wxRect, bool> QuickPlayIndicatorOverlay::DoGetRectangle(wxSize size)
1882 {
1883  wxRect rect(mOldQPIndicatorPos, 0, 1, size.GetHeight());
1884  return std::make_pair(
1885  rect,
1889  );
1890 }
1891 
1893 {
1897 
1898  if (mOldQPIndicatorPos >= 0) {
1900  ? AColor::IndicatorColor(&dc, true) // Draw green line for preview.
1902  ? AColor::SnapGuidePen(&dc)
1903  : AColor::Light(&dc, false)
1904  ;
1905 
1906  // Draw indicator in all visible tracks
1907  for ( const auto &data : static_cast<TrackPanel&>(panel).Cells() )
1908  {
1909  Track *const pTrack = dynamic_cast<Track*>(data.first.get());
1910  if (!pTrack)
1911  continue;
1912  const wxRect &rect = data.second;
1913 
1914  // Draw the NEW indicator in its NEW location
1915  AColor::Line(dc,
1917  rect.GetTop(),
1919  rect.GetBottom());
1920  }
1921  }
1922 }
1923 
1924 /**********************************************************************
1925 
1926  Implementation of AdornedRulerPanel.
1927  Either we find a way to make this more generic, Or it will move
1928  out of the widgets subdirectory into its own source file.
1929 
1930 **********************************************************************/
1931 
1932 #include "../ViewInfo.h"
1933 #include "../AColor.h"
1934 
1935 enum {
1942 
1944 };
1945 
1946 BEGIN_EVENT_TABLE(AdornedRulerPanel, OverlayPanel)
1947  EVT_PAINT(AdornedRulerPanel::OnPaint)
1948  EVT_SIZE(AdornedRulerPanel::OnSize)
1949  EVT_MOUSE_EVENTS(AdornedRulerPanel::OnMouseEvents)
1950  EVT_MOUSE_CAPTURE_LOST(AdornedRulerPanel::OnCaptureLost)
1951 
1952  // Context menu commands
1953  EVT_MENU(OnToggleQuickPlayID, AdornedRulerPanel::OnToggleQuickPlay)
1954  EVT_MENU(OnSyncQuickPlaySelID, AdornedRulerPanel::OnSyncSelToQuickPlay)
1955  EVT_MENU(OnTimelineToolTipID, AdornedRulerPanel::OnTimelineToolTips)
1956  EVT_MENU(OnAutoScrollID, AdornedRulerPanel::OnAutoScroll)
1958  EVT_MENU(OnScrubRulerID, AdornedRulerPanel::OnToggleScrubRulerFromMenu)
1959 
1960  // Pop up menus on Windows
1961  EVT_CONTEXT_MENU(AdornedRulerPanel::OnContextMenu)
1962 
1964  wxEVT_COMMAND_BUTTON_CLICKED,
1965  AdornedRulerPanel::OnTogglePinnedState )
1966 
1968 
1970  wxWindow *parent,
1971  wxWindowID id,
1972  const wxPoint& pos,
1973  const wxSize& size,
1974  ViewInfo *viewinfo)
1975 : OverlayPanel(parent, id, pos, size)
1976 , mProject(project)
1977 , mViewInfo(viewinfo)
1978 {
1979  for (auto &button : mButtons)
1980  button = nullptr;
1981 
1982  SetLabel( _("Timeline") );
1983  SetName(GetLabel());
1984  SetBackgroundStyle(wxBG_STYLE_PAINT);
1985 
1986  mCursorDefault = wxCursor(wxCURSOR_DEFAULT);
1987  mCursorHand = wxCursor(wxCURSOR_HAND);
1988  mCursorSizeWE = wxCursor(wxCURSOR_SIZEWE);
1989  mIsWE = false;
1990 
1991  mLeftOffset = 0;
1992  mIndTime = -1;
1993 
1994  mPlayRegionStart = -1;
1995  mPlayRegionLock = false;
1996  mPlayRegionEnd = -1;
1997  mOldPlayRegionStart = -1;
1998  mOldPlayRegionEnd = -1;
1999  mLeftDownClick = -1;
2000  mMouseEventState = mesNone;
2001  mIsDragging = false;
2002 
2003  mOuter = GetClientRect();
2004 
2005  mRuler.SetUseZoomInfo(mLeftOffset, mViewInfo);
2006  mRuler.SetLabelEdges( false );
2007  mRuler.SetFormat( Ruler::TimeFormat );
2008 
2009  mTracks = project->GetTracks();
2010 
2011  mSnapManager = NULL;
2012  mIsSnapped = false;
2013 
2014  mIsRecording = false;
2015 
2016  mTimelineToolTip = !!gPrefs->Read(wxT("/QuickPlay/ToolTips"), 1L);
2017  mPlayRegionDragsSelection = (gPrefs->Read(wxT("/QuickPlay/DragSelection"), 0L) == 1)? true : false;
2018  mQuickPlayEnabled = !!gPrefs->Read(wxT("/QuickPlay/QuickPlayEnabled"), 1L);
2019 
2020 #if wxUSE_TOOLTIPS
2021  wxToolTip::Enable(true);
2022 #endif
2023 
2024  wxTheApp->Bind(EVT_AUDIOIO_CAPTURE,
2026  this);
2027 }
2028 
2030 {
2031  if(HasCapture())
2032  ReleaseMouse();
2033 }
2034 
2035 #if 1
2036 namespace {
2037  static const wxChar *scrubEnabledPrefName = wxT("/QuickPlay/ScrubbingEnabled");
2038 
2039  bool ReadScrubEnabledPref()
2040  {
2041  bool result {};
2042 // DA: Scrub is disabled by default.
2043 #ifdef EXPERIMENTAL_DA
2044  gPrefs->Read(scrubEnabledPrefName, &result, false);
2045 #else
2046  gPrefs->Read(scrubEnabledPrefName, &result, true);
2047 #endif
2048  return result;
2049  }
2050 
2051  void WriteScrubEnabledPref(bool value)
2052  {
2053  gPrefs->Write(scrubEnabledPrefName, value);
2054  }
2055 }
2056 #endif
2057 
2059 {
2060  // Update button texts for language change
2062 
2063 #ifdef EXPERIMENTAL_SCROLLING_LIMITS
2064 #ifdef EXPERIMENTAL_TWO_TONE_TIME_RULER
2065  {
2066  bool scrollBeyondZero = false;
2067  gPrefs->Read(TracksBehaviorsPrefs::ScrollingPreferenceKey(), &scrollBeyondZero,
2069  mRuler.SetTwoTone(scrollBeyondZero);
2070  }
2071 #endif
2072 #endif
2073 
2074  mShowScrubbing = ReadScrubEnabledPref();
2075  // Affected by the last
2076  UpdateRects();
2077  SetPanelSize();
2078 
2080 }
2081 
2083 {
2084  // TODO: Should we do this to destroy the grabber??
2085  // Get rid of any children we may have
2086  // DestroyChildren();
2087 
2088  SetBackgroundColour(theTheme.Colour( clrMedium ));
2089 
2090  for (auto & button : mButtons) {
2091  if (button)
2092  button->Destroy();
2093  button = nullptr;
2094  }
2095 
2096  size_t iButton = 0;
2097  // Make the short row of time ruler pushbottons.
2098  // Don't bother with sizers. Their sizes and positions are fixed.
2099  // Add a grabber converted to a spacer.
2100  // This makes it visually clearer that the button is a button.
2101 
2102  wxPoint position( 1, 0 );
2103 
2104  Grabber * pGrabber = safenew Grabber(this, this->GetId());
2105  pGrabber->SetAsSpacer( true );
2106  //pGrabber->SetSize( 10, 27 ); // default is 10,27
2107  pGrabber->SetPosition( position );
2108 
2109  position.x = 12;
2110 
2111  auto size = theTheme.ImageSize( bmpRecoloredUpSmall );
2112  size.y = std::min(size.y, GetRulerHeight(false));
2113 
2114  auto buttonMaker = [&]
2115  (wxWindowID id, teBmps bitmap, bool toggle)
2116  {
2117  const auto button =
2119  this,
2120  bmpRecoloredUpSmall, bmpRecoloredDownSmall,
2121  bmpRecoloredUpHiliteSmall, bmpRecoloredHiliteSmall,
2122  bitmap, bitmap, bitmap,
2123  id, position, toggle, size
2124  );
2125 
2126  position.x += size.GetWidth();
2127  mButtons[iButton++] = button;
2128  return button;
2129  };
2130  auto button = buttonMaker(OnTogglePinnedStateID, bmpPlayPointerPinned, true);
2132  *button, 1,
2133  bmpRecoloredUpSmall, bmpRecoloredDownSmall,
2134  bmpRecoloredUpHiliteSmall, bmpRecoloredHiliteSmall,
2135  //bmpUnpinnedPlayHead, bmpUnpinnedPlayHead, bmpUnpinnedPlayHead,
2136  bmpPlayPointer, bmpPlayPointer, bmpPlayPointer,
2137  size);
2138 
2140 }
2141 
2143 {
2144  mRuler.Invalidate();
2145 }
2146 
2147 namespace {
2148  const wxString StartScrubbingMessage(const Scrubber &/*scrubber*/)
2149  {
2150  /* i18n-hint: These commands assist the user in finding a sound by ear. ...
2151  "Scrubbing" is variable-speed playback, ...
2152  "Seeking" is normal speed playback but with skips
2153  */
2154 #if 0
2155  if(scrubber.Seeks())
2156  return _("Click or drag to begin Seek");
2157  else
2158  return _("Click or drag to begin Scrub");
2159 #else
2160  return _("Click & move to Scrub. Click & drag to Seek.");
2161 #endif
2162  }
2163 
2164  const wxString ContinueScrubbingMessage(const Scrubber &scrubber)
2165  {
2166  /* i18n-hint: These commands assist the user in finding a sound by ear. ...
2167  "Scrubbing" is variable-speed playback, ...
2168  "Seeking" is normal speed playback but with skips
2169  */
2170 #if 0
2171  if(scrubber.Seeks())
2172  return _("Move to Seek");
2173  else
2174  return _("Move to Scrub");
2175 #else
2176  wxMouseState State = wxGetMouseState();
2177  if( State.LeftIsDown() ) {
2178  // Since mouse is down, mention dragging first.
2179  // IsScrubbing is true if Scrubbing OR seeking.
2180  if( scrubber.IsOneShotSeeking() )
2181  return _("Drag to Seek. Release to stop seeking.");
2182  else
2183  return _("Drag to Seek. Release and move to Scrub.");
2184  }
2185  // Since mouse is up, mention moving first.
2186  return _("Move to Scrub. Drag to Seek.");
2187 #endif
2188  }
2189 
2190  const wxString ScrubbingMessage(const Scrubber &scrubber)
2191  {
2192  if (scrubber.HasStartedScrubbing())
2193  return ContinueScrubbingMessage(scrubber);
2194  else
2195  return StartScrubbingMessage(scrubber);
2196  }
2197 }
2198 
2200 {
2201 #if wxUSE_TOOLTIPS
2202  if (mTimelineToolTip) {
2203  if (mIsRecording) {
2204  this->SetToolTip(_("Timeline actions disabled during recording"));
2205  }
2206  else {
2207  switch(choice) {
2209  if (!mQuickPlayEnabled) {
2210  this->SetToolTip(_("Quick-Play disabled"));
2211  }
2212  else {
2213  this->SetToolTip(_("Quick-Play enabled"));
2214  }
2215  break;
2217  {
2218  const auto message = ScrubbingMessage(mProject->GetScrubber());
2219  this->SetToolTip(message);
2220  }
2221  break;
2222  default:
2223  this->SetToolTip(NULL);
2224  break;
2225  }
2226  }
2227  }
2228  else {
2229  this->SetToolTip(NULL);
2230  }
2231 #endif
2232 }
2233 
2234 void AdornedRulerPanel::OnCapture(wxCommandEvent & evt)
2235 {
2236  evt.Skip();
2237 
2238  if (evt.GetInt() != 0)
2239  {
2240  // Set cursor immediately because OnMouseEvents is not called
2241  // if recording is initiated by a modal window (Timer Record).
2242  SetCursor(mCursorDefault);
2243  mIsRecording = true;
2244 
2245  // The quick play indicator is useless during recording
2247  }
2248  else {
2249  SetCursor(mCursorHand);
2250  mIsRecording = false;
2251  }
2253 }
2254 
2255 void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
2256 {
2257  if (mNeedButtonUpdate) {
2258  // Visit this block once only in the lifetime of this panel
2259  mNeedButtonUpdate = false;
2260  // Do this first time setting of button status texts
2261  // when we are sure the CommandManager is initialized.
2262  ReCreateButtons();
2263  // Sends a resize event, which will cause a second paint.
2264  UpdatePrefs();
2265  }
2266 
2267  wxPaintDC dc(this);
2268 
2269  auto &backDC = GetBackingDCForRepaint();
2270 
2271  DoDrawBackground(&backDC);
2272 
2274  {
2275  DoDrawSelection(&backDC);
2276  }
2277 
2278  DoDrawMarks(&backDC, true);
2279 
2280  DoDrawPlayRegion(&backDC);
2281 
2282  DoDrawEdge(&backDC);
2283 
2284  DisplayBitmap(dc);
2285 
2286  // Stroke extras direct to the client area,
2287  // maybe outside of the damaged area
2288  // As with TrackPanel, do not make a NEW wxClientDC or else Mac flashes badly!
2289  dc.DestroyClippingRegion();
2290  DrawOverlays(true, &dc);
2291 }
2292 
2293 void AdornedRulerPanel::OnSize(wxSizeEvent &evt)
2294 {
2295  mOuter = GetClientRect();
2296  if (mOuter.GetWidth() == 0 || mOuter.GetHeight() == 0)
2297  {
2298  return;
2299  }
2300 
2301  UpdateRects();
2302 
2303  OverlayPanel::OnSize(evt);
2304 }
2305 
2307 {
2308  mInner = mOuter;
2309  mInner.x += LeftMargin;
2310  mInner.width -= (LeftMargin + RightMargin);
2311 
2312  auto top = &mInner;
2313  auto bottom = &mInner;
2314 
2315  if (mShowScrubbing) {
2316  mScrubZone = mInner;
2317  auto scrubHeight = std::min(mScrubZone.height, (int)(ScrubHeight));
2318 
2319  int topHeight;
2320 #ifdef SCRUB_ABOVE
2321  top = &mScrubZone, topHeight = scrubHeight;
2322 #else
2323  auto qpHeight = mScrubZone.height - scrubHeight;
2324  bottom = &mScrubZone, topHeight = qpHeight;
2325  // Increase scrub zone height so that hit testing finds it and
2326  // not QP region, when on bottom 'edge'.
2327  mScrubZone.height+=BottomMargin;
2328 #endif
2329 
2330  top->height = topHeight;
2331  bottom->height -= topHeight;
2332  bottom->y += topHeight;
2333  }
2334 
2335  top->y += TopMargin;
2336  top->height -= TopMargin;
2337 
2338  bottom->height -= BottomMargin;
2339 
2340  if (!mShowScrubbing)
2341  mScrubZone = mInner;
2342 
2343  mRuler.SetBounds(mInner.GetLeft(),
2344  mInner.GetTop(),
2345  mInner.GetRight(),
2346  mInner.GetBottom());
2347 
2348 }
2349 
2350 double AdornedRulerPanel::Pos2Time(int p, bool ignoreFisheye)
2351 {
2353  , ignoreFisheye
2354  );
2355 }
2356 
2357 int AdornedRulerPanel::Time2Pos(double t, bool ignoreFisheye)
2358 {
2360  , ignoreFisheye
2361  );
2362 }
2363 
2364 
2365 bool AdornedRulerPanel::IsWithinMarker(int mousePosX, double markerTime)
2366 {
2367  if (markerTime < 0)
2368  return false;
2369 
2370  int pixelPos = Time2Pos(markerTime);
2371  int boundLeft = pixelPos - SELECT_TOLERANCE_PIXEL;
2372  int boundRight = pixelPos + SELECT_TOLERANCE_PIXEL;
2373 
2374  return mousePosX >= boundLeft && mousePosX < boundRight;
2375 }
2376 
2377 void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
2378 {
2379  // Disable mouse actions on Timeline while recording.
2380  if (mIsRecording) {
2381  if (HasCapture())
2382  ReleaseMouse();
2383  return;
2384  }
2385 
2386  const auto position = evt.GetPosition();
2387 
2388  const bool inScrubZone =
2389  // only if scrubbing is allowed now
2390  mProject->GetScrubber().CanScrub() &&
2391  mShowScrubbing &&
2392  mScrubZone.Contains(position);
2393  const bool inQPZone =
2394  (!inScrubZone) && mInner.Contains(position);
2395 
2396  const StatusChoice zone =
2397  evt.Leaving()
2399  : inScrubZone
2401  : inQPZone
2404  const bool changeInZone = (zone != mPrevZone);
2405  const bool changing = evt.Leaving() || evt.Entering() || changeInZone;
2406 
2407  wxCoord xx = evt.GetX();
2408  wxCoord mousePosX = xx;
2409  UpdateQuickPlayPos(mousePosX);
2410  HandleSnapping();
2411 
2412  // If not looping, restrict selection to end of project
2413  if (zone == StatusChoice::EnteringQP && !evt.ShiftDown()) {
2414  const double t1 = mTracks->GetEndTime();
2416  }
2417 
2418  // Handle status bar messages
2420 
2421  mPrevZone = zone;
2422 
2423  auto &scrubber = mProject->GetScrubber();
2424  if (scrubber.HasStartedScrubbing()) {
2425  if (evt.RightDown() )
2426  // Fall through to context menu handling
2427  ;
2428  else if ( evt.LeftUp() && inScrubZone)
2429  // Fall through to seeking changes to scrubbing
2430  ;
2431 // else if ( evt.LeftDown() && inScrubZone)
2432 // // Fall through to ready to seek
2433 // ;
2434  else {
2435  bool switchToQP = (zone == StatusChoice::EnteringQP && mQuickPlayEnabled);
2436  if (switchToQP && evt.LeftDown()) {
2437  // We can't stop scrubbing yet (see comments in Bug 1391), but we can pause it.
2439  // Don't return, fall through
2440  }
2441  else if (scrubber.IsPaused())
2442  // Just fall through
2443  ;
2444  else {
2445  // If already clicked for scrub, preempt the usual event handling,
2446  // no matter what the y coordinate.
2447 
2448  // Do this hack so scrubber can detect mouse drags anywhere
2449  evt.ResumePropagation(wxEVENT_PROPAGATE_MAX);
2450 
2451  //if (scrubber.IsScrubbing())
2452  evt.Skip();
2453  //else
2454  //evt.Skip();
2455 
2456  // Don't do this, it slows down drag-scrub on Mac.
2457  // Timer updates of display elsewhere make it unnecessary.
2458  // Done here, it's too frequent.
2459  // ShowQuickPlayIndicator();
2460 
2461  return;
2462  }
2463  }
2464  }
2465 
2466  // Store the initial play region state
2467  if(mMouseEventState == mesNone) {
2471  }
2472 
2473  // Handle entering and leaving of the bar, or movement from
2474  // one portion (quick play or scrub) to the other
2475  if (evt.Leaving() || (changeInZone && zone != StatusChoice::EnteringQP)) {
2476  if (evt.Leaving()) {
2477  // Erase the line
2479  }
2480 
2481  SetCursor(mCursorDefault);
2482  mIsWE = false;
2483 
2484  mSnapManager.reset();
2485 
2486  if(evt.Leaving())
2487  return;
2488  // else, may detect a scrub click below
2489  }
2490  else if (evt.Entering() || (changeInZone && zone == StatusChoice::EnteringQP)) {
2491  SetCursor(mCursorHand);
2493  return;
2494  }
2495 
2496  // Handle popup menus
2497  if (!HasCapture() && evt.RightDown() && !(evt.LeftIsDown())) {
2499  (inScrubZone ? MenuChoice::Scrub : MenuChoice::QuickPlay,
2500  &position);
2501  return;
2502  }
2503  else if( !HasCapture() && evt.LeftUp() && inScrubZone ) {
2504  if( scrubber.IsOneShotSeeking() ){
2505  scrubber.mInOneShotMode = false;
2506  return;
2507  }
2508  //wxLogDebug("up");
2509  // mouse going up => we shift to scrubbing.
2510  scrubber.MarkScrubStart(evt.m_x,
2513  // repaint_all so that the indicator changes shape.
2514  bool repaint_all = true;
2515  ShowQuickPlayIndicator(repaint_all);
2516  return;
2517  }
2518  else if ( !HasCapture() && inScrubZone) {
2519  // mouse going down => we are (probably) seeking
2520  bool repaint_all = false;
2521  if (evt.LeftDown()) {
2522  //wxLogDebug("down");
2523  scrubber.mInOneShotMode = !scrubber.IsScrubbing();
2524  scrubber.MarkScrubStart(evt.m_x,
2527  }
2528  else if( changeInZone ) {
2529  repaint_all = true;
2530  }
2531 
2532  ShowQuickPlayIndicator(repaint_all);
2533  return;
2534  }
2535  else if ( mQuickPlayEnabled) {
2536  bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart);
2537  bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd);
2538 
2539  if (isWithinStart || isWithinEnd) {
2540  if (!mIsWE) {
2541  SetCursor(mCursorSizeWE);
2542  mIsWE = true;
2543  }
2544  }
2545  else {
2546  if (mIsWE) {
2547  SetCursor(mCursorHand);
2548  mIsWE = false;
2549  }
2550  }
2551 
2552  if (evt.LeftDown()) {
2553  if( inQPZone ){
2554  HandleQPClick(evt, mousePosX);
2555  HandleQPDrag(evt, mousePosX);
2557  }
2558  }
2559  else if (evt.LeftIsDown() && HasCapture()) {
2560  HandleQPDrag(evt, mousePosX);
2562  }
2563  else if (evt.LeftUp() && HasCapture()) {
2564  HandleQPRelease(evt);
2566  }
2567  else // if (!inScrubZone)
2569  }
2570 }
2571 
2572 void AdornedRulerPanel::HandleQPClick(wxMouseEvent &evt, wxCoord mousePosX)
2573 {
2574  // Temporarily unlock locked play region
2575  if (mPlayRegionLock && evt.LeftDown()) {
2576  //mPlayRegionLock = true;
2578  }
2579 
2582  bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart);
2583  bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd);
2584 
2585  if (isWithinStart || isWithinEnd) {
2586  // If Quick-Play is playing from a point, we need to treat it as a click
2587  // not as dragging.
2590  // otherwise check which marker is nearer
2591  else {
2592  // Don't compare times, compare positions.
2593  //if (fabs(mQuickPlayPos - mPlayRegionStart) < fabs(mQuickPlayPos - mPlayRegionEnd))
2597  else
2599  }
2600  }
2601  else {
2602  // Clicked but not yet dragging
2604  }
2605 
2606  // Check if we are dragging BEFORE CaptureMouse.
2607  if (mMouseEventState != mesNone)
2608  SetCursor(mCursorSizeWE);
2609  if ( !HasCapture() )
2610  CaptureMouse();
2611 }
2612 
2613 void AdornedRulerPanel::HandleQPDrag(wxMouseEvent &/*event*/, wxCoord mousePosX)
2614 {
2615  bool isWithinClick =
2616  (mLeftDownClickUnsnapped >= 0) &&
2618  bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart);
2619  bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd);
2620  bool canDragSel = !mPlayRegionLock && mPlayRegionDragsSelection;
2621 
2622  switch (mMouseEventState)
2623  {
2624  case mesNone:
2625  // If close to either end of play region, snap to closest
2626  if (isWithinStart || isWithinEnd) {
2628 
2631  else
2633  }
2634  break;
2637 
2638  // Don't start dragging until beyond tollerance initial playback start
2639  if (!mIsDragging && isWithinStart)
2641  else
2642  mIsDragging = true;
2643  // avoid accidental tiny selection
2644  if (isWithinEnd)
2647  if (canDragSel) {
2648  DragSelection();
2649  }
2650  break;
2652  if (!mIsDragging && isWithinEnd) {
2654 
2656  }
2657  else
2658  mIsDragging = true;
2659  if (isWithinStart) {
2661 
2663  }
2665  if (canDragSel) {
2666  DragSelection();
2667  }
2668  break;
2670 
2671  // Don't start dragging until mouse is beyond tolerance of initial click.
2672  if (isWithinClick || mLeftDownClick == -1) {
2674 
2678  }
2679  else {
2681  }
2682  break;
2684  if (isWithinClick) {
2686 
2688  }
2689 
2690  if (mQuickPlayPos < mLeftDownClick) {
2693  }
2694  else {
2697  }
2698  if (canDragSel) {
2699  DragSelection();
2700  }
2701  break;
2702  }
2703  Refresh();
2704  Update();
2705 }
2706 
2707 void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
2708 {
2709  if (HasCapture())
2710  ReleaseMouse();
2711  else
2712  return;
2713 
2715 
2717  // Swap values to ensure mPlayRegionStart < mPlayRegionEnd
2718  double tmp = mPlayRegionStart;
2720  mPlayRegionEnd = tmp;
2721  }
2722 
2723  const double t0 = mTracks->GetStartTime();
2724  const double t1 = mTracks->GetEndTime();
2725  const double sel0 = mProject->GetSel0();
2726  const double sel1 = mProject->GetSel1();
2727 
2728  // We want some audio in the selection, but we allow a dragged
2729  // region to include selected white-space and space before audio start.
2730  if (evt.ShiftDown() && (mPlayRegionStart == mPlayRegionEnd)) {
2731  // Looping the selection or project.
2732  // Disable if track selection is in white-space beyond end of tracks and
2733  // play position is outside of track contents.
2734  if (((sel1 < t0) || (sel0 > t1)) &&
2735  ((mPlayRegionStart < t0) || (mPlayRegionStart > t1))) {
2736  ClearPlayRegion();
2737  }
2738  }
2739  // Disable if beyond end.
2740  else if (mPlayRegionStart >= t1) {
2741  ClearPlayRegion();
2742  }
2743  // Disable if empty selection before start.
2744  // (allow Quick-Play region to include 'pre-roll' white space)
2745  else if (((mPlayRegionEnd - mPlayRegionStart) > 0.0) && (mPlayRegionEnd < t0)) {
2746  ClearPlayRegion();
2747  }
2748 
2750  mIsDragging = false;
2751  mLeftDownClick = -1;
2752 
2753  auto cleanup = finally( [&] {
2754  if (mPlayRegionLock) {
2755  // Restore Locked Play region
2758  // and release local lock
2759  mPlayRegionLock = false;
2760  }
2761  } );
2762 
2763  StartQPPlay(evt.ShiftDown(), evt.ControlDown());
2764 }
2765 
2766 void AdornedRulerPanel::StartQPPlay(bool looped, bool cutPreview)
2767 {
2768  const double t0 = mTracks->GetStartTime();
2769  const double t1 = mTracks->GetEndTime();
2770  const double sel0 = mProject->GetSel0();
2771  const double sel1 = mProject->GetSel1();
2772 
2773  // Start / Restart playback on left click.
2774  bool startPlaying = (mPlayRegionStart >= 0);
2775 
2776  if (startPlaying) {
2778  ctb->StopPlaying();
2779 
2780  bool loopEnabled = true;
2781  double start, end;
2782 
2783  if ((mPlayRegionEnd - mPlayRegionStart == 0.0) && looped) {
2784  // Loop play a point will loop either a selection or the project.
2785  if ((mPlayRegionStart > sel0) && (mPlayRegionStart < sel1)) {
2786  // we are in a selection, so use the selection
2787  start = sel0;
2788  end = sel1;
2789  } // not in a selection, so use the project
2790  else {
2791  start = t0;
2792  end = t1;
2793  }
2794  }
2795  else {
2796  start = mPlayRegionStart;
2797  end = mPlayRegionEnd;
2798  }
2799  // Looping a tiny selection may freeze, so just play it once.
2800  loopEnabled = ((end - start) > 0.001)? true : false;
2801 
2803  options.playLooped = (loopEnabled && looped);
2804 
2805  auto oldStart = mPlayRegionStart;
2806  if (!cutPreview)
2807  options.pStartTime = &oldStart;
2808  else
2809  options.timeTrack = NULL;
2810 
2811  ControlToolBar::PlayAppearance appearance =
2813  : options.playLooped ? ControlToolBar::PlayAppearance::Looped
2815 
2816  mPlayRegionStart = start;
2817  mPlayRegionEnd = end;
2818  Refresh();
2819 
2820  ctb->PlayPlayRegion((SelectedRegion(start, end)),
2821  options, PlayMode::normalPlay,
2822  appearance,
2823  false,
2824  true);
2825 
2826  }
2827 }
2828 
2830 {
2831  if (choice == StatusChoice::NoChange)
2832  return;
2833 
2834  wxString message {};
2835 
2836  const auto &scrubber = mProject->GetScrubber();
2837  const bool scrubbing = scrubber.HasStartedScrubbing();
2838  if (scrubbing && choice != StatusChoice::Leaving)
2839  // Don't distinguish zones
2841 
2842  switch (choice) {
2844  {
2845  // message = Insert timeline status bar message here
2846  }
2847  break;
2848 
2850  {
2851  message = ScrubbingMessage(scrubber);
2852  }
2853  break;
2854 
2855  default:
2856  break;
2857  }
2858 
2859  // Display a message, or empty message
2861 
2862  RegenerateTooltips(choice);
2863 }
2864 
2865 // This version toggles ruler state indirectly via the scrubber
2866 // to ensure that all the places where the state is shown update.
2867 // For example buttons and menus must update.
2869 {
2870  auto &scrubber = mProject->GetScrubber();
2871  scrubber.OnToggleScrubRuler(*mProject);
2872 }
2873 
2874 void AdornedRulerPanel::OnToggleScrubRuler(/*wxCommandEvent&*/)
2875 {
2877  WriteScrubEnabledPref(mShowScrubbing);
2878  gPrefs->Flush();
2879  SetPanelSize();
2880 }
2881 
2883 {
2884  wxSize size { GetSize().GetWidth(), GetRulerHeight(mShowScrubbing) };
2885  SetSize(size);
2886  SetMinSize(size);
2887  GetParent()->PostSizeEventToParent();
2888 }
2889 
2890 void AdornedRulerPanel::OnContextMenu(wxContextMenuEvent & WXUNUSED(event))
2891 {
2893 }
2894 
2896 {
2897  auto common = [this]
2898  (AButton &button, const wxString &commandName, const wxString &label) {
2900  { label, commandName } );
2901  ToolBar::SetButtonToolTip(button, commands);
2902  button.SetLabel(button.GetToolTipText());
2903 
2904  button.UpdateStatus();
2905  };
2906 
2907  {
2908  bool state = TracksPrefs::GetPinnedHeadPreference();
2909  auto pinButton = static_cast<AButton*>(FindWindow(OnTogglePinnedStateID));
2910  if( !state )
2911  pinButton->PopUp();
2912  else
2913  pinButton->PushDown();
2914  pinButton->SetAlternateIdx(state ? 0 : 1);
2915  // Bug 1584: Toltip now shows what clicking will do.
2916  const auto label = state
2917  ? _("Click to unpin")
2918  : _("Click to pin");
2919  common(*pinButton, wxT("PinnedHead"), label);
2920  }
2921 }
2922 
2923 void AdornedRulerPanel::OnTogglePinnedState(wxCommandEvent & /*event*/)
2924 {
2927 }
2928 
2929 void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt))
2930 {
2932 
2933  wxMouseEvent e(wxEVT_LEFT_UP);
2934  e.m_x = mLastMouseX;
2935  OnMouseEvents(e);
2936 }
2937 
2939 {
2940  // Keep Quick-Play within usable track area.
2942  int width;
2943  tp->GetTracksUsableArea(&width, NULL);
2944  mousePosX = std::max(mousePosX, tp->GetLeftOffset());
2945  mousePosX = std::min(mousePosX, tp->GetLeftOffset() + width - 1);
2946 
2947  mLastMouseX = mousePosX;
2949 }
2950 
2951 // Pop-up menus
2952 
2953 void AdornedRulerPanel::ShowMenu(const wxPoint & pos)
2954 {
2955  wxMenu rulerMenu;
2956 
2957  if (mQuickPlayEnabled)
2958  rulerMenu.Append(OnToggleQuickPlayID, _("Disable Quick-Play"));
2959  else
2960  rulerMenu.Append(OnToggleQuickPlayID, _("Enable Quick-Play"));
2961 
2962  wxMenuItem *dragitem;
2964  dragitem = rulerMenu.Append(OnSyncQuickPlaySelID, _("Disable dragging selection"));
2965  else
2966  dragitem = rulerMenu.Append(OnSyncQuickPlaySelID, _("Enable dragging selection"));
2967  dragitem->Enable(mQuickPlayEnabled && !mProject->IsPlayRegionLocked());
2968 
2969 #if wxUSE_TOOLTIPS
2970  if (mTimelineToolTip)
2971  rulerMenu.Append(OnTimelineToolTipID, _("Disable Timeline Tooltips"));
2972  else
2973  rulerMenu.Append(OnTimelineToolTipID, _("Enable Timeline Tooltips"));
2974 #endif
2975 
2977  rulerMenu.Append(OnAutoScrollID, _("Do not scroll while playing"));
2978  else
2979  rulerMenu.Append(OnAutoScrollID, _("Update display while playing"));
2980 
2981  wxMenuItem *prlitem;
2982  if (!mProject->IsPlayRegionLocked())
2983  prlitem = rulerMenu.Append(OnLockPlayRegionID, _("Lock Play Region"));
2984  else
2985  prlitem = rulerMenu.Append(OnLockPlayRegionID, _("Unlock Play Region"));
2986  prlitem->Enable(mProject->IsPlayRegionLocked() || (mPlayRegionStart != mPlayRegionEnd));
2987 
2988  wxMenuItem *ruleritem;
2989  if (mShowScrubbing)
2990  ruleritem = rulerMenu.Append(OnScrubRulerID, _("Disable Scrub Ruler"));
2991  else
2992  ruleritem = rulerMenu.Append(OnScrubRulerID, _("Enable Scrub Ruler"));
2993 
2994  PopupMenu(&rulerMenu, pos);
2995 }
2996 
2997 void AdornedRulerPanel::ShowScrubMenu(const wxPoint & pos)
2998 {
2999  auto &scrubber = mProject->GetScrubber();
3000  PushEventHandler(&scrubber);
3001  auto cleanup = finally([this]{ PopEventHandler(); });
3002 
3003  wxMenu rulerMenu;
3004  mProject->GetScrubber().PopulatePopupMenu(rulerMenu);
3005  PopupMenu(&rulerMenu, pos);
3006 }
3007 
3009 {
3010  mQuickPlayEnabled = (mQuickPlayEnabled)? false : true;
3011  gPrefs->Write(wxT("/QuickPlay/QuickPlayEnabled"), mQuickPlayEnabled);
3012  gPrefs->Flush();
3014 }
3015 
3017 {
3019  gPrefs->Write(wxT("/QuickPlay/DragSelection"), mPlayRegionDragsSelection);
3020  gPrefs->Flush();
3021 }
3022 
3024 {
3027 }
3028 
3030 {
3031  if (!mSnapManager) {
3032  mSnapManager = std::make_unique<SnapManager>(mTracks, mViewInfo);
3033  }
3034 
3035  auto results = mSnapManager->Snap(NULL, mQuickPlayPos, false);
3036  mQuickPlayPos = results.outTime;
3037  mIsSnapped = results.Snapped();
3038 }
3039 
3041 {
3042  mTimelineToolTip = (mTimelineToolTip)? false : true;
3043  gPrefs->Write(wxT("/QuickPlay/ToolTips"), mTimelineToolTip);
3044  gPrefs->Flush();
3045 #if wxUSE_TOOLTIPS
3047 #endif
3048 }
3049 
3050 void AdornedRulerPanel::OnAutoScroll(wxCommandEvent&)
3051 {
3053  gPrefs->Write(wxT("/GUI/AutoScroll"), false);
3054  else
3055  gPrefs->Write(wxT("/GUI/AutoScroll"), true);
3056  mProject->UpdatePrefs();
3057  gPrefs->Flush();
3058 }
3059 
3060 
3062 {
3065  else
3067 }
3068 
3069 
3070 // Draws the horizontal <===>
3072 {
3073  double start, end;
3074  GetPlayRegion(&start, &end);
3075 
3076  if (start >= 0)
3077  {
3078  const int x1 = Time2Pos(start) + 1;
3079  const int x2 = Time2Pos(end);
3080  int y = mInner.y - TopMargin + mInner.height/2;
3081 
3082  bool isLocked = mProject->IsPlayRegionLocked();
3083  AColor::PlayRegionColor(dc, isLocked);
3084 
3085  wxPoint tri[3];
3086  wxRect r;
3087 
3088  tri[0].x = x1;
3089  tri[0].y = y + PLAY_REGION_GLOBAL_OFFSET_Y;
3090  tri[1].x = x1 + PLAY_REGION_TRIANGLE_SIZE;
3091  tri[1].y = y - PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
3092  tri[2].x = x1 + PLAY_REGION_TRIANGLE_SIZE;
3093  tri[2].y = y + PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
3094  dc->DrawPolygon(3, tri);
3095 
3096  r.x = x1;
3097  r.y = y - PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
3098  r.width = PLAY_REGION_RECT_WIDTH;
3099  r.height = PLAY_REGION_TRIANGLE_SIZE*2 + 1;
3100  dc->DrawRectangle(r);
3101 
3102  if (end != start)
3103  {
3104  tri[0].x = x2;
3105  tri[0].y = y + PLAY_REGION_GLOBAL_OFFSET_Y;
3106  tri[1].x = x2 - PLAY_REGION_TRIANGLE_SIZE;
3107  tri[1].y = y - PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
3108  tri[2].x = x2 - PLAY_REGION_TRIANGLE_SIZE;
3109  tri[2].y = y + PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
3110  dc->DrawPolygon(3, tri);
3111 
3112  r.x = x2 - PLAY_REGION_RECT_WIDTH + 1;
3113  r.y = y - PLAY_REGION_TRIANGLE_SIZE + PLAY_REGION_GLOBAL_OFFSET_Y;
3114  r.width = PLAY_REGION_RECT_WIDTH;
3115  r.height = PLAY_REGION_TRIANGLE_SIZE*2 + 1;
3116  dc->DrawRectangle(r);
3117 
3118  r.x = x1 + PLAY_REGION_TRIANGLE_SIZE;
3120  r.width = std::max(0, x2-x1 - PLAY_REGION_TRIANGLE_SIZE*2);
3121  r.height = PLAY_REGION_RECT_HEIGHT;
3122  dc->DrawRectangle(r);
3123  }
3124  }
3125 }
3126 
3127 void AdornedRulerPanel::ShowContextMenu( MenuChoice choice, const wxPoint *pPosition)
3128 {
3129  wxPoint position;
3130  if(pPosition)
3131  position = *pPosition;
3132  else
3133  {
3134  auto rect = GetRect();
3135  position = { rect.GetLeft() + 1, rect.GetBottom() + 1 };
3136  }
3137 
3138  switch (choice) {
3139  case MenuChoice::QuickPlay:
3140  ShowMenu(position); break;
3141  case MenuChoice::Scrub:
3142  ShowScrubMenu(position); break;
3143  default:
3144  return;
3145  }
3146 
3147  // dismiss and clear Quick-Play indicator
3149 
3150  if (HasCapture())
3151  ReleaseMouse();
3152 }
3153 
3155 {
3156  // Draw AdornedRulerPanel border
3157  AColor::UseThemeColour( dc, clrTrackInfo );
3158  dc->DrawRectangle( mInner );
3159 
3160  if (mShowScrubbing) {
3161  // Let's distinguish the scrubbing area by using a themable
3162  // colour and a line to set it off.
3163  AColor::UseThemeColour(dc, clrScrubRuler, clrTrackPanelText );
3164  wxRect ScrubRect = mScrubZone;
3165  ScrubRect.Inflate( 1,0 );
3166  dc->DrawRectangle(ScrubRect);
3167  }
3168 }
3169 
3171 {
3172  wxRect r = mOuter;
3173  r.width -= RightMargin;
3174  r.height -= BottomMargin;
3175  AColor::BevelTrackInfo( *dc, true, r );
3176 
3177  // Black stroke at bottom
3178  dc->SetPen( *wxBLACK_PEN );
3179  dc->DrawLine( mOuter.x,
3180  mOuter.y + mOuter.height - 1,
3181  mOuter.x + mOuter.width,
3182  mOuter.y + mOuter.height - 1 );
3183 }
3184 
3185 void AdornedRulerPanel::DoDrawMarks(wxDC * dc, bool /*text */ )
3186 {
3187  const double min = Pos2Time(0);
3188  const double hiddenMin = Pos2Time(0, true);
3189  const double max = Pos2Time(mInner.width);
3190  const double hiddenMax = Pos2Time(mInner.width, true);
3191 
3192  mRuler.SetTickColour( theTheme.Colour( clrTrackPanelText ) );
3193  mRuler.SetRange( min, max, hiddenMin, hiddenMax );
3194  mRuler.Draw( *dc );
3195 }
3196 
3198 {
3199  Refresh();
3200 }
3201 
3203 {
3204  // Draw selection
3205  const int p0 = 1 + max(0, Time2Pos(mViewInfo->selectedRegion.t0()));
3206  const int p1 = 2 + min(mInner.width, Time2Pos(mViewInfo->selectedRegion.t1()));
3207 
3208  dc->SetBrush( wxBrush( theTheme.Colour( clrRulerBackground )) );
3209  dc->SetPen( wxPen( theTheme.Colour( clrRulerBackground )) );
3210 
3211  wxRect r;
3212  r.x = p0;
3213  r.y = mInner.y;
3214  r.width = p1 - p0 - 1;
3215  r.height = mInner.height;
3216  dc->DrawRectangle( r );
3217 }
3218 
3220 {
3221  return ProperRulerHeight + (showScrubBar ? ScrubHeight : 0);
3222 }
3223 
3225 {
3226  mLeftOffset = offset;
3227  mRuler.SetUseZoomInfo(offset, mViewInfo);
3228 }
3229 
3230 // Draws the play/recording position indicator.
3232  (wxDC * dc, wxCoord xx, bool playing, int width, bool scrub, bool seek)
3233 {
3234  ADCChanger changer(dc); // Undo pen and brush changes at function exit
3235 
3236  AColor::IndicatorColor( dc, playing );
3237 
3238  wxPoint tri[ 3 ];
3239  if (seek) {
3240  auto height = IndicatorHeightForWidth(width);
3241  // Make four triangles
3242  const int TriangleWidth = width * 3 / 8;
3243 
3244  // Double-double headed, left-right
3245  auto yy = mShowScrubbing
3246  ? mScrubZone.y
3247  : (mInner.GetBottom() + 1) - 1 /* bevel */ - height;
3248  tri[ 0 ].x = xx - IndicatorOffset;
3249  tri[ 0 ].y = yy;
3250  tri[ 1 ].x = xx - IndicatorOffset;
3251  tri[ 1 ].y = yy + height;
3252  tri[ 2 ].x = xx - TriangleWidth;
3253  tri[ 2 ].y = yy + height / 2;
3254  dc->DrawPolygon( 3, tri );
3255 
3256  tri[ 0 ].x -= TriangleWidth;
3257  tri[ 1 ].x -= TriangleWidth;
3258  tri[ 2 ].x -= TriangleWidth;
3259  dc->DrawPolygon( 3, tri );
3260 
3261  tri[ 0 ].x = tri[ 1 ].x = xx + IndicatorOffset;
3262  tri[ 2 ].x = xx + TriangleWidth;
3263  dc->DrawPolygon( 3, tri );
3264 
3265 
3266  tri[ 0 ].x += TriangleWidth;
3267  tri[ 1 ].x += TriangleWidth;
3268  tri[ 2 ].x += TriangleWidth;
3269  dc->DrawPolygon( 3, tri );
3270  }
3271  else if (scrub) {
3272  auto height = IndicatorHeightForWidth(width);
3273  const int IndicatorHalfWidth = width / 2;
3274 
3275  // Double headed, left-right
3276  auto yy = mShowScrubbing
3277  ? mScrubZone.y
3278  : (mInner.GetBottom() + 1) - 1 /* bevel */ - height;
3279  tri[ 0 ].x = xx - IndicatorOffset;
3280  tri[ 0 ].y = yy;
3281  tri[ 1 ].x = xx - IndicatorOffset;
3282  tri[ 1 ].y = yy + height;
3283  tri[ 2 ].x = xx - IndicatorHalfWidth;
3284  tri[ 2 ].y = yy + height / 2;
3285  dc->DrawPolygon( 3, tri );
3286  tri[ 0 ].x = tri[ 1 ].x = xx + IndicatorOffset;
3287  tri[ 2 ].x = xx + IndicatorHalfWidth;
3288  dc->DrawPolygon( 3, tri );
3289  }
3290  else {
3291  bool pinned = TracksPrefs::GetPinnedHeadPreference();
3292  wxBitmap & bmp = theTheme.Bitmap( pinned ?
3293  (playing ? bmpPlayPointerPinned : bmpRecordPointerPinned) :
3294  (playing ? bmpPlayPointer : bmpRecordPointer)
3295  );
3296  const int IndicatorHalfWidth = bmp.GetWidth() / 2;
3297  dc->DrawBitmap( bmp, xx - IndicatorHalfWidth -1, mInner.y );
3298 #if 0
3299 
3300  // Down pointing triangle
3301  auto height = IndicatorHeightForWidth(width);
3302  const int IndicatorHalfWidth = width / 2;
3303  tri[ 0 ].x = xx - IndicatorHalfWidth;
3304  tri[ 0 ].y = mInner.y;
3305  tri[ 1 ].x = xx + IndicatorHalfWidth;
3306  tri[ 1 ].y = mInner.y;
3307  tri[ 2 ].x = xx;
3308  tri[ 2 ].y = mInner.y + height;
3309  dc->DrawPolygon( 3, tri );
3310 #endif
3311  }
3312 }
3313 
3315 {
3316  if (!mOverlay)
3317  mOverlay = std::make_unique<QuickPlayIndicatorOverlay>(mProject);
3318 
3319  return mOverlay.get();
3320 }
3321 
3323 {
3324  ShowOrHideQuickPlayIndicator(true, repaint_all);
3325 }
3326 
3328 {
3329  ShowOrHideQuickPlayIndicator(false, repaint_all);
3330 }
3331 
3332 // Draws the vertical line and green triangle indicating the Quick Play cursor position.
3333 void AdornedRulerPanel::ShowOrHideQuickPlayIndicator(bool show, bool repaint_all)
3334 {
3335  double latestEnd = std::max(mTracks->GetEndTime(), mProject->GetSel1());
3336  if (!show || (mQuickPlayPos >= latestEnd)) {
3337  GetOverlay()->Update(-1);
3338  }
3339  else {
3340  const int x = Time2Pos(mQuickPlayPos);
3341  bool previewScrub =
3344  GetOverlay()->Update(x, mIsSnapped, previewScrub);
3345  }
3346 
3347  mProject->GetTrackPanel()->DrawOverlays(repaint_all);
3348  DrawOverlays(repaint_all);
3349 }
3350 
3351 void AdornedRulerPanel::SetPlayRegion(double playRegionStart,
3352  double playRegionEnd)
3353 {
3354  // This is called by AudacityProject to make the play region follow
3355  // the current selection. But while the user is selecting a play region
3356  // with the mouse directly in the ruler, changes from outside are blocked.
3357  if (mMouseEventState != mesNone)
3358  return;
3359 
3360  mPlayRegionStart = playRegionStart;
3361  mPlayRegionEnd = playRegionEnd;
3362 
3363  Refresh();
3364 }
3365 
3367 {
3369  ctb->StopPlaying();
3370 
3371  mPlayRegionStart = -1;
3372  mPlayRegionEnd = -1;
3373 
3375 
3376  Refresh();
3377 }
3378 
3379 void AdornedRulerPanel::GetPlayRegion(double* playRegionStart,
3380  double* playRegionEnd)
3381 {
3382  if (mPlayRegionStart >= 0 && mPlayRegionEnd >= 0 &&
3384  {
3385  // swap values to make sure end > start
3386  *playRegionStart = mPlayRegionEnd;
3387  *playRegionEnd = mPlayRegionStart;
3388  } else
3389  {
3390  *playRegionStart = mPlayRegionStart;
3391  *playRegionEnd = mPlayRegionEnd;
3392  }
3393 }
3394 
3395 void AdornedRulerPanel::GetMaxSize(wxCoord *width, wxCoord *height)
3396 {
3397  mRuler.GetMaxSize(width, height);
3398 }
3399 
3400 bool AdornedRulerPanel::s_AcceptsFocus{ false };
3401 
3403  s_AcceptsFocus = true;
3404  return TempAllowFocus{ &s_AcceptsFocus };
3405 }
3406 
3408 {
3409  auto temp = TemporarilyAllowFocus();
3410  SetFocus();
3411 }
bool mLabelEdges
Definition: Ruler.h:227
RulerFormat
Definition: Ruler.h:35
void ShowContextMenu(MenuChoice choice, const wxPoint *pPosition)
Definition: Ruler.cpp:3127
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:3395
bool mMajorGrid
Definition: Ruler.h:233
static void PlayRegionColor(wxDC *dc, bool locked)
Definition: AColor.cpp:376
void ShowOrHideQuickPlayIndicator(bool show, bool repaint_all=false)
Definition: Ruler.cpp:3333
void SetLog(bool log)
Definition: Ruler.cpp:197
void Draw(wxDC &dc)
Definition: Ruler.cpp:1307
bool mIsDragging
Definition: Ruler.h:481
A ToolBar that has the main Transport buttons.
bool mQuickPlayEnabled
Definition: Ruler.h:467
void OnSize(wxSizeEvent &event)
Definition: BackedPanel.cpp:70
double mLeftDownClick
Definition: Ruler.h:479
void SetSpacing(int spacing)
Definition: Ruler.cpp:261
bool HasStartedScrubbing() const
Definition: Scrubbing.h:94
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:209
bool mHasSetSpacing
Definition: Ruler.h:226
AudioIOStartStreamOptions GetDefaultPlayOptions()
Definition: Project.cpp:1267
double ComputeWarpedLength(double t0, double t1) const
Compute the duration (in seconds at playback) of the specified region of the track.
Definition: TimeTrack.cpp:160
void RegenerateTooltips(StatusChoice choice)
Definition: Ruler.cpp:2199
bool mPlayRegionDragsSelection
Definition: Ruler.h:465
#define SELECT_TOLERANCE_PIXEL
Definition: Ruler.cpp:106
AdornedRulerPanel * GetRuler() const
Definition: Ruler.cpp:1808
bool bUpdateTrackIndicator
Definition: ViewInfo.h:182
double t0() const
std::unique_ptr< wxFont > mMinorMinorFont
Definition: Ruler.h:183
void HandleQPClick(wxMouseEvent &event, wxCoord mousePosX)
Definition: Ruler.cpp:2572
static const wxChar * ScrollingPreferenceKey()
bool mIsSnapped
Definition: Ruler.h:438
ViewInfo is used mainly to hold the zooming, selection and scroll information. It also has some statu...
Definition: ViewInfo.h:141
void InvalidateRuler()
Definition: Ruler.cpp:2142
double GetSel0() const
Definition: Project.h:186
bool isPoint() const
void Tick(int pos, double d, bool major, bool minor)
Definition: Ruler.cpp:764
void PopUp()
Definition: AButton.cpp:525
Scrubber & GetScrubber()
Definition: Project.h:785
SelectedRegion selectedRegion
Definition: ViewInfo.h:160
wxString mUnits
Definition: Ruler.h:236
double GetStartTime() const
Definition: Track.cpp:1404
wxPen mPen
Definition: Ruler.h:175
wxRect mInner
Definition: Ruler.h:428
wxCursor mCursorHand
Definition: Ruler.h:417
AudacityProject * mProject
Definition: Ruler.cpp:1779
int mNumMinor
Definition: Ruler.h:212
float ValueToPosition(float val) const
Definition: NumberScale.h:251
int mLengthOld
Definition: Ruler.h:180
Ruler mRuler
Definition: Ruler.h:421
void UpdateRects()
Definition: Ruler.cpp:2306
std::unique_ptr< SnapManager > mSnapManager
Definition: Ruler.h:437
bool mbMinor
Definition: Ruler.h:232
std::unique_ptr< wxFont > mMinorFont
Definition: Ruler.h:183
double mOldPlayRegionStart
Definition: Ruler.h:443
void OnToggleScrubRuler()
Definition: Ruler.cpp:2874
void OnLockPlayRegion(const CommandContext &context)
Definition: Menus.cpp:8639
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:499
ArrayOf< Label > mMajorLabels
Definition: Ruler.h:211
QuickPlayIndicatorOverlay * GetOverlay()
Definition: Ruler.cpp:3314
bool mFlip
Definition: Ruler.h:230
int IndicatorBigHeight()
Definition: Ruler.cpp:1715
An array of these created by the Ruler is used to determine what and where text annotations to the nu...
Definition: Ruler.h:200
static void SetButtonToolTip(AButton &button, const LocalizedCommandNameVector &commands)
Definition: ToolBar.cpp:828
int IndicatorBigWidth()
Definition: Ruler.cpp:1721
void TickCustom(int labelIdx, bool major, bool minor)
Definition: Ruler.cpp:872
bool IsPaused() const
Definition: Scrubbing.cpp:682
double GetEndTime() const
Definition: Track.cpp:1409
void DoDrawEdge(wxDC *dc)
Definition: Ruler.cpp:3170
bool mUserFonts
Definition: Ruler.h:184
void SetBounds(int left, int top, int right, int bottom)
Definition: Ruler.cpp:356
ArrayOf< int > mUserBits
Definition: Ruler.h:194
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:1533
wxString label
Definition: Tags.cpp:727
double mMax
Definition: Ruler.h:186
void Update(wxCoord xx)
Definition: Ruler.cpp:1743
ArrayOf< int > mBits
Definition: Ruler.h:195
double PositionToTime(wxInt64 position, wxInt64 origin=0, bool ignoreFisheye=false) const
Definition: ViewInfo.cpp:49
int mMaxWidth
Definition: Ruler.h:177
double mMin
Definition: Ruler.h:186
void OnUnlockPlayRegion(const CommandContext &context)
Definition: Menus.cpp:8653
void HandleSnapping()
Definition: Ruler.cpp:3029
void SetNumberScale(const NumberScale *pScale)
Definition: Ruler.cpp:315
void DoDrawMarks(wxDC *dc, bool)
Definition: Ruler.cpp:3185
bool mValid
Definition: Ruler.h:198
void StopPlaying(bool stopStream=true)
void OnContextMenu(wxContextMenuEvent &WXUNUSED(event))
Definition: Ruler.cpp:2890
void OnAutoScroll(wxCommandEvent &evt)
Definition: Ruler.cpp:3050
void OnMouseEvents(wxMouseEvent &evt)
Definition: Ruler.cpp:2377
void OnLockPlayRegion(const CommandContext &context)
void ShowScrubMenu(const wxPoint &pos)
Definition: Ruler.cpp:2997
void DrawSelection()
Definition: Ruler.cpp:3197
ViewInfo *const mViewInfo
Definition: Ruler.h:423
void HandleQPRelease(wxMouseEvent &event)
Definition: Ruler.cpp:2707
wxRect mOuter
Definition: Ruler.h:426
int GetRulerHeight()
Definition: Ruler.h:331
int teBmps
Definition: Theme.h:28
void OnSyncSelToQuickPlay(wxCommandEvent &evt)
Definition: Ruler.cpp:3016
wxSize ImageSize(int iIndex)
Definition: Theme.cpp:1240
std::unique_ptr< QuickPlayIndicatorOverlay > mOverlay
Definition: Ruler.h:483
bool RemoveOverlay(Overlay *pOverlay)
void OnCapture(wxCommandEvent &evt)
Definition: Ruler.cpp:2234
void DoDrawBackground(wxDC *dc)
Definition: Ruler.cpp:3154
static AButton * MakeButton(wxWindow *parent, teBmps eUp, teBmps eDown, teBmps eHilite, teBmps eDownHi, teBmps eStandardUp, teBmps eStandardDown, teBmps eDisabled, wxWindowID id, wxPoint placement, bool processdownevents, wxSize size)
Definition: ToolBar.cpp:770
void DoSetSize(int x, int y, int width, int height, int sizeFlags=wxSIZE_AUTO) override
Definition: Ruler.cpp:1671
std::unique_ptr< QuickPlayRulerOverlay > mPartner
Definition: Ruler.cpp:1782
double t1() const
int lx
Definition: Ruler.h:204
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
void OnTogglePinnedHead(const CommandContext &context)
Definition: Menus.cpp:2826
bool mMinorGrid
Definition: Ruler.h:234
std::pair< wxRect, bool > DoGetRectangle(wxSize size) override
Definition: Ruler.cpp:1813
~RulerPanel()
Definition: Ruler.cpp:1642
static TempAllowFocus TemporarilyAllowFocus()
Definition: Ruler.cpp:3402
wxRect mRect
Definition: Ruler.h:171
void Invalidate()
Definition: Ruler.cpp:369
#define safenew
Definition: Audacity.h:223
MouseEventState mMouseEventState
Definition: Ruler.h:477
void SetOrientation(int orient)
Definition: Ruler.cpp:220
void Draw(wxDC &dc, bool twoTone, wxColour c) const
Definition: Ruler.cpp:1575
int mTop
Definition: Ruler.h:178
void HideQuickPlayIndicator(bool repaint_all=false)
Definition: Ruler.cpp:3327
bool IsOneShotSeeking() const
Definition: Scrubbing.h:149
static void IndicatorColor(wxDC *dc, bool bIsNotRecording)
Definition: AColor.cpp:367
wxString LabelString(double d, bool major)
Definition: Ruler.cpp:616
double mPlayRegionStart
Definition: Ruler.h:441
#define PLAY_REGION_RECT_WIDTH
Definition: Ruler.cpp:109
bool mCustom
Definition: Ruler.h:231
wxBitmap & Bitmap(int iIndex)
Definition: Theme.cpp:1227
static bool s_AcceptsFocus
Definition: Ruler.h:394
void GetTracksUsableArea(int *width, int *height) const
Definition: TrackPanel.cpp:421
double mQuickPlayPos
Definition: Ruler.h:435
void FindLinearTickSizes(double UPP)
Definition: Ruler.cpp:385
void DisplayBitmap(wxDC &dc)
Definition: BackedPanel.cpp:65
int FindZero(Label *label, int len)
Definition: Ruler.cpp:1508
void SetTwoTone(bool twoTone)
Definition: Ruler.cpp:181
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:158
A kind of Track used to 'warp time'.
Definition: TimeTrack.h:29
wxColour mTickColour
Definition: Ruler.h:174
void HandleQPDrag(wxMouseEvent &event, wxCoord mousePosX)
Definition: Ruler.cpp:2613
void StartQPPlay(bool looped, bool cutPreview)
Definition: Ruler.cpp:2766
int mRight
Definition: Ruler.h:178
void SetCustomMinorLabels(wxArrayString *label, size_t numLabel, int start, int step)
Definition: Ruler.cpp:1563
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
bool IsWithinMarker(int mousePosX, double markerTime)
Definition: Ruler.cpp:2365
void DoDrawIndicator(wxDC *dc, wxCoord xx, bool playing, int width, bool scrub, bool seek)
Definition: Ruler.cpp:3232
int format
Definition: ExportPCM.cpp:56
double mMinor
Definition: Ruler.h:190
bool IsScrubbing() const
Definition: Scrubbing.cpp:574
bool mShowScrubbing
Definition: Ruler.h:487
Defines a selected portion of a project.
void SetPlayRegion(double playRegionStart, double playRegionEnd)
Definition: Ruler.cpp:3351
std::vector< LocalizedCommandName > LocalizedCommandNameVector
The TrackPanel class coordinates updates and operations on the main part of the screen which contains...
Definition: TrackPanel.h:245
bool mLog
Definition: Ruler.h:229
void SetLabelEdges(bool labelEdges)
Definition: Ruler.cpp:272
void ClearPlayRegion()
Definition: Ruler.cpp:3366
void OnToggleScrubRuler(const CommandContext &)
Definition: Scrubbing.cpp:923
int ly
Definition: Ruler.h:204
bool setT1(double t, bool maySwap=true)
struct holding stream options, including a pointer to the TimeTrack and AudioIOListener and whether t...
Definition: AudioIO.h:114
QuickPlayIndicatorOverlay & mPartner
Definition: Ruler.cpp:1751
int mLeftOffset
Definition: Ruler.h:239
AdornedRulerPanel * GetRulerPanel()
Definition: Project.cpp:1408
int IndicatorHeightForWidth(int width)
Definition: Ruler.cpp:1702
~Ruler()
Definition: Ruler.cpp:176
void ShowQuickPlayIndicator(bool repaint_all=false)
Definition: Ruler.cpp:3322
StatusChoice mPrevZone
Definition: Ruler.h:485
int mBottom
Definition: Ruler.h:178
void OnToggleQuickPlay(wxCommandEvent &evt)
Definition: Ruler.cpp:3008
void SetPanelSize()
Definition: Ruler.cpp:2882
QuickPlayRulerOverlay(QuickPlayIndicatorOverlay &partner)
Definition: Ruler.cpp:1795
void SetFlip(bool flip)
Definition: Ruler.cpp:285
EVT_COMMAND(OnTogglePinnedStateID, wxEVT_COMMAND_BUTTON_CLICKED, AdornedRulerPanel::OnTogglePinnedState) AdornedRulerPanel
Definition: Ruler.cpp:1963
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:94
bool Seeks() const
Definition: Scrubbing.cpp:627
TrackList * mTracks
Definition: Ruler.h:424
void OnCaptureLost(wxMouseCaptureLostEvent &evt)
Definition: Ruler.cpp:2929
void DrawOverlays(bool repaint_all, wxDC *pDC=nullptr)
RulerFormat mFormat
Definition: Ruler.h:228
bool mbTicksOnly
Definition: Ruler.h:169
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:177
void OnPaint(wxPaintEvent &evt)
Definition: Ruler.cpp:2255
void SetUnits(const wxString &units)
Definition: Ruler.cpp:208
int min(int a, int b)
wxRect mScrubZone
Definition: Ruler.h:427
ArrayOf< Label > mMinorMinorLabels
Definition: Ruler.h:215
const ZoomInfo * mUseZoomInfo
Definition: Ruler.h:238
double mHiddenMin
Definition: Ruler.h:187
static void UseThemeColour(wxDC *dc, int iBrush, int iPen=-1)
Definition: AColor.cpp:289
int Time2Pos(double t, bool ignoreFisheye=false)
Definition: Ruler.cpp:2357
bool mTwoTone
Definition: Ruler.h:237
void TP_DisplayStatusMessage(const wxString &msg) override
Definition: Project.cpp:5156
QuickPlayIndicatorOverlay(AudacityProject *project)
Definition: Ruler.cpp:1859
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:122
static void Light(wxDC *dc, bool selected, bool highlight=false)
Definition: AColor.cpp:308
bool CanScrub() const
Definition: Scrubbing.cpp:985
std::unique_ptr< bool, Resetter > TempAllowFocus
Definition: Ruler.h:396
void OnTogglePinnedState(wxCommandEvent &event)
Definition: Ruler.cpp:2923
wxDC & GetBackingDCForRepaint()
Definition: BackedPanel.cpp:35
#define PLAY_REGION_GLOBAL_OFFSET_Y
Definition: Ruler.cpp:111
void UpdatePrefs()
Definition: Project.cpp:1313
int mUserBitLen
Definition: Ruler.h:196
void Draw(OverlayPanel &panel, wxDC &dc) override
Definition: Ruler.cpp:1837
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
int mLead
Definition: Ruler.h:178
bool mPlayRegionLock
Definition: Ruler.h:440
void AddOverlay(Overlay *pOverlay)
std::pair< wxRect, bool > DoGetRectangle(wxSize size) override
Definition: Ruler.cpp:1881
IMPLEMENT_CLASS(ControlToolBar, ToolBar)
int mLength
Definition: Ruler.h:179
void UpdateStatusBarAndTooltips(StatusChoice choice)
Definition: Ruler.cpp:2829
wxDC * mDC
Definition: Ruler.h:181
static void MakeAlternateImages(AButton &button, int idx, teBmps eUp, teBmps eDown, teBmps eHilite, teBmps eDownHi, teBmps eStandardUp, teBmps eStandardDown, teBmps eDisabled, wxSize size)
Definition: ToolBar.cpp:803
AudacityProject *const mProject
Definition: Ruler.h:422
double mQuickPlayPosUnsnapped
Definition: Ruler.h:434
void DrawGrid(wxDC &dc, int length, bool minor=true, bool major=true, int xOffset=0, int yOffset=0)
Definition: Ruler.cpp:1449
int PlayPlayRegion(const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode playMode, PlayAppearance appearance=PlayAppearance::Straight, bool backwards=false, bool playWhiteSpace=false)
void SetFormat(RulerFormat format)
Definition: Ruler.cpp:186
void SetRange(double min, double max)
Definition: Ruler.cpp:234
void DoDrawPlayRegion(wxDC *dc)
Definition: Ruler.cpp:3071
#define PLAY_REGION_TRIANGLE_SIZE
Definition: Ruler.cpp:108
ControlToolBar * GetControlToolBar()
Definition: Project.cpp:4803
int mNumMajor
Definition: Ruler.h:210
Used to display a Ruler.
Definition: Ruler.h:32
static void SnapGuidePen(wxDC *dc)
Definition: AColor.cpp:391
void OnPause(const CommandContext &context)
Definition: Menus.cpp:2717
bool mTimelineToolTip
Definition: Ruler.h:466
double value
Definition: Ruler.h:202
static bool GetPinnedHeadPreference()
double mHiddenMax
Definition: Ruler.h:187
wxCursor mCursorSizeWE
Definition: Ruler.h:418
int mGridLineLength
Definition: Ruler.h:235
static bool ScrollingPreferenceDefault()
void SetAsSpacer(bool bIsSpacer)
Definition: Grabber.cpp:100
void Draw(OverlayPanel &panel, wxDC &dc) override
Definition: Ruler.cpp:1892
int mNumMinorMinor
Definition: Ruler.h:214
void OnLockPlayRegion(wxCommandEvent &evt)
Definition: Ruler.cpp:3061
void SetUseZoomInfo(int leftOffset, const ZoomInfo *zoomInfo)
Definition: Ruler.cpp:1590
void SetTickColour(const wxColour &colour)
Definition: Ruler.h:150
bool mInOneShotMode
Definition: Scrubbing.h:149
void ShowMenu(const wxPoint &pos)
Definition: Ruler.cpp:2953
std::pair< double, double > Range
Definition: Ruler.h:248
void OnTimelineToolTips(wxCommandEvent &evt)
Definition: Ruler.cpp:3040
void OnSize(wxSizeEvent &evt)
Definition: Ruler.cpp:2293
int mDigits
Definition: Ruler.h:192
void PopulatePopupMenu(wxMenu &menu)
Definition: Scrubbing.cpp:1017
The widget to the left of a ToolBar that allows it to be dragged around to NEW positions.
Definition: Grabber.h:97
int mLeft
Definition: Ruler.h:178
TrackPanel * GetTrackPanel()
Definition: Project.h:289
void OnToggleScrubRulerFromMenu(wxCommandEvent &)
Definition: Ruler.cpp:2868
#define PLAY_REGION_RECT_HEIGHT
Definition: Ruler.cpp:110
Ruler()
Definition: Ruler.cpp:119
wxWindow * mButtons[3]
Definition: Ruler.h:493
static void BevelTrackInfo(wxDC &dc, bool up, const wxRect &r, bool highlight=false)
Definition: AColor.cpp:262
void SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont)
Definition: Ruler.cpp:303
virtual ~QuickPlayRulerOverlay()
Definition: Ruler.cpp:1801
int mOrientation
Definition: Ruler.h:224
void UpdateQuickPlayPos(wxCoord &mousPosX)
Definition: Ruler.cpp:2938
This is an Audacity Specific ruler panel which additionally has border, selection markers...
Definition: Ruler.h:314
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1208
bool mbTicksAtExtremes
Definition: Ruler.h:170
int GetZeroPosition()
Definition: Ruler.cpp:1524
bool setT0(double t, bool maySwap=true)
bool mIsRecording
Definition: Ruler.h:446
int GetLeftOffset() const
Definition: TrackPanel.h:287
END_EVENT_TABLE()
void ReCreateButtons()
Definition: Ruler.cpp:2082
double mOldPlayRegionEnd
Definition: Ruler.h:444
double GetSel1() const
Definition: Project.h:187
void OfflimitsPixels(int start, int end)
Definition: Ruler.cpp:331
void DragSelection()
Definition: Ruler.cpp:3023
bool IsPlayRegionLocked()
Definition: Project.h:195
virtual ~QuickPlayIndicatorOverlay()
Definition: Ruler.cpp:1866
void SetCustomMajorLabels(wxArrayString *label, size_t numLabel, int start, int step)
Definition: Ruler.cpp:1551
double Pos2Time(int p, bool ignoreFisheye=false)
Definition: Ruler.cpp:2350
wxString text
Definition: Ruler.h:205
void SetFocusFromKbd() override
Definition: Ruler.cpp:3407
void GetPlayRegion(double *playRegionStart, double *playRegionEnd)
Definition: Ruler.cpp:3379
double mMajor
Definition: Ruler.h:189
bool mNeedButtonUpdate
Definition: Ruler.h:494
void MarkScrubStart(wxCoord xx, bool smoothScrolling, bool seek)
Definition: Scrubbing.cpp:262
void UpdateButtonStates()
Definition: Ruler.cpp:2895
double mLeftDownClickUnsnapped
Definition: Ruler.h:478
int IndicatorWidthForHeight(int height)
Definition: Ruler.cpp:1707
void OnPaint(wxPaintEvent &evt)
Definition: Ruler.cpp:1651
std::unique_ptr< wxFont > mMajorFont
Definition: Ruler.h:183
int pos
Definition: Ruler.h:203
void Update()
Definition: Ruler.cpp:986
void Update(int x, bool snapped=false, bool previewScrub=false)
Definition: Ruler.cpp:1873
void SetLeftOffset(int offset)
Definition: Ruler.cpp:3224
void OnSize(wxSizeEvent &evt)
Definition: Ruler.cpp:1662
void SetCustomMode(bool value)
Definition: Ruler.cpp:1549
void DoDrawSelection(wxDC *dc)
Definition: Ruler.cpp:3202
Makes temporary drawing context changes that you back out of, RAII style.
Definition: AColor.h:42
double mPlayRegionEnd
Definition: Ruler.h:442
void UpdatePrefs()
Definition: Ruler.cpp:2058
void OnErase(wxEraseEvent &evt)
Definition: Ruler.cpp:1646
A wxButton with mouse-over behaviour.
Definition: AButton.h:27
int mSpacing
Definition: Ruler.h:225
void SetMinor(bool value)
Definition: Ruler.cpp:298
ArrayOf< Label > mMinorLabels
Definition: Ruler.h:213
wxCursor mCursorDefault
Definition: Ruler.h:416
void UpdateStatus()
Definition: AButton.cpp:421