diff --git a/src/dialog/AboutDialog.cpp b/src/dialog/AboutDialog.cpp index 898c3532..6825400b 100644 --- a/src/dialog/AboutDialog.cpp +++ b/src/dialog/AboutDialog.cpp @@ -5,7 +5,7 @@ #include "ui_AboutDialog.h" #include "config-feather.h" -#include "utils/utils.h" +#include "utils/Utils.h" AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent) diff --git a/src/dialog/LocalMoneroInfoDialog.cpp b/src/dialog/LocalMoneroInfoDialog.cpp index f9216429..d3df1a7f 100644 --- a/src/dialog/LocalMoneroInfoDialog.cpp +++ b/src/dialog/LocalMoneroInfoDialog.cpp @@ -5,7 +5,7 @@ #include "ui_LocalMoneroInfoDialog.h" #include "utils/config.h" -#include "utils/utils.h" +#include "utils/Utils.h" LocalMoneroInfoDialog::LocalMoneroInfoDialog(QWidget *parent, LocalMoneroModel *model, int row) : QDialog(parent) diff --git a/src/dialog/OutputInfoDialog.cpp b/src/dialog/OutputInfoDialog.cpp index 1779e3d2..772d1fb4 100644 --- a/src/dialog/OutputInfoDialog.cpp +++ b/src/dialog/OutputInfoDialog.cpp @@ -5,7 +5,7 @@ #include "ui_OutputInfoDialog.h" #include "model/ModelUtils.h" -#include "utils/utils.h" +#include "utils/Utils.h" OutputInfoDialog::OutputInfoDialog(CoinsInfo *cInfo, QWidget *parent) : QDialog(parent) diff --git a/src/dialog/SignVerifyDialog.cpp b/src/dialog/SignVerifyDialog.cpp index 96244e9d..490aa566 100644 --- a/src/dialog/SignVerifyDialog.cpp +++ b/src/dialog/SignVerifyDialog.cpp @@ -6,7 +6,7 @@ #include -#include "utils/utils.h" +#include "utils/Utils.h" SignVerifyDialog::SignVerifyDialog(Wallet *wallet, QWidget *parent) : QDialog(parent) diff --git a/src/dialog/TxInfoDialog.cpp b/src/dialog/TxInfoDialog.cpp index dc549893..d873530d 100644 --- a/src/dialog/TxInfoDialog.cpp +++ b/src/dialog/TxInfoDialog.cpp @@ -15,7 +15,7 @@ #include "libwalletqt/Transfer.h" #include "libwalletqt/WalletManager.h" #include "model/ModelUtils.h" -#include "utils.h" +#include "Utils.h" TxInfoDialog::TxInfoDialog(QSharedPointer ctx, TransactionInfo *txInfo, QWidget *parent) : QDialog(parent) diff --git a/src/dialog/TxProofDialog.cpp b/src/dialog/TxProofDialog.cpp index 05b43b14..9299c4d5 100644 --- a/src/dialog/TxProofDialog.cpp +++ b/src/dialog/TxProofDialog.cpp @@ -8,7 +8,7 @@ #include "libwalletqt/Transfer.h" #include "utils/Icons.h" -#include "utils/utils.h" +#include "utils/Utils.h" TxProofDialog::TxProofDialog(QWidget *parent, QSharedPointer ctx, TransactionInfo *txInfo) : QDialog(parent) diff --git a/src/dialog/UpdateDialog.cpp b/src/dialog/UpdateDialog.cpp index 2dc47a55..af673d85 100644 --- a/src/dialog/UpdateDialog.cpp +++ b/src/dialog/UpdateDialog.cpp @@ -10,7 +10,7 @@ #include "utils/networking.h" #include "utils/NetworkManager.h" #include "utils/Updater.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include "zip.h" diff --git a/src/dialog/VerifyProofDialog.cpp b/src/dialog/VerifyProofDialog.cpp index 12e592b2..d696f8f6 100644 --- a/src/dialog/VerifyProofDialog.cpp +++ b/src/dialog/VerifyProofDialog.cpp @@ -8,7 +8,7 @@ #include "libwalletqt/WalletManager.h" #include "model/ModelUtils.h" -#include "utils/utils.h" +#include "utils/Utils.h" VerifyProofDialog::VerifyProofDialog(Wallet *wallet, QWidget *parent) : QDialog(parent) diff --git a/src/libwalletqt/TransactionHistory.cpp b/src/libwalletqt/TransactionHistory.cpp index e355275c..50a76f06 100644 --- a/src/libwalletqt/TransactionHistory.cpp +++ b/src/libwalletqt/TransactionHistory.cpp @@ -3,7 +3,7 @@ #include "TransactionHistory.h" #include "TransactionInfo.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include "utils/AppData.h" #include "utils/config.h" diff --git a/src/model/AddressBookModel.cpp b/src/model/AddressBookModel.cpp index 9f96a8f3..84380f10 100644 --- a/src/model/AddressBookModel.cpp +++ b/src/model/AddressBookModel.cpp @@ -4,7 +4,7 @@ #include "AddressBookModel.h" #include "AddressBook.h" #include "ModelUtils.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include "utils/Icons.h" AddressBookModel::AddressBookModel(QObject *parent, AddressBook *addressBook) diff --git a/src/model/HistoryView.cpp b/src/model/HistoryView.cpp index bc3f89b1..be1da45b 100644 --- a/src/model/HistoryView.cpp +++ b/src/model/HistoryView.cpp @@ -5,7 +5,7 @@ #include "TransactionHistoryProxyModel.h" #include "libwalletqt/TransactionInfo.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include #include diff --git a/src/model/LocalMoneroModel.cpp b/src/model/LocalMoneroModel.cpp index 065a7bed..9080ec10 100644 --- a/src/model/LocalMoneroModel.cpp +++ b/src/model/LocalMoneroModel.cpp @@ -3,7 +3,7 @@ #include "LocalMoneroModel.h" #include -#include "utils/utils.h" +#include "utils/Utils.h" LocalMoneroModel::LocalMoneroModel(QObject *parent) : QAbstractTableModel(parent) diff --git a/src/model/WalletKeysFilesModel.cpp b/src/model/WalletKeysFilesModel.cpp index 37fd03d5..225461a1 100644 --- a/src/model/WalletKeysFilesModel.cpp +++ b/src/model/WalletKeysFilesModel.cpp @@ -3,7 +3,7 @@ #include "WalletKeysFilesModel.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include #include #include "config.h" diff --git a/src/utils/RestoreHeightLookup.h b/src/utils/RestoreHeightLookup.h index b0499f03..9109be2b 100644 --- a/src/utils/RestoreHeightLookup.h +++ b/src/utils/RestoreHeightLookup.h @@ -10,7 +10,7 @@ #include #include "networktype.h" -#include "utils/utils.h" +#include "utils/Utils.h" struct RestoreHeightLookup { NetworkType::Type type; diff --git a/src/utils/TorManager.cpp b/src/utils/TorManager.cpp index 3a9c7bb7..e19ea541 100644 --- a/src/utils/TorManager.cpp +++ b/src/utils/TorManager.cpp @@ -8,7 +8,7 @@ #include #include -#include "utils/utils.h" +#include "utils/Utils.h" #include "utils/os/tails.h" #include "appcontext.h" #include "config-feather.h" diff --git a/src/utils/Updater.cpp b/src/utils/Updater.cpp index 6cd339e3..be00e832 100644 --- a/src/utils/Updater.cpp +++ b/src/utils/Updater.cpp @@ -6,10 +6,10 @@ #include #include -#include "utils.h" +#include "Utils.h" Updater::Updater() { - std::string featherWallet = Utils::fileGetContents(":/assets/gpg_keys/featherwallet.asc").toStdString(); + std::string featherWallet = Utils::fileOpen(":/assets/gpg_keys/featherwallet.asc").toStdString(); m_maintainers.emplace_back(featherWallet); } diff --git a/src/utils/utils.cpp b/src/utils/Utils.cpp similarity index 73% rename from src/utils/utils.cpp rename to src/utils/Utils.cpp index a779c4a8..eb170169 100644 --- a/src/utils/utils.cpp +++ b/src/utils/Utils.cpp @@ -1,54 +1,29 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include #include #include #include -#include #include -#include #include #include "constants.h" #include "networktype.h" -#include "utils.h" +#include "Utils.h" #include "utils/ColorScheme.h" #include "utils/config.h" #include "utils/os/tails.h" #include "utils/os/whonix.h" -QByteArray Utils::fileGetContents(const QString &path) -{ - QFile file(path); - if (!file.open(QFile::ReadOnly)) - { - throw std::runtime_error(QString("failed to open %1").arg(path).toStdString()); - } - - QByteArray data; - data.resize(file.size()); - if (file.read(data.data(), data.size()) != data.size()) - { - throw std::runtime_error(QString("failed to read %1").arg(path).toStdString()); - } - - return data; -} - -bool Utils::fileExists(const QString &path) { +namespace Utils { +bool fileExists(const QString &path) { QFileInfo check_file(path); return check_file.exists() && check_file.isFile(); } -bool Utils::dirExists(const QString &path) { - QDir pathDir(path); - return pathDir.exists(); -} - -QByteArray Utils::fileOpen(const QString &path) { +QByteArray fileOpen(const QString &path) { QFile file(path); - if(!file.open(QFile::ReadOnly | QFile::Text)) { + if (!file.open(QFile::ReadOnly | QFile::Text)) { return QByteArray(); } @@ -57,10 +32,11 @@ QByteArray Utils::fileOpen(const QString &path) { return data; } -QByteArray Utils::fileOpenQRC(const QString &path) { +QByteArray fileOpenQRC(const QString &path) { QFile file(path); - if(!file.open(QIODevice::ReadOnly)) { + if (!file.open(QIODevice::ReadOnly)) { qDebug() << "error: " << file.errorString(); + return QByteArray(); } QByteArray data = file.readAll(); @@ -68,9 +44,9 @@ QByteArray Utils::fileOpenQRC(const QString &path) { return data; } -bool Utils::fileWrite(const QString &path, const QString &data) { +bool fileWrite(const QString &path, const QString &data) { QFile file(path); - if(file.open(QIODevice::WriteOnly)){ + if (file.open(QIODevice::WriteOnly)) { QTextStream out(&file); out << data << Qt::endl; file.close(); return true; @@ -78,127 +54,27 @@ bool Utils::fileWrite(const QString &path, const QString &data) { return false; } -bool Utils::validateJSON(const QByteArray &blob) { - QJsonDocument doc = QJsonDocument::fromJson(blob); - QString jsonString = doc.toJson(QJsonDocument::Indented); - return !jsonString.isEmpty(); -} - -bool Utils::readJsonFile(QIODevice &device, QSettings::SettingsMap &map) { - QJsonDocument json = QJsonDocument::fromJson(device.readAll()); - map = json.object().toVariantMap(); - return true; -} - -bool Utils::writeJsonFile(QIODevice &device, const QSettings::SettingsMap &map) { - device.write(QJsonDocument(QJsonObject::fromVariantMap(map)).toJson(QJsonDocument::Indented)); - return true; -} - -void Utils::applicationLogHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - const QString fn = context.function ? QString::fromUtf8(context.function) : ""; - const QString date = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); - QString line; - - switch (type) { - case QtDebugMsg: - line = QString("[%1 D] %2(:%3) %4\n").arg(date).arg(fn).arg(context.line).arg(msg); - fprintf(stderr, "%s", line.toLatin1().data()); - break; - case QtInfoMsg: - line = QString("[%1 I] %2\n").arg(date).arg(msg); - fprintf(stdout, "%s", line.toLatin1().data()); - break; - case QtWarningMsg: - line = QString("[%1 W] %2(:%3) %4\n").arg(date).arg(fn).arg(context.line).arg(msg); - fprintf(stdout, "%s", line.toLatin1().data()); - break; - case QtCriticalMsg: - line = QString("[%1 C] %2(:%3) %4\n").arg(date).arg(fn).arg(context.line).arg(msg); - fprintf(stderr, "%s", line.toLatin1().data()); - break; - case QtFatalMsg: - line = QString("[%1 F] %2(:%3) %4\n").arg(date).arg(fn).arg(context.line).arg(msg); - fprintf(stderr, "%s", line.toLatin1().data()); - break; +bool pixmapWrite(const QString &path, const QPixmap &pixmap) { + qDebug() << "Writing xdg icon: " << path; + QFile file(path); + QFileInfo iconInfo(file); + QDir().mkpath(iconInfo.path()); + if(file.open(QIODevice::WriteOnly)){ + pixmap.save(&file, "PNG"); + file.close(); + return true; } - - auto message = logMessage(type, line, fn); - - //emit applicationLogUpdated(message); + return false; } -void Utils::desktopNotify(const QString &title, const QString &message, int duration) { - QStringList notify_send = QStringList() << title << message << "-t" << QString::number(duration); - QStringList kdialog = QStringList() << title << message; - QStringList macos = QStringList() << "-e" << QString(R"(display notification "%1" with title "%2")").arg(message).arg(title); -#if defined(Q_OS_LINUX) - QProcess process; - if (Utils::fileExists("/usr/bin/kdialog")) - process.start("/usr/bin/kdialog", kdialog); - else if (Utils::fileExists("/usr/bin/notify-send")) - process.start("/usr/bin/notify-send", notify_send); - process.waitForFinished(-1); - QString stdout = process.readAllStandardOutput(); - QString stderr = process.readAllStandardError(); -#elif defined(Q_OS_MACOS) - QProcess process; - // @TODO: need to escape special chars with "\" - process.start("osascript", macos); - process.waitForFinished(-1); - QString stdout = process.readAllStandardOutput(); - QString stderr = process.readAllStandardError(); -#endif -} - -bool Utils::portOpen(const QString &hostname, quint16 port){ - QTcpSocket socket; - socket.connectToHost(hostname, port); - return socket.waitForConnected(600); -} - -QString Utils::barrayToString(const QByteArray &data) { - return QString(QTextCodec::codecForMib(106)->toUnicode(data)); -} - -void Utils::externalLinkWarning(QWidget *parent, const QString &url){ - if(!config()->get(Config::warnOnExternalLink).toBool()) { - QDesktopServices::openUrl(QUrl(url)); - return; - } - - QString body = "You are about to open the following link:\n\n"; - body += QString("%1").arg(url); - - if (!(TailsOS::detect() || WhonixOS::detect())) - body += "\n\nYou will NOT be using Tor."; - - - QMessageBox linkWarning(parent); - linkWarning.setWindowTitle("External link warning"); - linkWarning.setText(body); - QPushButton *copy = linkWarning.addButton("Copy link", QMessageBox::HelpRole); - linkWarning.addButton(QMessageBox::Cancel); - linkWarning.addButton(QMessageBox::Ok); - linkWarning.setDefaultButton(QMessageBox::Ok); - - linkWarning.exec(); - - if (linkWarning.clickedButton() == copy) { - Utils::copyToClipboard(url); - } else if (linkWarning.result() == QMessageBox::Ok) { - QDesktopServices::openUrl(QUrl(url)); - } -} - -QStringList Utils::fileFind(const QRegExp &pattern, const QString &baseDir, int level, int depth, const int maxPerDir) { +QStringList fileFind(const QRegExp &pattern, const QString &baseDir, int level, int depth, const int maxPerDir) { // like `find /foo -name -maxdepth 2 "*.jpg"` QStringList rtn; QDir dir(baseDir); dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDot | QDir::NoDotDot); int fileCount = 0; - for(const auto &fileInfo: dir.entryInfoList({"*"})) { + for (const auto &fileInfo: dir.entryInfoList({"*"})) { fileCount += 1; if(fileCount > maxPerDir) return rtn; if(!fileInfo.isReadable()) @@ -209,7 +85,7 @@ QStringList Utils::fileFind(const QRegExp &pattern, const QString &baseDir, int if (fileInfo.isDir()) { if (level + 1 <= depth) - rtn << Utils::fileFind(pattern, path, level + 1, depth, maxPerDir); + rtn << fileFind(pattern, path, level + 1, depth, maxPerDir); } else if (pattern.exactMatch(fn)) rtn << path; @@ -217,7 +93,57 @@ QStringList Utils::fileFind(const QRegExp &pattern, const QString &baseDir, int return rtn; } -void Utils::copyToClipboard(const QString &string){ +bool dirExists(const QString &path) { + QDir pathDir(path); + return pathDir.exists(); +} + +QString defaultWalletDir() { + QString portablePath = QCoreApplication::applicationDirPath().append("/%1"); + if (QFile::exists(portablePath.arg(".portable"))) { + return portablePath.arg("feather_data/wallets"); + } + + if (TailsOS::detect()) { + QString path = []{ + QString appImagePath = qgetenv("APPIMAGE"); + if (appImagePath.isEmpty()) { + qDebug() << "Not an appimage, using currentPath()"; + return QDir::currentPath() + "/.feather/Monero/wallets"; + } + + QFileInfo appImageDir(appImagePath); + return appImageDir.absoluteDir().path() + "/.feather/Monero/wallets"; + }(); + + return path; + } + +#if defined(Q_OS_LINUX) or defined(Q_OS_MAC) + return QString("%1/Monero/wallets").arg(QDir::homePath()); +#elif defined(Q_OS_WIN) + return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero/wallets"; +#endif +} + +bool validateJSON(const QByteArray &blob) { + QJsonDocument doc = QJsonDocument::fromJson(blob); + QString jsonString = doc.toJson(QJsonDocument::Indented); + return !jsonString.isEmpty(); +} + +bool readJsonFile(QIODevice &device, QSettings::SettingsMap &map) { + QJsonDocument json = QJsonDocument::fromJson(device.readAll()); + map = json.object().toVariantMap(); + return true; +} + +bool writeJsonFile(QIODevice &device, const QSettings::SettingsMap &map) { + device.write(QJsonDocument(QJsonObject::fromVariantMap(map)).toJson(QJsonDocument::Indented)); + return true; +} + +void copyToClipboard(const QString &string){ QClipboard * clipboard = QApplication::clipboard(); if (!clipboard) { qWarning() << "Unable to access clipboard"; @@ -232,7 +158,7 @@ void Utils::copyToClipboard(const QString &string){ #endif } -QString Utils::copyFromClipboard() { +QString copyFromClipboard() { QClipboard * clipboard = QApplication::clipboard(); if (!clipboard) { qWarning() << "Unable to access clipboard"; @@ -241,7 +167,181 @@ QString Utils::copyFromClipboard() { return clipboard->text(); } -QString Utils::blockExplorerLink(const QString &blockExplorer, NetworkType::Type nettype, const QString &txid) { +QString xdgDesktopEntry(){ + return QString( + "[Desktop Entry]\n" + "Name=Feather\n" + "GenericName=Feather\n" + "X-GNOME-FullName=Feather\n" + "Comment=a free Monero desktop wallet\n" + "Keywords=Monero;\n" + "Exec=\"%1\" %u\n" + "Terminal=false\n" + "Type=Application\n" + "Icon=monero\n" + "Categories=Network;GNOME;Qt;\n" + "StartupNotify=true\n" + ).arg(QApplication::applicationFilePath()); +} + +bool xdgDesktopEntryWrite(const QString &path){ + QString mime = xdgDesktopEntry(); + QFileInfo file(path); + QDir().mkpath(file.path()); + qDebug() << "Writing xdg desktop entry: " << path; + return fileWrite(path, mime); +} + +void xdgRefreshApplications(){ + QStringList args = {QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)}; + QProcess::startDetached("update-desktop-database", args); +} + +bool xdgDesktopEntryRegister() { +#if defined(Q_OS_MACOS) + return false; +#endif +#if defined(Q_OS_WIN) + // @TODO: implement + return false; +#endif + + QPixmap appIcon(":assets/images/feather.png"); + QString pathIcon = QString("%1/.local/share/icons/feather.png").arg(QDir::homePath()); + if (!fileExists(pathIcon)) { + pixmapWrite(pathIcon, appIcon); + } + xdgDesktopEntryWrite(QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)); + + xdgRefreshApplications(); + return true; +} + +bool portOpen(const QString &hostname, quint16 port){ + QTcpSocket socket; + socket.connectToHost(hostname, port); + return socket.waitForConnected(600); +} + +quint16 getDefaultRpcPort(NetworkType::Type type) { + switch (type) { + case NetworkType::Type::MAINNET: + return 18081; + case NetworkType::Type::TESTNET: + return 28081; + case NetworkType::Type::STAGENET: + return 38081; + } + return 18081; +} + +bool isTorsocks() { +#if defined(Q_OS_MAC) + return qgetenv("DYLD_INSERT_LIBRARIES").indexOf("libtorsocks") >= 0; +#elif defined(Q_OS_LINUX) + return qgetenv("LD_PRELOAD").indexOf("libtorsocks") >= 0; +#else + return false; +#endif +} + +double roundSignificant(double N, double n) +{ + int h; + double b, d, e, i, j, m, f; + b = N; + + for (i = 0; b >= 1; ++i) + b = b / 10; + + d = n - i; + b = N; + b = b * pow(10, d); + e = b + 0.5; + if ((float)e == (float)ceil(b)) { + f = (ceil(b)); + h = f - 2; + if (h % 2 != 0) { + e = e - 1; + } + } + j = floor(e); + m = pow(10, d); + j = j / m; + return j; +} + +int maxLength(const QVector &array) { + int maxLength = 0; + for (const auto &str: array) { + auto length = str.length(); + if (length > maxLength) { + maxLength = length; + } + } + return maxLength; +} + +QString formatBytes(quint64 bytes) +{ + QVector sizes = { "B", "KB", "MB", "GB", "TB" }; + + int i; + double _data; + for (i = 0; i < sizes.count() && bytes >= 10000; i++, bytes /= 1000) + _data = bytes / 1000.0; + + if (_data < 0) + _data = 0; + + // unrealistic + if (_data > 10000) + _data = 0; + + return QString("%1 %2").arg(QString::number(_data, 'f', 1), sizes[i]); +} + +QMap localeCache = {}; +QLocale getCurrencyLocale(const QString ¤cyCode) { + QLocale locale; + if (localeCache.contains(currencyCode)) { + locale = localeCache[currencyCode]; + } else { + QList allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); + for (const auto& locale_: allLocales) { + if (locale_.currencySymbol(QLocale::CurrencyIsoCode) == currencyCode) { + locale = locale_; + } + } + localeCache[currencyCode] = locale; + } + return locale; +} + +QString amountToCurrencyString(double amount, const QString ¤cyCode) { + QLocale locale = getCurrencyLocale(currencyCode); + + // \xC2\xA0 = UTF-8 non-breaking space, it looks off. + if (currencyCode == "USD") + return locale.toCurrencyString(amount, "$").remove("\xC2\xA0"); + + return locale.toCurrencyString(amount).remove("\xC2\xA0"); +} + +QStandardItem *qStandardItem(const QString& text) { + auto font = QApplication::font(); + return Utils::qStandardItem(text, font); +} + +QStandardItem *qStandardItem(const QString& text, QFont &font) { + // stupid Qt doesnt set font sizes correctly on OSX + // @TODO: memleak + auto item = new QStandardItem(text); + item->setFont(font); + return item; +} + +QString blockExplorerLink(const QString &blockExplorer, NetworkType::Type nettype, const QString &txid) { if (blockExplorer == "exploremonero.com") { if (nettype == NetworkType::MAINNET) { return QString("https://exploremonero.com/transaction/%1").arg(txid); @@ -270,191 +370,60 @@ QString Utils::blockExplorerLink(const QString &blockExplorer, NetworkType::Type return QString(""); } -QStandardItem *Utils::qStandardItem(const QString& text) { - auto font = QApplication::font(); - return Utils::qStandardItem(text, font); +void externalLinkWarning(QWidget *parent, const QString &url){ + if (!config()->get(Config::warnOnExternalLink).toBool()) { + QDesktopServices::openUrl(QUrl(url)); + return; + } + + QString body = "You are about to open the following link:\n\n"; + body += QString("%1").arg(url); + + if (!(TailsOS::detect() || WhonixOS::detect())) + body += "\n\nYou will NOT be using Tor."; + + + QMessageBox linkWarning(parent); + linkWarning.setWindowTitle("External link warning"); + linkWarning.setText(body); + QPushButton *copy = linkWarning.addButton("Copy link", QMessageBox::HelpRole); + linkWarning.addButton(QMessageBox::Cancel); + linkWarning.addButton(QMessageBox::Ok); + linkWarning.setDefaultButton(QMessageBox::Ok); + + linkWarning.exec(); + + if (linkWarning.clickedButton() == copy) { + Utils::copyToClipboard(url); + } else if (linkWarning.result() == QMessageBox::Ok) { + QDesktopServices::openUrl(QUrl(url)); + } } -QStandardItem *Utils::qStandardItem(const QString& text, QFont &font) { - // stupid Qt doesnt set font sizes correctly on OSX - // @TODO: memleak - auto item = new QStandardItem(text); - item->setFont(font); - return item; -} - -QString Utils::getUnixAccountName() { - QString accountName = qgetenv("USER"); // mac/linux - if (accountName.isEmpty()) - accountName = qgetenv("USERNAME"); // Windows - if (accountName.isEmpty()) - throw std::runtime_error("Could derive system account name from env vars: USER or USERNAME"); - return accountName; -} - -QString Utils::xdgDesktopEntry(){ - return QString( - "[Desktop Entry]\n" - "Name=Feather\n" - "GenericName=Feather\n" - "X-GNOME-FullName=Feather\n" - "Comment=a free Monero desktop wallet\n" - "Keywords=Monero;\n" - "Exec=\"%1\" %u\n" - "Terminal=false\n" - "Type=Application\n" - "Icon=monero\n" - "Categories=Network;GNOME;Qt;\n" - "StartupNotify=true\n" - "X-GNOME-Bugzilla-Bugzilla=GNOME\n" - "X-GNOME-UsesNotifications=true\n" - ).arg(QApplication::applicationFilePath()); -} - -bool Utils::xdgDesktopEntryWrite(const QString &path){ - QString mime = xdgDesktopEntry(); - QFileInfo file(path); - QDir().mkpath(file.path()); - qDebug() << "Writing xdg desktop entry: " << path; - return Utils::fileWrite(path, mime); -} - -void Utils::xdgRefreshApplications(){ - QStringList args = {QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)}; +void desktopNotify(const QString &title, const QString &message, int duration) { + QStringList notify_send = QStringList() << title << message << "-t" << QString::number(duration); + QStringList kdialog = QStringList() << title << message; + QStringList macos = QStringList() << "-e" << QString(R"(display notification "%1" with title "%2")").arg(message).arg(title); +#if defined(Q_OS_LINUX) QProcess process; - process.start("update-desktop-database", args); - process.waitForFinished(2500); - process.close(); -} - -bool Utils::xdgDesktopEntryRegister() { -#if defined(Q_OS_MACOS) - return false; + if (fileExists("/usr/bin/kdialog")) + process.start("/usr/bin/kdialog", kdialog); + else if (fileExists("/usr/bin/notify-send")) + process.start("/usr/bin/notify-send", notify_send); + process.waitForFinished(-1); + QString stdout = process.readAllStandardOutput(); + QString stderr = process.readAllStandardError(); +#elif defined(Q_OS_MACOS) + QProcess process; + // @TODO: need to escape special chars with "\" + process.start("osascript", macos); + process.waitForFinished(-1); + QString stdout = process.readAllStandardOutput(); + QString stderr = process.readAllStandardError(); #endif -#if defined(Q_OS_WIN) - // @TODO: implement - return false; -#endif - // no support for Tails here - if(TailsOS::detect()) return false; - - QString writeLocations = "Write locations:\n"; - writeLocations += QString("- %1\n").arg(xdgPaths.pathApp); - writeLocations += QString("- %1\n").arg(xdgPaths.pathIcon); - - QPixmap appIcon(":assets/images/feather.png"); - if (!Utils::fileExists(xdgPaths.pathIcon)) - Utils::pixmapWrite(xdgPaths.pathIcon, appIcon); - Utils::xdgDesktopEntryWrite(xdgPaths.pathApp); - Utils::xdgRefreshApplications(); - return true; } -bool Utils::pixmapWrite(const QString &path, const QPixmap &pixmap) { - qDebug() << "Writing xdg icon: " << path; - QFile file(path); - QFileInfo iconInfo(file); - QDir().mkpath(iconInfo.path()); - if(file.open(QIODevice::WriteOnly)){ - pixmap.save(&file, "PNG"); - file.close(); - return true; - } - return false; -} - -QFont Utils::relativeFont(int delta) { - auto font = QApplication::font(); - font.setPointSize(font.pointSize() + delta); - return font; -} - -double Utils::roundSignificant(double N, double n) -{ - int h; - double b, d, e, i, j, m, f; - b = N; - - for (i = 0; b >= 1; ++i) - b = b / 10; - - d = n - i; - b = N; - b = b * pow(10, d); - e = b + 0.5; - if ((float)e == (float)ceil(b)) { - f = (ceil(b)); - h = f - 2; - if (h % 2 != 0) { - e = e - 1; - } - } - j = floor(e); - m = pow(10, d); - j = j / m; - return j; -} - -QString Utils::formatBytes(quint64 bytes) -{ - QVector sizes = { "B", "KB", "MB", "GB", "TB" }; - - int i; - double _data; - for (i = 0; i < sizes.count() && bytes >= 10000; i++, bytes /= 1000) - _data = bytes / 1000.0; - - if (_data < 0) - _data = 0; - - // unrealistic - if (_data > 10000) - _data = 0; - - return QString("%1 %2").arg(QString::number(_data, 'f', 1), sizes[i]); -} - - -QMap Utils::localeCache = {}; - -QLocale Utils::getCurrencyLocale(const QString ¤cyCode) { - QLocale locale; - if (localeCache.contains(currencyCode)) { - locale = localeCache[currencyCode]; - } else { - QList allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); - for (const auto& locale_: allLocales) { - if (locale_.currencySymbol(QLocale::CurrencyIsoCode) == currencyCode) { - locale = locale_; - } - } - localeCache[currencyCode] = locale; - } - return locale; -} - -QString Utils::amountToCurrencyString(double amount, const QString ¤cyCode) { - QLocale locale = getCurrencyLocale(currencyCode); - - // \xC2\xA0 = UTF-8 non-breaking space, it looks off. - if (currencyCode == "USD") - return locale.toCurrencyString(amount, "$").remove("\xC2\xA0"); - - return locale.toCurrencyString(amount).remove("\xC2\xA0"); -} - -int Utils::maxLength(const QVector &array) { - int maxLength = 0; - for (const auto &str: array) { - auto length = str.length(); - if (length > maxLength) { - maxLength = length; - } - } - return maxLength; -} - -QTextCharFormat Utils::addressTextFormat(const SubaddressIndex &index, quint64 amount) { +QTextCharFormat addressTextFormat(const SubaddressIndex &index, quint64 amount) { QTextCharFormat rec; if (index.isPrimary()) { rec.setBackground(QBrush(ColorScheme::YELLOW.asColor(true))); @@ -471,52 +440,51 @@ QTextCharFormat Utils::addressTextFormat(const SubaddressIndex &index, quint64 a return rec; } -bool Utils::isTorsocks() { -#if defined(Q_OS_MAC) - return qgetenv("DYLD_INSERT_LIBRARIES").indexOf("libtorsocks") >= 0; -#elif defined(Q_OS_LINUX) - return qgetenv("LD_PRELOAD").indexOf("libtorsocks") >= 0; -#else - return false; -#endif -} +void applicationLogHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + const QString fn = context.function ? QString::fromUtf8(context.function) : ""; + const QString date = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + QString line; -QString Utils::defaultWalletDir() { - QString portablePath = QCoreApplication::applicationDirPath().append("/%1"); - if (QFile::exists(portablePath.arg(".portable"))) { - return portablePath.arg("feather_data/wallets"); - } - - if (TailsOS::detect()) { - QString path = []{ - QString appImagePath = qgetenv("APPIMAGE"); - if (appImagePath.isEmpty()) { - qDebug() << "Not an appimage, using currentPath()"; - return QDir::currentPath() + "/.feather/Monero/wallets"; - } - - QFileInfo appImageDir(appImagePath); - return appImageDir.absoluteDir().path() + "/.feather/Monero/wallets"; - }(); - - return path; - } - -#if defined(Q_OS_LINUX) or defined(Q_OS_MAC) - return QString("%1/Monero/wallets").arg(QDir::homePath()); -#elif defined(Q_OS_WIN) - return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero/wallets"; -#endif -} - -quint16 Utils::getDefaultRpcPort(NetworkType::Type type) { switch (type) { - case NetworkType::Type::MAINNET: - return 18081; - case NetworkType::Type::TESTNET: - return 28081; - case NetworkType::Type::STAGENET: - return 38081; + case QtDebugMsg: + line = QString("[%1 D] %2(:%3) %4\n").arg(date).arg(fn).arg(context.line).arg(msg); + fprintf(stderr, "%s", line.toLatin1().data()); + break; + case QtInfoMsg: + line = QString("[%1 I] %2\n").arg(date).arg(msg); + fprintf(stdout, "%s", line.toLatin1().data()); + break; + case QtWarningMsg: + line = QString("[%1 W] %2(:%3) %4\n").arg(date).arg(fn).arg(context.line).arg(msg); + fprintf(stdout, "%s", line.toLatin1().data()); + break; + case QtCriticalMsg: + line = QString("[%1 C] %2(:%3) %4\n").arg(date).arg(fn).arg(context.line).arg(msg); + fprintf(stderr, "%s", line.toLatin1().data()); + break; + case QtFatalMsg: + line = QString("[%1 F] %2(:%3) %4\n").arg(date).arg(fn).arg(context.line).arg(msg); + fprintf(stderr, "%s", line.toLatin1().data()); + break; } - return 18081; +} + +QString barrayToString(const QByteArray &data) { + return QString(QTextCodec::codecForMib(106)->toUnicode(data)); +} + +QString getAccountName() { + QString accountName = qgetenv("USER"); // mac/linux + if (accountName.isEmpty()) + accountName = qgetenv("USERNAME"); // Windows + if (accountName.isEmpty()) + throw std::runtime_error("Could derive system account name from env vars: USER or USERNAME"); + return accountName; +} + +QFont relativeFont(int delta) { + auto font = QApplication::font(); + font.setPointSize(font.pointSize() + delta); + return font; +} } \ No newline at end of file diff --git a/src/utils/Utils.h b/src/utils/Utils.h new file mode 100644 index 00000000..2df870ff --- /dev/null +++ b/src/utils/Utils.h @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#ifndef FEATHER_UTILS_H +#define FEATHER_UTILS_H + +#include +#include +#include +#include + +#include "libwalletqt/Wallet.h" +#include "networktype.h" + +namespace Utils +{ + bool fileExists(const QString &path); + QByteArray fileOpen(const QString &path); + QByteArray fileOpenQRC(const QString &path); + bool fileWrite(const QString &path, const QString &data); + bool pixmapWrite(const QString &path, const QPixmap &pixmap); + QStringList fileFind(const QRegExp &pattern, const QString &baseDir, int level, int depth, int maxPerDir); + + bool dirExists(const QString &path); + QString defaultWalletDir(); + + bool validateJSON(const QByteArray &blob); + bool readJsonFile(QIODevice &device, QSettings::SettingsMap &map); + bool writeJsonFile(QIODevice &device, const QSettings::SettingsMap &map); + + void copyToClipboard(const QString &string); + QString copyFromClipboard(); + + QString xdgDesktopEntry(); + bool xdgDesktopEntryWrite(const QString &path); + void xdgRefreshApplications(); + bool xdgDesktopEntryRegister(); + + bool portOpen(const QString &hostname, quint16 port); + quint16 getDefaultRpcPort(NetworkType::Type type); + bool isTorsocks(); + + double roundSignificant(double N, double n); + int maxLength(const QVector &array); + QString formatBytes(quint64 bytes); + + QLocale getCurrencyLocale(const QString ¤cyCode); + QString amountToCurrencyString(double amount, const QString ¤cyCode); + + QStandardItem *qStandardItem(const QString &text); + QStandardItem *qStandardItem(const QString &text, QFont &font); + + QString blockExplorerLink(const QString &blockExplorer, NetworkType::Type nettype, const QString &txid); + void externalLinkWarning(QWidget *parent, const QString &url); + + void desktopNotify(const QString &title, const QString &message, int duration); + QTextCharFormat addressTextFormat(const SubaddressIndex &index, quint64 amount); + void applicationLogHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); + QString barrayToString(const QByteArray &data); + QString getAccountName(); + QFont relativeFont(int delta); + + template + QString QtEnumToString (QEnum value) { + return QString::fromStdString(std::string(QMetaEnum::fromType().valueToKey(value))); + } +} + +#endif //FEATHER_UTILS_H diff --git a/src/utils/WebsocketClient.cpp b/src/utils/WebsocketClient.cpp index 00bf16ba..5855f95d 100644 --- a/src/utils/WebsocketClient.cpp +++ b/src/utils/WebsocketClient.cpp @@ -4,7 +4,7 @@ #include "WebsocketClient.h" #include -#include "utils/utils.h" +#include "utils/Utils.h" WebsocketClient::WebsocketClient(QObject *parent) : QObject(parent) diff --git a/src/utils/WebsocketNotifier.cpp b/src/utils/WebsocketNotifier.cpp index 39a7c450..96140d91 100644 --- a/src/utils/WebsocketNotifier.cpp +++ b/src/utils/WebsocketNotifier.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2020-2021, The Monero Project. #include "WebsocketNotifier.h" -#include "utils.h" +#include "Utils.h" #include "utils/os/tails.h" #include "utils/os/whonix.h" diff --git a/src/utils/config.cpp b/src/utils/config.cpp index 2e4baf96..7beda169 100644 --- a/src/utils/config.cpp +++ b/src/utils/config.cpp @@ -4,7 +4,7 @@ // Copyright (c) 2020-2021, The Monero Project. #include "config.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include "utils/os/tails.h" #define QS QStringLiteral diff --git a/src/utils/networking.cpp b/src/utils/networking.cpp index 9b7e578b..5885fd95 100644 --- a/src/utils/networking.cpp +++ b/src/utils/networking.cpp @@ -4,7 +4,7 @@ #include #include -#include "utils/utils.h" +#include "utils/Utils.h" #include "utils/networking.h" UtilsNetworking::UtilsNetworking(QNetworkAccessManager *networkAccessManager, QObject *parent) : diff --git a/src/utils/networking.h b/src/utils/networking.h index 9452c8a8..1799ff1c 100644 --- a/src/utils/networking.h +++ b/src/utils/networking.h @@ -9,7 +9,7 @@ #include #include -#include "utils/utils.h" +#include "utils/Utils.h" class UtilsNetworking : public QObject { diff --git a/src/utils/nodes.cpp b/src/utils/nodes.cpp index ae976fb4..98f70fd6 100644 --- a/src/utils/nodes.cpp +++ b/src/utils/nodes.cpp @@ -4,7 +4,7 @@ #include #include "nodes.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include "utils/os/tails.h" #include "appcontext.h" #include "constants.h" diff --git a/src/utils/nodes.h b/src/utils/nodes.h index ce60584c..07ff6713 100644 --- a/src/utils/nodes.h +++ b/src/utils/nodes.h @@ -12,7 +12,7 @@ #include #include "model/NodeModel.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include "utils/config.h" enum NodeSource { @@ -106,6 +106,7 @@ struct FeatherNode { } }; +class AppContext; class Nodes : public QObject { Q_OBJECT diff --git a/src/utils/os/tails.cpp b/src/utils/os/tails.cpp index 80c5b378..f65f5769 100644 --- a/src/utils/os/tails.cpp +++ b/src/utils/os/tails.cpp @@ -5,7 +5,7 @@ #include #include "tails.h" -#include "utils.h" +#include "Utils.h" bool TailsOS::detected = false; bool TailsOS::isTails = false; diff --git a/src/utils/os/whonix.cpp b/src/utils/os/whonix.cpp index 6865f2c2..b22a0c83 100644 --- a/src/utils/os/whonix.cpp +++ b/src/utils/os/whonix.cpp @@ -3,7 +3,7 @@ #include "whonix.h" -#include "utils/utils.h" +#include "utils/Utils.h" bool WhonixOS::detect() { return !QString::fromLocal8Bit(qgetenv("WHONIX")).isEmpty(); diff --git a/src/utils/prices.h b/src/utils/prices.h index 03fa06be..6e6f7f42 100644 --- a/src/utils/prices.h +++ b/src/utils/prices.h @@ -6,7 +6,7 @@ #include -#include "utils/utils.h" +#include "utils/Utils.h" struct marketStruct { QString symbol; diff --git a/src/utils/txfiathistory.cpp b/src/utils/txfiathistory.cpp index b0f9fb83..73cd4717 100644 --- a/src/utils/txfiathistory.cpp +++ b/src/utils/txfiathistory.cpp @@ -5,7 +5,7 @@ #include #include "txfiathistory.h" -#include "utils/utils.h" +#include "utils/Utils.h" TxFiatHistory::TxFiatHistory(int genesis_timestamp, const QString &configDirectory, QObject *parent) : QObject(parent) diff --git a/src/utils/utils.h b/src/utils/utils.h deleted file mode 100644 index 32e5adde..00000000 --- a/src/utils/utils.h +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2020-2021, The Monero Project. - -#ifndef FEATHER_UTILS_H -#define FEATHER_UTILS_H - -#include -#include -#include -#include - -#include - -#include "networktype.h" -#include "libwalletqt/Wallet.h" - -struct logMessage -{ - logMessage(const QtMsgType &type, const QString &message, const QString &fn){ - logMessage::type = type; - logMessage::message = message; - logMessage::fn = fn; - } - QtMsgType type; - QString message; - QString fn; -}; - -struct xdgDesktopEntryPaths { - QString pathApp; - QString pathIcon; - QString PathMime; -}; - -const xdgDesktopEntryPaths xdgPaths = { - QString("%1/feather.desktop").arg(QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)), - QString("%1/.local/share/icons/feather.png").arg(QDir::homePath()), - QString("/") -}; - -class Utils -{ - -public: - static QByteArray fileGetContents(const QString &path); - static bool portOpen(const QString &hostname, quint16 port); - static bool fileExists(const QString &path); - static QByteArray fileOpen(const QString &path); - static QByteArray fileOpenQRC(const QString &path); - static void desktopNotify(const QString &title, const QString &message, int duration); - static bool fileWrite(const QString &path, const QString &data); - static QStringList fileFind(const QRegExp &pattern, const QString &baseDir, int level, int depth, int maxPerDir); - static bool validateJSON(const QByteArray &blob); - static bool readJsonFile(QIODevice &device, QSettings::SettingsMap &map); - static bool writeJsonFile(QIODevice &device, const QSettings::SettingsMap &map); - static void applicationLogHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); - static void externalLinkWarning(QWidget *parent, const QString &url); - static bool dirExists(const QString &path); - static QString barrayToString(const QByteArray &data); - static QStandardItem *qStandardItem(const QString &text); - static QStandardItem *qStandardItem(const QString &text, QFont &font); - static void copyToClipboard(const QString &string); - static QString copyFromClipboard(); - static QString blockExplorerLink(const QString &blockExplorer, NetworkType::Type nettype, const QString &txid); - static QString getUnixAccountName(); - static QString xdgDesktopEntry(); - static bool xdgDesktopEntryWrite(const QString &path); - static void xdgRefreshApplications(); - static bool xdgDesktopEntryRegister(); - static bool pixmapWrite(const QString &path, const QPixmap &pixmap); - static QFont relativeFont(int delta); - static double roundSignificant(double N, double n); - static QString formatBytes(quint64 bytes); - static QLocale getCurrencyLocale(const QString ¤cyCode); - static QString amountToCurrencyString(double amount, const QString ¤cyCode); - static int maxLength(const QVector &array); - static QMap localeCache; - static QTextCharFormat addressTextFormat(const SubaddressIndex &index, quint64 amount); - static bool isTorsocks(); - static QString defaultWalletDir(); - static quint16 getDefaultRpcPort(NetworkType::Type type); - - template - static QString QtEnumToString (const QEnum value) - { - return QString::fromStdString(std::string(QMetaEnum::fromType().valueToKey(value))); - } -}; - -class AppContext; // forward declaration - -#endif //FEATHER_UTILS_H diff --git a/src/utils/wsclient.cpp b/src/utils/wsclient.cpp index 7ef3aa9d..d9977f3c 100644 --- a/src/utils/wsclient.cpp +++ b/src/utils/wsclient.cpp @@ -4,7 +4,7 @@ #include #include #include "wsclient.h" -#include "utils/utils.h" +#include "utils/Utils.h" WSClient::WSClient(QUrl url, QObject *parent) : QObject(parent) diff --git a/src/utils/xmrig.cpp b/src/utils/xmrig.cpp index f9f937f4..2f8b613b 100644 --- a/src/utils/xmrig.cpp +++ b/src/utils/xmrig.cpp @@ -5,7 +5,7 @@ #include #include -#include "utils/utils.h" +#include "utils/Utils.h" #include "utils/xmrig.h" #include "utils/TorManager.h" #include "appcontext.h" diff --git a/src/widgets/PayToEdit.h b/src/widgets/PayToEdit.h index 78ffd235..0b3bfa70 100644 --- a/src/widgets/PayToEdit.h +++ b/src/widgets/PayToEdit.h @@ -8,7 +8,7 @@ #include #include -#include "utils/utils.h" +#include "utils/Utils.h" struct PartialTxOutput { explicit PartialTxOutput(QString address = "", quint64 amount = 0) diff --git a/src/widgets/RedditWidget.cpp b/src/widgets/RedditWidget.cpp index cbdcc3db..6ebe60d9 100644 --- a/src/widgets/RedditWidget.cpp +++ b/src/widgets/RedditWidget.cpp @@ -9,7 +9,7 @@ #include #include "model/RedditModel.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include "utils/config.h" RedditWidget::RedditWidget(QWidget *parent) diff --git a/src/wizard/PageNetwork.cpp b/src/wizard/PageNetwork.cpp index 586f1054..ad4a16cf 100644 --- a/src/wizard/PageNetwork.cpp +++ b/src/wizard/PageNetwork.cpp @@ -7,7 +7,7 @@ #include #include "constants.h" -#include "utils.h" +#include "Utils.h" #include "WalletWizard.h" PageNetwork::PageNetwork(QWidget *parent) diff --git a/src/wizard/PageWalletFile.cpp b/src/wizard/PageWalletFile.cpp index 2a9373d6..a0098c83 100644 --- a/src/wizard/PageWalletFile.cpp +++ b/src/wizard/PageWalletFile.cpp @@ -5,7 +5,7 @@ #include "PageWalletFile.h" #include "ui_PageWalletFile.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include #include diff --git a/src/wizard/PageWalletSeed.h b/src/wizard/PageWalletSeed.h index 5ed406d3..f1abdbaf 100644 --- a/src/wizard/PageWalletSeed.h +++ b/src/wizard/PageWalletSeed.h @@ -8,7 +8,7 @@ #include #include -#include "utils/utils.h" +#include "utils/Utils.h" namespace Ui { class PageWalletSeed; diff --git a/src/wizard/WalletWizard.cpp b/src/wizard/WalletWizard.cpp index f169937a..2529ed2a 100644 --- a/src/wizard/WalletWizard.cpp +++ b/src/wizard/WalletWizard.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "utils/utils.h" +#include "utils/Utils.h" #include "WalletWizard.h" #include "PageMenu.h"