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