/ 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      const bool privacy = isPrivacyModeActivated();
 800      wallet_view->setPrivacy(privacy);
 801      enableHistoryAction(privacy);
 802      const QString display_name = walletModel->getDisplayName();
 803      m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
 804  }
 805  
 806  void BitcoinGUI::removeWallet(WalletModel* walletModel)
 807  {
 808      if (!walletFrame) return;
 809  
 810      labelWalletHDStatusIcon->hide();
 811      labelWalletEncryptionIcon->hide();
 812  
 813      int index = m_wallet_selector->findData(QVariant::fromValue(walletModel));
 814      m_wallet_selector->removeItem(index);
 815      if (m_wallet_selector->count() == 0) {
 816          setWalletActionsEnabled(false);
 817          overviewAction->setChecked(true);
 818      } else if (m_wallet_selector->count() == 1) {
 819          m_wallet_selector_label_action->setVisible(false);
 820          m_wallet_selector_action->setVisible(false);
 821      }
 822      rpcConsole->removeWallet(walletModel);
 823      walletFrame->removeWallet(walletModel);
 824      updateWindowTitle();
 825  }
 826  
 827  void BitcoinGUI::setCurrentWallet(WalletModel* wallet_model)
 828  {
 829      if (!walletFrame || !m_wallet_controller) return;
 830      walletFrame->setCurrentWallet(wallet_model);
 831      for (int index = 0; index < m_wallet_selector->count(); ++index) {
 832          if (m_wallet_selector->itemData(index).value<WalletModel*>() == wallet_model) {
 833              m_wallet_selector->setCurrentIndex(index);
 834              break;
 835          }
 836      }
 837      updateWindowTitle();
 838  }
 839  
 840  void BitcoinGUI::setCurrentWalletBySelectorIndex(int index)
 841  {
 842      WalletModel* wallet_model = m_wallet_selector->itemData(index).value<WalletModel*>();
 843      if (wallet_model) setCurrentWallet(wallet_model);
 844  }
 845  
 846  void BitcoinGUI::removeAllWallets()
 847  {
 848      if(!walletFrame)
 849          return;
 850      setWalletActionsEnabled(false);
 851      walletFrame->removeAllWallets();
 852  }
 853  #endif // ENABLE_WALLET
 854  
 855  void BitcoinGUI::setWalletActionsEnabled(bool enabled)
 856  {
 857      overviewAction->setEnabled(enabled);
 858      sendCoinsAction->setEnabled(enabled);
 859      receiveCoinsAction->setEnabled(enabled);
 860      historyAction->setEnabled(enabled);
 861      encryptWalletAction->setEnabled(enabled);
 862      backupWalletAction->setEnabled(enabled);
 863      changePassphraseAction->setEnabled(enabled);
 864      signMessageAction->setEnabled(enabled);
 865      verifyMessageAction->setEnabled(enabled);
 866      usedSendingAddressesAction->setEnabled(enabled);
 867      usedReceivingAddressesAction->setEnabled(enabled);
 868      openAction->setEnabled(enabled);
 869      m_close_wallet_action->setEnabled(enabled);
 870      m_close_all_wallets_action->setEnabled(enabled);
 871  }
 872  
 873  void BitcoinGUI::createTrayIcon()
 874  {
 875      assert(QSystemTrayIcon::isSystemTrayAvailable());
 876  
 877  #ifndef Q_OS_MACOS
 878      if (QSystemTrayIcon::isSystemTrayAvailable()) {
 879          trayIcon = new QSystemTrayIcon(m_network_style->getTrayAndWindowIcon(), this);
 880          QString toolTip = tr("%1 client").arg(CLIENT_NAME) + " " + m_network_style->getTitleAddText();
 881          trayIcon->setToolTip(toolTip);
 882      }
 883  #endif
 884  }
 885  
 886  void BitcoinGUI::createTrayIconMenu()
 887  {
 888  #ifndef Q_OS_MACOS
 889      if (!trayIcon) return;
 890  #endif // Q_OS_MACOS
 891  
 892      // Configuration of the tray icon (or Dock icon) menu.
 893      QAction* show_hide_action{nullptr};
 894  #ifndef Q_OS_MACOS
 895      // Note: On macOS, the Dock icon's menu already has Show / Hide action.
 896      show_hide_action = trayIconMenu->addAction(QString(), this, &BitcoinGUI::toggleHidden);
 897      trayIconMenu->addSeparator();
 898  #endif // Q_OS_MACOS
 899  
 900      QAction* send_action{nullptr};
 901      QAction* receive_action{nullptr};
 902      QAction* sign_action{nullptr};
 903      QAction* verify_action{nullptr};
 904      if (enableWallet) {
 905          send_action = trayIconMenu->addAction(sendCoinsAction->text(), sendCoinsAction, &QAction::trigger);
 906          receive_action = trayIconMenu->addAction(receiveCoinsAction->text(), receiveCoinsAction, &QAction::trigger);
 907          trayIconMenu->addSeparator();
 908          sign_action = trayIconMenu->addAction(signMessageAction->text(), signMessageAction, &QAction::trigger);
 909          verify_action = trayIconMenu->addAction(verifyMessageAction->text(), verifyMessageAction, &QAction::trigger);
 910          trayIconMenu->addSeparator();
 911      }
 912      QAction* options_action = trayIconMenu->addAction(optionsAction->text(), optionsAction, &QAction::trigger);
 913      options_action->setMenuRole(QAction::PreferencesRole);
 914      QAction* node_window_action = trayIconMenu->addAction(openRPCConsoleAction->text(), openRPCConsoleAction, &QAction::trigger);
 915      QAction* quit_action{nullptr};
 916  #ifndef Q_OS_MACOS
 917      // Note: On macOS, the Dock icon's menu already has Quit action.
 918      trayIconMenu->addSeparator();
 919      quit_action = trayIconMenu->addAction(quitAction->text(), quitAction, &QAction::trigger);
 920  
 921      trayIcon->setContextMenu(trayIconMenu.get());
 922      connect(trayIcon, &QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason reason) {
 923          if (reason == QSystemTrayIcon::Trigger) {
 924              // Click on system tray icon triggers show/hide of the main window
 925              toggleHidden();
 926          }
 927      });
 928  #else
 929      // Note: On macOS, the Dock icon is used to provide the tray's functionality.
 930      MacDockIconHandler* dockIconHandler = MacDockIconHandler::instance();
 931      connect(dockIconHandler, &MacDockIconHandler::dockIconClicked, [this] {
 932          if (m_node.shutdownRequested()) return; // nothing to show, node is shutting down.
 933          show();
 934          activateWindow();
 935      });
 936      trayIconMenu->setAsDockMenu();
 937  #endif // Q_OS_MACOS
 938  
 939      connect(
 940          // Using QSystemTrayIcon::Context is not reliable.
 941          // See https://bugreports.qt.io/browse/QTBUG-91697
 942          trayIconMenu.get(), &QMenu::aboutToShow,
 943          [this, show_hide_action, send_action, receive_action, sign_action, verify_action, options_action, node_window_action, quit_action] {
 944              if (m_node.shutdownRequested()) return; // nothing to do, node is shutting down.
 945  
 946              if (show_hide_action) show_hide_action->setText(
 947                  (!isHidden() && !isMinimized() && !GUIUtil::isObscured(this)) ?
 948                      tr("&Hide") :
 949                      tr("S&how"));
 950              if (QApplication::activeModalWidget()) {
 951                  for (QAction* a : trayIconMenu.get()->actions()) {
 952                      a->setEnabled(false);
 953                  }
 954              } else {
 955                  if (show_hide_action) show_hide_action->setEnabled(true);
 956                  if (enableWallet) {
 957                      send_action->setEnabled(sendCoinsAction->isEnabled());
 958                      receive_action->setEnabled(receiveCoinsAction->isEnabled());
 959                      sign_action->setEnabled(signMessageAction->isEnabled());
 960                      verify_action->setEnabled(verifyMessageAction->isEnabled());
 961                  }
 962                  options_action->setEnabled(optionsAction->isEnabled());
 963                  node_window_action->setEnabled(openRPCConsoleAction->isEnabled());
 964                  if (quit_action) quit_action->setEnabled(true);
 965              }
 966          });
 967  }
 968  
 969  void BitcoinGUI::optionsClicked()
 970  {
 971      openOptionsDialogWithTab(OptionsDialog::TAB_MAIN);
 972  }
 973  
 974  void BitcoinGUI::aboutClicked()
 975  {
 976      if(!clientModel)
 977          return;
 978  
 979      auto dlg = new HelpMessageDialog(this, /*about=*/true);
 980      GUIUtil::ShowModalDialogAsynchronously(dlg);
 981  }
 982  
 983  void BitcoinGUI::showDebugWindow()
 984  {
 985      GUIUtil::bringToFront(rpcConsole);
 986      Q_EMIT consoleShown(rpcConsole);
 987  }
 988  
 989  void BitcoinGUI::showDebugWindowActivateConsole()
 990  {
 991      rpcConsole->setTabFocus(RPCConsole::TabTypes::CONSOLE);
 992      showDebugWindow();
 993  }
 994  
 995  void BitcoinGUI::showHelpMessageClicked()
 996  {
 997      GUIUtil::bringToFront(helpMessageDialog);
 998  }
 999  
1000  #ifdef ENABLE_WALLET
1001  void BitcoinGUI::openClicked()
1002  {
1003      OpenURIDialog dlg(platformStyle, this);
1004      if(dlg.exec())
1005      {
1006          Q_EMIT receivedURI(dlg.getURI());
1007      }
1008  }
1009  
1010  void BitcoinGUI::gotoOverviewPage()
1011  {
1012      overviewAction->setChecked(true);
1013      if (walletFrame) walletFrame->gotoOverviewPage();
1014  }
1015  
1016  void BitcoinGUI::gotoHistoryPage()
1017  {
1018      historyAction->setChecked(true);
1019      if (walletFrame) walletFrame->gotoHistoryPage();
1020  }
1021  
1022  void BitcoinGUI::gotoReceiveCoinsPage()
1023  {
1024      receiveCoinsAction->setChecked(true);
1025      if (walletFrame) walletFrame->gotoReceiveCoinsPage();
1026  }
1027  
1028  void BitcoinGUI::gotoSendCoinsPage(QString addr)
1029  {
1030      sendCoinsAction->setChecked(true);
1031      if (walletFrame) walletFrame->gotoSendCoinsPage(addr);
1032  }
1033  
1034  void BitcoinGUI::gotoSignMessageTab(QString addr)
1035  {
1036      if (walletFrame) walletFrame->gotoSignMessageTab(addr);
1037  }
1038  
1039  void BitcoinGUI::gotoVerifyMessageTab(QString addr)
1040  {
1041      if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
1042  }
1043  void BitcoinGUI::gotoLoadPSBT(bool from_clipboard)
1044  {
1045      if (walletFrame) walletFrame->gotoLoadPSBT(from_clipboard);
1046  }
1047  #endif // ENABLE_WALLET
1048  
1049  void BitcoinGUI::updateNetworkState()
1050  {
1051      if (!clientModel) return;
1052      int count = clientModel->getNumConnections();
1053      QString icon;
1054      switch(count)
1055      {
1056      case 0: icon = ":/icons/connect_0"; break;
1057      case 1: case 2: case 3: icon = ":/icons/connect_1"; break;
1058      case 4: case 5: case 6: icon = ":/icons/connect_2"; break;
1059      case 7: case 8: case 9: icon = ":/icons/connect_3"; break;
1060      default: icon = ":/icons/connect_4"; break;
1061      }
1062  
1063      QString tooltip;
1064  
1065      if (m_node.getNetworkActive()) {
1066          //: A substring of the tooltip.
1067          tooltip = tr("%n active connection(s) to Bitcoin network.", "", count);
1068      } else {
1069          //: A substring of the tooltip.
1070          tooltip = tr("Network activity disabled.");
1071          icon = ":/icons/network_disabled";
1072      }
1073  
1074      // Don't word-wrap this (fixed-width) tooltip
1075      tooltip = QLatin1String("<nobr>") + tooltip + QLatin1String("<br>") +
1076                //: A substring of the tooltip. "More actions" are available via the context menu.
1077                tr("Click for more actions.") + QLatin1String("</nobr>");
1078      connectionsControl->setToolTip(tooltip);
1079  
1080      connectionsControl->setThemedPixmap(icon, STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1081  }
1082  
1083  void BitcoinGUI::setNumConnections(int count)
1084  {
1085      updateNetworkState();
1086  }
1087  
1088  void BitcoinGUI::setNetworkActive(bool network_active)
1089  {
1090      updateNetworkState();
1091      m_network_context_menu->clear();
1092      m_network_context_menu->addAction(
1093          //: A context menu item. The "Peers tab" is an element of the "Node window".
1094          tr("Show Peers tab"),
1095          [this] {
1096              rpcConsole->setTabFocus(RPCConsole::TabTypes::PEERS);
1097              showDebugWindow();
1098          });
1099      m_network_context_menu->addAction(
1100          network_active ?
1101              //: A context menu item.
1102              tr("Disable network activity") :
1103              //: A context menu item. The network activity was disabled previously.
1104              tr("Enable network activity"),
1105          [this, new_state = !network_active] { m_node.setNetworkActive(new_state); });
1106  }
1107  
1108  void BitcoinGUI::updateHeadersSyncProgressLabel()
1109  {
1110      int64_t headersTipTime = clientModel->getHeaderTipTime();
1111      int headersTipHeight = clientModel->getHeaderTipHeight();
1112      int estHeadersLeft = (GetTime() - headersTipTime) / Params().GetConsensus().nPowTargetSpacing;
1113      if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC)
1114          progressBarLabel->setText(tr("Syncing Headers (%1%)…").arg(QString::number(100.0 / (headersTipHeight+estHeadersLeft)*headersTipHeight, 'f', 1)));
1115  }
1116  
1117  void BitcoinGUI::updateHeadersPresyncProgressLabel(int64_t height, const QDateTime& blockDate)
1118  {
1119      int estHeadersLeft = blockDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing;
1120      if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC)
1121          progressBarLabel->setText(tr("Pre-syncing Headers (%1%)…").arg(QString::number(100.0 / (height+estHeadersLeft)*height, 'f', 1)));
1122  }
1123  
1124  void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
1125  {
1126      if (!clientModel || !clientModel->getOptionsModel())
1127          return;
1128  
1129      auto dlg = new OptionsDialog(this, enableWallet);
1130      connect(dlg, &OptionsDialog::quitOnReset, this, &BitcoinGUI::quitRequested);
1131      dlg->setCurrentTab(tab);
1132      dlg->setClientModel(clientModel);
1133      dlg->setModel(clientModel->getOptionsModel());
1134      GUIUtil::ShowModalDialogAsynchronously(dlg);
1135  }
1136  
1137  void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state)
1138  {
1139  // Disabling macOS App Nap on initial sync, disk and reindex operations.
1140  #ifdef Q_OS_MACOS
1141      if (sync_state == SynchronizationState::POST_INIT) {
1142          m_app_nap_inhibitor->enableAppNap();
1143      } else {
1144          m_app_nap_inhibitor->disableAppNap();
1145      }
1146  #endif
1147  
1148      if (modalOverlay)
1149      {
1150          if (synctype != SyncType::BLOCK_SYNC)
1151              modalOverlay->setKnownBestHeight(count, blockDate, synctype == SyncType::HEADER_PRESYNC);
1152          else
1153              modalOverlay->tipUpdate(count, blockDate, nVerificationProgress);
1154      }
1155      if (!clientModel)
1156          return;
1157  
1158      // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbled text)
1159      statusBar()->clearMessage();
1160  
1161      // Acquire current block source
1162      BlockSource blockSource{clientModel->getBlockSource()};
1163      switch (blockSource) {
1164          case BlockSource::NETWORK:
1165              if (synctype == SyncType::HEADER_PRESYNC) {
1166                  updateHeadersPresyncProgressLabel(count, blockDate);
1167                  return;
1168              } else if (synctype == SyncType::HEADER_SYNC) {
1169                  updateHeadersSyncProgressLabel();
1170                  return;
1171              }
1172              progressBarLabel->setText(tr("Synchronizing with network…"));
1173              updateHeadersSyncProgressLabel();
1174              break;
1175          case BlockSource::DISK:
1176              if (synctype != SyncType::BLOCK_SYNC) {
1177                  progressBarLabel->setText(tr("Indexing blocks on disk…"));
1178              } else {
1179                  progressBarLabel->setText(tr("Processing blocks on disk…"));
1180              }
1181              break;
1182          case BlockSource::NONE:
1183              if (synctype != SyncType::BLOCK_SYNC) {
1184                  return;
1185              }
1186              progressBarLabel->setText(tr("Connecting to peers…"));
1187              break;
1188      }
1189  
1190      QString tooltip;
1191  
1192      QDateTime currentDate = QDateTime::currentDateTime();
1193      qint64 secs = blockDate.secsTo(currentDate);
1194  
1195      tooltip = tr("Processed %n block(s) of transaction history.", "", count);
1196  
1197      // Set icon state: spinning if catching up, tick otherwise
1198      if (secs < MAX_BLOCK_TIME_GAP) {
1199          tooltip = tr("Up to date") + QString(".<br>") + tooltip;
1200          labelBlocksIcon->setThemedPixmap(QStringLiteral(":/icons/synced"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1201  
1202  #ifdef ENABLE_WALLET
1203          if(walletFrame)
1204          {
1205              walletFrame->showOutOfSyncWarning(false);
1206              modalOverlay->showHide(true, true);
1207          }
1208  #endif // ENABLE_WALLET
1209  
1210          progressBarLabel->setVisible(false);
1211          progressBar->setVisible(false);
1212      }
1213      else
1214      {
1215          QString timeBehindText = GUIUtil::formatNiceTimeOffset(secs);
1216  
1217          progressBarLabel->setVisible(true);
1218          progressBar->setFormat(tr("%1 behind").arg(timeBehindText));
1219          progressBar->setMaximum(1000000000);
1220          progressBar->setValue(nVerificationProgress * 1000000000.0 + 0.5);
1221          progressBar->setVisible(true);
1222  
1223          tooltip = tr("Catching up…") + QString("<br>") + tooltip;
1224          if(count != prevBlocks)
1225          {
1226              labelBlocksIcon->setThemedPixmap(
1227                  QString(":/animation/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')),
1228                  STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1229              spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES;
1230          }
1231          prevBlocks = count;
1232  
1233  #ifdef ENABLE_WALLET
1234          if(walletFrame)
1235          {
1236              walletFrame->showOutOfSyncWarning(true);
1237              modalOverlay->showHide();
1238          }
1239  #endif // ENABLE_WALLET
1240  
1241          tooltip += QString("<br>");
1242          tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText);
1243          tooltip += QString("<br>");
1244          tooltip += tr("Transactions after this will not yet be visible.");
1245      }
1246  
1247      // Don't word-wrap this (fixed-width) tooltip
1248      tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
1249  
1250      labelBlocksIcon->setToolTip(tooltip);
1251      progressBarLabel->setToolTip(tooltip);
1252      progressBar->setToolTip(tooltip);
1253  }
1254  
1255  void BitcoinGUI::createWallet()
1256  {
1257  #ifdef ENABLE_WALLET
1258      auto activity = new CreateWalletActivity(getWalletController(), this);
1259      connect(activity, &CreateWalletActivity::created, this, &BitcoinGUI::setCurrentWallet);
1260      connect(activity, &CreateWalletActivity::created, rpcConsole, &RPCConsole::setCurrentWallet);
1261      activity->create();
1262  #endif // ENABLE_WALLET
1263  }
1264  
1265  void BitcoinGUI::message(const QString& title, QString message, unsigned int style, bool* ret, const QString& detailed_message)
1266  {
1267      // Default title. On macOS, the window title is ignored (as required by the macOS Guidelines).
1268      QString strTitle{CLIENT_NAME};
1269      // Default to information icon
1270      int nMBoxIcon = QMessageBox::Information;
1271      int nNotifyIcon = Notificator::Information;
1272  
1273      QString msgType;
1274      if (!title.isEmpty()) {
1275          msgType = title;
1276      } else {
1277          switch (style) {
1278          case CClientUIInterface::MSG_ERROR:
1279              msgType = tr("Error");
1280              message = tr("Error: %1").arg(message);
1281              break;
1282          case CClientUIInterface::MSG_WARNING:
1283              msgType = tr("Warning");
1284              message = tr("Warning: %1").arg(message);
1285              break;
1286          case CClientUIInterface::MSG_INFORMATION:
1287              msgType = tr("Information");
1288              // No need to prepend the prefix here.
1289              break;
1290          default:
1291              break;
1292          }
1293      }
1294  
1295      if (!msgType.isEmpty()) {
1296          strTitle += " - " + msgType;
1297      }
1298  
1299      if (style & CClientUIInterface::ICON_ERROR) {
1300          nMBoxIcon = QMessageBox::Critical;
1301          nNotifyIcon = Notificator::Critical;
1302      } else if (style & CClientUIInterface::ICON_WARNING) {
1303          nMBoxIcon = QMessageBox::Warning;
1304          nNotifyIcon = Notificator::Warning;
1305      }
1306  
1307      if (style & CClientUIInterface::MODAL) {
1308          // Check for buttons, use OK as default, if none was supplied
1309          QMessageBox::StandardButton buttons;
1310          if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK)))
1311              buttons = QMessageBox::Ok;
1312  
1313          showNormalIfMinimized();
1314          QMessageBox mBox(static_cast<QMessageBox::Icon>(nMBoxIcon), strTitle, message, buttons, this);
1315          mBox.setTextFormat(Qt::PlainText);
1316          mBox.setDetailedText(detailed_message);
1317          int r = mBox.exec();
1318          if (ret != nullptr)
1319              *ret = r == QMessageBox::Ok;
1320      } else {
1321          notificator->notify(static_cast<Notificator::Class>(nNotifyIcon), strTitle, message);
1322      }
1323  }
1324  
1325  void BitcoinGUI::changeEvent(QEvent *e)
1326  {
1327      if (e->type() == QEvent::PaletteChange) {
1328          overviewAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/overview")));
1329          sendCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/send")));
1330          receiveCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/receiving_addresses")));
1331          historyAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/history")));
1332      }
1333  
1334      QMainWindow::changeEvent(e);
1335  
1336  #ifndef Q_OS_MACOS // Ignored on Mac
1337      if(e->type() == QEvent::WindowStateChange)
1338      {
1339          if(clientModel && clientModel->getOptionsModel() && clientModel->getOptionsModel()->getMinimizeToTray())
1340          {
1341              QWindowStateChangeEvent *wsevt = static_cast<QWindowStateChangeEvent*>(e);
1342              if(!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized())
1343              {
1344                  QTimer::singleShot(0, this, &BitcoinGUI::hide);
1345                  e->ignore();
1346              }
1347              else if((wsevt->oldState() & Qt::WindowMinimized) && !isMinimized())
1348              {
1349                  QTimer::singleShot(0, this, &BitcoinGUI::show);
1350                  e->ignore();
1351              }
1352          }
1353      }
1354  #endif
1355  }
1356  
1357  void BitcoinGUI::closeEvent(QCloseEvent *event)
1358  {
1359  #ifndef Q_OS_MACOS // Ignored on Mac
1360      if(clientModel && clientModel->getOptionsModel())
1361      {
1362          if(!clientModel->getOptionsModel()->getMinimizeOnClose())
1363          {
1364              // close rpcConsole in case it was open to make some space for the shutdown window
1365              rpcConsole->close();
1366  
1367              Q_EMIT quitRequested();
1368          }
1369          else
1370          {
1371              QMainWindow::showMinimized();
1372              event->ignore();
1373          }
1374      }
1375  #else
1376      QMainWindow::closeEvent(event);
1377  #endif
1378  }
1379  
1380  void BitcoinGUI::showEvent(QShowEvent *event)
1381  {
1382      // enable the debug window when the main window shows up
1383      openRPCConsoleAction->setEnabled(true);
1384      aboutAction->setEnabled(true);
1385      optionsAction->setEnabled(true);
1386  }
1387  
1388  #ifdef ENABLE_WALLET
1389  void BitcoinGUI::incomingTransaction(const QString& date, BitcoinUnit unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName)
1390  {
1391      // On new transaction, make an info balloon
1392      QString msg = tr("Date: %1\n").arg(date) +
1393                    tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(unit, amount, true));
1394      if (m_node.walletLoader().getWallets().size() > 1 && !walletName.isEmpty()) {
1395          msg += tr("Wallet: %1\n").arg(walletName);
1396      }
1397      msg += tr("Type: %1\n").arg(type);
1398      if (!label.isEmpty())
1399          msg += tr("Label: %1\n").arg(label);
1400      else if (!address.isEmpty())
1401          msg += tr("Address: %1\n").arg(address);
1402      message((amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"),
1403               msg, CClientUIInterface::MSG_INFORMATION);
1404  }
1405  #endif // ENABLE_WALLET
1406  
1407  void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event)
1408  {
1409      // Accept only URIs
1410      if(event->mimeData()->hasUrls())
1411          event->acceptProposedAction();
1412  }
1413  
1414  void BitcoinGUI::dropEvent(QDropEvent *event)
1415  {
1416      if(event->mimeData()->hasUrls())
1417      {
1418          for (const QUrl &uri : event->mimeData()->urls())
1419          {
1420              Q_EMIT receivedURI(uri.toString());
1421          }
1422      }
1423      event->acceptProposedAction();
1424  }
1425  
1426  bool BitcoinGUI::eventFilter(QObject *object, QEvent *event)
1427  {
1428      // Catch status tip events
1429      if (event->type() == QEvent::StatusTip)
1430      {
1431          // Prevent adding text from setStatusTip(), if we currently use the status bar for displaying other stuff
1432          if (progressBarLabel->isVisible() || progressBar->isVisible())
1433              return true;
1434      }
1435      return QMainWindow::eventFilter(object, event);
1436  }
1437  
1438  #ifdef ENABLE_WALLET
1439  bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient)
1440  {
1441      // URI has to be valid
1442      if (walletFrame && walletFrame->handlePaymentRequest(recipient))
1443      {
1444          showNormalIfMinimized();
1445          gotoSendCoinsPage();
1446          return true;
1447      }
1448      return false;
1449  }
1450  
1451  void BitcoinGUI::setHDStatus(bool privkeyDisabled, int hdEnabled)
1452  {
1453      labelWalletHDStatusIcon->setThemedPixmap(privkeyDisabled ? QStringLiteral(":/icons/eye") : hdEnabled ? QStringLiteral(":/icons/hd_enabled") : QStringLiteral(":/icons/hd_disabled"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1454      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>"));
1455      labelWalletHDStatusIcon->show();
1456  }
1457  
1458  void BitcoinGUI::setEncryptionStatus(int status)
1459  {
1460      switch(status)
1461      {
1462      case WalletModel::NoKeys:
1463          labelWalletEncryptionIcon->hide();
1464          encryptWalletAction->setChecked(false);
1465          changePassphraseAction->setEnabled(false);
1466          encryptWalletAction->setEnabled(false);
1467          break;
1468      case WalletModel::Unencrypted:
1469          labelWalletEncryptionIcon->hide();
1470          encryptWalletAction->setChecked(false);
1471          changePassphraseAction->setEnabled(false);
1472          encryptWalletAction->setEnabled(true);
1473          break;
1474      case WalletModel::Unlocked:
1475          labelWalletEncryptionIcon->show();
1476          labelWalletEncryptionIcon->setThemedPixmap(QStringLiteral(":/icons/lock_open"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1477          labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
1478          encryptWalletAction->setChecked(true);
1479          changePassphraseAction->setEnabled(true);
1480          encryptWalletAction->setEnabled(false);
1481          break;
1482      case WalletModel::Locked:
1483          labelWalletEncryptionIcon->show();
1484          labelWalletEncryptionIcon->setThemedPixmap(QStringLiteral(":/icons/lock_closed"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1485          labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
1486          encryptWalletAction->setChecked(true);
1487          changePassphraseAction->setEnabled(true);
1488          encryptWalletAction->setEnabled(false);
1489          break;
1490      }
1491  }
1492  
1493  void BitcoinGUI::updateWalletStatus()
1494  {
1495      assert(walletFrame);
1496  
1497      WalletView * const walletView = walletFrame->currentWalletView();
1498      if (!walletView) {
1499          return;
1500      }
1501      WalletModel * const walletModel = walletView->getWalletModel();
1502      setEncryptionStatus(walletModel->getEncryptionStatus());
1503      setHDStatus(walletModel->wallet().privateKeysDisabled(), walletModel->wallet().hdEnabled());
1504  }
1505  #endif // ENABLE_WALLET
1506  
1507  void BitcoinGUI::updateProxyIcon()
1508  {
1509      std::string ip_port;
1510      bool proxy_enabled = clientModel->getProxyInfo(ip_port);
1511  
1512      if (proxy_enabled) {
1513          if (!GUIUtil::HasPixmap(labelProxyIcon)) {
1514              QString ip_port_q = QString::fromStdString(ip_port);
1515              labelProxyIcon->setThemedPixmap((":/icons/proxy"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
1516              labelProxyIcon->setToolTip(tr("Proxy is <b>enabled</b>: %1").arg(ip_port_q));
1517          } else {
1518              labelProxyIcon->show();
1519          }
1520      } else {
1521          labelProxyIcon->hide();
1522      }
1523  }
1524  
1525  void BitcoinGUI::updateWindowTitle()
1526  {
1527      QString window_title = CLIENT_NAME;
1528  #ifdef ENABLE_WALLET
1529      if (walletFrame) {
1530          WalletModel* const wallet_model = walletFrame->currentWalletModel();
1531          if (wallet_model && !wallet_model->getWalletName().isEmpty()) {
1532              window_title += " - " + wallet_model->getDisplayName();
1533          }
1534      }
1535  #endif
1536      if (!m_network_style->getTitleAddText().isEmpty()) {
1537          window_title += " - " + m_network_style->getTitleAddText();
1538      }
1539      setWindowTitle(window_title);
1540  }
1541  
1542  void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
1543  {
1544      if(!clientModel)
1545          return;
1546  
1547      if (!isHidden() && !isMinimized() && !GUIUtil::isObscured(this) && fToggleHidden) {
1548          hide();
1549      } else {
1550          GUIUtil::bringToFront(this);
1551      }
1552  }
1553  
1554  void BitcoinGUI::toggleHidden()
1555  {
1556      showNormalIfMinimized(true);
1557  }
1558  
1559  void BitcoinGUI::detectShutdown()
1560  {
1561      if (m_node.shutdownRequested())
1562      {
1563          if(rpcConsole)
1564              rpcConsole->hide();
1565          Q_EMIT quitRequested();
1566      }
1567  }
1568  
1569  void BitcoinGUI::showProgress(const QString &title, int nProgress)
1570  {
1571      if (nProgress == 0) {
1572          progressDialog = new QProgressDialog(title, QString(), 0, 100);
1573          GUIUtil::PolishProgressDialog(progressDialog);
1574          progressDialog->setWindowModality(Qt::ApplicationModal);
1575          progressDialog->setAutoClose(false);
1576          progressDialog->setValue(0);
1577      } else if (nProgress == 100) {
1578          if (progressDialog) {
1579              progressDialog->close();
1580              progressDialog->deleteLater();
1581              progressDialog = nullptr;
1582          }
1583      } else if (progressDialog) {
1584          progressDialog->setValue(nProgress);
1585      }
1586  }
1587  
1588  void BitcoinGUI::showModalOverlay()
1589  {
1590      if (modalOverlay && (progressBar->isVisible() || modalOverlay->isLayerVisible()))
1591          modalOverlay->toggleVisibility();
1592  }
1593  
1594  static bool ThreadSafeMessageBox(BitcoinGUI* gui, const bilingual_str& message, unsigned int style)
1595  {
1596      bool modal = (style & CClientUIInterface::MODAL);
1597      // The SECURE flag has no effect in the Qt GUI.
1598      // bool secure = (style & CClientUIInterface::SECURE);
1599      style &= ~CClientUIInterface::SECURE;
1600      bool ret = false;
1601  
1602      QString detailed_message; // This is original message, in English, for googling and referencing.
1603      if (message.original != message.translated) {
1604          detailed_message = BitcoinGUI::tr("Original message:") + "\n" + QString::fromStdString(message.original);
1605      }
1606      // The title is empty for node messages. The fallback title is usually set
1607      // by `style`.
1608      const QString title{};
1609  
1610      // In case of modal message, use blocking connection to wait for user to click a button
1611      bool invoked = QMetaObject::invokeMethod(gui, "message",
1612                                 modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
1613                                 Q_ARG(QString, title),
1614                                 Q_ARG(QString, QString::fromStdString(message.translated)),
1615                                 Q_ARG(unsigned int, style),
1616                                 Q_ARG(bool*, &ret),
1617                                 Q_ARG(QString, detailed_message));
1618      assert(invoked);
1619      return ret;
1620  }
1621  
1622  void BitcoinGUI::subscribeToCoreSignals()
1623  {
1624      // Connect signals to client
1625      m_handler_message_box = m_node.handleMessageBox([this](const bilingual_str& message, unsigned int style) {
1626          return ThreadSafeMessageBox(this, message, style);
1627      });
1628      m_handler_question = m_node.handleQuestion([this](const bilingual_str& message, const std::string& /*non_interactive_message*/, unsigned int style) {
1629          return ThreadSafeMessageBox(this, message, style);
1630      });
1631  }
1632  
1633  void BitcoinGUI::unsubscribeFromCoreSignals()
1634  {
1635      // Disconnect signals from client
1636      m_handler_message_box->disconnect();
1637      m_handler_question->disconnect();
1638  }
1639  
1640  bool BitcoinGUI::isPrivacyModeActivated() const
1641  {
1642      assert(m_mask_values_action);
1643      return m_mask_values_action->isChecked();
1644  }
1645  
1646  UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle* platformStyle)
1647      : m_platform_style{platformStyle}
1648  {
1649      createContextMenu();
1650      setToolTip(tr("Unit to show amounts in. Click to select another unit."));
1651      QList<BitcoinUnit> units = BitcoinUnits::availableUnits();
1652      int max_width = 0;
1653      const QFontMetrics fm(font());
1654      for (const BitcoinUnit unit : units) {
1655          max_width = qMax(max_width, GUIUtil::TextWidth(fm, BitcoinUnits::longName(unit)));
1656      }
1657      setMinimumSize(max_width, 0);
1658      setAlignment(Qt::AlignRight | Qt::AlignVCenter);
1659      setStyleSheet(QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name()));
1660  }
1661  
1662  /** So that it responds to button clicks */
1663  void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
1664  {
1665      onDisplayUnitsClicked(event->pos());
1666  }
1667  
1668  void UnitDisplayStatusBarControl::changeEvent(QEvent* e)
1669  {
1670      if (e->type() == QEvent::PaletteChange) {
1671          QString style = QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name());
1672          if (style != styleSheet()) {
1673              setStyleSheet(style);
1674          }
1675      }
1676  
1677      QLabel::changeEvent(e);
1678  }
1679  
1680  /** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
1681  void UnitDisplayStatusBarControl::createContextMenu()
1682  {
1683      menu = new QMenu(this);
1684      for (const BitcoinUnit u : BitcoinUnits::availableUnits()) {
1685          menu->addAction(BitcoinUnits::longName(u))->setData(QVariant::fromValue(u));
1686      }
1687      connect(menu, &QMenu::triggered, this, &UnitDisplayStatusBarControl::onMenuSelection);
1688  }
1689  
1690  /** Lets the control know about the Options Model (and its signals) */
1691  void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel)
1692  {
1693      if (_optionsModel)
1694      {
1695          this->optionsModel = _optionsModel;
1696  
1697          // be aware of a display unit change reported by the OptionsModel object.
1698          connect(_optionsModel, &OptionsModel::displayUnitChanged, this, &UnitDisplayStatusBarControl::updateDisplayUnit);
1699  
1700          // initialize the display units label with the current value in the model.
1701          updateDisplayUnit(_optionsModel->getDisplayUnit());
1702      }
1703  }
1704  
1705  /** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
1706  void UnitDisplayStatusBarControl::updateDisplayUnit(BitcoinUnit newUnits)
1707  {
1708      setText(BitcoinUnits::longName(newUnits));
1709  }
1710  
1711  /** Shows context menu with Display Unit options by the mouse coordinates */
1712  void UnitDisplayStatusBarControl::onDisplayUnitsClicked(const QPoint& point)
1713  {
1714      QPoint globalPos = mapToGlobal(point);
1715      menu->exec(globalPos);
1716  }
1717  
1718  /** Tells underlying optionsModel to update its current display unit. */
1719  void UnitDisplayStatusBarControl::onMenuSelection(QAction* action)
1720  {
1721      if (action)
1722      {
1723          optionsModel->setDisplayUnit(action->data());
1724      }
1725  }