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