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