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