guiutil.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // ECOin - Copyright (c) - 2014/2024 - GPLv3 - epsylon@riseup.net (https://03c8.net)
  2. #include "guiutil.h"
  3. #include "ecoinaddressvalidator.h"
  4. #include "walletmodel.h"
  5. #include "ecoinunits.h"
  6. #include "util.h"
  7. #include "init.h"
  8. #include <QString>
  9. #include <QDateTime>
  10. #include <QDoubleValidator>
  11. #include <QFont>
  12. #include <QLineEdit>
  13. #include <QUrl>
  14. #include <QAbstractItemView>
  15. #include <QApplication>
  16. #include <QClipboard>
  17. #include <QFileDialog>
  18. #include <QDesktopServices>
  19. #include <QThread>
  20. #include <boost/filesystem.hpp>
  21. #include <boost/filesystem/fstream.hpp>
  22. #include <QUrl>
  23. #include <QUrlQuery>
  24. #include <QtGlobal>
  25. #include <QToolTip>
  26. #ifdef WIN32
  27. #include <windows.h>
  28. #include <shlobj.h>
  29. #endif
  30. namespace GUIUtil {
  31. QString dateTimeStr(const QDateTime &datetime)
  32. {
  33. return datetime.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + datetime.toString("hh:mm");
  34. }
  35. QString dateTimeStr(qint64 nTime)
  36. {
  37. return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
  38. }
  39. QFont ecoinAddressFont()
  40. {
  41. QFont font("Monospace");
  42. font.setStyleHint(QFont::TypeWriter);
  43. return font;
  44. }
  45. void setupAddressWidget(QLineEdit *widget, QWidget *parent)
  46. {
  47. widget->setMaxLength(EcoinAddressValidator::MaxAddressLength);
  48. widget->setValidator(new EcoinAddressValidator(parent));
  49. widget->setFont(ecoinAddressFont());
  50. }
  51. void setupAmountWidget(QLineEdit *widget, QWidget *parent)
  52. {
  53. QDoubleValidator *amountValidator = new QDoubleValidator(parent);
  54. amountValidator->setDecimals(8);
  55. amountValidator->setBottom(0.0);
  56. widget->setValidator(amountValidator);
  57. widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
  58. }
  59. bool parseEcoinURI(const QUrl &uri, SendCoinsRecipient *out)
  60. {
  61. // Ecoin: check prefix
  62. if(uri.scheme() != QString("ecoin"))
  63. return false;
  64. SendCoinsRecipient rv;
  65. rv.address = uri.path();
  66. rv.amount = 0;
  67. QList<QPair<QString, QString>> items = QUrlQuery(uri).queryItems();
  68. for (QList<QPair<QString, QString>>::iterator i = items.begin(); i != items.end(); i++)
  69. {
  70. bool fShouldReturnFalse = false;
  71. if (i->first.startsWith("req-"))
  72. {
  73. i->first.remove(0, 4);
  74. fShouldReturnFalse = true;
  75. }
  76. if (i->first == "label")
  77. {
  78. rv.label = i->second;
  79. fShouldReturnFalse = false;
  80. }
  81. else if (i->first == "amount")
  82. {
  83. if(!i->second.isEmpty())
  84. {
  85. if(!EcoinUnits::parse(EcoinUnits::ECO, i->second, &rv.amount))
  86. {
  87. return false;
  88. }
  89. }
  90. fShouldReturnFalse = false;
  91. }
  92. if (fShouldReturnFalse)
  93. return false;
  94. }
  95. if(out)
  96. {
  97. *out = rv;
  98. }
  99. return true;
  100. }
  101. bool parseEcoinURI(QString uri, SendCoinsRecipient *out)
  102. {
  103. if(uri.startsWith("ecoin://"))
  104. {
  105. uri.replace(0, 10, "ecoin:");
  106. }
  107. QUrl uriInstance(uri);
  108. return parseEcoinURI(uriInstance, out);
  109. }
  110. QString HtmlEscape(const QString& str, bool fMultiLine)
  111. {
  112. QString escaped = str.toHtmlEscaped();
  113. if(fMultiLine)
  114. {
  115. escaped = escaped.replace("\n", "<br>\n");
  116. }
  117. return escaped;
  118. }
  119. QString HtmlEscape(const std::string& str, bool fMultiLine)
  120. {
  121. return HtmlEscape(QString::fromStdString(str), fMultiLine);
  122. }
  123. ToolTipToRichTextFilter::ToolTipToRichTextFilter(int threshold, QObject *parent) :
  124. QObject(parent),
  125. size_threshold(threshold)
  126. {
  127. // Constructor implementation
  128. }
  129. bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
  130. {
  131. if(evt->type() == QEvent::ToolTipChange)
  132. {
  133. QWidget *widget = static_cast<QWidget*>(obj);
  134. QString tooltip = widget->toolTip();
  135. if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt>") && !QToolTip::isVisible())
  136. {
  137. // Prefix <qt/> to make sure Qt detects this as rich text
  138. // Escape the current message as HTML and replace \n by <br>
  139. tooltip = "<qt>" + HtmlEscape(tooltip, true) + "</qt>";
  140. widget->setToolTip(tooltip);
  141. return true;
  142. }
  143. }
  144. return QObject::eventFilter(obj, evt);
  145. }
  146. void copyEntryData(QAbstractItemView *view, int column, int role)
  147. {
  148. if(!view || !view->selectionModel())
  149. return;
  150. QModelIndexList selection = view->selectionModel()->selectedRows(column);
  151. if(!selection.isEmpty())
  152. {
  153. // Copy first item
  154. QApplication::clipboard()->setText(selection.at(0).data(role).toString());
  155. }
  156. }
  157. QString getSaveFileName(QWidget *parent, const QString &caption,
  158. const QString &dir,
  159. const QString &filter,
  160. QString *selectedSuffixOut)
  161. {
  162. QString selectedFilter;
  163. QString myDir;
  164. if(dir.isEmpty()) // Default to user documents location
  165. {
  166. myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
  167. }
  168. else
  169. {
  170. myDir = dir;
  171. }
  172. QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter);
  173. /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
  174. QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
  175. QString selectedSuffix;
  176. if(filter_re.exactMatch(selectedFilter))
  177. {
  178. selectedSuffix = filter_re.cap(1);
  179. }
  180. /* Add suffix if needed */
  181. QFileInfo info(result);
  182. if(!result.isEmpty())
  183. {
  184. if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
  185. {
  186. /* No suffix specified, add selected suffix */
  187. if(!result.endsWith("."))
  188. result.append(".");
  189. result.append(selectedSuffix);
  190. }
  191. }
  192. /* Return selected suffix if asked to */
  193. if(selectedSuffixOut)
  194. {
  195. *selectedSuffixOut = selectedSuffix;
  196. }
  197. return result;
  198. }
  199. Qt::ConnectionType blockingGUIThreadConnection()
  200. {
  201. if(QThread::currentThread() != QCoreApplication::instance()->thread())
  202. {
  203. return Qt::BlockingQueuedConnection;
  204. }
  205. else
  206. {
  207. return Qt::DirectConnection;
  208. }
  209. }
  210. bool checkPoint(const QPoint &p, const QWidget *w)
  211. {
  212. QWidget *atW = qApp->widgetAt(w->mapToGlobal(p));
  213. if (!atW) return false;
  214. return atW->topLevelWidget() == w;
  215. }
  216. bool isObscured(QWidget *w)
  217. {
  218. return !(checkPoint(QPoint(0, 0), w)
  219. && checkPoint(QPoint(w->width() - 1, 0), w)
  220. && checkPoint(QPoint(0, w->height() - 1), w)
  221. && checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
  222. && checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
  223. }
  224. void openDebugLogfile()
  225. {
  226. boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
  227. /* Open debug.log with the associated application */
  228. if (boost::filesystem::exists(pathDebug))
  229. QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(pathDebug.string())));
  230. }
  231. #ifdef WIN32
  232. boost::filesystem::path static StartupShortcutPath()
  233. {
  234. return GetSpecialFolderPath(CSIDL_STARTUP) / "Ecoin.lnk";
  235. }
  236. bool GetStartOnSystemStartup()
  237. {
  238. // check for Ecoin.lnk
  239. return boost::filesystem::exists(StartupShortcutPath());
  240. }
  241. bool SetStartOnSystemStartup(bool fAutoStart)
  242. {
  243. // If the shortcut exists already, remove it for updating
  244. boost::filesystem::remove(StartupShortcutPath());
  245. if (fAutoStart)
  246. {
  247. CoInitialize(NULL);
  248. // Get a pointer to the IShellLink interface.
  249. IShellLink* psl = NULL;
  250. HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
  251. CLSCTX_INPROC_SERVER, IID_IShellLink,
  252. reinterpret_cast<void**>(&psl));
  253. if (SUCCEEDED(hres))
  254. {
  255. // Get the current executable path
  256. TCHAR pszExePath[MAX_PATH];
  257. GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
  258. TCHAR pszArgs[5] = TEXT("-min");
  259. // Set the path to the shortcut target
  260. psl->SetPath(pszExePath);
  261. PathRemoveFileSpec(pszExePath);
  262. psl->SetWorkingDirectory(pszExePath);
  263. psl->SetShowCmd(SW_SHOWMINNOACTIVE);
  264. psl->SetArguments(pszArgs);
  265. // Query IShellLink for the IPersistFile interface for
  266. // saving the shortcut in persistent storage.
  267. IPersistFile* ppf = NULL;
  268. hres = psl->QueryInterface(IID_IPersistFile,
  269. reinterpret_cast<void**>(&ppf));
  270. if (SUCCEEDED(hres))
  271. {
  272. WCHAR pwsz[MAX_PATH];
  273. // Ensure that the string is ANSI.
  274. MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH);
  275. // Save the link by calling IPersistFile::Save.
  276. hres = ppf->Save(pwsz, TRUE);
  277. ppf->Release();
  278. psl->Release();
  279. CoUninitialize();
  280. return true;
  281. }
  282. psl->Release();
  283. }
  284. CoUninitialize();
  285. return false;
  286. }
  287. return true;
  288. }
  289. #elif defined(LINUX)
  290. boost::filesystem::path static GetAutostartDir()
  291. {
  292. namespace fs = boost::filesystem;
  293. char* pszConfigHome = getenv("XDG_CONFIG_HOME");
  294. if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
  295. char* pszHome = getenv("HOME");
  296. if (pszHome) return fs::path(pszHome) / ".config" / "autostart";
  297. return fs::path();
  298. }
  299. boost::filesystem::path static GetAutostartFilePath()
  300. {
  301. return GetAutostartDir() / "ecoin.desktop";
  302. }
  303. bool GetStartOnSystemStartup()
  304. {
  305. boost::filesystem::ifstream optionFile(GetAutostartFilePath());
  306. if (!optionFile.good())
  307. return false;
  308. // Scan through file for "Hidden=true":
  309. std::string line;
  310. while (!optionFile.eof())
  311. {
  312. getline(optionFile, line);
  313. if (line.find("Hidden") != std::string::npos &&
  314. line.find("true") != std::string::npos)
  315. return false;
  316. }
  317. optionFile.close();
  318. return true;
  319. }
  320. bool SetStartOnSystemStartup(bool fAutoStart)
  321. {
  322. if (!fAutoStart)
  323. boost::filesystem::remove(GetAutostartFilePath());
  324. else
  325. {
  326. char pszExePath[MAX_PATH+1];
  327. memset(pszExePath, 0, sizeof(pszExePath));
  328. if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
  329. return false;
  330. boost::filesystem::create_directories(GetAutostartDir());
  331. boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
  332. if (!optionFile.good())
  333. return false;
  334. // Write a ecoin.desktop file to the autostart directory:
  335. optionFile << "[Desktop Entry]\n";
  336. optionFile << "Type=Application\n";
  337. optionFile << "Name=Ecoin\n";
  338. optionFile << "Exec=" << pszExePath << " -min\n";
  339. optionFile << "Terminal=false\n";
  340. optionFile << "Hidden=false\n";
  341. optionFile.close();
  342. }
  343. return true;
  344. }
  345. #else
  346. bool GetStartOnSystemStartup() { return false; }
  347. bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
  348. #endif
  349. HelpMessageBox::HelpMessageBox(QWidget *parent) :
  350. QMessageBox(parent)
  351. {
  352. header = tr("Ecoin-Qt") + " " + tr("version") + " " +
  353. QString::fromStdString(FormatFullVersion()) + "\n\n" +
  354. tr("Usage:") + "\n" +
  355. " ecoin-qt [" + tr("command-line options") + "] " + "\n";
  356. coreOptions = QString::fromStdString(HelpMessage());
  357. uiOptions = tr("UI options") + ":\n" +
  358. " -lang=<lang> " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
  359. " -min " + tr("Start minimized") + "\n" +
  360. " -splash " + tr("Show splash screen on startup (default: 1)") + "\n";
  361. setWindowTitle(tr("Ecoin-Qt"));
  362. setTextFormat(Qt::PlainText);
  363. // setMinimumWidth is ignored for QMessageBox so put in non-breaking spaces to make it wider.
  364. setText(header + QString(QChar(0x2003)).repeated(50));
  365. setDetailedText(coreOptions + "\n" + uiOptions);
  366. }
  367. void HelpMessageBox::printToConsole()
  368. {
  369. // On other operating systems, the expected action is to print the message to the console.
  370. QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions;
  371. fprintf(stdout, "%s", strUsage.toStdString().c_str());
  372. }
  373. void HelpMessageBox::showOrPrint()
  374. {
  375. #if defined(WIN32)
  376. // On Windows, show a message box, as there is no stderr/stdout in windowed applications
  377. exec();
  378. #else
  379. // On other operating systems, print help text to console
  380. printToConsole();
  381. #endif
  382. }
  383. } // namespace GUIUtil