From a5e6d72339f28699bc356603f695bd620be37e83 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 16 Jul 2011 19:01:05 +0200 Subject: [PATCH 01/12] add sendmany support --- bitcoin-qt.pro | 11 +- doc/assets-attribution.txt | 2 +- src/qt/addresstablemodel.cpp | 28 +++-- src/qt/addresstablemodel.h | 22 ++-- src/qt/bitcoin.qrc | 1 + src/qt/bitcoinaddressvalidator.cpp | 6 + src/qt/bitcoinamountfield.cpp | 33 +++++- src/qt/bitcoinamountfield.h | 10 +- src/qt/editaddressdialog.cpp | 19 ++-- src/qt/forms/sendcoinsdialog.ui | 166 +++------------------------ src/qt/forms/sendcoinsentry.ui | 175 ++++++++++++++++++++++++++++ src/qt/qvalidatedlineedit.cpp | 37 ++++++ src/qt/qvalidatedlineedit.h | 27 +++++ src/qt/sendcoinsdialog.cpp | 176 +++++++++++++++++++---------- src/qt/sendcoinsdialog.h | 14 ++- src/qt/sendcoinsentry.cpp | 119 +++++++++++++++++++ src/qt/sendcoinsentry.h | 45 ++++++++ src/qt/walletmodel.cpp | 91 +++++++++++---- src/qt/walletmodel.h | 31 ++++- 19 files changed, 735 insertions(+), 278 deletions(-) create mode 100644 src/qt/forms/sendcoinsentry.ui create mode 100644 src/qt/qvalidatedlineedit.cpp create mode 100644 src/qt/qvalidatedlineedit.h create mode 100644 src/qt/sendcoinsentry.cpp create mode 100644 src/qt/sendcoinsentry.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 9af4c671..cdff04b4 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -84,7 +84,9 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/overviewpage.h \ src/qt/csvmodelwriter.h \ src/qt/qtwin.h \ - src/crypter.h + src/crypter.h \ + src/qt/sendcoinsentry.h \ + src/qt/qvalidatedlineedit.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -124,7 +126,9 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/overviewpage.cpp \ src/qt/csvmodelwriter.cpp \ src/qt/qtwin.cpp \ - src/crypter.cpp + src/crypter.cpp \ + src/qt/sendcoinsentry.cpp \ + src/qt/qvalidatedlineedit.cpp RESOURCES += \ src/qt/bitcoin.qrc @@ -135,7 +139,8 @@ FORMS += \ src/qt/forms/aboutdialog.ui \ src/qt/forms/editaddressdialog.ui \ src/qt/forms/transactiondescdialog.ui \ - src/qt/forms/overviewpage.ui + src/qt/forms/overviewpage.ui \ + src/qt/forms/sendcoinsentry.ui CODECFORTR = UTF-8 TRANSLATIONS = src/qt/locale/bitcoin_nl.ts src/qt/locale/bitcoin_de.ts diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index f58b0da4..f3900fe0 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -29,7 +29,7 @@ License: You are free to do with these icons as you wish, including selling, Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png, src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png, src/qt/res/icons/add.png, src/qt/res/icons/edit.png, - src/qt/res/icons/editdelete.png + src/qt/res/icons/editdelete.png, src/qt/res/icons/remove.png (edited) Designer: http://www.everaldo.com Icon Pack: Crystal SVG License: LGPL diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 4578ca74..125ceebb 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -1,5 +1,6 @@ #include "addresstablemodel.h" #include "guiutil.h" +#include "walletmodel.h" #include "headers.h" @@ -72,8 +73,8 @@ struct AddressTablePriv } }; -AddressTableModel::AddressTableModel(CWallet *wallet, QObject *parent) : - QAbstractTableModel(parent),wallet(wallet),priv(0) +AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : + QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0) { columns << tr("Label") << tr("Address"); priv = new AddressTablePriv(wallet); @@ -150,6 +151,8 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu return false; AddressTableEntry *rec = static_cast(index.internalPointer()); + editStatus = OK; + if(role == Qt::EditRole) { switch(index.column()) @@ -160,8 +163,11 @@ bool AddressTableModel::setData(const QModelIndex & index, const QVariant & valu break; case Address: // Refuse to set invalid address - if(!validateAddress(value.toString())) + if(!walletModel->validateAddress(value.toString())) + { + editStatus = INVALID_ADDRESS; return false; + } // Double-check that we're not overwriting receiving address if(rec->type == AddressTableEntry::Sending) { @@ -240,13 +246,22 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); + editStatus = OK; + + if(type == Send) { + if(!walletModel->validateAddress(address)) + { + editStatus = INVALID_ADDRESS; + return QString(); + } // Check for duplicate CRITICAL_BLOCK(wallet->cs_mapAddressBook) { if(wallet->mapAddressBook.count(strAddress)) { + editStatus = DUPLICATE_ADDRESS; return QString(); } } @@ -291,13 +306,6 @@ void AddressTableModel::update() } -bool AddressTableModel::validateAddress(const QString &address) -{ - uint160 hash160 = 0; - - return AddressToHash160(address.toStdString(), hash160); -} - /* Look up label for address in address book, if not found return empty string. */ QString AddressTableModel::labelForAddress(const QString &address) const diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index d48e7866..296fa580 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -6,12 +6,13 @@ class AddressTablePriv; class CWallet; +class WalletModel; class AddressTableModel : public QAbstractTableModel { Q_OBJECT public: - explicit AddressTableModel(CWallet *wallet, QObject *parent = 0); + explicit AddressTableModel(CWallet *wallet, WalletModel *parent = 0); ~AddressTableModel(); enum ColumnIndex { @@ -19,9 +20,16 @@ public: Address = 1 /* Bitcoin address */ }; - enum { + enum RoleIndex { TypeRole = Qt::UserRole - } RoleIndex; + }; + + // Return status of last edit/insert operation + enum EditStatus { + OK = 0, + INVALID_ADDRESS = 1, + DUPLICATE_ADDRESS = 2 + }; static const QString Send; /* Send addres */ static const QString Receive; /* Receive address */ @@ -45,10 +53,6 @@ public: */ void updateList(); - /* Check address for validity - */ - bool validateAddress(const QString &address); - /* Look up label for address in address book, if not found return empty string. */ QString labelForAddress(const QString &address) const; @@ -58,10 +62,14 @@ public: */ int lookupAddress(const QString &address) const; + EditStatus getEditStatus() const { return editStatus; } + private: + WalletModel *walletModel; CWallet *wallet; AddressTablePriv *priv; QStringList columns; + EditStatus editStatus; signals: void defaultAddressChanged(const QString &address); diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 1522ce61..9ef8b2af 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -31,6 +31,7 @@ res/icons/export.png res/icons/synced.png res/icons/notsynced.png + res/icons/remove.png res/images/about.png diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index 4308a893..37387780 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -57,5 +57,11 @@ QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) co } } + // Empty address is "intermediate" input + if(input.isEmpty()) + { + state = QValidator::Intermediate; + } + return state; } diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 1359a32b..d545dc52 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -1,4 +1,5 @@ #include "bitcoinamountfield.h" +#include "qvalidatedlineedit.h" #include #include @@ -9,12 +10,12 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): QWidget(parent), amount(0), decimals(0) { - amount = new QLineEdit(this); + amount = new QValidatedLineEdit(this); amount->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); amount->setAlignment(Qt::AlignRight|Qt::AlignVCenter); amount->installEventFilter(this); amount->setMaximumWidth(100); - decimals = new QLineEdit(this); + decimals = new QValidatedLineEdit(this); decimals->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this)); decimals->setMaxLength(8); decimals->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); @@ -29,8 +30,9 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent): layout->addStretch(1); layout->setContentsMargins(0,0,0,0); - setFocusPolicy(Qt::TabFocus); setLayout(layout); + + setFocusPolicy(Qt::TabFocus); setFocusProxy(amount); // If one if the widgets changes, the combined content changes as well @@ -53,10 +55,28 @@ void BitcoinAmountField::setText(const QString &text) } } +bool BitcoinAmountField::validate() +{ + bool valid = true; + if(amount->text().isEmpty()) + { + amount->setValid(false); + valid = false; + } + if(decimals->text().isEmpty()) + { + decimals->setValid(false); + valid = false; + } + return valid; +} + QString BitcoinAmountField::text() const { if(amount->text().isEmpty() || decimals->text().isEmpty()) + { return QString(); + } return amount->text() + QString(".") + decimals->text(); } @@ -75,3 +95,10 @@ bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) } return false; } + +QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) +{ + QWidget::setTabOrder(prev, amount); + QWidget::setTabOrder(amount, decimals); + return decimals; +} diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 67304c8b..2a0ef4bd 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -4,7 +4,7 @@ #include QT_BEGIN_NAMESPACE -class QLineEdit; +class QValidatedLineEdit; QT_END_NAMESPACE // Coin amount entry widget with separate parts for whole @@ -18,6 +18,10 @@ public: void setText(const QString &text); QString text() const; + bool validate(); + // 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); signals: void textChanged(); @@ -27,8 +31,8 @@ protected: bool eventFilter(QObject *object, QEvent *event); private: - QLineEdit *amount; - QLineEdit *decimals; + QValidatedLineEdit *amount; + QValidatedLineEdit *decimals; }; diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 7ea5638b..a0b27e83 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -79,23 +79,22 @@ QString EditAddressDialog::saveCurrentRow() void EditAddressDialog::accept() { - if(mode == NewSendingAddress || mode == EditSendingAddress) + if(saveCurrentRow().isEmpty()) { - // For sending addresses, check validity - // Not needed for receiving addresses, as those are generated - if(!model->validateAddress(ui->addressEdit->text())) + switch(model->getEditStatus()) { + case AddressTableModel::DUPLICATE_ADDRESS: + QMessageBox::warning(this, windowTitle(), + tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), + QMessageBox::Ok, QMessageBox::Ok); + break; + case AddressTableModel::INVALID_ADDRESS: QMessageBox::warning(this, windowTitle(), tr("The entered address \"%1\" is not a valid bitcoin address.").arg(ui->addressEdit->text()), QMessageBox::Ok, QMessageBox::Ok); return; } - } - if(saveCurrentRow().isEmpty()) - { - QMessageBox::warning(this, windowTitle(), - tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()), - QMessageBox::Ok, QMessageBox::Ok); + return; } QDialog::accept(); diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 8009bd2b..57b79279 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -15,145 +15,10 @@ - - - Qt::Vertical - - - QSizePolicy::Preferred - - - - 20 - 12 - - - - - - + - 12 + 6 - - - - A&mount: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - payAmount - - - - - - - Pay &To: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - payTo - - - - - - - - - - 0 - - - - - true - - - Enter a label for this address to add it to your address book - - - - - - - - - &Label: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - addAsLabel - - - - - - - 0 - - - - - The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) - - - 34 - - - - - - - Look up adress in address book - - - - - - - :/icons/address-book:/icons/address-book - - - Alt+A - - - false - - - false - - - - - - - Paste address from system clipboard - - - - - - - :/icons/editpaste:/icons/editpaste - - - Alt+P - - - false - - - - - @@ -174,6 +39,17 @@ + + + + &Add recipient... + + + + :/icons/add:/icons/add + + + @@ -214,22 +90,6 @@ - - - BitcoinAmountField - QLineEdit -
bitcoinamountfield.h
- 1 -
-
- - payTo - addressBookButton - pasteButton - addAsLabel - payAmount - sendButton - diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui new file mode 100644 index 00000000..1159ef53 --- /dev/null +++ b/src/qt/forms/sendcoinsentry.ui @@ -0,0 +1,175 @@ + + + SendCoinsEntry + + + + 0 + 0 + 729 + 136 + + + + Form + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + 12 + + + + + A&mount: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payAmount + + + + + + + Pay &To: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + payTo + + + + + + + + + + 0 + + + + + true + + + Enter a label for this address to add it to your address book + + + + + + + + + &Label: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + addAsLabel + + + + + + + 0 + + + + + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + 34 + + + + + + + Look up adress in address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + false + + + + + + + Paste address from system clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + false + + + + + + + + + + + :/icons/res/icons/remove.png:/icons/res/icons/remove.png + + + + + + + + + + BitcoinAmountField + QLineEdit +
bitcoinamountfield.h
+ 1 +
+ + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + + + +
diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp new file mode 100644 index 00000000..4b5acd8b --- /dev/null +++ b/src/qt/qvalidatedlineedit.cpp @@ -0,0 +1,37 @@ +#include "qvalidatedlineedit.h" + +QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) : + QLineEdit(parent), valid(true) +{ + connect(this, SIGNAL(textChanged(QString)), this, SLOT(markValid())); +} + +void QValidatedLineEdit::setValid(bool valid) +{ + if(valid == this->valid) + { + return; + } + + if(valid) + { + setStyleSheet(""); + } + else + { + setStyleSheet("background:#FF8080"); + } + this->valid = valid; +} + +void QValidatedLineEdit::focusInEvent(QFocusEvent *evt) +{ + // Clear invalid flag on focus + setValid(true); + QLineEdit::focusInEvent(evt); +} + +void QValidatedLineEdit::markValid() +{ + setValid(true); +} diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h new file mode 100644 index 00000000..9fc026fa --- /dev/null +++ b/src/qt/qvalidatedlineedit.h @@ -0,0 +1,27 @@ +#ifndef QVALIDATEDLINEEDIT_H +#define QVALIDATEDLINEEDIT_H + +#include + +// Line edit that can be marked as "invalid". When marked as invalid, +// it will get a red background until it is focused. +class QValidatedLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit QValidatedLineEdit(QWidget *parent = 0); + +protected: + void focusInEvent(QFocusEvent *evt); + +private: + bool valid; + +public slots: + void setValid(bool valid); + +private slots: + void markValid(); +}; + +#endif // QVALIDATEDLINEEDIT_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 01d68dc0..38a0a655 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,43 +1,40 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" #include "walletmodel.h" -#include "addresstablemodel.h" #include "guiutil.h" - #include "addressbookpage.h" #include "optionsmodel.h" +#include "sendcoinsentry.h" + -#include -#include #include #include #include -#include -SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : +SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SendCoinsDialog), model(0) { ui->setupUi(this); -#if QT_VERSION >= 0x040700 - ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); - ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); -#endif - GUIUtil::setupAddressWidget(ui->payTo, this); + addEntry(); - // Set initial send-to address if provided - if(!address.isEmpty()) - { - ui->payTo->setText(address); - ui->payAmount->setFocus(); - } + connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry())); } void SendCoinsDialog::setModel(WalletModel *model) { this->model = model; + + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + entry->setModel(model); + } + } } SendCoinsDialog::~SendCoinsDialog() @@ -47,26 +44,38 @@ SendCoinsDialog::~SendCoinsDialog() void SendCoinsDialog::on_sendButton_clicked() { - bool valid; - QString payAmount = ui->payAmount->text(); - QString label; - qint64 payAmountParsed; - - valid = GUIUtil::parseMoney(payAmount, &payAmountParsed); + QList recipients; + bool valid = true; + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + if(entry->validate()) + { + recipients.append(entry->getValue()); + } + else + { + valid = false; + } + } + } - if(!valid || payAmount.isEmpty()) + if(!valid || recipients.isEmpty()) { - QMessageBox::warning(this, tr("Send Coins"), - tr("Must fill in an amount to pay."), - QMessageBox::Ok, QMessageBox::Ok); return; } - // Add address to address book under label, if specified - label = ui->addAsLabel->text(); + // Format confirmation message + QStringList formatted; + foreach(const SendCoinsRecipient &rcp, recipients) + { + formatted.append(tr("%1 BTC to %2 (%3)").arg(GUIUtil::formatMoney(rcp.amount), rcp.label, rcp.address)); + } QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), - tr("Are you sure you want to send %1 BTC to %2 (%3)?").arg(GUIUtil::formatMoney(payAmountParsed), label, ui->payTo->text()), + tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))), QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Cancel); @@ -75,32 +84,45 @@ void SendCoinsDialog::on_sendButton_clicked() return; } - switch(model->sendCoins(ui->payTo->text(), payAmountParsed, label)) + WalletModel::SendCoinsReturn sendstatus = model->sendCoins(recipients); + switch(sendstatus.status) { case WalletModel::InvalidAddress: QMessageBox::warning(this, tr("Send Coins"), tr("The recepient address is not valid, please recheck."), QMessageBox::Ok, QMessageBox::Ok); - ui->payTo->setFocus(); break; case WalletModel::InvalidAmount: QMessageBox::warning(this, tr("Send Coins"), tr("The amount to pay must be larger than 0."), QMessageBox::Ok, QMessageBox::Ok); - ui->payAmount->setFocus(); break; case WalletModel::AmountExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Amount exceeds your balance"), QMessageBox::Ok, QMessageBox::Ok); - ui->payAmount->setFocus(); break; case WalletModel::AmountWithFeeExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Total exceeds your balance when the %1 transaction fee is included"). - arg(GUIUtil::formatMoney(model->getOptionsModel()->getTransactionFee())), + arg(GUIUtil::formatMoney(sendstatus.fee)), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::DuplicateAddress: + QMessageBox::warning(this, tr("Send Coins"), + tr("Duplicate address found, can only send to each address once in one send operation"), + QMessageBox::Ok, QMessageBox::Ok); + break; + case WalletModel::TransactionCreationFailed: + QMessageBox::warning(this, tr("Send Coins"), + tr("Error: Transaction creation failed "), + QMessageBox::Ok, QMessageBox::Ok); + break; + break; + case WalletModel::TransactionCommitFailed: + QMessageBox::warning(this, tr("Send Coins"), + tr("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."), QMessageBox::Ok, QMessageBox::Ok); - ui->payAmount->setFocus(); break; case WalletModel::OK: accept(); @@ -108,34 +130,23 @@ void SendCoinsDialog::on_sendButton_clicked() } } -void SendCoinsDialog::on_pasteButton_clicked() +void SendCoinsDialog::clear() { - // Paste text from clipboard into recipient field - ui->payTo->setText(QApplication::clipboard()->text()); -} + // Remove entries until only one left + while(ui->entries->count() > 1) + { + delete ui->entries->takeAt(0)->widget(); + } -void SendCoinsDialog::on_addressBookButton_clicked() -{ - AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); - dlg.setModel(model->getAddressTableModel()); - if(dlg.exec()) + // Reset the entry that is left to empty + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(0)->widget()); + if(entry) { - ui->payTo->setText(dlg.getReturnValue()); - ui->payAmount->setFocus(); + entry->clear(); } -} -void SendCoinsDialog::on_payTo_textChanged(const QString &address) -{ - ui->addAsLabel->setText(model->getAddressTableModel()->labelForAddress(address)); -} + updateRemoveEnabled(); -void SendCoinsDialog::clear() -{ - ui->payTo->setText(QString()); - ui->addAsLabel->setText(QString()); - ui->payAmount->setText(QString()); - ui->payTo->setFocus(); ui->sendButton->setDefault(true); } @@ -148,3 +159,52 @@ void SendCoinsDialog::accept() { clear(); } + +void SendCoinsDialog::addEntry() +{ + SendCoinsEntry *entry = new SendCoinsEntry(this); + entry->setModel(model); + ui->entries->addWidget(entry); + connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*))); + + updateRemoveEnabled(); + + // Focus the field, so that entry can start immediately + entry->clear(); +} + +void SendCoinsDialog::updateRemoveEnabled() +{ + // Remove buttons are enabled as soon as there is more than one send-entry + bool enabled = (ui->entries->count() > 1); + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + entry->setRemoveEnabled(enabled); + } + } + setupTabChain(0); +} + +void SendCoinsDialog::removeEntry(SendCoinsEntry* entry) +{ + delete entry; + updateRemoveEnabled(); +} + +QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) +{ + for(int i = 0; i < ui->entries->count(); ++i) + { + SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); + if(entry) + { + prev = entry->setupTabChain(prev); + } + } + QWidget::setTabOrder(prev, ui->addButton); + QWidget::setTabOrder(ui->addButton, ui->sendButton); + return ui->sendButton; +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 46814af4..0f90be81 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -7,31 +7,37 @@ namespace Ui { class SendCoinsDialog; } class WalletModel; +class SendCoinsEntry; class SendCoinsDialog : public QDialog { Q_OBJECT public: - explicit SendCoinsDialog(QWidget *parent = 0, const QString &address = ""); + explicit SendCoinsDialog(QWidget *parent = 0); ~SendCoinsDialog(); void setModel(WalletModel *model); + // 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); + public slots: void clear(); void reject(); void accept(); + void addEntry(); + void updateRemoveEnabled(); private: Ui::SendCoinsDialog *ui; WalletModel *model; private slots: - void on_payTo_textChanged(const QString &address); - void on_addressBookButton_clicked(); - void on_pasteButton_clicked(); void on_sendButton_clicked(); + + void removeEntry(SendCoinsEntry* entry); }; #endif // SENDCOINSDIALOG_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp new file mode 100644 index 00000000..6e87e9cf --- /dev/null +++ b/src/qt/sendcoinsentry.cpp @@ -0,0 +1,119 @@ +#include "sendcoinsentry.h" +#include "ui_sendcoinsentry.h" +#include "guiutil.h" +#include "addressbookpage.h" +#include "walletmodel.h" +#include "addresstablemodel.h" + +#include "qapplication.h" +#include "qclipboard.h" + +#include + +SendCoinsEntry::SendCoinsEntry(QWidget *parent) : + QFrame(parent), + ui(new Ui::SendCoinsEntry), + model(0) +{ + ui->setupUi(this); + +#if QT_VERSION >= 0x040700 + ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); + ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); +#endif + setFocusPolicy(Qt::TabFocus); + setFocusProxy(ui->payTo); + + GUIUtil::setupAddressWidget(ui->payTo, this); +} + +SendCoinsEntry::~SendCoinsEntry() +{ + delete ui; +} + +void SendCoinsEntry::on_pasteButton_clicked() +{ + // Paste text from clipboard into recipient field + ui->payTo->setText(QApplication::clipboard()->text()); +} + +void SendCoinsEntry::on_addressBookButton_clicked() +{ + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); + dlg.setModel(model->getAddressTableModel()); + if(dlg.exec()) + { + ui->payTo->setText(dlg.getReturnValue()); + ui->payAmount->setFocus(); + } +} + +void SendCoinsEntry::on_payTo_textChanged(const QString &address) +{ + ui->addAsLabel->setText(model->getAddressTableModel()->labelForAddress(address)); +} + +void SendCoinsEntry::setModel(WalletModel *model) +{ + this->model = model; +} + +void SendCoinsEntry::setRemoveEnabled(bool enabled) +{ + ui->deleteButton->setEnabled(enabled); +} + +void SendCoinsEntry::clear() +{ + ui->payTo->clear(); + ui->addAsLabel->clear(); + ui->payAmount->setText(QString()); + ui->payTo->setFocus(); +} + +void SendCoinsEntry::on_deleteButton_clicked() +{ + emit removeEntry(this); +} + +bool SendCoinsEntry::validate() +{ + // Check input validity + bool retval = true; + + if(!ui->payAmount->validate()) + { + retval = false; + } + + if(!ui->payTo->hasAcceptableInput() || + (model && !model->validateAddress(ui->payTo->text()))) + { + ui->payTo->setValid(false); + retval = false; + } + + return retval; +} + +SendCoinsRecipient SendCoinsEntry::getValue() +{ + SendCoinsRecipient rv; + + rv.address = ui->payTo->text(); + rv.label = ui->addAsLabel->text(); + GUIUtil::parseMoney(ui->payAmount->text(), &rv.amount); + + return rv; +} + +QWidget *SendCoinsEntry::setupTabChain(QWidget *prev) +{ + QWidget::setTabOrder(prev, ui->payTo); + QWidget::setTabOrder(ui->payTo, ui->addressBookButton); + QWidget::setTabOrder(ui->addressBookButton, ui->pasteButton); + QWidget::setTabOrder(ui->pasteButton, ui->deleteButton); + QWidget::setTabOrder(ui->deleteButton, ui->addAsLabel); + return ui->payAmount->setupTabChain(ui->addAsLabel); +} diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h new file mode 100644 index 00000000..55fd12a1 --- /dev/null +++ b/src/qt/sendcoinsentry.h @@ -0,0 +1,45 @@ +#ifndef SENDCOINSENTRY_H +#define SENDCOINSENTRY_H + +#include + +namespace Ui { + class SendCoinsEntry; +} +class WalletModel; +class SendCoinsRecipient; + +class SendCoinsEntry : public QFrame +{ + Q_OBJECT + +public: + explicit SendCoinsEntry(QWidget *parent = 0); + ~SendCoinsEntry(); + + void setModel(WalletModel *model); + bool validate(); + SendCoinsRecipient getValue(); + // 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); + +public slots: + void setRemoveEnabled(bool enabled); + void clear(); + +signals: + void removeEntry(SendCoinsEntry *entry); + +private slots: + void on_deleteButton_clicked(); + void on_payTo_textChanged(const QString &address); + void on_addressBookButton_clicked(); + void on_pasteButton_clicked(); + +private: + Ui::SendCoinsEntry *ui; + WalletModel *model; +}; + +#endif // SENDCOINSENTRY_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index afe095c9..6e4b814d 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -7,6 +7,7 @@ #include "headers.h" #include +#include WalletModel::WalletModel(CWallet *wallet, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(0), addressTableModel(0), @@ -54,63 +55,105 @@ void WalletModel::update() addressTableModel->update(); } -WalletModel::StatusCode WalletModel::sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs) +bool WalletModel::validateAddress(const QString &address) { uint160 hash160 = 0; - bool valid = false; - if(!AddressToHash160(payTo.toUtf8().constData(), hash160)) + return AddressToHash160(address.toStdString(), hash160); +} + +WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList &recipients) +{ + qint64 total = 0; + QSet setAddress; + QString hex; + + if(recipients.empty()) { - return InvalidAddress; + return OK; } - if(payAmount <= 0) + // Pre-check input data for validity + foreach(const SendCoinsRecipient &rcp, recipients) { - return InvalidAmount; + uint160 hash160 = 0; + + if(!AddressToHash160(rcp.address.toUtf8().constData(), hash160)) + { + return InvalidAddress; + } + setAddress.insert(rcp.address); + + if(rcp.amount <= 0) + { + return InvalidAmount; + } + total += rcp.amount; } - if(payAmount > getBalance()) + if(recipients.size() > setAddress.size()) + { + return DuplicateAddress; + } + + if(total > getBalance()) { return AmountExceedsBalance; } - if((payAmount + nTransactionFee) > getBalance()) + if((total + nTransactionFee) > getBalance()) { - return AmountWithFeeExceedsBalance; + return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); } CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(wallet->cs_mapWallet) { - // Send to bitcoin address + // Sendmany + std::vector > vecSend; + foreach(const SendCoinsRecipient &rcp, recipients) + { + CScript scriptPubKey; + scriptPubKey.SetBitcoinAddress(rcp.address.toStdString()); + vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); + } + CWalletTx wtx; - CScript scriptPubKey; - scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; + CReserveKey keyChange(wallet); + int64 nFeeRequired = 0; + bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); - std::string strError = wallet->SendMoney(scriptPubKey, payAmount, wtx, true); - if (strError == "") + if(!fCreated) { - // OK + if((total + nFeeRequired) > wallet->GetBalance()) + { + return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); + } + return TransactionCreationFailed; } - else if (strError == "ABORTED") + if(!ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString(), NULL)) { return Aborted; } - else + if(!wallet->CommitTransaction(wtx, keyChange)) { - emit error(tr("Sending..."), QString::fromStdString(strError)); - return MiscError; + return TransactionCommitFailed; } + hex = QString::fromStdString(wtx.GetHash().GetHex()); } // Add addresses that we've sent to to the address book - std::string strAddress = payTo.toStdString(); - CRITICAL_BLOCK(wallet->cs_mapAddressBook) + foreach(const SendCoinsRecipient &rcp, recipients) { - if (!wallet->mapAddressBook.count(strAddress)) - wallet->SetAddressBookName(strAddress, addToAddressBookAs.toStdString()); + std::string strAddress = rcp.address.toStdString(); + CRITICAL_BLOCK(wallet->cs_mapAddressBook) + { + if (!wallet->mapAddressBook.count(strAddress)) + wallet->SetAddressBookName(strAddress, rcp.label.toStdString()); + } } - return OK; + return SendCoinsReturn(OK, 0, hex); } OptionsModel *WalletModel::getOptionsModel() diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 1105fb03..af2cac4b 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -8,6 +8,13 @@ class AddressTableModel; class TransactionTableModel; class CWallet; +struct SendCoinsRecipient +{ + QString address; + QString label; + qint64 amount; +}; + // Interface to a Bitcoin wallet class WalletModel : public QObject { @@ -22,6 +29,9 @@ public: InvalidAddress, AmountExceedsBalance, AmountWithFeeExceedsBalance, + DuplicateAddress, + TransactionCreationFailed, + TransactionCommitFailed, Aborted, MiscError }; @@ -34,8 +44,25 @@ public: qint64 getUnconfirmedBalance() const; int getNumTransactions() const; - /* Send coins */ - StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); + // Check address for validity + bool validateAddress(const QString &address); + + // Return status record for SendCoins + // fee is used in case status is "AmountWithFeeExceedsBalance" + // hex is filled with the transaction hash if status is "OK" + struct SendCoinsReturn + { + SendCoinsReturn(StatusCode status, + qint64 fee=0, + QString hex=QString()): + status(status), fee(fee), hex(hex) {} + StatusCode status; + qint64 fee; + QString hex; + }; + + // Send coins to list of recipients + SendCoinsReturn sendCoins(const QList &recipients); private: CWallet *wallet; From 9958e09dbc90c8a81a16be5de8c9bc62a00615d5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 16 Jul 2011 19:28:15 +0200 Subject: [PATCH 02/12] Revert "Now that send coins / receive coins etc are tabs, remove them from menu, and reorganize menu bar" This reverts commit ea37fb9187195d746a4b8a84da5cb6bd9e0e4aab. --- src/qt/bitcoingui.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 9ee178ac..016f2619 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -55,10 +55,14 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Menus QMenu *file = menuBar()->addMenu("&File"); - file->addAction(optionsAction); + file->addAction(sendCoinsAction); + file->addAction(receiveCoinsAction); file->addSeparator(); file->addAction(quitAction); + QMenu *settings = menuBar()->addMenu("&Settings"); + settings->addAction(optionsAction); + QMenu *help = menuBar()->addMenu("&Help"); help->addAction(aboutAction); From 9b9cd3dd2011d78c1b85bc7ddae6e193e9e84a62 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 16 Jul 2011 19:35:41 +0200 Subject: [PATCH 03/12] add missing icon --- src/qt/res/icons/remove.png | Bin 0 -> 1224 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/qt/res/icons/remove.png diff --git a/src/qt/res/icons/remove.png b/src/qt/res/icons/remove.png new file mode 100644 index 0000000000000000000000000000000000000000..a44b6d130b5aeeccc3641a6c0b831d83d9c03235 GIT binary patch literal 1224 zcmV;(1ULJMP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipe_ z3ndRC4ewL{00c-$L_t(o!?l;oZyZGshrjCXnb{p@AHhOFh(km`+z5&kaX{q6Kaf9z z1Ls7DBNECXaNz{v0A~bek}DyJ0up&3L=r_750Tge<7e#c?kWzmUXQUMHp;d%>fTaM z*H^#luIUvsW52EXtN;xa5xGAfdX%y)wLB?-df-Hnqj{ai%$cmkDcbs@iUaJ zL@|_1B9llYNn|pY=Dud|?hH%stUXx(1WwGJpd%fUB@&IpL?T%tOEW(D)KL!7_6=Zw znljSS*twF3lRnuPN7Hlw1jw36+C<-0Ad*mW)2mJg0BZoY705fJ-?C-P(*h8(W}?Pj zdq72r#0Ee!V$(>0f@&t_xWoCRA1je_8Yk?D0|+T^E1;5zTS~|_M7G@Q*r_OxGck`^ z(T`8SmPN*=-~a&fVcC7D>h(T)_SEaIq6*r)Z8jdOVwO-<35WWIY6XDWWRBVPJTn{J zxpeJEFS?<46cAAq-#8ps-Yl}dD7+cexpS|5@cya8FTQ}PVlkm9VWw!xSk92rq$fp4 zU5)0a&^AEq`6PiUSYdu30+-f;j0U*UBA&p*>co|a4j3soRpBwEXRoDv@7bJs*E9YtU$_aK3HJ!@;5A9 zzk!c&N961QktOc1DGAN;#mw^UJ1ktj%KF{gI1JyCSN{Wo zNjh2zBp`2YHaq`cqPCoPwD6F{pKf#i%2h^xJj4MWPauQXjvR+^DN6rX?X+)>ytb$MW3Itj^7`^2@KpwYA-!ClRo{A-4K9L=-4m5DJ7W!Ve#R z!rjlmAapud&aBVRKem8q<82p&dkjRL*osC069qh*i;HY>u(lUO7WT}4+GB5Mvj_ae zW!0Apkskl6wd<}cfLIE611C-8bCPg?LkJ>>87od!qHs~njJNxP6ZeKJ6pM;FkK(FXg7d2hqWg|bD z(v_^$dS#!%7+_cp8B_yi!VKM_W+g387oc<{y`o3I>@%na+e(yu>Y~Pv{B!}jp{6cs zW{Q~xqy^C{d*qEgU4Txg==$yiM(UtPY-|y4u=;{aAEQkzJZx3YUY>c545Bl_6%<2LF0000 Date: Sat, 16 Jul 2011 19:45:37 +0200 Subject: [PATCH 04/12] update readme --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 16d03c3a..dc9a8b15 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ Bitcoin-qt: Qt4 based GUI replacement for Bitcoin **Warning** **Warning** **Warning** -Pre-alpha stuff! I'm using this client myself on the production network, and I haven't noticed any glitches, but remember: always backup your wallet. +Alpha version! I'm using this client myself on the production network, and I haven't noticed any glitches, but remember: always backup your wallet. Testing on the testnet is recommended. This has been implemented: @@ -28,6 +28,8 @@ This has been implemented: - Progress bar on initial block download +- Sendmany support + This has to be done: - Start at system start From b5f918cbd69e02f1e955fe90a13444a15a7de43f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sat, 16 Jul 2011 19:54:44 +0200 Subject: [PATCH 05/12] readme update --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index dc9a8b15..98add9f3 100644 --- a/README.rst +++ b/README.rst @@ -28,7 +28,7 @@ This has been implemented: - Progress bar on initial block download -- Sendmany support +- Sendmany support in UI (send to multiple recipients as well) This has to be done: From 5df0b03c950184b2e2fdbfc6e9f8075dcf81c75c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 17 Jul 2011 14:06:43 +0200 Subject: [PATCH 06/12] make initial block download reporting somewhat better by tracking version responses --- src/main.cpp | 6 +++++- src/qt/bitcoingui.cpp | 11 +++++++++-- src/qt/clientmodel.cpp | 18 ++++++++++++------ src/qt/clientmodel.h | 3 +++ src/qt/guiconstants.h | 2 +- src/qt/walletmodel.cpp | 21 +++++++++++++++------ src/qt/walletmodel.h | 4 ++++ 7 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index e3ad3504..3a482e7c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,7 +32,7 @@ map mapNextTx; map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); -const int nTotalBlocksEstimate = 134444; // Conservative estimate of total nr of blocks on main chain +int nTotalBlocksEstimate = 134444; // Conservative estimate of total nr of blocks on main chain const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download" CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; @@ -1869,6 +1869,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); + if(pfrom->nStartingHeight > nTotalBlocksEstimate) + { + nTotalBlocksEstimate = pfrom->nStartingHeight; + } } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 016f2619..6a6f3f32 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -292,17 +292,21 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count) { int total = clientModel->getTotalBlocksEstimate(); + QString tooltip; + if(count < total) { progressBarLabel->setVisible(true); progressBar->setVisible(true); progressBar->setMaximum(total); progressBar->setValue(count); + tooltip = tr("Downloaded %1 of %2 blocks of transaction history.").arg(count).arg(total); } else { progressBarLabel->setVisible(false); progressBar->setVisible(false); + tooltip = tr("Downloaded %1 blocks of transaction history.").arg(count); } QDateTime now = QDateTime::currentDateTime(); @@ -329,10 +333,13 @@ void BitcoinGUI::setNumBlocks(int count) { text = tr("%n day(s) ago","",secs/(60*60*24)); } + tooltip += QString("\n"); + tooltip += tr("Last block was generated %1.").arg(QLocale::system().toString(lastBlockDate)); labelBlocks->setText(" " + text); - labelBlocks->setToolTip(tr("Downloaded %n block(s) of transaction history. Last block was generated %1.", "", count) - .arg(QLocale::system().toString(lastBlockDate))); + labelBlocks->setToolTip(tooltip); + progressBarLabel->setToolTip(tooltip); + progressBar->setToolTip(tooltip); } void BitcoinGUI::error(const QString &title, const QString &message) diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 8885b4cb..c147aa5a 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -10,7 +10,8 @@ #include ClientModel::ClientModel(CWallet *wallet, QObject *parent) : - QObject(parent), wallet(wallet), optionsModel(0) + QObject(parent), wallet(wallet), optionsModel(0), + cachedNumConnections(0), cachedNumBlocks(0) { // Until signal notifications is built into the bitcoin core, // simply update everything after polling using a timer. @@ -38,11 +39,16 @@ QDateTime ClientModel::getLastBlockDate() const void ClientModel::update() { - // Plainly emit all signals for now. To be more efficient this should check - // whether the values actually changed first, although it'd be even better if these - // were events coming in from the bitcoin core. - emit numConnectionsChanged(getNumConnections()); - emit numBlocksChanged(getNumBlocks()); + int newNumConnections = getNumConnections(); + int newNumBlocks = getNumBlocks(); + + if(cachedNumConnections != newNumConnections) + emit numConnectionsChanged(newNumConnections); + if(cachedNumBlocks != newNumBlocks) + emit numBlocksChanged(newNumBlocks); + + cachedNumConnections = newNumConnections; + cachedNumBlocks = newNumBlocks; } bool ClientModel::isTestNet() const diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 6c2c275c..f7ad14c2 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -42,6 +42,9 @@ private: OptionsModel *optionsModel; + int cachedNumConnections; + int cachedNumBlocks; + signals: void numConnectionsChanged(int count); void numBlocksChanged(int count); diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index cdd1a74d..59f49625 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -2,7 +2,7 @@ #define GUICONSTANTS_H /* milliseconds between model updates */ -static const int MODEL_UPDATE_DELAY = 250; +static const int MODEL_UPDATE_DELAY = 500; /* size of cache */ static const unsigned int WALLET_CACHE_SIZE = 100; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 6e4b814d..4ff2e0ab 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -11,7 +11,8 @@ WalletModel::WalletModel(CWallet *wallet, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(0), addressTableModel(0), - transactionTableModel(0) + transactionTableModel(0), + cachedBalance(0), cachedUnconfirmedBalance(0), cachedNumTransactions(0) { // Until signal notifications is built into the bitcoin core, // simply update everything after polling using a timer. @@ -46,11 +47,19 @@ int WalletModel::getNumTransactions() const void WalletModel::update() { - // Plainly emit all signals for now. To be more efficient this should check - // whether the values actually changed first, although it'd be even better if these - // were events coming in from the bitcoin core. - emit balanceChanged(getBalance(), wallet->GetUnconfirmedBalance()); - emit numTransactionsChanged(getNumTransactions()); + qint64 newBalance = getBalance(); + qint64 newUnconfirmedBalance = getUnconfirmedBalance(); + int newNumTransactions = getNumTransactions(); + + if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance) + emit balanceChanged(newBalance, newUnconfirmedBalance); + + if(cachedNumTransactions != newNumTransactions) + emit numTransactionsChanged(newNumTransactions); + + cachedBalance = newBalance; + cachedUnconfirmedBalance = newUnconfirmedBalance; + cachedNumTransactions = newNumTransactions; addressTableModel->update(); } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index af2cac4b..668d4463 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -73,6 +73,10 @@ private: AddressTableModel *addressTableModel; TransactionTableModel *transactionTableModel; + qint64 cachedBalance; + qint64 cachedUnconfirmedBalance; + qint64 cachedNumTransactions; + signals: void balanceChanged(qint64 balance, qint64 unconfirmedBalance); void numTransactionsChanged(int count); From 8dcffd4d0717e71226da8c3a848b7b6905074637 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 17 Jul 2011 17:30:58 +0200 Subject: [PATCH 07/12] show rotating spinner when block download out of date, tick otherwise --- doc/assets-attribution.txt | 13 +++--- scripts/img/reload.xcf | Bin 0 -> 28597 bytes scripts/img/reload_scaled.png | Bin 0 -> 905 bytes scripts/make_spinner.py | 41 +++++++++++++++++ src/qt/bitcoin.qrc | 6 ++- src/qt/bitcoingui.cpp | 64 ++++++++++++++++++++------- src/qt/bitcoingui.h | 3 ++ src/qt/res/icons/synced.png | Bin 1127 -> 698 bytes src/qt/res/movies/update_spinner.mng | Bin 0 -> 27707 bytes 9 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 scripts/img/reload.xcf create mode 100644 scripts/img/reload_scaled.png create mode 100755 scripts/make_spinner.py create mode 100644 src/qt/res/movies/update_spinner.mng diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index f3900fe0..2ab8f56e 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -14,7 +14,7 @@ Designer: FatCow Web Hosting License: Creative Commons Attribution (by) Site: http://findicons.com/icon/163938/book_open -Icon: src/qt/res/icons/connect*.png +Icon: src/qt/res/icons/connect*.png, src/qt/res/icons/synced.png Icon Pack: Human-O2 Designer: schollidesign License: GNU/GPL @@ -52,9 +52,8 @@ Designer: Jack Cai License: Creative Commons Attribution No Derivatives (by-nd) Site: http://findicons.com/icon/175944/home?id=176221# -Icon: src/qt/res/icons/synced.png, - src/qt/res/icons/notsynced.png -Icon Pack: Gloss: Basic -Designer: Momenticons -License: Creative Commons Attribution (by) -Site: http://www.momenticons.com/ +Icon: scripts/img/reload.xcf (modified),src/qt/res/movies/update_spinner.mng +Icon Pack: Kids +Designer: Everaldo (Everaldo Coelho) +License: GNU/GPL +Site: http://findicons.com/icon/17102/reload?id=17102 diff --git a/scripts/img/reload.xcf b/scripts/img/reload.xcf new file mode 100644 index 0000000000000000000000000000000000000000..c3ce165adbad91ba5e4c7013b12f8df92219401d GIT binary patch literal 28597 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 fgTXt||GV)Yvgres/icons/overview.png res/icons/export.png res/icons/synced.png - res/icons/notsynced.png - res/icons/remove.png + res/icons/remove.png
res/images/about.png + + res/movies/update_spinner.mng + diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6a6f3f32..ed687c45 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -107,17 +108,35 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Create status bar statusBar(); + // Status bar "Connections" notification + QFrame *frameConnections = new QFrame(); + frameConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); + frameConnections->setMinimumWidth(150); + frameConnections->setMaximumWidth(150); + QHBoxLayout *frameConnectionsLayout = new QHBoxLayout(frameConnections); + frameConnectionsLayout->setContentsMargins(3,0,3,0); + frameConnectionsLayout->setSpacing(3); + labelConnectionsIcon = new QLabel(); + labelConnectionsIcon->setToolTip(tr("Number of connections to other clients")); + frameConnectionsLayout->addWidget(labelConnectionsIcon); labelConnections = new QLabel(); - labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelConnections->setMinimumWidth(150); - labelConnections->setMaximumWidth(150); labelConnections->setToolTip(tr("Number of connections to other clients")); - + frameConnectionsLayout->addWidget(labelConnections); + frameConnectionsLayout->addStretch(); + + // Status bar "Blocks" notification + QFrame *frameBlocks = new QFrame(); + frameBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); + frameBlocks->setMinimumWidth(150); + frameBlocks->setMaximumWidth(150); + QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); + frameBlocksLayout->setContentsMargins(3,0,3,0); + frameBlocksLayout->setSpacing(3); + labelBlocksIcon = new QLabel(); + frameBlocksLayout->addWidget(labelBlocksIcon); labelBlocks = new QLabel(); - labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken); - labelBlocks->setMinimumWidth(150); - labelBlocks->setMaximumWidth(150); - labelBlocks->setToolTip(tr("Number of blocks in the block chain")); + frameBlocksLayout->addWidget(labelBlocks); + frameBlocksLayout->addStretch(); // Progress bar for blocks download progressBarLabel = new QLabel(tr("Synchronizing with network...")); @@ -128,11 +147,13 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): statusBar()->addWidget(progressBarLabel); statusBar()->addWidget(progressBar); - statusBar()->addPermanentWidget(labelConnections); - statusBar()->addPermanentWidget(labelBlocks); + statusBar()->addPermanentWidget(frameConnections); + statusBar()->addPermanentWidget(frameBlocks); createTrayIcon(); + syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this); + gotoOverviewPage(); } @@ -285,8 +306,8 @@ void BitcoinGUI::setNumConnections(int count) case 7: case 8: case 9: icon = ":/icons/connect_3"; break; default: icon = ":/icons/connect_4"; break; } - labelConnections->setTextFormat(Qt::RichText); - labelConnections->setText(" " + tr("%n connection(s)", "", count)); + labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(16,16)); + labelConnections->setText(tr("%n connection(s)", "", count)); } void BitcoinGUI::setNumBlocks(int count) @@ -313,13 +334,13 @@ void BitcoinGUI::setNumBlocks(int count) QDateTime lastBlockDate = clientModel->getLastBlockDate(); int secs = lastBlockDate.secsTo(now); QString text; - QString icon = ":/icons/notsynced"; + bool spinning = true; // "Up to date" icon, and outdated icon if(secs < 30*60) { text = "Up to date"; - icon = ":/icons/synced"; + spinning = false; } else if(secs < 60*60) { @@ -334,9 +355,20 @@ void BitcoinGUI::setNumBlocks(int count) text = tr("%n day(s) ago","",secs/(60*60*24)); } tooltip += QString("\n"); - tooltip += tr("Last block was generated %1.").arg(QLocale::system().toString(lastBlockDate)); + tooltip += tr("Last received block was generated %1.").arg(text); + + if(spinning) + { + labelBlocksIcon->setMovie(syncIconMovie); + syncIconMovie->start(); + } + else + { + labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(16,16)); + } + labelBlocks->setText(text); - labelBlocks->setText(" " + text); + labelBlocksIcon->setToolTip(tooltip); labelBlocks->setToolTip(tooltip); progressBarLabel->setToolTip(tooltip); progressBar->setToolTip(tooltip); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 95e0eb70..4fc17dd3 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -57,6 +57,7 @@ private: QLabel *labelConnections; QLabel *labelConnectionsIcon; QLabel *labelBlocks; + QLabel *labelBlocksIcon; QLabel *progressBarLabel; QProgressBar *progressBar; @@ -74,6 +75,8 @@ private: QSystemTrayIcon *trayIcon; TransactionView *transactionView; + QMovie *syncIconMovie; + void createActions(); QWidget *createTabs(); void createTrayIcon(); diff --git a/src/qt/res/icons/synced.png b/src/qt/res/icons/synced.png index 910fc396eded0241ddb2e19678f1406a815e163f..8e428b6a7033b6ee8b06116f47ba1069461ba232 100644 GIT binary patch delta 682 zcmV;b0#*Iz2)YH39De{G^Z#K00004VQb$4nuFf3k00006VoOIv0RI600RN!9r;`8x z010qNS#tmY3ljhU3ljkVnw%H_000McNliru*b5L91v|sxE|vfQ0y;@VK~y-))sszV zk^vmXAK%xnbUK|)&8eFjPL|o&`f?P@jdh5iAn1$1pt{7aqkmnt(I*7O4j%dKPP_J8b{;r;Fc>o&DfqZvo^JL zTb|CNKo<~*`G3`s&j3 zGMfhpF{Z+I@Fe1imuk39nvRN3yE@g^+SP4s&2kAL5O9@+`$-=F*l)C2PB{-eGpTeI zmSe!w7(5K!#f#YV;wl9fbq+E9rv#PflS~cb6hRV7Y=1H;hs-xIX4f0cedgX7tyHFC znF45rz@wlS(UovGRl;8SELH%3Ctl!Z>wB;m+4d4GHNWV1}B;dl&6 zOab0EUVO+#Uw$lNpgfLD-6F{aP|iEbUE8}Uo2Vg!iZF&2HFogDMG4JiL9r#wybWL} zlX{vcVt-#@6ou~(%m;ufi+ky1`9YIX1AqV{Ttk^#1061fLC8ix|Yzrx>wRGmdl)JJRWI!jNLCP94> z!5@5wg=G9zzJinQPk{YXz<70`ccaUoz$>8;JR<=gwJLBmM`1KH_^%i2EBplCVa-Qb Q`~Uy|07*qoM6N<$g5=^ewg3PC delta 1114 zcmV-g1f~1B1?LEm9Df05^w0MI000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|DhWnpA_ zami&o000CDNklQ z7S{iI%uKBR|6PjbW)l3TYWDLz&ut(_>YLo0A1q&P+i`L*Ffjepob;KMk>w8y0|Ucf zCh6VZMgHqENQg+W0R#Yq0DJ!dY`t0_7bfN30|);ACJi_MzU7+xDh)vb2NnPW&Cu6b z1OoOP>fhkS`+ouf1OMIutp5t`W)R2@p!fyY4xJ71QoRD>@v`;>68m}%3=swd5%{nG z008+A|2{}8E+b+kL)Gd5PMR(N00000AQ?yt>iw|qHzhp<+Stp{sQe-0ssgB z_x=HD-4E#j<~&LN7|Ea$4-+c<{`~&$_2>Qu)@&B|DSwdi1pNN`6d@(|%?AiG0RHv> z`r!7>06ijE2g(GZ=m^qq3d2mlfi|F{DF2SyhGRtf|1CG!9O{sSi#JqG;$_!k2G_67jw z=k%c&|1<^q8LaaB9If&i1~dcw?FRt*?G66|000004DD5D)Y#&h4EH4aB?mwQ*wE|t z;?pJ?{OT9$0yIr6txZHx_}s_bnd!_VR$`i@&3_csA`J>L;6kMIH-G9@$#sc9ZFleu^9cq1wv_&Bd`an9j8=)v0+m%FbU zX*P>`(Jo-ng5vbzRN9&Wn@&R(ABO}<=)hM9;yyH5)$wRFeot!muB$r@sTv2%h=PIh zXn&5)0oCvnOAM$s)F=^+cV8j&xG^B)$RA7$i zhO4}#fFQvbkl>am*H!K-5b11Zq|q+GUw=bf(DqV;HiK75!MqW$qw5a=$M;EW)0hke z^yinM3Bx~qM;71I&(7R6_rU`~FkppE?-?fxmkV&mVwrzW2+qP{^GO>QKZD+z?Y-8eyJ+W;kXZExAzRz8!>Z$sluIgHA zUEMGG#rmvE-;PpHltP5Vg98BpAyAMOSN%Jp{O30RJO1wrp5b1{9jMt z0=~Y!P^3DZ|IT5ZrFGo?j?(|V6~q% z{#xK9RYep)LBRe_|FwD*bV&F+BFg-)kAZ>uYazb-G64Yr+maC%QRkWHR{O!QU_$Wq zY1oGCwaIHwU02RDe9<+jS7x3h?cS)fHom~YnVh~@Vjb3iOK;8@FoYHtr`8n1Vn^Fp zc_D7ujB%Sp5p(GGw&`;s@!DwhCb;!&Yv$=qVCLw>k2vs)g@FdV1#*x31PeprgMWQQ z|B45cD5nvSc<6x(d&#)WL{JkCgUsOWnncS`!x&02>OA;8iA2~iSF-D~1jG8K@|&p_ z%C0cpu0H51-KSAmnuTEQ+59a?^A~wp*VL-lhM5{ zTp=aLS=2D8UEStrjD4?Zk&Bz|gnky26H|`%m@x_zpKn-_^}{eiDK}}avNiQagu$nJ ziJPwwSkYiTzn%50D+2QJuWA?a)5+Y?g}Boy#1Vs5VA`(xP$H^^c(nJT(#;670u5CT z#GA;z&eETFdh2<#N>#g2G6Bd;qE|O~% zSsy4v5=UIFn>dwX+p3$$@g&3{QjSVGIB@ZvC5sQyF>7T`GK^{q`_@9yXwV7F3of`& zIBQPz19V}rj8cl?JBNLsJ~e3b;#&pUKUH2TqO1J2bQcW4J{{wn`S`<6bt}yF+-qRg z;TfBwHgo*Wrl9&&^=i{|DXAF**NSi6q&UmU#eWVTlHGa43MX!F=m`4{YU16%VvnW> z*iW~6MeVKjjB&ks zNP?b{#%}lJ!tmcAWWSVCiXn2qHc6NaL0>Xwm9zYDWJNs(aG~~OYXXFxp+pE4J1Ggpdme>?~`J<=+*|UfV`m&od9UN?F)dA&d5sdOo3J?>%U!v1#mK zcnZdUZr`?d!20)|XonQ*6HxxBpT*?cwwf8#S|Xh5D935SUduI;e*q=KLe^;fi};~? z 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 literal 0 HcmV?d00001 From 1907b96d69c220d153825c7a80365593b3cf821e Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 17 Jul 2011 17:42:41 +0200 Subject: [PATCH 08/12] put sendcoins entries in scroll area, so that window does not become bigger than screen with many recipients --- src/qt/forms/sendcoinsdialog.ui | 162 ++++++++++++++++++-------------- src/qt/forms/sendcoinsentry.ui | 2 +- 2 files changed, 93 insertions(+), 71 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 57b79279..8907ab3d 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -15,78 +15,100 @@ - - - 6 + + + true - - - - - - 12 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - &Add recipient... - - - - :/icons/add:/icons/add - - - - - - - - 150 - 0 - - - - Confirm the send action + + + + 0 + 0 + 666 + 197 + + + + + 0 - - &Send - - - - :/icons/send:/icons/send - - - true - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - + + + + 6 + + + + + + + 12 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Add recipient... + + + + :/icons/add:/icons/add + + + + + + + + 150 + 0 + + + + Confirm the send action + + + &Send + + + + :/icons/send:/icons/send + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 1159ef53..2dca6b74 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -147,7 +147,7 @@ - :/icons/res/icons/remove.png:/icons/res/icons/remove.png + :/icons/remove:/icons/remove From a75e1e32923365dada7bee8cc0c2468d759355eb Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 18 Jul 2011 06:55:05 +0200 Subject: [PATCH 09/12] Fix "Last received block was generated Up to date" --- src/qt/bitcoingui.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index ed687c45..c4462dd4 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -334,13 +334,11 @@ void BitcoinGUI::setNumBlocks(int count) QDateTime lastBlockDate = clientModel->getLastBlockDate(); int secs = lastBlockDate.secsTo(now); QString text; - bool spinning = true; - // "Up to date" icon, and outdated icon - if(secs < 30*60) + // Represent time from last generated block in human readable text + if(secs < 60) { - text = "Up to date"; - spinning = false; + text = tr("%n second(s) ago","",secs); } else if(secs < 60*60) { @@ -354,6 +352,16 @@ void BitcoinGUI::setNumBlocks(int count) { text = tr("%n day(s) ago","",secs/(60*60*24)); } + + // In the label we want to be less specific + QString labelText = text; + bool spinning = true; + if(secs < 30*60) + { + labelText = "Up to date"; + spinning = false; + } + tooltip += QString("\n"); tooltip += tr("Last received block was generated %1.").arg(text); @@ -366,7 +374,7 @@ void BitcoinGUI::setNumBlocks(int count) { labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(16,16)); } - labelBlocks->setText(text); + labelBlocks->setText(labelText); labelBlocksIcon->setToolTip(tooltip); labelBlocks->setToolTip(tooltip); From 24c835b0b669dbd3c072d06e27c2e92b252e6af9 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 18 Jul 2011 18:34:53 +0200 Subject: [PATCH 10/12] windows build fix --- src/serialize.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serialize.h b/src/serialize.h index cb3a3ea0..857d0468 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -30,6 +30,7 @@ typedef unsigned long long uint64; #endif #ifdef __WXMSW__ +#include // This is used to attempt to keep keying material out of swap // Note that VirtualLock does not provide this as a guarantee on Windows, // but, in practice, memory that has been VirtualLock'd almost never gets written to From 68e327ae7b7232837c675bb3d51e17e823bc17d8 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 18 Jul 2011 18:38:56 +0200 Subject: [PATCH 11/12] move buttons to bottom of send coins tab, outside of scroll area --- src/qt/forms/sendcoinsdialog.ui | 110 ++++++++++++++++---------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 8907ab3d..547582e8 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -25,7 +25,7 @@ 0 0 666 - 197 + 162 @@ -39,60 +39,6 @@ - - - - 12 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - &Add recipient... - - - - :/icons/add:/icons/add - - - - - - - - 150 - 0 - - - - Confirm the send action - - - &Send - - - - :/icons/send:/icons/send - - - true - - - - - @@ -110,6 +56,60 @@ + + + + 12 + + + + + &Add recipient... + + + + :/icons/add:/icons/add + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 150 + 0 + + + + Confirm the send action + + + &Send + + + + :/icons/send:/icons/send + + + true + + + + + From 174b3eddc05f26abbc97a83027a4da9ba4114d57 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 18 Jul 2011 21:02:17 +0200 Subject: [PATCH 12/12] one remove/delete icon is enough and the red minus better matches the add icon --- doc/assets-attribution.txt | 2 +- src/qt/bitcoin.qrc | 1 - src/qt/forms/addressbookpage.ui | 2 +- src/qt/res/icons/editdelete.png | Bin 1368 -> 0 bytes 4 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 src/qt/res/icons/editdelete.png diff --git a/doc/assets-attribution.txt b/doc/assets-attribution.txt index 2ab8f56e..c0323379 100644 --- a/doc/assets-attribution.txt +++ b/doc/assets-attribution.txt @@ -29,7 +29,7 @@ License: You are free to do with these icons as you wish, including selling, Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png, src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png, src/qt/res/icons/add.png, src/qt/res/icons/edit.png, - src/qt/res/icons/editdelete.png, src/qt/res/icons/remove.png (edited) + src/qt/res/icons/remove.png (edited) Designer: http://www.everaldo.com Icon Pack: Crystal SVG License: LGPL diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index f6b22f45..5199a8ea 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -25,7 +25,6 @@ res/icons/bitcoin_testnet.png res/icons/toolbar_testnet.png res/icons/edit.png - res/icons/editdelete.png res/icons/history.png res/icons/overview.png res/icons/export.png diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui index d3feedb5..fb098c82 100644 --- a/src/qt/forms/addressbookpage.ui +++ b/src/qt/forms/addressbookpage.ui @@ -89,7 +89,7 @@ - :/icons/editdelete:/icons/editdelete + :/icons/remove:/icons/remove diff --git a/src/qt/res/icons/editdelete.png b/src/qt/res/icons/editdelete.png deleted file mode 100644 index 945d221eeaa3cf0ffabbf288d7da7541adf05ddf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1368 zcmV-e1*iInP)PbXFRCwBA z{Qv(y11@m%;xpWm1i;N(ckU;L*(>2w1F{<+fEWomz+7L7frWr&AQQg-W?=a7^FPD) zAHNyCeEZ4p>iu_yr;lzwjP$cnz~=yf0AeEK6GkF@`|BSA!{5I^{O>;y|AXK^zZhJc ztmPN2JNOKrUVs2%VxR(G2Aaam48n{ItSrn7EImH1}iHw@g*CMV1xuf08zyOV1t0JU}AtcfCU&0KYslICdNMu??3+nCeB|B zzkYmW(9zMAnYa4jOJscj0YoqtSO9YYGZ78|ra^|^e;62kf^)-Ph6hhyGhDcO52yN! zaBHyL00G28tq=f310y4Z21S9aq%eb|r~t#a@4pycz5C4Y6__>u0WJLX`#;0A>(@Y9 zjDh$pKmbwA0ZdR=Fad*$@h?Q|A23^jEdKlF9|J2ZGsC~X{}~vWP%}0_0Fj;sjxJ#M z-)P9d@a+SpBbZ>>5||DiUt-|uc4Oc?xtxKWorQs&jg^6e9f*N6G5`o57LqNWmk24v zx*Zu9OZPJ{a0#GfN00~q+&#y@*5=Ic@AF3nk;_LI{sTkgvsoC!f9C%TO#e{*3=lv} z{k1l{60SgIWVaNb1g&DrzILE-!>IAm@4=_>w1BwYO$YYl&Aj>}?EdL9n|NMp|Oi(GwhUNf(03zN2)(H%ZN%K*J z{sDvb&%Fx_zZx7Feq7kczyb<(ESCSuoX+sy+?Rm`=m-`TR4V}jh_o~Sas(*;fl&dn z9Ap6)-@U-V+Ug9k5Y6&`*)tdzouV07{{9Eon}7eKIshPmNY92qM}X?zzcUgcmj8xg zES59m&1PV5i~?p+VD