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 }