Bitcoin ABC 0.30.5
P2P Digital Currency
bitcoinunits.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2016 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 <common/args.h>
8#include <consensus/amount.h>
9#include <currencyunit.h>
10
11#include <QStringList>
12
13#include <cassert>
14
15// clang-format off
17 std::map<
19 std::tuple<
20 QString /* longname */,
21 QString /* description */
22 >
23 >;
24static const unitNameMap xecUnits = {
25 {BitcoinUnits::Unit::base,
26 {"XEC",
27 "eCash"}},
28 {BitcoinUnits::Unit::sub,
29 {"Satoshi (sat)",
30 "Satoshi (sat) (1 / 100)"}},
31};
32static const unitNameMap bchUnits = {
33 {BitcoinUnits::Unit::base,
34 {"BCHA",
35 "Bitcoins"}},
36 {BitcoinUnits::Unit::sub,
37 {"Satoshi (sat)",
38 "Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"}},
39};
40// clang-format on
41
43 return gArgs.GetBoolArg("-ecash", DEFAULT_ECASH) ? xecUnits : bchUnits;
44}
45
47 : QAbstractListModel(parent), unitlist(availableUnits()) {}
48
49QList<BitcoinUnits::Unit> BitcoinUnits::availableUnits() {
50 QList<BitcoinUnits::Unit> unitlist;
51 unitlist.append(base);
52 unitlist.append(sub);
53 return unitlist;
54}
55
56bool BitcoinUnits::valid(int unit) {
57 switch (unit) {
58 case base:
59 case sub:
60 return true;
61 default:
62 return false;
63 }
64}
65
66QString BitcoinUnits::longName(int unit) {
67 const auto &units = getUnitsAtRuntime();
68 auto it = units.find(BitcoinUnits::Unit(unit));
69 return it != units.end() ? std::get<0>(it->second) : "???";
70}
71
72QString BitcoinUnits::shortName(int unit) {
73 if (unit == sub) {
74 return QString("sat");
75 }
76 return longName(unit);
77}
78
79QString BitcoinUnits::description(int unit) {
80 const auto &units = getUnitsAtRuntime();
81 auto it = units.find(BitcoinUnits::Unit(unit));
82 return it != units.end() ? std::get<1>(it->second) : "???";
83}
84
86 const auto &currency = Currency::get();
87 switch (unit) {
88 case base:
89 return currency.baseunit;
90 case sub:
91 return currency.subunit;
92 default:
93 assert(false && "non-existent BitcoinUnits::Unit");
94 }
95}
96
98 switch (unit) {
99 case base:
100 return Currency::get().decimals;
101 case sub:
102 return 0;
103 default:
104 assert(false && "non-existent BitcoinUnits::Unit");
105 }
106}
107
108QString BitcoinUnits::format(int unit, const Amount nIn, bool fPlus,
109 SeparatorStyle separators, bool justify) {
110 // Note: not using straight sprintf here because we do NOT want
111 // localized number formatting.
112 if (!valid(unit)) {
113 // Refuse to format invalid unit
114 return QString();
115 }
116 qint64 n = qint64(nIn / SATOSHI);
117 qint64 coin = factor(unit) / SATOSHI;
118 int num_decimals = decimals(unit);
119 qint64 n_abs = (n > 0 ? n : -n);
120 qint64 quotient = n_abs / coin;
121 QString quotient_str = QString::number(quotient);
122 if (justify) {
123 quotient_str = quotient_str.rightJustified(16 - num_decimals, ' ');
124 }
125
126 // Use SI-style thin space separators as these are locale independent and
127 // can't be confused with the decimal marker.
128 QChar thin_sp(THIN_SP_CP);
129 int q_size = quotient_str.size();
130 if (separators == SeparatorStyle::ALWAYS ||
131 (separators == SeparatorStyle::STANDARD && q_size > 4)) {
132 for (int i = 3; i < q_size; i += 3) {
133 quotient_str.insert(q_size - i, thin_sp);
134 }
135 }
136
137 if (n < 0) {
138 quotient_str.insert(0, '-');
139 } else if (fPlus && n > 0) {
140 quotient_str.insert(0, '+');
141 }
142
143 if (num_decimals > 0) {
144 qint64 remainder = n_abs % coin;
145 QString remainder_str =
146 QString::number(remainder).rightJustified(num_decimals, '0');
147 return quotient_str + QString(".") + remainder_str;
148 } else {
149 return quotient_str;
150 }
151}
152
153// NOTE: Using formatWithUnit in an HTML context risks wrapping
154// quantities at the thousands separator. More subtly, it also results
155// in a standard space rather than a thin space, due to a bug in Qt's
156// XML whitespace canonicalisation
157//
158// Please take care to use formatHtmlWithUnit instead, when
159// appropriate.
160
161QString BitcoinUnits::formatWithUnit(int unit, const Amount amount,
162 bool plussign, SeparatorStyle separators) {
163 return format(unit, amount, plussign, separators) + QString(" ") +
164 shortName(unit);
165}
166
167QString BitcoinUnits::formatHtmlWithUnit(int unit, const Amount amount,
168 bool plussign,
169 SeparatorStyle separators) {
170 QString str(formatWithUnit(unit, amount, plussign, separators));
171 str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML));
172 return QString("<span style='white-space: nowrap;'>%1</span>").arg(str);
173}
174
175QString BitcoinUnits::formatWithPrivacy(int unit, const Amount &amount,
176 SeparatorStyle separators,
177 bool privacy) {
178 assert(amount >= Amount::zero());
179 QString value;
180 if (privacy) {
181 value = format(unit, Amount::zero(), false, separators, true)
182 .replace('0', '#');
183 } else {
184 value = format(unit, amount, false, separators, true);
185 }
186 return value + QString(" ") + shortName(unit);
187}
188
189bool BitcoinUnits::parse(int unit, const QString &value, Amount *val_out) {
190 if (!valid(unit) || value.isEmpty()) {
191 // Refuse to parse invalid unit or empty string
192 return false;
193 }
194 int num_decimals = decimals(unit);
195
196 // Ignore spaces and thin spaces when parsing
197 QStringList parts = removeSpaces(value).split(".");
198
199 if (parts.size() > 2) {
200 // More than one dot
201 return false;
202 }
203 QString whole = parts[0];
204 QString decimals;
205
206 if (parts.size() > 1) {
207 decimals = parts[1];
208 }
209 if (decimals.size() > num_decimals) {
210 // Exceeds max precision
211 return false;
212 }
213 bool ok = false;
214 QString str = whole + decimals.leftJustified(num_decimals, '0');
215
216 if (str.size() > 18) {
217 // Longer numbers will exceed 63 bits
218 return false;
219 }
220 Amount retvalue(int64_t(str.toLongLong(&ok)) * SATOSHI);
221 if (val_out) {
222 *val_out = retvalue;
223 }
224 return ok;
225}
226
228 QString amountTitle = QObject::tr("Amount");
229 if (BitcoinUnits::valid(unit)) {
230 amountTitle += " (" + BitcoinUnits::shortName(unit) + ")";
231 }
232 return amountTitle;
233}
234
235int BitcoinUnits::rowCount(const QModelIndex &parent) const {
236 Q_UNUSED(parent);
237 return unitlist.size();
238}
239
240QVariant BitcoinUnits::data(const QModelIndex &index, int role) const {
241 int row = index.row();
242 if (row >= 0 && row < unitlist.size()) {
243 Unit unit = unitlist.at(row);
244 switch (role) {
245 case Qt::EditRole:
246 case Qt::DisplayRole:
247 return QVariant(longName(unit));
248 case Qt::ToolTipRole:
249 return QVariant(description(unit));
250 case UnitRole:
251 return QVariant(static_cast<int>(unit));
252 }
253 }
254 return QVariant();
255}
256
258 return MAX_MONEY;
259}
static constexpr Amount SATOSHI
Definition: amount.h:143
static constexpr Amount MAX_MONEY
No amount larger than this (in satoshi) is valid.
Definition: amount.h:165
ArgsManager gArgs
Definition: args.cpp:38
static const unitNameMap xecUnits
static const unitNameMap bchUnits
std::map< BitcoinUnits::Unit, std::tuple< QString, QString > > unitNameMap
static const unitNameMap & getUnitsAtRuntime()
#define THIN_SP_CP
Definition: bitcoinunits.h:24
#define THIN_SP_HTML
Definition: bitcoinunits.h:26
#define THIN_SP_UTF8
Definition: bitcoinunits.h:25
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: args.cpp:556
@ UnitRole
Unit identifier.
Definition: bitcoinunits.h:92
int rowCount(const QModelIndex &parent) const override
QList< BitcoinUnits::Unit > unitlist
Definition: bitcoinunits.h:108
static QString formatWithUnit(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
static int decimals(int unit)
Number of decimals left.
QVariant data(const QModelIndex &index, int role) const override
static Amount factor(int unit)
Number of Satoshis (1e-8) per unit.
static bool valid(int unit)
Is unit ID valid?
static QString formatWithPrivacy(int unit, const Amount &amount, SeparatorStyle separators, bool privacy)
Format as string (with unit) of fixed length to preserve privacy, if it is set.
static QString description(int unit)
Longer description.
static QString removeSpaces(QString text)
Definition: bitcoinunits.h:98
static QString longName(int unit)
Long name.
static Amount maxMoney()
Return maximum number of base units (Satoshis)
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
static QString format(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD, bool justify=false)
Format as string.
static bool parse(int unit, const QString &value, Amount *val_out)
Parse string to coin amount.
static QString getAmountColumnTitle(int unit)
Gets title for amount column including current display unit if optionsModel reference available *‍/.
Unit
Currency units Please add only sensible ones.
Definition: bitcoinunits.h:42
BitcoinUnits(QObject *parent)
static QString shortName(int unit)
Short name.
static QString formatHtmlWithUnit(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as HTML string (with unit)
constexpr bool DEFAULT_ECASH
Definition: currencyunit.h:10
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32
static const Currency & get()
Definition: amount.cpp:18
uint8_t decimals
Definition: amount.h:149
assert(!tx.IsCoinBase())