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