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"
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 ) ;
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
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
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 ) ;
connect ( m_statusBtnConnectionStatusIndicator , & StatusBarButton : : clicked , this , & MainWindow : : showConnectionStatusDialog ) ;
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
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 ) ;
connect ( ui - > actionChange_restore_height , & QAction : : triggered , this , & MainWindow : : showRestoreHeightDialog ) ;
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
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
2021-05-24 23:10:45 +00:00
m_priceTickerWidgets . append ( new PriceTickerWidget ( this , m_ctx , " XMR " ) ) ;
m_priceTickerWidgets . append ( new PriceTickerWidget ( this , m_ctx , " BTC " ) ) ;
for ( const auto & widget : m_priceTickerWidgets ) {
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 ) ;
}
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 ) ;
connect ( m_ctx . get ( ) , & AppContext : : customRestoreHeightSet , this , & MainWindow : : onCustomRestoreHeightSet ) ;
2021-05-02 18:22:38 +00:00
// Nodes
2021-05-24 14:50:09 +00:00
connect ( m_ctx - > nodes , & Nodes : : updateStatus , this , & MainWindow : : onSetStatusText ) ;
2021-05-02 18:22:38 +00:00
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 ) ;
2021-05-18 15:59:18 +00:00
if ( ! torManager ( ) - > torConnected )
2021-07-02 22:25:15 +00:00
this - > setStatusText ( " Starting Tor (may take a while) " ) ;
2020-10-07 10:36:04 +00:00
// 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-10-07 10:36:04 +00:00
qDebug ( ) < < Q_FUNC_INFO ;
2021-05-22 14:12:39 +00:00
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
}
2021-05-24 14:50:09 +00:00
void MainWindow : : onSetStatusText ( const QString & text ) {
this - > setStatusText ( text ) ;
}
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 ( ) ;
}
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
}
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 : : showConnectionStatusDialog ( ) {
2021-05-18 15:59:18 +00:00
auto status = m_ctx - > wallet - > connectionStatus ( ) ;
bool synchronized = m_ctx - > wallet - > isSynchronized ( ) ;
2020-10-07 10:36:04 +00:00
QString statusMsg ;
switch ( status ) {
case Wallet : : ConnectionStatus_Disconnected :
2021-08-14 14:57:39 +00:00
statusMsg = " Wallet is disconnected from node. " ;
2020-10-07 10:36:04 +00:00
break ;
case Wallet : : ConnectionStatus_Connecting : {
auto node = m_ctx - > nodes - > connection ( ) ;
2021-05-02 18:22:38 +00:00
statusMsg = QString ( " Wallet is connecting to %1 " ) . arg ( node . toAddress ( ) ) ;
2020-10-07 10:36:04 +00:00
break ;
}
case Wallet : : ConnectionStatus_WrongVersion :
2021-08-14 14:57:39 +00:00
statusMsg = " Wallet is connected to incompatible node. " ;
2020-10-07 10:36:04 +00:00
break ;
2021-03-27 15:04:03 +00:00
case Wallet : : ConnectionStatus_Synchronizing :
2021-03-24 01:37:54 +00:00
case Wallet : : ConnectionStatus_Synchronized : {
2021-03-27 15:04:03 +00:00
auto node = m_ctx - > nodes - > connection ( ) ;
2021-05-02 18:22:38 +00:00
statusMsg = QString ( " Wallet is connected to %1 " ) . arg ( node . toAddress ( ) ) ;
2021-03-27 15:04:03 +00:00
if ( synchronized ) {
statusMsg + = " and synchronized " ;
} else {
statusMsg + = " and synchronizing " ;
}
2021-03-24 01:37:54 +00:00
break ;
}
2020-10-07 10:36:04 +00:00
default :
statusMsg = " Unknown connection status (this should never happen). " ;
}
2021-05-18 15:59:18 +00:00
statusMsg + = QString ( " \n \n Tx: %1, Rx: %2 " ) . arg ( Utils : : formatBytes ( m_ctx - > wallet - > getBytesSent ( ) ) ,
Utils : : formatBytes ( m_ctx - > wallet - > getBytesReceived ( ) ) ) ;
2020-11-23 16:57:38 +00:00
2020-10-07 10:36:04 +00:00
QMessageBox : : information ( this , " Connection Status " , statusMsg ) ;
}
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 : : showRestoreHeightDialog ( ) {
// settings custom restore height is only available for 25 word seeds
2021-05-18 15:59:18 +00:00
auto seed = m_ctx - > wallet - > getCacheAttribute ( " feather.seed " ) ;
2021-05-25 00:26:28 +00:00
if ( ! seed . isEmpty ( ) ) { // TODO: update this warning (import tx, delete cache, restore from seed)
2020-10-07 10:36:04 +00:00
const auto msg = " This wallet has a 14 word mnemonic seed which has the restore height embedded. " ;
QMessageBox : : warning ( this , " Cannot set custom restore height " , msg ) ;
return ;
}
2021-05-25 00:26:28 +00:00
RestoreHeightDialog dialog { this , m_ctx - > wallet - > getWalletCreationHeight ( ) } ;
if ( dialog . exec ( ) = = QDialog : : Accepted ) {
int restoreHeight = dialog . getHeight ( ) ;
m_ctx - > onSetRestoreHeight ( restoreHeight ) ;
}
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 } ;
for ( const auto & widget : m_priceTickerWidgets ) {
connect ( & settings , & Settings : : preferredFiatCurrencyChanged , widget , & PriceTickerWidget : : updateDisplay ) ;
}
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 ) ;
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
}
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 ( ) ;
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 ( ) {
2021-05-18 15:59:18 +00:00
double donation = appData ( ) - > prices . convert ( " EUR " , " XMR " , constants : : donationAmount ) ;
2020-12-14 00:59:32 +00:00
if ( donation < = 0 )
donation = 0.1337 ;
2020-10-07 10:36:04 +00:00
2021-05-18 15:59:18 +00:00
m_sendWidget - > fill ( constants : : donationAddress , " Donation to the Feather development team " , donation ) ;
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 ( ) {
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 " ;
2021-05-18 15:59:18 +00:00
m_ctx - > wallet - > exportKeyImages ( fn , true ) ;
auto err = m_ctx - > wallet - > errorString ( ) ;
2020-10-14 18:18:25 +00:00
if ( ! err . isEmpty ( ) ) {
QMessageBox : : warning ( this , " Key image export " , QString ( " Failed to export key images. \n Reason: %1 " ) . arg ( err ) ) ;
} 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 ;
2021-05-18 15:59:18 +00:00
m_ctx - > wallet - > importKeyImages ( fn ) ;
auto err = m_ctx - > wallet - > errorString ( ) ;
2020-10-14 18:18:25 +00:00
if ( ! err . isEmpty ( ) ) {
QMessageBox : : warning ( this , " Key image import " , QString ( " Failed to import key images. \n \n %1 " ) . arg ( err ) ) ;
} 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 " ;
2021-05-18 15:59:18 +00:00
m_ctx - > wallet - > exportOutputs ( fn , true ) ;
auto err = m_ctx - > wallet - > errorString ( ) ;
2020-10-14 18:18:25 +00:00
if ( ! err . isEmpty ( ) ) {
QMessageBox : : warning ( this , " Outputs export " , QString ( " Failed to export outputs. \n Reason: %1 " ) . arg ( err ) ) ;
} 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 ;
2021-05-18 15:59:18 +00:00
m_ctx - > wallet - > importOutputs ( fn ) ;
auto err = m_ctx - > wallet - > errorString ( ) ;
2020-10-14 18:18:25 +00:00
if ( ! err . isEmpty ( ) ) {
QMessageBox : : warning ( this , " Outputs import " , QString ( " Failed to import outputs. \n \n %1 " ) . arg ( err ) ) ;
} 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
}
void MainWindow : : onCustomRestoreHeightSet ( int height ) {
auto msg = QString ( " The restore height for this wallet has been set to %1. "
" Please re-open the wallet. Feather will now quit. " ) . arg ( height ) ;
QMessageBox : : information ( this , " Cannot set custom restore height " , msg ) ;
this - > menuQuitClicked ( ) ;
}
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-02-11 11:50:09 +00:00
if ( ! qEnvironmentVariableIsEmpty ( " APPIMAGE " ) ) {
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 ;
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 ( ) {
bool ok ;
while ( true ) {
QString password = QInputDialog : : getText ( this , " Enter password " , " Please enter your password: " , QLineEdit : : EchoMode : : Password , " " , & ok ) ;
if ( ! ok ) { // Dialog cancelled
return false ;
}
if ( password ! = m_ctx - > wallet - > getPassword ( ) ) {
QMessageBox : : warning ( this , " Error " , " Incorrect password " ) ;
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 ) ;
}
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 ;