Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
EntryModel.hxx
Go to the documentation of this file.
1
34#ifndef _UTILS_ENTRYMODEL_HXX_
35#define _UTILS_ENTRYMODEL_HXX_
36
37#include <algorithm>
38#include <cstring>
39#include <functional>
40#include <type_traits>
41#include <limits>
42
44
48template <class T>
50{
51public:
54 : value_(0)
55 , valueMin_(std::numeric_limits<T>::lowest())
56 , valueMax_(std::numeric_limits<T>::max())
58 , maxSize_(0)
59 , size_(0)
60 , isAtInitialValue_(false)
61 , empty_(true)
62 , base_(10)
63 , autoClamp_(true)
64 {
65 }
66
68 void clear()
69 {
70 value_ = 0;
72 size_ = 0;
73 empty_ = true;
74 isAtInitialValue_ = false;
75 }
76
80 void init(unsigned max_size, int base)
81 {
83 autoClamp_ = true;
84 clear();
85 set_base(base); // this will call set_boundaries()
86 }
87
96 void init(unsigned max_size, int base, T value, bool automatic_clamp = true)
97 {
98 init(max_size, base);
99 autoClamp_ = automatic_clamp;
100 value_ = value;
101 isAtInitialValue_ = true;
102 empty_ = false;
103 }
104
107 void push_back(uint8_t val)
108 {
109 HASSERT(val < base_);
110 if (size_ >= maxSize_)
111 {
112 // clear entry, return without appending the character
113 clear();
114 return;
115 }
117 {
118 // clear entry before inserting character
119 clear();
120 }
121 if (value_ == 0 && val != 0 && size_)
122 {
123 // This is a special case where we transition from a user having
124 // entered all 0 (as a first digits) and then enters a non-zero
125 // (as a next digit).
127 }
128 value_ *= base_;
129 if (value_ < 0)
130 {
131 value_ -= val;
132 }
133 else
134 {
135 value_ += val;
136 }
137 if (value_ == 0 && !empty_)
138 {
139 if (numLeadingZeros_ < 31)
140 {
142 }
143 }
144 empty_ = false;
145 ++size_;
146 auto_clamp();
147 }
148
151 void push_back_char(char c)
152 {
153 switch (base_)
154 {
155 case 10:
156 push_back(c - '0');
157 break;
158 case 16:
159 c = toupper(c);
160 push_back(c <= '9' ? c - '0' : c - 'A' + 10);
161 break;
162 }
163 }
164
168 EntryModel &append(uint8_t val)
169 {
170 push_back(val);
171 return *this;
172 }
173
178 {
180 return *this;
181 }
182
184 void pop_back()
185 {
186 value_ /= base_;
187 if (value_ == 0 && numLeadingZeros_)
188 {
190 }
191 if (size_)
192 {
193 --size_;
194 }
196 {
197 isAtInitialValue_ = false;
198 auto_clamp();
199 // need to compute the size now that the initial value is false
201 }
202 if (size_ == 0)
203 {
204 // no more characters left, so the entry is "empty"
205 empty_ = true;
206 }
207 auto_clamp();
208 }
209
212 void set_base(int base)
213 {
214 HASSERT(base == 10 || base == 16);
215 base_ = base;
217 }
218
222 void set_base(int base, bool convert)
223 {
224 if (base != base_)
225 {
226 if (convert)
227 {
228 string str = get_string();
229 if (std::is_signed<T>::value)
230 {
231 value_ = strtoll(str.c_str(), nullptr, base);
232 }
233 else
234 {
235 value_ = strtoull(str.c_str(), nullptr, base);
236 }
237 }
238 set_base(base);
239 }
240 }
241
244 void set_value(T value)
245 {
246 init(maxSize_, base_, value, autoClamp_);
247 }
248
252 size_t size()
253 {
254 return size_;
255 }
256
259 size_t max_size()
260 {
261 return maxSize_;
262 }
263
266 bool empty()
267 {
268 return (!isAtInitialValue_ && empty_);
269 }
270
274 {
275 return size_ < maxSize_;
276 }
277
281 {
282 return isAtInitialValue_;
283 }
284
289 {
290 return numLeadingZeros_ > 0;
291 }
292
294 void set_leading_zeros(unsigned num)
295 {
296 numLeadingZeros_ = num;
297 }
298
309 T get_value(bool force_clamp = false)
310 {
311 if (force_clamp)
312 {
313 clamp(true);
314 }
315 return value_;
316 }
317
321 string get_string(bool right_justify = false)
322 {
323 string str;
325 {
326 switch (base_)
327 {
328 default:
329 // should never get here.
330 break;
331 case 10:
332 if (std::is_signed<T>::value)
333 {
334 str = int64_to_string(value_);
335 }
336 else
337 {
338 str = uint64_to_string(value_);
339 }
340 break;
341 case 16:
342 if (std::is_signed<T>::value)
343 {
344 str = int64_to_string_hex(value_);
345 }
346 else
347 {
348 str = uint64_to_string_hex(value_);
349 }
350 // requires all characters in upper case
351 transform(str.begin(), str.end(), str.begin(), toupper);
352 break;
353 }
354 }
355
357 {
358 str.insert(value_ < 0 ? 1 : 0, numLeadingZeros_, '0');
359 }
360 if (right_justify && str.size() < maxSize_)
361 {
362 str.insert(0, maxSize_ - str.size(), ' ');
363 }
364 return str;
365 }
366
368 void set_min()
369 {
371 }
372
374 void set_max()
375 {
377 }
378
381 {
382 if (value_ < 0)
383 {
384 --size_;
385 }
386 value_ = -value_;
387 if (value_ < 0)
388 {
389 ++size_;
390 }
391 auto_clamp();
392 }
393
400 virtual void clamp(bool force = false)
401 {
402 if (value_ == 0 && size_ < maxSize_ && !force)
403 {
404 // skip clamping if we have space for more leading zeros
405 return;
406 }
407 if (force || !empty_)
408 {
409 empty_ = false;
410 if (value_ < valueMin_)
411 {
414 }
415 else if (value_ > valueMax_)
416 {
419 }
420 }
421 }
422
428 {
429 isAtInitialValue_ = false;
430 if (value_ < std::numeric_limits<T>::max())
431 {
432 ++value_;
433 }
434 clamp();
435 return value_;
436 }
437
443 {
444 isAtInitialValue_ = false;
445 if (value_ > std::numeric_limits<T>::lowest())
446 {
447 --value_;
448 }
449 clamp();
450 return value_;
451 }
452
453protected:
459 void auto_clamp(bool force = false)
460 {
461 if (autoClamp_)
462 {
463 clamp(force);
464 }
465 }
466
468 virtual void set_boundaries()
469 {
470 valueMax_ = 0;
471 for (unsigned i = 0; i < maxSize_; ++i)
472 {
473 valueMax_ *= base_;
474 valueMax_ += base_ - 1;
475 }
476 valueMin_ = std::is_signed<T>::value ? valueMax_ / -base_ : 0;
477 }
478
481 {
482 // calculate new size_
483 size_ = value_ < 0 ? 1 : 0;
484 for (T tmp = value_ < 0 ? -value_ : value_; tmp != 0; tmp /= base_)
485 {
486 ++size_;
487 }
488 numLeadingZeros_ = std::min(static_cast<unsigned>(numLeadingZeros_),
489 static_cast<unsigned>(maxSize_ - size_));
491 }
492
496
497 unsigned numLeadingZeros_ : 5;
498 unsigned maxSize_ : 5;
499 unsigned size_ : 5;
500 unsigned isAtInitialValue_ : 1;
501 unsigned empty_ : 1;
502 unsigned base_ : 6;
503 unsigned autoClamp_ : 1;
504
506};
507
511template <class T> class EntryModelBounded : public EntryModel<T>
512{
513public:
516 : EntryModel<T>()
517 {
518 }
519
528 void init(unsigned max_size, int base, T value, T min, T max, T default_val,
529 bool automatic_clamp = true)
530 {
531 // purposely do not boundary check the min, max, and default values
532 EntryModel<T>::init(max_size, base, value, automatic_clamp);
533 // override type min/max values
536 valueDefault_ = default_val;
537 }
538
544
545private:
550 void clamp(bool force = false) override
551 {
552 if (force && EntryModel<T>::empty_)
553 {
554 set_default();
555 }
556 else
557 {
559 }
560 }
561
564 void set_boundaries() override
565 {
566 }
567
569
571};
572
573#endif // _UTILS_ENTRYMODEL_HXX_
574
Specialization of EntryModel with upper and lower bounds.
EntryModelBounded()
Constructor.
void set_default()
Set the value to the default.
void clamp(bool force=false) override
Clamp the value at the min or max.
T valueDefault_
default value
void init(unsigned max_size, int base, T value, T min, T max, T default_val, bool automatic_clamp=true)
Initialize with a value.
void set_boundaries() override
Override base class to do nothing.
Implementation of a text entry menu.
bool empty()
Test if the entry is "empty".
unsigned isAtInitialValue_
true if still has the initial value
unsigned maxSize_
maximum number of digits
string get_string(bool right_justify=false)
Get the value as a string.
void push_back_char(char c)
Append a character to the "back".
EntryModel()
Constructor.
EntryModel & append_char(char c)
Append a character to the "back".
unsigned numLeadingZeros_
number of leading zeros
bool is_at_initial_value()
Determine if this object is holding an initial or modified value.
void calculate_size()
Calculate the size in digits.
T value_
present value held
void pop_back()
Removes (deletes) a character off the end.
void change_sign()
Change the sign of the data.
void clear()
Clear the entry string.
void init(unsigned max_size, int base)
Initialize empty.
void set_leading_zeros(unsigned num)
Sets the number of leading zeros without changing the value.
T valueMin_
minimum value representable by maxSize_
void set_min()
Set the value to the minimum.
T operator--()
Pre-decrement value.
T operator++()
Pre-increment value.
bool cursor_visible()
Test if cursor is visible.
EntryModel & append(uint8_t val)
Append a value to the "back".
T get_value(bool force_clamp=false)
Get the entry as an unsigned integer value.
void auto_clamp(bool force=false)
Calls clamp() only if automatic clamping is enabled (autoClamp_ = true).
virtual void clamp(bool force=false)
Clamp the value at the min or max.
unsigned autoClamp_
true to auto clamp the values
void init(unsigned max_size, int base, T value, bool automatic_clamp=true)
Initialize with a value.
virtual void set_boundaries()
Set min and max boundaries supported based on maxSize_ (digit count).
unsigned empty_
true if the value_ is "empty"
void set_base(int base, bool convert)
Set the radix base.
size_t max_size()
Get the max size (in digits).
unsigned base_
radix base
size_t size()
Get the size (actual number of digits).
bool has_leading_zeros()
It is not always possible with get_string() to return the leading zeros.
void set_value(T value)
Set the value, keep the max number of digits and base the same.
void push_back(uint8_t val)
Append a value to the "back".
T valueMax_
maximum value representable by maxSize_
unsigned size_
actual number of digits
void set_max()
Set the value to the maximum.
void set_base(int base)
Set the radix base.
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Removes default copy-constructor and assignment added by C++.
Definition macros.h:171