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