strip out unfinished product, review and market stuff,
[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 bool GetStartOnSystemStartup();\r
11 void SetStartOnSystemStartup(bool fAutoStart);\r
12 \r
13 \r
14 \r
15 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)\r
16 \r
17 CMainFrame* pframeMain = NULL;\r
18 CMyTaskBarIcon* ptaskbaricon = NULL;\r
19 extern int g_isPainting;\r
20 bool fClosedToTray = false;\r
21 \r
22 // Settings\r
23 int fShowGenerated = true;\r
24 int fMinimizeToTray = true;\r
25 int fMinimizeOnClose = true;\r
26 \r
27 \r
28 \r
29 \r
30 \r
31 \r
32 \r
33 //////////////////////////////////////////////////////////////////////////////\r
34 //\r
35 // Util\r
36 //\r
37 \r
38 void HandleCtrlA(wxKeyEvent& event)\r
39 {\r
40     // Ctrl-a select all\r
41     wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();\r
42     if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')\r
43         textCtrl->SetSelection(-1, -1);\r
44     event.Skip();\r
45 }\r
46 \r
47 bool Is24HourTime()\r
48 {\r
49     //char pszHourFormat[256];\r
50     //pszHourFormat[0] = '\0';\r
51     //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);\r
52     //return (pszHourFormat[0] != '0');\r
53     return true;\r
54 }\r
55 \r
56 string DateStr(int64 nTime)\r
57 {\r
58     // Can only be used safely here in the UI\r
59     return (string)wxDateTime((time_t)nTime).FormatDate();\r
60 }\r
61 \r
62 string DateTimeStr(int64 nTime)\r
63 {\r
64     // Can only be used safely here in the UI\r
65     wxDateTime datetime((time_t)nTime);\r
66     if (Is24HourTime())\r
67         return (string)datetime.Format("%x %H:%M");\r
68     else\r
69         return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");\r
70 }\r
71 \r
72 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)\r
73 {\r
74     // Helper to simplify access to listctrl\r
75     wxListItem item;\r
76     item.m_itemId = nIndex;\r
77     item.m_col = nColumn;\r
78     item.m_mask = wxLIST_MASK_TEXT;\r
79     if (!listCtrl->GetItem(item))\r
80         return "";\r
81     return item.GetText();\r
82 }\r
83 \r
84 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)\r
85 {\r
86     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);\r
87     listCtrl->SetItem(nIndex, 1, str1);\r
88     return nIndex;\r
89 }\r
90 \r
91 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)\r
92 {\r
93     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);\r
94     listCtrl->SetItem(nIndex, 1, str1);\r
95     listCtrl->SetItem(nIndex, 2, str2);\r
96     listCtrl->SetItem(nIndex, 3, str3);\r
97     listCtrl->SetItem(nIndex, 4, str4);\r
98     return nIndex;\r
99 }\r
100 \r
101 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)\r
102 {\r
103     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);\r
104     listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);\r
105     listCtrl->SetItem(nIndex, 1, str1);\r
106     listCtrl->SetItem(nIndex, 2, str2);\r
107     listCtrl->SetItem(nIndex, 3, str3);\r
108     listCtrl->SetItem(nIndex, 4, str4);\r
109     return nIndex;\r
110 }\r
111 \r
112 void SetSelection(wxListCtrl* listCtrl, int nIndex)\r
113 {\r
114     int nSize = listCtrl->GetItemCount();\r
115     long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);\r
116     for (int i = 0; i < nSize; i++)\r
117         listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);\r
118 }\r
119 \r
120 int GetSelection(wxListCtrl* listCtrl)\r
121 {\r
122     int nSize = listCtrl->GetItemCount();\r
123     for (int i = 0; i < nSize; i++)\r
124         if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))\r
125             return i;\r
126     return -1;\r
127 }\r
128 \r
129 string HtmlEscape(const char* psz, bool fMultiLine=false)\r
130 {\r
131     int len = 0;\r
132     for (const char* p = psz; *p; p++)\r
133     {\r
134              if (*p == '<') len += 4;\r
135         else if (*p == '>') len += 4;\r
136         else if (*p == '&') len += 5;\r
137         else if (*p == '"') len += 6;\r
138         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;\r
139         else if (*p == '\n' && fMultiLine) len += 5;\r
140         else\r
141             len++;\r
142     }\r
143     string str;\r
144     str.reserve(len);\r
145     for (const char* p = psz; *p; p++)\r
146     {\r
147              if (*p == '<') str += "&lt;";\r
148         else if (*p == '>') str += "&gt;";\r
149         else if (*p == '&') str += "&amp;";\r
150         else if (*p == '"') str += "&quot;";\r
151         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += "&nbsp;";\r
152         else if (*p == '\n' && fMultiLine) str += "<br>\n";\r
153         else\r
154             str += *p;\r
155     }\r
156     return str;\r
157 }\r
158 \r
159 string HtmlEscape(const string& str, bool fMultiLine=false)\r
160 {\r
161     return HtmlEscape(str.c_str(), fMultiLine);\r
162 }\r
163 \r
164 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)\r
165 {\r
166     *pnRet = wxMessageBox(message, caption, style, parent, x, y);\r
167     *pfDone = true;\r
168 }\r
169 \r
170 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)\r
171 {\r
172     if (fDaemon)\r
173     {\r
174         printf("wxMessageBox %s: %s\n", caption.c_str(), message.c_str());\r
175         return wxOK;\r
176     }\r
177 \r
178 #ifdef __WXMSW__\r
179     return wxMessageBox(message, caption, style, parent, x, y);\r
180 #else\r
181     if (wxThread::IsMain())\r
182     {\r
183         return wxMessageBox(message, caption, style, parent, x, y);\r
184     }\r
185     else\r
186     {\r
187         int nRet = 0;\r
188         bool fDone = false;\r
189         UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));\r
190         while (!fDone)\r
191             Sleep(100);\r
192         return nRet;\r
193     }\r
194 #endif\r
195 }\r
196 \r
197 \r
198 \r
199 \r
200 \r
201 \r
202 \r
203 \r
204 \r
205 \r
206 //////////////////////////////////////////////////////////////////////////////\r
207 //\r
208 // CMainFrame\r
209 //\r
210 \r
211 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)\r
212 {\r
213     Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);\r
214 \r
215     // Init\r
216     fRefreshListCtrl = false;\r
217     fRefreshListCtrlRunning = false;\r
218     fOnSetFocusAddress = false;\r
219     fRefresh = false;\r
220     m_choiceFilter->SetSelection(0);\r
221     double dResize = 1.0;\r
222 #ifdef __WXMSW__\r
223     SetIcon(wxICON(bitcoin));\r
224 #else\r
225     SetIcon(bitcoin16_xpm);\r
226     wxFont fontTmp = m_staticText41->GetFont();\r
227     fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);\r
228     m_staticTextBalance->SetFont(fontTmp);\r
229     m_staticTextBalance->SetSize(140, 17);\r
230     // & underlines don't work on the toolbar buttons on gtk\r
231     m_toolBar->ClearTools();\r
232     m_toolBar->AddTool(wxID_BUTTONSEND, "Send Coins", wxBitmap(send20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);\r
233     m_toolBar->AddTool(wxID_BUTTONRECEIVE, "Address Book", wxBitmap(addressbook20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);\r
234     m_toolBar->Realize();\r
235     // resize to fit ubuntu's huge default font\r
236     dResize = 1.20;\r
237     SetSize((dResize + 0.02) * GetSize().GetWidth(), 1.09 * GetSize().GetHeight());\r
238 #endif\r
239     m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + "  ");\r
240     m_listCtrl->SetFocus();\r
241     ptaskbaricon = new CMyTaskBarIcon();\r
242 \r
243     // Init column headers\r
244     int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;\r
245     if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))\r
246         nDateWidth += 12;\r
247     m_listCtrl->InsertColumn(0, "",             wxLIST_FORMAT_LEFT,  dResize * 0);\r
248     m_listCtrl->InsertColumn(1, "",             wxLIST_FORMAT_LEFT,  dResize * 0);\r
249     m_listCtrl->InsertColumn(2, "Status",       wxLIST_FORMAT_LEFT,  dResize * 110);\r
250     m_listCtrl->InsertColumn(3, "Date",         wxLIST_FORMAT_LEFT,  dResize * nDateWidth);\r
251     m_listCtrl->InsertColumn(4, "Description",  wxLIST_FORMAT_LEFT,  dResize * 409 - nDateWidth);\r
252     m_listCtrl->InsertColumn(5, "Debit",        wxLIST_FORMAT_RIGHT, dResize * 79);\r
253     m_listCtrl->InsertColumn(6, "Credit",       wxLIST_FORMAT_RIGHT, dResize * 79);\r
254 \r
255     // Init status bar\r
256     int pnWidths[3] = { -100, 88, 290 };\r
257 #ifndef __WXMSW__\r
258     pnWidths[1] = pnWidths[1] * 1.1 * dResize;\r
259     pnWidths[2] = pnWidths[2] * 1.1 * dResize;\r
260 #endif\r
261     m_statusBar->SetFieldsCount(3, pnWidths);\r
262 \r
263     // Fill your address text box\r
264     vector<unsigned char> vchPubKey;\r
265     if (CWalletDB("r").ReadDefaultKey(vchPubKey))\r
266         m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));\r
267 \r
268     // Fill listctrl with wallet transactions\r
269     RefreshListCtrl();\r
270 }\r
271 \r
272 CMainFrame::~CMainFrame()\r
273 {\r
274     pframeMain = NULL;\r
275     delete ptaskbaricon;\r
276     ptaskbaricon = NULL;\r
277 }\r
278 \r
279 void ExitTimeout(void* parg)\r
280 {\r
281 #ifdef __WXMSW__\r
282     Sleep(5000);\r
283     ExitProcess(0);\r
284 #endif\r
285 }\r
286 \r
287 void Shutdown(void* parg)\r
288 {\r
289     static CCriticalSection cs_Shutdown;\r
290     static bool fTaken;\r
291     bool fFirstThread;\r
292     CRITICAL_BLOCK(cs_Shutdown)\r
293     {\r
294         fFirstThread = !fTaken;\r
295         fTaken = true;\r
296     }\r
297     static bool fExit;\r
298     if (fFirstThread)\r
299     {\r
300         fShutdown = true;\r
301         nTransactionsUpdated++;\r
302         DBFlush(false);\r
303         StopNode();\r
304         DBFlush(true);\r
305         CreateThread(ExitTimeout, NULL);\r
306         Sleep(50);\r
307         printf("Bitcoin exiting\n\n");\r
308         fExit = true;\r
309         exit(0);\r
310     }\r
311     else\r
312     {\r
313         while (!fExit)\r
314             Sleep(500);\r
315         Sleep(100);\r
316         ExitThread(0);\r
317     }\r
318 }\r
319 \r
320 void CMainFrame::OnClose(wxCloseEvent& event)\r
321 {\r
322     if (fMinimizeOnClose && event.CanVeto() && !IsIconized())\r
323     {\r
324         // Divert close to minimize\r
325         event.Veto();\r
326         fClosedToTray = true;\r
327         Iconize(true);\r
328     }\r
329     else\r
330     {\r
331         Destroy();\r
332         CreateThread(Shutdown, NULL);\r
333     }\r
334 }\r
335 \r
336 void CMainFrame::OnIconize(wxIconizeEvent& event)\r
337 {\r
338     // Hide the task bar button when minimized.\r
339     // Event is sent when the frame is minimized or restored.\r
340     // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way\r
341     // to get rid of the deprecated warning.  Just ignore it.\r
342     if (!event.Iconized())\r
343         fClosedToTray = false;\r
344 #ifndef __WXMSW__\r
345     // Tray is not reliable on Linux gnome\r
346     fClosedToTray = false;\r
347 #endif\r
348     if (fMinimizeToTray && event.Iconized())\r
349         fClosedToTray = true;\r
350     Show(!fClosedToTray);\r
351     ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);\r
352 }\r
353 \r
354 void CMainFrame::OnMouseEvents(wxMouseEvent& event)\r
355 {\r
356     RandAddSeed();\r
357     RAND_add(&event.m_x, sizeof(event.m_x), 0.25);\r
358     RAND_add(&event.m_y, sizeof(event.m_y), 0.25);\r
359 }\r
360 \r
361 void CMainFrame::OnListColBeginDrag(wxListEvent& event)\r
362 {\r
363      // Hidden columns not resizeable\r
364      if (event.GetColumn() <= 1 && !fDebug)\r
365         event.Veto();\r
366 }\r
367 \r
368 int CMainFrame::GetSortIndex(const string& strSort)\r
369 {\r
370 #ifdef __WXMSW__\r
371     return 0;\r
372 #else\r
373     // The wx generic listctrl implementation used on GTK doesn't sort,\r
374     // so we have to do it ourselves.  Remember, we sort in reverse order.\r
375     // In the wx generic implementation, they store the list of items\r
376     // in a vector, so indexed lookups are fast, but inserts are slower\r
377     // the closer they are to the top.\r
378     int low = 0;\r
379     int high = m_listCtrl->GetItemCount();\r
380     while (low < high)\r
381     {\r
382         int mid = low + ((high - low) / 2);\r
383         if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)\r
384             high = mid;\r
385         else\r
386             low = mid + 1;\r
387     }\r
388     return low;\r
389 #endif\r
390 }\r
391 \r
392 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
393 {\r
394     strSort = " " + strSort;       // leading space to workaround wx2.9.0 ubuntu 9.10 bug\r
395     long nData = *(long*)&hashKey; //  where first char of hidden column is displayed\r
396 \r
397     // Find item\r
398     if (!fNew && nIndex == -1)\r
399     {\r
400         string strHash = " " + hashKey.ToString();\r
401         while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)\r
402             if (GetItemText(m_listCtrl, nIndex, 1) == strHash)\r
403                 break;\r
404     }\r
405 \r
406     // fNew is for blind insert, only use if you're sure it's new\r
407     if (fNew || nIndex == -1)\r
408     {\r
409         nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);\r
410     }\r
411     else\r
412     {\r
413         // If sort key changed, must delete and reinsert to make it relocate\r
414         if (GetItemText(m_listCtrl, nIndex, 0) != strSort)\r
415         {\r
416             m_listCtrl->DeleteItem(nIndex);\r
417             nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);\r
418         }\r
419     }\r
420 \r
421     m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());\r
422     m_listCtrl->SetItem(nIndex, 2, str2);\r
423     m_listCtrl->SetItem(nIndex, 3, str3);\r
424     m_listCtrl->SetItem(nIndex, 4, str4);\r
425     m_listCtrl->SetItem(nIndex, 5, str5);\r
426     m_listCtrl->SetItem(nIndex, 6, str6);\r
427     m_listCtrl->SetItemData(nIndex, nData);\r
428 }\r
429 \r
430 bool CMainFrame::DeleteLine(uint256 hashKey)\r
431 {\r
432     long nData = *(long*)&hashKey;\r
433 \r
434     // Find item\r
435     int nIndex = -1;\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     if (nIndex != -1)\r
442         m_listCtrl->DeleteItem(nIndex);\r
443 \r
444     return nIndex != -1;\r
445 }\r
446 \r
447 string FormatTxStatus(const CWalletTx& wtx)\r
448 {\r
449     // Status\r
450     if (!wtx.IsFinal())\r
451     {\r
452         if (wtx.nLockTime < 500000000)\r
453             return strprintf("Open for %d blocks", nBestHeight - wtx.nLockTime);\r
454         else\r
455             return strprintf("Open until %s", DateTimeStr(wtx.nLockTime).c_str());\r
456     }\r
457     else\r
458     {\r
459         int nDepth = wtx.GetDepthInMainChain();\r
460         if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)\r
461             return strprintf("%d/offline?", nDepth);\r
462         else if (nDepth < 6)\r
463             return strprintf("%d/unconfirmed", nDepth);\r
464         else\r
465             return strprintf("%d confirmations", nDepth);\r
466     }\r
467 }\r
468 \r
469 string SingleLine(const string& strIn)\r
470 {\r
471     string strOut;\r
472     bool fOneSpace = false;\r
473     foreach(int c, strIn)\r
474     {\r
475         if (isspace(c))\r
476         {\r
477             fOneSpace = true;\r
478         }\r
479         else if (c > ' ')\r
480         {\r
481             if (fOneSpace && !strOut.empty())\r
482                 strOut += ' ';\r
483             strOut += c;\r
484             fOneSpace = false;\r
485         }\r
486     }\r
487     return strOut;\r
488 }\r
489 \r
490 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)\r
491 {\r
492     int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();\r
493     int64 nCredit = wtx.GetCredit();\r
494     int64 nDebit = wtx.GetDebit();\r
495     int64 nNet = nCredit - nDebit;\r
496     uint256 hash = wtx.GetHash();\r
497     string strStatus = FormatTxStatus(wtx);\r
498     map<string, string> mapValue = wtx.mapValue;\r
499     wtx.nLinesDisplayed = 1;\r
500     nListViewUpdated++;\r
501 \r
502     // Filter\r
503     if (wtx.IsCoinBase())\r
504     {\r
505         // Don't show generated coin until confirmed by at least one block after it\r
506         // so we don't get the user's hopes up until it looks like it's probably accepted.\r
507         //\r
508         // It is not an error when generated blocks are not accepted.  By design,\r
509         // some percentage of blocks, like 10% or more, will end up not accepted.\r
510         // This is the normal mechanism by which the network copes with latency.\r
511         //\r
512         // We display regular transactions right away before any confirmation\r
513         // because they can always get into some block eventually.  Generated coins\r
514         // are special because if their block is not accepted, they are not valid.\r
515         //\r
516         if (wtx.GetDepthInMainChain() < 2)\r
517         {\r
518             wtx.nLinesDisplayed = 0;\r
519             return false;\r
520         }\r
521 \r
522         // View->Show Generated\r
523         if (!fShowGenerated)\r
524             return false;\r
525     }\r
526 \r
527     // Find the block the tx is in\r
528     CBlockIndex* pindex = NULL;\r
529     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);\r
530     if (mi != mapBlockIndex.end())\r
531         pindex = (*mi).second;\r
532 \r
533     // Sort order, unrecorded transactions sort to the top\r
534     string strSort = strprintf("%010d-%01d-%010u",\r
535         (pindex ? pindex->nHeight : INT_MAX),\r
536         (wtx.IsCoinBase() ? 1 : 0),\r
537         wtx.nTimeReceived);\r
538 \r
539     // Insert line\r
540     if (nNet > 0 || wtx.IsCoinBase())\r
541     {\r
542         //\r
543         // Credit\r
544         //\r
545         string strDescription;\r
546 \r
547         if (wtx.IsCoinBase())\r
548         {\r
549             // Coinbase\r
550             strDescription = "Generated";\r
551             if (nCredit == 0)\r
552             {\r
553                 int64 nUnmatured = 0;\r
554                 foreach(const CTxOut& txout, wtx.vout)\r
555                     nUnmatured += txout.GetCredit();\r
556                 if (wtx.IsInMainChain())\r
557                 {\r
558                     strDescription = strprintf("Generated (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());\r
559 \r
560                     // Check if the block was requested by anyone\r
561                     if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)\r
562                         strDescription = "Generated - Warning: This block was not received by any other nodes and will probably not be accepted!";\r
563                 }\r
564                 else\r
565                 {\r
566                     strDescription = "Generated (not accepted)";\r
567                 }\r
568             }\r
569         }\r
570         else if (!mapValue["from"].empty() || !mapValue["message"].empty())\r
571         {\r
572             // Online transaction\r
573             if (!mapValue["from"].empty())\r
574                 strDescription += "From: " + mapValue["from"];\r
575             if (!mapValue["message"].empty())\r
576             {\r
577                 if (!strDescription.empty())\r
578                     strDescription += " - ";\r
579                 strDescription += mapValue["message"];\r
580             }\r
581         }\r
582         else\r
583         {\r
584             // Offline transaction\r
585             foreach(const CTxOut& txout, wtx.vout)\r
586             {\r
587                 if (txout.IsMine())\r
588                 {\r
589                     vector<unsigned char> vchPubKey;\r
590                     if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))\r
591                     {\r
592                         CRITICAL_BLOCK(cs_mapAddressBook)\r
593                         {\r
594                             string strAddress = PubKeyToAddress(vchPubKey);\r
595                             if (mapAddressBook.count(strAddress))\r
596                             {\r
597                                 //strDescription += "Received payment to ";\r
598                                 //strDescription += "Received with address ";\r
599                                 strDescription += "From: unknown, To: ";\r
600                                 strDescription += strAddress;\r
601                                 /// The labeling feature is just too confusing, so I hid it\r
602                                 /// by putting it at the end where it runs off the screen.\r
603                                 /// It can still be seen by widening the column, or in the\r
604                                 /// details dialog.\r
605                                 if (!mapAddressBook[strAddress].empty())\r
606                                     strDescription += " (" + mapAddressBook[strAddress] + ")";\r
607                             }\r
608                         }\r
609                     }\r
610                     break;\r
611                 }\r
612             }\r
613         }\r
614 \r
615         InsertLine(fNew, nIndex, hash, strSort,\r
616                    strStatus,\r
617                    nTime ? DateTimeStr(nTime) : "",\r
618                    SingleLine(strDescription),\r
619                    "",\r
620                    FormatMoney(nNet, true));\r
621     }\r
622     else\r
623     {\r
624         bool fAllFromMe = true;\r
625         foreach(const CTxIn& txin, wtx.vin)\r
626             fAllFromMe = fAllFromMe && txin.IsMine();\r
627 \r
628         bool fAllToMe = true;\r
629         foreach(const CTxOut& txout, wtx.vout)\r
630             fAllToMe = fAllToMe && txout.IsMine();\r
631 \r
632         if (fAllFromMe && fAllToMe)\r
633         {\r
634             // Payment to self\r
635             int64 nValue = wtx.vout[0].nValue;\r
636             InsertLine(fNew, nIndex, hash, strSort,\r
637                        strStatus,\r
638                        nTime ? DateTimeStr(nTime) : "",\r
639                        "Payment to yourself",\r
640                        "",\r
641                        "");\r
642             /// issue: can't tell which is the payment and which is the change anymore\r
643             //           FormatMoney(nNet - nValue, true),\r
644             //           FormatMoney(nValue, true));\r
645         }\r
646         else if (fAllFromMe)\r
647         {\r
648             //\r
649             // Debit\r
650             //\r
651             int64 nTxFee = nDebit - wtx.GetValueOut();\r
652             wtx.nLinesDisplayed = 0;\r
653             for (int nOut = 0; nOut < wtx.vout.size(); nOut++)\r
654             {\r
655                 const CTxOut& txout = wtx.vout[nOut];\r
656                 if (txout.IsMine())\r
657                     continue;\r
658 \r
659                 string strAddress;\r
660                 if (!mapValue["to"].empty())\r
661                 {\r
662                     // Online transaction\r
663                     strAddress = mapValue["to"];\r
664                 }\r
665                 else\r
666                 {\r
667                     // Offline transaction\r
668                     uint160 hash160;\r
669                     if (ExtractHash160(txout.scriptPubKey, hash160))\r
670                         strAddress = Hash160ToAddress(hash160);\r
671                 }\r
672 \r
673                 string strDescription = "To: ";\r
674                 CRITICAL_BLOCK(cs_mapAddressBook)\r
675                     if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())\r
676                         strDescription += mapAddressBook[strAddress] + " ";\r
677                 strDescription += strAddress;\r
678                 if (!mapValue["message"].empty())\r
679                 {\r
680                     if (!strDescription.empty())\r
681                         strDescription += " - ";\r
682                     strDescription += mapValue["message"];\r
683                 }\r
684 \r
685                 int64 nValue = txout.nValue;\r
686                 if (nOut == 0 && nTxFee > 0)\r
687                     nValue += nTxFee;\r
688 \r
689                 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),\r
690                            strStatus,\r
691                            nTime ? DateTimeStr(nTime) : "",\r
692                            SingleLine(strDescription),\r
693                            FormatMoney(-nValue, true),\r
694                            "");\r
695                 wtx.nLinesDisplayed++;\r
696             }\r
697         }\r
698         else\r
699         {\r
700             //\r
701             // Mixed debit transaction, can't break down payees\r
702             //\r
703             bool fAllMine = true;\r
704             foreach(const CTxOut& txout, wtx.vout)\r
705                 fAllMine = fAllMine && txout.IsMine();\r
706             foreach(const CTxIn& txin, wtx.vin)\r
707                 fAllMine = fAllMine && txin.IsMine();\r
708 \r
709             InsertLine(fNew, nIndex, hash, strSort,\r
710                        strStatus,\r
711                        nTime ? DateTimeStr(nTime) : "",\r
712                        "",\r
713                        FormatMoney(nNet, true),\r
714                        "");\r
715         }\r
716     }\r
717 \r
718     return true;\r
719 }\r
720 \r
721 void CMainFrame::RefreshListCtrl()\r
722 {\r
723     fRefreshListCtrl = true;\r
724     ::wxWakeUpIdle();\r
725 }\r
726 \r
727 void CMainFrame::OnIdle(wxIdleEvent& event)\r
728 {\r
729     if (fRefreshListCtrl)\r
730     {\r
731         // Collect list of wallet transactions and sort newest first\r
732         bool fEntered = false;\r
733         vector<pair<unsigned int, uint256> > vSorted;\r
734         TRY_CRITICAL_BLOCK(cs_mapWallet)\r
735         {\r
736             printf("RefreshListCtrl starting\n");\r
737             fEntered = true;\r
738             fRefreshListCtrl = false;\r
739             vWalletUpdated.clear();\r
740 \r
741             // Do the newest transactions first\r
742             vSorted.reserve(mapWallet.size());\r
743             for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\r
744             {\r
745                 const CWalletTx& wtx = (*it).second;\r
746                 unsigned int nTime = UINT_MAX - wtx.GetTxTime();\r
747                 vSorted.push_back(make_pair(nTime, (*it).first));\r
748             }\r
749             m_listCtrl->DeleteAllItems();\r
750         }\r
751         if (!fEntered)\r
752             return;\r
753 \r
754         sort(vSorted.begin(), vSorted.end());\r
755 \r
756         // Fill list control\r
757         for (int i = 0; i < vSorted.size();)\r
758         {\r
759             if (fShutdown)\r
760                 return;\r
761             bool fEntered = false;\r
762             TRY_CRITICAL_BLOCK(cs_mapWallet)\r
763             {\r
764                 fEntered = true;\r
765                 uint256& hash = vSorted[i++].second;\r
766                 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);\r
767                 if (mi != mapWallet.end())\r
768                     InsertTransaction((*mi).second, true);\r
769             }\r
770             if (!fEntered || i == 100 || i % 500 == 0)\r
771                 wxYield();\r
772         }\r
773 \r
774         printf("RefreshListCtrl done\n");\r
775 \r
776         // Update transaction total display\r
777         MainFrameRepaint();\r
778     }\r
779     else\r
780     {\r
781         // Check for time updates\r
782         static int64 nLastTime;\r
783         if (GetTime() > nLastTime + 30)\r
784         {\r
785             TRY_CRITICAL_BLOCK(cs_mapWallet)\r
786             {\r
787                 nLastTime = GetTime();\r
788                 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\r
789                 {\r
790                     CWalletTx& wtx = (*it).second;\r
791                     if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())\r
792                         InsertTransaction(wtx, false);\r
793                 }\r
794             }\r
795         }\r
796     }\r
797 }\r
798 \r
799 void CMainFrame::RefreshStatusColumn()\r
800 {\r
801     static int nLastTop;\r
802     static CBlockIndex* pindexLastBest;\r
803     static unsigned int nLastRefreshed;\r
804 \r
805     int nTop = max((int)m_listCtrl->GetTopItem(), 0);\r
806     if (nTop == nLastTop && pindexLastBest == pindexBest)\r
807         return;\r
808 \r
809     TRY_CRITICAL_BLOCK(cs_mapWallet)\r
810     {\r
811         int nStart = nTop;\r
812         int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());\r
813 \r
814         if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)\r
815         {\r
816             // If no updates, only need to do the part that moved onto the screen\r
817             if (nStart >= nLastTop && nStart < nLastTop + 100)\r
818                 nStart = nLastTop + 100;\r
819             if (nEnd >= nLastTop && nEnd < nLastTop + 100)\r
820                 nEnd = nLastTop;\r
821         }\r
822         nLastTop = nTop;\r
823         pindexLastBest = pindexBest;\r
824         nLastRefreshed = nListViewUpdated;\r
825 \r
826         for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)\r
827         {\r
828             uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));\r
829             map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);\r
830             if (mi == mapWallet.end())\r
831             {\r
832                 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");\r
833                 continue;\r
834             }\r
835             CWalletTx& wtx = (*mi).second;\r
836             if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)\r
837             {\r
838                 if (!InsertTransaction(wtx, false, nIndex))\r
839                     m_listCtrl->DeleteItem(nIndex--);\r
840             }\r
841             else\r
842                 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));\r
843         }\r
844     }\r
845 }\r
846 \r
847 void CMainFrame::OnPaint(wxPaintEvent& event)\r
848 {\r
849     if (fRefresh)\r
850     {\r
851         fRefresh = false;\r
852         Refresh();\r
853     }\r
854     event.Skip();\r
855 }\r
856 \r
857 \r
858 unsigned int nNeedRepaint = 0;\r
859 unsigned int nLastRepaint = 0;\r
860 int64 nLastRepaintTime = 0;\r
861 int64 nRepaintInterval = 500;\r
862 \r
863 void ThreadDelayedRepaint(void* parg)\r
864 {\r
865     while (!fShutdown)\r
866     {\r
867         if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)\r
868         {\r
869             nLastRepaint = nNeedRepaint;\r
870             if (pframeMain)\r
871             {\r
872                 printf("DelayedRepaint\n");\r
873                 wxPaintEvent event;\r
874                 pframeMain->fRefresh = true;\r
875                 pframeMain->GetEventHandler()->AddPendingEvent(event);\r
876             }\r
877         }\r
878         Sleep(nRepaintInterval);\r
879     }\r
880 }\r
881 \r
882 void MainFrameRepaint()\r
883 {\r
884     // This is called by network code that shouldn't access pframeMain\r
885     // directly because it could still be running after the UI is closed.\r
886     if (pframeMain)\r
887     {\r
888         // Don't repaint too often\r
889         static int64 nLastRepaintRequest;\r
890         if (GetTimeMillis() - nLastRepaintRequest < 100)\r
891         {\r
892             nNeedRepaint++;\r
893             return;\r
894         }\r
895         nLastRepaintRequest = GetTimeMillis();\r
896 \r
897         printf("MainFrameRepaint\n");\r
898         wxPaintEvent event;\r
899         pframeMain->fRefresh = true;\r
900         pframeMain->GetEventHandler()->AddPendingEvent(event);\r
901     }\r
902 }\r
903 \r
904 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)\r
905 {\r
906     if (ptaskbaricon)\r
907         ptaskbaricon->UpdateTooltip();\r
908 \r
909     //\r
910     // Slower stuff\r
911     //\r
912     static int nTransactionCount;\r
913     bool fPaintedBalance = false;\r
914     if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)\r
915     {\r
916         nLastRepaint = nNeedRepaint;\r
917         nLastRepaintTime = GetTimeMillis();\r
918 \r
919         // Update listctrl contents\r
920         if (!vWalletUpdated.empty())\r
921         {\r
922             TRY_CRITICAL_BLOCK(cs_mapWallet)\r
923             {\r
924                 string strTop;\r
925                 if (m_listCtrl->GetItemCount())\r
926                     strTop = (string)m_listCtrl->GetItemText(0);\r
927                 foreach(uint256 hash, vWalletUpdated)\r
928                 {\r
929                     map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);\r
930                     if (mi != mapWallet.end())\r
931                         InsertTransaction((*mi).second, false);\r
932                 }\r
933                 vWalletUpdated.clear();\r
934                 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))\r
935                     m_listCtrl->ScrollList(0, INT_MIN/2);\r
936             }\r
937         }\r
938 \r
939         // Balance total\r
940         TRY_CRITICAL_BLOCK(cs_mapWallet)\r
941         {\r
942             fPaintedBalance = true;\r
943             m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + "  ");\r
944 \r
945             // Count hidden and multi-line transactions\r
946             nTransactionCount = 0;\r
947             for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\r
948             {\r
949                 CWalletTx& wtx = (*it).second;\r
950                 nTransactionCount += wtx.nLinesDisplayed;\r
951             }\r
952         }\r
953     }\r
954     if (!vWalletUpdated.empty() || !fPaintedBalance)\r
955         nNeedRepaint++;\r
956 \r
957     // Update status column of visible items only\r
958     RefreshStatusColumn();\r
959 \r
960     // Update status bar\r
961     string strGen = "";\r
962     if (fGenerateBitcoins)\r
963         strGen = "    Generating";\r
964     if (fGenerateBitcoins && vNodes.empty())\r
965         strGen = "(not connected)";\r
966     m_statusBar->SetStatusText(strGen, 1);\r
967 \r
968     string strStatus = strprintf("     %d connections     %d blocks     %d transactions", vNodes.size(), nBestHeight + 1, nTransactionCount);\r
969     m_statusBar->SetStatusText(strStatus, 2);\r
970 \r
971     if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)\r
972         m_statusBar->SetStatusText("     ERROR: ThreadSocketHandler has stopped", 0);\r
973 \r
974     // Pass through to listctrl to actually do the paint, we're just hooking the message\r
975     m_listCtrl->Disconnect(wxEVT_PAINT, (wxObjectEventFunction)NULL, NULL, this);\r
976     m_listCtrl->GetEventHandler()->ProcessEvent(event);\r
977     m_listCtrl->Connect(wxEVT_PAINT, wxPaintEventHandler(CMainFrame::OnPaintListCtrl), NULL, this);\r
978 }\r
979 \r
980 \r
981 void UIThreadCall(boost::function0<void> fn)\r
982 {\r
983     // Call this with a function object created with bind.\r
984     // bind needs all parameters to match the function's expected types\r
985     // and all default parameters specified.  Some examples:\r
986     //  UIThreadCall(bind(wxBell));\r
987     //  UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));\r
988     //  UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));\r
989     if (pframeMain)\r
990     {\r
991         wxCommandEvent event(wxEVT_UITHREADCALL);\r
992         event.SetClientData((void*)new boost::function0<void>(fn));\r
993         pframeMain->GetEventHandler()->AddPendingEvent(event);\r
994     }\r
995 }\r
996 \r
997 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)\r
998 {\r
999     boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();\r
1000     (*pfn)();\r
1001     delete pfn;\r
1002 }\r
1003 \r
1004 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)\r
1005 {\r
1006     // File->Exit\r
1007     Close(true);\r
1008 }\r
1009 \r
1010 void CMainFrame::OnMenuViewShowGenerated(wxCommandEvent& event)\r
1011 {\r
1012     // View->Show Generated\r
1013     fShowGenerated = event.IsChecked();\r
1014     CWalletDB().WriteSetting("fShowGenerated", fShowGenerated);\r
1015     RefreshListCtrl();\r
1016 }\r
1017 \r
1018 void CMainFrame::OnUpdateUIViewShowGenerated(wxUpdateUIEvent& event)\r
1019 {\r
1020     event.Check(fShowGenerated);\r
1021 }\r
1022 \r
1023 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)\r
1024 {\r
1025     // Options->Generate Coins\r
1026     GenerateBitcoins(event.IsChecked());\r
1027 }\r
1028 \r
1029 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)\r
1030 {\r
1031     event.Check(fGenerateBitcoins);\r
1032 }\r
1033 \r
1034 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)\r
1035 {\r
1036     // Options->Change Your Address\r
1037     OnButtonChange(event);\r
1038 }\r
1039 \r
1040 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)\r
1041 {\r
1042     // Options->Options\r
1043     COptionsDialog dialog(this);\r
1044     dialog.ShowModal();\r
1045 }\r
1046 \r
1047 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)\r
1048 {\r
1049     // Help->About\r
1050     CAboutDialog dialog(this);\r
1051     dialog.ShowModal();\r
1052 }\r
1053 \r
1054 void CMainFrame::OnButtonSend(wxCommandEvent& event)\r
1055 {\r
1056     // Toolbar: Send\r
1057     CSendDialog dialog(this);\r
1058     dialog.ShowModal();\r
1059 }\r
1060 \r
1061 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)\r
1062 {\r
1063     // Toolbar: Address Book\r
1064     CAddressBookDialog dialogAddr(this, "", false);\r
1065     if (dialogAddr.ShowModal() == 2)\r
1066     {\r
1067         // Send\r
1068         CSendDialog dialogSend(this, dialogAddr.GetAddress());\r
1069         dialogSend.ShowModal();\r
1070     }\r
1071 }\r
1072 \r
1073 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)\r
1074 {\r
1075     // Automatically select-all when entering window\r
1076     m_textCtrlAddress->SetSelection(-1, -1);\r
1077     fOnSetFocusAddress = true;\r
1078     event.Skip();\r
1079 }\r
1080 \r
1081 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)\r
1082 {\r
1083     if (fOnSetFocusAddress)\r
1084         m_textCtrlAddress->SetSelection(-1, -1);\r
1085     fOnSetFocusAddress = false;\r
1086     event.Skip();\r
1087 }\r
1088 \r
1089 void CMainFrame::OnButtonChange(wxCommandEvent& event)\r
1090 {\r
1091     CYourAddressDialog dialog(this, string(m_textCtrlAddress->GetValue()));\r
1092     if (!dialog.ShowModal())\r
1093         return;\r
1094     string strAddress = (string)dialog.GetAddress();\r
1095     if (strAddress != m_textCtrlAddress->GetValue())\r
1096     {\r
1097         uint160 hash160;\r
1098         if (!AddressToHash160(strAddress, hash160))\r
1099             return;\r
1100         if (!mapPubKeys.count(hash160))\r
1101             return;\r
1102         CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);\r
1103         m_textCtrlAddress->SetValue(strAddress);\r
1104     }\r
1105 }\r
1106 \r
1107 void CMainFrame::OnButtonCopy(wxCommandEvent& event)\r
1108 {\r
1109     // Copy address box to clipboard\r
1110     if (wxTheClipboard->Open())\r
1111     {\r
1112         wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));\r
1113         wxTheClipboard->Close();\r
1114     }\r
1115 }\r
1116 \r
1117 void CMainFrame::OnListItemActivated(wxListEvent& event)\r
1118 {\r
1119     uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));\r
1120     CWalletTx wtx;\r
1121     CRITICAL_BLOCK(cs_mapWallet)\r
1122     {\r
1123         map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);\r
1124         if (mi == mapWallet.end())\r
1125         {\r
1126             printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");\r
1127             return;\r
1128         }\r
1129         wtx = (*mi).second;\r
1130     }\r
1131     CTxDetailsDialog dialog(this, wtx);\r
1132     dialog.ShowModal();\r
1133     //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);\r
1134     //pdialog->Show();\r
1135 }\r
1136 \r
1137 \r
1138 \r
1139 \r
1140 \r
1141 \r
1142 \r
1143 //////////////////////////////////////////////////////////////////////////////\r
1144 //\r
1145 // CTxDetailsDialog\r
1146 //\r
1147 \r
1148 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)\r
1149 {\r
1150     CRITICAL_BLOCK(cs_mapAddressBook)\r
1151     {\r
1152         string strHTML;\r
1153         strHTML.reserve(4000);\r
1154         strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";\r
1155 \r
1156         int64 nTime = wtx.GetTxTime();\r
1157         int64 nCredit = wtx.GetCredit();\r
1158         int64 nDebit = wtx.GetDebit();\r
1159         int64 nNet = nCredit - nDebit;\r
1160 \r
1161 \r
1162 \r
1163         strHTML += "<b>Status:</b> " + FormatTxStatus(wtx);\r
1164         int nRequests = wtx.GetRequestCount();\r
1165         if (nRequests != -1)\r
1166         {\r
1167             if (nRequests == 0)\r
1168                 strHTML += ", has not been successfully broadcast yet";\r
1169             else if (nRequests == 1)\r
1170                 strHTML += strprintf(", broadcast through %d node", nRequests);\r
1171             else\r
1172                 strHTML += strprintf(", broadcast through %d nodes", nRequests);\r
1173         }\r
1174         strHTML += "<br>";\r
1175 \r
1176         strHTML += "<b>Date:</b> " + (nTime ? DateTimeStr(nTime) : "") + "<br>";\r
1177 \r
1178 \r
1179         //\r
1180         // From\r
1181         //\r
1182         if (wtx.IsCoinBase())\r
1183         {\r
1184             strHTML += "<b>Source:</b> Generated<br>";\r
1185         }\r
1186         else if (!wtx.mapValue["from"].empty())\r
1187         {\r
1188             // Online transaction\r
1189             if (!wtx.mapValue["from"].empty())\r
1190                 strHTML += "<b>From:</b> " + HtmlEscape(wtx.mapValue["from"]) + "<br>";\r
1191         }\r
1192         else\r
1193         {\r
1194             // Offline transaction\r
1195             if (nNet > 0)\r
1196             {\r
1197                 // Credit\r
1198                 foreach(const CTxOut& txout, wtx.vout)\r
1199                 {\r
1200                     if (txout.IsMine())\r
1201                     {\r
1202                         vector<unsigned char> vchPubKey;\r
1203                         if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))\r
1204                         {\r
1205                             string strAddress = PubKeyToAddress(vchPubKey);\r
1206                             if (mapAddressBook.count(strAddress))\r
1207                             {\r
1208                                 strHTML += "<b>From:</b> unknown<br>";\r
1209                                 strHTML += "<b>To:</b> ";\r
1210                                 strHTML += HtmlEscape(strAddress);\r
1211                                 if (!mapAddressBook[strAddress].empty())\r
1212                                     strHTML += " (yours, label: " + mapAddressBook[strAddress] + ")";\r
1213                                 else\r
1214                                     strHTML += " (yours)";\r
1215                                 strHTML += "<br>";\r
1216                             }\r
1217                         }\r
1218                         break;\r
1219                     }\r
1220                 }\r
1221             }\r
1222         }\r
1223 \r
1224 \r
1225         //\r
1226         // To\r
1227         //\r
1228         string strAddress;\r
1229         if (!wtx.mapValue["to"].empty())\r
1230         {\r
1231             // Online transaction\r
1232             strAddress = wtx.mapValue["to"];\r
1233             strHTML += "<b>To:</b> ";\r
1234             if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())\r
1235                 strHTML += mapAddressBook[strAddress] + " ";\r
1236             strHTML += HtmlEscape(strAddress) + "<br>";\r
1237         }\r
1238 \r
1239 \r
1240         //\r
1241         // Amount\r
1242         //\r
1243         if (wtx.IsCoinBase() && nCredit == 0)\r
1244         {\r
1245             //\r
1246             // Coinbase\r
1247             //\r
1248             int64 nUnmatured = 0;\r
1249             foreach(const CTxOut& txout, wtx.vout)\r
1250                 nUnmatured += txout.GetCredit();\r
1251             if (wtx.IsInMainChain())\r
1252                 strHTML += strprintf("<b>Credit:</b> (%s matures in %d more blocks)<br>", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());\r
1253             else\r
1254                 strHTML += "<b>Credit:</b> (not accepted)<br>";\r
1255         }\r
1256         else if (nNet > 0)\r
1257         {\r
1258             //\r
1259             // Credit\r
1260             //\r
1261             strHTML += "<b>Credit:</b> " + FormatMoney(nNet) + "<br>";\r
1262         }\r
1263         else\r
1264         {\r
1265             bool fAllFromMe = true;\r
1266             foreach(const CTxIn& txin, wtx.vin)\r
1267                 fAllFromMe = fAllFromMe && txin.IsMine();\r
1268 \r
1269             bool fAllToMe = true;\r
1270             foreach(const CTxOut& txout, wtx.vout)\r
1271                 fAllToMe = fAllToMe && txout.IsMine();\r
1272 \r
1273             if (fAllFromMe)\r
1274             {\r
1275                 //\r
1276                 // Debit\r
1277                 //\r
1278                 foreach(const CTxOut& txout, wtx.vout)\r
1279                 {\r
1280                     if (txout.IsMine())\r
1281                         continue;\r
1282 \r
1283                     if (wtx.mapValue["to"].empty())\r
1284                     {\r
1285                         // Offline transaction\r
1286                         uint160 hash160;\r
1287                         if (ExtractHash160(txout.scriptPubKey, hash160))\r
1288                         {\r
1289                             string strAddress = Hash160ToAddress(hash160);\r
1290                             strHTML += "<b>To:</b> ";\r
1291                             if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())\r
1292                                 strHTML += mapAddressBook[strAddress] + " ";\r
1293                             strHTML += strAddress;\r
1294                             strHTML += "<br>";\r
1295                         }\r
1296                     }\r
1297 \r
1298                     strHTML += "<b>Debit:</b> " + FormatMoney(-txout.nValue) + "<br>";\r
1299                 }\r
1300 \r
1301                 if (fAllToMe)\r
1302                 {\r
1303                     // Payment to self\r
1304                     /// issue: can't tell which is the payment and which is the change anymore\r
1305                     //int64 nValue = wtx.vout[0].nValue;\r
1306                     //strHTML += "<b>Debit:</b> " + FormatMoney(-nValue) + "<br>";\r
1307                     //strHTML += "<b>Credit:</b> " + FormatMoney(nValue) + "<br>";\r
1308                 }\r
1309 \r
1310                 int64 nTxFee = nDebit - wtx.GetValueOut();\r
1311                 if (nTxFee > 0)\r
1312                     strHTML += "<b>Transaction fee:</b> " + FormatMoney(-nTxFee) + "<br>";\r
1313             }\r
1314             else\r
1315             {\r
1316                 //\r
1317                 // Mixed debit transaction\r
1318                 //\r
1319                 foreach(const CTxIn& txin, wtx.vin)\r
1320                     if (txin.IsMine())\r
1321                         strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";\r
1322                 foreach(const CTxOut& txout, wtx.vout)\r
1323                     if (txout.IsMine())\r
1324                         strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";\r
1325             }\r
1326         }\r
1327 \r
1328         strHTML += "<b>Net amount:</b> " + FormatMoney(nNet, true) + "<br>";\r
1329 \r
1330 \r
1331         //\r
1332         // Message\r
1333         //\r
1334         if (!wtx.mapValue["message"].empty())\r
1335             strHTML += "<br><b>Message:</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";\r
1336 \r
1337         if (wtx.IsCoinBase())\r
1338             strHTML += "<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
1339 \r
1340 \r
1341         //\r
1342         // Debug view\r
1343         //\r
1344         if (fDebug)\r
1345         {\r
1346             strHTML += "<hr><br>debug print<br><br>";\r
1347             foreach(const CTxIn& txin, wtx.vin)\r
1348                 if (txin.IsMine())\r
1349                     strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";\r
1350             foreach(const CTxOut& txout, wtx.vout)\r
1351                 if (txout.IsMine())\r
1352                     strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";\r
1353 \r
1354             strHTML += "<b>Inputs:</b><br>";\r
1355             CRITICAL_BLOCK(cs_mapWallet)\r
1356             {\r
1357                 foreach(const CTxIn& txin, wtx.vin)\r
1358                 {\r
1359                     COutPoint prevout = txin.prevout;\r
1360                     map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);\r
1361                     if (mi != mapWallet.end())\r
1362                     {\r
1363                         const CWalletTx& prev = (*mi).second;\r
1364                         if (prevout.n < prev.vout.size())\r
1365                         {\r
1366                             strHTML += HtmlEscape(prev.ToString(), true);\r
1367                             strHTML += " &nbsp;&nbsp; " + FormatTxStatus(prev) + ", ";\r
1368                             strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";\r
1369                         }\r
1370                     }\r
1371                 }\r
1372             }\r
1373 \r
1374             strHTML += "<br><hr><br><b>Transaction:</b><br>";\r
1375             strHTML += HtmlEscape(wtx.ToString(), true);\r
1376         }\r
1377 \r
1378 \r
1379 \r
1380         strHTML += "</font></html>";\r
1381         string(strHTML.begin(), strHTML.end()).swap(strHTML);\r
1382         m_htmlWin->SetPage(strHTML);\r
1383         m_buttonOK->SetFocus();\r
1384     }\r
1385 }\r
1386 \r
1387 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)\r
1388 {\r
1389     Close();\r
1390     //Destroy();\r
1391 }\r
1392 \r
1393 \r
1394 \r
1395 \r
1396 \r
1397 //////////////////////////////////////////////////////////////////////////////\r
1398 //\r
1399 // COptionsDialog\r
1400 //\r
1401 \r
1402 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)\r
1403 {\r
1404     // Set up list box of page choices\r
1405     m_listBox->Append("Main");\r
1406     //m_listBox->Append("Test 2");\r
1407     m_listBox->SetSelection(0);\r
1408     SelectPage(0);\r
1409 #ifndef __WXMSW__\r
1410     m_checkBoxMinimizeOnClose->SetLabel("&Minimize on close");\r
1411     m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet\r
1412 #endif\r
1413 \r
1414     // Init values\r
1415     m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));\r
1416     m_checkBoxLimitProcessors->SetValue(fLimitProcessors);\r
1417     m_spinCtrlLimitProcessors->Enable(fLimitProcessors);\r
1418     m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);\r
1419     int nProcessors = wxThread::GetCPUCount();\r
1420     if (nProcessors < 1)\r
1421         nProcessors = 999;\r
1422     m_spinCtrlLimitProcessors->SetRange(1, nProcessors);\r
1423     m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());\r
1424     m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);\r
1425     m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);\r
1426     m_checkBoxUseProxy->SetValue(fUseProxy);\r
1427     m_textCtrlProxyIP->Enable(fUseProxy);\r
1428     m_textCtrlProxyPort->Enable(fUseProxy);\r
1429     m_staticTextProxyIP->Enable(fUseProxy);\r
1430     m_staticTextProxyPort->Enable(fUseProxy);\r
1431     m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());\r
1432     m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());\r
1433 \r
1434     m_buttonOK->SetFocus();\r
1435 }\r
1436 \r
1437 void COptionsDialog::SelectPage(int nPage)\r
1438 {\r
1439     m_panelMain->Show(nPage == 0);\r
1440     m_panelTest2->Show(nPage == 1);\r
1441 \r
1442     m_scrolledWindow->Layout();\r
1443     m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);\r
1444 }\r
1445 \r
1446 void COptionsDialog::OnListBox(wxCommandEvent& event)\r
1447 {\r
1448     SelectPage(event.GetSelection());\r
1449 }\r
1450 \r
1451 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)\r
1452 {\r
1453     int64 nTmp = nTransactionFee;\r
1454     ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);\r
1455     m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));\r
1456 }\r
1457 \r
1458 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)\r
1459 {\r
1460     m_spinCtrlLimitProcessors->Enable(event.IsChecked());\r
1461 }\r
1462 \r
1463 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)\r
1464 {\r
1465     m_textCtrlProxyIP->Enable(event.IsChecked());\r
1466     m_textCtrlProxyPort->Enable(event.IsChecked());\r
1467     m_staticTextProxyIP->Enable(event.IsChecked());\r
1468     m_staticTextProxyPort->Enable(event.IsChecked());\r
1469 }\r
1470 \r
1471 CAddress COptionsDialog::GetProxyAddr()\r
1472 {\r
1473     // Be careful about byte order, addr.ip and addr.port are big endian\r
1474     CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());\r
1475     if (addr.ip == INADDR_NONE)\r
1476         addr.ip = addrProxy.ip;\r
1477     int nPort = atoi(m_textCtrlProxyPort->GetValue());\r
1478     addr.port = htons(nPort);\r
1479     if (nPort <= 0 || nPort > USHRT_MAX)\r
1480         addr.port = addrProxy.port;\r
1481     return addr;\r
1482 }\r
1483 \r
1484 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)\r
1485 {\r
1486     m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());\r
1487     m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());\r
1488 }\r
1489 \r
1490 \r
1491 void COptionsDialog::OnButtonOK(wxCommandEvent& event)\r
1492 {\r
1493     OnButtonApply(event);\r
1494     Close();\r
1495 }\r
1496 \r
1497 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)\r
1498 {\r
1499     Close();\r
1500 }\r
1501 \r
1502 void COptionsDialog::OnButtonApply(wxCommandEvent& event)\r
1503 {\r
1504     CWalletDB walletdb;\r
1505 \r
1506     int64 nPrevTransactionFee = nTransactionFee;\r
1507     if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)\r
1508         walletdb.WriteSetting("nTransactionFee", nTransactionFee);\r
1509 \r
1510     int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);\r
1511     if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())\r
1512     {\r
1513         fLimitProcessors = m_checkBoxLimitProcessors->GetValue();\r
1514         walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);\r
1515     }\r
1516     if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())\r
1517     {\r
1518         nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();\r
1519         walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);\r
1520     }\r
1521     if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)\r
1522         GenerateBitcoins(fGenerateBitcoins);\r
1523 \r
1524     if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())\r
1525     {\r
1526         fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();\r
1527         SetStartOnSystemStartup(fTmpStartOnSystemStartup);\r
1528     }\r
1529 \r
1530     if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())\r
1531     {\r
1532         fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();\r
1533         walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);\r
1534         ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);\r
1535     }\r
1536 \r
1537     if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())\r
1538     {\r
1539         fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();\r
1540         walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);\r
1541     }\r
1542 \r
1543     fUseProxy = m_checkBoxUseProxy->GetValue();\r
1544     walletdb.WriteSetting("fUseProxy", fUseProxy);\r
1545 \r
1546     addrProxy = GetProxyAddr();\r
1547     walletdb.WriteSetting("addrProxy", addrProxy);\r
1548 }\r
1549 \r
1550 \r
1551 \r
1552 \r
1553 \r
1554 \r
1555 //////////////////////////////////////////////////////////////////////////////\r
1556 //\r
1557 // CAboutDialog\r
1558 //\r
1559 \r
1560 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)\r
1561 {\r
1562     m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d beta", VERSION/100, VERSION%100));\r
1563 \r
1564 #if !wxUSE_UNICODE\r
1565     // Workaround until upgrade to wxWidgets supporting UTF-8\r
1566     // Hack to change the (c) character from UTF-8 back to ANSI\r
1567     wxString str = m_staticTextMain->GetLabel();\r
1568     if (str.Find('\xC2') != wxNOT_FOUND)\r
1569         str.Remove(str.Find('\xC2'), 1);\r
1570     m_staticTextMain->SetLabel(str);\r
1571 #endif\r
1572 #ifndef __WXMSW__\r
1573     // Resize on Linux to make the window fit the text.\r
1574     // The text was wrapped manually rather than using the Wrap setting because\r
1575     // the wrap would be too small on Linux and it can't be changed at this point.\r
1576     wxFont fontTmp = m_staticTextMain->GetFont();\r
1577     if (fontTmp.GetPointSize() > 8);\r
1578         fontTmp.SetPointSize(8);\r
1579     m_staticTextMain->SetFont(fontTmp);\r
1580     SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() - 4);\r
1581 #endif\r
1582 }\r
1583 \r
1584 void CAboutDialog::OnButtonOK(wxCommandEvent& event)\r
1585 {\r
1586     Close();\r
1587 }\r
1588 \r
1589 \r
1590 \r
1591 \r
1592 \r
1593 \r
1594 //////////////////////////////////////////////////////////////////////////////\r
1595 //\r
1596 // CSendDialog\r
1597 //\r
1598 \r
1599 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)\r
1600 {\r
1601     // Init\r
1602     m_textCtrlAddress->SetValue(strAddress);\r
1603     m_choiceTransferType->SetSelection(0);\r
1604     m_bitmapCheckMark->Show(false);\r
1605     fEnabledPrev = true;\r
1606     m_textCtrlAddress->SetFocus();\r
1607     //// todo: should add a display of your balance for convenience\r
1608 #ifndef __WXMSW__\r
1609     wxFont fontTmp = m_staticTextInstructions->GetFont();\r
1610     if (fontTmp.GetPointSize() > 9);\r
1611         fontTmp.SetPointSize(9);\r
1612     m_staticTextInstructions->SetFont(fontTmp);\r
1613     SetSize(725, 380);\r
1614 #endif\r
1615 \r
1616     // Set Icon\r
1617     wxIcon iconSend;\r
1618     iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));\r
1619     SetIcon(iconSend);\r
1620 \r
1621     wxCommandEvent event;\r
1622     OnTextAddress(event);\r
1623 \r
1624     // Fixup the tab order\r
1625     m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);\r
1626     m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);\r
1627     this->Layout();\r
1628 }\r
1629 \r
1630 void CSendDialog::OnTextAddress(wxCommandEvent& event)\r
1631 {\r
1632     // Check mark\r
1633     bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());\r
1634     m_bitmapCheckMark->Show(fBitcoinAddress);\r
1635 \r
1636     // Grey out message if bitcoin address\r
1637     bool fEnable = !fBitcoinAddress;\r
1638     m_staticTextFrom->Enable(fEnable);\r
1639     m_textCtrlFrom->Enable(fEnable);\r
1640     m_staticTextMessage->Enable(fEnable);\r
1641     m_textCtrlMessage->Enable(fEnable);\r
1642     m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));\r
1643     if (!fEnable && fEnabledPrev)\r
1644     {\r
1645         strFromSave    = m_textCtrlFrom->GetValue();\r
1646         strMessageSave = m_textCtrlMessage->GetValue();\r
1647         m_textCtrlFrom->SetValue("Will appear as \"From: Unknown\"");\r
1648         m_textCtrlMessage->SetValue("Can't include a message when sending to a Bitcoin address");\r
1649     }\r
1650     else if (fEnable && !fEnabledPrev)\r
1651     {\r
1652         m_textCtrlFrom->SetValue(strFromSave);\r
1653         m_textCtrlMessage->SetValue(strMessageSave);\r
1654     }\r
1655     fEnabledPrev = fEnable;\r
1656 }\r
1657 \r
1658 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)\r
1659 {\r
1660     // Reformat the amount\r
1661     if (m_textCtrlAmount->GetValue().Trim().empty())\r
1662         return;\r
1663     int64 nTmp;\r
1664     if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))\r
1665         m_textCtrlAmount->SetValue(FormatMoney(nTmp));\r
1666 }\r
1667 \r
1668 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)\r
1669 {\r
1670     // Open address book\r
1671     CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), true);\r
1672     if (dialog.ShowModal())\r
1673         m_textCtrlAddress->SetValue(dialog.GetAddress());\r
1674 }\r
1675 \r
1676 void CSendDialog::OnButtonPaste(wxCommandEvent& event)\r
1677 {\r
1678     // Copy clipboard to address box\r
1679     if (wxTheClipboard->Open())\r
1680     {\r
1681         if (wxTheClipboard->IsSupported(wxDF_TEXT))\r
1682         {\r
1683             wxTextDataObject data;\r
1684             wxTheClipboard->GetData(data);\r
1685             m_textCtrlAddress->SetValue(data.GetText());\r
1686         }\r
1687         wxTheClipboard->Close();\r
1688     }\r
1689 }\r
1690 \r
1691 void CSendDialog::OnButtonSend(wxCommandEvent& event)\r
1692 {\r
1693     CWalletTx wtx;\r
1694     string strAddress = (string)m_textCtrlAddress->GetValue();\r
1695 \r
1696     // Parse amount\r
1697     int64 nValue = 0;\r
1698     if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)\r
1699     {\r
1700         wxMessageBox("Error in amount  ", "Send Coins");\r
1701         return;\r
1702     }\r
1703     if (nValue > GetBalance())\r
1704     {\r
1705         wxMessageBox("Amount exceeds your balance  ", "Send Coins");\r
1706         return;\r
1707     }\r
1708     if (nValue + nTransactionFee > GetBalance())\r
1709     {\r
1710         wxMessageBox(string("Total exceeds your balance when the ") + FormatMoney(nTransactionFee) + " transaction fee is included  ", "Send Coins");\r
1711         return;\r
1712     }\r
1713 \r
1714     // Parse bitcoin address\r
1715     uint160 hash160;\r
1716     bool fBitcoinAddress = AddressToHash160(strAddress, hash160);\r
1717 \r
1718     if (fBitcoinAddress)\r
1719     {\r
1720         // Send to bitcoin address\r
1721         CScript scriptPubKey;\r
1722         scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;\r
1723 \r
1724         string strError = SendMoney(scriptPubKey, nValue, wtx);\r
1725         if (strError != "")\r
1726             wxMessageBox(strError + "  ", "Sending...");\r
1727         else\r
1728             wxMessageBox("Payment sent  ", "Sending...");\r
1729     }\r
1730     else\r
1731     {\r
1732         // Parse IP address\r
1733         CAddress addr(strAddress);\r
1734         if (!addr.IsValid())\r
1735         {\r
1736             wxMessageBox("Invalid address  ", "Send Coins");\r
1737             return;\r
1738         }\r
1739 \r
1740         // Message\r
1741         wtx.mapValue["to"] = strAddress;\r
1742         wtx.mapValue["from"] = m_textCtrlFrom->GetValue();\r
1743         wtx.mapValue["message"] = m_textCtrlMessage->GetValue();\r
1744 \r
1745         // Send to IP address\r
1746         CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);\r
1747         if (!pdialog->ShowModal())\r
1748             return;\r
1749     }\r
1750 \r
1751     CRITICAL_BLOCK(cs_mapAddressBook)\r
1752         if (!mapAddressBook.count(strAddress))\r
1753             SetAddressBookName(strAddress, "");\r
1754 \r
1755     EndModal(true);\r
1756 }\r
1757 \r
1758 void CSendDialog::OnButtonCancel(wxCommandEvent& event)\r
1759 {\r
1760     // Cancel\r
1761     EndModal(false);\r
1762 }\r
1763 \r
1764 \r
1765 \r
1766 \r
1767 \r
1768 \r
1769 //////////////////////////////////////////////////////////////////////////////\r
1770 //\r
1771 // CSendingDialog\r
1772 //\r
1773 \r
1774 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
1775 {\r
1776     addr = addrIn;\r
1777     nPrice = nPriceIn;\r
1778     wtx = wtxIn;\r
1779     start = wxDateTime::UNow();\r
1780     memset(pszStatus, 0, sizeof(pszStatus));\r
1781     fCanCancel = true;\r
1782     fAbort = false;\r
1783     fSuccess = false;\r
1784     fUIDone = false;\r
1785     fWorkDone = false;\r
1786 #ifndef __WXMSW__\r
1787     SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());\r
1788 #endif\r
1789 \r
1790     SetTitle(strprintf("Sending %s to %s", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));\r
1791     m_textCtrlStatus->SetValue("");\r
1792 \r
1793     CreateThread(SendingDialogStartTransfer, this);\r
1794 }\r
1795 \r
1796 CSendingDialog::~CSendingDialog()\r
1797 {\r
1798     printf("~CSendingDialog()\n");\r
1799 }\r
1800 \r
1801 void CSendingDialog::Close()\r
1802 {\r
1803     // Last one out turn out the lights.\r
1804     // fWorkDone signals that work side is done and UI thread should call destroy.\r
1805     // fUIDone signals that UI window has closed and work thread should call destroy.\r
1806     // This allows the window to disappear and end modality when cancelled\r
1807     // without making the user wait for ConnectNode to return.  The dialog object\r
1808     // hangs around in the background until the work thread exits.\r
1809     if (IsModal())\r
1810         EndModal(fSuccess);\r
1811     else\r
1812         Show(false);\r
1813     if (fWorkDone)\r
1814         Destroy();\r
1815     else\r
1816         fUIDone = true;\r
1817 }\r
1818 \r
1819 void CSendingDialog::OnClose(wxCloseEvent& event)\r
1820 {\r
1821     if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)\r
1822     {\r
1823         Close();\r
1824     }\r
1825     else\r
1826     {\r
1827         event.Veto();\r
1828         wxCommandEvent cmdevent;\r
1829         OnButtonCancel(cmdevent);\r
1830     }\r
1831 }\r
1832 \r
1833 void CSendingDialog::OnButtonOK(wxCommandEvent& event)\r
1834 {\r
1835     if (fWorkDone)\r
1836         Close();\r
1837 }\r
1838 \r
1839 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)\r
1840 {\r
1841     if (fCanCancel)\r
1842         fAbort = true;\r
1843 }\r
1844 \r
1845 void CSendingDialog::OnPaint(wxPaintEvent& event)\r
1846 {\r
1847     if (strlen(pszStatus) > 130)\r
1848         m_textCtrlStatus->SetValue(string("\n") + pszStatus);\r
1849     else\r
1850         m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);\r
1851     m_staticTextSending->SetFocus();\r
1852     if (!fCanCancel)\r
1853         m_buttonCancel->Enable(false);\r
1854     if (fWorkDone)\r
1855     {\r
1856         m_buttonOK->Enable(true);\r
1857         m_buttonOK->SetFocus();\r
1858         m_buttonCancel->Enable(false);\r
1859     }\r
1860     if (fAbort && fCanCancel && IsShown())\r
1861     {\r
1862         strcpy(pszStatus, "CANCELLED");\r
1863         m_buttonOK->Enable(true);\r
1864         m_buttonOK->SetFocus();\r
1865         m_buttonCancel->Enable(false);\r
1866         m_buttonCancel->SetLabel("Cancelled");\r
1867         Close();\r
1868         wxMessageBox("Transfer cancelled  ", "Sending...", wxOK, this);\r
1869     }\r
1870     event.Skip();\r
1871 }\r
1872 \r
1873 \r
1874 //\r
1875 // Everything from here on is not in the UI thread and must only communicate\r
1876 // with the rest of the dialog through variables and calling repaint.\r
1877 //\r
1878 \r
1879 void CSendingDialog::Repaint()\r
1880 {\r
1881     Refresh();\r
1882     wxPaintEvent event;\r
1883     GetEventHandler()->AddPendingEvent(event);\r
1884 }\r
1885 \r
1886 bool CSendingDialog::Status()\r
1887 {\r
1888     if (fUIDone)\r
1889     {\r
1890         Destroy();\r
1891         return false;\r
1892     }\r
1893     if (fAbort && fCanCancel)\r
1894     {\r
1895         memset(pszStatus, 0, 10);\r
1896         strcpy(pszStatus, "CANCELLED");\r
1897         Repaint();\r
1898         fWorkDone = true;\r
1899         return false;\r
1900     }\r
1901     return true;\r
1902 }\r
1903 \r
1904 bool CSendingDialog::Status(const string& str)\r
1905 {\r
1906     if (!Status())\r
1907         return false;\r
1908 \r
1909     // This can be read by the UI thread at any time,\r
1910     // so copy in a way that can be read cleanly at all times.\r
1911     memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));\r
1912     strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));\r
1913 \r
1914     Repaint();\r
1915     return true;\r
1916 }\r
1917 \r
1918 bool CSendingDialog::Error(const string& str)\r
1919 {\r
1920     fCanCancel = false;\r
1921     fWorkDone = true;\r
1922     Status(string("Error: ") + str);\r
1923     return false;\r
1924 }\r
1925 \r
1926 void SendingDialogStartTransfer(void* parg)\r
1927 {\r
1928     ((CSendingDialog*)parg)->StartTransfer();\r
1929 }\r
1930 \r
1931 void CSendingDialog::StartTransfer()\r
1932 {\r
1933     // Make sure we have enough money\r
1934     if (nPrice + nTransactionFee > GetBalance())\r
1935     {\r
1936         Error("You don't have enough money");\r
1937         return;\r
1938     }\r
1939 \r
1940     // We may have connected already for product details\r
1941     if (!Status("Connecting..."))\r
1942         return;\r
1943     CNode* pnode = ConnectNode(addr, 15 * 60);\r
1944     if (!pnode)\r
1945     {\r
1946         Error("Unable to connect");\r
1947         return;\r
1948     }\r
1949 \r
1950     // Send order to seller, with response going to OnReply2 via event handler\r
1951     if (!Status("Requesting public key..."))\r
1952         return;\r
1953     pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);\r
1954 }\r
1955 \r
1956 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)\r
1957 {\r
1958     ((CSendingDialog*)parg)->OnReply2(vRecv);\r
1959 }\r
1960 \r
1961 void CSendingDialog::OnReply2(CDataStream& vRecv)\r
1962 {\r
1963     if (!Status("Received public key..."))\r
1964         return;\r
1965 \r
1966     CScript scriptPubKey;\r
1967     int nRet;\r
1968     try\r
1969     {\r
1970         vRecv >> nRet;\r
1971         if (nRet > 0)\r
1972         {\r
1973             string strMessage;\r
1974             vRecv >> strMessage;\r
1975             Error("Transfer was not accepted");\r
1976             //// todo: enlarge the window and enable a hidden white box to put seller's message\r
1977             return;\r
1978         }\r
1979         vRecv >> scriptPubKey;\r
1980     }\r
1981     catch (...)\r
1982     {\r
1983         //// what do we want to do about this?\r
1984         Error("Invalid response received");\r
1985         return;\r
1986     }\r
1987 \r
1988     // Pause to give the user a chance to cancel\r
1989     while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))\r
1990     {\r
1991         Sleep(200);\r
1992         if (!Status())\r
1993             return;\r
1994     }\r
1995 \r
1996     CRITICAL_BLOCK(cs_main)\r
1997     {\r
1998         // Pay\r
1999         if (!Status("Creating transaction..."))\r
2000             return;\r
2001         if (nPrice + nTransactionFee > GetBalance())\r
2002         {\r
2003             Error("You don't have enough money");\r
2004             return;\r
2005         }\r
2006         CKey key;\r
2007         int64 nFeeRequired;\r
2008         if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))\r
2009         {\r
2010             if (nPrice + nFeeRequired > GetBalance())\r
2011                 Error(strprintf("This is an oversized transaction that requires a transaction fee of %s", FormatMoney(nFeeRequired).c_str()));\r
2012             else\r
2013                 Error("Transaction creation failed");\r
2014             return;\r
2015         }\r
2016 \r
2017         // Make sure we're still connected\r
2018         CNode* pnode = ConnectNode(addr, 2 * 60 * 60);\r
2019         if (!pnode)\r
2020         {\r
2021             Error("Lost connection, transaction cancelled");\r
2022             return;\r
2023         }\r
2024 \r
2025         // Last chance to cancel\r
2026         Sleep(50);\r
2027         if (!Status())\r
2028             return;\r
2029         fCanCancel = false;\r
2030         if (fAbort)\r
2031         {\r
2032             fCanCancel = true;\r
2033             if (!Status())\r
2034                 return;\r
2035             fCanCancel = false;\r
2036         }\r
2037         if (!Status("Sending payment..."))\r
2038             return;\r
2039 \r
2040         // Commit\r
2041         if (!CommitTransactionSpent(wtx, key))\r
2042         {\r
2043             Error("Error finalizing payment");\r
2044             return;\r
2045         }\r
2046 \r
2047         // Send payment tx to seller, with response going to OnReply3 via event handler\r
2048         pnode->PushRequest("submitorder", wtx, SendingDialogOnReply3, this);\r
2049 \r
2050         // Accept and broadcast transaction\r
2051         if (!wtx.AcceptTransaction())\r
2052             printf("ERROR: CSendingDialog : wtxNew.AcceptTransaction() %s failed\n", wtx.GetHash().ToString().c_str());\r
2053         wtx.RelayWalletTransaction();\r
2054 \r
2055         Status("Waiting for confirmation...");\r
2056         MainFrameRepaint();\r
2057     }\r
2058 }\r
2059 \r
2060 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)\r
2061 {\r
2062     ((CSendingDialog*)parg)->OnReply3(vRecv);\r
2063 }\r
2064 \r
2065 void CSendingDialog::OnReply3(CDataStream& vRecv)\r
2066 {\r
2067     int nRet;\r
2068     try\r
2069     {\r
2070         vRecv >> nRet;\r
2071         if (nRet > 0)\r
2072         {\r
2073             Error("The payment was sent, but the recipient was unable to verify it.\n"\r
2074                   "The transaction is recorded and will credit to the recipient,\n"\r
2075                   "but the comment information will be blank.");\r
2076             return;\r
2077         }\r
2078     }\r
2079     catch (...)\r
2080     {\r
2081         //// what do we want to do about this?\r
2082         Error("Payment was sent, but an invalid response was received");\r
2083         return;\r
2084     }\r
2085 \r
2086     fSuccess = true;\r
2087     fWorkDone = true;\r
2088     Status("Payment completed");\r
2089 }\r
2090 \r
2091 \r
2092 \r
2093 \r
2094 \r
2095 \r
2096 //////////////////////////////////////////////////////////////////////////////\r
2097 //\r
2098 // CYourAddressDialog\r
2099 //\r
2100 \r
2101 CYourAddressDialog::CYourAddressDialog(wxWindow* parent, const string& strInitSelected) : CYourAddressDialogBase(parent)\r
2102 {\r
2103     // Init column headers\r
2104     m_listCtrl->InsertColumn(0, "Label", wxLIST_FORMAT_LEFT, 200);\r
2105     m_listCtrl->InsertColumn(1, "Bitcoin Address", wxLIST_FORMAT_LEFT, 350);\r
2106     m_listCtrl->SetFocus();\r
2107 \r
2108     // Fill listctrl with address book data\r
2109     CRITICAL_BLOCK(cs_mapKeys)\r
2110     CRITICAL_BLOCK(cs_mapAddressBook)\r
2111     {\r
2112         foreach(const PAIRTYPE(string, string)& item, mapAddressBook)\r
2113         {\r
2114             string strAddress = item.first;\r
2115             string strName = item.second;\r
2116             uint160 hash160;\r
2117             bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));\r
2118             if (fMine)\r
2119             {\r
2120                 int nIndex = InsertLine(m_listCtrl, strName, strAddress);\r
2121                 if (strAddress == strInitSelected)\r
2122                     m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);\r
2123             }\r
2124         }\r
2125     }\r
2126 }\r
2127 \r
2128 wxString CYourAddressDialog::GetAddress()\r
2129 {\r
2130     int nIndex = GetSelection(m_listCtrl);\r
2131     if (nIndex == -1)\r
2132         return "";\r
2133     return GetItemText(m_listCtrl, nIndex, 1);\r
2134 }\r
2135 \r
2136 void CYourAddressDialog::OnListEndLabelEdit(wxListEvent& event)\r
2137 {\r
2138     // Update address book with edited name\r
2139     if (event.IsEditCancelled())\r
2140         return;\r
2141     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);\r
2142     SetAddressBookName(strAddress, string(event.GetText()));\r
2143     pframeMain->RefreshListCtrl();\r
2144 }\r
2145 \r
2146 void CYourAddressDialog::OnListItemSelected(wxListEvent& event)\r
2147 {\r
2148 }\r
2149 \r
2150 void CYourAddressDialog::OnListItemActivated(wxListEvent& event)\r
2151 {\r
2152     // Doubleclick edits item\r
2153     wxCommandEvent event2;\r
2154     OnButtonRename(event2);\r
2155 }\r
2156 \r
2157 void CYourAddressDialog::OnButtonRename(wxCommandEvent& event)\r
2158 {\r
2159     // Ask new name\r
2160     int nIndex = GetSelection(m_listCtrl);\r
2161     if (nIndex == -1)\r
2162         return;\r
2163     string strName = (string)m_listCtrl->GetItemText(nIndex);\r
2164     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);\r
2165     CGetTextFromUserDialog dialog(this, "Edit Address Label", "New Label", strName);\r
2166     if (!dialog.ShowModal())\r
2167         return;\r
2168     strName = dialog.GetValue();\r
2169 \r
2170     // Change name\r
2171     SetAddressBookName(strAddress, strName);\r
2172     m_listCtrl->SetItemText(nIndex, strName);\r
2173     pframeMain->RefreshListCtrl();\r
2174 }\r
2175 \r
2176 void CYourAddressDialog::OnButtonNew(wxCommandEvent& event)\r
2177 {\r
2178     // Ask name\r
2179     CGetTextFromUserDialog dialog(this, "New Bitcoin Address", "Label", "");\r
2180     if (!dialog.ShowModal())\r
2181         return;\r
2182     string strName = dialog.GetValue();\r
2183 \r
2184     // Generate new key\r
2185     string strAddress = PubKeyToAddress(GenerateNewKey());\r
2186     SetAddressBookName(strAddress, strName);\r
2187 \r
2188     // Add to list and select it\r
2189     int nIndex = InsertLine(m_listCtrl, strName, strAddress);\r
2190     SetSelection(m_listCtrl, nIndex);\r
2191     m_listCtrl->SetFocus();\r
2192 }\r
2193 \r
2194 void CYourAddressDialog::OnButtonCopy(wxCommandEvent& event)\r
2195 {\r
2196     // Copy address box to clipboard\r
2197     if (wxTheClipboard->Open())\r
2198     {\r
2199         wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));\r
2200         wxTheClipboard->Close();\r
2201     }\r
2202 }\r
2203 \r
2204 void CYourAddressDialog::OnButtonOK(wxCommandEvent& event)\r
2205 {\r
2206     // OK\r
2207     EndModal(true);\r
2208 }\r
2209 \r
2210 void CYourAddressDialog::OnButtonCancel(wxCommandEvent& event)\r
2211 {\r
2212     // Cancel\r
2213     EndModal(false);\r
2214 }\r
2215 \r
2216 void CYourAddressDialog::OnClose(wxCloseEvent& event)\r
2217 {\r
2218     // Close\r
2219     EndModal(false);\r
2220 }\r
2221 \r
2222 \r
2223 \r
2224 \r
2225 \r
2226 \r
2227 //////////////////////////////////////////////////////////////////////////////\r
2228 //\r
2229 // CAddressBookDialog\r
2230 //\r
2231 \r
2232 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, bool fSendingIn) : CAddressBookDialogBase(parent)\r
2233 {\r
2234     fSending = fSendingIn;\r
2235     if (!fSending)\r
2236         m_buttonCancel->Show(false);\r
2237 \r
2238     // Init column headers\r
2239     m_listCtrl->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 200);\r
2240     m_listCtrl->InsertColumn(1, "Address", wxLIST_FORMAT_LEFT, 350);\r
2241     m_listCtrl->SetFocus();\r
2242 \r
2243     // Set Icon\r
2244     wxIcon iconAddressBook;\r
2245     iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));\r
2246     SetIcon(iconAddressBook);\r
2247 \r
2248     // Fill listctrl with address book data\r
2249     CRITICAL_BLOCK(cs_mapKeys)\r
2250     CRITICAL_BLOCK(cs_mapAddressBook)\r
2251     {\r
2252         foreach(const PAIRTYPE(string, string)& item, mapAddressBook)\r
2253         {\r
2254             string strAddress = item.first;\r
2255             string strName = item.second;\r
2256             uint160 hash160;\r
2257             bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));\r
2258             if (!fMine)\r
2259             {\r
2260                 int nIndex = InsertLine(m_listCtrl, strName, strAddress);\r
2261                 if (strAddress == strInitSelected)\r
2262                     m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);\r
2263             }\r
2264         }\r
2265     }\r
2266 }\r
2267 \r
2268 wxString CAddressBookDialog::GetAddress()\r
2269 {\r
2270     int nIndex = GetSelection(m_listCtrl);\r
2271     if (nIndex == -1)\r
2272         return "";\r
2273     return GetItemText(m_listCtrl, nIndex, 1);\r
2274 }\r
2275 \r
2276 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)\r
2277 {\r
2278     // Update address book with edited name\r
2279     if (event.IsEditCancelled())\r
2280         return;\r
2281     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);\r
2282     SetAddressBookName(strAddress, string(event.GetText()));\r
2283     pframeMain->RefreshListCtrl();\r
2284 }\r
2285 \r
2286 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)\r
2287 {\r
2288 }\r
2289 \r
2290 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)\r
2291 {\r
2292     if (fSending)\r
2293     {\r
2294         // Doubleclick returns selection\r
2295         EndModal(GetAddress() != "" ? 2 : 0);\r
2296     }\r
2297     else\r
2298     {\r
2299         // Doubleclick edits item\r
2300         wxCommandEvent event2;\r
2301         OnButtonEdit(event2);\r
2302     }\r
2303 }\r
2304 \r
2305 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)\r
2306 {\r
2307     uint160 hash160;\r
2308     bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));\r
2309     if (fMine)\r
2310         wxMessageBox("This is one of your own addresses for receiving payments and cannot be entered in the address book.  ", strTitle);\r
2311     return fMine;\r
2312 }\r
2313 \r
2314 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)\r
2315 {\r
2316     // Ask new name\r
2317     int nIndex = GetSelection(m_listCtrl);\r
2318     if (nIndex == -1)\r
2319         return;\r
2320     string strName = (string)m_listCtrl->GetItemText(nIndex);\r
2321     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);\r
2322     string strAddressOrg = strAddress;\r
2323     do\r
2324     {\r
2325         CGetTextFromUserDialog dialog(this, "Edit Address", "Name", strName, "Address", strAddress);\r
2326         if (!dialog.ShowModal())\r
2327             return;\r
2328         strName = dialog.GetValue1();\r
2329         strAddress = dialog.GetValue2();\r
2330     }\r
2331     while (CheckIfMine(strAddress, "Edit Address"));\r
2332 \r
2333     // Change name\r
2334     if (strAddress != strAddressOrg)\r
2335         CWalletDB().EraseName(strAddressOrg);\r
2336     SetAddressBookName(strAddress, strName);\r
2337     m_listCtrl->SetItem(nIndex, 1, strAddress);\r
2338     m_listCtrl->SetItemText(nIndex, strName);\r
2339     pframeMain->RefreshListCtrl();\r
2340 }\r
2341 \r
2342 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)\r
2343 {\r
2344     // Ask name\r
2345     string strName;\r
2346     string strAddress;\r
2347     do\r
2348     {\r
2349         CGetTextFromUserDialog dialog(this, "New Address", "Name", strName, "Address", strAddress);\r
2350         if (!dialog.ShowModal())\r
2351             return;\r
2352         strName = dialog.GetValue1();\r
2353         strAddress = dialog.GetValue2();\r
2354     }\r
2355     while (CheckIfMine(strAddress, "New Address"));\r
2356 \r
2357     // Add to list and select it\r
2358     SetAddressBookName(strAddress, strName);\r
2359     int nIndex = InsertLine(m_listCtrl, strName, strAddress);\r
2360     SetSelection(m_listCtrl, nIndex);\r
2361     m_listCtrl->SetFocus();\r
2362     pframeMain->RefreshListCtrl();\r
2363 }\r
2364 \r
2365 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)\r
2366 {\r
2367     for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)\r
2368     {\r
2369         if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))\r
2370         {\r
2371             string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);\r
2372             CWalletDB().EraseName(strAddress);\r
2373             m_listCtrl->DeleteItem(nIndex);\r
2374         }\r
2375     }\r
2376     pframeMain->RefreshListCtrl();\r
2377 }\r
2378 \r
2379 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)\r
2380 {\r
2381     // Copy address box to clipboard\r
2382     if (wxTheClipboard->Open())\r
2383     {\r
2384         wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));\r
2385         wxTheClipboard->Close();\r
2386     }\r
2387 }\r
2388 \r
2389 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)\r
2390 {\r
2391     // OK\r
2392     EndModal(GetAddress() != "" ? 1 : 0);\r
2393 }\r
2394 \r
2395 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)\r
2396 {\r
2397     // Cancel\r
2398     EndModal(0);\r
2399 }\r
2400 \r
2401 void CAddressBookDialog::OnClose(wxCloseEvent& event)\r
2402 {\r
2403     // Close\r
2404     EndModal(0);\r
2405 }\r
2406 \r
2407 \r
2408 \r
2409 \r
2410 \r
2411 \r
2412 //////////////////////////////////////////////////////////////////////////////\r
2413 //\r
2414 // CMyTaskBarIcon\r
2415 //\r
2416 \r
2417 enum\r
2418 {\r
2419     ID_TASKBAR_RESTORE = 10001,\r
2420     ID_TASKBAR_OPTIONS,\r
2421     ID_TASKBAR_GENERATE,\r
2422     ID_TASKBAR_EXIT,\r
2423 };\r
2424 \r
2425 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)\r
2426     EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)\r
2427     EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)\r
2428     EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)\r
2429     EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)\r
2430     EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)\r
2431     EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)\r
2432 END_EVENT_TABLE()\r
2433 \r
2434 void CMyTaskBarIcon::Show(bool fShow)\r
2435 {\r
2436     static char pszPrevTip[200];\r
2437     if (fShow)\r
2438     {\r
2439         string strTooltip = "Bitcoin";\r
2440         if (fGenerateBitcoins)\r
2441             strTooltip = "Bitcoin - Generating";\r
2442         if (fGenerateBitcoins && vNodes.empty())\r
2443             strTooltip = "Bitcoin - (not connected)";\r
2444 \r
2445         // Optimization, only update when changed, using char array to be reentrant\r
2446         if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)\r
2447         {\r
2448             strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));\r
2449 #ifdef __WXMSW__\r
2450             SetIcon(wxICON(bitcoin), strTooltip);\r
2451 #else\r
2452             SetIcon(bitcoin20_xpm, strTooltip);\r
2453 #endif\r
2454         }\r
2455     }\r
2456     else\r
2457     {\r
2458         strlcpy(pszPrevTip, "", sizeof(pszPrevTip));\r
2459         RemoveIcon();\r
2460     }\r
2461 }\r
2462 \r
2463 void CMyTaskBarIcon::Hide()\r
2464 {\r
2465     Show(false);\r
2466 }\r
2467 \r
2468 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)\r
2469 {\r
2470     Restore();\r
2471 }\r
2472 \r
2473 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)\r
2474 {\r
2475     Restore();\r
2476 }\r
2477 \r
2478 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)\r
2479 {\r
2480     // Since it's modal, get the main window to do it\r
2481     wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_MENUOPTIONSOPTIONS);\r
2482     pframeMain->GetEventHandler()->AddPendingEvent(event2);\r
2483 }\r
2484 \r
2485 void CMyTaskBarIcon::Restore()\r
2486 {\r
2487     pframeMain->Show();\r
2488     wxIconizeEvent event(0, false);\r
2489     pframeMain->GetEventHandler()->AddPendingEvent(event);\r
2490     pframeMain->Iconize(false);\r
2491     pframeMain->Raise();\r
2492 }\r
2493 \r
2494 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)\r
2495 {\r
2496     GenerateBitcoins(event.IsChecked());\r
2497 }\r
2498 \r
2499 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)\r
2500 {\r
2501     event.Check(fGenerateBitcoins);\r
2502 }\r
2503 \r
2504 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)\r
2505 {\r
2506     pframeMain->Close(true);\r
2507 }\r
2508 \r
2509 void CMyTaskBarIcon::UpdateTooltip()\r
2510 {\r
2511     if (IsIconInstalled())\r
2512         Show(true);\r
2513 }\r
2514 \r
2515 wxMenu* CMyTaskBarIcon::CreatePopupMenu()\r
2516 {\r
2517     wxMenu* pmenu = new wxMenu;\r
2518     pmenu->Append(ID_TASKBAR_RESTORE, "&Open Bitcoin");\r
2519     pmenu->Append(ID_TASKBAR_OPTIONS, "O&ptions...");\r
2520     pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, "&Generate Coins")->Check(fGenerateBitcoins);\r
2521 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu\r
2522     pmenu->AppendSeparator();\r
2523     pmenu->Append(ID_TASKBAR_EXIT, "E&xit");\r
2524 #endif\r
2525     return pmenu;\r
2526 }\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 // CMyApp\r
2540 //\r
2541 \r
2542 // Define a new application\r
2543 class CMyApp: public wxApp\r
2544 {\r
2545   public:\r
2546     CMyApp(){};\r
2547     ~CMyApp(){};\r
2548     bool OnInit();\r
2549     bool OnInit2();\r
2550     int OnExit();\r
2551 \r
2552     // 2nd-level exception handling: we get all the exceptions occurring in any\r
2553     // event handler here\r
2554     virtual bool OnExceptionInMainLoop();\r
2555 \r
2556     // 3rd, and final, level exception handling: whenever an unhandled\r
2557     // exception is caught, this function is called\r
2558     virtual void OnUnhandledException();\r
2559 \r
2560     // and now for something different: this function is called in case of a\r
2561     // crash (e.g. dereferencing null pointer, division by 0, ...)\r
2562     virtual void OnFatalException();\r
2563 };\r
2564 \r
2565 IMPLEMENT_APP(CMyApp)\r
2566 \r
2567 bool CMyApp::OnInit()\r
2568 {\r
2569     bool fRet = false;\r
2570     try\r
2571     {\r
2572         fRet = OnInit2();\r
2573     }\r
2574     catch (std::exception& e) {\r
2575         PrintException(&e, "OnInit()");\r
2576     } catch (...) {\r
2577         PrintException(NULL, "OnInit()");\r
2578     }\r
2579     if (!fRet)\r
2580         Shutdown(NULL);\r
2581     return fRet;\r
2582 }\r
2583 \r
2584 bool CMyApp::OnInit2()\r
2585 {\r
2586 #ifdef _MSC_VER\r
2587     // Turn off microsoft heap dump noise for now\r
2588     _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);\r
2589     _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));\r
2590 #endif\r
2591 #if defined(__WXMSW__) && defined(__WXDEBUG__)\r
2592     // Disable malfunctioning wxWidgets debug assertion\r
2593     g_isPainting = 10000;\r
2594 #endif\r
2595     wxImage::AddHandler(new wxPNGHandler);\r
2596 #ifdef __WXMSW__\r
2597     SetAppName("Bitcoin");\r
2598 #else\r
2599     SetAppName("bitcoin");\r
2600     umask(077);\r
2601 #endif\r
2602 \r
2603     //\r
2604     // Parameters\r
2605     //\r
2606     if (argc > 1 && argv[1][0] != '-' && argv[1][0] != '/')\r
2607     {\r
2608         int ret = CommandLineRPC(argc, argv);\r
2609         exit(ret);\r
2610     }\r
2611 \r
2612     ParseParameters(argc, argv);\r
2613     if (mapArgs.count("-?") || mapArgs.count("--help"))\r
2614     {\r
2615 #ifdef __WXMSW__\r
2616         string strUsage =\r
2617             "Usage: bitcoin [options]\t\t\t\t\t\t\n"\r
2618             "Options:\n"\r
2619             "  -gen\t\t  Generate coins\n"\r
2620             "  -gen=0\t\t  Don't generate coins\n"\r
2621             "  -min\t\t  Start minimized\n"\r
2622             "  -datadir=<dir>\t  Specify data directory\n"\r
2623             "  -proxy=<ip:port>\t  Connect through socks4 proxy\n"\r
2624             "  -addnode=<ip>\t  Add a node to connect to\n"\r
2625             "  -connect=<ip>\t  Connect only to the specified node\n"\r
2626             "  -?\t\t  This help message\n";\r
2627         wxMessageBox(strUsage, "Bitcoin", wxOK);\r
2628 #else\r
2629         string strUsage =\r
2630             "Usage: bitcoin [options]\n"\r
2631             "Options:\n"\r
2632             "  -gen              Generate coins\n"\r
2633             "  -gen=0            Don't generate coins\n"\r
2634             "  -min              Start minimized\n"\r
2635             "  -datadir=<dir>    Specify data directory\n"\r
2636             "  -proxy=<ip:port>  Connect through socks4 proxy\n"\r
2637             "  -addnode=<ip>     Add a node to connect to\n"\r
2638             "  -connect=<ip>     Connect only to the specified node\n"\r
2639             "  -?                This help message\n";\r
2640         fprintf(stderr, "%s", strUsage.c_str());\r
2641 #endif\r
2642         return false;\r
2643     }\r
2644 \r
2645     if (mapArgs.count("-datadir"))\r
2646         strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir));\r
2647 \r
2648     if (mapArgs.count("-debug"))\r
2649         fDebug = true;\r
2650 \r
2651     if (mapArgs.count("-printtodebugger"))\r
2652         fPrintToDebugger = true;\r
2653 \r
2654     if (mapArgs.count("-daemon") || mapArgs.count("-d"))\r
2655     {\r
2656         fDaemon = true;\r
2657         /// todo: need to fork\r
2658         ///  should it fork after the bind/single instance stuff?\r
2659     }\r
2660 \r
2661     if (!fDebug && !pszSetDataDir[0])\r
2662         ShrinkDebugFile();\r
2663     printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");\r
2664     printf("Bitcoin version %d%s, OS version %s\n", VERSION, pszSubVer, ((string)wxGetOsDescription()).c_str());\r
2665 \r
2666     if (mapArgs.count("-loadblockindextest"))\r
2667     {\r
2668         CTxDB txdb("r");\r
2669         txdb.LoadBlockIndex();\r
2670         PrintBlockTree();\r
2671         return false;\r
2672     }\r
2673 \r
2674     //\r
2675     // Limit to single instance per user\r
2676     // Required to protect the database files if we're going to keep deleting log.*\r
2677     //\r
2678 #ifdef __WXMSW__\r
2679     // todo: wxSingleInstanceChecker wasn't working on Linux, never deleted its lock file\r
2680     //  maybe should go by whether successfully bind port 8333 instead\r
2681     wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH");\r
2682     for (int i = 0; i < strMutexName.size(); i++)\r
2683         if (!isalnum(strMutexName[i]))\r
2684             strMutexName[i] = '.';\r
2685     wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);\r
2686     if (psingleinstancechecker->IsAnotherRunning())\r
2687     {\r
2688         printf("Existing instance found\n");\r
2689         unsigned int nStart = GetTime();\r
2690         loop\r
2691         {\r
2692             // TODO: find out how to do this in Linux, or replace with wxWidgets commands\r
2693             // Show the previous instance and exit\r
2694             HWND hwndPrev = FindWindow("wxWindowClassNR", "Bitcoin");\r
2695             if (hwndPrev)\r
2696             {\r
2697                 if (IsIconic(hwndPrev))\r
2698                     ShowWindow(hwndPrev, SW_RESTORE);\r
2699                 SetForegroundWindow(hwndPrev);\r
2700                 return false;\r
2701             }\r
2702 \r
2703             if (GetTime() > nStart + 60)\r
2704                 return false;\r
2705 \r
2706             // Resume this instance if the other exits\r
2707             delete psingleinstancechecker;\r
2708             Sleep(1000);\r
2709             psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);\r
2710             if (!psingleinstancechecker->IsAnotherRunning())\r
2711                 break;\r
2712         }\r
2713     }\r
2714 #endif\r
2715 \r
2716     // Bind to the port early so we can tell if another instance is already running.\r
2717     // This is a backup to wxSingleInstanceChecker, which doesn't work on Linux.\r
2718     string strErrors;\r
2719     if (!BindListenPort(strErrors))\r
2720     {\r
2721         wxMessageBox(strErrors, "Bitcoin");\r
2722         return false;\r
2723     }\r
2724 \r
2725     //\r
2726     // Load data files\r
2727     //\r
2728     bool fFirstRun;\r
2729     strErrors = "";\r
2730     int64 nStart;\r
2731 \r
2732     printf("Loading addresses...\n");\r
2733     nStart = GetTimeMillis();\r
2734     if (!LoadAddresses())\r
2735         strErrors += "Error loading addr.dat      \n";\r
2736     printf(" addresses   %15"PRI64d"ms\n", GetTimeMillis() - nStart);\r
2737 \r
2738     printf("Loading block index...\n");\r
2739     nStart = GetTimeMillis();\r
2740     if (!LoadBlockIndex())\r
2741         strErrors += "Error loading blkindex.dat      \n";\r
2742     printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);\r
2743 \r
2744     printf("Loading wallet...\n");\r
2745     nStart = GetTimeMillis();\r
2746     if (!LoadWallet(fFirstRun))\r
2747         strErrors += "Error loading wallet.dat      \n";\r
2748     printf(" wallet      %15"PRI64d"ms\n", GetTimeMillis() - nStart);\r
2749 \r
2750     printf("Done loading\n");\r
2751 \r
2752         //// debug print\r
2753         printf("mapBlockIndex.size() = %d\n",   mapBlockIndex.size());\r
2754         printf("nBestHeight = %d\n",            nBestHeight);\r
2755         printf("mapKeys.size() = %d\n",         mapKeys.size());\r
2756         printf("mapPubKeys.size() = %d\n",      mapPubKeys.size());\r
2757         printf("mapWallet.size() = %d\n",       mapWallet.size());\r
2758         printf("mapAddressBook.size() = %d\n",  mapAddressBook.size());\r
2759 \r
2760     if (!strErrors.empty())\r
2761     {\r
2762         wxMessageBox(strErrors, "Bitcoin");\r
2763         return false;\r
2764     }\r
2765 \r
2766     // Add wallet transactions that aren't already in a block to mapTransactions\r
2767     ReacceptWalletTransactions();\r
2768 \r
2769     //\r
2770     // Parameters\r
2771     //\r
2772     if (mapArgs.count("-printblockindex") || mapArgs.count("-printblocktree"))\r
2773     {\r
2774         PrintBlockTree();\r
2775         return false;\r
2776     }\r
2777 \r
2778     if (mapArgs.count("-printblock"))\r
2779     {\r
2780         string strMatch = mapArgs["-printblock"];\r
2781         int nFound = 0;\r
2782         for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)\r
2783         {\r
2784             uint256 hash = (*mi).first;\r
2785             if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0)\r
2786             {\r
2787                 CBlockIndex* pindex = (*mi).second;\r
2788                 CBlock block;\r
2789                 block.ReadFromDisk(pindex);\r
2790                 block.BuildMerkleTree();\r
2791                 block.print();\r
2792                 printf("\n");\r
2793                 nFound++;\r
2794             }\r
2795         }\r
2796         if (nFound == 0)\r
2797             printf("No blocks matching %s were found\n", strMatch.c_str());\r
2798         return false;\r
2799     }\r
2800 \r
2801     if (mapArgs.count("-gen"))\r
2802     {\r
2803         if (mapArgs["-gen"].empty())\r
2804             fGenerateBitcoins = true;\r
2805         else\r
2806             fGenerateBitcoins = (atoi(mapArgs["-gen"].c_str()) != 0);\r
2807     }\r
2808 \r
2809     if (mapArgs.count("-proxy"))\r
2810     {\r
2811         fUseProxy = true;\r
2812         addrProxy = CAddress(mapArgs["-proxy"]);\r
2813         if (!addrProxy.IsValid())\r
2814         {\r
2815             wxMessageBox("Invalid -proxy address", "Bitcoin");\r
2816             return false;\r
2817         }\r
2818     }\r
2819 \r
2820     if (mapArgs.count("-addnode"))\r
2821     {\r
2822         foreach(string strAddr, mapMultiArgs["-addnode"])\r
2823         {\r
2824             CAddress addr(strAddr, NODE_NETWORK);\r
2825             addr.nTime = 0; // so it won't relay unless successfully connected\r
2826             if (addr.IsValid())\r
2827                 AddAddress(addr);\r
2828         }\r
2829     }\r
2830 \r
2831     //\r
2832     // Create the main frame window\r
2833     //\r
2834     if (!fDaemon)\r
2835     {\r
2836         pframeMain = new CMainFrame(NULL);\r
2837         if (mapArgs.count("-min"))\r
2838             pframeMain->Iconize(true);\r
2839         pframeMain->Show(true);  // have to show first to get taskbar button to hide\r
2840         if (fMinimizeToTray && pframeMain->IsIconized())\r
2841             fClosedToTray = true;\r
2842         pframeMain->Show(!fClosedToTray);\r
2843         ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);\r
2844 \r
2845         CreateThread(ThreadDelayedRepaint, NULL);\r
2846     }\r
2847 \r
2848     if (!CheckDiskSpace())\r
2849         return false;\r
2850 \r
2851     RandAddSeedPerfmon();\r
2852 \r
2853     if (!CreateThread(StartNode, NULL))\r
2854         wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin");\r
2855 \r
2856     if (mapArgs.count("-server") || fDaemon)\r
2857         CreateThread(ThreadRPCServer, NULL);\r
2858 \r
2859     if (fFirstRun)\r
2860         SetStartOnSystemStartup(true);\r
2861 \r
2862 \r
2863     //\r
2864     // Tests\r
2865     //\r
2866 #ifdef __WXMSW__\r
2867     if (argc >= 2 && stricmp(argv[1], "-send") == 0)\r
2868 #else\r
2869     if (argc >= 2 && strcmp(argv[1], "-send") == 0)\r
2870 #endif\r
2871     {\r
2872         int64 nValue = 1;\r
2873         if (argc >= 3)\r
2874             ParseMoney(argv[2], nValue);\r
2875 \r
2876         string strAddress;\r
2877         if (argc >= 4)\r
2878             strAddress = argv[3];\r
2879         CAddress addr(strAddress);\r
2880 \r
2881         CWalletTx wtx;\r
2882         wtx.mapValue["to"] = strAddress;\r
2883         wtx.mapValue["from"] = addrLocalHost.ToString();\r
2884         wtx.mapValue["message"] = "command line send";\r
2885 \r
2886         // Send to IP address\r
2887         CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx);\r
2888         if (!pdialog->ShowModal())\r
2889             return false;\r
2890     }\r
2891 \r
2892     return true;\r
2893 }\r
2894 \r
2895 int CMyApp::OnExit()\r
2896 {\r
2897     Shutdown(NULL);\r
2898     return wxApp::OnExit();\r
2899 }\r
2900 \r
2901 bool CMyApp::OnExceptionInMainLoop()\r
2902 {\r
2903     try\r
2904     {\r
2905         throw;\r
2906     }\r
2907     catch (std::exception& e)\r
2908     {\r
2909         PrintException(&e, "CMyApp::OnExceptionInMainLoop()");\r
2910         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());\r
2911         Sleep(1000);\r
2912         throw;\r
2913     }\r
2914     catch (...)\r
2915     {\r
2916         PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");\r
2917         wxLogWarning("Unknown exception");\r
2918         Sleep(1000);\r
2919         throw;\r
2920     }\r
2921 \r
2922     return true;\r
2923 }\r
2924 \r
2925 void CMyApp::OnUnhandledException()\r
2926 {\r
2927     // this shows how we may let some exception propagate uncaught\r
2928     try\r
2929     {\r
2930         throw;\r
2931     }\r
2932     catch (std::exception& e)\r
2933     {\r
2934         PrintException(&e, "CMyApp::OnUnhandledException()");\r
2935         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());\r
2936         Sleep(1000);\r
2937         throw;\r
2938     }\r
2939     catch (...)\r
2940     {\r
2941         PrintException(NULL, "CMyApp::OnUnhandledException()");\r
2942         wxLogWarning("Unknown exception");\r
2943         Sleep(1000);\r
2944         throw;\r
2945     }\r
2946 }\r
2947 \r
2948 void CMyApp::OnFatalException()\r
2949 {\r
2950     wxMessageBox("Program has crashed and will terminate.  ", "Bitcoin", wxOK | wxICON_ERROR);\r
2951 }\r
2952 \r
2953 \r
2954 \r
2955 \r
2956 \r
2957 #ifdef __WXMSW__\r
2958 typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);\r
2959 \r
2960 string MyGetSpecialFolderPath(int nFolder, bool fCreate)\r
2961 {\r
2962     char pszPath[MAX_PATH+100] = "";\r
2963 \r
2964     // SHGetSpecialFolderPath is not usually available on NT 4.0\r
2965     HMODULE hShell32 = LoadLibrary("shell32.dll");\r
2966     if (hShell32)\r
2967     {\r
2968         PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath =\r
2969             (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA");\r
2970         if (pSHGetSpecialFolderPath)\r
2971             (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate);\r
2972         FreeModule(hShell32);\r
2973     }\r
2974 \r
2975     // Backup option\r
2976     if (pszPath[0] == '\0')\r
2977     {\r
2978         if (nFolder == CSIDL_STARTUP)\r
2979         {\r
2980             strcpy(pszPath, getenv("USERPROFILE"));\r
2981             strcat(pszPath, "\\Start Menu\\Programs\\Startup");\r
2982         }\r
2983         else if (nFolder == CSIDL_APPDATA)\r
2984         {\r
2985             strcpy(pszPath, getenv("APPDATA"));\r
2986         }\r
2987     }\r
2988 \r
2989     return pszPath;\r
2990 }\r
2991 \r
2992 string StartupShortcutPath()\r
2993 {\r
2994     return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";\r
2995 }\r
2996 \r
2997 bool GetStartOnSystemStartup()\r
2998 {\r
2999     return wxFileExists(StartupShortcutPath());\r
3000 }\r
3001 \r
3002 void SetStartOnSystemStartup(bool fAutoStart)\r
3003 {\r
3004     // If the shortcut exists already, remove it for updating\r
3005     remove(StartupShortcutPath().c_str());\r
3006 \r
3007     if (fAutoStart)\r
3008     {\r
3009         CoInitialize(NULL);\r
3010 \r
3011         // Get a pointer to the IShellLink interface.\r
3012         IShellLink* psl = NULL;\r
3013         HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,\r
3014                                 CLSCTX_INPROC_SERVER, IID_IShellLink,\r
3015                                 reinterpret_cast<void**>(&psl));\r
3016 \r
3017         if (SUCCEEDED(hres))\r
3018         {\r
3019             // Get the current executable path\r
3020             char pszExePath[MAX_PATH];\r
3021             GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));\r
3022 \r
3023             // Set the path to the shortcut target\r
3024             psl->SetPath(pszExePath);\r
3025             PathRemoveFileSpec(pszExePath);\r
3026             psl->SetWorkingDirectory(pszExePath);\r
3027             psl->SetShowCmd(SW_SHOWMINNOACTIVE);\r
3028 \r
3029             // Query IShellLink for the IPersistFile interface for\r
3030             // saving the shortcut in persistent storage.\r
3031             IPersistFile* ppf = NULL;\r
3032             hres = psl->QueryInterface(IID_IPersistFile,\r
3033                                        reinterpret_cast<void**>(&ppf));\r
3034             if (SUCCEEDED(hres))\r
3035             {\r
3036                 WCHAR pwsz[MAX_PATH];\r
3037                 // Ensure that the string is ANSI.\r
3038                 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);\r
3039                 // Save the link by calling IPersistFile::Save.\r
3040                 hres = ppf->Save(pwsz, TRUE);\r
3041                 ppf->Release();\r
3042             }\r
3043             psl->Release();\r
3044         }\r
3045         CoUninitialize();\r
3046     }\r
3047 }\r
3048 #else\r
3049 bool GetStartOnSystemStartup() { return false; }\r
3050 void SetStartOnSystemStartup(bool fAutoStart) { }\r
3051 #endif\r
3052 \r
3053 \r
3054 \r
3055 \r
3056 \r
3057 \r
3058 \r
3059 \r
3060 \r
3061 \r