1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   NumericTextCtrl.h
6 
7   Dominic Mazzoni
8 
9   See NumericTextCtrl.cpp for documentation on how to use the
10   format string to specify how a NumericTextCtrl's fields are
11   laid out.
12 
13 **********************************************************************/
14 
15 #ifndef __AUDACITY_TIME_TEXT_CTRL__
16 #define __AUDACITY_TIME_TEXT_CTRL__
17 
18 
19 
20 #include "MemoryX.h"
21 #include "ComponentInterface.h"
22 #include "ComponentInterfaceSymbol.h"
23 #include <vector>
24 #include <wx/setup.h> // for wxUSE_* macros
25 #include <wx/defs.h>
26 #include <wx/control.h> // to inherit
27 
28 #include "Internat.h"
29 
30 // One event type for each type of control.  Event is raised when a control
31 // changes its format.  Owners of controls of the same type can listen and
32 // update their formats to agree.
33 DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_TIMETEXTCTRL_UPDATED, -1);
34 DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_FREQUENCYTEXTCTRL_UPDATED, -1);
35 DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_BANDWIDTHTEXTCTRL_UPDATED,
36                             -1);
37 
38 /** \brief struct to hold a formatting control string and its user facing name
39  * Used in an array to hold the built-in time formats that are always available
40  * to the user */
41 struct BuiltinFormatString;
42 
43 class NumericField;
44 
45 class DigitInfo;
46 
47 class AUDACITY_DLL_API NumericConverter /* not final */
48 {
49 public:
50 
51    enum Type {
52       TIME,
53       ATIME, // for Audio time control.
54       FREQUENCY,
55       BANDWIDTH,
56    };
57 
58    struct FormatStrings {
59       TranslatableString formatStr;
60       // How to name the fraction of the unit; not necessary for time formats
61       // or when the format string has no decimal point
62       TranslatableString fraction;
63 
64       FormatStrings(
65          const TranslatableString &format = {},
66          const TranslatableString &fraction = {})
67          : formatStr{ format }, fraction{ fraction }
68       {}
69 
70       friend bool operator == ( const FormatStrings &x, const FormatStrings &y )
71          { return x.formatStr == y.formatStr && x.fraction == y.fraction; }
72       friend bool operator != ( const FormatStrings &x, const FormatStrings &y )
73          { return !(x == y); }
74    };
75 
76    static NumericFormatSymbol DefaultSelectionFormat();
77    static NumericFormatSymbol TimeAndSampleFormat();
78    static NumericFormatSymbol SecondsFormat();
79    static NumericFormatSymbol HoursMinsSecondsFormat();
80    static NumericFormatSymbol HundredthsFormat();
81    static NumericFormatSymbol HertzFormat();
82 
83    static NumericFormatSymbol LookupFormat( Type type, const wxString& id);
84 
85    NumericConverter(Type type,
86                     const NumericFormatSymbol & formatName = {},
87                     double value = 0.0f,
88                     double sampleRate = 1.0f /* to prevent div by 0 */);
89    NumericConverter(const NumericConverter&);
90 
91    virtual ~NumericConverter();
92 
93    // ValueToControls() formats a raw value (either provided as
94    // argument, or mValue, depending on the version of the function
95    // called). The result is stored to mValueString.
96    virtual void ValueToControls();
97    virtual void ValueToControls(double rawValue, bool nearest = true);
98 
99    // Converts the stored formatted string (mValueString) back to a
100    // raw value (mValue).
101    virtual void ControlsToValue();
102 
103 private:
104    void ParseFormatString(const TranslatableString & untranslatedFormat);
105 
106 public:
107    void PrintDebugInfo();
108 
109    // returns true iff the format name really changed:
110    bool SetFormatName(const NumericFormatSymbol & formatName);
111 
112    // returns true iff the format string really changed:
113    bool SetFormatString(const FormatStrings & formatString);
114 
115    void SetSampleRate(double sampleRate);
116    void SetValue(double newValue);
117    void SetMinValue(double minValue);
118    void ResetMinValue();
119    void SetMaxValue(double maxValue);
120    void ResetMaxValue();
121 
122    double GetValue();
123 
124    wxString GetString();
125 
126    int GetFormatIndex();
127 
128    int GetNumBuiltins();
129    NumericFormatSymbol GetBuiltinName(const int index);
130    FormatStrings GetBuiltinFormat(const int index);
131    FormatStrings GetBuiltinFormat(const NumericFormatSymbol & name);
132 
133    // Adjust the value by the number "steps" in the active format.
134    // Increment if "dir" is 1, decrement if "dir" is -1.
135    void Adjust(int steps, int dir);
136 
137    void Increment();
138    void Decrement();
139 
140 protected:
141    Type           mType;
142 
143    double         mValue;
144 
145    double         mMinValue;
146    double         mMaxValue;
147    double         mInvalidValue;
148 
149    FormatStrings mFormatString;
150 
151    std::vector<NumericField> mFields;
152    wxString       mPrefix;
153    wxString       mValueTemplate;
154    wxString       mValueMask;
155    // Formatted mValue, by ValueToControls().
156    wxString       mValueString;
157 
158    double         mScalingFactor;
159    double         mSampleRate;
160    bool           mNtscDrop;
161 
162    int            mFocusedDigit;
163    std::vector<DigitInfo> mDigits;
164 
165    const BuiltinFormatString *mBuiltinFormatStrings;
166    const size_t mNBuiltins;
167    int mDefaultNdx;
168 };
169 
170 class AUDACITY_DLL_API NumericTextCtrl final
171    : public wxControl, public NumericConverter
172 {
173    friend class NumericTextCtrlAx;
174 
175  public:
176    DECLARE_DYNAMIC_CLASS(NumericTextCtrl)
177 
178    struct Options {
179       bool autoPos { true };
180       bool readOnly { false };
181       bool menuEnabled { true };
182       bool hasInvalidValue { false };
183       double invalidValue { -1.0 };
184       FormatStrings format {};
185       bool hasValue { false };
186       double value{ -1.0 };
187 
OptionsOptions188       Options() {}
189 
AutoPosOptions190       Options &AutoPos (bool enable) { autoPos = enable; return *this; }
ReadOnlyOptions191       Options &ReadOnly (bool enable) { readOnly = enable; return *this; }
MenuEnabledOptions192       Options &MenuEnabled (bool enable) { menuEnabled = enable; return *this; }
193       Options &InvalidValue (bool has, double v = -1.0)
194          { hasInvalidValue = has, invalidValue = v; return *this; }
195       // use a custom format not in the tables:
FormatOptions196       Options &Format (const FormatStrings &f)
197          { format = f; return *this; }
ValueOptions198       Options &Value (bool has, double v)
199          { hasValue = has, value = v; return *this; }
200    };
201 
202    NumericTextCtrl(wxWindow *parent, wxWindowID winid,
203                    NumericConverter::Type type,
204                    const NumericFormatSymbol &formatName = {},
205                    double value = 0.0,
206                    double sampleRate = 44100,
207                    const Options &options = {},
208                    const wxPoint &pos = wxDefaultPosition,
209                    const wxSize &size = wxDefaultSize);
210 
211    virtual ~NumericTextCtrl();
212 
213    // Hide the inherited function that takes wxString
214    void SetName( const TranslatableString &name );
215 
216    wxSize ComputeSizing(bool update = true, wxCoord digitW = 0, wxCoord digitH = 0);
217    bool Layout() override;
218    void Fit() override;
219 
220    void SetSampleRate(double sampleRate);
221    void SetValue(double newValue);
222 
223    // returns true iff the format string really changed:
224    bool SetFormatString(const FormatStrings & formatString);
225 
226    // returns true iff the format name really changed:
227    bool SetFormatName(const NumericFormatSymbol & formatName);
228 
229    void SetFieldFocus(int /* digit */);
230 
GetDimensions()231    wxSize GetDimensions() { return wxSize(mWidth + mButtonWidth, mHeight); }
GetDigitSize()232    wxSize GetDigitSize() { return wxSize(mDigitBoxW, mDigitBoxH); }
233    void SetDigitSize(int width, int height);
234    void SetReadOnly(bool readOnly = true);
235    void EnableMenu(bool enable = true);
236 
237    // The text control permits typing DELETE to make the value invalid only if this
238    // function has previously been called.
239    // Maybe you want something other than the default of -1 to indicate the invalid value
240    // this control returns to the program, so you can specify.
241    void SetInvalidValue(double invalidValue);
242 
GetFocusedField()243    int GetFocusedField() { return mLastField; }
GetFocusedDigit()244    int GetFocusedDigit() { return mFocusedDigit; }
245 
246 private:
247 
248    void OnCaptureKey(wxCommandEvent &event);
249    void OnKeyDown(wxKeyEvent &event);
250    void OnKeyUp(wxKeyEvent &event);
251    void OnMouse(wxMouseEvent &event);
252    void OnErase(wxEraseEvent &event);
253    void OnPaint(wxPaintEvent &event);
254    void OnFocus(wxFocusEvent &event);
255    void OnContext(wxContextMenuEvent &event);
256 
257    // Formats mValue into mValueString, using the method of the base class.
258    // Triggers a refresh of the wx window only when the value actually
259    // changed since last time a refresh was triggered.
260    void ValueToControls() override;
261    void ControlsToValue() override;
262 
263    // If autoPos was enabled, focus the first non-zero digit
264    void UpdateAutoFocus();
265 
266    void Updated(bool keyup = false);
267 
268 private:
269 
270    bool           mMenuEnabled;
271    bool           mReadOnly;
272 
273    std::unique_ptr<wxBitmap> mBackgroundBitmap;
274 
275    std::unique_ptr<wxFont> mDigitFont, mLabelFont;
276    int            mDigitBoxW;
277    int            mDigitBoxH;
278    int            mDigitW;
279    int            mDigitH;
280    int            mBorderLeft;
281    int            mBorderTop;
282    int            mBorderRight;
283    int            mBorderBottom;
284    int            mWidth;
285    int            mHeight;
286    int            mButtonWidth;
287 
288    int            mLastField;
289 
290    // If true, the focus will be set to the first non-zero digit
291    bool           mAutoPos;
292 
293    // Keeps track of extra fractional scrollwheel steps
294    double         mScrollRemainder;
295 
296    NumericConverter::Type mType;
297 
298    bool           mAllowInvalidValue;
299 
300    DECLARE_EVENT_TABLE()
301 };
302 
303 #endif // __AUDACITY_TIME_TEXT_CTRL__
304