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