diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 7887a31b6..adfcf3eb1 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -121,6 +121,9 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans // 1) System default language QString lang_territory = GetLangTerritory(); + // Set default locale for amount and date formatting according to the selected language + QLocale::setDefault(QLocale(lang_territory)); + // Convert to "de" only by truncating "_DE" QString lang = lang_territory; lang.truncate(lang_territory.lastIndexOf('_')); diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 1033bf1a0..5e8150a56 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -25,7 +25,7 @@ public: explicit AmountSpinBox(QWidget *parent): QAbstractSpinBox(parent), currentUnit(BitcoinUnits::BTC), - singleStep(100000000) // koinu + singleStep(COIN) // koinu { setAlignment(Qt::AlignRight); @@ -48,7 +48,7 @@ public: CAmount val = parse(input, &valid); if(valid) { - input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways); + input = BitcoinUnits::format(currentUnit, val, false, true); lineEdit()->setText(input); } } @@ -60,7 +60,7 @@ public: void setValue(const CAmount& value) { - lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways)); + lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, true)); emit valueChanged(); } @@ -195,7 +195,6 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) : amount(0) { amount = new AmountSpinBox(this); - amount->setLocale(QLocale::c()); amount->installEventFilter(this); amount->setMaximumWidth(170); @@ -220,6 +219,14 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) : unitChanged(unit->currentIndex()); } +void BitcoinAmountField::setText(const QString &text) +{ + if (text.isEmpty()) + amount->clear(); + else + amount->setValue(QLocale().toDouble(text)); +} + void BitcoinAmountField::clear() { amount->clear(); diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index b047e6c51..8b5880b0d 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -15,6 +15,8 @@ QT_BEGIN_NAMESPACE class QValueComboBox; QT_END_NAMESPACE +class AmountSpinBox; + /** Widget for entering bitcoin amounts. */ class BitcoinAmountField: public QWidget @@ -67,6 +69,8 @@ private: AmountSpinBox *amount; QValueComboBox *unit; + void setText(const QString &text); + QString text() const; private slots: void unitChanged(int idx); diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 08c2bfa17..a4430c4ce 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -7,6 +7,7 @@ #include "primitives/transaction.h" #include +#include BitcoinUnits::BitcoinUnits(QObject *parent): QAbstractListModel(parent), @@ -69,6 +70,17 @@ qint64 BitcoinUnits::factor(int unit) } } +qint64 BitcoinUnits::maxAmount(int unit) +{ + switch(unit) + { + case BTC: return Q_INT64_C(900000000000); //less than the coin supply until the year 2170 + case mBTC: return Q_INT64_C(900000000000000); + case uBTC: return Q_INT64_C(900000000000000000); // Slightly under max value for int64 + default: return 0; + } +} + int BitcoinUnits::decimals(int unit) { switch(unit) @@ -80,37 +92,42 @@ int BitcoinUnits::decimals(int unit) } } -QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators) +QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, bool fTrim, const QLocale &locale_in) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. if(!valid(unit)) return QString(); // Refuse to format invalid unit qint64 n = (qint64)nIn; + QLocale locale(locale_in); qint64 coin = factor(unit); int num_decimals = decimals(unit); + qint64 n_abs = (n > 0 ? n : -n); qint64 quotient = n_abs / coin; qint64 remainder = n_abs % coin; - QString quotient_str = QString::number(quotient); - QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0'); + // Quotient has group (decimal) separators if locale has this enabled + QString quotient_str = locale.toString(quotient); + // Remainder does not have group separators + locale.setNumberOptions(QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator); + QString remainder_str = locale.toString(remainder).rightJustified(num_decimals, '0'); - // Use SI-style thin space separators as these are locale independent and can't be - // confused with the decimal marker. - QChar thin_sp(THIN_SP_CP); - int q_size = quotient_str.size(); - if (separators == separatorAlways || (separators == separatorStandard && q_size > 4)) - for (int i = 3; i < q_size; i += 3) - quotient_str.insert(q_size - i, thin_sp); + if(fTrim) + { + // Right-trim excess zeros after the decimal point + int nTrim = 0; + for (int i = remainder_str.size()-1; i>=2 && (remainder_str.at(i) == '0'); --i) + ++nTrim; + remainder_str.chop(nTrim); + } if (n < 0) quotient_str.insert(0, '-'); else if (fPlus && n > 0) quotient_str.insert(0, '+'); - return quotient_str + QString(".") + remainder_str; + return quotient_str + locale.decimalPoint() + remainder_str; } - // TODO: Review all remaining calls to BitcoinUnits::formatWithUnit to // TODO: determine whether the output is used in a plain text context // TODO: or an HTML context (and replace with @@ -126,54 +143,64 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator // Please take care to use formatHtmlWithUnit instead, when // appropriate. -QString BitcoinUnits::formatWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators) +QString BitcoinUnits::formatWithUnit(int unit, const CAmount& amount, bool plussign, bool trim, const QLocale &locale) { - return format(unit, amount, plussign, separators) + QString(" ") + name(unit); + return format(unit, amount, plussign, trim) + QString(" ") + name(unit); } -QString BitcoinUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators) +QString BitcoinUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign, bool trim, const QLocale &locale) { - QString str(formatWithUnit(unit, amount, plussign, separators)); + QString str(formatWithUnit(unit, amount, plussign, trim, locale)); str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML)); return QString("%1").arg(str); } -bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out) +bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out, const QLocale &locale_in) { if(!valid(unit) || value.isEmpty()) return false; // Refuse to parse invalid unit or empty string + + QLocale locale(locale_in); + qint64 coin = factor(unit); int num_decimals = decimals(unit); - // Ignore spaces and thin spaces when parsing - QStringList parts = removeSpaces(value).split("."); + QStringList parts = value.split(locale.decimalPoint()); + bool ok = false; if(parts.size() > 2) - { - return false; // More than one dot - } - QString whole = parts[0]; - QString decimals; + return false; // More than one decimal point + // Parse whole part (may include locale-specific group separators) +#if QT_VERSION < 0x050000 + qint64 whole = locale.toLongLong(parts[0], &ok, 10); +#else + qint64 whole = locale.toLongLong(parts[0], &ok); +#endif + if(!ok) + return false; // Parse error + if(whole > maxAmount(unit) || whole < 0) + return false; // Overflow or underflow + + // Parse decimals part (if present, may not include group separators) + qint64 decimals = 0; if(parts.size() > 1) { - decimals = parts[1]; + if(parts[1].size() > num_decimals) + return false; // Exceeds max precision + locale.setNumberOptions(QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator); +#if QT_VERSION < 0x050000 + decimals = locale.toLongLong(parts[1].leftJustified(num_decimals, '0'), &ok, 10); +#else + decimals = locale.toLongLong(parts[1].leftJustified(num_decimals, '0'), &ok); +#endif + if(!ok || decimals < 0) + return false; // Parse error } - if(decimals.size() > num_decimals) - { - return false; // Exceeds max precision - } - bool ok = false; - QString str = whole + decimals.leftJustified(num_decimals, '0'); - if(str.size() > 18) - { - return false; // Longer numbers will exceed 63 bits - } - CAmount retvalue(str.toLongLong(&ok)); if(val_out) { - *val_out = retvalue; + *val_out = whole * coin + decimals; } return ok; } diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 1871c33a7..afc066aac 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -9,6 +9,7 @@ #include #include +#include // U+2009 THIN SPACE = UTF-8 E2 80 89 #define REAL_THIN_SP_CP 0x2009 @@ -82,15 +83,17 @@ public: static QString description(int unit); //! Number of Satoshis (1e-8) per unit static qint64 factor(int unit); + //! Max amount per unit + static qint64 maxAmount(int unit); //! Number of decimals left static int decimals(int unit); //! Format as string - static QString format(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard); + static QString format(int unit, const CAmount& amount, bool plussign=false, bool trim=true, const QLocale &locale=QLocale()); //! Format as string (with unit) - static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard); - static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard); + static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, bool trim=true, const QLocale &locale=QLocale()); + static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, bool trim=true, const QLocale &locale=QLocale()); //! Parse string to coin amount - static bool parse(int unit, const QString &value, CAmount *val_out); + static bool parse(int unit, const QString &value, CAmount *val_out, const QLocale &locale=QLocale()); //! Gets title for amount column including current display unit if optionsModel reference available */ static QString getAmountColumnTitle(int unit); ///@} diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index d41aae114..38c7c4211 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -161,7 +161,10 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { if(!i->second.isEmpty()) { - if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount)) + // Parse amount in C locale with no number separators + QLocale locale(QLocale::c()); + locale.setNumberOptions(QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator); + if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount, locale)) { return false; }