You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
977 lines
26 KiB
977 lines
26 KiB
2 years ago
|
/* / mcu / sound.h
|
||
|
* EEZ Modular Firmware
|
||
|
* Copyright (C) 2015-present, Envox d.o.o.
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include <eez/conf.h>
|
||
|
|
||
|
#if OPTION_KEYPAD
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <ctype.h>
|
||
|
#include <math.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <eez/core/sound.h>
|
||
|
|
||
|
#include <eez/gui/gui.h>
|
||
|
#include <eez/gui/widgets/button.h>
|
||
|
#include <eez/gui/widgets/text.h>
|
||
|
#include <eez/gui/widgets/display_data.h>
|
||
|
#include <eez/gui/keypad.h>
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
#pragma warning(disable : 4996)
|
||
|
#endif
|
||
|
|
||
|
#define CONF_GUI_KEYPAD_PASSWORD_LAST_CHAR_VISIBLE_DURATION_MS 500UL
|
||
|
|
||
|
namespace eez {
|
||
|
namespace gui {
|
||
|
|
||
|
void Keypad::pageAlloc() {
|
||
|
}
|
||
|
|
||
|
void Keypad::pageFree() {
|
||
|
m_appContext = nullptr;
|
||
|
}
|
||
|
|
||
|
void Keypad::init(AppContext *appContext, const char *label_) {
|
||
|
if (m_appContext != appContext) {
|
||
|
if (m_appContext) {
|
||
|
m_appContext->popPage();
|
||
|
}
|
||
|
m_appContext = appContext;
|
||
|
}
|
||
|
|
||
|
m_label[0] = 0;
|
||
|
m_keypadText[0] = 0;
|
||
|
m_cursorPosition = 0;
|
||
|
m_xScroll = 0;
|
||
|
m_okCallback = 0;
|
||
|
m_cancelCallback = 0;
|
||
|
m_setDefaultCallback = 0;
|
||
|
m_keypadMode = KEYPAD_MODE_LOWERCASE;
|
||
|
m_isPassword = false;
|
||
|
|
||
|
if (label_) {
|
||
|
stringCopy(m_label, sizeof(m_label), label_);
|
||
|
} else {
|
||
|
m_label[0] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Value Keypad::getKeypadTextValue() {
|
||
|
char text[MAX_KEYPAD_TEXT_LENGTH + 2];
|
||
|
getKeypadText(text, sizeof(text));
|
||
|
return Value::makeStringRef(text, strlen(text), 0x893cbf99);
|
||
|
}
|
||
|
|
||
|
void Keypad::getKeypadText(char *text, size_t count) {
|
||
|
// text
|
||
|
char *textPtr = text;
|
||
|
|
||
|
if (*m_label) {
|
||
|
stringCopy(textPtr, count, m_label);
|
||
|
auto len = strlen(m_label);
|
||
|
textPtr += len;
|
||
|
count -= len;
|
||
|
}
|
||
|
|
||
|
if (m_isPassword) {
|
||
|
int n = strlen(m_keypadText);
|
||
|
if (n > 0) {
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < n - 1; ++i) {
|
||
|
*textPtr++ = '*';
|
||
|
}
|
||
|
|
||
|
uint32_t currentTimeMs = millis();
|
||
|
if (
|
||
|
currentTimeMs - m_lastKeyAppendTimeMs <=
|
||
|
CONF_GUI_KEYPAD_PASSWORD_LAST_CHAR_VISIBLE_DURATION_MS
|
||
|
) {
|
||
|
*textPtr++ = m_keypadText[i];
|
||
|
} else {
|
||
|
*textPtr++ = '*';
|
||
|
}
|
||
|
|
||
|
*textPtr++ = 0;
|
||
|
} else {
|
||
|
*textPtr = 0;
|
||
|
}
|
||
|
} else {
|
||
|
stringCopy(textPtr, count, m_keypadText);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Keypad::start(AppContext *appContext, const char *label, const char *text, int minChars_, int maxChars_, bool isPassword_, void (*ok)(char *), void (*cancel)(), void (*setDefault)()) {
|
||
|
init(appContext, label);
|
||
|
|
||
|
m_okCallback = ok;
|
||
|
m_cancelCallback = cancel;
|
||
|
m_setDefaultCallback = setDefault;
|
||
|
|
||
|
m_minChars = minChars_;
|
||
|
m_maxChars = maxChars_;
|
||
|
m_isPassword = isPassword_;
|
||
|
|
||
|
if (text) {
|
||
|
stringCopy(m_keypadText, sizeof(m_keypadText), text);
|
||
|
m_cursorPosition = strlen(m_keypadText);
|
||
|
} else {
|
||
|
m_keypadText[0] = 0;
|
||
|
m_cursorPosition = 0;
|
||
|
}
|
||
|
m_keypadMode = KEYPAD_MODE_LOWERCASE;
|
||
|
}
|
||
|
|
||
|
void Keypad::insertChar(char c) {
|
||
|
int n = strlen(m_keypadText);
|
||
|
if (n < m_maxChars && (n + (*m_label ? strlen(m_label) : 0)) < MAX_KEYPAD_TEXT_LENGTH) {
|
||
|
for (int i = n; i >= m_cursorPosition; i--) {
|
||
|
m_keypadText[i + 1] = m_keypadText[i];
|
||
|
}
|
||
|
m_keypadText[m_cursorPosition] = c;
|
||
|
m_cursorPosition++;
|
||
|
m_lastKeyAppendTimeMs = millis();
|
||
|
} else {
|
||
|
sound::playBeep();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Keypad::key() {
|
||
|
auto widgetCursor = getFoundWidgetAtDown();
|
||
|
auto widget = widgetCursor.widget;
|
||
|
|
||
|
if (widget->type == WIDGET_TYPE_TEXT) {
|
||
|
auto textWidget = (TextWidget *)widget;
|
||
|
if (textWidget->text) {
|
||
|
key(static_cast<const char *>(textWidget->text)[0]);
|
||
|
return;
|
||
|
}
|
||
|
} else if (widget->type == WIDGET_TYPE_BUTTON) {
|
||
|
auto buttonWidget = (ButtonWidget *)widget;
|
||
|
if (buttonWidget->text) {
|
||
|
key(static_cast<const char *>(buttonWidget->text)[0]);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Value value = get(widgetCursor, widget->data);
|
||
|
|
||
|
char str[10];
|
||
|
value.toText(str, sizeof(str));
|
||
|
key(str[0]);
|
||
|
}
|
||
|
|
||
|
void Keypad::key(char ch) {
|
||
|
insertChar(ch);
|
||
|
}
|
||
|
|
||
|
void Keypad::space() {
|
||
|
insertChar(' ');
|
||
|
}
|
||
|
|
||
|
void Keypad::back() {
|
||
|
if (m_cursorPosition > 0) {
|
||
|
int n = strlen(m_keypadText);
|
||
|
for (int i = m_cursorPosition; i < n; i++) {
|
||
|
m_keypadText[i - 1] = m_keypadText[i];
|
||
|
}
|
||
|
m_cursorPosition--;
|
||
|
m_keypadText[n - 1] = 0;
|
||
|
} else {
|
||
|
sound::playBeep();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Keypad::clear() {
|
||
|
m_keypadText[0] = 0;
|
||
|
m_cursorPosition = 0;
|
||
|
}
|
||
|
|
||
|
void Keypad::sign() {
|
||
|
}
|
||
|
|
||
|
void Keypad::unit() {
|
||
|
}
|
||
|
|
||
|
bool Keypad::isOkEnabled() {
|
||
|
int n = strlen(m_keypadText);
|
||
|
return n >= m_minChars && n <= m_maxChars;
|
||
|
}
|
||
|
|
||
|
void Keypad::ok() {
|
||
|
m_okCallback(m_keypadText);
|
||
|
}
|
||
|
|
||
|
void Keypad::cancel() {
|
||
|
if (m_cancelCallback) {
|
||
|
m_cancelCallback();
|
||
|
} else {
|
||
|
m_appContext->popPage();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool Keypad::canSetDefault() {
|
||
|
return m_setDefaultCallback != nullptr;
|
||
|
}
|
||
|
|
||
|
void Keypad::setDefault() {
|
||
|
if (m_setDefaultCallback) {
|
||
|
m_setDefaultCallback();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int Keypad::getCursorPosition() {
|
||
|
return m_cursorPosition + strlen(m_label);
|
||
|
}
|
||
|
|
||
|
void Keypad::setCursorPosition(int cursorPosition) {
|
||
|
m_cursorPosition = cursorPosition - strlen(m_label);
|
||
|
|
||
|
if (m_cursorPosition < 0) {
|
||
|
m_cursorPosition = 0;
|
||
|
} else {
|
||
|
int n = strlen(m_keypadText);
|
||
|
if (m_cursorPosition > n) {
|
||
|
m_cursorPosition = n;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int Keypad::getXScroll(const WidgetCursor &widgetCursor) {
|
||
|
int x = DISPLAY_DATA_getCursorXPosition(getCursorPosition(), widgetCursor);
|
||
|
|
||
|
x -= widgetCursor.x;
|
||
|
|
||
|
if (x < m_xScroll + widgetCursor.w / 4) {
|
||
|
m_xScroll = MAX(x - widgetCursor.w / 2, 0);
|
||
|
} else if (m_xScroll + widgetCursor.w < x + CURSOR_WIDTH) {
|
||
|
m_xScroll = x + CURSOR_WIDTH - widgetCursor.w;
|
||
|
}
|
||
|
|
||
|
return m_xScroll;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
NumericKeypadOptions::NumericKeypadOptions() {
|
||
|
pageId = PAGE_ID_NUMERIC_KEYPAD;
|
||
|
|
||
|
this->slotIndex = -1;
|
||
|
this->subchannelIndex = -1;
|
||
|
|
||
|
allowZero = false;
|
||
|
min = NAN;
|
||
|
max = NAN;
|
||
|
|
||
|
flags.checkWhileTyping = false;
|
||
|
flags.signButtonEnabled = false;
|
||
|
flags.dotButtonEnabled = false;
|
||
|
|
||
|
option1Action = NUMERIC_KEYPAD_OPTION_ACTION_NONE;
|
||
|
option2Action = NUMERIC_KEYPAD_OPTION_ACTION_NONE;
|
||
|
option3Action = NUMERIC_KEYPAD_OPTION_ACTION_NONE;
|
||
|
|
||
|
editValueUnit = UNIT_UNKNOWN;
|
||
|
|
||
|
encoderPrecision = 0.01f;
|
||
|
}
|
||
|
|
||
|
void NumericKeypadOptions::enableMaxButton() {
|
||
|
option1Action = NUMERIC_KEYPAD_OPTION_ACTION_MAX;
|
||
|
option1ButtonText = "max";
|
||
|
}
|
||
|
|
||
|
void NumericKeypadOptions::enableMinButton() {
|
||
|
if (option2Action != NUMERIC_KEYPAD_OPTION_ACTION_NONE) {
|
||
|
option3Action = NUMERIC_KEYPAD_OPTION_ACTION_MIN;
|
||
|
option3ButtonText = "min";
|
||
|
} else {
|
||
|
option2Action = NUMERIC_KEYPAD_OPTION_ACTION_MIN;
|
||
|
option2ButtonText = "min";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NumericKeypadOptions::enableDefButton() {
|
||
|
if (option2Action != NUMERIC_KEYPAD_OPTION_ACTION_NONE) {
|
||
|
option3Action = NUMERIC_KEYPAD_OPTION_ACTION_DEF;
|
||
|
option3ButtonText = "def";
|
||
|
} else {
|
||
|
option2Action = NUMERIC_KEYPAD_OPTION_ACTION_DEF;
|
||
|
option2ButtonText = "def";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void NumericKeypad::init(
|
||
|
AppContext *appContext,
|
||
|
const char *label,
|
||
|
const Value &value,
|
||
|
NumericKeypadOptions &options,
|
||
|
void(*okFloat)(float),
|
||
|
void(*okUint32)(uint32_t),
|
||
|
void(*cancel)()
|
||
|
) {
|
||
|
Keypad::init(appContext, label);
|
||
|
|
||
|
m_okFloatCallback = okFloat;
|
||
|
m_okUint32Callback = okUint32;
|
||
|
m_cancelCallback = cancel;
|
||
|
|
||
|
m_startValue = value;
|
||
|
|
||
|
m_options = options;
|
||
|
|
||
|
if (value.getType() == VALUE_TYPE_IP_ADDRESS) {
|
||
|
m_options.flags.dotButtonEnabled = true;
|
||
|
}
|
||
|
|
||
|
m_options.editValueUnit = findDerivedUnit(m_startValue.getFloat(), m_startValue.getUnit());
|
||
|
|
||
|
m_minChars = 0;
|
||
|
m_maxChars = 16;
|
||
|
|
||
|
reset();
|
||
|
|
||
|
if (value.getType() == VALUE_TYPE_IP_ADDRESS) {
|
||
|
ipAddressToString(value.getUInt32(), m_keypadText, sizeof(m_keypadText));
|
||
|
m_cursorPosition = strlen(m_keypadText);
|
||
|
m_state = BEFORE_DOT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool NumericKeypad::isEditing() {
|
||
|
return m_state != EMPTY && m_state != START;
|
||
|
}
|
||
|
|
||
|
char NumericKeypad::getDotSign() {
|
||
|
if (m_startValue.getType() == VALUE_TYPE_TIME_ZONE) {
|
||
|
return ':';
|
||
|
}
|
||
|
return '.';
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::appendEditUnit(char *text, size_t maxTextLength) {
|
||
|
stringAppendString(text, maxTextLength, " ");
|
||
|
stringAppendString(text, maxTextLength, getUnitName(m_options.editValueUnit));
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::getKeypadText(char *text, size_t count) {
|
||
|
if (*m_label) {
|
||
|
stringCopy(text, count, m_label);
|
||
|
text += strlen(m_label);
|
||
|
}
|
||
|
|
||
|
getText(text, 16);
|
||
|
}
|
||
|
|
||
|
bool NumericKeypad::getText(char *text, size_t count) {
|
||
|
if (m_state == START) {
|
||
|
m_startValue.toText(text, count);
|
||
|
return false;
|
||
|
}
|
||
|
stringCopy(text, count, m_keypadText);
|
||
|
appendEditUnit(text, count);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
Unit NumericKeypad::getEditUnit() {
|
||
|
return m_options.editValueUnit;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool NumericKeypad::isMicroAmperAllowed() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool NumericKeypad::isAmperAllowed() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
float NumericKeypad::getPrecision() {
|
||
|
return m_options.encoderPrecision;
|
||
|
}
|
||
|
|
||
|
Unit NumericKeypad::getSwitchToUnit() {
|
||
|
Unit unit = getSmallerUnit(m_options.editValueUnit, m_options.min, getPrecision());
|
||
|
if (unit == UNIT_UNKNOWN) {
|
||
|
unit = getBiggestUnit(m_options.editValueUnit, m_options.max);
|
||
|
}
|
||
|
|
||
|
if (unit == UNIT_UNKNOWN) {
|
||
|
return m_options.editValueUnit;
|
||
|
}
|
||
|
|
||
|
if (unit == UNIT_MICRO_AMPER) {
|
||
|
if (isMicroAmperAllowed()) {
|
||
|
return UNIT_MICRO_AMPER;
|
||
|
} else {
|
||
|
return getBiggestUnit(m_options.editValueUnit, m_options.max);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (unit == UNIT_MICRO_AMPER_PP) {
|
||
|
if (isMicroAmperAllowed()) {
|
||
|
return UNIT_MICRO_AMPER_PP;
|
||
|
} else {
|
||
|
return getBiggestUnit(m_options.editValueUnit, m_options.max);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (unit == UNIT_AMPER) {
|
||
|
if (isAmperAllowed()) {
|
||
|
return UNIT_AMPER;
|
||
|
} else {
|
||
|
return getSmallestUnit(m_options.editValueUnit, m_options.min, getPrecision());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (unit == UNIT_AMPER_PP) {
|
||
|
if (isAmperAllowed()) {
|
||
|
return UNIT_AMPER_PP;
|
||
|
} else {
|
||
|
return getSmallestUnit(m_options.editValueUnit, m_options.min, getPrecision());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return unit;
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::toggleEditUnit() {
|
||
|
m_options.editValueUnit = getSwitchToUnit();
|
||
|
}
|
||
|
|
||
|
int NumericKeypad::getCursorPosition() {
|
||
|
if (m_state == START) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return Keypad::getCursorPosition();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
double NumericKeypad::getValue() {
|
||
|
const char *p = m_keypadText;
|
||
|
|
||
|
bool empty = true;
|
||
|
|
||
|
int a = 0;
|
||
|
double b = 0;
|
||
|
int sign = 1;
|
||
|
|
||
|
if (*p == '-') {
|
||
|
sign = -1;
|
||
|
++p;
|
||
|
} else if (*p == '+') {
|
||
|
++p;
|
||
|
}
|
||
|
|
||
|
while (*p && *p != getDotSign()) {
|
||
|
a = a * 10 + (*p - '0');
|
||
|
++p;
|
||
|
empty = false;
|
||
|
}
|
||
|
|
||
|
if (*p) {
|
||
|
const char *q = p + strlen(p) - 1;
|
||
|
while (q != p) {
|
||
|
b = (b + (*q - '0')) / 10;
|
||
|
--q;
|
||
|
empty = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (empty) {
|
||
|
return NAN;
|
||
|
}
|
||
|
|
||
|
return sign * (a + b) * getUnitFactor(m_options.editValueUnit);
|
||
|
}
|
||
|
|
||
|
bool NumericKeypad::checkNumSignificantDecimalDigits() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::digit(int d) {
|
||
|
if (m_state == START || m_state == EMPTY) {
|
||
|
m_state = BEFORE_DOT;
|
||
|
if (m_startValue.getType() == VALUE_TYPE_TIME_ZONE) {
|
||
|
if (strlen(m_keypadText) == 0) {
|
||
|
insertChar('+');
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if (m_keypadText[m_cursorPosition] == '-' || m_keypadText[m_cursorPosition] == '+') {
|
||
|
sound::playBeep();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
insertChar(d + '0');
|
||
|
|
||
|
if (!checkNumSignificantDecimalDigits()) {
|
||
|
back();
|
||
|
sound::playBeep();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::dot() {
|
||
|
if (!m_options.flags.dotButtonEnabled) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (m_startValue.getType() == VALUE_TYPE_IP_ADDRESS) {
|
||
|
if (m_state == EMPTY || m_state == START) {
|
||
|
sound::playBeep();
|
||
|
} else {
|
||
|
insertChar(getDotSign());
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (m_state == START || m_state == EMPTY) {
|
||
|
if (m_startValue.getType() == VALUE_TYPE_TIME_ZONE) {
|
||
|
if (strlen(m_keypadText) == 0) {
|
||
|
insertChar('+');
|
||
|
}
|
||
|
}
|
||
|
insertChar('0');
|
||
|
m_state = BEFORE_DOT;
|
||
|
}
|
||
|
|
||
|
if (m_state == BEFORE_DOT && m_keypadText[m_cursorPosition] != '-' && m_keypadText[m_cursorPosition] != '+') {
|
||
|
insertChar(getDotSign());
|
||
|
m_state = AFTER_DOT;
|
||
|
} else {
|
||
|
sound::playBeep();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::reset() {
|
||
|
m_state = m_startValue.getType() != VALUE_TYPE_UNDEFINED ? START : EMPTY;
|
||
|
m_keypadText[0] = 0;
|
||
|
m_cursorPosition = 0;
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::key(char ch) {
|
||
|
if (ch >= '0' && ch <= '9') {
|
||
|
digit(ch - '0');
|
||
|
} else if (ch == '.') {
|
||
|
dot();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::space() {
|
||
|
// DO NOTHING
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::caps() {
|
||
|
// DO NOTHING
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::back() {
|
||
|
if (m_cursorPosition > 0) {
|
||
|
if (m_keypadText[m_cursorPosition - 1] == getDotSign()) {
|
||
|
m_state = BEFORE_DOT;
|
||
|
}
|
||
|
|
||
|
Keypad::back();
|
||
|
|
||
|
int n = strlen(m_keypadText);
|
||
|
|
||
|
if (n == 1) {
|
||
|
if (m_keypadText[0] == '+' || m_keypadText[0] == '-') {
|
||
|
m_state = EMPTY;
|
||
|
}
|
||
|
} else if (n == 0) {
|
||
|
m_state = EMPTY;
|
||
|
}
|
||
|
} else if (m_state == START) {
|
||
|
m_state = EMPTY;
|
||
|
} else {
|
||
|
sound::playBeep();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::clear() {
|
||
|
if (m_state != START) {
|
||
|
reset();
|
||
|
} else {
|
||
|
sound::playBeep();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::sign() {
|
||
|
if (m_options.flags.signButtonEnabled) {
|
||
|
if (m_startValue.getType() == VALUE_TYPE_TIME_ZONE) {
|
||
|
if (m_keypadText[0] == 0) {
|
||
|
m_keypadText[0] = '-';
|
||
|
m_keypadText[1] = 0;
|
||
|
m_cursorPosition = 1;
|
||
|
m_state = BEFORE_DOT;
|
||
|
} else if (m_keypadText[0] == '-') {
|
||
|
m_keypadText[0] = '+';
|
||
|
} else {
|
||
|
m_keypadText[0] = '-';
|
||
|
}
|
||
|
} else {
|
||
|
if (m_keypadText[0] == '-') {
|
||
|
stringCopy(m_keypadText, sizeof(m_keypadText), m_keypadText + 1);
|
||
|
if (m_cursorPosition > 0) {
|
||
|
m_cursorPosition--;
|
||
|
}
|
||
|
} else if (m_keypadText[0] == '+') {
|
||
|
m_keypadText[0] = '-';
|
||
|
} else {
|
||
|
auto n = strlen(m_keypadText);
|
||
|
memmove(m_keypadText + 1, m_keypadText, n);
|
||
|
m_keypadText[n + 1] = 0;
|
||
|
m_keypadText[0] = '-';
|
||
|
m_cursorPosition++;
|
||
|
}
|
||
|
|
||
|
if (m_state == START || m_state == EMPTY) {
|
||
|
m_state = BEFORE_DOT;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// not supported
|
||
|
sound::playBeep();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::unit() {
|
||
|
if (m_state == START) {
|
||
|
m_state = EMPTY;
|
||
|
}
|
||
|
toggleEditUnit();
|
||
|
}
|
||
|
|
||
|
bool NumericKeypad::isOkEnabled() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::showLessThanMinErrorMessage() {
|
||
|
m_appContext->errorMessageWithAction("Less than min. allowed value", nullptr, "Close");
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::showGreaterThanMaxErrorMessage() {
|
||
|
m_appContext->errorMessageWithAction("Greater than max. allowed value", nullptr, "Close");
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::ok() {
|
||
|
if (m_state == START) {
|
||
|
if (m_startValue.getType() == VALUE_TYPE_IP_ADDRESS) {
|
||
|
m_okFloatCallback(1.0f * m_startValue.getUInt32());
|
||
|
} else if (m_startValue.getType() == VALUE_TYPE_TIME_ZONE) {
|
||
|
m_okFloatCallback(m_startValue.getInt() / 100.0f);
|
||
|
} else {
|
||
|
m_okFloatCallback(m_startValue.isFloat() ? m_startValue.getFloat()
|
||
|
: m_startValue.getInt());
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (m_state != EMPTY) {
|
||
|
if (m_startValue.getType() == VALUE_TYPE_IP_ADDRESS) {
|
||
|
uint32_t ipAddress;
|
||
|
if (parseIpAddress(m_keypadText, strlen(m_keypadText), ipAddress)) {
|
||
|
m_okUint32Callback(ipAddress);
|
||
|
m_state = START;
|
||
|
m_keypadText[0] = 0;
|
||
|
m_cursorPosition = 0;
|
||
|
} else {
|
||
|
m_appContext->errorMessage("Invalid IP address format!");
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
} else {
|
||
|
float value = (float)getValue();
|
||
|
if (isNaN(value)) {
|
||
|
sound::playBeep();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float EPSILON = 1E-9f;
|
||
|
|
||
|
if (!isNaN(m_options.min) && value < m_options.min && !(value == 0 && m_options.allowZero)) {
|
||
|
if (value < m_options.min - EPSILON) {
|
||
|
showLessThanMinErrorMessage();
|
||
|
return;
|
||
|
}
|
||
|
value = m_options.min;
|
||
|
} else if (!isNaN(m_options.max) && value > m_options.max) {
|
||
|
if (value > m_options.max + EPSILON) {
|
||
|
showGreaterThanMaxErrorMessage();
|
||
|
return;
|
||
|
}
|
||
|
value = m_options.max;
|
||
|
}
|
||
|
|
||
|
m_okFloatCallback((float)value);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sound::playBeep();
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::cancel() {
|
||
|
if (m_cancelCallback) {
|
||
|
m_cancelCallback();
|
||
|
} else {
|
||
|
m_appContext->popPage();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::setMaxValue() {
|
||
|
m_okFloatCallback(m_options.max);
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::setMinValue() {
|
||
|
m_okFloatCallback(m_options.min);
|
||
|
}
|
||
|
|
||
|
void NumericKeypad::setDefValue() {
|
||
|
m_okFloatCallback(m_options.def);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void onKeypadTextTouch(const WidgetCursor &widgetCursor, Event &touchEvent) {
|
||
|
if (touchEvent.type != EVENT_TYPE_TOUCH_DOWN && touchEvent.type != EVENT_TYPE_TOUCH_MOVE) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Keypad *keypad = getActiveKeypad();
|
||
|
if (keypad) {
|
||
|
keypad->setCursorPosition(DISPLAY_DATA_getCharIndexAtPosition(touchEvent.x, widgetCursor));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
void data_keypad_edit_unit(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
Keypad *keypad = getActiveKeypad();
|
||
|
if (keypad) {
|
||
|
value = getUnitName(keypad->getSwitchToUnit());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_text(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
Keypad *keypad = getActiveKeypad();
|
||
|
if (keypad) {
|
||
|
value = keypad->getKeypadTextValue();
|
||
|
}
|
||
|
} else if (operation == DATA_OPERATION_GET_TEXT_CURSOR_POSITION) {
|
||
|
Keypad *keypad = getActiveKeypad();
|
||
|
if (keypad) {
|
||
|
value = keypad->getCursorPosition();
|
||
|
}
|
||
|
} else if (operation == DATA_OPERATION_GET_X_SCROLL) {
|
||
|
Keypad *keypad = getActiveKeypad();
|
||
|
if (keypad) {
|
||
|
value = keypad->getXScroll(*(WidgetCursor *)value.getVoidPointer());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_mode(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
Keypad *keypad = getActiveKeypad();
|
||
|
if (keypad) {
|
||
|
value = keypad->m_keypadMode;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_option1_text(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
NumericKeypad *keypad = getActiveNumericKeypad();
|
||
|
if (keypad) {
|
||
|
value = Value(keypad->m_options.option1Action != NUMERIC_KEYPAD_OPTION_ACTION_NONE ? keypad->m_options.option1ButtonText : "");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_option1_enabled(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
NumericKeypad *keypad = getActiveNumericKeypad();
|
||
|
if (keypad) {
|
||
|
value = (int)(keypad->m_options.option1Action != NUMERIC_KEYPAD_OPTION_ACTION_NONE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_option2_text(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
NumericKeypad *keypad = getActiveNumericKeypad();
|
||
|
if (keypad) {
|
||
|
value = Value(keypad->m_options.option2Action != NUMERIC_KEYPAD_OPTION_ACTION_NONE ? keypad->m_options.option2ButtonText : "");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_option2_enabled(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
NumericKeypad *keypad = getActiveNumericKeypad();
|
||
|
if (keypad) {
|
||
|
value = (int)(keypad->m_options.option2Action != NUMERIC_KEYPAD_OPTION_ACTION_NONE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_option3_text(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
NumericKeypad *keypad = getActiveNumericKeypad();
|
||
|
if (keypad) {
|
||
|
value = Value(keypad->m_options.option3Action != NUMERIC_KEYPAD_OPTION_ACTION_NONE ? keypad->m_options.option3ButtonText : "");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_option3_enabled(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
NumericKeypad *keypad = getActiveNumericKeypad();
|
||
|
if (keypad) {
|
||
|
value = (int)(keypad->m_options.option3Action != NUMERIC_KEYPAD_OPTION_ACTION_NONE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_sign_enabled(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
NumericKeypad *keypad = getActiveNumericKeypad();
|
||
|
if (keypad) {
|
||
|
value = (int)keypad->m_options.flags.signButtonEnabled;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_dot_enabled(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
NumericKeypad *keypad = getActiveNumericKeypad();
|
||
|
if (keypad) {
|
||
|
value = (int)keypad->m_options.flags.dotButtonEnabled;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_unit_enabled(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
NumericKeypad *keypad = getActiveNumericKeypad();
|
||
|
if (keypad) {
|
||
|
value =
|
||
|
getSmallestUnit(keypad->m_options.editValueUnit, keypad->m_options.min, keypad->getPrecision()) !=
|
||
|
getBiggestUnit(keypad->m_options.editValueUnit, keypad->m_options.max);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_ok_enabled(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
Keypad *keypad = getActiveKeypad();
|
||
|
if (keypad) {
|
||
|
value = keypad->isOkEnabled() ? 1 : 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void data_keypad_can_set_default(DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) {
|
||
|
if (operation == DATA_OPERATION_GET) {
|
||
|
Keypad *keypad = getActiveKeypad();
|
||
|
if (keypad) {
|
||
|
value = keypad->canSetDefault();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void action_keypad_key() {
|
||
|
getActiveKeypad()->key();
|
||
|
}
|
||
|
|
||
|
void action_keypad_space() {
|
||
|
getActiveKeypad()->space();
|
||
|
}
|
||
|
|
||
|
void action_keypad_back() {
|
||
|
getActiveKeypad()->back();
|
||
|
}
|
||
|
|
||
|
void action_keypad_clear() {
|
||
|
getActiveKeypad()->clear();
|
||
|
}
|
||
|
|
||
|
void action_toggle_keypad_mode() {
|
||
|
auto keypad = getActiveKeypad();
|
||
|
if (keypad->m_keypadMode == KEYPAD_MODE_LOWERCASE) {
|
||
|
getActiveKeypad()->m_keypadMode = KEYPAD_MODE_UPPERCASE;
|
||
|
} else if (keypad->m_keypadMode == KEYPAD_MODE_UPPERCASE) {
|
||
|
getActiveKeypad()->m_keypadMode = KEYPAD_MODE_SYMBOL;
|
||
|
} else {
|
||
|
getActiveKeypad()->m_keypadMode = KEYPAD_MODE_LOWERCASE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void action_keypad_ok() {
|
||
|
getActiveKeypad()->ok();
|
||
|
}
|
||
|
|
||
|
void action_keypad_cancel() {
|
||
|
getActiveKeypad()->cancel();
|
||
|
}
|
||
|
|
||
|
void action_keypad_set_default() {
|
||
|
getActiveKeypad()->setDefault();
|
||
|
}
|
||
|
|
||
|
void action_keypad_sign() {
|
||
|
getActiveKeypad()->sign();
|
||
|
}
|
||
|
|
||
|
void action_keypad_unit() {
|
||
|
getActiveKeypad()->unit();
|
||
|
}
|
||
|
|
||
|
void action_keypad_option1() {
|
||
|
executeNumericKeypadOptionHook(1);
|
||
|
}
|
||
|
|
||
|
void action_keypad_option2() {
|
||
|
executeNumericKeypadOptionHook(2);
|
||
|
}
|
||
|
|
||
|
void action_keypad_option3() {
|
||
|
executeNumericKeypadOptionHook(3);
|
||
|
}
|
||
|
|
||
|
} // namespace gui
|
||
|
} // namespace eez
|
||
|
|
||
|
#endif
|