fix -datadir=./subdir relative path,
[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 && IsLockdown())
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 bool fPrevLockdown;
1017     if (IsLockdown())
1018         m_statusBar->SetStatusText(string("    ") + _("WARNING: Displayed transactions may not be correct!  You may need to upgrade, or other nodes may need to upgrade."), 0);
1019     else if (fPrevLockdown)
1020         m_statusBar->SetStatusText("", 0);
1021     fPrevLockdown = IsLockdown();
1022
1023     string strGen = "";
1024     if (fGenerateBitcoins)
1025         strGen = _("    Generating");
1026     if (fGenerateBitcoins && vNodes.empty())
1027         strGen = _("(not connected)");
1028     m_statusBar->SetStatusText(strGen, 1);
1029
1030     string strStatus = strprintf(_("     %d connections     %d blocks     %d transactions"), vNodes.size(), nBestHeight + 1, nTransactionCount);
1031     m_statusBar->SetStatusText(strStatus, 2);
1032
1033     if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
1034         m_statusBar->SetStatusText("     ERROR: ThreadSocketHandler has stopped", 0);
1035
1036     // Update receiving address
1037     string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1038     if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1039         m_textCtrlAddress->SetValue(strDefaultAddress);
1040 }
1041
1042
1043 void UIThreadCall(boost::function0<void> fn)
1044 {
1045     // Call this with a function object created with bind.
1046     // bind needs all parameters to match the function's expected types
1047     // and all default parameters specified.  Some examples:
1048     //  UIThreadCall(bind(wxBell));
1049     //  UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1050     //  UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1051     if (pframeMain)
1052     {
1053         wxCommandEvent event(wxEVT_UITHREADCALL);
1054         event.SetClientData((void*)new boost::function0<void>(fn));
1055         pframeMain->GetEventHandler()->AddPendingEvent(event);
1056     }
1057 }
1058
1059 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1060 {
1061     boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1062     (*pfn)();
1063     delete pfn;
1064 }
1065
1066 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1067 {
1068     // File->Exit
1069     Close(true);
1070 }
1071
1072 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
1073 {
1074     // Options->Generate Coins
1075     GenerateBitcoins(event.IsChecked());
1076 }
1077
1078 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1079 {
1080     event.Check(fGenerateBitcoins);
1081 }
1082
1083 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1084 {
1085     // Options->Your Receiving Addresses
1086     CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1087     if (!dialog.ShowModal())
1088         return;
1089 }
1090
1091 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1092 {
1093     // Options->Options
1094     COptionsDialog dialog(this);
1095     dialog.ShowModal();
1096 }
1097
1098 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1099 {
1100     // Help->About
1101     CAboutDialog dialog(this);
1102     dialog.ShowModal();
1103 }
1104
1105 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1106 {
1107     // Toolbar: Send
1108     CSendDialog dialog(this);
1109     dialog.ShowModal();
1110 }
1111
1112 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1113 {
1114     // Toolbar: Address Book
1115     CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1116     if (dialogAddr.ShowModal() == 2)
1117     {
1118         // Send
1119         CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1120         dialogSend.ShowModal();
1121     }
1122 }
1123
1124 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1125 {
1126     // Automatically select-all when entering window
1127     event.Skip();
1128     m_textCtrlAddress->SetSelection(-1, -1);
1129     fOnSetFocusAddress = true;
1130 }
1131
1132 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1133 {
1134     event.Skip();
1135     if (fOnSetFocusAddress)
1136         m_textCtrlAddress->SetSelection(-1, -1);
1137     fOnSetFocusAddress = false;
1138 }
1139
1140 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1141 {
1142     // Ask name
1143     CGetTextFromUserDialog dialog(this,
1144         _("New Receiving Address"),
1145         _("You should use a new address for each payment you receive.\n\nLabel"),
1146         "");
1147     if (!dialog.ShowModal())
1148         return;
1149     string strName = dialog.GetValue();
1150
1151     // Generate new key
1152     string strAddress = PubKeyToAddress(GenerateNewKey());
1153
1154     // Save
1155     SetAddressBookName(strAddress, strName);
1156     SetDefaultReceivingAddress(strAddress);
1157 }
1158
1159 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1160 {
1161     // Copy address box to clipboard
1162     if (wxTheClipboard->Open())
1163     {
1164         wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1165         wxTheClipboard->Close();
1166     }
1167 }
1168
1169 void CMainFrame::OnListItemActivated(wxListEvent& event)
1170 {
1171     uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1172     CWalletTx wtx;
1173     CRITICAL_BLOCK(cs_mapWallet)
1174     {
1175         map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1176         if (mi == mapWallet.end())
1177         {
1178             printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1179             return;
1180         }
1181         wtx = (*mi).second;
1182     }
1183     CTxDetailsDialog dialog(this, wtx);
1184     dialog.ShowModal();
1185     //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1186     //pdialog->Show();
1187 }
1188
1189
1190
1191
1192
1193
1194 //////////////////////////////////////////////////////////////////////////////
1195 //
1196 // CTxDetailsDialog
1197 //
1198
1199 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1200 {
1201     CRITICAL_BLOCK(cs_mapAddressBook)
1202     {
1203         string strHTML;
1204         strHTML.reserve(4000);
1205         strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1206
1207         int64 nTime = wtx.GetTxTime();
1208         int64 nCredit = wtx.GetCredit();
1209         int64 nDebit = wtx.GetDebit();
1210         int64 nNet = nCredit - nDebit;
1211
1212
1213
1214         strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1215         int nRequests = wtx.GetRequestCount();
1216         if (nRequests != -1)
1217         {
1218             if (nRequests == 0)
1219                 strHTML += _(", has not been successfully broadcast yet");
1220             else if (nRequests == 1)
1221                 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1222             else
1223                 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1224         }
1225         strHTML += "<br>";
1226
1227         strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1228
1229
1230         //
1231         // From
1232         //
1233         if (wtx.IsCoinBase())
1234         {
1235             strHTML += _("<b>Source:</b> Generated<br>");
1236         }
1237         else if (!wtx.mapValue["from"].empty())
1238         {
1239             // Online transaction
1240             if (!wtx.mapValue["from"].empty())
1241                 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1242         }
1243         else
1244         {
1245             // Offline transaction
1246             if (nNet > 0)
1247             {
1248                 // Credit
1249                 foreach(const CTxOut& txout, wtx.vout)
1250                 {
1251                     if (txout.IsMine())
1252                     {
1253                         vector<unsigned char> vchPubKey;
1254                         if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1255                         {
1256                             string strAddress = PubKeyToAddress(vchPubKey);
1257                             if (mapAddressBook.count(strAddress))
1258                             {
1259                                 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1260                                 strHTML += _("<b>To:</b> ");
1261                                 strHTML += HtmlEscape(strAddress);
1262                                 if (!mapAddressBook[strAddress].empty())
1263                                     strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1264                                 else
1265                                     strHTML += _(" (yours)");
1266                                 strHTML += "<br>";
1267                             }
1268                         }
1269                         break;
1270                     }
1271                 }
1272             }
1273         }
1274
1275
1276         //
1277         // To
1278         //
1279         string strAddress;
1280         if (!wtx.mapValue["to"].empty())
1281         {
1282             // Online transaction
1283             strAddress = wtx.mapValue["to"];
1284             strHTML += _("<b>To:</b> ");
1285             if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1286                 strHTML += mapAddressBook[strAddress] + " ";
1287             strHTML += HtmlEscape(strAddress) + "<br>";
1288         }
1289
1290
1291         //
1292         // Amount
1293         //
1294         if (wtx.IsCoinBase() && nCredit == 0)
1295         {
1296             //
1297             // Coinbase
1298             //
1299             int64 nUnmatured = 0;
1300             foreach(const CTxOut& txout, wtx.vout)
1301                 nUnmatured += txout.GetCredit();
1302             strHTML += _("<b>Credit:</b> ");
1303             if (wtx.IsInMainChain())
1304                 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1305             else
1306                 strHTML += _("(not accepted)");
1307             strHTML += "<br>";
1308         }
1309         else if (nNet > 0)
1310         {
1311             //
1312             // Credit
1313             //
1314             strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1315         }
1316         else
1317         {
1318             bool fAllFromMe = true;
1319             foreach(const CTxIn& txin, wtx.vin)
1320                 fAllFromMe = fAllFromMe && txin.IsMine();
1321
1322             bool fAllToMe = true;
1323             foreach(const CTxOut& txout, wtx.vout)
1324                 fAllToMe = fAllToMe && txout.IsMine();
1325
1326             if (fAllFromMe)
1327             {
1328                 //
1329                 // Debit
1330                 //
1331                 foreach(const CTxOut& txout, wtx.vout)
1332                 {
1333                     if (txout.IsMine())
1334                         continue;
1335
1336                     if (wtx.mapValue["to"].empty())
1337                     {
1338                         // Offline transaction
1339                         uint160 hash160;
1340                         if (ExtractHash160(txout.scriptPubKey, hash160))
1341                         {
1342                             string strAddress = Hash160ToAddress(hash160);
1343                             strHTML += _("<b>To:</b> ");
1344                             if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1345                                 strHTML += mapAddressBook[strAddress] + " ";
1346                             strHTML += strAddress;
1347                             strHTML += "<br>";
1348                         }
1349                     }
1350
1351                     strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1352                 }
1353
1354                 if (fAllToMe)
1355                 {
1356                     // Payment to self
1357                     /// issue: can't tell which is the payment and which is the change anymore
1358                     //int64 nValue = wtx.vout[0].nValue;
1359                     //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1360                     //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1361                 }
1362
1363                 int64 nTxFee = nDebit - wtx.GetValueOut();
1364                 if (nTxFee > 0)
1365                     strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1366             }
1367             else
1368             {
1369                 //
1370                 // Mixed debit transaction
1371                 //
1372                 foreach(const CTxIn& txin, wtx.vin)
1373                     if (txin.IsMine())
1374                         strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1375                 foreach(const CTxOut& txout, wtx.vout)
1376                     if (txout.IsMine())
1377                         strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1378             }
1379         }
1380
1381         strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1382
1383
1384         //
1385         // Message
1386         //
1387         if (!wtx.mapValue["message"].empty())
1388             strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1389
1390         if (wtx.IsCoinBase())
1391             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>";
1392
1393
1394         //
1395         // Debug view
1396         //
1397         if (fDebug)
1398         {
1399             strHTML += "<hr><br>debug print<br><br>";
1400             foreach(const CTxIn& txin, wtx.vin)
1401                 if (txin.IsMine())
1402                     strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1403             foreach(const CTxOut& txout, wtx.vout)
1404                 if (txout.IsMine())
1405                     strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1406
1407             strHTML += "<b>Inputs:</b><br>";
1408             CRITICAL_BLOCK(cs_mapWallet)
1409             {
1410                 foreach(const CTxIn& txin, wtx.vin)
1411                 {
1412                     COutPoint prevout = txin.prevout;
1413                     map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1414                     if (mi != mapWallet.end())
1415                     {
1416                         const CWalletTx& prev = (*mi).second;
1417                         if (prevout.n < prev.vout.size())
1418                         {
1419                             strHTML += HtmlEscape(prev.ToString(), true);
1420                             strHTML += " &nbsp;&nbsp; " + FormatTxStatus(prev) + ", ";
1421                             strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1422                         }
1423                     }
1424                 }
1425             }
1426
1427             strHTML += "<br><hr><br><b>Transaction:</b><br>";
1428             strHTML += HtmlEscape(wtx.ToString(), true);
1429         }
1430
1431
1432
1433         strHTML += "</font></html>";
1434         string(strHTML.begin(), strHTML.end()).swap(strHTML);
1435         m_htmlWin->SetPage(strHTML);
1436         m_buttonOK->SetFocus();
1437     }
1438 }
1439
1440 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1441 {
1442     Close();
1443     //Destroy();
1444 }
1445
1446
1447
1448
1449
1450
1451 //////////////////////////////////////////////////////////////////////////////
1452 //
1453 // Startup folder
1454 //
1455
1456 #ifdef __WXMSW__
1457 string StartupShortcutPath()
1458 {
1459     return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1460 }
1461
1462 bool GetStartOnSystemStartup()
1463 {
1464     return filesystem::exists(StartupShortcutPath().c_str());
1465 }
1466
1467 void SetStartOnSystemStartup(bool fAutoStart)
1468 {
1469     // If the shortcut exists already, remove it for updating
1470     remove(StartupShortcutPath().c_str());
1471
1472     if (fAutoStart)
1473     {
1474         CoInitialize(NULL);
1475
1476         // Get a pointer to the IShellLink interface.
1477         IShellLink* psl = NULL;
1478         HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1479                                 CLSCTX_INPROC_SERVER, IID_IShellLink,
1480                                 reinterpret_cast<void**>(&psl));
1481
1482         if (SUCCEEDED(hres))
1483         {
1484             // Get the current executable path
1485             TCHAR pszExePath[MAX_PATH];
1486             GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1487
1488             // Set the path to the shortcut target
1489             psl->SetPath(pszExePath);
1490             PathRemoveFileSpec(pszExePath);
1491             psl->SetWorkingDirectory(pszExePath);
1492             psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1493
1494             // Query IShellLink for the IPersistFile interface for
1495             // saving the shortcut in persistent storage.
1496             IPersistFile* ppf = NULL;
1497             hres = psl->QueryInterface(IID_IPersistFile,
1498                                        reinterpret_cast<void**>(&ppf));
1499             if (SUCCEEDED(hres))
1500             {
1501                 WCHAR pwsz[MAX_PATH];
1502                 // Ensure that the string is ANSI.
1503                 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1504                 // Save the link by calling IPersistFile::Save.
1505                 hres = ppf->Save(pwsz, TRUE);
1506                 ppf->Release();
1507             }
1508             psl->Release();
1509         }
1510         CoUninitialize();
1511     }
1512 }
1513
1514 #elif defined(__WXGTK__)
1515
1516 // Follow the Desktop Application Autostart Spec:
1517 //  http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1518
1519 boost::filesystem::path GetAutostartDir()
1520 {
1521     namespace fs = boost::filesystem;
1522
1523     char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1524     if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1525     char* pszHome = getenv("HOME");
1526     if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1527     return fs::path();
1528 }
1529
1530 boost::filesystem::path GetAutostartFilePath()
1531 {
1532     return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1533 }
1534
1535 bool GetStartOnSystemStartup()
1536 {
1537     boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1538     if (!optionFile.good())
1539         return false;
1540     // Scan through file for "Hidden=true":
1541     string line;
1542     while (!optionFile.eof())
1543     {
1544         getline(optionFile, line);
1545         if (line.find("Hidden") != string::npos &&
1546             line.find("true") != string::npos)
1547             return false;
1548     }
1549     optionFile.close();
1550
1551     return true;
1552 }
1553
1554 void SetStartOnSystemStartup(bool fAutoStart)
1555 {
1556     if (!fAutoStart)
1557     {
1558         unlink(GetAutostartFilePath().native_file_string().c_str());
1559     }
1560     else
1561     {
1562         char pszExePath[MAX_PATH+1];
1563         memset(pszExePath, 0, sizeof(pszExePath));
1564         if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1565             return;
1566
1567         boost::filesystem::create_directories(GetAutostartDir());
1568
1569         boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1570         if (!optionFile.good())
1571         {
1572             wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1573             return;
1574         }
1575         // Write a bitcoin.desktop file to the autostart directory:
1576         optionFile << "[Desktop Entry]\n";
1577         optionFile << "Type=Application\n";
1578         optionFile << "Name=Bitcoin\n";
1579         optionFile << "Exec=" << pszExePath << "\n";
1580         optionFile << "Terminal=false\n";
1581         optionFile << "Hidden=false\n";
1582         optionFile.close();
1583     }
1584 }
1585 #else
1586
1587 // TODO: OSX startup stuff; see:
1588 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1589
1590 bool GetStartOnSystemStartup() { return false; }
1591 void SetStartOnSystemStartup(bool fAutoStart) { }
1592
1593 #endif
1594
1595
1596
1597
1598
1599
1600 //////////////////////////////////////////////////////////////////////////////
1601 //
1602 // COptionsDialog
1603 //
1604
1605 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1606 {
1607     // Set up list box of page choices
1608     m_listBox->Append(_("Main"));
1609     //m_listBox->Append(_("Test 2"));
1610     m_listBox->SetSelection(0);
1611     SelectPage(0);
1612 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1613     m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1614     if (!mapArgs.count("-minimizetotray"))
1615     {
1616         // Minimize to tray is just too buggy on Linux
1617         fMinimizeToTray = false;
1618         m_checkBoxMinimizeToTray->SetValue(false);
1619         m_checkBoxMinimizeToTray->Enable(false);
1620         m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1621     }
1622 #endif
1623 #ifdef __WXMAC_OSX__
1624     m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1625 #endif
1626
1627     // Init values
1628     m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1629     m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
1630     m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
1631     m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
1632     int nProcessors = wxThread::GetCPUCount();
1633     if (nProcessors < 1)
1634         nProcessors = 999;
1635     m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
1636     m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1637     m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1638     m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1639     m_checkBoxUseProxy->SetValue(fUseProxy);
1640     m_textCtrlProxyIP->Enable(fUseProxy);
1641     m_textCtrlProxyPort->Enable(fUseProxy);
1642     m_staticTextProxyIP->Enable(fUseProxy);
1643     m_staticTextProxyPort->Enable(fUseProxy);
1644     m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1645     m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1646
1647     m_buttonOK->SetFocus();
1648 }
1649
1650 void COptionsDialog::SelectPage(int nPage)
1651 {
1652     m_panelMain->Show(nPage == 0);
1653     m_panelTest2->Show(nPage == 1);
1654
1655     m_scrolledWindow->Layout();
1656     m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1657 }
1658
1659 void COptionsDialog::OnListBox(wxCommandEvent& event)
1660 {
1661     SelectPage(event.GetSelection());
1662 }
1663
1664 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1665 {
1666     event.Skip();
1667     int64 nTmp = nTransactionFee;
1668     ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1669     m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1670 }
1671
1672 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
1673 {
1674     m_spinCtrlLimitProcessors->Enable(event.IsChecked());
1675 }
1676
1677 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1678 {
1679     m_textCtrlProxyIP->Enable(event.IsChecked());
1680     m_textCtrlProxyPort->Enable(event.IsChecked());
1681     m_staticTextProxyIP->Enable(event.IsChecked());
1682     m_staticTextProxyPort->Enable(event.IsChecked());
1683 }
1684
1685 CAddress COptionsDialog::GetProxyAddr()
1686 {
1687     // Be careful about byte order, addr.ip and addr.port are big endian
1688     CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1689     if (addr.ip == INADDR_NONE)
1690         addr.ip = addrProxy.ip;
1691     int nPort = atoi(m_textCtrlProxyPort->GetValue());
1692     addr.port = htons(nPort);
1693     if (nPort <= 0 || nPort > USHRT_MAX)
1694         addr.port = addrProxy.port;
1695     return addr;
1696 }
1697
1698 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1699 {
1700     event.Skip();
1701     m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1702     m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1703 }
1704
1705
1706 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1707 {
1708     OnButtonApply(event);
1709     Close();
1710 }
1711
1712 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1713 {
1714     Close();
1715 }
1716
1717 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1718 {
1719     CWalletDB walletdb;
1720
1721     int64 nPrevTransactionFee = nTransactionFee;
1722     if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1723         walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1724
1725     int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
1726     if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
1727     {
1728         fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
1729         walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
1730     }
1731     if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
1732     {
1733         nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
1734         walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
1735     }
1736     if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
1737         GenerateBitcoins(fGenerateBitcoins);
1738
1739     if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1740     {
1741         fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1742         SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1743     }
1744
1745     if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1746     {
1747         fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1748         walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1749         ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1750     }
1751
1752     if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1753     {
1754         fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1755         walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1756     }
1757
1758     fUseProxy = m_checkBoxUseProxy->GetValue();
1759     walletdb.WriteSetting("fUseProxy", fUseProxy);
1760
1761     addrProxy = GetProxyAddr();
1762     walletdb.WriteSetting("addrProxy", addrProxy);
1763 }
1764
1765
1766
1767
1768
1769
1770 //////////////////////////////////////////////////////////////////////////////
1771 //
1772 // CAboutDialog
1773 //
1774
1775 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1776 {
1777     m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d%s beta"), VERSION/10000, (VERSION/100)%100, VERSION%100, pszSubVer));
1778
1779     // Change (c) into UTF-8 or ANSI copyright symbol
1780     wxString str = m_staticTextMain->GetLabel();
1781 #if wxUSE_UNICODE
1782     str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1783 #else
1784     str.Replace("(c)", "\xA9");
1785 #endif
1786     m_staticTextMain->SetLabel(str);
1787 #ifndef __WXMSW__
1788     // Resize on Linux to make the window fit the text.
1789     // The text was wrapped manually rather than using the Wrap setting because
1790     // the wrap would be too small on Linux and it can't be changed at this point.
1791     wxFont fontTmp = m_staticTextMain->GetFont();
1792     if (fontTmp.GetPointSize() > 8);
1793         fontTmp.SetPointSize(8);
1794     m_staticTextMain->SetFont(fontTmp);
1795     SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1796 #endif
1797 }
1798
1799 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1800 {
1801     Close();
1802 }
1803
1804
1805
1806
1807
1808
1809 //////////////////////////////////////////////////////////////////////////////
1810 //
1811 // CSendDialog
1812 //
1813
1814 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1815 {
1816     // Init
1817     m_textCtrlAddress->SetValue(strAddress);
1818     m_choiceTransferType->SetSelection(0);
1819     m_bitmapCheckMark->Show(false);
1820     fEnabledPrev = true;
1821     m_textCtrlAddress->SetFocus();
1822     //// todo: should add a display of your balance for convenience
1823 #ifndef __WXMSW__
1824     wxFont fontTmp = m_staticTextInstructions->GetFont();
1825     if (fontTmp.GetPointSize() > 9);
1826         fontTmp.SetPointSize(9);
1827     m_staticTextInstructions->SetFont(fontTmp);
1828     SetSize(725, 380);
1829 #endif
1830
1831     // Set Icon
1832     wxIcon iconSend;
1833     iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1834     SetIcon(iconSend);
1835
1836     wxCommandEvent event;
1837     OnTextAddress(event);
1838
1839     // Fixup the tab order
1840     m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1841     m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1842     this->Layout();
1843 }
1844
1845 void CSendDialog::OnTextAddress(wxCommandEvent& event)
1846 {
1847     // Check mark
1848     event.Skip();
1849     bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
1850     m_bitmapCheckMark->Show(fBitcoinAddress);
1851
1852     // Grey out message if bitcoin address
1853     bool fEnable = !fBitcoinAddress;
1854     m_staticTextFrom->Enable(fEnable);
1855     m_textCtrlFrom->Enable(fEnable);
1856     m_staticTextMessage->Enable(fEnable);
1857     m_textCtrlMessage->Enable(fEnable);
1858     m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
1859     if (!fEnable && fEnabledPrev)
1860     {
1861         strFromSave    = m_textCtrlFrom->GetValue();
1862         strMessageSave = m_textCtrlMessage->GetValue();
1863         m_textCtrlFrom->SetValue(_("n/a"));
1864         m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
1865     }
1866     else if (fEnable && !fEnabledPrev)
1867     {
1868         m_textCtrlFrom->SetValue(strFromSave);
1869         m_textCtrlMessage->SetValue(strMessageSave);
1870     }
1871     fEnabledPrev = fEnable;
1872 }
1873
1874 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1875 {
1876     // Reformat the amount
1877     event.Skip();
1878     if (m_textCtrlAmount->GetValue().Trim().empty())
1879         return;
1880     int64 nTmp;
1881     if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1882         m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1883 }
1884
1885 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1886 {
1887     // Open address book
1888     CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1889     if (dialog.ShowModal())
1890         m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1891 }
1892
1893 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1894 {
1895     // Copy clipboard to address box
1896     if (wxTheClipboard->Open())
1897     {
1898         if (wxTheClipboard->IsSupported(wxDF_TEXT))
1899         {
1900             wxTextDataObject data;
1901             wxTheClipboard->GetData(data);
1902             m_textCtrlAddress->SetValue(data.GetText());
1903         }
1904         wxTheClipboard->Close();
1905     }
1906 }
1907
1908 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1909 {
1910     CWalletTx wtx;
1911     string strAddress = (string)m_textCtrlAddress->GetValue();
1912
1913     // Parse amount
1914     int64 nValue = 0;
1915     if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1916     {
1917         wxMessageBox(_("Error in amount  "), _("Send Coins"));
1918         return;
1919     }
1920     if (nValue > GetBalance())
1921     {
1922         wxMessageBox(_("Amount exceeds your balance  "), _("Send Coins"));
1923         return;
1924     }
1925     if (nValue + nTransactionFee > GetBalance())
1926     {
1927         wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included  "), _("Send Coins"));
1928         return;
1929     }
1930
1931     // Parse bitcoin address
1932     uint160 hash160;
1933     bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1934
1935     if (fBitcoinAddress)
1936     {
1937         // Send to bitcoin address
1938         CScript scriptPubKey;
1939         scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1940
1941         string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1942         if (strError == "")
1943             wxMessageBox(_("Payment sent  "), _("Sending..."));
1944         else if (strError != "ABORTED")
1945             wxMessageBox(strError + "  ", _("Sending..."));
1946     }
1947     else
1948     {
1949         // Parse IP address
1950         CAddress addr(strAddress);
1951         if (!addr.IsValid())
1952         {
1953             wxMessageBox(_("Invalid address  "), _("Send Coins"));
1954             return;
1955         }
1956
1957         // Message
1958         wtx.mapValue["to"] = strAddress;
1959         wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
1960         wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
1961
1962         // Send to IP address
1963         CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1964         if (!pdialog->ShowModal())
1965             return;
1966     }
1967
1968     CRITICAL_BLOCK(cs_mapAddressBook)
1969         if (!mapAddressBook.count(strAddress))
1970             SetAddressBookName(strAddress, "");
1971
1972     EndModal(true);
1973 }
1974
1975 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1976 {
1977     // Cancel
1978     EndModal(false);
1979 }
1980
1981
1982
1983
1984
1985
1986 //////////////////////////////////////////////////////////////////////////////
1987 //
1988 // CSendingDialog
1989 //
1990
1991 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
1992 {
1993     addr = addrIn;
1994     nPrice = nPriceIn;
1995     wtx = wtxIn;
1996     start = wxDateTime::UNow();
1997     memset(pszStatus, 0, sizeof(pszStatus));
1998     fCanCancel = true;
1999     fAbort = false;
2000     fSuccess = false;
2001     fUIDone = false;
2002     fWorkDone = false;
2003 #ifndef __WXMSW__
2004     SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2005 #endif
2006
2007     SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2008     m_textCtrlStatus->SetValue("");
2009
2010     CreateThread(SendingDialogStartTransfer, this);
2011 }
2012
2013 CSendingDialog::~CSendingDialog()
2014 {
2015     printf("~CSendingDialog()\n");
2016 }
2017
2018 void CSendingDialog::Close()
2019 {
2020     // Last one out turn out the lights.
2021     // fWorkDone signals that work side is done and UI thread should call destroy.
2022     // fUIDone signals that UI window has closed and work thread should call destroy.
2023     // This allows the window to disappear and end modality when cancelled
2024     // without making the user wait for ConnectNode to return.  The dialog object
2025     // hangs around in the background until the work thread exits.
2026     if (IsModal())
2027         EndModal(fSuccess);
2028     else
2029         Show(false);
2030     if (fWorkDone)
2031         Destroy();
2032     else
2033         fUIDone = true;
2034 }
2035
2036 void CSendingDialog::OnClose(wxCloseEvent& event)
2037 {
2038     if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2039     {
2040         Close();
2041     }
2042     else
2043     {
2044         event.Veto();
2045         wxCommandEvent cmdevent;
2046         OnButtonCancel(cmdevent);
2047     }
2048 }
2049
2050 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2051 {
2052     if (fWorkDone)
2053         Close();
2054 }
2055
2056 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2057 {
2058     if (fCanCancel)
2059         fAbort = true;
2060 }
2061
2062 void CSendingDialog::OnPaint(wxPaintEvent& event)
2063 {
2064     event.Skip();
2065     if (strlen(pszStatus) > 130)
2066         m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2067     else
2068         m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2069     m_staticTextSending->SetFocus();
2070     if (!fCanCancel)
2071         m_buttonCancel->Enable(false);
2072     if (fWorkDone)
2073     {
2074         m_buttonOK->Enable(true);
2075         m_buttonOK->SetFocus();
2076         m_buttonCancel->Enable(false);
2077     }
2078     if (fAbort && fCanCancel && IsShown())
2079     {
2080         strcpy(pszStatus, _("CANCELLED"));
2081         m_buttonOK->Enable(true);
2082         m_buttonOK->SetFocus();
2083         m_buttonCancel->Enable(false);
2084         m_buttonCancel->SetLabel(_("Cancelled"));
2085         Close();
2086         wxMessageBox(_("Transfer cancelled  "), _("Sending..."), wxOK, this);
2087     }
2088 }
2089
2090
2091 //
2092 // Everything from here on is not in the UI thread and must only communicate
2093 // with the rest of the dialog through variables and calling repaint.
2094 //
2095
2096 void CSendingDialog::Repaint()
2097 {
2098     Refresh();
2099     wxPaintEvent event;
2100     GetEventHandler()->AddPendingEvent(event);
2101 }
2102
2103 bool CSendingDialog::Status()
2104 {
2105     if (fUIDone)
2106     {
2107         Destroy();
2108         return false;
2109     }
2110     if (fAbort && fCanCancel)
2111     {
2112         memset(pszStatus, 0, 10);
2113         strcpy(pszStatus, _("CANCELLED"));
2114         Repaint();
2115         fWorkDone = true;
2116         return false;
2117     }
2118     return true;
2119 }
2120
2121 bool CSendingDialog::Status(const string& str)
2122 {
2123     if (!Status())
2124         return false;
2125
2126     // This can be read by the UI thread at any time,
2127     // so copy in a way that can be read cleanly at all times.
2128     memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2129     strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2130
2131     Repaint();
2132     return true;
2133 }
2134
2135 bool CSendingDialog::Error(const string& str)
2136 {
2137     fCanCancel = false;
2138     fWorkDone = true;
2139     Status(string(_("Error: ")) + str);
2140     return false;
2141 }
2142
2143 void SendingDialogStartTransfer(void* parg)
2144 {
2145     ((CSendingDialog*)parg)->StartTransfer();
2146 }
2147
2148 void CSendingDialog::StartTransfer()
2149 {
2150     // Make sure we have enough money
2151     if (nPrice + nTransactionFee > GetBalance())
2152     {
2153         Error(_("Insufficient funds"));
2154         return;
2155     }
2156
2157     // We may have connected already for product details
2158     if (!Status(_("Connecting...")))
2159         return;
2160     CNode* pnode = ConnectNode(addr, 15 * 60);
2161     if (!pnode)
2162     {
2163         Error(_("Unable to connect"));
2164         return;
2165     }
2166
2167     // Send order to seller, with response going to OnReply2 via event handler
2168     if (!Status(_("Requesting public key...")))
2169         return;
2170     pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2171 }
2172
2173 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2174 {
2175     ((CSendingDialog*)parg)->OnReply2(vRecv);
2176 }
2177
2178 void CSendingDialog::OnReply2(CDataStream& vRecv)
2179 {
2180     if (!Status(_("Received public key...")))
2181         return;
2182
2183     CScript scriptPubKey;
2184     int nRet;
2185     try
2186     {
2187         vRecv >> nRet;
2188         if (nRet > 0)
2189         {
2190             string strMessage;
2191             vRecv >> strMessage;
2192             Error(_("Transfer was not accepted"));
2193             //// todo: enlarge the window and enable a hidden white box to put seller's message
2194             return;
2195         }
2196         vRecv >> scriptPubKey;
2197     }
2198     catch (...)
2199     {
2200         //// what do we want to do about this?
2201         Error(_("Invalid response received"));
2202         return;
2203     }
2204
2205     // Pause to give the user a chance to cancel
2206     while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2207     {
2208         Sleep(200);
2209         if (!Status())
2210             return;
2211     }
2212
2213     CRITICAL_BLOCK(cs_main)
2214     {
2215         // Pay
2216         if (!Status(_("Creating transaction...")))
2217             return;
2218         if (nPrice + nTransactionFee > GetBalance())
2219         {
2220             Error(_("Insufficient funds"));
2221             return;
2222         }
2223         CKey key;
2224         int64 nFeeRequired;
2225         if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
2226         {
2227             if (nPrice + nFeeRequired > GetBalance())
2228                 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
2229             else
2230                 Error(_("Transaction creation failed"));
2231             return;
2232         }
2233
2234         // Transaction fee
2235         if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2236         {
2237             Error(_("Transaction aborted"));
2238             return;
2239         }
2240
2241         // Make sure we're still connected
2242         CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2243         if (!pnode)
2244         {
2245             Error(_("Lost connection, transaction cancelled"));
2246             return;
2247         }
2248
2249         // Last chance to cancel
2250         Sleep(50);
2251         if (!Status())
2252             return;
2253         fCanCancel = false;
2254         if (fAbort)
2255         {
2256             fCanCancel = true;
2257             if (!Status())
2258                 return;
2259             fCanCancel = false;
2260         }
2261         if (!Status(_("Sending payment...")))
2262             return;
2263
2264         // Commit
2265         if (!CommitTransaction(wtx, key))
2266         {
2267             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."));
2268             return;
2269         }
2270
2271         // Send payment tx to seller, with response going to OnReply3 via event handler
2272         CWalletTx wtxSend = wtx;
2273         wtxSend.fFromMe = false;
2274         pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2275
2276         Status(_("Waiting for confirmation..."));
2277         MainFrameRepaint();
2278     }
2279 }
2280
2281 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2282 {
2283     ((CSendingDialog*)parg)->OnReply3(vRecv);
2284 }
2285
2286 void CSendingDialog::OnReply3(CDataStream& vRecv)
2287 {
2288     int nRet;
2289     try
2290     {
2291         vRecv >> nRet;
2292         if (nRet > 0)
2293         {
2294             Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2295                     "The transaction is recorded and will credit to the recipient,\n"
2296                     "but the comment information will be blank."));
2297             return;
2298         }
2299     }
2300     catch (...)
2301     {
2302         //// what do we want to do about this?
2303         Error(_("Payment was sent, but an invalid response was received"));
2304         return;
2305     }
2306
2307     fSuccess = true;
2308     fWorkDone = true;
2309     Status(_("Payment completed"));
2310 }
2311
2312
2313
2314
2315
2316
2317 //////////////////////////////////////////////////////////////////////////////
2318 //
2319 // CAddressBookDialog
2320 //
2321
2322 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2323 {
2324     // Set initially selected page
2325     wxNotebookEvent event;
2326     event.SetSelection(nPageIn);
2327     OnNotebookPageChanged(event);
2328     m_notebook->ChangeSelection(nPageIn);
2329
2330     fDuringSend = fDuringSendIn;
2331     if (!fDuringSend)
2332         m_buttonCancel->Show(false);
2333
2334     // Set Icon
2335     wxIcon iconAddressBook;
2336     iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2337     SetIcon(iconAddressBook);
2338
2339     // Init column headers
2340     m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2341     m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2342     m_listCtrlSending->SetFocus();
2343     m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2344     m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2345     m_listCtrlReceiving->SetFocus();
2346
2347     // Fill listctrl with address book data
2348     CRITICAL_BLOCK(cs_mapKeys)
2349     CRITICAL_BLOCK(cs_mapAddressBook)
2350     {
2351         string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2352         foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2353         {
2354             string strAddress = item.first;
2355             string strName = item.second;
2356             uint160 hash160;
2357             bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2358             wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2359             int nIndex = InsertLine(plistCtrl, strName, strAddress);
2360             if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2361                 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2362         }
2363     }
2364 }
2365
2366 wxString CAddressBookDialog::GetSelectedAddress()
2367 {
2368     int nIndex = GetSelection(m_listCtrl);
2369     if (nIndex == -1)
2370         return "";
2371     return GetItemText(m_listCtrl, nIndex, 1);
2372 }
2373
2374 wxString CAddressBookDialog::GetSelectedSendingAddress()
2375 {
2376     int nIndex = GetSelection(m_listCtrlSending);
2377     if (nIndex == -1)
2378         return "";
2379     return GetItemText(m_listCtrlSending, nIndex, 1);
2380 }
2381
2382 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2383 {
2384     int nIndex = GetSelection(m_listCtrlReceiving);
2385     if (nIndex == -1)
2386         return "";
2387     return GetItemText(m_listCtrlReceiving, nIndex, 1);
2388 }
2389
2390 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2391 {
2392     event.Skip();
2393     nPage = event.GetSelection();
2394     if (nPage == SENDING)
2395         m_listCtrl = m_listCtrlSending;
2396     else if (nPage == RECEIVING)
2397         m_listCtrl = m_listCtrlReceiving;
2398     m_buttonDelete->Show(nPage == SENDING);
2399     m_buttonCopy->Show(nPage == RECEIVING);
2400     this->Layout();
2401     m_listCtrl->SetFocus();
2402 }
2403
2404 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2405 {
2406     // Update address book with edited name
2407     event.Skip();
2408     if (event.IsEditCancelled())
2409         return;
2410     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2411     SetAddressBookName(strAddress, string(event.GetText()));
2412     pframeMain->RefreshListCtrl();
2413 }
2414
2415 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2416 {
2417     event.Skip();
2418     if (nPage == RECEIVING)
2419         SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2420 }
2421
2422 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2423 {
2424     event.Skip();
2425     if (fDuringSend)
2426     {
2427         // Doubleclick returns selection
2428         EndModal(GetSelectedAddress() != "" ? 2 : 0);
2429         return;
2430     }
2431
2432     // Doubleclick edits item
2433     wxCommandEvent event2;
2434     OnButtonEdit(event2);
2435 }
2436
2437 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2438 {
2439     if (nPage != SENDING)
2440         return;
2441     for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2442     {
2443         if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2444         {
2445             string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2446             CWalletDB().EraseName(strAddress);
2447             m_listCtrl->DeleteItem(nIndex);
2448         }
2449     }
2450     pframeMain->RefreshListCtrl();
2451 }
2452
2453 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2454 {
2455     // Copy address box to clipboard
2456     if (wxTheClipboard->Open())
2457     {
2458         wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2459         wxTheClipboard->Close();
2460     }
2461 }
2462
2463 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2464 {
2465     uint160 hash160;
2466     bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2467     if (fMine)
2468         wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book.  "), strTitle);
2469     return fMine;
2470 }
2471
2472 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2473 {
2474     int nIndex = GetSelection(m_listCtrl);
2475     if (nIndex == -1)
2476         return;
2477     string strName = (string)m_listCtrl->GetItemText(nIndex);
2478     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2479     string strAddressOrg = strAddress;
2480
2481     if (nPage == SENDING)
2482     {
2483         // Ask name and address
2484         do
2485         {
2486             CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2487             if (!dialog.ShowModal())
2488                 return;
2489             strName = dialog.GetValue1();
2490             strAddress = dialog.GetValue2();
2491         }
2492         while (CheckIfMine(strAddress, _("Edit Address")));
2493
2494     }
2495     else if (nPage == RECEIVING)
2496     {
2497         // Ask name
2498         CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2499         if (!dialog.ShowModal())
2500             return;
2501         strName = dialog.GetValue();
2502     }
2503
2504     // Write back
2505     if (strAddress != strAddressOrg)
2506         CWalletDB().EraseName(strAddressOrg);
2507     SetAddressBookName(strAddress, strName);
2508     m_listCtrl->SetItem(nIndex, 1, strAddress);
2509     m_listCtrl->SetItemText(nIndex, strName);
2510     pframeMain->RefreshListCtrl();
2511 }
2512
2513 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2514 {
2515     string strName;
2516     string strAddress;
2517
2518     if (nPage == SENDING)
2519     {
2520         // Ask name and address
2521         do
2522         {
2523             CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2524             if (!dialog.ShowModal())
2525                 return;
2526             strName = dialog.GetValue1();
2527             strAddress = dialog.GetValue2();
2528         }
2529         while (CheckIfMine(strAddress, _("Add Address")));
2530     }
2531     else if (nPage == RECEIVING)
2532     {
2533         // Ask name
2534         CGetTextFromUserDialog dialog(this,
2535             _("New Receiving Address"),
2536             _("You should use a new address for each payment you receive.\n\nLabel"),
2537             "");
2538         if (!dialog.ShowModal())
2539             return;
2540         strName = dialog.GetValue();
2541
2542         // Generate new key
2543         strAddress = PubKeyToAddress(GenerateNewKey());
2544     }
2545
2546     // Add to list and select it
2547     SetAddressBookName(strAddress, strName);
2548     int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2549     SetSelection(m_listCtrl, nIndex);
2550     m_listCtrl->SetFocus();
2551     if (nPage == SENDING)
2552         pframeMain->RefreshListCtrl();
2553 }
2554
2555 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2556 {
2557     // OK
2558     EndModal(GetSelectedAddress() != "" ? 1 : 0);
2559 }
2560
2561 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2562 {
2563     // Cancel
2564     EndModal(0);
2565 }
2566
2567 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2568 {
2569     // Close
2570     EndModal(0);
2571 }
2572
2573
2574
2575
2576
2577
2578 //////////////////////////////////////////////////////////////////////////////
2579 //
2580 // CMyTaskBarIcon
2581 //
2582
2583 enum
2584 {
2585     ID_TASKBAR_RESTORE = 10001,
2586     ID_TASKBAR_OPTIONS,
2587     ID_TASKBAR_GENERATE,
2588     ID_TASKBAR_EXIT,
2589 };
2590
2591 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2592     EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2593     EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2594     EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2595     EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
2596     EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2597     EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2598 END_EVENT_TABLE()
2599
2600 void CMyTaskBarIcon::Show(bool fShow)
2601 {
2602     static char pszPrevTip[200];
2603     if (fShow)
2604     {
2605         string strTooltip = _("Bitcoin");
2606         if (fGenerateBitcoins)
2607             strTooltip = _("Bitcoin - Generating");
2608         if (fGenerateBitcoins && vNodes.empty())
2609             strTooltip = _("Bitcoin - (not connected)");
2610
2611         // Optimization, only update when changed, using char array to be reentrant
2612         if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2613         {
2614             strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2615 #ifdef __WXMSW__
2616             // somehow it'll choose the wrong size and scale it down if
2617             // we use the main icon, so we hand it one with only 16x16
2618             SetIcon(wxICON(favicon), strTooltip);
2619 #else
2620             SetIcon(bitcoin80_xpm, strTooltip);
2621 #endif
2622         }
2623     }
2624     else
2625     {
2626         strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2627         RemoveIcon();
2628     }
2629 }
2630
2631 void CMyTaskBarIcon::Hide()
2632 {
2633     Show(false);
2634 }
2635
2636 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2637 {
2638     Restore();
2639 }
2640
2641 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2642 {
2643     Restore();
2644 }
2645
2646 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2647 {
2648     // Since it's modal, get the main window to do it
2649     wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2650     pframeMain->GetEventHandler()->AddPendingEvent(event2);
2651 }
2652
2653 void CMyTaskBarIcon::Restore()
2654 {
2655     pframeMain->Show();
2656     wxIconizeEvent event(0, false);
2657     pframeMain->GetEventHandler()->AddPendingEvent(event);
2658     pframeMain->Iconize(false);
2659     pframeMain->Raise();
2660 }
2661
2662 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
2663 {
2664     GenerateBitcoins(event.IsChecked());
2665 }
2666
2667 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2668 {
2669     event.Check(fGenerateBitcoins);
2670 }
2671
2672 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2673 {
2674     pframeMain->Close(true);
2675 }
2676
2677 void CMyTaskBarIcon::UpdateTooltip()
2678 {
2679     if (IsIconInstalled())
2680         Show(true);
2681 }
2682
2683 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2684 {
2685     wxMenu* pmenu = new wxMenu;
2686     pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2687     pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2688     pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
2689 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2690     pmenu->AppendSeparator();
2691     pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2692 #endif
2693     return pmenu;
2694 }
2695
2696
2697
2698
2699
2700
2701 //////////////////////////////////////////////////////////////////////////////
2702 //
2703 // CMyApp
2704 //
2705
2706 void CreateMainWindow()
2707 {
2708     pframeMain = new CMainFrame(NULL);
2709     if (mapArgs.count("-min"))
2710         pframeMain->Iconize(true);
2711 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2712     if (!mapArgs.count("-minimizetotray"))
2713         fMinimizeToTray = false;
2714 #endif
2715     pframeMain->Show(true);  // have to show first to get taskbar button to hide
2716     if (fMinimizeToTray && pframeMain->IsIconized())
2717         fClosedToTray = true;
2718     pframeMain->Show(!fClosedToTray);
2719     ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2720     CreateThread(ThreadDelayedRepaint, NULL);
2721 }
2722
2723
2724 // Define a new application
2725 class CMyApp : public wxApp
2726 {
2727 public:
2728     CMyApp(){};
2729     ~CMyApp(){};
2730     bool OnInit();
2731     bool OnInit2();
2732     int OnExit();
2733
2734     // Hook Initialize so we can start without GUI
2735     virtual bool Initialize(int& argc, wxChar** argv);
2736
2737     // 2nd-level exception handling: we get all the exceptions occurring in any
2738     // event handler here
2739     virtual bool OnExceptionInMainLoop();
2740
2741     // 3rd, and final, level exception handling: whenever an unhandled
2742     // exception is caught, this function is called
2743     virtual void OnUnhandledException();
2744
2745     // and now for something different: this function is called in case of a
2746     // crash (e.g. dereferencing null pointer, division by 0, ...)
2747     virtual void OnFatalException();
2748 };
2749
2750 IMPLEMENT_APP(CMyApp)
2751
2752 bool CMyApp::Initialize(int& argc, wxChar** argv)
2753 {
2754     for (int i = 1; i < argc; i++)
2755         if (!IsSwitchChar(argv[i][0]))
2756             fCommandLine = true;
2757
2758     if (!fCommandLine)
2759     {
2760         // wxApp::Initialize will remove environment-specific parameters,
2761         // so it's too early to call ParseParameters yet
2762         for (int i = 1; i < argc; i++)
2763         {
2764             wxString str = argv[i];
2765             #ifdef __WXMSW__
2766             if (str.size() >= 1 && str[0] == '/')
2767                 str[0] = '-';
2768             char pszLower[MAX_PATH];
2769             strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2770             strlwr(pszLower);
2771             str = pszLower;
2772             #endif
2773             if (str == "-daemon")
2774                 fDaemon = true;
2775         }
2776     }
2777
2778 #ifdef __WXGTK__
2779     if (fDaemon || fCommandLine)
2780     {
2781         // Call the original Initialize while suppressing error messages
2782         // and ignoring failure.  If unable to initialize GTK, it fails
2783         // near the end so hopefully the last few things don't matter.
2784         {
2785             wxLogNull logNo;
2786             wxApp::Initialize(argc, argv);
2787         }
2788
2789         if (fDaemon)
2790         {
2791             // Daemonize
2792             pid_t pid = fork();
2793             if (pid < 0)
2794             {
2795                 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2796                 return false;
2797             }
2798             if (pid > 0)
2799                 pthread_exit((void*)0);
2800         }
2801
2802         return true;
2803     }
2804 #endif
2805
2806     return wxApp::Initialize(argc, argv);
2807 }
2808
2809 bool CMyApp::OnInit()
2810 {
2811 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2812     // Disable malfunctioning wxWidgets debug assertion
2813     extern int g_isPainting;
2814     g_isPainting = 10000;
2815 #endif
2816 #ifdef GUI
2817     wxImage::AddHandler(new wxPNGHandler);
2818 #endif
2819 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2820     SetAppName("Bitcoin");
2821 #else
2822     SetAppName("bitcoin");
2823 #endif
2824 #ifdef __WXMSW__
2825 #if wxUSE_UNICODE
2826     // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2827     // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2828     class wxMBConv_win32 : public wxMBConv
2829     {
2830     public:
2831         long m_CodePage;
2832         size_t m_minMBCharWidth;
2833     };
2834     if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2835         ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2836 #endif
2837 #endif
2838
2839     // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2840     g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2841     g_locale.AddCatalogLookupPathPrefix("locale");
2842 #ifndef __WXMSW__
2843     g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2844     g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2845 #endif
2846     g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2847     g_locale.AddCatalog("bitcoin");
2848
2849     return AppInit(argc, argv);
2850 }
2851
2852 int CMyApp::OnExit()
2853 {
2854     Shutdown(NULL);
2855     return wxApp::OnExit();
2856 }
2857
2858 bool CMyApp::OnExceptionInMainLoop()
2859 {
2860     try
2861     {
2862         throw;
2863     }
2864     catch (std::exception& e)
2865     {
2866         PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2867         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2868         Sleep(1000);
2869         throw;
2870     }
2871     catch (...)
2872     {
2873         PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2874         wxLogWarning("Unknown exception");
2875         Sleep(1000);
2876         throw;
2877     }
2878     return true;
2879 }
2880
2881 void CMyApp::OnUnhandledException()
2882 {
2883     // this shows how we may let some exception propagate uncaught
2884     try
2885     {
2886         throw;
2887     }
2888     catch (std::exception& e)
2889     {
2890         PrintException(&e, "CMyApp::OnUnhandledException()");
2891         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2892         Sleep(1000);
2893         throw;
2894     }
2895     catch (...)
2896     {
2897         PrintException(NULL, "CMyApp::OnUnhandledException()");
2898         wxLogWarning("Unknown exception");
2899         Sleep(1000);
2900         throw;
2901     }
2902 }
2903
2904 void CMyApp::OnFatalException()
2905 {
2906     wxMessageBox(_("Program has crashed and will terminate.  "), "Bitcoin", wxOK | wxICON_ERROR);
2907 }