1 // Copyright (c) 2009 Satoshi Nakamoto
\r
2 // Distributed under the MIT/X11 software license, see the accompanying
\r
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
\r
10 void ThreadRequestProductDetails(void* parg);
\r
11 void ThreadRandSendTest(void* parg);
\r
12 bool GetStartOnSystemStartup();
\r
13 void SetStartOnSystemStartup(bool fAutoStart);
\r
17 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
\r
18 DEFINE_EVENT_TYPE(wxEVT_REPLY1)
\r
19 DEFINE_EVENT_TYPE(wxEVT_REPLY2)
\r
20 DEFINE_EVENT_TYPE(wxEVT_REPLY3)
\r
22 CMainFrame* pframeMain = NULL;
\r
23 CMyTaskBarIcon* ptaskbaricon = NULL;
\r
24 map<string, string> mapAddressBook;
\r
25 bool fRandSendTest = false;
\r
27 extern int g_isPainting;
\r
28 bool fClosedToTray = false;
\r
31 int fShowGenerated = true;
\r
32 int fMinimizeToTray = true;
\r
33 int fMinimizeOnClose = true;
\r
41 //////////////////////////////////////////////////////////////////////////////
\r
46 void HandleCtrlA(wxKeyEvent& event)
\r
48 // Ctrl-a select all
\r
49 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
\r
50 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
\r
51 textCtrl->SetSelection(-1, -1);
\r
57 //char pszHourFormat[256];
\r
58 //pszHourFormat[0] = '\0';
\r
59 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
\r
60 //return (pszHourFormat[0] != '0');
\r
64 string DateStr(int64 nTime)
\r
66 // Can only be used safely here in the UI
\r
67 return (string)wxDateTime((time_t)nTime).FormatDate();
\r
70 string DateTimeStr(int64 nTime)
\r
72 // Can only be used safely here in the UI
\r
73 wxDateTime datetime((time_t)nTime);
\r
75 return (string)datetime.Format("%x %H:%M");
\r
77 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
\r
80 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
\r
82 // Helper to simplify access to listctrl
\r
84 item.m_itemId = nIndex;
\r
85 item.m_col = nColumn;
\r
86 item.m_mask = wxLIST_MASK_TEXT;
\r
87 if (!listCtrl->GetItem(item))
\r
89 return item.GetText();
\r
92 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
\r
94 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
95 listCtrl->SetItem(nIndex, 1, str1);
\r
99 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
101 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
102 listCtrl->SetItem(nIndex, 1, str1);
\r
103 listCtrl->SetItem(nIndex, 2, str2);
\r
104 listCtrl->SetItem(nIndex, 3, str3);
\r
105 listCtrl->SetItem(nIndex, 4, str4);
\r
109 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
111 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
112 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
\r
113 listCtrl->SetItem(nIndex, 1, str1);
\r
114 listCtrl->SetItem(nIndex, 2, str2);
\r
115 listCtrl->SetItem(nIndex, 3, str3);
\r
116 listCtrl->SetItem(nIndex, 4, str4);
\r
120 void SetSelection(wxListCtrl* listCtrl, int nIndex)
\r
122 int nSize = listCtrl->GetItemCount();
\r
123 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
124 for (int i = 0; i < nSize; i++)
\r
125 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
\r
128 int GetSelection(wxListCtrl* listCtrl)
\r
130 int nSize = listCtrl->GetItemCount();
\r
131 for (int i = 0; i < nSize; i++)
\r
132 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
\r
138 string HtmlEscape(const char* psz, bool fMultiLine=false)
\r
141 for (const char* p = psz; *p; p++)
\r
143 if (*p == '<') len += 4;
\r
144 else if (*p == '>') len += 4;
\r
145 else if (*p == '&') len += 5;
\r
146 else if (*p == '"') len += 6;
\r
147 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
\r
148 else if (*p == '\n' && fMultiLine) len += 5;
\r
154 for (const char* p = psz; *p; p++)
\r
156 if (*p == '<') str += "<";
\r
157 else if (*p == '>') str += ">";
\r
158 else if (*p == '&') str += "&";
\r
159 else if (*p == '"') str += """;
\r
160 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
\r
161 else if (*p == '\n' && fMultiLine) str += "<br>\n";
\r
168 string HtmlEscape(const string& str, bool fMultiLine=false)
\r
170 return HtmlEscape(str.c_str(), fMultiLine);
\r
173 void AddToMyProducts(CProduct product)
\r
175 CProduct& productInsert = mapMyProducts[product.GetHash()];
\r
176 productInsert = product;
\r
177 InsertLine(pframeMain->m_listCtrlProductsSent, &productInsert,
\r
178 product.mapValue["category"],
\r
179 product.mapValue["title"].substr(0, 100),
\r
180 product.mapValue["description"].substr(0, 100),
\r
181 product.mapValue["price"],
\r
185 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
\r
187 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
\r
191 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
\r
194 return wxMessageBox(message, caption, style, parent, x, y);
\r
196 if (wxThread::IsMain())
\r
198 return wxMessageBox(message, caption, style, parent, x, y);
\r
203 bool fDone = false;
\r
204 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
\r
221 //////////////////////////////////////////////////////////////////////////////
\r
225 // If this code gets used again, it should be replaced with something like UIThreadCall
\r
227 set<void*> setCallbackAvailable;
\r
228 CCriticalSection cs_setCallbackAvailable;
\r
230 void AddCallbackAvailable(void* p)
\r
232 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
233 setCallbackAvailable.insert(p);
\r
236 void RemoveCallbackAvailable(void* p)
\r
238 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
239 setCallbackAvailable.erase(p);
\r
242 bool IsCallbackAvailable(void* p)
\r
244 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
245 return setCallbackAvailable.count(p);
\r
249 template<typename T>
\r
250 void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T pbeginIn, const T pendIn)
\r
255 const char* pbegin = (pendIn != pbeginIn) ? &pbeginIn[0] : NULL;
\r
256 const char* pend = pbegin + (pendIn - pbeginIn) * sizeof(pbeginIn[0]);
\r
257 wxCommandEvent event(nEventID);
\r
258 wxString strData(wxChar(0), (pend - pbegin) / sizeof(wxChar) + 1);
\r
259 memcpy(&strData[0], pbegin, pend - pbegin);
\r
260 event.SetString(strData);
\r
261 event.SetInt(pend - pbegin);
\r
263 pevthandler->AddPendingEvent(event);
\r
267 void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T& obj)
\r
271 AddPendingCustomEvent(pevthandler, nEventID, ss.begin(), ss.end());
\r
274 void AddPendingReplyEvent1(void* pevthandler, CDataStream& vRecv)
\r
276 if (IsCallbackAvailable(pevthandler))
\r
277 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY1, vRecv.begin(), vRecv.end());
\r
280 void AddPendingReplyEvent2(void* pevthandler, CDataStream& vRecv)
\r
282 if (IsCallbackAvailable(pevthandler))
\r
283 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY2, vRecv.begin(), vRecv.end());
\r
286 void AddPendingReplyEvent3(void* pevthandler, CDataStream& vRecv)
\r
288 if (IsCallbackAvailable(pevthandler))
\r
289 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY3, vRecv.begin(), vRecv.end());
\r
292 CDataStream GetStreamFromEvent(const wxCommandEvent& event)
\r
294 wxString strData = event.GetString();
\r
295 const char* pszBegin = strData.c_str();
\r
296 return CDataStream(pszBegin, pszBegin + event.GetInt(), SER_NETWORK);
\r
305 //////////////////////////////////////////////////////////////////////////////
\r
310 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
\r
312 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
\r
315 fRefreshListCtrl = false;
\r
316 fRefreshListCtrlRunning = false;
\r
317 fOnSetFocusAddress = false;
\r
319 m_choiceFilter->SetSelection(0);
\r
320 double dResize = 1.0;
\r
322 SetIcon(wxICON(bitcoin));
\r
324 SetIcon(bitcoin16_xpm);
\r
325 wxFont fontTmp = m_staticText41->GetFont();
\r
326 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
\r
327 m_staticTextBalance->SetFont(fontTmp);
\r
328 m_staticTextBalance->SetSize(140, 17);
\r
329 // & underlines don't work on the toolbar buttons on gtk
\r
330 m_toolBar->ClearTools();
\r
331 m_toolBar->AddTool(wxID_BUTTONSEND, "Send Coins", wxBitmap(send20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
\r
332 m_toolBar->AddTool(wxID_BUTTONRECEIVE, "Address Book", wxBitmap(addressbook20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
\r
333 m_toolBar->Realize();
\r
334 // resize to fit ubuntu's huge default font
\r
336 SetSize(dResize * GetSize().GetWidth(), 1.1 * GetSize().GetHeight());
\r
339 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
340 m_listCtrl->SetFocus();
\r
341 ptaskbaricon = new CMyTaskBarIcon();
\r
343 // Init column headers
\r
344 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
\r
345 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
\r
347 m_listCtrl->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
348 m_listCtrl->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
349 m_listCtrl->InsertColumn(2, "Status", wxLIST_FORMAT_LEFT, dResize * 90);
\r
350 m_listCtrl->InsertColumn(3, "Date", wxLIST_FORMAT_LEFT, dResize * nDateWidth);
\r
351 m_listCtrl->InsertColumn(4, "Description", wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
\r
352 m_listCtrl->InsertColumn(5, "Debit", wxLIST_FORMAT_RIGHT, dResize * 79);
\r
353 m_listCtrl->InsertColumn(6, "Credit", wxLIST_FORMAT_RIGHT, dResize * 79);
\r
355 //m_listCtrlProductsSent->InsertColumn(0, "Category", wxLIST_FORMAT_LEFT, 100);
\r
356 //m_listCtrlProductsSent->InsertColumn(1, "Title", wxLIST_FORMAT_LEFT, 100);
\r
357 //m_listCtrlProductsSent->InsertColumn(2, "Description", wxLIST_FORMAT_LEFT, 100);
\r
358 //m_listCtrlProductsSent->InsertColumn(3, "Price", wxLIST_FORMAT_LEFT, 100);
\r
359 //m_listCtrlProductsSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
\r
361 //m_listCtrlOrdersSent->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100);
\r
362 //m_listCtrlOrdersSent->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100);
\r
363 //m_listCtrlOrdersSent->InsertColumn(2, "", wxLIST_FORMAT_LEFT, 100);
\r
364 //m_listCtrlOrdersSent->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100);
\r
365 //m_listCtrlOrdersSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
\r
367 //m_listCtrlOrdersReceived->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100);
\r
368 //m_listCtrlOrdersReceived->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100);
\r
369 //m_listCtrlOrdersReceived->InsertColumn(2, "Payment Status", wxLIST_FORMAT_LEFT, 100);
\r
370 //m_listCtrlOrdersReceived->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100);
\r
371 //m_listCtrlOrdersReceived->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
\r
374 int pnWidths[3] = { -100, 88, 290 };
\r
376 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
\r
377 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
\r
379 m_statusBar->SetFieldsCount(3, pnWidths);
\r
381 // Fill your address text box
\r
382 vector<unsigned char> vchPubKey;
\r
383 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
\r
384 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
\r
386 // Fill listctrl with wallet transactions
\r
390 CMainFrame::~CMainFrame()
\r
393 delete ptaskbaricon;
\r
394 ptaskbaricon = NULL;
\r
397 void ExitTimeout(void* parg)
\r
405 void Shutdown(void* parg)
\r
407 static CCriticalSection cs_Shutdown;
\r
408 static bool fTaken;
\r
410 CRITICAL_BLOCK(cs_Shutdown)
\r
412 fFirstThread = !fTaken;
\r
419 nTransactionsUpdated++;
\r
423 CreateThread(ExitTimeout, NULL);
\r
425 printf("Bitcoin exiting\n\n");
\r
438 void CMainFrame::OnClose(wxCloseEvent& event)
\r
440 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
\r
442 // Divert close to minimize
\r
444 fClosedToTray = true;
\r
450 CreateThread(Shutdown, NULL);
\r
454 void CMainFrame::OnIconize(wxIconizeEvent& event)
\r
456 // Hide the task bar button when minimized.
\r
457 // Event is sent when the frame is minimized or restored.
\r
458 if (!event.Iconized())
\r
459 fClosedToTray = false;
\r
461 // Tray is not reliable on Linux gnome
\r
462 fClosedToTray = false;
\r
464 if (fMinimizeToTray && event.Iconized())
\r
465 fClosedToTray = true;
\r
466 Show(!fClosedToTray);
\r
467 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
470 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
\r
473 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
\r
474 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
\r
477 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
\r
479 // Hidden columns not resizeable
\r
480 if (event.GetColumn() <= 1 && !fDebug)
\r
484 int CMainFrame::GetSortIndex(const string& strSort)
\r
489 // The wx generic listctrl implementation used on GTK doesn't sort,
\r
490 // so we have to do it ourselves. Remember, we sort in reverse order.
\r
491 // In the wx generic implementation, they store the list of items
\r
492 // in a vector, so indexed lookups are fast, but inserts are slower
\r
493 // the closer they are to the top.
\r
495 int high = m_listCtrl->GetItemCount();
\r
498 int mid = low + ((high - low) / 2);
\r
499 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
\r
508 void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6)
\r
510 string str0 = strSort;
\r
511 long nData = *(long*)&hashKey;
\r
514 if (!fNew && nIndex == -1)
\r
516 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
517 if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())
\r
521 // fNew is for blind insert, only use if you're sure it's new
\r
522 if (fNew || nIndex == -1)
\r
524 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), str0);
\r
528 // If sort key changed, must delete and reinsert to make it relocate
\r
529 if (GetItemText(m_listCtrl, nIndex, 0) != str0)
\r
531 m_listCtrl->DeleteItem(nIndex);
\r
532 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), str0);
\r
536 m_listCtrl->SetItem(nIndex, 1, hashKey.ToString());
\r
537 m_listCtrl->SetItem(nIndex, 2, str2);
\r
538 m_listCtrl->SetItem(nIndex, 3, str3);
\r
539 m_listCtrl->SetItem(nIndex, 4, str4);
\r
540 m_listCtrl->SetItem(nIndex, 5, str5);
\r
541 m_listCtrl->SetItem(nIndex, 6, str6);
\r
542 m_listCtrl->SetItemData(nIndex, nData);
\r
545 bool CMainFrame::DeleteLine(uint256 hashKey)
\r
547 long nData = *(long*)&hashKey;
\r
551 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
552 if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())
\r
556 m_listCtrl->DeleteItem(nIndex);
\r
558 return nIndex != -1;
\r
561 string FormatTxStatus(const CWalletTx& wtx)
\r
564 if (!wtx.IsFinal())
\r
566 if (wtx.nLockTime < 500000000)
\r
567 return strprintf("Open for %d blocks", nBestHeight - wtx.nLockTime);
\r
569 return strprintf("Open until %s", DateTimeStr(wtx.nLockTime).c_str());
\r
573 int nDepth = wtx.GetDepthInMainChain();
\r
574 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
575 return strprintf("%d/offline?", nDepth);
\r
576 else if (nDepth < 6)
\r
577 return strprintf("%d/unconfirmed", nDepth);
\r
579 return strprintf("%d blocks", nDepth);
\r
583 string SingleLine(const string& strIn)
\r
586 bool fOneSpace = false;
\r
587 foreach(int c, strIn)
\r
595 if (fOneSpace && !strOut.empty())
\r
604 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
\r
606 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
\r
607 int64 nCredit = wtx.GetCredit();
\r
608 int64 nDebit = wtx.GetDebit();
\r
609 int64 nNet = nCredit - nDebit;
\r
610 uint256 hash = wtx.GetHash();
\r
611 string strStatus = FormatTxStatus(wtx);
\r
612 map<string, string> mapValue = wtx.mapValue;
\r
613 wtx.nLinesDisplayed = 1;
\r
614 nListViewUpdated++;
\r
617 if (wtx.IsCoinBase())
\r
619 // Don't show generated coin until confirmed by at least one block after it
\r
620 // so we don't get the user's hopes up until it looks like it's probably accepted.
\r
622 // It is not an error when generated blocks are not accepted. By design,
\r
623 // some percentage of blocks, like 10% or more, will end up not accepted.
\r
624 // This is the normal mechanism by which the network copes with latency.
\r
626 // We display regular transactions right away before any confirmation
\r
627 // because they can always get into some block eventually. Generated coins
\r
628 // are special because if their block is not accepted, they are not valid.
\r
630 if (wtx.GetDepthInMainChain() < 2)
\r
632 wtx.nLinesDisplayed = 0;
\r
636 // View->Show Generated
\r
637 if (!fShowGenerated)
\r
641 // Find the block the tx is in
\r
642 CBlockIndex* pindex = NULL;
\r
643 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
\r
644 if (mi != mapBlockIndex.end())
\r
645 pindex = (*mi).second;
\r
647 // Sort order, unrecorded transactions sort to the top
\r
648 string strSort = strprintf("%010d-%01d-%010u",
\r
649 (pindex ? pindex->nHeight : INT_MAX),
\r
650 (wtx.IsCoinBase() ? 1 : 0),
\r
651 wtx.nTimeReceived);
\r
654 if (nNet > 0 || wtx.IsCoinBase())
\r
659 string strDescription;
\r
661 if (wtx.IsCoinBase())
\r
664 strDescription = "Generated";
\r
667 int64 nUnmatured = 0;
\r
668 foreach(const CTxOut& txout, wtx.vout)
\r
669 nUnmatured += txout.GetCredit();
\r
670 if (wtx.IsInMainChain())
\r
672 strDescription = strprintf("Generated (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
674 // Check if the block was requested by anyone
\r
675 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
676 strDescription = "Generated - Warning: This block was not received by any other nodes and will probably not be accepted!";
\r
680 strDescription = "Generated (not accepted)";
\r
684 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
\r
686 // Online transaction
\r
687 if (!mapValue["from"].empty())
\r
688 strDescription += "From: " + mapValue["from"];
\r
689 if (!mapValue["message"].empty())
\r
691 if (!strDescription.empty())
\r
692 strDescription += " - ";
\r
693 strDescription += mapValue["message"];
\r
698 // Offline transaction
\r
699 foreach(const CTxOut& txout, wtx.vout)
\r
701 if (txout.IsMine())
\r
703 vector<unsigned char> vchPubKey;
\r
704 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
706 string strAddress = PubKeyToAddress(vchPubKey);
\r
707 if (mapAddressBook.count(strAddress))
\r
709 //strDescription += "Received payment to ";
\r
710 //strDescription += "Received with address ";
\r
711 strDescription += "From: unknown, To: ";
\r
712 strDescription += strAddress;
\r
713 /// The labeling feature is just too confusing, so I hid it
\r
714 /// by putting it at the end where it runs off the screen.
\r
715 /// It can still be seen by widening the column, or in the
\r
716 /// details dialog.
\r
717 if (!mapAddressBook[strAddress].empty())
\r
718 strDescription += " (" + mapAddressBook[strAddress] + ")";
\r
726 InsertLine(fNew, nIndex, hash, strSort,
\r
728 nTime ? DateTimeStr(nTime) : "",
\r
729 SingleLine(strDescription),
\r
731 FormatMoney(nNet, true));
\r
735 bool fAllFromMe = true;
\r
736 foreach(const CTxIn& txin, wtx.vin)
\r
737 fAllFromMe = fAllFromMe && txin.IsMine();
\r
739 bool fAllToMe = true;
\r
740 foreach(const CTxOut& txout, wtx.vout)
\r
741 fAllToMe = fAllToMe && txout.IsMine();
\r
743 if (fAllFromMe && fAllToMe)
\r
746 int64 nValue = wtx.vout[0].nValue;
\r
747 InsertLine(fNew, nIndex, hash, strSort,
\r
749 nTime ? DateTimeStr(nTime) : "",
\r
750 "Payment to yourself",
\r
753 /// issue: can't tell which is the payment and which is the change anymore
\r
754 // FormatMoney(nNet - nValue, true),
\r
755 // FormatMoney(nValue, true));
\r
757 else if (fAllFromMe)
\r
762 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
763 wtx.nLinesDisplayed = 0;
\r
764 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
\r
766 const CTxOut& txout = wtx.vout[nOut];
\r
767 if (txout.IsMine())
\r
771 if (!mapValue["to"].empty())
\r
773 // Online transaction
\r
774 strAddress = mapValue["to"];
\r
778 // Offline transaction
\r
780 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
781 strAddress = Hash160ToAddress(hash160);
\r
784 string strDescription = "To: ";
\r
785 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
786 strDescription += mapAddressBook[strAddress] + " ";
\r
787 strDescription += strAddress;
\r
788 if (!mapValue["message"].empty())
\r
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->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->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::OnListItemActivatedAllTransactions(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::OnListItemActivatedAllTransactions() : 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
1283 strHTML.reserve(4000);
\r
1284 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
\r
1286 int64 nTime = wtx.GetTxTime();
\r
1287 int64 nCredit = wtx.GetCredit();
\r
1288 int64 nDebit = wtx.GetDebit();
\r
1289 int64 nNet = nCredit - nDebit;
\r
1293 strHTML += "<b>Status:</b> " + FormatTxStatus(wtx);
\r
1294 int nRequests = wtx.GetRequestCount();
\r
1295 if (nRequests != -1)
\r
1297 if (nRequests == 0)
\r
1298 strHTML += ", has not been successfully broadcast yet";
\r
1299 else if (nRequests == 1)
\r
1300 strHTML += strprintf(", broadcast through %d node", nRequests);
\r
1302 strHTML += strprintf(", broadcast through %d nodes", nRequests);
\r
1304 strHTML += "<br>";
\r
1306 strHTML += "<b>Date:</b> " + (nTime ? DateTimeStr(nTime) : "") + "<br>";
\r
1312 if (wtx.IsCoinBase())
\r
1314 strHTML += "<b>Source:</b> Generated<br>";
\r
1316 else if (!wtx.mapValue["from"].empty())
\r
1318 // Online transaction
\r
1319 if (!wtx.mapValue["from"].empty())
\r
1320 strHTML += "<b>From:</b> " + HtmlEscape(wtx.mapValue["from"]) + "<br>";
\r
1324 // Offline transaction
\r
1328 foreach(const CTxOut& txout, wtx.vout)
\r
1330 if (txout.IsMine())
\r
1332 vector<unsigned char> vchPubKey;
\r
1333 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
1335 string strAddress = PubKeyToAddress(vchPubKey);
\r
1336 if (mapAddressBook.count(strAddress))
\r
1338 strHTML += "<b>From:</b> unknown<br>";
\r
1339 strHTML += "<b>To:</b> ";
\r
1340 strHTML += HtmlEscape(strAddress);
\r
1341 if (!mapAddressBook[strAddress].empty())
\r
1342 strHTML += " (yours, label: " + mapAddressBook[strAddress] + ")";
\r
1344 strHTML += " (yours)";
\r
1345 strHTML += "<br>";
\r
1358 string strAddress;
\r
1359 if (!wtx.mapValue["to"].empty())
\r
1361 // Online transaction
\r
1362 strAddress = wtx.mapValue["to"];
\r
1363 strHTML += "<b>To:</b> ";
\r
1364 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1365 strHTML += mapAddressBook[strAddress] + " ";
\r
1366 strHTML += HtmlEscape(strAddress) + "<br>";
\r
1373 if (wtx.IsCoinBase() && nCredit == 0)
\r
1378 int64 nUnmatured = 0;
\r
1379 foreach(const CTxOut& txout, wtx.vout)
\r
1380 nUnmatured += txout.GetCredit();
\r
1381 if (wtx.IsInMainChain())
\r
1382 strHTML += strprintf("<b>Credit:</b> (%s matures in %d more blocks)<br>", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
1384 strHTML += "<b>Credit:</b> (not accepted)<br>";
\r
1386 else if (nNet > 0)
\r
1391 strHTML += "<b>Credit:</b> " + FormatMoney(nNet) + "<br>";
\r
1395 bool fAllFromMe = true;
\r
1396 foreach(const CTxIn& txin, wtx.vin)
\r
1397 fAllFromMe = fAllFromMe && txin.IsMine();
\r
1399 bool fAllToMe = true;
\r
1400 foreach(const CTxOut& txout, wtx.vout)
\r
1401 fAllToMe = fAllToMe && txout.IsMine();
\r
1408 foreach(const CTxOut& txout, wtx.vout)
\r
1410 if (txout.IsMine())
\r
1413 if (wtx.mapValue["to"].empty())
\r
1415 // Offline transaction
\r
1417 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
1419 string strAddress = Hash160ToAddress(hash160);
\r
1420 strHTML += "<b>To:</b> ";
\r
1421 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1422 strHTML += mapAddressBook[strAddress] + " ";
\r
1423 strHTML += strAddress;
\r
1424 strHTML += "<br>";
\r
1428 strHTML += "<b>Debit:</b> " + FormatMoney(-txout.nValue) + "<br>";
\r
1433 // Payment to self
\r
1434 /// issue: can't tell which is the payment and which is the change anymore
\r
1435 //int64 nValue = wtx.vout[0].nValue;
\r
1436 //strHTML += "<b>Debit:</b> " + FormatMoney(-nValue) + "<br>";
\r
1437 //strHTML += "<b>Credit:</b> " + FormatMoney(nValue) + "<br>";
\r
1440 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
1442 strHTML += "<b>Transaction fee:</b> " + FormatMoney(-nTxFee) + "<br>";
\r
1447 // Mixed debit transaction
\r
1449 foreach(const CTxIn& txin, wtx.vin)
\r
1450 if (txin.IsMine())
\r
1451 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1452 foreach(const CTxOut& txout, wtx.vout)
\r
1453 if (txout.IsMine())
\r
1454 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1458 strHTML += "<b>Net amount:</b> " + FormatMoney(nNet, true) + "<br>";
\r
1464 if (!wtx.mapValue["message"].empty())
\r
1465 strHTML += "<br><b>Message:</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
\r
1467 if (wtx.IsCoinBase())
\r
1468 strHTML += "<br>Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.<br>";
\r
1476 strHTML += "<hr><br>debug print<br><br>";
\r
1477 foreach(const CTxIn& txin, wtx.vin)
\r
1478 if (txin.IsMine())
\r
1479 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1480 foreach(const CTxOut& txout, wtx.vout)
\r
1481 if (txout.IsMine())
\r
1482 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1484 strHTML += "<b>Inputs:</b><br>";
\r
1485 CRITICAL_BLOCK(cs_mapWallet)
\r
1487 foreach(const CTxIn& txin, wtx.vin)
\r
1489 COutPoint prevout = txin.prevout;
\r
1490 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
\r
1491 if (mi != mapWallet.end())
\r
1493 const CWalletTx& prev = (*mi).second;
\r
1494 if (prevout.n < prev.vout.size())
\r
1496 strHTML += HtmlEscape(prev.ToString(), true);
\r
1497 strHTML += " " + FormatTxStatus(prev) + ", ";
\r
1498 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
\r
1504 strHTML += "<br><hr><br><b>Transaction:</b><br>";
\r
1505 strHTML += HtmlEscape(wtx.ToString(), true);
\r
1510 strHTML += "</font></html>";
\r
1511 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
1512 m_htmlWin->SetPage(strHTML);
\r
1513 m_buttonOK->SetFocus();
\r
1516 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
\r
1526 //////////////////////////////////////////////////////////////////////////////
\r
1531 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
\r
1533 // Set up list box of page choices
\r
1534 m_listBox->Append("Main");
\r
1535 //m_listBox->Append("Test 2");
\r
1536 m_listBox->SetSelection(0);
\r
1539 m_checkBoxMinimizeOnClose->SetLabel("&Minimize on close");
\r
1540 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
\r
1544 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
\r
1545 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
\r
1546 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
\r
1547 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
\r
1548 int nProcessors = wxThread::GetCPUCount();
\r
1549 if (nProcessors < 1)
\r
1550 nProcessors = 999;
\r
1551 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
\r
1552 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
\r
1553 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
\r
1554 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
\r
1555 m_checkBoxUseProxy->SetValue(fUseProxy);
\r
1556 m_textCtrlProxyIP->Enable(fUseProxy);
\r
1557 m_textCtrlProxyPort->Enable(fUseProxy);
\r
1558 m_staticTextProxyIP->Enable(fUseProxy);
\r
1559 m_staticTextProxyPort->Enable(fUseProxy);
\r
1560 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
\r
1561 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
\r
1563 m_buttonOK->SetFocus();
\r
1566 void COptionsDialog::SelectPage(int nPage)
\r
1568 m_panelMain->Show(nPage == 0);
\r
1569 m_panelTest2->Show(nPage == 1);
\r
1571 m_scrolledWindow->Layout();
\r
1572 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
\r
1575 void COptionsDialog::OnListBox(wxCommandEvent& event)
\r
1577 SelectPage(event.GetSelection());
\r
1580 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
\r
1582 int64 nTmp = nTransactionFee;
\r
1583 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
\r
1584 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
\r
1587 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
\r
1589 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
\r
1592 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
\r
1594 m_textCtrlProxyIP->Enable(event.IsChecked());
\r
1595 m_textCtrlProxyPort->Enable(event.IsChecked());
\r
1596 m_staticTextProxyIP->Enable(event.IsChecked());
\r
1597 m_staticTextProxyPort->Enable(event.IsChecked());
\r
1600 CAddress COptionsDialog::GetProxyAddr()
\r
1602 // Be careful about byte order, addr.ip and addr.port are big endian
\r
1603 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
\r
1604 if (addr.ip == INADDR_NONE)
\r
1605 addr.ip = addrProxy.ip;
\r
1606 int nPort = atoi(m_textCtrlProxyPort->GetValue());
\r
1607 addr.port = htons(nPort);
\r
1608 if (nPort <= 0 || nPort > USHRT_MAX)
\r
1609 addr.port = addrProxy.port;
\r
1613 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
\r
1615 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
\r
1616 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
\r
1620 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
\r
1622 OnButtonApply(event);
\r
1626 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
\r
1631 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
\r
1633 CWalletDB walletdb;
\r
1635 int64 nPrevTransactionFee = nTransactionFee;
\r
1636 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
\r
1637 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
\r
1639 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
\r
1640 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
\r
1642 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
\r
1643 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
\r
1645 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
\r
1647 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
\r
1648 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
\r
1650 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
\r
1651 GenerateBitcoins(fGenerateBitcoins);
\r
1653 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
\r
1655 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
\r
1656 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
\r
1659 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
\r
1661 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
\r
1662 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
\r
1663 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
1666 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
\r
1668 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
\r
1669 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
\r
1672 fUseProxy = m_checkBoxUseProxy->GetValue();
\r
1673 walletdb.WriteSetting("fUseProxy", fUseProxy);
\r
1675 addrProxy = GetProxyAddr();
\r
1676 walletdb.WriteSetting("addrProxy", addrProxy);
\r
1684 //////////////////////////////////////////////////////////////////////////////
\r
1689 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
\r
1691 m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d beta", VERSION/100, VERSION%100));
\r
1693 // Workaround until upgrade to wxWidgets supporting UTF-8
\r
1694 wxString str = m_staticTextMain->GetLabel();
\r
1695 if (str.Find('Â') != wxNOT_FOUND)
\r
1696 str.Remove(str.Find('Â'), 1);
\r
1697 m_staticTextMain->SetLabel(str);
\r
1699 SetSize(510, 380);
\r
1703 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
\r
1713 //////////////////////////////////////////////////////////////////////////////
\r
1718 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
\r
1721 m_textCtrlAddress->SetValue(strAddress);
\r
1722 m_choiceTransferType->SetSelection(0);
\r
1723 m_bitmapCheckMark->Show(false);
\r
1724 fEnabledPrev = true;
\r
1725 m_textCtrlAddress->SetFocus();
\r
1726 //// todo: should add a display of your balance for convenience
\r
1728 wxFont fontTmp = m_staticTextInstructions->GetFont();
\r
1729 if (fontTmp.GetPointSize() > 9);
\r
1730 fontTmp.SetPointSize(9);
\r
1731 m_staticTextInstructions->SetFont(fontTmp);
\r
1732 SetSize(725, wxDefaultCoord);
\r
1737 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
\r
1738 SetIcon(iconSend);
\r
1740 wxCommandEvent event;
\r
1741 OnTextAddress(event);
\r
1743 // Fixup the tab order
\r
1744 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
\r
1745 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
\r
1749 void CSendDialog::OnTextAddress(wxCommandEvent& event)
\r
1752 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
\r
1753 m_bitmapCheckMark->Show(fBitcoinAddress);
\r
1755 // Grey out message if bitcoin address
\r
1756 bool fEnable = !fBitcoinAddress;
\r
1757 m_staticTextFrom->Enable(fEnable);
\r
1758 m_textCtrlFrom->Enable(fEnable);
\r
1759 m_staticTextMessage->Enable(fEnable);
\r
1760 m_textCtrlMessage->Enable(fEnable);
\r
1761 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
\r
1762 if (!fEnable && fEnabledPrev)
\r
1764 strFromSave = m_textCtrlFrom->GetValue();
\r
1765 strMessageSave = m_textCtrlMessage->GetValue();
\r
1766 m_textCtrlFrom->SetValue("Will appear as \"From: Unknown\"");
\r
1767 m_textCtrlMessage->SetValue("Can't include a message when sending to a Bitcoin address");
\r
1769 else if (fEnable && !fEnabledPrev)
\r
1771 m_textCtrlFrom->SetValue(strFromSave);
\r
1772 m_textCtrlMessage->SetValue(strMessageSave);
\r
1774 fEnabledPrev = fEnable;
\r
1777 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
\r
1779 // Reformat the amount
\r
1780 if (m_textCtrlAmount->GetValue().Trim().empty())
\r
1783 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
\r
1784 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
\r
1787 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
\r
1789 // Open address book
\r
1790 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), true);
\r
1791 if (dialog.ShowModal())
\r
1792 m_textCtrlAddress->SetValue(dialog.GetAddress());
\r
1795 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
\r
1797 // Copy clipboard to address box
\r
1798 if (wxTheClipboard->Open())
\r
1800 if (wxTheClipboard->IsSupported(wxDF_TEXT))
\r
1802 wxTextDataObject data;
\r
1803 wxTheClipboard->GetData(data);
\r
1804 m_textCtrlAddress->SetValue(data.GetText());
\r
1806 wxTheClipboard->Close();
\r
1810 void CSendDialog::OnButtonSend(wxCommandEvent& event)
\r
1813 string strAddress = (string)m_textCtrlAddress->GetValue();
\r
1817 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
\r
1819 wxMessageBox("Error in amount ", "Send Coins");
\r
1822 if (nValue > GetBalance())
\r
1824 wxMessageBox("Amount exceeds your balance ", "Send Coins");
\r
1827 if (nValue + nTransactionFee > GetBalance())
\r
1829 wxMessageBox(string("Total exceeds your balance when the ") + FormatMoney(nTransactionFee) + " transaction fee is included ", "Send Coins");
\r
1833 // Parse bitcoin address
\r
1835 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
\r
1837 if (fBitcoinAddress)
\r
1839 // Send to bitcoin address
\r
1840 CScript scriptPubKey;
\r
1841 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
\r
1843 if (!SendMoney(scriptPubKey, nValue, wtx))
\r
1846 wxMessageBox("Payment sent ", "Sending...");
\r
1850 // Parse IP address
\r
1851 CAddress addr(strAddress);
\r
1852 if (!addr.IsValid())
\r
1854 wxMessageBox("Invalid address ", "Send Coins");
\r
1859 wtx.mapValue["to"] = strAddress;
\r
1860 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
\r
1861 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
\r
1863 // Send to IP address
\r
1864 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
\r
1865 if (!pdialog->ShowModal())
\r
1869 if (!mapAddressBook.count(strAddress))
\r
1870 SetAddressBookName(strAddress, "");
\r
1875 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
\r
1886 //////////////////////////////////////////////////////////////////////////////
\r
1891 CSendingDialog::CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 nPriceIn, const CWalletTx& wtxIn) : CSendingDialogBase(NULL) // we have to give null so parent can't destroy us
\r
1894 nPrice = nPriceIn;
\r
1896 start = wxDateTime::UNow();
\r
1897 memset(pszStatus, 0, sizeof(pszStatus));
\r
1898 fCanCancel = true;
\r
1902 fWorkDone = false;
\r
1904 SetSize(1.2 * GetSize().GetWidth(), 1.05 * GetSize().GetHeight());
\r
1907 SetTitle(strprintf("Sending %s to %s", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
\r
1908 m_textCtrlStatus->SetValue("");
\r
1910 CreateThread(SendingDialogStartTransfer, this);
\r
1913 CSendingDialog::~CSendingDialog()
\r
1915 printf("~CSendingDialog()\n");
\r
1918 void CSendingDialog::Close()
\r
1920 // Last one out turn out the lights.
\r
1921 // fWorkDone signals that work side is done and UI thread should call destroy.
\r
1922 // fUIDone signals that UI window has closed and work thread should call destroy.
\r
1923 // This allows the window to disappear and end modality when cancelled
\r
1924 // without making the user wait for ConnectNode to return. The dialog object
\r
1925 // hangs around in the background until the work thread exits.
\r
1927 EndModal(fSuccess);
\r
1936 void CSendingDialog::OnClose(wxCloseEvent& event)
\r
1938 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
\r
1945 wxCommandEvent cmdevent;
\r
1946 OnButtonCancel(cmdevent);
\r
1950 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
\r
1956 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
\r
1962 void CSendingDialog::OnPaint(wxPaintEvent& event)
\r
1964 if (strlen(pszStatus) > 130)
\r
1965 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
\r
1967 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
\r
1968 m_staticTextSending->SetFocus();
\r
1970 m_buttonCancel->Enable(false);
\r
1973 m_buttonOK->Enable(true);
\r
1974 m_buttonOK->SetFocus();
\r
1975 m_buttonCancel->Enable(false);
\r
1977 if (fAbort && fCanCancel && IsShown())
\r
1979 strcpy(pszStatus, "CANCELLED");
\r
1980 m_buttonOK->Enable(true);
\r
1981 m_buttonOK->SetFocus();
\r
1982 m_buttonCancel->Enable(false);
\r
1983 m_buttonCancel->SetLabel("Cancelled");
\r
1985 wxMessageBox("Transfer cancelled ", "Sending...", wxOK, this);
\r
1992 // Everything from here on is not in the UI thread and must only communicate
\r
1993 // with the rest of the dialog through variables and calling repaint.
\r
1996 void CSendingDialog::Repaint()
\r
1999 wxPaintEvent event;
\r
2000 AddPendingEvent(event);
\r
2003 bool CSendingDialog::Status()
\r
2010 if (fAbort && fCanCancel)
\r
2012 memset(pszStatus, 0, 10);
\r
2013 strcpy(pszStatus, "CANCELLED");
\r
2021 bool CSendingDialog::Status(const string& str)
\r
2026 // This can be read by the UI thread at any time,
\r
2027 // so copy in a way that can be read cleanly at all times.
\r
2028 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
\r
2029 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
\r
2035 bool CSendingDialog::Error(const string& str)
\r
2037 fCanCancel = false;
\r
2039 Status(string("Error: ") + str);
\r
2043 void SendingDialogStartTransfer(void* parg)
\r
2045 ((CSendingDialog*)parg)->StartTransfer();
\r
2048 void CSendingDialog::StartTransfer()
\r
2050 // Make sure we have enough money
\r
2051 if (nPrice + nTransactionFee > GetBalance())
\r
2053 Error("You don't have enough money");
\r
2057 // We may have connected already for product details
\r
2058 if (!Status("Connecting..."))
\r
2060 CNode* pnode = ConnectNode(addr, 15 * 60);
\r
2063 Error("Unable to connect");
\r
2067 // Send order to seller, with response going to OnReply2 via event handler
\r
2068 if (!Status("Requesting public key..."))
\r
2070 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
\r
2073 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
\r
2075 ((CSendingDialog*)parg)->OnReply2(vRecv);
\r
2078 void CSendingDialog::OnReply2(CDataStream& vRecv)
\r
2080 if (!Status("Received public key..."))
\r
2083 CScript scriptPubKey;
\r
2090 string strMessage;
\r
2091 vRecv >> strMessage;
\r
2092 Error("Transfer was not accepted");
\r
2093 //// todo: enlarge the window and enable a hidden white box to put seller's message
\r
2096 vRecv >> scriptPubKey;
\r
2100 //// what do we want to do about this?
\r
2101 Error("Invalid response received");
\r
2105 // Pause to give the user a chance to cancel
\r
2106 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
\r
2113 CRITICAL_BLOCK(cs_main)
\r
2116 if (!Status("Creating transaction..."))
\r
2118 if (nPrice + nTransactionFee > GetBalance())
\r
2120 Error("You don't have enough money");
\r
2124 int64 nFeeRequired;
\r
2125 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
\r
2127 if (nPrice + nFeeRequired > GetBalance())
\r
2128 Error(strprintf("This is an oversized transaction that requires a transaction fee of %s", FormatMoney(nFeeRequired).c_str()));
\r
2130 Error("Transaction creation failed");
\r
2134 // Make sure we're still connected
\r
2135 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
\r
2138 Error("Lost connection, transaction cancelled");
\r
2142 // Last chance to cancel
\r
2146 fCanCancel = false;
\r
2149 fCanCancel = true;
\r
2152 fCanCancel = false;
\r
2154 if (!Status("Sending payment..."))
\r
2158 if (!CommitTransactionSpent(wtx, key))
\r
2160 Error("Error finalizing payment");
\r
2164 // Send payment tx to seller, with response going to OnReply3 via event handler
\r
2165 pnode->PushRequest("submitorder", wtx, SendingDialogOnReply3, this);
\r
2167 // Accept and broadcast transaction
\r
2168 if (!wtx.AcceptTransaction())
\r
2169 printf("ERROR: CSendingDialog : wtxNew.AcceptTransaction() %s failed\n", wtx.GetHash().ToString().c_str());
\r
2170 wtx.RelayWalletTransaction();
\r
2172 Status("Waiting for confirmation...");
\r
2173 MainFrameRepaint();
\r
2177 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
\r
2179 ((CSendingDialog*)parg)->OnReply3(vRecv);
\r
2182 void CSendingDialog::OnReply3(CDataStream& vRecv)
\r
2190 Error("The payment was sent, but the recipient was unable to verify it.\n"
\r
2191 "The transaction is recorded and will credit to the recipient,\n"
\r
2192 "but the comment information will be blank.");
\r
2198 //// what do we want to do about this?
\r
2199 Error("Payment was sent, but an invalid response was received");
\r
2205 Status("Payment completed");
\r
2213 //////////////////////////////////////////////////////////////////////////////
\r
2215 // CYourAddressDialog
\r
2218 CYourAddressDialog::CYourAddressDialog(wxWindow* parent, const string& strInitSelected) : CYourAddressDialogBase(parent)
\r
2220 // Init column headers
\r
2221 m_listCtrl->InsertColumn(0, "Label", wxLIST_FORMAT_LEFT, 200);
\r
2222 m_listCtrl->InsertColumn(1, "Bitcoin Address", wxLIST_FORMAT_LEFT, 350);
\r
2223 m_listCtrl->SetFocus();
\r
2225 // Fill listctrl with address book data
\r
2226 CRITICAL_BLOCK(cs_mapKeys)
\r
2228 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2230 string strAddress = item.first;
\r
2231 string strName = item.second;
\r
2233 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2236 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2237 if (strAddress == strInitSelected)
\r
2238 m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2244 wxString CYourAddressDialog::GetAddress()
\r
2246 int nIndex = GetSelection(m_listCtrl);
\r
2249 return GetItemText(m_listCtrl, nIndex, 1);
\r
2252 void CYourAddressDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2254 // Update address book with edited name
\r
2255 if (event.IsEditCancelled())
\r
2257 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2258 SetAddressBookName(strAddress, string(event.GetText()));
\r
2259 pframeMain->RefreshListCtrl();
\r
2262 void CYourAddressDialog::OnListItemSelected(wxListEvent& event)
\r
2266 void CYourAddressDialog::OnListItemActivated(wxListEvent& event)
\r
2268 // Doubleclick edits item
\r
2269 wxCommandEvent event2;
\r
2270 OnButtonRename(event2);
\r
2273 void CYourAddressDialog::OnButtonRename(wxCommandEvent& event)
\r
2276 int nIndex = GetSelection(m_listCtrl);
\r
2279 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2280 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2281 CGetTextFromUserDialog dialog(this, "Edit Address Label", "New Label", strName);
\r
2282 if (!dialog.ShowModal())
\r
2284 strName = dialog.GetValue();
\r
2287 SetAddressBookName(strAddress, strName);
\r
2288 m_listCtrl->SetItemText(nIndex, strName);
\r
2289 pframeMain->RefreshListCtrl();
\r
2292 void CYourAddressDialog::OnButtonNew(wxCommandEvent& event)
\r
2295 CGetTextFromUserDialog dialog(this, "New Bitcoin Address", "Label", "");
\r
2296 if (!dialog.ShowModal())
\r
2298 string strName = dialog.GetValue();
\r
2300 // Generate new key
\r
2301 string strAddress = PubKeyToAddress(GenerateNewKey());
\r
2302 SetAddressBookName(strAddress, strName);
\r
2304 // Add to list and select it
\r
2305 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2306 SetSelection(m_listCtrl, nIndex);
\r
2307 m_listCtrl->SetFocus();
\r
2310 void CYourAddressDialog::OnButtonCopy(wxCommandEvent& event)
\r
2312 // Copy address box to clipboard
\r
2313 if (wxTheClipboard->Open())
\r
2315 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
\r
2316 wxTheClipboard->Close();
\r
2320 void CYourAddressDialog::OnButtonOK(wxCommandEvent& event)
\r
2326 void CYourAddressDialog::OnButtonCancel(wxCommandEvent& event)
\r
2332 void CYourAddressDialog::OnClose(wxCloseEvent& event)
\r
2343 //////////////////////////////////////////////////////////////////////////////
\r
2345 // CAddressBookDialog
\r
2348 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, bool fSendingIn) : CAddressBookDialogBase(parent)
\r
2350 fSending = fSendingIn;
\r
2352 m_buttonCancel->Show(false);
\r
2354 // Init column headers
\r
2355 m_listCtrl->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 200);
\r
2356 m_listCtrl->InsertColumn(1, "Address", wxLIST_FORMAT_LEFT, 350);
\r
2357 m_listCtrl->SetFocus();
\r
2360 wxIcon iconAddressBook;
\r
2361 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
\r
2362 SetIcon(iconAddressBook);
\r
2364 // Fill listctrl with address book data
\r
2365 CRITICAL_BLOCK(cs_mapKeys)
\r
2367 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2369 string strAddress = item.first;
\r
2370 string strName = item.second;
\r
2372 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2375 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2376 if (strAddress == strInitSelected)
\r
2377 m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2383 wxString CAddressBookDialog::GetAddress()
\r
2385 int nIndex = GetSelection(m_listCtrl);
\r
2388 return GetItemText(m_listCtrl, nIndex, 1);
\r
2391 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2393 // Update address book with edited name
\r
2394 if (event.IsEditCancelled())
\r
2396 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2397 SetAddressBookName(strAddress, string(event.GetText()));
\r
2398 pframeMain->RefreshListCtrl();
\r
2401 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
\r
2405 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
\r
2409 // Doubleclick returns selection
\r
2410 EndModal(GetAddress() != "" ? 2 : 0);
\r
2414 // Doubleclick edits item
\r
2415 wxCommandEvent event2;
\r
2416 OnButtonEdit(event2);
\r
2420 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
\r
2423 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2425 wxMessageBox("This is one of your own addresses for receiving payments and cannot be entered in the address book. ", strTitle);
\r
2429 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
\r
2432 int nIndex = GetSelection(m_listCtrl);
\r
2435 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2436 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2437 string strAddressOrg = strAddress;
\r
2440 CGetTextFromUserDialog dialog(this, "Edit Address", "Name", strName, "Address", strAddress);
\r
2441 if (!dialog.ShowModal())
\r
2443 strName = dialog.GetValue1();
\r
2444 strAddress = dialog.GetValue2();
\r
2446 while (CheckIfMine(strAddress, "Edit Address"));
\r
2449 if (strAddress != strAddressOrg)
\r
2450 CWalletDB().EraseName(strAddressOrg);
\r
2451 SetAddressBookName(strAddress, strName);
\r
2452 m_listCtrl->SetItem(nIndex, 1, strAddress);
\r
2453 m_listCtrl->SetItemText(nIndex, strName);
\r
2454 pframeMain->RefreshListCtrl();
\r
2457 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
\r
2461 string strAddress;
\r
2464 CGetTextFromUserDialog dialog(this, "New Address", "Name", strName, "Address", strAddress);
\r
2465 if (!dialog.ShowModal())
\r
2467 strName = dialog.GetValue1();
\r
2468 strAddress = dialog.GetValue2();
\r
2470 while (CheckIfMine(strAddress, "New Address"));
\r
2472 // Add to list and select it
\r
2473 SetAddressBookName(strAddress, strName);
\r
2474 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2475 SetSelection(m_listCtrl, nIndex);
\r
2476 m_listCtrl->SetFocus();
\r
2477 pframeMain->RefreshListCtrl();
\r
2480 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
\r
2482 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
\r
2484 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
\r
2486 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2487 CWalletDB().EraseName(strAddress);
\r
2488 m_listCtrl->DeleteItem(nIndex);
\r
2491 pframeMain->RefreshListCtrl();
\r
2494 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
\r
2496 // Copy address box to clipboard
\r
2497 if (wxTheClipboard->Open())
\r
2499 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
\r
2500 wxTheClipboard->Close();
\r
2504 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
\r
2507 EndModal(GetAddress() != "" ? 1 : 0);
\r
2510 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
\r
2516 void CAddressBookDialog::OnClose(wxCloseEvent& event)
\r
2527 //////////////////////////////////////////////////////////////////////////////
\r
2529 // CProductsDialog
\r
2532 bool CompareIntStringPairBestFirst(const pair<int, string>& item1, const pair<int, string>& item2)
\r
2534 return (item1.first > item2.first);
\r
2537 CProductsDialog::CProductsDialog(wxWindow* parent) : CProductsDialogBase(parent)
\r
2539 // Init column headers
\r
2540 m_listCtrl->InsertColumn(0, "Title", wxLIST_FORMAT_LEFT, 200);
\r
2541 m_listCtrl->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 80);
\r
2542 m_listCtrl->InsertColumn(2, "Seller", wxLIST_FORMAT_LEFT, 80);
\r
2543 m_listCtrl->InsertColumn(3, "Stars", wxLIST_FORMAT_LEFT, 50);
\r
2544 m_listCtrl->InsertColumn(4, "Power", wxLIST_FORMAT_LEFT, 50);
\r
2546 // Tally top categories
\r
2547 map<string, int> mapTopCategories;
\r
2548 CRITICAL_BLOCK(cs_mapProducts)
\r
2549 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)
\r
2550 mapTopCategories[(*mi).second.mapValue["category"]]++;
\r
2552 // Sort top categories
\r
2553 vector<pair<int, string> > vTopCategories;
\r
2554 for (map<string, int>::iterator mi = mapTopCategories.begin(); mi != mapTopCategories.end(); ++mi)
\r
2555 vTopCategories.push_back(make_pair((*mi).second, (*mi).first));
\r
2556 sort(vTopCategories.begin(), vTopCategories.end(), CompareIntStringPairBestFirst);
\r
2558 // Fill categories combo box
\r
2560 for (vector<pair<int, string> >::iterator it = vTopCategories.begin(); it != vTopCategories.end() && nLimit-- > 0; ++it)
\r
2561 m_comboBoxCategory->Append((*it).second);
\r
2563 // Fill window with initial search
\r
2564 //wxCommandEvent event;
\r
2565 //OnButtonSearch(event);
\r
2568 void CProductsDialog::OnCombobox(wxCommandEvent& event)
\r
2570 OnButtonSearch(event);
\r
2573 bool CompareProductsBestFirst(const CProduct* p1, const CProduct* p2)
\r
2575 return (p1->nAtoms > p2->nAtoms);
\r
2578 void CProductsDialog::OnButtonSearch(wxCommandEvent& event)
\r
2580 string strCategory = (string)m_comboBoxCategory->GetValue();
\r
2581 string strSearch = (string)m_textCtrlSearch->GetValue();
\r
2583 // Search products
\r
2584 vector<CProduct*> vProductsFound;
\r
2585 CRITICAL_BLOCK(cs_mapProducts)
\r
2587 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)
\r
2589 CProduct& product = (*mi).second;
\r
2590 if (product.mapValue["category"].find(strCategory) != -1)
\r
2592 if (product.mapValue["title"].find(strSearch) != -1 ||
\r
2593 product.mapValue["description"].find(strSearch) != -1 ||
\r
2594 product.mapValue["seller"].find(strSearch) != -1)
\r
2596 vProductsFound.push_back(&product);
\r
2603 sort(vProductsFound.begin(), vProductsFound.end(), CompareProductsBestFirst);
\r
2606 foreach(CProduct* pproduct, vProductsFound)
\r
2608 InsertLine(m_listCtrl,
\r
2609 pproduct->mapValue["title"],
\r
2610 pproduct->mapValue["price"],
\r
2611 pproduct->mapValue["seller"],
\r
2612 pproduct->mapValue["stars"],
\r
2613 itostr(pproduct->nAtoms));
\r
2617 void CProductsDialog::OnListItemActivated(wxListEvent& event)
\r
2619 // Doubleclick opens product
\r
2620 CViewProductDialog* pdialog = new CViewProductDialog(this, m_vProduct[event.GetIndex()]);
\r
2630 //////////////////////////////////////////////////////////////////////////////
\r
2632 // CEditProductDialog
\r
2635 CEditProductDialog::CEditProductDialog(wxWindow* parent) : CEditProductDialogBase(parent)
\r
2637 m_textCtrlLabel[0 ] = m_textCtrlLabel0;
\r
2638 m_textCtrlLabel[1 ] = m_textCtrlLabel1;
\r
2639 m_textCtrlLabel[2 ] = m_textCtrlLabel2;
\r
2640 m_textCtrlLabel[3 ] = m_textCtrlLabel3;
\r
2641 m_textCtrlLabel[4 ] = m_textCtrlLabel4;
\r
2642 m_textCtrlLabel[5 ] = m_textCtrlLabel5;
\r
2643 m_textCtrlLabel[6 ] = m_textCtrlLabel6;
\r
2644 m_textCtrlLabel[7 ] = m_textCtrlLabel7;
\r
2645 m_textCtrlLabel[8 ] = m_textCtrlLabel8;
\r
2646 m_textCtrlLabel[9 ] = m_textCtrlLabel9;
\r
2647 m_textCtrlLabel[10] = m_textCtrlLabel10;
\r
2648 m_textCtrlLabel[11] = m_textCtrlLabel11;
\r
2649 m_textCtrlLabel[12] = m_textCtrlLabel12;
\r
2650 m_textCtrlLabel[13] = m_textCtrlLabel13;
\r
2651 m_textCtrlLabel[14] = m_textCtrlLabel14;
\r
2652 m_textCtrlLabel[15] = m_textCtrlLabel15;
\r
2653 m_textCtrlLabel[16] = m_textCtrlLabel16;
\r
2654 m_textCtrlLabel[17] = m_textCtrlLabel17;
\r
2655 m_textCtrlLabel[18] = m_textCtrlLabel18;
\r
2656 m_textCtrlLabel[19] = m_textCtrlLabel19;
\r
2658 m_textCtrlField[0 ] = m_textCtrlField0;
\r
2659 m_textCtrlField[1 ] = m_textCtrlField1;
\r
2660 m_textCtrlField[2 ] = m_textCtrlField2;
\r
2661 m_textCtrlField[3 ] = m_textCtrlField3;
\r
2662 m_textCtrlField[4 ] = m_textCtrlField4;
\r
2663 m_textCtrlField[5 ] = m_textCtrlField5;
\r
2664 m_textCtrlField[6 ] = m_textCtrlField6;
\r
2665 m_textCtrlField[7 ] = m_textCtrlField7;
\r
2666 m_textCtrlField[8 ] = m_textCtrlField8;
\r
2667 m_textCtrlField[9 ] = m_textCtrlField9;
\r
2668 m_textCtrlField[10] = m_textCtrlField10;
\r
2669 m_textCtrlField[11] = m_textCtrlField11;
\r
2670 m_textCtrlField[12] = m_textCtrlField12;
\r
2671 m_textCtrlField[13] = m_textCtrlField13;
\r
2672 m_textCtrlField[14] = m_textCtrlField14;
\r
2673 m_textCtrlField[15] = m_textCtrlField15;
\r
2674 m_textCtrlField[16] = m_textCtrlField16;
\r
2675 m_textCtrlField[17] = m_textCtrlField17;
\r
2676 m_textCtrlField[18] = m_textCtrlField18;
\r
2677 m_textCtrlField[19] = m_textCtrlField19;
\r
2679 m_buttonDel[0 ] = m_buttonDel0;
\r
2680 m_buttonDel[1 ] = m_buttonDel1;
\r
2681 m_buttonDel[2 ] = m_buttonDel2;
\r
2682 m_buttonDel[3 ] = m_buttonDel3;
\r
2683 m_buttonDel[4 ] = m_buttonDel4;
\r
2684 m_buttonDel[5 ] = m_buttonDel5;
\r
2685 m_buttonDel[6 ] = m_buttonDel6;
\r
2686 m_buttonDel[7 ] = m_buttonDel7;
\r
2687 m_buttonDel[8 ] = m_buttonDel8;
\r
2688 m_buttonDel[9 ] = m_buttonDel9;
\r
2689 m_buttonDel[10] = m_buttonDel10;
\r
2690 m_buttonDel[11] = m_buttonDel11;
\r
2691 m_buttonDel[12] = m_buttonDel12;
\r
2692 m_buttonDel[13] = m_buttonDel13;
\r
2693 m_buttonDel[14] = m_buttonDel14;
\r
2694 m_buttonDel[15] = m_buttonDel15;
\r
2695 m_buttonDel[16] = m_buttonDel16;
\r
2696 m_buttonDel[17] = m_buttonDel17;
\r
2697 m_buttonDel[18] = m_buttonDel18;
\r
2698 m_buttonDel[19] = m_buttonDel19;
\r
2700 for (int i = 1; i < FIELDS_MAX; i++)
\r
2701 ShowLine(i, false);
\r
2706 void CEditProductDialog::LayoutAll()
\r
2708 m_scrolledWindow->Layout();
\r
2709 m_scrolledWindow->GetSizer()->Fit(m_scrolledWindow);
\r
2713 void CEditProductDialog::ShowLine(int i, bool fShow)
\r
2715 m_textCtrlLabel[i]->Show(fShow);
\r
2716 m_textCtrlField[i]->Show(fShow);
\r
2717 m_buttonDel[i]->Show(fShow);
\r
2720 void CEditProductDialog::OnButtonDel0(wxCommandEvent& event) { OnButtonDel(event, 0); }
\r
2721 void CEditProductDialog::OnButtonDel1(wxCommandEvent& event) { OnButtonDel(event, 1); }
\r
2722 void CEditProductDialog::OnButtonDel2(wxCommandEvent& event) { OnButtonDel(event, 2); }
\r
2723 void CEditProductDialog::OnButtonDel3(wxCommandEvent& event) { OnButtonDel(event, 3); }
\r
2724 void CEditProductDialog::OnButtonDel4(wxCommandEvent& event) { OnButtonDel(event, 4); }
\r
2725 void CEditProductDialog::OnButtonDel5(wxCommandEvent& event) { OnButtonDel(event, 5); }
\r
2726 void CEditProductDialog::OnButtonDel6(wxCommandEvent& event) { OnButtonDel(event, 6); }
\r
2727 void CEditProductDialog::OnButtonDel7(wxCommandEvent& event) { OnButtonDel(event, 7); }
\r
2728 void CEditProductDialog::OnButtonDel8(wxCommandEvent& event) { OnButtonDel(event, 8); }
\r
2729 void CEditProductDialog::OnButtonDel9(wxCommandEvent& event) { OnButtonDel(event, 9); }
\r
2730 void CEditProductDialog::OnButtonDel10(wxCommandEvent& event) { OnButtonDel(event, 10); }
\r
2731 void CEditProductDialog::OnButtonDel11(wxCommandEvent& event) { OnButtonDel(event, 11); }
\r
2732 void CEditProductDialog::OnButtonDel12(wxCommandEvent& event) { OnButtonDel(event, 12); }
\r
2733 void CEditProductDialog::OnButtonDel13(wxCommandEvent& event) { OnButtonDel(event, 13); }
\r
2734 void CEditProductDialog::OnButtonDel14(wxCommandEvent& event) { OnButtonDel(event, 14); }
\r
2735 void CEditProductDialog::OnButtonDel15(wxCommandEvent& event) { OnButtonDel(event, 15); }
\r
2736 void CEditProductDialog::OnButtonDel16(wxCommandEvent& event) { OnButtonDel(event, 16); }
\r
2737 void CEditProductDialog::OnButtonDel17(wxCommandEvent& event) { OnButtonDel(event, 17); }
\r
2738 void CEditProductDialog::OnButtonDel18(wxCommandEvent& event) { OnButtonDel(event, 18); }
\r
2739 void CEditProductDialog::OnButtonDel19(wxCommandEvent& event) { OnButtonDel(event, 19); }
\r
2741 void CEditProductDialog::OnButtonDel(wxCommandEvent& event, int n)
\r
2745 m_scrolledWindow->GetViewStart(&x, &y);
\r
2747 for (i = n; i < FIELDS_MAX-1; i++)
\r
2749 m_textCtrlLabel[i]->SetValue(m_textCtrlLabel[i+1]->GetValue());
\r
2750 m_textCtrlField[i]->SetValue(m_textCtrlField[i+1]->GetValue());
\r
2751 if (!m_buttonDel[i+1]->IsShown())
\r
2754 m_textCtrlLabel[i]->SetValue("");
\r
2755 m_textCtrlField[i]->SetValue("");
\r
2756 ShowLine(i, false);
\r
2757 m_buttonAddField->Enable(true);
\r
2759 m_scrolledWindow->Scroll(0, y);
\r
2763 void CEditProductDialog::OnButtonAddField(wxCommandEvent& event)
\r
2765 for (int i = 0; i < FIELDS_MAX; i++)
\r
2767 if (!m_buttonDel[i]->IsShown())
\r
2770 ShowLine(i, true);
\r
2771 if (i == FIELDS_MAX-1)
\r
2772 m_buttonAddField->Enable(false);
\r
2774 m_scrolledWindow->Scroll(0, 99999);
\r
2781 void CEditProductDialog::OnButtonSend(wxCommandEvent& event)
\r
2784 GetProduct(product);
\r
2786 // Sign the detailed product
\r
2787 product.vchPubKeyFrom = keyUser.GetPubKey();
\r
2788 if (!keyUser.Sign(product.GetSigHash(), product.vchSig))
\r
2790 wxMessageBox("Error digitally signing the product ");
\r
2794 // Save detailed product
\r
2795 AddToMyProducts(product);
\r
2797 // Strip down to summary product
\r
2798 product.mapDetails.clear();
\r
2799 product.vOrderForm.clear();
\r
2801 // Sign the summary product
\r
2802 if (!keyUser.Sign(product.GetSigHash(), product.vchSig))
\r
2804 wxMessageBox("Error digitally signing the product ");
\r
2809 if (!product.CheckProduct())
\r
2811 wxMessageBox("Errors found in product ");
\r
2816 AdvertStartPublish(pnodeLocalHost, MSG_PRODUCT, 0, product);
\r
2821 void CEditProductDialog::OnButtonPreview(wxCommandEvent& event)
\r
2824 GetProduct(product);
\r
2825 CViewProductDialog* pdialog = new CViewProductDialog(this, product);
\r
2829 void CEditProductDialog::OnButtonCancel(wxCommandEvent& event)
\r
2834 void CEditProductDialog::SetProduct(const CProduct& productIn)
\r
2836 CProduct product = productIn;
\r
2838 m_comboBoxCategory->SetValue(product.mapValue["category"]);
\r
2839 m_textCtrlTitle->SetValue(product.mapValue["title"]);
\r
2840 m_textCtrlPrice->SetValue(product.mapValue["price"]);
\r
2841 m_textCtrlDescription->SetValue(product.mapValue["description"]);
\r
2842 m_textCtrlInstructions->SetValue(product.mapValue["instructions"]);
\r
2844 for (int i = 0; i < FIELDS_MAX; i++)
\r
2846 bool fUsed = i < product.vOrderForm.size();
\r
2847 m_buttonDel[i]->Show(fUsed);
\r
2848 m_textCtrlLabel[i]->Show(fUsed);
\r
2849 m_textCtrlField[i]->Show(fUsed);
\r
2853 m_textCtrlLabel[i]->SetValue(product.vOrderForm[i].first);
\r
2854 string strControl = product.vOrderForm[i].second;
\r
2855 if (strControl.substr(0, 5) == "text=")
\r
2856 m_textCtrlField[i]->SetValue("");
\r
2857 else if (strControl.substr(0, 7) == "choice=")
\r
2858 m_textCtrlField[i]->SetValue(strControl.substr(7));
\r
2860 m_textCtrlField[i]->SetValue(strControl);
\r
2864 void CEditProductDialog::GetProduct(CProduct& product)
\r
2866 // map<string, string> mapValue;
\r
2867 // vector<pair<string, string> > vOrderForm;
\r
2869 product.mapValue["category"] = m_comboBoxCategory->GetValue().Trim();
\r
2870 product.mapValue["title"] = m_textCtrlTitle->GetValue().Trim();
\r
2871 product.mapValue["price"] = m_textCtrlPrice->GetValue().Trim();
\r
2872 product.mapValue["description"] = m_textCtrlDescription->GetValue().Trim();
\r
2873 product.mapValue["instructions"] = m_textCtrlInstructions->GetValue().Trim();
\r
2875 for (int i = 0; i < FIELDS_MAX; i++)
\r
2877 if (m_buttonDel[i]->IsShown())
\r
2879 string strLabel = (string)m_textCtrlLabel[i]->GetValue().Trim();
\r
2880 string strControl = (string)m_textCtrlField[i]->GetValue();
\r
2881 if (strControl.empty())
\r
2882 strControl = "text=";
\r
2884 strControl = "choice=" + strControl;
\r
2885 product.vOrderForm.push_back(make_pair(strLabel, strControl));
\r
2896 //////////////////////////////////////////////////////////////////////////////
\r
2898 // CViewProductDialog
\r
2901 CViewProductDialog::CViewProductDialog(wxWindow* parent, const CProduct& productIn) : CViewProductDialogBase(parent)
\r
2903 Connect(wxEVT_REPLY1, wxCommandEventHandler(CViewProductDialog::OnReply1), NULL, this);
\r
2904 AddCallbackAvailable(GetEventHandler());
\r
2906 // Fill display with product summary while waiting for details
\r
2907 product = productIn;
\r
2908 UpdateProductDisplay(false);
\r
2910 m_buttonBack->Enable(false);
\r
2911 m_buttonNext->Enable(!product.vOrderForm.empty());
\r
2912 m_htmlWinReviews->Show(true);
\r
2913 m_scrolledWindow->Show(false);
\r
2916 // Request details from seller
\r
2917 CreateThread(ThreadRequestProductDetails, new pair<CProduct, wxEvtHandler*>(product, GetEventHandler()));
\r
2920 CViewProductDialog::~CViewProductDialog()
\r
2922 RemoveCallbackAvailable(GetEventHandler());
\r
2925 void ThreadRequestProductDetails(void* parg)
\r
2927 // Extract parameters
\r
2928 pair<CProduct, wxEvtHandler*>* pitem = (pair<CProduct, wxEvtHandler*>*)parg;
\r
2929 CProduct product = pitem->first;
\r
2930 wxEvtHandler* pevthandler = pitem->second;
\r
2933 // Connect to seller
\r
2934 CNode* pnode = ConnectNode(product.addr, 5 * 60);
\r
2937 CDataStream ssEmpty;
\r
2938 AddPendingReplyEvent1(pevthandler, ssEmpty);
\r
2942 // Request detailed product, with response going to OnReply1 via dialog's event handler
\r
2943 pnode->PushRequest("getdetails", product.GetHash(), AddPendingReplyEvent1, (void*)pevthandler);
\r
2946 void CViewProductDialog::OnReply1(wxCommandEvent& event)
\r
2948 CDataStream ss = GetStreamFromEvent(event);
\r
2951 product.mapValue["description"] = "-- CAN'T CONNECT TO SELLER --\n";
\r
2952 UpdateProductDisplay(true);
\r
2957 CProduct product2;
\r
2964 if (product2.GetHash() != product.GetHash())
\r
2966 if (!product2.CheckSignature())
\r
2971 product.mapValue["description"] = "-- INVALID RESPONSE --\n";
\r
2972 UpdateProductDisplay(true);
\r
2976 product = product2;
\r
2977 UpdateProductDisplay(true);
\r
2980 bool CompareReviewsBestFirst(const CReview* p1, const CReview* p2)
\r
2982 return (p1->nAtoms > p2->nAtoms);
\r
2985 void CViewProductDialog::UpdateProductDisplay(bool fDetails)
\r
2987 // Product and reviews
\r
2989 strHTML.reserve(4000);
\r
2990 strHTML += "<html>\n"
\r
2992 "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"
\r
2995 strHTML += "<b>Category:</b> " + HtmlEscape(product.mapValue["category"]) + "<br>\n";
\r
2996 strHTML += "<b>Title:</b> " + HtmlEscape(product.mapValue["title"]) + "<br>\n";
\r
2997 strHTML += "<b>Price:</b> " + HtmlEscape(product.mapValue["price"]) + "<br>\n";
\r
3000 strHTML += "<b>Loading details...</b><br>\n<br>\n";
\r
3002 strHTML += HtmlEscape(product.mapValue["description"], true) + "<br>\n<br>\n";
\r
3004 strHTML += "<b>Reviews:</b><br>\n<br>\n";
\r
3006 if (!product.vchPubKeyFrom.empty())
\r
3008 CReviewDB reviewdb("r");
\r
3011 vector<CReview> vReviews;
\r
3012 reviewdb.ReadReviews(product.GetUserHash(), vReviews);
\r
3014 // Get reviewer's number of atoms
\r
3015 vector<CReview*> vSortedReviews;
\r
3016 vSortedReviews.reserve(vReviews.size());
\r
3017 for (vector<CReview>::reverse_iterator it = vReviews.rbegin(); it != vReviews.rend(); ++it)
\r
3019 CReview& review = *it;
\r
3021 reviewdb.ReadUser(review.GetUserHash(), user);
\r
3022 review.nAtoms = user.GetAtomCount();
\r
3023 vSortedReviews.push_back(&review);
\r
3029 stable_sort(vSortedReviews.begin(), vSortedReviews.end(), CompareReviewsBestFirst);
\r
3032 foreach(CReview* preview, vSortedReviews)
\r
3034 CReview& review = *preview;
\r
3035 int nStars = atoi(review.mapValue["stars"].c_str());
\r
3036 if (nStars < 1 || nStars > 5)
\r
3039 strHTML += "<b>" + itostr(nStars) + (nStars == 1 ? " star" : " stars") + "</b>";
\r
3040 strHTML += " ";
\r
3041 strHTML += DateStr(atoi64(review.mapValue["date"])) + "<br>\n";
\r
3042 strHTML += HtmlEscape(review.mapValue["review"], true);
\r
3043 strHTML += "<br>\n<br>\n";
\r
3047 strHTML += "</body>\n</html>\n";
\r
3049 // Shrink capacity to fit
\r
3050 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
3052 m_htmlWinReviews->SetPage(strHTML);
\r
3054 ///// need to find some other indicator to use so can allow empty order form
\r
3055 if (product.vOrderForm.empty())
\r
3059 m_staticTextInstructions->SetLabel(product.mapValue["instructions"]);
\r
3060 for (int i = 0; i < FIELDS_MAX; i++)
\r
3062 m_staticTextLabel[i] = NULL;
\r
3063 m_textCtrlField[i] = NULL;
\r
3064 m_choiceField[i] = NULL;
\r
3067 // Construct flexgridsizer
\r
3068 wxBoxSizer* bSizer21 = (wxBoxSizer*)m_scrolledWindow->GetSizer();
\r
3069 wxFlexGridSizer* fgSizer;
\r
3070 fgSizer = new wxFlexGridSizer(0, 2, 0, 0);
\r
3071 fgSizer->AddGrowableCol(1);
\r
3072 fgSizer->SetFlexibleDirection(wxBOTH);
\r
3073 fgSizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
\r
3075 // Construct order form fields
\r
3076 wxWindow* windowLast = NULL;
\r
3077 for (int i = 0; i < product.vOrderForm.size(); i++)
\r
3079 string strLabel = product.vOrderForm[i].first;
\r
3080 string strControl = product.vOrderForm[i].second;
\r
3082 if (strLabel.size() < 20)
\r
3083 strLabel.insert(strLabel.begin(), 20 - strLabel.size(), ' ');
\r
3085 m_staticTextLabel[i] = new wxStaticText(m_scrolledWindow, wxID_ANY, strLabel, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
\r
3086 m_staticTextLabel[i]->Wrap(-1);
\r
3087 fgSizer->Add(m_staticTextLabel[i], 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5);
\r
3089 if (strControl.substr(0, 5) == "text=")
\r
3091 m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
\r
3092 fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);
\r
3093 windowLast = m_textCtrlField[i];
\r
3095 else if (strControl.substr(0, 7) == "choice=")
\r
3097 vector<string> vChoices;
\r
3098 ParseString(strControl.substr(7), ',', vChoices);
\r
3100 wxArrayString arraystring;
\r
3101 foreach(const string& str, vChoices)
\r
3102 arraystring.Add(str);
\r
3104 m_choiceField[i] = new wxChoice(m_scrolledWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize, arraystring, 0);
\r
3105 fgSizer->Add(m_choiceField[i], 0, wxALL|wxALIGN_CENTER_VERTICAL, 5);
\r
3106 windowLast = m_choiceField[i];
\r
3110 m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
\r
3111 fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);
\r
3112 m_staticTextLabel[i]->Show(false);
\r
3113 m_textCtrlField[i]->Show(false);
\r
3117 // Insert after instructions and before submit/cancel buttons
\r
3118 bSizer21->Insert(2, fgSizer, 0, wxEXPAND|wxRIGHT|wxLEFT, 5);
\r
3119 m_scrolledWindow->Layout();
\r
3120 bSizer21->Fit(m_scrolledWindow);
\r
3123 // Fixup the tab order
\r
3124 m_buttonSubmitForm->MoveAfterInTabOrder(windowLast);
\r
3125 m_buttonCancelForm->MoveAfterInTabOrder(m_buttonSubmitForm);
\r
3126 //m_buttonBack->MoveAfterInTabOrder(m_buttonCancelForm);
\r
3127 //m_buttonNext->MoveAfterInTabOrder(m_buttonBack);
\r
3128 //m_buttonCancel->MoveAfterInTabOrder(m_buttonNext);
\r
3132 void CViewProductDialog::GetOrder(CWalletTx& wtx)
\r
3135 for (int i = 0; i < product.vOrderForm.size(); i++)
\r
3138 if (m_textCtrlField[i])
\r
3139 strValue = m_textCtrlField[i]->GetValue().Trim();
\r
3141 strValue = m_choiceField[i]->GetStringSelection();
\r
3142 wtx.vOrderForm.push_back(make_pair(m_staticTextLabel[i]->GetLabel(), strValue));
\r
3146 void CViewProductDialog::OnButtonSubmitForm(wxCommandEvent& event)
\r
3148 m_buttonSubmitForm->Enable(false);
\r
3149 m_buttonCancelForm->Enable(false);
\r
3154 CSendingDialog* pdialog = new CSendingDialog(this, product.addr, atoi64(product.mapValue["price"]), wtx);
\r
3155 if (!pdialog->ShowModal())
\r
3157 m_buttonSubmitForm->Enable(true);
\r
3158 m_buttonCancelForm->Enable(true);
\r
3163 void CViewProductDialog::OnButtonCancelForm(wxCommandEvent& event)
\r
3168 void CViewProductDialog::OnButtonBack(wxCommandEvent& event)
\r
3171 m_htmlWinReviews->Show(true);
\r
3172 m_scrolledWindow->Show(false);
\r
3173 m_buttonBack->Enable(false);
\r
3174 m_buttonNext->Enable(!product.vOrderForm.empty());
\r
3179 void CViewProductDialog::OnButtonNext(wxCommandEvent& event)
\r
3181 if (!product.vOrderForm.empty())
\r
3184 m_htmlWinReviews->Show(false);
\r
3185 m_scrolledWindow->Show(true);
\r
3186 m_buttonBack->Enable(true);
\r
3187 m_buttonNext->Enable(false);
\r
3193 void CViewProductDialog::OnButtonCancel(wxCommandEvent& event)
\r
3204 //////////////////////////////////////////////////////////////////////////////
\r
3206 // CViewOrderDialog
\r
3209 CViewOrderDialog::CViewOrderDialog(wxWindow* parent, CWalletTx order, bool fReceived) : CViewOrderDialogBase(parent)
\r
3211 int64 nPrice = (fReceived ? order.GetCredit() : order.GetDebit());
\r
3214 strHTML.reserve(4000);
\r
3215 strHTML += "<html>\n"
\r
3217 "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"
\r
3220 strHTML += "<b>Time:</b> " + HtmlEscape(DateTimeStr(order.nTimeReceived)) + "<br>\n";
\r
3221 strHTML += "<b>Price:</b> " + HtmlEscape(FormatMoney(nPrice)) + "<br>\n";
\r
3222 strHTML += "<b>Status:</b> " + HtmlEscape(FormatTxStatus(order)) + "<br>\n";
\r
3224 strHTML += "<table>\n";
\r
3225 for (int i = 0; i < order.vOrderForm.size(); i++)
\r
3227 strHTML += " <tr><td><b>" + HtmlEscape(order.vOrderForm[i].first) + ":</b></td>";
\r
3228 strHTML += "<td>" + HtmlEscape(order.vOrderForm[i].second) + "</td></tr>\n";
\r
3230 strHTML += "</table>\n";
\r
3232 strHTML += "</body>\n</html>\n";
\r
3234 // Shrink capacity to fit
\r
3235 // (strings are ref counted, so it may live on in SetPage)
\r
3236 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
3238 m_htmlWin->SetPage(strHTML);
\r
3241 void CViewOrderDialog::OnButtonOK(wxCommandEvent& event)
\r
3252 //////////////////////////////////////////////////////////////////////////////
\r
3254 // CEditReviewDialog
\r
3257 CEditReviewDialog::CEditReviewDialog(wxWindow* parent) : CEditReviewDialogBase(parent)
\r
3261 void CEditReviewDialog::OnButtonSubmit(wxCommandEvent& event)
\r
3263 if (m_choiceStars->GetSelection() == -1)
\r
3265 wxMessageBox("Please select a rating ");
\r
3270 GetReview(review);
\r
3272 // Sign the review
\r
3273 review.vchPubKeyFrom = keyUser.GetPubKey();
\r
3274 if (!keyUser.Sign(review.GetSigHash(), review.vchSig))
\r
3276 wxMessageBox("Unable to digitally sign the review ");
\r
3281 if (!review.AcceptReview())
\r
3283 wxMessageBox("Save failed ");
\r
3286 RelayMessage(CInv(MSG_REVIEW, review.GetHash()), review);
\r
3291 void CEditReviewDialog::OnButtonCancel(wxCommandEvent& event)
\r
3296 void CEditReviewDialog::GetReview(CReview& review)
\r
3298 review.mapValue["time"] = i64tostr(GetAdjustedTime());
\r
3299 review.mapValue["stars"] = itostr(m_choiceStars->GetSelection()+1);
\r
3300 review.mapValue["review"] = m_textCtrlReview->GetValue();
\r
3309 //////////////////////////////////////////////////////////////////////////////
\r
3316 ID_TASKBAR_RESTORE = 10001,
\r
3317 ID_TASKBAR_OPTIONS,
\r
3318 ID_TASKBAR_GENERATE,
\r
3322 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
\r
3323 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
\r
3324 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
\r
3325 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
\r
3326 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
\r
3327 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
\r
3328 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
\r
3331 void CMyTaskBarIcon::Show(bool fShow)
\r
3333 static char pszPrevTip[200];
\r
3336 string strTooltip = "Bitcoin";
\r
3337 if (fGenerateBitcoins)
\r
3338 strTooltip = "Bitcoin - Generating";
\r
3339 if (fGenerateBitcoins && vNodes.empty())
\r
3340 strTooltip = "Bitcoin - (not connected)";
\r
3342 // Optimization, only update when changed, using char array to be reentrant
\r
3343 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
\r
3345 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
\r
3347 SetIcon(wxICON(bitcoin), strTooltip);
\r
3349 SetIcon(bitcoin20_xpm, strTooltip);
\r
3355 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
\r
3360 void CMyTaskBarIcon::Hide()
\r
3365 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
\r
3370 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
\r
3375 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
\r
3377 // Since it's modal, get the main window to do it
\r
3378 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_MENUOPTIONSOPTIONS);
\r
3379 pframeMain->AddPendingEvent(event2);
\r
3382 void CMyTaskBarIcon::Restore()
\r
3384 pframeMain->Show();
\r
3385 wxIconizeEvent event(0, false);
\r
3386 pframeMain->AddPendingEvent(event);
\r
3387 pframeMain->Iconize(false);
\r
3388 pframeMain->Raise();
\r
3391 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
\r
3393 GenerateBitcoins(event.IsChecked());
\r
3396 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
\r
3398 event.Check(fGenerateBitcoins);
\r
3401 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
\r
3403 pframeMain->Close(true);
\r
3406 void CMyTaskBarIcon::UpdateTooltip()
\r
3408 if (IsIconInstalled())
\r
3412 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
\r
3414 wxMenu* pmenu = new wxMenu;
\r
3415 pmenu->Append(ID_TASKBAR_RESTORE, "&Open Bitcoin");
\r
3416 pmenu->Append(ID_TASKBAR_OPTIONS, "O&ptions...");
\r
3417 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, "&Generate Coins")->Check(fGenerateBitcoins);
\r
3418 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
\r
3419 pmenu->AppendSeparator();
\r
3420 pmenu->Append(ID_TASKBAR_EXIT, "E&xit");
\r
3434 //////////////////////////////////////////////////////////////////////////////
\r
3439 // Define a new application
\r
3440 class CMyApp: public wxApp
\r
3449 // 2nd-level exception handling: we get all the exceptions occurring in any
\r
3450 // event handler here
\r
3451 virtual bool OnExceptionInMainLoop();
\r
3453 // 3rd, and final, level exception handling: whenever an unhandled
\r
3454 // exception is caught, this function is called
\r
3455 virtual void OnUnhandledException();
\r
3457 // and now for something different: this function is called in case of a
\r
3458 // crash (e.g. dereferencing null pointer, division by 0, ...)
\r
3459 virtual void OnFatalException();
\r
3462 IMPLEMENT_APP(CMyApp)
\r
3464 bool CMyApp::OnInit()
\r
3466 bool fRet = false;
\r
3471 catch (std::exception& e) {
\r
3472 PrintException(&e, "OnInit()");
\r
3474 PrintException(NULL, "OnInit()");
\r
3481 bool CMyApp::OnInit2()
\r
3484 // Turn off microsoft heap dump noise for now
\r
3485 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
\r
3486 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
\r
3488 #if defined(__WXMSW__) && defined(__WXDEBUG__)
\r
3489 // Disable malfunctioning wxWidgets debug assertion
\r
3490 g_isPainting = 10000;
\r
3492 wxImage::AddHandler(new wxPNGHandler);
\r
3494 SetAppName("Bitcoin");
\r
3496 SetAppName("bitcoin");
\r
3503 ParseParameters(argc, argv);
\r
3504 if (mapArgs.count("-?") || mapArgs.count("--help"))
\r
3508 "Usage: bitcoin [options]\t\t\t\t\t\t\n"
\r
3510 " -gen\t\t Generate coins\n"
\r
3511 " -gen=0\t\t Don't generate coins\n"
\r
3512 " -min\t\t Start minimized\n"
\r
3513 " -datadir=<dir>\t Specify data directory\n"
\r
3514 " -proxy=<ip:port>\t Connect through socks4 proxy\n"
\r
3515 " -addnode=<ip>\t Add a node to connect to\n"
\r
3516 " -connect=<ip>\t Connect only to the specified node\n"
\r
3517 " -?\t\t This help message\n";
\r
3518 wxMessageBox(strUsage, "Bitcoin", wxOK);
\r
3521 "Usage: bitcoin [options]\n"
\r
3523 " -gen Generate coins\n"
\r
3524 " -gen=0 Don't generate coins\n"
\r
3525 " -min Start minimized\n"
\r
3526 " -datadir=<dir> Specify data directory\n"
\r
3527 " -proxy=<ip:port> Connect through socks4 proxy\n"
\r
3528 " -addnode=<ip> Add a node to connect to\n"
\r
3529 " -connect=<ip> Connect only to the specified node\n"
\r
3530 " -? This help message\n";
\r
3531 fprintf(stderr, "%s", strUsage.c_str());
\r
3536 if (mapArgs.count("-datadir"))
\r
3537 strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir));
\r
3539 if (mapArgs.count("-debug"))
\r
3542 if (mapArgs.count("-printtodebugger"))
\r
3543 fPrintToDebugger = true;
\r
3545 if (!fDebug && !pszSetDataDir[0])
\r
3546 ShrinkDebugFile();
\r
3547 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
\r
3548 printf("Bitcoin version %d%s, OS version %s\n", VERSION, pszSubVer, wxGetOsDescription().mb_str());
\r
3550 if (mapArgs.count("-loadblockindextest"))
\r
3553 txdb.LoadBlockIndex();
\r
3559 // Limit to single instance per user
\r
3560 // Required to protect the database files if we're going to keep deleting log.*
\r
3563 // todo: wxSingleInstanceChecker wasn't working on Linux, never deleted its lock file
\r
3564 // maybe should go by whether successfully bind port 8333 instead
\r
3565 wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH");
\r
3566 for (int i = 0; i < strMutexName.size(); i++)
\r
3567 if (!isalnum(strMutexName[i]))
\r
3568 strMutexName[i] = '.';
\r
3569 wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
\r
3570 if (psingleinstancechecker->IsAnotherRunning())
\r
3572 printf("Existing instance found\n");
\r
3573 unsigned int nStart = GetTime();
\r
3576 // TODO: find out how to do this in Linux, or replace with wxWidgets commands
\r
3577 // Show the previous instance and exit
\r
3578 HWND hwndPrev = FindWindow("wxWindowClassNR", "Bitcoin");
\r
3581 if (IsIconic(hwndPrev))
\r
3582 ShowWindow(hwndPrev, SW_RESTORE);
\r
3583 SetForegroundWindow(hwndPrev);
\r
3587 if (GetTime() > nStart + 60)
\r
3590 // Resume this instance if the other exits
\r
3591 delete psingleinstancechecker;
\r
3593 psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
\r
3594 if (!psingleinstancechecker->IsAnotherRunning())
\r
3600 // Bind to the port early so we can tell if another instance is already running.
\r
3601 // This is a backup to wxSingleInstanceChecker, which doesn't work on Linux.
\r
3603 if (!BindListenPort(strErrors))
\r
3605 wxMessageBox(strErrors, "Bitcoin");
\r
3610 // Load data files
\r
3616 printf("Loading addresses...\n");
\r
3617 nStart = GetTimeMillis();
\r
3618 if (!LoadAddresses())
\r
3619 strErrors += "Error loading addr.dat \n";
\r
3620 printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
3622 printf("Loading block index...\n");
\r
3623 nStart = GetTimeMillis();
\r
3624 if (!LoadBlockIndex())
\r
3625 strErrors += "Error loading blkindex.dat \n";
\r
3626 printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
3628 printf("Loading wallet...\n");
\r
3629 nStart = GetTimeMillis();
\r
3630 if (!LoadWallet(fFirstRun))
\r
3631 strErrors += "Error loading wallet.dat \n";
\r
3632 printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
3634 printf("Done loading\n");
\r
3637 printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size());
\r
3638 printf("nBestHeight = %d\n", nBestHeight);
\r
3639 printf("mapKeys.size() = %d\n", mapKeys.size());
\r
3640 printf("mapPubKeys.size() = %d\n", mapPubKeys.size());
\r
3641 printf("mapWallet.size() = %d\n", mapWallet.size());
\r
3642 printf("mapAddressBook.size() = %d\n", mapAddressBook.size());
\r
3644 if (!strErrors.empty())
\r
3646 wxMessageBox(strErrors, "Bitcoin");
\r
3650 // Add wallet transactions that aren't already in a block to mapTransactions
\r
3651 ReacceptWalletTransactions();
\r
3656 if (mapArgs.count("-printblockindex") || mapArgs.count("-printblocktree"))
\r
3662 if (mapArgs.count("-printblock"))
\r
3664 string strMatch = mapArgs["-printblock"];
\r
3666 for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
\r
3668 uint256 hash = (*mi).first;
\r
3669 if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0)
\r
3671 CBlockIndex* pindex = (*mi).second;
\r
3673 block.ReadFromDisk(pindex);
\r
3674 block.BuildMerkleTree();
\r
3681 printf("No blocks matching %s were found\n", strMatch.c_str());
\r
3685 if (mapArgs.count("-gen"))
\r
3687 if (mapArgs["-gen"].empty())
\r
3688 fGenerateBitcoins = true;
\r
3690 fGenerateBitcoins = (atoi(mapArgs["-gen"].c_str()) != 0);
\r
3693 if (mapArgs.count("-proxy"))
\r
3696 addrProxy = CAddress(mapArgs["-proxy"]);
\r
3697 if (!addrProxy.IsValid())
\r
3699 wxMessageBox("Invalid -proxy address", "Bitcoin");
\r
3704 if (mapArgs.count("-addnode"))
\r
3707 foreach(string strAddr, mapMultiArgs["-addnode"])
\r
3709 CAddress addr(strAddr, NODE_NETWORK);
\r
3710 addr.nTime = 0; // so it won't relay unless successfully connected
\r
3711 if (addr.IsValid())
\r
3712 AddAddress(addrdb, addr);
\r
3717 // Create the main frame window
\r
3719 pframeMain = new CMainFrame(NULL);
\r
3720 if (mapArgs.count("-min"))
\r
3721 pframeMain->Iconize(true);
\r
3722 pframeMain->Show(true); // have to show first to get taskbar button to hide
\r
3723 if (fMinimizeToTray && pframeMain->IsIconized())
\r
3724 fClosedToTray = true;
\r
3725 pframeMain->Show(!fClosedToTray);
\r
3726 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
3728 CreateThread(ThreadDelayedRepaint, NULL);
\r
3730 if (!CheckDiskSpace())
\r
3733 RandAddSeedPerfmon();
\r
3735 if (!CreateThread(StartNode, NULL))
\r
3736 wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin");
\r
3739 SetStartOnSystemStartup(true);
\r
3746 if (argc >= 2 && stricmp(argv[1], "-send") == 0)
\r
3748 if (argc >= 2 && strcmp(argv[1], "-send") == 0)
\r
3753 ParseMoney(argv[2], nValue);
\r
3755 string strAddress;
\r
3757 strAddress = argv[3];
\r
3758 CAddress addr(strAddress);
\r
3761 wtx.mapValue["to"] = strAddress;
\r
3762 wtx.mapValue["from"] = addrLocalHost.ToString();
\r
3763 wtx.mapValue["message"] = "command line send";
\r
3765 // Send to IP address
\r
3766 CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx);
\r
3767 if (!pdialog->ShowModal())
\r
3774 int CMyApp::OnExit()
\r
3777 return wxApp::OnExit();
\r
3780 bool CMyApp::OnExceptionInMainLoop()
\r
3786 catch (std::exception& e)
\r
3788 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
\r
3789 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
3795 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
\r
3796 wxLogWarning("Unknown exception");
\r
3804 void CMyApp::OnUnhandledException()
\r
3806 // this shows how we may let some exception propagate uncaught
\r
3811 catch (std::exception& e)
\r
3813 PrintException(&e, "CMyApp::OnUnhandledException()");
\r
3814 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
3820 PrintException(NULL, "CMyApp::OnUnhandledException()");
\r
3821 wxLogWarning("Unknown exception");
\r
3827 void CMyApp::OnFatalException()
\r
3829 wxMessageBox("Program has crashed and will terminate. ", "Bitcoin", wxOK | wxICON_ERROR);
\r
3837 typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
\r
3839 string MyGetSpecialFolderPath(int nFolder, bool fCreate)
\r
3841 char pszPath[MAX_PATH+100] = "";
\r
3843 // SHGetSpecialFolderPath is not usually available on NT 4.0
\r
3844 HMODULE hShell32 = LoadLibrary("shell32.dll");
\r
3847 PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath =
\r
3848 (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA");
\r
3849 if (pSHGetSpecialFolderPath)
\r
3850 (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate);
\r
3851 FreeModule(hShell32);
\r
3855 if (pszPath[0] == '\0')
\r
3857 if (nFolder == CSIDL_STARTUP)
\r
3859 strcpy(pszPath, getenv("USERPROFILE"));
\r
3860 strcat(pszPath, "\\Start Menu\\Programs\\Startup");
\r
3862 else if (nFolder == CSIDL_APPDATA)
\r
3864 strcpy(pszPath, getenv("APPDATA"));
\r
3871 string StartupShortcutPath()
\r
3873 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
\r
3876 bool GetStartOnSystemStartup()
\r
3878 return wxFileExists(StartupShortcutPath());
\r
3881 void SetStartOnSystemStartup(bool fAutoStart)
\r
3883 // If the shortcut exists already, remove it for updating
\r
3884 remove(StartupShortcutPath().c_str());
\r
3888 CoInitialize(NULL);
\r
3890 // Get a pointer to the IShellLink interface.
\r
3891 IShellLink* psl = NULL;
\r
3892 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
\r
3893 CLSCTX_INPROC_SERVER, IID_IShellLink,
\r
3894 reinterpret_cast<void**>(&psl));
\r
3896 if (SUCCEEDED(hres))
\r
3898 // Get the current executable path
\r
3899 char pszExePath[MAX_PATH];
\r
3900 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
\r
3902 // Set the path to the shortcut target
\r
3903 psl->SetPath(pszExePath);
\r
3904 PathRemoveFileSpec(pszExePath);
\r
3905 psl->SetWorkingDirectory(pszExePath);
\r
3906 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
\r
3908 // Query IShellLink for the IPersistFile interface for
\r
3909 // saving the shortcut in persistent storage.
\r
3910 IPersistFile* ppf = NULL;
\r
3911 hres = psl->QueryInterface(IID_IPersistFile,
\r
3912 reinterpret_cast<void**>(&ppf));
\r
3913 if (SUCCEEDED(hres))
\r
3915 WCHAR pwsz[MAX_PATH];
\r
3916 // Ensure that the string is ANSI.
\r
3917 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
\r
3918 // Save the link by calling IPersistFile::Save.
\r
3919 hres = ppf->Save(pwsz, TRUE);
\r
3928 bool GetStartOnSystemStartup() { return false; }
\r
3929 void SetStartOnSystemStartup(bool fAutoStart) { }
\r