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