From 0af424533fbec3a17704c4061aa4a4cfee59f1c2 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Mon, 20 Jul 2020 11:50:55 +0300 Subject: [PATCH 1/3] qt, util: Split out qt/guifileutil module --- .../libbitcoin_qt/libbitcoin_qt.vcxproj | 1 + src/Makefile.qt.include | 2 + src/qt/addressbookpage.cpp | 1 + src/qt/clientmodel.cpp | 2 +- src/qt/guifileutil.cpp | 313 ++++++++++++++++++ src/qt/guifileutil.h | 68 ++++ src/qt/guiutil.cpp | 292 ---------------- src/qt/guiutil.h | 48 --- src/qt/intro.cpp | 2 +- src/qt/optionsdialog.cpp | 1 + src/qt/optionsmodel.cpp | 2 +- src/qt/paymentserver.cpp | 1 + src/qt/psbtoperationsdialog.cpp | 3 +- src/qt/qrimagewidget.cpp | 1 + src/qt/rpcconsole.cpp | 10 +- src/qt/rpcconsole.h | 1 - src/qt/sendcoinsdialog.cpp | 1 + src/qt/transactionview.cpp | 1 + src/qt/walletview.cpp | 3 +- 19 files changed, 403 insertions(+), 350 deletions(-) create mode 100644 src/qt/guifileutil.cpp create mode 100644 src/qt/guifileutil.h diff --git a/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj b/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj index 6a3c9f1dc12..a155e16a162 100644 --- a/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj +++ b/build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj @@ -23,6 +23,7 @@ + diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 848053e8417..a0baf9bf961 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -119,6 +119,7 @@ BITCOIN_QT_H = \ qt/csvmodelwriter.h \ qt/editaddressdialog.h \ qt/guiconstants.h \ + qt/guifileutil.h \ qt/guiutil.h \ qt/intro.h \ qt/macdockiconhandler.h \ @@ -219,6 +220,7 @@ BITCOIN_QT_BASE_CPP = \ qt/bitcoinunits.cpp \ qt/clientmodel.cpp \ qt/csvmodelwriter.cpp \ + qt/guifileutil.cpp \ qt/guiutil.cpp \ qt/intro.cpp \ qt/modaloverlay.cpp \ diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index aa4ec04497a..8780f0b15ef 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 7822d4c5f3a..4a920c5cda1 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/src/qt/guifileutil.cpp b/src/qt/guifileutil.cpp new file mode 100644 index 00000000000..acabff253eb --- /dev/null +++ b/src/qt/guifileutil.cpp @@ -0,0 +1,313 @@ +// Copyright (c) 2011-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include + +#ifdef WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#endif // WIN32 + +#ifdef Q_OS_LINUX +#include +#include +#endif // Q_OS_LINUX + +#include +#include +#include +#include +#include +#include +#include +#ifdef Q_OS_MAC +#include +#endif // Q_OS_MAC + +namespace GUIUtil { + +QString getDefaultDataDirectory() +{ + return boostPathToQString(GetDefaultDataDir()); +} + +QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, + const QString &filter, + QString *selectedSuffixOut) +{ + QString selectedFilter; + QString myDir; + if(dir.isEmpty()) // Default to user documents location + { + myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + } + else + { + myDir = dir; + } + /* Directly convert path to native OS path separators */ + QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter)); + + /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */ + QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]"); + QString selectedSuffix; + if(filter_re.exactMatch(selectedFilter)) + { + selectedSuffix = filter_re.cap(1); + } + + /* Add suffix if needed */ + QFileInfo info(result); + if(!result.isEmpty()) + { + if(info.suffix().isEmpty() && !selectedSuffix.isEmpty()) + { + /* No suffix specified, add selected suffix */ + if(!result.endsWith(".")) + result.append("."); + result.append(selectedSuffix); + } + } + + /* Return selected suffix if asked to */ + if(selectedSuffixOut) + { + *selectedSuffixOut = selectedSuffix; + } + return result; +} + +QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, + const QString &filter, + QString *selectedSuffixOut) +{ + QString selectedFilter; + QString myDir; + if(dir.isEmpty()) // Default to user documents location + { + myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + } + else + { + myDir = dir; + } + /* Directly convert path to native OS path separators */ + QString result = QDir::toNativeSeparators(QFileDialog::getOpenFileName(parent, caption, myDir, filter, &selectedFilter)); + + if(selectedSuffixOut) + { + /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */ + QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]"); + QString selectedSuffix; + if(filter_re.exactMatch(selectedFilter)) + { + selectedSuffix = filter_re.cap(1); + } + *selectedSuffixOut = selectedSuffix; + } + return result; +} + +void openDebugLogfile() +{ + fs::path pathDebug = GetDataDir() / "debug.log"; + + /* Open debug.log with the associated application */ + if (fs::exists(pathDebug)) + QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug))); +} + +bool openBitcoinConf() +{ + fs::path pathConfig = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); + + /* Create the file */ + fsbridge::ofstream configFile(pathConfig, std::ios_base::app); + + if (!configFile.good()) + return false; + + configFile.close(); + + /* Open bitcoin.conf with the associated application */ + bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig))); +#ifdef Q_OS_MAC + // Workaround for macOS-specific behavior; see #15409. + if (!res) { + res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", boostPathToQString(pathConfig)}); + } +#endif + + return res; +} + +#ifdef WIN32 +fs::path static StartupShortcutPath() +{ + std::string chain = gArgs.GetChainName(); + if (chain == CBaseChainParams::MAIN) + return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk"; + if (chain == CBaseChainParams::TESTNET) // Remove this special case when CBaseChainParams::TESTNET = "testnet4" + return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin (testnet).lnk"; + return GetSpecialFolderPath(CSIDL_STARTUP) / strprintf("Bitcoin (%s).lnk", chain); +} + +bool GetStartOnSystemStartup() +{ + // check for Bitcoin*.lnk + return fs::exists(StartupShortcutPath()); +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + // If the shortcut exists already, remove it for updating + fs::remove(StartupShortcutPath()); + + if (fAutoStart) + { + CoInitialize(nullptr); + + // Get a pointer to the IShellLink interface. + IShellLinkW* psl = nullptr; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, + CLSCTX_INPROC_SERVER, IID_IShellLinkW, + reinterpret_cast(&psl)); + + if (SUCCEEDED(hres)) + { + // Get the current executable path + WCHAR pszExePath[MAX_PATH]; + GetModuleFileNameW(nullptr, pszExePath, ARRAYSIZE(pszExePath)); + + // Start client minimized + QString strArgs = "-min"; + // Set -testnet /-regtest options + strArgs += QString::fromStdString(strprintf(" -chain=%s", gArgs.GetChainName())); + + // Set the path to the shortcut target + psl->SetPath(pszExePath); + PathRemoveFileSpecW(pszExePath); + psl->SetWorkingDirectory(pszExePath); + psl->SetShowCmd(SW_SHOWMINNOACTIVE); + psl->SetArguments(strArgs.toStdWString().c_str()); + + // Query IShellLink for the IPersistFile interface for + // saving the shortcut in persistent storage. + IPersistFile* ppf = nullptr; + hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast(&ppf)); + if (SUCCEEDED(hres)) + { + // Save the link by calling IPersistFile::Save. + hres = ppf->Save(StartupShortcutPath().wstring().c_str(), TRUE); + ppf->Release(); + psl->Release(); + CoUninitialize(); + return true; + } + psl->Release(); + } + CoUninitialize(); + return false; + } + return true; +} +#elif defined(Q_OS_LINUX) + +// Follow the Desktop Application Autostart Spec: +// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html + +fs::path static GetAutostartDir() +{ + char* pszConfigHome = getenv("XDG_CONFIG_HOME"); + if (pszConfigHome) return fs::path(pszConfigHome) / "autostart"; + char* pszHome = getenv("HOME"); + if (pszHome) return fs::path(pszHome) / ".config" / "autostart"; + return fs::path(); +} + +fs::path static GetAutostartFilePath() +{ + std::string chain = gArgs.GetChainName(); + if (chain == CBaseChainParams::MAIN) + return GetAutostartDir() / "bitcoin.desktop"; + return GetAutostartDir() / strprintf("bitcoin-%s.desktop", chain); +} + +bool GetStartOnSystemStartup() +{ + fsbridge::ifstream optionFile(GetAutostartFilePath()); + if (!optionFile.good()) + return false; + // Scan through file for "Hidden=true": + std::string line; + while (!optionFile.eof()) + { + getline(optionFile, line); + if (line.find("Hidden") != std::string::npos && + line.find("true") != std::string::npos) + return false; + } + optionFile.close(); + + return true; +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + if (!fAutoStart) + fs::remove(GetAutostartFilePath()); + else + { + char pszExePath[MAX_PATH+1]; + ssize_t r = readlink("/proc/self/exe", pszExePath, sizeof(pszExePath) - 1); + if (r == -1) + return false; + pszExePath[r] = '\0'; + + fs::create_directories(GetAutostartDir()); + + fsbridge::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc); + if (!optionFile.good()) + return false; + std::string chain = gArgs.GetChainName(); + // Write a bitcoin.desktop file to the autostart directory: + optionFile << "[Desktop Entry]\n"; + optionFile << "Type=Application\n"; + if (chain == CBaseChainParams::MAIN) + optionFile << "Name=Bitcoin\n"; + else + optionFile << strprintf("Name=Bitcoin (%s)\n", chain); + optionFile << "Exec=" << pszExePath << strprintf(" -min -chain=%s\n", chain); + optionFile << "Terminal=false\n"; + optionFile << "Hidden=false\n"; + optionFile.close(); + } + return true; +} + +#else + +bool GetStartOnSystemStartup() { return false; } +bool SetStartOnSystemStartup(bool fAutoStart) { return false; } + +#endif + +fs::path qstringToBoostPath(const QString &path) +{ + return fs::path(path.toStdString()); +} + +QString boostPathToQString(const fs::path &path) +{ + return QString::fromStdString(path.string()); +} + +} // namespace GUIUtil diff --git a/src/qt/guifileutil.h b/src/qt/guifileutil.h new file mode 100644 index 00000000000..e2d712d2f5a --- /dev/null +++ b/src/qt/guifileutil.h @@ -0,0 +1,68 @@ +// Copyright (c) 2011-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_GUIFILEUTIL_H +#define BITCOIN_QT_GUIFILEUTIL_H + +#include + +#include + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +/** Filesystem utility functions used by the GUI. + */ +namespace GUIUtil { +/** + * Determine default data directory for operating system. + */ +QString getDefaultDataDirectory(); + +/** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix + when no suffix is provided by the user. + + @param[in] parent Parent window (or 0) + @param[in] caption Window caption (or empty, for default) + @param[in] dir Starting directory (or empty, to default to documents directory) + @param[in] filter Filter specification such as "Comma Separated Files (*.csv)" + @param[out] selectedSuffixOut Pointer to return the suffix (file type) that was selected (or 0). + Can be useful when choosing the save file format based on suffix. + */ +QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, + const QString &filter, + QString *selectedSuffixOut); + +/** Get open filename, convenience wrapper for QFileDialog::getOpenFileName. + + @param[in] parent Parent window (or 0) + @param[in] caption Window caption (or empty, for default) + @param[in] dir Starting directory (or empty, to default to documents directory) + @param[in] filter Filter specification such as "Comma Separated Files (*.csv)" + @param[out] selectedSuffixOut Pointer to return the suffix (file type) that was selected (or 0). + Can be useful when choosing the save file format based on suffix. + */ +QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, + const QString &filter, + QString *selectedSuffixOut); + +// Open debug.log +void openDebugLogfile(); + +// Open the config file +bool openBitcoinConf(); + +bool GetStartOnSystemStartup(); +bool SetStartOnSystemStartup(bool fAutoStart); + +/* Convert QString to OS specific boost path through UTF-8 */ +fs::path qstringToBoostPath(const QString &path); + +/* Convert OS specific boost path to QString through UTF-8 */ +QString boostPathToQString(const fs::path &path); + +} // namespace GUIUtil + +#endif // BITCOIN_QT_GUIFILEUTIL_H diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index bab17562a60..35c271d8fd4 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -18,24 +17,12 @@ #include #include