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 "MainWindow.h"
# include "ui_MainWindow.h"
2021-05-02 18:22:38 +00:00
2021-06-28 17:20:16 +00:00
# include <QFileDialog>
2021-07-06 14:10:24 +00:00
# include <QInputDialog>
2021-06-28 17:48:23 +00:00
# include <QMessageBox>
2023-12-02 18:28:31 +00:00
# include <QCheckBox>
2021-06-28 17:20:16 +00:00
2021-06-28 17:48:23 +00:00
# include "constants.h"
2023-12-02 18:28:31 +00:00
# include "dialog/AddressCheckerIndexDialog.h"
2021-06-28 17:48:23 +00:00
# include "dialog/BalanceDialog.h"
2021-06-27 11:46:32 +00:00
# include "dialog/DebugInfoDialog.h"
2021-06-28 17:48:23 +00:00
# include "dialog/PasswordDialog.h"
2021-06-27 12:22:54 +00:00
# include "dialog/TxBroadcastDialog.h"
2021-06-28 17:48:23 +00:00
# include "dialog/TxConfAdvDialog.h"
# include "dialog/TxConfDialog.h"
2021-06-27 11:46:32 +00:00
# include "dialog/TxImportDialog.h"
2021-07-03 15:48:07 +00:00
# include "dialog/TxInfoDialog.h"
2021-06-28 17:48:23 +00:00
# include "dialog/ViewOnlyDialog.h"
# include "dialog/WalletInfoDialog.h"
2021-01-25 16:38:04 +00:00
# include "dialog/WalletCacheDebugDialog.h"
2020-10-07 10:36:04 +00:00
# include "libwalletqt/AddressBook.h"
2023-11-17 13:05:31 +00:00
# include "libwalletqt/rows/CoinsInfo.h"
2022-03-12 13:54:08 +00:00
# include "libwalletqt/Transfer.h"
2023-11-30 14:01:39 +00:00
# include "plugins/PluginRegistry.h"
2021-05-02 18:22:38 +00:00
# include "utils/AppData.h"
2021-06-28 17:48:23 +00:00
# include "utils/AsyncTask.h"
2021-05-02 18:22:38 +00:00
# include "utils/ColorScheme.h"
# include "utils/Icons.h"
2021-06-28 17:48:23 +00:00
# include "utils/SemanticVersion.h"
2021-05-18 15:59:18 +00:00
# include "utils/TorManager.h"
2021-06-28 17:48:23 +00:00
# include "utils/WebsocketNotifier.h"
2020-10-07 10:36:04 +00:00
2023-09-12 14:15:40 +00:00
# include "wallet/wallet_errors.h"
2023-12-02 18:28:31 +00:00
# ifdef WITH_SCANNER
# include "wizard/offline_tx_signing/OfflineTxSigningWizard.h"
# include "dialog/URDialog.h"
# endif
2023-04-19 15:41:46 +00:00
# ifdef CHECK_UPDATES
# include "utils/updater/UpdateDialog.h"
# endif
2023-02-11 17:11:21 +00:00
2021-05-18 15:59:18 +00:00
MainWindow : : MainWindow ( WindowManager * windowManager , Wallet * wallet , QWidget * parent )
2021-05-02 18:22:38 +00:00
: QMainWindow ( parent )
, ui ( new Ui : : MainWindow )
2021-05-18 15:59:18 +00:00
, m_windowManager ( windowManager )
2023-03-01 02:05:56 +00:00
, m_wallet ( wallet )
, m_nodes ( new Nodes ( this , wallet ) )
, m_rpc ( new DaemonRpc ( this , " " ) )
2020-10-07 10:36:04 +00:00
{
ui - > setupUi ( this ) ;
2021-05-18 15:59:18 +00:00
// Ensure the destructor is called after closeEvent()
setAttribute ( Qt : : WA_DeleteOnClose ) ;
2021-05-02 18:22:38 +00:00
m_splashDialog = new SplashDialog ( this ) ;
2023-03-01 02:05:56 +00:00
m_accountSwitcherDialog = new AccountSwitcherDialog ( m_wallet , this ) ;
2020-10-07 10:36:04 +00:00
2023-04-19 15:41:46 +00:00
# ifdef CHECK_UPDATES
2023-02-01 14:40:12 +00:00
m_updater = QSharedPointer < Updater > ( new Updater ( this ) ) ;
2023-04-19 15:41:46 +00:00
# endif
2023-02-01 14:40:12 +00:00
2020-10-07 10:36:04 +00:00
this - > restoreGeo ( ) ;
2021-05-02 18:22:38 +00:00
this - > initStatusBar ( ) ;
2023-11-30 14:01:39 +00:00
this - > initPlugins ( ) ;
2021-05-02 18:22:38 +00:00
this - > initWidgets ( ) ;
this - > initMenu ( ) ;
2023-12-02 18:28:31 +00:00
this - > initOffline ( ) ;
2021-05-02 18:22:38 +00:00
this - > initWalletContext ( ) ;
2023-11-30 14:01:39 +00:00
emit uiSetup ( ) ;
2021-05-02 18:22:38 +00:00
2023-12-02 18:28:31 +00:00
this - > onOfflineMode ( conf ( ) - > get ( Config : : offlineMode ) . toBool ( ) ) ;
2023-11-30 14:01:39 +00:00
conf ( ) - > set ( Config : : restartRequired , false ) ;
2023-12-02 18:28:31 +00:00
2021-05-02 18:22:38 +00:00
// Websocket notifier
2023-04-19 15:41:46 +00:00
# ifdef CHECK_UPDATES
2023-02-01 14:40:12 +00:00
connect ( websocketNotifier ( ) , & WebsocketNotifier : : UpdatesReceived , m_updater . data ( ) , & Updater : : wsUpdatesReceived ) ;
2023-04-19 15:41:46 +00:00
# endif
2023-11-30 14:01:39 +00:00
2021-05-18 15:59:18 +00:00
websocketNotifier ( ) - > emitCache ( ) ; // Get cached data
2020-10-07 10:36:04 +00:00
2022-03-15 12:36:20 +00:00
connect ( m_windowManager , & WindowManager : : websocketStatusChanged , this , & MainWindow : : onWebsocketStatusChanged ) ;
2023-09-12 14:15:40 +00:00
this - > onWebsocketStatusChanged ( ! conf ( ) - > get ( Config : : disableWebsocket ) . toBool ( ) ) ;
2022-03-15 12:36:20 +00:00
2023-03-01 02:05:56 +00:00
connect ( m_windowManager , & WindowManager : : proxySettingsChanged , this , & MainWindow : : onProxySettingsChanged ) ;
connect ( m_windowManager , & WindowManager : : updateBalance , m_wallet , & Wallet : : updateBalance ) ;
connect ( m_windowManager , & WindowManager : : offlineMode , this , & MainWindow : : onOfflineMode ) ;
2023-02-11 17:11:21 +00:00
2021-05-18 15:59:18 +00:00
connect ( torManager ( ) , & TorManager : : connectionStateChanged , this , & MainWindow : : onTorConnectionStateChanged ) ;
this - > onTorConnectionStateChanged ( torManager ( ) - > torConnected ) ;
2020-10-07 10:36:04 +00:00
2023-04-19 15:41:46 +00:00
# ifdef CHECK_UPDATES
2023-02-01 14:40:12 +00:00
connect ( m_updater . data ( ) , & Updater : : updateAvailable , this , & MainWindow : : showUpdateNotification ) ;
2023-04-19 15:41:46 +00:00
# endif
2023-02-01 14:40:12 +00:00
2021-05-02 18:22:38 +00:00
ColorScheme : : updateFromWidget ( this ) ;
2021-10-22 11:05:40 +00:00
QTimer : : singleShot ( 1 , [ this ] { this - > updateWidgetIcons ( ) ; } ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
// Timers
connect ( & m_updateBytes , & QTimer : : timeout , this , & MainWindow : : updateNetStats ) ;
connect ( & m_txTimer , & QTimer : : timeout , [ this ] {
m_statusLabelStatus - > setText ( " Constructing transaction " + this - > statusDots ( ) ) ;
} ) ;
2020-10-07 10:36:04 +00:00
2023-09-12 14:15:40 +00:00
conf ( ) - > set ( Config : : firstRun , false ) ;
2021-05-26 22:24:35 +00:00
2021-05-18 15:59:18 +00:00
this - > onWalletOpened ( ) ;
2020-10-17 21:14:56 +00:00
2021-05-18 15:59:18 +00:00
# ifdef DONATE_BEG
this - > donationNag ( ) ;
# endif
2022-03-04 16:20:17 +00:00
connect ( m_windowManager - > eventFilter , & EventFilter : : userActivity , this , & MainWindow : : userActivity ) ;
connect ( & m_checkUserActivity , & QTimer : : timeout , this , & MainWindow : : checkUserActivity ) ;
m_checkUserActivity . setInterval ( 5000 ) ;
m_checkUserActivity . start ( ) ;
2021-05-02 18:22:38 +00:00
}
2020-10-14 20:12:32 +00:00
2021-05-02 18:22:38 +00:00
void MainWindow : : initStatusBar ( ) {
# if defined(Q_OS_WIN)
2023-01-09 02:17:25 +00:00
// No separators between statusbar widgets
2021-05-02 18:22:38 +00:00
this - > statusBar ( ) - > setStyleSheet ( " QStatusBar::item {border: None;} " ) ;
# endif
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
this - > statusBar ( ) - > setFixedHeight ( 30 ) ;
2020-12-31 00:00:37 +00:00
2021-05-02 18:22:38 +00:00
m_statusLabelStatus = new QLabel ( " Idle " , this ) ;
m_statusLabelStatus - > setTextInteractionFlags ( Qt : : TextSelectableByMouse ) ;
this - > statusBar ( ) - > addWidget ( m_statusLabelStatus ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
m_statusLabelNetStats = new QLabel ( " " , this ) ;
m_statusLabelNetStats - > setTextInteractionFlags ( Qt : : TextSelectableByMouse ) ;
this - > statusBar ( ) - > addWidget ( m_statusLabelNetStats ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
m_statusUpdateAvailable = new QPushButton ( this ) ;
m_statusUpdateAvailable - > setFlat ( true ) ;
m_statusUpdateAvailable - > setCursor ( Qt : : PointingHandCursor ) ;
m_statusUpdateAvailable - > setIcon ( icons ( ) - > icon ( " tab_party.png " ) ) ;
m_statusUpdateAvailable - > hide ( ) ;
this - > statusBar ( ) - > addPermanentWidget ( m_statusUpdateAvailable ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
m_statusLabelBalance = new ClickableLabel ( this ) ;
m_statusLabelBalance - > setText ( " Balance: 0 XMR " ) ;
m_statusLabelBalance - > setTextInteractionFlags ( Qt : : TextSelectableByMouse ) ;
m_statusLabelBalance - > setCursor ( Qt : : PointingHandCursor ) ;
this - > statusBar ( ) - > addPermanentWidget ( m_statusLabelBalance ) ;
connect ( m_statusLabelBalance , & ClickableLabel : : clicked , this , & MainWindow : : showBalanceDialog ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
m_statusBtnConnectionStatusIndicator = new StatusBarButton ( icons ( ) - > icon ( " status_disconnected.svg " ) , " Connection status " , this ) ;
2022-03-15 10:12:08 +00:00
connect ( m_statusBtnConnectionStatusIndicator , & StatusBarButton : : clicked , [ this ] ( ) {
2023-02-11 17:24:18 +00:00
this - > onShowSettingsPage ( Settings : : Pages : : NETWORK ) ;
2022-03-15 10:12:08 +00:00
} ) ;
2021-05-02 18:22:38 +00:00
this - > statusBar ( ) - > addPermanentWidget ( m_statusBtnConnectionStatusIndicator ) ;
2023-02-11 17:11:21 +00:00
this - > onConnectionStatusChanged ( Wallet : : ConnectionStatus_Disconnected ) ;
2020-12-24 14:46:56 +00:00
2021-05-22 13:42:26 +00:00
m_statusAccountSwitcher = new StatusBarButton ( icons ( ) - > icon ( " change_account.png " ) , " Account switcher " , this ) ;
connect ( m_statusAccountSwitcher , & StatusBarButton : : clicked , this , & MainWindow : : showAccountSwitcherDialog ) ;
this - > statusBar ( ) - > addPermanentWidget ( m_statusAccountSwitcher ) ;
2021-05-02 18:22:38 +00:00
m_statusBtnPassword = new StatusBarButton ( icons ( ) - > icon ( " lock.svg " ) , " Password " , this ) ;
connect ( m_statusBtnPassword , & StatusBarButton : : clicked , this , & MainWindow : : showPasswordDialog ) ;
this - > statusBar ( ) - > addPermanentWidget ( m_statusBtnPassword ) ;
2020-12-24 14:46:56 +00:00
2021-05-02 18:22:38 +00:00
m_statusBtnPreferences = new StatusBarButton ( icons ( ) - > icon ( " preferences.svg " ) , " Settings " , this ) ;
connect ( m_statusBtnPreferences , & StatusBarButton : : clicked , this , & MainWindow : : menuSettingsClicked ) ;
this - > statusBar ( ) - > addPermanentWidget ( m_statusBtnPreferences ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
m_statusBtnSeed = new StatusBarButton ( icons ( ) - > icon ( " seed.png " ) , " Seed " , this ) ;
connect ( m_statusBtnSeed , & StatusBarButton : : clicked , this , & MainWindow : : showSeedDialog ) ;
this - > statusBar ( ) - > addPermanentWidget ( m_statusBtnSeed ) ;
2020-10-07 10:36:04 +00:00
2023-02-11 17:11:21 +00:00
m_statusBtnProxySettings = new StatusBarButton ( icons ( ) - > icon ( " tor_logo_disabled.png " ) , " Proxy settings " , this ) ;
connect ( m_statusBtnProxySettings , & StatusBarButton : : clicked , this , & MainWindow : : menuProxySettingsClicked ) ;
this - > statusBar ( ) - > addPermanentWidget ( m_statusBtnProxySettings ) ;
this - > onProxySettingsChanged ( ) ;
2020-10-07 10:36:04 +00:00
2021-07-01 21:00:47 +00:00
m_statusBtnHwDevice = new StatusBarButton ( this - > hardwareDevicePairedIcon ( ) , this - > getHardwareDevice ( ) , this ) ;
2021-05-02 18:22:38 +00:00
connect ( m_statusBtnHwDevice , & StatusBarButton : : clicked , this , & MainWindow : : menuHwDeviceClicked ) ;
this - > statusBar ( ) - > addPermanentWidget ( m_statusBtnHwDevice ) ;
m_statusBtnHwDevice - > hide ( ) ;
}
2020-10-07 10:36:04 +00:00
2023-11-30 14:01:39 +00:00
void MainWindow : : initPlugins ( ) {
const QStringList enabledPlugins = conf ( ) - > get ( Config : : enabledPlugins ) . toStringList ( ) ;
for ( const auto & plugin_creator : PluginRegistry : : getPluginCreators ( ) ) {
Plugin * plugin = plugin_creator ( ) ;
if ( ! PluginRegistry : : getInstance ( ) . isPluginEnabled ( plugin - > id ( ) ) ) {
continue ;
}
qDebug ( ) < < " Initializing plugin: " < < plugin - > id ( ) ;
plugin - > initialize ( m_wallet , this ) ;
connect ( plugin , & Plugin : : setStatusText , this , & MainWindow : : setStatusText ) ;
connect ( plugin , & Plugin : : fillSendTab , this , & MainWindow : : fillSendTab ) ;
connect ( this , & MainWindow : : updateIcons , plugin , & Plugin : : skinChanged ) ;
connect ( this , & MainWindow : : aboutToQuit , plugin , & Plugin : : aboutToQuit ) ;
connect ( this , & MainWindow : : uiSetup , plugin , & Plugin : : uiSetup ) ;
2020-10-07 10:36:04 +00:00
2023-11-30 14:01:39 +00:00
m_plugins . append ( plugin ) ;
}
std : : sort ( m_plugins . begin ( ) , m_plugins . end ( ) , [ ] ( Plugin * a , Plugin * b ) {
return a - > idx ( ) < b - > idx ( ) ;
} ) ;
}
void MainWindow : : initWidgets ( ) {
2021-05-02 18:22:38 +00:00
// [History]
2023-03-01 02:05:56 +00:00
m_historyWidget = new HistoryWidget ( m_wallet , this ) ;
2021-05-18 15:59:18 +00:00
ui - > historyWidgetLayout - > addWidget ( m_historyWidget ) ;
connect ( m_historyWidget , & HistoryWidget : : viewOnBlockExplorer , this , & MainWindow : : onViewOnBlockExplorer ) ;
connect ( m_historyWidget , & HistoryWidget : : resendTransaction , this , & MainWindow : : onResendTransaction ) ;
2021-05-02 18:22:38 +00:00
2021-05-10 15:05:10 +00:00
// [Send]
2023-03-01 02:05:56 +00:00
m_sendWidget = new SendWidget ( m_wallet , this ) ;
2021-05-10 15:05:10 +00:00
ui - > sendWidgetLayout - > addWidget ( m_sendWidget ) ;
2021-05-18 15:59:18 +00:00
// --------------
2023-03-01 02:05:56 +00:00
m_contactsWidget = new ContactsWidget ( m_wallet , this ) ;
2021-05-18 15:59:18 +00:00
ui - > contactsWidgetLayout - > addWidget ( m_contactsWidget ) ;
2021-05-10 15:05:10 +00:00
2021-05-02 18:22:38 +00:00
// [Receive]
2023-03-01 02:05:56 +00:00
m_receiveWidget = new ReceiveWidget ( m_wallet , this ) ;
2021-05-18 15:59:18 +00:00
ui - > receiveWidgetLayout - > addWidget ( m_receiveWidget ) ;
connect ( m_receiveWidget , & ReceiveWidget : : showTransactions , [ this ] ( const QString & text ) {
m_historyWidget - > setSearchText ( text ) ;
2023-11-30 14:01:39 +00:00
ui - > tabWidget - > setCurrentIndex ( this - > findTab ( " History " ) ) ;
2020-10-07 10:36:04 +00:00
} ) ;
2021-05-18 15:59:18 +00:00
connect ( m_contactsWidget , & ContactsWidget : : fillAddress , m_sendWidget , & SendWidget : : fillAddress ) ;
2020-10-07 10:36:04 +00:00
2021-05-10 15:05:10 +00:00
// [Coins]
2023-03-01 02:05:56 +00:00
m_coinsWidget = new CoinsWidget ( m_wallet , this ) ;
2021-05-10 15:05:10 +00:00
ui - > coinsWidgetLayout - > addWidget ( m_coinsWidget ) ;
2020-10-07 10:36:04 +00:00
2023-11-30 14:01:39 +00:00
// [Plugins..]
for ( auto * plugin : m_plugins ) {
if ( ! plugin - > hasParent ( ) ) {
qDebug ( ) < < " Adding tab: " < < plugin - > displayName ( ) ;
2020-11-23 16:57:38 +00:00
2023-11-30 14:01:39 +00:00
if ( plugin - > insertFirst ( ) ) {
ui - > tabWidget - > insertTab ( 0 , plugin - > tab ( ) , icons ( ) - > icon ( plugin - > icon ( ) ) , plugin - > displayName ( ) ) ;
} else {
ui - > tabWidget - > addTab ( plugin - > tab ( ) , icons ( ) - > icon ( plugin - > icon ( ) ) , plugin - > displayName ( ) ) ;
}
2021-07-07 17:06:31 +00:00
2023-11-30 14:01:39 +00:00
for ( auto * child : m_plugins ) {
if ( child - > hasParent ( ) & & child - > parent ( ) = = plugin - > id ( ) ) {
plugin - > addSubPlugin ( child ) ;
}
}
}
}
2022-03-20 22:30:48 +00:00
ui - > frame_coinControl - > setVisible ( false ) ;
connect ( ui - > btn_resetCoinControl , & QPushButton : : clicked , [ this ] {
2023-03-01 02:05:56 +00:00
m_wallet - > setSelectedInputs ( { } ) ;
2022-03-20 22:30:48 +00:00
} ) ;
2023-01-19 14:12:16 +00:00
m_walletUnlockWidget = new WalletUnlockWidget ( this ) ;
m_walletUnlockWidget - > setWalletName ( this - > walletName ( ) ) ;
ui - > walletUnlockLayout - > addWidget ( m_walletUnlockWidget ) ;
connect ( m_walletUnlockWidget , & WalletUnlockWidget : : closeWallet , this , & MainWindow : : close ) ;
connect ( m_walletUnlockWidget , & WalletUnlockWidget : : unlockWallet , this , & MainWindow : : unlockWallet ) ;
2023-11-30 14:01:39 +00:00
ui - > tabWidget - > setCurrentIndex ( 0 ) ;
2023-01-19 14:12:16 +00:00
ui - > stackedWidget - > setCurrentIndex ( 0 ) ;
2020-10-07 10:36:04 +00:00
}
2021-05-02 18:22:38 +00:00
void MainWindow : : initMenu ( ) {
// TODO: Rename actions to follow style
// [File]
2021-05-18 15:59:18 +00:00
connect ( ui - > actionOpen , & QAction : : triggered , this , & MainWindow : : menuOpenClicked ) ;
connect ( ui - > actionNew_Restore , & QAction : : triggered , this , & MainWindow : : menuNewRestoreClicked ) ;
2023-01-19 14:12:16 +00:00
connect ( ui - > actionLock , & QAction : : triggered , this , & MainWindow : : lockWallet ) ;
2021-05-18 15:59:18 +00:00
connect ( ui - > actionClose , & QAction : : triggered , this , & MainWindow : : menuWalletCloseClicked ) ; // Close current wallet
connect ( ui - > actionQuit , & QAction : : triggered , this , & MainWindow : : menuQuitClicked ) ; // Quit application
connect ( ui - > actionSettings , & QAction : : triggered , this , & MainWindow : : menuSettingsClicked ) ;
2020-10-07 10:36:04 +00:00
2021-07-08 12:03:54 +00:00
// [File] -> [Recently open]
m_clearRecentlyOpenAction = new QAction ( " Clear history " , ui - > menuFile ) ;
connect ( m_clearRecentlyOpenAction , & QAction : : triggered , this , & MainWindow : : menuClearHistoryClicked ) ;
2021-05-02 18:22:38 +00:00
// [Wallet]
connect ( ui - > actionInformation , & QAction : : triggered , this , & MainWindow : : showWalletInfoDialog ) ;
2021-05-22 13:42:26 +00:00
connect ( ui - > actionAccount , & QAction : : triggered , this , & MainWindow : : showAccountSwitcherDialog ) ;
2021-05-02 18:22:38 +00:00
connect ( ui - > actionPassword , & QAction : : triggered , this , & MainWindow : : showPasswordDialog ) ;
connect ( ui - > actionSeed , & QAction : : triggered , this , & MainWindow : : showSeedDialog ) ;
connect ( ui - > actionKeys , & QAction : : triggered , this , & MainWindow : : showKeysDialog ) ;
connect ( ui - > actionViewOnly , & QAction : : triggered , this , & MainWindow : : showViewOnlyDialog ) ;
// [Wallet] -> [Advanced]
2021-08-14 14:53:22 +00:00
connect ( ui - > actionStore_wallet , & QAction : : triggered , this , & MainWindow : : tryStoreWallet ) ;
2023-03-01 02:05:56 +00:00
connect ( ui - > actionUpdate_balance , & QAction : : triggered , [ this ] { m_wallet - > updateBalance ( ) ; } ) ;
connect ( ui - > actionRefresh_tabs , & QAction : : triggered , [ this ] { m_wallet - > refreshModels ( ) ; } ) ;
2021-05-02 18:22:38 +00:00
connect ( ui - > actionRescan_spent , & QAction : : triggered , this , & MainWindow : : rescanSpent ) ;
connect ( ui - > actionWallet_cache_debug , & QAction : : triggered , this , & MainWindow : : showWalletCacheDebugDialog ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
// [Wallet] -> [History]
connect ( ui - > actionExport_CSV , & QAction : : triggered , this , & MainWindow : : onExportHistoryCSV ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
// [Wallet] -> [Contacts]
connect ( ui - > actionExportContactsCSV , & QAction : : triggered , this , & MainWindow : : onExportContactsCSV ) ;
connect ( ui - > actionImportContactsCSV , & QAction : : triggered , this , & MainWindow : : importContacts ) ;
2020-11-25 13:35:28 +00:00
2021-05-02 18:22:38 +00:00
// [View]
2020-10-07 10:36:04 +00:00
m_tabShowHideSignalMapper = new QSignalMapper ( this ) ;
2021-05-23 14:58:18 +00:00
connect ( ui - > actionShow_Searchbar , & QAction : : toggled , this , & MainWindow : : toggleSearchbar ) ;
2023-09-12 14:15:40 +00:00
ui - > actionShow_Searchbar - > setChecked ( conf ( ) - > get ( Config : : showSearchbar ) . toBool ( ) ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
// Show/Hide Coins
2020-10-07 10:36:04 +00:00
connect ( ui - > actionShow_Coins , & QAction : : triggered , m_tabShowHideSignalMapper , QOverload < > : : of ( & QSignalMapper : : map ) ) ;
2023-11-30 14:01:39 +00:00
m_tabShowHideMapper [ " Coins " ] = new ToggleTab ( ui - > tabCoins , " Coins " , " Coins " , ui - > actionShow_Coins ) ;
2020-10-07 10:36:04 +00:00
m_tabShowHideSignalMapper - > setMapping ( ui - > actionShow_Coins , " Coins " ) ;
2020-10-21 11:24:16 +00:00
2023-11-30 14:01:39 +00:00
// Show/Hide Plugins..
for ( const auto & plugin : m_plugins ) {
if ( plugin - > parent ( ) ! = " " ) {
continue ;
}
2020-12-25 22:18:40 +00:00
2023-11-30 14:01:39 +00:00
auto * pluginAction = new QAction ( QString ( " Show %1 " ) . arg ( plugin - > displayName ( ) ) , this ) ;
ui - > menuView - > insertAction ( plugin - > insertFirst ( ) ? ui - > actionPlaceholderBegin : ui - > actionPlaceholderEnd , pluginAction ) ;
connect ( pluginAction , & QAction : : triggered , m_tabShowHideSignalMapper , QOverload < > : : of ( & QSignalMapper : : map ) ) ;
m_tabShowHideMapper [ plugin - > displayName ( ) ] = new ToggleTab ( plugin - > tab ( ) , plugin - > displayName ( ) , plugin - > displayName ( ) , pluginAction ) ;
m_tabShowHideSignalMapper - > setMapping ( pluginAction , plugin - > displayName ( ) ) ;
}
ui - > actionPlaceholderBegin - > setVisible ( false ) ;
ui - > actionPlaceholderEnd - > setVisible ( false ) ;
2020-10-07 10:36:04 +00:00
2023-11-30 14:01:39 +00:00
QStringList enabledTabs = conf ( ) - > get ( Config : : enabledTabs ) . toStringList ( ) ;
2020-10-07 10:36:04 +00:00
for ( const auto & key : m_tabShowHideMapper . keys ( ) ) {
const auto toggleTab = m_tabShowHideMapper . value ( key ) ;
2023-11-30 14:01:39 +00:00
bool show = enabledTabs . contains ( key ) ;
2020-10-07 10:36:04 +00:00
toggleTab - > menuAction - > setText ( ( show ? QString ( " Hide " ) : QString ( " Show " ) ) + toggleTab - > name ) ;
ui - > tabWidget - > setTabVisible ( ui - > tabWidget - > indexOf ( toggleTab - > tab ) , show ) ;
}
connect ( m_tabShowHideSignalMapper , & QSignalMapper : : mappedString , this , & MainWindow : : menuToggleTabVisible ) ;
2021-05-02 18:22:38 +00:00
// [Tools]
connect ( ui - > actionSignVerify , & QAction : : triggered , this , & MainWindow : : menuSignVerifyClicked ) ;
connect ( ui - > actionVerifyTxProof , & QAction : : triggered , this , & MainWindow : : menuVerifyTxProof ) ;
2023-12-02 18:28:31 +00:00
connect ( ui - > actionKeyImageSync , & QAction : : triggered , this , & MainWindow : : showKeyImageSyncWizard ) ;
2021-05-02 18:22:38 +00:00
connect ( ui - > actionLoadSignedTxFromFile , & QAction : : triggered , this , & MainWindow : : loadSignedTx ) ;
connect ( ui - > actionLoadSignedTxFromText , & QAction : : triggered , this , & MainWindow : : loadSignedTxFromText ) ;
connect ( ui - > actionImport_transaction , & QAction : : triggered , this , & MainWindow : : importTransaction ) ;
2023-12-02 18:28:31 +00:00
connect ( ui - > actionTransmitOverUR , & QAction : : triggered , this , & MainWindow : : showURDialog ) ;
2021-05-02 18:22:38 +00:00
connect ( ui - > actionPay_to_many , & QAction : : triggered , this , & MainWindow : : payToMany ) ;
2021-07-06 14:10:24 +00:00
connect ( ui - > actionAddress_checker , & QAction : : triggered , this , & MainWindow : : showAddressChecker ) ;
2021-05-02 18:22:38 +00:00
connect ( ui - > actionCreateDesktopEntry , & QAction : : triggered , this , & MainWindow : : onCreateDesktopEntry ) ;
2023-12-02 18:28:31 +00:00
if ( m_wallet - > viewOnly ( ) ) {
ui - > actionKeyImageSync - > setText ( " Key image sync " ) ;
}
2021-05-02 18:22:38 +00:00
// TODO: Allow creating desktop entry on Windows and Mac
# if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
ui - > actionCreateDesktopEntry - > setDisabled ( true ) ;
# endif
2020-10-07 10:36:04 +00:00
2022-02-25 19:48:14 +00:00
# ifndef SELF_CONTAINED
ui - > actionCreateDesktopEntry - > setVisible ( false ) ;
# endif
2021-05-02 18:22:38 +00:00
// [Help]
connect ( ui - > actionAbout , & QAction : : triggered , this , & MainWindow : : menuAboutClicked ) ;
2023-02-01 14:40:12 +00:00
# if defined(CHECK_UPDATES)
connect ( ui - > actionCheckForUpdates , & QAction : : triggered , this , & MainWindow : : showUpdateDialog ) ;
# else
ui - > actionCheckForUpdates - > setVisible ( false ) ;
# endif
2023-02-12 15:24:02 +00:00
2021-05-02 18:22:38 +00:00
connect ( ui - > actionOfficialWebsite , & QAction : : triggered , [ this ] ( ) { Utils : : externalLinkWarning ( this , " https://featherwallet.org " ) ; } ) ;
connect ( ui - > actionDonate_to_Feather , & QAction : : triggered , this , & MainWindow : : donateButtonClicked ) ;
2023-01-09 02:17:25 +00:00
connect ( ui - > actionDocumentation , & QAction : : triggered , this , & MainWindow : : onShowDocumentation ) ;
2021-05-02 18:22:38 +00:00
connect ( ui - > actionReport_bug , & QAction : : triggered , this , & MainWindow : : onReportBug ) ;
connect ( ui - > actionShow_debug_info , & QAction : : triggered , this , & MainWindow : : showDebugInfo ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
// Setup shortcuts
ui - > actionStore_wallet - > setShortcut ( QKeySequence ( " Ctrl+S " ) ) ;
ui - > actionRefresh_tabs - > setShortcut ( QKeySequence ( " Ctrl+R " ) ) ;
2021-05-18 15:59:18 +00:00
ui - > actionOpen - > setShortcut ( QKeySequence ( " Ctrl+O " ) ) ;
ui - > actionNew_Restore - > setShortcut ( QKeySequence ( " Ctrl+N " ) ) ;
2023-01-19 14:12:16 +00:00
ui - > actionLock - > setShortcut ( QKeySequence ( " Ctrl+L " ) ) ;
2021-05-02 18:22:38 +00:00
ui - > actionClose - > setShortcut ( QKeySequence ( " Ctrl+W " ) ) ;
ui - > actionShow_debug_info - > setShortcut ( QKeySequence ( " Ctrl+D " ) ) ;
ui - > actionSettings - > setShortcut ( QKeySequence ( " Ctrl+Alt+S " ) ) ;
ui - > actionUpdate_balance - > setShortcut ( QKeySequence ( " Ctrl+U " ) ) ;
2021-05-23 14:58:18 +00:00
ui - > actionShow_Searchbar - > setShortcut ( QKeySequence ( " Ctrl+F " ) ) ;
2021-10-21 21:13:25 +00:00
ui - > actionDocumentation - > setShortcut ( QKeySequence ( " F1 " ) ) ;
2021-05-02 18:22:38 +00:00
}
2020-10-07 10:36:04 +00:00
2023-12-02 18:28:31 +00:00
void MainWindow : : initOffline ( ) {
// TODO: check if we have any cameras available
2023-12-11 23:25:42 +00:00
ui - > btn_help - > setFocusPolicy ( Qt : : NoFocus ) ;
ui - > btn_viewOnlyDetails - > setFocusPolicy ( Qt : : NoFocus ) ;
ui - > btn_checkAddress - > setFocusPolicy ( Qt : : NoFocus ) ;
ui - > btn_signTransaction - > setFocusPolicy ( Qt : : StrongFocus ) ;
ui - > btn_signTransaction - > setFocus ( ) ;
2023-12-02 18:28:31 +00:00
connect ( ui - > btn_help , & QPushButton : : clicked , [ this ] {
windowManager ( ) - > showDocs ( this , " offline_tx_signing " ) ;
} ) ;
2023-12-05 21:06:48 +00:00
connect ( ui - > btn_viewOnlyDetails , & QPushButton : : clicked , [ this ] {
this - > showViewOnlyDialog ( ) ;
} ) ;
2023-12-02 18:28:31 +00:00
connect ( ui - > btn_checkAddress , & QPushButton : : clicked , [ this ] {
AddressCheckerIndexDialog dialog { m_wallet , this } ;
dialog . exec ( ) ;
} ) ;
connect ( ui - > btn_signTransaction , & QPushButton : : clicked , [ this ] {
this - > showKeyImageSyncWizard ( ) ;
} ) ;
switch ( conf ( ) - > get ( Config : : offlineTxSigningMethod ) . toInt ( ) ) {
case OfflineTxSigningWizard : : Method : : FILES :
ui - > radio_airgapFiles - > setChecked ( true ) ;
break ;
default :
ui - > radio_airgapUR - > setChecked ( true ) ;
}
// We can't use rich text for radio buttons
connect ( ui - > label_airgapUR , & ClickableLabel : : clicked , [ this ] {
ui - > radio_airgapUR - > setChecked ( true ) ;
} ) ;
connect ( ui - > label_airgapFiles , & ClickableLabel : : clicked , [ this ] {
ui - > radio_airgapFiles - > setChecked ( true ) ;
} ) ;
connect ( ui - > radio_airgapFiles , & QCheckBox : : toggled , [ this ] ( bool checked ) {
if ( checked ) {
conf ( ) - > set ( Config : : offlineTxSigningMethod , OfflineTxSigningWizard : : Method : : FILES ) ;
}
} ) ;
connect ( ui - > radio_airgapUR , & QCheckBox : : toggled , [ this ] ( bool checked ) {
if ( checked ) {
conf ( ) - > set ( Config : : offlineTxSigningMethod , OfflineTxSigningWizard : : Method : : UR ) ;
}
} ) ;
}
2021-05-02 18:22:38 +00:00
void MainWindow : : initWalletContext ( ) {
2023-03-01 02:05:56 +00:00
connect ( m_wallet , & Wallet : : balanceUpdated , this , & MainWindow : : onBalanceUpdated ) ;
connect ( m_wallet , & Wallet : : synchronized , this , & MainWindow : : onSynchronized ) ; //TODO
connect ( m_wallet , & Wallet : : blockchainSync , this , & MainWindow : : onBlockchainSync ) ;
connect ( m_wallet , & Wallet : : refreshSync , this , & MainWindow : : onRefreshSync ) ;
2023-09-12 14:15:40 +00:00
connect ( m_wallet , & Wallet : : transactionCreated , this , & MainWindow : : onTransactionCreated ) ;
2023-03-01 02:05:56 +00:00
connect ( m_wallet , & Wallet : : transactionCommitted , this , & MainWindow : : onTransactionCommitted ) ;
connect ( m_wallet , & Wallet : : initiateTransaction , this , & MainWindow : : onInitiateTransaction ) ;
connect ( m_wallet , & Wallet : : keysCorrupted , this , & MainWindow : : onKeysCorrupted ) ;
connect ( m_wallet , & Wallet : : selectedInputsChanged , this , & MainWindow : : onSelectedInputsChanged ) ;
2021-05-02 18:22:38 +00:00
2021-05-18 15:59:18 +00:00
// Wallet
2023-03-01 02:05:56 +00:00
connect ( m_wallet , & Wallet : : connectionStatusChanged , [ this ] ( int status ) {
2023-02-11 17:11:21 +00:00
// Order is important, first inform UI about a potential disconnect, then reconnect
this - > onConnectionStatusChanged ( status ) ;
2023-03-01 02:05:56 +00:00
m_nodes - > autoConnect ( ) ;
} ) ;
connect ( m_wallet , & Wallet : : currentSubaddressAccountChanged , this , & MainWindow : : updateTitle ) ;
connect ( m_wallet , & Wallet : : walletPassphraseNeeded , this , & MainWindow : : onWalletPassphraseNeeded ) ;
connect ( m_wallet , & Wallet : : unconfirmedMoneyReceived , this , [ this ] ( const QString & txId , uint64_t amount ) {
if ( m_wallet - > isSynchronized ( ) ) {
auto notify = QString ( " %1 XMR (pending) " ) . arg ( WalletManager : : displayAmount ( amount , false ) ) ;
2023-03-29 08:46:53 +00:00
m_windowManager - > notify ( " Payment received " , notify , 5000 ) ;
2023-03-01 02:05:56 +00:00
}
} ) ;
// Device
connect ( m_wallet , & Wallet : : deviceButtonRequest , this , & MainWindow : : onDeviceButtonRequest ) ;
connect ( m_wallet , & Wallet : : deviceButtonPressed , this , & MainWindow : : onDeviceButtonPressed ) ;
connect ( m_wallet , & Wallet : : deviceError , this , & MainWindow : : onDeviceError ) ;
connect ( m_wallet , & Wallet : : donationSent , this , [ ] {
2023-09-12 14:15:40 +00:00
conf ( ) - > set ( Config : : donateBeg , - 1 ) ;
2023-03-01 02:05:56 +00:00
} ) ;
2023-03-02 12:41:33 +00:00
connect ( m_wallet , & Wallet : : multiBroadcast , this , & MainWindow : : onMultiBroadcast ) ;
2021-05-02 18:22:38 +00:00
}
2020-10-07 10:36:04 +00:00
void MainWindow : : menuToggleTabVisible ( const QString & key ) {
const auto toggleTab = m_tabShowHideMapper [ key ] ;
2023-11-30 14:01:39 +00:00
QStringList enabledTabs = conf ( ) - > get ( Config : : enabledTabs ) . toStringList ( ) ;
bool show = enabledTabs . contains ( key ) ;
2020-10-07 10:36:04 +00:00
show = ! show ;
2023-11-30 14:01:39 +00:00
if ( show ) {
enabledTabs . append ( key ) ;
} else {
enabledTabs . removeAll ( key ) ;
}
conf ( ) - > set ( Config : : enabledTabs , enabledTabs ) ;
2020-10-07 10:36:04 +00:00
ui - > tabWidget - > setTabVisible ( ui - > tabWidget - > indexOf ( toggleTab - > tab ) , show ) ;
toggleTab - > menuAction - > setText ( ( show ? QString ( " Hide " ) : QString ( " Show " ) ) + toggleTab - > name ) ;
}
2021-07-08 12:03:54 +00:00
void MainWindow : : menuClearHistoryClicked ( ) {
2023-09-12 14:15:40 +00:00
conf ( ) - > remove ( Config : : recentlyOpenedWallets ) ;
2021-07-08 12:03:54 +00:00
this - > updateRecentlyOpenedMenu ( ) ;
}
2021-05-18 15:59:18 +00:00
QString MainWindow : : walletName ( ) {
2023-03-01 02:05:56 +00:00
return QFileInfo ( m_wallet - > cachePath ( ) ) . fileName ( ) ;
2020-10-07 10:36:04 +00:00
}
2021-05-18 15:59:18 +00:00
QString MainWindow : : walletCachePath ( ) {
2023-03-01 02:05:56 +00:00
return m_wallet - > cachePath ( ) ;
2020-10-07 10:36:04 +00:00
}
2021-05-18 15:59:18 +00:00
QString MainWindow : : walletKeysPath ( ) {
2023-03-01 02:05:56 +00:00
return m_wallet - > keysPath ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : onWalletOpened ( ) {
qDebug ( ) < < Q_FUNC_INFO ;
2021-05-02 18:22:38 +00:00
m_splashDialog - > hide ( ) ;
2023-03-01 02:05:56 +00:00
m_wallet - > setRingDatabase ( Utils : : ringDatabasePath ( ) ) ;
2022-07-01 11:46:10 +00:00
2023-03-01 02:05:56 +00:00
m_wallet - > updateBalance ( ) ;
if ( m_wallet - > isHwBacked ( ) ) {
2021-05-02 18:22:38 +00:00
m_statusBtnHwDevice - > show ( ) ;
}
2021-03-08 20:03:20 +00:00
this - > bringToFront ( ) ;
2020-10-07 10:36:04 +00:00
this - > setEnabled ( true ) ;
// receive page
2023-03-01 02:05:56 +00:00
m_wallet - > subaddress ( ) - > refresh ( m_wallet - > currentSubaddressAccount ( ) ) ;
if ( m_wallet - > subaddress ( ) - > count ( ) = = 1 ) {
2020-10-07 10:36:04 +00:00
for ( int i = 0 ; i < 10 ; i + + ) {
2023-03-01 02:05:56 +00:00
m_wallet - > subaddress ( ) - > addRow ( m_wallet - > currentSubaddressAccount ( ) , " " ) ;
2020-10-07 10:36:04 +00:00
}
}
2023-03-01 02:05:56 +00:00
m_wallet - > subaddressModel ( ) - > setCurrentSubaddressAccount ( m_wallet - > currentSubaddressAccount ( ) ) ;
2020-10-07 10:36:04 +00:00
// history page
2023-11-17 13:05:31 +00:00
m_wallet - > history ( ) - > refresh ( ) ;
2020-10-07 10:36:04 +00:00
// coins page
2023-11-17 13:05:31 +00:00
m_wallet - > coins ( ) - > refresh ( ) ;
2023-03-01 02:05:56 +00:00
m_coinsWidget - > setModel ( m_wallet - > coinsModel ( ) , m_wallet - > coins ( ) ) ;
m_wallet - > coinsModel ( ) - > setCurrentSubaddressAccount ( m_wallet - > currentSubaddressAccount ( ) ) ;
2021-07-02 14:12:07 +00:00
// Coin labeling uses set_tx_note, so we need to refresh history too
2023-03-01 02:05:56 +00:00
connect ( m_wallet - > coins ( ) , & Coins : : descriptionChanged , [ this ] {
2023-11-17 13:05:31 +00:00
m_wallet - > history ( ) - > refresh ( ) ;
2021-07-02 14:12:07 +00:00
} ) ;
// Vice versa
2023-03-01 02:05:56 +00:00
connect ( m_wallet - > history ( ) , & TransactionHistory : : txNoteChanged , [ this ] {
2023-11-17 13:05:31 +00:00
m_wallet - > coins ( ) - > refresh ( ) ;
2021-07-02 14:12:07 +00:00
} ) ;
2020-10-22 03:50:59 +00:00
2020-11-14 09:57:06 +00:00
this - > updatePasswordIcon ( ) ;
2021-05-23 13:58:28 +00:00
this - > updateTitle ( ) ;
2023-03-01 02:05:56 +00:00
m_nodes - > allowConnection ( ) ;
m_nodes - > connectToNode ( ) ;
2021-05-02 18:22:38 +00:00
m_updateBytes . start ( 250 ) ;
2021-05-18 15:59:18 +00:00
2023-09-12 14:15:40 +00:00
if ( conf ( ) - > get ( Config : : writeRecentlyOpenedWallets ) . toBool ( ) ) {
2023-03-01 02:05:56 +00:00
this - > addToRecentlyOpened ( m_wallet - > cachePath ( ) ) ;
2023-02-11 17:11:21 +00:00
}
2020-10-07 10:36:04 +00:00
}
2020-12-25 14:20:39 +00:00
void MainWindow : : onBalanceUpdated ( quint64 balance , quint64 spendable ) {
2023-09-12 14:15:40 +00:00
bool hide = conf ( ) - > get ( Config : : hideBalance ) . toBool ( ) ;
int displaySetting = conf ( ) - > get ( Config : : balanceDisplay ) . toInt ( ) ;
int decimals = conf ( ) - > get ( Config : : amountPrecision ) . toInt ( ) ;
2020-11-02 09:37:36 +00:00
2021-05-22 14:12:39 +00:00
QString balance_str = " Balance: " ;
if ( hide ) {
balance_str + = " HIDDEN " ;
}
else if ( displaySetting = = Config : : totalBalance ) {
2021-05-25 17:11:45 +00:00
balance_str + = QString ( " %1 XMR " ) . arg ( WalletManager : : displayAmount ( balance , false , decimals ) ) ;
2021-05-04 23:09:19 +00:00
}
2021-05-22 14:12:39 +00:00
else if ( displaySetting = = Config : : spendable | | displaySetting = = Config : : spendablePlusUnconfirmed ) {
2021-05-25 17:11:45 +00:00
balance_str + = QString ( " %1 XMR " ) . arg ( WalletManager : : displayAmount ( spendable , false , decimals ) ) ;
2021-05-04 23:09:19 +00:00
2021-05-22 14:12:39 +00:00
if ( displaySetting = = Config : : spendablePlusUnconfirmed & & balance > spendable ) {
2021-05-25 17:11:45 +00:00
balance_str + = QString ( " (+%1 XMR unconfirmed) " ) . arg ( WalletManager : : displayAmount ( balance - spendable , false , decimals ) ) ;
2021-05-22 14:12:39 +00:00
}
}
2020-11-02 09:37:36 +00:00
2020-12-25 14:20:39 +00:00
m_statusLabelBalance - > setToolTip ( " Click for details " ) ;
2021-05-22 14:12:39 +00:00
m_statusLabelBalance - > setText ( balance_str ) ;
2020-10-07 10:36:04 +00:00
}
2020-12-31 00:00:37 +00:00
void MainWindow : : setStatusText ( const QString & text , bool override , int timeout ) {
if ( override ) {
m_statusOverrideActive = true ;
m_statusLabelStatus - > setText ( text ) ;
QTimer : : singleShot ( timeout , [ this ] {
m_statusOverrideActive = false ;
this - > setStatusText ( m_statusText ) ;
} ) ;
return ;
}
2020-12-24 14:46:56 +00:00
m_statusText = text ;
2020-12-31 00:00:37 +00:00
if ( ! m_statusOverrideActive & & ! m_constructingTransaction ) {
2020-12-24 14:46:56 +00:00
m_statusLabelStatus - > setText ( text ) ;
2020-12-31 00:00:37 +00:00
}
2020-12-24 14:46:56 +00:00
}
2021-08-14 14:53:22 +00:00
void MainWindow : : tryStoreWallet ( ) {
2023-03-01 02:05:56 +00:00
if ( m_wallet - > connectionStatus ( ) = = Wallet : : ConnectionStatus : : ConnectionStatus_Synchronizing ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Unable to save wallet " , " Can't save wallet during synchronization " , { " Wait until synchronization is finished and try again " } , " synchronization " ) ;
2021-08-14 14:53:22 +00:00
return ;
}
2023-03-01 02:05:56 +00:00
m_wallet - > store ( ) ;
2021-08-14 14:53:22 +00:00
}
2022-03-15 12:36:20 +00:00
void MainWindow : : onWebsocketStatusChanged ( bool enabled ) {
ui - > actionShow_Home - > setVisible ( enabled ) ;
2023-11-30 14:01:39 +00:00
QStringList enabledTabs = conf ( ) - > get ( Config : : enabledTabs ) . toStringList ( ) ;
for ( const auto & plugin : m_plugins ) {
if ( plugin - > hasParent ( ) ) {
continue ;
}
if ( plugin - > requiresWebsocket ( ) ) {
// TODO: unload plugins
ui - > tabWidget - > setTabVisible ( this - > findTab ( plugin - > displayName ( ) ) , enabled & & enabledTabs . contains ( plugin - > displayName ( ) ) ) ;
}
}
2022-03-15 12:36:20 +00:00
2022-07-03 16:24:33 +00:00
m_historyWidget - > setWebsocketEnabled ( enabled ) ;
2023-02-13 09:41:07 +00:00
m_sendWidget - > setWebsocketEnabled ( enabled ) ;
2022-03-15 12:36:20 +00:00
}
2023-02-11 17:11:21 +00:00
void MainWindow : : onProxySettingsChanged ( ) {
2023-03-01 02:05:56 +00:00
m_nodes - > connectToNode ( ) ;
2023-09-12 14:15:40 +00:00
int proxy = conf ( ) - > get ( Config : : proxy ) . toInt ( ) ;
2023-02-11 17:11:21 +00:00
if ( proxy = = Config : : Proxy : : Tor ) {
this - > onTorConnectionStateChanged ( torManager ( ) - > torConnected ) ;
m_statusBtnProxySettings - > show ( ) ;
return ;
}
if ( proxy = = Config : : Proxy : : i2p ) {
m_statusBtnProxySettings - > setIcon ( icons ( ) - > icon ( " i2p.png " ) ) ;
m_statusBtnProxySettings - > show ( ) ;
return ;
}
m_statusBtnProxySettings - > hide ( ) ;
}
2023-03-01 02:05:56 +00:00
void MainWindow : : onOfflineMode ( bool offline ) {
2023-12-02 18:28:31 +00:00
this - > onConnectionStatusChanged ( Wallet : : ConnectionStatus_Disconnected ) ;
m_wallet - > setOffline ( offline ) ;
if ( m_wallet - > viewOnly ( ) ) {
2023-03-01 02:05:56 +00:00
return ;
}
2023-12-02 18:28:31 +00:00
if ( ui - > stackedWidget - > currentIndex ( ) ! = Stack : : LOCKED ) {
ui - > stackedWidget - > setCurrentIndex ( offline ? Stack : : OFFLINE : Stack : : WALLET ) ;
}
ui - > actionPay_to_many - > setVisible ( ! offline ) ;
ui - > menuView - > setDisabled ( offline ) ;
m_statusLabelBalance - > setVisible ( ! offline ) ;
m_statusBtnProxySettings - > setVisible ( ! offline ) ;
2023-03-01 02:05:56 +00:00
}
2023-03-02 12:41:33 +00:00
void MainWindow : : onMultiBroadcast ( const QMap < QString , QString > & txHexMap ) {
QMapIterator < QString , QString > i ( txHexMap ) ;
while ( i . hasNext ( ) ) {
i . next ( ) ;
for ( const auto & node : m_nodes - > nodes ( ) ) {
QString address = node . toURL ( ) ;
qDebug ( ) < < QString ( " Relaying %1 to: %2 " ) . arg ( i . key ( ) , address ) ;
m_rpc - > setDaemonAddress ( address ) ;
m_rpc - > sendRawTransaction ( i . value ( ) ) ;
}
}
}
2020-10-07 10:36:04 +00:00
void MainWindow : : onSynchronized ( ) {
2020-11-23 16:57:38 +00:00
this - > updateNetStats ( ) ;
2020-12-24 14:46:56 +00:00
this - > setStatusText ( " Synchronized " ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : onBlockchainSync ( int height , int target ) {
2020-12-23 01:42:22 +00:00
QString blocks = ( target > = height ) ? QString : : number ( target - height ) : " ? " ;
QString heightText = QString ( " Blockchain sync: %1 blocks remaining " ) . arg ( blocks ) ;
2020-12-24 14:46:56 +00:00
this - > setStatusText ( heightText ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : onRefreshSync ( int height , int target ) {
2020-12-23 01:42:22 +00:00
QString blocks = ( target > = height ) ? QString : : number ( target - height ) : " ? " ;
2020-12-30 03:58:17 +00:00
QString heightText = QString ( " Wallet sync: %1 blocks remaining " ) . arg ( blocks ) ;
2020-12-24 14:46:56 +00:00
this - > setStatusText ( heightText ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : onConnectionStatusChanged ( int status )
{
2020-11-23 16:57:38 +00:00
qDebug ( ) < < " Wallet connection status changed " < < Utils : : QtEnumToString ( static_cast < Wallet : : ConnectionStatus > ( status ) ) ;
2020-10-07 10:36:04 +00:00
// Update connection info in status bar.
2021-05-02 18:22:38 +00:00
QIcon icon ;
2023-09-12 14:15:40 +00:00
if ( conf ( ) - > get ( Config : : offlineMode ) . toBool ( ) ) {
2023-02-11 17:11:21 +00:00
icon = icons ( ) - > icon ( " status_offline.svg " ) ;
2023-12-02 18:28:31 +00:00
this - > setStatusText ( " Offline mode " ) ;
2023-02-11 17:11:21 +00:00
} else {
switch ( status ) {
case Wallet : : ConnectionStatus_Disconnected :
icon = icons ( ) - > icon ( " status_disconnected.svg " ) ;
this - > setStatusText ( " Disconnected " ) ;
break ;
case Wallet : : ConnectionStatus_Connecting :
icon = icons ( ) - > icon ( " status_lagging.svg " ) ;
this - > setStatusText ( " Connecting to node " ) ;
break ;
case Wallet : : ConnectionStatus_WrongVersion :
icon = icons ( ) - > icon ( " status_disconnected.svg " ) ;
this - > setStatusText ( " Incompatible node " ) ;
break ;
case Wallet : : ConnectionStatus_Synchronizing :
icon = icons ( ) - > icon ( " status_waiting.svg " ) ;
break ;
case Wallet : : ConnectionStatus_Synchronized :
icon = icons ( ) - > icon ( " status_connected.svg " ) ;
break ;
default :
icon = icons ( ) - > icon ( " status_disconnected.svg " ) ;
break ;
}
2020-10-07 10:36:04 +00:00
}
2021-05-02 18:22:38 +00:00
m_statusBtnConnectionStatusIndicator - > setIcon ( icon ) ;
2020-10-07 10:36:04 +00:00
}
2023-09-12 14:15:40 +00:00
void MainWindow : : onTransactionCreated ( PendingTransaction * tx , const QVector < QString > & address ) {
// Clean up some UI
m_constructingTransaction = false ;
m_txTimer . stop ( ) ;
this - > setStatusText ( m_statusText ) ;
if ( m_wallet - > isHwBacked ( ) ) {
m_splashDialog - > hide ( ) ;
}
2021-08-19 18:22:53 +00:00
if ( tx - > status ( ) ! = PendingTransaction : : Status_Ok ) {
2023-12-13 14:24:02 +00:00
if ( m_showDeviceError ) {
// The hardware devices has disconnected during tx construction.
// Due to a macOS-specific Qt bug, we have to prevent it from stacking two QMessageBoxes, otherwise
// the UI becomes unresponsive. The reconnect dialog should take priority.
m_wallet - > disposeTransaction ( tx ) ;
return ;
}
2023-09-12 14:15:40 +00:00
QString errMsg = tx - > errorString ( ) ;
2020-10-07 10:36:04 +00:00
2023-09-12 14:15:40 +00:00
Utils : : Message message { this , Utils : : ERROR , " Failed to construct transaction " , errMsg } ;
2020-10-07 10:36:04 +00:00
2023-09-12 14:15:40 +00:00
if ( tx - > getException ( ) ) {
try
{
std : : rethrow_exception ( tx - > getException ( ) ) ;
}
catch ( const tools : : error : : daemon_busy & e ) {
message . description = QString ( " Node was unable to respond. Failed request: %1 " ) . arg ( QString : : fromStdString ( e . request ( ) ) ) ;
message . helpItems = { " Try sending the transaction again. " , " If this keeps happening, connect to a different node. " } ;
}
catch ( const tools : : error : : no_connection_to_daemon & e ) {
message . description = QString ( " Connection to node lost. Failed request: %1 " ) . arg ( QString : : fromStdString ( e . request ( ) ) ) ;
message . helpItems = { " Try sending the transaction again. " , " If this keeps happening, connect to a different node. " } ;
}
catch ( const tools : : error : : wallet_rpc_error & e ) {
message . description = QString ( " RPC error: %1 " ) . arg ( QString : : fromStdString ( e . to_string ( ) ) ) ;
message . helpItems = { " Try sending the transaction again. " , " If this keeps happening, connect to a different node. " } ;
}
catch ( const tools : : error : : get_outs_error & e ) {
message . description = " Failed to get enough decoy outputs from node " ;
message . helpItems = { " Your transaction has too many inputs. Try sending a lower amount. " } ;
}
catch ( const tools : : error : : not_enough_unlocked_money & e ) {
2023-10-25 23:23:22 +00:00
QString error ;
if ( e . fee ( ) > e . available ( ) ) {
error = QString ( " Transaction fee exceeds spendable balance. \n \n Spendable balance: %1 \n Transaction fee: %2 " ) . arg ( WalletManager : : displayAmount ( e . available ( ) ) , WalletManager : : displayAmount ( e . fee ( ) ) ) ;
}
else {
error = QString ( " Spendable balance insufficient to pay for transaction. \n \n Spendable balance: %1 \n Transaction needs: %2 " ) . arg ( WalletManager : : displayAmount ( e . available ( ) ) , WalletManager : : displayAmount ( e . tx_amount ( ) + e . fee ( ) ) ) ;
}
message . description = error ;
2023-09-12 14:15:40 +00:00
message . helpItems = { " Wait for more balance to unlock. " , " Click 'Help' to learn more about how balance works. " } ;
message . doc = " balance " ;
}
catch ( const tools : : error : : not_enough_money & e ) {
message . description = QString ( " Not enough money to transfer \n \n Total balance: %1 \n Transaction amount: %2 " ) . arg ( WalletManager : : displayAmount ( e . available ( ) ) , WalletManager : : displayAmount ( e . tx_amount ( ) ) ) ;
message . helpItems = { " If you are trying to send your entire balance, click 'Max'. " } ;
message . doc = " balance " ;
}
catch ( const tools : : error : : tx_not_possible & e ) {
message . description = QString ( " Not enough money to transfer. Transaction amount + fee exceeds available balance. " ) ;
message . helpItems = { " If you're trying to send your entire balance, click 'Max'. " } ;
message . doc = " balance " ;
}
catch ( const tools : : error : : not_enough_outs_to_mix & e ) {
message . description = " Not enough outputs for specified ring size. " ;
}
catch ( const tools : : error : : tx_not_constructed & ) {
message . description = " Transaction was not constructed " ;
message . helpItems = { " You have found a bug. Please contact the developers. " } ;
message . doc = " report_an_issue " ;
}
catch ( const tools : : error : : tx_rejected & e ) {
// TODO: provide helptext
message . description = QString ( " Transaction was rejected by node. Reason: %1. " ) . arg ( QString : : fromStdString ( e . status ( ) ) ) ;
}
catch ( const tools : : error : : tx_sum_overflow & e ) {
message . description = " Transaction tries to spend an unrealistic amount of XMR " ;
message . helpItems = { " You have found a bug. Please contact the developers. " } ;
message . doc = " report_an_issue " ;
}
catch ( const tools : : error : : zero_amount & ) {
message . description = " Destination amount is zero " ;
message . helpItems = { " You have found a bug. Please contact the developers. " } ;
message . doc = " report_an_issue " ;
}
catch ( const tools : : error : : zero_destination & ) {
message . description = " Transaction has no destination " ;
message . helpItems = { " You have found a bug. Please contact the developers. " } ;
message . doc = " report_an_issue " ;
}
catch ( const tools : : error : : tx_too_big & e ) {
message . description = " Transaction too big " ;
message . helpItems = { " Try sending a smaller amount. " } ;
}
catch ( const tools : : error : : transfer_error & e ) {
message . description = QString ( " Unknown transfer error: %1 " ) . arg ( QString : : fromStdString ( e . what ( ) ) ) ;
message . helpItems = { " You have found a bug. Please contact the developers. " } ;
message . doc = " report_an_issue " ;
}
catch ( const tools : : error : : wallet_internal_error & e ) {
2023-10-26 00:16:09 +00:00
bool bug = true ;
2023-09-12 14:15:40 +00:00
QString msg = e . what ( ) ;
message . description = QString ( " Internal error: %1 " ) . arg ( QString : : fromStdString ( e . what ( ) ) ) ;
2023-10-26 00:16:09 +00:00
2023-09-12 14:15:40 +00:00
if ( msg . contains ( " Daemon response did not include the requested real output " ) ) {
QString currentNode = m_nodes - > connection ( ) . toAddress ( ) ;
message . description + = QString ( " \n You are currently connected to: %1 \n \n "
" This node may be acting maliciously. You are strongly recommended to disconnect from this node. "
" Please report this incident to the developers. " ) . arg ( currentNode ) ;
message . doc = " report_an_issue " ;
}
2023-10-26 00:16:09 +00:00
if ( msg . startsWith ( " No unlocked balance " ) ) {
// TODO: We're sending ALL, but fractional outputs got ignored
message . description = " Spendable balance insufficient to pay for transaction fee. " ;
bug = false ;
}
2021-05-22 18:13:48 +00:00
2023-10-26 00:16:09 +00:00
if ( bug ) {
message . helpItems = { " You have found a bug. Please contact the developers. " } ;
message . doc = " report_an_issue " ;
}
2023-09-12 14:15:40 +00:00
}
catch ( const std : : exception & e ) {
message . description = QString : : fromStdString ( e . what ( ) ) ;
}
2021-05-22 18:13:48 +00:00
}
2023-09-12 14:15:40 +00:00
Utils : : showMsg ( message ) ;
2023-03-01 02:05:56 +00:00
m_wallet - > disposeTransaction ( tx ) ;
2021-08-19 18:22:53 +00:00
return ;
2021-05-22 18:13:48 +00:00
}
else if ( tx - > txCount ( ) = = 0 ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Failed to construct transaction " , " No transactions were constructed " , { " You have found a bug. Please contact the developers. " } , " report_an_issue " ) ;
2023-03-01 02:05:56 +00:00
m_wallet - > disposeTransaction ( tx ) ;
2021-08-19 18:22:53 +00:00
return ;
2021-05-22 18:13:48 +00:00
}
2022-03-12 13:54:08 +00:00
else if ( tx - > txCount ( ) > 1 ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Failed to construct transaction " , " Split transactions are not supported " , { " Try sending a smaller amount. " } ) ;
2023-03-01 02:05:56 +00:00
m_wallet - > disposeTransaction ( tx ) ;
2022-03-12 13:54:08 +00:00
return ;
}
2022-03-12 13:56:03 +00:00
// This is a weak check to see if we send to all specified destination addresses
// This is here to catch rare memory corruption errors during transaction construction
// TODO: also check that amounts match
tx - > refresh ( ) ;
QSet < QString > outputAddresses ;
for ( const auto & output : tx - > transaction ( 0 ) - > outputs ( ) ) {
2022-07-26 16:59:17 +00:00
outputAddresses . insert ( WalletManager : : baseAddressFromIntegratedAddress ( output - > address ( ) , constants : : networkType ) ) ;
2022-03-12 13:56:03 +00:00
}
QSet < QString > destAddresses ;
for ( const auto & addr : address ) {
2022-07-26 16:59:17 +00:00
// TODO: Monero core bug, integrated address is not added to dests for transactions spending ALL
destAddresses . insert ( WalletManager : : baseAddressFromIntegratedAddress ( addr , constants : : networkType ) ) ;
2022-03-12 13:56:03 +00:00
}
if ( ! outputAddresses . contains ( destAddresses ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Transaction fails sanity check " , " Constructed transaction doesn't appear to send to (all) specified destination address(es). Try creating the transaction again. " ) ;
2023-03-01 02:05:56 +00:00
m_wallet - > disposeTransaction ( tx ) ;
2022-03-12 13:56:03 +00:00
return ;
}
2021-01-26 23:55:27 +00:00
2023-03-01 02:05:56 +00:00
m_wallet - > addCacheTransaction ( tx - > txid ( ) [ 0 ] , tx - > signedTxToHex ( 0 ) ) ;
2021-08-19 18:22:53 +00:00
2023-12-02 18:28:31 +00:00
// Offline transaction signing
if ( m_wallet - > viewOnly ( ) ) {
# ifdef WITH_SCANNER
OfflineTxSigningWizard wizard ( this , m_wallet , tx ) ;
wizard . exec ( ) ;
if ( ! wizard . readyToCommit ( ) ) {
return ;
} else {
tx = wizard . signedTx ( ) ;
}
if ( tx - > txCount ( ) = = 0 ) {
Utils : : showError ( this , " Failed to load transaction " , " No transactions were found " , { " You have found a bug. Please contact the developers. " } , " report_an_issue " ) ;
m_wallet - > disposeTransaction ( tx ) ;
return ;
}
# else
Utils : : showError ( this , " Can't open offline transaction signing wizard " , " Feather was built without webcam QR scanner support " ) ;
return ;
# endif
}
2021-08-19 18:22:53 +00:00
// Show advanced dialog on multi-destination transactions
2023-12-02 18:28:31 +00:00
if ( address . size ( ) > 1 ) {
2023-03-01 02:05:56 +00:00
TxConfAdvDialog dialog_adv { m_wallet , m_wallet - > tmpTxDescription , this } ;
dialog_adv . setTransaction ( tx , ! m_wallet - > viewOnly ( ) ) ;
2021-08-19 18:22:53 +00:00
dialog_adv . exec ( ) ;
return ;
}
2020-10-16 03:05:05 +00:00
2023-03-01 02:05:56 +00:00
TxConfDialog dialog { m_wallet , tx , address [ 0 ] , m_wallet - > tmpTxDescription , this } ;
2021-08-19 18:22:53 +00:00
switch ( dialog . exec ( ) ) {
case QDialog : : Rejected :
{
2023-03-01 02:05:56 +00:00
if ( ! dialog . showAdvanced ) {
m_wallet - > disposeTransaction ( tx ) ;
}
2021-08-19 18:22:53 +00:00
break ;
2020-10-16 03:05:05 +00:00
}
2021-08-19 18:22:53 +00:00
case QDialog : : Accepted :
2023-03-01 02:05:56 +00:00
m_wallet - > commitTransaction ( tx , m_wallet - > tmpTxDescription ) ;
2021-08-19 18:22:53 +00:00
break ;
}
if ( dialog . showAdvanced ) {
2023-03-01 02:05:56 +00:00
TxConfAdvDialog dialog_adv { m_wallet , m_wallet - > tmpTxDescription , this } ;
2021-08-19 18:22:53 +00:00
dialog_adv . setTransaction ( tx ) ;
dialog_adv . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
}
2023-03-02 12:41:33 +00:00
void MainWindow : : onTransactionCommitted ( bool success , PendingTransaction * tx , const QStringList & txid ) {
2023-09-12 14:15:40 +00:00
if ( ! success ) {
2023-12-02 18:28:31 +00:00
QString error = tx - > errorString ( ) ;
if ( m_wallet - > viewOnly ( ) & & error . contains ( " double spend " ) ) {
m_wallet - > setForceKeyImageSync ( true ) ;
}
2023-12-14 15:37:46 +00:00
if ( error . contains ( " no connection to daemon " ) ) {
auto button = QMessageBox : : question ( this , " Unable to send transaction " , " No connection to node. Retry sending transaction? " ) ;
if ( button = = QMessageBox : : Yes ) {
m_wallet - > commitTransaction ( tx , m_wallet - > tmpTxDescription ) ;
}
return ;
}
2023-12-02 18:28:31 +00:00
Utils : : showError ( this , " Failed to send transaction " , error ) ;
2023-09-12 14:15:40 +00:00
return ;
2020-10-07 10:36:04 +00:00
}
2023-09-12 14:15:40 +00:00
QMessageBox msgBox { this } ;
QPushButton * showDetailsButton = msgBox . addButton ( " Show details " , QMessageBox : : ActionRole ) ;
msgBox . addButton ( QMessageBox : : Ok ) ;
QString body = QString ( " Successfully sent %1 transaction(s). " ) . arg ( txid . count ( ) ) ;
msgBox . setText ( body ) ;
msgBox . setWindowTitle ( " Transaction sent " ) ;
msgBox . setIcon ( QMessageBox : : Icon : : Information ) ;
msgBox . exec ( ) ;
if ( msgBox . clickedButton ( ) = = showDetailsButton ) {
this - > showHistoryTab ( ) ;
2023-11-17 13:05:31 +00:00
TransactionRow * txInfo = m_wallet - > history ( ) - > transaction ( txid . first ( ) ) ;
2023-09-12 14:15:40 +00:00
auto * dialog = new TxInfoDialog ( m_wallet , txInfo , this ) ;
connect ( dialog , & TxInfoDialog : : resendTranscation , this , & MainWindow : : onResendTransaction ) ;
dialog - > show ( ) ;
dialog - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
2020-10-07 10:36:04 +00:00
}
2023-09-12 14:15:40 +00:00
m_sendWidget - > clearFields ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : showWalletInfoDialog ( ) {
2023-03-01 02:05:56 +00:00
WalletInfoDialog dialog { m_wallet , this } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : showSeedDialog ( ) {
2023-03-01 02:05:56 +00:00
if ( m_wallet - > isHwBacked ( ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " Seed unavailable " , " Wallet keys are stored on a hardware device " , { } , " show_wallet_seed " ) ;
2021-05-02 18:22:38 +00:00
return ;
}
2023-03-01 02:05:56 +00:00
if ( m_wallet - > viewOnly ( ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " Seed unavailable " , " Wallet is view-only " , { " To obtain your private spendkey go to Wallet -> Keys " } , " show_wallet_seed " ) ;
2021-03-14 21:12:02 +00:00
return ;
}
2023-03-01 02:05:56 +00:00
if ( ! m_wallet - > isDeterministic ( ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " Seed unavailable " , " Wallet is non-deterministic and has no seed " ,
{ " To obtain wallet keys go to Wallet -> Keys " } , " show_wallet_seed " ) ;
2021-03-14 21:12:02 +00:00
return ;
}
2021-06-04 18:48:44 +00:00
if ( ! this - > verifyPassword ( ) ) {
return ;
}
2023-03-01 02:05:56 +00:00
SeedDialog dialog { m_wallet , this } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : showPasswordDialog ( ) {
2023-03-01 02:05:56 +00:00
PasswordChangeDialog dialog { this , m_wallet } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-11-14 09:57:06 +00:00
this - > updatePasswordIcon ( ) ;
}
void MainWindow : : updatePasswordIcon ( ) {
2023-03-01 02:05:56 +00:00
bool emptyPassword = m_wallet - > verifyPassword ( " " ) ;
2023-01-31 16:00:50 +00:00
QIcon icon = emptyPassword ? icons ( ) - > icon ( " unlock.svg " ) : icons ( ) - > icon ( " lock.svg " ) ;
2020-11-14 09:57:06 +00:00
m_statusBtnPassword - > setIcon ( icon ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : showKeysDialog ( ) {
2021-06-04 18:48:44 +00:00
if ( ! this - > verifyPassword ( ) ) {
return ;
}
2023-03-01 02:05:56 +00:00
KeysDialog dialog { m_wallet , this } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
2020-10-12 22:01:06 +00:00
void MainWindow : : showViewOnlyDialog ( ) {
2023-12-11 22:52:44 +00:00
if ( ! this - > verifyPassword ( ) ) {
return ;
}
2023-03-01 02:05:56 +00:00
ViewOnlyDialog dialog { m_wallet , this } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-10-12 22:01:06 +00:00
}
2023-12-02 18:28:31 +00:00
void MainWindow : : showKeyImageSyncWizard ( ) {
# ifdef WITH_SCANNER
OfflineTxSigningWizard wizard { this , m_wallet } ;
wizard . exec ( ) ;
if ( wizard . readyToSign ( ) ) {
TxConfAdvDialog dialog { m_wallet , " " , this , true } ;
dialog . setUnsignedTransaction ( wizard . unsignedTransaction ( ) ) ;
auto r = dialog . exec ( ) ;
if ( r ! = QDialog : : Accepted ) {
return ;
}
wizard . setStartId ( OfflineTxSigningWizard : : Page_ExportSignedTx ) ;
wizard . restart ( ) ;
wizard . exec ( ) ;
}
# else
Utils : : showError ( this , " Can't open offline transaction signing wizard " , " Feather was built without webcam QR scanner support " ) ;
# endif
}
2021-05-02 18:22:38 +00:00
void MainWindow : : menuHwDeviceClicked ( ) {
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " Hardware device " , QString ( " This wallet is backed by a %1 hardware device. " ) . arg ( this - > getHardwareDevice ( ) ) ) ;
2021-05-02 18:22:38 +00:00
}
2021-05-18 15:59:18 +00:00
void MainWindow : : menuOpenClicked ( ) {
m_windowManager - > wizardOpenWallet ( ) ;
}
2020-10-07 10:36:04 +00:00
void MainWindow : : menuNewRestoreClicked ( ) {
2021-05-18 15:59:18 +00:00
m_windowManager - > showWizard ( WalletWizard : : Page_Menu ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : menuQuitClicked ( ) {
2021-05-18 15:59:18 +00:00
this - > close ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : menuWalletCloseClicked ( ) {
2021-05-18 15:59:18 +00:00
m_windowManager - > showWizard ( WalletWizard : : Page_Menu ) ;
this - > close ( ) ;
2020-10-07 10:36:04 +00:00
}
2023-02-11 17:11:21 +00:00
void MainWindow : : menuProxySettingsClicked ( ) {
this - > menuSettingsClicked ( true ) ;
}
2020-10-07 10:36:04 +00:00
void MainWindow : : menuAboutClicked ( ) {
2021-05-02 18:22:38 +00:00
AboutDialog dialog { this } ;
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
2023-02-11 17:11:21 +00:00
void MainWindow : : menuSettingsClicked ( bool showProxyTab ) {
2023-03-01 02:05:56 +00:00
m_windowManager - > showSettings ( m_nodes , this , showProxyTab ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : menuSignVerifyClicked ( ) {
2023-03-01 02:05:56 +00:00
SignVerifyDialog dialog { m_wallet , this } ;
2021-05-02 18:22:38 +00:00
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : menuVerifyTxProof ( ) {
2023-03-01 02:05:56 +00:00
VerifyProofDialog dialog { m_wallet , this } ;
2021-05-02 18:22:38 +00:00
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
2022-03-11 13:56:07 +00:00
void MainWindow : : onShowSettingsPage ( int page ) {
2023-09-12 14:15:40 +00:00
conf ( ) - > set ( Config : : lastSettingsPage , page ) ;
2022-03-11 13:56:07 +00:00
this - > menuSettingsClicked ( ) ;
}
2020-10-07 10:36:04 +00:00
void MainWindow : : skinChanged ( const QString & skinName ) {
2021-01-28 22:48:14 +00:00
ColorScheme : : updateFromWidget ( this ) ;
2021-06-25 14:14:49 +00:00
this - > updateWidgetIcons ( ) ;
}
2021-02-03 19:40:35 +00:00
2021-06-25 14:14:49 +00:00
void MainWindow : : updateWidgetIcons ( ) {
m_sendWidget - > skinChanged ( ) ;
2023-11-30 14:01:39 +00:00
emit updateIcons ( ) ;
2021-07-01 21:00:47 +00:00
m_statusBtnHwDevice - > setIcon ( this - > hardwareDevicePairedIcon ( ) ) ;
}
QIcon MainWindow : : hardwareDevicePairedIcon ( ) {
QString filename ;
2023-03-01 02:05:56 +00:00
if ( m_wallet - > isLedger ( ) )
2021-07-01 21:00:47 +00:00
filename = " ledger.png " ;
2023-03-01 02:05:56 +00:00
else if ( m_wallet - > isTrezor ( ) )
2021-07-01 21:00:47 +00:00
filename = ColorScheme : : darkScheme ? " trezor_white.png " : " trezor.png " ;
return icons ( ) - > icon ( filename ) ;
}
QIcon MainWindow : : hardwareDeviceUnpairedIcon ( ) {
QString filename ;
2023-03-01 02:05:56 +00:00
if ( m_wallet - > isLedger ( ) )
2021-07-01 21:00:47 +00:00
filename = " ledger_unpaired.png " ;
2023-03-01 02:05:56 +00:00
else if ( m_wallet - > isTrezor ( ) )
2021-07-01 21:00:47 +00:00
filename = ColorScheme : : darkScheme ? " trezor_unpaired_white.png " : " trezor_unpaired.png " ;
return icons ( ) - > icon ( filename ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : closeEvent ( QCloseEvent * event ) {
2021-05-18 15:59:18 +00:00
qDebug ( ) < < Q_FUNC_INFO ;
if ( ! this - > cleanedUp ) {
this - > cleanedUp = true ;
2023-11-30 14:01:39 +00:00
emit aboutToQuit ( ) ;
2022-07-02 20:41:46 +00:00
2021-05-25 13:30:19 +00:00
m_historyWidget - > resetModel ( ) ;
2021-05-18 15:59:18 +00:00
m_updateBytes . stop ( ) ;
m_txTimer . stop ( ) ;
2021-07-09 22:25:13 +00:00
// Wallet signal may fire after AppContext is gone, causing segv
2023-03-01 02:05:56 +00:00
m_wallet - > disconnect ( ) ;
this - > disconnect ( ) ;
2020-10-07 10:36:04 +00:00
2021-05-18 15:59:18 +00:00
this - > saveGeo ( ) ;
m_windowManager - > closeWindow ( this ) ;
}
event - > accept ( ) ;
2020-10-07 10:36:04 +00:00
}
2023-02-11 17:11:21 +00:00
void MainWindow : : changeEvent ( QEvent * event )
{
if ( ( event - > type ( ) = = QEvent : : WindowStateChange ) & & this - > isMinimized ( ) ) {
2023-09-12 14:15:40 +00:00
if ( conf ( ) - > get ( Config : : lockOnMinimize ) . toBool ( ) ) {
2023-02-11 17:11:21 +00:00
this - > lockWallet ( ) ;
}
} else {
QMainWindow : : changeEvent ( event ) ;
}
}
2020-10-07 10:36:04 +00:00
void MainWindow : : donateButtonClicked ( ) {
2023-10-16 20:13:59 +00:00
m_sendWidget - > fill ( constants : : donationAddress , constants : : donationDescription ) ;
2023-11-30 14:01:39 +00:00
ui - > tabWidget - > setCurrentIndex ( this - > findTab ( " Send " ) ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : showHistoryTab ( ) {
this - > raise ( ) ;
2023-11-30 14:01:39 +00:00
ui - > tabWidget - > setCurrentIndex ( this - > findTab ( " History " ) ) ;
2020-10-07 10:36:04 +00:00
}
2022-06-23 17:48:30 +00:00
void MainWindow : : fillSendTab ( const QString & address , const QString & description ) {
m_sendWidget - > fill ( address , description ) ;
2023-11-30 14:01:39 +00:00
ui - > tabWidget - > setCurrentIndex ( this - > findTab ( " Send " ) ) ;
2020-10-07 10:36:04 +00:00
}
2021-01-26 23:55:27 +00:00
void MainWindow : : payToMany ( ) {
2023-11-30 14:01:39 +00:00
ui - > tabWidget - > setCurrentIndex ( this - > findTab ( " Send " ) ) ;
2021-05-10 15:05:10 +00:00
m_sendWidget - > payToMany ( ) ;
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " Pay to many " , " Enter a list of outputs in the 'Pay to' field. \n "
" One output per line. \n "
" Format: address, amount \n "
" A maximum of 16 addresses may be specified. " ) ;
2021-01-26 23:55:27 +00:00
}
2020-10-07 10:36:04 +00:00
void MainWindow : : onViewOnBlockExplorer ( const QString & txid ) {
2023-09-12 14:15:40 +00:00
QString blockExplorerLink = Utils : : blockExplorerLink ( conf ( ) - > get ( Config : : blockExplorer ) . toString ( ) , constants : : networkType , txid ) ;
2020-12-14 02:20:05 +00:00
Utils : : externalLinkWarning ( this , blockExplorerLink ) ;
2020-10-07 10:36:04 +00:00
}
2020-12-14 22:07:23 +00:00
void MainWindow : : onResendTransaction ( const QString & txid ) {
2023-03-01 02:05:56 +00:00
QString txHex = m_wallet - > getCacheTransaction ( txid ) ;
2021-07-04 21:17:10 +00:00
if ( txHex . isEmpty ( ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Unable to resend transaction " , " Transaction was not found in the transaction cache. " ) ;
2020-12-14 22:07:23 +00:00
return ;
}
// Connect to a different node so chances of successful relay are higher
2023-03-01 02:05:56 +00:00
m_nodes - > autoConnect ( true ) ;
2020-12-14 22:07:23 +00:00
2023-03-01 02:05:56 +00:00
TxBroadcastDialog dialog { this , m_nodes , txHex } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-12-14 22:07:23 +00:00
}
2020-10-21 06:25:02 +00:00
void MainWindow : : importContacts ( ) {
const QString targetFile = QFileDialog : : getOpenFileName ( this , " Import CSV file " , QDir : : homePath ( ) , " CSV Files (*.csv) " ) ;
if ( targetFile . isEmpty ( ) ) return ;
2023-03-01 02:05:56 +00:00
auto * model = m_wallet - > addressBookModel ( ) ;
2020-10-21 06:25:02 +00:00
QMapIterator < QString , QString > i ( model - > readCSV ( targetFile ) ) ;
int inserts = 0 ;
while ( i . hasNext ( ) ) {
i . next ( ) ;
2023-03-01 02:05:56 +00:00
bool addressValid = WalletManager : : addressValid ( i . value ( ) , m_wallet - > nettype ( ) ) ;
2020-10-21 06:25:02 +00:00
if ( addressValid ) {
2023-11-17 13:05:31 +00:00
m_wallet - > addressBook ( ) - > addRow ( i . value ( ) , i . key ( ) ) ;
2020-10-21 06:25:02 +00:00
inserts + + ;
}
}
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " Contacts imported " , QString ( " Total contacts imported: %1 " ) . arg ( inserts ) ) ;
2020-10-21 06:25:02 +00:00
}
2020-10-07 10:36:04 +00:00
void MainWindow : : saveGeo ( ) {
2023-09-12 14:15:40 +00:00
conf ( ) - > set ( Config : : geometry , QString ( saveGeometry ( ) . toBase64 ( ) ) ) ;
conf ( ) - > set ( Config : : windowState , QString ( saveState ( ) . toBase64 ( ) ) ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : restoreGeo ( ) {
2023-09-12 14:15:40 +00:00
bool geo = this - > restoreGeometry ( QByteArray : : fromBase64 ( conf ( ) - > get ( Config : : geometry ) . toByteArray ( ) ) ) ;
bool windowState = this - > restoreState ( QByteArray : : fromBase64 ( conf ( ) - > get ( Config : : windowState ) . toByteArray ( ) ) ) ;
2020-10-07 10:36:04 +00:00
qDebug ( ) < < " Restored window state: " < < geo < < " " < < windowState ;
}
void MainWindow : : showDebugInfo ( ) {
2023-03-01 02:05:56 +00:00
DebugInfoDialog dialog { m_wallet , m_nodes , this } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2021-01-25 16:38:04 +00:00
}
void MainWindow : : showWalletCacheDebugDialog ( ) {
2022-03-04 12:42:14 +00:00
if ( ! this - > verifyPassword ( ) ) {
return ;
}
2023-03-01 02:05:56 +00:00
WalletCacheDebugDialog dialog { m_wallet , this } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
2021-05-22 13:42:26 +00:00
void MainWindow : : showAccountSwitcherDialog ( ) {
2023-01-17 21:06:08 +00:00
m_accountSwitcherDialog - > show ( ) ;
2023-03-13 18:46:31 +00:00
m_accountSwitcherDialog - > update ( ) ;
2021-05-22 13:42:26 +00:00
}
2021-07-06 14:10:24 +00:00
void MainWindow : : showAddressChecker ( ) {
QString address = QInputDialog : : getText ( this , " Address Checker " , " Address: " ) ;
if ( address . isEmpty ( ) ) {
return ;
}
if ( ! WalletManager : : addressValid ( address , constants : : networkType ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " Invalid address " , " The address you entered is not a valid XMR address for the current network type. " ) ;
2021-07-06 14:10:24 +00:00
return ;
}
2023-03-01 02:05:56 +00:00
SubaddressIndex index = m_wallet - > subaddressIndex ( address ) ;
2021-07-06 14:10:24 +00:00
if ( ! index . isValid ( ) ) {
// TODO: probably mention lookahead here
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " This address does not belong to this wallet " , " " ) ;
2021-07-06 14:10:24 +00:00
return ;
} else {
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , QString ( " This address belongs to Account #%1 " ) . arg ( index . major ) ) ;
2021-07-06 14:10:24 +00:00
}
}
2023-12-02 18:28:31 +00:00
void MainWindow : : showURDialog ( ) {
URDialog dialog { this } ;
dialog . exec ( ) ;
2020-10-16 03:05:05 +00:00
}
void MainWindow : : loadSignedTx ( ) {
2023-06-17 23:12:35 +00:00
QString fn = QFileDialog : : getOpenFileName ( this , " Select transaction to load " , QDir : : homePath ( ) , " Transaction (*signed_monero_tx);;All Files (*) " ) ;
2020-10-16 03:05:05 +00:00
if ( fn . isEmpty ( ) ) return ;
2023-03-01 02:05:56 +00:00
PendingTransaction * tx = m_wallet - > loadSignedTxFile ( fn ) ;
auto err = m_wallet - > errorString ( ) ;
2020-10-16 03:05:05 +00:00
if ( ! err . isEmpty ( ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Unable to load signed transaction " , err ) ;
2020-10-16 03:05:05 +00:00
return ;
}
2023-12-02 18:28:31 +00:00
TxConfAdvDialog dialog { m_wallet , " " , this , true } ;
2021-05-18 15:59:18 +00:00
dialog . setTransaction ( tx ) ;
dialog . exec ( ) ;
2020-10-16 03:05:05 +00:00
}
void MainWindow : : loadSignedTxFromText ( ) {
2023-03-01 02:05:56 +00:00
TxBroadcastDialog dialog { this , m_nodes } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-10-16 03:05:05 +00:00
}
2020-11-10 11:38:37 +00:00
void MainWindow : : importTransaction ( ) {
2023-09-12 14:15:40 +00:00
if ( conf ( ) - > get ( Config : : torPrivacyLevel ) . toInt ( ) = = Config : : allTorExceptNode ) {
2021-05-04 23:09:19 +00:00
// TODO: don't show if connected to local node
auto result = QMessageBox : : warning ( this , " Warning " , " Using this feature may allow a remote node to associate the transaction with your IP address. \n "
" \n "
" Connect to a trusted node or run Feather over Tor if network level metadata leakage is included in your threat model. " ,
QMessageBox : : Ok | QMessageBox : : Cancel ) ;
if ( result ! = QMessageBox : : Ok ) {
return ;
}
2020-11-10 11:38:37 +00:00
}
2021-05-04 23:09:19 +00:00
2023-03-01 02:05:56 +00:00
TxImportDialog dialog ( this , m_wallet ) ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-11-10 11:38:37 +00:00
}
2021-05-02 18:22:38 +00:00
void MainWindow : : onDeviceError ( const QString & error ) {
2023-03-01 02:05:56 +00:00
qCritical ( ) < < " Device error: " < < error ;
2021-05-02 18:22:38 +00:00
if ( m_showDeviceError ) {
return ;
}
2021-07-01 21:00:47 +00:00
m_statusBtnHwDevice - > setIcon ( this - > hardwareDeviceUnpairedIcon ( ) ) ;
2021-05-02 18:22:38 +00:00
while ( true ) {
m_showDeviceError = true ;
auto result = QMessageBox : : question ( this , " Hardware device " , " Lost connection to hardware device. Attempt to reconnect? " ) ;
if ( result = = QMessageBox : : Yes ) {
2023-03-01 02:05:56 +00:00
bool r = m_wallet - > reconnectDevice ( ) ;
2021-05-02 18:22:38 +00:00
if ( r ) {
break ;
}
}
2021-05-18 15:59:18 +00:00
if ( result = = QMessageBox : : No ) {
this - > menuWalletCloseClicked ( ) ;
2021-05-02 18:22:38 +00:00
return ;
}
}
2021-07-01 21:00:47 +00:00
m_statusBtnHwDevice - > setIcon ( this - > hardwareDevicePairedIcon ( ) ) ;
2023-03-01 02:05:56 +00:00
m_wallet - > startRefresh ( ) ;
2021-05-02 18:22:38 +00:00
m_showDeviceError = false ;
}
2021-07-01 21:00:47 +00:00
void MainWindow : : onDeviceButtonRequest ( quint64 code ) {
2021-07-09 21:48:43 +00:00
qDebug ( ) < < " DeviceButtonRequest, code: " < < code ;
2023-03-01 02:05:56 +00:00
if ( m_wallet - > isTrezor ( ) ) {
2021-07-01 21:00:47 +00:00
switch ( code ) {
2021-07-09 21:48:43 +00:00
case 1 :
{
m_splashDialog - > setMessage ( " Action required on device: Enter your PIN to continue " ) ;
m_splashDialog - > setIcon ( QPixmap ( " :/assets/images/key.png " ) ) ;
m_splashDialog - > show ( ) ;
m_splashDialog - > setEnabled ( true ) ;
break ;
}
2021-07-02 16:06:19 +00:00
case 8 :
2021-07-09 21:48:43 +00:00
default :
2021-07-01 21:00:47 +00:00
{
2021-07-02 16:06:19 +00:00
// Annoyingly, this code is used for a variety of actions, including:
// Confirm refresh: Do you really want to start refresh?
// Confirm export: Do you really want to export tx_key?
if ( m_constructingTransaction ) { // This code is also used when signing a tx, we handle this elsewhere
2021-07-01 21:00:47 +00:00
break ;
}
2021-07-02 16:06:19 +00:00
m_splashDialog - > setMessage ( " Confirm action on device to proceed " ) ;
2023-03-29 09:26:10 +00:00
m_splashDialog - > setIcon ( QPixmap ( " :/assets/images/confirmed.svg " ) ) ;
2021-07-01 21:00:47 +00:00
m_splashDialog - > show ( ) ;
m_splashDialog - > setEnabled ( true ) ;
break ;
}
}
}
}
void MainWindow : : onDeviceButtonPressed ( ) {
if ( m_constructingTransaction ) {
return ;
}
m_splashDialog - > hide ( ) ;
}
2021-07-08 00:34:27 +00:00
void MainWindow : : onWalletPassphraseNeeded ( bool on_device ) {
auto button = QMessageBox : : question ( nullptr , " Wallet Passphrase Needed " , " Enter passphrase on hardware wallet? \n \n "
" It is recommended to enter passphrase on "
" the hardware wallet for better security. " ,
QMessageBox : : Yes | QMessageBox : : No , QMessageBox : : Yes ) ;
if ( button = = QMessageBox : : Yes ) {
2023-03-01 02:05:56 +00:00
m_wallet - > onPassphraseEntered ( " " , true , false ) ;
2021-07-08 00:34:27 +00:00
return ;
}
bool ok ;
QString passphrase = QInputDialog : : getText ( nullptr , " Wallet Passphrase Needed " , " Enter passphrase: " , QLineEdit : : EchoMode : : Password , " " , & ok ) ;
if ( ok ) {
2023-03-01 02:05:56 +00:00
m_wallet - > onPassphraseEntered ( passphrase , false , false ) ;
2021-07-08 00:34:27 +00:00
} else {
2023-03-01 02:05:56 +00:00
m_wallet - > onPassphraseEntered ( passphrase , false , true ) ;
2021-07-08 00:34:27 +00:00
}
}
2020-11-23 16:57:38 +00:00
void MainWindow : : updateNetStats ( ) {
2023-03-01 02:05:56 +00:00
if ( ! m_wallet | | m_wallet - > connectionStatus ( ) = = Wallet : : ConnectionStatus_Disconnected
| | m_wallet - > connectionStatus ( ) = = Wallet : : ConnectionStatus_Synchronized )
2021-07-06 19:29:12 +00:00
{
m_statusLabelNetStats - > hide ( ) ;
2020-11-23 16:57:38 +00:00
return ;
}
2021-05-02 18:22:38 +00:00
2021-07-06 19:29:12 +00:00
m_statusLabelNetStats - > show ( ) ;
2023-03-01 02:05:56 +00:00
m_statusLabelNetStats - > setText ( QString ( " (D: %1) " ) . arg ( Utils : : formatBytes ( m_wallet - > getBytesReceived ( ) ) ) ) ;
2020-11-23 16:57:38 +00:00
}
2020-12-14 19:44:37 +00:00
void MainWindow : : rescanSpent ( ) {
2023-12-02 18:28:31 +00:00
QMessageBox warning { this } ;
warning . setWindowTitle ( " Warning " ) ;
warning . setText ( " Rescanning spent outputs reveals which outputs you own to the node. "
" Make sure you are connected to a trusted node. \n \n "
" Do you want to proceed? " ) ;
warning . setStandardButtons ( QMessageBox : : Yes | QMessageBox : : No ) ;
auto r = warning . exec ( ) ;
if ( r = = QMessageBox : : No ) {
return ;
}
2023-03-01 02:05:56 +00:00
if ( ! m_wallet - > rescanSpent ( ) ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Failed to rescan spent outputs " , m_wallet - > errorString ( ) ) ;
2020-12-14 19:44:37 +00:00
} else {
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " Successfully rescanned spent outputs " ) ;
2020-12-14 19:44:37 +00:00
}
}
2020-12-25 14:20:39 +00:00
void MainWindow : : showBalanceDialog ( ) {
2023-03-01 02:05:56 +00:00
BalanceDialog dialog { this , m_wallet } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-12-25 14:20:39 +00:00
}
2020-12-24 14:46:56 +00:00
QString MainWindow : : statusDots ( ) {
m_statusDots + + ;
m_statusDots = m_statusDots % 4 ;
return QString ( " . " ) . repeated ( m_statusDots ) ;
}
2021-05-18 15:59:18 +00:00
void MainWindow : : showOrHide ( ) {
if ( this - > isHidden ( ) )
this - > bringToFront ( ) ;
else
this - > hide ( ) ;
}
2021-03-08 20:03:20 +00:00
void MainWindow : : bringToFront ( ) {
ensurePolished ( ) ;
setWindowState ( ( windowState ( ) & ~ Qt : : WindowMinimized ) | Qt : : WindowActive ) ;
show ( ) ;
raise ( ) ;
activateWindow ( ) ;
}
2023-02-11 17:11:21 +00:00
void MainWindow : : onPreferredFiatCurrencyChanged ( ) {
m_sendWidget - > onPreferredFiatCurrencyChanged ( ) ;
}
void MainWindow : : onHideUpdateNotifications ( bool hidden ) {
if ( hidden ) {
m_statusUpdateAvailable - > hide ( ) ;
}
2023-04-19 15:41:46 +00:00
# ifdef CHECK_UPDATES
2023-02-11 17:11:21 +00:00
else if ( m_updater - > state = = Updater : : State : : UPDATE_AVAILABLE ) {
m_statusUpdateAvailable - > show ( ) ;
}
2023-04-19 15:41:46 +00:00
# endif
2023-02-11 17:11:21 +00:00
}
2021-05-18 15:59:18 +00:00
void MainWindow : : onTorConnectionStateChanged ( bool connected ) {
2023-09-12 14:15:40 +00:00
if ( conf ( ) - > get ( Config : : proxy ) . toInt ( ) ! = Config : : Proxy : : Tor ) {
2023-02-11 17:11:21 +00:00
return ;
}
2021-05-18 15:59:18 +00:00
if ( connected )
2023-02-11 17:11:21 +00:00
m_statusBtnProxySettings - > setIcon ( icons ( ) - > icon ( " tor_logo.png " ) ) ;
2021-05-18 15:59:18 +00:00
else
2023-02-11 17:11:21 +00:00
m_statusBtnProxySettings - > setIcon ( icons ( ) - > icon ( " tor_logo_disabled.png " ) ) ;
2021-05-02 18:22:38 +00:00
}
2023-02-01 14:40:12 +00:00
void MainWindow : : showUpdateNotification ( ) {
2023-04-19 15:41:46 +00:00
# ifdef CHECK_UPDATES
2023-09-12 14:15:40 +00:00
if ( conf ( ) - > get ( Config : : hideUpdateNotifications ) . toBool ( ) ) {
2023-02-11 17:11:21 +00:00
return ;
}
2023-02-01 14:40:12 +00:00
QString versionDisplay { m_updater - > version } ;
2021-05-02 18:22:38 +00:00
versionDisplay . replace ( " beta " , " Beta " ) ;
QString updateText = QString ( " Update to Feather %1 is available " ) . arg ( versionDisplay ) ;
m_statusUpdateAvailable - > setText ( updateText ) ;
2021-05-04 23:09:19 +00:00
m_statusUpdateAvailable - > setToolTip ( " Click to Download update. " ) ;
2021-05-02 18:22:38 +00:00
m_statusUpdateAvailable - > show ( ) ;
2021-05-04 23:09:19 +00:00
m_statusUpdateAvailable - > disconnect ( ) ;
2023-02-01 14:40:12 +00:00
connect ( m_statusUpdateAvailable , & StatusBarButton : : clicked , this , & MainWindow : : showUpdateDialog ) ;
2023-04-19 15:41:46 +00:00
# endif
2021-05-02 18:22:38 +00:00
}
2023-02-01 14:40:12 +00:00
void MainWindow : : showUpdateDialog ( ) {
2023-04-19 15:41:46 +00:00
# ifdef CHECK_UPDATES
2023-02-01 14:40:12 +00:00
UpdateDialog updateDialog { this , m_updater } ;
2021-05-18 15:59:18 +00:00
connect ( & updateDialog , & UpdateDialog : : restartWallet , m_windowManager , & WindowManager : : restartApplication ) ;
2021-05-02 18:22:38 +00:00
updateDialog . exec ( ) ;
2023-04-19 15:41:46 +00:00
# endif
2021-05-02 18:22:38 +00:00
}
2021-03-24 01:37:54 +00:00
2021-05-02 18:22:38 +00:00
void MainWindow : : onInitiateTransaction ( ) {
m_statusDots = 0 ;
m_constructingTransaction = true ;
m_txTimer . start ( 1000 ) ;
2021-05-04 23:09:19 +00:00
2023-03-01 02:05:56 +00:00
if ( m_wallet - > isHwBacked ( ) ) {
2021-05-04 23:09:19 +00:00
QString message = " Constructing transaction: action may be required on device. " ;
m_splashDialog - > setMessage ( message ) ;
m_splashDialog - > setIcon ( QPixmap ( " :/assets/images/unconfirmed.png " ) ) ;
m_splashDialog - > show ( ) ;
m_splashDialog - > setEnabled ( true ) ;
}
2021-05-02 18:22:38 +00:00
}
2022-03-12 12:53:46 +00:00
void MainWindow : : onKeysCorrupted ( ) {
if ( ! m_criticalWarningShown ) {
m_criticalWarningShown = true ;
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Wallet keys are corrupted " , " WARNING! \n \n To prevent LOSS OF FUNDS do NOT continue to use this wallet file. \n \n Restore your wallet from seed. \n \n Please report this incident to the Feather developers. \n \n WARNING! " ) ;
2023-12-15 13:10:55 +00:00
m_sendWidget - > disallowSending ( ) ;
2022-03-12 12:53:46 +00:00
}
}
2022-03-20 22:30:48 +00:00
void MainWindow : : onSelectedInputsChanged ( const QStringList & selectedInputs ) {
int numInputs = selectedInputs . size ( ) ;
ui - > frame_coinControl - > setStyleSheet ( ColorScheme : : GREEN . asStylesheet ( true ) ) ;
ui - > frame_coinControl - > setVisible ( numInputs > 0 ) ;
if ( numInputs > 0 ) {
quint64 totalAmount = 0 ;
2023-03-01 02:05:56 +00:00
auto coins = m_wallet - > coins ( ) - > coinsFromKeyImage ( selectedInputs ) ;
2022-03-20 22:30:48 +00:00
for ( const auto coin : coins ) {
totalAmount + = coin - > amount ( ) ;
}
QString text = QString ( " Coin control active: %1 selected outputs, %2 XMR " ) . arg ( QString : : number ( numInputs ) , WalletManager : : displayAmount ( totalAmount ) ) ;
ui - > label_coinControl - > setText ( text ) ;
}
}
2023-03-29 09:00:41 +00:00
void MainWindow : : onExportHistoryCSV ( ) {
2021-05-02 18:22:38 +00:00
QString fn = QFileDialog : : getSaveFileName ( this , " Save CSV file " , QDir : : homePath ( ) , " CSV (*.csv) " ) ;
if ( fn . isEmpty ( ) )
return ;
if ( ! fn . endsWith ( " .csv " ) )
fn + = " .csv " ;
2023-03-01 02:05:56 +00:00
m_wallet - > history ( ) - > writeCSV ( fn ) ;
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " CSV export " , QString ( " Transaction history exported to %1 " ) . arg ( fn ) ) ;
2021-05-02 18:22:38 +00:00
}
2023-03-29 09:00:41 +00:00
void MainWindow : : onExportContactsCSV ( ) {
2023-03-01 02:05:56 +00:00
auto * model = m_wallet - > addressBookModel ( ) ;
2021-05-02 18:22:38 +00:00
if ( model - > rowCount ( ) < = 0 ) {
2023-09-12 14:15:40 +00:00
Utils : : showInfo ( this , " Unable to export contacts " , " No contacts to export " ) ;
2021-05-02 18:22:38 +00:00
return ;
}
const QString targetDir = QFileDialog : : getExistingDirectory ( this , " Select CSV output directory " , QDir : : homePath ( ) , QFileDialog : : ShowDirsOnly ) ;
if ( targetDir . isEmpty ( ) ) return ;
2023-03-29 09:00:41 +00:00
qint64 now = QDateTime : : currentMSecsSinceEpoch ( ) ;
2021-05-02 18:22:38 +00:00
QString fn = QString ( " %1/monero-contacts_%2.csv " ) . arg ( targetDir , QString : : number ( now / 1000 ) ) ;
2023-09-12 14:15:40 +00:00
if ( model - > writeCSV ( fn ) ) {
Utils : : showInfo ( this , " Contacts exported successfully " , QString ( " Exported to: %1 " ) . arg ( fn ) ) ;
}
2021-05-02 18:22:38 +00:00
}
2023-03-29 09:00:41 +00:00
void MainWindow : : onCreateDesktopEntry ( ) {
2021-05-02 18:22:38 +00:00
auto msg = Utils : : xdgDesktopEntryRegister ( ) ? " Desktop entry created " : " Desktop entry not created due to an error. " ;
QMessageBox : : information ( this , " Desktop entry " , msg ) ;
}
2023-01-09 02:17:25 +00:00
void MainWindow : : onShowDocumentation ( ) {
2023-09-12 14:15:40 +00:00
// TODO: welcome page
m_windowManager - > showDocs ( this ) ;
2021-10-21 21:13:25 +00:00
}
2023-03-29 09:00:41 +00:00
void MainWindow : : onReportBug ( ) {
2023-09-12 14:15:40 +00:00
m_windowManager - > showDocs ( this , " report_an_issue " ) ;
2021-05-02 18:22:38 +00:00
}
QString MainWindow : : getHardwareDevice ( ) {
2023-03-01 02:05:56 +00:00
if ( ! m_wallet - > isHwBacked ( ) )
2021-05-02 18:22:38 +00:00
return " " ;
2023-03-01 02:05:56 +00:00
if ( m_wallet - > isTrezor ( ) )
2021-05-02 18:22:38 +00:00
return " Trezor " ;
2023-03-01 02:05:56 +00:00
if ( m_wallet - > isLedger ( ) )
2021-05-02 18:22:38 +00:00
return " Ledger " ;
return " Unknown " ;
}
2021-05-23 13:58:28 +00:00
void MainWindow : : updateTitle ( ) {
2023-03-01 02:05:56 +00:00
QString title = QString ( " %1 (#%2) " ) . arg ( this - > walletName ( ) , QString : : number ( m_wallet - > currentSubaddressAccount ( ) ) ) ;
2021-05-23 13:58:28 +00:00
2023-11-30 14:01:39 +00:00
if ( m_wallet - > viewOnly ( ) ) {
2021-05-02 18:22:38 +00:00
title + = " [view-only] " ;
2023-11-30 14:01:39 +00:00
}
2021-05-02 18:22:38 +00:00
2021-05-23 13:58:28 +00:00
title + = " - Feather " ;
2021-05-02 18:22:38 +00:00
this - > setWindowTitle ( title ) ;
2021-03-24 01:37:54 +00:00
}
2021-05-18 15:59:18 +00:00
void MainWindow : : donationNag ( ) {
2023-03-01 02:05:56 +00:00
if ( m_wallet - > nettype ( ) ! = NetworkType : : Type : : MAINNET )
2021-05-18 15:59:18 +00:00
return ;
2023-03-01 02:05:56 +00:00
if ( m_wallet - > viewOnly ( ) )
2021-05-18 15:59:18 +00:00
return ;
2023-03-01 02:05:56 +00:00
if ( m_wallet - > balanceAll ( ) = = 0 )
2022-06-23 10:41:16 +00:00
return ;
2023-09-12 14:15:40 +00:00
auto donationCounter = conf ( ) - > get ( Config : : donateBeg ) . toInt ( ) ;
2021-05-18 15:59:18 +00:00
if ( donationCounter = = - 1 )
return ;
donationCounter + + ;
if ( donationCounter % constants : : donationBoundary = = 0 ) {
auto msg = " Feather is a 100% community-sponsored endeavor. Please consider supporting "
" the project financially. Get rid of this message by donating any amount. " ;
int ret = QMessageBox : : information ( this , " Donate to Feather " , msg , QMessageBox : : Yes , QMessageBox : : No ) ;
if ( ret = = QMessageBox : : Yes ) {
this - > donateButtonClicked ( ) ;
}
}
2023-09-12 14:15:40 +00:00
conf ( ) - > set ( Config : : donateBeg , donationCounter ) ;
2021-05-18 15:59:18 +00:00
}
2023-01-26 15:05:54 +00:00
void MainWindow : : addToRecentlyOpened ( QString keysFile ) {
2023-09-12 14:15:40 +00:00
auto recent = conf ( ) - > get ( Config : : recentlyOpenedWallets ) . toList ( ) ;
2021-05-18 15:59:18 +00:00
2023-01-26 15:05:54 +00:00
if ( Utils : : isPortableMode ( ) ) {
QDir appPath { Utils : : applicationPath ( ) } ;
keysFile = appPath . relativeFilePath ( keysFile ) ;
}
2021-05-18 15:59:18 +00:00
if ( recent . contains ( keysFile ) ) {
recent . removeOne ( keysFile ) ;
}
recent . insert ( 0 , keysFile ) ;
QList < QVariant > recent_ ;
int count = 0 ;
for ( const auto & file : recent ) {
if ( Utils : : fileExists ( file . toString ( ) ) ) {
recent_ . append ( file ) ;
count + + ;
}
if ( count > = 5 ) {
break ;
}
}
2023-09-12 14:15:40 +00:00
conf ( ) - > set ( Config : : recentlyOpenedWallets , recent_ ) ;
2021-07-08 12:03:54 +00:00
this - > updateRecentlyOpenedMenu ( ) ;
}
void MainWindow : : updateRecentlyOpenedMenu ( ) {
2021-05-18 15:59:18 +00:00
ui - > menuRecently_open - > clear ( ) ;
2023-09-12 14:15:40 +00:00
const QStringList recentWallets = conf ( ) - > get ( Config : : recentlyOpenedWallets ) . toStringList ( ) ;
2021-07-08 12:03:54 +00:00
for ( const auto & walletPath : recentWallets ) {
QFileInfo fileInfo { walletPath } ;
2023-01-26 15:05:54 +00:00
ui - > menuRecently_open - > addAction ( fileInfo . fileName ( ) , m_windowManager , std : : bind ( & WindowManager : : tryOpenWallet , m_windowManager , fileInfo . absoluteFilePath ( ) , " " ) ) ;
2021-05-18 15:59:18 +00:00
}
2021-07-08 12:03:54 +00:00
ui - > menuRecently_open - > addSeparator ( ) ;
ui - > menuRecently_open - > addAction ( m_clearRecentlyOpenAction ) ;
2021-05-18 15:59:18 +00:00
}
2022-07-03 17:41:08 +00:00
bool MainWindow : : verifyPassword ( bool sensitive ) {
2022-05-27 09:49:32 +00:00
bool incorrectPassword = false ;
2021-06-04 18:48:44 +00:00
while ( true ) {
2022-07-03 17:41:08 +00:00
PasswordDialog passwordDialog { this - > walletName ( ) , incorrectPassword , sensitive , this } ;
2022-03-04 16:20:17 +00:00
int ret = passwordDialog . exec ( ) ;
if ( ret = = QDialog : : Rejected ) {
2021-06-04 18:48:44 +00:00
return false ;
}
2023-01-31 16:00:50 +00:00
2023-03-01 02:05:56 +00:00
if ( ! m_wallet - > verifyPassword ( passwordDialog . password ) ) {
2022-05-27 09:49:32 +00:00
incorrectPassword = true ;
2021-06-04 18:48:44 +00:00
continue ;
}
break ;
}
return true ;
}
2022-03-04 16:20:17 +00:00
void MainWindow : : userActivity ( ) {
m_userLastActive = QDateTime : : currentSecsSinceEpoch ( ) ;
}
2023-01-19 14:12:16 +00:00
void MainWindow : : closeQDialogChildren ( QObject * object ) {
for ( QObject * child : object - > children ( ) ) {
if ( auto * childDlg = dynamic_cast < QDialog * > ( child ) ) {
qDebug ( ) < < " Closing dialog: " < < childDlg - > objectName ( ) ;
childDlg - > close ( ) ;
}
this - > closeQDialogChildren ( child ) ;
}
}
2022-03-04 16:20:17 +00:00
void MainWindow : : checkUserActivity ( ) {
2023-09-12 14:15:40 +00:00
if ( ! conf ( ) - > get ( Config : : inactivityLockEnabled ) . toBool ( ) ) {
2022-03-04 16:20:17 +00:00
return ;
}
if ( m_constructingTransaction ) {
return ;
}
2023-09-12 14:15:40 +00:00
if ( ( m_userLastActive + ( conf ( ) - > get ( Config : : inactivityLockTimeout ) . toInt ( ) * 60 ) ) < QDateTime : : currentSecsSinceEpoch ( ) ) {
2022-03-04 16:20:17 +00:00
qInfo ( ) < < " Locking wallet for inactivity " ;
2023-01-19 14:12:16 +00:00
this - > lockWallet ( ) ;
}
}
void MainWindow : : lockWallet ( ) {
if ( m_locked ) {
return ;
2022-03-04 16:20:17 +00:00
}
2023-01-19 14:12:16 +00:00
if ( m_constructingTransaction ) {
2023-09-12 14:15:40 +00:00
Utils : : showError ( this , " Unable to lock wallet " , " Can't lock wallet during transaction construction " ) ;
2023-01-19 14:12:16 +00:00
return ;
}
m_walletUnlockWidget - > reset ( ) ;
// Close all open QDialogs
this - > closeQDialogChildren ( this ) ;
ui - > tabWidget - > hide ( ) ;
this - > statusBar ( ) - > hide ( ) ;
this - > menuBar ( ) - > hide ( ) ;
ui - > stackedWidget - > setCurrentIndex ( 1 ) ;
m_checkUserActivity . stop ( ) ;
m_locked = true ;
}
void MainWindow : : unlockWallet ( const QString & password ) {
if ( ! m_locked ) {
return ;
}
2023-03-01 02:05:56 +00:00
if ( ! m_wallet - > verifyPassword ( password ) ) {
2023-01-19 14:12:16 +00:00
m_walletUnlockWidget - > incorrectPassword ( ) ;
return ;
}
m_walletUnlockWidget - > reset ( ) ;
ui - > tabWidget - > show ( ) ;
this - > statusBar ( ) - > show ( ) ;
this - > menuBar ( ) - > show ( ) ;
ui - > stackedWidget - > setCurrentIndex ( 0 ) ;
2023-12-02 18:28:31 +00:00
this - > onOfflineMode ( conf ( ) - > get ( Config : : offlineMode ) . toBool ( ) ) ;
2023-01-19 14:12:16 +00:00
m_checkUserActivity . start ( ) ;
m_locked = false ;
2022-03-04 16:20:17 +00:00
}
2021-05-23 14:58:18 +00:00
void MainWindow : : toggleSearchbar ( bool visible ) {
2023-09-12 14:15:40 +00:00
conf ( ) - > set ( Config : : showSearchbar , visible ) ;
2021-05-23 14:58:18 +00:00
m_historyWidget - > setSearchbarVisible ( visible ) ;
m_receiveWidget - > setSearchbarVisible ( visible ) ;
m_contactsWidget - > setSearchbarVisible ( visible ) ;
2021-06-28 17:20:16 +00:00
m_coinsWidget - > setSearchbarVisible ( visible ) ;
2021-05-23 14:58:18 +00:00
int currentTab = ui - > tabWidget - > currentIndex ( ) ;
2023-11-30 14:01:39 +00:00
if ( currentTab = = this - > findTab ( " History " ) )
2021-05-23 14:58:18 +00:00
m_historyWidget - > focusSearchbar ( ) ;
2023-11-30 14:01:39 +00:00
else if ( currentTab = = this - > findTab ( " Send " ) )
2021-05-23 14:58:18 +00:00
m_contactsWidget - > focusSearchbar ( ) ;
2023-11-30 14:01:39 +00:00
else if ( currentTab = = this - > findTab ( " Receive " ) )
2021-05-23 14:58:18 +00:00
m_receiveWidget - > focusSearchbar ( ) ;
2023-11-30 14:01:39 +00:00
else if ( currentTab = = this - > findTab ( " Coins " ) )
2021-07-02 14:51:46 +00:00
m_coinsWidget - > focusSearchbar ( ) ;
2021-05-23 14:58:18 +00:00
}
2023-11-30 14:01:39 +00:00
int MainWindow : : findTab ( const QString & title ) {
for ( int i = 0 ; i < ui - > tabWidget - > count ( ) ; i + + ) {
if ( ui - > tabWidget - > tabText ( i ) = = title ) {
return i ;
}
}
return - 1 ;
}
2023-03-01 02:05:56 +00:00
MainWindow : : ~ MainWindow ( ) {
qDebug ( ) < < " ~MainWindow " ;
2023-03-29 09:00:41 +00:00
}