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;
499 for (
int i = 0; i < columnCount; i++)
501 nColumnsWidthSum += tableView->horizontalHeader()->sectionSize(i);
503 return nColumnsWidthSum;
508 int nResult = lastColumnMinimumWidth;
509 int nTableWidth = tableView->horizontalHeader()->width();
513 int nOtherColsWidth = getColumnsWidth() - tableView->horizontalHeader()->sectionSize(column);
514 nResult = std::max(nResult, nTableWidth - nOtherColsWidth);
523 disconnectViewHeadersSignals();
524 resizeColumn(lastColumnIndex, getAvailableWidthForColumn(lastColumnIndex));
525 connectViewHeadersSignals();
527 int nTableWidth = tableView->horizontalHeader()->width();
528 int nColsWidth = getColumnsWidth();
529 if (nColsWidth > nTableWidth)
531 resizeColumn(secondToLastColumnIndex,getAvailableWidthForColumn(secondToLastColumnIndex));
538 disconnectViewHeadersSignals();
539 resizeColumn(column, getAvailableWidthForColumn(column));
540 connectViewHeadersSignals();
546 adjustTableColumnsWidth();
547 int remainingWidth = getAvailableWidthForColumn(logicalIndex);
548 if (newSize > remainingWidth)
550 resizeColumn(logicalIndex, remainingWidth);
558 if ((getColumnsWidth() - this->tableView->horizontalHeader()->width()) != 0)
560 disconnectViewHeadersSignals();
561 resizeColumn(secondToLastColumnIndex, getAvailableWidthForColumn(secondToLastColumnIndex));
562 connectViewHeadersSignals();
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);
858 Q_EMIT clicked(event->pos());
863 Q_EMIT clicked(event->pos());
868 if (event->type() == QEvent::KeyPress) {
869 if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
870 Q_EMIT keyEscapePressed();
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)
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)
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.
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.