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