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