Line data Source code
1 : // Copyright (c) 2011-2019 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 <wallet/wallet.h>
6 :
7 : #include <qt/receivecoinsdialog.h>
8 : #include <qt/forms/ui_receivecoinsdialog.h>
9 :
10 : #include <qt/addresstablemodel.h>
11 : #include <qt/optionsmodel.h>
12 : #include <qt/platformstyle.h>
13 : #include <qt/receiverequestdialog.h>
14 : #include <qt/recentrequeststablemodel.h>
15 : #include <qt/walletmodel.h>
16 :
17 : #include <QAction>
18 : #include <QCursor>
19 : #include <QMessageBox>
20 : #include <QScrollBar>
21 : #include <QTextDocument>
22 :
23 0 : ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
24 0 : QDialog(parent),
25 0 : ui(new Ui::ReceiveCoinsDialog),
26 0 : columnResizingFixer(nullptr),
27 0 : model(nullptr),
28 0 : platformStyle(_platformStyle)
29 0 : {
30 0 : ui->setupUi(this);
31 :
32 0 : if (!_platformStyle->getImagesOnButtons()) {
33 0 : ui->clearButton->setIcon(QIcon());
34 0 : ui->receiveButton->setIcon(QIcon());
35 0 : ui->showRequestButton->setIcon(QIcon());
36 0 : ui->removeRequestButton->setIcon(QIcon());
37 0 : } else {
38 0 : ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
39 0 : ui->receiveButton->setIcon(_platformStyle->SingleColorIcon(":/icons/receiving_addresses"));
40 0 : ui->showRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/edit"));
41 0 : ui->removeRequestButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
42 : }
43 :
44 : // context menu actions
45 0 : QAction *copyURIAction = new QAction(tr("Copy URI"), this);
46 0 : QAction *copyLabelAction = new QAction(tr("Copy label"), this);
47 0 : QAction *copyMessageAction = new QAction(tr("Copy message"), this);
48 0 : QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
49 :
50 : // context menu
51 0 : contextMenu = new QMenu(this);
52 0 : contextMenu->addAction(copyURIAction);
53 0 : contextMenu->addAction(copyLabelAction);
54 0 : contextMenu->addAction(copyMessageAction);
55 0 : contextMenu->addAction(copyAmountAction);
56 :
57 : // context menu signals
58 0 : connect(ui->recentRequestsView, &QWidget::customContextMenuRequested, this, &ReceiveCoinsDialog::showMenu);
59 0 : connect(copyURIAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyURI);
60 0 : connect(copyLabelAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyLabel);
61 0 : connect(copyMessageAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyMessage);
62 0 : connect(copyAmountAction, &QAction::triggered, this, &ReceiveCoinsDialog::copyAmount);
63 :
64 0 : connect(ui->clearButton, &QPushButton::clicked, this, &ReceiveCoinsDialog::clear);
65 0 : }
66 :
67 0 : void ReceiveCoinsDialog::setModel(WalletModel *_model)
68 : {
69 0 : this->model = _model;
70 :
71 0 : if(_model && _model->getOptionsModel())
72 : {
73 0 : _model->getRecentRequestsTableModel()->sort(RecentRequestsTableModel::Date, Qt::DescendingOrder);
74 0 : connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &ReceiveCoinsDialog::updateDisplayUnit);
75 0 : updateDisplayUnit();
76 :
77 0 : QTableView* tableView = ui->recentRequestsView;
78 :
79 0 : tableView->verticalHeader()->hide();
80 0 : tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
81 0 : tableView->setModel(_model->getRecentRequestsTableModel());
82 0 : tableView->setAlternatingRowColors(true);
83 0 : tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
84 0 : tableView->setSelectionMode(QAbstractItemView::ContiguousSelection);
85 0 : tableView->setColumnWidth(RecentRequestsTableModel::Date, DATE_COLUMN_WIDTH);
86 0 : tableView->setColumnWidth(RecentRequestsTableModel::Label, LABEL_COLUMN_WIDTH);
87 0 : tableView->setColumnWidth(RecentRequestsTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH);
88 :
89 0 : connect(tableView->selectionModel(),
90 : &QItemSelectionModel::selectionChanged, this,
91 : &ReceiveCoinsDialog::recentRequestsView_selectionChanged);
92 : // Last 2 columns are set by the columnResizingFixer, when the table geometry is ready.
93 0 : columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this);
94 :
95 0 : if (model->wallet().getDefaultAddressType() == OutputType::BECH32) {
96 0 : ui->useBech32->setCheckState(Qt::Checked);
97 0 : } else {
98 0 : ui->useBech32->setCheckState(Qt::Unchecked);
99 : }
100 :
101 : // Set the button to be enabled or disabled based on whether the wallet can give out new addresses.
102 0 : ui->receiveButton->setEnabled(model->wallet().canGetAddresses());
103 :
104 : // Enable/disable the receive button if the wallet is now able/unable to give out new addresses.
105 0 : connect(model, &WalletModel::canGetAddressesChanged, [this] {
106 0 : ui->receiveButton->setEnabled(model->wallet().canGetAddresses());
107 0 : });
108 0 : }
109 0 : }
110 :
111 0 : ReceiveCoinsDialog::~ReceiveCoinsDialog()
112 0 : {
113 0 : delete ui;
114 0 : }
115 :
116 0 : void ReceiveCoinsDialog::clear()
117 : {
118 0 : ui->reqAmount->clear();
119 0 : ui->reqLabel->setText("");
120 0 : ui->reqMessage->setText("");
121 0 : updateDisplayUnit();
122 0 : }
123 :
124 0 : void ReceiveCoinsDialog::reject()
125 : {
126 0 : clear();
127 0 : }
128 :
129 0 : void ReceiveCoinsDialog::accept()
130 : {
131 0 : clear();
132 0 : }
133 :
134 0 : void ReceiveCoinsDialog::updateDisplayUnit()
135 : {
136 0 : if(model && model->getOptionsModel())
137 : {
138 0 : ui->reqAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
139 0 : }
140 0 : }
141 :
142 0 : void ReceiveCoinsDialog::on_receiveButton_clicked()
143 : {
144 0 : if(!model || !model->getOptionsModel() || !model->getAddressTableModel() || !model->getRecentRequestsTableModel())
145 : return;
146 :
147 0 : QString address;
148 0 : QString label = ui->reqLabel->text();
149 : /* Generate new receiving address */
150 : OutputType address_type;
151 0 : if (ui->useBech32->isChecked()) {
152 : address_type = OutputType::BECH32;
153 0 : } else {
154 0 : address_type = model->wallet().getDefaultAddressType();
155 0 : if (address_type == OutputType::BECH32) {
156 : address_type = OutputType::P2SH_SEGWIT;
157 : }
158 : }
159 0 : address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, "", address_type);
160 :
161 0 : switch(model->getAddressTableModel()->getEditStatus())
162 : {
163 : case AddressTableModel::EditStatus::OK: {
164 : // Success
165 0 : SendCoinsRecipient info(address, label,
166 0 : ui->reqAmount->value(), ui->reqMessage->text());
167 0 : ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
168 0 : dialog->setAttribute(Qt::WA_DeleteOnClose);
169 0 : dialog->setModel(model);
170 0 : dialog->setInfo(info);
171 0 : dialog->show();
172 :
173 : /* Store request for later reference */
174 0 : model->getRecentRequestsTableModel()->addNewRequest(info);
175 : break;
176 0 : }
177 : case AddressTableModel::EditStatus::WALLET_UNLOCK_FAILURE:
178 0 : QMessageBox::critical(this, windowTitle(),
179 0 : tr("Could not unlock wallet."),
180 : QMessageBox::Ok, QMessageBox::Ok);
181 0 : break;
182 : case AddressTableModel::EditStatus::KEY_GENERATION_FAILURE:
183 0 : QMessageBox::critical(this, windowTitle(),
184 0 : tr("Could not generate new %1 address").arg(QString::fromStdString(FormatOutputType(address_type))),
185 : QMessageBox::Ok, QMessageBox::Ok);
186 0 : break;
187 : // These aren't valid return values for our action
188 : case AddressTableModel::EditStatus::INVALID_ADDRESS:
189 : case AddressTableModel::EditStatus::DUPLICATE_ADDRESS:
190 : case AddressTableModel::EditStatus::NO_CHANGES:
191 0 : assert(false);
192 : }
193 0 : clear();
194 0 : }
195 :
196 0 : void ReceiveCoinsDialog::on_recentRequestsView_doubleClicked(const QModelIndex &index)
197 : {
198 0 : const RecentRequestsTableModel *submodel = model->getRecentRequestsTableModel();
199 0 : ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
200 0 : dialog->setModel(model);
201 0 : dialog->setInfo(submodel->entry(index.row()).recipient);
202 0 : dialog->setAttribute(Qt::WA_DeleteOnClose);
203 0 : dialog->show();
204 0 : }
205 :
206 0 : void ReceiveCoinsDialog::recentRequestsView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
207 : {
208 : // Enable Show/Remove buttons only if anything is selected.
209 0 : bool enable = !ui->recentRequestsView->selectionModel()->selectedRows().isEmpty();
210 0 : ui->showRequestButton->setEnabled(enable);
211 0 : ui->removeRequestButton->setEnabled(enable);
212 0 : }
213 :
214 0 : void ReceiveCoinsDialog::on_showRequestButton_clicked()
215 : {
216 0 : if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
217 : return;
218 0 : QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
219 :
220 0 : for (const QModelIndex& index : selection) {
221 0 : on_recentRequestsView_doubleClicked(index);
222 0 : }
223 0 : }
224 :
225 0 : void ReceiveCoinsDialog::on_removeRequestButton_clicked()
226 : {
227 0 : if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
228 : return;
229 0 : QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
230 0 : if(selection.empty())
231 0 : return;
232 : // correct for selection mode ContiguousSelection
233 0 : QModelIndex firstIndex = selection.at(0);
234 0 : model->getRecentRequestsTableModel()->removeRows(firstIndex.row(), selection.length(), firstIndex.parent());
235 0 : }
236 :
237 : // We override the virtual resizeEvent of the QWidget to adjust tables column
238 : // sizes as the tables width is proportional to the dialogs width.
239 0 : void ReceiveCoinsDialog::resizeEvent(QResizeEvent *event)
240 : {
241 0 : QWidget::resizeEvent(event);
242 0 : columnResizingFixer->stretchColumnWidth(RecentRequestsTableModel::Message);
243 0 : }
244 :
245 0 : QModelIndex ReceiveCoinsDialog::selectedRow()
246 : {
247 0 : if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
248 0 : return QModelIndex();
249 0 : QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
250 0 : if(selection.empty())
251 0 : return QModelIndex();
252 : // correct for selection mode ContiguousSelection
253 0 : QModelIndex firstIndex = selection.at(0);
254 0 : return firstIndex;
255 0 : }
256 :
257 : // copy column of selected row to clipboard
258 0 : void ReceiveCoinsDialog::copyColumnToClipboard(int column)
259 : {
260 0 : QModelIndex firstIndex = selectedRow();
261 0 : if (!firstIndex.isValid()) {
262 0 : return;
263 : }
264 0 : GUIUtil::setClipboard(model->getRecentRequestsTableModel()->index(firstIndex.row(), column).data(Qt::EditRole).toString());
265 0 : }
266 :
267 : // context menu
268 0 : void ReceiveCoinsDialog::showMenu(const QPoint &point)
269 : {
270 0 : if (!selectedRow().isValid()) {
271 : return;
272 : }
273 0 : contextMenu->exec(QCursor::pos());
274 0 : }
275 :
276 : // context menu action: copy URI
277 0 : void ReceiveCoinsDialog::copyURI()
278 : {
279 0 : QModelIndex sel = selectedRow();
280 0 : if (!sel.isValid()) {
281 0 : return;
282 : }
283 :
284 0 : const RecentRequestsTableModel * const submodel = model->getRecentRequestsTableModel();
285 0 : const QString uri = GUIUtil::formatBitcoinURI(submodel->entry(sel.row()).recipient);
286 0 : GUIUtil::setClipboard(uri);
287 0 : }
288 :
289 : // context menu action: copy label
290 0 : void ReceiveCoinsDialog::copyLabel()
291 : {
292 0 : copyColumnToClipboard(RecentRequestsTableModel::Label);
293 0 : }
294 :
295 : // context menu action: copy message
296 0 : void ReceiveCoinsDialog::copyMessage()
297 : {
298 0 : copyColumnToClipboard(RecentRequestsTableModel::Message);
299 0 : }
300 :
301 : // context menu action: copy amount
302 0 : void ReceiveCoinsDialog::copyAmount()
303 : {
304 0 : copyColumnToClipboard(RecentRequestsTableModel::Amount);
305 0 : }
|