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