Bitcoin ABC 0.31.6
P2P Digital Currency
guiutil.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2019 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <qt/guiutil.h>
6
7#include <cashaddrenc.h>
8#include <chainparams.h>
9#include <common/args.h>
10#include <interfaces/node.h>
11#include <key_io.h>
12#include <logging.h>
13#include <policy/policy.h>
15#include <protocol.h>
17#include <qt/bitcoinunits.h>
20#include <script/script.h>
21#include <script/standard.h>
22#include <util/chaintype.h>
23#include <util/exception.h>
24#include <util/fs.h>
25#include <util/fs_helpers.h>
26#include <util/strencodings.h>
27#include <util/time.h>
28
29#ifdef WIN32
30#ifndef NOMINMAX
31#define NOMINMAX
32#endif
33#include <shellapi.h>
34#include <shlobj.h>
35#include <shlwapi.h>
36#endif
37
38#include <QAbstractItemView>
39#include <QApplication>
40#include <QClipboard>
41#include <QDateTime>
42#include <QDesktopServices>
43#include <QDialog>
44#include <QDoubleValidator>
45#include <QFileDialog>
46#include <QFont>
47#include <QFontDatabase>
48#include <QFontMetrics>
49#include <QGuiApplication>
50#include <QKeyEvent>
51#include <QKeySequence>
52#include <QLineEdit>
53#include <QList>
54#include <QMenu>
55#include <QMouseEvent>
56#include <QProcess>
57#include <QProgressDialog>
58#include <QRegularExpression>
59#include <QScreen>
60#include <QSettings>
61#include <QShortcut>
62#include <QSize>
63#include <QStandardPaths>
64#include <QString>
65#include <QTextDocument> // for Qt::mightBeRichText
66#include <QThread>
67#include <QUrlQuery>
68#include <QtGlobal>
69
70#include <chrono>
71#include <exception>
72#include <fstream>
73#include <string>
74#include <vector>
75
76#if defined(Q_OS_MAC)
77
78void ForceActivation();
79#endif
80
81namespace GUIUtil {
82
83QString dateTimeStr(const QDateTime &date) {
84 return date.date().toString(QLocale().dateFormat(QLocale::ShortFormat)) +
85 QString(" ") + date.toString("hh:mm");
86}
87
88QString dateTimeStr(qint64 nTime) {
89 return dateTimeStr(QDateTime::fromSecsSinceEpoch(nTime));
90}
91
93 return QFontDatabase::systemFont(QFontDatabase::FixedFont);
94}
95
96static std::string MakeAddrInvalid(std::string addr,
97 const CChainParams &params) {
98 if (addr.size() < 2) {
99 return "";
100 }
101
102 // Checksum is at the end of the address. Swapping chars to make it invalid.
103 std::swap(addr[addr.size() - 1], addr[addr.size() - 2]);
104 if (!IsValidDestinationString(addr, params)) {
105 return addr;
106 }
107
108 return "";
109}
110
111std::string DummyAddress(const CChainParams &params) {
112 // Just some dummy data to generate a convincing random-looking (but
113 // consistent) address
114 static const std::vector<uint8_t> dummydata = {
115 0xeb, 0x15, 0x23, 0x1d, 0xfc, 0xeb, 0x60, 0x92, 0x58, 0x86,
116 0xb6, 0x7d, 0x06, 0x52, 0x99, 0x92, 0x59, 0x15, 0xae, 0xb1};
117
118 const CTxDestination dstKey = PKHash(uint160(dummydata));
119 return MakeAddrInvalid(EncodeCashAddr(dstKey, params), params);
120}
121
122// Addresses are stored in the database with the encoding that the client was
123// configured with at the time of creation.
124//
125// This converts to cashaddr.
126QString convertToCashAddr(const CChainParams &params, const QString &addr) {
127 if (!IsValidDestinationString(addr.toStdString(), params)) {
128 // We have something sketchy as input. Do not try to convert.
129 return addr;
130 }
131 CTxDestination dst = DecodeDestination(addr.toStdString(), params);
132 return QString::fromStdString(EncodeCashAddr(dst, params));
133}
134
135void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent) {
136 parent->setFocusProxy(widget);
137
138 widget->setFont(fixedPitchFont());
139 // We don't want translators to use own addresses in translations
140 // and this is the only place, where this address is supplied.
141 widget->setPlaceholderText(
142 QObject::tr("Enter a Bitcoin address (e.g. %1)")
143 .arg(QString::fromStdString(DummyAddress(Params()))));
144 widget->setValidator(
145 new BitcoinAddressEntryValidator(Params().CashAddrPrefix(), parent));
147}
148
149bool parseBitcoinURI(const QString &scheme, const QUrl &uri,
150 SendCoinsRecipient *out) {
151 // return if URI has wrong scheme.
152 if (!uri.isValid() || uri.scheme() != scheme) {
153 return false;
154 }
155
157 rv.address = uri.scheme() + ":" + uri.path();
158
159 // Trim any following forward slash which may have been added by the OS
160 if (rv.address.endsWith("/")) {
161 rv.address.truncate(rv.address.length() - 1);
162 }
163 rv.amount = Amount::zero();
164
165 QUrlQuery uriQuery(uri);
166 QList<QPair<QString, QString>> items = uriQuery.queryItems();
167 for (QList<QPair<QString, QString>>::iterator i = items.begin();
168 i != items.end(); i++) {
169 bool fShouldReturnFalse = false;
170 if (i->first.startsWith("req-")) {
171 i->first.remove(0, 4);
172 fShouldReturnFalse = true;
173 }
174
175 if (i->first == "label") {
176 rv.label = i->second;
177 fShouldReturnFalse = false;
178 }
179 if (i->first == "message") {
180 rv.message = i->second;
181 fShouldReturnFalse = false;
182 } else if (i->first == "amount") {
183 if (!i->second.isEmpty()) {
185 &rv.amount)) {
186 return false;
187 }
188 }
189 fShouldReturnFalse = false;
190 }
191
192 if (fShouldReturnFalse) {
193 return false;
194 }
195 }
196 if (out) {
197 *out = rv;
198 }
199 return true;
200}
201
202bool parseBitcoinURI(const QString &scheme, QString uri,
203 SendCoinsRecipient *out) {
204 //
205 // Cannot handle this later, because bitcoincash://
206 // will cause Qt to see the part after // as host,
207 // which will lower-case it (and thus invalidate the address).
208 if (uri.startsWith(scheme + "://", Qt::CaseInsensitive)) {
209 uri.replace(0, scheme.length() + 3, scheme + ":");
210 }
211 QUrl uriInstance(uri);
212 return parseBitcoinURI(scheme, uriInstance, out);
213}
214
216 return formatBitcoinURI(Params(), info);
217}
218
219QString formatBitcoinURI(const CChainParams &params,
220 const SendCoinsRecipient &info) {
221 QString ret = convertToCashAddr(params, info.address);
222 int paramCount = 0;
223
224 if (info.amount != Amount::zero()) {
225 ret += QString("?amount=%1")
227 BitcoinUnits::base, info.amount, false,
229 paramCount++;
230 }
231
232 if (!info.label.isEmpty()) {
233 QString lbl(QUrl::toPercentEncoding(info.label));
234 ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl);
235 paramCount++;
236 }
237
238 if (!info.message.isEmpty()) {
239 QString msg(QUrl::toPercentEncoding(info.message));
240 ret +=
241 QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg);
242 paramCount++;
243 }
244
245 return ret;
246}
247
248bool isDust(interfaces::Node &node, const QString &address, const Amount amount,
249 const CChainParams &chainParams) {
250 CTxDestination dest = DecodeDestination(address.toStdString(), chainParams);
251 CScript script = GetScriptForDestination(dest);
252 CTxOut txOut(amount, script);
253 return IsDust(txOut, node.getDustRelayFee());
254}
255
256QString HtmlEscape(const QString &str, bool fMultiLine) {
257 QString escaped = str.toHtmlEscaped();
258 if (fMultiLine) {
259 escaped = escaped.replace("\n", "<br>\n");
260 }
261 return escaped;
262}
263
264QString HtmlEscape(const std::string &str, bool fMultiLine) {
265 return HtmlEscape(QString::fromStdString(str), fMultiLine);
266}
267
268void copyEntryData(const QAbstractItemView *view, int column, int role) {
269 if (!view || !view->selectionModel()) {
270 return;
271 }
272 QModelIndexList selection = view->selectionModel()->selectedRows(column);
273
274 if (!selection.isEmpty()) {
275 // Copy first item
276 setClipboard(selection.at(0).data(role).toString());
277 }
278}
279
280QList<QModelIndex> getEntryData(const QAbstractItemView *view, int column) {
281 if (!view || !view->selectionModel()) {
282 return QList<QModelIndex>();
283 }
284 return view->selectionModel()->selectedRows(column);
285}
286
287bool hasEntryData(const QAbstractItemView *view, int column, int role) {
288 QModelIndexList selection = getEntryData(view, column);
289 if (selection.isEmpty()) {
290 return false;
291 }
292 return !selection.at(0).data(role).toString().isEmpty();
293}
294
297}
298
299QString ExtractFirstSuffixFromFilter(const QString &filter) {
300 QRegularExpression filter_re(QStringLiteral(".* \\(\\*\\.(.*)[ \\)]"),
301 QRegularExpression::InvertedGreedinessOption);
302 QString suffix;
303 QRegularExpressionMatch m = filter_re.match(filter);
304 if (m.hasMatch()) {
305 suffix = m.captured(1);
306 }
307 return suffix;
308}
309
310QString getSaveFileName(QWidget *parent, const QString &caption,
311 const QString &dir, const QString &filter,
312 QString *selectedSuffixOut) {
313 QString selectedFilter;
314 QString myDir;
315 // Default to user documents location
316 if (dir.isEmpty()) {
317 myDir =
318 QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
319 } else {
320 myDir = dir;
321 }
322 /* Directly convert path to native OS path separators */
323 QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(
324 parent, caption, myDir, filter, &selectedFilter));
325
326 QString selectedSuffix = ExtractFirstSuffixFromFilter(selectedFilter);
327
328 /* Add suffix if needed */
329 QFileInfo info(result);
330 if (!result.isEmpty()) {
331 if (info.suffix().isEmpty() && !selectedSuffix.isEmpty()) {
332 /* No suffix specified, add selected suffix */
333 if (!result.endsWith(".")) {
334 result.append(".");
335 }
336 result.append(selectedSuffix);
337 }
338 }
339
340 /* Return selected suffix if asked to */
341 if (selectedSuffixOut) {
342 *selectedSuffixOut = selectedSuffix;
343 }
344 return result;
345}
346
347QString getOpenFileName(QWidget *parent, const QString &caption,
348 const QString &dir, const QString &filter,
349 QString *selectedSuffixOut) {
350 QString selectedFilter;
351 QString myDir;
352 // Default to user documents location
353 if (dir.isEmpty()) {
354 myDir =
355 QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
356 } else {
357 myDir = dir;
358 }
359 /* Directly convert path to native OS path separators */
360 QString result = QDir::toNativeSeparators(QFileDialog::getOpenFileName(
361 parent, caption, myDir, filter, &selectedFilter));
362
363 if (selectedSuffixOut) {
364 *selectedSuffixOut = ExtractFirstSuffixFromFilter(selectedFilter);
365 }
366 return result;
367}
368
370 if (QThread::currentThread() != qApp->thread()) {
371 return Qt::BlockingQueuedConnection;
372 } else {
373 return Qt::DirectConnection;
374 }
375}
376
377bool checkPoint(const QPoint &p, const QWidget *w) {
378 QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p));
379 if (!atW) {
380 return false;
381 }
382 return atW->window() == w;
383}
384
385bool isObscured(QWidget *w) {
386 return !(checkPoint(QPoint(0, 0), w) &&
387 checkPoint(QPoint(w->width() - 1, 0), w) &&
388 checkPoint(QPoint(0, w->height() - 1), w) &&
389 checkPoint(QPoint(w->width() - 1, w->height() - 1), w) &&
390 checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
391}
392
393void bringToFront(QWidget *w) {
394#ifdef Q_OS_MAC
396#endif
397
398 if (w) {
399 // activateWindow() (sometimes) helps with keyboard focus on Windows
400 if (w->isMinimized()) {
401 w->showNormal();
402 } else {
403 w->show();
404 }
405 w->activateWindow();
406 w->raise();
407 }
408}
409
411 QObject::connect(new QShortcut(QKeySequence(QObject::tr("Ctrl+W")), w),
412 &QShortcut::activated, w, &QWidget::close);
413}
414
416 fs::path pathDebug = gArgs.GetDataDirNet() / "debug.log";
417
418 /* Open debug.log with the associated application */
419 if (fs::exists(pathDebug)) {
420 QDesktopServices::openUrl(
421 QUrl::fromLocalFile(boostPathToQString(pathDebug)));
422 }
423}
424
426 fs::path pathConfig = gArgs.GetConfigFilePath();
427
428 /* Create the file */
429 std::ofstream configFile{pathConfig, std::ios_base::app};
430
431 if (!configFile.good()) {
432 return false;
433 }
434
435 configFile.close();
436
437 /* Open bitcoin.conf with the associated application */
438 bool res = QDesktopServices::openUrl(
439 QUrl::fromLocalFile(boostPathToQString(pathConfig)));
440#ifdef Q_OS_MAC
441 // Workaround for macOS-specific behavior; see #15409.
442 if (!res) {
443 res = QProcess::startDetached(
444 "/usr/bin/open", QStringList{"-t", boostPathToQString(pathConfig)});
445 }
446#endif
447
448 return res;
449}
450
451QStringList splitSkipEmptyParts(const QString &s, const QString &separator) {
452 return s.split(separator,
453#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
454 Qt::SkipEmptyParts
455#else
456 QString::SkipEmptyParts
457#endif
458 );
459}
460
462 QObject *parent)
463 : QObject(parent), size_threshold(_size_threshold) {}
464
465bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt) {
466 if (evt->type() == QEvent::ToolTipChange) {
467 QWidget *widget = static_cast<QWidget *>(obj);
468 QString tooltip = widget->toolTip();
469 if (tooltip.size() > size_threshold && !tooltip.startsWith("<qt") &&
470 !Qt::mightBeRichText(tooltip)) {
471 // Envelop with <qt></qt> to make sure Qt detects this as rich text
472 // Escape the current message as HTML and replace \n by <br>
473 tooltip = "<qt>" + HtmlEscape(tooltip, true) + "</qt>";
474 widget->setToolTip(tooltip);
475 return true;
476 }
477 }
478 return QObject::eventFilter(obj, evt);
479}
480
482 : QObject(parent) {}
483
484bool LabelOutOfFocusEventFilter::eventFilter(QObject *watched, QEvent *event) {
485 if (event->type() == QEvent::FocusOut) {
486 auto focus_out = static_cast<QFocusEvent *>(event);
487 if (focus_out->reason() != Qt::PopupFocusReason) {
488 auto label = qobject_cast<QLabel *>(watched);
489 if (label) {
490 auto flags = label->textInteractionFlags();
491 label->setTextInteractionFlags(Qt::NoTextInteraction);
492 label->setTextInteractionFlags(flags);
493 }
494 }
495 }
496
497 return QObject::eventFilter(watched, event);
498}
499
501 connect(tableView->horizontalHeader(), &QHeaderView::sectionResized, this,
503 connect(tableView->horizontalHeader(), &QHeaderView::geometriesChanged,
505}
506
507// We need to disconnect these while handling the resize events, otherwise we
508// can enter infinite loops.
510 disconnect(tableView->horizontalHeader(), &QHeaderView::sectionResized,
512 disconnect(tableView->horizontalHeader(), &QHeaderView::geometriesChanged,
514} // namespace GUIUtil
515
516// Setup the resize mode, handles compatibility for Qt5 and below as the method
517// signatures changed.
518// Refactored here for readability.
520 int logicalIndex, QHeaderView::ResizeMode resizeMode) {
521 tableView->horizontalHeader()->setSectionResizeMode(logicalIndex,
522 resizeMode);
523}
524
526 int width) {
527 tableView->setColumnWidth(nColumnIndex, width);
528 tableView->horizontalHeader()->resizeSection(nColumnIndex, width);
529}
530
532 int nColumnsWidthSum = 0;
533 for (int i = 0; i < columnCount; i++) {
534 nColumnsWidthSum += tableView->horizontalHeader()->sectionSize(i);
535 }
536 return nColumnsWidthSum;
537}
538
540 int nResult = lastColumnMinimumWidth;
541 int nTableWidth = tableView->horizontalHeader()->width();
542
543 if (nTableWidth > 0) {
544 int nOtherColsWidth =
546 tableView->horizontalHeader()->sectionSize(column);
547 nResult = std::max(nResult, nTableWidth - nOtherColsWidth);
548 }
549
550 return nResult;
551}
552
553// Make sure we don't make the columns wider than the table's viewport width.
558
559 int nTableWidth = tableView->horizontalHeader()->width();
560 int nColsWidth = getColumnsWidth();
561 if (nColsWidth > nTableWidth) {
564 }
565}
566
567// Make column use all the space available, useful during window resizing.
572}
573
574// When a section is resized this is a slot-proxy for ajustAmountColumnWidth().
576 int oldSize,
577 int newSize) {
579 int remainingWidth = getAvailableWidthForColumn(logicalIndex);
580 if (newSize > remainingWidth) {
581 resizeColumn(logicalIndex, remainingWidth);
582 }
583}
584
585// When the table's geometry is ready, we manually perform the stretch of the
586// "Message" column,
587// as the "Stretch" resize mode does not allow for interactive resizing.
589 if ((getColumnsWidth() - this->tableView->horizontalHeader()->width()) !=
590 0) {
595 }
596}
597
603 QTableView *table, int lastColMinimumWidth, int allColsMinimumWidth,
604 QObject *parent)
605 : QObject(parent), tableView(table),
606 lastColumnMinimumWidth(lastColMinimumWidth),
607 allColumnsMinimumWidth(allColsMinimumWidth) {
608 columnCount = tableView->horizontalHeader()->count();
611 tableView->horizontalHeader()->setMinimumSectionSize(
613 setViewHeaderResizeMode(secondToLastColumnIndex, QHeaderView::Interactive);
614 setViewHeaderResizeMode(lastColumnIndex, QHeaderView::Interactive);
615}
616
617#ifdef WIN32
618static fs::path StartupShortcutPath() {
619 ChainType chain = gArgs.GetChainType();
620 if (chain == ChainType::MAIN) {
621 return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk";
622 }
623 // Remove this special case when CBaseChainParams::DataDir() is incremented
624 // to "testnet4"
625 if (chain == ChainType::TESTNET) {
626 return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (testnet).lnk";
627 }
628 return GetSpecialFolderPath(CSIDL_STARTUP) /
629 strprintf("Bitcoin (%s).lnk", ChainTypeToString(chain));
630}
631
633 // check for Bitcoin*.lnk
634 return fs::exists(StartupShortcutPath());
635}
636
637bool SetStartOnSystemStartup(bool fAutoStart) {
638 // If the shortcut exists already, remove it for updating
639 fs::remove(StartupShortcutPath());
640
641 if (fAutoStart) {
642 CoInitialize(nullptr);
643
644 // Get a pointer to the IShellLink interface.
645 IShellLinkW *psl = nullptr;
646 HRESULT hres =
647 CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
648 IID_IShellLinkW, reinterpret_cast<void **>(&psl));
649
650 if (SUCCEEDED(hres)) {
651 // Get the current executable path
652 WCHAR pszExePath[MAX_PATH];
653 GetModuleFileNameW(nullptr, pszExePath, ARRAYSIZE(pszExePath));
654
655 // Start client minimized
656 QString strArgs = "-min";
657 // Set -testnet /-regtest options
658 strArgs += QString::fromStdString(
659 strprintf(" -chain=%s", gArgs.GetChainTypeString()));
660
661 // Set the path to the shortcut target
662 psl->SetPath(pszExePath);
663 PathRemoveFileSpecW(pszExePath);
664 psl->SetWorkingDirectory(pszExePath);
665 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
666 psl->SetArguments(strArgs.toStdWString().c_str());
667
668 // Query IShellLink for the IPersistFile interface for
669 // saving the shortcut in persistent storage.
670 IPersistFile *ppf = nullptr;
671 hres = psl->QueryInterface(IID_IPersistFile,
672 reinterpret_cast<void **>(&ppf));
673 if (SUCCEEDED(hres)) {
674 // Save the link by calling IPersistFile::Save.
675 hres = ppf->Save(StartupShortcutPath().wstring().c_str(), TRUE);
676 ppf->Release();
677 psl->Release();
678 CoUninitialize();
679 return true;
680 }
681 psl->Release();
682 }
683 CoUninitialize();
684 return false;
685 }
686 return true;
687}
688#elif defined(Q_OS_LINUX)
689
690// Follow the Desktop Application Autostart Spec:
691// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
692
693static fs::path GetAutostartDir() {
694 char *pszConfigHome = getenv("XDG_CONFIG_HOME");
695 if (pszConfigHome) {
696 return fs::path(pszConfigHome) / "autostart";
697 }
698 char *pszHome = getenv("HOME");
699 if (pszHome) {
700 return fs::path(pszHome) / ".config" / "autostart";
701 }
702 return fs::path();
703}
704
705static fs::path GetAutostartFilePath() {
706 ChainType chain = gArgs.GetChainType();
707 if (chain == ChainType::MAIN) {
708 return GetAutostartDir() / "bitcoin.desktop";
709 }
710 return GetAutostartDir() /
711 strprintf("bitcoin-%s.desktop", ChainTypeToString(chain));
712}
713
715 std::ifstream optionFile{GetAutostartFilePath()};
716 if (!optionFile.good()) {
717 return false;
718 }
719 // Scan through file for "Hidden=true":
720 std::string line;
721 while (!optionFile.eof()) {
722 getline(optionFile, line);
723 if (line.find("Hidden") != std::string::npos &&
724 line.find("true") != std::string::npos) {
725 return false;
726 }
727 }
728 optionFile.close();
729
730 return true;
731}
732
733bool SetStartOnSystemStartup(bool fAutoStart) {
734 if (!fAutoStart) {
735 fs::remove(GetAutostartFilePath());
736 } else {
737 char pszExePath[MAX_PATH + 1];
738 ssize_t r =
739 readlink("/proc/self/exe", pszExePath, sizeof(pszExePath) - 1);
740 if (r == -1) {
741 return false;
742 }
743 pszExePath[r] = '\0';
744
745 fs::create_directories(GetAutostartDir());
746
747 std::ofstream optionFile{GetAutostartFilePath(),
748 std::ios_base::out | std::ios_base::trunc};
749 if (!optionFile.good()) {
750 return false;
751 }
752 ChainType chain = gArgs.GetChainType();
753 // Write a bitcoin.desktop file to the autostart directory:
754 optionFile << "[Desktop Entry]\n";
755 optionFile << "Type=Application\n";
756 if (chain == ChainType::MAIN) {
757 optionFile << "Name=Bitcoin\n";
758 } else {
759 optionFile << strprintf("Name=Bitcoin (%s)\n",
760 ChainTypeToString(chain));
761 }
762 optionFile << "Exec=" << pszExePath
763 << strprintf(" -min -chain=%s\n", ChainTypeToString(chain));
764 optionFile << "Terminal=false\n";
765 optionFile << "Hidden=false\n";
766 optionFile.close();
767 }
768 return true;
769}
770
771#else
772
774 return false;
775}
776bool SetStartOnSystemStartup(bool fAutoStart) {
777 return false;
778}
779
780#endif
781
782void setClipboard(const QString &str) {
783 QApplication::clipboard()->setText(str, QClipboard::Clipboard);
784 QApplication::clipboard()->setText(str, QClipboard::Selection);
785}
786
787fs::path qstringToBoostPath(const QString &path) {
788 return fs::u8path(path.toStdString());
789}
790
791QString boostPathToQString(const fs::path &path) {
792 return QString::fromStdString(path.u8string());
793}
794
796 switch (net) {
797 case NET_UNROUTABLE:
798 return QObject::tr("Unroutable");
799 case NET_IPV4:
800 return "IPv4";
801 case NET_IPV6:
802 return "IPv6";
803 case NET_ONION:
804 return "Onion";
805 case NET_I2P:
806 return "I2P";
807 case NET_CJDNS:
808 return "CJDNS";
809 case NET_INTERNAL:
810 return QObject::tr("Internal");
811 case NET_MAX:
812 assert(false);
813 } // no default case, so the compiler can warn about missing cases
814 assert(false);
815}
816
817QString formatDurationStr(std::chrono::seconds dur) {
818 const auto secs = count_seconds(dur);
819 QStringList strList;
820 int days = secs / 86400;
821 int hours = (secs % 86400) / 3600;
822 int mins = (secs % 3600) / 60;
823 int seconds = secs % 60;
824
825 if (days) {
826 strList.append(QString(QObject::tr("%1 d")).arg(days));
827 }
828 if (hours) {
829 strList.append(QString(QObject::tr("%1 h")).arg(hours));
830 }
831 if (mins) {
832 strList.append(QString(QObject::tr("%1 m")).arg(mins));
833 }
834 if (seconds || (!days && !hours && !mins)) {
835 strList.append(QString(QObject::tr("%1 s")).arg(seconds));
836 }
837
838 return strList.join(" ");
839}
840
841QString formatServicesStr(quint64 mask) {
842 QStringList strList;
843
844 constexpr uint64_t nonExperimentalMask =
846 for (const auto &flag : serviceFlagsToStr(mask & nonExperimentalMask)) {
847 strList.append(QString::fromStdString(flag));
848 }
849
850 if (strList.size()) {
851 return strList.join(" & ");
852 } else {
853 return QObject::tr("None");
854 }
855}
856
857QString formatPingTime(std::chrono::microseconds ping_time) {
858 return (ping_time == std::chrono::microseconds::max() || ping_time == 0us)
859 ? QObject::tr("N/A")
860 : QString(QObject::tr("%1 ms"))
861 .arg(QString::number(
862 int(count_microseconds(ping_time) / 1000), 10));
863}
864
865QString formatTimeOffset(int64_t nTimeOffset) {
866 return QString(QObject::tr("%1 s"))
867 .arg(QString::number((int)nTimeOffset, 10));
868}
869
870QString formatNiceTimeOffset(qint64 secs) {
871 // Represent time from last generated block in human readable text
872 QString timeBehindText;
873 const int HOUR_IN_SECONDS = 60 * 60;
874 const int DAY_IN_SECONDS = 24 * 60 * 60;
875 const int WEEK_IN_SECONDS = 7 * 24 * 60 * 60;
876 // Average length of year in Gregorian calendar
877 const int YEAR_IN_SECONDS = 31556952;
878 if (secs < 60) {
879 timeBehindText = QObject::tr("%n second(s)", "", secs);
880 } else if (secs < 2 * HOUR_IN_SECONDS) {
881 timeBehindText = QObject::tr("%n minute(s)", "", secs / 60);
882 } else if (secs < 2 * DAY_IN_SECONDS) {
883 timeBehindText = QObject::tr("%n hour(s)", "", secs / HOUR_IN_SECONDS);
884 } else if (secs < 2 * WEEK_IN_SECONDS) {
885 timeBehindText = QObject::tr("%n day(s)", "", secs / DAY_IN_SECONDS);
886 } else if (secs < YEAR_IN_SECONDS) {
887 timeBehindText = QObject::tr("%n week(s)", "", secs / WEEK_IN_SECONDS);
888 } else {
889 qint64 years = secs / YEAR_IN_SECONDS;
890 qint64 remainder = secs % YEAR_IN_SECONDS;
891 timeBehindText = QObject::tr("%1 and %2")
892 .arg(QObject::tr("%n year(s)", "", years))
893 .arg(QObject::tr("%n week(s)", "",
894 remainder / WEEK_IN_SECONDS));
895 }
896 return timeBehindText;
897}
898
899QString formatBytes(uint64_t bytes) {
900 if (bytes < 1024) {
901 return QString(QObject::tr("%1 B")).arg(bytes);
902 }
903 if (bytes < 1024 * 1024) {
904 return QString(QObject::tr("%1 KB")).arg(bytes / 1024);
905 }
906 if (bytes < 1024 * 1024 * 1024) {
907 return QString(QObject::tr("%1 MB")).arg(bytes / 1024 / 1024);
908 }
909
910 return QString(QObject::tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024);
911}
912
914#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
915 return !pixmap(Qt::ReturnByValue).isNull();
916#else
917 return pixmap() != nullptr;
918#endif
919}
920
921qreal calculateIdealFontSize(int width, const QString &text, QFont font,
922 qreal minPointSize, qreal font_size) {
923 while (font_size >= minPointSize) {
924 font.setPointSizeF(font_size);
925 QFontMetrics fm(font);
926 if (GUIUtil::TextWidth(fm, text) < width) {
927 break;
928 }
929 font_size -= 0.5;
930 }
931 return font_size;
932}
933
934void ClickableLabel::mouseReleaseEvent(QMouseEvent *event) {
935 Q_EMIT clicked(event->pos());
936}
937
939 Q_EMIT clicked(event->pos());
940}
941
942bool ItemDelegate::eventFilter(QObject *object, QEvent *event) {
943 if (event->type() == QEvent::KeyPress) {
944 if (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Escape) {
945 Q_EMIT keyEscapePressed();
946 }
947 }
948 return QItemDelegate::eventFilter(object, event);
949}
950
951int TextWidth(const QFontMetrics &fm, const QString &text) {
952#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
953 return fm.horizontalAdvance(text);
954#else
955 return fm.width(text);
956#endif
957}
958
959void PolishProgressDialog(QProgressDialog *dialog) {
960#ifdef Q_OS_MAC
961 // Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357.
962 const int margin = GUIUtil::TextWidth(dialog->fontMetrics(), "X");
963 dialog->resize(dialog->width() + 2 * margin, dialog->height());
964 dialog->show();
965#else
966 Q_UNUSED(dialog);
967#endif
968}
969
970void LogQtInfo() {
971#ifdef QT_STATIC
972 const std::string qt_link{"static"};
973#else
974 const std::string qt_link{"dynamic"};
975#endif
976#ifdef QT_STATICPLUGIN
977 const std::string plugin_link{"static"};
978#else
979 const std::string plugin_link{"dynamic"};
980#endif
981 LogPrintf("Qt %s (%s), plugin=%s (%s)\n", qVersion(), qt_link,
982 QGuiApplication::platformName().toStdString(), plugin_link);
983 LogPrintf("System: %s, %s\n", QSysInfo::prettyProductName().toStdString(),
984 QSysInfo::buildAbi().toStdString());
985 for (const QScreen *s : QGuiApplication::screens()) {
986 LogPrintf("Screen: %s %dx%d, pixel ratio=%.1f\n",
987 s->name().toStdString(), s->size().width(),
988 s->size().height(), s->devicePixelRatio());
989 }
990}
991
992void PopupMenu(QMenu *menu, const QPoint &point, QAction *at_action) {
993 // The qminimal plugin does not provide window system integration.
994 if (QApplication::platformName() == "minimal") {
995 return;
996 }
997 menu->popup(point, at_action);
998}
999
1000void ShowModalDialogAsynchronously(QDialog *dialog) {
1001 dialog->setAttribute(Qt::WA_DeleteOnClose);
1002 dialog->setWindowModality(Qt::ApplicationModal);
1003 dialog->show();
1004}
1005
1006} // namespace GUIUtil
fs::path GetDefaultDataDir()
Definition: args.cpp:723
ArgsManager gArgs
Definition: args.cpp:40
int flags
Definition: bitcoin-tx.cpp:542
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams &params)
Definition: cashaddrenc.cpp:90
const CChainParams & Params()
Return the currently selected parameters.
Definition: chainparams.cpp:21
std::string ChainTypeToString(ChainType chain)
Definition: chaintype.cpp:11
ChainType
Definition: chaintype.h:11
ChainType GetChainType() const
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
Definition: args.cpp:757
std::string GetChainTypeString() const
Returns the appropriate chain name string from the program arguments.
Definition: args.cpp:766
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:217
fs::path GetConfigFilePath() const
Return config file path (read-only)
Definition: args.cpp:753
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.
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:86
An output of a transaction.
Definition: transaction.h:128
void mouseReleaseEvent(QMouseEvent *event) override
Definition: guiutil.cpp:934
bool hasPixmap() const
Definition: guiutil.cpp:913
void clicked(const QPoint &point)
Emitted when the label is clicked.
void mouseReleaseEvent(QMouseEvent *event) override
Definition: guiutil.cpp:938
void clicked(const QPoint &point)
Emitted when the progressbar is clicked.
bool eventFilter(QObject *object, QEvent *event) override
Definition: guiutil.cpp:942
bool eventFilter(QObject *watched, QEvent *event) override
Definition: guiutil.cpp:484
LabelOutOfFocusEventFilter(QObject *parent)
Definition: guiutil.cpp:481
void setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode)
Definition: guiutil.cpp:519
void resizeColumn(int nColumnIndex, int width)
Definition: guiutil.cpp:525
void on_sectionResized(int logicalIndex, int oldSize, int newSize)
Definition: guiutil.cpp:575
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...
Definition: guiutil.cpp:602
bool eventFilter(QObject *obj, QEvent *evt) override
Definition: guiutil.cpp:465
ToolTipToRichTextFilter(int size_threshold, QObject *parent=0)
Definition: guiutil.cpp:461
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:...
Definition: fs.h:30
std::string u8string() const
Definition: fs.h:72
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:59
160-bit opaque blob.
Definition: uint256.h:117
#define MAX_PATH
Definition: compat.h:70
ConnectionType
Different types of connections to a peer.
bool IsValidDestinationString(const std::string &str, const CChainParams &params)
Definition: key_io.cpp:183
CTxDestination DecodeDestination(const std::string &addr, const CChainParams &params)
Definition: key_io.cpp:174
#define LogPrintf(...)
Definition: logging.h:227
void ForceActivation()
Force application activation on macOS.
Utility functions used by the Bitcoin Qt UI.
Definition: bitcoingui.h:58
QString NetworkToQString(Network net)
Convert enum Network to QString.
Definition: guiutil.cpp:795
fs::path qstringToBoostPath(const QString &path)
Convert QString to OS specific boost path through UTF-8.
Definition: guiutil.cpp:787
bool isObscured(QWidget *w)
Definition: guiutil.cpp:385
bool parseBitcoinURI(const QString &scheme, const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:149
bool openBitcoinConf()
Definition: guiutil.cpp:425
bool isDust(interfaces::Node &node, const QString &address, const Amount amount, const CChainParams &chainParams)
Definition: guiutil.cpp:248
Qt::ConnectionType blockingGUIThreadConnection()
Get connection type to call object slot in GUI thread with invokeMethod.
Definition: guiutil.cpp:369
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:256
void PopupMenu(QMenu *menu, const QPoint &point, QAction *at_action)
Call QMenu::popup() only on supported QT_QPA_PLATFORM.
Definition: guiutil.cpp:992
QList< QModelIndex > getEntryData(const QAbstractItemView *view, int column)
Return a field of the currently selected entry as a QString.
Definition: guiutil.cpp:280
QString formatBytes(uint64_t bytes)
Definition: guiutil.cpp:899
void ShowModalDialogAsynchronously(QDialog *dialog)
Shows a QDialog instance asynchronously, and deletes it on close.
Definition: guiutil.cpp:1000
QString formatDurationStr(std::chrono::seconds dur)
Convert seconds into a QString with days, hours, mins, secs.
Definition: guiutil.cpp:817
void handleCloseWindowShortcut(QWidget *w)
Definition: guiutil.cpp:410
QString ExtractFirstSuffixFromFilter(const QString &filter)
Extract first suffix from filter pattern "Description (*.foo)" or "Description (*....
Definition: guiutil.cpp:299
void PolishProgressDialog(QProgressDialog *dialog)
Definition: guiutil.cpp:959
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut)
Get open filename, convenience wrapper for QFileDialog::getOpenFileName.
Definition: guiutil.cpp:347
std::string DummyAddress(const CChainParams &params)
Definition: guiutil.cpp:111
static std::string MakeAddrInvalid(std::string addr, const CChainParams &params)
Definition: guiutil.cpp:96
QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: guiutil.cpp:295
void copyEntryData(const QAbstractItemView *view, int column, int role)
Copy a field of the currently selected entry of a view to the clipboard.
Definition: guiutil.cpp:268
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 ...
Definition: guiutil.cpp:310
QString boostPathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
Definition: guiutil.cpp:791
bool SetStartOnSystemStartup(bool fAutoStart)
Definition: guiutil.cpp:776
void bringToFront(QWidget *w)
Definition: guiutil.cpp:393
void LogQtInfo()
Writes to debug.log short info about the used Qt and the host system.
Definition: guiutil.cpp:970
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.
Definition: guiutil.cpp:857
void openDebugLogfile()
Definition: guiutil.cpp:415
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:83
bool checkPoint(const QPoint &p, const QWidget *w)
Definition: guiutil.cpp:377
QString formatBitcoinURI(const SendCoinsRecipient &info)
Definition: guiutil.cpp:215
QString formatTimeOffset(int64_t nTimeOffset)
Format a CNodeCombinedStats.nTimeOffset into a user-readable string.
Definition: guiutil.cpp:865
QString convertToCashAddr(const CChainParams &params, const QString &addr)
Definition: guiutil.cpp:126
QString formatServicesStr(quint64 mask)
Format CNodeStats.nServices bitmask into a user-readable string.
Definition: guiutil.cpp:841
QString formatNiceTimeOffset(qint64 secs)
Definition: guiutil.cpp:870
bool GetStartOnSystemStartup()
Definition: guiutil.cpp:773
QStringList splitSkipEmptyParts(const QString &s, const QString &separator)
Definition: guiutil.cpp:451
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text.
Definition: guiutil.cpp:951
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
Definition: guiutil.cpp:135
void setClipboard(const QString &str)
Definition: guiutil.cpp:782
bool hasEntryData(const QAbstractItemView *view, int column, int role)
Returns true if the specified field of the currently selected view entry is not empty.
Definition: guiutil.cpp:287
QFont fixedPitchFont()
Definition: guiutil.cpp:92
qreal calculateIdealFontSize(int width, const QString &text, QFont font, qreal minPointSize, qreal font_size)
Definition: guiutil.cpp:921
static path u8path(const std::string &utf8_str)
Definition: fs.h:90
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...
Definition: fs.h:179
static bool exists(const path &p)
Definition: fs.h:102
Definition: init.h:31
Network
A network type.
Definition: netaddress.h:44
@ NET_I2P
I2P.
Definition: netaddress.h:59
@ NET_CJDNS
CJDNS.
Definition: netaddress.h:62
@ NET_MAX
Dummy value to indicate the number of NET_* constants.
Definition: netaddress.h:69
@ NET_ONION
TOR (v2 or v3)
Definition: netaddress.h:56
@ NET_IPV6
IPv6.
Definition: netaddress.h:53
@ NET_IPV4
IPv4.
Definition: netaddress.h:50
@ NET_UNROUTABLE
Addresses from these networks are not publicly routable on the global Internet.
Definition: netaddress.h:47
@ NET_INTERNAL
A set of addresses that represent the hash of a string or FQDN.
Definition: netaddress.h:66
@ SUCCEEDED
Succeeded.
Definition: netbase.cpp:295
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
Definition: policy.cpp:34
std::vector< std::string > serviceFlagsToStr(const uint64_t flags)
Convert service flags (a bitmask of NODE_*) to human readable strings.
Definition: protocol.cpp:284
@ NODE_LAST_NON_EXPERIMENTAL_SERVICE_BIT
Definition: protocol.h:368
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:240
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:85
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32
constexpr int64_t count_microseconds(std::chrono::microseconds t)
Definition: time.h:61
constexpr int64_t count_seconds(std::chrono::seconds t)
Definition: time.h:55
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
assert(!tx.IsCoinBase())