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