2020-10-07 10:36:04 +00:00
// SPDX-License-Identifier: BSD-3-Clause
2023-01-02 19:30:11 +00:00
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
2020-10-07 10:36:04 +00:00
2021-06-28 17:48:23 +00:00
# include "CoinsWidget.h"
# include "ui_CoinsWidget.h"
# include <QMessageBox>
2021-06-27 11:46:32 +00:00
# include "dialog/OutputInfoDialog.h"
# include "dialog/OutputSweepDialog.h"
2021-05-02 18:22:38 +00:00
# include "utils/Icons.h"
2023-03-01 02:05:56 +00:00
# include "utils/Utils.h"
2020-10-07 10:36:04 +00:00
2023-03-01 02:05:56 +00:00
CoinsWidget : : CoinsWidget ( Wallet * wallet , QWidget * parent )
2021-06-28 17:48:23 +00:00
: QWidget ( parent )
, ui ( new Ui : : CoinsWidget )
2023-03-01 02:05:56 +00:00
, m_wallet ( wallet )
2021-06-28 17:48:23 +00:00
, m_headerMenu ( new QMenu ( this ) )
, m_copyMenu ( new QMenu ( " Copy " , this ) )
2020-10-07 10:36:04 +00:00
{
ui - > setupUi ( this ) ;
// header context menu
ui - > coins - > header ( ) - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
m_showSpentAction = m_headerMenu - > addAction ( " Show spent outputs " , this , & CoinsWidget : : setShowSpent ) ;
m_showSpentAction - > setCheckable ( true ) ;
connect ( ui - > coins - > header ( ) , & QHeaderView : : customContextMenuRequested , this , & CoinsWidget : : showHeaderMenu ) ;
// copy menu
2021-10-23 20:42:49 +00:00
m_copyMenu - > addAction ( " Public Key " , this , [ this ] { copy ( copyField : : PubKey ) ; } ) ;
2020-10-07 10:36:04 +00:00
m_copyMenu - > addAction ( " Key Image " , this , [ this ] { copy ( copyField : : KeyImage ) ; } ) ;
m_copyMenu - > addAction ( " Transaction ID " , this , [ this ] { copy ( copyField : : TxID ) ; } ) ;
m_copyMenu - > addAction ( " Address " , this , [ this ] { copy ( copyField : : Address ) ; } ) ;
2020-12-14 01:30:30 +00:00
m_copyMenu - > addAction ( " Label " , this , [ this ] { copy ( copyField : : Label ) ; } ) ;
2020-10-07 10:36:04 +00:00
m_copyMenu - > addAction ( " Height " , this , [ this ] { copy ( copyField : : Height ) ; } ) ;
m_copyMenu - > addAction ( " Amount " , this , [ this ] { copy ( copyField : : Amount ) ; } ) ;
// context menu
ui - > coins - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
2021-07-02 15:27:26 +00:00
m_editLabelAction = new QAction ( " Edit Label " , this ) ;
connect ( m_editLabelAction , & QAction : : triggered , this , & CoinsWidget : : editLabel ) ;
2020-10-21 14:53:17 +00:00
m_thawOutputAction = new QAction ( " Thaw output " , this ) ;
m_freezeOutputAction = new QAction ( " Freeze output " , this ) ;
2020-10-07 10:36:04 +00:00
2020-10-21 14:53:17 +00:00
m_freezeAllSelectedAction = new QAction ( " Freeze selected " , this ) ;
m_thawAllSelectedAction = new QAction ( " Thaw selected " , this ) ;
2020-10-07 10:36:04 +00:00
2022-03-20 22:30:48 +00:00
m_spendAction = new QAction ( " Spend " , this ) ;
2021-10-23 20:42:49 +00:00
m_viewOutputAction = new QAction ( " Details " , this ) ;
2020-10-21 14:53:17 +00:00
m_sweepOutputAction = new QAction ( " Sweep output " , this ) ;
2021-06-28 16:05:21 +00:00
m_sweepOutputsAction = new QAction ( " Sweep selected outputs " , this ) ;
2021-07-02 15:27:26 +00:00
2021-10-01 13:37:20 +00:00
connect ( m_freezeOutputAction , & QAction : : triggered , this , & CoinsWidget : : freezeAllSelected ) ;
connect ( m_thawOutputAction , & QAction : : triggered , this , & CoinsWidget : : thawAllSelected ) ;
2022-03-20 22:30:48 +00:00
connect ( m_spendAction , & QAction : : triggered , this , & CoinsWidget : : spendSelected ) ;
2020-10-07 10:36:04 +00:00
connect ( m_viewOutputAction , & QAction : : triggered , this , & CoinsWidget : : viewOutput ) ;
2021-07-02 15:43:47 +00:00
connect ( m_sweepOutputAction , & QAction : : triggered , this , & CoinsWidget : : onSweepOutputs ) ;
connect ( m_sweepOutputsAction , & QAction : : triggered , this , & CoinsWidget : : onSweepOutputs ) ;
2020-10-07 10:36:04 +00:00
connect ( m_freezeAllSelectedAction , & QAction : : triggered , this , & CoinsWidget : : freezeAllSelected ) ;
connect ( m_thawAllSelectedAction , & QAction : : triggered , this , & CoinsWidget : : thawAllSelected ) ;
connect ( ui - > coins , & QTreeView : : customContextMenuRequested , this , & CoinsWidget : : showContextMenu ) ;
2021-07-02 14:12:07 +00:00
connect ( ui - > coins , & QTreeView : : doubleClicked , [ this ] ( QModelIndex index ) {
if ( ! m_model ) return ;
if ( ! ( m_model - > flags ( index ) & Qt : : ItemIsEditable ) ) {
this - > viewOutput ( ) ;
}
} ) ;
2021-06-28 17:20:16 +00:00
connect ( ui - > search , & QLineEdit : : textChanged , this , & CoinsWidget : : setSearchFilter ) ;
2022-03-20 22:30:48 +00:00
2023-03-01 02:05:56 +00:00
connect ( m_wallet , & Wallet : : selectedInputsChanged , this , & CoinsWidget : : selectCoins ) ;
2020-10-07 10:36:04 +00:00
}
void CoinsWidget : : setModel ( CoinsModel * model , Coins * coins ) {
m_coins = coins ;
m_model = model ;
2021-02-03 23:14:20 +00:00
m_proxyModel = new CoinsProxyModel ( this , m_coins ) ;
2020-10-07 10:36:04 +00:00
m_proxyModel - > setSourceModel ( m_model ) ;
ui - > coins - > setModel ( m_proxyModel ) ;
ui - > coins - > setColumnHidden ( CoinsModel : : Spent , true ) ;
ui - > coins - > setColumnHidden ( CoinsModel : : SpentHeight , true ) ;
ui - > coins - > setColumnHidden ( CoinsModel : : Frozen , true ) ;
2023-03-01 02:05:56 +00:00
if ( ! m_wallet - > viewOnly ( ) ) {
2020-10-14 02:21:38 +00:00
ui - > coins - > setColumnHidden ( CoinsModel : : KeyImageKnown , true ) ;
} else {
ui - > coins - > setColumnHidden ( CoinsModel : : KeyImageKnown , false ) ;
}
2020-10-07 10:36:04 +00:00
ui - > coins - > header ( ) - > setSectionResizeMode ( QHeaderView : : ResizeToContents ) ;
2021-07-02 14:12:07 +00:00
ui - > coins - > header ( ) - > setSectionResizeMode ( CoinsModel : : Label , QHeaderView : : Stretch ) ;
2020-10-07 10:36:04 +00:00
ui - > coins - > header ( ) - > setSortIndicator ( CoinsModel : : BlockHeight , Qt : : DescendingOrder ) ;
ui - > coins - > setSortingEnabled ( true ) ;
}
2021-06-28 17:20:16 +00:00
void CoinsWidget : : setSearchbarVisible ( bool visible ) {
ui - > search - > setVisible ( visible ) ;
}
2021-07-02 14:51:46 +00:00
void CoinsWidget : : focusSearchbar ( ) {
ui - > search - > setFocusPolicy ( Qt : : StrongFocus ) ;
ui - > search - > setFocus ( ) ;
}
2020-10-07 10:36:04 +00:00
void CoinsWidget : : showContextMenu ( const QPoint & point ) {
QModelIndexList list = ui - > coins - > selectionModel ( ) - > selectedRows ( ) ;
auto * menu = new QMenu ( ui - > coins ) ;
if ( list . size ( ) > 1 ) {
2022-03-20 22:30:48 +00:00
menu - > addAction ( m_spendAction ) ;
2020-10-07 10:36:04 +00:00
menu - > addAction ( m_freezeAllSelectedAction ) ;
menu - > addAction ( m_thawAllSelectedAction ) ;
2021-06-28 16:05:21 +00:00
menu - > addAction ( m_sweepOutputsAction ) ;
2020-10-07 10:36:04 +00:00
}
else {
2021-03-14 21:12:02 +00:00
CoinsInfo * c = this - > currentEntry ( ) ;
if ( ! c ) return ;
2020-10-07 10:36:04 +00:00
2021-03-14 21:12:02 +00:00
bool isSpent = c - > spent ( ) ;
bool isFrozen = c - > frozen ( ) ;
bool isUnlocked = c - > unlocked ( ) ;
2020-10-07 10:36:04 +00:00
2022-03-20 22:30:48 +00:00
menu - > addAction ( m_spendAction ) ;
2020-10-07 10:36:04 +00:00
menu - > addMenu ( m_copyMenu ) ;
2021-07-02 15:27:26 +00:00
menu - > addAction ( m_editLabelAction ) ;
2020-10-07 10:36:04 +00:00
if ( ! isSpent ) {
isFrozen ? menu - > addAction ( m_thawOutputAction ) : menu - > addAction ( m_freezeOutputAction ) ;
2020-10-21 19:53:46 +00:00
2020-10-07 10:36:04 +00:00
menu - > addAction ( m_sweepOutputAction ) ;
2020-10-21 19:53:46 +00:00
if ( isFrozen | | ! isUnlocked ) {
m_sweepOutputAction - > setDisabled ( true ) ;
} else {
m_sweepOutputAction - > setEnabled ( true ) ;
}
2020-10-07 10:36:04 +00:00
}
2020-10-21 19:53:46 +00:00
2020-10-07 10:36:04 +00:00
menu - > addAction ( m_viewOutputAction ) ;
}
menu - > popup ( ui - > coins - > viewport ( ) - > mapToGlobal ( point ) ) ;
}
void CoinsWidget : : showHeaderMenu ( const QPoint & position )
{
m_headerMenu - > popup ( QCursor : : pos ( ) ) ;
}
void CoinsWidget : : setShowSpent ( bool show )
{
if ( ! m_proxyModel ) return ;
m_proxyModel - > setShowSpent ( show ) ;
}
2021-06-28 17:20:16 +00:00
void CoinsWidget : : setSearchFilter ( const QString & filter ) {
if ( ! m_proxyModel ) return ;
m_proxyModel - > setSearchFilter ( filter ) ;
}
2021-10-01 13:37:20 +00:00
QStringList CoinsWidget : : selectedPubkeys ( ) {
2020-10-07 10:36:04 +00:00
QModelIndexList list = ui - > coins - > selectionModel ( ) - > selectedRows ( ) ;
2021-10-01 13:37:20 +00:00
QStringList pubkeys ;
2020-10-07 10:36:04 +00:00
for ( QModelIndex index : list ) {
2021-10-01 13:37:20 +00:00
pubkeys < < m_model - > entryFromIndex ( m_proxyModel - > mapToSource ( index ) ) - > pubKey ( ) ;
2020-10-07 10:36:04 +00:00
}
2021-10-01 13:37:20 +00:00
return pubkeys ;
2020-10-07 10:36:04 +00:00
}
2021-10-01 13:37:20 +00:00
void CoinsWidget : : freezeAllSelected ( ) {
QStringList pubkeys = this - > selectedPubkeys ( ) ;
this - > freezeCoins ( pubkeys ) ;
2020-10-07 10:36:04 +00:00
}
void CoinsWidget : : thawAllSelected ( ) {
2021-10-01 13:37:20 +00:00
QStringList pubkeys = this - > selectedPubkeys ( ) ;
this - > thawCoins ( pubkeys ) ;
2020-10-07 10:36:04 +00:00
}
2022-03-20 22:30:48 +00:00
void CoinsWidget : : spendSelected ( ) {
QModelIndexList list = ui - > coins - > selectionModel ( ) - > selectedRows ( ) ;
QStringList keyimages ;
for ( QModelIndex index : list ) {
keyimages < < m_model - > entryFromIndex ( m_proxyModel - > mapToSource ( index ) ) - > keyImage ( ) ;
}
2023-03-01 02:05:56 +00:00
m_wallet - > setSelectedInputs ( keyimages ) ;
2022-03-20 22:30:48 +00:00
this - > selectCoins ( keyimages ) ;
}
2020-10-07 10:36:04 +00:00
void CoinsWidget : : viewOutput ( ) {
2021-03-14 21:12:02 +00:00
CoinsInfo * c = this - > currentEntry ( ) ;
if ( ! c ) return ;
2020-10-07 10:36:04 +00:00
2021-03-14 21:12:02 +00:00
auto * dialog = new OutputInfoDialog ( c , this ) ;
dialog - > show ( ) ;
2020-10-07 10:36:04 +00:00
}
2021-07-02 15:43:47 +00:00
void CoinsWidget : : onSweepOutputs ( ) {
2021-06-28 16:05:21 +00:00
QVector < CoinsInfo * > selectedCoins = this - > currentEntries ( ) ;
QVector < QString > keyImages ;
quint64 totalAmount = 0 ;
for ( const auto coin : selectedCoins ) {
if ( ! coin ) return ;
QString keyImage = coin - > keyImage ( ) ;
if ( ! coin - > keyImageKnown ( ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Unable to sweep outputs " , " Selected output has unknown key image " ) ;
2021-06-28 16:05:21 +00:00
return ;
}
2021-07-02 15:43:47 +00:00
if ( coin - > spent ( ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Unable to sweep outputs " , " Selected output was already spent " ) ;
2021-07-02 15:43:47 +00:00
return ;
}
if ( coin - > frozen ( ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Unable to sweep outputs " , " Selected output is frozen " , { " Thaw the selected output(s) before spending " } , " freeze_thaw_outputs " ) ;
2021-07-02 15:43:47 +00:00
return ;
}
if ( ! coin - > unlocked ( ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Unable to sweep outputs " , " Selected output is locked " , { " Wait until the output has reached the required number of confirmation before spending. " } ) ;
2021-07-02 15:43:47 +00:00
return ;
}
2021-06-28 16:05:21 +00:00
keyImages . push_back ( keyImage ) ;
totalAmount + = coin - > amount ( ) ;
}
OutputSweepDialog dialog { this , totalAmount } ;
int ret = dialog . exec ( ) ;
if ( ! ret ) return ;
2023-03-01 02:05:56 +00:00
m_wallet - > sweepOutputs ( keyImages , dialog . address ( ) , dialog . churn ( ) , dialog . outputs ( ) ) ;
2021-06-28 16:05:21 +00:00
}
2020-10-07 10:36:04 +00:00
void CoinsWidget : : copy ( copyField field ) {
2021-03-14 21:12:02 +00:00
CoinsInfo * c = this - > currentEntry ( ) ;
if ( ! c ) return ;
2020-10-07 10:36:04 +00:00
QString data ;
2021-03-14 21:12:02 +00:00
switch ( field ) {
case PubKey :
data = c - > pubKey ( ) ;
break ;
case KeyImage :
data = c - > keyImage ( ) ;
break ;
case TxID :
data = c - > hash ( ) ;
break ;
case Address :
data = c - > address ( ) ;
break ;
2021-07-02 14:12:07 +00:00
case Label : {
if ( ! c - > description ( ) . isEmpty ( ) )
data = c - > description ( ) ;
else
data = c - > addressLabel ( ) ;
2021-03-14 21:12:02 +00:00
break ;
2021-07-02 14:12:07 +00:00
}
2021-03-14 21:12:02 +00:00
case Height :
data = QString : : number ( c - > blockHeight ( ) ) ;
break ;
case Amount :
data = c - > displayAmount ( ) ;
break ;
}
2020-10-07 10:36:04 +00:00
Utils : : copyToClipboard ( data ) ;
}
2021-03-14 21:12:02 +00:00
CoinsInfo * CoinsWidget : : currentEntry ( ) {
QModelIndexList list = ui - > coins - > selectionModel ( ) - > selectedRows ( ) ;
if ( list . size ( ) = = 1 ) {
return m_model - > entryFromIndex ( m_proxyModel - > mapToSource ( list . first ( ) ) ) ;
} else {
return nullptr ;
}
}
2021-06-28 16:05:21 +00:00
QVector < CoinsInfo * > CoinsWidget : : currentEntries ( ) {
QModelIndexList list = ui - > coins - > selectionModel ( ) - > selectedRows ( ) ;
QVector < CoinsInfo * > selectedCoins ;
for ( const auto index : list ) {
selectedCoins . push_back ( m_model - > entryFromIndex ( m_proxyModel - > mapToSource ( index ) ) ) ;
}
return selectedCoins ;
}
2021-10-01 13:37:20 +00:00
void CoinsWidget : : freezeCoins ( QStringList & pubkeys ) {
for ( auto & pubkey : pubkeys ) {
2023-03-01 02:05:56 +00:00
m_wallet - > coins ( ) - > freeze ( pubkey ) ;
2021-05-02 18:22:38 +00:00
}
2023-03-01 02:05:56 +00:00
m_wallet - > coins ( ) - > refresh ( m_wallet - > currentSubaddressAccount ( ) ) ;
m_wallet - > updateBalance ( ) ;
2021-05-02 18:22:38 +00:00
}
2021-10-01 13:37:20 +00:00
void CoinsWidget : : thawCoins ( QStringList & pubkeys ) {
for ( auto & pubkey : pubkeys ) {
2023-03-01 02:05:56 +00:00
m_wallet - > coins ( ) - > thaw ( pubkey ) ;
2021-05-02 18:22:38 +00:00
}
2023-03-01 02:05:56 +00:00
m_wallet - > coins ( ) - > refresh ( m_wallet - > currentSubaddressAccount ( ) ) ;
m_wallet - > updateBalance ( ) ;
2021-05-02 18:22:38 +00:00
}
2022-03-20 22:30:48 +00:00
void CoinsWidget : : selectCoins ( const QStringList & keyimages ) {
m_model - > setSelected ( keyimages ) ;
ui - > coins - > clearSelection ( ) ;
}
2021-07-02 15:27:26 +00:00
void CoinsWidget : : editLabel ( ) {
QModelIndex index = ui - > coins - > currentIndex ( ) . siblingAtColumn ( m_model - > ModelColumn : : Label ) ;
ui - > coins - > setCurrentIndex ( index ) ;
ui - > coins - > edit ( index ) ;
}
2021-06-27 12:13:05 +00:00
CoinsWidget : : ~ CoinsWidget ( ) = default ;