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