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 bool fRandSendTest = false;
\r
26 extern int g_isPainting;
\r
27 bool fClosedToTray = false;
\r
30 int fShowGenerated = true;
\r
31 int fMinimizeToTray = true;
\r
32 int fMinimizeOnClose = true;
\r
40 //////////////////////////////////////////////////////////////////////////////
\r
45 void HandleCtrlA(wxKeyEvent& event)
\r
47 // Ctrl-a select all
\r
48 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
\r
49 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
\r
50 textCtrl->SetSelection(-1, -1);
\r
56 //char pszHourFormat[256];
\r
57 //pszHourFormat[0] = '\0';
\r
58 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
\r
59 //return (pszHourFormat[0] != '0');
\r
63 string DateStr(int64 nTime)
\r
65 // Can only be used safely here in the UI
\r
66 return (string)wxDateTime((time_t)nTime).FormatDate();
\r
69 string DateTimeStr(int64 nTime)
\r
71 // Can only be used safely here in the UI
\r
72 wxDateTime datetime((time_t)nTime);
\r
74 return (string)datetime.Format("%x %H:%M");
\r
76 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
\r
79 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
\r
81 // Helper to simplify access to listctrl
\r
83 item.m_itemId = nIndex;
\r
84 item.m_col = nColumn;
\r
85 item.m_mask = wxLIST_MASK_TEXT;
\r
86 if (!listCtrl->GetItem(item))
\r
88 return item.GetText();
\r
91 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
\r
93 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
94 listCtrl->SetItem(nIndex, 1, str1);
\r
98 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
100 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
101 listCtrl->SetItem(nIndex, 1, str1);
\r
102 listCtrl->SetItem(nIndex, 2, str2);
\r
103 listCtrl->SetItem(nIndex, 3, str3);
\r
104 listCtrl->SetItem(nIndex, 4, str4);
\r
108 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
110 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
111 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
\r
112 listCtrl->SetItem(nIndex, 1, str1);
\r
113 listCtrl->SetItem(nIndex, 2, str2);
\r
114 listCtrl->SetItem(nIndex, 3, str3);
\r
115 listCtrl->SetItem(nIndex, 4, str4);
\r
119 void SetSelection(wxListCtrl* listCtrl, int nIndex)
\r
121 int nSize = listCtrl->GetItemCount();
\r
122 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
123 for (int i = 0; i < nSize; i++)
\r
124 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
\r
127 int GetSelection(wxListCtrl* listCtrl)
\r
129 int nSize = listCtrl->GetItemCount();
\r
130 for (int i = 0; i < nSize; i++)
\r
131 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
\r
136 string HtmlEscape(const char* psz, bool fMultiLine=false)
\r
139 for (const char* p = psz; *p; p++)
\r
141 if (*p == '<') len += 4;
\r
142 else if (*p == '>') len += 4;
\r
143 else if (*p == '&') len += 5;
\r
144 else if (*p == '"') len += 6;
\r
145 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
\r
146 else if (*p == '\n' && fMultiLine) len += 5;
\r
152 for (const char* p = psz; *p; p++)
\r
154 if (*p == '<') str += "<";
\r
155 else if (*p == '>') str += ">";
\r
156 else if (*p == '&') str += "&";
\r
157 else if (*p == '"') str += """;
\r
158 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
\r
159 else if (*p == '\n' && fMultiLine) str += "<br>\n";
\r
166 string HtmlEscape(const string& str, bool fMultiLine=false)
\r
168 return HtmlEscape(str.c_str(), fMultiLine);
\r
171 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
\r
173 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
\r
177 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
\r
181 printf("wxMessageBox %s: %s\n", caption.c_str(), message.c_str());
\r
186 return wxMessageBox(message, caption, style, parent, x, y);
\r
188 if (wxThread::IsMain())
\r
190 return wxMessageBox(message, caption, style, parent, x, y);
\r
195 bool fDone = false;
\r
196 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
\r
213 //////////////////////////////////////////////////////////////////////////////
\r
217 // If this code gets used again, it should be replaced with something like UIThreadCall
\r
219 set<void*> setCallbackAvailable;
\r
220 CCriticalSection cs_setCallbackAvailable;
\r
222 void AddCallbackAvailable(void* p)
\r
224 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
225 setCallbackAvailable.insert(p);
\r
228 void RemoveCallbackAvailable(void* p)
\r
230 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
231 setCallbackAvailable.erase(p);
\r
234 bool IsCallbackAvailable(void* p)
\r
236 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
237 return setCallbackAvailable.count(p);
\r
241 template<typename T>
\r
242 void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T pbeginIn, const T pendIn)
\r
244 // Need to rewrite with something like UIThreadCall
\r
245 // I'm tired of maintaining this hack that's only called by unfinished unused code.
\r
246 assert(("Unimplemented", 0));
\r
247 //if (!pevthandler)
\r
250 //const char* pbegin = (pendIn != pbeginIn) ? &pbeginIn[0] : NULL;
\r
251 //const char* pend = pbegin + (pendIn - pbeginIn) * sizeof(pbeginIn[0]);
\r
252 //wxCommandEvent event(nEventID);
\r
253 //wxString strData(wxChar(0), (pend - pbegin) / sizeof(wxChar) + 1);
\r
254 //memcpy(&strData[0], pbegin, pend - pbegin);
\r
255 //event.SetString(strData);
\r
256 //event.SetInt(pend - pbegin);
\r
258 //pevthandler->AddPendingEvent(event);
\r
262 void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T& obj)
\r
266 AddPendingCustomEvent(pevthandler, nEventID, ss.begin(), ss.end());
\r
269 void AddPendingReplyEvent1(void* pevthandler, CDataStream& vRecv)
\r
271 if (IsCallbackAvailable(pevthandler))
\r
272 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY1, vRecv.begin(), vRecv.end());
\r
275 void AddPendingReplyEvent2(void* pevthandler, CDataStream& vRecv)
\r
277 if (IsCallbackAvailable(pevthandler))
\r
278 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY2, vRecv.begin(), vRecv.end());
\r
281 void AddPendingReplyEvent3(void* pevthandler, CDataStream& vRecv)
\r
283 if (IsCallbackAvailable(pevthandler))
\r
284 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY3, vRecv.begin(), vRecv.end());
\r
287 CDataStream GetStreamFromEvent(const wxCommandEvent& event)
\r
289 wxString strData = event.GetString();
\r
290 const char* pszBegin = strData.c_str();
\r
291 return CDataStream(pszBegin, pszBegin + event.GetInt(), SER_NETWORK);
\r
300 //////////////////////////////////////////////////////////////////////////////
\r
305 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
\r
307 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
\r
310 fRefreshListCtrl = false;
\r
311 fRefreshListCtrlRunning = false;
\r
312 fOnSetFocusAddress = false;
\r
314 m_choiceFilter->SetSelection(0);
\r
315 double dResize = 1.0;
\r
317 SetIcon(wxICON(bitcoin));
\r
319 SetIcon(bitcoin16_xpm);
\r
320 wxFont fontTmp = m_staticText41->GetFont();
\r
321 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
\r
322 m_staticTextBalance->SetFont(fontTmp);
\r
323 m_staticTextBalance->SetSize(140, 17);
\r
324 // & underlines don't work on the toolbar buttons on gtk
\r
325 m_toolBar->ClearTools();
\r
326 m_toolBar->AddTool(wxID_BUTTONSEND, "Send Coins", wxBitmap(send20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
\r
327 m_toolBar->AddTool(wxID_BUTTONRECEIVE, "Address Book", wxBitmap(addressbook20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
\r
328 m_toolBar->Realize();
\r
329 // resize to fit ubuntu's huge default font
\r
331 SetSize(dResize * GetSize().GetWidth(), 1.1 * GetSize().GetHeight());
\r
333 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
334 m_listCtrl->SetFocus();
\r
335 ptaskbaricon = new CMyTaskBarIcon();
\r
337 // Init column headers
\r
338 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
\r
339 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
\r
341 m_listCtrl->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
342 m_listCtrl->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
343 m_listCtrl->InsertColumn(2, "Status", wxLIST_FORMAT_LEFT, dResize * 110);
\r
344 m_listCtrl->InsertColumn(3, "Date", wxLIST_FORMAT_LEFT, dResize * nDateWidth);
\r
345 m_listCtrl->InsertColumn(4, "Description", wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
\r
346 m_listCtrl->InsertColumn(5, "Debit", wxLIST_FORMAT_RIGHT, dResize * 79);
\r
347 m_listCtrl->InsertColumn(6, "Credit", wxLIST_FORMAT_RIGHT, dResize * 79);
\r
349 //m_listCtrlProductsSent->InsertColumn(0, "Category", wxLIST_FORMAT_LEFT, 100);
\r
350 //m_listCtrlProductsSent->InsertColumn(1, "Title", wxLIST_FORMAT_LEFT, 100);
\r
351 //m_listCtrlProductsSent->InsertColumn(2, "Description", wxLIST_FORMAT_LEFT, 100);
\r
352 //m_listCtrlProductsSent->InsertColumn(3, "Price", wxLIST_FORMAT_LEFT, 100);
\r
353 //m_listCtrlProductsSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
\r
355 //m_listCtrlOrdersSent->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100);
\r
356 //m_listCtrlOrdersSent->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100);
\r
357 //m_listCtrlOrdersSent->InsertColumn(2, "", wxLIST_FORMAT_LEFT, 100);
\r
358 //m_listCtrlOrdersSent->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100);
\r
359 //m_listCtrlOrdersSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
\r
361 //m_listCtrlOrdersReceived->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100);
\r
362 //m_listCtrlOrdersReceived->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100);
\r
363 //m_listCtrlOrdersReceived->InsertColumn(2, "Payment Status", wxLIST_FORMAT_LEFT, 100);
\r
364 //m_listCtrlOrdersReceived->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100);
\r
365 //m_listCtrlOrdersReceived->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
\r
368 int pnWidths[3] = { -100, 88, 290 };
\r
370 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
\r
371 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
\r
373 m_statusBar->SetFieldsCount(3, pnWidths);
\r
375 // Fill your address text box
\r
376 vector<unsigned char> vchPubKey;
\r
377 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
\r
378 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
\r
380 // Fill listctrl with wallet transactions
\r
384 CMainFrame::~CMainFrame()
\r
387 delete ptaskbaricon;
\r
388 ptaskbaricon = NULL;
\r
391 void ExitTimeout(void* parg)
\r
399 void Shutdown(void* parg)
\r
401 static CCriticalSection cs_Shutdown;
\r
402 static bool fTaken;
\r
404 CRITICAL_BLOCK(cs_Shutdown)
\r
406 fFirstThread = !fTaken;
\r
413 nTransactionsUpdated++;
\r
417 CreateThread(ExitTimeout, NULL);
\r
419 printf("Bitcoin exiting\n\n");
\r
432 void CMainFrame::OnClose(wxCloseEvent& event)
\r
434 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
\r
436 // Divert close to minimize
\r
438 fClosedToTray = true;
\r
444 CreateThread(Shutdown, NULL);
\r
448 void CMainFrame::OnIconize(wxIconizeEvent& event)
\r
450 // Hide the task bar button when minimized.
\r
451 // Event is sent when the frame is minimized or restored.
\r
452 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
\r
453 // to get rid of the deprecated warning. Just ignore it.
\r
454 if (!event.Iconized())
\r
455 fClosedToTray = false;
\r
457 // Tray is not reliable on Linux gnome
\r
458 fClosedToTray = false;
\r
460 if (fMinimizeToTray && event.Iconized())
\r
461 fClosedToTray = true;
\r
462 Show(!fClosedToTray);
\r
463 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
466 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
\r
469 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
\r
470 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
\r
473 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
\r
475 // Hidden columns not resizeable
\r
476 if (event.GetColumn() <= 1 && !fDebug)
\r
480 int CMainFrame::GetSortIndex(const string& strSort)
\r
485 // The wx generic listctrl implementation used on GTK doesn't sort,
\r
486 // so we have to do it ourselves. Remember, we sort in reverse order.
\r
487 // In the wx generic implementation, they store the list of items
\r
488 // in a vector, so indexed lookups are fast, but inserts are slower
\r
489 // the closer they are to the top.
\r
491 int high = m_listCtrl->GetItemCount();
\r
494 int mid = low + ((high - low) / 2);
\r
495 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
\r
504 void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6)
\r
506 string str0 = strSort;
\r
507 long nData = *(long*)&hashKey;
\r
510 if (!fNew && nIndex == -1)
\r
512 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
513 if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())
\r
517 // fNew is for blind insert, only use if you're sure it's new
\r
518 if (fNew || nIndex == -1)
\r
520 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), str0);
\r
524 // If sort key changed, must delete and reinsert to make it relocate
\r
525 if (GetItemText(m_listCtrl, nIndex, 0) != str0)
\r
527 m_listCtrl->DeleteItem(nIndex);
\r
528 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), str0);
\r
532 m_listCtrl->SetItem(nIndex, 1, hashKey.ToString());
\r
533 m_listCtrl->SetItem(nIndex, 2, str2);
\r
534 m_listCtrl->SetItem(nIndex, 3, str3);
\r
535 m_listCtrl->SetItem(nIndex, 4, str4);
\r
536 m_listCtrl->SetItem(nIndex, 5, str5);
\r
537 m_listCtrl->SetItem(nIndex, 6, str6);
\r
538 m_listCtrl->SetItemData(nIndex, nData);
\r
541 bool CMainFrame::DeleteLine(uint256 hashKey)
\r
543 long nData = *(long*)&hashKey;
\r
547 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
548 if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())
\r
552 m_listCtrl->DeleteItem(nIndex);
\r
554 return nIndex != -1;
\r
557 string FormatTxStatus(const CWalletTx& wtx)
\r
560 if (!wtx.IsFinal())
\r
562 if (wtx.nLockTime < 500000000)
\r
563 return strprintf("Open for %d blocks", nBestHeight - wtx.nLockTime);
\r
565 return strprintf("Open until %s", DateTimeStr(wtx.nLockTime).c_str());
\r
569 int nDepth = wtx.GetDepthInMainChain();
\r
570 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
571 return strprintf("%d/offline?", nDepth);
\r
572 else if (nDepth < 6)
\r
573 return strprintf("%d/unconfirmed", nDepth);
\r
575 return strprintf("%d confirmations", nDepth);
\r
579 string SingleLine(const string& strIn)
\r
582 bool fOneSpace = false;
\r
583 foreach(int c, strIn)
\r
591 if (fOneSpace && !strOut.empty())
\r
600 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
\r
602 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
\r
603 int64 nCredit = wtx.GetCredit();
\r
604 int64 nDebit = wtx.GetDebit();
\r
605 int64 nNet = nCredit - nDebit;
\r
606 uint256 hash = wtx.GetHash();
\r
607 string strStatus = FormatTxStatus(wtx);
\r
608 map<string, string> mapValue = wtx.mapValue;
\r
609 wtx.nLinesDisplayed = 1;
\r
610 nListViewUpdated++;
\r
613 if (wtx.IsCoinBase())
\r
615 // Don't show generated coin until confirmed by at least one block after it
\r
616 // so we don't get the user's hopes up until it looks like it's probably accepted.
\r
618 // It is not an error when generated blocks are not accepted. By design,
\r
619 // some percentage of blocks, like 10% or more, will end up not accepted.
\r
620 // This is the normal mechanism by which the network copes with latency.
\r
622 // We display regular transactions right away before any confirmation
\r
623 // because they can always get into some block eventually. Generated coins
\r
624 // are special because if their block is not accepted, they are not valid.
\r
626 if (wtx.GetDepthInMainChain() < 2)
\r
628 wtx.nLinesDisplayed = 0;
\r
632 // View->Show Generated
\r
633 if (!fShowGenerated)
\r
637 // Find the block the tx is in
\r
638 CBlockIndex* pindex = NULL;
\r
639 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
\r
640 if (mi != mapBlockIndex.end())
\r
641 pindex = (*mi).second;
\r
643 // Sort order, unrecorded transactions sort to the top
\r
644 string strSort = strprintf("%010d-%01d-%010u",
\r
645 (pindex ? pindex->nHeight : INT_MAX),
\r
646 (wtx.IsCoinBase() ? 1 : 0),
\r
647 wtx.nTimeReceived);
\r
650 if (nNet > 0 || wtx.IsCoinBase())
\r
655 string strDescription;
\r
657 if (wtx.IsCoinBase())
\r
660 strDescription = "Generated";
\r
663 int64 nUnmatured = 0;
\r
664 foreach(const CTxOut& txout, wtx.vout)
\r
665 nUnmatured += txout.GetCredit();
\r
666 if (wtx.IsInMainChain())
\r
668 strDescription = strprintf("Generated (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
670 // Check if the block was requested by anyone
\r
671 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
672 strDescription = "Generated - Warning: This block was not received by any other nodes and will probably not be accepted!";
\r
676 strDescription = "Generated (not accepted)";
\r
680 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
\r
682 // Online transaction
\r
683 if (!mapValue["from"].empty())
\r
684 strDescription += "From: " + mapValue["from"];
\r
685 if (!mapValue["message"].empty())
\r
687 if (!strDescription.empty())
\r
688 strDescription += " - ";
\r
689 strDescription += mapValue["message"];
\r
694 // Offline transaction
\r
695 foreach(const CTxOut& txout, wtx.vout)
\r
697 if (txout.IsMine())
\r
699 vector<unsigned char> vchPubKey;
\r
700 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
702 CRITICAL_BLOCK(cs_mapAddressBook)
\r
704 string strAddress = PubKeyToAddress(vchPubKey);
\r
705 if (mapAddressBook.count(strAddress))
\r
707 //strDescription += "Received payment to ";
\r
708 //strDescription += "Received with address ";
\r
709 strDescription += "From: unknown, To: ";
\r
710 strDescription += strAddress;
\r
711 /// The labeling feature is just too confusing, so I hid it
\r
712 /// by putting it at the end where it runs off the screen.
\r
713 /// It can still be seen by widening the column, or in the
\r
714 /// details dialog.
\r
715 if (!mapAddressBook[strAddress].empty())
\r
716 strDescription += " (" + mapAddressBook[strAddress] + ")";
\r
725 InsertLine(fNew, nIndex, hash, strSort,
\r
727 nTime ? DateTimeStr(nTime) : "",
\r
728 SingleLine(strDescription),
\r
730 FormatMoney(nNet, true));
\r
734 bool fAllFromMe = true;
\r
735 foreach(const CTxIn& txin, wtx.vin)
\r
736 fAllFromMe = fAllFromMe && txin.IsMine();
\r
738 bool fAllToMe = true;
\r
739 foreach(const CTxOut& txout, wtx.vout)
\r
740 fAllToMe = fAllToMe && txout.IsMine();
\r
742 if (fAllFromMe && fAllToMe)
\r
745 int64 nValue = wtx.vout[0].nValue;
\r
746 InsertLine(fNew, nIndex, hash, strSort,
\r
748 nTime ? DateTimeStr(nTime) : "",
\r
749 "Payment to yourself",
\r
752 /// issue: can't tell which is the payment and which is the change anymore
\r
753 // FormatMoney(nNet - nValue, true),
\r
754 // FormatMoney(nValue, true));
\r
756 else if (fAllFromMe)
\r
761 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
762 wtx.nLinesDisplayed = 0;
\r
763 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
\r
765 const CTxOut& txout = wtx.vout[nOut];
\r
766 if (txout.IsMine())
\r
770 if (!mapValue["to"].empty())
\r
772 // Online transaction
\r
773 strAddress = mapValue["to"];
\r
777 // Offline transaction
\r
779 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
780 strAddress = Hash160ToAddress(hash160);
\r
783 string strDescription = "To: ";
\r
784 CRITICAL_BLOCK(cs_mapAddressBook)
\r
785 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
786 strDescription += mapAddressBook[strAddress] + " ";
\r
787 strDescription += strAddress;
\r
788 if (!mapValue["message"].empty())
\r
790 if (!strDescription.empty())
\r
791 strDescription += " - ";
\r
792 strDescription += mapValue["message"];
\r
795 int64 nValue = txout.nValue;
\r
796 if (nOut == 0 && nTxFee > 0)
\r
799 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
\r
801 nTime ? DateTimeStr(nTime) : "",
\r
802 SingleLine(strDescription),
\r
803 FormatMoney(-nValue, true),
\r
805 wtx.nLinesDisplayed++;
\r
811 // Mixed debit transaction, can't break down payees
\r
813 bool fAllMine = true;
\r
814 foreach(const CTxOut& txout, wtx.vout)
\r
815 fAllMine = fAllMine && txout.IsMine();
\r
816 foreach(const CTxIn& txin, wtx.vin)
\r
817 fAllMine = fAllMine && txin.IsMine();
\r
819 InsertLine(fNew, nIndex, hash, strSort,
\r
821 nTime ? DateTimeStr(nTime) : "",
\r
823 FormatMoney(nNet, true),
\r
831 void CMainFrame::RefreshListCtrl()
\r
833 fRefreshListCtrl = true;
\r
837 void CMainFrame::OnIdle(wxIdleEvent& event)
\r
839 if (fRefreshListCtrl)
\r
841 // Collect list of wallet transactions and sort newest first
\r
842 bool fEntered = false;
\r
843 vector<pair<unsigned int, uint256> > vSorted;
\r
844 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
846 printf("RefreshListCtrl starting\n");
\r
848 fRefreshListCtrl = false;
\r
849 vWalletUpdated.clear();
\r
851 // Do the newest transactions first
\r
852 vSorted.reserve(mapWallet.size());
\r
853 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
855 const CWalletTx& wtx = (*it).second;
\r
856 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
\r
857 vSorted.push_back(make_pair(nTime, (*it).first));
\r
859 m_listCtrl->DeleteAllItems();
\r
864 sort(vSorted.begin(), vSorted.end());
\r
866 // Fill list control
\r
867 for (int i = 0; i < vSorted.size();)
\r
871 bool fEntered = false;
\r
872 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
875 uint256& hash = vSorted[i++].second;
\r
876 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
877 if (mi != mapWallet.end())
\r
878 InsertTransaction((*mi).second, true);
\r
880 if (!fEntered || i == 100 || i % 500 == 0)
\r
884 printf("RefreshListCtrl done\n");
\r
886 // Update transaction total display
\r
887 MainFrameRepaint();
\r
891 // Check for time updates
\r
892 static int64 nLastTime;
\r
893 if (GetTime() > nLastTime + 30)
\r
895 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
897 nLastTime = GetTime();
\r
898 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
900 CWalletTx& wtx = (*it).second;
\r
901 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
\r
902 InsertTransaction(wtx, false);
\r
909 void CMainFrame::RefreshStatusColumn()
\r
911 static int nLastTop;
\r
912 static CBlockIndex* pindexLastBest;
\r
913 static unsigned int nLastRefreshed;
\r
915 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
\r
916 if (nTop == nLastTop && pindexLastBest == pindexBest)
\r
919 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
922 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
\r
924 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
\r
926 // If no updates, only need to do the part that moved onto the screen
\r
927 if (nStart >= nLastTop && nStart < nLastTop + 100)
\r
928 nStart = nLastTop + 100;
\r
929 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
\r
933 pindexLastBest = pindexBest;
\r
934 nLastRefreshed = nListViewUpdated;
\r
936 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
\r
938 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
\r
939 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
940 if (mi == mapWallet.end())
\r
942 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
\r
945 CWalletTx& wtx = (*mi).second;
\r
946 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
\r
948 if (!InsertTransaction(wtx, false, nIndex))
\r
949 m_listCtrl->DeleteItem(nIndex--);
\r
952 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
\r
957 void CMainFrame::OnPaint(wxPaintEvent& event)
\r
968 unsigned int nNeedRepaint = 0;
\r
969 unsigned int nLastRepaint = 0;
\r
970 int64 nLastRepaintTime = 0;
\r
971 int64 nRepaintInterval = 500;
\r
973 void ThreadDelayedRepaint(void* parg)
\r
977 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
979 nLastRepaint = nNeedRepaint;
\r
982 printf("DelayedRepaint\n");
\r
983 wxPaintEvent event;
\r
984 pframeMain->fRefresh = true;
\r
985 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
988 Sleep(nRepaintInterval);
\r
992 void MainFrameRepaint()
\r
994 // This is called by network code that shouldn't access pframeMain
\r
995 // directly because it could still be running after the UI is closed.
\r
998 // Don't repaint too often
\r
999 static int64 nLastRepaintRequest;
\r
1000 if (GetTimeMillis() - nLastRepaintRequest < 100)
\r
1005 nLastRepaintRequest = GetTimeMillis();
\r
1007 printf("MainFrameRepaint\n");
\r
1008 wxPaintEvent event;
\r
1009 pframeMain->fRefresh = true;
\r
1010 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
1014 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
\r
1017 ptaskbaricon->UpdateTooltip();
\r
1022 static int nTransactionCount;
\r
1023 bool fPaintedBalance = false;
\r
1024 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
1026 nLastRepaint = nNeedRepaint;
\r
1027 nLastRepaintTime = GetTimeMillis();
\r
1029 // Update listctrl contents
\r
1030 if (!vWalletUpdated.empty())
\r
1032 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
1035 if (m_listCtrl->GetItemCount())
\r
1036 strTop = (string)m_listCtrl->GetItemText(0);
\r
1037 foreach(uint256 hash, vWalletUpdated)
\r
1039 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1040 if (mi != mapWallet.end())
\r
1041 InsertTransaction((*mi).second, false);
\r
1043 vWalletUpdated.clear();
\r
1044 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
\r
1045 m_listCtrl->ScrollList(0, INT_MIN/2);
\r
1050 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
1052 fPaintedBalance = true;
\r
1053 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
1055 // Count hidden and multi-line transactions
\r
1056 nTransactionCount = 0;
\r
1057 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
1059 CWalletTx& wtx = (*it).second;
\r
1060 nTransactionCount += wtx.nLinesDisplayed;
\r
1064 if (!vWalletUpdated.empty() || !fPaintedBalance)
\r
1067 // Update status column of visible items only
\r
1068 RefreshStatusColumn();
\r
1070 // Update status bar
\r
1071 string strGen = "";
\r
1072 if (fGenerateBitcoins)
\r
1073 strGen = " Generating";
\r
1074 if (fGenerateBitcoins && vNodes.empty())
\r
1075 strGen = "(not connected)";
\r
1076 m_statusBar->SetStatusText(strGen, 1);
\r
1078 string strStatus = strprintf(" %d connections %d blocks %d transactions", vNodes.size(), nBestHeight + 1, nTransactionCount);
\r
1079 m_statusBar->SetStatusText(strStatus, 2);
\r
1081 if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
\r
1082 m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0);
\r
1084 // Pass through to listctrl to actually do the paint, we're just hooking the message
\r
1085 m_listCtrl->Disconnect(wxEVT_PAINT, (wxObjectEventFunction)NULL, NULL, this);
\r
1086 m_listCtrl->GetEventHandler()->ProcessEvent(event);
\r
1087 m_listCtrl->Connect(wxEVT_PAINT, wxPaintEventHandler(CMainFrame::OnPaintListCtrl), NULL, this);
\r
1091 void UIThreadCall(boost::function0<void> fn)
\r
1093 // Call this with a function object created with bind.
\r
1094 // bind needs all parameters to match the function's expected types
\r
1095 // and all default parameters specified. Some examples:
\r
1096 // UIThreadCall(bind(wxBell));
\r
1097 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
\r
1098 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
\r
1101 wxCommandEvent event(wxEVT_UITHREADCALL);
\r
1102 event.SetClientData((void*)new boost::function0<void>(fn));
\r
1103 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
1107 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
\r
1109 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
\r
1114 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
\r
1120 void CMainFrame::OnMenuViewShowGenerated(wxCommandEvent& event)
\r
1122 // View->Show Generated
\r
1123 fShowGenerated = event.IsChecked();
\r
1124 CWalletDB().WriteSetting("fShowGenerated", fShowGenerated);
\r
1125 RefreshListCtrl();
\r
1128 void CMainFrame::OnUpdateUIViewShowGenerated(wxUpdateUIEvent& event)
\r
1130 event.Check(fShowGenerated);
\r
1133 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
\r
1135 // Options->Generate Coins
\r
1136 GenerateBitcoins(event.IsChecked());
\r
1139 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
\r
1141 event.Check(fGenerateBitcoins);
\r
1144 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
\r
1146 // Options->Change Your Address
\r
1147 OnButtonChange(event);
\r
1150 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
\r
1152 // Options->Options
\r
1153 COptionsDialog dialog(this);
\r
1154 dialog.ShowModal();
\r
1157 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
\r
1160 CAboutDialog dialog(this);
\r
1161 dialog.ShowModal();
\r
1164 void CMainFrame::OnButtonSend(wxCommandEvent& event)
\r
1167 CSendDialog dialog(this);
\r
1168 dialog.ShowModal();
\r
1171 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
\r
1173 // Toolbar: Address Book
\r
1174 CAddressBookDialog dialogAddr(this, "", false);
\r
1175 if (dialogAddr.ShowModal() == 2)
\r
1178 CSendDialog dialogSend(this, dialogAddr.GetAddress());
\r
1179 dialogSend.ShowModal();
\r
1183 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
\r
1185 // Automatically select-all when entering window
\r
1186 m_textCtrlAddress->SetSelection(-1, -1);
\r
1187 fOnSetFocusAddress = true;
\r
1191 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
\r
1193 if (fOnSetFocusAddress)
\r
1194 m_textCtrlAddress->SetSelection(-1, -1);
\r
1195 fOnSetFocusAddress = false;
\r
1199 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
\r
1201 // Copy address box to clipboard
\r
1202 if (wxTheClipboard->Open())
\r
1204 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
\r
1205 wxTheClipboard->Close();
\r
1209 void CMainFrame::OnButtonChange(wxCommandEvent& event)
\r
1211 CYourAddressDialog dialog(this, string(m_textCtrlAddress->GetValue()));
\r
1212 if (!dialog.ShowModal())
\r
1214 string strAddress = (string)dialog.GetAddress();
\r
1215 if (strAddress != m_textCtrlAddress->GetValue())
\r
1218 if (!AddressToHash160(strAddress, hash160))
\r
1220 if (!mapPubKeys.count(hash160))
\r
1222 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
\r
1223 m_textCtrlAddress->SetValue(strAddress);
\r
1227 void CMainFrame::OnListItemActivated(wxListEvent& event)
\r
1229 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
\r
1231 CRITICAL_BLOCK(cs_mapWallet)
\r
1233 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1234 if (mi == mapWallet.end())
\r
1236 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
\r
1239 wtx = (*mi).second;
\r
1241 CTxDetailsDialog dialog(this, wtx);
\r
1242 dialog.ShowModal();
\r
1243 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
\r
1244 //pdialog->Show();
\r
1247 void CMainFrame::OnListItemActivatedProductsSent(wxListEvent& event)
\r
1249 CProduct& product = *(CProduct*)event.GetItem().GetData();
\r
1250 CEditProductDialog* pdialog = new CEditProductDialog(this);
\r
1251 pdialog->SetProduct(product);
\r
1255 void CMainFrame::OnListItemActivatedOrdersSent(wxListEvent& event)
\r
1257 CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();
\r
1258 CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, false);
\r
1262 void CMainFrame::OnListItemActivatedOrdersReceived(wxListEvent& event)
\r
1264 CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();
\r
1265 CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, true);
\r
1275 //////////////////////////////////////////////////////////////////////////////
\r
1277 // CTxDetailsDialog
\r
1280 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
\r
1282 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1285 strHTML.reserve(4000);
\r
1286 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
\r
1288 int64 nTime = wtx.GetTxTime();
\r
1289 int64 nCredit = wtx.GetCredit();
\r
1290 int64 nDebit = wtx.GetDebit();
\r
1291 int64 nNet = nCredit - nDebit;
\r
1295 strHTML += "<b>Status:</b> " + FormatTxStatus(wtx);
\r
1296 int nRequests = wtx.GetRequestCount();
\r
1297 if (nRequests != -1)
\r
1299 if (nRequests == 0)
\r
1300 strHTML += ", has not been successfully broadcast yet";
\r
1301 else if (nRequests == 1)
\r
1302 strHTML += strprintf(", broadcast through %d node", nRequests);
\r
1304 strHTML += strprintf(", broadcast through %d nodes", nRequests);
\r
1306 strHTML += "<br>";
\r
1308 strHTML += "<b>Date:</b> " + (nTime ? DateTimeStr(nTime) : "") + "<br>";
\r
1314 if (wtx.IsCoinBase())
\r
1316 strHTML += "<b>Source:</b> Generated<br>";
\r
1318 else if (!wtx.mapValue["from"].empty())
\r
1320 // Online transaction
\r
1321 if (!wtx.mapValue["from"].empty())
\r
1322 strHTML += "<b>From:</b> " + HtmlEscape(wtx.mapValue["from"]) + "<br>";
\r
1326 // Offline transaction
\r
1330 foreach(const CTxOut& txout, wtx.vout)
\r
1332 if (txout.IsMine())
\r
1334 vector<unsigned char> vchPubKey;
\r
1335 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
1337 string strAddress = PubKeyToAddress(vchPubKey);
\r
1338 if (mapAddressBook.count(strAddress))
\r
1340 strHTML += "<b>From:</b> unknown<br>";
\r
1341 strHTML += "<b>To:</b> ";
\r
1342 strHTML += HtmlEscape(strAddress);
\r
1343 if (!mapAddressBook[strAddress].empty())
\r
1344 strHTML += " (yours, label: " + mapAddressBook[strAddress] + ")";
\r
1346 strHTML += " (yours)";
\r
1347 strHTML += "<br>";
\r
1360 string strAddress;
\r
1361 if (!wtx.mapValue["to"].empty())
\r
1363 // Online transaction
\r
1364 strAddress = wtx.mapValue["to"];
\r
1365 strHTML += "<b>To:</b> ";
\r
1366 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1367 strHTML += mapAddressBook[strAddress] + " ";
\r
1368 strHTML += HtmlEscape(strAddress) + "<br>";
\r
1375 if (wtx.IsCoinBase() && nCredit == 0)
\r
1380 int64 nUnmatured = 0;
\r
1381 foreach(const CTxOut& txout, wtx.vout)
\r
1382 nUnmatured += txout.GetCredit();
\r
1383 if (wtx.IsInMainChain())
\r
1384 strHTML += strprintf("<b>Credit:</b> (%s matures in %d more blocks)<br>", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
1386 strHTML += "<b>Credit:</b> (not accepted)<br>";
\r
1388 else if (nNet > 0)
\r
1393 strHTML += "<b>Credit:</b> " + FormatMoney(nNet) + "<br>";
\r
1397 bool fAllFromMe = true;
\r
1398 foreach(const CTxIn& txin, wtx.vin)
\r
1399 fAllFromMe = fAllFromMe && txin.IsMine();
\r
1401 bool fAllToMe = true;
\r
1402 foreach(const CTxOut& txout, wtx.vout)
\r
1403 fAllToMe = fAllToMe && txout.IsMine();
\r
1410 foreach(const CTxOut& txout, wtx.vout)
\r
1412 if (txout.IsMine())
\r
1415 if (wtx.mapValue["to"].empty())
\r
1417 // Offline transaction
\r
1419 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
1421 string strAddress = Hash160ToAddress(hash160);
\r
1422 strHTML += "<b>To:</b> ";
\r
1423 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1424 strHTML += mapAddressBook[strAddress] + " ";
\r
1425 strHTML += strAddress;
\r
1426 strHTML += "<br>";
\r
1430 strHTML += "<b>Debit:</b> " + FormatMoney(-txout.nValue) + "<br>";
\r
1435 // Payment to self
\r
1436 /// issue: can't tell which is the payment and which is the change anymore
\r
1437 //int64 nValue = wtx.vout[0].nValue;
\r
1438 //strHTML += "<b>Debit:</b> " + FormatMoney(-nValue) + "<br>";
\r
1439 //strHTML += "<b>Credit:</b> " + FormatMoney(nValue) + "<br>";
\r
1442 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
1444 strHTML += "<b>Transaction fee:</b> " + FormatMoney(-nTxFee) + "<br>";
\r
1449 // Mixed debit transaction
\r
1451 foreach(const CTxIn& txin, wtx.vin)
\r
1452 if (txin.IsMine())
\r
1453 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1454 foreach(const CTxOut& txout, wtx.vout)
\r
1455 if (txout.IsMine())
\r
1456 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1460 strHTML += "<b>Net amount:</b> " + FormatMoney(nNet, true) + "<br>";
\r
1466 if (!wtx.mapValue["message"].empty())
\r
1467 strHTML += "<br><b>Message:</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
\r
1469 if (wtx.IsCoinBase())
\r
1470 strHTML += "<br>Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.<br>";
\r
1478 strHTML += "<hr><br>debug print<br><br>";
\r
1479 foreach(const CTxIn& txin, wtx.vin)
\r
1480 if (txin.IsMine())
\r
1481 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1482 foreach(const CTxOut& txout, wtx.vout)
\r
1483 if (txout.IsMine())
\r
1484 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1486 strHTML += "<b>Inputs:</b><br>";
\r
1487 CRITICAL_BLOCK(cs_mapWallet)
\r
1489 foreach(const CTxIn& txin, wtx.vin)
\r
1491 COutPoint prevout = txin.prevout;
\r
1492 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
\r
1493 if (mi != mapWallet.end())
\r
1495 const CWalletTx& prev = (*mi).second;
\r
1496 if (prevout.n < prev.vout.size())
\r
1498 strHTML += HtmlEscape(prev.ToString(), true);
\r
1499 strHTML += " " + FormatTxStatus(prev) + ", ";
\r
1500 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
\r
1506 strHTML += "<br><hr><br><b>Transaction:</b><br>";
\r
1507 strHTML += HtmlEscape(wtx.ToString(), true);
\r
1512 strHTML += "</font></html>";
\r
1513 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
1514 m_htmlWin->SetPage(strHTML);
\r
1515 m_buttonOK->SetFocus();
\r
1519 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
\r
1529 //////////////////////////////////////////////////////////////////////////////
\r
1534 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
\r
1536 // Set up list box of page choices
\r
1537 m_listBox->Append("Main");
\r
1538 //m_listBox->Append("Test 2");
\r
1539 m_listBox->SetSelection(0);
\r
1542 m_checkBoxMinimizeOnClose->SetLabel("&Minimize on close");
\r
1543 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
\r
1547 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
\r
1548 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
\r
1549 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
\r
1550 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
\r
1551 int nProcessors = wxThread::GetCPUCount();
\r
1552 if (nProcessors < 1)
\r
1553 nProcessors = 999;
\r
1554 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
\r
1555 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
\r
1556 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
\r
1557 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
\r
1558 m_checkBoxUseProxy->SetValue(fUseProxy);
\r
1559 m_textCtrlProxyIP->Enable(fUseProxy);
\r
1560 m_textCtrlProxyPort->Enable(fUseProxy);
\r
1561 m_staticTextProxyIP->Enable(fUseProxy);
\r
1562 m_staticTextProxyPort->Enable(fUseProxy);
\r
1563 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
\r
1564 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
\r
1566 m_buttonOK->SetFocus();
\r
1569 void COptionsDialog::SelectPage(int nPage)
\r
1571 m_panelMain->Show(nPage == 0);
\r
1572 m_panelTest2->Show(nPage == 1);
\r
1574 m_scrolledWindow->Layout();
\r
1575 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
\r
1578 void COptionsDialog::OnListBox(wxCommandEvent& event)
\r
1580 SelectPage(event.GetSelection());
\r
1583 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
\r
1585 int64 nTmp = nTransactionFee;
\r
1586 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
\r
1587 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
\r
1590 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
\r
1592 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
\r
1595 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
\r
1597 m_textCtrlProxyIP->Enable(event.IsChecked());
\r
1598 m_textCtrlProxyPort->Enable(event.IsChecked());
\r
1599 m_staticTextProxyIP->Enable(event.IsChecked());
\r
1600 m_staticTextProxyPort->Enable(event.IsChecked());
\r
1603 CAddress COptionsDialog::GetProxyAddr()
\r
1605 // Be careful about byte order, addr.ip and addr.port are big endian
\r
1606 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
\r
1607 if (addr.ip == INADDR_NONE)
\r
1608 addr.ip = addrProxy.ip;
\r
1609 int nPort = atoi(m_textCtrlProxyPort->GetValue());
\r
1610 addr.port = htons(nPort);
\r
1611 if (nPort <= 0 || nPort > USHRT_MAX)
\r
1612 addr.port = addrProxy.port;
\r
1616 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
\r
1618 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
\r
1619 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
\r
1623 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
\r
1625 OnButtonApply(event);
\r
1629 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
\r
1634 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
\r
1636 CWalletDB walletdb;
\r
1638 int64 nPrevTransactionFee = nTransactionFee;
\r
1639 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
\r
1640 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
\r
1642 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
\r
1643 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
\r
1645 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
\r
1646 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
\r
1648 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
\r
1650 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
\r
1651 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
\r
1653 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
\r
1654 GenerateBitcoins(fGenerateBitcoins);
\r
1656 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
\r
1658 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
\r
1659 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
\r
1662 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
\r
1664 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
\r
1665 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
\r
1666 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
1669 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
\r
1671 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
\r
1672 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
\r
1675 fUseProxy = m_checkBoxUseProxy->GetValue();
\r
1676 walletdb.WriteSetting("fUseProxy", fUseProxy);
\r
1678 addrProxy = GetProxyAddr();
\r
1679 walletdb.WriteSetting("addrProxy", addrProxy);
\r
1687 //////////////////////////////////////////////////////////////////////////////
\r
1692 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
\r
1694 m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d beta", VERSION/100, VERSION%100));
\r
1696 #if !wxUSE_UNICODE
\r
1697 // Workaround until upgrade to wxWidgets supporting UTF-8
\r
1698 // Hack to change the (c) character from UTF-8 back to ANSI
\r
1699 wxString str = m_staticTextMain->GetLabel();
\r
1700 if (str.Find('\xC2') != wxNOT_FOUND)
\r
1701 str.Remove(str.Find('\xC2'), 1);
\r
1702 m_staticTextMain->SetLabel(str);
\r
1705 // Resize on Linux to make the window fit the text.
\r
1706 // The text was wrapped manually rather than using the Wrap setting because
\r
1707 // the wrap would be too small on Linux and it can't be changed at this point.
\r
1708 wxFont fontTmp = m_staticTextMain->GetFont();
\r
1709 if (fontTmp.GetPointSize() > 8);
\r
1710 fontTmp.SetPointSize(8);
\r
1711 m_staticTextMain->SetFont(fontTmp);
\r
1712 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() - 4);
\r
1716 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
\r
1726 //////////////////////////////////////////////////////////////////////////////
\r
1731 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
\r
1734 m_textCtrlAddress->SetValue(strAddress);
\r
1735 m_choiceTransferType->SetSelection(0);
\r
1736 m_bitmapCheckMark->Show(false);
\r
1737 fEnabledPrev = true;
\r
1738 m_textCtrlAddress->SetFocus();
\r
1739 //// todo: should add a display of your balance for convenience
\r
1741 wxFont fontTmp = m_staticTextInstructions->GetFont();
\r
1742 if (fontTmp.GetPointSize() > 9);
\r
1743 fontTmp.SetPointSize(9);
\r
1744 m_staticTextInstructions->SetFont(fontTmp);
\r
1745 SetSize(725, 380);
\r
1750 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
\r
1751 SetIcon(iconSend);
\r
1753 wxCommandEvent event;
\r
1754 OnTextAddress(event);
\r
1756 // Fixup the tab order
\r
1757 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
\r
1758 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
\r
1762 void CSendDialog::OnTextAddress(wxCommandEvent& event)
\r
1765 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
\r
1766 m_bitmapCheckMark->Show(fBitcoinAddress);
\r
1768 // Grey out message if bitcoin address
\r
1769 bool fEnable = !fBitcoinAddress;
\r
1770 m_staticTextFrom->Enable(fEnable);
\r
1771 m_textCtrlFrom->Enable(fEnable);
\r
1772 m_staticTextMessage->Enable(fEnable);
\r
1773 m_textCtrlMessage->Enable(fEnable);
\r
1774 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
\r
1775 if (!fEnable && fEnabledPrev)
\r
1777 strFromSave = m_textCtrlFrom->GetValue();
\r
1778 strMessageSave = m_textCtrlMessage->GetValue();
\r
1779 m_textCtrlFrom->SetValue("Will appear as \"From: Unknown\"");
\r
1780 m_textCtrlMessage->SetValue("Can't include a message when sending to a Bitcoin address");
\r
1782 else if (fEnable && !fEnabledPrev)
\r
1784 m_textCtrlFrom->SetValue(strFromSave);
\r
1785 m_textCtrlMessage->SetValue(strMessageSave);
\r
1787 fEnabledPrev = fEnable;
\r
1790 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
\r
1792 // Reformat the amount
\r
1793 if (m_textCtrlAmount->GetValue().Trim().empty())
\r
1796 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
\r
1797 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
\r
1800 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
\r
1802 // Open address book
\r
1803 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), true);
\r
1804 if (dialog.ShowModal())
\r
1805 m_textCtrlAddress->SetValue(dialog.GetAddress());
\r
1808 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
\r
1810 // Copy clipboard to address box
\r
1811 if (wxTheClipboard->Open())
\r
1813 if (wxTheClipboard->IsSupported(wxDF_TEXT))
\r
1815 wxTextDataObject data;
\r
1816 wxTheClipboard->GetData(data);
\r
1817 m_textCtrlAddress->SetValue(data.GetText());
\r
1819 wxTheClipboard->Close();
\r
1823 void CSendDialog::OnButtonSend(wxCommandEvent& event)
\r
1826 string strAddress = (string)m_textCtrlAddress->GetValue();
\r
1830 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
\r
1832 wxMessageBox("Error in amount ", "Send Coins");
\r
1835 if (nValue > GetBalance())
\r
1837 wxMessageBox("Amount exceeds your balance ", "Send Coins");
\r
1840 if (nValue + nTransactionFee > GetBalance())
\r
1842 wxMessageBox(string("Total exceeds your balance when the ") + FormatMoney(nTransactionFee) + " transaction fee is included ", "Send Coins");
\r
1846 // Parse bitcoin address
\r
1848 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
\r
1850 if (fBitcoinAddress)
\r
1852 // Send to bitcoin address
\r
1853 CScript scriptPubKey;
\r
1854 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
\r
1856 string strError = SendMoney(scriptPubKey, nValue, wtx);
\r
1857 if (strError != "")
\r
1858 wxMessageBox(strError + " ", "Sending...");
\r
1860 wxMessageBox("Payment sent ", "Sending...");
\r
1864 // Parse IP address
\r
1865 CAddress addr(strAddress);
\r
1866 if (!addr.IsValid())
\r
1868 wxMessageBox("Invalid address ", "Send Coins");
\r
1873 wtx.mapValue["to"] = strAddress;
\r
1874 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
\r
1875 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
\r
1877 // Send to IP address
\r
1878 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
\r
1879 if (!pdialog->ShowModal())
\r
1883 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1884 if (!mapAddressBook.count(strAddress))
\r
1885 SetAddressBookName(strAddress, "");
\r
1890 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
\r
1901 //////////////////////////////////////////////////////////////////////////////
\r
1906 CSendingDialog::CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 nPriceIn, const CWalletTx& wtxIn) : CSendingDialogBase(NULL) // we have to give null so parent can't destroy us
\r
1909 nPrice = nPriceIn;
\r
1911 start = wxDateTime::UNow();
\r
1912 memset(pszStatus, 0, sizeof(pszStatus));
\r
1913 fCanCancel = true;
\r
1917 fWorkDone = false;
\r
1919 SetSize(1.2 * GetSize().GetWidth(), 1.05 * GetSize().GetHeight());
\r
1922 SetTitle(strprintf("Sending %s to %s", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
\r
1923 m_textCtrlStatus->SetValue("");
\r
1925 CreateThread(SendingDialogStartTransfer, this);
\r
1928 CSendingDialog::~CSendingDialog()
\r
1930 printf("~CSendingDialog()\n");
\r
1933 void CSendingDialog::Close()
\r
1935 // Last one out turn out the lights.
\r
1936 // fWorkDone signals that work side is done and UI thread should call destroy.
\r
1937 // fUIDone signals that UI window has closed and work thread should call destroy.
\r
1938 // This allows the window to disappear and end modality when cancelled
\r
1939 // without making the user wait for ConnectNode to return. The dialog object
\r
1940 // hangs around in the background until the work thread exits.
\r
1942 EndModal(fSuccess);
\r
1951 void CSendingDialog::OnClose(wxCloseEvent& event)
\r
1953 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
\r
1960 wxCommandEvent cmdevent;
\r
1961 OnButtonCancel(cmdevent);
\r
1965 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
\r
1971 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
\r
1977 void CSendingDialog::OnPaint(wxPaintEvent& event)
\r
1979 if (strlen(pszStatus) > 130)
\r
1980 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
\r
1982 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
\r
1983 m_staticTextSending->SetFocus();
\r
1985 m_buttonCancel->Enable(false);
\r
1988 m_buttonOK->Enable(true);
\r
1989 m_buttonOK->SetFocus();
\r
1990 m_buttonCancel->Enable(false);
\r
1992 if (fAbort && fCanCancel && IsShown())
\r
1994 strcpy(pszStatus, "CANCELLED");
\r
1995 m_buttonOK->Enable(true);
\r
1996 m_buttonOK->SetFocus();
\r
1997 m_buttonCancel->Enable(false);
\r
1998 m_buttonCancel->SetLabel("Cancelled");
\r
2000 wxMessageBox("Transfer cancelled ", "Sending...", wxOK, this);
\r
2007 // Everything from here on is not in the UI thread and must only communicate
\r
2008 // with the rest of the dialog through variables and calling repaint.
\r
2011 void CSendingDialog::Repaint()
\r
2014 wxPaintEvent event;
\r
2015 GetEventHandler()->AddPendingEvent(event);
\r
2018 bool CSendingDialog::Status()
\r
2025 if (fAbort && fCanCancel)
\r
2027 memset(pszStatus, 0, 10);
\r
2028 strcpy(pszStatus, "CANCELLED");
\r
2036 bool CSendingDialog::Status(const string& str)
\r
2041 // This can be read by the UI thread at any time,
\r
2042 // so copy in a way that can be read cleanly at all times.
\r
2043 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
\r
2044 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
\r
2050 bool CSendingDialog::Error(const string& str)
\r
2052 fCanCancel = false;
\r
2054 Status(string("Error: ") + str);
\r
2058 void SendingDialogStartTransfer(void* parg)
\r
2060 ((CSendingDialog*)parg)->StartTransfer();
\r
2063 void CSendingDialog::StartTransfer()
\r
2065 // Make sure we have enough money
\r
2066 if (nPrice + nTransactionFee > GetBalance())
\r
2068 Error("You don't have enough money");
\r
2072 // We may have connected already for product details
\r
2073 if (!Status("Connecting..."))
\r
2075 CNode* pnode = ConnectNode(addr, 15 * 60);
\r
2078 Error("Unable to connect");
\r
2082 // Send order to seller, with response going to OnReply2 via event handler
\r
2083 if (!Status("Requesting public key..."))
\r
2085 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
\r
2088 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
\r
2090 ((CSendingDialog*)parg)->OnReply2(vRecv);
\r
2093 void CSendingDialog::OnReply2(CDataStream& vRecv)
\r
2095 if (!Status("Received public key..."))
\r
2098 CScript scriptPubKey;
\r
2105 string strMessage;
\r
2106 vRecv >> strMessage;
\r
2107 Error("Transfer was not accepted");
\r
2108 //// todo: enlarge the window and enable a hidden white box to put seller's message
\r
2111 vRecv >> scriptPubKey;
\r
2115 //// what do we want to do about this?
\r
2116 Error("Invalid response received");
\r
2120 // Pause to give the user a chance to cancel
\r
2121 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
\r
2128 CRITICAL_BLOCK(cs_main)
\r
2131 if (!Status("Creating transaction..."))
\r
2133 if (nPrice + nTransactionFee > GetBalance())
\r
2135 Error("You don't have enough money");
\r
2139 int64 nFeeRequired;
\r
2140 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
\r
2142 if (nPrice + nFeeRequired > GetBalance())
\r
2143 Error(strprintf("This is an oversized transaction that requires a transaction fee of %s", FormatMoney(nFeeRequired).c_str()));
\r
2145 Error("Transaction creation failed");
\r
2149 // Make sure we're still connected
\r
2150 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
\r
2153 Error("Lost connection, transaction cancelled");
\r
2157 // Last chance to cancel
\r
2161 fCanCancel = false;
\r
2164 fCanCancel = true;
\r
2167 fCanCancel = false;
\r
2169 if (!Status("Sending payment..."))
\r
2173 if (!CommitTransactionSpent(wtx, key))
\r
2175 Error("Error finalizing payment");
\r
2179 // Send payment tx to seller, with response going to OnReply3 via event handler
\r
2180 pnode->PushRequest("submitorder", wtx, SendingDialogOnReply3, this);
\r
2182 // Accept and broadcast transaction
\r
2183 if (!wtx.AcceptTransaction())
\r
2184 printf("ERROR: CSendingDialog : wtxNew.AcceptTransaction() %s failed\n", wtx.GetHash().ToString().c_str());
\r
2185 wtx.RelayWalletTransaction();
\r
2187 Status("Waiting for confirmation...");
\r
2188 MainFrameRepaint();
\r
2192 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
\r
2194 ((CSendingDialog*)parg)->OnReply3(vRecv);
\r
2197 void CSendingDialog::OnReply3(CDataStream& vRecv)
\r
2205 Error("The payment was sent, but the recipient was unable to verify it.\n"
\r
2206 "The transaction is recorded and will credit to the recipient,\n"
\r
2207 "but the comment information will be blank.");
\r
2213 //// what do we want to do about this?
\r
2214 Error("Payment was sent, but an invalid response was received");
\r
2220 Status("Payment completed");
\r
2228 //////////////////////////////////////////////////////////////////////////////
\r
2230 // CYourAddressDialog
\r
2233 CYourAddressDialog::CYourAddressDialog(wxWindow* parent, const string& strInitSelected) : CYourAddressDialogBase(parent)
\r
2235 // Init column headers
\r
2236 m_listCtrl->InsertColumn(0, "Label", wxLIST_FORMAT_LEFT, 200);
\r
2237 m_listCtrl->InsertColumn(1, "Bitcoin Address", wxLIST_FORMAT_LEFT, 350);
\r
2238 m_listCtrl->SetFocus();
\r
2240 // Fill listctrl with address book data
\r
2241 CRITICAL_BLOCK(cs_mapKeys)
\r
2242 CRITICAL_BLOCK(cs_mapAddressBook)
\r
2244 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2246 string strAddress = item.first;
\r
2247 string strName = item.second;
\r
2249 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2252 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2253 if (strAddress == strInitSelected)
\r
2254 m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2260 wxString CYourAddressDialog::GetAddress()
\r
2262 int nIndex = GetSelection(m_listCtrl);
\r
2265 return GetItemText(m_listCtrl, nIndex, 1);
\r
2268 void CYourAddressDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2270 // Update address book with edited name
\r
2271 if (event.IsEditCancelled())
\r
2273 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2274 SetAddressBookName(strAddress, string(event.GetText()));
\r
2275 pframeMain->RefreshListCtrl();
\r
2278 void CYourAddressDialog::OnListItemSelected(wxListEvent& event)
\r
2282 void CYourAddressDialog::OnListItemActivated(wxListEvent& event)
\r
2284 // Doubleclick edits item
\r
2285 wxCommandEvent event2;
\r
2286 OnButtonRename(event2);
\r
2289 void CYourAddressDialog::OnButtonRename(wxCommandEvent& event)
\r
2292 int nIndex = GetSelection(m_listCtrl);
\r
2295 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2296 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2297 CGetTextFromUserDialog dialog(this, "Edit Address Label", "New Label", strName);
\r
2298 if (!dialog.ShowModal())
\r
2300 strName = dialog.GetValue();
\r
2303 SetAddressBookName(strAddress, strName);
\r
2304 m_listCtrl->SetItemText(nIndex, strName);
\r
2305 pframeMain->RefreshListCtrl();
\r
2308 void CYourAddressDialog::OnButtonNew(wxCommandEvent& event)
\r
2311 CGetTextFromUserDialog dialog(this, "New Bitcoin Address", "Label", "");
\r
2312 if (!dialog.ShowModal())
\r
2314 string strName = dialog.GetValue();
\r
2316 // Generate new key
\r
2317 string strAddress = PubKeyToAddress(GenerateNewKey());
\r
2318 SetAddressBookName(strAddress, strName);
\r
2320 // Add to list and select it
\r
2321 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2322 SetSelection(m_listCtrl, nIndex);
\r
2323 m_listCtrl->SetFocus();
\r
2326 void CYourAddressDialog::OnButtonCopy(wxCommandEvent& event)
\r
2328 // Copy address box to clipboard
\r
2329 if (wxTheClipboard->Open())
\r
2331 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
\r
2332 wxTheClipboard->Close();
\r
2336 void CYourAddressDialog::OnButtonOK(wxCommandEvent& event)
\r
2342 void CYourAddressDialog::OnButtonCancel(wxCommandEvent& event)
\r
2348 void CYourAddressDialog::OnClose(wxCloseEvent& event)
\r
2359 //////////////////////////////////////////////////////////////////////////////
\r
2361 // CAddressBookDialog
\r
2364 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, bool fSendingIn) : CAddressBookDialogBase(parent)
\r
2366 fSending = fSendingIn;
\r
2368 m_buttonCancel->Show(false);
\r
2370 // Init column headers
\r
2371 m_listCtrl->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 200);
\r
2372 m_listCtrl->InsertColumn(1, "Address", wxLIST_FORMAT_LEFT, 350);
\r
2373 m_listCtrl->SetFocus();
\r
2376 wxIcon iconAddressBook;
\r
2377 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
\r
2378 SetIcon(iconAddressBook);
\r
2380 // Fill listctrl with address book data
\r
2381 CRITICAL_BLOCK(cs_mapKeys)
\r
2382 CRITICAL_BLOCK(cs_mapAddressBook)
\r
2384 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2386 string strAddress = item.first;
\r
2387 string strName = item.second;
\r
2389 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2392 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2393 if (strAddress == strInitSelected)
\r
2394 m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2400 wxString CAddressBookDialog::GetAddress()
\r
2402 int nIndex = GetSelection(m_listCtrl);
\r
2405 return GetItemText(m_listCtrl, nIndex, 1);
\r
2408 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2410 // Update address book with edited name
\r
2411 if (event.IsEditCancelled())
\r
2413 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2414 SetAddressBookName(strAddress, string(event.GetText()));
\r
2415 pframeMain->RefreshListCtrl();
\r
2418 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
\r
2422 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
\r
2426 // Doubleclick returns selection
\r
2427 EndModal(GetAddress() != "" ? 2 : 0);
\r
2431 // Doubleclick edits item
\r
2432 wxCommandEvent event2;
\r
2433 OnButtonEdit(event2);
\r
2437 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
\r
2440 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2442 wxMessageBox("This is one of your own addresses for receiving payments and cannot be entered in the address book. ", strTitle);
\r
2446 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
\r
2449 int nIndex = GetSelection(m_listCtrl);
\r
2452 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2453 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2454 string strAddressOrg = strAddress;
\r
2457 CGetTextFromUserDialog dialog(this, "Edit Address", "Name", strName, "Address", strAddress);
\r
2458 if (!dialog.ShowModal())
\r
2460 strName = dialog.GetValue1();
\r
2461 strAddress = dialog.GetValue2();
\r
2463 while (CheckIfMine(strAddress, "Edit Address"));
\r
2466 if (strAddress != strAddressOrg)
\r
2467 CWalletDB().EraseName(strAddressOrg);
\r
2468 SetAddressBookName(strAddress, strName);
\r
2469 m_listCtrl->SetItem(nIndex, 1, strAddress);
\r
2470 m_listCtrl->SetItemText(nIndex, strName);
\r
2471 pframeMain->RefreshListCtrl();
\r
2474 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
\r
2478 string strAddress;
\r
2481 CGetTextFromUserDialog dialog(this, "New Address", "Name", strName, "Address", strAddress);
\r
2482 if (!dialog.ShowModal())
\r
2484 strName = dialog.GetValue1();
\r
2485 strAddress = dialog.GetValue2();
\r
2487 while (CheckIfMine(strAddress, "New Address"));
\r
2489 // Add to list and select it
\r
2490 SetAddressBookName(strAddress, strName);
\r
2491 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2492 SetSelection(m_listCtrl, nIndex);
\r
2493 m_listCtrl->SetFocus();
\r
2494 pframeMain->RefreshListCtrl();
\r
2497 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
\r
2499 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
\r
2501 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
\r
2503 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2504 CWalletDB().EraseName(strAddress);
\r
2505 m_listCtrl->DeleteItem(nIndex);
\r
2508 pframeMain->RefreshListCtrl();
\r
2511 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
\r
2513 // Copy address box to clipboard
\r
2514 if (wxTheClipboard->Open())
\r
2516 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
\r
2517 wxTheClipboard->Close();
\r
2521 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
\r
2524 EndModal(GetAddress() != "" ? 1 : 0);
\r
2527 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
\r
2533 void CAddressBookDialog::OnClose(wxCloseEvent& event)
\r
2544 //////////////////////////////////////////////////////////////////////////////
\r
2546 // CProductsDialog
\r
2549 bool CompareIntStringPairBestFirst(const pair<int, string>& item1, const pair<int, string>& item2)
\r
2551 return (item1.first > item2.first);
\r
2554 CProductsDialog::CProductsDialog(wxWindow* parent) : CProductsDialogBase(parent)
\r
2556 // Init column headers
\r
2557 m_listCtrl->InsertColumn(0, "Title", wxLIST_FORMAT_LEFT, 200);
\r
2558 m_listCtrl->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 80);
\r
2559 m_listCtrl->InsertColumn(2, "Seller", wxLIST_FORMAT_LEFT, 80);
\r
2560 m_listCtrl->InsertColumn(3, "Stars", wxLIST_FORMAT_LEFT, 50);
\r
2561 m_listCtrl->InsertColumn(4, "Power", wxLIST_FORMAT_LEFT, 50);
\r
2563 // Tally top categories
\r
2564 map<string, int> mapTopCategories;
\r
2565 CRITICAL_BLOCK(cs_mapProducts)
\r
2566 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)
\r
2567 mapTopCategories[(*mi).second.mapValue["category"]]++;
\r
2569 // Sort top categories
\r
2570 vector<pair<int, string> > vTopCategories;
\r
2571 for (map<string, int>::iterator mi = mapTopCategories.begin(); mi != mapTopCategories.end(); ++mi)
\r
2572 vTopCategories.push_back(make_pair((*mi).second, (*mi).first));
\r
2573 sort(vTopCategories.begin(), vTopCategories.end(), CompareIntStringPairBestFirst);
\r
2575 // Fill categories combo box
\r
2577 for (vector<pair<int, string> >::iterator it = vTopCategories.begin(); it != vTopCategories.end() && nLimit-- > 0; ++it)
\r
2578 m_comboBoxCategory->Append((*it).second);
\r
2580 // Fill window with initial search
\r
2581 //wxCommandEvent event;
\r
2582 //OnButtonSearch(event);
\r
2585 void CProductsDialog::OnCombobox(wxCommandEvent& event)
\r
2587 OnButtonSearch(event);
\r
2590 bool CompareProductsBestFirst(const CProduct* p1, const CProduct* p2)
\r
2592 return (p1->nAtoms > p2->nAtoms);
\r
2595 void CProductsDialog::OnButtonSearch(wxCommandEvent& event)
\r
2597 string strCategory = (string)m_comboBoxCategory->GetValue();
\r
2598 string strSearch = (string)m_textCtrlSearch->GetValue();
\r
2600 // Search products
\r
2601 vector<CProduct*> vProductsFound;
\r
2602 CRITICAL_BLOCK(cs_mapProducts)
\r
2604 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)
\r
2606 CProduct& product = (*mi).second;
\r
2607 if (product.mapValue["category"].find(strCategory) != -1)
\r
2609 if (product.mapValue["title"].find(strSearch) != -1 ||
\r
2610 product.mapValue["description"].find(strSearch) != -1 ||
\r
2611 product.mapValue["seller"].find(strSearch) != -1)
\r
2613 vProductsFound.push_back(&product);
\r
2620 sort(vProductsFound.begin(), vProductsFound.end(), CompareProductsBestFirst);
\r
2623 foreach(CProduct* pproduct, vProductsFound)
\r
2625 InsertLine(m_listCtrl,
\r
2626 pproduct->mapValue["title"],
\r
2627 pproduct->mapValue["price"],
\r
2628 pproduct->mapValue["seller"],
\r
2629 pproduct->mapValue["stars"],
\r
2630 itostr(pproduct->nAtoms));
\r
2634 void CProductsDialog::OnListItemActivated(wxListEvent& event)
\r
2636 // Doubleclick opens product
\r
2637 CViewProductDialog* pdialog = new CViewProductDialog(this, m_vProduct[event.GetIndex()]);
\r
2647 //////////////////////////////////////////////////////////////////////////////
\r
2649 // CEditProductDialog
\r
2652 CEditProductDialog::CEditProductDialog(wxWindow* parent) : CEditProductDialogBase(parent)
\r
2654 m_textCtrlLabel[0 ] = m_textCtrlLabel0;
\r
2655 m_textCtrlLabel[1 ] = m_textCtrlLabel1;
\r
2656 m_textCtrlLabel[2 ] = m_textCtrlLabel2;
\r
2657 m_textCtrlLabel[3 ] = m_textCtrlLabel3;
\r
2658 m_textCtrlLabel[4 ] = m_textCtrlLabel4;
\r
2659 m_textCtrlLabel[5 ] = m_textCtrlLabel5;
\r
2660 m_textCtrlLabel[6 ] = m_textCtrlLabel6;
\r
2661 m_textCtrlLabel[7 ] = m_textCtrlLabel7;
\r
2662 m_textCtrlLabel[8 ] = m_textCtrlLabel8;
\r
2663 m_textCtrlLabel[9 ] = m_textCtrlLabel9;
\r
2664 m_textCtrlLabel[10] = m_textCtrlLabel10;
\r
2665 m_textCtrlLabel[11] = m_textCtrlLabel11;
\r
2666 m_textCtrlLabel[12] = m_textCtrlLabel12;
\r
2667 m_textCtrlLabel[13] = m_textCtrlLabel13;
\r
2668 m_textCtrlLabel[14] = m_textCtrlLabel14;
\r
2669 m_textCtrlLabel[15] = m_textCtrlLabel15;
\r
2670 m_textCtrlLabel[16] = m_textCtrlLabel16;
\r
2671 m_textCtrlLabel[17] = m_textCtrlLabel17;
\r
2672 m_textCtrlLabel[18] = m_textCtrlLabel18;
\r
2673 m_textCtrlLabel[19] = m_textCtrlLabel19;
\r
2675 m_textCtrlField[0 ] = m_textCtrlField0;
\r
2676 m_textCtrlField[1 ] = m_textCtrlField1;
\r
2677 m_textCtrlField[2 ] = m_textCtrlField2;
\r
2678 m_textCtrlField[3 ] = m_textCtrlField3;
\r
2679 m_textCtrlField[4 ] = m_textCtrlField4;
\r
2680 m_textCtrlField[5 ] = m_textCtrlField5;
\r
2681 m_textCtrlField[6 ] = m_textCtrlField6;
\r
2682 m_textCtrlField[7 ] = m_textCtrlField7;
\r
2683 m_textCtrlField[8 ] = m_textCtrlField8;
\r
2684 m_textCtrlField[9 ] = m_textCtrlField9;
\r
2685 m_textCtrlField[10] = m_textCtrlField10;
\r
2686 m_textCtrlField[11] = m_textCtrlField11;
\r
2687 m_textCtrlField[12] = m_textCtrlField12;
\r
2688 m_textCtrlField[13] = m_textCtrlField13;
\r
2689 m_textCtrlField[14] = m_textCtrlField14;
\r
2690 m_textCtrlField[15] = m_textCtrlField15;
\r
2691 m_textCtrlField[16] = m_textCtrlField16;
\r
2692 m_textCtrlField[17] = m_textCtrlField17;
\r
2693 m_textCtrlField[18] = m_textCtrlField18;
\r
2694 m_textCtrlField[19] = m_textCtrlField19;
\r
2696 m_buttonDel[0 ] = m_buttonDel0;
\r
2697 m_buttonDel[1 ] = m_buttonDel1;
\r
2698 m_buttonDel[2 ] = m_buttonDel2;
\r
2699 m_buttonDel[3 ] = m_buttonDel3;
\r
2700 m_buttonDel[4 ] = m_buttonDel4;
\r
2701 m_buttonDel[5 ] = m_buttonDel5;
\r
2702 m_buttonDel[6 ] = m_buttonDel6;
\r
2703 m_buttonDel[7 ] = m_buttonDel7;
\r
2704 m_buttonDel[8 ] = m_buttonDel8;
\r
2705 m_buttonDel[9 ] = m_buttonDel9;
\r
2706 m_buttonDel[10] = m_buttonDel10;
\r
2707 m_buttonDel[11] = m_buttonDel11;
\r
2708 m_buttonDel[12] = m_buttonDel12;
\r
2709 m_buttonDel[13] = m_buttonDel13;
\r
2710 m_buttonDel[14] = m_buttonDel14;
\r
2711 m_buttonDel[15] = m_buttonDel15;
\r
2712 m_buttonDel[16] = m_buttonDel16;
\r
2713 m_buttonDel[17] = m_buttonDel17;
\r
2714 m_buttonDel[18] = m_buttonDel18;
\r
2715 m_buttonDel[19] = m_buttonDel19;
\r
2717 for (int i = 1; i < FIELDS_MAX; i++)
\r
2718 ShowLine(i, false);
\r
2723 void CEditProductDialog::LayoutAll()
\r
2725 m_scrolledWindow->Layout();
\r
2726 m_scrolledWindow->GetSizer()->Fit(m_scrolledWindow);
\r
2730 void CEditProductDialog::ShowLine(int i, bool fShow)
\r
2732 m_textCtrlLabel[i]->Show(fShow);
\r
2733 m_textCtrlField[i]->Show(fShow);
\r
2734 m_buttonDel[i]->Show(fShow);
\r
2737 void CEditProductDialog::OnButtonDel0(wxCommandEvent& event) { OnButtonDel(event, 0); }
\r
2738 void CEditProductDialog::OnButtonDel1(wxCommandEvent& event) { OnButtonDel(event, 1); }
\r
2739 void CEditProductDialog::OnButtonDel2(wxCommandEvent& event) { OnButtonDel(event, 2); }
\r
2740 void CEditProductDialog::OnButtonDel3(wxCommandEvent& event) { OnButtonDel(event, 3); }
\r
2741 void CEditProductDialog::OnButtonDel4(wxCommandEvent& event) { OnButtonDel(event, 4); }
\r
2742 void CEditProductDialog::OnButtonDel5(wxCommandEvent& event) { OnButtonDel(event, 5); }
\r
2743 void CEditProductDialog::OnButtonDel6(wxCommandEvent& event) { OnButtonDel(event, 6); }
\r
2744 void CEditProductDialog::OnButtonDel7(wxCommandEvent& event) { OnButtonDel(event, 7); }
\r
2745 void CEditProductDialog::OnButtonDel8(wxCommandEvent& event) { OnButtonDel(event, 8); }
\r
2746 void CEditProductDialog::OnButtonDel9(wxCommandEvent& event) { OnButtonDel(event, 9); }
\r
2747 void CEditProductDialog::OnButtonDel10(wxCommandEvent& event) { OnButtonDel(event, 10); }
\r
2748 void CEditProductDialog::OnButtonDel11(wxCommandEvent& event) { OnButtonDel(event, 11); }
\r
2749 void CEditProductDialog::OnButtonDel12(wxCommandEvent& event) { OnButtonDel(event, 12); }
\r
2750 void CEditProductDialog::OnButtonDel13(wxCommandEvent& event) { OnButtonDel(event, 13); }
\r
2751 void CEditProductDialog::OnButtonDel14(wxCommandEvent& event) { OnButtonDel(event, 14); }
\r
2752 void CEditProductDialog::OnButtonDel15(wxCommandEvent& event) { OnButtonDel(event, 15); }
\r
2753 void CEditProductDialog::OnButtonDel16(wxCommandEvent& event) { OnButtonDel(event, 16); }
\r
2754 void CEditProductDialog::OnButtonDel17(wxCommandEvent& event) { OnButtonDel(event, 17); }
\r
2755 void CEditProductDialog::OnButtonDel18(wxCommandEvent& event) { OnButtonDel(event, 18); }
\r
2756 void CEditProductDialog::OnButtonDel19(wxCommandEvent& event) { OnButtonDel(event, 19); }
\r
2758 void CEditProductDialog::OnButtonDel(wxCommandEvent& event, int n)
\r
2762 m_scrolledWindow->GetViewStart(&x, &y);
\r
2764 for (i = n; i < FIELDS_MAX-1; i++)
\r
2766 m_textCtrlLabel[i]->SetValue(m_textCtrlLabel[i+1]->GetValue());
\r
2767 m_textCtrlField[i]->SetValue(m_textCtrlField[i+1]->GetValue());
\r
2768 if (!m_buttonDel[i+1]->IsShown())
\r
2771 m_textCtrlLabel[i]->SetValue("");
\r
2772 m_textCtrlField[i]->SetValue("");
\r
2773 ShowLine(i, false);
\r
2774 m_buttonAddField->Enable(true);
\r
2776 m_scrolledWindow->Scroll(0, y);
\r
2780 void CEditProductDialog::OnButtonAddField(wxCommandEvent& event)
\r
2782 for (int i = 0; i < FIELDS_MAX; i++)
\r
2784 if (!m_buttonDel[i]->IsShown())
\r
2787 ShowLine(i, true);
\r
2788 if (i == FIELDS_MAX-1)
\r
2789 m_buttonAddField->Enable(false);
\r
2791 m_scrolledWindow->Scroll(0, 99999);
\r
2798 void AddToMyProducts(CProduct product)
\r
2800 CProduct& productInsert = mapMyProducts[product.GetHash()];
\r
2801 productInsert = product;
\r
2802 //InsertLine(pframeMain->m_listCtrlProductsSent, &productInsert,
\r
2803 // product.mapValue["category"],
\r
2804 // product.mapValue["title"].substr(0, 100),
\r
2805 // product.mapValue["description"].substr(0, 100),
\r
2806 // product.mapValue["price"],
\r
2810 void CEditProductDialog::OnButtonSend(wxCommandEvent& event)
\r
2813 GetProduct(product);
\r
2815 // Sign the detailed product
\r
2816 product.vchPubKeyFrom = keyUser.GetPubKey();
\r
2817 if (!keyUser.Sign(product.GetSigHash(), product.vchSig))
\r
2819 wxMessageBox("Error digitally signing the product ");
\r
2823 // Save detailed product
\r
2824 AddToMyProducts(product);
\r
2826 // Strip down to summary product
\r
2827 product.mapDetails.clear();
\r
2828 product.vOrderForm.clear();
\r
2830 // Sign the summary product
\r
2831 if (!keyUser.Sign(product.GetSigHash(), product.vchSig))
\r
2833 wxMessageBox("Error digitally signing the product ");
\r
2838 if (!product.CheckProduct())
\r
2840 wxMessageBox("Errors found in product ");
\r
2845 AdvertStartPublish(pnodeLocalHost, MSG_PRODUCT, 0, product);
\r
2850 void CEditProductDialog::OnButtonPreview(wxCommandEvent& event)
\r
2853 GetProduct(product);
\r
2854 CViewProductDialog* pdialog = new CViewProductDialog(this, product);
\r
2858 void CEditProductDialog::OnButtonCancel(wxCommandEvent& event)
\r
2863 void CEditProductDialog::SetProduct(const CProduct& productIn)
\r
2865 CProduct product = productIn;
\r
2867 m_comboBoxCategory->SetValue(product.mapValue["category"]);
\r
2868 m_textCtrlTitle->SetValue(product.mapValue["title"]);
\r
2869 m_textCtrlPrice->SetValue(product.mapValue["price"]);
\r
2870 m_textCtrlDescription->SetValue(product.mapValue["description"]);
\r
2871 m_textCtrlInstructions->SetValue(product.mapValue["instructions"]);
\r
2873 for (int i = 0; i < FIELDS_MAX; i++)
\r
2875 bool fUsed = i < product.vOrderForm.size();
\r
2876 m_buttonDel[i]->Show(fUsed);
\r
2877 m_textCtrlLabel[i]->Show(fUsed);
\r
2878 m_textCtrlField[i]->Show(fUsed);
\r
2882 m_textCtrlLabel[i]->SetValue(product.vOrderForm[i].first);
\r
2883 string strControl = product.vOrderForm[i].second;
\r
2884 if (strControl.substr(0, 5) == "text=")
\r
2885 m_textCtrlField[i]->SetValue("");
\r
2886 else if (strControl.substr(0, 7) == "choice=")
\r
2887 m_textCtrlField[i]->SetValue(strControl.substr(7));
\r
2889 m_textCtrlField[i]->SetValue(strControl);
\r
2893 void CEditProductDialog::GetProduct(CProduct& product)
\r
2895 // map<string, string> mapValue;
\r
2896 // vector<pair<string, string> > vOrderForm;
\r
2898 product.mapValue["category"] = m_comboBoxCategory->GetValue().Trim();
\r
2899 product.mapValue["title"] = m_textCtrlTitle->GetValue().Trim();
\r
2900 product.mapValue["price"] = m_textCtrlPrice->GetValue().Trim();
\r
2901 product.mapValue["description"] = m_textCtrlDescription->GetValue().Trim();
\r
2902 product.mapValue["instructions"] = m_textCtrlInstructions->GetValue().Trim();
\r
2904 for (int i = 0; i < FIELDS_MAX; i++)
\r
2906 if (m_buttonDel[i]->IsShown())
\r
2908 string strLabel = (string)m_textCtrlLabel[i]->GetValue().Trim();
\r
2909 string strControl = (string)m_textCtrlField[i]->GetValue();
\r
2910 if (strControl.empty())
\r
2911 strControl = "text=";
\r
2913 strControl = "choice=" + strControl;
\r
2914 product.vOrderForm.push_back(make_pair(strLabel, strControl));
\r
2925 //////////////////////////////////////////////////////////////////////////////
\r
2927 // CViewProductDialog
\r
2930 CViewProductDialog::CViewProductDialog(wxWindow* parent, const CProduct& productIn) : CViewProductDialogBase(parent)
\r
2932 Connect(wxEVT_REPLY1, wxCommandEventHandler(CViewProductDialog::OnReply1), NULL, this);
\r
2933 AddCallbackAvailable(GetEventHandler());
\r
2935 // Fill display with product summary while waiting for details
\r
2936 product = productIn;
\r
2937 UpdateProductDisplay(false);
\r
2939 m_buttonBack->Enable(false);
\r
2940 m_buttonNext->Enable(!product.vOrderForm.empty());
\r
2941 m_htmlWinReviews->Show(true);
\r
2942 m_scrolledWindow->Show(false);
\r
2945 // Request details from seller
\r
2946 CreateThread(ThreadRequestProductDetails, new pair<CProduct, wxEvtHandler*>(product, GetEventHandler()));
\r
2949 CViewProductDialog::~CViewProductDialog()
\r
2951 RemoveCallbackAvailable(GetEventHandler());
\r
2954 void ThreadRequestProductDetails(void* parg)
\r
2956 // Extract parameters
\r
2957 pair<CProduct, wxEvtHandler*>* pitem = (pair<CProduct, wxEvtHandler*>*)parg;
\r
2958 CProduct product = pitem->first;
\r
2959 wxEvtHandler* pevthandler = pitem->second;
\r
2962 // Connect to seller
\r
2963 CNode* pnode = ConnectNode(product.addr, 5 * 60);
\r
2966 CDataStream ssEmpty;
\r
2967 AddPendingReplyEvent1(pevthandler, ssEmpty);
\r
2971 // Request detailed product, with response going to OnReply1 via dialog's event handler
\r
2972 pnode->PushRequest("getdetails", product.GetHash(), AddPendingReplyEvent1, (void*)pevthandler);
\r
2975 void CViewProductDialog::OnReply1(wxCommandEvent& event)
\r
2977 CDataStream ss = GetStreamFromEvent(event);
\r
2980 product.mapValue["description"] = "-- CAN'T CONNECT TO SELLER --\n";
\r
2981 UpdateProductDisplay(true);
\r
2986 CProduct product2;
\r
2993 if (product2.GetHash() != product.GetHash())
\r
2995 if (!product2.CheckSignature())
\r
3000 product.mapValue["description"] = "-- INVALID RESPONSE --\n";
\r
3001 UpdateProductDisplay(true);
\r
3005 product = product2;
\r
3006 UpdateProductDisplay(true);
\r
3009 bool CompareReviewsBestFirst(const CReview* p1, const CReview* p2)
\r
3011 return (p1->nAtoms > p2->nAtoms);
\r
3014 void CViewProductDialog::UpdateProductDisplay(bool fDetails)
\r
3016 // Product and reviews
\r
3018 strHTML.reserve(4000);
\r
3019 strHTML += "<html>\n"
\r
3021 "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"
\r
3024 strHTML += "<b>Category:</b> " + HtmlEscape(product.mapValue["category"]) + "<br>\n";
\r
3025 strHTML += "<b>Title:</b> " + HtmlEscape(product.mapValue["title"]) + "<br>\n";
\r
3026 strHTML += "<b>Price:</b> " + HtmlEscape(product.mapValue["price"]) + "<br>\n";
\r
3029 strHTML += "<b>Loading details...</b><br>\n<br>\n";
\r
3031 strHTML += HtmlEscape(product.mapValue["description"], true) + "<br>\n<br>\n";
\r
3033 strHTML += "<b>Reviews:</b><br>\n<br>\n";
\r
3035 if (!product.vchPubKeyFrom.empty())
\r
3037 CReviewDB reviewdb("r");
\r
3040 vector<CReview> vReviews;
\r
3041 reviewdb.ReadReviews(product.GetUserHash(), vReviews);
\r
3043 // Get reviewer's number of atoms
\r
3044 vector<CReview*> vSortedReviews;
\r
3045 vSortedReviews.reserve(vReviews.size());
\r
3046 for (vector<CReview>::reverse_iterator it = vReviews.rbegin(); it != vReviews.rend(); ++it)
\r
3048 CReview& review = *it;
\r
3050 reviewdb.ReadUser(review.GetUserHash(), user);
\r
3051 review.nAtoms = user.GetAtomCount();
\r
3052 vSortedReviews.push_back(&review);
\r
3058 stable_sort(vSortedReviews.begin(), vSortedReviews.end(), CompareReviewsBestFirst);
\r
3061 foreach(CReview* preview, vSortedReviews)
\r
3063 CReview& review = *preview;
\r
3064 int nStars = atoi(review.mapValue["stars"].c_str());
\r
3065 if (nStars < 1 || nStars > 5)
\r
3068 strHTML += "<b>" + itostr(nStars) + (nStars == 1 ? " star" : " stars") + "</b>";
\r
3069 strHTML += " ";
\r
3070 strHTML += DateStr(atoi64(review.mapValue["date"])) + "<br>\n";
\r
3071 strHTML += HtmlEscape(review.mapValue["review"], true);
\r
3072 strHTML += "<br>\n<br>\n";
\r
3076 strHTML += "</body>\n</html>\n";
\r
3078 // Shrink capacity to fit
\r
3079 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
3081 m_htmlWinReviews->SetPage(strHTML);
\r
3083 ///// need to find some other indicator to use so can allow empty order form
\r
3084 if (product.vOrderForm.empty())
\r
3088 m_staticTextInstructions->SetLabel(product.mapValue["instructions"]);
\r
3089 for (int i = 0; i < FIELDS_MAX; i++)
\r
3091 m_staticTextLabel[i] = NULL;
\r
3092 m_textCtrlField[i] = NULL;
\r
3093 m_choiceField[i] = NULL;
\r
3096 // Construct flexgridsizer
\r
3097 wxBoxSizer* bSizer21 = (wxBoxSizer*)m_scrolledWindow->GetSizer();
\r
3098 wxFlexGridSizer* fgSizer;
\r
3099 fgSizer = new wxFlexGridSizer(0, 2, 0, 0);
\r
3100 fgSizer->AddGrowableCol(1);
\r
3101 fgSizer->SetFlexibleDirection(wxBOTH);
\r
3102 fgSizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
\r
3104 // Construct order form fields
\r
3105 wxWindow* windowLast = NULL;
\r
3106 for (int i = 0; i < product.vOrderForm.size(); i++)
\r
3108 string strLabel = product.vOrderForm[i].first;
\r
3109 string strControl = product.vOrderForm[i].second;
\r
3111 if (strLabel.size() < 20)
\r
3112 strLabel.insert(strLabel.begin(), 20 - strLabel.size(), ' ');
\r
3114 m_staticTextLabel[i] = new wxStaticText(m_scrolledWindow, wxID_ANY, strLabel, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
\r
3115 m_staticTextLabel[i]->Wrap(-1);
\r
3116 fgSizer->Add(m_staticTextLabel[i], 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5);
\r
3118 if (strControl.substr(0, 5) == "text=")
\r
3120 m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
\r
3121 fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);
\r
3122 windowLast = m_textCtrlField[i];
\r
3124 else if (strControl.substr(0, 7) == "choice=")
\r
3126 vector<string> vChoices;
\r
3127 ParseString(strControl.substr(7), ',', vChoices);
\r
3129 wxArrayString arraystring;
\r
3130 foreach(const string& str, vChoices)
\r
3131 arraystring.Add(str);
\r
3133 m_choiceField[i] = new wxChoice(m_scrolledWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize, arraystring, 0);
\r
3134 fgSizer->Add(m_choiceField[i], 0, wxALL|wxALIGN_CENTER_VERTICAL, 5);
\r
3135 windowLast = m_choiceField[i];
\r
3139 m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
\r
3140 fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);
\r
3141 m_staticTextLabel[i]->Show(false);
\r
3142 m_textCtrlField[i]->Show(false);
\r
3146 // Insert after instructions and before submit/cancel buttons
\r
3147 bSizer21->Insert(2, fgSizer, 0, wxEXPAND|wxRIGHT|wxLEFT, 5);
\r
3148 m_scrolledWindow->Layout();
\r
3149 bSizer21->Fit(m_scrolledWindow);
\r
3152 // Fixup the tab order
\r
3153 m_buttonSubmitForm->MoveAfterInTabOrder(windowLast);
\r
3154 m_buttonCancelForm->MoveAfterInTabOrder(m_buttonSubmitForm);
\r
3155 //m_buttonBack->MoveAfterInTabOrder(m_buttonCancelForm);
\r
3156 //m_buttonNext->MoveAfterInTabOrder(m_buttonBack);
\r
3157 //m_buttonCancel->MoveAfterInTabOrder(m_buttonNext);
\r
3161 void CViewProductDialog::GetOrder(CWalletTx& wtx)
\r
3164 for (int i = 0; i < product.vOrderForm.size(); i++)
\r
3167 if (m_textCtrlField[i])
\r
3168 strValue = m_textCtrlField[i]->GetValue().Trim();
\r
3170 strValue = m_choiceField[i]->GetStringSelection();
\r
3171 wtx.vOrderForm.push_back(make_pair(m_staticTextLabel[i]->GetLabel(), strValue));
\r
3175 void CViewProductDialog::OnButtonSubmitForm(wxCommandEvent& event)
\r
3177 m_buttonSubmitForm->Enable(false);
\r
3178 m_buttonCancelForm->Enable(false);
\r
3183 CSendingDialog* pdialog = new CSendingDialog(this, product.addr, atoi64(product.mapValue["price"]), wtx);
\r
3184 if (!pdialog->ShowModal())
\r
3186 m_buttonSubmitForm->Enable(true);
\r
3187 m_buttonCancelForm->Enable(true);
\r
3192 void CViewProductDialog::OnButtonCancelForm(wxCommandEvent& event)
\r
3197 void CViewProductDialog::OnButtonBack(wxCommandEvent& event)
\r
3200 m_htmlWinReviews->Show(true);
\r
3201 m_scrolledWindow->Show(false);
\r
3202 m_buttonBack->Enable(false);
\r
3203 m_buttonNext->Enable(!product.vOrderForm.empty());
\r
3208 void CViewProductDialog::OnButtonNext(wxCommandEvent& event)
\r
3210 if (!product.vOrderForm.empty())
\r
3213 m_htmlWinReviews->Show(false);
\r
3214 m_scrolledWindow->Show(true);
\r
3215 m_buttonBack->Enable(true);
\r
3216 m_buttonNext->Enable(false);
\r
3222 void CViewProductDialog::OnButtonCancel(wxCommandEvent& event)
\r
3233 //////////////////////////////////////////////////////////////////////////////
\r
3235 // CViewOrderDialog
\r
3238 CViewOrderDialog::CViewOrderDialog(wxWindow* parent, CWalletTx order, bool fReceived) : CViewOrderDialogBase(parent)
\r
3240 int64 nPrice = (fReceived ? order.GetCredit() : order.GetDebit());
\r
3243 strHTML.reserve(4000);
\r
3244 strHTML += "<html>\n"
\r
3246 "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"
\r
3249 strHTML += "<b>Time:</b> " + HtmlEscape(DateTimeStr(order.nTimeReceived)) + "<br>\n";
\r
3250 strHTML += "<b>Price:</b> " + HtmlEscape(FormatMoney(nPrice)) + "<br>\n";
\r
3251 strHTML += "<b>Status:</b> " + HtmlEscape(FormatTxStatus(order)) + "<br>\n";
\r
3253 strHTML += "<table>\n";
\r
3254 for (int i = 0; i < order.vOrderForm.size(); i++)
\r
3256 strHTML += " <tr><td><b>" + HtmlEscape(order.vOrderForm[i].first) + ":</b></td>";
\r
3257 strHTML += "<td>" + HtmlEscape(order.vOrderForm[i].second) + "</td></tr>\n";
\r
3259 strHTML += "</table>\n";
\r
3261 strHTML += "</body>\n</html>\n";
\r
3263 // Shrink capacity to fit
\r
3264 // (strings are ref counted, so it may live on in SetPage)
\r
3265 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
3267 m_htmlWin->SetPage(strHTML);
\r
3270 void CViewOrderDialog::OnButtonOK(wxCommandEvent& event)
\r
3281 //////////////////////////////////////////////////////////////////////////////
\r
3283 // CEditReviewDialog
\r
3286 CEditReviewDialog::CEditReviewDialog(wxWindow* parent) : CEditReviewDialogBase(parent)
\r
3290 void CEditReviewDialog::OnButtonSubmit(wxCommandEvent& event)
\r
3292 if (m_choiceStars->GetSelection() == -1)
\r
3294 wxMessageBox("Please select a rating ");
\r
3299 GetReview(review);
\r
3301 // Sign the review
\r
3302 review.vchPubKeyFrom = keyUser.GetPubKey();
\r
3303 if (!keyUser.Sign(review.GetSigHash(), review.vchSig))
\r
3305 wxMessageBox("Unable to digitally sign the review ");
\r
3310 if (!review.AcceptReview())
\r
3312 wxMessageBox("Save failed ");
\r
3315 RelayMessage(CInv(MSG_REVIEW, review.GetHash()), review);
\r
3320 void CEditReviewDialog::OnButtonCancel(wxCommandEvent& event)
\r
3325 void CEditReviewDialog::GetReview(CReview& review)
\r
3327 review.mapValue["time"] = i64tostr(GetAdjustedTime());
\r
3328 review.mapValue["stars"] = itostr(m_choiceStars->GetSelection()+1);
\r
3329 review.mapValue["review"] = m_textCtrlReview->GetValue();
\r
3338 //////////////////////////////////////////////////////////////////////////////
\r
3345 ID_TASKBAR_RESTORE = 10001,
\r
3346 ID_TASKBAR_OPTIONS,
\r
3347 ID_TASKBAR_GENERATE,
\r
3351 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
\r
3352 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
\r
3353 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
\r
3354 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
\r
3355 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
\r
3356 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
\r
3357 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
\r
3360 void CMyTaskBarIcon::Show(bool fShow)
\r
3362 static char pszPrevTip[200];
\r
3365 string strTooltip = "Bitcoin";
\r
3366 if (fGenerateBitcoins)
\r
3367 strTooltip = "Bitcoin - Generating";
\r
3368 if (fGenerateBitcoins && vNodes.empty())
\r
3369 strTooltip = "Bitcoin - (not connected)";
\r
3371 // Optimization, only update when changed, using char array to be reentrant
\r
3372 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
\r
3374 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
\r
3376 SetIcon(wxICON(bitcoin), strTooltip);
\r
3378 SetIcon(bitcoin20_xpm, strTooltip);
\r
3384 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
\r
3389 void CMyTaskBarIcon::Hide()
\r
3394 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
\r
3399 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
\r
3404 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
\r
3406 // Since it's modal, get the main window to do it
\r
3407 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_MENUOPTIONSOPTIONS);
\r
3408 pframeMain->GetEventHandler()->AddPendingEvent(event2);
\r
3411 void CMyTaskBarIcon::Restore()
\r
3413 pframeMain->Show();
\r
3414 wxIconizeEvent event(0, false);
\r
3415 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
3416 pframeMain->Iconize(false);
\r
3417 pframeMain->Raise();
\r
3420 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
\r
3422 GenerateBitcoins(event.IsChecked());
\r
3425 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
\r
3427 event.Check(fGenerateBitcoins);
\r
3430 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
\r
3432 pframeMain->Close(true);
\r
3435 void CMyTaskBarIcon::UpdateTooltip()
\r
3437 if (IsIconInstalled())
\r
3441 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
\r
3443 wxMenu* pmenu = new wxMenu;
\r
3444 pmenu->Append(ID_TASKBAR_RESTORE, "&Open Bitcoin");
\r
3445 pmenu->Append(ID_TASKBAR_OPTIONS, "O&ptions...");
\r
3446 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, "&Generate Coins")->Check(fGenerateBitcoins);
\r
3447 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
\r
3448 pmenu->AppendSeparator();
\r
3449 pmenu->Append(ID_TASKBAR_EXIT, "E&xit");
\r
3463 //////////////////////////////////////////////////////////////////////////////
\r
3468 // Define a new application
\r
3469 class CMyApp: public wxApp
\r
3478 // 2nd-level exception handling: we get all the exceptions occurring in any
\r
3479 // event handler here
\r
3480 virtual bool OnExceptionInMainLoop();
\r
3482 // 3rd, and final, level exception handling: whenever an unhandled
\r
3483 // exception is caught, this function is called
\r
3484 virtual void OnUnhandledException();
\r
3486 // and now for something different: this function is called in case of a
\r
3487 // crash (e.g. dereferencing null pointer, division by 0, ...)
\r
3488 virtual void OnFatalException();
\r
3491 IMPLEMENT_APP(CMyApp)
\r
3493 bool CMyApp::OnInit()
\r
3495 bool fRet = false;
\r
3500 catch (std::exception& e) {
\r
3501 PrintException(&e, "OnInit()");
\r
3503 PrintException(NULL, "OnInit()");
\r
3510 bool CMyApp::OnInit2()
\r
3513 // Turn off microsoft heap dump noise for now
\r
3514 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
\r
3515 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
\r
3517 #if defined(__WXMSW__) && defined(__WXDEBUG__)
\r
3518 // Disable malfunctioning wxWidgets debug assertion
\r
3519 g_isPainting = 10000;
\r
3521 wxImage::AddHandler(new wxPNGHandler);
\r
3523 SetAppName("Bitcoin");
\r
3525 SetAppName("bitcoin");
\r
3532 if (argc > 1 && argv[1][0] != '-' && argv[1][0] != '/')
\r
3534 int ret = CommandLineRPC(argc, argv);
\r
3538 ParseParameters(argc, argv);
\r
3539 if (mapArgs.count("-?") || mapArgs.count("--help"))
\r
3543 "Usage: bitcoin [options]\t\t\t\t\t\t\n"
\r
3545 " -gen\t\t Generate coins\n"
\r
3546 " -gen=0\t\t Don't generate coins\n"
\r
3547 " -min\t\t Start minimized\n"
\r
3548 " -datadir=<dir>\t Specify data directory\n"
\r
3549 " -proxy=<ip:port>\t Connect through socks4 proxy\n"
\r
3550 " -addnode=<ip>\t Add a node to connect to\n"
\r
3551 " -connect=<ip>\t Connect only to the specified node\n"
\r
3552 " -?\t\t This help message\n";
\r
3553 wxMessageBox(strUsage, "Bitcoin", wxOK);
\r
3556 "Usage: bitcoin [options]\n"
\r
3558 " -gen Generate coins\n"
\r
3559 " -gen=0 Don't generate coins\n"
\r
3560 " -min Start minimized\n"
\r
3561 " -datadir=<dir> Specify data directory\n"
\r
3562 " -proxy=<ip:port> Connect through socks4 proxy\n"
\r
3563 " -addnode=<ip> Add a node to connect to\n"
\r
3564 " -connect=<ip> Connect only to the specified node\n"
\r
3565 " -? This help message\n";
\r
3566 fprintf(stderr, "%s", strUsage.c_str());
\r
3571 if (mapArgs.count("-datadir"))
\r
3572 strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir));
\r
3574 if (mapArgs.count("-debug"))
\r
3577 if (mapArgs.count("-printtodebugger"))
\r
3578 fPrintToDebugger = true;
\r
3580 if (mapArgs.count("-daemon") || mapArgs.count("-d"))
\r
3583 /// todo: need to fork
\r
3584 /// should it fork after the bind/single instance stuff?
\r
3587 if (!fDebug && !pszSetDataDir[0])
\r
3588 ShrinkDebugFile();
\r
3589 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
\r
3590 printf("Bitcoin version %d%s, OS version %s\n", VERSION, pszSubVer, ((string)wxGetOsDescription()).c_str());
\r
3592 if (mapArgs.count("-loadblockindextest"))
\r
3595 txdb.LoadBlockIndex();
\r
3601 // Limit to single instance per user
\r
3602 // Required to protect the database files if we're going to keep deleting log.*
\r
3605 // todo: wxSingleInstanceChecker wasn't working on Linux, never deleted its lock file
\r
3606 // maybe should go by whether successfully bind port 8333 instead
\r
3607 wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH");
\r
3608 for (int i = 0; i < strMutexName.size(); i++)
\r
3609 if (!isalnum(strMutexName[i]))
\r
3610 strMutexName[i] = '.';
\r
3611 wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
\r
3612 if (psingleinstancechecker->IsAnotherRunning())
\r
3614 printf("Existing instance found\n");
\r
3615 unsigned int nStart = GetTime();
\r
3618 // TODO: find out how to do this in Linux, or replace with wxWidgets commands
\r
3619 // Show the previous instance and exit
\r
3620 HWND hwndPrev = FindWindow("wxWindowClassNR", "Bitcoin");
\r
3623 if (IsIconic(hwndPrev))
\r
3624 ShowWindow(hwndPrev, SW_RESTORE);
\r
3625 SetForegroundWindow(hwndPrev);
\r
3629 if (GetTime() > nStart + 60)
\r
3632 // Resume this instance if the other exits
\r
3633 delete psingleinstancechecker;
\r
3635 psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
\r
3636 if (!psingleinstancechecker->IsAnotherRunning())
\r
3642 // Bind to the port early so we can tell if another instance is already running.
\r
3643 // This is a backup to wxSingleInstanceChecker, which doesn't work on Linux.
\r
3645 if (!BindListenPort(strErrors))
\r
3647 wxMessageBox(strErrors, "Bitcoin");
\r
3652 // Load data files
\r
3658 printf("Loading addresses...\n");
\r
3659 nStart = GetTimeMillis();
\r
3660 if (!LoadAddresses())
\r
3661 strErrors += "Error loading addr.dat \n";
\r
3662 printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
3664 printf("Loading block index...\n");
\r
3665 nStart = GetTimeMillis();
\r
3666 if (!LoadBlockIndex())
\r
3667 strErrors += "Error loading blkindex.dat \n";
\r
3668 printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
3670 printf("Loading wallet...\n");
\r
3671 nStart = GetTimeMillis();
\r
3672 if (!LoadWallet(fFirstRun))
\r
3673 strErrors += "Error loading wallet.dat \n";
\r
3674 printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
3676 printf("Done loading\n");
\r
3679 printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size());
\r
3680 printf("nBestHeight = %d\n", nBestHeight);
\r
3681 printf("mapKeys.size() = %d\n", mapKeys.size());
\r
3682 printf("mapPubKeys.size() = %d\n", mapPubKeys.size());
\r
3683 printf("mapWallet.size() = %d\n", mapWallet.size());
\r
3684 printf("mapAddressBook.size() = %d\n", mapAddressBook.size());
\r
3686 if (!strErrors.empty())
\r
3688 wxMessageBox(strErrors, "Bitcoin");
\r
3692 // Add wallet transactions that aren't already in a block to mapTransactions
\r
3693 ReacceptWalletTransactions();
\r
3698 if (mapArgs.count("-printblockindex") || mapArgs.count("-printblocktree"))
\r
3704 if (mapArgs.count("-printblock"))
\r
3706 string strMatch = mapArgs["-printblock"];
\r
3708 for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
\r
3710 uint256 hash = (*mi).first;
\r
3711 if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0)
\r
3713 CBlockIndex* pindex = (*mi).second;
\r
3715 block.ReadFromDisk(pindex);
\r
3716 block.BuildMerkleTree();
\r
3723 printf("No blocks matching %s were found\n", strMatch.c_str());
\r
3727 if (mapArgs.count("-gen"))
\r
3729 if (mapArgs["-gen"].empty())
\r
3730 fGenerateBitcoins = true;
\r
3732 fGenerateBitcoins = (atoi(mapArgs["-gen"].c_str()) != 0);
\r
3735 if (mapArgs.count("-proxy"))
\r
3738 addrProxy = CAddress(mapArgs["-proxy"]);
\r
3739 if (!addrProxy.IsValid())
\r
3741 wxMessageBox("Invalid -proxy address", "Bitcoin");
\r
3746 if (mapArgs.count("-addnode"))
\r
3748 foreach(string strAddr, mapMultiArgs["-addnode"])
\r
3750 CAddress addr(strAddr, NODE_NETWORK);
\r
3751 addr.nTime = 0; // so it won't relay unless successfully connected
\r
3752 if (addr.IsValid())
\r
3758 // Create the main frame window
\r
3762 pframeMain = new CMainFrame(NULL);
\r
3763 if (mapArgs.count("-min"))
\r
3764 pframeMain->Iconize(true);
\r
3765 pframeMain->Show(true); // have to show first to get taskbar button to hide
\r
3766 if (fMinimizeToTray && pframeMain->IsIconized())
\r
3767 fClosedToTray = true;
\r
3768 pframeMain->Show(!fClosedToTray);
\r
3769 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
3771 CreateThread(ThreadDelayedRepaint, NULL);
\r
3774 if (!CheckDiskSpace())
\r
3777 RandAddSeedPerfmon();
\r
3779 if (!CreateThread(StartNode, NULL))
\r
3780 wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin");
\r
3782 if (mapArgs.count("-server") || fDaemon)
\r
3783 CreateThread(ThreadRPCServer, NULL);
\r
3786 SetStartOnSystemStartup(true);
\r
3793 if (argc >= 2 && stricmp(argv[1], "-send") == 0)
\r
3795 if (argc >= 2 && strcmp(argv[1], "-send") == 0)
\r
3800 ParseMoney(argv[2], nValue);
\r
3802 string strAddress;
\r
3804 strAddress = argv[3];
\r
3805 CAddress addr(strAddress);
\r
3808 wtx.mapValue["to"] = strAddress;
\r
3809 wtx.mapValue["from"] = addrLocalHost.ToString();
\r
3810 wtx.mapValue["message"] = "command line send";
\r
3812 // Send to IP address
\r
3813 CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx);
\r
3814 if (!pdialog->ShowModal())
\r
3821 int CMyApp::OnExit()
\r
3824 return wxApp::OnExit();
\r
3827 bool CMyApp::OnExceptionInMainLoop()
\r
3833 catch (std::exception& e)
\r
3835 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
\r
3836 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
3842 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
\r
3843 wxLogWarning("Unknown exception");
\r
3851 void CMyApp::OnUnhandledException()
\r
3853 // this shows how we may let some exception propagate uncaught
\r
3858 catch (std::exception& e)
\r
3860 PrintException(&e, "CMyApp::OnUnhandledException()");
\r
3861 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
3867 PrintException(NULL, "CMyApp::OnUnhandledException()");
\r
3868 wxLogWarning("Unknown exception");
\r
3874 void CMyApp::OnFatalException()
\r
3876 wxMessageBox("Program has crashed and will terminate. ", "Bitcoin", wxOK | wxICON_ERROR);
\r
3884 typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
\r
3886 string MyGetSpecialFolderPath(int nFolder, bool fCreate)
\r
3888 char pszPath[MAX_PATH+100] = "";
\r
3890 // SHGetSpecialFolderPath is not usually available on NT 4.0
\r
3891 HMODULE hShell32 = LoadLibrary("shell32.dll");
\r
3894 PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath =
\r
3895 (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA");
\r
3896 if (pSHGetSpecialFolderPath)
\r
3897 (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate);
\r
3898 FreeModule(hShell32);
\r
3902 if (pszPath[0] == '\0')
\r
3904 if (nFolder == CSIDL_STARTUP)
\r
3906 strcpy(pszPath, getenv("USERPROFILE"));
\r
3907 strcat(pszPath, "\\Start Menu\\Programs\\Startup");
\r
3909 else if (nFolder == CSIDL_APPDATA)
\r
3911 strcpy(pszPath, getenv("APPDATA"));
\r
3918 string StartupShortcutPath()
\r
3920 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
\r
3923 bool GetStartOnSystemStartup()
\r
3925 return wxFileExists(StartupShortcutPath());
\r
3928 void SetStartOnSystemStartup(bool fAutoStart)
\r
3930 // If the shortcut exists already, remove it for updating
\r
3931 remove(StartupShortcutPath().c_str());
\r
3935 CoInitialize(NULL);
\r
3937 // Get a pointer to the IShellLink interface.
\r
3938 IShellLink* psl = NULL;
\r
3939 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
\r
3940 CLSCTX_INPROC_SERVER, IID_IShellLink,
\r
3941 reinterpret_cast<void**>(&psl));
\r
3943 if (SUCCEEDED(hres))
\r
3945 // Get the current executable path
\r
3946 char pszExePath[MAX_PATH];
\r
3947 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
\r
3949 // Set the path to the shortcut target
\r
3950 psl->SetPath(pszExePath);
\r
3951 PathRemoveFileSpec(pszExePath);
\r
3952 psl->SetWorkingDirectory(pszExePath);
\r
3953 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
\r
3955 // Query IShellLink for the IPersistFile interface for
\r
3956 // saving the shortcut in persistent storage.
\r
3957 IPersistFile* ppf = NULL;
\r
3958 hres = psl->QueryInterface(IID_IPersistFile,
\r
3959 reinterpret_cast<void**>(&ppf));
\r
3960 if (SUCCEEDED(hres))
\r
3962 WCHAR pwsz[MAX_PATH];
\r
3963 // Ensure that the string is ANSI.
\r
3964 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
\r
3965 // Save the link by calling IPersistFile::Save.
\r
3966 hres = ppf->Save(pwsz, TRUE);
\r
3975 bool GetStartOnSystemStartup() { return false; }
\r
3976 void SetStartOnSystemStartup(bool fAutoStart) { }
\r