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