bfb708b3e90097f0bb0b0d6b54f7f2a0c15379d0
[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.\nBitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1226
1227     Close(true);
1228 }
1229
1230 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1231 {
1232     // Options->Change Wallet Encryption Passphrase
1233     if (!pwalletMain->IsCrypted())
1234     {
1235         wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1236         return;
1237     }
1238
1239     string strOldWalletPass;
1240     strOldWalletPass.reserve(100);
1241     mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1242
1243     // obtain current wallet encrypt/decrypt key, from passphrase
1244     // Note that the passphrase is not mlock()d during this entry and could potentially
1245     // be obtained from disk long after bitcoin has run.
1246     strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1247                                              _("Passphrase")).ToStdString();
1248
1249     bool fWasLocked = pwalletMain->IsLocked();
1250     pwalletMain->Lock();
1251
1252     if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1253     {
1254         fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1255         munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1256         wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1257         return;
1258     }
1259
1260     if (fWasLocked)
1261         pwalletMain->Lock();
1262
1263     string strNewWalletPass;
1264     strNewWalletPass.reserve(100);
1265     mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1266
1267     // obtain new wallet encrypt/decrypt key, from passphrase
1268     // Note that the passphrase is not mlock()d during this entry and could potentially
1269     // be obtained from disk long after bitcoin has run.
1270     strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1271                                              _("Passphrase")).ToStdString();
1272
1273     if (!strNewWalletPass.size())
1274     {
1275         fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1276         fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1277         munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1278         munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1279         wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1280         return;
1281     }
1282
1283     string strNewWalletPassTest;
1284     strNewWalletPassTest.reserve(100);
1285     mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1286
1287     // obtain new wallet encrypt/decrypt key, from passphrase
1288     // Note that the passphrase is not mlock()d during this entry and could potentially
1289     // be obtained from disk long after bitcoin has run.
1290     strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1291                                                  _("Passphrase")).ToStdString();
1292
1293     if (strNewWalletPassTest != strNewWalletPass)
1294     {
1295         fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1296         fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1297         fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1298         munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1299         munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1300         munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1301         wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1302         return;
1303     }
1304
1305     if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1306     {
1307         fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1308         fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1309         fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1310         munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1311         munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1312         munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1313         wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1314         return;
1315     }
1316     fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1317     fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1318     fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1319     munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1320     munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1321     munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1322     wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
1323 }
1324
1325 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1326 {
1327     // Options->Options
1328     COptionsDialog dialog(this);
1329     dialog.ShowModal();
1330 }
1331
1332 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1333 {
1334     // Help->About
1335     CAboutDialog dialog(this);
1336     dialog.ShowModal();
1337 }
1338
1339 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1340 {
1341     // Toolbar: Send
1342     CSendDialog dialog(this);
1343     dialog.ShowModal();
1344 }
1345
1346 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1347 {
1348     // Toolbar: Address Book
1349     CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1350     if (dialogAddr.ShowModal() == 2)
1351     {
1352         // Send
1353         CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1354         dialogSend.ShowModal();
1355     }
1356 }
1357
1358 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1359 {
1360     // Automatically select-all when entering window
1361     event.Skip();
1362     m_textCtrlAddress->SetSelection(-1, -1);
1363     fOnSetFocusAddress = true;
1364 }
1365
1366 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1367 {
1368     event.Skip();
1369     if (fOnSetFocusAddress)
1370         m_textCtrlAddress->SetSelection(-1, -1);
1371     fOnSetFocusAddress = false;
1372 }
1373
1374 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1375 {
1376     // Ask name
1377     CGetTextFromUserDialog dialog(this,
1378         _("New Receiving Address"),
1379         _("You should use a new address for each payment you receive.\n\nLabel"),
1380         "");
1381     if (!dialog.ShowModal())
1382         return;
1383     string strName = dialog.GetValue();
1384
1385     string strAddress;
1386
1387     bool fWasLocked = pwalletMain->IsLocked();
1388     if (!GetWalletPassphrase())
1389         return;
1390
1391     // Generate new key
1392     std::vector<unsigned char> newKey;
1393     pwalletMain->GetKeyFromPool(newKey, true);
1394     strAddress = CBitcoinAddress(newKey).ToString();
1395
1396     if (fWasLocked)
1397         pwalletMain->Lock();
1398
1399     // Save
1400     CRITICAL_BLOCK(pwalletMain->cs_wallet)
1401         pwalletMain->SetAddressBookName(strAddress, strName);
1402     SetDefaultReceivingAddress(strAddress);
1403 }
1404
1405 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1406 {
1407     // Copy address box to clipboard
1408     if (wxTheClipboard->Open())
1409     {
1410         wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1411         wxTheClipboard->Close();
1412     }
1413 }
1414
1415 void CMainFrame::OnListItemActivated(wxListEvent& event)
1416 {
1417     uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1418     CWalletTx wtx;
1419     CRITICAL_BLOCK(pwalletMain->cs_wallet)
1420     {
1421         map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1422         if (mi == pwalletMain->mapWallet.end())
1423         {
1424             printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1425             return;
1426         }
1427         wtx = (*mi).second;
1428     }
1429     CTxDetailsDialog dialog(this, wtx);
1430     dialog.ShowModal();
1431     //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1432     //pdialog->Show();
1433 }
1434
1435
1436
1437
1438
1439
1440 //////////////////////////////////////////////////////////////////////////////
1441 //
1442 // CTxDetailsDialog
1443 //
1444
1445 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1446 {
1447 #ifdef __WXMSW__
1448     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1449 #endif
1450     CRITICAL_BLOCK(pwalletMain->cs_wallet)
1451     {
1452         string strHTML;
1453         strHTML.reserve(4000);
1454         strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1455
1456         int64 nTime = wtx.GetTxTime();
1457         int64 nCredit = wtx.GetCredit();
1458         int64 nDebit = wtx.GetDebit();
1459         int64 nNet = nCredit - nDebit;
1460
1461
1462
1463         strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1464         int nRequests = wtx.GetRequestCount();
1465         if (nRequests != -1)
1466         {
1467             if (nRequests == 0)
1468                 strHTML += _(", has not been successfully broadcast yet");
1469             else if (nRequests == 1)
1470                 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1471             else
1472                 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1473         }
1474         strHTML += "<br>";
1475
1476         strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1477
1478
1479         //
1480         // From
1481         //
1482         if (wtx.IsCoinBase())
1483         {
1484             strHTML += _("<b>Source:</b> Generated<br>");
1485         }
1486         else if (!wtx.mapValue["from"].empty())
1487         {
1488             // Online transaction
1489             if (!wtx.mapValue["from"].empty())
1490                 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1491         }
1492         else
1493         {
1494             // Offline transaction
1495             if (nNet > 0)
1496             {
1497                 // Credit
1498                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1499                 {
1500                     if (pwalletMain->IsMine(txout))
1501                     {
1502                         CBitcoinAddress address;
1503                         if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1504                         {
1505                             if (pwalletMain->mapAddressBook.count(address))
1506                             {
1507                                 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1508                                 strHTML += _("<b>To:</b> ");
1509                                 strHTML += HtmlEscape(address.ToString());
1510                                 if (!pwalletMain->mapAddressBook[address].empty())
1511                                     strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[address] + ")";
1512                                 else
1513                                     strHTML += _(" (yours)");
1514                                 strHTML += "<br>";
1515                             }
1516                         }
1517                         break;
1518                     }
1519                 }
1520             }
1521         }
1522
1523
1524         //
1525         // To
1526         //
1527         string strAddress;
1528         if (!wtx.mapValue["to"].empty())
1529         {
1530             // Online transaction
1531             strAddress = wtx.mapValue["to"];
1532             strHTML += _("<b>To:</b> ");
1533             if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1534                 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1535             strHTML += HtmlEscape(strAddress) + "<br>";
1536         }
1537
1538
1539         //
1540         // Amount
1541         //
1542         if (wtx.IsCoinBase() && nCredit == 0)
1543         {
1544             //
1545             // Coinbase
1546             //
1547             int64 nUnmatured = 0;
1548             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1549                 nUnmatured += pwalletMain->GetCredit(txout);
1550             strHTML += _("<b>Credit:</b> ");
1551             if (wtx.IsInMainChain())
1552                 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1553             else
1554                 strHTML += _("(not accepted)");
1555             strHTML += "<br>";
1556         }
1557         else if (nNet > 0)
1558         {
1559             //
1560             // Credit
1561             //
1562             strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1563         }
1564         else
1565         {
1566             bool fAllFromMe = true;
1567             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1568                 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1569
1570             bool fAllToMe = true;
1571             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1572                 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1573
1574             if (fAllFromMe)
1575             {
1576                 //
1577                 // Debit
1578                 //
1579                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1580                 {
1581                     if (pwalletMain->IsMine(txout))
1582                         continue;
1583
1584                     if (wtx.mapValue["to"].empty())
1585                     {
1586                         // Offline transaction
1587                         CBitcoinAddress address;
1588                         if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1589                         {
1590                             string strAddress = address.ToString();
1591                             strHTML += _("<b>To:</b> ");
1592                             if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
1593                                 strHTML += pwalletMain->mapAddressBook[address] + " ";
1594                             strHTML += strAddress;
1595                             strHTML += "<br>";
1596                         }
1597                     }
1598
1599                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1600                 }
1601
1602                 if (fAllToMe)
1603                 {
1604                     // Payment to self
1605                     int64 nChange = wtx.GetChange();
1606                     int64 nValue = nCredit - nChange;
1607                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1608                     strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1609                 }
1610
1611                 int64 nTxFee = nDebit - wtx.GetValueOut();
1612                 if (nTxFee > 0)
1613                     strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1614             }
1615             else
1616             {
1617                 //
1618                 // Mixed debit transaction
1619                 //
1620                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1621                     if (pwalletMain->IsMine(txin))
1622                         strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1623                 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1624                     if (pwalletMain->IsMine(txout))
1625                         strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1626             }
1627         }
1628
1629         strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1630
1631
1632         //
1633         // Message
1634         //
1635         if (!wtx.mapValue["message"].empty())
1636             strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1637         if (!wtx.mapValue["comment"].empty())
1638             strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1639
1640         if (wtx.IsCoinBase())
1641             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>";
1642
1643
1644         //
1645         // Debug view
1646         //
1647         if (fDebug)
1648         {
1649             strHTML += "<hr><br>debug print<br><br>";
1650             BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1651                 if (pwalletMain->IsMine(txin))
1652                     strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1653             BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1654                 if (pwalletMain->IsMine(txout))
1655                     strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1656
1657             strHTML += "<br><b>Transaction:</b><br>";
1658             strHTML += HtmlEscape(wtx.ToString(), true);
1659
1660             strHTML += "<br><b>Inputs:</b><br>";
1661             CRITICAL_BLOCK(pwalletMain->cs_wallet)
1662             {
1663                 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1664                 {
1665                     COutPoint prevout = txin.prevout;
1666                     map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1667                     if (mi != pwalletMain->mapWallet.end())
1668                     {
1669                         const CWalletTx& prev = (*mi).second;
1670                         if (prevout.n < prev.vout.size())
1671                         {
1672                             strHTML += HtmlEscape(prev.ToString(), true);
1673                             strHTML += " &nbsp;&nbsp; " + FormatTxStatus(prev) + ", ";
1674                             strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1675                         }
1676                     }
1677                 }
1678             }
1679         }
1680
1681
1682
1683         strHTML += "</font></html>";
1684         string(strHTML.begin(), strHTML.end()).swap(strHTML);
1685         m_htmlWin->SetPage(strHTML);
1686         m_buttonOK->SetFocus();
1687     }
1688 }
1689
1690 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1691 {
1692     EndModal(false);
1693 }
1694
1695
1696
1697
1698
1699
1700 //////////////////////////////////////////////////////////////////////////////
1701 //
1702 // Startup folder
1703 //
1704
1705 #ifdef __WXMSW__
1706 string StartupShortcutPath()
1707 {
1708     return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1709 }
1710
1711 bool GetStartOnSystemStartup()
1712 {
1713     return filesystem::exists(StartupShortcutPath().c_str());
1714 }
1715
1716 void SetStartOnSystemStartup(bool fAutoStart)
1717 {
1718     // If the shortcut exists already, remove it for updating
1719     remove(StartupShortcutPath().c_str());
1720
1721     if (fAutoStart)
1722     {
1723         CoInitialize(NULL);
1724
1725         // Get a pointer to the IShellLink interface.
1726         IShellLink* psl = NULL;
1727         HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1728                                 CLSCTX_INPROC_SERVER, IID_IShellLink,
1729                                 reinterpret_cast<void**>(&psl));
1730
1731         if (SUCCEEDED(hres))
1732         {
1733             // Get the current executable path
1734             TCHAR pszExePath[MAX_PATH];
1735             GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1736
1737             // Set the path to the shortcut target
1738             psl->SetPath(pszExePath);
1739             PathRemoveFileSpec(pszExePath);
1740             psl->SetWorkingDirectory(pszExePath);
1741             psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1742
1743             // Query IShellLink for the IPersistFile interface for
1744             // saving the shortcut in persistent storage.
1745             IPersistFile* ppf = NULL;
1746             hres = psl->QueryInterface(IID_IPersistFile,
1747                                        reinterpret_cast<void**>(&ppf));
1748             if (SUCCEEDED(hres))
1749             {
1750                 WCHAR pwsz[MAX_PATH];
1751                 // Ensure that the string is ANSI.
1752                 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1753                 // Save the link by calling IPersistFile::Save.
1754                 hres = ppf->Save(pwsz, TRUE);
1755                 ppf->Release();
1756             }
1757             psl->Release();
1758         }
1759         CoUninitialize();
1760     }
1761 }
1762
1763 #elif defined(__WXGTK__)
1764
1765 // Follow the Desktop Application Autostart Spec:
1766 //  http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1767
1768 boost::filesystem::path GetAutostartDir()
1769 {
1770     namespace fs = boost::filesystem;
1771
1772     char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1773     if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1774     char* pszHome = getenv("HOME");
1775     if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1776     return fs::path();
1777 }
1778
1779 boost::filesystem::path GetAutostartFilePath()
1780 {
1781     return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1782 }
1783
1784 bool GetStartOnSystemStartup()
1785 {
1786     boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1787     if (!optionFile.good())
1788         return false;
1789     // Scan through file for "Hidden=true":
1790     string line;
1791     while (!optionFile.eof())
1792     {
1793         getline(optionFile, line);
1794         if (line.find("Hidden") != string::npos &&
1795             line.find("true") != string::npos)
1796             return false;
1797     }
1798     optionFile.close();
1799
1800     return true;
1801 }
1802
1803 void SetStartOnSystemStartup(bool fAutoStart)
1804 {
1805     if (!fAutoStart)
1806     {
1807 #if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
1808         unlink(GetAutostartFilePath().string().c_str());
1809 #else
1810         unlink(GetAutostartFilePath().native_file_string().c_str());
1811 #endif
1812     }
1813     else
1814     {
1815         char pszExePath[MAX_PATH+1];
1816         memset(pszExePath, 0, sizeof(pszExePath));
1817         if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1818             return;
1819
1820         boost::filesystem::create_directories(GetAutostartDir());
1821
1822         boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1823         if (!optionFile.good())
1824         {
1825             wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1826             return;
1827         }
1828         // Write a bitcoin.desktop file to the autostart directory:
1829         optionFile << "[Desktop Entry]\n";
1830         optionFile << "Type=Application\n";
1831         optionFile << "Name=Bitcoin\n";
1832         optionFile << "Exec=" << pszExePath << "\n";
1833         optionFile << "Terminal=false\n";
1834         optionFile << "Hidden=false\n";
1835         optionFile.close();
1836     }
1837 }
1838 #else
1839
1840 // TODO: OSX startup stuff; see:
1841 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1842
1843 bool GetStartOnSystemStartup() { return false; }
1844 void SetStartOnSystemStartup(bool fAutoStart) { }
1845
1846 #endif
1847
1848
1849
1850
1851
1852
1853 //////////////////////////////////////////////////////////////////////////////
1854 //
1855 // COptionsDialog
1856 //
1857
1858 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1859 {
1860     // Set up list box of page choices
1861     m_listBox->Append(_("Main"));
1862     //m_listBox->Append(_("Test 2"));
1863     m_listBox->SetSelection(0);
1864     SelectPage(0);
1865 #ifndef __WXMSW__
1866     SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1867 #else
1868     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1869 #endif
1870 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1871     m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1872     if (!GetBoolArg("-minimizetotray"))
1873     {
1874         // Minimize to tray is just too buggy on Linux
1875         fMinimizeToTray = false;
1876         m_checkBoxMinimizeToTray->SetValue(false);
1877         m_checkBoxMinimizeToTray->Enable(false);
1878         m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1879     }
1880 #endif
1881 #ifdef __WXMAC_OSX__
1882     m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1883 #endif
1884
1885     // Init values
1886     m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1887     m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1888     m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1889     m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1890     if (fHaveUPnP)
1891         m_checkBoxUseUPnP->SetValue(fUseUPnP);
1892     else
1893         m_checkBoxUseUPnP->Enable(false);
1894     m_checkBoxUseProxy->SetValue(fUseProxy);
1895     m_textCtrlProxyIP->Enable(fUseProxy);
1896     m_textCtrlProxyPort->Enable(fUseProxy);
1897     m_staticTextProxyIP->Enable(fUseProxy);
1898     m_staticTextProxyPort->Enable(fUseProxy);
1899     m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1900     m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1901
1902     m_buttonOK->SetFocus();
1903 }
1904
1905 void COptionsDialog::SelectPage(int nPage)
1906 {
1907     m_panelMain->Show(nPage == 0);
1908     m_panelTest2->Show(nPage == 1);
1909
1910     m_scrolledWindow->Layout();
1911     m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1912 }
1913
1914 void COptionsDialog::OnListBox(wxCommandEvent& event)
1915 {
1916     SelectPage(event.GetSelection());
1917 }
1918
1919 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1920 {
1921     event.Skip();
1922     int64 nTmp = nTransactionFee;
1923     ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1924     m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1925 }
1926
1927 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1928 {
1929     m_textCtrlProxyIP->Enable(event.IsChecked());
1930     m_textCtrlProxyPort->Enable(event.IsChecked());
1931     m_staticTextProxyIP->Enable(event.IsChecked());
1932     m_staticTextProxyPort->Enable(event.IsChecked());
1933 }
1934
1935 CAddress COptionsDialog::GetProxyAddr()
1936 {
1937     // Be careful about byte order, addr.ip and addr.port are big endian
1938     CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1939     if (addr.ip == INADDR_NONE)
1940         addr.ip = addrProxy.ip;
1941     int nPort = atoi(m_textCtrlProxyPort->GetValue());
1942     addr.port = htons(nPort);
1943     if (nPort <= 0 || nPort > USHRT_MAX)
1944         addr.port = addrProxy.port;
1945     return addr;
1946 }
1947
1948 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1949 {
1950     event.Skip();
1951     m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1952     m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1953 }
1954
1955
1956 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1957 {
1958     OnButtonApply(event);
1959     EndModal(false);
1960 }
1961
1962 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1963 {
1964     EndModal(false);
1965 }
1966
1967 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1968 {
1969     CWalletDB walletdb(pwalletMain->strWalletFile);
1970
1971     int64 nPrevTransactionFee = nTransactionFee;
1972     if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1973         walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1974
1975     if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1976     {
1977         fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1978         SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1979     }
1980
1981     if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1982     {
1983         fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1984         walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1985         ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1986     }
1987
1988     if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1989     {
1990         fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1991         walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1992     }
1993
1994     if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1995     {
1996         fUseUPnP = m_checkBoxUseUPnP->GetValue();
1997         walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1998         MapPort(fUseUPnP);
1999     }
2000
2001     fUseProxy = m_checkBoxUseProxy->GetValue();
2002     walletdb.WriteSetting("fUseProxy", fUseProxy);
2003
2004     addrProxy = GetProxyAddr();
2005     walletdb.WriteSetting("addrProxy", addrProxy);
2006 }
2007
2008
2009
2010
2011
2012
2013 //////////////////////////////////////////////////////////////////////////////
2014 //
2015 // CAboutDialog
2016 //
2017
2018 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2019 {
2020     m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2021
2022     // Change (c) into UTF-8 or ANSI copyright symbol
2023     wxString str = m_staticTextMain->GetLabel();
2024 #if wxUSE_UNICODE
2025     str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2026 #else
2027     str.Replace("(c)", "\xA9");
2028 #endif
2029     m_staticTextMain->SetLabel(str);
2030 #ifndef __WXMSW__
2031     // Resize on Linux to make the window fit the text.
2032     // The text was wrapped manually rather than using the Wrap setting because
2033     // the wrap would be too small on Linux and it can't be changed at this point.
2034     wxFont fontTmp = m_staticTextMain->GetFont();
2035     if (fontTmp.GetPointSize() > 8);
2036         fontTmp.SetPointSize(8);
2037     m_staticTextMain->SetFont(fontTmp);
2038     SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2039 #else
2040     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2041 #endif
2042 }
2043
2044 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2045 {
2046     EndModal(false);
2047 }
2048
2049
2050
2051
2052
2053
2054 //////////////////////////////////////////////////////////////////////////////
2055 //
2056 // CSendDialog
2057 //
2058
2059 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2060 {
2061     // Init
2062     m_textCtrlAddress->SetValue(strAddress);
2063     m_choiceTransferType->SetSelection(0);
2064     m_bitmapCheckMark->Show(false);
2065     fEnabledPrev = true;
2066     m_textCtrlAddress->SetFocus();
2067     
2068     //// todo: should add a display of your balance for convenience
2069 #ifndef __WXMSW__
2070     wxFont fontTmp = m_staticTextInstructions->GetFont();
2071     if (fontTmp.GetPointSize() > 9);
2072         fontTmp.SetPointSize(9);
2073     m_staticTextInstructions->SetFont(fontTmp);
2074     SetSize(725, 180);
2075 #else
2076     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2077 #endif
2078     
2079     // Set Icon
2080     if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2081     {
2082         wxIcon iconSend;
2083         iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2084         SetIcon(iconSend);
2085     }
2086 #ifdef __WXMSW__
2087     else
2088         SetIcon(wxICON(bitcoin));
2089 #endif
2090
2091     // Fixup the tab order
2092     m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2093     m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2094     this->Layout();
2095 }
2096
2097 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2098 {
2099     // Reformat the amount
2100     event.Skip();
2101     if (m_textCtrlAmount->GetValue().Trim().empty())
2102         return;
2103     int64 nTmp;
2104     if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2105         m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2106 }
2107
2108 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2109 {
2110     // Open address book
2111     CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2112     if (dialog.ShowModal())
2113         m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2114 }
2115
2116 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2117 {
2118     // Copy clipboard to address box
2119     if (wxTheClipboard->Open())
2120     {
2121         if (wxTheClipboard->IsSupported(wxDF_TEXT))
2122         {
2123             wxTextDataObject data;
2124             wxTheClipboard->GetData(data);
2125             m_textCtrlAddress->SetValue(data.GetText());
2126         }
2127         wxTheClipboard->Close();
2128     }
2129 }
2130
2131 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2132 {
2133     static CCriticalSection cs_sendlock;
2134     TRY_CRITICAL_BLOCK(cs_sendlock)
2135     {
2136         CWalletTx wtx;
2137         string strAddress = (string)m_textCtrlAddress->GetValue();
2138
2139         // Parse amount
2140         int64 nValue = 0;
2141         if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2142         {
2143             wxMessageBox(_("Error in amount  "), _("Send Coins"));
2144             return;
2145         }
2146         if (nValue > pwalletMain->GetBalance())
2147         {
2148             wxMessageBox(_("Amount exceeds your balance  "), _("Send Coins"));
2149             return;
2150         }
2151         if (nValue + nTransactionFee > pwalletMain->GetBalance())
2152         {
2153             wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included  "), _("Send Coins"));
2154             return;
2155         }
2156
2157         // Parse bitcoin address
2158         CBitcoinAddress address(strAddress);
2159         bool fBitcoinAddress = address.IsValid();
2160
2161         if (fBitcoinAddress)
2162         {
2163             bool fWasLocked = pwalletMain->IsLocked();
2164             if (!GetWalletPassphrase())
2165                 return;
2166
2167             string strError;
2168             CRITICAL_BLOCK(cs_main)
2169             CRITICAL_BLOCK(pwalletMain->cs_wallet)
2170             {
2171                 // Send to bitcoin address
2172                 CScript scriptPubKey;
2173                 scriptPubKey.SetBitcoinAddress(address);
2174
2175                 strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2176             }
2177             if (strError == "")
2178             {
2179                 pframeMain->RefreshListCtrl();
2180                 wxMessageBox(_("Payment sent  "), _("Sending..."));
2181             }
2182             else if (strError == "ABORTED")
2183             {
2184                 if (fWasLocked)
2185                     pwalletMain->Lock();
2186                 return; // leave send dialog open
2187             }
2188             else
2189             {
2190                 wxMessageBox(strError + "  ", _("Sending..."));
2191                 EndModal(false);
2192                 if (fWasLocked)
2193                     pwalletMain->Lock();
2194                 return;
2195             }
2196
2197             if (fWasLocked)
2198                 pwalletMain->Lock();
2199         }
2200         else
2201         {
2202             // Parse IP address
2203             CAddress addr(strAddress);
2204             if (!addr.IsValid())
2205             {
2206                 wxMessageBox(_("Invalid address  "), _("Send Coins"));
2207                 return;
2208             }
2209
2210             // Message
2211             wtx.mapValue["to"] = strAddress;
2212
2213             // Send to IP address
2214             CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2215             if (!pdialog->ShowModal())
2216                 return;
2217         }
2218
2219         CRITICAL_BLOCK(pwalletMain->cs_wallet)
2220             if (!pwalletMain->mapAddressBook.count(address))
2221                 pwalletMain->SetAddressBookName(strAddress, "");
2222
2223         EndModal(true);
2224     }
2225 }
2226
2227 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2228 {
2229     // Cancel
2230     EndModal(false);
2231 }
2232
2233
2234
2235
2236
2237
2238 //////////////////////////////////////////////////////////////////////////////
2239 //
2240 // CSendingDialog
2241 //
2242
2243 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
2244 {
2245     addr = addrIn;
2246     nPrice = nPriceIn;
2247     wtx = wtxIn;
2248     start = wxDateTime::UNow();
2249     memset(pszStatus, 0, sizeof(pszStatus));
2250     fCanCancel = true;
2251     fAbort = false;
2252     fSuccess = false;
2253     fUIDone = false;
2254     fWorkDone = false;
2255 #ifndef __WXMSW__
2256     SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2257 #else
2258     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2259 #endif
2260
2261     SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2262     m_textCtrlStatus->SetValue("");
2263
2264     CreateThread(SendingDialogStartTransfer, this);
2265 }
2266
2267 CSendingDialog::~CSendingDialog()
2268 {
2269     printf("~CSendingDialog()\n");
2270 }
2271
2272 void CSendingDialog::Close()
2273 {
2274     // Last one out turn out the lights.
2275     // fWorkDone signals that work side is done and UI thread should call destroy.
2276     // fUIDone signals that UI window has closed and work thread should call destroy.
2277     // This allows the window to disappear and end modality when cancelled
2278     // without making the user wait for ConnectNode to return.  The dialog object
2279     // hangs around in the background until the work thread exits.
2280     if (IsModal())
2281         EndModal(fSuccess);
2282     else
2283         Show(false);
2284     if (fWorkDone)
2285         Destroy();
2286     else
2287         fUIDone = true;
2288 }
2289
2290 void CSendingDialog::OnClose(wxCloseEvent& event)
2291 {
2292     if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2293     {
2294         Close();
2295     }
2296     else
2297     {
2298         event.Veto();
2299         wxCommandEvent cmdevent;
2300         OnButtonCancel(cmdevent);
2301     }
2302 }
2303
2304 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2305 {
2306     if (fWorkDone)
2307         Close();
2308 }
2309
2310 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2311 {
2312     if (fCanCancel)
2313         fAbort = true;
2314 }
2315
2316 void CSendingDialog::OnPaint(wxPaintEvent& event)
2317 {
2318     event.Skip();
2319     if (strlen(pszStatus) > 130)
2320         m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2321     else
2322         m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2323     m_staticTextSending->SetFocus();
2324     if (!fCanCancel)
2325         m_buttonCancel->Enable(false);
2326     if (fWorkDone)
2327     {
2328         m_buttonOK->Enable(true);
2329         m_buttonOK->SetFocus();
2330         m_buttonCancel->Enable(false);
2331     }
2332     if (fAbort && fCanCancel && IsShown())
2333     {
2334         strcpy(pszStatus, _("CANCELLED"));
2335         m_buttonOK->Enable(true);
2336         m_buttonOK->SetFocus();
2337         m_buttonCancel->Enable(false);
2338         m_buttonCancel->SetLabel(_("Cancelled"));
2339         Close();
2340         wxMessageBox(_("Transfer cancelled  "), _("Sending..."), wxOK, this);
2341     }
2342 }
2343
2344
2345 //
2346 // Everything from here on is not in the UI thread and must only communicate
2347 // with the rest of the dialog through variables and calling repaint.
2348 //
2349
2350 void CSendingDialog::Repaint()
2351 {
2352     Refresh();
2353     wxPaintEvent event;
2354     GetEventHandler()->AddPendingEvent(event);
2355 }
2356
2357 bool CSendingDialog::Status()
2358 {
2359     if (fUIDone)
2360     {
2361         Destroy();
2362         return false;
2363     }
2364     if (fAbort && fCanCancel)
2365     {
2366         memset(pszStatus, 0, 10);
2367         strcpy(pszStatus, _("CANCELLED"));
2368         Repaint();
2369         fWorkDone = true;
2370         return false;
2371     }
2372     return true;
2373 }
2374
2375 bool CSendingDialog::Status(const string& str)
2376 {
2377     if (!Status())
2378         return false;
2379
2380     // This can be read by the UI thread at any time,
2381     // so copy in a way that can be read cleanly at all times.
2382     memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2383     strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2384
2385     Repaint();
2386     return true;
2387 }
2388
2389 bool CSendingDialog::Error(const string& str)
2390 {
2391     fCanCancel = false;
2392     fWorkDone = true;
2393     Status(string(_("Error: ")) + str);
2394     return false;
2395 }
2396
2397 void SendingDialogStartTransfer(void* parg)
2398 {
2399     ((CSendingDialog*)parg)->StartTransfer();
2400 }
2401
2402 void CSendingDialog::StartTransfer()
2403 {
2404     // Make sure we have enough money
2405     if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2406     {
2407         Error(_("Insufficient funds"));
2408         return;
2409     }
2410
2411     // We may have connected already for product details
2412     if (!Status(_("Connecting...")))
2413         return;
2414     CNode* pnode = ConnectNode(addr, 15 * 60);
2415     if (!pnode)
2416     {
2417         Error(_("Unable to connect"));
2418         return;
2419     }
2420
2421     // Send order to seller, with response going to OnReply2 via event handler
2422     if (!Status(_("Requesting public key...")))
2423         return;
2424     pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2425 }
2426
2427 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2428 {
2429     ((CSendingDialog*)parg)->OnReply2(vRecv);
2430 }
2431
2432 void CSendingDialog::OnReply2(CDataStream& vRecv)
2433 {
2434     if (!Status(_("Received public key...")))
2435         return;
2436
2437     CScript scriptPubKey;
2438     int nRet;
2439     try
2440     {
2441         vRecv >> nRet;
2442         if (nRet > 0)
2443         {
2444             string strMessage;
2445             if (!vRecv.empty())
2446                 vRecv >> strMessage;
2447             if (nRet == 2)
2448                 Error(_("Recipient is not accepting transactions sent by IP address"));
2449             else
2450                 Error(_("Transfer was not accepted"));
2451             //// todo: enlarge the window and enable a hidden white box to put seller's message
2452             return;
2453         }
2454         vRecv >> scriptPubKey;
2455     }
2456     catch (...)
2457     {
2458         //// what do we want to do about this?
2459         Error(_("Invalid response received"));
2460         return;
2461     }
2462
2463     // Pause to give the user a chance to cancel
2464     while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2465     {
2466         Sleep(200);
2467         if (!Status())
2468             return;
2469     }
2470
2471     // Pay
2472     if (!Status(_("Creating transaction...")))
2473         return;
2474     if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2475     {
2476         Error(_("Insufficient funds"));
2477         return;
2478     }
2479
2480     CReserveKey reservekey(pwalletMain);
2481     int64 nFeeRequired;
2482     bool fWasLocked = pwalletMain->IsLocked();
2483     if (!GetWalletPassphrase())
2484         return;
2485
2486     bool fTxCreated = false;
2487     CRITICAL_BLOCK(cs_main)
2488     CRITICAL_BLOCK(pwalletMain->cs_wallet)
2489     {
2490         fTxCreated = pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired);
2491     }
2492     if (!fTxCreated)
2493     {
2494         if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2495             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()));
2496         else
2497             Error(_("Transaction creation failed"));
2498         return;
2499     }
2500
2501     if (fWasLocked)
2502         pwalletMain->Lock();
2503
2504     // Transaction fee
2505     if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2506     {
2507         Error(_("Transaction aborted"));
2508         return;
2509     }
2510
2511     // Make sure we're still connected
2512     CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2513     if (!pnode)
2514     {
2515         Error(_("Lost connection, transaction cancelled"));
2516         return;
2517     }
2518
2519     // Last chance to cancel
2520     Sleep(50);
2521     if (!Status())
2522         return;
2523     fCanCancel = false;
2524     if (fAbort)
2525     {
2526         fCanCancel = true;
2527         if (!Status())
2528             return;
2529         fCanCancel = false;
2530     }
2531     if (!Status(_("Sending payment...")))
2532         return;
2533
2534     // Commit
2535     bool fTxCommitted = false;
2536     CRITICAL_BLOCK(cs_main)
2537     CRITICAL_BLOCK(pwalletMain->cs_wallet)
2538     {
2539         fTxCommitted = pwalletMain->CommitTransaction(wtx, reservekey);
2540     }
2541     if (!fTxCommitted)
2542     {
2543         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."));
2544         return;
2545     }
2546
2547     // Send payment tx to seller, with response going to OnReply3 via event handler
2548     CWalletTx wtxSend = wtx;
2549     wtxSend.fFromMe = false;
2550     pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2551
2552     Status(_("Waiting for confirmation..."));
2553     MainFrameRepaint();
2554 }
2555
2556 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2557 {
2558     ((CSendingDialog*)parg)->OnReply3(vRecv);
2559 }
2560
2561 void CSendingDialog::OnReply3(CDataStream& vRecv)
2562 {
2563     int nRet;
2564     try
2565     {
2566         vRecv >> nRet;
2567         if (nRet > 0)
2568         {
2569             Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2570                     "The transaction is recorded and will credit to the recipient,\n"
2571                     "but the comment information will be blank."));
2572             return;
2573         }
2574     }
2575     catch (...)
2576     {
2577         //// what do we want to do about this?
2578         Error(_("Payment was sent, but an invalid response was received"));
2579         return;
2580     }
2581
2582     fSuccess = true;
2583     fWorkDone = true;
2584     Status(_("Payment completed"));
2585 }
2586
2587
2588
2589
2590
2591
2592 //////////////////////////////////////////////////////////////////////////////
2593 //
2594 // CAddressBookDialog
2595 //
2596
2597 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2598 {
2599 #ifdef __WXMSW__
2600     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2601 #endif
2602
2603     // Set initially selected page
2604     wxNotebookEvent event;
2605     event.SetSelection(nPageIn);
2606     OnNotebookPageChanged(event);
2607     m_notebook->ChangeSelection(nPageIn);
2608
2609     fDuringSend = fDuringSendIn;
2610     if (!fDuringSend)
2611         m_buttonCancel->Show(false);
2612
2613     // Set Icon
2614     if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2615     {
2616         wxIcon iconAddressBook;
2617         iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2618         SetIcon(iconAddressBook);
2619     }
2620 #ifdef __WXMSW__
2621     else
2622         SetIcon(wxICON(bitcoin));
2623 #endif
2624
2625     // Init column headers
2626     m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2627     m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2628     m_listCtrlSending->SetFocus();
2629     m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2630     m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2631     m_listCtrlReceiving->SetFocus();
2632
2633     // Fill listctrl with address book data
2634     CRITICAL_BLOCK(pwalletMain->cs_wallet)
2635     {
2636         string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2637         BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2638         {
2639             const CBitcoinAddress& address = item.first;
2640             string strName = item.second;
2641             bool fMine = pwalletMain->HaveKey(address);
2642             wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2643             int nIndex = InsertLine(plistCtrl, strName, address.ToString());
2644             if (address.ToString() == (fMine ? strDefaultReceiving : string(strInitSelected)))
2645                 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2646         }
2647     }
2648 }
2649
2650 wxString CAddressBookDialog::GetSelectedAddress()
2651 {
2652     int nIndex = GetSelection(m_listCtrl);
2653     if (nIndex == -1)
2654         return "";
2655     return GetItemText(m_listCtrl, nIndex, 1);
2656 }
2657
2658 wxString CAddressBookDialog::GetSelectedSendingAddress()
2659 {
2660     int nIndex = GetSelection(m_listCtrlSending);
2661     if (nIndex == -1)
2662         return "";
2663     return GetItemText(m_listCtrlSending, nIndex, 1);
2664 }
2665
2666 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2667 {
2668     int nIndex = GetSelection(m_listCtrlReceiving);
2669     if (nIndex == -1)
2670         return "";
2671     return GetItemText(m_listCtrlReceiving, nIndex, 1);
2672 }
2673
2674 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2675 {
2676     event.Skip();
2677     nPage = event.GetSelection();
2678     if (nPage == SENDING)
2679         m_listCtrl = m_listCtrlSending;
2680     else if (nPage == RECEIVING)
2681         m_listCtrl = m_listCtrlReceiving;
2682     m_buttonDelete->Show(nPage == SENDING);
2683     m_buttonCopy->Show(nPage == RECEIVING);
2684     this->Layout();
2685     m_listCtrl->SetFocus();
2686 }
2687
2688 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2689 {
2690     // Update address book with edited name
2691     event.Skip();
2692     if (event.IsEditCancelled())
2693         return;
2694     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2695     CRITICAL_BLOCK(pwalletMain->cs_wallet)
2696         pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2697     pframeMain->RefreshListCtrl();
2698 }
2699
2700 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2701 {
2702     event.Skip();
2703     if (nPage == RECEIVING)
2704         SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2705 }
2706
2707 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2708 {
2709     event.Skip();
2710     if (fDuringSend)
2711     {
2712         // Doubleclick returns selection
2713         EndModal(GetSelectedAddress() != "" ? 2 : 0);
2714         return;
2715     }
2716
2717     // Doubleclick edits item
2718     wxCommandEvent event2;
2719     OnButtonEdit(event2);
2720 }
2721
2722 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2723 {
2724     if (nPage != SENDING)
2725         return;
2726     for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2727     {
2728         if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2729         {
2730             string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2731             CRITICAL_BLOCK(pwalletMain->cs_wallet)
2732                 pwalletMain->DelAddressBookName(strAddress);
2733             m_listCtrl->DeleteItem(nIndex);
2734         }
2735     }
2736     pframeMain->RefreshListCtrl();
2737 }
2738
2739 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2740 {
2741     // Copy address box to clipboard
2742     if (wxTheClipboard->Open())
2743     {
2744         wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2745         wxTheClipboard->Close();
2746     }
2747 }
2748
2749 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2750 {
2751     CBitcoinAddress address(strAddress);
2752     bool fMine = address.IsValid() && pwalletMain->HaveKey(address);
2753     if (fMine)
2754         wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book.  "), strTitle);
2755     return fMine;
2756 }
2757
2758 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2759 {
2760     int nIndex = GetSelection(m_listCtrl);
2761     if (nIndex == -1)
2762         return;
2763     string strName = (string)m_listCtrl->GetItemText(nIndex);
2764     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2765     string strAddressOrg = strAddress;
2766
2767     if (nPage == SENDING)
2768     {
2769         // Ask name and address
2770         do
2771         {
2772             CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2773             if (!dialog.ShowModal())
2774                 return;
2775             strName = dialog.GetValue1();
2776             strAddress = dialog.GetValue2();
2777         }
2778         while (CheckIfMine(strAddress, _("Edit Address")));
2779
2780     }
2781     else if (nPage == RECEIVING)
2782     {
2783         // Ask name
2784         CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2785         if (!dialog.ShowModal())
2786             return;
2787         strName = dialog.GetValue();
2788     }
2789
2790     // Write back
2791     CRITICAL_BLOCK(pwalletMain->cs_wallet)
2792     {
2793         if (strAddress != strAddressOrg)
2794             pwalletMain->DelAddressBookName(strAddressOrg);
2795         pwalletMain->SetAddressBookName(strAddress, strName);
2796     }
2797     m_listCtrl->SetItem(nIndex, 1, strAddress);
2798     m_listCtrl->SetItemText(nIndex, strName);
2799     pframeMain->RefreshListCtrl();
2800 }
2801
2802 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2803 {
2804     string strName;
2805     string strAddress;
2806
2807     if (nPage == SENDING)
2808     {
2809         // Ask name and address
2810         do
2811         {
2812             CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2813             if (!dialog.ShowModal())
2814                 return;
2815             strName = dialog.GetValue1();
2816             strAddress = dialog.GetValue2();
2817         }
2818         while (CheckIfMine(strAddress, _("Add Address")));
2819     }
2820     else if (nPage == RECEIVING)
2821     {
2822         // Ask name
2823         CGetTextFromUserDialog dialog(this,
2824             _("New Receiving Address"),
2825             _("You should use a new address for each payment you receive.\n\nLabel"),
2826             "");
2827         if (!dialog.ShowModal())
2828             return;
2829         strName = dialog.GetValue();
2830
2831         bool fWasLocked = pwalletMain->IsLocked();
2832         if (!GetWalletPassphrase())
2833             return;
2834
2835         // Generate new key
2836         std::vector<unsigned char> newKey;
2837         pwalletMain->GetKeyFromPool(newKey, true);
2838         strAddress = CBitcoinAddress(newKey).ToString();
2839
2840         if (fWasLocked)
2841             pwalletMain->Lock();
2842     }
2843
2844     // Add to list and select it
2845     CRITICAL_BLOCK(pwalletMain->cs_wallet)
2846         pwalletMain->SetAddressBookName(strAddress, strName);
2847     int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2848     SetSelection(m_listCtrl, nIndex);
2849     m_listCtrl->SetFocus();
2850     if (nPage == SENDING)
2851         pframeMain->RefreshListCtrl();
2852 }
2853
2854 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2855 {
2856     // OK
2857     EndModal(GetSelectedAddress() != "" ? 1 : 0);
2858 }
2859
2860 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2861 {
2862     // Cancel
2863     EndModal(0);
2864 }
2865
2866 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2867 {
2868     // Close
2869     EndModal(0);
2870 }
2871
2872
2873
2874
2875
2876
2877 //////////////////////////////////////////////////////////////////////////////
2878 //
2879 // CMyTaskBarIcon
2880 //
2881
2882 enum
2883 {
2884     ID_TASKBAR_RESTORE = 10001,
2885     ID_TASKBAR_SEND,
2886     ID_TASKBAR_OPTIONS,
2887     ID_TASKBAR_GENERATE,
2888     ID_TASKBAR_EXIT,
2889 };
2890
2891 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2892     EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2893     EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2894     EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2895     EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2896     EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2897     EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2898 END_EVENT_TABLE()
2899
2900 void CMyTaskBarIcon::Show(bool fShow)
2901 {
2902     static char pszPrevTip[200];
2903     if (fShow)
2904     {
2905         string strTooltip = _("Bitcoin");
2906         if (fGenerateBitcoins)
2907             strTooltip = _("Bitcoin - Generating");
2908         if (fGenerateBitcoins && vNodes.empty())
2909             strTooltip = _("Bitcoin - (not connected)");
2910
2911         // Optimization, only update when changed, using char array to be reentrant
2912         if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2913         {
2914             strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2915 #ifdef __WXMSW__
2916             // somehow it'll choose the wrong size and scale it down if
2917             // we use the main icon, so we hand it one with only 16x16
2918             SetIcon(wxICON(favicon), strTooltip);
2919 #else
2920             SetIcon(bitcoin80_xpm, strTooltip);
2921 #endif
2922         }
2923     }
2924     else
2925     {
2926         strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2927         RemoveIcon();
2928     }
2929 }
2930
2931 void CMyTaskBarIcon::Hide()
2932 {
2933     Show(false);
2934 }
2935
2936 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2937 {
2938     Restore();
2939 }
2940
2941 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2942 {
2943     Restore();
2944 }
2945
2946 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2947 {
2948     // Taskbar: Send
2949     CSendDialog dialog(pframeMain);
2950     dialog.ShowModal();
2951 }
2952
2953 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2954 {
2955     // Since it's modal, get the main window to do it
2956     wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2957     pframeMain->GetEventHandler()->AddPendingEvent(event2);
2958 }
2959
2960 void CMyTaskBarIcon::Restore()
2961 {
2962     pframeMain->Show();
2963     wxIconizeEvent event(0, false);
2964     pframeMain->GetEventHandler()->AddPendingEvent(event);
2965     pframeMain->Iconize(false);
2966     pframeMain->Raise();
2967 }
2968
2969 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2970 {
2971     event.Check(fGenerateBitcoins);
2972 }
2973
2974 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2975 {
2976     pframeMain->Close(true);
2977 }
2978
2979 void CMyTaskBarIcon::UpdateTooltip()
2980 {
2981     if (IsIconInstalled())
2982         Show(true);
2983 }
2984
2985 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2986 {
2987     wxMenu* pmenu = new wxMenu;
2988     pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2989     pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2990     pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2991 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2992     pmenu->AppendSeparator();
2993     pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2994 #endif
2995     return pmenu;
2996 }
2997
2998
2999
3000
3001
3002
3003 //////////////////////////////////////////////////////////////////////////////
3004 //
3005 // CMyApp
3006 //
3007
3008 void CreateMainWindow()
3009 {
3010     pframeMain = new CMainFrame(NULL);
3011     if (GetBoolArg("-min"))
3012         pframeMain->Iconize(true);
3013 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3014     if (!GetBoolArg("-minimizetotray"))
3015         fMinimizeToTray = false;
3016 #endif
3017     pframeMain->Show(true);  // have to show first to get taskbar button to hide
3018     if (fMinimizeToTray && pframeMain->IsIconized())
3019         fClosedToTray = true;
3020     pframeMain->Show(!fClosedToTray);
3021     ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3022     CreateThread(ThreadDelayedRepaint, NULL);
3023 }
3024
3025
3026 // Define a new application
3027 class CMyApp : public wxApp
3028 {
3029 public:
3030     CMyApp(){};
3031     ~CMyApp(){};
3032     bool OnInit();
3033     bool OnInit2();
3034     int OnExit();
3035
3036     // Hook Initialize so we can start without GUI
3037     virtual bool Initialize(int& argc, wxChar** argv);
3038
3039     // 2nd-level exception handling: we get all the exceptions occurring in any
3040     // event handler here
3041     virtual bool OnExceptionInMainLoop();
3042
3043     // 3rd, and final, level exception handling: whenever an unhandled
3044     // exception is caught, this function is called
3045     virtual void OnUnhandledException();
3046
3047     // and now for something different: this function is called in case of a
3048     // crash (e.g. dereferencing null pointer, division by 0, ...)
3049     virtual void OnFatalException();
3050 };
3051
3052 IMPLEMENT_APP(CMyApp)
3053
3054 bool CMyApp::Initialize(int& argc, wxChar** argv)
3055 {
3056     for (int i = 1; i < argc; i++)
3057         if (!IsSwitchChar(argv[i][0]))
3058             fCommandLine = true;
3059
3060     if (!fCommandLine)
3061     {
3062         // wxApp::Initialize will remove environment-specific parameters,
3063         // so it's too early to call ParseParameters yet
3064         for (int i = 1; i < argc; i++)
3065         {
3066             wxString str = argv[i];
3067             #ifdef __WXMSW__
3068             if (str.size() >= 1 && str[0] == '/')
3069                 str[0] = '-';
3070             char pszLower[MAX_PATH];
3071             strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3072             strlwr(pszLower);
3073             str = pszLower;
3074             #endif
3075             if (str == "-daemon")
3076                 fDaemon = true;
3077         }
3078     }
3079
3080 #ifdef __WXGTK__
3081     if (fDaemon || fCommandLine)
3082     {
3083         // Call the original Initialize while suppressing error messages
3084         // and ignoring failure.  If unable to initialize GTK, it fails
3085         // near the end so hopefully the last few things don't matter.
3086         {
3087             wxLogNull logNo;
3088             wxApp::Initialize(argc, argv);
3089         }
3090
3091         if (fDaemon)
3092         {
3093             // Daemonize
3094             pid_t pid = fork();
3095             if (pid < 0)
3096             {
3097                 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3098                 return false;
3099             }
3100             if (pid > 0)
3101                 pthread_exit((void*)0);
3102
3103             pid_t sid = setsid();
3104             if (sid < 0)
3105                 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3106         }
3107
3108         return true;
3109     }
3110 #endif
3111
3112     return wxApp::Initialize(argc, argv);
3113 }
3114
3115 bool CMyApp::OnInit()
3116 {
3117 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3118     // Disable malfunctioning wxWidgets debug assertion
3119     extern int g_isPainting;
3120     g_isPainting = 10000;
3121 #endif
3122 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3123     SetAppName("Bitcoin");
3124 #else
3125     SetAppName("bitcoin");
3126 #endif
3127 #ifdef __WXMSW__
3128 #if wxUSE_UNICODE
3129     // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3130     // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3131     class wxMBConv_win32 : public wxMBConv
3132     {
3133     public:
3134         long m_CodePage;
3135         size_t m_minMBCharWidth;
3136     };
3137     if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3138         ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3139 #endif
3140 #endif
3141
3142     // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3143     g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3144     g_locale.AddCatalogLookupPathPrefix("locale");
3145 #ifndef __WXMSW__
3146     g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3147     g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3148 #endif
3149     g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3150     g_locale.AddCatalog("bitcoin");
3151
3152 #ifdef __WXMSW__
3153     HDC hdc = GetDC(NULL);
3154     if (hdc)
3155     {
3156         nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3157         nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3158         ReleaseDC(NULL, hdc);
3159     }
3160 #endif
3161
3162     return AppInit(argc, argv);
3163 }
3164
3165 int CMyApp::OnExit()
3166 {
3167     Shutdown(NULL);
3168     return wxApp::OnExit();
3169 }
3170
3171 bool CMyApp::OnExceptionInMainLoop()
3172 {
3173     try
3174     {
3175         throw;
3176     }
3177     catch (std::exception& e)
3178     {
3179         PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3180         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3181         Sleep(1000);
3182         throw;
3183     }
3184     catch (...)
3185     {
3186         PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3187         wxLogWarning("Unknown exception");
3188         Sleep(1000);
3189         throw;
3190     }
3191     return true;
3192 }
3193
3194 void CMyApp::OnUnhandledException()
3195 {
3196     // this shows how we may let some exception propagate uncaught
3197     try
3198     {
3199         throw;
3200     }
3201     catch (std::exception& e)
3202     {
3203         PrintException(&e, "CMyApp::OnUnhandledException()");
3204         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3205         Sleep(1000);
3206         throw;
3207     }
3208     catch (...)
3209     {
3210         PrintException(NULL, "CMyApp::OnUnhandledException()");
3211         wxLogWarning("Unknown exception");
3212         Sleep(1000);
3213         throw;
3214     }
3215 }
3216
3217 void CMyApp::OnFatalException()
3218 {
3219     wxMessageBox(_("Program has crashed and will terminate.  "), "Bitcoin", wxOK | wxICON_ERROR);
3220 }