Merge pull request 'Misc changes' (#344) from tobtoht/feather:wizard_redesign into master

Reviewed-on: https://git.featherwallet.org/feather/feather/pulls/344
This commit is contained in:
tobtoht 2021-03-14 21:14:56 +00:00
commit a3351ba6b2
46 changed files with 562 additions and 258 deletions

View file

@ -29,7 +29,7 @@ if(DEBUG)
set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_VERBOSE_MAKEFILE ON)
endif() endif()
set(MONERO_HEAD "e175e02b9b8d289bccab3ff0f3fd70c4dbf8c71f") set(MONERO_HEAD "52acbb68c1a4cc67ee539325a8febc2c144596c4")
set(BUILD_GUI_DEPS ON) set(BUILD_GUI_DEPS ON)
set(ARCH "x86-64") set(ARCH "x86-64")
set(BUILD_64 ON) set(BUILD_64 ON)

View file

@ -42,7 +42,7 @@ RUN git clone -b v1.2.11 --depth 1 https://github.com/madler/zlib && \
make -j$THREADS install && \ make -j$THREADS install && \
rm -rf $(pwd) rm -rf $(pwd)
RUN git clone -b tor-0.4.5.5-rc --depth 1 https://git.torproject.org/tor.git && \ RUN git clone -b tor-0.4.5.6 --depth 1 https://git.torproject.org/tor.git && \
cd tor && \ cd tor && \
git reset --hard b36a00e9a9d3eb4b2949951afaa72e45fb7e68cd && \ git reset --hard b36a00e9a9d3eb4b2949951afaa72e45fb7e68cd && \
./autogen.sh && \ ./autogen.sh && \

View file

@ -146,8 +146,7 @@ RUN wget https://github.com/libevent/libevent/releases/download/release-2.1.11-s
make -j$THREADS install && \ make -j$THREADS install && \
rm -rf $(pwd) rm -rf $(pwd)
ENV TOR_VERSION=0.4.5.5-rc RUN git clone -b tor-0.4.5.6 --depth 1 https://git.torproject.org/tor.git && \
RUN git clone -b tor-0.4.5.5-rc --depth 1 https://git.torproject.org/tor.git && \
cd tor && \ cd tor && \
git reset --hard b36a00e9a9d3eb4b2949951afaa72e45fb7e68cd && \ git reset --hard b36a00e9a9d3eb4b2949951afaa72e45fb7e68cd && \
./autogen.sh && \ ./autogen.sh && \

View file

@ -1,27 +1,19 @@
# Feather - a free Monero desktop wallet # Feather - a free Monero desktop wallet
[![Build Status](https://build.featherwallet.org/api/badges/feather/feather/status.svg)](https://build.featherwallet.org/feather/feather) Feather is a free, open-source Monero wallet for Linux, Tails, macOS and Windows. It is written in C++ with the Qt framework.
Feather is a free, open-source Monero client Linux with ports for Mac OS and Windows written in C++ with the Qt framework. Copyright (c) 2020-2021, The Monero Project.
## Development resources ## Resources
* Web: [featherwallet.org](https://featherwallet.org) * Web: [featherwallet.org](https://featherwallet.org)
* Git: [git.featherwallet.org/feather/feather](https://git.featherwallet.org/feather/feather) * Git: [git.featherwallet.org/feather/feather](https://git.featherwallet.org/feather/feather)
* Mail: dev@featherwallet.org * Mail: dev@featherwallet.org
* IRC: `#feather` on OFTC * IRC: `#feather` on OFTC
* Development builds: [build.featherwallet.org/files](https://build.featherwallet.org/files/) * Development builds: [build.featherwallet.org/files](https://build.featherwallet.org/files/)
Copyright (c) 2020-2021 The Monero Project.
## Compiling Feather from source ## Compiling Feather from source
Feather uses Monero, as such it requires the same dependencies as outlined in [Monero's README](https://github.com/monero-project/monero#compiling-monero-from-source). Additionally, Feather uses: See [BUILDING.md](https://git.featherwallet.org/feather/feather/src/branch/master/BUILDING.md) for information on how to build from source.
- Qt 5.15.0
- libqrencode
- openpgp
See [BUILDING.md](https://git.featherwallet.org/feather/feather/src/branch/master/BUILDING.md) for information on how to compile a build.
## Supporting the project ## Supporting the project

2
monero

@ -1 +1 @@
Subproject commit e175e02b9b8d289bccab3ff0f3fd70c4dbf8c71f Subproject commit 52acbb68c1a4cc67ee539325a8febc2c144596c4

View file

@ -89,19 +89,12 @@ void CoinsWidget::showContextMenu(const QPoint &point) {
menu->addAction(m_thawAllSelectedAction); menu->addAction(m_thawAllSelectedAction);
} }
else { else {
QModelIndex index = ui->coins->indexAt(point); CoinsInfo* c = this->currentEntry();
if (!index.isValid()) { if (!c) return;
return;
}
int row = m_proxyModel->mapToSource(index).row(); bool isSpent = c->spent();
bool isFrozen = c->frozen();
bool isSpent, isFrozen, isUnlocked; bool isUnlocked = c->unlocked();
m_coins->coin(row, [&isSpent, &isFrozen, &isUnlocked](CoinsInfo &c) {
isSpent = c.spent();
isFrozen = c.frozen();
isUnlocked = c.unlocked();
});
menu->addMenu(m_copyMenu); menu->addMenu(m_copyMenu);
@ -166,44 +159,28 @@ void CoinsWidget::thawAllSelected() {
} }
void CoinsWidget::viewOutput() { void CoinsWidget::viewOutput() {
QModelIndex index = ui->coins->currentIndex(); CoinsInfo* c = this->currentEntry();
if (!c) return;
int row = m_proxyModel->mapToSource(index).row(); auto * dialog = new OutputInfoDialog(c, this);
QPointer<CoinsInfo> c; dialog->show();
m_coins->coin(row, [&c](CoinsInfo &cInfo) {
c = &cInfo;
});
if (c) {
auto * dialog = new OutputInfoDialog(c, this);
dialog->show();
}
} }
void CoinsWidget::onSweepOutput() { void CoinsWidget::onSweepOutput() {
QModelIndex index = ui->coins->currentIndex(); CoinsInfo* c = this->currentEntry();
int row = m_proxyModel->mapToSource(index).row(); if (!c) return;
QString keyImage; QString keyImage = c->keyImage();
bool keyImageKnown;
m_coins->coin(row, [&keyImage, &keyImageKnown](CoinsInfo &c) {
keyImageKnown = c.keyImageKnown();
keyImage = c.keyImage();
});
qCritical() << "key image: " << keyImage; if (!c->keyImageKnown()) {
if (!keyImageKnown) {
QMessageBox::warning(this, "Unable to sweep output", "Unable to sweep output: key image unknown"); QMessageBox::warning(this, "Unable to sweep output", "Unable to sweep output: key image unknown");
return; return;
} }
auto * dialog = new OutputSweepDialog(this); auto *dialog = new OutputSweepDialog(this, c);
int ret = dialog->exec(); int ret = dialog->exec();
if (!ret) return; if (!ret) return;
qCritical() << "key image: " << keyImage;
emit sweepOutput(keyImage, dialog->address(), dialog->churn(), dialog->outputs()); emit sweepOutput(keyImage, dialog->address(), dialog->churn(), dialog->outputs());
dialog->deleteLater(); dialog->deleteLater();
} }
@ -213,39 +190,46 @@ void CoinsWidget::resetModel() {
} }
void CoinsWidget::copy(copyField field) { void CoinsWidget::copy(copyField field) {
QModelIndex index = ui->coins->currentIndex(); CoinsInfo* c = this->currentEntry();
int row = m_proxyModel->mapToSource(index).row(); if (!c) return;
QString data; QString data;
m_coins->coin(row, [field, &data](CoinsInfo &c) { switch (field) {
switch (field) { case PubKey:
case PubKey: data = c->pubKey();
data = c.pubKey(); break;
break; case KeyImage:
case KeyImage: data = c->keyImage();
data = c.keyImage(); break;
break; case TxID:
case TxID: data = c->hash();
data = c.hash(); break;
break; case Address:
case Address: data = c->address();
data = c.address(); break;
break; case Label:
case Label: data = c->addressLabel();
data = c.addressLabel(); break;
break; case Height:
case Height: data = QString::number(c->blockHeight());
data = QString::number(c.blockHeight()); break;
break; case Amount:
case Amount: data = c->displayAmount();
data = c.displayAmount(); break;
break; }
}
});
Utils::copyToClipboard(data); Utils::copyToClipboard(data);
} }
CoinsInfo* CoinsWidget::currentEntry() {
QModelIndexList list = ui->coins->selectionModel()->selectedRows();
if (list.size() == 1) {
return m_model->entryFromIndex(m_proxyModel->mapToSource(list.first()));
} else {
return nullptr;
}
}
CoinsWidget::~CoinsWidget() { CoinsWidget::~CoinsWidget() {
delete ui; delete ui;
} }

View file

@ -73,6 +73,7 @@ private:
void showContextMenu(const QPoint & point); void showContextMenu(const QPoint & point);
void copy(copyField field); void copy(copyField field);
CoinsInfo* currentEntry();
}; };

View file

@ -9,6 +9,7 @@
AboutDialog::AboutDialog(QWidget *parent) AboutDialog::AboutDialog(QWidget *parent)
: QDialog(parent) : QDialog(parent)
, ui(new Ui::AboutDialog) , ui(new Ui::AboutDialog)
, m_model(new QStringListModel(this))
{ {
ui->setupUi(this); ui->setupUi(this);
this->setWindowIcon(QIcon("://assets/images/appicons/64x64.png")); this->setWindowIcon(QIcon("://assets/images/appicons/64x64.png"));
@ -26,8 +27,6 @@ AboutDialog::AboutDialog(QWidget *parent)
auto ack_text = Utils::barrayToString(ack); auto ack_text = Utils::barrayToString(ack);
ui->ackText->setText(ack_text); ui->ackText->setText(ack_text);
m_model = new QStringListModel(this);
QString contributors = Utils::barrayToString(Utils::fileOpenQRC(":assets/contributors.txt")); QString contributors = Utils::barrayToString(Utils::fileOpenQRC(":assets/contributors.txt"));
QStringList contributor_list = contributors.split("\n"); QStringList contributor_list = contributors.split("\n");
m_model->setStringList(contributor_list); m_model->setStringList(contributor_list);

View file

@ -3,15 +3,19 @@
#include "ui_outputsweepdialog.h" #include "ui_outputsweepdialog.h"
#include "outputsweepdialog.h" #include "outputsweepdialog.h"
#include "libwalletqt/WalletManager.h"
OutputSweepDialog::OutputSweepDialog(QWidget *parent) OutputSweepDialog::OutputSweepDialog(QWidget *parent, CoinsInfo* coin)
: QDialog(parent) : QDialog(parent)
, ui(new Ui::OutputSweepDialog) , ui(new Ui::OutputSweepDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
m_amount = coin->amount();
connect(ui->checkBox_churn, &QCheckBox::toggled, [&](bool toggled){ connect(ui->checkBox_churn, &QCheckBox::toggled, [&](bool toggled){
ui->lineEdit_address->setEnabled(!toggled); ui->lineEdit_address->setEnabled(!toggled);
ui->lineEdit_address->setText(toggled ? "Primary address" : "");
}); });
connect(ui->buttonBox, &QDialogButtonBox::accepted, [&](){ connect(ui->buttonBox, &QDialogButtonBox::accepted, [&](){
@ -20,6 +24,19 @@ OutputSweepDialog::OutputSweepDialog(QWidget *parent)
m_outputs = ui->spinBox_numOutputs->value(); m_outputs = ui->spinBox_numOutputs->value();
}); });
connect(ui->spinBox_numOutputs, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value){
if (value == 1) {
ui->label_split->setText("");
return;
}
QString origAmount = WalletManager::displayAmount(m_amount);
QString splitAmount = WalletManager::displayAmount(m_amount / value);
ui->label_split->setText(QString("%1 XMR ≈ %2x %3 XMR").arg(origAmount, QString::number(value), splitAmount));
});
ui->label_split->setText("");
this->adjustSize(); this->adjustSize();
} }

View file

@ -5,6 +5,7 @@
#define FEATHER_OUTPUTSWEEPDIALOG_H #define FEATHER_OUTPUTSWEEPDIALOG_H
#include <QDialog> #include <QDialog>
#include "libwalletqt/CoinsInfo.h"
namespace Ui { namespace Ui {
class OutputSweepDialog; class OutputSweepDialog;
@ -15,7 +16,7 @@ class OutputSweepDialog : public QDialog
Q_OBJECT Q_OBJECT
public: public:
explicit OutputSweepDialog(QWidget *parent = nullptr); explicit OutputSweepDialog(QWidget *parent, CoinsInfo* coin);
~OutputSweepDialog() override; ~OutputSweepDialog() override;
QString address(); QString address();
@ -25,6 +26,8 @@ public:
private: private:
Ui::OutputSweepDialog *ui; Ui::OutputSweepDialog *ui;
uint64_t m_amount;
QString m_address; QString m_address;
bool m_churn; bool m_churn;
int m_outputs; int m_outputs;

View file

@ -6,16 +6,22 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>623</width> <width>720</width>
<height>231</height> <height>193</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Sweep output</string> <string>Sweep output</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>10</number>
</property>
<item> <item>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
@ -36,48 +42,60 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBox_churn">
<property name="text">
<string>Send to self (churn)</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="checkBox_churn"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="text"> <item>
<string>Send to self (churn)</string> <widget class="QLabel" name="label_2">
</property> <property name="text">
</widget> <string>Number of outputs:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_numOutputs">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>16</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QLabel" name="label_split">
<property name="title"> <property name="enabled">
<string>Advanced options</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool> <bool>false</bool>
</property> </property>
<layout class="QFormLayout" name="formLayout_2"> <property name="text">
<property name="fieldGrowthPolicy"> <string>1.000000000000 XMR ≈ 5x 0.200000000000 XMR</string>
<enum>QFormLayout::ExpandingFieldsGrow</enum> </property>
</property> <property name="textInteractionFlags">
<item row="0" column="0"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<widget class="QLabel" name="label_2"> </property>
<property name="text">
<string>Number of outputs:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spinBox_numOutputs">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>16</number>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item>

View file

@ -7,10 +7,15 @@
#include "libwalletqt/CoinsInfo.h" #include "libwalletqt/CoinsInfo.h"
#include "libwalletqt/WalletManager.h" #include "libwalletqt/WalletManager.h"
#include "libwalletqt/Transfer.h" #include "libwalletqt/Transfer.h"
#include "libwalletqt/TransactionHistory.h"
#include "utils.h" #include "utils.h"
#include "utils/ColorScheme.h" #include "utils/ColorScheme.h"
#include "model/ModelUtils.h"
#include "config.h"
#include "appcontext.h"
#include <QMessageBox> #include <QMessageBox>
#include <QScrollBar>
TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent) TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent)
: QDialog(parent) : QDialog(parent)
@ -23,32 +28,26 @@ TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *tx
m_txid = txInfo->hash(); m_txid = txInfo->hash();
ui->label_txid->setText(m_txid); ui->label_txid->setText(m_txid);
QString txKey = m_wallet->getTxKey(txInfo->hash()); m_txKey = m_wallet->getTxKey(txInfo->hash());
if (txKey.isEmpty()) { if (m_txKey.isEmpty()) {
ui->btn_CopyTxKey->setEnabled(false); ui->btn_CopyTxKey->setEnabled(false);
ui->btn_CopyTxKey->setToolTip("Transaction key unknown"); ui->btn_CopyTxKey->setToolTip("Transaction key unknown");
} }
m_txKey = txKey;
connect(ui->btn_CopyTxKey, &QPushButton::pressed, this, &TransactionInfoDialog::copyTxKey); connect(ui->btn_CopyTxKey, &QPushButton::pressed, this, &TransactionInfoDialog::copyTxKey);
connect(ui->btn_createTxProof, &QPushButton::pressed, this, &TransactionInfoDialog::createTxProof); connect(ui->btn_createTxProof, &QPushButton::pressed, this, &TransactionInfoDialog::createTxProof);
QString blockHeight = QString::number(txInfo->blockHeight()); connect(m_wallet, &Wallet::newBlock, this, &TransactionInfoDialog::updateData);
if (blockHeight == "0")
blockHeight = "Unconfirmed";
ui->label_status->setText(QString("Status: %1 confirmations").arg(txInfo->confirmations())); this->setData(txInfo);
ui->label_date->setText(QString("Date: %1").arg(txInfo->timestamp().toString("yyyy-MM-dd HH:mm")));
ui->label_blockHeight->setText(QString("Block height: %1").arg(blockHeight));
QString direction = txInfo->direction() == TransactionInfo::Direction_In ? "received" : "sent"; if (AppContext::txCache.contains(txInfo->hash()) && (txInfo->isFailed() || txInfo->isPending()) && txInfo->direction() != TransactionInfo::Direction_In) {
ui->label_amount->setText(QString("Amount %1: %2").arg(direction, txInfo->displayAmount())); connect(ui->btn_rebroadcastTx, &QPushButton::pressed, [this]{
emit resendTranscation(m_txid);
QString fee = txInfo->fee().isEmpty() ? "n/a" : txInfo->fee(); });
ui->label_fee->setText(QString("Fee: %1").arg(txInfo->isCoinbase() ? WalletManager::displayAmount(0) : fee)); } else {
ui->label_unlockTime->setText(QString("Unlock time: %1 (height)").arg(txInfo->unlockTime())); ui->btn_rebroadcastTx->hide();
}
qDebug() << m_wallet->coins()->coins_from_txid(txInfo->hash());
QTextCursor cursor = ui->destinations->textCursor(); QTextCursor cursor = ui->destinations->textCursor();
for (const auto& transfer : txInfo->transfers()) { for (const auto& transfer : txInfo->transfers()) {
@ -59,15 +58,66 @@ TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *tx
cursor.insertText(QString(" %1").arg(amount), QTextCharFormat()); cursor.insertText(QString(" %1").arg(amount), QTextCharFormat());
cursor.insertBlock(); cursor.insertBlock();
} }
if (txInfo->transfers().size() == 0) { if (txInfo->transfers().size() == 0) {
ui->frameDestinations->hide(); ui->frameDestinations->hide();
} }
m_txProofDialog = new TxProofDialog(this, m_wallet, txInfo); m_txProofDialog = new TxProofDialog(this, m_wallet, 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(); 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").arg(direction, tx->displayAmount()));
QString fee = tx->fee().isEmpty() ? "n/a" : tx->fee();
ui->label_fee->setText(QString("Fee: %1 XMR").arg(tx->isCoinbase() ? WalletManager::displayAmount(0) : fee));
}
void TransactionInfoDialog::updateData() {
if (!m_wallet) return;
TransactionInfo* tx = m_wallet->history()->transaction(m_txid);
if (!tx) return;
this->setData(tx);
}
void TransactionInfoDialog::copyTxKey() { void TransactionInfoDialog::copyTxKey() {
Utils::copyToClipboard(m_txKey); Utils::copyToClipboard(m_txKey);
} }

View file

@ -24,9 +24,14 @@ public:
explicit TransactionInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent = nullptr); explicit TransactionInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent = nullptr);
~TransactionInfoDialog() override; ~TransactionInfoDialog() override;
signals:
void resendTranscation(const QString &txid);
private: private:
void copyTxKey(); void copyTxKey();
void createTxProof(); void createTxProof();
void setData(TransactionInfo* tx);
void updateData();
Ui::TransactionInfoDialog *ui; Ui::TransactionInfoDialog *ui;
@ -35,6 +40,7 @@ private:
Wallet *m_wallet; Wallet *m_wallet;
QString m_txKey; QString m_txKey;
QString m_txid; QString m_txid;
QTimer m_updateTimer;
}; };
#endif //FEATHER_TRANSACTIONINFODIALOG_H #endif //FEATHER_TRANSACTIONINFODIALOG_H

View file

@ -6,17 +6,14 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>829</width> <width>929</width>
<height>493</height> <height>631</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Transaction</string> <string>Transaction</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
</item>
<item> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
@ -37,81 +34,46 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_2"> <widget class="QLabel" name="label_status">
<item> <property name="text">
<widget class="QLabel" name="label_status"> <string>Status:</string>
<property name="text"> </property>
<string>Status:</string> <property name="textInteractionFlags">
</property> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_date">
<property name="text">
<string>Date:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_blockHeight">
<property name="text">
<string>Block height:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_3"> <widget class="QLabel" name="label_lock">
<item> <property name="text">
<widget class="QLabel" name="label_amount"> <string>Lock: </string>
<property name="text"> </property>
<string>Amount received:</string> <property name="textInteractionFlags">
</property> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<property name="textInteractionFlags"> </property>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> </widget>
</property> </item>
</widget> <item>
</item> <widget class="QLabel" name="label_amount">
<item> <property name="text">
<widget class="QLabel" name="label_fee"> <string>Amount received:</string>
<property name="text"> </property>
<string>Fee: </string> <property name="textInteractionFlags">
</property> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<property name="textInteractionFlags"> </property>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> </widget>
</property> </item>
</widget> <item>
</item> <widget class="QLabel" name="label_fee">
<item> <property name="text">
<widget class="QLabel" name="label_unlockTime"> <string>Fee: </string>
<property name="text"> </property>
<string>Unlock time:</string> <property name="textInteractionFlags">
</property> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<property name="textInteractionFlags"> </property>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> </widget>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</item> </item>
@ -154,6 +116,12 @@
</property> </property>
<item> <item>
<widget class="QLabel" name="label_destinations"> <widget class="QLabel" name="label_destinations">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>Destinations:</string> <string>Destinations:</string>
</property> </property>
@ -162,17 +130,11 @@
<item> <item>
<widget class="QTextEdit" name="destinations"> <widget class="QTextEdit" name="destinations">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="readOnly"> <property name="readOnly">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -187,7 +149,7 @@
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="sizeType"> <property name="sizeType">
<enum>QSizePolicy::Minimum</enum> <enum>QSizePolicy::Expanding</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
@ -213,6 +175,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="btn_rebroadcastTx">
<property name="text">
<string>Rebroadcast Transaction</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">

View file

@ -53,6 +53,9 @@
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
</property> </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
@ -67,6 +70,9 @@
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
</property> </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
@ -88,6 +94,9 @@
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
</property> </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -106,6 +115,9 @@
<property name="text"> <property name="text">
<string>Description:</string> <string>Description:</string>
</property> </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@ -113,6 +125,9 @@
<property name="text"> <property name="text">
<string>Size: </string> <string>Size: </string>
</property> </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@ -120,6 +135,9 @@
<property name="text"> <property name="text">
<string>Unlock time: </string> <string>Unlock time: </string>
</property> </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@ -127,6 +145,9 @@
<property name="text"> <property name="text">
<string>Ringsize:</string> <string>Ringsize:</string>
</property> </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>

View file

@ -26,7 +26,7 @@ ViewOnlyDialog::ViewOnlyDialog(AppContext *ctx, QWidget *parent)
} }
void ViewOnlyDialog::onWriteViewOnlyWallet(){ void ViewOnlyDialog::onWriteViewOnlyWallet(){
QString fn = QFileDialog::getSaveFileName(this, "Save .keys wallet file", QDir::homePath(), "Monero wallet (*.keys)"); QString fn = QFileDialog::getSaveFileName(this, "Save .keys wallet file", Utils::defaultWalletDir(), "Monero wallet (*.keys)");
if(fn.isEmpty()) return; if(fn.isEmpty()) return;
if(!fn.endsWith(".keys")) fn += ".keys"; if(!fn.endsWith(".keys")) fn += ".keys";

View file

@ -101,6 +101,9 @@ void HistoryWidget::showTxDetails() {
if (!tx) return; if (!tx) return;
auto *dialog = new TransactionInfoDialog(m_wallet, tx, this); auto *dialog = new TransactionInfoDialog(m_wallet, tx, this);
connect(dialog, &TransactionInfoDialog::resendTranscation, [this](const QString &txid){
emit resendTransaction(txid);
});
dialog->show(); dialog->show();
} }

View file

@ -24,6 +24,11 @@ bool Coins::coin(int index, std::function<void (CoinsInfo &)> callback)
return true; return true;
} }
CoinsInfo* Coins::coin(int index)
{
return m_tinfo.value(index);
}
void Coins::refresh(quint32 accountIndex) void Coins::refresh(quint32 accountIndex)
{ {
emit refreshStarted(); emit refreshStarted();
@ -74,18 +79,17 @@ void Coins::thaw(int index) const
emit coinThawed(); emit coinThawed();
} }
QStringList Coins::coins_from_txid(const QString &txid) QVector<CoinsInfo*> Coins::coins_from_txid(const QString &txid)
{ {
QStringList keyimages; QVector<CoinsInfo*> coins;
for (int i = 0; i < this->count(); i++) { for (int i = 0; i < this->count(); i++) {
this->coin(i, [&keyimages, &txid](const CoinsInfo &x) { CoinsInfo* coin = this->coin(i);
if (x.hash() == txid) { if (coin->hash() == txid) {
keyimages += x.keyImage(); coins.append(coin);
} }
});
} }
return keyimages; return coins;
} }
Coins::Coins(Monero::Coins *pimpl, QObject *parent) Coins::Coins(Monero::Coins *pimpl, QObject *parent)

View file

@ -25,11 +25,12 @@ Q_OBJECT
public: public:
Q_INVOKABLE bool coin(int index, std::function<void (CoinsInfo &)> callback); Q_INVOKABLE bool coin(int index, std::function<void (CoinsInfo &)> callback);
Q_INVOKABLE CoinsInfo * coin(int index);
Q_INVOKABLE void refresh(quint32 accountIndex); Q_INVOKABLE void refresh(quint32 accountIndex);
Q_INVOKABLE void refreshUnlocked(); Q_INVOKABLE void refreshUnlocked();
Q_INVOKABLE void freeze(int index) const; Q_INVOKABLE void freeze(int index) const;
Q_INVOKABLE void thaw(int index) const; Q_INVOKABLE void thaw(int index) const;
Q_INVOKABLE QStringList coins_from_txid(const QString &txid); // Todo: return CoinsInfo vector Q_INVOKABLE QVector<CoinsInfo*> coins_from_txid(const QString &txid);
quint64 count() const; quint64 count() const;

View file

@ -78,3 +78,8 @@ quint64 Subaddress::count() const
return m_rows.size(); return m_rows.size();
} }
Monero::SubaddressRow* Subaddress::row(int index) const
{
return m_rows.value(index);
}

View file

@ -23,6 +23,7 @@ public:
Q_INVOKABLE void refresh(quint32 accountIndex) const; Q_INVOKABLE void refresh(quint32 accountIndex) const;
Q_INVOKABLE quint64 unusedLookahead() const; Q_INVOKABLE quint64 unusedLookahead() const;
quint64 count() const; quint64 count() const;
Monero::SubaddressRow* row(int index) const;
signals: signals:
void refreshStarted() const; void refreshStarted() const;

View file

@ -293,6 +293,11 @@ bool Wallet::viewOnly() const
return m_walletImpl->watchOnly(); return m_walletImpl->watchOnly();
} }
bool Wallet::isDeterministic() const
{
return m_walletImpl->isDeterministic();
}
quint64 Wallet::balance() const quint64 Wallet::balance() const
{ {
return balance(m_currentSubaddressAccount); return balance(m_currentSubaddressAccount);
@ -400,9 +405,11 @@ void Wallet::refreshHeightAsync()
daemonHeightFuture.second.waitForFinished(); daemonHeightFuture.second.waitForFinished();
targetHeightFuture.second.waitForFinished(); targetHeightFuture.second.waitForFinished();
setConnectionStatus(ConnectionStatus_Connected); if (daemonHeight > 0 && targetHeight > 0) {
emit heightRefreshed(walletHeight, daemonHeight, targetHeight);
emit heightRefreshed(walletHeight, daemonHeight, targetHeight); qDebug() << "Setting connection status from refreshHeightAsync";
setConnectionStatus(ConnectionStatus_Connected);
}
}); });
} }

View file

@ -224,6 +224,9 @@ public:
//! returns if view only wallet //! returns if view only wallet
Q_INVOKABLE bool viewOnly() const; Q_INVOKABLE bool viewOnly() const;
//! return true if deterministic keys
Q_INVOKABLE bool isDeterministic() const;
Q_INVOKABLE void refreshHeightAsync(); Q_INVOKABLE void refreshHeightAsync();
//! export/import key images //! export/import key images

View file

@ -597,7 +597,7 @@ void MainWindow::onWalletOpened() {
// receive page // receive page
m_ctx->currentWallet->subaddress()->refresh( m_ctx->currentWallet->currentSubaddressAccount()); m_ctx->currentWallet->subaddress()->refresh( m_ctx->currentWallet->currentSubaddressAccount());
ui->receiveWidget->setModel( m_ctx->currentWallet->subaddressModel(), m_ctx->currentWallet->subaddress()); ui->receiveWidget->setModel( m_ctx->currentWallet->subaddressModel(), m_ctx->currentWallet);
if (m_ctx->currentWallet->subaddress()->count() == 1) { if (m_ctx->currentWallet->subaddress()->count() == 1) {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
m_ctx->currentWallet->subaddress()->addRow(m_ctx->currentWallet->currentSubaddressAccount(), ""); m_ctx->currentWallet->subaddress()->addRow(m_ctx->currentWallet->currentSubaddressAccount(), "");
@ -837,6 +837,16 @@ void MainWindow::showWalletInfoDialog() {
} }
void MainWindow::showSeedDialog() { void MainWindow::showSeedDialog() {
if (m_ctx->currentWallet->viewOnly()) {
QMessageBox::information(this, "Information", "Wallet is view-only and has no seed.\n\nTo obtain wallet keys go to Wallet -> View-Only");
return;
}
if (!m_ctx->currentWallet->isDeterministic()) {
QMessageBox::information(this, "Information", "Wallet is non-deterministic and has no seed.\n\nTo obtain wallet keys go to Wallet -> Keys");
return;
}
auto *dialog = new SeedDialog(m_ctx->currentWallet, this); auto *dialog = new SeedDialog(m_ctx->currentWallet, this);
dialog->exec(); dialog->exec();
dialog->deleteLater(); dialog->deleteLater();
@ -1303,6 +1313,11 @@ void MainWindow::updateNetStats() {
return; return;
} }
if (m_ctx->currentWallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected) {
m_statusLabelNetStats->setText("");
return;
}
if (m_ctx->currentWallet->connectionStatus() == Wallet::ConnectionStatus_Connected && m_ctx->currentWallet->synchronized()) { if (m_ctx->currentWallet->connectionStatus() == Wallet::ConnectionStatus_Connected && m_ctx->currentWallet->synchronized()) {
m_statusLabelNetStats->setText(""); m_statusLabelNetStats->setText("");
return; return;

View file

@ -197,4 +197,9 @@ QVariant CoinsModel::parseTransactionInfo(const CoinsInfo &cInfo, int column, in
return QVariant(); return QVariant();
} }
} }
}
CoinsInfo* CoinsModel::entryFromIndex(const QModelIndex &index) const {
Q_ASSERT(index.isValid() && index.row() < m_coins->count());
return m_coins->coin(index.row());
} }

View file

@ -43,6 +43,8 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
CoinsInfo* entryFromIndex(const QModelIndex &index) const;
public slots: public slots:
void startReset(); void startReset();
void endReset(); void endReset();

View file

@ -4,6 +4,7 @@
#include "HistoryView.h" #include "HistoryView.h"
#include "TransactionHistoryProxyModel.h" #include "TransactionHistoryProxyModel.h"
#include "libwalletqt/TransactionInfo.h"
#include <QHeaderView> #include <QHeaderView>
#include <QMenu> #include <QMenu>
@ -186,3 +187,14 @@ void HistoryView::resetViewToDefaults()
fitColumnsToWindow(); fitColumnsToWindow();
} }
} }
void HistoryView::keyPressEvent(QKeyEvent *event) {
TransactionInfo* tx = this->currentEntry();
if (event->matches(QKeySequence::Copy) && tx) {
Utils::copyToClipboard(tx->hash());
}
else {
QTreeView::keyPressEvent(event);
}
}

View file

@ -4,6 +4,7 @@
#ifndef FEATHER_HISTORYVIEW_H #ifndef FEATHER_HISTORYVIEW_H
#define FEATHER_HISTORYVIEW_H #define FEATHER_HISTORYVIEW_H
#include <QKeyEvent>
#include <QTreeView> #include <QTreeView>
#include <QActionGroup> #include <QActionGroup>
@ -29,6 +30,9 @@ private slots:
void fitColumnsToContents(); void fitColumnsToContents();
void resetViewToDefaults(); void resetViewToDefaults();
protected:
void keyPressEvent(QKeyEvent *event);
private: private:
TransactionHistoryModel* sourceModel(); TransactionHistoryModel* sourceModel();

View file

@ -180,3 +180,8 @@ bool SubaddressModel::isShowFullAddresses() const {
int SubaddressModel::unusedLookahead() const { int SubaddressModel::unusedLookahead() const {
return m_subaddress->unusedLookahead(); return m_subaddress->unusedLookahead();
} }
Monero::SubaddressRow* SubaddressModel::entryFromIndex(const QModelIndex &index) const {
Q_ASSERT(index.isValid() && index.row() < m_subaddress->count());
return m_subaddress->row(index.row());
}

View file

@ -38,6 +38,8 @@ public:
bool isShowFullAddresses() const; bool isShowFullAddresses() const;
void setShowFullAddresses(bool show); void setShowFullAddresses(bool show);
Monero::SubaddressRow* entryFromIndex(const QModelIndex &index) const;
int unusedLookahead() const; int unusedLookahead() const;
public slots: public slots:

View file

@ -29,6 +29,10 @@ bool SubaddressProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &so
if (sourceRow == 0 && m_hidePrimary) if (sourceRow == 0 && m_hidePrimary)
return false; return false;
if (!m_showHidden && m_hiddenAddresses.contains(address)) {
return false;
}
if (!m_searchRegExp.isEmpty()) { if (!m_searchRegExp.isEmpty()) {
return address.contains(m_searchCaseSensitiveRegExp) || label.contains(m_searchRegExp); return address.contains(m_searchCaseSensitiveRegExp) || label.contains(m_searchRegExp);
} }

View file

@ -22,16 +22,30 @@ public slots:
m_searchCaseSensitiveRegExp.setPattern(searchString); m_searchCaseSensitiveRegExp.setPattern(searchString);
invalidateFilter(); invalidateFilter();
} }
void setShowUsed(const bool showUsed){ void setShowUsed(const bool showUsed){
m_showUsed = showUsed; m_showUsed = showUsed;
invalidateFilter(); invalidateFilter();
} }
void setShowHidden(const bool showHidden){
m_showHidden = showHidden;
invalidateFilter();
}
void setHiddenAddresses(const QStringList& hiddenAddresses) {
m_hiddenAddresses = hiddenAddresses;
invalidateFilter();
}
private: private:
Subaddress *m_subaddress; Subaddress *m_subaddress;
QStringList m_hiddenAddresses;
QRegExp m_searchRegExp; QRegExp m_searchRegExp;
QRegExp m_searchCaseSensitiveRegExp; QRegExp m_searchCaseSensitiveRegExp;
bool m_showUsed = false; bool m_showUsed = false;
bool m_showHidden = false;
bool m_hidePrimary; bool m_hidePrimary;
}; };

View file

@ -141,7 +141,10 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
switch (column) switch (column)
{ {
case Column::Date: case Column::Date:
return tInfo.timestamp().toString("yyyy-MM-dd HH:mm "); {
return tInfo.timestamp().toString(QString("%1 %2 ").arg(config()->get(Config::dateFormat).toString(),
config()->get(Config::timeFormat).toString()));
}
case Column::Description: case Column::Description:
return tInfo.description(); return tInfo.description();
case Column::Amount: case Column::Amount:

View file

@ -35,13 +35,19 @@ ReceiveWidget::ReceiveWidget(QWidget *parent) :
connect(ui->qrCode, &ClickableLabel::clicked, this, &ReceiveWidget::showQrCodeDialog); connect(ui->qrCode, &ClickableLabel::clicked, this, &ReceiveWidget::showQrCodeDialog);
connect(ui->label_addressSearch, &QLineEdit::textChanged, this, &ReceiveWidget::setSearchFilter); connect(ui->label_addressSearch, &QLineEdit::textChanged, this, &ReceiveWidget::setSearchFilter);
connect(ui->check_showUsed, &QCheckBox::clicked, this, &ReceiveWidget::setShowUsedAddresses);
connect(ui->check_showHidden, &QCheckBox::clicked, this, &ReceiveWidget::setShowHiddenAddresses);
} }
void ReceiveWidget::setModel(SubaddressModel * model, Subaddress * subaddress) { void ReceiveWidget::setModel(SubaddressModel * model, Wallet * wallet) {
m_subaddress = subaddress; m_wallet = wallet;
m_subaddress = wallet->subaddress();
m_model = model; m_model = model;
m_proxyModel = new SubaddressProxyModel(this, subaddress); m_proxyModel = new SubaddressProxyModel(this, m_subaddress);
m_proxyModel->setSourceModel(m_model); m_proxyModel->setSourceModel(m_model);
m_proxyModel->setHiddenAddresses(this->getHiddenAddresses());
ui->addresses->setModel(m_proxyModel); ui->addresses->setModel(m_proxyModel);
ui->addresses->setColumnHidden(SubaddressModel::isUsed, true); ui->addresses->setColumnHidden(SubaddressModel::isUsed, true);
@ -74,13 +80,13 @@ void ReceiveWidget::editLabel() {
} }
void ReceiveWidget::showContextMenu(const QPoint &point) { void ReceiveWidget::showContextMenu(const QPoint &point) {
QModelIndex index = ui->addresses->indexAt(point); Monero::SubaddressRow* row = this->currentEntry();
if (!index.isValid()) { if (!row) return;
return;
} QString address = QString::fromStdString(row->getAddress());
bool isUsed = row->isUsed();
auto *menu = new QMenu(ui->addresses); auto *menu = new QMenu(ui->addresses);
bool isUsed = index.model()->data(index.siblingAtColumn(SubaddressModel::isUsed), Qt::UserRole).toBool();
menu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ReceiveWidget::copyAddress); menu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ReceiveWidget::copyAddress);
menu->addAction(QIcon(":/assets/images/copy.png"), "Copy label", this, &ReceiveWidget::copyLabel); menu->addAction(QIcon(":/assets/images/copy.png"), "Copy label", this, &ReceiveWidget::copyLabel);
@ -90,6 +96,13 @@ void ReceiveWidget::showContextMenu(const QPoint &point) {
menu->addAction(m_showTransactionsAction); menu->addAction(m_showTransactionsAction);
} }
QStringList hiddenAddresses = this->getHiddenAddresses();
if (hiddenAddresses.contains(address)) {
menu->addAction("Show address", this, &ReceiveWidget::showAddress);
} else {
menu->addAction("Hide address", this, &ReceiveWidget::hideAddress);
}
menu->popup(ui->addresses->viewport()->mapToGlobal(point)); menu->popup(ui->addresses->viewport()->mapToGlobal(point));
} }
@ -117,6 +130,11 @@ void ReceiveWidget::setShowUsedAddresses(bool show) {
m_proxyModel->setShowUsed(show); m_proxyModel->setShowUsed(show);
} }
void ReceiveWidget::setShowHiddenAddresses(bool show) {
if (!m_proxyModel) return;
m_proxyModel->setShowHidden(show);
}
void ReceiveWidget::setSearchFilter(const QString &filter) { void ReceiveWidget::setSearchFilter(const QString &filter) {
if (!m_proxyModel) return; if (!m_proxyModel) return;
m_proxyModel->setSearchFilter(filter); m_proxyModel->setSearchFilter(filter);
@ -128,6 +146,24 @@ void ReceiveWidget::showHeaderMenu(const QPoint& position)
m_headerMenu->exec(QCursor::pos()); m_headerMenu->exec(QCursor::pos());
} }
void ReceiveWidget::hideAddress()
{
Monero::SubaddressRow* row = this->currentEntry();
if (!row) return;
QString address = QString::fromStdString(row->getAddress());
this->addHiddenAddress(address);
m_proxyModel->setHiddenAddresses(this->getHiddenAddresses());
}
void ReceiveWidget::showAddress()
{
Monero::SubaddressRow* row = this->currentEntry();
if (!row) return;
QString address = QString::fromStdString(row->getAddress());
this->removeHiddenAddress(address);
m_proxyModel->setHiddenAddresses(this->getHiddenAddresses());
}
void ReceiveWidget::updateQrCode(){ void ReceiveWidget::updateQrCode(){
QModelIndex index = ui->addresses->currentIndex(); QModelIndex index = ui->addresses->currentIndex();
if (!index.isValid()) { if (!index.isValid()) {
@ -156,6 +192,36 @@ void ReceiveWidget::showQrCodeDialog() {
dialog->deleteLater(); dialog->deleteLater();
} }
QStringList ReceiveWidget::getHiddenAddresses() {
QString data = m_wallet->getCacheAttribute("feather.hiddenaddresses");
return data.split(",");
}
void ReceiveWidget::addHiddenAddress(const QString& address) {
QStringList hiddenAddresses = this->getHiddenAddresses();
if (!hiddenAddresses.contains(address)) {
hiddenAddresses.append(address);
}
QString data = hiddenAddresses.join(",");
m_wallet->setCacheAttribute("feather.hiddenaddresses", data);
}
void ReceiveWidget::removeHiddenAddress(const QString &address) {
QStringList hiddenAddresses = this->getHiddenAddresses();
hiddenAddresses.removeAll(address);
QString data = hiddenAddresses.join(",");
m_wallet->setCacheAttribute("feather.hiddenaddresses", data);
}
Monero::SubaddressRow* ReceiveWidget::currentEntry() {
QModelIndexList list = ui->addresses->selectionModel()->selectedRows();
if (list.size() == 1) {
return m_model->entryFromIndex(m_proxyModel->mapToSource(list.first()));
} else {
return nullptr;
}
}
ReceiveWidget::~ReceiveWidget() { ReceiveWidget::~ReceiveWidget() {
delete ui; delete ui;
} }

View file

@ -23,7 +23,7 @@ Q_OBJECT
public: public:
explicit ReceiveWidget(QWidget *parent = nullptr); explicit ReceiveWidget(QWidget *parent = nullptr);
void setModel(SubaddressModel * model, Subaddress * subaddress); void setModel(SubaddressModel * model, Wallet * wallet);
~ReceiveWidget() override; ~ReceiveWidget() override;
@ -34,6 +34,7 @@ public slots:
void showContextMenu(const QPoint& point); void showContextMenu(const QPoint& point);
void setShowFullAddresses(bool show); void setShowFullAddresses(bool show);
void setShowUsedAddresses(bool show); void setShowUsedAddresses(bool show);
void setShowHiddenAddresses(bool show);
void setSearchFilter(const QString &filter); void setSearchFilter(const QString &filter);
void onShowTransactions(); void onShowTransactions();
void resetModel(); void resetModel();
@ -44,6 +45,8 @@ signals:
private slots: private slots:
void showHeaderMenu(const QPoint& position); void showHeaderMenu(const QPoint& position);
void hideAddress();
void showAddress();
private: private:
Ui::ReceiveWidget *ui; Ui::ReceiveWidget *ui;
@ -54,9 +57,14 @@ private:
Subaddress * m_subaddress; Subaddress * m_subaddress;
SubaddressModel * m_model; SubaddressModel * m_model;
SubaddressProxyModel * m_proxyModel; SubaddressProxyModel * m_proxyModel;
Wallet * m_wallet;
void updateQrCode(); void updateQrCode();
void showQrCodeDialog(); void showQrCodeDialog();
QStringList getHiddenAddresses();
void addHiddenAddress(const QString& address);
void removeHiddenAddress(const QString& address);
Monero::SubaddressRow* currentEntry();
}; };
#endif //FEATHER_RECEIVEWIDGET_H #endif //FEATHER_RECEIVEWIDGET_H

View file

@ -82,6 +82,27 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="check_showUsed">
<property name="text">
<string>Show used</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="check_showHidden">
<property name="text">
<string>Show hidden</string>
</property>
</widget>
</item>
</layout>
</item>
<item> <item>
<widget class="QPushButton" name="btn_generateSubaddress"> <widget class="QPushButton" name="btn_generateSubaddress">
<property name="text"> <property name="text">

View file

@ -46,7 +46,14 @@
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="PayToEdit" name="lineAddress"/> <widget class="PayToEdit" name="lineAddress">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
</widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="HelpLabel" name="label_Description"> <widget class="HelpLabel" name="label_Description">

View file

@ -48,10 +48,29 @@ Settings::Settings(QWidget *parent) :
} }
ui->comboBox_amountPrecision->setCurrentIndex(config()->get(Config::amountPrecision).toInt()); ui->comboBox_amountPrecision->setCurrentIndex(config()->get(Config::amountPrecision).toInt());
// Date format combobox
QDateTime now = QDateTime::currentDateTime();
for (const auto & format : m_dateFormats) {
ui->comboBox_dateFormat->addItem(now.toString(format));
}
QString dateFormatSetting = config()->get(Config::dateFormat).toString();
if (m_dateFormats.contains(dateFormatSetting))
ui->comboBox_dateFormat->setCurrentIndex(m_dateFormats.indexOf(dateFormatSetting));
// Time format combobox
for (const auto & format : m_timeFormats) {
ui->comboBox_timeFormat->addItem(now.toString(format));
}
QString timeFormatSetting = config()->get(Config::timeFormat).toString();
if (m_timeFormats.contains(timeFormatSetting))
ui->comboBox_timeFormat->setCurrentIndex(m_timeFormats.indexOf(timeFormatSetting));
connect(ui->comboBox_skin, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_skinChanged); connect(ui->comboBox_skin, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_skinChanged);
connect(ui->comboBox_blockExplorer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_blockExplorerChanged); connect(ui->comboBox_blockExplorer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_blockExplorerChanged);
connect(ui->comboBox_redditFrontend, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_redditFrontendChanged); connect(ui->comboBox_redditFrontend, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_redditFrontendChanged);
connect(ui->comboBox_amountPrecision, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_amountPrecisionChanged); connect(ui->comboBox_amountPrecision, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_amountPrecisionChanged);
connect(ui->comboBox_dateFormat, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_dateFormatChanged);
connect(ui->comboBox_timeFormat, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_timeFormatChanged);
// setup preferred fiat currency combobox // setup preferred fiat currency combobox
QStringList fiatCurrencies; QStringList fiatCurrencies;
@ -110,6 +129,14 @@ void Settings::comboBox_amountPrecisionChanged(int pos) {
emit amountPrecisionChanged(pos); emit amountPrecisionChanged(pos);
} }
void Settings::comboBox_dateFormatChanged(int pos) {
config()->set(Config::dateFormat, m_dateFormats.at(pos));
}
void Settings::comboBox_timeFormatChanged(int pos) {
config()->set(Config::timeFormat, m_timeFormats.at(pos));
}
void Settings::copyToClipboard() { void Settings::copyToClipboard() {
ui->textLogs->copy(); ui->textLogs->copy();
} }

View file

@ -40,15 +40,18 @@ public slots:
void comboBox_blockExplorerChanged(int pos); void comboBox_blockExplorerChanged(int pos);
void comboBox_redditFrontendChanged(int pos); void comboBox_redditFrontendChanged(int pos);
void comboBox_amountPrecisionChanged(int pos); void comboBox_amountPrecisionChanged(int pos);
void comboBox_dateFormatChanged(int pos);
private: void comboBox_timeFormatChanged(int pos);
QStringList m_skins{"Native", "QDarkStyle", "Breeze/Dark", "Breeze/Light"};
private: private:
void setupSkinCombobox(); void setupSkinCombobox();
AppContext *m_ctx; AppContext *m_ctx;
Ui::Settings *ui; Ui::Settings *ui;
QStringList m_skins{"Native", "QDarkStyle", "Breeze/Dark", "Breeze/Light"};
QStringList m_dateFormats{"yyyy-MM-dd", "MM-dd-yyyy", "dd-MM-yyyy"};
QStringList m_timeFormats{"hh:mm", "hh:mm ap"};
}; };
#endif // SETTINGS_H #endif // SETTINGS_H

View file

@ -161,14 +161,14 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="7" column="0">
<widget class="QCheckBox" name="checkBox_externalLink"> <widget class="QCheckBox" name="checkBox_externalLink">
<property name="text"> <property name="text">
<string>Warn before opening external link</string> <string>Warn before opening external link</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="8" column="0">
<widget class="QCheckBox" name="checkBox_hideBalance"> <widget class="QCheckBox" name="checkBox_hideBalance">
<property name="text"> <property name="text">
<string>Hide balance</string> <string>Hide balance</string>
@ -211,6 +211,26 @@
<item row="4" column="1"> <item row="4" column="1">
<widget class="QComboBox" name="comboBox_amountPrecision"/> <widget class="QComboBox" name="comboBox_amountPrecision"/>
</item> </item>
<item row="5" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Date format:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="comboBox_dateFormat"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Time format:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="comboBox_timeFormat"/>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_node"> <widget class="QWidget" name="tab_node">

View file

@ -48,7 +48,9 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::redditFrontend, {QS("redditFrontend"), "old.reddit.com"}}, {Config::redditFrontend, {QS("redditFrontend"), "old.reddit.com"}},
{Config::showHistorySyncNotice, {QS("showHistorySyncNotice"), true}}, {Config::showHistorySyncNotice, {QS("showHistorySyncNotice"), true}},
{Config::GUI_HistoryViewState, {QS("GUI_HistoryViewState"), {}}}, {Config::GUI_HistoryViewState, {QS("GUI_HistoryViewState"), {}}},
{Config::amountPrecision, {QS("amountPrecision"), 4}} {Config::amountPrecision, {QS("amountPrecision"), 4}},
{Config::dateFormat, {QS("dateFormat"), "yyyy-MM-dd"}},
{Config::timeFormat, {QS("timeFormat"), "HH:mm"}}
}; };

View file

@ -52,7 +52,9 @@ public:
showHistorySyncNotice, showHistorySyncNotice,
GUI_HistoryViewState, GUI_HistoryViewState,
amountPrecision, amountPrecision,
portableMode portableMode,
dateFormat,
timeFormat
}; };
~Config() override; ~Config() override;

View file

@ -82,6 +82,9 @@
</item> </item>
<item> <item>
<widget class="QCheckBox" name="check_darkMode"> <widget class="QCheckBox" name="check_darkMode">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text"> <property name="text">
<string>Dark mode</string> <string>Dark mode</string>
</property> </property>

View file

@ -26,7 +26,6 @@ public:
private: private:
AppContext *m_ctx; AppContext *m_ctx;
QLabel *topLabel;
Ui::PageNetwork *ui; Ui::PageNetwork *ui;
}; };

View file

@ -27,8 +27,6 @@ public:
int nextId() const override; int nextId() const override;
private: private:
void resetWidgets();
AppContext *m_ctx; AppContext *m_ctx;
WizardFields *m_fields; WizardFields *m_fields;
Ui::PageWalletRestoreKeys *ui; Ui::PageWalletRestoreKeys *ui;

View file

@ -5,7 +5,6 @@
#include "PageWalletRestoreSeed.h" #include "PageWalletRestoreSeed.h"
#include "ui_PageWalletRestoreSeed.h" #include "ui_PageWalletRestoreSeed.h"
#include <QLineEdit>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QMessageBox> #include <QMessageBox>