Audacity 3.2.0
NumericConverter.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 NumericConverter.cpp
6
7 Dominic Mazzoni
8
9 Paul Licameli split from NumericTextCtrl.cpp
10
11
12********************************************************************//****************************************************************/
29#include "NumericConverter.h"
32
33#include "Project.h"
34
36
37#include <cmath>
38#include <wx/setup.h> // for wxUSE_* macros
39#include <wx/wx.h>
40
41//
42// ----------------------------------------------------------------------------
43// NumericConverter Class
44// ----------------------------------------------------------------------------
45//
46
48 const NumericFormatSymbol & formatName,
49 double value)
50 : mContext { context }
51 , mType { std::move(type) }
52{
55
56 SetFormatName(formatName);
57 SetValue(value);
58}
59
61 const TranslatableString& untranslatedFormat)
62{
64 mContext, mType, untranslatedFormat);
65
66 return mFormatter != nullptr;
67}
68
70{
71}
72
74{
76}
77
78void NumericConverter::ValueToControls(double rawValue, bool nearest /* = true */)
79{
80 if (!mFormatter)
81 return;
82
83 auto result = mFormatter->ValueToString(rawValue, nearest);
84
85 mValueString = std::move(result.valueString);
86 mFieldValueStrings = std::move(result.fieldValueStrings);
87}
88
90{
91 if (!mFormatter)
92 {
94 return;
95 }
96
97 auto result = mFormatter->StringToValue(mValueString);
98
99 mValue = result.has_value() ?
100 std::clamp(*result, mMinValue, mMaxValue) :
102}
103
105{
106 if (mType != type)
107 {
108 // Ensure that the format change will happen,
109 // duration formats lists matches the time list
110 mFormatSymbol = {};
111 mType = type;
112 }
113
114 return SetFormatName(formatName);
115}
116
118{
119 if (mFormatSymbol == formatName && !formatName.empty())
120 return false;
121
122 const auto newFormat = NumericConverterFormats::Lookup(mContext, mType, formatName);
123
124 if (mFormatSymbol == newFormat)
125 return false;
126
127 mFormatSymbol = newFormat;
128 mCustomFormat = {};
129
131
132 return true;
133}
134
136{
137 return mFormatSymbol;
138}
139
141{
142 if (mCustomFormat == customFormat)
143 return false;
144
145 if (!ParseFormatString(customFormat))
146 return false;
147
148 mFormatSymbol = {};
149 mCustomFormat = customFormat;
150
152
153 return true;
154}
155
156void NumericConverter::SetValue(double newValue)
157{
158 mValue = newValue;
161}
162
163void NumericConverter::SetMinValue(double minValue)
164{
165 mMinValue = minValue;
166 if (mMaxValue < minValue)
167 mMaxValue = minValue;
168 if (mValue < minValue)
169 SetValue(minValue);
170}
171
173{
174 mMinValue = 0.0;
175}
176
177void NumericConverter::SetMaxValue(double maxValue)
178{
179 mMaxValue = maxValue;
180 if (mMinValue > maxValue) {
181 mMinValue = maxValue;
182 }
183 if (mValue > maxValue)
184 SetValue(maxValue);
185}
186
188{
189 mMaxValue = std::numeric_limits<double>::max();
190}
191
193{
195 return mValue;
196}
197
199{
201 return mValueString;
202}
203
204int NumericConverter::GetSafeFocusedDigit(int focusedDigit) const noexcept
205{
206 if (focusedDigit < 0)
207 return int(mFormatter->GetDigitInfos().size() - 1);
208 else
209 return std::clamp<int>(
210 focusedDigit, 0, mFormatter->GetDigitInfos().size() - 1);
211}
212
213void NumericConverter::Increment(int focusedDigit)
214{
215 Adjust(1, 1, focusedDigit);
216}
217
218void NumericConverter::Decrement(int focusedDigit)
219{
220 Adjust(1, -1, focusedDigit);
221}
222
224{
225 if (!mFormatSymbol.empty())
226 {
228
229 if (formatterItem == nullptr)
230 {
231 assert(formatterItem != nullptr);
232 return false;
233 }
234
235 mFormatter = formatterItem->factory->Create(mContext);
236 }
237 else if (!mCustomFormat.empty ())
238 {
240 }
241
242 if (mFormatter)
243 {
245 mFormatter->Subscribe([this](auto) { OnFormatUpdated(); });
246 }
247
249 return mFormatter != nullptr;
250}
251
253{
254 if (!mFormatter)
255 return;
256
259}
260
261void NumericConverter::Adjust(int steps, int dir, int focusedDigit)
262{
263 if (!mFormatter || mFormatter->GetDigitInfos().empty())
264 return;
265 // It is possible and "valid" for steps to be zero if a
266 // high precision device is being used and wxWidgets supports
267 // reporting a higher precision...Mac wx3 does.
268 if (steps == 0)
269 return;
270
271 focusedDigit = GetSafeFocusedDigit(focusedDigit);
272
273 wxASSERT(dir == -1 || dir == 1);
274 wxASSERT(steps > 0);
275 if (steps < 0)
276 steps = -steps;
277
278 while (steps != 0)
279 {
280 mValue = mFormatter->SingleStep(mValue, focusedDigit, dir > 0);
281 steps--;
282 }
283
284 mValue = std::clamp(mValue, mMinValue, mMaxValue);
285
287}
std::unique_ptr< NumericConverterFormatter > CreateParsedNumericConverterFormatter(const FormatterContext &context, NumericConverterType type, const TranslatableString &format)
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
A context in which formatter operates.
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
void Decrement(int focusedDigit=-1)
NumericConverterType mType
virtual void ControlsToValue()
virtual ~NumericConverter()
virtual void ValueToControls()
bool ParseFormatString(const TranslatableString &untranslatedFormat)
NumericFormatSymbol mFormatSymbol
NumericConverter(const FormatterContext &context, NumericConverterType type, const NumericFormatSymbol &formatName={}, double value=0.0f)
bool SetCustomFormat(const TranslatableString &customFormat)
virtual void OnFormatUpdated()
std::vector< wxString > mFieldValueStrings
void SetMinValue(double minValue)
void Adjust(int steps, int dir, int focusedDigit)
void SetValue(double newValue)
FormatterContext mContext
bool SetFormatName(const NumericFormatSymbol &formatName)
std::unique_ptr< NumericConverterFormatter > mFormatter
TranslatableString mCustomFormat
void SetMaxValue(double maxValue)
void Increment(int focusedDigit=-1)
NumericFormatSymbol GetFormatName() const
Observer::Subscription mFormatUpdatedSubscription
int GetSafeFocusedDigit(int focusedDigit) const noexcept
bool SetTypeAndFormatName(const NumericConverterType &type, const NumericFormatSymbol &formatName)
Holds a msgid for the translation catalog; may also bind format arguments.
NUMERIC_FORMATS_API NumericFormatSymbol Lookup(const FormatterContext &context, const NumericConverterType &type, const NumericFormatSymbol &formatIdentifier)
Looks up the format, returns Default for the type if the format is not registered.
STL namespace.
static const NumericConverterRegistryItem * Find(const FormatterContext &context, const NumericConverterType &type, const NumericFormatSymbol &symbol)