Skip to content

Commit 2d7585e

Browse files
committed
Fix Requested Payments History Multiselect
The recent payment requests list has somewhat broken functionality. You can only select contiguous rows. Sorting the list loses the selection data. The context menu appears when multiple rows are selected despite the actions only affecting the first in the list. These issues are all corrected. First, a QSortFilterProxyModel is inserted to take care of sorting instead of sorting the model manually. This also preserves selection data when sorting. Second, the selection model is changed to ExtendedSelection to allow for non-contiguous multi- select. Third, the context menu is disabled when multiple rows are selected, as none of the menu items operate on multiple selected items.
1 parent 78aee0f commit 2d7585e

File tree

4 files changed

+74
-68
lines changed

4 files changed

+74
-68
lines changed

src/qt/receivecoinsdialog.cpp

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5+
#include <reverse_iterator.h>
56
#include <wallet/wallet.h>
67

78
#include <qt/receivecoinsdialog.h>
@@ -30,6 +31,9 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
3031
{
3132
ui->setupUi(this);
3233

34+
m_sort_proxy = new QSortFilterProxyModel(this);
35+
m_sort_proxy->setSortRole(Qt::UserRole);
36+
3337
if (!_platformStyle->getImagesOnButtons()) {
3438
ui->clearButton->setIcon(QIcon());
3539
ui->receiveButton->setIcon(QIcon());
@@ -57,7 +61,7 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle, QWid
5761
tableView->verticalHeader()->hide();
5862
tableView->setAlternatingRowColors(true);
5963
tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
60-
tableView->setSelectionMode(QAbstractItemView::ContiguousSelection);
64+
tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
6165

6266
QSettings settings;
6367
if (!tableView->horizontalHeader()->restoreState(settings.value("RecentRequestsViewHeaderState").toByteArray())) {
@@ -75,12 +79,12 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
7579

7680
if(_model && _model->getOptionsModel())
7781
{
78-
_model->getRecentRequestsTableModel()->sort(RecentRequestsTableModel::Date, Qt::DescendingOrder);
7982
connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &ReceiveCoinsDialog::updateDisplayUnit);
8083
updateDisplayUnit();
8184

8285
QTableView* tableView = ui->recentRequestsView;
83-
tableView->setModel(_model->getRecentRequestsTableModel());
86+
tableView->setModel(m_sort_proxy);
87+
m_sort_proxy->setSourceModel(_model->getRecentRequestsTableModel());
8488
tableView->sortByColumn(RecentRequestsTableModel::Date, Qt::DescendingOrder);
8589

8690
connect(tableView->selectionModel(),
@@ -192,12 +196,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked()
192196

193197
void ReceiveCoinsDialog::on_recentRequestsView_doubleClicked(const QModelIndex &index)
194198
{
195-
const RecentRequestsTableModel *submodel = model->getRecentRequestsTableModel();
196-
ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
197-
dialog->setModel(model);
198-
dialog->setInfo(submodel->entry(index.row()).recipient);
199-
dialog->setAttribute(Qt::WA_DeleteOnClose);
200-
dialog->show();
199+
ShowReceiveRequestDialogForItem(selectedRow());
201200
}
202201

203202
void ReceiveCoinsDialog::recentRequestsView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
@@ -210,37 +209,55 @@ void ReceiveCoinsDialog::recentRequestsView_selectionChanged(const QItemSelectio
210209

211210
void ReceiveCoinsDialog::on_showRequestButton_clicked()
212211
{
213-
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
214-
return;
215-
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
212+
QModelIndexList selection = SelectedRows();
216213

217214
for (const QModelIndex& index : selection) {
218-
on_recentRequestsView_doubleClicked(index);
215+
ShowReceiveRequestDialogForItem(index);
219216
}
220217
}
221218

222219
void ReceiveCoinsDialog::on_removeRequestButton_clicked()
223220
{
224-
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
225-
return;
226-
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
221+
QModelIndexList selection = SelectedRows();
227222
if(selection.empty())
228223
return;
229-
// correct for selection mode ContiguousSelection
230-
QModelIndex firstIndex = selection.at(0);
231-
model->getRecentRequestsTableModel()->removeRows(firstIndex.row(), selection.length(), firstIndex.parent());
224+
225+
// Collect row indices in a set (sorted) and pass in reverse order to removeRows
226+
// to avoid having to keep track of changed source indices after each removal
227+
std::set<int> row_indices;
228+
for (const QModelIndex& ind : selection) {
229+
row_indices.insert(ind.row());
230+
}
231+
232+
for (auto row_ind : reverse_iterate(row_indices)) {
233+
model->getRecentRequestsTableModel()->removeRows(row_ind, 1);
234+
}
232235
}
233236

234237
QModelIndex ReceiveCoinsDialog::selectedRow()
235238
{
236239
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
237240
return QModelIndex();
238241
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
239-
if(selection.empty())
242+
// Only allow single-selection in this method
243+
if (selection.size() != 1)
240244
return QModelIndex();
241-
// correct for selection mode ContiguousSelection
242-
QModelIndex firstIndex = selection.at(0);
243-
return firstIndex;
245+
246+
QModelIndex row_ind = m_sort_proxy->mapToSource(selection.at(0));
247+
return row_ind;
248+
}
249+
250+
QModelIndexList ReceiveCoinsDialog::SelectedRows()
251+
{
252+
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
253+
return QModelIndexList();
254+
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
255+
QModelIndexList source_mapped;
256+
for (auto row : selection) {
257+
source_mapped.append(m_sort_proxy->mapToSource(row));
258+
}
259+
260+
return source_mapped;
244261
}
245262

246263
// copy column of selected row to clipboard
@@ -253,6 +270,19 @@ void ReceiveCoinsDialog::copyColumnToClipboard(int column)
253270
GUIUtil::setClipboard(model->getRecentRequestsTableModel()->index(firstIndex.row(), column).data(Qt::EditRole).toString());
254271
}
255272

273+
void ReceiveCoinsDialog::ShowReceiveRequestDialogForItem(const QModelIndex& index)
274+
{
275+
if (!index.isValid()) {
276+
return;
277+
}
278+
const RecentRequestsTableModel *submodel = model->getRecentRequestsTableModel();
279+
ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
280+
dialog->setModel(model);
281+
dialog->setInfo(submodel->entry(index.row()).recipient);
282+
dialog->setAttribute(Qt::WA_DeleteOnClose);
283+
dialog->show();
284+
}
285+
256286
// context menu
257287
void ReceiveCoinsDialog::showMenu(const QPoint &point)
258288
{

src/qt/receivecoinsdialog.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <QKeyEvent>
1414
#include <QMenu>
1515
#include <QPoint>
16+
#include <QSortFilterProxyModel>
1617
#include <QVariant>
1718

1819
class PlatformStyle;
@@ -51,15 +52,19 @@ public Q_SLOTS:
5152

5253
private:
5354
Ui::ReceiveCoinsDialog *ui;
55+
QSortFilterProxyModel* m_sort_proxy;
5456
WalletModel *model;
5557
QMenu *contextMenu;
5658
QAction* copyLabelAction;
5759
QAction* copyMessageAction;
5860
QAction* copyAmountAction;
5961
const PlatformStyle *platformStyle;
6062

63+
// Returns QModelIndex in source model
6164
QModelIndex selectedRow();
65+
QModelIndexList SelectedRows();
6266
void copyColumnToClipboard(int column);
67+
void ShowReceiveRequestDialogForItem(const QModelIndex& index);
6368

6469
private Q_SLOTS:
6570
void on_receiveButton_clicked();

src/qt/recentrequeststablemodel.cpp

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,23 @@ QVariant RecentRequestsTableModel::data(const QModelIndex &index, int role) cons
9090
else
9191
return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount);
9292
}
93-
}
94-
else if (role == Qt::TextAlignmentRole)
95-
{
93+
} else if (role == Qt::TextAlignmentRole) {
9694
if (index.column() == Amount)
97-
return (int)(Qt::AlignRight|Qt::AlignVCenter);
95+
return (int)(Qt::AlignRight | Qt::AlignVCenter);
96+
} else if (role == Qt::UserRole) {
97+
const RecentRequestEntry* rec = &list[index.row()];
98+
switch (index.column()) {
99+
case Date:
100+
return rec->date;
101+
case Label:
102+
return rec->recipient.label;
103+
case Message:
104+
return rec->recipient.message;
105+
case Amount:
106+
return rec->recipient.amount;
107+
default:
108+
return QVariant();
109+
}
98110
}
99111
return QVariant();
100112
}
@@ -210,35 +222,7 @@ void RecentRequestsTableModel::addNewRequest(RecentRequestEntry &recipient)
210222
endInsertRows();
211223
}
212224

213-
void RecentRequestsTableModel::sort(int column, Qt::SortOrder order)
214-
{
215-
std::sort(list.begin(), list.end(), RecentRequestEntryLessThan(column, order));
216-
Q_EMIT dataChanged(index(0, 0, QModelIndex()), index(list.size() - 1, NUMBER_OF_COLUMNS - 1, QModelIndex()));
217-
}
218-
219225
void RecentRequestsTableModel::updateDisplayUnit()
220226
{
221227
updateAmountColumnTitle();
222228
}
223-
224-
bool RecentRequestEntryLessThan::operator()(const RecentRequestEntry& left, const RecentRequestEntry& right) const
225-
{
226-
const RecentRequestEntry* pLeft = &left;
227-
const RecentRequestEntry* pRight = &right;
228-
if (order == Qt::DescendingOrder)
229-
std::swap(pLeft, pRight);
230-
231-
switch(column)
232-
{
233-
case RecentRequestsTableModel::Date:
234-
return pLeft->date.toSecsSinceEpoch() < pRight->date.toSecsSinceEpoch();
235-
case RecentRequestsTableModel::Label:
236-
return pLeft->recipient.label < pRight->recipient.label;
237-
case RecentRequestsTableModel::Message:
238-
return pLeft->recipient.message < pRight->recipient.message;
239-
case RecentRequestsTableModel::Amount:
240-
return pLeft->recipient.amount < pRight->recipient.amount;
241-
default:
242-
return pLeft->id < pRight->id;
243-
}
244-
}

src/qt/recentrequeststablemodel.h

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,6 @@ class RecentRequestEntry
3434
}
3535
};
3636

37-
class RecentRequestEntryLessThan
38-
{
39-
public:
40-
RecentRequestEntryLessThan(int nColumn, Qt::SortOrder fOrder):
41-
column(nColumn), order(fOrder) {}
42-
bool operator()(const RecentRequestEntry& left, const RecentRequestEntry& right) const;
43-
44-
private:
45-
int column;
46-
Qt::SortOrder order;
47-
};
48-
4937
/** Model for list of recently generated payment requests / bitcoin: URIs.
5038
* Part of wallet model.
5139
*/
@@ -75,7 +63,6 @@ class RecentRequestsTableModel: public QAbstractTableModel
7563
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
7664
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
7765
Qt::ItemFlags flags(const QModelIndex &index) const override;
78-
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
7966
/*@}*/
8067

8168
const RecentRequestEntry &entry(int row) const { return list[row]; }

0 commit comments

Comments
 (0)