[bench] Avoid function call arguments which are pointers to uninitialized values
[bitcoinplatinum.git] / src / qt / guiutil.cpp
blobfd3dcac4248ff323199021bfb2e74371a7abdc4f
1 // Copyright (c) 2011-2016 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 #include "guiutil.h"
7 #include "bitcoinaddressvalidator.h"
8 #include "bitcoinunits.h"
9 #include "qvalidatedlineedit.h"
10 #include "walletmodel.h"
12 #include "primitives/transaction.h"
13 #include "init.h"
14 #include "policy/policy.h"
15 #include "protocol.h"
16 #include "script/script.h"
17 #include "script/standard.h"
18 #include "util.h"
20 #ifdef WIN32
21 #ifdef _WIN32_WINNT
22 #undef _WIN32_WINNT
23 #endif
24 #define _WIN32_WINNT 0x0501
25 #ifdef _WIN32_IE
26 #undef _WIN32_IE
27 #endif
28 #define _WIN32_IE 0x0501
29 #define WIN32_LEAN_AND_MEAN 1
30 #ifndef NOMINMAX
31 #define NOMINMAX
32 #endif
33 #include "shellapi.h"
34 #include "shlobj.h"
35 #include "shlwapi.h"
36 #endif
38 #include <boost/filesystem.hpp>
39 #include <boost/filesystem/fstream.hpp>
40 #include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
41 #include <boost/scoped_array.hpp>
43 #include <QAbstractItemView>
44 #include <QApplication>
45 #include <QClipboard>
46 #include <QDateTime>
47 #include <QDesktopServices>
48 #include <QDesktopWidget>
49 #include <QDoubleValidator>
50 #include <QFileDialog>
51 #include <QFont>
52 #include <QLineEdit>
53 #include <QSettings>
54 #include <QTextDocument> // for Qt::mightBeRichText
55 #include <QThread>
56 #include <QMouseEvent>
58 #if QT_VERSION < 0x050000
59 #include <QUrl>
60 #else
61 #include <QUrlQuery>
62 #endif
64 #if QT_VERSION >= 0x50200
65 #include <QFontDatabase>
66 #endif
68 static boost::filesystem::detail::utf8_codecvt_facet utf8;
70 #if defined(Q_OS_MAC)
71 extern double NSAppKitVersionNumber;
72 #if !defined(NSAppKitVersionNumber10_8)
73 #define NSAppKitVersionNumber10_8 1187
74 #endif
75 #if !defined(NSAppKitVersionNumber10_9)
76 #define NSAppKitVersionNumber10_9 1265
77 #endif
78 #endif
80 namespace GUIUtil {
82 QString dateTimeStr(const QDateTime &date)
84 return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
87 QString dateTimeStr(qint64 nTime)
89 return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
92 QFont fixedPitchFont()
94 #if QT_VERSION >= 0x50200
95 return QFontDatabase::systemFont(QFontDatabase::FixedFont);
96 #else
97 QFont font("Monospace");
98 #if QT_VERSION >= 0x040800
99 font.setStyleHint(QFont::Monospace);
100 #else
101 font.setStyleHint(QFont::TypeWriter);
102 #endif
103 return font;
104 #endif
107 // Just some dummy data to generate an convincing random-looking (but consistent) address
108 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};
110 // Generate a dummy address with invalid CRC, starting with the network prefix.
111 static std::string DummyAddress(const CChainParams &params)
113 std::vector<unsigned char> sourcedata = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
114 sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
115 for(int i=0; i<256; ++i) { // Try every trailing byte
116 std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size());
117 if (!CBitcoinAddress(s).IsValid())
118 return s;
119 sourcedata[sourcedata.size()-1] += 1;
121 return "";
124 void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
126 parent->setFocusProxy(widget);
128 widget->setFont(fixedPitchFont());
129 #if QT_VERSION >= 0x040700
130 // We don't want translators to use own addresses in translations
131 // and this is the only place, where this address is supplied.
132 widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. %1)").arg(
133 QString::fromStdString(DummyAddress(Params()))));
134 #endif
135 widget->setValidator(new BitcoinAddressEntryValidator(parent));
136 widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
139 void setupAmountWidget(QLineEdit *widget, QWidget *parent)
141 QDoubleValidator *amountValidator = new QDoubleValidator(parent);
142 amountValidator->setDecimals(8);
143 amountValidator->setBottom(0.0);
144 widget->setValidator(amountValidator);
145 widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
148 bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
150 // return if URI is not valid or is no bitcoin: URI
151 if(!uri.isValid() || uri.scheme() != QString("bitcoin"))
152 return false;
154 SendCoinsRecipient rv;
155 rv.address = uri.path();
156 // Trim any following forward slash which may have been added by the OS
157 if (rv.address.endsWith("/")) {
158 rv.address.truncate(rv.address.length() - 1);
160 rv.amount = 0;
162 #if QT_VERSION < 0x050000
163 QList<QPair<QString, QString> > items = uri.queryItems();
164 #else
165 QUrlQuery uriQuery(uri);
166 QList<QPair<QString, QString> > items = uriQuery.queryItems();
167 #endif
168 for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
170 bool fShouldReturnFalse = false;
171 if (i->first.startsWith("req-"))
173 i->first.remove(0, 4);
174 fShouldReturnFalse = true;
177 if (i->first == "label")
179 rv.label = i->second;
180 fShouldReturnFalse = false;
182 if (i->first == "message")
184 rv.message = i->second;
185 fShouldReturnFalse = false;
187 else if (i->first == "amount")
189 if(!i->second.isEmpty())
191 if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
193 return false;
196 fShouldReturnFalse = false;
199 if (fShouldReturnFalse)
200 return false;
202 if(out)
204 *out = rv;
206 return true;
209 bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
211 // Convert bitcoin:// to bitcoin:
213 // Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host,
214 // which will lower-case it (and thus invalidate the address).
215 if(uri.startsWith("bitcoin://", Qt::CaseInsensitive))
217 uri.replace(0, 10, "bitcoin:");
219 QUrl uriInstance(uri);
220 return parseBitcoinURI(uriInstance, out);
223 QString formatBitcoinURI(const SendCoinsRecipient &info)
225 QString ret = QString("bitcoin:%1").arg(info.address);
226 int paramCount = 0;
228 if (info.amount)
230 ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::separatorNever));
231 paramCount++;
234 if (!info.label.isEmpty())
236 QString lbl(QUrl::toPercentEncoding(info.label));
237 ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl);
238 paramCount++;
241 if (!info.message.isEmpty())
243 QString msg(QUrl::toPercentEncoding(info.message));
244 ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg);
245 paramCount++;
248 return ret;
251 bool isDust(const QString& address, const CAmount& amount)
253 CTxDestination dest = CBitcoinAddress(address.toStdString()).Get();
254 CScript script = GetScriptForDestination(dest);
255 CTxOut txOut(amount, script);
256 return txOut.IsDust(dustRelayFee);
259 QString HtmlEscape(const QString& str, bool fMultiLine)
261 #if QT_VERSION < 0x050000
262 QString escaped = Qt::escape(str);
263 #else
264 QString escaped = str.toHtmlEscaped();
265 #endif
266 if(fMultiLine)
268 escaped = escaped.replace("\n", "<br>\n");
270 return escaped;
273 QString HtmlEscape(const std::string& str, bool fMultiLine)
275 return HtmlEscape(QString::fromStdString(str), fMultiLine);
278 void copyEntryData(QAbstractItemView *view, int column, int role)
280 if(!view || !view->selectionModel())
281 return;
282 QModelIndexList selection = view->selectionModel()->selectedRows(column);
284 if(!selection.isEmpty())
286 // Copy first item
287 setClipboard(selection.at(0).data(role).toString());
291 QList<QModelIndex> getEntryData(QAbstractItemView *view, int column)
293 if(!view || !view->selectionModel())
294 return QList<QModelIndex>();
295 return view->selectionModel()->selectedRows(column);
298 QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
299 const QString &filter,
300 QString *selectedSuffixOut)
302 QString selectedFilter;
303 QString myDir;
304 if(dir.isEmpty()) // Default to user documents location
306 #if QT_VERSION < 0x050000
307 myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
308 #else
309 myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
310 #endif
312 else
314 myDir = dir;
316 /* Directly convert path to native OS path separators */
317 QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter));
319 /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
320 QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
321 QString selectedSuffix;
322 if(filter_re.exactMatch(selectedFilter))
324 selectedSuffix = filter_re.cap(1);
327 /* Add suffix if needed */
328 QFileInfo info(result);
329 if(!result.isEmpty())
331 if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
333 /* No suffix specified, add selected suffix */
334 if(!result.endsWith("."))
335 result.append(".");
336 result.append(selectedSuffix);
340 /* Return selected suffix if asked to */
341 if(selectedSuffixOut)
343 *selectedSuffixOut = selectedSuffix;
345 return result;
348 QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir,
349 const QString &filter,
350 QString *selectedSuffixOut)
352 QString selectedFilter;
353 QString myDir;
354 if(dir.isEmpty()) // Default to user documents location
356 #if QT_VERSION < 0x050000
357 myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
358 #else
359 myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
360 #endif
362 else
364 myDir = dir;
366 /* Directly convert path to native OS path separators */
367 QString result = QDir::toNativeSeparators(QFileDialog::getOpenFileName(parent, caption, myDir, filter, &selectedFilter));
369 if(selectedSuffixOut)
371 /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
372 QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
373 QString selectedSuffix;
374 if(filter_re.exactMatch(selectedFilter))
376 selectedSuffix = filter_re.cap(1);
378 *selectedSuffixOut = selectedSuffix;
380 return result;
383 Qt::ConnectionType blockingGUIThreadConnection()
385 if(QThread::currentThread() != qApp->thread())
387 return Qt::BlockingQueuedConnection;
389 else
391 return Qt::DirectConnection;
395 bool checkPoint(const QPoint &p, const QWidget *w)
397 QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p));
398 if (!atW) return false;
399 return atW->topLevelWidget() == w;
402 bool isObscured(QWidget *w)
404 return !(checkPoint(QPoint(0, 0), w)
405 && checkPoint(QPoint(w->width() - 1, 0), w)
406 && checkPoint(QPoint(0, w->height() - 1), w)
407 && checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
408 && checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
411 void openDebugLogfile()
413 boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
415 /* Open debug.log with the associated application */
416 if (boost::filesystem::exists(pathDebug))
417 QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug)));
420 void SubstituteFonts(const QString& language)
422 #if defined(Q_OS_MAC)
423 // Background:
424 // OSX's default font changed in 10.9 and Qt is unable to find it with its
425 // usual fallback methods when building against the 10.7 sdk or lower.
426 // The 10.8 SDK added a function to let it find the correct fallback font.
427 // If this fallback is not properly loaded, some characters may fail to
428 // render correctly.
430 // The same thing happened with 10.10. .Helvetica Neue DeskInterface is now default.
432 // Solution: If building with the 10.7 SDK or lower and the user's platform
433 // is 10.9 or higher at runtime, substitute the correct font. This needs to
434 // happen before the QApplication is created.
435 #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8
436 if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_8)
438 if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9)
439 /* On a 10.9 - 10.9.x system */
440 QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
441 else
443 /* 10.10 or later system */
444 if (language == "zh_CN" || language == "zh_TW" || language == "zh_HK") // traditional or simplified Chinese
445 QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Heiti SC");
446 else if (language == "ja") // Japanesee
447 QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Songti SC");
448 else
449 QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Lucida Grande");
452 #endif
453 #endif
456 ToolTipToRichTextFilter::ToolTipToRichTextFilter(int _size_threshold, QObject *parent) :
457 QObject(parent),
458 size_threshold(_size_threshold)
463 bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
465 if(evt->type() == QEvent::ToolTipChange)
467 QWidget *widget = static_cast<QWidget*>(obj);
468 QString tooltip = widget->toolTip();
469 if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt") && !Qt::mightBeRichText(tooltip))
471 // Envelop with <qt></qt> to make sure Qt detects this as rich text
472 // Escape the current message as HTML and replace \n by <br>
473 tooltip = "<qt>" + HtmlEscape(tooltip, true) + "</qt>";
474 widget->setToolTip(tooltip);
475 return true;
478 return QObject::eventFilter(obj, evt);
481 void TableViewLastColumnResizingFixer::connectViewHeadersSignals()
483 connect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(on_sectionResized(int,int,int)));
484 connect(tableView->horizontalHeader(), SIGNAL(geometriesChanged()), this, SLOT(on_geometriesChanged()));
487 // We need to disconnect these while handling the resize events, otherwise we can enter infinite loops.
488 void TableViewLastColumnResizingFixer::disconnectViewHeadersSignals()
490 disconnect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(on_sectionResized(int,int,int)));
491 disconnect(tableView->horizontalHeader(), SIGNAL(geometriesChanged()), this, SLOT(on_geometriesChanged()));
494 // Setup the resize mode, handles compatibility for Qt5 and below as the method signatures changed.
495 // Refactored here for readability.
496 void TableViewLastColumnResizingFixer::setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode)
498 #if QT_VERSION < 0x050000
499 tableView->horizontalHeader()->setResizeMode(logicalIndex, resizeMode);
500 #else
501 tableView->horizontalHeader()->setSectionResizeMode(logicalIndex, resizeMode);
502 #endif
505 void TableViewLastColumnResizingFixer::resizeColumn(int nColumnIndex, int width)
507 tableView->setColumnWidth(nColumnIndex, width);
508 tableView->horizontalHeader()->resizeSection(nColumnIndex, width);
511 int TableViewLastColumnResizingFixer::getColumnsWidth()
513 int nColumnsWidthSum = 0;
514 for (int i = 0; i < columnCount; i++)
516 nColumnsWidthSum += tableView->horizontalHeader()->sectionSize(i);
518 return nColumnsWidthSum;
521 int TableViewLastColumnResizingFixer::getAvailableWidthForColumn(int column)
523 int nResult = lastColumnMinimumWidth;
524 int nTableWidth = tableView->horizontalHeader()->width();
526 if (nTableWidth > 0)
528 int nOtherColsWidth = getColumnsWidth() - tableView->horizontalHeader()->sectionSize(column);
529 nResult = std::max(nResult, nTableWidth - nOtherColsWidth);
532 return nResult;
535 // Make sure we don't make the columns wider than the table's viewport width.
536 void TableViewLastColumnResizingFixer::adjustTableColumnsWidth()
538 disconnectViewHeadersSignals();
539 resizeColumn(lastColumnIndex, getAvailableWidthForColumn(lastColumnIndex));
540 connectViewHeadersSignals();
542 int nTableWidth = tableView->horizontalHeader()->width();
543 int nColsWidth = getColumnsWidth();
544 if (nColsWidth > nTableWidth)
546 resizeColumn(secondToLastColumnIndex,getAvailableWidthForColumn(secondToLastColumnIndex));
550 // Make column use all the space available, useful during window resizing.
551 void TableViewLastColumnResizingFixer::stretchColumnWidth(int column)
553 disconnectViewHeadersSignals();
554 resizeColumn(column, getAvailableWidthForColumn(column));
555 connectViewHeadersSignals();
558 // When a section is resized this is a slot-proxy for ajustAmountColumnWidth().
559 void TableViewLastColumnResizingFixer::on_sectionResized(int logicalIndex, int oldSize, int newSize)
561 adjustTableColumnsWidth();
562 int remainingWidth = getAvailableWidthForColumn(logicalIndex);
563 if (newSize > remainingWidth)
565 resizeColumn(logicalIndex, remainingWidth);
569 // When the table's geometry is ready, we manually perform the stretch of the "Message" column,
570 // as the "Stretch" resize mode does not allow for interactive resizing.
571 void TableViewLastColumnResizingFixer::on_geometriesChanged()
573 if ((getColumnsWidth() - this->tableView->horizontalHeader()->width()) != 0)
575 disconnectViewHeadersSignals();
576 resizeColumn(secondToLastColumnIndex, getAvailableWidthForColumn(secondToLastColumnIndex));
577 connectViewHeadersSignals();
582 * Initializes all internal variables and prepares the
583 * the resize modes of the last 2 columns of the table and
585 TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth, QObject *parent) :
586 QObject(parent),
587 tableView(table),
588 lastColumnMinimumWidth(lastColMinimumWidth),
589 allColumnsMinimumWidth(allColsMinimumWidth)
591 columnCount = tableView->horizontalHeader()->count();
592 lastColumnIndex = columnCount - 1;
593 secondToLastColumnIndex = columnCount - 2;
594 tableView->horizontalHeader()->setMinimumSectionSize(allColumnsMinimumWidth);
595 setViewHeaderResizeMode(secondToLastColumnIndex, QHeaderView::Interactive);
596 setViewHeaderResizeMode(lastColumnIndex, QHeaderView::Interactive);
599 #ifdef WIN32
600 boost::filesystem::path static StartupShortcutPath()
602 std::string chain = ChainNameFromCommandLine();
603 if (chain == CBaseChainParams::MAIN)
604 return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk";
605 if (chain == CBaseChainParams::TESTNET) // Remove this special case when CBaseChainParams::TESTNET = "testnet4"
606 return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (testnet).lnk";
607 return GetSpecialFolderPath(CSIDL_STARTUP) / strprintf("Bitcoin (%s).lnk", chain);
610 bool GetStartOnSystemStartup()
612 // check for Bitcoin*.lnk
613 return boost::filesystem::exists(StartupShortcutPath());
616 bool SetStartOnSystemStartup(bool fAutoStart)
618 // If the shortcut exists already, remove it for updating
619 boost::filesystem::remove(StartupShortcutPath());
621 if (fAutoStart)
623 CoInitialize(NULL);
625 // Get a pointer to the IShellLink interface.
626 IShellLink* psl = NULL;
627 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
628 CLSCTX_INPROC_SERVER, IID_IShellLink,
629 reinterpret_cast<void**>(&psl));
631 if (SUCCEEDED(hres))
633 // Get the current executable path
634 TCHAR pszExePath[MAX_PATH];
635 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
637 // Start client minimized
638 QString strArgs = "-min";
639 // Set -testnet /-regtest options
640 strArgs += QString::fromStdString(strprintf(" -testnet=%d -regtest=%d", GetBoolArg("-testnet", false), GetBoolArg("-regtest", false)));
642 #ifdef UNICODE
643 boost::scoped_array<TCHAR> args(new TCHAR[strArgs.length() + 1]);
644 // Convert the QString to TCHAR*
645 strArgs.toWCharArray(args.get());
646 // Add missing '\0'-termination to string
647 args[strArgs.length()] = '\0';
648 #endif
650 // Set the path to the shortcut target
651 psl->SetPath(pszExePath);
652 PathRemoveFileSpec(pszExePath);
653 psl->SetWorkingDirectory(pszExePath);
654 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
655 #ifndef UNICODE
656 psl->SetArguments(strArgs.toStdString().c_str());
657 #else
658 psl->SetArguments(args.get());
659 #endif
661 // Query IShellLink for the IPersistFile interface for
662 // saving the shortcut in persistent storage.
663 IPersistFile* ppf = NULL;
664 hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&ppf));
665 if (SUCCEEDED(hres))
667 WCHAR pwsz[MAX_PATH];
668 // Ensure that the string is ANSI.
669 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH);
670 // Save the link by calling IPersistFile::Save.
671 hres = ppf->Save(pwsz, TRUE);
672 ppf->Release();
673 psl->Release();
674 CoUninitialize();
675 return true;
677 psl->Release();
679 CoUninitialize();
680 return false;
682 return true;
684 #elif defined(Q_OS_LINUX)
686 // Follow the Desktop Application Autostart Spec:
687 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
689 boost::filesystem::path static GetAutostartDir()
691 namespace fs = boost::filesystem;
693 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
694 if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
695 char* pszHome = getenv("HOME");
696 if (pszHome) return fs::path(pszHome) / ".config" / "autostart";
697 return fs::path();
700 boost::filesystem::path static GetAutostartFilePath()
702 std::string chain = ChainNameFromCommandLine();
703 if (chain == CBaseChainParams::MAIN)
704 return GetAutostartDir() / "bitcoin.desktop";
705 return GetAutostartDir() / strprintf("bitcoin-%s.lnk", chain);
708 bool GetStartOnSystemStartup()
710 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
711 if (!optionFile.good())
712 return false;
713 // Scan through file for "Hidden=true":
714 std::string line;
715 while (!optionFile.eof())
717 getline(optionFile, line);
718 if (line.find("Hidden") != std::string::npos &&
719 line.find("true") != std::string::npos)
720 return false;
722 optionFile.close();
724 return true;
727 bool SetStartOnSystemStartup(bool fAutoStart)
729 if (!fAutoStart)
730 boost::filesystem::remove(GetAutostartFilePath());
731 else
733 char pszExePath[MAX_PATH+1];
734 memset(pszExePath, 0, sizeof(pszExePath));
735 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
736 return false;
738 boost::filesystem::create_directories(GetAutostartDir());
740 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
741 if (!optionFile.good())
742 return false;
743 std::string chain = ChainNameFromCommandLine();
744 // Write a bitcoin.desktop file to the autostart directory:
745 optionFile << "[Desktop Entry]\n";
746 optionFile << "Type=Application\n";
747 if (chain == CBaseChainParams::MAIN)
748 optionFile << "Name=Bitcoin\n";
749 else
750 optionFile << strprintf("Name=Bitcoin (%s)\n", chain);
751 optionFile << "Exec=" << pszExePath << strprintf(" -min -testnet=%d -regtest=%d\n", GetBoolArg("-testnet", false), GetBoolArg("-regtest", false));
752 optionFile << "Terminal=false\n";
753 optionFile << "Hidden=false\n";
754 optionFile.close();
756 return true;
760 #elif defined(Q_OS_MAC)
761 #pragma GCC diagnostic push
762 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
763 // based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m
765 #include <CoreFoundation/CoreFoundation.h>
766 #include <CoreServices/CoreServices.h>
768 LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl);
769 LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl)
771 // loop through the list of startup items and try to find the bitcoin app
772 CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, NULL);
773 for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) {
774 LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i);
775 UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
776 CFURLRef currentItemURL = NULL;
778 #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100
779 if(&LSSharedFileListItemCopyResolvedURL)
780 currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, NULL);
781 #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100
782 else
783 LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
784 #endif
785 #else
786 LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
787 #endif
789 if(currentItemURL && CFEqual(currentItemURL, findUrl)) {
790 // found
791 CFRelease(currentItemURL);
792 return item;
794 if(currentItemURL) {
795 CFRelease(currentItemURL);
798 return NULL;
801 bool GetStartOnSystemStartup()
803 CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
804 LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
805 LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
806 return !!foundItem; // return boolified object
809 bool SetStartOnSystemStartup(bool fAutoStart)
811 CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
812 LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
813 LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
815 if(fAutoStart && !foundItem) {
816 // add bitcoin app to startup item list
817 LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, bitcoinAppUrl, NULL, NULL);
819 else if(!fAutoStart && foundItem) {
820 // remove item
821 LSSharedFileListItemRemove(loginItems, foundItem);
823 return true;
825 #pragma GCC diagnostic pop
826 #else
828 bool GetStartOnSystemStartup() { return false; }
829 bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
831 #endif
833 void saveWindowGeometry(const QString& strSetting, QWidget *parent)
835 QSettings settings;
836 settings.setValue(strSetting + "Pos", parent->pos());
837 settings.setValue(strSetting + "Size", parent->size());
840 void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent)
842 QSettings settings;
843 QPoint pos = settings.value(strSetting + "Pos").toPoint();
844 QSize size = settings.value(strSetting + "Size", defaultSize).toSize();
846 if (!pos.x() && !pos.y()) {
847 QRect screen = QApplication::desktop()->screenGeometry();
848 pos.setX((screen.width() - size.width()) / 2);
849 pos.setY((screen.height() - size.height()) / 2);
852 parent->resize(size);
853 parent->move(pos);
856 void setClipboard(const QString& str)
858 QApplication::clipboard()->setText(str, QClipboard::Clipboard);
859 QApplication::clipboard()->setText(str, QClipboard::Selection);
862 boost::filesystem::path qstringToBoostPath(const QString &path)
864 return boost::filesystem::path(path.toStdString(), utf8);
867 QString boostPathToQString(const boost::filesystem::path &path)
869 return QString::fromStdString(path.string(utf8));
872 QString formatDurationStr(int secs)
874 QStringList strList;
875 int days = secs / 86400;
876 int hours = (secs % 86400) / 3600;
877 int mins = (secs % 3600) / 60;
878 int seconds = secs % 60;
880 if (days)
881 strList.append(QString(QObject::tr("%1 d")).arg(days));
882 if (hours)
883 strList.append(QString(QObject::tr("%1 h")).arg(hours));
884 if (mins)
885 strList.append(QString(QObject::tr("%1 m")).arg(mins));
886 if (seconds || (!days && !hours && !mins))
887 strList.append(QString(QObject::tr("%1 s")).arg(seconds));
889 return strList.join(" ");
892 QString formatServicesStr(quint64 mask)
894 QStringList strList;
896 // Just scan the last 8 bits for now.
897 for (int i = 0; i < 8; i++) {
898 uint64_t check = 1 << i;
899 if (mask & check)
901 switch (check)
903 case NODE_NETWORK:
904 strList.append("NETWORK");
905 break;
906 case NODE_GETUTXO:
907 strList.append("GETUTXO");
908 break;
909 case NODE_BLOOM:
910 strList.append("BLOOM");
911 break;
912 case NODE_WITNESS:
913 strList.append("WITNESS");
914 break;
915 case NODE_XTHIN:
916 strList.append("XTHIN");
917 break;
918 default:
919 strList.append(QString("%1[%2]").arg("UNKNOWN").arg(check));
924 if (strList.size())
925 return strList.join(" & ");
926 else
927 return QObject::tr("None");
930 QString formatPingTime(double dPingTime)
932 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));
935 QString formatTimeOffset(int64_t nTimeOffset)
937 return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10));
940 QString formatNiceTimeOffset(qint64 secs)
942 // Represent time from last generated block in human readable text
943 QString timeBehindText;
944 const int HOUR_IN_SECONDS = 60*60;
945 const int DAY_IN_SECONDS = 24*60*60;
946 const int WEEK_IN_SECONDS = 7*24*60*60;
947 const int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar
948 if(secs < 60)
950 timeBehindText = QObject::tr("%n second(s)","",secs);
952 else if(secs < 2*HOUR_IN_SECONDS)
954 timeBehindText = QObject::tr("%n minute(s)","",secs/60);
956 else if(secs < 2*DAY_IN_SECONDS)
958 timeBehindText = QObject::tr("%n hour(s)","",secs/HOUR_IN_SECONDS);
960 else if(secs < 2*WEEK_IN_SECONDS)
962 timeBehindText = QObject::tr("%n day(s)","",secs/DAY_IN_SECONDS);
964 else if(secs < YEAR_IN_SECONDS)
966 timeBehindText = QObject::tr("%n week(s)","",secs/WEEK_IN_SECONDS);
968 else
970 qint64 years = secs / YEAR_IN_SECONDS;
971 qint64 remainder = secs % YEAR_IN_SECONDS;
972 timeBehindText = QObject::tr("%1 and %2").arg(QObject::tr("%n year(s)", "", years)).arg(QObject::tr("%n week(s)","", remainder/WEEK_IN_SECONDS));
974 return timeBehindText;
977 void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
979 Q_EMIT clicked(event->pos());
982 void ClickableProgressBar::mouseReleaseEvent(QMouseEvent *event)
984 Q_EMIT clicked(event->pos());
987 } // namespace GUIUtil