Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

guiutil.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Copyright (c) 2014-2020 The Dash Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <qt/guiutil.h>
7 
8 #include <qt/appearancewidget.h>
10 #include <qt/bitcoingui.h>
11 #include <qt/bitcoinunits.h>
12 #include <qt/optionsdialog.h>
13 #include <qt/qvalidatedlineedit.h>
14 #include <qt/walletmodel.h>
15 
16 #include <primitives/transaction.h>
17 #include <init.h>
18 #include <policy/policy.h>
19 #include <protocol.h>
20 #include <script/script.h>
21 #include <script/standard.h>
22 #include <ui_interface.h>
23 #include <util.h>
24 
25 #ifdef WIN32
26 #ifdef _WIN32_WINNT
27 #undef _WIN32_WINNT
28 #endif
29 #define _WIN32_WINNT 0x0501
30 #ifdef _WIN32_IE
31 #undef _WIN32_IE
32 #endif
33 #define _WIN32_IE 0x0501
34 #define WIN32_LEAN_AND_MEAN 1
35 #ifndef NOMINMAX
36 #define NOMINMAX
37 #endif
38 #include <shellapi.h>
39 #include <shlobj.h>
40 #include <shlwapi.h>
41 #endif
42 
43 #include <boost/scoped_array.hpp>
44 
45 #include <QAbstractButton>
46 #include <QAbstractItemView>
47 #include <QApplication>
48 #include <QClipboard>
49 #include <QDateTime>
50 #include <QDebug>
51 #include <QDesktopServices>
52 #include <QDesktopWidget>
53 #include <QDialogButtonBox>
54 #include <QDoubleValidator>
55 #include <QFileDialog>
56 #include <QFont>
57 #include <QLineEdit>
58 #include <QSettings>
59 #include <QTextDocument> // for Qt::mightBeRichText
60 #include <QThread>
61 #include <QTimer>
62 #include <QMouseEvent>
63 #include <QVBoxLayout>
64 
65 #if QT_VERSION < 0x050000
66 #include <QUrl>
67 #else
68 #include <QUrlQuery>
69 #endif
70 
71 #if QT_VERSION >= 0x50200
72 #include <QFontDatabase>
73 #endif
74 
75 static fs::detail::utf8_codecvt_facet utf8;
76 
77 #if defined(Q_OS_MAC)
78 extern double NSAppKitVersionNumber;
79 #if !defined(NSAppKitVersionNumber10_8)
80 #define NSAppKitVersionNumber10_8 1187
81 #endif
82 #if !defined(NSAppKitVersionNumber10_9)
83 #define NSAppKitVersionNumber10_9 1265
84 #endif
85 
86 #pragma GCC diagnostic push
87 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
88 
89 #include <CoreServices/CoreServices.h>
90 #include <QProcess>
91 
92 void ForceActivation();
93 #endif
94 
95 namespace GUIUtil {
96 
98 // The default stylesheet directory
99 static const QString defaultStylesheetDirectory = ":css";
100 // The actual stylesheet directory
102 // The name of the traditional theme
103 static const QString traditionalTheme = "Traditional";
104 // The theme to set by default if settings are missing or incorrect
105 static const QString defaultTheme = "Light";
106 // The prefix a theme name should have if we want to apply dark colors and styles to it
107 static const QString darkThemePrefix = "Dark";
108 // Mapping css file => theme.
109 static const std::map<QString, QString> mapStyleToTheme{
110  {"general.css", ""},
111  {"dark.css", "Dark"},
112  {"light.css", "Light"},
113  {"traditional.css", "Traditional"}
114 };
115 
117 static std::unique_ptr<QFont> osDefaultFont;
120 static const int defaultFontSize = 12;
121 static const double fontScaleSteps = 0.01;
122 #ifdef Q_OS_MAC
123 static const QFont::Weight defaultFontWeightNormal = QFont::ExtraLight;
124 static const QFont::Weight defaultFontWeightBold = QFont::Medium;
125 static const int defaultFontScale = 0;
126 #else
127 static const QFont::Weight defaultFontWeightNormal = QFont::Light;
128 static const QFont::Weight defaultFontWeightBold = QFont::Medium;
129 static const int defaultFontScale = 0;
130 #endif
131 
133 // Application font family. May be overwritten by -font-family.
135 // Application font scale value. May be overwritten by -font-scale.
137 // Application font weight for normal text. May be overwritten by -font-weight-normal.
139 // Application font weight for bold text. May be overwritten by -font-weight-bold.
140 static QFont::Weight fontWeightBold = defaultFontWeightBold;
141 
142 // Contains all widgets and its font attributes (weight, italic, size) with font changes due to GUIUtil::setFont
143 static std::map<QWidget*, std::tuple<FontWeight, bool, int>> mapNormalFontUpdates;
144 // Contains a list of supported font weights for all members of GUIUtil::FontFamily
145 static std::map<FontFamily, std::vector<QFont::Weight>> mapSupportedWeights;
146 
147 #ifdef Q_OS_MAC
148 // Contains all widgets where the macOS focus rect has been disabled.
149 static std::set<QWidget*> setRectsDisabled;
150 #endif
151 
152 static const std::map<ThemedColor, QColor> themedColors = {
153  { ThemedColor::DEFAULT, QColor(85, 85, 85) },
154  { ThemedColor::UNCONFIRMED, QColor(128, 128, 128) },
155  { ThemedColor::BLUE, QColor(0, 141, 228) },
156  { ThemedColor::ORANGE, QColor(199, 147, 4) },
157  { ThemedColor::RED, QColor(168, 72, 50) },
158  { ThemedColor::GREEN, QColor(94, 140, 65) },
159  { ThemedColor::BAREADDRESS, QColor(140, 140, 140) },
160  { ThemedColor::TX_STATUS_OPENUNTILDATE, QColor(64, 64, 255) },
161  { ThemedColor::BACKGROUND_WIDGET, QColor(234, 234, 236) },
162  { ThemedColor::BORDER_WIDGET, QColor(220, 220, 220) },
163  { ThemedColor::BACKGROUND_NETSTATS, QColor(210, 210, 210, 230) },
164  { ThemedColor::BORDER_NETSTATS, QColor(180, 180, 180) },
165  { ThemedColor::QR_PIXEL, QColor(85, 85, 85) },
166  { ThemedColor::ICON_ALTERNATIVE_COLOR, QColor(167, 167, 167) },
167 };
168 
169 static const std::map<ThemedColor, QColor> themedDarkColors = {
170  { ThemedColor::DEFAULT, QColor(199, 199, 199) },
171  { ThemedColor::UNCONFIRMED, QColor(170, 170, 170) },
172  { ThemedColor::BLUE, QColor(0, 89, 154) },
173  { ThemedColor::ORANGE, QColor(199, 147, 4) },
174  { ThemedColor::RED, QColor(168, 72, 50) },
175  { ThemedColor::GREEN, QColor(94, 140, 65) },
176  { ThemedColor::BAREADDRESS, QColor(140, 140, 140) },
177  { ThemedColor::TX_STATUS_OPENUNTILDATE, QColor(64, 64, 255) },
178  { ThemedColor::BACKGROUND_WIDGET, QColor(45, 45, 46) },
179  { ThemedColor::BORDER_WIDGET, QColor(74, 74, 75) },
180  { ThemedColor::BACKGROUND_NETSTATS, QColor(45, 45, 46, 230) },
181  { ThemedColor::BORDER_NETSTATS, QColor(74, 74, 75) },
182  { ThemedColor::QR_PIXEL, QColor(199, 199, 199) },
183  { ThemedColor::ICON_ALTERNATIVE_COLOR, QColor(74, 74, 75) },
184 };
185 
186 static const std::map<ThemedStyle, QString> themedStyles = {
187  { ThemedStyle::TS_INVALID, "background:#a84832;" },
188  { ThemedStyle::TS_ERROR, "color:#a84832;" },
189  { ThemedStyle::TS_SUCCESS, "color:#5e8c41;" },
190  { ThemedStyle::TS_COMMAND, "color:#008de4;" },
191  { ThemedStyle::TS_PRIMARY, "color:#333;" },
192  { ThemedStyle::TS_SECONDARY, "color:#444;" },
193 };
194 
195 static const std::map<ThemedStyle, QString> themedDarkStyles = {
196  { ThemedStyle::TS_INVALID, "background:#a84832;" },
197  { ThemedStyle::TS_ERROR, "color:#a84832;" },
198  { ThemedStyle::TS_SUCCESS, "color:#5e8c41;" },
199  { ThemedStyle::TS_COMMAND, "color:#00599a;" },
200  { ThemedStyle::TS_PRIMARY, "color:#c7c7c7;" },
201  { ThemedStyle::TS_SECONDARY, "color:#aaa;" },
202 };
203 
205 {
206  QString theme = QSettings().value("theme", "").toString();
207  return theme.startsWith(darkThemePrefix) ? themedDarkColors.at(color) : themedColors.at(color);
208 }
209 
211 {
212  QString theme = QSettings().value("theme", "").toString();
213  return theme.startsWith(darkThemePrefix) ? themedDarkStyles.at(style) : themedStyles.at(style);
214 }
215 
216 QIcon getIcon(const QString& strIcon, const ThemedColor color, const ThemedColor colorAlternative, const QString& strIconPath)
217 {
218  QColor qcolor = getThemedQColor(color);
219  QColor qcolorAlternative = getThemedQColor(colorAlternative);
220  QIcon icon(strIconPath + strIcon);
221  QIcon themedIcon;
222  for (const QSize& size : icon.availableSizes()) {
223  QImage image(icon.pixmap(size).toImage());
224  image = image.convertToFormat(QImage::Format_ARGB32);
225  for (int x = 0; x < image.width(); ++x) {
226  for (int y = 0; y < image.height(); ++y) {
227  const QRgb rgb = image.pixel(x, y);
228  QColor* pColor;
229  if ((rgb & RGB_MASK) < RGB_HALF) {
230  pColor = &qcolor;
231  } else {
232  pColor = &qcolorAlternative;
233  }
234  image.setPixel(x, y, qRgba(pColor->red(), pColor->green(), pColor->blue(), qAlpha(rgb)));
235  }
236  }
237  themedIcon.addPixmap(QPixmap::fromImage(image));
238  }
239  return themedIcon;
240 }
241 
242 QIcon getIcon(const QString& strIcon, const ThemedColor color, const QString& strIconPath)
243 {
244  return getIcon(strIcon, color, ThemedColor::ICON_ALTERNATIVE_COLOR, strIconPath);
245 }
246 
247 void setIcon(QAbstractButton* button, const QString& strIcon, const ThemedColor color, const ThemedColor colorAlternative, const QSize& size)
248 {
249  button->setIcon(getIcon(strIcon, color, colorAlternative));
250  button->setIconSize(size);
251 }
252 
253 void setIcon(QAbstractButton* button, const QString& strIcon, const ThemedColor color, const QSize& size)
254 {
255  setIcon(button, strIcon, color, ThemedColor::ICON_ALTERNATIVE_COLOR, size);
256 }
257 
258 QString dateTimeStr(const QDateTime &date)
259 {
260  return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
261 }
262 
263 QString dateTimeStr(qint64 nTime)
264 {
265  return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
266 }
267 
268 // Just some dummy data to generate an convincing random-looking (but consistent) address
269 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};
270 
271 // Generate a dummy address with invalid CRC, starting with the network prefix.
272 static std::string DummyAddress(const CChainParams &params)
273 {
274  std::vector<unsigned char> sourcedata = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
275  sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
276  for(int i=0; i<256; ++i) { // Try every trailing byte
277  std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size());
278  if (!IsValidDestinationString(s)) {
279  return s;
280  }
281  sourcedata[sourcedata.size()-1] += 1;
282  }
283  return "";
284 }
285 
286 void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent, bool fAllowURI)
287 {
288  parent->setFocusProxy(widget);
289 
290 #if QT_VERSION >= 0x040700
291  // We don't want translators to use own addresses in translations
292  // and this is the only place, where this address is supplied.
293  widget->setPlaceholderText(QObject::tr("Enter a Dash address (e.g. %1)").arg(
294  QString::fromStdString(DummyAddress(Params()))));
295 #endif
296  widget->setValidator(new BitcoinAddressEntryValidator(parent, fAllowURI));
297  widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
298 }
299 
300 void setupAmountWidget(QLineEdit *widget, QWidget *parent)
301 {
302  QDoubleValidator *amountValidator = new QDoubleValidator(parent);
303  amountValidator->setDecimals(8);
304  amountValidator->setBottom(0.0);
305  widget->setValidator(amountValidator);
306  widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
307 }
308 
309 void setupAppearance(QWidget* parent, OptionsModel* model)
310 {
311  if (!QSettings().value("fAppearanceSetupDone", false).toBool()) {
312  // First make sure SystemDefault has reasonable default values if it does not support the full range of weights.
316  QSettings().setValue("fontWeightNormal", weightToArg(fontWeightNormal));
317  QSettings().setValue("fontWeightBold", weightToArg(fontWeightBold));
318  }
319  // Create the dialog
320  QDialog dlg(parent);
321  dlg.setObjectName("AppearanceSetup");
322  dlg.setWindowTitle(QObject::tr("Appearance Setup"));
323  dlg.setWindowIcon(QIcon(":icons/bitcoin"));
324  // And the widgets we add to it
325  QLabel lblHeading(QObject::tr("Please choose your preferred settings for the appearance of %1").arg(QObject::tr(PACKAGE_NAME)), &dlg);
326  lblHeading.setObjectName("lblHeading");
327  lblHeading.setWordWrap(true);
328  QLabel lblSubHeading(QObject::tr("This can also be adjusted later in the \"Appearance\" tab of the preferences."), &dlg);
329  lblSubHeading.setObjectName("lblSubHeading");
330  lblSubHeading.setWordWrap(true);
331  AppearanceWidget appearance(&dlg);
332  appearance.setModel(model);
333  QFrame line(&dlg);
334  line.setFrameShape(QFrame::HLine);
335  QDialogButtonBox buttonBox(QDialogButtonBox::Save);
336  // Put them into a vbox and add the vbox to the dialog
337  QVBoxLayout layout;
338  layout.addWidget(&lblHeading);
339  layout.addWidget(&lblSubHeading);
340  layout.addWidget(&line);
341  layout.addWidget(&appearance);
342  layout.addWidget(&buttonBox);
343  dlg.setLayout(&layout);
344  // Adjust the headings
345  setFont({&lblHeading}, FontWeight::Bold, 16);
346  setFont({&lblSubHeading}, FontWeight::Normal, 14, true);
347  // Make sure the dialog closes and accepts the settings if save has been pressed
348  QObject::connect(&buttonBox, &QDialogButtonBox::accepted, [&]() {
349  QSettings().setValue("fAppearanceSetupDone", true);
350  appearance.accept();
351  dlg.accept();
352  });
353  // And fire it!
354  dlg.exec();
355  }
356 }
357 
358 bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
359 {
360  // return if URI is not valid or is no dash: URI
361  if(!uri.isValid() || uri.scheme() != QString("dash"))
362  return false;
363 
365  rv.address = uri.path();
366  // Trim any following forward slash which may have been added by the OS
367  if (rv.address.endsWith("/")) {
368  rv.address.truncate(rv.address.length() - 1);
369  }
370  rv.amount = 0;
371 
372 #if QT_VERSION < 0x050000
373  QList<QPair<QString, QString> > items = uri.queryItems();
374 #else
375  QUrlQuery uriQuery(uri);
376  QList<QPair<QString, QString> > items = uriQuery.queryItems();
377 #endif
378 
379  for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
380  {
381  bool fShouldReturnFalse = false;
382  if (i->first.startsWith("req-"))
383  {
384  i->first.remove(0, 4);
385  fShouldReturnFalse = true;
386  }
387 
388  if (i->first == "label")
389  {
390  rv.label = i->second;
391  fShouldReturnFalse = false;
392  }
393  if (i->first == "IS")
394  {
395  // we simply ignore IS
396  fShouldReturnFalse = false;
397  }
398  if (i->first == "message")
399  {
400  rv.message = i->second;
401  fShouldReturnFalse = false;
402  }
403  else if (i->first == "amount")
404  {
405  if(!i->second.isEmpty())
406  {
407  if(!BitcoinUnits::parse(BitcoinUnits::DASH, i->second, &rv.amount))
408  {
409  return false;
410  }
411  }
412  fShouldReturnFalse = false;
413  }
414 
415  if (fShouldReturnFalse)
416  return false;
417  }
418  if(out)
419  {
420  *out = rv;
421  }
422  return true;
423 }
424 
425 bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
426 {
427  // Convert dash:// to dash:
428  //
429  // Cannot handle this later, because dash:// will cause Qt to see the part after // as host,
430  // which will lower-case it (and thus invalidate the address).
431  if(uri.startsWith("dash://", Qt::CaseInsensitive))
432  {
433  uri.replace(0, 7, "dash:");
434  }
435  QUrl uriInstance(uri);
436  return parseBitcoinURI(uriInstance, out);
437 }
438 
439 bool validateBitcoinURI(const QString& uri)
440 {
441  SendCoinsRecipient rcp;
442  return parseBitcoinURI(uri, &rcp);
443 }
444 
446 {
447  QString ret = QString("dash:%1").arg(info.address);
448  int paramCount = 0;
449 
450  if (info.amount)
451  {
452  ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::DASH, info.amount, false, BitcoinUnits::separatorNever));
453  paramCount++;
454  }
455 
456  if (!info.label.isEmpty())
457  {
458  QString lbl(QUrl::toPercentEncoding(info.label));
459  ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl);
460  paramCount++;
461  }
462 
463  if (!info.message.isEmpty())
464  {
465  QString msg(QUrl::toPercentEncoding(info.message));
466  ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg);
467  paramCount++;
468  }
469 
470  return ret;
471 }
472 
473 bool isDust(const QString& address, const CAmount& amount)
474 {
475  CTxDestination dest = DecodeDestination(address.toStdString());
476  CScript script = GetScriptForDestination(dest);
477  CTxOut txOut(amount, script);
478  return IsDust(txOut, ::dustRelayFee);
479 }
480 
481 QString HtmlEscape(const QString& str, bool fMultiLine)
482 {
483 #if QT_VERSION < 0x050000
484  QString escaped = Qt::escape(str);
485 #else
486  QString escaped = str.toHtmlEscaped();
487 #endif
488  escaped = escaped.replace(" ", "&nbsp;");
489  if(fMultiLine)
490  {
491  escaped = escaped.replace("\n", "<br>\n");
492  }
493  return escaped;
494 }
495 
496 QString HtmlEscape(const std::string& str, bool fMultiLine)
497 {
498  return HtmlEscape(QString::fromStdString(str), fMultiLine);
499 }
500 
501 void copyEntryData(QAbstractItemView *view, int column, int role)
502 {
503  if(!view || !view->selectionModel())
504  return;
505  QModelIndexList selection = view->selectionModel()->selectedRows(column);
506 
507  if(!selection.isEmpty())
508  {
509  // Copy first item
510  setClipboard(selection.at(0).data(role).toString());
511  }
512 }
513 
514 QList<QModelIndex> getEntryData(QAbstractItemView *view, int column)
515 {
516  if(!view || !view->selectionModel())
517  return QList<QModelIndex>();
518  return view->selectionModel()->selectedRows(column);
519 }
520 
521 QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
522  const QString &filter,
523  QString *selectedSuffixOut)
524 {
525  QString selectedFilter;
526  QString myDir;
527  if(dir.isEmpty()) // Default to user documents location
528  {
529 #if QT_VERSION < 0x050000
530  myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
531 #else
532  myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
533 #endif
534  }
535  else
536  {
537  myDir = dir;
538  }
539  /* Directly convert path to native OS path separators */
540  QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter));
541 
542  /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
543  QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
544  QString selectedSuffix;
545  if(filter_re.exactMatch(selectedFilter))
546  {
547  selectedSuffix = filter_re.cap(1);
548  }
549 
550  /* Add suffix if needed */
551  QFileInfo info(result);
552  if(!result.isEmpty())
553  {
554  if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
555  {
556  /* No suffix specified, add selected suffix */
557  if(!result.endsWith("."))
558  result.append(".");
559  result.append(selectedSuffix);
560  }
561  }
562 
563  /* Return selected suffix if asked to */
564  if(selectedSuffixOut)
565  {
566  *selectedSuffixOut = selectedSuffix;
567  }
568  return result;
569 }
570 
571 QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir,
572  const QString &filter,
573  QString *selectedSuffixOut)
574 {
575  QString selectedFilter;
576  QString myDir;
577  if(dir.isEmpty()) // Default to user documents location
578  {
579 #if QT_VERSION < 0x050000
580  myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
581 #else
582  myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
583 #endif
584  }
585  else
586  {
587  myDir = dir;
588  }
589  /* Directly convert path to native OS path separators */
590  QString result = QDir::toNativeSeparators(QFileDialog::getOpenFileName(parent, caption, myDir, filter, &selectedFilter));
591 
592  if(selectedSuffixOut)
593  {
594  /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
595  QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
596  QString selectedSuffix;
597  if(filter_re.exactMatch(selectedFilter))
598  {
599  selectedSuffix = filter_re.cap(1);
600  }
601  *selectedSuffixOut = selectedSuffix;
602  }
603  return result;
604 }
605 
606 Qt::ConnectionType blockingGUIThreadConnection()
607 {
608  if(QThread::currentThread() != qApp->thread())
609  {
610  return Qt::BlockingQueuedConnection;
611  }
612  else
613  {
614  return Qt::DirectConnection;
615  }
616 }
617 
618 bool checkPoint(const QPoint &p, const QWidget *w)
619 {
620  QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p));
621  if (!atW) return false;
622  return atW->topLevelWidget() == w;
623 }
624 
625 bool isObscured(QWidget *w)
626 {
627  return !(checkPoint(QPoint(0, 0), w)
628  && checkPoint(QPoint(w->width() - 1, 0), w)
629  && checkPoint(QPoint(0, w->height() - 1), w)
630  && checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
631  && checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
632 }
633 
634 void bringToFront(QWidget* w)
635 {
636 #ifdef Q_OS_MAC
637  ForceActivation();
638 #endif
639 
640  if (w) {
641  // activateWindow() (sometimes) helps with keyboard focus on Windows
642  if (w->isMinimized()) {
643  w->showNormal();
644  } else {
645  w->show();
646  }
647  w->activateWindow();
648  w->raise();
649  }
650 }
651 
653 {
654  fs::path pathDebug = GetDataDir() / "debug.log";
655 
656  /* Open debug.log with the associated application */
657  if (fs::exists(pathDebug))
658  QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug)));
659 }
660 
662 {
663  fs::path pathConfig = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
664 
665  /* Open dash.conf with the associated application */
666  if (fs::exists(pathConfig))
667  QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
668 }
669 
671 {
672  fs::path backupsDir = GetBackupsDir();
673 
674  /* Open folder with default browser */
675  if (fs::exists(backupsDir))
676  QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(backupsDir)));
677 }
678 
679 void SubstituteFonts(const QString& language)
680 {
681 #if defined(Q_OS_MAC)
682 // Background:
683 // OSX's default font changed in 10.9 and Qt is unable to find it with its
684 // usual fallback methods when building against the 10.7 sdk or lower.
685 // The 10.8 SDK added a function to let it find the correct fallback font.
686 // If this fallback is not properly loaded, some characters may fail to
687 // render correctly.
688 //
689 // The same thing happened with 10.10. .Helvetica Neue DeskInterface is now default.
690 //
691 // Solution: If building with the 10.7 SDK or lower and the user's platform
692 // is 10.9 or higher at runtime, substitute the correct font. This needs to
693 // happen before the QApplication is created.
694 #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8
695  if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_8)
696  {
697  if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9)
698  /* On a 10.9 - 10.9.x system */
699  QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
700  else
701  {
702  /* 10.10 or later system */
703  if (language == "zh_CN" || language == "zh_TW" || language == "zh_HK") // traditional or simplified Chinese
704  QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Heiti SC");
705  else if (language == "ja") // Japanese
706  QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Songti SC");
707  else
708  QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Lucida Grande");
709  }
710  }
711 #endif
712 #endif
713 }
714 
715 ToolTipToRichTextFilter::ToolTipToRichTextFilter(int _size_threshold, QObject *parent) :
716  QObject(parent),
717  size_threshold(_size_threshold)
718 {
719 
720 }
721 
722 bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
723 {
724  if(evt->type() == QEvent::ToolTipChange)
725  {
726  QWidget *widget = static_cast<QWidget*>(obj);
727  QString tooltip = widget->toolTip();
728  if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt"))
729  {
730  // Escape the current message as HTML and replace \n by <br> if it's not rich text
731  if(!Qt::mightBeRichText(tooltip))
732  tooltip = HtmlEscape(tooltip, true);
733  // Envelop with <qt></qt> to make sure Qt detects every tooltip as rich text
734  // and style='white-space:pre' to preserve line composition
735  tooltip = "<qt style='white-space:pre'>" + tooltip + "</qt>";
736  widget->setToolTip(tooltip);
737  return true;
738  }
739  }
740  return QObject::eventFilter(obj, evt);
741 }
742 
744 {
745  connect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(on_sectionResized(int,int,int)));
746  connect(tableView->horizontalHeader(), SIGNAL(geometriesChanged()), this, SLOT(on_geometriesChanged()));
747 }
748 
749 // We need to disconnect these while handling the resize events, otherwise we can enter infinite loops.
751 {
752  disconnect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(on_sectionResized(int,int,int)));
753  disconnect(tableView->horizontalHeader(), SIGNAL(geometriesChanged()), this, SLOT(on_geometriesChanged()));
754 }
755 
756 // Setup the resize mode, handles compatibility for Qt5 and below as the method signatures changed.
757 // Refactored here for readability.
758 void TableViewLastColumnResizingFixer::setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode)
759 {
760 #if QT_VERSION < 0x050000
761  tableView->horizontalHeader()->setResizeMode(logicalIndex, resizeMode);
762 #else
763  tableView->horizontalHeader()->setSectionResizeMode(logicalIndex, resizeMode);
764 #endif
765 }
766 
767 void TableViewLastColumnResizingFixer::resizeColumn(int nColumnIndex, int width)
768 {
769  tableView->setColumnWidth(nColumnIndex, width);
770  tableView->horizontalHeader()->resizeSection(nColumnIndex, width);
771 }
772 
774 {
775  int nColumnsWidthSum = 0;
776  for (int i = 0; i < columnCount; i++)
777  {
778  nColumnsWidthSum += tableView->horizontalHeader()->sectionSize(i);
779  }
780  return nColumnsWidthSum;
781 }
782 
784 {
785  int nResult = lastColumnMinimumWidth;
786  int nTableWidth = tableView->horizontalHeader()->width();
787 
788  if (nTableWidth > 0)
789  {
790  int nOtherColsWidth = getColumnsWidth() - tableView->horizontalHeader()->sectionSize(column);
791  nResult = std::max(nResult, nTableWidth - nOtherColsWidth);
792  }
793 
794  return nResult;
795 }
796 
797 // Make sure we don't make the columns wider than the table's viewport width.
799 {
803 
804  int nTableWidth = tableView->horizontalHeader()->width();
805  int nColsWidth = getColumnsWidth();
806  if (nColsWidth > nTableWidth)
807  {
809  }
810 }
811 
812 // Make column use all the space available, useful during window resizing.
814 {
816  resizeColumn(column, getAvailableWidthForColumn(column));
818 }
819 
820 // When a section is resized this is a slot-proxy for ajustAmountColumnWidth().
821 void TableViewLastColumnResizingFixer::on_sectionResized(int logicalIndex, int oldSize, int newSize)
822 {
824  int remainingWidth = getAvailableWidthForColumn(logicalIndex);
825  if (newSize > remainingWidth)
826  {
827  resizeColumn(logicalIndex, remainingWidth);
828  }
829 }
830 
831 // When the table's geometry is ready, we manually perform the stretch of the "Message" column,
832 // as the "Stretch" resize mode does not allow for interactive resizing.
834 {
835  if ((getColumnsWidth() - this->tableView->horizontalHeader()->width()) != 0)
836  {
840  }
841 }
842 
847 TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth, QObject *parent) :
848  QObject(parent),
849  tableView(table),
850  lastColumnMinimumWidth(lastColMinimumWidth),
851  allColumnsMinimumWidth(allColsMinimumWidth)
852 {
853  columnCount = tableView->horizontalHeader()->count();
856  tableView->horizontalHeader()->setMinimumSectionSize(allColumnsMinimumWidth);
857  setViewHeaderResizeMode(secondToLastColumnIndex, QHeaderView::Interactive);
858  setViewHeaderResizeMode(lastColumnIndex, QHeaderView::Interactive);
859 }
860 
861 #ifdef WIN32
862 fs::path static StartupShortcutPath()
863 {
864  std::string chain = gArgs.GetChainName();
865  if (chain == CBaseChainParams::MAIN)
866  return GetSpecialFolderPath(CSIDL_STARTUP) / "Dash Core.lnk";
867  if (chain == CBaseChainParams::TESTNET) // Remove this special case when CBaseChainParams::TESTNET = "testnet4"
868  return GetSpecialFolderPath(CSIDL_STARTUP) / "Dash Core (testnet).lnk";
869  return GetSpecialFolderPath(CSIDL_STARTUP) / strprintf("Dash Core (%s).lnk", chain);
870 }
871 
873 {
874  // check for "Dash Core*.lnk"
875  return fs::exists(StartupShortcutPath());
876 }
877 
878 bool SetStartOnSystemStartup(bool fAutoStart)
879 {
880  // If the shortcut exists already, remove it for updating
881  fs::remove(StartupShortcutPath());
882 
883  if (fAutoStart)
884  {
885  CoInitialize(nullptr);
886 
887  // Get a pointer to the IShellLink interface.
888  IShellLink* psl = nullptr;
889  HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr,
890  CLSCTX_INPROC_SERVER, IID_IShellLink,
891  reinterpret_cast<void**>(&psl));
892 
893  if (SUCCEEDED(hres))
894  {
895  // Get the current executable path
896  TCHAR pszExePath[MAX_PATH];
897  GetModuleFileName(nullptr, pszExePath, sizeof(pszExePath));
898 
899  // Start client minimized
900  QString strArgs = "-min";
901  // Set -testnet /-regtest options
902  strArgs += QString::fromStdString(strprintf(" -testnet=%d -regtest=%d", gArgs.GetBoolArg("-testnet", false), gArgs.GetBoolArg("-regtest", false)));
903 
904 #ifdef UNICODE
905  boost::scoped_array<TCHAR> args(new TCHAR[strArgs.length() + 1]);
906  // Convert the QString to TCHAR*
907  strArgs.toWCharArray(args.get());
908  // Add missing '\0'-termination to string
909  args[strArgs.length()] = '\0';
910 #endif
911 
912  // Set the path to the shortcut target
913  psl->SetPath(pszExePath);
914  PathRemoveFileSpec(pszExePath);
915  psl->SetWorkingDirectory(pszExePath);
916  psl->SetShowCmd(SW_SHOWMINNOACTIVE);
917 #ifndef UNICODE
918  psl->SetArguments(strArgs.toStdString().c_str());
919 #else
920  psl->SetArguments(args.get());
921 #endif
922 
923  // Query IShellLink for the IPersistFile interface for
924  // saving the shortcut in persistent storage.
925  IPersistFile* ppf = nullptr;
926  hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&ppf));
927  if (SUCCEEDED(hres))
928  {
929  WCHAR pwsz[MAX_PATH];
930  // Ensure that the string is ANSI.
931  MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH);
932  // Save the link by calling IPersistFile::Save.
933  hres = ppf->Save(pwsz, TRUE);
934  ppf->Release();
935  psl->Release();
936  CoUninitialize();
937  return true;
938  }
939  psl->Release();
940  }
941  CoUninitialize();
942  return false;
943  }
944  return true;
945 }
946 #elif defined(Q_OS_LINUX)
947 
948 // Follow the Desktop Application Autostart Spec:
949 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
950 
951 fs::path static GetAutostartDir()
952 {
953  char* pszConfigHome = getenv("XDG_CONFIG_HOME");
954  if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
955  char* pszHome = getenv("HOME");
956  if (pszHome) return fs::path(pszHome) / ".config" / "autostart";
957  return fs::path();
958 }
959 
960 fs::path static GetAutostartFilePath()
961 {
962  std::string chain = gArgs.GetChainName();
963  if (chain == CBaseChainParams::MAIN)
964  return GetAutostartDir() / "dashcore.desktop";
965  return GetAutostartDir() / strprintf("dashcore-%s.lnk", chain);
966 }
967 
969 {
970  fs::ifstream optionFile(GetAutostartFilePath());
971  if (!optionFile.good())
972  return false;
973  // Scan through file for "Hidden=true":
974  std::string line;
975  while (!optionFile.eof())
976  {
977  getline(optionFile, line);
978  if (line.find("Hidden") != std::string::npos &&
979  line.find("true") != std::string::npos)
980  return false;
981  }
982  optionFile.close();
983 
984  return true;
985 }
986 
987 bool SetStartOnSystemStartup(bool fAutoStart)
988 {
989  if (!fAutoStart)
990  fs::remove(GetAutostartFilePath());
991  else
992  {
993  char pszExePath[MAX_PATH+1];
994  ssize_t r = readlink("/proc/self/exe", pszExePath, sizeof(pszExePath) - 1);
995  if (r == -1)
996  return false;
997  pszExePath[r] = '\0';
998 
999  fs::create_directories(GetAutostartDir());
1000 
1001  fs::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
1002  if (!optionFile.good())
1003  return false;
1004  std::string chain = gArgs.GetChainName();
1005  // Write a dashcore.desktop file to the autostart directory:
1006  optionFile << "[Desktop Entry]\n";
1007  optionFile << "Type=Application\n";
1008  if (chain == CBaseChainParams::MAIN)
1009  optionFile << "Name=Dash Core\n";
1010  else
1011  optionFile << strprintf("Name=Dash Core (%s)\n", chain);
1012  optionFile << "Exec=" << pszExePath << strprintf(" -min -testnet=%d -regtest=%d\n", gArgs.GetBoolArg("-testnet", false), gArgs.GetBoolArg("-regtest", false));
1013  optionFile << "Terminal=false\n";
1014  optionFile << "Hidden=false\n";
1015  optionFile.close();
1016  }
1017  return true;
1018 }
1019 
1020 
1021 #elif defined(Q_OS_MAC)
1022 // based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m
1023 
1024 LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl);
1025 LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl)
1026 {
1027  CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, nullptr);
1028  if (listSnapshot == nullptr) {
1029  return nullptr;
1030  }
1031 
1032  // loop through the list of startup items and try to find the Dash Core app
1033  for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) {
1034  LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i);
1035  UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
1036  CFURLRef currentItemURL = nullptr;
1037 
1038 #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100
1039  if(&LSSharedFileListItemCopyResolvedURL)
1040  currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr);
1041 #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100
1042  else
1043  LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, nullptr);
1044 #endif
1045 #else
1046  LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, nullptr);
1047 #endif
1048 
1049  if(currentItemURL) {
1050  if (CFEqual(currentItemURL, findUrl)) {
1051  // found
1052  CFRelease(listSnapshot);
1053  CFRelease(currentItemURL);
1054  return item;
1055  }
1056  CFRelease(currentItemURL);
1057  }
1058  }
1059 
1060  CFRelease(listSnapshot);
1061  return nullptr;
1062 }
1063 
1065 {
1066  CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
1067  if (bitcoinAppUrl == nullptr) {
1068  return false;
1069  }
1070 
1071  LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr);
1072  LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
1073 
1074  CFRelease(bitcoinAppUrl);
1075  return !!foundItem; // return boolified object
1076 }
1077 
1078 bool SetStartOnSystemStartup(bool fAutoStart)
1079 {
1080  CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
1081  if (bitcoinAppUrl == nullptr) {
1082  return false;
1083  }
1084 
1085  LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr);
1086  LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
1087 
1088  if(fAutoStart && !foundItem) {
1089  // add Dash Core app to startup item list
1090  LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, nullptr, nullptr, bitcoinAppUrl, nullptr, nullptr);
1091  }
1092  else if(!fAutoStart && foundItem) {
1093  // remove item
1094  LSSharedFileListItemRemove(loginItems, foundItem);
1095  }
1096 
1097  CFRelease(bitcoinAppUrl);
1098  return true;
1099 }
1100 #pragma GCC diagnostic pop
1101 #else
1102 
1103 bool GetStartOnSystemStartup() { return false; }
1104 bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
1105 
1106 #endif
1107 
1109 {
1110  // Migration (12.1)
1111  QSettings settings;
1112  if(!settings.value("fMigrationDone121", false).toBool()) {
1113  settings.remove("theme");
1114  settings.remove("nWindowPos");
1115  settings.remove("nWindowSize");
1116  settings.setValue("fMigrationDone121", true);
1117  }
1118 }
1119 
1120 void setStyleSheetDirectory(const QString& path)
1121 {
1122  stylesheetDirectory = path;
1123 }
1124 
1126 {
1128 }
1129 
1130 const std::vector<QString> listStyleSheets()
1131 {
1132  std::vector<QString> vecStylesheets;
1133  for (const auto& it : mapStyleToTheme) {
1134  vecStylesheets.push_back(it.first);
1135  }
1136  return vecStylesheets;
1137 }
1138 
1139 const std::vector<QString> listThemes()
1140 {
1141  std::vector<QString> vecThemes;
1142  for (const auto& it : mapStyleToTheme) {
1143  if (!it.second.isEmpty()) {
1144  vecThemes.push_back(it.second);
1145  }
1146  }
1147  return vecThemes;
1148 }
1149 
1150 const QString getDefaultTheme()
1151 {
1152  return defaultTheme;
1153 }
1154 
1155 void loadStyleSheet(QWidget* widget, bool fForceUpdate)
1156 {
1158  LOCK(cs_css);
1159 
1160  static std::unique_ptr<QString> stylesheet;
1161  static std::set<QWidget*> setWidgets;
1162 
1163  bool fDebugCustomStyleSheets = gArgs.GetBoolArg("-debug-ui", false) && isStyleSheetDirectoryCustom();
1164  bool fStyleSheetChanged = false;
1165 
1166  if (stylesheet == nullptr || fForceUpdate || fDebugCustomStyleSheets) {
1167  auto hasModified = [](const std::vector<QString>& vecFiles) -> bool {
1168  static std::map<const QString, QDateTime> mapLastModified;
1169 
1170  bool fModified = false;
1171  for (auto file = vecFiles.begin(); file != vecFiles.end() && !fModified; ++file) {
1172  QFileInfo info(*file);
1173  QDateTime lastModified = info.lastModified(), prevLastModified;
1174  auto it = mapLastModified.emplace(std::make_pair(*file, lastModified));
1175  prevLastModified = it.second ? QDateTime() : it.first->second;
1176  it.first->second = lastModified;
1177  fModified = prevLastModified != lastModified;
1178  }
1179  return fModified;
1180  };
1181 
1182  auto loadFiles = [&](const std::vector<QString>& vecFiles) -> bool {
1183  if (!fForceUpdate && fDebugCustomStyleSheets && !hasModified(vecFiles)) {
1184  return false;
1185  }
1186 
1187  std::string platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM);
1188  stylesheet = std::make_unique<QString>();
1189 
1190  for (const auto& file : vecFiles) {
1191  QFile qFile(file);
1192  if (!qFile.open(QFile::ReadOnly)) {
1193  throw std::runtime_error(strprintf("%s: Failed to open file: %s", __func__, file.toStdString()));
1194  }
1195 
1196  QString strStyle = QLatin1String(qFile.readAll());
1197  // Process all <os=...></os> groups in the stylesheet first
1198  QRegularExpressionMatch osStyleMatch;
1199  QRegularExpression osStyleExp(
1200  "^"
1201  "(<os=(?:'|\").+(?:'|\")>)" // group 1
1202  "((?:.|\n)+?)" // group 2
1203  "(</os>?)" // group 3
1204  "$");
1205  osStyleExp.setPatternOptions(QRegularExpression::MultilineOption);
1206  QRegularExpressionMatchIterator it = osStyleExp.globalMatch(strStyle);
1207 
1208  // For all <os=...></os> sections
1209  while (it.hasNext() && (osStyleMatch = it.next()).isValid()) {
1210  QStringList listMatches = osStyleMatch.capturedTexts();
1211 
1212  // Full match + 3 group matches
1213  if (listMatches.size() % 4) {
1214  throw std::runtime_error(strprintf("%s: Invalid <os=...></os> section in file %s", __func__, file.toStdString()));
1215  }
1216 
1217  for (int i = 0; i < listMatches.size(); i += 4) {
1218  if (!listMatches[i + 1].contains(QString::fromStdString(platformName))) {
1219  // If os is not supported for this styles
1220  // just remove the full match
1221  strStyle.replace(listMatches[i], "");
1222  } else {
1223  // If its supported remove the <os=...></os> tags
1224  strStyle.replace(listMatches[i + 1], "");
1225  strStyle.replace(listMatches[i + 3], "");
1226  }
1227  }
1228  }
1229  stylesheet->append(strStyle);
1230  }
1231  return true;
1232  };
1233 
1234  auto pathToFile = [&](const QString& file) -> QString {
1235  return stylesheetDirectory + "/" + file + (isStyleSheetDirectoryCustom() ? ".css" : "");
1236  };
1237 
1238  std::vector<QString> vecFiles;
1239  // If light/dark theme is used load general styles first
1240  if (dashThemeActive()) {
1241  vecFiles.push_back(pathToFile("general"));
1242  }
1243  vecFiles.push_back(pathToFile(getActiveTheme()));
1244 
1245  fStyleSheetChanged = loadFiles(vecFiles);
1246  }
1247 
1248  bool fUpdateStyleSheet = fForceUpdate || (fDebugCustomStyleSheets && fStyleSheetChanged);
1249 
1250  if (widget) {
1251  setWidgets.insert(widget);
1252  widget->setStyleSheet(*stylesheet);
1253  }
1254 
1255  QWidgetList allWidgets = QApplication::allWidgets();
1256  auto it = setWidgets.begin();
1257  while (it != setWidgets.end()) {
1258  if (!allWidgets.contains(*it)) {
1259  it = setWidgets.erase(it);
1260  continue;
1261  }
1262  if (fUpdateStyleSheet && *it != widget) {
1263  (*it)->setStyleSheet(*stylesheet);
1264  }
1265  ++it;
1266  }
1267 
1268  if (!ShutdownRequested() && fDebugCustomStyleSheets && !fForceUpdate) {
1269  QTimer::singleShot(200, [] { loadStyleSheet(); });
1270  }
1271 }
1272 
1273 FontFamily fontFamilyFromString(const QString& strFamily)
1274 {
1275  if (strFamily == "SystemDefault") {
1277  }
1278  if (strFamily == "Montserrat") {
1279  return FontFamily::Montserrat;
1280  }
1281  throw std::invalid_argument(strprintf("Invalid font-family: %s", strFamily.toStdString()));
1282 }
1283 
1285 {
1286  switch (family) {
1288  return "SystemDefault";
1290  return "Montserrat";
1291  default:
1292  assert(false);
1293  }
1294 }
1295 
1297 {
1298  fontFamily = family;
1300  updateFonts();
1301 }
1302 
1304 {
1305  return defaultFontFamily;
1306 }
1307 
1309 {
1310  return fontFamily;
1311 }
1312 
1313 bool weightFromArg(int nArg, QFont::Weight& weight)
1314 {
1315  const std::map<int, QFont::Weight> mapWeight{
1316  {0, QFont::Thin},
1317  {1, QFont::ExtraLight},
1318  {2, QFont::Light},
1319  {3, QFont::Normal},
1320  {4, QFont::Medium},
1321  {5, QFont::DemiBold},
1322  {6, QFont::Bold},
1323  {7, QFont::ExtraBold},
1324  {8, QFont::Black}
1325  };
1326  auto it = mapWeight.find(nArg);
1327  if (it == mapWeight.end()) {
1328  return false;
1329  }
1330  weight = it->second;
1331  return true;
1332 }
1333 
1334 int weightToArg(const QFont::Weight weight)
1335 {
1336  const std::map<QFont::Weight, int> mapWeight{
1337  {QFont::Thin, 0},
1338  {QFont::ExtraLight, 1},
1339  {QFont::Light, 2},
1340  {QFont::Normal, 3},
1341  {QFont::Medium, 4},
1342  {QFont::DemiBold, 5},
1343  {QFont::Bold, 6},
1344  {QFont::ExtraBold, 7},
1345  {QFont::Black, 8}
1346  };
1347  assert(mapWeight.count(weight));
1348  return mapWeight.find(weight)->second;
1349 }
1350 
1352 {
1353  return defaultFontWeightNormal;
1354 }
1355 
1356 QFont::Weight toQFontWeight(FontWeight weight)
1357 {
1358  return weight == FontWeight::Bold ? getFontWeightBold() : getFontWeightNormal();
1359 }
1360 
1361 QFont::Weight getFontWeightNormal()
1362 {
1363  return fontWeightNormal;
1364 }
1365 
1366 void setFontWeightNormal(QFont::Weight weight)
1367 {
1368  fontWeightNormal = weight;
1369  updateFonts();
1370 }
1371 
1373 {
1374  return defaultFontWeightBold;
1375 }
1376 
1377 QFont::Weight getFontWeightBold()
1378 {
1379  return fontWeightBold;
1380 }
1381 
1382 void setFontWeightBold(QFont::Weight weight)
1383 {
1384  fontWeightBold = weight;
1385  updateFonts();
1386 }
1387 
1389 {
1390  return defaultFontScale;
1391 }
1392 
1394 {
1395  return fontScale;
1396 }
1397 
1398 void setFontScale(int nScale)
1399 {
1400  fontScale = nScale;
1401  updateFonts();
1402 }
1403 
1404 double getScaledFontSize(int nSize)
1405 {
1406  return std::round(nSize * (1 + (fontScale * fontScaleSteps)) * 4) / 4.0;
1407 }
1408 
1410 {
1411  // Before any font changes store the applications default font to use it as SystemDefault.
1412  osDefaultFont = std::make_unique<QFont>(QApplication::font());
1413 
1414  QString family = fontFamilyToString(FontFamily::Montserrat);
1415  QString italic = "Italic";
1416 
1417  std::map<QString, bool> mapStyles{
1418  {"Thin", true},
1419  {"ExtraLight", true},
1420  {"Light", true},
1421  {"Italic", false},
1422  {"Regular", false},
1423  {"Medium", true},
1424  {"SemiBold", true},
1425  {"Bold", true},
1426  {"ExtraBold", true},
1427  {"Black", true},
1428  };
1429 
1430  QFontDatabase database;
1431  std::vector<int> vecFontIds;
1432 
1433  for (const auto& it : mapStyles) {
1434  QString font = ":fonts/" + family + "-" + it.first;
1435  vecFontIds.push_back(QFontDatabase::addApplicationFont(font));
1436  qDebug() << __func__ << ": " << font << " loaded with id " << vecFontIds.back();
1437  if (it.second) {
1438  vecFontIds.push_back(QFontDatabase::addApplicationFont(font + italic));
1439  qDebug() << __func__ << ": " << font + italic << " loaded with id " << vecFontIds.back();
1440  }
1441  }
1442 
1443  // Fail if an added id is -1 which means QFontDatabase::addApplicationFont failed.
1444  if (std::find(vecFontIds.begin(), vecFontIds.end(), -1) != vecFontIds.end()) {
1445  return false;
1446  }
1447 
1448  // Print debug logs for added fonts fetched by the added ids
1449  for (const auto& i : vecFontIds) {
1450  auto families = QFontDatabase::applicationFontFamilies(i);
1451  for (const QString& f : families) {
1452  qDebug() << __func__ << ": - Font id " << i << " is family: " << f;
1453  const QStringList fontStyles = database.styles(f);
1454  for (const QString& style : fontStyles) {
1455  qDebug() << __func__ << ": Style for family " << f << " with id: " << i << ": " << style;
1456  }
1457  }
1458  }
1459  // Print debug logs for added fonts fetched by the family name
1460  const QStringList fontFamilies = database.families();
1461  for (const QString& f : fontFamilies) {
1462  if (f.contains(family)) {
1463  const QStringList fontStyles = database.styles(f);
1464  for (const QString& style : fontStyles) {
1465  qDebug() << __func__ << ": Family: " << f << ", Style: " << style;
1466  }
1467  }
1468  }
1469 
1470  // Load font related settings
1471  QSettings settings;
1472  QFont::Weight weight;
1473 
1474  if (!gArgs.IsArgSet("-font-family")) {
1475  fontFamily = fontFamilyFromString(settings.value("fontFamily").toString());
1476  }
1477 
1478  if (!gArgs.IsArgSet("-font-scale")) {
1479  fontScale = settings.value("fontScale").toInt();
1480  }
1481 
1482  if (!gArgs.IsArgSet("-font-weight-normal") && weightFromArg(settings.value("fontWeightNormal").toInt(), weight)) {
1483  fontWeightNormal = weight;
1484  }
1485 
1486  if (!gArgs.IsArgSet("-font-weight-bold") && weightFromArg(settings.value("fontWeightBold").toInt(), weight)) {
1487  fontWeightBold = weight;
1488  }
1489 
1491 
1492  // Initialize supported font weights for all available fonts
1493  // Generate a vector with supported font weights by comparing the width of a certain test text for all font weights
1494  auto supportedWeights = [](FontFamily family) -> std::vector<QFont::Weight> {
1495  auto getTestWidth = [&](QFont::Weight weight) -> int {
1496  QFont font = getFont(family, weight, false, defaultFontSize);
1497  return QFontMetrics(font).width("Check the width of this text to see if the weight change has an impact!");
1498  };
1499  std::vector<QFont::Weight> vecWeights{QFont::Thin, QFont::ExtraLight, QFont::Light,
1500  QFont::Normal, QFont::Medium, QFont::DemiBold,
1501  QFont::Bold, QFont::Black};
1502  std::vector<QFont::Weight> vecSupported;
1503  QFont::Weight prevWeight = vecWeights.front();
1504  for (auto weight = vecWeights.begin() + 1; weight != vecWeights.end(); ++weight) {
1505  if (getTestWidth(prevWeight) != getTestWidth(*weight)) {
1506  if (vecSupported.empty()) {
1507  vecSupported.push_back(prevWeight);
1508  }
1509  vecSupported.push_back(*weight);
1510  }
1511  prevWeight = *weight;
1512  }
1513  return vecSupported;
1514  };
1515 
1516  mapSupportedWeights.insert(std::make_pair(FontFamily::SystemDefault, supportedWeights(FontFamily::SystemDefault)));
1517  mapSupportedWeights.insert(std::make_pair(FontFamily::Montserrat, supportedWeights(FontFamily::Montserrat)));
1518 
1519  return true;
1520 }
1521 
1523 {
1524  std::unique_ptr<QFont> font;
1525 
1527  QString family = fontFamilyToString(FontFamily::Montserrat);
1528 #ifdef Q_OS_MAC
1530  font = std::make_unique<QFont>(getFontNormal());
1531  } else {
1532  font = std::make_unique<QFont>(family);
1533  font->setWeight(getFontWeightNormalDefault());
1534  }
1535 #else
1536  font = std::make_unique<QFont>(family);
1537  font->setWeight(getFontWeightNormal());
1538 #endif
1539  } else {
1540  font = std::make_unique<QFont>(*osDefaultFont);
1541  }
1542 
1543  font->setPointSizeF(defaultFontSize);
1544  qApp->setFont(*font);
1545 
1546  qDebug() << __func__ << ": " << qApp->font().toString() <<
1547  " family: " << qApp->font().family() <<
1548  ", style: " << qApp->font().styleName() <<
1549  " match: " << qApp->font().exactMatch();
1550 }
1551 
1552 void setFont(const std::vector<QWidget*>& vecWidgets, FontWeight weight, int nPointSize, bool fItalic)
1553 {
1554  for (auto it : vecWidgets) {
1555  auto fontAttributes = std::make_tuple(weight, fItalic, nPointSize);
1556  auto itFontUpdate = mapNormalFontUpdates.emplace(std::make_pair(it, fontAttributes));
1557  if (!itFontUpdate.second) {
1558  itFontUpdate.first->second = fontAttributes;
1559  }
1560  }
1561 }
1562 
1564 {
1565  // Fonts need to be loaded by GUIIUtil::loadFonts(), if not just return.
1566  if (!osDefaultFont) {
1567  return;
1568  }
1569 
1570  static std::map<QWidget*, int> mapWidgetDefaultFontSizes;
1571  static std::map<QString, int> mapClassDefaultFontSizes;
1572  std::map<QWidget*, std::pair<QFont, bool>> mapWidgetFonts;
1573 
1574  for (QWidget* w : qApp->allWidgets()) {
1575  std::vector<QString> vecIgnore{
1576  "QWidget", "QDialog", "QFrame", "QStackedWidget", "QDesktopWidget", "QDesktopScreenWidget",
1577  "QTipLabel", "QMessageBox", "QMenu", "QComboBoxPrivateScroller", "QComboBoxPrivateContainer",
1578  "QScrollBar", "QListView", "BitcoinGUI", "WalletView", "WalletFrame"
1579  };
1580  if (std::find(vecIgnore.begin(), vecIgnore.end(), w->metaObject()->className()) != vecIgnore.end()) {
1581  continue;
1582  }
1583  QFont font = w->font();
1584  font.setFamily(qApp->font().family());
1585  font.setWeight(getFontWeightNormal());
1586  font.setStyleName(qApp->font().styleName());
1587  font.setStyle(qApp->font().style());
1588  // Set the font size based on the widgets default font size + the font scale
1589  bool fAdded = false;
1590  if (!mapWidgetDefaultFontSizes.count(w)) {
1591  mapWidgetDefaultFontSizes.emplace(std::make_pair(w, font.pointSize() > 0 ? font.pointSize() : defaultFontSize));
1592  fAdded = true;
1593  }
1594  font.setPointSizeF(getScaledFontSize(mapWidgetDefaultFontSizes[w]));
1595  bool fUpdateRequired = fAdded || (mapNormalFontUpdates.find(w) == mapNormalFontUpdates.end() && font != w->font());
1596  mapWidgetFonts.emplace(w, std::make_pair(font, fUpdateRequired));
1597  }
1598 
1599  auto itn = mapNormalFontUpdates.begin();
1600  while (itn != mapNormalFontUpdates.end()) {
1601  auto itw = mapWidgetFonts.find(itn->first);
1602  if (itw != mapWidgetFonts.end()) {
1603  int nSize = std::get<2>(itn->second);
1604  if (nSize == -1) {
1605  nSize = mapWidgetDefaultFontSizes[itn->first];
1606  }
1607  QFont&& font = getFont(std::get<0>(itn->second), std::get<1>(itn->second), nSize);
1608  if (itn->first->font() != font) {
1609  itw->second.first = font;
1610  itw->second.second = true;
1611  }
1612  ++itn;
1613  } else {
1614  itn = mapNormalFontUpdates.erase(itn);
1615  }
1616  }
1617 
1618  for (auto it : mapWidgetFonts) {
1619  if (it.second.second) {
1620  it.first->setFont(it.second.first);
1621  }
1622  }
1623 
1624  // Cleanup mapDefaultFontSize to remove deleted widgets
1625  auto itd = mapWidgetDefaultFontSizes.begin();
1626  while (itd != mapWidgetDefaultFontSizes.end()) {
1627  if (qApp->allWidgets().contains(itd->first)) {
1628  ++itd;
1629  } else {
1630  itd = mapWidgetDefaultFontSizes.erase(itd);
1631  }
1632  }
1633 
1634  // Scale the global font for QToolTip labels, QMenu and QMessageBox instances
1635  QFont fontToolTip = qApp->font("QTipLabel");
1636  QFont fontMenu = qApp->font("QMenu");
1637  QFont fontMessageBox = qApp->font("QMessageBox");
1638  // Store their default font sizes before ever applying any scale to it
1639  if (!mapClassDefaultFontSizes.count("QTipLabel")) {
1640  mapClassDefaultFontSizes.emplace("QTipLabel", fontToolTip.pointSize());
1641  }
1642  if (!mapClassDefaultFontSizes.count("QMenu")) {
1643  mapClassDefaultFontSizes.emplace("QMenu", fontMenu.pointSize());
1644  }
1645  if (!mapClassDefaultFontSizes.count("QMessageBox")) {
1646  mapClassDefaultFontSizes.emplace("QMessageBox", fontMessageBox.pointSize());
1647  }
1648  // And give them the proper scaled size based on their default sizes if required
1649  double dSize = getScaledFontSize(mapClassDefaultFontSizes["QTipLabel"]);
1650  if (fontToolTip.pointSizeF() != dSize) {
1651  fontToolTip.setPointSizeF(dSize);
1652  qApp->setFont(fontToolTip, "QTipLabel");
1653  }
1654  dSize = getScaledFontSize(mapClassDefaultFontSizes["QMenu"]);
1655  if (fontMenu.pointSizeF() != dSize) {
1656  fontMenu.setPointSizeF(dSize);
1657  qApp->setFont(fontMenu, "QMenu");
1658  }
1659  dSize = getScaledFontSize(getScaledFontSize(mapClassDefaultFontSizes["QMessageBox"]));
1660  if (fontMessageBox.pointSizeF() != dSize) {
1661  fontMessageBox.setPointSizeF(dSize);
1662  qApp->setFont(fontMessageBox, "QMessageBox");
1663  }
1664 }
1665 
1666 QFont getFont(FontFamily family, QFont::Weight qWeight, bool fItalic, int nPointSize)
1667 {
1668  QFont font;
1669 
1670  if (family == FontFamily::Montserrat) {
1671  static std::map<QFont::Weight, QString> mapMontserratMapping{
1672  {QFont::Thin, "Thin"},
1673  {QFont::ExtraLight, "ExtraLight"},
1674  {QFont::Light, "Light"},
1675  {QFont::Medium, "Medium"},
1676  {QFont::DemiBold, "SemiBold"},
1677  {QFont::ExtraBold, "ExtraBold"},
1678  {QFont::Black, "Black"},
1679 #ifdef Q_OS_MAC
1680  {QFont::Normal, "Regular"},
1681  {QFont::Bold, "Bold"},
1682 #else
1683  {QFont::Normal, ""},
1684  {QFont::Bold, ""},
1685 #endif
1686  };
1687 
1688  assert(mapMontserratMapping.count(qWeight));
1689 
1690 #ifdef Q_OS_MAC
1691 
1692  QString styleName = mapMontserratMapping[qWeight];
1693 
1694  if (fItalic) {
1695  if (styleName == "Regular") {
1696  styleName = "Italic";
1697  } else {
1698  styleName += " Italic";
1699  }
1700  }
1701 
1702  font.setFamily(fontFamilyToString(FontFamily::Montserrat));
1703  font.setStyleName(styleName);
1704 #else
1705  font.setFamily(fontFamilyToString(FontFamily::Montserrat) + " " + mapMontserratMapping[qWeight]);
1706  font.setWeight(qWeight);
1707  font.setStyle(fItalic ? QFont::StyleItalic : QFont::StyleNormal);
1708 #endif
1709  } else {
1710  font.setFamily(osDefaultFont->family());
1711  font.setWeight(qWeight);
1712  font.setStyle(fItalic ? QFont::StyleItalic : QFont::StyleNormal);
1713  }
1714 
1715  if (nPointSize != -1) {
1716  font.setPointSizeF(getScaledFontSize(nPointSize));
1717  }
1718 
1719  if (gArgs.GetBoolArg("-debug-ui", false)) {
1720  qDebug() << __func__ << ": font size: " << font.pointSizeF() << " family: " << font.family() << ", style: " << font.styleName() << ", weight:" << font.weight() << " match: " << font.exactMatch();
1721  }
1722 
1723  return font;
1724 }
1725 
1726 QFont getFont(QFont::Weight qWeight, bool fItalic, int nPointSize)
1727 {
1728  return getFont(fontFamily, qWeight, fItalic, nPointSize);
1729 }
1730 QFont getFont(FontWeight weight, bool fItalic, int nPointSize)
1731 {
1732  return getFont(toQFontWeight(weight), fItalic, nPointSize);
1733 }
1734 
1736 {
1737  return getFont(FontWeight::Normal);
1738 }
1739 
1741 {
1742  return getFont(FontWeight::Bold);
1743 }
1744 
1745 std::vector<QFont::Weight> getSupportedWeights()
1746 {
1747  assert(mapSupportedWeights.count(fontFamily));
1749 }
1750 
1751 QFont::Weight supportedWeightFromIndex(int nIndex)
1752 {
1753  auto vecWeights = getSupportedWeights();
1754  assert(vecWeights.size() > nIndex);
1755  return vecWeights[nIndex];
1756 }
1757 
1758 int supportedWeightToIndex(QFont::Weight weight)
1759 {
1760  auto vecWeights = getSupportedWeights();
1761  for (int index = 0; index < vecWeights.size(); ++index) {
1762  if (weight == vecWeights[index]) {
1763  return index;
1764  }
1765  }
1766  assert(false);
1767 }
1768 
1770 {
1771  QSettings settings;
1772  return settings.value("theme", defaultTheme).toString();
1773 }
1774 
1776 {
1777  QSettings settings;
1778  QString theme = settings.value("theme", "").toString();
1779  return theme != traditionalTheme;
1780 }
1781 
1782 void loadTheme(QWidget* widget, bool fForce)
1783 {
1784  loadStyleSheet(widget, fForce);
1785  updateFonts();
1787 }
1788 
1789 void disableMacFocusRect(const QWidget* w)
1790 {
1791 #ifdef Q_OS_MAC
1792  for (const auto& c : w->findChildren<QWidget*>()) {
1793  if (c->testAttribute(Qt::WA_MacShowFocusRect)) {
1794  c->setAttribute(Qt::WA_MacShowFocusRect, !dashThemeActive());
1795  setRectsDisabled.emplace(c);
1796  }
1797  }
1798 #endif
1799 }
1800 
1802 {
1803 #ifdef Q_OS_MAC
1804  QWidgetList allWidgets = QApplication::allWidgets();
1805  auto it = setRectsDisabled.begin();
1806  while (it != setRectsDisabled.end()) {
1807  if (allWidgets.contains(*it)) {
1808  (*it)->setAttribute(Qt::WA_MacShowFocusRect, !dashThemeActive());
1809  ++it;
1810  } else {
1811  it = setRectsDisabled.erase(it);
1812  }
1813  }
1814 #endif
1815 }
1816 
1817 void setClipboard(const QString& str)
1818 {
1819  QApplication::clipboard()->setText(str, QClipboard::Clipboard);
1820  QApplication::clipboard()->setText(str, QClipboard::Selection);
1821 }
1822 
1823 fs::path qstringToBoostPath(const QString &path)
1824 {
1825  return fs::path(path.toStdString(), utf8);
1826 }
1827 
1828 QString boostPathToQString(const fs::path &path)
1829 {
1830  return QString::fromStdString(path.string(utf8));
1831 }
1832 
1833 QString formatDurationStr(int secs)
1834 {
1835  QStringList strList;
1836  int days = secs / 86400;
1837  int hours = (secs % 86400) / 3600;
1838  int mins = (secs % 3600) / 60;
1839  int seconds = secs % 60;
1840 
1841  if (days)
1842  strList.append(QString(QObject::tr("%1 d")).arg(days));
1843  if (hours)
1844  strList.append(QString(QObject::tr("%1 h")).arg(hours));
1845  if (mins)
1846  strList.append(QString(QObject::tr("%1 m")).arg(mins));
1847  if (seconds || (!days && !hours && !mins))
1848  strList.append(QString(QObject::tr("%1 s")).arg(seconds));
1849 
1850  return strList.join(" ");
1851 }
1852 
1853 QString formatServicesStr(quint64 mask)
1854 {
1855  QStringList strList;
1856 
1857  // Just scan the last 8 bits for now.
1858  for (int i = 0; i < 8; i++) {
1859  uint64_t check = 1 << i;
1860  if (mask & check)
1861  {
1862  switch (check)
1863  {
1864  case NODE_NETWORK:
1865  strList.append("NETWORK");
1866  break;
1867  case NODE_GETUTXO:
1868  strList.append("GETUTXO");
1869  break;
1870  case NODE_BLOOM:
1871  strList.append("BLOOM");
1872  break;
1873  case NODE_XTHIN:
1874  strList.append("XTHIN");
1875  break;
1876  default:
1877  strList.append(QString("%1[%2]").arg("UNKNOWN").arg(check));
1878  }
1879  }
1880  }
1881 
1882  if (strList.size())
1883  return strList.join(" & ");
1884  else
1885  return QObject::tr("None");
1886 }
1887 
1888 QString formatPingTime(double dPingTime)
1889 {
1890  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));
1891 }
1892 
1894 {
1895  return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10));
1896 }
1897 
1898 QString formatNiceTimeOffset(qint64 secs)
1899 {
1900  // Represent time from last generated block in human readable text
1901  QString timeBehindText;
1902  const int HOUR_IN_SECONDS = 60*60;
1903  const int DAY_IN_SECONDS = 24*60*60;
1904  const int WEEK_IN_SECONDS = 7*24*60*60;
1905  const int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar
1906  if(secs < 60)
1907  {
1908  timeBehindText = QObject::tr("%n second(s)","",secs);
1909  }
1910  else if(secs < 2*HOUR_IN_SECONDS)
1911  {
1912  timeBehindText = QObject::tr("%n minute(s)","",secs/60);
1913  }
1914  else if(secs < 2*DAY_IN_SECONDS)
1915  {
1916  timeBehindText = QObject::tr("%n hour(s)","",secs/HOUR_IN_SECONDS);
1917  }
1918  else if(secs < 2*WEEK_IN_SECONDS)
1919  {
1920  timeBehindText = QObject::tr("%n day(s)","",secs/DAY_IN_SECONDS);
1921  }
1922  else if(secs < YEAR_IN_SECONDS)
1923  {
1924  timeBehindText = QObject::tr("%n week(s)","",secs/WEEK_IN_SECONDS);
1925  }
1926  else
1927  {
1928  qint64 years = secs / YEAR_IN_SECONDS;
1929  qint64 remainder = secs % YEAR_IN_SECONDS;
1930  timeBehindText = QObject::tr("%1 and %2").arg(QObject::tr("%n year(s)", "", years)).arg(QObject::tr("%n week(s)","", remainder/WEEK_IN_SECONDS));
1931  }
1932  return timeBehindText;
1933 }
1934 
1935 QString formatBytes(uint64_t bytes)
1936 {
1937  if(bytes < 1024)
1938  return QString(QObject::tr("%1 B")).arg(bytes);
1939  if(bytes < 1024 * 1024)
1940  return QString(QObject::tr("%1 KB")).arg(bytes / 1024);
1941  if(bytes < 1024 * 1024 * 1024)
1942  return QString(QObject::tr("%1 MB")).arg(bytes / 1024 / 1024);
1943 
1944  return QString(QObject::tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024);
1945 }
1946 
1947 qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize, qreal font_size) {
1948  while(font_size >= minPointSize) {
1949  font.setPointSizeF(font_size);
1950  QFontMetrics fm(font);
1951  if (fm.width(text) < width) {
1952  break;
1953  }
1954  font_size -= 0.5;
1955  }
1956  return font_size;
1957 }
1958 
1959 void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
1960 {
1961  Q_EMIT clicked(event->pos());
1962 }
1963 
1965 {
1966  Q_EMIT clicked(event->pos());
1967 }
1968 
1969 } // namespace GUIUtil
void SubstituteFonts(const QString &language)
Definition: guiutil.cpp:679
void openDebugLogfile()
Definition: guiutil.cpp:652
bool isDust(const QString &address, const CAmount &amount)
Definition: guiutil.cpp:473
double getScaledFontSize(int nSize)
get font size with GUIUtil::fontScale applied
Definition: guiutil.cpp:1404
static std::map< FontFamily, std::vector< QFont::Weight > > mapSupportedWeights
Definition: guiutil.cpp:145
FontFamily
Definition: guiutil.h:277
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition: util.cpp:784
const std::vector< QString > listThemes()
Return a list of all theme css files.
Definition: guiutil.cpp:1139
bool weightFromArg(int nArg, QFont::Weight &weight)
Convert weight value from args (0-8) to QFont::Weight.
Definition: guiutil.cpp:1313
QFont::Weight getFontWeightNormal()
Definition: guiutil.cpp:1361
FontFamily getFontFamilyDefault()
set/get font family: GUIUtil::fontFamily
Definition: guiutil.cpp:1303
static CCriticalSection cs_css
Definition: guiutil.cpp:97
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:80
void setFontWeightBold(QFont::Weight weight)
Definition: guiutil.cpp:1382
QFont getFont(FontFamily family, QFont::Weight qWeight, bool fItalic, int nPointSize)
Get a properly weighted QFont object with the selected font.
Definition: guiutil.cpp:1666
Utility functions used by the Dash Qt UI.
Definition: guiutil.cpp:95
int weightToArg(const QFont::Weight weight)
Convert QFont::Weight to an arg value (0-8)
Definition: guiutil.cpp:1334
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:571
QList< QModelIndex > getEntryData(QAbstractItemView *view, int column)
Return a field of the currently selected entry as a QString.
Definition: guiutil.cpp:514
QFont::Weight supportedWeightFromIndex(int nIndex)
Convert an index to a weight in the supported weights vector.
Definition: guiutil.cpp:1751
void setupAmountWidget(QLineEdit *widget, QWidget *parent)
Definition: guiutil.cpp:300
const char *const BITCOIN_CONF_FILENAME
Definition: util.cpp:104
void setFont(const std::vector< QWidget *> &vecWidgets, FontWeight weight, int nPointSize, bool fItalic)
Workaround to set correct font styles in all themes since there is a bug in macOS which leads to issu...
Definition: guiutil.cpp:1552
FontFamily fontFamilyFromString(const QString &strFamily)
Definition: guiutil.cpp:1273
void setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode)
Definition: guiutil.cpp:758
#define RGB_HALF
Defines the half in RGB space, basically a grey in the middle between black and white.
Definition: guiconstants.h:24
bool ShutdownRequested()
Definition: init.cpp:179
#define strprintf
Definition: tinyformat.h:1066
bool dashThemeActive()
Check if a dash specific theme is activated (light/dark).
Definition: guiutil.cpp:1775
static const QString darkThemePrefix
Definition: guiutil.cpp:107
QColor getThemedQColor(ThemedColor color)
Helper to get colors for various themes which can&#39;t be applied via css for some reason.
Definition: guiutil.cpp:204
void setFontWeightNormal(QFont::Weight weight)
Definition: guiutil.cpp:1366
QFont getFontNormal()
Get the default normal QFont.
Definition: guiutil.cpp:1735
CTxDestination DecodeDestination(const std::string &str)
Definition: base58.cpp:336
#define MAX_PATH
Definition: compat.h:84
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:258
void loadTheme(QWidget *widget, bool fForce)
Load the theme and update all UI elements according to the appearance settings.
Definition: guiutil.cpp:1782
FontFamily getFontFamily()
Definition: guiutil.cpp:1308
QFont::Weight getFontWeightNormalDefault()
set/get normal font weight: GUIUtil::fontWeightNormal
Definition: guiutil.cpp:1351
std::string EncodeBase58(const unsigned char *pbegin, const unsigned char *pend)
Why base-58 instead of standard base-64 encoding?
Definition: base58.cpp:71
const std::vector< QString > listStyleSheets()
Return a list of all required css files.
Definition: guiutil.cpp:1130
static int64_t nTimeOffset
Definition: timedata.cpp:20
Qt::ConnectionType blockingGUIThreadConnection()
Get connection type to call object slot in GUI thread with invokeMethod.
Definition: guiutil.cpp:606
QString formatBytes(uint64_t bytes)
Definition: guiutil.cpp:1935
QString formatTimeOffset(int64_t nTimeOffset)
Definition: guiutil.cpp:1893
static const int defaultFontScale
Definition: guiutil.cpp:129
static const std::map< ThemedColor, QColor > themedDarkColors
Definition: guiutil.cpp:169
bool GetStartOnSystemStartup()
Definition: guiutil.cpp:1103
static const double fontScaleSteps
Definition: guiutil.cpp:121
static const std::map< ThemedColor, QColor > themedColors
Definition: guiutil.cpp:152
ToolTipToRichTextFilter(int size_threshold, QObject *parent=0)
Definition: guiutil.cpp:715
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent, bool fAllowURI)
Definition: guiutil.cpp:286
CChainParams defines various tweakable parameters of a given instance of the Dash system...
Definition: chainparams.h:41
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:481
FontWeight
Definition: guiutil.h:290
QFont::Weight getFontWeightBoldDefault()
set/get bold font weight: GUIUtil::fontWeightBold
Definition: guiutil.cpp:1372
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: util.cpp:824
Line edit that can be marked as "invalid" to show input validation feedback.
void migrateQtSettings()
Modify Qt network specific settings on migration.
Definition: guiutil.cpp:1108
static fs::detail::utf8_codecvt_facet utf8
Definition: guiutil.cpp:75
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:358
void setupAppearance(QWidget *parent, OptionsModel *model)
Definition: guiutil.cpp:309
static const QString defaultStylesheetDirectory
Definition: guiutil.cpp:99
QString formatBitcoinURI(const SendCoinsRecipient &info)
Definition: guiutil.cpp:445
QFont::Weight toQFontWeight(FontWeight weight)
Convert GUIUtil::FontWeight to QFont::Weight.
Definition: guiutil.cpp:1356
const QString getDefaultTheme()
Return the name of the default theme `.
Definition: guiutil.cpp:1150
static bool parse(int unit, const QString &value, CAmount *val_out)
Parse string to coin amount.
void bringToFront(QWidget *w)
Definition: guiutil.cpp:634
static const std::string MAIN
BIP70 chain name strings (main, test or regtest)
static const QFont::Weight defaultFontWeightNormal
Definition: guiutil.cpp:127
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:847
static const QString defaultTheme
Definition: guiutil.cpp:105
void openConfigfile()
Definition: guiutil.cpp:661
void updateMacFocusRects()
Enable/Disable the macOS focus rects depending on the current theme.
Definition: guiutil.cpp:1801
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
bool isStyleSheetDirectoryCustom()
Check if a custom css directory has been set with -custom-css-dir.
Definition: guiutil.cpp:1125
QFont::Weight getFontWeightBold()
Definition: guiutil.cpp:1377
void updateFonts()
Update the font of all widgets where a custom font has been set with GUIUtil::setFont.
Definition: guiutil.cpp:1563
bool IsValidDestinationString(const std::string &str, const CChainParams &params)
Definition: base58.cpp:341
bool isObscured(QWidget *w)
Definition: guiutil.cpp:625
bool eventFilter(QObject *obj, QEvent *evt)
Definition: guiutil.cpp:722
qreal calculateIdealFontSize(int width, const QString &text, QFont font, qreal minPointSize, qreal font_size)
Definition: guiutil.cpp:1947
QString formatDurationStr(int secs)
Definition: guiutil.cpp:1833
void setClipboard(const QString &str)
Definition: guiutil.cpp:1817
#define LOCK(cs)
Definition: sync.h:178
QString fontFamilyToString(FontFamily family)
Definition: guiutil.cpp:1284
QIcon getIcon(const QString &strIcon, const ThemedColor color, const ThemedColor colorAlternative, const QString &strIconPath)
Helper to get an icon colorized with the given color (replaces black) and colorAlternative (replaces ...
Definition: guiutil.cpp:216
bool validateBitcoinURI(const QString &uri)
Definition: guiutil.cpp:439
static const int defaultFontSize
Definition: guiutil.cpp:120
static std::unique_ptr< QFont > osDefaultFont
loadFonts stores the SystemDefault font in osDefaultFont to be able to reference it later again ...
Definition: guiutil.cpp:117
int supportedWeightToIndex(QFont::Weight weight)
Convert a weight to an index in the supported weights vector.
Definition: guiutil.cpp:1758
void clicked(const QPoint &point)
Emitted when the label is clicked.
Base58 entry widget validator, checks for valid characters and removes some whitespace.
std::vector< QFont::Weight > getSupportedWeights()
Return supported weights for the current font family.
Definition: guiutil.cpp:1745
An output of a transaction.
Definition: transaction.h:144
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:256
QString getActiveTheme()
Return the name of the currently active theme.
Definition: guiutil.cpp:1769
static int fontScale
Definition: guiutil.cpp:136
QString formatPingTime(double dPingTime)
Definition: guiutil.cpp:1888
void disableMacFocusRect(const QWidget *w)
Disable the OS default focus rect for macOS because we have custom focus rects set in the css files...
Definition: guiutil.cpp:1789
void on_sectionResized(int logicalIndex, int oldSize, int newSize)
Definition: guiutil.cpp:821
void setStyleSheetDirectory(const QString &path)
Change the stylesheet directory.
Definition: guiutil.cpp:1120
ThemedStyle
Definition: guiutil.h:72
constexpr T mask
Definition: bits.hpp:45
void mouseReleaseEvent(QMouseEvent *event)
Definition: guiutil.cpp:1959
static QString stylesheetDirectory
Definition: guiutil.cpp:101
ArgsManager gArgs
Definition: util.cpp:108
bool loadFonts()
Load dash specific appliciation fonts.
Definition: guiutil.cpp:1409
static const std::map< ThemedStyle, QString > themedStyles
Definition: guiutil.cpp:186
QFont getFontBold()
Get the default bold QFont.
Definition: guiutil.cpp:1740
void mouseReleaseEvent(QMouseEvent *event)
Definition: guiutil.cpp:1964
void setModel(OptionsModel *model)
fs::path qstringToBoostPath(const QString &path)
Definition: guiutil.cpp:1823
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:25
const CChainParams & Params()
Return the currently selected parameters.
fs::path GetConfigFile(const std::string &confPath)
Definition: util.cpp:976
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:389
int getFontScaleDefault()
set/get font scale: GUIUtil::fontScale
Definition: guiutil.cpp:1388
QString formatServicesStr(quint64 mask)
Definition: guiutil.cpp:1853
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:521
static std::string DummyAddress(const CChainParams &params)
Definition: guiutil.cpp:272
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: util.cpp:808
ThemedColor
Definition: guiutil.h:40
static const std::map< QString, QString > mapStyleToTheme
Definition: guiutil.cpp:109
void ForceActivation()
Force application activation on macOS.
QString getThemedStyleQString(ThemedStyle style)
Helper to get css style strings which are injected into rich text through qt.
Definition: guiutil.cpp:210
bool checkPoint(const QPoint &p, const QWidget *w)
Definition: guiutil.cpp:618
void setCheckValidator(const QValidator *v)
#define AssertLockNotHeld(cs)
Definition: sync.h:88
void setIcon(QAbstractButton *button, const QString &strIcon, const ThemedColor color, const ThemedColor colorAlternative, const QSize &size)
Helper to set an icon for a button with the given color (replaces black) and colorAlternative (replac...
Definition: guiutil.cpp:247
fs::path GetBackupsDir()
Definition: util.cpp:960
void setFontScale(int nScale)
Definition: guiutil.cpp:1398
static const std::string TESTNET
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
Definition: policy.cpp:35
static const std::map< ThemedStyle, QString > themedDarkStyles
Definition: guiutil.cpp:195
std::string GetChainName() const
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
Definition: util.cpp:1026
bool SetStartOnSystemStartup(bool fAutoStart)
Definition: guiutil.cpp:1104
static std::map< QWidget *, std::tuple< FontWeight, bool, int > > mapNormalFontUpdates
Definition: guiutil.cpp:143
const fs::path & GetDataDir(bool fNetSpecific)
Definition: util.cpp:928
static QFont::Weight fontWeightNormal
Definition: guiutil.cpp:138
void showBackups()
Definition: guiutil.cpp:670
void clicked(const QPoint &point)
Emitted when the progressbar is clicked.
QString formatNiceTimeOffset(qint64 secs)
Definition: guiutil.cpp:1898
int getFontScale()
Definition: guiutil.cpp:1393
void loadStyleSheet(QWidget *widget, bool fForceUpdate)
Updates the widgets stylesheet and adds it to the list of ui debug elements.
Definition: guiutil.cpp:1155
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:501
QString boostPathToQString(const fs::path &path)
Definition: guiutil.cpp:1828
void setFontFamily(FontFamily family)
Definition: guiutil.cpp:1296
static const std::string DEFAULT_UIPLATFORM
Definition: bitcoingui.h:55
Bitcoin address widget validator, checks for a valid bitcoin address.
static FontFamily fontFamily
Font related variables.
Definition: guiutil.cpp:134
static const uint8_t dummydata[]
Definition: guiutil.cpp:269
static const QFont::Weight defaultFontWeightBold
Definition: guiutil.cpp:128
static QFont::Weight fontWeightBold
Definition: guiutil.cpp:140
void setApplicationFont()
Set an application wide default font, depends on the selected theme.
Definition: guiutil.cpp:1522
void resizeColumn(int nColumnIndex, int width)
Definition: guiutil.cpp:767
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string.
Wrapped mutex: supports recursive locking, but no waiting TODO: We should move away from using the re...
Definition: sync.h:94
static const QString traditionalTheme
Definition: guiutil.cpp:103
const std::vector< unsigned char > & Base58Prefix(Base58Type type) const
Definition: chainparams.h:79
static const FontFamily defaultFontFamily
Font related default values.
Definition: guiutil.cpp:119
CFeeRate dustRelayFee
Definition: policy.cpp:177
Released under the MIT license