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