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