diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index 31c032ecb..91e4edc33 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -96,6 +96,7 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \ moc_optionsmodel.cpp moc_overviewpage.cpp moc_paymentserver.cpp \ moc_receiverequestdialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \ moc_receivecoinsdialog.cpp \ + moc_recentrequeststablemodel.cpp \ moc_rpcconsole.cpp moc_sendcoinsdialog.cpp moc_sendcoinsentry.cpp \ moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_trafficgraphwidget.cpp moc_transactiondesc.cpp \ moc_transactiondescdialog.cpp moc_transactionfilterproxy.cpp \ @@ -122,6 +123,7 @@ BITCOIN_QT_H = aboutdialog.h addressbookpage.h addresstablemodel.h \ optionsdialog.h \ optionsmodel.h overviewpage.h paymentrequestplus.h paymentserver.h \ receivecoinsdialog.h \ + recentrequeststablemodel.h \ receiverequestdialog.h qvalidatedlineedit.h qvaluecombobox.h rpcconsole.h \ sendcoinsdialog.h sendcoinsentry.h signverifymessagedialog.h splashscreen.h \ trafficgraphwidget.h transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \ @@ -157,6 +159,7 @@ BITCOIN_QT_CPP = aboutdialog.cpp addressbookpage.cpp \ optionsdialog.cpp optionsmodel.cpp overviewpage.cpp paymentrequestplus.cpp \ paymentserver.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp \ receivecoinsdialog.cpp receiverequestdialog.cpp \ + recentrequeststablemodel.cpp \ rpcconsole.cpp sendcoinsdialog.cpp sendcoinsentry.cpp \ signverifymessagedialog.cpp splashscreen.cpp trafficgraphwidget.cpp transactiondesc.cpp \ transactiondescdialog.cpp transactionfilterproxy.cpp transactionrecord.cpp \ diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index d686cd4fd..5e7d8e617 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -297,7 +297,7 @@ QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, { if(orientation == Qt::Horizontal) { - if(role == Qt::DisplayRole) + if(role == Qt::DisplayRole && section < columns.size()) { return columns[section]; } diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui index 6d1a72ecd..e7138f537 100644 --- a/src/qt/forms/receivecoinsdialog.ui +++ b/src/qt/forms/receivecoinsdialog.ui @@ -7,35 +7,60 @@ 0 0 776 - 343 + 364 - - + + + + Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before. + - &Amount: + R&euse an existing receiving address (not recommended) + + + + + + + + + + + + + + &Message: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - reqAmount + reqMessage - - - - - 80 - 0 - + + + + The label to associate with the new receiving address + + + + - The amount to request + The message to attach to payment request + + + + + + + Use this form to request payments. All fields are optional. @@ -52,72 +77,34 @@ - - - - The label to associate with the receiving address - - - - + - &Message: + &Amount: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - reqMessage - - - - - - - The message to attach to payment request + reqAmount - - - - + + + + + 80 + 0 + - - - - - Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before. - - - R&euse an existing receiving address (not recommended) - - - - - - - Use this form to request payments. All fields are optional. + The amount to request - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -178,6 +165,98 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 75 + true + + + + Previously requested payments + + + + + + + + + + + + Show the selected request (does the same as double clicking an entry) + + + Show + + + + :/icons/edit:/icons/edit + + + + + + + Remove the selected entries from the list + + + Remove + + + + :/icons/remove:/icons/remove + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + @@ -187,6 +266,17 @@
bitcoinamountfield.h
+ + reqLabel + reqAmount + reqMessage + reuseAddress + clearButton + receiveButton + recentRequestsView + showRequestButton + removeRequestButton + diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui index c9cb3de69..85928c9be 100644 --- a/src/qt/forms/receiverequestdialog.ui +++ b/src/qt/forms/receiverequestdialog.ui @@ -83,13 +83,6 @@ - - - - &Copy Image - - - diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 45b232529..a536fa2ad 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -12,6 +12,7 @@ #include "guiutil.h" #include "receiverequestdialog.h" #include "addresstablemodel.h" +#include "recentrequeststablemodel.h" #include #include @@ -27,6 +28,8 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(QWidget *parent) : #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac ui->clearButton->setIcon(QIcon()); ui->receiveButton->setIcon(QIcon()); + ui->showRequestButton->setIcon(QIcon()); + ui->removeRequestButton->setIcon(QIcon()); #endif connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); } @@ -39,6 +42,19 @@ void ReceiveCoinsDialog::setModel(WalletModel *model) { connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); updateDisplayUnit(); + + ui->recentRequestsView->setModel(model->getRecentRequestsTableModel()); + ui->recentRequestsView->setAlternatingRowColors(true); + ui->recentRequestsView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->recentRequestsView->setSelectionMode(QAbstractItemView::ContiguousSelection); + ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Date, 130); + ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Label, 120); +#if QT_VERSION < 0x050000 + ui->recentRequestsView->horizontalHeader()->setResizeMode(RecentRequestsTableModel::Message, QHeaderView::Stretch); +#else + ui->recentRequestsView->horizontalHeader()->setSectionResizeMode(RecentRequestsTableModel::Message, QHeaderView::Stretch); +#endif + ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Amount, 100); } } @@ -76,7 +92,7 @@ void ReceiveCoinsDialog::updateDisplayUnit() void ReceiveCoinsDialog::on_receiveButton_clicked() { - if(!model || !model->getOptionsModel() || !model->getAddressTableModel()) + if(!model || !model->getOptionsModel() || !model->getAddressTableModel() || !model->getRecentRequestsTableModel()) return; QString address; @@ -108,4 +124,41 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); clear(); + + /* Store request for later reference */ + model->getRecentRequestsTableModel()->addNewRequest(info); +} + +void ReceiveCoinsDialog::on_recentRequestsView_doubleClicked(const QModelIndex &index) +{ + const RecentRequestsTableModel *submodel = model->getRecentRequestsTableModel(); + ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this); + dialog->setModel(model->getOptionsModel()); + dialog->setInfo(submodel->entry(index.row()).recipient); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); +} + +void ReceiveCoinsDialog::on_showRequestButton_clicked() +{ + if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel()) + return; + QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows(); + + foreach (QModelIndex index, selection) + { + on_recentRequestsView_doubleClicked(index); + } +} + +void ReceiveCoinsDialog::on_removeRequestButton_clicked() +{ + if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel()) + return; + QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows(); + if(selection.empty()) + return; + // correct for selection mode ContiguousSelection + QModelIndex firstIndex = selection.at(0); + model->getRecentRequestsTableModel()->removeRows(firstIndex.row(), selection.length(), firstIndex.parent()); } diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 9980edd1f..4435bf669 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -13,6 +13,9 @@ namespace Ui { } class WalletModel; class OptionsModel; +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE /** Dialog for requesting payment of bitcoins */ class ReceiveCoinsDialog : public QDialog @@ -36,6 +39,9 @@ private: private slots: void on_receiveButton_clicked(); + void on_showRequestButton_clicked(); + void on_removeRequestButton_clicked(); + void on_recentRequestsView_doubleClicked(const QModelIndex &index); void updateDisplayUnit(); }; diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index 7e92715df..b5e45341d 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -85,12 +85,10 @@ ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) : #ifndef USE_QRCODE ui->btnSaveAs->setVisible(false); - ui->btnCopyImage->setVisible(false); ui->lblQRCode->setVisible(false); #endif connect(ui->btnSaveAs, SIGNAL(clicked()), ui->lblQRCode, SLOT(saveImage())); - connect(ui->btnCopyImage, SIGNAL(clicked()), ui->lblQRCode, SLOT(copyImage())); } ReceiveRequestDialog::~ReceiveRequestDialog() diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp new file mode 100644 index 000000000..06f64af14 --- /dev/null +++ b/src/qt/recentrequeststablemodel.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "recentrequeststablemodel.h" +#include "guiutil.h" +#include "bitcoinunits.h" +#include "optionsmodel.h" + +RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent): + walletModel(parent) +{ + /* These columns must match the indices in the ColumnIndex enumeration */ + columns << tr("Date") << tr("Label") << tr("Message") << tr("Amount"); +} + +RecentRequestsTableModel::~RecentRequestsTableModel() +{ + /* Intentionally left empty */ +} + +int RecentRequestsTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return list.length(); +} + +int RecentRequestsTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant RecentRequestsTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid() || index.row() >= list.length()) + return QVariant(); + + const RecentRequestEntry *rec = &list[index.row()]; + + if(role == Qt::DisplayRole || role == Qt::EditRole) + { + switch(index.column()) + { + case Date: + return GUIUtil::dateTimeStr(rec->date); + case Label: + if(rec->recipient.label.isEmpty() && role == Qt::DisplayRole) + { + return tr("(no label)"); + } + else + { + return rec->recipient.label; + } + case Message: + if(rec->recipient.message.isEmpty() && role == Qt::DisplayRole) + { + return tr("(no message)"); + } + else + { + return rec->recipient.message; + } + case Amount: + return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount); + } + } + return QVariant(); +} + +bool RecentRequestsTableModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + return true; +} + +QVariant RecentRequestsTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole && section < columns.size()) + { + return columns[section]; + } + } + return QVariant(); +} + +QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const +{ + return createIndex(row, column, 0); +} + +bool RecentRequestsTableModel::removeRows(int row, int count, const QModelIndex &parent) +{ + Q_UNUSED(parent); + if(count > 0 && row >= 0 && (row+count) <= list.size()) + { + beginRemoveRows(parent, row, row + count - 1); + list.erase(list.begin() + row, list.begin() + row + count); + endRemoveRows(); + return true; + } else { + return false; + } +} + +Qt::ItemFlags RecentRequestsTableModel::flags(const QModelIndex &index) const +{ + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; +} + +void RecentRequestsTableModel::addNewRequest(const SendCoinsRecipient &recipient) +{ + RecentRequestEntry newEntry; + newEntry.date = QDateTime::currentDateTime(); + newEntry.recipient = recipient; + beginInsertRows(QModelIndex(), 0, 0); + list.prepend(newEntry); + endInsertRows(); +} diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h new file mode 100644 index 000000000..d00a2a905 --- /dev/null +++ b/src/qt/recentrequeststablemodel.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef RECENTREQUESTSTABLEMODEL_H +#define RECENTREQUESTSTABLEMODEL_H + +#include +#include +#include + +#include "walletmodel.h" + +class CWallet; + +struct RecentRequestEntry +{ + QDateTime date; + SendCoinsRecipient recipient; +}; + +/** Model for list of recently generated payment requests / bitcoin URIs. + * Part of wallet model. + */ +class RecentRequestsTableModel: public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit RecentRequestsTableModel(CWallet *wallet, WalletModel *parent = 0); + ~RecentRequestsTableModel(); + + enum ColumnIndex { + Date = 0, + Label = 1, + Message = 2, + Amount = 3 + }; + + /** @name Methods overridden from QAbstractTableModel + @{*/ + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + Qt::ItemFlags flags(const QModelIndex &index) const; + /*@}*/ + + const RecentRequestEntry &entry(int row) const { return list[row]; } + void addNewRequest(const SendCoinsRecipient &recipient); + +private: + WalletModel *walletModel; + QStringList columns; + QList list; +}; + +#endif diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 2470af41a..984a5a2e7 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -7,6 +7,7 @@ #include "addresstablemodel.h" #include "guiconstants.h" #include "transactiontablemodel.h" +#include "recentrequeststablemodel.h" #include "base58.h" #include "db.h" @@ -26,6 +27,7 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), transactionTableModel(0), + recentRequestsTableModel(0), cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), cachedNumTransactions(0), cachedEncryptionStatus(Unencrypted), @@ -33,6 +35,7 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p { addressTableModel = new AddressTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); + recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); // This timer will be fired repeatedly to update the balance pollTimer = new QTimer(this); @@ -325,6 +328,11 @@ TransactionTableModel *WalletModel::getTransactionTableModel() return transactionTableModel; } +RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel() +{ + return recentRequestsTableModel; +} + WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { if(!wallet->IsCrypted()) diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 32ddbbc6f..44a1912ec 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -18,6 +18,7 @@ class AddressTableModel; class OptionsModel; class TransactionTableModel; +class RecentRequestsTableModel; class WalletModelTransaction; class CCoinControl; @@ -88,6 +89,7 @@ public: OptionsModel *getOptionsModel(); AddressTableModel *getAddressTableModel(); TransactionTableModel *getTransactionTableModel(); + RecentRequestsTableModel *getRecentRequestsTableModel(); qint64 getBalance(const CCoinControl *coinControl = NULL) const; qint64 getUnconfirmedBalance() const; @@ -160,6 +162,7 @@ private: AddressTableModel *addressTableModel; TransactionTableModel *transactionTableModel; + RecentRequestsTableModel *recentRequestsTableModel; // Cache some values to be able to detect changes qint64 cachedBalance;