diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index afe307c77..f22ac932d 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -1,469 +1,473 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include // For GetConfig #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include WalletView::WalletView(const PlatformStyle *_platformStyle, WalletModel *_walletModel, QWidget *parent) : QStackedWidget(parent), clientModel(nullptr), walletModel(_walletModel), platformStyle(_platformStyle) { // Create tabs overviewPage = new OverviewPage(platformStyle); transactionsPage = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(); QHBoxLayout *hbox_buttons = new QHBoxLayout(); transactionView = new TransactionView(platformStyle, this); vbox->addWidget(transactionView); QPushButton *exportButton = new QPushButton(tr("&Export"), this); exportButton->setToolTip( tr("Export the data in the current tab to a file")); if (platformStyle->getImagesOnButtons()) { exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export")); } hbox_buttons->addStretch(); hbox_buttons->addWidget(exportButton); vbox->addLayout(hbox_buttons); transactionsPage->setLayout(vbox); receiveCoinsPage = new ReceiveCoinsDialog(platformStyle); sendCoinsPage = new SendCoinsDialog(platformStyle, walletModel); usedSendingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::SendingTab, this); usedReceivingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this); addWidget(overviewPage); addWidget(transactionsPage); addWidget(receiveCoinsPage); addWidget(sendCoinsPage); + connect(overviewPage, &OverviewPage::transactionClicked, this, + &WalletView::transactionClicked); // Clicking on a transaction on the overview pre-selects the transaction on // the transaction history page connect(overviewPage, &OverviewPage::transactionClicked, transactionView, static_cast( &TransactionView::focusTransaction)); connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::requestedSyncWarningInfo); + connect(sendCoinsPage, &SendCoinsDialog::coinsSent, this, + &WalletView::coinsSent); // Highlight transaction after send connect(sendCoinsPage, &SendCoinsDialog::coinsSent, transactionView, static_cast( &TransactionView::focusTransaction)); // Clicking on "Export" allows to export the transaction list connect(exportButton, &QPushButton::clicked, transactionView, &TransactionView::exportClicked); // Pass through messages from sendCoinsPage connect(sendCoinsPage, &SendCoinsDialog::message, this, &WalletView::message); // Pass through messages from transactionView connect(transactionView, &TransactionView::message, this, &WalletView::message); // Set the model properly. setWalletModel(walletModel); } WalletView::~WalletView() {} void WalletView::setBitcoinGUI(BitcoinGUI *gui) { if (gui) { // Clicking on a transaction on the overview page simply sends you to // transaction history page - connect(overviewPage, &OverviewPage::transactionClicked, gui, + connect(this, &WalletView::transactionClicked, gui, &BitcoinGUI::gotoHistoryPage); // Navigate to transaction history page after send - connect(sendCoinsPage, &SendCoinsDialog::coinsSent, gui, + connect(this, &WalletView::coinsSent, gui, &BitcoinGUI::gotoHistoryPage); // Receive and report messages connect( this, &WalletView::message, [gui](const QString &title, const QString &message, unsigned int style) { gui->message(title, message, style); }); // Pass through encryption status changed signals connect(this, &WalletView::encryptionStatusChanged, gui, &BitcoinGUI::updateWalletStatus); // Pass through transaction notifications connect(this, &WalletView::incomingTransaction, gui, &BitcoinGUI::incomingTransaction); // Connect HD enabled state signal connect(this, &WalletView::hdEnabledStatusChanged, gui, &BitcoinGUI::updateWalletStatus); } } void WalletView::setClientModel(ClientModel *_clientModel) { this->clientModel = _clientModel; overviewPage->setClientModel(_clientModel); sendCoinsPage->setClientModel(_clientModel); } void WalletView::setWalletModel(WalletModel *_walletModel) { this->walletModel = _walletModel; // Put transaction list in tabs transactionView->setModel(_walletModel); overviewPage->setWalletModel(_walletModel); receiveCoinsPage->setModel(_walletModel); sendCoinsPage->setModel(_walletModel); usedReceivingAddressesPage->setModel( _walletModel ? _walletModel->getAddressTableModel() : nullptr); usedSendingAddressesPage->setModel( _walletModel ? _walletModel->getAddressTableModel() : nullptr); if (_walletModel) { // Receive and pass through messages from wallet model connect(_walletModel, &WalletModel::message, this, &WalletView::message); // Handle changes in encryption status connect(_walletModel, &WalletModel::encryptionStatusChanged, this, &WalletView::encryptionStatusChanged); updateEncryptionStatus(); // update HD status Q_EMIT hdEnabledStatusChanged(); // Balloon pop-up for new transaction connect(_walletModel->getTransactionTableModel(), &TransactionTableModel::rowsInserted, this, &WalletView::processNewTransaction); // Ask for passphrase if needed connect(_walletModel, &WalletModel::requireUnlock, this, &WalletView::unlockWallet); // Show progress dialog connect(_walletModel, &WalletModel::showProgress, this, &WalletView::showProgress); } } void WalletView::processNewTransaction(const QModelIndex &parent, int start, int end) { // Prevent balloon-spam when initial block download is in progress if (!walletModel || !clientModel || clientModel->node().isInitialBlockDownload()) { return; } TransactionTableModel *ttm = walletModel->getTransactionTableModel(); if (!ttm || ttm->processingQueuedTransactions()) { return; } QString date = ttm->index(start, TransactionTableModel::Date, parent) .data() .toString(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) .data(Qt::EditRole) .toULongLong(); QString type = ttm->index(start, TransactionTableModel::Type, parent) .data() .toString(); QModelIndex index = ttm->index(start, 0, parent); QString address = ttm->data(index, TransactionTableModel::AddressRole).toString(); QString label = GUIUtil::HtmlEscape( ttm->data(index, TransactionTableModel::LabelRole).toString()); Q_EMIT incomingTransaction( date, walletModel->getOptionsModel()->getDisplayUnit(), int64_t(amount) * SATOSHI, type, address, label, GUIUtil::HtmlEscape(walletModel->getWalletName())); } void WalletView::gotoOverviewPage() { setCurrentWidget(overviewPage); } void WalletView::gotoHistoryPage() { setCurrentWidget(transactionsPage); } void WalletView::gotoReceiveCoinsPage() { setCurrentWidget(receiveCoinsPage); } void WalletView::gotoSendCoinsPage(QString addr) { setCurrentWidget(sendCoinsPage); if (!addr.isEmpty()) { sendCoinsPage->setAddress(addr); } } void WalletView::gotoSignMessageTab(QString addr) { // calls show() in showTab_SM() SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this); signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose); signVerifyMessageDialog->setModel(walletModel); signVerifyMessageDialog->showTab_SM(true); if (!addr.isEmpty()) { signVerifyMessageDialog->setAddress_SM(addr); } } void WalletView::gotoVerifyMessageTab(QString addr) { // calls show() in showTab_VM() SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this); signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose); signVerifyMessageDialog->setModel(walletModel); signVerifyMessageDialog->showTab_VM(true); if (!addr.isEmpty()) { signVerifyMessageDialog->setAddress_VM(addr); } } void WalletView::gotoLoadPSBT() { QString filename = GUIUtil::getOpenFileName( this, tr("Load Transaction Data"), QString(), tr("Partially Signed Transaction (*.psbt)"), nullptr); if (filename.isEmpty()) { return; } if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) { Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR); return; } std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary); std::string dataStr(std::istreambuf_iterator{in}, {}); std::string error; PartiallySignedTransaction psbtx; if (!DecodeRawPSBT(psbtx, dataStr, error)) { Q_EMIT message(tr("Error"), tr("Unable to decode PSBT file") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR); return; } CMutableTransaction mtx; bool complete = false; PSBTAnalysis analysis = AnalyzePSBT(psbtx); QMessageBox msgBox; msgBox.setText("PSBT"); switch (analysis.next) { case PSBTRole::CREATOR: case PSBTRole::UPDATER: msgBox.setInformativeText( "PSBT is incomplete. Copy to clipboard for manual inspection?"); break; case PSBTRole::SIGNER: msgBox.setInformativeText( "Transaction needs more signatures. Copy to clipboard?"); break; case PSBTRole::FINALIZER: case PSBTRole::EXTRACTOR: complete = FinalizeAndExtractPSBT(psbtx, mtx); if (complete) { msgBox.setInformativeText( tr("Would you like to send this transaction?")); } else { // The analyzer missed something, e.g. if there are // final_scriptSig but with invalid signatures. msgBox.setInformativeText( tr("There was an unexpected problem processing the PSBT. " "Copy to clipboard for manual inspection?")); } } msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); switch (msgBox.exec()) { case QMessageBox::Yes: { if (complete) { std::string err_string; CTransactionRef tx = MakeTransactionRef(mtx); TransactionError result = BroadcastTransaction( *clientModel->node().context(), GetConfig(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* wait_callback */ false); if (result == TransactionError::OK) { Q_EMIT message(tr("Success"), tr("Broadcasted transaction successfully."), CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL); } else { Q_EMIT message(tr("Error"), QString::fromStdString(err_string), CClientUIInterface::MSG_ERROR); } } else { // Serialize the PSBT CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << psbtx; GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str()); Q_EMIT message(tr("PSBT copied"), "Copied to clipboard", CClientUIInterface::MSG_INFORMATION); return; } } case QMessageBox::Cancel: break; default: assert(false); } } bool WalletView::handlePaymentRequest(const SendCoinsRecipient &recipient) { return sendCoinsPage->handlePaymentRequest(recipient); } void WalletView::showOutOfSyncWarning(bool fShow) { overviewPage->showOutOfSyncWarning(fShow); } void WalletView::updateEncryptionStatus() { Q_EMIT encryptionStatusChanged(); } void WalletView::encryptWallet(bool status) { if (!walletModel) { return; } AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt : AskPassphraseDialog::Decrypt, this); dlg.setModel(walletModel); dlg.exec(); updateEncryptionStatus(); } void WalletView::backupWallet() { QString filename = GUIUtil::getSaveFileName(this, tr("Backup Wallet"), QString(), tr("Wallet Data (*.dat)"), nullptr); if (filename.isEmpty()) { return; } if (!walletModel->wallet().backupWallet(filename.toLocal8Bit().data())) { Q_EMIT message( tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.") .arg(filename), CClientUIInterface::MSG_ERROR); } else { Q_EMIT message( tr("Backup Successful"), tr("The wallet data was successfully saved to %1.").arg(filename), CClientUIInterface::MSG_INFORMATION); } } void WalletView::changePassphrase() { AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this); dlg.setModel(walletModel); dlg.exec(); } void WalletView::unlockWallet() { if (!walletModel) { return; } // Unlock wallet when requested by wallet model if (walletModel->getEncryptionStatus() == WalletModel::Locked) { AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this); dlg.setModel(walletModel); dlg.exec(); } } void WalletView::usedSendingAddresses() { if (!walletModel) { return; } GUIUtil::bringToFront(usedSendingAddressesPage); } void WalletView::usedReceivingAddresses() { if (!walletModel) { return; } GUIUtil::bringToFront(usedReceivingAddressesPage); } void WalletView::showProgress(const QString &title, int nProgress) { if (nProgress == 0) { progressDialog = new QProgressDialog(title, tr("Cancel"), 0, 100); GUIUtil::PolishProgressDialog(progressDialog); progressDialog->setWindowModality(Qt::ApplicationModal); progressDialog->setMinimumDuration(0); progressDialog->setAutoClose(false); progressDialog->setValue(0); } else if (nProgress == 100) { if (progressDialog) { progressDialog->close(); progressDialog->deleteLater(); progressDialog = nullptr; } } else if (progressDialog) { if (progressDialog->wasCanceled()) { getWalletModel()->wallet().abortRescan(); } else { progressDialog->setValue(nProgress); } } } void WalletView::requestedSyncWarningInfo() { Q_EMIT outOfSyncWarningClicked(); } diff --git a/src/qt/walletview.h b/src/qt/walletview.h index fcccb2ac0..150db8d7d 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -1,141 +1,143 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_WALLETVIEW_H #define BITCOIN_QT_WALLETVIEW_H #include #include class BitcoinGUI; class ClientModel; class OverviewPage; class PlatformStyle; class ReceiveCoinsDialog; class SendCoinsDialog; class SendCoinsRecipient; class TransactionView; class WalletModel; class AddressBookPage; QT_BEGIN_NAMESPACE class QModelIndex; class QProgressDialog; QT_END_NAMESPACE /** * WalletView class. This class represents the view to a single wallet. * It was added to support multiple wallet functionality. Each wallet gets its * own WalletView instance. * It communicates with both the client and the wallet models to give the user * an up-to-date view of the current core state. */ class WalletView : public QStackedWidget { Q_OBJECT public: WalletView(const PlatformStyle *platformStyle, WalletModel *walletModel, QWidget *parent); ~WalletView(); void setBitcoinGUI(BitcoinGUI *gui); /** * Set the client model. * The client model represents the part of the core that communicates with * the P2P network, and is wallet-agnostic. */ void setClientModel(ClientModel *clientModel); WalletModel *getWalletModel() { return walletModel; } /** * Set the wallet model. * The wallet model represents a bitcoin wallet, and offers access to the * list of transactions, address book and sending functionality. */ void setWalletModel(WalletModel *walletModel); bool handlePaymentRequest(const SendCoinsRecipient &recipient); void showOutOfSyncWarning(bool fShow); private: ClientModel *clientModel; WalletModel *walletModel; OverviewPage *overviewPage; QWidget *transactionsPage; ReceiveCoinsDialog *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; AddressBookPage *usedSendingAddressesPage; AddressBookPage *usedReceivingAddressesPage; TransactionView *transactionView; QProgressDialog *progressDialog; const PlatformStyle *platformStyle; public Q_SLOTS: /** Switch to overview (home) page */ void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ void gotoSendCoinsPage(QString addr = ""); /** Show Sign/Verify Message dialog and switch to sign message tab */ void gotoSignMessageTab(QString addr = ""); /** Show Sign/Verify Message dialog and switch to verify message tab */ void gotoVerifyMessageTab(QString addr = ""); /** Load Partially Signed Bitcoin Transaction */ void gotoLoadPSBT(); /** * Show incoming transaction notification for new transactions. * * The new items are those between start and end inclusive, under the given * parent item. */ void processNewTransaction(const QModelIndex &parent, int start, int end); /** Encrypt the wallet */ void encryptWallet(bool status); /** Backup the wallet */ void backupWallet(); /** Change encrypted wallet passphrase */ void changePassphrase(); /** Ask for passphrase to unlock wallet temporarily */ void unlockWallet(); /** Show used sending addresses */ void usedSendingAddresses(); /** Show used receiving addresses */ void usedReceivingAddresses(); /** Re-emit encryption status signal */ void updateEncryptionStatus(); /** Show progress dialog e.g. for rescan */ void showProgress(const QString &title, int nProgress); /** User has requested more information about the out of sync state */ void requestedSyncWarningInfo(); Q_SIGNALS: + void transactionClicked(); + void coinsSent(); /** Fired when a message should be reported to the user */ void message(const QString &title, const QString &message, unsigned int style); /** Encryption status of wallet changed */ void encryptionStatusChanged(); /** HD-Enabled status of wallet changed (only possible during startup) */ void hdEnabledStatusChanged(); /** Notify that a new transaction appeared */ void incomingTransaction(const QString &date, int unit, const Amount amount, const QString &type, const QString &address, const QString &label, const QString &walletName); /** Notify that the out of sync warning icon has been pressed */ void outOfSyncWarningClicked(); }; #endif // BITCOIN_QT_WALLETVIEW_H