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 <common/args.h>
10 #include <interfaces/node.h>
11 #include <key_io.h>
12 #include <logging.h>
13 #include <policy/policy.h>
14 #include <primitives/transaction.h>
15 #include <protocol.h>
17 #include <qt/bitcoinunits.h>
18 #include <qt/qvalidatedlineedit.h>
19 #include <qt/sendcoinsrecipient.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 
73 void ForceActivation();
74 #endif
75 
76 namespace GUIUtil {
77 
78 QString dateTimeStr(const QDateTime &date) {
79  return date.date().toString(QLocale().dateFormat(QLocale::ShortFormat)) +
80  QString(" ") + date.toString("hh:mm");
81 }
82 
83 QString dateTimeStr(qint64 nTime) {
84  return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
85 }
86 
87 QFont fixedPitchFont() {
88  return QFontDatabase::systemFont(QFontDatabase::FixedFont);
89 }
90 
91 static 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 
106 std::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.
121 QString 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 
130 void 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));
141  widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
142 }
143 
144 bool 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()) {
179  if (!BitcoinUnits::parse(BitcoinUnits::base, i->second,
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 
197 bool 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 
210 QString formatBitcoinURI(const SendCoinsRecipient &info) {
211  return formatBitcoinURI(Params(), info);
212 }
213 
214 QString 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 
243 bool 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 
251 QString 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 
259 QString HtmlEscape(const std::string &str, bool fMultiLine) {
260  return HtmlEscape(QString::fromStdString(str), fMultiLine);
261 }
262 
263 void 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 
275 QList<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 
282 bool 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 
294 QString 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 
337 QString 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 
374 bool 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 
382 bool 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 
390 void bringToFront(QWidget *w) {
391 #ifdef Q_OS_MAC
392  ForceActivation();
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 
407 void handleCloseWindowShortcut(QWidget *w) {
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 
448 QStringList 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 
462 bool 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 
481 bool 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 =
542  getColumnsWidth() -
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.
567  resizeColumn(column, getAvailableWidthForColumn(column));
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
615 static 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 
633 bool 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 
689 static 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 
701 static 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 
728 bool 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 }
770 bool SetStartOnSystemStartup(bool fAutoStart) {
771  return false;
772 }
773 
774 #endif
775 
776 void setClipboard(const QString &str) {
777  QApplication::clipboard()->setText(str, QClipboard::Clipboard);
778  QApplication::clipboard()->setText(str, QClipboard::Selection);
779 }
780 
781 fs::path qstringToBoostPath(const QString &path) {
782  return fs::u8path(path.toStdString());
783 }
784 
785 QString boostPathToQString(const fs::path &path) {
786  return QString::fromStdString(path.u8string());
787 }
788 
789 QString NetworkToQString(Network net) {
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 
811 QString 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 
835 QString 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 
851 QString 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 
859 QString formatTimeOffset(int64_t nTimeOffset) {
860  return QString(QObject::tr("%1 s"))
861  .arg(QString::number((int)nTimeOffset, 10));
862 }
863 
864 QString 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 
893 QString 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 
915 qreal 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 
928 void ClickableLabel::mouseReleaseEvent(QMouseEvent *event) {
929  Q_EMIT clicked(event->pos());
930 }
931 
932 void ClickableProgressBar::mouseReleaseEvent(QMouseEvent *event) {
933  Q_EMIT clicked(event->pos());
934 }
935 
936 bool 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 
945 int 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 
953 void 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 
964 void 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 
986 void 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:543
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
const 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
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:431
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:58
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:151
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: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: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())