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