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