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