Hush lite wallet https://faq.hush.is/sdl
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

303 lines
10 KiB

// Copyright 2019-2024 The Hush developers
// Released under the GPLv3
#include "txtablemodel.h"
#include "settings.h"
#include "controller.h"
#include "guiconstants.h"
TxTableModel::TxTableModel(QObject *parent)
: QAbstractTableModel(parent) {
headers << QObject::tr("Type") << QObject::tr("Address") << QObject::tr("Date/Time") << QObject::tr("Confirmations") << QObject::tr("Amount");
}
TxTableModel::~TxTableModel() {
delete modeldata;
}
void TxTableModel::replaceData(const QList<TransactionItem>& data) {
delete modeldata;
modeldata = new QList<TransactionItem>();
// Copy over the data and sort it
std::copy(data.begin(), data.end(), std::back_inserter(*modeldata));
std::sort(modeldata->begin(), modeldata->end(), [=] (auto a, auto b) {
return a.datetime > b.datetime; // reverse sort
});
dataChanged(index(0, 0), index(modeldata->size()-1, columnCount(index(0,0))-1));
layoutChanged();
}
bool TxTableModel::exportToCsv(QString fileName) const {
if (!modeldata)
return false;
QFile file(fileName);
if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate))
return false;
QTextStream out(&file); // we will serialize the data into the file
// Write headers
for (int i = 0; i < headers.length(); i++) {
out << "\"" << headers[i] << "\",";
}
out << "\"Memo\"";
out << endl;
// Write out each row
for (int row = 0; row < modeldata->length(); row++) {
for (int col = 0; col < headers.length(); col++) {
out << "\"" << data(index(row, col), Qt::DisplayRole).toString() << "\",";
}
// Memo
out << "\"" << this->getMemo(row) << "\"";
out << endl;
}
file.close();
return true;
}
int TxTableModel::rowCount(const QModelIndex&) const
{
if (modeldata == nullptr) return 0;
return modeldata->size();
}
int TxTableModel::columnCount(const QModelIndex&) const
{
return headers.size();
}
QString TxTableModel::concatMultipleMemos(const TransactionItem& dat) const {
if (dat.items.length() == 1) {
return dat.items[0].memo;
} else {
// Concat all the memos
QString memo;
for (auto item : dat.items) {
if (!item.memo.trimmed().isEmpty()) {
memo += item.address + ": \"" + item.memo + "\"\n";
}
}
return memo;
}
};
QVariant TxTableModel::data(const QModelIndex &index, int role) const {
// Get current theme name
QString theme_name = Settings::getInstance()->get_theme_name();
QBrush b;
QColor color;
if (theme_name == "Dark" || theme_name == "Midnight") {
color = COLOR_WHITE;
}else{
color = COLOR_BLACK;
}
// Align numeric columns (confirmations, amount) right
if (role == Qt::TextAlignmentRole &&
(index.column() == Column::Confirmations || index.column() == Column::Amount))
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
auto dat = modeldata->at(index.row());
if (role == Qt::ForegroundRole) {
if (dat.confirmations <= 0) {
b.setColor(Qt::red);
return b;
}
b.setColor(color);
return b;
}
if (role == Qt::DisplayRole) {
switch (index.column()) {
case Column::Type: return dat.type;
case Column::Address: {
auto addr = dat.address;
if (addr.trimmed().isEmpty())
return "(Shielded)";
else
return addr;
}
case Column::Time: return QDateTime::fromMSecsSinceEpoch(dat.datetime * (qint64)1000).toLocalTime().toString();
case Column::Confirmations: return QString::number(dat.confirmations);
case Column::Amount: {
// Sum up all the amounts
CAmount total;
for (int i=0; i < dat.items.length(); i++) {
total = total + dat.items[i].amount;
}
return total.toDecimalhushString();
}
}
}
if (role == Qt::ToolTipRole) {
switch (index.column()) {
case Column::Type: {
// If there are multiple memos, then mark them as such
if (dat.items.length() == 1) {
auto memo = dat.items[0].memo;
if (memo.startsWith("hush:")) {
return Settings::paymentURIPretty(Settings::parseURI(memo));
} else {
return modeldata->at(index.row()).type +
(memo.isEmpty() ? "" : " tx memo: \"" + memo.toHtmlEscaped() + "\"");
}
} else {
return concatMultipleMemos(dat);
}
}
case Column::Address: {
auto addr = modeldata->at(index.row()).address;
if (addr.trimmed().isEmpty())
return "(Shielded)";
else
return addr;
}
case Column::Time: return QDateTime::fromMSecsSinceEpoch(modeldata->at(index.row()).datetime * (qint64)1000).toLocalTime().toString();
case Column::Confirmations: return QString("%1 Network Confirmations").arg(QString::number(dat.confirmations));
case Column::Amount: {
// Sum up all the amounts
CAmount total;
for (int i=0; i < dat.items.length(); i++) {
total = total + dat.items[i].amount;
}
if (Settings::getInstance()->get_currency_name() == "USD") {
return total.toDecimalUSDString();
} else if (Settings::getInstance()->get_currency_name() == "EUR") {
return total.toDecimalEURString();
} else if (Settings::getInstance()->get_currency_name() == "BTC") {
return total.toDecimalBTCString();
} else if (Settings::getInstance()->get_currency_name() == "CNY") {
return total.toDecimalCNYString();
} else if (Settings::getInstance()->get_currency_name() == "RUB") {
return total.toDecimalRUBString();
} else if (Settings::getInstance()->get_currency_name() == "CAD") {
return total.toDecimalCADString();
} else if (Settings::getInstance()->get_currency_name() == "SGD") {
return total.toDecimalSGDString();
} else if (Settings::getInstance()->get_currency_name() == "CHF") {
return total.toDecimalCHFString();
} else if (Settings::getInstance()->get_currency_name() == "INR") {
return total.toDecimalINRString();
} else if (Settings::getInstance()->get_currency_name() == "GBP") {
return total.toDecimalGBPString();
} else if (Settings::getInstance()->get_currency_name() == "AUD") {
return total.toDecimalAUDString();
}
}
}
}
if (role == Qt::DecorationRole && index.column() == 0) {
bool hasMemo = false;
for (int i=0; i < dat.items.length(); i++) {
if (!dat.items[i].memo.isEmpty()) {
hasMemo = true;
}
}
// If the memo is a Payment URI, then show a payment request icon
if (dat.items.length() == 1 && dat.items[0].memo.startsWith("hush:")) {
QImage image = colorizeIcon(QIcon(":/icons/res/paymentreq.gif"), color);
QIcon icon;
icon.addPixmap(QPixmap::fromImage(image));
return QVariant(icon.pixmap(16, 16));
} else if (hasMemo) {
// Return the info pixmap to indicate memo
QIcon icon(":/icons/res/mail.png");
return QVariant(icon.pixmap(16, 16));
} else {
if (dat.type == "Receive"){
QImage image = colorizeIcon(QIcon(":/icons/res/tx_input.png"), color);
QIcon icon;
icon.addPixmap(QPixmap::fromImage(image));
return QVariant(icon.pixmap(16, 16));
}
if (dat.type == "send"){
QImage image = colorizeIcon(QIcon(":/icons/res/tx_output.png"), color);
QIcon icon;
icon.addPixmap(QPixmap::fromImage(image));
return QVariant(icon.pixmap(16, 16));
}
}
}
return QVariant();
}
QVariant TxTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::TextAlignmentRole && (section == Column::Confirmations || section == Column::Amount))
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
if (role == Qt::FontRole) {
QFont f;
f.setBold(true);
return f;
}
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
return headers.at(section);
}
return QVariant();
}
QString TxTableModel::getTxId(int row) const {
return modeldata->at(row).txid;
}
QString TxTableModel::getMemo(int row) const {
auto dat = modeldata->at(row);
return concatMultipleMemos(dat);
}
qint64 TxTableModel::getConfirmations(int row) const {
return modeldata->at(row).confirmations;
}
QString TxTableModel::getAddr(int row) const {
return modeldata->at(row).address.trimmed();
}
qint64 TxTableModel::getDate(int row) const {
return modeldata->at(row).datetime;
}
QString TxTableModel::getType(int row) const {
return modeldata->at(row).type;
}
QString TxTableModel::getAmt(int row) const {
auto dat = modeldata->at(row);
CAmount total;
for (int i=0; i < dat.items.length(); i++) {
total = total + dat.items[i].amount;
}
return total.toDecimalString();
}
QImage TxTableModel::colorizeIcon(QIcon icon, QColor color) const{
QImage img(icon.pixmap(16, 16).toImage());
img = img.convertToFormat(QImage::Format_ARGB32);
for (int x = img.width(); x--; )
{
for (int y = img.height(); y--; )
{
const QRgb rgb = img.pixel(x, y);
img.setPixel(x, y, qRgba(color.red(), color.green(), color.blue(), qAlpha(rgb)));
}
}
return img;
}