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.

3513 lines
103 KiB

// Copyright (c) 2009 Satoshi Nakamoto
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "headers.h"
#ifdef _MSC_VER
#include <crtdbg.h>
#endif
DEFINE_EVENT_TYPE(wxEVT_CROSSTHREADCALL)
DEFINE_EVENT_TYPE(wxEVT_REPLY1)
DEFINE_EVENT_TYPE(wxEVT_REPLY2)
DEFINE_EVENT_TYPE(wxEVT_REPLY3)
DEFINE_EVENT_TYPE(wxEVT_TABLEADDED)
DEFINE_EVENT_TYPE(wxEVT_TABLEUPDATED)
DEFINE_EVENT_TYPE(wxEVT_TABLEDELETED)
CMainFrame* pframeMain = NULL;
map<string, string> mapAddressBook;
CBitcoinTBIcon* taskBarIcon = NULL; // Tray icon
void ThreadRequestProductDetails(void* parg);
void ThreadRandSendTest(void* parg);
bool fRandSendTest = false;
void RandSend();
extern int g_isPainting;
// UI settings and their default values
int minimizeToTray = 1;
int closeToTray = 1;
int startOnSysBoot = 1;
int askBeforeClosing = 1;
int alwaysShowTrayIcon = 1;
//////////////////////////////////////////////////////////////////////////////
//
// Util
//
void HandleCtrlA(wxKeyEvent& event)
{
// Ctrl-a select all
wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
textCtrl->SetSelection(-1, -1);
event.Skip();
}
bool Is24HourTime()
{
//char pszHourFormat[256];
//pszHourFormat[0] = '\0';
//GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
//return (pszHourFormat[0] != '0');
return true;
}
string DateStr(int64 nTime)
{
return (string)wxDateTime((time_t)nTime).FormatDate();
}
string DateTimeStr(int64 nTime)
{
wxDateTime datetime((time_t)nTime);
if (Is24HourTime())
return (string)datetime.Format("%x %H:%M");
else
return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
}
wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
{
// Helper to simplify access to listctrl
wxListItem item;
item.m_itemId = nIndex;
item.m_col = nColumn;
item.m_mask = wxLIST_MASK_TEXT;
if (!listCtrl->GetItem(item))
return "";
return item.GetText();
}
int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
{
int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
listCtrl->SetItem(nIndex, 1, str1);
return nIndex;
}
int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
{
int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
listCtrl->SetItem(nIndex, 1, str1);
listCtrl->SetItem(nIndex, 2, str2);
listCtrl->SetItem(nIndex, 3, str3);
listCtrl->SetItem(nIndex, 4, str4);
return nIndex;
}
int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
{
int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
listCtrl->SetItem(nIndex, 1, str1);
listCtrl->SetItem(nIndex, 2, str2);
listCtrl->SetItem(nIndex, 3, str3);
listCtrl->SetItem(nIndex, 4, str4);
return nIndex;
}
void SetSelection(wxListCtrl* listCtrl, int nIndex)
{
int nSize = listCtrl->GetItemCount();
long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
for (int i = 0; i < nSize; i++)
listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
}
int GetSelection(wxListCtrl* listCtrl)
{
int nSize = listCtrl->GetItemCount();
for (int i = 0; i < nSize; i++)
if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
return i;
return -1;
}
string HtmlEscape(const char* psz, bool fMultiLine=false)
{
int len = 0;
for (const char* p = psz; *p; p++)
{
if (*p == '<') len += 4;
else if (*p == '>') len += 4;
else if (*p == '&') len += 5;
else if (*p == '"') len += 6;
else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
else if (*p == '\n' && fMultiLine) len += 5;
else
len++;
}
string str;
str.reserve(len);
for (const char* p = psz; *p; p++)
{
if (*p == '<') str += "&lt;";
else if (*p == '>') str += "&gt;";
else if (*p == '&') str += "&amp;";
else if (*p == '"') str += "&quot;";
else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += "&nbsp;";
else if (*p == '\n' && fMultiLine) str += "<br>\n";
else
str += *p;
}
return str;
}
string HtmlEscape(const string& str, bool fMultiLine=false)
{
return HtmlEscape(str.c_str(), fMultiLine);
}
void AddToMyProducts(CProduct product)
{
CProduct& productInsert = mapMyProducts[product.GetHash()];
productInsert = product;
InsertLine(pframeMain->m_listCtrlProductsSent, &productInsert,
product.mapValue["category"],
product.mapValue["title"].substr(0, 100),
product.mapValue["description"].substr(0, 100),
product.mapValue["price"],
"");
}
//////////////////////////////////////////////////////////////////////////////
//
// Custom events
//
set<void*> setCallbackAvailable;
CCriticalSection cs_setCallbackAvailable;
void AddCallbackAvailable(void* p)
{
CRITICAL_BLOCK(cs_setCallbackAvailable)
setCallbackAvailable.insert(p);
}
void RemoveCallbackAvailable(void* p)
{
CRITICAL_BLOCK(cs_setCallbackAvailable)
setCallbackAvailable.erase(p);
}
bool IsCallbackAvailable(void* p)
{
CRITICAL_BLOCK(cs_setCallbackAvailable)
return setCallbackAvailable.count(p);
return false;
}
template<typename T>
void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T pbeginIn, const T pendIn)
{
if (!pevthandler)
return;
const char* pbegin = (pendIn != pbeginIn) ? &pbeginIn[0] : NULL;
const char* pend = pbegin + (pendIn - pbeginIn) * sizeof(pbeginIn[0]);
wxCommandEvent event(nEventID);
wxString strData(wxChar(0), (pend - pbegin) / sizeof(wxChar) + 1);
memcpy(&strData[0], pbegin, pend - pbegin);
event.SetString(strData);
event.SetInt(pend - pbegin);
pevthandler->AddPendingEvent(event);
}
template<class T>
void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T& obj)
{
CDataStream ss;
ss << obj;
AddPendingCustomEvent(pevthandler, nEventID, ss.begin(), ss.end());
}
void AddPendingReplyEvent1(void* pevthandler, CDataStream& vRecv)
{
if (IsCallbackAvailable(pevthandler))
AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY1, vRecv.begin(), vRecv.end());
}
void AddPendingReplyEvent2(void* pevthandler, CDataStream& vRecv)
{
if (IsCallbackAvailable(pevthandler))
AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY2, vRecv.begin(), vRecv.end());
}
void AddPendingReplyEvent3(void* pevthandler, CDataStream& vRecv)
{
if (IsCallbackAvailable(pevthandler))
AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY3, vRecv.begin(), vRecv.end());
}
CDataStream GetStreamFromEvent(const wxCommandEvent& event)
{
wxString strData = event.GetString();
return CDataStream(strData.begin(), strData.begin() + event.GetInt(), SER_NETWORK);
}
//////////////////////////////////////////////////////////////////////////////
//
// CMainFrame
//
CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
{
Connect(wxEVT_CROSSTHREADCALL, wxCommandEventHandler(CMainFrame::OnCrossThreadCall), NULL, this);
// Init
fRefreshListCtrl = false;
fRefreshListCtrlRunning = false;
fOnSetFocusAddress = false;
pindexBestLast = NULL;
m_choiceFilter->SetSelection(0);
m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
m_listCtrl->SetFocus();
SetIcon(wxICON(bitcoin));
m_menuOptions->Check(wxID_OPTIONSGENERATEBITCOINS, fGenerateBitcoins);
// Init toolbar with transparency masked bitmaps
m_toolBar->ClearTools();
//// shouldn't have to do mask separately anymore, bitmap alpha support added in wx 2.8.9,
wxBitmap bmpSend(wxT("send20"), wxBITMAP_TYPE_RESOURCE);
bmpSend.SetMask(new wxMask(wxBitmap(wxT("send20mask"), wxBITMAP_TYPE_RESOURCE)));
m_toolBar->AddTool(wxID_BUTTONSEND, wxT("&Send Coins"), bmpSend, wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
wxBitmap bmpAddressBook(wxT("addressbook20"), wxBITMAP_TYPE_RESOURCE);
bmpAddressBook.SetMask(new wxMask(wxBitmap(wxT("addressbook20mask"), wxBITMAP_TYPE_RESOURCE)));
m_toolBar->AddTool(wxID_BUTTONRECEIVE, wxT("&Address Book"), bmpAddressBook, wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
m_toolBar->Realize();
// Init column headers
int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
m_listCtrl->InsertColumn(0, "", wxLIST_FORMAT_LEFT, 0);
m_listCtrl->InsertColumn(1, "", wxLIST_FORMAT_LEFT, 0);
m_listCtrl->InsertColumn(2, "Status", wxLIST_FORMAT_LEFT, 90);
m_listCtrl->InsertColumn(3, "Date", wxLIST_FORMAT_LEFT, nDateWidth);
m_listCtrl->InsertColumn(4, "Description", wxLIST_FORMAT_LEFT, 409 - nDateWidth);
m_listCtrl->InsertColumn(5, "Debit", wxLIST_FORMAT_RIGHT, 79);
m_listCtrl->InsertColumn(6, "Credit", wxLIST_FORMAT_RIGHT, 79);
//m_listCtrlProductsSent->InsertColumn(0, "Category", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlProductsSent->InsertColumn(1, "Title", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlProductsSent->InsertColumn(2, "Description", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlProductsSent->InsertColumn(3, "Price", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlProductsSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlOrdersSent->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlOrdersSent->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlOrdersSent->InsertColumn(2, "", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlOrdersSent->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlOrdersSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlOrdersReceived->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlOrdersReceived->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlOrdersReceived->InsertColumn(2, "Payment Status", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlOrdersReceived->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100);
//m_listCtrlOrdersReceived->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
// Init status bar
int pnWidths[3] = { -100, 81, 286 };
m_statusBar->SetFieldsCount(3, pnWidths);
// Fill your address text box
vector<unsigned char> vchPubKey;
if (CWalletDB("r").ReadDefaultKey(vchPubKey))
m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
// Fill listctrl with wallet transactions
RefreshListCtrl();
}
CMainFrame::~CMainFrame()
{
pframeMain = NULL;
}
void Shutdown(void* parg)
{
static CCriticalSection cs_Shutdown;
CRITICAL_BLOCK(cs_Shutdown)
{
fShutdown = true;
nTransactionsUpdated++;
DBFlush(false);
StopNode();
DBFlush(true);
printf("Bitcoin exiting\n");
exit(0);
}
}
void CMainFrame::OnClose(wxCloseEvent& event)
{
if (closeToTray && event.CanVeto()) {
event.Veto();
SendToTray();
}
else if (!event.CanVeto() || !askBeforeClosing || wxMessageBox("Quit program?", "Confirm", wxYES_NO, this) == wxYES) {
delete taskBarIcon;
Destroy();
_beginthread(Shutdown, 0, NULL);
}
}
void CMainFrame::OnIconize(wxIconizeEvent& event)
{
if (minimizeToTray) {
SendToTray();
}
}
void CMainFrame::SendToTray()
{
Hide();
taskBarIcon->Show();
}
void CMainFrame::OnMouseEvents(wxMouseEvent& event)
{
RandAddSeed();
RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
}
void CMainFrame::OnListColBeginDrag(wxListEvent& event)
{
// Hidden columns not resizeable
if (event.GetColumn() <= 1 && !fDebug)
event.Veto();
}
void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6)
{
string str0 = strSort;
long nData = *(long*)&hashKey;
if (fNew)
{
nIndex = m_listCtrl->InsertItem(0, str0);
}
else
{
if (nIndex == -1)
{
// Find item
while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())
break;
if (nIndex == -1)
{
printf("CMainFrame::InsertLine : Couldn't find item to be updated\n");
return;
}
}
// If sort key changed, must delete and reinsert to make it relocate
if (GetItemText(m_listCtrl, nIndex, 0) != str0)
{
m_listCtrl->DeleteItem(nIndex);
nIndex = m_listCtrl->InsertItem(0, str0);
}
}
m_listCtrl->SetItem(nIndex, 1, hashKey.ToString());
m_listCtrl->SetItem(nIndex, 2, str2);
m_listCtrl->SetItem(nIndex, 3, str3);
m_listCtrl->SetItem(nIndex, 4, str4);
m_listCtrl->SetItem(nIndex, 5, str5);
m_listCtrl->SetItem(nIndex, 6, str6);
m_listCtrl->SetItemData(nIndex, nData);
}
string FormatTxStatus(const CWalletTx& wtx)
{
// Status
int nDepth = wtx.GetDepthInMainChain();
if (!wtx.IsFinal())
return strprintf("Open for %d blocks", nBestHeight - wtx.nLockTime);
else if (nDepth < 6)
return strprintf("%d/unconfirmed", nDepth);
else
return strprintf("%d blocks", nDepth);
}
string SingleLine(const string& strIn)
{
string strOut;
bool fOneSpace = false;
foreach(int c, strIn)
{
if (isspace(c))
{
fOneSpace = true;
}
else if (c > ' ')
{
if (fOneSpace && !strOut.empty())
strOut += ' ';
strOut += c;
fOneSpace = false;
}
}
return strOut;
}
void CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
{
int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
int64 nCredit = wtx.GetCredit();
int64 nDebit = wtx.GetDebit();
int64 nNet = nCredit - nDebit;
uint256 hash = wtx.GetHash();
string strStatus = FormatTxStatus(wtx);
map<string, string> mapValue = wtx.mapValue;
// Find the block the tx is in
CBlockIndex* pindex = NULL;
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
if (mi != mapBlockIndex.end())
pindex = (*mi).second;
// Sort order, unrecorded transactions sort to the top
string strSort = strprintf("%010d-%01d-%010u",
(pindex ? pindex->nHeight : INT_MAX),
(wtx.IsCoinBase() ? 1 : 0),
wtx.nTimeReceived);
// Insert line
if (nNet > 0 || wtx.IsCoinBase())
{
//
// Credit
//
string strDescription;
if (wtx.IsCoinBase())
{
// Coinbase
strDescription = "Generated";
if (nCredit == 0)
{
int64 nUnmatured = 0;
foreach(const CTxOut& txout, wtx.vout)
nUnmatured += txout.GetCredit();
if (wtx.IsInMainChain())
strDescription += strprintf(" (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
else
strDescription += " (not accepted)";
}
}
else if (!mapValue["from"].empty() || !mapValue["message"].empty())
{
// Online transaction
if (!mapValue["from"].empty())
strDescription += "From: " + mapValue["from"];
if (!mapValue["message"].empty())
{
if (!strDescription.empty())
strDescription += " - ";
strDescription += mapValue["message"];
}
}
else
{
// Offline transaction
foreach(const CTxOut& txout, wtx.vout)
{
if (txout.IsMine())
{
vector<unsigned char> vchPubKey;
if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
{
string strAddress = PubKeyToAddress(vchPubKey);
if (mapAddressBook.count(strAddress))
{
//strDescription += "Received payment to ";
//strDescription += "Received with address ";
strDescription += "From: unknown, To: ";
strDescription += strAddress;
/// The labeling feature is just too confusing, so I hid it
/// by putting it at the end where it runs off the screen.
/// It can still be seen by widening the column, or in the
/// details dialog.
if (!mapAddressBook[strAddress].empty())
strDescription += " (" + mapAddressBook[strAddress] + ")";
}
}
break;
}
}
}
InsertLine(fNew, nIndex, hash, strSort,
strStatus,
nTime ? DateTimeStr(nTime) : "",
SingleLine(strDescription),
"",
FormatMoney(nNet, true));
}
else
{
bool fAllFromMe = true;
foreach(const CTxIn& txin, wtx.vin)
fAllFromMe = fAllFromMe && txin.IsMine();
bool fAllToMe = true;
foreach(const CTxOut& txout, wtx.vout)
fAllToMe = fAllToMe && txout.IsMine();
if (fAllFromMe && fAllToMe)
{
// Payment to self
int64 nValue = wtx.vout[0].nValue;
InsertLine(fNew, nIndex, hash, strSort,
strStatus,
nTime ? DateTimeStr(nTime) : "",
"Payment to yourself",
FormatMoney(nNet - nValue, true),
FormatMoney(nValue, true));
}
else if (fAllFromMe)
{
//
// Debit
//
int64 nTxFee = nDebit - wtx.GetValueOut();
for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
{
const CTxOut& txout = wtx.vout[nOut];
if (txout.IsMine())
continue;
string strAddress;
if (!mapValue["to"].empty())
{
// Online transaction
strAddress = mapValue["to"];
}
else
{
// Offline transaction
uint160 hash160;
if (ExtractHash160(txout.scriptPubKey, hash160))
strAddress = Hash160ToAddress(hash160);
}
string strDescription = "To: ";
if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
strDescription += mapAddressBook[strAddress] + " ";
strDescription += strAddress;
if (!mapValue["message"].empty())
{
if (!strDescription.empty())
strDescription += " - ";
strDescription += mapValue["message"];
}
int64 nValue = txout.nValue;
if (nOut == 0 && nTxFee > 0)
nValue += nTxFee;
InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
strStatus,
nTime ? DateTimeStr(nTime) : "",
SingleLine(strDescription),
FormatMoney(-nValue, true),
"");
}
}
else
{
//
// Mixed debit transaction, can't break down payees
//
bool fAllMine = true;
foreach(const CTxOut& txout, wtx.vout)
fAllMine = fAllMine && txout.IsMine();
foreach(const CTxIn& txin, wtx.vin)
fAllMine = fAllMine && txin.IsMine();
InsertLine(fNew, nIndex, hash, strSort,
strStatus,
nTime ? DateTimeStr(nTime) : "",
"",
FormatMoney(nNet, true),
"");
}
}
}
void CMainFrame::RefreshStatus()
{
static int nLastTop;
int nTop = m_listCtrl->GetTopItem();
if (nTop == nLastTop && pindexBestLast == pindexBest)
return;
TRY_CRITICAL_BLOCK(cs_mapWallet)
{
int nStart = nTop;
int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
if (pindexBestLast == pindexBest)
{
if (nStart >= nLastTop && nStart < nLastTop + 100)
nStart = nLastTop + 100;
if (nEnd >= nLastTop && nEnd < nLastTop + 100)
nEnd = nLastTop;
}
nLastTop = nTop;
pindexBestLast = pindexBest;
for (int nIndex = nStart; nIndex < nEnd; nIndex++)
{
uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
if (mi == mapWallet.end())
{
printf("CMainFrame::RefreshStatus() : tx not found in mapWallet\n");
continue;
}
const CWalletTx& wtx = (*mi).second;
if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
InsertTransaction(wtx, false, nIndex);
else
m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
}
}
}
void CMainFrame::RefreshListCtrl()
{
fRefreshListCtrl = true;
::wxWakeUpIdle();
}
void CMainFrame::OnIdle(wxIdleEvent& event)
{
if (fRefreshListCtrl)
{
// Collect list of wallet transactions and sort newest first
bool fEntered = false;
vector<pair<unsigned int, uint256> > vSorted;
TRY_CRITICAL_BLOCK(cs_mapWallet)
{
printf("RefreshListCtrl starting\n");
fEntered = true;
fRefreshListCtrl = false;
vWalletUpdated.clear();
// Do the newest transactions first
vSorted.reserve(mapWallet.size());
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
unsigned int nTime = UINT_MAX - wtx.GetTxTime();
vSorted.push_back(make_pair(nTime, (*it).first));
}
m_listCtrl->DeleteAllItems();
}
if (!fEntered)
return;
sort(vSorted.begin(), vSorted.end());
// Fill list control
for (int i = 0; i < vSorted.size();)
{
if (fShutdown)
return;
bool fEntered = false;
TRY_CRITICAL_BLOCK(cs_mapWallet)
{
fEntered = true;
uint256& hash = vSorted[i++].second;
map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
if (mi != mapWallet.end())
InsertTransaction((*mi).second, true);
}
if (!fEntered || i == 100 || i % 500 == 0)
wxYield();
}
printf("RefreshListCtrl done\n");
}
else
{
// Check for time updates
static int64 nLastTime;
if (GetTime() > nLastTime + 30)
{
TRY_CRITICAL_BLOCK(cs_mapWallet)
{
nLastTime = GetTime();
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
CWalletTx& wtx = (*it).second;
if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
InsertTransaction(wtx, false);
}
}
}
}
}
void CMainFrame::OnPaint(wxPaintEvent& event)
{
event.Skip();
}
void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
{
// Update listctrl contents
if (!vWalletUpdated.empty())
{
TRY_CRITICAL_BLOCK(cs_mapWallet)
{
pair<uint256, bool> item;
foreach(item, vWalletUpdated)
{
bool fNew = item.second;
map<uint256, CWalletTx>::iterator mi = mapWallet.find(item.first);
if (mi != mapWallet.end())
{
printf("vWalletUpdated: %s %s\n", (*mi).second.GetHash().ToString().substr(0,6).c_str(), fNew ? "new" : "");
InsertTransaction((*mi).second, fNew);
}
}
m_listCtrl->ScrollList(0, INT_MAX);
vWalletUpdated.clear();
}
}
// Update status column of visible items only
RefreshStatus();
// Update status bar
string strGen = "";
if (fGenerateBitcoins)
strGen = " Generating";
if (fGenerateBitcoins && vNodes.empty())
strGen = "(not connected)";
m_statusBar->SetStatusText(strGen, 1);
string strStatus = strprintf(" %d connections %d blocks %d transactions", vNodes.size(), nBestHeight + 1, m_listCtrl->GetItemCount());
m_statusBar->SetStatusText(strStatus, 2);
// Balance total
TRY_CRITICAL_BLOCK(cs_mapWallet)
m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
m_listCtrl->OnPaint(event);
}
void CrossThreadCall(wxCommandEvent& event)
{
if (pframeMain)
pframeMain->GetEventHandler()->AddPendingEvent(event);
}
void CrossThreadCall(int nID, void* pdata)
{
wxCommandEvent event;
event.SetInt(nID);
event.SetClientData(pdata);
if (pframeMain)
pframeMain->GetEventHandler()->AddPendingEvent(event);
}
void CMainFrame::OnCrossThreadCall(wxCommandEvent& event)
{
void* pdata = event.GetClientData();
switch (event.GetInt())
{
case UICALL_ADDORDER:
{
break;
}
case UICALL_UPDATEORDER:
{
break;
}
}
}
void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
{
Close(true);
}
void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
{
GenerateBitcoins(event.IsChecked());
Refresh();
wxPaintEvent eventPaint;
AddPendingEvent(eventPaint);
}
void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
{
OnButtonChange(event);
}
void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
{
COptionsDialog dialog(this);
dialog.ShowModal();
}
void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
{
CAboutDialog dialog(this);
dialog.ShowModal();
}
void CMainFrame::OnUpdateMenuGenerate( wxUpdateUIEvent& event ) {
event.Check(fGenerateBitcoins);
}
void CMainFrame::OnButtonSend(wxCommandEvent& event)
{
/// debug test
if (fRandSendTest)
{
RandSend();
return;
}
// Toolbar: Send
CSendDialog dialog(this);
dialog.ShowModal();
}
void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
{
// Toolbar: Address Book
CAddressBookDialog dialogAddr(this, "", false);
if (dialogAddr.ShowModal() == 2)
{
// Send
CSendDialog dialogSend(this, dialogAddr.GetAddress());
dialogSend.ShowModal();
}
}
void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
{
// Automatically select-all when entering window
m_textCtrlAddress->SetSelection(-1, -1);
fOnSetFocusAddress = true;
event.Skip();
}
void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
{
if (fOnSetFocusAddress)
m_textCtrlAddress->SetSelection(-1, -1);
fOnSetFocusAddress = false;
event.Skip();
}
void CMainFrame::OnButtonCopy(wxCommandEvent& event)
{
// Copy address box to clipboard
if (wxTheClipboard->Open())
{
wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
wxTheClipboard->Close();
}
}
void CMainFrame::OnButtonChange(wxCommandEvent& event)
{
CYourAddressDialog dialog(this, string(m_textCtrlAddress->GetValue()));
if (!dialog.ShowModal())
return;
string strAddress = (string)dialog.GetAddress();
if (strAddress != m_textCtrlAddress->GetValue())
{
uint160 hash160;
if (!AddressToHash160(strAddress, hash160))
return;
if (!mapPubKeys.count(hash160))
return;
CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
m_textCtrlAddress->SetValue(strAddress);
}
}
void CMainFrame::OnListItemActivatedAllTransactions(wxListEvent& event)
{
uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
CWalletTx wtx;
CRITICAL_BLOCK(cs_mapWallet)
{
map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
if (mi == mapWallet.end())
{
printf("CMainFrame::OnListItemActivatedAllTransactions() : tx not found in mapWallet\n");
return;
}
wtx = (*mi).second;
}
CTxDetailsDialog dialog(this, wtx);
dialog.ShowModal();
//CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
//pdialog->Show();
}
void CMainFrame::OnListItemActivatedProductsSent(wxListEvent& event)
{
CProduct& product = *(CProduct*)event.GetItem().GetData();
CEditProductDialog* pdialog = new CEditProductDialog(this);
pdialog->SetProduct(product);
pdialog->Show();
}
void CMainFrame::OnListItemActivatedOrdersSent(wxListEvent& event)
{
CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();
CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, false);
pdialog->Show();
}
void CMainFrame::OnListItemActivatedOrdersReceived(wxListEvent& event)
{
CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();
CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, true);
pdialog->Show();
}
//////////////////////////////////////////////////////////////////////////////
//
// CTxDetailsDialog
//
CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
{
string strHTML;
strHTML.reserve(4000);
strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
int64 nTime = wtx.GetTxTime();
int64 nCredit = wtx.GetCredit();
int64 nDebit = wtx.GetDebit();
int64 nNet = nCredit - nDebit;
strHTML += "<b>Status:</b> " + FormatTxStatus(wtx) + "<br>";
strHTML += "<b>Date:</b> " + (nTime ? DateTimeStr(nTime) : "") + "<br>";
//
// From
//
if (wtx.IsCoinBase())
{
strHTML += "<b>Source:</b> Generated<br>";
}
else if (!wtx.mapValue["from"].empty())
{
// Online transaction
if (!wtx.mapValue["from"].empty())
strHTML += "<b>From:</b> " + HtmlEscape(wtx.mapValue["from"]) + "<br>";
}
else
{
// Offline transaction
if (nNet > 0)
{
// Credit
foreach(const CTxOut& txout, wtx.vout)
{
if (txout.IsMine())
{
vector<unsigned char> vchPubKey;
if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
{
string strAddress = PubKeyToAddress(vchPubKey);
if (mapAddressBook.count(strAddress))
{
strHTML += "<b>From:</b> unknown<br>";
strHTML += "<b>To:</b> ";
strHTML += HtmlEscape(strAddress);
if (!mapAddressBook[strAddress].empty())
strHTML += " (yours, label: " + mapAddressBook[strAddress] + ")";
else
strHTML += " (yours)";
strHTML += "<br>";
}
}
break;
}
}
}
}
//
// To
//
string strAddress;
if (!wtx.mapValue["to"].empty())
{
// Online transaction
strAddress = wtx.mapValue["to"];
strHTML += "<b>To:</b> ";
if (mapAddressBook.count