/ src / qt / bitcoingui.cpp
bitcoingui.cpp
   1  // Copyright (c) 2011-2022 The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #if defined(HAVE_CONFIG_H)
   6  #include <config/bitcoin-config.h>
   7  #endif
   8  
   9  #include <qt/bitcoingui.h>
  10  
  11  #include <qt/bitcoinunits.h>
  12  #include <qt/clientmodel.h>
  13  #include <qt/createwalletdialog.h>
  14  #include <qt/guiconstants.h>
  15  #include <qt/guiutil.h>
  16  #include <qt/modaloverlay.h>
  17  #include <qt/networkstyle.h>
  18  #include <qt/notificator.h>
  19  #include <qt/openuridialog.h>
  20  #include <qt/optionsdialog.h>
  21  #include <qt/optionsmodel.h>
  22  #include <qt/platformstyle.h>
  23  #include <qt/rpcconsole.h>
  24  #include <qt/utilitydialog.h>
  25  
  26  #ifdef ENABLE_WALLET
  27  #include <qt/walletcontroller.h>
  28  #include <qt/walletframe.h>
  29  #include <qt/walletmodel.h>
  30  #include <qt/walletview.h>
  31  #endif // ENABLE_WALLET
  32  
  33  #ifdef Q_OS_MACOS
  34  #include <qt/macdockiconhandler.h>
  35  #endif
  36  
  37  #include <chain.h>
  38  #include <chainparams.h>
  39  #include <common/system.h>
  40  #include <interfaces/handler.h>
  41  #include <interfaces/node.h>
  42  #include <node/interface_ui.h>
  43  #include <util/translation.h>
  44  #include <validation.h>
  45  
  46  #include <functional>
  47  
  48  #include <QAction>
  49  #include <QActionGroup>
  50  #include <QApplication>
  51  #include <QComboBox>
  52  #include <QCursor>
  53  #include <QDateTime>
  54  #include <QDragEnterEvent>
  55  #include <QInputDialog>
  56  #include <QKeySequence>
  57  #include <QListWidget>
  58  #include <QMenu>
  59  #include <QMenuBar>
  60  #include <QMessageBox>
  61  #include <QMimeData>
  62  #include <QProgressDialog>
  63  #include <QScreen>
  64  #include <QSettings>
  65  #include <QShortcut>
  66  #include <QStackedWidget>
  67  #include <QStatusBar>
  68  #include <QStyle>
  69  #include <QSystemTrayIcon>
  70  #include <QTimer>
  71  #include <QToolBar>
  72  #include <QUrlQuery>
  73  #include <QVBoxLayout>
  74  #include <QWindow>
  75  
  76  
  77  const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
  78  #if defined(Q_OS_MACOS)
  79          "macosx"
  80  #elif defined(Q_OS_WIN)
  81          "windows"
  82  #else
  83          "other"
  84  #endif
  85          ;
  86  
  87  BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) :
  88      QMainWindow(parent),
  89      m_node(node),
  90      trayIconMenu{new QMenu()},
  91      platformStyle(_platformStyle),
  92      m_network_style(networkStyle)
  93  {
  94      QSettings settings;
  95      if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) {
  96          // Restore failed (perhaps missing setting), center the window
  97          move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center());
  98      }
  99  
 100      setContextMenuPolicy(Qt::PreventContextMenu);
 101  
 102  #ifdef ENABLE_WALLET
 103      enableWallet = WalletModel::isWalletEnabled();
 104  #endif // ENABLE_WALLET
 105      QApplication::setWindowIcon(m_network_style->getTrayAndWindowIcon());
 106      setWindowIcon(m_network_style->getTrayAndWindowIcon());
 107      updateWindowTitle();
 108  
 109      rpcConsole = new RPCConsole(node, _platformStyle, nullptr);
 110      helpMessageDialog = new HelpMessageDialog(this, false);
 111  #ifdef ENABLE_WALLET
 112      if(enableWallet)
 113      {
 114          /** Create wallet frame and make it the central widget */
 115          walletFrame = new WalletFrame(_platformStyle, this);
 116          connect(walletFrame, &WalletFrame::createWalletButtonClicked, this, &BitcoinGUI::createWallet);
 117          connect(walletFrame, &WalletFrame::message, [this](const QString& title, const QString& message, unsigned int style) {
 118              this->message(title, message, style);
 119          });
 120          connect(walletFrame, &WalletFrame::currentWalletSet, [this] { updateWalletStatus(); });
 121          setCentralWidget(walletFrame);
 122      } else
 123  #endif // ENABLE_WALLET
 124      {
 125          /* When compiled without wallet or -disablewallet is provided,
 126           * the central widget is the rpc console.
 127           */
 128          setCentralWidget(rpcConsole);
 129          Q_EMIT consoleShown(rpcConsole);
 130      }
 131  
 132      modalOverlay = new ModalOverlay(enableWallet, this->centralWidget());
 133  
 134      // Accept D&D of URIs
 135      setAcceptDrops(true);
 136  
 137      // Create actions for the toolbar, menu bar and tray/dock icon
 138      // Needs walletFrame to be initialized
 139      createActions();
 140  
 141      // Create application menu bar
 142      createMenuBar();
 143  
 144      // Create the toolbars
 145      createToolBars();
 146  
 147      // Create system tray icon and notification
 148      if (QSystemTrayIcon::isSystemTrayAvailable()) {
 149          createTrayIcon();
 150      }
 151      notificator = new Notificator(QApplication::applicationName(), trayIcon, this);
 152  
 153      // Create status bar
 154      statusBar();
 155  
 156      // Disable size grip because it looks ugly and nobody needs it
 157      statusBar()->setSizeGripEnabled(false);
 158  
 159      // Status bar notification icons
 160      QFrame *frameBlocks = new QFrame();
 161      frameBlocks->setContentsMargins(0,0,0,0);
 162      frameBlocks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
 163      QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
 164      frameBlocksLayout->setContentsMargins(3,0,3,0);
 165      frameBlocksLayout->setSpacing(3);
 166      unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle);
 167      labelWalletEncryptionIcon = new GUIUtil::ThemedLabel(platformStyle);
 168      labelWalletHDStatusIcon = new GUIUtil::ThemedLabel(platformStyle);
 169      labelProxyIcon = new GUIUtil::ClickableLabel(platformStyle);
 170      connectionsControl = new GUIUtil::ClickableLabel(platformStyle);
 171      labelBlocksIcon = new GUIUtil::ClickableLabel(platformStyle);
 172      if(enableWallet)
 173      {
 174          frameBlocksLayout->addStretch();
 175          frameBlocksLayout->addWidget(unitDisplayControl);
 176          frameBlocksLayout->addStretch();
 177          frameBlocksLayout->addWidget(labelWalletEncryptionIcon);
 178          labelWalletEncryptionIcon->hide();
 179          frameBlocksLayout->addWidget(labelWalletHDStatusIcon);
 180          labelWalletHDStatusIcon->hide();
 181      }
 182      frameBlocksLayout->addWidget(labelProxyIcon);
 183      frameBlocksLayout->addStretch();
 184      frameBlocksLayout->addWidget(connectionsControl);
 185      frameBlocksLayout->addStretch();
 186      frameBlocksLayout->addWidget(labelBlocksIcon);
 187      frameBlocksLayout->addStretch();
 188  
 189      // Progress bar and label for blocks download
 190      progressBarLabel = new QLabel();
 191      progressBarLabel->setVisible(false);
 192      progressBar = new GUIUtil::ProgressBar();
 193      progressBar->setAlignment(Qt::AlignCenter);
 194      progressBar->setVisible(false);
 195  
 196      // Override style sheet for progress bar for styles that have a segmented progress bar,
 197      // as they make the text unreadable (workaround for issue #1071)
 198      // See https://doc.qt.io/qt-5/gallery.html
 199      QString curStyle = QApplication::style()->metaObject()->className();
 200      if(curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle")
 201      {
 202          progressBar->setStyleSheet("QProgressBar { background-color: #e8e8e8; border: 1px solid grey; border-radius: 7px; padding: 1px; text-align: center; } QProgressBar::chunk { background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #FF8000, stop: 1 orange); border-radius: 7px; margin: 0px; }");
 203      }
 204  
 205      statusBar()->addWidget(progressBarLabel);
 206      statusBar()->addWidget(progressBar);
 207      statusBar()->addPermanentWidget(frameBlocks);
 208  
 209      // Install event filter to be able to catch status tip events (QEvent::StatusTip)
 210      this->installEventFilter(this);
 211  
 212      // Initially wallet actions should be disabled
 213      setWalletActionsEnabled(false);
 214  
 215      // Subscribe to notifications from core
 216      subscribeToCoreSignals();
 217  
 218      connect(labelProxyIcon, &GUIUtil::ClickableLabel::clicked, [this] {
 219          openOptionsDialogWithTab(OptionsDialog::TAB_NETWORK);
 220      });
 221  
 222      connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay);
 223      connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay);
 224  
 225  #ifdef Q_OS_MACOS
 226      m_app_nap_inhibitor = new CAppNapInhibitor;
 227  #endif
 228  
 229      GUIUtil::handleCloseWindowShortcut(this);
 230  }
 231  
 232  BitcoinGUI::~BitcoinGUI()
 233  {
 234      // Unsubscribe from notifications from core
 235      unsubscribeFromCoreSignals();
 236  
 237      QSettings settings;
 238      settings.setValue("MainWindowGeometry", saveGeometry());
 239      if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
 240          trayIcon->hide();
 241  #ifdef Q_OS_MACOS
 242      delete m_app_nap_inhibitor;
 243      MacDockIconHandler::cleanup();
 244  #endif
 245  
 246      delete rpcConsole;
 247  }
 248  
 249  void BitcoinGUI::createActions()
 250  {
 251      QActionGroup *tabGroup = new QActionGroup(this);
 252      connect(modalOverlay, &ModalOverlay::triggered, tabGroup, &QActionGroup::setEnabled);
 253  
 254      overviewAction = new QAction(platformStyle->SingleColorIcon(":/icons/overview"), tr("&Overview"), this);
 255      overviewAction->setStatusTip(tr("Show general overview of wallet"));
 256      overviewAction->setToolTip(overviewAction->statusTip());
 257      overviewAction->setCheckable(true);
 258      overviewAction->setShortcut(QKeySequence(QStringLiteral("Alt+1")));
 259      tabGroup->addAction(overviewAction);
 260  
 261      sendCoinsAction = new QAction(platformStyle->SingleColorIcon(":/icons/send"), tr("&Send"), this);
 262      sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address"));
 263      sendCoinsAction->setToolTip(sendCoinsAction->statusTip());
 264      sendCoinsAction->setCheckable(true);
 265      sendCoinsAction->setShortcut(QKeySequence(QStringLiteral("Alt+2")));
 266      tabGroup->addAction(sendCoinsAction);
 267  
 268      receiveCoinsAction = new QAction(platformStyle->SingleColorIcon(":/icons/receiving_addresses"), tr("&Receive"), this);
 269      receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and bitcoin: URIs)"));
 270      receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip());
 271      receiveCoinsAction->setCheckable(true);
 272      receiveCoinsAction->setShortcut(QKeySequence(QStringLiteral("Alt+3")));
 273      tabGroup->addAction(receiveCoinsAction);
 274  
 275      historyAction = new QAction(platformStyle->SingleColorIcon(":/icons/history"), tr("&Transactions"), this);
 276      historyAction->setStatusTip(tr("Browse transaction history"));
 277      historyAction->setToolTip(historyAction->statusTip());
 278      historyAction->setCheckable(true);
 279      historyAction->setShortcut(QKeySequence(QStringLiteral("Alt+4")));
 280      tabGroup->addAction(historyAction);
 281  
 282  #ifdef ENABLE_WALLET
 283      // These showNormalIfMinimized are needed because Send Coins and Receive Coins
 284      // can be triggered from the tray menu, and need to show the GUI to be useful.
 285      connect(overviewAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
 286      connect(overviewAction, &QAction::triggered, this, &BitcoinGUI::gotoOverviewPage);
 287      connect(sendCoinsAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
 288      connect(sendCoinsAction, &QAction::triggered, [this]{ gotoSendCoinsPage(); });
 289      connect(receiveCoinsAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
 290      connect(receiveCoinsAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage);
 291      connect(historyAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
 292      connect(historyAction, &QAction::triggered, this, &BitcoinGUI::gotoHistoryPage);
 293  #endif // ENABLE_WALLET
 294  
 295      quitAction = new QAction(tr("E&xit"), this);
 296      quitAction->setStatusTip(tr("Quit application"));
 297      quitAction->setShortcut(QKeySequence(tr("Ctrl+Q")));
 298      quitAction->setMenuRole(QAction::QuitRole);
 299      aboutAction = new QAction(tr("&About %1").arg(PACKAGE_NAME), this);
 300      aboutAction->setStatusTip(tr("Show information about %1").arg(PACKAGE_NAME));
 301      aboutAction->setMenuRole(QAction::AboutRole);
 302      aboutAction->setEnabled(false);
 303      aboutQtAction = new QAction(tr("About &Qt"), this);
 304      aboutQtAction->setStatusTip(tr("Show information about Qt"));
 305      aboutQtAction->setMenuRole(QAction::AboutQtRole);
 306      optionsAction = new QAction(tr("&Options…"), this);
 307      optionsAction->setStatusTip(tr("Modify configuration options for %1").arg(PACKAGE_NAME));
 308      optionsAction->setMenuRole(QAction::PreferencesRole);
 309      optionsAction->setEnabled(false);
 310  
 311      encryptWalletAction = new QAction(tr("&Encrypt Wallet…"), this);
 312      encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet"));
 313      encryptWalletAction->setCheckable(true);
 314      backupWalletAction = new QAction(tr("&Backup Wallet…"), this);
 315      backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
 316      changePassphraseAction = new QAction(tr("&Change Passphrase…"), this);
 317      changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption"));
 318      signMessageAction = new QAction(tr("Sign &message…"), this);
 319      signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
 320      verifyMessageAction = new QAction(tr("&Verify message…"), this);
 321      verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
 322      m_load_psbt_action = new QAction(tr("&Load PSBT from file…"), this);
 323      m_load_psbt_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction"));
 324      m_load_psbt_clipboard_action = new QAction(tr("Load PSBT from &clipboard…"), this);
 325      m_load_psbt_clipboard_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction from clipboard"));
 326  
 327      openRPCConsoleAction = new QAction(tr("Node window"), this);
 328      openRPCConsoleAction->setStatusTip(tr("Open node debugging and diagnostic console"));
 329      // initially disable the debug window menu item
 330      openRPCConsoleAction->setEnabled(false);
 331      openRPCConsoleAction->setObjectName("openRPCConsoleAction");
 332  
 333      usedSendingAddressesAction = new QAction(tr("&Sending addresses"), this);
 334      usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels"));
 335      usedReceivingAddressesAction = new QAction(tr("&Receiving addresses"), this);
 336      usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels"));
 337  
 338      openAction = new QAction(tr("Open &URI…"), this);
 339      openAction->setStatusTip(tr("Open a bitcoin: URI"));
 340  
 341      m_open_wallet_action = new QAction(tr("Open Wallet"), this);
 342      m_open_wallet_action->setEnabled(false);
 343      m_open_wallet_action->setStatusTip(tr("Open a wallet"));
 344      m_open_wallet_menu = new QMenu(this);
 345  
 346      m_close_wallet_action = new QAction(tr("Close Wallet…"), this);
 347      m_close_wallet_action->setStatusTip(tr("Close wallet"));
 348  
 349      m_create_wallet_action = new QAction(tr("Create Wallet…"), this);
 350      m_create_wallet_action->setEnabled(false);
 351      m_create_wallet_action->setStatusTip(tr("Create a new wallet"));
 352  
 353      //: Name of the menu item that restores wallet from a backup file.
 354      m_restore_wallet_action = new QAction(tr("Restore Wallet…"), this);
 355      m_restore_wallet_action->setEnabled(false);
 356      //: Status tip for Restore Wallet menu item
 357      m_restore_wallet_action->setStatusTip(tr("Restore a wallet from a backup file"));
 358  
 359      m_close_all_wallets_action = new QAction(tr("Close All Wallets…"), this);
 360      m_close_all_wallets_action->setStatusTip(tr("Close all wallets"));
 361  
 362      m_migrate_wallet_action = new QAction(tr("Migrate Wallet"), this);
 363      m_migrate_wallet_action->setEnabled(false);
 364      m_migrate_wallet_action->setStatusTip(tr("Migrate a wallet"));
 365  
 366      showHelpMessageAction = new QAction(tr("&Command-line options"), this);
 367      showHelpMessageAction->setMenuRole(QAction::NoRole);
 368      showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(PACKAGE_NAME));
 369  
 370      m_mask_values_action = new QAction(tr("&Mask values"), this);
 371      m_mask_values_action->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_M));
 372      m_mask_values_action->setStatusTip(tr("Mask the values in the Overview tab"));
 373      m_mask_values_action->setCheckable(true);
 374  
 375      connect(quitAction, &QAction::triggered, this, &BitcoinGUI::quitRequested);
 376      connect(aboutAction, &QAction::triggered, this, &BitcoinGUI::aboutClicked);
 377      connect(aboutQtAction, &QAction::triggered, qApp, QApplication::aboutQt);
 378      connect(optionsAction, &QAction::triggered, this, &BitcoinGUI::optionsClicked);
 379      connect(showHelpMessageAction, &QAction::triggered, this, &BitcoinGUI::showHelpMessageClicked);
 380      connect(openRPCConsoleAction, &QAction::triggered, this, &BitcoinGUI::showDebugWindow);
 381      // prevents an open debug window from becoming stuck/unusable on client shutdown
 382      connect(quitAction, &QAction::triggered, rpcConsole, &QWidget::hide);
 383  
 384  #ifdef ENABLE_WALLET
 385      if(walletFrame)
 386      {
 387          connect(encryptWalletAction, &QAction::triggered, walletFrame, &WalletFrame::encryptWallet);
 388          connect(backupWalletAction, &QAction::triggered, walletFrame, &WalletFrame::backupWallet);
 389          connect(changePassphraseAction, &QAction::triggered, walletFrame, &WalletFrame::changePassphrase);
 390          connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
 391          connect(signMessageAction, &QAction::triggered, [this]{ gotoSignMessageTab(); });
 392          connect(m_load_psbt_action, &QAction::triggered, [this]{ gotoLoadPSBT(); });
 393          connect(m_load_psbt_clipboard_action, &QAction::triggered, [this]{ gotoLoadPSBT(true); });
 394          connect(verifyMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
 395          connect(verifyMessageAction, &QAction::triggered, [this]{ gotoVerifyMessageTab(); });
 396          connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
 397          connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses);
 398          connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked);
 399          connect(m_open_wallet_menu, &QMenu::aboutToShow, [this] {
 400              m_open_wallet_menu->clear();
 401              for (const std::pair<const std::string, bool>& i : m_wallet_controller->listWalletDir()) {
 402                  const std::string& path = i.first;
 403                  QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
 404                  // Menu items remove single &. Single & are shown when && is in
 405                  // the string, but only the first occurrence. So replace only
 406                  // the first & with &&.
 407                  name.replace(name.indexOf(QChar('&')), 1, QString("&&"));
 408                  QAction* action = m_open_wallet_menu->addAction(name);
 409  
 410                  if (i.second) {
 411                      // This wallet is already loaded
 412                      action->setEnabled(false);
 413                      continue;
 414                  }
 415  
 416                  connect(action, &QAction::triggered, [this, path] {
 417                      auto activity = new OpenWalletActivity(m_wallet_controller, this);
 418                      connect(activity, &OpenWalletActivity::opened, this, &BitcoinGUI::setCurrentWallet, Qt::QueuedConnection);
 419                      connect(activity, &OpenWalletActivity::opened, rpcConsole, &RPCConsole::setCurrentWallet, Qt::QueuedConnection);
 420                      activity->open(path);
 421                  });
 422              }
 423              if (m_open_wallet_menu->isEmpty()) {
 424                  QAction* action = m_open_wallet_menu->addAction(tr("No wallets available"));
 425                  action->setEnabled(false);
 426              }
 427          });
 428          connect(m_restore_wallet_action, &QAction::triggered, [this] {
 429              //: Name of the wallet data file format.
 430              QString name_data_file = tr("Wallet Data");
 431  
 432              //: The title for Restore Wallet File Windows
 433              QString title_windows = tr("Load Wallet Backup");
 434  
 435              QString backup_file = GUIUtil::getOpenFileName(this, title_windows, QString(), name_data_file + QLatin1String(" (*.dat)"), nullptr);
 436              if (backup_file.isEmpty()) return;
 437  
 438              bool wallet_name_ok;
 439              /*: Title of pop-up window shown when the user is attempting to
 440                  restore a wallet. */
 441              QString title = tr("Restore Wallet");
 442              //: Label of the input field where the name of the wallet is entered.
 443              QString label = tr("Wallet Name");
 444              QString wallet_name = QInputDialog::getText(this, title, label, QLineEdit::Normal, "", &wallet_name_ok);
 445              if (!wallet_name_ok || wallet_name.isEmpty()) return;
 446  
 447              auto activity = new RestoreWalletActivity(m_wallet_controller, this);
 448              connect(activity, &RestoreWalletActivity::restored, this, &BitcoinGUI::setCurrentWallet, Qt::QueuedConnection);
 449              connect(activity, &RestoreWalletActivity::restored, rpcConsole, &RPCConsole::setCurrentWallet, Qt::QueuedConnection);
 450  
 451              auto backup_file_path = fs::PathFromString(backup_file.toStdString());
 452              activity->restore(backup_file_path, wallet_name.toStdString());
 453          });
 454          connect(m_close_wallet_action, &QAction::triggered, [this] {
 455              m_wallet_controller->closeWallet(walletFrame->currentWalletModel(), this);
 456          });
 457          connect(m_create_wallet_action, &QAction::triggered, this, &BitcoinGUI::createWallet);
 458          connect(m_close_all_wallets_action, &QAction::triggered, [this] {
 459              m_wallet_controller->closeAllWallets(this);
 460          });
 461          connect(m_migrate_wallet_action, &QAction::triggered, [this] {
 462              auto activity = new MigrateWalletActivity(m_wallet_controller, this);
 463              connect(activity, &MigrateWalletActivity::migrated, this, &BitcoinGUI::setCurrentWallet);
 464              activity->migrate(walletFrame->currentWalletModel());
 465          });
 466          connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy);
 467          connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::enableHistoryAction);
 468      }
 469  #endif // ENABLE_WALLET
 470  
 471      connect(new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_C), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindowActivateConsole);
 472      connect(new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_D), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindow);
 473  }
 474  
 475  void BitcoinGUI::createMenuBar()
 476  {
 477      appMenuBar = menuBar();
 478  
 479      // Configure the menus
 480      QMenu *file = appMenuBar->addMenu(tr("&File"));
 481      if(walletFrame)
 482      {
 483          file->addAction(m_create_wallet_action);
 484          file->addAction(m_open_wallet_action);
 485          file->addAction(m_close_wallet_action);
 486          file->addAction(m_close_all_wallets_action);
 487          file->addAction(m_migrate_wallet_action);
 488          file->addSeparator();
 489          file->addAction(backupWalletAction);
 490          file->addAction(m_restore_wallet_action);
 491          file->addSeparator();
 492          file->addAction(openAction);
 493          file->addAction(signMessageAction);
 494          file->addAction(verifyMessageAction);
 495          file->addAction(m_load_psbt_action);
 496          file->addAction(m_load_psbt_clipboard_action);
 497          file->addSeparator();
 498      }
 499      file->addAction(quitAction);
 500  
 501      QMenu *settings = appMenuBar->addMenu(tr("&Settings"));
 502      if(walletFrame)
 503      {
 504          settings->addAction(encryptWalletAction);
 505          settings->addAction(changePassphraseAction);
 506          settings->addSeparator();
 507          settings->addAction(m_mask_values_action);
 508          settings->addSeparator();
 509      }
 510      settings->addAction(optionsAction);
 511  
 512      QMenu* window_menu = appMenuBar->addMenu(tr("&Window"));
 513  
 514      QAction* minimize_action = window_menu->addAction(tr("&Minimize"));
 515      minimize_action->setShortcut(QKeySequence(tr("Ctrl+M")));
 516      connect(minimize_action, &QAction::triggered, [] {
 517          QApplication::activeWindow()->showMinimized();
 518      });
 519      connect(qApp, &QApplication::focusWindowChanged, this, [minimize_action] (QWindow* window) {
 520          minimize_action->setEnabled(window != nullptr && (window->flags() & Qt::Dialog) != Qt::Dialog && window->windowState() != Qt::WindowMinimized);
 521      });
 522  
 523  #ifdef Q_OS_MACOS
 524      QAction* zoom_action = window_menu->addAction(tr("Zoom"));
 525      connect(zoom_action, &QAction::triggered, [] {
 526          QWindow* window = qApp->focusWindow();
 527          if (window->windowState() != Qt::WindowMaximized) {
 528              window->showMaximized();
 529          } else {
 530              window->showNormal();
 531          }
 532      });
 533  
 534      connect(qApp, &QApplication::focusWindowChanged, this, [zoom_action] (QWindow* window) {
 535          zoom_action->setEnabled(window != nullptr);
 536      });
 537  #endif
 538  
 539      if (walletFrame) {
 540  #ifdef Q_OS_MACOS
 541          window_menu->addSeparator();
 542          QAction* main_window_action = window_menu->addAction(tr("Main Window"));
 543          connect(main_window_action, &QAction::triggered, [this] {
 544              GUIUtil::bringToFront(this);
 545          });
 546  #endif
 547          window_menu->addSeparator();
 548          window_menu->addAction(usedSendingAddressesAction);
 549          window_menu->addAction(usedReceivingAddressesAction);
 550      }
 551  
 552      window_menu->addSeparator();
 553      for (RPCConsole::TabTypes tab_type : rpcConsole->tabs()) {
 554          QAction* tab_action = window_menu->addAction(rpcConsole->tabTitle(tab_type));
 555          tab_action->setShortcut(rpcConsole->tabShortcut(tab_type));
 556          connect(tab_action, &QAction::triggered, [this, tab_type] {
 557              rpcConsole->setTabFocus(tab_type);
 558              showDebugWindow();
 559          });
 560      }
 561  
 562      QMenu *help = appMenuBar->addMenu(tr("&Help"));
 563      help->addAction(showHelpMessageAction);
 564      help->addSeparator();
 565      help->addAction(aboutAction);
 566      help->addAction(aboutQtAction);
 567  }
 568  
 569  void BitcoinGUI::createToolBars()
 570  {
 571      if(walletFrame)
 572      {
 573          QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
 574          appToolBar = toolbar;
 575          toolbar->setMovable(false);
 576          toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
 577          toolbar->addAction(overviewAction);
 578          toolbar->addAction(sendCoinsAction);
 579          toolbar->addAction(receiveCoinsAction);
 580          toolbar->addAction(historyAction);
 581          overviewAction->setChecked(true);
 582  
 583  #ifdef ENABLE_WALLET
 584          QWidget *spacer = new QWidget();
 585          spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 586          toolbar->addWidget(spacer);
 587  
 588          m_wallet_selector = new QComboBox();
 589          m_wallet_selector->setSizeAdjustPolicy(QComboBox::AdjustToContents);
 590          connect(m_wallet_selector, qOverload<int>(&QComboBox::currentIndexChanged), this, &BitcoinGUI::setCurrentWalletBySelectorIndex);
 591  
 592          m_wallet_selector_label = new QLabel();
 593          m_wallet_selector_label->setText(tr("Wallet:") + " ");
 594          m_wallet_selector_label->setBuddy(m_wallet_selector);
 595  
 596          m_wallet_selector_label_action = appToolBar->addWidget(m_wallet_selector_label);
 597          m_wallet_selector_action = appToolBar->addWidget(m_wallet_selector);
 598  
 599          m_wallet_selector_label_action->setVisible(false);
 600          m_wallet_selector_action->setVisible(false);
 601  #endif
 602      }
 603  }
 604  
 605  void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndHeaderTipInfo* tip_info)
 606  {
 607      this->clientModel = _clientModel;
 608      if(_clientModel)
 609      {
 610          // Create system tray menu (or setup the dock menu) that late to prevent users from calling actions,
 611          // while the client has not yet fully loaded
 612          createTrayIconMenu();
 613  
 614          // Keep up to date with client
 615          setNetworkActive(m_node.getNetworkActive());
 616          connect(connectionsControl, &GUIUtil::ClickableLabel::clicked, [this] {
 617              GUIUtil::PopupMenu(m_network_context_menu, QCursor::pos());
 618          });
 619          connect(_clientModel, &ClientModel::numConnectionsChanged, this, &BitcoinGUI::setNumConnections);
 620          connect(_clientModel, &ClientModel::networkActiveChanged, this, &BitcoinGUI::setNetworkActive);
 621  
 622          modalOverlay->setKnownBestHeight(tip_info->header_height, QDateTime::fromSecsSinceEpoch(tip_info->header_time), /*presync=*/false);
 623          setNumBlocks(tip_info->block_height, QDateTime::fromSecsSinceEpoch(tip_info->block_time), tip_info->verification_progress, SyncType::BLOCK_SYNC, SynchronizationState::INIT_DOWNLOAD);
 624          connect(_clientModel, &ClientModel::numBlocksChanged, this, &BitcoinGUI::setNumBlocks);
 625  
 626          // Receive and report messages from client model
 627          connect(_clientModel, &ClientModel::message, [this](const QString &title, const QString &message, unsigned int style){
 628              this->message(title, message, style);
 629          });
 630  
 631          // Show progress dialog
 632          connect(_clientModel, &ClientModel::showProgress, this, &BitcoinGUI::showProgress);
 633  
 634          rpcConsole->setClientModel(_clientModel, tip_info->block_height, tip_info->block_time, tip_info->verification_progress);
 635  
 636          updateProxyIcon();
 637  
 638  #ifdef ENABLE_WALLET
 639          if(walletFrame)
 640          {
 641              walletFrame->setClientModel(_clientModel);
 642          }
 643  #endif // ENABLE_WALLET
 644          unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel());
 645  
 646          OptionsModel* optionsModel = _clientModel->getOptionsModel();
 647          if (optionsModel && trayIcon) {
 648              // be aware of the tray icon disable state change reported by the OptionsModel object.
 649              connect(optionsModel, &OptionsModel::showTrayIconChanged, trayIcon, &QSystemTrayIcon::setVisible);
 650  
 651              // initialize the disable state of the tray icon with the current value in the model.
 652              trayIcon->setVisible(optionsModel->getShowTrayIcon());
 653          }
 654  
 655          m_mask_values_action->setChecked(_clientModel->getOptionsModel()->getOption(OptionsModel::OptionID::MaskValues).toBool());
 656      } else {
 657          // Shutdown requested, disable menus
 658          if (trayIconMenu)
 659          {
 660              // Disable context menu on tray icon
 661              trayIconMenu->clear();
 662          }
 663          // Propagate cleared model to child objects
 664          rpcConsole->setClientModel(nullptr);
 665  #ifdef ENABLE_WALLET
 666          if (walletFrame)
 667          {
 668              walletFrame->setClientModel(nullptr);
 669          }
 670  #endif // ENABLE_WALLET
 671          unitDisplayControl->setOptionsModel(nullptr);
 672          // Disable top bar menu actions
 673          appMenuBar->clear();
 674      }
 675  }
 676  
 677  #ifdef ENABLE_WALLET
 678  void BitcoinGUI::enableHistoryAction(bool privacy)
 679  {
 680      if (walletFrame->currentWalletModel()) {
 681          historyAction->setEnabled(!privacy);
 682          if (historyAction->isChecked()) gotoOverviewPage();
 683      }
 684  }
 685  
 686  void BitcoinGUI::setWalletController(WalletController* wallet_controller, bool show_loading_minimized)
 687  {
 688      assert(!m_wallet_controller);
 689      assert(wallet_controller);
 690  
 691      m_wallet_controller = wallet_controller;
 692  
 693      m_create_wallet_action->setEnabled(true);
 694      m_open_wallet_action->setEnabled(true);
 695      m_open_wallet_action->setMenu(m_open_wallet_menu);
 696      m_restore_wallet_action->setEnabled(true);
 697  
 698      GUIUtil::ExceptionSafeConnect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet);
 699      connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet);
 700      connect(wallet_controller, &WalletController::destroyed, this, [this] {
 701          // wallet_controller gets destroyed manually, but it leaves our member copy dangling
 702          m_wallet_controller = nullptr;
 703      });
 704  
 705      auto activity = new LoadWalletsActivity(m_wallet_controller, this);
 706      activity->load(show_loading_minimized);
 707  }
 708  
 709  WalletController* BitcoinGUI::getWalletController()
 710  {
 711      return m_wallet_controller;
 712  }
 713  
 714  void BitcoinGUI::addWallet(WalletModel* walletModel)
 715  {
 716      if (!walletFrame || !m_wallet_controller) return;
 717  
 718      WalletView* wallet_view = new WalletView(walletModel, platformStyle, walletFrame);
 719      if (!walletFrame->addView(wallet_view)) return;
 720  
 721      rpcConsole->addWallet(walletModel);
 722      if (m_wallet_selector->count() == 0) {
 723          setWalletActionsEnabled(true);
 724      } else if (m_wallet_selector->count() == 1) {
 725          m_wallet_selector_label_action->setVisible(true);
 726          m_wallet_selector_action->setVisible(true);
 727      }
 728  
 729      connect(wallet_view, &WalletView::outOfSyncWarningClicked, this, &BitcoinGUI::showModalOverlay);
 730      connect(wallet_view, &WalletView::transactionClicked, this, &BitcoinGUI::gotoHistoryPage);
 731      connect(wallet_view, &WalletView::coinsSent, this, &BitcoinGUI::gotoHistoryPage);
 732      connect(wallet_view, &WalletView::message, [this](const QString& title, const QString& message, unsigned int style) {
 733          this->message(title, message, style);
 734      });
 735      connect(wallet_view, &WalletView::encryptionStatusChanged, this, &BitcoinGUI::updateWalletStatus);
 736      connect(wallet_view, &WalletView::incomingTransaction, this, &BitcoinGUI::incomingTransaction);
 737      connect(this, &BitcoinGUI::setPrivacy, wallet_view, &WalletView::setPrivacy);
 738      const bool privacy = isPrivacyModeActivated();
 739      wallet_view->setPrivacy(privacy);
 740      enableHistoryAction(privacy);
 741      const QString display_name = walletModel->getDisplayName();
 742      m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
 743  }
 744  
 745  void BitcoinGUI::removeWallet(WalletModel* walletModel)
 746  {
 747      if (!walletFrame) return;
 748  
 749      labelWalletHDStatusIcon->hide();
 750      labelWalletEncryptionIcon->hide();
 751  
 752      int index = m_wallet_selector->findData(QVariant::fromValue(walletModel));
 753      m_wallet_selector->removeItem(index);
 754      if (m_wallet_selector->count() == 0) {
 755          setWalletActionsEnabled(false);
 756          overviewAction->setChecked(true);
 757      } else if (m_wallet_selector->count() == 1) {
 758          m_wallet_selector_label_action->setVisible(false);
 759          m_wallet_selector_action->setVisible(false);
 760      }
 761      rpcConsole->removeWallet(walletModel);
 762      walletFrame->removeWallet(walletModel);
 763      updateWindowTitle();
 764  }
 765  
 766  void BitcoinGUI::setCurrentWallet(WalletModel* wallet_model)
 767  {
 768      if (!walletFrame || !m_wallet_controller) return;
 769      walletFrame->setCurrentWallet(wallet_model);
 770      for (int index = 0; index < m_wallet_selector->count(); ++index) {
 771          if (m_wallet_selector->itemData(index).value<WalletModel*>() == wallet_model) {
 772              m_wallet_selector->setCurrentIndex(index);
 773              break;
 774          }
 775      }
 776      updateWindowTitle();
 777      m_migrate_wallet_action->setEnabled(wallet_model->wallet().isLegacy());
 778  }
 779  
 780  void BitcoinGUI::setCurrentWalletBySelectorIndex(int index)
 781  {
 782      WalletModel* wallet_model = m_wallet_selector->itemData(index).value<WalletModel*>();
 783      if (wallet_model) setCurrentWallet(wallet_model);
 784  }
 785  
 786  void BitcoinGUI::removeAllWallets()
 787  {
 788      if(!walletFrame)
 789          return;
 790      setWalletActionsEnabled(false);
 791      walletFrame->removeAllWallets();
 792  }
 793  #endif // ENABLE_WALLET
 794  
 795  void BitcoinGUI::setWalletActionsEnabled(bool enabled)
 796  {
 797      overviewAction->setEnabled(enabled);
 798      sendCoinsAction->setEnabled(enabled);
 799      receiveCoinsAction->setEnabled(enabled);
 800      historyAction->setEnabled(enabled);
 801      encryptWalletAction->setEnabled(enabled);
 802      backupWalletAction->setEnabled(enabled);
 803      changePassphraseAction->setEnabled(enabled);
 804      signMessageAction->setEnabled(enabled);
 805      verifyMessageAction->setEnabled(enabled);
 806      usedSendingAddressesAction->setEnabled(enabled);
 807      usedReceivingAddressesAction->setEnabled(enabled);
 808      openAction->setEnabled(enabled);
 809      m_close_wallet_action->setEnabled(enabled);
 810      m_close_all_wallets_action->setEnabled(enabled);
 811      m_migrate_wallet_action->setEnabled(enabled);
 812  }
 813  
 814  void BitcoinGUI::createTrayIcon()
 815  {
 816      assert(QSystemTrayIcon::isSystemTrayAvailable());
 817  
 818  #ifndef Q_OS_MACOS
 819      if (QSystemTrayIcon::isSystemTrayAvailable()) {
 820          trayIcon = new QSystemTrayIcon(m_network_style->getTrayAndWindowIcon(), this);
 821          QString toolTip = tr("%1 client").arg(PACKAGE_NAME) + " " + m_network_style->getTitleAddText();
 822          trayIcon->setToolTip(toolTip);
 823      }
 824  #endif
 825  }
 826  
 827  void BitcoinGUI::createTrayIconMenu()
 828  {
 829  #ifndef Q_OS_MACOS
 830      if (!trayIcon) return;
 831  #endif // Q_OS_MACOS
 832  
 833      // Configuration of the tray icon (or Dock icon) menu.
 834      QAction* show_hide_action{nullptr};
 835  #ifndef Q_OS_MACOS
 836      // Note: On macOS, the Dock icon's menu already has Show / Hide action.
 837      show_hide_action = trayIconMenu->addAction(QString(), this, &BitcoinGUI::toggleHidden);
 838      trayIconMenu->addSeparator();
 839  #endif // Q_OS_MACOS
 840  
 841      QAction* send_action{nullptr};
 842      QAction* receive_action{nullptr};
 843      QAction* sign_action{nullptr};
 844      QAction* verify_action{nullptr};
 845      if (enableWallet) {
 846          send_action = trayIconMenu->addAction(sendCoinsAction->text(), sendCoinsAction, &QAction::trigger);
 847          receive_action = trayIconMenu->addAction(receiveCoinsAction->text(), receiveCoinsAction, &QAction::trigger);
 848          trayIconMenu->addSeparator();
 849          sign_action = trayIconMenu->addAction(signMessageAction->text(), signMessageAction, &QAction::trigger);
 850          verify_action = trayIconMenu->addAction(verifyMessageAction->text(), verifyMessageAction, &QAction::trigger);
 851          trayIconMenu->addSeparator();
 852      }
 853      QAction* options_action = trayIconMenu->addAction(optionsAction->text(), optionsAction, &QAction::trigger);
 854      options_action->setMenuRole(QAction::PreferencesRole);
 855      QAction* node_window_action = trayIconMenu->addAction(openRPCConsoleAction->text(), openRPCConsoleAction, &QAction::trigger);
 856      QAction* quit_action{nullptr};
 857  #ifndef Q_OS_MACOS
 858      // Note: On macOS, the Dock icon's menu already has Quit action.
 859      trayIconMenu->addSeparator();
 860      quit_action = trayIconMenu->addAction(quitAction->text(), quitAction, &QAction::trigger);
 861  
 862      trayIcon->setContextMenu(trayIconMenu.get());
 863      connect(trayIcon, &QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason reason) {
 864          if (reason == QSystemTrayIcon::Trigger) {
 865              // Click on system tray icon triggers show/hide of the main window
 866              toggleHidden();
 867          }
 868      });
 869  #else
 870      // Note: On macOS, the Dock icon is used to provide the tray's functionality.
 871      MacDockIconHandler* dockIconHandler = MacDockIconHandler::instance();
 872      connect(dockIconHandler, &MacDockIconHandler::dockIconClicked, [this] {
 873          if (m_node.shutdownRequested()) return; // nothing to show, node is shutting down.
 874          show();
 875          activateWindow();
 876      });
 877      trayIconMenu->setAsDockMenu();
 878  #endif // Q_OS_MACOS
 879  
 880      connect(
 881          // Using QSystemTrayIcon::Context is not reliable.
 882          // See https://bugreports.qt.io/browse/QTBUG-91697
 883          trayIconMenu.get(), &QMenu::aboutToShow,
 884          [this, show_hide_action, send_action, receive_action, sign_action, verify_action, options_action, node_window_action, quit_action] {
 885              if (m_node.shutdownRequested()) return; // nothing to do, node is shutting down.
 886  
 887              if (show_hide_action) show_hide_action->setText(
 888                  (!isHidden() && !isMinimized() && !GUIUtil::isObscured(this)) ?
 889                      tr("&Hide") :
 890                      tr("S&how"));
 891              if (QApplication::activeModalWidget()) {
 892                  for (QAction* a : trayIconMenu.get()->actions()) {
 893                      a->setEnabled(false);
 894                  }
 895              } else {
 896                  if (show_hide_action) show_hide_action->setEnabled(true);
 897                  if (enableWallet) {
 898                      send_action->setEnabled(sendCoinsAction->isEnabled());
 899                      receive_action->setEnabled(receiveCoinsAction->isEnabled());
 900                      sign_action->setEnabled(signMessageAction->isEnabled());
 901                      verify_action->setEnabled(verifyMessageAction->isEnabled());
 902                  }
 903                  options_action->setEnabled(optionsAction->isEnabled());
 904                  node_window_action->setEnabled(openRPCConsoleAction->isEnabled());
 905                  if (quit_action) quit_action->setEnabled(true);
 906              }
 907          });
 908  }
 909  
 910  void BitcoinGUI::optionsClicked()
 911  {
 912      openOptionsDialogWithTab(OptionsDialog::TAB_MAIN);
 913  }
 914  
 915  void BitcoinGUI::aboutClicked()
 916  {
 917      if(!clientModel)
 918          return;
 919  
 920      auto dlg = new HelpMessageDialog(this, /*about=*/true);
 921      GUIUtil::ShowModalDialogAsynchronously(dlg);
 922  }
 923  
 924  void BitcoinGUI::showDebugWindow()
 925  {
 926      GUIUtil::bringToFront(rpcConsole);
 927      Q_EMIT consoleShown(rpcConsole);
 928  }
 929  
 930  void BitcoinGUI::showDebugWindowActivateConsole()
 931  {
 932      rpcConsole->setTabFocus(RPCConsole::TabTypes::CONSOLE);
 933      showDebugWindow();
 934  }
 935  
 936  void BitcoinGUI::showHelpMessageClicked()
 937  {
 938      GUIUtil::bringToFront(helpMessageDialog);
 939  }
 940  
 941  #ifdef ENABLE_WALLET
 942  void BitcoinGUI::openClicked()
 943  {
 944      OpenURIDialog dlg(platformStyle, this);
 945      if(dlg.exec())
 946      {
 947          Q_EMIT receivedURI(dlg.getURI());
 948      }
 949  }
 950  
 951  void BitcoinGUI::gotoOverviewPage()
 952  {
 953      overviewAction->setChecked(true);
 954      if (walletFrame) walletFrame->gotoOverviewPage();
 955  }
 956  
 957  void BitcoinGUI::gotoHistoryPage()
 958  {
 959      historyAction->setChecked(true);
 960      if (walletFrame) walletFrame->gotoHistoryPage();
 961  }
 962  
 963  void BitcoinGUI::gotoReceiveCoinsPage()
 964  {
 965      receiveCoinsAction->setChecked(true);
 966      if (walletFrame) walletFrame->gotoReceiveCoinsPage();
 967  }
 968  
 969  void BitcoinGUI::gotoSendCoinsPage(QString addr)
 970  {
 971      sendCoinsAction->setChecked(true);
 972      if (walletFrame) walletFrame->gotoSendCoinsPage(addr);
 973  }
 974  
 975  void BitcoinGUI::gotoSignMessageTab(QString addr)
 976  {
 977      if (walletFrame) walletFrame->gotoSignMessageTab(addr);
 978  }
 979  
 980  void BitcoinGUI::gotoVerifyMessageTab(QString addr)
 981  {
 982      if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
 983  }
 984  void BitcoinGUI::gotoLoadPSBT(bool from_clipboard)
 985  {
 986      if (walletFrame) walletFrame->gotoLoadPSBT(from_clipboard);
 987  }
 988  #endif // ENABLE_WALLET
 989  
 990  void BitcoinGUI::updateNetworkState()
 991  {
 992      if (!clientModel) return;
 993      int count = clientModel->getNumConnections();
 994      QString icon;
 995      switch(count)
 996      {
 997      case 0: icon = ":/icons/connect_0"; break;
 998      case 1: case 2: case 3: icon = ":/icons/connect_1"; break;
 999      case 4: case 5: case 6: icon = ":/icons/connect_2"; break;
1000      case 7: case 8: case 9: icon = ":/icons/connect_3"; break;
1001      default: icon = ":/icons/connect_4"; break;
1002      }
1003  
1004      QString tooltip;
1005  
1006      if (m_node.getNetworkActive()) {
1007          //: A substring of the tooltip.
1008          tooltip = tr("%n active connection(s) to Bitcoin network.", "", count);
1009      } else {
1010          //: A substring of the tooltip.
1011          tooltip = tr("Network activity disabled.");
1012          icon = ":/icons/network_disabled";
1013      }
1014  
1015      // Don't word-wrap this (fixed-width) tooltip
1016      tooltip = QLatin1String("<nobr>") + tooltip + QLatin1String("<br>") +
1017                //: A substring of the tooltip. "More actions" are available via the context menu.
1018                tr("Click for more actions.") + QLatin1String("</nobr>");
1019      connectionsControl->setToolTip(tooltip);
1020  
1021      connectionsControl->setThemedPixmap(icon, STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1022  }
1023  
1024  void BitcoinGUI::setNumConnections(int count)
1025  {
1026      updateNetworkState();
1027  }
1028  
1029  void BitcoinGUI::setNetworkActive(bool network_active)
1030  {
1031      updateNetworkState();
1032      m_network_context_menu->clear();
1033      m_network_context_menu->addAction(
1034          //: A context menu item. The "Peers tab" is an element of the "Node window".
1035          tr("Show Peers tab"),
1036          [this] {
1037              rpcConsole->setTabFocus(RPCConsole::TabTypes::PEERS);
1038              showDebugWindow();
1039          });
1040      m_network_context_menu->addAction(
1041          network_active ?
1042              //: A context menu item.
1043              tr("Disable network activity") :
1044              //: A context menu item. The network activity was disabled previously.
1045              tr("Enable network activity"),
1046          [this, new_state = !network_active] { m_node.setNetworkActive(new_state); });
1047  }
1048  
1049  void BitcoinGUI::updateHeadersSyncProgressLabel()
1050  {
1051      int64_t headersTipTime = clientModel->getHeaderTipTime();
1052      int headersTipHeight = clientModel->getHeaderTipHeight();
1053      int estHeadersLeft = (GetTime() - headersTipTime) / Params().GetConsensus().nPowTargetSpacing;
1054      if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC)
1055          progressBarLabel->setText(tr("Syncing Headers (%1%)…").arg(QString::number(100.0 / (headersTipHeight+estHeadersLeft)*headersTipHeight, 'f', 1)));
1056  }
1057  
1058  void BitcoinGUI::updateHeadersPresyncProgressLabel(int64_t height, const QDateTime& blockDate)
1059  {
1060      int estHeadersLeft = blockDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing;
1061      if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC)
1062          progressBarLabel->setText(tr("Pre-syncing Headers (%1%)…").arg(QString::number(100.0 / (height+estHeadersLeft)*height, 'f', 1)));
1063  }
1064  
1065  void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
1066  {
1067      if (!clientModel || !clientModel->getOptionsModel())
1068          return;
1069  
1070      auto dlg = new OptionsDialog(this, enableWallet);
1071      connect(dlg, &OptionsDialog::quitOnReset, this, &BitcoinGUI::quitRequested);
1072      dlg->setCurrentTab(tab);
1073      dlg->setClientModel(clientModel);
1074      dlg->setModel(clientModel->getOptionsModel());
1075      GUIUtil::ShowModalDialogAsynchronously(dlg);
1076  }
1077  
1078  void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state)
1079  {
1080  // Disabling macOS App Nap on initial sync, disk and reindex operations.
1081  #ifdef Q_OS_MACOS
1082      if (sync_state == SynchronizationState::POST_INIT) {
1083          m_app_nap_inhibitor->enableAppNap();
1084      } else {
1085          m_app_nap_inhibitor->disableAppNap();
1086      }
1087  #endif
1088  
1089      if (modalOverlay)
1090      {
1091          if (synctype != SyncType::BLOCK_SYNC)
1092              modalOverlay->setKnownBestHeight(count, blockDate, synctype == SyncType::HEADER_PRESYNC);
1093          else
1094              modalOverlay->tipUpdate(count, blockDate, nVerificationProgress);
1095      }
1096      if (!clientModel)
1097          return;
1098  
1099      // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbled text)
1100      statusBar()->clearMessage();
1101  
1102      // Acquire current block source
1103      BlockSource blockSource{clientModel->getBlockSource()};
1104      switch (blockSource) {
1105          case BlockSource::NETWORK:
1106              if (synctype == SyncType::HEADER_PRESYNC) {
1107                  updateHeadersPresyncProgressLabel(count, blockDate);
1108                  return;
1109              } else if (synctype == SyncType::HEADER_SYNC) {
1110                  updateHeadersSyncProgressLabel();
1111                  return;
1112              }
1113              progressBarLabel->setText(tr("Synchronizing with network…"));
1114              updateHeadersSyncProgressLabel();
1115              break;
1116          case BlockSource::DISK:
1117              if (synctype != SyncType::BLOCK_SYNC) {
1118                  progressBarLabel->setText(tr("Indexing blocks on disk…"));
1119              } else {
1120                  progressBarLabel->setText(tr("Processing blocks on disk…"));
1121              }
1122              break;
1123          case BlockSource::NONE:
1124              if (synctype != SyncType::BLOCK_SYNC) {
1125                  return;
1126              }
1127              progressBarLabel->setText(tr("Connecting to peers…"));
1128              break;
1129      }
1130  
1131      QString tooltip;
1132  
1133      QDateTime currentDate = QDateTime::currentDateTime();
1134      qint64 secs = blockDate.secsTo(currentDate);
1135  
1136      tooltip = tr("Processed %n block(s) of transaction history.", "", count);
1137  
1138      // Set icon state: spinning if catching up, tick otherwise
1139      if (secs < MAX_BLOCK_TIME_GAP) {
1140          tooltip = tr("Up to date") + QString(".<br>") + tooltip;
1141          labelBlocksIcon->setThemedPixmap(QStringLiteral(":/icons/synced"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1142  
1143  #ifdef ENABLE_WALLET
1144          if(walletFrame)
1145          {
1146              walletFrame->showOutOfSyncWarning(false);
1147              modalOverlay->showHide(true, true);
1148          }
1149  #endif // ENABLE_WALLET
1150  
1151          progressBarLabel->setVisible(false);
1152          progressBar->setVisible(false);
1153      }
1154      else
1155      {
1156          QString timeBehindText = GUIUtil::formatNiceTimeOffset(secs);
1157  
1158          progressBarLabel->setVisible(true);
1159          progressBar->setFormat(tr("%1 behind").arg(timeBehindText));
1160          progressBar->setMaximum(1000000000);
1161          progressBar->setValue(nVerificationProgress * 1000000000.0 + 0.5);
1162          progressBar->setVisible(true);
1163  
1164          tooltip = tr("Catching up…") + QString("<br>") + tooltip;
1165          if(count != prevBlocks)
1166          {
1167              labelBlocksIcon->setThemedPixmap(
1168                  QString(":/animation/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')),
1169                  STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1170              spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES;
1171          }
1172          prevBlocks = count;
1173  
1174  #ifdef ENABLE_WALLET
1175          if(walletFrame)
1176          {
1177              walletFrame->showOutOfSyncWarning(true);
1178              modalOverlay->showHide();
1179          }
1180  #endif // ENABLE_WALLET
1181  
1182          tooltip += QString("<br>");
1183          tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText);
1184          tooltip += QString("<br>");
1185          tooltip += tr("Transactions after this will not yet be visible.");
1186      }
1187  
1188      // Don't word-wrap this (fixed-width) tooltip
1189      tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
1190  
1191      labelBlocksIcon->setToolTip(tooltip);
1192      progressBarLabel->setToolTip(tooltip);
1193      progressBar->setToolTip(tooltip);
1194  }
1195  
1196  void BitcoinGUI::createWallet()
1197  {
1198  #ifdef ENABLE_WALLET
1199  #ifndef USE_SQLITE
1200      // Compiled without sqlite support (required for descriptor wallets)
1201      message(tr("Error creating wallet"), tr("Cannot create new wallet, the software was compiled without sqlite support (required for descriptor wallets)"), CClientUIInterface::MSG_ERROR);
1202      return;
1203  #endif // USE_SQLITE
1204      auto activity = new CreateWalletActivity(getWalletController(), this);
1205      connect(activity, &CreateWalletActivity::created, this, &BitcoinGUI::setCurrentWallet);
1206      connect(activity, &CreateWalletActivity::created, rpcConsole, &RPCConsole::setCurrentWallet);
1207      activity->create();
1208  #endif // ENABLE_WALLET
1209  }
1210  
1211  void BitcoinGUI::message(const QString& title, QString message, unsigned int style, bool* ret, const QString& detailed_message)
1212  {
1213      // Default title. On macOS, the window title is ignored (as required by the macOS Guidelines).
1214      QString strTitle{PACKAGE_NAME};
1215      // Default to information icon
1216      int nMBoxIcon = QMessageBox::Information;
1217      int nNotifyIcon = Notificator::Information;
1218  
1219      QString msgType;
1220      if (!title.isEmpty()) {
1221          msgType = title;
1222      } else {
1223          switch (style) {
1224          case CClientUIInterface::MSG_ERROR:
1225              msgType = tr("Error");
1226              message = tr("Error: %1").arg(message);
1227              break;
1228          case CClientUIInterface::MSG_WARNING:
1229              msgType = tr("Warning");
1230              message = tr("Warning: %1").arg(message);
1231              break;
1232          case CClientUIInterface::MSG_INFORMATION:
1233              msgType = tr("Information");
1234              // No need to prepend the prefix here.
1235              break;
1236          default:
1237              break;
1238          }
1239      }
1240  
1241      if (!msgType.isEmpty()) {
1242          strTitle += " - " + msgType;
1243      }
1244  
1245      if (style & CClientUIInterface::ICON_ERROR) {
1246          nMBoxIcon = QMessageBox::Critical;
1247          nNotifyIcon = Notificator::Critical;
1248      } else if (style & CClientUIInterface::ICON_WARNING) {
1249          nMBoxIcon = QMessageBox::Warning;
1250          nNotifyIcon = Notificator::Warning;
1251      }
1252  
1253      if (style & CClientUIInterface::MODAL) {
1254          // Check for buttons, use OK as default, if none was supplied
1255          QMessageBox::StandardButton buttons;
1256          if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK)))
1257              buttons = QMessageBox::Ok;
1258  
1259          showNormalIfMinimized();
1260          QMessageBox mBox(static_cast<QMessageBox::Icon>(nMBoxIcon), strTitle, message, buttons, this);
1261          mBox.setTextFormat(Qt::PlainText);
1262          mBox.setDetailedText(detailed_message);
1263          int r = mBox.exec();
1264          if (ret != nullptr)
1265              *ret = r == QMessageBox::Ok;
1266      } else {
1267          notificator->notify(static_cast<Notificator::Class>(nNotifyIcon), strTitle, message);
1268      }
1269  }
1270  
1271  void BitcoinGUI::changeEvent(QEvent *e)
1272  {
1273      if (e->type() == QEvent::PaletteChange) {
1274          overviewAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/overview")));
1275          sendCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/send")));
1276          receiveCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/receiving_addresses")));
1277          historyAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/history")));
1278      }
1279  
1280      QMainWindow::changeEvent(e);
1281  
1282  #ifndef Q_OS_MACOS // Ignored on Mac
1283      if(e->type() == QEvent::WindowStateChange)
1284      {
1285          if(clientModel && clientModel->getOptionsModel() && clientModel->getOptionsModel()->getMinimizeToTray())
1286          {
1287              QWindowStateChangeEvent *wsevt = static_cast<QWindowStateChangeEvent*>(e);
1288              if(!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized())
1289              {
1290                  QTimer::singleShot(0, this, &BitcoinGUI::hide);
1291                  e->ignore();
1292              }
1293              else if((wsevt->oldState() & Qt::WindowMinimized) && !isMinimized())
1294              {
1295                  QTimer::singleShot(0, this, &BitcoinGUI::show);
1296                  e->ignore();
1297              }
1298          }
1299      }
1300  #endif
1301  }
1302  
1303  void BitcoinGUI::closeEvent(QCloseEvent *event)
1304  {
1305  #ifndef Q_OS_MACOS // Ignored on Mac
1306      if(clientModel && clientModel->getOptionsModel())
1307      {
1308          if(!clientModel->getOptionsModel()->getMinimizeOnClose())
1309          {
1310              // close rpcConsole in case it was open to make some space for the shutdown window
1311              rpcConsole->close();
1312  
1313              Q_EMIT quitRequested();
1314          }
1315          else
1316          {
1317              QMainWindow::showMinimized();
1318              event->ignore();
1319          }
1320      }
1321  #else
1322      QMainWindow::closeEvent(event);
1323  #endif
1324  }
1325  
1326  void BitcoinGUI::showEvent(QShowEvent *event)
1327  {
1328      // enable the debug window when the main window shows up
1329      openRPCConsoleAction->setEnabled(true);
1330      aboutAction->setEnabled(true);
1331      optionsAction->setEnabled(true);
1332  }
1333  
1334  #ifdef ENABLE_WALLET
1335  void BitcoinGUI::incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName)
1336  {
1337      // On new transaction, make an info balloon
1338      QString msg = tr("Date: %1\n").arg(date) +
1339                    tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(unit, amount, true));
1340      if (m_node.walletLoader().getWallets().size() > 1 && !walletName.isEmpty()) {
1341          msg += tr("Wallet: %1\n").arg(walletName);
1342      }
1343      msg += tr("Type: %1\n").arg(type);
1344      if (!label.isEmpty())
1345          msg += tr("Label: %1\n").arg(label);
1346      else if (!address.isEmpty())
1347          msg += tr("Address: %1\n").arg(address);
1348      message((amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"),
1349               msg, CClientUIInterface::MSG_INFORMATION);
1350  }
1351  #endif // ENABLE_WALLET
1352  
1353  void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event)
1354  {
1355      // Accept only URIs
1356      if(event->mimeData()->hasUrls())
1357          event->acceptProposedAction();
1358  }
1359  
1360  void BitcoinGUI::dropEvent(QDropEvent *event)
1361  {
1362      if(event->mimeData()->hasUrls())
1363      {
1364          for (const QUrl &uri : event->mimeData()->urls())
1365          {
1366              Q_EMIT receivedURI(uri.toString());
1367          }
1368      }
1369      event->acceptProposedAction();
1370  }
1371  
1372  bool BitcoinGUI::eventFilter(QObject *object, QEvent *event)
1373  {
1374      // Catch status tip events
1375      if (event->type() == QEvent::StatusTip)
1376      {
1377          // Prevent adding text from setStatusTip(), if we currently use the status bar for displaying other stuff
1378          if (progressBarLabel->isVisible() || progressBar->isVisible())
1379              return true;
1380      }
1381      return QMainWindow::eventFilter(object, event);
1382  }
1383  
1384  #ifdef ENABLE_WALLET
1385  bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient)
1386  {
1387      // URI has to be valid
1388      if (walletFrame && walletFrame->handlePaymentRequest(recipient))
1389      {
1390          showNormalIfMinimized();
1391          gotoSendCoinsPage();
1392          return true;
1393      }
1394      return false;
1395  }
1396  
1397  void BitcoinGUI::setHDStatus(bool privkeyDisabled, int hdEnabled)
1398  {
1399      labelWalletHDStatusIcon->setThemedPixmap(privkeyDisabled ? QStringLiteral(":/icons/eye") : hdEnabled ? QStringLiteral(":/icons/hd_enabled") : QStringLiteral(":/icons/hd_disabled"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1400      labelWalletHDStatusIcon->setToolTip(privkeyDisabled ? tr("Private key <b>disabled</b>") : hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>"));
1401      labelWalletHDStatusIcon->show();
1402  }
1403  
1404  void BitcoinGUI::setEncryptionStatus(int status)
1405  {
1406      switch(status)
1407      {
1408      case WalletModel::NoKeys:
1409          labelWalletEncryptionIcon->hide();
1410          encryptWalletAction->setChecked(false);
1411          changePassphraseAction->setEnabled(false);
1412          encryptWalletAction->setEnabled(false);
1413          break;
1414      case WalletModel::Unencrypted:
1415          labelWalletEncryptionIcon->hide();
1416          encryptWalletAction->setChecked(false);
1417          changePassphraseAction->setEnabled(false);
1418          encryptWalletAction->setEnabled(true);
1419          break;
1420      case WalletModel::Unlocked:
1421          labelWalletEncryptionIcon->show();
1422          labelWalletEncryptionIcon->setThemedPixmap(QStringLiteral(":/icons/lock_open"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1423          labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
1424          encryptWalletAction->setChecked(true);
1425          changePassphraseAction->setEnabled(true);
1426          encryptWalletAction->setEnabled(false);
1427          break;
1428      case WalletModel::Locked:
1429          labelWalletEncryptionIcon->show();
1430          labelWalletEncryptionIcon->setThemedPixmap(QStringLiteral(":/icons/lock_closed"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1431          labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
1432          encryptWalletAction->setChecked(true);
1433          changePassphraseAction->setEnabled(true);
1434          encryptWalletAction->setEnabled(false);
1435          break;
1436      }
1437  }
1438  
1439  void BitcoinGUI::updateWalletStatus()
1440  {
1441      assert(walletFrame);
1442  
1443      WalletView * const walletView = walletFrame->currentWalletView();
1444      if (!walletView) {
1445          return;
1446      }
1447      WalletModel * const walletModel = walletView->getWalletModel();
1448      setEncryptionStatus(walletModel->getEncryptionStatus());
1449      setHDStatus(walletModel->wallet().privateKeysDisabled(), walletModel->wallet().hdEnabled());
1450  }
1451  #endif // ENABLE_WALLET
1452  
1453  void BitcoinGUI::updateProxyIcon()
1454  {
1455      std::string ip_port;
1456      bool proxy_enabled = clientModel->getProxyInfo(ip_port);
1457  
1458      if (proxy_enabled) {
1459          if (!GUIUtil::HasPixmap(labelProxyIcon)) {
1460              QString ip_port_q = QString::fromStdString(ip_port);
1461              labelProxyIcon->setThemedPixmap((":/icons/proxy"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1462              labelProxyIcon->setToolTip(tr("Proxy is <b>enabled</b>: %1").arg(ip_port_q));
1463          } else {
1464              labelProxyIcon->show();
1465          }
1466      } else {
1467          labelProxyIcon->hide();
1468      }
1469  }
1470  
1471  void BitcoinGUI::updateWindowTitle()
1472  {
1473      QString window_title = PACKAGE_NAME;
1474  #ifdef ENABLE_WALLET
1475      if (walletFrame) {
1476          WalletModel* const wallet_model = walletFrame->currentWalletModel();
1477          if (wallet_model && !wallet_model->getWalletName().isEmpty()) {
1478              window_title += " - " + wallet_model->getDisplayName();
1479          }
1480      }
1481  #endif
1482      if (!m_network_style->getTitleAddText().isEmpty()) {
1483          window_title += " - " + m_network_style->getTitleAddText();
1484      }
1485      setWindowTitle(window_title);
1486  }
1487  
1488  void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
1489  {
1490      if(!clientModel)
1491          return;
1492  
1493      if (!isHidden() && !isMinimized() && !GUIUtil::isObscured(this) && fToggleHidden) {
1494          hide();
1495      } else {
1496          GUIUtil::bringToFront(this);
1497      }
1498  }
1499  
1500  void BitcoinGUI::toggleHidden()
1501  {
1502      showNormalIfMinimized(true);
1503  }
1504  
1505  void BitcoinGUI::detectShutdown()
1506  {
1507      if (m_node.shutdownRequested())
1508      {
1509          if(rpcConsole)
1510              rpcConsole->hide();
1511          Q_EMIT quitRequested();
1512      }
1513  }
1514  
1515  void BitcoinGUI::showProgress(const QString &title, int nProgress)
1516  {
1517      if (nProgress == 0) {
1518          progressDialog = new QProgressDialog(title, QString(), 0, 100);
1519          GUIUtil::PolishProgressDialog(progressDialog);
1520          progressDialog->setWindowModality(Qt::ApplicationModal);
1521          progressDialog->setAutoClose(false);
1522          progressDialog->setValue(0);
1523      } else if (nProgress == 100) {
1524          if (progressDialog) {
1525              progressDialog->close();
1526              progressDialog->deleteLater();
1527              progressDialog = nullptr;
1528          }
1529      } else if (progressDialog) {
1530          progressDialog->setValue(nProgress);
1531      }
1532  }
1533  
1534  void BitcoinGUI::showModalOverlay()
1535  {
1536      if (modalOverlay && (progressBar->isVisible() || modalOverlay->isLayerVisible()))
1537          modalOverlay->toggleVisibility();
1538  }
1539  
1540  static bool ThreadSafeMessageBox(BitcoinGUI* gui, const bilingual_str& message, const std::string& caption, unsigned int style)
1541  {
1542      bool modal = (style & CClientUIInterface::MODAL);
1543      // The SECURE flag has no effect in the Qt GUI.
1544      // bool secure = (style & CClientUIInterface::SECURE);
1545      style &= ~CClientUIInterface::SECURE;
1546      bool ret = false;
1547  
1548      QString detailed_message; // This is original message, in English, for googling and referencing.
1549      if (message.original != message.translated) {
1550          detailed_message = BitcoinGUI::tr("Original message:") + "\n" + QString::fromStdString(message.original);
1551      }
1552  
1553      // In case of modal message, use blocking connection to wait for user to click a button
1554      bool invoked = QMetaObject::invokeMethod(gui, "message",
1555                                 modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
1556                                 Q_ARG(QString, QString::fromStdString(caption)),
1557                                 Q_ARG(QString, QString::fromStdString(message.translated)),
1558                                 Q_ARG(unsigned int, style),
1559                                 Q_ARG(bool*, &ret),
1560                                 Q_ARG(QString, detailed_message));
1561      assert(invoked);
1562      return ret;
1563  }
1564  
1565  void BitcoinGUI::subscribeToCoreSignals()
1566  {
1567      // Connect signals to client
1568      m_handler_message_box = m_node.handleMessageBox(std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
1569      m_handler_question = m_node.handleQuestion(std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_3, std::placeholders::_4));
1570  }
1571  
1572  void BitcoinGUI::unsubscribeFromCoreSignals()
1573  {
1574      // Disconnect signals from client
1575      m_handler_message_box->disconnect();
1576      m_handler_question->disconnect();
1577  }
1578  
1579  bool BitcoinGUI::isPrivacyModeActivated() const
1580  {
1581      assert(m_mask_values_action);
1582      return m_mask_values_action->isChecked();
1583  }
1584  
1585  UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle* platformStyle)
1586      : m_platform_style{platformStyle}
1587  {
1588      createContextMenu();
1589      setToolTip(tr("Unit to show amounts in. Click to select another unit."));
1590      QList<BitcoinUnit> units = BitcoinUnits::availableUnits();
1591      int max_width = 0;
1592      const QFontMetrics fm(font());
1593      for (const BitcoinUnit unit : units) {
1594          max_width = qMax(max_width, GUIUtil::TextWidth(fm, BitcoinUnits::longName(unit)));
1595      }
1596      setMinimumSize(max_width, 0);
1597      setAlignment(Qt::AlignRight | Qt::AlignVCenter);
1598      setStyleSheet(QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name()));
1599  }
1600  
1601  /** So that it responds to button clicks */
1602  void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
1603  {
1604      onDisplayUnitsClicked(event->pos());
1605  }
1606  
1607  void UnitDisplayStatusBarControl::changeEvent(QEvent* e)
1608  {
1609      if (e->type() == QEvent::PaletteChange) {
1610          QString style = QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name());
1611          if (style != styleSheet()) {
1612              setStyleSheet(style);
1613          }
1614      }
1615  
1616      QLabel::changeEvent(e);
1617  }
1618  
1619  /** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
1620  void UnitDisplayStatusBarControl::createContextMenu()
1621  {
1622      menu = new QMenu(this);
1623      for (const BitcoinUnit u : BitcoinUnits::availableUnits()) {
1624          menu->addAction(BitcoinUnits::longName(u))->setData(QVariant::fromValue(u));
1625      }
1626      connect(menu, &QMenu::triggered, this, &UnitDisplayStatusBarControl::onMenuSelection);
1627  }
1628  
1629  /** Lets the control know about the Options Model (and its signals) */
1630  void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel)
1631  {
1632      if (_optionsModel)
1633      {
1634          this->optionsModel = _optionsModel;
1635  
1636          // be aware of a display unit change reported by the OptionsModel object.
1637          connect(_optionsModel, &OptionsModel::displayUnitChanged, this, &UnitDisplayStatusBarControl::updateDisplayUnit);
1638  
1639          // initialize the display units label with the current value in the model.
1640          updateDisplayUnit(_optionsModel->getDisplayUnit());
1641      }
1642  }
1643  
1644  /** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
1645  void UnitDisplayStatusBarControl::updateDisplayUnit(BitcoinUnit newUnits)
1646  {
1647      setText(BitcoinUnits::longName(newUnits));
1648  }
1649  
1650  /** Shows context menu with Display Unit options by the mouse coordinates */
1651  void UnitDisplayStatusBarControl::onDisplayUnitsClicked(const QPoint& point)
1652  {
1653      QPoint globalPos = mapToGlobal(point);
1654      menu->exec(globalPos);
1655  }
1656  
1657  /** Tells underlying optionsModel to update its current display unit. */
1658  void UnitDisplayStatusBarControl::onMenuSelection(QAction* action)
1659  {
1660      if (action)
1661      {
1662          optionsModel->setDisplayUnit(action->data());
1663      }
1664  }