diff options
Diffstat (limited to 'src/qt/guiutil.cpp')
-rw-r--r-- | src/qt/guiutil.cpp | 168 |
1 files changed, 133 insertions, 35 deletions
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 89d6deb70d..9565fa508f 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2020 The Bitcoin Core developers +// Copyright (c) 2011-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -6,11 +6,13 @@ #include <qt/bitcoinaddressvalidator.h> #include <qt/bitcoinunits.h> +#include <qt/platformstyle.h> #include <qt/qvalidatedlineedit.h> #include <qt/sendcoinsrecipient.h> #include <base58.h> #include <chainparams.h> +#include <fs.h> #include <interfaces/node.h> #include <key_io.h> #include <policy/policy.h> @@ -19,6 +21,7 @@ #include <script/script.h> #include <script/standard.h> #include <util/system.h> +#include <util/time.h> #ifdef WIN32 #ifndef NOMINMAX @@ -29,11 +32,13 @@ #include <shlwapi.h> #endif +#include <QAbstractButton> #include <QAbstractItemView> #include <QApplication> #include <QClipboard> #include <QDateTime> #include <QDesktopServices> +#include <QDialog> #include <QDoubleValidator> #include <QFileDialog> #include <QFont> @@ -42,6 +47,7 @@ #include <QGuiApplication> #include <QJsonObject> #include <QKeyEvent> +#include <QLatin1String> #include <QLineEdit> #include <QList> #include <QLocale> @@ -59,7 +65,12 @@ #include <QUrlQuery> #include <QtGlobal> +#include <cassert> #include <chrono> +#include <exception> +#include <fstream> +#include <string> +#include <vector> #if defined(Q_OS_MAC) @@ -77,7 +88,7 @@ QString dateTimeStr(const QDateTime &date) QString dateTimeStr(qint64 nTime) { - return dateTimeStr(QDateTime::fromTime_t((qint32)nTime)); + return dateTimeStr(QDateTime::fromSecsSinceEpoch(nTime)); } QFont fixedPitchFont(bool use_embedded_font) @@ -119,6 +130,11 @@ void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent) widget->setCheckValidator(new BitcoinAddressCheckValidator(parent)); } +void AddButtonShortcut(QAbstractButton* button, const QKeySequence& shortcut) +{ + QObject::connect(new QShortcut(shortcut, button), &QShortcut::activated, [button]() { button->animateClick(); }); +} + bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { // return if URI is not valid or is no bitcoin: URI @@ -262,9 +278,15 @@ bool hasEntryData(const QAbstractItemView *view, int column, int role) return !selection.at(0).data(role).toString().isEmpty(); } +void LoadFont(const QString& file_name) +{ + const int id = QFontDatabase::addApplicationFont(file_name); + assert(id != -1); +} + QString getDefaultDataDirectory() { - return boostPathToQString(GetDefaultDataDir()); + return PathToQString(GetDefaultDataDir()); } QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, @@ -397,11 +419,11 @@ void handleCloseWindowShortcut(QWidget* w) void openDebugLogfile() { - fs::path pathDebug = GetDataDir() / "debug.log"; + fs::path pathDebug = gArgs.GetDataDirNet() / "debug.log"; /* Open debug.log with the associated application */ if (fs::exists(pathDebug)) - QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug))); + QDesktopServices::openUrl(QUrl::fromLocalFile(PathToQString(pathDebug))); } bool openBitcoinConf() @@ -409,7 +431,7 @@ bool openBitcoinConf() fs::path pathConfig = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); /* Create the file */ - fsbridge::ofstream configFile(pathConfig, std::ios_base::app); + std::ofstream configFile{pathConfig, std::ios_base::app}; if (!configFile.good()) return false; @@ -417,11 +439,11 @@ bool openBitcoinConf() configFile.close(); /* Open bitcoin.conf with the associated application */ - bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig))); + bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(PathToQString(pathConfig))); #ifdef Q_OS_MAC // Workaround for macOS-specific behavior; see #15409. if (!res) { - res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", boostPathToQString(pathConfig)}); + res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", PathToQString(pathConfig)}); } #endif @@ -569,7 +591,7 @@ fs::path static GetAutostartFilePath() bool GetStartOnSystemStartup() { - fsbridge::ifstream optionFile(GetAutostartFilePath()); + std::ifstream optionFile{GetAutostartFilePath()}; if (!optionFile.good()) return false; // Scan through file for "Hidden=true": @@ -600,7 +622,7 @@ bool SetStartOnSystemStartup(bool fAutoStart) fs::create_directories(GetAutostartDir()); - fsbridge::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc); + std::ofstream optionFile{GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc}; if (!optionFile.good()) return false; std::string chain = gArgs.GetChainName(); @@ -628,18 +650,21 @@ bool SetStartOnSystemStartup(bool fAutoStart) { return false; } void setClipboard(const QString& str) { - QApplication::clipboard()->setText(str, QClipboard::Clipboard); - QApplication::clipboard()->setText(str, QClipboard::Selection); + QClipboard* clipboard = QApplication::clipboard(); + clipboard->setText(str, QClipboard::Clipboard); + if (clipboard->supportsSelection()) { + clipboard->setText(str, QClipboard::Selection); + } } -fs::path qstringToBoostPath(const QString &path) +fs::path QStringToPath(const QString &path) { - return fs::path(path.toStdString()); + return fs::u8path(path.toStdString()); } -QString boostPathToQString(const fs::path &path) +QString PathToQString(const fs::path &path) { - return QString::fromStdString(path.string()); + return QString::fromStdString(path.u8string()); } QString NetworkToQString(Network net) @@ -661,21 +686,34 @@ QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction { QString prefix; if (prepend_direction) { - prefix = (conn_type == ConnectionType::INBOUND) ? QObject::tr("Inbound") : QObject::tr("Outbound") + " "; + prefix = (conn_type == ConnectionType::INBOUND) ? + /*: An inbound connection from a peer. An inbound connection + is a connection initiated by a peer. */ + QObject::tr("Inbound") : + /*: An outbound connection to a peer. An outbound connection + is a connection initiated by us. */ + QObject::tr("Outbound") + " "; } switch (conn_type) { case ConnectionType::INBOUND: return prefix; + //: Peer connection type that relays all network information. case ConnectionType::OUTBOUND_FULL_RELAY: return prefix + QObject::tr("Full Relay"); + /*: Peer connection type that relays network information about + blocks and not transactions or addresses. */ case ConnectionType::BLOCK_RELAY: return prefix + QObject::tr("Block Relay"); + //: Peer connection type established manually through one of several methods. case ConnectionType::MANUAL: return prefix + QObject::tr("Manual"); + //: Short-lived peer connection type that tests the aliveness of known addresses. case ConnectionType::FEELER: return prefix + QObject::tr("Feeler"); + //: Short-lived peer connection type that solicits known addresses from a peer. case ConnectionType::ADDR_FETCH: return prefix + QObject::tr("Address Fetch"); } // no default case, so the compiler can warn about missing cases assert(false); } -QString formatDurationStr(int secs) +QString formatDurationStr(std::chrono::seconds dur) { + const auto secs = count_seconds(dur); QStringList strList; int days = secs / 86400; int hours = (secs % 86400) / 3600; @@ -683,13 +721,13 @@ QString formatDurationStr(int secs) int seconds = secs % 60; if (days) - strList.append(QString(QObject::tr("%1 d")).arg(days)); + strList.append(QObject::tr("%1 d").arg(days)); if (hours) - strList.append(QString(QObject::tr("%1 h")).arg(hours)); + strList.append(QObject::tr("%1 h").arg(hours)); if (mins) - strList.append(QString(QObject::tr("%1 m")).arg(mins)); + strList.append(QObject::tr("%1 m").arg(mins)); if (seconds || (!days && !hours && !mins)) - strList.append(QString(QObject::tr("%1 s")).arg(seconds)); + strList.append(QObject::tr("%1 s").arg(seconds)); return strList.join(" "); } @@ -703,7 +741,7 @@ QString formatServicesStr(quint64 mask) } if (strList.size()) - return strList.join(" & "); + return strList.join(", "); else return QObject::tr("None"); } @@ -712,12 +750,12 @@ QString formatPingTime(std::chrono::microseconds ping_time) { return (ping_time == std::chrono::microseconds::max() || ping_time == 0us) ? QObject::tr("N/A") : - QString(QObject::tr("%1 ms")).arg(QString::number((int)(count_microseconds(ping_time) / 1000), 10)); + QObject::tr("%1 ms").arg(QString::number((int)(count_microseconds(ping_time) / 1000), 10)); } QString formatTimeOffset(int64_t nTimeOffset) { - return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10)); + return QObject::tr("%1 s").arg(QString::number((int)nTimeOffset, 10)); } QString formatNiceTimeOffset(qint64 secs) @@ -759,14 +797,14 @@ QString formatNiceTimeOffset(qint64 secs) QString formatBytes(uint64_t bytes) { - if(bytes < 1024) - return QString(QObject::tr("%1 B")).arg(bytes); - if(bytes < 1024 * 1024) - return QString(QObject::tr("%1 KB")).arg(bytes / 1024); - if(bytes < 1024 * 1024 * 1024) - return QString(QObject::tr("%1 MB")).arg(bytes / 1024 / 1024); + if (bytes < 1'000) + return QObject::tr("%1 B").arg(bytes); + if (bytes < 1'000'000) + return QObject::tr("%1 kB").arg(bytes / 1'000); + if (bytes < 1'000'000'000) + return QObject::tr("%1 MB").arg(bytes / 1'000'000); - return QString(QObject::tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024); + return QObject::tr("%1 GB").arg(bytes / 1'000'000'000); } qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize, qreal font_size) { @@ -781,6 +819,39 @@ qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal m return font_size; } +ThemedLabel::ThemedLabel(const PlatformStyle* platform_style, QWidget* parent) + : QLabel{parent}, m_platform_style{platform_style} +{ + assert(m_platform_style); +} + +void ThemedLabel::setThemedPixmap(const QString& image_filename, int width, int height) +{ + m_image_filename = image_filename; + m_pixmap_width = width; + m_pixmap_height = height; + updateThemedPixmap(); +} + +void ThemedLabel::changeEvent(QEvent* e) +{ + if (e->type() == QEvent::PaletteChange) { + updateThemedPixmap(); + } + + QLabel::changeEvent(e); +} + +void ThemedLabel::updateThemedPixmap() +{ + setPixmap(m_platform_style->SingleColorIcon(m_image_filename).pixmap(m_pixmap_width, m_pixmap_height)); +} + +ClickableLabel::ClickableLabel(const PlatformStyle* platform_style, QWidget* parent) + : ThemedLabel{platform_style, parent} +{ +} + void ClickableLabel::mouseReleaseEvent(QMouseEvent *event) { Q_EMIT clicked(event->pos()); @@ -807,10 +878,12 @@ void PolishProgressDialog(QProgressDialog* dialog) // Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357. const int margin = TextWidth(dialog->fontMetrics(), ("X")); dialog->resize(dialog->width() + 2 * margin, dialog->height()); - dialog->show(); -#else - Q_UNUSED(dialog); #endif + // QProgressDialog estimates the time the operation will take (based on time + // for steps), and only shows itself if that estimate is beyond minimumDuration. + // The default minimumDuration value is 4 seconds, and it could make users + // think that the GUI is frozen. + dialog->setMinimumDuration(0); } int TextWidth(const QFontMetrics& fm, const QString& text) @@ -893,4 +966,29 @@ QImage GetImage(const QLabel* label) #endif } +QString MakeHtmlLink(const QString& source, const QString& link) +{ + return QString(source).replace( + link, + QLatin1String("<a href=\"") + link + QLatin1String("\">") + link + QLatin1String("</a>")); +} + +void PrintSlotException( + const std::exception* exception, + const QObject* sender, + const QObject* receiver) +{ + std::string description = sender->metaObject()->className(); + description += "->"; + description += receiver->metaObject()->className(); + PrintExceptionContinue(exception, description.c_str()); +} + +void ShowModalDialogAsynchronously(QDialog* dialog) +{ + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setWindowModality(Qt::ApplicationModal); + dialog->show(); +} + } // namespace GUIUtil |