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