Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsfractionnumericformat.cpp 3 : : ---------------------------- 4 : : begin : March 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 "qgsfractionnumericformat.h" 18 : : #include "qgis.h" 19 : : #include <QString> 20 : : #include <memory> 21 : : #include <iostream> 22 : : #include <locale> 23 : : #include <sstream> 24 : : #include <iomanip> 25 : : 26 : 0 : struct formatter : std::numpunct<wchar_t> 27 : : { 28 : 0 : formatter( QChar thousands, bool showThousands, QChar decimal ) 29 : 0 : : mThousands( thousands.unicode() ) 30 : 0 : , mDecimal( decimal.unicode() ) 31 : 0 : , mShowThousands( showThousands ) 32 : 0 : {} 33 : 0 : wchar_t do_decimal_point() const override { return mDecimal; } 34 : 0 : wchar_t do_thousands_sep() const override { return mThousands; } 35 : 0 : std::string do_grouping() const override { return mShowThousands ? "\3" : "\0"; } 36 : : 37 : : wchar_t mThousands; 38 : : wchar_t mDecimal; 39 : : bool mShowThousands = true; 40 : : }; 41 : : 42 : 5 : QgsFractionNumericFormat::QgsFractionNumericFormat() 43 : 10 : { 44 : 5 : } 45 : : 46 : 5 : QString QgsFractionNumericFormat::id() const 47 : : { 48 : 10 : return QStringLiteral( "fraction" ); 49 : : } 50 : : 51 : 0 : QString QgsFractionNumericFormat::visibleName() const 52 : : { 53 : 0 : return QObject::tr( "Fraction" ); 54 : : } 55 : : 56 : 0 : int QgsFractionNumericFormat::sortKey() 57 : : { 58 : 0 : return 100; 59 : : } 60 : : 61 : 0 : QString QgsFractionNumericFormat::formatDouble( double value, const QgsNumericFormatContext &context ) const 62 : : { 63 : 0 : std::basic_stringstream<wchar_t> os; 64 : 0 : os.imbue( std::locale( os.getloc(), new formatter( mThousandsSeparator.isNull() ? context.thousandsSeparator() : mThousandsSeparator, 65 : 0 : mShowThousandsSeparator, 66 : 0 : context.decimalSeparator() ) ) ); 67 : : 68 : : unsigned long long num; 69 : : unsigned long long den; 70 : : int sign; 71 : : 72 : 0 : QString res; 73 : : 74 : 0 : const double fixed = std::floor( std::fabs( value ) ); 75 : 0 : bool success = doubleToVulgarFraction( std::fabs( value ) - fixed, num, den, sign ); 76 : 0 : if ( success ) 77 : : { 78 : 0 : if ( mUseDedicatedUnicode && num == 1 && den == 2 ) 79 : 0 : res = QChar( 0xBD ); //½ 80 : 0 : else if ( mUseDedicatedUnicode && num == 1 && den == 3 ) 81 : 0 : res = QChar( 0x2153 ); //⅓ 82 : 0 : else if ( mUseDedicatedUnicode && num == 2 && den == 3 ) 83 : 0 : res = QChar( 0x2154 ); //⅔ 84 : 0 : else if ( mUseDedicatedUnicode && num == 1 && den == 4 ) 85 : 0 : res = QChar( 0xBC ); //¼ 86 : 0 : else if ( mUseDedicatedUnicode && num == 3 && den == 4 ) 87 : 0 : res = QChar( 0xBE ); //¾ 88 : 0 : else if ( mUseDedicatedUnicode && num == 1 && den == 5 ) 89 : 0 : res = QChar( 0x2155 ); //⅕ 90 : 0 : else if ( mUseDedicatedUnicode && num == 2 && den == 5 ) 91 : 0 : res = QChar( 0x2156 ); //⅖ 92 : 0 : else if ( mUseDedicatedUnicode && num == 3 && den == 5 ) 93 : 0 : res = QChar( 0x2157 ); //⅗ 94 : 0 : else if ( mUseDedicatedUnicode && num == 4 && den == 5 ) 95 : 0 : res = QChar( 0x2158 ); //⅘ 96 : 0 : else if ( mUseDedicatedUnicode && num == 1 && den == 6 ) 97 : 0 : res = QChar( 0x2159 ); //⅙ 98 : 0 : else if ( mUseDedicatedUnicode && num == 5 && den == 6 ) 99 : 0 : res = QChar( 0x215A ); //⅚ 100 : 0 : else if ( mUseDedicatedUnicode && num == 1 && den == 7 ) 101 : 0 : res = QChar( 0x2150 ); //⅐ 102 : 0 : else if ( mUseDedicatedUnicode && num == 1 && den == 8 ) 103 : 0 : res = QChar( 0x215B ); //⅛ 104 : 0 : else if ( mUseDedicatedUnicode && num == 3 && den == 8 ) 105 : 0 : res = QChar( 0x215C ); //⅜ 106 : 0 : else if ( mUseDedicatedUnicode && num == 5 && den == 8 ) 107 : 0 : res = QChar( 0x215D ); //⅝ 108 : 0 : else if ( mUseDedicatedUnicode && num == 7 && den == 8 ) 109 : 0 : res = QChar( 0x215E ); //⅞ 110 : 0 : else if ( mUseDedicatedUnicode && num == 1 && den == 9 ) 111 : 0 : res = QChar( 0x2151 ); //⅑ 112 : 0 : else if ( mUseDedicatedUnicode && num == 1 && den == 10 ) 113 : 0 : res = QChar( 0x2152 ); //⅒ 114 : 0 : else if ( mUseUnicodeSuperSubscript ) 115 : 0 : res = num == 0 ? QString() : QStringLiteral( "%1%2%3" ).arg( toUnicodeSuperscript( QString::number( num ) ), 116 : 0 : QChar( 0x002F ), // "SOLIDUS" character 117 : 0 : toUnicodeSubscript( QString::number( den ) ) ); 118 : : else 119 : 0 : res = num == 0 ? QString() : QStringLiteral( "%2/%3" ).arg( num ).arg( den ); 120 : 0 : if ( fixed ) 121 : : { 122 : 0 : os << std::fixed << std::setprecision( 0 ); 123 : 0 : os << fixed; 124 : 0 : res.prepend( QString::fromStdWString( os.str() ) + ' ' ); 125 : 0 : res = res.trimmed(); 126 : 0 : } 127 : 0 : if ( res.isEmpty() ) 128 : 0 : res = QString::number( 0 ); 129 : : 130 : 0 : if ( value < 0 ) 131 : 0 : res.prepend( context.negativeSign() ); 132 : 0 : } 133 : : else 134 : : { 135 : 0 : os << std::fixed << std::setprecision( 10 ); 136 : 0 : os << value; 137 : 0 : res = QString::fromStdWString( os.str() ); 138 : : } 139 : : 140 : 0 : if ( value > 0 && mShowPlusSign ) 141 : : { 142 : 0 : res.prepend( context.positiveSign() ); 143 : 0 : } 144 : : 145 : 0 : return res; 146 : 0 : } 147 : : 148 : 0 : QgsNumericFormat *QgsFractionNumericFormat::clone() const 149 : : { 150 : 0 : return new QgsFractionNumericFormat( *this ); 151 : : } 152 : : 153 : 0 : QgsNumericFormat *QgsFractionNumericFormat::create( const QVariantMap &configuration, const QgsReadWriteContext &context ) const 154 : : { 155 : 0 : std::unique_ptr< QgsFractionNumericFormat > res = std::make_unique< QgsFractionNumericFormat >(); 156 : 0 : res->setConfiguration( configuration, context ); 157 : 0 : return res.release(); 158 : 0 : } 159 : : 160 : 0 : QVariantMap QgsFractionNumericFormat::configuration( const QgsReadWriteContext & ) const 161 : : { 162 : 0 : QVariantMap res; 163 : 0 : res.insert( QStringLiteral( "show_thousand_separator" ), mShowThousandsSeparator ); 164 : 0 : res.insert( QStringLiteral( "show_plus" ), mShowPlusSign ); 165 : 0 : res.insert( QStringLiteral( "thousand_separator" ), mThousandsSeparator ); 166 : 0 : res.insert( QStringLiteral( "use_dedicated_unicode" ), mUseDedicatedUnicode ); 167 : 0 : res.insert( QStringLiteral( "use_unicode_supersubscript" ), mUseUnicodeSuperSubscript ); 168 : 0 : return res; 169 : 0 : } 170 : : 171 : 0 : double QgsFractionNumericFormat::suggestSampleValue() const 172 : : { 173 : 0 : return 1234.75; 174 : : } 175 : : 176 : 0 : bool QgsFractionNumericFormat::useDedicatedUnicodeCharacters() const 177 : : { 178 : 0 : return mUseDedicatedUnicode; 179 : : } 180 : : 181 : 5 : void QgsFractionNumericFormat::setUseDedicatedUnicodeCharacters( bool enabled ) 182 : 5 : { 183 : 5 : mUseDedicatedUnicode = enabled; 184 : 5 : } 185 : : 186 : 0 : bool QgsFractionNumericFormat::useUnicodeSuperSubscript() const 187 : : { 188 : 0 : return mUseUnicodeSuperSubscript; 189 : : } 190 : : 191 : 0 : void QgsFractionNumericFormat::setUseUnicodeSuperSubscript( bool enabled ) 192 : : { 193 : 0 : mUseUnicodeSuperSubscript = enabled; 194 : 0 : } 195 : : 196 : 0 : void QgsFractionNumericFormat::setConfiguration( const QVariantMap &configuration, const QgsReadWriteContext & ) 197 : : { 198 : 0 : mShowThousandsSeparator = configuration.value( QStringLiteral( "show_thousand_separator" ), true ).toBool(); 199 : 0 : mShowPlusSign = configuration.value( QStringLiteral( "show_plus" ), false ).toBool(); 200 : 0 : mThousandsSeparator = configuration.value( QStringLiteral( "thousand_separator" ), QChar() ).toChar(); 201 : 0 : mUseDedicatedUnicode = configuration.value( QStringLiteral( "use_dedicated_unicode" ), false ).toBool(); 202 : 0 : mUseUnicodeSuperSubscript = configuration.value( QStringLiteral( "use_unicode_supersubscript" ), true ).toBool(); 203 : 0 : } 204 : : 205 : 0 : bool QgsFractionNumericFormat::showThousandsSeparator() const 206 : : { 207 : 0 : return mShowThousandsSeparator; 208 : : } 209 : : 210 : 0 : void QgsFractionNumericFormat::setShowThousandsSeparator( bool showThousandsSeparator ) 211 : : { 212 : 0 : mShowThousandsSeparator = showThousandsSeparator; 213 : 0 : } 214 : : 215 : 0 : bool QgsFractionNumericFormat::showPlusSign() const 216 : : { 217 : 0 : return mShowPlusSign; 218 : : } 219 : : 220 : 0 : void QgsFractionNumericFormat::setShowPlusSign( bool showPlusSign ) 221 : : { 222 : 0 : mShowPlusSign = showPlusSign; 223 : 0 : } 224 : : 225 : 0 : QChar QgsFractionNumericFormat::thousandsSeparator() const 226 : : { 227 : 0 : return mThousandsSeparator; 228 : : } 229 : : 230 : 0 : void QgsFractionNumericFormat::setThousandsSeparator( QChar character ) 231 : : { 232 : 0 : mThousandsSeparator = character; 233 : 0 : } 234 : : 235 : 0 : QString QgsFractionNumericFormat::toUnicodeSuperscript( const QString &input ) 236 : : { 237 : 0 : QString res = input; 238 : 0 : for ( int i = 0; i < input.size(); ++i ) 239 : : { 240 : 0 : QChar c = input.at( i ); 241 : 0 : if ( c == '0' ) 242 : 0 : res[i] = QChar( 0x2070 ); //⁰ 243 : 0 : else if ( c == '1' ) 244 : 0 : res[i] = QChar( 0x00B9 ); //¹ 245 : 0 : else if ( c == '2' ) 246 : 0 : res[i] = QChar( 0x00B2 ); //² 247 : 0 : else if ( c == '3' ) 248 : 0 : res[i] = QChar( 0x00B3 ); //³ 249 : 0 : else if ( c == '4' ) 250 : 0 : res[i] = QChar( 0x2074 ); //⁴ 251 : 0 : else if ( c == '5' ) 252 : 0 : res[i] = QChar( 0x2075 ); //⁵ 253 : 0 : else if ( c == '6' ) 254 : 0 : res[i] = QChar( 0x2076 ); //⁶ 255 : 0 : else if ( c == '7' ) 256 : 0 : res[i] = QChar( 0x2077 ); //⁷ 257 : 0 : else if ( c == '8' ) 258 : 0 : res[i] = QChar( 0x2078 ); //⁸ 259 : 0 : else if ( c == '9' ) 260 : 0 : res[i] = QChar( 0x2079 ); //⁹ 261 : 0 : } 262 : 0 : return res; 263 : 0 : } 264 : : 265 : 0 : QString QgsFractionNumericFormat::toUnicodeSubscript( const QString &input ) 266 : : { 267 : 0 : QString res = input; 268 : 0 : for ( int i = 0; i < input.size(); ++i ) 269 : : { 270 : 0 : QChar c = input.at( i ); 271 : 0 : if ( c == '0' ) 272 : 0 : res[i] = QChar( 0x2080 ); //₀ 273 : 0 : else if ( c == '1' ) 274 : 0 : res[i] = QChar( 0x2081 ); //₁ 275 : 0 : else if ( c == '2' ) 276 : 0 : res[i] = QChar( 0x2082 ); //₂ 277 : 0 : else if ( c == '3' ) 278 : 0 : res[i] = QChar( 0x2083 ); //₃ 279 : 0 : else if ( c == '4' ) 280 : 0 : res[i] = QChar( 0x2084 ); //₄ 281 : 0 : else if ( c == '5' ) 282 : 0 : res[i] = QChar( 0x2085 ); //₅ 283 : 0 : else if ( c == '6' ) 284 : 0 : res[i] = QChar( 0x2086 ); //₆ 285 : 0 : else if ( c == '7' ) 286 : 0 : res[i] = QChar( 0x2087 ); //₇ 287 : 0 : else if ( c == '8' ) 288 : 0 : res[i] = QChar( 0x2088 ); //₈ 289 : 0 : else if ( c == '9' ) 290 : 0 : res[i] = QChar( 0x2089 ); //₉ 291 : 0 : } 292 : 0 : return res; 293 : 0 : }