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