transaction color based on confirmed/not confirmed, basic transaction model impl
This commit is contained in:
parent
0856c1a03e
commit
f488e7358d
@ -55,7 +55,8 @@ HEADERS += gui/include/bitcoingui.h \
|
||||
core/include/rpc.h \
|
||||
gui/src/clientmodel.h \
|
||||
gui/include/clientmodel.h \
|
||||
gui/include/guiutil.h
|
||||
gui/include/guiutil.h \
|
||||
gui/include/transactionrecord.h
|
||||
SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \
|
||||
gui/src/transactiontablemodel.cpp \
|
||||
gui/src/addresstablemodel.cpp \
|
||||
@ -80,7 +81,8 @@ SOURCES += gui/src/bitcoin.cpp gui/src/bitcoingui.cpp \
|
||||
json/src/json_spirit_value.cpp \
|
||||
json/src/json_spirit_reader.cpp \
|
||||
gui/src/clientmodel.cpp \
|
||||
gui/src/guiutil.cpp
|
||||
gui/src/guiutil.cpp \
|
||||
gui/src/transactionrecord.cpp
|
||||
|
||||
RESOURCES += \
|
||||
gui/bitcoin.qrc
|
||||
|
94
gui/include/transactionrecord.h
Normal file
94
gui/include/transactionrecord.h
Normal file
@ -0,0 +1,94 @@
|
||||
#ifndef TRANSACTIONRECORD_H
|
||||
#define TRANSACTIONRECORD_H
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <QList>
|
||||
|
||||
class TransactionStatus
|
||||
{
|
||||
public:
|
||||
TransactionStatus():
|
||||
confirmed(false), sortKey(""), maturity(Mature),
|
||||
matures_in(0), status(Offline), depth(0), open_for(0)
|
||||
{ }
|
||||
|
||||
enum Maturity
|
||||
{
|
||||
Immature,
|
||||
Mature,
|
||||
MaturesIn,
|
||||
MaturesWarning, /* Will likely not mature because no nodes have confirmed */
|
||||
NotAccepted
|
||||
};
|
||||
|
||||
enum Status {
|
||||
OpenUntilDate,
|
||||
OpenUntilBlock,
|
||||
Offline,
|
||||
Unconfirmed,
|
||||
HaveConfirmations
|
||||
};
|
||||
|
||||
bool confirmed;
|
||||
std::string sortKey;
|
||||
|
||||
/* For "Generated" transactions */
|
||||
Maturity maturity;
|
||||
int matures_in;
|
||||
|
||||
/* Reported status */
|
||||
Status status;
|
||||
int64 depth;
|
||||
int64 open_for; /* Timestamp if status==OpenUntilDate, otherwise number of blocks */
|
||||
};
|
||||
|
||||
class TransactionRecord
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Other,
|
||||
Generated,
|
||||
SendToAddress,
|
||||
SendToIP,
|
||||
RecvFromAddress,
|
||||
RecvFromIP,
|
||||
SendToSelf
|
||||
};
|
||||
|
||||
TransactionRecord():
|
||||
hash(), time(0), type(Other), address(""), debit(0), credit(0)
|
||||
{
|
||||
}
|
||||
|
||||
TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status):
|
||||
hash(hash), time(time), type(Other), address(""), debit(0),
|
||||
credit(0), status(status)
|
||||
{
|
||||
}
|
||||
|
||||
TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status,
|
||||
Type type, const std::string &address,
|
||||
int64 debit, int64 credit):
|
||||
hash(hash), time(time), type(type), address(address), debit(debit), credit(credit),
|
||||
status(status)
|
||||
{
|
||||
}
|
||||
|
||||
static bool showTransaction(const CWalletTx &wtx);
|
||||
static QList<TransactionRecord> decomposeTransaction(const CWalletTx &wtx);
|
||||
|
||||
/* Fixed */
|
||||
uint256 hash;
|
||||
int64 time;
|
||||
Type type;
|
||||
std::string address;
|
||||
int64 debit;
|
||||
int64 credit;
|
||||
|
||||
/* Status: can change with block chain update */
|
||||
TransactionStatus status;
|
||||
};
|
||||
|
||||
#endif // TRANSACTIONRECORD_H
|
@ -29,7 +29,6 @@ public:
|
||||
/* TypeRole values */
|
||||
static const QString Sent;
|
||||
static const QString Received;
|
||||
static const QString Generated;
|
||||
static const QString Other;
|
||||
|
||||
int rowCount(const QModelIndex &parent) const;
|
||||
|
@ -204,9 +204,9 @@ QWidget *BitcoinGUI::createTabs()
|
||||
transaction_table->verticalHeader()->hide();
|
||||
|
||||
transaction_table->horizontalHeader()->resizeSection(
|
||||
TransactionTableModel::Status, 112);
|
||||
TransactionTableModel::Status, 120);
|
||||
transaction_table->horizontalHeader()->resizeSection(
|
||||
TransactionTableModel::Date, 112);
|
||||
TransactionTableModel::Date, 120);
|
||||
transaction_table->horizontalHeader()->setResizeMode(
|
||||
TransactionTableModel::Description, QHeaderView::Stretch);
|
||||
transaction_table->horizontalHeader()->resizeSection(
|
||||
|
@ -5,5 +5,5 @@
|
||||
QString DateTimeStr(qint64 nTime)
|
||||
{
|
||||
QDateTime date = QDateTime::fromMSecsSinceEpoch(nTime*1000);
|
||||
return date.toString(Qt::DefaultLocaleShortDate) + QString(" ") + date.toString("hh:mm");
|
||||
return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
|
||||
}
|
||||
|
224
gui/src/transactionrecord.cpp
Normal file
224
gui/src/transactionrecord.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
#include "transactionrecord.h"
|
||||
|
||||
|
||||
/* Return positive answer if transaction should be shown in list.
|
||||
*/
|
||||
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
|
||||
{
|
||||
if (wtx.IsCoinBase())
|
||||
{
|
||||
// Don't show generated coin until confirmed by at least one block after it
|
||||
// so we don't get the user's hopes up until it looks like it's probably accepted.
|
||||
//
|
||||
// It is not an error when generated blocks are not accepted. By design,
|
||||
// some percentage of blocks, like 10% or more, will end up not accepted.
|
||||
// This is the normal mechanism by which the network copes with latency.
|
||||
//
|
||||
// We display regular transactions right away before any confirmation
|
||||
// because they can always get into some block eventually. Generated coins
|
||||
// are special because if their block is not accepted, they are not valid.
|
||||
//
|
||||
if (wtx.GetDepthInMainChain() < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Decompose CWallet transaction to model transaction records.
|
||||
*/
|
||||
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWalletTx &wtx)
|
||||
{
|
||||
QList<TransactionRecord> parts;
|
||||
int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
|
||||
int64 nCredit = wtx.GetCredit(true);
|
||||
int64 nDebit = wtx.GetDebit();
|
||||
int64 nNet = nCredit - nDebit;
|
||||
uint256 hash = wtx.GetHash();
|
||||
std::map<std::string, std::string> mapValue = wtx.mapValue;
|
||||
|
||||
// Find the block the tx is in
|
||||
CBlockIndex* pindex = NULL;
|
||||
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
|
||||
if (mi != mapBlockIndex.end())
|
||||
pindex = (*mi).second;
|
||||
|
||||
// Determine transaction status
|
||||
TransactionStatus status;
|
||||
// Sort order, unrecorded transactions sort to the top
|
||||
status.sortKey = strprintf("%010d-%01d-%010u",
|
||||
(pindex ? pindex->nHeight : INT_MAX),
|
||||
(wtx.IsCoinBase() ? 1 : 0),
|
||||
wtx.nTimeReceived);
|
||||
status.confirmed = wtx.IsConfirmed();
|
||||
status.depth = wtx.GetDepthInMainChain();
|
||||
|
||||
if (!wtx.IsFinal())
|
||||
{
|
||||
if (wtx.nLockTime < 500000000)
|
||||
{
|
||||
status.status = TransactionStatus::OpenUntilBlock;
|
||||
status.open_for = nBestHeight - wtx.nLockTime;
|
||||
} else {
|
||||
status.status = TransactionStatus::OpenUntilDate;
|
||||
status.open_for = wtx.nLockTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
|
||||
{
|
||||
status.status = TransactionStatus::Offline;
|
||||
} else if (status.depth < 6)
|
||||
{
|
||||
status.status = TransactionStatus::Unconfirmed;
|
||||
} else
|
||||
{
|
||||
status.status = TransactionStatus::HaveConfirmations;
|
||||
}
|
||||
}
|
||||
|
||||
if (showTransaction(wtx))
|
||||
{
|
||||
if (nNet > 0 || wtx.IsCoinBase())
|
||||
{
|
||||
//
|
||||
// Credit
|
||||
//
|
||||
TransactionRecord sub(hash, nTime, status);
|
||||
|
||||
sub.credit = nNet;
|
||||
|
||||
if (wtx.IsCoinBase())
|
||||
{
|
||||
// Generated
|
||||
sub.type = TransactionRecord::Generated;
|
||||
|
||||
if (nCredit == 0)
|
||||
{
|
||||
sub.status.maturity = TransactionStatus::Immature;
|
||||
|
||||
int64 nUnmatured = 0;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
nUnmatured += txout.GetCredit();
|
||||
sub.credit = nUnmatured;
|
||||
|
||||
if (wtx.IsInMainChain())
|
||||
{
|
||||
sub.status.maturity = TransactionStatus::MaturesIn;
|
||||
sub.status.matures_in = wtx.GetBlocksToMaturity();
|
||||
|
||||
// Check if the block was requested by anyone
|
||||
if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
|
||||
sub.status.maturity = TransactionStatus::MaturesWarning;
|
||||
}
|
||||
else
|
||||
{
|
||||
sub.status.maturity = TransactionStatus::NotAccepted;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!mapValue["from"].empty() || !mapValue["message"].empty())
|
||||
{
|
||||
// Received by IP connection
|
||||
sub.type = TransactionRecord::RecvFromIP;
|
||||
if (!mapValue["from"].empty())
|
||||
sub.address = mapValue["from"];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Received by Bitcoin Address
|
||||
sub.type = TransactionRecord::RecvFromAddress;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
{
|
||||
if (txout.IsMine())
|
||||
{
|
||||
std::vector<unsigned char> vchPubKey;
|
||||
if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
|
||||
{
|
||||
sub.address = PubKeyToAddress(vchPubKey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
parts.append(sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool fAllFromMe = true;
|
||||
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||||
fAllFromMe = fAllFromMe && txin.IsMine();
|
||||
|
||||
bool fAllToMe = true;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
fAllToMe = fAllToMe && txout.IsMine();
|
||||
|
||||
if (fAllFromMe && fAllToMe)
|
||||
{
|
||||
// Payment to self
|
||||
int64 nChange = wtx.GetChange();
|
||||
|
||||
parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::SendToSelf, "",
|
||||
-(nDebit - nChange), nCredit - nChange));
|
||||
}
|
||||
else if (fAllFromMe)
|
||||
{
|
||||
//
|
||||
// Debit
|
||||
//
|
||||
int64 nTxFee = nDebit - wtx.GetValueOut();
|
||||
|
||||
for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
|
||||
{
|
||||
const CTxOut& txout = wtx.vout[nOut];
|
||||
TransactionRecord sub(hash, nTime, status);
|
||||
|
||||
if (txout.IsMine())
|
||||
{
|
||||
// Sent to self
|
||||
sub.type = TransactionRecord::SendToSelf;
|
||||
sub.credit = txout.nValue;
|
||||
} else if (!mapValue["to"].empty())
|
||||
{
|
||||
// Sent to IP
|
||||
sub.type = TransactionRecord::SendToIP;
|
||||
sub.address = mapValue["to"];
|
||||
} else {
|
||||
// Sent to Bitcoin Address
|
||||
sub.type = TransactionRecord::SendToAddress;
|
||||
uint160 hash160;
|
||||
if (ExtractHash160(txout.scriptPubKey, hash160))
|
||||
sub.address = Hash160ToAddress(hash160);
|
||||
}
|
||||
|
||||
int64 nValue = txout.nValue;
|
||||
/* Add fee to first output */
|
||||
if (nTxFee > 0)
|
||||
{
|
||||
nValue += nTxFee;
|
||||
nTxFee = 0;
|
||||
}
|
||||
sub.debit = nValue;
|
||||
sub.status.sortKey += strprintf("-%d", nOut);
|
||||
|
||||
parts.append(sub);
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// Mixed debit transaction, can't break down payees
|
||||
//
|
||||
bool fAllMine = true;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
fAllMine = fAllMine && txout.IsMine();
|
||||
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||||
fAllMine = fAllMine && txin.IsMine();
|
||||
|
||||
parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::Other, "", nNet, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
@ -1,328 +1,17 @@
|
||||
#include "transactiontablemodel.h"
|
||||
#include "guiutil.h"
|
||||
#include "transactionrecord.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <QLocale>
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
#include <QColor>
|
||||
|
||||
const QString TransactionTableModel::Sent = "s";
|
||||
const QString TransactionTableModel::Received = "r";
|
||||
const QString TransactionTableModel::Generated = "g";
|
||||
const QString TransactionTableModel::Other = "o";
|
||||
|
||||
/* TODO: look up address in address book
|
||||
when showing.
|
||||
Color based on confirmation status.
|
||||
(fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128))
|
||||
*/
|
||||
|
||||
class TransactionStatus
|
||||
{
|
||||
public:
|
||||
TransactionStatus():
|
||||
confirmed(false), sortKey(""), maturity(Mature),
|
||||
matures_in(0), status(Offline), depth(0), open_for(0)
|
||||
{ }
|
||||
|
||||
enum Maturity
|
||||
{
|
||||
Immature,
|
||||
Mature,
|
||||
MaturesIn,
|
||||
MaturesWarning, /* Will probably not mature because no nodes have confirmed */
|
||||
NotAccepted
|
||||
};
|
||||
|
||||
enum Status {
|
||||
OpenUntilDate,
|
||||
OpenUntilBlock,
|
||||
Offline,
|
||||
Unconfirmed,
|
||||
HaveConfirmations
|
||||
};
|
||||
|
||||
bool confirmed;
|
||||
std::string sortKey;
|
||||
|
||||
/* For "Generated" transactions */
|
||||
Maturity maturity;
|
||||
int matures_in;
|
||||
|
||||
/* Reported status */
|
||||
Status status;
|
||||
int64 depth;
|
||||
int64 open_for; /* Timestamp if status==OpenUntilDate, otherwise number of blocks */
|
||||
};
|
||||
|
||||
class TransactionRecord
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Other,
|
||||
Generated,
|
||||
SendToAddress,
|
||||
SendToIP,
|
||||
RecvFromAddress,
|
||||
RecvFromIP,
|
||||
SendToSelf
|
||||
};
|
||||
|
||||
TransactionRecord():
|
||||
hash(), time(0), type(Other), address(""), debit(0), credit(0)
|
||||
{
|
||||
}
|
||||
|
||||
TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status):
|
||||
hash(hash), time(time), type(Other), address(""), debit(0),
|
||||
credit(0), status(status)
|
||||
{
|
||||
}
|
||||
|
||||
TransactionRecord(uint256 hash, int64 time, const TransactionStatus &status,
|
||||
Type type, const std::string &address,
|
||||
int64 debit, int64 credit):
|
||||
hash(hash), time(time), type(type), address(address), debit(debit), credit(credit),
|
||||
status(status)
|
||||
{
|
||||
}
|
||||
|
||||
/* Fixed */
|
||||
uint256 hash;
|
||||
int64 time;
|
||||
Type type;
|
||||
std::string address;
|
||||
int64 debit;
|
||||
int64 credit;
|
||||
|
||||
/* Status: can change with block chain update */
|
||||
TransactionStatus status;
|
||||
};
|
||||
|
||||
/* Return positive answer if transaction should be shown in list.
|
||||
*/
|
||||
bool showTransaction(const CWalletTx &wtx)
|
||||
{
|
||||
if (wtx.IsCoinBase())
|
||||
{
|
||||
// Don't show generated coin until confirmed by at least one block after it
|
||||
// so we don't get the user's hopes up until it looks like it's probably accepted.
|
||||
//
|
||||
// It is not an error when generated blocks are not accepted. By design,
|
||||
// some percentage of blocks, like 10% or more, will end up not accepted.
|
||||
// This is the normal mechanism by which the network copes with latency.
|
||||
//
|
||||
// We display regular transactions right away before any confirmation
|
||||
// because they can always get into some block eventually. Generated coins
|
||||
// are special because if their block is not accepted, they are not valid.
|
||||
//
|
||||
if (wtx.GetDepthInMainChain() < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Decompose CWallet transaction to model transaction records.
|
||||
*/
|
||||
QList<TransactionRecord> decomposeTransaction(const CWalletTx &wtx)
|
||||
{
|
||||
QList<TransactionRecord> parts;
|
||||
int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
|
||||
int64 nCredit = wtx.GetCredit(true);
|
||||
int64 nDebit = wtx.GetDebit();
|
||||
int64 nNet = nCredit - nDebit;
|
||||
uint256 hash = wtx.GetHash();
|
||||
std::map<std::string, std::string> mapValue = wtx.mapValue;
|
||||
|
||||
// Find the block the tx is in
|
||||
CBlockIndex* pindex = NULL;
|
||||
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
|
||||
if (mi != mapBlockIndex.end())
|
||||
pindex = (*mi).second;
|
||||
|
||||
// Determine transaction status
|
||||
TransactionStatus status;
|
||||
// Sort order, unrecorded transactions sort to the top
|
||||
status.sortKey = strprintf("%010d-%01d-%010u",
|
||||
(pindex ? pindex->nHeight : INT_MAX),
|
||||
(wtx.IsCoinBase() ? 1 : 0),
|
||||
wtx.nTimeReceived);
|
||||
status.confirmed = wtx.IsConfirmed();
|
||||
status.depth = wtx.GetDepthInMainChain();
|
||||
|
||||
if (!wtx.IsFinal())
|
||||
{
|
||||
if (wtx.nLockTime < 500000000)
|
||||
{
|
||||
status.status = TransactionStatus::OpenUntilBlock;
|
||||
status.open_for = nBestHeight - wtx.nLockTime;
|
||||
} else {
|
||||
status.status = TransactionStatus::OpenUntilDate;
|
||||
status.open_for = wtx.nLockTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
|
||||
{
|
||||
status.status = TransactionStatus::Offline;
|
||||
} else if (status.depth < 6)
|
||||
{
|
||||
status.status = TransactionStatus::Unconfirmed;
|
||||
} else
|
||||
{
|
||||
status.status = TransactionStatus::HaveConfirmations;
|
||||
}
|
||||
}
|
||||
|
||||
if (showTransaction(wtx))
|
||||
{
|
||||
|
||||
if (nNet > 0 || wtx.IsCoinBase())
|
||||
{
|
||||
//
|
||||
// Credit
|
||||
//
|
||||
TransactionRecord sub(hash, nTime, status);
|
||||
|
||||
sub.credit = nNet;
|
||||
|
||||
if (wtx.IsCoinBase())
|
||||
{
|
||||
// Generated
|
||||
sub.type = TransactionRecord::Generated;
|
||||
|
||||
if (nCredit == 0)
|
||||
{
|
||||
sub.status.maturity = TransactionStatus::Immature;
|
||||
|
||||
int64 nUnmatured = 0;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
nUnmatured += txout.GetCredit();
|
||||
sub.credit = nUnmatured;
|
||||
|
||||
if (wtx.IsInMainChain())
|
||||
{
|
||||
sub.status.maturity = TransactionStatus::MaturesIn;
|
||||
sub.status.matures_in = wtx.GetBlocksToMaturity();
|
||||
|
||||
// Check if the block was requested by anyone
|
||||
if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
|
||||
sub.status.maturity = TransactionStatus::MaturesWarning;
|
||||
}
|
||||
else
|
||||
{
|
||||
sub.status.maturity = TransactionStatus::NotAccepted;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!mapValue["from"].empty() || !mapValue["message"].empty())
|
||||
{
|
||||
// Received by IP connection
|
||||
sub.type = TransactionRecord::RecvFromIP;
|
||||
if (!mapValue["from"].empty())
|
||||
sub.address = mapValue["from"];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Received by Bitcoin Address
|
||||
sub.type = TransactionRecord::RecvFromAddress;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
{
|
||||
if (txout.IsMine())
|
||||
{
|
||||
std::vector<unsigned char> vchPubKey;
|
||||
if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
|
||||
{
|
||||
sub.address = PubKeyToAddress(vchPubKey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
parts.append(sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool fAllFromMe = true;
|
||||
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||||
fAllFromMe = fAllFromMe && txin.IsMine();
|
||||
|
||||
bool fAllToMe = true;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
fAllToMe = fAllToMe && txout.IsMine();
|
||||
|
||||
if (fAllFromMe && fAllToMe)
|
||||
{
|
||||
// Payment to self
|
||||
int64 nChange = wtx.GetChange();
|
||||
|
||||
parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::SendToSelf, "",
|
||||
-(nDebit - nChange), nCredit - nChange));
|
||||
}
|
||||
else if (fAllFromMe)
|
||||
{
|
||||
//
|
||||
// Debit
|
||||
//
|
||||
int64 nTxFee = nDebit - wtx.GetValueOut();
|
||||
|
||||
for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
|
||||
{
|
||||
const CTxOut& txout = wtx.vout[nOut];
|
||||
TransactionRecord sub(hash, nTime, status);
|
||||
|
||||
if (txout.IsMine())
|
||||
{
|
||||
// Sent to self
|
||||
sub.type = TransactionRecord::SendToSelf;
|
||||
sub.credit = txout.nValue;
|
||||
} else if (!mapValue["to"].empty())
|
||||
{
|
||||
// Sent to IP
|
||||
sub.type = TransactionRecord::SendToIP;
|
||||
sub.address = mapValue["to"];
|
||||
} else {
|
||||
// Sent to Bitcoin Address
|
||||
sub.type = TransactionRecord::SendToAddress;
|
||||
uint160 hash160;
|
||||
if (ExtractHash160(txout.scriptPubKey, hash160))
|
||||
sub.address = Hash160ToAddress(hash160);
|
||||
}
|
||||
|
||||
int64 nValue = txout.nValue;
|
||||
/* Add fee to first output */
|
||||
if (nTxFee > 0)
|
||||
{
|
||||
nValue += nTxFee;
|
||||
nTxFee = 0;
|
||||
}
|
||||
sub.debit = nValue;
|
||||
sub.status.sortKey += strprintf("-%d", nOut);
|
||||
|
||||
parts.append(sub);
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// Mixed debit transaction, can't break down payees
|
||||
//
|
||||
bool fAllMine = true;
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
|
||||
fAllMine = fAllMine && txout.IsMine();
|
||||
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||||
fAllMine = fAllMine && txin.IsMine();
|
||||
|
||||
parts.append(TransactionRecord(hash, nTime, status, TransactionRecord::Other, "", nNet, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
/* Internal implementation */
|
||||
class TransactionTableImpl
|
||||
{
|
||||
@ -347,7 +36,7 @@ public:
|
||||
/* TODO: Make note of new and removed transactions */
|
||||
/* insertedIndices */
|
||||
/* removedIndices */
|
||||
cachedWallet.append(decomposeTransaction(it->second));
|
||||
cachedWallet.append(TransactionRecord::decomposeTransaction(it->second));
|
||||
}
|
||||
}
|
||||
/* beginInsertRows(QModelIndex(), first, last) */
|
||||
@ -446,7 +135,35 @@ QVariant TransactionTableModel::formatTxDate(const TransactionRecord *wtx) const
|
||||
|
||||
QVariant TransactionTableModel::formatTxDescription(const TransactionRecord *wtx) const
|
||||
{
|
||||
return QVariant();
|
||||
QString description;
|
||||
/* TODO: look up label for wtx->address in address book if
|
||||
TransactionRecord::RecvFromAddress / TransactionRecord::SendToAddress
|
||||
|
||||
strDescription += strAddress.substr(0,12) + "... ";
|
||||
strDescription += "(" + strLabel + ")";
|
||||
*/
|
||||
switch(wtx->type)
|
||||
{
|
||||
case TransactionRecord::RecvFromAddress:
|
||||
description = tr("From: ") + QString::fromStdString(wtx->address);
|
||||
break;
|
||||
case TransactionRecord::RecvFromIP:
|
||||
description = tr("From IP: ") + QString::fromStdString(wtx->address);
|
||||
break;
|
||||
case TransactionRecord::SendToAddress:
|
||||
description = tr("To: ") + QString::fromStdString(wtx->address);
|
||||
break;
|
||||
case TransactionRecord::SendToIP:
|
||||
description = tr("To IP: ") + QString::fromStdString(wtx->address);
|
||||
break;
|
||||
case TransactionRecord::SendToSelf:
|
||||
description = tr("Payment to yourself");
|
||||
break;
|
||||
case TransactionRecord::Generated:
|
||||
description = tr("Generated");
|
||||
break;
|
||||
}
|
||||
return QVariant(description);
|
||||
}
|
||||
|
||||
QVariant TransactionTableModel::formatTxDebit(const TransactionRecord *wtx) const
|
||||
@ -503,18 +220,28 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
|
||||
} else if (role == Qt::TextAlignmentRole)
|
||||
{
|
||||
return column_alignments[index.column()];
|
||||
} else if (role == Qt::ForegroundRole)
|
||||
{
|
||||
if(rec->status.confirmed)
|
||||
{
|
||||
return QColor(0, 0, 0);
|
||||
} else {
|
||||
return QColor(128, 128, 128);
|
||||
}
|
||||
} else if (role == TypeRole)
|
||||
{
|
||||
/* user role: transaction type for filtering
|
||||
"s" (sent)
|
||||
"r" (received)
|
||||
"g" (generated)
|
||||
*/
|
||||
switch(index.row() % 3)
|
||||
switch(rec->type)
|
||||
{
|
||||
case 0: return QString("s");
|
||||
case 1: return QString("r");
|
||||
case 2: return QString("o");
|
||||
case TransactionRecord::RecvFromAddress:
|
||||
case TransactionRecord::RecvFromIP:
|
||||
case TransactionRecord::Generated:
|
||||
return TransactionTableModel::Received;
|
||||
case TransactionRecord::SendToAddress:
|
||||
case TransactionRecord::SendToIP:
|
||||
case TransactionRecord::SendToSelf:
|
||||
return TransactionTableModel::Sent;
|
||||
default:
|
||||
return TransactionTableModel::Other;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
|
Loading…
Reference in New Issue
Block a user