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