QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsfractionnumericformat.cpp
Go to the documentation of this file.
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
18#include "qgis.h"
19#include <QString>
20#include <memory>
21#include <iostream>
22#include <locale>
23#include <sstream>
24#include <iomanip>
25
26struct formatter : std::numpunct<wchar_t>
27{
28 formatter( QChar thousands, bool showThousands, QChar decimal )
29 : mThousands( thousands.unicode() )
30 , mDecimal( decimal.unicode() )
31 , mShowThousands( showThousands )
32 {}
33 wchar_t do_decimal_point() const override { return mDecimal; }
34 wchar_t do_thousands_sep() const override { return mThousands; }
35 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
43{
44}
45
47{
48 return QStringLiteral( "fraction" );
49}
50
52{
53 return QObject::tr( "Fraction" );
54}
55
57{
58 return 100;
59}
60
61QString QgsFractionNumericFormat::formatDouble( double value, const QgsNumericFormatContext &context ) const
62{
63 std::basic_stringstream<wchar_t> os;
64 os.imbue( std::locale( os.getloc(), new formatter( mThousandsSeparator.isNull() ? context.thousandsSeparator() : mThousandsSeparator,
65 mShowThousandsSeparator,
66 context.decimalSeparator() ) ) );
67
68 unsigned long long num;
69 unsigned long long den;
70 int sign;
71
72 QString res;
73
74 const double fixed = std::floor( std::fabs( value ) );
75 const bool success = doubleToVulgarFraction( std::fabs( value ) - fixed, num, den, sign );
76 if ( success )
77 {
78 if ( mUseDedicatedUnicode && num == 1 && den == 2 )
79 res = QChar( 0xBD ); //½
80 else if ( mUseDedicatedUnicode && num == 1 && den == 3 )
81 res = QChar( 0x2153 ); //⅓
82 else if ( mUseDedicatedUnicode && num == 2 && den == 3 )
83 res = QChar( 0x2154 ); //⅔
84 else if ( mUseDedicatedUnicode && num == 1 && den == 4 )
85 res = QChar( 0xBC ); //¼
86 else if ( mUseDedicatedUnicode && num == 3 && den == 4 )
87 res = QChar( 0xBE ); //¾
88 else if ( mUseDedicatedUnicode && num == 1 && den == 5 )
89 res = QChar( 0x2155 ); //⅕
90 else if ( mUseDedicatedUnicode && num == 2 && den == 5 )
91 res = QChar( 0x2156 ); //⅖
92 else if ( mUseDedicatedUnicode && num == 3 && den == 5 )
93 res = QChar( 0x2157 ); //⅗
94 else if ( mUseDedicatedUnicode && num == 4 && den == 5 )
95 res = QChar( 0x2158 ); //⅘
96 else if ( mUseDedicatedUnicode && num == 1 && den == 6 )
97 res = QChar( 0x2159 ); //⅙
98 else if ( mUseDedicatedUnicode && num == 5 && den == 6 )
99 res = QChar( 0x215A ); //⅚
100 else if ( mUseDedicatedUnicode && num == 1 && den == 7 )
101 res = QChar( 0x2150 ); //⅐
102 else if ( mUseDedicatedUnicode && num == 1 && den == 8 )
103 res = QChar( 0x215B ); //⅛
104 else if ( mUseDedicatedUnicode && num == 3 && den == 8 )
105 res = QChar( 0x215C ); //⅜
106 else if ( mUseDedicatedUnicode && num == 5 && den == 8 )
107 res = QChar( 0x215D ); //⅝
108 else if ( mUseDedicatedUnicode && num == 7 && den == 8 )
109 res = QChar( 0x215E ); //⅞
110 else if ( mUseDedicatedUnicode && num == 1 && den == 9 )
111 res = QChar( 0x2151 ); //⅑
112 else if ( mUseDedicatedUnicode && num == 1 && den == 10 )
113 res = QChar( 0x2152 ); //⅒
114 else if ( mUseUnicodeSuperSubscript )
115 res = num == 0 ? QString() : QStringLiteral( "%1%2%3" ).arg( toUnicodeSuperscript( QString::number( num ) ),
116 QChar( 0x002F ), // "SOLIDUS" character
117 toUnicodeSubscript( QString::number( den ) ) );
118 else
119 res = num == 0 ? QString() : QStringLiteral( "%2/%3" ).arg( num ).arg( den );
120 if ( fixed )
121 {
122 os << std::fixed << std::setprecision( 0 );
123 os << fixed;
124 res.prepend( QString::fromStdWString( os.str() ) + ' ' );
125 res = res.trimmed();
126 }
127 if ( res.isEmpty() )
128 res = QString::number( 0 );
129
130 if ( value < 0 )
131 res.prepend( context.negativeSign() );
132 }
133 else
134 {
135 os << std::fixed << std::setprecision( 10 );
136 os << value;
137 res = QString::fromStdWString( os.str() );
138 }
139
140 if ( value > 0 && mShowPlusSign )
141 {
142 res.prepend( context.positiveSign() );
143 }
144
145 return res;
146}
147
149{
150 return new QgsFractionNumericFormat( *this );
151}
152
153QgsNumericFormat *QgsFractionNumericFormat::create( const QVariantMap &configuration, const QgsReadWriteContext &context ) const
154{
155 std::unique_ptr< QgsFractionNumericFormat > res = std::make_unique< QgsFractionNumericFormat >();
156 res->setConfiguration( configuration, context );
157 return res.release();
158}
159
161{
162 QVariantMap res;
163 res.insert( QStringLiteral( "show_thousand_separator" ), mShowThousandsSeparator );
164 res.insert( QStringLiteral( "show_plus" ), mShowPlusSign );
165 res.insert( QStringLiteral( "thousand_separator" ), mThousandsSeparator.isNull() ? QVariant() : QVariant::fromValue( mThousandsSeparator ) );
166 res.insert( QStringLiteral( "use_dedicated_unicode" ), mUseDedicatedUnicode );
167 res.insert( QStringLiteral( "use_unicode_supersubscript" ), mUseUnicodeSuperSubscript );
168 return res;
169}
170
172{
173 return 1234.75;
174}
175
177{
178 return mUseDedicatedUnicode;
179}
180
182{
183 mUseDedicatedUnicode = enabled;
184}
185
187{
188 return mUseUnicodeSuperSubscript;
189}
190
192{
193 mUseUnicodeSuperSubscript = enabled;
194}
195
196void QgsFractionNumericFormat::setConfiguration( const QVariantMap &configuration, const QgsReadWriteContext & )
197{
198 mShowThousandsSeparator = configuration.value( QStringLiteral( "show_thousand_separator" ), true ).toBool();
199 mShowPlusSign = configuration.value( QStringLiteral( "show_plus" ), false ).toBool();
200 mThousandsSeparator = configuration.value( QStringLiteral( "thousand_separator" ), QChar() ).toChar();
201 mUseDedicatedUnicode = configuration.value( QStringLiteral( "use_dedicated_unicode" ), false ).toBool();
202 mUseUnicodeSuperSubscript = configuration.value( QStringLiteral( "use_unicode_supersubscript" ), true ).toBool();
203}
204
206{
207 return mShowThousandsSeparator;
208}
209
211{
212 mShowThousandsSeparator = showThousandsSeparator;
213}
214
216{
217 return mShowPlusSign;
218}
219
221{
222 mShowPlusSign = showPlusSign;
223}
224
226{
227 return mThousandsSeparator;
228}
229
231{
232 mThousandsSeparator = character;
233}
234
236{
237 QString res = input;
238 for ( int i = 0; i < input.size(); ++i )
239 {
240 const QChar c = input.at( i );
241 if ( c == '0' )
242 res[i] = QChar( 0x2070 ); //⁰
243 else if ( c == '1' )
244 res[i] = QChar( 0x00B9 ); //¹
245 else if ( c == '2' )
246 res[i] = QChar( 0x00B2 ); //²
247 else if ( c == '3' )
248 res[i] = QChar( 0x00B3 ); //³
249 else if ( c == '4' )
250 res[i] = QChar( 0x2074 ); //⁴
251 else if ( c == '5' )
252 res[i] = QChar( 0x2075 ); //⁵
253 else if ( c == '6' )
254 res[i] = QChar( 0x2076 ); //⁶
255 else if ( c == '7' )
256 res[i] = QChar( 0x2077 ); //⁷
257 else if ( c == '8' )
258 res[i] = QChar( 0x2078 ); //⁸
259 else if ( c == '9' )
260 res[i] = QChar( 0x2079 ); //⁹
261 }
262 return res;
263}
264
265QString QgsFractionNumericFormat::toUnicodeSubscript( const QString &input )
266{
267 QString res = input;
268 for ( int i = 0; i < input.size(); ++i )
269 {
270 const QChar c = input.at( i );
271 if ( c == '0' )
272 res[i] = QChar( 0x2080 ); //₀
273 else if ( c == '1' )
274 res[i] = QChar( 0x2081 ); //₁
275 else if ( c == '2' )
276 res[i] = QChar( 0x2082 ); //₂
277 else if ( c == '3' )
278 res[i] = QChar( 0x2083 ); //₃
279 else if ( c == '4' )
280 res[i] = QChar( 0x2084 ); //₄
281 else if ( c == '5' )
282 res[i] = QChar( 0x2085 ); //₅
283 else if ( c == '6' )
284 res[i] = QChar( 0x2086 ); //₆
285 else if ( c == '7' )
286 res[i] = QChar( 0x2087 ); //₇
287 else if ( c == '8' )
288 res[i] = QChar( 0x2088 ); //₈
289 else if ( c == '9' )
290 res[i] = QChar( 0x2089 ); //₉
291 }
292 return res;
293}
void setUseUnicodeSuperSubscript(bool enabled)
Sets whether unicode superscript and subscript characters should be used, (e.g.
QString visibleName() const override
Returns the translated, user-visible name for this format.
bool useDedicatedUnicodeCharacters() const
Returns true if dedicated unicode characters should be used, when the are available for the particula...
QgsNumericFormat * create(const QVariantMap &configuration, const QgsReadWriteContext &context) const override
Creates a new copy of the format, using the supplied configuration.
QgsNumericFormat * clone() const override
Clones the format, returning a new object.
static QString toUnicodeSubscript(const QString &input)
Converts numbers in an input string to unicode subscript equivalents.
QChar thousandsSeparator() const
Returns any override for the thousands separator character.
void setUseDedicatedUnicodeCharacters(bool enabled)
Sets whether dedicated unicode characters should be used, when the are available for the particular f...
bool showThousandsSeparator() const
Returns true if the thousands grouping separator will be shown.
bool useUnicodeSuperSubscript() const
Returns true if unicode superscript and subscript characters should be used, (e.g.
bool showPlusSign() const
Returns true if a leading plus sign will be shown for positive values.
void setShowPlusSign(bool show)
Sets whether a leading plus sign will be shown for positive values.
int sortKey() override
Returns a sorting key value, where formats with a lower sort key will be shown earlier in lists.
double suggestSampleValue() const override
Returns a suggested sample value which nicely represents the current format configuration.
void setShowThousandsSeparator(bool show)
Sets whether the thousands grouping separator will be shown.
QString id() const override
Returns a unique id for this numeric format.
QgsFractionNumericFormat()
Default constructor.
static bool doubleToVulgarFraction(const double value, unsigned long long &numerator, unsigned long long &denominator, int &sign, const double tolerance=1e-10)
Converts a double value to a vulgar fraction (e.g.
QString formatDouble(double value, const QgsNumericFormatContext &context) const override
Returns a formatted string representation of a numeric double value.
static QString toUnicodeSuperscript(const QString &input)
Converts numbers in an input string to unicode superscript equivalents.
virtual void setConfiguration(const QVariantMap &configuration, const QgsReadWriteContext &context)
Sets the format's configuration.
QVariantMap configuration(const QgsReadWriteContext &context) const override
Returns the current configuration of the formatter.
void setThousandsSeparator(QChar character)
Sets an override character for the thousands separator character.
A context for numeric formats.
QChar negativeSign() const
Returns the negative sign character.
QChar thousandsSeparator() const
Returns the thousands separator character.
QChar decimalSeparator() const
Returns the decimal separator character.
QChar positiveSign() const
Returns the positive sign character.
A numeric formatter allows for formatting a numeric value for display, using a variety of different f...
The class is used as a container of context for various read/write operations on other objects.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
wchar_t do_decimal_point() const override
std::string do_grouping() const override
wchar_t do_thousands_sep() const override
formatter(QChar thousands, bool showThousands, QChar decimal)