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