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