Handle high DPI a bit more gracefully on Win32. #243
[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     else
1867         SetIcon(wxICON(bitcoin));
1868
1869     // Fixup the tab order
1870     m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1871     m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1872     this->Layout();
1873 }
1874
1875 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1876 {
1877     // Reformat the amount
1878     event.Skip();
1879     if (m_textCtrlAmount->GetValue().Trim().empty())
1880         return;
1881     int64 nTmp;
1882     if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1883         m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1884 }
1885
1886 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1887 {
1888     // Open address book
1889     CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1890     if (dialog.ShowModal())
1891         m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1892 }
1893
1894 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1895 {
1896     // Copy clipboard to address box
1897     if (wxTheClipboard->Open())
1898     {
1899         if (wxTheClipboard->IsSupported(wxDF_TEXT))
1900         {
1901             wxTextDataObject data;
1902             wxTheClipboard->GetData(data);
1903             m_textCtrlAddress->SetValue(data.GetText());
1904         }
1905         wxTheClipboard->Close();
1906     }
1907 }
1908
1909 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1910 {
1911     static CCriticalSection cs_sendlock;
1912     TRY_CRITICAL_BLOCK(cs_sendlock)
1913     {
1914         CWalletTx wtx;
1915         string strAddress = (string)m_textCtrlAddress->GetValue();
1916
1917         // Parse amount
1918         int64 nValue = 0;
1919         if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1920         {
1921             wxMessageBox(_("Error in amount  "), _("Send Coins"));
1922             return;
1923         }
1924         if (nValue > GetBalance())
1925         {
1926             wxMessageBox(_("Amount exceeds your balance  "), _("Send Coins"));
1927             return;
1928         }
1929         if (nValue + nTransactionFee > GetBalance())
1930         {
1931             wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included  "), _("Send Coins"));
1932             return;
1933         }
1934
1935         // Parse bitcoin address
1936         uint160 hash160;
1937         bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1938
1939         if (fBitcoinAddress)
1940         {
1941             CRITICAL_BLOCK(cs_main)
1942             {
1943                 // Send to bitcoin address
1944                 CScript scriptPubKey;
1945                 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1946
1947                 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1948                 if (strError == "")
1949                     wxMessageBox(_("Payment sent  "), _("Sending..."));
1950                 else if (strError == "ABORTED")
1951                     return; // leave send dialog open
1952                 else
1953                 {
1954                     wxMessageBox(strError + "  ", _("Sending..."));
1955                     EndModal(false);
1956                     return;
1957                 }
1958             }
1959         }
1960         else
1961         {
1962             // Parse IP address
1963             CAddress addr(strAddress);
1964             if (!addr.IsValid())
1965             {
1966                 wxMessageBox(_("Invalid address  "), _("Send Coins"));
1967                 return;
1968             }
1969
1970             // Message
1971             wtx.mapValue["to"] = strAddress;
1972
1973             // Send to IP address
1974             CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1975             if (!pdialog->ShowModal())
1976                 return;
1977         }
1978
1979         CRITICAL_BLOCK(cs_mapAddressBook)
1980             if (!mapAddressBook.count(strAddress))
1981                 SetAddressBookName(strAddress, "");
1982
1983         EndModal(true);
1984     }
1985 }
1986
1987 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1988 {
1989     // Cancel
1990     EndModal(false);
1991 }
1992
1993
1994
1995
1996
1997
1998 //////////////////////////////////////////////////////////////////////////////
1999 //
2000 // CSendingDialog
2001 //
2002
2003 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
2004 {
2005     addr = addrIn;
2006     nPrice = nPriceIn;
2007     wtx = wtxIn;
2008     start = wxDateTime::UNow();
2009     memset(pszStatus, 0, sizeof(pszStatus));
2010     fCanCancel = true;
2011     fAbort = false;
2012     fSuccess = false;
2013     fUIDone = false;
2014     fWorkDone = false;
2015 #ifndef __WXMSW__
2016     SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2017 #else
2018     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2019 #endif
2020
2021     SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2022     m_textCtrlStatus->SetValue("");
2023
2024     CreateThread(SendingDialogStartTransfer, this);
2025 }
2026
2027 CSendingDialog::~CSendingDialog()
2028 {
2029     printf("~CSendingDialog()\n");
2030 }
2031
2032 void CSendingDialog::Close()
2033 {
2034     // Last one out turn out the lights.
2035     // fWorkDone signals that work side is done and UI thread should call destroy.
2036     // fUIDone signals that UI window has closed and work thread should call destroy.
2037     // This allows the window to disappear and end modality when cancelled
2038     // without making the user wait for ConnectNode to return.  The dialog object
2039     // hangs around in the background until the work thread exits.
2040     if (IsModal())
2041         EndModal(fSuccess);
2042     else
2043         Show(false);
2044     if (fWorkDone)
2045         Destroy();
2046     else
2047         fUIDone = true;
2048 }
2049
2050 void CSendingDialog::OnClose(wxCloseEvent& event)
2051 {
2052     if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2053     {
2054         Close();
2055     }
2056     else
2057     {
2058         event.Veto();
2059         wxCommandEvent cmdevent;
2060         OnButtonCancel(cmdevent);
2061     }
2062 }
2063
2064 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2065 {
2066     if (fWorkDone)
2067         Close();
2068 }
2069
2070 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2071 {
2072     if (fCanCancel)
2073         fAbort = true;
2074 }
2075
2076 void CSendingDialog::OnPaint(wxPaintEvent& event)
2077 {
2078     event.Skip();
2079     if (strlen(pszStatus) > 130)
2080         m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2081     else
2082         m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2083     m_staticTextSending->SetFocus();
2084     if (!fCanCancel)
2085         m_buttonCancel->Enable(false);
2086     if (fWorkDone)
2087     {
2088         m_buttonOK->Enable(true);
2089         m_buttonOK->SetFocus();
2090         m_buttonCancel->Enable(false);
2091     }
2092     if (fAbort && fCanCancel && IsShown())
2093     {
2094         strcpy(pszStatus, _("CANCELLED"));
2095         m_buttonOK->Enable(true);
2096         m_buttonOK->SetFocus();
2097         m_buttonCancel->Enable(false);
2098         m_buttonCancel->SetLabel(_("Cancelled"));
2099         Close();
2100         wxMessageBox(_("Transfer cancelled  "), _("Sending..."), wxOK, this);
2101     }
2102 }
2103
2104
2105 //
2106 // Everything from here on is not in the UI thread and must only communicate
2107 // with the rest of the dialog through variables and calling repaint.
2108 //
2109
2110 void CSendingDialog::Repaint()
2111 {
2112     Refresh();
2113     wxPaintEvent event;
2114     GetEventHandler()->AddPendingEvent(event);
2115 }
2116
2117 bool CSendingDialog::Status()
2118 {
2119     if (fUIDone)
2120     {
2121         Destroy();
2122         return false;
2123     }
2124     if (fAbort && fCanCancel)
2125     {
2126         memset(pszStatus, 0, 10);
2127         strcpy(pszStatus, _("CANCELLED"));
2128         Repaint();
2129         fWorkDone = true;
2130         return false;
2131     }
2132     return true;
2133 }
2134
2135 bool CSendingDialog::Status(const string& str)
2136 {
2137     if (!Status())
2138         return false;
2139
2140     // This can be read by the UI thread at any time,
2141     // so copy in a way that can be read cleanly at all times.
2142     memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2143     strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2144
2145     Repaint();
2146     return true;
2147 }
2148
2149 bool CSendingDialog::Error(const string& str)
2150 {
2151     fCanCancel = false;
2152     fWorkDone = true;
2153     Status(string(_("Error: ")) + str);
2154     return false;
2155 }
2156
2157 void SendingDialogStartTransfer(void* parg)
2158 {
2159     ((CSendingDialog*)parg)->StartTransfer();
2160 }
2161
2162 void CSendingDialog::StartTransfer()
2163 {
2164     // Make sure we have enough money
2165     if (nPrice + nTransactionFee > GetBalance())
2166     {
2167         Error(_("Insufficient funds"));
2168         return;
2169     }
2170
2171     // We may have connected already for product details
2172     if (!Status(_("Connecting...")))
2173         return;
2174     CNode* pnode = ConnectNode(addr, 15 * 60);
2175     if (!pnode)
2176     {
2177         Error(_("Unable to connect"));
2178         return;
2179     }
2180
2181     // Send order to seller, with response going to OnReply2 via event handler
2182     if (!Status(_("Requesting public key...")))
2183         return;
2184     pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2185 }
2186
2187 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2188 {
2189     ((CSendingDialog*)parg)->OnReply2(vRecv);
2190 }
2191
2192 void CSendingDialog::OnReply2(CDataStream& vRecv)
2193 {
2194     if (!Status(_("Received public key...")))
2195         return;
2196
2197     CScript scriptPubKey;
2198     int nRet;
2199     try
2200     {
2201         vRecv >> nRet;
2202         if (nRet > 0)
2203         {
2204             string strMessage;
2205             if (!vRecv.empty())
2206                 vRecv >> strMessage;
2207             if (nRet == 2)
2208                 Error(_("Recipient is not accepting transactions sent by IP address"));
2209             else
2210                 Error(_("Transfer was not accepted"));
2211             //// todo: enlarge the window and enable a hidden white box to put seller's message
2212             return;
2213         }
2214         vRecv >> scriptPubKey;
2215     }
2216     catch (...)
2217     {
2218         //// what do we want to do about this?
2219         Error(_("Invalid response received"));
2220         return;
2221     }
2222
2223     // Pause to give the user a chance to cancel
2224     while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2225     {
2226         Sleep(200);
2227         if (!Status())
2228             return;
2229     }
2230
2231     CRITICAL_BLOCK(cs_main)
2232     {
2233         // Pay
2234         if (!Status(_("Creating transaction...")))
2235             return;
2236         if (nPrice + nTransactionFee > GetBalance())
2237         {
2238             Error(_("Insufficient funds"));
2239             return;
2240         }
2241         CReserveKey reservekey;
2242         int64 nFeeRequired;
2243         if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2244         {
2245             if (nPrice + nFeeRequired > GetBalance())
2246                 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()));
2247             else
2248                 Error(_("Transaction creation failed"));
2249             return;
2250         }
2251
2252         // Transaction fee
2253         if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2254         {
2255             Error(_("Transaction aborted"));
2256             return;
2257         }
2258
2259         // Make sure we're still connected
2260         CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2261         if (!pnode)
2262         {
2263             Error(_("Lost connection, transaction cancelled"));
2264             return;
2265         }
2266
2267         // Last chance to cancel
2268         Sleep(50);
2269         if (!Status())
2270             return;
2271         fCanCancel = false;
2272         if (fAbort)
2273         {
2274             fCanCancel = true;
2275             if (!Status())
2276                 return;
2277             fCanCancel = false;
2278         }
2279         if (!Status(_("Sending payment...")))
2280             return;
2281
2282         // Commit
2283         if (!CommitTransaction(wtx, reservekey))
2284         {
2285             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."));
2286             return;
2287         }
2288
2289         // Send payment tx to seller, with response going to OnReply3 via event handler
2290         CWalletTx wtxSend = wtx;
2291         wtxSend.fFromMe = false;
2292         pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2293
2294         Status(_("Waiting for confirmation..."));
2295         MainFrameRepaint();
2296     }
2297 }
2298
2299 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2300 {
2301     ((CSendingDialog*)parg)->OnReply3(vRecv);
2302 }
2303
2304 void CSendingDialog::OnReply3(CDataStream& vRecv)
2305 {
2306     int nRet;
2307     try
2308     {
2309         vRecv >> nRet;
2310         if (nRet > 0)
2311         {
2312             Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2313                     "The transaction is recorded and will credit to the recipient,\n"
2314                     "but the comment information will be blank."));
2315             return;
2316         }
2317     }
2318     catch (...)
2319     {
2320         //// what do we want to do about this?
2321         Error(_("Payment was sent, but an invalid response was received"));
2322         return;
2323     }
2324
2325     fSuccess = true;
2326     fWorkDone = true;
2327     Status(_("Payment completed"));
2328 }
2329
2330
2331
2332
2333
2334
2335 //////////////////////////////////////////////////////////////////////////////
2336 //
2337 // CAddressBookDialog
2338 //
2339
2340 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2341 {
2342 #ifdef __WXMSW__
2343     SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2344 #endif
2345
2346     // Set initially selected page
2347     wxNotebookEvent event;
2348     event.SetSelection(nPageIn);
2349     OnNotebookPageChanged(event);
2350     m_notebook->ChangeSelection(nPageIn);
2351
2352     fDuringSend = fDuringSendIn;
2353     if (!fDuringSend)
2354         m_buttonCancel->Show(false);
2355
2356     // Set Icon
2357     if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2358     {
2359         wxIcon iconAddressBook;
2360         iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2361         SetIcon(iconAddressBook);
2362     }
2363     else
2364         SetIcon(wxICON(bitcoin));
2365
2366     // Init column headers
2367     m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2368     m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2369     m_listCtrlSending->SetFocus();
2370     m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2371     m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2372     m_listCtrlReceiving->SetFocus();
2373
2374     // Fill listctrl with address book data
2375     CRITICAL_BLOCK(cs_mapKeys)
2376     CRITICAL_BLOCK(cs_mapAddressBook)
2377     {
2378         string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2379         BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook)
2380         {
2381             string strAddress = item.first;
2382             string strName = item.second;
2383             uint160 hash160;
2384             bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2385             wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2386             int nIndex = InsertLine(plistCtrl, strName, strAddress);
2387             if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2388                 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2389         }
2390     }
2391 }
2392
2393 wxString CAddressBookDialog::GetSelectedAddress()
2394 {
2395     int nIndex = GetSelection(m_listCtrl);
2396     if (nIndex == -1)
2397         return "";
2398     return GetItemText(m_listCtrl, nIndex, 1);
2399 }
2400
2401 wxString CAddressBookDialog::GetSelectedSendingAddress()
2402 {
2403     int nIndex = GetSelection(m_listCtrlSending);
2404     if (nIndex == -1)
2405         return "";
2406     return GetItemText(m_listCtrlSending, nIndex, 1);
2407 }
2408
2409 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2410 {
2411     int nIndex = GetSelection(m_listCtrlReceiving);
2412     if (nIndex == -1)
2413         return "";
2414     return GetItemText(m_listCtrlReceiving, nIndex, 1);
2415 }
2416
2417 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2418 {
2419     event.Skip();
2420     nPage = event.GetSelection();
2421     if (nPage == SENDING)
2422         m_listCtrl = m_listCtrlSending;
2423     else if (nPage == RECEIVING)
2424         m_listCtrl = m_listCtrlReceiving;
2425     m_buttonDelete->Show(nPage == SENDING);
2426     m_buttonCopy->Show(nPage == RECEIVING);
2427     this->Layout();
2428     m_listCtrl->SetFocus();
2429 }
2430
2431 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2432 {
2433     // Update address book with edited name
2434     event.Skip();
2435     if (event.IsEditCancelled())
2436         return;
2437     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2438     SetAddressBookName(strAddress, string(event.GetText()));
2439     pframeMain->RefreshListCtrl();
2440 }
2441
2442 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2443 {
2444     event.Skip();
2445     if (nPage == RECEIVING)
2446         SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2447 }
2448
2449 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2450 {
2451     event.Skip();
2452     if (fDuringSend)
2453     {
2454         // Doubleclick returns selection
2455         EndModal(GetSelectedAddress() != "" ? 2 : 0);
2456         return;
2457     }
2458
2459     // Doubleclick edits item
2460     wxCommandEvent event2;
2461     OnButtonEdit(event2);
2462 }
2463
2464 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2465 {
2466     if (nPage != SENDING)
2467         return;
2468     for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2469     {
2470         if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2471         {
2472             string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2473             CWalletDB().EraseName(strAddress);
2474             m_listCtrl->DeleteItem(nIndex);
2475         }
2476     }
2477     pframeMain->RefreshListCtrl();
2478 }
2479
2480 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2481 {
2482     // Copy address box to clipboard
2483     if (wxTheClipboard->Open())
2484     {
2485         wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2486         wxTheClipboard->Close();
2487     }
2488 }
2489
2490 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2491 {
2492     uint160 hash160;
2493     bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2494     if (fMine)
2495         wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book.  "), strTitle);
2496     return fMine;
2497 }
2498
2499 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2500 {
2501     int nIndex = GetSelection(m_listCtrl);
2502     if (nIndex == -1)
2503         return;
2504     string strName = (string)m_listCtrl->GetItemText(nIndex);
2505     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2506     string strAddressOrg = strAddress;
2507
2508     if (nPage == SENDING)
2509     {
2510         // Ask name and address
2511         do
2512         {
2513             CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2514             if (!dialog.ShowModal())
2515                 return;
2516             strName = dialog.GetValue1();
2517             strAddress = dialog.GetValue2();
2518         }
2519         while (CheckIfMine(strAddress, _("Edit Address")));
2520
2521     }
2522     else if (nPage == RECEIVING)
2523     {
2524         // Ask name
2525         CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2526         if (!dialog.ShowModal())
2527             return;
2528         strName = dialog.GetValue();
2529     }
2530
2531     // Write back
2532     if (strAddress != strAddressOrg)
2533         CWalletDB().EraseName(strAddressOrg);
2534     SetAddressBookName(strAddress, strName);
2535     m_listCtrl->SetItem(nIndex, 1, strAddress);
2536     m_listCtrl->SetItemText(nIndex, strName);
2537     pframeMain->RefreshListCtrl();
2538 }
2539
2540 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2541 {
2542     string strName;
2543     string strAddress;
2544
2545     if (nPage == SENDING)
2546     {
2547         // Ask name and address
2548         do
2549         {
2550             CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2551             if (!dialog.ShowModal())
2552                 return;
2553             strName = dialog.GetValue1();
2554             strAddress = dialog.GetValue2();
2555         }
2556         while (CheckIfMine(strAddress, _("Add Address")));
2557     }
2558     else if (nPage == RECEIVING)
2559     {
2560         // Ask name
2561         CGetTextFromUserDialog dialog(this,
2562             _("New Receiving Address"),
2563             _("You should use a new address for each payment you receive.\n\nLabel"),
2564             "");
2565         if (!dialog.ShowModal())
2566             return;
2567         strName = dialog.GetValue();
2568
2569         // Generate new key
2570         strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2571     }
2572
2573     // Add to list and select it
2574     SetAddressBookName(strAddress, strName);
2575     int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2576     SetSelection(m_listCtrl, nIndex);
2577     m_listCtrl->SetFocus();
2578     if (nPage == SENDING)
2579         pframeMain->RefreshListCtrl();
2580 }
2581
2582 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2583 {
2584     // OK
2585     EndModal(GetSelectedAddress() != "" ? 1 : 0);
2586 }
2587
2588 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2589 {
2590     // Cancel
2591     EndModal(0);
2592 }
2593
2594 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2595 {
2596     // Close
2597     EndModal(0);
2598 }
2599
2600
2601
2602
2603
2604
2605 //////////////////////////////////////////////////////////////////////////////
2606 //
2607 // CMyTaskBarIcon
2608 //
2609
2610 enum
2611 {
2612     ID_TASKBAR_RESTORE = 10001,
2613     ID_TASKBAR_SEND,
2614     ID_TASKBAR_OPTIONS,
2615     ID_TASKBAR_GENERATE,
2616     ID_TASKBAR_EXIT,
2617 };
2618
2619 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2620     EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2621     EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2622     EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2623     EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2624     EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2625     EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2626 END_EVENT_TABLE()
2627
2628 void CMyTaskBarIcon::Show(bool fShow)
2629 {
2630     static char pszPrevTip[200];
2631     if (fShow)
2632     {
2633         string strTooltip = _("Bitcoin");
2634         if (fGenerateBitcoins)
2635             strTooltip = _("Bitcoin - Generating");
2636         if (fGenerateBitcoins && vNodes.empty())
2637             strTooltip = _("Bitcoin - (not connected)");
2638
2639         // Optimization, only update when changed, using char array to be reentrant
2640         if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2641         {
2642             strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2643 #ifdef __WXMSW__
2644             // somehow it'll choose the wrong size and scale it down if
2645             // we use the main icon, so we hand it one with only 16x16
2646             SetIcon(wxICON(favicon), strTooltip);
2647 #else
2648             SetIcon(bitcoin80_xpm, strTooltip);
2649 #endif
2650         }
2651     }
2652     else
2653     {
2654         strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2655         RemoveIcon();
2656     }
2657 }
2658
2659 void CMyTaskBarIcon::Hide()
2660 {
2661     Show(false);
2662 }
2663
2664 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2665 {
2666     Restore();
2667 }
2668
2669 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2670 {
2671     Restore();
2672 }
2673
2674 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2675 {
2676     // Taskbar: Send
2677     CSendDialog dialog(pframeMain);
2678     dialog.ShowModal();
2679 }
2680
2681 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2682 {
2683     // Since it's modal, get the main window to do it
2684     wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2685     pframeMain->GetEventHandler()->AddPendingEvent(event2);
2686 }
2687
2688 void CMyTaskBarIcon::Restore()
2689 {
2690     pframeMain->Show();
2691     wxIconizeEvent event(0, false);
2692     pframeMain->GetEventHandler()->AddPendingEvent(event);
2693     pframeMain->Iconize(false);
2694     pframeMain->Raise();
2695 }
2696
2697 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2698 {
2699     event.Check(fGenerateBitcoins);
2700 }
2701
2702 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2703 {
2704     pframeMain->Close(true);
2705 }
2706
2707 void CMyTaskBarIcon::UpdateTooltip()
2708 {
2709     if (IsIconInstalled())
2710         Show(true);
2711 }
2712
2713 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2714 {
2715     wxMenu* pmenu = new wxMenu;
2716     pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2717     pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2718     pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2719 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2720     pmenu->AppendSeparator();
2721     pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2722 #endif
2723     return pmenu;
2724 }
2725
2726
2727
2728
2729
2730
2731 //////////////////////////////////////////////////////////////////////////////
2732 //
2733 // CMyApp
2734 //
2735
2736 void CreateMainWindow()
2737 {
2738     pframeMain = new CMainFrame(NULL);
2739     if (GetBoolArg("-min"))
2740         pframeMain->Iconize(true);
2741 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2742     if (!GetBoolArg("-minimizetotray"))
2743         fMinimizeToTray = false;
2744 #endif
2745     pframeMain->Show(true);  // have to show first to get taskbar button to hide
2746     if (fMinimizeToTray && pframeMain->IsIconized())
2747         fClosedToTray = true;
2748     pframeMain->Show(!fClosedToTray);
2749     ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2750     CreateThread(ThreadDelayedRepaint, NULL);
2751 }
2752
2753
2754 // Define a new application
2755 class CMyApp : public wxApp
2756 {
2757 public:
2758     CMyApp(){};
2759     ~CMyApp(){};
2760     bool OnInit();
2761     bool OnInit2();
2762     int OnExit();
2763
2764     // Hook Initialize so we can start without GUI
2765     virtual bool Initialize(int& argc, wxChar** argv);
2766
2767     // 2nd-level exception handling: we get all the exceptions occurring in any
2768     // event handler here
2769     virtual bool OnExceptionInMainLoop();
2770
2771     // 3rd, and final, level exception handling: whenever an unhandled
2772     // exception is caught, this function is called
2773     virtual void OnUnhandledException();
2774
2775     // and now for something different: this function is called in case of a
2776     // crash (e.g. dereferencing null pointer, division by 0, ...)
2777     virtual void OnFatalException();
2778 };
2779
2780 IMPLEMENT_APP(CMyApp)
2781
2782 bool CMyApp::Initialize(int& argc, wxChar** argv)
2783 {
2784     for (int i = 1; i < argc; i++)
2785         if (!IsSwitchChar(argv[i][0]))
2786             fCommandLine = true;
2787
2788     if (!fCommandLine)
2789     {
2790         // wxApp::Initialize will remove environment-specific parameters,
2791         // so it's too early to call ParseParameters yet
2792         for (int i = 1; i < argc; i++)
2793         {
2794             wxString str = argv[i];
2795             #ifdef __WXMSW__
2796             if (str.size() >= 1 && str[0] == '/')
2797                 str[0] = '-';
2798             char pszLower[MAX_PATH];
2799             strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2800             strlwr(pszLower);
2801             str = pszLower;
2802             #endif
2803             if (str == "-daemon")
2804                 fDaemon = true;
2805         }
2806     }
2807
2808 #ifdef __WXGTK__
2809     if (fDaemon || fCommandLine)
2810     {
2811         // Call the original Initialize while suppressing error messages
2812         // and ignoring failure.  If unable to initialize GTK, it fails
2813         // near the end so hopefully the last few things don't matter.
2814         {
2815             wxLogNull logNo;
2816             wxApp::Initialize(argc, argv);
2817         }
2818
2819         if (fDaemon)
2820         {
2821             // Daemonize
2822             pid_t pid = fork();
2823             if (pid < 0)
2824             {
2825                 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2826                 return false;
2827             }
2828             if (pid > 0)
2829                 pthread_exit((void*)0);
2830
2831             pid_t sid = setsid();
2832             if (sid < 0)
2833                 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
2834         }
2835
2836         return true;
2837     }
2838 #endif
2839
2840     return wxApp::Initialize(argc, argv);
2841 }
2842
2843 bool CMyApp::OnInit()
2844 {
2845 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2846     // Disable malfunctioning wxWidgets debug assertion
2847     extern int g_isPainting;
2848     g_isPainting = 10000;
2849 #endif
2850 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2851     SetAppName("Bitcoin");
2852 #else
2853     SetAppName("bitcoin");
2854 #endif
2855 #ifdef __WXMSW__
2856 #if wxUSE_UNICODE
2857     // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2858     // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2859     class wxMBConv_win32 : public wxMBConv
2860     {
2861     public:
2862         long m_CodePage;
2863         size_t m_minMBCharWidth;
2864     };
2865     if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2866         ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2867 #endif
2868 #endif
2869
2870     // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2871     g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2872     g_locale.AddCatalogLookupPathPrefix("locale");
2873 #ifndef __WXMSW__
2874     g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2875     g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2876 #endif
2877     g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2878     g_locale.AddCatalog("bitcoin");
2879
2880 #ifdef __WXMSW__
2881     HDC hdc = GetDC(NULL);
2882     if (hdc)
2883     {
2884         nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
2885         nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
2886         ReleaseDC(NULL, hdc);
2887     }
2888 #endif
2889
2890     return AppInit(argc, argv);
2891 }
2892
2893 int CMyApp::OnExit()
2894 {
2895     Shutdown(NULL);
2896     return wxApp::OnExit();
2897 }
2898
2899 bool CMyApp::OnExceptionInMainLoop()
2900 {
2901     try
2902     {
2903         throw;
2904     }
2905     catch (std::exception& e)
2906     {
2907         PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2908         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2909         Sleep(1000);
2910         throw;
2911     }
2912     catch (...)
2913     {
2914         PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2915         wxLogWarning("Unknown exception");
2916         Sleep(1000);
2917         throw;
2918     }
2919     return true;
2920 }
2921
2922 void CMyApp::OnUnhandledException()
2923 {
2924     // this shows how we may let some exception propagate uncaught
2925     try
2926     {
2927         throw;
2928     }
2929     catch (std::exception& e)
2930     {
2931         PrintException(&e, "CMyApp::OnUnhandledException()");
2932         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2933         Sleep(1000);
2934         throw;
2935     }
2936     catch (...)
2937     {
2938         PrintException(NULL, "CMyApp::OnUnhandledException()");
2939         wxLogWarning("Unknown exception");
2940         Sleep(1000);
2941         throw;
2942     }
2943 }
2944
2945 void CMyApp::OnFatalException()
2946 {
2947     wxMessageBox(_("Program has crashed and will terminate.  "), "Bitcoin", wxOK | wxICON_ERROR);
2948 }