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