Audacity  3.0.3
NumericTextCtrl.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  NumericTextCtrl.cpp
6 
7  Dominic Mazzoni
8 
9 
10 ********************************************************************//****************************************************************//****************************************************************//****************************************************************/
169 #include "NumericTextCtrl.h"
170 
171 #include "SampleCount.h"
172 #include "../AllThemeResources.h"
173 #include "../AColor.h"
174 #include "../KeyboardCapture.h"
175 #include "../Theme.h"
176 
177 #include <algorithm>
178 #include <math.h>
179 #include <limits>
180 
181 #include <wx/setup.h> // for wxUSE_* macros
182 
183 #include <wx/wx.h>
184 #include <wx/dcbuffer.h>
185 #include <wx/font.h>
186 #include <wx/intl.h>
187 #include <wx/menu.h>
188 #include <wx/sizer.h>
189 #include <wx/stattext.h>
190 #include <wx/tooltip.h>
191 #include <wx/toplevel.h>
192 
193 #if wxUSE_ACCESSIBILITY
194 #include "WindowAccessible.h"
195 
196 class NumericTextCtrlAx final : public WindowAccessible
197 {
198 public:
200 
201  virtual ~ NumericTextCtrlAx();
202 
203  // Performs the default action. childId is 0 (the action for this object)
204  // or > 0 (the action for a child).
205  // Return wxACC_NOT_SUPPORTED if there is no default action for this
206  // window (e.g. an edit control).
207  wxAccStatus DoDefaultAction(int childId) override;
208 
209  // Retrieves the address of an IDispatch interface for the specified child.
210  // All objects must support this property.
211  wxAccStatus GetChild(int childId, wxAccessible **child) override;
212 
213  // Gets the number of children.
214  wxAccStatus GetChildCount(int *childCount) override;
215 
216  // Gets the default action for this object (0) or > 0 (the action for a child).
217  // Return wxACC_OK even if there is no action. actionName is the action, or the empty
218  // string if there is no action.
219  // The retrieved string describes the action that is performed on an object,
220  // not what the object does as a result. For example, a toolbar button that prints
221  // a document has a default action of "Press" rather than "Prints the current document."
222  wxAccStatus GetDefaultAction(int childId, wxString *actionName) override;
223 
224  // Returns the description for this object or a child.
225  wxAccStatus GetDescription(int childId, wxString *description) override;
226 
227  // Gets the window with the keyboard focus.
228  // If childId is 0 and child is NULL, no object in
229  // this subhierarchy has the focus.
230  // If this object has the focus, child should be 'this'.
231  wxAccStatus GetFocus(int *childId, wxAccessible **child) override;
232 
233  // Returns help text for this object or a child, similar to tooltip text.
234  wxAccStatus GetHelpText(int childId, wxString *helpText) override;
235 
236  // Returns the keyboard shortcut for this object or child.
237  // Return e.g. ALT+K
238  wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut) override;
239 
240  // Returns the rectangle for this object (id = 0) or a child element (id > 0).
241  // rect is in screen coordinates.
242  wxAccStatus GetLocation(wxRect & rect, int elementId) override;
243 
244  // Gets the name of the specified object.
245  wxAccStatus GetName(int childId, wxString *name) override;
246 
247  // Returns a role constant.
248  wxAccStatus GetRole(int childId, wxAccRole *role) override;
249 
250  // Gets a variant representing the selected children
251  // of this object.
252  // Acceptable values:
253  // - a null variant (IsNull() returns TRUE)
254  // - a list variant (GetType() == wxT("list"))
255  // - an integer representing the selected child element,
256  // or 0 if this object is selected (GetType() == wxT("long"))
257  // - a "void*" pointer to a wxAccessible child object
258  wxAccStatus GetSelections(wxVariant *selections) override;
259 
260  // Returns a state constant.
261  wxAccStatus GetState(int childId, long *state) override;
262 
263  // Returns a localized string representing the value for the object
264  // or child.
265  wxAccStatus GetValue(int childId, wxString *strValue) override;
266 
267 private:
268  NumericTextCtrl *mCtrl;
269  int mLastField;
270  int mLastDigit;
271  wxString mCachedName;
272  wxString mLastCtrlString;
273 };
274 
275 #endif // wxUSE_ACCESSIBILITY
276 
277 //
278 // ----------------------------------------------------------------------------
279 // BuiltinFormatString Struct
280 // ----------------------------------------------------------------------------
281 //
286 {
289 
290  friend inline bool operator ==
291  (const BuiltinFormatString &a, const BuiltinFormatString &b)
292  { return a.name == b.name; }
293 };
294 
295 //
296 // ----------------------------------------------------------------------------
297 // NumericField Class
298 // ----------------------------------------------------------------------------
299 //
301 {
302 public:
303  NumericField(bool _frac, int _base, int _range, bool _zeropad)
304  {
305  frac = _frac;
306  base = _base;
307  range = _range;
308  zeropad = _zeropad;
309  digits = 0;
310  }
311  NumericField( const NumericField & ) = default;
312  NumericField &operator = ( const NumericField & ) = default;
313  //NumericField( NumericField && ) = default;
314  //NumericField &operator = ( NumericField && ) = default;
316  {
317  if (range > 1)
318  digits = (int)ceil(log10(range-1.0));
319  else
320  digits = 5; // hack: default
321  if (zeropad && range>1)
322  formatStr.Printf(wxT("%%0%dd"), digits); // ex. "%03d" if digits is 3
323  else {
324  formatStr.Printf(wxT("%%0%dd"), digits);
325  }
326  }
327  bool frac; // is it a fractional field
328  int base; // divide by this (multiply, after decimal point)
329  int range; // then take modulo this
330  int digits;
331  int pos; // Index of this field in the ValueString
332  int fieldX; // x-position of the field on-screen
333  int fieldW; // width of the field on-screen
334  int labelX; // x-position of the label on-screen
335  bool zeropad;
336  wxString label;
337  wxString formatStr;
338  wxString str;
339 };
340 
341 //
342 // ----------------------------------------------------------------------------
343 // DigitInfo Class
344 // ----------------------------------------------------------------------------
345 //
347 {
348 public:
349  DigitInfo(int _field, int _index, int _pos, wxRect _box)
350  {
351  field = _field;
352  index = _index;
353  pos = _pos;
354  digitBox = _box;
355  }
356  int field; // Which field
357  int index; // Index of this digit within the field
358  int pos; // Position in the ValueString
359  wxRect digitBox;
360 };
361 
362 namespace {
363 
369  {
370  /* i18n-hint: Name of time display format that shows time in seconds */
371  { XO("seconds") },
372  /* i18n-hint: Format string for displaying time in seconds. Change the comma
373  * in the middle to the 1000s separator for your locale, and the 'seconds'
374  * on the end to the word for seconds. Don't change the numbers. */
375  XO("01000,01000 seconds")
376  },
377 
378  {
379  /* i18n-hint: Name of time display format that shows time in hours, minutes
380  * and seconds */
381  { XO("hh:mm:ss") },
382  /* i18n-hint: Format string for displaying time in hours, minutes and
383  * seconds. Change the 'h' to the abbreviation for hours, 'm' to the
384  * abbreviation for minutes and 's' to the abbreviation for seconds. Don't
385  * change the numbers unless there aren't 60 seconds in a minute in your
386  * locale */
387  XO("0100 h 060 m 060 s")
388  },
389 
390  {
391  /* i18n-hint: Name of time display format that shows time in days, hours,
392  * minutes and seconds */
393  { XO("dd:hh:mm:ss") },
394  /* i18n-hint: Format string for displaying time in days, hours, minutes and
395  * seconds. Change the 'days' to the word for days, 'h' to the abbreviation
396  * for hours, 'm' to the abbreviation for minutes and 's' to the
397  * abbreviation for seconds. Don't change the numbers unless there aren't
398  * 24 hours in a day in your locale */
399  XO("0100 days 024 h 060 m 060 s")
400  },
401 
402  {
403  /* i18n-hint: Name of time display format that shows time in hours,
404  * minutes, seconds and hundredths of a second (1/100 second) */
405  { XO("hh:mm:ss + hundredths") },
406  /* i18n-hint: Format string for displaying time in hours, minutes, seconds
407  * and hundredths of a second. Change the 'h' to the abbreviation for hours,
408  * 'm' to the abbreviation for minutes and 's' to the abbreviation for seconds
409  * (the hundredths are shown as decimal seconds). Don't change the numbers
410  * unless there aren't 60 minutes in an hour in your locale.
411  * The decimal separator is specified using '<' if your language uses a ',' or
412  * to '>' if your language uses a '.'. */
413  XO("0100 h 060 m 060>0100 s")
414  },
415 
416  {
417  /* i18n-hint: Name of time display format that shows time in hours,
418  * minutes, seconds and milliseconds (1/1000 second) */
419  { XO("hh:mm:ss + milliseconds") },
420  /* i18n-hint: Format string for displaying time in hours, minutes, seconds
421  * and milliseconds. Change the 'h' to the abbreviation for hours, 'm' to the
422  * abbreviation for minutes and 's' to the abbreviation for seconds (the
423  * milliseconds are shown as decimal seconds) . Don't change the numbers
424  * unless there aren't 60 minutes in an hour in your locale.
425  * The decimal separator is specified using '<' if your language uses a ',' or
426  * to '>' if your language uses a '.'. */
427  XO("0100 h 060 m 060>01000 s")
428  },
429 
430  {
431  /* i18n-hint: Name of time display format that shows time in hours,
432  * minutes, seconds and samples (at the current project sample rate) */
433  { XO("hh:mm:ss + samples") },
434  /* i18n-hint: Format string for displaying time in hours, minutes, seconds
435  * and samples. Change the 'h' to the abbreviation for hours, 'm' to the
436  * abbreviation for minutes, 's' to the abbreviation for seconds and
437  * translate samples . Don't change the numbers
438  * unless there aren't 60 seconds in a minute in your locale.
439  * The decimal separator is specified using '<' if your language uses a ',' or
440  * to '>' if your language uses a '.'. */
441  XO("0100 h 060 m 060 s+># samples")
442  },
443 
444  {
445  /* i18n-hint: Name of time display format that shows time in samples (at the
446  * current project sample rate). For example the number of a sample at 1
447  * second into a recording at 44.1KHz would be 44,100.
448  */
449  { XO("samples") },
450  /* i18n-hint: Format string for displaying time in samples (lots of samples).
451  * Change the ',' to the 1000s separator for your locale, and translate
452  * samples. If 1000s aren't a base multiple for your number system, then you
453  * can change the numbers to an appropriate one, and put a 0 on the front */
454  XO("01000,01000,01000 samples|#")
455  },
456 
457  {
458  /* i18n-hint: Name of time display format that shows time in hours, minutes,
459  * seconds and frames at 24 frames per second (commonly used for films) */
460  { XO("hh:mm:ss + film frames (24 fps)") },
461  /* i18n-hint: Format string for displaying time in hours, minutes, seconds
462  * and frames at 24 frames per second. Change the 'h' to the abbreviation
463  * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation
464  * for seconds and translate 'frames' . Don't change the numbers
465  * unless there aren't 60 seconds in a minute in your locale.
466  * The decimal separator is specified using '<' if your language uses a ',' or
467  * to '>' if your language uses a '.'. */
468  XO("0100 h 060 m 060 s+>24 frames")
469  },
470 
471  {
472  /* i18n-hint: Name of time display format that shows time in frames (lots of
473  * frames) at 24 frames per second (commonly used for films) */
474  { XO("film frames (24 fps)") },
475  /* i18n-hint: Format string for displaying time in frames at 24 frames per
476  * second. Change the comma
477  * in the middle to the 1000s separator for your locale,
478  * translate 'frames' and leave the rest alone */
479  XO("01000,01000 frames|24")
480  },
481 
482  {
483  /* i18n-hint: Name of time display format that shows time in hours, minutes,
484  * seconds and frames at NTSC TV drop-frame rate (used for American /
485  * Japanese TV, and very odd) */
486  { XO("hh:mm:ss + NTSC drop frames") },
487  /* i18n-hint: Format string for displaying time in hours, minutes, seconds
488  * and frames with NTSC drop frames. Change the 'h' to the abbreviation
489  * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation
490  * for seconds and translate 'frames'. Leave the |N alone, it's important!
491  * The decimal separator is specified using '<' if your language uses a ',' or
492  * to '>' if your language uses a '.'. */
493  XO("0100 h 060 m 060 s+>30 frames|N")
494  },
495 
496  {
497  /* i18n-hint: Name of time display format that shows time in hours, minutes,
498  * seconds and frames at NTSC TV non-drop-frame rate (used for American /
499  * Japanese TV, and doesn't quite match wall time */
500  { XO("hh:mm:ss + NTSC non-drop frames") },
501  /* i18n-hint: Format string for displaying time in hours, minutes, seconds
502  * and frames with NTSC drop frames. Change the 'h' to the abbreviation
503  * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation
504  * for seconds and translate 'frames'. Leave the | .999000999 alone,
505  * the whole things really is slightly off-speed!
506  * The decimal separator is specified using '<' if your language uses a ',' or
507  * to '>' if your language uses a '.'. */
508  XO("0100 h 060 m 060 s+>030 frames| .999000999")
509  },
510 
511  {
512  /* i18n-hint: Name of time display format that shows time in frames at NTSC
513  * TV frame rate (used for American / Japanese TV */
514  { XO("NTSC frames") },
515  /* i18n-hint: Format string for displaying time in frames with NTSC frames.
516  * Change the comma
517  * in the middle to the 1000s separator for your locale,
518  * translate 'frames' and leave the rest alone. That really is the frame
519  * rate! */
520  XO("01000,01000 frames|29.97002997")
521  },
522 
523  {
524  /* i18n-hint: Name of time display format that shows time in hours, minutes,
525  * seconds and frames at PAL TV frame rate (used for European TV) */
526  { XO("hh:mm:ss + PAL frames (25 fps)") },
527  /* i18n-hint: Format string for displaying time in hours, minutes, seconds
528  * and frames with PAL TV frames. Change the 'h' to the abbreviation
529  * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation
530  * for seconds and translate 'frames'. Nice simple time code!
531  * The decimal separator is specified using '<' if your language uses a ',' or
532  * to '>' if your language uses a '.'. */
533  XO("0100 h 060 m 060 s+>25 frames")
534  },
535 
536  {
537  /* i18n-hint: Name of time display format that shows time in frames at PAL
538  * TV frame rate (used for European TV) */
539  { XO("PAL frames (25 fps)") },
540  /* i18n-hint: Format string for displaying time in frames with NTSC frames.
541  * Change the comma
542  * in the middle to the 1000s separator for your locale,
543  * translate 'frames' and leave the rest alone. */
544  XO("01000,01000 frames|25")
545  },
546 
547  {
548  /* i18n-hint: Name of time display format that shows time in hours, minutes,
549  * seconds and frames at CD Audio frame rate (75 frames per second) */
550  { XO("hh:mm:ss + CDDA frames (75 fps)") },
551  /* i18n-hint: Format string for displaying time in hours, minutes, seconds
552  * and frames with CD Audio frames. Change the 'h' to the abbreviation
553  * for hours, 'm' to the abbreviation for minutes, 's' to the abbreviation
554  * for seconds and translate 'frames'.
555  * The decimal separator is specified using '<' if your language uses a ',' or
556  * to '>' if your language uses a '.'. */
557  XO("0100 h 060 m 060 s+>75 frames")
558  },
559 
560  {
561  /* i18n-hint: Name of time display format that shows time in frames at CD
562  * Audio frame rate (75 frames per second) */
563  { XO("CDDA frames (75 fps)") },
564  /* i18n-hint: Format string for displaying time in frames with CD Audio
565  * frames. Change the comma
566  * in the middle to the 1000s separator for your locale,
567  * translate 'frames' and leave the rest alone */
568  XO("01000,01000 frames|75")
569  },
570 };
571 
577  {
578  /* i18n-hint: Name of display format that shows frequency in hertz */
579  { XO("Hz") },
580  {
581  /* i18n-hint: Format string for displaying frequency in hertz. Change
582  * the decimal point for your locale. Don't change the numbers.
583  * The decimal separator is specified using '<' if your language uses a ',' or
584  * to '>' if your language uses a '.'. */
585  XO("010,01000>0100 Hz")
586  , XO("centihertz")
587  }
588  },
589 
590  {
591  /* i18n-hint: Name of display format that shows frequency in kilohertz */
592  { XO("kHz") },
593  {
594  /* i18n-hint: Format string for displaying frequency in kilohertz. Change
595  * the decimal point for your locale. Don't change the numbers.
596  * The decimal separator is specified using '<' if your language uses a ',' or
597  * to '>' if your language uses a '.'. */
598  XO("01000>01000 kHz|0.001")
599  , XO("hertz")
600  }
601  },
602 };
603 
609  {
610  /* i18n-hint: Name of display format that shows log of frequency
611  * in octaves */
612  { XO("octaves") },
613  {
614  /* i18n-hint: Format string for displaying log of frequency in octaves.
615  * Change the decimal points for your locale. Don't change the numbers.
616  * The decimal separator is specified using '<' if your language uses a ',' or
617  * to '>' if your language uses a '.'. */
618  XO("100>01000 octaves|1.442695041"), // Scale factor is 1 / ln (2)
619  /* i18n-hint: an octave is a doubling of frequency */
620  XO("thousandths of octaves")
621  }
622  },
623 
624  {
625  /* i18n-hint: Name of display format that shows log of frequency
626  * in semitones and cents */
627  { XO("semitones + cents") },
628  {
629  /* i18n-hint: Format string for displaying log of frequency in semitones
630  * and cents.
631  * Change the decimal points for your locale. Don't change the numbers.
632  * The decimal separator is specified using '<' if your language uses a ',' or
633  * to '>' if your language uses a '.'. */
634  XO("1000 semitones >0100 cents|17.312340491"), // Scale factor is 12 / ln (2)
635  /* i18n-hint: a cent is a hundredth of a semitone (which is 1/12 octave) */
636  XO("hundredths of cents")
637  }
638  },
639 
640  {
641  /* i18n-hint: Name of display format that shows log of frequency
642  * in decades */
643  { XO("decades") },
644  {
645  /* i18n-hint: Format string for displaying log of frequency in decades.
646  * Change the decimal points for your locale. Don't change the numbers. */
647  XO("10>01000 decades|0.434294482"), // Scale factor is 1 / ln (10)
648  /* i18n-hint: a decade is a tenfold increase of frequency */
649  XO("thousandths of decades")
650  }
651  },
652 };
653 
656  {
657  switch (type) {
658  default:
660  return TimeConverterFormats_;
665  }
666  }
667 
670  {
671  switch (type) {
672  default:
674  return WXSIZEOF(TimeConverterFormats_);
676  return WXSIZEOF(FrequencyConverterFormats_);
678  return WXSIZEOF(BandwidthConverterFormats_);
679  }
680  }
681 }
682 
683 //
684 // ----------------------------------------------------------------------------
685 // NumericConverter Class
686 // ----------------------------------------------------------------------------
687 //
689 { return TimeConverterFormats_[4].name; }
691 { return TimeConverterFormats_[5].name; }
693 { return TimeConverterFormats_[0].name; }
695 { return TimeConverterFormats_[1].name; }
697 { return TimeConverterFormats_[3].name; }
698 
700 { return FrequencyConverterFormats_[0].name; }
701 
703 {
704  if (id.empty()) {
705  if (type == TIME)
706  return DefaultSelectionFormat();
707  else
708  return ChooseBuiltinFormatStrings(type)[0].name;
709  }
710  else {
711  auto begin = ChooseBuiltinFormatStrings(type);
712  auto end = begin + ChooseNBuiltinFormatStrings(type);
713  auto iter = std::find( begin, end, BuiltinFormatString{ id, {} } );
714  if (iter == end)
715  iter = begin;
716  return iter->name;
717  }
718 }
719 
721  const NumericFormatSymbol & formatName,
722  double value,
723  double sampleRate)
724  : mBuiltinFormatStrings( ChooseBuiltinFormatStrings( type ) )
725  , mNBuiltins( ChooseNBuiltinFormatStrings( type ) )
726 {
727  ResetMinValue();
728  ResetMaxValue();
729  mInvalidValue = -1.0;
730 
731  mDefaultNdx = 0;
732 
733  mType = type;
734 
735  if (type == NumericConverter::TIME )
736  mDefaultNdx = 4; // Default to "hh:mm:ss + milliseconds".
737 
738  mScalingFactor = 1.0f;
739  mSampleRate = 1.0f;
740  mNtscDrop = false;
741 
742  mFocusedDigit = 0;
743 
744  mValue = value; // used in SetSampleRate, reassigned later
745 
746  SetSampleRate(sampleRate);
747  SetFormatName(formatName);
748  SetValue(value); // mValue got overridden to -1 in ControlsToValue(), reassign
749 }
750 
752  const TranslatableString & untranslatedFormat)
753 {
754  auto format = untranslatedFormat.Translation();
755 
756  mPrefix = wxT("");
757  mFields.clear();
758  mDigits.clear();
759  mScalingFactor = 1.0;
760 
761  // We will change inFrac to true when we hit our first decimal point.
762  bool inFrac = false;
763  int fracMult = 1;
764  int numWholeFields = 0;
765  int numFracFields = 0;
766  wxString numStr;
767  wxString delimStr;
768  unsigned int i;
769 
770  mNtscDrop = false;
771  for(i=0; i<format.length(); i++) {
772  bool handleDelim = false;
773  bool handleNum = false;
774 
775  if (format[i] == '|') {
776  wxString remainder = format.Right(format.length() - i - 1);
777  // For languages which use , as a separator.
778  remainder.Replace(wxT(","), wxT("."));
779 
780  if (remainder == wxT("#"))
782  else if (remainder == wxT("N")) {
783  mNtscDrop = true;
784  }
785  else
786  // Use the C locale here for string to number.
787  // Translations are often incomplete.
788  // We can't rely on the correct ',' or '.' in the
789  // translation, so we work based on '.' for decimal point.
790  remainder.ToCDouble(&mScalingFactor);
791  i = format.length()-1; // force break out of loop
792  if (!delimStr.empty())
793  handleDelim = true;
794  if (!numStr.empty())
795  handleNum = true;
796  }
797  else if ((format[i] >= '0' && format[i] <='9') ||
798  format[i] == wxT('*') || format[i] == wxT('#')) {
799  numStr += format[i];
800  if (!delimStr.empty())
801  handleDelim = true;
802  }
803  else {
804  delimStr += format[i];
805  if (!numStr.empty())
806  handleNum = true;
807  }
808 
809  if (i == format.length() - 1) {
810  if (!numStr.empty())
811  handleNum = true;
812  if (!delimStr.empty())
813  handleDelim = true;
814  }
815 
816  if (handleNum) {
817  bool zeropad = false;
818  long range = 0;
819 
820  if (numStr.Right(1) == wxT("#"))
821  range = (long int)mSampleRate;
822  else if (numStr.Right(1) != wxT("*")) {
823  numStr.ToLong(&range);
824  }
825  if (numStr.GetChar(0)=='0' && numStr.length()>1)
826  zeropad = true;
827 
828  // Hack: always zeropad
829  zeropad = true;
830 
831  if (inFrac) {
832  int base = fracMult * range;
833  mFields.push_back(NumericField(inFrac, base, range, zeropad));
834  fracMult *= range;
835  numFracFields++;
836  }
837  else {
838  unsigned int j;
839  for(j=0; j<mFields.size(); j++)
840  mFields[j].base *= range;
841  mFields.push_back(NumericField(inFrac, 1, range, zeropad));
842  numWholeFields++;
843  }
844  numStr = wxT("");
845  }
846 
847  if (handleDelim) {
848  bool goToFrac = false;
849 
850  if (!inFrac) {
851  wxChar delim = delimStr[delimStr.length()-1];
852  if (delim=='<' || delim=='>') {
853  goToFrac = true;
854  if (delimStr.length() > 1)
855  delimStr = delimStr.BeforeLast(delim);
856  }
857  }
858 
859  if (inFrac) {
860  if (numFracFields == 0) {
861  // Should never happen
862  return;
863  }
864  if (handleNum && numFracFields > 1)
865  mFields[mFields.size()-2].label = delimStr;
866  else
867  mFields[mFields.size()-1].label = delimStr;
868  }
869  else {
870  if (numWholeFields == 0)
871  mPrefix = delimStr;
872  else {
873  delimStr.Replace(wxT("<"), wxT(","));
874  delimStr.Replace(wxT(">"), wxT("."));
875  mFields[numWholeFields-1].label = delimStr;
876  }
877  }
878 
879  if (goToFrac)
880  inFrac = true;
881  delimStr = wxT("");
882  }
883  }
884 
885  for(i = 0; i < mFields.size(); i++) {
886  mFields[i].CreateDigitFormatStr();
887  }
888 
889  int pos = 0;
890  int j;
891  mValueMask = wxT("");
892  mValueTemplate = wxT("");
893 
895  for(j=0; j<(int)mPrefix.length(); j++)
896  mValueMask += wxT(".");
897  pos += mPrefix.length();
898 
899  for(i = 0; i < mFields.size(); i++) {
900  mFields[i].pos = pos;
901 
902  for(j=0; j<mFields[i].digits; j++) {
903  mDigits.push_back(DigitInfo(i, j, pos, wxRect()));
904  mValueTemplate += wxT("0");
905  mValueMask += wxT("0");
906  pos++;
907  }
908 
909  pos += mFields[i].label.length();
910  mValueTemplate += mFields[i].label;
911  for(j=0; j<(int)mFields[i].label.length(); j++)
912  mValueMask += wxT(".");
913  }
914 }
915 
917 {
918  unsigned int i;
919 
920  wxPrintf("%s", (const char *)mPrefix.mb_str());
921 
922  for(i = 0; i < mFields.size(); i++) {
923  if (mFields[i].frac) {
924  wxPrintf("(t * %d) %% %d '%s' ",
925  mFields[i].base,
926  mFields[i].range,
927  (const char *)mFields[i].label.mb_str());
928 
929  }
930  else {
931  wxPrintf("(t / %d) %% %d '%s' ",
932  mFields[i].base,
933  mFields[i].range,
934  (const char *)mFields[i].label.mb_str());
935  }
936  }
937 
938  wxPrintf("\n");
939 }
940 
942 
944 {
945 }
946 
948 {
950 }
951 
952 void NumericConverter::ValueToControls(double rawValue, bool nearest /* = true */)
953 {
954  //rawValue = 4.9995f; Only for testing!
955  if (mType == TIME)
956  rawValue =
957  floor(rawValue * mSampleRate + (nearest ? 0.5f : 0.0f))
958  / mSampleRate; // put on a sample
959  double theValue =
960  rawValue * mScalingFactor
961  // PRL: what WAS this .000001 for? Nobody could explain.
962  // + .000001
963  ;
964  sampleCount t_int;
965  bool round = true;
966  // We round on the last field. If we have a fractional field we round using it.
967  // Otherwise we round to nearest integer.
968  for(unsigned int i = 0; i < mFields.size(); i++) {
969  if (mFields[i].frac)
970  round = false;
971  }
972  if (theValue < 0)
973  t_int = -1;
974  else if(round)
975  t_int = sampleCount(theValue + (nearest ? 0.5f : 0.0f));
976  else
977  {
978  wxASSERT( mFields.back().frac );
979  theValue += (nearest ? 0.5f : 0.0f) / mFields.back().base;
980  t_int = sampleCount(theValue);
981  }
982  double t_frac;
983  if (theValue < 0)
984  t_frac = -1;
985  else
986  t_frac = (theValue - t_int.as_double() );
987  unsigned int i;
988  int tenMins;
989  int mins;
990  int addMins;
991  int secs;
992  int frames;
993 
995 
996  if(mNtscDrop && theValue >= 0) {
997  frames = (int)(theValue*30./1.001 + (nearest ? 0.5f : 0.0f));
998  tenMins = frames/17982;
999  frames -= tenMins*17982;
1000  mins = tenMins * 10;
1001  if(frames >= 1800) {
1002  frames -= 1800;
1003  mins++;
1004  addMins = frames/1798;
1005  frames -= addMins*1798;
1006  mins += addMins;
1007  secs = frames/30;
1008  frames -= secs*30;
1009  frames += 2;
1010  if( frames >= 30 ) {
1011  secs++;
1012  frames -= 30;
1013  }
1014  }
1015  else {
1016  secs = frames/30;
1017  frames -= secs*30;
1018  }
1019  t_int = mins * 60 + secs;
1020  t_frac = frames / 30.;
1021  }
1022 
1023  for(i = 0; i < mFields.size(); i++) {
1024  long long value = -1;
1025 
1026  if (mFields[i].frac) {
1027  // JKC: This old code looks bogus to me.
1028  // The rounding is not propagating to earlier fields in the frac case.
1029  //value = (int)(t_frac * mFields[i].base + 0.5); // +0.5 as rounding required
1030  // I did the rounding earlier.
1031  if (t_frac >= 0)
1032  value = t_frac * mFields[i].base;
1033  // JKC: TODO: Find out what the range is supposed to do.
1034  // It looks bogus too.
1035  //if (mFields[i].range > 0)
1036  // value = value % mFields[i].range;
1037  }
1038  else {
1039  if (t_int >= 0) {
1040  value = t_int.as_long_long() / mFields[i].base;
1041  if (mFields[i].range > 0)
1042  value = value % mFields[i].range;
1043  }
1044  }
1045 
1046  wxString field;
1047  if (value < 0) {
1048  for (int ii = 0; ii < mFields[i].digits; ++ii)
1049  field += wxT("-");
1050  }
1051  else
1052  field = wxString::Format(mFields[i].formatStr, (int) value);
1053  mValueString += field;
1054  mValueString += mFields[i].label;
1055  }
1056 }
1057 
1059 {
1060  unsigned int i;
1061  double t = 0.0;
1062 
1063  if (mFields.size() > 0 &&
1064  mValueString.Mid(mFields[0].pos, 1) == wxChar('-')) {
1066  return;
1067  }
1068 
1069  for(i = 0; i < mFields.size(); i++) {
1070  long val;
1071  mFields[i].str = mValueString.Mid(mFields[i].pos,
1072  mFields[i].digits);
1073  mFields[i].str.ToLong(&val);
1074  if (mFields[i].frac)
1075  t += (val / (double)mFields[i].base);
1076  else
1077  t += (val * (double)mFields[i].base);
1078  }
1079 
1080  t /= mScalingFactor;
1081  if(mNtscDrop) {
1082  int t_int = (int)(t + .000000001);
1083  double t_frac = (t - t_int);
1084  int tenMins = t_int/600;
1085  double frames = tenMins*17982;
1086  t_int -= tenMins*600;
1087  int mins = t_int/60;
1088  int addMins = 0;
1089  if( mins > 0 ) {
1090  frames += 1800;
1091  addMins = mins - 1;
1092  }
1093  frames += addMins * 1798;
1094  t_int -= mins*60;
1095  if( mins == 0 ) //first min of a block of 10, don't drop frames 0 and 1
1096  frames += t_int * 30 + t_frac*30.;
1097  else { //drop frames 0 and 1 of first seconds of these minutes
1098  if( t_int > 0 )
1099  frames += 28 + (t_int-1)*30 + t_frac*30.;
1100  else
1101  frames += t_frac*30. -2.;
1102  }
1103  t = frames * 1.001 / 30.;
1104  }
1105 
1106  mValue = std::max(mMinValue, std::min(mMaxValue, t));
1107 }
1108 
1110 {
1111  return
1112  SetFormatString(GetBuiltinFormat(formatName));
1113 }
1114 
1116 {
1117  if (mFormatString != formatString) {
1118  mFormatString = formatString;
1120  ValueToControls();
1121  ControlsToValue();
1122  return true;
1123  }
1124  else
1125  return false;
1126 }
1127 
1128 void NumericConverter::SetSampleRate(double sampleRate)
1129 {
1130  mSampleRate = sampleRate;
1132  ValueToControls();
1133  ControlsToValue();
1134 }
1135 
1136 void NumericConverter::SetValue(double newValue)
1137 {
1138  mValue = newValue;
1139  ValueToControls();
1140  ControlsToValue();
1141 }
1142 
1143 void NumericConverter::SetMinValue(double minValue)
1144 {
1145  mMinValue = minValue;
1146  if (mMaxValue < minValue)
1147  mMaxValue = minValue;
1148  if (mValue < minValue)
1149  SetValue(minValue);
1150 }
1151 
1153 {
1154  mMinValue = 0.0;
1155 }
1156 
1157 void NumericConverter::SetMaxValue(double maxValue)
1158 {
1159  mMaxValue = maxValue;
1160  if (mMinValue > maxValue) {
1161  mMinValue = maxValue;
1162  }
1163  if (mValue > maxValue)
1164  SetValue(maxValue);
1165 }
1166 
1168 {
1169  mMaxValue = std::numeric_limits<double>::max();
1170 }
1171 
1173 {
1174  ControlsToValue();
1175  return mValue;
1176 }
1177 
1179 {
1180  // int ndx = 1;
1181  int ndx = std::min(1, GetNumBuiltins() - 1);
1182  int i;
1183 
1184  for (i = 0; i < GetNumBuiltins(); i++) {
1185  if (mFormatString == GetBuiltinFormat(i)) {
1186  ndx = i;
1187  break;
1188  }
1189  }
1190 
1191  return ndx;
1192 }
1193 
1195 {
1196  return mNBuiltins;
1197 }
1198 
1200 {
1201  if (index >= 0 && index < GetNumBuiltins())
1202  return mBuiltinFormatStrings[index].name;
1203 
1204  return {};
1205 }
1206 
1208 {
1209  if (index >= 0 && index < GetNumBuiltins())
1210  return mBuiltinFormatStrings[index].formatStrings;
1211 
1212  return {};
1213 }
1214 
1217 {
1218  int ndx =
1219  std::find( mBuiltinFormatStrings, mBuiltinFormatStrings + mNBuiltins,
1220  BuiltinFormatString{ name, {} } )
1221  - mBuiltinFormatStrings;
1222  if (ndx == (int)mNBuiltins)
1223  ndx = mDefaultNdx;
1224 
1225  return GetBuiltinFormat(ndx);
1226 }
1227 
1229 {
1230  ValueToControls();
1231 
1232  return mValueString;
1233 }
1234 
1236 {
1237  mFocusedDigit = mDigits.size() - 1;
1238  Adjust(1, 1);
1239 }
1240 
1242 {
1243  mFocusedDigit = mDigits.size() - 1;
1244  Adjust(1, -1);
1245 }
1246 
1247 void NumericConverter::Adjust(int steps, int dir)
1248 {
1249  // It is possible and "valid" for steps to be zero if a
1250  // high precision device is being used and wxWidgets supports
1251  // reporting a higher precision...Mac wx3 does.
1252  if (steps == 0)
1253  return;
1254 
1255  wxASSERT(dir == -1 || dir == 1);
1256  wxASSERT(steps > 0);
1257  if (steps < 0)
1258  steps = -steps;
1259 
1260  while (steps != 0)
1261  {
1262  for (size_t i = 0; i < mFields.size(); i++)
1263  {
1264  if ((mDigits[mFocusedDigit].pos >= mFields[i].pos) &&
1265  (mDigits[mFocusedDigit].pos < mFields[i].pos + mFields[i].digits))
1266  { //it's this field
1267  if (!mNtscDrop)
1268  {
1269  ControlsToValue();
1270  }
1271  else
1272  {
1273  mNtscDrop = false;
1274  ControlsToValue();
1275  mNtscDrop = true;
1276  }
1277 
1278  if (mValue < 0)
1279  mValue = 0;
1280 
1282 
1283  double mult = pow(10., mFields[i].digits - (mDigits[mFocusedDigit].pos - mFields[i].pos) - 1);
1284  if (mFields[i].frac)
1285  {
1286  mValue += ((mult / (double)mFields[i].base) * dir);
1287  }
1288  else
1289  {
1290  mValue += ((mult * (double)mFields[i].base) * dir);
1291  }
1292 
1293  if (mNtscDrop)
1294  {
1295  if ((mValue - (int)mValue) * 30 < 2)
1296  {
1297  if ((((int)mValue) % 60 == 0) && (((int)mValue) % 600 != 0))
1298  {
1299  mValue = (int)mValue + (dir > 0 ? 2. : -1.) / 30.;
1300  }
1301  }
1302  }
1303 
1304  if (mValue < 0.)
1305  {
1306  mValue = 0.;
1307  }
1308 
1309  mValue = std::max(mMinValue, std::min(mMaxValue, mValue));
1310 
1312 
1313  if (!mNtscDrop)
1314  {
1315  ValueToControls();
1316  }
1317  else
1318  {
1319  mNtscDrop = false;
1320  ValueToControls();
1321  mNtscDrop = true;
1322  ControlsToValue();
1323  }
1324  break;
1325  }
1326  }
1327  steps--;
1328  }
1329 
1330  ControlsToValue();
1331 }
1332 
1333 #define ID_MENU 9800
1334 
1335 // Custom events
1336 
1337 DEFINE_EVENT_TYPE(EVT_TIMETEXTCTRL_UPDATED)
1338 DEFINE_EVENT_TYPE(EVT_FREQUENCYTEXTCTRL_UPDATED)
1339 DEFINE_EVENT_TYPE(EVT_BANDWIDTHTEXTCTRL_UPDATED)
1340 
1341 BEGIN_EVENT_TABLE(NumericTextCtrl, wxControl)
1342  EVT_ERASE_BACKGROUND(NumericTextCtrl::OnErase)
1343  EVT_PAINT(NumericTextCtrl::OnPaint)
1344  EVT_CONTEXT_MENU(NumericTextCtrl::OnContext)
1345  EVT_MOUSE_EVENTS(NumericTextCtrl::OnMouse)
1346  EVT_KEY_DOWN(NumericTextCtrl::OnKeyDown)
1347  EVT_KEY_UP(NumericTextCtrl::OnKeyUp)
1348  EVT_SET_FOCUS(NumericTextCtrl::OnFocus)
1349  EVT_KILL_FOCUS(NumericTextCtrl::OnFocus)
1350  EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, NumericTextCtrl::OnCaptureKey)
1352 
1353 IMPLEMENT_CLASS(NumericTextCtrl, wxControl)
1354 
1355 NumericTextCtrl::NumericTextCtrl(wxWindow *parent, wxWindowID id,
1356  NumericConverter::Type type,
1357  const NumericFormatSymbol &formatName,
1358  double timeValue,
1359  double sampleRate,
1360  const Options &options,
1361  const wxPoint &pos,
1362  const wxSize &size):
1363  wxControl(parent, id, pos, size, wxSUNKEN_BORDER | wxWANTS_CHARS),
1364  NumericConverter(type, formatName, timeValue, sampleRate),
1365  mBackgroundBitmap{},
1366  mDigitFont{},
1367  mLabelFont{},
1368  mLastField(1),
1369  mAutoPos(options.autoPos)
1370  , mType(type)
1371 {
1372  mAllowInvalidValue = false;
1373 
1374  mDigitBoxW = 11;
1375  mDigitBoxH = 19;
1376 
1377  mBorderLeft = 1;
1378  mBorderTop = 1;
1379  mBorderRight = 1;
1380  mBorderBottom = 1;
1381 
1382  mReadOnly = options.readOnly;
1383  mMenuEnabled = options.menuEnabled;
1384  mButtonWidth = mMenuEnabled ? 9 : 0;
1385 
1386  SetLayoutDirection(wxLayout_LeftToRight);
1387  Layout();
1388  Fit();
1389  ValueToControls();
1390 
1391  //PRL -- would this fix the following?
1392  //ValueToControls();
1393 
1394  //mchinen - aug 15 09 - this seems to put the mValue back to zero, and do nothing else.
1395  //ControlsToValue();
1396 
1397  mScrollRemainder = 0.0;
1398 
1399 #if wxUSE_ACCESSIBILITY
1400  SetLabel(wxT(""));
1401  SetName( {} );
1402  SetAccessible(safenew NumericTextCtrlAx(this));
1403 #endif
1404 
1405  if (options.hasInvalidValue)
1406  SetInvalidValue( options.invalidValue );
1407 
1408  if (!options.format.formatStr.empty())
1409  SetFormatString( options.format );
1410 
1411  if (options.hasValue)
1412  SetValue( options.value );
1413 
1414 }
1415 
1417 {
1418 }
1419 
1421 {
1422  wxControl::SetName( name.Translation() );
1423 }
1424 
1425 // Set the focus to the first (left-most) non-zero digit
1426 // If all digits are zero, the right-most position is focused
1427 // If all digits are hyphens (invalid), the left-most position is focused
1429 {
1430  if (!mAutoPos)
1431  return;
1432 
1433  mFocusedDigit = 0;
1434  while (mFocusedDigit < ((int)mDigits.size() - 1)) {
1435  wxChar dgt = mValueString[mDigits[mFocusedDigit].pos];
1436  if (dgt != '0') {
1437  break;
1438  }
1439  mFocusedDigit++;
1440  }
1441 }
1442 
1444 {
1445  return
1446  SetFormatString(GetBuiltinFormat(formatName));
1447 }
1448 
1450 {
1451  auto result =
1452  NumericConverter::SetFormatString(formatString);
1453  if (result) {
1454  Layout();
1455  Fit();
1456  ValueToControls();
1457  ControlsToValue();
1458  UpdateAutoFocus();
1459  }
1460  return result;
1461 }
1462 
1463 void NumericTextCtrl::SetSampleRate(double sampleRate)
1464 {
1465  NumericConverter::SetSampleRate(sampleRate);
1466  Layout();
1467  Fit();
1468  ValueToControls();
1469  ControlsToValue();
1470 }
1471 
1472 void NumericTextCtrl::SetValue(double newValue)
1473 {
1474  NumericConverter::SetValue(newValue);
1475  ValueToControls();
1476  ControlsToValue();
1477 }
1478 
1479 void NumericTextCtrl::SetDigitSize(int width, int height)
1480 {
1481  mDigitBoxW = width;
1482  mDigitBoxH = height;
1483  Layout();
1484  Fit();
1485 }
1486 
1487 void NumericTextCtrl::SetReadOnly(bool readOnly)
1488 {
1489  mReadOnly = readOnly;
1490 }
1491 
1493 {
1494 #if wxUSE_TOOLTIPS
1495  wxString tip(_("(Use context menu to change format.)"));
1496  if (enable)
1497  SetToolTip(tip);
1498  else {
1499  wxToolTip *tt = GetToolTip();
1500  if (tt && tt->GetTip() == tip)
1501  SetToolTip(NULL);
1502  }
1503 #endif
1504  mMenuEnabled = enable;
1505  mButtonWidth = enable ? 9 : 0;
1506  Layout();
1507  Fit();
1508 }
1509 
1510 void NumericTextCtrl::SetInvalidValue(double invalidValue)
1511 {
1512  const bool wasInvalid = mAllowInvalidValue && (mValue == mInvalidValue);
1513  mAllowInvalidValue = true;
1514  mInvalidValue = invalidValue;
1515  if (wasInvalid)
1516  SetValue(invalidValue);
1517 }
1518 
1519 wxSize NumericTextCtrl::ComputeSizing(bool update, wxCoord boxW, wxCoord boxH)
1520 {
1521  // Get current box size
1522  if (boxW == 0) {
1523  boxW = mDigitBoxW;
1524  }
1525 
1526  if (boxH == 0) {
1527  boxH = mDigitBoxH;
1528  }
1529  boxH -= (mBorderTop + mBorderBottom);
1530 
1531  // We can use the screen device context since we're not drawing to it
1532  wxScreenDC dc;
1533 
1534  // First calculate a rough point size
1535  wxFont pf(wxSize(boxW, boxH), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1536  int fontSize = pf.GetPointSize();
1537  wxCoord strW;
1538  wxCoord strH;
1539 
1540  // Now decrease it until we fit within our digit box
1541  dc.SetFont(pf);
1542  dc.GetTextExtent(wxT("0"), &strW, &strH);
1543  while (strW > boxW || strH > boxH) {
1544  dc.SetFont(wxFont(--fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
1545  dc.GetTextExtent(wxT("0"), &strW, &strH);
1546  }
1547  fontSize--;
1548 
1549  // Create the digit font with the new point size
1550  if (update) {
1551  mDigitFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1552  dc.SetFont(*mDigitFont);
1553 
1554  // Remember the actual digit width and height using the new font
1555  dc.GetTextExtent(wxT("0"), &mDigitW, &mDigitH);
1556  }
1557 
1558  // The label font should be a little smaller
1559  std::unique_ptr<wxFont> labelFont = std::make_unique<wxFont>(fontSize - 1, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1560 
1561  // Use the label font for all remaining measurements since only non-digit text is left
1562  dc.SetFont(*labelFont);
1563 
1564  // Remember the pointer if updating
1565  if (update) {
1566  mLabelFont = std::move(labelFont);
1567  }
1568 
1569  // Get the width of the prefix, if any
1570  dc.GetTextExtent(mPrefix, &strW, &strH);
1571 
1572  // Bump x-position to the end of the prefix
1573  int x = mBorderLeft + strW;
1574 
1575  if (update) {
1576  // Set the character position past the prefix
1577  int pos = mPrefix.length();
1578 
1579  // Reset digits array
1580  mDigits.clear();
1581 
1582  // Figure out the x-position of each field and label in the box
1583  for (int i = 0, fcnt = mFields.size(); i < fcnt; ++i) {
1584  // Get the size of the label
1585  dc.GetTextExtent(mFields[i].label, &strW, &strH);
1586 
1587  // Remember this field's x-position
1588  mFields[i].fieldX = x;
1589 
1590  // Remember metrics for each digit
1591  for (int j = 0, dcnt = mFields[i].digits; j < dcnt; ++j) {
1592  mDigits.push_back(DigitInfo(i, j, pos, wxRect(x, mBorderTop, boxW, boxH)));
1593  x += boxW;
1594  pos++;
1595  }
1596 
1597  // Remember the label's x-position
1598  mFields[i].labelX = x;
1599 
1600  // Bump to end of label
1601  x += strW;
1602 
1603  // Remember the label's width
1604  mFields[i].fieldW = x;
1605 
1606  // Bump character position to end of label
1607  pos += mFields[i].label.length();
1608  }
1609  }
1610  else {
1611  // Determine the maximum x-position (length) of the remaining fields
1612  for (int i = 0, fcnt = mFields.size(); i < fcnt; ++i) {
1613  // Get the size of the label
1614  dc.GetTextExtent(mFields[i].label, &strW, &strH);
1615 
1616  // Just bump to next field
1617  x += (boxW * mFields[i].digits) + strW;
1618  }
1619  }
1620 
1621  // Calculate the maximum dimensions
1622  wxSize dim(x + mBorderRight, boxH + mBorderTop + mBorderBottom);
1623 
1624  // Save maximumFinally, calculate the minimum dimensions
1625  if (update) {
1626  mWidth = dim.x;
1627  mHeight = dim.y;
1628  }
1629 
1630  return wxSize(dim.x + mButtonWidth, dim.y);
1631 }
1632 
1634 {
1635  ComputeSizing();
1636 
1637  wxMemoryDC memDC;
1638  wxCoord strW, strH;
1639  memDC.SetFont(*mLabelFont);
1640  memDC.GetTextExtent(mPrefix, &strW, &strH);
1641 
1642  int i;
1643 
1644  // Draw the background bitmap - it contains black boxes where
1645  // all of the digits go and all of the other text
1646 
1647  wxBrush Brush;
1648 
1649  mBackgroundBitmap = std::make_unique<wxBitmap>(mWidth + mButtonWidth, mHeight,24);
1650  memDC.SelectObject(*mBackgroundBitmap);
1651 
1652  theTheme.SetBrushColour( Brush, clrTimeHours );
1653  memDC.SetBrush(Brush);
1654  memDC.SetPen(*wxTRANSPARENT_PEN);
1655  memDC.DrawRectangle(0, 0, mWidth + mButtonWidth, mHeight);
1656 
1657  int numberBottom = mBorderTop + (mDigitBoxH - mDigitH)/2 + mDigitH;
1658 
1659  memDC.GetTextExtent(wxT("0"), &strW, &strH);
1660  int labelTop = numberBottom - strH;
1661 
1662  memDC.SetTextForeground(theTheme.Colour( clrTimeFont ));
1663  memDC.SetTextBackground(theTheme.Colour( clrTimeBack ));
1664  memDC.DrawText(mPrefix, mBorderLeft, labelTop);
1665 
1666  theTheme.SetBrushColour( Brush, clrTimeBack );
1667  memDC.SetBrush(Brush);
1668  //memDC.SetBrush(*wxLIGHT_GREY_BRUSH);
1669  for(i = 0; i < mDigits.size(); i++)
1670  memDC.DrawRectangle(mDigits[i].digitBox);
1671  memDC.SetBrush( wxNullBrush );
1672 
1673  for(i = 0; i < mFields.size(); i++)
1674  memDC.DrawText(mFields[i].label,
1675  mFields[i].labelX, labelTop);
1676 
1677  if (mMenuEnabled) {
1678  wxRect r(mWidth, 0, mButtonWidth - 1, mHeight - 1);
1679  AColor::Bevel(memDC, true, r);
1680  memDC.SetBrush(*wxBLACK_BRUSH);
1681  memDC.SetPen(*wxBLACK_PEN);
1682  AColor::Arrow(memDC,
1683  mWidth + 1,
1684  (mHeight / 2) - 2,
1685  mButtonWidth - 2);
1686  }
1687  return true;
1688 }
1689 
1691 {
1692  wxSize sz = GetSize();
1693  wxSize csz = GetClientSize();
1694 
1695  sz.x = mButtonWidth + mWidth + (sz.x - csz.x);
1696  sz.y = mHeight + (sz.y - csz.y);
1697 
1698  SetInitialSize(sz);
1699 }
1700 
1701 void NumericTextCtrl::OnErase(wxEraseEvent & WXUNUSED(event))
1702 {
1703  // Ignore it to prevent flashing
1704 }
1705 
1706 void NumericTextCtrl::OnPaint(wxPaintEvent & WXUNUSED(event))
1707 {
1708  wxBufferedPaintDC dc(this);
1709  bool focused = (FindFocus() == this);
1710 
1711  dc.DrawBitmap(*mBackgroundBitmap, 0, 0);
1712 
1713  wxPen Pen;
1714  wxBrush Brush;
1715  if (focused) {
1716  theTheme.SetPenColour( Pen, clrTimeFontFocus );
1717  dc.SetPen(Pen);
1718  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1719  dc.DrawRectangle(0, 0, mWidth, mHeight);
1720  dc.SetPen( wxNullPen );
1721  }
1722 
1723  dc.SetFont(*mDigitFont);
1724  dc.SetTextForeground(theTheme.Colour( clrTimeFont ));
1725  dc.SetTextBackground(theTheme.Colour( clrTimeBack ));
1726 
1727  dc.SetPen(*wxTRANSPARENT_PEN);
1728  theTheme.SetBrushColour( Brush , clrTimeBackFocus );
1729  dc.SetBrush( Brush );
1730 
1731  int i;
1732  for(i = 0; i < (int)mDigits.size(); i++) {
1733  wxRect box = mDigits[i].digitBox;
1734  if (focused && mFocusedDigit == i) {
1735  dc.DrawRectangle(box);
1736  dc.SetTextForeground(theTheme.Colour( clrTimeFontFocus ));
1737  dc.SetTextBackground(theTheme.Colour( clrTimeBackFocus ));
1738  }
1739  int pos = mDigits[i].pos;
1740  wxString digit = mValueString.Mid(pos, 1);
1741  int x = box.x + (mDigitBoxW - mDigitW)/2;
1742  int y = box.y + (mDigitBoxH - mDigitH)/2;
1743  dc.DrawText(digit, x, y);
1744  if (focused && mFocusedDigit == i) {
1745  dc.SetTextForeground(theTheme.Colour( clrTimeFont ));
1746  dc.SetTextBackground(theTheme.Colour( clrTimeBack ));
1747  }
1748  }
1749  dc.SetPen( wxNullPen );
1750  dc.SetBrush( wxNullBrush );
1751 }
1752 
1753 void NumericTextCtrl::OnContext(wxContextMenuEvent &event)
1754 {
1755  wxMenu menu;
1756  int i;
1757 
1758  if (!mMenuEnabled) {
1759  event.Skip();
1760  return;
1761  }
1762 
1763  SetFocus();
1764 
1765  int currentSelection = -1;
1766  for (i = 0; i < GetNumBuiltins(); i++) {
1767  menu.AppendRadioItem(ID_MENU + i, GetBuiltinName(i).Translation());
1768  if (mFormatString == GetBuiltinFormat(i)) {
1769  menu.Check(ID_MENU + i, true);
1770  currentSelection = i;
1771  }
1772  }
1773 
1774  PopupMenu(&menu, wxPoint(0, 0));
1775 
1776  // This used to be in an EVT_MENU() event handler, but GTK
1777  // is sensitive to what is done within the handler if the
1778  // user happens to check the first menuitem and then is
1779  // moving down the menu when the ...CTRL_UPDATED event
1780  // handler kicks in.
1781  for (i = 0; i < GetNumBuiltins(); i++) {
1782  if (menu.IsChecked(ID_MENU + i) && i != currentSelection) {
1784 
1785  int eventType = 0;
1786  switch (mType) {
1788  eventType = EVT_TIMETEXTCTRL_UPDATED;
1789  break;
1791  eventType = EVT_FREQUENCYTEXTCTRL_UPDATED;
1792  break;
1794  eventType = EVT_BANDWIDTHTEXTCTRL_UPDATED;
1795  break;
1796  default:
1797  wxASSERT(false);
1798  break;
1799  }
1800 
1801  wxCommandEvent e(eventType, GetId());
1802  e.SetInt(i);
1803  e.SetString(GetBuiltinName(i).Internal());
1804  GetParent()->GetEventHandler()->AddPendingEvent(e);
1805  }
1806  }
1807 
1808 }
1809 
1810 void NumericTextCtrl::OnMouse(wxMouseEvent &event)
1811 {
1812  if (event.LeftDown() && event.GetX() >= mWidth) {
1813  wxContextMenuEvent e;
1814  OnContext(e);
1815  }
1816  else if (event.LeftDown()) {
1817  SetFocus();
1818 
1819  int bestDist = 9999;
1820  unsigned int i;
1821 
1822  mFocusedDigit = 0;
1823  for(i = 0; i < mDigits.size(); i++) {
1824  int dist = abs(event.m_x - (mDigits[i].digitBox.x +
1825  mDigits[i].digitBox.width/2));
1826  if (dist < bestDist) {
1827  mFocusedDigit = i;
1828  bestDist = dist;
1829  }
1830  }
1831 
1832  Refresh(false);
1833  }
1834  else if (event.RightDown() && mMenuEnabled) {
1835  wxContextMenuEvent e;
1836  OnContext(e);
1837  }
1838  else if(!mReadOnly && event.m_wheelRotation != 0 ) {
1839  double steps = event.m_wheelRotation /
1840  (event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0) +
1842  mScrollRemainder = steps - floor(steps);
1843  steps = floor(steps);
1844 
1845  Adjust((int)fabs(steps), steps < 0.0 ? -1 : 1);
1846  Updated();
1847  }
1848 }
1849 
1850 void NumericTextCtrl::OnFocus(wxFocusEvent &event)
1851 {
1852  KeyboardCapture::OnFocus( *this, event );
1853 
1854  if (event.GetEventType() != wxEVT_KILL_FOCUS &&
1855  mFocusedDigit <= 0 )
1856  UpdateAutoFocus();
1857 
1858  event.Skip( false ); // PRL: not sure why, but preserving old behavior
1859 }
1860 
1861 void NumericTextCtrl::OnCaptureKey(wxCommandEvent &event)
1862 {
1863  wxKeyEvent *kevent = (wxKeyEvent *)event.GetEventObject();
1864  int keyCode = kevent->GetKeyCode();
1865 
1866  // Convert numeric keypad entries.
1867  if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9))
1868  keyCode -= WXK_NUMPAD0 - '0';
1869 
1870  switch (keyCode)
1871  {
1872  case WXK_BACK:
1873  case WXK_LEFT:
1874  case WXK_RIGHT:
1875  case WXK_HOME:
1876  case WXK_END:
1877  case WXK_UP:
1878  case WXK_DOWN:
1879  case WXK_TAB:
1880  case WXK_RETURN:
1881  case WXK_NUMPAD_ENTER:
1882  case WXK_DELETE:
1883  return;
1884 
1885  default:
1886  if (keyCode >= '0' && keyCode <= '9' && !kevent->HasAnyModifiers())
1887  return;
1888  }
1889 
1890  event.Skip();
1891 
1892  return;
1893 }
1894 
1895 void NumericTextCtrl::OnKeyUp(wxKeyEvent &event)
1896 {
1897  int keyCode = event.GetKeyCode();
1898 
1899  event.Skip(true);
1900 
1901  if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9))
1902  keyCode -= WXK_NUMPAD0 - '0';
1903 
1904  if ((keyCode >= '0' && keyCode <= '9' && !event.HasAnyModifiers()) ||
1905  (keyCode == WXK_DELETE) ||
1906  (keyCode == WXK_BACK) ||
1907  (keyCode == WXK_UP) ||
1908  (keyCode == WXK_DOWN)) {
1909  Updated(true);
1910  }
1911 }
1912 
1913 void NumericTextCtrl::OnKeyDown(wxKeyEvent &event)
1914 {
1915  if (mDigits.size() == 0)
1916  {
1917  mFocusedDigit = 0;
1918  return;
1919  }
1920 
1921  event.Skip(false);
1922 
1923  int keyCode = event.GetKeyCode();
1924  int digit = mFocusedDigit;
1925 
1926  if (mFocusedDigit < 0)
1927  mFocusedDigit = 0;
1928  if (mFocusedDigit >= (int)mDigits.size())
1929  mFocusedDigit = mDigits.size() - 1;
1930 
1931  // Convert numeric keypad entries.
1932  if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9))
1933  keyCode -= WXK_NUMPAD0 - '0';
1934 
1935  if (!mReadOnly && (keyCode >= '0' && keyCode <= '9' && !event.HasAnyModifiers())) {
1936  int digitPosition = mDigits[mFocusedDigit].pos;
1937  if (mValueString[digitPosition] == wxChar('-')) {
1938  mValue = std::max(mMinValue, std::min(mMaxValue, 0.0));
1939  ValueToControls();
1940  // Beware relocation of the string
1941  digitPosition = mDigits[mFocusedDigit].pos;
1942  }
1943  mValueString[digitPosition] = wxChar(keyCode);
1944  ControlsToValue();
1945  Refresh();// Force an update of the control. [Bug 1497]
1946  ValueToControls();
1947  mFocusedDigit = (mFocusedDigit + 1) % (mDigits.size());
1948  Updated();
1949  }
1950 
1951  else if (!mReadOnly && keyCode == WXK_DELETE) {
1952  if (mAllowInvalidValue)
1954  }
1955 
1956  else if (!mReadOnly && keyCode == WXK_BACK) {
1957  // Moves left, replaces that char with '0', stays there...
1958  mFocusedDigit--;
1959  mFocusedDigit += mDigits.size();
1960  mFocusedDigit %= mDigits.size();
1961  wxString::reference theDigit = mValueString[mDigits[mFocusedDigit].pos];
1962  if (theDigit != wxChar('-'))
1963  theDigit = '0';
1964  ControlsToValue();
1965  Refresh();// Force an update of the control. [Bug 1497]
1966  ValueToControls();
1967  Updated();
1968  }
1969 
1970  else if (keyCode == WXK_LEFT) {
1971  mFocusedDigit--;
1972  mFocusedDigit += mDigits.size();
1973  mFocusedDigit %= mDigits.size();
1974  Refresh();
1975  }
1976 
1977  else if (keyCode == WXK_RIGHT) {
1978  mFocusedDigit++;
1979  mFocusedDigit %= mDigits.size();
1980  Refresh();
1981  }
1982 
1983  else if (keyCode == WXK_HOME) {
1984  mFocusedDigit = 0;
1985  Refresh();
1986  }
1987 
1988  else if (keyCode == WXK_END) {
1989  mFocusedDigit = mDigits.size() - 1;
1990  Refresh();
1991  }
1992 
1993  else if (!mReadOnly && keyCode == WXK_UP) {
1994  Adjust(1, 1);
1995  Updated();
1996  }
1997 
1998  else if (!mReadOnly && keyCode == WXK_DOWN) {
1999  Adjust(1, -1);
2000  Updated();
2001  }
2002 
2003  else if (keyCode == WXK_TAB) {
2004 #if defined(__WXMSW__)
2005  // Using Navigate() on Windows, rather than the following code causes
2006  // bug 1542
2007  wxWindow* parent = GetParent();
2008  wxNavigationKeyEvent nevent;
2009  nevent.SetWindowChange(event.ControlDown());
2010  nevent.SetDirection(!event.ShiftDown());
2011  nevent.SetEventObject(parent);
2012  nevent.SetCurrentFocus(parent);
2013  GetParent()->GetEventHandler()->ProcessEvent(nevent);
2014 #else
2015  Navigate(event.ShiftDown()
2016  ? wxNavigationKeyEvent::IsBackward
2017  : wxNavigationKeyEvent::IsForward);
2018 #endif
2019  }
2020 
2021  else if (keyCode == WXK_RETURN || keyCode == WXK_NUMPAD_ENTER) {
2022  wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
2023  wxWindow *def = tlw->GetDefaultItem();
2024  if (def && def->IsEnabled()) {
2025  wxCommandEvent cevent(wxEVT_COMMAND_BUTTON_CLICKED,
2026  def->GetId());
2027  cevent.SetEventObject( def );
2028  GetParent()->GetEventHandler()->ProcessEvent(cevent);
2029  }
2030  }
2031 
2032  else {
2033  event.Skip();
2034  return;
2035  }
2036 
2037  if (digit != mFocusedDigit) {
2039  }
2040 }
2041 
2043 {
2044 #if wxUSE_ACCESSIBILITY
2045  if (mDigits.size() == 0)
2046  {
2047  mFocusedDigit = 0;
2048  return;
2049  }
2050  mFocusedDigit = digit;
2051  mLastField = mDigits[mFocusedDigit].field + 1;
2052 
2053  GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
2054  this,
2055  wxOBJID_CLIENT,
2056  mFocusedDigit + 1);
2057 #endif
2058 }
2059 
2060 void NumericTextCtrl::Updated(bool keyup /* = false */)
2061 {
2062  wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
2063 
2064  // This will give listeners the ability to do tasks when the
2065  // update has been completed, like when the UP ARROW has been
2066  // held down and is finally released.
2067  event.SetInt(keyup);
2068  event.SetEventObject(this);
2069  GetEventHandler()->ProcessEvent(event);
2070 
2071 #if wxUSE_ACCESSIBILITY
2072  if (!keyup) {
2073  if (mDigits.size() == 0)
2074  {
2075  mFocusedDigit = 0;
2076  return;
2077  }
2078 
2079  // The object_focus event is only needed by Window-Eyes
2080  // and can be removed when we cease to support this screen reader.
2081  GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
2082  this,
2083  wxOBJID_CLIENT,
2084  mFocusedDigit + 1);
2085 
2086  GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_NAMECHANGE,
2087  this,
2088  wxOBJID_CLIENT,
2089  mFocusedDigit + 1);
2090  }
2091 #endif
2092 }
2093 
2095 {
2096  const wxString previousValueString = mValueString;
2098  if (mValueString != previousValueString) {
2099  // Doing this only when needed is an optimization.
2100  // NumerixTextCtrls are used in the selection bar at the bottom
2101  // of Audacity, and are updated at high frequency through
2102  // SetValue() when Audacity is playing. This consumes a
2103  // significant amount of CPU. Typically, when a track is
2104  // playing, only one of the NumericTextCtrl actually changes
2105  // (the audio position). We save CPU by updating the control
2106  // only when needed.
2107  Refresh(false);
2108  }
2109 }
2110 
2111 
2113 {
2115 }
2116 
2117 #if wxUSE_ACCESSIBILITY
2118 
2119 NumericTextCtrlAx::NumericTextCtrlAx(NumericTextCtrl *ctrl)
2120 : WindowAccessible(ctrl)
2121 {
2122  mCtrl = ctrl;
2123  mLastField = -1;
2124  mLastDigit = -1;
2125 }
2126 
2127 NumericTextCtrlAx::~NumericTextCtrlAx()
2128 {
2129 }
2130 
2131 // Performs the default action. childId is 0 (the action for this object)
2132 // or > 0 (the action for a child).
2133 // Return wxACC_NOT_SUPPORTED if there is no default action for this
2134 // window (e.g. an edit control).
2135 wxAccStatus NumericTextCtrlAx::DoDefaultAction(int WXUNUSED(childId))
2136 {
2137  return wxACC_NOT_SUPPORTED;
2138 }
2139 
2140 // Retrieves the address of an IDispatch interface for the specified child.
2141 // All objects must support this property.
2142 wxAccStatus NumericTextCtrlAx::GetChild(int childId, wxAccessible **child)
2143 {
2144  if (childId == wxACC_SELF) {
2145  *child = this;
2146  }
2147  else {
2148  *child = NULL;
2149  }
2150 
2151  return wxACC_OK;
2152 }
2153 
2154 // Gets the number of children.
2155 wxAccStatus NumericTextCtrlAx::GetChildCount(int *childCount)
2156 {
2157  *childCount = mCtrl->mDigits.size();
2158 
2159  return wxACC_OK;
2160 }
2161 
2162 // Gets the default action for this object (0) or > 0 (the action for
2163 // a child). Return wxACC_OK even if there is no action. actionName
2164 // is the action, or the empty string if there is no action. The
2165 // retrieved string describes the action that is performed on an
2166 // object, not what the object does as a result. For example, a
2167 // toolbar button that prints a document has a default action of
2168 // "Press" rather than "Prints the current document."
2169 wxAccStatus NumericTextCtrlAx::GetDefaultAction(int WXUNUSED(childId), wxString *actionName)
2170 {
2171  actionName->clear();
2172 
2173  return wxACC_OK;
2174 }
2175 
2176 // Returns the description for this object or a child.
2177 wxAccStatus NumericTextCtrlAx::GetDescription(int WXUNUSED(childId), wxString *description)
2178 {
2179  description->clear();
2180 
2181  return wxACC_OK;
2182 }
2183 
2184 // Gets the window with the keyboard focus.
2185 // If childId is 0 and child is NULL, no object in
2186 // this subhierarchy has the focus.
2187 // If this object has the focus, child should be 'this'.
2188 wxAccStatus NumericTextCtrlAx::GetFocus(int *childId, wxAccessible **child)
2189 {
2190  *childId = mCtrl->GetFocusedDigit();
2191  *child = this;
2192 
2193  return wxACC_OK;
2194 }
2195 
2196 // Returns help text for this object or a child, similar to tooltip text.
2197 wxAccStatus NumericTextCtrlAx::GetHelpText(int WXUNUSED(childId), wxString *helpText)
2198 {
2199 // removed help text, as on balance it's more of an irritation than useful
2200 #if 0 // was #if wxUSE_TOOLTIPS
2201  wxToolTip *pTip = mCtrl->GetToolTip();
2202  if (pTip) {
2203  *helpText = pTip->GetTip();
2204  }
2205 
2206  return wxACC_OK;
2207 #else
2208  helpText->clear();
2209 
2210  return wxACC_NOT_SUPPORTED;
2211 #endif
2212 }
2213 
2214 // Returns the keyboard shortcut for this object or child.
2215 // Return e.g. ALT+K
2216 wxAccStatus NumericTextCtrlAx::GetKeyboardShortcut(int WXUNUSED(childId), wxString *shortcut)
2217 {
2218  shortcut->clear();
2219 
2220  return wxACC_OK;
2221 }
2222 
2223 // Returns the rectangle for this object (id = 0) or a child element (id > 0).
2224 // rect is in screen coordinates.
2225 wxAccStatus NumericTextCtrlAx::GetLocation(wxRect & rect, int elementId)
2226 {
2227  if ((elementId != wxACC_SELF) &&
2228  // We subtract 1, below, and need to avoid neg index to mDigits.
2229  (elementId > 0))
2230  {
2231 // rect.x += mCtrl->mFields[elementId - 1].fieldX;
2232 // rect.width = mCtrl->mFields[elementId - 1].fieldW;
2233  rect = mCtrl->mDigits[elementId - 1].digitBox;
2234  rect.SetPosition(mCtrl->ClientToScreen(rect.GetPosition()));
2235  }
2236  else
2237  {
2238  rect = mCtrl->GetRect();
2239  rect.SetPosition(mCtrl->GetParent()->ClientToScreen(rect.GetPosition()));
2240  }
2241 
2242  return wxACC_OK;
2243 }
2244 
2245 static void GetFraction( wxString &label,
2246  const NumericConverter::FormatStrings &formatStrings,
2247  bool isTime, int digits )
2248 {
2249  TranslatableString tr = formatStrings.fraction;
2250  if ( tr.empty() ) {
2251  wxASSERT( isTime );
2252  if (digits == 2)
2253  tr = XO("centiseconds");
2254  else if (digits == 3)
2255  tr = XO("milliseconds");
2256  }
2257  if (!tr.empty())
2258  label = tr.Translation();
2259 }
2260 
2261 // Gets the name of the specified object.
2262 wxAccStatus NumericTextCtrlAx::GetName(int childId, wxString *name)
2263 {
2264  // Slightly messy trick to save us some prefixing.
2265  std::vector<NumericField> & mFields = mCtrl->mFields;
2266 
2267  wxString ctrlString = mCtrl->GetString();
2268  int field = mCtrl->GetFocusedField();
2269 
2270  // Return the entire string including the control label
2271  // when the requested child ID is wxACC_SELF. (Mainly when
2272  // the control gets the focus.)
2273  if ((childId == wxACC_SELF) ||
2274  // We subtract 1 from childId in the other cases below, and
2275  // need to avoid neg index to mDigits, so funnel into this clause.
2276  (childId < 1))
2277  {
2278  *name = mCtrl->GetName();
2279  if (name->empty()) {
2280  *name = mCtrl->GetLabel();
2281  }
2282 
2283  *name += wxT(" ") +
2284  mCtrl->GetString();
2285  }
2286  // This case is needed because of the behaviour of Narrator, which
2287  // is different for the other Windows screen readers. After a focus event,
2288  // Narrator causes getName() to be called more than once. However, the code in
2289  // the following else statement assumes that it is executed only once
2290  // when the focus has been moved to another digit. This else if statement
2291  // ensures that this is the case, by using a cached value if nothing
2292  // has changed.
2293  else if (childId == mLastDigit && ctrlString.IsSameAs(mLastCtrlString)) {
2294  *name = mCachedName;
2295  }
2296  else {
2297  // The user has moved from one field of the time to another so
2298  // report the value of the field and the field's label.
2299  if (mLastField != field) {
2300  wxString label = mFields[field - 1].label;
2301  int cnt = mFields.size();
2302  wxString decimal = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
2303 
2304  // If the NEW field is the last field, then check it to see if
2305  // it represents fractions of a second.
2306  // PRL: click a digit of the control and use left and right arrow keys
2307  // to exercise this code
2308  const bool isTime = (mCtrl->mType == NumericTextCtrl::TIME);
2309  if (field > 1 && field == cnt) {
2310  if (mFields[field - 2].label == decimal) {
2311  int digits = mFields[field - 1].digits;
2312  GetFraction( label, mCtrl->mFormatString,
2313  isTime, digits );
2314  }
2315  }
2316  // If the field following this one represents fractions of a
2317  // second then use that label instead of the decimal point.
2318  else if (label == decimal && field == cnt - 1) {
2319  label = mFields[field].label;
2320  }
2321 
2322  *name = mFields[field - 1].str +
2323  wxT(" ") +
2324  label +
2325  wxT(", ") + // comma inserts a slight pause
2326  mCtrl->GetString().at(mCtrl->mDigits[childId - 1].pos);
2327  mLastField = field;
2328  mLastDigit = childId;
2329  }
2330  // The user has moved from one digit to another within a field so
2331  // just report the digit under the cursor.
2332  else if (mLastDigit != childId) {
2333  *name = mCtrl->GetString().at(mCtrl->mDigits[childId - 1].pos);
2334  mLastDigit = childId;
2335  }
2336  // The user has updated the value of a field, so report the field's
2337  // value only.
2338  else if (field > 0)
2339  {
2340  *name = mFields[field - 1].str;
2341  }
2342 
2343  mCachedName = *name;
2344  mLastCtrlString = ctrlString;
2345  }
2346 
2347  return wxACC_OK;
2348 }
2349 
2350 // Returns a role constant.
2351 wxAccStatus NumericTextCtrlAx::GetRole(int WXUNUSED(childId), wxAccRole *role)
2352 {
2353  *role = wxROLE_SYSTEM_STATICTEXT;
2354  return wxACC_OK;
2355 }
2356 
2357 // Gets a variant representing the selected children
2358 // of this object.
2359 // Acceptable values:
2360 // - a null variant (IsNull() returns TRUE)
2361 // - a list variant (GetType() == wxT("list"))
2362 // - an integer representing the selected child element,
2363 // or 0 if this object is selected (GetType() == wxT("long"))
2364 // - a "void*" pointer to a wxAccessible child object
2365 wxAccStatus NumericTextCtrlAx::GetSelections(wxVariant * WXUNUSED(selections))
2366 {
2367  return wxACC_NOT_IMPLEMENTED;
2368 }
2369 
2370 // Returns a state constant.
2371 wxAccStatus NumericTextCtrlAx::GetState(int WXUNUSED(childId), long *state)
2372 {
2373  *state = wxACC_STATE_SYSTEM_FOCUSABLE;
2374  *state |= (mCtrl == wxWindow::FindFocus() ? wxACC_STATE_SYSTEM_FOCUSED : 0);
2375 
2376  return wxACC_OK;
2377 }
2378 
2379 // Returns a localized string representing the value for the object
2380 // or child.
2381 wxAccStatus NumericTextCtrlAx::GetValue(int WXUNUSED(childId), wxString * WXUNUSED(strValue))
2382 {
2383  return wxACC_NOT_IMPLEMENTED;
2384 }
2385 
2386 #endif
2387 
NumericField::fieldX
int fieldX
Definition: NumericTextCtrl.cpp:332
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
field
#define field(n, t)
Definition: ImportAUP.cpp:167
NumericField::label
wxString label
Definition: NumericTextCtrl.cpp:336
NumericTextCtrl::Updated
void Updated(bool keyup=false)
Definition: NumericTextCtrl.cpp:2060
TranslatableString::empty
bool empty() const
Definition: TranslatableString.h:72
NumericTextCtrl::SetFormatName
bool SetFormatName(const NumericFormatSymbol &formatName)
Definition: NumericTextCtrl.cpp:1443
NumericConverter::SetFormatString
bool SetFormatString(const FormatStrings &formatString)
Definition: NumericTextCtrl.cpp:1115
DigitInfo
DigitInfo is a class used in NumericTextCtrl.
Definition: NumericTextCtrl.cpp:347
NumericField::digits
int digits
Definition: NumericTextCtrl.cpp:330
NumericConverter::ResetMaxValue
void ResetMaxValue()
Definition: NumericTextCtrl.cpp:1167
ComponentInterfaceSymbol::Translation
const wxString Translation() const
Definition: ComponentInterfaceSymbol.h:58
NumericField::frac
bool frac
Definition: NumericTextCtrl.cpp:327
NumericConverter::SetFormatName
bool SetFormatName(const NumericFormatSymbol &formatName)
Definition: NumericTextCtrl.cpp:1109
DigitInfo::DigitInfo
DigitInfo(int _field, int _index, int _pos, wxRect _box)
Definition: NumericTextCtrl.cpp:349
NumericConverter::~NumericConverter
virtual ~NumericConverter()
Definition: NumericTextCtrl.cpp:943
EVT_COMMAND
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:92
NumericConverter::PrintDebugInfo
void PrintDebugInfo()
Definition: NumericTextCtrl.cpp:916
NumericField
NumericField is a class used in NumericTextCtrl.
Definition: NumericTextCtrl.cpp:301
NumericTextCtrl::Fit
void Fit() override
Definition: NumericTextCtrl.cpp:1690
NumericTextCtrl::mLastField
int mLastField
Definition: NumericTextCtrl.h:288
anonymous_namespace{NumericTextCtrl.cpp}::ChooseNBuiltinFormatStrings
size_t ChooseNBuiltinFormatStrings(NumericConverter::Type type)
Definition: NumericTextCtrl.cpp:669
DEFINE_EVENT_TYPE
DEFINE_EVENT_TYPE(EVT_FREQWINDOW_RECALC)
NumericField::pos
int pos
Definition: NumericTextCtrl.cpp:331
NumericTextCtrl::SetFormatString
bool SetFormatString(const FormatStrings &formatString)
Definition: NumericTextCtrl.cpp:1449
NumericTextCtrl::mBorderTop
int mBorderTop
Definition: NumericTextCtrl.h:281
NumericField::range
int range
Definition: NumericTextCtrl.cpp:329
NumericTextCtrl::Layout
bool Layout() override
Definition: NumericTextCtrl.cpp:1633
NumericConverter::HundredthsFormat
static NumericFormatSymbol HundredthsFormat()
Definition: NumericTextCtrl.cpp:696
BuiltinFormatString
struct to hold a formatting control string and its user facing name Used in an array to hold the buil...
Definition: NumericTextCtrl.cpp:286
NumericTextCtrl::SetName
void SetName(const TranslatableString &name)
Definition: NumericTextCtrl.cpp:1420
NumericConverter::FormatStrings::fraction
TranslatableString fraction
Definition: NumericTextCtrl.h:62
XO
#define XO(s)
Definition: Internat.h:31
DigitInfo::index
int index
Definition: NumericTextCtrl.cpp:357
NumericConverter::mFields
std::vector< NumericField > mFields
Definition: NumericTextCtrl.h:151
NumericConverter::mScalingFactor
double mScalingFactor
Definition: NumericTextCtrl.h:158
NumericField::zeropad
bool zeropad
Definition: NumericTextCtrl.cpp:335
NumericConverter::Increment
void Increment()
Definition: NumericTextCtrl.cpp:1235
NumericTextCtrl::SetDigitSize
void SetDigitSize(int width, int height)
Definition: NumericTextCtrl.cpp:1479
NumericConverter::mNBuiltins
const size_t mNBuiltins
Definition: NumericTextCtrl.h:166
NumericConverter::mValue
double mValue
Definition: NumericTextCtrl.h:143
NumericConverter::mValueMask
wxString mValueMask
Definition: NumericTextCtrl.h:154
NumericTextCtrl::mBorderBottom
int mBorderBottom
Definition: NumericTextCtrl.h:283
NumericTextCtrl::ComputeSizing
wxSize ComputeSizing(bool update=true, wxCoord digitW=0, wxCoord digitH=0)
Definition: NumericTextCtrl.cpp:1519
NumericTextCtrl::mBorderLeft
int mBorderLeft
Definition: NumericTextCtrl.h:280
NumericConverter::ValueToControls
virtual void ValueToControls()
Definition: NumericTextCtrl.cpp:947
NumericConverter::HertzFormat
static NumericFormatSymbol HertzFormat()
Definition: NumericTextCtrl.cpp:699
NumericConverter::GetFormatIndex
int GetFormatIndex()
Definition: NumericTextCtrl.cpp:1178
NumericTextCtrl::OnMouse
void OnMouse(wxMouseEvent &event)
Definition: NumericTextCtrl.cpp:1810
NumericTextCtrl
Definition: NumericTextCtrl.h:172
NumericConverter::mDigits
std::vector< DigitInfo > mDigits
Definition: NumericTextCtrl.h:163
NumericConverter::mBuiltinFormatStrings
const BuiltinFormatString * mBuiltinFormatStrings
Definition: NumericTextCtrl.h:165
NumericTextCtrl::SetInvalidValue
void SetInvalidValue(double invalidValue)
Definition: NumericTextCtrl.cpp:1510
NumericTextCtrl::SetValue
void SetValue(double newValue)
Definition: NumericTextCtrl.cpp:1472
NumericConverter::mPrefix
wxString mPrefix
Definition: NumericTextCtrl.h:152
wxEVT_COMMAND_TEXT_UPDATED
wxEVT_COMMAND_TEXT_UPDATED
Definition: Nyquist.cpp:129
NumericTextCtrlAx
NumericTextCtrlAx gives the NumericTextCtrl Accessibility.
ComponentInterfaceSymbol
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Definition: ComponentInterfaceSymbol.h:27
NumericTextCtrl::Options
Definition: NumericTextCtrl.h:178
NumericConverter::BANDWIDTH
@ BANDWIDTH
Definition: NumericTextCtrl.h:55
DigitInfo::field
int field
Definition: NumericTextCtrl.cpp:356
NumericTextCtrl::OnKeyDown
void OnKeyDown(wxKeyEvent &event)
Definition: NumericTextCtrl.cpp:1913
NumericConverter::FormatStrings::formatStr
TranslatableString formatStr
Definition: NumericTextCtrl.h:59
NumericConverter::mNtscDrop
bool mNtscDrop
Definition: NumericTextCtrl.h:160
NumericConverter::TimeAndSampleFormat
static NumericFormatSymbol TimeAndSampleFormat()
Definition: NumericTextCtrl.cpp:690
SampleCount.h
NumericTextCtrl::mReadOnly
bool mReadOnly
Definition: NumericTextCtrl.h:271
NumericConverter::mInvalidValue
double mInvalidValue
Definition: NumericTextCtrl.h:147
NumericTextCtrl::mType
NumericConverter::Type mType
Definition: NumericTextCtrl.h:296
NumericTextCtrl::ValueToControls
void ValueToControls() override
Definition: NumericTextCtrl.cpp:2094
NumericField::operator=
NumericField & operator=(const NumericField &)=default
NumericConverter::DefaultSelectionFormat
static NumericFormatSymbol DefaultSelectionFormat()
Definition: NumericTextCtrl.cpp:688
NumericTextCtrl::OnFocus
void OnFocus(wxFocusEvent &event)
Definition: NumericTextCtrl.cpp:1850
NumericConverter::LookupFormat
static NumericFormatSymbol LookupFormat(Type type, const wxString &id)
Definition: NumericTextCtrl.cpp:702
NumericTextCtrl::UpdateAutoFocus
void UpdateAutoFocus()
Definition: NumericTextCtrl.cpp:1428
NumericConverter::mSampleRate
double mSampleRate
Definition: NumericTextCtrl.h:159
NumericTextCtrl::mWidth
int mWidth
Definition: NumericTextCtrl.h:284
NumericTextCtrl::SetSampleRate
void SetSampleRate(double sampleRate)
Definition: NumericTextCtrl.cpp:1463
NumericConverter::ControlsToValue
virtual void ControlsToValue()
Definition: NumericTextCtrl.cpp:1058
NumericTextCtrl::OnKeyUp
void OnKeyUp(wxKeyEvent &event)
Definition: NumericTextCtrl.cpp:1895
sampleCount::as_double
double as_double() const
Definition: SampleCount.h:45
NumericConverter::GetBuiltinFormat
FormatStrings GetBuiltinFormat(const int index)
Definition: NumericTextCtrl.cpp:1207
NumericTextCtrl::mHeight
int mHeight
Definition: NumericTextCtrl.h:285
label
TranslatableString label
Definition: Tags.cpp:756
NumericTextCtrl::mDigitH
int mDigitH
Definition: NumericTextCtrl.h:279
NumericConverter::NumericConverter
NumericConverter(Type type, const NumericFormatSymbol &formatName={}, double value=0.0f, double sampleRate=1.0f)
Definition: NumericTextCtrl.cpp:720
sampleCount::as_long_long
long long as_long_long() const
Definition: SampleCount.h:47
NumericConverter::ParseFormatString
void ParseFormatString(const TranslatableString &untranslatedFormat)
Definition: NumericTextCtrl.cpp:751
NumericConverter::mType
Type mType
Definition: NumericTextCtrl.h:141
NumericTextCtrl::mDigitBoxW
int mDigitBoxW
Definition: NumericTextCtrl.h:276
NumericTextCtrl::mDigitBoxH
int mDigitBoxH
Definition: NumericTextCtrl.h:277
NumericTextCtrl::OnContext
void OnContext(wxContextMenuEvent &event)
Definition: NumericTextCtrl.cpp:1753
NumericConverter::mMinValue
double mMinValue
Definition: NumericTextCtrl.h:145
name
const TranslatableString name
Definition: Distortion.cpp:98
anonymous_namespace{NumericTextCtrl.cpp}::TimeConverterFormats_
static const BuiltinFormatString TimeConverterFormats_[]
array of formats the control knows about internally array of string pairs for name of the format and ...
Definition: NumericTextCtrl.cpp:368
ThemeBase::SetBrushColour
void SetBrushColour(wxBrush &Brush, int iIndex)
Definition: Theme.cpp:1204
NumericConverter::TIME
@ TIME
Definition: NumericTextCtrl.h:52
format
int format
Definition: ExportPCM.cpp:56
AColor::Arrow
static void Arrow(wxDC &dc, wxCoord x, wxCoord y, int width, bool down=true)
Definition: AColor.cpp:85
NumericTextCtrl::mMenuEnabled
bool mMenuEnabled
Definition: NumericTextCtrl.h:270
NumericConverter::GetNumBuiltins
int GetNumBuiltins()
Definition: NumericTextCtrl.cpp:1194
NumericTextCtrl::~NumericTextCtrl
virtual ~NumericTextCtrl()
Definition: NumericTextCtrl.cpp:1416
WindowAccessible
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
NumericTextCtrl::SetReadOnly
void SetReadOnly(bool readOnly=true)
Definition: NumericTextCtrl.cpp:1487
NumericField::NumericField
NumericField(bool _frac, int _base, int _range, bool _zeropad)
Definition: NumericTextCtrl.cpp:303
DigitInfo::digitBox
wxRect digitBox
Definition: NumericTextCtrl.cpp:359
NumericConverter::HoursMinsSecondsFormat
static NumericFormatSymbol HoursMinsSecondsFormat()
Definition: NumericTextCtrl.cpp:694
NumericConverter::mMaxValue
double mMaxValue
Definition: NumericTextCtrl.h:146
NumericConverter::SetMinValue
void SetMinValue(double minValue)
Definition: NumericTextCtrl.cpp:1143
NumericConverter::FREQUENCY
@ FREQUENCY
Definition: NumericTextCtrl.h:54
NumericConverter::SetMaxValue
void SetMaxValue(double maxValue)
Definition: NumericTextCtrl.cpp:1157
NumericConverter::FormatStrings
Definition: NumericTextCtrl.h:58
id
int id
Definition: WaveTrackControls.cpp:591
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
NumericTextCtrl::mScrollRemainder
double mScrollRemainder
Definition: NumericTextCtrl.h:294
NumericConverter::mDefaultNdx
int mDefaultNdx
Definition: NumericTextCtrl.h:167
AColor::Bevel
static void Bevel(wxDC &dc, bool up, const wxRect &r)
Definition: AColor.cpp:191
NumericConverter::GetString
wxString GetString()
Definition: NumericTextCtrl.cpp:1228
NumericConverter
NumericConverter provides the advanced formatting control used in the selection bar of Audacity.
Definition: NumericTextCtrl.h:48
WindowAccessible.h
NumericConverter::mFormatString
FormatStrings mFormatString
Definition: NumericTextCtrl.h:149
NumericTextCtrl::SetFieldFocus
void SetFieldFocus(int)
Definition: NumericTextCtrl.cpp:2042
NumericConverter::GetBuiltinName
NumericFormatSymbol GetBuiltinName(const int index)
Definition: NumericTextCtrl.cpp:1199
NumericField::CreateDigitFormatStr
void CreateDigitFormatStr()
Definition: NumericTextCtrl.cpp:315
NumericConverter::mValueTemplate
wxString mValueTemplate
Definition: NumericTextCtrl.h:153
NumericField::fieldW
int fieldW
Definition: NumericTextCtrl.cpp:333
NumericTextCtrl::mLabelFont
std::unique_ptr< wxFont > mLabelFont
Definition: NumericTextCtrl.h:275
NumericConverter::SetValue
void SetValue(double newValue)
Definition: NumericTextCtrl.cpp:1136
NumericField::NumericField
NumericField(const NumericField &)=default
_
#define _(s)
Definition: Internat.h:75
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
ID_MENU
#define ID_MENU
Definition: NumericTextCtrl.cpp:1333
NumericTextCtrl::mDigitFont
std::unique_ptr< wxFont > mDigitFont
Definition: NumericTextCtrl.h:275
anonymous_namespace{NumericTextCtrl.cpp}::ChooseBuiltinFormatStrings
const BuiltinFormatString * ChooseBuiltinFormatStrings(NumericConverter::Type type)
Definition: NumericTextCtrl.cpp:655
NumericTextCtrl::mBackgroundBitmap
std::unique_ptr< wxBitmap > mBackgroundBitmap
Definition: NumericTextCtrl.h:273
KeyboardCapture::OnFocus
void OnFocus(wxWindow &window, wxFocusEvent &event)
a function useful to implement a focus event handler The window releases the keyboard if the event is...
Definition: KeyboardCapture.cpp:95
IMPLEMENT_CLASS
IMPLEMENT_CLASS(ControlToolBar, ToolBar)
NumericConverter::Adjust
void Adjust(int steps, int dir)
Definition: NumericTextCtrl.cpp:1247
NumericTextCtrl.h
ExceptionType::Internal
@ Internal
Indicates internal failure from Audacity.
ThemeBase::Colour
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1197
NumericField::base
int base
Definition: NumericTextCtrl.cpp:328
BuiltinFormatString::formatStrings
NumericConverter::FormatStrings formatStrings
Definition: NumericTextCtrl.cpp:288
NumericTextCtrl::OnErase
void OnErase(wxEraseEvent &event)
Definition: NumericTextCtrl.cpp:1701
NumericConverter::ResetMinValue
void ResetMinValue()
Definition: NumericTextCtrl.cpp:1152
NumericTextCtrl::ControlsToValue
void ControlsToValue() override
Definition: NumericTextCtrl.cpp:2112
NumericConverter::mFocusedDigit
int mFocusedDigit
Definition: NumericTextCtrl.h:162
theTheme
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:203
TranslatableString::Translation
wxString Translation() const
Definition: TranslatableString.h:79
NumericConverter::Decrement
void Decrement()
Definition: NumericTextCtrl.cpp:1241
NumericConverter::SetSampleRate
void SetSampleRate(double sampleRate)
Definition: NumericTextCtrl.cpp:1128
anonymous_namespace{wxWidgetsBasicUI.cpp}::GetParent
wxWindow * GetParent(const BasicUI::WindowPlacement &placement)
Definition: wxWidgetsBasicUI.cpp:39
anonymous_namespace{NumericTextCtrl.cpp}::BandwidthConverterFormats_
static const BuiltinFormatString BandwidthConverterFormats_[]
array of formats the control knows about internally array of string pairs for name of the format and ...
Definition: NumericTextCtrl.cpp:608
NumericTextCtrl::mAllowInvalidValue
bool mAllowInvalidValue
Definition: NumericTextCtrl.h:298
NumericField::str
wxString str
Definition: NumericTextCtrl.cpp:338
safenew
#define safenew
Definition: MemoryX.h:10
NumericTextCtrl::EnableMenu
void EnableMenu(bool enable=true)
Definition: NumericTextCtrl.cpp:1492
NumericConverter::Type
Type
Definition: NumericTextCtrl.h:51
DigitInfo::pos
int pos
Definition: NumericTextCtrl.cpp:358
NumericTextCtrl::mAutoPos
bool mAutoPos
Definition: NumericTextCtrl.h:291
NumericField::labelX
int labelX
Definition: NumericTextCtrl.cpp:334
NumericTextCtrl::mDigitW
int mDigitW
Definition: NumericTextCtrl.h:278
NumericConverter::GetValue
double GetValue()
Definition: NumericTextCtrl.cpp:1172
ThemeBase::SetPenColour
void SetPenColour(wxPen &Pen, int iIndex)
Definition: Theme.cpp:1210
NumericTextCtrl::mButtonWidth
int mButtonWidth
Definition: NumericTextCtrl.h:286
END_EVENT_TABLE
END_EVENT_TABLE()
NumericField::formatStr
wxString formatStr
Definition: NumericTextCtrl.cpp:337
NumericTextCtrl::OnPaint
void OnPaint(wxPaintEvent &event)
Definition: NumericTextCtrl.cpp:1706
NumericConverter::mValueString
wxString mValueString
Definition: NumericTextCtrl.h:156
NumericTextCtrl::mBorderRight
int mBorderRight
Definition: NumericTextCtrl.h:282
anonymous_namespace{NumericTextCtrl.cpp}::FrequencyConverterFormats_
static const BuiltinFormatString FrequencyConverterFormats_[]
array of formats the control knows about internally array of string pairs for name of the format and ...
Definition: NumericTextCtrl.cpp:576
NumericTextCtrl::OnCaptureKey
void OnCaptureKey(wxCommandEvent &event)
Definition: NumericTextCtrl.cpp:1861
NumericConverter::SecondsFormat
static NumericFormatSymbol SecondsFormat()
Definition: NumericTextCtrl.cpp:692
BuiltinFormatString::name
NumericFormatSymbol name
Definition: NumericTextCtrl.cpp:287