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