From 16a03ee0ba3f5b7a4c1af1d53c5ab2e660899878 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 24 Jan 2012 10:34:02 -0500 Subject: [PATCH 01/12] =?UTF-8?q?Support=20for=20Tonal=20Bitcoin=20units?= =?UTF-8?q?=20(=E1=B5=87TBC,=20=CB=A2TBC,=20and=20TBC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only available on dropdowns when a Tonal-compatible font is installed --- src/Makefile.qt.include | 2 + src/qt/bitcoinamountfield.cpp | 8 +- src/qt/bitcoinunits.cpp | 152 ++++++++++++++++++++++++++++++---- src/qt/bitcoinunits.h | 15 +++- src/qt/tonalutils.cpp | 56 +++++++++++++ src/qt/tonalutils.h | 25 ++++++ 6 files changed, 236 insertions(+), 22 deletions(-) create mode 100644 src/qt/tonalutils.cpp create mode 100644 src/qt/tonalutils.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 602a118259..0ba09c0275 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -152,6 +152,7 @@ BITCOIN_QT_H = \ qt/sendcoinsrecipient.h \ qt/signverifymessagedialog.h \ qt/splashscreen.h \ + qt/tonalutils.h \ qt/trafficgraphwidget.h \ qt/transactiondesc.h \ qt/transactiondescdialog.h \ @@ -243,6 +244,7 @@ BITCOIN_QT_BASE_CPP = \ qt/qvaluecombobox.cpp \ qt/rpcconsole.cpp \ qt/splashscreen.cpp \ + qt/tonalutils.cpp \ qt/trafficgraphwidget.cpp \ qt/utilitydialog.cpp diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index a7e2d22488..09133ab7a2 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -94,7 +94,11 @@ public: { bool valid = false; CAmount val = value(&valid); - val = val + steps * singleStep; + CAmount currentSingleStep = singleStep; + if (!currentSingleStep) { + currentSingleStep = BitcoinUnits::singlestep(currentUnit); + } + val = val + steps * currentSingleStep; val = qBound(m_min_amount, val, m_max_amount); setValue(val); } @@ -151,7 +155,7 @@ public: private: BitcoinUnit currentUnit{BitcoinUnit::BTC}; - CAmount singleStep{CAmount(100000)}; // satoshis + CAmount singleStep{CAmount(0)}; mutable QSize cachedMinimumSizeHint; bool m_allow_empty{true}; CAmount m_min_amount{CAmount(0)}; diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index fe3eb3240b..25ae0fd168 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include @@ -11,6 +12,7 @@ #include static constexpr auto MAX_DIGITS_BTC = 16; +static constexpr auto MAX_DIGITS_TBC = 13; BitcoinUnits::BitcoinUnits(QObject *parent): QAbstractListModel(parent), @@ -20,11 +22,17 @@ BitcoinUnits::BitcoinUnits(QObject *parent): QList BitcoinUnits::availableUnits() { - QList unitlist; + static QList unitlist; unitlist.append(Unit::BTC); unitlist.append(Unit::mBTC); unitlist.append(Unit::uBTC); unitlist.append(Unit::SAT); + if (TonalUtils::Supported()) + { + unitlist.append(Unit::bTBC); + unitlist.append(Unit::sTBC); + unitlist.append(Unit::TBC); + } return unitlist; } @@ -35,6 +43,9 @@ QString BitcoinUnits::longName(Unit unit) case Unit::mBTC: return QString("mBTC"); case Unit::uBTC: return QString::fromUtf8("µBTC (bits)"); case Unit::SAT: return QString("Satoshi (sat)"); + case Unit::bTBC: return QString::fromUtf8("ᵇTBC"); + case Unit::sTBC: return QString::fromUtf8("ˢTBC"); + case Unit::TBC: return QString("TBC"); } // no default case, so the compiler can warn about missing cases assert(false); } @@ -46,6 +57,9 @@ QString BitcoinUnits::shortName(Unit unit) case Unit::mBTC: return longName(unit); case Unit::uBTC: return QString("bits"); case Unit::SAT: return QString("sat"); + case Unit::bTBC: return QString::fromUtf8("ᵇTBC"); + case Unit::sTBC: return QString::fromUtf8("ˢTBC"); + case Unit::TBC: return QString("TBC"); } // no default case, so the compiler can warn about missing cases assert(false); } @@ -53,10 +67,13 @@ QString BitcoinUnits::shortName(Unit unit) QString BitcoinUnits::description(Unit unit) { switch (unit) { - case Unit::BTC: return QString("Bitcoins"); + case Unit::BTC: return QString("Bitcoins (decimal)"); case Unit::mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)"); case Unit::uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); case Unit::SAT: return QString("Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); + case Unit::bTBC: return QString("Bong-Bitcoins (1,0000 tonal)"); + case Unit::sTBC: return QString("San-Bitcoins (100 tonal)"); + case Unit::TBC: return QString("Bitcoins (tonal)"); } // no default case, so the compiler can warn about missing cases assert(false); } @@ -68,6 +85,9 @@ qint64 BitcoinUnits::factor(Unit unit) case Unit::mBTC: return 100'000; case Unit::uBTC: return 100; case Unit::SAT: return 1; + case Unit::bTBC: return 0x100000000LL; + case Unit::sTBC: return 0x1000000; + case Unit::TBC: return 0x10000; } // no default case, so the compiler can warn about missing cases assert(false); } @@ -79,10 +99,57 @@ int BitcoinUnits::decimals(Unit unit) case Unit::mBTC: return 5; case Unit::uBTC: return 2; case Unit::SAT: return 0; + case Unit::bTBC: return 8; + case Unit::sTBC: return 6; + case Unit::TBC: return 4; } // no default case, so the compiler can warn about missing cases assert(false); } +int BitcoinUnits::radix(Unit unit) +{ + switch (unit) { + case Unit::bTBC: + case Unit::sTBC: + case Unit::TBC: + return 0x10; + default: + return 10; + } +} + +BitcoinUnit BitcoinUnits::numsys(Unit unit) +{ + switch (unit) { + case Unit::bTBC: + case Unit::sTBC: + case Unit::TBC: + return Unit::TBC; + default: + return Unit::BTC; + } +} + +qint64 BitcoinUnits::max_digits(Unit unit) +{ + switch (numsys(unit)) { + case Unit::TBC: + return MAX_DIGITS_TBC; + default: + return MAX_DIGITS_BTC; + } +} + +qint64 BitcoinUnits::singlestep(Unit unit) +{ + switch (numsys(unit)) { + case Unit::TBC: + return 0x10000; + default: + return 100000; + } +} + QString BitcoinUnits::format(Unit unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify) { // Note: not using straight sprintf here because we do NOT want @@ -92,11 +159,22 @@ QString BitcoinUnits::format(Unit unit, const CAmount& nIn, bool fPlus, Separato int num_decimals = decimals(unit); qint64 n_abs = (n > 0 ? n : -n); qint64 quotient = n_abs / coin; - QString quotient_str = QString::number(quotient); + int uradix = radix(unit); + QString quotient_str = QString::number(quotient, uradix); if (justify) { - quotient_str = quotient_str.rightJustified(MAX_DIGITS_BTC - num_decimals, ' '); + quotient_str = quotient_str.rightJustified(max_digits(unit) - num_decimals, ' '); } + QString remainder_str; + if (num_decimals > 0) { + const qint64 remainder = n_abs % coin; + remainder_str = QString::number(remainder, uradix).rightJustified(num_decimals, '0'); + } + + switch (numsys(unit)) { + case Unit::BTC: + { + // 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); @@ -105,18 +183,28 @@ QString BitcoinUnits::format(Unit unit, const CAmount& nIn, bool fPlus, Separato for (int i = 3; i < q_size; i += 3) quotient_str.insert(q_size - i, thin_sp); + break; + } + case Unit::TBC: + { + // Right-trim excess zeros after the decimal point + static const QRegExp tail_zeros("0+$"); + remainder_str.remove(tail_zeros); + TonalUtils::ConvertFromHex(quotient_str); + TonalUtils::ConvertFromHex(remainder_str); + break; + } + default: assert(false); + } + if (n < 0) quotient_str.insert(0, '-'); else if (fPlus && n > 0) quotient_str.insert(0, '+'); - if (num_decimals > 0) { - qint64 remainder = n_abs % coin; - QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0'); - return quotient_str + QString(".") + remainder_str; - } else { - return quotient_str; - } + if (!remainder_str.isEmpty()) + quotient_str += QString(".") + remainder_str; + return quotient_str; } @@ -180,11 +268,18 @@ bool BitcoinUnits::parse(Unit unit, const QString& value, CAmount* val_out) bool ok = false; QString str = whole + decimals.leftJustified(num_decimals, '0'); + Unit unumsys = numsys(unit); + if (unumsys == Unit::TBC) { + if (str.size() > 15) + return false; // Longer numbers may exceed 63 bits + TonalUtils::ConvertToHex(str); + } else if(str.size() > 18) { return false; // Longer numbers will exceed 63 bits } - CAmount retvalue(str.toLongLong(&ok)); + + CAmount retvalue(str.toLongLong(&ok, radix(unit))); if(val_out) { *val_out = retvalue; @@ -229,13 +324,16 @@ CAmount BitcoinUnits::maxMoney() } namespace { -qint8 ToQint8(BitcoinUnit unit) +std::variant ToSetting(BitcoinUnit unit) { switch (unit) { - case BitcoinUnit::BTC: return 0; - case BitcoinUnit::mBTC: return 1; - case BitcoinUnit::uBTC: return 2; - case BitcoinUnit::SAT: return 3; + case BitcoinUnit::BTC: return qint8{0}; + case BitcoinUnit::mBTC: return qint8{1}; + case BitcoinUnit::uBTC: return qint8{2}; + case BitcoinUnit::SAT: return qint8{3}; + case BitcoinUnit::bTBC: return QString("bTBC"); + case BitcoinUnit::sTBC: return QString("sTBC"); + case BitcoinUnit::TBC: return QString("TBC"); } // no default case, so the compiler can warn about missing cases assert(false); } @@ -250,11 +348,24 @@ BitcoinUnit FromQint8(qint8 num) } assert(false); } + +BitcoinUnit FromSetting(const QString& s, BitcoinUnit def) +{ + if (s == "bTBC") return BitcoinUnit::bTBC; + if (s == "sTBC") return BitcoinUnit::sTBC; + if (s == "TBC") return BitcoinUnit::TBC; + return def; +} } // namespace QDataStream& operator<<(QDataStream& out, const BitcoinUnit& unit) { - return out << ToQint8(unit); + auto setting_val = ToSetting(unit); + if (const QString* setting_str = std::get_if(&setting_val)) { + return out << qint8{0} << *setting_str; + } else { + return out << std::get(setting_val); + } } QDataStream& operator>>(QDataStream& in, BitcoinUnit& unit) @@ -262,5 +373,10 @@ QDataStream& operator>>(QDataStream& in, BitcoinUnit& unit) qint8 input; in >> input; unit = FromQint8(input); + if (!in.atEnd()) { + QString setting_str; + in >> setting_str; + unit = FromSetting(setting_str, unit); + } return in; } diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index b3b5a8fc18..17ad482cef 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -43,7 +43,10 @@ public: BTC, mBTC, uBTC, - SAT + SAT, + bTBC, + sTBC, + TBC, }; Q_ENUM(Unit) @@ -68,8 +71,16 @@ public: static QString description(Unit unit); //! Number of Satoshis (1e-8) per unit static qint64 factor(Unit unit); - //! Number of decimals left + //! Number of fractional places static int decimals(Unit unit); + //! Radix + static int radix(Unit unit); + //! Number system + static Unit numsys(Unit unit); + //! Number of digits total in maximum value + static qint64 max_digits(Unit unit); + //! "Single step" amount, in satoshis + static qint64 singlestep(Unit unit); //! Format as string static QString format(Unit unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false); //! Format as string (with unit) diff --git a/src/qt/tonalutils.cpp b/src/qt/tonalutils.cpp new file mode 100644 index 0000000000..4212e3f9e7 --- /dev/null +++ b/src/qt/tonalutils.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include + +bool TonalUtils::Supported() +{ + QFontMetrics fm = QFontMetrics(QFont()); + return fm.inFont(0xe9d9); +} + +static QRegExpValidator tv(QRegExp("-?(?:[\\d\\xe9d9-\\xe9df]+\\.?|[\\d\\xe9d9-\\xe9df]*\\.[\\d\\xe9d9-\\xe9df]*)"), nullptr); + +QValidator::State TonalUtils::validate(QString&input, int&pos) +{ + return tv.validate(input, pos); +} + +void TonalUtils::ConvertFromHex(QString&str) +{ + for (int i = 0; i < str.size(); ++i) + { + ushort c = str[i].unicode(); + if (c == '9') + str[i] = 0xe9d9; + else + if (c >= 'A' && c <= 'F') + str[i] = c + 0xe999; + else + if (c >= 'a' && c <= 'f') + str[i] = c + 0xe979; + } +} + +void TonalUtils::ConvertToHex(QString&str) +{ + for (int i = 0; i < str.size(); ++i) + { + ushort c = str[i].unicode(); + if (c == 0xe9d9) + str[i] = '9'; + else + if (c == '9') + str[i] = 'a'; + else + if (c >= 0xe9da && c <= 0xe9df) + str[i] = c - 0xe999; + } +} diff --git a/src/qt/tonalutils.h b/src/qt/tonalutils.h new file mode 100644 index 0000000000..eb6786b765 --- /dev/null +++ b/src/qt/tonalutils.h @@ -0,0 +1,25 @@ +// Copyright (c) 2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_TONALUTILS_H +#define BITCOIN_QT_TONALUTILS_H + +#include + +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE + +class TonalUtils +{ +public: + static bool Supported(); + + static QValidator::State validate(QString&input, int&pos); + + static void ConvertFromHex(QString&); + static void ConvertToHex(QString&); +}; + +#endif // BITCOIN_QT_TONALUTILS_H From d65857b2a2dbb37836d6d2b951b34cbc927fa929 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 2 Feb 2019 03:46:54 +0000 Subject: [PATCH 02/12] Bugfix: GUI: bitcoinunits: Don't make unitlist static, since it gets rebuilt every call --- src/qt/bitcoinunits.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 25ae0fd168..5063968cb1 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -22,7 +22,7 @@ BitcoinUnits::BitcoinUnits(QObject *parent): QList BitcoinUnits::availableUnits() { - static QList unitlist; + QList unitlist; unitlist.append(Unit::BTC); unitlist.append(Unit::mBTC); unitlist.append(Unit::uBTC); From 2c412bd30fe19d6053818b8eb6692977bc40e28c Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 30 Mar 2019 04:54:51 +0000 Subject: [PATCH 03/12] GUI/tonalutils: For Tonal support detection, check that the font has all glyphs and they all have the same sizes --- src/qt/tonalutils.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/qt/tonalutils.cpp b/src/qt/tonalutils.cpp index 4212e3f9e7..6cf3efe4fe 100644 --- a/src/qt/tonalutils.cpp +++ b/src/qt/tonalutils.cpp @@ -10,10 +10,31 @@ #include #include +static const QList tonal_digits{0xe9df, 0xe9de, 0xe9dd, 0xe9dc, 0xe9db, 0xe9da, 0xe9d9, '8', '7', '6', '5', '4', '3', '2', '1', '0'}; + +namespace { + +bool font_supports_tonal(const QFont& font) +{ + const QFontMetrics fm(font); + QString s = "000"; + const QSize sz = fm.size(0, s); + for (const auto& c : tonal_digits) { + if (!fm.inFont(c)) return false; + if (sz != fm.size(0, s)) return false; + } + return true; +} + +} // anon namespace + bool TonalUtils::Supported() { - QFontMetrics fm = QFontMetrics(QFont()); - return fm.inFont(0xe9d9); + QFont default_font; + if (font_supports_tonal(default_font)) return true; + QFont last_resort_font(default_font.lastResortFamily()); + if (font_supports_tonal(last_resort_font)) return true; + return false; } static QRegExpValidator tv(QRegExp("-?(?:[\\d\\xe9d9-\\xe9df]+\\.?|[\\d\\xe9d9-\\xe9df]*\\.[\\d\\xe9d9-\\xe9df]*)"), nullptr); From d3491a389858d64aa8f0e3212ca92b08e159b693 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 11 Oct 2021 16:38:23 +0000 Subject: [PATCH 04/12] GUI: Fix comparison of character size for Tonal font detection --- src/qt/tonalutils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/tonalutils.cpp b/src/qt/tonalutils.cpp index 6cf3efe4fe..948bc3fe23 100644 --- a/src/qt/tonalutils.cpp +++ b/src/qt/tonalutils.cpp @@ -21,6 +21,7 @@ bool font_supports_tonal(const QFont& font) const QSize sz = fm.size(0, s); for (const auto& c : tonal_digits) { if (!fm.inFont(c)) return false; + s[0] = s[1] = s[2] = c; if (sz != fm.size(0, s)) return false; } return true; From e6a89a9cc55e8e898f5fd857d7d82398e9ad2aac Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 16 May 2022 23:09:33 +0000 Subject: [PATCH 05/12] qt/tonalutils: Split Tonal character regex to one location --- src/qt/tonalutils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qt/tonalutils.cpp b/src/qt/tonalutils.cpp index 948bc3fe23..921e51882e 100644 --- a/src/qt/tonalutils.cpp +++ b/src/qt/tonalutils.cpp @@ -38,7 +38,8 @@ bool TonalUtils::Supported() return false; } -static QRegExpValidator tv(QRegExp("-?(?:[\\d\\xe9d9-\\xe9df]+\\.?|[\\d\\xe9d9-\\xe9df]*\\.[\\d\\xe9d9-\\xe9df]*)"), nullptr); +#define RE_TONAL_DIGIT "[\\d\\xe9d9-\\xe9df]" +static QRegExpValidator tv(QRegExp("-?(?:" RE_TONAL_DIGIT "+\\.?|" RE_TONAL_DIGIT "*\\." RE_TONAL_DIGIT "*)"), nullptr); QValidator::State TonalUtils::validate(QString&input, int&pos) { From d05c79698e4a0d43c9d50ede5e6fd1f60daa335e Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 16 May 2022 23:15:24 +0000 Subject: [PATCH 06/12] qt/tonalutils: Add support for parsing UCSUR Tonal codepoints --- src/qt/tonalutils.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/qt/tonalutils.cpp b/src/qt/tonalutils.cpp index 921e51882e..5f84d57de9 100644 --- a/src/qt/tonalutils.cpp +++ b/src/qt/tonalutils.cpp @@ -38,7 +38,7 @@ bool TonalUtils::Supported() return false; } -#define RE_TONAL_DIGIT "[\\d\\xe9d9-\\xe9df]" +#define RE_TONAL_DIGIT "[\\d\\xe8e9-\\xe8ef\\xe9d9-\\xe9df]" static QRegExpValidator tv(QRegExp("-?(?:" RE_TONAL_DIGIT "+\\.?|" RE_TONAL_DIGIT "*\\." RE_TONAL_DIGIT "*)"), nullptr); QValidator::State TonalUtils::validate(QString&input, int&pos) @@ -67,12 +67,16 @@ void TonalUtils::ConvertToHex(QString&str) for (int i = 0; i < str.size(); ++i) { ushort c = str[i].unicode(); - if (c == 0xe9d9) + if (c == 0xe9d9 || c == 0xe8e9) { str[i] = '9'; + } else if (c == '9') str[i] = 'a'; else + if (c >= 0xe8ea && c <= 0xe8ef) { + str[i] = c - (0xe8ea - 'a'); + } else if (c >= 0xe9da && c <= 0xe9df) str[i] = c - 0xe999; } From e165451bba3392d52c85a2c5afdebde6b88b6974 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 16 May 2022 23:21:15 +0000 Subject: [PATCH 07/12] qt/tonalutils: Add support for parsing lower value "reserved" Tonal digits (0-8) --- src/qt/tonalutils.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qt/tonalutils.cpp b/src/qt/tonalutils.cpp index 5f84d57de9..f5b1e1289c 100644 --- a/src/qt/tonalutils.cpp +++ b/src/qt/tonalutils.cpp @@ -38,7 +38,7 @@ bool TonalUtils::Supported() return false; } -#define RE_TONAL_DIGIT "[\\d\\xe8e9-\\xe8ef\\xe9d9-\\xe9df]" +#define RE_TONAL_DIGIT "[\\d\\xe8e0-\\xe8ef\\xe9d0-\\xe9df]" static QRegExpValidator tv(QRegExp("-?(?:" RE_TONAL_DIGIT "+\\.?|" RE_TONAL_DIGIT "*\\." RE_TONAL_DIGIT "*)"), nullptr); QValidator::State TonalUtils::validate(QString&input, int&pos) @@ -67,15 +67,15 @@ void TonalUtils::ConvertToHex(QString&str) for (int i = 0; i < str.size(); ++i) { ushort c = str[i].unicode(); - if (c == 0xe9d9 || c == 0xe8e9) { - str[i] = '9'; - } - else if (c == '9') str[i] = 'a'; else - if (c >= 0xe8ea && c <= 0xe8ef) { + if (c >= 0xe8e0 && c <= 0xe8e9) { // UCSUR 0-9 + str[i] = c - (0xe8e0 - '0'); + } else if (c >= 0xe8ea && c <= 0xe8ef) { // UCSUR a-f str[i] = c - (0xe8ea - 'a'); + } else if (c >= 0xe9d0 && c <= 0xe9d9) { + str[i] = c - (0xe9d0 - '0'); } else if (c >= 0xe9da && c <= 0xe9df) str[i] = c - 0xe999; From ae9a0ad194b68355aea9a9a2ce4a1b4c9fc1466a Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 16 May 2022 23:25:08 +0000 Subject: [PATCH 08/12] qt/tonalutils: Use UCSUR codepoints for Tonal output --- src/qt/tonalutils.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qt/tonalutils.cpp b/src/qt/tonalutils.cpp index f5b1e1289c..a7639a89bf 100644 --- a/src/qt/tonalutils.cpp +++ b/src/qt/tonalutils.cpp @@ -10,7 +10,7 @@ #include #include -static const QList tonal_digits{0xe9df, 0xe9de, 0xe9dd, 0xe9dc, 0xe9db, 0xe9da, 0xe9d9, '8', '7', '6', '5', '4', '3', '2', '1', '0'}; +static const QList tonal_digits{0xe8ef, 0xe8ee, 0xe8ed, 0xe8ec, 0xe8eb, 0xe8ea, 0xe8e9, '8', '7', '6', '5', '4', '3', '2', '1', '0'}; namespace { @@ -52,13 +52,13 @@ void TonalUtils::ConvertFromHex(QString&str) { ushort c = str[i].unicode(); if (c == '9') - str[i] = 0xe9d9; + str[i] = 0xe8e9; else if (c >= 'A' && c <= 'F') - str[i] = c + 0xe999; + str[i] = c + (0xe8ea - 'A'); else if (c >= 'a' && c <= 'f') - str[i] = c + 0xe979; + str[i] = c + (0xe8ea - 'a'); } } From 57931dc3912958e55c43e7aa9e88278aeecadfea Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 16 May 2022 23:01:41 +0000 Subject: [PATCH 09/12] qt/tonalunits: Require at least one digit for valid Tonal numbers --- src/qt/tonalutils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/tonalutils.cpp b/src/qt/tonalutils.cpp index a7639a89bf..4d6767cb91 100644 --- a/src/qt/tonalutils.cpp +++ b/src/qt/tonalutils.cpp @@ -39,7 +39,7 @@ bool TonalUtils::Supported() } #define RE_TONAL_DIGIT "[\\d\\xe8e0-\\xe8ef\\xe9d0-\\xe9df]" -static QRegExpValidator tv(QRegExp("-?(?:" RE_TONAL_DIGIT "+\\.?|" RE_TONAL_DIGIT "*\\." RE_TONAL_DIGIT "*)"), nullptr); +static QRegExpValidator tv(QRegExp("-?(?:" RE_TONAL_DIGIT "+\\.?|" RE_TONAL_DIGIT "*\\." RE_TONAL_DIGIT "+)"), nullptr); QValidator::State TonalUtils::validate(QString&input, int&pos) { From 5145e1efeb0b415a71f299ebea90709310bf292a Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 7 Aug 2023 21:37:34 +0000 Subject: [PATCH 10/12] GUI: Save any TBC settings in a parallel setting key --- src/qt/bitcoinunits.cpp | 2 +- src/qt/optionsmodel.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 5063968cb1..3adf94a4bd 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -346,7 +346,7 @@ BitcoinUnit FromQint8(qint8 num) case 2: return BitcoinUnit::uBTC; case 3: return BitcoinUnit::SAT; } - assert(false); + return BitcoinUnit::BTC; } BitcoinUnit FromSetting(const QString& s, BitcoinUnit def) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index bee8fafddc..6592dbbf51 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -163,6 +163,9 @@ bool OptionsModel::Init(bilingual_str& error) settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(BitcoinUnit::BTC)); } QVariant unit = settings.value("DisplayBitcoinUnit"); + if (settings.contains("DisplayBitcoinUnitKnots")) { + unit = settings.value("DisplayBitcoinUnitKnots"); + } if (unit.canConvert()) { m_display_bitcoin_unit = unit.value(); } else { @@ -653,7 +656,12 @@ void OptionsModel::setDisplayUnit(const QVariant& new_unit) if (new_unit.isNull() || new_unit.value() == m_display_bitcoin_unit) return; m_display_bitcoin_unit = new_unit.value(); QSettings settings; - settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit)); + if (BitcoinUnits::numsys(m_display_bitcoin_unit) == BitcoinUnit::BTC) { + settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(m_display_bitcoin_unit)); + settings.remove("DisplayBitcoinUnitKnots"); + } else { + settings.setValue("DisplayBitcoinUnitKnots", QVariant::fromValue(m_display_bitcoin_unit)); + } Q_EMIT displayUnitChanged(m_display_bitcoin_unit); } From 134d72187b163df02db970053527cd7e18fe1c67 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 7 Aug 2023 21:58:07 +0000 Subject: [PATCH 11/12] GUI: If new DisplayBitcoinUnit is missing, migrate nDisplayUnit --- src/qt/bitcoinunits.cpp | 12 +++++++++--- src/qt/bitcoinunits.h | 2 ++ src/qt/optionsmodel.cpp | 7 ++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 3adf94a4bd..a5ca9617c4 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -348,15 +348,21 @@ BitcoinUnit FromQint8(qint8 num) } return BitcoinUnit::BTC; } +} // namespace -BitcoinUnit FromSetting(const QString& s, BitcoinUnit def) +BitcoinUnit BitcoinUnits::FromSetting(const QString& s, BitcoinUnit def) { + if (s == "0") return BitcoinUnit::BTC; + if (s == "1") return BitcoinUnit::mBTC; + if (s == "2") return BitcoinUnit::uBTC; + if (s == "3") return BitcoinUnit::SAT; + if (s == "4") return BitcoinUnit::sTBC; + if (s == "5") return BitcoinUnit::TBC; if (s == "bTBC") return BitcoinUnit::bTBC; if (s == "sTBC") return BitcoinUnit::sTBC; if (s == "TBC") return BitcoinUnit::TBC; return def; } -} // namespace QDataStream& operator<<(QDataStream& out, const BitcoinUnit& unit) { @@ -376,7 +382,7 @@ QDataStream& operator>>(QDataStream& in, BitcoinUnit& unit) if (!in.atEnd()) { QString setting_str; in >> setting_str; - unit = FromSetting(setting_str, unit); + unit = BitcoinUnits::FromSetting(setting_str, unit); } return in; } diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 17ad482cef..4d7c94134c 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -63,6 +63,8 @@ public: //! Get list of units, for drop-down box static QList availableUnits(); + //! Convert setting(s) string to unit + static Unit FromSetting(const QString&, Unit def); //! Long name static QString longName(Unit unit); //! Short name diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 6592dbbf51..5e69c4f38a 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -160,7 +160,12 @@ bool OptionsModel::Init(bilingual_str& error) // Display if (!settings.contains("DisplayBitcoinUnit")) { - settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(BitcoinUnit::BTC)); + auto init_unit = BitcoinUnit::BTC; + if (settings.contains("nDisplayUnit")) { + // Migrate to new setting + init_unit = BitcoinUnits::FromSetting(settings.value("nDisplayUnit").toString(), init_unit); + } + settings.setValue("DisplayBitcoinUnit", QVariant::fromValue(init_unit)); } QVariant unit = settings.value("DisplayBitcoinUnit"); if (settings.contains("DisplayBitcoinUnitKnots")) { From bbd4c9ad42b19c066657c8b263a30a4ddb799972 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 7 Aug 2023 22:11:15 +0000 Subject: [PATCH 12/12] GUI: Update nDisplayUnit to keep old versions in sync --- src/qt/bitcoinunits.cpp | 6 +++--- src/qt/bitcoinunits.h | 2 ++ src/qt/optionsmodel.cpp | 9 +++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index a5ca9617c4..be29decf2e 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -323,8 +323,7 @@ CAmount BitcoinUnits::maxMoney() return MAX_MONEY; } -namespace { -std::variant ToSetting(BitcoinUnit unit) +std::variant BitcoinUnits::ToSetting(BitcoinUnit unit) { switch (unit) { case BitcoinUnit::BTC: return qint8{0}; @@ -338,6 +337,7 @@ std::variant ToSetting(BitcoinUnit unit) assert(false); } +namespace { BitcoinUnit FromQint8(qint8 num) { switch (num) { @@ -366,7 +366,7 @@ BitcoinUnit BitcoinUnits::FromSetting(const QString& s, BitcoinUnit def) QDataStream& operator<<(QDataStream& out, const BitcoinUnit& unit) { - auto setting_val = ToSetting(unit); + auto setting_val = BitcoinUnits::ToSetting(unit); if (const QString* setting_str = std::get_if(&setting_val)) { return out << qint8{0} << *setting_str; } else { diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 4d7c94134c..4e9e77cee0 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -63,6 +63,8 @@ public: //! Get list of units, for drop-down box static QList availableUnits(); + //! String for setting(s) + static std::variant ToSetting(Unit unit); //! Convert setting(s) string to unit static Unit FromSetting(const QString&, Unit def); //! Long name diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 5e69c4f38a..58b5091639 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -667,6 +667,15 @@ void OptionsModel::setDisplayUnit(const QVariant& new_unit) } else { settings.setValue("DisplayBitcoinUnitKnots", QVariant::fromValue(m_display_bitcoin_unit)); } + { + // For older versions: + auto setting_val = BitcoinUnits::ToSetting(m_display_bitcoin_unit); + if (const QString* setting_str = std::get_if(&setting_val)) { + settings.setValue("nDisplayUnit", *setting_str); + } else { + settings.setValue("nDisplayUnit", std::get(setting_val)); + } + } Q_EMIT displayUnitChanged(m_display_bitcoin_unit); }