Bitcoin ABC  0.29.2
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 <consensus/amount.h>
8 #include <currencyunit.h>
9 #include <util/system.h>
10 
11 #include <QStringList>
12 
13 #include <cassert>
14 
15 // clang-format off
16 using unitNameMap =
17  std::map<
19  std::tuple<
20  QString /* longname */,
21  QString /* description */
22  >
23  >;
24 static const unitNameMap xecUnits = {
25  {BitcoinUnits::Unit::base,
26  {"XEC",
27  "eCash"}},
28  {BitcoinUnits::Unit::sub,
29  {"Satoshi (sat)",
30  "Satoshi (sat) (1 / 100)"}},
31 };
32 static 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 
42 static const unitNameMap &getUnitsAtRuntime() {
43  return gArgs.GetBoolArg("-ecash", DEFAULT_ECASH) ? xecUnits : bchUnits;
44 }
45 
47  : QAbstractListModel(parent), unitlist(availableUnits()) {}
48 
49 QList<BitcoinUnits::Unit> BitcoinUnits::availableUnits() {
50  QList<BitcoinUnits::Unit> unitlist;
51  unitlist.append(base);
52  unitlist.append(sub);
53  return unitlist;
54 }
55 
56 bool BitcoinUnits::valid(int unit) {
57  switch (unit) {
58  case base:
59  case sub:
60  return true;
61  default:
62  return false;
63  }
64 }
65 
66 QString 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 
72 QString BitcoinUnits::shortName(int unit) {
73  if (unit == sub) {
74  return QString("sat");
75  }
76  return longName(unit);
77 }
78 
79 QString 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 
97 int BitcoinUnits::decimals(int unit) {
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 
108 QString 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 
161 QString 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 
167 QString 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 
175 QString 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 
189 bool 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 
235 int BitcoinUnits::rowCount(const QModelIndex &parent) const {
236  Q_UNUSED(parent);
237  return unitlist.size();
238 }
239 
240 QVariant 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
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: system.cpp:665
@ 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
ArgsManager gArgs
Definition: system.cpp:80
assert(!tx.IsCoinBase())