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