Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsbasicnumericformat.cpp 3 : : ---------------------------- 4 : : begin : January 2020 5 : : copyright : (C) 2020 by Nyall Dawson 6 : : email : nyall dot dawson at gmail dot com 7 : : 8 : : *************************************************************************** 9 : : * * 10 : : * This program is free software; you can redistribute it and/or modify * 11 : : * it under the terms of the GNU General Public License as published by * 12 : : * the Free Software Foundation; either version 2 of the License, or * 13 : : * (at your option) any later version. * 14 : : * * 15 : : ***************************************************************************/ 16 : : 17 : : #include "qgsbasicnumericformat.h" 18 : : #include "qgis.h" 19 : : #include <memory> 20 : : #include <iostream> 21 : : #include <locale> 22 : : #include <iomanip> 23 : : 24 : 0 : struct formatter : std::numpunct<wchar_t> 25 : : { 26 : 0 : formatter( QChar thousands, bool showThousands, QChar decimal ) 27 : 0 : : mThousands( thousands.unicode() ) 28 : 0 : , mDecimal( decimal.unicode() ) 29 : 0 : , mShowThousands( showThousands ) 30 : 0 : {} 31 : 0 : wchar_t do_decimal_point() const override { return mDecimal; } 32 : 0 : wchar_t do_thousands_sep() const override { return mThousands; } 33 : 0 : std::string do_grouping() const override { return mShowThousands ? "\3" : "\0"; } 34 : : 35 : : wchar_t mThousands; 36 : : wchar_t mDecimal; 37 : : bool mShowThousands = true; 38 : : }; 39 : : 40 : 38 : QgsBasicNumericFormat::QgsBasicNumericFormat() 41 : 76 : { 42 : 38 : } 43 : : 44 : 5 : QString QgsBasicNumericFormat::id() const 45 : : { 46 : 10 : return QStringLiteral( "basic" ); 47 : : } 48 : : 49 : 0 : QString QgsBasicNumericFormat::visibleName() const 50 : : { 51 : 0 : return QObject::tr( "Number" ); 52 : : } 53 : : 54 : 0 : int QgsBasicNumericFormat::sortKey() 55 : : { 56 : 0 : return 1; 57 : : } 58 : : 59 : 0 : QString QgsBasicNumericFormat::formatDouble( double value, const QgsNumericFormatContext &context ) const 60 : : { 61 : 0 : QChar decimal = mDecimalSeparator.isNull() ? context.decimalSeparator() : mDecimalSeparator; 62 : 0 : std::basic_stringstream<wchar_t> os; 63 : 0 : os.imbue( std::locale( os.getloc(), new formatter( mThousandsSeparator.isNull() ? context.thousandsSeparator() : mThousandsSeparator, 64 : 0 : mShowThousandsSeparator, 65 : 0 : decimal ) ) ); 66 : : 67 : 0 : if ( !mUseScientific ) 68 : : { 69 : 0 : switch ( mRoundingType ) 70 : : { 71 : : case DecimalPlaces: 72 : 0 : os << std::fixed << std::setprecision( mNumberDecimalPlaces ); 73 : 0 : os << value; 74 : 0 : break; 75 : : 76 : : case SignificantFigures: 77 : : { 78 : 0 : if ( qgsDoubleNear( value, 0 ) ) 79 : : { 80 : 0 : os << std::fixed << std::setprecision( mNumberDecimalPlaces - 1 ) << value; 81 : 0 : } 82 : : else 83 : : { 84 : : // digits before decimal point 85 : 0 : const int d = std::floor( std::log10( value < 0 ? -value : value ) ) + 1; 86 : 0 : double order = std::pow( 10.0, mNumberDecimalPlaces - d ); 87 : 0 : os << std::fixed << std::setprecision( std::max( mNumberDecimalPlaces - d, 0 ) ) << std::round( value * order ) / order; 88 : : } 89 : 0 : break; 90 : : } 91 : : } 92 : 0 : } 93 : : else 94 : : { 95 : 0 : os << std::scientific << std::setprecision( mNumberDecimalPlaces ); 96 : 0 : os << value; 97 : : } 98 : : 99 : 0 : QString res = QString::fromStdWString( os.str() ); 100 : : 101 : 0 : if ( mShowPlusSign && value > 0 ) 102 : 0 : res.prepend( context.positiveSign() ); 103 : : 104 : 0 : if ( !mShowTrailingZeros && res.contains( decimal ) ) 105 : : { 106 : 0 : int trimPoint = res.length() - 1; 107 : 0 : int ePoint = 0; 108 : 0 : if ( mUseScientific ) 109 : : { 110 : 0 : while ( res.at( trimPoint ).toUpper() != context.exponential().toUpper() ) 111 : 0 : trimPoint--; 112 : 0 : ePoint = trimPoint; 113 : 0 : trimPoint--; 114 : 0 : } 115 : : 116 : 0 : while ( res.at( trimPoint ) == context.zeroDigit() ) 117 : 0 : trimPoint--; 118 : : 119 : 0 : if ( res.at( trimPoint ) == decimal ) 120 : 0 : trimPoint--; 121 : : 122 : 0 : QString original = res; 123 : 0 : res.truncate( trimPoint + 1 ); 124 : 0 : if ( mUseScientific ) 125 : 0 : res += original.mid( ePoint ); 126 : 0 : } 127 : : 128 : 0 : return res; 129 : 0 : } 130 : : 131 : 0 : QgsNumericFormat *QgsBasicNumericFormat::clone() const 132 : : { 133 : 0 : return new QgsBasicNumericFormat( *this ); 134 : : } 135 : : 136 : 0 : QgsNumericFormat *QgsBasicNumericFormat::create( const QVariantMap &configuration, const QgsReadWriteContext &context ) const 137 : : { 138 : 0 : std::unique_ptr< QgsBasicNumericFormat > res = std::make_unique< QgsBasicNumericFormat >(); 139 : 0 : res->setConfiguration( configuration, context ); 140 : 0 : return res.release(); 141 : 0 : } 142 : : 143 : 0 : QVariantMap QgsBasicNumericFormat::configuration( const QgsReadWriteContext & ) const 144 : : { 145 : 0 : QVariantMap res; 146 : 0 : res.insert( QStringLiteral( "decimals" ), mNumberDecimalPlaces ); 147 : 0 : res.insert( QStringLiteral( "show_thousand_separator" ), mShowThousandsSeparator ); 148 : 0 : res.insert( QStringLiteral( "show_plus" ), mShowPlusSign ); 149 : 0 : res.insert( QStringLiteral( "show_trailing_zeros" ), mShowTrailingZeros ); 150 : 0 : res.insert( QStringLiteral( "rounding_type" ), static_cast< int >( mRoundingType ) ); 151 : 0 : res.insert( QStringLiteral( "thousand_separator" ), mThousandsSeparator ); 152 : 0 : res.insert( QStringLiteral( "decimal_separator" ), mDecimalSeparator ); 153 : 0 : return res; 154 : 0 : } 155 : : 156 : 8 : void QgsBasicNumericFormat::setConfiguration( const QVariantMap &configuration, const QgsReadWriteContext & ) 157 : : { 158 : 16 : mNumberDecimalPlaces = configuration.value( QStringLiteral( "decimals" ), 6 ).toInt(); 159 : 16 : mShowThousandsSeparator = configuration.value( QStringLiteral( "show_thousand_separator" ), true ).toBool(); 160 : 16 : mShowPlusSign = configuration.value( QStringLiteral( "show_plus" ), false ).toBool(); 161 : 16 : mShowTrailingZeros = configuration.value( QStringLiteral( "show_trailing_zeros" ), false ).toBool(); 162 : 16 : mRoundingType = static_cast< RoundingType >( configuration.value( QStringLiteral( "rounding_type" ), static_cast< int >( DecimalPlaces ) ).toInt() ); 163 : 16 : mThousandsSeparator = configuration.value( QStringLiteral( "thousand_separator" ), QChar() ).toChar(); 164 : 16 : mDecimalSeparator = configuration.value( QStringLiteral( "decimal_separator" ), QChar() ).toChar(); 165 : 8 : } 166 : : 167 : 0 : int QgsBasicNumericFormat::numberDecimalPlaces() const 168 : 38 : { 169 : 0 : return mNumberDecimalPlaces; 170 : : } 171 : : 172 : 43 : void QgsBasicNumericFormat::setNumberDecimalPlaces( int numberDecimalPlaces ) 173 : 38 : { 174 : 43 : mNumberDecimalPlaces = numberDecimalPlaces; 175 : 43 : } 176 : : 177 : 38 : bool QgsBasicNumericFormat::showThousandsSeparator() const 178 : : { 179 : 0 : return mShowThousandsSeparator; 180 : : } 181 : : 182 : 0 : void QgsBasicNumericFormat::setShowThousandsSeparator( bool showThousandsSeparator ) 183 : : { 184 : 0 : mShowThousandsSeparator = showThousandsSeparator; 185 : 0 : } 186 : : 187 : 0 : bool QgsBasicNumericFormat::showPlusSign() const 188 : : { 189 : 0 : return mShowPlusSign; 190 : : } 191 : : 192 : 0 : void QgsBasicNumericFormat::setShowPlusSign( bool showPlusSign ) 193 : : { 194 : 0 : mShowPlusSign = showPlusSign; 195 : 0 : } 196 : : 197 : 0 : bool QgsBasicNumericFormat::showTrailingZeros() const 198 : : { 199 : 0 : return mShowTrailingZeros; 200 : : } 201 : : 202 : 5 : void QgsBasicNumericFormat::setShowTrailingZeros( bool showTrailingZeros ) 203 : : { 204 : 5 : mShowTrailingZeros = showTrailingZeros; 205 : 5 : } 206 : : 207 : 0 : QgsBasicNumericFormat::RoundingType QgsBasicNumericFormat::roundingType() const 208 : : { 209 : 0 : return mRoundingType; 210 : : } 211 : : 212 : 0 : void QgsBasicNumericFormat::setRoundingType( QgsBasicNumericFormat::RoundingType type ) 213 : : { 214 : 0 : mRoundingType = type; 215 : 0 : } 216 : : 217 : 0 : QChar QgsBasicNumericFormat::thousandsSeparator() const 218 : : { 219 : 0 : return mThousandsSeparator; 220 : : } 221 : : 222 : 0 : void QgsBasicNumericFormat::setThousandsSeparator( QChar character ) 223 : : { 224 : 0 : mThousandsSeparator = character; 225 : 0 : } 226 : : 227 : 0 : QChar QgsBasicNumericFormat::decimalSeparator() const 228 : : { 229 : 0 : return mDecimalSeparator; 230 : : } 231 : : 232 : 0 : void QgsBasicNumericFormat::setDecimalSeparator( QChar character ) 233 : : { 234 : 0 : mDecimalSeparator = character; 235 : 0 : }