2020-10-07 10:36:04 +00:00
// SPDX-License-Identifier: BSD-3-Clause
2022-02-10 10:26:41 +00:00
// SPDX-FileCopyrightText: 2020-2022 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>
2021-06-28 17:20:16 +00:00
2021-05-02 18:22:38 +00:00
# include "config-feather.h"
2021-06-28 17:48:23 +00:00
# include "constants.h"
# include "dialog/AccountSwitcherDialog.h"
# 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 11:46:32 +00:00
# include "dialog/TorInfoDialog.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"
2021-05-02 18:22:38 +00:00
# include "dialog/UpdateDialog.h"
2020-10-07 10:36:04 +00:00
# include "libwalletqt/AddressBook.h"
2022-03-20 22:30:48 +00:00
# include "libwalletqt/CoinsInfo.h"
2022-03-12 13:54:08 +00:00
# include "libwalletqt/Transfer.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/NetworkManager.h"
2021-05-25 13:06:38 +00:00
# include "utils/os/tails.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/Updater.h"
# include "utils/WebsocketNotifier.h"
2020-10-07 10:36:04 +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 )
, m_ctx ( new AppContext ( wallet ) )
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 ) ;
2020-10-11 17:15:21 +00:00
m_windowCalc = new CalcWindow ( this ) ;
2021-05-02 18:22:38 +00:00
m_splashDialog = new SplashDialog ( this ) ;
2020-10-07 10:36:04 +00:00
this - > restoreGeo ( ) ;
2021-05-02 18:22:38 +00:00
this - > initStatusBar ( ) ;
this - > initWidgets ( ) ;
this - > initMenu ( ) ;
this - > initHome ( ) ;
this - > initWalletContext ( ) ;
// Websocket notifier
connect ( websocketNotifier ( ) , & WebsocketNotifier : : CCSReceived , ui - > ccsWidget - > model ( ) , & CCSModel : : updateEntries ) ;
connect ( websocketNotifier ( ) , & WebsocketNotifier : : RedditReceived , ui - > redditWidget - > model ( ) , & RedditModel : : updatePosts ) ;
2022-05-25 20:40:56 +00:00
connect ( websocketNotifier ( ) , & WebsocketNotifier : : RevuoReceived , ui - > revuoWidget , & RevuoWidget : : updateItems ) ;
2021-05-02 18:22:38 +00:00
connect ( websocketNotifier ( ) , & WebsocketNotifier : : UpdatesReceived , this , & MainWindow : : onUpdatesAvailable ) ;
# ifdef HAS_XMRIG
connect ( websocketNotifier ( ) , & WebsocketNotifier : : XMRigDownloadsReceived , m_xmrig , & XMRigWidget : : onDownloads ) ;
2020-10-07 10:36:04 +00:00
# endif
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 ) ;
this - > onWebsocketStatusChanged ( ! config ( ) - > get ( Config : : disableWebsocket ) . toBool ( ) ) ;
2021-05-18 15:59:18 +00:00
connect ( m_windowManager , & WindowManager : : torSettingsChanged , m_ctx . get ( ) , & AppContext : : onTorSettingsChanged ) ;
connect ( torManager ( ) , & TorManager : : connectionStateChanged , this , & MainWindow : : onTorConnectionStateChanged ) ;
this - > onTorConnectionStateChanged ( torManager ( ) - > torConnected ) ;
2020-10-07 10:36:04 +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
2021-05-26 22:20:48 +00:00
config ( ) - > 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)
// No seperators between statusbar widgets
this - > statusBar ( ) - > setStyleSheet ( " QStatusBar::item {border: None;} " ) ;
# endif
2020-10-07 10:36:04 +00:00
2021-10-28 17:50:18 +00:00
# if defined(Q_OS_MACOS)
this - > patchStylesheetMac ( ) ;
# endif
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 ] ( ) {
this - > onShowSettingsPage ( 2 ) ;
} ) ;
2021-05-02 18:22:38 +00:00
this - > statusBar ( ) - > addPermanentWidget ( m_statusBtnConnectionStatusIndicator ) ;
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
2021-05-26 13:48:53 +00:00
m_statusBtnTor = new StatusBarButton ( icons ( ) - > icon ( " tor_logo_disabled.png " ) , " Tor settings " , this ) ;
2021-05-02 18:22:38 +00:00
connect ( m_statusBtnTor , & StatusBarButton : : clicked , this , & MainWindow : : menuTorClicked ) ;
this - > statusBar ( ) - > addPermanentWidget ( m_statusBtnTor ) ;
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
2021-05-02 18:22:38 +00:00
void MainWindow : : initWidgets ( ) {
int homeWidget = config ( ) - > get ( Config : : homeWidget ) . toInt ( ) ;
ui - > tabHomeWidget - > setCurrentIndex ( TabsHome ( homeWidget ) ) ;
connect ( ui - > tabHomeWidget , & QTabWidget : : currentChanged , [ ] ( int index ) {
config ( ) - > set ( Config : : homeWidget , TabsHome ( index ) ) ;
2020-10-07 10:36:04 +00:00
} ) ;
2021-05-02 18:22:38 +00:00
// [History]
2021-05-18 15:59:18 +00:00
m_historyWidget = new HistoryWidget ( m_ctx , this ) ;
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]
m_sendWidget = new SendWidget ( m_ctx , this ) ;
ui - > sendWidgetLayout - > addWidget ( m_sendWidget ) ;
2021-05-18 15:59:18 +00:00
// --------------
m_contactsWidget = new ContactsWidget ( m_ctx , this ) ;
ui - > contactsWidgetLayout - > addWidget ( m_contactsWidget ) ;
2021-05-10 15:05:10 +00:00
2021-05-02 18:22:38 +00:00
// [Receive]
2021-05-18 15:59:18 +00:00
m_receiveWidget = new ReceiveWidget ( m_ctx , this ) ;
ui - > receiveWidgetLayout - > addWidget ( m_receiveWidget ) ;
connect ( m_receiveWidget , & ReceiveWidget : : showTransactions , [ this ] ( const QString & text ) {
m_historyWidget - > setSearchText ( text ) ;
2020-10-21 11:24:16 +00:00
ui - > tabWidget - > setCurrentIndex ( Tabs : : 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]
m_coinsWidget = new CoinsWidget ( m_ctx , this ) ;
ui - > coinsWidgetLayout - > addWidget ( m_coinsWidget ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
# ifdef HAS_LOCALMONERO
m_localMoneroWidget = new LocalMoneroWidget ( this , m_ctx ) ;
ui - > localMoneroLayout - > addWidget ( m_localMoneroWidget ) ;
# else
ui - > tabWidgetExchanges - > setTabVisible ( 0 , false ) ;
2020-10-22 03:50:59 +00:00
# endif
2021-05-02 18:22:38 +00:00
# ifdef HAS_XMRIG
m_xmrig = new XMRigWidget ( m_ctx , this ) ;
ui - > xmrRigLayout - > addWidget ( m_xmrig ) ;
2020-11-23 16:57:38 +00:00
2021-05-23 13:58:28 +00:00
connect ( m_xmrig , & XMRigWidget : : miningStarted , [ this ] { this - > updateTitle ( ) ; } ) ;
connect ( m_xmrig , & XMRigWidget : : miningEnded , [ this ] { this - > updateTitle ( ) ; } ) ;
2021-05-02 18:22:38 +00:00
# else
ui - > tabWidget - > setTabVisible ( Tabs : : XMRIG , false ) ;
# endif
2021-07-07 17:06:31 +00:00
# if defined(Q_OS_MACOS)
ui - > line - > hide ( ) ;
# endif
2022-03-20 22:30:48 +00:00
ui - > frame_coinControl - > setVisible ( false ) ;
connect ( ui - > btn_resetCoinControl , & QPushButton : : clicked , [ this ] {
m_ctx - > setSelectedInputs ( { } ) ;
} ) ;
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 ) ;
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 ) ;
2021-05-02 18:22:38 +00:00
connect ( ui - > actionUpdate_balance , & QAction : : triggered , [ this ] { m_ctx - > updateBalance ( ) ; } ) ;
connect ( ui - > actionRefresh_tabs , & QAction : : triggered , [ this ] { m_ctx - > refreshModels ( ) ; } ) ;
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] -> [Advanced] -> [Export]
connect ( ui - > actionExportOutputs , & QAction : : triggered , this , & MainWindow : : exportOutputs ) ;
connect ( ui - > actionExportKeyImages , & QAction : : triggered , this , & MainWindow : : exportKeyImages ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
// [Wallet] -> [Advanced] -> [Import]
connect ( ui - > actionImportOutputs , & QAction : : triggered , this , & MainWindow : : importOutputs ) ;
connect ( ui - > actionImportKeyImages , & QAction : : triggered , this , & MainWindow : : importKeyImages ) ;
2021-03-24 01:37:54 +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 ) ;
ui - > actionShow_Searchbar - > setChecked ( config ( ) - > get ( Config : : showSearchbar ) . toBool ( ) ) ;
2020-10-07 10:36:04 +00:00
2021-05-02 18:22:38 +00:00
// Show/Hide Home
2020-12-30 04:45:00 +00:00
connect ( ui - > actionShow_Home , & QAction : : triggered , m_tabShowHideSignalMapper , QOverload < > : : of ( & QSignalMapper : : map ) ) ;
m_tabShowHideMapper [ " Home " ] = new ToggleTab ( ui - > tabHome , " Home " , " Home " , ui - > actionShow_Home , Config : : showTabHome ) ;
m_tabShowHideSignalMapper - > setMapping ( ui - > actionShow_Home , " Home " ) ;
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 ) ) ;
m_tabShowHideMapper [ " Coins " ] = new ToggleTab ( ui - > tabCoins , " Coins " , " Coins " , ui - > actionShow_Coins , Config : : showTabCoins ) ;
m_tabShowHideSignalMapper - > setMapping ( ui - > actionShow_Coins , " Coins " ) ;
2020-10-21 11:24:16 +00:00
2021-05-02 18:22:38 +00:00
// Show/Hide Calc
2020-10-21 11:24:16 +00:00
connect ( ui - > actionShow_calc , & QAction : : triggered , m_tabShowHideSignalMapper , QOverload < > : : of ( & QSignalMapper : : map ) ) ;
m_tabShowHideMapper [ " Calc " ] = new ToggleTab ( ui - > tabCalc , " Calc " , " Calc " , ui - > actionShow_calc , Config : : showTabCalc ) ;
m_tabShowHideSignalMapper - > setMapping ( ui - > actionShow_calc , " Calc " ) ;
2021-05-02 18:22:38 +00:00
// Show/Hide Exchange
# if defined(HAS_LOCALMONERO)
connect ( ui - > actionShow_Exchange , & QAction : : triggered , m_tabShowHideSignalMapper , QOverload < > : : of ( & QSignalMapper : : map ) ) ;
m_tabShowHideMapper [ " Exchange " ] = new ToggleTab ( ui - > tabExchange , " Exchange " , " Exchange " , ui - > actionShow_Exchange , Config : : showTabExchange ) ;
m_tabShowHideSignalMapper - > setMapping ( ui - > actionShow_Exchange , " Exchange " ) ;
# else
2021-01-04 12:22:49 +00:00
ui - > actionShow_Exchange - > setVisible ( false ) ;
2021-02-01 16:53:08 +00:00
ui - > tabWidget - > setTabVisible ( Tabs : : EXCHANGES , false ) ;
2021-05-02 18:22:38 +00:00
# endif
2020-12-25 22:18:40 +00:00
2021-05-02 18:22:38 +00:00
// Show/Hide Mining
2020-10-17 21:14:56 +00:00
# if defined(HAS_XMRIG)
2020-10-14 20:12:32 +00:00
connect ( ui - > actionShow_XMRig , & QAction : : triggered , m_tabShowHideSignalMapper , QOverload < > : : of ( & QSignalMapper : : map ) ) ;
2020-12-12 14:22:31 +00:00
m_tabShowHideMapper [ " Mining " ] = new ToggleTab ( ui - > tabXmrRig , " Mining " , " Mining " , ui - > actionShow_XMRig , Config : : showTabXMRig ) ;
m_tabShowHideSignalMapper - > setMapping ( ui - > actionShow_XMRig , " Mining " ) ;
2020-10-14 20:12:32 +00:00
# else
ui - > actionShow_XMRig - > setVisible ( false ) ;
# endif
2020-10-07 10:36:04 +00:00
for ( const auto & key : m_tabShowHideMapper . keys ( ) ) {
const auto toggleTab = m_tabShowHideMapper . value ( key ) ;
const bool show = config ( ) - > get ( toggleTab - > configKey ) . toBool ( ) ;
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 ) ;
connect ( ui - > actionLoadUnsignedTxFromFile , & QAction : : triggered , this , & MainWindow : : loadUnsignedTx ) ;
connect ( ui - > actionLoadUnsignedTxFromClipboard , & QAction : : triggered , this , & MainWindow : : loadUnsignedTxFromClipboard ) ;
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 ) ;
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 - > actionCalculator , & QAction : : triggered , this , & MainWindow : : showCalcWindow ) ;
connect ( ui - > actionCreateDesktopEntry , & QAction : : triggered , this , & MainWindow : : onCreateDesktopEntry ) ;
// 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 ) ;
connect ( ui - > actionOfficialWebsite , & QAction : : triggered , [ this ] ( ) { Utils : : externalLinkWarning ( this , " https://featherwallet.org " ) ; } ) ;
connect ( ui - > actionDonate_to_Feather , & QAction : : triggered , this , & MainWindow : : donateButtonClicked ) ;
2021-10-21 21:13:25 +00:00
connect ( ui - > actionDocumentation , & QAction : : triggered , this , & MainWindow : : onShowDocumentaton ) ;
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 " ) ) ;
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
2021-05-02 18:22:38 +00:00
void MainWindow : : initHome ( ) {
// Ticker widgets
2022-06-10 11:42:55 +00:00
m_tickerWidgets . append ( new PriceTickerWidget ( this , m_ctx , " XMR " ) ) ;
m_tickerWidgets . append ( new PriceTickerWidget ( this , m_ctx , " BTC " ) ) ;
m_tickerWidgets . append ( new RatioTickerWidget ( this , m_ctx , " XMR " , " BTC " ) ) ;
for ( const auto & widget : m_tickerWidgets ) {
2021-05-24 23:10:45 +00:00
ui - > tickerLayout - > addWidget ( widget ) ;
2021-05-02 18:22:38 +00:00
}
2021-05-24 23:10:45 +00:00
m_balanceTickerWidget = new BalanceTickerWidget ( this , m_ctx , false ) ;
ui - > fiatTickerLayout - > addWidget ( m_balanceTickerWidget ) ;
2021-05-02 18:22:38 +00:00
connect ( ui - > ccsWidget , & CCSWidget : : selected , this , & MainWindow : : showSendScreen ) ;
connect ( ui - > redditWidget , & RedditWidget : : setStatusText , this , & MainWindow : : setStatusText ) ;
2022-05-25 20:40:56 +00:00
connect ( ui - > revuoWidget , & RevuoWidget : : donate , [ this ] ( const QString & address , const QString & description ) {
m_sendWidget - > fill ( address , description ) ;
ui - > tabWidget - > setCurrentIndex ( Tabs : : SEND ) ;
} ) ;
2021-05-02 18:22:38 +00:00
}
void MainWindow : : initWalletContext ( ) {
2021-05-18 15:59:18 +00:00
connect ( m_ctx . get ( ) , & AppContext : : balanceUpdated , this , & MainWindow : : onBalanceUpdated ) ;
connect ( m_ctx . get ( ) , & AppContext : : synchronized , this , & MainWindow : : onSynchronized ) ;
connect ( m_ctx . get ( ) , & AppContext : : blockchainSync , this , & MainWindow : : onBlockchainSync ) ;
connect ( m_ctx . get ( ) , & AppContext : : refreshSync , this , & MainWindow : : onRefreshSync ) ;
connect ( m_ctx . get ( ) , & AppContext : : createTransactionError , this , & MainWindow : : onCreateTransactionError ) ;
connect ( m_ctx . get ( ) , & AppContext : : createTransactionSuccess , this , & MainWindow : : onCreateTransactionSuccess ) ;
connect ( m_ctx . get ( ) , & AppContext : : transactionCommitted , this , & MainWindow : : onTransactionCommitted ) ;
connect ( m_ctx . get ( ) , & AppContext : : deviceError , this , & MainWindow : : onDeviceError ) ;
2021-07-01 21:00:47 +00:00
connect ( m_ctx . get ( ) , & AppContext : : deviceButtonRequest , this , & MainWindow : : onDeviceButtonRequest ) ;
connect ( m_ctx . get ( ) , & AppContext : : deviceButtonPressed , this , & MainWindow : : onDeviceButtonPressed ) ;
2021-05-18 15:59:18 +00:00
connect ( m_ctx . get ( ) , & AppContext : : initiateTransaction , this , & MainWindow : : onInitiateTransaction ) ;
connect ( m_ctx . get ( ) , & AppContext : : endTransaction , this , & MainWindow : : onEndTransaction ) ;
2022-03-12 12:53:46 +00:00
connect ( m_ctx . get ( ) , & AppContext : : keysCorrupted , this , & MainWindow : : onKeysCorrupted ) ;
2022-03-20 22:30:48 +00:00
connect ( m_ctx . get ( ) , & AppContext : : selectedInputsChanged , this , & MainWindow : : onSelectedInputsChanged ) ;
2021-05-02 18:22:38 +00:00
// Nodes
connect ( m_ctx - > nodes , & Nodes : : nodeExhausted , this , & MainWindow : : showNodeExhaustedMessage ) ;
connect ( m_ctx - > nodes , & Nodes : : WSNodeExhausted , this , & MainWindow : : showWSNodeExhaustedMessage ) ;
2021-05-18 15:59:18 +00:00
// Wallet
2021-07-03 01:29:13 +00:00
connect ( m_ctx - > wallet , & Wallet : : connectionStatusChanged , this , & MainWindow : : onConnectionStatusChanged ) ;
connect ( m_ctx - > wallet , & Wallet : : currentSubaddressAccountChanged , this , & MainWindow : : updateTitle ) ;
2021-07-08 00:34:27 +00:00
connect ( m_ctx - > wallet , & Wallet : : walletPassphraseNeeded , this , & MainWindow : : onWalletPassphraseNeeded ) ;
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 ] ;
bool show = config ( ) - > get ( toggleTab - > configKey ) . toBool ( ) ;
show = ! show ;
config ( ) - > set ( toggleTab - > configKey , show ) ;
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 ( ) {
config ( ) - > remove ( Config : : recentlyOpenedWallets ) ;
this - > updateRecentlyOpenedMenu ( ) ;
}
2021-05-18 15:59:18 +00:00
QString MainWindow : : walletName ( ) {
return QFileInfo ( m_ctx - > wallet - > cachePath ( ) ) . fileName ( ) ;
2020-10-07 10:36:04 +00:00
}
2021-05-18 15:59:18 +00:00
QString MainWindow : : walletCachePath ( ) {
return m_ctx - > wallet - > cachePath ( ) ;
2020-10-07 10:36:04 +00:00
}
2021-05-18 15:59:18 +00:00
QString MainWindow : : walletKeysPath ( ) {
return m_ctx - > wallet - > keysPath ( ) ;
2020-10-07 10:36:04 +00:00
}
2021-05-02 18:22:38 +00:00
void MainWindow : : displayWalletErrorMsg ( const QString & err ) {
QString errMsg = err ;
if ( err . contains ( " No device found " ) ) {
errMsg + = " \n \n This wallet is backed by a hardware device. Make sure the Monero app is opened on the device. \n "
" You may need to restart Feather before the device can get detected. " ;
}
if ( errMsg . contains ( " Unable to open device " ) ) {
errMsg + = " \n \n The device might be in use by a different application. " ;
}
if ( errMsg . contains ( " SW_CLIENT_NOT_SUPPORTED " ) ) {
errMsg + = " \n \n Incompatible version: you may need to upgrade the Monero app on the Ledger device to the latest version. " ;
}
else if ( errMsg . contains ( " Wrong Device Status " ) ) {
2021-05-04 23:09:19 +00:00
errMsg + = " \n \n The device may need to be unlocked. " ;
}
else if ( errMsg . contains ( " Wrong Channel " ) ) {
errMsg + = " \n \n Restart the hardware device and try again. " ;
2021-05-02 18:22:38 +00:00
}
QMessageBox : : warning ( this , " Wallet error " , errMsg ) ;
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 ( ) ;
2021-05-26 23:51:10 +00:00
m_ctx - > updateBalance ( ) ;
2021-05-18 15:59:18 +00:00
if ( m_ctx - > 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
2021-05-22 13:42:26 +00:00
m_ctx - > wallet - > subaddress ( ) - > refresh ( m_ctx - > wallet - > currentSubaddressAccount ( ) ) ;
2021-05-18 15:59:18 +00:00
if ( m_ctx - > wallet - > subaddress ( ) - > count ( ) = = 1 ) {
2020-10-07 10:36:04 +00:00
for ( int i = 0 ; i < 10 ; i + + ) {
2021-05-18 15:59:18 +00:00
m_ctx - > wallet - > subaddress ( ) - > addRow ( m_ctx - > wallet - > currentSubaddressAccount ( ) , " " ) ;
2020-10-07 10:36:04 +00:00
}
}
2021-05-22 13:42:26 +00:00
m_ctx - > wallet - > subaddressModel ( ) - > setCurrentSubaddressAcount ( m_ctx - > wallet - > currentSubaddressAccount ( ) ) ;
2020-10-07 10:36:04 +00:00
// history page
2021-05-18 15:59:18 +00:00
m_ctx - > wallet - > history ( ) - > refresh ( m_ctx - > wallet - > currentSubaddressAccount ( ) ) ;
2020-10-07 10:36:04 +00:00
// coins page
2021-05-18 15:59:18 +00:00
m_ctx - > wallet - > coins ( ) - > refresh ( m_ctx - > wallet - > currentSubaddressAccount ( ) ) ;
m_coinsWidget - > setModel ( m_ctx - > wallet - > coinsModel ( ) , m_ctx - > wallet - > coins ( ) ) ;
2021-07-02 14:12:07 +00:00
m_ctx - > wallet - > coinsModel ( ) - > setCurrentSubaddressAccount ( m_ctx - > wallet - > currentSubaddressAccount ( ) ) ;
// Coin labeling uses set_tx_note, so we need to refresh history too
connect ( m_ctx - > wallet - > coins ( ) , & Coins : : descriptionChanged , [ this ] {
m_ctx - > wallet - > history ( ) - > refresh ( m_ctx - > wallet - > currentSubaddressAccount ( ) ) ;
} ) ;
// Vice versa
connect ( m_ctx - > wallet - > history ( ) , & TransactionHistory : : txNoteChanged , [ this ] {
m_ctx - > wallet - > coins ( ) - > refresh ( m_ctx - > wallet - > currentSubaddressAccount ( ) ) ;
} ) ;
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 ( ) ;
2021-05-18 15:59:18 +00:00
m_ctx - > nodes - > connectToNode ( ) ;
2021-05-02 18:22:38 +00:00
m_updateBytes . start ( 250 ) ;
2021-05-18 15:59:18 +00:00
2021-07-08 12:03:54 +00:00
this - > addToRecentlyOpened ( m_ctx - > wallet - > cachePath ( ) ) ;
2020-10-07 10:36:04 +00:00
}
2020-12-25 14:20:39 +00:00
void MainWindow : : onBalanceUpdated ( quint64 balance , quint64 spendable ) {
2020-11-02 09:37:36 +00:00
bool hide = config ( ) - > get ( Config : : hideBalance ) . toBool ( ) ;
2021-05-22 14:12:39 +00:00
int displaySetting = config ( ) - > get ( Config : : balanceDisplay ) . toInt ( ) ;
2021-05-25 17:11:45 +00:00
int decimals = config ( ) - > 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 ) ;
2021-05-24 23:10:45 +00:00
m_balanceTickerWidget - > setHidden ( hide ) ;
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 ( ) {
if ( m_ctx - > wallet - > connectionStatus ( ) = = Wallet : : ConnectionStatus : : ConnectionStatus_Synchronizing ) {
QMessageBox : : warning ( this , " Save wallet " , " Unable to save wallet during synchronization. \n \n "
" Wait until synchronization is finished and try again. " ) ;
return ;
}
m_ctx - > wallet - > store ( ) ;
}
2022-03-15 12:36:20 +00:00
void MainWindow : : onWebsocketStatusChanged ( bool enabled ) {
ui - > actionShow_Home - > setVisible ( enabled ) ;
ui - > actionShow_calc - > setVisible ( enabled ) ;
ui - > actionShow_Exchange - > setVisible ( enabled ) ;
ui - > tabWidget - > setTabVisible ( Tabs : : HOME , enabled & & config ( ) - > get ( Config : : showTabHome ) . toBool ( ) ) ;
ui - > tabWidget - > setTabVisible ( Tabs : : CALC , enabled & & config ( ) - > get ( Config : : showTabCalc ) . toBool ( ) ) ;
ui - > tabWidget - > setTabVisible ( Tabs : : EXCHANGES , enabled & & config ( ) - > get ( Config : : showTabExchange ) . toBool ( ) ) ;
# ifdef HAS_XMRIG
m_xmrig - > setDownloadsTabEnabled ( enabled ) ;
# endif
}
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 ;
2020-10-07 10:36:04 +00:00
switch ( status ) {
case Wallet : : ConnectionStatus_Disconnected :
2021-05-02 18:22:38 +00:00
icon = icons ( ) - > icon ( " status_disconnected.svg " ) ;
2020-12-24 14:46:56 +00:00
this - > setStatusText ( " Disconnected " ) ;
2020-10-07 10:36:04 +00:00
break ;
case Wallet : : ConnectionStatus_Connecting :
2021-05-02 18:22:38 +00:00
icon = icons ( ) - > icon ( " status_lagging.svg " ) ;
2021-03-24 01:37:54 +00:00
this - > setStatusText ( " Connecting to node " ) ;
2020-10-07 10:36:04 +00:00
break ;
case Wallet : : ConnectionStatus_WrongVersion :
2021-05-02 18:22:38 +00:00
icon = icons ( ) - > icon ( " status_disconnected.svg " ) ;
2021-03-24 01:37:54 +00:00
this - > setStatusText ( " Incompatible node " ) ;
break ;
case Wallet : : ConnectionStatus_Synchronizing :
2021-05-02 18:22:38 +00:00
icon = icons ( ) - > icon ( " status_waiting.svg " ) ;
2021-03-24 01:37:54 +00:00
break ;
case Wallet : : ConnectionStatus_Synchronized :
2021-05-02 18:22:38 +00:00
icon = icons ( ) - > icon ( " status_connected.svg " ) ;
2020-10-07 10:36:04 +00:00
break ;
default :
2021-05-02 18:22:38 +00:00
icon = icons ( ) - > icon ( " status_disconnected.svg " ) ;
2020-10-07 10:36:04 +00:00
break ;
}
2021-05-02 18:22:38 +00:00
m_statusBtnConnectionStatusIndicator - > setIcon ( icon ) ;
2020-10-07 10:36:04 +00:00
}
2021-01-26 23:55:27 +00:00
void MainWindow : : onCreateTransactionSuccess ( PendingTransaction * tx , const QVector < QString > & address ) {
2021-05-22 18:13:48 +00:00
QString err { " Can't create transaction: " } ;
2021-08-19 18:22:53 +00:00
if ( tx - > status ( ) ! = PendingTransaction : : Status_Ok ) {
2021-05-22 18:13:48 +00:00
QString tx_err = tx - > errorString ( ) ;
2020-10-07 10:36:04 +00:00
qCritical ( ) < < tx_err ;
2021-05-18 15:59:18 +00:00
if ( m_ctx - > wallet - > connectionStatus ( ) = = Wallet : : ConnectionStatus_WrongVersion )
2022-02-11 11:50:09 +00:00
err = QString ( " %1 Wrong node version: %2 " ) . arg ( err , tx_err ) ;
2020-10-07 10:36:04 +00:00
else
2022-02-11 11:50:09 +00:00
err = QString ( " %1 %2 " ) . arg ( err , tx_err ) ;
2020-10-07 10:36:04 +00:00
2021-08-14 14:57:39 +00:00
if ( tx_err . contains ( " Node response did not include the requested real output " ) ) {
2021-05-22 18:13:48 +00:00
QString currentNode = m_ctx - > nodes - > connection ( ) . toAddress ( ) ;
err + = 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 dev@featherwallet.org, #feather on OFTC or /r/FeatherWallet. " ) . arg ( currentNode ) ;
}
2020-10-07 10:36:04 +00:00
qDebug ( ) < < Q_FUNC_INFO < < err ;
2021-05-04 23:09:19 +00:00
this - > displayWalletErrorMsg ( err ) ;
2021-05-18 15:59:18 +00:00
m_ctx - > 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 ) {
2022-02-11 11:50:09 +00:00
err = QString ( " %1 %2 " ) . arg ( err , " No unmixable outputs to sweep. " ) ;
2020-10-07 10:36:04 +00:00
qDebug ( ) < < Q_FUNC_INFO < < err ;
2021-05-04 23:09:19 +00:00
this - > displayWalletErrorMsg ( err ) ;
2021-05-18 15:59:18 +00:00
m_ctx - > 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 ) {
err = QString ( " %1 %2 " ) . arg ( err , " Split transactions are not supported. Try sending a smaller amount. " ) ;
qDebug ( ) < < Q_FUNC_INFO < < err ;
this - > displayWalletErrorMsg ( err ) ;
m_ctx - > wallet - > disposeTransaction ( tx ) ;
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 ( ) ) {
outputAddresses . insert ( output - > address ( ) ) ;
}
QSet < QString > destAddresses ;
for ( const auto & addr : address ) {
destAddresses . insert ( addr ) ;
}
if ( ! outputAddresses . contains ( destAddresses ) ) {
err = QString ( " %1 %2 " ) . arg ( err , " Constructed transaction doesn't appear to send to (all) specified destination address(es). Try creating the transaction again. " ) ;
qDebug ( ) < < Q_FUNC_INFO < < err ;
this - > displayWalletErrorMsg ( err ) ;
m_ctx - > wallet - > disposeTransaction ( tx ) ;
return ;
}
2021-01-26 23:55:27 +00:00
2021-08-19 18:22:53 +00:00
m_ctx - > addCacheTransaction ( tx - > txid ( ) [ 0 ] , tx - > signedTxToHex ( 0 ) ) ;
// Show advanced dialog on multi-destination transactions
2021-10-13 20:31:47 +00:00
if ( address . size ( ) > 1 | | m_ctx - > wallet - > viewOnly ( ) ) {
2021-08-19 18:22:53 +00:00
TxConfAdvDialog dialog_adv { m_ctx , m_ctx - > tmpTxDescription , this } ;
2021-10-13 21:02:21 +00:00
dialog_adv . setTransaction ( tx , ! m_ctx - > wallet - > viewOnly ( ) ) ;
2021-08-19 18:22:53 +00:00
dialog_adv . exec ( ) ;
return ;
}
2020-10-16 03:05:05 +00:00
2021-08-19 18:22:53 +00:00
TxConfDialog dialog { m_ctx , tx , address [ 0 ] , m_ctx - > tmpTxDescription , this } ;
switch ( dialog . exec ( ) ) {
case QDialog : : Rejected :
{
if ( ! dialog . showAdvanced )
m_ctx - > onCancelTransaction ( tx , address ) ;
break ;
2020-10-16 03:05:05 +00:00
}
2021-08-19 18:22:53 +00:00
case QDialog : : Accepted :
2021-10-22 23:46:08 +00:00
m_ctx - > commitTransaction ( tx , m_ctx - > tmpTxDescription ) ;
2021-08-19 18:22:53 +00:00
break ;
}
if ( dialog . showAdvanced ) {
TxConfAdvDialog dialog_adv { m_ctx , m_ctx - > tmpTxDescription , this } ;
dialog_adv . setTransaction ( tx ) ;
dialog_adv . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
}
void MainWindow : : onTransactionCommitted ( bool status , PendingTransaction * tx , const QStringList & txid ) {
2021-03-08 20:03:20 +00:00
if ( status ) { // success
2021-07-03 15:48:07 +00:00
QMessageBox msgBox { this } ;
QPushButton * showDetailsButton = msgBox . addButton ( " Show details " , QMessageBox : : ActionRole ) ;
msgBox . addButton ( QMessageBox : : Ok ) ;
2020-10-07 10:36:04 +00:00
QString body = QString ( " Successfully sent %1 transaction(s). " ) . arg ( txid . count ( ) ) ;
2021-07-03 15:48:07 +00:00
msgBox . setText ( body ) ;
msgBox . setWindowTitle ( " Transaction sent " ) ;
msgBox . setIcon ( QMessageBox : : Icon : : Information ) ;
msgBox . exec ( ) ;
if ( msgBox . clickedButton ( ) = = showDetailsButton ) {
this - > showHistoryTab ( ) ;
TransactionInfo * txInfo = m_ctx - > wallet - > history ( ) - > transaction ( txid . first ( ) ) ;
2021-10-21 17:56:40 +00:00
auto * dialog = new TxInfoDialog ( m_ctx , txInfo , this ) ;
connect ( dialog , & TxInfoDialog : : resendTranscation , this , & MainWindow : : onResendTransaction ) ;
dialog - > show ( ) ;
dialog - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
2021-07-03 15:48:07 +00:00
}
2021-05-10 15:05:10 +00:00
m_sendWidget - > clearFields ( ) ;
2020-10-07 10:36:04 +00:00
} else {
auto err = tx - > errorString ( ) ;
QString body = QString ( " Error committing transaction: %1 " ) . arg ( err ) ;
QMessageBox : : warning ( this , " Transaction failed " , body ) ;
}
}
void MainWindow : : onCreateTransactionError ( const QString & message ) {
auto msg = QString ( " Error while creating transaction: %1 " ) . arg ( message ) ;
if ( msg . contains ( " failed to get random outs " ) ) {
msg + = " \n \n Your transaction has too many inputs. Try sending a lower amount. " ;
}
QMessageBox : : warning ( this , " Transaction failed " , msg ) ;
}
void MainWindow : : showWalletInfoDialog ( ) {
2021-05-18 15:59:18 +00:00
WalletInfoDialog dialog { m_ctx , this } ;
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : showSeedDialog ( ) {
2021-05-18 15:59:18 +00:00
if ( m_ctx - > wallet - > isHwBacked ( ) ) {
2021-05-04 23:09:19 +00:00
QMessageBox : : information ( this , " Information " , " Seed unavailable: Wallet keys are stored on hardware device. " ) ;
2021-05-02 18:22:38 +00:00
return ;
}
2021-05-18 15:59:18 +00:00
if ( m_ctx - > wallet - > viewOnly ( ) ) {
2021-03-14 21:12:02 +00:00
QMessageBox : : information ( this , " Information " , " Wallet is view-only and has no seed. \n \n To obtain wallet keys go to Wallet -> View-Only " ) ;
return ;
}
2021-05-18 15:59:18 +00:00
if ( ! m_ctx - > wallet - > isDeterministic ( ) ) {
2021-03-14 21:12:02 +00:00
QMessageBox : : information ( this , " Information " , " Wallet is non-deterministic and has no seed. \n \n To obtain wallet keys go to Wallet -> Keys " ) ;
return ;
}
2021-06-04 18:48:44 +00:00
if ( ! this - > verifyPassword ( ) ) {
return ;
}
2021-05-18 15:59:18 +00:00
SeedDialog dialog { m_ctx , this } ;
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : showPasswordDialog ( ) {
2021-07-03 01:29:13 +00:00
PasswordChangeDialog dialog { this , m_ctx - > wallet } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-11-14 09:57:06 +00:00
this - > updatePasswordIcon ( ) ;
}
void MainWindow : : updatePasswordIcon ( ) {
2021-05-18 15:59:18 +00:00
QIcon icon = m_ctx - > wallet - > getPassword ( ) . isEmpty ( ) ? 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 ;
}
2021-05-18 15:59:18 +00:00
KeysDialog dialog { m_ctx , this } ;
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
2020-10-12 22:01:06 +00:00
void MainWindow : : showViewOnlyDialog ( ) {
2021-05-18 15:59:18 +00:00
ViewOnlyDialog dialog { m_ctx , this } ;
dialog . exec ( ) ;
2020-10-12 22:01:06 +00:00
}
2020-10-07 10:36:04 +00:00
void MainWindow : : menuTorClicked ( ) {
2021-05-18 15:59:18 +00:00
auto * dialog = new TorInfoDialog ( m_ctx , this ) ;
connect ( dialog , & TorInfoDialog : : torSettingsChanged , m_windowManager , & WindowManager : : onTorSettingsChanged ) ;
2020-10-07 10:36:04 +00:00
dialog - > exec ( ) ;
dialog - > deleteLater ( ) ;
}
2021-05-02 18:22:38 +00:00
void MainWindow : : menuHwDeviceClicked ( ) {
QMessageBox : : information ( this , " Hardware Device " , QString ( " This wallet is backed by a %1 hardware device. " ) . arg ( this - > getHardwareDevice ( ) ) ) ;
}
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
}
void MainWindow : : menuAboutClicked ( ) {
2021-05-02 18:22:38 +00:00
AboutDialog dialog { this } ;
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : menuSettingsClicked ( ) {
2021-10-22 11:05:40 +00:00
Settings settings { m_ctx , this } ;
2022-06-10 11:42:55 +00:00
for ( const auto & widget : m_tickerWidgets ) {
connect ( & settings , & Settings : : preferredFiatCurrencyChanged , widget , & TickerWidgetBase : : updateDisplay ) ;
2021-10-22 11:05:40 +00:00
}
connect ( & settings , & Settings : : preferredFiatCurrencyChanged , m_balanceTickerWidget , & BalanceTickerWidget : : updateDisplay ) ;
connect ( & settings , & Settings : : preferredFiatCurrencyChanged , m_sendWidget , QOverload < > : : of ( & SendWidget : : onPreferredFiatCurrencyChanged ) ) ;
connect ( & settings , & Settings : : skinChanged , this , & MainWindow : : skinChanged ) ;
2022-03-15 12:36:20 +00:00
connect ( & settings , & Settings : : websocketStatusChanged , m_windowManager , & WindowManager : : onWebsocketStatusChanged ) ;
2021-10-22 11:05:40 +00:00
settings . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : menuSignVerifyClicked ( ) {
2021-07-03 01:29:13 +00:00
SignVerifyDialog dialog { m_ctx - > wallet , this } ;
2021-05-02 18:22:38 +00:00
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : menuVerifyTxProof ( ) {
2021-07-03 01:29:13 +00:00
VerifyProofDialog dialog { m_ctx - > 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 ) {
config ( ) - > set ( Config : : lastSettingsPage , page ) ;
this - > menuSettingsClicked ( ) ;
}
2020-10-07 10:36:04 +00:00
void MainWindow : : skinChanged ( const QString & skinName ) {
2021-05-18 15:59:18 +00:00
m_windowManager - > changeSkin ( skinName ) ;
2021-01-28 22:48:14 +00:00
ColorScheme : : updateFromWidget ( this ) ;
2021-06-25 14:14:49 +00:00
this - > updateWidgetIcons ( ) ;
2021-10-28 17:50:18 +00:00
# if defined(Q_OS_MACOS)
this - > patchStylesheetMac ( ) ;
# endif
2021-06-25 14:14:49 +00:00
}
2021-02-03 19:40:35 +00:00
2021-06-25 14:14:49 +00:00
void MainWindow : : updateWidgetIcons ( ) {
m_sendWidget - > skinChanged ( ) ;
2021-05-02 18:22:38 +00:00
# ifdef HAS_LOCALMONERO
m_localMoneroWidget - > skinChanged ( ) ;
# endif
2021-02-03 19:40:35 +00:00
ui - > conversionWidget - > skinChanged ( ) ;
2022-05-25 20:40:56 +00:00
ui - > revuoWidget - > skinChanged ( ) ;
2021-07-01 21:00:47 +00:00
m_statusBtnHwDevice - > setIcon ( this - > hardwareDevicePairedIcon ( ) ) ;
}
QIcon MainWindow : : hardwareDevicePairedIcon ( ) {
QString filename ;
if ( m_ctx - > wallet - > isLedger ( ) )
filename = " ledger.png " ;
else if ( m_ctx - > wallet - > isTrezor ( ) )
filename = ColorScheme : : darkScheme ? " trezor_white.png " : " trezor.png " ;
return icons ( ) - > icon ( filename ) ;
}
QIcon MainWindow : : hardwareDeviceUnpairedIcon ( ) {
QString filename ;
if ( m_ctx - > wallet - > isLedger ( ) )
filename = " ledger_unpaired.png " ;
else if ( m_ctx - > wallet - > isTrezor ( ) )
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 ;
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
m_ctx - > stopTimers ( ) ;
// Wallet signal may fire after AppContext is gone, causing segv
m_ctx - > wallet - > 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
}
void MainWindow : : donateButtonClicked ( ) {
2022-02-23 23:44:13 +00:00
m_sendWidget - > fill ( constants : : donationAddress , " Donation to the Feather development team " ) ;
2020-10-21 11:24:16 +00:00
ui - > tabWidget - > setCurrentIndex ( Tabs : : SEND ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : showHistoryTab ( ) {
this - > raise ( ) ;
2020-10-21 11:24:16 +00:00
ui - > tabWidget - > setCurrentIndex ( Tabs : : HISTORY ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : showSendTab ( ) {
this - > raise ( ) ;
2020-10-21 11:24:16 +00:00
ui - > tabWidget - > setCurrentIndex ( Tabs : : SEND ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : showCalcWindow ( ) {
m_windowCalc - > show ( ) ;
}
2021-01-26 23:55:27 +00:00
void MainWindow : : payToMany ( ) {
ui - > tabWidget - > setCurrentIndex ( Tabs : : SEND ) ;
2021-05-10 15:05:10 +00:00
m_sendWidget - > payToMany ( ) ;
2021-01-26 23:55:27 +00:00
QMessageBox : : information ( 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-07-02 16:01:11 +00:00
void MainWindow : : showSendScreen ( const CCSEntry & entry ) { // TODO: rename this function
m_sendWidget - > fill ( entry . address , QString ( " CCS: %1 " ) . arg ( entry . title ) ) ;
2020-10-21 11:24:16 +00:00
ui - > tabWidget - > setCurrentIndex ( Tabs : : SEND ) ;
2020-10-07 10:36:04 +00:00
}
void MainWindow : : onViewOnBlockExplorer ( const QString & txid ) {
2021-05-18 15:59:18 +00:00
QString blockExplorerLink = Utils : : blockExplorerLink ( config ( ) - > 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 ) {
2021-07-04 21:17:10 +00:00
QString txHex = m_ctx - > getCacheTransaction ( txid ) ;
if ( txHex . isEmpty ( ) ) {
2020-12-14 22:07:23 +00:00
QMessageBox : : warning ( this , " Unable to resend transaction " , " Transaction was not found in transaction cache. Unable to resend. " ) ;
return ;
}
// Connect to a different node so chances of successful relay are higher
m_ctx - > nodes - > autoConnect ( true ) ;
2021-07-04 21:17:10 +00:00
TxBroadcastDialog dialog { this , m_ctx , 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 ;
2021-05-18 15:59:18 +00:00
auto * model = m_ctx - > 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 ( ) ;
2021-05-18 15:59:18 +00:00
bool addressValid = WalletManager : : addressValid ( i . value ( ) , m_ctx - > wallet - > nettype ( ) ) ;
2020-10-21 06:25:02 +00:00
if ( addressValid ) {
2021-05-18 15:59:18 +00:00
m_ctx - > wallet - > addressBook ( ) - > addRow ( i . value ( ) , " " , i . key ( ) ) ;
2020-10-21 06:25:02 +00:00
inserts + + ;
}
}
QMessageBox : : information ( this , " Contacts imported " , QString ( " Total contacts imported: %1 " ) . arg ( inserts ) ) ;
}
2020-10-07 10:36:04 +00:00
void MainWindow : : saveGeo ( ) {
config ( ) - > set ( Config : : geometry , QString ( saveGeometry ( ) . toBase64 ( ) ) ) ;
config ( ) - > set ( Config : : windowState , QString ( saveState ( ) . toBase64 ( ) ) ) ;
}
void MainWindow : : restoreGeo ( ) {
bool geo = this - > restoreGeometry ( QByteArray : : fromBase64 ( config ( ) - > get ( Config : : geometry ) . toByteArray ( ) ) ) ;
bool windowState = this - > restoreState ( QByteArray : : fromBase64 ( config ( ) - > get ( Config : : windowState ) . toByteArray ( ) ) ) ;
qDebug ( ) < < " Restored window state: " < < geo < < " " < < windowState ;
}
void MainWindow : : showDebugInfo ( ) {
2021-05-18 15:59:18 +00:00
DebugInfoDialog dialog { m_ctx , this } ;
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 ;
}
2021-05-18 15:59:18 +00:00
WalletCacheDebugDialog dialog { m_ctx , this } ;
dialog . exec ( ) ;
2020-10-07 10:36:04 +00:00
}
2021-05-22 13:42:26 +00:00
void MainWindow : : showAccountSwitcherDialog ( ) {
AccountSwitcherDialog dialog { m_ctx , this } ;
dialog . exec ( ) ;
}
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 ) ) {
QMessageBox : : warning ( this , " Address Checker " , " Invalid address. " ) ;
return ;
}
SubaddressIndex index = m_ctx - > wallet - > subaddressIndex ( address ) ;
if ( ! index . isValid ( ) ) {
// TODO: probably mention lookahead here
QMessageBox : : warning ( this , " Address Checker " , " This address does not belong to this wallet. " ) ;
return ;
} else {
QMessageBox : : information ( this , " Address Checker " , QString ( " This address belongs to Account #%1 " ) . arg ( index . major ) ) ;
}
}
2020-10-07 10:36:04 +00:00
void MainWindow : : showNodeExhaustedMessage ( ) {
// Spawning dialogs inside a lambda can cause system freezes on linux so we have to do it this way ¯\_(ツ)_/¯
auto msg = " Feather is in 'custom node connection mode' but could not "
" find an eligible node to connect to. Please go to Settings->Node "
" and enter a node manually. " ;
QMessageBox : : warning ( this , " Could not connect to a node " , msg ) ;
}
void MainWindow : : showWSNodeExhaustedMessage ( ) {
auto msg = " Feather is in 'automatic node connection mode' but the "
2020-10-10 02:01:25 +00:00
" websocket server returned no available nodes. Please go to Settings->Node "
2020-10-07 10:36:04 +00:00
" and enter a node manually. " ;
QMessageBox : : warning ( this , " Could not connect to a node " , msg ) ;
}
2020-10-14 18:18:25 +00:00
void MainWindow : : exportKeyImages ( ) {
2021-10-13 20:31:47 +00:00
QString fn = QFileDialog : : getSaveFileName ( this , " Save key images to file " , QString ( " %1/%2_%3 " ) . arg ( QDir : : homePath ( ) , this - > walletName ( ) , QString : : number ( QDateTime : : currentSecsSinceEpoch ( ) ) ) , " Key Images (*_keyImages) " ) ;
2020-10-14 18:18:25 +00:00
if ( fn . isEmpty ( ) ) return ;
if ( ! fn . endsWith ( " _keyImages " ) ) fn + = " _keyImages " ;
2022-03-14 19:49:15 +00:00
bool r = m_ctx - > wallet - > exportKeyImages ( fn , true ) ;
if ( ! r ) {
QMessageBox : : warning ( this , " Key image export " , QString ( " Failed to export key images. \n Reason: %1 " ) . arg ( m_ctx - > wallet - > errorString ( ) ) ) ;
2020-10-14 18:18:25 +00:00
} else {
QMessageBox : : information ( this , " Key image export " , " Successfully exported key images. " ) ;
}
}
void MainWindow : : importKeyImages ( ) {
QString fn = QFileDialog : : getOpenFileName ( this , " Import key image file " , QDir : : homePath ( ) , " Key Images (*_keyImages) " ) ;
if ( fn . isEmpty ( ) ) return ;
2022-03-14 19:49:15 +00:00
bool r = m_ctx - > wallet - > importKeyImages ( fn ) ;
if ( ! r ) {
QMessageBox : : warning ( this , " Key image import " , QString ( " Failed to import key images. \n \n %1 " ) . arg ( m_ctx - > wallet - > errorString ( ) ) ) ;
2020-10-14 18:18:25 +00:00
} else {
QMessageBox : : information ( this , " Key image import " , " Successfully imported key images " ) ;
m_ctx - > refreshModels ( ) ;
}
}
void MainWindow : : exportOutputs ( ) {
2021-10-13 20:31:47 +00:00
QString fn = QFileDialog : : getSaveFileName ( this , " Save outputs to file " , QString ( " %1/%2_%3 " ) . arg ( QDir : : homePath ( ) , this - > walletName ( ) , QString : : number ( QDateTime : : currentSecsSinceEpoch ( ) ) ) , " Outputs (*_outputs) " ) ;
2020-10-14 18:18:25 +00:00
if ( fn . isEmpty ( ) ) return ;
if ( ! fn . endsWith ( " _outputs " ) ) fn + = " _outputs " ;
2022-03-14 19:49:15 +00:00
bool r = m_ctx - > wallet - > exportOutputs ( fn , true ) ;
if ( ! r ) {
QMessageBox : : warning ( this , " Outputs export " , QString ( " Failed to export outputs. \n Reason: %1 " ) . arg ( m_ctx - > wallet - > errorString ( ) ) ) ;
2020-10-14 18:18:25 +00:00
} else {
QMessageBox : : information ( this , " Outputs export " , " Successfully exported outputs. " ) ;
}
}
void MainWindow : : importOutputs ( ) {
QString fn = QFileDialog : : getOpenFileName ( this , " Import outputs file " , QDir : : homePath ( ) , " Outputs (*_outputs) " ) ;
if ( fn . isEmpty ( ) ) return ;
2022-03-14 19:49:15 +00:00
bool r = m_ctx - > wallet - > importOutputs ( fn ) ;
if ( ! r ) {
QMessageBox : : warning ( this , " Outputs import " , QString ( " Failed to import outputs. \n \n %1 " ) . arg ( m_ctx - > wallet - > errorString ( ) ) ) ;
2020-10-14 18:18:25 +00:00
} else {
QMessageBox : : information ( this , " Outputs import " , " Successfully imported outputs " ) ;
m_ctx - > refreshModels ( ) ;
}
}
2020-10-16 03:05:05 +00:00
void MainWindow : : loadUnsignedTx ( ) {
QString fn = QFileDialog : : getOpenFileName ( this , " Select transaction to load " , QDir : : homePath ( ) , " Transaction (*unsigned_monero_tx) " ) ;
if ( fn . isEmpty ( ) ) return ;
2021-05-18 15:59:18 +00:00
UnsignedTransaction * tx = m_ctx - > wallet - > loadTxFile ( fn ) ;
auto err = m_ctx - > wallet - > errorString ( ) ;
2020-10-16 03:05:05 +00:00
if ( ! err . isEmpty ( ) ) {
QMessageBox : : warning ( this , " Load transaction from file " , QString ( " Failed to load transaction. \n \n %1 " ) . arg ( err ) ) ;
return ;
}
this - > createUnsignedTxDialog ( tx ) ;
}
void MainWindow : : loadUnsignedTxFromClipboard ( ) {
QString unsigned_tx = Utils : : copyFromClipboard ( ) ;
if ( unsigned_tx . isEmpty ( ) ) {
QMessageBox : : warning ( this , " Load unsigned transaction from clipboard " , " Clipboard is empty " ) ;
return ;
}
2021-05-18 15:59:18 +00:00
UnsignedTransaction * tx = m_ctx - > wallet - > loadTxFromBase64Str ( unsigned_tx ) ;
auto err = m_ctx - > wallet - > errorString ( ) ;
2020-10-16 03:05:05 +00:00
if ( ! err . isEmpty ( ) ) {
QMessageBox : : warning ( this , " Load unsigned transaction from clipboard " , QString ( " Failed to load transaction. \n \n %1 " ) . arg ( err ) ) ;
return ;
}
this - > createUnsignedTxDialog ( tx ) ;
}
void MainWindow : : loadSignedTx ( ) {
QString fn = QFileDialog : : getOpenFileName ( this , " Select transaction to load " , QDir : : homePath ( ) , " Transaction (*signed_monero_tx) " ) ;
if ( fn . isEmpty ( ) ) return ;
2021-05-18 15:59:18 +00:00
PendingTransaction * tx = m_ctx - > wallet - > loadSignedTxFile ( fn ) ;
auto err = m_ctx - > wallet - > errorString ( ) ;
2020-10-16 03:05:05 +00:00
if ( ! err . isEmpty ( ) ) {
QMessageBox : : warning ( this , " Load signed transaction from file " , err ) ;
return ;
}
2021-05-18 15:59:18 +00:00
TxConfAdvDialog dialog { m_ctx , " " , this } ;
dialog . setTransaction ( tx ) ;
dialog . exec ( ) ;
2020-10-16 03:05:05 +00:00
}
void MainWindow : : loadSignedTxFromText ( ) {
2021-06-27 12:22:54 +00:00
TxBroadcastDialog dialog { this , m_ctx } ;
2021-05-18 15:59:18 +00:00
dialog . exec ( ) ;
2020-10-16 03:05:05 +00:00
}
void MainWindow : : createUnsignedTxDialog ( UnsignedTransaction * tx ) {
2021-05-18 15:59:18 +00:00
TxConfAdvDialog dialog { m_ctx , " " , this } ;
dialog . setUnsignedTransaction ( tx ) ;
dialog . exec ( ) ;
2020-10-16 03:05:05 +00:00
}
2020-11-10 11:38:37 +00:00
void MainWindow : : importTransaction ( ) {
2021-05-04 23:09:19 +00:00
if ( config ( ) - > get ( Config : : torPrivacyLevel ) . toInt ( ) = = Config : : allTorExceptNode ) {
// 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
2021-05-18 15:59:18 +00:00
TxImportDialog dialog ( this , m_ctx ) ;
dialog . exec ( ) ;
2020-11-10 11:38:37 +00:00
}
2021-05-02 18:22:38 +00:00
void MainWindow : : onDeviceError ( const QString & error ) {
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 ) {
2021-05-18 15:59:18 +00:00
bool r = m_ctx - > 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 ( ) ) ;
2021-05-18 15:59:18 +00:00
m_ctx - > 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 ;
2021-07-01 21:00:47 +00:00
if ( m_ctx - > wallet - > isTrezor ( ) ) {
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 " ) ;
2021-07-01 21:00:47 +00:00
m_splashDialog - > setIcon ( QPixmap ( " :/assets/images/confirmed.png " ) ) ;
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 ) {
m_ctx - > wallet - > onPassphraseEntered ( " " , true , false ) ;
return ;
}
bool ok ;
QString passphrase = QInputDialog : : getText ( nullptr , " Wallet Passphrase Needed " , " Enter passphrase: " , QLineEdit : : EchoMode : : Password , " " , & ok ) ;
if ( ok ) {
m_ctx - > wallet - > onPassphraseEntered ( passphrase , false , false ) ;
} else {
m_ctx - > wallet - > onPassphraseEntered ( passphrase , false , true ) ;
}
}
2020-11-23 16:57:38 +00:00
void MainWindow : : updateNetStats ( ) {
2021-07-06 19:29:12 +00:00
if ( ! m_ctx - > wallet | | m_ctx - > wallet - > connectionStatus ( ) = = Wallet : : ConnectionStatus_Disconnected
| | m_ctx - > wallet - > connectionStatus ( ) = = Wallet : : ConnectionStatus_Synchronized )
{
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 ( ) ;
2021-05-18 15:59:18 +00:00
m_statusLabelNetStats - > setText ( QString ( " (D: %1) " ) . arg ( Utils : : formatBytes ( m_ctx - > wallet - > getBytesReceived ( ) ) ) ) ;
2020-11-23 16:57:38 +00:00
}
2020-12-14 19:44:37 +00:00
void MainWindow : : rescanSpent ( ) {
2021-05-18 15:59:18 +00:00
if ( ! m_ctx - > wallet - > rescanSpent ( ) ) {
QMessageBox : : warning ( this , " Rescan spent " , m_ctx - > wallet - > errorString ( ) ) ;
2020-12-14 19:44:37 +00:00
} else {
QMessageBox : : information ( this , " Rescan spent " , " Successfully rescanned spent outputs. " ) ;
}
}
2020-12-25 14:20:39 +00:00
void MainWindow : : showBalanceDialog ( ) {
2021-07-03 01:29:13 +00:00
BalanceDialog dialog { this , m_ctx - > 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 ( ) ;
}
2021-05-18 15:59:18 +00:00
void MainWindow : : onTorConnectionStateChanged ( bool connected ) {
if ( connected )
m_statusBtnTor - > setIcon ( icons ( ) - > icon ( " tor_logo.png " ) ) ;
else
m_statusBtnTor - > setIcon ( icons ( ) - > icon ( " tor_logo_disabled.png " ) ) ;
2021-05-02 18:22:38 +00:00
}
void MainWindow : : onCheckUpdatesComplete ( const QString & version , const QString & binaryFilename ,
const QString & hash , const QString & signer ) {
QString versionDisplay { version } ;
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 ( ) ;
2021-05-02 18:22:38 +00:00
connect ( m_statusUpdateAvailable , & StatusBarButton : : clicked , [ this , version , binaryFilename , hash , signer ] {
this - > onShowUpdateCheck ( version , binaryFilename , hash , signer ) ;
} ) ;
}
void MainWindow : : onShowUpdateCheck ( const QString & version , const QString & binaryFilename ,
const QString & hash , const QString & signer ) {
QString downloadUrl = QString ( " https://featherwallet.org/files/releases/%1/%2 " ) . arg ( this - > getPlatformTag ( ) , binaryFilename ) ;
UpdateDialog updateDialog { this , version , downloadUrl , hash , signer } ;
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 ( ) ;
}
2021-03-24 01:37:54 +00:00
2021-05-02 18:22:38 +00:00
void MainWindow : : onUpdatesAvailable ( const QJsonObject & updates ) {
QString featherVersionStr { FEATHER_VERSION } ;
auto featherVersion = SemanticVersion : : fromString ( featherVersionStr ) ;
QString platformTag = getPlatformTag ( ) ;
if ( platformTag . isEmpty ( ) ) {
qWarning ( ) < < " Unsupported platform, unable to fetch update " ;
return ;
}
QJsonObject platformData = updates [ " platform " ] . toObject ( ) [ platformTag ] . toObject ( ) ;
if ( platformData . isEmpty ( ) ) {
qWarning ( ) < < " Unable to find current platform in updates data " ;
return ;
}
QString newVersion = platformData [ " version " ] . toString ( ) ;
2021-05-04 23:09:19 +00:00
if ( SemanticVersion : : fromString ( newVersion ) < = featherVersion ) {
return ;
}
2021-05-02 18:22:38 +00:00
// Hooray! New update available
2021-05-18 15:59:18 +00:00
QString hashesUrl = QString ( " %1/files/releases/hashes-%2-plain.txt " ) . arg ( constants : : websiteUrl , newVersion ) ;
2021-05-02 18:22:38 +00:00
UtilsNetworking network { getNetworkTor ( ) } ;
QNetworkReply * reply = network . get ( hashesUrl ) ;
connect ( reply , & QNetworkReply : : finished , this , std : : bind ( & MainWindow : : onSignedHashesReceived , this , reply , platformTag , newVersion ) ) ;
}
void MainWindow : : onSignedHashesReceived ( QNetworkReply * reply , const QString & platformTag , const QString & version ) {
if ( reply - > error ( ) ! = QNetworkReply : : NoError ) {
qWarning ( ) < < " Unable to fetch signed hashes: " < < reply - > errorString ( ) ;
return ;
}
QByteArray armoredSignedHashes = reply - > readAll ( ) ;
reply - > deleteLater ( ) ;
const QString binaryFilename = QString ( " feather-%1-%2.zip " ) . arg ( version , platformTag ) ;
QString signer ;
QByteArray signedHash = AsyncTask : : runAndWaitForFuture ( [ armoredSignedHashes , binaryFilename , & signer ] {
try {
return Updater ( ) . verifyParseSignedHashes ( armoredSignedHashes , binaryFilename , signer ) ;
}
catch ( const std : : exception & e ) {
qWarning ( ) < < " Failed to fetch and verify signed hash: " < < e . what ( ) ;
return QByteArray { } ;
}
} ) ;
if ( signedHash . isEmpty ( ) ) {
return ;
}
QString hash = signedHash . toHex ( ) ;
qInfo ( ) < < " Update found: " < < binaryFilename < < hash < < " signed by: " < < signer ;
this - > onCheckUpdatesComplete ( version , binaryFilename , hash , signer ) ;
}
void MainWindow : : onInitiateTransaction ( ) {
m_statusDots = 0 ;
m_constructingTransaction = true ;
m_txTimer . start ( 1000 ) ;
2021-05-04 23:09:19 +00:00
2021-05-18 15:59:18 +00:00
if ( m_ctx - > 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
}
void MainWindow : : onEndTransaction ( ) {
// Todo: endTransaction can fail to fire when the node is switched during tx creation
m_constructingTransaction = false ;
m_txTimer . stop ( ) ;
this - > setStatusText ( m_statusText ) ;
2021-05-04 23:09:19 +00:00
2021-05-18 15:59:18 +00:00
if ( m_ctx - > wallet - > isHwBacked ( ) ) {
2021-05-04 23:09:19 +00:00
m_splashDialog - > hide ( ) ;
}
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 ;
QMessageBox : : warning ( this , " Critical error " , " WARNING! \n \n The wallet keys are corrupted. \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! " ) ;
m_sendWidget - > disableSendButton ( ) ;
}
}
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 ;
auto coins = m_ctx - > wallet - > coins ( ) - > coinsFromKeyImage ( selectedInputs ) ;
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 ) ;
}
}
2021-05-02 18:22:38 +00:00
void MainWindow : : onExportHistoryCSV ( bool checked ) {
2021-05-18 15:59:18 +00:00
if ( m_ctx - > wallet = = nullptr )
2021-05-02 18:22:38 +00:00
return ;
QString fn = QFileDialog : : getSaveFileName ( this , " Save CSV file " , QDir : : homePath ( ) , " CSV (*.csv) " ) ;
if ( fn . isEmpty ( ) )
return ;
if ( ! fn . endsWith ( " .csv " ) )
fn + = " .csv " ;
2021-05-18 15:59:18 +00:00
m_ctx - > wallet - > history ( ) - > writeCSV ( fn ) ;
2021-05-02 18:22:38 +00:00
QMessageBox : : information ( this , " CSV export " , QString ( " Transaction history exported to %1 " ) . arg ( fn ) ) ;
}
void MainWindow : : onExportContactsCSV ( bool checked ) {
2021-05-18 15:59:18 +00:00
if ( m_ctx - > wallet = = nullptr ) return ;
auto * model = m_ctx - > wallet - > addressBookModel ( ) ;
2021-05-02 18:22:38 +00:00
if ( model - > rowCount ( ) < = 0 ) {
QMessageBox : : warning ( this , " Error " , " Addressbook empty " ) ;
return ;
}
const QString targetDir = QFileDialog : : getExistingDirectory ( this , " Select CSV output directory " , QDir : : homePath ( ) , QFileDialog : : ShowDirsOnly ) ;
if ( targetDir . isEmpty ( ) ) return ;
qint64 now = QDateTime : : currentDateTime ( ) . currentMSecsSinceEpoch ( ) ;
QString fn = QString ( " %1/monero-contacts_%2.csv " ) . arg ( targetDir , QString : : number ( now / 1000 ) ) ;
if ( model - > writeCSV ( fn ) )
QMessageBox : : information ( this , " Address book exported " , QString ( " Address book exported to %1 " ) . arg ( fn ) ) ;
}
void MainWindow : : onCreateDesktopEntry ( bool checked ) {
auto msg = Utils : : xdgDesktopEntryRegister ( ) ? " Desktop entry created " : " Desktop entry not created due to an error. " ;
QMessageBox : : information ( this , " Desktop entry " , msg ) ;
}
2021-10-21 21:13:25 +00:00
void MainWindow : : onShowDocumentaton ( ) {
Utils : : externalLinkWarning ( this , " https://docs.featherwallet.org " ) ;
}
2021-05-02 18:22:38 +00:00
void MainWindow : : onReportBug ( bool checked ) {
2021-10-21 21:13:25 +00:00
Utils : : externalLinkWarning ( this , " https://docs.featherwallet.org/guides/report-an-issue " ) ;
2021-05-02 18:22:38 +00:00
}
QString MainWindow : : getPlatformTag ( ) {
# ifdef Q_OS_MACOS
return " mac " ;
# endif
# ifdef Q_OS_WIN
2021-10-29 11:54:13 +00:00
# ifdef PLATFORM_INSTALLER
return " win-installer " ;
# endif
2021-05-02 18:22:38 +00:00
return " win " ;
# endif
# ifdef Q_OS_LINUX
2022-03-12 14:19:36 +00:00
bool isAppImage = ! qEnvironmentVariableIsEmpty ( " APPIMAGE " ) ;
if ( TailsOS : : detect ( ) & & isAppImage ) {
return " tails-appimage " ;
}
if ( isAppImage ) {
2021-05-02 18:22:38 +00:00
return " linux-appimage " ;
}
return " linux " ;
# endif
return " " ;
}
QString MainWindow : : getHardwareDevice ( ) {
2021-05-18 15:59:18 +00:00
if ( ! m_ctx - > wallet - > isHwBacked ( ) )
2021-05-02 18:22:38 +00:00
return " " ;
2021-05-18 15:59:18 +00:00
if ( m_ctx - > wallet - > isTrezor ( ) )
2021-05-02 18:22:38 +00:00
return " Trezor " ;
2021-05-18 15:59:18 +00:00
if ( m_ctx - > 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 ( ) {
QString title = QString ( " %1 (#%2) " ) . arg ( this - > walletName ( ) , QString : : number ( m_ctx - > wallet - > currentSubaddressAccount ( ) ) ) ;
if ( m_ctx - > wallet - > viewOnly ( ) )
2021-05-02 18:22:38 +00:00
title + = " [view-only] " ;
2021-06-14 18:01:40 +00:00
# ifdef HAS_XMRIG
2021-05-23 13:58:28 +00:00
if ( m_xmrig - > isMining ( ) )
2021-05-02 18:22:38 +00:00
title + = " [mining] " ;
2021-06-14 18:01:40 +00:00
# endif
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 ( ) {
if ( m_ctx - > networkType ! = NetworkType : : Type : : MAINNET )
return ;
if ( m_ctx - > wallet - > viewOnly ( ) )
return ;
2022-06-23 10:41:16 +00:00
if ( m_ctx - > wallet - > balanceAll ( ) = = 0 )
return ;
2021-05-18 15:59:18 +00:00
auto donationCounter = config ( ) - > get ( Config : : donateBeg ) . toInt ( ) ;
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 ( ) ;
}
}
config ( ) - > set ( Config : : donateBeg , donationCounter ) ;
}
2021-07-08 12:03:54 +00:00
void MainWindow : : addToRecentlyOpened ( const QString & keysFile ) {
2021-05-18 15:59:18 +00:00
auto recent = config ( ) - > get ( Config : : recentlyOpenedWallets ) . toList ( ) ;
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 ;
}
}
config ( ) - > 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 ( ) ;
2021-07-08 12:03:54 +00:00
const QStringList recentWallets = config ( ) - > get ( Config : : recentlyOpenedWallets ) . toStringList ( ) ;
for ( const auto & walletPath : recentWallets ) {
QFileInfo fileInfo { walletPath } ;
ui - > menuRecently_open - > addAction ( fileInfo . fileName ( ) , m_windowManager , std : : bind ( & WindowManager : : tryOpenWallet , m_windowManager , walletPath , " " ) ) ;
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
}
2021-06-04 18:48:44 +00:00
bool MainWindow : : verifyPassword ( ) {
2022-05-27 09:49:32 +00:00
bool incorrectPassword = false ;
2021-06-04 18:48:44 +00:00
while ( true ) {
2022-05-27 09:49:32 +00:00
PasswordDialog passwordDialog { this - > walletName ( ) , incorrectPassword , true , 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 ;
}
2022-03-04 16:20:17 +00:00
if ( passwordDialog . password ! = m_ctx - > wallet - > getPassword ( ) ) {
2022-05-27 09:49:32 +00:00
incorrectPassword = true ;
2021-06-04 18:48:44 +00:00
continue ;
}
break ;
}
return true ;
}
2021-10-28 17:50:18 +00:00
void MainWindow : : patchStylesheetMac ( ) {
auto patch = Utils : : fileOpenQRC ( " :assets/macStylesheet.patch " ) ;
auto patch_text = Utils : : barrayToString ( patch ) ;
QString styleSheet = qApp - > styleSheet ( ) + patch_text ;
qApp - > setStyleSheet ( styleSheet ) ;
}
2022-03-04 16:20:17 +00:00
void MainWindow : : userActivity ( ) {
m_userLastActive = QDateTime : : currentSecsSinceEpoch ( ) ;
}
void MainWindow : : checkUserActivity ( ) {
if ( ! config ( ) - > get ( Config : : inactivityLockEnabled ) . toBool ( ) ) {
return ;
}
if ( m_constructingTransaction ) {
return ;
}
if ( ( m_userLastActive + ( config ( ) - > get ( Config : : inactivityLockTimeout ) . toInt ( ) * 60 ) ) < QDateTime : : currentSecsSinceEpoch ( ) ) {
m_checkUserActivity . stop ( ) ;
qInfo ( ) < < " Locking wallet for inactivity " ;
if ( ! this - > verifyPassword ( ) ) {
this - > setEnabled ( false ) ;
this - > close ( ) ;
// This doesn't close the wallet immediately.
2022-05-24 15:53:45 +00:00
// FIXME
// do {
2022-03-04 16:20:17 +00:00
QApplication : : processEvents ( ) ;
// Because running it a single time is apparently not enough.
// TODO: Qt bug? Need proper fix for this.
2022-05-24 15:53:45 +00:00
// } while (QApplication::hasPendingEvents());
2022-03-04 16:20:17 +00:00
} else {
m_checkUserActivity . start ( ) ;
}
}
}
2021-05-23 14:58:18 +00:00
void MainWindow : : toggleSearchbar ( bool visible ) {
config ( ) - > set ( Config : : showSearchbar , visible ) ;
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 ( ) ;
if ( currentTab = = Tabs : : HISTORY )
m_historyWidget - > focusSearchbar ( ) ;
else if ( currentTab = = Tabs : : SEND )
m_contactsWidget - > focusSearchbar ( ) ;
else if ( currentTab = = Tabs : : RECEIVE )
m_receiveWidget - > focusSearchbar ( ) ;
2021-07-02 14:51:46 +00:00
else if ( currentTab = = Tabs : : COINS )
m_coinsWidget - > focusSearchbar ( ) ;
2021-05-23 14:58:18 +00:00
}
2021-06-27 12:13:05 +00:00
MainWindow : : ~ MainWindow ( ) = default ;