diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 0cc07b4d1b..3db9eba60f 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -1,278 +1,280 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent) : QDialog(parent), ui(new Ui::AskPassphraseDialog), mode(_mode), model(0), fCapsLock(false) { ui->setupUi(this); ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint()); ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint()); ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint()); ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE); ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE); ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE); // Setup Caps Lock detection. ui->passEdit1->installEventFilter(this); ui->passEdit2->installEventFilter(this); ui->passEdit3->installEventFilter(this); switch (mode) { case Encrypt: // Ask passphrase x2 ui->warningLabel->setText( tr("Enter the new passphrase to the wallet.
Please use a " "passphrase of ten or more random characters, or " "eight or more words.")); ui->passLabel1->hide(); ui->passEdit1->hide(); setWindowTitle(tr("Encrypt wallet")); break; case Unlock: // Ask passphrase ui->warningLabel->setText(tr("This operation needs your wallet " "passphrase to unlock the wallet.")); ui->passLabel2->hide(); ui->passEdit2->hide(); ui->passLabel3->hide(); ui->passEdit3->hide(); setWindowTitle(tr("Unlock wallet")); break; case Decrypt: // Ask passphrase ui->warningLabel->setText(tr("This operation needs your wallet " "passphrase to decrypt the wallet.")); ui->passLabel2->hide(); ui->passEdit2->hide(); ui->passLabel3->hide(); ui->passEdit3->hide(); setWindowTitle(tr("Decrypt wallet")); break; case ChangePass: // Ask old passphrase + new passphrase x2 setWindowTitle(tr("Change passphrase")); ui->warningLabel->setText(tr( "Enter the old passphrase and new passphrase to the wallet.")); break; } textChanged(); connect(ui->toggleShowPasswordButton, &QPushButton::toggled, this, &AskPassphraseDialog::toggleShowPassword); connect(ui->passEdit1, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged); connect(ui->passEdit2, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged); connect(ui->passEdit3, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged); } AskPassphraseDialog::~AskPassphraseDialog() { secureClearPassFields(); delete ui; } void AskPassphraseDialog::setModel(WalletModel *_model) { this->model = _model; } void AskPassphraseDialog::accept() { SecureString oldpass, newpass1, newpass2; - if (!model) return; + if (!model) { + return; + } oldpass.reserve(MAX_PASSPHRASE_SIZE); newpass1.reserve(MAX_PASSPHRASE_SIZE); newpass2.reserve(MAX_PASSPHRASE_SIZE); // TODO: get rid of this .c_str() by implementing // SecureString::operator=(std::string) // Alternately, find a way to make this input mlock()'d to begin with. oldpass.assign(ui->passEdit1->text().toStdString().c_str()); newpass1.assign(ui->passEdit2->text().toStdString().c_str()); newpass2.assign(ui->passEdit3->text().toStdString().c_str()); secureClearPassFields(); switch (mode) { case Encrypt: { if (newpass1.empty() || newpass2.empty()) { // Cannot encrypt with empty passphrase break; } QMessageBox::StandardButton retval = QMessageBox::question( this, tr("Confirm wallet encryption"), tr("Warning: If you encrypt your wallet and lose your " "passphrase, you will LOSE ALL OF YOUR BITCOINS!") + "

" + tr("Are you sure you wish to encrypt your wallet?"), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if (retval == QMessageBox::Yes) { if (newpass1 == newpass2) { if (model->setWalletEncrypted(true, newpass1)) { QMessageBox::warning( this, tr("Wallet encrypted"), "" + tr("Your wallet is now encrypted. " "Remember that encrypting your wallet " "cannot fully protect your bitcoins from " "being stolen by malware infecting your " "computer.") + "

" + tr("IMPORTANT: Any previous backups you have " "made of your wallet file should be " "replaced with the newly generated, " "encrypted wallet file. " "For security reasons, previous backups of " "the unencrypted wallet file will become " "useless as soon as you start using the " "new, encrypted wallet.") + "
"); } else { QMessageBox::critical( this, tr("Wallet encryption failed"), tr("Wallet encryption failed due to an internal " "error. Your wallet was not encrypted.")); } QDialog::accept(); // Success } else { QMessageBox::critical( this, tr("Wallet encryption failed"), tr("The supplied passphrases do not match.")); } } else { QDialog::reject(); // Cancelled } } break; case Unlock: if (!model->setWalletLocked(false, oldpass)) { QMessageBox::critical(this, tr("Wallet unlock failed"), tr("The passphrase entered for the " "wallet decryption was incorrect.")); } else { QDialog::accept(); // Success } break; case Decrypt: if (!model->setWalletEncrypted(false, oldpass)) { QMessageBox::critical(this, tr("Wallet decryption failed"), tr("The passphrase entered for the " "wallet decryption was incorrect.")); } else { QDialog::accept(); // Success } break; case ChangePass: if (newpass1 == newpass2) { if (model->changePassphrase(oldpass, newpass1)) { QMessageBox::information( this, tr("Wallet encrypted"), tr("Wallet passphrase was successfully changed.")); QDialog::accept(); // Success } else { QMessageBox::critical( this, tr("Wallet encryption failed"), tr("The passphrase entered for the wallet decryption " "was incorrect.")); } } else { QMessageBox::critical( this, tr("Wallet encryption failed"), tr("The supplied passphrases do not match.")); } break; } } void AskPassphraseDialog::textChanged() { // Validate input, set Ok button to enabled when acceptable bool acceptable = false; switch (mode) { case Encrypt: // New passphrase x2 acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); break; case Unlock: // Old passphrase x1 case Decrypt: acceptable = !ui->passEdit1->text().isEmpty(); break; case ChangePass: // Old passphrase x1, new passphrase x2 acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); break; } ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable); } bool AskPassphraseDialog::event(QEvent *event) { // Detect Caps Lock key press. if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); if (ke->key() == Qt::Key_CapsLock) { fCapsLock = !fCapsLock; } if (fCapsLock) { ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!")); } else { ui->capsLabel->clear(); } } return QWidget::event(event); } void AskPassphraseDialog::toggleShowPassword(bool show) { ui->toggleShowPasswordButton->setDown(show); const auto renderingMode = show ? QLineEdit::Normal : QLineEdit::Password; ui->passEdit1->setEchoMode(renderingMode); ui->passEdit2->setEchoMode(renderingMode); ui->passEdit3->setEchoMode(renderingMode); } bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event) { /* Detect Caps Lock. * There is no good OS-independent way to check a key state in Qt, but we * can detect Caps Lock by checking for the following condition: * Shift key is down and the result is a lower case character, or * Shift key is not down and the result is an upper case character. */ if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); QString str = ke->text(); if (str.length() != 0) { const QChar *psz = str.unicode(); bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0; if ((fShift && *psz >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) { fCapsLock = true; ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!")); } else if (psz->isLetter()) { fCapsLock = false; ui->capsLabel->clear(); } } } return QDialog::eventFilter(object, event); } static void SecureClearQLineEdit(QLineEdit *edit) { // Attempt to overwrite text so that they do not linger around in memory edit->setText(QString(" ").repeated(edit->text().size())); edit->clear(); } void AskPassphraseDialog::secureClearPassFields() { SecureClearQLineEdit(ui->passEdit1); SecureClearQLineEdit(ui->passEdit2); SecureClearQLineEdit(ui->passEdit3); } diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index 646187b521..99d76a2409 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -1,161 +1,171 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include bool BannedNodeLessThan::operator()(const CCombinedBan &left, const CCombinedBan &right) const { const CCombinedBan *pLeft = &left; const CCombinedBan *pRight = &right; - if (order == Qt::DescendingOrder) std::swap(pLeft, pRight); + if (order == Qt::DescendingOrder) { + std::swap(pLeft, pRight); + } switch (column) { case BanTableModel::Address: return pLeft->subnet.ToString().compare(pRight->subnet.ToString()) < 0; case BanTableModel::Bantime: return pLeft->banEntry.nBanUntil < pRight->banEntry.nBanUntil; } return false; } // private implementation class BanTablePriv { public: /** Local cache of peer information */ QList cachedBanlist; /** Column to sort nodes by (default to unsorted) */ int sortColumn{-1}; /** Order (ascending or descending) to sort nodes by */ Qt::SortOrder sortOrder; /** Pull a full list of banned nodes from CNode into our cache */ void refreshBanlist(interfaces::Node &node) { banmap_t banMap; node.getBanned(banMap); cachedBanlist.clear(); cachedBanlist.reserve(banMap.size()); for (const auto &entry : banMap) { CCombinedBan banEntry; banEntry.subnet = entry.first; banEntry.banEntry = entry.second; cachedBanlist.append(banEntry); } if (sortColumn >= 0) { // sort cachedBanlist (use stable sort to prevent rows jumping // around unnecessarily) std::stable_sort(cachedBanlist.begin(), cachedBanlist.end(), BannedNodeLessThan(sortColumn, sortOrder)); } } int size() const { return cachedBanlist.size(); } CCombinedBan *index(int idx) { - if (idx >= 0 && idx < cachedBanlist.size()) return &cachedBanlist[idx]; + if (idx >= 0 && idx < cachedBanlist.size()) { + return &cachedBanlist[idx]; + } return 0; } }; BanTableModel::BanTableModel(interfaces::Node &node, ClientModel *parent) : QAbstractTableModel(parent), m_node(node), clientModel(parent) { columns << tr("IP/Netmask") << tr("Banned Until"); priv.reset(new BanTablePriv()); // load initial data refresh(); } BanTableModel::~BanTableModel() { // Intentionally left empty } int BanTableModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return priv->size(); } int BanTableModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return columns.length(); } QVariant BanTableModel::data(const QModelIndex &index, int role) const { - if (!index.isValid()) return QVariant(); + if (!index.isValid()) { + return QVariant(); + } CCombinedBan *rec = static_cast(index.internalPointer()); if (role == Qt::DisplayRole) { switch (index.column()) { case Address: return QString::fromStdString(rec->subnet.ToString()); case Bantime: QDateTime date = QDateTime::fromMSecsSinceEpoch(0); date = date.addSecs(rec->banEntry.nBanUntil); return date.toString(Qt::SystemLocaleLongDate); } } return QVariant(); } QVariant BanTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole && section < columns.size()) { return columns[section]; } } return QVariant(); } Qt::ItemFlags BanTableModel::flags(const QModelIndex &index) const { - if (!index.isValid()) return 0; + if (!index.isValid()) { + return 0; + } Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; return retval; } QModelIndex BanTableModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); CCombinedBan *data = priv->index(row); - if (data) return createIndex(row, column, data); + if (data) { + return createIndex(row, column, data); + } return QModelIndex(); } void BanTableModel::refresh() { Q_EMIT layoutAboutToBeChanged(); priv->refreshBanlist(m_node); Q_EMIT layoutChanged(); } void BanTableModel::sort(int column, Qt::SortOrder order) { priv->sortColumn = column; priv->sortOrder = order; refresh(); } bool BanTableModel::shouldShow() { return priv->size() > 0; } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index cb679f04c5..eacdc7ab81 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1,1463 +1,1481 @@ // Copyright (c) 2011-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_MAC #include #endif #include #include #include #include #include #include #include #include #include #ifdef ENABLE_WALLET #include #include #include #include #endif // ENABLE_WALLET #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string BitcoinGUI::DEFAULT_UIPLATFORM = #if defined(Q_OS_MAC) "macosx" #elif defined(Q_OS_WIN) "windows" #else "other" #endif ; BitcoinGUI::BitcoinGUI(interfaces::Node &node, const Config *configIn, const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) : QMainWindow(parent), m_node(node), config(configIn), platformStyle(_platformStyle), m_network_style(networkStyle) { QSettings settings; if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { // Restore failed (perhaps missing setting), center the window move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); } #ifdef ENABLE_WALLET enableWallet = WalletModel::isWalletEnabled(); #endif // ENABLE_WALLET QApplication::setWindowIcon(m_network_style->getTrayAndWindowIcon()); setWindowIcon(m_network_style->getTrayAndWindowIcon()); updateWindowTitle(); rpcConsole = new RPCConsole(node, _platformStyle, 0); helpMessageDialog = new HelpMessageDialog(node, this, false); #ifdef ENABLE_WALLET if (enableWallet) { /** Create wallet frame and make it the central widget */ walletFrame = new WalletFrame(_platformStyle, this); setCentralWidget(walletFrame); } else #endif // ENABLE_WALLET { /** * When compiled without wallet or -disablewallet is provided, the * central widget is the rpc console. */ setCentralWidget(rpcConsole); Q_EMIT consoleShown(rpcConsole); } // Accept D&D of URIs setAcceptDrops(true); // Create actions for the toolbar, menu bar and tray/dock icon // Needs walletFrame to be initialized createActions(); // Create application menu bar createMenuBar(); // Create the toolbars createToolBars(); // Create system tray icon and notification if (QSystemTrayIcon::isSystemTrayAvailable()) { createTrayIcon(); } notificator = new Notificator(QApplication::applicationName(), trayIcon, this); // Create status bar statusBar(); // Disable size grip because it looks ugly and nobody needs it statusBar()->setSizeGripEnabled(false); // Status bar notification icons QFrame *frameBlocks = new QFrame(); frameBlocks->setContentsMargins(0, 0, 0, 0); frameBlocks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); frameBlocksLayout->setContentsMargins(3, 0, 3, 0); frameBlocksLayout->setSpacing(3); unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle); labelWalletEncryptionIcon = new QLabel(); labelWalletHDStatusIcon = new QLabel(); labelProxyIcon = new GUIUtil::ClickableLabel(); connectionsControl = new GUIUtil::ClickableLabel(); labelBlocksIcon = new GUIUtil::ClickableLabel(); if (enableWallet) { frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(unitDisplayControl); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelWalletEncryptionIcon); frameBlocksLayout->addWidget(labelWalletHDStatusIcon); } frameBlocksLayout->addWidget(labelProxyIcon); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(connectionsControl); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelBlocksIcon); frameBlocksLayout->addStretch(); // Progress bar and label for blocks download progressBarLabel = new QLabel(); progressBarLabel->setVisible(false); progressBar = new GUIUtil::ProgressBar(); progressBar->setAlignment(Qt::AlignCenter); progressBar->setVisible(false); // Override style sheet for progress bar for styles that have a segmented // progress bar, as they make the text unreadable (workaround for issue // #1071) // See https://doc.qt.io/qt-5/gallery.html QString curStyle = QApplication::style()->metaObject()->className(); if (curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle") { 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; }"); } statusBar()->addWidget(progressBarLabel); statusBar()->addWidget(progressBar); statusBar()->addPermanentWidget(frameBlocks); // Install event filter to be able to catch status tip events // (QEvent::StatusTip) this->installEventFilter(this); // Initially wallet actions should be disabled setWalletActionsEnabled(false); // Subscribe to notifications from core subscribeToCoreSignals(); connect(connectionsControl, &GUIUtil::ClickableLabel::clicked, [this] { m_node.setNetworkActive(!m_node.getNetworkActive()); }); connect(labelProxyIcon, &GUIUtil::ClickableLabel::clicked, [this] { openOptionsDialogWithTab(OptionsDialog::TAB_NETWORK); }); modalOverlay = new ModalOverlay(this->centralWidget()); #ifdef ENABLE_WALLET if (enableWallet) { connect(walletFrame, &WalletFrame::requestedSyncWarningInfo, this, &BitcoinGUI::showModalOverlay); connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay); connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay); } #endif } BitcoinGUI::~BitcoinGUI() { // Unsubscribe from notifications from core unsubscribeFromCoreSignals(); QSettings settings; settings.setValue("MainWindowGeometry", saveGeometry()); // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) if (trayIcon) { trayIcon->hide(); } #ifdef Q_OS_MAC delete appMenuBar; MacDockIconHandler::cleanup(); #endif delete rpcConsole; } void BitcoinGUI::createActions() { QActionGroup *tabGroup = new QActionGroup(this); overviewAction = new QAction(platformStyle->SingleColorIcon(":/icons/overview"), tr("&Overview"), this); overviewAction->setStatusTip(tr("Show general overview of wallet")); overviewAction->setToolTip(overviewAction->statusTip()); overviewAction->setCheckable(true); overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); tabGroup->addAction(overviewAction); sendCoinsAction = new QAction( platformStyle->SingleColorIcon(":/icons/send"), tr("&Send"), this); sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address")); sendCoinsAction->setToolTip(sendCoinsAction->statusTip()); sendCoinsAction->setCheckable(true); sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); tabGroup->addAction(sendCoinsAction); sendCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/send"), sendCoinsAction->text(), this); sendCoinsMenuAction->setStatusTip(sendCoinsAction->statusTip()); sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip()); receiveCoinsAction = new QAction( platformStyle->SingleColorIcon(":/icons/receiving_addresses"), tr("&Receive"), this); receiveCoinsAction->setStatusTip( tr("Request payments (generates QR codes and %1: URIs)") .arg(QString::fromStdString( config->GetChainParams().CashAddrPrefix()))); receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); receiveCoinsAction->setCheckable(true); receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); tabGroup->addAction(receiveCoinsAction); receiveCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/receiving_addresses"), receiveCoinsAction->text(), this); receiveCoinsMenuAction->setStatusTip(receiveCoinsAction->statusTip()); receiveCoinsMenuAction->setToolTip(receiveCoinsMenuAction->statusTip()); historyAction = new QAction(platformStyle->SingleColorIcon(":/icons/history"), tr("&Transactions"), this); historyAction->setStatusTip(tr("Browse transaction history")); historyAction->setToolTip(historyAction->statusTip()); historyAction->setCheckable(true); historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); tabGroup->addAction(historyAction); #ifdef ENABLE_WALLET // These showNormalIfMinimized are needed because Send Coins and Receive // Coins can be triggered from the tray menu, and need to show the GUI to be // useful. connect(overviewAction, &QAction::triggered, [this] { showNormalIfMinimized(); }); connect(overviewAction, &QAction::triggered, this, &BitcoinGUI::gotoOverviewPage); connect(sendCoinsAction, &QAction::triggered, [this] { showNormalIfMinimized(); }); connect(sendCoinsAction, &QAction::triggered, [this] { gotoSendCoinsPage(); }); connect(sendCoinsMenuAction, &QAction::triggered, [this] { showNormalIfMinimized(); }); connect(sendCoinsMenuAction, &QAction::triggered, [this] { gotoSendCoinsPage(); }); connect(receiveCoinsAction, &QAction::triggered, [this] { showNormalIfMinimized(); }); connect(receiveCoinsAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage); connect(receiveCoinsMenuAction, &QAction::triggered, [this] { showNormalIfMinimized(); }); connect(receiveCoinsMenuAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage); connect(historyAction, &QAction::triggered, [this] { showNormalIfMinimized(); }); connect(historyAction, &QAction::triggered, this, &BitcoinGUI::gotoHistoryPage); #endif // ENABLE_WALLET quitAction = new QAction(platformStyle->TextColorIcon(":/icons/quit"), tr("E&xit"), this); quitAction->setStatusTip(tr("Quit application")); quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); quitAction->setMenuRole(QAction::QuitRole); aboutAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&About %1").arg(tr(PACKAGE_NAME)), this); aboutAction->setStatusTip( tr("Show information about %1").arg(tr(PACKAGE_NAME))); aboutAction->setMenuRole(QAction::AboutRole); aboutAction->setEnabled(false); aboutQtAction = new QAction(platformStyle->TextColorIcon(":/icons/about_qt"), tr("About &Qt"), this); aboutQtAction->setStatusTip(tr("Show information about Qt")); aboutQtAction->setMenuRole(QAction::AboutQtRole); optionsAction = new QAction(platformStyle->TextColorIcon(":/icons/options"), tr("&Options..."), this); optionsAction->setStatusTip( tr("Modify configuration options for %1").arg(tr(PACKAGE_NAME))); optionsAction->setMenuRole(QAction::PreferencesRole); optionsAction->setEnabled(false); toggleHideAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&Show / Hide"), this); toggleHideAction->setStatusTip(tr("Show or hide the main Window")); encryptWalletAction = new QAction(platformStyle->TextColorIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); encryptWalletAction->setStatusTip( tr("Encrypt the private keys that belong to your wallet")); encryptWalletAction->setCheckable(true); backupWalletAction = new QAction(platformStyle->TextColorIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); backupWalletAction->setStatusTip(tr("Backup wallet to another location")); changePassphraseAction = new QAction(platformStyle->TextColorIcon(":/icons/key"), tr("&Change Passphrase..."), this); changePassphraseAction->setStatusTip( tr("Change the passphrase used for wallet encryption")); signMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/edit"), tr("Sign &message..."), this); signMessageAction->setStatusTip( tr("Sign messages with your Bitcoin addresses to prove you own them")); verifyMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/verify"), tr("&Verify message..."), this); verifyMessageAction->setStatusTip( tr("Verify messages to ensure they were signed with specified Bitcoin " "addresses")); openRPCConsoleAction = new QAction(platformStyle->TextColorIcon(":/icons/debugwindow"), tr("&Debug window"), this); openRPCConsoleAction->setStatusTip( tr("Open debugging and diagnostic console")); // initially disable the debug window menu item openRPCConsoleAction->setEnabled(false); openRPCConsoleAction->setObjectName("openRPCConsoleAction"); usedSendingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Sending addresses..."), this); usedSendingAddressesAction->setStatusTip( tr("Show the list of used sending addresses and labels")); usedReceivingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Receiving addresses..."), this); usedReceivingAddressesAction->setStatusTip( tr("Show the list of used receiving addresses and labels")); openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this); openAction->setStatusTip( tr("Open a %1: URI or payment request") .arg(QString::fromStdString( config->GetChainParams().CashAddrPrefix()))); showHelpMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/info"), tr("&Command-line options"), this); showHelpMessageAction->setMenuRole(QAction::NoRole); showHelpMessageAction->setStatusTip( tr("Show the %1 help message to get a list with possible Bitcoin " "command-line options") .arg(tr(PACKAGE_NAME))); connect(quitAction, &QAction::triggered, qApp, QApplication::quit); connect(aboutAction, &QAction::triggered, this, &BitcoinGUI::aboutClicked); connect(aboutQtAction, &QAction::triggered, qApp, QApplication::aboutQt); connect(optionsAction, &QAction::triggered, this, &BitcoinGUI::optionsClicked); connect(toggleHideAction, &QAction::triggered, this, &BitcoinGUI::toggleHidden); connect(showHelpMessageAction, &QAction::triggered, this, &BitcoinGUI::showHelpMessageClicked); connect(openRPCConsoleAction, &QAction::triggered, this, &BitcoinGUI::showDebugWindow); // prevents an open debug window from becoming stuck/unusable on client // shutdown connect(quitAction, &QAction::triggered, rpcConsole, &QWidget::hide); #ifdef ENABLE_WALLET if (walletFrame) { connect(encryptWalletAction, &QAction::triggered, walletFrame, &WalletFrame::encryptWallet); connect(backupWalletAction, &QAction::triggered, walletFrame, &WalletFrame::backupWallet); connect(changePassphraseAction, &QAction::triggered, walletFrame, &WalletFrame::changePassphrase); connect(signMessageAction, &QAction::triggered, [this] { showNormalIfMinimized(); }); connect(signMessageAction, &QAction::triggered, [this] { gotoSignMessageTab(); }); connect(verifyMessageAction, &QAction::triggered, [this] { showNormalIfMinimized(); }); connect(verifyMessageAction, &QAction::triggered, [this] { gotoVerifyMessageTab(); }); connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses); connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses); connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked); } #endif // ENABLE_WALLET connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindowActivateConsole); connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_D), this), &QShortcut::activated, this, &BitcoinGUI::showDebugWindow); } void BitcoinGUI::createMenuBar() { #ifdef Q_OS_MAC // Create a decoupled menu bar on Mac which stays even if the window is // closed appMenuBar = new QMenuBar(); #else // Get the main window's menu bar on other platforms appMenuBar = menuBar(); #endif // Configure the menus QMenu *file = appMenuBar->addMenu(tr("&File")); if (walletFrame) { file->addAction(openAction); file->addAction(backupWalletAction); file->addAction(signMessageAction); file->addAction(verifyMessageAction); file->addSeparator(); file->addAction(usedSendingAddressesAction); file->addAction(usedReceivingAddressesAction); file->addSeparator(); } file->addAction(quitAction); QMenu *settings = appMenuBar->addMenu(tr("&Settings")); if (walletFrame) { settings->addAction(encryptWalletAction); settings->addAction(changePassphraseAction); settings->addSeparator(); } settings->addAction(optionsAction); QMenu *help = appMenuBar->addMenu(tr("&Help")); if (walletFrame) { help->addAction(openRPCConsoleAction); } help->addAction(showHelpMessageAction); help->addSeparator(); help->addAction(aboutAction); help->addAction(aboutQtAction); } void BitcoinGUI::createToolBars() { if (walletFrame) { QToolBar *toolbar = addToolBar(tr("Tabs toolbar")); appToolBar = toolbar; toolbar->setContextMenuPolicy(Qt::PreventContextMenu); toolbar->setMovable(false); toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolbar->addAction(overviewAction); toolbar->addAction(sendCoinsAction); toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); overviewAction->setChecked(true); #ifdef ENABLE_WALLET QWidget *spacer = new QWidget(); spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); toolbar->addWidget(spacer); m_wallet_selector = new QComboBox(); connect(m_wallet_selector, static_cast( &QComboBox::currentIndexChanged), this, &BitcoinGUI::setCurrentWalletBySelectorIndex); m_wallet_selector_label = new QLabel(); m_wallet_selector_label->setText(tr("Wallet:") + " "); m_wallet_selector_label->setBuddy(m_wallet_selector); m_wallet_selector_label_action = appToolBar->addWidget(m_wallet_selector_label); m_wallet_selector_action = appToolBar->addWidget(m_wallet_selector); m_wallet_selector_label_action->setVisible(false); m_wallet_selector_action->setVisible(false); #endif } } void BitcoinGUI::setClientModel(ClientModel *_clientModel) { this->clientModel = _clientModel; if (_clientModel) { // Create system tray menu (or setup the dock menu) that late to prevent // users from calling actions, while the client has not yet fully loaded createTrayIconMenu(); // Keep up to date with client updateNetworkState(); connect(_clientModel, &ClientModel::numConnectionsChanged, this, &BitcoinGUI::setNumConnections); connect(_clientModel, &ClientModel::networkActiveChanged, this, &BitcoinGUI::setNetworkActive); modalOverlay->setKnownBestHeight( _clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(_clientModel->getHeaderTipTime())); setNumBlocks(m_node.getNumBlocks(), QDateTime::fromTime_t(m_node.getLastBlockTime()), m_node.getVerificationProgress(), false); connect(_clientModel, &ClientModel::numBlocksChanged, this, &BitcoinGUI::setNumBlocks); // Receive and report messages from client model connect(_clientModel, &ClientModel::message, [this](const QString &title, const QString &message, unsigned int style) { this->message(title, message, style); }); // Show progress dialog connect(_clientModel, &ClientModel::showProgress, this, &BitcoinGUI::showProgress); rpcConsole->setClientModel(_clientModel); updateProxyIcon(); #ifdef ENABLE_WALLET if (walletFrame) { walletFrame->setClientModel(_clientModel); } #endif // ENABLE_WALLET unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel()); OptionsModel *optionsModel = _clientModel->getOptionsModel(); if (optionsModel && trayIcon) { // be aware of the tray icon disable state change reported by the // OptionsModel object. connect(optionsModel, &OptionsModel::hideTrayIconChanged, this, &BitcoinGUI::setTrayIconVisible); // initialize the disable state of the tray icon with the current // value in the model. setTrayIconVisible(optionsModel->getHideTrayIcon()); } } else { // Disable possibility to show main window via action toggleHideAction->setEnabled(false); if (trayIconMenu) { // Disable context menu on tray icon trayIconMenu->clear(); } // Propagate cleared model to child objects rpcConsole->setClientModel(nullptr); #ifdef ENABLE_WALLET if (walletFrame) { walletFrame->setClientModel(nullptr); } #endif // ENABLE_WALLET unitDisplayControl->setOptionsModel(nullptr); } } #ifdef ENABLE_WALLET void BitcoinGUI::setWalletController(WalletController *wallet_controller) { assert(!m_wallet_controller); assert(wallet_controller); m_wallet_controller = wallet_controller; connect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet); connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet); for (WalletModel *wallet_model : m_wallet_controller->getWallets()) { addWallet(wallet_model); } } void BitcoinGUI::addWallet(WalletModel *walletModel) { if (!walletFrame) { return; } const QString display_name = walletModel->getDisplayName(); setWalletActionsEnabled(true); rpcConsole->addWallet(walletModel); walletFrame->addWallet(walletModel); m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel)); if (m_wallet_selector->count() == 2) { m_wallet_selector_label_action->setVisible(true); m_wallet_selector_action->setVisible(true); } } void BitcoinGUI::removeWallet(WalletModel *walletModel) { if (!walletFrame) { return; } int index = m_wallet_selector->findData(QVariant::fromValue(walletModel)); m_wallet_selector->removeItem(index); if (m_wallet_selector->count() == 0) { setWalletActionsEnabled(false); } else if (m_wallet_selector->count() == 1) { m_wallet_selector_label_action->setVisible(false); m_wallet_selector_action->setVisible(false); } rpcConsole->removeWallet(walletModel); walletFrame->removeWallet(walletModel); updateWindowTitle(); } void BitcoinGUI::setCurrentWallet(WalletModel *wallet_model) { if (!walletFrame) { return; } walletFrame->setCurrentWallet(wallet_model); for (int index = 0; index < m_wallet_selector->count(); ++index) { if (m_wallet_selector->itemData(index).value() == wallet_model) { m_wallet_selector->setCurrentIndex(index); break; } } updateWindowTitle(); } void BitcoinGUI::setCurrentWalletBySelectorIndex(int index) { WalletModel *wallet_model = m_wallet_selector->itemData(index).value(); if (wallet_model) { setCurrentWallet(wallet_model); } } void BitcoinGUI::removeAllWallets() { - if (!walletFrame) return; + if (!walletFrame) { + return; + } setWalletActionsEnabled(false); walletFrame->removeAllWallets(); } #endif // ENABLE_WALLET void BitcoinGUI::setWalletActionsEnabled(bool enabled) { overviewAction->setEnabled(enabled); sendCoinsAction->setEnabled(enabled); sendCoinsMenuAction->setEnabled(enabled); receiveCoinsAction->setEnabled(enabled); receiveCoinsMenuAction->setEnabled(enabled); historyAction->setEnabled(enabled); encryptWalletAction->setEnabled(enabled); backupWalletAction->setEnabled(enabled); changePassphraseAction->setEnabled(enabled); signMessageAction->setEnabled(enabled); verifyMessageAction->setEnabled(enabled); usedSendingAddressesAction->setEnabled(enabled); usedReceivingAddressesAction->setEnabled(enabled); openAction->setEnabled(enabled); } void BitcoinGUI::createTrayIcon() { assert(QSystemTrayIcon::isSystemTrayAvailable()); #ifndef Q_OS_MAC if (QSystemTrayIcon::isSystemTrayAvailable()) { trayIcon = new QSystemTrayIcon(m_network_style->getTrayAndWindowIcon(), this); QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " + m_network_style->getTitleAddText(); trayIcon->setToolTip(toolTip); } #endif } void BitcoinGUI::createTrayIconMenu() { #ifndef Q_OS_MAC // Return if trayIcon is unset (only on non-macOSes) if (!trayIcon) { return; } trayIconMenu = new QMenu(this); trayIcon->setContextMenu(trayIconMenu); connect(trayIcon, &QSystemTrayIcon::activated, this, &BitcoinGUI::trayIconActivated); #else // Note: On macOS, the Dock icon is used to provide the tray's // functionality. MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance(); connect(dockIconHandler, &MacDockIconHandler::dockIconClicked, this, &BitcoinGUI::macosDockIconActivated); trayIconMenu = new QMenu(this); trayIconMenu->setAsDockMenu(); #endif // Configuration of the tray icon (or Dock icon) menu #ifndef Q_OS_MAC // Note: On macOS, the Dock icon's menu already has Show / Hide action. trayIconMenu->addAction(toggleHideAction); trayIconMenu->addSeparator(); #endif trayIconMenu->addAction(sendCoinsMenuAction); trayIconMenu->addAction(receiveCoinsMenuAction); trayIconMenu->addSeparator(); trayIconMenu->addAction(signMessageAction); trayIconMenu->addAction(verifyMessageAction); trayIconMenu->addSeparator(); trayIconMenu->addAction(optionsAction); trayIconMenu->addAction(openRPCConsoleAction); #ifndef Q_OS_MAC // This is built-in on macOS trayIconMenu->addSeparator(); trayIconMenu->addAction(quitAction); #endif } #ifndef Q_OS_MAC void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) { if (reason == QSystemTrayIcon::Trigger) { // Click on system tray icon triggers show/hide of the main window toggleHidden(); } } #else void BitcoinGUI::macosDockIconActivated() { show(); activateWindow(); } #endif void BitcoinGUI::optionsClicked() { openOptionsDialogWithTab(OptionsDialog::TAB_MAIN); } void BitcoinGUI::aboutClicked() { - if (!clientModel) return; + if (!clientModel) { + return; + } HelpMessageDialog dlg(m_node, this, true); dlg.exec(); } void BitcoinGUI::showDebugWindow() { GUIUtil::bringToFront(rpcConsole); Q_EMIT consoleShown(rpcConsole); } void BitcoinGUI::showDebugWindowActivateConsole() { rpcConsole->setTabFocus(RPCConsole::TAB_CONSOLE); showDebugWindow(); } void BitcoinGUI::showHelpMessageClicked() { helpMessageDialog->show(); } #ifdef ENABLE_WALLET void BitcoinGUI::openClicked() { OpenURIDialog dlg(config->GetChainParams(), this); if (dlg.exec()) { Q_EMIT receivedURI(dlg.getURI()); } } void BitcoinGUI::gotoOverviewPage() { overviewAction->setChecked(true); - if (walletFrame) walletFrame->gotoOverviewPage(); + if (walletFrame) { + walletFrame->gotoOverviewPage(); + } } void BitcoinGUI::gotoHistoryPage() { historyAction->setChecked(true); - if (walletFrame) walletFrame->gotoHistoryPage(); + if (walletFrame) { + walletFrame->gotoHistoryPage(); + } } void BitcoinGUI::gotoReceiveCoinsPage() { receiveCoinsAction->setChecked(true); - if (walletFrame) walletFrame->gotoReceiveCoinsPage(); + if (walletFrame) { + walletFrame->gotoReceiveCoinsPage(); + } } void BitcoinGUI::gotoSendCoinsPage(QString addr) { sendCoinsAction->setChecked(true); - if (walletFrame) walletFrame->gotoSendCoinsPage(addr); + if (walletFrame) { + walletFrame->gotoSendCoinsPage(addr); + } } void BitcoinGUI::gotoSignMessageTab(QString addr) { - if (walletFrame) walletFrame->gotoSignMessageTab(addr); + if (walletFrame) { + walletFrame->gotoSignMessageTab(addr); + } } void BitcoinGUI::gotoVerifyMessageTab(QString addr) { - if (walletFrame) walletFrame->gotoVerifyMessageTab(addr); + if (walletFrame) { + walletFrame->gotoVerifyMessageTab(addr); + } } #endif // ENABLE_WALLET void BitcoinGUI::updateNetworkState() { int count = clientModel->getNumConnections(); QString icon; switch (count) { case 0: icon = ":/icons/connect_0"; break; case 1: case 2: case 3: icon = ":/icons/connect_1"; break; case 4: case 5: case 6: icon = ":/icons/connect_2"; break; case 7: case 8: case 9: icon = ":/icons/connect_3"; break; default: icon = ":/icons/connect_4"; break; } QString tooltip; if (m_node.getNetworkActive()) { tooltip = tr("%n active connection(s) to Bitcoin network", "", count) + QString(".
") + tr("Click to disable network activity."); } else { tooltip = tr("Network activity disabled.") + QString("
") + tr("Click to enable network activity again."); icon = ":/icons/network_disabled"; } // Don't word-wrap this (fixed-width) tooltip tooltip = QString("") + tooltip + QString(""); connectionsControl->setToolTip(tooltip); connectionsControl->setPixmap(platformStyle->SingleColorIcon(icon).pixmap( STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); } void BitcoinGUI::setNumConnections(int count) { updateNetworkState(); } void BitcoinGUI::setNetworkActive(bool networkActive) { updateNetworkState(); } void BitcoinGUI::updateHeadersSyncProgressLabel() { int64_t headersTipTime = clientModel->getHeaderTipTime(); int headersTipHeight = clientModel->getHeaderTipHeight(); int estHeadersLeft = (GetTime() - headersTipTime) / config->GetChainParams().GetConsensus().nPowTargetSpacing; if (estHeadersLeft > HEADER_HEIGHT_DELTA_SYNC) { progressBarLabel->setText( tr("Syncing Headers (%1%)...") .arg(QString::number(100.0 / (headersTipHeight + estHeadersLeft) * headersTipHeight, 'f', 1))); } } void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab) { if (!clientModel || !clientModel->getOptionsModel()) { return; } OptionsDialog dlg(this, enableWallet); dlg.setCurrentTab(tab); dlg.setModel(clientModel->getOptionsModel()); dlg.exec(); } void BitcoinGUI::setNumBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, bool header) { if (modalOverlay) { if (header) { modalOverlay->setKnownBestHeight(count, blockDate); } else { modalOverlay->tipUpdate(count, blockDate, nVerificationProgress); } } if (!clientModel) { return; } // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait // until chain-sync starts -> garbled text) statusBar()->clearMessage(); // Acquire current block source enum BlockSource blockSource = clientModel->getBlockSource(); switch (blockSource) { case BlockSource::NETWORK: if (header) { updateHeadersSyncProgressLabel(); return; } progressBarLabel->setText(tr("Synchronizing with network...")); updateHeadersSyncProgressLabel(); break; case BlockSource::DISK: if (header) { progressBarLabel->setText(tr("Indexing blocks on disk...")); } else { progressBarLabel->setText(tr("Processing blocks on disk...")); } break; case BlockSource::REINDEX: progressBarLabel->setText(tr("Reindexing blocks on disk...")); break; case BlockSource::NONE: if (header) { return; } progressBarLabel->setText(tr("Connecting to peers...")); break; } QString tooltip; QDateTime currentDate = QDateTime::currentDateTime(); qint64 secs = blockDate.secsTo(currentDate); tooltip = tr("Processed %n block(s) of transaction history.", "", count); // Set icon state: spinning if catching up, tick otherwise if (secs < MAX_BLOCK_TIME_GAP) { tooltip = tr("Up to date") + QString(".
") + tooltip; labelBlocksIcon->setPixmap( platformStyle->SingleColorIcon(":/icons/synced") .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); #ifdef ENABLE_WALLET if (walletFrame) { walletFrame->showOutOfSyncWarning(false); modalOverlay->showHide(true, true); } #endif // ENABLE_WALLET progressBarLabel->setVisible(false); progressBar->setVisible(false); } else { QString timeBehindText = GUIUtil::formatNiceTimeOffset(secs); progressBarLabel->setVisible(true); progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); progressBar->setMaximum(1000000000); progressBar->setValue(nVerificationProgress * 1000000000.0 + 0.5); progressBar->setVisible(true); tooltip = tr("Catching up...") + QString("
") + tooltip; if (count != prevBlocks) { labelBlocksIcon->setPixmap( platformStyle ->SingleColorIcon(QString(":/movies/spinner-%1") .arg(spinnerFrame, 3, 10, QChar('0'))) .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES; } prevBlocks = count; #ifdef ENABLE_WALLET if (walletFrame) { walletFrame->showOutOfSyncWarning(true); modalOverlay->showHide(); } #endif // ENABLE_WALLET tooltip += QString("
"); tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText); tooltip += QString("
"); tooltip += tr("Transactions after this will not yet be visible."); } // Don't word-wrap this (fixed-width) tooltip tooltip = QString("") + tooltip + QString(""); labelBlocksIcon->setToolTip(tooltip); progressBarLabel->setToolTip(tooltip); progressBar->setToolTip(tooltip); } void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, bool *ret) { // default title QString strTitle = tr("Bitcoin"); // Default to information icon int nMBoxIcon = QMessageBox::Information; int nNotifyIcon = Notificator::Information; QString msgType; // Prefer supplied title over style based title if (!title.isEmpty()) { msgType = title; } else { switch (style) { case CClientUIInterface::MSG_ERROR: msgType = tr("Error"); break; case CClientUIInterface::MSG_WARNING: msgType = tr("Warning"); break; case CClientUIInterface::MSG_INFORMATION: msgType = tr("Information"); break; default: break; } } // Append title to "Bitcoin - " if (!msgType.isEmpty()) { strTitle += " - " + msgType; } // Check for error/warning icon if (style & CClientUIInterface::ICON_ERROR) { nMBoxIcon = QMessageBox::Critical; nNotifyIcon = Notificator::Critical; } else if (style & CClientUIInterface::ICON_WARNING) { nMBoxIcon = QMessageBox::Warning; nNotifyIcon = Notificator::Warning; } // Display message if (style & CClientUIInterface::MODAL) { // Check for buttons, use OK as default, if none was supplied QMessageBox::StandardButton buttons; if (!(buttons = (QMessageBox::StandardButton)( - style & CClientUIInterface::BTN_MASK))) + style & CClientUIInterface::BTN_MASK))) { buttons = QMessageBox::Ok; + } showNormalIfMinimized(); QMessageBox mBox(static_cast(nMBoxIcon), strTitle, message, buttons, this); mBox.setTextFormat(Qt::PlainText); int r = mBox.exec(); if (ret != nullptr) { *ret = r == QMessageBox::Ok; } - } else + } else { notificator->notify(static_cast(nNotifyIcon), strTitle, message); + } } void BitcoinGUI::changeEvent(QEvent *e) { QMainWindow::changeEvent(e); #ifndef Q_OS_MAC // Ignored on Mac if (e->type() == QEvent::WindowStateChange) { if (clientModel && clientModel->getOptionsModel() && clientModel->getOptionsModel()->getMinimizeToTray()) { QWindowStateChangeEvent *wsevt = static_cast(e); if (!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized()) { QTimer::singleShot(0, this, &BitcoinGUI::hide); e->ignore(); } else if ((wsevt->oldState() & Qt::WindowMinimized) && !isMinimized()) { QTimer::singleShot(0, this, &BitcoinGUI::show); e->ignore(); } } } #endif } void BitcoinGUI::closeEvent(QCloseEvent *event) { #ifndef Q_OS_MAC // Ignored on Mac if (clientModel && clientModel->getOptionsModel()) { if (!clientModel->getOptionsModel()->getMinimizeOnClose()) { // close rpcConsole in case it was open to make some space for the // shutdown window rpcConsole->close(); QApplication::quit(); } else { QMainWindow::showMinimized(); event->ignore(); } } #else QMainWindow::closeEvent(event); #endif } void BitcoinGUI::showEvent(QShowEvent *event) { // enable the debug window when the main window shows up openRPCConsoleAction->setEnabled(true); aboutAction->setEnabled(true); optionsAction->setEnabled(true); } #ifdef ENABLE_WALLET void BitcoinGUI::incomingTransaction(const QString &date, int unit, const Amount amount, const QString &type, const QString &address, const QString &label, const QString &walletName) { // On new transaction, make an info balloon QString msg = tr("Date: %1\n").arg(date) + tr("Amount: %1\n") .arg(BitcoinUnits::formatWithUnit(unit, amount, true)); if (m_node.getWallets().size() > 1 && !walletName.isEmpty()) { msg += tr("Wallet: %1\n").arg(walletName); } msg += tr("Type: %1\n").arg(type); if (!label.isEmpty()) { msg += tr("Label: %1\n").arg(label); } else if (!address.isEmpty()) { msg += tr("Address: %1\n").arg(address); } message(amount < Amount::zero() ? tr("Sent transaction") : tr("Incoming transaction"), msg, CClientUIInterface::MSG_INFORMATION); } #endif // ENABLE_WALLET void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event) { // Accept only URIs if (event->mimeData()->hasUrls()) { event->acceptProposedAction(); } } void BitcoinGUI::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls()) { for (const QUrl &uri : event->mimeData()->urls()) { Q_EMIT receivedURI(uri.toString()); } } event->acceptProposedAction(); } bool BitcoinGUI::eventFilter(QObject *object, QEvent *event) { // Catch status tip events if (event->type() == QEvent::StatusTip) { // Prevent adding text from setStatusTip(), if we currently use the // status bar for displaying other stuff if (progressBarLabel->isVisible() || progressBar->isVisible()) { return true; } } return QMainWindow::eventFilter(object, event); } #ifdef ENABLE_WALLET bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient &recipient) { // URI has to be valid if (walletFrame && walletFrame->handlePaymentRequest(recipient)) { showNormalIfMinimized(); gotoSendCoinsPage(); return true; } return false; } void BitcoinGUI::setHDStatus(int hdEnabled) { labelWalletHDStatusIcon->setPixmap( platformStyle ->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled") .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelWalletHDStatusIcon->setToolTip( hdEnabled ? tr("HD key generation is enabled") : tr("HD key generation is disabled")); // eventually disable the QLabel to set its opacity to 50% labelWalletHDStatusIcon->setEnabled(hdEnabled); } void BitcoinGUI::setEncryptionStatus(int status) { switch (status) { case WalletModel::Unencrypted: labelWalletEncryptionIcon->hide(); encryptWalletAction->setChecked(false); changePassphraseAction->setEnabled(false); encryptWalletAction->setEnabled(true); break; case WalletModel::Unlocked: labelWalletEncryptionIcon->show(); labelWalletEncryptionIcon->setPixmap( platformStyle->SingleColorIcon(":/icons/lock_open") .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelWalletEncryptionIcon->setToolTip( tr("Wallet is encrypted and currently unlocked")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); encryptWalletAction->setEnabled( false); // TODO: decrypt currently not supported break; case WalletModel::Locked: labelWalletEncryptionIcon->show(); labelWalletEncryptionIcon->setPixmap( platformStyle->SingleColorIcon(":/icons/lock_closed") .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelWalletEncryptionIcon->setToolTip( tr("Wallet is encrypted and currently locked")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); encryptWalletAction->setEnabled( false); // TODO: decrypt currently not supported break; } } void BitcoinGUI::updateWalletStatus() { if (!walletFrame) { return; } WalletView *const walletView = walletFrame->currentWalletView(); if (!walletView) { return; } WalletModel *const walletModel = walletView->getWalletModel(); setEncryptionStatus(walletModel->getEncryptionStatus()); setHDStatus(walletModel->wallet().hdEnabled()); } #endif // ENABLE_WALLET void BitcoinGUI::updateProxyIcon() { std::string ip_port; bool proxy_enabled = clientModel->getProxyInfo(ip_port); if (proxy_enabled) { if (labelProxyIcon->pixmap() == 0) { QString ip_port_q = QString::fromStdString(ip_port); labelProxyIcon->setPixmap( platformStyle->SingleColorIcon(":/icons/proxy") .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelProxyIcon->setToolTip( tr("Proxy is enabled: %1").arg(ip_port_q)); } else { labelProxyIcon->show(); } } else { labelProxyIcon->hide(); } } void BitcoinGUI::updateWindowTitle() { QString window_title = tr(PACKAGE_NAME) + " - "; #ifdef ENABLE_WALLET if (walletFrame) { WalletModel *const wallet_model = walletFrame->currentWalletModel(); if (wallet_model && !wallet_model->getWalletName().isEmpty()) { window_title += wallet_model->getDisplayName() + " - "; } } #endif window_title += m_network_style->getTitleAddText(); setWindowTitle(window_title); } void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) { if (!clientModel) { return; } if (!isHidden() && !isMinimized() && !GUIUtil::isObscured(this) && fToggleHidden) { hide(); } else { GUIUtil::bringToFront(this); } } void BitcoinGUI::toggleHidden() { showNormalIfMinimized(true); } void BitcoinGUI::detectShutdown() { if (m_node.shutdownRequested()) { if (rpcConsole) { rpcConsole->hide(); } qApp->quit(); } } void BitcoinGUI::showProgress(const QString &title, int nProgress) { if (nProgress == 0) { progressDialog = new QProgressDialog(title, "", 0, 100); progressDialog->setWindowModality(Qt::ApplicationModal); progressDialog->setMinimumDuration(0); progressDialog->setCancelButton(0); progressDialog->setAutoClose(false); progressDialog->setValue(0); } else if (progressDialog) { if (nProgress == 100) { progressDialog->close(); progressDialog->deleteLater(); } else { progressDialog->setValue(nProgress); } } } void BitcoinGUI::setTrayIconVisible(bool fHideTrayIcon) { if (trayIcon) { trayIcon->setVisible(!fHideTrayIcon); } } void BitcoinGUI::showModalOverlay() { if (modalOverlay && (progressBar->isVisible() || modalOverlay->isLayerVisible())) { modalOverlay->toggleVisibility(); } } static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string &message, const std::string &caption, unsigned int style) { // Redundantly log and print message in non-gui fashion noui_ThreadSafeMessageBox(message, caption, style); bool modal = (style & CClientUIInterface::MODAL); // The SECURE flag has no effect in the Qt GUI. // bool secure = (style & CClientUIInterface::SECURE); style &= ~CClientUIInterface::SECURE; bool ret = false; // In case of modal message, use blocking connection to wait for user to // click a button QMetaObject::invokeMethod(gui, "message", modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(caption)), Q_ARG(QString, QString::fromStdString(message)), Q_ARG(unsigned int, style), Q_ARG(bool *, &ret)); return ret; } void BitcoinGUI::subscribeToCoreSignals() { // Connect signals to client m_handler_message_box = m_node.handleMessageBox( std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); m_handler_question = m_node.handleQuestion( std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_3, std::placeholders::_4)); } void BitcoinGUI::unsubscribeFromCoreSignals() { // Disconnect signals from client m_handler_message_box->disconnect(); m_handler_question->disconnect(); } UnitDisplayStatusBarControl::UnitDisplayStatusBarControl( const PlatformStyle *platformStyle) : optionsModel(0), menu(0) { createContextMenu(); setToolTip(tr("Unit to show amounts in. Click to select another unit.")); QList units = BitcoinUnits::availableUnits(); int max_width = 0; const QFontMetrics fm(font()); for (const BitcoinUnits::Unit unit : units) { max_width = qMax(max_width, fm.width(BitcoinUnits::name(unit))); } setMinimumSize(max_width, 0); setAlignment(Qt::AlignRight | Qt::AlignVCenter); setStyleSheet(QString("QLabel { color : %1 }") .arg(platformStyle->SingleColor().name())); } /** So that it responds to button clicks */ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) { onDisplayUnitsClicked(event->pos()); } /** Creates context menu, its actions, and wires up all the relevant signals for * mouse events. */ void UnitDisplayStatusBarControl::createContextMenu() { menu = new QMenu(this); for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) { QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this); menuAction->setData(QVariant(u)); menu->addAction(menuAction); } connect(menu, &QMenu::triggered, this, &UnitDisplayStatusBarControl::onMenuSelection); } /** Lets the control know about the Options Model (and its signals) */ void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *_optionsModel) { if (_optionsModel) { this->optionsModel = _optionsModel; // be aware of a display unit change reported by the OptionsModel // object. connect(_optionsModel, &OptionsModel::displayUnitChanged, this, &UnitDisplayStatusBarControl::updateDisplayUnit); // initialize the display units label with the current value in the // model. updateDisplayUnit(_optionsModel->getDisplayUnit()); } } /** When Display Units are changed on OptionsModel it will refresh the display * text of the control on the status bar */ void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits) { setText(BitcoinUnits::name(newUnits)); } /** Shows context menu with Display Unit options by the mouse coordinates */ void UnitDisplayStatusBarControl::onDisplayUnitsClicked(const QPoint &point) { QPoint globalPos = mapToGlobal(point); menu->exec(globalPos); } /** Tells underlying optionsModel to update its current display unit. */ void UnitDisplayStatusBarControl::onMenuSelection(QAction *action) { if (action) { optionsModel->setDisplayUnit(action->data()); } } diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp index 95e7154d1d..fe616a2aef 100644 --- a/src/qt/csvmodelwriter.cpp +++ b/src/qt/csvmodelwriter.cpp @@ -1,76 +1,78 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include CSVModelWriter::CSVModelWriter(const QString &_filename, QObject *parent) : QObject(parent), filename(_filename), model(0) {} void CSVModelWriter::setModel(const QAbstractItemModel *_model) { this->model = _model; } void CSVModelWriter::addColumn(const QString &title, int column, int role) { Column col; col.title = title; col.column = column; col.role = role; columns.append(col); } static void writeValue(QTextStream &f, const QString &value) { QString escaped = value; escaped.replace('"', "\"\""); f << "\"" << escaped << "\""; } static void writeSep(QTextStream &f) { f << ","; } static void writeNewline(QTextStream &f) { f << "\n"; } bool CSVModelWriter::write() { QFile file(filename); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return false; + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + return false; + } QTextStream out(&file); int numRows = 0; if (model) { numRows = model->rowCount(); } // Header row for (int i = 0; i < columns.size(); ++i) { if (i != 0) { writeSep(out); } writeValue(out, columns[i].title); } writeNewline(out); // Data rows for (int j = 0; j < numRows; ++j) { for (int i = 0; i < columns.size(); ++i) { if (i != 0) { writeSep(out); } QVariant data = model->index(j, columns[i].column).data(columns[i].role); writeValue(out, data.toString()); } writeNewline(out); } file.close(); return file.error() == QFile::NoError; } diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index 2098395b9f..2cdee86752 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -1,173 +1,183 @@ // Copyright (c) 2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include ModalOverlay::ModalOverlay(QWidget *parent) : QWidget(parent), ui(new Ui::ModalOverlay), bestHeaderHeight(0), bestHeaderDate(QDateTime()), layerIsVisible(false), userClosed(false) { ui->setupUi(this); connect(ui->closeButton, &QPushButton::clicked, this, &ModalOverlay::closeClicked); if (parent) { parent->installEventFilter(this); raise(); } blockProcessTime.clear(); setVisible(false); } ModalOverlay::~ModalOverlay() { delete ui; } bool ModalOverlay::eventFilter(QObject *obj, QEvent *ev) { if (obj == parent()) { if (ev->type() == QEvent::Resize) { QResizeEvent *rev = static_cast(ev); resize(rev->size()); - if (!layerIsVisible) setGeometry(0, height(), width(), height()); + if (!layerIsVisible) { + setGeometry(0, height(), width(), height()); + } } else if (ev->type() == QEvent::ChildAdded) { raise(); } } return QWidget::eventFilter(obj, ev); } //! Tracks parent widget changes bool ModalOverlay::event(QEvent *ev) { if (ev->type() == QEvent::ParentAboutToChange) { - if (parent()) parent()->removeEventFilter(this); + if (parent()) { + parent()->removeEventFilter(this); + } } else if (ev->type() == QEvent::ParentChange) { if (parent()) { parent()->installEventFilter(this); raise(); } } return QWidget::event(ev); } void ModalOverlay::setKnownBestHeight(int count, const QDateTime &blockDate) { if (count > bestHeaderHeight) { bestHeaderHeight = count; bestHeaderDate = blockDate; } } void ModalOverlay::tipUpdate(int count, const QDateTime &blockDate, double nVerificationProgress) { QDateTime currentDate = QDateTime::currentDateTime(); // keep a vector of samples of verification progress at height blockProcessTime.push_front( qMakePair(currentDate.toMSecsSinceEpoch(), nVerificationProgress)); // show progress speed if we have more than one sample if (blockProcessTime.size() >= 2) { double progressDelta = 0; double progressPerHour = 0; qint64 timeDelta = 0; qint64 remainingMSecs = 0; double remainingProgress = 1.0 - nVerificationProgress; for (int i = 1; i < blockProcessTime.size(); i++) { QPair sample = blockProcessTime[i]; // take first sample after 500 seconds or last available one if (sample.first < (currentDate.toMSecsSinceEpoch() - 500 * 1000) || i == blockProcessTime.size() - 1) { progressDelta = blockProcessTime[0].second - sample.second; timeDelta = blockProcessTime[0].first - sample.first; progressPerHour = progressDelta / (double)timeDelta * 1000 * 3600; remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1; break; } } // show progress increase per hour ui->progressIncreasePerH->setText( QString::number(progressPerHour * 100, 'f', 2) + "%"); // show expected remaining time if (remainingMSecs >= 0) { ui->expectedTimeLeft->setText( GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0)); } else { ui->expectedTimeLeft->setText(QObject::tr("unknown")); } static const int MAX_SAMPLES = 5000; if (blockProcessTime.count() > MAX_SAMPLES) { blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count() - MAX_SAMPLES); } } // show the last block date ui->newestBlockDate->setText(blockDate.toString()); // show the percentage done according to nVerificationProgress ui->percentageProgress->setText( QString::number(nVerificationProgress * 100, 'f', 2) + "%"); ui->progressBar->setValue(nVerificationProgress * 100); - if (!bestHeaderDate.isValid()) + if (!bestHeaderDate.isValid()) { // not syncing return; + } // estimate the number of headers left based on nPowTargetSpacing // and check if the gui is not aware of the best header (happens rarely) int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / Params().GetConsensus().nPowTargetSpacing; bool hasBestHeader = bestHeaderHeight >= count; // show remaining number of blocks if (estimateNumHeadersLeft < HEADER_HEIGHT_DELTA_SYNC && hasBestHeader) { ui->numberOfBlocksLeft->setText( QString::number(bestHeaderHeight - count)); } else { ui->numberOfBlocksLeft->setText( tr("Unknown. Syncing Headers (%1)...").arg(bestHeaderHeight)); ui->expectedTimeLeft->setText(tr("Unknown...")); } } void ModalOverlay::toggleVisibility() { showHide(layerIsVisible, true); - if (!layerIsVisible) userClosed = true; + if (!layerIsVisible) { + userClosed = true; + } } void ModalOverlay::showHide(bool hide, bool userRequested) { if ((layerIsVisible && !hide) || (!layerIsVisible && hide) || - (!hide && userClosed && !userRequested)) + (!hide && userClosed && !userRequested)) { return; + } - if (!isVisible() && !hide) setVisible(true); + if (!isVisible() && !hide) { + setVisible(true); + } setGeometry(0, hide ? 0 : height(), width(), height()); QPropertyAnimation *animation = new QPropertyAnimation(this, "pos"); animation->setDuration(300); animation->setStartValue(QPoint(0, hide ? 0 : this->height())); animation->setEndValue(QPoint(0, hide ? this->height() : 0)); animation->setEasingCurve(QEasingCurve::OutQuad); animation->start(QAbstractAnimation::DeleteWhenStopped); layerIsVisible = !hide; } void ModalOverlay::closeClicked() { showHide(true); userClosed = true; } diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index e32417b9d3..62a77a5314 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -1,410 +1,418 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include // for -dbcache defaults #include // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS #include #include #include #include #include #include #include OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : QDialog(parent), ui(new Ui::OptionsDialog), model(0), mapper(0) { ui->setupUi(this); /* Main elements init */ ui->databaseCache->setMinimum(nMinDbCache); ui->databaseCache->setMaximum(nMaxDbCache); static const uint64_t GiB = 1024 * 1024 * 1024; static const uint64_t nMinDiskSpace = MIN_DISK_SPACE_FOR_BLOCK_FILES / GiB + (MIN_DISK_SPACE_FOR_BLOCK_FILES % GiB) ? 1 : 0; ui->pruneSize->setMinimum(nMinDiskSpace); ui->threadsScriptVerif->setMinimum(-GetNumCores()); ui->threadsScriptVerif->setMaximum(MAX_SCRIPTCHECK_THREADS); ui->pruneWarning->setVisible(false); ui->pruneWarning->setStyleSheet("QLabel { color: red; }"); ui->pruneSize->setEnabled(false); connect(ui->prune, &QPushButton::toggled, ui->pruneSize, &QWidget::setEnabled); /* Network elements init */ #ifndef USE_UPNP ui->mapPortUpnp->setEnabled(false); #endif ui->proxyIp->setEnabled(false); ui->proxyPort->setEnabled(false); ui->proxyPort->setValidator(new QIntValidator(1, 65535, this)); ui->proxyIpTor->setEnabled(false); ui->proxyPortTor->setEnabled(false); ui->proxyPortTor->setValidator(new QIntValidator(1, 65535, this)); connect(ui->connectSocks, &QPushButton::toggled, ui->proxyIp, &QWidget::setEnabled); connect(ui->connectSocks, &QPushButton::toggled, ui->proxyPort, &QWidget::setEnabled); connect(ui->connectSocks, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState); connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyIpTor, &QWidget::setEnabled); connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyPortTor, &QWidget::setEnabled); connect(ui->connectSocksTor, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState); /* Window elements init */ #ifdef Q_OS_MAC /* remove Window tab on Mac */ ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow)); #endif /* remove Wallet tab in case of -disablewallet */ if (!enableWallet) { ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWallet)); } /* Display elements init */ QDir translations(":translations"); ui->bitcoinAtStartup->setToolTip( ui->bitcoinAtStartup->toolTip().arg(tr(PACKAGE_NAME))); ui->bitcoinAtStartup->setText( ui->bitcoinAtStartup->text().arg(tr(PACKAGE_NAME))); ui->openBitcoinConfButton->setToolTip( ui->openBitcoinConfButton->toolTip().arg(tr(PACKAGE_NAME))); ui->lang->setToolTip(ui->lang->toolTip().arg(tr(PACKAGE_NAME))); ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant("")); for (const QString &langStr : translations.entryList()) { QLocale locale(langStr); /** check if the locale name consists of 2 parts (language_country) */ if (langStr.contains("_")) { /** display language strings as "native language - native country * (locale name)", e.g. "Deutsch - Deutschland (de)" */ ui->lang->addItem(locale.nativeLanguageName() + QString(" - ") + locale.nativeCountryName() + QString(" (") + langStr + QString(")"), QVariant(langStr)); } else { /** display language strings as "native language (locale name)", * e.g. "Deutsch (de)" */ ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr)); } } ui->thirdPartyTxUrls->setPlaceholderText("https://example.com/tx/%s"); ui->unit->setModel(new BitcoinUnits(this)); /* Widget-to-option mapper */ mapper = new QDataWidgetMapper(this); mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); mapper->setOrientation(Qt::Vertical); GUIUtil::ItemDelegate *delegate = new GUIUtil::ItemDelegate(mapper); connect(delegate, &GUIUtil::ItemDelegate::keyEscapePressed, this, &OptionsDialog::reject); mapper->setItemDelegate(delegate); /* setup/change UI elements when proxy IPs are invalid/valid */ ui->proxyIp->setCheckValidator(new ProxyAddressValidator(parent)); ui->proxyIpTor->setCheckValidator(new ProxyAddressValidator(parent)); connect(ui->proxyIp, &QValidatedLineEdit::validationDidChange, this, &OptionsDialog::updateProxyValidationState); connect(ui->proxyIpTor, &QValidatedLineEdit::validationDidChange, this, &OptionsDialog::updateProxyValidationState); connect(ui->proxyPort, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState); connect(ui->proxyPortTor, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState); if (!QSystemTrayIcon::isSystemTrayAvailable()) { ui->hideTrayIcon->setChecked(true); ui->hideTrayIcon->setEnabled(false); ui->minimizeToTray->setChecked(false); ui->minimizeToTray->setEnabled(false); } } OptionsDialog::~OptionsDialog() { delete ui; } void OptionsDialog::setModel(OptionsModel *_model) { this->model = _model; if (_model) { /* check if client restart is needed and show persistent message */ - if (_model->isRestartRequired()) showRestartWarning(true); + if (_model->isRestartRequired()) { + showRestartWarning(true); + } QString strLabel = _model->getOverriddenByCommandLine(); - if (strLabel.isEmpty()) strLabel = tr("none"); + if (strLabel.isEmpty()) { + strLabel = tr("none"); + } ui->overriddenByCommandLineLabel->setText(strLabel); mapper->setModel(_model); setMapper(); mapper->toFirst(); updateDefaultProxyNets(); } /* warn when one of the following settings changes by user action (placed * here so init via mapper doesn't trigger them) */ /* Main */ connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::togglePruneWarning); connect(ui->pruneSize, static_cast(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); connect(ui->databaseCache, static_cast(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); connect(ui->threadsScriptVerif, static_cast(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); /* Wallet */ connect(ui->spendZeroConfChange, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); /* Network */ connect(ui->allowIncoming, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); connect(ui->connectSocks, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); connect(ui->connectSocksTor, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); /* Display */ connect( ui->lang, static_cast(&QValueComboBox::valueChanged), [this] { showRestartWarning(); }); connect(ui->thirdPartyTxUrls, &QLineEdit::textChanged, [this] { showRestartWarning(); }); } void OptionsDialog::setCurrentTab(OptionsDialog::Tab tab) { QWidget *tab_widget = nullptr; if (tab == OptionsDialog::Tab::TAB_NETWORK) { tab_widget = ui->tabNetwork; } if (tab == OptionsDialog::Tab::TAB_MAIN) { tab_widget = ui->tabMain; } if (tab_widget && ui->tabWidget->currentWidget() != tab_widget) { ui->tabWidget->setCurrentWidget(tab_widget); } } void OptionsDialog::setMapper() { /* Main */ mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup); mapper->addMapping(ui->threadsScriptVerif, OptionsModel::ThreadsScriptVerif); mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache); mapper->addMapping(ui->prune, OptionsModel::Prune); mapper->addMapping(ui->pruneSize, OptionsModel::PruneSize); /* Wallet */ mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange); mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); mapper->addMapping(ui->allowIncoming, OptionsModel::Listen); mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse); mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP); mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort); mapper->addMapping(ui->connectSocksTor, OptionsModel::ProxyUseTor); mapper->addMapping(ui->proxyIpTor, OptionsModel::ProxyIPTor); mapper->addMapping(ui->proxyPortTor, OptionsModel::ProxyPortTor); /* Window */ #ifndef Q_OS_MAC if (QSystemTrayIcon::isSystemTrayAvailable()) { mapper->addMapping(ui->hideTrayIcon, OptionsModel::HideTrayIcon); mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray); } mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose); #endif /* Display */ mapper->addMapping(ui->lang, OptionsModel::Language); mapper->addMapping(ui->unit, OptionsModel::DisplayUnit); mapper->addMapping(ui->thirdPartyTxUrls, OptionsModel::ThirdPartyTxUrls); } void OptionsDialog::setOkButtonState(bool fState) { ui->okButton->setEnabled(fState); } void OptionsDialog::on_resetButton_clicked() { if (model) { // confirmation dialog QMessageBox::StandardButton btnRetVal = QMessageBox::question( this, tr("Confirm options reset"), tr("Client restart required to activate changes.") + "

" + tr("Client will be shut down. Do you want to proceed?"), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); - if (btnRetVal == QMessageBox::Cancel) return; + if (btnRetVal == QMessageBox::Cancel) { + return; + } /* reset all options and close GUI */ model->Reset(); QApplication::quit(); } } void OptionsDialog::on_openBitcoinConfButton_clicked() { /* explain the purpose of the config file */ QMessageBox::information( this, tr("Configuration options"), tr("The configuration file is used to specify advanced user options " "which override GUI settings. Additionally, any command-line " "options will override this configuration file.")); /* show an error if there was some problem opening the file */ if (!GUIUtil::openBitcoinConf()) { QMessageBox::critical( this, tr("Error"), tr("The configuration file could not be opened.")); } } void OptionsDialog::on_okButton_clicked() { mapper->submit(); accept(); updateDefaultProxyNets(); } void OptionsDialog::on_cancelButton_clicked() { reject(); } void OptionsDialog::on_hideTrayIcon_stateChanged(int fState) { if (fState) { ui->minimizeToTray->setChecked(false); ui->minimizeToTray->setEnabled(false); } else { ui->minimizeToTray->setEnabled(true); } } void OptionsDialog::togglePruneWarning(bool enabled) { ui->pruneWarning->setVisible(!ui->pruneWarning->isVisible()); } void OptionsDialog::showRestartWarning(bool fPersistent) { ui->statusLabel->setStyleSheet("QLabel { color: red; }"); if (fPersistent) { ui->statusLabel->setText( tr("Client restart required to activate changes.")); } else { ui->statusLabel->setText( tr("This change would require a client restart.")); // clear non-persistent status label after 10 seconds // TODO: should perhaps be a class attribute, if we extend the use of // statusLabel QTimer::singleShot(10000, this, &OptionsDialog::clearStatusLabel); } } void OptionsDialog::clearStatusLabel() { ui->statusLabel->clear(); if (model && model->isRestartRequired()) { showRestartWarning(true); } } void OptionsDialog::updateProxyValidationState() { QValidatedLineEdit *pUiProxyIp = ui->proxyIp; QValidatedLineEdit *otherProxyWidget = (pUiProxyIp == ui->proxyIpTor) ? ui->proxyIp : ui->proxyIpTor; if (pUiProxyIp->isValid() && (!ui->proxyPort->isEnabled() || ui->proxyPort->text().toInt() > 0) && (!ui->proxyPortTor->isEnabled() || ui->proxyPortTor->text().toInt() > 0)) { // Only enable ok button if both proxys are valid setOkButtonState(otherProxyWidget->isValid()); clearStatusLabel(); } else { setOkButtonState(false); ui->statusLabel->setStyleSheet("QLabel { color: red; }"); ui->statusLabel->setText(tr("The supplied proxy address is invalid.")); } } void OptionsDialog::updateDefaultProxyNets() { proxyType proxy; std::string strProxy; QString strDefaultProxyGUI; model->node().getProxy(NET_IPV4, proxy); strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort(); strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv4->setChecked(true) : ui->proxyReachIPv4->setChecked(false); model->node().getProxy(NET_IPV6, proxy); strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort(); strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv6->setChecked(true) : ui->proxyReachIPv6->setChecked(false); model->node().getProxy(NET_ONION, proxy); strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort(); strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false); } ProxyAddressValidator::ProxyAddressValidator(QObject *parent) : QValidator(parent) {} QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) const { Q_UNUSED(pos); // Validate the proxy CService serv( LookupNumeric(input.toStdString().c_str(), DEFAULT_GUI_PROXY_PORT)); proxyType addrProxy = proxyType(serv, true); - if (addrProxy.IsValid()) return QValidator::Acceptable; + if (addrProxy.IsValid()) { + return QValidator::Acceptable; + } return QValidator::Invalid; } diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 39a3eb3120..15fb530fad 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -1,575 +1,577 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include #include // for -dbcache defaults #include // For DEFAULT_SCRIPTCHECK_THREADS #include #include #include const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1"; static const QString GetDefaultProxyAddress(); OptionsModel::OptionsModel(interfaces::Node &node, QObject *parent, bool resetSettings) : QAbstractListModel(parent), m_node(node) { Init(resetSettings); } void OptionsModel::addOverriddenOption(const std::string &option) { strOverriddenByCommandLine += QString::fromStdString(option) + "=" + QString::fromStdString(gArgs.GetArg(option, "")) + " "; } // Writes all missing QSettings with their default values void OptionsModel::Init(bool resetSettings) { if (resetSettings) { Reset(); } checkAndMigrate(); QSettings settings; // Ensure restart flag is unset on client startup setRestartRequired(false); // These are Qt-only settings: // Window if (!settings.contains("fHideTrayIcon")) { settings.setValue("fHideTrayIcon", false); } fHideTrayIcon = settings.value("fHideTrayIcon").toBool(); Q_EMIT hideTrayIconChanged(fHideTrayIcon); if (!settings.contains("fMinimizeToTray")) { settings.setValue("fMinimizeToTray", false); } fMinimizeToTray = settings.value("fMinimizeToTray").toBool() && !fHideTrayIcon; if (!settings.contains("fMinimizeOnClose")) { settings.setValue("fMinimizeOnClose", false); } fMinimizeOnClose = settings.value("fMinimizeOnClose").toBool(); // Display if (!settings.contains("nDisplayUnit")) { settings.setValue("nDisplayUnit", BitcoinUnits::BCH); } nDisplayUnit = settings.value("nDisplayUnit").toInt(); if (!settings.contains("strThirdPartyTxUrls")) { settings.setValue("strThirdPartyTxUrls", ""); } strThirdPartyTxUrls = settings.value("strThirdPartyTxUrls", "").toString(); if (!settings.contains("fCoinControlFeatures")) { settings.setValue("fCoinControlFeatures", false); } fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool(); // These are shared with the core or have a command-line parameter // and we want command-line parameters to overwrite the GUI settings. // // If setting doesn't exist create it with defaults. // // If gArgs.SoftSetArg() or gArgs.SoftSetBoolArg() return false we were // overridden // by command-line and show this in the UI. // Main if (!settings.contains("bPrune")) { settings.setValue("bPrune", false); } if (!settings.contains("nPruneSize")) { settings.setValue("nPruneSize", 2); } // Convert prune size to MB: const uint64_t nPruneSizeMB = settings.value("nPruneSize").toInt() * 1000; if (!m_node.softSetArg("-prune", settings.value("bPrune").toBool() ? std::to_string(nPruneSizeMB) : "0")) { addOverriddenOption("-prune"); } if (!settings.contains("nDatabaseCache")) { settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache); } if (!m_node.softSetArg( "-dbcache", settings.value("nDatabaseCache").toString().toStdString())) { addOverriddenOption("-dbcache"); } if (!settings.contains("nThreadsScriptVerif")) { settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS); } if (!m_node.softSetArg( "-par", settings.value("nThreadsScriptVerif").toString().toStdString())) { addOverriddenOption("-par"); } if (!settings.contains("strDataDir")) { settings.setValue("strDataDir", Intro::getDefaultDataDirectory()); } // Wallet #ifdef ENABLE_WALLET if (!settings.contains("bSpendZeroConfChange")) { settings.setValue("bSpendZeroConfChange", true); } if (!m_node.softSetBoolArg( "-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool())) { addOverriddenOption("-spendzeroconfchange"); } #endif // Network if (!settings.contains("fUseUPnP")) { settings.setValue("fUseUPnP", DEFAULT_UPNP); } if (!m_node.softSetBoolArg("-upnp", settings.value("fUseUPnP").toBool())) { addOverriddenOption("-upnp"); } if (!settings.contains("fListen")) { settings.setValue("fListen", DEFAULT_LISTEN); } if (!m_node.softSetBoolArg("-listen", settings.value("fListen").toBool())) { addOverriddenOption("-listen"); } if (!settings.contains("fUseProxy")) { settings.setValue("fUseProxy", false); } if (!settings.contains("addrProxy")) { settings.setValue("addrProxy", GetDefaultProxyAddress()); } // Only try to set -proxy, if user has enabled fUseProxy if (settings.value("fUseProxy").toBool() && !m_node.softSetArg( "-proxy", settings.value("addrProxy").toString().toStdString())) { addOverriddenOption("-proxy"); } else if (!settings.value("fUseProxy").toBool() && !gArgs.GetArg("-proxy", "").empty()) { addOverriddenOption("-proxy"); } if (!settings.contains("fUseSeparateProxyTor")) { settings.setValue("fUseSeparateProxyTor", false); } if (!settings.contains("addrSeparateProxyTor")) { settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress()); } // Only try to set -onion, if user has enabled fUseSeparateProxyTor if (settings.value("fUseSeparateProxyTor").toBool() && !m_node.softSetArg( "-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) { addOverriddenOption("-onion"); } else if (!settings.value("fUseSeparateProxyTor").toBool() && !gArgs.GetArg("-onion", "").empty()) { addOverriddenOption("-onion"); } // Display if (!settings.contains("language")) { settings.setValue("language", ""); } if (!m_node.softSetArg( "-lang", settings.value("language").toString().toStdString())) { addOverriddenOption("-lang"); } language = settings.value("language").toString(); } /** * Helper function to copy contents from one QSettings to another. * By using allKeys this also covers nested settings in a hierarchy. */ static void CopySettings(QSettings &dst, const QSettings &src) { for (const QString &key : src.allKeys()) { dst.setValue(key, src.value(key)); } } /** Back up a QSettings to an ini-formatted file. */ static void BackupSettings(const fs::path &filename, const QSettings &src) { qWarning() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename); QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat); dst.clear(); CopySettings(dst, src); } void OptionsModel::Reset() { QSettings settings; // Backup old settings to chain-specific datadir for troubleshooting BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings); // Save the strDataDir setting QString dataDir = Intro::getDefaultDataDirectory(); dataDir = settings.value("strDataDir", dataDir).toString(); // Remove all entries from our QSettings object settings.clear(); // Set strDataDir settings.setValue("strDataDir", dataDir); // Set that this was reset settings.setValue("fReset", true); // default setting for OptionsModel::StartAtStartup - disabled if (GUIUtil::GetStartOnSystemStartup()) { GUIUtil::SetStartOnSystemStartup(false); } } int OptionsModel::rowCount(const QModelIndex &parent) const { return OptionIDRowCount; } struct ProxySetting { bool is_set; QString ip; QString port; }; static ProxySetting GetProxySetting(QSettings &settings, const QString &name) { static const ProxySetting default_val = { false, DEFAULT_GUI_PROXY_HOST, QString("%1").arg(DEFAULT_GUI_PROXY_PORT)}; // Handle the case that the setting is not set at all if (!settings.contains(name)) { return default_val; } // contains IP at index 0 and port at index 1 QStringList ip_port = settings.value(name).toString().split(":", QString::SkipEmptyParts); if (ip_port.size() == 2) { return {true, ip_port.at(0), ip_port.at(1)}; } else { // Invalid: return default return default_val; } } static void SetProxySetting(QSettings &settings, const QString &name, const ProxySetting &ip_port) { settings.setValue(name, ip_port.ip + ":" + ip_port.port); } static const QString GetDefaultProxyAddress() { return QString("%1:%2") .arg(DEFAULT_GUI_PROXY_HOST) .arg(DEFAULT_GUI_PROXY_PORT); } // read QSettings values and return them QVariant OptionsModel::data(const QModelIndex &index, int role) const { if (role == Qt::EditRole) { QSettings settings; switch (index.row()) { case StartAtStartup: return GUIUtil::GetStartOnSystemStartup(); case HideTrayIcon: return fHideTrayIcon; case MinimizeToTray: return fMinimizeToTray; case MapPortUPnP: #ifdef USE_UPNP return settings.value("fUseUPnP"); #else return false; #endif case MinimizeOnClose: return fMinimizeOnClose; // default proxy case ProxyUse: return settings.value("fUseProxy", false); case ProxyIP: return GetProxySetting(settings, "addrProxy").ip; case ProxyPort: return GetProxySetting(settings, "addrProxy").port; // separate Tor proxy case ProxyUseTor: return settings.value("fUseSeparateProxyTor", false); case ProxyIPTor: return GetProxySetting(settings, "addrSeparateProxyTor").ip; case ProxyPortTor: return GetProxySetting(settings, "addrSeparateProxyTor").port; #ifdef ENABLE_WALLET case SpendZeroConfChange: return settings.value("bSpendZeroConfChange"); #endif case DisplayUnit: return nDisplayUnit; case ThirdPartyTxUrls: return strThirdPartyTxUrls; case Language: return settings.value("language"); case CoinControlFeatures: return fCoinControlFeatures; case Prune: return settings.value("bPrune"); case PruneSize: return settings.value("nPruneSize"); case DatabaseCache: return settings.value("nDatabaseCache"); case ThreadsScriptVerif: return settings.value("nThreadsScriptVerif"); case Listen: return settings.value("fListen"); default: return QVariant(); } } return QVariant(); } // write QSettings values bool OptionsModel::setData(const QModelIndex &index, const QVariant &value, int role) { bool successful = true; /* set to false on parse error */ if (role == Qt::EditRole) { QSettings settings; switch (index.row()) { case StartAtStartup: successful = GUIUtil::SetStartOnSystemStartup(value.toBool()); break; case HideTrayIcon: fHideTrayIcon = value.toBool(); settings.setValue("fHideTrayIcon", fHideTrayIcon); Q_EMIT hideTrayIconChanged(fHideTrayIcon); break; case MinimizeToTray: fMinimizeToTray = value.toBool(); settings.setValue("fMinimizeToTray", fMinimizeToTray); break; case MapPortUPnP: // core option - can be changed on-the-fly settings.setValue("fUseUPnP", value.toBool()); m_node.mapPort(value.toBool()); break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); settings.setValue("fMinimizeOnClose", fMinimizeOnClose); break; // default proxy case ProxyUse: if (settings.value("fUseProxy") != value) { settings.setValue("fUseProxy", value.toBool()); setRestartRequired(true); } break; case ProxyIP: { auto ip_port = GetProxySetting(settings, "addrProxy"); if (!ip_port.is_set || ip_port.ip != value.toString()) { ip_port.ip = value.toString(); SetProxySetting(settings, "addrProxy", ip_port); setRestartRequired(true); } } break; case ProxyPort: { auto ip_port = GetProxySetting(settings, "addrProxy"); if (!ip_port.is_set || ip_port.port != value.toString()) { ip_port.port = value.toString(); SetProxySetting(settings, "addrProxy", ip_port); setRestartRequired(true); } } break; // separate Tor proxy case ProxyUseTor: if (settings.value("fUseSeparateProxyTor") != value) { settings.setValue("fUseSeparateProxyTor", value.toBool()); setRestartRequired(true); } break; case ProxyIPTor: { auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor"); if (!ip_port.is_set || ip_port.ip != value.toString()) { ip_port.ip = value.toString(); SetProxySetting(settings, "addrSeparateProxyTor", ip_port); setRestartRequired(true); } } break; case ProxyPortTor: { auto ip_port = GetProxySetting(settings, "addrSeparateProxyTor"); if (!ip_port.is_set || ip_port.port != value.toString()) { ip_port.port = value.toString(); SetProxySetting(settings, "addrSeparateProxyTor", ip_port); setRestartRequired(true); } } break; #ifdef ENABLE_WALLET case SpendZeroConfChange: if (settings.value("bSpendZeroConfChange") != value) { settings.setValue("bSpendZeroConfChange", value); setRestartRequired(true); } break; #endif case DisplayUnit: setDisplayUnit(value); break; case ThirdPartyTxUrls: if (strThirdPartyTxUrls != value.toString()) { strThirdPartyTxUrls = value.toString(); settings.setValue("strThirdPartyTxUrls", strThirdPartyTxUrls); setRestartRequired(true); } break; case Language: if (settings.value("language") != value) { settings.setValue("language", value); setRestartRequired(true); } break; case CoinControlFeatures: fCoinControlFeatures = value.toBool(); settings.setValue("fCoinControlFeatures", fCoinControlFeatures); Q_EMIT coinControlFeaturesChanged(fCoinControlFeatures); break; case Prune: if (settings.value("bPrune") != value) { settings.setValue("bPrune", value); setRestartRequired(true); } break; case PruneSize: if (settings.value("nPruneSize") != value) { settings.setValue("nPruneSize", value); setRestartRequired(true); } break; case DatabaseCache: if (settings.value("nDatabaseCache") != value) { settings.setValue("nDatabaseCache", value); setRestartRequired(true); } break; case ThreadsScriptVerif: if (settings.value("nThreadsScriptVerif") != value) { settings.setValue("nThreadsScriptVerif", value); setRestartRequired(true); } break; case Listen: if (settings.value("fListen") != value) { settings.setValue("fListen", value); setRestartRequired(true); } break; default: break; } } Q_EMIT dataChanged(index, index); return successful; } /** Updates current unit in memory, settings and emits * displayUnitChanged(newUnit) signal */ void OptionsModel::setDisplayUnit(const QVariant &value) { if (!value.isNull()) { QSettings settings; nDisplayUnit = value.toInt(); settings.setValue("nDisplayUnit", nDisplayUnit); Q_EMIT displayUnitChanged(nDisplayUnit); } } bool OptionsModel::getProxySettings(QNetworkProxy &proxy) const { // Directly query current base proxy, because // GUI settings can be overridden with -proxy. proxyType curProxy; if (m_node.getProxy(NET_IPV4, curProxy)) { proxy.setType(QNetworkProxy::Socks5Proxy); proxy.setHostName(QString::fromStdString(curProxy.proxy.ToStringIP())); proxy.setPort(curProxy.proxy.GetPort()); return true; - } else + } else { proxy.setType(QNetworkProxy::NoProxy); + } return false; } void OptionsModel::setRestartRequired(bool fRequired) { QSettings settings; return settings.setValue("fRestartRequired", fRequired); } bool OptionsModel::isRestartRequired() const { QSettings settings; return settings.value("fRestartRequired", false).toBool(); } void OptionsModel::checkAndMigrate() { // Migration of default values // Check if the QSettings container was already loaded with this client // version QSettings settings; static const char strSettingsVersionKey[] = "nSettingsVersion"; int settingsVersion = settings.contains(strSettingsVersionKey) ? settings.value(strSettingsVersionKey).toInt() : 0; if (settingsVersion < CLIENT_VERSION) { // -dbcache was bumped from 100 to 300 in 0.13 // see https://github.com/bitcoin/bitcoin/pull/8273 // force people to upgrade to the new value if they are using 100MB if (settingsVersion < 130000 && settings.contains("nDatabaseCache") && - settings.value("nDatabaseCache").toLongLong() == 100) + settings.value("nDatabaseCache").toLongLong() == 100) { settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache); + } settings.setValue(strSettingsVersionKey, CLIENT_VERSION); } // Overwrite the 'addrProxy' setting in case it has been set to an illegal // default value (see issue #12623; PR #12650). if (settings.contains("addrProxy") && settings.value("addrProxy").toString().endsWith("%2")) { settings.setValue("addrProxy", GetDefaultProxyAddress()); } // Overwrite the 'addrSeparateProxyTor' setting in case it has been set to // an illegal default value (see issue #12623; PR #12650). if (settings.contains("addrSeparateProxyTor") && settings.value("addrSeparateProxyTor").toString().endsWith("%2")) { settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress()); } } diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index f40596f912..704783c1e0 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -1,291 +1,293 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #define DECORATION_SIZE 54 #define NUM_ITEMS 5 Q_DECLARE_METATYPE(interfaces::WalletBalances) class TxViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: explicit TxViewDelegate(const PlatformStyle *_platformStyle, QObject *parent = nullptr) : QAbstractItemDelegate(parent), unit(BitcoinUnits::BCH), platformStyle(_platformStyle) {} inline void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { painter->save(); QIcon icon = qvariant_cast( index.data(TransactionTableModel::RawDecorationRole)); QRect mainRect = option.rect; QRect decorationRect(mainRect.topLeft(), QSize(DECORATION_SIZE, DECORATION_SIZE)); int xspace = DECORATION_SIZE + 8; int ypad = 6; int halfheight = (mainRect.height() - 2 * ypad) / 2; QRect amountRect(mainRect.left() + xspace, mainRect.top() + ypad, mainRect.width() - xspace, halfheight); QRect addressRect(mainRect.left() + xspace, mainRect.top() + ypad + halfheight, mainRect.width() - xspace, halfheight); icon = platformStyle->SingleColorIcon(icon); icon.paint(painter, decorationRect); QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime(); QString address = index.data(Qt::DisplayRole).toString(); Amount amount( int64_t( index.data(TransactionTableModel::AmountRole).toLongLong()) * SATOSHI); bool confirmed = index.data(TransactionTableModel::ConfirmedRole).toBool(); QVariant value = index.data(Qt::ForegroundRole); QColor foreground = option.palette.color(QPalette::Text); if (value.canConvert()) { QBrush brush = qvariant_cast(value); foreground = brush.color(); } painter->setPen(foreground); QRect boundingRect; painter->drawText(addressRect, Qt::AlignLeft | Qt::AlignVCenter, address, &boundingRect); if (index.data(TransactionTableModel::WatchonlyRole).toBool()) { QIcon iconWatchonly = qvariant_cast( index.data(TransactionTableModel::WatchonlyDecorationRole)); QRect watchonlyRect(boundingRect.right() + 5, mainRect.top() + ypad + halfheight, 16, halfheight); iconWatchonly.paint(painter, watchonlyRect); } if (amount < Amount::zero()) { foreground = COLOR_NEGATIVE; } else if (!confirmed) { foreground = COLOR_UNCONFIRMED; } else { foreground = option.palette.color(QPalette::Text); } painter->setPen(foreground); QString amountText = BitcoinUnits::formatWithUnit( unit, amount, true, BitcoinUnits::separatorAlways); if (!confirmed) { amountText = QString("[") + amountText + QString("]"); } painter->drawText(amountRect, Qt::AlignRight | Qt::AlignVCenter, amountText); painter->setPen(option.palette.color(QPalette::Text)); painter->drawText(amountRect, Qt::AlignLeft | Qt::AlignVCenter, GUIUtil::dateTimeStr(date)); painter->restore(); } inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { return QSize(DECORATION_SIZE, DECORATION_SIZE); } int unit; const PlatformStyle *platformStyle; }; #include OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) : QWidget(parent), ui(new Ui::OverviewPage), clientModel(0), walletModel(0), txdelegate(new TxViewDelegate(platformStyle, this)) { ui->setupUi(this); m_balances.balance = -SATOSHI; // use a SingleColorIcon for the "out of sync warning" icon QIcon icon = platformStyle->SingleColorIcon(":/icons/warning"); // also set the disabled icon because we are using a disabled QPushButton to // work around missing HiDPI support of QLabel // (https://bugreports.qt.io/browse/QTBUG-42503) icon.addPixmap(icon.pixmap(QSize(64, 64), QIcon::Normal), QIcon::Disabled); ui->labelTransactionsStatus->setIcon(icon); ui->labelWalletStatus->setIcon(icon); // Recent transactions ui->listTransactions->setItemDelegate(txdelegate); ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE)); ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2)); ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); connect(ui->listTransactions, &QListView::clicked, this, &OverviewPage::handleTransactionClicked); // start with displaying the "out of sync" warnings showOutOfSyncWarning(true); connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks); connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks); } void OverviewPage::handleTransactionClicked(const QModelIndex &index) { - if (filter) Q_EMIT transactionClicked(filter->mapToSource(index)); + if (filter) { + Q_EMIT transactionClicked(filter->mapToSource(index)); + } } void OverviewPage::handleOutOfSyncWarningClicks() { Q_EMIT outOfSyncWarningClicked(); } OverviewPage::~OverviewPage() { delete ui; } void OverviewPage::setBalance(const interfaces::WalletBalances &balances) { int unit = walletModel->getOptionsModel()->getDisplayUnit(); m_balances = balances; ui->labelBalance->setText(BitcoinUnits::formatWithUnit( unit, balances.balance, false, BitcoinUnits::separatorAlways)); ui->labelUnconfirmed->setText( BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways)); ui->labelImmature->setText(BitcoinUnits::formatWithUnit( unit, balances.immature_balance, false, BitcoinUnits::separatorAlways)); ui->labelTotal->setText(BitcoinUnits::formatWithUnit( unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways)); ui->labelWatchAvailable->setText( BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways)); ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit( unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways)); ui->labelWatchImmature->setText( BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways)); ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit( unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways)); // only show immature (newly mined) balance if it's non-zero, so as not to // complicate things for the non-mining users bool showImmature = balances.immature_balance != Amount::zero(); bool showWatchOnlyImmature = balances.immature_watch_only_balance != Amount::zero(); // for symmetry reasons also show immature label when the watch-only one is // shown ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature); ui->labelWatchImmature->setVisible( showWatchOnlyImmature); // show watch-only immature balance } // show/hide watch-only labels void OverviewPage::updateWatchOnlyLabels(bool showWatchOnly) { // show spendable label (only when watch-only is active) ui->labelSpendable->setVisible(showWatchOnly); // show watch-only label ui->labelWatchonly->setVisible(showWatchOnly); // show watch-only balance separator line ui->lineWatchBalance->setVisible(showWatchOnly); // show watch-only available balance ui->labelWatchAvailable->setVisible(showWatchOnly); // show watch-only pending balance ui->labelWatchPending->setVisible(showWatchOnly); // show watch-only total balance ui->labelWatchTotal->setVisible(showWatchOnly); if (!showWatchOnly) { ui->labelWatchImmature->hide(); } } void OverviewPage::setClientModel(ClientModel *model) { this->clientModel = model; if (model) { // Show warning if this is a prerelease version connect(model, &ClientModel::alertsChanged, this, &OverviewPage::updateAlerts); updateAlerts(model->getStatusBarWarnings()); } } void OverviewPage::setWalletModel(WalletModel *model) { this->walletModel = model; if (model && model->getOptionsModel()) { // Set up transaction list filter.reset(new TransactionFilterProxy()); filter->setSourceModel(model->getTransactionTableModel()); filter->setLimit(NUM_ITEMS); filter->setDynamicSortFilter(true); filter->setSortRole(Qt::EditRole); filter->setShowInactive(false); filter->sort(TransactionTableModel::Date, Qt::DescendingOrder); ui->listTransactions->setModel(filter.get()); ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); // Keep up to date with wallet interfaces::Wallet &wallet = model->wallet(); interfaces::WalletBalances balances = wallet.getBalances(); setBalance(balances); connect(model, &WalletModel::balanceChanged, this, &OverviewPage::setBalance); connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit); updateWatchOnlyLabels(wallet.haveWatchOnly()); connect(model, &WalletModel::notifyWatchonlyChanged, this, &OverviewPage::updateWatchOnlyLabels); } // update the display unit, to not use the default ("BCH") updateDisplayUnit(); } void OverviewPage::updateDisplayUnit() { if (walletModel && walletModel->getOptionsModel()) { if (m_balances.balance != -SATOSHI) { setBalance(m_balances); } // Update txdelegate->unit with the current unit txdelegate->unit = walletModel->getOptionsModel()->getDisplayUnit(); ui->listTransactions->update(); } } void OverviewPage::updateAlerts(const QString &warnings) { this->ui->labelAlerts->setVisible(!warnings.isEmpty()); this->ui->labelAlerts->setText(warnings); } void OverviewPage::showOutOfSyncWarning(bool fShow) { ui->labelWalletStatus->setVisible(fShow); ui->labelTransactionsStatus->setVisible(fShow); } diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index fc3e4e64d5..f19a391f70 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -1,236 +1,242 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // // Wraps dumb protocol buffer paymentRequest with some extra methods // #include #include