34 #include <QAbstractItemView>
35 #include <QApplication>
38 #include <QDesktopServices>
39 #include <QDoubleValidator>
40 #include <QFileDialog>
42 #include <QFontDatabase>
43 #include <QFontMetrics>
44 #include <QGuiApplication>
49 #include <QMouseEvent>
51 #include <QProgressDialog>
57 #include <QTextDocument>
76 return date.date().toString(QLocale().dateFormat(QLocale::ShortFormat)) +
77 QString(
" ") + date.toString(
"hh:mm");
81 return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
85 return QFontDatabase::systemFont(QFontDatabase::FixedFont);
90 if (addr.size() < 2) {
95 std::swap(addr[addr.size() - 1], addr[addr.size() - 2]);
106 static const std::vector<uint8_t> dummydata = {
107 0xeb, 0x15, 0x23, 0x1d, 0xfc, 0xeb, 0x60, 0x92, 0x58, 0x86,
108 0xb6, 0x7d, 0x06, 0x52, 0x99, 0x92, 0x59, 0x15, 0xae, 0xb1};
128 parent->setFocusProxy(widget);
133 widget->setPlaceholderText(
134 QObject::tr(
"Enter a Bitcoin address (e.g. %1)")
136 widget->setValidator(
144 if (!uri.isValid() || uri.scheme() != scheme) {
149 rv.
address = uri.scheme() +
":" + uri.path();
152 if (rv.
address.endsWith(
"/")) {
157 QUrlQuery uriQuery(uri);
158 QList<QPair<QString, QString>> items = uriQuery.queryItems();
159 for (QList<QPair<QString, QString>>::iterator i = items.begin();
160 i != items.end(); i++) {
161 bool fShouldReturnFalse =
false;
162 if (i->first.startsWith(
"req-")) {
163 i->first.remove(0, 4);
164 fShouldReturnFalse =
true;
167 if (i->first ==
"label") {
168 rv.
label = i->second;
169 fShouldReturnFalse =
false;
171 if (i->first ==
"message") {
173 fShouldReturnFalse =
false;
174 }
else if (i->first ==
"amount") {
175 if (!i->second.isEmpty()) {
181 fShouldReturnFalse =
false;
184 if (fShouldReturnFalse) {
200 if (uri.startsWith(scheme +
"://", Qt::CaseInsensitive)) {
201 uri.replace(0, scheme.length() + 3, scheme +
":");
203 QUrl uriInstance(uri);
217 ret += QString(
"?amount=%1")
224 if (!info.
label.isEmpty()) {
225 QString lbl(QUrl::toPercentEncoding(info.
label));
226 ret += QString(
"%1label=%2").arg(paramCount == 0 ?
"?" :
"&").arg(lbl);
231 QString msg(QUrl::toPercentEncoding(info.
message));
233 QString(
"%1message=%2").arg(paramCount == 0 ?
"?" :
"&").arg(msg);
244 CTxOut txOut(amount, script);
249 QString escaped = str.toHtmlEscaped();
251 escaped = escaped.replace(
"\n",
"<br>\n");
256 QString
HtmlEscape(
const std::string &str,
bool fMultiLine) {
257 return HtmlEscape(QString::fromStdString(str), fMultiLine);
261 if (!view || !view->selectionModel()) {
264 QModelIndexList selection = view->selectionModel()->selectedRows(column);
266 if (!selection.isEmpty()) {
272 QList<QModelIndex>
getEntryData(
const QAbstractItemView *view,
int column) {
273 if (!view || !view->selectionModel()) {
274 return QList<QModelIndex>();
276 return view->selectionModel()->selectedRows(column);
279 bool hasEntryData(
const QAbstractItemView *view,
int column,
int role) {
281 if (selection.isEmpty()) {
284 return !selection.at(0).data(role).toString().isEmpty();
292 const QString &dir,
const QString &filter,
293 QString *selectedSuffixOut) {
294 QString selectedFilter;
299 QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
305 parent, caption, myDir, filter, &selectedFilter));
309 QRegExp filter_re(
".* \\(\\*\\.(.*)[ \\)]");
310 QString selectedSuffix;
311 if (filter_re.exactMatch(selectedFilter)) {
312 selectedSuffix = filter_re.cap(1);
316 QFileInfo info(result);
317 if (!result.isEmpty()) {
318 if (info.suffix().isEmpty() && !selectedSuffix.isEmpty()) {
320 if (!result.endsWith(
".")) {
323 result.append(selectedSuffix);
328 if (selectedSuffixOut) {
329 *selectedSuffixOut = selectedSuffix;
335 const QString &dir,
const QString &filter,
336 QString *selectedSuffixOut) {
337 QString selectedFilter;
342 QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
348 parent, caption, myDir, filter, &selectedFilter));
350 if (selectedSuffixOut) {
353 QRegExp filter_re(
".* \\(\\*\\.(.*)[ \\)]");
354 QString selectedSuffix;
355 if (filter_re.exactMatch(selectedFilter)) {
356 selectedSuffix = filter_re.cap(1);
358 *selectedSuffixOut = selectedSuffix;
364 if (QThread::currentThread() != qApp->thread()) {
365 return Qt::BlockingQueuedConnection;
367 return Qt::DirectConnection;
372 QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p));
376 return atW->window() == w;
383 checkPoint(QPoint(w->width() - 1, w->height() - 1), w) &&
384 checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
394 if (w->isMinimized()) {
405 QObject::connect(
new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), w),
406 &QShortcut::activated, w, &QWidget::close);
414 QDesktopServices::openUrl(
424 std::ofstream configFile{pathConfig, std::ios_base::app};
426 if (!configFile.good()) {
433 bool res = QDesktopServices::openUrl(
438 res = QProcess::startDetached(
447 return s.split(separator,
448 #
if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
451 QString::SkipEmptyParts
458 : QObject(parent), size_threshold(_size_threshold) {}
461 if (evt->type() == QEvent::ToolTipChange) {
462 QWidget *widget =
static_cast<QWidget *
>(obj);
463 QString tooltip = widget->toolTip();
464 if (tooltip.size() >
size_threshold && !tooltip.startsWith(
"<qt") &&
465 !Qt::mightBeRichText(tooltip)) {
468 tooltip =
"<qt>" +
HtmlEscape(tooltip,
true) +
"</qt>";
469 widget->setToolTip(tooltip);
473 return QObject::eventFilter(obj, evt);
480 if (event->type() == QEvent::FocusOut) {
481 auto focus_out =
static_cast<QFocusEvent *
>(event);
482 if (focus_out->reason() != Qt::PopupFocusReason) {
483 auto label = qobject_cast<QLabel *>(watched);
485 auto flags = label->textInteractionFlags();
486 label->setTextInteractionFlags(Qt::NoTextInteraction);
487 label->setTextInteractionFlags(
flags);
492 return QObject::eventFilter(watched, event);
496 connect(
tableView->horizontalHeader(), &QHeaderView::sectionResized,
this,
498 connect(
tableView->horizontalHeader(), &QHeaderView::geometriesChanged,
505 disconnect(
tableView->horizontalHeader(), &QHeaderView::sectionResized,
507 disconnect(
tableView->horizontalHeader(), &QHeaderView::geometriesChanged,
515 int logicalIndex, QHeaderView::ResizeMode resizeMode) {
516 tableView->horizontalHeader()->setSectionResizeMode(logicalIndex,
522 tableView->setColumnWidth(nColumnIndex, width);
523 tableView->horizontalHeader()->resizeSection(nColumnIndex, width);
527 int nColumnsWidthSum = 0;
529 nColumnsWidthSum +=
tableView->horizontalHeader()->sectionSize(i);
531 return nColumnsWidthSum;
536 int nTableWidth =
tableView->horizontalHeader()->width();
538 if (nTableWidth > 0) {
539 int nOtherColsWidth =
541 tableView->horizontalHeader()->sectionSize(column);
542 nResult = std::max(nResult, nTableWidth - nOtherColsWidth);
554 int nTableWidth =
tableView->horizontalHeader()->width();
556 if (nColsWidth > nTableWidth) {
575 if (newSize > remainingWidth) {
598 QTableView *table,
int lastColMinimumWidth,
int allColsMinimumWidth,
600 : QObject(parent), tableView(table),
601 lastColumnMinimumWidth(lastColMinimumWidth),
602 allColumnsMinimumWidth(allColsMinimumWidth) {
606 tableView->horizontalHeader()->setMinimumSectionSize(
613 static fs::path StartupShortcutPath() {
616 return GetSpecialFolderPath(CSIDL_STARTUP) /
"Bitcoin.lnk";
620 return GetSpecialFolderPath(CSIDL_STARTUP) /
"Bitcoin (testnet).lnk";
622 return GetSpecialFolderPath(CSIDL_STARTUP) /
633 fs::remove(StartupShortcutPath());
636 CoInitialize(
nullptr);
639 IShellLinkW *psl =
nullptr;
641 CoCreateInstance(CLSID_ShellLink,
nullptr, CLSCTX_INPROC_SERVER,
642 IID_IShellLinkW,
reinterpret_cast<void **
>(&psl));
647 GetModuleFileNameW(
nullptr, pszExePath, ARRAYSIZE(pszExePath));
650 QString strArgs =
"-min";
652 strArgs += QString::fromStdString(
656 psl->SetPath(pszExePath);
657 PathRemoveFileSpecW(pszExePath);
658 psl->SetWorkingDirectory(pszExePath);
659 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
660 psl->SetArguments(strArgs.toStdWString().c_str());
664 IPersistFile *ppf =
nullptr;
665 hres = psl->QueryInterface(IID_IPersistFile,
666 reinterpret_cast<void **
>(&ppf));
669 hres = ppf->Save(StartupShortcutPath().wstring().c_str(), TRUE);
682 #elif defined(Q_OS_LINUX)
688 char *pszConfigHome = getenv(
"XDG_CONFIG_HOME");
690 return fs::path(pszConfigHome) /
"autostart";
692 char *pszHome = getenv(
"HOME");
694 return fs::path(pszHome) /
".config" /
"autostart";
699 static fs::path GetAutostartFilePath() {
702 return GetAutostartDir() /
"bitcoin.desktop";
704 return GetAutostartDir() /
strprintf(
"bitcoin-%s.desktop", chain);
708 std::ifstream optionFile{GetAutostartFilePath()};
709 if (!optionFile.good()) {
714 while (!optionFile.eof()) {
715 getline(optionFile, line);
716 if (line.find(
"Hidden") != std::string::npos &&
717 line.find(
"true") != std::string::npos) {
728 fs::remove(GetAutostartFilePath());
732 readlink(
"/proc/self/exe", pszExePath,
sizeof(pszExePath) - 1);
736 pszExePath[r] =
'\0';
740 std::ofstream optionFile{GetAutostartFilePath(),
741 std::ios_base::out | std::ios_base::trunc};
742 if (!optionFile.good()) {
747 optionFile <<
"[Desktop Entry]\n";
748 optionFile <<
"Type=Application\n";
750 optionFile <<
"Name=Bitcoin\n";
752 optionFile <<
strprintf(
"Name=Bitcoin (%s)\n", chain);
754 optionFile <<
"Exec=" << pszExePath
755 <<
strprintf(
" -min -chain=%s\n", chain);
756 optionFile <<
"Terminal=false\n";
757 optionFile <<
"Hidden=false\n";
775 QApplication::clipboard()->setText(str, QClipboard::Clipboard);
776 QApplication::clipboard()->setText(str, QClipboard::Selection);
784 return QString::fromStdString(path.
u8string());
790 return QObject::tr(
"Unroutable");
802 return QObject::tr(
"Internal");
812 int days = secs / 86400;
813 int hours = (secs % 86400) / 3600;
814 int mins = (secs % 3600) / 60;
815 int seconds = secs % 60;
818 strList.append(QString(QObject::tr(
"%1 d")).arg(days));
821 strList.append(QString(QObject::tr(
"%1 h")).arg(hours));
824 strList.append(QString(QObject::tr(
"%1 m")).arg(mins));
826 if (seconds || (!days && !hours && !mins)) {
827 strList.append(QString(QObject::tr(
"%1 s")).arg(seconds));
830 return strList.join(
" ");
836 constexpr uint64_t nonExperimentalMask =
839 strList.append(QString::fromStdString(flag));
842 if (strList.size()) {
843 return strList.join(
" & ");
845 return QObject::tr(
"None");
850 return (ping_time == std::chrono::microseconds::max() || ping_time == 0us)
852 : QString(QObject::tr(
"%1 ms"))
853 .arg(QString::number(
858 return QString(QObject::tr(
"%1 s"))
859 .arg(QString::number((
int)nTimeOffset, 10));
864 QString timeBehindText;
865 const int HOUR_IN_SECONDS = 60 * 60;
866 const int DAY_IN_SECONDS = 24 * 60 * 60;
867 const int WEEK_IN_SECONDS = 7 * 24 * 60 * 60;
869 const int YEAR_IN_SECONDS = 31556952;
871 timeBehindText = QObject::tr(
"%n second(s)",
"", secs);
872 }
else if (secs < 2 * HOUR_IN_SECONDS) {
873 timeBehindText = QObject::tr(
"%n minute(s)",
"", secs / 60);
874 }
else if (secs < 2 * DAY_IN_SECONDS) {
875 timeBehindText = QObject::tr(
"%n hour(s)",
"", secs / HOUR_IN_SECONDS);
876 }
else if (secs < 2 * WEEK_IN_SECONDS) {
877 timeBehindText = QObject::tr(
"%n day(s)",
"", secs / DAY_IN_SECONDS);
878 }
else if (secs < YEAR_IN_SECONDS) {
879 timeBehindText = QObject::tr(
"%n week(s)",
"", secs / WEEK_IN_SECONDS);
881 qint64 years = secs / YEAR_IN_SECONDS;
882 qint64 remainder = secs % YEAR_IN_SECONDS;
883 timeBehindText = QObject::tr(
"%1 and %2")
884 .arg(QObject::tr(
"%n year(s)",
"", years))
885 .arg(QObject::tr(
"%n week(s)",
"",
886 remainder / WEEK_IN_SECONDS));
888 return timeBehindText;
893 return QString(QObject::tr(
"%1 B")).arg(bytes);
895 if (bytes < 1024 * 1024) {
896 return QString(QObject::tr(
"%1 KB")).arg(bytes / 1024);
898 if (bytes < 1024 * 1024 * 1024) {
899 return QString(QObject::tr(
"%1 MB")).arg(bytes / 1024 / 1024);
902 return QString(QObject::tr(
"%1 GB")).arg(bytes / 1024 / 1024 / 1024);
906 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
907 return !pixmap(Qt::ReturnByValue).isNull();
909 return pixmap() !=
nullptr;
914 qreal minPointSize, qreal font_size) {
915 while (font_size >= minPointSize) {
916 font.setPointSizeF(font_size);
917 QFontMetrics fm(font);
935 if (event->type() == QEvent::KeyPress) {
936 if (
static_cast<QKeyEvent *
>(event)->key() == Qt::Key_Escape) {
940 return QItemDelegate::eventFilter(
object, event);
943 int TextWidth(
const QFontMetrics &fm,
const QString &text) {
944 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
945 return fm.horizontalAdvance(text);
947 return fm.width(text);
955 dialog->resize(dialog->width() + 2 * margin, dialog->height());
964 const std::string qt_link{
"static"};
966 const std::string qt_link{
"dynamic"};
968 #ifdef QT_STATICPLUGIN
969 const std::string plugin_link{
"static"};
971 const std::string plugin_link{
"dynamic"};
973 LogPrintf(
"Qt %s (%s), plugin=%s (%s)\n", qVersion(), qt_link,
974 QGuiApplication::platformName().toStdString(), plugin_link);
975 LogPrintf(
"System: %s, %s\n", QSysInfo::prettyProductName().toStdString(),
976 QSysInfo::buildAbi().toStdString());
977 for (
const QScreen *s : QGuiApplication::screens()) {
978 LogPrintf(
"Screen: %s %dx%d, pixel ratio=%.1f\n",
979 s->name().toStdString(), s->size().width(),
980 s->size().height(), s->devicePixelRatio());
984 void PopupMenu(QMenu *menu,
const QPoint &point, QAction *at_action) {
986 if (QApplication::platformName() ==
"minimal") {
989 menu->popup(point, at_action);
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams ¶ms)
const CChainParams & Params()
Return the currently selected parameters.
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
std::string GetChainName() const
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
Bitcoin address widget validator, checks for a valid bitcoin address.
Bitcoin address entry widget validator, checks for valid characters and removes some whitespace.
static QString format(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD, bool justify=false)
Format as string.
static bool parse(int unit, const QString &value, Amount *val_out)
Parse string to coin amount.
static const std::string TESTNET
static const std::string MAIN
BIP70 chain name strings (main, test or regtest)
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Serialized script, used inside transaction inputs and outputs.
An output of a transaction.
void mouseReleaseEvent(QMouseEvent *event) override
void clicked(const QPoint &point)
Emitted when the label is clicked.
void mouseReleaseEvent(QMouseEvent *event) override
void clicked(const QPoint &point)
Emitted when the progressbar is clicked.
bool eventFilter(QObject *object, QEvent *event) override
bool eventFilter(QObject *watched, QEvent *event) override
LabelOutOfFocusEventFilter(QObject *parent)
void setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode)
int allColumnsMinimumWidth
void resizeColumn(int nColumnIndex, int width)
void on_sectionResized(int logicalIndex, int oldSize, int newSize)
TableViewLastColumnResizingFixer(QTableView *table, int lastColMinimumWidth, int allColsMinimumWidth, QObject *parent)
Initializes all internal variables and prepares the the resize modes of the last 2 columns of the tab...
int secondToLastColumnIndex
void stretchColumnWidth(int column)
void on_geometriesChanged()
void connectViewHeadersSignals()
void adjustTableColumnsWidth()
int getAvailableWidthForColumn(int column)
void disconnectViewHeadersSignals()
int lastColumnMinimumWidth
bool eventFilter(QObject *obj, QEvent *evt) override
ToolTipToRichTextFilter(int size_threshold, QObject *parent=0)
Line edit that can be marked as "invalid" to show input validation feedback.
void setCheckValidator(const QValidator *v)
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
std::string u8string() const
Top-level interface for a bitcoin node (bitcoind process).
bool IsValidDestinationString(const std::string &str, const CChainParams ¶ms)
CTxDestination DecodeDestination(const std::string &addr, const CChainParams ¶ms)
void ForceActivation()
Force application activation on macOS.
Utility functions used by the Bitcoin Qt UI.
QString NetworkToQString(Network net)
Convert enum Network to QString.
fs::path qstringToBoostPath(const QString &path)
Convert QString to OS specific boost path through UTF-8.
bool isObscured(QWidget *w)
bool parseBitcoinURI(const QString &scheme, const QUrl &uri, SendCoinsRecipient *out)
bool isDust(interfaces::Node &node, const QString &address, const Amount amount, const CChainParams &chainParams)
Qt::ConnectionType blockingGUIThreadConnection()
Get connection type to call object slot in GUI thread with invokeMethod.
QString HtmlEscape(const QString &str, bool fMultiLine)
void PopupMenu(QMenu *menu, const QPoint &point, QAction *at_action)
Call QMenu::popup() only on supported QT_QPA_PLATFORM.
QList< QModelIndex > getEntryData(const QAbstractItemView *view, int column)
Return a field of the currently selected entry as a QString.
QString formatBytes(uint64_t bytes)
QString formatDurationStr(std::chrono::seconds dur)
Convert seconds into a QString with days, hours, mins, secs.
void handleCloseWindowShortcut(QWidget *w)
void PolishProgressDialog(QProgressDialog *dialog)
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut)
Get open filename, convenience wrapper for QFileDialog::getOpenFileName.
std::string DummyAddress(const CChainParams ¶ms)
static std::string MakeAddrInvalid(std::string addr, const CChainParams ¶ms)
QString getDefaultDataDirectory()
Determine default data directory for operating system.
void copyEntryData(const QAbstractItemView *view, int column, int role)
Copy a field of the currently selected entry of a view to the clipboard.
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut)
Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix when ...
QString boostPathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
bool SetStartOnSystemStartup(bool fAutoStart)
void bringToFront(QWidget *w)
void LogQtInfo()
Writes to debug.log short info about the used Qt and the host system.
QString formatPingTime(std::chrono::microseconds ping_time)
Format a CNodeStats.m_last_ping_time into a user-readable string or display N/A, if 0.
QString dateTimeStr(const QDateTime &date)
bool checkPoint(const QPoint &p, const QWidget *w)
QString formatBitcoinURI(const SendCoinsRecipient &info)
QString formatTimeOffset(int64_t nTimeOffset)
Format a CNodeCombinedStats.nTimeOffset into a user-readable string.
QString convertToCashAddr(const CChainParams ¶ms, const QString &addr)
QString formatServicesStr(quint64 mask)
Format CNodeStats.nServices bitmask into a user-readable string.
QString formatNiceTimeOffset(qint64 secs)
bool GetStartOnSystemStartup()
QStringList splitSkipEmptyParts(const QString &s, const QString &separator)
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text.
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
void setClipboard(const QString &str)
bool hasEntryData(const QAbstractItemView *view, int column, int role)
Returns true if the specified field of the currently selected view entry is not empty.
qreal calculateIdealFontSize(int width, const QString &text, QFont font, qreal minPointSize, qreal font_size)
static path u8path(const std::string &utf8_str)
static bool create_directories(const std::filesystem::path &p)
Create directory (and if necessary its parents), unless the leaf directory already exists or is a sym...
static bool exists(const path &p)
ConnectionType
Different types of connections to a peer.
@ NET_MAX
Dummy value to indicate the number of NET_* constants.
@ NET_ONION
TOR (v2 or v3)
@ NET_UNROUTABLE
Addresses from these networks are not publicly routable on the global Internet.
@ NET_INTERNAL
A set of addresses that represent the hash of a string or FQDN.
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
std::vector< std::string > serviceFlagsToStr(const uint64_t flags)
Convert service flags (a bitmask of NODE_*) to human readable strings.
@ NODE_LAST_NON_EXPERIMENTAL_SERVICE_BIT
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
static constexpr Amount zero() noexcept
fs::path GetDefaultDataDir()
fs::path GetConfigFile(const std::string &confPath)
const char *const BITCOIN_CONF_FILENAME
constexpr int64_t count_microseconds(std::chrono::microseconds t)
constexpr int64_t count_seconds(std::chrono::seconds t)