json-rpc password, minor UI tweaks, removed some outdated txt files
[novacoin.git] / ui.cpp
1 // Copyright (c) 2009-2010 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
12 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
13
14 CMainFrame* pframeMain = NULL;
15 CMyTaskBarIcon* ptaskbaricon = NULL;
16 bool fClosedToTray = false;
17
18
19
20
21
22
23
24
25
26 //////////////////////////////////////////////////////////////////////////////
27 //
28 // Util
29 //
30
31 void HandleCtrlA(wxKeyEvent& event)
32 {
33     // Ctrl-a select all
34     event.Skip();
35     wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
36     if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
37         textCtrl->SetSelection(-1, -1);
38 }
39
40 bool Is24HourTime()
41 {
42     //char pszHourFormat[256];
43     //pszHourFormat[0] = '\0';
44     //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
45     //return (pszHourFormat[0] != '0');
46     return true;
47 }
48
49 string DateStr(int64 nTime)
50 {
51     // Can only be used safely here in the UI
52     return (string)wxDateTime((time_t)nTime).FormatDate();
53 }
54
55 string DateTimeStr(int64 nTime)
56 {
57     // Can only be used safely here in the UI
58     wxDateTime datetime((time_t)nTime);
59     if (Is24HourTime())
60         return (string)datetime.Format("%x %H:%M");
61     else
62         return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
63 }
64
65 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
66 {
67     // Helper to simplify access to listctrl
68     wxListItem item;
69     item.m_itemId = nIndex;
70     item.m_col = nColumn;
71     item.m_mask = wxLIST_MASK_TEXT;
72     if (!listCtrl->GetItem(item))
73         return "";
74     return item.GetText();
75 }
76
77 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
78 {
79     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
80     listCtrl->SetItem(nIndex, 1, str1);
81     return nIndex;
82 }
83
84 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
85 {
86     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
87     listCtrl->SetItem(nIndex, 1, str1);
88     listCtrl->SetItem(nIndex, 2, str2);
89     listCtrl->SetItem(nIndex, 3, str3);
90     listCtrl->SetItem(nIndex, 4, str4);
91     return nIndex;
92 }
93
94 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
95 {
96     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
97     listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
98     listCtrl->SetItem(nIndex, 1, str1);
99     listCtrl->SetItem(nIndex, 2, str2);
100     listCtrl->SetItem(nIndex, 3, str3);
101     listCtrl->SetItem(nIndex, 4, str4);
102     return nIndex;
103 }
104
105 void SetSelection(wxListCtrl* listCtrl, int nIndex)
106 {
107     int nSize = listCtrl->GetItemCount();
108     long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
109     for (int i = 0; i < nSize; i++)
110         listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
111 }
112
113 int GetSelection(wxListCtrl* listCtrl)
114 {
115     int nSize = listCtrl->GetItemCount();
116     for (int i = 0; i < nSize; i++)
117         if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
118             return i;
119     return -1;
120 }
121
122 string HtmlEscape(const char* psz, bool fMultiLine=false)
123 {
124     int len = 0;
125     for (const char* p = psz; *p; p++)
126     {
127              if (*p == '<') len += 4;
128         else if (*p == '>') len += 4;
129         else if (*p == '&') len += 5;
130         else if (*p == '"') len += 6;
131         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
132         else if (*p == '\n' && fMultiLine) len += 5;
133         else
134             len++;
135     }
136     string str;
137     str.reserve(len);
138     for (const char* p = psz; *p; p++)
139     {
140              if (*p == '<') str += "&lt;";
141         else if (*p == '>') str += "&gt;";
142         else if (*p == '&') str += "&amp;";
143         else if (*p == '"') str += "&quot;";
144         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += "&nbsp;";
145         else if (*p == '\n' && fMultiLine) str += "<br>\n";
146         else
147             str += *p;
148     }
149     return str;
150 }
151
152 string HtmlEscape(const string& str, bool fMultiLine=false)
153 {
154     return HtmlEscape(str.c_str(), fMultiLine);
155 }
156
157 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
158 {
159     *pnRet = wxMessageBox(message, caption, style, parent, x, y);
160     *pfDone = true;
161 }
162
163 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
164 {
165 #ifdef __WXMSW__
166     return wxMessageBox(message, caption, style, parent, x, y);
167 #else
168     if (wxThread::IsMain() || fDaemon)
169     {
170         return wxMessageBox(message, caption, style, parent, x, y);
171     }
172     else
173     {
174         int nRet = 0;
175         bool fDone = false;
176         UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
177         while (!fDone)
178             Sleep(100);
179         return nRet;
180     }
181 #endif
182 }
183
184 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
185 {
186     if (nFeeRequired == 0 || fDaemon)
187         return true;
188     string strMessage = strprintf(
189         _("This transaction is over the size limit.  You can still send it for a fee of %s, "
190           "which goes to the nodes that process your transaction and helps to support the network.  "
191           "Do you want to pay the fee?"),
192         FormatMoney(nFeeRequired).c_str());
193     return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
194 }
195
196 void CalledSetStatusBar(const string& strText, int nField)
197 {
198     if (pframeMain && pframeMain->m_statusBar)
199         pframeMain->m_statusBar->SetStatusText(strText, nField);
200 }
201
202 void SetDefaultReceivingAddress(const string& strAddress)
203 {
204     // Update main window address and database
205     if (pframeMain == NULL)
206         return;
207     if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
208     {
209         uint160 hash160;
210         if (!AddressToHash160(strAddress, hash160))
211             return;
212         if (!mapPubKeys.count(hash160))
213             return;
214         CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
215         pframeMain->m_textCtrlAddress->SetValue(strAddress);
216     }
217 }
218
219
220
221
222
223
224
225
226
227
228 //////////////////////////////////////////////////////////////////////////////
229 //
230 // CMainFrame
231 //
232
233 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
234 {
235     Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
236
237     // Set initially selected page
238     wxNotebookEvent event;
239     event.SetSelection(0);
240     OnNotebookPageChanged(event);
241     m_notebook->ChangeSelection(0);
242
243     // Init
244     fRefreshListCtrl = false;
245     fRefreshListCtrlRunning = false;
246     fOnSetFocusAddress = false;
247     fRefresh = false;
248     m_choiceFilter->SetSelection(0);
249     double dResize = 1.0;
250 #ifdef __WXMSW__
251     SetIcon(wxICON(bitcoin));
252 #else
253     SetIcon(bitcoin80_xpm);
254     SetBackgroundColour(m_toolBar->GetBackgroundColour());
255     wxFont fontTmp = m_staticText41->GetFont();
256     fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
257     m_staticTextBalance->SetFont(fontTmp);
258     m_staticTextBalance->SetSize(140, 17);
259     // resize to fit ubuntu's huge default font
260     dResize = 1.22;
261     SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
262 #endif
263     m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + "  ");
264     m_listCtrl->SetFocus();
265     ptaskbaricon = new CMyTaskBarIcon();
266 #ifdef __WXMAC__
267     // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
268     // to their standard places, leaving these menus empty.
269     GetMenuBar()->Remove(2); // remove Help menu
270     GetMenuBar()->Remove(0); // remove File menu
271 #endif
272
273     // Init column headers
274     int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
275     if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
276         nDateWidth += 12;
277 #ifdef __WXMAC__
278     nDateWidth += 5;
279     dResize -= 0.01;
280 #endif
281     wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
282     foreach(wxListCtrl* p, pplistCtrl)
283     {
284         p->InsertColumn(0, "",               wxLIST_FORMAT_LEFT,  dResize * 0);
285         p->InsertColumn(1, "",               wxLIST_FORMAT_LEFT,  dResize * 0);
286         p->InsertColumn(2, _("Status"),      wxLIST_FORMAT_LEFT,  dResize * 112);
287         p->InsertColumn(3, _("Date"),        wxLIST_FORMAT_LEFT,  dResize * nDateWidth);
288         p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT,  dResize * 409 - nDateWidth);
289         p->InsertColumn(5, _("Debit"),       wxLIST_FORMAT_RIGHT, dResize * 79);
290         p->InsertColumn(6, _("Credit"),      wxLIST_FORMAT_RIGHT, dResize * 79);
291     }
292
293     // Init status bar
294     int pnWidths[3] = { -100, 88, 300 };
295 #ifndef __WXMSW__
296     pnWidths[1] = pnWidths[1] * 1.1 * dResize;
297     pnWidths[2] = pnWidths[2] * 1.1 * dResize;
298 #endif
299     m_statusBar->SetFieldsCount(3, pnWidths);
300
301     // Fill your address text box
302     vector<unsigned char> vchPubKey;
303     if (CWalletDB("r").ReadDefaultKey(vchPubKey))
304         m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
305
306     // Fill listctrl with wallet transactions
307     RefreshListCtrl();
308 }
309
310 CMainFrame::~CMainFrame()
311 {
312     pframeMain = NULL;
313     delete ptaskbaricon;
314     ptaskbaricon = NULL;
315 }
316
317 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
318 {
319     event.Skip();
320     nPage = event.GetSelection();
321     if (nPage == ALL)
322     {
323         m_listCtrl = m_listCtrlAll;
324         fShowGenerated = true;
325         fShowSent = true;
326         fShowReceived = true;
327     }
328     else if (nPage == SENTRECEIVED)
329     {
330         m_listCtrl = m_listCtrlSentReceived;
331         fShowGenerated = false;
332         fShowSent = true;
333         fShowReceived = true;
334     }
335     else if (nPage == SENT)
336     {
337         m_listCtrl = m_listCtrlSent;
338         fShowGenerated = false;
339         fShowSent = true;
340         fShowReceived = false;
341     }
342     else if (nPage == RECEIVED)
343     {
344         m_listCtrl = m_listCtrlReceived;
345         fShowGenerated = false;
346         fShowSent = false;
347         fShowReceived = true;
348     }
349     RefreshListCtrl();
350     m_listCtrl->SetFocus();
351 }
352
353 void CMainFrame::OnClose(wxCloseEvent& event)
354 {
355     if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
356     {
357         // Divert close to minimize
358         event.Veto();
359         fClosedToTray = true;
360         Iconize(true);
361     }
362     else
363     {
364         Destroy();
365         CreateThread(Shutdown, NULL);
366     }
367 }
368
369 void CMainFrame::OnIconize(wxIconizeEvent& event)
370 {
371     event.Skip();
372     // Hide the task bar button when minimized.
373     // Event is sent when the frame is minimized or restored.
374     // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
375     // to get rid of the deprecated warning.  Just ignore it.
376     if (!event.Iconized())
377         fClosedToTray = false;
378 #ifdef __WXGTK__
379     if (mapArgs.count("-minimizetotray")) {
380 #endif
381     // The tray icon sometimes disappears on ubuntu karmic
382     // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
383     // Reports of CPU peg on 64-bit linux
384     if (fMinimizeToTray && event.Iconized())
385         fClosedToTray = true;
386     Show(!fClosedToTray);
387     ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
388 #ifdef __WXGTK__
389     }
390 #endif
391 }
392
393 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
394 {
395     event.Skip();
396     RandAddSeed();
397     RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
398     RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
399 }
400
401 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
402 {
403     // Hidden columns not resizeable
404     if (event.GetColumn() <= 1 && !fDebug)
405         event.Veto();
406     else
407         event.Skip();
408 }
409
410 int CMainFrame::GetSortIndex(const string& strSort)
411 {
412 #ifdef __WXMSW__
413     return 0;
414 #else
415     // The wx generic listctrl implementation used on GTK doesn't sort,
416     // so we have to do it ourselves.  Remember, we sort in reverse order.
417     // In the wx generic implementation, they store the list of items
418     // in a vector, so indexed lookups are fast, but inserts are slower
419     // the closer they are to the top.
420     int low = 0;
421     int high = m_listCtrl->GetItemCount();
422     while (low < high)
423     {
424         int mid = low + ((high - low) / 2);
425         if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
426             high = mid;
427         else
428             low = mid + 1;
429     }
430     return low;
431 #endif
432 }
433
434 void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6)
435 {
436     strSort = " " + strSort;       // leading space to workaround wx2.9.0 ubuntu 9.10 bug
437     long nData = *(long*)&hashKey; //  where first char of hidden column is displayed
438
439     // Find item
440     if (!fNew && nIndex == -1)
441     {
442         string strHash = " " + hashKey.ToString();
443         while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
444             if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
445                 break;
446     }
447
448     // fNew is for blind insert, only use if you're sure it's new
449     if (fNew || nIndex == -1)
450     {
451         nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
452     }
453     else
454     {
455         // If sort key changed, must delete and reinsert to make it relocate
456         if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
457         {
458             m_listCtrl->DeleteItem(nIndex);
459             nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
460         }
461     }
462
463     m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
464     m_listCtrl->SetItem(nIndex, 2, str2);
465     m_listCtrl->SetItem(nIndex, 3, str3);
466     m_listCtrl->SetItem(nIndex, 4, str4);
467     m_listCtrl->SetItem(nIndex, 5, str5);
468     m_listCtrl->SetItem(nIndex, 6, str6);
469     m_listCtrl->SetItemData(nIndex, nData);
470 }
471
472 bool CMainFrame::DeleteLine(uint256 hashKey)
473 {
474     long nData = *(long*)&hashKey;
475
476     // Find item
477     int nIndex = -1;
478     string strHash = " " + hashKey.ToString();
479     while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
480         if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
481             break;
482
483     if (nIndex != -1)
484         m_listCtrl->DeleteItem(nIndex);
485
486     return nIndex != -1;
487 }
488
489 string FormatTxStatus(const CWalletTx& wtx)
490 {
491     // Status
492     if (!wtx.IsFinal())
493     {
494         if (wtx.nLockTime < 500000000)
495             return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
496         else
497             return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
498     }
499     else
500     {
501         int nDepth = wtx.GetDepthInMainChain();
502         if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
503             return strprintf(_("%d/offline?"), nDepth);
504         else if (nDepth < 6)
505             return strprintf(_("%d/unconfirmed"), nDepth);
506         else
507             return strprintf(_("%d confirmations"), nDepth);
508     }
509 }
510
511 string SingleLine(const string& strIn)
512 {
513     string strOut;
514     bool fOneSpace = false;
515     foreach(int c, strIn)
516     {
517         if (isspace(c))
518         {
519             fOneSpace = true;
520         }
521         else if (c > ' ')
522         {
523             if (fOneSpace && !strOut.empty())
524                 strOut += ' ';
525             strOut += c;
526             fOneSpace = false;
527         }
528     }
529     return strOut;
530 }
531
532 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
533 {
534     int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
535     int64 nCredit = wtx.GetCredit(true);
536     int64 nDebit = wtx.GetDebit();
537     int64 nNet = nCredit - nDebit;
538     uint256 hash = wtx.GetHash();
539     string strStatus = FormatTxStatus(wtx);
540     map<string, string> mapValue = wtx.mapValue;
541     wtx.nLinesDisplayed = 1;
542     nListViewUpdated++;
543
544     // Filter
545     if (wtx.IsCoinBase())
546     {
547         // Don't show generated coin until confirmed by at least one block after it
548         // so we don't get the user's hopes up until it looks like it's probably accepted.
549         //
550         // It is not an error when generated blocks are not accepted.  By design,
551         // some percentage of blocks, like 10% or more, will end up not accepted.
552         // This is the normal mechanism by which the network copes with latency.
553         //
554         // We display regular transactions right away before any confirmation
555         // because they can always get into some block eventually.  Generated coins
556         // are special because if their block is not accepted, they are not valid.
557         //
558         if (wtx.GetDepthInMainChain() < 2)
559         {
560             wtx.nLinesDisplayed = 0;
561             return false;
562         }
563
564         if (!fShowGenerated)
565             return false;
566     }
567
568     // Find the block the tx is in
569     CBlockIndex* pindex = NULL;
570     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
571     if (mi != mapBlockIndex.end())
572         pindex = (*mi).second;
573
574     // Sort order, unrecorded transactions sort to the top
575     string strSort = strprintf("%010d-%01d-%010u",
576         (pindex ? pindex->nHeight : INT_MAX),
577         (wtx.IsCoinBase() ? 1 : 0),
578         wtx.nTimeReceived);
579
580     // Insert line
581     if (nNet > 0 || wtx.IsCoinBase())
582     {
583         //
584         // Credit
585         //
586         string strDescription;
587         if (wtx.IsCoinBase())
588         {
589             // Generated
590             strDescription = _("Generated");
591             if (nCredit == 0)
592             {
593                 int64 nUnmatured = 0;
594                 foreach(const CTxOut& txout, wtx.vout)
595                     nUnmatured += txout.GetCredit();
596                 if (wtx.IsInMainChain())
597                 {
598                     strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
599
600                     // Check if the block was requested by anyone
601                     if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
602                         strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
603                 }
604                 else
605                 {
606                     strDescription = _("Generated (not accepted)");
607                 }
608             }
609         }
610         else if (!mapValue["from"].empty() || !mapValue["message"].empty())
611         {
612             // Received by IP connection
613             if (!fShowReceived)
614                 return false;
615             if (!mapValue["from"].empty())
616                 strDescription += _("From: ") + mapValue["from"];
617             if (!mapValue["message"].empty())
618             {
619                 if (!strDescription.empty())
620                     strDescription += " - ";
621                 strDescription += mapValue["message"];
622             }
623         }
624         else
625         {
626             // Received by Bitcoin Address
627             if (!fShowReceived)
628                 return false;
629             foreach(const CTxOut& txout, wtx.vout)
630             {
631                 if (txout.IsMine())
632                 {
633                     vector<unsigned char> vchPubKey;
634                     if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
635                     {
636                         CRITICAL_BLOCK(cs_mapAddressBook)
637                         {
638                             //strDescription += _("Received payment to ");
639                             //strDescription += _("Received with address ");
640                             strDescription += _("From: unknown, Received with: ");
641                             string strAddress = PubKeyToAddress(vchPubKey);
642                             map<string, string>::iterator mi = mapAddressBook.find(strAddress);
643                             if (mi != mapAddressBook.end() && !(*mi).second.empty())
644                             {
645                                 string strLabel = (*mi).second;
646                                 strDescription += strAddress.substr(0,12) + "... ";
647                                 strDescription += "(" + strLabel + ")";
648                             }
649                             else
650                                 strDescription += strAddress;
651                         }
652                     }
653                     break;
654                 }
655             }
656         }
657
658         InsertLine(fNew, nIndex, hash, strSort,
659                    strStatus,
660                    nTime ? DateTimeStr(nTime) : "",
661                    SingleLine(strDescription),
662                    "",
663                    FormatMoney(nNet, true));
664     }
665     else
666     {
667         bool fAllFromMe = true;
668         foreach(const CTxIn& txin, wtx.vin)
669             fAllFromMe = fAllFromMe && txin.IsMine();
670
671         bool fAllToMe = true;
672         foreach(const CTxOut& txout, wtx.vout)
673             fAllToMe = fAllToMe && txout.IsMine();
674
675         if (fAllFromMe && fAllToMe)
676         {
677             // Payment to self
678             int64 nValue = wtx.vout[0].nValue;
679             InsertLine(fNew, nIndex, hash, strSort,
680                        strStatus,
681                        nTime ? DateTimeStr(nTime) : "",
682                        _("Payment to yourself"),
683                        "",
684                        "");
685             /// issue: can't tell which is the payment and which is the change anymore
686             //           FormatMoney(nNet - nValue, true),
687             //           FormatMoney(nValue, true));
688         }
689         else if (fAllFromMe)
690         {
691             //
692             // Debit
693             //
694             if (!fShowSent)
695                 return false;
696
697             int64 nTxFee = nDebit - wtx.GetValueOut();
698             wtx.nLinesDisplayed = 0;
699             for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
700             {
701                 const CTxOut& txout = wtx.vout[nOut];
702                 if (txout.IsMine())
703                     continue;
704
705                 string strAddress;
706                 if (!mapValue["to"].empty())
707                 {
708                     // Sent to IP
709                     strAddress = mapValue["to"];
710                 }
711                 else
712                 {
713                     // Sent to Bitcoin Address
714                     uint160 hash160;
715                     if (ExtractHash160(txout.scriptPubKey, hash160))
716                         strAddress = Hash160ToAddress(hash160);
717                 }
718
719                 string strDescription = _("To: ");
720                 CRITICAL_BLOCK(cs_mapAddressBook)
721                     if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
722                         strDescription += mapAddressBook[strAddress] + " ";
723                 strDescription += strAddress;
724                 if (!mapValue["message"].empty())
725                 {
726                     if (!strDescription.empty())
727                         strDescription += " - ";
728                     strDescription += mapValue["message"];
729                 }
730
731                 int64 nValue = txout.nValue;
732                 if (nTxFee > 0)
733                 {
734                     nValue += nTxFee;
735                     nTxFee = 0;
736                 }
737
738                 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
739                            strStatus,
740                            nTime ? DateTimeStr(nTime) : "",
741                            SingleLine(strDescription),
742                            FormatMoney(-nValue, true),
743                            "");
744                 wtx.nLinesDisplayed++;
745             }
746         }
747         else
748         {
749             //
750             // Mixed debit transaction, can't break down payees
751             //
752             bool fAllMine = true;
753             foreach(const CTxOut& txout, wtx.vout)
754                 fAllMine = fAllMine && txout.IsMine();
755             foreach(const CTxIn& txin, wtx.vin)
756                 fAllMine = fAllMine && txin.IsMine();
757
758             InsertLine(fNew, nIndex, hash, strSort,
759                        strStatus,
760                        nTime ? DateTimeStr(nTime) : "",
761                        "",
762                        FormatMoney(nNet, true),
763                        "");
764         }
765     }
766
767     return true;
768 }
769
770 void CMainFrame::RefreshListCtrl()
771 {
772     fRefreshListCtrl = true;
773     ::wxWakeUpIdle();
774 }
775
776 void CMainFrame::OnIdle(wxIdleEvent& event)
777 {
778     if (fRefreshListCtrl)
779     {
780         // Collect list of wallet transactions and sort newest first
781         bool fEntered = false;
782         vector<pair<unsigned int, uint256> > vSorted;
783         TRY_CRITICAL_BLOCK(cs_mapWallet)
784         {
785             printf("RefreshListCtrl starting\n");
786             fEntered = true;
787             fRefreshListCtrl = false;
788             vWalletUpdated.clear();
789
790             // Do the newest transactions first
791             vSorted.reserve(mapWallet.size());
792             for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
793             {
794                 const CWalletTx& wtx = (*it).second;
795                 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
796                 vSorted.push_back(make_pair(nTime, (*it).first));
797             }
798             m_listCtrl->DeleteAllItems();
799         }
800         if (!fEntered)
801             return;
802
803         sort(vSorted.begin(), vSorted.end());
804
805         // Fill list control
806         for (int i = 0; i < vSorted.size();)
807         {
808             if (fShutdown)
809                 return;
810             bool fEntered = false;
811             TRY_CRITICAL_BLOCK(cs_mapWallet)
812             {
813                 fEntered = true;
814                 uint256& hash = vSorted[i++].second;
815                 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
816                 if (mi != mapWallet.end())
817                     InsertTransaction((*mi).second, true);
818             }
819             if (!fEntered || i == 100 || i % 500 == 0)
820                 wxYield();
821         }
822
823         printf("RefreshListCtrl done\n");
824
825         // Update transaction total display
826         MainFrameRepaint();
827     }
828     else
829     {
830         // Check for time updates
831         static int64 nLastTime;
832         if (GetTime() > nLastTime + 30)
833         {
834             TRY_CRITICAL_BLOCK(cs_mapWallet)
835             {
836                 nLastTime = GetTime();
837                 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
838                 {
839                     CWalletTx& wtx = (*it).second;
840                     if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
841                         InsertTransaction(wtx, false);
842                 }
843             }
844         }
845     }
846 }
847
848 void CMainFrame::RefreshStatusColumn()
849 {
850     static int nLastTop;
851     static CBlockIndex* pindexLastBest;
852     static unsigned int nLastRefreshed;
853
854     int nTop = max((int)m_listCtrl->GetTopItem(), 0);
855     if (nTop == nLastTop && pindexLastBest == pindexBest)
856         return;
857
858     TRY_CRITICAL_BLOCK(cs_mapWallet)
859     {
860         int nStart = nTop;
861         int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
862
863         if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
864         {
865             // If no updates, only need to do the part that moved onto the screen
866             if (nStart >= nLastTop && nStart < nLastTop + 100)
867                 nStart = nLastTop + 100;
868             if (nEnd >= nLastTop && nEnd < nLastTop + 100)
869                 nEnd = nLastTop;
870         }
871         nLastTop = nTop;
872         pindexLastBest = pindexBest;
873         nLastRefreshed = nListViewUpdated;
874
875         for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
876         {
877             uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
878             map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
879             if (mi == mapWallet.end())
880             {
881                 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
882                 continue;
883             }
884             CWalletTx& wtx = (*mi).second;
885             if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
886             {
887                 if (!InsertTransaction(wtx, false, nIndex))
888                     m_listCtrl->DeleteItem(nIndex--);
889             }
890             else
891                 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
892         }
893     }
894 }
895
896 void CMainFrame::OnPaint(wxPaintEvent& event)
897 {
898     event.Skip();
899     if (fRefresh)
900     {
901         fRefresh = false;
902         Refresh();
903     }
904 }
905
906
907 unsigned int nNeedRepaint = 0;
908 unsigned int nLastRepaint = 0;
909 int64 nLastRepaintTime = 0;
910 int64 nRepaintInterval = 500;
911
912 void ThreadDelayedRepaint(void* parg)
913 {
914     while (!fShutdown)
915     {
916         if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
917         {
918             nLastRepaint = nNeedRepaint;
919             if (pframeMain)
920             {
921                 printf("DelayedRepaint\n");
922                 wxPaintEvent event;
923                 pframeMain->fRefresh = true;
924                 pframeMain->GetEventHandler()->AddPendingEvent(event);
925             }
926         }
927         Sleep(nRepaintInterval);
928     }
929 }
930
931 void MainFrameRepaint()
932 {
933     // This is called by network code that shouldn't access pframeMain
934     // directly because it could still be running after the UI is closed.
935     if (pframeMain)
936     {
937         // Don't repaint too often
938         static int64 nLastRepaintRequest;
939         if (GetTimeMillis() - nLastRepaintRequest < 100)
940         {
941             nNeedRepaint++;
942             return;
943         }
944         nLastRepaintRequest = GetTimeMillis();
945
946         printf("MainFrameRepaint\n");
947         wxPaintEvent event;
948         pframeMain->fRefresh = true;
949         pframeMain->GetEventHandler()->AddPendingEvent(event);
950     }
951 }
952
953 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
954 {
955     // Skip lets the listctrl do the paint, we're just hooking the message
956     event.Skip();
957
958     if (ptaskbaricon)
959         ptaskbaricon->UpdateTooltip();
960
961     //
962     // Slower stuff
963     //
964     static int nTransactionCount;
965     bool fPaintedBalance = false;
966     if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
967     {
968         nLastRepaint = nNeedRepaint;
969         nLastRepaintTime = GetTimeMillis();
970
971         // Update listctrl contents
972         if (!vWalletUpdated.empty())
973         {
974             TRY_CRITICAL_BLOCK(cs_mapWallet)
975             {
976                 string strTop;
977                 if (m_listCtrl->GetItemCount())
978                     strTop = (string)m_listCtrl->GetItemText(0);
979                 foreach(uint256 hash, vWalletUpdated)
980                 {
981                     map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
982                     if (mi != mapWallet.end())
983                         InsertTransaction((*mi).second, false);
984                 }
985                 vWalletUpdated.clear();
986                 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
987                     m_listCtrl->ScrollList(0, INT_MIN/2);
988             }
989         }
990
991         // Balance total
992         TRY_CRITICAL_BLOCK(cs_mapWallet)
993         {
994             fPaintedBalance = true;
995             m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + "  ");
996
997             // Count hidden and multi-line transactions
998             nTransactionCount = 0;
999             for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1000             {
1001                 CWalletTx& wtx = (*it).second;
1002                 nTransactionCount += wtx.nLinesDisplayed;
1003             }
1004         }
1005     }
1006     if (!vWalletUpdated.empty() || !fPaintedBalance)
1007         nNeedRepaint++;
1008
1009     // Update status column of visible items only
1010     RefreshStatusColumn();
1011
1012     // Update status bar
1013     string strGen = "";
1014     if (fGenerateBitcoins)
1015         strGen = _("    Generating");
1016     if (fGenerateBitcoins && vNodes.empty())
1017         strGen = _("(not connected)");
1018     m_statusBar->SetStatusText(strGen, 1);
1019
1020     string strStatus = strprintf(_("     %d connections     %d blocks     %d transactions"), vNodes.size(), nBestHeight + 1, nTransactionCount);
1021     m_statusBar->SetStatusText(strStatus, 2);
1022
1023     if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
1024         m_statusBar->SetStatusText("     ERROR: ThreadSocketHandler has stopped", 0);
1025
1026     // Update receiving address
1027     string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1028     if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1029         m_textCtrlAddress->SetValue(strDefaultAddress);
1030 }
1031
1032
1033 void UIThreadCall(boost::function0<void> fn)
1034 {
1035     // Call this with a function object created with bind.
1036     // bind needs all parameters to match the function's expected types
1037     // and all default parameters specified.  Some examples:
1038     //  UIThreadCall(bind(wxBell));
1039     //  UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1040     //  UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1041     if (pframeMain)
1042     {
1043         wxCommandEvent event(wxEVT_UITHREADCALL);
1044         event.SetClientData((void*)new boost::function0<void>(fn));
1045         pframeMain->GetEventHandler()->AddPendingEvent(event);
1046     }
1047 }
1048
1049 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1050 {
1051     boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1052     (*pfn)();
1053     delete pfn;
1054 }
1055
1056 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1057 {
1058     // File->Exit
1059     Close(true);
1060 }
1061
1062 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
1063 {
1064     // Options->Generate Coins
1065     GenerateBitcoins(event.IsChecked());
1066 }
1067
1068 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1069 {
1070     event.Check(fGenerateBitcoins);
1071 }
1072
1073 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1074 {
1075     // Options->Your Receiving Addresses
1076     CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1077     if (!dialog.ShowModal())
1078         return;
1079 }
1080
1081 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1082 {
1083     // Options->Options
1084     COptionsDialog dialog(this);
1085     dialog.ShowModal();
1086 }
1087
1088 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1089 {
1090     // Help->About
1091     CAboutDialog dialog(this);
1092     dialog.ShowModal();
1093 }
1094
1095 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1096 {
1097     // Toolbar: Send
1098     CSendDialog dialog(this);
1099     dialog.ShowModal();
1100 }
1101
1102 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1103 {
1104     // Toolbar: Address Book
1105     CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1106     if (dialogAddr.ShowModal() == 2)
1107     {
1108         // Send
1109         CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1110         dialogSend.ShowModal();
1111     }
1112 }
1113
1114 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1115 {
1116     // Automatically select-all when entering window
1117     event.Skip();
1118     m_textCtrlAddress->SetSelection(-1, -1);
1119     fOnSetFocusAddress = true;
1120 }
1121
1122 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1123 {
1124     event.Skip();
1125     if (fOnSetFocusAddress)
1126         m_textCtrlAddress->SetSelection(-1, -1);
1127     fOnSetFocusAddress = false;
1128 }
1129
1130 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1131 {
1132     // Ask name
1133     CGetTextFromUserDialog dialog(this,
1134         _("New Receiving Address"),
1135         _("You should use a new address for each payment you receive.\n\nLabel"),
1136         "");
1137     if (!dialog.ShowModal())
1138         return;
1139     string strName = dialog.GetValue();
1140
1141     // Generate new key
1142     string strAddress = PubKeyToAddress(GenerateNewKey());
1143
1144     // Save
1145     SetAddressBookName(strAddress, strName);
1146     SetDefaultReceivingAddress(strAddress);
1147 }
1148
1149 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1150 {
1151     // Copy address box to clipboard
1152     if (wxTheClipboard->Open())
1153     {
1154         wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1155         wxTheClipboard->Close();
1156     }
1157 }
1158
1159 void CMainFrame::OnListItemActivated(wxListEvent& event)
1160 {
1161     uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1162     CWalletTx wtx;
1163     CRITICAL_BLOCK(cs_mapWallet)
1164     {
1165         map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1166         if (mi == mapWallet.end())
1167         {
1168             printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1169             return;
1170         }
1171         wtx = (*mi).second;
1172     }
1173     CTxDetailsDialog dialog(this, wtx);
1174     dialog.ShowModal();
1175     //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1176     //pdialog->Show();
1177 }
1178
1179
1180
1181
1182
1183
1184 //////////////////////////////////////////////////////////////////////////////
1185 //
1186 // CTxDetailsDialog
1187 //
1188
1189 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1190 {
1191     CRITICAL_BLOCK(cs_mapAddressBook)
1192     {
1193         string strHTML;
1194         strHTML.reserve(4000);
1195         strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1196
1197         int64 nTime = wtx.GetTxTime();
1198         int64 nCredit = wtx.GetCredit();
1199         int64 nDebit = wtx.GetDebit();
1200         int64 nNet = nCredit - nDebit;
1201
1202
1203
1204         strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1205         int nRequests = wtx.GetRequestCount();
1206         if (nRequests != -1)
1207         {
1208             if (nRequests == 0)
1209                 strHTML += _(", has not been successfully broadcast yet");
1210             else if (nRequests == 1)
1211                 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1212             else
1213                 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1214         }
1215         strHTML += "<br>";
1216
1217         strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1218
1219
1220         //
1221         // From
1222         //
1223         if (wtx.IsCoinBase())
1224         {
1225             strHTML += _("<b>Source:</b> Generated<br>");
1226         }
1227         else if (!wtx.mapValue["from"].empty())
1228         {
1229             // Online transaction
1230             if (!wtx.mapValue["from"].empty())
1231                 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1232         }
1233         else
1234         {
1235             // Offline transaction
1236             if (nNet > 0)
1237             {
1238                 // Credit
1239                 foreach(const CTxOut& txout, wtx.vout)
1240                 {
1241                     if (txout.IsMine())
1242                     {
1243                         vector<unsigned char> vchPubKey;
1244                         if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1245                         {
1246                             string strAddress = PubKeyToAddress(vchPubKey);
1247                             if (mapAddressBook.count(strAddress))
1248                             {
1249                                 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1250                                 strHTML += _("<b>To:</b> ");
1251                                 strHTML += HtmlEscape(strAddress);
1252                                 if (!mapAddressBook[strAddress].empty())
1253                                     strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1254                                 else
1255                                     strHTML += _(" (yours)");
1256                                 strHTML += "<br>";
1257                             }
1258                         }
1259                         break;
1260                     }
1261                 }
1262             }
1263         }
1264
1265
1266         //
1267         // To
1268         //
1269         string strAddress;
1270         if (!wtx.mapValue["to"].empty())
1271         {
1272             // Online transaction
1273             strAddress = wtx.mapValue["to"];
1274             strHTML += _("<b>To:</b> ");
1275             if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1276                 strHTML += mapAddressBook[strAddress] + " ";
1277             strHTML += HtmlEscape(strAddress) + "<br>";
1278         }
1279
1280
1281         //
1282         // Amount
1283         //
1284         if (wtx.IsCoinBase() && nCredit == 0)
1285         {
1286             //
1287             // Coinbase
1288             //
1289             int64 nUnmatured = 0;
1290             foreach(const CTxOut& txout, wtx.vout)
1291                 nUnmatured += txout.GetCredit();
1292             strHTML += _("<b>Credit:</b> ");
1293             if (wtx.IsInMainChain())
1294                 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1295             else
1296                 strHTML += _("(not accepted)");
1297             strHTML += "<br>";
1298         }
1299         else if (nNet > 0)
1300         {
1301             //
1302             // Credit
1303             //
1304             strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1305         }
1306         else
1307         {
1308             bool fAllFromMe = true;
1309             foreach(const CTxIn& txin, wtx.vin)
1310                 fAllFromMe = fAllFromMe && txin.IsMine();
1311
1312             bool fAllToMe = true;
1313             foreach(const CTxOut& txout, wtx.vout)
1314                 fAllToMe = fAllToMe && txout.IsMine();
1315
1316             if (fAllFromMe)
1317             {
1318                 //
1319                 // Debit
1320                 //
1321                 foreach(const CTxOut& txout, wtx.vout)
1322                 {
1323                     if (txout.IsMine())
1324                         continue;
1325
1326                     if (wtx.mapValue["to"].empty())
1327                     {
1328                         // Offline transaction
1329                         uint160 hash160;
1330                         if (ExtractHash160(txout.scriptPubKey, hash160))
1331                         {
1332                             string strAddress = Hash160ToAddress(hash160);
1333                             strHTML += _("<b>To:</b> ");
1334                             if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1335                                 strHTML += mapAddressBook[strAddress] + " ";
1336                             strHTML += strAddress;
1337                             strHTML += "<br>";
1338                         }
1339                     }
1340
1341                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1342                 }
1343
1344                 if (fAllToMe)
1345                 {
1346                     // Payment to self
1347                     /// issue: can't tell which is the payment and which is the change anymore
1348                     //int64 nValue = wtx.vout[0].nValue;
1349                     //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1350                     //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1351                 }
1352
1353                 int64 nTxFee = nDebit - wtx.GetValueOut();
1354                 if (nTxFee > 0)
1355                     strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1356             }
1357             else
1358             {
1359                 //
1360                 // Mixed debit transaction
1361                 //
1362                 foreach(const CTxIn& txin, wtx.vin)
1363                     if (txin.IsMine())
1364                         strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1365                 foreach(const CTxOut& txout, wtx.vout)
1366                     if (txout.IsMine())
1367                         strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1368             }
1369         }
1370
1371         strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1372
1373
1374         //
1375         // Message
1376         //
1377         if (!wtx.mapValue["message"].empty())
1378             strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1379
1380         if (wtx.IsCoinBase())
1381             strHTML += string() + "<br>" + _("Generated coins must wait 120 blocks before they can be spent.  When you generated this block, it was broadcast to the network to be added to the block chain.  If it fails to get into the chain, it will change to \"not accepted\" and not be spendable.  This may occasionally happen if another node generates a block within a few seconds of yours.") + "<br>";
1382
1383
1384         //
1385         // Debug view
1386         //
1387         if (fDebug)
1388         {
1389             strHTML += "<hr><br>debug print<br><br>";
1390             foreach(const CTxIn& txin, wtx.vin)
1391                 if (txin.IsMine())
1392                     strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1393             foreach(const CTxOut& txout, wtx.vout)
1394                 if (txout.IsMine())
1395                     strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1396
1397             strHTML += "<b>Inputs:</b><br>";
1398             CRITICAL_BLOCK(cs_mapWallet)
1399             {
1400                 foreach(const CTxIn& txin, wtx.vin)
1401                 {
1402                     COutPoint prevout = txin.prevout;
1403                     map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1404                     if (mi != mapWallet.end())
1405                     {
1406                         const CWalletTx& prev = (*mi).second;
1407                         if (prevout.n < prev.vout.size())
1408                         {
1409                             strHTML += HtmlEscape(prev.ToString(), true);
1410                             strHTML += " &nbsp;&nbsp; " + FormatTxStatus(prev) + ", ";
1411                             strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1412                         }
1413                     }
1414                 }
1415             }
1416
1417             strHTML += "<br><hr><br><b>Transaction:</b><br>";
1418             strHTML += HtmlEscape(wtx.ToString(), true);
1419         }
1420
1421
1422
1423         strHTML += "</font></html>";
1424         string(strHTML.begin(), strHTML.end()).swap(strHTML);
1425         m_htmlWin->SetPage(strHTML);
1426         m_buttonOK->SetFocus();
1427     }
1428 }
1429
1430 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1431 {
1432     Close();
1433     //Destroy();
1434 }
1435
1436
1437
1438
1439
1440 //////////////////////////////////////////////////////////////////////////////
1441 //
1442 // COptionsDialog
1443 //
1444
1445 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1446 {
1447     // Set up list box of page choices
1448     m_listBox->Append(_("Main"));
1449     //m_listBox->Append(_("Test 2"));
1450     m_listBox->SetSelection(0);
1451     SelectPage(0);
1452 #ifdef __WXGTK__
1453     m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1454     if (!mapArgs.count("-minimizetotray"))
1455     {
1456         // Minimize to tray is just too buggy on Linux
1457         fMinimizeToTray = false;
1458         m_checkBoxMinimizeToTray->SetValue(false);
1459         m_checkBoxMinimizeToTray->Enable(false);
1460         m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1461     }
1462 #endif
1463 #ifdef __WXMAC_OSX__
1464     m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1465 #endif
1466
1467     // Init values
1468     m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1469     m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
1470     m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
1471     m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
1472     int nProcessors = wxThread::GetCPUCount();
1473     if (nProcessors < 1)
1474         nProcessors = 999;
1475     m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
1476     m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1477     m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1478     m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1479     m_checkBoxUseProxy->SetValue(fUseProxy);
1480     m_textCtrlProxyIP->Enable(fUseProxy);
1481     m_textCtrlProxyPort->Enable(fUseProxy);
1482     m_staticTextProxyIP->Enable(fUseProxy);
1483     m_staticTextProxyPort->Enable(fUseProxy);
1484     m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1485     m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1486
1487     m_buttonOK->SetFocus();
1488 }
1489
1490 void COptionsDialog::SelectPage(int nPage)
1491 {
1492     m_panelMain->Show(nPage == 0);
1493     m_panelTest2->Show(nPage == 1);
1494
1495     m_scrolledWindow->Layout();
1496     m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1497 }
1498
1499 void COptionsDialog::OnListBox(wxCommandEvent& event)
1500 {
1501     SelectPage(event.GetSelection());
1502 }
1503
1504 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1505 {
1506     event.Skip();
1507     int64 nTmp = nTransactionFee;
1508     ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1509     m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1510 }
1511
1512 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
1513 {
1514     m_spinCtrlLimitProcessors->Enable(event.IsChecked());
1515 }
1516
1517 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1518 {
1519     m_textCtrlProxyIP->Enable(event.IsChecked());
1520     m_textCtrlProxyPort->Enable(event.IsChecked());
1521     m_staticTextProxyIP->Enable(event.IsChecked());
1522     m_staticTextProxyPort->Enable(event.IsChecked());
1523 }
1524
1525 CAddress COptionsDialog::GetProxyAddr()
1526 {
1527     // Be careful about byte order, addr.ip and addr.port are big endian
1528     CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1529     if (addr.ip == INADDR_NONE)
1530         addr.ip = addrProxy.ip;
1531     int nPort = atoi(m_textCtrlProxyPort->GetValue());
1532     addr.port = htons(nPort);
1533     if (nPort <= 0 || nPort > USHRT_MAX)
1534         addr.port = addrProxy.port;
1535     return addr;
1536 }
1537
1538 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1539 {
1540     event.Skip();
1541     m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1542     m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1543 }
1544
1545
1546 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1547 {
1548     OnButtonApply(event);
1549     Close();
1550 }
1551
1552 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1553 {
1554     Close();
1555 }
1556
1557 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1558 {
1559     CWalletDB walletdb;
1560
1561     int64 nPrevTransactionFee = nTransactionFee;
1562     if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1563         walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1564
1565     int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
1566     if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
1567     {
1568         fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
1569         walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
1570     }
1571     if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
1572     {
1573         nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
1574         walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
1575     }
1576     if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
1577         GenerateBitcoins(fGenerateBitcoins);
1578
1579     if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1580     {
1581         fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1582         SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1583     }
1584
1585     if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1586     {
1587         fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1588         walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1589         ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1590     }
1591
1592     if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1593     {
1594         fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1595         walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1596     }
1597
1598     fUseProxy = m_checkBoxUseProxy->GetValue();
1599     walletdb.WriteSetting("fUseProxy", fUseProxy);
1600
1601     addrProxy = GetProxyAddr();
1602     walletdb.WriteSetting("addrProxy", addrProxy);
1603 }
1604
1605
1606
1607
1608
1609 //////////////////////////////////////////////////////////////////////////////
1610 //
1611 // CAboutDialog
1612 //
1613
1614 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1615 {
1616     m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d beta"), VERSION/10000, (VERSION/100)%100, VERSION%100));
1617
1618     // Change (c) into UTF-8 or ANSI copyright symbol
1619     wxString str = m_staticTextMain->GetLabel();
1620 #if wxUSE_UNICODE
1621     str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1622 #else
1623     str.Replace("(c)", "\xA9");
1624 #endif
1625     m_staticTextMain->SetLabel(str);
1626 #ifndef __WXMSW__
1627     // Resize on Linux to make the window fit the text.
1628     // The text was wrapped manually rather than using the Wrap setting because
1629     // the wrap would be too small on Linux and it can't be changed at this point.
1630     wxFont fontTmp = m_staticTextMain->GetFont();
1631     if (fontTmp.GetPointSize() > 8);
1632         fontTmp.SetPointSize(8);
1633     m_staticTextMain->SetFont(fontTmp);
1634     SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1635 #endif
1636 }
1637
1638 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1639 {
1640     Close();
1641 }
1642
1643
1644
1645
1646
1647
1648 //////////////////////////////////////////////////////////////////////////////
1649 //
1650 // CSendDialog
1651 //
1652
1653 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1654 {
1655     // Init
1656     m_textCtrlAddress->SetValue(strAddress);
1657     m_choiceTransferType->SetSelection(0);
1658     m_bitmapCheckMark->Show(false);
1659     fEnabledPrev = true;
1660     m_textCtrlAddress->SetFocus();
1661     //// todo: should add a display of your balance for convenience
1662 #ifndef __WXMSW__
1663     wxFont fontTmp = m_staticTextInstructions->GetFont();
1664     if (fontTmp.GetPointSize() > 9);
1665         fontTmp.SetPointSize(9);
1666     m_staticTextInstructions->SetFont(fontTmp);
1667     SetSize(725, 380);
1668 #endif
1669
1670     // Set Icon
1671     wxIcon iconSend;
1672     iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1673     SetIcon(iconSend);
1674
1675     wxCommandEvent event;
1676     OnTextAddress(event);
1677
1678     // Fixup the tab order
1679     m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1680     m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1681     this->Layout();
1682 }
1683
1684 void CSendDialog::OnTextAddress(wxCommandEvent& event)
1685 {
1686     // Check mark
1687     event.Skip();
1688     bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
1689     m_bitmapCheckMark->Show(fBitcoinAddress);
1690
1691     // Grey out message if bitcoin address
1692     bool fEnable = !fBitcoinAddress;
1693     m_staticTextFrom->Enable(fEnable);
1694     m_textCtrlFrom->Enable(fEnable);
1695     m_staticTextMessage->Enable(fEnable);
1696     m_textCtrlMessage->Enable(fEnable);
1697     m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
1698     if (!fEnable && fEnabledPrev)
1699     {
1700         strFromSave    = m_textCtrlFrom->GetValue();
1701         strMessageSave = m_textCtrlMessage->GetValue();
1702         m_textCtrlFrom->SetValue(_("Will appear as \"From: Unknown\""));
1703         m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
1704     }
1705     else if (fEnable && !fEnabledPrev)
1706     {
1707         m_textCtrlFrom->SetValue(strFromSave);
1708         m_textCtrlMessage->SetValue(strMessageSave);
1709     }
1710     fEnabledPrev = fEnable;
1711 }
1712
1713 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1714 {
1715     // Reformat the amount
1716     event.Skip();
1717     if (m_textCtrlAmount->GetValue().Trim().empty())
1718         return;
1719     int64 nTmp;
1720     if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1721         m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1722 }
1723
1724 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1725 {
1726     // Open address book
1727     CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1728     if (dialog.ShowModal())
1729         m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1730 }
1731
1732 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1733 {
1734     // Copy clipboard to address box
1735     if (wxTheClipboard->Open())
1736     {
1737         if (wxTheClipboard->IsSupported(wxDF_TEXT))
1738         {
1739             wxTextDataObject data;
1740             wxTheClipboard->GetData(data);
1741             m_textCtrlAddress->SetValue(data.GetText());
1742         }
1743         wxTheClipboard->Close();
1744     }
1745 }
1746
1747 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1748 {
1749     CWalletTx wtx;
1750     string strAddress = (string)m_textCtrlAddress->GetValue();
1751
1752     // Parse amount
1753     int64 nValue = 0;
1754     if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1755     {
1756         wxMessageBox(_("Error in amount  "), _("Send Coins"));
1757         return;
1758     }
1759     if (nValue > GetBalance())
1760     {
1761         wxMessageBox(_("Amount exceeds your balance  "), _("Send Coins"));
1762         return;
1763     }
1764     if (nValue + nTransactionFee > GetBalance())
1765     {
1766         wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included  "), _("Send Coins"));
1767         return;
1768     }
1769
1770     // Parse bitcoin address
1771     uint160 hash160;
1772     bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1773
1774     if (fBitcoinAddress)
1775     {
1776         // Send to bitcoin address
1777         CScript scriptPubKey;
1778         scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1779
1780         string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1781         if (strError == "")
1782             wxMessageBox(_("Payment sent  "), _("Sending..."));
1783         else if (strError != "ABORTED")
1784             wxMessageBox(strError + "  ", _("Sending..."));
1785     }
1786     else
1787     {
1788         // Parse IP address
1789         CAddress addr(strAddress);
1790         if (!addr.IsValid())
1791         {
1792             wxMessageBox(_("Invalid address  "), _("Send Coins"));
1793             return;
1794         }
1795
1796         // Message
1797         wtx.mapValue["to"] = strAddress;
1798         wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
1799         wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
1800
1801         // Send to IP address
1802         CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1803         if (!pdialog->ShowModal())
1804             return;
1805     }
1806
1807     CRITICAL_BLOCK(cs_mapAddressBook)
1808         if (!mapAddressBook.count(strAddress))
1809             SetAddressBookName(strAddress, "");
1810
1811     EndModal(true);
1812 }
1813
1814 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1815 {
1816     // Cancel
1817     EndModal(false);
1818 }
1819
1820
1821
1822
1823
1824
1825 //////////////////////////////////////////////////////////////////////////////
1826 //
1827 // CSendingDialog
1828 //
1829
1830 CSendingDialog::CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 nPriceIn, const CWalletTx& wtxIn) : CSendingDialogBase(NULL) // we have to give null so parent can't destroy us
1831 {
1832     addr = addrIn;
1833     nPrice = nPriceIn;
1834     wtx = wtxIn;
1835     start = wxDateTime::UNow();
1836     memset(pszStatus, 0, sizeof(pszStatus));
1837     fCanCancel = true;
1838     fAbort = false;
1839     fSuccess = false;
1840     fUIDone = false;
1841     fWorkDone = false;
1842 #ifndef __WXMSW__
1843     SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
1844 #endif
1845
1846     SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
1847     m_textCtrlStatus->SetValue("");
1848
1849     CreateThread(SendingDialogStartTransfer, this);
1850 }
1851
1852 CSendingDialog::~CSendingDialog()
1853 {
1854     printf("~CSendingDialog()\n");
1855 }
1856
1857 void CSendingDialog::Close()
1858 {
1859     // Last one out turn out the lights.
1860     // fWorkDone signals that work side is done and UI thread should call destroy.
1861     // fUIDone signals that UI window has closed and work thread should call destroy.
1862     // This allows the window to disappear and end modality when cancelled
1863     // without making the user wait for ConnectNode to return.  The dialog object
1864     // hangs around in the background until the work thread exits.
1865     if (IsModal())
1866         EndModal(fSuccess);
1867     else
1868         Show(false);
1869     if (fWorkDone)
1870         Destroy();
1871     else
1872         fUIDone = true;
1873 }
1874
1875 void CSendingDialog::OnClose(wxCloseEvent& event)
1876 {
1877     if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
1878     {
1879         Close();
1880     }
1881     else
1882     {
1883         event.Veto();
1884         wxCommandEvent cmdevent;
1885         OnButtonCancel(cmdevent);
1886     }
1887 }
1888
1889 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
1890 {
1891     if (fWorkDone)
1892         Close();
1893 }
1894
1895 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
1896 {
1897     if (fCanCancel)
1898         fAbort = true;
1899 }
1900
1901 void CSendingDialog::OnPaint(wxPaintEvent& event)
1902 {
1903     event.Skip();
1904     if (strlen(pszStatus) > 130)
1905         m_textCtrlStatus->SetValue(string("\n") + pszStatus);
1906     else
1907         m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
1908     m_staticTextSending->SetFocus();
1909     if (!fCanCancel)
1910         m_buttonCancel->Enable(false);
1911     if (fWorkDone)
1912     {
1913         m_buttonOK->Enable(true);
1914         m_buttonOK->SetFocus();
1915         m_buttonCancel->Enable(false);
1916     }
1917     if (fAbort && fCanCancel && IsShown())
1918     {
1919         strcpy(pszStatus, _("CANCELLED"));
1920         m_buttonOK->Enable(true);
1921         m_buttonOK->SetFocus();
1922         m_buttonCancel->Enable(false);
1923         m_buttonCancel->SetLabel(_("Cancelled"));
1924         Close();
1925         wxMessageBox(_("Transfer cancelled  "), _("Sending..."), wxOK, this);
1926     }
1927 }
1928
1929
1930 //
1931 // Everything from here on is not in the UI thread and must only communicate
1932 // with the rest of the dialog through variables and calling repaint.
1933 //
1934
1935 void CSendingDialog::Repaint()
1936 {
1937     Refresh();
1938     wxPaintEvent event;
1939     GetEventHandler()->AddPendingEvent(event);
1940 }
1941
1942 bool CSendingDialog::Status()
1943 {
1944     if (fUIDone)
1945     {
1946         Destroy();
1947         return false;
1948     }
1949     if (fAbort && fCanCancel)
1950     {
1951         memset(pszStatus, 0, 10);
1952         strcpy(pszStatus, _("CANCELLED"));
1953         Repaint();
1954         fWorkDone = true;
1955         return false;
1956     }
1957     return true;
1958 }
1959
1960 bool CSendingDialog::Status(const string& str)
1961 {
1962     if (!Status())
1963         return false;
1964
1965     // This can be read by the UI thread at any time,
1966     // so copy in a way that can be read cleanly at all times.
1967     memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
1968     strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
1969
1970     Repaint();
1971     return true;
1972 }
1973
1974 bool CSendingDialog::Error(const string& str)
1975 {
1976     fCanCancel = false;
1977     fWorkDone = true;
1978     Status(string(_("Error: ")) + str);
1979     return false;
1980 }
1981
1982 void SendingDialogStartTransfer(void* parg)
1983 {
1984     ((CSendingDialog*)parg)->StartTransfer();
1985 }
1986
1987 void CSendingDialog::StartTransfer()
1988 {
1989     // Make sure we have enough money
1990     if (nPrice + nTransactionFee > GetBalance())
1991     {
1992         Error(_("Insufficient funds"));
1993         return;
1994     }
1995
1996     // We may have connected already for product details
1997     if (!Status(_("Connecting...")))
1998         return;
1999     CNode* pnode = ConnectNode(addr, 15 * 60);
2000     if (!pnode)
2001     {
2002         Error(_("Unable to connect"));
2003         return;
2004     }
2005
2006     // Send order to seller, with response going to OnReply2 via event handler
2007     if (!Status(_("Requesting public key...")))
2008         return;
2009     pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2010 }
2011
2012 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2013 {
2014     ((CSendingDialog*)parg)->OnReply2(vRecv);
2015 }
2016
2017 void CSendingDialog::OnReply2(CDataStream& vRecv)
2018 {
2019     if (!Status(_("Received public key...")))
2020         return;
2021
2022     CScript scriptPubKey;
2023     int nRet;
2024     try
2025     {
2026         vRecv >> nRet;
2027         if (nRet > 0)
2028         {
2029             string strMessage;
2030             vRecv >> strMessage;
2031             Error(_("Transfer was not accepted"));
2032             //// todo: enlarge the window and enable a hidden white box to put seller's message
2033             return;
2034         }
2035         vRecv >> scriptPubKey;
2036     }
2037     catch (...)
2038     {
2039         //// what do we want to do about this?
2040         Error(_("Invalid response received"));
2041         return;
2042     }
2043
2044     // Pause to give the user a chance to cancel
2045     while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2046     {
2047         Sleep(200);
2048         if (!Status())
2049             return;
2050     }
2051
2052     CRITICAL_BLOCK(cs_main)
2053     {
2054         // Pay
2055         if (!Status(_("Creating transaction...")))
2056             return;
2057         if (nPrice + nTransactionFee > GetBalance())
2058         {
2059             Error(_("Insufficient funds"));
2060             return;
2061         }
2062         CKey key;
2063         int64 nFeeRequired;
2064         if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
2065         {
2066             if (nPrice + nFeeRequired > GetBalance())
2067                 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
2068             else
2069                 Error(_("Transaction creation failed"));
2070             return;
2071         }
2072
2073         // Transaction fee
2074         if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2075         {
2076             Error(_("Transaction aborted"));
2077             return;
2078         }
2079
2080         // Make sure we're still connected
2081         CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2082         if (!pnode)
2083         {
2084             Error(_("Lost connection, transaction cancelled"));
2085             return;
2086         }
2087
2088         // Last chance to cancel
2089         Sleep(50);
2090         if (!Status())
2091             return;
2092         fCanCancel = false;
2093         if (fAbort)
2094         {
2095             fCanCancel = true;
2096             if (!Status())
2097                 return;
2098             fCanCancel = false;
2099         }
2100         if (!Status(_("Sending payment...")))
2101             return;
2102
2103         // Commit
2104         if (!CommitTransaction(wtx, key))
2105         {
2106             Error(_("The transaction was rejected.  This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."));
2107             return;
2108         }
2109
2110         // Send payment tx to seller, with response going to OnReply3 via event handler
2111         CWalletTx wtxSend = wtx;
2112         wtxSend.fFromMe = false;
2113         pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2114
2115         Status(_("Waiting for confirmation..."));
2116         MainFrameRepaint();
2117     }
2118 }
2119
2120 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2121 {
2122     ((CSendingDialog*)parg)->OnReply3(vRecv);
2123 }
2124
2125 void CSendingDialog::OnReply3(CDataStream& vRecv)
2126 {
2127     int nRet;
2128     try
2129     {
2130         vRecv >> nRet;
2131         if (nRet > 0)
2132         {
2133             Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2134                     "The transaction is recorded and will credit to the recipient,\n"
2135                     "but the comment information will be blank."));
2136             return;
2137         }
2138     }
2139     catch (...)
2140     {
2141         //// what do we want to do about this?
2142         Error(_("Payment was sent, but an invalid response was received"));
2143         return;
2144     }
2145
2146     fSuccess = true;
2147     fWorkDone = true;
2148     Status(_("Payment completed"));
2149 }
2150
2151
2152
2153
2154
2155
2156 //////////////////////////////////////////////////////////////////////////////
2157 //
2158 // CAddressBookDialog
2159 //
2160
2161 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2162 {
2163     // Set initially selected page
2164     wxNotebookEvent event;
2165     event.SetSelection(nPageIn);
2166     OnNotebookPageChanged(event);
2167     m_notebook->ChangeSelection(nPageIn);
2168
2169     fDuringSend = fDuringSendIn;
2170     if (!fDuringSend)
2171         m_buttonCancel->Show(false);
2172
2173     // Set Icon
2174     wxIcon iconAddressBook;
2175     iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2176     SetIcon(iconAddressBook);
2177
2178     // Init column headers
2179     m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2180     m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2181     m_listCtrlSending->SetFocus();
2182     m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2183     m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2184     m_listCtrlReceiving->SetFocus();
2185
2186     // Fill listctrl with address book data
2187     CRITICAL_BLOCK(cs_mapKeys)
2188     CRITICAL_BLOCK(cs_mapAddressBook)
2189     {
2190         string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2191         foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2192         {
2193             string strAddress = item.first;
2194             string strName = item.second;
2195             uint160 hash160;
2196             bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2197             wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2198             int nIndex = InsertLine(plistCtrl, strName, strAddress);
2199             if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2200                 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2201         }
2202     }
2203 }
2204
2205 wxString CAddressBookDialog::GetSelectedAddress()
2206 {
2207     int nIndex = GetSelection(m_listCtrl);
2208     if (nIndex == -1)
2209         return "";
2210     return GetItemText(m_listCtrl, nIndex, 1);
2211 }
2212
2213 wxString CAddressBookDialog::GetSelectedSendingAddress()
2214 {
2215     int nIndex = GetSelection(m_listCtrlSending);
2216     if (nIndex == -1)
2217         return "";
2218     return GetItemText(m_listCtrlSending, nIndex, 1);
2219 }
2220
2221 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2222 {
2223     int nIndex = GetSelection(m_listCtrlReceiving);
2224     if (nIndex == -1)
2225         return "";
2226     return GetItemText(m_listCtrlReceiving, nIndex, 1);
2227 }
2228
2229 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2230 {
2231     event.Skip();
2232     nPage = event.GetSelection();
2233     if (nPage == SENDING)
2234         m_listCtrl = m_listCtrlSending;
2235     else if (nPage == RECEIVING)
2236         m_listCtrl = m_listCtrlReceiving;
2237     m_buttonDelete->Show(nPage == SENDING);
2238     m_buttonCopy->Show(nPage == RECEIVING);
2239     this->Layout();
2240     m_listCtrl->SetFocus();
2241 }
2242
2243 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2244 {
2245     // Update address book with edited name
2246     event.Skip();
2247     if (event.IsEditCancelled())
2248         return;
2249     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2250     SetAddressBookName(strAddress, string(event.GetText()));
2251     pframeMain->RefreshListCtrl();
2252 }
2253
2254 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2255 {
2256     event.Skip();
2257     if (nPage == RECEIVING)
2258         SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2259 }
2260
2261 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2262 {
2263     event.Skip();
2264     if (fDuringSend)
2265     {
2266         // Doubleclick returns selection
2267         EndModal(GetSelectedAddress() != "" ? 2 : 0);
2268         return;
2269     }
2270
2271     // Doubleclick edits item
2272     wxCommandEvent event2;
2273     OnButtonEdit(event2);
2274 }
2275
2276 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2277 {
2278     if (nPage != SENDING)
2279         return;
2280     for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2281     {
2282         if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2283         {
2284             string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2285             CWalletDB().EraseName(strAddress);
2286             m_listCtrl->DeleteItem(nIndex);
2287         }
2288     }
2289     pframeMain->RefreshListCtrl();
2290 }
2291
2292 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2293 {
2294     // Copy address box to clipboard
2295     if (wxTheClipboard->Open())
2296     {
2297         wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2298         wxTheClipboard->Close();
2299     }
2300 }
2301
2302 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2303 {
2304     uint160 hash160;
2305     bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2306     if (fMine)
2307         wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book.  "), strTitle);
2308     return fMine;
2309 }
2310
2311 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2312 {
2313     int nIndex = GetSelection(m_listCtrl);
2314     if (nIndex == -1)
2315         return;
2316     string strName = (string)m_listCtrl->GetItemText(nIndex);
2317     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2318     string strAddressOrg = strAddress;
2319
2320     if (nPage == SENDING)
2321     {
2322         // Ask name and address
2323         do
2324         {
2325             CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2326             if (!dialog.ShowModal())
2327                 return;
2328             strName = dialog.GetValue1();
2329             strAddress = dialog.GetValue2();
2330         }
2331         while (CheckIfMine(strAddress, _("Edit Address")));
2332
2333     }
2334     else if (nPage == RECEIVING)
2335     {
2336         // Ask name
2337         CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2338         if (!dialog.ShowModal())
2339             return;
2340         strName = dialog.GetValue();
2341     }
2342
2343     // Write back
2344     if (strAddress != strAddressOrg)
2345         CWalletDB().EraseName(strAddressOrg);
2346     SetAddressBookName(strAddress, strName);
2347     m_listCtrl->SetItem(nIndex, 1, strAddress);
2348     m_listCtrl->SetItemText(nIndex, strName);
2349     pframeMain->RefreshListCtrl();
2350 }
2351
2352 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2353 {
2354     string strName;
2355     string strAddress;
2356
2357     if (nPage == SENDING)
2358     {
2359         // Ask name and address
2360         do
2361         {
2362             CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2363             if (!dialog.ShowModal())
2364                 return;
2365             strName = dialog.GetValue1();
2366             strAddress = dialog.GetValue2();
2367         }
2368         while (CheckIfMine(strAddress, _("Add Address")));
2369     }
2370     else if (nPage == RECEIVING)
2371     {
2372         // Ask name
2373         CGetTextFromUserDialog dialog(this,
2374             _("New Receiving Address"),
2375             _("You should use a new address for each payment you receive.\n\nLabel"),
2376             "");
2377         if (!dialog.ShowModal())
2378             return;
2379         strName = dialog.GetValue();
2380
2381         // Generate new key
2382         strAddress = PubKeyToAddress(GenerateNewKey());
2383     }
2384
2385     // Add to list and select it
2386     SetAddressBookName(strAddress, strName);
2387     int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2388     SetSelection(m_listCtrl, nIndex);
2389     m_listCtrl->SetFocus();
2390     if (nPage == SENDING)
2391         pframeMain->RefreshListCtrl();
2392 }
2393
2394 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2395 {
2396     // OK
2397     EndModal(GetSelectedAddress() != "" ? 1 : 0);
2398 }
2399
2400 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2401 {
2402     // Cancel
2403     EndModal(0);
2404 }
2405
2406 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2407 {
2408     // Close
2409     EndModal(0);
2410 }
2411
2412
2413
2414
2415
2416
2417 //////////////////////////////////////////////////////////////////////////////
2418 //
2419 // CMyTaskBarIcon
2420 //
2421
2422 enum
2423 {
2424     ID_TASKBAR_RESTORE = 10001,
2425     ID_TASKBAR_OPTIONS,
2426     ID_TASKBAR_GENERATE,
2427     ID_TASKBAR_EXIT,
2428 };
2429
2430 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2431     EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2432     EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2433     EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2434     EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
2435     EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2436     EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2437 END_EVENT_TABLE()
2438
2439 void CMyTaskBarIcon::Show(bool fShow)
2440 {
2441     static char pszPrevTip[200];
2442     if (fShow)
2443     {
2444         string strTooltip = _("Bitcoin");
2445         if (fGenerateBitcoins)
2446             strTooltip = _("Bitcoin - Generating");
2447         if (fGenerateBitcoins && vNodes.empty())
2448             strTooltip = _("Bitcoin - (not connected)");
2449
2450         // Optimization, only update when changed, using char array to be reentrant
2451         if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2452         {
2453             strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2454 #ifdef __WXMSW__
2455             // somehow it'll choose the wrong size and scale it down if
2456             // we use the main icon, so we hand it one with only 16x16
2457             SetIcon(wxICON(favicon), strTooltip);
2458 #else
2459             SetIcon(bitcoin80_xpm, strTooltip);
2460 #endif
2461         }
2462     }
2463     else
2464     {
2465         strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2466         RemoveIcon();
2467     }
2468 }
2469
2470 void CMyTaskBarIcon::Hide()
2471 {
2472     Show(false);
2473 }
2474
2475 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2476 {
2477     Restore();
2478 }
2479
2480 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2481 {
2482     Restore();
2483 }
2484
2485 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2486 {
2487     // Since it's modal, get the main window to do it
2488     wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2489     pframeMain->GetEventHandler()->AddPendingEvent(event2);
2490 }
2491
2492 void CMyTaskBarIcon::Restore()
2493 {
2494     pframeMain->Show();
2495     wxIconizeEvent event(0, false);
2496     pframeMain->GetEventHandler()->AddPendingEvent(event);
2497     pframeMain->Iconize(false);
2498     pframeMain->Raise();
2499 }
2500
2501 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
2502 {
2503     GenerateBitcoins(event.IsChecked());
2504 }
2505
2506 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2507 {
2508     event.Check(fGenerateBitcoins);
2509 }
2510
2511 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2512 {
2513     pframeMain->Close(true);
2514 }
2515
2516 void CMyTaskBarIcon::UpdateTooltip()
2517 {
2518     if (IsIconInstalled())
2519         Show(true);
2520 }
2521
2522 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2523 {
2524     wxMenu* pmenu = new wxMenu;
2525     pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2526     pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2527     pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
2528 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2529     pmenu->AppendSeparator();
2530     pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2531 #endif
2532     return pmenu;
2533 }
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547 void CreateMainWindow()
2548 {
2549     pframeMain = new CMainFrame(NULL);
2550     if (mapArgs.count("-min"))
2551         pframeMain->Iconize(true);
2552 #ifdef __WXGTK__
2553     if (!mapArgs.count("-minimizetotray"))
2554         fMinimizeToTray = false;
2555 #endif
2556     pframeMain->Show(true);  // have to show first to get taskbar button to hide
2557     if (fMinimizeToTray && pframeMain->IsIconized())
2558         fClosedToTray = true;
2559     pframeMain->Show(!fClosedToTray);
2560     ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2561     CreateThread(ThreadDelayedRepaint, NULL);
2562 }