You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

guiutil.cpp 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. // Copyright (c) 2011-2013 The Bitcoin developers
  2. // Distributed under the MIT/X11 software license, see the accompanying
  3. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4. #include "guiutil.h"
  5. #include "bitcoinaddressvalidator.h"
  6. #include "bitcoinunits.h"
  7. #include "qvalidatedlineedit.h"
  8. #include "walletmodel.h"
  9. #include "core.h"
  10. #include "init.h"
  11. #include "util.h"
  12. #ifdef WIN32
  13. #ifdef _WIN32_WINNT
  14. #undef _WIN32_WINNT
  15. #endif
  16. #define _WIN32_WINNT 0x0501
  17. #ifdef _WIN32_IE
  18. #undef _WIN32_IE
  19. #endif
  20. #define _WIN32_IE 0x0501
  21. #define WIN32_LEAN_AND_MEAN 1
  22. #ifndef NOMINMAX
  23. #define NOMINMAX
  24. #endif
  25. #include "shellapi.h"
  26. #include "shlobj.h"
  27. #include "shlwapi.h"
  28. #endif
  29. #include <boost/filesystem.hpp>
  30. #include <boost/filesystem/fstream.hpp>
  31. #if BOOST_FILESYSTEM_VERSION >= 3
  32. #include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
  33. #endif
  34. #include <QAbstractItemView>
  35. #include <QApplication>
  36. #include <QClipboard>
  37. #include <QDateTime>
  38. #include <QDesktopServices>
  39. #include <QDesktopWidget>
  40. #include <QDoubleValidator>
  41. #include <QFileDialog>
  42. #include <QFont>
  43. #include <QLineEdit>
  44. #include <QSettings>
  45. #include <QTextDocument> // for Qt::mightBeRichText
  46. #include <QThread>
  47. #if QT_VERSION < 0x050000
  48. #include <QUrl>
  49. #else
  50. #include <QUrlQuery>
  51. #endif
  52. #if BOOST_FILESYSTEM_VERSION >= 3
  53. static boost::filesystem::detail::utf8_codecvt_facet utf8;
  54. #endif
  55. namespace GUIUtil {
  56. QString dateTimeStr(const QDateTime &date)
  57. {
  58. return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
  59. }
  60. QString dateTimeStr(qint64 nTime)
  61. {
  62. return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
  63. }
  64. QFont bitcoinAddressFont()
  65. {
  66. QFont font("Monospace");
  67. font.setStyleHint(QFont::TypeWriter);
  68. return font;
  69. }
  70. void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
  71. {
  72. parent->setFocusProxy(widget);
  73. widget->setFont(bitcoinAddressFont());
  74. #if QT_VERSION >= 0x040700
  75. widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)"));
  76. #endif
  77. widget->setValidator(new BitcoinAddressEntryValidator(parent));
  78. widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
  79. }
  80. void setupAmountWidget(QLineEdit *widget, QWidget *parent)
  81. {
  82. QDoubleValidator *amountValidator = new QDoubleValidator(parent);
  83. amountValidator->setDecimals(8);
  84. amountValidator->setBottom(0.0);
  85. widget->setValidator(amountValidator);
  86. widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
  87. }
  88. bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
  89. {
  90. // return if URI is not valid or is no bitcoin: URI
  91. if(!uri.isValid() || uri.scheme() != QString("bitcoin"))
  92. return false;
  93. SendCoinsRecipient rv;
  94. rv.address = uri.path();
  95. rv.amount = 0;
  96. #if QT_VERSION < 0x050000
  97. QList<QPair<QString, QString> > items = uri.queryItems();
  98. #else
  99. QUrlQuery uriQuery(uri);
  100. QList<QPair<QString, QString> > items = uriQuery.queryItems();
  101. #endif
  102. for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
  103. {
  104. bool fShouldReturnFalse = false;
  105. if (i->first.startsWith("req-"))
  106. {
  107. i->first.remove(0, 4);
  108. fShouldReturnFalse = true;
  109. }
  110. if (i->first == "label")
  111. {
  112. rv.label = i->second;
  113. fShouldReturnFalse = false;
  114. }
  115. if (i->first == "message")
  116. {
  117. rv.message = i->second;
  118. fShouldReturnFalse = false;
  119. }
  120. else if (i->first == "amount")
  121. {
  122. if(!i->second.isEmpty())
  123. {
  124. if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
  125. {
  126. return false;
  127. }
  128. }
  129. fShouldReturnFalse = false;
  130. }
  131. if (fShouldReturnFalse)
  132. return false;
  133. }
  134. if(out)
  135. {
  136. *out = rv;
  137. }
  138. return true;
  139. }
  140. bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
  141. {
  142. // Convert bitcoin:// to bitcoin:
  143. //
  144. // Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host,
  145. // which will lower-case it (and thus invalidate the address).
  146. if(uri.startsWith("bitcoin://", Qt::CaseInsensitive))
  147. {
  148. uri.replace(0, 10, "bitcoin:");
  149. }
  150. QUrl uriInstance(uri);
  151. return parseBitcoinURI(uriInstance, out);
  152. }
  153. QString formatBitcoinURI(const SendCoinsRecipient &info)
  154. {
  155. QString ret = QString("bitcoin:%1").arg(info.address);
  156. int paramCount = 0;
  157. if (info.amount)
  158. {
  159. ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount));
  160. paramCount++;
  161. }
  162. if (!info.label.isEmpty())
  163. {
  164. QString lbl(QUrl::toPercentEncoding(info.label));
  165. ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl);
  166. paramCount++;
  167. }
  168. if (!info.message.isEmpty())
  169. {
  170. QString msg(QUrl::toPercentEncoding(info.message));;
  171. ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg);
  172. paramCount++;
  173. }
  174. return ret;
  175. }
  176. bool isDust(const QString& address, qint64 amount)
  177. {
  178. CTxDestination dest = CBitcoinAddress(address.toStdString()).Get();
  179. CScript script; script.SetDestination(dest);
  180. CTxOut txOut(amount, script);
  181. return txOut.IsDust(CTransaction::nMinRelayTxFee);
  182. }
  183. QString HtmlEscape(const QString& str, bool fMultiLine)
  184. {
  185. #if QT_VERSION < 0x050000
  186. QString escaped = Qt::escape(str);
  187. #else
  188. QString escaped = str.toHtmlEscaped();
  189. #endif
  190. if(fMultiLine)
  191. {
  192. escaped = escaped.replace("\n", "<br>\n");
  193. }
  194. return escaped;
  195. }
  196. QString HtmlEscape(const std::string& str, bool fMultiLine)
  197. {
  198. return HtmlEscape(QString::fromStdString(str), fMultiLine);
  199. }
  200. void copyEntryData(QAbstractItemView *view, int column, int role)
  201. {
  202. if(!view || !view->selectionModel())
  203. return;
  204. QModelIndexList selection = view->selectionModel()->selectedRows(column);
  205. if(!selection.isEmpty())
  206. {
  207. // Copy first item
  208. setClipboard(selection.at(0).data(role).toString());
  209. }
  210. }
  211. QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
  212. const QString &filter,
  213. QString *selectedSuffixOut)
  214. {
  215. QString selectedFilter;
  216. QString myDir;
  217. if(dir.isEmpty()) // Default to user documents location
  218. {
  219. #if QT_VERSION < 0x050000
  220. myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
  221. #else
  222. myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
  223. #endif
  224. }
  225. else
  226. {
  227. myDir = dir;
  228. }
  229. /* Directly convert path to native OS path separators */
  230. QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter));
  231. /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
  232. QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
  233. QString selectedSuffix;
  234. if(filter_re.exactMatch(selectedFilter))
  235. {
  236. selectedSuffix = filter_re.cap(1);
  237. }
  238. /* Add suffix if needed */
  239. QFileInfo info(result);
  240. if(!result.isEmpty())
  241. {
  242. if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
  243. {
  244. /* No suffix specified, add selected suffix */
  245. if(!result.endsWith("."))
  246. result.append(".");
  247. result.append(selectedSuffix);
  248. }
  249. }
  250. /* Return selected suffix if asked to */
  251. if(selectedSuffixOut)
  252. {
  253. *selectedSuffixOut = selectedSuffix;
  254. }
  255. return result;
  256. }
  257. QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir,
  258. const QString &filter,
  259. QString *selectedSuffixOut)
  260. {
  261. QString selectedFilter;
  262. QString myDir;
  263. if(dir.isEmpty()) // Default to user documents location
  264. {
  265. #if QT_VERSION < 0x050000
  266. myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
  267. #else
  268. myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
  269. #endif
  270. }
  271. else
  272. {
  273. myDir = dir;
  274. }
  275. /* Directly convert path to native OS path separators */
  276. QString result = QDir::toNativeSeparators(QFileDialog::getOpenFileName(parent, caption, myDir, filter, &selectedFilter));
  277. if(selectedSuffixOut)
  278. {
  279. /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
  280. QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
  281. QString selectedSuffix;
  282. if(filter_re.exactMatch(selectedFilter))
  283. {
  284. selectedSuffix = filter_re.cap(1);
  285. }
  286. *selectedSuffixOut = selectedSuffix;
  287. }
  288. return result;
  289. }
  290. Qt::ConnectionType blockingGUIThreadConnection()
  291. {
  292. if(QThread::currentThread() != qApp->thread())
  293. {
  294. return Qt::BlockingQueuedConnection;
  295. }
  296. else
  297. {
  298. return Qt::DirectConnection;
  299. }
  300. }
  301. bool checkPoint(const QPoint &p, const QWidget *w)
  302. {
  303. QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p));
  304. if (!atW) return false;
  305. return atW->topLevelWidget() == w;
  306. }
  307. bool isObscured(QWidget *w)
  308. {
  309. return !(checkPoint(QPoint(0, 0), w)
  310. && checkPoint(QPoint(w->width() - 1, 0), w)
  311. && checkPoint(QPoint(0, w->height() - 1), w)
  312. && checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
  313. && checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
  314. }
  315. void openDebugLogfile()
  316. {
  317. boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
  318. /* Open debug.log with the associated application */
  319. if (boost::filesystem::exists(pathDebug))
  320. QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug)));
  321. }
  322. ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) :
  323. QObject(parent), size_threshold(size_threshold)
  324. {
  325. }
  326. bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
  327. {
  328. if(evt->type() == QEvent::ToolTipChange)
  329. {
  330. QWidget *widget = static_cast<QWidget*>(obj);
  331. QString tooltip = widget->toolTip();
  332. if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt") && !Qt::mightBeRichText(tooltip))
  333. {
  334. // Envelop with <qt></qt> to make sure Qt detects this as rich text
  335. // Escape the current message as HTML and replace \n by <br>
  336. tooltip = "<qt>" + HtmlEscape(tooltip, true) + "</qt>";
  337. widget->setToolTip(tooltip);
  338. return true;
  339. }
  340. }
  341. return QObject::eventFilter(obj, evt);
  342. }
  343. void TableViewLastColumnResizingFixer::connectViewHeadersSignals()
  344. {
  345. connect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(on_sectionResized(int,int,int)));
  346. connect(tableView->horizontalHeader(), SIGNAL(geometriesChanged()), this, SLOT(on_geometriesChanged()));
  347. }
  348. //we need to disconnect these while handling the resize events, otherwise we can enter infinite loops
  349. void TableViewLastColumnResizingFixer::disconnectViewHeadersSignals()
  350. {
  351. disconnect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(on_sectionResized(int,int,int)));
  352. disconnect(tableView->horizontalHeader(), SIGNAL(geometriesChanged()), this, SLOT(on_geometriesChanged()));
  353. }
  354. //setup the resize mode, handles compatibility for QT5 and below as the method signatures changed. (refactored here for readability)
  355. void TableViewLastColumnResizingFixer::setViewHeaderResizeMode(int logicalIndex, QHeaderView::ResizeMode resizeMode)
  356. {
  357. #if QT_VERSION < 0x050000
  358. tableView->horizontalHeader()->setResizeMode(logicalIndex, resizeMode);
  359. #else
  360. tableView->horizontalHeader()->setSectionResizeMode(logicalIndex, resizeMode);
  361. #endif
  362. }
  363. void TableViewLastColumnResizingFixer::resizeColumn(int nColumnIndex, int width) {
  364. tableView->setColumnWidth(nColumnIndex, width);
  365. tableView->horizontalHeader()->resizeSection(nColumnIndex, width);
  366. }
  367. int TableViewLastColumnResizingFixer::getColumnsWidth()
  368. {
  369. int nColumnsWidthSum = 0;
  370. for (int i = 0; i < columnCount; i++)
  371. {
  372. nColumnsWidthSum += tableView->horizontalHeader()->sectionSize(i);
  373. }
  374. return nColumnsWidthSum;
  375. }
  376. int TableViewLastColumnResizingFixer::getAvailableWidthForColumn(int column)
  377. {
  378. int nResult = lastColumnMinimumWidth;
  379. int nTableWidth = tableView->horizontalHeader()->width();
  380. if (nTableWidth > 0)
  381. {
  382. int nOtherColsWidth = getColumnsWidth() - tableView->horizontalHeader()->sectionSize(column);
  383. nResult = std::max(nResult, nTableWidth - nOtherColsWidth);
  384. }
  385. return nResult;
  386. }
  387. //make sure we don't make the columns wider than the table's viewport's width.
  388. void TableViewLastColumnResizingFixer::adjustTableColumnsWidth()
  389. {
  390. disconnectViewHeadersSignals();
  391. resizeColumn(lastColumnIndex, getAvailableWidthForColumn(lastColumnIndex));
  392. connectViewHeadersSignals();
  393. int nTableWidth = tableView->horizontalHeader()->width();
  394. int nColsWidth = getColumnsWidth();
  395. if (nColsWidth > nTableWidth)
  396. {
  397. resizeColumn(secondToLastColumnIndex,getAvailableWidthForColumn(secondToLastColumnIndex));
  398. }
  399. }
  400. //make column use all the space available, useful during window resizing.
  401. void TableViewLastColumnResizingFixer::stretchColumnWidth(int column) {
  402. disconnectViewHeadersSignals();
  403. resizeColumn(column, getAvailableWidthForColumn(column));
  404. connectViewHeadersSignals();
  405. }
  406. //when a section is resized this is a slot-proxy for ajustAmountColumnWidth()
  407. void TableViewLastColumnResizingFixer::on_sectionResized(int logicalIndex, int oldSize, int newSize)
  408. {
  409. adjustTableColumnsWidth();
  410. int remainingWidth = getAvailableWidthForColumn(logicalIndex);
  411. if (newSize > remainingWidth)
  412. {
  413. resizeColumn(logicalIndex, remainingWidth);
  414. }
  415. }
  416. //when the table's geometry is ready, we manually perform the Stretch of the "Message" column
  417. //as the "Stretch" resize mode does not allow for interactive resizing.
  418. void TableViewLastColumnResizingFixer::on_geometriesChanged()
  419. {
  420. if ((getColumnsWidth() - this->tableView->horizontalHeader()->width()) != 0)
  421. {
  422. disconnectViewHeadersSignals();
  423. resizeColumn(secondToLastColumnIndex, getAvailableWidthForColumn(secondToLastColumnIndex));
  424. connectViewHeadersSignals();
  425. }
  426. }
  427. /**
  428. * Initializes all internal variables and prepares the
  429. * the resize modes of the last 2 columns of the table and
  430. */
  431. TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* table, int lastColMinimumWidth, int allColsMinimumWidth) :
  432. tableView(table),
  433. lastColumnMinimumWidth(lastColMinimumWidth),
  434. allColumnsMinimumWidth(allColsMinimumWidth)
  435. {
  436. columnCount = tableView->horizontalHeader()->count();
  437. lastColumnIndex = columnCount - 1;
  438. secondToLastColumnIndex = columnCount - 2;
  439. tableView->horizontalHeader()->setMinimumSectionSize(allColumnsMinimumWidth);
  440. setViewHeaderResizeMode(secondToLastColumnIndex, QHeaderView::Interactive);
  441. setViewHeaderResizeMode(lastColumnIndex, QHeaderView::Interactive);
  442. }
  443. #ifdef WIN32
  444. boost::filesystem::path static StartupShortcutPath()
  445. {
  446. return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk";
  447. }
  448. bool GetStartOnSystemStartup()
  449. {
  450. // check for Bitcoin.lnk
  451. return boost::filesystem::exists(StartupShortcutPath());
  452. }
  453. bool SetStartOnSystemStartup(bool fAutoStart)
  454. {
  455. // If the shortcut exists already, remove it for updating
  456. boost::filesystem::remove(StartupShortcutPath());
  457. if (fAutoStart)
  458. {
  459. CoInitialize(NULL);
  460. // Get a pointer to the IShellLink interface.
  461. IShellLink* psl = NULL;
  462. HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
  463. CLSCTX_INPROC_SERVER, IID_IShellLink,
  464. reinterpret_cast<void**>(&psl));
  465. if (SUCCEEDED(hres))
  466. {
  467. // Get the current executable path
  468. TCHAR pszExePath[MAX_PATH];
  469. GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
  470. TCHAR pszArgs[5] = TEXT("-min");
  471. // Set the path to the shortcut target
  472. psl->SetPath(pszExePath);
  473. PathRemoveFileSpec(pszExePath);
  474. psl->SetWorkingDirectory(pszExePath);
  475. psl->SetShowCmd(SW_SHOWMINNOACTIVE);
  476. psl->SetArguments(pszArgs);
  477. // Query IShellLink for the IPersistFile interface for
  478. // saving the shortcut in persistent storage.
  479. IPersistFile* ppf = NULL;
  480. hres = psl->QueryInterface(IID_IPersistFile,
  481. reinterpret_cast<void**>(&ppf));
  482. if (SUCCEEDED(hres))
  483. {
  484. WCHAR pwsz[MAX_PATH];
  485. // Ensure that the string is ANSI.
  486. MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH);
  487. // Save the link by calling IPersistFile::Save.
  488. hres = ppf->Save(pwsz, TRUE);
  489. ppf->Release();
  490. psl->Release();
  491. CoUninitialize();
  492. return true;
  493. }
  494. psl->Release();
  495. }
  496. CoUninitialize();
  497. return false;
  498. }
  499. return true;
  500. }
  501. #elif defined(LINUX)
  502. // Follow the Desktop Application Autostart Spec:
  503. // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
  504. boost::filesystem::path static GetAutostartDir()
  505. {
  506. namespace fs = boost::filesystem;
  507. char* pszConfigHome = getenv("XDG_CONFIG_HOME");
  508. if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
  509. char* pszHome = getenv("HOME");
  510. if (pszHome) return fs::path(pszHome) / ".config" / "autostart";
  511. return fs::path();
  512. }
  513. boost::filesystem::path static GetAutostartFilePath()
  514. {
  515. return GetAutostartDir() / "bitcoin.desktop";
  516. }
  517. bool GetStartOnSystemStartup()
  518. {
  519. boost::filesystem::ifstream optionFile(GetAutostartFilePath());
  520. if (!optionFile.good())
  521. return false;
  522. // Scan through file for "Hidden=true":
  523. std::string line;
  524. while (!optionFile.eof())
  525. {
  526. getline(optionFile, line);
  527. if (line.find("Hidden") != std::string::npos &&
  528. line.find("true") != std::string::npos)
  529. return false;
  530. }
  531. optionFile.close();
  532. return true;
  533. }
  534. bool SetStartOnSystemStartup(bool fAutoStart)
  535. {
  536. if (!fAutoStart)
  537. boost::filesystem::remove(GetAutostartFilePath());
  538. else
  539. {
  540. char pszExePath[MAX_PATH+1];
  541. memset(pszExePath, 0, sizeof(pszExePath));
  542. if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
  543. return false;
  544. boost::filesystem::create_directories(GetAutostartDir());
  545. boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
  546. if (!optionFile.good())
  547. return false;
  548. // Write a bitcoin.desktop file to the autostart directory:
  549. optionFile << "[Desktop Entry]\n";
  550. optionFile << "Type=Application\n";
  551. optionFile << "Name=Bitcoin\n";
  552. optionFile << "Exec=" << pszExePath << " -min\n";
  553. optionFile << "Terminal=false\n";
  554. optionFile << "Hidden=false\n";
  555. optionFile.close();
  556. }
  557. return true;
  558. }
  559. #elif defined(Q_OS_MAC)
  560. // based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m
  561. #include <CoreFoundation/CoreFoundation.h>
  562. #include <CoreServices/CoreServices.h>
  563. LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl);
  564. LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl)
  565. {
  566. // loop through the list of startup items and try to find the bitcoin app
  567. CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, NULL);
  568. for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) {
  569. LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i);
  570. UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
  571. CFURLRef currentItemURL = NULL;
  572. LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
  573. if(currentItemURL && CFEqual(currentItemURL, findUrl)) {
  574. // found
  575. CFRelease(currentItemURL);
  576. return item;
  577. }
  578. if(currentItemURL) {
  579. CFRelease(currentItemURL);
  580. }
  581. }
  582. return NULL;
  583. }
  584. bool GetStartOnSystemStartup()
  585. {
  586. CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
  587. LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
  588. LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
  589. return !!foundItem; // return boolified object
  590. }
  591. bool SetStartOnSystemStartup(bool fAutoStart)
  592. {
  593. CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
  594. LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
  595. LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl);
  596. if(fAutoStart && !foundItem) {
  597. // add bitcoin app to startup item list
  598. LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, bitcoinAppUrl, NULL, NULL);
  599. }
  600. else if(!fAutoStart && foundItem) {
  601. // remove item
  602. LSSharedFileListItemRemove(loginItems, foundItem);
  603. }
  604. return true;
  605. }
  606. #else
  607. bool GetStartOnSystemStartup() { return false; }
  608. bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
  609. #endif
  610. void saveWindowGeometry(const QString& strSetting, QWidget *parent)
  611. {
  612. QSettings settings;
  613. settings.setValue(strSetting + "Pos", parent->pos());
  614. settings.setValue(strSetting + "Size", parent->size());
  615. }
  616. void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent)
  617. {
  618. QSettings settings;
  619. QPoint pos = settings.value(strSetting + "Pos").toPoint();
  620. QSize size = settings.value(strSetting + "Size", defaultSize).toSize();
  621. if (!pos.x() && !pos.y()) {
  622. QRect screen = QApplication::desktop()->screenGeometry();
  623. pos.setX((screen.width() - size.width()) / 2);
  624. pos.setY((screen.height() - size.height()) / 2);
  625. }
  626. parent->resize(size);
  627. parent->move(pos);
  628. }
  629. void setClipboard(const QString& str)
  630. {
  631. QApplication::clipboard()->setText(str, QClipboard::Clipboard);
  632. QApplication::clipboard()->setText(str, QClipboard::Selection);
  633. }
  634. #if BOOST_FILESYSTEM_VERSION >= 3
  635. boost::filesystem::path qstringToBoostPath(const QString &path)
  636. {
  637. return boost::filesystem::path(path.toStdString(), utf8);
  638. }
  639. QString boostPathToQString(const boost::filesystem::path &path)
  640. {
  641. return QString::fromStdString(path.string(utf8));
  642. }
  643. #else
  644. #warning Conversion between boost path and QString can use invalid character encoding with boost_filesystem v2 and older
  645. boost::filesystem::path qstringToBoostPath(const QString &path)
  646. {
  647. return boost::filesystem::path(path.toStdString());
  648. }
  649. QString boostPathToQString(const boost::filesystem::path &path)
  650. {
  651. return QString::fromStdString(path.string());
  652. }
  653. #endif
  654. } // namespace GUIUtil