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 "audacity/Types.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 {
943 }
944 
946 {
948 }
949 
950 void NumericConverter::ValueToControls(double rawValue, bool nearest /* = true */)
951 {
952  //rawValue = 4.9995f; Only for testing!
953  if (mType == TIME)
954  rawValue =
955  floor(rawValue * mSampleRate + (nearest ? 0.5f : 0.0f))
956  / mSampleRate; // put on a sample
957  double theValue =
958  rawValue * mScalingFactor
959  // PRL: what WAS this .000001 for? Nobody could explain.
960  // + .000001
961  ;
962  sampleCount t_int;
963  bool round = true;
964  // We round on the last field. If we have a fractional field we round using it.
965  // Otherwise we round to nearest integer.
966  for(unsigned int i = 0; i < mFields.size(); i++) {
967  if (mFields[i].frac)
968  round = false;
969  }
970  if (theValue < 0)
971  t_int = -1;
972  else if(round)
973  t_int = sampleCount(theValue + (nearest ? 0.5f : 0.0f));
974  else
975  {
976  wxASSERT( mFields.back().frac );
977  theValue += (nearest ? 0.5f : 0.0f) / mFields.back().base;
978  t_int = sampleCount(theValue);
979  }
980  double t_frac;
981  if (theValue < 0)
982  t_frac = -1;
983  else
984  t_frac = (theValue - t_int.as_double() );
985  unsigned int i;
986  int tenMins;
987  int mins;
988  int addMins;
989  int secs;
990  int frames;
991 
993 
994  if(mNtscDrop && theValue >= 0) {
995  frames = (int)(theValue*30./1.001 + (nearest ? 0.5f : 0.0f));
996  tenMins = frames/17982;
997  frames -= tenMins*17982;
998  mins = tenMins * 10;
999  if(frames >= 1800) {
1000  frames -= 1800;
1001  mins++;
1002  addMins = frames/1798;
1003  frames -= addMins*1798;
1004  mins += addMins;
1005  secs = frames/30;
1006  frames -= secs*30;
1007  frames += 2;
1008  if( frames >= 30 ) {
1009  secs++;
1010  frames -= 30;
1011  }
1012  }
1013  else {
1014  secs = frames/30;
1015  frames -= secs*30;
1016  }
1017  t_int = mins * 60 + secs;
1018  t_frac = frames / 30.;
1019  }
1020 
1021  for(i = 0; i < mFields.size(); i++) {
1022  long long value = -1;
1023 
1024  if (mFields[i].frac) {
1025  // JKC: This old code looks bogus to me.
1026  // The rounding is not propagating to earlier fields in the frac case.
1027  //value = (int)(t_frac * mFields[i].base + 0.5); // +0.5 as rounding required
1028  // I did the rounding earlier.
1029  if (t_frac >= 0)
1030  value = t_frac * mFields[i].base;
1031  // JKC: TODO: Find out what the range is supposed to do.
1032  // It looks bogus too.
1033  //if (mFields[i].range > 0)
1034  // value = value % mFields[i].range;
1035  }
1036  else {
1037  if (t_int >= 0) {
1038  value = t_int.as_long_long() / mFields[i].base;
1039  if (mFields[i].range > 0)
1040  value = value % mFields[i].range;
1041  }
1042  }
1043 
1044  wxString field;
1045  if (value < 0) {
1046  for (int ii = 0; ii < mFields[i].digits; ++ii)
1047  field += wxT("-");
1048  }
1049  else
1050  field = wxString::Format(mFields[i].formatStr, (int) value);
1051  mValueString += field;
1052  mValueString += mFields[i].label;
1053  }
1054 }
1055 
1057 {
1058  unsigned int i;
1059  double t = 0.0;
1060 
1061  if (mFields.size() > 0 &&
1062  mValueString.Mid(mFields[0].pos, 1) == wxChar('-')) {
1064  return;
1065  }
1066 
1067  for(i = 0; i < mFields.size(); i++) {
1068  long val;
1069  mFields[i].str = mValueString.Mid(mFields[i].pos,
1070  mFields[i].digits);
1071  mFields[i].str.ToLong(&val);
1072  if (mFields[i].frac)
1073  t += (val / (double)mFields[i].base);
1074  else
1075  t += (val * (double)mFields[i].base);
1076  }
1077 
1078  t /= mScalingFactor;
1079  if(mNtscDrop) {
1080  int t_int = (int)(t + .000000001);
1081  double t_frac = (t - t_int);
1082  int tenMins = t_int/600;
1083  double frames = tenMins*17982;
1084  t_int -= tenMins*600;
1085  int mins = t_int/60;
1086  int addMins = 0;
1087  if( mins > 0 ) {
1088  frames += 1800;
1089  addMins = mins - 1;
1090  }
1091  frames += addMins * 1798;
1092  t_int -= mins*60;
1093  if( mins == 0 ) //first min of a block of 10, don't drop frames 0 and 1
1094  frames += t_int * 30 + t_frac*30.;
1095  else { //drop frames 0 and 1 of first seconds of these minutes
1096  if( t_int > 0 )
1097  frames += 28 + (t_int-1)*30 + t_frac*30.;
1098  else
1099  frames += t_frac*30. -2.;
1100  }
1101  t = frames * 1.001 / 30.;
1102  }
1103 
1104  mValue = std::max(mMinValue, std::min(mMaxValue, t));
1105 }
1106 
1108 {
1109  return
1110  SetFormatString(GetBuiltinFormat(formatName));
1111 }
1112 
1114 {
1115  if (mFormatString != formatString) {
1116  mFormatString = formatString;
1118  ValueToControls();
1119  ControlsToValue();
1120  return true;
1121  }
1122  else
1123  return false;
1124 }
1125 
1126 void NumericConverter::SetSampleRate(double sampleRate)
1127 {
1128  mSampleRate = sampleRate;
1130  ValueToControls();
1131  ControlsToValue();
1132 }
1133 
1134 void NumericConverter::SetValue(double newValue)
1135 {
1136  mValue = newValue;
1137  ValueToControls();
1138  ControlsToValue();
1139 }
1140 
1141 void NumericConverter::SetMinValue(double minValue)
1142 {
1143  mMinValue = minValue;
1144  if (mMaxValue < minValue)
1145  mMaxValue = minValue;
1146  if (mValue < minValue)
1147  SetValue(minValue);
1148 }
1149 
1151 {
1152  mMinValue = 0.0;
1153 }
1154 
1155 void NumericConverter::SetMaxValue(double maxValue)
1156 {
1157  mMaxValue = maxValue;
1158  if (mMinValue > maxValue) {
1159  mMinValue = maxValue;
1160  }
1161  if (mValue > maxValue)
1162  SetValue(maxValue);
1163 }
1164 
1166 {
1167  mMaxValue = std::numeric_limits<double>::max();
1168 }
1169 
1171 {
1172  ControlsToValue();
1173  return mValue;
1174 }
1175 
1177 {
1178  // int ndx = 1;
1179  int ndx = std::min(1, GetNumBuiltins() - 1);
1180  int i;
1181 
1182  for (i = 0; i < GetNumBuiltins(); i++) {
1183  if (mFormatString == GetBuiltinFormat(i)) {
1184  ndx = i;
1185  break;
1186  }
1187  }
1188 
1189  return ndx;
1190 }
1191 
1193 {
1194  return mNBuiltins;
1195 }
1196 
1198 {
1199  if (index >= 0 && index < GetNumBuiltins())
1200  return mBuiltinFormatStrings[index].name;
1201 
1202  return {};
1203 }
1204 
1206 {
1207  if (index >= 0 && index < GetNumBuiltins())
1208  return mBuiltinFormatStrings[index].formatStrings;
1209 
1210  return {};
1211 }
1212 
1215 {
1216  int ndx =
1217  std::find( mBuiltinFormatStrings, mBuiltinFormatStrings + mNBuiltins,
1218  BuiltinFormatString{ name, {} } )
1219  - mBuiltinFormatStrings;
1220  if (ndx == (int)mNBuiltins)
1221  ndx = mDefaultNdx;
1222 
1223  return GetBuiltinFormat(ndx);
1224 }
1225 
1227 {
1228  ValueToControls();
1229 
1230  return mValueString;
1231 }
1232 
1234 {
1235  mFocusedDigit = mDigits.size() - 1;
1236  Adjust(1, 1);
1237 }
1238 
1240 {
1241  mFocusedDigit = mDigits.size() - 1;
1242  Adjust(1, -1);
1243 }
1244 
1245 void NumericConverter::Adjust(int steps, int dir)
1246 {
1247  // It is possible and "valid" for steps to be zero if a
1248  // high precision device is being used and wxWidgets supports
1249  // reporting a higher precision...Mac wx3 does.
1250  if (steps == 0)
1251  return;
1252 
1253  wxASSERT(dir == -1 || dir == 1);
1254  wxASSERT(steps > 0);
1255  if (steps < 0)
1256  steps = -steps;
1257 
1258  while (steps != 0)
1259  {
1260  for (size_t i = 0; i < mFields.size(); i++)
1261  {
1262  if ((mDigits[mFocusedDigit].pos >= mFields[i].pos) &&
1263  (mDigits[mFocusedDigit].pos < mFields[i].pos + mFields[i].digits))
1264  { //it's this field
1265  if (!mNtscDrop)
1266  {
1267  ControlsToValue();
1268  }
1269  else
1270  {
1271  mNtscDrop = false;
1272  ControlsToValue();
1273  mNtscDrop = true;
1274  }
1275 
1276  if (mValue < 0)
1277  mValue = 0;
1278 
1280 
1281  double mult = pow(10., mFields[i].digits - (mDigits[mFocusedDigit].pos - mFields[i].pos) - 1);
1282  if (mFields[i].frac)
1283  {
1284  mValue += ((mult / (double)mFields[i].base) * dir);
1285  }
1286  else
1287  {
1288  mValue += ((mult * (double)mFields[i].base) * dir);
1289  }
1290 
1291  if (mNtscDrop)
1292  {
1293  if ((mValue - (int)mValue) * 30 < 2)
1294  {
1295  if ((((int)mValue) % 60 == 0) && (((int)mValue) % 600 != 0))
1296  {
1297  mValue = (int)mValue + (dir > 0 ? 2. : -1.) / 30.;
1298  }
1299  }
1300  }
1301 
1302  if (mValue < 0.)
1303  {
1304  mValue = 0.;
1305  }
1306 
1307  mValue = std::max(mMinValue, std::min(mMaxValue, mValue));
1308 
1310 
1311  if (!mNtscDrop)
1312  {
1313  ValueToControls();
1314  }
1315  else
1316  {
1317  mNtscDrop = false;
1318  ValueToControls();
1319  mNtscDrop = true;
1320  ControlsToValue();
1321  }
1322  break;
1323  }
1324  }
1325  steps--;
1326  }
1327 
1328  ControlsToValue();
1329 }
1330 
1331 #define ID_MENU 9800
1332 
1333 // Custom events
1334 
1335 DEFINE_EVENT_TYPE(EVT_TIMETEXTCTRL_UPDATED)
1336 DEFINE_EVENT_TYPE(EVT_FREQUENCYTEXTCTRL_UPDATED)
1337 DEFINE_EVENT_TYPE(EVT_BANDWIDTHTEXTCTRL_UPDATED)
1338 
1339 BEGIN_EVENT_TABLE(NumericTextCtrl, wxControl)
1340  EVT_ERASE_BACKGROUND(NumericTextCtrl::OnErase)
1341  EVT_PAINT(NumericTextCtrl::OnPaint)
1342  EVT_CONTEXT_MENU(NumericTextCtrl::OnContext)
1343  EVT_MOUSE_EVENTS(NumericTextCtrl::OnMouse)
1344  EVT_KEY_DOWN(NumericTextCtrl::OnKeyDown)
1345  EVT_KEY_UP(NumericTextCtrl::OnKeyUp)
1346  EVT_SET_FOCUS(NumericTextCtrl::OnFocus)
1347  EVT_KILL_FOCUS(NumericTextCtrl::OnFocus)
1348  EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, NumericTextCtrl::OnCaptureKey)
1350 
1351 IMPLEMENT_CLASS(NumericTextCtrl, wxControl)
1352 
1353 NumericTextCtrl::NumericTextCtrl(wxWindow *parent, wxWindowID id,
1354  NumericConverter::Type type,
1355  const NumericFormatSymbol &formatName,
1356  double timeValue,
1357  double sampleRate,
1358  const Options &options,
1359  const wxPoint &pos,
1360  const wxSize &size):
1361  wxControl(parent, id, pos, size, wxSUNKEN_BORDER | wxWANTS_CHARS),
1362  NumericConverter(type, formatName, timeValue, sampleRate),
1363  mBackgroundBitmap{},
1364  mDigitFont{},
1365  mLabelFont{},
1366  mLastField(1),
1367  mAutoPos(options.autoPos)
1368  , mType(type)
1369 {
1370  mAllowInvalidValue = false;
1371 
1372  mDigitBoxW = 11;
1373  mDigitBoxH = 19;
1374 
1375  mBorderLeft = 1;
1376  mBorderTop = 1;
1377  mBorderRight = 1;
1378  mBorderBottom = 1;
1379 
1380  mReadOnly = options.readOnly;
1381  mMenuEnabled = options.menuEnabled;
1382  mButtonWidth = mMenuEnabled ? 9 : 0;
1383 
1384  SetLayoutDirection(wxLayout_LeftToRight);
1385  Layout();
1386  Fit();
1387  ValueToControls();
1388 
1389  //PRL -- would this fix the following?
1390  //ValueToControls();
1391 
1392  //mchinen - aug 15 09 - this seems to put the mValue back to zero, and do nothing else.
1393  //ControlsToValue();
1394 
1395  mScrollRemainder = 0.0;
1396 
1397 #if wxUSE_ACCESSIBILITY
1398  SetLabel(wxT(""));
1399  SetName( {} );
1400  SetAccessible(safenew NumericTextCtrlAx(this));
1401 #endif
1402 
1403  if (options.hasInvalidValue)
1404  SetInvalidValue( options.invalidValue );
1405 
1406  if (!options.format.formatStr.empty())
1407  SetFormatString( options.format );
1408 
1409  if (options.hasValue)
1410  SetValue( options.value );
1411 
1412 }
1413 
1415 {
1416 }
1417 
1419 {
1420  wxControl::SetName( name.Translation() );
1421 }
1422 
1423 // Set the focus to the first (left-most) non-zero digit
1424 // If all digits are zero, the right-most position is focused
1425 // If all digits are hyphens (invalid), the left-most position is focused
1427 {
1428  if (!mAutoPos)
1429  return;
1430 
1431  mFocusedDigit = 0;
1432  while (mFocusedDigit < ((int)mDigits.size() - 1)) {
1433  wxChar dgt = mValueString[mDigits[mFocusedDigit].pos];
1434  if (dgt != '0') {
1435  break;
1436  }
1437  mFocusedDigit++;
1438  }
1439 }
1440 
1442 {
1443  return
1444  SetFormatString(GetBuiltinFormat(formatName));
1445 }
1446 
1448 {
1449  auto result =
1450  NumericConverter::SetFormatString(formatString);
1451  if (result) {
1452  Layout();
1453  Fit();
1454  ValueToControls();
1455  ControlsToValue();
1456  UpdateAutoFocus();
1457  }
1458  return result;
1459 }
1460 
1461 void NumericTextCtrl::SetSampleRate(double sampleRate)
1462 {
1463  NumericConverter::SetSampleRate(sampleRate);
1464  Layout();
1465  Fit();
1466  ValueToControls();
1467  ControlsToValue();
1468 }
1469 
1470 void NumericTextCtrl::SetValue(double newValue)
1471 {
1472  NumericConverter::SetValue(newValue);
1473  ValueToControls();
1474  ControlsToValue();
1475 }
1476 
1477 void NumericTextCtrl::SetDigitSize(int width, int height)
1478 {
1479  mDigitBoxW = width;
1480  mDigitBoxH = height;
1481  Layout();
1482  Fit();
1483 }
1484 
1485 void NumericTextCtrl::SetReadOnly(bool readOnly)
1486 {
1487  mReadOnly = readOnly;
1488 }
1489 
1491 {
1492 #if wxUSE_TOOLTIPS
1493  wxString tip(_("(Use context menu to change format.)"));
1494  if (enable)
1495  SetToolTip(tip);
1496  else {
1497  wxToolTip *tt = GetToolTip();
1498  if (tt && tt->GetTip() == tip)
1499  SetToolTip(NULL);
1500  }
1501 #endif
1502  mMenuEnabled = enable;
1503  mButtonWidth = enable ? 9 : 0;
1504  Layout();
1505  Fit();
1506 }
1507 
1508 void NumericTextCtrl::SetInvalidValue(double invalidValue)
1509 {
1510  const bool wasInvalid = mAllowInvalidValue && (mValue == mInvalidValue);
1511  mAllowInvalidValue = true;
1512  mInvalidValue = invalidValue;
1513  if (wasInvalid)
1514  SetValue(invalidValue);
1515 }
1516 
1517 wxSize NumericTextCtrl::ComputeSizing(bool update, wxCoord boxW, wxCoord boxH)
1518 {
1519  // Get current box size
1520  if (boxW == 0) {
1521  boxW = mDigitBoxW;
1522  }
1523 
1524  if (boxH == 0) {
1525  boxH = mDigitBoxH;
1526  }
1527  boxH -= (mBorderTop + mBorderBottom);
1528 
1529  // We can use the screen device context since we're not drawing to it
1530  wxScreenDC dc;
1531 
1532  // First calculate a rough point size
1533  wxFont pf(wxSize(boxW, boxH), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1534  int fontSize = pf.GetPointSize();
1535  wxCoord strW;
1536  wxCoord strH;
1537 
1538  // Now decrease it until we fit within our digit box
1539  dc.SetFont(pf);
1540  dc.GetTextExtent(wxT("0"), &strW, &strH);
1541  while (strW > boxW || strH > boxH) {
1542  dc.SetFont(wxFont(--fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
1543  dc.GetTextExtent(wxT("0"), &strW, &strH);
1544  }
1545  fontSize--;
1546 
1547  // Create the digit font with the new point size
1548  if (update) {
1549  mDigitFont = std::make_unique<wxFont>(fontSize, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1550  dc.SetFont(*mDigitFont);
1551 
1552  // Remember the actual digit width and height using the new font
1553  dc.GetTextExtent(wxT("0"), &mDigitW, &mDigitH);
1554  }
1555 
1556  // The label font should be a little smaller
1557  std::unique_ptr<wxFont> labelFont = std::make_unique<wxFont>(fontSize - 1, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1558 
1559  // Use the label font for all remaining measurements since only non-digit text is left
1560  dc.SetFont(*labelFont);
1561 
1562  // Remember the pointer if updating
1563  if (update) {
1564  mLabelFont = std::move(labelFont);
1565  }
1566 
1567  // Get the width of the prefix, if any
1568  dc.GetTextExtent(mPrefix, &strW, &strH);
1569 
1570  // Bump x-position to the end of the prefix
1571  int x = mBorderLeft + strW;
1572 
1573  if (update) {
1574  // Set the character position past the prefix
1575  int pos = mPrefix.length();
1576 
1577  // Reset digits array
1578  mDigits.clear();
1579 
1580  // Figure out the x-position of each field and label in the box
1581  for (int i = 0, fcnt = mFields.size(); i < fcnt; ++i) {
1582  // Get the size of the label
1583  dc.GetTextExtent(mFields[i].label, &strW, &strH);
1584 
1585  // Remember this field's x-position
1586  mFields[i].fieldX = x;
1587 
1588  // Remember metrics for each digit
1589  for (int j = 0, dcnt = mFields[i].digits; j < dcnt; ++j) {
1590  mDigits.push_back(DigitInfo(i, j, pos, wxRect(x, mBorderTop, boxW, boxH)));
1591  x += boxW;
1592  pos++;
1593  }
1594 
1595  // Remember the label's x-position
1596  mFields[i].labelX = x;
1597 
1598  // Bump to end of label
1599  x += strW;
1600 
1601  // Remember the label's width
1602  mFields[i].fieldW = x;
1603 
1604  // Bump character position to end of label
1605  pos += mFields[i].label.length();
1606  }
1607  }
1608  else {
1609  // Determine the maximum x-position (length) of the remaining fields
1610  for (int i = 0, fcnt = mFields.size(); i < fcnt; ++i) {
1611  // Get the size of the label
1612  dc.GetTextExtent(mFields[i].label, &strW, &strH);
1613 
1614  // Just bump to next field
1615  x += (boxW * mFields[i].digits) + strW;
1616  }
1617  }
1618 
1619  // Calculate the maximum dimensions
1620  wxSize dim(x + mBorderRight, boxH + mBorderTop + mBorderBottom);
1621 
1622  // Save maximumFinally, calculate the minimum dimensions
1623  if (update) {
1624  mWidth = dim.x;
1625  mHeight = dim.y;
1626  }
1627 
1628  return wxSize(dim.x + mButtonWidth, dim.y);
1629 }
1630 
1632 {
1633  ComputeSizing();
1634 
1635  wxMemoryDC memDC;
1636  wxCoord strW, strH;
1637  memDC.SetFont(*mLabelFont);
1638  memDC.GetTextExtent(mPrefix, &strW, &strH);
1639 
1640  int i;
1641 
1642  // Draw the background bitmap - it contains black boxes where
1643  // all of the digits go and all of the other text
1644 
1645  wxBrush Brush;
1646 
1647  mBackgroundBitmap = std::make_unique<wxBitmap>(mWidth + mButtonWidth, mHeight,24);
1648  memDC.SelectObject(*mBackgroundBitmap);
1649 
1650  theTheme.SetBrushColour( Brush, clrTimeHours );
1651  memDC.SetBrush(Brush);
1652  memDC.SetPen(*wxTRANSPARENT_PEN);
1653  memDC.DrawRectangle(0, 0, mWidth + mButtonWidth, mHeight);
1654 
1655  int numberBottom = mBorderTop + (mDigitBoxH - mDigitH)/2 + mDigitH;
1656 
1657  memDC.GetTextExtent(wxT("0"), &strW, &strH);
1658  int labelTop = numberBottom - strH;
1659 
1660  memDC.SetTextForeground(theTheme.Colour( clrTimeFont ));
1661  memDC.SetTextBackground(theTheme.Colour( clrTimeBack ));
1662  memDC.DrawText(mPrefix, mBorderLeft, labelTop);
1663 
1664  theTheme.SetBrushColour( Brush, clrTimeBack );
1665  memDC.SetBrush(Brush);
1666  //memDC.SetBrush(*wxLIGHT_GREY_BRUSH);
1667  for(i = 0; i < mDigits.size(); i++)
1668  memDC.DrawRectangle(mDigits[i].digitBox);
1669  memDC.SetBrush( wxNullBrush );
1670 
1671  for(i = 0; i < mFields.size(); i++)
1672  memDC.DrawText(mFields[i].label,
1673  mFields[i].labelX, labelTop);
1674 
1675  if (mMenuEnabled) {
1676  wxRect r(mWidth, 0, mButtonWidth - 1, mHeight - 1);
1677  AColor::Bevel(memDC, true, r);
1678  memDC.SetBrush(*wxBLACK_BRUSH);
1679  memDC.SetPen(*wxBLACK_PEN);
1680  AColor::Arrow(memDC,
1681  mWidth + 1,
1682  (mHeight / 2) - 2,
1683  mButtonWidth - 2);
1684  }
1685  return true;
1686 }
1687 
1689 {
1690  wxSize sz = GetSize();
1691  wxSize csz = GetClientSize();
1692 
1693  sz.x = mButtonWidth + mWidth + (sz.x - csz.x);
1694  sz.y = mHeight + (sz.y - csz.y);
1695 
1696  SetInitialSize(sz);
1697 }
1698 
1699 void NumericTextCtrl::OnErase(wxEraseEvent & WXUNUSED(event))
1700 {
1701  // Ignore it to prevent flashing
1702 }
1703 
1704 void NumericTextCtrl::OnPaint(wxPaintEvent & WXUNUSED(event))
1705 {
1706  wxBufferedPaintDC dc(this);
1707  bool focused = (FindFocus() == this);
1708 
1709  dc.DrawBitmap(*mBackgroundBitmap, 0, 0);
1710 
1711  wxPen Pen;
1712  wxBrush Brush;
1713  if (focused) {
1714  theTheme.SetPenColour( Pen, clrTimeFontFocus );
1715  dc.SetPen(Pen);
1716  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1717  dc.DrawRectangle(0, 0, mWidth, mHeight);
1718  dc.SetPen( wxNullPen );
1719  }
1720 
1721  dc.SetFont(*mDigitFont);
1722  dc.SetTextForeground(theTheme.Colour( clrTimeFont ));
1723  dc.SetTextBackground(theTheme.Colour( clrTimeBack ));
1724 
1725  dc.SetPen(*wxTRANSPARENT_PEN);
1726  theTheme.SetBrushColour( Brush , clrTimeBackFocus );
1727  dc.SetBrush( Brush );
1728 
1729  int i;
1730  for(i = 0; i < (int)mDigits.size(); i++) {
1731  wxRect box = mDigits[i].digitBox;
1732  if (focused && mFocusedDigit == i) {
1733  dc.DrawRectangle(box);
1734  dc.SetTextForeground(theTheme.Colour( clrTimeFontFocus ));
1735  dc.SetTextBackground(theTheme.Colour( clrTimeBackFocus ));
1736  }
1737  int pos = mDigits[i].pos;
1738  wxString digit = mValueString.Mid(pos, 1);
1739  int x = box.x + (mDigitBoxW - mDigitW)/2;
1740  int y = box.y + (mDigitBoxH - mDigitH)/2;
1741  dc.DrawText(digit, x, y);
1742  if (focused && mFocusedDigit == i) {
1743  dc.SetTextForeground(theTheme.Colour( clrTimeFont ));
1744  dc.SetTextBackground(theTheme.Colour( clrTimeBack ));
1745  }
1746  }
1747  dc.SetPen( wxNullPen );
1748  dc.SetBrush( wxNullBrush );
1749 }
1750 
1751 void NumericTextCtrl::OnContext(wxContextMenuEvent &event)
1752 {
1753  wxMenu menu;
1754  int i;
1755 
1756  if (!mMenuEnabled) {
1757  event.Skip();
1758  return;
1759  }
1760 
1761  SetFocus();
1762 
1763  int currentSelection = -1;
1764  for (i = 0; i < GetNumBuiltins(); i++) {
1765  menu.AppendRadioItem(ID_MENU + i, GetBuiltinName(i).Translation());
1766  if (mFormatString == GetBuiltinFormat(i)) {
1767  menu.Check(ID_MENU + i, true);
1768  currentSelection = i;
1769  }
1770  }
1771 
1772  PopupMenu(&menu, wxPoint(0, 0));
1773 
1774  // This used to be in an EVT_MENU() event handler, but GTK
1775  // is sensitive to what is done within the handler if the
1776  // user happens to check the first menuitem and then is
1777  // moving down the menu when the ...CTRL_UPDATED event
1778  // handler kicks in.
1779  for (i = 0; i < GetNumBuiltins(); i++) {
1780  if (menu.IsChecked(ID_MENU + i) && i != currentSelection) {
1782 
1783  int eventType = 0;
1784  switch (mType) {
1786  eventType = EVT_TIMETEXTCTRL_UPDATED;
1787  break;
1789  eventType = EVT_FREQUENCYTEXTCTRL_UPDATED;
1790  break;
1792  eventType = EVT_BANDWIDTHTEXTCTRL_UPDATED;
1793  break;
1794  default:
1795  wxASSERT(false);
1796  break;
1797  }
1798 
1799  wxCommandEvent e(eventType, GetId());
1800  e.SetInt(i);
1801  e.SetString(GetBuiltinName(i).Internal());
1802  GetParent()->GetEventHandler()->AddPendingEvent(e);
1803  }
1804  }
1805 
1806 }
1807 
1808 void NumericTextCtrl::OnMouse(wxMouseEvent &event)
1809 {
1810  if (event.LeftDown() && event.GetX() >= mWidth) {
1811  wxContextMenuEvent e;
1812  OnContext(e);
1813  }
1814  else if (event.LeftDown()) {
1815  SetFocus();
1816 
1817  int bestDist = 9999;
1818  unsigned int i;
1819 
1820  mFocusedDigit = 0;
1821  for(i = 0; i < mDigits.size(); i++) {
1822  int dist = abs(event.m_x - (mDigits[i].digitBox.x +
1823  mDigits[i].digitBox.width/2));
1824  if (dist < bestDist) {
1825  mFocusedDigit = i;
1826  bestDist = dist;
1827  }
1828  }
1829 
1830  Refresh(false);
1831  }
1832  else if (event.RightDown() && mMenuEnabled) {
1833  wxContextMenuEvent e;
1834  OnContext(e);
1835  }
1836  else if(!mReadOnly && event.m_wheelRotation != 0 ) {
1837  double steps = event.m_wheelRotation /
1838  (event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0) +
1840  mScrollRemainder = steps - floor(steps);
1841  steps = floor(steps);
1842 
1843  Adjust((int)fabs(steps), steps < 0.0 ? -1 : 1);
1844  Updated();
1845  }
1846 }
1847 
1848 void NumericTextCtrl::OnFocus(wxFocusEvent &event)
1849 {
1850  KeyboardCapture::OnFocus( *this, event );
1851 
1852  if (event.GetEventType() != wxEVT_KILL_FOCUS &&
1853  mFocusedDigit <= 0 )
1854  UpdateAutoFocus();
1855 
1856  event.Skip( false ); // PRL: not sure why, but preserving old behavior
1857 }
1858 
1859 void NumericTextCtrl::OnCaptureKey(wxCommandEvent &event)
1860 {
1861  wxKeyEvent *kevent = (wxKeyEvent *)event.GetEventObject();
1862  int keyCode = kevent->GetKeyCode();
1863 
1864  // Convert numeric keypad entries.
1865  if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9))
1866  keyCode -= WXK_NUMPAD0 - '0';
1867 
1868  switch (keyCode)
1869  {
1870  case WXK_BACK:
1871  case WXK_LEFT:
1872  case WXK_RIGHT:
1873  case WXK_HOME:
1874  case WXK_END:
1875  case WXK_UP:
1876  case WXK_DOWN:
1877  case WXK_TAB:
1878  case WXK_RETURN:
1879  case WXK_NUMPAD_ENTER:
1880  case WXK_DELETE:
1881  return;
1882 
1883  default:
1884  if (keyCode >= '0' && keyCode <= '9' && !kevent->HasAnyModifiers())
1885  return;
1886  }
1887 
1888  event.Skip();
1889 
1890  return;
1891 }
1892 
1893 void NumericTextCtrl::OnKeyUp(wxKeyEvent &event)
1894 {
1895  int keyCode = event.GetKeyCode();
1896 
1897  event.Skip(true);
1898 
1899  if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9))
1900  keyCode -= WXK_NUMPAD0 - '0';
1901 
1902  if ((keyCode >= '0' && keyCode <= '9' && !event.HasAnyModifiers()) ||
1903  (keyCode == WXK_DELETE) ||
1904  (keyCode == WXK_BACK) ||
1905  (keyCode == WXK_UP) ||
1906  (keyCode == WXK_DOWN)) {
1907  Updated(true);
1908  }
1909 }
1910 
1911 void NumericTextCtrl::OnKeyDown(wxKeyEvent &event)
1912 {
1913  if (mDigits.size() == 0)
1914  {
1915  mFocusedDigit = 0;
1916  return;
1917  }
1918 
1919  event.Skip(false);
1920 
1921  int keyCode = event.GetKeyCode();
1922  int digit = mFocusedDigit;
1923 
1924  if (mFocusedDigit < 0)
1925  mFocusedDigit = 0;
1926  if (mFocusedDigit >= (int)mDigits.size())
1927  mFocusedDigit = mDigits.size() - 1;
1928 
1929  // Convert numeric keypad entries.
1930  if ((keyCode >= WXK_NUMPAD0) && (keyCode <= WXK_NUMPAD9))
1931  keyCode -= WXK_NUMPAD0 - '0';
1932 
1933  if (!mReadOnly && (keyCode >= '0' && keyCode <= '9' && !event.HasAnyModifiers())) {
1934  int digitPosition = mDigits[mFocusedDigit].pos;
1935  if (mValueString[digitPosition] == wxChar('-')) {
1936  mValue = std::max(mMinValue, std::min(mMaxValue, 0.0));
1937  ValueToControls();
1938  // Beware relocation of the string
1939  digitPosition = mDigits[mFocusedDigit].pos;
1940  }
1941  mValueString[digitPosition] = wxChar(keyCode);
1942  ControlsToValue();
1943  Refresh();// Force an update of the control. [Bug 1497]
1944  ValueToControls();
1945  mFocusedDigit = (mFocusedDigit + 1) % (mDigits.size());
1946  Updated();
1947  }
1948 
1949  else if (!mReadOnly && keyCode == WXK_DELETE) {
1950  if (mAllowInvalidValue)
1952  }
1953 
1954  else if (!mReadOnly && keyCode == WXK_BACK) {
1955  // Moves left, replaces that char with '0', stays there...
1956  mFocusedDigit--;
1957  mFocusedDigit += mDigits.size();
1958  mFocusedDigit %= mDigits.size();
1959  wxString::reference theDigit = mValueString[mDigits[mFocusedDigit].pos];
1960  if (theDigit != wxChar('-'))
1961  theDigit = '0';
1962  ControlsToValue();
1963  Refresh();// Force an update of the control. [Bug 1497]
1964  ValueToControls();
1965  Updated();
1966  }
1967 
1968  else if (keyCode == WXK_LEFT) {
1969  mFocusedDigit--;
1970  mFocusedDigit += mDigits.size();
1971  mFocusedDigit %= mDigits.size();
1972  Refresh();
1973  }
1974 
1975  else if (keyCode == WXK_RIGHT) {
1976  mFocusedDigit++;
1977  mFocusedDigit %= mDigits.size();
1978  Refresh();
1979  }
1980 
1981  else if (keyCode == WXK_HOME) {
1982  mFocusedDigit = 0;
1983  Refresh();
1984  }
1985 
1986  else if (keyCode == WXK_END) {
1987  mFocusedDigit = mDigits.size() - 1;
1988  Refresh();
1989  }
1990 
1991  else if (!mReadOnly && keyCode == WXK_UP) {
1992  Adjust(1, 1);
1993  Updated();
1994  }
1995 
1996  else if (!mReadOnly && keyCode == WXK_DOWN) {
1997  Adjust(1, -1);
1998  Updated();
1999  }
2000 
2001  else if (keyCode == WXK_TAB) {
2002 #if defined(__WXMSW__)
2003  // Using Navigate() on Windows, rather than the following code causes
2004  // bug 1542
2005  wxWindow* parent = GetParent();
2006  wxNavigationKeyEvent nevent;
2007  nevent.SetWindowChange(event.ControlDown());
2008  nevent.SetDirection(!event.ShiftDown());
2009  nevent.SetEventObject(parent);
2010  nevent.SetCurrentFocus(parent);
2011  GetParent()->GetEventHandler()->ProcessEvent(nevent);
2012 #else
2013  Navigate(event.ShiftDown()
2014  ? wxNavigationKeyEvent::IsBackward
2015  : wxNavigationKeyEvent::IsForward);
2016 #endif
2017  }
2018 
2019  else if (keyCode == WXK_RETURN || keyCode == WXK_NUMPAD_ENTER) {
2020  wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
2021  wxWindow *def = tlw->GetDefaultItem();
2022  if (def && def->IsEnabled()) {
2023  wxCommandEvent cevent(wxEVT_COMMAND_BUTTON_CLICKED,
2024  def->GetId());
2025  GetParent()->GetEventHandler()->ProcessEvent(cevent);
2026  }
2027  }
2028 
2029  else {
2030  event.Skip();
2031  return;
2032  }
2033 
2034  if (digit != mFocusedDigit) {
2036  }
2037 }
2038 
2040 {
2041 #if wxUSE_ACCESSIBILITY
2042  if (mDigits.size() == 0)
2043  {
2044  mFocusedDigit = 0;
2045  return;
2046  }
2047  mFocusedDigit = digit;
2048  mLastField = mDigits[mFocusedDigit].field + 1;
2049 
2050  GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
2051  this,
2052  wxOBJID_CLIENT,
2053  mFocusedDigit + 1);
2054 #endif
2055 }
2056 
2057 void NumericTextCtrl::Updated(bool keyup /* = false */)
2058 {
2059  wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
2060 
2061  // This will give listeners the ability to do tasks when the
2062  // update has been completed, like when the UP ARROW has been
2063  // held down and is finally released.
2064  event.SetInt(keyup);
2065  event.SetEventObject(this);
2066  GetEventHandler()->ProcessEvent(event);
2067 
2068 #if wxUSE_ACCESSIBILITY
2069  if (!keyup) {
2070  if (mDigits.size() == 0)
2071  {
2072  mFocusedDigit = 0;
2073  return;
2074  }
2075 
2076  // The object_focus event is only needed by Window-Eyes
2077  // and can be removed when we cease to support this screen reader.
2078  GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
2079  this,
2080  wxOBJID_CLIENT,
2081  mFocusedDigit + 1);
2082 
2083  GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_NAMECHANGE,
2084  this,
2085  wxOBJID_CLIENT,
2086  mFocusedDigit + 1);
2087  }
2088 #endif
2089 }
2090 
2092 {
2093  const wxString previousValueString = mValueString;
2095  if (mValueString != previousValueString) {
2096  // Doing this only when needed is an optimization.
2097  // NumerixTextCtrls are used in the selection bar at the bottom
2098  // of Audacity, and are updated at high frequency through
2099  // SetValue() when Audacity is playing. This consumes a
2100  // significant amount of CPU. Typically, when a track is
2101  // playing, only one of the NumericTextCtrl actually changes
2102  // (the audio position). We save CPU by updating the control
2103  // only when needed.
2104  Refresh(false);
2105  }
2106 }
2107 
2108 
2110 {
2112 }
2113 
2114 #if wxUSE_ACCESSIBILITY
2115 
2116 NumericTextCtrlAx::NumericTextCtrlAx(NumericTextCtrl *ctrl)
2117 : WindowAccessible(ctrl)
2118 {
2119  mCtrl = ctrl;
2120  mLastField = -1;
2121  mLastDigit = -1;
2122 }
2123 
2124 NumericTextCtrlAx::~NumericTextCtrlAx()
2125 {
2126 }
2127 
2128 // Performs the default action. childId is 0 (the action for this object)
2129 // or > 0 (the action for a child).
2130 // Return wxACC_NOT_SUPPORTED if there is no default action for this
2131 // window (e.g. an edit control).
2132 wxAccStatus NumericTextCtrlAx::DoDefaultAction(int WXUNUSED(childId))
2133 {
2134  return wxACC_NOT_SUPPORTED;
2135 }
2136 
2137 // Retrieves the address of an IDispatch interface for the specified child.
2138 // All objects must support this property.
2139 wxAccStatus NumericTextCtrlAx::GetChild(int childId, wxAccessible **child)
2140 {
2141  if (childId == wxACC_SELF) {
2142  *child = this;
2143  }
2144  else {
2145  *child = NULL;
2146  }
2147 
2148  return wxACC_OK;
2149 }
2150 
2151 // Gets the number of children.
2152 wxAccStatus NumericTextCtrlAx::GetChildCount(int *childCount)
2153 {
2154  *childCount = mCtrl->mDigits.size();
2155 
2156  return wxACC_OK;
2157 }
2158 
2159 // Gets the default action for this object (0) or > 0 (the action for
2160 // a child). Return wxACC_OK even if there is no action. actionName
2161 // is the action, or the empty string if there is no action. The
2162 // retrieved string describes the action that is performed on an
2163 // object, not what the object does as a result. For example, a
2164 // toolbar button that prints a document has a default action of
2165 // "Press" rather than "Prints the current document."
2166 wxAccStatus NumericTextCtrlAx::GetDefaultAction(int WXUNUSED(childId), wxString *actionName)
2167 {
2168  actionName->clear();
2169 
2170  return wxACC_OK;
2171 }
2172 
2173 // Returns the description for this object or a child.
2174 wxAccStatus NumericTextCtrlAx::GetDescription(int WXUNUSED(childId), wxString *description)
2175 {
2176  description->clear();
2177 
2178  return wxACC_OK;
2179 }
2180 
2181 // Gets the window with the keyboard focus.
2182 // If childId is 0 and child is NULL, no object in
2183 // this subhierarchy has the focus.
2184 // If this object has the focus, child should be 'this'.
2185 wxAccStatus NumericTextCtrlAx::GetFocus(int *childId, wxAccessible **child)
2186 {
2187  *childId = mCtrl->GetFocusedDigit();
2188  *child = this;
2189 
2190  return wxACC_OK;
2191 }
2192 
2193 // Returns help text for this object or a child, similar to tooltip text.
2194 wxAccStatus NumericTextCtrlAx::GetHelpText(int WXUNUSED(childId), wxString *helpText)
2195 {
2196 // removed help text, as on balance it's more of an irritation than useful
2197 #if 0 // was #if wxUSE_TOOLTIPS
2198  wxToolTip *pTip = mCtrl->GetToolTip();
2199  if (pTip) {
2200  *helpText = pTip->GetTip();
2201  }
2202 
2203  return wxACC_OK;
2204 #else
2205  helpText->clear();
2206 
2207  return wxACC_NOT_SUPPORTED;
2208 #endif
2209 }
2210 
2211 // Returns the keyboard shortcut for this object or child.
2212 // Return e.g. ALT+K
2213 wxAccStatus NumericTextCtrlAx::GetKeyboardShortcut(int WXUNUSED(childId), wxString *shortcut)
2214 {
2215  shortcut->clear();
2216 
2217  return wxACC_OK;
2218 }
2219 
2220 // Returns the rectangle for this object (id = 0) or a child element (id > 0).
2221 // rect is in screen coordinates.
2222 wxAccStatus NumericTextCtrlAx::GetLocation(wxRect & rect, int elementId)
2223 {
2224  if ((elementId != wxACC_SELF) &&
2225  // We subtract 1, below, and need to avoid neg index to mDigits.
2226  (elementId > 0))
2227  {
2228 // rect.x += mCtrl->mFields[elementId - 1].fieldX;
2229 // rect.width = mCtrl->mFields[elementId - 1].fieldW;
2230  rect = mCtrl->mDigits[elementId - 1].digitBox;
2231  rect.SetPosition(mCtrl->ClientToScreen(rect.GetPosition()));
2232  }
2233  else
2234  {
2235  rect = mCtrl->GetRect();
2236  rect.SetPosition(mCtrl->GetParent()->ClientToScreen(rect.GetPosition()));
2237  }
2238 
2239  return wxACC_OK;
2240 }
2241 
2242 static void GetFraction( wxString &label,
2243  const NumericConverter::FormatStrings &formatStrings,
2244  bool isTime, int digits )
2245 {
2246  TranslatableString tr = formatStrings.fraction;
2247  if ( tr.empty() ) {
2248  wxASSERT( isTime );
2249  if (digits == 2)
2250  tr = XO("centiseconds");
2251  else if (digits == 3)
2252  tr = XO("milliseconds");
2253  }
2254  if (!tr.empty())
2255  label = tr.Translation();
2256 }
2257 
2258 // Gets the name of the specified object.
2259 wxAccStatus NumericTextCtrlAx::GetName(int childId, wxString *name)
2260 {
2261  // Slightly messy trick to save us some prefixing.
2262  std::vector<NumericField> & mFields = mCtrl->mFields;
2263 
2264  wxString ctrlString = mCtrl->GetString();
2265  int field = mCtrl->GetFocusedField();
2266 
2267  // Return the entire string including the control label
2268  // when the requested child ID is wxACC_SELF. (Mainly when
2269  // the control gets the focus.)
2270  if ((childId == wxACC_SELF) ||
2271  // We subtract 1 from childId in the other cases below, and
2272  // need to avoid neg index to mDigits, so funnel into this clause.
2273  (childId < 1))
2274  {
2275  *name = mCtrl->GetName();
2276  if (name->empty()) {
2277  *name = mCtrl->GetLabel();
2278  }
2279 
2280  *name += wxT(" ") +
2281  mCtrl->GetString();
2282  }
2283  // This case is needed because of the behaviour of Narrator, which
2284  // is different for the other Windows screen readers. After a focus event,
2285  // Narrator causes getName() to be called more than once. However, the code in
2286  // the following else statement assumes that it is executed only once
2287  // when the focus has been moved to another digit. This else if statement
2288  // ensures that this is the case, by using a cached value if nothing
2289  // has changed.
2290  else if (childId == mLastDigit && ctrlString.IsSameAs(mLastCtrlString)) {
2291  *name = mCachedName;
2292  }
2293  else {
2294  // The user has moved from one field of the time to another so
2295  // report the value of the field and the field's label.
2296  if (mLastField != field) {
2297  wxString label = mFields[field - 1].label;
2298  int cnt = mFields.size();
2299  wxString decimal = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
2300 
2301  // If the NEW field is the last field, then check it to see if
2302  // it represents fractions of a second.
2303  // PRL: click a digit of the control and use left and right arrow keys
2304  // to exercise this code
2305  const bool isTime = (mCtrl->mType == NumericTextCtrl::TIME);
2306  if (field > 1 && field == cnt) {
2307  if (mFields[field - 2].label == decimal) {
2308  int digits = mFields[field - 1].digits;
2309  GetFraction( label, mCtrl->mFormatString,
2310  isTime, digits );
2311  }
2312  }
2313  // If the field following this one represents fractions of a
2314  // second then use that label instead of the decimal point.
2315  else if (label == decimal && field == cnt - 1) {
2316  label = mFields[field].label;
2317  }
2318 
2319  *name = mFields[field - 1].str +
2320  wxT(" ") +
2321  label +
2322  wxT(", ") + // comma inserts a slight pause
2323  mCtrl->GetString().at(mCtrl->mDigits[childId - 1].pos);
2324  mLastField = field;
2325  mLastDigit = childId;
2326  }
2327  // The user has moved from one digit to another within a field so
2328  // just report the digit under the cursor.
2329  else if (mLastDigit != childId) {
2330  *name = mCtrl->GetString().at(mCtrl->mDigits[childId - 1].pos);
2331  mLastDigit = childId;
2332  }
2333  // The user has updated the value of a field, so report the field's
2334  // value only.
2335  else if (field > 0)
2336  {
2337  *name = mFields[field - 1].str;
2338  }
2339 
2340  mCachedName = *name;
2341  mLastCtrlString = ctrlString;
2342  }
2343 
2344  return wxACC_OK;
2345 }
2346 
2347 // Returns a role constant.
2348 wxAccStatus NumericTextCtrlAx::GetRole(int WXUNUSED(childId), wxAccRole *role)
2349 {
2350  *role = wxROLE_SYSTEM_STATICTEXT;
2351  return wxACC_OK;
2352 }
2353 
2354 // Gets a variant representing the selected children
2355 // of this object.
2356 // Acceptable values:
2357 // - a null variant (IsNull() returns TRUE)
2358 // - a list variant (GetType() == wxT("list"))
2359 // - an integer representing the selected child element,
2360 // or 0 if this object is selected (GetType() == wxT("long"))
2361 // - a "void*" pointer to a wxAccessible child object
2362 wxAccStatus NumericTextCtrlAx::GetSelections(wxVariant * WXUNUSED(selections))
2363 {
2364  return wxACC_NOT_IMPLEMENTED;
2365 }
2366 
2367 // Returns a state constant.
2368 wxAccStatus NumericTextCtrlAx::GetState(int WXUNUSED(childId), long *state)
2369 {
2370  *state = wxACC_STATE_SYSTEM_FOCUSABLE;
2371  *state |= (mCtrl == wxWindow::FindFocus() ? wxACC_STATE_SYSTEM_FOCUSED : 0);
2372 
2373  return wxACC_OK;
2374 }
2375 
2376 // Returns a localized string representing the value for the object
2377 // or child.
2378 wxAccStatus NumericTextCtrlAx::GetValue(int WXUNUSED(childId), wxString * WXUNUSED(strValue))
2379 {
2380  return wxACC_NOT_IMPLEMENTED;
2381 }
2382 
2383 #endif
2384 
NumericField::fieldX
int fieldX
Definition: NumericTextCtrl.cpp:332
TranslatableString
Definition: Types.h:290
field
#define field(n, t)
Definition: ImportAUP.cpp:165
NumericField::label
wxString label
Definition: NumericTextCtrl.cpp:336
NumericTextCtrl::Updated
void Updated(bool keyup=false)
Definition: NumericTextCtrl.cpp:2057
TranslatableString::empty
bool empty() const
Definition: Types.h:329
NumericTextCtrl::SetFormatName
bool SetFormatName(const NumericFormatSymbol &formatName)
Definition: NumericTextCtrl.cpp:1441
NumericConverter::SetFormatString
bool SetFormatString(const FormatStrings &formatString)
Definition: NumericTextCtrl.cpp:1113
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:1165
ComponentInterfaceSymbol::Translation
const wxString Translation() const
Definition: ComponentInterface.h:91
NumericField::frac
bool frac
Definition: NumericTextCtrl.cpp:327
NumericConverter::SetFormatName
bool SetFormatName(const NumericFormatSymbol &formatName)
Definition: NumericTextCtrl.cpp:1107
DigitInfo::DigitInfo
DigitInfo(int _field, int _index, int _pos, wxRect _box)
Definition: NumericTextCtrl.cpp:349
NumericConverter::~NumericConverter
virtual ~NumericConverter()
Definition: NumericTextCtrl.cpp:941
EVT_COMMAND
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:91
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:1688
NumericTextCtrl::mLastField
int mLastField
Definition: NumericTextCtrl.h:285
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:1447
NumericTextCtrl::mBorderTop
int mBorderTop
Definition: NumericTextCtrl.h:278
NumericField::range
int range
Definition: NumericTextCtrl.cpp:329
NumericTextCtrl::Layout
bool Layout() override
Definition: NumericTextCtrl.cpp:1631
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:1418
NumericConverter::FormatStrings::fraction
TranslatableString fraction
Definition: NumericTextCtrl.h:61
XO
#define XO(s)
Definition: Internat.h:32
DigitInfo::index
int index
Definition: NumericTextCtrl.cpp:357
NumericConverter::mFields
std::vector< NumericField > mFields
Definition: NumericTextCtrl.h:149
NumericConverter::mScalingFactor
double mScalingFactor
Definition: NumericTextCtrl.h:156
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:94
NumericField::zeropad
bool zeropad
Definition: NumericTextCtrl.cpp:335
NumericConverter::Increment
void Increment()
Definition: NumericTextCtrl.cpp:1233
NumericTextCtrl::SetDigitSize
void SetDigitSize(int width, int height)
Definition: NumericTextCtrl.cpp:1477
NumericConverter::mNBuiltins
const size_t mNBuiltins
Definition: NumericTextCtrl.h:164
NumericConverter::mValue
double mValue
Definition: NumericTextCtrl.h:141
NumericConverter::mValueMask
wxString mValueMask
Definition: NumericTextCtrl.h:152
NumericTextCtrl::mBorderBottom
int mBorderBottom
Definition: NumericTextCtrl.h:280
NumericTextCtrl::ComputeSizing
wxSize ComputeSizing(bool update=true, wxCoord digitW=0, wxCoord digitH=0)
Definition: NumericTextCtrl.cpp:1517
NumericTextCtrl::mBorderLeft
int mBorderLeft
Definition: NumericTextCtrl.h:277
NumericConverter::ValueToControls
virtual void ValueToControls()
Definition: NumericTextCtrl.cpp:945
NumericConverter::HertzFormat
static NumericFormatSymbol HertzFormat()
Definition: NumericTextCtrl.cpp:699
NumericConverter::GetFormatIndex
int GetFormatIndex()
Definition: NumericTextCtrl.cpp:1176
NumericTextCtrl::OnMouse
void OnMouse(wxMouseEvent &event)
Definition: NumericTextCtrl.cpp:1808
NumericTextCtrl
Definition: NumericTextCtrl.h:169
NumericConverter::mDigits
std::vector< DigitInfo > mDigits
Definition: NumericTextCtrl.h:161
NumericConverter::mBuiltinFormatStrings
const BuiltinFormatString * mBuiltinFormatStrings
Definition: NumericTextCtrl.h:163
NumericTextCtrl::SetInvalidValue
void SetInvalidValue(double invalidValue)
Definition: NumericTextCtrl.cpp:1508
NumericTextCtrl::SetValue
void SetValue(double newValue)
Definition: NumericTextCtrl.cpp:1470
NumericConverter::mPrefix
wxString mPrefix
Definition: NumericTextCtrl.h:150
wxEVT_COMMAND_TEXT_UPDATED
wxEVT_COMMAND_TEXT_UPDATED
Definition: Nyquist.cpp:126
NumericTextCtrlAx
NumericTextCtrlAx gives the NumericTextCtrl Accessibility.
ComponentInterfaceSymbol
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Definition: ComponentInterface.h:60
NumericTextCtrl::Options
Definition: NumericTextCtrl.h:175
NumericConverter::BANDWIDTH
@ BANDWIDTH
Definition: NumericTextCtrl.h:54
DigitInfo::field
int field
Definition: NumericTextCtrl.cpp:356
NumericTextCtrl::OnKeyDown
void OnKeyDown(wxKeyEvent &event)
Definition: NumericTextCtrl.cpp:1911
NumericConverter::FormatStrings::formatStr
TranslatableString formatStr
Definition: NumericTextCtrl.h:58
NumericConverter::mNtscDrop
bool mNtscDrop
Definition: NumericTextCtrl.h:158
NumericConverter::TimeAndSampleFormat
static NumericFormatSymbol TimeAndSampleFormat()
Definition: NumericTextCtrl.cpp:690
NumericTextCtrl::mReadOnly
bool mReadOnly
Definition: NumericTextCtrl.h:268
NumericConverter::mInvalidValue
double mInvalidValue
Definition: NumericTextCtrl.h:145
NumericTextCtrl::mType
NumericConverter::Type mType
Definition: NumericTextCtrl.h:293
NumericTextCtrl::ValueToControls
void ValueToControls() override
Definition: NumericTextCtrl.cpp:2091
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:1848
NumericConverter::LookupFormat
static NumericFormatSymbol LookupFormat(Type type, const wxString &id)
Definition: NumericTextCtrl.cpp:702
NumericTextCtrl::UpdateAutoFocus
void UpdateAutoFocus()
Definition: NumericTextCtrl.cpp:1426
NumericConverter::mSampleRate
double mSampleRate
Definition: NumericTextCtrl.h:157
NumericTextCtrl::mWidth
int mWidth
Definition: NumericTextCtrl.h:281
NumericTextCtrl::SetSampleRate
void SetSampleRate(double sampleRate)
Definition: NumericTextCtrl.cpp:1461
NumericConverter::ControlsToValue
virtual void ControlsToValue()
Definition: NumericTextCtrl.cpp:1056
NumericTextCtrl::OnKeyUp
void OnKeyUp(wxKeyEvent &event)
Definition: NumericTextCtrl.cpp:1893
sampleCount::as_double
double as_double() const
Definition: Types.h:608
NumericConverter::GetBuiltinFormat
FormatStrings GetBuiltinFormat(const int index)
Definition: NumericTextCtrl.cpp:1205
NumericTextCtrl::mHeight
int mHeight
Definition: NumericTextCtrl.h:282
label
TranslatableString label
Definition: Tags.cpp:755
NumericTextCtrl::mDigitH
int mDigitH
Definition: NumericTextCtrl.h:276
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: Types.h:610
NumericConverter::ParseFormatString
void ParseFormatString(const TranslatableString &untranslatedFormat)
Definition: NumericTextCtrl.cpp:751
NumericConverter::mType
Type mType
Definition: NumericTextCtrl.h:139
NumericTextCtrl::mDigitBoxW
int mDigitBoxW
Definition: NumericTextCtrl.h:273
NumericTextCtrl::mDigitBoxH
int mDigitBoxH
Definition: NumericTextCtrl.h:274
NumericTextCtrl::OnContext
void OnContext(wxContextMenuEvent &event)
Definition: NumericTextCtrl.cpp:1751
NumericConverter::mMinValue
double mMinValue
Definition: NumericTextCtrl.h:143
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:1199
NumericConverter::TIME
@ TIME
Definition: NumericTextCtrl.h:51
format
int format
Definition: ExportPCM.cpp:54
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:267
NumericConverter::GetNumBuiltins
int GetNumBuiltins()
Definition: NumericTextCtrl.cpp:1192
NumericTextCtrl::~NumericTextCtrl
virtual ~NumericTextCtrl()
Definition: NumericTextCtrl.cpp:1414
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:1485
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:144
NumericConverter::SetMinValue
void SetMinValue(double minValue)
Definition: NumericTextCtrl.cpp:1141
NumericConverter::FREQUENCY
@ FREQUENCY
Definition: NumericTextCtrl.h:53
NumericConverter::SetMaxValue
void SetMaxValue(double maxValue)
Definition: NumericTextCtrl.cpp:1155
Types.h
NumericConverter::FormatStrings
Definition: NumericTextCtrl.h:57
id
int id
Definition: WaveTrackControls.cpp:589
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
NumericTextCtrl::mScrollRemainder
double mScrollRemainder
Definition: NumericTextCtrl.h:291
NumericConverter::mDefaultNdx
int mDefaultNdx
Definition: NumericTextCtrl.h:165
AColor::Bevel
static void Bevel(wxDC &dc, bool up, const wxRect &r)
Definition: AColor.cpp:191
NumericConverter::GetString
wxString GetString()
Definition: NumericTextCtrl.cpp:1226
NumericConverter
NumericConverter provides the advanced formatting control used in the selection bar of Audacity.
Definition: NumericTextCtrl.h:47
WindowAccessible.h
NumericConverter::mFormatString
FormatStrings mFormatString
Definition: NumericTextCtrl.h:147
NumericTextCtrl::SetFieldFocus
void SetFieldFocus(int)
Definition: NumericTextCtrl.cpp:2039
NumericConverter::GetBuiltinName
NumericFormatSymbol GetBuiltinName(const int index)
Definition: NumericTextCtrl.cpp:1197
NumericField::CreateDigitFormatStr
void CreateDigitFormatStr()
Definition: NumericTextCtrl.cpp:315
NumericConverter::mValueTemplate
wxString mValueTemplate
Definition: NumericTextCtrl.h:151
NumericField::fieldW
int fieldW
Definition: NumericTextCtrl.cpp:333
NumericTextCtrl::mLabelFont
std::unique_ptr< wxFont > mLabelFont
Definition: NumericTextCtrl.h:272
NumericConverter::SetValue
void SetValue(double newValue)
Definition: NumericTextCtrl.cpp:1134
NumericField::NumericField
NumericField(const NumericField &)=default
_
#define _(s)
Definition: Internat.h:76
sampleCount
Definition: Types.h:581
ID_MENU
#define ID_MENU
Definition: NumericTextCtrl.cpp:1331
NumericTextCtrl::mDigitFont
std::unique_ptr< wxFont > mDigitFont
Definition: NumericTextCtrl.h:272
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:270
IMPLEMENT_CLASS
IMPLEMENT_CLASS(ControlToolBar, ToolBar)
NumericConverter::Adjust
void Adjust(int steps, int dir)
Definition: NumericTextCtrl.cpp:1245
NumericTextCtrl.h
ThemeBase::Colour
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1192
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:1699
NumericConverter::ResetMinValue
void ResetMinValue()
Definition: NumericTextCtrl.cpp:1150
NumericTextCtrl::ControlsToValue
void ControlsToValue() override
Definition: NumericTextCtrl.cpp:2109
NumericConverter::mFocusedDigit
int mFocusedDigit
Definition: NumericTextCtrl.h:160
theTheme
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:201
TranslatableString::Translation
wxString Translation() const
Definition: Types.h:337
NumericConverter::Decrement
void Decrement()
Definition: NumericTextCtrl.cpp:1239
NumericConverter::SetSampleRate
void SetSampleRate(double sampleRate)
Definition: NumericTextCtrl.cpp:1126
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:295
NumericField::str
wxString str
Definition: NumericTextCtrl.cpp:338
safenew
#define safenew
Definition: MemoryX.h:8
NumericTextCtrl::EnableMenu
void EnableMenu(bool enable=true)
Definition: NumericTextCtrl.cpp:1490
NumericConverter::Type
Type
Definition: NumericTextCtrl.h:50
DigitInfo::pos
int pos
Definition: NumericTextCtrl.cpp:358
NumericTextCtrl::mAutoPos
bool mAutoPos
Definition: NumericTextCtrl.h:288
NumericField::labelX
int labelX
Definition: NumericTextCtrl.cpp:334
NumericTextCtrl::mDigitW
int mDigitW
Definition: NumericTextCtrl.h:275
NumericConverter::GetValue
double GetValue()
Definition: NumericTextCtrl.cpp:1170
ThemeBase::SetPenColour
void SetPenColour(wxPen &Pen, int iIndex)
Definition: Theme.cpp:1205
NumericTextCtrl::mButtonWidth
int mButtonWidth
Definition: NumericTextCtrl.h:283
END_EVENT_TABLE
END_EVENT_TABLE()
NumericField::formatStr
wxString formatStr
Definition: NumericTextCtrl.cpp:337
NumericTextCtrl::OnPaint
void OnPaint(wxPaintEvent &event)
Definition: NumericTextCtrl.cpp:1704
NumericConverter::mValueString
wxString mValueString
Definition: NumericTextCtrl.h:154
NumericTextCtrl::mBorderRight
int mBorderRight
Definition: NumericTextCtrl.h:279
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:1859
NumericConverter::SecondsFormat
static NumericFormatSymbol SecondsFormat()
Definition: NumericTextCtrl.cpp:692
BuiltinFormatString::name
NumericFormatSymbol name
Definition: NumericTextCtrl.cpp:287