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