QGIS API Documentation  3.19.0-Master (26212d215f)
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 
26 struct 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 
61 QString 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  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 
153 QgsNumericFormat *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 );
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 
196 void 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 
210 void QgsFractionNumericFormat::setShowThousandsSeparator( bool showThousandsSeparator )
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 
235 QString QgsFractionNumericFormat::toUnicodeSuperscript( const QString &input )
236 {
237  QString res = input;
238  for ( int i = 0; i < input.size(); ++i )
239  {
240  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 
265 QString QgsFractionNumericFormat::toUnicodeSubscript( const QString &input )
266 {
267  QString res = input;
268  for ( int i = 0; i < input.size(); ++i )
269  {
270  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)