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