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