diff --git a/.dockerignore b/.dockerignore index 8a57c81..7f69149 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ * !contrib/Qt5.15_LinuxPatch.json !utils/pubkeys/* -!contrib/build-deps/verify-packages.sh \ No newline at end of file +!contrib/build-deps/verify-packages.sh +!contrib/monero-seed.patch \ No newline at end of file diff --git a/BUILDING.md b/BUILDING.md index 8959837..dc855d4 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -22,7 +22,7 @@ git clone --branch master --recursive https://git.featherwallet.org/feather/feat cd feather ``` -Replace `master` with the desired version tag (e.g. `beta-7`) to build the release binary. +Replace `master` with the desired version tag (e.g. `beta-8`) to build the release binary. #### 2. Base image @@ -63,7 +63,7 @@ git clone --branch master --recursive https://git.featherwallet.org/feather/feat cd feather ``` -Replace `master` with the desired version tag (e.g. `beta-7`) to build the release binary. +Replace `master` with the desired version tag (e.g. `beta-8`) to build the release binary. #### 2. Base image diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e52e00..cc85406 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ set(THREADS_PREFER_PTHREAD_FLAG ON) set(VERSION_MAJOR "0") set(VERSION_MINOR "1") set(VERSION_REVISION "0") -set(VERSION "beta-7") +set(VERSION "beta-8") option(STATIC "Link libraries statically, requires static Qt") @@ -17,7 +17,7 @@ option(LOCALMONERO "Include LocalMonero module" ON) option(XMRIG "Include XMRig module" ON) option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF) option(CHECK_UPDATES "Enable checking for application updates" OFF) -option(USE_DEVICE_TREZOR "Trezor support compilation" OFF) +option(USE_DEVICE_TREZOR "Trezor support compilation" ON) option(DONATE_BEG "Prompt donation window every once in a while" ON) option(WITH_SCANNER "Enable webcam QR scanner" OFF) @@ -34,7 +34,7 @@ if(DEBUG) set(CMAKE_VERBOSE_MAKEFILE ON) endif() -set(MONERO_HEAD "36fb05da3394505f8033ceb8806b28909617696f") +set(MONERO_HEAD "9c1a80a642c37f7d6ee5c14b774bbb38e6cb9c52") set(BUILD_GUI_DEPS ON) set(ARCH "x86-64") set(BUILD_64 ON) @@ -73,6 +73,8 @@ add_subdirectory(monero) set_property(TARGET wallet_merged PROPERTY FOLDER "monero") get_directory_property(ARCH_WIDTH DIRECTORY "monero" DEFINITION ARCH_WIDTH) get_directory_property(UNBOUND_LIBRARY DIRECTORY "monero" DEFINITION UNBOUND_LIBRARY) +get_directory_property(DEVICE_TREZOR_READY DIRECTORY "monero" DEFINITION DEVICE_TREZOR_READY) +get_directory_property(TREZOR_DEP_LIBS DIRECTORY "monero" DEFINITION TREZOR_DEP_LIBS) include(CMakePackageConfigHelpers) include(VersionMonero) diff --git a/Dockerfile b/Dockerfile index a162361..d77723a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -266,9 +266,11 @@ RUN git clone -b v4.1.1 --depth 1 https://github.com/fukuchi/libqrencode.git && # monero-seed: Required for Feather # Tevador's 14 word seed library +ADD contrib/monero-seed.patch . RUN git clone https://git.featherwallet.org/feather/monero-seed.git && \ cd monero-seed && \ git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \ + git apply /monero-seed.patch && \ cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \ make -Cbuild -j$THREADS && \ make -Cbuild install && \ diff --git a/Makefile b/Makefile index dc0c2a3..c17ab0e 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ CMAKEFLAGS = \ -DINSTALL_VENDORED_LIBUNBOUND=Off \ -DMANUAL_SUBMODULES=1 \ -DSTATIC=On \ - -DUSE_DEVICE_TREZOR=Off \ + -DUSE_DEVICE_TREZOR=On \ $(CMAKEFLAGS_EXTRA) release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64" diff --git a/contrib/monero-seed.patch b/contrib/monero-seed.patch new file mode 100644 index 0000000..e064997 --- /dev/null +++ b/contrib/monero-seed.patch @@ -0,0 +1,37 @@ +diff --git a/src/argon2/blake2/blake2.h b/src/argon2/blake2/blake2.h +index 9f97e1c..469e8fe 100644 +--- a/src/argon2/blake2/blake2.h ++++ b/src/argon2/blake2/blake2.h +@@ -66,6 +66,14 @@ enum { + 1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT) + }; + ++#define blake2b_init moneroseed_blake2b_init ++#define blake2b_init_key moneroseed_blake2b_init_key ++#define blake2b_init_param moneroseed_blake2b_init_param ++#define blake2b_update moneroseed_blake2b_update ++#define blake2b_final moneroseed_blake2b_final ++#define blake2b moneroseed_blake2b ++#define blake2b_long moneroseed_blake2b_long ++ + /* Streaming API */ + ARGON2_LOCAL int blake2b_init(blake2b_state *S, size_t outlen); + ARGON2_LOCAL int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, +diff --git a/src/argon2/core.h b/src/argon2/core.h +index 78000ba..e569eb4 100644 +--- a/src/argon2/core.h ++++ b/src/argon2/core.h +@@ -101,6 +101,13 @@ typedef struct Argon2_thread_data { + + /*************************Argon2 core functions********************************/ + ++#define finalize moneroseed_finalize ++#define initialize moneroseed_initialize ++#define validate_inputs moneroseed_validate_inputs ++#define fill_first_blocks moneroseed_fill_first_blocks ++#define initial_hash moneroseed_initial_hash ++#define fill_memory_blocks moneroseed_fill_memory_blocks ++ + /* Allocates memory to the given pointer, uses the appropriate allocator as + * specified in the context. Total allocated memory is num*size. + * @param context argon2_context which specifies the allocator diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56d9314..4c369d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -250,6 +250,10 @@ target_link_libraries(feather ${LIBZIP_LIBRARIES} ) +if(DEVICE_TREZOR_READY) + target_link_libraries(feather ${TREZOR_DEP_LIBS}) +endif() + if (WITH_SCANNER) target_link_libraries(feather Qt5::Multimedia diff --git a/src/calcwidget.cpp b/src/CalcWidget.cpp similarity index 98% rename from src/calcwidget.cpp rename to src/CalcWidget.cpp index 11616a2..f773716 100644 --- a/src/calcwidget.cpp +++ b/src/CalcWidget.cpp @@ -1,14 +1,15 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. +#include "CalcWidget.h" +#include "ui_CalcWidget.h" + #include -#include "calcwidget.h" -#include "ui_calcwidget.h" -#include "utils/ColorScheme.h" -#include "utils/AppData.h" -#include "utils/config.h" #include "dialog/CalcConfigDialog.h" +#include "utils/AppData.h" +#include "utils/ColorScheme.h" +#include "utils/config.h" CalcWidget::CalcWidget(QWidget *parent) : QWidget(parent) @@ -144,6 +145,4 @@ void CalcWidget::setupComboBox(QComboBox *comboBox, const QStringList &crypto, c comboBox->addItems(fiat); } -CalcWidget::~CalcWidget() { - delete ui; -} +CalcWidget::~CalcWidget() = default; \ No newline at end of file diff --git a/src/calcwidget.h b/src/CalcWidget.h similarity index 94% rename from src/calcwidget.h rename to src/CalcWidget.h index f5c4af1..b4fb58a 100644 --- a/src/calcwidget.h +++ b/src/CalcWidget.h @@ -31,7 +31,7 @@ private: void convert(bool reverse); void setupComboBox(QComboBox *comboBox, const QStringList &crypto, const QStringList &fiat); - Ui::CalcWidget *ui; + QScopedPointer ui; bool m_comboBoxInit = false; }; diff --git a/src/calcwidget.ui b/src/CalcWidget.ui similarity index 100% rename from src/calcwidget.ui rename to src/CalcWidget.ui diff --git a/src/calcwindow.cpp b/src/CalcWindow.cpp similarity index 81% rename from src/calcwindow.cpp rename to src/CalcWindow.cpp index 1b72fc3..1b55c1d 100644 --- a/src/calcwindow.cpp +++ b/src/CalcWindow.cpp @@ -1,12 +1,11 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "calcwindow.h" -#include "mainwindow.h" -#include "utils/Icons.h" -#include "utils/AppData.h" +#include "CalcWindow.h" +#include "ui_CalcWindow.h" -#include "ui_calcwindow.h" +#include "utils/AppData.h" +#include "utils/Icons.h" CalcWindow::CalcWindow(QWidget *parent) : QMainWindow(parent) @@ -23,6 +22,4 @@ void CalcWindow::closeEvent(QCloseEvent *foo) { emit closed(); } -CalcWindow::~CalcWindow() { - delete ui; -} +CalcWindow::~CalcWindow() = default; \ No newline at end of file diff --git a/src/calcwindow.h b/src/CalcWindow.h similarity index 75% rename from src/calcwindow.h rename to src/CalcWindow.h index 886a9fb..2514f43 100644 --- a/src/calcwindow.h +++ b/src/CalcWindow.h @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef CalcWindow_H -#define CalcWindow_H +#ifndef FEATHER_CALCWINDOW_H +#define FEATHER_CALCWINDOW_H #include @@ -25,7 +25,7 @@ private: void closeEvent(QCloseEvent *bar) override; private: - Ui::CalcWindow *ui; + QScopedPointer ui; }; -#endif // CalcWindow_H +#endif // FEATHER_CALCWINDOW_H diff --git a/src/calcwindow.ui b/src/CalcWindow.ui similarity index 97% rename from src/calcwindow.ui rename to src/CalcWindow.ui index 1d490cf..2447bba 100644 --- a/src/calcwindow.ui +++ b/src/CalcWindow.ui @@ -37,7 +37,7 @@ CalcWidget QWidget -
calcwidget.h
+
CalcWidget.h
1
diff --git a/src/coinswidget.cpp b/src/CoinsWidget.cpp similarity index 65% rename from src/coinswidget.cpp rename to src/CoinsWidget.cpp index 60228c1..2a97294 100644 --- a/src/coinswidget.cpp +++ b/src/CoinsWidget.cpp @@ -1,22 +1,21 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "coinswidget.h" -#include "ui_coinswidget.h" -#include "dialog/outputinfodialog.h" -#include "dialog/outputsweepdialog.h" -#include "mainwindow.h" -#include "utils/Icons.h" +#include "CoinsWidget.h" +#include "ui_CoinsWidget.h" -#include #include +#include "dialog/OutputInfoDialog.h" +#include "dialog/OutputSweepDialog.h" +#include "utils/Icons.h" + CoinsWidget::CoinsWidget(QSharedPointer ctx, QWidget *parent) - : QWidget(parent) - , ui(new Ui::CoinsWidget) - , m_ctx(std::move(ctx)) - , m_headerMenu(new QMenu(this)) - , m_copyMenu(new QMenu("Copy",this)) + : QWidget(parent) + , ui(new Ui::CoinsWidget) + , m_ctx(std::move(ctx)) + , m_headerMenu(new QMenu(this)) + , m_copyMenu(new QMenu("Copy",this)) { ui->setupUi(this); @@ -39,6 +38,9 @@ CoinsWidget::CoinsWidget(QSharedPointer ctx, QWidget *parent) // context menu ui->coins->setContextMenuPolicy(Qt::CustomContextMenu); + m_editLabelAction = new QAction("Edit Label", this); + connect(m_editLabelAction, &QAction::triggered, this, &CoinsWidget::editLabel); + m_thawOutputAction = new QAction("Thaw output", this); m_freezeOutputAction = new QAction("Freeze output", this); @@ -47,16 +49,26 @@ CoinsWidget::CoinsWidget(QSharedPointer ctx, QWidget *parent) m_viewOutputAction = new QAction(icons()->icon("info2.svg"), "Details", this); m_sweepOutputAction = new QAction("Sweep output", this); + m_sweepOutputsAction = new QAction("Sweep selected outputs", this); + connect(m_freezeOutputAction, &QAction::triggered, this, &CoinsWidget::freezeOutput); connect(m_thawOutputAction, &QAction::triggered, this, &CoinsWidget::thawOutput); connect(m_viewOutputAction, &QAction::triggered, this, &CoinsWidget::viewOutput); - connect(m_sweepOutputAction, &QAction::triggered, this, &CoinsWidget::onSweepOutput); + connect(m_sweepOutputAction, &QAction::triggered, this, &CoinsWidget::onSweepOutputs); + connect(m_sweepOutputsAction, &QAction::triggered, this, &CoinsWidget::onSweepOutputs); connect(m_freezeAllSelectedAction, &QAction::triggered, this, &CoinsWidget::freezeAllSelected); connect(m_thawAllSelectedAction, &QAction::triggered, this, &CoinsWidget::thawAllSelected); connect(ui->coins, &QTreeView::customContextMenuRequested, this, &CoinsWidget::showContextMenu); - connect(ui->coins, &QTreeView::doubleClicked, this, &CoinsWidget::viewOutput); + connect(ui->coins, &QTreeView::doubleClicked, [this](QModelIndex index){ + if (!m_model) return; + if (!(m_model->flags(index) & Qt::ItemIsEditable)) { + this->viewOutput(); + } + }); + + connect(ui->search, &QLineEdit::textChanged, this, &CoinsWidget::setSearchFilter); } void CoinsWidget::setModel(CoinsModel * model, Coins * coins) { @@ -76,11 +88,20 @@ void CoinsWidget::setModel(CoinsModel * model, Coins * coins) { } ui->coins->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui->coins->header()->setSectionResizeMode(CoinsModel::AddressLabel, QHeaderView::Stretch); + ui->coins->header()->setSectionResizeMode(CoinsModel::Label, QHeaderView::Stretch); ui->coins->header()->setSortIndicator(CoinsModel::BlockHeight, Qt::DescendingOrder); ui->coins->setSortingEnabled(true); } +void CoinsWidget::setSearchbarVisible(bool visible) { + ui->search->setVisible(visible); +} + +void CoinsWidget::focusSearchbar() { + ui->search->setFocusPolicy(Qt::StrongFocus); + ui->search->setFocus(); +} + void CoinsWidget::showContextMenu(const QPoint &point) { QModelIndexList list = ui->coins->selectionModel()->selectedRows(); @@ -88,6 +109,7 @@ void CoinsWidget::showContextMenu(const QPoint &point) { if (list.size() > 1) { menu->addAction(m_freezeAllSelectedAction); menu->addAction(m_thawAllSelectedAction); + menu->addAction(m_sweepOutputsAction); } else { CoinsInfo* c = this->currentEntry(); @@ -98,6 +120,7 @@ void CoinsWidget::showContextMenu(const QPoint &point) { bool isUnlocked = c->unlocked(); menu->addMenu(m_copyMenu); + menu->addAction(m_editLabelAction); if (!isSpent) { isFrozen ? menu->addAction(m_thawOutputAction) : menu->addAction(m_freezeOutputAction); @@ -127,6 +150,11 @@ void CoinsWidget::setShowSpent(bool show) m_proxyModel->setShowSpent(show); } +void CoinsWidget::setSearchFilter(const QString &filter) { + if (!m_proxyModel) return; + m_proxyModel->setSearchFilter(filter); +} + void CoinsWidget::freezeOutput() { QModelIndex index = ui->coins->currentIndex(); QVector indexes = {m_proxyModel->mapToSource(index).row()}; @@ -167,23 +195,46 @@ void CoinsWidget::viewOutput() { dialog->show(); } -void CoinsWidget::onSweepOutput() { - CoinsInfo* c = this->currentEntry(); - if (!c) return; +void CoinsWidget::onSweepOutputs() { + QVector selectedCoins = this->currentEntries(); + QVector keyImages; - QString keyImage = c->keyImage(); + quint64 totalAmount = 0; + for (const auto coin : selectedCoins) { + if (!coin) return; - if (!c->keyImageKnown()) { - QMessageBox::warning(this, "Unable to sweep output", "Unable to sweep output: key image unknown"); - return; + QString keyImage = coin->keyImage(); + if (!coin->keyImageKnown()) { + QMessageBox::warning(this, "Unable to sweep outputs", "Unable to create transaction: selected output has unknown key image"); + return; + } + + if (coin->spent()) { + QMessageBox::warning(this, "Unable to sweep outputs", "Unable to create transaction: selected output was already spent"); + return; + } + + if (coin->frozen()) { + QMessageBox::warning(this, "Unable to sweep outputs", "Unable to create transaction: selected output is frozen.\n\n" + "Thaw the selected output(s) before spending."); + return; + } + + if (!coin->unlocked()) { + QMessageBox::warning(this, "Unable to sweep outputs", "Unable to create transaction: selected output is locked.\n\n" + "Wait until the output has reached the required number of confirmation before spending."); + return; + } + + keyImages.push_back(keyImage); + totalAmount += coin->amount(); } - auto *dialog = new OutputSweepDialog(this, c); - int ret = dialog->exec(); + OutputSweepDialog dialog{this, totalAmount}; + int ret = dialog.exec(); if (!ret) return; - m_ctx->onSweepOutput(keyImage, dialog->address(), dialog->churn(), dialog->outputs()); - dialog->deleteLater(); + m_ctx->onSweepOutputs(keyImages, dialog.address(), dialog.churn(), dialog.outputs()); } void CoinsWidget::copy(copyField field) { @@ -204,9 +255,13 @@ void CoinsWidget::copy(copyField field) { case Address: data = c->address(); break; - case Label: - data = c->addressLabel(); + case Label: { + if (!c->description().isEmpty()) + data = c->description(); + else + data = c->addressLabel(); break; + } case Height: data = QString::number(c->blockHeight()); break; @@ -227,6 +282,15 @@ CoinsInfo* CoinsWidget::currentEntry() { } } +QVector CoinsWidget::currentEntries() { + QModelIndexList list = ui->coins->selectionModel()->selectedRows(); + QVector selectedCoins; + for (const auto index : list) { + selectedCoins.push_back(m_model->entryFromIndex(m_proxyModel->mapToSource(index))); + } + return selectedCoins; +} + void CoinsWidget::freezeCoins(const QVector& indexes) { for (int i : indexes) { m_ctx->wallet->coins()->freeze(i); @@ -243,6 +307,10 @@ void CoinsWidget::thawCoins(const QVector &indexes) { m_ctx->updateBalance(); } -CoinsWidget::~CoinsWidget() { - delete ui; +void CoinsWidget::editLabel() { + QModelIndex index = ui->coins->currentIndex().siblingAtColumn(m_model->ModelColumn::Label); + ui->coins->setCurrentIndex(index); + ui->coins->edit(index); } + +CoinsWidget::~CoinsWidget() = default; \ No newline at end of file diff --git a/src/coinswidget.h b/src/CoinsWidget.h similarity index 83% rename from src/coinswidget.h rename to src/CoinsWidget.h index 91ffba9..90cbb53 100644 --- a/src/coinswidget.h +++ b/src/CoinsWidget.h @@ -4,15 +4,15 @@ #ifndef FEATHER_COINSWIDGET_H #define FEATHER_COINSWIDGET_H +#include +#include +#include + #include "appcontext.h" #include "model/CoinsModel.h" #include "model/CoinsProxyModel.h" #include "libwalletqt/Coins.h" -#include -#include -#include - namespace Ui { class CoinsWidget; } @@ -26,6 +26,10 @@ public: void setModel(CoinsModel * model, Coins * coins); ~CoinsWidget() override; +public slots: + void setSearchbarVisible(bool visible); + void focusSearchbar(); + private slots: void showHeaderMenu(const QPoint& position); void setShowSpent(bool show); @@ -34,7 +38,9 @@ private slots: void thawOutput(); void thawAllSelected(); void viewOutput(); - void onSweepOutput(); + void onSweepOutputs(); + void setSearchFilter(const QString &filter); + void editLabel(); private: void freezeCoins(const QVector& indexes); @@ -50,7 +56,7 @@ private: Amount }; - Ui::CoinsWidget *ui; + QScopedPointer ui; QSharedPointer m_ctx; QMenu *m_contextMenu; @@ -63,6 +69,8 @@ private: QAction *m_thawAllSelectedAction; QAction *m_viewOutputAction; QAction *m_sweepOutputAction; + QAction *m_sweepOutputsAction; + QAction *m_editLabelAction; Coins *m_coins; CoinsModel * m_model; CoinsProxyModel * m_proxyModel; @@ -70,6 +78,7 @@ private: void showContextMenu(const QPoint & point); void copy(copyField field); CoinsInfo* currentEntry(); + QVector currentEntries(); }; diff --git a/src/coinswidget.ui b/src/CoinsWidget.ui similarity index 87% rename from src/coinswidget.ui rename to src/CoinsWidget.ui index cddca31..eaf7a76 100644 --- a/src/coinswidget.ui +++ b/src/CoinsWidget.ui @@ -29,6 +29,13 @@ 0 + + + + Search.. + + + diff --git a/src/contactswidget.cpp b/src/ContactsWidget.cpp similarity index 95% rename from src/contactswidget.cpp rename to src/ContactsWidget.cpp index 62b0908..a4616e0 100644 --- a/src/contactswidget.cpp +++ b/src/ContactsWidget.cpp @@ -1,20 +1,20 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "contactswidget.h" -#include "ui_contactswidget.h" -#include "dialog/contactsdialog.h" -#include "model/ModelUtils.h" -#include "mainwindow.h" -#include "libwalletqt/AddressBook.h" -#include "utils/Icons.h" +#include "ContactsWidget.h" +#include "ui_ContactsWidget.h" #include +#include "dialog/ContactsDialog.h" +#include "libwalletqt/AddressBook.h" +#include "model/ModelUtils.h" +#include "utils/Icons.h" + ContactsWidget::ContactsWidget(QSharedPointer ctx, QWidget *parent) - : QWidget(parent) - , ui(new Ui::ContactsWidget) - , m_ctx(std::move(ctx)) + : QWidget(parent) + , ui(new Ui::ContactsWidget) + , m_ctx(std::move(ctx)) { ui->setupUi(this); @@ -150,7 +150,4 @@ void ContactsWidget::deleteContact() m_model->deleteRow(m_proxyModel->mapToSource(index).row()); } -ContactsWidget::~ContactsWidget() -{ - delete ui; -} +ContactsWidget::~ContactsWidget() = default; diff --git a/src/contactswidget.h b/src/ContactsWidget.h similarity index 88% rename from src/contactswidget.h rename to src/ContactsWidget.h index 5c2a819..e800a0e 100644 --- a/src/contactswidget.h +++ b/src/ContactsWidget.h @@ -1,16 +1,16 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef CONTACTSWIDGET_H -#define CONTACTSWIDGET_H - -#include "model/AddressBookModel.h" -#include "model/AddressBookProxyModel.h" -#include "appcontext.h" +#ifndef FEATHER_CONTACTSWIDGET_H +#define FEATHER_CONTACTSWIDGET_H #include #include +#include "appcontext.h" +#include "model/AddressBookModel.h" +#include "model/AddressBookProxyModel.h" + namespace Ui { class ContactsWidget; } @@ -42,7 +42,7 @@ private slots: void showHeaderMenu(const QPoint &position); private: - Ui::ContactsWidget *ui; + QScopedPointer ui; QSharedPointer m_ctx; QAction *m_showFullAddressesAction; @@ -53,4 +53,4 @@ private: AddressBookProxyModel * m_proxyModel; }; -#endif // CONTACTSWIDGET_H +#endif // FEATHER_CONTACTSWIDGET_H diff --git a/src/contactswidget.ui b/src/ContactsWidget.ui similarity index 100% rename from src/contactswidget.ui rename to src/ContactsWidget.ui diff --git a/src/historywidget.cpp b/src/HistoryWidget.cpp similarity index 90% rename from src/historywidget.cpp rename to src/HistoryWidget.cpp index a0e8602..e31390d 100644 --- a/src/historywidget.cpp +++ b/src/HistoryWidget.cpp @@ -1,23 +1,24 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "historywidget.h" -#include "ui_historywidget.h" -#include "dialog/transactioninfodialog.h" -#include "dialog/TxProofDialog.h" -#include "utils/Icons.h" -#include "utils/config.h" -#include "appcontext.h" +#include "HistoryWidget.h" +#include "ui_HistoryWidget.h" #include +#include "appcontext.h" +#include "dialog/TxInfoDialog.h" +#include "dialog/TxProofDialog.h" +#include "utils/config.h" +#include "utils/Icons.h" + HistoryWidget::HistoryWidget(QSharedPointer ctx, QWidget *parent) - : QWidget(parent) - , ui(new Ui::HistoryWidget) - , m_ctx(std::move(ctx)) - , m_contextMenu(new QMenu(this)) - , m_copyMenu(new QMenu("Copy", this)) - , m_model(m_ctx->wallet->historyModel()) + : QWidget(parent) + , ui(new Ui::HistoryWidget) + , m_ctx(std::move(ctx)) + , m_contextMenu(new QMenu(this)) + , m_copyMenu(new QMenu("Copy", this)) + , m_model(m_ctx->wallet->historyModel()) { ui->setupUi(this); m_contextMenu->addMenu(m_copyMenu); @@ -82,7 +83,7 @@ void HistoryWidget::showContextMenu(const QPoint &point) { if (!tx) return; bool unconfirmed = tx->isFailed() || tx->isPending(); - if (m_ctx->txCache.contains(tx->hash()) && unconfirmed && tx->direction() != TransactionInfo::Direction_In) { + if (unconfirmed && tx->direction() != TransactionInfo::Direction_In) { menu.addAction(icons()->icon("info2.svg"), "Resend transaction", this, &HistoryWidget::onResendTransaction); } @@ -115,8 +116,8 @@ void HistoryWidget::showTxDetails() { auto *tx = ui->history->currentEntry(); if (!tx) return; - auto *dialog = new TransactionInfoDialog(m_ctx, tx, this); - connect(dialog, &TransactionInfoDialog::resendTranscation, [this](const QString &txid){ + auto *dialog = new TxInfoDialog(m_ctx, tx, this); + connect(dialog, &TxInfoDialog::resendTranscation, [this](const QString &txid){ emit resendTransaction(txid); }); dialog->show(); @@ -181,6 +182,4 @@ void HistoryWidget::showSyncNoticeMsg() { "To update the history page during synchronization press Ctrl+R."); } -HistoryWidget::~HistoryWidget() { - delete ui; -} +HistoryWidget::~HistoryWidget() = default; \ No newline at end of file diff --git a/src/historywidget.h b/src/HistoryWidget.h similarity index 97% rename from src/historywidget.h rename to src/HistoryWidget.h index a41a262..3a414dd 100644 --- a/src/historywidget.h +++ b/src/HistoryWidget.h @@ -4,15 +4,15 @@ #ifndef FEATHER_HISTORYWIDGET_H #define FEATHER_HISTORYWIDGET_H -#include "model/TransactionHistoryModel.h" -#include "model/TransactionHistoryProxyModel.h" -#include "libwalletqt/Coins.h" -#include "libwalletqt/Wallet.h" -#include "appcontext.h" - #include #include +#include "appcontext.h" +#include "libwalletqt/Coins.h" +#include "libwalletqt/Wallet.h" +#include "model/TransactionHistoryModel.h" +#include "model/TransactionHistoryProxyModel.h" + namespace Ui { class HistoryWidget; } @@ -56,7 +56,7 @@ private: void showContextMenu(const QPoint &point); void showSyncNoticeMsg(); - Ui::HistoryWidget *ui; + QScopedPointer ui; QSharedPointer m_ctx; QMenu *m_contextMenu; QMenu *m_copyMenu; diff --git a/src/historywidget.ui b/src/HistoryWidget.ui similarity index 100% rename from src/historywidget.ui rename to src/HistoryWidget.ui diff --git a/src/mainwindow.cpp b/src/MainWindow.cpp similarity index 87% rename from src/mainwindow.cpp rename to src/MainWindow.cpp index b869d89..e1c7b96 100644 --- a/src/mainwindow.cpp +++ b/src/MainWindow.cpp @@ -1,38 +1,40 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include -#include +#include "MainWindow.h" +#include "ui_MainWindow.h" -#include "mainwindow.h" -#include "ui_mainwindow.h" +#include +#include +#include #include "config-feather.h" -#include "dialog/txconfdialog.h" -#include "dialog/txconfadvdialog.h" -#include "dialog/debuginfodialog.h" -#include "dialog/walletinfodialog.h" -#include "dialog/torinfodialog.h" -#include "dialog/viewonlydialog.h" -#include "dialog/broadcasttxdialog.h" -#include "dialog/tximportdialog.h" -#include "dialog/passworddialog.h" -#include "dialog/balancedialog.h" +#include "constants.h" +#include "dialog/AccountSwitcherDialog.h" +#include "dialog/BalanceDialog.h" +#include "dialog/DebugInfoDialog.h" +#include "dialog/PasswordDialog.h" +#include "dialog/TorInfoDialog.h" +#include "dialog/TxBroadcastDialog.h" +#include "dialog/TxConfAdvDialog.h" +#include "dialog/TxConfDialog.h" +#include "dialog/TxImportDialog.h" +#include "dialog/TxInfoDialog.h" +#include "dialog/ViewOnlyDialog.h" +#include "dialog/WalletInfoDialog.h" #include "dialog/WalletCacheDebugDialog.h" #include "dialog/UpdateDialog.h" -#include "dialog/AccountSwitcherDialog.h" -#include "constants.h" #include "libwalletqt/AddressBook.h" -#include "utils/AsyncTask.h" #include "utils/AppData.h" +#include "utils/AsyncTask.h" #include "utils/ColorScheme.h" -#include "utils/SemanticVersion.h" -#include "utils/NetworkManager.h" #include "utils/Icons.h" -#include "utils/WebsocketNotifier.h" -#include "utils/Updater.h" +#include "utils/NetworkManager.h" #include "utils/os/tails.h" +#include "utils/SemanticVersion.h" #include "utils/TorManager.h" +#include "utils/Updater.h" +#include "utils/WebsocketNotifier.h" MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *parent) : QMainWindow(parent) @@ -151,7 +153,7 @@ void MainWindow::initStatusBar() { connect(m_statusBtnTor, &StatusBarButton::clicked, this, &MainWindow::menuTorClicked); this->statusBar()->addPermanentWidget(m_statusBtnTor); - m_statusBtnHwDevice = new StatusBarButton(icons()->icon("ledger.png"), "Ledger", this); + m_statusBtnHwDevice = new StatusBarButton(this->hardwareDevicePairedIcon(), this->getHardwareDevice(), this); connect(m_statusBtnHwDevice, &StatusBarButton::clicked, this, &MainWindow::menuHwDeviceClicked); this->statusBar()->addPermanentWidget(m_statusBtnHwDevice); m_statusBtnHwDevice->hide(); @@ -206,6 +208,10 @@ void MainWindow::initWidgets() { #else ui->tabWidget->setTabVisible(Tabs::XMRIG, false); #endif + +#if defined(Q_OS_MACOS) + ui->line->hide(); +#endif } void MainWindow::initMenu() { @@ -217,6 +223,10 @@ void MainWindow::initMenu() { connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::menuQuitClicked); // Quit application connect(ui->actionSettings, &QAction::triggered, this, &MainWindow::menuSettingsClicked); + // [File] -> [Recently open] + m_clearRecentlyOpenAction = new QAction("Clear history", ui->menuFile); + connect(m_clearRecentlyOpenAction, &QAction::triggered, this, &MainWindow::menuClearHistoryClicked); + // [Wallet] connect(ui->actionInformation, &QAction::triggered, this, &MainWindow::showWalletInfoDialog); connect(ui->actionAccount, &QAction::triggered, this, &MainWindow::showAccountSwitcherDialog); @@ -304,6 +314,7 @@ void MainWindow::initMenu() { connect(ui->actionLoadSignedTxFromText, &QAction::triggered, this, &MainWindow::loadSignedTxFromText); connect(ui->actionImport_transaction, &QAction::triggered, this, &MainWindow::importTransaction); connect(ui->actionPay_to_many, &QAction::triggered, this, &MainWindow::payToMany); + connect(ui->actionAddress_checker, &QAction::triggered, this, &MainWindow::showAddressChecker); connect(ui->actionCalculator, &QAction::triggered, this, &MainWindow::showCalcWindow); connect(ui->actionCreateDesktopEntry, &QAction::triggered, this, &MainWindow::onCreateDesktopEntry); @@ -356,6 +367,8 @@ void MainWindow::initWalletContext() { connect(m_ctx.get(), &AppContext::createTransactionSuccess, this, &MainWindow::onCreateTransactionSuccess); connect(m_ctx.get(), &AppContext::transactionCommitted, this, &MainWindow::onTransactionCommitted); connect(m_ctx.get(), &AppContext::deviceError, this, &MainWindow::onDeviceError); + connect(m_ctx.get(), &AppContext::deviceButtonRequest, this, &MainWindow::onDeviceButtonRequest); + connect(m_ctx.get(), &AppContext::deviceButtonPressed, this, &MainWindow::onDeviceButtonPressed); connect(m_ctx.get(), &AppContext::initiateTransaction, this, &MainWindow::onInitiateTransaction); connect(m_ctx.get(), &AppContext::endTransaction, this, &MainWindow::onEndTransaction); connect(m_ctx.get(), &AppContext::customRestoreHeightSet, this, &MainWindow::onCustomRestoreHeightSet); @@ -366,8 +379,9 @@ void MainWindow::initWalletContext() { connect(m_ctx->nodes, &Nodes::WSNodeExhausted, this, &MainWindow::showWSNodeExhaustedMessage); // Wallet - connect(m_ctx->wallet.get(), &Wallet::connectionStatusChanged, this, &MainWindow::onConnectionStatusChanged); - connect(m_ctx->wallet.get(), &Wallet::currentSubaddressAccountChanged, this, &MainWindow::updateTitle); + connect(m_ctx->wallet, &Wallet::connectionStatusChanged, this, &MainWindow::onConnectionStatusChanged); + connect(m_ctx->wallet, &Wallet::currentSubaddressAccountChanged, this, &MainWindow::updateTitle); + connect(m_ctx->wallet, &Wallet::walletPassphraseNeeded, this, &MainWindow::onWalletPassphraseNeeded); } void MainWindow::menuToggleTabVisible(const QString &key){ @@ -379,6 +393,11 @@ void MainWindow::menuToggleTabVisible(const QString &key){ toggleTab->menuAction->setText((show ? QString("Hide ") : QString("Show ")) + toggleTab->name); } +void MainWindow::menuClearHistoryClicked() { + config()->remove(Config::recentlyOpenedWallets); + this->updateRecentlyOpenedMenu(); +} + QString MainWindow::walletName() { return QFileInfo(m_ctx->wallet->cachePath()).fileName(); } @@ -426,9 +445,7 @@ void MainWindow::onWalletOpened() { this->bringToFront(); this->setEnabled(true); if (!torManager()->torConnected) - this->setStatusText("Wallet opened - Starting Tor (may take a while)"); - else - this->setStatusText("Wallet opened - Searching for node"); + this->setStatusText("Starting Tor (may take a while)"); // receive page m_ctx->wallet->subaddress()->refresh(m_ctx->wallet->currentSubaddressAccount()); @@ -445,13 +462,23 @@ void MainWindow::onWalletOpened() { // coins page m_ctx->wallet->coins()->refresh(m_ctx->wallet->currentSubaddressAccount()); m_coinsWidget->setModel(m_ctx->wallet->coinsModel(), m_ctx->wallet->coins()); + m_ctx->wallet->coinsModel()->setCurrentSubaddressAccount(m_ctx->wallet->currentSubaddressAccount()); + + // Coin labeling uses set_tx_note, so we need to refresh history too + connect(m_ctx->wallet->coins(), &Coins::descriptionChanged, [this] { + m_ctx->wallet->history()->refresh(m_ctx->wallet->currentSubaddressAccount()); + }); + // Vice versa + connect(m_ctx->wallet->history(), &TransactionHistory::txNoteChanged, [this] { + m_ctx->wallet->coins()->refresh(m_ctx->wallet->currentSubaddressAccount()); + }); this->updatePasswordIcon(); this->updateTitle(); m_ctx->nodes->connectToNode(); m_updateBytes.start(250); - this->updateRecentlyOpened(m_ctx->wallet->cachePath()); + this->addToRecentlyOpened(m_ctx->wallet->cachePath()); } void MainWindow::onBalanceUpdated(quint64 balance, quint64 spendable) { @@ -619,8 +646,22 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QVecto void MainWindow::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid) { if (status) { // success + QMessageBox msgBox{this}; + QPushButton *showDetailsButton = msgBox.addButton("Show details", QMessageBox::ActionRole); + msgBox.addButton(QMessageBox::Ok); QString body = QString("Successfully sent %1 transaction(s).").arg(txid.count()); - QMessageBox::information(this, "Transactions sent", body); + msgBox.setText(body); + msgBox.setWindowTitle("Transaction sent"); + msgBox.setIcon(QMessageBox::Icon::Information); + msgBox.exec(); + if (msgBox.clickedButton() == showDetailsButton) { + this->showHistoryTab(); + TransactionInfo *txInfo = m_ctx->wallet->history()->transaction(txid.first()); + TxInfoDialog dialog{m_ctx, txInfo, this}; + connect(&dialog, &TxInfoDialog::resendTranscation, this, &MainWindow::onResendTransaction); + dialog.exec(); + } + m_sendWidget->clearFields(); } else { auto err = tx->errorString(); @@ -704,7 +745,7 @@ void MainWindow::showConnectionStatusDialog() { } void MainWindow::showPasswordDialog() { - PasswordChangeDialog dialog{this, m_ctx->wallet.get()}; + PasswordChangeDialog dialog{this, m_ctx->wallet}; dialog.exec(); this->updatePasswordIcon(); } @@ -780,12 +821,12 @@ void MainWindow::menuSettingsClicked() { } void MainWindow::menuSignVerifyClicked() { - SignVerifyDialog dialog{m_ctx->wallet.get(), this}; + SignVerifyDialog dialog{m_ctx->wallet, this}; dialog.exec(); } void MainWindow::menuVerifyTxProof() { - VerifyProofDialog dialog{m_ctx->wallet.get(), this}; + VerifyProofDialog dialog{m_ctx->wallet, this}; dialog.exec(); } @@ -801,6 +842,26 @@ void MainWindow::updateWidgetIcons() { m_localMoneroWidget->skinChanged(); #endif ui->conversionWidget->skinChanged(); + + m_statusBtnHwDevice->setIcon(this->hardwareDevicePairedIcon()); +} + +QIcon MainWindow::hardwareDevicePairedIcon() { + QString filename; + if (m_ctx->wallet->isLedger()) + filename = "ledger.png"; + else if (m_ctx->wallet->isTrezor()) + filename = ColorScheme::darkScheme ? "trezor_white.png" : "trezor.png"; + return icons()->icon(filename); +} + +QIcon MainWindow::hardwareDeviceUnpairedIcon() { + QString filename; + if (m_ctx->wallet->isLedger()) + filename = "ledger_unpaired.png"; + else if (m_ctx->wallet->isTrezor()) + filename = ColorScheme::darkScheme ? "trezor_unpaired_white.png" : "trezor_unpaired.png"; + return icons()->icon(filename); } void MainWindow::closeEvent(QCloseEvent *event) { @@ -853,8 +914,8 @@ void MainWindow::payToMany() { "A maximum of 16 addresses may be specified."); } -void MainWindow::showSendScreen(const CCSEntry &entry) { - m_sendWidget->fill(entry); +void MainWindow::showSendScreen(const CCSEntry &entry) { // TODO: rename this function + m_sendWidget->fill(entry.address, QString("CCS: %1").arg(entry.title)); ui->tabWidget->setCurrentIndex(Tabs::SEND); } @@ -864,7 +925,8 @@ void MainWindow::onViewOnBlockExplorer(const QString &txid) { } void MainWindow::onResendTransaction(const QString &txid) { - if (!m_ctx->txCache.contains(txid)) { + QString txHex = m_ctx->getCacheTransaction(txid); + if (txHex.isEmpty()) { QMessageBox::warning(this, "Unable to resend transaction", "Transaction was not found in transaction cache. Unable to resend."); return; } @@ -872,7 +934,7 @@ void MainWindow::onResendTransaction(const QString &txid) { // Connect to a different node so chances of successful relay are higher m_ctx->nodes->autoConnect(true); - BroadcastTxDialog dialog{this, m_ctx, m_ctx->txCache[txid]}; + TxBroadcastDialog dialog{this, m_ctx, txHex}; dialog.exec(); } @@ -921,6 +983,27 @@ void MainWindow::showAccountSwitcherDialog() { dialog.exec(); } +void MainWindow::showAddressChecker() { + QString address = QInputDialog::getText(this, "Address Checker", "Address: "); + if (address.isEmpty()) { + return; + } + + if (!WalletManager::addressValid(address, constants::networkType)) { + QMessageBox::warning(this, "Address Checker", "Invalid address."); + return; + } + + SubaddressIndex index = m_ctx->wallet->subaddressIndex(address); + if (!index.isValid()) { + // TODO: probably mention lookahead here + QMessageBox::warning(this, "Address Checker", "This address does not belong to this wallet."); + return; + } else { + QMessageBox::information(this, "Address Checker", QString("This address belongs to Account #%1").arg(index.major)); + } +} + void MainWindow::showNodeExhaustedMessage() { // Spawning dialogs inside a lambda can cause system freezes on linux so we have to do it this way ¯\_(ツ)_/¯ @@ -1034,7 +1117,7 @@ void MainWindow::loadSignedTx() { } void MainWindow::loadSignedTxFromText() { - BroadcastTxDialog dialog{this, m_ctx}; + TxBroadcastDialog dialog{this, m_ctx}; dialog.exec(); } @@ -1065,7 +1148,8 @@ void MainWindow::onDeviceError(const QString &error) { if (m_showDeviceError) { return; } - m_statusBtnHwDevice->setIcon(icons()->icon("ledger_unpaired.png")); + + m_statusBtnHwDevice->setIcon(this->hardwareDeviceUnpairedIcon()); while (true) { m_showDeviceError = true; auto result = QMessageBox::question(this, "Hardware device", "Lost connection to hardware device. Attempt to reconnect?"); @@ -1080,27 +1164,70 @@ void MainWindow::onDeviceError(const QString &error) { return; } } - m_statusBtnHwDevice->setIcon(icons()->icon("ledger.png")); + m_statusBtnHwDevice->setIcon(this->hardwareDevicePairedIcon()); m_ctx->wallet->startRefresh(); m_showDeviceError = false; } +void MainWindow::onDeviceButtonRequest(quint64 code) { + if (m_ctx->wallet->isTrezor()) { + switch (code) { + case 8: + { + // Annoyingly, this code is used for a variety of actions, including: + // Confirm refresh: Do you really want to start refresh? + // Confirm export: Do you really want to export tx_key? + + if (m_constructingTransaction) { // This code is also used when signing a tx, we handle this elsewhere + break; + } + + m_splashDialog->setMessage("Confirm action on device to proceed"); + m_splashDialog->setIcon(QPixmap(":/assets/images/confirmed.png")); + m_splashDialog->show(); + m_splashDialog->setEnabled(true); + break; + } + } + } +} + +void MainWindow::onDeviceButtonPressed() { + if (m_constructingTransaction) { + return; + } + + m_splashDialog->hide(); +} + +void MainWindow::onWalletPassphraseNeeded(bool on_device) { + auto button = QMessageBox::question(nullptr, "Wallet Passphrase Needed", "Enter passphrase on hardware wallet?\n\n" + "It is recommended to enter passphrase on " + "the hardware wallet for better security.", + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (button == QMessageBox::Yes) { + m_ctx->wallet->onPassphraseEntered("", true, false); + return; + } + + bool ok; + QString passphrase = QInputDialog::getText(nullptr, "Wallet Passphrase Needed", "Enter passphrase:", QLineEdit::EchoMode::Password, "", &ok); + if (ok) { + m_ctx->wallet->onPassphraseEntered(passphrase, false, false); + } else { + m_ctx->wallet->onPassphraseEntered(passphrase, false, true); + } +} + void MainWindow::updateNetStats() { - if (m_ctx->wallet == nullptr) { - m_statusLabelNetStats->setText(""); - return; - } - if (m_ctx->wallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected) { - m_statusLabelNetStats->setText(""); + if (!m_ctx->wallet || m_ctx->wallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected + || m_ctx->wallet->connectionStatus() == Wallet::ConnectionStatus_Synchronized) + { + m_statusLabelNetStats->hide(); return; } - if (m_ctx->wallet->connectionStatus() == Wallet::ConnectionStatus_Synchronized) { - m_statusLabelNetStats->setText(""); - return; - } - - + m_statusLabelNetStats->show(); m_statusLabelNetStats->setText(QString("(D: %1)").arg(Utils::formatBytes(m_ctx->wallet->getBytesReceived()))); } @@ -1113,7 +1240,7 @@ void MainWindow::rescanSpent() { } void MainWindow::showBalanceDialog() { - BalanceDialog dialog{this, m_ctx->wallet.get()}; + BalanceDialog dialog{this, m_ctx->wallet}; dialog.exec(); } @@ -1379,7 +1506,7 @@ void MainWindow::donationNag() { config()->set(Config::donateBeg, donationCounter); } -void MainWindow::updateRecentlyOpened(const QString &keysFile) { +void MainWindow::addToRecentlyOpened(const QString &keysFile) { auto recent = config()->get(Config::recentlyOpenedWallets).toList(); if (recent.contains(keysFile)) { @@ -1400,12 +1527,19 @@ void MainWindow::updateRecentlyOpened(const QString &keysFile) { } config()->set(Config::recentlyOpenedWallets, recent_); + + this->updateRecentlyOpenedMenu(); +} + +void MainWindow::updateRecentlyOpenedMenu() { ui->menuRecently_open->clear(); - for (const auto &var : recent_) { - QString path = var.toString(); - QFileInfo fileInfo{path}; - ui->menuRecently_open->addAction(fileInfo.fileName(), m_windowManager, std::bind(&WindowManager::tryOpenWallet, m_windowManager, path, "")); + const QStringList recentWallets = config()->get(Config::recentlyOpenedWallets).toStringList(); + for (const auto &walletPath : recentWallets) { + QFileInfo fileInfo{walletPath}; + ui->menuRecently_open->addAction(fileInfo.fileName(), m_windowManager, std::bind(&WindowManager::tryOpenWallet, m_windowManager, walletPath, "")); } + ui->menuRecently_open->addSeparator(); + ui->menuRecently_open->addAction(m_clearRecentlyOpenAction); } void MainWindow::toggleSearchbar(bool visible) { @@ -1414,6 +1548,7 @@ void MainWindow::toggleSearchbar(bool visible) { m_historyWidget->setSearchbarVisible(visible); m_receiveWidget->setSearchbarVisible(visible); m_contactsWidget->setSearchbarVisible(visible); + m_coinsWidget->setSearchbarVisible(visible); int currentTab = ui->tabWidget->currentIndex(); if (currentTab == Tabs::HISTORY) @@ -1422,8 +1557,8 @@ void MainWindow::toggleSearchbar(bool visible) { m_contactsWidget->focusSearchbar(); else if (currentTab == Tabs::RECEIVE) m_receiveWidget->focusSearchbar(); + else if (currentTab == Tabs::COINS) + m_coinsWidget->focusSearchbar(); } -MainWindow::~MainWindow() { - delete ui; -} +MainWindow::~MainWindow() = default; \ No newline at end of file diff --git a/src/mainwindow.h b/src/MainWindow.h similarity index 85% rename from src/mainwindow.h rename to src/MainWindow.h index a54f2b8..b83f0a1 100644 --- a/src/mainwindow.h +++ b/src/MainWindow.h @@ -1,28 +1,27 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef MAINWINDOW_H -#define MAINWINDOW_H +#ifndef FEATHER_MAINWINDOW_H +#define FEATHER_MAINWINDOW_H #include #include #include -#include #include "appcontext.h" #include "components.h" -#include "calcwindow.h" -#include "settings.h" +#include "CalcWindow.h" +#include "SettingsDialog.h" -#include "dialog/aboutdialog.h" -#include "dialog/signverifydialog.h" -#include "dialog/verifyproofdialog.h" -#include "dialog/seeddialog.h" -#include "dialog/passwordchangedialog.h" -#include "dialog/keysdialog.h" -#include "dialog/aboutdialog.h" +#include "dialog/AboutDialog.h" +#include "dialog/SignVerifyDialog.h" +#include "dialog/VerifyProofDialog.h" +#include "dialog/SeedDialog.h" +#include "dialog/PasswordChangeDialog.h" +#include "dialog/KeysDialog.h" +#include "dialog/AboutDialog.h" #include "dialog/RestoreHeightDialog.h" -#include "dialog/splashdialog.h" +#include "dialog/SplashDialog.h" #include "libwalletqt/Wallet.h" #include "model/SubaddressModel.h" #include "model/SubaddressProxyModel.h" @@ -31,16 +30,16 @@ #include "model/CoinsProxyModel.h" #include "utils/networking.h" #include "utils/config.h" -#include "widgets/ccswidget.h" -#include "widgets/redditwidget.h" +#include "widgets/CCSWidget.h" +#include "widgets/RedditWidget.h" #include "widgets/TickerWidget.h" #include "wizard/WalletWizard.h" -#include "contactswidget.h" -#include "historywidget.h" -#include "sendwidget.h" -#include "receivewidget.h" -#include "coinswidget.h" +#include "ContactsWidget.h" +#include "HistoryWidget.h" +#include "SendWidget.h" +#include "ReceiveWidget.h" +#include "CoinsWidget.h" #include "WindowManager.h" @@ -49,7 +48,7 @@ #endif #ifdef HAS_XMRIG -#include "widgets/xmrigwidget.h" +#include "widgets/XMRigWidget.h" #endif namespace Ui { @@ -114,6 +113,7 @@ private slots: void menuWalletCloseClicked(); void menuTorClicked(); void menuToggleTabVisible(const QString &key); + void menuClearHistoryClicked(); void onExportHistoryCSV(bool checked); void onExportContactsCSV(bool checked); void onCreateDesktopEntry(bool checked); @@ -156,6 +156,7 @@ private slots: void showViewOnlyDialog(); void showWalletCacheDebugDialog(); void showAccountSwitcherDialog(); + void showAddressChecker(); void donateButtonClicked(); void showCalcWindow(); @@ -172,12 +173,17 @@ private slots: void showRestoreHeightDialog(); void importTransaction(); void onDeviceError(const QString &error); + void onDeviceButtonRequest(quint64 code); + void onDeviceButtonPressed(); + void onWalletPassphraseNeeded(bool on_device); void menuHwDeviceClicked(); void onUpdatesAvailable(const QJsonObject &updates); void toggleSearchbar(bool enabled); void onSetStatusText(const QString &text); private: + friend WindowManager; + void initStatusBar(); void initWidgets(); void initMenu(); @@ -203,10 +209,14 @@ private: QString getHardwareDevice(); void updateTitle(); void donationNag(); - void updateRecentlyOpened(const QString &filename); + void addToRecentlyOpened(const QString &filename); + void updateRecentlyOpenedMenu(); void updateWidgetIcons(); - Ui::MainWindow *ui; + QIcon hardwareDevicePairedIcon(); + QIcon hardwareDeviceUnpairedIcon(); + + QScopedPointer ui; WindowManager *m_windowManager; QSharedPointer m_ctx; @@ -229,6 +239,8 @@ private: QList m_priceTickerWidgets; BalanceTickerWidget *m_balanceTickerWidget; + QPointer m_clearRecentlyOpenAction; + // lower status bar QPushButton *m_statusUpdateAvailable; ClickableLabel *m_statusLabelBalance; @@ -257,4 +269,4 @@ private: bool cleanedUp = false; }; -#endif // MAINWINDOW_H +#endif // FEATHER_MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/MainWindow.ui similarity index 98% rename from src/mainwindow.ui rename to src/MainWindow.ui index fc0c81d..6b88681 100644 --- a/src/mainwindow.ui +++ b/src/MainWindow.ui @@ -352,7 +352,7 @@ 0 0 977 - 28 + 27 @@ -455,6 +455,7 @@ + @@ -797,25 +798,30 @@ Show Searchbar + + + Address checker + + CalcWidget QWidget -
calcwidget.h
+
CalcWidget.h
1
CCSWidget QWidget -
widgets/ccswidget.h
+
widgets/CCSWidget.h
1
RedditWidget QWidget -
widgets/redditwidget.h
+
widgets/RedditWidget.h
1
diff --git a/src/receivewidget.cpp b/src/ReceiveWidget.cpp similarity index 96% rename from src/receivewidget.cpp rename to src/ReceiveWidget.cpp index e5a74e6..477e51c 100644 --- a/src/receivewidget.cpp +++ b/src/ReceiveWidget.cpp @@ -1,19 +1,20 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "ui_receivewidget.h" -#include "receivewidget.h" -#include "model/ModelUtils.h" -#include "dialog/qrcodedialog.h" -#include "utils/Icons.h" +#include "ReceiveWidget.h" +#include "ui_ReceiveWidget.h" #include #include +#include "dialog/QrCodeDialog.h" +#include "model/ModelUtils.h" +#include "utils/Icons.h" + ReceiveWidget::ReceiveWidget(QSharedPointer ctx, QWidget *parent) - : QWidget(parent) - , ui(new Ui::ReceiveWidget) - , m_ctx(std::move(ctx)) + : QWidget(parent) + , ui(new Ui::ReceiveWidget) + , m_ctx(std::move(ctx)) { ui->setupUi(this); @@ -208,9 +209,8 @@ void ReceiveWidget::showQrCodeDialog() { } QString address = index.model()->data(index.siblingAtColumn(SubaddressModel::Address), Qt::UserRole).toString(); QrCode qr(address, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH); - auto *dialog = new QrCodeDialog(this, qr, "Address"); - dialog->exec(); - dialog->deleteLater(); + QrCodeDialog dialog{this, &qr, "Address"}; + dialog.exec(); } QStringList ReceiveWidget::getHiddenAddresses() { @@ -243,6 +243,4 @@ Monero::SubaddressRow* ReceiveWidget::currentEntry() { } } -ReceiveWidget::~ReceiveWidget() { - delete ui; -} +ReceiveWidget::~ReceiveWidget() = default; \ No newline at end of file diff --git a/src/receivewidget.h b/src/ReceiveWidget.h similarity index 96% rename from src/receivewidget.h rename to src/ReceiveWidget.h index 8fa16e8..23ee78b 100644 --- a/src/receivewidget.h +++ b/src/ReceiveWidget.h @@ -4,14 +4,15 @@ #ifndef FEATHER_RECEIVEWIDGET_H #define FEATHER_RECEIVEWIDGET_H +#include +#include +#include + #include "appcontext.h" -#include "qrcode/QrCode.h" #include "libwalletqt/Subaddress.h" #include "model/SubaddressProxyModel.h" #include "model/SubaddressModel.h" - -#include -#include +#include "qrcode/QrCode.h" namespace Ui { class ReceiveWidget; @@ -50,7 +51,7 @@ private slots: void generateSubaddress(); private: - Ui::ReceiveWidget *ui; + QScopedPointer ui; QSharedPointer m_ctx; QMenu *m_headerMenu; QAction *m_showFullAddressesAction; diff --git a/src/receivewidget.ui b/src/ReceiveWidget.ui similarity index 100% rename from src/receivewidget.ui rename to src/ReceiveWidget.ui diff --git a/src/sendwidget.cpp b/src/SendWidget.cpp similarity index 91% rename from src/sendwidget.cpp rename to src/SendWidget.cpp index 60f3fdc..c767170 100644 --- a/src/sendwidget.cpp +++ b/src/SendWidget.cpp @@ -1,14 +1,15 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. +#include "SendWidget.h" +#include "ui_SendWidget.h" + #include -#include "sendwidget.h" -#include "mainwindow.h" -#include "ui_sendwidget.h" + +#include "ColorScheme.h" #include "constants.h" #include "utils/AppData.h" #include "Icons.h" -#include "ColorScheme.h" #ifdef WITH_SCANNER #include "qrcode_scanner/QrCodeScanDialog.h" @@ -40,6 +41,7 @@ SendWidget::SendWidget(QSharedPointer ctx, QWidget *parent) connect(ui->lineAmount, &QLineEdit::textChanged, this, &SendWidget::amountEdited); connect(ui->lineAddress, &QPlainTextEdit::textChanged, this, &SendWidget::addressEdited); connect(ui->btn_openAlias, &QPushButton::clicked, this, &SendWidget::aliasClicked); + connect(ui->lineAddress, &PayToEdit::dataPasted, this, &SendWidget::onDataPasted); ui->label_conversionAmount->setText(""); ui->label_conversionAmount->hide(); ui->btn_openAlias->hide(); @@ -86,22 +88,20 @@ void SendWidget::amountEdited(const QString &text) { this->updateConversionLabel(); } -void SendWidget::fill(const CCSEntry &entry) { - this->fill(entry.address, QString("CCS: %1").arg(entry.title), 0.0); -} - void SendWidget::fill(double amount) { ui->lineAmount->setText(QString::number(amount)); } void SendWidget::fill(const QString &address, const QString &description, double amount) { - ui->lineDescription->setText(description); ui->lineAddress->setText(address); - ui->lineAddress->moveCursor(QTextCursor::Start); + ui->lineDescription->setText(description); + if (amount > 0) ui->lineAmount->setText(QString::number(amount)); + ui->lineAmount->setFocus(); + this->updateConversionLabel(); } @@ -275,6 +275,25 @@ void SendWidget::onEndTransaction() { ui->btnSend->setEnabled(true); } +void SendWidget::onDataPasted(const QString &data) { + if (!data.isEmpty()) { + QVariantMap uriData = m_ctx->wallet->parse_uri_to_object(data); + if (!uriData.contains("error")) { + if (uriData.contains("address")) + ui->lineAddress->setText(uriData.value("address").toString()); + if (uriData.contains("amount")) + ui->lineAmount->setText(uriData.value("amount").toString()); + if (uriData.contains("tx_description")) + ui->lineDescription->setText(uriData.value("tx_description").toString()); + } else { + ui->lineAddress->setText(data); + } + } + else { + QMessageBox::warning(this, "Error", "No Qr Code found."); + } +} + void SendWidget::setupComboBox() { ui->comboCurrencySelection->clear(); @@ -302,6 +321,4 @@ void SendWidget::skinChanged() { } } -SendWidget::~SendWidget() { - delete ui; -} +SendWidget::~SendWidget() = default; \ No newline at end of file diff --git a/src/sendwidget.h b/src/SendWidget.h similarity index 80% rename from src/sendwidget.h rename to src/SendWidget.h index f72b985..31c9c0a 100644 --- a/src/sendwidget.h +++ b/src/SendWidget.h @@ -1,12 +1,13 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef SENDWIDGET_H -#define SENDWIDGET_H +#ifndef FEATHER_SENDWIDGET_H +#define FEATHER_SENDWIDGET_H #include + #include "appcontext.h" -#include "widgets/ccswidget.h" +#include "widgets/CCSWidget.h" namespace Ui { class SendWidget; @@ -18,8 +19,7 @@ Q_OBJECT public: explicit SendWidget(QSharedPointer ctx, QWidget *parent = nullptr); - void fill(const CCSEntry &entry); - void fill(const QString &address, const QString& description, double amount = 0); + void fill(const QString &address, const QString &description, double amount = 0); void fill(double amount); void clearFields(); void payToMany(); @@ -44,14 +44,17 @@ public slots: void onInitiateTransaction(); void onEndTransaction(); +private slots: + void onDataPasted(const QString &data); + private: void setupComboBox(); double amountDouble(); - Ui::SendWidget *ui; + QScopedPointer ui; QSharedPointer m_ctx; quint64 amount(); double conversionAmount(); }; -#endif // SENDWIDGET_H +#endif // FEATHER_SENDWIDGET_H diff --git a/src/sendwidget.ui b/src/SendWidget.ui similarity index 100% rename from src/sendwidget.ui rename to src/SendWidget.ui diff --git a/src/settings.cpp b/src/SettingsDialog.cpp similarity index 97% rename from src/settings.cpp rename to src/SettingsDialog.cpp index fe3eb2c..d948f96 100644 --- a/src/settings.cpp +++ b/src/SettingsDialog.cpp @@ -1,16 +1,15 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "settings.h" -#include "ui_settings.h" -#include "mainwindow.h" +#include "SettingsDialog.h" +#include "ui_SettingsDialog.h" #include Settings::Settings(QSharedPointer ctx, QWidget *parent) - : QDialog(parent) - , ui(new Ui::Settings) - , m_ctx(std::move(ctx)) + : QDialog(parent) + , ui(new Ui::Settings) + , m_ctx(std::move(ctx)) { ui->setupUi(this); @@ -194,6 +193,4 @@ void Settings::setupLocalMoneroFrontendCombobox() { ui->combo_localMoneroFrontend->setCurrentIndex(ui->combo_localMoneroFrontend->findData(config()->get(Config::localMoneroFrontend).toString())); } -Settings::~Settings() { - delete ui; -} +Settings::~Settings() = default; \ No newline at end of file diff --git a/src/settings.h b/src/SettingsDialog.h similarity index 90% rename from src/settings.h rename to src/SettingsDialog.h index 24d01db..4cbf18c 100644 --- a/src/settings.h +++ b/src/SettingsDialog.h @@ -1,15 +1,15 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef SETTINGS_H -#define SETTINGS_H +#ifndef FEATHER_SETTINGS_H +#define FEATHER_SETTINGS_H +#include #include #include -#include #include "appcontext.h" -#include "widgets/nodewidget.h" +#include "widgets/NodeWidget.h" namespace Ui { class Settings; @@ -50,7 +50,7 @@ private: void setupSkinCombobox(); void setupLocalMoneroFrontendCombobox(); - Ui::Settings *ui; + QScopedPointer ui; QSharedPointer m_ctx; QStringList m_skins{"Native", "QDarkStyle", "Breeze/Dark", "Breeze/Light"}; @@ -58,4 +58,4 @@ private: QStringList m_timeFormats{"hh:mm", "hh:mm ap"}; }; -#endif // SETTINGS_H +#endif // FEATHER_SETTINGS_H diff --git a/src/settings.ui b/src/SettingsDialog.ui similarity index 99% rename from src/settings.ui rename to src/SettingsDialog.ui index 533afdf..554d5e3 100644 --- a/src/settings.ui +++ b/src/SettingsDialog.ui @@ -670,7 +670,7 @@ NodeWidget QWidget -
widgets/nodewidget.h
+
widgets/NodeWidget.h
1
diff --git a/src/WindowManager.cpp b/src/WindowManager.cpp index ad5326e..cddd7e4 100644 --- a/src/WindowManager.cpp +++ b/src/WindowManager.cpp @@ -2,25 +2,30 @@ // Copyright (c) 2020-2021, The Monero Project. #include "WindowManager.h" + +#include +#include + #include "constants.h" -#include "dialog/passworddialog.h" -#include "dialog/splashdialog.h" -#include "utils/WebsocketNotifier.h" -#include "utils/os/tails.h" +#include "dialog/PasswordDialog.h" +#include "dialog/SplashDialog.h" #include "utils/Icons.h" #include "utils/NetworkManager.h" +#include "utils/os/tails.h" #include "utils/TorManager.h" - -#include +#include "utils/WebsocketNotifier.h" WindowManager::WindowManager() { m_walletManager = WalletManager::instance(); m_splashDialog = new SplashDialog; + m_cleanupThread = new QThread(); connect(m_walletManager, &WalletManager::walletOpened, this, &WindowManager::onWalletOpened); connect(m_walletManager, &WalletManager::walletCreated, this, &WindowManager::onWalletCreated); connect(m_walletManager, &WalletManager::deviceButtonRequest, this, &WindowManager::onDeviceButtonRequest); + connect(m_walletManager, &WalletManager::deviceButtonPressed, this, &WindowManager::onDeviceButtonPressed); connect(m_walletManager, &WalletManager::deviceError, this, &WindowManager::onDeviceError); + connect(m_walletManager, &WalletManager::walletPassphraseNeeded, this, &WindowManager::onWalletPassphraseNeeded); connect(qApp, &QGuiApplication::lastWindowClosed, this, &WindowManager::quitAfterLastWindow); @@ -67,6 +72,11 @@ void WindowManager::close() { void WindowManager::closeWindow(MainWindow *window) { m_windows.removeOne(window); + + // Move Wallet to a different thread for cleanup so it doesn't block GUI thread + window->m_ctx->wallet->moveToThread(m_cleanupThread); + m_cleanupThread->start(); + window->m_ctx->wallet->deleteLater(); } void WindowManager::restartApplication(const QString &binaryFilename) { @@ -141,7 +151,7 @@ void WindowManager::onWalletOpened(Wallet *wallet) { // Don't show incorrect password when we try with empty password for the first time bool showIncorrectPassword = m_openWalletTriedOnce; m_openWalletTriedOnce = true; - this->onWalletOpenPasswordRequired(showIncorrectPassword, wallet->cachePath()); + this->onWalletOpenPasswordRequired(showIncorrectPassword, wallet->keysPath()); } else if (errMsg == QString("basic_string::_M_replace_aux") || errMsg == QString("std::bad_alloc")) { qCritical() << errMsg; @@ -227,7 +237,7 @@ void WindowManager::tryCreateWallet(FeatherSeed seed, const QString &path, const this->onWalletOpened(wallet); } -void WindowManager::tryCreateWalletFromDevice(const QString &path, const QString &password, int restoreHeight) +void WindowManager::tryCreateWalletFromDevice(const QString &path, const QString &password, const QString &deviceName, int restoreHeight) { if (Utils::fileExists(path)) { auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path); @@ -236,7 +246,7 @@ void WindowManager::tryCreateWalletFromDevice(const QString &path, const QString } m_openingWallet = true; - m_walletManager->createWalletFromDeviceAsync(path, password, constants::networkType, "Ledger", restoreHeight); + m_walletManager->createWalletFromDeviceAsync(path, password, constants::networkType, deviceName, restoreHeight); } void WindowManager::tryCreateWalletFromKeys(const QString &path, const QString &password, const QString &address, @@ -293,16 +303,38 @@ void WindowManager::handleWalletError(const QString &message) { } void WindowManager::displayWalletErrorMessage(const QString &message) { - QString errMsg = message; + QString errMsg = QString("Error: %1").arg(message); + QString link; + + // Ledger if (message.contains("No device found")) { - errMsg += "\n\nThis wallet is backed by a hardware device. Make sure the Monero app is opened on the device.\n" + errMsg += "\n\nThis wallet is backed by a Ledger hardware device. Make sure the Monero app is opened on the device.\n" "You may need to restart Feather before the device can get detected."; } if (message.contains("Unable to open device")) { errMsg += "\n\nThe device might be in use by a different application."; #if defined(Q_OS_LINUX) errMsg += "\n\nNote: On Linux you may need to follow the instructions in the link below before the device can be opened:\n" - "https://support.ledger.com/hc/en-us/articles/115005165269-Fix-connection-issues"; + "https://support.ledger.com/hc/en-us/articles/115005165269-Fix-connection-issues"; + link = "https://support.ledger.com/hc/en-us/articles/115005165269-Fix-connection-issues"; +#endif + } + + // TREZOR + if (message.contains("Unable to claim libusb device")) { + errMsg += "\n\nThis wallet is backed by a Trezor hardware device. Feather was unable to access the device. " + "Please make sure it is not opened by another program and try again."; + } + if (message.contains("Cannot get a device address")) { + errMsg += "\n\nRestart the Trezor hardware device and try again."; + } + + if (message.contains("Could not connect to the device Trezor") || message.contains("Device connect failed")) { + errMsg += "\n\nThis wallet is backed by a Trezor hardware device. Make sure the device is connected to your computer and unlocked."; +#if defined(Q_OS_LINUX) + errMsg += "\n\nNote: On Linux you may need to follow the instructions in the link below before the device can be opened:\n" + "https://wiki.trezor.io/Udev_rules"; + link = "https://wiki.trezor.io/Udev_rules"; #endif } @@ -323,23 +355,68 @@ void WindowManager::displayWalletErrorMessage(const QString &message) { msgBox.setWindowTitle("Wallet error"); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); + QPushButton *openLinkButton = nullptr; + if (!link.isEmpty()) { + openLinkButton = msgBox.addButton("Open link", QMessageBox::ActionRole); + } msgBox.exec(); + if (openLinkButton && msgBox.clickedButton() == openLinkButton) { + Utils::externalLinkWarning(nullptr, link); + } } // ######################## DEVICE ######################## void WindowManager::onDeviceButtonRequest(quint64 code) { - m_splashDialog->setMessage("Action required on device: Export the view key to open the wallet."); + QString message; + switch (code) { + case 1: // Trezor + message = "Action required on device: enter your PIN to continue."; + break; + case 8: // Trezor + message = "Action required on device: Export watch-only credentials to open the wallet."; + break; + case 19: // Trezor + message = "Action required on device: Enter passphrase to open the wallet."; + break; + default: + message = "Action required on device: Export the view key to open the wallet."; + } + + m_splashDialog->setMessage(message); m_splashDialog->setIcon(QPixmap(":/assets/images/key.png")); m_splashDialog->show(); m_splashDialog->setEnabled(true); } +void WindowManager::onDeviceButtonPressed() { + m_splashDialog->hide(); +} + void WindowManager::onDeviceError(const QString &errorMessage) { // TODO: when does this get called? qCritical() << Q_FUNC_INFO << errorMessage; } +void WindowManager::onWalletPassphraseNeeded(bool on_device) { + auto button = QMessageBox::question(nullptr, "Wallet Passphrase Needed", "Enter passphrase on hardware wallet?\n\n" + "It is recommended to enter passphrase on " + "the hardware wallet for better security.", + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (button == QMessageBox::Yes) { + m_walletManager->onPassphraseEntered("", true, false); + return; + } + + bool ok; + QString passphrase = QInputDialog::getText(nullptr, "Wallet Passphrase Needed", "Enter passphrase:", QLineEdit::EchoMode::Password, "", &ok); + if (ok) { + m_walletManager->onPassphraseEntered(passphrase, false, false); + } else { + m_walletManager->onPassphraseEntered(passphrase, false, true); + } +} + // ######################## TRAY ######################## void WindowManager::buildTrayMenu() { diff --git a/src/WindowManager.h b/src/WindowManager.h index 7c9c3d9..801d537 100644 --- a/src/WindowManager.h +++ b/src/WindowManager.h @@ -5,11 +5,12 @@ #define FEATHER_WINDOWMANAGER_H #include + +#include "dialog/TorInfoDialog.h" #include "libwalletqt/WalletManager.h" #include "libwalletqt/Wallet.h" +#include "MainWindow.h" #include "wizard/WalletWizard.h" -#include "dialog/torinfodialog.h" -#include "mainwindow.h" class MainWindow; class WindowManager : public QObject { @@ -38,11 +39,13 @@ private slots: void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path); void onInitialNetworkConfigured(); void onDeviceButtonRequest(quint64 code); + void onDeviceButtonPressed(); void onDeviceError(const QString &errorMessage); + void onWalletPassphraseNeeded(bool on_device); private: void tryCreateWallet(FeatherSeed seed, const QString &path, const QString &password, const QString &seedOffset); - void tryCreateWalletFromDevice(const QString &path, const QString &password, int restoreHeight); + void tryCreateWalletFromDevice(const QString &path, const QString &password, const QString &deviceName, int restoreHeight); void tryCreateWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight); bool autoOpenWallet(); @@ -76,6 +79,8 @@ private: bool m_openWalletTriedOnce = false; bool m_openingWallet = false; bool m_initialNetworkConfigured = false; + + QThread *m_cleanupThread; }; diff --git a/src/appcontext.cpp b/src/appcontext.cpp index 96b4563..2714b12 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -2,7 +2,6 @@ // Copyright (c) 2020-2021, The Monero Project. #include -#include #include "appcontext.h" #include "constants.h" @@ -27,21 +26,22 @@ AppContext::AppContext(Wallet *wallet) , networkType(constants::networkType) , m_rpc(new DaemonRpc{this, getNetworkTor(), ""}) { - connect(this->wallet.get(), &Wallet::moneySpent, this, &AppContext::onMoneySpent); - connect(this->wallet.get(), &Wallet::moneyReceived, this, &AppContext::onMoneyReceived); - connect(this->wallet.get(), &Wallet::unconfirmedMoneyReceived, this, &AppContext::onUnconfirmedMoneyReceived); - connect(this->wallet.get(), &Wallet::newBlock, this, &AppContext::onWalletNewBlock); - connect(this->wallet.get(), &Wallet::updated, this, &AppContext::onWalletUpdate); - connect(this->wallet.get(), &Wallet::refreshed, this, &AppContext::onWalletRefreshed); - connect(this->wallet.get(), &Wallet::transactionCommitted, this, &AppContext::onTransactionCommitted); - connect(this->wallet.get(), &Wallet::heightRefreshed, this, &AppContext::onHeightRefreshed); - connect(this->wallet.get(), &Wallet::transactionCreated, this, &AppContext::onTransactionCreated); - connect(this->wallet.get(), &Wallet::deviceError, this, &AppContext::onDeviceError); - connect(this->wallet.get(), &Wallet::deviceButtonRequest, this, &AppContext::onDeviceButtonRequest); - connect(this->wallet.get(), &Wallet::connectionStatusChanged, [this]{ + connect(this->wallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent); + connect(this->wallet, &Wallet::moneyReceived, this, &AppContext::onMoneyReceived); + connect(this->wallet, &Wallet::unconfirmedMoneyReceived, this, &AppContext::onUnconfirmedMoneyReceived); + connect(this->wallet, &Wallet::newBlock, this, &AppContext::onWalletNewBlock); + connect(this->wallet, &Wallet::updated, this, &AppContext::onWalletUpdate); + connect(this->wallet, &Wallet::refreshed, this, &AppContext::onWalletRefreshed); + connect(this->wallet, &Wallet::transactionCommitted, this, &AppContext::onTransactionCommitted); + connect(this->wallet, &Wallet::heightRefreshed, this, &AppContext::onHeightRefreshed); + connect(this->wallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated); + connect(this->wallet, &Wallet::deviceError, this, &AppContext::onDeviceError); + connect(this->wallet, &Wallet::deviceButtonRequest, this, &AppContext::onDeviceButtonRequest); + connect(this->wallet, &Wallet::deviceButtonPressed, this, &AppContext::onDeviceButtonPressed); + connect(this->wallet, &Wallet::connectionStatusChanged, [this]{ this->nodes->autoConnect(); }); - connect(this->wallet.get(), &Wallet::currentSubaddressAccountChanged, [this]{ + connect(this->wallet, &Wallet::currentSubaddressAccountChanged, [this]{ this->updateBalance(); }); @@ -110,13 +110,13 @@ void AppContext::onCreateTransactionMultiDest(const QVector &addresses, emit initiateTransaction(); } -void AppContext::onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) { +void AppContext::onSweepOutputs(const QVector &keyImages, QString address, bool churn, int outputs) { if (churn) { - address = this->wallet->address(0, 0); // primary address + address = this->wallet->address(0, 0); } qInfo() << "Creating transaction"; - this->wallet->createTransactionSingleAsync(keyImage, address, outputs, this->tx_priority); + this->wallet->createTransactionSelectedAsync(keyImages, address, outputs, this->tx_priority); emit initiateTransaction(); } @@ -158,6 +158,15 @@ void AppContext::onMultiBroadcast(PendingTransaction *tx) { } } +void AppContext::addCacheTransaction(const QString &txid, const QString &txHex) const { + this->wallet->setCacheAttribute(QString("tx:%1").arg(txid), txHex); +} + +QString AppContext::getCacheTransaction(const QString &txid) const { + QString txHex = this->wallet->getCacheAttribute(QString("tx:%1").arg(txid)); + return txHex; +} + // ################## Models ################## void AppContext::onPreferredFiatCurrencyChanged(const QString &symbol) { @@ -179,6 +188,10 @@ void AppContext::onDeviceButtonRequest(quint64 code) { emit deviceButtonRequest(code); } +void AppContext::onDeviceButtonPressed() { + emit deviceButtonPressed(); +} + void AppContext::onDeviceError(const QString &message) { qCritical() << "Device error: " << message; emit deviceError(message); @@ -257,6 +270,10 @@ void AppContext::onOpenAliasResolve(const QString &openAlias) { emit openAliasResolveError(msg); } +void AppContext::stopTimers() { + m_storeTimer.stop(); +} + // ########################################## LIBWALLET QT SIGNALS #################################################### void AppContext::onMoneySpent(const QString &txId, quint64 amount) { diff --git a/src/appcontext.h b/src/appcontext.h index ebf482b..46fd215 100644 --- a/src/appcontext.h +++ b/src/appcontext.h @@ -25,7 +25,7 @@ Q_OBJECT public: explicit AppContext(Wallet *wallet); - QScopedPointer wallet; + Wallet *wallet; Nodes *nodes; bool donationSending = false; @@ -34,7 +34,6 @@ public: NetworkType::Type networkType; PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low; - QMap txCache; // libwalletqt bool refreshed = false; @@ -46,11 +45,16 @@ public: void storeWallet(); + void stopTimers(); + + void addCacheTransaction(const QString &txid, const QString &txHex) const; + QString getCacheTransaction(const QString &txid) const; + public slots: void onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all); void onCreateTransactionMultiDest(const QVector &addresses, const QVector &amounts, const QString &description); void onCancelTransaction(PendingTransaction *tx, const QVector &address); - void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs); + void onSweepOutputs(const QVector &keyImages, QString address, bool churn, int outputs); void onCreateTransactionError(const QString &msg); void onOpenAliasResolve(const QString &openAlias); void onSetRestoreHeight(quint64 height); @@ -58,6 +62,7 @@ public slots: void onAmountPrecisionChanged(int precision); void onMultiBroadcast(PendingTransaction *tx); void onDeviceButtonRequest(quint64 code); + void onDeviceButtonPressed(); void onDeviceError(const QString &message); void onTorSettingsChanged(); // should not be here @@ -91,6 +96,7 @@ signals: void initiateTransaction(); void endTransaction(); void deviceButtonRequest(quint64 code); + void deviceButtonPressed(); void deviceError(const QString &message); private: diff --git a/src/assets.qrc b/src/assets.qrc index 9956d8b..4e81320 100644 --- a/src/assets.qrc +++ b/src/assets.qrc @@ -107,7 +107,9 @@ assets/images/tor_logo_disabled.png assets/images/tor_logo.png assets/images/trezor.png + assets/images/trezor_white.png assets/images/trezor_unpaired.png + assets/images/trezor_unpaired_white.png assets/images/unconfirmed.png assets/images/unlock.png assets/images/unlock.svg diff --git a/src/assets/images/trezor_unpaired_white.png b/src/assets/images/trezor_unpaired_white.png new file mode 100644 index 0000000..751f663 Binary files /dev/null and b/src/assets/images/trezor_unpaired_white.png differ diff --git a/src/assets/images/trezor_white.png b/src/assets/images/trezor_white.png new file mode 100644 index 0000000..30a52a9 Binary files /dev/null and b/src/assets/images/trezor_white.png differ diff --git a/src/assets/nodes.json b/src/assets/nodes.json index 8b4c13a..7c99388 100644 --- a/src/assets/nodes.json +++ b/src/assets/nodes.json @@ -1,7 +1,6 @@ { "mainnet": { "tor": [ - "xmrtolujkxnlinre.onion:18081", "xmrag4hf5xlabmob.onion:18081", "monero26mmldsallmxok2kwamne4ve3mybvvn2yijsvss7ey63hc4yyd.onion:18081", "monero5sjoz5xmjn.onion:18081", @@ -24,14 +23,23 @@ "node.xmr.ru:18081", "selsta1.featherwallet.net:18081", "selsta2.featherwallet.net:18081", - "node-1.sethsimmons.me:18089" + "node-1.sethsimmons.me:18089", + "node.melo.tools:18081" + ] + }, + "testnet": { + "tor": [], + "clearnet": [ + "testnet.melo.tools:28081" ] }, "stagenet": { - "tor": [], + "tor": [ + "ct36dsbe3oubpbebpxmiqz4uqk6zb6nhmkhoekileo4fts23rvuse2qd.onion:38081" + ], "clearnet": [ - "run.your.own.node.xmr.pm:38089", - "super.fast.node.xmr.pm:38089" + "super.fast.node.xmr.pm:38089", + "stagenet.melo.tools:38081" ] } } diff --git a/src/components.h b/src/components.h index 546c27b..eeb3804 100644 --- a/src/components.h +++ b/src/components.h @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef COMP_H -#define COMP_H +#ifndef FEATHER_COMPONENTS_H +#define FEATHER_COMPONENTS_H #include #include @@ -120,4 +120,5 @@ protected: void mousePressEvent(QMouseEvent* event) override; }; -#endif + +#endif //FEATHER_COMPONENTS_H diff --git a/src/constants.cpp b/src/constants.cpp new file mode 100644 index 0000000..d8b0c1c --- /dev/null +++ b/src/constants.cpp @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#include "constants.h" + +namespace constants { + NetworkType::Type networkType = NetworkType::Type::MAINNET; +}; diff --git a/src/constants.h b/src/constants.h index 76c79e1..d9e9963 100644 --- a/src/constants.h +++ b/src/constants.h @@ -11,8 +11,7 @@ namespace constants { - // application constants - static NetworkType::Type networkType; // TODO: compiler moans, also not really a const + extern NetworkType::Type networkType; // TODO: not really a const // coin constants const std::string coinName = "monero"; diff --git a/src/dialog/aboutdialog.cpp b/src/dialog/AboutDialog.cpp similarity index 92% rename from src/dialog/aboutdialog.cpp rename to src/dialog/AboutDialog.cpp index a076c0b..6825400 100644 --- a/src/dialog/aboutdialog.cpp +++ b/src/dialog/AboutDialog.cpp @@ -1,10 +1,11 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "aboutdialog.h" -#include "ui_aboutdialog.h" -#include "utils/utils.h" +#include "AboutDialog.h" +#include "ui_AboutDialog.h" + #include "config-feather.h" +#include "utils/Utils.h" AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent) @@ -38,7 +39,4 @@ AboutDialog::AboutDialog(QWidget *parent) this->adjustSize(); } -AboutDialog::~AboutDialog() { - delete ui; -} - +AboutDialog::~AboutDialog() = default; \ No newline at end of file diff --git a/src/dialog/aboutdialog.h b/src/dialog/AboutDialog.h similarity index 76% rename from src/dialog/aboutdialog.h rename to src/dialog/AboutDialog.h index 5f08290..4853e4f 100644 --- a/src/dialog/aboutdialog.h +++ b/src/dialog/AboutDialog.h @@ -1,12 +1,11 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef ABOUT_H -#define ABOUT_H +#ifndef FEATHER_ABOUT_H +#define FEATHER_ABOUT_H #include #include -#include namespace Ui { class AboutDialog; @@ -21,8 +20,8 @@ public: ~AboutDialog() override; private: - Ui::AboutDialog *ui; + QScopedPointer ui; QStringListModel *m_model; }; -#endif // ABOUT_H +#endif // FEATHER_ABOUT_H diff --git a/src/dialog/aboutdialog.ui b/src/dialog/AboutDialog.ui similarity index 100% rename from src/dialog/aboutdialog.ui rename to src/dialog/AboutDialog.ui diff --git a/src/dialog/AccountSwitcherDialog.cpp b/src/dialog/AccountSwitcherDialog.cpp index 5041657..ee1e10e 100644 --- a/src/dialog/AccountSwitcherDialog.cpp +++ b/src/dialog/AccountSwitcherDialog.cpp @@ -4,11 +4,12 @@ #include "AccountSwitcherDialog.h" #include "ui_AccountSwitcherDialog.h" -#include "libwalletqt/SubaddressAccount.h" -#include "utils/Icons.h" -#include "model/ModelUtils.h" #include +#include "libwalletqt/SubaddressAccount.h" +#include "model/ModelUtils.h" +#include "utils/Icons.h" + AccountSwitcherDialog::AccountSwitcherDialog(QSharedPointer ctx, QWidget *parent) : QDialog(parent) , ui(new Ui::AccountSwitcherDialog) @@ -44,7 +45,7 @@ AccountSwitcherDialog::AccountSwitcherDialog(QSharedPointer ctx, QWi m_ctx->wallet->subaddressAccount()->refresh(); }); - connect(m_ctx->wallet.get(), &Wallet::currentSubaddressAccountChanged, this, &AccountSwitcherDialog::updateSelection); + connect(m_ctx->wallet, &Wallet::currentSubaddressAccountChanged, this, &AccountSwitcherDialog::updateSelection); connect(m_ctx->wallet->subaddressAccount(), &SubaddressAccount::refreshFinished, this, &AccountSwitcherDialog::updateSelection); this->updateSelection(); @@ -78,7 +79,6 @@ void AccountSwitcherDialog::editLabel() { } void AccountSwitcherDialog::updateSelection() { - qDebug() << "test"; QModelIndex index = m_model->index(m_ctx->wallet->currentSubaddressAccount(), 0); ui->accounts->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } @@ -103,6 +103,4 @@ Monero::SubaddressAccountRow* AccountSwitcherDialog::currentEntry() { return m_ctx->wallet->subaddressAccountModel()->entryFromIndex(index); } -AccountSwitcherDialog::~AccountSwitcherDialog() { - delete ui; -} +AccountSwitcherDialog::~AccountSwitcherDialog() = default; diff --git a/src/dialog/AccountSwitcherDialog.h b/src/dialog/AccountSwitcherDialog.h index 077b3c1..08c15be 100644 --- a/src/dialog/AccountSwitcherDialog.h +++ b/src/dialog/AccountSwitcherDialog.h @@ -5,8 +5,8 @@ #define FEATHER_ACCOUNTSWITCHERDIALOG_H #include -#include "appcontext.h" +#include "appcontext.h" #include "model/SubaddressAccountModel.h" namespace Ui { @@ -33,7 +33,7 @@ private: Monero::SubaddressAccountRow* currentEntry(); - Ui::AccountSwitcherDialog *ui; + QScopedPointer ui; QSharedPointer m_ctx; SubaddressAccountModel *m_model; SubaddressAccountProxyModel *m_proxyModel; diff --git a/src/dialog/AddressInfoDialog.ui b/src/dialog/AddressInfoDialog.ui new file mode 100644 index 0000000..b5edc52 --- /dev/null +++ b/src/dialog/AddressInfoDialog.ui @@ -0,0 +1,139 @@ + + + AddressInfoDialog + + + + 0 + 0 + 520 + 310 + + + + Address + + + + + + Address: + + + + + + + true + + + + + + + Public view key: + + + + + + + true + + + + + + + Public spend key: + + + + + + + true + + + + + + + Subaddress index: + + + + + + + + 500 + 0 + + + + true + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AddressInfoDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AddressInfoDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/dialog/balancedialog.cpp b/src/dialog/BalanceDialog.cpp similarity index 70% rename from src/dialog/balancedialog.cpp rename to src/dialog/BalanceDialog.cpp index 0d13121..7c49000 100644 --- a/src/dialog/balancedialog.cpp +++ b/src/dialog/BalanceDialog.cpp @@ -1,10 +1,11 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "balancedialog.h" -#include "ui_balancedialog.h" +#include "BalanceDialog.h" +#include "ui_BalanceDialog.h" #include "libwalletqt/WalletManager.h" +#include "model/ModelUtils.h" BalanceDialog::BalanceDialog(QWidget *parent, Wallet *wallet) : QDialog(parent) @@ -16,12 +17,15 @@ BalanceDialog::BalanceDialog(QWidget *parent, Wallet *wallet) "This will take 20 minutes on average."); ui->label_unconfirmed->setText(WalletManager::displayAmount(wallet->balance() - wallet->unlockedBalance())); + ui->label_unconfirmed->setFont(ModelUtils::getMonospaceFont()); + ui->label_spendable->setText(WalletManager::displayAmount(wallet->unlockedBalance())); + ui->label_spendable->setFont(ModelUtils::getMonospaceFont()); + ui->label_total->setText(WalletManager::displayAmount(wallet->balance())); + ui->label_total->setFont(ModelUtils::getMonospaceFont()); this->adjustSize(); } -BalanceDialog::~BalanceDialog() { - delete ui; -} +BalanceDialog::~BalanceDialog() = default; diff --git a/src/dialog/balancedialog.h b/src/dialog/BalanceDialog.h similarity index 91% rename from src/dialog/balancedialog.h rename to src/dialog/BalanceDialog.h index 3812be0..1880ae9 100644 --- a/src/dialog/balancedialog.h +++ b/src/dialog/BalanceDialog.h @@ -4,10 +4,10 @@ #ifndef FEATHER_BALANCEDIALOG_H #define FEATHER_BALANCEDIALOG_H -#include "libwalletqt/Wallet.h" - #include +#include "libwalletqt/Wallet.h" + namespace Ui { class BalanceDialog; } @@ -21,7 +21,7 @@ public: ~BalanceDialog() override; private: - Ui::BalanceDialog *ui; + QScopedPointer ui; }; #endif //FEATHER_BALANCEDIALOG_H diff --git a/src/dialog/balancedialog.ui b/src/dialog/BalanceDialog.ui similarity index 100% rename from src/dialog/balancedialog.ui rename to src/dialog/BalanceDialog.ui diff --git a/src/dialog/CMakeLists.txt b/src/dialog/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/dialog/CalcConfigDialog.cpp b/src/dialog/CalcConfigDialog.cpp index 319d376..caafb94 100644 --- a/src/dialog/CalcConfigDialog.cpp +++ b/src/dialog/CalcConfigDialog.cpp @@ -3,6 +3,7 @@ #include "CalcConfigDialog.h" #include "ui_CalcConfigDialog.h" + #include "AppData.h" #include "utils/config.h" @@ -97,6 +98,4 @@ void CalcConfigDialog::fillListWidgets() { setChecked(ui->list_fiat, checkedFiatCurrencies); } -CalcConfigDialog::~CalcConfigDialog() { - delete ui; -} +CalcConfigDialog::~CalcConfigDialog() = default; \ No newline at end of file diff --git a/src/dialog/CalcConfigDialog.h b/src/dialog/CalcConfigDialog.h index aceed43..37f7a2d 100644 --- a/src/dialog/CalcConfigDialog.h +++ b/src/dialog/CalcConfigDialog.h @@ -32,7 +32,7 @@ private: void fillListWidgets(); QListWidget* getVisibleListWidget(); - Ui::CalcConfigDialog *ui; + QScopedPointer ui; }; diff --git a/src/dialog/contactsdialog.cpp b/src/dialog/ContactsDialog.cpp similarity index 80% rename from src/dialog/contactsdialog.cpp rename to src/dialog/ContactsDialog.cpp index 455a265..439208d 100644 --- a/src/dialog/contactsdialog.cpp +++ b/src/dialog/ContactsDialog.cpp @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "ui_contactsdialog.h" -#include "contactsdialog.h" +#include "ui_ContactsDialog.h" +#include "ContactsDialog.h" ContactsDialog::ContactsDialog(QWidget *parent, const QString &address, const QString &name) - : QDialog(parent) - , ui(new Ui::ContactsDialog) + : QDialog(parent) + , ui(new Ui::ContactsDialog) { ui->setupUi(this); setMinimumWidth(400); @@ -25,15 +25,12 @@ ContactsDialog::ContactsDialog(QWidget *parent, const QString &address, const QS this->adjustSize(); } -ContactsDialog::~ContactsDialog() -{ - delete ui; -} - QString ContactsDialog::getAddress() { return m_address; } QString ContactsDialog::getName() { return m_name; -} \ No newline at end of file +} + +ContactsDialog::~ContactsDialog() = default; \ No newline at end of file diff --git a/src/dialog/contactsdialog.h b/src/dialog/ContactsDialog.h similarity index 92% rename from src/dialog/contactsdialog.h rename to src/dialog/ContactsDialog.h index f94bf34..03b81c4 100644 --- a/src/dialog/contactsdialog.h +++ b/src/dialog/ContactsDialog.h @@ -22,7 +22,7 @@ public: QString getName(); private: - Ui::ContactsDialog *ui; + QScopedPointer ui; QString m_address; QString m_name; diff --git a/src/dialog/contactsdialog.ui b/src/dialog/ContactsDialog.ui similarity index 100% rename from src/dialog/contactsdialog.ui rename to src/dialog/ContactsDialog.ui diff --git a/src/dialog/debuginfodialog.cpp b/src/dialog/DebugInfoDialog.cpp similarity index 98% rename from src/dialog/debuginfodialog.cpp rename to src/dialog/DebugInfoDialog.cpp index f6371ad..bbc1b02 100644 --- a/src/dialog/debuginfodialog.cpp +++ b/src/dialog/DebugInfoDialog.cpp @@ -1,13 +1,14 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "debuginfodialog.h" -#include "ui_debuginfodialog.h" +#include "DebugInfoDialog.h" +#include "ui_DebugInfoDialog.h" + #include "config-feather.h" -#include "utils/WebsocketClient.h" -#include "utils/TorManager.h" -#include "utils/WebsocketNotifier.h" #include "utils/os/tails.h" +#include "utils/TorManager.h" +#include "utils/WebsocketClient.h" +#include "utils/WebsocketNotifier.h" DebugInfoDialog::DebugInfoDialog(QSharedPointer ctx, QWidget *parent) : QDialog(parent) @@ -146,6 +147,4 @@ void DebugInfoDialog::copyToClipboad() { Utils::copyToClipboard(text); } -DebugInfoDialog::~DebugInfoDialog() { - delete ui; -} +DebugInfoDialog::~DebugInfoDialog() = default; \ No newline at end of file diff --git a/src/dialog/debuginfodialog.h b/src/dialog/DebugInfoDialog.h similarity index 93% rename from src/dialog/debuginfodialog.h rename to src/dialog/DebugInfoDialog.h index 80a5391..d2fbc83 100644 --- a/src/dialog/debuginfodialog.h +++ b/src/dialog/DebugInfoDialog.h @@ -5,6 +5,7 @@ #define FEATHER_DEBUGINFODIALOG_H #include + #include "appcontext.h" #include "libwalletqt/Wallet.h" @@ -25,7 +26,7 @@ private: void copyToClipboad(); void updateInfo(); - Ui::DebugInfoDialog *ui; + QScopedPointer ui; QSharedPointer m_ctx; QTimer m_updateTimer; diff --git a/src/dialog/debuginfodialog.ui b/src/dialog/DebugInfoDialog.ui similarity index 100% rename from src/dialog/debuginfodialog.ui rename to src/dialog/DebugInfoDialog.ui diff --git a/src/dialog/InfoDialog.cpp b/src/dialog/InfoDialog.cpp index 9b791a9..976d96c 100644 --- a/src/dialog/InfoDialog.cpp +++ b/src/dialog/InfoDialog.cpp @@ -14,6 +14,4 @@ InfoDialog::InfoDialog(QWidget *parent, const QString &title, const QString &inf ui->info->setPlainText(infoData); } -InfoDialog::~InfoDialog() { - delete ui; -} \ No newline at end of file +InfoDialog::~InfoDialog() = default; \ No newline at end of file diff --git a/src/dialog/InfoDialog.h b/src/dialog/InfoDialog.h index 3858156..fa18b5b 100644 --- a/src/dialog/InfoDialog.h +++ b/src/dialog/InfoDialog.h @@ -19,7 +19,7 @@ public: ~InfoDialog() override; private: - Ui::InfoDialog *ui; + QScopedPointer ui; }; diff --git a/src/dialog/keysdialog.cpp b/src/dialog/KeysDialog.cpp similarity index 89% rename from src/dialog/keysdialog.cpp rename to src/dialog/KeysDialog.cpp index 5618481..7fe362d 100644 --- a/src/dialog/keysdialog.cpp +++ b/src/dialog/KeysDialog.cpp @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "keysdialog.h" -#include "ui_keysdialog.h" +#include "KeysDialog.h" +#include "ui_KeysDialog.h" KeysDialog::KeysDialog(QSharedPointer ctx, QWidget *parent) : QDialog(parent) @@ -22,7 +22,4 @@ KeysDialog::KeysDialog(QSharedPointer ctx, QWidget *parent) this->adjustSize(); } -KeysDialog::~KeysDialog() -{ - delete ui; -} +KeysDialog::~KeysDialog() = default; diff --git a/src/dialog/keysdialog.h b/src/dialog/KeysDialog.h similarity index 91% rename from src/dialog/keysdialog.h rename to src/dialog/KeysDialog.h index 84df75d..5684075 100644 --- a/src/dialog/keysdialog.h +++ b/src/dialog/KeysDialog.h @@ -5,6 +5,7 @@ #define FEATHER_KEYSDIALOG_H #include + #include "appcontext.h" namespace Ui { @@ -20,7 +21,7 @@ public: ~KeysDialog() override; private: - Ui::KeysDialog *ui; + QScopedPointer ui; }; diff --git a/src/dialog/keysdialog.ui b/src/dialog/KeysDialog.ui similarity index 100% rename from src/dialog/keysdialog.ui rename to src/dialog/KeysDialog.ui diff --git a/src/dialog/LocalMoneroInfoDialog.cpp b/src/dialog/LocalMoneroInfoDialog.cpp index 8cdfa03..d3df1a7 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) @@ -46,6 +46,4 @@ void LocalMoneroInfoDialog::onGoToOffer() { Utils::externalLinkWarning(this, offerUrl); } -LocalMoneroInfoDialog::~LocalMoneroInfoDialog() { - delete ui; -} \ No newline at end of file +LocalMoneroInfoDialog::~LocalMoneroInfoDialog() = default; \ No newline at end of file diff --git a/src/dialog/LocalMoneroInfoDialog.h b/src/dialog/LocalMoneroInfoDialog.h index 584f87a..e5f6cd1 100644 --- a/src/dialog/LocalMoneroInfoDialog.h +++ b/src/dialog/LocalMoneroInfoDialog.h @@ -6,6 +6,7 @@ #include #include + #include "model/LocalMoneroModel.h" namespace Ui { @@ -26,7 +27,7 @@ private slots: private: void setLabelText(QLabel *label, LocalMoneroModel::Column column); - Ui::LocalMoneroInfoDialog *ui; + QScopedPointer ui; LocalMoneroModel *m_model; int m_row; }; diff --git a/src/dialog/outputinfodialog.cpp b/src/dialog/OutputInfoDialog.cpp similarity index 90% rename from src/dialog/outputinfodialog.cpp rename to src/dialog/OutputInfoDialog.cpp index 26895bd..772d1fb 100644 --- a/src/dialog/outputinfodialog.cpp +++ b/src/dialog/OutputInfoDialog.cpp @@ -1,10 +1,11 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "outputinfodialog.h" -#include "ui_outputinfodialog.h" +#include "OutputInfoDialog.h" +#include "ui_OutputInfoDialog.h" + #include "model/ModelUtils.h" -#include "utils/utils.h" +#include "utils/Utils.h" OutputInfoDialog::OutputInfoDialog(CoinsInfo *cInfo, QWidget *parent) : QDialog(parent) @@ -37,6 +38,4 @@ OutputInfoDialog::OutputInfoDialog(CoinsInfo *cInfo, QWidget *parent) this->adjustSize(); } -OutputInfoDialog::~OutputInfoDialog() { - delete ui; -} +OutputInfoDialog::~OutputInfoDialog() = default; \ No newline at end of file diff --git a/src/dialog/outputinfodialog.h b/src/dialog/OutputInfoDialog.h similarity index 91% rename from src/dialog/outputinfodialog.h rename to src/dialog/OutputInfoDialog.h index a691ab6..18dd39c 100644 --- a/src/dialog/outputinfodialog.h +++ b/src/dialog/OutputInfoDialog.h @@ -5,6 +5,7 @@ #define FEATHER_OUTPUTINFODIALOG_H #include + #include "libwalletqt/Coins.h" #include "libwalletqt/CoinsInfo.h" @@ -21,7 +22,7 @@ public: ~OutputInfoDialog() override; private: - Ui::OutputInfoDialog *ui; + QScopedPointer ui; }; diff --git a/src/dialog/outputinfodialog.ui b/src/dialog/OutputInfoDialog.ui similarity index 100% rename from src/dialog/outputinfodialog.ui rename to src/dialog/OutputInfoDialog.ui diff --git a/src/dialog/outputsweepdialog.cpp b/src/dialog/OutputSweepDialog.cpp similarity index 86% rename from src/dialog/outputsweepdialog.cpp rename to src/dialog/OutputSweepDialog.cpp index f92c3a2..7c7d2fb 100644 --- a/src/dialog/outputsweepdialog.cpp +++ b/src/dialog/OutputSweepDialog.cpp @@ -1,18 +1,18 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "ui_outputsweepdialog.h" -#include "outputsweepdialog.h" +#include "OutputSweepDialog.h" +#include "ui_OutputSweepDialog.h" + #include "libwalletqt/WalletManager.h" -OutputSweepDialog::OutputSweepDialog(QWidget *parent, CoinsInfo* coin) +OutputSweepDialog::OutputSweepDialog(QWidget *parent, quint64 amount) : QDialog(parent) , ui(new Ui::OutputSweepDialog) + , m_amount(amount) { ui->setupUi(this); - m_amount = coin->amount(); - connect(ui->checkBox_churn, &QCheckBox::toggled, [&](bool toggled){ ui->lineEdit_address->setEnabled(!toggled); ui->lineEdit_address->setText(toggled ? "Primary address" : ""); @@ -40,11 +40,6 @@ OutputSweepDialog::OutputSweepDialog(QWidget *parent, CoinsInfo* coin) this->adjustSize(); } -OutputSweepDialog::~OutputSweepDialog() -{ - delete ui; -} - QString OutputSweepDialog::address() { return m_address; } @@ -55,4 +50,6 @@ bool OutputSweepDialog::churn() const { int OutputSweepDialog::outputs() const { return m_outputs; -} \ No newline at end of file +} + +OutputSweepDialog::~OutputSweepDialog() = default; \ No newline at end of file diff --git a/src/dialog/outputsweepdialog.h b/src/dialog/OutputSweepDialog.h similarity index 83% rename from src/dialog/outputsweepdialog.h rename to src/dialog/OutputSweepDialog.h index f91e65f..4b565fb 100644 --- a/src/dialog/outputsweepdialog.h +++ b/src/dialog/OutputSweepDialog.h @@ -5,6 +5,7 @@ #define FEATHER_OUTPUTSWEEPDIALOG_H #include + #include "libwalletqt/CoinsInfo.h" namespace Ui { @@ -16,7 +17,7 @@ class OutputSweepDialog : public QDialog Q_OBJECT public: - explicit OutputSweepDialog(QWidget *parent, CoinsInfo* coin); + explicit OutputSweepDialog(QWidget *parent, quint64 amount); ~OutputSweepDialog() override; QString address(); @@ -24,7 +25,7 @@ public: int outputs() const; private: - Ui::OutputSweepDialog *ui; + QScopedPointer ui; uint64_t m_amount; diff --git a/src/dialog/outputsweepdialog.ui b/src/dialog/OutputSweepDialog.ui similarity index 100% rename from src/dialog/outputsweepdialog.ui rename to src/dialog/OutputSweepDialog.ui diff --git a/src/dialog/passwordchangedialog.cpp b/src/dialog/PasswordChangeDialog.cpp similarity index 94% rename from src/dialog/passwordchangedialog.cpp rename to src/dialog/PasswordChangeDialog.cpp index 106e001..14c5e9d 100644 --- a/src/dialog/passwordchangedialog.cpp +++ b/src/dialog/PasswordChangeDialog.cpp @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "passwordchangedialog.h" -#include "ui_passwordchangedialog.h" +#include "PasswordChangeDialog.h" +#include "ui_PasswordChangeDialog.h" #include @@ -40,11 +40,6 @@ PasswordChangeDialog::PasswordChangeDialog(QWidget *parent, Wallet *wallet) this->adjustSize(); } -PasswordChangeDialog::~PasswordChangeDialog() -{ - delete ui; -} - void PasswordChangeDialog::passwordsMatch() { bool match = ui->lineEdit_newPassword->text() == ui->lineEdit_confirmPassword->text(); ui->btn_OK->setEnabled(match); @@ -69,4 +64,6 @@ void PasswordChangeDialog::setPassword() { else { QMessageBox::warning(this, "Error", QString("Error: %1").arg(m_wallet->errorString())); } -} \ No newline at end of file +} + +PasswordChangeDialog::~PasswordChangeDialog() = default; \ No newline at end of file diff --git a/src/dialog/passwordchangedialog.h b/src/dialog/PasswordChangeDialog.h similarity index 91% rename from src/dialog/passwordchangedialog.h rename to src/dialog/PasswordChangeDialog.h index 43debbb..7fc91e0 100644 --- a/src/dialog/passwordchangedialog.h +++ b/src/dialog/PasswordChangeDialog.h @@ -5,6 +5,7 @@ #define FEATHER_PASSWORDCHANGEDIALOG_H #include + #include "libwalletqt/Wallet.h" namespace Ui { @@ -20,11 +21,11 @@ public: ~PasswordChangeDialog() override; private: - Ui::PasswordChangeDialog *ui; - Wallet *m_wallet; - void passwordsMatch(); void setPassword(); + + QScopedPointer ui; + Wallet *m_wallet; }; #endif //FEATHER_PASSWORDCHANGEDIALOG_H diff --git a/src/dialog/passwordchangedialog.ui b/src/dialog/PasswordChangeDialog.ui similarity index 100% rename from src/dialog/passwordchangedialog.ui rename to src/dialog/PasswordChangeDialog.ui diff --git a/src/dialog/passworddialog.cpp b/src/dialog/PasswordDialog.cpp similarity index 85% rename from src/dialog/passworddialog.cpp rename to src/dialog/PasswordDialog.cpp index 284c47f..d4e0e4a 100644 --- a/src/dialog/passworddialog.cpp +++ b/src/dialog/PasswordDialog.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "passworddialog.h" -#include "ui_passworddialog.h" +#include "PasswordDialog.h" +#include "ui_PasswordDialog.h" + #include "utils/Icons.h" PasswordDialog::PasswordDialog(const QString &walletName, bool incorrectPassword, QWidget *parent) @@ -22,7 +23,4 @@ PasswordDialog::PasswordDialog(const QString &walletName, bool incorrectPassword this->adjustSize(); } -PasswordDialog::~PasswordDialog() -{ - delete ui; -} +PasswordDialog::~PasswordDialog() = default; \ No newline at end of file diff --git a/src/dialog/passworddialog.h b/src/dialog/PasswordDialog.h similarity index 91% rename from src/dialog/passworddialog.h rename to src/dialog/PasswordDialog.h index a204cea..544331f 100644 --- a/src/dialog/passworddialog.h +++ b/src/dialog/PasswordDialog.h @@ -21,7 +21,7 @@ public: QString password = ""; private: - Ui::PasswordDialog *ui; + QScopedPointer ui; }; #endif //FEATHER_PASSWORDDIALOG_H diff --git a/src/dialog/passworddialog.ui b/src/dialog/PasswordDialog.ui similarity index 100% rename from src/dialog/passworddialog.ui rename to src/dialog/PasswordDialog.ui diff --git a/src/dialog/qrcodedialog.cpp b/src/dialog/QrCodeDialog.cpp similarity index 69% rename from src/dialog/qrcodedialog.cpp rename to src/dialog/QrCodeDialog.cpp index a7dee06..4b1c7cf 100644 --- a/src/dialog/qrcodedialog.cpp +++ b/src/dialog/QrCodeDialog.cpp @@ -1,22 +1,23 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "qrcodedialog.h" -#include "ui_qrcodedialog.h" +#include "QrCodeDialog.h" +#include "ui_QrCodeDialog.h" #include #include #include -QrCodeDialog::QrCodeDialog(QWidget *parent, const QrCode &qrCode, const QString &title) +QrCodeDialog::QrCodeDialog(QWidget *parent, QrCode *qrCode, const QString &title) : QDialog(parent) , ui(new Ui::QrCodeDialog) { ui->setupUi(this); this->setWindowTitle(title); - m_pixmap = qrCode.toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio); - ui->QrCode->setPixmap(m_pixmap); + ui->qrWidget->setQrCode(qrCode); + + m_pixmap = qrCode->toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio); connect(ui->btn_CopyImage, &QPushButton::clicked, this, &QrCodeDialog::copyImage); connect(ui->btn_Save, &QPushButton::clicked, this, &QrCodeDialog::saveImage); @@ -24,17 +25,7 @@ QrCodeDialog::QrCodeDialog(QWidget *parent, const QrCode &qrCode, const QString accept(); }); - this->adjustSize(); -} - -QrCodeDialog::~QrCodeDialog() -{ - delete ui; -} - -void QrCodeDialog::setQrCode(const QrCode &qrCode) { - m_pixmap = qrCode.toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio); - ui->QrCode->setPixmap(m_pixmap); + this->resize(500, 500); } void QrCodeDialog::copyImage() { @@ -52,4 +43,6 @@ void QrCodeDialog::saveImage() { file.open(QIODevice::WriteOnly); m_pixmap.save(&file, "PNG"); QMessageBox::information(this, "Information", "QR code saved to file"); -} \ No newline at end of file +} + +QrCodeDialog::~QrCodeDialog() = default; \ No newline at end of file diff --git a/src/dialog/qrcodedialog.h b/src/dialog/QrCodeDialog.h similarity index 72% rename from src/dialog/qrcodedialog.h rename to src/dialog/QrCodeDialog.h index 83c9715..706b120 100644 --- a/src/dialog/qrcodedialog.h +++ b/src/dialog/QrCodeDialog.h @@ -3,9 +3,11 @@ #ifndef FEATHER_QRCODEDIALOG_H #define FEATHER_QRCODEDIALOG_H + #include #include "qrcode/QrCode.h" +#include "widgets/QrCodeWidget.h" namespace Ui { class QrCodeDialog; @@ -16,17 +18,15 @@ class QrCodeDialog : public QDialog Q_OBJECT public: - explicit QrCodeDialog(QWidget *parent, const QrCode &qrCode, const QString &title = "Qr Code"); + explicit QrCodeDialog(QWidget *parent, QrCode *qrCode, const QString &title = "Qr Code"); ~QrCodeDialog() override; - void setQrCode(const QrCode &qrCode); private: void copyImage(); void saveImage(); - Ui::QrCodeDialog *ui; + QScopedPointer ui; QPixmap m_pixmap; }; - #endif //FEATHER_QRCODEDIALOG_H diff --git a/src/dialog/qrcodedialog.ui b/src/dialog/QrCodeDialog.ui similarity index 82% rename from src/dialog/qrcodedialog.ui rename to src/dialog/QrCodeDialog.ui index c160dc9..50a4790 100644 --- a/src/dialog/qrcodedialog.ui +++ b/src/dialog/QrCodeDialog.ui @@ -6,8 +6,8 @@ 0 0 - 522 - 562 + 520 + 446 @@ -15,22 +15,19 @@ - + - + 0 0 - 500 - 500 + 150 + 150 - - QrCode - @@ -88,6 +85,14 @@ + + + QrCodeWidget + QWidget +
widgets/QrCodeWidget.h
+ 1 +
+
diff --git a/src/dialog/RestoreHeightDialog.h b/src/dialog/RestoreHeightDialog.h index 16159c9..88cad2c 100644 --- a/src/dialog/RestoreHeightDialog.h +++ b/src/dialog/RestoreHeightDialog.h @@ -5,6 +5,7 @@ #define FEATHER_RESTOREHEIGHTDIALOG_H #include + #include "widgets/RestoreHeightWidget.h" class RestoreHeightDialog : public QDialog diff --git a/src/dialog/seeddialog.cpp b/src/dialog/SeedDialog.cpp similarity index 96% rename from src/dialog/seeddialog.cpp rename to src/dialog/SeedDialog.cpp index ca8405d..9153b2a 100644 --- a/src/dialog/seeddialog.cpp +++ b/src/dialog/SeedDialog.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "ui_seeddialog.h" -#include "seeddialog.h" +#include "SeedDialog.h" +#include "ui_SeedDialog.h" + #include "constants.h" SeedDialog::SeedDialog(QSharedPointer ctx, QWidget *parent) @@ -62,7 +63,4 @@ void SeedDialog::setSeed(const QString &seed) { "").arg(words)); } -SeedDialog::~SeedDialog() -{ - delete ui; -} \ No newline at end of file +SeedDialog::~SeedDialog() = default; \ No newline at end of file diff --git a/src/dialog/seeddialog.h b/src/dialog/SeedDialog.h similarity index 92% rename from src/dialog/seeddialog.h rename to src/dialog/SeedDialog.h index b1e211b..a0398bd 100644 --- a/src/dialog/seeddialog.h +++ b/src/dialog/SeedDialog.h @@ -5,6 +5,7 @@ #define FEATHER_SEEDDIALOG_H #include + #include "appcontext.h" namespace Ui { @@ -22,7 +23,7 @@ public: private: void setSeed(const QString &seed); - Ui::SeedDialog *ui; + QScopedPointer ui; QSharedPointer m_ctx; }; diff --git a/src/dialog/seeddialog.ui b/src/dialog/SeedDialog.ui similarity index 100% rename from src/dialog/seeddialog.ui rename to src/dialog/SeedDialog.ui diff --git a/src/dialog/signverifydialog.cpp b/src/dialog/SignVerifyDialog.cpp similarity index 83% rename from src/dialog/signverifydialog.cpp rename to src/dialog/SignVerifyDialog.cpp index 283c33b..490aa56 100644 --- a/src/dialog/signverifydialog.cpp +++ b/src/dialog/SignVerifyDialog.cpp @@ -1,12 +1,13 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "signverifydialog.h" -#include "ui_signverifydialog.h" -#include "utils/utils.h" +#include "SignVerifyDialog.h" +#include "ui_SignVerifyDialog.h" #include +#include "utils/Utils.h" + SignVerifyDialog::SignVerifyDialog(Wallet *wallet, QWidget *parent) : QDialog(parent) , ui(new Ui::SignVerifyDialog) @@ -25,6 +26,12 @@ SignVerifyDialog::SignVerifyDialog(Wallet *wallet, QWidget *parent) ui->address->setText(m_wallet->address(0, 0)); ui->address->setCursorPosition(0); + if (m_wallet->isHwBacked()) { + // We don't have the secret spend key to sign messages + ui->btn_Sign->setEnabled(false); + ui->btn_Sign->setToolTip("Message signing is not supported on this hardware device."); + } + ui->btn_Copy->setVisible(false); } @@ -52,7 +59,4 @@ void SignVerifyDialog::copyToClipboard() { Utils::copyToClipboard(sig.join("\n")); } -SignVerifyDialog::~SignVerifyDialog() -{ - delete ui; -} \ No newline at end of file +SignVerifyDialog::~SignVerifyDialog() = default; \ No newline at end of file diff --git a/src/dialog/signverifydialog.h b/src/dialog/SignVerifyDialog.h similarity index 92% rename from src/dialog/signverifydialog.h rename to src/dialog/SignVerifyDialog.h index 0436cea..d7819f6 100644 --- a/src/dialog/signverifydialog.h +++ b/src/dialog/SignVerifyDialog.h @@ -5,6 +5,7 @@ #define FEATHER_SIGNVERIFYDIALOG_H #include + #include "libwalletqt/Wallet.h" namespace Ui { @@ -19,14 +20,14 @@ public: explicit SignVerifyDialog(Wallet *wallet, QWidget *parent = nullptr); ~SignVerifyDialog() override; -private: - Ui::SignVerifyDialog *ui; - Wallet *m_wallet; - private slots: void signMessage(); void verifyMessage(); void copyToClipboard(); + +private: + QScopedPointer ui; + Wallet *m_wallet; }; diff --git a/src/dialog/signverifydialog.ui b/src/dialog/SignVerifyDialog.ui similarity index 100% rename from src/dialog/signverifydialog.ui rename to src/dialog/SignVerifyDialog.ui diff --git a/src/dialog/splashdialog.cpp b/src/dialog/SplashDialog.cpp similarity index 87% rename from src/dialog/splashdialog.cpp rename to src/dialog/SplashDialog.cpp index 06b2481..ba0bc5c 100644 --- a/src/dialog/splashdialog.cpp +++ b/src/dialog/SplashDialog.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "splashdialog.h" -#include "ui_splashdialog.h" +#include "SplashDialog.h" +#include "ui_SplashDialog.h" + #include "utils/Icons.h" SplashDialog::SplashDialog(QWidget *parent) @@ -29,6 +30,4 @@ void SplashDialog::setIcon(const QPixmap &icon) { ui->icon->setPixmap(icon.scaledToWidth(32, Qt::SmoothTransformation)); } -SplashDialog::~SplashDialog() { - delete ui; -} +SplashDialog::~SplashDialog() = default; \ No newline at end of file diff --git a/src/dialog/splashdialog.h b/src/dialog/SplashDialog.h similarity index 92% rename from src/dialog/splashdialog.h rename to src/dialog/SplashDialog.h index c4acf90..7341b7d 100644 --- a/src/dialog/splashdialog.h +++ b/src/dialog/SplashDialog.h @@ -22,7 +22,7 @@ public: void setIcon(const QPixmap &icon); private: - Ui::SplashDialog *ui; + QScopedPointer ui; }; #endif //FEATHER_SPLASHDIALOG_H diff --git a/src/dialog/splashdialog.ui b/src/dialog/SplashDialog.ui similarity index 100% rename from src/dialog/splashdialog.ui rename to src/dialog/SplashDialog.ui diff --git a/src/dialog/torinfodialog.cpp b/src/dialog/TorInfoDialog.cpp similarity index 88% rename from src/dialog/torinfodialog.cpp rename to src/dialog/TorInfoDialog.cpp index 59ebbf5..adaed2d 100644 --- a/src/dialog/torinfodialog.cpp +++ b/src/dialog/TorInfoDialog.cpp @@ -1,17 +1,19 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "torinfodialog.h" -#include "ui_torinfodialog.h" +#include "TorInfoDialog.h" +#include "ui_TorInfoDialog.h" -#include #include -#include #include +#include +#include +#include -#include "utils/TorManager.h" -#include "utils/os/tails.h" +#include "utils/ColorScheme.h" #include "utils/Icons.h" +#include "utils/os/tails.h" +#include "utils/TorManager.h" TorInfoDialog::TorInfoDialog(QSharedPointer ctx, QWidget *parent) : QDialog(parent) @@ -36,6 +38,9 @@ TorInfoDialog::TorInfoDialog(QSharedPointer ctx, QWidget *parent) initPrivacyLevel(); onConnectionStatusChanged(torManager()->torConnected); + auto *portValidator = new QRegularExpressionValidator{QRegularExpression("[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]")}; + ui->line_port->setValidator(portValidator); + connect(torManager(), &TorManager::connectionStateChanged, this, &TorInfoDialog::onConnectionStatusChanged); connect(torManager(), &TorManager::logsUpdated, this, &TorInfoDialog::onLogsUpdated); @@ -139,9 +144,10 @@ void TorInfoDialog::initPrivacyLevel() { ui->frame_notice->hide(); } - QPixmap iconNoTor(":/assets/images/securityLevelStandardWhite.png"); - QPixmap iconNoSync(":/assets/images/securityLevelSaferWhite.png"); - QPixmap iconAllTor(":/assets/images/securityLevelSafestWhite.png"); + bool dark = ColorScheme::darkScheme; + QPixmap iconNoTor(dark ? ":/assets/images/securityLevelStandardWhite.png" : ":/assets/images/securityLevelStandard.png"); + QPixmap iconNoSync(dark ? ":/assets/images/securityLevelSaferWhite.png" : ":/assets/images/securityLevelSafer.png"); + QPixmap iconAllTor(dark ? ":/assets/images/securityLevelSafestWhite.png" : ":/assets/images/securityLevelSafest.png"); ui->icon_noTor->setPixmap(iconNoTor.scaledToHeight(16, Qt::SmoothTransformation)); ui->icon_noSync->setPixmap(iconNoSync.scaledToHeight(16, Qt::SmoothTransformation)); ui->icon_allTor->setPixmap(iconAllTor.scaledToHeight(16, Qt::SmoothTransformation)); @@ -165,6 +171,4 @@ void TorInfoDialog::onShowInitSyncConfigDialog() { } } -TorInfoDialog::~TorInfoDialog() { - delete ui; -} +TorInfoDialog::~TorInfoDialog() = default; \ No newline at end of file diff --git a/src/dialog/torinfodialog.h b/src/dialog/TorInfoDialog.h similarity index 93% rename from src/dialog/torinfodialog.h rename to src/dialog/TorInfoDialog.h index 3b3847e..0b5e25d 100644 --- a/src/dialog/torinfodialog.h +++ b/src/dialog/TorInfoDialog.h @@ -5,7 +5,6 @@ #define FEATHER_TORINFODIALOG_H #include -#include #include "appcontext.h" @@ -38,7 +37,7 @@ private: void initConnectionSettings(); void initPrivacyLevel(); - Ui::TorInfoDialog *ui; + QScopedPointer ui; QSharedPointer m_ctx; }; diff --git a/src/dialog/torinfodialog.ui b/src/dialog/TorInfoDialog.ui similarity index 100% rename from src/dialog/torinfodialog.ui rename to src/dialog/TorInfoDialog.ui diff --git a/src/dialog/broadcasttxdialog.cpp b/src/dialog/TxBroadcastDialog.cpp similarity index 71% rename from src/dialog/broadcasttxdialog.cpp rename to src/dialog/TxBroadcastDialog.cpp index 42b5d59..58223db 100644 --- a/src/dialog/broadcasttxdialog.cpp +++ b/src/dialog/TxBroadcastDialog.cpp @@ -1,15 +1,16 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "broadcasttxdialog.h" -#include "ui_broadcasttxdialog.h" -#include "utils/NetworkManager.h" +#include "TxBroadcastDialog.h" +#include "ui_TxBroadcastDialog.h" #include -BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, QSharedPointer ctx, const QString &transactionHex) +#include "utils/NetworkManager.h" + +TxBroadcastDialog::TxBroadcastDialog(QWidget *parent, QSharedPointer ctx, const QString &transactionHex) : QDialog(parent) - , ui(new Ui::BroadcastTxDialog) + , ui(new Ui::TxBroadcastDialog) , m_ctx(std::move(ctx)) { ui->setupUi(this); @@ -17,10 +18,10 @@ BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, QSharedPointer auto node = m_ctx->nodes->connection(); m_rpc = new DaemonRpc(this, getNetworkTor(), node.toAddress()); - connect(ui->btn_Broadcast, &QPushButton::clicked, this, &BroadcastTxDialog::broadcastTx); - connect(ui->btn_Close, &QPushButton::clicked, this, &BroadcastTxDialog::reject); + connect(ui->btn_Broadcast, &QPushButton::clicked, this, &TxBroadcastDialog::broadcastTx); + connect(ui->btn_Close, &QPushButton::clicked, this, &TxBroadcastDialog::reject); - connect(m_rpc, &DaemonRpc::ApiResponse, this, &BroadcastTxDialog::onApiResponse); + connect(m_rpc, &DaemonRpc::ApiResponse, this, &TxBroadcastDialog::onApiResponse); if (!transactionHex.isEmpty()) { ui->transaction->setPlainText(transactionHex); @@ -29,7 +30,7 @@ BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, QSharedPointer this->adjustSize(); } -void BroadcastTxDialog::broadcastTx() { +void TxBroadcastDialog::broadcastTx() { QString tx = ui->transaction->toPlainText(); FeatherNode node = ui->radio_useCustom->isChecked() ? FeatherNode(ui->customNode->text()) : m_ctx->nodes->connection(); @@ -44,7 +45,7 @@ void BroadcastTxDialog::broadcastTx() { m_rpc->sendRawTransaction(tx); } -void BroadcastTxDialog::onApiResponse(const DaemonRpc::DaemonResponse &resp) { +void TxBroadcastDialog::onApiResponse(const DaemonRpc::DaemonResponse &resp) { if (!resp.ok) { QMessageBox::warning(this, "Transaction broadcast", resp.status); return; @@ -56,6 +57,4 @@ void BroadcastTxDialog::onApiResponse(const DaemonRpc::DaemonResponse &resp) { } } -BroadcastTxDialog::~BroadcastTxDialog() { - delete ui; -} +TxBroadcastDialog::~TxBroadcastDialog() = default; diff --git a/src/dialog/broadcasttxdialog.h b/src/dialog/TxBroadcastDialog.h similarity index 56% rename from src/dialog/broadcasttxdialog.h rename to src/dialog/TxBroadcastDialog.h index 0f32c7d..944d463 100644 --- a/src/dialog/broadcasttxdialog.h +++ b/src/dialog/TxBroadcastDialog.h @@ -1,35 +1,35 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef FEATHER_BROADCASTTXDIALOG_H -#define FEATHER_BROADCASTTXDIALOG_H +#ifndef FEATHER_TXBROADCASTDIALOG_H +#define FEATHER_TXBROADCASTDIALOG_H #include + #include "appcontext.h" #include "utils/daemonrpc.h" namespace Ui { - class BroadcastTxDialog; + class TxBroadcastDialog; } -class BroadcastTxDialog : public QDialog +class TxBroadcastDialog : public QDialog { Q_OBJECT public: - explicit BroadcastTxDialog(QWidget *parent, QSharedPointer ctx, const QString &transactionHex = ""); - ~BroadcastTxDialog() override; + explicit TxBroadcastDialog(QWidget *parent, QSharedPointer ctx, const QString &transactionHex = ""); + ~TxBroadcastDialog() override; private slots: void broadcastTx(); void onApiResponse(const DaemonRpc::DaemonResponse &resp); private: - Ui::BroadcastTxDialog *ui; + QScopedPointer ui; QSharedPointer m_ctx; - UtilsNetworking *m_network; DaemonRpc *m_rpc; }; -#endif //FEATHER_BROADCASTTXDIALOG_H +#endif //FEATHER_TXBROADCASTDIALOG_H diff --git a/src/dialog/broadcasttxdialog.ui b/src/dialog/TxBroadcastDialog.ui similarity index 96% rename from src/dialog/broadcasttxdialog.ui rename to src/dialog/TxBroadcastDialog.ui index 0be33f2..65341f1 100644 --- a/src/dialog/broadcasttxdialog.ui +++ b/src/dialog/TxBroadcastDialog.ui @@ -1,7 +1,7 @@ - BroadcastTxDialog - + TxBroadcastDialog + 0 diff --git a/src/dialog/txconfadvdialog.cpp b/src/dialog/TxConfAdvDialog.cpp similarity index 96% rename from src/dialog/txconfadvdialog.cpp rename to src/dialog/TxConfAdvDialog.cpp index 2f27c55..fda8f53 100644 --- a/src/dialog/txconfadvdialog.cpp +++ b/src/dialog/TxConfAdvDialog.cpp @@ -1,17 +1,18 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "txconfadvdialog.h" -#include "ui_txconfadvdialog.h" -#include "qrcode/QrCode.h" -#include "dialog/qrcodedialog.h" -#include "libwalletqt/Transfer.h" -#include "libwalletqt/Input.h" -#include "model/ModelUtils.h" +#include "TxConfAdvDialog.h" +#include "ui_TxConfAdvDialog.h" #include #include +#include "dialog/QrCodeDialog.h" +#include "libwalletqt/Input.h" +#include "libwalletqt/Transfer.h" +#include "model/ModelUtils.h" +#include "qrcode/QrCode.h" + TxConfAdvDialog::TxConfAdvDialog(QSharedPointer ctx, const QString &description, QWidget *parent) : QDialog(parent) , ui(new Ui::TxConfAdvDialog) @@ -109,7 +110,7 @@ void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) { auto address = o->address(); auto amount = WalletManager::displayAmount(o->amount()); auto index = m_ctx->wallet->subaddressIndex(address); - cursor.insertText(address, Utils::addressTextFormat(index)); + cursor.insertText(address, Utils::addressTextFormat(index, o->amount())); cursor.insertText(QString(" %1").arg(amount), QTextCharFormat()); cursor.insertBlock(); } @@ -153,9 +154,8 @@ void TxConfAdvDialog::unsignedQrCode() { } QrCode qr(m_tx->unsignedTxToBin(), QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::LOW); - auto *dialog = new QrCodeDialog(this, qr, "Unsigned Transaction"); - dialog->exec(); - dialog->deleteLater(); + QrCodeDialog dialog{this, &qr, "Unsigned Transaction"}; + dialog.exec(); } void TxConfAdvDialog::unsignedCopy() { @@ -183,6 +183,4 @@ void TxConfAdvDialog::closeDialog() { QDialog::reject(); } -TxConfAdvDialog::~TxConfAdvDialog() { - delete ui; -} +TxConfAdvDialog::~TxConfAdvDialog() = default; \ No newline at end of file diff --git a/src/dialog/txconfadvdialog.h b/src/dialog/TxConfAdvDialog.h similarity index 95% rename from src/dialog/txconfadvdialog.h rename to src/dialog/TxConfAdvDialog.h index e59a766..922abfb 100644 --- a/src/dialog/txconfadvdialog.h +++ b/src/dialog/TxConfAdvDialog.h @@ -5,13 +5,12 @@ #define FEATHER_TXCONFADVDIALOG_H #include -#include -#include #include +#include #include -#include "libwalletqt/PendingTransaction.h" #include "appcontext.h" +#include "libwalletqt/PendingTransaction.h" namespace Ui { class TxConfAdvDialog; @@ -42,7 +41,7 @@ private: void signedQrCode(); void signedSaveFile(); - Ui::TxConfAdvDialog *ui; + QScopedPointer ui; QSharedPointer m_ctx; PendingTransaction *m_tx = nullptr; UnsignedTransaction *m_utx = nullptr; diff --git a/src/dialog/txconfadvdialog.ui b/src/dialog/TxConfAdvDialog.ui similarity index 100% rename from src/dialog/txconfadvdialog.ui rename to src/dialog/TxConfAdvDialog.ui diff --git a/src/dialog/txconfdialog.cpp b/src/dialog/TxConfDialog.cpp similarity index 93% rename from src/dialog/txconfdialog.cpp rename to src/dialog/TxConfDialog.cpp index dbf9f23..2d1c4b2 100644 --- a/src/dialog/txconfdialog.cpp +++ b/src/dialog/TxConfDialog.cpp @@ -1,16 +1,17 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "txconfdialog.h" -#include "ui_txconfdialog.h" -#include "model/ModelUtils.h" -#include "txconfadvdialog.h" -#include "constants.h" -#include "utils/AppData.h" -#include "utils/ColorScheme.h" +#include "TxConfDialog.h" +#include "ui_TxConfDialog.h" #include +#include "constants.h" +#include "model/ModelUtils.h" +#include "TxConfAdvDialog.h" +#include "utils/AppData.h" +#include "utils/ColorScheme.h" + TxConfDialog::TxConfDialog(QSharedPointer ctx, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent) : QDialog(parent) , ui(new Ui::TxConfDialog) @@ -76,7 +77,7 @@ TxConfDialog::TxConfDialog(QSharedPointer ctx, PendingTransaction *t connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::setShowAdvanced); - m_ctx->txCache[tx->txid()[0]] = tx->signedTxToHex(0); + m_ctx->addCacheTransaction(tx->txid()[0], tx->signedTxToHex(0)); // Todo: Iterate over all txs this->adjustSize(); } @@ -85,6 +86,4 @@ void TxConfDialog::setShowAdvanced() { QDialog::reject(); } -TxConfDialog::~TxConfDialog() { - delete ui; -} +TxConfDialog::~TxConfDialog() = default; \ No newline at end of file diff --git a/src/dialog/txconfdialog.h b/src/dialog/TxConfDialog.h similarity index 94% rename from src/dialog/txconfdialog.h rename to src/dialog/TxConfDialog.h index 1057df8..4d2ab87 100644 --- a/src/dialog/txconfdialog.h +++ b/src/dialog/TxConfDialog.h @@ -5,9 +5,10 @@ #define FEATHER_TXCONFDIALOG_H #include + +#include "appcontext.h" #include "libwalletqt/PendingTransaction.h" #include "libwalletqt/WalletManager.h" -#include "appcontext.h" namespace Ui { class TxConfDialog; @@ -26,7 +27,7 @@ public: private: void setShowAdvanced(); - Ui::TxConfDialog *ui; + QScopedPointer ui; QSharedPointer m_ctx; PendingTransaction *m_tx; QString m_address; diff --git a/src/dialog/txconfdialog.ui b/src/dialog/TxConfDialog.ui similarity index 100% rename from src/dialog/txconfdialog.ui rename to src/dialog/TxConfDialog.ui diff --git a/src/dialog/tximportdialog.cpp b/src/dialog/TxImportDialog.cpp similarity index 96% rename from src/dialog/tximportdialog.cpp rename to src/dialog/TxImportDialog.cpp index a256248..60cf6ab 100644 --- a/src/dialog/tximportdialog.cpp +++ b/src/dialog/TxImportDialog.cpp @@ -1,12 +1,13 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "tximportdialog.h" -#include "ui_tximportdialog.h" -#include "utils/NetworkManager.h" +#include "TxImportDialog.h" +#include "ui_TxImportDialog.h" #include +#include "utils/NetworkManager.h" + TxImportDialog::TxImportDialog(QWidget *parent, QSharedPointer ctx) : QDialog(parent) , ui(new Ui::TxImportDialog) @@ -100,6 +101,4 @@ void TxImportDialog::onImport() { m_ctx->refreshModels(); } -TxImportDialog::~TxImportDialog() { - delete ui; -} +TxImportDialog::~TxImportDialog() = default; diff --git a/src/dialog/tximportdialog.h b/src/dialog/TxImportDialog.h similarity index 92% rename from src/dialog/tximportdialog.h rename to src/dialog/TxImportDialog.h index 6b6992c..4919252 100644 --- a/src/dialog/tximportdialog.h +++ b/src/dialog/TxImportDialog.h @@ -5,6 +5,7 @@ #define FEATHER_TXIMPORTDIALOG_H #include + #include "appcontext.h" #include "utils/daemonrpc.h" @@ -26,10 +27,9 @@ private slots: void onApiResponse(const DaemonRpc::DaemonResponse &resp); private: - Ui::TxImportDialog *ui; + QScopedPointer ui; QSharedPointer m_ctx; - UtilsNetworking *m_network; DaemonRpc *m_rpc; QTimer *m_loadTimer; diff --git a/src/dialog/tximportdialog.ui b/src/dialog/TxImportDialog.ui similarity index 100% rename from src/dialog/tximportdialog.ui rename to src/dialog/TxImportDialog.ui diff --git a/src/dialog/TxInfoDialog.cpp b/src/dialog/TxInfoDialog.cpp new file mode 100644 index 0000000..d873530 --- /dev/null +++ b/src/dialog/TxInfoDialog.cpp @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#include "TxInfoDialog.h" +#include "ui_TxInfoDialog.h" + +#include +#include + +#include "appcontext.h" +#include "config.h" +#include "libwalletqt/Coins.h" +#include "libwalletqt/CoinsInfo.h" +#include "libwalletqt/TransactionHistory.h" +#include "libwalletqt/Transfer.h" +#include "libwalletqt/WalletManager.h" +#include "model/ModelUtils.h" +#include "Utils.h" + +TxInfoDialog::TxInfoDialog(QSharedPointer ctx, TransactionInfo *txInfo, QWidget *parent) + : QDialog(parent) + , ui(new Ui::TxInfoDialog) + , m_ctx(std::move(ctx)) + , m_txInfo(txInfo) + , m_txProofDialog(new TxProofDialog(this, m_ctx, txInfo)) +{ + ui->setupUi(this); + + m_txid = txInfo->hash(); + ui->label_txid->setText(m_txid); + + connect(ui->btn_CopyTxKey, &QPushButton::pressed, this, &TxInfoDialog::copyTxKey); + connect(ui->btn_createTxProof, &QPushButton::pressed, this, &TxInfoDialog::createTxProof); + + connect(m_ctx->wallet, &Wallet::newBlock, this, &TxInfoDialog::updateData); + + this->setData(txInfo); + + if ((txInfo->isFailed() || txInfo->isPending()) && txInfo->direction() != TransactionInfo::Direction_In) { + connect(ui->btn_rebroadcastTx, &QPushButton::pressed, [this]{ + emit resendTranscation(m_txid); + }); + } else { + ui->btn_rebroadcastTx->hide(); + } + + if (txInfo->direction() == TransactionInfo::Direction_Out) { + // TODO: this will not properly represent coinjoin-like transactions. + QVector coins = m_ctx->wallet->coins()->coins_from_txid(m_txid); + QTextCursor c_i = ui->inputs->textCursor(); + QString inputs_str; + for (const auto &coin : coins) { + inputs_str += QString("%1 %2\n").arg(coin->pubKey(), coin->displayAmount()); + } + ui->inputs->setText(inputs_str); + ui->label_inputs->setText(QString("Inputs (%1)").arg(QString::number(coins.size()))); + this->adjustHeight(ui->inputs, coins.size()); + } else { + ui->frameInputs->hide(); + } + + QTextCursor cursor = ui->outputs->textCursor(); + + auto transfers = txInfo->transfers(); + if (!transfers.isEmpty()) { + for (const auto& transfer : transfers) { + auto address = transfer->address(); + auto amount = WalletManager::displayAmount(transfer->amount()); + auto index = m_ctx->wallet->subaddressIndex(address); + cursor.insertText(address, Utils::addressTextFormat(index, transfer->amount())); + cursor.insertText(QString(" %1").arg(amount), QTextCharFormat()); + cursor.insertBlock(); + } + ui->label_outputs->setText(QString("Destinations (%2)").arg(QString::number(transfers.size()))); + this->adjustHeight(ui->outputs, transfers.size()); + } else { + ui->frameOutputs->hide(); + } + + this->adjustSize(); +} + +void TxInfoDialog::adjustHeight(QTextEdit *textEdit, qreal docHeight) { + QCoreApplication::processEvents(); + + qreal lineHeight = QFontMetrics(ui->outputs->document()->defaultFont()).height(); + + int h = int(docHeight * (lineHeight + 2) + 11); + h = qMin(qMax(h, 100), 600); + textEdit->setMinimumHeight(h); + textEdit->setMaximumHeight(h); + textEdit->verticalScrollBar()->hide(); +} + +void TxInfoDialog::setData(TransactionInfo *tx) { + QString blockHeight = QString::number(tx->blockHeight()); + + if (tx->isFailed()) { + ui->label_status->setText("Status: Failed (node was unable to relay transaction)"); + } + if (blockHeight == "0") { + ui->label_status->setText("Status: Unconfirmed (in mempool)"); + } + else { + QString dateTimeFormat = QString("%1 %2").arg(config()->get(Config::dateFormat).toString(), config()->get(Config::timeFormat).toString()); + QString date = tx->timestamp().toString(dateTimeFormat); + QString statusText = QString("Status: Included in block %1 (%2 confirmations) on %3").arg(blockHeight, QString::number(tx->confirmations()), date); + ui->label_status->setText(statusText); + } + + + if (tx->confirmationsRequired() > tx->confirmations()) { + bool mandatoryLock = tx->confirmationsRequired() == 10; + QString confsRequired = QString::number(tx->confirmationsRequired() - tx->confirmations()); + ui->label_lock->setText(QString("Lock: Outputs become spendable in %1 blocks (%2)").arg(confsRequired, mandatoryLock ? "consensus rule" : "specified by sender")); + } else { + ui->label_lock->setText("Lock: Outputs are spendable"); + } + + QString direction = tx->direction() == TransactionInfo::Direction_In ? "received" : "sent"; + ui->label_amount->setText(QString("Amount %1: %2 XMR").arg(direction, tx->displayAmount())); + + QString fee; + if (tx->isCoinbase()) + fee = "Not applicable"; + else if (tx->direction() == TransactionInfo::Direction_In) + fee = "Paid by sender"; + else if (tx->fee().isEmpty()) + fee = "N/A"; + else + fee = QString("%1 XMR").arg(tx->fee()); + + ui->label_fee->setText(QString("Fee: %1").arg(fee)); + +} + +void TxInfoDialog::updateData() { + TransactionInfo *tx = m_ctx->wallet->history()->transaction(m_txid); + if (!tx) return; + this->setData(tx); +} + +void TxInfoDialog::copyTxKey() { + m_ctx->wallet->getTxKeyAsync(m_txid, [this](QVariantMap map){ + QString txKey = map.value("tx_key").toString(); + if (txKey.isEmpty()) { + QMessageBox::warning(this, "Unable to copy transaction key", "Transaction key unknown"); + } else { + Utils::copyToClipboard(txKey); + QMessageBox::information(this, "Transaction key copied", "Transaction key copied to clipboard."); + } + }); +} + +void TxInfoDialog::createTxProof() { + m_txProofDialog->show(); + m_txProofDialog->getTxKey(); +} + +TxInfoDialog::~TxInfoDialog() = default; \ No newline at end of file diff --git a/src/dialog/transactioninfodialog.h b/src/dialog/TxInfoDialog.h similarity index 52% rename from src/dialog/transactioninfodialog.h rename to src/dialog/TxInfoDialog.h index 6cb2960..0041471 100644 --- a/src/dialog/transactioninfodialog.h +++ b/src/dialog/TxInfoDialog.h @@ -1,26 +1,28 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef FEATHER_TRANSACTIONINFODIALOG_H -#define FEATHER_TRANSACTIONINFODIALOG_H +#ifndef FEATHER_TXINFODIALOG_H +#define FEATHER_TXINFODIALOG_H #include #include +#include #include + #include "appcontext.h" #include "dialog/TxProofDialog.h" namespace Ui { - class TransactionInfoDialog; + class TxInfoDialog; } -class TransactionInfoDialog : public QDialog +class TxInfoDialog : public QDialog { Q_OBJECT public: - explicit TransactionInfoDialog(QSharedPointer ctx, TransactionInfo *txInfo, QWidget *parent = nullptr); - ~TransactionInfoDialog() override; + explicit TxInfoDialog(QSharedPointer ctx, TransactionInfo *txInfo, QWidget *parent = nullptr); + ~TxInfoDialog() override; signals: void resendTranscation(const QString &txid); @@ -28,17 +30,15 @@ signals: private: void copyTxKey(); void createTxProof(); - void setData(TransactionInfo* tx); + void setData(TransactionInfo *tx); void updateData(); + void adjustHeight(QTextEdit *textEdit, qreal docHeight); - Ui::TransactionInfoDialog *ui; - + QScopedPointer ui; QSharedPointer m_ctx; TransactionInfo *m_txInfo; TxProofDialog *m_txProofDialog; - QString m_txKey; QString m_txid; - QTimer m_updateTimer; }; -#endif //FEATHER_TRANSACTIONINFODIALOG_H +#endif //FEATHER_TXINFODIALOG_H diff --git a/src/dialog/transactioninfodialog.ui b/src/dialog/TxInfoDialog.ui similarity index 80% rename from src/dialog/transactioninfodialog.ui rename to src/dialog/TxInfoDialog.ui index ceb0dfc..02b18c8 100644 --- a/src/dialog/transactioninfodialog.ui +++ b/src/dialog/TxInfoDialog.ui @@ -1,7 +1,7 @@ - TransactionInfoDialog - + TxInfoDialog + 0 @@ -94,7 +94,51 @@
- + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Inputs: + + + + + + + + 0 + 0 + + + + true + + + + + + + + QFrame::NoFrame @@ -115,7 +159,7 @@ 0 - + 0 @@ -128,7 +172,7 @@ - + 0 diff --git a/src/dialog/TxProofDialog.cpp b/src/dialog/TxProofDialog.cpp index 428a697..9299c4d 100644 --- a/src/dialog/TxProofDialog.cpp +++ b/src/dialog/TxProofDialog.cpp @@ -7,8 +7,8 @@ #include #include "libwalletqt/Transfer.h" -#include "utils/utils.h" #include "utils/Icons.h" +#include "utils/Utils.h" TxProofDialog::TxProofDialog(QWidget *parent, QSharedPointer ctx, TransactionInfo *txInfo) : QDialog(parent) @@ -18,7 +18,7 @@ TxProofDialog::TxProofDialog(QWidget *parent, QSharedPointer ctx, Tr ui->setupUi(this); m_txid = txInfo->hash(); - m_txKey = m_ctx->wallet->getTxKey(m_txid); + m_direction = txInfo->direction(); for (auto const &t: txInfo->transfers()) { @@ -54,6 +54,14 @@ TxProofDialog::TxProofDialog(QWidget *parent, QSharedPointer ctx, Tr this->adjustSize(); } +void TxProofDialog::getTxKey() { + if (!m_txKey.isEmpty()) return; + + m_ctx->wallet->getTxKeyAsync(m_txid, [this](QVariantMap map){ + m_txKey = map.value("tx_key").toString(); + }); +} + void TxProofDialog::setTxId(const QString &txid) { ui->label_txid->setText(txid); } @@ -67,6 +75,11 @@ void TxProofDialog::selectSpendProof() { return; } + if (m_ctx->wallet->isHwBacked()) { + this->showWarning("SpendProof creation is not supported on this hardware device."); + return; + } + ui->frame_message->show(); ui->label_summary->setText("This proof shows you created a transaction with the txid shown above."); } @@ -226,7 +239,4 @@ TxProof TxProofDialog::getProof() { return proof; } -TxProofDialog::~TxProofDialog() { - delete ui; -} - +TxProofDialog::~TxProofDialog() = default; \ No newline at end of file diff --git a/src/dialog/TxProofDialog.h b/src/dialog/TxProofDialog.h index d010248..ba47a57 100644 --- a/src/dialog/TxProofDialog.h +++ b/src/dialog/TxProofDialog.h @@ -6,8 +6,8 @@ #include -#include "libwalletqt/TransactionInfo.h" #include "appcontext.h" +#include "libwalletqt/TransactionInfo.h" namespace Ui { class TxProofDialog; @@ -21,6 +21,7 @@ public: explicit TxProofDialog(QWidget *parent, QSharedPointer ctx, TransactionInfo *txid); ~TxProofDialog() override; void setTxId(const QString &txid); + void getTxKey(); private slots: void selectSpendProof(); @@ -42,15 +43,15 @@ private: void toggleButtons(bool enabled); void showWarning(const QString &message); + QScopedPointer ui; + QSharedPointer m_ctx; + QStringList m_OutDestinations; QStringList m_InDestinations; QString m_txid; QString m_txKey; Mode m_mode; TransactionInfo::Direction m_direction; - - Ui::TxProofDialog *ui; - QSharedPointer m_ctx; }; #endif //FEATHER_TXPROOFDIALOG_H diff --git a/src/dialog/UpdateDialog.cpp b/src/dialog/UpdateDialog.cpp index f548370..af673d8 100644 --- a/src/dialog/UpdateDialog.cpp +++ b/src/dialog/UpdateDialog.cpp @@ -5,13 +5,12 @@ #include "ui_UpdateDialog.h" #include -#include +#include "utils/AsyncTask.h" #include "utils/networking.h" #include "utils/NetworkManager.h" -#include "utils/AsyncTask.h" #include "utils/Updater.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include "zip.h" @@ -223,6 +222,4 @@ void UpdateDialog::setStatus(const QString &msg, bool success) { ui->label_body->setStyleSheet(""); } -UpdateDialog::~UpdateDialog() { - delete ui; -} +UpdateDialog::~UpdateDialog() = default; \ No newline at end of file diff --git a/src/dialog/UpdateDialog.h b/src/dialog/UpdateDialog.h index 86d959a..2739666 100644 --- a/src/dialog/UpdateDialog.h +++ b/src/dialog/UpdateDialog.h @@ -34,7 +34,7 @@ signals: private: void setStatus(const QString &msg, bool success = false); - Ui::UpdateDialog *ui; + QScopedPointer ui; QString m_version; QString m_downloadUrl; diff --git a/src/dialog/verifyproofdialog.cpp b/src/dialog/VerifyProofDialog.cpp similarity index 97% rename from src/dialog/verifyproofdialog.cpp rename to src/dialog/VerifyProofDialog.cpp index ecabd65..d696f8f 100644 --- a/src/dialog/verifyproofdialog.cpp +++ b/src/dialog/VerifyProofDialog.cpp @@ -1,15 +1,15 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "verifyproofdialog.h" -#include "ui_verifyproofdialog.h" - -#include "libwalletqt/WalletManager.h" -#include "utils/utils.h" -#include "model/ModelUtils.h" +#include "VerifyProofDialog.h" +#include "ui_VerifyProofDialog.h" #include +#include "libwalletqt/WalletManager.h" +#include "model/ModelUtils.h" +#include "utils/Utils.h" + VerifyProofDialog::VerifyProofDialog(Wallet *wallet, QWidget *parent) : QDialog(parent) , ui(new Ui::VerifyProofDialog) @@ -53,11 +53,6 @@ VerifyProofDialog::VerifyProofDialog(Wallet *wallet, QWidget *parent) ui->input_formattedProof->setFont(ModelUtils::getMonospaceFont()); } -VerifyProofDialog::~VerifyProofDialog() -{ - delete ui; -} - void VerifyProofDialog::checkProof() { switch (ui->tabWidget->currentIndex()) { case 0: @@ -173,4 +168,6 @@ void VerifyProofDialog::proofStatus(bool success, const QString &message) { success ? QMessageBox::information(this, "Information", message) : QMessageBox::warning(this, "Warning", message); } -} \ No newline at end of file +} + +VerifyProofDialog::~VerifyProofDialog() = default; \ No newline at end of file diff --git a/src/dialog/verifyproofdialog.h b/src/dialog/VerifyProofDialog.h similarity index 95% rename from src/dialog/verifyproofdialog.h rename to src/dialog/VerifyProofDialog.h index 9159dec..8c538eb 100644 --- a/src/dialog/verifyproofdialog.h +++ b/src/dialog/VerifyProofDialog.h @@ -6,6 +6,7 @@ #include #include + #include "libwalletqt/Wallet.h" namespace Ui { @@ -31,7 +32,7 @@ private: void checkFormattedProof(); void proofStatus(bool success, const QString &message); - Ui::VerifyProofDialog *ui; + QScopedPointer ui; Wallet *m_wallet; QPixmap m_success; diff --git a/src/dialog/verifyproofdialog.ui b/src/dialog/VerifyProofDialog.ui similarity index 100% rename from src/dialog/verifyproofdialog.ui rename to src/dialog/VerifyProofDialog.ui diff --git a/src/dialog/viewonlydialog.cpp b/src/dialog/ViewOnlyDialog.cpp similarity index 94% rename from src/dialog/viewonlydialog.cpp rename to src/dialog/ViewOnlyDialog.cpp index 8aead69..160c0f7 100644 --- a/src/dialog/viewonlydialog.cpp +++ b/src/dialog/ViewOnlyDialog.cpp @@ -1,13 +1,13 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. +#include "ViewOnlyDialog.h" +#include "ui_ViewOnlyDialog.h" + #include #include #include -#include "viewonlydialog.h" -#include "ui_viewonlydialog.h" - ViewOnlyDialog::ViewOnlyDialog(QSharedPointer ctx, QWidget *parent) : QDialog(parent) , ui(new Ui::ViewOnlyDialog) @@ -54,7 +54,4 @@ void ViewOnlyDialog::copyToClipboad() { Utils::copyToClipboard(text); } -ViewOnlyDialog::~ViewOnlyDialog() -{ - delete ui; -} +ViewOnlyDialog::~ViewOnlyDialog() = default; diff --git a/src/dialog/viewonlydialog.h b/src/dialog/ViewOnlyDialog.h similarity index 92% rename from src/dialog/viewonlydialog.h rename to src/dialog/ViewOnlyDialog.h index 50caa38..3cb9942 100644 --- a/src/dialog/viewonlydialog.h +++ b/src/dialog/ViewOnlyDialog.h @@ -5,6 +5,7 @@ #define FEATHER_VIEWONLYDIALOG_H #include + #include "appcontext.h" namespace Ui { @@ -23,9 +24,10 @@ private slots: void onWriteViewOnlyWallet(); private: - Ui::ViewOnlyDialog *ui; - QSharedPointer m_ctx; void copyToClipboad(); + + QScopedPointer ui; + QSharedPointer m_ctx; }; diff --git a/src/dialog/viewonlydialog.ui b/src/dialog/ViewOnlyDialog.ui similarity index 100% rename from src/dialog/viewonlydialog.ui rename to src/dialog/ViewOnlyDialog.ui diff --git a/src/dialog/WalletCacheDebugDialog.cpp b/src/dialog/WalletCacheDebugDialog.cpp index 3f1d2d7..f2547ee 100644 --- a/src/dialog/WalletCacheDebugDialog.cpp +++ b/src/dialog/WalletCacheDebugDialog.cpp @@ -3,10 +3,11 @@ #include "WalletCacheDebugDialog.h" #include "ui_WalletCacheDebugDialog.h" -#include "model/ModelUtils.h" #include +#include "model/ModelUtils.h" + WalletCacheDebugDialog::WalletCacheDebugDialog(QSharedPointer ctx, QWidget *parent) : QDialog(parent) , ui(new Ui::WalletCacheDebugDialog) @@ -91,7 +92,4 @@ void WalletCacheDebugDialog::setOutput(const QString &output) { ui->output->setPlainText(output); } -WalletCacheDebugDialog::~WalletCacheDebugDialog() { - delete ui; -} - +WalletCacheDebugDialog::~WalletCacheDebugDialog() = default; diff --git a/src/dialog/WalletCacheDebugDialog.h b/src/dialog/WalletCacheDebugDialog.h index e2ba3c7..0b8f076 100644 --- a/src/dialog/WalletCacheDebugDialog.h +++ b/src/dialog/WalletCacheDebugDialog.h @@ -5,6 +5,7 @@ #define FEATHER_WALLETCACHEDEBUGDIALOG_H #include + #include "appcontext.h" namespace Ui { @@ -21,7 +22,8 @@ public: private: void setOutput(const QString &output); - Ui::WalletCacheDebugDialog *ui; + + QScopedPointer ui; QSharedPointer m_ctx; }; diff --git a/src/dialog/walletinfodialog.cpp b/src/dialog/WalletInfoDialog.cpp similarity index 50% rename from src/dialog/walletinfodialog.cpp rename to src/dialog/WalletInfoDialog.cpp index 57ae5c4..fac1766 100644 --- a/src/dialog/walletinfodialog.cpp +++ b/src/dialog/WalletInfoDialog.cpp @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "walletinfodialog.h" -#include "ui_walletinfodialog.h" +#include "WalletInfoDialog.h" +#include "ui_WalletInfoDialog.h" #include @@ -19,20 +19,29 @@ WalletInfoDialog::WalletInfoDialog(QSharedPointer ctx, QWidget *pare ui->label_netType->setText(Utils::QtEnumToString(m_ctx->wallet->nettype())); ui->label_seedType->setText(m_ctx->wallet->getCacheAttribute("feather.seed").isEmpty() ? "25 word" : "14 word"); ui->label_viewOnly->setText(m_ctx->wallet->viewOnly() ? "True" : "False"); - ui->label_path->setText(m_ctx->wallet->keysPath()); + ui->label_keysFile->setText(m_ctx->wallet->keysPath()); + ui->label_cacheFile->setText(m_ctx->wallet->cachePath()); ui->label_cacheSize->setText(QString("%1 MB").arg(QString::number(cache.size() / 1e6, 'f', 2))); connect(ui->btn_openWalletDir, &QPushButton::clicked, this, &WalletInfoDialog::openWalletDir); - this->adjustSize(); -} + ui->label_keysFileLabel->setHelpText("The \"keys file\" stores the wallet keys and wallet settings. " + "It is encrypted with the wallet password (if set).\n\n" + "Your funds will be irreversibly lost if you delete this file " + "without having a backup of your mnemonic seed or private keys."); -WalletInfoDialog::~WalletInfoDialog() { - delete ui; + ui->label_cacheFileLabel->setHelpText("The \"cache file\" stores transaction data, contacts, address labels, " + "block hashes, the 14-word seed (if applicable), and other miscellaneous information. " + "It is encrypted with the wallet password (if set).\n\n" + "Warning: Transaction keys and the 14-word seed CANNOT be recovered if this file is deleted."); + + this->adjustSize(); } void WalletInfoDialog::openWalletDir() { QFileInfo file(m_ctx->wallet->keysPath()); QDesktopServices::openUrl(QUrl(QString("file://%1").arg(file.absolutePath()), QUrl::TolerantMode)); -} \ No newline at end of file +} + +WalletInfoDialog::~WalletInfoDialog() = default; \ No newline at end of file diff --git a/src/dialog/walletinfodialog.h b/src/dialog/WalletInfoDialog.h similarity index 92% rename from src/dialog/walletinfodialog.h rename to src/dialog/WalletInfoDialog.h index 3764732..9ab4c25 100644 --- a/src/dialog/walletinfodialog.h +++ b/src/dialog/WalletInfoDialog.h @@ -23,7 +23,7 @@ public: private: void openWalletDir(); - Ui::WalletInfoDialog *ui; + QScopedPointer ui; QSharedPointer m_ctx; }; diff --git a/src/dialog/walletinfodialog.ui b/src/dialog/WalletInfoDialog.ui similarity index 87% rename from src/dialog/walletinfodialog.ui rename to src/dialog/WalletInfoDialog.ui index 8b1f0d5..9fd0622 100644 --- a/src/dialog/walletinfodialog.ui +++ b/src/dialog/WalletInfoDialog.ui @@ -85,14 +85,31 @@ - + - Path: + Keys file: - + + + TextLabel + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Cache size: + + + + + TextLabel @@ -102,20 +119,17 @@ - + - Cache size: + Cache file: - + TextLabel - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - @@ -143,6 +157,13 @@ + + + HelpLabel + QLabel +
components.h
+
+
diff --git a/src/dialog/transactioninfodialog.cpp b/src/dialog/transactioninfodialog.cpp deleted file mode 100644 index b4d0adc..0000000 --- a/src/dialog/transactioninfodialog.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2020-2021, The Monero Project. - -#include "transactioninfodialog.h" -#include "ui_transactioninfodialog.h" - -#include "libwalletqt/CoinsInfo.h" -#include "libwalletqt/WalletManager.h" -#include "libwalletqt/Transfer.h" -#include "libwalletqt/TransactionHistory.h" -#include "utils.h" -#include "utils/ColorScheme.h" -#include "model/ModelUtils.h" -#include "config.h" -#include "appcontext.h" - -#include -#include - -TransactionInfoDialog::TransactionInfoDialog(QSharedPointer ctx, TransactionInfo *txInfo, QWidget *parent) - : QDialog(parent) - , ui(new Ui::TransactionInfoDialog) - , m_ctx(std::move(ctx)) - , m_txInfo(txInfo) -{ - ui->setupUi(this); - - m_txid = txInfo->hash(); - ui->label_txid->setText(m_txid); - - m_txKey = m_ctx->wallet->getTxKey(txInfo->hash()); - if (m_txKey.isEmpty()) { - ui->btn_CopyTxKey->setEnabled(false); - ui->btn_CopyTxKey->setToolTip("Transaction key unknown"); - } - - connect(ui->btn_CopyTxKey, &QPushButton::pressed, this, &TransactionInfoDialog::copyTxKey); - connect(ui->btn_createTxProof, &QPushButton::pressed, this, &TransactionInfoDialog::createTxProof); - - connect(m_ctx->wallet.get(), &Wallet::newBlock, this, &TransactionInfoDialog::updateData); - - this->setData(txInfo); - - if (m_ctx->txCache.contains(txInfo->hash()) && (txInfo->isFailed() || txInfo->isPending()) && txInfo->direction() != TransactionInfo::Direction_In) { - connect(ui->btn_rebroadcastTx, &QPushButton::pressed, [this]{ - emit resendTranscation(m_txid); - }); - } else { - ui->btn_rebroadcastTx->hide(); - } - - QTextCursor cursor = ui->destinations->textCursor(); - for (const auto& transfer : txInfo->transfers()) { - auto address = transfer->address(); - auto amount = WalletManager::displayAmount(transfer->amount()); - auto index = m_ctx->wallet->subaddressIndex(address); - cursor.insertText(address, Utils::addressTextFormat(index)); - cursor.insertText(QString(" %1").arg(amount), QTextCharFormat()); - cursor.insertBlock(); - } - - if (txInfo->transfers().size() == 0) { - ui->frameDestinations->hide(); - } - - m_txProofDialog = new TxProofDialog(this, m_ctx, txInfo); - - QCoreApplication::processEvents(); - - qreal lineHeight = QFontMetrics(ui->destinations->document()->defaultFont()).height(); - qreal docHeight = txInfo->transfers().size(); - int h = int(docHeight * (lineHeight + 2) + 11); - h = qMin(qMax(h, 100), 600); - ui->destinations->setMinimumHeight(h); - ui->destinations->setMaximumHeight(h); - ui->destinations->verticalScrollBar()->hide(); - - this->adjustSize(); -} - -void TransactionInfoDialog::setData(TransactionInfo* tx) { - QString blockHeight = QString::number(tx->blockHeight()); - - if (tx->isFailed()) { - ui->label_status->setText("Status: Failed (node was unable to relay transaction)"); - } - if (blockHeight == "0") { - ui->label_status->setText("Status: Unconfirmed (in mempool)"); - } - else { - QString dateTimeFormat = QString("%1 %2").arg(config()->get(Config::dateFormat).toString(), config()->get(Config::timeFormat).toString()); - QString date = tx->timestamp().toString(dateTimeFormat); - QString statusText = QString("Status: Included in block %1 (%2 confirmations) on %3").arg(blockHeight, QString::number(tx->confirmations()), date); - ui->label_status->setText(statusText); - } - - - if (tx->confirmationsRequired() > tx->confirmations()) { - bool mandatoryLock = tx->confirmationsRequired() == 10; - QString confsRequired = QString::number(tx->confirmationsRequired() - tx->confirmations()); - ui->label_lock->setText(QString("Lock: Outputs become spendable in %1 blocks (%2)").arg(confsRequired, mandatoryLock ? "consensus rule" : "specified by sender")); - } else { - ui->label_lock->setText("Lock: Outputs are spendable"); - } - - QString direction = tx->direction() == TransactionInfo::Direction_In ? "received" : "sent"; - ui->label_amount->setText(QString("Amount %1: %2 XMR").arg(direction, tx->displayAmount())); - - QString fee; - if (tx->isCoinbase()) - fee = "Not applicable"; - else if (tx->direction() == TransactionInfo::Direction_In) - fee = "Paid by sender"; - else if (tx->fee().isEmpty()) - fee = "N/A"; - else - fee = QString("%1 XMR").arg(tx->fee()); - - ui->label_fee->setText(QString("Fee: %1").arg(fee)); - -} - -void TransactionInfoDialog::updateData() { - TransactionInfo* tx = m_ctx->wallet->history()->transaction(m_txid); - if (!tx) return; - this->setData(tx); -} - -void TransactionInfoDialog::copyTxKey() { - Utils::copyToClipboard(m_txKey); -} - -void TransactionInfoDialog::createTxProof() { - m_txProofDialog->show(); -} - -TransactionInfoDialog::~TransactionInfoDialog() { - delete ui; -} diff --git a/src/libwalletqt/AddressBook.cpp b/src/libwalletqt/AddressBook.cpp index 3f4fd85..9e9efe0 100644 --- a/src/libwalletqt/AddressBook.cpp +++ b/src/libwalletqt/AddressBook.cpp @@ -4,7 +4,7 @@ #include "AddressBook.h" #include -AddressBook::AddressBook(Monero::AddressBook *abImpl,QObject *parent) +AddressBook::AddressBook(Monero::AddressBook *abImpl, QObject *parent) : QObject(parent), m_addressBookImpl(abImpl) { getAll(); @@ -127,3 +127,16 @@ QString AddressBook::getDescription(const QString &address) const } return m_rows.value(*it)->description(); } + +QString AddressBook::getAddress(const QString &description) const +{ + QReadLocker locker(&m_lock); + + for (const auto &row : m_rows) { + if (row->description() == description) { + return row->address(); + } + } + + return QString(); +} \ No newline at end of file diff --git a/src/libwalletqt/AddressBook.h b/src/libwalletqt/AddressBook.h index 6b88704..d94f938 100644 --- a/src/libwalletqt/AddressBook.h +++ b/src/libwalletqt/AddressBook.h @@ -29,6 +29,7 @@ public: Q_INVOKABLE QString errorString() const; Q_INVOKABLE int errorCode() const; Q_INVOKABLE QString getDescription(const QString &address) const; + Q_INVOKABLE QString getAddress(const QString &description) const; enum ErrorCode { Status_Ok, @@ -47,9 +48,6 @@ signals: void refreshFinished() const; void descriptionChanged() const; - -public slots: - private: explicit AddressBook(Monero::AddressBook * abImpl, QObject *parent); friend class Wallet; diff --git a/src/libwalletqt/Coins.cpp b/src/libwalletqt/Coins.cpp index d0440db..ac49d27 100644 --- a/src/libwalletqt/Coins.cpp +++ b/src/libwalletqt/Coins.cpp @@ -96,6 +96,13 @@ QVector Coins::coins_from_txid(const QString &txid) return coins; } +void Coins::setDescription(int index, quint32 accountIndex, const QString &description) +{ + m_pimpl->setDescription(index, description.toStdString()); + this->refresh(accountIndex); + emit descriptionChanged(); +} + Coins::Coins(Monero::Coins *pimpl, QObject *parent) : QObject(parent) , m_pimpl(pimpl) diff --git a/src/libwalletqt/Coins.h b/src/libwalletqt/Coins.h index 62060a9..1813cb0 100644 --- a/src/libwalletqt/Coins.h +++ b/src/libwalletqt/Coins.h @@ -31,6 +31,7 @@ public: Q_INVOKABLE void freeze(int index) const; Q_INVOKABLE void thaw(int index) const; Q_INVOKABLE QVector coins_from_txid(const QString &txid); + Q_INVOKABLE void setDescription(int index, quint32 accountIndex, const QString &description); quint64 count() const; @@ -39,6 +40,7 @@ signals: void refreshFinished() const; void coinFrozen() const; void coinThawed() const; + void descriptionChanged() const; private: explicit Coins(Monero::Coins * pimpl, QObject *parent = nullptr); diff --git a/src/libwalletqt/CoinsInfo.cpp b/src/libwalletqt/CoinsInfo.cpp index d6076cb..b8ee753 100644 --- a/src/libwalletqt/CoinsInfo.cpp +++ b/src/libwalletqt/CoinsInfo.cpp @@ -104,6 +104,10 @@ bool CoinsInfo::coinbase() const { return m_coinbase; } +QString CoinsInfo::description() const { + return m_description; +} + CoinsInfo::CoinsInfo(const Monero::CoinsInfo *pimpl, QObject *parent) : QObject(parent) , m_blockHeight(pimpl->blockHeight()) @@ -126,6 +130,7 @@ CoinsInfo::CoinsInfo(const Monero::CoinsInfo *pimpl, QObject *parent) , m_unlocked(pimpl->unlocked()) , m_pubKey(QString::fromStdString(pimpl->pubKey())) , m_coinbase(pimpl->coinbase()) + , m_description(QString::fromStdString(pimpl->description())) { } diff --git a/src/libwalletqt/CoinsInfo.h b/src/libwalletqt/CoinsInfo.h index 05c71e7..d8143a6 100644 --- a/src/libwalletqt/CoinsInfo.h +++ b/src/libwalletqt/CoinsInfo.h @@ -35,6 +35,7 @@ Q_OBJECT Q_PROPERTY(bool unlocked READ unlocked) Q_PROPERTY(QString pubKey READ pubKey) Q_PROPERTY(bool coinbase READ coinbase) + Q_PROPERTY(QString description READ description) public: quint64 blockHeight() const; @@ -58,6 +59,7 @@ public: bool unlocked() const; QString pubKey() const; bool coinbase() const; + QString description() const; void setUnlocked(bool unlocked); @@ -86,6 +88,7 @@ private: bool m_unlocked; QString m_pubKey; bool m_coinbase; + QString m_description; }; #endif //FEATHER_COINSINFO_H diff --git a/src/libwalletqt/TransactionHistory.cpp b/src/libwalletqt/TransactionHistory.cpp index e355275..50a76f0 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/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 003fd36..98d0d9d 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -377,6 +377,7 @@ void Wallet::switchSubaddressAccount(quint32 accountIndex) m_history->refresh(m_currentSubaddressAccount); m_coins->refresh(m_currentSubaddressAccount); this->subaddressModel()->setCurrentSubaddressAcount(m_currentSubaddressAccount); + this->coinsModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount); emit currentSubaddressAccountChanged(); } } @@ -723,6 +724,29 @@ void Wallet::createTransactionSingleAsync(const QString &key_image, const QStrin }); } +PendingTransaction *Wallet::createTransactionSelected(const QVector &key_images, const QString &dst_addr, + size_t outputs, PendingTransaction::Priority priority) +{ + std::vector kis; + for (const auto &key_image : key_images) { + kis.push_back(key_image.toStdString()); + } + Monero::PendingTransaction *ptImpl = m_walletImpl->createTransactionSelected(kis, dst_addr.toStdString(), outputs, static_cast(priority)); + PendingTransaction *result = new PendingTransaction(ptImpl, this); + + return result; +} + +void Wallet::createTransactionSelectedAsync(const QVector &key_images, const QString &dst_addr, + size_t outputs, PendingTransaction::Priority priority) +{ + m_scheduler.run([this, key_images, dst_addr, outputs, priority] { + PendingTransaction *tx = createTransactionSelected(key_images, dst_addr, outputs, priority); + QVector address {dst_addr}; + emit transactionCreated(tx, address); + }); +} + PendingTransaction *Wallet::createSweepUnmixableTransaction() { // pauseRefresh(); @@ -945,12 +969,14 @@ QString Wallet::getTxKey(const QString &txid) const return QString::fromStdString(m_walletImpl->getTxKey(txid.toStdString())); } -//void Wallet::getTxKeyAsync(const QString &txid, const QJSValue &callback) -//{ -// m_scheduler.run([this, txid] { -// return QJSValueList({txid, getTxKey(txid)}); -// }, callback); -//} +void Wallet::getTxKeyAsync(const QString &txid, const std::function &callback) +{ + m_scheduler.run([this, txid] { + QVariantMap map; + map["tx_key"] = getTxKey(txid); + return map; + }, callback); +} QString Wallet::checkTxKey(const QString &txid, const QString &tx_key, const QString &address) { @@ -1076,7 +1102,8 @@ bool Wallet::verifySignedMessage(const QString &message, const QString &address, return m_walletImpl->verifySignedMessage(message.toStdString(), address.toStdString(), signature.toStdString()); } } -bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector &unknown_parameters, QString &error) + +bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector &unknown_parameters, QString &error) const { std::string s_address, s_payment_id, s_tx_description, s_recipient_name, s_error; std::vector s_unknown_parameters; @@ -1094,6 +1121,30 @@ bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id return res; } +QVariantMap Wallet::parse_uri_to_object(const QString &uri) const +{ + QString address; + QString payment_id; + uint64_t amount = 0; + QString tx_description; + QString recipient_name; + QVector unknown_parameters; + QString error; + + QVariantMap result; + if (this->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error)) { + result.insert("address", address); + result.insert("payment_id", payment_id); + result.insert("amount", amount > 0 ? QString::fromStdString(Monero::Wallet::displayAmount(amount)) : ""); + result.insert("tx_description", tx_description); + result.insert("recipient_name", recipient_name); + } else { + result.insert("error", error); + } + + return result; +} + bool Wallet::rescanSpent() { QMutexLocker locker(&m_asyncMutex); diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index 94dcc25..49e44eb 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -76,9 +76,9 @@ class Wallet : public QObject, public PassprasePrompter Q_OBJECT public: - Wallet(QObject * parent = nullptr); - Wallet(Monero::Wallet *w, QObject * parent = nullptr); - ~Wallet(); + explicit Wallet(QObject *parent = nullptr); + explicit Wallet(Monero::Wallet *w, QObject * parent = nullptr); + ~Wallet() override; enum Status { Status_Ok = Monero::Wallet::Status_Ok, @@ -298,6 +298,14 @@ public: void createTransactionSingleAsync(const QString &key_image, const QString &dst_addr, size_t outputs, PendingTransaction::Priority priority); + //! creates transaction with selected inputs + PendingTransaction * createTransactionSelected(const QVector &key_images, const QString &dst_addr, + size_t outputs, PendingTransaction::Priority priority); + + //! creates async transaction with selected inputs + void createTransactionSelectedAsync(const QVector &key_images, const QString &dst_addr, + size_t outputs, PendingTransaction::Priority priority); + //! creates sweep unmixable transaction PendingTransaction * createSweepUnmixableTransaction(); @@ -376,7 +384,8 @@ public: bool verifySignedMessage(const QString &message, const QString &address, const QString &signature, bool filename = false) const; //! Parse URI - bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector &unknown_parameters, QString &error); + bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector &unknown_parameters, QString &error) const; + QVariantMap parse_uri_to_object(const QString &uri) const; //! Namespace your cacheAttribute keys to avoid collisions bool setCacheAttribute(const QString &key, const QString &val); @@ -385,7 +394,8 @@ public: bool setUserNote(const QString &txid, const QString ¬e); QString getUserNote(const QString &txid) const; QString getTxKey(const QString &txid) const; - //void getTxKeyAsync(const QString &txid, const QJSValue &callback); + void getTxKeyAsync(const QString &txid, const std::function &callback); + QString checkTxKey(const QString &txid, const QString &tx_key, const QString &address); TxProof getTxProof(const QString &txid, const QString &address, const QString &message) const; // void getTxProofAsync(const QString &txid, const QString &address, const QString &message, const QJSValue &callback); diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index aab885f..9c47dd8 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -9,40 +9,40 @@ class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver { public: - WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_phelper(mgr) {} + explicit WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_phelper(mgr) {} - virtual void moneySpent(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; }; - virtual void moneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; }; - virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; }; - virtual void newBlock(uint64_t height) override { (void) height; }; - virtual void updated() override {}; - virtual void refreshed(bool success) override {}; + void moneySpent(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; }; + void moneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; }; + void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; }; + void newBlock(uint64_t height) override { (void) height; }; + void updated() override {}; + void refreshed(bool success) override {}; - virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override + void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override { qDebug() << __FUNCTION__; m_phelper.onPassphraseEntered(passphrase, enter_on_device, entry_abort); } -// virtual Monero::optional onDevicePassphraseRequest(bool & on_device) override -// { -// qDebug() << __FUNCTION__; -// return m_phelper.onDevicePassphraseRequest(on_device); -// } -// - virtual void onDeviceButtonRequest(uint64_t code) override + Monero::optional onDevicePassphraseRequest(bool & on_device) override + { + qDebug() << __FUNCTION__; + return m_phelper.onDevicePassphraseRequest(on_device); + } + + void onDeviceButtonRequest(uint64_t code) override { qDebug() << __FUNCTION__; emit m_mgr->deviceButtonRequest(code); } -// -// virtual void onDeviceButtonPressed() override -// { -// qDebug() << __FUNCTION__; -// emit m_mgr->deviceButtonPressed(); -// } - virtual void onDeviceError(const std::string &message) override + void onDeviceButtonPressed() override + { + qDebug() << __FUNCTION__; + emit m_mgr->deviceButtonPressed(); + } + + void onDeviceError(const std::string &message) override { qDebug() << __FUNCTION__; emit m_mgr->deviceError(QString::fromStdString(message)); @@ -304,38 +304,6 @@ QString WalletManager::resolveOpenAlias(const QString &address) const res = std::string(dnssec_valid ? "true" : "false") + "|" + res; return QString::fromStdString(res); } -bool WalletManager::parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector &unknown_parameters, QString &error) const -{ - QMutexLocker locker(&m_mutex); - // TODO: FIXME -// if (m_currentWallet) -// return m_currentWallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error); - return false; -} - -QVariantMap WalletManager::parse_uri_to_object(const QString &uri) const -{ - QString address; - QString payment_id; - uint64_t amount = 0; - QString tx_description; - QString recipient_name; - QVector unknown_parameters; - QString error; - - QVariantMap result; - if (this->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error)) { - result.insert("address", address); - result.insert("payment_id", payment_id); - result.insert("amount", amount > 0 ? displayAmount(amount) : ""); - result.insert("tx_description", tx_description); - result.insert("recipient_name", recipient_name); - } else { - result.insert("error", error); - } - - return result; -} void WalletManager::setLogLevel(int logLevel) { diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h index b43f83d..b9086c5 100644 --- a/src/libwalletqt/WalletManager.h +++ b/src/libwalletqt/WalletManager.h @@ -143,8 +143,6 @@ public: Q_INVOKABLE qint64 subi(qint64 x, qint64 y) const { return x - y; } Q_INVOKABLE QString resolveOpenAlias(const QString &address) const; - Q_INVOKABLE bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector &unknown_parameters, QString &error) const; - Q_INVOKABLE QVariantMap parse_uri_to_object(const QString &uri) const; // clear/rename wallet cache Q_INVOKABLE static bool clearWalletCache(const QString &fileName); diff --git a/src/main.cpp b/src/main.cpp index 62fdb9f..46fef96 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,10 +6,10 @@ #include #include +#include "cli.h" #include "config-feather.h" #include "constants.h" -#include "mainwindow.h" -#include "cli.h" +#include "MainWindow.h" #include "WindowManager.h" #if defined(Q_OS_WIN) diff --git a/src/model/AddressBookModel.cpp b/src/model/AddressBookModel.cpp index 9f96a8f..84380f1 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/CoinsModel.cpp b/src/model/CoinsModel.cpp index ca297fb..a5a409d 100644 --- a/src/model/CoinsModel.cpp +++ b/src/model/CoinsModel.cpp @@ -13,8 +13,8 @@ #include CoinsModel::CoinsModel(QObject *parent, Coins *coins) - : QAbstractTableModel(parent), - m_coins(coins) + : QAbstractTableModel(parent) + , m_coins(coins) { connect(m_coins, &Coins::refreshStarted, this, &CoinsModel::startReset); connect(m_coins, &Coins::refreshFinished, this, &CoinsModel::endReset); @@ -61,7 +61,7 @@ QVariant CoinsModel::data(const QModelIndex &index, int role) const QVariant result; bool found = m_coins->coin(index.row(), [this, &index, &result, &role](const CoinsInfo &cInfo) { - if(role == Qt::DisplayRole || role == Qt::UserRole) { + if(role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) { result = parseTransactionInfo(cInfo, index.column(), role); } else if (role == Qt::BackgroundRole) { @@ -97,7 +97,7 @@ QVariant CoinsModel::data(const QModelIndex &index, int role) const else if (role == Qt::FontRole) { switch(index.column()) { case PubKey: - case OutputPoint: + case TxID: case Address: result = ModelUtils::getMonospaceFont(); } @@ -119,6 +119,9 @@ QVariant CoinsModel::data(const QModelIndex &index, int role) const else if (!cInfo.unlocked()) { result = "Output is locked (needs more confirmations)"; } + else if (cInfo.spent()) { + result = "Output is spent"; + } } }); if (!found) { @@ -137,13 +140,13 @@ QVariant CoinsModel::headerData(int section, Qt::Orientation orientation, int ro switch(section) { case PubKey: return QString("Pub Key"); - case OutputPoint: - return QString("Output point"); + case TxID: + return QString("TxID"); case BlockHeight: return QString("Height"); case Address: return QString("Address"); - case AddressLabel: + case Label: return QString("Label"); case SpentHeight: return QString("Spent Height"); @@ -160,6 +163,38 @@ QVariant CoinsModel::headerData(int section, Qt::Orientation orientation, int ro return QVariant(); } +Qt::ItemFlags CoinsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + + if (index.column() == Label) + return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; + + return QAbstractTableModel::flags(index); +} + +bool CoinsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) { + const int row = index.row(); + + switch (index.column()) { + case Label: + m_coins->setDescription(row, m_currentSubaddressAccount, value.toString()); + emit descriptionChanged(); + break; + default: + return false; + } + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + + return true; + } + + return false; +} + QVariant CoinsModel::parseTransactionInfo(const CoinsInfo &cInfo, int column, int role) const { switch (column) @@ -168,14 +203,17 @@ QVariant CoinsModel::parseTransactionInfo(const CoinsInfo &cInfo, int column, in return ""; case PubKey: return cInfo.pubKey().mid(0,8); - case OutputPoint: - return cInfo.hash().mid(0, 8) + ":" + QString::number(cInfo.internalOutputIndex()); + case TxID: + return cInfo.hash().mid(0, 8) + " "; case BlockHeight: return cInfo.blockHeight(); case Address: return ModelUtils::displayAddress(cInfo.address(), 1, ""); - case AddressLabel: + case Label: { + if (!cInfo.description().isEmpty()) + return cInfo.description(); return cInfo.addressLabel(); + } case Spent: return cInfo.spent(); case SpentHeight: @@ -197,6 +235,10 @@ QVariant CoinsModel::parseTransactionInfo(const CoinsInfo &cInfo, int column, in } } +void CoinsModel::setCurrentSubaddressAccount(quint32 accountIndex) { + m_currentSubaddressAccount = accountIndex; +} + CoinsInfo* CoinsModel::entryFromIndex(const QModelIndex &index) const { Q_ASSERT(index.isValid() && index.row() < m_coins->count()); return m_coins->coin(index.row()); diff --git a/src/model/CoinsModel.h b/src/model/CoinsModel.h index 97dd19f..0f69d27 100644 --- a/src/model/CoinsModel.h +++ b/src/model/CoinsModel.h @@ -23,9 +23,9 @@ public: { KeyImageKnown = 0, PubKey, - OutputPoint, + TxID, Address, - AddressLabel, + Label, BlockHeight, SpentHeight, Amount, @@ -42,9 +42,16 @@ public: int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; CoinsInfo* entryFromIndex(const QModelIndex &index) const; + void setCurrentSubaddressAccount(quint32 accountIndex); + +signals: + void descriptionChanged(); + public slots: void startReset(); void endReset(); @@ -53,6 +60,7 @@ private: QVariant parseTransactionInfo(const CoinsInfo &cInfo, int column, int role) const; Coins *m_coins; + quint32 m_currentSubaddressAccount; }; #endif //FEATHER_COINSMODEL_H diff --git a/src/model/CoinsProxyModel.cpp b/src/model/CoinsProxyModel.cpp index d376813..50318f7 100644 --- a/src/model/CoinsProxyModel.cpp +++ b/src/model/CoinsProxyModel.cpp @@ -6,19 +6,37 @@ #include "libwalletqt/CoinsInfo.h" CoinsProxyModel::CoinsProxyModel(QObject *parent, Coins *coins) - : QSortFilterProxyModel(parent), m_coins(coins) + : QSortFilterProxyModel(parent) + , m_coins(coins) + , m_searchRegExp("") { + m_searchRegExp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); setSortRole(Qt::UserRole); } +void CoinsProxyModel::setShowSpent(const bool showSpent) { + m_showSpent = showSpent; + invalidateFilter(); +} + +void CoinsProxyModel::setSearchFilter(const QString &searchString) { + m_searchRegExp.setPattern(searchString); + invalidateFilter(); +} + bool CoinsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { - bool isSpent; - int accountIndex; - m_coins->coin(sourceRow, [&isSpent, &accountIndex](const CoinsInfo &c){ - isSpent = c.spent(); - accountIndex = c.subaddrAccount(); - }); + CoinsInfo* coin = m_coins->coin(sourceRow); - return !(!m_showSpent && isSpent) && accountIndex == 0; + if (!m_showSpent && coin->spent()) { + return false; + } + + if (!m_searchRegExp.pattern().isEmpty()) { + return coin->pubKey().contains(m_searchRegExp) || coin->address().contains(m_searchRegExp) + || coin->hash().contains(m_searchRegExp) || coin->addressLabel().contains(m_searchRegExp) + || coin->description().contains(m_searchRegExp); + } + + return true; } \ No newline at end of file diff --git a/src/model/CoinsProxyModel.h b/src/model/CoinsProxyModel.h index 43bff13..6c522ed 100644 --- a/src/model/CoinsProxyModel.h +++ b/src/model/CoinsProxyModel.h @@ -5,6 +5,7 @@ #define FEATHER_COINSPROXYMODEL_H #include + #include "libwalletqt/Coins.h" class CoinsProxyModel : public QSortFilterProxyModel @@ -12,17 +13,16 @@ class CoinsProxyModel : public QSortFilterProxyModel Q_OBJECT public: explicit CoinsProxyModel(QObject* parent, Coins *coins); - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; public slots: - void setShowSpent(const bool showSpent){ - m_showSpent = showSpent; - invalidateFilter(); - } + void setSearchFilter(const QString &searchString); + void setShowSpent(bool showSpent); private: - bool m_showSpent = false; Coins *m_coins; + bool m_showSpent = false; + QRegularExpression m_searchRegExp; }; #endif //FEATHER_COINSPROXYMODEL_H diff --git a/src/model/HistoryView.cpp b/src/model/HistoryView.cpp index bc3f89b..be1da45 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 065a7be..9080ec1 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/TransactionHistoryModel.cpp b/src/model/TransactionHistoryModel.cpp index 0374cf1..2265a3e 100644 --- a/src/model/TransactionHistoryModel.cpp +++ b/src/model/TransactionHistoryModel.cpp @@ -123,6 +123,14 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const } } } + else if (role == Qt::FontRole) { + switch(index.column()) { + case Column::TxID: + { + result = ModelUtils::getMonospaceFont(); + } + } + } }); if (!found) { diff --git a/src/model/TransactionHistoryProxyModel.cpp b/src/model/TransactionHistoryProxyModel.cpp index 40b7f34..c549c01 100644 --- a/src/model/TransactionHistoryProxyModel.cpp +++ b/src/model/TransactionHistoryProxyModel.cpp @@ -7,12 +7,11 @@ #include "libwalletqt/TransactionInfo.h" TransactionHistoryProxyModel::TransactionHistoryProxyModel(Wallet *wallet, QObject *parent) - : QSortFilterProxyModel(parent), - m_wallet(wallet), - m_searchRegExp("") + : QSortFilterProxyModel(parent) + , m_wallet(wallet) + , m_searchRegExp("") { - m_searchRegExp.setCaseSensitivity(Qt::CaseInsensitive); - m_searchRegExp.setPatternSyntax(QRegExp::RegExp); + m_searchRegExp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); m_history = m_wallet->history(); } diff --git a/src/model/TransactionHistoryProxyModel.h b/src/model/TransactionHistoryProxyModel.h index a84535d..45feb00 100644 --- a/src/model/TransactionHistoryProxyModel.h +++ b/src/model/TransactionHistoryProxyModel.h @@ -4,11 +4,10 @@ #ifndef FEATHER_TRANSACTIONHISTORYPROXYMODEL_H #define FEATHER_TRANSACTIONHISTORYPROXYMODEL_H +#include + #include "libwalletqt/TransactionHistory.h" #include "libwalletqt/Wallet.h" -#include "libwalletqt/TransactionHistory.h" - -#include class TransactionHistoryProxyModel : public QSortFilterProxyModel { @@ -28,7 +27,7 @@ private: Wallet *m_wallet; TransactionHistory *m_history; - QRegExp m_searchRegExp; + QRegularExpression m_searchRegExp; }; #endif //FEATHER_TRANSACTIONHISTORYPROXYMODEL_H diff --git a/src/model/WalletKeysFilesModel.cpp b/src/model/WalletKeysFilesModel.cpp index 37fd03d..225461a 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/qrcode/QrCode.cpp b/src/qrcode/QrCode.cpp index 6769540..7326826 100644 --- a/src/qrcode/QrCode.cpp +++ b/src/qrcode/QrCode.cpp @@ -164,4 +164,20 @@ QPixmap QrCode::toPixmap(const int margin) const painter.end(); return pixmap; -} \ No newline at end of file +} + +int QrCode::width() { + if (!isValid()) { + return 0; + } + + return d_ptr->m_qrcode->width; +} + +unsigned char* QrCode::data() { + if (!isValid()) { + return nullptr; + } + + return d_ptr->m_qrcode->data; +} diff --git a/src/qrcode/QrCode.h b/src/qrcode/QrCode.h index 50173b4..879be77 100644 --- a/src/qrcode/QrCode.h +++ b/src/qrcode/QrCode.h @@ -71,6 +71,9 @@ public: void writeSvg(QIODevice* outputDevice, const int dpi, const int margin = 4) const; QPixmap toPixmap(const int margin = 4) const; + int width(); + unsigned char* data(); + private: void init(const QString& data, const Version version, const ErrorCorrectionLevel ecl, const bool caseSensitive); diff --git a/src/qrcode_scanner/QrCodeScanDialog.cpp b/src/qrcode_scanner/QrCodeScanDialog.cpp index 502f308..2980f5c 100644 --- a/src/qrcode_scanner/QrCodeScanDialog.cpp +++ b/src/qrcode_scanner/QrCodeScanDialog.cpp @@ -119,5 +119,4 @@ QrCodeScanDialog::~QrCodeScanDialog() m_thread->terminate(); m_thread->wait(); } - delete ui; } \ No newline at end of file diff --git a/src/qrcode_scanner/QrCodeScanDialog.h b/src/qrcode_scanner/QrCodeScanDialog.h index 8227ed1..59fde5c 100644 --- a/src/qrcode_scanner/QrCodeScanDialog.h +++ b/src/qrcode_scanner/QrCodeScanDialog.h @@ -37,7 +37,7 @@ private: void displayCameraError(); void takeImage(); - Ui::QrCodeScanDialog *ui; + QScopedPointer ui; QScopedPointer m_camera; QScopedPointer m_imageCapture; diff --git a/src/utils/RestoreHeightLookup.h b/src/utils/RestoreHeightLookup.h index b0499f0..9109be2 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 3a9c7bb..e19ea54 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 6cd339e..be00e83 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 71% rename from src/utils/utils.cpp rename to src/utils/Utils.cpp index 78fc830..eb17016 100644 --- a/src/utils/utils.cpp +++ b/src/utils/Utils.cpp @@ -1,53 +1,29 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include #include #include #include -#include #include -#include #include -#include "utils.h" +#include "constants.h" +#include "networktype.h" +#include "Utils.h" +#include "utils/ColorScheme.h" #include "utils/config.h" #include "utils/os/tails.h" #include "utils/os/whonix.h" -#include "utils/ColorScheme.h" -#include "constants.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(); } @@ -56,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(); @@ -67,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; @@ -77,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()) @@ -208,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; @@ -216,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"; @@ -231,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"; @@ -240,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); @@ -269,20 +370,110 @@ 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; +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; + 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 } -QString Utils::getUnixAccountName() { +QTextCharFormat addressTextFormat(const SubaddressIndex &index, quint64 amount) { + QTextCharFormat rec; + if (index.isPrimary()) { + rec.setBackground(QBrush(ColorScheme::YELLOW.asColor(true))); + rec.setToolTip("Wallet change/primary address"); + } + else if (index.isValid()) { + rec.setBackground(QBrush(ColorScheme::GREEN.asColor(true))); + rec.setToolTip("Wallet receive address"); + } + else if (amount == 0) { + rec.setBackground(QBrush(ColorScheme::GRAY.asColor(true))); + rec.setToolTip("Dummy output (Min. 2 outs consensus rule)"); + } + return rec; +} + +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; + + 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; + } +} + +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 @@ -291,218 +482,9 @@ QString Utils::getUnixAccountName() { 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)}; - QProcess process; - process.start("update-desktop-database", args); - process.waitForFinished(2500); - process.close(); -} - -bool Utils::xdgDesktopEntryRegister() { -#if defined(Q_OS_MACOS) - return false; -#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) { +QFont 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) { - if (index.isPrimary()) { - QTextCharFormat rec; - rec.setBackground(QBrush(ColorScheme::YELLOW.asColor(true))); - rec.setToolTip("Wallet change/primary address"); - return rec; - } - if (index.isValid()) { - QTextCharFormat rec; - rec.setBackground(QBrush(ColorScheme::GREEN.asColor(true))); - rec.setToolTip("Wallet receive address"); - return rec; - } - return QTextCharFormat(); -} - -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 -} - -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 } \ No newline at end of file diff --git a/src/utils/Utils.h b/src/utils/Utils.h new file mode 100644 index 0000000..2df870f --- /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 00bf16b..5855f95 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 39a7c45..96140d9 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 2e4baf9..2e434ea 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 @@ -51,6 +51,7 @@ static const QHash configStrings = { // Mining {Config::xmrigPath,{QS("xmrigPath"), ""}}, {Config::xmrigPool,{QS("xmrigPool"), "pool.xmr.pt:9000"}}, + {Config::pools,{QS("pools"), {}}}, // Settings {Config::preferredFiatCurrency,{QS("preferredFiatCurrency"), "USD"}}, @@ -110,6 +111,14 @@ void Config::set(ConfigKey key, const QVariant& value) emit changed(key); } +void Config::remove(ConfigKey key) +{ + auto cfg = configStrings[key]; + m_settings->remove(cfg.name); + + emit changed(key); +} + /** * Sync configuration with persistent storage. * diff --git a/src/utils/config.h b/src/utils/config.h index 96fdb34..1dce530 100644 --- a/src/utils/config.h +++ b/src/utils/config.h @@ -3,8 +3,8 @@ // Copyright (C) 2011 Felix Geyer // Copyright (c) 2020-2021, The Monero Project. -#ifndef FEATHER_SETTINGS_H -#define FEATHER_SETTINGS_H +#ifndef FEATHER_CONFIG_H +#define FEATHER_CONFIG_H #include #include @@ -55,6 +55,7 @@ public: // Mining xmrigPath, xmrigPool, + pools, // Settings preferredFiatCurrency, @@ -101,6 +102,7 @@ public: QVariant get(ConfigKey key); QString getFileName(); void set(ConfigKey key, const QVariant& value); + void remove(ConfigKey key); void sync(); void resetToDefaults(); @@ -128,4 +130,4 @@ inline Config* config() return Config::instance(); } -#endif //FEATHER_SETTINGS_H +#endif //FEATHER_CONFIG_H diff --git a/src/utils/networking.cpp b/src/utils/networking.cpp index 9b7e578..5885fd9 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 9452c8a..1799ff1 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 ae976fb..98f70fd 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 ce60584..07ff671 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 80c5b37..f65f576 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 6865f2c..b22a0c8 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 03fa06b..6e6f7f4 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/scheduler.cpp b/src/utils/scheduler.cpp index e8b8503..bc9bb36 100644 --- a/src/utils/scheduler.cpp +++ b/src/utils/scheduler.cpp @@ -45,32 +45,27 @@ QPair> FutureScheduler::run(std::function function) }); } -//QPair> FutureScheduler::run(std::function function, const QJSValue &callback) -//{ -// if (!callback.isCallable()) -// { -// throw std::runtime_error("js callback must be callable"); -// } - -// return execute([this, function, callback](QFutureWatcher *watcher) { -// connect(watcher, &QFutureWatcher::finished, [watcher, callback] { -// QJSValue(callback).call(watcher->future().result()); -// }); -// return QtConcurrent::run([this, function] { -// QJSValueList result; -// try -// { -// result = function(); -// } -// catch (const std::exception &exception) -// { -// qWarning() << "Exception thrown from async function: " << exception.what(); -// } -// done(); -// return result; -// }); -// }); -//} +QPair> FutureScheduler::run(const std::function &function, const std::function &callback) noexcept +{ + return execute([this, function, callback](QFutureWatcher *watcher) { + connect(watcher, &QFutureWatcher::finished, [watcher, callback] { + callback(watcher->future().result()); + }); + return QtConcurrent::run([this, function] { + QVariantMap result; + try + { + result = function(); + } + catch (const std::exception &exception) + { + qWarning() << "Exception thrown from async function: " << exception.what(); + } + done(); + return result; + }); + }); +} bool FutureScheduler::stopping() const noexcept { diff --git a/src/utils/scheduler.h b/src/utils/scheduler.h index 1dd9e23..df02703 100644 --- a/src/utils/scheduler.h +++ b/src/utils/scheduler.h @@ -25,6 +25,8 @@ public: void shutdownWaitForFinished() noexcept; QPair> run(std::function function) noexcept; + QPair> run(const std::function& function, const std::function& callback) noexcept; + // QPair> run(std::function function, const QJSValue &callback); bool stopping() const noexcept; @@ -57,7 +59,7 @@ private: } QFutureWatcher schedule(std::function function); - //QFutureWatcher schedule(std::function function, const QJSValue &callback); +// QFutureWatcher schedule(std::function function, std::function &callback); private: size_t Alive; diff --git a/src/utils/txfiathistory.cpp b/src/utils/txfiathistory.cpp index b0f9fb8..73cd471 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 7b88b8d..0000000 --- a/src/utils/utils.h +++ /dev/null @@ -1,91 +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); - static bool isTorsocks(); - static QString defaultWalletDir(); - - 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 7ef3aa9..d9977f3 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/wsclient.h b/src/utils/wsclient.h index 033679e..29a7955 100644 --- a/src/utils/wsclient.h +++ b/src/utils/wsclient.h @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef ECHOCLIENT_H -#define ECHOCLIENT_H +#ifndef FEATHER_WSCLIENT_H +#define FEATHER_WSCLIENT_H #include #include @@ -39,4 +39,4 @@ private: QTimer m_pingTimer; }; -#endif // ECHOCLIENT_H \ No newline at end of file +#endif // FEATHER_WSCLIENT_H \ No newline at end of file diff --git a/src/utils/xmrig.cpp b/src/utils/xmrig.cpp index f9f937f..2f8b613 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/ccsprogressdelegate.cpp b/src/widgets/CCSProgressDelegate.cpp similarity index 93% rename from src/widgets/ccsprogressdelegate.cpp rename to src/widgets/CCSProgressDelegate.cpp index 18c39b3..dfed0ca 100644 --- a/src/widgets/ccsprogressdelegate.cpp +++ b/src/widgets/CCSProgressDelegate.cpp @@ -1,13 +1,13 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include "ccsprogressdelegate.h" +#include "CCSProgressDelegate.h" #include CCSProgressDelegate::CCSProgressDelegate(CCSModel *model, QWidget *parent) - : QStyledItemDelegate(parent) - , m_model(model) + : QStyledItemDelegate(parent) + , m_model(model) { } diff --git a/src/widgets/ccsprogressdelegate.h b/src/widgets/CCSProgressDelegate.h similarity index 99% rename from src/widgets/ccsprogressdelegate.h rename to src/widgets/CCSProgressDelegate.h index c2fd13e..1ad23b1 100644 --- a/src/widgets/ccsprogressdelegate.h +++ b/src/widgets/CCSProgressDelegate.h @@ -5,6 +5,7 @@ #define FEATHER_CSSPROGRESSDELEGATE_H #include + #include "model/CCSModel.h" class CCSProgressDelegate : public QStyledItemDelegate diff --git a/src/widgets/ccswidget.cpp b/src/widgets/CCSWidget.cpp similarity index 86% rename from src/widgets/ccswidget.cpp rename to src/widgets/CCSWidget.cpp index a170ad6..a80571a 100644 --- a/src/widgets/ccswidget.cpp +++ b/src/widgets/CCSWidget.cpp @@ -1,19 +1,20 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. +#include "CCSWidget.h" +#include "ui_CCSWidget.h" + +#include #include #include -#include -#include "ccswidget.h" -#include "ui_ccswidget.h" -#include "ccsprogressdelegate.h" +#include "CCSProgressDelegate.h" -CCSWidget::CCSWidget(QWidget *parent) : - QWidget(parent), - ui(new Ui::CSSWidget), - m_model(new CCSModel(this)), - m_contextMenu(new QMenu(this)) +CCSWidget::CCSWidget(QWidget *parent) + : QWidget(parent) + , ui(new Ui::CSSWidget) + , m_model(new CCSModel(this)) + , m_contextMenu(new QMenu(this)) { ui->setupUi(this); auto progressDelegate = new CCSProgressDelegate(m_model, this); @@ -69,6 +70,4 @@ void CCSWidget::showContextMenu(const QPoint &pos) { m_contextMenu->exec(ui->tableView->viewport()->mapToGlobal(pos)); } -CCSWidget::~CCSWidget() { - delete ui; -} +CCSWidget::~CCSWidget() = default; \ No newline at end of file diff --git a/src/widgets/ccswidget.h b/src/widgets/CCSWidget.h similarity index 85% rename from src/widgets/ccswidget.h rename to src/widgets/CCSWidget.h index 9fa367c..21332e8 100644 --- a/src/widgets/ccswidget.h +++ b/src/widgets/CCSWidget.h @@ -1,18 +1,18 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef CSSWIDGET_H -#define CSSWIDGET_H +#ifndef FEATHER_CSSWIDGET_H +#define FEATHER_CSSWIDGET_H +#include #include #include -#include #include -#include +#include -#include "widgets/CCSEntry.h" -#include "model/CCSModel.h" #include "appcontext.h" +#include "model/CCSModel.h" +#include "widgets/CCSEntry.h" namespace Ui { class CSSWidget; @@ -40,9 +40,9 @@ private: void setupTable(); void showContextMenu(const QPoint &pos); - Ui::CSSWidget *ui; + QScopedPointer ui; CCSModel *m_model; QMenu *m_contextMenu; }; -#endif // CSSWIDGET_H +#endif // FEATHER_CSSWIDGET_H diff --git a/src/widgets/ccswidget.ui b/src/widgets/CCSWidget.ui similarity index 100% rename from src/widgets/ccswidget.ui rename to src/widgets/CCSWidget.ui diff --git a/src/widgets/LocalMoneroWidget.cpp b/src/widgets/LocalMoneroWidget.cpp index db3c31b..5fb70f3 100644 --- a/src/widgets/LocalMoneroWidget.cpp +++ b/src/widgets/LocalMoneroWidget.cpp @@ -3,19 +3,20 @@ #include "LocalMoneroWidget.h" #include "ui_LocalMoneroWidget.h" -#include "utils/ColorScheme.h" -#include "utils/Icons.h" -#include "utils/NetworkManager.h" -#include "utils/WebsocketNotifier.h" -#include "dialog/LocalMoneroInfoDialog.h" #include #include +#include "dialog/LocalMoneroInfoDialog.h" +#include "utils/ColorScheme.h" +#include "utils/Icons.h" +#include "utils/NetworkManager.h" +#include "utils/WebsocketNotifier.h" + LocalMoneroWidget::LocalMoneroWidget(QWidget *parent, QSharedPointer ctx) - : QWidget(parent) - , ui(new Ui::LocalMoneroWidget) - , m_ctx(std::move(ctx)) + : QWidget(parent) + , ui(new Ui::LocalMoneroWidget) + , m_ctx(std::move(ctx)) { ui->setupUi(this); @@ -143,10 +144,6 @@ void LocalMoneroWidget::searchOffers(int page) { m_api->sellMoneroOnline(currencyCode, countryCode, paymentMethod, amount, page); } -LocalMoneroWidget::~LocalMoneroWidget() { - delete ui; -} - void LocalMoneroWidget::showContextMenu(const QPoint &point) { QModelIndex index = ui->treeView->indexAt(point); if (!index.isValid()) { @@ -207,4 +204,6 @@ void LocalMoneroWidget::updatePaymentMethods() { ui->combo_paymentMethod->addItem(name, payment_method); } } -} \ No newline at end of file +} + +LocalMoneroWidget::~LocalMoneroWidget() = default; \ No newline at end of file diff --git a/src/widgets/LocalMoneroWidget.h b/src/widgets/LocalMoneroWidget.h index 4c08878..d5d6acc 100644 --- a/src/widgets/LocalMoneroWidget.h +++ b/src/widgets/LocalMoneroWidget.h @@ -6,8 +6,8 @@ #include -#include "appcontext.h" #include "api/LocalMoneroApi.h" +#include "appcontext.h" #include "model/LocalMoneroModel.h" namespace Ui { @@ -41,7 +41,7 @@ private: void viewOfferDetails(); void updatePaymentMethods(); - Ui::LocalMoneroWidget *ui; + QScopedPointer ui; QSharedPointer m_ctx; int m_currentPage = 0; diff --git a/src/widgets/nodewidget.cpp b/src/widgets/NodeWidget.cpp similarity index 98% rename from src/widgets/nodewidget.cpp rename to src/widgets/NodeWidget.cpp index 94b857c..23d6afa 100644 --- a/src/widgets/nodewidget.cpp +++ b/src/widgets/NodeWidget.cpp @@ -1,15 +1,17 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#include -#include -#include +#include "NodeWidget.h" +#include "ui_NodeWidget.h" + +#include #include +#include +#include +#include +#include #include "model/NodeModel.h" -#include "nodewidget.h" -#include "ui_nodewidget.h" -#include "mainwindow.h" #include "utils/Icons.h" NodeWidget::NodeWidget(QWidget *parent) @@ -188,6 +190,4 @@ NodeModel* NodeWidget::model() { return m_wsModel; } -NodeWidget::~NodeWidget() { - delete ui; -} +NodeWidget::~NodeWidget() = default; diff --git a/src/widgets/nodewidget.h b/src/widgets/NodeWidget.h similarity index 87% rename from src/widgets/nodewidget.h rename to src/widgets/NodeWidget.h index e7fa10d..0d3a781 100644 --- a/src/widgets/nodewidget.h +++ b/src/widgets/NodeWidget.h @@ -1,12 +1,13 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef NODEWIDGET_H -#define NODEWIDGET_H +#ifndef FEATHER_NODEWIDGET_H +#define FEATHER_NODEWIDGET_H -#include -#include #include +#include +#include + #include "appcontext.h" #include "model/NodeModel.h" #include "utils/nodes.h" @@ -44,10 +45,13 @@ signals: void nodeSourceChanged(NodeSource nodeSource); private: + void showContextMenu(const QPoint &pos, const FeatherNode &node); + FeatherNode selectedNode(); + + QScopedPointer ui; QSharedPointer m_ctx; - Ui::NodeWidget *ui; - NodeModel* m_customModel; - NodeModel* m_wsModel; + NodeModel *m_customModel; + NodeModel *m_wsModel; QTreeView *m_activeView; @@ -55,9 +59,6 @@ private: QAction *m_contextActionRemove; QAction *m_contextActionOpenStatusURL; QAction *m_contextActionCopy; - - void showContextMenu(const QPoint &pos, const FeatherNode &node); - FeatherNode selectedNode(); }; -#endif // NODEWIDGET_H +#endif // FEATHER_NODEWIDGET_H diff --git a/src/widgets/nodewidget.ui b/src/widgets/NodeWidget.ui similarity index 100% rename from src/widgets/nodewidget.ui rename to src/widgets/NodeWidget.ui diff --git a/src/widgets/PayToEdit.cpp b/src/widgets/PayToEdit.cpp index f19a6e3..1a7fc81 100644 --- a/src/widgets/PayToEdit.cpp +++ b/src/widgets/PayToEdit.cpp @@ -4,13 +4,15 @@ #include "PayToEdit.h" -#include +#include +#include +#include #include - -#include "utils/utils.h" -#include "model/ModelUtils.h" +#include #include "libwalletqt/WalletManager.h" +#include "model/ModelUtils.h" +#include "qrcode_scanner/QrCodeUtils.h" PayToEdit::PayToEdit(QWidget *parent) : QPlainTextEdit(parent) { @@ -74,6 +76,45 @@ bool PayToEdit::isOpenAlias() { return true; } +void PayToEdit::keyPressEvent(QKeyEvent *event) { + if (event->matches(QKeySequence::Paste)) { + this->pasteEvent(QApplication::clipboard()->mimeData()); + event->accept(); + } + + QPlainTextEdit::keyPressEvent(event); +} + +void PayToEdit::pasteEvent(const QMimeData *mimeData) { + QImage image; + if (mimeData->hasImage()) { + image = qvariant_cast(mimeData->imageData()); + } + else if (mimeData->hasUrls()) { + QList urlList = mimeData->urls(); + if (urlList.count() > 1) { + return; + } + QFileInfo file(urlList.at(0).toLocalFile()); + if (file.exists()) { + image = QImage{file.absoluteFilePath()}; + } + } + else { + return; + } + + if (image.isNull()) { + qDebug() << "Invalid image"; + return; + } + + image.convertTo(QImage::Format_RGB32); + QString result = QrCodeUtils::scanImage(image); + + dataPasted(result); +} + void PayToEdit::checkText() { m_errors.clear(); m_outputs.clear(); diff --git a/src/widgets/PayToEdit.h b/src/widgets/PayToEdit.h index 78ffd23..0d698df 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) @@ -48,10 +48,18 @@ public: void payToMany(); bool isOpenAlias(); +signals: + void dataPasted(const QString &data); + +protected: + void keyPressEvent(QKeyEvent *event) override; + private: void checkText(); void updateSize(); + void pasteEvent(const QMimeData *mimeData); + PartialTxOutput parseAddressAndAmount(const QString &line); quint64 parseAmount(QString amount); QString parseAddress(QString address); diff --git a/src/widgets/QrCodeWidget.cpp b/src/widgets/QrCodeWidget.cpp new file mode 100644 index 0000000..e82902b --- /dev/null +++ b/src/widgets/QrCodeWidget.cpp @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#include "QrCodeWidget.h" + +#include +#include +#include + +QrCodeWidget::QrCodeWidget(QWidget *parent) : QWidget(parent) +{ +} + +void QrCodeWidget::setQrCode(QrCode *qrCode) { + m_qrcode = qrCode; + + int k = m_qrcode->width(); + this->setMinimumSize(k*5, k*5); + + this->update(); +} + +void QrCodeWidget::paintEvent(QPaintEvent *event) { + // Implementation adapted from Electrum: qrcodewidget.py + if (!m_qrcode) { + return; + } + + QColor black{0, 0, 0, 255}; + QColor white{255, 255, 255, 255}; + QPen blackPen{black}; + blackPen.setJoinStyle(Qt::MiterJoin); + + QPainter painter(this); + + auto r = painter.viewport(); + int k = m_qrcode->width(); + int margin = 10; + int framesize = std::min(r.width(), r.height()); + int boxsize = int((framesize - (2*margin)) / k); + int size = k*boxsize; + int left = (framesize - size)/2; + int top = (framesize - size)/2; + + painter.setBrush(white); + painter.setPen(white); + painter.drawRect(0, 0, framesize, framesize); + + painter.setBrush(black); + painter.setPen(blackPen); + + unsigned char* dot = m_qrcode->data(); + for (int row = 0; row < k; row++) { + for (int column = 0; column < k; column++) { + if (quint8(0x01) == (static_cast(*dot++) & quint8(0x01))) { + painter.drawRect(int(left+(column*boxsize)), int(top+(row*boxsize)), boxsize - 1, boxsize - 1); + } + } + } +} \ No newline at end of file diff --git a/src/widgets/QrCodeWidget.h b/src/widgets/QrCodeWidget.h new file mode 100644 index 0000000..855936e --- /dev/null +++ b/src/widgets/QrCodeWidget.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2020-2021, The Monero Project. + +#ifndef FEATHER_QRCODEWIDGET_H +#define FEATHER_QRCODEWIDGET_H + +#include + +#include "qrcode/QrCode.h" + +class QrCodeWidget : public QWidget +{ + Q_OBJECT + +public: + explicit QrCodeWidget(QWidget *parent = nullptr); + void setQrCode(QrCode *qrCode); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + QrCode *m_qrcode = nullptr; +}; + +#endif //FEATHER_QRCODEWIDGET_H diff --git a/src/widgets/redditwidget.cpp b/src/widgets/RedditWidget.cpp similarity index 87% rename from src/widgets/redditwidget.cpp rename to src/widgets/RedditWidget.cpp index 386c086..6ebe60d 100644 --- a/src/widgets/redditwidget.cpp +++ b/src/widgets/RedditWidget.cpp @@ -1,21 +1,22 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. +#include "RedditWidget.h" +#include "ui_RedditWidget.h" + +#include #include #include -#include #include "model/RedditModel.h" -#include "redditwidget.h" -#include "ui_redditwidget.h" -#include "utils/utils.h" +#include "utils/Utils.h" #include "utils/config.h" -RedditWidget::RedditWidget(QWidget *parent) : - QWidget(parent), - ui(new Ui::RedditWidget), - m_model(new RedditModel(this)), - m_contextMenu(new QMenu(this)) +RedditWidget::RedditWidget(QWidget *parent) + : QWidget(parent) + , ui(new Ui::RedditWidget) + , m_model(new RedditModel(this)) + , m_contextMenu(new QMenu(this)) { ui->setupUi(this); ui->tableView->setModel(m_model); @@ -74,6 +75,4 @@ QString RedditWidget::getLink(const QString &permaLink) { return QString("https://%1%2").arg(redditFrontend, permaLink); } -RedditWidget::~RedditWidget() { - delete ui; -} +RedditWidget::~RedditWidget() = default; \ No newline at end of file diff --git a/src/widgets/redditwidget.h b/src/widgets/RedditWidget.h similarity index 84% rename from src/widgets/redditwidget.h rename to src/widgets/RedditWidget.h index d5d8fc1..0fdfdf9 100644 --- a/src/widgets/redditwidget.h +++ b/src/widgets/RedditWidget.h @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef REDDITWIDGET_H -#define REDDITWIDGET_H +#ifndef FEATHER_REDDITWIDGET_H +#define FEATHER_REDDITWIDGET_H +#include #include #include -#include #include "model/RedditModel.h" @@ -35,9 +35,9 @@ private: void copyUrl(); QString getLink(const QString &permaLink); - Ui::RedditWidget *ui; + QScopedPointer ui; RedditModel* const m_model; QMenu *m_contextMenu; }; -#endif // REDDITWIDGET_H +#endif // FEATHER_REDDITWIDGET_H diff --git a/src/widgets/redditwidget.ui b/src/widgets/RedditWidget.ui similarity index 100% rename from src/widgets/redditwidget.ui rename to src/widgets/RedditWidget.ui diff --git a/src/widgets/RestoreHeightWidget.cpp b/src/widgets/RestoreHeightWidget.cpp index b3250f1..0edb09e 100644 --- a/src/widgets/RestoreHeightWidget.cpp +++ b/src/widgets/RestoreHeightWidget.cpp @@ -10,8 +10,8 @@ #include "constants.h" RestoreHeightWidget::RestoreHeightWidget(QWidget *parent) - : QWidget(parent) - , ui(new Ui::RestoreHeightWidget) + : QWidget(parent) + , ui(new Ui::RestoreHeightWidget) { ui->setupUi(this); @@ -56,6 +56,4 @@ void RestoreHeightWidget::onRestoreHeightChanged() { ui->line_creationDate->setText(date.toString("yyyy-MM-dd")); } -RestoreHeightWidget::~RestoreHeightWidget() { - delete ui; -} \ No newline at end of file +RestoreHeightWidget::~RestoreHeightWidget() = default; \ No newline at end of file diff --git a/src/widgets/RestoreHeightWidget.h b/src/widgets/RestoreHeightWidget.h index a7f466b..1702f81 100644 --- a/src/widgets/RestoreHeightWidget.h +++ b/src/widgets/RestoreHeightWidget.h @@ -26,7 +26,7 @@ private slots: void onRestoreHeightChanged(); private: - Ui::RestoreHeightWidget *ui; + QScopedPointer ui; }; diff --git a/src/widgets/TickerWidget.cpp b/src/widgets/TickerWidget.cpp index d8458ff..ec99840 100644 --- a/src/widgets/TickerWidget.cpp +++ b/src/widgets/TickerWidget.cpp @@ -8,9 +8,9 @@ #include "utils/AppData.h" TickerWidgetBase::TickerWidgetBase(QWidget *parent, QSharedPointer ctx) - : QWidget(parent) - , ui(new Ui::TickerWidget) - , m_ctx(std::move(ctx)) + : QWidget(parent) + , ui(new Ui::TickerWidget) + , m_ctx(std::move(ctx)) { ui->setupUi(this); @@ -21,9 +21,7 @@ TickerWidgetBase::TickerWidgetBase(QWidget *parent, QSharedPointer c ui->tickerFiat->setText("..."); } -TickerWidgetBase::~TickerWidgetBase() { - delete ui; -} +TickerWidgetBase::~TickerWidgetBase() = default; void TickerWidgetBase::setTitle(const QString &title) { ui->tickerBox->setTitle(title); @@ -52,8 +50,8 @@ void TickerWidgetBase::setFiatText(double amount, const QString &fiatCurrency) { // BalanceTickerWidget BalanceTickerWidget::BalanceTickerWidget(QWidget *parent, QSharedPointer ctx, bool totalBalance) - : TickerWidgetBase(parent, std::move(ctx)) - , m_totalBalance(totalBalance) + : TickerWidgetBase(parent, std::move(ctx)) + , m_totalBalance(totalBalance) { if (totalBalance) this->setTitle("Total balance"); @@ -78,8 +76,8 @@ void BalanceTickerWidget::updateDisplay() { // PriceTickerWidget PriceTickerWidget::PriceTickerWidget(QWidget *parent, QSharedPointer ctx, QString symbol) - : TickerWidgetBase(parent, std::move(ctx)) - , m_symbol(std::move(symbol)) + : TickerWidgetBase(parent, std::move(ctx)) + , m_symbol(std::move(symbol)) { this->setTitle(m_symbol); diff --git a/src/widgets/TickerWidget.h b/src/widgets/TickerWidget.h index cd586bf..73a4f88 100644 --- a/src/widgets/TickerWidget.h +++ b/src/widgets/TickerWidget.h @@ -30,7 +30,7 @@ public slots: virtual void updateDisplay() = 0; private: - Ui::TickerWidget *ui; + QScopedPointer ui; protected: QSharedPointer m_ctx; diff --git a/src/widgets/xmrigwidget.cpp b/src/widgets/XMRigWidget.cpp similarity index 85% rename from src/widgets/xmrigwidget.cpp rename to src/widgets/XMRigWidget.cpp index aef8e7c..39cc22b 100644 --- a/src/widgets/xmrigwidget.cpp +++ b/src/widgets/XMRigWidget.cpp @@ -1,24 +1,26 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. +#include "XMRigWidget.h" +#include "ui_XMRigWidget.h" + +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include "xmrigwidget.h" -#include "ui_xmrigwidget.h" #include "utils/Icons.h" XMRigWidget::XMRigWidget(QSharedPointer ctx, QWidget *parent) - : QWidget(parent) - , ui(new Ui::XMRigWidget) - , m_ctx(std::move(ctx)) - , m_XMRig(new XmRig(Config::defaultConfigDir().path())) - , m_model(new QStandardItemModel(this)) - , m_contextMenu(new QMenu(this)) + : QWidget(parent) + , ui(new Ui::XMRigWidget) + , m_ctx(std::move(ctx)) + , m_XMRig(new XmRig(Config::defaultConfigDir().path())) + , m_model(new QStandardItemModel(this)) + , m_contextMenu(new QMenu(this)) { ui->setupUi(this); @@ -68,15 +70,22 @@ XMRigWidget::XMRigWidget(QSharedPointer ctx, QWidget *parent) // pools ui->poolFrame->show(); - ui->combo_pools->insertItems(0, m_pools); - auto preferredPool = config()->get(Config::xmrigPool).toString(); - if (m_pools.contains(preferredPool)) - ui->combo_pools->setCurrentIndex(m_pools.indexOf(preferredPool)); - else { - preferredPool = m_pools.at(0); - config()->set(Config::xmrigPool, preferredPool); - } - connect(ui->combo_pools, QOverload::of(&QComboBox::currentIndexChanged), this, &XMRigWidget::onPoolChanged); + this->updatePools(); + connect(ui->combo_pools, &QComboBox::currentTextChanged, this, &XMRigWidget::onPoolChanged); + + connect(ui->btn_poolConfig, &QPushButton::clicked, [this]{ + QStringList pools = config()->get(Config::pools).toStringList(); + bool ok; + QString poolStr = QInputDialog::getMultiLineText(this, "Pool addresses", "Set pool addresses (one per line):", pools.join("\n"), &ok); + if (!ok) { + return; + } + QStringList newPools = poolStr.split("\n"); + newPools.removeAll(""); + newPools.removeDuplicates(); + config()->set(Config::pools, newPools); + this->updatePools(); + }); // info ui->console->appendPlainText(QString("Detected %1 CPU threads.").arg(threads)); @@ -131,8 +140,10 @@ void XMRigWidget::onThreadsValueChanged(int threads) { ui->label_threads->setText(QString("CPU threads: %1").arg(m_threads)); } -void XMRigWidget::onPoolChanged(int pos) { - config()->set(Config::xmrigPool, m_pools.at(pos)); +void XMRigWidget::onPoolChanged(const QString &pool) { + if (!pool.isEmpty()) { + config()->set(Config::xmrigPool, pool); + } } void XMRigWidget::onBrowseClicked() { @@ -278,6 +289,24 @@ void XMRigWidget::showContextMenu(const QPoint &pos) { m_contextMenu->exec(ui->tableView->viewport()->mapToGlobal(pos)); } +void XMRigWidget::updatePools() { + QStringList pools = config()->get(Config::pools).toStringList(); + if (pools.isEmpty()) { + pools = m_defaultPools; + config()->set(Config::pools, pools); + } + ui->combo_pools->clear(); + ui->combo_pools->insertItems(0, pools); + + QString preferredPool = config()->get(Config::xmrigPool).toString(); + if (pools.contains(preferredPool)) { + ui->combo_pools->setCurrentIndex(pools.indexOf(preferredPool)); + } else { + preferredPool = pools.at(0); + config()->set(Config::xmrigPool, preferredPool); + } +} + void XMRigWidget::onSoloChecked(int state) { if(state == 2) { ui->poolFrame->hide(); @@ -300,6 +329,4 @@ QStandardItemModel *XMRigWidget::model() { return m_model; } -XMRigWidget::~XMRigWidget() { - delete ui; -} +XMRigWidget::~XMRigWidget() = default; \ No newline at end of file diff --git a/src/widgets/xmrigwidget.h b/src/widgets/XMRigWidget.h similarity index 72% rename from src/widgets/xmrigwidget.h rename to src/widgets/XMRigWidget.h index fd800a4..d4b9fff 100644 --- a/src/widgets/xmrigwidget.h +++ b/src/widgets/XMRigWidget.h @@ -1,16 +1,16 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef XMRIGWIDGET_H -#define XMRIGWIDGET_H +#ifndef FEATHER_XMRIGWIDGET_H +#define FEATHER_XMRIGWIDGET_H #include #include #include +#include "appcontext.h" #include "utils/xmrig.h" #include "utils/config.h" -#include "appcontext.h" namespace Ui { class XMRigWidget; @@ -42,7 +42,7 @@ public slots: private slots: void onBrowseClicked(); void onThreadsValueChanged(int date); - void onPoolChanged(int pos); + void onPoolChanged(const QString &pool); signals: void miningStarted(); @@ -50,8 +50,9 @@ signals: private: void showContextMenu(const QPoint &pos); + void updatePools(); - Ui::XMRigWidget *ui; + QScopedPointer ui; QSharedPointer m_ctx; XmRig * m_XMRig; QStandardItemModel *m_model; @@ -60,7 +61,7 @@ private: bool m_isMining = false; int m_threads; QStringList m_urls; - QStringList m_pools{"pool.xmr.pt:9000", "pool.supportxmr.com:9000", "mine.xmrpool.net:443", "xmrpool.eu:9999", "xmr-eu1.nanopool.org:14433", "pool.minexmr.com:6666", "us-west.minexmr.com:6666", "monerohash.com:9999", "cryptonote.social:5555", "cryptonote.social:5556"}; + QStringList m_defaultPools{"pool.xmr.pt:9000", "pool.supportxmr.com:9000", "mine.xmrpool.net:443", "xmrpool.eu:9999", "xmr-eu1.nanopool.org:14433", "pool.minexmr.com:6666", "us-west.minexmr.com:6666", "monerohash.com:9999", "cryptonote.social:5555", "cryptonote.social:5556"}; }; -#endif // REDDITWIDGET_H +#endif // FEATHER_XMRWIDGET_H diff --git a/src/widgets/xmrigwidget.ui b/src/widgets/XMRigWidget.ui similarity index 79% rename from src/widgets/xmrigwidget.ui rename to src/widgets/XMRigWidget.ui index 5fc6db9..8b1cb9f 100644 --- a/src/widgets/xmrigwidget.ui +++ b/src/widgets/XMRigWidget.ui @@ -13,7 +13,7 @@ Form - + 0 @@ -26,12 +26,12 @@ 0 - + 1 - + Mining @@ -121,7 +121,7 @@ - + Settings @@ -241,56 +241,52 @@ QFrame::Plain - + 0 0 - - 0 - 0 - - - - - - - - Pool - - - - - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + + + + Pool + + + + + + + + 0 + 0 + + + + + + + + Configure + + + + + + + Qt::Horizontal + + + + 414 + 20 + + + @@ -303,53 +299,42 @@ QFrame::Plain - + 0 0 - - 0 - 0 - - - - - - - - Node address - - - - - - - 127.0.0.1:18081 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + + + + Node address + + + + + + + 127.0.0.1:18081 + + + + + + + Qt::Horizontal + + + + 173 + 20 + + + @@ -464,12 +449,12 @@ - + Downloads - - + + @@ -493,7 +478,7 @@ - + Qt::CustomContextMenu diff --git a/src/wizard/PageHardwareDevice.cpp b/src/wizard/PageHardwareDevice.cpp index 6bdb48f..83a371c 100644 --- a/src/wizard/PageHardwareDevice.cpp +++ b/src/wizard/PageHardwareDevice.cpp @@ -13,6 +13,10 @@ PageHardwareDevice::PageHardwareDevice(WizardFields *fields, QWidget *parent) , m_fields(fields) { ui->setupUi(this); + + ui->combo_deviceType->addItem("Ledger Nano S", DeviceType::LEDGER_NANO_S); + ui->combo_deviceType->addItem("Ledger Nano X", DeviceType::LEDGER_NANO_X); + ui->combo_deviceType->addItem("Trezor Model T", DeviceType::TREZOR_MODEL_T); } void PageHardwareDevice::initializePage() { @@ -28,6 +32,7 @@ int PageHardwareDevice::nextId() const { } bool PageHardwareDevice::validatePage() { + m_fields->deviceType = static_cast(ui->combo_deviceType->currentData().toInt()); return true; } diff --git a/src/wizard/PageHardwareDevice.ui b/src/wizard/PageHardwareDevice.ui index 736edea..9d2cc95 100644 --- a/src/wizard/PageHardwareDevice.ui +++ b/src/wizard/PageHardwareDevice.ui @@ -22,13 +22,7 @@ - - - - Ledger Nano S/X - - - + diff --git a/src/wizard/PageNetwork.cpp b/src/wizard/PageNetwork.cpp index b359c88..ad4a16c 100644 --- a/src/wizard/PageNetwork.cpp +++ b/src/wizard/PageNetwork.cpp @@ -3,27 +3,55 @@ #include "PageNetwork.h" #include "ui_PageNetwork.h" -#include "WalletWizard.h" + +#include + #include "constants.h" +#include "Utils.h" +#include "WalletWizard.h" PageNetwork::PageNetwork(QWidget *parent) : QWizardPage(parent) , ui(new Ui::PageNetwork) + , m_portOpenWatcher(new QFutureWatcher>(this)) { ui->setupUi(this); this->setTitle("Welcome to Feather"); + ui->frame_nodeDetected->hide(); ui->frame_customNode->hide(); - ui->btnGroup_network->setId(ui->radio_autoConnect, 0); - ui->btnGroup_network->setId(ui->radio_custom, 1); + ui->btnGroup_network->setId(ui->radio_autoConnect, Button::AUTO); + ui->btnGroup_network->setId(ui->radio_custom, Button::CUSTOM); + + QPixmap infoIcon = QPixmap(":/assets/images/info2.svg"); + ui->infoIcon->setPixmap(infoIcon.scaledToWidth(32, Qt::SmoothTransformation)); connect(ui->btnGroup_network, &QButtonGroup::idClicked, [this](int id) { - ui->frame_customNode->setVisible(id == 1); + ui->frame_customNode->setVisible(id == Button::CUSTOM); }); connect(ui->line_customNode, &QLineEdit::textEdited, [this]{ this->completeChanged(); }); + + connect(m_portOpenWatcher, &QFutureWatcher>::finished, [this](){ + auto res = m_portOpenWatcher->result(); + bool nodeFound = res.first; + if (nodeFound) { + ui->frame_nodeDetected->show(); + ui->label_nodeDetected->setText(QString("Feather detected a local node on %1").arg(res.second)); + + ui->btnGroup_network->button(Button::CUSTOM)->click(); + ui->line_customNode->setText(res.second); + } + }); + + QFuture> portOpen = QtConcurrent::run([]{ + QString localhost = "127.0.0.1"; + quint16 port = Utils::getDefaultRpcPort(constants::networkType); + return QPair{Utils::portOpen(localhost, port), QString("%1:%2").arg(localhost, QString::number(port))}; + }); + m_portOpenWatcher->setFuture(portOpen); } int PageNetwork::nextId() const { @@ -34,7 +62,7 @@ bool PageNetwork::validatePage() { int id = ui->btnGroup_network->checkedId(); config()->set(Config::nodeSource, id); - if (id == 1) { + if (id == Button::CUSTOM) { NodeList nodeList; nodeList.addNode(ui->line_customNode->text(), constants::networkType, NodeList::Type::custom); } @@ -43,7 +71,7 @@ bool PageNetwork::validatePage() { } bool PageNetwork::isComplete() const { - if (ui->btnGroup_network->checkedId() == 0) { + if (ui->btnGroup_network->checkedId() == Button::AUTO) { return true; } diff --git a/src/wizard/PageNetwork.h b/src/wizard/PageNetwork.h index 4e68bb9..8e5999f 100644 --- a/src/wizard/PageNetwork.h +++ b/src/wizard/PageNetwork.h @@ -26,7 +26,13 @@ public: bool isComplete() const override; private: + enum Button { + AUTO=0, + CUSTOM + }; + Ui::PageNetwork *ui; + QFutureWatcher> *m_portOpenWatcher; }; #endif //FEATHER_WIZARDNETWORK_H diff --git a/src/wizard/PageNetwork.ui b/src/wizard/PageNetwork.ui index 6b54e7e..a151c99 100644 --- a/src/wizard/PageNetwork.ui +++ b/src/wizard/PageNetwork.ui @@ -70,6 +70,54 @@ + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + icon + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 0 + + + + + + + + Feather detected a local node on port 18081. + + + + + + diff --git a/src/wizard/PageOpenWallet.cpp b/src/wizard/PageOpenWallet.cpp index 4c0e188..c4eea2b 100644 --- a/src/wizard/PageOpenWallet.cpp +++ b/src/wizard/PageOpenWallet.cpp @@ -17,34 +17,15 @@ PageOpenWallet::PageOpenWallet(WalletKeysFilesModel *wallets, QWidget *parent) { ui->setupUi(this); - connect(ui->btnBrowse, &QPushButton::clicked, [this]{ - QString walletDir = config()->get(Config::walletDirectory).toString(); - QString path = QFileDialog::getOpenFileName(this, "Select your wallet file", walletDir, "Wallet file (*.keys)"); - if (path.isEmpty()) - return; - - QFileInfo infoPath(path); - if(!infoPath.isReadable()) { - QMessageBox::warning(this, "Cannot read wallet file", "Permission error."); - return; - } - - if (ui->openOnStartup->isChecked()) - config()->set(Config::autoOpenWalletPath, QString("%1%2").arg(constants::networkType).arg(path)); - - emit openWallet(path); - }); - this->setTitle("Open wallet file"); this->setButtonText(QWizard::FinishButton, "Open wallet"); - ui->walletTable->setSelectionBehavior(QAbstractItemView::SelectRows); - ui->walletTable->setContextMenuPolicy(Qt::CustomContextMenu); - m_keysProxy = new WalletKeysFilesProxyModel(this, constants::networkType); m_keysProxy->setSourceModel(m_walletKeysFilesModel); m_keysProxy->setSortRole(Qt::UserRole); + ui->walletTable->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->walletTable->setContextMenuPolicy(Qt::CustomContextMenu); ui->walletTable->setModel(m_keysProxy); ui->walletTable->hideColumn(WalletKeysFilesModel::NetworkType); ui->walletTable->hideColumn(WalletKeysFilesModel::Path); @@ -52,18 +33,21 @@ PageOpenWallet::PageOpenWallet(WalletKeysFilesModel *wallets, QWidget *parent) ui->walletTable->header()->setSectionResizeMode(WalletKeysFilesModel::FileName, QHeaderView::Stretch); ui->walletTable->setSortingEnabled(true); ui->walletTable->sortByColumn(WalletKeysFilesModel::Modified, Qt::DescendingOrder); - ui->walletTable->show(); connect(ui->walletTable->selectionModel(), &QItemSelectionModel::currentRowChanged, [this](QModelIndex current, QModelIndex prev){ this->updatePath(); }); - connect(ui->walletTable, &QTreeView::doubleClicked, [this]{ - // Simulate next button click - QWizard *wizard = this->wizard(); - if (wizard) { - wizard->button(QWizard::FinishButton)->click(); - } + connect(ui->walletTable, &QTreeView::doubleClicked, this, &PageOpenWallet::nextPage); + + connect(ui->btnBrowse, &QPushButton::clicked, [this]{ + QString walletDir = config()->get(Config::walletDirectory).toString(); + m_walletFile = QFileDialog::getOpenFileName(this, "Select your wallet file", walletDir, "Wallet file (*.keys)"); + if (m_walletFile.isEmpty()) + return; + this->nextPage(); }); + + this->updatePath(); } void PageOpenWallet::initializePage() { @@ -77,6 +61,7 @@ void PageOpenWallet::updatePath() { return; } + m_walletFile = index.model()->data(index.siblingAtColumn(WalletKeysFilesModel::Path), Qt::UserRole).toString(); QString path = index.model()->data(index.siblingAtColumn(WalletKeysFilesModel::Path), Qt::DisplayRole).toString(); ui->linePath->setText(path); } @@ -85,17 +70,30 @@ int PageOpenWallet::nextId() const { return -1; } +void PageOpenWallet::nextPage() { + // Simulate next button click + QWizard *wizard = this->wizard(); + if (wizard) { + wizard->button(QWizard::FinishButton)->click(); + } +} + bool PageOpenWallet::validatePage() { - QModelIndex index = ui->walletTable->currentIndex(); - if(!index.isValid()) { - QMessageBox::warning(this, "Wallet not selected", "Please select a wallet from the list."); + if (m_walletFile.isEmpty()) { + QMessageBox::warning(this, "No wallet file selected", "Please select a wallet from the list."); return false; } - QString walletPath = index.model()->data(index.siblingAtColumn(WalletKeysFilesModel::Column::Path), Qt::UserRole).toString(); - auto autoWallet = ui->openOnStartup->isChecked() ? QString("%1%2").arg(constants::networkType).arg(walletPath) : ""; + QFileInfo infoPath(m_walletFile); + if (!infoPath.isReadable()) { + QMessageBox::warning(this, "Permission error", "Cannot read wallet file."); + return false; + } + + // Clear autoOpen if openOnStartup is not checked + auto autoWallet = ui->openOnStartup->isChecked() ? QString("%1%2").arg(constants::networkType).arg(m_walletFile) : ""; config()->set(Config::autoOpenWalletPath, autoWallet); - emit openWallet(walletPath); + emit openWallet(m_walletFile); return true; } diff --git a/src/wizard/PageOpenWallet.h b/src/wizard/PageOpenWallet.h index 1385203..b41e0e3 100644 --- a/src/wizard/PageOpenWallet.h +++ b/src/wizard/PageOpenWallet.h @@ -28,6 +28,9 @@ public: signals: void openWallet(QString path); +private slots: + void nextPage(); + private: void updatePath(); @@ -35,6 +38,7 @@ private: WalletKeysFilesModel *m_walletKeysFilesModel; WalletKeysFilesProxyModel *m_keysProxy; QStandardItemModel *m_model; + QString m_walletFile; }; #endif //FEATHER_OPENWALLET_H diff --git a/src/wizard/PageSetPassword.cpp b/src/wizard/PageSetPassword.cpp index 11366c4..4f0d6e5 100644 --- a/src/wizard/PageSetPassword.cpp +++ b/src/wizard/PageSetPassword.cpp @@ -29,7 +29,7 @@ PageSetPassword::PageSetPassword(WizardFields *fields, QWidget *parent) void PageSetPassword::initializePage() { this->setTitle(m_fields->modeText); ui->line_password->setText(""); - ui->line_password->setText(""); + ui->line_confirmPassword->setText(""); } bool PageSetPassword::validatePage() { diff --git a/src/wizard/PageSetRestoreHeight.cpp b/src/wizard/PageSetRestoreHeight.cpp index 7bd13fa..ca9955a 100644 --- a/src/wizard/PageSetRestoreHeight.cpp +++ b/src/wizard/PageSetRestoreHeight.cpp @@ -29,9 +29,6 @@ PageSetRestoreHeight::PageSetRestoreHeight(WizardFields *fields, QWidget *parent ui->warningIcon->setPixmap(pixmap2.scaledToWidth(32, Qt::SmoothTransformation)); ui->infoIcon->setPixmap(pixmap2.scaledToWidth(32, Qt::SmoothTransformation)); - ui->frame_scanWarning->hide(); - ui->frame_walletAgeWarning->hide(); - connect(ui->line_creationDate, &QLineEdit::textEdited, [this]{ this->onCreationDateEdited(); this->completeChanged(); @@ -46,6 +43,8 @@ void PageSetRestoreHeight::initializePage() { this->setTitle("Restore height"); ui->line_creationDate->setText(""); ui->line_restoreHeight->setText(""); + ui->frame_scanWarning->hide(); + ui->frame_walletAgeWarning->hide(); } void PageSetRestoreHeight::onCreationDateEdited() { @@ -71,10 +70,7 @@ void PageSetRestoreHeight::onCreationDateEdited() { void PageSetRestoreHeight::onRestoreHeightEdited() { int restoreHeight = ui->line_restoreHeight->text().toInt(); if (restoreHeight == 0) { - ui->frame_walletAgeWarning->hide(); - ui->frame_scanWarning->hide(); - ui->line_creationDate->setText(""); - return; + restoreHeight = 1; } QDateTime date = appData()->restoreHeights[constants::networkType]->heightToDate(restoreHeight); @@ -96,7 +92,7 @@ void PageSetRestoreHeight::showWalletAgeWarning(const QDateTime &date) { } bool PageSetRestoreHeight::validatePage() { - m_fields->restoreHeight = ui->line_restoreHeight->text().toInt(); + m_fields->restoreHeight = std::max(1, ui->line_restoreHeight->text().toInt()); return true; } diff --git a/src/wizard/PageWalletFile.cpp b/src/wizard/PageWalletFile.cpp index 4171ae4..a0098c8 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 @@ -98,7 +98,14 @@ QString PageWalletFile::defaultWalletName() { do { QString walletStr = QString("wallet_%1"); if (m_fields->mode == WizardMode::CreateWalletFromDevice) { - walletStr = QString("ledger_%1"); + switch (m_fields->deviceType) { + case DeviceType::LEDGER_NANO_S: + case DeviceType::LEDGER_NANO_X: + walletStr = QString("ledger_%1"); + break; + case DeviceType::TREZOR_MODEL_T: + walletStr = QString("trezor_%1"); + } } walletName = walletStr.arg(count); count++; diff --git a/src/wizard/PageWalletSeed.h b/src/wizard/PageWalletSeed.h index 5ed406d..f1abdba 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 5501b6d..2529ed2 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" @@ -93,7 +93,17 @@ void WalletWizard::onCreateWallet() { restoreHeight = m_wizardFields.restoreHeight; } - emit createWalletFromDevice(walletPath, m_wizardFields.password, restoreHeight); + QString deviceName; + switch (m_wizardFields.deviceType) { + case DeviceType::LEDGER_NANO_S: + case DeviceType::LEDGER_NANO_X: + deviceName = "Ledger"; + break; + case DeviceType::TREZOR_MODEL_T: + deviceName = "Trezor"; + } + + emit createWalletFromDevice(walletPath, m_wizardFields.password, deviceName, restoreHeight); return; } diff --git a/src/wizard/WalletWizard.h b/src/wizard/WalletWizard.h index 2c2db39..377ea38 100644 --- a/src/wizard/WalletWizard.h +++ b/src/wizard/WalletWizard.h @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2021, The Monero Project. -#ifndef WALLETWIZARD_H -#define WALLETWIZARD_H +#ifndef FEATHER_WALLETWIZARD_H +#define FEATHER_WALLETWIZARD_H #include #include @@ -21,6 +21,12 @@ enum WizardMode { CreateWalletFromDevice }; +enum DeviceType { + LEDGER_NANO_S = 0, + LEDGER_NANO_X, + TREZOR_MODEL_T +}; + struct WizardFields { QString walletName; QString walletDir; @@ -34,6 +40,7 @@ struct WizardFields { WizardMode mode; int restoreHeight = 0; SeedType seedType; + DeviceType deviceType; }; class WalletWizard : public QWizard @@ -63,7 +70,7 @@ signals: void openWallet(QString path, QString password); void defaultWalletDirChanged(QString walletDir); - void createWalletFromDevice(const QString &path, const QString &password, int restoreHeight); + void createWalletFromDevice(const QString &path, const QString &password, const QString &deviceName, int restoreHeight); void createWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, bool deterministic = false); void createWallet(FeatherSeed seed, const QString &path, const QString &password, const QString &seedOffset = ""); @@ -75,4 +82,4 @@ private: WizardFields m_wizardFields; }; -#endif // WALLETWIZARD_H +#endif // FEATHER_WALLETWIZARD_H