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