Merge branch 'qtnetworkport-25+knots' into rwconf_gui-25+knots

This commit is contained in:
Luke Dashjr 2023-08-10 18:55:47 +00:00
commit b7daee6daf
18 changed files with 507 additions and 155 deletions

View File

@ -260,6 +260,9 @@ public:
//! Send init error.
virtual void initError(const bilingual_str& message) = 0;
//! Ask init question.
virtual bool initQuestion(const bilingual_str& message, const bilingual_str& non_interactive_message, const bilingual_str& caption, unsigned int style) = 0;
//! Send progress indicator.
virtual void showProgress(const std::string& title, int progress, bool resume_possible) = 0;

View File

@ -2314,6 +2314,27 @@ bool CConnman::InitBinds(const Options& options)
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::None);
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::None);
if (!fBound) {
int defaultPort = Params().GetDefaultPort();
// If listening failed and another port than the standard port was specified,
// ask if the user wants to connect via the standard port for the network instead
if (GetListenPort() != defaultPort) {
bool fRet = uiInterface.ThreadSafeQuestion(
strprintf(_("Do you want to use the standard network port for %s (port %s) instead?"), PACKAGE_NAME, defaultPort),
strprintf(_("Listen on port %s failed.").translated, GetListenPort()),
"", CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL | CClientUIInterface::BTN_OK | CClientUIInterface::BTN_ABORT);
if (fRet) {
gArgs.ForceSetArg("-port", defaultPort);
// Attempt to use standard port
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
fBound |= Bind(CService(inaddr6_any, defaultPort), BF_NONE, NetPermissionFlags::None);
fBound |= Bind(CService(inaddr_any, defaultPort), BF_NONE, NetPermissionFlags::None);
}
}
}
}
return fBound;
}

View File

@ -721,6 +721,9 @@ public:
void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); }
void initWarning(const bilingual_str& message) override { InitWarning(message); }
void initError(const bilingual_str& message) override { InitError(message); }
bool initQuestion(const bilingual_str& message, const bilingual_str& non_interactive_message, const bilingual_str& caption, unsigned int style) override {
return uiInterface.ThreadSafeQuestion(message, non_interactive_message.translated, caption.translated, style);
}
void showProgress(const std::string& title, int progress, bool resume_possible) override
{
::uiInterface.ShowProgress(title, progress, resume_possible);

View File

@ -315,6 +315,55 @@
<string>&amp;Network</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_Network">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_Network_Port">
<item>
<widget class="QLabel" name="networkPortLabel">
<property name="text">
<string>Network &amp;port</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="buddy">
<cstring>networkPort</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="networkPort">
<property name="minimumSize">
<size>
<width>55</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Network port (e.g. 8333)</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_Network_Port">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="mapPortUpnp">
<property name="toolTip">
@ -772,103 +821,63 @@
</layout>
</item>
<item>
<widget class="QGroupBox" name="font_groupBox">
<property name="title">
<string>Monospaced font in the Overview tab:</string>
<layout class="QHBoxLayout" name="horizontalLayout_4_Display">
<item>
<widget class="QLabel" name="moneyFontLabel">
<property name="text">
<string>Font in the Overview tab: </string>
</property>
<property name="buddy">
<cstring>moneyFont</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="moneyFont"/>
</item>
<item>
<widget class="QLabel" name="moneyFont_preview">
<property name="text">
<string notr="true">111.11111111 BTC
909.09090909 BTC</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_qrFont">
<item>
<widget class="QLabel" name="qrFontLabel">
<property name="text">
<string>Font in QR Codes: </string>
</property>
<property name="buddy">
<cstring>qrFont</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="qrFont"/>
</item>
<item>
<widget class="QLabel" name="qrFont_preview">
<property name="text">
<string notr="true">1NS17iag9jJgT…
bc1p2q3rvn3gp…</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="peersTabAlternatingRowColors">
<property name="toolTip">
<string>Alternate the row colors for the &quot;Peers&quot; and &quot;Banned peers&quot; tables in the Peers tab.</string>
</property>
<property name="text">
<string>Alternate row colors in the Peers tab</string>
</property>
<layout class="QVBoxLayout" name="font_verticalLayout">
<item>
<layout class="QHBoxLayout" name="embeddedFont_horizontalLayout">
<item>
<widget class="QRadioButton" name="embeddedFont_radioButton">
<property name="text">
<string>embedded &quot;%1&quot;</string>
</property>
</widget>
</item>
<item>
<spacer name="embeddedFont_horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="embeddedFont_verticalLayout">
<item>
<widget class="QLabel" name="embeddedFont_label_1">
<property name="text">
<string notr="true">111.11111111 BTC</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="embeddedFont_label_9">
<property name="text">
<string notr="true">909.09090909 BTC</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="Line" name="font_line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="systemFont_horizontalLayout">
<item>
<widget class="QRadioButton" name="systemFont_radioButton">
<property name="text">
<string>closest matching &quot;%1&quot;</string>
</property>
</widget>
</item>
<item>
<spacer name="systemFont_horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="systemFont_verticalLayout">
<item>
<widget class="QLabel" name="systemFont_label_1">
<property name="text">
<string notr="true">111.11111111 BTC</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="systemFont_label_9">
<property name="text">
<string notr="true">909.09090909 BTC</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>

View File

@ -23,14 +23,70 @@
#include <chrono>
#include <QApplication>
#include <QDataWidgetMapper>
#include <QDir>
#include <QFontDialog>
#include <QIntValidator>
#include <QLocale>
#include <QMessageBox>
#include <QSystemTrayIcon>
#include <QTimer>
int setFontChoice(QComboBox* cb, const OptionsModel::FontChoice& fc)
{
int i;
for (i = cb->count(); --i >= 0; ) {
QVariant item_data = cb->itemData(i);
if (!item_data.canConvert<OptionsModel::FontChoice>()) continue;
if (item_data.value<OptionsModel::FontChoice>() == fc) {
break;
}
}
if (i == -1) {
// New item needed
QFont chosen_font = OptionsModel::getFontForChoice(fc);
QSignalBlocker block_currentindexchanged_signal(cb); // avoid triggering QFontDialog
cb->insertItem(0, QFontInfo(chosen_font).family(), QVariant::fromValue(fc));
i = 0;
}
cb->setCurrentIndex(i);
return i;
}
void setupFontOptions(QComboBox* cb, QLabel* preview)
{
QFont embedded_font{GUIUtil::fixedPitchFont(true)};
QFont system_font{GUIUtil::fixedPitchFont(false)};
cb->addItem(QObject::tr("Embedded \"%1\"").arg(QFontInfo(embedded_font).family()), QVariant::fromValue(OptionsModel::FontChoice{OptionsModel::FontChoiceAbstract::EmbeddedFont}));
cb->addItem(QObject::tr("Default system font \"%1\"").arg(QFontInfo(system_font).family()), QVariant::fromValue(OptionsModel::FontChoice{OptionsModel::FontChoiceAbstract::BestSystemFont}));
cb->addItem(QObject::tr("Custom…"));
const auto& on_font_choice_changed = [cb, preview](int index) {
static int previous_index = -1;
QVariant item_data = cb->itemData(index);
QFont f;
if (item_data.canConvert<OptionsModel::FontChoice>()) {
f = OptionsModel::getFontForChoice(item_data.value<OptionsModel::FontChoice>());
} else {
bool ok;
f = QFontDialog::getFont(&ok, GUIUtil::fixedPitchFont(false), cb->parentWidget());
if (!ok) {
cb->setCurrentIndex(previous_index);
return;
}
index = setFontChoice(cb, OptionsModel::FontChoice{f});
}
if (preview) {
preview->setFont(f);
}
previous_index = index;
};
QObject::connect(cb, QOverload<int>::of(&QComboBox::currentIndexChanged), on_font_choice_changed);
on_font_choice_changed(cb->currentIndex());
}
OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
: QDialog(parent, GUIUtil::dialog_flags),
ui(new Ui::OptionsDialog)
@ -48,6 +104,9 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
ui->pruneSize->setEnabled(false);
connect(ui->prune, &QPushButton::toggled, ui->pruneSize, &QWidget::setEnabled);
ui->networkPort->setValidator(new QIntValidator(1024, 65535, this));
connect(ui->networkPort, SIGNAL(textChanged(const QString&)), this, SLOT(checkLineEdit()));
/* Network elements init */
#ifndef USE_UPNP
ui->mapPortUpnp->setEnabled(false);
@ -148,19 +207,13 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
ui->minimizeToTray->setEnabled(false);
}
QFont embedded_font{GUIUtil::fixedPitchFont(true)};
ui->embeddedFont_radioButton->setText(ui->embeddedFont_radioButton->text().arg(QFontInfo(embedded_font).family()));
embedded_font.setWeight(QFont::Bold);
ui->embeddedFont_label_1->setFont(embedded_font);
ui->embeddedFont_label_9->setFont(embedded_font);
QFont system_font{GUIUtil::fixedPitchFont(false)};
ui->systemFont_radioButton->setText(ui->systemFont_radioButton->text().arg(QFontInfo(system_font).family()));
system_font.setWeight(QFont::Bold);
ui->systemFont_label_1->setFont(system_font);
ui->systemFont_label_9->setFont(system_font);
// Checking the embeddedFont_radioButton automatically unchecks the systemFont_radioButton.
ui->systemFont_radioButton->setChecked(true);
setupFontOptions(ui->moneyFont, ui->moneyFont_preview);
setupFontOptions(ui->qrFont, ui->qrFont_preview);
#ifndef USE_QRCODE
ui->qrFontLabel->setVisible(false);
ui->qrFont->setVisible(false);
ui->qrFont_preview->setVisible(false);
#endif
GUIUtil::handleCloseWindowShortcut(this);
}
@ -198,6 +251,12 @@ void OptionsDialog::setModel(OptionsModel *_model)
setMapper();
mapper->toFirst();
const auto& font_for_money = _model->data(_model->index(OptionsModel::FontForMoney, 0), Qt::EditRole).value<OptionsModel::FontChoice>();
setFontChoice(ui->moneyFont, font_for_money);
const auto& font_for_qrcodes = _model->data(_model->index(OptionsModel::FontForQRCodes, 0), Qt::EditRole).value<OptionsModel::FontChoice>();
setFontChoice(ui->qrFont, font_for_qrcodes);
updateDefaultProxyNets();
}
@ -213,6 +272,7 @@ void OptionsDialog::setModel(OptionsModel *_model)
/* Wallet */
connect(ui->spendZeroConfChange, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
/* Network */
connect(ui->networkPort, SIGNAL(textChanged(const QString &)), this, SLOT(showRestartWarning()));
connect(ui->allowIncoming, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
connect(ui->enableServer, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
connect(ui->connectSocks, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
@ -249,6 +309,7 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->m_enable_psbt_controls, OptionsModel::EnablePSBTControls);
/* Network */
mapper->addMapping(ui->networkPort, OptionsModel::NetworkPort);
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
mapper->addMapping(ui->mapPortNatpmp, OptionsModel::MapPortNatpmp);
mapper->addMapping(ui->allowIncoming, OptionsModel::Listen);
@ -272,10 +333,20 @@ void OptionsDialog::setMapper()
#endif
/* Display */
mapper->addMapping(ui->peersTabAlternatingRowColors, OptionsModel::PeersTabAlternatingRowColors);
mapper->addMapping(ui->lang, OptionsModel::Language);
mapper->addMapping(ui->unit, OptionsModel::DisplayUnit);
mapper->addMapping(ui->thirdPartyTxUrls, OptionsModel::ThirdPartyTxUrls);
mapper->addMapping(ui->embeddedFont_radioButton, OptionsModel::UseEmbeddedMonospacedFont);
}
void OptionsDialog::checkLineEdit()
{
QLineEdit * const lineedit = qobject_cast<QLineEdit*>(QObject::sender());
if (lineedit->hasAcceptableInput()) {
lineedit->setStyleSheet("");
} else {
lineedit->setStyleSheet("color: red;");
}
}
void OptionsDialog::setOkButtonState(bool fState)
@ -337,6 +408,28 @@ void OptionsDialog::on_openBitcoinConfButton_clicked()
void OptionsDialog::on_okButton_clicked()
{
for (int i = 0; i < ui->tabWidget->count(); ++i) {
QWidget * const tab = ui->tabWidget->widget(i);
Q_FOREACH(QObject* o, tab->children()) {
QLineEdit * const lineedit = qobject_cast<QLineEdit*>(o);
if (lineedit && !lineedit->hasAcceptableInput()) {
int row = mapper->mappedSection(lineedit);
if (model->data(model->index(row, 0), Qt::EditRole) == lineedit->text()) {
// Allow unchanged fields through
continue;
}
ui->tabWidget->setCurrentWidget(tab);
lineedit->setFocus(Qt::OtherFocusReason);
lineedit->selectAll();
QMessageBox::critical(this, tr("Invalid setting"), tr("The value entered is invalid."));
return;
}
}
}
model->setData(model->index(OptionsModel::FontForMoney, 0), ui->moneyFont->itemData(ui->moneyFont->currentIndex()));
model->setData(model->index(OptionsModel::FontForQRCodes, 0), ui->qrFont->itemData(ui->qrFont->currentIndex()));
mapper->submit();
accept();
updateDefaultProxyNets();

View File

@ -67,6 +67,7 @@ private Q_SLOTS:
void updateProxyValidationState();
/* query the networks, for which the default proxy is used */
void updateDefaultProxyNets();
void checkLineEdit();
Q_SIGNALS:
void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, uint16_t nProxyPort);

View File

@ -12,6 +12,7 @@
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <chainparams.h>
#include <interfaces/node.h>
#include <mapport.h>
#include <net.h>
@ -118,6 +119,37 @@ struct ProxySetting {
static ProxySetting ParseProxyString(const std::string& proxy);
static std::string ProxyString(bool is_set, QString ip, QString port);
static const QLatin1String fontchoice_str_embedded{"embedded"};
static const QLatin1String fontchoice_str_best_system{"best_system"};
static const QString fontchoice_str_custom_prefix{QStringLiteral("custom, ")};
QString OptionsModel::FontChoiceToString(const OptionsModel::FontChoice& f)
{
if (std::holds_alternative<FontChoiceAbstract>(f)) {
if (f == UseBestSystemFont) {
return fontchoice_str_best_system;
} else {
return fontchoice_str_embedded;
}
}
return fontchoice_str_custom_prefix + std::get<QFont>(f).toString();
}
OptionsModel::FontChoice OptionsModel::FontChoiceFromString(const QString& s)
{
if (s == fontchoice_str_best_system) {
return FontChoiceAbstract::BestSystemFont;
} else if (s == fontchoice_str_embedded) {
return FontChoiceAbstract::EmbeddedFont;
} else if (s.startsWith(fontchoice_str_custom_prefix)) {
QFont f;
f.fromString(s.mid(fontchoice_str_custom_prefix.size()));
return f;
} else {
return FontChoiceAbstract::EmbeddedFont; // default
}
}
OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent) :
QAbstractListModel(parent), m_node{node}
{
@ -214,12 +246,34 @@ bool OptionsModel::Init(bilingual_str& error)
m_sub_fee_from_amount = settings.value("SubFeeFromAmount", false).toBool();
#endif
// Network
if (!settings.contains("nNetworkPort"))
settings.setValue("nNetworkPort", (quint16)Params().GetDefaultPort());
if (!gArgs.SoftSetArg("-port", settings.value("nNetworkPort").toString().toStdString()))
addOverriddenOption("-port");
// Display
if (!settings.contains("UseEmbeddedMonospacedFont")) {
settings.setValue("UseEmbeddedMonospacedFont", "true");
if (settings.contains("FontForMoney")) {
m_font_money = FontChoiceFromString(settings.value("FontForMoney").toString());
} else if (settings.contains("UseEmbeddedMonospacedFont")) {
if (settings.value("UseEmbeddedMonospacedFont").toBool()) {
m_font_money = FontChoiceAbstract::EmbeddedFont;
} else {
m_font_money = FontChoiceAbstract::BestSystemFont;
}
}
m_use_embedded_monospaced_font = settings.value("UseEmbeddedMonospacedFont").toBool();
Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
Q_EMIT fontForMoneyChanged(getFontForMoney());
if (settings.contains("FontForQRCodes")) {
m_font_qrcodes = FontChoiceFromString(settings.value("FontForQRCodes").toString());
}
Q_EMIT fontForQRCodesChanged(getFontChoiceForQRCodes());
if (!settings.contains("PeersTabAlternatingRowColors")) {
settings.setValue("PeersTabAlternatingRowColors", "false");
}
m_peers_tab_alternating_row_colors = settings.value("PeersTabAlternatingRowColors").toBool();
Q_EMIT peersTabAlternatingRowColorsChanged(m_peers_tab_alternating_row_colors);
m_mask_values = settings.value("mask_values", false).toBool();
@ -371,6 +425,8 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con
return m_show_tray_icon;
case MinimizeToTray:
return fMinimizeToTray;
case NetworkPort:
return settings.value("nNetworkPort");
case MapPortUPnP:
#ifdef USE_UPNP
return SettingToBool(setting(), DEFAULT_UPNP);
@ -427,8 +483,12 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con
return strThirdPartyTxUrls;
case Language:
return QString::fromStdString(SettingToString(setting(), ""));
case UseEmbeddedMonospacedFont:
return m_use_embedded_monospaced_font;
case FontForMoney:
return QVariant::fromValue(m_font_money);
case FontForQRCodes:
return QVariant::fromValue(m_font_qrcodes);
case PeersTabAlternatingRowColors:
return m_peers_tab_alternating_row_colors;
case CoinControlFeatures:
return fCoinControlFeatures;
case EnablePSBTControls:
@ -454,6 +514,23 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con
}
}
QFont OptionsModel::getFontForChoice(const FontChoice& fc)
{
QFont f;
if (std::holds_alternative<FontChoiceAbstract>(fc)) {
f = GUIUtil::fixedPitchFont(fc != UseBestSystemFont);
f.setWeight(QFont::Bold);
} else {
f = std::get<QFont>(fc);
}
return f;
}
QFont OptionsModel::getFontForMoney() const
{
return getFontForChoice(m_font_money);
}
bool OptionsModel::setOption(OptionID option, const QVariant& value, const std::string& suffix)
{
auto changed = [&] { return value.isValid() && value != getOption(option, suffix); };
@ -475,6 +552,18 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std::
fMinimizeToTray = value.toBool();
settings.setValue("fMinimizeToTray", fMinimizeToTray);
break;
case NetworkPort:
if (settings.value("nNetworkPort") != value) {
// If the port input box is empty, set to default port
if (value.toString().isEmpty()) {
settings.setValue("nNetworkPort", (quint16)Params().GetDefaultPort());
}
else {
settings.setValue("nNetworkPort", (quint16)value.toInt());
}
setRestartRequired(true);
}
break;
case MapPortUPnP: // core option - can be changed on-the-fly
if (changed()) {
update(value.toBool());
@ -586,10 +675,28 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std::
setRestartRequired(true);
}
break;
case UseEmbeddedMonospacedFont:
m_use_embedded_monospaced_font = value.toBool();
settings.setValue("UseEmbeddedMonospacedFont", m_use_embedded_monospaced_font);
Q_EMIT useEmbeddedMonospacedFontChanged(m_use_embedded_monospaced_font);
case FontForMoney:
{
const auto& new_font = value.value<FontChoice>();
if (m_font_money == new_font) break;
settings.setValue("FontForMoney", FontChoiceToString(new_font));
m_font_money = new_font;
Q_EMIT fontForMoneyChanged(getFontForMoney());
break;
}
case FontForQRCodes:
{
const auto& new_font = value.value<FontChoice>();
if (m_font_qrcodes == new_font) break;
settings.setValue("FontForQRCodes", FontChoiceToString(new_font));
m_font_qrcodes = new_font;
Q_EMIT fontForQRCodesChanged(new_font);
break;
}
case PeersTabAlternatingRowColors:
m_peers_tab_alternating_row_colors = value.toBool();
settings.setValue("PeersTabAlternatingRowColors", m_peers_tab_alternating_row_colors);
Q_EMIT peersTabAlternatingRowColorsChanged(m_peers_tab_alternating_row_colors);
break;
case CoinControlFeatures:
fCoinControlFeatures = value.toBool();

View File

@ -10,8 +10,10 @@
#include <qt/guiconstants.h>
#include <QAbstractListModel>
#include <QFont>
#include <assert.h>
#include <variant>
struct bilingual_str;
namespace interfaces {
@ -48,6 +50,7 @@ public:
StartAtStartup, // bool
ShowTrayIcon, // bool
MinimizeToTray, // bool
NetworkPort, // int
MapPortUPnP, // bool
MapPortNatpmp, // bool
MinimizeOnClose, // bool
@ -60,7 +63,9 @@ public:
DisplayUnit, // BitcoinUnit
ThirdPartyTxUrls, // QString
Language, // QString
UseEmbeddedMonospacedFont, // bool
FontForMoney, // FontChoice
FontForQRCodes, // FontChoice
PeersTabAlternatingRowColors, // bool
CoinControlFeatures, // bool
SubFeeFromAmount, // bool
ThreadsScriptVerif, // int
@ -76,6 +81,14 @@ public:
OptionIDRowCount,
};
enum class FontChoiceAbstract {
EmbeddedFont,
BestSystemFont,
};
typedef std::variant<FontChoiceAbstract, QFont> FontChoice;
static inline const FontChoice UseBestSystemFont{FontChoiceAbstract::BestSystemFont};
static QFont getFontForChoice(const FontChoice& fc);
bool Init(bilingual_str& error);
void Reset();
@ -93,7 +106,9 @@ public:
bool getMinimizeOnClose() const { return fMinimizeOnClose; }
BitcoinUnit getDisplayUnit() const { return m_display_bitcoin_unit; }
QString getThirdPartyTxUrls() const { return strThirdPartyTxUrls; }
bool getUseEmbeddedMonospacedFont() const { return m_use_embedded_monospaced_font; }
QFont getFontForMoney() const;
FontChoice getFontChoiceForQRCodes() const { return m_font_qrcodes; }
bool getPeersTabAlternatingRowColors() const { return m_peers_tab_alternating_row_colors; }
bool getCoinControlFeatures() const { return fCoinControlFeatures; }
bool getSubFeeFromAmount() const { return m_sub_fee_from_amount; }
bool getEnablePSBTControls() const { return m_enable_psbt_controls; }
@ -120,7 +135,9 @@ private:
QString language;
BitcoinUnit m_display_bitcoin_unit;
QString strThirdPartyTxUrls;
bool m_use_embedded_monospaced_font;
FontChoice m_font_money{FontChoiceAbstract::EmbeddedFont};
FontChoice m_font_qrcodes{FontChoiceAbstract::EmbeddedFont};
bool m_peers_tab_alternating_row_colors;
bool fCoinControlFeatures;
bool m_sub_fee_from_amount;
bool m_enable_psbt_controls;
@ -129,6 +146,9 @@ private:
/* settings that were overridden by command-line */
QString strOverriddenByCommandLine;
static QString FontChoiceToString(const OptionsModel::FontChoice&);
static FontChoice FontChoiceFromString(const QString&);
// Add option to list of GUI options overridden through command line/config file
void addOverriddenOption(const std::string &option);
@ -139,7 +159,11 @@ Q_SIGNALS:
void displayUnitChanged(BitcoinUnit unit);
void coinControlFeaturesChanged(bool);
void showTrayIconChanged(bool);
void useEmbeddedMonospacedFontChanged(bool);
void fontForMoneyChanged(const QFont&);
void fontForQRCodesChanged(const FontChoice&);
void peersTabAlternatingRowColorsChanged(bool);
};
Q_DECLARE_METATYPE(OptionsModel::FontChoice)
#endif // BITCOIN_QT_OPTIONSMODEL_H

View File

@ -250,8 +250,8 @@ void OverviewPage::setClientModel(ClientModel *model)
connect(model, &ClientModel::alertsChanged, this, &OverviewPage::updateAlerts);
updateAlerts(model->getStatusBarWarnings());
connect(model->getOptionsModel(), &OptionsModel::useEmbeddedMonospacedFontChanged, this, &OverviewPage::setMonospacedFont);
setMonospacedFont(model->getOptionsModel()->getUseEmbeddedMonospacedFont());
connect(model->getOptionsModel(), &OptionsModel::fontForMoneyChanged, this, &OverviewPage::setMonospacedFont);
setMonospacedFont(clientModel->getOptionsModel()->getFontForMoney());
}
}
@ -340,10 +340,8 @@ void OverviewPage::showOutOfSyncWarning(bool fShow)
ui->labelTransactionsStatus->setVisible(fShow);
}
void OverviewPage::setMonospacedFont(bool use_embedded_font)
void OverviewPage::setMonospacedFont(const QFont& f)
{
QFont f = GUIUtil::fixedPitchFont(use_embedded_font);
f.setWeight(QFont::Bold);
ui->labelBalance->setFont(f);
ui->labelUnconfirmed->setFont(f);
ui->labelImmature->setFont(f);

View File

@ -65,7 +65,7 @@ private Q_SLOTS:
void handleTransactionClicked(const QModelIndex &index);
void updateAlerts(const QString &warnings);
void updateWatchOnlyLabels(bool showWatchOnly);
void setMonospacedFont(bool use_embedded_font);
void setMonospacedFont(const QFont&);
};
#endif // BITCOIN_QT_OVERVIEWPAGE_H

View File

@ -5,6 +5,7 @@
#include <qt/qrimagewidget.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <QApplication>
#include <QClipboard>
@ -31,7 +32,7 @@ QRImageWidget::QRImageWidget(QWidget* parent)
contextMenu->addAction(tr("&Copy Image"), this, &QRImageWidget::copyImage);
}
bool QRImageWidget::setQR(const QString& data, const QString& text)
bool QRImageWidget::setQR(const QString& data, const QString& text, const OptionsModel::FontChoice& fontchoice)
{
#ifdef USE_QRCODE
setText("");
@ -50,36 +51,71 @@ bool QRImageWidget::setQR(const QString& data, const QString& text)
return false;
}
QImage qrImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32);
qrImage.fill(0xffffff);
QImage qrImage = QImage(code->width, code->width, QImage::Format_RGB32);
unsigned char *p = code->data;
for (int y = 0; y < code->width; ++y) {
for (int x = 0; x < code->width; ++x) {
qrImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff));
qrImage.setPixel(x, y, ((*p & 1) ? 0x0 : 0xffffff));
++p;
}
}
QRcode_free(code);
const int qr_image_size = QR_IMAGE_SIZE + (text.isEmpty() ? 0 : 2 * QR_IMAGE_MARGIN);
QImage qrAddrImage(qr_image_size, qr_image_size, QImage::Format_RGB32);
int qr_image_width = QR_IMAGE_SIZE + (2 * QR_IMAGE_MARGIN);
int qr_image_height = qr_image_width;
int qr_image_x_margin = QR_IMAGE_MARGIN;
int text_lines;
QFont font;
if (text.isEmpty()) {
text_lines = 0;
} else {
const int max_text_width = qr_image_width - (2 * QR_IMAGE_TEXT_MARGIN);
// Determine font to use
if (std::holds_alternative<OptionsModel::FontChoiceAbstract>(fontchoice)) {
font = GUIUtil::fixedPitchFont(fontchoice != OptionsModel::UseBestSystemFont);
font.setWeight(QFont::Bold);
font.setStretch(QFont::SemiCondensed);
font.setLetterSpacing(QFont::AbsoluteSpacing, 1);
const qreal font_size = GUIUtil::calculateIdealFontSize(max_text_width, text, font);
font.setPointSizeF(font_size);
} else {
font = std::get<QFont>(fontchoice);
}
// Plan how many lines are needed
QFontMetrics fm(font);
const int text_width = GUIUtil::TextWidth(fm, text);
if (text_width > max_text_width && text_width < max_text_width * 5 / 4) {
// Allow the image to grow up to 25% wider
qr_image_width = text_width + (2 * QR_IMAGE_TEXT_MARGIN);
qr_image_x_margin = (qr_image_width - QR_IMAGE_SIZE) / 2;
text_lines = 1;
} else {
text_lines = (text_width + max_text_width - 1) / max_text_width;
}
qr_image_height += (fm.height() * text_lines) + QR_IMAGE_TEXT_MARGIN;
}
QImage qrAddrImage(qr_image_width, qr_image_height, QImage::Format_RGB32);
qrAddrImage.fill(0xffffff);
{
QPainter painter(&qrAddrImage);
painter.drawImage(QR_IMAGE_MARGIN, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
painter.drawImage(qr_image_x_margin, QR_IMAGE_MARGIN, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
if (!text.isEmpty()) {
QRect paddedRect = qrAddrImage.rect();
paddedRect.setHeight(QR_IMAGE_SIZE + QR_IMAGE_TEXT_MARGIN);
paddedRect.setHeight(paddedRect.height() - QR_IMAGE_TEXT_MARGIN);
QFont font = GUIUtil::fixedPitchFont();
font.setStretch(QFont::SemiCondensed);
font.setLetterSpacing(QFont::AbsoluteSpacing, 1);
const qreal font_size = GUIUtil::calculateIdealFontSize(paddedRect.width() - 2 * QR_IMAGE_TEXT_MARGIN, text, font);
font.setPointSizeF(font_size);
QString text_wrapped = text;
const int char_per_line = (text.size() + text_lines - 1) / text_lines;
for (int line = 1, pos = 0; line < text_lines; ++line) {
pos += char_per_line;
text_wrapped.insert(pos, QChar{'\n'});
}
painter.setFont(font);
painter.drawText(paddedRect, Qt::AlignBottom | Qt::AlignCenter, text);
painter.drawText(paddedRect, Qt::AlignBottom | Qt::AlignCenter, text_wrapped);
}
}
@ -92,6 +128,11 @@ bool QRImageWidget::setQR(const QString& data, const QString& text)
#endif
}
bool QRImageWidget::setQR(const QString& data)
{
return setQR(data, "", OptionsModel::FontChoiceAbstract::EmbeddedFont);
}
QImage QRImageWidget::exportImage()
{
return GUIUtil::GetImage(this);

View File

@ -5,6 +5,8 @@
#ifndef BITCOIN_QT_QRIMAGEWIDGET_H
#define BITCOIN_QT_QRIMAGEWIDGET_H
#include <qt/optionsmodel.h>
#include <QImage>
#include <QLabel>
@ -12,9 +14,9 @@
static const int MAX_URI_LENGTH = 255;
/* Size of exported QR Code image */
static constexpr int QR_IMAGE_SIZE = 300;
static constexpr int QR_IMAGE_TEXT_MARGIN = 10;
static constexpr int QR_IMAGE_MARGIN = 2 * QR_IMAGE_TEXT_MARGIN;
static constexpr int QR_IMAGE_SIZE = 252;
static constexpr int QR_IMAGE_TEXT_MARGIN = 8;
static constexpr int QR_IMAGE_MARGIN = 24;
QT_BEGIN_NAMESPACE
class QMenu;
@ -29,7 +31,8 @@ class QRImageWidget : public QLabel
public:
explicit QRImageWidget(QWidget *parent = nullptr);
bool setQR(const QString& data, const QString& text = "");
bool setQR(const QString& data);
bool setQR(const QString& data, const QString& text, const OptionsModel::FontChoice& fontchoice);
QImage exportImage();
public Q_SLOTS:

View File

@ -49,8 +49,11 @@ void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info)
QString uri = GUIUtil::formatBitcoinURI(info);
#ifdef USE_QRCODE
if (ui->qr_code->setQR(uri, info.address)) {
if (ui->qr_code->setQR(uri, info.address, model->getOptionsModel()->getFontChoiceForQRCodes())) {
connect(ui->btnSaveAs, &QPushButton::clicked, ui->qr_code, &QRImageWidget::saveImage);
connect(model->getOptionsModel(), &OptionsModel::fontForQRCodesChanged, this, [=](const OptionsModel::FontChoice& fontchoice){
ui->qr_code->setQR(uri, info.address, fontchoice);
});
} else {
ui->btnSaveAs->setEnabled(false);
}

View File

@ -14,6 +14,7 @@
#include <qt/bantablemodel.h>
#include <qt/clientmodel.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/peertablesortproxy.h>
#include <qt/platformstyle.h>
#include <qt/walletmodel.h>
@ -488,6 +489,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
m_peer_widget_header_state = settings.value("PeersTabPeerHeaderState").toByteArray();
m_banlist_widget_header_state = settings.value("PeersTabBanlistHeaderState").toByteArray();
m_alternating_row_colors = settings.value("PeersTabAlternatingRowColors").toBool();
constexpr QChar nonbreaking_hyphen(8209);
const std::vector<QString> CONNECTION_TYPE_DOC{
@ -672,6 +674,11 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
connect(model, &ClientModel::mempoolSizeChanged, this, &RPCConsole::setMempoolSize);
connect(model->getOptionsModel(), &OptionsModel::peersTabAlternatingRowColorsChanged, [this](bool alternating_row_colors) {
ui->peerWidget->setAlternatingRowColors(alternating_row_colors);
ui->banlistWidget->setAlternatingRowColors(alternating_row_colors);
});
// set up peer table
ui->peerWidget->setModel(model->peerTableSortProxy());
ui->peerWidget->verticalHeader()->hide();
@ -687,6 +694,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
ui->peerWidget->horizontalHeader()->setSectionResizeMode(PeerTableModel::Age, QHeaderView::ResizeToContents);
ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this));
ui->peerWidget->setAlternatingRowColors(m_alternating_row_colors);
// create peer table context menu
peersTableContextMenu = new QMenu(this);
@ -719,6 +727,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
}
ui->banlistWidget->horizontalHeader()->setSectionResizeMode(BanTableModel::Address, QHeaderView::ResizeToContents);
ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
ui->banlistWidget->setAlternatingRowColors(m_alternating_row_colors);
// create ban table context menu
banTableContextMenu = new QMenu(this);

View File

@ -175,6 +175,7 @@ private:
bool m_is_executing{false};
QByteArray m_peer_widget_header_state;
QByteArray m_banlist_widget_header_state;
bool m_alternating_row_colors{false};
/** Update UI with latest network info from model. */
void updateNetworkState();

View File

@ -569,9 +569,19 @@ bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
}
void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
{
ForceSetArgV(strArg, util::SettingsValue{strValue});
}
void ArgsManager::ForceSetArg(const std::string& arg, const int64_t value)
{
ForceSetArg(arg, ToString(value));
}
void ArgsManager::ForceSetArgV(const std::string& arg, const util::SettingsValue& value)
{
LOCK(cs_args);
m_settings.forced_settings[SettingName(strArg)] = strValue;
m_settings.forced_settings[SettingName(arg)] = value;
}
void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)

View File

@ -334,7 +334,9 @@ protected:
// Forces an arg setting. Called by SoftSetArg() if the arg hasn't already
// been set. Also called directly in testing.
void ForceSetArg(const std::string& strArg, const std::string& strValue);
void ForceSetArg(const std::string& arg, const std::string& value);
void ForceSetArg(const std::string& arg, int64_t value);
void ForceSetArgV(const std::string& arg, const util::SettingsValue& value);
/**
* Returns the appropriate chain name from the program arguments.

View File

@ -6,6 +6,7 @@
#include <wallet/load.h>
#include <interfaces/chain.h>
#include <node/interface_ui.h>
#include <scheduler.h>
#include <util/check.h>
#include <util/fs.h>
@ -22,6 +23,17 @@
#include <system_error>
namespace wallet {
bool HandleWalletLoadError(interfaces::Chain& chain, const std::string& wallet_file, const bilingual_str& error_string)
{
if (!chain.initQuestion(error_string + Untranslated("\n\n") + _("Continue without this wallet?"), error_string, _("Error"), CClientUIInterface::MSG_ERROR | CClientUIInterface::MODAL | CClientUIInterface::BTN_OK | CClientUIInterface::BTN_ABORT)) {
return false;
}
RemoveWalletSetting(chain, wallet_file);
return true;
}
bool VerifyWallets(WalletContext& context)
{
interfaces::Chain& chain = *context.chain;
@ -74,6 +86,7 @@ bool VerifyWallets(WalletContext& context)
// Keep track of each wallet absolute path to detect duplicates.
std::set<fs::path> wallet_paths;
bool modified_wallet_list = false;
for (const auto& wallet : chain.getSettingsList("wallet")) {
const auto& wallet_file = wallet.get_str();
const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file));
@ -93,12 +106,20 @@ bool VerifyWallets(WalletContext& context)
if (status == DatabaseStatus::FAILED_NOT_FOUND) {
chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s", error_string.original)));
} else {
chain.initError(error_string);
return false;
if (HandleWalletLoadError(chain, wallet_file, error_string)) {
modified_wallet_list = true;
} else {
return false;
}
}
}
}
if (modified_wallet_list) {
// Ensure new wallet list overrides commandline options
args.ForceSetArgV("wallet", chain.getRwSetting("wallet"));
}
return true;
}
@ -127,8 +148,11 @@ bool LoadWallets(WalletContext& context)
std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings) : nullptr;
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
if (!pwallet) {
chain.initError(error);
return false;
if (HandleWalletLoadError(chain, name, error)) {
continue;
} else {
return false;
}
}
NotifyWalletLoaded(context, pwallet);