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