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