Fix rpc-hanging deadlocks
[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_mapAddressBook)
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_mapAddressBook)
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->GetEventHandler()->AddPendingEvent(event);
1036     }
1037 }
1038
1039 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
1040 {
1041     // Skip lets the listctrl do the paint, we're just hooking the message
1042     event.Skip();
1043
1044     if (ptaskbaricon)
1045         ptaskbaricon->UpdateTooltip();
1046
1047     //
1048     // Slower stuff
1049     //
1050     static int nTransactionCount;
1051     bool fPaintedBalance = false;
1052     if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1053     {
1054         nLastRepaint = nNeedRepaint;
1055         nLastRepaintTime = GetTimeMillis();
1056
1057         // Update listctrl contents
1058         if (!pwalletMain->vWalletUpdated.empty())
1059         {
1060             TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
1061             {
1062                 string strTop;
1063                 if (m_listCtrl->GetItemCount())
1064                     strTop = (string)m_listCtrl->GetItemText(0);
1065                 BOOST_FOREACH(uint256 hash, pwalletMain->vWalletUpdated)
1066                 {
1067                     map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1068                     if (mi != pwalletMain->mapWallet.end())
1069                         InsertTransaction((*mi).second, false);
1070                 }
1071                 pwalletMain->vWalletUpdated.clear();
1072                 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1073                     m_listCtrl->ScrollList(0, INT_MIN/2);
1074             }
1075         }
1076
1077         // Balance total
1078         TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
1079         {
1080             fPaintedBalance = true;
1081             m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + "  ");
1082
1083             // Count hidden and multi-line transactions
1084             nTransactionCount = 0;
1085             for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1086             {
1087                 CWalletTx& wtx = (*it).second;
1088                 nTransactionCount += wtx.nLinesDisplayed;
1089             }
1090         }
1091     }
1092     if (!pwalletMain->vWalletUpdated.empty() || !fPaintedBalance)
1093         nNeedRepaint++;
1094
1095     // Update status column of visible items only
1096     RefreshStatusColumn();
1097
1098     // Update status bar
1099     static string strPrevWarning;
1100     string strWarning = GetWarnings("statusbar");
1101     if (strWarning != "")
1102         m_statusBar->SetStatusText(string("    ") + _(strWarning), 0);
1103     else if (strPrevWarning != "")
1104         m_statusBar->SetStatusText("", 0);
1105     strPrevWarning = strWarning;
1106
1107     string strGen = "";
1108     if (fGenerateBitcoins)
1109         strGen = _("    Generating");
1110     if (fGenerateBitcoins && vNodes.empty())
1111         strGen = _("(not connected)");
1112     m_statusBar->SetStatusText(strGen, 1);
1113
1114     string strStatus = strprintf(_("     %d connections     %d blocks     %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1115     m_statusBar->SetStatusText(strStatus, 2);
1116
1117     // Update receiving address
1118     string strDefaultAddress = CBitcoinAddress(pwalletMain->vchDefaultKey).ToString();
1119     if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1120         m_textCtrlAddress->SetValue(strDefaultAddress);
1121 }
1122
1123
1124 void UIThreadCall(boost::function0<void> fn)
1125 {
1126     // Call this with a function object created with bind.
1127     // bind needs all parameters to match the function's expected types
1128     // and all default parameters specified.  Some examples:
1129     //  UIThreadCall(bind(wxBell));
1130     //  UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1131     //  UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1132     if (pframeMain)
1133     {
1134         wxCommandEvent event(wxEVT_UITHREADCALL);
1135         event.SetClientData((void*)new boost::function0<void>(fn));
1136         pframeMain->GetEventHandler()->AddPendingEvent(event);
1137     }
1138 }
1139
1140 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1141 {
1142     boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1143     (*pfn)();
1144     delete pfn;
1145 }
1146
1147 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1148 {
1149     // File->Exit
1150     Close(true);
1151 }
1152
1153 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1154 {
1155     event.Check(fGenerateBitcoins);
1156 }
1157
1158 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1159 {
1160     // Options->Your Receiving Addresses
1161     CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1162     if (!dialog.ShowModal())
1163         return;
1164 }
1165
1166 void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event)
1167 {
1168     // Options->Encrypt Wallet
1169     if (pwalletMain->IsCrypted())
1170     {
1171         wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR);
1172         return;
1173     }
1174
1175     string strWalletPass;
1176     strWalletPass.reserve(100);
1177     mlock(&strWalletPass[0], strWalletPass.capacity());
1178
1179     // obtain current wallet encrypt/decrypt key, from passphrase
1180     // Note that the passphrase is not mlock()d during this entry and could potentially
1181     // be obtained from disk long after bitcoin has run.
1182     strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."),
1183                                           _("Passphrase")).ToStdString();
1184
1185     if (!strWalletPass.size())
1186     {
1187         fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1188         munlock(&strWalletPass[0], strWalletPass.capacity());
1189         wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1190         return;
1191     }
1192
1193     if(wxMessageBox(_("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"), "Bitcoin", wxYES_NO) != wxYES)
1194         return;
1195
1196     string strWalletPassTest;
1197     strWalletPassTest.reserve(100);
1198     mlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1199     strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."),
1200                                               _("Passphrase")).ToStdString();
1201
1202     if (strWalletPassTest != strWalletPass)
1203     {
1204         fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1205         fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1206         munlock(&strWalletPass[0], strWalletPass.capacity());
1207         munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1208         wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1209         return;
1210     }
1211
1212     if (!pwalletMain->EncryptWallet(strWalletPass))
1213     {
1214         fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1215         fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1216         munlock(&strWalletPass[0], strWalletPass.capacity());
1217         munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1218         wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR);
1219         return;
1220     }
1221     fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1222     fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1223     munlock(&strWalletPass[0], strWalletPass.capacity());
1224     munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1225     wxMessageBox(_("Wallet Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1226
1227     m_menuOptions->Remove(m_menuOptionsEncryptWallet);
1228     m_menuOptions->Insert(m_menuOptions->GetMenuItemCount() - 1, m_menuOptionsChangeWalletPassphrase);
1229 }
1230
1231 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1232 {
1233     // Options->Change Wallet Encryption Passphrase
1234     if (!pwalletMain->IsCrypted())
1235     {
1236         wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1237         return;
1238     }
1239
1240     string strOldWalletPass;
1241     strOldWalletPass.reserve(100);
1242     mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1243
1244     // obtain current wallet encrypt/decrypt key, from passphrase
1245     // Note that the passphrase is not mlock()d during this entry and could potentially
1246     // be obtained from disk long after bitcoin has run.
1247     strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1248                                              _("Passphrase")).ToStdString();
1249
1250     CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1251     {
1252         bool fWasLocked = pwalletMain->IsLocked();
1253         pwalletMain->Lock();
1254
1255         if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1256         {
1257             fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1258             munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1259             wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1260             return;
1261         }
1262
1263         if (fWasLocked)
1264             pwalletMain->Lock();
1265
1266         string strNewWalletPass;
1267         strNewWalletPass.reserve(100);
1268         mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1269
1270         // obtain new wallet encrypt/decrypt key, from passphrase
1271         // Note that the passphrase is not mlock()d during this entry and could potentially
1272         // be obtained from disk long after bitcoin has run.
1273         strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1274                                                  _("Passphrase")).ToStdString();
1275
1276         if (!strNewWalletPass.size())
1277         {
1278             fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1279             fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1280             munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1281             munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1282             wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1283             return;
1284         }
1285
1286         string strNewWalletPassTest;
1287         strNewWalletPassTest.reserve(100);
1288         mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1289
1290         // obtain new wallet encrypt/decrypt key, from passphrase
1291         // Note that the passphrase is not mlock()d during this entry and could potentially
1292         // be obtained from disk long after bitcoin has run.
1293         strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1294                                                      _("Passphrase")).ToStdString();
1295
1296         if (strNewWalletPassTest != strNewWalletPass)
1297         {
1298             fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1299             fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1300             fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1301             munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1302             munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1303             munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1304             wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1305             return;
1306         }
1307
1308         if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1309         {
1310             fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1311             fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1312             fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1313             munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1314             munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1315             munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1316             wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1317             return;
1318         }
1319         fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1320         fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1321         fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1322         munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1323         munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1324         munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1325         wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
1326     }
1327 }
1328
1329 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1330 {
1331     // Options->Options
1332     COptionsDialog dialog(this);
1333     dialog.ShowModal();
1334 }
1335
1336 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1337 {
1338     // Help->About
1339     CAboutDialog dialog(this);
1340     dialog.ShowModal();
1341 }
1342
1343 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1344 {
1345     // Toolbar: Send
1346     CSendDialog dialog(this);
1347     dialog.ShowModal();
1348 }
1349
1350 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1351 {
1352     // Toolbar: Address Book
1353     CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1354     if (dialogAddr.ShowModal() == 2)
1355     {
1356         // Send
1357         CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1358         dialogSend.ShowModal();
1359     }
1360 }
1361
1362 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1363 {
1364     // Automatically select-all when entering window
1365     event.Skip();
1366     m_textCtrlAddress->SetSelection(-1, -1);
1367     fOnSetFocusAddress = true;
1368 }
1369
1370 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1371 {
1372     event.Skip();
1373     if (fOnSetFocusAddress)
1374         m_textCtrlAddress->SetSelection(-1, -1);
1375     fOnSetFocusAddress = false;
1376 }
1377
1378 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1379 {
1380     // Ask name
1381     CGetTextFromUserDialog dialog(this,
1382         _("New Receiving Address"),
1383         _("You should use a new address for each payment you receive.\n\nLabel"),
1384         "");
1385     if (!dialog.ShowModal())
1386         return;
1387     string strName = dialog.GetValue();
1388
1389     string strAddress;
1390     CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1391     {
1392         bool fWasLocked = pwalletMain->IsLocked();
1393         if (!GetWalletPassphrase())
1394             return;
1395
1396         // Generate new key
1397         strAddress = CBitcoinAddress(pwalletMain->GetOrReuseKeyFromPool()).ToString();
1398
1399         if (fWasLocked)
1400             pwalletMain->Lock();
1401     }
1402
1403     // Save
1404     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1405         pwalletMain->SetAddressBookName(strAddress, strName);
1406     SetDefaultReceivingAddress(strAddress);
1407 }
1408
1409 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1410 {
1411     // Copy address box to clipboard
1412     if (wxTheClipboard->Open())
1413     {
1414         wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1415         wxTheClipboard->Close();
1416     }
1417 }
1418
1419 void CMainFrame::OnListItemActivated(wxListEvent& event)
1420 {
1421     uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1422     CWalletTx wtx;
1423     CRITICAL_BLOCK(pwalletMain->cs_wallet)
1424     {
1425         map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1426         if (mi == pwalletMain->mapWallet.end())
1427         {
1428             printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1429             return;
1430         }
1431         wtx = (*mi).second;
1432     }
1433     CTxDetailsDialog dialog(this, wtx);
1434     dialog.ShowModal();
1435     //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1436     //pdialog->Show();
1437 }
1438
1439
1440
1441
1442
1443
1444 //////////////////////////////////////////////////////////////////////////////
1445 //
1446 // CTxDetailsDialog
1447 //
1448
1449 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1450 {
1451 #ifdef __WXMSW__
1452     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1453 #endif
1454     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1455     {
1456         string strHTML;
1457         strHTML.reserve(4000);
1458         strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1459
1460         int64 nTime = wtx.GetTxTime();
1461         int64 nCredit = wtx.GetCredit();
1462         int64 nDebit = wtx.GetDebit();
1463         int64 nNet = nCredit - nDebit;
1464
1465
1466
1467         strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1468         int nRequests = wtx.GetRequestCount();
1469         if (nRequests != -1)
1470         {
1471             if (nRequests == 0)
1472                 strHTML += _(", has not been successfully broadcast yet");
1473             else if (nRequests == 1)
1474                 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1475             else
1476                 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1477         }
1478         strHTML += "<br>";
1479
1480         strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1481
1482
1483         //
1484         // From
1485         //
1486         if (wtx.IsCoinBase())
1487         {
1488             strHTML += _("<b>Source:</b> Generated<br>");
1489         }
1490         else if (!wtx.mapValue["from"].empty())
1491         {
1492             // Online transaction
1493             if (!wtx.mapValue["from"].empty())
1494                 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1495         }
1496         else
1497         {
1498             // Offline transaction
1499             if (nNet > 0)
1500             {
1501                 // Credit
1502                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1503                 {
1504                     if (pwalletMain->IsMine(txout))
1505                     {
1506                         CBitcoinAddress address;
1507                         if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1508                         {
1509                             if (pwalletMain->mapAddressBook.count(address))
1510                             {
1511                                 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1512                                 strHTML += _("<b>To:</b> ");
1513                                 strHTML += HtmlEscape(address.ToString());
1514                                 if (!pwalletMain->mapAddressBook[address].empty())
1515                                     strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[address] + ")";
1516                                 else
1517                                     strHTML += _(" (yours)");
1518                                 strHTML += "<br>";
1519                             }
1520                         }
1521                         break;
1522                     }
1523                 }
1524             }
1525         }
1526
1527
1528         //
1529         // To
1530         //
1531         string strAddress;
1532         if (!wtx.mapValue["to"].empty())
1533         {
1534             // Online transaction
1535             strAddress = wtx.mapValue["to"];
1536             strHTML += _("<b>To:</b> ");
1537             if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1538                 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1539             strHTML += HtmlEscape(strAddress) + "<br>";
1540         }
1541
1542
1543         //
1544         // Amount
1545         //
1546         if (wtx.IsCoinBase() && nCredit == 0)
1547         {
1548             //
1549             // Coinbase
1550             //
1551             int64 nUnmatured = 0;
1552             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1553                 nUnmatured += pwalletMain->GetCredit(txout);
1554             strHTML += _("<b>Credit:</b> ");
1555             if (wtx.IsInMainChain())
1556                 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1557             else
1558                 strHTML += _("(not accepted)");
1559             strHTML += "<br>";
1560         }
1561         else if (nNet > 0)
1562         {
1563             //
1564             // Credit
1565             //
1566             strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1567         }
1568         else
1569         {
1570             bool fAllFromMe = true;
1571             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1572                 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1573
1574             bool fAllToMe = true;
1575             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1576                 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1577
1578             if (fAllFromMe)
1579             {
1580                 //
1581                 // Debit
1582                 //
1583                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1584                 {
1585                     if (pwalletMain->IsMine(txout))
1586                         continue;
1587
1588                     if (wtx.mapValue["to"].empty())
1589                     {
1590                         // Offline transaction
1591                         CBitcoinAddress address;
1592                         if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1593                         {
1594                             string strAddress = address.ToString();
1595                             strHTML += _("<b>To:</b> ");
1596                             if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
1597                                 strHTML += pwalletMain->mapAddressBook[address] + " ";
1598                             strHTML += strAddress;
1599                             strHTML += "<br>";
1600                         }
1601                     }
1602
1603                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1604                 }
1605
1606                 if (fAllToMe)
1607                 {
1608                     // Payment to self
1609                     int64 nChange = wtx.GetChange();
1610                     int64 nValue = nCredit - nChange;
1611                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1612                     strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1613                 }
1614
1615                 int64 nTxFee = nDebit - wtx.GetValueOut();
1616                 if (nTxFee > 0)
1617                     strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1618             }
1619             else
1620             {
1621                 //
1622                 // Mixed debit transaction
1623                 //
1624                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1625                     if (pwalletMain->IsMine(txin))
1626                         strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1627                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1628                     if (pwalletMain->IsMine(txout))
1629                         strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1630             }
1631         }
1632
1633         strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1634
1635
1636         //
1637         // Message
1638         //
1639         if (!wtx.mapValue["message"].empty())
1640             strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1641         if (!wtx.mapValue["comment"].empty())
1642             strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1643
1644         if (wtx.IsCoinBase())
1645             strHTML += string() + "<br>" + _("Generated coins must wait 120 blocks before they can be spent.  When you generated this block, it was broadcast to the network to be added to the block chain.  If it fails to get into the chain, it will change to \"not accepted\" and not be spendable.  This may occasionally happen if another node generates a block within a few seconds of yours.") + "<br>";
1646
1647
1648         //
1649         // Debug view
1650         //
1651         if (fDebug)
1652         {
1653             strHTML += "<hr><br>debug print<br><br>";
1654             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1655                 if (pwalletMain->IsMine(txin))
1656                     strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1657             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1658                 if (pwalletMain->IsMine(txout))
1659                     strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1660
1661             strHTML += "<br><b>Transaction:</b><br>";
1662             strHTML += HtmlEscape(wtx.ToString(), true);
1663
1664             strHTML += "<br><b>Inputs:</b><br>";
1665             CRITICAL_BLOCK(pwalletMain->cs_wallet)
1666             {
1667                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1668                 {
1669                     COutPoint prevout = txin.prevout;
1670                     map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1671                     if (mi != pwalletMain->mapWallet.end())
1672                     {
1673                         const CWalletTx& prev = (*mi).second;
1674                         if (prevout.n < prev.vout.size())
1675                         {
1676                             strHTML += HtmlEscape(prev.ToString(), true);
1677                             strHTML += " &nbsp;&nbsp; " + FormatTxStatus(prev) + ", ";
1678                             strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1679                         }
1680                     }
1681                 }
1682             }
1683         }
1684
1685
1686
1687         strHTML += "</font></html>";
1688         string(strHTML.begin(), strHTML.end()).swap(strHTML);
1689         m_htmlWin->SetPage(strHTML);
1690         m_buttonOK->SetFocus();
1691     }
1692 }
1693
1694 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1695 {
1696     EndModal(false);
1697 }
1698
1699
1700
1701
1702
1703
1704 //////////////////////////////////////////////////////////////////////////////
1705 //
1706 // Startup folder
1707 //
1708
1709 #ifdef __WXMSW__
1710 string StartupShortcutPath()
1711 {
1712     return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1713 }
1714
1715 bool GetStartOnSystemStartup()
1716 {
1717     return filesystem::exists(StartupShortcutPath().c_str());
1718 }
1719
1720 void SetStartOnSystemStartup(bool fAutoStart)
1721 {
1722     // If the shortcut exists already, remove it for updating
1723     remove(StartupShortcutPath().c_str());
1724
1725     if (fAutoStart)
1726     {
1727         CoInitialize(NULL);
1728
1729         // Get a pointer to the IShellLink interface.
1730         IShellLink* psl = NULL;
1731         HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1732                                 CLSCTX_INPROC_SERVER, IID_IShellLink,
1733                                 reinterpret_cast<void**>(&psl));
1734
1735         if (SUCCEEDED(hres))
1736         {
1737             // Get the current executable path
1738             TCHAR pszExePath[MAX_PATH];
1739             GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1740
1741             // Set the path to the shortcut target
1742             psl->SetPath(pszExePath);
1743             PathRemoveFileSpec(pszExePath);
1744             psl->SetWorkingDirectory(pszExePath);
1745             psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1746
1747             // Query IShellLink for the IPersistFile interface for
1748             // saving the shortcut in persistent storage.
1749             IPersistFile* ppf = NULL;
1750             hres = psl->QueryInterface(IID_IPersistFile,
1751                                        reinterpret_cast<void**>(&ppf));
1752             if (SUCCEEDED(hres))
1753             {
1754                 WCHAR pwsz[MAX_PATH];
1755                 // Ensure that the string is ANSI.
1756                 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1757                 // Save the link by calling IPersistFile::Save.
1758                 hres = ppf->Save(pwsz, TRUE);
1759                 ppf->Release();
1760             }
1761             psl->Release();
1762         }
1763         CoUninitialize();
1764     }
1765 }
1766
1767 #elif defined(__WXGTK__)
1768
1769 // Follow the Desktop Application Autostart Spec:
1770 //  http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1771
1772 boost::filesystem::path GetAutostartDir()
1773 {
1774     namespace fs = boost::filesystem;
1775
1776     char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1777     if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1778     char* pszHome = getenv("HOME");
1779     if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1780     return fs::path();
1781 }
1782
1783 boost::filesystem::path GetAutostartFilePath()
1784 {
1785     return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1786 }
1787
1788 bool GetStartOnSystemStartup()
1789 {
1790     boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1791     if (!optionFile.good())
1792         return false;
1793     // Scan through file for "Hidden=true":
1794     string line;
1795     while (!optionFile.eof())
1796     {
1797         getline(optionFile, line);
1798         if (line.find("Hidden") != string::npos &&
1799             line.find("true") != string::npos)
1800             return false;
1801     }
1802     optionFile.close();
1803
1804     return true;
1805 }
1806
1807 void SetStartOnSystemStartup(bool fAutoStart)
1808 {
1809     if (!fAutoStart)
1810     {
1811         unlink(GetAutostartFilePath().native_file_string().c_str());
1812     }
1813     else
1814     {
1815         char pszExePath[MAX_PATH+1];
1816         memset(pszExePath, 0, sizeof(pszExePath));
1817         if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1818             return;
1819
1820         boost::filesystem::create_directories(GetAutostartDir());
1821
1822         boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1823         if (!optionFile.good())
1824         {
1825             wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1826             return;
1827         }
1828         // Write a bitcoin.desktop file to the autostart directory:
1829         optionFile << "[Desktop Entry]\n";
1830         optionFile << "Type=Application\n";
1831         optionFile << "Name=Bitcoin\n";
1832         optionFile << "Exec=" << pszExePath << "\n";
1833         optionFile << "Terminal=false\n";
1834         optionFile << "Hidden=false\n";
1835         optionFile.close();
1836     }
1837 }
1838 #else
1839
1840 // TODO: OSX startup stuff; see:
1841 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1842
1843 bool GetStartOnSystemStartup() { return false; }
1844 void SetStartOnSystemStartup(bool fAutoStart) { }
1845
1846 #endif
1847
1848
1849
1850
1851
1852
1853 //////////////////////////////////////////////////////////////////////////////
1854 //
1855 // COptionsDialog
1856 //
1857
1858 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1859 {
1860     // Set up list box of page choices
1861     m_listBox->Append(_("Main"));
1862     //m_listBox->Append(_("Test 2"));
1863     m_listBox->SetSelection(0);
1864     SelectPage(0);
1865 #ifndef __WXMSW__
1866     SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1867 #else
1868     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1869 #endif
1870 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1871     m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1872     if (!GetBoolArg("-minimizetotray"))
1873     {
1874         // Minimize to tray is just too buggy on Linux
1875         fMinimizeToTray = false;
1876         m_checkBoxMinimizeToTray->SetValue(false);
1877         m_checkBoxMinimizeToTray->Enable(false);
1878         m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1879     }
1880 #endif
1881 #ifdef __WXMAC_OSX__
1882     m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1883 #endif
1884
1885     // Init values
1886     m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1887     m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1888     m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1889     m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1890     if (fHaveUPnP)
1891         m_checkBoxUseUPnP->SetValue(fUseUPnP);
1892     else
1893         m_checkBoxUseUPnP->Enable(false);
1894     m_checkBoxUseProxy->SetValue(fUseProxy);
1895     m_textCtrlProxyIP->Enable(fUseProxy);
1896     m_textCtrlProxyPort->Enable(fUseProxy);
1897     m_staticTextProxyIP->Enable(fUseProxy);
1898     m_staticTextProxyPort->Enable(fUseProxy);
1899     m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1900     m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1901
1902     m_buttonOK->SetFocus();
1903 }
1904
1905 void COptionsDialog::SelectPage(int nPage)
1906 {
1907     m_panelMain->Show(nPage == 0);
1908     m_panelTest2->Show(nPage == 1);
1909
1910     m_scrolledWindow->Layout();
1911     m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1912 }
1913
1914 void COptionsDialog::OnListBox(wxCommandEvent& event)
1915 {
1916     SelectPage(event.GetSelection());
1917 }
1918
1919 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1920 {
1921     event.Skip();
1922     int64 nTmp = nTransactionFee;
1923     ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1924     m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1925 }
1926
1927 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1928 {
1929     m_textCtrlProxyIP->Enable(event.IsChecked());
1930     m_textCtrlProxyPort->Enable(event.IsChecked());
1931     m_staticTextProxyIP->Enable(event.IsChecked());
1932     m_staticTextProxyPort->Enable(event.IsChecked());
1933 }
1934
1935 CAddress COptionsDialog::GetProxyAddr()
1936 {
1937     // Be careful about byte order, addr.ip and addr.port are big endian
1938     CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1939     if (addr.ip == INADDR_NONE)
1940         addr.ip = addrProxy.ip;
1941     int nPort = atoi(m_textCtrlProxyPort->GetValue());
1942     addr.port = htons(nPort);
1943     if (nPort <= 0 || nPort > USHRT_MAX)
1944         addr.port = addrProxy.port;
1945     return addr;
1946 }
1947
1948 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1949 {
1950     event.Skip();
1951     m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1952     m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1953 }
1954
1955
1956 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1957 {
1958     OnButtonApply(event);
1959     EndModal(false);
1960 }
1961
1962 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1963 {
1964     EndModal(false);
1965 }
1966
1967 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1968 {
1969     CWalletDB walletdb(pwalletMain->strWalletFile);
1970
1971     int64 nPrevTransactionFee = nTransactionFee;
1972     if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1973         walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1974
1975     if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1976     {
1977         fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1978         SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1979     }
1980
1981     if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1982     {
1983         fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1984         walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1985         ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1986     }
1987
1988     if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1989     {
1990         fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1991         walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1992     }
1993
1994     if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1995     {
1996         fUseUPnP = m_checkBoxUseUPnP->GetValue();
1997         walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1998         MapPort(fUseUPnP);
1999     }
2000
2001     fUseProxy = m_checkBoxUseProxy->GetValue();
2002     walletdb.WriteSetting("fUseProxy", fUseProxy);
2003
2004     addrProxy = GetProxyAddr();
2005     walletdb.WriteSetting("addrProxy", addrProxy);
2006 }
2007
2008
2009
2010
2011
2012
2013 //////////////////////////////////////////////////////////////////////////////
2014 //
2015 // CAboutDialog
2016 //
2017
2018 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2019 {
2020     m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2021
2022     // Change (c) into UTF-8 or ANSI copyright symbol
2023     wxString str = m_staticTextMain->GetLabel();
2024 #if wxUSE_UNICODE
2025     str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2026 #else
2027     str.Replace("(c)", "\xA9");
2028 #endif
2029     m_staticTextMain->SetLabel(str);
2030 #ifndef __WXMSW__
2031     // Resize on Linux to make the window fit the text.
2032     // The text was wrapped manually rather than using the Wrap setting because
2033     // the wrap would be too small on Linux and it can't be changed at this point.
2034     wxFont fontTmp = m_staticTextMain->GetFont();
2035     if (fontTmp.GetPointSize() > 8);
2036         fontTmp.SetPointSize(8);
2037     m_staticTextMain->SetFont(fontTmp);
2038     SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2039 #else
2040     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2041 #endif
2042 }
2043
2044 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2045 {
2046     EndModal(false);
2047 }
2048
2049
2050
2051
2052
2053
2054 //////////////////////////////////////////////////////////////////////////////
2055 //
2056 // CSendDialog
2057 //
2058
2059 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2060 {
2061     // Init
2062     m_textCtrlAddress->SetValue(strAddress);
2063     m_choiceTransferType->SetSelection(0);
2064     m_bitmapCheckMark->Show(false);
2065     fEnabledPrev = true;
2066     m_textCtrlAddress->SetFocus();
2067     
2068     //// todo: should add a display of your balance for convenience
2069 #ifndef __WXMSW__
2070     wxFont fontTmp = m_staticTextInstructions->GetFont();
2071     if (fontTmp.GetPointSize() > 9);
2072         fontTmp.SetPointSize(9);
2073     m_staticTextInstructions->SetFont(fontTmp);
2074     SetSize(725, 180);
2075 #else
2076     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2077 #endif
2078     
2079     // Set Icon
2080     if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2081     {
2082         wxIcon iconSend;
2083         iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2084         SetIcon(iconSend);
2085     }
2086 #ifdef __WXMSW__
2087     else
2088         SetIcon(wxICON(bitcoin));
2089 #endif
2090
2091     // Fixup the tab order
2092     m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2093     m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2094     this->Layout();
2095 }
2096
2097 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2098 {
2099     // Reformat the amount
2100     event.Skip();
2101     if (m_textCtrlAmount->GetValue().Trim().empty())
2102         return;
2103     int64 nTmp;
2104     if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2105         m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2106 }
2107
2108 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2109 {
2110     // Open address book
2111     CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2112     if (dialog.ShowModal())
2113         m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2114 }
2115
2116 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2117 {
2118     // Copy clipboard to address box
2119     if (wxTheClipboard->Open())
2120     {
2121         if (wxTheClipboard->IsSupported(wxDF_TEXT))
2122         {
2123             wxTextDataObject data;
2124             wxTheClipboard->GetData(data);
2125             m_textCtrlAddress->SetValue(data.GetText());
2126         }
2127         wxTheClipboard->Close();
2128     }
2129 }
2130
2131 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2132 {
2133     static CCriticalSection cs_sendlock;
2134     TRY_CRITICAL_BLOCK(cs_sendlock)
2135     {
2136         CWalletTx wtx;
2137         string strAddress = (string)m_textCtrlAddress->GetValue();
2138
2139         // Parse amount
2140         int64 nValue = 0;
2141         if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2142         {
2143             wxMessageBox(_("Error in amount  "), _("Send Coins"));
2144             return;
2145         }
2146         if (nValue > pwalletMain->GetBalance())
2147         {
2148             wxMessageBox(_("Amount exceeds your balance  "), _("Send Coins"));
2149             return;
2150         }
2151         if (nValue + nTransactionFee > pwalletMain->GetBalance())
2152         {
2153             wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included  "), _("Send Coins"));
2154             return;
2155         }
2156
2157         // Parse bitcoin address
2158         CBitcoinAddress address(strAddress);
2159         bool fBitcoinAddress = address.IsValid();
2160
2161         if (fBitcoinAddress)
2162         {
2163             CRITICAL_BLOCK(cs_main)
2164             CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2165             {
2166                 bool fWasLocked = pwalletMain->IsLocked();
2167                 if (!GetWalletPassphrase())
2168                     return;
2169
2170                 // Send to bitcoin address
2171                 CScript scriptPubKey;
2172                 scriptPubKey.SetBitcoinAddress(address);
2173
2174                 string strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2175                 if (strError == "")
2176                     wxMessageBox(_("Payment sent  "), _("Sending..."));
2177                 else if (strError == "ABORTED")
2178                 {
2179                     if (fWasLocked)
2180                         pwalletMain->Lock();
2181                     return; // leave send dialog open
2182                 }
2183                 else
2184                 {
2185                     wxMessageBox(strError + "  ", _("Sending..."));
2186                     EndModal(false);
2187                     if (fWasLocked)
2188                         pwalletMain->Lock();
2189                     return;
2190                 }
2191
2192                 if (fWasLocked)
2193                     pwalletMain->Lock();
2194             }
2195         }
2196         else
2197         {
2198             // Parse IP address
2199             CAddress addr(strAddress);
2200             if (!addr.IsValid())
2201             {
2202                 wxMessageBox(_("Invalid address  "), _("Send Coins"));
2203                 return;
2204             }
2205
2206             // Message
2207             wtx.mapValue["to"] = strAddress;
2208
2209             // Send to IP address
2210             CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2211             if (!pdialog->ShowModal())
2212                 return;
2213         }
2214
2215         CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2216             if (!pwalletMain->mapAddressBook.count(address))
2217                 pwalletMain->SetAddressBookName(strAddress, "");
2218
2219         EndModal(true);
2220     }
2221 }
2222
2223 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2224 {
2225     // Cancel
2226     EndModal(false);
2227 }
2228
2229
2230
2231
2232
2233
2234 //////////////////////////////////////////////////////////////////////////////
2235 //
2236 // CSendingDialog
2237 //
2238
2239 CSendingDialog::CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 nPriceIn, const CWalletTx& wtxIn) : CSendingDialogBase(NULL) // we have to give null so parent can't destroy us
2240 {
2241     addr = addrIn;
2242     nPrice = nPriceIn;
2243     wtx = wtxIn;
2244     start = wxDateTime::UNow();
2245     memset(pszStatus, 0, sizeof(pszStatus));
2246     fCanCancel = true;
2247     fAbort = false;
2248     fSuccess = false;
2249     fUIDone = false;
2250     fWorkDone = false;
2251 #ifndef __WXMSW__
2252     SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2253 #else
2254     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2255 #endif
2256
2257     SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2258     m_textCtrlStatus->SetValue("");
2259
2260     CreateThread(SendingDialogStartTransfer, this);
2261 }
2262
2263 CSendingDialog::~CSendingDialog()
2264 {
2265     printf("~CSendingDialog()\n");
2266 }
2267
2268 void CSendingDialog::Close()
2269 {
2270     // Last one out turn out the lights.
2271     // fWorkDone signals that work side is done and UI thread should call destroy.
2272     // fUIDone signals that UI window has closed and work thread should call destroy.
2273     // This allows the window to disappear and end modality when cancelled
2274     // without making the user wait for ConnectNode to return.  The dialog object
2275     // hangs around in the background until the work thread exits.
2276     if (IsModal())
2277         EndModal(fSuccess);
2278     else
2279         Show(false);
2280     if (fWorkDone)
2281         Destroy();
2282     else
2283         fUIDone = true;
2284 }
2285
2286 void CSendingDialog::OnClose(wxCloseEvent& event)
2287 {
2288     if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2289     {
2290         Close();
2291     }
2292     else
2293     {
2294         event.Veto();
2295         wxCommandEvent cmdevent;
2296         OnButtonCancel(cmdevent);
2297     }
2298 }
2299
2300 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2301 {
2302     if (fWorkDone)
2303         Close();
2304 }
2305
2306 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2307 {
2308     if (fCanCancel)
2309         fAbort = true;
2310 }
2311
2312 void CSendingDialog::OnPaint(wxPaintEvent& event)
2313 {
2314     event.Skip();
2315     if (strlen(pszStatus) > 130)
2316         m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2317     else
2318         m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2319     m_staticTextSending->SetFocus();
2320     if (!fCanCancel)
2321         m_buttonCancel->Enable(false);
2322     if (fWorkDone)
2323     {
2324         m_buttonOK->Enable(true);
2325         m_buttonOK->SetFocus();
2326         m_buttonCancel->Enable(false);
2327     }
2328     if (fAbort && fCanCancel && IsShown())
2329     {
2330         strcpy(pszStatus, _("CANCELLED"));
2331         m_buttonOK->Enable(true);
2332         m_buttonOK->SetFocus();
2333         m_buttonCancel->Enable(false);
2334         m_buttonCancel->SetLabel(_("Cancelled"));
2335         Close();
2336         wxMessageBox(_("Transfer cancelled  "), _("Sending..."), wxOK, this);
2337     }
2338 }
2339
2340
2341 //
2342 // Everything from here on is not in the UI thread and must only communicate
2343 // with the rest of the dialog through variables and calling repaint.
2344 //
2345
2346 void CSendingDialog::Repaint()
2347 {
2348     Refresh();
2349     wxPaintEvent event;
2350     GetEventHandler()->AddPendingEvent(event);
2351 }
2352
2353 bool CSendingDialog::Status()
2354 {
2355     if (fUIDone)
2356     {
2357         Destroy();
2358         return false;
2359     }
2360     if (fAbort && fCanCancel)
2361     {
2362         memset(pszStatus, 0, 10);
2363         strcpy(pszStatus, _("CANCELLED"));
2364         Repaint();
2365         fWorkDone = true;
2366         return false;
2367     }
2368     return true;
2369 }
2370
2371 bool CSendingDialog::Status(const string& str)
2372 {
2373     if (!Status())
2374         return false;
2375
2376     // This can be read by the UI thread at any time,
2377     // so copy in a way that can be read cleanly at all times.
2378     memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2379     strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2380
2381     Repaint();
2382     return true;
2383 }
2384
2385 bool CSendingDialog::Error(const string& str)
2386 {
2387     fCanCancel = false;
2388     fWorkDone = true;
2389     Status(string(_("Error: ")) + str);
2390     return false;
2391 }
2392
2393 void SendingDialogStartTransfer(void* parg)
2394 {
2395     ((CSendingDialog*)parg)->StartTransfer();
2396 }
2397
2398 void CSendingDialog::StartTransfer()
2399 {
2400     // Make sure we have enough money
2401     if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2402     {
2403         Error(_("Insufficient funds"));
2404         return;
2405     }
2406
2407     // We may have connected already for product details
2408     if (!Status(_("Connecting...")))
2409         return;
2410     CNode* pnode = ConnectNode(addr, 15 * 60);
2411     if (!pnode)
2412     {
2413         Error(_("Unable to connect"));
2414         return;
2415     }
2416
2417     // Send order to seller, with response going to OnReply2 via event handler
2418     if (!Status(_("Requesting public key...")))
2419         return;
2420     pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2421 }
2422
2423 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2424 {
2425     ((CSendingDialog*)parg)->OnReply2(vRecv);
2426 }
2427
2428 void CSendingDialog::OnReply2(CDataStream& vRecv)
2429 {
2430     if (!Status(_("Received public key...")))
2431         return;
2432
2433     CScript scriptPubKey;
2434     int nRet;
2435     try
2436     {
2437         vRecv >> nRet;
2438         if (nRet > 0)
2439         {
2440             string strMessage;
2441             if (!vRecv.empty())
2442                 vRecv >> strMessage;
2443             if (nRet == 2)
2444                 Error(_("Recipient is not accepting transactions sent by IP address"));
2445             else
2446                 Error(_("Transfer was not accepted"));
2447             //// todo: enlarge the window and enable a hidden white box to put seller's message
2448             return;
2449         }
2450         vRecv >> scriptPubKey;
2451     }
2452     catch (...)
2453     {
2454         //// what do we want to do about this?
2455         Error(_("Invalid response received"));
2456         return;
2457     }
2458
2459     // Pause to give the user a chance to cancel
2460     while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2461     {
2462         Sleep(200);
2463         if (!Status())
2464             return;
2465     }
2466
2467     CRITICAL_BLOCK(cs_main)
2468     {
2469         // Pay
2470         if (!Status(_("Creating transaction...")))
2471             return;
2472         if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2473         {
2474             Error(_("Insufficient funds"));
2475             return;
2476         }
2477
2478         CReserveKey reservekey(pwalletMain);
2479         int64 nFeeRequired;
2480         CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2481         {
2482             bool fWasLocked = pwalletMain->IsLocked();
2483             if (!GetWalletPassphrase())
2484                 return;
2485
2486             if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2487             {
2488                 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2489                     Error(strprintf(_("This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds"), FormatMoney(nFeeRequired).c_str()));
2490                 else
2491                     Error(_("Transaction creation failed"));
2492                 return;
2493             }
2494
2495             if (fWasLocked)
2496                 pwalletMain->Lock();
2497        }
2498
2499         // Transaction fee
2500         if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2501         {
2502             Error(_("Transaction aborted"));
2503             return;
2504         }
2505
2506         // Make sure we're still connected
2507         CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2508         if (!pnode)
2509         {
2510             Error(_("Lost connection, transaction cancelled"));
2511             return;
2512         }
2513
2514         // Last chance to cancel
2515         Sleep(50);
2516         if (!Status())
2517             return;
2518         fCanCancel = false;
2519         if (fAbort)
2520         {
2521             fCanCancel = true;
2522             if (!Status())
2523                 return;
2524             fCanCancel = false;
2525         }
2526         if (!Status(_("Sending payment...")))
2527             return;
2528
2529         // Commit
2530         if (!pwalletMain->CommitTransaction(wtx, reservekey))
2531         {
2532             Error(_("The transaction was rejected.  This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."));
2533             return;
2534         }
2535
2536         // Send payment tx to seller, with response going to OnReply3 via event handler
2537         CWalletTx wtxSend = wtx;
2538         wtxSend.fFromMe = false;
2539         pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2540
2541         Status(_("Waiting for confirmation..."));
2542         MainFrameRepaint();
2543     }
2544 }
2545
2546 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2547 {
2548     ((CSendingDialog*)parg)->OnReply3(vRecv);
2549 }
2550
2551 void CSendingDialog::OnReply3(CDataStream& vRecv)
2552 {
2553     int nRet;
2554     try
2555     {
2556         vRecv >> nRet;
2557         if (nRet > 0)
2558         {
2559             Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2560                     "The transaction is recorded and will credit to the recipient,\n"
2561                     "but the comment information will be blank."));
2562             return;
2563         }
2564     }
2565     catch (...)
2566     {
2567         //// what do we want to do about this?
2568         Error(_("Payment was sent, but an invalid response was received"));
2569         return;
2570     }
2571
2572     fSuccess = true;
2573     fWorkDone = true;
2574     Status(_("Payment completed"));
2575 }
2576
2577
2578
2579
2580
2581
2582 //////////////////////////////////////////////////////////////////////////////
2583 //
2584 // CAddressBookDialog
2585 //
2586
2587 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2588 {
2589 #ifdef __WXMSW__
2590     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2591 #endif
2592
2593     // Set initially selected page
2594     wxNotebookEvent event;
2595     event.SetSelection(nPageIn);
2596     OnNotebookPageChanged(event);
2597     m_notebook->ChangeSelection(nPageIn);
2598
2599     fDuringSend = fDuringSendIn;
2600     if (!fDuringSend)
2601         m_buttonCancel->Show(false);
2602
2603     // Set Icon
2604     if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2605     {
2606         wxIcon iconAddressBook;
2607         iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2608         SetIcon(iconAddressBook);
2609     }
2610 #ifdef __WXMSW__
2611     else
2612         SetIcon(wxICON(bitcoin));
2613 #endif
2614
2615     // Init column headers
2616     m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2617     m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2618     m_listCtrlSending->SetFocus();
2619     m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2620     m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2621     m_listCtrlReceiving->SetFocus();
2622
2623     // Fill listctrl with address book data
2624     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2625     {
2626         string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2627         BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2628         {
2629             const CBitcoinAddress& address = item.first;
2630             string strName = item.second;
2631             bool fMine = pwalletMain->HaveKey(address);
2632             wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2633             int nIndex = InsertLine(plistCtrl, strName, address.ToString());
2634             if (address.ToString() == (fMine ? strDefaultReceiving : string(strInitSelected)))
2635                 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2636         }
2637     }
2638 }
2639
2640 wxString CAddressBookDialog::GetSelectedAddress()
2641 {
2642     int nIndex = GetSelection(m_listCtrl);
2643     if (nIndex == -1)
2644         return "";
2645     return GetItemText(m_listCtrl, nIndex, 1);
2646 }
2647
2648 wxString CAddressBookDialog::GetSelectedSendingAddress()
2649 {
2650     int nIndex = GetSelection(m_listCtrlSending);
2651     if (nIndex == -1)
2652         return "";
2653     return GetItemText(m_listCtrlSending, nIndex, 1);
2654 }
2655
2656 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2657 {
2658     int nIndex = GetSelection(m_listCtrlReceiving);
2659     if (nIndex == -1)
2660         return "";
2661     return GetItemText(m_listCtrlReceiving, nIndex, 1);
2662 }
2663
2664 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2665 {
2666     event.Skip();
2667     nPage = event.GetSelection();
2668     if (nPage == SENDING)
2669         m_listCtrl = m_listCtrlSending;
2670     else if (nPage == RECEIVING)
2671         m_listCtrl = m_listCtrlReceiving;
2672     m_buttonDelete->Show(nPage == SENDING);
2673     m_buttonCopy->Show(nPage == RECEIVING);
2674     this->Layout();
2675     m_listCtrl->SetFocus();
2676 }
2677
2678 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2679 {
2680     // Update address book with edited name
2681     event.Skip();
2682     if (event.IsEditCancelled())
2683         return;
2684     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2685     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2686         pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2687     pframeMain->RefreshListCtrl();
2688 }
2689
2690 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2691 {
2692     event.Skip();
2693     if (nPage == RECEIVING)
2694         SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2695 }
2696
2697 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2698 {
2699     event.Skip();
2700     if (fDuringSend)
2701     {
2702         // Doubleclick returns selection
2703         EndModal(GetSelectedAddress() != "" ? 2 : 0);
2704         return;
2705     }
2706
2707     // Doubleclick edits item
2708     wxCommandEvent event2;
2709     OnButtonEdit(event2);
2710 }
2711
2712 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2713 {
2714     if (nPage != SENDING)
2715         return;
2716     for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2717     {
2718         if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2719         {
2720             string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2721             CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2722                 pwalletMain->DelAddressBookName(strAddress);
2723             m_listCtrl->DeleteItem(nIndex);
2724         }
2725     }
2726     pframeMain->RefreshListCtrl();
2727 }
2728
2729 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2730 {
2731     // Copy address box to clipboard
2732     if (wxTheClipboard->Open())
2733     {
2734         wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2735         wxTheClipboard->Close();
2736     }
2737 }
2738
2739 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2740 {
2741     CBitcoinAddress address(strAddress);
2742     bool fMine = address.IsValid() && pwalletMain->HaveKey(address);
2743     if (fMine)
2744         wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book.  "), strTitle);
2745     return fMine;
2746 }
2747
2748 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2749 {
2750     int nIndex = GetSelection(m_listCtrl);
2751     if (nIndex == -1)
2752         return;
2753     string strName = (string)m_listCtrl->GetItemText(nIndex);
2754     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2755     string strAddressOrg = strAddress;
2756
2757     if (nPage == SENDING)
2758     {
2759         // Ask name and address
2760         do
2761         {
2762             CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2763             if (!dialog.ShowModal())
2764                 return;
2765             strName = dialog.GetValue1();
2766             strAddress = dialog.GetValue2();
2767         }
2768         while (CheckIfMine(strAddress, _("Edit Address")));
2769
2770     }
2771     else if (nPage == RECEIVING)
2772     {
2773         // Ask name
2774         CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2775         if (!dialog.ShowModal())
2776             return;
2777         strName = dialog.GetValue();
2778     }
2779
2780     // Write back
2781     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2782     {
2783         if (strAddress != strAddressOrg)
2784             pwalletMain->DelAddressBookName(strAddressOrg);
2785         pwalletMain->SetAddressBookName(strAddress, strName);
2786     }
2787     m_listCtrl->SetItem(nIndex, 1, strAddress);
2788     m_listCtrl->SetItemText(nIndex, strName);
2789     pframeMain->RefreshListCtrl();
2790 }
2791
2792 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2793 {
2794     string strName;
2795     string strAddress;
2796
2797     if (nPage == SENDING)
2798     {
2799         // Ask name and address
2800         do
2801         {
2802             CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2803             if (!dialog.ShowModal())
2804                 return;
2805             strName = dialog.GetValue1();
2806             strAddress = dialog.GetValue2();
2807         }
2808         while (CheckIfMine(strAddress, _("Add Address")));
2809     }
2810     else if (nPage == RECEIVING)
2811     {
2812         // Ask name
2813         CGetTextFromUserDialog dialog(this,
2814             _("New Receiving Address"),
2815             _("You should use a new address for each payment you receive.\n\nLabel"),
2816             "");
2817         if (!dialog.ShowModal())
2818             return;
2819         strName = dialog.GetValue();
2820
2821         CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2822         {
2823             bool fWasLocked = pwalletMain->IsLocked();
2824             if (!GetWalletPassphrase())
2825                 return;
2826
2827             // Generate new key
2828             strAddress = CBitcoinAddress(pwalletMain->GetOrReuseKeyFromPool()).ToString();
2829
2830             if (fWasLocked)
2831                 pwalletMain->Lock();
2832         }
2833     }
2834
2835     // Add to list and select it
2836     CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
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 }