diff --git a/src/Gui/QuantitySpinBox.cpp b/src/Gui/QuantitySpinBox.cpp index 7185118236f0..845508eb5857 100644 --- a/src/Gui/QuantitySpinBox.cpp +++ b/src/Gui/QuantitySpinBox.cpp @@ -84,7 +84,7 @@ class QuantitySpinBoxPrivate return text; } - bool validate(QString& input, Base::Quantity& result) const + bool validate(QString& input, Base::Quantity& result, const App::ObjectIdentifier& path) const { Q_Q(const QuantitySpinBox); @@ -97,9 +97,8 @@ class QuantitySpinBoxPrivate QString tmp = input; auto validateInput = [&](QString& tmp) -> QValidator::State { - int pos = 0; QValidator::State state; - Base::Quantity res = validateAndInterpret(tmp, pos, state); + Base::Quantity res = validateAndInterpret(tmp, state, path); res.setFormat(quantity.getFormat()); if (state == QValidator::Acceptable) { success = true; @@ -110,183 +109,120 @@ class QuantitySpinBoxPrivate }; QValidator::State state = validateInput(tmp); - if (state == QValidator::Intermediate) { - if (!q->hasExpression()) { - tmp = tmp.trimmed(); - tmp += QLatin1Char(' '); - tmp += unitStr; - validateInput(tmp); - } - else { - // Accept the expression as it is but try to add the right unit string - success = true; - - Base::Quantity quantity; - double value; - if (parseString(input, quantity, value)) { - quantity.setUnit(unit); - result = quantity; - - // Now translate the quantity into its string representation using the user-defined unit system - input = Base::UnitsApi::schemaTranslate(result); - } + if (state == QValidator::Intermediate && q->hasExpression()) { + // Accept the expression as it is but try to add the right unit string + success = true; + + Base::Quantity quantity; + double value; + if (parseString(input, quantity, value, path)) { + quantity.setUnit(unit); + result = quantity; + + // Now translate the quantity into its string representation using the user-defined unit system + input = Base::UnitsApi::schemaTranslate(result); } } return success; } - bool parseString(const QString& str, Base::Quantity& result, double& value) const + bool parseString(const QString& str, Base::Quantity& result, double& value, const App::ObjectIdentifier& path) const { + App::ObjectIdentifier pathtmp = path; try { QString copy = str; copy.remove(locale.groupSeparator()); - result = Base::Quantity::parse(copy); - value = result.getValue(); - return true; + //Expression parser + std::shared_ptr expr(ExpressionParser::parse(path.getDocumentObject(), copy.toUtf8().constData())); + if (expr) { + + std::unique_ptr res(expr->eval()); + NumberExpression * n = Base::freecad_dynamic_cast(res.get()); + if (n){ + result = n->getQuantity(); + value = result.getValue(); + return true; + } + } } - catch (Base::ParserError&) { + catch (Base::Exception&) { return false; } + return false; } - Base::Quantity validateAndInterpret(QString& input, int& pos, QValidator::State& state) const + Base::Quantity validateAndInterpret(QString& input, QValidator::State& state, const App::ObjectIdentifier& path) const { Base::Quantity res; const double max = this->maximum; const double min = this->minimum; QString copy = input; - - int len = copy.size(); - - const bool plus = max >= 0; - const bool minus = min <= 0; - - switch (len) { - case 0: - state = max != min ? QValidator::Intermediate : QValidator::Invalid; - goto end; - case 1: - if (copy.at(0) == locale.decimalPoint()) { - state = QValidator::Intermediate; - copy.prepend(QLatin1Char('0')); - pos++; - len++; - goto end; - } - else if (copy.at(0) == QLatin1Char('+')) { - // the quantity parser doesn't allow numbers of the form '+1.0' - state = QValidator::Invalid; - goto end; - } - else if (copy.at(0) == QLatin1Char('-')) { - if (minus) - state = QValidator::Intermediate; - else - state = QValidator::Invalid; - goto end; + double value = min; + bool ok = false; + + QChar space = QLatin1Char(' '), period = QLatin1Char ('.'), plus = QLatin1Char('+'), minus = QLatin1Char('-'), division = QLatin1Char('/'), multiply = QLatin1Char('*'), exp = QLatin1Char('^'), par = QLatin1Char('('); + + if (locale.negativeSign() != minus) + copy.replace(locale.negativeSign(), minus); + if (locale.positiveSign() != plus) + copy.replace(locale.positiveSign(), plus); + + //Prep for expression parser + unsigned int length = copy.length(), shift; + bool noUnit = false; + for (unsigned int pos=0; pos < length; pos++) { + QChar num = copy[pos]; + if (num == division || num == multiply || num == exp || num == par){ + break; // Stop if anything odd happens } - break; - case 2: - if (copy.at(1) == locale.decimalPoint() - && (plus && copy.at(0) == QLatin1Char('+'))) { - state = QValidator::Intermediate; - goto end; + else if (num.isNumber()) { + noUnit = true; } - if (copy.at(1) == locale.decimalPoint() - && (minus && copy.at(0) == QLatin1Char('-'))) { - state = QValidator::Intermediate; - copy.insert(1, QLatin1Char('0')); - pos++; - len++; - goto end; + else if (num != space && num != period && num != plus && num != minus) { + noUnit = false; } - break; - default: break; - } - { - if (copy.at(0) == locale.groupSeparator()) { - state = QValidator::Invalid; - goto end; - } - else if (len > 1) { - bool decOccurred = false; - for (const auto & i : copy) { - if (i == locale.decimalPoint()) { - // Disallow multiple decimal points within the same numeric substring - if (decOccurred) { - state = QValidator::Invalid; - goto end; - } - decOccurred = true; - } - // Reset decOcurred if non-numeric character found - else if (!(i == locale.groupSeparator() || i.isDigit())) { - decOccurred = false; - } - } + if (noUnit == true && + (num == plus || // 1+ -> 1mm+ + num == minus || // 1- -> 1mm- + (pos == length - 1 && (pos += 1)))) { // 1EOL-> 1mmEOL + copy.insert(pos, unitStr); + pos += shift = unitStr.length(); + length += shift; + noUnit = false; } + } - if (locale.negativeSign() != QLatin1Char('-')) - copy.replace(locale.negativeSign(), QLatin1Char('-')); - if (locale.positiveSign() != QLatin1Char('+')) - copy.replace(locale.positiveSign(), QLatin1Char('+')); + ok = parseString(copy, res, value, path); - double value = min; - bool ok = parseString(copy, res, value); + // If result has not unit: add default unit + if (res.getUnit().isEmpty()){ + res.setUnit(unit); + } - if (!ok) { - // input may not be finished - state = QValidator::Intermediate; - } - else if (value >= min && value <= max) { - if (copy.endsWith(locale.decimalPoint())) { - // input shouldn't end with a decimal point - state = QValidator::Intermediate; - } - else if (res.getUnit().isEmpty() && !this->unit.isEmpty()) { - // if not dimensionless the input should have a dimension - state = QValidator::Intermediate; - } - else if (res.getUnit() != this->unit) { - // If the user input is of the form "number * unit", "number + unit" - // or "number - unit" it's rejected by the quantity parser and it's - // assumed that the user input is not complete yet (Intermediate). - // However, if the user input is of the form "number / unit" it's accepted - // by the parser but because the units mismatch it's considered as invalid - // and the last valid input will be restored. - // See #0004422: PartDesign value input does not accept trailing slash - // To work around this issue of the quantity parser it's checked if the - // inversed unit matches and if yes the input is also considered as not - // complete. - if (res.getUnit().pow(-1) == this->unit) - state = QValidator::Intermediate; - else - state = QValidator::Invalid; - } - else { - state = QValidator::Acceptable; - } - } - else if (max == min) { // when max and min is the same the only non-Invalid input is max (or min) + if (!ok) { + // input may not be finished + state = QValidator::Intermediate; + } + else if (value >= min && value <= max) { + state = QValidator::Acceptable; + } + else if (max == min) { // when max and min is the same the only non-Invalid input is max (or min) + state = QValidator::Invalid; + } + else { + if ((value >= 0 && value > max) || (value < 0 && value < min)) { state = QValidator::Invalid; } else { - if ((value >= 0 && value > max) || (value < 0 && value < min)) { - state = QValidator::Invalid; - } - else { - state = QValidator::Intermediate; - } + state = QValidator::Intermediate; } } -end: if (state != QValidator::Acceptable) { res.setValue(max > 0 ? min : max); } - input = copy; return res; } @@ -491,10 +427,10 @@ void QuantitySpinBox::validateInput() { Q_D(QuantitySpinBox); - int pos = 0; QValidator::State state; QString text = lineEdit()->text(); - d->validateAndInterpret(text, pos, state); + const App::ObjectIdentifier & path = getPath(); + d->validateAndInterpret(text, state, path); if (state != QValidator::Acceptable) { lineEdit()->setText(d->validStr); } @@ -550,7 +486,8 @@ void QuantitySpinBox::userInput(const QString & text) QString tmp = text; Base::Quantity res; - if (d->validate(tmp, res)) { + const App::ObjectIdentifier & path = getPath(); + if (d->validate(tmp, res, path)) { d->validStr = tmp; d->validInput = true; } @@ -971,12 +908,12 @@ Base::Quantity QuantitySpinBox::valueFromText(const QString &text) const Q_D(const QuantitySpinBox); QString copy = text; - int pos = lineEdit()->cursorPosition(); QValidator::State state = QValidator::Acceptable; - Base::Quantity quant = d->validateAndInterpret(copy, pos, state); + const App::ObjectIdentifier & path = getPath(); + Base::Quantity quant = d->validateAndInterpret(copy, state, path); if (state != QValidator::Acceptable) { fixup(copy); - quant = d->validateAndInterpret(copy, pos, state); + quant = d->validateAndInterpret(copy, state, path); } return quant; @@ -985,8 +922,11 @@ Base::Quantity QuantitySpinBox::valueFromText(const QString &text) const QValidator::State QuantitySpinBox::validate(QString &text, int &pos) const { Q_D(const QuantitySpinBox); + Q_UNUSED(pos) + QValidator::State state; - d->validateAndInterpret(text, pos, state); + const App::ObjectIdentifier & path = getPath(); + d->validateAndInterpret(text, state, path); return state; }