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
10 void ThreadRequestProductDetails(void* parg);
\r
11 void ThreadRandSendTest(void* parg);
\r
12 bool GetStartOnSystemStartup();
\r
13 void SetStartOnSystemStartup(bool fAutoStart);
\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
22 CMainFrame* pframeMain = NULL;
\r
23 CMyTaskBarIcon* ptaskbaricon = NULL;
\r
24 map<string, string> mapAddressBook;
\r
25 bool fRandSendTest = false;
\r
27 extern int g_isPainting;
\r
28 bool fClosedToTray = false;
\r
31 int fShowGenerated = true;
\r
32 int fMinimizeToTray = true;
\r
33 int fMinimizeOnClose = true;
\r
41 //////////////////////////////////////////////////////////////////////////////
\r
46 void HandleCtrlA(wxKeyEvent& event)
\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
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
64 string DateStr(int64 nTime)
\r
66 // Can only be used safely here in the UI
\r
67 return (string)wxDateTime((time_t)nTime).FormatDate();
\r
70 string DateTimeStr(int64 nTime)
\r
72 // Can only be used safely here in the UI
\r
73 wxDateTime datetime((time_t)nTime);
\r
75 return (string)datetime.Format("%x %H:%M");
\r
77 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
\r
80 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
\r
82 // Helper to simplify access to listctrl
\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
89 return item.GetText();
\r
92 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
\r
94 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
95 listCtrl->SetItem(nIndex, 1, str1);
\r
99 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\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
109 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\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
120 void SetSelection(wxListCtrl* listCtrl, int nIndex)
\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
128 int GetSelection(wxListCtrl* listCtrl)
\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
137 string HtmlEscape(const char* psz, bool fMultiLine=false)
\r
140 for (const char* p = psz; *p; p++)
\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
153 for (const char* p = psz; *p; p++)
\r
155 if (*p == '<') str += "<";
\r
156 else if (*p == '>') str += ">";
\r
157 else if (*p == '&') str += "&";
\r
158 else if (*p == '"') str += """;
\r
159 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
\r
160 else if (*p == '\n' && fMultiLine) str += "<br>\n";
\r
167 string HtmlEscape(const string& str, bool fMultiLine=false)
\r
169 return HtmlEscape(str.c_str(), fMultiLine);
\r
172 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
\r
174 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
\r
178 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
\r
180 if (mapArgs.count("-noui"))
\r
184 return wxMessageBox(message, caption, style, parent, x, y);
\r
186 if (wxThread::IsMain())
\r
188 return wxMessageBox(message, caption, style, parent, x, y);
\r
193 bool fDone = false;
\r
194 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
\r
211 //////////////////////////////////////////////////////////////////////////////
\r
215 // If this code gets used again, it should be replaced with something like UIThreadCall
\r
217 set<void*> setCallbackAvailable;
\r
218 CCriticalSection cs_setCallbackAvailable;
\r
220 void AddCallbackAvailable(void* p)
\r
222 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
223 setCallbackAvailable.insert(p);
\r
226 void RemoveCallbackAvailable(void* p)
\r
228 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
229 setCallbackAvailable.erase(p);
\r
232 bool IsCallbackAvailable(void* p)
\r
234 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
235 return setCallbackAvailable.count(p);
\r
239 template<typename T>
\r
240 void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T pbeginIn, const T pendIn)
\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
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
256 //pevthandler->AddPendingEvent(event);
\r
260 void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T& obj)
\r
264 AddPendingCustomEvent(pevthandler, nEventID, ss.begin(), ss.end());
\r
267 void AddPendingReplyEvent1(void* pevthandler, CDataStream& vRecv)
\r
269 if (IsCallbackAvailable(pevthandler))
\r
270 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY1, vRecv.begin(), vRecv.end());
\r
273 void AddPendingReplyEvent2(void* pevthandler, CDataStream& vRecv)
\r
275 if (IsCallbackAvailable(pevthandler))
\r
276 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY2, vRecv.begin(), vRecv.end());
\r
279 void AddPendingReplyEvent3(void* pevthandler, CDataStream& vRecv)
\r
281 if (IsCallbackAvailable(pevthandler))
\r
282 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY3, vRecv.begin(), vRecv.end());
\r
285 CDataStream GetStreamFromEvent(const wxCommandEvent& event)
\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
298 //////////////////////////////////////////////////////////////////////////////
\r
303 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
\r
305 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
\r
308 fRefreshListCtrl = false;
\r
309 fRefreshListCtrlRunning = false;
\r
310 fOnSetFocusAddress = false;
\r
312 m_choiceFilter->SetSelection(0);
\r
313 double dResize = 1.0;
\r
315 SetIcon(wxICON(bitcoin));
\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
329 SetSize(dResize * GetSize().GetWidth(), 1.1 * GetSize().GetHeight());
\r
331 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
332 m_listCtrl->SetFocus();
\r
333 ptaskbaricon = new CMyTaskBarIcon();
\r
335 // Init column headers
\r
336 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
\r
337 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
\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
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
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
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
366 int pnWidths[3] = { -100, 88, 290 };
\r
368 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
\r
369 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
\r
371 m_statusBar->SetFieldsCount(3, pnWidths);
\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
378 // Fill listctrl with wallet transactions
\r
382 CMainFrame::~CMainFrame()
\r
385 delete ptaskbaricon;
\r
386 ptaskbaricon = NULL;
\r
389 void ExitTimeout(void* parg)
\r
397 void Shutdown(void* parg)
\r
399 static CCriticalSection cs_Shutdown;
\r
400 static bool fTaken;
\r
402 CRITICAL_BLOCK(cs_Shutdown)
\r
404 fFirstThread = !fTaken;
\r
411 nTransactionsUpdated++;
\r
415 CreateThread(ExitTimeout, NULL);
\r
417 printf("Bitcoin exiting\n\n");
\r
430 void CMainFrame::OnClose(wxCloseEvent& event)
\r
432 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
\r
434 // Divert close to minimize
\r
436 fClosedToTray = true;
\r
442 CreateThread(Shutdown, NULL);
\r
446 void CMainFrame::OnIconize(wxIconizeEvent& event)
\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
455 // Tray is not reliable on Linux gnome
\r
456 fClosedToTray = false;
\r
458 if (fMinimizeToTray && event.Iconized())
\r
459 fClosedToTray = true;
\r
460 Show(!fClosedToTray);
\r
461 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
464 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
\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
471 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
\r
473 // Hidden columns not resizeable
\r
474 if (event.GetColumn() <= 1 && !fDebug)
\r
478 int CMainFrame::GetSortIndex(const string& strSort)
\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
489 int high = m_listCtrl->GetItemCount();
\r
492 int mid = low + ((high - low) / 2);
\r
493 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
\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
504 string str0 = strSort;
\r
505 long nData = *(long*)&hashKey;
\r
508 if (!fNew && nIndex == -1)
\r
510 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
511 if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())
\r
515 // fNew is for blind insert, only use if you're sure it's new
\r
516 if (fNew || nIndex == -1)
\r
518 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), str0);
\r
522 // If sort key changed, must delete and reinsert to make it relocate
\r
523 if (GetItemText(m_listCtrl, nIndex, 0) != str0)
\r
525 m_listCtrl->DeleteItem(nIndex);
\r
526 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), str0);
\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
539 bool CMainFrame::DeleteLine(uint256 hashKey)
\r
541 long nData = *(long*)&hashKey;
\r
545 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
546 if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())
\r
550 m_listCtrl->DeleteItem(nIndex);
\r
552 return nIndex != -1;
\r
555 string FormatTxStatus(const CWalletTx& wtx)
\r
558 if (!wtx.IsFinal())
\r
560 if (wtx.nLockTime < 500000000)
\r
561 return strprintf("Open for %d blocks", nBestHeight - wtx.nLockTime);
\r
563 return strprintf("Open until %s", DateTimeStr(wtx.nLockTime).c_str());
\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
573 return strprintf("%d confirmations", nDepth);
\r
577 string SingleLine(const string& strIn)
\r
580 bool fOneSpace = false;
\r
581 foreach(int c, strIn)
\r
589 if (fOneSpace && !strOut.empty())
\r
598 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
\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
611 if (wtx.IsCoinBase())
\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
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
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
624 if (wtx.GetDepthInMainChain() < 2)
\r
626 wtx.nLinesDisplayed = 0;
\r
630 // View->Show Generated
\r
631 if (!fShowGenerated)
\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
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
648 if (nNet > 0 || wtx.IsCoinBase())
\r
653 string strDescription;
\r
655 if (wtx.IsCoinBase())
\r
658 strDescription = "Generated";
\r
661 int64 nUnmatured = 0;
\r
662 foreach(const CTxOut& txout, wtx.vout)
\r
663 nUnmatured += txout.GetCredit();
\r
664 if (wtx.IsInMainChain())
\r
666 strDescription = strprintf("Generated (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\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
674 strDescription = "Generated (not accepted)";
\r
678 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
\r
680 // Online transaction
\r
681 if (!mapValue["from"].empty())
\r
682 strDescription += "From: " + mapValue["from"];
\r
683 if (!mapValue["message"].empty())
\r
685 if (!strDescription.empty())
\r
686 strDescription += " - ";
\r
687 strDescription += mapValue["message"];
\r
692 // Offline transaction
\r
693 foreach(const CTxOut& txout, wtx.vout)
\r
695 if (txout.IsMine())
\r
697 vector<unsigned char> vchPubKey;
\r
698 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
700 string strAddress = PubKeyToAddress(vchPubKey);
\r
701 if (mapAddressBook.count(strAddress))
\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
720 InsertLine(fNew, nIndex, hash, strSort,
\r
722 nTime ? DateTimeStr(nTime) : "",
\r
723 SingleLine(strDescription),
\r
725 FormatMoney(nNet, true));
\r
729 bool fAllFromMe = true;
\r
730 foreach(const CTxIn& txin, wtx.vin)
\r
731 fAllFromMe = fAllFromMe && txin.IsMine();
\r
733 bool fAllToMe = true;
\r
734 foreach(const CTxOut& txout, wtx.vout)
\r
735 fAllToMe = fAllToMe && txout.IsMine();
\r
737 if (fAllFromMe && fAllToMe)
\r
740 int64 nValue = wtx.vout[0].nValue;
\r
741 InsertLine(fNew, nIndex, hash, strSort,
\r
743 nTime ? DateTimeStr(nTime) : "",
\r
744 "Payment to yourself",
\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
751 else if (fAllFromMe)
\r
756 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
757 wtx.nLinesDisplayed = 0;
\r
758 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
\r
760 const CTxOut& txout = wtx.vout[nOut];
\r
761 if (txout.IsMine())
\r
765 if (!mapValue["to"].empty())
\r
767 // Online transaction
\r
768 strAddress = mapValue["to"];
\r
772 // Offline transaction
\r
774 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
775 strAddress = Hash160ToAddress(hash160);
\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
784 if (!strDescription.empty())
\r
785 strDescription += " - ";
\r
786 strDescription += mapValue["message"];
\r
789 int64 nValue = txout.nValue;
\r
790 if (nOut == 0 && nTxFee > 0)
\r
793 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
\r
795 nTime ? DateTimeStr(nTime) : "",
\r
796 SingleLine(strDescription),
\r
797 FormatMoney(-nValue, true),
\r
799 wtx.nLinesDisplayed++;
\r
805 // Mixed debit transaction, can't break down payees
\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
813 InsertLine(fNew, nIndex, hash, strSort,
\r
815 nTime ? DateTimeStr(nTime) : "",
\r
817 FormatMoney(nNet, true),
\r
825 void CMainFrame::RefreshListCtrl()
\r
827 fRefreshListCtrl = true;
\r
831 void CMainFrame::OnIdle(wxIdleEvent& event)
\r
833 if (fRefreshListCtrl)
\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
840 printf("RefreshListCtrl starting\n");
\r
842 fRefreshListCtrl = false;
\r
843 vWalletUpdated.clear();
\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
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
853 m_listCtrl->DeleteAllItems();
\r
858 sort(vSorted.begin(), vSorted.end());
\r
860 // Fill list control
\r
861 for (int i = 0; i < vSorted.size();)
\r
865 bool fEntered = false;
\r
866 TRY_CRITICAL_BLOCK(cs_mapWallet)
\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
874 if (!fEntered || i == 100 || i % 500 == 0)
\r
878 printf("RefreshListCtrl done\n");
\r
880 // Update transaction total display
\r
881 MainFrameRepaint();
\r
885 // Check for time updates
\r
886 static int64 nLastTime;
\r
887 if (GetTime() > nLastTime + 30)
\r
889 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
891 nLastTime = GetTime();
\r
892 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
894 CWalletTx& wtx = (*it).second;
\r
895 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
\r
896 InsertTransaction(wtx, false);
\r
903 void CMainFrame::RefreshStatusColumn()
\r
905 static int nLastTop;
\r
906 static CBlockIndex* pindexLastBest;
\r
907 static unsigned int nLastRefreshed;
\r
909 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
\r
910 if (nTop == nLastTop && pindexLastBest == pindexBest)
\r
913 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
916 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
\r
918 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
\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
927 pindexLastBest = pindexBest;
\r
928 nLastRefreshed = nListViewUpdated;
\r
930 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
\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
936 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
\r
939 CWalletTx& wtx = (*mi).second;
\r
940 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
\r
942 if (!InsertTransaction(wtx, false, nIndex))
\r
943 m_listCtrl->DeleteItem(nIndex--);
\r
946 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
\r
951 void CMainFrame::OnPaint(wxPaintEvent& event)
\r
962 unsigned int nNeedRepaint = 0;
\r
963 unsigned int nLastRepaint = 0;
\r
964 int64 nLastRepaintTime = 0;
\r
965 int64 nRepaintInterval = 500;
\r
967 void ThreadDelayedRepaint(void* parg)
\r
971 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
973 nLastRepaint = nNeedRepaint;
\r
976 printf("DelayedRepaint\n");
\r
977 wxPaintEvent event;
\r
978 pframeMain->fRefresh = true;
\r
979 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
982 Sleep(nRepaintInterval);
\r
986 void MainFrameRepaint()
\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
992 // Don't repaint too often
\r
993 static int64 nLastRepaintRequest;
\r
994 if (GetTimeMillis() - nLastRepaintRequest < 100)
\r
999 nLastRepaintRequest = GetTimeMillis();
\r
1001 printf("MainFrameRepaint\n");
\r
1002 wxPaintEvent event;
\r
1003 pframeMain->fRefresh = true;
\r
1004 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
1008 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
\r
1011 ptaskbaricon->UpdateTooltip();
\r
1016 static int nTransactionCount;
\r
1017 bool fPaintedBalance = false;
\r
1018 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
1020 nLastRepaint = nNeedRepaint;
\r
1021 nLastRepaintTime = GetTimeMillis();
\r
1023 // Update listctrl contents
\r
1024 if (!vWalletUpdated.empty())
\r
1026 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
1029 if (m_listCtrl->GetItemCount())
\r
1030 strTop = (string)m_listCtrl->GetItemText(0);
\r
1031 foreach(uint256 hash, vWalletUpdated)
\r
1033 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1034 if (mi != mapWallet.end())
\r
1035 InsertTransaction((*mi).second, false);
\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
1044 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
1046 fPaintedBalance = true;
\r
1047 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\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
1053 CWalletTx& wtx = (*it).second;
\r
1054 nTransactionCount += wtx.nLinesDisplayed;
\r
1058 if (!vWalletUpdated.empty() || !fPaintedBalance)
\r
1061 // Update status column of visible items only
\r
1062 RefreshStatusColumn();
\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
1072 string strStatus = strprintf(" %d connections %d blocks %d transactions", vNodes.size(), nBestHeight + 1, nTransactionCount);
\r
1073 m_statusBar->SetStatusText(strStatus, 2);
\r
1075 if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
\r
1076 m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0);
\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
1085 void UIThreadCall(boost::function0<void> fn)
\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
1095 wxCommandEvent event(wxEVT_UITHREADCALL);
\r
1096 event.SetClientData((void*)new boost::function0<void>(fn));
\r
1097 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
1101 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
\r
1103 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
\r
1108 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
\r
1114 void CMainFrame::OnMenuViewShowGenerated(wxCommandEvent& event)
\r
1116 // View->Show Generated
\r
1117 fShowGenerated = event.IsChecked();
\r
1118 CWalletDB().WriteSetting("fShowGenerated", fShowGenerated);
\r
1119 RefreshListCtrl();
\r
1122 void CMainFrame::OnUpdateUIViewShowGenerated(wxUpdateUIEvent& event)
\r
1124 event.Check(fShowGenerated);
\r
1127 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
\r
1129 // Options->Generate Coins
\r
1130 GenerateBitcoins(event.IsChecked());
\r
1133 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
\r
1135 event.Check(fGenerateBitcoins);
\r
1138 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
\r
1140 // Options->Change Your Address
\r
1141 OnButtonChange(event);
\r
1144 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
\r
1146 // Options->Options
\r
1147 COptionsDialog dialog(this);
\r
1148 dialog.ShowModal();
\r
1151 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
\r
1154 CAboutDialog dialog(this);
\r
1155 dialog.ShowModal();
\r
1158 void CMainFrame::OnButtonSend(wxCommandEvent& event)
\r
1161 CSendDialog dialog(this);
\r
1162 dialog.ShowModal();
\r
1165 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
\r
1167 // Toolbar: Address Book
\r
1168 CAddressBookDialog dialogAddr(this, "", false);
\r
1169 if (dialogAddr.ShowModal() == 2)
\r
1172 CSendDialog dialogSend(this, dialogAddr.GetAddress());
\r
1173 dialogSend.ShowModal();
\r
1177 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
\r
1179 // Automatically select-all when entering window
\r
1180 m_textCtrlAddress->SetSelection(-1, -1);
\r
1181 fOnSetFocusAddress = true;
\r
1185 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
\r
1187 if (fOnSetFocusAddress)
\r
1188 m_textCtrlAddress->SetSelection(-1, -1);
\r
1189 fOnSetFocusAddress = false;
\r
1193 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
\r
1195 // Copy address box to clipboard
\r
1196 if (wxTheClipboard->Open())
\r
1198 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
\r
1199 wxTheClipboard->Close();
\r
1203 void CMainFrame::OnButtonChange(wxCommandEvent& event)
\r
1205 CYourAddressDialog dialog(this, string(m_textCtrlAddress->GetValue()));
\r
1206 if (!dialog.ShowModal())
\r
1208 string strAddress = (string)dialog.GetAddress();
\r
1209 if (strAddress != m_textCtrlAddress->GetValue())
\r
1212 if (!AddressToHash160(strAddress, hash160))
\r
1214 if (!mapPubKeys.count(hash160))
\r
1216 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
\r
1217 m_textCtrlAddress->SetValue(strAddress);
\r
1221 void CMainFrame::OnListItemActivated(wxListEvent& event)
\r
1223 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
\r
1225 CRITICAL_BLOCK(cs_mapWallet)
\r
1227 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1228 if (mi == mapWallet.end())
\r
1230 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
\r
1233 wtx = (*mi).second;
\r
1235 CTxDetailsDialog dialog(this, wtx);
\r
1236 dialog.ShowModal();
\r
1237 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
\r
1238 //pdialog->Show();
\r
1241 void CMainFrame::OnListItemActivatedProductsSent(wxListEvent& event)
\r
1243 CProduct& product = *(CProduct*)event.GetItem().GetData();
\r
1244 CEditProductDialog* pdialog = new CEditProductDialog(this);
\r
1245 pdialog->SetProduct(product);
\r
1249 void CMainFrame::OnListItemActivatedOrdersSent(wxListEvent& event)
\r
1251 CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();
\r
1252 CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, false);
\r
1256 void CMainFrame::OnListItemActivatedOrdersReceived(wxListEvent& event)
\r
1258 CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();
\r
1259 CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, true);
\r
1269 //////////////////////////////////////////////////////////////////////////////
\r
1271 // CTxDetailsDialog
\r
1274 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
\r
1277 strHTML.reserve(4000);
\r
1278 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
\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
1287 strHTML += "<b>Status:</b> " + FormatTxStatus(wtx);
\r
1288 int nRequests = wtx.GetRequestCount();
\r
1289 if (nRequests != -1)
\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
1296 strHTML += strprintf(", broadcast through %d nodes", nRequests);
\r
1298 strHTML += "<br>";
\r
1300 strHTML += "<b>Date:</b> " + (nTime ? DateTimeStr(nTime) : "") + "<br>";
\r
1306 if (wtx.IsCoinBase())
\r
1308 strHTML += "<b>Source:</b> Generated<br>";
\r
1310 else if (!wtx.mapValue["from"].empty())
\r
1312 // Online transaction
\r
1313 if (!wtx.mapValue["from"].empty())
\r
1314 strHTML += "<b>From:</b> " + HtmlEscape(wtx.mapValue["from"]) + "<br>";
\r
1318 // Offline transaction
\r
1322 foreach(const CTxOut& txout, wtx.vout)
\r
1324 if (txout.IsMine())
\r
1326 vector<unsigned char> vchPubKey;
\r
1327 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
1329 string strAddress = PubKeyToAddress(vchPubKey);
\r
1330 if (mapAddressBook.count(strAddress))
\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
1338 strHTML += " (yours)";
\r
1339 strHTML += "<br>";
\r
1352 string strAddress;
\r
1353 if (!wtx.mapValue["to"].empty())
\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
1367 if (wtx.IsCoinBase() && nCredit == 0)
\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
1378 strHTML += "<b>Credit:</b> (not accepted)<br>";
\r
1380 else if (nNet > 0)
\r
1385 strHTML += "<b>Credit:</b> " + FormatMoney(nNet) + "<br>";
\r
1389 bool fAllFromMe = true;
\r
1390 foreach(const CTxIn& txin, wtx.vin)
\r
1391 fAllFromMe = fAllFromMe && txin.IsMine();
\r
1393 bool fAllToMe = true;
\r
1394 foreach(const CTxOut& txout, wtx.vout)
\r
1395 fAllToMe = fAllToMe && txout.IsMine();
\r
1402 foreach(const CTxOut& txout, wtx.vout)
\r
1404 if (txout.IsMine())
\r
1407 if (wtx.mapValue["to"].empty())
\r
1409 // Offline transaction
\r
1411 if (ExtractHash160(txout.scriptPubKey, hash160))
\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
1422 strHTML += "<b>Debit:</b> " + FormatMoney(-txout.nValue) + "<br>";
\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
1434 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
1436 strHTML += "<b>Transaction fee:</b> " + FormatMoney(-nTxFee) + "<br>";
\r
1441 // Mixed debit transaction
\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
1452 strHTML += "<b>Net amount:</b> " + FormatMoney(nNet, true) + "<br>";
\r
1458 if (!wtx.mapValue["message"].empty())
\r
1459 strHTML += "<br><b>Message:</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
\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
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
1478 strHTML += "<b>Inputs:</b><br>";
\r
1479 CRITICAL_BLOCK(cs_mapWallet)
\r
1481 foreach(const CTxIn& txin, wtx.vin)
\r
1483 COutPoint prevout = txin.prevout;
\r
1484 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
\r
1485 if (mi != mapWallet.end())
\r
1487 const CWalletTx& prev = (*mi).second;
\r
1488 if (prevout.n < prev.vout.size())
\r
1490 strHTML += HtmlEscape(prev.ToString(), true);
\r
1491 strHTML += " " + FormatTxStatus(prev) + ", ";
\r
1492 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
\r
1498 strHTML += "<br><hr><br><b>Transaction:</b><br>";
\r
1499 strHTML += HtmlEscape(wtx.ToString(), true);
\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
1510 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
\r
1520 //////////////////////////////////////////////////////////////////////////////
\r
1525 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
\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
1533 m_checkBoxMinimizeOnClose->SetLabel("&Minimize on close");
\r
1534 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
\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
1557 m_buttonOK->SetFocus();
\r
1560 void COptionsDialog::SelectPage(int nPage)
\r
1562 m_panelMain->Show(nPage == 0);
\r
1563 m_panelTest2->Show(nPage == 1);
\r
1565 m_scrolledWindow->Layout();
\r
1566 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
\r
1569 void COptionsDialog::OnListBox(wxCommandEvent& event)
\r
1571 SelectPage(event.GetSelection());
\r
1574 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
\r
1576 int64 nTmp = nTransactionFee;
\r
1577 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
\r
1578 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
\r
1581 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
\r
1583 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
\r
1586 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
\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
1594 CAddress COptionsDialog::GetProxyAddr()
\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
1607 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
\r
1609 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
\r
1610 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
\r
1614 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
\r
1616 OnButtonApply(event);
\r
1620 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
\r
1625 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
\r
1627 CWalletDB walletdb;
\r
1629 int64 nPrevTransactionFee = nTransactionFee;
\r
1630 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
\r
1631 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
\r
1633 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
\r
1634 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
\r
1636 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
\r
1637 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
\r
1639 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
\r
1641 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
\r
1642 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
\r
1644 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
\r
1645 GenerateBitcoins(fGenerateBitcoins);
\r
1647 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
\r
1649 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
\r
1650 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
\r
1653 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
\r
1655 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
\r
1656 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
\r
1657 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
1660 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
\r
1662 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
\r
1663 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
\r
1666 fUseProxy = m_checkBoxUseProxy->GetValue();
\r
1667 walletdb.WriteSetting("fUseProxy", fUseProxy);
\r
1669 addrProxy = GetProxyAddr();
\r
1670 walletdb.WriteSetting("addrProxy", addrProxy);
\r
1678 //////////////////////////////////////////////////////////////////////////////
\r
1683 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
\r
1685 m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d beta", VERSION/100, VERSION%100));
\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
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
1706 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
\r
1716 //////////////////////////////////////////////////////////////////////////////
\r
1721 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
\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
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
1740 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
\r
1741 SetIcon(iconSend);
\r
1743 wxCommandEvent event;
\r
1744 OnTextAddress(event);
\r
1746 // Fixup the tab order
\r
1747 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
\r
1748 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
\r
1752 void CSendDialog::OnTextAddress(wxCommandEvent& event)
\r
1755 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
\r
1756 m_bitmapCheckMark->Show(fBitcoinAddress);
\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
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
1772 else if (fEnable && !fEnabledPrev)
\r
1774 m_textCtrlFrom->SetValue(strFromSave);
\r
1775 m_textCtrlMessage->SetValue(strMessageSave);
\r
1777 fEnabledPrev = fEnable;
\r
1780 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
\r
1782 // Reformat the amount
\r
1783 if (m_textCtrlAmount->GetValue().Trim().empty())
\r
1786 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
\r
1787 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
\r
1790 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
\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
1798 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
\r
1800 // Copy clipboard to address box
\r
1801 if (wxTheClipboard->Open())
\r
1803 if (wxTheClipboard->IsSupported(wxDF_TEXT))
\r
1805 wxTextDataObject data;
\r
1806 wxTheClipboard->GetData(data);
\r
1807 m_textCtrlAddress->SetValue(data.GetText());
\r
1809 wxTheClipboard->Close();
\r
1813 void CSendDialog::OnButtonSend(wxCommandEvent& event)
\r
1816 string strAddress = (string)m_textCtrlAddress->GetValue();
\r
1820 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
\r
1822 wxMessageBox("Error in amount ", "Send Coins");
\r
1825 if (nValue > GetBalance())
\r
1827 wxMessageBox("Amount exceeds your balance ", "Send Coins");
\r
1830 if (nValue + nTransactionFee > GetBalance())
\r
1832 wxMessageBox(string("Total exceeds your balance when the ") + FormatMoney(nTransactionFee) + " transaction fee is included ", "Send Coins");
\r
1836 // Parse bitcoin address
\r
1838 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
\r
1840 if (fBitcoinAddress)
\r
1842 // Send to bitcoin address
\r
1843 CScript scriptPubKey;
\r
1844 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
\r
1846 if (!SendMoney(scriptPubKey, nValue, wtx))
\r
1849 wxMessageBox("Payment sent ", "Sending...");
\r
1853 // Parse IP address
\r
1854 CAddress addr(strAddress);
\r
1855 if (!addr.IsValid())
\r
1857 wxMessageBox("Invalid address ", "Send Coins");
\r
1862 wtx.mapValue["to"] = strAddress;
\r
1863 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
\r
1864 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
\r
1866 // Send to IP address
\r
1867 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
\r
1868 if (!pdialog->ShowModal())
\r
1872 if (!mapAddressBook.count(strAddress))
\r
1873 SetAddressBookName(strAddress, "");
\r
1878 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
\r
1889 //////////////////////////////////////////////////////////////////////////////
\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
1897 nPrice = nPriceIn;
\r
1899 start = wxDateTime::UNow();
\r
1900 memset(pszStatus, 0, sizeof(pszStatus));
\r
1901 fCanCancel = true;
\r
1905 fWorkDone = false;
\r
1907 SetSize(1.2 * GetSize().GetWidth(), 1.05 * GetSize().GetHeight());
\r
1910 SetTitle(strprintf("Sending %s to %s", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
\r
1911 m_textCtrlStatus->SetValue("");
\r
1913 CreateThread(SendingDialogStartTransfer, this);
\r
1916 CSendingDialog::~CSendingDialog()
\r
1918 printf("~CSendingDialog()\n");
\r
1921 void CSendingDialog::Close()
\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
1930 EndModal(fSuccess);
\r
1939 void CSendingDialog::OnClose(wxCloseEvent& event)
\r
1941 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
\r
1948 wxCommandEvent cmdevent;
\r
1949 OnButtonCancel(cmdevent);
\r
1953 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
\r
1959 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
\r
1965 void CSendingDialog::OnPaint(wxPaintEvent& event)
\r
1967 if (strlen(pszStatus) > 130)
\r
1968 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
\r
1970 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
\r
1971 m_staticTextSending->SetFocus();
\r
1973 m_buttonCancel->Enable(false);
\r
1976 m_buttonOK->Enable(true);
\r
1977 m_buttonOK->SetFocus();
\r
1978 m_buttonCancel->Enable(false);
\r
1980 if (fAbort && fCanCancel && IsShown())
\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
1988 wxMessageBox("Transfer cancelled ", "Sending...", wxOK, this);
\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
1999 void CSendingDialog::Repaint()
\r
2002 wxPaintEvent event;
\r
2003 GetEventHandler()->AddPendingEvent(event);
\r
2006 bool CSendingDialog::Status()
\r
2013 if (fAbort && fCanCancel)
\r
2015 memset(pszStatus, 0, 10);
\r
2016 strcpy(pszStatus, "CANCELLED");
\r
2024 bool CSendingDialog::Status(const string& str)
\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
2038 bool CSendingDialog::Error(const string& str)
\r
2040 fCanCancel = false;
\r
2042 Status(string("Error: ") + str);
\r
2046 void SendingDialogStartTransfer(void* parg)
\r
2048 ((CSendingDialog*)parg)->StartTransfer();
\r
2051 void CSendingDialog::StartTransfer()
\r
2053 // Make sure we have enough money
\r
2054 if (nPrice + nTransactionFee > GetBalance())
\r
2056 Error("You don't have enough money");
\r
2060 // We may have connected already for product details
\r
2061 if (!Status("Connecting..."))
\r
2063 CNode* pnode = ConnectNode(addr, 15 * 60);
\r
2066 Error("Unable to connect");
\r
2070 // Send order to seller, with response going to OnReply2 via event handler
\r
2071 if (!Status("Requesting public key..."))
\r
2073 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
\r
2076 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
\r
2078 ((CSendingDialog*)parg)->OnReply2(vRecv);
\r
2081 void CSendingDialog::OnReply2(CDataStream& vRecv)
\r
2083 if (!Status("Received public key..."))
\r
2086 CScript scriptPubKey;
\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
2099 vRecv >> scriptPubKey;
\r
2103 //// what do we want to do about this?
\r
2104 Error("Invalid response received");
\r
2108 // Pause to give the user a chance to cancel
\r
2109 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
\r
2116 CRITICAL_BLOCK(cs_main)
\r
2119 if (!Status("Creating transaction..."))
\r
2121 if (nPrice + nTransactionFee > GetBalance())
\r
2123 Error("You don't have enough money");
\r
2127 int64 nFeeRequired;
\r
2128 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
\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
2133 Error("Transaction creation failed");
\r
2137 // Make sure we're still connected
\r
2138 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
\r
2141 Error("Lost connection, transaction cancelled");
\r
2145 // Last chance to cancel
\r
2149 fCanCancel = false;
\r
2152 fCanCancel = true;
\r
2155 fCanCancel = false;
\r
2157 if (!Status("Sending payment..."))
\r
2161 if (!CommitTransactionSpent(wtx, key))
\r
2163 Error("Error finalizing payment");
\r
2167 // Send payment tx to seller, with response going to OnReply3 via event handler
\r
2168 pnode->PushRequest("submitorder", wtx, SendingDialogOnReply3, this);
\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
2175 Status("Waiting for confirmation...");
\r
2176 MainFrameRepaint();
\r
2180 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
\r
2182 ((CSendingDialog*)parg)->OnReply3(vRecv);
\r
2185 void CSendingDialog::OnReply3(CDataStream& vRecv)
\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
2201 //// what do we want to do about this?
\r
2202 Error("Payment was sent, but an invalid response was received");
\r
2208 Status("Payment completed");
\r
2216 //////////////////////////////////////////////////////////////////////////////
\r
2218 // CYourAddressDialog
\r
2221 CYourAddressDialog::CYourAddressDialog(wxWindow* parent, const string& strInitSelected) : CYourAddressDialogBase(parent)
\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
2228 // Fill listctrl with address book data
\r
2229 CRITICAL_BLOCK(cs_mapKeys)
\r
2231 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2233 string strAddress = item.first;
\r
2234 string strName = item.second;
\r
2236 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\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
2247 wxString CYourAddressDialog::GetAddress()
\r
2249 int nIndex = GetSelection(m_listCtrl);
\r
2252 return GetItemText(m_listCtrl, nIndex, 1);
\r
2255 void CYourAddressDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2257 // Update address book with edited name
\r
2258 if (event.IsEditCancelled())
\r
2260 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2261 SetAddressBookName(strAddress, string(event.GetText()));
\r
2262 pframeMain->RefreshListCtrl();
\r
2265 void CYourAddressDialog::OnListItemSelected(wxListEvent& event)
\r
2269 void CYourAddressDialog::OnListItemActivated(wxListEvent& event)
\r
2271 // Doubleclick edits item
\r
2272 wxCommandEvent event2;
\r
2273 OnButtonRename(event2);
\r
2276 void CYourAddressDialog::OnButtonRename(wxCommandEvent& event)
\r
2279 int nIndex = GetSelection(m_listCtrl);
\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
2287 strName = dialog.GetValue();
\r
2290 SetAddressBookName(strAddress, strName);
\r
2291 m_listCtrl->SetItemText(nIndex, strName);
\r
2292 pframeMain->RefreshListCtrl();
\r
2295 void CYourAddressDialog::OnButtonNew(wxCommandEvent& event)
\r
2298 CGetTextFromUserDialog dialog(this, "New Bitcoin Address", "Label", "");
\r
2299 if (!dialog.ShowModal())
\r
2301 string strName = dialog.GetValue();
\r
2303 // Generate new key
\r
2304 string strAddress = PubKeyToAddress(GenerateNewKey());
\r
2305 SetAddressBookName(strAddress, strName);
\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
2313 void CYourAddressDialog::OnButtonCopy(wxCommandEvent& event)
\r
2315 // Copy address box to clipboard
\r
2316 if (wxTheClipboard->Open())
\r
2318 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
\r
2319 wxTheClipboard->Close();
\r
2323 void CYourAddressDialog::OnButtonOK(wxCommandEvent& event)
\r
2329 void CYourAddressDialog::OnButtonCancel(wxCommandEvent& event)
\r
2335 void CYourAddressDialog::OnClose(wxCloseEvent& event)
\r
2346 //////////////////////////////////////////////////////////////////////////////
\r
2348 // CAddressBookDialog
\r
2351 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, bool fSendingIn) : CAddressBookDialogBase(parent)
\r
2353 fSending = fSendingIn;
\r
2355 m_buttonCancel->Show(false);
\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
2363 wxIcon iconAddressBook;
\r
2364 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
\r
2365 SetIcon(iconAddressBook);
\r
2367 // Fill listctrl with address book data
\r
2368 CRITICAL_BLOCK(cs_mapKeys)
\r
2370 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2372 string strAddress = item.first;
\r
2373 string strName = item.second;
\r
2375 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\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
2386 wxString CAddressBookDialog::GetAddress()
\r
2388 int nIndex = GetSelection(m_listCtrl);
\r
2391 return GetItemText(m_listCtrl, nIndex, 1);
\r
2394 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2396 // Update address book with edited name
\r
2397 if (event.IsEditCancelled())
\r
2399 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2400 SetAddressBookName(strAddress, string(event.GetText()));
\r
2401 pframeMain->RefreshListCtrl();
\r
2404 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
\r
2408 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
\r
2412 // Doubleclick returns selection
\r
2413 EndModal(GetAddress() != "" ? 2 : 0);
\r
2417 // Doubleclick edits item
\r
2418 wxCommandEvent event2;
\r
2419 OnButtonEdit(event2);
\r
2423 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
\r
2426 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2428 wxMessageBox("This is one of your own addresses for receiving payments and cannot be entered in the address book. ", strTitle);
\r
2432 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
\r
2435 int nIndex = GetSelection(m_listCtrl);
\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
2443 CGetTextFromUserDialog dialog(this, "Edit Address", "Name", strName, "Address", strAddress);
\r
2444 if (!dialog.ShowModal())
\r
2446 strName = dialog.GetValue1();
\r
2447 strAddress = dialog.GetValue2();
\r
2449 while (CheckIfMine(strAddress, "Edit Address"));
\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
2460 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
\r
2464 string strAddress;
\r
2467 CGetTextFromUserDialog dialog(this, "New Address", "Name", strName, "Address", strAddress);
\r
2468 if (!dialog.ShowModal())
\r
2470 strName = dialog.GetValue1();
\r
2471 strAddress = dialog.GetValue2();
\r
2473 while (CheckIfMine(strAddress, "New Address"));
\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
2483 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
\r
2485 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
\r
2487 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
\r
2489 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2490 CWalletDB().EraseName(strAddress);
\r
2491 m_listCtrl->DeleteItem(nIndex);
\r
2494 pframeMain->RefreshListCtrl();
\r
2497 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
\r
2499 // Copy address box to clipboard
\r
2500 if (wxTheClipboard->Open())
\r
2502 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
\r
2503 wxTheClipboard->Close();
\r
2507 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
\r
2510 EndModal(GetAddress() != "" ? 1 : 0);
\r
2513 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
\r
2519 void CAddressBookDialog::OnClose(wxCloseEvent& event)
\r
2530 //////////////////////////////////////////////////////////////////////////////
\r
2532 // CProductsDialog
\r
2535 bool CompareIntStringPairBestFirst(const pair<int, string>& item1, const pair<int, string>& item2)
\r
2537 return (item1.first > item2.first);
\r
2540 CProductsDialog::CProductsDialog(wxWindow* parent) : CProductsDialogBase(parent)
\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
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
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
2561 // Fill categories combo box
\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
2566 // Fill window with initial search
\r
2567 //wxCommandEvent event;
\r
2568 //OnButtonSearch(event);
\r
2571 void CProductsDialog::OnCombobox(wxCommandEvent& event)
\r
2573 OnButtonSearch(event);
\r
2576 bool CompareProductsBestFirst(const CProduct* p1, const CProduct* p2)
\r
2578 return (p1->nAtoms > p2->nAtoms);
\r
2581 void CProductsDialog::OnButtonSearch(wxCommandEvent& event)
\r
2583 string strCategory = (string)m_comboBoxCategory->GetValue();
\r
2584 string strSearch = (string)m_textCtrlSearch->GetValue();
\r
2586 // Search products
\r
2587 vector<CProduct*> vProductsFound;
\r
2588 CRITICAL_BLOCK(cs_mapProducts)
\r
2590 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)
\r
2592 CProduct& product = (*mi).second;
\r
2593 if (product.mapValue["category"].find(strCategory) != -1)
\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
2599 vProductsFound.push_back(&product);
\r
2606 sort(vProductsFound.begin(), vProductsFound.end(), CompareProductsBestFirst);
\r
2609 foreach(CProduct* pproduct, vProductsFound)
\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
2620 void CProductsDialog::OnListItemActivated(wxListEvent& event)
\r
2622 // Doubleclick opens product
\r
2623 CViewProductDialog* pdialog = new CViewProductDialog(this, m_vProduct[event.GetIndex()]);
\r
2633 //////////////////////////////////////////////////////////////////////////////
\r
2635 // CEditProductDialog
\r
2638 CEditProductDialog::CEditProductDialog(wxWindow* parent) : CEditProductDialogBase(parent)
\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
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
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
2703 for (int i = 1; i < FIELDS_MAX; i++)
\r
2704 ShowLine(i, false);
\r
2709 void CEditProductDialog::LayoutAll()
\r
2711 m_scrolledWindow->Layout();
\r
2712 m_scrolledWindow->GetSizer()->Fit(m_scrolledWindow);
\r
2716 void CEditProductDialog::ShowLine(int i, bool fShow)
\r
2718 m_textCtrlLabel[i]->Show(fShow);
\r
2719 m_textCtrlField[i]->Show(fShow);
\r
2720 m_buttonDel[i]->Show(fShow);
\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
2744 void CEditProductDialog::OnButtonDel(wxCommandEvent& event, int n)
\r
2748 m_scrolledWindow->GetViewStart(&x, &y);
\r
2750 for (i = n; i < FIELDS_MAX-1; i++)
\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
2757 m_textCtrlLabel[i]->SetValue("");
\r
2758 m_textCtrlField[i]->SetValue("");
\r
2759 ShowLine(i, false);
\r
2760 m_buttonAddField->Enable(true);
\r
2762 m_scrolledWindow->Scroll(0, y);
\r
2766 void CEditProductDialog::OnButtonAddField(wxCommandEvent& event)
\r
2768 for (int i = 0; i < FIELDS_MAX; i++)
\r
2770 if (!m_buttonDel[i]->IsShown())
\r
2773 ShowLine(i, true);
\r
2774 if (i == FIELDS_MAX-1)
\r
2775 m_buttonAddField->Enable(false);
\r
2777 m_scrolledWindow->Scroll(0, 99999);
\r
2784 void AddToMyProducts(CProduct product)
\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
2796 void CEditProductDialog::OnButtonSend(wxCommandEvent& event)
\r
2799 GetProduct(product);
\r
2801 // Sign the detailed product
\r
2802 product.vchPubKeyFrom = keyUser.GetPubKey();
\r
2803 if (!keyUser.Sign(product.GetSigHash(), product.vchSig))
\r
2805 wxMessageBox("Error digitally signing the product ");
\r
2809 // Save detailed product
\r
2810 AddToMyProducts(product);
\r
2812 // Strip down to summary product
\r
2813 product.mapDetails.clear();
\r
2814 product.vOrderForm.clear();
\r
2816 // Sign the summary product
\r
2817 if (!keyUser.Sign(product.GetSigHash(), product.vchSig))
\r
2819 wxMessageBox("Error digitally signing the product ");
\r
2824 if (!product.CheckProduct())
\r
2826 wxMessageBox("Errors found in product ");
\r
2831 AdvertStartPublish(pnodeLocalHost, MSG_PRODUCT, 0, product);
\r
2836 void CEditProductDialog::OnButtonPreview(wxCommandEvent& event)
\r
2839 GetProduct(product);
\r
2840 CViewProductDialog* pdialog = new CViewProductDialog(this, product);
\r
2844 void CEditProductDialog::OnButtonCancel(wxCommandEvent& event)
\r
2849 void CEditProductDialog::SetProduct(const CProduct& productIn)
\r
2851 CProduct product = productIn;
\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
2859 for (int i = 0; i < FIELDS_MAX; i++)
\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
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
2875 m_textCtrlField[i]->SetValue(strControl);
\r
2879 void CEditProductDialog::GetProduct(CProduct& product)
\r
2881 // map<string, string> mapValue;
\r
2882 // vector<pair<string, string> > vOrderForm;
\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
2890 for (int i = 0; i < FIELDS_MAX; i++)
\r
2892 if (m_buttonDel[i]->IsShown())
\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
2899 strControl = "choice=" + strControl;
\r
2900 product.vOrderForm.push_back(make_pair(strLabel, strControl));
\r
2911 //////////////////////////////////////////////////////////////////////////////
\r
2913 // CViewProductDialog
\r
2916 CViewProductDialog::CViewProductDialog(wxWindow* parent, const CProduct& productIn) : CViewProductDialogBase(parent)
\r
2918 Connect(wxEVT_REPLY1, wxCommandEventHandler(CViewProductDialog::OnReply1), NULL, this);
\r
2919 AddCallbackAvailable(GetEventHandler());
\r
2921 // Fill display with product summary while waiting for details
\r
2922 product = productIn;
\r
2923 UpdateProductDisplay(false);
\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
2931 // Request details from seller
\r
2932 CreateThread(ThreadRequestProductDetails, new pair<CProduct, wxEvtHandler*>(product, GetEventHandler()));
\r
2935 CViewProductDialog::~CViewProductDialog()
\r
2937 RemoveCallbackAvailable(GetEventHandler());
\r
2940 void ThreadRequestProductDetails(void* parg)
\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
2948 // Connect to seller
\r
2949 CNode* pnode = ConnectNode(product.addr, 5 * 60);
\r
2952 CDataStream ssEmpty;
\r
2953 AddPendingReplyEvent1(pevthandler, ssEmpty);
\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
2961 void CViewProductDialog::OnReply1(wxCommandEvent& event)
\r
2963 CDataStream ss = GetStreamFromEvent(event);
\r
2966 product.mapValue["description"] = "-- CAN'T CONNECT TO SELLER --\n";
\r
2967 UpdateProductDisplay(true);
\r
2972 CProduct product2;
\r
2979 if (product2.GetHash() != product.GetHash())
\r
2981 if (!product2.CheckSignature())
\r
2986 product.mapValue["description"] = "-- INVALID RESPONSE --\n";
\r
2987 UpdateProductDisplay(true);
\r
2991 product = product2;
\r
2992 UpdateProductDisplay(true);
\r
2995 bool CompareReviewsBestFirst(const CReview* p1, const CReview* p2)
\r
2997 return (p1->nAtoms > p2->nAtoms);
\r
3000 void CViewProductDialog::UpdateProductDisplay(bool fDetails)
\r
3002 // Product and reviews
\r
3004 strHTML.reserve(4000);
\r
3005 strHTML += "<html>\n"
\r
3007 "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\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
3015 strHTML += "<b>Loading details...</b><br>\n<br>\n";
\r
3017 strHTML += HtmlEscape(product.mapValue["description"], true) + "<br>\n<br>\n";
\r
3019 strHTML += "<b>Reviews:</b><br>\n<br>\n";
\r
3021 if (!product.vchPubKeyFrom.empty())
\r
3023 CReviewDB reviewdb("r");
\r
3026 vector<CReview> vReviews;
\r
3027 reviewdb.ReadReviews(product.GetUserHash(), vReviews);
\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
3034 CReview& review = *it;
\r
3036 reviewdb.ReadUser(review.GetUserHash(), user);
\r
3037 review.nAtoms = user.GetAtomCount();
\r
3038 vSortedReviews.push_back(&review);
\r
3044 stable_sort(vSortedReviews.begin(), vSortedReviews.end(), CompareReviewsBestFirst);
\r
3047 foreach(CReview* preview, vSortedReviews)
\r
3049 CReview& review = *preview;
\r
3050 int nStars = atoi(review.mapValue["stars"].c_str());
\r
3051 if (nStars < 1 || nStars > 5)
\r
3054 strHTML += "<b>" + itostr(nStars) + (nStars == 1 ? " star" : " stars") + "</b>";
\r
3055 strHTML += " ";
\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
3062 strHTML += "</body>\n</html>\n";
\r
3064 // Shrink capacity to fit
\r
3065 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
3067 m_htmlWinReviews->SetPage(strHTML);
\r
3069 ///// need to find some other indicator to use so can allow empty order form
\r
3070 if (product.vOrderForm.empty())
\r
3074 m_staticTextInstructions->SetLabel(product.mapValue["instructions"]);
\r
3075 for (int i = 0; i < FIELDS_MAX; i++)
\r
3077 m_staticTextLabel[i] = NULL;
\r
3078 m_textCtrlField[i] = NULL;
\r
3079 m_choiceField[i] = NULL;
\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
3090 // Construct order form fields
\r
3091 wxWindow* windowLast = NULL;
\r
3092 for (int i = 0; i < product.vOrderForm.size(); i++)
\r
3094 string strLabel = product.vOrderForm[i].first;
\r
3095 string strControl = product.vOrderForm[i].second;
\r
3097 if (strLabel.size() < 20)
\r
3098 strLabel.insert(strLabel.begin(), 20 - strLabel.size(), ' ');
\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
3104 if (strControl.substr(0, 5) == "text=")
\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
3110 else if (strControl.substr(0, 7) == "choice=")
\r
3112 vector<string> vChoices;
\r
3113 ParseString(strControl.substr(7), ',', vChoices);
\r
3115 wxArrayString arraystring;
\r
3116 foreach(const string& str, vChoices)
\r
3117 arraystring.Add(str);
\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
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
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
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
3147 void CViewProductDialog::GetOrder(CWalletTx& wtx)
\r
3150 for (int i = 0; i < product.vOrderForm.size(); i++)
\r
3153 if (m_textCtrlField[i])
\r
3154 strValue = m_textCtrlField[i]->GetValue().Trim();
\r
3156 strValue = m_choiceField[i]->GetStringSelection();
\r
3157 wtx.vOrderForm.push_back(make_pair(m_staticTextLabel[i]->GetLabel(), strValue));
\r
3161 void CViewProductDialog::OnButtonSubmitForm(wxCommandEvent& event)
\r
3163 m_buttonSubmitForm->Enable(false);
\r
3164 m_buttonCancelForm->Enable(false);
\r
3169 CSendingDialog* pdialog = new CSendingDialog(this, product.addr, atoi64(product.mapValue["price"]), wtx);
\r
3170 if (!pdialog->ShowModal())
\r
3172 m_buttonSubmitForm->Enable(true);
\r
3173 m_buttonCancelForm->Enable(true);
\r
3178 void CViewProductDialog::OnButtonCancelForm(wxCommandEvent& event)
\r
3183 void CViewProductDialog::OnButtonBack(wxCommandEvent& event)
\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
3194 void CViewProductDialog::OnButtonNext(wxCommandEvent& event)
\r
3196 if (!product.vOrderForm.empty())
\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
3208 void CViewProductDialog::OnButtonCancel(wxCommandEvent& event)
\r
3219 //////////////////////////////////////////////////////////////////////////////
\r
3221 // CViewOrderDialog
\r
3224 CViewOrderDialog::CViewOrderDialog(wxWindow* parent, CWalletTx order, bool fReceived) : CViewOrderDialogBase(parent)
\r
3226 int64 nPrice = (fReceived ? order.GetCredit() : order.GetDebit());
\r
3229 strHTML.reserve(4000);
\r
3230 strHTML += "<html>\n"
\r
3232 "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\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
3239 strHTML += "<table>\n";
\r
3240 for (int i = 0; i < order.vOrderForm.size(); i++)
\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
3245 strHTML += "</table>\n";
\r
3247 strHTML += "</body>\n</html>\n";
\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
3253 m_htmlWin->SetPage(strHTML);
\r
3256 void CViewOrderDialog::OnButtonOK(wxCommandEvent& event)
\r
3267 //////////////////////////////////////////////////////////////////////////////
\r
3269 // CEditReviewDialog
\r
3272 CEditReviewDialog::CEditReviewDialog(wxWindow* parent) : CEditReviewDialogBase(parent)
\r
3276 void CEditReviewDialog::OnButtonSubmit(wxCommandEvent& event)
\r
3278 if (m_choiceStars->GetSelection() == -1)
\r
3280 wxMessageBox("Please select a rating ");
\r
3285 GetReview(review);
\r
3287 // Sign the review
\r
3288 review.vchPubKeyFrom = keyUser.GetPubKey();
\r
3289 if (!keyUser.Sign(review.GetSigHash(), review.vchSig))
\r
3291 wxMessageBox("Unable to digitally sign the review ");
\r
3296 if (!review.AcceptReview())
\r
3298 wxMessageBox("Save failed ");
\r
3301 RelayMessage(CInv(MSG_REVIEW, review.GetHash()), review);
\r
3306 void CEditReviewDialog::OnButtonCancel(wxCommandEvent& event)
\r
3311 void CEditReviewDialog::GetReview(CReview& review)
\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
3324 //////////////////////////////////////////////////////////////////////////////
\r
3331 ID_TASKBAR_RESTORE = 10001,
\r
3332 ID_TASKBAR_OPTIONS,
\r
3333 ID_TASKBAR_GENERATE,
\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
3346 void CMyTaskBarIcon::Show(bool fShow)
\r
3348 static char pszPrevTip[200];
\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
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
3360 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
\r
3362 SetIcon(wxICON(bitcoin), strTooltip);
\r
3364 SetIcon(bitcoin20_xpm, strTooltip);
\r
3370 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
\r
3375 void CMyTaskBarIcon::Hide()
\r
3380 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
\r
3385 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
\r
3390 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
\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
3397 void CMyTaskBarIcon::Restore()
\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
3406 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
\r
3408 GenerateBitcoins(event.IsChecked());
\r
3411 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
\r
3413 event.Check(fGenerateBitcoins);
\r
3416 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
\r
3418 pframeMain->Close(true);
\r
3421 void CMyTaskBarIcon::UpdateTooltip()
\r
3423 if (IsIconInstalled())
\r
3427 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
\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
3449 //////////////////////////////////////////////////////////////////////////////
\r
3454 // Define a new application
\r
3455 class CMyApp: public wxApp
\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
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
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
3477 IMPLEMENT_APP(CMyApp)
\r
3479 bool CMyApp::OnInit()
\r
3481 bool fRet = false;
\r
3486 catch (std::exception& e) {
\r
3487 PrintException(&e, "OnInit()");
\r
3489 PrintException(NULL, "OnInit()");
\r
3496 bool CMyApp::OnInit2()
\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
3503 #if defined(__WXMSW__) && defined(__WXDEBUG__)
\r
3504 // Disable malfunctioning wxWidgets debug assertion
\r
3505 g_isPainting = 10000;
\r
3507 wxImage::AddHandler(new wxPNGHandler);
\r
3509 SetAppName("Bitcoin");
\r
3511 SetAppName("bitcoin");
\r
3518 ParseParameters(argc, argv);
\r
3519 if (mapArgs.count("-?") || mapArgs.count("--help"))
\r
3523 "Usage: bitcoin [options]\t\t\t\t\t\t\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
3536 "Usage: bitcoin [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
3551 if (mapArgs.count("-datadir"))
\r
3552 strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir));
\r
3554 if (mapArgs.count("-debug"))
\r
3557 if (mapArgs.count("-printtodebugger"))
\r
3558 fPrintToDebugger = true;
\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
3565 if (mapArgs.count("-loadblockindextest"))
\r
3568 txdb.LoadBlockIndex();
\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
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
3587 printf("Existing instance found\n");
\r
3588 unsigned int nStart = GetTime();
\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
3596 if (IsIconic(hwndPrev))
\r
3597 ShowWindow(hwndPrev, SW_RESTORE);
\r
3598 SetForegroundWindow(hwndPrev);
\r
3602 if (GetTime() > nStart + 60)
\r
3605 // Resume this instance if the other exits
\r
3606 delete psingleinstancechecker;
\r
3608 psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
\r
3609 if (!psingleinstancechecker->IsAnotherRunning())
\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
3618 if (!BindListenPort(strErrors))
\r
3620 wxMessageBox(strErrors, "Bitcoin");
\r
3625 // Load data files
\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
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
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
3649 printf("Done loading\n");
\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
3659 if (!strErrors.empty())
\r
3661 wxMessageBox(strErrors, "Bitcoin");
\r
3665 // Add wallet transactions that aren't already in a block to mapTransactions
\r
3666 ReacceptWalletTransactions();
\r
3671 if (mapArgs.count("-printblockindex") || mapArgs.count("-printblocktree"))
\r
3677 if (mapArgs.count("-printblock"))
\r
3679 string strMatch = mapArgs["-printblock"];
\r
3681 for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
\r
3683 uint256 hash = (*mi).first;
\r
3684 if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0)
\r
3686 CBlockIndex* pindex = (*mi).second;
\r
3688 block.ReadFromDisk(pindex);
\r
3689 block.BuildMerkleTree();
\r
3696 printf("No blocks matching %s were found\n", strMatch.c_str());
\r
3700 if (mapArgs.count("-gen"))
\r
3702 if (mapArgs["-gen"].empty())
\r
3703 fGenerateBitcoins = true;
\r
3705 fGenerateBitcoins = (atoi(mapArgs["-gen"].c_str()) != 0);
\r
3708 if (mapArgs.count("-proxy"))
\r
3711 addrProxy = CAddress(mapArgs["-proxy"]);
\r
3712 if (!addrProxy.IsValid())
\r
3714 wxMessageBox("Invalid -proxy address", "Bitcoin");
\r
3719 if (mapArgs.count("-addnode"))
\r
3721 foreach(string strAddr, mapMultiArgs["-addnode"])
\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
3731 // Create the main frame window
\r
3733 if (!mapArgs.count("-noui"))
\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
3744 CreateThread(ThreadDelayedRepaint, NULL);
\r
3747 if (!CheckDiskSpace())
\r
3750 RandAddSeedPerfmon();
\r
3752 if (!CreateThread(StartNode, NULL))
\r
3753 wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin");
\r
3756 SetStartOnSystemStartup(true);
\r
3763 if (argc >= 2 && stricmp(argv[1], "-send") == 0)
\r
3765 if (argc >= 2 && strcmp(argv[1], "-send") == 0)
\r
3770 ParseMoney(argv[2], nValue);
\r
3772 string strAddress;
\r
3774 strAddress = argv[3];
\r
3775 CAddress addr(strAddress);
\r
3778 wtx.mapValue["to"] = strAddress;
\r
3779 wtx.mapValue["from"] = addrLocalHost.ToString();
\r
3780 wtx.mapValue["message"] = "command line send";
\r
3782 // Send to IP address
\r
3783 CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx);
\r
3784 if (!pdialog->ShowModal())
\r
3791 int CMyApp::OnExit()
\r
3794 return wxApp::OnExit();
\r
3797 bool CMyApp::OnExceptionInMainLoop()
\r
3803 catch (std::exception& e)
\r
3805 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
\r
3806 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
3812 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
\r
3813 wxLogWarning("Unknown exception");
\r
3821 void CMyApp::OnUnhandledException()
\r
3823 // this shows how we may let some exception propagate uncaught
\r
3828 catch (std::exception& e)
\r
3830 PrintException(&e, "CMyApp::OnUnhandledException()");
\r
3831 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
3837 PrintException(NULL, "CMyApp::OnUnhandledException()");
\r
3838 wxLogWarning("Unknown exception");
\r
3844 void CMyApp::OnFatalException()
\r
3846 wxMessageBox("Program has crashed and will terminate. ", "Bitcoin", wxOK | wxICON_ERROR);
\r
3854 typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
\r
3856 string MyGetSpecialFolderPath(int nFolder, bool fCreate)
\r
3858 char pszPath[MAX_PATH+100] = "";
\r
3860 // SHGetSpecialFolderPath is not usually available on NT 4.0
\r
3861 HMODULE hShell32 = LoadLibrary("shell32.dll");
\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
3872 if (pszPath[0] == '\0')
\r
3874 if (nFolder == CSIDL_STARTUP)
\r
3876 strcpy(pszPath, getenv("USERPROFILE"));
\r
3877 strcat(pszPath, "\\Start Menu\\Programs\\Startup");
\r
3879 else if (nFolder == CSIDL_APPDATA)
\r
3881 strcpy(pszPath, getenv("APPDATA"));
\r
3888 string StartupShortcutPath()
\r
3890 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
\r
3893 bool GetStartOnSystemStartup()
\r
3895 return wxFileExists(StartupShortcutPath());
\r
3898 void SetStartOnSystemStartup(bool fAutoStart)
\r
3900 // If the shortcut exists already, remove it for updating
\r
3901 remove(StartupShortcutPath().c_str());
\r
3905 CoInitialize(NULL);
\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
3913 if (SUCCEEDED(hres))
\r
3915 // Get the current executable path
\r
3916 char pszExePath[MAX_PATH];
\r
3917 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
\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
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
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
3945 bool GetStartOnSystemStartup() { return false; }
\r
3946 void SetStartOnSystemStartup(bool fAutoStart) { }
\r