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