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