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