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