command line and JSON-RPC first draft, requires Boost 1.35 or higher for boost::asio,
[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 void ThreadRequestProductDetails(void* parg);\r
11 void ThreadRandSendTest(void* parg);\r
12 bool GetStartOnSystemStartup();\r
13 void SetStartOnSystemStartup(bool fAutoStart);\r
14 \r
15 \r
16 \r
17 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)\r
18 DEFINE_EVENT_TYPE(wxEVT_REPLY1)\r
19 DEFINE_EVENT_TYPE(wxEVT_REPLY2)\r
20 DEFINE_EVENT_TYPE(wxEVT_REPLY3)\r
21 \r
22 CMainFrame* pframeMain = NULL;\r
23 CMyTaskBarIcon* ptaskbaricon = NULL;\r
24 bool fRandSendTest = false;\r
25 void RandSend();\r
26 extern int g_isPainting;\r
27 bool fClosedToTray = false;\r
28 \r
29 // Settings\r
30 int fShowGenerated = true;\r
31 int fMinimizeToTray = true;\r
32 int fMinimizeOnClose = true;\r
33 \r
34 \r
35 \r
36 \r
37 \r
38 \r
39 \r
40 //////////////////////////////////////////////////////////////////////////////\r
41 //\r
42 // Util\r
43 //\r
44 \r
45 void HandleCtrlA(wxKeyEvent& event)\r
46 {\r
47     // Ctrl-a select all\r
48     wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();\r
49     if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')\r
50         textCtrl->SetSelection(-1, -1);\r
51     event.Skip();\r
52 }\r
53 \r
54 bool Is24HourTime()\r
55 {\r
56     //char pszHourFormat[256];\r
57     //pszHourFormat[0] = '\0';\r
58     //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);\r
59     //return (pszHourFormat[0] != '0');\r
60     return true;\r
61 }\r
62 \r
63 string DateStr(int64 nTime)\r
64 {\r
65     // Can only be used safely here in the UI\r
66     return (string)wxDateTime((time_t)nTime).FormatDate();\r
67 }\r
68 \r
69 string DateTimeStr(int64 nTime)\r
70 {\r
71     // Can only be used safely here in the UI\r
72     wxDateTime datetime((time_t)nTime);\r
73     if (Is24HourTime())\r
74         return (string)datetime.Format("%x %H:%M");\r
75     else\r
76         return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");\r
77 }\r
78 \r
79 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)\r
80 {\r
81     // Helper to simplify access to listctrl\r
82     wxListItem item;\r
83     item.m_itemId = nIndex;\r
84     item.m_col = nColumn;\r
85     item.m_mask = wxLIST_MASK_TEXT;\r
86     if (!listCtrl->GetItem(item))\r
87         return "";\r
88     return item.GetText();\r
89 }\r
90 \r
91 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)\r
92 {\r
93     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);\r
94     listCtrl->SetItem(nIndex, 1, str1);\r
95     return nIndex;\r
96 }\r
97 \r
98 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)\r
99 {\r
100     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);\r
101     listCtrl->SetItem(nIndex, 1, str1);\r
102     listCtrl->SetItem(nIndex, 2, str2);\r
103     listCtrl->SetItem(nIndex, 3, str3);\r
104     listCtrl->SetItem(nIndex, 4, str4);\r
105     return nIndex;\r
106 }\r
107 \r
108 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)\r
109 {\r
110     int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);\r
111     listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);\r
112     listCtrl->SetItem(nIndex, 1, str1);\r
113     listCtrl->SetItem(nIndex, 2, str2);\r
114     listCtrl->SetItem(nIndex, 3, str3);\r
115     listCtrl->SetItem(nIndex, 4, str4);\r
116     return nIndex;\r
117 }\r
118 \r
119 void SetSelection(wxListCtrl* listCtrl, int nIndex)\r
120 {\r
121     int nSize = listCtrl->GetItemCount();\r
122     long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);\r
123     for (int i = 0; i < nSize; i++)\r
124         listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);\r
125 }\r
126 \r
127 int GetSelection(wxListCtrl* listCtrl)\r
128 {\r
129     int nSize = listCtrl->GetItemCount();\r
130     for (int i = 0; i < nSize; i++)\r
131         if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))\r
132             return i;\r
133     return -1;\r
134 }\r
135 \r
136 string HtmlEscape(const char* psz, bool fMultiLine=false)\r
137 {\r
138     int len = 0;\r
139     for (const char* p = psz; *p; p++)\r
140     {\r
141              if (*p == '<') len += 4;\r
142         else if (*p == '>') len += 4;\r
143         else if (*p == '&') len += 5;\r
144         else if (*p == '"') len += 6;\r
145         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;\r
146         else if (*p == '\n' && fMultiLine) len += 5;\r
147         else\r
148             len++;\r
149     }\r
150     string str;\r
151     str.reserve(len);\r
152     for (const char* p = psz; *p; p++)\r
153     {\r
154              if (*p == '<') str += "&lt;";\r
155         else if (*p == '>') str += "&gt;";\r
156         else if (*p == '&') str += "&amp;";\r
157         else if (*p == '"') str += "&quot;";\r
158         else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += "&nbsp;";\r
159         else if (*p == '\n' && fMultiLine) str += "<br>\n";\r
160         else\r
161             str += *p;\r
162     }\r
163     return str;\r
164 }\r
165 \r
166 string HtmlEscape(const string& str, bool fMultiLine=false)\r
167 {\r
168     return HtmlEscape(str.c_str(), fMultiLine);\r
169 }\r
170 \r
171 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)\r
172 {\r
173     *pnRet = wxMessageBox(message, caption, style, parent, x, y);\r
174     *pfDone = true;\r
175 }\r
176 \r
177 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)\r
178 {\r
179     if (fDaemon)\r
180     {\r
181         printf("wxMessageBox %s: %s\n", caption.c_str(), message.c_str());\r
182         return wxOK;\r
183     }\r
184 \r
185 #ifdef __WXMSW__\r
186     return wxMessageBox(message, caption, style, parent, x, y);\r
187 #else\r
188     if (wxThread::IsMain())\r
189     {\r
190         return wxMessageBox(message, caption, style, parent, x, y);\r
191     }\r
192     else\r
193     {\r
194         int nRet = 0;\r
195         bool fDone = false;\r
196         UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));\r
197         while (!fDone)\r
198             Sleep(100);\r
199         return nRet;\r
200     }\r
201 #endif\r
202 }\r
203 \r
204 \r
205 \r
206 \r
207 \r
208 \r
209 \r
210 \r
211 \r
212 \r
213 //////////////////////////////////////////////////////////////////////////////\r
214 //\r
215 // Custom events\r
216 //\r
217 // If this code gets used again, it should be replaced with something like UIThreadCall\r
218 \r
219 set<void*> setCallbackAvailable;\r
220 CCriticalSection cs_setCallbackAvailable;\r
221 \r
222 void AddCallbackAvailable(void* p)\r
223 {\r
224     CRITICAL_BLOCK(cs_setCallbackAvailable)\r
225         setCallbackAvailable.insert(p);\r
226 }\r
227 \r
228 void RemoveCallbackAvailable(void* p)\r
229 {\r
230     CRITICAL_BLOCK(cs_setCallbackAvailable)\r
231         setCallbackAvailable.erase(p);\r
232 }\r
233 \r
234 bool IsCallbackAvailable(void* p)\r
235 {\r
236     CRITICAL_BLOCK(cs_setCallbackAvailable)\r
237         return setCallbackAvailable.count(p);\r
238     return false;\r
239 }\r
240 \r
241 template<typename T>\r
242 void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T pbeginIn, const T pendIn)\r
243 {\r
244     // Need to rewrite with something like UIThreadCall\r
245     // I'm tired of maintaining this hack that's only called by unfinished unused code.\r
246     assert(("Unimplemented", 0));\r
247     //if (!pevthandler)\r
248     //    return;\r
249     //\r
250     //const char* pbegin = (pendIn != pbeginIn) ? &pbeginIn[0] : NULL;\r
251     //const char* pend = pbegin + (pendIn - pbeginIn) * sizeof(pbeginIn[0]);\r
252     //wxCommandEvent event(nEventID);\r
253     //wxString strData(wxChar(0), (pend - pbegin) / sizeof(wxChar) + 1);\r
254     //memcpy(&strData[0], pbegin, pend - pbegin);\r
255     //event.SetString(strData);\r
256     //event.SetInt(pend - pbegin);\r
257     //\r
258     //pevthandler->AddPendingEvent(event);\r
259 }\r
260 \r
261 template<class T>\r
262 void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T& obj)\r
263 {\r
264     CDataStream ss;\r
265     ss << obj;\r
266     AddPendingCustomEvent(pevthandler, nEventID, ss.begin(), ss.end());\r
267 }\r
268 \r
269 void AddPendingReplyEvent1(void* pevthandler, CDataStream& vRecv)\r
270 {\r
271     if (IsCallbackAvailable(pevthandler))\r
272         AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY1, vRecv.begin(), vRecv.end());\r
273 }\r
274 \r
275 void AddPendingReplyEvent2(void* pevthandler, CDataStream& vRecv)\r
276 {\r
277     if (IsCallbackAvailable(pevthandler))\r
278         AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY2, vRecv.begin(), vRecv.end());\r
279 }\r
280 \r
281 void AddPendingReplyEvent3(void* pevthandler, CDataStream& vRecv)\r
282 {\r
283     if (IsCallbackAvailable(pevthandler))\r
284         AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY3, vRecv.begin(), vRecv.end());\r
285 }\r
286 \r
287 CDataStream GetStreamFromEvent(const wxCommandEvent& event)\r
288 {\r
289     wxString strData = event.GetString();\r
290     const char* pszBegin = strData.c_str();\r
291     return CDataStream(pszBegin, pszBegin + event.GetInt(), SER_NETWORK);\r
292 }\r
293 \r
294 \r
295 \r
296 \r
297 \r
298 \r
299 \r
300 //////////////////////////////////////////////////////////////////////////////\r
301 //\r
302 // CMainFrame\r
303 //\r
304 \r
305 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)\r
306 {\r
307     Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);\r
308 \r
309     // Init\r
310     fRefreshListCtrl = false;\r
311     fRefreshListCtrlRunning = false;\r
312     fOnSetFocusAddress = false;\r
313     fRefresh = false;\r
314     m_choiceFilter->SetSelection(0);\r
315     double dResize = 1.0;\r
316 #ifdef __WXMSW__\r
317     SetIcon(wxICON(bitcoin));\r
318 #else\r
319     SetIcon(bitcoin16_xpm);\r
320     wxFont fontTmp = m_staticText41->GetFont();\r
321     fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);\r
322     m_staticTextBalance->SetFont(fontTmp);\r
323     m_staticTextBalance->SetSize(140, 17);\r
324     // & underlines don't work on the toolbar buttons on gtk\r
325     m_toolBar->ClearTools();\r
326     m_toolBar->AddTool(wxID_BUTTONSEND, "Send Coins", wxBitmap(send20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);\r
327     m_toolBar->AddTool(wxID_BUTTONRECEIVE, "Address Book", wxBitmap(addressbook20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);\r
328     m_toolBar->Realize();\r
329     // resize to fit ubuntu's huge default font\r
330     dResize = 1.20;\r
331     SetSize(dResize * GetSize().GetWidth(), 1.1 * GetSize().GetHeight());\r
332 #endif\r
333     m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + "  ");\r
334     m_listCtrl->SetFocus();\r
335     ptaskbaricon = new CMyTaskBarIcon();\r
336 \r
337     // Init column headers\r
338     int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;\r
339     if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))\r
340         nDateWidth += 12;\r
341     m_listCtrl->InsertColumn(0, "",             wxLIST_FORMAT_LEFT,  dResize * 0);\r
342     m_listCtrl->InsertColumn(1, "",             wxLIST_FORMAT_LEFT,  dResize * 0);\r
343     m_listCtrl->InsertColumn(2, "Status",       wxLIST_FORMAT_LEFT,  dResize * 110);\r
344     m_listCtrl->InsertColumn(3, "Date",         wxLIST_FORMAT_LEFT,  dResize * nDateWidth);\r
345     m_listCtrl->InsertColumn(4, "Description",  wxLIST_FORMAT_LEFT,  dResize * 409 - nDateWidth);\r
346     m_listCtrl->InsertColumn(5, "Debit",        wxLIST_FORMAT_RIGHT, dResize * 79);\r
347     m_listCtrl->InsertColumn(6, "Credit",       wxLIST_FORMAT_RIGHT, dResize * 79);\r
348 \r
349     //m_listCtrlProductsSent->InsertColumn(0, "Category",      wxLIST_FORMAT_LEFT,  100);\r
350     //m_listCtrlProductsSent->InsertColumn(1, "Title",         wxLIST_FORMAT_LEFT,  100);\r
351     //m_listCtrlProductsSent->InsertColumn(2, "Description",   wxLIST_FORMAT_LEFT,  100);\r
352     //m_listCtrlProductsSent->InsertColumn(3, "Price",         wxLIST_FORMAT_LEFT,  100);\r
353     //m_listCtrlProductsSent->InsertColumn(4, "",              wxLIST_FORMAT_LEFT,  100);\r
354 \r
355     //m_listCtrlOrdersSent->InsertColumn(0, "Time",          wxLIST_FORMAT_LEFT,  100);\r
356     //m_listCtrlOrdersSent->InsertColumn(1, "Price",         wxLIST_FORMAT_LEFT,  100);\r
357     //m_listCtrlOrdersSent->InsertColumn(2, "",              wxLIST_FORMAT_LEFT,  100);\r
358     //m_listCtrlOrdersSent->InsertColumn(3, "",              wxLIST_FORMAT_LEFT,  100);\r
359     //m_listCtrlOrdersSent->InsertColumn(4, "",              wxLIST_FORMAT_LEFT,  100);\r
360 \r
361     //m_listCtrlOrdersReceived->InsertColumn(0, "Time",            wxLIST_FORMAT_LEFT,  100);\r
362     //m_listCtrlOrdersReceived->InsertColumn(1, "Price",           wxLIST_FORMAT_LEFT,  100);\r
363     //m_listCtrlOrdersReceived->InsertColumn(2, "Payment Status",  wxLIST_FORMAT_LEFT,  100);\r
364     //m_listCtrlOrdersReceived->InsertColumn(3, "",                wxLIST_FORMAT_LEFT,  100);\r
365     //m_listCtrlOrdersReceived->InsertColumn(4, "",                wxLIST_FORMAT_LEFT,  100);\r
366 \r
367     // Init status bar\r
368     int pnWidths[3] = { -100, 88, 290 };\r
369 #ifndef __WXMSW__\r
370     pnWidths[1] = pnWidths[1] * 1.1 * dResize;\r
371     pnWidths[2] = pnWidths[2] * 1.1 * dResize;\r
372 #endif\r
373     m_statusBar->SetFieldsCount(3, pnWidths);\r
374 \r
375     // Fill your address text box\r
376     vector<unsigned char> vchPubKey;\r
377     if (CWalletDB("r").ReadDefaultKey(vchPubKey))\r
378         m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));\r
379 \r
380     // Fill listctrl with wallet transactions\r
381     RefreshListCtrl();\r
382 }\r
383 \r
384 CMainFrame::~CMainFrame()\r
385 {\r
386     pframeMain = NULL;\r
387     delete ptaskbaricon;\r
388     ptaskbaricon = NULL;\r
389 }\r
390 \r
391 void ExitTimeout(void* parg)\r
392 {\r
393 #ifdef __WXMSW__\r
394     Sleep(5000);\r
395     ExitProcess(0);\r
396 #endif\r
397 }\r
398 \r
399 void Shutdown(void* parg)\r
400 {\r
401     static CCriticalSection cs_Shutdown;\r
402     static bool fTaken;\r
403     bool fFirstThread;\r
404     CRITICAL_BLOCK(cs_Shutdown)\r
405     {\r
406         fFirstThread = !fTaken;\r
407         fTaken = true;\r
408     }\r
409     static bool fExit;\r
410     if (fFirstThread)\r
411     {\r
412         fShutdown = true;\r
413         nTransactionsUpdated++;\r
414         DBFlush(false);\r
415         StopNode();\r
416         DBFlush(true);\r
417         CreateThread(ExitTimeout, NULL);\r
418         Sleep(50);\r
419         printf("Bitcoin exiting\n\n");\r
420         fExit = true;\r
421         exit(0);\r
422     }\r
423     else\r
424     {\r
425         while (!fExit)\r
426             Sleep(500);\r
427         Sleep(100);\r
428         ExitThread(0);\r
429     }\r
430 }\r
431 \r
432 void CMainFrame::OnClose(wxCloseEvent& event)\r
433 {\r
434     if (fMinimizeOnClose && event.CanVeto() && !IsIconized())\r
435     {\r
436         // Divert close to minimize\r
437         event.Veto();\r
438         fClosedToTray = true;\r
439         Iconize(true);\r
440     }\r
441     else\r
442     {\r
443         Destroy();\r
444         CreateThread(Shutdown, NULL);\r
445     }\r
446 }\r
447 \r
448 void CMainFrame::OnIconize(wxIconizeEvent& event)\r
449 {\r
450     // Hide the task bar button when minimized.\r
451     // Event is sent when the frame is minimized or restored.\r
452     // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way\r
453     // to get rid of the deprecated warning.  Just ignore it.\r
454     if (!event.Iconized())\r
455         fClosedToTray = false;\r
456 #ifndef __WXMSW__\r
457     // Tray is not reliable on Linux gnome\r
458     fClosedToTray = false;\r
459 #endif\r
460     if (fMinimizeToTray && event.Iconized())\r
461         fClosedToTray = true;\r
462     Show(!fClosedToTray);\r
463     ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);\r
464 }\r
465 \r
466 void CMainFrame::OnMouseEvents(wxMouseEvent& event)\r
467 {\r
468     RandAddSeed();\r
469     RAND_add(&event.m_x, sizeof(event.m_x), 0.25);\r
470     RAND_add(&event.m_y, sizeof(event.m_y), 0.25);\r
471 }\r
472 \r
473 void CMainFrame::OnListColBeginDrag(wxListEvent& event)\r
474 {\r
475      // Hidden columns not resizeable\r
476      if (event.GetColumn() <= 1 && !fDebug)\r
477         event.Veto();\r
478 }\r
479 \r
480 int CMainFrame::GetSortIndex(const string& strSort)\r
481 {\r
482 #ifdef __WXMSW__\r
483     return 0;\r
484 #else\r
485     // The wx generic listctrl implementation used on GTK doesn't sort,\r
486     // so we have to do it ourselves.  Remember, we sort in reverse order.\r
487     // In the wx generic implementation, they store the list of items\r
488     // in a vector, so indexed lookups are fast, but inserts are slower\r
489     // the closer they are to the top.\r
490     int low = 0;\r
491     int high = m_listCtrl->GetItemCount();\r
492     while (low < high)\r
493     {\r
494         int mid = low + ((high - low) / 2);\r
495         if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)\r
496             high = mid;\r
497         else\r
498             low = mid + 1;\r
499     }\r
500     return low;\r
501 #endif\r
502 }\r
503 \r
504 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
505 {\r
506     string str0 = strSort;\r
507     long nData = *(long*)&hashKey;\r
508 \r
509     // Find item\r
510     if (!fNew && nIndex == -1)\r
511     {\r
512         while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)\r
513             if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())\r
514                 break;\r
515     }\r
516 \r
517     // fNew is for blind insert, only use if you're sure it's new\r
518     if (fNew || nIndex == -1)\r
519     {\r
520         nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), str0);\r
521     }\r
522     else\r
523     {\r
524         // If sort key changed, must delete and reinsert to make it relocate\r
525         if (GetItemText(m_listCtrl, nIndex, 0) != str0)\r
526         {\r
527             m_listCtrl->DeleteItem(nIndex);\r
528             nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), str0);\r
529         }\r
530     }\r
531 \r
532     m_listCtrl->SetItem(nIndex, 1, hashKey.ToString());\r
533     m_listCtrl->SetItem(nIndex, 2, str2);\r
534     m_listCtrl->SetItem(nIndex, 3, str3);\r
535     m_listCtrl->SetItem(nIndex, 4, str4);\r
536     m_listCtrl->SetItem(nIndex, 5, str5);\r
537     m_listCtrl->SetItem(nIndex, 6, str6);\r
538     m_listCtrl->SetItemData(nIndex, nData);\r
539 }\r
540 \r
541 bool CMainFrame::DeleteLine(uint256 hashKey)\r
542 {\r
543     long nData = *(long*)&hashKey;\r
544 \r
545     // Find item\r
546     int nIndex = -1;\r
547     while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)\r
548         if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())\r
549             break;\r
550 \r
551     if (nIndex != -1)\r
552         m_listCtrl->DeleteItem(nIndex);\r
553 \r
554     return nIndex != -1;\r
555 }\r
556 \r
557 string FormatTxStatus(const CWalletTx& wtx)\r
558 {\r
559     // Status\r
560     if (!wtx.IsFinal())\r
561     {\r
562         if (wtx.nLockTime < 500000000)\r
563             return strprintf("Open for %d blocks", nBestHeight - wtx.nLockTime);\r
564         else\r
565             return strprintf("Open until %s", DateTimeStr(wtx.nLockTime).c_str());\r
566     }\r
567     else\r
568     {\r
569         int nDepth = wtx.GetDepthInMainChain();\r
570         if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)\r
571             return strprintf("%d/offline?", nDepth);\r
572         else if (nDepth < 6)\r
573             return strprintf("%d/unconfirmed", nDepth);\r
574         else\r
575             return strprintf("%d confirmations", nDepth);\r
576     }\r
577 }\r
578 \r
579 string SingleLine(const string& strIn)\r
580 {\r
581     string strOut;\r
582     bool fOneSpace = false;\r
583     foreach(int c, strIn)\r
584     {\r
585         if (isspace(c))\r
586         {\r
587             fOneSpace = true;\r
588         }\r
589         else if (c > ' ')\r
590         {\r
591             if (fOneSpace && !strOut.empty())\r
592                 strOut += ' ';\r
593             strOut += c;\r
594             fOneSpace = false;\r
595         }\r
596     }\r
597     return strOut;\r
598 }\r
599 \r
600 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)\r
601 {\r
602     int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();\r
603     int64 nCredit = wtx.GetCredit();\r
604     int64 nDebit = wtx.GetDebit();\r
605     int64 nNet = nCredit - nDebit;\r
606     uint256 hash = wtx.GetHash();\r
607     string strStatus = FormatTxStatus(wtx);\r
608     map<string, string> mapValue = wtx.mapValue;\r
609     wtx.nLinesDisplayed = 1;\r
610     nListViewUpdated++;\r
611 \r
612     // Filter\r
613     if (wtx.IsCoinBase())\r
614     {\r
615         // Don't show generated coin until confirmed by at least one block after it\r
616         // so we don't get the user's hopes up until it looks like it's probably accepted.\r
617         //\r
618         // It is not an error when generated blocks are not accepted.  By design,\r
619         // some percentage of blocks, like 10% or more, will end up not accepted.\r
620         // This is the normal mechanism by which the network copes with latency.\r
621         //\r
622         // We display regular transactions right away before any confirmation\r
623         // because they can always get into some block eventually.  Generated coins\r
624         // are special because if their block is not accepted, they are not valid.\r
625         //\r
626         if (wtx.GetDepthInMainChain() < 2)\r
627         {\r
628             wtx.nLinesDisplayed = 0;\r
629             return false;\r
630         }\r
631 \r
632         // View->Show Generated\r
633         if (!fShowGenerated)\r
634             return false;\r
635     }\r
636 \r
637     // Find the block the tx is in\r
638     CBlockIndex* pindex = NULL;\r
639     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);\r
640     if (mi != mapBlockIndex.end())\r
641         pindex = (*mi).second;\r
642 \r
643     // Sort order, unrecorded transactions sort to the top\r
644     string strSort = strprintf("%010d-%01d-%010u",\r
645         (pindex ? pindex->nHeight : INT_MAX),\r
646         (wtx.IsCoinBase() ? 1 : 0),\r
647         wtx.nTimeReceived);\r
648 \r
649     // Insert line\r
650     if (nNet > 0 || wtx.IsCoinBase())\r
651     {\r
652         //\r
653         // Credit\r
654         //\r
655         string strDescription;\r
656 \r
657         if (wtx.IsCoinBase())\r
658         {\r
659             // Coinbase\r
660             strDescription = "Generated";\r
661             if (nCredit == 0)\r
662             {\r
663                 int64 nUnmatured = 0;\r
664                 foreach(const CTxOut& txout, wtx.vout)\r
665                     nUnmatured += txout.GetCredit();\r
666                 if (wtx.IsInMainChain())\r
667                 {\r
668                     strDescription = strprintf("Generated (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());\r
669 \r
670                     // Check if the block was requested by anyone\r
671                     if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)\r
672                         strDescription = "Generated - Warning: This block was not received by any other nodes and will probably not be accepted!";\r
673                 }\r
674                 else\r
675                 {\r
676                     strDescription = "Generated (not accepted)";\r
677                 }\r
678             }\r
679         }\r
680         else if (!mapValue["from"].empty() || !mapValue["message"].empty())\r
681         {\r
682             // Online transaction\r
683             if (!mapValue["from"].empty())\r
684                 strDescription += "From: " + mapValue["from"];\r
685             if (!mapValue["message"].empty())\r
686             {\r
687                 if (!strDescription.empty())\r
688                     strDescription += " - ";\r
689                 strDescription += mapValue["message"];\r
690             }\r
691         }\r
692         else\r
693         {\r
694             // Offline transaction\r
695             foreach(const CTxOut& txout, wtx.vout)\r
696             {\r
697                 if (txout.IsMine())\r
698                 {\r
699                     vector<unsigned char> vchPubKey;\r
700                     if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))\r
701                     {\r
702                         CRITICAL_BLOCK(cs_mapAddressBook)\r
703                         {\r
704                             string strAddress = PubKeyToAddress(vchPubKey);\r
705                             if (mapAddressBook.count(strAddress))\r
706                             {\r
707                                 //strDescription += "Received payment to ";\r
708                                 //strDescription += "Received with address ";\r
709                                 strDescription += "From: unknown, To: ";\r
710                                 strDescription += strAddress;\r
711                                 /// The labeling feature is just too confusing, so I hid it\r
712                                 /// by putting it at the end where it runs off the screen.\r
713                                 /// It can still be seen by widening the column, or in the\r
714                                 /// details dialog.\r
715                                 if (!mapAddressBook[strAddress].empty())\r
716                                     strDescription += " (" + mapAddressBook[strAddress] + ")";\r
717                             }\r
718                         }\r
719                     }\r
720                     break;\r
721                 }\r
722             }\r
723         }\r
724 \r
725         InsertLine(fNew, nIndex, hash, strSort,\r
726                    strStatus,\r
727                    nTime ? DateTimeStr(nTime) : "",\r
728                    SingleLine(strDescription),\r
729                    "",\r
730                    FormatMoney(nNet, true));\r
731     }\r
732     else\r
733     {\r
734         bool fAllFromMe = true;\r
735         foreach(const CTxIn& txin, wtx.vin)\r
736             fAllFromMe = fAllFromMe && txin.IsMine();\r
737 \r
738         bool fAllToMe = true;\r
739         foreach(const CTxOut& txout, wtx.vout)\r
740             fAllToMe = fAllToMe && txout.IsMine();\r
741 \r
742         if (fAllFromMe && fAllToMe)\r
743         {\r
744             // Payment to self\r
745             int64 nValue = wtx.vout[0].nValue;\r
746             InsertLine(fNew, nIndex, hash, strSort,\r
747                        strStatus,\r
748                        nTime ? DateTimeStr(nTime) : "",\r
749                        "Payment to yourself",\r
750                        "",\r
751                        "");\r
752             /// issue: can't tell which is the payment and which is the change anymore\r
753             //           FormatMoney(nNet - nValue, true),\r
754             //           FormatMoney(nValue, true));\r
755         }\r
756         else if (fAllFromMe)\r
757         {\r
758             //\r
759             // Debit\r
760             //\r
761             int64 nTxFee = nDebit - wtx.GetValueOut();\r
762             wtx.nLinesDisplayed = 0;\r
763             for (int nOut = 0; nOut < wtx.vout.size(); nOut++)\r
764             {\r
765                 const CTxOut& txout = wtx.vout[nOut];\r
766                 if (txout.IsMine())\r
767                     continue;\r
768 \r
769                 string strAddress;\r
770                 if (!mapValue["to"].empty())\r
771                 {\r
772                     // Online transaction\r
773                     strAddress = mapValue["to"];\r
774                 }\r
775                 else\r
776                 {\r
777                     // Offline transaction\r
778                     uint160 hash160;\r
779                     if (ExtractHash160(txout.scriptPubKey, hash160))\r
780                         strAddress = Hash160ToAddress(hash160);\r
781                 }\r
782 \r
783                 string strDescription = "To: ";\r
784                 CRITICAL_BLOCK(cs_mapAddressBook)\r
785                     if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())\r
786                         strDescription += mapAddressBook[strAddress] + " ";\r
787                 strDescription += strAddress;\r
788                 if (!mapValue["message"].empty())\r
789                 {\r
790                     if (!strDescription.empty())\r
791                         strDescription += " - ";\r
792                     strDescription += mapValue["message"];\r
793                 }\r
794 \r
795                 int64 nValue = txout.nValue;\r
796                 if (nOut == 0 && nTxFee > 0)\r
797                     nValue += nTxFee;\r
798 \r
799                 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),\r
800                            strStatus,\r
801                            nTime ? DateTimeStr(nTime) : "",\r
802                            SingleLine(strDescription),\r
803                            FormatMoney(-nValue, true),\r
804                            "");\r
805                 wtx.nLinesDisplayed++;\r
806             }\r
807         }\r
808         else\r
809         {\r
810             //\r
811             // Mixed debit transaction, can't break down payees\r
812             //\r
813             bool fAllMine = true;\r
814             foreach(const CTxOut& txout, wtx.vout)\r
815                 fAllMine = fAllMine && txout.IsMine();\r
816             foreach(const CTxIn& txin, wtx.vin)\r
817                 fAllMine = fAllMine && txin.IsMine();\r
818 \r
819             InsertLine(fNew, nIndex, hash, strSort,\r
820                        strStatus,\r
821                        nTime ? DateTimeStr(nTime) : "",\r
822                        "",\r
823                        FormatMoney(nNet, true),\r
824                        "");\r
825         }\r
826     }\r
827 \r
828     return true;\r
829 }\r
830 \r
831 void CMainFrame::RefreshListCtrl()\r
832 {\r
833     fRefreshListCtrl = true;\r
834     ::wxWakeUpIdle();\r
835 }\r
836 \r
837 void CMainFrame::OnIdle(wxIdleEvent& event)\r
838 {\r
839     if (fRefreshListCtrl)\r
840     {\r
841         // Collect list of wallet transactions and sort newest first\r
842         bool fEntered = false;\r
843         vector<pair<unsigned int, uint256> > vSorted;\r
844         TRY_CRITICAL_BLOCK(cs_mapWallet)\r
845         {\r
846             printf("RefreshListCtrl starting\n");\r
847             fEntered = true;\r
848             fRefreshListCtrl = false;\r
849             vWalletUpdated.clear();\r
850 \r
851             // Do the newest transactions first\r
852             vSorted.reserve(mapWallet.size());\r
853             for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\r
854             {\r
855                 const CWalletTx& wtx = (*it).second;\r
856                 unsigned int nTime = UINT_MAX - wtx.GetTxTime();\r
857                 vSorted.push_back(make_pair(nTime, (*it).first));\r
858             }\r
859             m_listCtrl->DeleteAllItems();\r
860         }\r
861         if (!fEntered)\r
862             return;\r
863 \r
864         sort(vSorted.begin(), vSorted.end());\r
865 \r
866         // Fill list control\r
867         for (int i = 0; i < vSorted.size();)\r
868         {\r
869             if (fShutdown)\r
870                 return;\r
871             bool fEntered = false;\r
872             TRY_CRITICAL_BLOCK(cs_mapWallet)\r
873             {\r
874                 fEntered = true;\r
875                 uint256& hash = vSorted[i++].second;\r
876                 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);\r
877                 if (mi != mapWallet.end())\r
878                     InsertTransaction((*mi).second, true);\r
879             }\r
880             if (!fEntered || i == 100 || i % 500 == 0)\r
881                 wxYield();\r
882         }\r
883 \r
884         printf("RefreshListCtrl done\n");\r
885 \r
886         // Update transaction total display\r
887         MainFrameRepaint();\r
888     }\r
889     else\r
890     {\r
891         // Check for time updates\r
892         static int64 nLastTime;\r
893         if (GetTime() > nLastTime + 30)\r
894         {\r
895             TRY_CRITICAL_BLOCK(cs_mapWallet)\r
896             {\r
897                 nLastTime = GetTime();\r
898                 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\r
899                 {\r
900                     CWalletTx& wtx = (*it).second;\r
901                     if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())\r
902                         InsertTransaction(wtx, false);\r
903                 }\r
904             }\r
905         }\r
906     }\r
907 }\r
908 \r
909 void CMainFrame::RefreshStatusColumn()\r
910 {\r
911     static int nLastTop;\r
912     static CBlockIndex* pindexLastBest;\r
913     static unsigned int nLastRefreshed;\r
914 \r
915     int nTop = max((int)m_listCtrl->GetTopItem(), 0);\r
916     if (nTop == nLastTop && pindexLastBest == pindexBest)\r
917         return;\r
918 \r
919     TRY_CRITICAL_BLOCK(cs_mapWallet)\r
920     {\r
921         int nStart = nTop;\r
922         int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());\r
923 \r
924         if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)\r
925         {\r
926             // If no updates, only need to do the part that moved onto the screen\r
927             if (nStart >= nLastTop && nStart < nLastTop + 100)\r
928                 nStart = nLastTop + 100;\r
929             if (nEnd >= nLastTop && nEnd < nLastTop + 100)\r
930                 nEnd = nLastTop;\r
931         }\r
932         nLastTop = nTop;\r
933         pindexLastBest = pindexBest;\r
934         nLastRefreshed = nListViewUpdated;\r
935 \r
936         for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)\r
937         {\r
938             uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));\r
939             map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);\r
940             if (mi == mapWallet.end())\r
941             {\r
942                 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");\r
943                 continue;\r
944             }\r
945             CWalletTx& wtx = (*mi).second;\r
946             if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)\r
947             {\r
948                 if (!InsertTransaction(wtx, false, nIndex))\r
949                     m_listCtrl->DeleteItem(nIndex--);\r
950             }\r
951             else\r
952                 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));\r
953         }\r
954     }\r
955 }\r
956 \r
957 void CMainFrame::OnPaint(wxPaintEvent& event)\r
958 {\r
959     if (fRefresh)\r
960     {\r
961         fRefresh = false;\r
962         Refresh();\r
963     }\r
964     event.Skip();\r
965 }\r
966 \r
967 \r
968 unsigned int nNeedRepaint = 0;\r
969 unsigned int nLastRepaint = 0;\r
970 int64 nLastRepaintTime = 0;\r
971 int64 nRepaintInterval = 500;\r
972 \r
973 void ThreadDelayedRepaint(void* parg)\r
974 {\r
975     while (!fShutdown)\r
976     {\r
977         if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)\r
978         {\r
979             nLastRepaint = nNeedRepaint;\r
980             if (pframeMain)\r
981             {\r
982                 printf("DelayedRepaint\n");\r
983                 wxPaintEvent event;\r
984                 pframeMain->fRefresh = true;\r
985                 pframeMain->GetEventHandler()->AddPendingEvent(event);\r
986             }\r
987         }\r
988         Sleep(nRepaintInterval);\r
989     }\r
990 }\r
991 \r
992 void MainFrameRepaint()\r
993 {\r
994     // This is called by network code that shouldn't access pframeMain\r
995     // directly because it could still be running after the UI is closed.\r
996     if (pframeMain)\r
997     {\r
998         // Don't repaint too often\r
999         static int64 nLastRepaintRequest;\r
1000         if (GetTimeMillis() - nLastRepaintRequest < 100)\r
1001         {\r
1002             nNeedRepaint++;\r
1003             return;\r
1004         }\r
1005         nLastRepaintRequest = GetTimeMillis();\r
1006 \r
1007         printf("MainFrameRepaint\n");\r
1008         wxPaintEvent event;\r
1009         pframeMain->fRefresh = true;\r
1010         pframeMain->GetEventHandler()->AddPendingEvent(event);\r
1011     }\r
1012 }\r
1013 \r
1014 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)\r
1015 {\r
1016     if (ptaskbaricon)\r
1017         ptaskbaricon->UpdateTooltip();\r
1018 \r
1019     //\r
1020     // Slower stuff\r
1021     //\r
1022     static int nTransactionCount;\r
1023     bool fPaintedBalance = false;\r
1024     if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)\r
1025     {\r
1026         nLastRepaint = nNeedRepaint;\r
1027         nLastRepaintTime = GetTimeMillis();\r
1028 \r
1029         // Update listctrl contents\r
1030         if (!vWalletUpdated.empty())\r
1031         {\r
1032             TRY_CRITICAL_BLOCK(cs_mapWallet)\r
1033             {\r
1034                 string strTop;\r
1035                 if (m_listCtrl->GetItemCount())\r
1036                     strTop = (string)m_listCtrl->GetItemText(0);\r
1037                 foreach(uint256 hash, vWalletUpdated)\r
1038                 {\r
1039                     map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);\r
1040                     if (mi != mapWallet.end())\r
1041                         InsertTransaction((*mi).second, false);\r
1042                 }\r
1043                 vWalletUpdated.clear();\r
1044                 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))\r
1045                     m_listCtrl->ScrollList(0, INT_MIN/2);\r
1046             }\r
1047         }\r
1048 \r
1049         // Balance total\r
1050         TRY_CRITICAL_BLOCK(cs_mapWallet)\r
1051         {\r
1052             fPaintedBalance = true;\r
1053             m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + "  ");\r
1054 \r
1055             // Count hidden and multi-line transactions\r
1056             nTransactionCount = 0;\r
1057             for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\r
1058             {\r
1059                 CWalletTx& wtx = (*it).second;\r
1060                 nTransactionCount += wtx.nLinesDisplayed;\r
1061             }\r
1062         }\r
1063     }\r
1064     if (!vWalletUpdated.empty() || !fPaintedBalance)\r
1065         nNeedRepaint++;\r
1066 \r
1067     // Update status column of visible items only\r
1068     RefreshStatusColumn();\r
1069 \r
1070     // Update status bar\r
1071     string strGen = "";\r
1072     if (fGenerateBitcoins)\r
1073         strGen = "    Generating";\r
1074     if (fGenerateBitcoins && vNodes.empty())\r
1075         strGen = "(not connected)";\r
1076     m_statusBar->SetStatusText(strGen, 1);\r
1077 \r
1078     string strStatus = strprintf("     %d connections     %d blocks     %d transactions", vNodes.size(), nBestHeight + 1, nTransactionCount);\r
1079     m_statusBar->SetStatusText(strStatus, 2);\r
1080 \r
1081     if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)\r
1082         m_statusBar->SetStatusText("     ERROR: ThreadSocketHandler has stopped", 0);\r
1083 \r
1084     // Pass through to listctrl to actually do the paint, we're just hooking the message\r
1085     m_listCtrl->Disconnect(wxEVT_PAINT, (wxObjectEventFunction)NULL, NULL, this);\r
1086     m_listCtrl->GetEventHandler()->ProcessEvent(event);\r
1087     m_listCtrl->Connect(wxEVT_PAINT, wxPaintEventHandler(CMainFrame::OnPaintListCtrl), NULL, this);\r
1088 }\r
1089 \r
1090 \r
1091 void UIThreadCall(boost::function0<void> fn)\r
1092 {\r
1093     // Call this with a function object created with bind.\r
1094     // bind needs all parameters to match the function's expected types\r
1095     // and all default parameters specified.  Some examples:\r
1096     //  UIThreadCall(bind(wxBell));\r
1097     //  UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));\r
1098     //  UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));\r
1099     if (pframeMain)\r
1100     {\r
1101         wxCommandEvent event(wxEVT_UITHREADCALL);\r
1102         event.SetClientData((void*)new boost::function0<void>(fn));\r
1103         pframeMain->GetEventHandler()->AddPendingEvent(event);\r
1104     }\r
1105 }\r
1106 \r
1107 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)\r
1108 {\r
1109     boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();\r
1110     (*pfn)();\r
1111     delete pfn;\r
1112 }\r
1113 \r
1114 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)\r
1115 {\r
1116     // File->Exit\r
1117     Close(true);\r
1118 }\r
1119 \r
1120 void CMainFrame::OnMenuViewShowGenerated(wxCommandEvent& event)\r
1121 {\r
1122     // View->Show Generated\r
1123     fShowGenerated = event.IsChecked();\r
1124     CWalletDB().WriteSetting("fShowGenerated", fShowGenerated);\r
1125     RefreshListCtrl();\r
1126 }\r
1127 \r
1128 void CMainFrame::OnUpdateUIViewShowGenerated(wxUpdateUIEvent& event)\r
1129 {\r
1130     event.Check(fShowGenerated);\r
1131 }\r
1132 \r
1133 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)\r
1134 {\r
1135     // Options->Generate Coins\r
1136     GenerateBitcoins(event.IsChecked());\r
1137 }\r
1138 \r
1139 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)\r
1140 {\r
1141     event.Check(fGenerateBitcoins);\r
1142 }\r
1143 \r
1144 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)\r
1145 {\r
1146     // Options->Change Your Address\r
1147     OnButtonChange(event);\r
1148 }\r
1149 \r
1150 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)\r
1151 {\r
1152     // Options->Options\r
1153     COptionsDialog dialog(this);\r
1154     dialog.ShowModal();\r
1155 }\r
1156 \r
1157 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)\r
1158 {\r
1159     // Help->About\r
1160     CAboutDialog dialog(this);\r
1161     dialog.ShowModal();\r
1162 }\r
1163 \r
1164 void CMainFrame::OnButtonSend(wxCommandEvent& event)\r
1165 {\r
1166     // Toolbar: Send\r
1167     CSendDialog dialog(this);\r
1168     dialog.ShowModal();\r
1169 }\r
1170 \r
1171 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)\r
1172 {\r
1173     // Toolbar: Address Book\r
1174     CAddressBookDialog dialogAddr(this, "", false);\r
1175     if (dialogAddr.ShowModal() == 2)\r
1176     {\r
1177         // Send\r
1178         CSendDialog dialogSend(this, dialogAddr.GetAddress());\r
1179         dialogSend.ShowModal();\r
1180     }\r
1181 }\r
1182 \r
1183 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)\r
1184 {\r
1185     // Automatically select-all when entering window\r
1186     m_textCtrlAddress->SetSelection(-1, -1);\r
1187     fOnSetFocusAddress = true;\r
1188     event.Skip();\r
1189 }\r
1190 \r
1191 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)\r
1192 {\r
1193     if (fOnSetFocusAddress)\r
1194         m_textCtrlAddress->SetSelection(-1, -1);\r
1195     fOnSetFocusAddress = false;\r
1196     event.Skip();\r
1197 }\r
1198 \r
1199 void CMainFrame::OnButtonCopy(wxCommandEvent& event)\r
1200 {\r
1201     // Copy address box to clipboard\r
1202     if (wxTheClipboard->Open())\r
1203     {\r
1204         wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));\r
1205         wxTheClipboard->Close();\r
1206     }\r
1207 }\r
1208 \r
1209 void CMainFrame::OnButtonChange(wxCommandEvent& event)\r
1210 {\r
1211     CYourAddressDialog dialog(this, string(m_textCtrlAddress->GetValue()));\r
1212     if (!dialog.ShowModal())\r
1213         return;\r
1214     string strAddress = (string)dialog.GetAddress();\r
1215     if (strAddress != m_textCtrlAddress->GetValue())\r
1216     {\r
1217         uint160 hash160;\r
1218         if (!AddressToHash160(strAddress, hash160))\r
1219             return;\r
1220         if (!mapPubKeys.count(hash160))\r
1221             return;\r
1222         CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);\r
1223         m_textCtrlAddress->SetValue(strAddress);\r
1224     }\r
1225 }\r
1226 \r
1227 void CMainFrame::OnListItemActivated(wxListEvent& event)\r
1228 {\r
1229     uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));\r
1230     CWalletTx wtx;\r
1231     CRITICAL_BLOCK(cs_mapWallet)\r
1232     {\r
1233         map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);\r
1234         if (mi == mapWallet.end())\r
1235         {\r
1236             printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");\r
1237             return;\r
1238         }\r
1239         wtx = (*mi).second;\r
1240     }\r
1241     CTxDetailsDialog dialog(this, wtx);\r
1242     dialog.ShowModal();\r
1243     //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);\r
1244     //pdialog->Show();\r
1245 }\r
1246 \r
1247 void CMainFrame::OnListItemActivatedProductsSent(wxListEvent& event)\r
1248 {\r
1249     CProduct& product = *(CProduct*)event.GetItem().GetData();\r
1250     CEditProductDialog* pdialog = new CEditProductDialog(this);\r
1251     pdialog->SetProduct(product);\r
1252     pdialog->Show();\r
1253 }\r
1254 \r
1255 void CMainFrame::OnListItemActivatedOrdersSent(wxListEvent& event)\r
1256 {\r
1257     CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();\r
1258     CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, false);\r
1259     pdialog->Show();\r
1260 }\r
1261 \r
1262 void CMainFrame::OnListItemActivatedOrdersReceived(wxListEvent& event)\r
1263 {\r
1264     CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();\r
1265     CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, true);\r
1266     pdialog->Show();\r
1267 }\r
1268 \r
1269 \r
1270 \r
1271 \r
1272 \r
1273 \r
1274 \r
1275 //////////////////////////////////////////////////////////////////////////////\r
1276 //\r
1277 // CTxDetailsDialog\r
1278 //\r
1279 \r
1280 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)\r
1281 {\r
1282     CRITICAL_BLOCK(cs_mapAddressBook)\r
1283     {\r
1284         string strHTML;\r
1285         strHTML.reserve(4000);\r
1286         strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";\r
1287 \r
1288         int64 nTime = wtx.GetTxTime();\r
1289         int64 nCredit = wtx.GetCredit();\r
1290         int64 nDebit = wtx.GetDebit();\r
1291         int64 nNet = nCredit - nDebit;\r
1292 \r
1293 \r
1294 \r
1295         strHTML += "<b>Status:</b> " + FormatTxStatus(wtx);\r
1296         int nRequests = wtx.GetRequestCount();\r
1297         if (nRequests != -1)\r
1298         {\r
1299             if (nRequests == 0)\r
1300                 strHTML += ", has not been successfully broadcast yet";\r
1301             else if (nRequests == 1)\r
1302                 strHTML += strprintf(", broadcast through %d node", nRequests);\r
1303             else\r
1304                 strHTML += strprintf(", broadcast through %d nodes", nRequests);\r
1305         }\r
1306         strHTML += "<br>";\r
1307 \r
1308         strHTML += "<b>Date:</b> " + (nTime ? DateTimeStr(nTime) : "") + "<br>";\r
1309 \r
1310 \r
1311         //\r
1312         // From\r
1313         //\r
1314         if (wtx.IsCoinBase())\r
1315         {\r
1316             strHTML += "<b>Source:</b> Generated<br>";\r
1317         }\r
1318         else if (!wtx.mapValue["from"].empty())\r
1319         {\r
1320             // Online transaction\r
1321             if (!wtx.mapValue["from"].empty())\r
1322                 strHTML += "<b>From:</b> " + HtmlEscape(wtx.mapValue["from"]) + "<br>";\r
1323         }\r
1324         else\r
1325         {\r
1326             // Offline transaction\r
1327             if (nNet > 0)\r
1328             {\r
1329                 // Credit\r
1330                 foreach(const CTxOut& txout, wtx.vout)\r
1331                 {\r
1332                     if (txout.IsMine())\r
1333                     {\r
1334                         vector<unsigned char> vchPubKey;\r
1335                         if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))\r
1336                         {\r
1337                             string strAddress = PubKeyToAddress(vchPubKey);\r
1338                             if (mapAddressBook.count(strAddress))\r
1339                             {\r
1340                                 strHTML += "<b>From:</b> unknown<br>";\r
1341                                 strHTML += "<b>To:</b> ";\r
1342                                 strHTML += HtmlEscape(strAddress);\r
1343                                 if (!mapAddressBook[strAddress].empty())\r
1344                                     strHTML += " (yours, label: " + mapAddressBook[strAddress] + ")";\r
1345                                 else\r
1346                                     strHTML += " (yours)";\r
1347                                 strHTML += "<br>";\r
1348                             }\r
1349                         }\r
1350                         break;\r
1351                     }\r
1352                 }\r
1353             }\r
1354         }\r
1355 \r
1356 \r
1357         //\r
1358         // To\r
1359         //\r
1360         string strAddress;\r
1361         if (!wtx.mapValue["to"].empty())\r
1362         {\r
1363             // Online transaction\r
1364             strAddress = wtx.mapValue["to"];\r
1365             strHTML += "<b>To:</b> ";\r
1366             if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())\r
1367                 strHTML += mapAddressBook[strAddress] + " ";\r
1368             strHTML += HtmlEscape(strAddress) + "<br>";\r
1369         }\r
1370 \r
1371 \r
1372         //\r
1373         // Amount\r
1374         //\r
1375         if (wtx.IsCoinBase() && nCredit == 0)\r
1376         {\r
1377             //\r
1378             // Coinbase\r
1379             //\r
1380             int64 nUnmatured = 0;\r
1381             foreach(const CTxOut& txout, wtx.vout)\r
1382                 nUnmatured += txout.GetCredit();\r
1383             if (wtx.IsInMainChain())\r
1384                 strHTML += strprintf("<b>Credit:</b> (%s matures in %d more blocks)<br>", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());\r
1385             else\r
1386                 strHTML += "<b>Credit:</b> (not accepted)<br>";\r
1387         }\r
1388         else if (nNet > 0)\r
1389         {\r
1390             //\r
1391             // Credit\r
1392             //\r
1393             strHTML += "<b>Credit:</b> " + FormatMoney(nNet) + "<br>";\r
1394         }\r
1395         else\r
1396         {\r
1397             bool fAllFromMe = true;\r
1398             foreach(const CTxIn& txin, wtx.vin)\r
1399                 fAllFromMe = fAllFromMe && txin.IsMine();\r
1400 \r
1401             bool fAllToMe = true;\r
1402             foreach(const CTxOut& txout, wtx.vout)\r
1403                 fAllToMe = fAllToMe && txout.IsMine();\r
1404 \r
1405             if (fAllFromMe)\r
1406             {\r
1407                 //\r
1408                 // Debit\r
1409                 //\r
1410                 foreach(const CTxOut& txout, wtx.vout)\r
1411                 {\r
1412                     if (txout.IsMine())\r
1413                         continue;\r
1414 \r
1415                     if (wtx.mapValue["to"].empty())\r
1416                     {\r
1417                         // Offline transaction\r
1418                         uint160 hash160;\r
1419                         if (ExtractHash160(txout.scriptPubKey, hash160))\r
1420                         {\r
1421                             string strAddress = Hash160ToAddress(hash160);\r
1422                             strHTML += "<b>To:</b> ";\r
1423                             if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())\r
1424                                 strHTML += mapAddressBook[strAddress] + " ";\r
1425                             strHTML += strAddress;\r
1426                             strHTML += "<br>";\r
1427                         }\r
1428                     }\r
1429 \r
1430                     strHTML += "<b>Debit:</b> " + FormatMoney(-txout.nValue) + "<br>";\r
1431                 }\r
1432 \r
1433                 if (fAllToMe)\r
1434                 {\r
1435                     // Payment to self\r
1436                     /// issue: can't tell which is the payment and which is the change anymore\r
1437                     //int64 nValue = wtx.vout[0].nValue;\r
1438                     //strHTML += "<b>Debit:</b> " + FormatMoney(-nValue) + "<br>";\r
1439                     //strHTML += "<b>Credit:</b> " + FormatMoney(nValue) + "<br>";\r
1440                 }\r
1441 \r
1442                 int64 nTxFee = nDebit - wtx.GetValueOut();\r
1443                 if (nTxFee > 0)\r
1444                     strHTML += "<b>Transaction fee:</b> " + FormatMoney(-nTxFee) + "<br>";\r
1445             }\r
1446             else\r
1447             {\r
1448                 //\r
1449                 // Mixed debit transaction\r
1450                 //\r
1451                 foreach(const CTxIn& txin, wtx.vin)\r
1452                     if (txin.IsMine())\r
1453                         strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";\r
1454                 foreach(const CTxOut& txout, wtx.vout)\r
1455                     if (txout.IsMine())\r
1456                         strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";\r
1457             }\r
1458         }\r
1459 \r
1460         strHTML += "<b>Net amount:</b> " + FormatMoney(nNet, true) + "<br>";\r
1461 \r
1462 \r
1463         //\r
1464         // Message\r
1465         //\r
1466         if (!wtx.mapValue["message"].empty())\r
1467             strHTML += "<br><b>Message:</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";\r
1468 \r
1469         if (wtx.IsCoinBase())\r
1470             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
1471 \r
1472 \r
1473         //\r
1474         // Debug view\r
1475         //\r
1476         if (fDebug)\r
1477         {\r
1478             strHTML += "<hr><br>debug print<br><br>";\r
1479             foreach(const CTxIn& txin, wtx.vin)\r
1480                 if (txin.IsMine())\r
1481                     strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";\r
1482             foreach(const CTxOut& txout, wtx.vout)\r
1483                 if (txout.IsMine())\r
1484                     strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";\r
1485 \r
1486             strHTML += "<b>Inputs:</b><br>";\r
1487             CRITICAL_BLOCK(cs_mapWallet)\r
1488             {\r
1489                 foreach(const CTxIn& txin, wtx.vin)\r
1490                 {\r
1491                     COutPoint prevout = txin.prevout;\r
1492                     map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);\r
1493                     if (mi != mapWallet.end())\r
1494                     {\r
1495                         const CWalletTx& prev = (*mi).second;\r
1496                         if (prevout.n < prev.vout.size())\r
1497                         {\r
1498                             strHTML += HtmlEscape(prev.ToString(), true);\r
1499                             strHTML += " &nbsp;&nbsp; " + FormatTxStatus(prev) + ", ";\r
1500                             strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";\r
1501                         }\r
1502                     }\r
1503                 }\r
1504             }\r
1505 \r
1506             strHTML += "<br><hr><br><b>Transaction:</b><br>";\r
1507             strHTML += HtmlEscape(wtx.ToString(), true);\r
1508         }\r
1509 \r
1510 \r
1511 \r
1512         strHTML += "</font></html>";\r
1513         string(strHTML.begin(), strHTML.end()).swap(strHTML);\r
1514         m_htmlWin->SetPage(strHTML);\r
1515         m_buttonOK->SetFocus();\r
1516     }\r
1517 }\r
1518 \r
1519 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)\r
1520 {\r
1521     Close();\r
1522     //Destroy();\r
1523 }\r
1524 \r
1525 \r
1526 \r
1527 \r
1528 \r
1529 //////////////////////////////////////////////////////////////////////////////\r
1530 //\r
1531 // COptionsDialog\r
1532 //\r
1533 \r
1534 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)\r
1535 {\r
1536     // Set up list box of page choices\r
1537     m_listBox->Append("Main");\r
1538     //m_listBox->Append("Test 2");\r
1539     m_listBox->SetSelection(0);\r
1540     SelectPage(0);\r
1541 #ifndef __WXMSW__\r
1542     m_checkBoxMinimizeOnClose->SetLabel("&Minimize on close");\r
1543     m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet\r
1544 #endif\r
1545 \r
1546     // Init values\r
1547     m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));\r
1548     m_checkBoxLimitProcessors->SetValue(fLimitProcessors);\r
1549     m_spinCtrlLimitProcessors->Enable(fLimitProcessors);\r
1550     m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);\r
1551     int nProcessors = wxThread::GetCPUCount();\r
1552     if (nProcessors < 1)\r
1553         nProcessors = 999;\r
1554     m_spinCtrlLimitProcessors->SetRange(1, nProcessors);\r
1555     m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());\r
1556     m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);\r
1557     m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);\r
1558     m_checkBoxUseProxy->SetValue(fUseProxy);\r
1559     m_textCtrlProxyIP->Enable(fUseProxy);\r
1560     m_textCtrlProxyPort->Enable(fUseProxy);\r
1561     m_staticTextProxyIP->Enable(fUseProxy);\r
1562     m_staticTextProxyPort->Enable(fUseProxy);\r
1563     m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());\r
1564     m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());\r
1565 \r
1566     m_buttonOK->SetFocus();\r
1567 }\r
1568 \r
1569 void COptionsDialog::SelectPage(int nPage)\r
1570 {\r
1571     m_panelMain->Show(nPage == 0);\r
1572     m_panelTest2->Show(nPage == 1);\r
1573 \r
1574     m_scrolledWindow->Layout();\r
1575     m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);\r
1576 }\r
1577 \r
1578 void COptionsDialog::OnListBox(wxCommandEvent& event)\r
1579 {\r
1580     SelectPage(event.GetSelection());\r
1581 }\r
1582 \r
1583 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)\r
1584 {\r
1585     int64 nTmp = nTransactionFee;\r
1586     ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);\r
1587     m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));\r
1588 }\r
1589 \r
1590 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)\r
1591 {\r
1592     m_spinCtrlLimitProcessors->Enable(event.IsChecked());\r
1593 }\r
1594 \r
1595 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)\r
1596 {\r
1597     m_textCtrlProxyIP->Enable(event.IsChecked());\r
1598     m_textCtrlProxyPort->Enable(event.IsChecked());\r
1599     m_staticTextProxyIP->Enable(event.IsChecked());\r
1600     m_staticTextProxyPort->Enable(event.IsChecked());\r
1601 }\r
1602 \r
1603 CAddress COptionsDialog::GetProxyAddr()\r
1604 {\r
1605     // Be careful about byte order, addr.ip and addr.port are big endian\r
1606     CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());\r
1607     if (addr.ip == INADDR_NONE)\r
1608         addr.ip = addrProxy.ip;\r
1609     int nPort = atoi(m_textCtrlProxyPort->GetValue());\r
1610     addr.port = htons(nPort);\r
1611     if (nPort <= 0 || nPort > USHRT_MAX)\r
1612         addr.port = addrProxy.port;\r
1613     return addr;\r
1614 }\r
1615 \r
1616 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)\r
1617 {\r
1618     m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());\r
1619     m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());\r
1620 }\r
1621 \r
1622 \r
1623 void COptionsDialog::OnButtonOK(wxCommandEvent& event)\r
1624 {\r
1625     OnButtonApply(event);\r
1626     Close();\r
1627 }\r
1628 \r
1629 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)\r
1630 {\r
1631     Close();\r
1632 }\r
1633 \r
1634 void COptionsDialog::OnButtonApply(wxCommandEvent& event)\r
1635 {\r
1636     CWalletDB walletdb;\r
1637 \r
1638     int64 nPrevTransactionFee = nTransactionFee;\r
1639     if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)\r
1640         walletdb.WriteSetting("nTransactionFee", nTransactionFee);\r
1641 \r
1642     int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);\r
1643     if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())\r
1644     {\r
1645         fLimitProcessors = m_checkBoxLimitProcessors->GetValue();\r
1646         walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);\r
1647     }\r
1648     if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())\r
1649     {\r
1650         nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();\r
1651         walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);\r
1652     }\r
1653     if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)\r
1654         GenerateBitcoins(fGenerateBitcoins);\r
1655 \r
1656     if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())\r
1657     {\r
1658         fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();\r
1659         SetStartOnSystemStartup(fTmpStartOnSystemStartup);\r
1660     }\r
1661 \r
1662     if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())\r
1663     {\r
1664         fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();\r
1665         walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);\r
1666         ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);\r
1667     }\r
1668 \r
1669     if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())\r
1670     {\r
1671         fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();\r
1672         walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);\r
1673     }\r
1674 \r
1675     fUseProxy = m_checkBoxUseProxy->GetValue();\r
1676     walletdb.WriteSetting("fUseProxy", fUseProxy);\r
1677 \r
1678     addrProxy = GetProxyAddr();\r
1679     walletdb.WriteSetting("addrProxy", addrProxy);\r
1680 }\r
1681 \r
1682 \r
1683 \r
1684 \r
1685 \r
1686 \r
1687 //////////////////////////////////////////////////////////////////////////////\r
1688 //\r
1689 // CAboutDialog\r
1690 //\r
1691 \r
1692 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)\r
1693 {\r
1694     m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d beta", VERSION/100, VERSION%100));\r
1695 \r
1696 #if !wxUSE_UNICODE\r
1697     // Workaround until upgrade to wxWidgets supporting UTF-8\r
1698     // Hack to change the (c) character from UTF-8 back to ANSI\r
1699     wxString str = m_staticTextMain->GetLabel();\r
1700     if (str.Find('\xC2') != wxNOT_FOUND)\r
1701         str.Remove(str.Find('\xC2'), 1);\r
1702     m_staticTextMain->SetLabel(str);\r
1703 #endif\r
1704 #ifndef __WXMSW__\r
1705     // Resize on Linux to make the window fit the text.\r
1706     // The text was wrapped manually rather than using the Wrap setting because\r
1707     // the wrap would be too small on Linux and it can't be changed at this point.\r
1708     wxFont fontTmp = m_staticTextMain->GetFont();\r
1709     if (fontTmp.GetPointSize() > 8);\r
1710         fontTmp.SetPointSize(8);\r
1711     m_staticTextMain->SetFont(fontTmp);\r
1712     SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() - 4);\r
1713 #endif\r
1714 }\r
1715 \r
1716 void CAboutDialog::OnButtonOK(wxCommandEvent& event)\r
1717 {\r
1718     Close();\r
1719 }\r
1720 \r
1721 \r
1722 \r
1723 \r
1724 \r
1725 \r
1726 //////////////////////////////////////////////////////////////////////////////\r
1727 //\r
1728 // CSendDialog\r
1729 //\r
1730 \r
1731 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)\r
1732 {\r
1733     // Init\r
1734     m_textCtrlAddress->SetValue(strAddress);\r
1735     m_choiceTransferType->SetSelection(0);\r
1736     m_bitmapCheckMark->Show(false);\r
1737     fEnabledPrev = true;\r
1738     m_textCtrlAddress->SetFocus();\r
1739     //// todo: should add a display of your balance for convenience\r
1740 #ifndef __WXMSW__\r
1741     wxFont fontTmp = m_staticTextInstructions->GetFont();\r
1742     if (fontTmp.GetPointSize() > 9);\r
1743         fontTmp.SetPointSize(9);\r
1744     m_staticTextInstructions->SetFont(fontTmp);\r
1745     SetSize(725, 380);\r
1746 #endif\r
1747 \r
1748     // Set Icon\r
1749     wxIcon iconSend;\r
1750     iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));\r
1751     SetIcon(iconSend);\r
1752 \r
1753     wxCommandEvent event;\r
1754     OnTextAddress(event);\r
1755 \r
1756     // Fixup the tab order\r
1757     m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);\r
1758     m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);\r
1759     this->Layout();\r
1760 }\r
1761 \r
1762 void CSendDialog::OnTextAddress(wxCommandEvent& event)\r
1763 {\r
1764     // Check mark\r
1765     bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());\r
1766     m_bitmapCheckMark->Show(fBitcoinAddress);\r
1767 \r
1768     // Grey out message if bitcoin address\r
1769     bool fEnable = !fBitcoinAddress;\r
1770     m_staticTextFrom->Enable(fEnable);\r
1771     m_textCtrlFrom->Enable(fEnable);\r
1772     m_staticTextMessage->Enable(fEnable);\r
1773     m_textCtrlMessage->Enable(fEnable);\r
1774     m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));\r
1775     if (!fEnable && fEnabledPrev)\r
1776     {\r
1777         strFromSave    = m_textCtrlFrom->GetValue();\r
1778         strMessageSave = m_textCtrlMessage->GetValue();\r
1779         m_textCtrlFrom->SetValue("Will appear as \"From: Unknown\"");\r
1780         m_textCtrlMessage->SetValue("Can't include a message when sending to a Bitcoin address");\r
1781     }\r
1782     else if (fEnable && !fEnabledPrev)\r
1783     {\r
1784         m_textCtrlFrom->SetValue(strFromSave);\r
1785         m_textCtrlMessage->SetValue(strMessageSave);\r
1786     }\r
1787     fEnabledPrev = fEnable;\r
1788 }\r
1789 \r
1790 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)\r
1791 {\r
1792     // Reformat the amount\r
1793     if (m_textCtrlAmount->GetValue().Trim().empty())\r
1794         return;\r
1795     int64 nTmp;\r
1796     if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))\r
1797         m_textCtrlAmount->SetValue(FormatMoney(nTmp));\r
1798 }\r
1799 \r
1800 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)\r
1801 {\r
1802     // Open address book\r
1803     CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), true);\r
1804     if (dialog.ShowModal())\r
1805         m_textCtrlAddress->SetValue(dialog.GetAddress());\r
1806 }\r
1807 \r
1808 void CSendDialog::OnButtonPaste(wxCommandEvent& event)\r
1809 {\r
1810     // Copy clipboard to address box\r
1811     if (wxTheClipboard->Open())\r
1812     {\r
1813         if (wxTheClipboard->IsSupported(wxDF_TEXT))\r
1814         {\r
1815             wxTextDataObject data;\r
1816             wxTheClipboard->GetData(data);\r
1817             m_textCtrlAddress->SetValue(data.GetText());\r
1818         }\r
1819         wxTheClipboard->Close();\r
1820     }\r
1821 }\r
1822 \r
1823 void CSendDialog::OnButtonSend(wxCommandEvent& event)\r
1824 {\r
1825     CWalletTx wtx;\r
1826     string strAddress = (string)m_textCtrlAddress->GetValue();\r
1827 \r
1828     // Parse amount\r
1829     int64 nValue = 0;\r
1830     if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)\r
1831     {\r
1832         wxMessageBox("Error in amount  ", "Send Coins");\r
1833         return;\r
1834     }\r
1835     if (nValue > GetBalance())\r
1836     {\r
1837         wxMessageBox("Amount exceeds your balance  ", "Send Coins");\r
1838         return;\r
1839     }\r
1840     if (nValue + nTransactionFee > GetBalance())\r
1841     {\r
1842         wxMessageBox(string("Total exceeds your balance when the ") + FormatMoney(nTransactionFee) + " transaction fee is included  ", "Send Coins");\r
1843         return;\r
1844     }\r
1845 \r
1846     // Parse bitcoin address\r
1847     uint160 hash160;\r
1848     bool fBitcoinAddress = AddressToHash160(strAddress, hash160);\r
1849 \r
1850     if (fBitcoinAddress)\r
1851     {\r
1852         // Send to bitcoin address\r
1853         CScript scriptPubKey;\r
1854         scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;\r
1855 \r
1856         string strError = SendMoney(scriptPubKey, nValue, wtx);\r
1857         if (strError != "")\r
1858             wxMessageBox(strError + "  ", "Sending...");\r
1859         else\r
1860             wxMessageBox("Payment sent  ", "Sending...");\r
1861     }\r
1862     else\r
1863     {\r
1864         // Parse IP address\r
1865         CAddress addr(strAddress);\r
1866         if (!addr.IsValid())\r
1867         {\r
1868             wxMessageBox("Invalid address  ", "Send Coins");\r
1869             return;\r
1870         }\r
1871 \r
1872         // Message\r
1873         wtx.mapValue["to"] = strAddress;\r
1874         wtx.mapValue["from"] = m_textCtrlFrom->GetValue();\r
1875         wtx.mapValue["message"] = m_textCtrlMessage->GetValue();\r
1876 \r
1877         // Send to IP address\r
1878         CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);\r
1879         if (!pdialog->ShowModal())\r
1880             return;\r
1881     }\r
1882 \r
1883     CRITICAL_BLOCK(cs_mapAddressBook)\r
1884         if (!mapAddressBook.count(strAddress))\r
1885             SetAddressBookName(strAddress, "");\r
1886 \r
1887     EndModal(true);\r
1888 }\r
1889 \r
1890 void CSendDialog::OnButtonCancel(wxCommandEvent& event)\r
1891 {\r
1892     // Cancel\r
1893     EndModal(false);\r
1894 }\r
1895 \r
1896 \r
1897 \r
1898 \r
1899 \r
1900 \r
1901 //////////////////////////////////////////////////////////////////////////////\r
1902 //\r
1903 // CSendingDialog\r
1904 //\r
1905 \r
1906 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
1907 {\r
1908     addr = addrIn;\r
1909     nPrice = nPriceIn;\r
1910     wtx = wtxIn;\r
1911     start = wxDateTime::UNow();\r
1912     memset(pszStatus, 0, sizeof(pszStatus));\r
1913     fCanCancel = true;\r
1914     fAbort = false;\r
1915     fSuccess = false;\r
1916     fUIDone = false;\r
1917     fWorkDone = false;\r
1918 #ifndef __WXMSW__\r
1919     SetSize(1.2 * GetSize().GetWidth(), 1.05 * GetSize().GetHeight());\r
1920 #endif\r
1921 \r
1922     SetTitle(strprintf("Sending %s to %s", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));\r
1923     m_textCtrlStatus->SetValue("");\r
1924 \r
1925     CreateThread(SendingDialogStartTransfer, this);\r
1926 }\r
1927 \r
1928 CSendingDialog::~CSendingDialog()\r
1929 {\r
1930     printf("~CSendingDialog()\n");\r
1931 }\r
1932 \r
1933 void CSendingDialog::Close()\r
1934 {\r
1935     // Last one out turn out the lights.\r
1936     // fWorkDone signals that work side is done and UI thread should call destroy.\r
1937     // fUIDone signals that UI window has closed and work thread should call destroy.\r
1938     // This allows the window to disappear and end modality when cancelled\r
1939     // without making the user wait for ConnectNode to return.  The dialog object\r
1940     // hangs around in the background until the work thread exits.\r
1941     if (IsModal())\r
1942         EndModal(fSuccess);\r
1943     else\r
1944         Show(false);\r
1945     if (fWorkDone)\r
1946         Destroy();\r
1947     else\r
1948         fUIDone = true;\r
1949 }\r
1950 \r
1951 void CSendingDialog::OnClose(wxCloseEvent& event)\r
1952 {\r
1953     if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)\r
1954     {\r
1955         Close();\r
1956     }\r
1957     else\r
1958     {\r
1959         event.Veto();\r
1960         wxCommandEvent cmdevent;\r
1961         OnButtonCancel(cmdevent);\r
1962     }\r
1963 }\r
1964 \r
1965 void CSendingDialog::OnButtonOK(wxCommandEvent& event)\r
1966 {\r
1967     if (fWorkDone)\r
1968         Close();\r
1969 }\r
1970 \r
1971 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)\r
1972 {\r
1973     if (fCanCancel)\r
1974         fAbort = true;\r
1975 }\r
1976 \r
1977 void CSendingDialog::OnPaint(wxPaintEvent& event)\r
1978 {\r
1979     if (strlen(pszStatus) > 130)\r
1980         m_textCtrlStatus->SetValue(string("\n") + pszStatus);\r
1981     else\r
1982         m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);\r
1983     m_staticTextSending->SetFocus();\r
1984     if (!fCanCancel)\r
1985         m_buttonCancel->Enable(false);\r
1986     if (fWorkDone)\r
1987     {\r
1988         m_buttonOK->Enable(true);\r
1989         m_buttonOK->SetFocus();\r
1990         m_buttonCancel->Enable(false);\r
1991     }\r
1992     if (fAbort && fCanCancel && IsShown())\r
1993     {\r
1994         strcpy(pszStatus, "CANCELLED");\r
1995         m_buttonOK->Enable(true);\r
1996         m_buttonOK->SetFocus();\r
1997         m_buttonCancel->Enable(false);\r
1998         m_buttonCancel->SetLabel("Cancelled");\r
1999         Close();\r
2000         wxMessageBox("Transfer cancelled  ", "Sending...", wxOK, this);\r
2001     }\r
2002     event.Skip();\r
2003 }\r
2004 \r
2005 \r
2006 //\r
2007 // Everything from here on is not in the UI thread and must only communicate\r
2008 // with the rest of the dialog through variables and calling repaint.\r
2009 //\r
2010 \r
2011 void CSendingDialog::Repaint()\r
2012 {\r
2013     Refresh();\r
2014     wxPaintEvent event;\r
2015     GetEventHandler()->AddPendingEvent(event);\r
2016 }\r
2017 \r
2018 bool CSendingDialog::Status()\r
2019 {\r
2020     if (fUIDone)\r
2021     {\r
2022         Destroy();\r
2023         return false;\r
2024     }\r
2025     if (fAbort && fCanCancel)\r
2026     {\r
2027         memset(pszStatus, 0, 10);\r
2028         strcpy(pszStatus, "CANCELLED");\r
2029         Repaint();\r
2030         fWorkDone = true;\r
2031         return false;\r
2032     }\r
2033     return true;\r
2034 }\r
2035 \r
2036 bool CSendingDialog::Status(const string& str)\r
2037 {\r
2038     if (!Status())\r
2039         return false;\r
2040 \r
2041     // This can be read by the UI thread at any time,\r
2042     // so copy in a way that can be read cleanly at all times.\r
2043     memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));\r
2044     strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));\r
2045 \r
2046     Repaint();\r
2047     return true;\r
2048 }\r
2049 \r
2050 bool CSendingDialog::Error(const string& str)\r
2051 {\r
2052     fCanCancel = false;\r
2053     fWorkDone = true;\r
2054     Status(string("Error: ") + str);\r
2055     return false;\r
2056 }\r
2057 \r
2058 void SendingDialogStartTransfer(void* parg)\r
2059 {\r
2060     ((CSendingDialog*)parg)->StartTransfer();\r
2061 }\r
2062 \r
2063 void CSendingDialog::StartTransfer()\r
2064 {\r
2065     // Make sure we have enough money\r
2066     if (nPrice + nTransactionFee > GetBalance())\r
2067     {\r
2068         Error("You don't have enough money");\r
2069         return;\r
2070     }\r
2071 \r
2072     // We may have connected already for product details\r
2073     if (!Status("Connecting..."))\r
2074         return;\r
2075     CNode* pnode = ConnectNode(addr, 15 * 60);\r
2076     if (!pnode)\r
2077     {\r
2078         Error("Unable to connect");\r
2079         return;\r
2080     }\r
2081 \r
2082     // Send order to seller, with response going to OnReply2 via event handler\r
2083     if (!Status("Requesting public key..."))\r
2084         return;\r
2085     pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);\r
2086 }\r
2087 \r
2088 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)\r
2089 {\r
2090     ((CSendingDialog*)parg)->OnReply2(vRecv);\r
2091 }\r
2092 \r
2093 void CSendingDialog::OnReply2(CDataStream& vRecv)\r
2094 {\r
2095     if (!Status("Received public key..."))\r
2096         return;\r
2097 \r
2098     CScript scriptPubKey;\r
2099     int nRet;\r
2100     try\r
2101     {\r
2102         vRecv >> nRet;\r
2103         if (nRet > 0)\r
2104         {\r
2105             string strMessage;\r
2106             vRecv >> strMessage;\r
2107             Error("Transfer was not accepted");\r
2108             //// todo: enlarge the window and enable a hidden white box to put seller's message\r
2109             return;\r
2110         }\r
2111         vRecv >> scriptPubKey;\r
2112     }\r
2113     catch (...)\r
2114     {\r
2115         //// what do we want to do about this?\r
2116         Error("Invalid response received");\r
2117         return;\r
2118     }\r
2119 \r
2120     // Pause to give the user a chance to cancel\r
2121     while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))\r
2122     {\r
2123         Sleep(200);\r
2124         if (!Status())\r
2125             return;\r
2126     }\r
2127 \r
2128     CRITICAL_BLOCK(cs_main)\r
2129     {\r
2130         // Pay\r
2131         if (!Status("Creating transaction..."))\r
2132             return;\r
2133         if (nPrice + nTransactionFee > GetBalance())\r
2134         {\r
2135             Error("You don't have enough money");\r
2136             return;\r
2137         }\r
2138         CKey key;\r
2139         int64 nFeeRequired;\r
2140         if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))\r
2141         {\r
2142             if (nPrice + nFeeRequired > GetBalance())\r
2143                 Error(strprintf("This is an oversized transaction that requires a transaction fee of %s", FormatMoney(nFeeRequired).c_str()));\r
2144             else\r
2145                 Error("Transaction creation failed");\r
2146             return;\r
2147         }\r
2148 \r
2149         // Make sure we're still connected\r
2150         CNode* pnode = ConnectNode(addr, 2 * 60 * 60);\r
2151         if (!pnode)\r
2152         {\r
2153             Error("Lost connection, transaction cancelled");\r
2154             return;\r
2155         }\r
2156 \r
2157         // Last chance to cancel\r
2158         Sleep(50);\r
2159         if (!Status())\r
2160             return;\r
2161         fCanCancel = false;\r
2162         if (fAbort)\r
2163         {\r
2164             fCanCancel = true;\r
2165             if (!Status())\r
2166                 return;\r
2167             fCanCancel = false;\r
2168         }\r
2169         if (!Status("Sending payment..."))\r
2170             return;\r
2171 \r
2172         // Commit\r
2173         if (!CommitTransactionSpent(wtx, key))\r
2174         {\r
2175             Error("Error finalizing payment");\r
2176             return;\r
2177         }\r
2178 \r
2179         // Send payment tx to seller, with response going to OnReply3 via event handler\r
2180         pnode->PushRequest("submitorder", wtx, SendingDialogOnReply3, this);\r
2181 \r
2182         // Accept and broadcast transaction\r
2183         if (!wtx.AcceptTransaction())\r
2184             printf("ERROR: CSendingDialog : wtxNew.AcceptTransaction() %s failed\n", wtx.GetHash().ToString().c_str());\r
2185         wtx.RelayWalletTransaction();\r
2186 \r
2187         Status("Waiting for confirmation...");\r
2188         MainFrameRepaint();\r
2189     }\r
2190 }\r
2191 \r
2192 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)\r
2193 {\r
2194     ((CSendingDialog*)parg)->OnReply3(vRecv);\r
2195 }\r
2196 \r
2197 void CSendingDialog::OnReply3(CDataStream& vRecv)\r
2198 {\r
2199     int nRet;\r
2200     try\r
2201     {\r
2202         vRecv >> nRet;\r
2203         if (nRet > 0)\r
2204         {\r
2205             Error("The payment was sent, but the recipient was unable to verify it.\n"\r
2206                   "The transaction is recorded and will credit to the recipient,\n"\r
2207                   "but the comment information will be blank.");\r
2208             return;\r
2209         }\r
2210     }\r
2211     catch (...)\r
2212     {\r
2213         //// what do we want to do about this?\r
2214         Error("Payment was sent, but an invalid response was received");\r
2215         return;\r
2216     }\r
2217 \r
2218     fSuccess = true;\r
2219     fWorkDone = true;\r
2220     Status("Payment completed");\r
2221 }\r
2222 \r
2223 \r
2224 \r
2225 \r
2226 \r
2227 \r
2228 //////////////////////////////////////////////////////////////////////////////\r
2229 //\r
2230 // CYourAddressDialog\r
2231 //\r
2232 \r
2233 CYourAddressDialog::CYourAddressDialog(wxWindow* parent, const string& strInitSelected) : CYourAddressDialogBase(parent)\r
2234 {\r
2235     // Init column headers\r
2236     m_listCtrl->InsertColumn(0, "Label", wxLIST_FORMAT_LEFT, 200);\r
2237     m_listCtrl->InsertColumn(1, "Bitcoin Address", wxLIST_FORMAT_LEFT, 350);\r
2238     m_listCtrl->SetFocus();\r
2239 \r
2240     // Fill listctrl with address book data\r
2241     CRITICAL_BLOCK(cs_mapKeys)\r
2242     CRITICAL_BLOCK(cs_mapAddressBook)\r
2243     {\r
2244         foreach(const PAIRTYPE(string, string)& item, mapAddressBook)\r
2245         {\r
2246             string strAddress = item.first;\r
2247             string strName = item.second;\r
2248             uint160 hash160;\r
2249             bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));\r
2250             if (fMine)\r
2251             {\r
2252                 int nIndex = InsertLine(m_listCtrl, strName, strAddress);\r
2253                 if (strAddress == strInitSelected)\r
2254                     m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);\r
2255             }\r
2256         }\r
2257     }\r
2258 }\r
2259 \r
2260 wxString CYourAddressDialog::GetAddress()\r
2261 {\r
2262     int nIndex = GetSelection(m_listCtrl);\r
2263     if (nIndex == -1)\r
2264         return "";\r
2265     return GetItemText(m_listCtrl, nIndex, 1);\r
2266 }\r
2267 \r
2268 void CYourAddressDialog::OnListEndLabelEdit(wxListEvent& event)\r
2269 {\r
2270     // Update address book with edited name\r
2271     if (event.IsEditCancelled())\r
2272         return;\r
2273     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);\r
2274     SetAddressBookName(strAddress, string(event.GetText()));\r
2275     pframeMain->RefreshListCtrl();\r
2276 }\r
2277 \r
2278 void CYourAddressDialog::OnListItemSelected(wxListEvent& event)\r
2279 {\r
2280 }\r
2281 \r
2282 void CYourAddressDialog::OnListItemActivated(wxListEvent& event)\r
2283 {\r
2284     // Doubleclick edits item\r
2285     wxCommandEvent event2;\r
2286     OnButtonRename(event2);\r
2287 }\r
2288 \r
2289 void CYourAddressDialog::OnButtonRename(wxCommandEvent& event)\r
2290 {\r
2291     // Ask new name\r
2292     int nIndex = GetSelection(m_listCtrl);\r
2293     if (nIndex == -1)\r
2294         return;\r
2295     string strName = (string)m_listCtrl->GetItemText(nIndex);\r
2296     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);\r
2297     CGetTextFromUserDialog dialog(this, "Edit Address Label", "New Label", strName);\r
2298     if (!dialog.ShowModal())\r
2299         return;\r
2300     strName = dialog.GetValue();\r
2301 \r
2302     // Change name\r
2303     SetAddressBookName(strAddress, strName);\r
2304     m_listCtrl->SetItemText(nIndex, strName);\r
2305     pframeMain->RefreshListCtrl();\r
2306 }\r
2307 \r
2308 void CYourAddressDialog::OnButtonNew(wxCommandEvent& event)\r
2309 {\r
2310     // Ask name\r
2311     CGetTextFromUserDialog dialog(this, "New Bitcoin Address", "Label", "");\r
2312     if (!dialog.ShowModal())\r
2313         return;\r
2314     string strName = dialog.GetValue();\r
2315 \r
2316     // Generate new key\r
2317     string strAddress = PubKeyToAddress(GenerateNewKey());\r
2318     SetAddressBookName(strAddress, strName);\r
2319 \r
2320     // Add to list and select it\r
2321     int nIndex = InsertLine(m_listCtrl, strName, strAddress);\r
2322     SetSelection(m_listCtrl, nIndex);\r
2323     m_listCtrl->SetFocus();\r
2324 }\r
2325 \r
2326 void CYourAddressDialog::OnButtonCopy(wxCommandEvent& event)\r
2327 {\r
2328     // Copy address box to clipboard\r
2329     if (wxTheClipboard->Open())\r
2330     {\r
2331         wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));\r
2332         wxTheClipboard->Close();\r
2333     }\r
2334 }\r
2335 \r
2336 void CYourAddressDialog::OnButtonOK(wxCommandEvent& event)\r
2337 {\r
2338     // OK\r
2339     EndModal(true);\r
2340 }\r
2341 \r
2342 void CYourAddressDialog::OnButtonCancel(wxCommandEvent& event)\r
2343 {\r
2344     // Cancel\r
2345     EndModal(false);\r
2346 }\r
2347 \r
2348 void CYourAddressDialog::OnClose(wxCloseEvent& event)\r
2349 {\r
2350     // Close\r
2351     EndModal(false);\r
2352 }\r
2353 \r
2354 \r
2355 \r
2356 \r
2357 \r
2358 \r
2359 //////////////////////////////////////////////////////////////////////////////\r
2360 //\r
2361 // CAddressBookDialog\r
2362 //\r
2363 \r
2364 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, bool fSendingIn) : CAddressBookDialogBase(parent)\r
2365 {\r
2366     fSending = fSendingIn;\r
2367     if (!fSending)\r
2368         m_buttonCancel->Show(false);\r
2369 \r
2370     // Init column headers\r
2371     m_listCtrl->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 200);\r
2372     m_listCtrl->InsertColumn(1, "Address", wxLIST_FORMAT_LEFT, 350);\r
2373     m_listCtrl->SetFocus();\r
2374 \r
2375     // Set Icon\r
2376     wxIcon iconAddressBook;\r
2377     iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));\r
2378     SetIcon(iconAddressBook);\r
2379 \r
2380     // Fill listctrl with address book data\r
2381     CRITICAL_BLOCK(cs_mapKeys)\r
2382     CRITICAL_BLOCK(cs_mapAddressBook)\r
2383     {\r
2384         foreach(const PAIRTYPE(string, string)& item, mapAddressBook)\r
2385         {\r
2386             string strAddress = item.first;\r
2387             string strName = item.second;\r
2388             uint160 hash160;\r
2389             bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));\r
2390             if (!fMine)\r
2391             {\r
2392                 int nIndex = InsertLine(m_listCtrl, strName, strAddress);\r
2393                 if (strAddress == strInitSelected)\r
2394                     m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);\r
2395             }\r
2396         }\r
2397     }\r
2398 }\r
2399 \r
2400 wxString CAddressBookDialog::GetAddress()\r
2401 {\r
2402     int nIndex = GetSelection(m_listCtrl);\r
2403     if (nIndex == -1)\r
2404         return "";\r
2405     return GetItemText(m_listCtrl, nIndex, 1);\r
2406 }\r
2407 \r
2408 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)\r
2409 {\r
2410     // Update address book with edited name\r
2411     if (event.IsEditCancelled())\r
2412         return;\r
2413     string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);\r
2414     SetAddressBookName(strAddress, string(event.GetText()));\r
2415     pframeMain->RefreshListCtrl();\r
2416 }\r
2417 \r
2418 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)\r
2419 {\r
2420 }\r
2421 \r
2422 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)\r
2423 {\r
2424     if (fSending)\r
2425     {\r
2426         // Doubleclick returns selection\r
2427         EndModal(GetAddress() != "" ? 2 : 0);\r
2428     }\r
2429     else\r
2430     {\r
2431         // Doubleclick edits item\r
2432         wxCommandEvent event2;\r
2433         OnButtonEdit(event2);\r
2434     }\r
2435 }\r
2436 \r
2437 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)\r
2438 {\r
2439     uint160 hash160;\r
2440     bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));\r
2441     if (fMine)\r
2442         wxMessageBox("This is one of your own addresses for receiving payments and cannot be entered in the address book.  ", strTitle);\r
2443     return fMine;\r
2444 }\r
2445 \r
2446 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)\r
2447 {\r
2448     // Ask new name\r
2449     int nIndex = GetSelection(m_listCtrl);\r
2450     if (nIndex == -1)\r
2451         return;\r
2452     string strName = (string)m_listCtrl->GetItemText(nIndex);\r
2453     string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);\r
2454     string strAddressOrg = strAddress;\r
2455     do\r
2456     {\r
2457         CGetTextFromUserDialog dialog(this, "Edit Address", "Name", strName, "Address", strAddress);\r
2458         if (!dialog.ShowModal())\r
2459             return;\r
2460         strName = dialog.GetValue1();\r
2461         strAddress = dialog.GetValue2();\r
2462     }\r
2463     while (CheckIfMine(strAddress, "Edit Address"));\r
2464 \r
2465     // Change name\r
2466     if (strAddress != strAddressOrg)\r
2467         CWalletDB().EraseName(strAddressOrg);\r
2468     SetAddressBookName(strAddress, strName);\r
2469     m_listCtrl->SetItem(nIndex, 1, strAddress);\r
2470     m_listCtrl->SetItemText(nIndex, strName);\r
2471     pframeMain->RefreshListCtrl();\r
2472 }\r
2473 \r
2474 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)\r
2475 {\r
2476     // Ask name\r
2477     string strName;\r
2478     string strAddress;\r
2479     do\r
2480     {\r
2481         CGetTextFromUserDialog dialog(this, "New Address", "Name", strName, "Address", strAddress);\r
2482         if (!dialog.ShowModal())\r
2483             return;\r
2484         strName = dialog.GetValue1();\r
2485         strAddress = dialog.GetValue2();\r
2486     }\r
2487     while (CheckIfMine(strAddress, "New Address"));\r
2488 \r
2489     // Add to list and select it\r
2490     SetAddressBookName(strAddress, strName);\r
2491     int nIndex = InsertLine(m_listCtrl, strName, strAddress);\r
2492     SetSelection(m_listCtrl, nIndex);\r
2493     m_listCtrl->SetFocus();\r
2494     pframeMain->RefreshListCtrl();\r
2495 }\r
2496 \r
2497 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)\r
2498 {\r
2499     for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)\r
2500     {\r
2501         if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))\r
2502         {\r
2503             string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);\r
2504             CWalletDB().EraseName(strAddress);\r
2505             m_listCtrl->DeleteItem(nIndex);\r
2506         }\r
2507     }\r
2508     pframeMain->RefreshListCtrl();\r
2509 }\r
2510 \r
2511 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)\r
2512 {\r
2513     // Copy address box to clipboard\r
2514     if (wxTheClipboard->Open())\r
2515     {\r
2516         wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));\r
2517         wxTheClipboard->Close();\r
2518     }\r
2519 }\r
2520 \r
2521 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)\r
2522 {\r
2523     // OK\r
2524     EndModal(GetAddress() != "" ? 1 : 0);\r
2525 }\r
2526 \r
2527 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)\r
2528 {\r
2529     // Cancel\r
2530     EndModal(0);\r
2531 }\r
2532 \r
2533 void CAddressBookDialog::OnClose(wxCloseEvent& event)\r
2534 {\r
2535     // Close\r
2536     EndModal(0);\r
2537 }\r
2538 \r
2539 \r
2540 \r
2541 \r
2542 \r
2543 \r
2544 //////////////////////////////////////////////////////////////////////////////\r
2545 //\r
2546 // CProductsDialog\r
2547 //\r
2548 \r
2549 bool CompareIntStringPairBestFirst(const pair<int, string>& item1, const pair<int, string>& item2)\r
2550 {\r
2551     return (item1.first > item2.first);\r
2552 }\r
2553 \r
2554 CProductsDialog::CProductsDialog(wxWindow* parent) : CProductsDialogBase(parent)\r
2555 {\r
2556     // Init column headers\r
2557     m_listCtrl->InsertColumn(0, "Title",  wxLIST_FORMAT_LEFT, 200);\r
2558     m_listCtrl->InsertColumn(1, "Price",  wxLIST_FORMAT_LEFT, 80);\r
2559     m_listCtrl->InsertColumn(2, "Seller", wxLIST_FORMAT_LEFT, 80);\r
2560     m_listCtrl->InsertColumn(3, "Stars",  wxLIST_FORMAT_LEFT, 50);\r
2561     m_listCtrl->InsertColumn(4, "Power",  wxLIST_FORMAT_LEFT, 50);\r
2562 \r
2563     // Tally top categories\r
2564     map<string, int> mapTopCategories;\r
2565     CRITICAL_BLOCK(cs_mapProducts)\r
2566         for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)\r
2567             mapTopCategories[(*mi).second.mapValue["category"]]++;\r
2568 \r
2569     // Sort top categories\r
2570     vector<pair<int, string> > vTopCategories;\r
2571     for (map<string, int>::iterator mi = mapTopCategories.begin(); mi != mapTopCategories.end(); ++mi)\r
2572         vTopCategories.push_back(make_pair((*mi).second, (*mi).first));\r
2573     sort(vTopCategories.begin(), vTopCategories.end(), CompareIntStringPairBestFirst);\r
2574 \r
2575     // Fill categories combo box\r
2576     int nLimit = 250;\r
2577     for (vector<pair<int, string> >::iterator it = vTopCategories.begin(); it != vTopCategories.end() && nLimit-- > 0; ++it)\r
2578         m_comboBoxCategory->Append((*it).second);\r
2579 \r
2580     // Fill window with initial search\r
2581     //wxCommandEvent event;\r
2582     //OnButtonSearch(event);\r
2583 }\r
2584 \r
2585 void CProductsDialog::OnCombobox(wxCommandEvent& event)\r
2586 {\r
2587     OnButtonSearch(event);\r
2588 }\r
2589 \r
2590 bool CompareProductsBestFirst(const CProduct* p1, const CProduct* p2)\r
2591 {\r
2592     return (p1->nAtoms > p2->nAtoms);\r
2593 }\r
2594 \r
2595 void CProductsDialog::OnButtonSearch(wxCommandEvent& event)\r
2596 {\r
2597     string strCategory = (string)m_comboBoxCategory->GetValue();\r
2598     string strSearch = (string)m_textCtrlSearch->GetValue();\r
2599 \r
2600     // Search products\r
2601     vector<CProduct*> vProductsFound;\r
2602     CRITICAL_BLOCK(cs_mapProducts)\r
2603     {\r
2604         for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)\r
2605         {\r
2606             CProduct& product = (*mi).second;\r
2607             if (product.mapValue["category"].find(strCategory) != -1)\r
2608             {\r
2609                 if (product.mapValue["title"].find(strSearch) != -1 ||\r
2610                     product.mapValue["description"].find(strSearch) != -1 ||\r
2611                     product.mapValue["seller"].find(strSearch) != -1)\r
2612                 {\r
2613                     vProductsFound.push_back(&product);\r
2614                 }\r
2615             }\r
2616         }\r
2617     }\r
2618 \r
2619     // Sort\r
2620     sort(vProductsFound.begin(), vProductsFound.end(), CompareProductsBestFirst);\r
2621 \r
2622     // Display\r
2623     foreach(CProduct* pproduct, vProductsFound)\r
2624     {\r
2625         InsertLine(m_listCtrl,\r
2626                    pproduct->mapValue["title"],\r
2627                    pproduct->mapValue["price"],\r
2628                    pproduct->mapValue["seller"],\r
2629                    pproduct->mapValue["stars"],\r
2630                    itostr(pproduct->nAtoms));\r
2631     }\r
2632 }\r
2633 \r
2634 void CProductsDialog::OnListItemActivated(wxListEvent& event)\r
2635 {\r
2636     // Doubleclick opens product\r
2637     CViewProductDialog* pdialog = new CViewProductDialog(this, m_vProduct[event.GetIndex()]);\r
2638     pdialog->Show();\r
2639 }\r
2640 \r
2641 \r
2642 \r
2643 \r
2644 \r
2645 \r
2646 \r
2647 //////////////////////////////////////////////////////////////////////////////\r
2648 //\r
2649 // CEditProductDialog\r
2650 //\r
2651 \r
2652 CEditProductDialog::CEditProductDialog(wxWindow* parent) : CEditProductDialogBase(parent)\r
2653 {\r
2654     m_textCtrlLabel[0 ] = m_textCtrlLabel0;\r
2655     m_textCtrlLabel[1 ] = m_textCtrlLabel1;\r
2656     m_textCtrlLabel[2 ] = m_textCtrlLabel2;\r
2657     m_textCtrlLabel[3 ] = m_textCtrlLabel3;\r
2658     m_textCtrlLabel[4 ] = m_textCtrlLabel4;\r
2659     m_textCtrlLabel[5 ] = m_textCtrlLabel5;\r
2660     m_textCtrlLabel[6 ] = m_textCtrlLabel6;\r
2661     m_textCtrlLabel[7 ] = m_textCtrlLabel7;\r
2662     m_textCtrlLabel[8 ] = m_textCtrlLabel8;\r
2663     m_textCtrlLabel[9 ] = m_textCtrlLabel9;\r
2664     m_textCtrlLabel[10] = m_textCtrlLabel10;\r
2665     m_textCtrlLabel[11] = m_textCtrlLabel11;\r
2666     m_textCtrlLabel[12] = m_textCtrlLabel12;\r
2667     m_textCtrlLabel[13] = m_textCtrlLabel13;\r
2668     m_textCtrlLabel[14] = m_textCtrlLabel14;\r
2669     m_textCtrlLabel[15] = m_textCtrlLabel15;\r
2670     m_textCtrlLabel[16] = m_textCtrlLabel16;\r
2671     m_textCtrlLabel[17] = m_textCtrlLabel17;\r
2672     m_textCtrlLabel[18] = m_textCtrlLabel18;\r
2673     m_textCtrlLabel[19] = m_textCtrlLabel19;\r
2674 \r
2675     m_textCtrlField[0 ] = m_textCtrlField0;\r
2676     m_textCtrlField[1 ] = m_textCtrlField1;\r
2677     m_textCtrlField[2 ] = m_textCtrlField2;\r
2678     m_textCtrlField[3 ] = m_textCtrlField3;\r
2679     m_textCtrlField[4 ] = m_textCtrlField4;\r
2680     m_textCtrlField[5 ] = m_textCtrlField5;\r
2681     m_textCtrlField[6 ] = m_textCtrlField6;\r
2682     m_textCtrlField[7 ] = m_textCtrlField7;\r
2683     m_textCtrlField[8 ] = m_textCtrlField8;\r
2684     m_textCtrlField[9 ] = m_textCtrlField9;\r
2685     m_textCtrlField[10] = m_textCtrlField10;\r
2686     m_textCtrlField[11] = m_textCtrlField11;\r
2687     m_textCtrlField[12] = m_textCtrlField12;\r
2688     m_textCtrlField[13] = m_textCtrlField13;\r
2689     m_textCtrlField[14] = m_textCtrlField14;\r
2690     m_textCtrlField[15] = m_textCtrlField15;\r
2691     m_textCtrlField[16] = m_textCtrlField16;\r
2692     m_textCtrlField[17] = m_textCtrlField17;\r
2693     m_textCtrlField[18] = m_textCtrlField18;\r
2694     m_textCtrlField[19] = m_textCtrlField19;\r
2695 \r
2696     m_buttonDel[0 ] = m_buttonDel0;\r
2697     m_buttonDel[1 ] = m_buttonDel1;\r
2698     m_buttonDel[2 ] = m_buttonDel2;\r
2699     m_buttonDel[3 ] = m_buttonDel3;\r
2700     m_buttonDel[4 ] = m_buttonDel4;\r
2701     m_buttonDel[5 ] = m_buttonDel5;\r
2702     m_buttonDel[6 ] = m_buttonDel6;\r
2703     m_buttonDel[7 ] = m_buttonDel7;\r
2704     m_buttonDel[8 ] = m_buttonDel8;\r
2705     m_buttonDel[9 ] = m_buttonDel9;\r
2706     m_buttonDel[10] = m_buttonDel10;\r
2707     m_buttonDel[11] = m_buttonDel11;\r
2708     m_buttonDel[12] = m_buttonDel12;\r
2709     m_buttonDel[13] = m_buttonDel13;\r
2710     m_buttonDel[14] = m_buttonDel14;\r
2711     m_buttonDel[15] = m_buttonDel15;\r
2712     m_buttonDel[16] = m_buttonDel16;\r
2713     m_buttonDel[17] = m_buttonDel17;\r
2714     m_buttonDel[18] = m_buttonDel18;\r
2715     m_buttonDel[19] = m_buttonDel19;\r
2716 \r
2717     for (int i = 1; i < FIELDS_MAX; i++)\r
2718         ShowLine(i, false);\r
2719 \r
2720     LayoutAll();\r
2721 }\r
2722 \r
2723 void CEditProductDialog::LayoutAll()\r
2724 {\r
2725     m_scrolledWindow->Layout();\r
2726     m_scrolledWindow->GetSizer()->Fit(m_scrolledWindow);\r
2727     this->Layout();\r
2728 }\r
2729 \r
2730 void CEditProductDialog::ShowLine(int i, bool fShow)\r
2731 {\r
2732     m_textCtrlLabel[i]->Show(fShow);\r
2733     m_textCtrlField[i]->Show(fShow);\r
2734     m_buttonDel[i]->Show(fShow);\r
2735 }\r
2736 \r
2737 void CEditProductDialog::OnButtonDel0(wxCommandEvent& event)  { OnButtonDel(event, 0); }\r
2738 void CEditProductDialog::OnButtonDel1(wxCommandEvent& event)  { OnButtonDel(event, 1); }\r
2739 void CEditProductDialog::OnButtonDel2(wxCommandEvent& event)  { OnButtonDel(event, 2); }\r
2740 void CEditProductDialog::OnButtonDel3(wxCommandEvent& event)  { OnButtonDel(event, 3); }\r
2741 void CEditProductDialog::OnButtonDel4(wxCommandEvent& event)  { OnButtonDel(event, 4); }\r
2742 void CEditProductDialog::OnButtonDel5(wxCommandEvent& event)  { OnButtonDel(event, 5); }\r
2743 void CEditProductDialog::OnButtonDel6(wxCommandEvent& event)  { OnButtonDel(event, 6); }\r
2744 void CEditProductDialog::OnButtonDel7(wxCommandEvent& event)  { OnButtonDel(event, 7); }\r
2745 void CEditProductDialog::OnButtonDel8(wxCommandEvent& event)  { OnButtonDel(event, 8); }\r
2746 void CEditProductDialog::OnButtonDel9(wxCommandEvent& event)  { OnButtonDel(event, 9); }\r
2747 void CEditProductDialog::OnButtonDel10(wxCommandEvent& event) { OnButtonDel(event, 10); }\r
2748 void CEditProductDialog::OnButtonDel11(wxCommandEvent& event) { OnButtonDel(event, 11); }\r
2749 void CEditProductDialog::OnButtonDel12(wxCommandEvent& event) { OnButtonDel(event, 12); }\r
2750 void CEditProductDialog::OnButtonDel13(wxCommandEvent& event) { OnButtonDel(event, 13); }\r
2751 void CEditProductDialog::OnButtonDel14(wxCommandEvent& event) { OnButtonDel(event, 14); }\r
2752 void CEditProductDialog::OnButtonDel15(wxCommandEvent& event) { OnButtonDel(event, 15); }\r
2753 void CEditProductDialog::OnButtonDel16(wxCommandEvent& event) { OnButtonDel(event, 16); }\r
2754 void CEditProductDialog::OnButtonDel17(wxCommandEvent& event) { OnButtonDel(event, 17); }\r
2755 void CEditProductDialog::OnButtonDel18(wxCommandEvent& event) { OnButtonDel(event, 18); }\r
2756 void CEditProductDialog::OnButtonDel19(wxCommandEvent& event) { OnButtonDel(event, 19); }\r
2757 \r
2758 void CEditProductDialog::OnButtonDel(wxCommandEvent& event, int n)\r
2759 {\r
2760     Freeze();\r
2761     int x, y;\r
2762     m_scrolledWindow->GetViewStart(&x, &y);\r
2763     int i;\r
2764     for (i = n; i < FIELDS_MAX-1; i++)\r
2765     {\r
2766         m_textCtrlLabel[i]->SetValue(m_textCtrlLabel[i+1]->GetValue());\r
2767         m_textCtrlField[i]->SetValue(m_textCtrlField[i+1]->GetValue());\r
2768         if (!m_buttonDel[i+1]->IsShown())\r
2769             break;\r
2770     }\r
2771     m_textCtrlLabel[i]->SetValue("");\r
2772     m_textCtrlField[i]->SetValue("");\r
2773     ShowLine(i, false);\r
2774     m_buttonAddField->Enable(true);\r
2775     LayoutAll();\r
2776     m_scrolledWindow->Scroll(0, y);\r
2777     Thaw();\r
2778 }\r
2779 \r
2780 void CEditProductDialog::OnButtonAddField(wxCommandEvent& event)\r
2781 {\r
2782     for (int i = 0; i < FIELDS_MAX; i++)\r
2783     {\r
2784         if (!m_buttonDel[i]->IsShown())\r
2785         {\r
2786             Freeze();\r
2787             ShowLine(i, true);\r
2788             if (i == FIELDS_MAX-1)\r
2789                 m_buttonAddField->Enable(false);\r
2790             LayoutAll();\r
2791             m_scrolledWindow->Scroll(0, 99999);\r
2792             Thaw();\r
2793             break;\r
2794         }\r
2795     }\r
2796 }\r
2797 \r
2798 void AddToMyProducts(CProduct product)\r
2799 {\r
2800     CProduct& productInsert = mapMyProducts[product.GetHash()];\r
2801     productInsert = product;\r
2802     //InsertLine(pframeMain->m_listCtrlProductsSent, &productInsert,\r
2803     //            product.mapValue["category"],\r
2804     //            product.mapValue["title"].substr(0, 100),\r
2805     //            product.mapValue["description"].substr(0, 100),\r
2806     //            product.mapValue["price"],\r
2807     //            "");\r
2808 }\r
2809 \r
2810 void CEditProductDialog::OnButtonSend(wxCommandEvent& event)\r
2811 {\r
2812     CProduct product;\r
2813     GetProduct(product);\r
2814 \r
2815     // Sign the detailed product\r
2816     product.vchPubKeyFrom = keyUser.GetPubKey();\r
2817     if (!keyUser.Sign(product.GetSigHash(), product.vchSig))\r
2818     {\r
2819         wxMessageBox("Error digitally signing the product  ");\r
2820         return;\r
2821     }\r
2822 \r
2823     // Save detailed product\r
2824     AddToMyProducts(product);\r
2825 \r
2826     // Strip down to summary product\r
2827     product.mapDetails.clear();\r
2828     product.vOrderForm.clear();\r
2829 \r
2830     // Sign the summary product\r
2831     if (!keyUser.Sign(product.GetSigHash(), product.vchSig))\r
2832     {\r
2833         wxMessageBox("Error digitally signing the product  ");\r
2834         return;\r
2835     }\r
2836 \r
2837     // Verify\r
2838     if (!product.CheckProduct())\r
2839     {\r
2840         wxMessageBox("Errors found in product  ");\r
2841         return;\r
2842     }\r
2843 \r
2844     // Broadcast\r
2845     AdvertStartPublish(pnodeLocalHost, MSG_PRODUCT, 0, product);\r
2846 \r
2847     Destroy();\r
2848 }\r
2849 \r
2850 void CEditProductDialog::OnButtonPreview(wxCommandEvent& event)\r
2851 {\r
2852     CProduct product;\r
2853     GetProduct(product);\r
2854     CViewProductDialog* pdialog = new CViewProductDialog(this, product);\r
2855     pdialog->Show();\r
2856 }\r
2857 \r
2858 void CEditProductDialog::OnButtonCancel(wxCommandEvent& event)\r
2859 {\r
2860     Destroy();\r
2861 }\r
2862 \r
2863 void CEditProductDialog::SetProduct(const CProduct& productIn)\r
2864 {\r
2865     CProduct product = productIn;\r
2866 \r
2867     m_comboBoxCategory->SetValue(product.mapValue["category"]);\r
2868     m_textCtrlTitle->SetValue(product.mapValue["title"]);\r
2869     m_textCtrlPrice->SetValue(product.mapValue["price"]);\r
2870     m_textCtrlDescription->SetValue(product.mapValue["description"]);\r
2871     m_textCtrlInstructions->SetValue(product.mapValue["instructions"]);\r
2872 \r
2873     for (int i = 0; i < FIELDS_MAX; i++)\r
2874     {\r
2875         bool fUsed = i < product.vOrderForm.size();\r
2876         m_buttonDel[i]->Show(fUsed);\r
2877         m_textCtrlLabel[i]->Show(fUsed);\r
2878         m_textCtrlField[i]->Show(fUsed);\r
2879         if (!fUsed)\r
2880             continue;\r
2881 \r
2882         m_textCtrlLabel[i]->SetValue(product.vOrderForm[i].first);\r
2883         string strControl = product.vOrderForm[i].second;\r
2884         if (strControl.substr(0, 5) == "text=")\r
2885             m_textCtrlField[i]->SetValue("");\r
2886         else if (strControl.substr(0, 7) == "choice=")\r
2887             m_textCtrlField[i]->SetValue(strControl.substr(7));\r
2888         else\r
2889             m_textCtrlField[i]->SetValue(strControl);\r
2890     }\r
2891 }\r
2892 \r
2893 void CEditProductDialog::GetProduct(CProduct& product)\r
2894 {\r
2895     // map<string, string> mapValue;\r
2896     // vector<pair<string, string> > vOrderForm;\r
2897 \r
2898     product.mapValue["category"]     = m_comboBoxCategory->GetValue().Trim();\r
2899     product.mapValue["title"]        = m_textCtrlTitle->GetValue().Trim();\r
2900     product.mapValue["price"]        = m_textCtrlPrice->GetValue().Trim();\r
2901     product.mapValue["description"]  = m_textCtrlDescription->GetValue().Trim();\r
2902     product.mapValue["instructions"] = m_textCtrlInstructions->GetValue().Trim();\r
2903 \r
2904     for (int i = 0; i < FIELDS_MAX; i++)\r
2905     {\r
2906         if (m_buttonDel[i]->IsShown())\r
2907         {\r
2908             string strLabel = (string)m_textCtrlLabel[i]->GetValue().Trim();\r
2909             string strControl = (string)m_textCtrlField[i]->GetValue();\r
2910             if (strControl.empty())\r
2911                 strControl = "text=";\r
2912             else\r
2913                 strControl = "choice=" + strControl;\r
2914             product.vOrderForm.push_back(make_pair(strLabel, strControl));\r
2915         }\r
2916     }\r
2917 }\r
2918 \r
2919 \r
2920 \r
2921 \r
2922 \r
2923 \r
2924 \r
2925 //////////////////////////////////////////////////////////////////////////////\r
2926 //\r
2927 // CViewProductDialog\r
2928 //\r
2929 \r
2930 CViewProductDialog::CViewProductDialog(wxWindow* parent, const CProduct& productIn) : CViewProductDialogBase(parent)\r
2931 {\r
2932     Connect(wxEVT_REPLY1, wxCommandEventHandler(CViewProductDialog::OnReply1), NULL, this);\r
2933     AddCallbackAvailable(GetEventHandler());\r
2934 \r
2935     // Fill display with product summary while waiting for details\r
2936     product = productIn;\r
2937     UpdateProductDisplay(false);\r
2938 \r
2939     m_buttonBack->Enable(false);\r
2940     m_buttonNext->Enable(!product.vOrderForm.empty());\r
2941     m_htmlWinReviews->Show(true);\r
2942     m_scrolledWindow->Show(false);\r
2943     this->Layout();\r
2944 \r
2945     // Request details from seller\r
2946     CreateThread(ThreadRequestProductDetails, new pair<CProduct, wxEvtHandler*>(product, GetEventHandler()));\r
2947 }\r
2948 \r
2949 CViewProductDialog::~CViewProductDialog()\r
2950 {\r
2951     RemoveCallbackAvailable(GetEventHandler());\r
2952 }\r
2953 \r
2954 void ThreadRequestProductDetails(void* parg)\r
2955 {\r
2956     // Extract parameters\r
2957     pair<CProduct, wxEvtHandler*>* pitem = (pair<CProduct, wxEvtHandler*>*)parg;\r
2958     CProduct product = pitem->first;\r
2959     wxEvtHandler* pevthandler = pitem->second;\r
2960     delete pitem;\r
2961 \r
2962     // Connect to seller\r
2963     CNode* pnode = ConnectNode(product.addr, 5 * 60);\r
2964     if (!pnode)\r
2965     {\r
2966         CDataStream ssEmpty;\r
2967         AddPendingReplyEvent1(pevthandler, ssEmpty);\r
2968         return;\r
2969     }\r
2970 \r
2971     // Request detailed product, with response going to OnReply1 via dialog's event handler\r
2972     pnode->PushRequest("getdetails", product.GetHash(), AddPendingReplyEvent1, (void*)pevthandler);\r
2973 }\r
2974 \r
2975 void CViewProductDialog::OnReply1(wxCommandEvent& event)\r
2976 {\r
2977     CDataStream ss = GetStreamFromEvent(event);\r
2978     if (ss.empty())\r
2979     {\r
2980         product.mapValue["description"] = "-- CAN'T CONNECT TO SELLER --\n";\r
2981         UpdateProductDisplay(true);\r
2982         return;\r
2983     }\r
2984 \r
2985     int nRet;\r
2986     CProduct product2;\r
2987     try\r
2988     {\r
2989         ss >> nRet;\r
2990         if (nRet > 0)\r
2991             throw false;\r
2992         ss >> product2;\r
2993         if (product2.GetHash() != product.GetHash())\r
2994             throw false;\r
2995         if (!product2.CheckSignature())\r
2996             throw false;\r
2997     }\r
2998     catch (...)\r
2999     {\r
3000         product.mapValue["description"] = "-- INVALID RESPONSE --\n";\r
3001         UpdateProductDisplay(true);\r
3002         return;\r
3003     }\r
3004 \r
3005     product = product2;\r
3006     UpdateProductDisplay(true);\r
3007 }\r
3008 \r
3009 bool CompareReviewsBestFirst(const CReview* p1, const CReview* p2)\r
3010 {\r
3011     return (p1->nAtoms > p2->nAtoms);\r
3012 }\r
3013 \r
3014 void CViewProductDialog::UpdateProductDisplay(bool fDetails)\r
3015 {\r
3016     // Product and reviews\r
3017     string strHTML;\r
3018     strHTML.reserve(4000);\r
3019     strHTML += "<html>\n"\r
3020                "<head>\n"\r
3021                "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"\r
3022                "</head>\n"\r
3023                "<body>\n";\r
3024     strHTML += "<b>Category:</b> " + HtmlEscape(product.mapValue["category"]) + "<br>\n";\r
3025     strHTML += "<b>Title:</b> "    + HtmlEscape(product.mapValue["title"])    + "<br>\n";\r
3026     strHTML += "<b>Price:</b> "    + HtmlEscape(product.mapValue["price"])    + "<br>\n";\r
3027 \r
3028     if (!fDetails)\r
3029         strHTML += "<b>Loading details...</b><br>\n<br>\n";\r
3030     else\r
3031         strHTML += HtmlEscape(product.mapValue["description"], true) + "<br>\n<br>\n";\r
3032 \r
3033     strHTML += "<b>Reviews:</b><br>\n<br>\n";\r
3034 \r
3035     if (!product.vchPubKeyFrom.empty())\r
3036     {\r
3037         CReviewDB reviewdb("r");\r
3038 \r
3039         // Get reviews\r
3040         vector<CReview> vReviews;\r
3041         reviewdb.ReadReviews(product.GetUserHash(), vReviews);\r
3042 \r
3043         // Get reviewer's number of atoms\r
3044         vector<CReview*> vSortedReviews;\r
3045         vSortedReviews.reserve(vReviews.size());\r
3046         for (vector<CReview>::reverse_iterator it = vReviews.rbegin(); it != vReviews.rend(); ++it)\r
3047         {\r
3048             CReview& review = *it;\r
3049             CUser user;\r
3050             reviewdb.ReadUser(review.GetUserHash(), user);\r
3051             review.nAtoms = user.GetAtomCount();\r
3052             vSortedReviews.push_back(&review);\r
3053         }\r
3054 \r
3055         reviewdb.Close();\r
3056 \r
3057         // Sort\r
3058         stable_sort(vSortedReviews.begin(), vSortedReviews.end(), CompareReviewsBestFirst);\r
3059 \r
3060         // Format reviews\r
3061         foreach(CReview* preview, vSortedReviews)\r
3062         {\r
3063             CReview& review = *preview;\r
3064             int nStars = atoi(review.mapValue["stars"].c_str());\r
3065             if (nStars < 1 || nStars > 5)\r
3066                 continue;\r
3067 \r
3068             strHTML += "<b>" + itostr(nStars) + (nStars == 1 ? " star" : " stars") + "</b>";\r
3069             strHTML += " &nbsp;&nbsp;&nbsp; ";\r
3070             strHTML += DateStr(atoi64(review.mapValue["date"])) + "<br>\n";\r
3071             strHTML += HtmlEscape(review.mapValue["review"], true);\r
3072             strHTML += "<br>\n<br>\n";\r
3073         }\r
3074     }\r
3075 \r
3076     strHTML += "</body>\n</html>\n";\r
3077 \r
3078     // Shrink capacity to fit\r
3079     string(strHTML.begin(), strHTML.end()).swap(strHTML);\r
3080 \r
3081     m_htmlWinReviews->SetPage(strHTML);\r
3082 \r
3083     ///// need to find some other indicator to use so can allow empty order form\r
3084     if (product.vOrderForm.empty())\r
3085         return;\r
3086 \r
3087     // Order form\r
3088     m_staticTextInstructions->SetLabel(product.mapValue["instructions"]);\r
3089     for (int i = 0; i < FIELDS_MAX; i++)\r
3090     {\r
3091         m_staticTextLabel[i] = NULL;\r
3092         m_textCtrlField[i] = NULL;\r
3093         m_choiceField[i] = NULL;\r
3094     }\r
3095 \r
3096     // Construct flexgridsizer\r
3097     wxBoxSizer* bSizer21 = (wxBoxSizer*)m_scrolledWindow->GetSizer();\r
3098     wxFlexGridSizer* fgSizer;\r
3099     fgSizer = new wxFlexGridSizer(0, 2, 0, 0);\r
3100     fgSizer->AddGrowableCol(1);\r
3101     fgSizer->SetFlexibleDirection(wxBOTH);\r
3102     fgSizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);\r
3103 \r
3104     // Construct order form fields\r
3105     wxWindow* windowLast = NULL;\r
3106     for (int i = 0; i < product.vOrderForm.size(); i++)\r
3107     {\r
3108         string strLabel = product.vOrderForm[i].first;\r
3109         string strControl = product.vOrderForm[i].second;\r
3110 \r
3111         if (strLabel.size() < 20)\r
3112             strLabel.insert(strLabel.begin(), 20 - strLabel.size(), ' ');\r
3113 \r
3114         m_staticTextLabel[i] = new wxStaticText(m_scrolledWindow, wxID_ANY, strLabel, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);\r
3115         m_staticTextLabel[i]->Wrap(-1);\r
3116         fgSizer->Add(m_staticTextLabel[i], 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5);\r
3117 \r
3118         if (strControl.substr(0, 5) == "text=")\r
3119         {\r
3120             m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);\r
3121             fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);\r
3122             windowLast = m_textCtrlField[i];\r
3123         }\r
3124         else if (strControl.substr(0, 7) == "choice=")\r
3125         {\r
3126             vector<string> vChoices;\r
3127             ParseString(strControl.substr(7), ',', vChoices);\r
3128 \r
3129             wxArrayString arraystring;\r
3130             foreach(const string& str, vChoices)\r
3131                 arraystring.Add(str);\r
3132 \r
3133             m_choiceField[i] = new wxChoice(m_scrolledWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize, arraystring, 0);\r
3134             fgSizer->Add(m_choiceField[i], 0, wxALL|wxALIGN_CENTER_VERTICAL, 5);\r
3135             windowLast = m_choiceField[i];\r
3136         }\r
3137         else\r
3138         {\r
3139             m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);\r
3140             fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);\r
3141             m_staticTextLabel[i]->Show(false);\r
3142             m_textCtrlField[i]->Show(false);\r
3143         }\r
3144     }\r
3145 \r
3146     // Insert after instructions and before submit/cancel buttons\r
3147     bSizer21->Insert(2, fgSizer, 0, wxEXPAND|wxRIGHT|wxLEFT, 5);\r
3148     m_scrolledWindow->Layout();\r
3149     bSizer21->Fit(m_scrolledWindow);\r
3150     this->Layout();\r
3151 \r
3152     // Fixup the tab order\r
3153     m_buttonSubmitForm->MoveAfterInTabOrder(windowLast);\r
3154     m_buttonCancelForm->MoveAfterInTabOrder(m_buttonSubmitForm);\r
3155     //m_buttonBack->MoveAfterInTabOrder(m_buttonCancelForm);\r
3156     //m_buttonNext->MoveAfterInTabOrder(m_buttonBack);\r
3157     //m_buttonCancel->MoveAfterInTabOrder(m_buttonNext);\r
3158     this->Layout();\r
3159 }\r
3160 \r
3161 void CViewProductDialog::GetOrder(CWalletTx& wtx)\r
3162 {\r
3163     wtx.SetNull();\r
3164     for (int i = 0; i < product.vOrderForm.size(); i++)\r
3165     {\r
3166         string strValue;\r
3167         if (m_textCtrlField[i])\r
3168             strValue = m_textCtrlField[i]->GetValue().Trim();\r
3169         else\r
3170             strValue = m_choiceField[i]->GetStringSelection();\r
3171         wtx.vOrderForm.push_back(make_pair(m_staticTextLabel[i]->GetLabel(), strValue));\r
3172     }\r
3173 }\r
3174 \r
3175 void CViewProductDialog::OnButtonSubmitForm(wxCommandEvent& event)\r
3176 {\r
3177     m_buttonSubmitForm->Enable(false);\r
3178     m_buttonCancelForm->Enable(false);\r
3179 \r
3180     CWalletTx wtx;\r
3181     GetOrder(wtx);\r
3182 \r
3183     CSendingDialog* pdialog = new CSendingDialog(this, product.addr, atoi64(product.mapValue["price"]), wtx);\r
3184     if (!pdialog->ShowModal())\r
3185     {\r
3186         m_buttonSubmitForm->Enable(true);\r
3187         m_buttonCancelForm->Enable(true);\r
3188         return;\r
3189     }\r
3190 }\r
3191 \r
3192 void CViewProductDialog::OnButtonCancelForm(wxCommandEvent& event)\r
3193 {\r
3194     Destroy();\r
3195 }\r
3196 \r
3197 void CViewProductDialog::OnButtonBack(wxCommandEvent& event)\r
3198 {\r
3199     Freeze();\r
3200     m_htmlWinReviews->Show(true);\r
3201     m_scrolledWindow->Show(false);\r
3202     m_buttonBack->Enable(false);\r
3203     m_buttonNext->Enable(!product.vOrderForm.empty());\r
3204     this->Layout();\r
3205     Thaw();\r
3206 }\r
3207 \r
3208 void CViewProductDialog::OnButtonNext(wxCommandEvent& event)\r
3209 {\r
3210     if (!product.vOrderForm.empty())\r
3211     {\r
3212         Freeze();\r
3213         m_htmlWinReviews->Show(false);\r
3214         m_scrolledWindow->Show(true);\r
3215         m_buttonBack->Enable(true);\r
3216         m_buttonNext->Enable(false);\r
3217         this->Layout();\r
3218         Thaw();\r
3219     }\r
3220 }\r
3221 \r
3222 void CViewProductDialog::OnButtonCancel(wxCommandEvent& event)\r
3223 {\r
3224     Destroy();\r
3225 }\r
3226 \r
3227 \r
3228 \r
3229 \r
3230 \r
3231 \r
3232 \r
3233 //////////////////////////////////////////////////////////////////////////////\r
3234 //\r
3235 // CViewOrderDialog\r
3236 //\r
3237 \r
3238 CViewOrderDialog::CViewOrderDialog(wxWindow* parent, CWalletTx order, bool fReceived) : CViewOrderDialogBase(parent)\r
3239 {\r
3240     int64 nPrice = (fReceived ? order.GetCredit() : order.GetDebit());\r
3241 \r
3242     string strHTML;\r
3243     strHTML.reserve(4000);\r
3244     strHTML += "<html>\n"\r
3245                "<head>\n"\r
3246                "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"\r
3247                "</head>\n"\r
3248                "<body>\n";\r
3249     strHTML += "<b>Time:</b> "   + HtmlEscape(DateTimeStr(order.nTimeReceived)) + "<br>\n";\r
3250     strHTML += "<b>Price:</b> "  + HtmlEscape(FormatMoney(nPrice)) + "<br>\n";\r
3251     strHTML += "<b>Status:</b> " + HtmlEscape(FormatTxStatus(order)) + "<br>\n";\r
3252 \r
3253     strHTML += "<table>\n";\r
3254     for (int i = 0; i < order.vOrderForm.size(); i++)\r
3255     {\r
3256         strHTML += " <tr><td><b>" + HtmlEscape(order.vOrderForm[i].first) + ":</b></td>";\r
3257         strHTML += "<td>" + HtmlEscape(order.vOrderForm[i].second) + "</td></tr>\n";\r
3258     }\r
3259     strHTML += "</table>\n";\r
3260 \r
3261     strHTML += "</body>\n</html>\n";\r
3262 \r
3263     // Shrink capacity to fit\r
3264     // (strings are ref counted, so it may live on in SetPage)\r
3265     string(strHTML.begin(), strHTML.end()).swap(strHTML);\r
3266 \r
3267     m_htmlWin->SetPage(strHTML);\r
3268 }\r
3269 \r
3270 void CViewOrderDialog::OnButtonOK(wxCommandEvent& event)\r
3271 {\r
3272     Destroy();\r
3273 }\r
3274 \r
3275 \r
3276 \r
3277 \r
3278 \r
3279 \r
3280 \r
3281 //////////////////////////////////////////////////////////////////////////////\r
3282 //\r
3283 // CEditReviewDialog\r
3284 //\r
3285 \r
3286 CEditReviewDialog::CEditReviewDialog(wxWindow* parent) : CEditReviewDialogBase(parent)\r
3287 {\r
3288 }\r
3289 \r
3290 void CEditReviewDialog::OnButtonSubmit(wxCommandEvent& event)\r
3291 {\r
3292     if (m_choiceStars->GetSelection() == -1)\r
3293     {\r
3294         wxMessageBox("Please select a rating  ");\r
3295         return;\r
3296     }\r
3297 \r
3298     CReview review;\r
3299     GetReview(review);\r
3300 \r
3301     // Sign the review\r
3302     review.vchPubKeyFrom = keyUser.GetPubKey();\r
3303     if (!keyUser.Sign(review.GetSigHash(), review.vchSig))\r
3304     {\r
3305         wxMessageBox("Unable to digitally sign the review  ");\r
3306         return;\r
3307     }\r
3308 \r
3309     // Broadcast\r
3310     if (!review.AcceptReview())\r
3311     {\r
3312         wxMessageBox("Save failed  ");\r
3313         return;\r
3314     }\r
3315     RelayMessage(CInv(MSG_REVIEW, review.GetHash()), review);\r
3316 \r
3317     Destroy();\r
3318 }\r
3319 \r
3320 void CEditReviewDialog::OnButtonCancel(wxCommandEvent& event)\r
3321 {\r
3322     Destroy();\r
3323 }\r
3324 \r
3325 void CEditReviewDialog::GetReview(CReview& review)\r
3326 {\r
3327     review.mapValue["time"]   = i64tostr(GetAdjustedTime());\r
3328     review.mapValue["stars"]  = itostr(m_choiceStars->GetSelection()+1);\r
3329     review.mapValue["review"] = m_textCtrlReview->GetValue();\r
3330 }\r
3331 \r
3332 \r
3333 \r
3334 \r
3335 \r
3336 \r
3337 \r
3338 //////////////////////////////////////////////////////////////////////////////\r
3339 //\r
3340 // CMyTaskBarIcon\r
3341 //\r
3342 \r
3343 enum\r
3344 {\r
3345     ID_TASKBAR_RESTORE = 10001,\r
3346     ID_TASKBAR_OPTIONS,\r
3347     ID_TASKBAR_GENERATE,\r
3348     ID_TASKBAR_EXIT,\r
3349 };\r
3350 \r
3351 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)\r
3352     EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)\r
3353     EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)\r
3354     EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)\r
3355     EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)\r
3356     EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)\r
3357     EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)\r
3358 END_EVENT_TABLE()\r
3359 \r
3360 void CMyTaskBarIcon::Show(bool fShow)\r
3361 {\r
3362     static char pszPrevTip[200];\r
3363     if (fShow)\r
3364     {\r
3365         string strTooltip = "Bitcoin";\r
3366         if (fGenerateBitcoins)\r
3367             strTooltip = "Bitcoin - Generating";\r
3368         if (fGenerateBitcoins && vNodes.empty())\r
3369             strTooltip = "Bitcoin - (not connected)";\r
3370 \r
3371         // Optimization, only update when changed, using char array to be reentrant\r
3372         if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)\r
3373         {\r
3374             strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));\r
3375 #ifdef __WXMSW__\r
3376             SetIcon(wxICON(bitcoin), strTooltip);\r
3377 #else\r
3378             SetIcon(bitcoin20_xpm, strTooltip);\r
3379 #endif\r
3380         }\r
3381     }\r
3382     else\r
3383     {\r
3384         strlcpy(pszPrevTip, "", sizeof(pszPrevTip));\r
3385         RemoveIcon();\r
3386     }\r
3387 }\r
3388 \r
3389 void CMyTaskBarIcon::Hide()\r
3390 {\r
3391     Show(false);\r
3392 }\r
3393 \r
3394 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)\r
3395 {\r
3396     Restore();\r
3397 }\r
3398 \r
3399 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)\r
3400 {\r
3401     Restore();\r
3402 }\r
3403 \r
3404 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)\r
3405 {\r
3406     // Since it's modal, get the main window to do it\r
3407     wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_MENUOPTIONSOPTIONS);\r
3408     pframeMain->GetEventHandler()->AddPendingEvent(event2);\r
3409 }\r
3410 \r
3411 void CMyTaskBarIcon::Restore()\r
3412 {\r
3413     pframeMain->Show();\r
3414     wxIconizeEvent event(0, false);\r
3415     pframeMain->GetEventHandler()->AddPendingEvent(event);\r
3416     pframeMain->Iconize(false);\r
3417     pframeMain->Raise();\r
3418 }\r
3419 \r
3420 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)\r
3421 {\r
3422     GenerateBitcoins(event.IsChecked());\r
3423 }\r
3424 \r
3425 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)\r
3426 {\r
3427     event.Check(fGenerateBitcoins);\r
3428 }\r
3429 \r
3430 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)\r
3431 {\r
3432     pframeMain->Close(true);\r
3433 }\r
3434 \r
3435 void CMyTaskBarIcon::UpdateTooltip()\r
3436 {\r
3437     if (IsIconInstalled())\r
3438         Show(true);\r
3439 }\r
3440 \r
3441 wxMenu* CMyTaskBarIcon::CreatePopupMenu()\r
3442 {\r
3443     wxMenu* pmenu = new wxMenu;\r
3444     pmenu->Append(ID_TASKBAR_RESTORE, "&Open Bitcoin");\r
3445     pmenu->Append(ID_TASKBAR_OPTIONS, "O&ptions...");\r
3446     pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, "&Generate Coins")->Check(fGenerateBitcoins);\r
3447 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu\r
3448     pmenu->AppendSeparator();\r
3449     pmenu->Append(ID_TASKBAR_EXIT, "E&xit");\r
3450 #endif\r
3451     return pmenu;\r
3452 }\r
3453 \r
3454 \r
3455 \r
3456 \r
3457 \r
3458 \r
3459 \r
3460 \r
3461 \r
3462 \r
3463 //////////////////////////////////////////////////////////////////////////////\r
3464 //\r
3465 // CMyApp\r
3466 //\r
3467 \r
3468 // Define a new application\r
3469 class CMyApp: public wxApp\r
3470 {\r
3471   public:\r
3472     CMyApp(){};\r
3473     ~CMyApp(){};\r
3474     bool OnInit();\r
3475     bool OnInit2();\r
3476     int OnExit();\r
3477 \r
3478     // 2nd-level exception handling: we get all the exceptions occurring in any\r
3479     // event handler here\r
3480     virtual bool OnExceptionInMainLoop();\r
3481 \r
3482     // 3rd, and final, level exception handling: whenever an unhandled\r
3483     // exception is caught, this function is called\r
3484     virtual void OnUnhandledException();\r
3485 \r
3486     // and now for something different: this function is called in case of a\r
3487     // crash (e.g. dereferencing null pointer, division by 0, ...)\r
3488     virtual void OnFatalException();\r
3489 };\r
3490 \r
3491 IMPLEMENT_APP(CMyApp)\r
3492 \r
3493 bool CMyApp::OnInit()\r
3494 {\r
3495     bool fRet = false;\r
3496     try\r
3497     {\r
3498         fRet = OnInit2();\r
3499     }\r
3500     catch (std::exception& e) {\r
3501         PrintException(&e, "OnInit()");\r
3502     } catch (...) {\r
3503         PrintException(NULL, "OnInit()");\r
3504     }\r
3505     if (!fRet)\r
3506         Shutdown(NULL);\r
3507     return fRet;\r
3508 }\r
3509 \r
3510 bool CMyApp::OnInit2()\r
3511 {\r
3512 #ifdef _MSC_VER\r
3513     // Turn off microsoft heap dump noise for now\r
3514     _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);\r
3515     _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));\r
3516 #endif\r
3517 #if defined(__WXMSW__) && defined(__WXDEBUG__)\r
3518     // Disable malfunctioning wxWidgets debug assertion\r
3519     g_isPainting = 10000;\r
3520 #endif\r
3521     wxImage::AddHandler(new wxPNGHandler);\r
3522 #ifdef __WXMSW__\r
3523     SetAppName("Bitcoin");\r
3524 #else\r
3525     SetAppName("bitcoin");\r
3526     umask(077);\r
3527 #endif\r
3528 \r
3529     //\r
3530     // Parameters\r
3531     //\r
3532     if (argc > 1 && argv[1][0] != '-' && argv[1][0] != '/')\r
3533     {\r
3534         int ret = CommandLineRPC(argc, argv);\r
3535         exit(ret);\r
3536     }\r
3537 \r
3538     ParseParameters(argc, argv);\r
3539     if (mapArgs.count("-?") || mapArgs.count("--help"))\r
3540     {\r
3541 #ifdef __WXMSW__\r
3542         string strUsage =\r
3543             "Usage: bitcoin [options]\t\t\t\t\t\t\n"\r
3544             "Options:\n"\r
3545             "  -gen\t\t  Generate coins\n"\r
3546             "  -gen=0\t\t  Don't generate coins\n"\r
3547             "  -min\t\t  Start minimized\n"\r
3548             "  -datadir=<dir>\t  Specify data directory\n"\r
3549             "  -proxy=<ip:port>\t  Connect through socks4 proxy\n"\r
3550             "  -addnode=<ip>\t  Add a node to connect to\n"\r
3551             "  -connect=<ip>\t  Connect only to the specified node\n"\r
3552             "  -?\t\t  This help message\n";\r
3553         wxMessageBox(strUsage, "Bitcoin", wxOK);\r
3554 #else\r
3555         string strUsage =\r
3556             "Usage: bitcoin [options]\n"\r
3557             "Options:\n"\r
3558             "  -gen              Generate coins\n"\r
3559             "  -gen=0            Don't generate coins\n"\r
3560             "  -min              Start minimized\n"\r
3561             "  -datadir=<dir>    Specify data directory\n"\r
3562             "  -proxy=<ip:port>  Connect through socks4 proxy\n"\r
3563             "  -addnode=<ip>     Add a node to connect to\n"\r
3564             "  -connect=<ip>     Connect only to the specified node\n"\r
3565             "  -?                This help message\n";\r
3566         fprintf(stderr, "%s", strUsage.c_str());\r
3567 #endif\r
3568         return false;\r
3569     }\r
3570 \r
3571     if (mapArgs.count("-datadir"))\r
3572         strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir));\r
3573 \r
3574     if (mapArgs.count("-debug"))\r
3575         fDebug = true;\r
3576 \r
3577     if (mapArgs.count("-printtodebugger"))\r
3578         fPrintToDebugger = true;\r
3579 \r
3580     if (mapArgs.count("-daemon") || mapArgs.count("-d"))\r
3581     {\r
3582         fDaemon = true;\r
3583         /// todo: need to fork\r
3584         ///  should it fork after the bind/single instance stuff?\r
3585     }\r
3586 \r
3587     if (!fDebug && !pszSetDataDir[0])\r
3588         ShrinkDebugFile();\r
3589     printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");\r
3590     printf("Bitcoin version %d%s, OS version %s\n", VERSION, pszSubVer, ((string)wxGetOsDescription()).c_str());\r
3591 \r
3592     if (mapArgs.count("-loadblockindextest"))\r
3593     {\r
3594         CTxDB txdb("r");\r
3595         txdb.LoadBlockIndex();\r
3596         PrintBlockTree();\r
3597         return false;\r
3598     }\r
3599 \r
3600     //\r
3601     // Limit to single instance per user\r
3602     // Required to protect the database files if we're going to keep deleting log.*\r
3603     //\r
3604 #ifdef __WXMSW__\r
3605     // todo: wxSingleInstanceChecker wasn't working on Linux, never deleted its lock file\r
3606     //  maybe should go by whether successfully bind port 8333 instead\r
3607     wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH");\r
3608     for (int i = 0; i < strMutexName.size(); i++)\r
3609         if (!isalnum(strMutexName[i]))\r
3610             strMutexName[i] = '.';\r
3611     wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);\r
3612     if (psingleinstancechecker->IsAnotherRunning())\r
3613     {\r
3614         printf("Existing instance found\n");\r
3615         unsigned int nStart = GetTime();\r
3616         loop\r
3617         {\r
3618             // TODO: find out how to do this in Linux, or replace with wxWidgets commands\r
3619             // Show the previous instance and exit\r
3620             HWND hwndPrev = FindWindow("wxWindowClassNR", "Bitcoin");\r
3621             if (hwndPrev)\r
3622             {\r
3623                 if (IsIconic(hwndPrev))\r
3624                     ShowWindow(hwndPrev, SW_RESTORE);\r
3625                 SetForegroundWindow(hwndPrev);\r
3626                 return false;\r
3627             }\r
3628 \r
3629             if (GetTime() > nStart + 60)\r
3630                 return false;\r
3631 \r
3632             // Resume this instance if the other exits\r
3633             delete psingleinstancechecker;\r
3634             Sleep(1000);\r
3635             psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);\r
3636             if (!psingleinstancechecker->IsAnotherRunning())\r
3637                 break;\r
3638         }\r
3639     }\r
3640 #endif\r
3641 \r
3642     // Bind to the port early so we can tell if another instance is already running.\r
3643     // This is a backup to wxSingleInstanceChecker, which doesn't work on Linux.\r
3644     string strErrors;\r
3645     if (!BindListenPort(strErrors))\r
3646     {\r
3647         wxMessageBox(strErrors, "Bitcoin");\r
3648         return false;\r
3649     }\r
3650 \r
3651     //\r
3652     // Load data files\r
3653     //\r
3654     bool fFirstRun;\r
3655     strErrors = "";\r
3656     int64 nStart;\r
3657 \r
3658     printf("Loading addresses...\n");\r
3659     nStart = GetTimeMillis();\r
3660     if (!LoadAddresses())\r
3661         strErrors += "Error loading addr.dat      \n";\r
3662     printf(" addresses   %15"PRI64d"ms\n", GetTimeMillis() - nStart);\r
3663 \r
3664     printf("Loading block index...\n");\r
3665     nStart = GetTimeMillis();\r
3666     if (!LoadBlockIndex())\r
3667         strErrors += "Error loading blkindex.dat      \n";\r
3668     printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);\r
3669 \r
3670     printf("Loading wallet...\n");\r
3671     nStart = GetTimeMillis();\r
3672     if (!LoadWallet(fFirstRun))\r
3673         strErrors += "Error loading wallet.dat      \n";\r
3674     printf(" wallet      %15"PRI64d"ms\n", GetTimeMillis() - nStart);\r
3675 \r
3676     printf("Done loading\n");\r
3677 \r
3678         //// debug print\r
3679         printf("mapBlockIndex.size() = %d\n",   mapBlockIndex.size());\r
3680         printf("nBestHeight = %d\n",            nBestHeight);\r
3681         printf("mapKeys.size() = %d\n",         mapKeys.size());\r
3682         printf("mapPubKeys.size() = %d\n",      mapPubKeys.size());\r
3683         printf("mapWallet.size() = %d\n",       mapWallet.size());\r
3684         printf("mapAddressBook.size() = %d\n",  mapAddressBook.size());\r
3685 \r
3686     if (!strErrors.empty())\r
3687     {\r
3688         wxMessageBox(strErrors, "Bitcoin");\r
3689         return false;\r
3690     }\r
3691 \r
3692     // Add wallet transactions that aren't already in a block to mapTransactions\r
3693     ReacceptWalletTransactions();\r
3694 \r
3695     //\r
3696     // Parameters\r
3697     //\r
3698     if (mapArgs.count("-printblockindex") || mapArgs.count("-printblocktree"))\r
3699     {\r
3700         PrintBlockTree();\r
3701         return false;\r
3702     }\r
3703 \r
3704     if (mapArgs.count("-printblock"))\r
3705     {\r
3706         string strMatch = mapArgs["-printblock"];\r
3707         int nFound = 0;\r
3708         for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)\r
3709         {\r
3710             uint256 hash = (*mi).first;\r
3711             if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0)\r
3712             {\r
3713                 CBlockIndex* pindex = (*mi).second;\r
3714                 CBlock block;\r
3715                 block.ReadFromDisk(pindex);\r
3716                 block.BuildMerkleTree();\r
3717                 block.print();\r
3718                 printf("\n");\r
3719                 nFound++;\r
3720             }\r
3721         }\r
3722         if (nFound == 0)\r
3723             printf("No blocks matching %s were found\n", strMatch.c_str());\r
3724         return false;\r
3725     }\r
3726 \r
3727     if (mapArgs.count("-gen"))\r
3728     {\r
3729         if (mapArgs["-gen"].empty())\r
3730             fGenerateBitcoins = true;\r
3731         else\r
3732             fGenerateBitcoins = (atoi(mapArgs["-gen"].c_str()) != 0);\r
3733     }\r
3734 \r
3735     if (mapArgs.count("-proxy"))\r
3736     {\r
3737         fUseProxy = true;\r
3738         addrProxy = CAddress(mapArgs["-proxy"]);\r
3739         if (!addrProxy.IsValid())\r
3740         {\r
3741             wxMessageBox("Invalid -proxy address", "Bitcoin");\r
3742             return false;\r
3743         }\r
3744     }\r
3745 \r
3746     if (mapArgs.count("-addnode"))\r
3747     {\r
3748         foreach(string strAddr, mapMultiArgs["-addnode"])\r
3749         {\r
3750             CAddress addr(strAddr, NODE_NETWORK);\r
3751             addr.nTime = 0; // so it won't relay unless successfully connected\r
3752             if (addr.IsValid())\r
3753                 AddAddress(addr);\r
3754         }\r
3755     }\r
3756 \r
3757     //\r
3758     // Create the main frame window\r
3759     //\r
3760     if (!fDaemon)\r
3761     {\r
3762         pframeMain = new CMainFrame(NULL);\r
3763         if (mapArgs.count("-min"))\r
3764             pframeMain->Iconize(true);\r
3765         pframeMain->Show(true);  // have to show first to get taskbar button to hide\r
3766         if (fMinimizeToTray && pframeMain->IsIconized())\r
3767             fClosedToTray = true;\r
3768         pframeMain->Show(!fClosedToTray);\r
3769         ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);\r
3770 \r
3771         CreateThread(ThreadDelayedRepaint, NULL);\r
3772     }\r
3773 \r
3774     if (!CheckDiskSpace())\r
3775         return false;\r
3776 \r
3777     RandAddSeedPerfmon();\r
3778 \r
3779     if (!CreateThread(StartNode, NULL))\r
3780         wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin");\r
3781 \r
3782     if (mapArgs.count("-server") || fDaemon)\r
3783         CreateThread(ThreadRPCServer, NULL);\r
3784 \r
3785     if (fFirstRun)\r
3786         SetStartOnSystemStartup(true);\r
3787 \r
3788 \r
3789     //\r
3790     // Tests\r
3791     //\r
3792 #ifdef __WXMSW__\r
3793     if (argc >= 2 && stricmp(argv[1], "-send") == 0)\r
3794 #else\r
3795     if (argc >= 2 && strcmp(argv[1], "-send") == 0)\r
3796 #endif\r
3797     {\r
3798         int64 nValue = 1;\r
3799         if (argc >= 3)\r
3800             ParseMoney(argv[2], nValue);\r
3801 \r
3802         string strAddress;\r
3803         if (argc >= 4)\r
3804             strAddress = argv[3];\r
3805         CAddress addr(strAddress);\r
3806 \r
3807         CWalletTx wtx;\r
3808         wtx.mapValue["to"] = strAddress;\r
3809         wtx.mapValue["from"] = addrLocalHost.ToString();\r
3810         wtx.mapValue["message"] = "command line send";\r
3811 \r
3812         // Send to IP address\r
3813         CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx);\r
3814         if (!pdialog->ShowModal())\r
3815             return false;\r
3816     }\r
3817 \r
3818     return true;\r
3819 }\r
3820 \r
3821 int CMyApp::OnExit()\r
3822 {\r
3823     Shutdown(NULL);\r
3824     return wxApp::OnExit();\r
3825 }\r
3826 \r
3827 bool CMyApp::OnExceptionInMainLoop()\r
3828 {\r
3829     try\r
3830     {\r
3831         throw;\r
3832     }\r
3833     catch (std::exception& e)\r
3834     {\r
3835         PrintException(&e, "CMyApp::OnExceptionInMainLoop()");\r
3836         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());\r
3837         Sleep(1000);\r
3838         throw;\r
3839     }\r
3840     catch (...)\r
3841     {\r
3842         PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");\r
3843         wxLogWarning("Unknown exception");\r
3844         Sleep(1000);\r
3845         throw;\r
3846     }\r
3847 \r
3848     return true;\r
3849 }\r
3850 \r
3851 void CMyApp::OnUnhandledException()\r
3852 {\r
3853     // this shows how we may let some exception propagate uncaught\r
3854     try\r
3855     {\r
3856         throw;\r
3857     }\r
3858     catch (std::exception& e)\r
3859     {\r
3860         PrintException(&e, "CMyApp::OnUnhandledException()");\r
3861         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());\r
3862         Sleep(1000);\r
3863         throw;\r
3864     }\r
3865     catch (...)\r
3866     {\r
3867         PrintException(NULL, "CMyApp::OnUnhandledException()");\r
3868         wxLogWarning("Unknown exception");\r
3869         Sleep(1000);\r
3870         throw;\r
3871     }\r
3872 }\r
3873 \r
3874 void CMyApp::OnFatalException()\r
3875 {\r
3876     wxMessageBox("Program has crashed and will terminate.  ", "Bitcoin", wxOK | wxICON_ERROR);\r
3877 }\r
3878 \r
3879 \r
3880 \r
3881 \r
3882 \r
3883 #ifdef __WXMSW__\r
3884 typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);\r
3885 \r
3886 string MyGetSpecialFolderPath(int nFolder, bool fCreate)\r
3887 {\r
3888     char pszPath[MAX_PATH+100] = "";\r
3889 \r
3890     // SHGetSpecialFolderPath is not usually available on NT 4.0\r
3891     HMODULE hShell32 = LoadLibrary("shell32.dll");\r
3892     if (hShell32)\r
3893     {\r
3894         PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath =\r
3895             (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA");\r
3896         if (pSHGetSpecialFolderPath)\r
3897             (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate);\r
3898         FreeModule(hShell32);\r
3899     }\r
3900 \r
3901     // Backup option\r
3902     if (pszPath[0] == '\0')\r
3903     {\r
3904         if (nFolder == CSIDL_STARTUP)\r
3905         {\r
3906             strcpy(pszPath, getenv("USERPROFILE"));\r
3907             strcat(pszPath, "\\Start Menu\\Programs\\Startup");\r
3908         }\r
3909         else if (nFolder == CSIDL_APPDATA)\r
3910         {\r
3911             strcpy(pszPath, getenv("APPDATA"));\r
3912         }\r
3913     }\r
3914 \r
3915     return pszPath;\r
3916 }\r
3917 \r
3918 string StartupShortcutPath()\r
3919 {\r
3920     return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";\r
3921 }\r
3922 \r
3923 bool GetStartOnSystemStartup()\r
3924 {\r
3925     return wxFileExists(StartupShortcutPath());\r
3926 }\r
3927 \r
3928 void SetStartOnSystemStartup(bool fAutoStart)\r
3929 {\r
3930     // If the shortcut exists already, remove it for updating\r
3931     remove(StartupShortcutPath().c_str());\r
3932 \r
3933     if (fAutoStart)\r
3934     {\r
3935         CoInitialize(NULL);\r
3936 \r
3937         // Get a pointer to the IShellLink interface.\r
3938         IShellLink* psl = NULL;\r
3939         HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,\r
3940                                 CLSCTX_INPROC_SERVER, IID_IShellLink,\r
3941                                 reinterpret_cast<void**>(&psl));\r
3942 \r
3943         if (SUCCEEDED(hres))\r
3944         {\r
3945             // Get the current executable path\r
3946             char pszExePath[MAX_PATH];\r
3947             GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));\r
3948 \r
3949             // Set the path to the shortcut target\r
3950             psl->SetPath(pszExePath);\r
3951             PathRemoveFileSpec(pszExePath);\r
3952             psl->SetWorkingDirectory(pszExePath);\r
3953             psl->SetShowCmd(SW_SHOWMINNOACTIVE);\r
3954 \r
3955             // Query IShellLink for the IPersistFile interface for\r
3956             // saving the shortcut in persistent storage.\r
3957             IPersistFile* ppf = NULL;\r
3958             hres = psl->QueryInterface(IID_IPersistFile,\r
3959                                        reinterpret_cast<void**>(&ppf));\r
3960             if (SUCCEEDED(hres))\r
3961             {\r
3962                 WCHAR pwsz[MAX_PATH];\r
3963                 // Ensure that the string is ANSI.\r
3964                 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);\r
3965                 // Save the link by calling IPersistFile::Save.\r
3966                 hres = ppf->Save(pwsz, TRUE);\r
3967                 ppf->Release();\r
3968             }\r
3969             psl->Release();\r
3970         }\r
3971         CoUninitialize();\r
3972     }\r
3973 }\r
3974 #else\r
3975 bool GetStartOnSystemStartup() { return false; }\r
3976 void SetStartOnSystemStartup(bool fAutoStart) { }\r
3977 #endif\r
3978 \r
3979 \r
3980 \r
3981 \r
3982 \r
3983 \r
3984 \r
3985 \r
3986 \r
3987 \r