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