Line data Source code
1 : // Copyright (c) 2011-2020 The Bitcoin Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <qt/walletview.h>
6 :
7 : #include <qt/addressbookpage.h>
8 : #include <qt/askpassphrasedialog.h>
9 : #include <qt/clientmodel.h>
10 : #include <qt/guiutil.h>
11 : #include <qt/psbtoperationsdialog.h>
12 : #include <qt/optionsmodel.h>
13 : #include <qt/overviewpage.h>
14 : #include <qt/platformstyle.h>
15 : #include <qt/receivecoinsdialog.h>
16 : #include <qt/sendcoinsdialog.h>
17 : #include <qt/signverifymessagedialog.h>
18 : #include <qt/transactiontablemodel.h>
19 : #include <qt/transactionview.h>
20 : #include <qt/walletmodel.h>
21 :
22 : #include <interfaces/node.h>
23 : #include <node/ui_interface.h>
24 : #include <psbt.h>
25 : #include <util/strencodings.h>
26 :
27 : #include <QAction>
28 : #include <QActionGroup>
29 : #include <QApplication>
30 : #include <QClipboard>
31 : #include <QFileDialog>
32 : #include <QHBoxLayout>
33 : #include <QProgressDialog>
34 : #include <QPushButton>
35 : #include <QVBoxLayout>
36 :
37 0 : WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent):
38 0 : QStackedWidget(parent),
39 0 : clientModel(nullptr),
40 0 : walletModel(nullptr),
41 0 : platformStyle(_platformStyle)
42 0 : {
43 : // Create tabs
44 0 : overviewPage = new OverviewPage(platformStyle);
45 :
46 0 : transactionsPage = new QWidget(this);
47 0 : QVBoxLayout *vbox = new QVBoxLayout();
48 0 : QHBoxLayout *hbox_buttons = new QHBoxLayout();
49 0 : transactionView = new TransactionView(platformStyle, this);
50 0 : vbox->addWidget(transactionView);
51 0 : QPushButton *exportButton = new QPushButton(tr("&Export"), this);
52 0 : exportButton->setToolTip(tr("Export the data in the current tab to a file"));
53 0 : if (platformStyle->getImagesOnButtons()) {
54 0 : exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
55 0 : }
56 0 : hbox_buttons->addStretch();
57 0 : hbox_buttons->addWidget(exportButton);
58 0 : vbox->addLayout(hbox_buttons);
59 0 : transactionsPage->setLayout(vbox);
60 :
61 0 : receiveCoinsPage = new ReceiveCoinsDialog(platformStyle);
62 0 : sendCoinsPage = new SendCoinsDialog(platformStyle);
63 :
64 0 : usedSendingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::SendingTab, this);
65 0 : usedReceivingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this);
66 :
67 0 : addWidget(overviewPage);
68 0 : addWidget(transactionsPage);
69 0 : addWidget(receiveCoinsPage);
70 0 : addWidget(sendCoinsPage);
71 :
72 0 : connect(overviewPage, &OverviewPage::transactionClicked, this, &WalletView::transactionClicked);
73 : // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page
74 0 : connect(overviewPage, &OverviewPage::transactionClicked, transactionView, static_cast<void (TransactionView::*)(const QModelIndex&)>(&TransactionView::focusTransaction));
75 :
76 0 : connect(overviewPage, &OverviewPage::outOfSyncWarningClicked, this, &WalletView::requestedSyncWarningInfo);
77 :
78 0 : connect(sendCoinsPage, &SendCoinsDialog::coinsSent, this, &WalletView::coinsSent);
79 : // Highlight transaction after send
80 0 : connect(sendCoinsPage, &SendCoinsDialog::coinsSent, transactionView, static_cast<void (TransactionView::*)(const uint256&)>(&TransactionView::focusTransaction));
81 :
82 : // Clicking on "Export" allows to export the transaction list
83 0 : connect(exportButton, &QPushButton::clicked, transactionView, &TransactionView::exportClicked);
84 :
85 : // Pass through messages from sendCoinsPage
86 0 : connect(sendCoinsPage, &SendCoinsDialog::message, this, &WalletView::message);
87 : // Pass through messages from transactionView
88 0 : connect(transactionView, &TransactionView::message, this, &WalletView::message);
89 :
90 0 : connect(this, &WalletView::setPrivacy, overviewPage, &OverviewPage::setPrivacy);
91 0 : }
92 :
93 0 : WalletView::~WalletView()
94 0 : {
95 0 : }
96 :
97 0 : void WalletView::setClientModel(ClientModel *_clientModel)
98 : {
99 0 : this->clientModel = _clientModel;
100 :
101 0 : overviewPage->setClientModel(_clientModel);
102 0 : sendCoinsPage->setClientModel(_clientModel);
103 0 : if (walletModel) walletModel->setClientModel(_clientModel);
104 0 : }
105 :
106 0 : void WalletView::setWalletModel(WalletModel *_walletModel)
107 : {
108 0 : this->walletModel = _walletModel;
109 :
110 : // Put transaction list in tabs
111 0 : transactionView->setModel(_walletModel);
112 0 : overviewPage->setWalletModel(_walletModel);
113 0 : receiveCoinsPage->setModel(_walletModel);
114 0 : sendCoinsPage->setModel(_walletModel);
115 0 : usedReceivingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr);
116 0 : usedSendingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr);
117 :
118 0 : if (_walletModel)
119 : {
120 : // Receive and pass through messages from wallet model
121 0 : connect(_walletModel, &WalletModel::message, this, &WalletView::message);
122 :
123 : // Handle changes in encryption status
124 0 : connect(_walletModel, &WalletModel::encryptionStatusChanged, this, &WalletView::encryptionStatusChanged);
125 0 : updateEncryptionStatus();
126 :
127 : // update HD status
128 0 : Q_EMIT hdEnabledStatusChanged();
129 :
130 : // Balloon pop-up for new transaction
131 0 : connect(_walletModel->getTransactionTableModel(), &TransactionTableModel::rowsInserted, this, &WalletView::processNewTransaction);
132 :
133 : // Ask for passphrase if needed
134 0 : connect(_walletModel, &WalletModel::requireUnlock, this, &WalletView::unlockWallet);
135 :
136 : // Show progress dialog
137 0 : connect(_walletModel, &WalletModel::showProgress, this, &WalletView::showProgress);
138 0 : }
139 0 : }
140 :
141 0 : void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/)
142 : {
143 : // Prevent balloon-spam when initial block download is in progress
144 0 : if (!walletModel || !clientModel || clientModel->node().isInitialBlockDownload())
145 : return;
146 :
147 0 : TransactionTableModel *ttm = walletModel->getTransactionTableModel();
148 0 : if (!ttm || ttm->processingQueuedTransactions())
149 0 : return;
150 :
151 0 : QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString();
152 0 : qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();
153 0 : QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString();
154 0 : QModelIndex index = ttm->index(start, 0, parent);
155 0 : QString address = ttm->data(index, TransactionTableModel::AddressRole).toString();
156 0 : QString label = GUIUtil::HtmlEscape(ttm->data(index, TransactionTableModel::LabelRole).toString());
157 :
158 0 : Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label, GUIUtil::HtmlEscape(walletModel->getWalletName()));
159 0 : }
160 :
161 0 : void WalletView::gotoOverviewPage()
162 : {
163 0 : setCurrentWidget(overviewPage);
164 0 : }
165 :
166 0 : void WalletView::gotoHistoryPage()
167 : {
168 0 : setCurrentWidget(transactionsPage);
169 0 : }
170 :
171 0 : void WalletView::gotoReceiveCoinsPage()
172 : {
173 0 : setCurrentWidget(receiveCoinsPage);
174 0 : }
175 :
176 0 : void WalletView::gotoSendCoinsPage(QString addr)
177 : {
178 0 : setCurrentWidget(sendCoinsPage);
179 :
180 0 : if (!addr.isEmpty())
181 0 : sendCoinsPage->setAddress(addr);
182 0 : }
183 :
184 0 : void WalletView::gotoSignMessageTab(QString addr)
185 : {
186 : // calls show() in showTab_SM()
187 0 : SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this);
188 0 : signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
189 0 : signVerifyMessageDialog->setModel(walletModel);
190 0 : signVerifyMessageDialog->showTab_SM(true);
191 :
192 0 : if (!addr.isEmpty())
193 0 : signVerifyMessageDialog->setAddress_SM(addr);
194 0 : }
195 :
196 0 : void WalletView::gotoVerifyMessageTab(QString addr)
197 : {
198 : // calls show() in showTab_VM()
199 0 : SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(platformStyle, this);
200 0 : signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose);
201 0 : signVerifyMessageDialog->setModel(walletModel);
202 0 : signVerifyMessageDialog->showTab_VM(true);
203 :
204 0 : if (!addr.isEmpty())
205 0 : signVerifyMessageDialog->setAddress_VM(addr);
206 0 : }
207 :
208 0 : void WalletView::gotoLoadPSBT(bool from_clipboard)
209 : {
210 0 : std::string data;
211 :
212 0 : if (from_clipboard) {
213 0 : std::string raw = QApplication::clipboard()->text().toStdString();
214 0 : bool invalid;
215 0 : data = DecodeBase64(raw, &invalid);
216 0 : if (invalid) {
217 0 : Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR);
218 0 : return;
219 : }
220 0 : } else {
221 0 : QString filename = GUIUtil::getOpenFileName(this,
222 0 : tr("Load Transaction Data"), QString(),
223 0 : tr("Partially Signed Transaction (*.psbt)"), nullptr);
224 0 : if (filename.isEmpty()) return;
225 0 : if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
226 0 : Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
227 0 : return;
228 : }
229 0 : std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
230 0 : data = std::string(std::istreambuf_iterator<char>{in}, {});
231 0 : }
232 :
233 0 : std::string error;
234 0 : PartiallySignedTransaction psbtx;
235 0 : if (!DecodeRawPSBT(psbtx, data, error)) {
236 0 : Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
237 0 : return;
238 : }
239 :
240 0 : PSBTOperationsDialog* dlg = new PSBTOperationsDialog(this, walletModel, clientModel);
241 0 : dlg->openWithPSBT(psbtx);
242 0 : dlg->setAttribute(Qt::WA_DeleteOnClose);
243 0 : dlg->exec();
244 0 : }
245 :
246 0 : bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
247 : {
248 0 : return sendCoinsPage->handlePaymentRequest(recipient);
249 : }
250 :
251 0 : void WalletView::showOutOfSyncWarning(bool fShow)
252 : {
253 0 : overviewPage->showOutOfSyncWarning(fShow);
254 0 : }
255 :
256 0 : void WalletView::updateEncryptionStatus()
257 : {
258 0 : Q_EMIT encryptionStatusChanged();
259 0 : }
260 :
261 0 : void WalletView::encryptWallet(bool status)
262 : {
263 0 : if(!walletModel)
264 : return;
265 0 : AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt : AskPassphraseDialog::Decrypt, this);
266 0 : dlg.setModel(walletModel);
267 0 : dlg.exec();
268 :
269 0 : updateEncryptionStatus();
270 0 : }
271 :
272 0 : void WalletView::backupWallet()
273 : {
274 0 : QString filename = GUIUtil::getSaveFileName(this,
275 0 : tr("Backup Wallet"), QString(),
276 0 : tr("Wallet Data (*.dat)"), nullptr);
277 :
278 0 : if (filename.isEmpty())
279 0 : return;
280 :
281 0 : if (!walletModel->wallet().backupWallet(filename.toLocal8Bit().data())) {
282 0 : Q_EMIT message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename),
283 : CClientUIInterface::MSG_ERROR);
284 0 : }
285 : else {
286 0 : Q_EMIT message(tr("Backup Successful"), tr("The wallet data was successfully saved to %1.").arg(filename),
287 : CClientUIInterface::MSG_INFORMATION);
288 : }
289 0 : }
290 :
291 0 : void WalletView::changePassphrase()
292 : {
293 0 : AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
294 0 : dlg.setModel(walletModel);
295 0 : dlg.exec();
296 0 : }
297 :
298 0 : void WalletView::unlockWallet()
299 : {
300 0 : if(!walletModel)
301 : return;
302 : // Unlock wallet when requested by wallet model
303 0 : if (walletModel->getEncryptionStatus() == WalletModel::Locked)
304 : {
305 0 : AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
306 0 : dlg.setModel(walletModel);
307 0 : dlg.exec();
308 0 : }
309 0 : }
310 :
311 0 : void WalletView::usedSendingAddresses()
312 : {
313 0 : if(!walletModel)
314 : return;
315 :
316 0 : GUIUtil::bringToFront(usedSendingAddressesPage);
317 0 : }
318 :
319 0 : void WalletView::usedReceivingAddresses()
320 : {
321 0 : if(!walletModel)
322 : return;
323 :
324 0 : GUIUtil::bringToFront(usedReceivingAddressesPage);
325 0 : }
326 :
327 0 : void WalletView::showProgress(const QString &title, int nProgress)
328 : {
329 0 : if (nProgress == 0) {
330 0 : progressDialog = new QProgressDialog(title, tr("Cancel"), 0, 100);
331 0 : GUIUtil::PolishProgressDialog(progressDialog);
332 0 : progressDialog->setWindowModality(Qt::ApplicationModal);
333 0 : progressDialog->setMinimumDuration(0);
334 0 : progressDialog->setAutoClose(false);
335 0 : progressDialog->setValue(0);
336 0 : } else if (nProgress == 100) {
337 0 : if (progressDialog) {
338 0 : progressDialog->close();
339 0 : progressDialog->deleteLater();
340 0 : progressDialog = nullptr;
341 0 : }
342 0 : } else if (progressDialog) {
343 0 : if (progressDialog->wasCanceled()) {
344 0 : getWalletModel()->wallet().abortRescan();
345 0 : } else {
346 0 : progressDialog->setValue(nProgress);
347 : }
348 : }
349 0 : }
350 :
351 0 : void WalletView::requestedSyncWarningInfo()
352 : {
353 0 : Q_EMIT outOfSyncWarningClicked();
354 0 : }
|