diff --git a/src/Base/UnitsApi.h b/src/Base/UnitsApi.h index 50f7cbb894..a6e4e1a21a 100644 --- a/src/Base/UnitsApi.h +++ b/src/Base/UnitsApi.h @@ -73,7 +73,7 @@ public: // set the number of decimals static void setDecimals(int); - // fet the number of decimals + // get the number of decimals static int getDecimals(); /// set the application defaults //static void setDefaults(void); diff --git a/src/Mod/TechDraw/App/DrawViewDimension.cpp b/src/Mod/TechDraw/App/DrawViewDimension.cpp index a27cef499b..6b30f43f29 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.cpp +++ b/src/Mod/TechDraw/App/DrawViewDimension.cpp @@ -581,6 +581,32 @@ bool DrawViewDimension::isMultiValueSchema(void) const return result; } +std::string DrawViewDimension::getBaseLengthUnit(Base::UnitSystem system) +{ + switch (system) { + case Base::UnitSystem::SI1: + return "mm"; + case Base::UnitSystem::SI2: + return "m"; + case Base::UnitSystem::Imperial1: + return "in"; + case Base::UnitSystem::ImperialDecimal: + return "in"; + case Base::UnitSystem::Centimeters: + return "cm"; + case Base::UnitSystem::ImperialBuilding: + return "ft"; + case Base::UnitSystem::MmMin: + return "mm"; + case Base::UnitSystem::ImperialCivil: + return "ft"; + case Base::UnitSystem::FemMilliMeterNewton: + return "mm"; + default: + return "Unknown schema"; + } +} + std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int partial) { std::string result; @@ -588,8 +614,8 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int QString qUserStringUnits; QString formattedValue; - bool angularMeasure = false; + Base::Quantity asQuantity; asQuantity.setValue(value); if ( (Type.isValue("Angle")) || @@ -600,7 +626,7 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int asQuantity.setUnit(Base::Unit::Length); } - QString qUserString = asQuantity.getUserString(); // this handles mm to inch/km/parsec etc + QString qUserString = asQuantity.getUserString(); // this handles mm to inch/km/parsec etc // and decimal positions but won't give more than // Global_Decimals precision // really should be able to ask units for value @@ -610,6 +636,9 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int //if it is multi-unit schema, don't even try to use Alt Decimals Base::UnitSystem unitSystem = Base::UnitsApi::getSchema(); + // we need to know what length unit is used by the scheme + std::string BaseLengthUnit = getBaseLengthUnit(unitSystem); + //get formatSpec prefix/suffix/specifier QStringList qsl = getPrefixSuffixSpec(qFormatSpec); QString formatPrefix = qsl[0]; //FormatSpec prefix @@ -619,24 +648,14 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int //handle multi value schemes (yd/ft/in, dms, etc) std::string genPrefix = getPrefix(); //general prefix - diameter, radius, etc QString qMultiValueStr; - QString qGenPrefix = QString::fromUtf8(genPrefix.data(),genPrefix.size()); - if ( (unitSystem == Base::UnitSystem::ImperialBuilding) && - !angularMeasure ) { - multiValueSchema = true; - qMultiValueStr = qUserString; - if (!genPrefix.empty()) { - //qUserString from Quantity includes units - prefix + R + nnn ft + suffix - qMultiValueStr = formatPrefix + qGenPrefix + qUserString + formatSuffix; - } - formattedValue = qMultiValueStr; - } else if ((unitSystem == Base::UnitSystem::ImperialCivil) && - angularMeasure) { + QString qGenPrefix = QString::fromUtf8(genPrefix.data(), genPrefix.size()); + if ((unitSystem == Base::UnitSystem::ImperialCivil) && angularMeasure) { QString dispMinute = QString::fromUtf8("\'"); QString dispSecond = QString::fromUtf8("\""); QString schemeMinute = QString::fromUtf8("M"); QString schemeSecond = QString::fromUtf8("S"); - QString displaySub = qUserString.replace(schemeMinute,dispMinute); - displaySub = displaySub.replace(schemeSecond,dispSecond); + QString displaySub = qUserString.replace(schemeMinute, dispMinute); + displaySub = displaySub.replace(schemeSecond, dispSecond); multiValueSchema = true; qMultiValueStr = displaySub; if (!genPrefix.empty()) { @@ -645,85 +664,119 @@ std::string DrawViewDimension::formatValue(qreal value, QString qFormatSpec, int } formattedValue = qMultiValueStr; } else { - //handle single value schemes if (formatSpecifier.isEmpty()) { Base::Console().Warning("Warning - no numeric format in formatSpec %s - %s\n", qPrintable(qFormatSpec), getNameInDocument()); return Base::Tools::toStdString(qFormatSpec); } - QRegExp rxUnits(QString::fromUtf8(" \\D*$")); //space + any non digits at end of string - QString userVal = qUserString; - userVal.remove(rxUnits); //getUserString(defaultDecimals) without units - - QLocale loc; - double userValNum = loc.toDouble(userVal); + // for older TD drawings the formatSpecifier "%g" was used, but the number of decimals was + // neverheless limited. To keep old drawings, we limit the number of decimals too + // if the TD preferences option to use the global decimal number is set + // the formatSpecifier can have a prefix and/or suffix + if (useDecimals() && formatSpecifier.contains(QString::fromLatin1("%g"), Qt::CaseInsensitive)) { + int globalPrecision = Base::UnitsApi::getDecimals(); + // change formatSpecifier to e.g. "%.2f" + QString newSpecifier = QString::fromStdString("%." + std::to_string(globalPrecision) + "f"); + formatSpecifier.replace(QString::fromLatin1("%g"), newSpecifier, Qt::CaseInsensitive); + } + + // qUserString is the value + unit with default decimals, so extract the unit + // we cannot just use unit.getString() because this would convert '°' to 'deg' + QRegExp rxUnits(QString::fromUtf8(" \\D*$")); // space + any non digits at end of string int pos = 0; - if ((pos = rxUnits.indexIn(qUserString, 0)) != -1) { - qUserStringUnits = rxUnits.cap(0); //entire capture - non numerics at end of qUserString + if ((pos = rxUnits.indexIn(qUserString, 0)) != -1) { + qUserStringUnits = rxUnits.cap(0); // entire capture - non numerics at end of qUserString } - formattedValue = userVal; //sensible default - #if QT_VERSION >= 0x050000 - formattedValue = QString::asprintf(Base::Tools::toStdString(formatSpecifier).c_str(),userValNum); - #else + + // we can have 2 possible results: + // - the value in the base unit but without displayed unit + // - the value + unit (not necessarily the base unit!) + // the user can overwrite the decimal settings, so we must in every case use the formatSpecifier + // if useDecimals(), then formatSpecifier = global decimals, otherwise it is %.2f + QLocale loc; + double userVal; + bool checkDecimals = true; + if (showUnits() || (Type.isValue("Angle")) || (Type.isValue("Angle3Pt"))) { + formattedValue = qUserString; // result value + unit (not necessarily base unit!) + // remove unit + formattedValue.remove(rxUnits); + // to number + userVal = loc.toDouble(formattedValue); + if (userVal >= 1.0) + // we can assure we didn't make an error > 10% via getUserString() + checkDecimals = false; + } + if (checkDecimals){ + // get value in the base unit with default decimals + // for the conversion we use the same method as in DlgUnitsCalculator::valueChanged + // get the conversion factor for the unit + double convertValue = Base::Quantity::parse(QString::fromLatin1("1") + QString::fromStdString(BaseLengthUnit)).getValue(); + // the result is now just val / convertValue because val is always in the base unit + userVal = asQuantity.getValue() / convertValue; + } + // we reformat the value +#if QT_VERSION >= 0x050000 + formattedValue = QString::asprintf(Base::Tools::toStdString(formatSpecifier).c_str(), userVal); +#else QString qs2; - formattedValue = qs2.sprintf(Base::Tools::toStdString(formatSpecifier).c_str(),userValNum); - #endif - - QString repl = userVal; - if (useDecimals()) { - if (showUnits() || (Type.isValue("Angle")) ||(Type.isValue("Angle3Pt")) ) { - repl = qUserString; - } else { - repl = userVal; - } - } else { - if (showUnits() || (Type.isValue("Angle")) || (Type.isValue("Angle3Pt"))) { - repl = formattedValue + qUserStringUnits; - } else { - repl = formattedValue; - } + formattedValue = qs2.sprintf(Base::Tools::toStdString(formatSpecifier).c_str(), userVal); +#endif + // if abs(1 - userVal / formattedValue) > 0.1 we know that we make an error greater than 10% + // then we need more digits + if (abs(userVal - formattedValue.toDouble()) > 0.1 * userVal) { + int i = 1; + do { // increase decimals step by step until error is < 10 % + formattedValue = QLocale().toString(userVal, 'f', i); + ++i; + } while (abs(userVal - loc.toDouble(formattedValue)) > 0.1 * userVal); + // We purposely don't reset the formatSpecifier. + // Why "%.1f" is overwritten for a value of e.g. "0.001" is obvious, + // moreover such cases only occurs when + // changing unit schemes on existing drawings. Moreover a typical case is that + // you accidentally used e.g. a building scheme, see your mistake and go back + // then you would end up with e.g. "%.5f" and must manually correct this. } - qFormatSpec.replace(formatSpecifier,repl); - //this next bit is so inelegant!!! + // replace decimal sign if necessary QChar dp = QChar::fromLatin1('.'); if (loc.decimalPoint() != dp) { - qFormatSpec.replace(dp,loc.decimalPoint()); - formattedValue.replace(dp,loc.decimalPoint()); - } - //Remove space between dimension and degree sign - if ((Type.isValue("Angle")) || (Type.isValue("Angle3Pt"))) { - QRegExp space(QString::fromUtf8("\\s")); - qFormatSpec.remove(space); + formattedValue.replace(dp, loc.decimalPoint()); } } - //formattedValue - formatted numeric value - //qUserStringUnits - unit abbrev - //qFormatSpec - prefix + formattedValue w/units + suffix - //partial = 0 --> prefix + formattedValue w/units +suffix - // prefix 4' 11" suffix - result = qFormatSpec.toUtf8().constData(); + if ((unitSystem == Base::UnitSystem::ImperialBuilding) && + !angularMeasure) { + multiValueSchema = true; + qMultiValueStr = formattedValue; + if (!genPrefix.empty()) { + //qUserString from Quantity includes units - prefix + R + nnn ft + suffix + qMultiValueStr = formatPrefix + qGenPrefix + qUserString + formatSuffix; + } - std::string ssPrefix = Base::Tools::toStdString(formatPrefix); - std::string ssSuffix = Base::Tools::toStdString(formatSuffix); - std::string ssUnits = Base::Tools::toStdString(qUserStringUnits); - if (multiValueSchema) { - result = ssPrefix + - Base::Tools::toStdString(qMultiValueStr) + - ssSuffix + - ssUnits; + formattedValue = qMultiValueStr; } - if (partial == 1) { //prefix number suffix - result = ssPrefix + - Base::Tools::toStdString(formattedValue) + - ssSuffix; - } else if (partial == 2) { //just the unit + result = formattedValue.toStdString(); + + if (partial == 0) { // then also multiValueSchema is true + result = Base::Tools::toStdString(formatPrefix) + + Base::Tools::toStdString(qMultiValueStr) + + Base::Tools::toStdString(formatSuffix) + + Base::Tools::toStdString(qUserStringUnits); + } + else if (partial == 1) { // prefix number suffix + result = Base::Tools::toStdString(formatPrefix) + + result + + Base::Tools::toStdString(formatSuffix); + } + else if (partial == 2) { // just the unit if ((Type.isValue("Angle")) || (Type.isValue("Angle3Pt"))) { - QRegExp space(QString::fromUtf8("\\s")); - qUserStringUnits.remove(space); + // remove space between dimension and unit if unit is not "deg" + if ( !qUserStringUnits.contains(QString::fromLatin1("deg")) ) { + QRegExp space(QString::fromUtf8("\\s")); + qUserStringUnits.remove(space); + } result = Base::Tools::toStdString(qUserStringUnits); } else if (showUnits()) { result = Base::Tools::toStdString(qUserStringUnits); @@ -759,7 +812,6 @@ std::pair DrawViewDimension::getFormattedToleranceValu std::string DrawViewDimension::getFormattedDimensionValue(int partial) { -// Base::Console().Message("DVD::getFormattedValue(%d)\n", partial); QString qFormatSpec = QString::fromUtf8(FormatSpec.getStrValue().data()); if (Arbitrary.getValue()) { @@ -774,9 +826,8 @@ QStringList DrawViewDimension::getPrefixSuffixSpec(QString fSpec) QStringList result; QString formatPrefix; QString formatSuffix; - QString formatted; //find the %x.y tag in FormatSpec - QRegExp rxFormat(QString::fromUtf8("%[+-]?[0-9]*\\.*[0-9]*[aefgAEFG]")); //printf double format spec + QRegExp rxFormat(QString::fromUtf8("%[+-]?[0-9]*\\.*[0-9]*[aefgAEFG]")); //printf double format spec QString match; int pos = 0; if ((pos = rxFormat.indexIn(fSpec, 0)) != -1) { diff --git a/src/Mod/TechDraw/App/DrawViewDimension.h b/src/Mod/TechDraw/App/DrawViewDimension.h index 75c4abfae9..865c2601a4 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.h +++ b/src/Mod/TechDraw/App/DrawViewDimension.h @@ -27,6 +27,7 @@ # include # include # include +# include #include "DrawView.h" @@ -157,6 +158,8 @@ public: bool isMultiValueSchema(void) const; + std::string getBaseLengthUnit(Base::UnitSystem system); + pointPair getArrowPositions(void); void saveArrowPositions(const Base::Vector2d positions[]); diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui index 67ec8c0c8b..e6ba0f06d2 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui @@ -112,6 +112,11 @@ 0 + + + true + + Use system setting for number of decimals