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