Merge pull request #190 from johnmaguire2013/tray.
[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     EndModal(false);
1470 }
1471
1472
1473
1474
1475
1476
1477 //////////////////////////////////////////////////////////////////////////////
1478 //
1479 // Startup folder
1480 //
1481
1482 #ifdef __WXMSW__
1483 string StartupShortcutPath()
1484 {
1485     return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1486 }
1487
1488 bool GetStartOnSystemStartup()
1489 {
1490     return filesystem::exists(StartupShortcutPath().c_str());
1491 }
1492
1493 void SetStartOnSystemStartup(bool fAutoStart)
1494 {
1495     // If the shortcut exists already, remove it for updating
1496     remove(StartupShortcutPath().c_str());
1497
1498     if (fAutoStart)
1499     {
1500         CoInitialize(NULL);
1501
1502         // Get a pointer to the IShellLink interface.
1503         IShellLink* psl = NULL;
1504         HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1505                                 CLSCTX_INPROC_SERVER, IID_IShellLink,
1506                                 reinterpret_cast<void**>(&psl));
1507
1508         if (SUCCEEDED(hres))
1509         {
1510             // Get the current executable path
1511             TCHAR pszExePath[MAX_PATH];
1512             GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1513
1514             // Set the path to the shortcut target
1515             psl->SetPath(pszExePath);
1516             PathRemoveFileSpec(pszExePath);
1517             psl->SetWorkingDirectory(pszExePath);
1518             psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1519
1520             // Query IShellLink for the IPersistFile interface for
1521             // saving the shortcut in persistent storage.
1522             IPersistFile* ppf = NULL;
1523             hres = psl->QueryInterface(IID_IPersistFile,
1524                                        reinterpret_cast<void**>(&ppf));
1525             if (SUCCEEDED(hres))
1526             {
1527                 WCHAR pwsz[MAX_PATH];
1528                 // Ensure that the string is ANSI.
1529                 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1530                 // Save the link by calling IPersistFile::Save.
1531                 hres = ppf->Save(pwsz, TRUE);
1532                 ppf->Release();
1533             }
1534             psl->Release();
1535         }
1536         CoUninitialize();
1537     }
1538 }
1539
1540 #elif defined(__WXGTK__)
1541
1542 // Follow the Desktop Application Autostart Spec:
1543 //  http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1544
1545 boost::filesystem::path GetAutostartDir()
1546 {
1547     namespace fs = boost::filesystem;
1548
1549     char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1550     if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1551     char* pszHome = getenv("HOME");
1552     if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1553     return fs::path();
1554 }
1555
1556 boost::filesystem::path GetAutostartFilePath()
1557 {
1558     return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1559 }
1560
1561 bool GetStartOnSystemStartup()
1562 {
1563     boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1564     if (!optionFile.good())
1565         return false;
1566     // Scan through file for "Hidden=true":
1567     string line;
1568     while (!optionFile.eof())
1569     {
1570         getline(optionFile, line);
1571         if (line.find("Hidden") != string::npos &&
1572             line.find("true") != string::npos)
1573             return false;
1574     }
1575     optionFile.close();
1576
1577     return true;
1578 }
1579
1580 void SetStartOnSystemStartup(bool fAutoStart)
1581 {
1582     if (!fAutoStart)
1583     {
1584         unlink(GetAutostartFilePath().native_file_string().c_str());
1585     }
1586     else
1587     {
1588         char pszExePath[MAX_PATH+1];
1589         memset(pszExePath, 0, sizeof(pszExePath));
1590         if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1591             return;
1592
1593         boost::filesystem::create_directories(GetAutostartDir());
1594
1595         boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1596         if (!optionFile.good())
1597         {
1598             wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1599             return;
1600         }
1601         // Write a bitcoin.desktop file to the autostart directory:
1602         optionFile << "[Desktop Entry]\n";
1603         optionFile << "Type=Application\n";
1604         optionFile << "Name=Bitcoin\n";
1605         optionFile << "Exec=" << pszExePath << "\n";
1606         optionFile << "Terminal=false\n";
1607         optionFile << "Hidden=false\n";
1608         optionFile.close();
1609     }
1610 }
1611 #else
1612
1613 // TODO: OSX startup stuff; see:
1614 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1615
1616 bool GetStartOnSystemStartup() { return false; }
1617 void SetStartOnSystemStartup(bool fAutoStart) { }
1618
1619 #endif
1620
1621
1622
1623
1624
1625
1626 //////////////////////////////////////////////////////////////////////////////
1627 //
1628 // COptionsDialog
1629 //
1630
1631 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1632 {
1633     // Set up list box of page choices
1634     m_listBox->Append(_("Main"));
1635     //m_listBox->Append(_("Test 2"));
1636     m_listBox->SetSelection(0);
1637     SelectPage(0);
1638 #ifndef __WXMSW__
1639     SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1640 #endif
1641 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1642     m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1643     if (!GetBoolArg("-minimizetotray"))
1644     {
1645         // Minimize to tray is just too buggy on Linux
1646         fMinimizeToTray = false;
1647         m_checkBoxMinimizeToTray->SetValue(false);
1648         m_checkBoxMinimizeToTray->Enable(false);
1649         m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1650     }
1651 #endif
1652 #ifdef __WXMAC_OSX__
1653     m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1654 #endif
1655
1656     // Init values
1657     m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1658     m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
1659     m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
1660     m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
1661     int nProcessors = wxThread::GetCPUCount();
1662     if (nProcessors < 1)
1663         nProcessors = 999;
1664     m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
1665     m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1666     m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1667     m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1668     if (fHaveUPnP)
1669         m_checkBoxUseUPnP->SetValue(fUseUPnP);
1670     else
1671         m_checkBoxUseUPnP->Enable(false);
1672     m_checkBoxUseProxy->SetValue(fUseProxy);
1673     m_textCtrlProxyIP->Enable(fUseProxy);
1674     m_textCtrlProxyPort->Enable(fUseProxy);
1675     m_staticTextProxyIP->Enable(fUseProxy);
1676     m_staticTextProxyPort->Enable(fUseProxy);
1677     m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1678     m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1679
1680     m_buttonOK->SetFocus();
1681 }
1682
1683 void COptionsDialog::SelectPage(int nPage)
1684 {
1685     m_panelMain->Show(nPage == 0);
1686     m_panelTest2->Show(nPage == 1);
1687
1688     m_scrolledWindow->Layout();
1689     m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1690 }
1691
1692 void COptionsDialog::OnListBox(wxCommandEvent& event)
1693 {
1694     SelectPage(event.GetSelection());
1695 }
1696
1697 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1698 {
1699     event.Skip();
1700     int64 nTmp = nTransactionFee;
1701     ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1702     m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1703 }
1704
1705 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
1706 {
1707     m_spinCtrlLimitProcessors->Enable(event.IsChecked());
1708 }
1709
1710 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1711 {
1712     m_textCtrlProxyIP->Enable(event.IsChecked());
1713     m_textCtrlProxyPort->Enable(event.IsChecked());
1714     m_staticTextProxyIP->Enable(event.IsChecked());
1715     m_staticTextProxyPort->Enable(event.IsChecked());
1716 }
1717
1718 CAddress COptionsDialog::GetProxyAddr()
1719 {
1720     // Be careful about byte order, addr.ip and addr.port are big endian
1721     CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1722     if (addr.ip == INADDR_NONE)
1723         addr.ip = addrProxy.ip;
1724     int nPort = atoi(m_textCtrlProxyPort->GetValue());
1725     addr.port = htons(nPort);
1726     if (nPort <= 0 || nPort > USHRT_MAX)
1727         addr.port = addrProxy.port;
1728     return addr;
1729 }
1730
1731 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1732 {
1733     event.Skip();
1734     m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1735     m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1736 }
1737
1738
1739 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1740 {
1741     OnButtonApply(event);
1742     EndModal(false);
1743 }
1744
1745 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1746 {
1747     EndModal(false);
1748 }
1749
1750 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1751 {
1752     CWalletDB walletdb;
1753
1754     int64 nPrevTransactionFee = nTransactionFee;
1755     if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1756         walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1757
1758     int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
1759     if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
1760     {
1761         fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
1762         walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
1763     }
1764     if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
1765     {
1766         nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
1767         walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
1768     }
1769     if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
1770         GenerateBitcoins(fGenerateBitcoins);
1771
1772     if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1773     {
1774         fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1775         SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1776     }
1777
1778     if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1779     {
1780         fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1781         walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1782         ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1783     }
1784
1785     if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1786     {
1787         fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1788         walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1789     }
1790
1791     if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1792     {
1793         fUseUPnP = m_checkBoxUseUPnP->GetValue();
1794         walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1795         MapPort(fUseUPnP);
1796     }
1797
1798     fUseProxy = m_checkBoxUseProxy->GetValue();
1799     walletdb.WriteSetting("fUseProxy", fUseProxy);
1800
1801     addrProxy = GetProxyAddr();
1802     walletdb.WriteSetting("addrProxy", addrProxy);
1803 }
1804
1805
1806
1807
1808
1809
1810 //////////////////////////////////////////////////////////////////////////////
1811 //
1812 // CAboutDialog
1813 //
1814
1815 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1816 {
1817     m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
1818
1819     // Change (c) into UTF-8 or ANSI copyright symbol
1820     wxString str = m_staticTextMain->GetLabel();
1821 #if wxUSE_UNICODE
1822     str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1823 #else
1824     str.Replace("(c)", "\xA9");
1825 #endif
1826     m_staticTextMain->SetLabel(str);
1827 #ifndef __WXMSW__
1828     // Resize on Linux to make the window fit the text.
1829     // The text was wrapped manually rather than using the Wrap setting because
1830     // the wrap would be too small on Linux and it can't be changed at this point.
1831     wxFont fontTmp = m_staticTextMain->GetFont();
1832     if (fontTmp.GetPointSize() > 8);
1833         fontTmp.SetPointSize(8);
1834     m_staticTextMain->SetFont(fontTmp);
1835     SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1836 #endif
1837 }
1838
1839 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1840 {
1841     EndModal(false);
1842 }
1843
1844
1845
1846
1847
1848
1849 //////////////////////////////////////////////////////////////////////////////
1850 //
1851 // CSendDialog
1852 //
1853
1854 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1855 {
1856     // Init
1857     m_textCtrlAddress->SetValue(strAddress);
1858     m_choiceTransferType->SetSelection(0);
1859     m_bitmapCheckMark->Show(false);
1860     fEnabledPrev = true;
1861     m_textCtrlAddress->SetFocus();
1862     
1863     //// todo: should add a display of your balance for convenience
1864 #ifndef __WXMSW__
1865     wxFont fontTmp = m_staticTextInstructions->GetFont();
1866     if (fontTmp.GetPointSize() > 9);
1867         fontTmp.SetPointSize(9);
1868     m_staticTextInstructions->SetFont(fontTmp);
1869     SetSize(725, 180);
1870 #endif
1871     
1872     // Set Icon
1873     wxIcon iconSend;
1874     iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1875     SetIcon(iconSend);
1876
1877     // Fixup the tab order
1878     m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1879     m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1880     this->Layout();
1881 }
1882
1883 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1884 {
1885     // Reformat the amount
1886     event.Skip();
1887     if (m_textCtrlAmount->GetValue().Trim().empty())
1888         return;
1889     int64 nTmp;
1890     if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1891         m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1892 }
1893
1894 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1895 {
1896     // Open address book
1897     CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1898     if (dialog.ShowModal())
1899         m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1900 }
1901
1902 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1903 {
1904     // Copy clipboard to address box
1905     if (wxTheClipboard->Open())
1906     {
1907         if (wxTheClipboard->IsSupported(wxDF_TEXT))
1908         {
1909             wxTextDataObject data;
1910             wxTheClipboard->GetData(data);
1911             m_textCtrlAddress->SetValue(data.GetText());
1912         }
1913         wxTheClipboard->Close();
1914     }
1915 }
1916
1917 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1918 {
1919     static CCriticalSection cs_sendlock;
1920     TRY_CRITICAL_BLOCK(cs_sendlock)
1921     {
1922         CWalletTx wtx;
1923         string strAddress = (string)m_textCtrlAddress->GetValue();
1924
1925         // Parse amount
1926         int64 nValue = 0;
1927         if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1928         {
1929             wxMessageBox(_("Error in amount  "), _("Send Coins"));
1930             return;
1931         }
1932         if (nValue > GetBalance())
1933         {
1934             wxMessageBox(_("Amount exceeds your balance  "), _("Send Coins"));
1935             return;
1936         }
1937         if (nValue + nTransactionFee > GetBalance())
1938         {
1939             wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included  "), _("Send Coins"));
1940             return;
1941         }
1942
1943         // Parse bitcoin address
1944         uint160 hash160;
1945         bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1946
1947         if (fBitcoinAddress)
1948         {
1949             CRITICAL_BLOCK(cs_main)
1950             {
1951                 // Send to bitcoin address
1952                 CScript scriptPubKey;
1953                 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1954
1955                 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1956                 if (strError == "")
1957                     wxMessageBox(_("Payment sent  "), _("Sending..."));
1958                 else if (strError == "ABORTED")
1959                     return; // leave send dialog open
1960                 else
1961                 {
1962                     wxMessageBox(strError + "  ", _("Sending..."));
1963                     EndModal(false);
1964                 }
1965             }
1966         }
1967         else
1968         {
1969             // Parse IP address
1970             CAddress addr(strAddress);
1971             if (!addr.IsValid())
1972             {
1973                 wxMessageBox(_("Invalid address  "), _("Send Coins"));
1974                 return;
1975             }
1976
1977             // Message
1978             wtx.mapValue["to"] = strAddress;
1979
1980             // Send to IP address
1981             CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1982             if (!pdialog->ShowModal())
1983                 return;
1984         }
1985
1986         CRITICAL_BLOCK(cs_mapAddressBook)
1987             if (!mapAddressBook.count(strAddress))
1988                 SetAddressBookName(strAddress, "");
1989
1990         EndModal(true);
1991     }
1992 }
1993
1994 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1995 {
1996     // Cancel
1997     EndModal(false);
1998 }
1999
2000
2001
2002
2003
2004
2005 //////////////////////////////////////////////////////////////////////////////
2006 //
2007 // CSendingDialog
2008 //
2009
2010 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
2011 {
2012     addr = addrIn;
2013     nPrice = nPriceIn;
2014     wtx = wtxIn;
2015     start = wxDateTime::UNow();
2016     memset(pszStatus, 0, sizeof(pszStatus));
2017     fCanCancel = true;
2018     fAbort = false;
2019     fSuccess = false;
2020     fUIDone = false;
2021     fWorkDone = false;
2022 #ifndef __WXMSW__
2023     SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2024 #endif
2025
2026     SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2027     m_textCtrlStatus->SetValue("");
2028
2029     CreateThread(SendingDialogStartTransfer, this);
2030 }
2031
2032 CSendingDialog::~CSendingDialog()
2033 {
2034     printf("~CSendingDialog()\n");
2035 }
2036
2037 void CSendingDialog::Close()
2038 {
2039     // Last one out turn out the lights.
2040     // fWorkDone signals that work side is done and UI thread should call destroy.
2041     // fUIDone signals that UI window has closed and work thread should call destroy.
2042     // This allows the window to disappear and end modality when cancelled
2043     // without making the user wait for ConnectNode to return.  The dialog object
2044     // hangs around in the background until the work thread exits.
2045     if (IsModal())
2046         EndModal(fSuccess);
2047     else
2048         Show(false);
2049     if (fWorkDone)
2050         Destroy();
2051     else
2052         fUIDone = true;
2053 }
2054
2055 void CSendingDialog::OnClose(wxCloseEvent& event)
2056 {
2057     if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2058     {
2059         Close();
2060     }
2061     else
2062     {
2063         event.Veto();
2064         wxCommandEvent cmdevent;
2065         OnButtonCancel(cmdevent);
2066     }
2067 }
2068
2069 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2070 {
2071     if (fWorkDone)
2072         Close();
2073 }
2074
2075 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2076 {
2077     if (fCanCancel)
2078         fAbort = true;
2079 }
2080
2081 void CSendingDialog::OnPaint(wxPaintEvent& event)
2082 {
2083     event.Skip();
2084     if (strlen(pszStatus) > 130)
2085         m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2086     else
2087         m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2088     m_staticTextSending->SetFocus();
2089     if (!fCanCancel)
2090         m_buttonCancel->Enable(false);
2091     if (fWorkDone)
2092     {
2093         m_buttonOK->Enable(true);
2094         m_buttonOK->SetFocus();
2095         m_buttonCancel->Enable(false);
2096     }
2097     if (fAbort && fCanCancel && IsShown())
2098     {
2099         strcpy(pszStatus, _("CANCELLED"));
2100         m_buttonOK->Enable(true);
2101         m_buttonOK->SetFocus();
2102         m_buttonCancel->Enable(false);
2103         m_buttonCancel->SetLabel(_("Cancelled"));
2104         Close();
2105         wxMessageBox(_("Transfer cancelled  "), _("Sending..."), wxOK, this);
2106     }
2107 }
2108
2109
2110 //
2111 // Everything from here on is not in the UI thread and must only communicate
2112 // with the rest of the dialog through variables and calling repaint.
2113 //
2114
2115 void CSendingDialog::Repaint()
2116 {
2117     Refresh();
2118     wxPaintEvent event;
2119     GetEventHandler()->AddPendingEvent(event);
2120 }
2121
2122 bool CSendingDialog::Status()
2123 {
2124     if (fUIDone)
2125     {
2126         Destroy();
2127         return false;
2128     }
2129     if (fAbort && fCanCancel)
2130     {
2131         memset(pszStatus, 0, 10);
2132         strcpy(pszStatus, _("CANCELLED"));
2133         Repaint();
2134         fWorkDone = true;
2135         return false;
2136     }
2137     return true;
2138 }
2139
2140 bool CSendingDialog::Status(const string& str)
2141 {
2142     if (!Status())
2143         return false;
2144
2145     // This can be read by the UI thread at any time,
2146     // so copy in a way that can be read cleanly at all times.
2147     memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2148     strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2149
2150     Repaint();
2151     return true;
2152 }
2153
2154 bool CSendingDialog::Error(const string& str)
2155 {
2156     fCanCancel = false;
2157     fWorkDone = true;
2158     Status(string(_("Error: ")) + str);
2159     return false;
2160 }
2161
2162 void SendingDialogStartTransfer(void* parg)
2163 {
2164     ((CSendingDialog*)parg)->StartTransfer();
2165 }
2166
2167 void CSendingDialog::StartTransfer()
2168 {
2169     // Make sure we have enough money
2170     if (nPrice + nTransactionFee > GetBalance())
2171     {
2172         Error(_("Insufficient funds"));
2173         return;
2174     }
2175
2176     // We may have connected already for product details
2177     if (!Status(_("Connecting...")))
2178         return;
2179     CNode* pnode = ConnectNode(addr, 15 * 60);
2180     if (!pnode)
2181     {
2182         Error(_("Unable to connect"));
2183         return;
2184     }
2185
2186     // Send order to seller, with response going to OnReply2 via event handler
2187     if (!Status(_("Requesting public key...")))
2188         return;
2189     pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2190 }
2191
2192 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2193 {
2194     ((CSendingDialog*)parg)->OnReply2(vRecv);
2195 }
2196
2197 void CSendingDialog::OnReply2(CDataStream& vRecv)
2198 {
2199     if (!Status(_("Received public key...")))
2200         return;
2201
2202     CScript scriptPubKey;
2203     int nRet;
2204     try
2205     {
2206         vRecv >> nRet;
2207         if (nRet > 0)
2208         {
2209             string strMessage;
2210             if (!vRecv.empty())
2211                 vRecv >> strMessage;
2212             if (nRet == 2)
2213                 Error(_("Recipient is not accepting transactions sent by IP address"));
2214             else
2215                 Error(_("Transfer was not accepted"));
2216             //// todo: enlarge the window and enable a hidden white box to put seller's message
2217             return;
2218         }
2219         vRecv >> scriptPubKey;
2220     }
2221     catch (...)
2222     {
2223         //// what do we want to do about this?
2224         Error(_("Invalid response received"));
2225         return;
2226     }
2227
2228     // Pause to give the user a chance to cancel
2229     while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2230     {
2231         Sleep(200);
2232         if (!Status())
2233             return;
2234     }
2235
2236     CRITICAL_BLOCK(cs_main)
2237     {
2238         // Pay
2239         if (!Status(_("Creating transaction...")))
2240             return;
2241         if (nPrice + nTransactionFee > GetBalance())
2242         {
2243             Error(_("Insufficient funds"));
2244             return;
2245         }
2246         CReserveKey reservekey;
2247         int64 nFeeRequired;
2248         if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2249         {
2250             if (nPrice + nFeeRequired > GetBalance())
2251                 Error(strprintf(_("This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds"), FormatMoney(nFeeRequired).c_str()));
2252             else
2253                 Error(_("Transaction creation failed"));
2254             return;
2255         }
2256
2257         // Transaction fee
2258         if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2259         {
2260             Error(_("Transaction aborted"));
2261             return;
2262         }
2263
2264         // Make sure we're still connected
2265         CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2266         if (!pnode)
2267         {
2268             Error(_("Lost connection, transaction cancelled"));
2269             return;
2270         }
2271
2272         // Last chance to cancel
2273         Sleep(50);
2274         if (!Status())
2275             return;
2276         fCanCancel = false;
2277         if (fAbort)
2278         {
2279             fCanCancel = true;
2280             if (!Status())
2281                 return;
2282             fCanCancel = false;
2283         }
2284         if (!Status(_("Sending payment...")))
2285             return;
2286
2287         // Commit
2288         if (!CommitTransaction(wtx, reservekey))
2289         {
2290             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."));
2291             return;
2292         }
2293
2294         // Send payment tx to seller, with response going to OnReply3 via event handler
2295         CWalletTx wtxSend = wtx;
2296         wtxSend.fFromMe = false;
2297         pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2298
2299         Status(_("Waiting for confirmation..."));
2300         MainFrameRepaint();
2301     }
2302 }
2303
2304 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2305 {
2306     ((CSendingDialog*)parg)->OnReply3(vRecv);
2307 }
2308
2309 void CSendingDialog::OnReply3(CDataStream& vRecv)
2310 {
2311     int nRet;
2312     try
2313     {
2314         vRecv >> nRet;
2315         if (nRet > 0)
2316         {
2317             Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2318                     "The transaction is recorded and will credit to the recipient,\n"
2319                     "but the comment information will be blank."));
2320             return;
2321         }
2322     }
2323     catch (...)
2324     {
2325         //// what do we want to do about this?
2326         Error(_("Payment was sent, but an invalid response was received"));
2327         return;
2328     }
2329
2330     fSuccess = true;
2331     fWorkDone = true;
2332     Status(_("Payment completed"));
2333 }
2334
2335
2336
2337
2338
2339
2340 //////////////////////////////////////////////////////////////////////////////
2341 //
2342 // CAddressBookDialog
2343 //
2344
2345 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2346 {
2347     // Set initially selected page
2348     wxNotebookEvent event;
2349     event.SetSelection(nPageIn);
2350     OnNotebookPageChanged(event);
2351     m_notebook->ChangeSelection(nPageIn);
2352
2353     fDuringSend = fDuringSendIn;
2354     if (!fDuringSend)
2355         m_buttonCancel->Show(false);
2356
2357     // Set Icon
2358     wxIcon iconAddressBook;
2359     iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2360     SetIcon(iconAddressBook);
2361
2362     // Init column headers
2363     m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2364     m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2365     m_listCtrlSending->SetFocus();
2366     m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2367     m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2368     m_listCtrlReceiving->SetFocus();
2369
2370     // Fill listctrl with address book data
2371     CRITICAL_BLOCK(cs_mapKeys)
2372     CRITICAL_BLOCK(cs_mapAddressBook)
2373     {
2374         string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2375         foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2376         {
2377             string strAddress = item.first;
2378             string strName = item.second;
2379             uint160 hash160;
2380             bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2381             wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2382             int nIndex = InsertLine(plistCtrl, strName, strAddress);
2383             if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2384                 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2385         }
2386     }
2387 }
2388
2389 wxString CAddressBookDialog::GetSelectedAddress()
2390 {
2391     int nIndex = GetSelection(m_listCtrl);
2392     if (nIndex == -1)
2393         return "";
2394     return GetItemText(m_listCtrl, nIndex, 1);
2395 }
2396
2397 wxString CAddressBookDialog::GetSelectedSendingAddress()
2398 {
2399     int nIndex = GetSelection(m_listCtrlSending);
2400     if (nIndex == -1)
2401         return "";
2402     return GetItemText(m_listCtrlSending, nIndex, 1);
2403 }
2404
2405 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2406 {
2407     int nIndex = GetSelection(m_listCtrlReceiving);
2408     if (nIndex == -1)
2409         return "";
2410     return GetItemText(m_listCtrlReceiving, nIndex, 1);
2411 }
2412
2413 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2414 {
2415     event.Skip();
2416     nPage = event.GetSelection();
2417     if (nPage == SENDING)
2418         m_listCtrl = m_listCtrlSending;
2419     else if (nPage == RECEIVING)
2420         m_listCtrl = m_listCtrlReceiving;
2421     m_buttonDelete->Show(nPage == SENDING);
2422     m_buttonCopy->Show(nPage == RECEIVING);
2423     this->Layout();
2424     m_listCtrl->SetFocus();
2425 }
2426
2427 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2428 {
2429     // Update address book with edited name
2430     event.Skip();
2431     if (event.IsEditCancelled())
2432         return;
2433     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2434     SetAddressBookName(strAddress, string(event.GetText()));
2435     pframeMain->RefreshListCtrl();
2436 }
2437
2438 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2439 {
2440     event.Skip();
2441     if (nPage == RECEIVING)
2442         SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2443 }
2444
2445 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2446 {
2447     event.Skip();
2448     if (fDuringSend)
2449     {
2450         // Doubleclick returns selection
2451         EndModal(GetSelectedAddress() != "" ? 2 : 0);
2452         return;
2453     }
2454
2455     // Doubleclick edits item
2456     wxCommandEvent event2;
2457     OnButtonEdit(event2);
2458 }
2459
2460 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2461 {
2462     if (nPage != SENDING)
2463         return;
2464     for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2465     {
2466         if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2467         {
2468             string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2469             CWalletDB().EraseName(strAddress);
2470             m_listCtrl->DeleteItem(nIndex);
2471         }
2472     }
2473     pframeMain->RefreshListCtrl();
2474 }
2475
2476 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2477 {
2478     // Copy address box to clipboard
2479     if (wxTheClipboard->Open())
2480     {
2481         wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2482         wxTheClipboard->Close();
2483     }
2484 }
2485
2486 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2487 {
2488     uint160 hash160;
2489     bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2490     if (fMine)
2491         wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book.  "), strTitle);
2492     return fMine;
2493 }
2494
2495 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2496 {
2497     int nIndex = GetSelection(m_listCtrl);
2498     if (nIndex == -1)
2499         return;
2500     string strName = (string)m_listCtrl->GetItemText(nIndex);
2501     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2502     string strAddressOrg = strAddress;
2503
2504     if (nPage == SENDING)
2505     {
2506         // Ask name and address
2507         do
2508         {
2509             CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2510             if (!dialog.ShowModal())
2511                 return;
2512             strName = dialog.GetValue1();
2513             strAddress = dialog.GetValue2();
2514         }
2515         while (CheckIfMine(strAddress, _("Edit Address")));
2516
2517     }
2518     else if (nPage == RECEIVING)
2519     {
2520         // Ask name
2521         CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2522         if (!dialog.ShowModal())
2523             return;
2524         strName = dialog.GetValue();
2525     }
2526
2527     // Write back
2528     if (strAddress != strAddressOrg)
2529         CWalletDB().EraseName(strAddressOrg);
2530     SetAddressBookName(strAddress, strName);
2531     m_listCtrl->SetItem(nIndex, 1, strAddress);
2532     m_listCtrl->SetItemText(nIndex, strName);
2533     pframeMain->RefreshListCtrl();
2534 }
2535
2536 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2537 {
2538     string strName;
2539     string strAddress;
2540
2541     if (nPage == SENDING)
2542     {
2543         // Ask name and address
2544         do
2545         {
2546             CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2547             if (!dialog.ShowModal())
2548                 return;
2549             strName = dialog.GetValue1();
2550             strAddress = dialog.GetValue2();
2551         }
2552         while (CheckIfMine(strAddress, _("Add Address")));
2553     }
2554     else if (nPage == RECEIVING)
2555     {
2556         // Ask name
2557         CGetTextFromUserDialog dialog(this,
2558             _("New Receiving Address"),
2559             _("You should use a new address for each payment you receive.\n\nLabel"),
2560             "");
2561         if (!dialog.ShowModal())
2562             return;
2563         strName = dialog.GetValue();
2564
2565         // Generate new key
2566         strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2567     }
2568
2569     // Add to list and select it
2570     SetAddressBookName(strAddress, strName);
2571     int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2572     SetSelection(m_listCtrl, nIndex);
2573     m_listCtrl->SetFocus();
2574     if (nPage == SENDING)
2575         pframeMain->RefreshListCtrl();
2576 }
2577
2578 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2579 {
2580     // OK
2581     EndModal(GetSelectedAddress() != "" ? 1 : 0);
2582 }
2583
2584 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2585 {
2586     // Cancel
2587     EndModal(0);
2588 }
2589
2590 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2591 {
2592     // Close
2593     EndModal(0);
2594 }
2595
2596
2597
2598
2599
2600
2601 //////////////////////////////////////////////////////////////////////////////
2602 //
2603 // CMyTaskBarIcon
2604 //
2605
2606 enum
2607 {
2608     ID_TASKBAR_RESTORE = 10001,
2609     ID_TASKBAR_SEND,
2610     ID_TASKBAR_OPTIONS,
2611     ID_TASKBAR_GENERATE,
2612     ID_TASKBAR_EXIT,
2613 };
2614
2615 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2616     EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2617     EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2618     EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2619     EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2620     EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
2621     EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2622     EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2623 END_EVENT_TABLE()
2624
2625 void CMyTaskBarIcon::Show(bool fShow)
2626 {
2627     static char pszPrevTip[200];
2628     if (fShow)
2629     {
2630         string strTooltip = strprintf(_("Balance: %s"), FormatMoney(GetBalance()).c_str());
2631         if (fGenerateBitcoins)
2632             strTooltip = strprintf(_("Bitcoin - Generating (Balance: %s)"), FormatMoney(GetBalance()).c_str());
2633         if (fGenerateBitcoins && vNodes.empty())
2634             strTooltip = _("Bitcoin - (not connected)");
2635
2636         // Optimization, only update when changed, using char array to be reentrant
2637         if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2638         {
2639             strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2640 #ifdef __WXMSW__
2641             // somehow it'll choose the wrong size and scale it down if
2642             // we use the main icon, so we hand it one with only 16x16
2643             SetIcon(wxICON(favicon), strTooltip);
2644 #else
2645             SetIcon(bitcoin80_xpm, strTooltip);
2646 #endif
2647         }
2648     }
2649     else
2650     {
2651         strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2652         RemoveIcon();
2653     }
2654 }
2655
2656 void CMyTaskBarIcon::Hide()
2657 {
2658     Show(false);
2659 }
2660
2661 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2662 {
2663     Restore();
2664 }
2665
2666 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2667 {
2668     Restore();
2669 }
2670
2671 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2672 {
2673     // Taskbar: Send
2674     CSendDialog dialog(pframeMain);
2675     dialog.ShowModal();
2676 }
2677
2678 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2679 {
2680     // Since it's modal, get the main window to do it
2681     wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2682     pframeMain->GetEventHandler()->AddPendingEvent(event2);
2683 }
2684
2685 void CMyTaskBarIcon::Restore()
2686 {
2687     pframeMain->Show();
2688     wxIconizeEvent event(0, false);
2689     pframeMain->GetEventHandler()->AddPendingEvent(event);
2690     pframeMain->Iconize(false);
2691     pframeMain->Raise();
2692 }
2693
2694 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
2695 {
2696     GenerateBitcoins(event.IsChecked());
2697 }
2698
2699 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2700 {
2701     event.Check(fGenerateBitcoins);
2702 }
2703
2704 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2705 {
2706     pframeMain->Close(true);
2707 }
2708
2709 void CMyTaskBarIcon::UpdateTooltip()
2710 {
2711     if (IsIconInstalled())
2712         Show(true);
2713 }
2714
2715 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2716 {
2717     wxMenu* pmenu = new wxMenu;
2718     pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2719     pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2720     pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2721     pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
2722 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2723     pmenu->AppendSeparator();
2724     pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2725 #endif
2726     return pmenu;
2727 }
2728
2729
2730
2731
2732
2733
2734 //////////////////////////////////////////////////////////////////////////////
2735 //
2736 // CMyApp
2737 //
2738
2739 void CreateMainWindow()
2740 {
2741     pframeMain = new CMainFrame(NULL);
2742     if (GetBoolArg("-min"))
2743         pframeMain->Iconize(true);
2744 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2745     if (!GetBoolArg("-minimizetotray"))
2746         fMinimizeToTray = false;
2747 #endif
2748     pframeMain->Show(true);  // have to show first to get taskbar button to hide
2749     if (fMinimizeToTray && pframeMain->IsIconized())
2750         fClosedToTray = true;
2751     pframeMain->Show(!fClosedToTray);
2752     ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2753     CreateThread(ThreadDelayedRepaint, NULL);
2754 }
2755
2756
2757 // Define a new application
2758 class CMyApp : public wxApp
2759 {
2760 public:
2761     CMyApp(){};
2762     ~CMyApp(){};
2763     bool OnInit();
2764     bool OnInit2();
2765     int OnExit();
2766
2767     // Hook Initialize so we can start without GUI
2768     virtual bool Initialize(int& argc, wxChar** argv);
2769
2770     // 2nd-level exception handling: we get all the exceptions occurring in any
2771     // event handler here
2772     virtual bool OnExceptionInMainLoop();
2773
2774     // 3rd, and final, level exception handling: whenever an unhandled
2775     // exception is caught, this function is called
2776     virtual void OnUnhandledException();
2777
2778     // and now for something different: this function is called in case of a
2779     // crash (e.g. dereferencing null pointer, division by 0, ...)
2780     virtual void OnFatalException();
2781 };
2782
2783 IMPLEMENT_APP(CMyApp)
2784
2785 bool CMyApp::Initialize(int& argc, wxChar** argv)
2786 {
2787     for (int i = 1; i < argc; i++)
2788         if (!IsSwitchChar(argv[i][0]))
2789             fCommandLine = true;
2790
2791     if (!fCommandLine)
2792     {
2793         // wxApp::Initialize will remove environment-specific parameters,
2794         // so it's too early to call ParseParameters yet
2795         for (int i = 1; i < argc; i++)
2796         {
2797             wxString str = argv[i];
2798             #ifdef __WXMSW__
2799             if (str.size() >= 1 && str[0] == '/')
2800                 str[0] = '-';
2801             char pszLower[MAX_PATH];
2802             strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2803             strlwr(pszLower);
2804             str = pszLower;
2805             #endif
2806             if (str == "-daemon")
2807                 fDaemon = true;
2808         }
2809     }
2810
2811 #ifdef __WXGTK__
2812     if (fDaemon || fCommandLine)
2813     {
2814         // Call the original Initialize while suppressing error messages
2815         // and ignoring failure.  If unable to initialize GTK, it fails
2816         // near the end so hopefully the last few things don't matter.
2817         {
2818             wxLogNull logNo;
2819             wxApp::Initialize(argc, argv);
2820         }
2821
2822         if (fDaemon)
2823         {
2824             // Daemonize
2825             pid_t pid = fork();
2826             if (pid < 0)
2827             {
2828                 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2829                 return false;
2830             }
2831             if (pid > 0)
2832                 pthread_exit((void*)0);
2833
2834             pid_t sid = setsid();
2835             if (sid < 0)
2836                 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
2837         }
2838
2839         return true;
2840     }
2841 #endif
2842
2843     return wxApp::Initialize(argc, argv);
2844 }
2845
2846 bool CMyApp::OnInit()
2847 {
2848 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2849     // Disable malfunctioning wxWidgets debug assertion
2850     extern int g_isPainting;
2851     g_isPainting = 10000;
2852 #endif
2853 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2854     SetAppName("Bitcoin");
2855 #else
2856     SetAppName("bitcoin");
2857 #endif
2858 #ifdef __WXMSW__
2859 #if wxUSE_UNICODE
2860     // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2861     // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2862     class wxMBConv_win32 : public wxMBConv
2863     {
2864     public:
2865         long m_CodePage;
2866         size_t m_minMBCharWidth;
2867     };
2868     if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2869         ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2870 #endif
2871 #endif
2872
2873     // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2874     g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2875     g_locale.AddCatalogLookupPathPrefix("locale");
2876 #ifndef __WXMSW__
2877     g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2878     g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2879 #endif
2880     g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2881     g_locale.AddCatalog("bitcoin");
2882
2883     return AppInit(argc, argv);
2884 }
2885
2886 int CMyApp::OnExit()
2887 {
2888     Shutdown(NULL);
2889     return wxApp::OnExit();
2890 }
2891
2892 bool CMyApp::OnExceptionInMainLoop()
2893 {
2894     try
2895     {
2896         throw;
2897     }
2898     catch (std::exception& e)
2899     {
2900         PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2901         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2902         Sleep(1000);
2903         throw;
2904     }
2905     catch (...)
2906     {
2907         PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2908         wxLogWarning("Unknown exception");
2909         Sleep(1000);
2910         throw;
2911     }
2912     return true;
2913 }
2914
2915 void CMyApp::OnUnhandledException()
2916 {
2917     // this shows how we may let some exception propagate uncaught
2918     try
2919     {
2920         throw;
2921     }
2922     catch (std::exception& e)
2923     {
2924         PrintException(&e, "CMyApp::OnUnhandledException()");
2925         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2926         Sleep(1000);
2927         throw;
2928     }
2929     catch (...)
2930     {
2931         PrintException(NULL, "CMyApp::OnUnhandledException()");
2932         wxLogWarning("Unknown exception");
2933         Sleep(1000);
2934         throw;
2935     }
2936 }
2937
2938 void CMyApp::OnFatalException()
2939 {
2940     wxMessageBox(_("Program has crashed and will terminate.  "), "Bitcoin", wxOK | wxICON_ERROR);
2941 }