Browse Source

Merge pull request #4167

7149499 Add comments re BitcoinUnits::formatWithUnit/formatHtmlWithUnit (Roy Badami)
f7d70c6 Remove unused fAlign argument from BitcoinUnits::format and friends (Roy Badami)
2e4fee2 Show bitcoin quantities with full precision, even in the presence of trailing zeros (Roy Badami)
7007402 Implement SI-style (thin space) thoudands separator (Roy Badami)
0.10
Wladimir J. van der Laan 11 years ago
parent
commit
40d2d69223
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 49
      src/qt/bitcoinamountfield.cpp
  2. 57
      src/qt/bitcoinunits.cpp
  3. 53
      src/qt/bitcoinunits.h
  4. 18
      src/qt/overviewpage.cpp
  5. 8
      src/qt/sendcoinsdialog.cpp
  6. 26
      src/qt/transactiondesc.cpp
  7. 10
      src/qt/transactiontablemodel.cpp
  8. 4
      src/qt/transactiontablemodel.h
  9. 21
      src/qt/transactionview.cpp
  10. 3
      src/qt/transactionview.h

49
src/qt/bitcoinamountfield.cpp

@ -14,6 +14,51 @@
#include <QKeyEvent> #include <QKeyEvent>
#include <qmath.h> // for qPow() #include <qmath.h> // 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) : BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
QWidget(parent), QWidget(parent),
amount(0), amount(0),
@ -21,7 +66,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
{ {
nSingleStep = 100000; // satoshis nSingleStep = 100000; // satoshis
amount = new QDoubleSpinBox(this); amount = new AmountSpinBox(this);
amount->setLocale(QLocale::c()); amount->setLocale(QLocale::c());
amount->installEventFilter(this); amount->installEventFilter(this);
amount->setMaximumWidth(170); amount->setMaximumWidth(170);
@ -52,7 +97,7 @@ void BitcoinAmountField::setText(const QString &text)
if (text.isEmpty()) if (text.isEmpty())
amount->clear(); amount->clear();
else else
amount->setValue(text.toDouble()); amount->setValue(BitcoinUnits::removeSpaces(text).toDouble());
} }
void BitcoinAmountField::clear() void BitcoinAmountField::clear()

57
src/qt/bitcoinunits.cpp

@ -61,8 +61,8 @@ QString BitcoinUnits::description(int unit)
switch(unit) switch(unit)
{ {
case BTC: return QString("Bitcoins"); case BTC: return QString("Bitcoins");
case mBTC: return QString("Milli-Bitcoins (1 / 1,000)"); case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)");
case uBTC: return QString("Micro-Bitcoins (1 / 1,000,000)"); case uBTC: return QString("Micro-Bitcoins (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
default: return QString("???"); default: return QString("???");
} }
} }
@ -111,7 +111,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)
{ {
// Note: not using straight sprintf here because we do NOT want // Note: not using straight sprintf here because we do NOT want
// localized number formatting. // localized number formatting.
@ -125,11 +125,20 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus)
QString quotient_str = QString::number(quotient); QString quotient_str = QString::number(quotient);
QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0'); QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0');
// Right-trim excess zeros after the decimal point // Use SI-stule separators as these are locale indendent and can't be
int nTrim = 0; // confused with the decimal marker. Rule is to use a thin space every
for (int i = remainder_str.size()-1; i>=2 && (remainder_str.at(i) == '0'); --i) // three digits on *both* sides of the decimal point - but only if there
++nTrim; // are five or more digits
remainder_str.chop(nTrim); 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) if (n < 0)
quotient_str.insert(0, '-'); quotient_str.insert(0, '-');
@ -138,17 +147,43 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus)
return quotient_str + QString(".") + remainder_str; return quotient_str + QString(".") + remainder_str;
} }
QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign)
// 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);
}
QString BitcoinUnits::formatHtmlWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators)
{ {
return format(unit, amount, plussign) + QString(" ") + name(unit); QString str(formatWithUnit(unit, amount, plussign, separators));
str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML));
return QString("<span style='white-space: nowrap;'>%1</span>").arg(str);
} }
bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out)
{ {
if(!valid(unit) || value.isEmpty()) if(!valid(unit) || value.isEmpty())
return false; // Refuse to parse invalid unit or empty string return false; // Refuse to parse invalid unit or empty string
int num_decimals = decimals(unit); 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) if(parts.size() > 2)
{ {

53
src/qt/bitcoinunits.h

@ -8,6 +8,37 @@
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QString> #include <QString>
// 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 "&thinsp;"
// 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 "&#8202;"
// 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 "&#8198;"
// 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 "&#8199;"
// 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 "<span style='white-space: nowrap; font-size: 6pt'> </span>"
// 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 /** Bitcoin unit definitions. Encapsulates parsing and formatting
and serves as list model for drop-down selection boxes. and serves as list model for drop-down selection boxes.
*/ */
@ -28,6 +59,13 @@ public:
uBTC uBTC
}; };
enum SeparatorStyle
{
separatorNever,
separatorStandard,
separatorAlways
};
//! @name Static API //! @name Static API
//! Unit conversion and formatting //! Unit conversion and formatting
///@{ ///@{
@ -51,9 +89,10 @@ public:
//! Number of decimals left //! Number of decimals left
static int decimals(int unit); static int decimals(int unit);
//! Format as string //! 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);
//! Format as string (with unit) //! 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);
static QString formatHtmlWithUnit(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
//! Parse string to coin amount //! Parse string to coin amount
static bool parse(int unit, const QString &value, qint64 *val_out); static bool parse(int unit, const QString &value, qint64 *val_out);
//! Gets title for amount column including current display unit if optionsModel reference available */ //! Gets title for amount column including current display unit if optionsModel reference available */
@ -71,6 +110,16 @@ public:
QVariant data(const QModelIndex &index, int role) const; 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: private:
QList<BitcoinUnits::Unit> unitlist; QList<BitcoinUnits::Unit> unitlist;
}; };

18
src/qt/overviewpage.cpp

@ -72,7 +72,7 @@ public:
foreground = option.palette.color(QPalette::Text); foreground = option.palette.color(QPalette::Text);
} }
painter->setPen(foreground); painter->setPen(foreground);
QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true); QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::separatorAlways);
if(!confirmed) if(!confirmed)
{ {
amountText = QString("[") + amountText + QString("]"); amountText = QString("[") + amountText + QString("]");
@ -147,14 +147,14 @@ void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64
currentWatchOnlyBalance = watchOnlyBalance; currentWatchOnlyBalance = watchOnlyBalance;
currentWatchUnconfBalance = watchUnconfBalance; currentWatchUnconfBalance = watchUnconfBalance;
currentWatchImmatureBalance = watchImmatureBalance; currentWatchImmatureBalance = watchImmatureBalance;
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance)); ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways));
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance)); ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance, false, BitcoinUnits::separatorAlways));
ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance)); ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance, false, BitcoinUnits::separatorAlways));
ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance)); ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance, false, BitcoinUnits::separatorAlways));
ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance)); ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance, false, BitcoinUnits::separatorAlways));
ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance)); ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance, false, BitcoinUnits::separatorAlways));
ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance)); ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance, false, BitcoinUnits::separatorAlways));
ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance)); 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 // only show immature (newly mined) balance if it's non-zero, so as not to complicate things
// for the non-mining users // for the non-mining users

8
src/qt/sendcoinsdialog.cpp

@ -143,7 +143,7 @@ void SendCoinsDialog::on_sendButton_clicked()
foreach(const SendCoinsRecipient &rcp, recipients) foreach(const SendCoinsRecipient &rcp, recipients)
{ {
// generate bold amount string // generate bold amount string
QString amount = "<b>" + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
amount.append("</b>"); amount.append("</b>");
// generate monospace address string // generate monospace address string
QString address = "<span style='font-family: monospace;'>" + rcp.address; QString address = "<span style='font-family: monospace;'>" + rcp.address;
@ -211,7 +211,7 @@ void SendCoinsDialog::on_sendButton_clicked()
{ {
// append fee string if a fee is required // append fee string if a fee is required
questionString.append("<hr /><span style='color:#aa0000;'>"); questionString.append("<hr /><span style='color:#aa0000;'>");
questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
questionString.append("</span> "); questionString.append("</span> ");
questionString.append(tr("added as transaction fee")); questionString.append(tr("added as transaction fee"));
} }
@ -223,10 +223,10 @@ void SendCoinsDialog::on_sendButton_clicked()
foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits()) foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
{ {
if(u != model->getOptionsModel()->getDisplayUnit()) if(u != model->getOptionsModel()->getDisplayUnit())
alternativeUnits.append(BitcoinUnits::formatWithUnit(u, totalAmount)); alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
} }
questionString.append(tr("Total Amount %1 (= %2)") 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") + " "))); .arg(alternativeUnits.join(" " + tr("or") + " ")));
QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),

26
src/qt/transactiondesc.cpp

@ -136,7 +136,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
nUnmatured += wallet->GetCredit(txout, ISMINE_ALL); nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
strHTML += "<b>" + tr("Credit") + ":</b> "; strHTML += "<b>" + tr("Credit") + ":</b> ";
if (wtx.IsInMainChain()) 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 else
strHTML += "(" + tr("not accepted") + ")"; strHTML += "(" + tr("not accepted") + ")";
strHTML += "<br>"; strHTML += "<br>";
@ -146,7 +146,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// //
// Credit // Credit
// //
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet) + "<br>"; strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nNet) + "<br>";
} }
else else
{ {
@ -197,9 +197,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
} }
} }
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "<br>"; strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue) + "<br>";
if(toSelf) if(toSelf)
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, txout.nValue) + "<br>"; strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue) + "<br>";
} }
if (fAllToMe) if (fAllToMe)
@ -207,13 +207,13 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Payment to self // Payment to self
int64_t nChange = wtx.GetChange(); int64_t nChange = wtx.GetChange();
int64_t nValue = nCredit - nChange; int64_t nValue = nCredit - nChange;
strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>"; strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "<br>";
strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>"; strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>";
} }
int64_t nTxFee = nDebit - wtx.GetValueOut(); int64_t nTxFee = nDebit - wtx.GetValueOut();
if (nTxFee > 0) if (nTxFee > 0)
strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nTxFee) + "<br>"; strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "<br>";
} }
else else
{ {
@ -222,14 +222,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// //
BOOST_FOREACH(const CTxIn& txin, wtx.vin) BOOST_FOREACH(const CTxIn& txin, wtx.vin)
if (wallet->IsMine(txin)) if (wallet->IsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>"; strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout) BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if (wallet->IsMine(txout)) if (wallet->IsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>"; strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
} }
} }
strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet, true) + "<br>"; strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nNet, true) + "<br>";
// //
// Message // Message
@ -275,10 +275,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>"; strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
BOOST_FOREACH(const CTxIn& txin, wtx.vin) BOOST_FOREACH(const CTxIn& txin, wtx.vin)
if(wallet->IsMine(txin)) if(wallet->IsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>"; strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout) BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if(wallet->IsMine(txout)) if(wallet->IsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>"; strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>"; strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
@ -304,7 +304,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); 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) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>"; strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>"; strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>";
} }

10
src/qt/transactiontablemodel.cpp

@ -5,7 +5,6 @@
#include "transactiontablemodel.h" #include "transactiontablemodel.h"
#include "addresstablemodel.h" #include "addresstablemodel.h"
#include "bitcoinunits.h"
#include "guiconstants.h" #include "guiconstants.h"
#include "guiutil.h" #include "guiutil.h"
#include "optionsmodel.h" #include "optionsmodel.h"
@ -436,9 +435,9 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const
return QVariant(); return QVariant();
} }
QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed, BitcoinUnits::SeparatorStyle separators) 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);
if(showUnconfirmed) if(showUnconfirmed)
{ {
if(!wtx->status.countsForBalance) if(!wtx->status.countsForBalance)
@ -523,7 +522,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case ToAddress: case ToAddress:
return formatTxToAddress(rec, false); return formatTxToAddress(rec, false);
case Amount: case Amount:
return formatTxAmount(rec); return formatTxAmount(rec, true, BitcoinUnits::separatorAlways);
} }
break; break;
case Qt::EditRole: case Qt::EditRole:
@ -586,7 +585,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case ConfirmedRole: case ConfirmedRole:
return rec->status.countsForBalance; return rec->status.countsForBalance;
case FormattedAmountRole: case FormattedAmountRole:
return formatTxAmount(rec, false); // Used for copy/export, so don't include separators
return formatTxAmount(rec, false, BitcoinUnits::separatorNever);
case StatusRole: case StatusRole:
return rec->status.status; return rec->status.status;
} }

4
src/qt/transactiontablemodel.h

@ -8,6 +8,8 @@
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QStringList> #include <QStringList>
#include "bitcoinunits.h"
class TransactionRecord; class TransactionRecord;
class TransactionTablePriv; class TransactionTablePriv;
class WalletModel; class WalletModel;
@ -78,7 +80,7 @@ private:
QString formatTxDate(const TransactionRecord *wtx) const; QString formatTxDate(const TransactionRecord *wtx) const;
QString formatTxType(const TransactionRecord *wtx) const; QString formatTxType(const TransactionRecord *wtx) const;
QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) 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) const;
QString formatTooltip(const TransactionRecord *rec) const; QString formatTooltip(const TransactionRecord *rec) const;
QVariant txStatusDecoration(const TransactionRecord *wtx) const; QVariant txStatusDecoration(const TransactionRecord *wtx) const;
QVariant txAddressDecoration(const TransactionRecord *wtx) const; QVariant txAddressDecoration(const TransactionRecord *wtx) const;

21
src/qt/transactionview.cpp

@ -123,6 +123,8 @@ TransactionView::TransactionView(QWidget *parent) :
view->setTabKeyNavigation(false); view->setTabKeyNavigation(false);
view->setContextMenuPolicy(Qt::CustomContextMenu); view->setContextMenuPolicy(Qt::CustomContextMenu);
view->installEventFilter(this);
transactionView = view; transactionView = view;
// Actions // Actions
@ -480,3 +482,22 @@ void TransactionView::resizeEvent(QResizeEvent* event)
QWidget::resizeEvent(event); QWidget::resizeEvent(event);
columnResizingFixer->stretchColumnWidth(TransactionTableModel::ToAddress); 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<QKeyEvent *>(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);
}

3
src/qt/transactionview.h

@ -8,6 +8,7 @@
#include "guiutil.h" #include "guiutil.h"
#include <QWidget> #include <QWidget>
#include <QKeyEvent>
class TransactionFilterProxy; class TransactionFilterProxy;
class WalletModel; class WalletModel;
@ -78,6 +79,8 @@ private:
virtual void resizeEvent(QResizeEvent* event); virtual void resizeEvent(QResizeEvent* event);
bool eventFilter(QObject *obj, QEvent *event);
private slots: private slots:
void contextualMenu(const QPoint &); void contextualMenu(const QPoint &);
void dateRangeChanged(); void dateRangeChanged();

Loading…
Cancel
Save