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 NumericFormatID & 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 UpdateFormatToFit(rawValue);
84 auto result = mFormatter->ValueToString(rawValue, nearest);
85
86 mValueString = std::move(result.valueString);
87 mFieldValueStrings = std::move(result.fieldValueStrings);
88}
89
91{
92 if (!mFormatter)
93 {
95 return;
96 }
97
98 auto result = mFormatter->StringToValue(mValueString);
99
100 mValue = result.has_value() ?
101 std::clamp(*result, mMinValue, mMaxValue) :
103}
104
106{
107 if (mType != type)
108 {
109 // Ensure that the format change will happen,
110 // duration formats lists matches the time list
111 mFormatID = {};
112 mType = type;
113 }
114
115 return SetFormatName(formatName);
116}
117
119{
120 if (mFormatID == formatName && !formatName.empty())
121 return false;
122
123 const auto newFormat =
125
126 if (mFormatID == newFormat)
127 return false;
128
129 mFormatID = newFormat;
130 mCustomFormat = {};
131
133
134 return true;
135}
136
138{
139 return mFormatID;
140}
141
143{
144 if (mCustomFormat == customFormat)
145 return false;
146
147 if (!ParseFormatString(customFormat))
148 return false;
149
150 mFormatID = {};
151 mCustomFormat = customFormat;
152
154
155 return true;
156}
157
158void NumericConverter::SetValue(double newValue)
159{
160 mValue = newValue;
163}
164
165void NumericConverter::SetMinValue(double minValue)
166{
167 mMinValue = minValue;
168 if (mMaxValue < minValue)
169 mMaxValue = minValue;
170 if (mValue < minValue)
171 SetValue(minValue);
172}
173
175{
176 mMinValue = 0.0;
177}
178
179void NumericConverter::SetMaxValue(double maxValue)
180{
181 mMaxValue = maxValue;
182 if (mMinValue > maxValue) {
183 mMinValue = maxValue;
184 }
185 if (mValue > maxValue)
186 SetValue(maxValue);
187}
188
190{
191 mMaxValue = std::numeric_limits<double>::max();
192}
193
195{
197 return mValue;
198}
199
201{
203 return mValueString;
204}
205
207{
208 mFormatter->UpdateFormatForValue(value, false);
209}
210
211int NumericConverter::GetSafeFocusedDigit(int focusedDigit) const noexcept
212{
213 if (focusedDigit < 0)
214 return int(mFormatter->GetDigitInfos().size() - 1);
215 else
216 return std::clamp<int>(
217 focusedDigit, 0, mFormatter->GetDigitInfos().size() - 1);
218}
219
220void NumericConverter::Increment(int focusedDigit)
221{
222 Adjust(1, 1, focusedDigit);
223}
224
225void NumericConverter::Decrement(int focusedDigit)
226{
227 Adjust(1, -1, focusedDigit);
228}
229
231{
232 if (!mFormatID.empty())
233 {
235
236 if (formatterItem == nullptr)
237 {
238 assert(formatterItem != nullptr);
239 return false;
240 }
241
242 mFormatter = formatterItem->factory->Create(mContext);
243 }
244 else if (!mCustomFormat.empty ())
245 {
247 }
248
249 if (mFormatter)
250 {
252 mFormatter->Subscribe([this](const auto& msg) {
253 OnFormatUpdated(false);
254 Publish({ msg.value });
255 });
256 }
257
258 OnFormatUpdated(true);
259 return mFormatter != nullptr;
260}
261
263{
264 if (!mFormatter)
265 return;
266
269}
270
271void NumericConverter::Adjust(int steps, int dir, int focusedDigit)
272{
273 if (!mFormatter || mFormatter->GetDigitInfos().empty())
274 return;
275 // It is possible and "valid" for steps to be zero if a
276 // high precision device is being used and wxWidgets supports
277 // reporting a higher precision...Mac wx3 does.
278 if (steps == 0)
279 return;
280
281 focusedDigit = GetSafeFocusedDigit(focusedDigit);
282
283 wxASSERT(dir == -1 || dir == 1);
284 wxASSERT(steps > 0);
285 if (steps < 0)
286 steps = -steps;
287
288 while (steps != 0)
289 {
290 mValue = mFormatter->SingleStep(mValue, focusedDigit, dir > 0);
291 steps--;
292 }
293
294 mValue = std::clamp(mValue, mMinValue, mMaxValue);
295
297}
std::unique_ptr< NumericConverterFormatter > CreateParsedNumericConverterFormatter(const FormatterContext &context, NumericConverterType type, const TranslatableString &format)
const wxString & Internal() const
A context in which formatter operates.
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
bool empty() const
Definition: Identifier.h:61
void Decrement(int focusedDigit=-1)
NumericConverterType mType
virtual void ControlsToValue()
virtual ~NumericConverter()
virtual void ValueToControls()
bool ParseFormatString(const TranslatableString &untranslatedFormat)
virtual void OnFormatUpdated(bool resetFocus)
bool SetCustomFormat(const TranslatableString &customFormat)
std::vector< wxString > mFieldValueStrings
void SetMinValue(double minValue)
void Adjust(int steps, int dir, int focusedDigit)
void UpdateFormatToFit(double value)
void SetValue(double newValue)
FormatterContext mContext
bool SetTypeAndFormatName(const NumericConverterType &type, const NumericFormatID &formatName)
std::unique_ptr< NumericConverterFormatter > mFormatter
TranslatableString mCustomFormat
void SetMaxValue(double maxValue)
void Increment(int focusedDigit=-1)
Observer::Subscription mFormatUpdatedSubscription
NumericFormatID mFormatID
NumericFormatID GetFormatName() const
int GetSafeFocusedDigit(int focusedDigit) const noexcept
NumericConverter(const FormatterContext &context, NumericConverterType type, const NumericFormatID &formatName={}, double value=0.0f)
bool SetFormatName(const NumericFormatID &formatName)
CallbackReturn Publish(const FormatChangedToFitValueMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207
Holds a msgid for the translation catalog; may also bind format arguments.
NUMERIC_FORMATS_API NumericFormatSymbol Lookup(const FormatterContext &context, const NumericConverterType &type, const NumericFormatID &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 NumericFormatID &symbol)