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