get rid of mapPubKeys
[novacoin.git] / src / 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 #include "db.h"
7 #include "init.h"
8 #include "strlcpy.h"
9 #include <boost/filesystem/fstream.hpp>
10 #include <boost/filesystem/convenience.hpp>
11 #ifdef _MSC_VER
12 #include <crtdbg.h>
13 #endif
14
15 using namespace std;
16 using namespace boost;
17
18
19 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
20
21 CMainFrame* pframeMain = NULL;
22 CMyTaskBarIcon* ptaskbaricon = NULL;
23 bool fClosedToTray = false;
24 wxLocale g_locale;
25
26 #ifdef __WXMSW__
27 double nScaleX = 1.0;
28 double nScaleY = 1.0;
29 #else
30 static const double nScaleX = 1.0;
31 static const double nScaleY = 1.0;
32 #endif
33
34
35
36
37
38
39
40
41 //////////////////////////////////////////////////////////////////////////////
42 //
43 // Util
44 //
45
46 void HandleCtrlA(wxKeyEvent& event)
47 {
48     // Ctrl-a select all
49     event.Skip();
50     wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
51     if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
52         textCtrl->SetSelection(-1, -1);
53 }
54
55 bool Is24HourTime()
56 {
57     //char pszHourFormat[256];
58     //pszHourFormat[0] = '\0';
59     //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
60     //return (pszHourFormat[0] != '0');
61     return true;
62 }
63
64 string DateStr(int64 nTime)
65 {
66     // Can only be used safely here in the UI
67     return (string)wxDateTime((time_t)nTime).FormatDate();
68 }
69
70 string DateTimeStr(int64 nTime)
71 {
72     // Can only be used safely here in the UI
73     wxDateTime datetime((time_t)nTime);
74     if (Is24HourTime())
75         return (string)datetime.Format("%x %H:%M");
76     else
77         return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
78 }
79
80 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
81 {
82     // Helper to simplify access to listctrl
83     wxListItem item;
84     item.m_itemId = nIndex;
85     item.m_col = nColumn;
86     item.m_mask = wxLIST_MASK_TEXT;
87     if (!listCtrl->GetItem(item))
88         return "";
89     return item.GetText();
90 }
91
92 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
93 {
94     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
95     listCtrl->SetItem(nIndex, 1, str1);
96     return nIndex;
97 }
98
99 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
100 {
101     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
102     listCtrl->SetItem(nIndex, 1, str1);
103     listCtrl->SetItem(nIndex, 2, str2);
104     listCtrl->SetItem(nIndex, 3, str3);
105     listCtrl->SetItem(nIndex, 4, str4);
106     return nIndex;
107 }
108
109 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
110 {
111     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
112     listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
113     listCtrl->SetItem(nIndex, 1, str1);
114     listCtrl->SetItem(nIndex, 2, str2);
115     listCtrl->SetItem(nIndex, 3, str3);
116     listCtrl->SetItem(nIndex, 4, str4);
117     return nIndex;
118 }
119
120 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
121 {
122     // Repaint on Windows is more flickery if the colour has ever been set,
123     // so don't want to set it unless it's different.  Default colour has
124     // alpha 0 transparent, so our colours don't match using operator==.
125     wxColour c1 = listCtrl->GetItemTextColour(nIndex);
126     if (!c1.IsOk())
127         c1 = wxColour(0,0,0);
128     if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
129         listCtrl->SetItemTextColour(nIndex, colour);
130 }
131
132 void SetSelection(wxListCtrl* listCtrl, int nIndex)
133 {
134     int nSize = listCtrl->GetItemCount();
135     long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
136     for (int i = 0; i < nSize; i++)
137         listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
138 }
139
140 int GetSelection(wxListCtrl* listCtrl)
141 {
142     int nSize = listCtrl->GetItemCount();
143     for (int i = 0; i < nSize; i++)
144         if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
145             return i;
146     return -1;
147 }
148
149 string HtmlEscape(const char* psz, bool fMultiLine=false)
150 {
151     int len = 0;
152     for (const char* p = psz; *p; p++)
153     {
154              if (*p == '<') len += 4;
155         else if (*p == '>') len += 4;
156         else if (*p == '&') len += 5;
157         else if (*p == '"') len += 6;
158         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
159         else if (*p == '\n' && fMultiLine) len += 5;
160         else
161             len++;
162     }
163     string str;
164     str.reserve(len);
165     for (const char* p = psz; *p; p++)
166     {
167              if (*p == '<') str += "&lt;";
168         else if (*p == '>') str += "&gt;";
169         else if (*p == '&') str += "&amp;";
170         else if (*p == '"') str += "&quot;";
171         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += "&nbsp;";
172         else if (*p == '\n' && fMultiLine) str += "<br>\n";
173         else
174             str += *p;
175     }
176     return str;
177 }
178
179 string HtmlEscape(const string& str, bool fMultiLine=false)
180 {
181     return HtmlEscape(str.c_str(), fMultiLine);
182 }
183
184 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
185 {
186     *pnRet = wxMessageBox(message, caption, style, parent, x, y);
187     *pfDone = true;
188 }
189
190 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
191 {
192 #ifdef __WXMSW__
193     return wxMessageBox(message, caption, style, parent, x, y);
194 #else
195     if (wxThread::IsMain() || fDaemon)
196     {
197         return wxMessageBox(message, caption, style, parent, x, y);
198     }
199     else
200     {
201         int nRet = 0;
202         bool fDone = false;
203         UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
204         while (!fDone)
205             Sleep(100);
206         return nRet;
207     }
208 #endif
209 }
210
211 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
212 {
213     if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
214         return true;
215     string strMessage = strprintf(
216         _("This transaction is over the size limit.  You can still send it for a fee of %s, "
217           "which goes to the nodes that process your transaction and helps to support the network.  "
218           "Do you want to pay the fee?"),
219         FormatMoney(nFeeRequired).c_str());
220     return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
221 }
222
223 void CalledSetStatusBar(const string& strText, int nField)
224 {
225     if (nField == 0 && GetWarnings("statusbar") != "")
226         return;
227     if (pframeMain && pframeMain->m_statusBar)
228         pframeMain->m_statusBar->SetStatusText(strText, nField);
229 }
230
231 void SetDefaultReceivingAddress(const string& strAddress)
232 {
233     // Update main window address and database
234     if (pframeMain == NULL)
235         return;
236     if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
237     {
238         uint160 hash160;
239         if (!AddressToHash160(strAddress, hash160))
240             return;
241         vector<unsigned char> vchPubKey;
242         if (!pwalletMain->GetPubKey(hash160, vchPubKey))
243             return;
244         pwalletMain->SetDefaultKey(vchPubKey);
245         pframeMain->m_textCtrlAddress->SetValue(strAddress);
246     }
247 }
248
249 bool GetWalletPassphrase()
250 {
251     if (pwalletMain->IsLocked())
252     {
253         string strWalletPass;
254         strWalletPass.reserve(100);
255         mlock(&strWalletPass[0], strWalletPass.capacity());
256
257         // obtain current wallet encrypt/decrypt key, from passphrase
258         // Note that the passphrase is not mlock()d during this entry and could potentially
259         // be obtained from disk long after bitcoin has run.
260         strWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
261                                               _("Passphrase")).ToStdString();
262
263         if (!strWalletPass.size())
264         {
265             fill(strWalletPass.begin(), strWalletPass.end(), '\0');
266             munlock(&strWalletPass[0], strWalletPass.capacity());
267             wxMessageBox(_("Please supply the current wallet decryption passphrase."), "Bitcoin");
268             return false;
269         }
270
271         if (!pwalletMain->Unlock(strWalletPass))
272         {
273             fill(strWalletPass.begin(), strWalletPass.end(), '\0');
274             munlock(&strWalletPass[0], strWalletPass.capacity());
275             wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin");
276             return false;
277         }
278         fill(strWalletPass.begin(), strWalletPass.end(), '\0');
279         munlock(&strWalletPass[0], strWalletPass.capacity());
280     }
281     return true;
282 }
283
284
285
286
287
288
289
290
291
292
293 //////////////////////////////////////////////////////////////////////////////
294 //
295 // CMainFrame
296 //
297
298 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
299 {
300     Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
301
302     // Set initially selected page
303     wxNotebookEvent event;
304     event.SetSelection(0);
305     OnNotebookPageChanged(event);
306     m_notebook->ChangeSelection(0);
307
308     // Init
309     fRefreshListCtrl = false;
310     fRefreshListCtrlRunning = false;
311     fOnSetFocusAddress = false;
312     fRefresh = false;
313     m_choiceFilter->SetSelection(0);
314     double dResize = nScaleX;
315 #ifdef __WXMSW__
316     SetIcon(wxICON(bitcoin));
317     SetSize(dResize * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
318 #else
319     SetIcon(bitcoin80_xpm);
320     SetBackgroundColour(m_toolBar->GetBackgroundColour());
321     wxFont fontTmp = m_staticText41->GetFont();
322     fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
323     m_staticTextBalance->SetFont(fontTmp);
324     m_staticTextBalance->SetSize(140, 17);
325     // resize to fit ubuntu's huge default font
326     dResize = 1.22;
327     SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
328 #endif
329     m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + "  ");
330     m_listCtrl->SetFocus();
331     ptaskbaricon = new CMyTaskBarIcon();
332 #ifdef __WXMAC_OSX__
333     // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
334     // to their standard places, leaving these menus empty.
335     GetMenuBar()->Remove(2); // remove Help menu
336     GetMenuBar()->Remove(0); // remove File menu
337 #endif
338
339     // Init column headers
340     int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
341     if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
342         nDateWidth += 12;
343 #ifdef __WXMAC_OSX__
344     nDateWidth += 5;
345     dResize -= 0.01;
346 #endif
347     wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
348     BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
349     {
350         p->InsertColumn(0, "",               wxLIST_FORMAT_LEFT,  dResize * 0);
351         p->InsertColumn(1, "",               wxLIST_FORMAT_LEFT,  dResize * 0);
352         p->InsertColumn(2, _("Status"),      wxLIST_FORMAT_LEFT,  dResize * 112);
353         p->InsertColumn(3, _("Date"),        wxLIST_FORMAT_LEFT,  dResize * nDateWidth);
354         p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT,  dResize * 409 - nDateWidth);
355         p->InsertColumn(5, _("Debit"),       wxLIST_FORMAT_RIGHT, dResize * 79);
356         p->InsertColumn(6, _("Credit"),      wxLIST_FORMAT_RIGHT, dResize * 79);
357     }
358
359     // Init status bar
360     int pnWidths[3] = { -100, 88, 300 };
361 #ifndef __WXMSW__
362     pnWidths[1] = pnWidths[1] * 1.1 * dResize;
363     pnWidths[2] = pnWidths[2] * 1.1 * dResize;
364 #endif
365     m_statusBar->SetFieldsCount(3, pnWidths);
366
367     // Fill your address text box
368     vector<unsigned char> vchPubKey;
369     if (CWalletDB(pwalletMain->strWalletFile,"r").ReadDefaultKey(vchPubKey))
370         m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
371
372     if (pwalletMain->IsCrypted())
373         m_menuOptions->Remove(m_menuOptionsEncryptWallet);
374     else
375         m_menuOptions->Remove(m_menuOptionsChangeWalletPassphrase);
376
377     // Fill listctrl with wallet transactions
378     RefreshListCtrl();
379 }
380
381 CMainFrame::~CMainFrame()
382 {
383     pframeMain = NULL;
384     delete ptaskbaricon;
385     ptaskbaricon = NULL;
386 }
387
388 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
389 {
390     event.Skip();
391     nPage = event.GetSelection();
392     if (nPage == ALL)
393     {
394         m_listCtrl = m_listCtrlAll;
395         fShowGenerated = true;
396         fShowSent = true;
397         fShowReceived = true;
398     }
399     else if (nPage == SENTRECEIVED)
400     {
401         m_listCtrl = m_listCtrlSentReceived;
402         fShowGenerated = false;
403         fShowSent = true;
404         fShowReceived = true;
405     }
406     else if (nPage == SENT)
407     {
408         m_listCtrl = m_listCtrlSent;
409         fShowGenerated = false;
410         fShowSent = true;
411         fShowReceived = false;
412     }
413     else if (nPage == RECEIVED)
414     {
415         m_listCtrl = m_listCtrlReceived;
416         fShowGenerated = false;
417         fShowSent = false;
418         fShowReceived = true;
419     }
420     RefreshListCtrl();
421     m_listCtrl->SetFocus();
422 }
423
424 void CMainFrame::OnClose(wxCloseEvent& event)
425 {
426     if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
427     {
428         // Divert close to minimize
429         event.Veto();
430         fClosedToTray = true;
431         Iconize(true);
432     }
433     else
434     {
435         Destroy();
436         CreateThread(Shutdown, NULL);
437     }
438 }
439
440 void CMainFrame::OnIconize(wxIconizeEvent& event)
441 {
442     event.Skip();
443     // Hide the task bar button when minimized.
444     // Event is sent when the frame is minimized or restored.
445     // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
446     // to get rid of the deprecated warning.  Just ignore it.
447     if (!event.Iconized())
448         fClosedToTray = false;
449 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
450     if (GetBoolArg("-minimizetotray")) {
451 #endif
452     // The tray icon sometimes disappears on ubuntu karmic
453     // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
454     // Reports of CPU peg on 64-bit linux
455     if (fMinimizeToTray && event.Iconized())
456         fClosedToTray = true;
457     Show(!fClosedToTray);
458     ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
459 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
460     }
461 #endif
462 }
463
464 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
465 {
466     event.Skip();
467     RandAddSeed();
468     RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
469     RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
470 }
471
472 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
473 {
474     // Hidden columns not resizeable
475     if (event.GetColumn() <= 1 && !fDebug)
476         event.Veto();
477     else
478         event.Skip();
479 }
480
481 int CMainFrame::GetSortIndex(const string& strSort)
482 {
483 #ifdef __WXMSW__
484     return 0;
485 #else
486     // The wx generic listctrl implementation used on GTK doesn't sort,
487     // so we have to do it ourselves.  Remember, we sort in reverse order.
488     // In the wx generic implementation, they store the list of items
489     // in a vector, so indexed lookups are fast, but inserts are slower
490     // the closer they are to the top.
491     int low = 0;
492     int high = m_listCtrl->GetItemCount();
493     while (low < high)
494     {
495         int mid = low + ((high - low) / 2);
496         if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
497             high = mid;
498         else
499             low = mid + 1;
500     }
501     return low;
502 #endif
503 }
504
505 void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxColour& colour, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6)
506 {
507     strSort = " " + strSort;       // leading space to workaround wx2.9.0 ubuntu 9.10 bug
508     long nData = *(long*)&hashKey; //  where first char of hidden column is displayed
509
510     // Find item
511     if (!fNew && nIndex == -1)
512     {
513         string strHash = " " + hashKey.ToString();
514         while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
515             if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
516                 break;
517     }
518
519     // fNew is for blind insert, only use if you're sure it's new
520     if (fNew || nIndex == -1)
521     {
522         nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
523     }
524     else
525     {
526         // If sort key changed, must delete and reinsert to make it relocate
527         if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
528         {
529             m_listCtrl->DeleteItem(nIndex);
530             nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
531         }
532     }
533
534     m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
535     m_listCtrl->SetItem(nIndex, 2, str2);
536     m_listCtrl->SetItem(nIndex, 3, str3);
537     m_listCtrl->SetItem(nIndex, 4, str4);
538     m_listCtrl->SetItem(nIndex, 5, str5);
539     m_listCtrl->SetItem(nIndex, 6, str6);
540     m_listCtrl->SetItemData(nIndex, nData);
541     SetItemTextColour(m_listCtrl, nIndex, colour);
542 }
543
544 bool CMainFrame::DeleteLine(uint256 hashKey)
545 {
546     long nData = *(long*)&hashKey;
547
548     // Find item
549     int nIndex = -1;
550     string strHash = " " + hashKey.ToString();
551     while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
552         if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
553             break;
554
555     if (nIndex != -1)
556         m_listCtrl->DeleteItem(nIndex);
557
558     return nIndex != -1;
559 }
560
561 string FormatTxStatus(const CWalletTx& wtx)
562 {
563     // Status
564     if (!wtx.IsFinal())
565     {
566         if (wtx.nLockTime < LOCKTIME_THRESHOLD)
567             return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
568         else
569             return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
570     }
571     else
572     {
573         int nDepth = wtx.GetDepthInMainChain();
574         if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
575             return strprintf(_("%d/offline?"), nDepth);
576         else if (nDepth < 6)
577             return strprintf(_("%d/unconfirmed"), nDepth);
578         else
579             return strprintf(_("%d confirmations"), nDepth);
580     }
581 }
582
583 string SingleLine(const string& strIn)
584 {
585     string strOut;
586     bool fOneSpace = false;
587     BOOST_FOREACH(unsigned char c, strIn)
588     {
589         if (isspace(c))
590         {
591             fOneSpace = true;
592         }
593         else if (c > ' ')
594         {
595             if (fOneSpace && !strOut.empty())
596                 strOut += ' ';
597             strOut += c;
598             fOneSpace = false;
599         }
600     }
601     return strOut;
602 }
603
604 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
605 {
606     int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
607     int64 nCredit = wtx.GetCredit(true);
608     int64 nDebit = wtx.GetDebit();
609     int64 nNet = nCredit - nDebit;
610     uint256 hash = wtx.GetHash();
611     string strStatus = FormatTxStatus(wtx);
612     bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
613     wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
614     map<string, string> mapValue = wtx.mapValue;
615     wtx.nLinesDisplayed = 1;
616     nListViewUpdated++;
617
618     // Filter
619     if (wtx.IsCoinBase())
620     {
621         // Don't show generated coin until confirmed by at least one block after it
622         // so we don't get the user's hopes up until it looks like it's probably accepted.
623         //
624         // It is not an error when generated blocks are not accepted.  By design,
625         // some percentage of blocks, like 10% or more, will end up not accepted.
626         // This is the normal mechanism by which the network copes with latency.
627         //
628         // We display regular transactions right away before any confirmation
629         // because they can always get into some block eventually.  Generated coins
630         // are special because if their block is not accepted, they are not valid.
631         //
632         if (wtx.GetDepthInMainChain() < 2)
633         {
634             wtx.nLinesDisplayed = 0;
635             return false;
636         }
637
638         if (!fShowGenerated)
639             return false;
640     }
641
642     // Find the block the tx is in
643     CBlockIndex* pindex = NULL;
644     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
645     if (mi != mapBlockIndex.end())
646         pindex = (*mi).second;
647
648     // Sort order, unrecorded transactions sort to the top
649     string strSort = strprintf("%010d-%01d-%010u",
650         (pindex ? pindex->nHeight : INT_MAX),
651         (wtx.IsCoinBase() ? 1 : 0),
652         wtx.nTimeReceived);
653
654     // Insert line
655     if (nNet > 0 || wtx.IsCoinBase())
656     {
657         //
658         // Credit
659         //
660         string strDescription;
661         if (wtx.IsCoinBase())
662         {
663             // Generated
664             strDescription = _("Generated");
665             if (nCredit == 0)
666             {
667                 int64 nUnmatured = 0;
668                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
669                     nUnmatured += pwalletMain->GetCredit(txout);
670                 if (wtx.IsInMainChain())
671                 {
672                     strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
673
674                     // Check if the block was requested by anyone
675                     if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
676                         strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
677                 }
678                 else
679                 {
680                     strDescription = _("Generated (not accepted)");
681                 }
682             }
683         }
684         else if (!mapValue["from"].empty() || !mapValue["message"].empty())
685         {
686             // Received by IP connection
687             if (!fShowReceived)
688                 return false;
689             if (!mapValue["from"].empty())
690                 strDescription += _("From: ") + mapValue["from"];
691             if (!mapValue["message"].empty())
692             {
693                 if (!strDescription.empty())
694                     strDescription += " - ";
695                 strDescription += mapValue["message"];
696             }
697         }
698         else
699         {
700             // Received by Bitcoin Address
701             if (!fShowReceived)
702                 return false;
703             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
704             {
705                 if (pwalletMain->IsMine(txout))
706                 {
707                     uint160 hash160;
708                     if (ExtractHash160(txout.scriptPubKey, pwalletMain, hash160))
709                     {
710                         CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
711                         {
712                             //strDescription += _("Received payment to ");
713                             //strDescription += _("Received with address ");
714                             strDescription += _("Received with: ");
715                             string strAddress = Hash160ToAddress(hash160);
716                             map<string, string>::iterator mi = pwalletMain->mapAddressBook.find(strAddress);
717                             if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
718                             {
719                                 string strLabel = (*mi).second;
720                                 strDescription += strAddress.substr(0,12) + "... ";
721                                 strDescription += "(" + strLabel + ")";
722                             }
723                             else
724                                 strDescription += strAddress;
725                         }
726                     }
727                     break;
728                 }
729             }
730         }
731
732         string strCredit = FormatMoney(nNet, true);
733         if (!fConfirmed)
734             strCredit = "[" + strCredit + "]";
735
736         InsertLine(fNew, nIndex, hash, strSort, colour,
737                    strStatus,
738                    nTime ? DateTimeStr(nTime) : "",
739                    SingleLine(strDescription),
740                    "",
741                    strCredit);
742     }
743     else
744     {
745         bool fAllFromMe = true;
746         BOOST_FOREACH(const CTxIn& txin, wtx.vin)
747             fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
748
749         bool fAllToMe = true;
750         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
751             fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
752
753         if (fAllFromMe && fAllToMe)
754         {
755             // Payment to self
756             int64 nChange = wtx.GetChange();
757             InsertLine(fNew, nIndex, hash, strSort, colour,
758                        strStatus,
759                        nTime ? DateTimeStr(nTime) : "",
760                        _("Payment to yourself"),
761                        FormatMoney(-(nDebit - nChange), true),
762                        FormatMoney(nCredit - nChange, true));
763         }
764         else if (fAllFromMe)
765         {
766             //
767             // Debit
768             //
769             if (!fShowSent)
770                 return false;
771
772             int64 nTxFee = nDebit - wtx.GetValueOut();
773             wtx.nLinesDisplayed = 0;
774             for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
775             {
776                 const CTxOut& txout = wtx.vout[nOut];
777                 if (pwalletMain->IsMine(txout))
778                     continue;
779
780                 string strAddress;
781                 if (!mapValue["to"].empty())
782                 {
783                     // Sent to IP
784                     strAddress = mapValue["to"];
785                 }
786                 else
787                 {
788                     // Sent to Bitcoin Address
789                     uint160 hash160;
790                     if (ExtractHash160(txout.scriptPubKey, pwalletMain, hash160))
791                         strAddress = Hash160ToAddress(hash160);
792                 }
793
794                 string strDescription = _("To: ");
795                 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
796                     if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
797                         strDescription += pwalletMain->mapAddressBook[strAddress] + " ";
798                 strDescription += strAddress;
799                 if (!mapValue["message"].empty())
800                 {
801                     if (!strDescription.empty())
802                         strDescription += " - ";
803                     strDescription += mapValue["message"];
804                 }
805                 else if (!mapValue["comment"].empty())
806                 {
807                     if (!strDescription.empty())
808                         strDescription += " - ";
809                     strDescription += mapValue["comment"];
810                 }
811
812                 int64 nValue = txout.nValue;
813                 if (nTxFee > 0)
814                 {
815                     nValue += nTxFee;
816                     nTxFee = 0;
817                 }
818
819                 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
820                            strStatus,
821                            nTime ? DateTimeStr(nTime) : "",
822                            SingleLine(strDescription),
823                            FormatMoney(-nValue, true),
824                            "");
825                 nIndex = -1;
826                 wtx.nLinesDisplayed++;
827             }
828         }
829         else
830         {
831             //
832             // Mixed debit transaction, can't break down payees
833             //
834             bool fAllMine = true;
835             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
836                 fAllMine = fAllMine && pwalletMain->IsMine(txout);
837             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
838                 fAllMine = fAllMine && pwalletMain->IsMine(txin);
839
840             InsertLine(fNew, nIndex, hash, strSort, colour,
841                        strStatus,
842                        nTime ? DateTimeStr(nTime) : "",
843                        "",
844                        FormatMoney(nNet, true),
845                        "");
846         }
847     }
848
849     return true;
850 }
851
852 void CMainFrame::RefreshListCtrl()
853 {
854     fRefreshListCtrl = true;
855     ::wxWakeUpIdle();
856 }
857
858 void CMainFrame::OnIdle(wxIdleEvent& event)
859 {
860     if (fRefreshListCtrl)
861     {
862         // Collect list of wallet transactions and sort newest first
863         bool fEntered = false;
864         vector<pair<unsigned int, uint256> > vSorted;
865         TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
866         {
867             printf("RefreshListCtrl starting\n");
868             fEntered = true;
869             fRefreshListCtrl = false;
870             pwalletMain->vWalletUpdated.clear();
871
872             // Do the newest transactions first
873             vSorted.reserve(pwalletMain->mapWallet.size());
874             for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
875             {
876                 const CWalletTx& wtx = (*it).second;
877                 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
878                 vSorted.push_back(make_pair(nTime, (*it).first));
879             }
880             m_listCtrl->DeleteAllItems();
881         }
882         if (!fEntered)
883             return;
884
885         sort(vSorted.begin(), vSorted.end());
886
887         // Fill list control
888         for (int i = 0; i < vSorted.size();)
889         {
890             if (fShutdown)
891                 return;
892             bool fEntered = false;
893             TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
894             {
895                 fEntered = true;
896                 uint256& hash = vSorted[i++].second;
897                 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
898                 if (mi != pwalletMain->mapWallet.end())
899                     InsertTransaction((*mi).second, true);
900             }
901             if (!fEntered || i == 100 || i % 500 == 0)
902                 wxYield();
903         }
904
905         printf("RefreshListCtrl done\n");
906
907         // Update transaction total display
908         MainFrameRepaint();
909     }
910     else
911     {
912         // Check for time updates
913         static int64 nLastTime;
914         if (GetTime() > nLastTime + 30)
915         {
916             TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
917             {
918                 nLastTime = GetTime();
919                 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
920                 {
921                     CWalletTx& wtx = (*it).second;
922                     if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
923                         InsertTransaction(wtx, false);
924                 }
925             }
926         }
927     }
928 }
929
930 void CMainFrame::RefreshStatusColumn()
931 {
932     static int nLastTop;
933     static CBlockIndex* pindexLastBest;
934     static unsigned int nLastRefreshed;
935
936     int nTop = max((int)m_listCtrl->GetTopItem(), 0);
937     if (nTop == nLastTop && pindexLastBest == pindexBest)
938         return;
939
940     TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
941     {
942         int nStart = nTop;
943         int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
944
945         if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
946         {
947             // If no updates, only need to do the part that moved onto the screen
948             if (nStart >= nLastTop && nStart < nLastTop + 100)
949                 nStart = nLastTop + 100;
950             if (nEnd >= nLastTop && nEnd < nLastTop + 100)
951                 nEnd = nLastTop;
952         }
953         nLastTop = nTop;
954         pindexLastBest = pindexBest;
955         nLastRefreshed = nListViewUpdated;
956
957         for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
958         {
959             uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
960             map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
961             if (mi == pwalletMain->mapWallet.end())
962             {
963                 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
964                 continue;
965             }
966             CWalletTx& wtx = (*mi).second;
967             if (wtx.IsCoinBase() ||
968                 wtx.GetTxTime() != wtx.nTimeDisplayed ||
969                 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
970             {
971                 if (!InsertTransaction(wtx, false, nIndex))
972                     m_listCtrl->DeleteItem(nIndex--);
973             }
974             else
975             {
976                 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
977             }
978         }
979     }
980 }
981
982 void CMainFrame::OnPaint(wxPaintEvent& event)
983 {
984     event.Skip();
985     if (fRefresh)
986     {
987         fRefresh = false;
988         Refresh();
989     }
990 }
991
992
993 unsigned int nNeedRepaint = 0;
994 unsigned int nLastRepaint = 0;
995 int64 nLastRepaintTime = 0;
996 int64 nRepaintInterval = 500;
997
998 void ThreadDelayedRepaint(void* parg)
999 {
1000     while (!fShutdown)
1001     {
1002         if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1003         {
1004             nLastRepaint = nNeedRepaint;
1005             if (pframeMain)
1006             {
1007                 printf("DelayedRepaint\n");
1008                 wxPaintEvent event;
1009                 pframeMain->fRefresh = true;
1010                 pframeMain->GetEventHandler()->AddPendingEvent(event);
1011             }
1012         }
1013         Sleep(nRepaintInterval);
1014     }
1015 }
1016
1017 void MainFrameRepaint()
1018 {
1019     // This is called by network code that shouldn't access pframeMain
1020     // directly because it could still be running after the UI is closed.
1021     if (pframeMain)
1022     {
1023         // Don't repaint too often
1024         static int64 nLastRepaintRequest;
1025         if (GetTimeMillis() - nLastRepaintRequest < 100)
1026         {
1027             nNeedRepaint++;
1028             return;
1029         }
1030         nLastRepaintRequest = GetTimeMillis();
1031
1032         printf("MainFrameRepaint\n");
1033         wxPaintEvent event;
1034         pframeMain->fRefresh = true;
1035         pframeMain->GetEventHandler()->AddPendingEvent(event);
1036     }
1037 }
1038
1039 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
1040 {
1041     // Skip lets the listctrl do the paint, we're just hooking the message
1042     event.Skip();
1043
1044     if (ptaskbaricon)
1045         ptaskbaricon->UpdateTooltip();
1046
1047     //
1048     // Slower stuff
1049     //
1050     static int nTransactionCount;
1051     bool fPaintedBalance = false;
1052     if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1053     {
1054         nLastRepaint = nNeedRepaint;
1055         nLastRepaintTime = GetTimeMillis();
1056
1057         // Update listctrl contents
1058         if (!pwalletMain->vWalletUpdated.empty())
1059         {
1060             TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1061             {
1062                 string strTop;
1063                 if (m_listCtrl->GetItemCount())
1064                     strTop = (string)m_listCtrl->GetItemText(0);
1065                 BOOST_FOREACH(uint256 hash, pwalletMain->vWalletUpdated)
1066                 {
1067                     map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1068                     if (mi != pwalletMain->mapWallet.end())
1069                         InsertTransaction((*mi).second, false);
1070                 }
1071                 pwalletMain->vWalletUpdated.clear();
1072                 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1073                     m_listCtrl->ScrollList(0, INT_MIN/2);
1074             }
1075         }
1076
1077         // Balance total
1078         TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1079         {
1080             fPaintedBalance = true;
1081             m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + "  ");
1082
1083             // Count hidden and multi-line transactions
1084             nTransactionCount = 0;
1085             for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1086             {
1087                 CWalletTx& wtx = (*it).second;
1088                 nTransactionCount += wtx.nLinesDisplayed;
1089             }
1090         }
1091     }
1092     if (!pwalletMain->vWalletUpdated.empty() || !fPaintedBalance)
1093         nNeedRepaint++;
1094
1095     // Update status column of visible items only
1096     RefreshStatusColumn();
1097
1098     // Update status bar
1099     static string strPrevWarning;
1100     string strWarning = GetWarnings("statusbar");
1101     if (strWarning != "")
1102         m_statusBar->SetStatusText(string("    ") + _(strWarning), 0);
1103     else if (strPrevWarning != "")
1104         m_statusBar->SetStatusText("", 0);
1105     strPrevWarning = strWarning;
1106
1107     string strGen = "";
1108     if (fGenerateBitcoins)
1109         strGen = _("    Generating");
1110     if (fGenerateBitcoins && vNodes.empty())
1111         strGen = _("(not connected)");
1112     m_statusBar->SetStatusText(strGen, 1);
1113
1114     string strStatus = strprintf(_("     %d connections     %d blocks     %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1115     m_statusBar->SetStatusText(strStatus, 2);
1116
1117     // Update receiving address
1118     string strDefaultAddress = PubKeyToAddress(pwalletMain->vchDefaultKey);
1119     if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1120         m_textCtrlAddress->SetValue(strDefaultAddress);
1121 }
1122
1123
1124 void UIThreadCall(boost::function0<void> fn)
1125 {
1126     // Call this with a function object created with bind.
1127     // bind needs all parameters to match the function's expected types
1128     // and all default parameters specified.  Some examples:
1129     //  UIThreadCall(bind(wxBell));
1130     //  UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1131     //  UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1132     if (pframeMain)
1133     {
1134         wxCommandEvent event(wxEVT_UITHREADCALL);
1135         event.SetClientData((void*)new boost::function0<void>(fn));
1136         pframeMain->GetEventHandler()->AddPendingEvent(event);
1137     }
1138 }
1139
1140 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1141 {
1142     boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1143     (*pfn)();
1144     delete pfn;
1145 }
1146
1147 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1148 {
1149     // File->Exit
1150     Close(true);
1151 }
1152
1153 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1154 {
1155     event.Check(fGenerateBitcoins);
1156 }
1157
1158 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1159 {
1160     // Options->Your Receiving Addresses
1161     CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1162     if (!dialog.ShowModal())
1163         return;
1164 }
1165
1166 void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event)
1167 {
1168     // Options->Encrypt Wallet
1169     if (pwalletMain->IsCrypted())
1170     {
1171         wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR);
1172         return;
1173     }
1174
1175     string strWalletPass;
1176     strWalletPass.reserve(100);
1177     mlock(&strWalletPass[0], strWalletPass.capacity());
1178
1179     // obtain current wallet encrypt/decrypt key, from passphrase
1180     // Note that the passphrase is not mlock()d during this entry and could potentially
1181     // be obtained from disk long after bitcoin has run.
1182     strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."),
1183                                           _("Passphrase")).ToStdString();
1184
1185     if (!strWalletPass.size())
1186     {
1187         fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1188         munlock(&strWalletPass[0], strWalletPass.capacity());
1189         wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1190         return;
1191     }
1192
1193     if(wxMessageBox(_("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"), "Bitcoin", wxYES_NO) != wxYES)
1194         return;
1195
1196     string strWalletPassTest;
1197     strWalletPassTest.reserve(100);
1198     mlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1199     strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."),
1200                                               _("Passphrase")).ToStdString();
1201
1202     if (strWalletPassTest != strWalletPass)
1203     {
1204         fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1205         fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1206         munlock(&strWalletPass[0], strWalletPass.capacity());
1207         munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1208         wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1209         return;
1210     }
1211
1212     if (!pwalletMain->EncryptWallet(strWalletPass))
1213     {
1214         fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1215         fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1216         munlock(&strWalletPass[0], strWalletPass.capacity());
1217         munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1218         wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR);
1219         return;
1220     }
1221     fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1222     fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1223     munlock(&strWalletPass[0], strWalletPass.capacity());
1224     munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1225     wxMessageBox(_("Wallet Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1226
1227     m_menuOptions->Remove(m_menuOptionsEncryptWallet);
1228     m_menuOptions->Insert(m_menuOptions->GetMenuItemCount() - 1, m_menuOptionsChangeWalletPassphrase);
1229 }
1230
1231 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1232 {
1233     // Options->Change Wallet Encryption Passphrase
1234     if (!pwalletMain->IsCrypted())
1235     {
1236         wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1237         return;
1238     }
1239
1240     string strOldWalletPass;
1241     strOldWalletPass.reserve(100);
1242     mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1243
1244     // obtain current wallet encrypt/decrypt key, from passphrase
1245     // Note that the passphrase is not mlock()d during this entry and could potentially
1246     // be obtained from disk long after bitcoin has run.
1247     strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1248                                              _("Passphrase")).ToStdString();
1249
1250     CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1251     {
1252         bool fWasLocked = pwalletMain->IsLocked();
1253         pwalletMain->Lock();
1254
1255         if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1256         {
1257             fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1258             munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1259             wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1260             return;
1261         }
1262
1263         if (fWasLocked)
1264             pwalletMain->Lock();
1265
1266         string strNewWalletPass;
1267         strNewWalletPass.reserve(100);
1268         mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1269
1270         // obtain new wallet encrypt/decrypt key, from passphrase
1271         // Note that the passphrase is not mlock()d during this entry and could potentially
1272         // be obtained from disk long after bitcoin has run.
1273         strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1274                                                  _("Passphrase")).ToStdString();
1275
1276         if (!strNewWalletPass.size())
1277         {
1278             fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1279             fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1280             munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1281             munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1282             wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1283             return;
1284         }
1285
1286         string strNewWalletPassTest;
1287         strNewWalletPassTest.reserve(100);
1288         mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1289
1290         // obtain new wallet encrypt/decrypt key, from passphrase
1291         // Note that the passphrase is not mlock()d during this entry and could potentially
1292         // be obtained from disk long after bitcoin has run.
1293         strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1294                                                      _("Passphrase")).ToStdString();
1295
1296         if (strNewWalletPassTest != strNewWalletPass)
1297         {
1298             fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1299             fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1300             fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1301             munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1302             munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1303             munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1304             wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1305             return;
1306         }
1307
1308         if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1309         {
1310             fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1311             fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1312             fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1313             munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1314             munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1315             munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1316             wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1317             return;
1318         }
1319         fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1320         fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1321         fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1322         munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1323         munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1324         munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1325         wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
1326     }
1327 }
1328
1329 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1330 {
1331     // Options->Options
1332     COptionsDialog dialog(this);
1333     dialog.ShowModal();
1334 }
1335
1336 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1337 {
1338     // Help->About
1339     CAboutDialog dialog(this);
1340     dialog.ShowModal();
1341 }
1342
1343 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1344 {
1345     // Toolbar: Send
1346     CSendDialog dialog(this);
1347     dialog.ShowModal();
1348 }
1349
1350 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1351 {
1352     // Toolbar: Address Book
1353     CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1354     if (dialogAddr.ShowModal() == 2)
1355     {
1356         // Send
1357         CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1358         dialogSend.ShowModal();
1359     }
1360 }
1361
1362 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1363 {
1364     // Automatically select-all when entering window
1365     event.Skip();
1366     m_textCtrlAddress->SetSelection(-1, -1);
1367     fOnSetFocusAddress = true;
1368 }
1369
1370 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1371 {
1372     event.Skip();
1373     if (fOnSetFocusAddress)
1374         m_textCtrlAddress->SetSelection(-1, -1);
1375     fOnSetFocusAddress = false;
1376 }
1377
1378 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1379 {
1380     // Ask name
1381     CGetTextFromUserDialog dialog(this,
1382         _("New Receiving Address"),
1383         _("You should use a new address for each payment you receive.\n\nLabel"),
1384         "");
1385     if (!dialog.ShowModal())
1386         return;
1387     string strName = dialog.GetValue();
1388
1389     string strAddress;
1390     CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1391     {
1392         bool fWasLocked = pwalletMain->IsLocked();
1393         if (!GetWalletPassphrase())
1394             return;
1395
1396         // Generate new key
1397         strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
1398
1399         if (fWasLocked)
1400             pwalletMain->Lock();
1401     }
1402
1403     // Save
1404     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1405         pwalletMain->SetAddressBookName(strAddress, strName);
1406     SetDefaultReceivingAddress(strAddress);
1407 }
1408
1409 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1410 {
1411     // Copy address box to clipboard
1412     if (wxTheClipboard->Open())
1413     {
1414         wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1415         wxTheClipboard->Close();
1416     }
1417 }
1418
1419 void CMainFrame::OnListItemActivated(wxListEvent& event)
1420 {
1421     uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1422     CWalletTx wtx;
1423     CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1424     {
1425         map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1426         if (mi == pwalletMain->mapWallet.end())
1427         {
1428             printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1429             return;
1430         }
1431         wtx = (*mi).second;
1432     }
1433     CTxDetailsDialog dialog(this, wtx);
1434     dialog.ShowModal();
1435     //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1436     //pdialog->Show();
1437 }
1438
1439
1440
1441
1442
1443
1444 //////////////////////////////////////////////////////////////////////////////
1445 //
1446 // CTxDetailsDialog
1447 //
1448
1449 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1450 {
1451 #ifdef __WXMSW__
1452     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1453 #endif
1454     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1455     {
1456         string strHTML;
1457         strHTML.reserve(4000);
1458         strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1459
1460         int64 nTime = wtx.GetTxTime();
1461         int64 nCredit = wtx.GetCredit();
1462         int64 nDebit = wtx.GetDebit();
1463         int64 nNet = nCredit - nDebit;
1464
1465
1466
1467         strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1468         int nRequests = wtx.GetRequestCount();
1469         if (nRequests != -1)
1470         {
1471             if (nRequests == 0)
1472                 strHTML += _(", has not been successfully broadcast yet");
1473             else if (nRequests == 1)
1474                 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1475             else
1476                 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1477         }
1478         strHTML += "<br>";
1479
1480         strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1481
1482
1483         //
1484         // From
1485         //
1486         if (wtx.IsCoinBase())
1487         {
1488             strHTML += _("<b>Source:</b> Generated<br>");
1489         }
1490         else if (!wtx.mapValue["from"].empty())
1491         {
1492             // Online transaction
1493             if (!wtx.mapValue["from"].empty())
1494                 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1495         }
1496         else
1497         {
1498             // Offline transaction
1499             if (nNet > 0)
1500             {
1501                 // Credit
1502                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1503                 {
1504                     if (pwalletMain->IsMine(txout))
1505                     {
1506                         uint160 hash160;
1507                         if (ExtractHash160(txout.scriptPubKey, pwalletMain, hash160))
1508                         {
1509                             string strAddress = Hash160ToAddress(hash160);
1510                             if (pwalletMain->mapAddressBook.count(strAddress))
1511                             {
1512                                 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1513                                 strHTML += _("<b>To:</b> ");
1514                                 strHTML += HtmlEscape(strAddress);
1515                                 if (!pwalletMain->mapAddressBook[strAddress].empty())
1516                                     strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[strAddress] + ")";
1517                                 else
1518                                     strHTML += _(" (yours)");
1519                                 strHTML += "<br>";
1520                             }
1521                         }
1522                         break;
1523                     }
1524                 }
1525             }
1526         }
1527
1528
1529         //
1530         // To
1531         //
1532         string strAddress;
1533         if (!wtx.mapValue["to"].empty())
1534         {
1535             // Online transaction
1536             strAddress = wtx.mapValue["to"];
1537             strHTML += _("<b>To:</b> ");
1538             if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1539                 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1540             strHTML += HtmlEscape(strAddress) + "<br>";
1541         }
1542
1543
1544         //
1545         // Amount
1546         //
1547         if (wtx.IsCoinBase() && nCredit == 0)
1548         {
1549             //
1550             // Coinbase
1551             //
1552             int64 nUnmatured = 0;
1553             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1554                 nUnmatured += pwalletMain->GetCredit(txout);
1555             strHTML += _("<b>Credit:</b> ");
1556             if (wtx.IsInMainChain())
1557                 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1558             else
1559                 strHTML += _("(not accepted)");
1560             strHTML += "<br>";
1561         }
1562         else if (nNet > 0)
1563         {
1564             //
1565             // Credit
1566             //
1567             strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1568         }
1569         else
1570         {
1571             bool fAllFromMe = true;
1572             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1573                 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1574
1575             bool fAllToMe = true;
1576             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1577                 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1578
1579             if (fAllFromMe)
1580             {
1581                 //
1582                 // Debit
1583                 //
1584                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1585                 {
1586                     if (pwalletMain->IsMine(txout))
1587                         continue;
1588
1589                     if (wtx.mapValue["to"].empty())
1590                     {
1591                         // Offline transaction
1592                         uint160 hash160;
1593                         if (ExtractHash160(txout.scriptPubKey, pwalletMain, hash160))
1594                         {
1595                             string strAddress = Hash160ToAddress(hash160);
1596                             strHTML += _("<b>To:</b> ");
1597                             if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1598                                 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1599                             strHTML += strAddress;
1600                             strHTML += "<br>";
1601                         }
1602                     }
1603
1604                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1605                 }
1606
1607                 if (fAllToMe)
1608                 {
1609                     // Payment to self
1610                     int64 nChange = wtx.GetChange();
1611                     int64 nValue = nCredit - nChange;
1612                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1613                     strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1614                 }
1615
1616                 int64 nTxFee = nDebit - wtx.GetValueOut();
1617                 if (nTxFee > 0)
1618                     strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1619             }
1620             else
1621             {
1622                 //
1623                 // Mixed debit transaction
1624                 //
1625                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1626                     if (pwalletMain->IsMine(txin))
1627                         strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1628                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1629                     if (pwalletMain->IsMine(txout))
1630                         strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1631             }
1632         }
1633
1634         strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1635
1636
1637         //
1638         // Message
1639         //
1640         if (!wtx.mapValue["message"].empty())
1641             strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1642         if (!wtx.mapValue["comment"].empty())
1643             strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1644
1645         if (wtx.IsCoinBase())
1646             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>";
1647
1648
1649         //
1650         // Debug view
1651         //
1652         if (fDebug)
1653         {
1654             strHTML += "<hr><br>debug print<br><br>";
1655             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1656                 if (pwalletMain->IsMine(txin))
1657                     strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1658             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1659                 if (pwalletMain->IsMine(txout))
1660                     strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1661
1662             strHTML += "<br><b>Transaction:</b><br>";
1663             strHTML += HtmlEscape(wtx.ToString(), true);
1664
1665             strHTML += "<br><b>Inputs:</b><br>";
1666             CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1667             {
1668                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1669                 {
1670                     COutPoint prevout = txin.prevout;
1671                     map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1672                     if (mi != pwalletMain->mapWallet.end())
1673                     {
1674                         const CWalletTx& prev = (*mi).second;
1675                         if (prevout.n < prev.vout.size())
1676                         {
1677                             strHTML += HtmlEscape(prev.ToString(), true);
1678                             strHTML += " &nbsp;&nbsp; " + FormatTxStatus(prev) + ", ";
1679                             strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1680                         }
1681                     }
1682                 }
1683             }
1684         }
1685
1686
1687
1688         strHTML += "</font></html>";
1689         string(strHTML.begin(), strHTML.end()).swap(strHTML);
1690         m_htmlWin->SetPage(strHTML);
1691         m_buttonOK->SetFocus();
1692     }
1693 }
1694
1695 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1696 {
1697     EndModal(false);
1698 }
1699
1700
1701
1702
1703
1704
1705 //////////////////////////////////////////////////////////////////////////////
1706 //
1707 // Startup folder
1708 //
1709
1710 #ifdef __WXMSW__
1711 string StartupShortcutPath()
1712 {
1713     return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1714 }
1715
1716 bool GetStartOnSystemStartup()
1717 {
1718     return filesystem::exists(StartupShortcutPath().c_str());
1719 }
1720
1721 void SetStartOnSystemStartup(bool fAutoStart)
1722 {
1723     // If the shortcut exists already, remove it for updating
1724     remove(StartupShortcutPath().c_str());
1725
1726     if (fAutoStart)
1727     {
1728         CoInitialize(NULL);
1729
1730         // Get a pointer to the IShellLink interface.
1731         IShellLink* psl = NULL;
1732         HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1733                                 CLSCTX_INPROC_SERVER, IID_IShellLink,
1734                                 reinterpret_cast<void**>(&psl));
1735
1736         if (SUCCEEDED(hres))
1737         {
1738             // Get the current executable path
1739             TCHAR pszExePath[MAX_PATH];
1740             GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1741
1742             // Set the path to the shortcut target
1743             psl->SetPath(pszExePath);
1744             PathRemoveFileSpec(pszExePath);
1745             psl->SetWorkingDirectory(pszExePath);
1746             psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1747
1748             // Query IShellLink for the IPersistFile interface for
1749             // saving the shortcut in persistent storage.
1750             IPersistFile* ppf = NULL;
1751             hres = psl->QueryInterface(IID_IPersistFile,
1752                                        reinterpret_cast<void**>(&ppf));
1753             if (SUCCEEDED(hres))
1754             {
1755                 WCHAR pwsz[MAX_PATH];
1756                 // Ensure that the string is ANSI.
1757                 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1758                 // Save the link by calling IPersistFile::Save.
1759                 hres = ppf->Save(pwsz, TRUE);
1760                 ppf->Release();
1761             }
1762             psl->Release();
1763         }
1764         CoUninitialize();
1765     }
1766 }
1767
1768 #elif defined(__WXGTK__)
1769
1770 // Follow the Desktop Application Autostart Spec:
1771 //  http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1772
1773 boost::filesystem::path GetAutostartDir()
1774 {
1775     namespace fs = boost::filesystem;
1776
1777     char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1778     if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1779     char* pszHome = getenv("HOME");
1780     if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1781     return fs::path();
1782 }
1783
1784 boost::filesystem::path GetAutostartFilePath()
1785 {
1786     return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1787 }
1788
1789 bool GetStartOnSystemStartup()
1790 {
1791     boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1792     if (!optionFile.good())
1793         return false;
1794     // Scan through file for "Hidden=true":
1795     string line;
1796     while (!optionFile.eof())
1797     {
1798         getline(optionFile, line);
1799         if (line.find("Hidden") != string::npos &&
1800             line.find("true") != string::npos)
1801             return false;
1802     }
1803     optionFile.close();
1804
1805     return true;
1806 }
1807
1808 void SetStartOnSystemStartup(bool fAutoStart)
1809 {
1810     if (!fAutoStart)
1811     {
1812         unlink(GetAutostartFilePath().native_file_string().c_str());
1813     }
1814     else
1815     {
1816         char pszExePath[MAX_PATH+1];
1817         memset(pszExePath, 0, sizeof(pszExePath));
1818         if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1819             return;
1820
1821         boost::filesystem::create_directories(GetAutostartDir());
1822
1823         boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1824         if (!optionFile.good())
1825         {
1826             wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1827             return;
1828         }
1829         // Write a bitcoin.desktop file to the autostart directory:
1830         optionFile << "[Desktop Entry]\n";
1831         optionFile << "Type=Application\n";
1832         optionFile << "Name=Bitcoin\n";
1833         optionFile << "Exec=" << pszExePath << "\n";
1834         optionFile << "Terminal=false\n";
1835         optionFile << "Hidden=false\n";
1836         optionFile.close();
1837     }
1838 }
1839 #else
1840
1841 // TODO: OSX startup stuff; see:
1842 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1843
1844 bool GetStartOnSystemStartup() { return false; }
1845 void SetStartOnSystemStartup(bool fAutoStart) { }
1846
1847 #endif
1848
1849
1850
1851
1852
1853
1854 //////////////////////////////////////////////////////////////////////////////
1855 //
1856 // COptionsDialog
1857 //
1858
1859 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1860 {
1861     // Set up list box of page choices
1862     m_listBox->Append(_("Main"));
1863     //m_listBox->Append(_("Test 2"));
1864     m_listBox->SetSelection(0);
1865     SelectPage(0);
1866 #ifndef __WXMSW__
1867     SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1868 #else
1869     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1870 #endif
1871 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1872     m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1873     if (!GetBoolArg("-minimizetotray"))
1874     {
1875         // Minimize to tray is just too buggy on Linux
1876         fMinimizeToTray = false;
1877         m_checkBoxMinimizeToTray->SetValue(false);
1878         m_checkBoxMinimizeToTray->Enable(false);
1879         m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1880     }
1881 #endif
1882 #ifdef __WXMAC_OSX__
1883     m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1884 #endif
1885
1886     // Init values
1887     m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1888     m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1889     m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1890     m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1891     if (fHaveUPnP)
1892         m_checkBoxUseUPnP->SetValue(fUseUPnP);
1893     else
1894         m_checkBoxUseUPnP->Enable(false);
1895     m_checkBoxUseProxy->SetValue(fUseProxy);
1896     m_textCtrlProxyIP->Enable(fUseProxy);
1897     m_textCtrlProxyPort->Enable(fUseProxy);
1898     m_staticTextProxyIP->Enable(fUseProxy);
1899     m_staticTextProxyPort->Enable(fUseProxy);
1900     m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1901     m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1902
1903     m_buttonOK->SetFocus();
1904 }
1905
1906 void COptionsDialog::SelectPage(int nPage)
1907 {
1908     m_panelMain->Show(nPage == 0);
1909     m_panelTest2->Show(nPage == 1);
1910
1911     m_scrolledWindow->Layout();
1912     m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1913 }
1914
1915 void COptionsDialog::OnListBox(wxCommandEvent& event)
1916 {
1917     SelectPage(event.GetSelection());
1918 }
1919
1920 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1921 {
1922     event.Skip();
1923     int64 nTmp = nTransactionFee;
1924     ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1925     m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1926 }
1927
1928 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1929 {
1930     m_textCtrlProxyIP->Enable(event.IsChecked());
1931     m_textCtrlProxyPort->Enable(event.IsChecked());
1932     m_staticTextProxyIP->Enable(event.IsChecked());
1933     m_staticTextProxyPort->Enable(event.IsChecked());
1934 }
1935
1936 CAddress COptionsDialog::GetProxyAddr()
1937 {
1938     // Be careful about byte order, addr.ip and addr.port are big endian
1939     CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1940     if (addr.ip == INADDR_NONE)
1941         addr.ip = addrProxy.ip;
1942     int nPort = atoi(m_textCtrlProxyPort->GetValue());
1943     addr.port = htons(nPort);
1944     if (nPort <= 0 || nPort > USHRT_MAX)
1945         addr.port = addrProxy.port;
1946     return addr;
1947 }
1948
1949 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1950 {
1951     event.Skip();
1952     m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1953     m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1954 }
1955
1956
1957 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1958 {
1959     OnButtonApply(event);
1960     EndModal(false);
1961 }
1962
1963 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1964 {
1965     EndModal(false);
1966 }
1967
1968 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1969 {
1970     CWalletDB walletdb(pwalletMain->strWalletFile);
1971
1972     int64 nPrevTransactionFee = nTransactionFee;
1973     if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1974         walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1975
1976     if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1977     {
1978         fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1979         SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1980     }
1981
1982     if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1983     {
1984         fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1985         walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1986         ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1987     }
1988
1989     if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1990     {
1991         fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1992         walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1993     }
1994
1995     if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1996     {
1997         fUseUPnP = m_checkBoxUseUPnP->GetValue();
1998         walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1999         MapPort(fUseUPnP);
2000     }
2001
2002     fUseProxy = m_checkBoxUseProxy->GetValue();
2003     walletdb.WriteSetting("fUseProxy", fUseProxy);
2004
2005     addrProxy = GetProxyAddr();
2006     walletdb.WriteSetting("addrProxy", addrProxy);
2007 }
2008
2009
2010
2011
2012
2013
2014 //////////////////////////////////////////////////////////////////////////////
2015 //
2016 // CAboutDialog
2017 //
2018
2019 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2020 {
2021     m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2022
2023     // Change (c) into UTF-8 or ANSI copyright symbol
2024     wxString str = m_staticTextMain->GetLabel();
2025 #if wxUSE_UNICODE
2026     str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2027 #else
2028     str.Replace("(c)", "\xA9");
2029 #endif
2030     m_staticTextMain->SetLabel(str);
2031 #ifndef __WXMSW__
2032     // Resize on Linux to make the window fit the text.
2033     // The text was wrapped manually rather than using the Wrap setting because
2034     // the wrap would be too small on Linux and it can't be changed at this point.
2035     wxFont fontTmp = m_staticTextMain->GetFont();
2036     if (fontTmp.GetPointSize() > 8);
2037         fontTmp.SetPointSize(8);
2038     m_staticTextMain->SetFont(fontTmp);
2039     SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2040 #else
2041     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2042 #endif
2043 }
2044
2045 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2046 {
2047     EndModal(false);
2048 }
2049
2050
2051
2052
2053
2054
2055 //////////////////////////////////////////////////////////////////////////////
2056 //
2057 // CSendDialog
2058 //
2059
2060 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2061 {
2062     // Init
2063     m_textCtrlAddress->SetValue(strAddress);
2064     m_choiceTransferType->SetSelection(0);
2065     m_bitmapCheckMark->Show(false);
2066     fEnabledPrev = true;
2067     m_textCtrlAddress->SetFocus();
2068     
2069     //// todo: should add a display of your balance for convenience
2070 #ifndef __WXMSW__
2071     wxFont fontTmp = m_staticTextInstructions->GetFont();
2072     if (fontTmp.GetPointSize() > 9);
2073         fontTmp.SetPointSize(9);
2074     m_staticTextInstructions->SetFont(fontTmp);
2075     SetSize(725, 180);
2076 #else
2077     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2078 #endif
2079     
2080     // Set Icon
2081     if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2082     {
2083         wxIcon iconSend;
2084         iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2085         SetIcon(iconSend);
2086     }
2087 #ifdef __WXMSW__
2088     else
2089         SetIcon(wxICON(bitcoin));
2090 #endif
2091
2092     // Fixup the tab order
2093     m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2094     m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2095     this->Layout();
2096 }
2097
2098 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2099 {
2100     // Reformat the amount
2101     event.Skip();
2102     if (m_textCtrlAmount->GetValue().Trim().empty())
2103         return;
2104     int64 nTmp;
2105     if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2106         m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2107 }
2108
2109 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2110 {
2111     // Open address book
2112     CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2113     if (dialog.ShowModal())
2114         m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2115 }
2116
2117 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2118 {
2119     // Copy clipboard to address box
2120     if (wxTheClipboard->Open())
2121     {
2122         if (wxTheClipboard->IsSupported(wxDF_TEXT))
2123         {
2124             wxTextDataObject data;
2125             wxTheClipboard->GetData(data);
2126             m_textCtrlAddress->SetValue(data.GetText());
2127         }
2128         wxTheClipboard->Close();
2129     }
2130 }
2131
2132 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2133 {
2134     static CCriticalSection cs_sendlock;
2135     TRY_CRITICAL_BLOCK(cs_sendlock)
2136     {
2137         CWalletTx wtx;
2138         string strAddress = (string)m_textCtrlAddress->GetValue();
2139
2140         // Parse amount
2141         int64 nValue = 0;
2142         if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2143         {
2144             wxMessageBox(_("Error in amount  "), _("Send Coins"));
2145             return;
2146         }
2147         if (nValue > pwalletMain->GetBalance())
2148         {
2149             wxMessageBox(_("Amount exceeds your balance  "), _("Send Coins"));
2150             return;
2151         }
2152         if (nValue + nTransactionFee > pwalletMain->GetBalance())
2153         {
2154             wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included  "), _("Send Coins"));
2155             return;
2156         }
2157
2158         // Parse bitcoin address
2159         uint160 hash160;
2160         bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
2161
2162         if (fBitcoinAddress)
2163         {
2164             CRITICAL_BLOCK(cs_main)
2165             CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2166             {
2167                 bool fWasLocked = pwalletMain->IsLocked();
2168                 if (!GetWalletPassphrase())
2169                     return;
2170
2171                 // Send to bitcoin address
2172                 CScript scriptPubKey;
2173                 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
2174
2175                 string strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2176                 if (strError == "")
2177                     wxMessageBox(_("Payment sent  "), _("Sending..."));
2178                 else if (strError == "ABORTED")
2179                 {
2180                     if (fWasLocked)
2181                         pwalletMain->Lock();
2182                     return; // leave send dialog open
2183                 }
2184                 else
2185                 {
2186                     wxMessageBox(strError + "  ", _("Sending..."));
2187                     EndModal(false);
2188                     if (fWasLocked)
2189                         pwalletMain->Lock();
2190                     return;
2191                 }
2192
2193                 if (fWasLocked)
2194                     pwalletMain->Lock();
2195             }
2196         }
2197         else
2198         {
2199             // Parse IP address
2200             CAddress addr(strAddress);
2201             if (!addr.IsValid())
2202             {
2203                 wxMessageBox(_("Invalid address  "), _("Send Coins"));
2204                 return;
2205             }
2206
2207             // Message
2208             wtx.mapValue["to"] = strAddress;
2209
2210             // Send to IP address
2211             CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2212             if (!pdialog->ShowModal())
2213                 return;
2214         }
2215
2216         CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2217             if (!pwalletMain->mapAddressBook.count(strAddress))
2218                 pwalletMain->SetAddressBookName(strAddress, "");
2219
2220         EndModal(true);
2221     }
2222 }
2223
2224 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2225 {
2226     // Cancel
2227     EndModal(false);
2228 }
2229
2230
2231
2232
2233
2234
2235 //////////////////////////////////////////////////////////////////////////////
2236 //
2237 // CSendingDialog
2238 //
2239
2240 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
2241 {
2242     addr = addrIn;
2243     nPrice = nPriceIn;
2244     wtx = wtxIn;
2245     start = wxDateTime::UNow();
2246     memset(pszStatus, 0, sizeof(pszStatus));
2247     fCanCancel = true;
2248     fAbort = false;
2249     fSuccess = false;
2250     fUIDone = false;
2251     fWorkDone = false;
2252 #ifndef __WXMSW__
2253     SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2254 #else
2255     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2256 #endif
2257
2258     SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2259     m_textCtrlStatus->SetValue("");
2260
2261     CreateThread(SendingDialogStartTransfer, this);
2262 }
2263
2264 CSendingDialog::~CSendingDialog()
2265 {
2266     printf("~CSendingDialog()\n");
2267 }
2268
2269 void CSendingDialog::Close()
2270 {
2271     // Last one out turn out the lights.
2272     // fWorkDone signals that work side is done and UI thread should call destroy.
2273     // fUIDone signals that UI window has closed and work thread should call destroy.
2274     // This allows the window to disappear and end modality when cancelled
2275     // without making the user wait for ConnectNode to return.  The dialog object
2276     // hangs around in the background until the work thread exits.
2277     if (IsModal())
2278         EndModal(fSuccess);
2279     else
2280         Show(false);
2281     if (fWorkDone)
2282         Destroy();
2283     else
2284         fUIDone = true;
2285 }
2286
2287 void CSendingDialog::OnClose(wxCloseEvent& event)
2288 {
2289     if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2290     {
2291         Close();
2292     }
2293     else
2294     {
2295         event.Veto();
2296         wxCommandEvent cmdevent;
2297         OnButtonCancel(cmdevent);
2298     }
2299 }
2300
2301 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2302 {
2303     if (fWorkDone)
2304         Close();
2305 }
2306
2307 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2308 {
2309     if (fCanCancel)
2310         fAbort = true;
2311 }
2312
2313 void CSendingDialog::OnPaint(wxPaintEvent& event)
2314 {
2315     event.Skip();
2316     if (strlen(pszStatus) > 130)
2317         m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2318     else
2319         m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2320     m_staticTextSending->SetFocus();
2321     if (!fCanCancel)
2322         m_buttonCancel->Enable(false);
2323     if (fWorkDone)
2324     {
2325         m_buttonOK->Enable(true);
2326         m_buttonOK->SetFocus();
2327         m_buttonCancel->Enable(false);
2328     }
2329     if (fAbort && fCanCancel && IsShown())
2330     {
2331         strcpy(pszStatus, _("CANCELLED"));
2332         m_buttonOK->Enable(true);
2333         m_buttonOK->SetFocus();
2334         m_buttonCancel->Enable(false);
2335         m_buttonCancel->SetLabel(_("Cancelled"));
2336         Close();
2337         wxMessageBox(_("Transfer cancelled  "), _("Sending..."), wxOK, this);
2338     }
2339 }
2340
2341
2342 //
2343 // Everything from here on is not in the UI thread and must only communicate
2344 // with the rest of the dialog through variables and calling repaint.
2345 //
2346
2347 void CSendingDialog::Repaint()
2348 {
2349     Refresh();
2350     wxPaintEvent event;
2351     GetEventHandler()->AddPendingEvent(event);
2352 }
2353
2354 bool CSendingDialog::Status()
2355 {
2356     if (fUIDone)
2357     {
2358         Destroy();
2359         return false;
2360     }
2361     if (fAbort && fCanCancel)
2362     {
2363         memset(pszStatus, 0, 10);
2364         strcpy(pszStatus, _("CANCELLED"));
2365         Repaint();
2366         fWorkDone = true;
2367         return false;
2368     }
2369     return true;
2370 }
2371
2372 bool CSendingDialog::Status(const string& str)
2373 {
2374     if (!Status())
2375         return false;
2376
2377     // This can be read by the UI thread at any time,
2378     // so copy in a way that can be read cleanly at all times.
2379     memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2380     strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2381
2382     Repaint();
2383     return true;
2384 }
2385
2386 bool CSendingDialog::Error(const string& str)
2387 {
2388     fCanCancel = false;
2389     fWorkDone = true;
2390     Status(string(_("Error: ")) + str);
2391     return false;
2392 }
2393
2394 void SendingDialogStartTransfer(void* parg)
2395 {
2396     ((CSendingDialog*)parg)->StartTransfer();
2397 }
2398
2399 void CSendingDialog::StartTransfer()
2400 {
2401     // Make sure we have enough money
2402     if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2403     {
2404         Error(_("Insufficient funds"));
2405         return;
2406     }
2407
2408     // We may have connected already for product details
2409     if (!Status(_("Connecting...")))
2410         return;
2411     CNode* pnode = ConnectNode(addr, 15 * 60);
2412     if (!pnode)
2413     {
2414         Error(_("Unable to connect"));
2415         return;
2416     }
2417
2418     // Send order to seller, with response going to OnReply2 via event handler
2419     if (!Status(_("Requesting public key...")))
2420         return;
2421     pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2422 }
2423
2424 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2425 {
2426     ((CSendingDialog*)parg)->OnReply2(vRecv);
2427 }
2428
2429 void CSendingDialog::OnReply2(CDataStream& vRecv)
2430 {
2431     if (!Status(_("Received public key...")))
2432         return;
2433
2434     CScript scriptPubKey;
2435     int nRet;
2436     try
2437     {
2438         vRecv >> nRet;
2439         if (nRet > 0)
2440         {
2441             string strMessage;
2442             if (!vRecv.empty())
2443                 vRecv >> strMessage;
2444             if (nRet == 2)
2445                 Error(_("Recipient is not accepting transactions sent by IP address"));
2446             else
2447                 Error(_("Transfer was not accepted"));
2448             //// todo: enlarge the window and enable a hidden white box to put seller's message
2449             return;
2450         }
2451         vRecv >> scriptPubKey;
2452     }
2453     catch (...)
2454     {
2455         //// what do we want to do about this?
2456         Error(_("Invalid response received"));
2457         return;
2458     }
2459
2460     // Pause to give the user a chance to cancel
2461     while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2462     {
2463         Sleep(200);
2464         if (!Status())
2465             return;
2466     }
2467
2468     CRITICAL_BLOCK(cs_main)
2469     {
2470         // Pay
2471         if (!Status(_("Creating transaction...")))
2472             return;
2473         if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2474         {
2475             Error(_("Insufficient funds"));
2476             return;
2477         }
2478
2479         CReserveKey reservekey(pwalletMain);
2480         int64 nFeeRequired;
2481         CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2482         {
2483             bool fWasLocked = pwalletMain->IsLocked();
2484             if (!GetWalletPassphrase())
2485                 return;
2486
2487             if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2488             {
2489                 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2490                     Error(strprintf(_("This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds"), FormatMoney(nFeeRequired).c_str()));
2491                 else
2492                     Error(_("Transaction creation failed"));
2493                 return;
2494             }
2495
2496             if (fWasLocked)
2497                 pwalletMain->Lock();
2498        }
2499
2500         // Transaction fee
2501         if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2502         {
2503             Error(_("Transaction aborted"));
2504             return;
2505         }
2506
2507         // Make sure we're still connected
2508         CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2509         if (!pnode)
2510         {
2511             Error(_("Lost connection, transaction cancelled"));
2512             return;
2513         }
2514
2515         // Last chance to cancel
2516         Sleep(50);
2517         if (!Status())
2518             return;
2519         fCanCancel = false;
2520         if (fAbort)
2521         {
2522             fCanCancel = true;
2523             if (!Status())
2524                 return;
2525             fCanCancel = false;
2526         }
2527         if (!Status(_("Sending payment...")))
2528             return;
2529
2530         // Commit
2531         if (!pwalletMain->CommitTransaction(wtx, reservekey))
2532         {
2533             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."));
2534             return;
2535         }
2536
2537         // Send payment tx to seller, with response going to OnReply3 via event handler
2538         CWalletTx wtxSend = wtx;
2539         wtxSend.fFromMe = false;
2540         pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2541
2542         Status(_("Waiting for confirmation..."));
2543         MainFrameRepaint();
2544     }
2545 }
2546
2547 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2548 {
2549     ((CSendingDialog*)parg)->OnReply3(vRecv);
2550 }
2551
2552 void CSendingDialog::OnReply3(CDataStream& vRecv)
2553 {
2554     int nRet;
2555     try
2556     {
2557         vRecv >> nRet;
2558         if (nRet > 0)
2559         {
2560             Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2561                     "The transaction is recorded and will credit to the recipient,\n"
2562                     "but the comment information will be blank."));
2563             return;
2564         }
2565     }
2566     catch (...)
2567     {
2568         //// what do we want to do about this?
2569         Error(_("Payment was sent, but an invalid response was received"));
2570         return;
2571     }
2572
2573     fSuccess = true;
2574     fWorkDone = true;
2575     Status(_("Payment completed"));
2576 }
2577
2578
2579
2580
2581
2582
2583 //////////////////////////////////////////////////////////////////////////////
2584 //
2585 // CAddressBookDialog
2586 //
2587
2588 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2589 {
2590 #ifdef __WXMSW__
2591     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2592 #endif
2593
2594     // Set initially selected page
2595     wxNotebookEvent event;
2596     event.SetSelection(nPageIn);
2597     OnNotebookPageChanged(event);
2598     m_notebook->ChangeSelection(nPageIn);
2599
2600     fDuringSend = fDuringSendIn;
2601     if (!fDuringSend)
2602         m_buttonCancel->Show(false);
2603
2604     // Set Icon
2605     if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2606     {
2607         wxIcon iconAddressBook;
2608         iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2609         SetIcon(iconAddressBook);
2610     }
2611 #ifdef __WXMSW__
2612     else
2613         SetIcon(wxICON(bitcoin));
2614 #endif
2615
2616     // Init column headers
2617     m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2618     m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2619     m_listCtrlSending->SetFocus();
2620     m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2621     m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2622     m_listCtrlReceiving->SetFocus();
2623
2624     // Fill listctrl with address book data
2625     CRITICAL_BLOCK(pwalletMain->cs_KeyStore)
2626     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2627     {
2628         string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2629         BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook)
2630         {
2631             string strAddress = item.first;
2632             string strName = item.second;
2633             uint160 hash160;
2634             bool fMine = (AddressToHash160(strAddress, hash160) && pwalletMain->HaveKey(hash160));
2635             wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2636             int nIndex = InsertLine(plistCtrl, strName, strAddress);
2637             if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2638                 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2639         }
2640     }
2641 }
2642
2643 wxString CAddressBookDialog::GetSelectedAddress()
2644 {
2645     int nIndex = GetSelection(m_listCtrl);
2646     if (nIndex == -1)
2647         return "";
2648     return GetItemText(m_listCtrl, nIndex, 1);
2649 }
2650
2651 wxString CAddressBookDialog::GetSelectedSendingAddress()
2652 {
2653     int nIndex = GetSelection(m_listCtrlSending);
2654     if (nIndex == -1)
2655         return "";
2656     return GetItemText(m_listCtrlSending, nIndex, 1);
2657 }
2658
2659 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2660 {
2661     int nIndex = GetSelection(m_listCtrlReceiving);
2662     if (nIndex == -1)
2663         return "";
2664     return GetItemText(m_listCtrlReceiving, nIndex, 1);
2665 }
2666
2667 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2668 {
2669     event.Skip();
2670     nPage = event.GetSelection();
2671     if (nPage == SENDING)
2672         m_listCtrl = m_listCtrlSending;
2673     else if (nPage == RECEIVING)
2674         m_listCtrl = m_listCtrlReceiving;
2675     m_buttonDelete->Show(nPage == SENDING);
2676     m_buttonCopy->Show(nPage == RECEIVING);
2677     this->Layout();
2678     m_listCtrl->SetFocus();
2679 }
2680
2681 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2682 {
2683     // Update address book with edited name
2684     event.Skip();
2685     if (event.IsEditCancelled())
2686         return;
2687     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2688     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2689         pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2690     pframeMain->RefreshListCtrl();
2691 }
2692
2693 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2694 {
2695     event.Skip();
2696     if (nPage == RECEIVING)
2697         SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2698 }
2699
2700 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2701 {
2702     event.Skip();
2703     if (fDuringSend)
2704     {
2705         // Doubleclick returns selection
2706         EndModal(GetSelectedAddress() != "" ? 2 : 0);
2707         return;
2708     }
2709
2710     // Doubleclick edits item
2711     wxCommandEvent event2;
2712     OnButtonEdit(event2);
2713 }
2714
2715 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2716 {
2717     if (nPage != SENDING)
2718         return;
2719     for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2720     {
2721         if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2722         {
2723             string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2724             CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2725                 pwalletMain->DelAddressBookName(strAddress);
2726             m_listCtrl->DeleteItem(nIndex);
2727         }
2728     }
2729     pframeMain->RefreshListCtrl();
2730 }
2731
2732 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2733 {
2734     // Copy address box to clipboard
2735     if (wxTheClipboard->Open())
2736     {
2737         wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2738         wxTheClipboard->Close();
2739     }
2740 }
2741
2742 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2743 {
2744     uint160 hash160;
2745     bool fMine = (AddressToHash160(strAddress, hash160) && pwalletMain->HaveKey(hash160));
2746     if (fMine)
2747         wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book.  "), strTitle);
2748     return fMine;
2749 }
2750
2751 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2752 {
2753     int nIndex = GetSelection(m_listCtrl);
2754     if (nIndex == -1)
2755         return;
2756     string strName = (string)m_listCtrl->GetItemText(nIndex);
2757     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2758     string strAddressOrg = strAddress;
2759
2760     if (nPage == SENDING)
2761     {
2762         // Ask name and address
2763         do
2764         {
2765             CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2766             if (!dialog.ShowModal())
2767                 return;
2768             strName = dialog.GetValue1();
2769             strAddress = dialog.GetValue2();
2770         }
2771         while (CheckIfMine(strAddress, _("Edit Address")));
2772
2773     }
2774     else if (nPage == RECEIVING)
2775     {
2776         // Ask name
2777         CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2778         if (!dialog.ShowModal())
2779             return;
2780         strName = dialog.GetValue();
2781     }
2782
2783     // Write back
2784     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2785     {
2786         if (strAddress != strAddressOrg)
2787             pwalletMain->DelAddressBookName(strAddressOrg);
2788         pwalletMain->SetAddressBookName(strAddress, strName);
2789     }
2790     m_listCtrl->SetItem(nIndex, 1, strAddress);
2791     m_listCtrl->SetItemText(nIndex, strName);
2792     pframeMain->RefreshListCtrl();
2793 }
2794
2795 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2796 {
2797     string strName;
2798     string strAddress;
2799
2800     if (nPage == SENDING)
2801     {
2802         // Ask name and address
2803         do
2804         {
2805             CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2806             if (!dialog.ShowModal())
2807                 return;
2808             strName = dialog.GetValue1();
2809             strAddress = dialog.GetValue2();
2810         }
2811         while (CheckIfMine(strAddress, _("Add Address")));
2812     }
2813     else if (nPage == RECEIVING)
2814     {
2815         // Ask name
2816         CGetTextFromUserDialog dialog(this,
2817             _("New Receiving Address"),
2818             _("You should use a new address for each payment you receive.\n\nLabel"),
2819             "");
2820         if (!dialog.ShowModal())
2821             return;
2822         strName = dialog.GetValue();
2823
2824         CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2825         {
2826             bool fWasLocked = pwalletMain->IsLocked();
2827             if (!GetWalletPassphrase())
2828                 return;
2829
2830             // Generate new key
2831             strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
2832
2833             if (fWasLocked)
2834                 pwalletMain->Lock();
2835         }
2836     }
2837
2838     // Add to list and select it
2839     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2840         pwalletMain->SetAddressBookName(strAddress, strName);
2841     int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2842     SetSelection(m_listCtrl, nIndex);
2843     m_listCtrl->SetFocus();
2844     if (nPage == SENDING)
2845         pframeMain->RefreshListCtrl();
2846 }
2847
2848 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2849 {
2850     // OK
2851     EndModal(GetSelectedAddress() != "" ? 1 : 0);
2852 }
2853
2854 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2855 {
2856     // Cancel
2857     EndModal(0);
2858 }
2859
2860 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2861 {
2862     // Close
2863     EndModal(0);
2864 }
2865
2866
2867
2868
2869
2870
2871 //////////////////////////////////////////////////////////////////////////////
2872 //
2873 // CMyTaskBarIcon
2874 //
2875
2876 enum
2877 {
2878     ID_TASKBAR_RESTORE = 10001,
2879     ID_TASKBAR_SEND,
2880     ID_TASKBAR_OPTIONS,
2881     ID_TASKBAR_GENERATE,
2882     ID_TASKBAR_EXIT,
2883 };
2884
2885 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2886     EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2887     EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2888     EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2889     EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2890     EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2891     EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2892 END_EVENT_TABLE()
2893
2894 void CMyTaskBarIcon::Show(bool fShow)
2895 {
2896     static char pszPrevTip[200];
2897     if (fShow)
2898     {
2899         string strTooltip = _("Bitcoin");
2900         if (fGenerateBitcoins)
2901             strTooltip = _("Bitcoin - Generating");
2902         if (fGenerateBitcoins && vNodes.empty())
2903             strTooltip = _("Bitcoin - (not connected)");
2904
2905         // Optimization, only update when changed, using char array to be reentrant
2906         if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2907         {
2908             strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2909 #ifdef __WXMSW__
2910             // somehow it'll choose the wrong size and scale it down if
2911             // we use the main icon, so we hand it one with only 16x16
2912             SetIcon(wxICON(favicon), strTooltip);
2913 #else
2914             SetIcon(bitcoin80_xpm, strTooltip);
2915 #endif
2916         }
2917     }
2918     else
2919     {
2920         strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2921         RemoveIcon();
2922     }
2923 }
2924
2925 void CMyTaskBarIcon::Hide()
2926 {
2927     Show(false);
2928 }
2929
2930 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2931 {
2932     Restore();
2933 }
2934
2935 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2936 {
2937     Restore();
2938 }
2939
2940 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2941 {
2942     // Taskbar: Send
2943     CSendDialog dialog(pframeMain);
2944     dialog.ShowModal();
2945 }
2946
2947 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2948 {
2949     // Since it's modal, get the main window to do it
2950     wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2951     pframeMain->GetEventHandler()->AddPendingEvent(event2);
2952 }
2953
2954 void CMyTaskBarIcon::Restore()
2955 {
2956     pframeMain->Show();
2957     wxIconizeEvent event(0, false);
2958     pframeMain->GetEventHandler()->AddPendingEvent(event);
2959     pframeMain->Iconize(false);
2960     pframeMain->Raise();
2961 }
2962
2963 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2964 {
2965     event.Check(fGenerateBitcoins);
2966 }
2967
2968 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2969 {
2970     pframeMain->Close(true);
2971 }
2972
2973 void CMyTaskBarIcon::UpdateTooltip()
2974 {
2975     if (IsIconInstalled())
2976         Show(true);
2977 }
2978
2979 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2980 {
2981     wxMenu* pmenu = new wxMenu;
2982     pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2983     pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2984     pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2985 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2986     pmenu->AppendSeparator();
2987     pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2988 #endif
2989     return pmenu;
2990 }
2991
2992
2993
2994
2995
2996
2997 //////////////////////////////////////////////////////////////////////////////
2998 //
2999 // CMyApp
3000 //
3001
3002 void CreateMainWindow()
3003 {
3004     pframeMain = new CMainFrame(NULL);
3005     if (GetBoolArg("-min"))
3006         pframeMain->Iconize(true);
3007 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3008     if (!GetBoolArg("-minimizetotray"))
3009         fMinimizeToTray = false;
3010 #endif
3011     pframeMain->Show(true);  // have to show first to get taskbar button to hide
3012     if (fMinimizeToTray && pframeMain->IsIconized())
3013         fClosedToTray = true;
3014     pframeMain->Show(!fClosedToTray);
3015     ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3016     CreateThread(ThreadDelayedRepaint, NULL);
3017 }
3018
3019
3020 // Define a new application
3021 class CMyApp : public wxApp
3022 {
3023 public:
3024     CMyApp(){};
3025     ~CMyApp(){};
3026     bool OnInit();
3027     bool OnInit2();
3028     int OnExit();
3029
3030     // Hook Initialize so we can start without GUI
3031     virtual bool Initialize(int& argc, wxChar** argv);
3032
3033     // 2nd-level exception handling: we get all the exceptions occurring in any
3034     // event handler here
3035     virtual bool OnExceptionInMainLoop();
3036
3037     // 3rd, and final, level exception handling: whenever an unhandled
3038     // exception is caught, this function is called
3039     virtual void OnUnhandledException();
3040
3041     // and now for something different: this function is called in case of a
3042     // crash (e.g. dereferencing null pointer, division by 0, ...)
3043     virtual void OnFatalException();
3044 };
3045
3046 IMPLEMENT_APP(CMyApp)
3047
3048 bool CMyApp::Initialize(int& argc, wxChar** argv)
3049 {
3050     for (int i = 1; i < argc; i++)
3051         if (!IsSwitchChar(argv[i][0]))
3052             fCommandLine = true;
3053
3054     if (!fCommandLine)
3055     {
3056         // wxApp::Initialize will remove environment-specific parameters,
3057         // so it's too early to call ParseParameters yet
3058         for (int i = 1; i < argc; i++)
3059         {
3060             wxString str = argv[i];
3061             #ifdef __WXMSW__
3062             if (str.size() >= 1 && str[0] == '/')
3063                 str[0] = '-';
3064             char pszLower[MAX_PATH];
3065             strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3066             strlwr(pszLower);
3067             str = pszLower;
3068             #endif
3069             if (str == "-daemon")
3070                 fDaemon = true;
3071         }
3072     }
3073
3074 #ifdef __WXGTK__
3075     if (fDaemon || fCommandLine)
3076     {
3077         // Call the original Initialize while suppressing error messages
3078         // and ignoring failure.  If unable to initialize GTK, it fails
3079         // near the end so hopefully the last few things don't matter.
3080         {
3081             wxLogNull logNo;
3082             wxApp::Initialize(argc, argv);
3083         }
3084
3085         if (fDaemon)
3086         {
3087             // Daemonize
3088             pid_t pid = fork();
3089             if (pid < 0)
3090             {
3091                 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3092                 return false;
3093             }
3094             if (pid > 0)
3095                 pthread_exit((void*)0);
3096
3097             pid_t sid = setsid();
3098             if (sid < 0)
3099                 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3100         }
3101
3102         return true;
3103     }
3104 #endif
3105
3106     return wxApp::Initialize(argc, argv);
3107 }
3108
3109 bool CMyApp::OnInit()
3110 {
3111 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3112     // Disable malfunctioning wxWidgets debug assertion
3113     extern int g_isPainting;
3114     g_isPainting = 10000;
3115 #endif
3116 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3117     SetAppName("Bitcoin");
3118 #else
3119     SetAppName("bitcoin");
3120 #endif
3121 #ifdef __WXMSW__
3122 #if wxUSE_UNICODE
3123     // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3124     // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3125     class wxMBConv_win32 : public wxMBConv
3126     {
3127     public:
3128         long m_CodePage;
3129         size_t m_minMBCharWidth;
3130     };
3131     if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3132         ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3133 #endif
3134 #endif
3135
3136     // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3137     g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3138     g_locale.AddCatalogLookupPathPrefix("locale");
3139 #ifndef __WXMSW__
3140     g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3141     g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3142 #endif
3143     g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3144     g_locale.AddCatalog("bitcoin");
3145
3146 #ifdef __WXMSW__
3147     HDC hdc = GetDC(NULL);
3148     if (hdc)
3149     {
3150         nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3151         nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3152         ReleaseDC(NULL, hdc);
3153     }
3154 #endif
3155
3156     return AppInit(argc, argv);
3157 }
3158
3159 int CMyApp::OnExit()
3160 {
3161     Shutdown(NULL);
3162     return wxApp::OnExit();
3163 }
3164
3165 bool CMyApp::OnExceptionInMainLoop()
3166 {
3167     try
3168     {
3169         throw;
3170     }
3171     catch (std::exception& e)
3172     {
3173         PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3174         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3175         Sleep(1000);
3176         throw;
3177     }
3178     catch (...)
3179     {
3180         PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3181         wxLogWarning("Unknown exception");
3182         Sleep(1000);
3183         throw;
3184     }
3185     return true;
3186 }
3187
3188 void CMyApp::OnUnhandledException()
3189 {
3190     // this shows how we may let some exception propagate uncaught
3191     try
3192     {
3193         throw;
3194     }
3195     catch (std::exception& e)
3196     {
3197         PrintException(&e, "CMyApp::OnUnhandledException()");
3198         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3199         Sleep(1000);
3200         throw;
3201     }
3202     catch (...)
3203     {
3204         PrintException(NULL, "CMyApp::OnUnhandledException()");
3205         wxLogWarning("Unknown exception");
3206         Sleep(1000);
3207         throw;
3208     }
3209 }
3210
3211 void CMyApp::OnFatalException()
3212 {
3213     wxMessageBox(_("Program has crashed and will terminate.  "), "Bitcoin", wxOK | wxICON_ERROR);
3214 }