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