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