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 }