1 // Copyright (c) 2009-2010 Satoshi Nakamoto
\r
2 // Distributed under the MIT/X11 software license, see the accompanying
\r
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
\r
10 void ThreadRequestProductDetails(void* parg);
\r
11 void ThreadRandSendTest(void* parg);
\r
12 bool GetStartOnSystemStartup();
\r
13 void SetStartOnSystemStartup(bool fAutoStart);
\r
17 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
\r
18 DEFINE_EVENT_TYPE(wxEVT_REPLY1)
\r
19 DEFINE_EVENT_TYPE(wxEVT_REPLY2)
\r
20 DEFINE_EVENT_TYPE(wxEVT_REPLY3)
\r
22 CMainFrame* pframeMain = NULL;
\r
23 CMyTaskBarIcon* ptaskbaricon = NULL;
\r
24 map<string, string> mapAddressBook;
\r
25 bool fRandSendTest = false;
\r
27 extern int g_isPainting;
\r
28 bool fClosedToTray = false;
\r
31 int fShowGenerated = true;
\r
32 int fMinimizeToTray = true;
\r
33 int fMinimizeOnClose = true;
\r
41 //////////////////////////////////////////////////////////////////////////////
\r
46 void HandleCtrlA(wxKeyEvent& event)
\r
48 // Ctrl-a select all
\r
49 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
\r
50 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
\r
51 textCtrl->SetSelection(-1, -1);
\r
57 //char pszHourFormat[256];
\r
58 //pszHourFormat[0] = '\0';
\r
59 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
\r
60 //return (pszHourFormat[0] != '0');
\r
64 string DateStr(int64 nTime)
\r
66 // Can only be used safely here in the UI
\r
67 return (string)wxDateTime((time_t)nTime).FormatDate();
\r
70 string DateTimeStr(int64 nTime)
\r
72 // Can only be used safely here in the UI
\r
73 wxDateTime datetime((time_t)nTime);
\r
75 return (string)datetime.Format("%x %H:%M");
\r
77 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
\r
80 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
\r
82 // Helper to simplify access to listctrl
\r
84 item.m_itemId = nIndex;
\r
85 item.m_col = nColumn;
\r
86 item.m_mask = wxLIST_MASK_TEXT;
\r
87 if (!listCtrl->GetItem(item))
\r
89 return item.GetText();
\r
92 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
\r
94 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
95 listCtrl->SetItem(nIndex, 1, str1);
\r
99 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
101 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
102 listCtrl->SetItem(nIndex, 1, str1);
\r
103 listCtrl->SetItem(nIndex, 2, str2);
\r
104 listCtrl->SetItem(nIndex, 3, str3);
\r
105 listCtrl->SetItem(nIndex, 4, str4);
\r
109 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
111 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
112 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
\r
113 listCtrl->SetItem(nIndex, 1, str1);
\r
114 listCtrl->SetItem(nIndex, 2, str2);
\r
115 listCtrl->SetItem(nIndex, 3, str3);
\r
116 listCtrl->SetItem(nIndex, 4, str4);
\r
120 void SetSelection(wxListCtrl* listCtrl, int nIndex)
\r
122 int nSize = listCtrl->GetItemCount();
\r
123 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
124 for (int i = 0; i < nSize; i++)
\r
125 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
\r
128 int GetSelection(wxListCtrl* listCtrl)
\r
130 int nSize = listCtrl->GetItemCount();
\r
131 for (int i = 0; i < nSize; i++)
\r
132 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
\r
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
193 if (mapArgs.count("-noui"))
\r
197 return wxMessageBox(message, caption, style, parent, x, y);
\r
199 if (wxThread::IsMain())
\r
201 return wxMessageBox(message, caption, style, parent, x, y);
\r
206 bool fDone = false;
\r
207 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
\r
224 //////////////////////////////////////////////////////////////////////////////
\r
228 // If this code gets used again, it should be replaced with something like UIThreadCall
\r
230 set<void*> setCallbackAvailable;
\r
231 CCriticalSection cs_setCallbackAvailable;
\r
233 void AddCallbackAvailable(void* p)
\r
235 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
236 setCallbackAvailable.insert(p);
\r
239 void RemoveCallbackAvailable(void* p)
\r
241 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
242 setCallbackAvailable.erase(p);
\r
245 bool IsCallbackAvailable(void* p)
\r
247 CRITICAL_BLOCK(cs_setCallbackAvailable)
\r
248 return setCallbackAvailable.count(p);
\r
252 template<typename T>
\r
253 void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T pbeginIn, const T pendIn)
\r
255 // Need to rewrite with something like UIThreadCall
\r
256 // I'm tired of maintaining this hack that's only called by unfinished unused code,
\r
257 // but I'm not willing to delete it because it serves as documentation of what the
\r
258 // unfinished code was trying to do.
\r
259 assert(("Unimplemented", 0));
\r
260 //if (!pevthandler)
\r
263 //const char* pbegin = (pendIn != pbeginIn) ? &pbeginIn[0] : NULL;
\r
264 //const char* pend = pbegin + (pendIn - pbeginIn) * sizeof(pbeginIn[0]);
\r
265 //wxCommandEvent event(nEventID);
\r
266 //wxString strData(wxChar(0), (pend - pbegin) / sizeof(wxChar) + 1);
\r
267 //memcpy(&strData[0], pbegin, pend - pbegin);
\r
268 //event.SetString(strData);
\r
269 //event.SetInt(pend - pbegin);
\r
271 //pevthandler->AddPendingEvent(event);
\r
275 void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T& obj)
\r
279 AddPendingCustomEvent(pevthandler, nEventID, ss.begin(), ss.end());
\r
282 void AddPendingReplyEvent1(void* pevthandler, CDataStream& vRecv)
\r
284 if (IsCallbackAvailable(pevthandler))
\r
285 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY1, vRecv.begin(), vRecv.end());
\r
288 void AddPendingReplyEvent2(void* pevthandler, CDataStream& vRecv)
\r
290 if (IsCallbackAvailable(pevthandler))
\r
291 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY2, vRecv.begin(), vRecv.end());
\r
294 void AddPendingReplyEvent3(void* pevthandler, CDataStream& vRecv)
\r
296 if (IsCallbackAvailable(pevthandler))
\r
297 AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY3, vRecv.begin(), vRecv.end());
\r
300 CDataStream GetStreamFromEvent(const wxCommandEvent& event)
\r
302 wxString strData = event.GetString();
\r
303 const char* pszBegin = strData.c_str();
\r
304 return CDataStream(pszBegin, pszBegin + event.GetInt(), SER_NETWORK);
\r
313 //////////////////////////////////////////////////////////////////////////////
\r
318 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
\r
320 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
\r
323 fRefreshListCtrl = false;
\r
324 fRefreshListCtrlRunning = false;
\r
325 fOnSetFocusAddress = false;
\r
327 m_choiceFilter->SetSelection(0);
\r
328 double dResize = 1.0;
\r
330 SetIcon(wxICON(bitcoin));
\r
332 SetIcon(bitcoin16_xpm);
\r
333 wxFont fontTmp = m_staticText41->GetFont();
\r
334 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
\r
335 m_staticTextBalance->SetFont(fontTmp);
\r
336 m_staticTextBalance->SetSize(140, 17);
\r
337 // & underlines don't work on the toolbar buttons on gtk
\r
338 m_toolBar->ClearTools();
\r
339 m_toolBar->AddTool(wxID_BUTTONSEND, "Send Coins", wxBitmap(send20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
\r
340 m_toolBar->AddTool(wxID_BUTTONRECEIVE, "Address Book", wxBitmap(addressbook20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
\r
341 m_toolBar->Realize();
\r
342 // resize to fit ubuntu's huge default font
\r
344 SetSize(dResize * GetSize().GetWidth(), 1.1 * GetSize().GetHeight());
\r
346 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
347 m_listCtrl->SetFocus();
\r
348 ptaskbaricon = new CMyTaskBarIcon();
\r
350 // Init column headers
\r
351 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
\r
352 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
\r
354 m_listCtrl->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
355 m_listCtrl->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
356 m_listCtrl->InsertColumn(2, "Status", wxLIST_FORMAT_LEFT, dResize * 110);
\r
357 m_listCtrl->InsertColumn(3, "Date", wxLIST_FORMAT_LEFT, dResize * nDateWidth);
\r
358 m_listCtrl->InsertColumn(4, "Description", wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
\r
359 m_listCtrl->InsertColumn(5, "Debit", wxLIST_FORMAT_RIGHT, dResize * 79);
\r
360 m_listCtrl->InsertColumn(6, "Credit", wxLIST_FORMAT_RIGHT, dResize * 79);
\r
362 //m_listCtrlProductsSent->InsertColumn(0, "Category", wxLIST_FORMAT_LEFT, 100);
\r
363 //m_listCtrlProductsSent->InsertColumn(1, "Title", wxLIST_FORMAT_LEFT, 100);
\r
364 //m_listCtrlProductsSent->InsertColumn(2, "Description", wxLIST_FORMAT_LEFT, 100);
\r
365 //m_listCtrlProductsSent->InsertColumn(3, "Price", wxLIST_FORMAT_LEFT, 100);
\r
366 //m_listCtrlProductsSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
\r
368 //m_listCtrlOrdersSent->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100);
\r
369 //m_listCtrlOrdersSent->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100);
\r
370 //m_listCtrlOrdersSent->InsertColumn(2, "", wxLIST_FORMAT_LEFT, 100);
\r
371 //m_listCtrlOrdersSent->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100);
\r
372 //m_listCtrlOrdersSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
\r
374 //m_listCtrlOrdersReceived->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100);
\r
375 //m_listCtrlOrdersReceived->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100);
\r
376 //m_listCtrlOrdersReceived->InsertColumn(2, "Payment Status", wxLIST_FORMAT_LEFT, 100);
\r
377 //m_listCtrlOrdersReceived->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100);
\r
378 //m_listCtrlOrdersReceived->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100);
\r
381 int pnWidths[3] = { -100, 88, 290 };
\r
383 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
\r
384 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
\r
386 m_statusBar->SetFieldsCount(3, pnWidths);
\r
388 // Fill your address text box
\r
389 vector<unsigned char> vchPubKey;
\r
390 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
\r
391 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
\r
393 // Fill listctrl with wallet transactions
\r
397 CMainFrame::~CMainFrame()
\r
400 delete ptaskbaricon;
\r
401 ptaskbaricon = NULL;
\r
404 void ExitTimeout(void* parg)
\r
412 void Shutdown(void* parg)
\r
414 static CCriticalSection cs_Shutdown;
\r
415 static bool fTaken;
\r
417 CRITICAL_BLOCK(cs_Shutdown)
\r
419 fFirstThread = !fTaken;
\r
426 nTransactionsUpdated++;
\r
430 CreateThread(ExitTimeout, NULL);
\r
432 printf("Bitcoin exiting\n\n");
\r
445 void CMainFrame::OnClose(wxCloseEvent& event)
\r
447 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
\r
449 // Divert close to minimize
\r
451 fClosedToTray = true;
\r
457 CreateThread(Shutdown, NULL);
\r
461 void CMainFrame::OnIconize(wxIconizeEvent& event)
\r
463 // Hide the task bar button when minimized.
\r
464 // Event is sent when the frame is minimized or restored.
\r
465 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
\r
466 // to get rid of the deprecated warning. Just ignore it.
\r
467 if (!event.Iconized())
\r
468 fClosedToTray = false;
\r
470 // Tray is not reliable on Linux gnome
\r
471 fClosedToTray = false;
\r
473 if (fMinimizeToTray && event.Iconized())
\r
474 fClosedToTray = true;
\r
475 Show(!fClosedToTray);
\r
476 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
479 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
\r
482 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
\r
483 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
\r
486 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
\r
488 // Hidden columns not resizeable
\r
489 if (event.GetColumn() <= 1 && !fDebug)
\r
493 int CMainFrame::GetSortIndex(const string& strSort)
\r
498 // The wx generic listctrl implementation used on GTK doesn't sort,
\r
499 // so we have to do it ourselves. Remember, we sort in reverse order.
\r
500 // In the wx generic implementation, they store the list of items
\r
501 // in a vector, so indexed lookups are fast, but inserts are slower
\r
502 // the closer they are to the top.
\r
504 int high = m_listCtrl->GetItemCount();
\r
507 int mid = low + ((high - low) / 2);
\r
508 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
\r
517 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
519 string str0 = strSort;
\r
520 long nData = *(long*)&hashKey;
\r
523 if (!fNew && nIndex == -1)
\r
525 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
526 if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())
\r
530 // fNew is for blind insert, only use if you're sure it's new
\r
531 if (fNew || nIndex == -1)
\r
533 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), str0);
\r
537 // If sort key changed, must delete and reinsert to make it relocate
\r
538 if (GetItemText(m_listCtrl, nIndex, 0) != str0)
\r
540 m_listCtrl->DeleteItem(nIndex);
\r
541 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), str0);
\r
545 m_listCtrl->SetItem(nIndex, 1, hashKey.ToString());
\r
546 m_listCtrl->SetItem(nIndex, 2, str2);
\r
547 m_listCtrl->SetItem(nIndex, 3, str3);
\r
548 m_listCtrl->SetItem(nIndex, 4, str4);
\r
549 m_listCtrl->SetItem(nIndex, 5, str5);
\r
550 m_listCtrl->SetItem(nIndex, 6, str6);
\r
551 m_listCtrl->SetItemData(nIndex, nData);
\r
554 bool CMainFrame::DeleteLine(uint256 hashKey)
\r
556 long nData = *(long*)&hashKey;
\r
560 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
561 if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString())
\r
565 m_listCtrl->DeleteItem(nIndex);
\r
567 return nIndex != -1;
\r
570 string FormatTxStatus(const CWalletTx& wtx)
\r
573 if (!wtx.IsFinal())
\r
575 if (wtx.nLockTime < 500000000)
\r
576 return strprintf("Open for %d blocks", nBestHeight - wtx.nLockTime);
\r
578 return strprintf("Open until %s", DateTimeStr(wtx.nLockTime).c_str());
\r
582 int nDepth = wtx.GetDepthInMainChain();
\r
583 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
584 return strprintf("%d/offline?", nDepth);
\r
585 else if (nDepth < 6)
\r
586 return strprintf("%d/unconfirmed", nDepth);
\r
588 return strprintf("%d confirmations", nDepth);
\r
592 string SingleLine(const string& strIn)
\r
595 bool fOneSpace = false;
\r
596 foreach(int c, strIn)
\r
604 if (fOneSpace && !strOut.empty())
\r
613 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
\r
615 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
\r
616 int64 nCredit = wtx.GetCredit();
\r
617 int64 nDebit = wtx.GetDebit();
\r
618 int64 nNet = nCredit - nDebit;
\r
619 uint256 hash = wtx.GetHash();
\r
620 string strStatus = FormatTxStatus(wtx);
\r
621 map<string, string> mapValue = wtx.mapValue;
\r
622 wtx.nLinesDisplayed = 1;
\r
623 nListViewUpdated++;
\r
626 if (wtx.IsCoinBase())
\r
628 // Don't show generated coin until confirmed by at least one block after it
\r
629 // so we don't get the user's hopes up until it looks like it's probably accepted.
\r
631 // It is not an error when generated blocks are not accepted. By design,
\r
632 // some percentage of blocks, like 10% or more, will end up not accepted.
\r
633 // This is the normal mechanism by which the network copes with latency.
\r
635 // We display regular transactions right away before any confirmation
\r
636 // because they can always get into some block eventually. Generated coins
\r
637 // are special because if their block is not accepted, they are not valid.
\r
639 if (wtx.GetDepthInMainChain() < 2)
\r
641 wtx.nLinesDisplayed = 0;
\r
645 // View->Show Generated
\r
646 if (!fShowGenerated)
\r
650 // Find the block the tx is in
\r
651 CBlockIndex* pindex = NULL;
\r
652 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
\r
653 if (mi != mapBlockIndex.end())
\r
654 pindex = (*mi).second;
\r
656 // Sort order, unrecorded transactions sort to the top
\r
657 string strSort = strprintf("%010d-%01d-%010u",
\r
658 (pindex ? pindex->nHeight : INT_MAX),
\r
659 (wtx.IsCoinBase() ? 1 : 0),
\r
660 wtx.nTimeReceived);
\r
663 if (nNet > 0 || wtx.IsCoinBase())
\r
668 string strDescription;
\r
670 if (wtx.IsCoinBase())
\r
673 strDescription = "Generated";
\r
676 int64 nUnmatured = 0;
\r
677 foreach(const CTxOut& txout, wtx.vout)
\r
678 nUnmatured += txout.GetCredit();
\r
679 if (wtx.IsInMainChain())
\r
681 strDescription = strprintf("Generated (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
683 // Check if the block was requested by anyone
\r
684 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
685 strDescription = "Generated - Warning: This block was not received by any other nodes and will probably not be accepted!";
\r
689 strDescription = "Generated (not accepted)";
\r
693 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
\r
695 // Online transaction
\r
696 if (!mapValue["from"].empty())
\r
697 strDescription += "From: " + mapValue["from"];
\r
698 if (!mapValue["message"].empty())
\r
700 if (!strDescription.empty())
\r
701 strDescription += " - ";
\r
702 strDescription += mapValue["message"];
\r
707 // Offline transaction
\r
708 foreach(const CTxOut& txout, wtx.vout)
\r
710 if (txout.IsMine())
\r
712 vector<unsigned char> vchPubKey;
\r
713 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
715 string strAddress = PubKeyToAddress(vchPubKey);
\r
716 if (mapAddressBook.count(strAddress))
\r
718 //strDescription += "Received payment to ";
\r
719 //strDescription += "Received with address ";
\r
720 strDescription += "From: unknown, To: ";
\r
721 strDescription += strAddress;
\r
722 /// The labeling feature is just too confusing, so I hid it
\r
723 /// by putting it at the end where it runs off the screen.
\r
724 /// It can still be seen by widening the column, or in the
\r
725 /// details dialog.
\r
726 if (!mapAddressBook[strAddress].empty())
\r
727 strDescription += " (" + mapAddressBook[strAddress] + ")";
\r
735 InsertLine(fNew, nIndex, hash, strSort,
\r
737 nTime ? DateTimeStr(nTime) : "",
\r
738 SingleLine(strDescription),
\r
740 FormatMoney(nNet, true));
\r
744 bool fAllFromMe = true;
\r
745 foreach(const CTxIn& txin, wtx.vin)
\r
746 fAllFromMe = fAllFromMe && txin.IsMine();
\r
748 bool fAllToMe = true;
\r
749 foreach(const CTxOut& txout, wtx.vout)
\r
750 fAllToMe = fAllToMe && txout.IsMine();
\r
752 if (fAllFromMe && fAllToMe)
\r
755 int64 nValue = wtx.vout[0].nValue;
\r
756 InsertLine(fNew, nIndex, hash, strSort,
\r
758 nTime ? DateTimeStr(nTime) : "",
\r
759 "Payment to yourself",
\r
762 /// issue: can't tell which is the payment and which is the change anymore
\r
763 // FormatMoney(nNet - nValue, true),
\r
764 // FormatMoney(nValue, true));
\r
766 else if (fAllFromMe)
\r
771 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
772 wtx.nLinesDisplayed = 0;
\r
773 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
\r
775 const CTxOut& txout = wtx.vout[nOut];
\r
776 if (txout.IsMine())
\r
780 if (!mapValue["to"].empty())
\r
782 // Online transaction
\r
783 strAddress = mapValue["to"];
\r
787 // Offline transaction
\r
789 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
790 strAddress = Hash160ToAddress(hash160);
\r
793 string strDescription = "To: ";
\r
794 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
795 strDescription += mapAddressBook[strAddress] + " ";
\r
796 strDescription += strAddress;
\r
797 if (!mapValue["message"].empty())
\r
799 if (!strDescription.empty())
\r
800 strDescription += " - ";
\r
801 strDescription += mapValue["message"];
\r
804 int64 nValue = txout.nValue;
\r
805 if (nOut == 0 && nTxFee > 0)
\r
808 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
\r
810 nTime ? DateTimeStr(nTime) : "",
\r
811 SingleLine(strDescription),
\r
812 FormatMoney(-nValue, true),
\r
814 wtx.nLinesDisplayed++;
\r
820 // Mixed debit transaction, can't break down payees
\r
822 bool fAllMine = true;
\r
823 foreach(const CTxOut& txout, wtx.vout)
\r
824 fAllMine = fAllMine && txout.IsMine();
\r
825 foreach(const CTxIn& txin, wtx.vin)
\r
826 fAllMine = fAllMine && txin.IsMine();
\r
828 InsertLine(fNew, nIndex, hash, strSort,
\r
830 nTime ? DateTimeStr(nTime) : "",
\r
832 FormatMoney(nNet, true),
\r
840 void CMainFrame::RefreshListCtrl()
\r
842 fRefreshListCtrl = true;
\r
846 void CMainFrame::OnIdle(wxIdleEvent& event)
\r
848 if (fRefreshListCtrl)
\r
850 // Collect list of wallet transactions and sort newest first
\r
851 bool fEntered = false;
\r
852 vector<pair<unsigned int, uint256> > vSorted;
\r
853 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
855 printf("RefreshListCtrl starting\n");
\r
857 fRefreshListCtrl = false;
\r
858 vWalletUpdated.clear();
\r
860 // Do the newest transactions first
\r
861 vSorted.reserve(mapWallet.size());
\r
862 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
864 const CWalletTx& wtx = (*it).second;
\r
865 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
\r
866 vSorted.push_back(make_pair(nTime, (*it).first));
\r
868 m_listCtrl->DeleteAllItems();
\r
873 sort(vSorted.begin(), vSorted.end());
\r
875 // Fill list control
\r
876 for (int i = 0; i < vSorted.size();)
\r
880 bool fEntered = false;
\r
881 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
884 uint256& hash = vSorted[i++].second;
\r
885 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
886 if (mi != mapWallet.end())
\r
887 InsertTransaction((*mi).second, true);
\r
889 if (!fEntered || i == 100 || i % 500 == 0)
\r
893 printf("RefreshListCtrl done\n");
\r
895 // Update transaction total display
\r
896 MainFrameRepaint();
\r
900 // Check for time updates
\r
901 static int64 nLastTime;
\r
902 if (GetTime() > nLastTime + 30)
\r
904 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
906 nLastTime = GetTime();
\r
907 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
909 CWalletTx& wtx = (*it).second;
\r
910 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
\r
911 InsertTransaction(wtx, false);
\r
918 void CMainFrame::RefreshStatusColumn()
\r
920 static int nLastTop;
\r
921 static CBlockIndex* pindexLastBest;
\r
922 static unsigned int nLastRefreshed;
\r
924 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
\r
925 if (nTop == nLastTop && pindexLastBest == pindexBest)
\r
928 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
931 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
\r
933 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
\r
935 // If no updates, only need to do the part that moved onto the screen
\r
936 if (nStart >= nLastTop && nStart < nLastTop + 100)
\r
937 nStart = nLastTop + 100;
\r
938 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
\r
942 pindexLastBest = pindexBest;
\r
943 nLastRefreshed = nListViewUpdated;
\r
945 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
\r
947 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
\r
948 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
949 if (mi == mapWallet.end())
\r
951 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
\r
954 CWalletTx& wtx = (*mi).second;
\r
955 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
\r
957 if (!InsertTransaction(wtx, false, nIndex))
\r
958 m_listCtrl->DeleteItem(nIndex--);
\r
961 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
\r
966 void CMainFrame::OnPaint(wxPaintEvent& event)
\r
977 unsigned int nNeedRepaint = 0;
\r
978 unsigned int nLastRepaint = 0;
\r
979 int64 nLastRepaintTime = 0;
\r
980 int64 nRepaintInterval = 500;
\r
982 void ThreadDelayedRepaint(void* parg)
\r
986 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
988 nLastRepaint = nNeedRepaint;
\r
991 printf("DelayedRepaint\n");
\r
992 wxPaintEvent event;
\r
993 pframeMain->fRefresh = true;
\r
994 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
997 Sleep(nRepaintInterval);
\r
1001 void MainFrameRepaint()
\r
1003 // This is called by network code that shouldn't access pframeMain
\r
1004 // directly because it could still be running after the UI is closed.
\r
1007 // Don't repaint too often
\r
1008 static int64 nLastRepaintRequest;
\r
1009 if (GetTimeMillis() - nLastRepaintRequest < 100)
\r
1014 nLastRepaintRequest = GetTimeMillis();
\r
1016 printf("MainFrameRepaint\n");
\r
1017 wxPaintEvent event;
\r
1018 pframeMain->fRefresh = true;
\r
1019 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
1023 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
\r
1026 ptaskbaricon->UpdateTooltip();
\r
1031 static int nTransactionCount;
\r
1032 bool fPaintedBalance = false;
\r
1033 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
1035 nLastRepaint = nNeedRepaint;
\r
1036 nLastRepaintTime = GetTimeMillis();
\r
1038 // Update listctrl contents
\r
1039 if (!vWalletUpdated.empty())
\r
1041 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
1044 if (m_listCtrl->GetItemCount())
\r
1045 strTop = (string)m_listCtrl->GetItemText(0);
\r
1046 foreach(uint256 hash, vWalletUpdated)
\r
1048 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1049 if (mi != mapWallet.end())
\r
1050 InsertTransaction((*mi).second, false);
\r
1052 vWalletUpdated.clear();
\r
1053 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
\r
1054 m_listCtrl->ScrollList(0, INT_MIN/2);
\r
1059 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
1061 fPaintedBalance = true;
\r
1062 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
1064 // Count hidden and multi-line transactions
\r
1065 nTransactionCount = 0;
\r
1066 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
1068 CWalletTx& wtx = (*it).second;
\r
1069 nTransactionCount += wtx.nLinesDisplayed;
\r
1073 if (!vWalletUpdated.empty() || !fPaintedBalance)
\r
1076 // Update status column of visible items only
\r
1077 RefreshStatusColumn();
\r
1079 // Update status bar
\r
1080 string strGen = "";
\r
1081 if (fGenerateBitcoins)
\r
1082 strGen = " Generating";
\r
1083 if (fGenerateBitcoins && vNodes.empty())
\r
1084 strGen = "(not connected)";
\r
1085 m_statusBar->SetStatusText(strGen, 1);
\r
1087 string strStatus = strprintf(" %d connections %d blocks %d transactions", vNodes.size(), nBestHeight + 1, nTransactionCount);
\r
1088 m_statusBar->SetStatusText(strStatus, 2);
\r
1090 if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
\r
1091 m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0);
\r
1093 // Pass through to listctrl to actually do the paint, we're just hooking the message
\r
1094 m_listCtrl->Disconnect(wxEVT_PAINT, (wxObjectEventFunction)NULL, NULL, this);
\r
1095 m_listCtrl->GetEventHandler()->ProcessEvent(event);
\r
1096 m_listCtrl->Connect(wxEVT_PAINT, wxPaintEventHandler(CMainFrame::OnPaintListCtrl), NULL, this);
\r
1100 void UIThreadCall(boost::function0<void> fn)
\r
1102 // Call this with a function object created with bind.
\r
1103 // bind needs all parameters to match the function's expected types
\r
1104 // and all default parameters specified. Some examples:
\r
1105 // UIThreadCall(bind(wxBell));
\r
1106 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
\r
1107 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
\r
1110 wxCommandEvent event(wxEVT_UITHREADCALL);
\r
1111 event.SetClientData((void*)new boost::function0<void>(fn));
\r
1112 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
1116 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
\r
1118 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
\r
1123 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
\r
1129 void CMainFrame::OnMenuViewShowGenerated(wxCommandEvent& event)
\r
1131 // View->Show Generated
\r
1132 fShowGenerated = event.IsChecked();
\r
1133 CWalletDB().WriteSetting("fShowGenerated", fShowGenerated);
\r
1134 RefreshListCtrl();
\r
1137 void CMainFrame::OnUpdateUIViewShowGenerated(wxUpdateUIEvent& event)
\r
1139 event.Check(fShowGenerated);
\r
1142 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
\r
1144 // Options->Generate Coins
\r
1145 GenerateBitcoins(event.IsChecked());
\r
1148 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
\r
1150 event.Check(fGenerateBitcoins);
\r
1153 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
\r
1155 // Options->Change Your Address
\r
1156 OnButtonChange(event);
\r
1159 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
\r
1161 // Options->Options
\r
1162 COptionsDialog dialog(this);
\r
1163 dialog.ShowModal();
\r
1166 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
\r
1169 CAboutDialog dialog(this);
\r
1170 dialog.ShowModal();
\r
1173 void CMainFrame::OnButtonSend(wxCommandEvent& event)
\r
1176 CSendDialog dialog(this);
\r
1177 dialog.ShowModal();
\r
1180 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
\r
1182 // Toolbar: Address Book
\r
1183 CAddressBookDialog dialogAddr(this, "", false);
\r
1184 if (dialogAddr.ShowModal() == 2)
\r
1187 CSendDialog dialogSend(this, dialogAddr.GetAddress());
\r
1188 dialogSend.ShowModal();
\r
1192 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
\r
1194 // Automatically select-all when entering window
\r
1195 m_textCtrlAddress->SetSelection(-1, -1);
\r
1196 fOnSetFocusAddress = true;
\r
1200 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
\r
1202 if (fOnSetFocusAddress)
\r
1203 m_textCtrlAddress->SetSelection(-1, -1);
\r
1204 fOnSetFocusAddress = false;
\r
1208 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
\r
1210 // Copy address box to clipboard
\r
1211 if (wxTheClipboard->Open())
\r
1213 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
\r
1214 wxTheClipboard->Close();
\r
1218 void CMainFrame::OnButtonChange(wxCommandEvent& event)
\r
1220 CYourAddressDialog dialog(this, string(m_textCtrlAddress->GetValue()));
\r
1221 if (!dialog.ShowModal())
\r
1223 string strAddress = (string)dialog.GetAddress();
\r
1224 if (strAddress != m_textCtrlAddress->GetValue())
\r
1227 if (!AddressToHash160(strAddress, hash160))
\r
1229 if (!mapPubKeys.count(hash160))
\r
1231 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
\r
1232 m_textCtrlAddress->SetValue(strAddress);
\r
1236 void CMainFrame::OnListItemActivatedAllTransactions(wxListEvent& event)
\r
1238 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
\r
1240 CRITICAL_BLOCK(cs_mapWallet)
\r
1242 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1243 if (mi == mapWallet.end())
\r
1245 printf("CMainFrame::OnListItemActivatedAllTransactions() : tx not found in mapWallet\n");
\r
1248 wtx = (*mi).second;
\r
1250 CTxDetailsDialog dialog(this, wtx);
\r
1251 dialog.ShowModal();
\r
1252 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
\r
1253 //pdialog->Show();
\r
1256 void CMainFrame::OnListItemActivatedProductsSent(wxListEvent& event)
\r
1258 CProduct& product = *(CProduct*)event.GetItem().GetData();
\r
1259 CEditProductDialog* pdialog = new CEditProductDialog(this);
\r
1260 pdialog->SetProduct(product);
\r
1264 void CMainFrame::OnListItemActivatedOrdersSent(wxListEvent& event)
\r
1266 CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();
\r
1267 CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, false);
\r
1271 void CMainFrame::OnListItemActivatedOrdersReceived(wxListEvent& event)
\r
1273 CWalletTx& order = *(CWalletTx*)event.GetItem().GetData();
\r
1274 CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, true);
\r
1284 //////////////////////////////////////////////////////////////////////////////
\r
1286 // CTxDetailsDialog
\r
1289 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
\r
1292 strHTML.reserve(4000);
\r
1293 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
\r
1295 int64 nTime = wtx.GetTxTime();
\r
1296 int64 nCredit = wtx.GetCredit();
\r
1297 int64 nDebit = wtx.GetDebit();
\r
1298 int64 nNet = nCredit - nDebit;
\r
1302 strHTML += "<b>Status:</b> " + FormatTxStatus(wtx);
\r
1303 int nRequests = wtx.GetRequestCount();
\r
1304 if (nRequests != -1)
\r
1306 if (nRequests == 0)
\r
1307 strHTML += ", has not been successfully broadcast yet";
\r
1308 else if (nRequests == 1)
\r
1309 strHTML += strprintf(", broadcast through %d node", nRequests);
\r
1311 strHTML += strprintf(", broadcast through %d nodes", nRequests);
\r
1313 strHTML += "<br>";
\r
1315 strHTML += "<b>Date:</b> " + (nTime ? DateTimeStr(nTime) : "") + "<br>";
\r
1321 if (wtx.IsCoinBase())
\r
1323 strHTML += "<b>Source:</b> Generated<br>";
\r
1325 else if (!wtx.mapValue["from"].empty())
\r
1327 // Online transaction
\r
1328 if (!wtx.mapValue["from"].empty())
\r
1329 strHTML += "<b>From:</b> " + HtmlEscape(wtx.mapValue["from"]) + "<br>";
\r
1333 // Offline transaction
\r
1337 foreach(const CTxOut& txout, wtx.vout)
\r
1339 if (txout.IsMine())
\r
1341 vector<unsigned char> vchPubKey;
\r
1342 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
1344 string strAddress = PubKeyToAddress(vchPubKey);
\r
1345 if (mapAddressBook.count(strAddress))
\r
1347 strHTML += "<b>From:</b> unknown<br>";
\r
1348 strHTML += "<b>To:</b> ";
\r
1349 strHTML += HtmlEscape(strAddress);
\r
1350 if (!mapAddressBook[strAddress].empty())
\r
1351 strHTML += " (yours, label: " + mapAddressBook[strAddress] + ")";
\r
1353 strHTML += " (yours)";
\r
1354 strHTML += "<br>";
\r
1367 string strAddress;
\r
1368 if (!wtx.mapValue["to"].empty())
\r
1370 // Online transaction
\r
1371 strAddress = wtx.mapValue["to"];
\r
1372 strHTML += "<b>To:</b> ";
\r
1373 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1374 strHTML += mapAddressBook[strAddress] + " ";
\r
1375 strHTML += HtmlEscape(strAddress) + "<br>";
\r
1382 if (wtx.IsCoinBase() && nCredit == 0)
\r
1387 int64 nUnmatured = 0;
\r
1388 foreach(const CTxOut& txout, wtx.vout)
\r
1389 nUnmatured += txout.GetCredit();
\r
1390 if (wtx.IsInMainChain())
\r
1391 strHTML += strprintf("<b>Credit:</b> (%s matures in %d more blocks)<br>", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
1393 strHTML += "<b>Credit:</b> (not accepted)<br>";
\r
1395 else if (nNet > 0)
\r
1400 strHTML += "<b>Credit:</b> " + FormatMoney(nNet) + "<br>";
\r
1404 bool fAllFromMe = true;
\r
1405 foreach(const CTxIn& txin, wtx.vin)
\r
1406 fAllFromMe = fAllFromMe && txin.IsMine();
\r
1408 bool fAllToMe = true;
\r
1409 foreach(const CTxOut& txout, wtx.vout)
\r
1410 fAllToMe = fAllToMe && txout.IsMine();
\r
1417 foreach(const CTxOut& txout, wtx.vout)
\r
1419 if (txout.IsMine())
\r
1422 if (wtx.mapValue["to"].empty())
\r
1424 // Offline transaction
\r
1426 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
1428 string strAddress = Hash160ToAddress(hash160);
\r
1429 strHTML += "<b>To:</b> ";
\r
1430 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1431 strHTML += mapAddressBook[strAddress] + " ";
\r
1432 strHTML += strAddress;
\r
1433 strHTML += "<br>";
\r
1437 strHTML += "<b>Debit:</b> " + FormatMoney(-txout.nValue) + "<br>";
\r
1442 // Payment to self
\r
1443 /// issue: can't tell which is the payment and which is the change anymore
\r
1444 //int64 nValue = wtx.vout[0].nValue;
\r
1445 //strHTML += "<b>Debit:</b> " + FormatMoney(-nValue) + "<br>";
\r
1446 //strHTML += "<b>Credit:</b> " + FormatMoney(nValue) + "<br>";
\r
1449 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
1451 strHTML += "<b>Transaction fee:</b> " + FormatMoney(-nTxFee) + "<br>";
\r
1456 // Mixed debit transaction
\r
1458 foreach(const CTxIn& txin, wtx.vin)
\r
1459 if (txin.IsMine())
\r
1460 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1461 foreach(const CTxOut& txout, wtx.vout)
\r
1462 if (txout.IsMine())
\r
1463 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1467 strHTML += "<b>Net amount:</b> " + FormatMoney(nNet, true) + "<br>";
\r
1473 if (!wtx.mapValue["message"].empty())
\r
1474 strHTML += "<br><b>Message:</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
\r
1476 if (wtx.IsCoinBase())
\r
1477 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
1485 strHTML += "<hr><br>debug print<br><br>";
\r
1486 foreach(const CTxIn& txin, wtx.vin)
\r
1487 if (txin.IsMine())
\r
1488 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1489 foreach(const CTxOut& txout, wtx.vout)
\r
1490 if (txout.IsMine())
\r
1491 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1493 strHTML += "<b>Inputs:</b><br>";
\r
1494 CRITICAL_BLOCK(cs_mapWallet)
\r
1496 foreach(const CTxIn& txin, wtx.vin)
\r
1498 COutPoint prevout = txin.prevout;
\r
1499 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
\r
1500 if (mi != mapWallet.end())
\r
1502 const CWalletTx& prev = (*mi).second;
\r
1503 if (prevout.n < prev.vout.size())
\r
1505 strHTML += HtmlEscape(prev.ToString(), true);
\r
1506 strHTML += " " + FormatTxStatus(prev) + ", ";
\r
1507 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
\r
1513 strHTML += "<br><hr><br><b>Transaction:</b><br>";
\r
1514 strHTML += HtmlEscape(wtx.ToString(), true);
\r
1519 strHTML += "</font></html>";
\r
1520 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
1521 m_htmlWin->SetPage(strHTML);
\r
1522 m_buttonOK->SetFocus();
\r
1525 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
\r
1535 //////////////////////////////////////////////////////////////////////////////
\r
1540 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
\r
1542 // Set up list box of page choices
\r
1543 m_listBox->Append("Main");
\r
1544 //m_listBox->Append("Test 2");
\r
1545 m_listBox->SetSelection(0);
\r
1548 m_checkBoxMinimizeOnClose->SetLabel("&Minimize on close");
\r
1549 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
\r
1553 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
\r
1554 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
\r
1555 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
\r
1556 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
\r
1557 int nProcessors = wxThread::GetCPUCount();
\r
1558 if (nProcessors < 1)
\r
1559 nProcessors = 999;
\r
1560 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
\r
1561 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
\r
1562 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
\r
1563 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
\r
1564 m_checkBoxUseProxy->SetValue(fUseProxy);
\r
1565 m_textCtrlProxyIP->Enable(fUseProxy);
\r
1566 m_textCtrlProxyPort->Enable(fUseProxy);
\r
1567 m_staticTextProxyIP->Enable(fUseProxy);
\r
1568 m_staticTextProxyPort->Enable(fUseProxy);
\r
1569 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
\r
1570 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
\r
1572 m_buttonOK->SetFocus();
\r
1575 void COptionsDialog::SelectPage(int nPage)
\r
1577 m_panelMain->Show(nPage == 0);
\r
1578 m_panelTest2->Show(nPage == 1);
\r
1580 m_scrolledWindow->Layout();
\r
1581 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
\r
1584 void COptionsDialog::OnListBox(wxCommandEvent& event)
\r
1586 SelectPage(event.GetSelection());
\r
1589 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
\r
1591 int64 nTmp = nTransactionFee;
\r
1592 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
\r
1593 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
\r
1596 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
\r
1598 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
\r
1601 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
\r
1603 m_textCtrlProxyIP->Enable(event.IsChecked());
\r
1604 m_textCtrlProxyPort->Enable(event.IsChecked());
\r
1605 m_staticTextProxyIP->Enable(event.IsChecked());
\r
1606 m_staticTextProxyPort->Enable(event.IsChecked());
\r
1609 CAddress COptionsDialog::GetProxyAddr()
\r
1611 // Be careful about byte order, addr.ip and addr.port are big endian
\r
1612 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
\r
1613 if (addr.ip == INADDR_NONE)
\r
1614 addr.ip = addrProxy.ip;
\r
1615 int nPort = atoi(m_textCtrlProxyPort->GetValue());
\r
1616 addr.port = htons(nPort);
\r
1617 if (nPort <= 0 || nPort > USHRT_MAX)
\r
1618 addr.port = addrProxy.port;
\r
1622 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
\r
1624 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
\r
1625 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
\r
1629 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
\r
1631 OnButtonApply(event);
\r
1635 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
\r
1640 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
\r
1642 CWalletDB walletdb;
\r
1644 int64 nPrevTransactionFee = nTransactionFee;
\r
1645 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
\r
1646 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
\r
1648 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
\r
1649 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
\r
1651 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
\r
1652 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
\r
1654 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
\r
1656 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
\r
1657 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
\r
1659 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
\r
1660 GenerateBitcoins(fGenerateBitcoins);
\r
1662 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
\r
1664 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
\r
1665 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
\r
1668 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
\r
1670 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
\r
1671 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
\r
1672 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
1675 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
\r
1677 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
\r
1678 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
\r
1681 fUseProxy = m_checkBoxUseProxy->GetValue();
\r
1682 walletdb.WriteSetting("fUseProxy", fUseProxy);
\r
1684 addrProxy = GetProxyAddr();
\r
1685 walletdb.WriteSetting("addrProxy", addrProxy);
\r
1693 //////////////////////////////////////////////////////////////////////////////
\r
1698 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
\r
1700 m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d beta", VERSION/100, VERSION%100));
\r
1702 // Workaround until upgrade to wxWidgets supporting UTF-8
\r
1703 wxString str = m_staticTextMain->GetLabel();
\r
1704 #if !wxUSE_UNICODE
\r
1705 if (str.Find('Â') != wxNOT_FOUND)
\r
1706 str.Remove(str.Find('Â'), 1);
\r
1709 SetSize(510, 380);
\r
1711 m_staticTextMain->SetLabel(str);
\r
1714 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
\r
1724 //////////////////////////////////////////////////////////////////////////////
\r
1729 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
\r
1732 m_textCtrlAddress->SetValue(strAddress);
\r
1733 m_choiceTransferType->SetSelection(0);
\r
1734 m_bitmapCheckMark->Show(false);
\r
1735 fEnabledPrev = true;
\r
1736 m_textCtrlAddress->SetFocus();
\r
1737 //// todo: should add a display of your balance for convenience
\r
1739 wxFont fontTmp = m_staticTextInstructions->GetFont();
\r
1740 if (fontTmp.GetPointSize() > 9);
\r
1741 fontTmp.SetPointSize(9);
\r
1742 m_staticTextInstructions->SetFont(fontTmp);
\r
1743 SetSize(725, 380);
\r
1748 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
\r
1749 SetIcon(iconSend);
\r
1751 wxCommandEvent event;
\r
1752 OnTextAddress(event);
\r
1754 // Fixup the tab order
\r
1755 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
\r
1756 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
\r
1760 void CSendDialog::OnTextAddress(wxCommandEvent& event)
\r
1763 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
\r
1764 m_bitmapCheckMark->Show(fBitcoinAddress);
\r
1766 // Grey out message if bitcoin address
\r
1767 bool fEnable = !fBitcoinAddress;
\r
1768 m_staticTextFrom->Enable(fEnable);
\r
1769 m_textCtrlFrom->Enable(fEnable);
\r
1770 m_staticTextMessage->Enable(fEnable);
\r
1771 m_textCtrlMessage->Enable(fEnable);
\r
1772 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
\r
1773 if (!fEnable && fEnabledPrev)
\r
1775 strFromSave = m_textCtrlFrom->GetValue();
\r
1776 strMessageSave = m_textCtrlMessage->GetValue();
\r
1777 m_textCtrlFrom->SetValue("Will appear as \"From: Unknown\"");
\r
1778 m_textCtrlMessage->SetValue("Can't include a message when sending to a Bitcoin address");
\r
1780 else if (fEnable && !fEnabledPrev)
\r
1782 m_textCtrlFrom->SetValue(strFromSave);
\r
1783 m_textCtrlMessage->SetValue(strMessageSave);
\r
1785 fEnabledPrev = fEnable;
\r
1788 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
\r
1790 // Reformat the amount
\r
1791 if (m_textCtrlAmount->GetValue().Trim().empty())
\r
1794 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
\r
1795 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
\r
1798 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
\r
1800 // Open address book
\r
1801 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), true);
\r
1802 if (dialog.ShowModal())
\r
1803 m_textCtrlAddress->SetValue(dialog.GetAddress());
\r
1806 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
\r
1808 // Copy clipboard to address box
\r
1809 if (wxTheClipboard->Open())
\r
1811 if (wxTheClipboard->IsSupported(wxDF_TEXT))
\r
1813 wxTextDataObject data;
\r
1814 wxTheClipboard->GetData(data);
\r
1815 m_textCtrlAddress->SetValue(data.GetText());
\r
1817 wxTheClipboard->Close();
\r
1821 void CSendDialog::OnButtonSend(wxCommandEvent& event)
\r
1824 string strAddress = (string)m_textCtrlAddress->GetValue();
\r
1828 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
\r
1830 wxMessageBox("Error in amount ", "Send Coins");
\r
1833 if (nValue > GetBalance())
\r
1835 wxMessageBox("Amount exceeds your balance ", "Send Coins");
\r
1838 if (nValue + nTransactionFee > GetBalance())
\r
1840 wxMessageBox(string("Total exceeds your balance when the ") + FormatMoney(nTransactionFee) + " transaction fee is included ", "Send Coins");
\r
1844 // Parse bitcoin address
\r
1846 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
\r
1848 if (fBitcoinAddress)
\r
1850 // Send to bitcoin address
\r
1851 CScript scriptPubKey;
\r
1852 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
\r
1854 if (!SendMoney(scriptPubKey, nValue, wtx))
\r
1857 wxMessageBox("Payment sent ", "Sending...");
\r
1861 // Parse IP address
\r
1862 CAddress addr(strAddress);
\r
1863 if (!addr.IsValid())
\r
1865 wxMessageBox("Invalid address ", "Send Coins");
\r
1870 wtx.mapValue["to"] = strAddress;
\r
1871 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
\r
1872 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
\r
1874 // Send to IP address
\r
1875 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
\r
1876 if (!pdialog->ShowModal())
\r
1880 if (!mapAddressBook.count(strAddress))
\r
1881 SetAddressBookName(strAddress, "");
\r
1886 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
\r
1897 //////////////////////////////////////////////////////////////////////////////
\r
1902 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
1905 nPrice = nPriceIn;
\r
1907 start = wxDateTime::UNow();
\r
1908 memset(pszStatus, 0, sizeof(pszStatus));
\r
1909 fCanCancel = true;
\r
1913 fWorkDone = false;
\r
1915 SetSize(1.2 * GetSize().GetWidth(), 1.05 * GetSize().GetHeight());
\r
1918 SetTitle(strprintf("Sending %s to %s", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
\r
1919 m_textCtrlStatus->SetValue("");
\r
1921 CreateThread(SendingDialogStartTransfer, this);
\r
1924 CSendingDialog::~CSendingDialog()
\r
1926 printf("~CSendingDialog()\n");
\r
1929 void CSendingDialog::Close()
\r
1931 // Last one out turn out the lights.
\r
1932 // fWorkDone signals that work side is done and UI thread should call destroy.
\r
1933 // fUIDone signals that UI window has closed and work thread should call destroy.
\r
1934 // This allows the window to disappear and end modality when cancelled
\r
1935 // without making the user wait for ConnectNode to return. The dialog object
\r
1936 // hangs around in the background until the work thread exits.
\r
1938 EndModal(fSuccess);
\r
1947 void CSendingDialog::OnClose(wxCloseEvent& event)
\r
1949 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
\r
1956 wxCommandEvent cmdevent;
\r
1957 OnButtonCancel(cmdevent);
\r
1961 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
\r
1967 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
\r
1973 void CSendingDialog::OnPaint(wxPaintEvent& event)
\r
1975 if (strlen(pszStatus) > 130)
\r
1976 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
\r
1978 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
\r
1979 m_staticTextSending->SetFocus();
\r
1981 m_buttonCancel->Enable(false);
\r
1984 m_buttonOK->Enable(true);
\r
1985 m_buttonOK->SetFocus();
\r
1986 m_buttonCancel->Enable(false);
\r
1988 if (fAbort && fCanCancel && IsShown())
\r
1990 strcpy(pszStatus, "CANCELLED");
\r
1991 m_buttonOK->Enable(true);
\r
1992 m_buttonOK->SetFocus();
\r
1993 m_buttonCancel->Enable(false);
\r
1994 m_buttonCancel->SetLabel("Cancelled");
\r
1996 wxMessageBox("Transfer cancelled ", "Sending...", wxOK, this);
\r
2003 // Everything from here on is not in the UI thread and must only communicate
\r
2004 // with the rest of the dialog through variables and calling repaint.
\r
2007 void CSendingDialog::Repaint()
\r
2010 wxPaintEvent event;
\r
2011 GetEventHandler()->AddPendingEvent(event);
\r
2014 bool CSendingDialog::Status()
\r
2021 if (fAbort && fCanCancel)
\r
2023 memset(pszStatus, 0, 10);
\r
2024 strcpy(pszStatus, "CANCELLED");
\r
2032 bool CSendingDialog::Status(const string& str)
\r
2037 // This can be read by the UI thread at any time,
\r
2038 // so copy in a way that can be read cleanly at all times.
\r
2039 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
\r
2040 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
\r
2046 bool CSendingDialog::Error(const string& str)
\r
2048 fCanCancel = false;
\r
2050 Status(string("Error: ") + str);
\r
2054 void SendingDialogStartTransfer(void* parg)
\r
2056 ((CSendingDialog*)parg)->StartTransfer();
\r
2059 void CSendingDialog::StartTransfer()
\r
2061 // Make sure we have enough money
\r
2062 if (nPrice + nTransactionFee > GetBalance())
\r
2064 Error("You don't have enough money");
\r
2068 // We may have connected already for product details
\r
2069 if (!Status("Connecting..."))
\r
2071 CNode* pnode = ConnectNode(addr, 15 * 60);
\r
2074 Error("Unable to connect");
\r
2078 // Send order to seller, with response going to OnReply2 via event handler
\r
2079 if (!Status("Requesting public key..."))
\r
2081 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
\r
2084 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
\r
2086 ((CSendingDialog*)parg)->OnReply2(vRecv);
\r
2089 void CSendingDialog::OnReply2(CDataStream& vRecv)
\r
2091 if (!Status("Received public key..."))
\r
2094 CScript scriptPubKey;
\r
2101 string strMessage;
\r
2102 vRecv >> strMessage;
\r
2103 Error("Transfer was not accepted");
\r
2104 //// todo: enlarge the window and enable a hidden white box to put seller's message
\r
2107 vRecv >> scriptPubKey;
\r
2111 //// what do we want to do about this?
\r
2112 Error("Invalid response received");
\r
2116 // Pause to give the user a chance to cancel
\r
2117 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
\r
2124 CRITICAL_BLOCK(cs_main)
\r
2127 if (!Status("Creating transaction..."))
\r
2129 if (nPrice + nTransactionFee > GetBalance())
\r
2131 Error("You don't have enough money");
\r
2135 int64 nFeeRequired;
\r
2136 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
\r
2138 if (nPrice + nFeeRequired > GetBalance())
\r
2139 Error(strprintf("This is an oversized transaction that requires a transaction fee of %s", FormatMoney(nFeeRequired).c_str()));
\r
2141 Error("Transaction creation failed");
\r
2145 // Make sure we're still connected
\r
2146 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
\r
2149 Error("Lost connection, transaction cancelled");
\r
2153 // Last chance to cancel
\r
2157 fCanCancel = false;
\r
2160 fCanCancel = true;
\r
2163 fCanCancel = false;
\r
2165 if (!Status("Sending payment..."))
\r
2169 if (!CommitTransactionSpent(wtx, key))
\r
2171 Error("Error finalizing payment");
\r
2175 // Send payment tx to seller, with response going to OnReply3 via event handler
\r
2176 pnode->PushRequest("submitorder", wtx, SendingDialogOnReply3, this);
\r
2178 // Accept and broadcast transaction
\r
2179 if (!wtx.AcceptTransaction())
\r
2180 printf("ERROR: CSendingDialog : wtxNew.AcceptTransaction() %s failed\n", wtx.GetHash().ToString().c_str());
\r
2181 wtx.RelayWalletTransaction();
\r
2183 Status("Waiting for confirmation...");
\r
2184 MainFrameRepaint();
\r
2188 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
\r
2190 ((CSendingDialog*)parg)->OnReply3(vRecv);
\r
2193 void CSendingDialog::OnReply3(CDataStream& vRecv)
\r
2201 Error("The payment was sent, but the recipient was unable to verify it.\n"
\r
2202 "The transaction is recorded and will credit to the recipient,\n"
\r
2203 "but the comment information will be blank.");
\r
2209 //// what do we want to do about this?
\r
2210 Error("Payment was sent, but an invalid response was received");
\r
2216 Status("Payment completed");
\r
2224 //////////////////////////////////////////////////////////////////////////////
\r
2226 // CYourAddressDialog
\r
2229 CYourAddressDialog::CYourAddressDialog(wxWindow* parent, const string& strInitSelected) : CYourAddressDialogBase(parent)
\r
2231 // Init column headers
\r
2232 m_listCtrl->InsertColumn(0, "Label", wxLIST_FORMAT_LEFT, 200);
\r
2233 m_listCtrl->InsertColumn(1, "Bitcoin Address", wxLIST_FORMAT_LEFT, 350);
\r
2234 m_listCtrl->SetFocus();
\r
2236 // Fill listctrl with address book data
\r
2237 CRITICAL_BLOCK(cs_mapKeys)
\r
2239 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2241 string strAddress = item.first;
\r
2242 string strName = item.second;
\r
2244 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2247 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2248 if (strAddress == strInitSelected)
\r
2249 m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2255 wxString CYourAddressDialog::GetAddress()
\r
2257 int nIndex = GetSelection(m_listCtrl);
\r
2260 return GetItemText(m_listCtrl, nIndex, 1);
\r
2263 void CYourAddressDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2265 // Update address book with edited name
\r
2266 if (event.IsEditCancelled())
\r
2268 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2269 SetAddressBookName(strAddress, string(event.GetText()));
\r
2270 pframeMain->RefreshListCtrl();
\r
2273 void CYourAddressDialog::OnListItemSelected(wxListEvent& event)
\r
2277 void CYourAddressDialog::OnListItemActivated(wxListEvent& event)
\r
2279 // Doubleclick edits item
\r
2280 wxCommandEvent event2;
\r
2281 OnButtonRename(event2);
\r
2284 void CYourAddressDialog::OnButtonRename(wxCommandEvent& event)
\r
2287 int nIndex = GetSelection(m_listCtrl);
\r
2290 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2291 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2292 CGetTextFromUserDialog dialog(this, "Edit Address Label", "New Label", strName);
\r
2293 if (!dialog.ShowModal())
\r
2295 strName = dialog.GetValue();
\r
2298 SetAddressBookName(strAddress, strName);
\r
2299 m_listCtrl->SetItemText(nIndex, strName);
\r
2300 pframeMain->RefreshListCtrl();
\r
2303 void CYourAddressDialog::OnButtonNew(wxCommandEvent& event)
\r
2306 CGetTextFromUserDialog dialog(this, "New Bitcoin Address", "Label", "");
\r
2307 if (!dialog.ShowModal())
\r
2309 string strName = dialog.GetValue();
\r
2311 // Generate new key
\r
2312 string strAddress = PubKeyToAddress(GenerateNewKey());
\r
2313 SetAddressBookName(strAddress, strName);
\r
2315 // Add to list and select it
\r
2316 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2317 SetSelection(m_listCtrl, nIndex);
\r
2318 m_listCtrl->SetFocus();
\r
2321 void CYourAddressDialog::OnButtonCopy(wxCommandEvent& event)
\r
2323 // Copy address box to clipboard
\r
2324 if (wxTheClipboard->Open())
\r
2326 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
\r
2327 wxTheClipboard->Close();
\r
2331 void CYourAddressDialog::OnButtonOK(wxCommandEvent& event)
\r
2337 void CYourAddressDialog::OnButtonCancel(wxCommandEvent& event)
\r
2343 void CYourAddressDialog::OnClose(wxCloseEvent& event)
\r
2354 //////////////////////////////////////////////////////////////////////////////
\r
2356 // CAddressBookDialog
\r
2359 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, bool fSendingIn) : CAddressBookDialogBase(parent)
\r
2361 fSending = fSendingIn;
\r
2363 m_buttonCancel->Show(false);
\r
2365 // Init column headers
\r
2366 m_listCtrl->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 200);
\r
2367 m_listCtrl->InsertColumn(1, "Address", wxLIST_FORMAT_LEFT, 350);
\r
2368 m_listCtrl->SetFocus();
\r
2371 wxIcon iconAddressBook;
\r
2372 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
\r
2373 SetIcon(iconAddressBook);
\r
2375 // Fill listctrl with address book data
\r
2376 CRITICAL_BLOCK(cs_mapKeys)
\r
2378 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2380 string strAddress = item.first;
\r
2381 string strName = item.second;
\r
2383 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2386 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2387 if (strAddress == strInitSelected)
\r
2388 m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2394 wxString CAddressBookDialog::GetAddress()
\r
2396 int nIndex = GetSelection(m_listCtrl);
\r
2399 return GetItemText(m_listCtrl, nIndex, 1);
\r
2402 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2404 // Update address book with edited name
\r
2405 if (event.IsEditCancelled())
\r
2407 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2408 SetAddressBookName(strAddress, string(event.GetText()));
\r
2409 pframeMain->RefreshListCtrl();
\r
2412 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
\r
2416 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
\r
2420 // Doubleclick returns selection
\r
2421 EndModal(GetAddress() != "" ? 2 : 0);
\r
2425 // Doubleclick edits item
\r
2426 wxCommandEvent event2;
\r
2427 OnButtonEdit(event2);
\r
2431 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
\r
2434 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2436 wxMessageBox("This is one of your own addresses for receiving payments and cannot be entered in the address book. ", strTitle);
\r
2440 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
\r
2443 int nIndex = GetSelection(m_listCtrl);
\r
2446 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2447 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2448 string strAddressOrg = strAddress;
\r
2451 CGetTextFromUserDialog dialog(this, "Edit Address", "Name", strName, "Address", strAddress);
\r
2452 if (!dialog.ShowModal())
\r
2454 strName = dialog.GetValue1();
\r
2455 strAddress = dialog.GetValue2();
\r
2457 while (CheckIfMine(strAddress, "Edit Address"));
\r
2460 if (strAddress != strAddressOrg)
\r
2461 CWalletDB().EraseName(strAddressOrg);
\r
2462 SetAddressBookName(strAddress, strName);
\r
2463 m_listCtrl->SetItem(nIndex, 1, strAddress);
\r
2464 m_listCtrl->SetItemText(nIndex, strName);
\r
2465 pframeMain->RefreshListCtrl();
\r
2468 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
\r
2472 string strAddress;
\r
2475 CGetTextFromUserDialog dialog(this, "New Address", "Name", strName, "Address", strAddress);
\r
2476 if (!dialog.ShowModal())
\r
2478 strName = dialog.GetValue1();
\r
2479 strAddress = dialog.GetValue2();
\r
2481 while (CheckIfMine(strAddress, "New Address"));
\r
2483 // Add to list and select it
\r
2484 SetAddressBookName(strAddress, strName);
\r
2485 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2486 SetSelection(m_listCtrl, nIndex);
\r
2487 m_listCtrl->SetFocus();
\r
2488 pframeMain->RefreshListCtrl();
\r
2491 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
\r
2493 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
\r
2495 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
\r
2497 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2498 CWalletDB().EraseName(strAddress);
\r
2499 m_listCtrl->DeleteItem(nIndex);
\r
2502 pframeMain->RefreshListCtrl();
\r
2505 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
\r
2507 // Copy address box to clipboard
\r
2508 if (wxTheClipboard->Open())
\r
2510 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
\r
2511 wxTheClipboard->Close();
\r
2515 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
\r
2518 EndModal(GetAddress() != "" ? 1 : 0);
\r
2521 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
\r
2527 void CAddressBookDialog::OnClose(wxCloseEvent& event)
\r
2538 //////////////////////////////////////////////////////////////////////////////
\r
2540 // CProductsDialog
\r
2543 bool CompareIntStringPairBestFirst(const pair<int, string>& item1, const pair<int, string>& item2)
\r
2545 return (item1.first > item2.first);
\r
2548 CProductsDialog::CProductsDialog(wxWindow* parent) : CProductsDialogBase(parent)
\r
2550 // Init column headers
\r
2551 m_listCtrl->InsertColumn(0, "Title", wxLIST_FORMAT_LEFT, 200);
\r
2552 m_listCtrl->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 80);
\r
2553 m_listCtrl->InsertColumn(2, "Seller", wxLIST_FORMAT_LEFT, 80);
\r
2554 m_listCtrl->InsertColumn(3, "Stars", wxLIST_FORMAT_LEFT, 50);
\r
2555 m_listCtrl->InsertColumn(4, "Power", wxLIST_FORMAT_LEFT, 50);
\r
2557 // Tally top categories
\r
2558 map<string, int> mapTopCategories;
\r
2559 CRITICAL_BLOCK(cs_mapProducts)
\r
2560 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)
\r
2561 mapTopCategories[(*mi).second.mapValue["category"]]++;
\r
2563 // Sort top categories
\r
2564 vector<pair<int, string> > vTopCategories;
\r
2565 for (map<string, int>::iterator mi = mapTopCategories.begin(); mi != mapTopCategories.end(); ++mi)
\r
2566 vTopCategories.push_back(make_pair((*mi).second, (*mi).first));
\r
2567 sort(vTopCategories.begin(), vTopCategories.end(), CompareIntStringPairBestFirst);
\r
2569 // Fill categories combo box
\r
2571 for (vector<pair<int, string> >::iterator it = vTopCategories.begin(); it != vTopCategories.end() && nLimit-- > 0; ++it)
\r
2572 m_comboBoxCategory->Append((*it).second);
\r
2574 // Fill window with initial search
\r
2575 //wxCommandEvent event;
\r
2576 //OnButtonSearch(event);
\r
2579 void CProductsDialog::OnCombobox(wxCommandEvent& event)
\r
2581 OnButtonSearch(event);
\r
2584 bool CompareProductsBestFirst(const CProduct* p1, const CProduct* p2)
\r
2586 return (p1->nAtoms > p2->nAtoms);
\r
2589 void CProductsDialog::OnButtonSearch(wxCommandEvent& event)
\r
2591 string strCategory = (string)m_comboBoxCategory->GetValue();
\r
2592 string strSearch = (string)m_textCtrlSearch->GetValue();
\r
2594 // Search products
\r
2595 vector<CProduct*> vProductsFound;
\r
2596 CRITICAL_BLOCK(cs_mapProducts)
\r
2598 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi)
\r
2600 CProduct& product = (*mi).second;
\r
2601 if (product.mapValue["category"].find(strCategory) != -1)
\r
2603 if (product.mapValue["title"].find(strSearch) != -1 ||
\r
2604 product.mapValue["description"].find(strSearch) != -1 ||
\r
2605 product.mapValue["seller"].find(strSearch) != -1)
\r
2607 vProductsFound.push_back(&product);
\r
2614 sort(vProductsFound.begin(), vProductsFound.end(), CompareProductsBestFirst);
\r
2617 foreach(CProduct* pproduct, vProductsFound)
\r
2619 InsertLine(m_listCtrl,
\r
2620 pproduct->mapValue["title"],
\r
2621 pproduct->mapValue["price"],
\r
2622 pproduct->mapValue["seller"],
\r
2623 pproduct->mapValue["stars"],
\r
2624 itostr(pproduct->nAtoms));
\r
2628 void CProductsDialog::OnListItemActivated(wxListEvent& event)
\r
2630 // Doubleclick opens product
\r
2631 CViewProductDialog* pdialog = new CViewProductDialog(this, m_vProduct[event.GetIndex()]);
\r
2641 //////////////////////////////////////////////////////////////////////////////
\r
2643 // CEditProductDialog
\r
2646 CEditProductDialog::CEditProductDialog(wxWindow* parent) : CEditProductDialogBase(parent)
\r
2648 m_textCtrlLabel[0 ] = m_textCtrlLabel0;
\r
2649 m_textCtrlLabel[1 ] = m_textCtrlLabel1;
\r
2650 m_textCtrlLabel[2 ] = m_textCtrlLabel2;
\r
2651 m_textCtrlLabel[3 ] = m_textCtrlLabel3;
\r
2652 m_textCtrlLabel[4 ] = m_textCtrlLabel4;
\r
2653 m_textCtrlLabel[5 ] = m_textCtrlLabel5;
\r
2654 m_textCtrlLabel[6 ] = m_textCtrlLabel6;
\r
2655 m_textCtrlLabel[7 ] = m_textCtrlLabel7;
\r
2656 m_textCtrlLabel[8 ] = m_textCtrlLabel8;
\r
2657 m_textCtrlLabel[9 ] = m_textCtrlLabel9;
\r
2658 m_textCtrlLabel[10] = m_textCtrlLabel10;
\r
2659 m_textCtrlLabel[11] = m_textCtrlLabel11;
\r
2660 m_textCtrlLabel[12] = m_textCtrlLabel12;
\r
2661 m_textCtrlLabel[13] = m_textCtrlLabel13;
\r
2662 m_textCtrlLabel[14] = m_textCtrlLabel14;
\r
2663 m_textCtrlLabel[15] = m_textCtrlLabel15;
\r
2664 m_textCtrlLabel[16] = m_textCtrlLabel16;
\r
2665 m_textCtrlLabel[17] = m_textCtrlLabel17;
\r
2666 m_textCtrlLabel[18] = m_textCtrlLabel18;
\r
2667 m_textCtrlLabel[19] = m_textCtrlLabel19;
\r
2669 m_textCtrlField[0 ] = m_textCtrlField0;
\r
2670 m_textCtrlField[1 ] = m_textCtrlField1;
\r
2671 m_textCtrlField[2 ] = m_textCtrlField2;
\r
2672 m_textCtrlField[3 ] = m_textCtrlField3;
\r
2673 m_textCtrlField[4 ] = m_textCtrlField4;
\r
2674 m_textCtrlField[5 ] = m_textCtrlField5;
\r
2675 m_textCtrlField[6 ] = m_textCtrlField6;
\r
2676 m_textCtrlField[7 ] = m_textCtrlField7;
\r
2677 m_textCtrlField[8 ] = m_textCtrlField8;
\r
2678 m_textCtrlField[9 ] = m_textCtrlField9;
\r
2679 m_textCtrlField[10] = m_textCtrlField10;
\r
2680 m_textCtrlField[11] = m_textCtrlField11;
\r
2681 m_textCtrlField[12] = m_textCtrlField12;
\r
2682 m_textCtrlField[13] = m_textCtrlField13;
\r
2683 m_textCtrlField[14] = m_textCtrlField14;
\r
2684 m_textCtrlField[15] = m_textCtrlField15;
\r
2685 m_textCtrlField[16] = m_textCtrlField16;
\r
2686 m_textCtrlField[17] = m_textCtrlField17;
\r
2687 m_textCtrlField[18] = m_textCtrlField18;
\r
2688 m_textCtrlField[19] = m_textCtrlField19;
\r
2690 m_buttonDel[0 ] = m_buttonDel0;
\r
2691 m_buttonDel[1 ] = m_buttonDel1;
\r
2692 m_buttonDel[2 ] = m_buttonDel2;
\r
2693 m_buttonDel[3 ] = m_buttonDel3;
\r
2694 m_buttonDel[4 ] = m_buttonDel4;
\r
2695 m_buttonDel[5 ] = m_buttonDel5;
\r
2696 m_buttonDel[6 ] = m_buttonDel6;
\r
2697 m_buttonDel[7 ] = m_buttonDel7;
\r
2698 m_buttonDel[8 ] = m_buttonDel8;
\r
2699 m_buttonDel[9 ] = m_buttonDel9;
\r
2700 m_buttonDel[10] = m_buttonDel10;
\r
2701 m_buttonDel[11] = m_buttonDel11;
\r
2702 m_buttonDel[12] = m_buttonDel12;
\r
2703 m_buttonDel[13] = m_buttonDel13;
\r
2704 m_buttonDel[14] = m_buttonDel14;
\r
2705 m_buttonDel[15] = m_buttonDel15;
\r
2706 m_buttonDel[16] = m_buttonDel16;
\r
2707 m_buttonDel[17] = m_buttonDel17;
\r
2708 m_buttonDel[18] = m_buttonDel18;
\r
2709 m_buttonDel[19] = m_buttonDel19;
\r
2711 for (int i = 1; i < FIELDS_MAX; i++)
\r
2712 ShowLine(i, false);
\r
2717 void CEditProductDialog::LayoutAll()
\r
2719 m_scrolledWindow->Layout();
\r
2720 m_scrolledWindow->GetSizer()->Fit(m_scrolledWindow);
\r
2724 void CEditProductDialog::ShowLine(int i, bool fShow)
\r
2726 m_textCtrlLabel[i]->Show(fShow);
\r
2727 m_textCtrlField[i]->Show(fShow);
\r
2728 m_buttonDel[i]->Show(fShow);
\r
2731 void CEditProductDialog::OnButtonDel0(wxCommandEvent& event) { OnButtonDel(event, 0); }
\r
2732 void CEditProductDialog::OnButtonDel1(wxCommandEvent& event) { OnButtonDel(event, 1); }
\r
2733 void CEditProductDialog::OnButtonDel2(wxCommandEvent& event) { OnButtonDel(event, 2); }
\r
2734 void CEditProductDialog::OnButtonDel3(wxCommandEvent& event) { OnButtonDel(event, 3); }
\r
2735 void CEditProductDialog::OnButtonDel4(wxCommandEvent& event) { OnButtonDel(event, 4); }
\r
2736 void CEditProductDialog::OnButtonDel5(wxCommandEvent& event) { OnButtonDel(event, 5); }
\r
2737 void CEditProductDialog::OnButtonDel6(wxCommandEvent& event) { OnButtonDel(event, 6); }
\r
2738 void CEditProductDialog::OnButtonDel7(wxCommandEvent& event) { OnButtonDel(event, 7); }
\r
2739 void CEditProductDialog::OnButtonDel8(wxCommandEvent& event) { OnButtonDel(event, 8); }
\r
2740 void CEditProductDialog::OnButtonDel9(wxCommandEvent& event) { OnButtonDel(event, 9); }
\r
2741 void CEditProductDialog::OnButtonDel10(wxCommandEvent& event) { OnButtonDel(event, 10); }
\r
2742 void CEditProductDialog::OnButtonDel11(wxCommandEvent& event) { OnButtonDel(event, 11); }
\r
2743 void CEditProductDialog::OnButtonDel12(wxCommandEvent& event) { OnButtonDel(event, 12); }
\r
2744 void CEditProductDialog::OnButtonDel13(wxCommandEvent& event) { OnButtonDel(event, 13); }
\r
2745 void CEditProductDialog::OnButtonDel14(wxCommandEvent& event) { OnButtonDel(event, 14); }
\r
2746 void CEditProductDialog::OnButtonDel15(wxCommandEvent& event) { OnButtonDel(event, 15); }
\r
2747 void CEditProductDialog::OnButtonDel16(wxCommandEvent& event) { OnButtonDel(event, 16); }
\r
2748 void CEditProductDialog::OnButtonDel17(wxCommandEvent& event) { OnButtonDel(event, 17); }
\r
2749 void CEditProductDialog::OnButtonDel18(wxCommandEvent& event) { OnButtonDel(event, 18); }
\r
2750 void CEditProductDialog::OnButtonDel19(wxCommandEvent& event) { OnButtonDel(event, 19); }
\r
2752 void CEditProductDialog::OnButtonDel(wxCommandEvent& event, int n)
\r
2756 m_scrolledWindow->GetViewStart(&x, &y);
\r
2758 for (i = n; i < FIELDS_MAX-1; i++)
\r
2760 m_textCtrlLabel[i]->SetValue(m_textCtrlLabel[i+1]->GetValue());
\r
2761 m_textCtrlField[i]->SetValue(m_textCtrlField[i+1]->GetValue());
\r
2762 if (!m_buttonDel[i+1]->IsShown())
\r
2765 m_textCtrlLabel[i]->SetValue("");
\r
2766 m_textCtrlField[i]->SetValue("");
\r
2767 ShowLine(i, false);
\r
2768 m_buttonAddField->Enable(true);
\r
2770 m_scrolledWindow->Scroll(0, y);
\r
2774 void CEditProductDialog::OnButtonAddField(wxCommandEvent& event)
\r
2776 for (int i = 0; i < FIELDS_MAX; i++)
\r
2778 if (!m_buttonDel[i]->IsShown())
\r
2781 ShowLine(i, true);
\r
2782 if (i == FIELDS_MAX-1)
\r
2783 m_buttonAddField->Enable(false);
\r
2785 m_scrolledWindow->Scroll(0, 99999);
\r
2792 void CEditProductDialog::OnButtonSend(wxCommandEvent& event)
\r
2795 GetProduct(product);
\r
2797 // Sign the detailed product
\r
2798 product.vchPubKeyFrom = keyUser.GetPubKey();
\r
2799 if (!keyUser.Sign(product.GetSigHash(), product.vchSig))
\r
2801 wxMessageBox("Error digitally signing the product ");
\r
2805 // Save detailed product
\r
2806 AddToMyProducts(product);
\r
2808 // Strip down to summary product
\r
2809 product.mapDetails.clear();
\r
2810 product.vOrderForm.clear();
\r
2812 // Sign the summary product
\r
2813 if (!keyUser.Sign(product.GetSigHash(), product.vchSig))
\r
2815 wxMessageBox("Error digitally signing the product ");
\r
2820 if (!product.CheckProduct())
\r
2822 wxMessageBox("Errors found in product ");
\r
2827 AdvertStartPublish(pnodeLocalHost, MSG_PRODUCT, 0, product);
\r
2832 void CEditProductDialog::OnButtonPreview(wxCommandEvent& event)
\r
2835 GetProduct(product);
\r
2836 CViewProductDialog* pdialog = new CViewProductDialog(this, product);
\r
2840 void CEditProductDialog::OnButtonCancel(wxCommandEvent& event)
\r
2845 void CEditProductDialog::SetProduct(const CProduct& productIn)
\r
2847 CProduct product = productIn;
\r
2849 m_comboBoxCategory->SetValue(product.mapValue["category"]);
\r
2850 m_textCtrlTitle->SetValue(product.mapValue["title"]);
\r
2851 m_textCtrlPrice->SetValue(product.mapValue["price"]);
\r
2852 m_textCtrlDescription->SetValue(product.mapValue["description"]);
\r
2853 m_textCtrlInstructions->SetValue(product.mapValue["instructions"]);
\r
2855 for (int i = 0; i < FIELDS_MAX; i++)
\r
2857 bool fUsed = i < product.vOrderForm.size();
\r
2858 m_buttonDel[i]->Show(fUsed);
\r
2859 m_textCtrlLabel[i]->Show(fUsed);
\r
2860 m_textCtrlField[i]->Show(fUsed);
\r
2864 m_textCtrlLabel[i]->SetValue(product.vOrderForm[i].first);
\r
2865 string strControl = product.vOrderForm[i].second;
\r
2866 if (strControl.substr(0, 5) == "text=")
\r
2867 m_textCtrlField[i]->SetValue("");
\r
2868 else if (strControl.substr(0, 7) == "choice=")
\r
2869 m_textCtrlField[i]->SetValue(strControl.substr(7));
\r
2871 m_textCtrlField[i]->SetValue(strControl);
\r
2875 void CEditProductDialog::GetProduct(CProduct& product)
\r
2877 // map<string, string> mapValue;
\r
2878 // vector<pair<string, string> > vOrderForm;
\r
2880 product.mapValue["category"] = m_comboBoxCategory->GetValue().Trim();
\r
2881 product.mapValue["title"] = m_textCtrlTitle->GetValue().Trim();
\r
2882 product.mapValue["price"] = m_textCtrlPrice->GetValue().Trim();
\r
2883 product.mapValue["description"] = m_textCtrlDescription->GetValue().Trim();
\r
2884 product.mapValue["instructions"] = m_textCtrlInstructions->GetValue().Trim();
\r
2886 for (int i = 0; i < FIELDS_MAX; i++)
\r
2888 if (m_buttonDel[i]->IsShown())
\r
2890 string strLabel = (string)m_textCtrlLabel[i]->GetValue().Trim();
\r
2891 string strControl = (string)m_textCtrlField[i]->GetValue();
\r
2892 if (strControl.empty())
\r
2893 strControl = "text=";
\r
2895 strControl = "choice=" + strControl;
\r
2896 product.vOrderForm.push_back(make_pair(strLabel, strControl));
\r
2907 //////////////////////////////////////////////////////////////////////////////
\r
2909 // CViewProductDialog
\r
2912 CViewProductDialog::CViewProductDialog(wxWindow* parent, const CProduct& productIn) : CViewProductDialogBase(parent)
\r
2914 Connect(wxEVT_REPLY1, wxCommandEventHandler(CViewProductDialog::OnReply1), NULL, this);
\r
2915 AddCallbackAvailable(GetEventHandler());
\r
2917 // Fill display with product summary while waiting for details
\r
2918 product = productIn;
\r
2919 UpdateProductDisplay(false);
\r
2921 m_buttonBack->Enable(false);
\r
2922 m_buttonNext->Enable(!product.vOrderForm.empty());
\r
2923 m_htmlWinReviews->Show(true);
\r
2924 m_scrolledWindow->Show(false);
\r
2927 // Request details from seller
\r
2928 CreateThread(ThreadRequestProductDetails, new pair<CProduct, wxEvtHandler*>(product, GetEventHandler()));
\r
2931 CViewProductDialog::~CViewProductDialog()
\r
2933 RemoveCallbackAvailable(GetEventHandler());
\r
2936 void ThreadRequestProductDetails(void* parg)
\r
2938 // Extract parameters
\r
2939 pair<CProduct, wxEvtHandler*>* pitem = (pair<CProduct, wxEvtHandler*>*)parg;
\r
2940 CProduct product = pitem->first;
\r
2941 wxEvtHandler* pevthandler = pitem->second;
\r
2944 // Connect to seller
\r
2945 CNode* pnode = ConnectNode(product.addr, 5 * 60);
\r
2948 CDataStream ssEmpty;
\r
2949 AddPendingReplyEvent1(pevthandler, ssEmpty);
\r
2953 // Request detailed product, with response going to OnReply1 via dialog's event handler
\r
2954 pnode->PushRequest("getdetails", product.GetHash(), AddPendingReplyEvent1, (void*)pevthandler);
\r
2957 void CViewProductDialog::OnReply1(wxCommandEvent& event)
\r
2959 CDataStream ss = GetStreamFromEvent(event);
\r
2962 product.mapValue["description"] = "-- CAN'T CONNECT TO SELLER --\n";
\r
2963 UpdateProductDisplay(true);
\r
2968 CProduct product2;
\r
2975 if (product2.GetHash() != product.GetHash())
\r
2977 if (!product2.CheckSignature())
\r
2982 product.mapValue["description"] = "-- INVALID RESPONSE --\n";
\r
2983 UpdateProductDisplay(true);
\r
2987 product = product2;
\r
2988 UpdateProductDisplay(true);
\r
2991 bool CompareReviewsBestFirst(const CReview* p1, const CReview* p2)
\r
2993 return (p1->nAtoms > p2->nAtoms);
\r
2996 void CViewProductDialog::UpdateProductDisplay(bool fDetails)
\r
2998 // Product and reviews
\r
3000 strHTML.reserve(4000);
\r
3001 strHTML += "<html>\n"
\r
3003 "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"
\r
3006 strHTML += "<b>Category:</b> " + HtmlEscape(product.mapValue["category"]) + "<br>\n";
\r
3007 strHTML += "<b>Title:</b> " + HtmlEscape(product.mapValue["title"]) + "<br>\n";
\r
3008 strHTML += "<b>Price:</b> " + HtmlEscape(product.mapValue["price"]) + "<br>\n";
\r
3011 strHTML += "<b>Loading details...</b><br>\n<br>\n";
\r
3013 strHTML += HtmlEscape(product.mapValue["description"], true) + "<br>\n<br>\n";
\r
3015 strHTML += "<b>Reviews:</b><br>\n<br>\n";
\r
3017 if (!product.vchPubKeyFrom.empty())
\r
3019 CReviewDB reviewdb("r");
\r
3022 vector<CReview> vReviews;
\r
3023 reviewdb.ReadReviews(product.GetUserHash(), vReviews);
\r
3025 // Get reviewer's number of atoms
\r
3026 vector<CReview*> vSortedReviews;
\r
3027 vSortedReviews.reserve(vReviews.size());
\r
3028 for (vector<CReview>::reverse_iterator it = vReviews.rbegin(); it != vReviews.rend(); ++it)
\r
3030 CReview& review = *it;
\r
3032 reviewdb.ReadUser(review.GetUserHash(), user);
\r
3033 review.nAtoms = user.GetAtomCount();
\r
3034 vSortedReviews.push_back(&review);
\r
3040 stable_sort(vSortedReviews.begin(), vSortedReviews.end(), CompareReviewsBestFirst);
\r
3043 foreach(CReview* preview, vSortedReviews)
\r
3045 CReview& review = *preview;
\r
3046 int nStars = atoi(review.mapValue["stars"].c_str());
\r
3047 if (nStars < 1 || nStars > 5)
\r
3050 strHTML += "<b>" + itostr(nStars) + (nStars == 1 ? " star" : " stars") + "</b>";
\r
3051 strHTML += " ";
\r
3052 strHTML += DateStr(atoi64(review.mapValue["date"])) + "<br>\n";
\r
3053 strHTML += HtmlEscape(review.mapValue["review"], true);
\r
3054 strHTML += "<br>\n<br>\n";
\r
3058 strHTML += "</body>\n</html>\n";
\r
3060 // Shrink capacity to fit
\r
3061 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
3063 m_htmlWinReviews->SetPage(strHTML);
\r
3065 ///// need to find some other indicator to use so can allow empty order form
\r
3066 if (product.vOrderForm.empty())
\r
3070 m_staticTextInstructions->SetLabel(product.mapValue["instructions"]);
\r
3071 for (int i = 0; i < FIELDS_MAX; i++)
\r
3073 m_staticTextLabel[i] = NULL;
\r
3074 m_textCtrlField[i] = NULL;
\r
3075 m_choiceField[i] = NULL;
\r
3078 // Construct flexgridsizer
\r
3079 wxBoxSizer* bSizer21 = (wxBoxSizer*)m_scrolledWindow->GetSizer();
\r
3080 wxFlexGridSizer* fgSizer;
\r
3081 fgSizer = new wxFlexGridSizer(0, 2, 0, 0);
\r
3082 fgSizer->AddGrowableCol(1);
\r
3083 fgSizer->SetFlexibleDirection(wxBOTH);
\r
3084 fgSizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
\r
3086 // Construct order form fields
\r
3087 wxWindow* windowLast = NULL;
\r
3088 for (int i = 0; i < product.vOrderForm.size(); i++)
\r
3090 string strLabel = product.vOrderForm[i].first;
\r
3091 string strControl = product.vOrderForm[i].second;
\r
3093 if (strLabel.size() < 20)
\r
3094 strLabel.insert(strLabel.begin(), 20 - strLabel.size(), ' ');
\r
3096 m_staticTextLabel[i] = new wxStaticText(m_scrolledWindow, wxID_ANY, strLabel, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
\r
3097 m_staticTextLabel[i]->Wrap(-1);
\r
3098 fgSizer->Add(m_staticTextLabel[i], 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5);
\r
3100 if (strControl.substr(0, 5) == "text=")
\r
3102 m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
\r
3103 fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);
\r
3104 windowLast = m_textCtrlField[i];
\r
3106 else if (strControl.substr(0, 7) == "choice=")
\r
3108 vector<string> vChoices;
\r
3109 ParseString(strControl.substr(7), ',', vChoices);
\r
3111 wxArrayString arraystring;
\r
3112 foreach(const string& str, vChoices)
\r
3113 arraystring.Add(str);
\r
3115 m_choiceField[i] = new wxChoice(m_scrolledWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize, arraystring, 0);
\r
3116 fgSizer->Add(m_choiceField[i], 0, wxALL|wxALIGN_CENTER_VERTICAL, 5);
\r
3117 windowLast = m_choiceField[i];
\r
3121 m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
\r
3122 fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5);
\r
3123 m_staticTextLabel[i]->Show(false);
\r
3124 m_textCtrlField[i]->Show(false);
\r
3128 // Insert after instructions and before submit/cancel buttons
\r
3129 bSizer21->Insert(2, fgSizer, 0, wxEXPAND|wxRIGHT|wxLEFT, 5);
\r
3130 m_scrolledWindow->Layout();
\r
3131 bSizer21->Fit(m_scrolledWindow);
\r
3134 // Fixup the tab order
\r
3135 m_buttonSubmitForm->MoveAfterInTabOrder(windowLast);
\r
3136 m_buttonCancelForm->MoveAfterInTabOrder(m_buttonSubmitForm);
\r
3137 //m_buttonBack->MoveAfterInTabOrder(m_buttonCancelForm);
\r
3138 //m_buttonNext->MoveAfterInTabOrder(m_buttonBack);
\r
3139 //m_buttonCancel->MoveAfterInTabOrder(m_buttonNext);
\r
3143 void CViewProductDialog::GetOrder(CWalletTx& wtx)
\r
3146 for (int i = 0; i < product.vOrderForm.size(); i++)
\r
3149 if (m_textCtrlField[i])
\r
3150 strValue = m_textCtrlField[i]->GetValue().Trim();
\r
3152 strValue = m_choiceField[i]->GetStringSelection();
\r
3153 wtx.vOrderForm.push_back(make_pair(m_staticTextLabel[i]->GetLabel(), strValue));
\r
3157 void CViewProductDialog::OnButtonSubmitForm(wxCommandEvent& event)
\r
3159 m_buttonSubmitForm->Enable(false);
\r
3160 m_buttonCancelForm->Enable(false);
\r
3165 CSendingDialog* pdialog = new CSendingDialog(this, product.addr, atoi64(product.mapValue["price"]), wtx);
\r
3166 if (!pdialog->ShowModal())
\r
3168 m_buttonSubmitForm->Enable(true);
\r
3169 m_buttonCancelForm->Enable(true);
\r
3174 void CViewProductDialog::OnButtonCancelForm(wxCommandEvent& event)
\r
3179 void CViewProductDialog::OnButtonBack(wxCommandEvent& event)
\r
3182 m_htmlWinReviews->Show(true);
\r
3183 m_scrolledWindow->Show(false);
\r
3184 m_buttonBack->Enable(false);
\r
3185 m_buttonNext->Enable(!product.vOrderForm.empty());
\r
3190 void CViewProductDialog::OnButtonNext(wxCommandEvent& event)
\r
3192 if (!product.vOrderForm.empty())
\r
3195 m_htmlWinReviews->Show(false);
\r
3196 m_scrolledWindow->Show(true);
\r
3197 m_buttonBack->Enable(true);
\r
3198 m_buttonNext->Enable(false);
\r
3204 void CViewProductDialog::OnButtonCancel(wxCommandEvent& event)
\r
3215 //////////////////////////////////////////////////////////////////////////////
\r
3217 // CViewOrderDialog
\r
3220 CViewOrderDialog::CViewOrderDialog(wxWindow* parent, CWalletTx order, bool fReceived) : CViewOrderDialogBase(parent)
\r
3222 int64 nPrice = (fReceived ? order.GetCredit() : order.GetDebit());
\r
3225 strHTML.reserve(4000);
\r
3226 strHTML += "<html>\n"
\r
3228 "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"
\r
3231 strHTML += "<b>Time:</b> " + HtmlEscape(DateTimeStr(order.nTimeReceived)) + "<br>\n";
\r
3232 strHTML += "<b>Price:</b> " + HtmlEscape(FormatMoney(nPrice)) + "<br>\n";
\r
3233 strHTML += "<b>Status:</b> " + HtmlEscape(FormatTxStatus(order)) + "<br>\n";
\r
3235 strHTML += "<table>\n";
\r
3236 for (int i = 0; i < order.vOrderForm.size(); i++)
\r
3238 strHTML += " <tr><td><b>" + HtmlEscape(order.vOrderForm[i].first) + ":</b></td>";
\r
3239 strHTML += "<td>" + HtmlEscape(order.vOrderForm[i].second) + "</td></tr>\n";
\r
3241 strHTML += "</table>\n";
\r
3243 strHTML += "</body>\n</html>\n";
\r
3245 // Shrink capacity to fit
\r
3246 // (strings are ref counted, so it may live on in SetPage)
\r
3247 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
3249 m_htmlWin->SetPage(strHTML);
\r
3252 void CViewOrderDialog::OnButtonOK(wxCommandEvent& event)
\r
3263 //////////////////////////////////////////////////////////////////////////////
\r
3265 // CEditReviewDialog
\r
3268 CEditReviewDialog::CEditReviewDialog(wxWindow* parent) : CEditReviewDialogBase(parent)
\r
3272 void CEditReviewDialog::OnButtonSubmit(wxCommandEvent& event)
\r
3274 if (m_choiceStars->GetSelection() == -1)
\r
3276 wxMessageBox("Please select a rating ");
\r
3281 GetReview(review);
\r
3283 // Sign the review
\r
3284 review.vchPubKeyFrom = keyUser.GetPubKey();
\r
3285 if (!keyUser.Sign(review.GetSigHash(), review.vchSig))
\r
3287 wxMessageBox("Unable to digitally sign the review ");
\r
3292 if (!review.AcceptReview())
\r
3294 wxMessageBox("Save failed ");
\r
3297 RelayMessage(CInv(MSG_REVIEW, review.GetHash()), review);
\r
3302 void CEditReviewDialog::OnButtonCancel(wxCommandEvent& event)
\r
3307 void CEditReviewDialog::GetReview(CReview& review)
\r
3309 review.mapValue["time"] = i64tostr(GetAdjustedTime());
\r
3310 review.mapValue["stars"] = itostr(m_choiceStars->GetSelection()+1);
\r
3311 review.mapValue["review"] = m_textCtrlReview->GetValue();
\r
3320 //////////////////////////////////////////////////////////////////////////////
\r
3327 ID_TASKBAR_RESTORE = 10001,
\r
3328 ID_TASKBAR_OPTIONS,
\r
3329 ID_TASKBAR_GENERATE,
\r
3333 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
\r
3334 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
\r
3335 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
\r
3336 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
\r
3337 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
\r
3338 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
\r
3339 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
\r
3342 void CMyTaskBarIcon::Show(bool fShow)
\r
3344 static char pszPrevTip[200];
\r
3347 string strTooltip = "Bitcoin";
\r
3348 if (fGenerateBitcoins)
\r
3349 strTooltip = "Bitcoin - Generating";
\r
3350 if (fGenerateBitcoins && vNodes.empty())
\r
3351 strTooltip = "Bitcoin - (not connected)";
\r
3353 // Optimization, only update when changed, using char array to be reentrant
\r
3354 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
\r
3356 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
\r
3358 SetIcon(wxICON(bitcoin), strTooltip);
\r
3360 SetIcon(bitcoin20_xpm, strTooltip);
\r
3366 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
\r
3371 void CMyTaskBarIcon::Hide()
\r
3376 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
\r
3381 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
\r
3386 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
\r
3388 // Since it's modal, get the main window to do it
\r
3389 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_MENUOPTIONSOPTIONS);
\r
3390 pframeMain->GetEventHandler()->AddPendingEvent(event2);
\r
3393 void CMyTaskBarIcon::Restore()
\r
3395 pframeMain->Show();
\r
3396 wxIconizeEvent event(0, false);
\r
3397 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
3398 pframeMain->Iconize(false);
\r
3399 pframeMain->Raise();
\r
3402 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
\r
3404 GenerateBitcoins(event.IsChecked());
\r
3407 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
\r
3409 event.Check(fGenerateBitcoins);
\r
3412 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
\r
3414 pframeMain->Close(true);
\r
3417 void CMyTaskBarIcon::UpdateTooltip()
\r
3419 if (IsIconInstalled())
\r
3423 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
\r
3425 wxMenu* pmenu = new wxMenu;
\r
3426 pmenu->Append(ID_TASKBAR_RESTORE, "&Open Bitcoin");
\r
3427 pmenu->Append(ID_TASKBAR_OPTIONS, "O&ptions...");
\r
3428 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, "&Generate Coins")->Check(fGenerateBitcoins);
\r
3429 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
\r
3430 pmenu->AppendSeparator();
\r
3431 pmenu->Append(ID_TASKBAR_EXIT, "E&xit");
\r
3445 //////////////////////////////////////////////////////////////////////////////
\r
3450 // Define a new application
\r
3451 class CMyApp: public wxApp
\r
3460 // 2nd-level exception handling: we get all the exceptions occurring in any
\r
3461 // event handler here
\r
3462 virtual bool OnExceptionInMainLoop();
\r
3464 // 3rd, and final, level exception handling: whenever an unhandled
\r
3465 // exception is caught, this function is called
\r
3466 virtual void OnUnhandledException();
\r
3468 // and now for something different: this function is called in case of a
\r
3469 // crash (e.g. dereferencing null pointer, division by 0, ...)
\r
3470 virtual void OnFatalException();
\r
3473 IMPLEMENT_APP(CMyApp)
\r
3475 bool CMyApp::OnInit()
\r
3477 bool fRet = false;
\r
3482 catch (std::exception& e) {
\r
3483 PrintException(&e, "OnInit()");
\r
3485 PrintException(NULL, "OnInit()");
\r
3492 bool CMyApp::OnInit2()
\r
3495 // Turn off microsoft heap dump noise for now
\r
3496 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
\r
3497 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
\r
3499 #if defined(__WXMSW__) && defined(__WXDEBUG__)
\r
3500 // Disable malfunctioning wxWidgets debug assertion
\r
3501 g_isPainting = 10000;
\r
3503 wxImage::AddHandler(new wxPNGHandler);
\r
3505 SetAppName("Bitcoin");
\r
3507 SetAppName("bitcoin");
\r
3514 ParseParameters(argc, argv);
\r
3515 if (mapArgs.count("-?") || mapArgs.count("--help"))
\r
3519 "Usage: bitcoin [options]\t\t\t\t\t\t\n"
\r
3521 " -gen\t\t Generate coins\n"
\r
3522 " -gen=0\t\t Don't generate coins\n"
\r
3523 " -min\t\t Start minimized\n"
\r
3524 " -datadir=<dir>\t Specify data directory\n"
\r
3525 " -proxy=<ip:port>\t Connect through socks4 proxy\n"
\r
3526 " -addnode=<ip>\t Add a node to connect to\n"
\r
3527 " -connect=<ip>\t Connect only to the specified node\n"
\r
3528 " -?\t\t This help message\n";
\r
3529 wxMessageBox(strUsage, "Bitcoin", wxOK);
\r
3532 "Usage: bitcoin [options]\n"
\r
3534 " -gen Generate coins\n"
\r
3535 " -gen=0 Don't generate coins\n"
\r
3536 " -min Start minimized\n"
\r
3537 " -datadir=<dir> Specify data directory\n"
\r
3538 " -proxy=<ip:port> Connect through socks4 proxy\n"
\r
3539 " -addnode=<ip> Add a node to connect to\n"
\r
3540 " -connect=<ip> Connect only to the specified node\n"
\r
3541 " -? This help message\n";
\r
3542 fprintf(stderr, "%s", strUsage.c_str());
\r
3547 if (mapArgs.count("-datadir"))
\r
3548 strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir));
\r
3550 if (mapArgs.count("-debug"))
\r
3553 if (mapArgs.count("-printtodebugger"))
\r
3554 fPrintToDebugger = true;
\r
3556 if (!fDebug && !pszSetDataDir[0])
\r
3557 ShrinkDebugFile();
\r
3558 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
\r
3559 printf("Bitcoin version %d%s, OS version %s\n", VERSION, pszSubVer, ((string)wxGetOsDescription()).c_str());
\r
3561 if (mapArgs.count("-loadblockindextest"))
\r
3564 txdb.LoadBlockIndex();
\r
3570 // Limit to single instance per user
\r
3571 // Required to protect the database files if we're going to keep deleting log.*
\r
3574 // todo: wxSingleInstanceChecker wasn't working on Linux, never deleted its lock file
\r
3575 // maybe should go by whether successfully bind port 8333 instead
\r
3576 wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH");
\r
3577 for (int i = 0; i < strMutexName.size(); i++)
\r
3578 if (!isalnum(strMutexName[i]))
\r
3579 strMutexName[i] = '.';
\r
3580 wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
\r
3581 if (psingleinstancechecker->IsAnotherRunning())
\r
3583 printf("Existing instance found\n");
\r
3584 unsigned int nStart = GetTime();
\r
3587 // TODO: find out how to do this in Linux, or replace with wxWidgets commands
\r
3588 // Show the previous instance and exit
\r
3589 HWND hwndPrev = FindWindow("wxWindowClassNR", "Bitcoin");
\r
3592 if (IsIconic(hwndPrev))
\r
3593 ShowWindow(hwndPrev, SW_RESTORE);
\r
3594 SetForegroundWindow(hwndPrev);
\r
3598 if (GetTime() > nStart + 60)
\r
3601 // Resume this instance if the other exits
\r
3602 delete psingleinstancechecker;
\r
3604 psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
\r
3605 if (!psingleinstancechecker->IsAnotherRunning())
\r
3611 // Bind to the port early so we can tell if another instance is already running.
\r
3612 // This is a backup to wxSingleInstanceChecker, which doesn't work on Linux.
\r
3614 if (!BindListenPort(strErrors))
\r
3616 wxMessageBox(strErrors, "Bitcoin");
\r
3621 // Load data files
\r
3627 printf("Loading addresses...\n");
\r
3628 nStart = GetTimeMillis();
\r
3629 if (!LoadAddresses())
\r
3630 strErrors += "Error loading addr.dat \n";
\r
3631 printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
3633 printf("Loading block index...\n");
\r
3634 nStart = GetTimeMillis();
\r
3635 if (!LoadBlockIndex())
\r
3636 strErrors += "Error loading blkindex.dat \n";
\r
3637 printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
3639 printf("Loading wallet...\n");
\r
3640 nStart = GetTimeMillis();
\r
3641 if (!LoadWallet(fFirstRun))
\r
3642 strErrors += "Error loading wallet.dat \n";
\r
3643 printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
3645 printf("Done loading\n");
\r
3648 printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size());
\r
3649 printf("nBestHeight = %d\n", nBestHeight);
\r
3650 printf("mapKeys.size() = %d\n", mapKeys.size());
\r
3651 printf("mapPubKeys.size() = %d\n", mapPubKeys.size());
\r
3652 printf("mapWallet.size() = %d\n", mapWallet.size());
\r
3653 printf("mapAddressBook.size() = %d\n", mapAddressBook.size());
\r
3655 if (!strErrors.empty())
\r
3657 wxMessageBox(strErrors, "Bitcoin");
\r
3661 // Add wallet transactions that aren't already in a block to mapTransactions
\r
3662 ReacceptWalletTransactions();
\r
3667 if (mapArgs.count("-printblockindex") || mapArgs.count("-printblocktree"))
\r
3673 if (mapArgs.count("-printblock"))
\r
3675 string strMatch = mapArgs["-printblock"];
\r
3677 for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
\r
3679 uint256 hash = (*mi).first;
\r
3680 if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0)
\r
3682 CBlockIndex* pindex = (*mi).second;
\r
3684 block.ReadFromDisk(pindex);
\r
3685 block.BuildMerkleTree();
\r
3692 printf("No blocks matching %s were found\n", strMatch.c_str());
\r
3696 if (mapArgs.count("-gen"))
\r
3698 if (mapArgs["-gen"].empty())
\r
3699 fGenerateBitcoins = true;
\r
3701 fGenerateBitcoins = (atoi(mapArgs["-gen"].c_str()) != 0);
\r
3704 if (mapArgs.count("-proxy"))
\r
3707 addrProxy = CAddress(mapArgs["-proxy"]);
\r
3708 if (!addrProxy.IsValid())
\r
3710 wxMessageBox("Invalid -proxy address", "Bitcoin");
\r
3715 if (mapArgs.count("-addnode"))
\r
3717 foreach(string strAddr, mapMultiArgs["-addnode"])
\r
3719 CAddress addr(strAddr, NODE_NETWORK);
\r
3720 addr.nTime = 0; // so it won't relay unless successfully connected
\r
3721 if (addr.IsValid())
\r
3727 // Create the main frame window
\r
3729 if (!mapArgs.count("-noui"))
\r
3731 pframeMain = new CMainFrame(NULL);
\r
3732 if (mapArgs.count("-min"))
\r
3733 pframeMain->Iconize(true);
\r
3734 pframeMain->Show(true); // have to show first to get taskbar button to hide
\r
3735 if (fMinimizeToTray && pframeMain->IsIconized())
\r
3736 fClosedToTray = true;
\r
3737 pframeMain->Show(!fClosedToTray);
\r
3738 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
3740 CreateThread(ThreadDelayedRepaint, NULL);
\r
3743 if (!CheckDiskSpace())
\r
3746 RandAddSeedPerfmon();
\r
3748 if (!CreateThread(StartNode, NULL))
\r
3749 wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin");
\r
3752 SetStartOnSystemStartup(true);
\r
3759 if (argc >= 2 && stricmp(argv[1], "-send") == 0)
\r
3761 if (argc >= 2 && strcmp(argv[1], "-send") == 0)
\r
3766 ParseMoney(argv[2], nValue);
\r
3768 string strAddress;
\r
3770 strAddress = argv[3];
\r
3771 CAddress addr(strAddress);
\r
3774 wtx.mapValue["to"] = strAddress;
\r
3775 wtx.mapValue["from"] = addrLocalHost.ToString();
\r
3776 wtx.mapValue["message"] = "command line send";
\r
3778 // Send to IP address
\r
3779 CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx);
\r
3780 if (!pdialog->ShowModal())
\r
3787 int CMyApp::OnExit()
\r
3790 return wxApp::OnExit();
\r
3793 bool CMyApp::OnExceptionInMainLoop()
\r
3799 catch (std::exception& e)
\r
3801 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
\r
3802 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
3808 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
\r
3809 wxLogWarning("Unknown exception");
\r
3817 void CMyApp::OnUnhandledException()
\r
3819 // this shows how we may let some exception propagate uncaught
\r
3824 catch (std::exception& e)
\r
3826 PrintException(&e, "CMyApp::OnUnhandledException()");
\r
3827 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
3833 PrintException(NULL, "CMyApp::OnUnhandledException()");
\r
3834 wxLogWarning("Unknown exception");
\r
3840 void CMyApp::OnFatalException()
\r
3842 wxMessageBox("Program has crashed and will terminate. ", "Bitcoin", wxOK | wxICON_ERROR);
\r
3850 typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
\r
3852 string MyGetSpecialFolderPath(int nFolder, bool fCreate)
\r
3854 char pszPath[MAX_PATH+100] = "";
\r
3856 // SHGetSpecialFolderPath is not usually available on NT 4.0
\r
3857 HMODULE hShell32 = LoadLibrary("shell32.dll");
\r
3860 PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath =
\r
3861 (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA");
\r
3862 if (pSHGetSpecialFolderPath)
\r
3863 (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate);
\r
3864 FreeModule(hShell32);
\r
3868 if (pszPath[0] == '\0')
\r
3870 if (nFolder == CSIDL_STARTUP)
\r
3872 strcpy(pszPath, getenv("USERPROFILE"));
\r
3873 strcat(pszPath, "\\Start Menu\\Programs\\Startup");
\r
3875 else if (nFolder == CSIDL_APPDATA)
\r
3877 strcpy(pszPath, getenv("APPDATA"));
\r
3884 string StartupShortcutPath()
\r
3886 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
\r
3889 bool GetStartOnSystemStartup()
\r
3891 return wxFileExists(StartupShortcutPath());
\r
3894 void SetStartOnSystemStartup(bool fAutoStart)
\r
3896 // If the shortcut exists already, remove it for updating
\r
3897 remove(StartupShortcutPath().c_str());
\r
3901 CoInitialize(NULL);
\r
3903 // Get a pointer to the IShellLink interface.
\r
3904 IShellLink* psl = NULL;
\r
3905 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
\r
3906 CLSCTX_INPROC_SERVER, IID_IShellLink,
\r
3907 reinterpret_cast<void**>(&psl));
\r
3909 if (SUCCEEDED(hres))
\r
3911 // Get the current executable path
\r
3912 char pszExePath[MAX_PATH];
\r
3913 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
\r
3915 // Set the path to the shortcut target
\r
3916 psl->SetPath(pszExePath);
\r
3917 PathRemoveFileSpec(pszExePath);
\r
3918 psl->SetWorkingDirectory(pszExePath);
\r
3919 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
\r
3921 // Query IShellLink for the IPersistFile interface for
\r
3922 // saving the shortcut in persistent storage.
\r
3923 IPersistFile* ppf = NULL;
\r
3924 hres = psl->QueryInterface(IID_IPersistFile,
\r
3925 reinterpret_cast<void**>(&ppf));
\r
3926 if (SUCCEEDED(hres))
\r
3928 WCHAR pwsz[MAX_PATH];
\r
3929 // Ensure that the string is ANSI.
\r
3930 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
\r
3931 // Save the link by calling IPersistFile::Save.
\r
3932 hres = ppf->Save(pwsz, TRUE);
\r
3941 bool GetStartOnSystemStartup() { return false; }
\r
3942 void SetStartOnSystemStartup(bool fAutoStart) { }
\r