32 #include <QAbstractItemView> 33 #include <QApplication> 36 #include <QDesktopServices> 37 #include <QDoubleValidator> 38 #include <QFileDialog> 40 #include <QFontDatabase> 41 #include <QFontMetrics> 42 #include <QGuiApplication> 48 #include <QMouseEvent> 49 #include <QProgressDialog> 55 #include <QTextDocument> 71 return QLocale::system().toString(date.date(), QLocale::ShortFormat) + QString(
" ") + date.toString(
"hh:mm");
76 return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
81 return QFontDatabase::systemFont(QFontDatabase::FixedFont);
85 static const uint8_t
dummydata[] = {0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47};
92 for(
int i=0; i<256; ++i) {
97 sourcedata[sourcedata.size()-1] += 1;
104 parent->setFocusProxy(widget);
109 widget->setPlaceholderText(QObject::tr(
"Enter a Bitcoin address (e.g. %1)").arg(
118 if(!uri.isValid() || uri.scheme() != QString(
"bitcoin"))
124 if (rv.
address.endsWith(
"/")) {
129 QUrlQuery uriQuery(uri);
130 QList<QPair<QString, QString> > items = uriQuery.queryItems();
131 for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
133 bool fShouldReturnFalse =
false;
134 if (i->first.startsWith(
"req-"))
136 i->first.remove(0, 4);
137 fShouldReturnFalse =
true;
140 if (i->first ==
"label")
142 rv.
label = i->second;
143 fShouldReturnFalse =
false;
145 if (i->first ==
"message")
148 fShouldReturnFalse =
false;
150 else if (i->first ==
"amount")
152 if(!i->second.isEmpty())
159 fShouldReturnFalse =
false;
162 if (fShouldReturnFalse)
174 QUrl uriInstance(uri);
180 bool bech_32 = info.
address.startsWith(QString::fromStdString(
Params().Bech32HRP() +
"1"));
182 QString ret = QString(
"bitcoin:%1").arg(bech_32 ? info.
address.toUpper() : info.
address);
191 if (!info.
label.isEmpty())
193 QString lbl(QUrl::toPercentEncoding(info.
label));
194 ret += QString(
"%1label=%2").arg(paramCount == 0 ?
"?" :
"&").arg(lbl);
200 QString msg(QUrl::toPercentEncoding(info.
message));
201 ret += QString(
"%1message=%2").arg(paramCount == 0 ?
"?" :
"&").arg(msg);
212 CTxOut txOut(amount, script);
218 QString escaped = str.toHtmlEscaped();
221 escaped = escaped.replace(
"\n",
"<br>\n");
228 return HtmlEscape(QString::fromStdString(str), fMultiLine);
233 if(!view || !view->selectionModel())
235 QModelIndexList selection = view->selectionModel()->selectedRows(column);
237 if(!selection.isEmpty())
244 QList<QModelIndex>
getEntryData(
const QAbstractItemView *view,
int column)
246 if(!view || !view->selectionModel())
247 return QList<QModelIndex>();
248 return view->selectionModel()->selectedRows(column);
254 if (selection.isEmpty())
return false;
255 return !selection.at(0).data(role).toString().isEmpty();
264 const QString &filter,
265 QString *selectedSuffixOut)
267 QString selectedFilter;
271 myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
281 QRegExp filter_re(
".* \\(\\*\\.(.*)[ \\)]");
282 QString selectedSuffix;
283 if(filter_re.exactMatch(selectedFilter))
285 selectedSuffix = filter_re.cap(1);
289 QFileInfo info(result);
290 if(!result.isEmpty())
292 if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
295 if(!result.endsWith(
"."))
297 result.append(selectedSuffix);
302 if(selectedSuffixOut)
304 *selectedSuffixOut = selectedSuffix;
310 const QString &filter,
311 QString *selectedSuffixOut)
313 QString selectedFilter;
317 myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
326 if(selectedSuffixOut)
329 QRegExp filter_re(
".* \\(\\*\\.(.*)[ \\)]");
330 QString selectedSuffix;
331 if(filter_re.exactMatch(selectedFilter))
333 selectedSuffix = filter_re.cap(1);
335 *selectedSuffixOut = selectedSuffix;
342 if(QThread::currentThread() != qApp->thread())
344 return Qt::BlockingQueuedConnection;
348 return Qt::DirectConnection;
354 QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p));
355 if (!atW)
return false;
356 return atW->window() == w;
364 &&
checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
365 &&
checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
376 if (w->isMinimized()) {
388 QObject::connect(
new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), w), &QShortcut::activated, w, &QWidget::close);
393 fs::path pathDebug =
GetDataDir() /
"debug.log";
396 if (fs::exists(pathDebug))
407 if (!configFile.good())
413 bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(
boostPathToQString(pathConfig)));
417 res = QProcess::startDetached(
"/usr/bin/open", QStringList{
"-t",
boostPathToQString(pathConfig)});
426 size_threshold(_size_threshold)
433 if(evt->type() == QEvent::ToolTipChange)
435 QWidget *widget =
static_cast<QWidget*
>(obj);
436 QString tooltip = widget->toolTip();
437 if(tooltip.size() >
size_threshold && !tooltip.startsWith(
"<qt") && !Qt::mightBeRichText(tooltip))
441 tooltip =
"<qt>" +
HtmlEscape(tooltip,
true) +
"</qt>";
442 widget->setToolTip(tooltip);
446 return QObject::eventFilter(obj, evt);
456 if (event->type() == QEvent::FocusOut) {
457 auto focus_out =
static_cast<QFocusEvent*
>(event);
458 if (focus_out->reason() != Qt::PopupFocusReason) {
459 auto label = qobject_cast<QLabel*>(watched);
461 auto flags = label->textInteractionFlags();
462 label->setTextInteractionFlags(Qt::NoTextInteraction);
463 label->setTextInteractionFlags(
flags);
468 return QObject::eventFilter(watched, event);
488 tableView->horizontalHeader()->setSectionResizeMode(logicalIndex, resizeMode);
493 tableView->setColumnWidth(nColumnIndex, width);
494 tableView->horizontalHeader()->resizeSection(nColumnIndex, width);
499 int nColumnsWidthSum = 0;
500 for (
int i = 0; i < columnCount; i++)
502 nColumnsWidthSum += tableView->horizontalHeader()->sectionSize(i);
504 return nColumnsWidthSum;
509 int nResult = lastColumnMinimumWidth;
510 int nTableWidth = tableView->horizontalHeader()->width();
514 int nOtherColsWidth = getColumnsWidth() - tableView->horizontalHeader()->sectionSize(column);
515 nResult = std::max(nResult, nTableWidth - nOtherColsWidth);
524 disconnectViewHeadersSignals();
525 resizeColumn(lastColumnIndex, getAvailableWidthForColumn(lastColumnIndex));
526 connectViewHeadersSignals();
528 int nTableWidth = tableView->horizontalHeader()->width();
529 int nColsWidth = getColumnsWidth();
530 if (nColsWidth > nTableWidth)
532 resizeColumn(secondToLastColumnIndex,getAvailableWidthForColumn(secondToLastColumnIndex));
539 disconnectViewHeadersSignals();
540 resizeColumn(column, getAvailableWidthForColumn(column));
541 connectViewHeadersSignals();
547 adjustTableColumnsWidth();
548 int remainingWidth = getAvailableWidthForColumn(logicalIndex);
549 if (newSize > remainingWidth)
551 resizeColumn(logicalIndex, remainingWidth);
559 if ((getColumnsWidth() - this->tableView->horizontalHeader()->width()) != 0)
561 disconnectViewHeadersSignals();
562 resizeColumn(secondToLastColumnIndex, getAvailableWidthForColumn(secondToLastColumnIndex));
563 connectViewHeadersSignals();
574 lastColumnMinimumWidth(lastColMinimumWidth),
575 allColumnsMinimumWidth(allColsMinimumWidth)
586 fs::path
static StartupShortcutPath()
590 return GetSpecialFolderPath(CSIDL_STARTUP) /
"Bitcoin.lnk";
592 return GetSpecialFolderPath(CSIDL_STARTUP) /
"Bitcoin (testnet).lnk";
593 return GetSpecialFolderPath(CSIDL_STARTUP) /
strprintf(
"Bitcoin (%s).lnk", chain);
599 return fs::exists(StartupShortcutPath());
605 fs::remove(StartupShortcutPath());
609 CoInitialize(
nullptr);
612 IShellLinkW* psl =
nullptr;
613 HRESULT hres = CoCreateInstance(CLSID_ShellLink,
nullptr,
614 CLSCTX_INPROC_SERVER, IID_IShellLinkW,
615 reinterpret_cast<void**>(&psl));
621 GetModuleFileNameW(
nullptr, pszExePath, ARRAYSIZE(pszExePath));
624 QString strArgs =
"-min";
629 psl->SetPath(pszExePath);
630 PathRemoveFileSpecW(pszExePath);
631 psl->SetWorkingDirectory(pszExePath);
632 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
633 psl->SetArguments(strArgs.toStdWString().c_str());
637 IPersistFile* ppf =
nullptr;
638 hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&ppf));
642 hres = ppf->Save(StartupShortcutPath().wstring().c_str(), TRUE);
655 #elif defined(Q_OS_LINUX) 660 fs::path
static GetAutostartDir()
662 char* pszConfigHome = getenv(
"XDG_CONFIG_HOME");
663 if (pszConfigHome)
return fs::path(pszConfigHome) /
"autostart";
664 char* pszHome = getenv(
"HOME");
665 if (pszHome)
return fs::path(pszHome) /
".config" /
"autostart";
669 fs::path
static GetAutostartFilePath()
673 return GetAutostartDir() /
"bitcoin.desktop";
674 return GetAutostartDir() /
strprintf(
"bitcoin-%s.desktop", chain);
680 if (!optionFile.good())
684 while (!optionFile.eof())
686 getline(optionFile, line);
687 if (line.find(
"Hidden") != std::string::npos &&
688 line.find(
"true") != std::string::npos)
699 fs::remove(GetAutostartFilePath());
703 ssize_t r = readlink(
"/proc/self/exe", pszExePath,
sizeof(pszExePath) - 1);
706 pszExePath[r] =
'\0';
708 fs::create_directories(GetAutostartDir());
710 fsbridge::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc);
711 if (!optionFile.good())
715 optionFile <<
"[Desktop Entry]\n";
716 optionFile <<
"Type=Application\n";
718 optionFile <<
"Name=Bitcoin\n";
720 optionFile <<
strprintf(
"Name=Bitcoin (%s)\n", chain);
721 optionFile <<
"Exec=" << pszExePath <<
strprintf(
" -min -chain=%s\n", chain);
722 optionFile <<
"Terminal=false\n";
723 optionFile <<
"Hidden=false\n";
738 QApplication::clipboard()->setText(str, QClipboard::Clipboard);
739 QApplication::clipboard()->setText(str, QClipboard::Selection);
744 return fs::path(path.toStdString());
749 return QString::fromStdString(path.string());
783 int days = secs / 86400;
784 int hours = (secs % 86400) / 3600;
785 int mins = (secs % 3600) / 60;
786 int seconds = secs % 60;
789 strList.append(QString(QObject::tr(
"%1 d")).arg(days));
791 strList.append(QString(QObject::tr(
"%1 h")).arg(hours));
793 strList.append(QString(QObject::tr(
"%1 m")).arg(mins));
794 if (seconds || (!days && !hours && !mins))
795 strList.append(QString(QObject::tr(
"%1 s")).arg(seconds));
797 return strList.join(
" ");
805 strList.append(QString::fromStdString(flag));
809 return strList.join(
" & ");
811 return QObject::tr(
"None");
816 return (ping_usec == std::numeric_limits<int64_t>::max() || ping_usec == 0) ? QObject::tr(
"N/A") : QString(QObject::tr(
"%1 ms")).arg(QString::number((
int)(ping_usec / 1000), 10));
821 return QString(QObject::tr(
"%1 s")).arg(QString::number((
int)nTimeOffset, 10));
827 QString timeBehindText;
828 const int HOUR_IN_SECONDS = 60*60;
829 const int DAY_IN_SECONDS = 24*60*60;
830 const int WEEK_IN_SECONDS = 7*24*60*60;
831 const int YEAR_IN_SECONDS = 31556952;
834 timeBehindText = QObject::tr(
"%n second(s)",
"",secs);
836 else if(secs < 2*HOUR_IN_SECONDS)
838 timeBehindText = QObject::tr(
"%n minute(s)",
"",secs/60);
840 else if(secs < 2*DAY_IN_SECONDS)
842 timeBehindText = QObject::tr(
"%n hour(s)",
"",secs/HOUR_IN_SECONDS);
844 else if(secs < 2*WEEK_IN_SECONDS)
846 timeBehindText = QObject::tr(
"%n day(s)",
"",secs/DAY_IN_SECONDS);
848 else if(secs < YEAR_IN_SECONDS)
850 timeBehindText = QObject::tr(
"%n week(s)",
"",secs/WEEK_IN_SECONDS);
854 qint64 years = secs / YEAR_IN_SECONDS;
855 qint64 remainder = secs % YEAR_IN_SECONDS;
856 timeBehindText = QObject::tr(
"%1 and %2").arg(QObject::tr(
"%n year(s)",
"", years)).arg(QObject::tr(
"%n week(s)",
"", remainder/WEEK_IN_SECONDS));
858 return timeBehindText;
864 return QString(QObject::tr(
"%1 B")).arg(bytes);
865 if(bytes < 1024 * 1024)
866 return QString(QObject::tr(
"%1 KB")).arg(bytes / 1024);
867 if(bytes < 1024 * 1024 * 1024)
868 return QString(QObject::tr(
"%1 MB")).arg(bytes / 1024 / 1024);
870 return QString(QObject::tr(
"%1 GB")).arg(bytes / 1024 / 1024 / 1024);
874 while(font_size >= minPointSize) {
875 font.setPointSizeF(font_size);
876 QFontMetrics fm(font);
887 Q_EMIT clicked(event->pos());
892 Q_EMIT clicked(event->pos());
897 if (event->type() == QEvent::KeyPress) {
898 if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
899 Q_EMIT keyEscapePressed();
902 return QItemDelegate::eventFilter(
object, event);
909 const int margin =
TextWidth(dialog->fontMetrics(), (
"X"));
910 dialog->resize(dialog->width() + 2 * margin, dialog->height());
917 int TextWidth(
const QFontMetrics& fm,
const QString& text)
919 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) 920 return fm.horizontalAdvance(text);
922 return fm.width(text);
929 const std::string qt_link{
"static"};
931 const std::string qt_link{
"dynamic"};
933 #ifdef QT_STATICPLUGIN 934 const std::string plugin_link{
"static"};
936 const std::string plugin_link{
"dynamic"};
938 LogPrintf(
"Qt %s (%s), plugin=%s (%s)\n", qVersion(), qt_link, QGuiApplication::platformName().toStdString(), plugin_link);
939 LogPrintf(
"System: %s, %s\n", QSysInfo::prettyProductName().toStdString(), QSysInfo::buildAbi().toStdString());
940 for (
const QScreen* s : QGuiApplication::screens()) {
941 LogPrintf(
"Screen: %s %dx%d, pixel ratio=%.1f\n", s->name().toStdString(), s->size().width(), s->size().height(), s->devicePixelRatio());
945 void PopupMenu(QMenu* menu,
const QPoint& point, QAction* at_action)
948 if (QApplication::platformName() ==
"minimal")
return;
949 menu->popup(point, at_action);
954 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 955 return date.startOfDay();
957 return QDateTime(date);
963 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) 964 return !label->pixmap(Qt::ReturnByValue).isNull();
966 return label->pixmap() !=
nullptr;
976 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) 977 return label->pixmap(Qt::ReturnByValue).toImage();
979 return label->pixmap()->toImage();
QString ConnectionTypeToQString(ConnectionType conn_type)
Convert enum ConnectionType to QString.
void stretchColumnWidth(int column)
QDateTime StartOfDay(const QDate &date)
Returns the start-moment of the day in local time.
ToolTipToRichTextFilter(int size_threshold, QObject *parent=nullptr)
bool eventFilter(QObject *obj, QEvent *evt) override
Utility functions used by the Bitcoin Qt UI.
void LogQtInfo()
Writes to debug.log short info about the used Qt and the host system.
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut)
Get open filename, convenience wrapper for QFileDialog::getOpenFileName.
A set of addresses that represent the hash of a string or FQDN.
fs::path GetDefaultDataDir()
void mouseReleaseEvent(QMouseEvent *event) override
We open manual connections to addresses that users explicitly inputted via the addnode RPC...
Dummy value to indicate the number of NET_* constants.
void setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode)
bool IsValidDestinationString(const std::string &str, const CChainParams ¶ms)
void PopupMenu(QMenu *menu, const QPoint &point, QAction *at_action)
Call QMenu::popup() only on supported QT_QPA_PLATFORM.
void on_geometriesChanged()
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text...
bool isDust(interfaces::Node &node, const QString &address, const CAmount &amount)
int secondToLastColumnIndex
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD, bool justify=false)
Format as string.
QString dateTimeStr(const QDateTime &date)
Qt::ConnectionType blockingGUIThreadConnection()
Get connection type to call object slot in GUI thread with invokeMethod.
QString formatBytes(uint64_t bytes)
QString formatTimeOffset(int64_t nTimeOffset)
Format a CNodeCombinedStats.nTimeOffset into a user-readable string.
static void LogPrintf(const char *fmt, const Args &... args)
bool GetStartOnSystemStartup()
std::string EncodeBase58(Span< const unsigned char > input)
Why base-58 instead of standard base-64 encoding?
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system...
QString HtmlEscape(const QString &str, bool fMultiLine)
std::vector< std::string > serviceFlagsToStr(uint64_t flags)
Convert service flags (a bitmask of NODE_*) to human readable strings.
void connectViewHeadersSignals()
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Line edit that can be marked as "invalid" to show input validation feedback.
int getAvailableWidthForColumn(int column)
const char *const BITCOIN_CONF_FILENAME
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
virtual CFeeRate getDustRelayFee()=0
Get dust relay fee.
QString formatBitcoinURI(const SendCoinsRecipient &info)
static bool parse(int unit, const QString &value, CAmount *val_out)
Parse string to coin amount.
void bringToFront(QWidget *w)
static const std::string MAIN
Chain name strings.
TableViewLastColumnResizingFixer(QTableView *table, int lastColMinimumWidth, int allColsMinimumWidth, QObject *parent)
Initializes all internal variables and prepares the the resize modes of the last 2 columns of the tab...
bool eventFilter(QObject *object, QEvent *event) override
int64_t CAmount
Amount in satoshis (Can be negative)
void mouseReleaseEvent(QMouseEvent *event) override
QString NetworkToQString(Network net)
Convert enum Network to QString.
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
bool isObscured(QWidget *w)
QString formatPingTime(int64_t ping_usec)
Format a CNodeStats.m_ping_usec into a user-readable string or display N/A, if 0. ...
qreal calculateIdealFontSize(int width, const QString &text, QFont font, qreal minPointSize, qreal font_size)
QString formatDurationStr(int secs)
Convert seconds into a QString with days, hours, mins, secs.
void setClipboard(const QString &str)
bool HasPixmap(const QLabel *label)
Returns true if pixmap has been set.
void handleCloseWindowShortcut(QWidget *w)
const fs::path & GetDataDir(bool fNetSpecific)
bool eventFilter(QObject *watched, QEvent *event) override
bool hasEntryData(const QAbstractItemView *view, int column, int role)
Returns true if the specified field of the currently selected view entry is not empty.
Base58 entry widget validator, checks for valid characters and removes some whitespace.
An output of a transaction.
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
void copyEntryData(const QAbstractItemView *view, int column, int role)
Copy a field of the currently selected entry of a view to the clipboard.
void PolishProgressDialog(QProgressDialog *dialog)
void on_sectionResized(int logicalIndex, int oldSize, int newSize)
Inbound connections are those initiated by a peer.
CTxDestination DecodeDestination(const std::string &str)
QImage GetImage(const QLabel *label)
void disconnectViewHeadersSignals()
int allColumnsMinimumWidth
fs::path GetConfigFile(const std::string &confPath)
fs::path qstringToBoostPath(const QString &path)
Convert QString to OS specific boost path through UTF-8.
ConnectionType
Different types of connections to a peer.
LabelOutOfFocusEventFilter(QObject *parent)
const CChainParams & Params()
Return the currently selected parameters.
Serialized script, used inside transaction inputs and outputs.
Feeler connections are short-lived connections made to check that a node is alive.
QString formatServicesStr(quint64 mask)
Format CNodeStats.nServices bitmask into a user-readable string.
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 ...
static std::string DummyAddress(const CChainParams ¶ms)
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
void ForceActivation()
Force application activation on macOS.
void adjustTableColumnsWidth()
bool checkPoint(const QPoint &p, const QWidget *w)
void setCheckValidator(const QValidator *v)
These are the default connections that we use to connect with the network.
static const std::string TESTNET
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
std::string GetChainName() const
Returns the appropriate chain name from the program arguments.
bool SetStartOnSystemStartup(bool fAutoStart)
QString formatNiceTimeOffset(qint64 secs)
We use block-relay-only connections to help prevent against partition attacks.
QString getDefaultDataDirectory()
Determine default data directory for operating system.
QString boostPathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
AddrFetch connections are short lived connections used to solicit addresses from peers.
Bitcoin address widget validator, checks for a valid bitcoin address.
Top-level interface for a bitcoin node (bitcoind process).
static const uint8_t dummydata[]
void resizeColumn(int nColumnIndex, int width)
const std::vector< unsigned char > & Base58Prefix(Base58Type type) const
QList< QModelIndex > getEntryData(const QAbstractItemView *view, int column)
Return a field of the currently selected entry as a QString.
Addresses from these networks are not publicly routable on the global Internet.