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