upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/ui.cpp
diff options
context:
space:
mode:
authorBitflash-sh <btf01@proton.me>2026-07-24 11:18:11 -0300
committerBitflash-sh <btf01@proton.me>2026-07-24 11:18:11 -0300
commit51d617e70f90a972185917ea0096c1ecef2bfa8b (patch)
tree9fbbcd867de9de9d0776afdd02f594ba312c46eb /src/ui.cpp
Bitflash: fair-launch release
Satoshi's bitcoin-0.1.0 reborn with RandomX CPU proof of work, anonymous .btf addressing over Nostr with CGNAT rendezvous, automatic peer discovery, a headless Linux node, and Windows GUI wallet. Fair launch, no premine, 21M cap.
Diffstat (limited to 'src/ui.cpp')
-rw-r--r--src/ui.cpp3265
1 files changed, 3265 insertions, 0 deletions
diff --git a/src/ui.cpp b/src/ui.cpp
new file mode 100644
index 0000000..3e734a5
--- /dev/null
+++ b/src/ui.cpp
@@ -0,0 +1,3265 @@
1// Copyright (c) 2009 Satoshi Nakamoto
2// Distributed under the MIT/X11 software license, see the accompanying
3// file license.txt or http://www.opensource.org/licenses/mit-license.php.
4
5#include "headers.h"
6#ifdef _MSC_VER
7#include <crtdbg.h>
8#endif
9
10
11
12DEFINE_EVENT_TYPE(wxEVT_CROSSTHREADCALL)
13
14// wx3 is Unicode: conversion bridges for the C functions inherited from 2009
15static inline bool ParseMoney(const wxString& str, int64& nRet)
16{
17 return ParseMoney(std::string(str.mb_str()).c_str(), nRet);
18}
19DEFINE_EVENT_TYPE(wxEVT_REPLY1)
20DEFINE_EVENT_TYPE(wxEVT_REPLY2)
21DEFINE_EVENT_TYPE(wxEVT_REPLY3)
22DEFINE_EVENT_TYPE(wxEVT_TABLEADDED)
23DEFINE_EVENT_TYPE(wxEVT_TABLEUPDATED)
24DEFINE_EVENT_TYPE(wxEVT_TABLEDELETED)
25
26CMainFrame* pframeMain = NULL;
27map<string, string> mapAddressBook;
28
29
30void ThreadRequestProductDetails(void* parg);
31void ThreadRandSendTest(void* parg);
32bool fRandSendTest = false;
33void RandSend();
34
35
36
37
38
39
40//////////////////////////////////////////////////////////////////////////////
41//
42// Util
43//
44
45void HandleCtrlA(wxKeyEvent& event)
46{
47 // Ctrl-a select all
48 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
49 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
50 textCtrl->SetSelection(-1, -1);
51 event.Skip();
52}
53
54bool Is24HourTime()
55{
56 //char pszHourFormat[256];
57 //pszHourFormat[0] = '\0';
58 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
59 //return (pszHourFormat[0] != '0');
60 return true;
61}
62
63string DateStr(int64 nTime)
64{
65 return (string)wxDateTime((time_t)nTime).FormatDate();
66}
67
68string DateTimeStr(int64 nTime)
69{
70 wxDateTime datetime((time_t)nTime);
71 if (Is24HourTime())
72 return (string)datetime.Format("%x %H:%M");
73 else
74 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
75}
76
77wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
78{
79 // Helper to simplify access to listctrl
80 wxListItem item;
81 item.m_itemId = nIndex;
82 item.m_col = nColumn;
83 item.m_mask = wxLIST_MASK_TEXT;
84 if (!listCtrl->GetItem(item))
85 return "";
86 return item.GetText();
87}
88
89int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
90{
91 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
92 listCtrl->SetItem(nIndex, 1, str1);
93 return nIndex;
94}
95
96int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
97{
98 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
99 listCtrl->SetItem(nIndex, 1, str1);
100 listCtrl->SetItem(nIndex, 2, str2);
101 listCtrl->SetItem(nIndex, 3, str3);
102 listCtrl->SetItem(nIndex, 4, str4);
103 return nIndex;
104}
105
106int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
107{
108 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
109 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
110 listCtrl->SetItem(nIndex, 1, str1);
111 listCtrl->SetItem(nIndex, 2, str2);
112 listCtrl->SetItem(nIndex, 3, str3);
113 listCtrl->SetItem(nIndex, 4, str4);
114 return nIndex;
115}
116
117void SetSelection(wxListCtrl* listCtrl, int nIndex)
118{
119 int nSize = listCtrl->GetItemCount();
120 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
121 for (int i = 0; i < nSize; i++)
122 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
123}
124
125int GetSelection(wxListCtrl* listCtrl)
126{
127 int nSize = listCtrl->GetItemCount();
128 for (int i = 0; i < nSize; i++)
129 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
130 return i;
131 return -1;
132}
133
134
135string HtmlEscape(const char* psz, bool fMultiLine=false)
136{
137 int len = 0;
138 for (const char* p = psz; *p; p++)
139 {
140 if (*p == '<') len += 4;
141 else if (*p == '>') len += 4;
142 else if (*p == '&') len += 5;
143 else if (*p == '"') len += 6;
144 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
145 else if (*p == '\n' && fMultiLine) len += 5;
146 else
147 len++;
148 }
149 string str;
150 str.reserve(len);
151 for (const char* p = psz; *p; p++)
152 {
153 if (*p == '<') str += "&lt;";
154 else if (*p == '>') str += "&gt;";
155 else if (*p == '&') str += "&amp;";
156 else if (*p == '"') str += "&quot;";
157 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += "&nbsp;";
158 else if (*p == '\n' && fMultiLine) str += "<br>\n";
159 else
160 str += *p;
161 }
162 return str;
163}
164
165string HtmlEscape(const string& str, bool fMultiLine=false)
166{
167 return HtmlEscape(str.c_str(), fMultiLine);
168}
169
170void AddToMyProducts(CProduct product)
171{
172 CProduct& productInsert = mapMyProducts[product.GetHash()];
173 productInsert = product;
174 InsertLine(pframeMain->m_listCtrlProductsSent, &productInsert,
175 product.mapValue["category"],
176 product.mapValue["title"].substr(0, 100),
177 product.mapValue["description"].substr(0, 100),
178 product.mapValue["price"],
179 "");
180}
181
182
183
184
185
186
187//////////////////////////////////////////////////////////////////////////////
188//
189// Custom events
190//
191
192set<void*> setCallbackAvailable;
193CCriticalSection cs_setCallbackAvailable;
194
195void AddCallbackAvailable(void* p)
196{
197 CRITICAL_BLOCK(cs_setCallbackAvailable)
198 setCallbackAvailable.insert(p);
199}
200
201void RemoveCallbackAvailable(void* p)
202{
203 CRITICAL_BLOCK(cs_setCallbackAvailable)
204 setCallbackAvailable.erase(p);
205}
206
207bool IsCallbackAvailable(void* p)
208{
209 CRITICAL_BLOCK(cs_setCallbackAvailable)
210 return setCallbackAvailable.count(p);
211 return false;
212}
213
214template<typename T>
215void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T pbeginIn, const T pendIn)
216{
217 if (!pevthandler)
218 return;
219
220 const char* pbegin = (pendIn != pbeginIn) ? (const char*)&pbeginIn[0] : NULL;
221 const char* pend = pbegin + (pendIn - pbeginIn) * sizeof(pbeginIn[0]);
222 wxCommandEvent event(nEventID);
223 // wxString in wx3 is Unicode and does not carry raw bytes;
224 // the serialized data travels in a buffer that the receiver frees
225 event.SetClientData(new std::string(pbegin, pend));
226
227 pevthandler->AddPendingEvent(event);
228}
229
230template<class T>
231void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T& obj)
232{
233 CDataStream ss;
234 ss << obj;
235 AddPendingCustomEvent(pevthandler, nEventID, ss.begin(), ss.end());
236}
237
238void AddPendingReplyEvent1(void* pevthandler, CDataStream& vRecv)
239{
240 if (IsCallbackAvailable(pevthandler))
241 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY1, vRecv.begin(), vRecv.end());
242}
243
244void AddPendingReplyEvent2(void* pevthandler, CDataStream& vRecv)
245{
246 if (IsCallbackAvailable(pevthandler))
247 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY2, vRecv.begin(), vRecv.end());
248}
249
250void AddPendingReplyEvent3(void* pevthandler, CDataStream& vRecv)
251{
252 if (IsCallbackAvailable(pevthandler))
253 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY3, vRecv.begin(), vRecv.end());
254}
255
256CDataStream GetStreamFromEvent(const wxCommandEvent& event)
257{
258 std::string* pstrData = (std::string*)event.GetClientData();
259 if (pstrData == NULL)
260 return CDataStream(SER_NETWORK);
261 CDataStream ss(pstrData->data(), pstrData->data() + pstrData->size(), SER_NETWORK);
262 delete pstrData;
263 ((wxCommandEvent&)event).SetClientData(NULL);
264 return ss;
265}
266
267
268
269
270
271
272
273//////////////////////////////////////////////////////////////////////////////
274//
275// CMainFrame
276//
277
278CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
279{
280 Connect(wxEVT_CROSSTHREADCALL, wxCommandEventHandler(CMainFrame::OnCrossThreadCall), NULL, this);
281
282 // Init
283 fRefreshListCtrl = false;
284 fRefreshListCtrlRunning = false;
285 fOnSetFocusAddress = false;
286 pindexBestLast = NULL;
287 m_choiceFilter->SetSelection(0);
288 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
289 m_listCtrl->SetFocus();
290 SetIcon(wxICON(bitcoin));
291 m_menuOptions->Check(wxID_OPTIONSGENERATEBITCOINS, fGenerateBitcoins);
292
293 // Init toolbar with transparency masked bitmaps
294 m_toolBar->ClearTools();
295
296 //// shouldn't have to do mask separately anymore, bitmap alpha support added in wx 2.8.9,
297 wxBitmap bmpSend(wxT("send20"), wxBITMAP_TYPE_RESOURCE);
298 bmpSend.SetMask(new wxMask(wxBitmap(wxT("send20mask"), wxBITMAP_TYPE_RESOURCE)));
299 m_toolBar->AddTool(wxID_BUTTONSEND, wxT("&Send Coins"), bmpSend, wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
300
301 wxBitmap bmpAddressBook(wxT("addressbook20"), wxBITMAP_TYPE_RESOURCE);
302 bmpAddressBook.SetMask(new wxMask(wxBitmap(wxT("addressbook20mask"), wxBITMAP_TYPE_RESOURCE)));
303 m_toolBar->AddTool(wxID_BUTTONRECEIVE, wxT("&Address Book"), bmpAddressBook, wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
304
305 m_toolBar->Realize();
306
307 // Init column headers
308 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
309 m_listCtrl->InsertColumn(0, "", wxLIST_FORMAT_LEFT, 0);
310 m_listCtrl->InsertColumn(1, "", wxLIST_FORMAT_LEFT, 0);
311 m_listCtrl->InsertColumn(2, "Status", wxLIST_FORMAT_LEFT, 90);
312 m_listCtrl->InsertColumn(3, "Date", wxLIST_FORMAT_LEFT, nDateWidth);
313 m_listCtrl->InsertColumn(4, "Description", wxLIST_FORMAT_LEFT, 409 - nDateWidth);
314 m_listCtrl->InsertColumn(5, "Debit", wxLIST_FORMAT_RIGHT, 79);
315 m_listCtrl->InsertColumn(6, "Credit", wxLIST_FORMAT_RIGHT, 79);
316
317 //m_listCtrlProductsSent->InsertColumn(0, "Category", wxLIST_FORMAT_LEFT, 100);
318 //m_listCtrlProductsSent->InsertColumn(1, "Title", wxLIST_FORMAT_LEFT, 100);
319 //m_listCtrlProductsSent->InsertColumn(2, "Description", wxLIST_FORMAT_LEFT, 100);
320 //m_listCtrlProductsSent->InsertColumn(3, "Price", wxLIST_FORMAT_LEFT, 100);
321 //m_listCtrlProductsSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
322
323 //m_listCtrlOrdersSent->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100);
324 //m_listCtrlOrdersSent->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100);
325 //m_listCtrlOrdersSent->InsertColumn(2, "", wxLIST_FORMAT_LEFT, 100);
326 //m_listCtrlOrdersSent->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100);
327 //m_listCtrlOrdersSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
328
329 //m_listCtrlOrdersReceived->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100);
330 //m_listCtrlOrdersReceived->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100);
331 //m_listCtrlOrdersReceived->InsertColumn(2, "Payment Status", wxLIST_FORMAT_LEFT, 100);
332 //m_listCtrlOrdersReceived->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100);
333 //m_listCtrlOrdersReceived->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
334
335 // Init status bar
336 int pnWidths[3] = { -100, 81, 286 };
337 m_statusBar->SetFieldsCount(3, pnWidths);
338
339 // Fill your address text box
340 vector<unsigned char> vchPubKey;
341 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
342 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
343
344 // Fill listctrl with wallet transactions
345 RefreshListCtrl();
346}
347
348CMainFrame::~CMainFrame()
349{
350 pframeMain = NULL;
351}
352
353void Shutdown(void* parg)
354{
355 static CCriticalSection cs_Shutdown;
356 CRITICAL_BLOCK(cs_Shutdown)
357 {
358 fShutdown = true;
359 nTransactionsUpdated++;
360 DBFlush(false);
361 StopNode();
362 DBFlush(true);
363
364 printf("Bitcoin exiting\n");
365 exit(0);
366 }
367}
368
369void CMainFrame::OnClose(wxCloseEvent& event)
370{
371 Destroy();
372 _beginthread(Shutdown, 0, NULL);
373}
374
375void CMainFrame::OnMouseEvents(wxMouseEvent& event)
376{
377 RandAddSeed();
378 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
379 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
380}
381
382void CMainFrame::OnListColBeginDrag(wxListEvent& event)
383{
384 // Hidden columns not resizeable
385 if (event.GetColumn() <= 1 && !fDebug)
386 event.Veto();
387}
388
389void 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)
390{
391 string str0 = strSort;
392 long nData = *(long*)&hashKey;
393
394 if (fNew)
395 {
396 nIndex = m_listCtrl->InsertItem(0, str0);
397 }
398 else
399 {
400 if (nIndex == -1)
401 {
402 // Find item
403 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
404 if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())
405 break;
406 if (nIndex == -1)
407 {
408 printf("CMainFrame::InsertLine : Couldn't find item to be updated\n");
409 return;
410 }
411 }
412
413 // If sort key changed, must delete and reinsert to make it relocate
414 if (GetItemText(m_listCtrl, nIndex, 0) != str0)
415 {
416 m_listCtrl->DeleteItem(nIndex);
417 nIndex = m_listCtrl->InsertItem(0, str0);
418 }
419 }
420
421 m_listCtrl->SetItem(nIndex, 1, hashKey.ToString());
422 m_listCtrl->SetItem(nIndex, 2, str2);
423 m_listCtrl->SetItem(nIndex, 3, str3);
424 m_listCtrl->SetItem(nIndex, 4, str4);
425 m_listCtrl->SetItem(nIndex, 5, str5);
426 m_listCtrl->SetItem(nIndex, 6, str6);
427 m_listCtrl->SetItemData(nIndex, nData);
428}
429
430string FormatTxStatus(const CWalletTx& wtx)
431{
432 // Status
433 int nDepth = wtx.GetDepthInMainChain();
434 if (!wtx.IsFinal())
435 return strprintf("Open for %d blocks", nBestHeight - wtx.nLockTime);
436 else if (nDepth < 6)
437 return strprintf("%d/unconfirmed", nDepth);
438 else
439 return strprintf("%d blocks", nDepth);
440}
441
442void CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
443{
444 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
445 int64 nCredit = wtx.GetCredit();
446 int64 nDebit = wtx.GetDebit();
447 int64 nNet = nCredit - nDebit;
448 uint256 hash = wtx.GetHash();
449 string strStatus = FormatTxStatus(wtx);
450 map<string, string> mapValue = wtx.mapValue;
451
452 // Find the block the tx is in
453 CBlockIndex* pindex = NULL;
454 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
455 if (mi != mapBlockIndex.end())
456 pindex = (*mi).second;
457
458 // Sort order, unrecorded transactions sort to the top
459 string strSort = strprintf("%010d-%01d-%010u",
460 (pindex ? pindex->nHeight : INT_MAX),
461 (wtx.IsCoinBase() ? 1 : 0),
462 wtx.nTimeReceived);
463
464 // Insert line
465 if (nNet > 0 || wtx.IsCoinBase())
466 {
467 //
468 // Credit
469 //
470 string strDescription;
471
472 if (wtx.IsCoinBase())
473 {
474 // Coinbase
475 strDescription = "Generated";
476 if (nCredit == 0)
477 {
478 int64 nUnmatured = 0;
479 foreach(const CTxOut& txout, wtx.vout)
480 nUnmatured += txout.GetCredit();
481 if (wtx.IsInMainChain())
482 strDescription += strprintf(" (%s matures in %d blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
483 else
484 strDescription += " (not accepted)";
485 }
486 }
487 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
488 {
489 // Online transaction
490 if (!mapValue["from"].empty())
491 strDescription += "From: " + mapValue["from"];
492 if (!mapValue["message"].empty())
493 {
494 if (!strDescription.empty())
495 strDescription += " - ";
496 strDescription += mapValue["message"];
497 }
498 }
499 else
500 {
501 // Offline transaction
502 foreach(const CTxOut& txout, wtx.vout)
503 {
504 if (txout.IsMine())
505 {
506 vector<unsigned char> vchPubKey;
507 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
508 {
509 string strAddress = PubKeyToAddress(vchPubKey);
510 if (mapAddressBook.count(strAddress))
511 {
512 strDescription += "Received with: ";
513 if (!mapAddressBook[strAddress].empty())
514 strDescription += mapAddressBook[strAddress] + " ";
515 strDescription += strAddress;
516 }
517 }
518 break;
519 }
520 }
521 }
522
523 string strDescription2;
524 foreach(int c, strDescription)
525 if (c >= ' ')
526 strDescription2 += c;
527
528 InsertLine(fNew, nIndex, hash, strSort,
529 strStatus,
530 nTime ? DateTimeStr(nTime) : "",
531 strDescription2,
532 "",
533 FormatMoney(nNet, true));
534 }
535 else
536 {
537 bool fAllFromMe = true;
538 foreach(const CTxIn& txin, wtx.vin)
539 fAllFromMe = fAllFromMe && txin.IsMine();
540
541 bool fAllToMe = true;
542 foreach(const CTxOut& txout, wtx.vout)
543 fAllToMe = fAllToMe && txout.IsMine();
544
545 if (fAllFromMe && fAllToMe)
546 {
547 // Payment to self
548 int64 nValue = wtx.vout[0].nValue;
549 InsertLine(fNew, nIndex, hash, strSort,
550 strStatus,
551 nTime ? DateTimeStr(nTime) : "",
552 "Payment to yourself",
553 FormatMoney(nNet - nValue, true),
554 FormatMoney(nValue, true));
555 }
556 else if (fAllFromMe)
557 {
558 //
559 // Debit
560 //
561 int64 nTxFee = nDebit - wtx.GetValueOut();
562 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
563 {
564 const CTxOut& txout = wtx.vout[nOut];
565 if (txout.IsMine())
566 continue;
567
568 string strAddress;
569 if (!mapValue["to"].empty())
570 {
571 // Online transaction
572 strAddress = mapValue["to"];
573 }
574 else
575 {
576 // Offline transaction
577 uint160 hash160;
578 if (ExtractHash160(txout.scriptPubKey, hash160))
579 strAddress = Hash160ToAddress(hash160);
580 }
581
582 string strDescription = "To: ";
583 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
584 strDescription += mapAddressBook[strAddress] + " ";
585 strDescription += strAddress;
586 if (!mapValue["message"].empty())
587 {
588 if (!strDescription.empty())
589 strDescription += " - ";
590 strDescription += mapValue["message"];
591 }
592
593 string strDescription2;
594 foreach(int c, strDescription)
595 if (c >= ' ')
596 strDescription2 += c;
597
598 int64 nValue = txout.nValue;
599 if (nOut == 0 && nTxFee > 0)
600 nValue += nTxFee;
601
602 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
603 strStatus,
604 nTime ? DateTimeStr(nTime) : "",
605 strDescription2,
606 FormatMoney(-nValue, true),
607 "");
608 }
609 }
610 else
611 {
612 //
613 // Mixed debit transaction, can't break down payees
614 //
615 bool fAllMine = true;
616 foreach(const CTxOut& txout, wtx.vout)
617 fAllMine = fAllMine && txout.IsMine();
618 foreach(const CTxIn& txin, wtx.vin)
619 fAllMine = fAllMine && txin.IsMine();
620
621 InsertLine(fNew, nIndex, hash, strSort,
622 strStatus,
623 nTime ? DateTimeStr(nTime) : "",
624 "",
625 FormatMoney(nNet, true),
626 "");
627 }
628 }
629}
630
631void CMainFrame::RefreshStatus()
632{
633 static int nLastTop;
634 int nTop = m_listCtrl->GetTopItem();
635 if (nTop == nLastTop && pindexBestLast == pindexBest)
636 return;
637
638 TRY_CRITICAL_BLOCK(cs_mapWallet)
639 {
640 int nStart = nTop;
641 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
642 if (pindexBestLast == pindexBest)
643 {
644 if (nStart >= nLastTop && nStart < nLastTop + 100)
645 nStart = nLastTop + 100;
646 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
647 nEnd = nLastTop;
648 }
649 nLastTop = nTop;
650 pindexBestLast = pindexBest;
651
652 for (int nIndex = nStart; nIndex < nEnd; nIndex++)
653 {
654 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
655 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
656 if (mi == mapWallet.end())
657 {
658 printf("CMainFrame::RefreshStatus() : tx not found in mapWallet\n");
659 continue;
660 }
661 const CWalletTx& wtx = (*mi).second;
662 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
663 InsertTransaction(wtx, false, nIndex);
664 else
665 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
666 }
667 }
668}
669
670void CMainFrame::RefreshListCtrl()
671{
672 fRefreshListCtrl = true;
673 ::wxWakeUpIdle();
674}
675
676void CMainFrame::OnIdle(wxIdleEvent& event)
677{
678 if (fRefreshListCtrl)
679 {
680 // Collect list of wallet transactions and sort newest first
681 bool fEntered = false;
682 vector<pair<unsigned int, uint256> > vSorted;
683 TRY_CRITICAL_BLOCK(cs_mapWallet)
684 {
685 printf("RefreshListCtrl starting\n");
686 fEntered = true;
687 fRefreshListCtrl = false;
688 vWalletUpdated.clear();
689
690 // Do the newest transactions first
691 vSorted.reserve(mapWallet.size());
692 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
693 {
694 const CWalletTx& wtx = (*it).second;
695 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
696 vSorted.push_back(make_pair(nTime, (*it).first));
697 }
698 m_listCtrl->DeleteAllItems();
699 }
700 if (!fEntered)
701 return;
702
703 sort(vSorted.begin(), vSorted.end());
704
705 // Fill list control
706 for (int i = 0; i < vSorted.size();)
707 {
708 if (fShutdown)
709 return;
710 bool fEntered = false;
711 TRY_CRITICAL_BLOCK(cs_mapWallet)
712 {
713 fEntered = true;
714 uint256& hash = vSorted[i++].second;
715 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
716 if (mi != mapWallet.end())
717 InsertTransaction((*mi).second, true);
718 }
719 if (!fEntered || i == 100 || i % 500 == 0)
720 wxYield();
721 }
722
723 printf("RefreshListCtrl done\n");
724 }
725 else
726 {
727 // Check for time updates
728 static int64 nLastTime;
729 if (GetTime() > nLastTime + 30)
730 {
731 TRY_CRITICAL_BLOCK(cs_mapWallet)
732 {
733 nLastTime = GetTime();
734 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
735 {
736 CWalletTx& wtx = (*it).second;
737 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
738 InsertTransaction(wtx, false);
739 }
740 }
741 }
742 }
743}
744
745void CMainFrame::OnPaint(wxPaintEvent& event)
746{
747 event.Skip();
748}
749
750void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
751{
752 // Update listctrl contents
753 if (!vWalletUpdated.empty())
754 {
755 TRY_CRITICAL_BLOCK(cs_mapWallet)
756 {
757 pair<uint256, bool> item;
758 foreach(item, vWalletUpdated)
759 {
760 bool fNew = item.second;
761 map<uint256, CWalletTx>::iterator mi = mapWallet.find(item.first);
762 if (mi != mapWallet.end())
763 {
764 printf("vWalletUpdated: %s %s\n", (*mi).second.GetHash().ToString().substr(0,6).c_str(), fNew ? "new" : "");
765 InsertTransaction((*mi).second, fNew);
766 }
767 }
768 m_listCtrl->ScrollList(0, INT_MAX);
769 vWalletUpdated.clear();
770 }
771 }
772
773 // Update status column of visible items only
774 RefreshStatus();
775
776 // Update status bar
777 string strGen = "";
778 if (fGenerateBitcoins)
779 strGen = " Generating";
780 if (fGenerateBitcoins && vNodes.empty())
781 strGen = "(not connected)";
782 m_statusBar->SetStatusText(strGen, 1);
783
784 string strStatus = strprintf(" %d connections %d blocks %d transactions", vNodes.size(), nBestHeight + 1, m_listCtrl->GetItemCount());
785 m_statusBar->SetStatusText(strStatus, 2);
786
787 // Balance total
788 TRY_CRITICAL_BLOCK(cs_mapWallet)
789 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
790
791 m_listCtrl->OnPaint(event);
792}
793
794void CrossThreadCall(wxCommandEvent& event)
795{
796 if (pframeMain)
797 pframeMain->GetEventHandler()->AddPendingEvent(event);
798}
799
800void CrossThreadCall(int nID, void* pdata)
801{
802 wxCommandEvent event;
803 event.SetInt(nID);
804 event.SetClientData(pdata);
805 if (pframeMain)
806 pframeMain->GetEventHandler()->AddPendingEvent(event);
807}
808
809void CMainFrame::OnCrossThreadCall(wxCommandEvent& event)
810{
811 void* pdata = event.GetClientData();
812 switch (event.GetInt())
813 {
814 case UICALL_ADDORDER:
815 {
816 break;
817 }
818
819 case UICALL_UPDATEORDER:
820 {
821 break;
822 }
823 }
824}
825
826void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
827{
828 Close(true);
829}
830
831void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
832{
833 fGenerateBitcoins = event.IsChecked();
834 nTransactionsUpdated++;
835 CWalletDB().WriteSetting("fGenerateBitcoins", fGenerateBitcoins);
836
837 if (fGenerateBitcoins)
838 if (_beginthread(ThreadBitcoinMiner, 0, NULL) == -1)
839 printf("Error: _beginthread(ThreadBitcoinMiner) failed\n");
840
841 Refresh();
842 Update();
843}
844
845void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
846{
847 COptionsDialog dialog(this);
848 dialog.ShowModal();
849}
850
851void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
852{
853 CAboutDialog dialog(this);
854 dialog.ShowModal();
855}
856
857void CMainFrame::OnButtonSend(wxCommandEvent& event)
858{
859 /// debug test
860 if (fRandSendTest)
861 {
862 RandSend();
863 return;
864 }
865
866 // Toolbar: Send
867 CSendDialog dialog(this);
868 dialog.ShowModal();
869}
870
871void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
872{
873 // Toolbar: Address Book
874 CAddressBookDialog dialogAddr(this, "", false);
875 if (dialogAddr.ShowModal() == 2)
876 {
877 // Send
878 CSendDialog dialogSend(this, dialogAddr.GetAddress());
879 dialogSend.ShowModal();
880 }
881}
882
883void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
884{
885 // Automatically select-all when entering window
886 m_textCtrlAddress->SetSelection(-1, -1);
887 fOnSetFocusAddress = true;
888 event.Skip();
889}
890
891void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
892{
893 if (fOnSetFocusAddress)
894 m_textCtrlAddress->SetSelection(-1, -1);
895 fOnSetFocusAddress = false;
896 event.Skip();
897}
898
899void CMainFrame::OnButtonCopy(wxCommandEvent& event)
900{
901 // Copy address box to clipboard
902 if (wxTheClipboard->Open())
903 {
904 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
905 wxTheClipboard->Close();
906 }
907}
908
909void CMainFrame::OnButtonChange(wxCommandEvent& event)
910{
911 CYourAddressDialog dialog(this, string(m_textCtrlAddress->GetValue()));
912 if (!dialog.ShowModal())
913 return;
914 string strAddress = (string)dialog.GetAddress();
915 if (strAddress != m_textCtrlAddress->GetValue())
916 {
917 uint160 hash160;
918 if (!AddressToHash160(strAddress, hash160))
919 return;
920 if (!mapPubKeys.count(hash160))
921 return;
922 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
923 m_textCtrlAddress->SetValue(strAddress);
924 }
925}
926
927void CMainFrame::OnListItemActivatedAllTransactions(wxListEvent& event)
928{
929 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
930 CWalletTx wtx;
931 CRITICAL_BLOCK(cs_mapWallet)
932 {
933 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
934 if (mi == mapWallet.end())
935 {
936 printf("CMainFrame::OnListItemActivatedAllTransactions() : tx not found in mapWallet\n");
937 return;
938 }
939 wtx = (*mi).second;
940 }
941 CTxDetailsDialog dialog(this, wtx);
942 dialog.ShowModal();
943 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
944 //pdialog->Show();
945}
946
947void CMainFrame::OnListItemActivatedProductsSent(wxListEvent& event)
948{
949 CProduct& product = *(CProduct*)event.GetItem().GetData();
950 CEditProductDialog* pdialog = new CEditProductDialog(this);
951 pdialog->SetProduct(product);
952 pdialog->Show();
953}
954
955void CMainFrame::OnListItemActivatedOrdersSent(wxListEvent& event)
956{
957 CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();
958 CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, false);
959 pdialog->Show();
960}
961
962void CMainFrame::OnListItemActivatedOrdersReceived(wxListEvent& event)
963{
964 CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();
965 CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, true);
966 pdialog->Show();
967}
968
969
970
971
972
973
974
975//////////////////////////////////////////////////////////////////////////////
976//
977// CTxDetailsDialog
978//
979
980CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
981{
982 string strHTML;
983 strHTML.reserve(4000);
984
985 int64 nTime = wtx.GetTxTime();
986 int64 nCredit = wtx.GetCredit();
987 int64 nDebit = wtx.GetDebit();
988 int64 nNet = nCredit - nDebit;
989
990
991
992
993 strHTML += "<b>Status:</b> " + FormatTxStatus(wtx) + "<br>";
994 strHTML += "<b>Date:</b> " + (nTime ? DateTimeStr(nTime) : "") + "<br>";
995
996
997 //
998 // From
999 //
1000 if (wtx.IsCoinBase())
1001 {
1002 strHTML += "<b>Source:</b> Generated<br>";
1003 }
1004 else if (!wtx.mapValue["from"].empty())
1005 {
1006 // Online transaction
1007 if (!wtx.mapValue["from"].empty())
1008 strHTML += "<b>From:</b> " + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1009 }
1010 else
1011 {
1012 // Offline transaction
1013 foreach(const CTxOut& txout, wtx.vout)
1014 {
1015 if (txout.IsMine())
1016 {
1017 vector<unsigned char> vchPubKey;
1018 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1019 {
1020 string strAddress = PubKeyToAddress(vchPubKey);
1021 if (mapAddressBook.count(strAddress))
1022 {
1023 strHTML += "<b>Received with:</b> ";
1024 if (!mapAddressBook[strAddress].empty())
1025 strHTML += mapAddressBook[strAddress] + " ";
1026 strHTML += HtmlEscape(strAddress);
1027 strHTML += "<br>";
1028 }
1029 }
1030 break;
1031 }
1032 }
1033 }
1034
1035
1036 //
1037 // To
1038 //
1039 string strAddress;
1040 if (!wtx.mapValue["to"].empty())
1041 {
1042 // Online transaction
1043 strAddress = wtx.mapValue["to"];
1044 strHTML += "<b>To:</b> ";
1045 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1046 strHTML += mapAddressBook[strAddress] + " ";
1047 strHTML += HtmlEscape(strAddress) + "<br>";
1048 }
1049
1050
1051 //
1052 // Amount
1053 //
1054 if (wtx.IsCoinBase() && nCredit == 0)
1055 {
1056 //
1057 // Coinbase
1058 //
1059 int64 nUnmatured = 0;
1060 foreach(const CTxOut& txout, wtx.vout)
1061 nUnmatured += txout.GetCredit();
1062 if (wtx.IsInMainChain())
1063 strHTML += strprintf("<b>Credit:</b> (%s matures in %d blocks)<br>", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1064 else
1065 strHTML += "<b>Credit:</b> (not accepted)<br>";
1066 }
1067 else if (nNet > 0)
1068 {
1069 //
1070 // Credit
1071 //
1072 strHTML += "<b>Credit:</b> " + FormatMoney(nNet) + "<br>";
1073 }
1074 else
1075 {
1076 bool fAllFromMe = true;
1077 foreach(const CTxIn& txin, wtx.vin)
1078 fAllFromMe = fAllFromMe && txin.IsMine();
1079
1080 bool fAllToMe = true;
1081 foreach(const CTxOut& txout, wtx.vout)
1082 fAllToMe = fAllToMe && txout.IsMine();
1083
1084 if (fAllFromMe)
1085 {
1086 //
1087 // Debit
1088 //
1089 foreach(const CTxOut& txout, wtx.vout)
1090 {
1091 if (txout.IsMine())
1092 continue;
1093
1094 string strAddress;
1095 if (!wtx.mapValue["to"].empty())
1096 {
1097 // Online transaction
1098 strAddress = wtx.mapValue["to"];
1099 }
1100 else
1101 {
1102 // Offline transaction
1103 uint160 hash160;
1104 if (ExtractHash160(txout.scriptPubKey, hash160))
1105 strAddress = Hash160ToAddress(hash160);
1106 }
1107
1108 strHTML += "<b>Debit:</b> " + FormatMoney(-txout.nValue) + " &nbsp;&nbsp; ";
1109 strHTML += "(to ";
1110 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1111 strHTML += mapAddressBook[strAddress] + " ";
1112 strHTML += strAddress;
1113 strHTML += ")<br>";
1114 }
1115
1116 if (fAllToMe)
1117 {
1118 // Payment to self
1119 int64 nValue = wtx.vout[0].nValue;
1120 strHTML += "<b>Debit:</b> " + FormatMoney(-nValue) + "<br>";
1121 strHTML += "<b>Credit:</b> " + FormatMoney(nValue) + "<br>";
1122 }
1123
1124 int64 nTxFee = nDebit - wtx.GetValueOut();
1125 if (nTxFee > 0)
1126 strHTML += "<b>Transaction fee:</b> " + FormatMoney(-nTxFee) + "<br>";
1127 }
1128 else
1129 {
1130 //
1131 // Mixed debit transaction
1132 //
1133 foreach(const CTxIn& txin, wtx.vin)
1134 if (txin.IsMine())
1135 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1136 foreach(const CTxOut& txout, wtx.vout)
1137 if (txout.IsMine())
1138 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1139 }
1140 }
1141
1142 strHTML += "<b>Net amount:</b> " + FormatMoney(nNet, true) + "<br>";
1143
1144
1145 //
1146 // Message
1147 //
1148 if (!wtx.mapValue["message"].empty())
1149 strHTML += "<br><b>Message:</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1150
1151
1152 //
1153 // Debug view
1154 //
1155 if (fDebug)
1156 {
1157 strHTML += "<hr><br>debug print<br><br>";
1158 foreach(const CTxIn& txin, wtx.vin)
1159 if (txin.IsMine())
1160 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1161 foreach(const CTxOut& txout, wtx.vout)
1162 if (txout.IsMine())
1163 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1164
1165 strHTML += "<b>Inputs:</b><br>";
1166 CRITICAL_BLOCK(cs_mapWallet)
1167 {
1168 foreach(const CTxIn& txin, wtx.vin)
1169 {
1170 COutPoint prevout = txin.prevout;
1171 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1172 if (mi != mapWallet.end())
1173 {
1174 const CWalletTx& prev = (*mi).second;
1175 if (prevout.n < prev.vout.size())
1176 {
1177 strHTML += HtmlEscape(prev.ToString(), true);
1178 strHTML += " &nbsp;&nbsp; " + FormatTxStatus(prev) + ", ";
1179 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1180 }
1181 }
1182 }
1183 }
1184
1185 strHTML += "<br><hr><br><b>Transaction:</b><br>";
1186 strHTML += HtmlEscape(wtx.ToString(), true);
1187 }
1188
1189
1190 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1191 m_htmlWin->SetPage(strHTML);
1192 m_buttonOK->SetFocus();
1193}
1194
1195void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1196{
1197 EndModal(true); // wx3: modal dialogs must EndModal, not Close()
1198 //Destroy();
1199}
1200
1201
1202
1203
1204
1205//////////////////////////////////////////////////////////////////////////////
1206//
1207// COptionsDialog
1208//
1209
1210COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1211{
1212 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1213 m_buttonOK->SetFocus();
1214}
1215
1216void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1217{
1218 int64 nTmp = nTransactionFee;
1219 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1220 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1221}
1222
1223void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1224{
1225 // nTransactionFee
1226 int64 nPrevTransactionFee = nTransactionFee;
1227 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1228 CWalletDB().WriteSetting("nTransactionFee", nTransactionFee);
1229
1230 EndModal(true); // wx3: modal dialogs must EndModal, not Close()
1231}
1232
1233void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1234{
1235 EndModal(false); // wx3: modal dialogs must EndModal, not Close()
1236}
1237
1238
1239
1240
1241
1242
1243//////////////////////////////////////////////////////////////////////////////
1244//
1245// CAboutDialog
1246//
1247
1248CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1249{
1250 m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d Alpha", VERSION/100, VERSION%100));
1251
1252 // (wx3 is natively Unicode; the wx2.8 UTF-8 workaround is no longer needed)
1253}
1254
1255void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1256{
1257 EndModal(true); // wx3: modal dialogs must EndModal, not Close()
1258}
1259
1260
1261
1262
1263
1264
1265//////////////////////////////////////////////////////////////////////////////
1266//
1267// CSendDialog
1268//
1269
1270CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1271{
1272 // Init
1273 m_textCtrlAddress->SetValue(strAddress);
1274 m_choiceTransferType->SetSelection(0);
1275 m_bitmapCheckMark->Show(false);
1276 //// todo: should add a display of your balance for convenience
1277
1278 // Set Icon
1279 wxBitmap bmpSend(wxT("send16"), wxBITMAP_TYPE_RESOURCE);
1280 bmpSend.SetMask(new wxMask(wxBitmap(wxT("send16masknoshadow"), wxBITMAP_TYPE_RESOURCE)));
1281 wxIcon iconSend;
1282 iconSend.CopyFromBitmap(bmpSend);
1283 SetIcon(iconSend);
1284
1285 wxCommandEvent event;
1286 OnTextAddress(event);
1287
1288 // Fixup the tab order
1289 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1290 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1291 this->Layout();
1292}
1293
1294void CSendDialog::OnTextAddress(wxCommandEvent& event)
1295{
1296 // Check mark
1297 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
1298 m_bitmapCheckMark->Show(fBitcoinAddress);
1299
1300 // Grey out message if bitcoin address
1301 bool fEnable = !fBitcoinAddress;
1302 m_staticTextFrom->Enable(fEnable);
1303 m_textCtrlFrom->Enable(fEnable);
1304 m_staticTextMessage->Enable(fEnable);
1305 m_textCtrlMessage->Enable(fEnable);
1306 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
1307}
1308
1309void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1310{
1311 // Reformat the amount
1312 if (m_textCtrlAmount->GetValue().Trim().empty())
1313 return;
1314 int64 nTmp;
1315 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1316 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1317}
1318
1319void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1320{
1321 // Open address book
1322 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), true);
1323 if (dialog.ShowModal())
1324 m_textCtrlAddress->SetValue(dialog.GetAddress());
1325}
1326
1327void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1328{
1329 // Copy clipboard to address box
1330 if (wxTheClipboard->Open())
1331 {
1332 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1333 {
1334 wxTextDataObject data;
1335 wxTheClipboard->GetData(data);
1336 m_textCtrlAddress->SetValue(data.GetText());
1337 }
1338 wxTheClipboard->Close();
1339 }
1340}
1341
1342void CSendDialog::OnButtonSend(wxCommandEvent& event)
1343{
1344 CWalletTx wtx;
1345 string strAddress = (string)m_textCtrlAddress->GetValue();
1346
1347 // Parse amount
1348 int64 nValue = 0;
1349 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1350 {
1351 wxMessageBox("Error in amount ");
1352 return;
1353 }
1354 if (nValue > GetBalance())
1355 {
1356 wxMessageBox("Amount exceeds your balance ");
1357 return;
1358 }
1359 if (nValue + nTransactionFee > GetBalance())
1360 {
1361 wxMessageBox(string("Total exceeds your balance when the ") + FormatMoney(nTransactionFee) + " transaction fee is included ");
1362 return;
1363 }
1364
1365 // Parse bitcoin address
1366 uint160 hash160;
1367 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1368
1369 if (fBitcoinAddress)
1370 {
1371 // Send to bitcoin address
1372 CScript scriptPubKey;
1373 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1374
1375 if (!SendMoney(scriptPubKey, nValue, wtx))
1376 return;
1377
1378 wxMessageBox("Payment sent ", "Sending...");
1379 }
1380 else
1381 {
1382 // Parse IP address
1383 CAddress addr(strAddress.c_str());
1384 if (addr.ip == 0)
1385 {
1386 wxMessageBox("Invalid address ");
1387 return;
1388 }
1389
1390 // Message
1391 wtx.mapValue["to"] = strAddress;
1392 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
1393 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
1394
1395 // Send to IP address
1396 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1397 if (!pdialog->ShowModal())
1398 return;
1399 }
1400
1401 if (!mapAddressBook.count(strAddress))
1402 SetAddressBookName(strAddress, "");
1403
1404 EndModal(true);
1405}
1406
1407void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1408{
1409 // Cancel
1410 EndModal(false);
1411}
1412
1413
1414
1415
1416
1417
1418//////////////////////////////////////////////////////////////////////////////
1419//
1420// CSendingDialog
1421//
1422
1423CSendingDialog::CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 nPriceIn, const CWalletTx& wtxIn) : CSendingDialogBase(NULL) // we have to give null so parent can't destroy us
1424{
1425 addr = addrIn;
1426 nPrice = nPriceIn;
1427 wtx = wtxIn;
1428 start = wxDateTime::UNow();
1429 strStatus = "";
1430 fCanCancel = true;
1431 fAbort = false;
1432 fSuccess = false;
1433 fUIDone = false;
1434 fWorkDone = false;
1435
1436 SetTitle(strprintf("Sending %s to %s...", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
1437 m_textCtrlStatus->SetValue("");
1438
1439 _beginthread(SendingDialogStartTransfer, 0, this);
1440}
1441
1442CSendingDialog::~CSendingDialog()
1443{
1444 printf("~CSendingDialog()\n");
1445}
1446
1447void CSendingDialog::Close()
1448{
1449 // Last one out turn out the lights.
1450 // fWorkDone signals that work side is done and UI thread should call destroy.
1451 // fUIDone signals that UI window has closed and work thread should call destroy.
1452 // This allows the window to disappear and end modality when cancelled
1453 // without making the user wait for ConnectNode to return. The dialog object
1454 // hangs around in the background until the work thread exits.
1455 if (IsModal())
1456 EndModal(fSuccess);
1457 else
1458 Show(false);
1459 if (fWorkDone)
1460 Destroy();
1461 else
1462 fUIDone = true;
1463}
1464
1465void CSendingDialog::OnClose(wxCloseEvent& event)
1466{
1467 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
1468 {
1469 Close();
1470 }
1471 else
1472 {
1473 event.Veto();
1474 wxCommandEvent cmdevent;
1475 OnButtonCancel(cmdevent);
1476 }
1477}
1478
1479void CSendingDialog::OnButtonOK(wxCommandEvent& event)
1480{
1481 if (fWorkDone)
1482 Close();
1483}
1484
1485void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
1486{
1487 if (fCanCancel)
1488 fAbort = true;
1489}
1490
1491void CSendingDialog::OnPaint(wxPaintEvent& event)
1492{
1493 if (strStatus.size() > 130)
1494 m_textCtrlStatus->SetValue(string("\n") + strStatus);
1495 else
1496 m_textCtrlStatus->SetValue(string("\n\n") + strStatus);
1497 m_staticTextSending->SetFocus();
1498 if (!fCanCancel)
1499 m_buttonCancel->Enable(false);
1500 if (fWorkDone)
1501 {
1502 m_buttonOK->Enable(true);
1503 m_buttonOK->SetFocus();
1504 m_buttonCancel->Enable(false);
1505 }
1506 if (fAbort && fCanCancel && IsShown())
1507 {
1508 strStatus = "CANCELLED";
1509 m_buttonOK->Enable(true);
1510 m_buttonOK->SetFocus();
1511 m_buttonCancel->Enable(false);
1512 m_buttonCancel->SetLabel("Cancelled");
1513 Close();
1514 wxMessageBox("Transfer cancelled ", "Sending...", wxOK, this);
1515 }
1516 event.Skip();
1517
1518 /// debug test
1519 if (fRandSendTest && fWorkDone && fSuccess)
1520 {
1521 Close();
1522 Sleep(1000);
1523 RandSend();
1524 }
1525}
1526
1527
1528//
1529// Everything from here on is not in the UI thread and must only communicate
1530// with the rest of the dialog through variables and calling repaint.
1531//
1532
1533void CSendingDialog::Repaint()
1534{
1535 // Called from worker threads: repaint only on the GUI thread
1536 if (wxTheApp)
1537 wxTheApp->CallAfter([this] { Refresh(); });
1538}
1539
1540bool CSendingDialog::Status()
1541{
1542 if (fUIDone)
1543 {
1544 Destroy();
1545 return false;
1546 }
1547 if (fAbort && fCanCancel)
1548 {
1549 strStatus = "CANCELLED";
1550 Repaint();
1551 fWorkDone = true;
1552 return false;
1553 }
1554 return true;
1555}
1556
1557bool CSendingDialog::Status(const string& str)
1558{
1559 if (!Status())
1560 return false;
1561 strStatus = str;
1562 Repaint();
1563 return true;
1564}
1565
1566bool CSendingDialog::Error(const string& str)
1567{
1568 fCanCancel = false;
1569 fWorkDone = true;
1570 Status(string("Error: ") + str);
1571 return false;
1572}
1573
1574void SendingDialogStartTransfer(void* parg)
1575{
1576 ((CSendingDialog*)parg)->StartTransfer();
1577}
1578
1579void CSendingDialog::StartTransfer()
1580{
1581 // Make sure we have enough money
1582 if (nPrice + nTransactionFee > GetBalance())
1583 {
1584 Error("You don't have enough money");
1585 return;
1586 }
1587
1588 // We may have connected already for product details
1589 if (!Status("Connecting..."))
1590 return;
1591 CNode* pnode = ConnectNode(addr, 5 * 60);
1592 if (!pnode)
1593 {
1594 Error("Unable to connect");
1595 return;
1596 }
1597
1598 // Send order to seller, with response going to OnReply2 via event handler
1599 if (!Status("Requesting public key..."))
1600 return;
1601 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
1602}
1603
1604void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
1605{
1606 ((CSendingDialog*)parg)->OnReply2(vRecv);
1607}
1608
1609void CSendingDialog::OnReply2(CDataStream& vRecv)
1610{
1611 if (!Status("Received public key..."))
1612 return;
1613
1614 CScript scriptPubKey;
1615 int nRet;
1616 try
1617 {
1618 vRecv >> nRet;
1619 if (nRet > 0)
1620 {
1621 string strMessage;
1622 vRecv >> strMessage;
1623 Error("Transfer was not accepted");
1624 //// todo: enlarge the window and enable a hidden white box to put seller's message
1625 return;
1626 }
1627 vRecv >> scriptPubKey;
1628 }
1629 catch (...)
1630 {
1631 //// what do we want to do about this?
1632 Error("Invalid response received");
1633 return;
1634 }
1635
1636 // Should already be connected
1637 CNode* pnode = ConnectNode(addr, 5 * 60);
1638 if (!pnode)
1639 {
1640 Error("Lost connection");
1641 return;
1642 }
1643
1644 // Pause to give the user a chance to cancel
1645 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
1646 {
1647 Sleep(200);
1648 if (!Status())
1649 return;
1650 }
1651
1652 CRITICAL_BLOCK(cs_main)
1653 {
1654 // Pay
1655 if (!Status("Creating transaction..."))
1656 return;
1657 if (nPrice + nTransactionFee > GetBalance())
1658 {
1659 Error("You don't have enough money");
1660 return;
1661 }
1662 int64 nFeeRequired;
1663 if (!CreateTransaction(scriptPubKey, nPrice, wtx, nFeeRequired))
1664 {
1665 if (nPrice + nFeeRequired > GetBalance())
1666 Error(strprintf("This is an oversized transaction that requires a transaction fee of %s", FormatMoney(nFeeRequired).c_str()));
1667 else
1668 Error("Transaction creation failed");
1669 return;
1670 }
1671
1672 // Last chance to cancel
1673 Sleep(50);
1674 if (!Status())
1675 return;
1676 fCanCancel = false;
1677 if (fAbort)
1678 {
1679 fCanCancel = true;
1680 if (!Status())
1681 return;
1682 fCanCancel = false;
1683 }
1684 if (!Status("Sending payment..."))
1685 return;
1686
1687 // Commit
1688 if (!CommitTransactionSpent(wtx))
1689 {
1690 Error("Error finalizing payment");
1691 return;
1692 }
1693
1694 // Send payment tx to seller, with response going to OnReply3 via event handler
1695 pnode->PushRequest("submitorder", wtx, SendingDialogOnReply3, this);
1696
1697 // Accept and broadcast transaction
1698 if (!wtx.AcceptTransaction())
1699 printf("ERROR: CSendingDialog : wtxNew.AcceptTransaction() %s failed\n", wtx.GetHash().ToString().c_str());
1700 wtx.RelayWalletTransaction();
1701
1702 Status("Waiting for confirmation...");
1703 MainFrameRepaint();
1704 }
1705}
1706
1707void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
1708{
1709 ((CSendingDialog*)parg)->OnReply3(vRecv);
1710}
1711
1712void CSendingDialog::OnReply3(CDataStream& vRecv)
1713{
1714 int nRet;
1715 try
1716 {
1717 vRecv >> nRet;
1718 if (nRet > 0)
1719 {
1720 Error("The payment was sent, but the recipient was unable to verify it.\n"
1721 "The transaction is recorded and will credit to the recipient if it is valid,\n"
1722 "but without comment information.");
1723 return;
1724 }
1725 }
1726 catch (...)
1727 {
1728 //// what do we want to do about this?
1729 Error("Payment was sent, but an invalid response was received");
1730 return;
1731 }
1732
1733 fSuccess = true;
1734 fWorkDone = true;
1735 Status("Payment completed");
1736}
1737
1738
1739
1740
1741
1742
1743//////////////////////////////////////////////////////////////////////////////
1744//
1745// CYourAddressDialog
1746//
1747
1748CYourAddressDialog::CYourAddressDialog(wxWindow* parent, const string& strInitSelected) : CYourAddressDialogBase(parent)
1749{
1750 // Init column headers
1751 m_listCtrl->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 200);
1752 m_listCtrl->InsertColumn(1, "Bitflash Address", wxLIST_FORMAT_LEFT, 350);
1753 m_listCtrl->SetFocus();
1754
1755 // Fill listctrl with address book data
1756 CRITICAL_BLOCK(cs_mapKeys)
1757 {
1758 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
1759 {
1760 string strAddress = item.first;
1761 string strName = item.second;
1762 uint160 hash160;
1763 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
1764 if (fMine)
1765 {
1766 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
1767 if (strAddress == strInitSelected)
1768 m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
1769 }
1770 }
1771 }
1772}
1773
1774wxString CYourAddressDialog::GetAddress()
1775{
1776 int nIndex = GetSelection(m_listCtrl);
1777 if (nIndex == -1)
1778 return "";
1779 return GetItemText(m_listCtrl, nIndex, 1);
1780}
1781
1782void CYourAddressDialog::OnListEndLabelEdit(wxListEvent& event)
1783{
1784 // Update address book with edited name
1785 if (event.IsEditCancelled())
1786 return;
1787 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
1788 SetAddressBookName(strAddress, string(event.GetText()));
1789 pframeMain->RefreshListCtrl();
1790}
1791
1792void CYourAddressDialog::OnListItemSelected(wxListEvent& event)
1793{
1794}
1795
1796void CYourAddressDialog::OnListItemActivated(wxListEvent& event)
1797{
1798 // Doubleclick returns selection
1799 EndModal(true);
1800}
1801
1802void CYourAddressDialog::OnButtonRename(wxCommandEvent& event)
1803{
1804 // Ask new name
1805 int nIndex = GetSelection(m_listCtrl);
1806 if (nIndex == -1)
1807 return;
1808 string strName = (string)m_listCtrl->GetItemText(nIndex);
1809 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
1810 CGetTextFromUserDialog dialog(this, "Rename Bitflash Address", "New Name", strName);
1811 if (!dialog.ShowModal())
1812 return;
1813 strName = dialog.GetValue();
1814
1815 // Change name
1816 SetAddressBookName(strAddress, strName);
1817 m_listCtrl->SetItemText(nIndex, strName);
1818 pframeMain->RefreshListCtrl();
1819}
1820
1821void CYourAddressDialog::OnButtonNew(wxCommandEvent& event)
1822{
1823 // Ask name
1824 CGetTextFromUserDialog dialog(this, "New Bitflash Address", "Name", "");
1825 if (!dialog.ShowModal())
1826 return;
1827 string strName = dialog.GetValue();
1828
1829 // Generate new key
1830 string strAddress = PubKeyToAddress(GenerateNewKey());
1831 SetAddressBookName(strAddress, strName);
1832
1833 // Add to list and select it
1834 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
1835 SetSelection(m_listCtrl, nIndex);
1836 m_listCtrl->SetFocus();
1837}
1838
1839void CYourAddressDialog::OnButtonCopy(wxCommandEvent& event)
1840{
1841 // Copy address box to clipboard
1842 if (wxTheClipboard->Open())
1843 {
1844 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
1845 wxTheClipboard->Close();
1846 }
1847}
1848
1849void CYourAddressDialog::OnButtonOK(wxCommandEvent& event)
1850{
1851 // OK
1852 EndModal(true);
1853}
1854
1855void CYourAddressDialog::OnButtonCancel(wxCommandEvent& event)
1856{
1857 // Cancel
1858 EndModal(false);
1859}
1860
1861void CYourAddressDialog::OnClose(wxCloseEvent& event)
1862{
1863 // Close
1864 EndModal(false);
1865}
1866
1867
1868
1869
1870
1871
1872//////////////////////////////////////////////////////////////////////////////
1873//
1874// CAddressBookDialog
1875//
1876
1877CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, bool fSendingIn) : CAddressBookDialogBase(parent)
1878{
1879 fSending = fSendingIn;
1880 if (!fSending)
1881 m_buttonCancel->Show(false);
1882
1883 // Init column headers
1884 m_listCtrl->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 200);
1885 m_listCtrl->InsertColumn(1, "Address", wxLIST_FORMAT_LEFT, 350);
1886 m_listCtrl->SetFocus();
1887
1888 // Set Icon
1889 wxBitmap bmpAddressBook(wxT("addressbook16"), wxBITMAP_TYPE_RESOURCE);
1890 bmpAddressBook.SetMask(new wxMask(wxBitmap(wxT("addressbook16mask"), wxBITMAP_TYPE_RESOURCE)));
1891 wxIcon iconAddressBook;
1892 iconAddressBook.CopyFromBitmap(bmpAddressBook);
1893 SetIcon(iconAddressBook);
1894
1895 // Fill listctrl with address book data
1896 CRITICAL_BLOCK(cs_mapKeys)
1897 {
1898 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
1899 {
1900 string strAddress = item.first;
1901 string strName = item.second;
1902 uint160 hash160;
1903 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
1904 if (!fMine)
1905 {
1906 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
1907 if (strAddress == strInitSelected)
1908 m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
1909 }
1910 }
1911 }
1912}
1913
1914wxString CAddressBookDialog::GetAddress()
1915{
1916 int nIndex = GetSelection(m_listCtrl);
1917 if (nIndex == -1)
1918 return "";
1919 return GetItemText(m_listCtrl, nIndex, 1);
1920}
1921
1922void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
1923{
1924 // Update address book with edited name
1925 if (event.IsEditCancelled())
1926 return;
1927 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
1928 SetAddressBookName(strAddress, string(event.GetText()));
1929 pframeMain->RefreshListCtrl();
1930}
1931
1932void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
1933{
1934}
1935
1936void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
1937{
1938 if (fSending)
1939 {
1940 // Doubleclick returns selection
1941 EndModal(GetAddress() != "" ? 2 : 0);
1942 }
1943 else
1944 {
1945 // Doubleclick edits item
1946 wxCommandEvent event;
1947 OnButtonEdit(event);
1948 }
1949}
1950
1951void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
1952{
1953 // Ask new name
1954 int nIndex = GetSelection(m_listCtrl);
1955 if (nIndex == -1)
1956 return;
1957 string strName = (string)m_listCtrl->GetItemText(nIndex);
1958 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
1959 string strAddressOrg = strAddress;
1960 CGetTextFromUserDialog dialog(this, "Edit Address", "Name", strName, "Address", strAddress);
1961 if (!dialog.ShowModal())
1962 return;
1963 strName = dialog.GetValue1();
1964 strAddress = dialog.GetValue2();
1965
1966 // Change name
1967 if (strAddress != strAddressOrg)
1968 CWalletDB().EraseName(strAddressOrg);
1969 SetAddressBookName(strAddress, strName);
1970 m_listCtrl->SetItem(nIndex, 1, strAddress);
1971 m_listCtrl->SetItemText(nIndex, strName);
1972 pframeMain->RefreshListCtrl();
1973}
1974
1975void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
1976{
1977 // Ask name
1978 CGetTextFromUserDialog dialog(this, "New Address", "Name", "", "Address", "");
1979 if (!dialog.ShowModal())
1980 return;
1981 string strName = dialog.GetValue1();
1982 string strAddress = dialog.GetValue2();
1983
1984 // Add to list and select it
1985 SetAddressBookName(strAddress, strName);
1986 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
1987 SetSelection(m_listCtrl, nIndex);
1988 m_listCtrl->SetFocus();
1989}
1990
1991void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
1992{
1993 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
1994 {
1995 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
1996 {
1997 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
1998 CWalletDB().EraseName(strAddress);
1999 m_listCtrl->DeleteItem(nIndex);
2000 }
2001 }
2002 pframeMain->RefreshListCtrl();
2003}
2004
2005void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2006{
2007 // Copy address box to clipboard
2008 if (wxTheClipboard->Open())
2009 {
2010 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
2011 wxTheClipboard->Close();
2012 }
2013}
2014
2015void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2016{
2017 // OK
2018 EndModal(GetAddress() != "" ? 1 : 0);
2019}
2020
2021void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2022{
2023 // Cancel
2024 EndModal(0);
2025}
2026
2027void CAddressBookDialog::OnClose(wxCloseEvent& event)
2028{
2029 // Close
2030 EndModal(0);
2031}
2032
2033
2034
2035
2036
2037
2038//////////////////////////////////////////////////////////////////////////////
2039//
2040// CProductsDialog
2041//
2042
2043bool CompareIntStringPairBestFirst(const pair<int, string>& item1, const pair<int, string>& item2)
2044{
2045 return (item1.first > item2.first);
2046}
2047
2048CProductsDialog::CProductsDialog(wxWindow* parent) : CProductsDialogBase(parent)
2049{
2050 // Init column headers
2051 m_listCtrl->InsertColumn(0, "Title", wxLIST_FORMAT_LEFT, 200);
2052 m_listCtrl->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 80);
2053 m_listCtrl->InsertColumn(2, "Seller", wxLIST_FORMAT_LEFT, 80);
2054 m_listCtrl->InsertColumn(3, "Stars", wxLIST_FORMAT_LEFT, 50);
2055 m_listCtrl->InsertColumn(4, "Power", wxLIST_FORMAT_LEFT, 50);
2056
2057 // Tally top categories
2058 map<string, int> mapTopCategories;
2059 CRITICAL_BLOCK(cs_mapProducts)
2060 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)
2061 mapTopCategories[(*mi).second.mapValue["category"]]++;
2062
2063 // Sort top categories
2064 vector<pair<int, string> > vTopCategories;
2065 for (map<string, int>::iterator mi = mapTopCategories.begin(); mi != mapTopCategories.end(); ++mi)
2066 vTopCategories.push_back(make_pair((*mi).second, (*mi).first));
2067 sort(vTopCategories.begin(), vTopCategories.end(), CompareIntStringPairBestFirst);
2068
2069 // Fill categories combo box
2070 int nLimit = 250;
2071 for (vector<pair<int, string> >::iterator it = vTopCategories.begin(); it != vTopCategories.end() && nLimit-- > 0; ++it)
2072 m_comboBoxCategory->Append((*it).second);
2073
2074 // Fill window with initial search
2075 //wxCommandEvent event;
2076 //OnButtonSearch(event);
2077}
2078
2079void CProductsDialog::OnCombobox(wxCommandEvent& event)
2080{
2081 OnButtonSearch(event);
2082}
2083
2084bool CompareProductsBestFirst(const CProduct* p1, const CProduct* p2)
2085{
2086 return (p1->nAtoms > p2->nAtoms);
2087}
2088
2089void CProductsDialog::OnButtonSearch(wxCommandEvent& event)
2090{
2091 string strCategory = (string)m_comboBoxCategory->GetValue();
2092 string strSearch = (string)m_textCtrlSearch->GetValue();
2093
2094 // Search products
2095 vector<CProduct*> vProductsFound;
2096 CRITICAL_BLOCK(cs_mapProducts)
2097 {
2098 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)
2099 {
2100 CProduct& product = (*mi).second;
2101 if (product.mapValue["category"].find(strCategory) != -1)
2102 {
2103 if (product.mapValue["title"].find(strSearch) != -1 ||
2104 product.mapValue["description"].find(strSearch) != -1 ||
2105 product.mapValue["seller"].find(strSearch) != -1)
2106 {
2107 vProductsFound.push_back(&product);
2108 }
2109 }
2110 }
2111 }
2112
2113 // Sort
2114 sort(vProductsFound.begin(), vProductsFound.end(), CompareProductsBestFirst);
2115
2116 // Display
2117 foreach(CProduct* pproduct, vProductsFound)
2118 {
2119 InsertLine(m_listCtrl,
2120 pproduct->mapValue["title"],
2121 pproduct->mapValue["price"],
2122 pproduct->mapValue["seller"],
2123 pproduct->mapValue["stars"],
2124 itostr(pproduct->nAtoms));
2125 }
2126}
2127
2128void CProductsDialog::OnListItemActivated(wxListEvent& event)
2129{
2130 // Doubleclick opens product
2131 CViewProductDialog* pdialog = new CViewProductDialog(this, m_vProduct[event.GetIndex()]);
2132 pdialog->Show();
2133}
2134
2135
2136
2137
2138
2139
2140
2141//////////////////////////////////////////////////////////////////////////////
2142//
2143// CEditProductDialog
2144//
2145
2146CEditProductDialog::CEditProductDialog(wxWindow* parent) : CEditProductDialogBase(parent)
2147{
2148 m_textCtrlLabel[0 ] = m_textCtrlLabel0;
2149 m_textCtrlLabel[1 ] = m_textCtrlLabel1;
2150 m_textCtrlLabel[2 ] = m_textCtrlLabel2;
2151 m_textCtrlLabel[3 ] = m_textCtrlLabel3;
2152 m_textCtrlLabel[4 ] = m_textCtrlLabel4;
2153 m_textCtrlLabel[5 ] = m_textCtrlLabel5;
2154 m_textCtrlLabel[6 ] = m_textCtrlLabel6;
2155 m_textCtrlLabel[7 ] = m_textCtrlLabel7;
2156 m_textCtrlLabel[8 ] = m_textCtrlLabel8;
2157 m_textCtrlLabel[9 ] = m_textCtrlLabel9;
2158 m_textCtrlLabel[10] = m_textCtrlLabel10;
2159 m_textCtrlLabel[11] = m_textCtrlLabel11;
2160 m_textCtrlLabel[12] = m_textCtrlLabel12;
2161 m_textCtrlLabel[13] = m_textCtrlLabel13;
2162 m_textCtrlLabel[14] = m_textCtrlLabel14;
2163 m_textCtrlLabel[15] = m_textCtrlLabel15;
2164 m_textCtrlLabel[16] = m_textCtrlLabel16;
2165 m_textCtrlLabel[17] = m_textCtrlLabel17;
2166 m_textCtrlLabel[18] = m_textCtrlLabel18;
2167 m_textCtrlLabel[19] = m_textCtrlLabel19;
2168
2169 m_textCtrlField[0 ] = m_textCtrlField0;
2170 m_textCtrlField[1 ] = m_textCtrlField1;
2171 m_textCtrlField[2 ] = m_textCtrlField2;
2172 m_textCtrlField[3 ] = m_textCtrlField3;
2173 m_textCtrlField[4 ] = m_textCtrlField4;
2174 m_textCtrlField[5 ] = m_textCtrlField5;
2175 m_textCtrlField[6 ] = m_textCtrlField6;
2176 m_textCtrlField[7 ] = m_textCtrlField7;
2177 m_textCtrlField[8 ] = m_textCtrlField8;
2178 m_textCtrlField[9 ] = m_textCtrlField9;
2179 m_textCtrlField[10] = m_textCtrlField10;
2180 m_textCtrlField[11] = m_textCtrlField11;
2181 m_textCtrlField[12] = m_textCtrlField12;
2182 m_textCtrlField[13] = m_textCtrlField13;
2183 m_textCtrlField[14] = m_textCtrlField14;
2184 m_textCtrlField[15] = m_textCtrlField15;
2185 m_textCtrlField[16] = m_textCtrlField16;
2186 m_textCtrlField[17] = m_textCtrlField17;
2187 m_textCtrlField[18] = m_textCtrlField18;
2188 m_textCtrlField[19] = m_textCtrlField19;
2189
2190 m_buttonDel[0 ] = m_buttonDel0;
2191 m_buttonDel[1 ] = m_buttonDel1;
2192 m_buttonDel[2 ] = m_buttonDel2;
2193 m_buttonDel[3 ] = m_buttonDel3;
2194 m_buttonDel[4 ] = m_buttonDel4;
2195 m_buttonDel[5 ] = m_buttonDel5;
2196 m_buttonDel[6 ] = m_buttonDel6;
2197 m_buttonDel[7 ] = m_buttonDel7;
2198 m_buttonDel[8 ] = m_buttonDel8;
2199 m_buttonDel[9 ] = m_buttonDel9;
2200 m_buttonDel[10] = m_buttonDel10;
2201 m_buttonDel[11] = m_buttonDel11;
2202 m_buttonDel[12] = m_buttonDel12;
2203 m_buttonDel[13] = m_buttonDel13;
2204 m_buttonDel[14] = m_buttonDel14;
2205 m_buttonDel[15] = m_buttonDel15;
2206 m_buttonDel[16] = m_buttonDel16;
2207 m_buttonDel[17] = m_buttonDel17;
2208 m_buttonDel[18] = m_buttonDel18;
2209 m_buttonDel[19] = m_buttonDel19;
2210
2211 for (int i = 1; i < FIELDS_MAX; i++)
2212 ShowLine(i, false);
2213
2214 LayoutAll();
2215}
2216
2217void CEditProductDialog::LayoutAll()
2218{
2219 m_scrolledWindow->Layout();
2220 m_scrolledWindow->GetSizer()->Fit(m_scrolledWindow);
2221 this->Layout();
2222}
2223
2224void CEditProductDialog::ShowLine(int i, bool fShow)
2225{
2226 m_textCtrlLabel[i]->Show(fShow);
2227 m_textCtrlField[i]->Show(fShow);
2228 m_buttonDel[i]->Show(fShow);
2229}
2230
2231void CEditProductDialog::OnButtonDel0(wxCommandEvent& event) { OnButtonDel(event, 0); }
2232void CEditProductDialog::OnButtonDel1(wxCommandEvent& event) { OnButtonDel(event, 1); }
2233void CEditProductDialog::OnButtonDel2(wxCommandEvent& event) { OnButtonDel(event, 2); }
2234void CEditProductDialog::OnButtonDel3(wxCommandEvent& event) { OnButtonDel(event, 3); }
2235void CEditProductDialog::OnButtonDel4(wxCommandEvent& event) { OnButtonDel(event, 4); }
2236void CEditProductDialog::OnButtonDel5(wxCommandEvent& event) { OnButtonDel(event, 5); }
2237void CEditProductDialog::OnButtonDel6(wxCommandEvent& event) { OnButtonDel(event, 6); }
2238void CEditProductDialog::OnButtonDel7(wxCommandEvent& event) { OnButtonDel(event, 7); }
2239void CEditProductDialog::OnButtonDel8(wxCommandEvent& event) { OnButtonDel(event, 8); }
2240void CEditProductDialog::OnButtonDel9(wxCommandEvent& event) { OnButtonDel(event, 9); }
2241void CEditProductDialog::OnButtonDel10(wxCommandEvent& event) { OnButtonDel(event, 10); }
2242void CEditProductDialog::OnButtonDel11(wxCommandEvent& event) { OnButtonDel(event, 11); }
2243void CEditProductDialog::OnButtonDel12(wxCommandEvent& event) { OnButtonDel(event, 12); }
2244void CEditProductDialog::OnButtonDel13(wxCommandEvent& event) { OnButtonDel(event, 13); }
2245void CEditProductDialog::OnButtonDel14(wxCommandEvent& event) { OnButtonDel(event, 14); }
2246void CEditProductDialog::OnButtonDel15(wxCommandEvent& event) { OnButtonDel(event, 15); }
2247void CEditProductDialog::OnButtonDel16(wxCommandEvent& event) { OnButtonDel(event, 16); }
2248void CEditProductDialog::OnButtonDel17(wxCommandEvent& event) { OnButtonDel(event, 17); }
2249void CEditProductDialog::OnButtonDel18(wxCommandEvent& event) { OnButtonDel(event, 18); }
2250void CEditProductDialog::OnButtonDel19(wxCommandEvent& event) { OnButtonDel(event, 19); }
2251
2252void CEditProductDialog::OnButtonDel(wxCommandEvent& event, int n)
2253{
2254 Freeze();
2255 int x, y;
2256 m_scrolledWindow->GetViewStart(&x, &y);
2257 int i;
2258 for (i = n; i < FIELDS_MAX-1; i++)
2259 {
2260 m_textCtrlLabel[i]->SetValue(m_textCtrlLabel[i+1]->GetValue());
2261 m_textCtrlField[i]->SetValue(m_textCtrlField[i+1]->GetValue());
2262 if (!m_buttonDel[i+1]->IsShown())
2263 break;
2264 }
2265 m_textCtrlLabel[i]->SetValue("");
2266 m_textCtrlField[i]->SetValue("");
2267 ShowLine(i, false);
2268 m_buttonAddField->Enable(true);
2269 LayoutAll();
2270 m_scrolledWindow->Scroll(0, y);
2271 Thaw();
2272}
2273
2274void CEditProductDialog::OnButtonAddField(wxCommandEvent& event)
2275{
2276 for (int i = 0; i < FIELDS_MAX; i++)
2277 {
2278 if (!m_buttonDel[i]->IsShown())
2279 {
2280 Freeze();
2281 ShowLine(i, true);
2282 if (i == FIELDS_MAX-1)
2283 m_buttonAddField->Enable(false);
2284 LayoutAll();
2285 m_scrolledWindow->Scroll(0, 99999);
2286 Thaw();
2287 break;
2288 }
2289 }
2290}
2291
2292void CEditProductDialog::OnButtonSend(wxCommandEvent& event)
2293{
2294 CProduct product;
2295 GetProduct(product);
2296
2297 // Sign the detailed product
2298 product.vchPubKeyFrom = keyUser.GetPubKey();
2299 if (!keyUser.Sign(product.GetSigHash(), product.vchSig))
2300 {
2301 wxMessageBox("Error digitally signing the product ");
2302 return;
2303 }
2304
2305 // Save detailed product
2306 AddToMyProducts(product);
2307
2308 // Strip down to summary product
2309 product.mapDetails.clear();
2310 product.vOrderForm.clear();
2311
2312 // Sign the summary product
2313 if (!keyUser.Sign(product.GetSigHash(), product.vchSig))
2314 {
2315 wxMessageBox("Error digitally signing the product ");
2316 return;
2317 }
2318
2319 // Verify
2320 if (!product.CheckProduct())
2321 {
2322 wxMessageBox("Errors found in product ");
2323 return;
2324 }
2325
2326 // Broadcast
2327 AdvertStartPublish(pnodeLocalHost, MSG_PRODUCT, 0, product);
2328
2329 Destroy();
2330}
2331
2332void CEditProductDialog::OnButtonPreview(wxCommandEvent& event)
2333{
2334 CProduct product;
2335 GetProduct(product);
2336 CViewProductDialog* pdialog = new CViewProductDialog(this, product);
2337 pdialog->Show();
2338}
2339
2340void CEditProductDialog::OnButtonCancel(wxCommandEvent& event)
2341{
2342 Destroy();
2343}
2344
2345void CEditProductDialog::SetProduct(const CProduct& productIn)
2346{
2347 CProduct product = productIn;
2348
2349 m_comboBoxCategory->SetValue(product.mapValue["category"]);
2350 m_textCtrlTitle->SetValue(product.mapValue["title"]);
2351 m_textCtrlPrice->SetValue(product.mapValue["price"]);
2352 m_textCtrlDescription->SetValue(product.mapValue["description"]);
2353 m_textCtrlInstructions->SetValue(product.mapValue["instructions"]);
2354
2355 for (int i = 0; i < FIELDS_MAX; i++)
2356 {
2357 bool fUsed = i < product.vOrderForm.size();
2358 m_buttonDel[i]->Show(fUsed);
2359 m_textCtrlLabel[i]->Show(fUsed);
2360 m_textCtrlField[i]->Show(fUsed);
2361 if (!fUsed)
2362 continue;
2363
2364 m_textCtrlLabel[i]->SetValue(product.vOrderForm[i].first);
2365 string strControl = product.vOrderForm[i].second;
2366 if (strControl.substr(0, 5) == "text=")
2367 m_textCtrlField[i]->SetValue("");
2368 else if (strControl.substr(0, 7) == "choice=")
2369 m_textCtrlField[i]->SetValue(strControl.substr(7));
2370 else
2371 m_textCtrlField[i]->SetValue(strControl);
2372 }
2373}
2374
2375void CEditProductDialog::GetProduct(CProduct& product)
2376{
2377 // map<string, string> mapValue;
2378 // vector<pair<string, string> > vOrderForm;
2379
2380 product.mapValue["category"] = m_comboBoxCategory->GetValue().Trim();
2381 product.mapValue["title"] = m_textCtrlTitle->GetValue().Trim();
2382 product.mapValue["price"] = m_textCtrlPrice->GetValue().Trim();
2383 product.mapValue["description"] = m_textCtrlDescription->GetValue().Trim();
2384 product.mapValue["instructions"] = m_textCtrlInstructions->GetValue().Trim();
2385
2386 for (int i = 0; i < FIELDS_MAX; i++)
2387 {
2388 if (m_buttonDel[i]->IsShown())
2389 {
2390 string strLabel = (string)m_textCtrlLabel[i]->GetValue().Trim();
2391 string strControl = (string)m_textCtrlField[i]->GetValue();
2392 if (strControl.empty())
2393 strControl = "text=";
2394 else
2395 strControl = "choice=" + strControl;
2396 product.vOrderForm.push_back(make_pair(strLabel, strControl));
2397 }
2398 }
2399}
2400
2401
2402
2403
2404
2405
2406
2407//////////////////////////////////////////////////////////////////////////////
2408//
2409// CViewProductDialog
2410//
2411
2412CViewProductDialog::CViewProductDialog(wxWindow* parent, const CProduct& productIn) : CViewProductDialogBase(parent)
2413{
2414 Connect(wxEVT_REPLY1, wxCommandEventHandler(CViewProductDialog::OnReply1), NULL, this);
2415 AddCallbackAvailable(GetEventHandler());
2416
2417 // Fill display with product summary while waiting for details
2418 product = productIn;
2419 UpdateProductDisplay(false);
2420
2421 m_buttonBack->Enable(false);
2422 m_buttonNext->Enable(!product.vOrderForm.empty());
2423 m_htmlWinReviews->Show(true);
2424 m_scrolledWindow->Show(false);
2425 this->Layout();
2426
2427 // Request details from seller
2428 _beginthread(ThreadRequestProductDetails, 0, new pair<CProduct, wxEvtHandler*>(product, GetEventHandler()));
2429}
2430
2431CViewProductDialog::~CViewProductDialog()
2432{
2433 RemoveCallbackAvailable(GetEventHandler());
2434}
2435
2436void ThreadRequestProductDetails(void* parg)
2437{
2438 // Extract parameters
2439 pair<CProduct, wxEvtHandler*>* pitem = (pair<CProduct, wxEvtHandler*>*)parg;
2440 CProduct product = pitem->first;
2441 wxEvtHandler* pevthandler = pitem->second;
2442 delete pitem;
2443
2444 // Connect to seller
2445 CNode* pnode = ConnectNode(product.addr, 5 * 60);
2446 if (!pnode)
2447 {
2448 CDataStream ssEmpty;
2449 AddPendingReplyEvent1(pevthandler, ssEmpty);
2450 return;
2451 }
2452
2453 // Request detailed product, with response going to OnReply1 via dialog's event handler
2454 pnode->PushRequest("getdetails", product.GetHash(), AddPendingReplyEvent1, (void*)pevthandler);
2455}
2456
2457void CViewProductDialog::OnReply1(wxCommandEvent& event)
2458{
2459 CDataStream ss = GetStreamFromEvent(event);
2460 if (ss.empty())
2461 {
2462 product.mapValue["description"] = "-- CAN'T CONNECT TO SELLER --\n";
2463 UpdateProductDisplay(true);
2464 return;
2465 }
2466
2467 int nRet;
2468 CProduct product2;
2469 try
2470 {
2471 ss >> nRet;
2472 if (nRet > 0)
2473 throw false;
2474 ss >> product2;
2475 if (product2.GetHash() != product.GetHash())
2476 throw false;
2477 if (!product2.CheckSignature())
2478 throw false;
2479 }
2480 catch (...)
2481 {
2482 product.mapValue["description"] = "-- INVALID RESPONSE --\n";
2483 UpdateProductDisplay(true);
2484 return;
2485 }
2486
2487 product = product2;
2488 UpdateProductDisplay(true);
2489}
2490
2491bool CompareReviewsBestFirst(const CReview* p1, const CReview* p2)
2492{
2493 return (p1->nAtoms > p2->nAtoms);
2494}
2495
2496void CViewProductDialog::UpdateProductDisplay(bool fDetails)
2497{
2498 // Product and reviews
2499 string strHTML;
2500 strHTML.reserve(4000);
2501 strHTML += "<html>\n"
2502 "<head>\n"
2503 "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"
2504 "</head>\n"
2505 "<body>\n";
2506 strHTML += "<b>Category:</b> " + HtmlEscape(product.mapValue["category"]) + "<br>\n";
2507 strHTML += "<b>Title:</b> " + HtmlEscape(product.mapValue["title"]) + "<br>\n";
2508 strHTML += "<b>Price:</b> " + HtmlEscape(product.mapValue["price"]) + "<br>\n";
2509
2510 if (!fDetails)
2511 strHTML += "<b>Loading details...</b><br>\n<br>\n";
2512 else
2513 strHTML += HtmlEscape(product.mapValue["description"], true) + "<br>\n<br>\n";
2514
2515 strHTML += "<b>Reviews:</b><br>\n<br>\n";
2516
2517 if (!product.vchPubKeyFrom.empty())
2518 {
2519 CReviewDB reviewdb("r");
2520
2521 // Get reviews
2522 vector<CReview> vReviews;
2523 reviewdb.ReadReviews(product.GetUserHash(), vReviews);
2524
2525 // Get reviewer's number of atoms
2526 vector<CReview*> vSortedReviews;
2527 vSortedReviews.reserve(vReviews.size());
2528 for (vector<CReview>::reverse_iterator it = vReviews.rbegin(); it != vReviews.rend(); ++it)
2529 {
2530 CReview& review = *it;
2531 CUser user;
2532 reviewdb.ReadUser(review.GetUserHash(), user);
2533 review.nAtoms = user.GetAtomCount();
2534 vSortedReviews.push_back(&review);
2535 }
2536
2537 reviewdb.Close();
2538
2539 // Sort
2540 stable_sort(vSortedReviews.begin(), vSortedReviews.end(), CompareReviewsBestFirst);
2541
2542 // Format reviews
2543 foreach(CReview* preview, vSortedReviews)
2544 {
2545 CReview& review = *preview;
2546 int nStars = atoi(review.mapValue["stars"].c_str());
2547 if (nStars < 1 || nStars > 5)
2548 continue;
2549
2550 strHTML += "<b>" + itostr(nStars) + (nStars == 1 ? " star" : " stars") + "</b>";
2551 strHTML += " &nbsp;&nbsp;&nbsp; ";
2552 strHTML += DateStr(atoi64(review.mapValue["date"])) + "<br>\n";
2553 strHTML += HtmlEscape(review.mapValue["review"], true);
2554 strHTML += "<br>\n<br>\n";
2555 }
2556 }
2557
2558 strHTML += "</body>\n</html>\n";
2559
2560 // Shrink capacity to fit
2561 string(strHTML.begin(), strHTML.end()).swap(strHTML);
2562
2563 m_htmlWinReviews->SetPage(strHTML);
2564
2565 ///// need to find some other indicator to use so can allow empty order form
2566 if (product.vOrderForm.empty())
2567 return;
2568
2569 // Order form
2570 m_staticTextInstructions->SetLabel(product.mapValue["instructions"]);
2571 for (int i = 0; i < FIELDS_MAX; i++)
2572 {
2573 m_staticTextLabel[i] = NULL;
2574 m_textCtrlField[i] = NULL;
2575 m_choiceField[i] = NULL;
2576 }
2577
2578 // Construct flexgridsizer
2579 wxBoxSizer* bSizer21 = (wxBoxSizer*)m_scrolledWindow->GetSizer();
2580 wxFlexGridSizer* fgSizer;
2581 fgSizer = new wxFlexGridSizer(0, 2, 0, 0);
2582 fgSizer->AddGrowableCol(1);
2583 fgSizer->SetFlexibleDirection(wxBOTH);
2584 fgSizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
2585
2586 // Construct order form fields
2587 wxWindow* windowLast = NULL;
2588 for (int i = 0; i < product.vOrderForm.size(); i++)
2589 {
2590 string strLabel = product.vOrderForm[i].first;
2591 string strControl = product.vOrderForm[i].second;
2592
2593 if (strLabel.size() < 20)
2594 strLabel.insert(strLabel.begin(), 20 - strLabel.size(), ' ');
2595
2596 m_staticTextLabel[i] = new wxStaticText(m_scrolledWindow, wxID_ANY, strLabel, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
2597 m_staticTextLabel[i]->Wrap(-1);
2598 fgSizer->Add(m_staticTextLabel[i], 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5);
2599
2600 if (strControl.substr(0, 5) == "text=")
2601 {
2602 m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
2603 fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);
2604 windowLast = m_textCtrlField[i];
2605 }
2606 else if (strControl.substr(0, 7) == "choice=")
2607 {
2608 vector<string> vChoices;
2609 ParseString(strControl.substr(7), ',', vChoices);
2610
2611 wxArrayString arraystring;
2612 foreach(const string& str, vChoices)
2613 arraystring.Add(str);
2614
2615 m_choiceField[i] = new wxChoice(m_scrolledWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize, arraystring, 0);
2616 fgSizer->Add(m_choiceField[i], 0, wxALL|wxALIGN_CENTER_VERTICAL, 5);
2617 windowLast = m_choiceField[i];
2618 }
2619 else
2620 {
2621 m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
2622 fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);
2623 m_staticTextLabel[i]->Show(false);
2624 m_textCtrlField[i]->Show(false);
2625 }
2626 }
2627
2628 // Insert after instructions and before submit/cancel buttons
2629 bSizer21->Insert(2, fgSizer, 0, wxEXPAND|wxRIGHT|wxLEFT, 5);
2630 m_scrolledWindow->Layout();
2631 bSizer21->Fit(m_scrolledWindow);
2632 this->Layout();
2633
2634 // Fixup the tab order
2635 m_buttonSubmitForm->MoveAfterInTabOrder(windowLast);
2636 m_buttonCancelForm->MoveAfterInTabOrder(m_buttonSubmitForm);
2637 //m_buttonBack->MoveAfterInTabOrder(m_buttonCancelForm);
2638 //m_buttonNext->MoveAfterInTabOrder(m_buttonBack);
2639 //m_buttonCancel->MoveAfterInTabOrder(m_buttonNext);
2640 this->Layout();
2641}
2642
2643void CViewProductDialog::GetOrder(CWalletTx& wtx)
2644{
2645 wtx.SetNull();
2646 for (int i = 0; i < product.vOrderForm.size(); i++)
2647 {
2648 string strValue;
2649 if (m_textCtrlField[i])
2650 strValue = m_textCtrlField[i]->GetValue().Trim();
2651 else
2652 strValue = m_choiceField[i]->GetStringSelection();
2653 wtx.vOrderForm.push_back(make_pair(m_staticTextLabel[i]->GetLabel(), strValue));
2654 }
2655}
2656
2657void CViewProductDialog::OnButtonSubmitForm(wxCommandEvent& event)
2658{
2659 m_buttonSubmitForm->Enable(false);
2660 m_buttonCancelForm->Enable(false);
2661
2662 CWalletTx wtx;
2663 GetOrder(wtx);
2664
2665 CSendingDialog* pdialog = new CSendingDialog(this, product.addr, atoi64(product.mapValue["price"]), wtx);
2666 if (!pdialog->ShowModal())
2667 {
2668 m_buttonSubmitForm->Enable(true);
2669 m_buttonCancelForm->Enable(true);
2670 return;
2671 }
2672}
2673
2674void CViewProductDialog::OnButtonCancelForm(wxCommandEvent& event)
2675{
2676 Destroy();
2677}
2678
2679void CViewProductDialog::OnButtonBack(wxCommandEvent& event)
2680{
2681 Freeze();
2682 m_htmlWinReviews->Show(true);
2683 m_scrolledWindow->Show(false);
2684 m_buttonBack->Enable(false);
2685 m_buttonNext->Enable(!product.vOrderForm.empty());
2686 this->Layout();
2687 Thaw();
2688}
2689
2690void CViewProductDialog::OnButtonNext(wxCommandEvent& event)
2691{
2692 if (!product.vOrderForm.empty())
2693 {
2694 Freeze();
2695 m_htmlWinReviews->Show(false);
2696 m_scrolledWindow->Show(true);
2697 m_buttonBack->Enable(true);
2698 m_buttonNext->Enable(false);
2699 this->Layout();
2700 Thaw();
2701 }
2702}
2703
2704void CViewProductDialog::OnButtonCancel(wxCommandEvent& event)
2705{
2706 Destroy();
2707}
2708
2709
2710
2711
2712
2713
2714
2715//////////////////////////////////////////////////////////////////////////////
2716//
2717// CViewOrderDialog
2718//
2719
2720CViewOrderDialog::CViewOrderDialog(wxWindow* parent, CWalletTx order, bool fReceived) : CViewOrderDialogBase(parent)
2721{
2722 int64 nPrice = (fReceived ? order.GetCredit() : order.GetDebit());
2723
2724 string strHTML;
2725 strHTML.reserve(4000);
2726 strHTML += "<html>\n"
2727 "<head>\n"
2728 "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"
2729 "</head>\n"
2730 "<body>\n";
2731 strHTML += "<b>Time:</b> " + HtmlEscape(DateTimeStr(order.nTimeReceived)) + "<br>\n";
2732 strHTML += "<b>Price:</b> " + HtmlEscape(FormatMoney(nPrice)) + "<br>\n";
2733 strHTML += "<b>Status:</b> " + HtmlEscape(FormatTxStatus(order)) + "<br>\n";
2734
2735 strHTML += "<table>\n";
2736 for (int i = 0; i < order.vOrderForm.size(); i++)
2737 {
2738 strHTML += " <tr><td><b>" + HtmlEscape(order.vOrderForm[i].first) + ":</b></td>";
2739 strHTML += "<td>" + HtmlEscape(order.vOrderForm[i].second) + "</td></tr>\n";
2740 }
2741 strHTML += "</table>\n";
2742
2743 strHTML += "</body>\n</html>\n";
2744
2745 // Shrink capacity to fit
2746 // (strings are ref counted, so it may live on in SetPage)
2747 string(strHTML.begin(), strHTML.end()).swap(strHTML);
2748
2749 m_htmlWin->SetPage(strHTML);
2750}
2751
2752void CViewOrderDialog::OnButtonOK(wxCommandEvent& event)
2753{
2754 Destroy();
2755}
2756
2757
2758
2759
2760
2761
2762
2763//////////////////////////////////////////////////////////////////////////////
2764//
2765// CEditReviewDialog
2766//
2767
2768CEditReviewDialog::CEditReviewDialog(wxWindow* parent) : CEditReviewDialogBase(parent)
2769{
2770}
2771
2772void CEditReviewDialog::OnButtonSubmit(wxCommandEvent& event)
2773{
2774 if (m_choiceStars->GetSelection() == -1)
2775 {
2776 wxMessageBox("Please select a rating ");
2777 return;
2778 }
2779
2780 CReview review;
2781 GetReview(review);
2782
2783 // Sign the review
2784 review.vchPubKeyFrom = keyUser.GetPubKey();
2785 if (!keyUser.Sign(review.GetSigHash(), review.vchSig))
2786 {
2787 wxMessageBox("Unable to digitally sign the review ");
2788 return;
2789 }
2790
2791 // Broadcast
2792 if (!review.AcceptReview())
2793 {
2794 wxMessageBox("Save failed ");
2795 return;
2796 }
2797 RelayMessage(CInv(MSG_REVIEW, review.GetHash()), review);
2798
2799 Destroy();
2800}
2801
2802void CEditReviewDialog::OnButtonCancel(wxCommandEvent& event)
2803{
2804 Destroy();
2805}
2806
2807void CEditReviewDialog::GetReview(CReview& review)
2808{
2809 review.mapValue["time"] = i64tostr(GetAdjustedTime());
2810 review.mapValue["stars"] = itostr(m_choiceStars->GetSelection()+1);
2811 review.mapValue["review"] = m_textCtrlReview->GetValue();
2812}
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825//////////////////////////////////////////////////////////////////////////////
2826//
2827// CMyApp
2828//
2829
2830// Define a new application
2831class CMyApp: public wxApp
2832{
2833 public:
2834 CMyApp(){};
2835 ~CMyApp(){};
2836 bool OnInit();
2837 bool OnInit2();
2838 int OnExit();
2839
2840 // The MSYS2 release wxWidgets DLLs ship with asserts enabled. Log any
2841 // assertion to debug.log instead of showing a modal "Debug Alert" popup,
2842 // so the packaged build runs cleanly by double-click.
2843 virtual void OnAssertFailure(const wxChar* file, int line, const wxChar* func,
2844 const wxChar* cond, const wxChar* msg)
2845 {
2846 printf("wxAssert: %s(%d) %s: %s %s\n",
2847 (const char*)wxString(file).mb_str(), line,
2848 (const char*)wxString(func).mb_str(),
2849 (const char*)wxString(cond).mb_str(),
2850 (const char*)wxString(msg).mb_str());
2851 }
2852
2853 // 2nd-level exception handling: we get all the exceptions occurring in any
2854 // event handler here
2855 virtual bool OnExceptionInMainLoop();
2856
2857 // 3rd, and final, level exception handling: whenever an unhandled
2858 // exception is caught, this function is called
2859 virtual void OnUnhandledException();
2860
2861 // and now for something different: this function is called in case of a
2862 // crash (e.g. dereferencing null pointer, division by 0, ...)
2863 virtual void OnFatalException();
2864};
2865
2866IMPLEMENT_APP(CMyApp)
2867
2868bool CMyApp::OnInit()
2869{
2870 // The 2009 wxFormBuilder layout mixes wxEXPAND with alignment flags, which
2871 // modern wxWidgets flags as an assertion. Disable those sizer consistency
2872 // checks globally so no "Debug Alert" popup ever blocks the UI.
2873 wxSizerFlags::DisableConsistencyChecks();
2874 try
2875 {
2876 return OnInit2();
2877 }
2878 catch (std::exception& e) {
2879 PrintException(&e, "OnInit()");
2880 } catch (...) {
2881 PrintException(NULL, "OnInit()");
2882 }
2883 return false;
2884}
2885
2886map<string, string> ParseParameters(int argc, char* argv[])
2887{
2888 map<string, string> mapArgs;
2889 for (int i = 0; i < argc; i++)
2890 {
2891 char psz[10000];
2892 strcpy(psz, argv[i]);
2893 char* pszValue = "";
2894 if (strchr(psz, '='))
2895 {
2896 pszValue = strchr(psz, '=');
2897 *pszValue++ = '\0';
2898 }
2899 strlwr(psz);
2900 if (psz[0] == '-')
2901 psz[0] = '/';
2902 mapArgs[psz] = pszValue;
2903 }
2904 return mapArgs;
2905}
2906
2907bool CMyApp::OnInit2()
2908{
2909#ifdef _MSC_VER
2910 // Turn off microsoft heap dump noise for now
2911 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2912 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
2913#endif
2914
2915 //// debug print
2916 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
2917 printf("Bitcoin CMyApp::OnInit()\n");
2918
2919 //
2920 // Parameters (parsed before the lock so /datadir and /port feed the mutex)
2921 //
2922 wxImage::AddHandler(new wxPNGHandler);
2923 map<string, string> mapArgs = ParseParameters(argc, argv);
2924
2925 if (mapArgs.count("/datadir"))
2926 strSetDataDir = mapArgs["/datadir"];
2927
2928 if (mapArgs.count("/port"))
2929 nListenPort = htons(atoi(mapArgs["/port"].c_str()));
2930
2931 //
2932 // Limit to single instance per user/datadir
2933 // The mutex includes the datadir to allow multiple nodes on one machine.
2934 //
2935 const char* pszHomePath = getenv("HOMEPATH");
2936 wxString strMutexName = wxString("Bitflash.") + (pszHomePath ? pszHomePath : "default") + "." + strSetDataDir.c_str();
2937 for (int i = 0; i < strMutexName.size(); i++)
2938 if (!isalnum(strMutexName[i]))
2939 strMutexName[i] = '.';
2940 wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
2941 if (psingleinstancechecker->IsAnotherRunning())
2942 {
2943 printf("Existing instance found\n");
2944 unsigned int nStart = GetTime();
2945 loop
2946 {
2947 // Show the previous instance and exit
2948 HWND hwndPrev = FindWindow("wxWindowClassNR", "Bitflash");
2949 if (hwndPrev)
2950 {
2951 if (IsIconic(hwndPrev))
2952 ShowWindow(hwndPrev, SW_RESTORE);
2953 SetForegroundWindow(hwndPrev);
2954 return false;
2955 }
2956
2957 if (GetTime() > nStart + 60)
2958 return false;
2959
2960 // Resume this instance if the other exits
2961 delete psingleinstancechecker;
2962 Sleep(1000);
2963 psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
2964 if (!psingleinstancechecker->IsAnotherRunning())
2965 break;
2966 }
2967 }
2968
2969 if (mapArgs.count("/proxy"))
2970 addrProxy = CAddress(mapArgs["/proxy"].c_str());
2971
2972 // Rendezvous meeting relay this node registers its .btf service at and
2973 // advertises in its descriptor (default is the seed relay; empty disables).
2974 if (mapArgs.count("/rvrelay"))
2975 strBtfMeetingRelay = mapArgs["/rvrelay"];
2976
2977 // Keep an anonymous outbound connection to a specific .btf peer.
2978 if (mapArgs.count("/connectbtf"))
2979 strBtfConnect = mapArgs["/connectbtf"];
2980
2981 if (mapArgs.count("/debug"))
2982 fDebug = true;
2983
2984 if (mapArgs.count("/dropmessages"))
2985 {
2986 nDropMessagesTest = atoi(mapArgs["/dropmessages"]);
2987 if (nDropMessagesTest == 0)
2988 nDropMessagesTest = 20;
2989 }
2990
2991 if (mapArgs.count("/loadblockindextest"))
2992 {
2993 CTxDB txdb("r");
2994 txdb.LoadBlockIndex();
2995 PrintBlockTree();
2996 ExitProcess(0);
2997 }
2998
2999 //
3000 // Load data files
3001 //
3002 string strErrors;
3003 int64 nStart, nEnd;
3004
3005 printf("Loading addresses...\n");
3006 QueryPerformanceCounter((LARGE_INTEGER*)&nStart);
3007 if (!LoadAddresses())
3008 strErrors += "Error loading addr.dat \n";
3009 QueryPerformanceCounter((LARGE_INTEGER*)&nEnd);
3010 printf(" addresses %20I64d\n", nEnd - nStart);
3011
3012 printf("Loading block index...\n");
3013 QueryPerformanceCounter((LARGE_INTEGER*)&nStart);
3014 if (!LoadBlockIndex())
3015 strErrors += "Error loading blkindex.dat \n";
3016 QueryPerformanceCounter((LARGE_INTEGER*)&nEnd);
3017 printf(" block index %20I64d\n", nEnd - nStart);
3018
3019 printf("Loading wallet...\n");
3020 QueryPerformanceCounter((LARGE_INTEGER*)&nStart);
3021 if (!LoadWallet())
3022 strErrors += "Error loading wallet.dat \n";
3023 QueryPerformanceCounter((LARGE_INTEGER*)&nEnd);
3024 printf(" wallet %20I64d\n", nEnd - nStart);
3025
3026 printf("Done loading\n");
3027
3028 //// debug print
3029 printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size());
3030 printf("nBestHeight = %d\n", nBestHeight);
3031 printf("mapKeys.size() = %d\n", mapKeys.size());
3032 printf("mapPubKeys.size() = %d\n", mapPubKeys.size());
3033 printf("mapWallet.size() = %d\n", mapWallet.size());
3034 printf("mapAddressBook.size() = %d\n", mapAddressBook.size());
3035
3036 if (!strErrors.empty())
3037 {
3038 wxMessageBox(strErrors);
3039 OnExit();
3040 return false;
3041 }
3042
3043 // Add wallet transactions that aren't already in a block to mapTransactions
3044 ReacceptWalletTransactions();
3045
3046 //
3047 // Parameters
3048 //
3049 if (mapArgs.count("/printblockindex") || mapArgs.count("/printblocktree"))
3050 {
3051 PrintBlockTree();
3052 OnExit();
3053 return false;
3054 }
3055
3056 if (mapArgs.count("/solomine"))
3057 fSoloMineTest = true;
3058
3059 if (mapArgs.count("/gen"))
3060 {
3061 if (mapArgs["/gen"].empty())
3062 fGenerateBitcoins = true;
3063 else
3064 fGenerateBitcoins = atoi(mapArgs["/gen"].c_str());
3065 }
3066
3067 //
3068 // Create the main frame window
3069 //
3070 {
3071 pframeMain = new CMainFrame(NULL);
3072 pframeMain->Show();
3073
3074 if (!StartNode(strErrors))
3075 wxMessageBox(strErrors);
3076
3077 if (fGenerateBitcoins)
3078 if (_beginthread(ThreadBitcoinMiner, 0, NULL) == -1)
3079 printf("Error: _beginthread(ThreadBitcoinMiner) failed\n");
3080
3081 //
3082 // Tests
3083 //
3084 if (argc >= 2 && wxString(argv[1]).CmpNoCase("/send") == 0)
3085 {
3086 int64 nValue = 1;
3087 if (argc >= 3)
3088 ParseMoney(argv[2], nValue);
3089
3090 string strAddress;
3091 if (argc >= 4)
3092 strAddress = argv[3];
3093 CAddress addr(strAddress.c_str());
3094
3095 CWalletTx wtx;
3096 wtx.mapValue["to"] = strAddress;
3097 wtx.mapValue["from"] = addrLocalHost.ToString();
3098 wtx.mapValue["message"] = "command line send";
3099
3100 // Send to IP address
3101 CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx);
3102 if (!pdialog->ShowModal())
3103 return false;
3104 }
3105
3106 if (mapArgs.count("/randsendtest"))
3107 {
3108 if (!mapArgs["/randsendtest"].empty())
3109 _beginthread(ThreadRandSendTest, 0, new string(mapArgs["/randsendtest"]));
3110 else
3111 fRandSendTest = true;
3112 fDebug = true;
3113 }
3114 }
3115
3116 return true;
3117}
3118
3119int CMyApp::OnExit()
3120{
3121 Shutdown(NULL);
3122 return wxApp::OnExit();
3123}
3124
3125bool CMyApp::OnExceptionInMainLoop()
3126{
3127 try
3128 {
3129 throw;
3130 }
3131 catch (std::exception& e)
3132 {
3133 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3134 wxLogWarning(_T("Exception %s %s"), typeid(e).name(), e.what());
3135 Sleep(1000);
3136 throw;
3137 }
3138 catch (...)
3139 {
3140 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3141 wxLogWarning(_T("Unknown exception"));
3142 Sleep(1000);
3143 throw;
3144 }
3145
3146 return true;
3147}
3148
3149void CMyApp::OnUnhandledException()
3150{
3151 // this shows how we may let some exception propagate uncaught
3152 try
3153 {
3154 throw;
3155 }
3156 catch (std::exception& e)
3157 {
3158 PrintException(&e, "CMyApp::OnUnhandledException()");
3159 wxLogWarning(_T("Exception %s %s"), typeid(e).name(), e.what());
3160 Sleep(1000);
3161 throw;
3162 }
3163 catch (...)
3164 {
3165 PrintException(NULL, "CMyApp::OnUnhandledException()");
3166 wxLogWarning(_T("Unknown exception"));
3167 Sleep(1000);
3168 throw;
3169 }
3170}
3171
3172void CMyApp::OnFatalException()
3173{
3174 wxMessageBox("Program has crashed and will terminate. ", "", wxOK | wxICON_ERROR);
3175}
3176
3177
3178
3179void MainFrameRepaint()
3180{
3181 if (pframeMain)
3182 {
3183 printf("MainFrameRepaint()\n");
3184 // Called from worker threads: repaint only on the GUI thread
3185 wxTheApp->CallAfter([] { if (pframeMain) pframeMain->Refresh(); });
3186 }
3187}
3188
3189
3190
3191
3192
3193
3194
3195// randsendtest to bitcoin address
3196void ThreadRandSendTest(void* parg)
3197{
3198 string strAddress = *(string*)parg;
3199 uint160 hash160;
3200 if (!AddressToHash160(strAddress, hash160))
3201 {
3202 wxMessageBox(strprintf("ThreadRandSendTest: Bitcoin address '%s' not valid ", strAddress.c_str()));
3203 return;
3204 }
3205
3206 loop
3207 {
3208 Sleep(GetRand(30) * 1000 + 100);
3209
3210 // Message
3211 CWalletTx wtx;
3212 wtx.mapValue["to"] = strAddress;
3213 wtx.mapValue["from"] = addrLocalHost.ToString();
3214 static int nRep;
3215 wtx.mapValue["message"] = strprintf("randsendtest %d\n", ++nRep);
3216
3217 // Value
3218 int64 nValue = (GetRand(9) + 1) * 100 * CENT;
3219 if (GetBalance() < nValue)
3220 {
3221 wxMessageBox("Out of money ");
3222 return;
3223 }
3224 nValue += (nRep % 100) * CENT;
3225
3226 // Send to bitcoin address
3227 CScript scriptPubKey;
3228 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
3229
3230 if (!SendMoney(scriptPubKey, nValue, wtx))
3231 return;
3232 }
3233}
3234
3235
3236// randsendtest to any connected node
3237void RandSend()
3238{
3239 CWalletTx wtx;
3240
3241 while (vNodes.empty())
3242 Sleep(1000);
3243 CAddress addr;
3244 CRITICAL_BLOCK(cs_vNodes)
3245 addr = vNodes[GetRand(vNodes.size())]->addr;
3246
3247 // Message
3248 wtx.mapValue["to"] = addr.ToString();
3249 wtx.mapValue["from"] = addrLocalHost.ToString();
3250 static int nRep;
3251 wtx.mapValue["message"] = strprintf("randsendtest %d\n", ++nRep);
3252
3253 // Value
3254 int64 nValue = (GetRand(999) + 1) * CENT;
3255 if (GetBalance() < nValue)
3256 {
3257 wxMessageBox("Out of money ");
3258 return;
3259 }
3260
3261 // Send to IP address
3262 CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx);
3263 if (!pdialog->Show())
3264 wxMessageBox("ShowModal Failed ");
3265}