From 587e52855a4c6c4f672ecec28ab9a029d4e4f850 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 26 Jul 2011 13:08:34 +0200 Subject: allow multiple units in bitcoin amount widget (for example, for sending) using a combobox --- src/qt/bitcoinamountfield.cpp | 71 ++++++++++++++++++++++++++++++++++++++---- src/qt/bitcoinamountfield.h | 24 ++++++++++++--- src/qt/bitcoinunits.cpp | 72 +++++++++++++++++++++++++++++++++++++------ src/qt/bitcoinunits.h | 36 +++++++++++++++++----- src/qt/optionsdialog.cpp | 1 - src/qt/optionsmodel.cpp | 14 ++------- src/qt/optionsmodel.h | 16 +++++----- src/qt/sendcoinsentry.cpp | 12 +++++++- 8 files changed, 197 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 330a7bfdef..7fe28f9a17 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -7,9 +7,10 @@ #include #include #include +#include BitcoinAmountField::BitcoinAmountField(QWidget *parent): - QWidget(parent), amount(0), decimals(0) + QWidget(parent), amount(0), decimals(0), currentUnit(-1) { amount = new QValidatedLineEdit(this); amount->setValidator(new QRegExpValidator(QRegExp("[0-9]*"), this)); @@ -18,7 +19,6 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): amount->setMaximumWidth(100); decimals = new QValidatedLineEdit(this); decimals->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); - decimals->setMaxLength(8); decimals->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); decimals->setMaximumWidth(75); @@ -27,7 +27,9 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): layout->addWidget(amount); layout->addWidget(new QLabel(QString("."))); layout->addWidget(decimals); - layout->addWidget(new QLabel(QString(" ") + BitcoinUnits::name(BitcoinUnits::BTC))); + unit = new QComboBox(this); + unit->setModel(new BitcoinUnits(this)); + layout->addWidget(unit); layout->addStretch(1); layout->setContentsMargins(0,0,0,0); @@ -39,6 +41,10 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): // If one if the widgets changes, the combined content changes as well connect(amount, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged())); connect(decimals, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged())); + connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int))); + + // TODO: set default based on configuration + unitChanged(unit->currentIndex()); } void BitcoinAmountField::setText(const QString &text) @@ -72,17 +78,22 @@ bool BitcoinAmountField::validate() } if(!BitcoinUnits::parse(BitcoinUnits::BTC, text(), 0)) { - amount->setValid(false); - decimals->setValid(false); + setValid(false); valid = false; } return valid; } +void BitcoinAmountField::setValid(bool valid) +{ + amount->setValid(valid); + decimals->setValid(valid); +} + QString BitcoinAmountField::text() const { - if(decimals->text().isEmpty()) + if(decimals->text().isEmpty() && amount->text().isEmpty()) { return QString(); } @@ -111,3 +122,51 @@ QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) QWidget::setTabOrder(amount, decimals); return decimals; } + +qint64 BitcoinAmountField::value(bool *valid_out) const +{ + qint64 val_out = 0; + bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out); + if(valid_out) + { + *valid_out = valid; + } + return val_out; +} + +void BitcoinAmountField::setValue(qint64 value) +{ + setText(BitcoinUnits::format(currentUnit, value)); +} + +void BitcoinAmountField::unitChanged(int idx) +{ + // Use description tooltip for current unit for the combobox + unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString()); + + // Determine new unit ID + int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt(); + + // Parse current value and convert to new unit + bool valid = false; + qint64 currentValue = value(&valid); + + currentUnit = newUnit; + + // Set max length after retrieving the value, to prevent truncation + amount->setMaxLength(BitcoinUnits::amountDigits(currentUnit)); + decimals->setMaxLength(BitcoinUnits::decimals(currentUnit)); + + if(valid) + { + setValue(currentValue); + } + else + { + // If current value is invalid, just clear field + setText(""); + } + setValid(true); + + +} diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index fd09ab2c9b..6e724d06ae 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -5,6 +5,7 @@ QT_BEGIN_NAMESPACE class QValidatedLineEdit; +class QComboBox; QT_END_NAMESPACE // Coin amount entry widget with separate parts for whole @@ -12,15 +13,21 @@ QT_END_NAMESPACE class BitcoinAmountField: public QWidget { Q_OBJECT - Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true); + //Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true); + Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true); public: explicit BitcoinAmountField(QWidget *parent = 0); - void setText(const QString &text); - QString text() const; + qint64 value(bool *valid=0) const; + void setValue(qint64 value); - void clear(); + // Mark current valid as invalid in UI + void setValid(bool valid); bool validate(); + + // Make field empty and ready for new input + void clear(); + // Qt messes up the tab chain by default in some cases (issue http://bugreports.qt.nokia.com/browse/QTBUG-10907) // Hence we have to set it up manually QWidget *setupTabChain(QWidget *prev); @@ -35,6 +42,15 @@ protected: private: QValidatedLineEdit *amount; QValidatedLineEdit *decimals; + QComboBox *unit; + int currentUnit; + + 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 567e51ff39..8414a759ee 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -2,7 +2,22 @@ #include -QString BitcoinUnits::name(BitcoinUnits::Unit unit) +BitcoinUnits::BitcoinUnits(QObject *parent): + QAbstractListModel(parent), + unitlist(availableUnits()) +{ +} + +QList BitcoinUnits::availableUnits() +{ + QList unitlist; + unitlist.append(BTC); + unitlist.append(mBTC); + unitlist.append(uBTC); + return unitlist; +} + +QString BitcoinUnits::name(int unit) { switch(unit) { @@ -13,18 +28,18 @@ QString BitcoinUnits::name(BitcoinUnits::Unit unit) } } -QString BitcoinUnits::description(BitcoinUnits::Unit unit) +QString BitcoinUnits::description(int unit) { switch(unit) { - case BTC: return QString("Bitcoin"); - case mBTC: return QString("Milli-bitcoin (1/1000)"); - case uBTC: return QString("Micro-bitcoin (1/1000,000)"); + case BTC: return QString("Bitcoins"); + case mBTC: return QString("Milli-Bitcoins (1 / 1,000)"); + case uBTC: return QString("Micro-Bitcoins (1 / 1,000,000)"); default: return QString("???"); } } -qint64 BitcoinUnits::factor(BitcoinUnits::Unit unit) +qint64 BitcoinUnits::factor(int unit) { switch(unit) { @@ -35,7 +50,18 @@ qint64 BitcoinUnits::factor(BitcoinUnits::Unit unit) } } -int BitcoinUnits::decimals(BitcoinUnits::Unit unit) +int BitcoinUnits::amountDigits(int unit) +{ + switch(unit) + { + case BTC: return 8; // 21,000,000 + case mBTC: return 11; // 21,000,000,000 + case uBTC: return 14; // 21,000,000,000,000 + default: return 0; + } +} + +int BitcoinUnits::decimals(int unit) { switch(unit) { @@ -46,7 +72,7 @@ int BitcoinUnits::decimals(BitcoinUnits::Unit unit) } } -QString BitcoinUnits::format(BitcoinUnits::Unit unit, qint64 n, bool fPlus) +QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. @@ -71,12 +97,12 @@ QString BitcoinUnits::format(BitcoinUnits::Unit unit, qint64 n, bool fPlus) return quotient_str + QString(".") + remainder_str; } -QString BitcoinUnits::formatWithUnit(BitcoinUnits::Unit unit, qint64 amount, bool plussign) +QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign) { return format(unit, amount, plussign) + QString(" ") + name(unit); } -bool BitcoinUnits::parse(BitcoinUnits::Unit unit, const QString &value, qint64 *val_out) +bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) { int num_decimals = decimals(unit); QStringList parts = value.split("."); @@ -94,3 +120,29 @@ bool BitcoinUnits::parse(BitcoinUnits::Unit unit, const QString &value, qint64 * } return ok; } + +int BitcoinUnits::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return unitlist.size(); +} + +QVariant BitcoinUnits::data(const QModelIndex &index, int role) const +{ + int row = index.row(); + if(row >= 0 && row < unitlist.size()) + { + Unit unit = unitlist.at(row); + switch(role) + { + case Qt::EditRole: + case Qt::DisplayRole: + return QVariant(name(unit)); + case Qt::ToolTipRole: + return QVariant(description(unit)); + case UnitRole: + return QVariant(static_cast(unit)); + } + } + return QVariant(); +} diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index fa85755ad4..18b623517c 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -2,33 +2,53 @@ #define BITCOINUNITS_H #include +#include // Bitcoin unit definitions -class BitcoinUnits +class BitcoinUnits: public QAbstractListModel { public: + explicit BitcoinUnits(QObject *parent); + enum Unit { + // Source: https://en.bitcoin.it/wiki/Units + // Please add only sensible ones BTC, mBTC, uBTC }; + /// Static API + // Get list of units, for dropdown box + static QList availableUnits(); // Short name - static QString name(Unit unit); + static QString name(int unit); // Longer description - static QString description(Unit unit); + static QString description(int unit); // Number of satoshis / unit - static qint64 factor(Unit unit); + static qint64 factor(int unit); + // Number of amount digits (to represent max number of coins) + static int amountDigits(int unit); // Number of decimals left - static int decimals(Unit unit); + static int decimals(int unit); // Format as string - static QString format(Unit unit, qint64 amount, bool plussign=false); + static QString format(int unit, qint64 amount, bool plussign=false); // Format as string (with unit) - static QString formatWithUnit(Unit unit, qint64 amount, bool plussign=false); + static QString formatWithUnit(int unit, qint64 amount, bool plussign=false); // Parse string to coin amount - static bool parse(Unit unit, const QString &value, qint64 *val_out); + static bool parse(int unit, const QString &value, qint64 *val_out); + /// AbstractListModel implementation + enum { + // Unit identifier + UnitRole = Qt::UserRole + } RoleIndex; + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; +private: + QList unitlist; }; +typedef BitcoinUnits::Unit BitcoinUnit; #endif // BITCOINUNITS_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 4f3a82d319..94f7abacf2 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -103,7 +103,6 @@ OptionsDialog::OptionsDialog(QWidget *parent): connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply())); /* Event bindings */ - qDebug() << "setup"; connect(contents_widget, SIGNAL(currentRowChanged(int)), this, SLOT(changePage(int))); connect(buttonbox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(okClicked())); connect(buttonbox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(cancelClicked())); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 8f285c6446..896170975e 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -36,7 +36,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const case ProxyPort: return QVariant(QString::fromStdString(addrProxy.ToStringPort())); case Fee: - return QVariant(QString::fromStdString(FormatMoney(nTransactionFee))); + return QVariant(nTransactionFee); default: return QVariant(); } @@ -104,16 +104,8 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in } break; case Fee: { - int64 retval; - if(ParseMoney(value.toString().toStdString(), retval)) - { - nTransactionFee = retval; - walletdb.WriteSetting("nTransactionFee", nTransactionFee); - } - else - { - successful = false; // Parse error - } + nTransactionFee = value.toLongLong(); + walletdb.WriteSetting("nTransactionFee", nTransactionFee); } break; default: diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index bdb797a2de..4ba44dc23f 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -18,14 +18,14 @@ public: explicit OptionsModel(CWallet *wallet, QObject *parent = 0); enum OptionID { - StartAtStartup, - MinimizeToTray, - MapPortUPnP, - MinimizeOnClose, - ConnectSOCKS4, - ProxyIP, - ProxyPort, - Fee, + StartAtStartup, // bool + MinimizeToTray, // bool + MapPortUPnP, // bool + MinimizeOnClose, // bool + ConnectSOCKS4, // bool + ProxyIP, // QString + ProxyPort, // QString + Fee, // qint64 OptionIDRowCount }; diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 9fc041111f..f3847f1693 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -87,6 +87,16 @@ bool SendCoinsEntry::validate() { retval = false; } + else + { + if(ui->payAmount->value() <= 0) + { + // Cannot send 0 coins or less + ui->payAmount->setValid(false); + retval = false; + } + } + if(!ui->payTo->hasAcceptableInput() || (model && !model->validateAddress(ui->payTo->text()))) @@ -104,7 +114,7 @@ SendCoinsRecipient SendCoinsEntry::getValue() rv.address = ui->payTo->text(); rv.label = ui->addAsLabel->text(); - BitcoinUnits::parse(BitcoinUnits::BTC, ui->payAmount->text(), &rv.amount); + rv.amount = ui->payAmount->value(); return rv; } -- cgit v1.2.3