32 #include <QAbstractItemView>
33 #include <QApplication>
36 #include <QDesktopServices>
37 #include <QDoubleValidator>
38 #include <QFileDialog>
40 #include <QFontDatabase>
41 #include <QFontMetrics>
42 #include <QGuiApplication>
47 #include <QMouseEvent>
48 #include <QProgressDialog>
54 #include <QTextDocument>
70 return date.date().toString(Qt::SystemLocaleShortDate) + QString(
" ") + date.toString(
"hh:mm");
75 return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
80 return QFontDatabase::systemFont(QFontDatabase::FixedFont);
84 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};
91 for(
int i=0; i<256; ++i) {
96 sourcedata[sourcedata.size()-1] += 1;
103 parent->setFocusProxy(widget);
108 widget->setPlaceholderText(QObject::tr(
"Enter a Bitcoin address (e.g. %1)").arg(
117 if(!uri.isValid() || uri.scheme() != QString(
"bitcoin"))
123 if (rv.
address.endsWith(
"/")) {
128 QUrlQuery uriQuery(uri);
129 QList<QPair<QString, QString> > items = uriQuery.queryItems();
130 for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
132 bool fShouldReturnFalse =
false;
133 if (i->first.startsWith(
"req-"))
135 i->first.remove(0, 4);
136 fShouldReturnFalse =
true;
139 if (i->first ==
"label")
141 rv.
label = i->second;
142 fShouldReturnFalse =
false;
144 if (i->first ==
"message")
147 fShouldReturnFalse =
false;
149 else if (i->first ==
"amount")
151 if(!i->second.isEmpty())
158 fShouldReturnFalse =
false;
161 if (fShouldReturnFalse)
173 QUrl uriInstance(uri);
179 bool bech_32 = info.
address.startsWith(QString::fromStdString(
Params().Bech32HRP() +
"1"));
181 QString ret = QString(
"bitcoin:%1").arg(bech_32 ? info.
address.toUpper() : info.
address);
190 if (!info.
label.isEmpty())
192 QString lbl(QUrl::toPercentEncoding(info.
label));
193 ret += QString(
"%1label=%2").arg(paramCount == 0 ?
"?" :
"&").arg(lbl);
199 QString msg(QUrl::toPercentEncoding(info.
message));
200 ret += QString(
"%1message=%2").arg(paramCount == 0 ?
"?" :
"&").arg(msg);
211 CTxOut txOut(amount, script);
217 QString escaped = str.toHtmlEscaped();
220 escaped = escaped.replace(
"\n",
"<br>\n");
227 return HtmlEscape(QString::fromStdString(str), fMultiLine);
232 if(!view || !view->selectionModel())
234 QModelIndexList selection = view->selectionModel()->selectedRows(column);
236 if(!selection.isEmpty())
243 QList<QModelIndex>
getEntryData(
const QAbstractItemView *view,
int column)
245 if(!view || !view->selectionModel())
246 return QList<QModelIndex>();
247 return view->selectionModel()->selectedRows(column);
253 if (selection.isEmpty())
return false;
254 return !selection.at(0).data(role).toString().isEmpty();
263 const QString &filter,
264 QString *selectedSuffixOut)
266 QString selectedFilter;
270 myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
280 QRegExp filter_re(
".* \\(\\*\\.(.*)[ \\)]");
281 QString selectedSuffix;
282 if(filter_re.exactMatch(selectedFilter))
284 selectedSuffix = filter_re.cap(1);
288 QFileInfo info(result);
289 if(!result.isEmpty())
291 if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
294 if(!result.endsWith(
"."))
296 result.append(selectedSuffix);
301 if(selectedSuffixOut)
303 *selectedSuffixOut = selectedSuffix;
309 const QString &filter,
310 QString *selectedSuffixOut)
312 QString selectedFilter;
316 myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
325 if(selectedSuffixOut)
328 QRegExp filter_re(
".* \\(\\*\\.(.*)[ \\)]");
329 QString selectedSuffix;
330 if(filter_re.exactMatch(selectedFilter))
332 selectedSuffix = filter_re.cap(1);
334 *selectedSuffixOut = selectedSuffix;
341 if(QThread::currentThread() != qApp->thread())
343 return Qt::BlockingQueuedConnection;
347 return Qt::DirectConnection;
353 QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p));
354 if (!atW)
return false;
355 return atW->window() == w;
363 &&
checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
364 &&
checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
375 if (w->isMinimized()) {
387 QObject::connect(
new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), w), &QShortcut::activated, w, &QWidget::close);
392 fs::path pathDebug =
GetDataDir() /
"debug.log";
395 if (fs::exists(pathDebug))
406 if (!configFile.good())
412 bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(
boostPathToQString(pathConfig)));
416 res = QProcess::startDetached(
"/usr/bin/open", QStringList{
"-t",
boostPathToQString(pathConfig)});
425 size_threshold(_size_threshold)
432 if(evt->type() == QEvent::ToolTipChange)
434 QWidget *widget =
static_cast<QWidget*
>(obj);
435 QString tooltip = widget->toolTip();
436 if(tooltip.size() >
size_threshold && !tooltip.startsWith(
"<qt") && !Qt::mightBeRichText(tooltip))
440 tooltip =
"<qt>" +
HtmlEscape(tooltip,
true) +
"</qt>";
441 widget->setToolTip(tooltip);
445 return QObject::eventFilter(obj, evt);
455 if (event->type() == QEvent::FocusOut) {
456 auto focus_out =
static_cast<QFocusEvent*
>(event);
457 if (focus_out->reason() != Qt::PopupFocusReason) {
458 auto label = qobject_cast<QLabel*>(watched);
460 auto flags = label->textInteractionFlags();
461 label->setTextInteractionFlags(Qt::NoTextInteraction);
462 label->setTextInteractionFlags(
flags);
467 return QObject::eventFilter(watched, event);
487 tableView->horizontalHeader()->setSectionResizeMode(logicalIndex, resizeMode);
492 tableView->setColumnWidth(nColumnIndex, width);
493 tableView->horizontalHeader()->resizeSection(nColumnIndex, width);
498 int nColumnsWidthSum = 0;
501 nColumnsWidthSum +=
tableView->horizontalHeader()->sectionSize(i);
503 return nColumnsWidthSum;
509 int nTableWidth =
tableView->horizontalHeader()->width();
514 nResult = std::max(nResult, nTableWidth - nOtherColsWidth);
527 int nTableWidth =
tableView->horizontalHeader()->width();
529 if (nColsWidth > nTableWidth)
548 if (newSize > remainingWidth)
573 lastColumnMinimumWidth(lastColMinimumWidth),
574 allColumnsMinimumWidth(allColsMinimumWidth)
585 fs::path
static StartupShortcutPath()
589 return GetSpecialFolderPath(CSIDL_STARTUP) /
"Bitcoin.lnk";
591 return GetSpecialFolderPath(CSIDL_STARTUP) /
"Bitcoin (testnet).lnk";
592 return GetSpecialFolderPath(CSIDL_STARTUP) /
strprintf(
"Bitcoin (%s).lnk", chain);
598 return fs::exists(StartupShortcutPath());
604 fs::remove(StartupShortcutPath());
608 CoInitialize(
nullptr);
611 IShellLinkW* psl =
nullptr;
612 HRESULT hres = CoCreateInstance(CLSID_ShellLink,
nullptr,
613 CLSCTX_INPROC_SERVER, IID_IShellLinkW,
614 reinterpret_cast<void**>(&psl));
620 GetModuleFileNameW(
nullptr, pszExePath, ARRAYSIZE(pszExePath));
623 QString strArgs =
"-min";
628 psl->SetPath(pszExePath);
629 PathRemoveFileSpecW(pszExePath);
630 psl->SetWorkingDirectory(pszExePath);
631 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
632 psl->SetArguments(strArgs.toStdWString().c_str());
636 IPersistFile* ppf =
nullptr;
637 hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&ppf));
641 hres = ppf->Save(StartupShortcutPath().wstring().c_str(), TRUE);
654 #elif defined(Q_OS_LINUX)
659 fs::path
static GetAutostartDir()
661 char* pszConfigHome = getenv(
"XDG_CONFIG_HOME");
662 if (pszConfigHome)
return fs::path(pszConfigHome) /
"autostart";
663 char* pszHome = getenv(
"HOME");
664 if (pszHome)
return fs::path(pszHome) /
".config" /
"autostart";
668 fs::path
static GetAutostartFilePath()
672 return GetAutostartDir() /
"bitcoin.desktop";
673 return GetAutostartDir() /
strprintf(
"bitcoin-%s.desktop", chain);
679 if (!optionFile.good())
683 while (!optionFile.eof())
685 getline(optionFile, line);
686 if (line.find(
"Hidden") != std::string::npos &&
687 line.find(
"true") != std::string::npos)
698 fs::remove(GetAutostartFilePath());
702 ssize_t r = readlink(
"/proc/self/exe", pszExePath,
sizeof(pszExePath) - 1);
705 pszExePath[r] =
'\0';
707 fs::create_directories(GetAutostartDir());
709 fsbridge::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc);
710 if (!optionFile.good())
714 optionFile <<
"[Desktop Entry]\n";
715 optionFile <<
"Type=Application\n";
717 optionFile <<
"Name=Bitcoin\n";
719 optionFile <<
strprintf(
"Name=Bitcoin (%s)\n", chain);
720 optionFile <<
"Exec=" << pszExePath <<
strprintf(
" -min -chain=%s\n", chain);
721 optionFile <<
"Terminal=false\n";
722 optionFile <<
"Hidden=false\n";
737 QApplication::clipboard()->setText(str, QClipboard::Clipboard);
738 QApplication::clipboard()->setText(str, QClipboard::Selection);
743 return fs::path(path.toStdString());
748 return QString::fromStdString(path.string());
754 int days = secs / 86400;
755 int hours = (secs % 86400) / 3600;
756 int mins = (secs % 3600) / 60;
757 int seconds = secs % 60;
760 strList.append(QString(QObject::tr(
"%1 d")).arg(days));
762 strList.append(QString(QObject::tr(
"%1 h")).arg(hours));
764 strList.append(QString(QObject::tr(
"%1 m")).arg(mins));
765 if (seconds || (!days && !hours && !mins))
766 strList.append(QString(QObject::tr(
"%1 s")).arg(seconds));
768 return strList.join(
" ");
776 strList.append(QString::fromStdString(flag));
780 return strList.join(
" & ");
782 return QObject::tr(
"None");
787 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));
792 return QString(QObject::tr(
"%1 s")).arg(QString::number((
int)nTimeOffset, 10));
798 QString timeBehindText;
799 const int HOUR_IN_SECONDS = 60*60;
800 const int DAY_IN_SECONDS = 24*60*60;
801 const int WEEK_IN_SECONDS = 7*24*60*60;
802 const int YEAR_IN_SECONDS = 31556952;
805 timeBehindText = QObject::tr(
"%n second(s)",
"",secs);
807 else if(secs < 2*HOUR_IN_SECONDS)
809 timeBehindText = QObject::tr(
"%n minute(s)",
"",secs/60);
811 else if(secs < 2*DAY_IN_SECONDS)
813 timeBehindText = QObject::tr(
"%n hour(s)",
"",secs/HOUR_IN_SECONDS);
815 else if(secs < 2*WEEK_IN_SECONDS)
817 timeBehindText = QObject::tr(
"%n day(s)",
"",secs/DAY_IN_SECONDS);
819 else if(secs < YEAR_IN_SECONDS)
821 timeBehindText = QObject::tr(
"%n week(s)",
"",secs/WEEK_IN_SECONDS);
825 qint64 years = secs / YEAR_IN_SECONDS;
826 qint64 remainder = secs % YEAR_IN_SECONDS;
827 timeBehindText = QObject::tr(
"%1 and %2").arg(QObject::tr(
"%n year(s)",
"", years)).arg(QObject::tr(
"%n week(s)",
"", remainder/WEEK_IN_SECONDS));
829 return timeBehindText;
835 return QString(QObject::tr(
"%1 B")).arg(bytes);
836 if(bytes < 1024 * 1024)
837 return QString(QObject::tr(
"%1 KB")).arg(bytes / 1024);
838 if(bytes < 1024 * 1024 * 1024)
839 return QString(QObject::tr(
"%1 MB")).arg(bytes / 1024 / 1024);
841 return QString(QObject::tr(
"%1 GB")).arg(bytes / 1024 / 1024 / 1024);
845 while(font_size >= minPointSize) {
846 font.setPointSizeF(font_size);
847 QFontMetrics fm(font);
868 if (event->type() == QEvent::KeyPress) {
869 if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
873 return QItemDelegate::eventFilter(
object, event);
880 const int margin =
TextWidth(dialog->fontMetrics(), (
"X"));
881 dialog->resize(dialog->width() + 2 * margin, dialog->height());
888 int TextWidth(
const QFontMetrics& fm,
const QString& text)
890 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
891 return fm.horizontalAdvance(text);
893 return fm.width(text);
900 const std::string qt_link{
"static"};
902 const std::string qt_link{
"dynamic"};
904 #ifdef QT_STATICPLUGIN
905 const std::string plugin_link{
"static"};
907 const std::string plugin_link{
"dynamic"};
909 LogPrintf(
"Qt %s (%s), plugin=%s (%s)\n", qVersion(), qt_link, QGuiApplication::platformName().toStdString(), plugin_link);
910 LogPrintf(
"System: %s, %s\n", QSysInfo::prettyProductName().toStdString(), QSysInfo::buildAbi().toStdString());
911 for (
const QScreen* s : QGuiApplication::screens()) {
912 LogPrintf(
"Screen: %s %dx%d, pixel ratio=%.1f\n", s->name().toStdString(), s->size().width(), s->size().height(), s->devicePixelRatio());
916 void PopupMenu(QMenu* menu,
const QPoint& point, QAction* at_action)
919 if (QApplication::platformName() ==
"minimal")
return;
920 menu->popup(point, at_action);
void stretchColumnWidth(int column)
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.
fs::path GetDefaultDataDir()
void mouseReleaseEvent(QMouseEvent *event) override
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...
static void LogPrintf(const char *fmt, const Args &...args)
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)
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()
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)
const std::vector< unsigned char > & Base58Prefix(Base58Type type) const
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
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
bool isObscured(QWidget *w)
QString formatPingTime(int64_t ping_usec)
qreal calculateIdealFontSize(int width, const QString &text, QFont font, qreal minPointSize, qreal font_size)
QString formatDurationStr(int secs)
void setClipboard(const QString &str)
void handleCloseWindowShortcut(QWidget *w)
int lastColumnMinimumWidth
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.
void clicked(const QPoint &point)
Emitted when the label is clicked.
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)
CTxDestination DecodeDestination(const std::string &str)
void disconnectViewHeadersSignals()
int allColumnsMinimumWidth
fs::path GetConfigFile(const std::string &confPath)
fs::path qstringToBoostPath(const QString &path)
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.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
QString formatServicesStr(quint64 mask)
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)
void ForceActivation()
Force application activation on macOS.
void adjustTableColumnsWidth()
bool checkPoint(const QPoint &p, const QWidget *w)
void setCheckValidator(const QValidator *v)
static const std::string TESTNET
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
bool SetStartOnSystemStartup(bool fAutoStart)
std::string GetChainName() const
Returns the appropriate chain name from the program arguments.
void clicked(const QPoint &point)
Emitted when the progressbar is clicked.
QString formatNiceTimeOffset(qint64 secs)
boost::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
QString getDefaultDataDirectory()
Determine default data directory for operating system.
QString boostPathToQString(const fs::path &path)
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)
QList< QModelIndex > getEntryData(const QAbstractItemView *view, int column)
Return a field of the currently selected entry as a QString.