From 7007402956579ace12d45cdcfae908802d3d6b6d Mon Sep 17 00:00:00 2001 From: Roy Badami Date: Fri, 9 May 2014 23:50:09 +0100 Subject: [PATCH 1/4] Implement SI-style (thin space) thoudands separator --- src/qt/bitcoinamountfield.cpp | 49 +++++++++++++++++++++++++++-- src/qt/bitcoinunits.cpp | 39 +++++++++++++++++++---- src/qt/bitcoinunits.h | 53 ++++++++++++++++++++++++++++++-- src/qt/overviewpage.cpp | 10 +++--- src/qt/sendcoinsdialog.cpp | 8 ++--- src/qt/transactiondesc.cpp | 24 +++++++-------- src/qt/transactiontablemodel.cpp | 10 +++--- src/qt/transactiontablemodel.h | 4 ++- src/qt/transactionview.cpp | 21 +++++++++++++ src/qt/transactionview.h | 3 ++ 10 files changed, 184 insertions(+), 37 deletions(-) diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 25ad0c66a..e047c278b 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -14,6 +14,51 @@ #include #include // for qPow() +// QDoubleSpinBox that shows SI-style thin space thousands separators +class AmountSpinBox: public QDoubleSpinBox +{ +public: + explicit AmountSpinBox(QWidget *parent): + QDoubleSpinBox(parent) + { + } + QString textFromValue(double value) const + { + QStringList parts = QDoubleSpinBox::textFromValue(value).split("."); + QString quotient_str = parts[0]; + QString remainder_str; + if(parts.size() > 1) + remainder_str = parts[1]; + + // Code duplication between here and BitcoinUnits::format + // TODO: Figure out how to share this code + QChar thin_sp(THIN_SP_CP); + int q_size = quotient_str.size(); + if (q_size > 4) + for (int i = 3; i < q_size; i += 3) + quotient_str.insert(q_size - i, thin_sp); + + int r_size = remainder_str.size(); + if (r_size > 4) + for (int i = 3, adj = 0; i < r_size; i += 3, adj++) + remainder_str.insert(i + adj, thin_sp); + + if(remainder_str.isEmpty()) + return quotient_str; + else + return quotient_str + QString(".") + remainder_str; + } + QValidator::State validate (QString &text, int &pos) const + { + QString s(BitcoinUnits::removeSpaces(text)); + return QDoubleSpinBox::validate(s, pos); + } + double valueFromText(const QString& text) const + { + return QDoubleSpinBox::valueFromText(BitcoinUnits::removeSpaces(text)); + } +}; + BitcoinAmountField::BitcoinAmountField(QWidget *parent) : QWidget(parent), amount(0), @@ -21,7 +66,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) : { nSingleStep = 100000; // satoshis - amount = new QDoubleSpinBox(this); + amount = new AmountSpinBox(this); amount->setLocale(QLocale::c()); amount->installEventFilter(this); amount->setMaximumWidth(170); @@ -52,7 +97,7 @@ void BitcoinAmountField::setText(const QString &text) if (text.isEmpty()) amount->clear(); else - amount->setValue(text.toDouble()); + amount->setValue(BitcoinUnits::removeSpaces(text).toDouble()); } void BitcoinAmountField::clear() diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 2fed443cf..1b5eaa2dc 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -50,8 +50,8 @@ QString BitcoinUnits::description(int unit) switch(unit) { case BTC: return QString("Bitcoins"); - case mBTC: return QString("Milli-Bitcoins (1 / 1,000)"); - case uBTC: return QString("Micro-Bitcoins (1 / 1,000,000)"); + case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)"); + case uBTC: return QString("Micro-Bitcoins (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); default: return QString("???"); } } @@ -100,7 +100,7 @@ int BitcoinUnits::decimals(int unit) } } -QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) +QString BitcoinUnits::format(int unit, qint64 n, bool fPlus, SeparatorStyle separators, bool fAlign) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. @@ -119,6 +119,23 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) for (int i = remainder_str.size()-1; i>=2 && (remainder_str.at(i) == '0'); --i) ++nTrim; remainder_str.chop(nTrim); + if (fAlign) + remainder_str.append(QString(QChar(FIGURE_SP_CP)).repeated(nTrim)); + + // Use SI-stule separators as these are locale indendent and can't be + // confused with the decimal marker. Rule is to use a thin space every + // three digits on *both* sides of the decimal point - but only if there + // are five or more digits + 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); + + int r_size = remainder_str.size(); + if (separators == separatorAlways || (separators == separatorStandard && r_size > 4)) + for (int i = 3, adj = 0; i < r_size ; i += 3, adj++) + remainder_str.insert(i + adj, thin_sp); if (n < 0) quotient_str.insert(0, '-'); @@ -127,17 +144,27 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) return quotient_str + QString(".") + remainder_str; } -QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign) +QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators, bool fAlign) { - return format(unit, amount, plussign) + QString(" ") + name(unit); + return format(unit, amount, plussign, separators, fAlign) + QString(" ") + name(unit); } +QString BitcoinUnits::formatHtmlWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators) +{ + QString str(formatWithUnit(unit, amount, plussign, separators)); + str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML)); + return QString("%1").arg(str); +} + + bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) { if(!valid(unit) || value.isEmpty()) return false; // Refuse to parse invalid unit or empty string int num_decimals = decimals(unit); - QStringList parts = value.split("."); + + // Ignore spaces and thin spaces when parsing + QStringList parts = removeSpaces(value).split("."); if(parts.size() > 2) { diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 46517fc07..a3017b9a8 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -8,6 +8,37 @@ #include #include +// U+2009 THIN SPACE = UTF-8 E2 80 89 +#define REAL_THIN_SP_CP 0x2009 +#define REAL_THIN_SP_UTF8 "\xE2\x80\x89" +#define REAL_THIN_SP_HTML " " + +// U+200A HAIR SPACE = UTF-8 E2 80 8A +#define HAIR_SP_CP 0x200A +#define HAIR_SP_UTF8 "\xE2\x80\x8A" +#define HAIR_SP_HTML " " + +// U+2006 SIX-PER-EM SPACE = UTF-8 E2 80 86 +#define SIXPEREM_SP_CP 0x2006 +#define SIXPEREM_SP_UTF8 "\xE2\x80\x86" +#define SIXPEREM_SP_HTML " " + +// U+2007 FIGURE SPACE = UTF-8 E2 80 87 +#define FIGURE_SP_CP 0x2007 +#define FIGURE_SP_UTF8 "\xE2\x80\x87" +#define FIGURE_SP_HTML " " + +// QMessageBox seems to have a bug whereby it doesn't display thin/hair spaces +// correctly. Workaround is to display a space in a small font. If you +// change this, please test that it doesn't cause the parent span to start +// wrapping. +#define HTML_HACK_SP " " + +// Define THIN_SP_* variables to be our preferred type of thin space +#define THIN_SP_CP REAL_THIN_SP_CP +#define THIN_SP_UTF8 REAL_THIN_SP_UTF8 +#define THIN_SP_HTML HTML_HACK_SP + /** Bitcoin unit definitions. Encapsulates parsing and formatting and serves as list model for drop-down selection boxes. */ @@ -28,6 +59,13 @@ public: uBTC }; + enum SeparatorStyle + { + separatorNever, + separatorStandard, + separatorAlways + }; + //! @name Static API //! Unit conversion and formatting ///@{ @@ -49,9 +87,10 @@ public: //! Number of decimals left static int decimals(int unit); //! Format as string - static QString format(int unit, qint64 amount, bool plussign=false); + static QString format(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard, bool fAlign=false); //! Format as string (with unit) - static QString formatWithUnit(int unit, qint64 amount, bool plussign=false); + static QString formatWithUnit(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard, bool fAlign=false); + static QString formatHtmlWithUnit(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard); //! Parse string to coin amount static bool parse(int unit, const QString &value, qint64 *val_out); ///@} @@ -67,6 +106,16 @@ public: QVariant data(const QModelIndex &index, int role) const; ///@} + static QString removeSpaces(QString text) + { + text.remove(' '); + text.remove(QChar(THIN_SP_CP)); +#if (THIN_SP_CP != REAL_THIN_SP_CP) + text.remove(QChar(REAL_THIN_SP_CP)); +#endif + return text; + } + private: QList unitlist; }; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 1a9d1de57..311563d94 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -72,7 +72,7 @@ public: foreground = option.palette.color(QPalette::Text); } painter->setPen(foreground); - QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true); + QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::separatorAlways, true); if(!confirmed) { amountText = QString("[") + amountText + QString("]"); @@ -141,10 +141,10 @@ void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 currentBalance = balance; currentUnconfirmedBalance = unconfirmedBalance; currentImmatureBalance = immatureBalance; - ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance)); - ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance)); - ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance)); - ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance)); + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways, true)); + ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance, false, BitcoinUnits::separatorAlways, true)); + ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance, false, BitcoinUnits::separatorAlways, true)); + ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance, false, BitcoinUnits::separatorAlways, true)); // only show immature (newly mined) balance if it's non-zero, so as not to complicate things // for the non-mining users diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 33621e54b..f432c4add 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -142,7 +142,7 @@ void SendCoinsDialog::on_sendButton_clicked() foreach(const SendCoinsRecipient &rcp, recipients) { // generate bold amount string - QString amount = "" + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); + QString amount = "" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); amount.append(""); // generate monospace address string QString address = "" + rcp.address; @@ -210,7 +210,7 @@ void SendCoinsDialog::on_sendButton_clicked() { // append fee string if a fee is required questionString.append("
"); - questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); + questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); questionString.append(" "); questionString.append(tr("added as transaction fee")); } @@ -222,10 +222,10 @@ void SendCoinsDialog::on_sendButton_clicked() foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits()) { if(u != model->getOptionsModel()->getDisplayUnit()) - alternativeUnits.append(BitcoinUnits::formatWithUnit(u, totalAmount)); + alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount)); } questionString.append(tr("Total Amount %1 (= %2)") - .arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount)) + .arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount)) .arg(alternativeUnits.join(" " + tr("or") + " "))); QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 45fb3d40c..0bb93035c 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -138,7 +138,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u nUnmatured += wallet->GetCredit(txout); strHTML += "" + tr("Credit") + ": "; if (wtx.IsInMainChain()) - strHTML += BitcoinUnits::formatWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; + strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; else strHTML += "(" + tr("not accepted") + ")"; strHTML += "
"; @@ -148,7 +148,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u // // Credit // - strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(unit, nNet) + "
"; + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nNet) + "
"; } else { @@ -184,7 +184,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u } } - strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "
"; + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue) + "
"; } if (fAllToMe) @@ -192,13 +192,13 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u // Payment to self int64_t nChange = wtx.GetChange(); int64_t nValue = nCredit - nChange; - strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(unit, -nValue) + "
"; - strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(unit, nValue) + "
"; + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "
"; + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "
"; } int64_t nTxFee = nDebit - wtx.GetValueOut(); if (nTxFee > 0) - strHTML += "" + tr("Transaction fee") + ": " + BitcoinUnits::formatWithUnit(unit, -nTxFee) + "
"; + strHTML += "" + tr("Transaction fee") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "
"; } else { @@ -207,14 +207,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u // BOOST_FOREACH(const CTxIn& txin, wtx.vin) if (wallet->IsMine(txin)) - strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "
"; + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin)) + "
"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if (wallet->IsMine(txout)) - strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "
"; + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout)) + "
"; } } - strHTML += "" + tr("Net amount") + ": " + BitcoinUnits::formatWithUnit(unit, nNet, true) + "
"; + strHTML += "" + tr("Net amount") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nNet, true) + "
"; // // Message @@ -260,10 +260,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u strHTML += "

" + tr("Debug information") + "

"; BOOST_FOREACH(const CTxIn& txin, wtx.vin) if(wallet->IsMine(txin)) - strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "
"; + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin)) + "
"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if(wallet->IsMine(txout)) - strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "
"; + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout)) + "
"; strHTML += "
" + tr("Transaction") + ":
"; strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); @@ -289,7 +289,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); } - strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(unit, vout.nValue); + strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? tr("true") : tr("false")) + ""; } } diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 8cf2b0a1b..c0f7edd87 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -5,7 +5,6 @@ #include "transactiontablemodel.h" #include "addresstablemodel.h" -#include "bitcoinunits.h" #include "guiconstants.h" #include "guiutil.h" #include "optionsmodel.h" @@ -425,9 +424,9 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const return QVariant(); } -QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const +QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed, BitcoinUnits::SeparatorStyle separators, bool fAlign) const { - QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); + QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit, false, separators, fAlign); if(showUnconfirmed) { if(!wtx->status.countsForBalance) @@ -512,7 +511,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case ToAddress: return formatTxToAddress(rec, false); case Amount: - return formatTxAmount(rec); + return formatTxAmount(rec, true, BitcoinUnits::separatorAlways, true); } break; case Qt::EditRole: @@ -569,7 +568,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case ConfirmedRole: return rec->status.countsForBalance; case FormattedAmountRole: - return formatTxAmount(rec, false); + // Used for copy/export, so don't include separators + return formatTxAmount(rec, false, BitcoinUnits::separatorNever); case StatusRole: return rec->status.status; } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 333e6bc6e..9ee375d78 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -8,6 +8,8 @@ #include #include +#include "bitcoinunits.h" + class TransactionRecord; class TransactionTablePriv; class WalletModel; @@ -78,7 +80,7 @@ private: QString formatTxDate(const TransactionRecord *wtx) const; QString formatTxType(const TransactionRecord *wtx) const; QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const; - QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true) const; + QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::separatorStandard, bool fAlign=false) const; QString formatTooltip(const TransactionRecord *rec) const; QVariant txStatusDecoration(const TransactionRecord *wtx) const; QVariant txAddressDecoration(const TransactionRecord *wtx) const; diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index d4d29416c..98914fc2d 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -123,6 +123,8 @@ TransactionView::TransactionView(QWidget *parent) : view->setTabKeyNavigation(false); view->setContextMenuPolicy(Qt::CustomContextMenu); + view->installEventFilter(this); + transactionView = view; // Actions @@ -480,3 +482,22 @@ void TransactionView::resizeEvent(QResizeEvent* event) QWidget::resizeEvent(event); columnResizingFixer->stretchColumnWidth(TransactionTableModel::ToAddress); } + +// Need to override default Ctrl+C action for amount as default behaviour is just to copy DisplayRole text +bool TransactionView::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) + { + QKeyEvent *ke = static_cast(event); + if (ke->key() == Qt::Key_C && ke->modifiers().testFlag(Qt::ControlModifier)) + { + QModelIndex i = this->transactionView->currentIndex(); + if (i.isValid() && i.column() == TransactionTableModel::Amount) + { + GUIUtil::setClipboard(i.data(TransactionTableModel::FormattedAmountRole).toString()); + return true; + } + } + } + return QWidget::eventFilter(obj, event); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 7a89fa11c..618efbc56 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -8,6 +8,7 @@ #include "guiutil.h" #include +#include class TransactionFilterProxy; class WalletModel; @@ -78,6 +79,8 @@ private: virtual void resizeEvent(QResizeEvent* event); + bool eventFilter(QObject *obj, QEvent *event); + private slots: void contextualMenu(const QPoint &); void dateRangeChanged(); From 2e4fee2ac4824570c1340a8f8fe2aed4580de879 Mon Sep 17 00:00:00 2001 From: Roy Badami Date: Mon, 7 Jul 2014 21:00:58 +0100 Subject: [PATCH 2/4] Show bitcoin quantities with full precision, even in the presence of trailing zeros --- src/qt/bitcoinunits.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 1b5eaa2dc..cf635e194 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -114,14 +114,6 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus, SeparatorStyle sepa QString quotient_str = QString::number(quotient); QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0'); - // 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 (fAlign) - remainder_str.append(QString(QChar(FIGURE_SP_CP)).repeated(nTrim)); - // Use SI-stule separators as these are locale indendent and can't be // confused with the decimal marker. Rule is to use a thin space every // three digits on *both* sides of the decimal point - but only if there From f7d70c603f3b2a664082e0829b92844233c46cb4 Mon Sep 17 00:00:00 2001 From: Roy Badami Date: Mon, 7 Jul 2014 22:27:09 +0100 Subject: [PATCH 3/4] Remove unused fAlign argument from BitcoinUnits::format and friends --- src/qt/bitcoinunits.cpp | 7 ++++--- src/qt/bitcoinunits.h | 4 ++-- src/qt/overviewpage.cpp | 18 +++++++++--------- src/qt/transactiontablemodel.cpp | 6 +++--- src/qt/transactiontablemodel.h | 2 +- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 089abd862..64751c178 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -111,7 +111,7 @@ int BitcoinUnits::decimals(int unit) } } -QString BitcoinUnits::format(int unit, qint64 n, bool fPlus, SeparatorStyle separators, bool fAlign) +QString BitcoinUnits::format(int unit, qint64 n, bool fPlus, SeparatorStyle separators) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. @@ -147,9 +147,10 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus, SeparatorStyle sepa return quotient_str + QString(".") + remainder_str; } -QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators, bool fAlign) + +QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators) { - return format(unit, amount, plussign, separators, fAlign) + QString(" ") + name(unit); + return format(unit, amount, plussign, separators) + QString(" ") + name(unit); } QString BitcoinUnits::formatHtmlWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators) diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index f8c679711..7fa24c854 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -89,9 +89,9 @@ public: //! Number of decimals left static int decimals(int unit); //! Format as string - static QString format(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard, bool fAlign=false); + static QString format(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard); //! Format as string (with unit) - static QString formatWithUnit(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard, bool fAlign=false); + static QString formatWithUnit(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard); static QString formatHtmlWithUnit(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard); //! Parse string to coin amount static bool parse(int unit, const QString &value, qint64 *val_out); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index f51b0311b..1c700b37f 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -72,7 +72,7 @@ public: foreground = option.palette.color(QPalette::Text); } painter->setPen(foreground); - QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::separatorAlways, true); + QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::separatorAlways); if(!confirmed) { amountText = QString("[") + amountText + QString("]"); @@ -147,14 +147,14 @@ void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 currentWatchOnlyBalance = watchOnlyBalance; currentWatchUnconfBalance = watchUnconfBalance; currentWatchImmatureBalance = watchImmatureBalance; - ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways, true)); - ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance, false, BitcoinUnits::separatorAlways, true)); - ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance, false, BitcoinUnits::separatorAlways, true)); - ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance, false, BitcoinUnits::separatorAlways, true)); - ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance, false, BitcoinUnits::separatorAlways, true)); - ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance, false, BitcoinUnits::separatorAlways, true)); - ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance, false, BitcoinUnits::separatorAlways, true)); - ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance, false, BitcoinUnits::separatorAlways, true)); + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways)); + ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance, false, BitcoinUnits::separatorAlways)); + ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance, false, BitcoinUnits::separatorAlways)); + ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance, false, BitcoinUnits::separatorAlways)); // only show immature (newly mined) balance if it's non-zero, so as not to complicate things // for the non-mining users diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index fb21ddc46..cf4c90c7f 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -435,9 +435,9 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const return QVariant(); } -QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed, BitcoinUnits::SeparatorStyle separators, bool fAlign) const +QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed, BitcoinUnits::SeparatorStyle separators) const { - QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit, false, separators, fAlign); + QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit, false, separators); if(showUnconfirmed) { if(!wtx->status.countsForBalance) @@ -522,7 +522,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case ToAddress: return formatTxToAddress(rec, false); case Amount: - return formatTxAmount(rec, true, BitcoinUnits::separatorAlways, true); + return formatTxAmount(rec, true, BitcoinUnits::separatorAlways); } break; case Qt::EditRole: diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 463e7bbff..ad88d14a9 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -80,7 +80,7 @@ private: QString formatTxDate(const TransactionRecord *wtx) const; QString formatTxType(const TransactionRecord *wtx) const; QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const; - QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::separatorStandard, bool fAlign=false) const; + QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::separatorStandard) const; QString formatTooltip(const TransactionRecord *rec) const; QVariant txStatusDecoration(const TransactionRecord *wtx) const; QVariant txAddressDecoration(const TransactionRecord *wtx) const; From 7149499fd85d5adea23c9c3057944c3f2f69a2d2 Mon Sep 17 00:00:00 2001 From: Roy Badami Date: Mon, 7 Jul 2014 22:28:11 +0100 Subject: [PATCH 4/4] Add comments re BitcoinUnits::formatWithUnit/formatHtmlWithUnit --- src/qt/bitcoinunits.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 64751c178..21aed235c 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -148,6 +148,21 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus, SeparatorStyle sepa } +// 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 +// TODO: BtcoinUnits::formatHtmlWithUnit in the latter case). Hopefully +// TODO: there aren't instances where the result could be used in +// TODO: either context. + +// NOTE: Using formatWithUnit in an HTML context risks wrapping +// quantities at the thousands separator. More subtly, it also results +// in a standard space rather than a thin space, due to a bug in Qt's +// XML whitespace canonicalisation +// +// Please take care to use formatHtmlWithUnit instead, when +// appropriate. + QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators) { return format(unit, amount, plussign, separators) + QString(" ") + name(unit);