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