Line data Source code
1 : // Copyright (c) 2011-2019 The Bitcoin Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <qt/bitcoinunits.h>
6 :
7 : #include <QStringList>
8 :
9 : #include <cassert>
10 :
11 0 : BitcoinUnits::BitcoinUnits(QObject *parent):
12 0 : QAbstractListModel(parent),
13 0 : unitlist(availableUnits())
14 0 : {
15 0 : }
16 :
17 0 : QList<BitcoinUnits::Unit> BitcoinUnits::availableUnits()
18 : {
19 0 : QList<BitcoinUnits::Unit> unitlist;
20 0 : unitlist.append(BTC);
21 0 : unitlist.append(mBTC);
22 0 : unitlist.append(uBTC);
23 0 : unitlist.append(SAT);
24 : return unitlist;
25 0 : }
26 :
27 5 : bool BitcoinUnits::valid(int unit)
28 : {
29 5 : switch(unit)
30 : {
31 : case BTC:
32 : case mBTC:
33 : case uBTC:
34 : case SAT:
35 5 : return true;
36 : default:
37 0 : return false;
38 : }
39 5 : }
40 :
41 0 : QString BitcoinUnits::longName(int unit)
42 : {
43 0 : switch(unit)
44 : {
45 0 : case BTC: return QString("BTC");
46 0 : case mBTC: return QString("mBTC");
47 0 : case uBTC: return QString::fromUtf8("µBTC (bits)");
48 0 : case SAT: return QString("Satoshi (sat)");
49 0 : default: return QString("???");
50 : }
51 0 : }
52 :
53 0 : QString BitcoinUnits::shortName(int unit)
54 : {
55 0 : switch(unit)
56 : {
57 0 : case uBTC: return QString::fromUtf8("bits");
58 0 : case SAT: return QString("sat");
59 0 : default: return longName(unit);
60 : }
61 0 : }
62 :
63 0 : QString BitcoinUnits::description(int unit)
64 : {
65 0 : switch(unit)
66 : {
67 0 : case BTC: return QString("Bitcoins");
68 0 : case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)");
69 0 : case uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
70 0 : case SAT: return QString("Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
71 0 : default: return QString("???");
72 : }
73 0 : }
74 :
75 0 : qint64 BitcoinUnits::factor(int unit)
76 : {
77 0 : switch(unit)
78 : {
79 0 : case BTC: return 100000000;
80 0 : case mBTC: return 100000;
81 0 : case uBTC: return 100;
82 0 : case SAT: return 1;
83 0 : default: return 100000000;
84 : }
85 0 : }
86 :
87 5 : int BitcoinUnits::decimals(int unit)
88 : {
89 5 : switch(unit)
90 : {
91 5 : case BTC: return 8;
92 0 : case mBTC: return 5;
93 0 : case uBTC: return 2;
94 0 : case SAT: return 0;
95 0 : default: return 0;
96 : }
97 5 : }
98 :
99 0 : QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify)
100 : {
101 : // Note: not using straight sprintf here because we do NOT want
102 : // localized number formatting.
103 0 : if(!valid(unit))
104 0 : return QString(); // Refuse to format invalid unit
105 0 : qint64 n = (qint64)nIn;
106 0 : qint64 coin = factor(unit);
107 0 : int num_decimals = decimals(unit);
108 0 : qint64 n_abs = (n > 0 ? n : -n);
109 0 : qint64 quotient = n_abs / coin;
110 0 : QString quotient_str = QString::number(quotient);
111 0 : if (justify) quotient_str = quotient_str.rightJustified(16 - num_decimals, ' ');
112 :
113 : // Use SI-style thin space separators as these are locale independent and can't be
114 : // confused with the decimal marker.
115 0 : QChar thin_sp(THIN_SP_CP);
116 0 : int q_size = quotient_str.size();
117 0 : if (separators == SeparatorStyle::ALWAYS || (separators == SeparatorStyle::STANDARD && q_size > 4))
118 0 : for (int i = 3; i < q_size; i += 3)
119 0 : quotient_str.insert(q_size - i, thin_sp);
120 :
121 0 : if (n < 0)
122 0 : quotient_str.insert(0, '-');
123 0 : else if (fPlus && n > 0)
124 0 : quotient_str.insert(0, '+');
125 :
126 0 : if (num_decimals > 0) {
127 0 : qint64 remainder = n_abs % coin;
128 0 : QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0');
129 0 : return quotient_str + QString(".") + remainder_str;
130 0 : } else {
131 0 : return quotient_str;
132 : }
133 0 : }
134 :
135 :
136 : // NOTE: Using formatWithUnit in an HTML context risks wrapping
137 : // quantities at the thousands separator. More subtly, it also results
138 : // in a standard space rather than a thin space, due to a bug in Qt's
139 : // XML whitespace canonicalisation
140 : //
141 : // Please take care to use formatHtmlWithUnit instead, when
142 : // appropriate.
143 :
144 0 : QString BitcoinUnits::formatWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
145 : {
146 0 : return format(unit, amount, plussign, separators) + QString(" ") + shortName(unit);
147 0 : }
148 :
149 0 : QString BitcoinUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
150 : {
151 0 : QString str(formatWithUnit(unit, amount, plussign, separators));
152 0 : str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML));
153 0 : return QString("<span style='white-space: nowrap;'>%1</span>").arg(str);
154 0 : }
155 :
156 0 : QString BitcoinUnits::formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy)
157 : {
158 0 : assert(amount >= 0);
159 0 : QString value;
160 0 : if (privacy) {
161 0 : value = format(unit, 0, false, separators, true).replace('0', '#');
162 0 : } else {
163 0 : value = format(unit, amount, false, separators, true);
164 : }
165 0 : return value + QString(" ") + shortName(unit);
166 0 : }
167 :
168 5 : bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out)
169 : {
170 5 : if(!valid(unit) || value.isEmpty())
171 0 : return false; // Refuse to parse invalid unit or empty string
172 5 : int num_decimals = decimals(unit);
173 :
174 : // Ignore spaces and thin spaces when parsing
175 5 : QStringList parts = removeSpaces(value).split(".");
176 :
177 5 : if(parts.size() > 2)
178 : {
179 0 : return false; // More than one dot
180 : }
181 5 : QString whole = parts[0];
182 5 : QString decimals;
183 :
184 5 : if(parts.size() > 1)
185 : {
186 3 : decimals = parts[1];
187 3 : }
188 5 : if(decimals.size() > num_decimals)
189 : {
190 0 : return false; // Exceeds max precision
191 : }
192 5 : bool ok = false;
193 5 : QString str = whole + decimals.leftJustified(num_decimals, '0');
194 :
195 5 : if(str.size() > 18)
196 : {
197 0 : return false; // Longer numbers will exceed 63 bits
198 : }
199 5 : CAmount retvalue(str.toLongLong(&ok));
200 5 : if(val_out)
201 : {
202 5 : *val_out = retvalue;
203 5 : }
204 5 : return ok;
205 5 : }
206 :
207 0 : QString BitcoinUnits::getAmountColumnTitle(int unit)
208 : {
209 0 : QString amountTitle = QObject::tr("Amount");
210 0 : if (BitcoinUnits::valid(unit))
211 : {
212 0 : amountTitle += " ("+BitcoinUnits::shortName(unit) + ")";
213 0 : }
214 : return amountTitle;
215 0 : }
216 :
217 0 : int BitcoinUnits::rowCount(const QModelIndex &parent) const
218 : {
219 : Q_UNUSED(parent);
220 0 : return unitlist.size();
221 : }
222 :
223 0 : QVariant BitcoinUnits::data(const QModelIndex &index, int role) const
224 : {
225 0 : int row = index.row();
226 0 : if(row >= 0 && row < unitlist.size())
227 : {
228 0 : Unit unit = unitlist.at(row);
229 0 : switch(role)
230 : {
231 : case Qt::EditRole:
232 : case Qt::DisplayRole:
233 0 : return QVariant(longName(unit));
234 : case Qt::ToolTipRole:
235 0 : return QVariant(description(unit));
236 : case UnitRole:
237 0 : return QVariant(static_cast<int>(unit));
238 : }
239 0 : }
240 0 : return QVariant();
241 0 : }
242 :
243 0 : CAmount BitcoinUnits::maxMoney()
244 : {
245 0 : return MAX_MONEY;
246 : }
|