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
198class NumericTextCtrlAx final : public WindowAccessible
199{
200public:
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
269private:
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{
304public:
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
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
338 wxString label;
339 wxString formatStr;
340 wxString str;
341};
342
343//
344// ----------------------------------------------------------------------------
345// DigitInfo Class
346// ----------------------------------------------------------------------------
347//
349{
350public:
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
364namespace {
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:
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 {
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{
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
954void 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);
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;
1124 return true;
1125 }
1126 else
1127 return false;
1128}
1129
1130void NumericConverter::SetSampleRate(double sampleRate)
1131{
1132 mSampleRate = sampleRate;
1136}
1137
1138void NumericConverter::SetValue(double newValue)
1139{
1140 mValue = newValue;
1143}
1144
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
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{
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{
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
1249void 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 {
1272 }
1273 else
1274 {
1275 mNtscDrop = false;
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 {
1318 }
1319 else
1320 {
1321 mNtscDrop = false;
1323 mNtscDrop = true;
1325 }
1326 break;
1327 }
1328 }
1329 steps--;
1330 }
1331
1333}
1334
1335#define ID_MENU 9800
1336
1337// Custom events
1338
1339DEFINE_EVENT_TYPE(EVT_TIMETEXTCTRL_UPDATED)
1340DEFINE_EVENT_TYPE(EVT_FREQUENCYTEXTCTRL_UPDATED)
1341DEFINE_EVENT_TYPE(EVT_BANDWIDTHTEXTCTRL_UPDATED)
1342
1343BEGIN_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
1356
1357NumericTextCtrl::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 =
1455 if (result) {
1456 Layout();
1457 Fit();
1461 }
1462 return result;
1463}
1464
1465void NumericTextCtrl::SetSampleRate(double sampleRate)
1466{
1468 Layout();
1469 Fit();
1472}
1473
1474void NumericTextCtrl::SetValue(double newValue)
1475{
1479}
1480
1481void NumericTextCtrl::SetDigitSize(int width, int height)
1482{
1483 mDigitBoxW = width;
1484 mDigitBoxH = height;
1485 Layout();
1486 Fit();
1487}
1488
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
1512void 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
1521wxSize 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
1703void NumericTextCtrl::OnErase(wxEraseEvent & WXUNUSED(event))
1704{
1705 // Ignore it to prevent flashing
1706}
1707
1708void 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
1755void 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(
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
1816void 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
1856void NumericTextCtrl::OnFocus(wxFocusEvent &event)
1857{
1858 KeyboardCapture::OnFocus( *this, event );
1859
1860 if (event.GetEventType() != wxEVT_KILL_FOCUS &&
1861 mFocusedDigit <= 0 )
1863
1864 event.Skip( false ); // PRL: not sure why, but preserving old behavior
1865}
1866
1867void 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
1901void 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
1919void 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));
1946 // Beware relocation of the string
1947 digitPosition = mDigits[mFocusedDigit].pos;
1948 }
1949 mValueString[digitPosition] = wxChar(keyCode);
1951 Refresh();// Force an update of the control. [Bug 1497]
1953 mFocusedDigit = (mFocusedDigit + 1) % (mDigits.size());
1954 Updated();
1955 }
1956
1957 else if (!mReadOnly && keyCode == WXK_DELETE) {
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';
1971 Refresh();// Force an update of the control. [Bug 1497]
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
2066void 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
2125NumericTextCtrlAx::NumericTextCtrlAx(NumericTextCtrl *ctrl)
2126: WindowAccessible(ctrl)
2127{
2128 mCtrl = ctrl;
2129 mLastField = -1;
2130 mLastDigit = -1;
2131}
2132
2133NumericTextCtrlAx::~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).
2141wxAccStatus 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.
2148wxAccStatus 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.
2161wxAccStatus 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."
2175wxAccStatus 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.
2183wxAccStatus 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'.
2194wxAccStatus 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.
2203wxAccStatus 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
2222wxAccStatus 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.
2231wxAccStatus 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
2251static 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.
2268wxAccStatus 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.
2357wxAccStatus 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
2371wxAccStatus NumericTextCtrlAx::GetSelections(wxVariant * WXUNUSED(selections))
2372{
2373 return wxACC_NOT_IMPLEMENTED;
2374}
2375
2376// Returns a state constant.
2377wxAccStatus 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.
2387wxAccStatus NumericTextCtrlAx::GetValue(int WXUNUSED(childId), wxString * WXUNUSED(strValue))
2388{
2389 return wxACC_NOT_IMPLEMENTED;
2390}
2391
2392#endif
2393
wxEVT_COMMAND_BUTTON_CLICKED
@ Internal
Indicates internal failure from Audacity.
IMPLEMENT_CLASS(AudioSetupToolBar, ToolBar)
Abstractions of menus and their items.
END_EVENT_TABLE()
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:82
int format
Definition: ExportPCM.cpp:56
DEFINE_EVENT_TYPE(EVT_FREQWINDOW_RECALC)
#define field(n, t)
Definition: ImportAUP.cpp:167
#define XO(s)
Definition: Internat.h:31
#define _(s)
Definition: Internat.h:75
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:92
#define safenew
Definition: MemoryX.h:10
#define ID_MENU
wxEVT_COMMAND_TEXT_UPDATED
Definition: Nyquist.cpp:133
TranslatableString label
Definition: TagsEditor.cpp:163
THEME_API Theme theTheme
Definition: Theme.cpp:82
int id
static void Arrow(wxDC &dc, wxCoord x, wxCoord y, int width, bool down=true)
Definition: AColor.cpp:160
static void Bevel(wxDC &dc, bool up, const wxRect &r)
Definition: AColor.cpp:266
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:209
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
const wxString Translation() const
DigitInfo is a class used in NumericTextCtrl.
DigitInfo(int _field, int _index, int _pos, wxRect _box)
NumericConverter provides the advanced formatting control used in the selection bar of Audacity.
void ParseFormatString(const TranslatableString &untranslatedFormat)
static NumericFormatSymbol HundredthsFormat()
FormatStrings mFormatString
static NumericFormatSymbol DefaultSelectionFormat()
static NumericFormatSymbol TimeAndSampleFormat()
const BuiltinFormatString * mBuiltinFormatStrings
virtual void ControlsToValue()
virtual ~NumericConverter()
virtual void ValueToControls()
std::vector< DigitInfo > mDigits
const size_t mNBuiltins
void SetSampleRate(double sampleRate)
FormatStrings GetBuiltinFormat(const int index)
bool SetFormatString(const FormatStrings &formatString)
void SetMinValue(double minValue)
std::vector< NumericField > mFields
static NumericFormatSymbol LookupFormat(Type type, const wxString &id)
void SetValue(double newValue)
static NumericFormatSymbol HertzFormat()
bool SetFormatName(const NumericFormatSymbol &formatName)
NumericFormatSymbol GetBuiltinName(const int index)
void SetMaxValue(double maxValue)
static NumericFormatSymbol HoursMinsSecondsFormat()
void Adjust(int steps, int dir)
NumericConverter(Type type, const NumericFormatSymbol &formatName={}, double value=0.0f, double sampleRate=1.0f)
static NumericFormatSymbol SecondsFormat()
NumericField is a class used in NumericTextCtrl.
NumericField(const NumericField &)=default
NumericField & operator=(const NumericField &)=default
void CreateDigitFormatStr()
NumericField(bool _frac, int _base, int _range, bool _zeropad)
NumericTextCtrlAx gives the NumericTextCtrl Accessibility.
void EnableMenu(bool enable=true)
void OnMouse(wxMouseEvent &event)
void Fit() override
void OnContext(wxContextMenuEvent &event)
bool SetFormatString(const FormatStrings &formatString)
void OnFocus(wxFocusEvent &event)
void OnKeyDown(wxKeyEvent &event)
void SetReadOnly(bool readOnly=true)
void Updated(bool keyup=false)
std::unique_ptr< wxFont > mDigitFont
virtual ~NumericTextCtrl()
void SetInvalidValue(double invalidValue)
bool Layout() override
void OnPaint(wxPaintEvent &event)
void SetSampleRate(double sampleRate)
void OnKeyUp(wxKeyEvent &event)
void OnErase(wxEraseEvent &event)
wxSize ComputeSizing(bool update=true, wxCoord digitW=0, wxCoord digitH=0)
void ValueToControls() override
void SetDigitSize(int width, int height)
std::unique_ptr< wxBitmap > mBackgroundBitmap
void SetName(const TranslatableString &name)
NumericConverter::Type mType
bool SetFormatName(const NumericFormatSymbol &formatName)
void ControlsToValue() override
void SetValue(double newValue)
std::unique_ptr< wxFont > mLabelFont
void OnCaptureKey(wxCommandEvent &event)
wxColour & Colour(int iIndex)
void SetBrushColour(wxBrush &Brush, int iIndex)
void SetPenColour(wxPen &Pen, int iIndex)
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
long long as_long_long() const
Definition: SampleCount.h:47
double as_double() const
Definition: SampleCount.h:45
void OnFocus(wxWindow &window, wxFocusEvent &event)
a function useful to implement a focus event handler The window releases the keyboard if the event is...
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
const BuiltinFormatString * ChooseBuiltinFormatStrings(NumericConverter::Type type)
size_t ChooseNBuiltinFormatStrings(NumericConverter::Type type)
static const BuiltinFormatString BandwidthConverterFormats_[]
array of formats the control knows about internally array of string pairs for name of the format and ...
static const BuiltinFormatString FrequencyConverterFormats_[]
array of formats the control knows about internally array of string pairs for name of the format and ...
static const BuiltinFormatString TimeConverterFormats_[]
array of formats the control knows about internally array of string pairs for name of the format and ...
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
Definition: fast_float.h:2512
struct to hold a formatting control string and its user facing name Used in an array to hold the buil...
NumericFormatSymbol name
NumericConverter::FormatStrings formatStrings
Window placement information for wxWidgetsBasicUI can be constructed from a wxWindow pointer.