1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2011 The Bitcoin developers
3 // Copyright (c) 2011 The PPCoin developers
4 // Distributed under the MIT/X11 software license, see the accompanying
5 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
11 #include <boost/filesystem/fstream.hpp>
12 #include <boost/filesystem/convenience.hpp>
18 using namespace boost;
21 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
23 CMainFrame* pframeMain = NULL;
24 CMyTaskBarIcon* ptaskbaricon = NULL;
25 bool fClosedToTray = false;
32 static const double nScaleX = 1.0;
33 static const double nScaleY = 1.0;
43 //////////////////////////////////////////////////////////////////////////////
48 void HandleCtrlA(wxKeyEvent& event)
52 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
53 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
54 textCtrl->SetSelection(-1, -1);
59 //char pszHourFormat[256];
60 //pszHourFormat[0] = '\0';
61 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
62 //return (pszHourFormat[0] != '0');
66 string DateStr(int64 nTime)
68 // Can only be used safely here in the UI
69 return (string)wxDateTime((time_t)nTime).FormatDate();
72 string DateTimeStr(int64 nTime)
74 // Can only be used safely here in the UI
75 wxDateTime datetime((time_t)nTime);
77 return (string)datetime.Format("%x %H:%M");
79 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
82 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
84 // Helper to simplify access to listctrl
86 item.m_itemId = nIndex;
88 item.m_mask = wxLIST_MASK_TEXT;
89 if (!listCtrl->GetItem(item))
91 return item.GetText();
94 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
96 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
97 listCtrl->SetItem(nIndex, 1, str1);
101 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
103 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
104 listCtrl->SetItem(nIndex, 1, str1);
105 listCtrl->SetItem(nIndex, 2, str2);
106 listCtrl->SetItem(nIndex, 3, str3);
107 listCtrl->SetItem(nIndex, 4, str4);
111 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
113 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
114 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
115 listCtrl->SetItem(nIndex, 1, str1);
116 listCtrl->SetItem(nIndex, 2, str2);
117 listCtrl->SetItem(nIndex, 3, str3);
118 listCtrl->SetItem(nIndex, 4, str4);
122 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
124 // Repaint on Windows is more flickery if the colour has ever been set,
125 // so don't want to set it unless it's different. Default colour has
126 // alpha 0 transparent, so our colours don't match using operator==.
127 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
129 c1 = wxColour(0,0,0);
130 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
131 listCtrl->SetItemTextColour(nIndex, colour);
134 void SetSelection(wxListCtrl* listCtrl, int nIndex)
136 int nSize = listCtrl->GetItemCount();
137 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
138 for (int i = 0; i < nSize; i++)
139 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
142 int GetSelection(wxListCtrl* listCtrl)
144 int nSize = listCtrl->GetItemCount();
145 for (int i = 0; i < nSize; i++)
146 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
151 string HtmlEscape(const char* psz, bool fMultiLine=false)
154 for (const char* p = psz; *p; p++)
156 if (*p == '<') len += 4;
157 else if (*p == '>') len += 4;
158 else if (*p == '&') len += 5;
159 else if (*p == '"') len += 6;
160 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
161 else if (*p == '\n' && fMultiLine) len += 5;
167 for (const char* p = psz; *p; p++)
169 if (*p == '<') str += "<";
170 else if (*p == '>') str += ">";
171 else if (*p == '&') str += "&";
172 else if (*p == '"') str += """;
173 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
174 else if (*p == '\n' && fMultiLine) str += "<br>\n";
181 string HtmlEscape(const string& str, bool fMultiLine=false)
183 return HtmlEscape(str.c_str(), fMultiLine);
186 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
188 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
192 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
195 return wxMessageBox(message, caption, style, parent, x, y);
197 if (wxThread::IsMain() || fDaemon)
199 return wxMessageBox(message, caption, style, parent, x, y);
205 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
213 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
215 if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
217 string strMessage = strprintf(
218 _("This transaction is over the size limit. You can still send it for a fee of %s, "
219 "which goes to the nodes that process your transaction and helps to support the network. "
220 "Do you want to pay the fee?"),
221 FormatMoney(nFeeRequired).c_str());
222 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
225 void CalledSetStatusBar(const string& strText, int nField)
227 if (nField == 0 && GetWarnings("statusbar") != "")
229 if (pframeMain && pframeMain->m_statusBar)
230 pframeMain->m_statusBar->SetStatusText(strText, nField);
233 void SetDefaultReceivingAddress(const string& strAddress)
235 // Update main window address and database
236 if (pframeMain == NULL)
238 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
240 CBitcoinAddress address(strAddress);
241 if (!address.IsValid())
243 vector<unsigned char> vchPubKey;
244 if (!pwalletMain->GetPubKey(address, vchPubKey))
246 pwalletMain->SetDefaultKey(vchPubKey);
247 pframeMain->m_textCtrlAddress->SetValue(strAddress);
251 bool GetWalletPassphrase()
253 if (pwalletMain->IsLocked())
255 string strWalletPass;
256 strWalletPass.reserve(100);
257 mlock(&strWalletPass[0], strWalletPass.capacity());
259 // obtain current wallet encrypt/decrypt key, from passphrase
260 // Note that the passphrase is not mlock()d during this entry and could potentially
261 // be obtained from disk long after bitcoin has run.
262 strWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
263 _("Passphrase")).ToStdString();
265 if (!strWalletPass.size())
267 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
268 munlock(&strWalletPass[0], strWalletPass.capacity());
269 wxMessageBox(_("Please supply the current wallet decryption passphrase."), "Bitcoin");
273 if (!pwalletMain->Unlock(strWalletPass))
275 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
276 munlock(&strWalletPass[0], strWalletPass.capacity());
277 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin");
280 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
281 munlock(&strWalletPass[0], strWalletPass.capacity());
295 //////////////////////////////////////////////////////////////////////////////
300 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
302 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
304 // Set initially selected page
305 wxNotebookEvent event;
306 event.SetSelection(0);
307 OnNotebookPageChanged(event);
308 m_notebook->ChangeSelection(0);
311 fRefreshListCtrl = false;
312 fRefreshListCtrlRunning = false;
313 fOnSetFocusAddress = false;
315 m_choiceFilter->SetSelection(0);
316 double dResize = nScaleX;
318 SetIcon(wxICON(bitcoin));
319 SetSize(dResize * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
321 SetIcon(bitcoin80_xpm);
322 SetBackgroundColour(m_toolBar->GetBackgroundColour());
323 wxFont fontTmp = m_staticText41->GetFont();
324 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
325 m_staticTextBalance->SetFont(fontTmp);
326 m_staticTextBalance->SetSize(140, 17);
327 // resize to fit ubuntu's huge default font
329 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
331 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
332 m_listCtrl->SetFocus();
333 ptaskbaricon = new CMyTaskBarIcon();
335 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
336 // to their standard places, leaving these menus empty.
337 GetMenuBar()->Remove(2); // remove Help menu
338 GetMenuBar()->Remove(0); // remove File menu
341 // Init column headers
342 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
343 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
349 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
350 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
352 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
353 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
354 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
355 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
356 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
357 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
358 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
362 int pnWidths[3] = { -100, 88, 300 };
364 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
365 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
367 m_statusBar->SetFieldsCount(3, pnWidths);
369 // Fill your address text box
370 vector<unsigned char> vchPubKey;
371 if (CWalletDB(pwalletMain->strWalletFile,"r").ReadDefaultKey(vchPubKey))
372 m_textCtrlAddress->SetValue(CBitcoinAddress(vchPubKey).ToString());
374 if (pwalletMain->IsCrypted())
375 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
377 m_menuOptions->Remove(m_menuOptionsChangeWalletPassphrase);
379 // Fill listctrl with wallet transactions
383 CMainFrame::~CMainFrame()
390 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
393 nPage = event.GetSelection();
396 m_listCtrl = m_listCtrlAll;
397 fShowGenerated = true;
399 fShowReceived = true;
401 else if (nPage == SENTRECEIVED)
403 m_listCtrl = m_listCtrlSentReceived;
404 fShowGenerated = false;
406 fShowReceived = true;
408 else if (nPage == SENT)
410 m_listCtrl = m_listCtrlSent;
411 fShowGenerated = false;
413 fShowReceived = false;
415 else if (nPage == RECEIVED)
417 m_listCtrl = m_listCtrlReceived;
418 fShowGenerated = false;
420 fShowReceived = true;
423 m_listCtrl->SetFocus();
426 void CMainFrame::OnClose(wxCloseEvent& event)
428 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
430 // Divert close to minimize
432 fClosedToTray = true;
438 CreateThread(Shutdown, NULL);
442 void CMainFrame::OnIconize(wxIconizeEvent& event)
445 // Hide the task bar button when minimized.
446 // Event is sent when the frame is minimized or restored.
447 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
448 // to get rid of the deprecated warning. Just ignore it.
449 if (!event.Iconized())
450 fClosedToTray = false;
451 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
452 if (GetBoolArg("-minimizetotray")) {
454 // The tray icon sometimes disappears on ubuntu karmic
455 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
456 // Reports of CPU peg on 64-bit linux
457 if (fMinimizeToTray && event.Iconized())
458 fClosedToTray = true;
459 Show(!fClosedToTray);
460 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
461 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
466 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
470 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
471 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
474 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
476 // Hidden columns not resizeable
477 if (event.GetColumn() <= 1 && !fDebug)
483 int CMainFrame::GetSortIndex(const string& strSort)
488 // The wx generic listctrl implementation used on GTK doesn't sort,
489 // so we have to do it ourselves. Remember, we sort in reverse order.
490 // In the wx generic implementation, they store the list of items
491 // in a vector, so indexed lookups are fast, but inserts are slower
492 // the closer they are to the top.
494 int high = m_listCtrl->GetItemCount();
497 int mid = low + ((high - low) / 2);
498 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
507 void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxColour& colour, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6)
509 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
510 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
513 if (!fNew && nIndex == -1)
515 string strHash = " " + hashKey.ToString();
516 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
517 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
521 // fNew is for blind insert, only use if you're sure it's new
522 if (fNew || nIndex == -1)
524 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
528 // If sort key changed, must delete and reinsert to make it relocate
529 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
531 m_listCtrl->DeleteItem(nIndex);
532 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
536 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
537 m_listCtrl->SetItem(nIndex, 2, str2);
538 m_listCtrl->SetItem(nIndex, 3, str3);
539 m_listCtrl->SetItem(nIndex, 4, str4);
540 m_listCtrl->SetItem(nIndex, 5, str5);
541 m_listCtrl->SetItem(nIndex, 6, str6);
542 m_listCtrl->SetItemData(nIndex, nData);
543 SetItemTextColour(m_listCtrl, nIndex, colour);
546 bool CMainFrame::DeleteLine(uint256 hashKey)
548 long nData = *(long*)&hashKey;
552 string strHash = " " + hashKey.ToString();
553 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
554 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
558 m_listCtrl->DeleteItem(nIndex);
563 string FormatTxStatus(const CWalletTx& wtx)
568 if (wtx.nLockTime < LOCKTIME_THRESHOLD)
569 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
571 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
575 int nDepth = wtx.GetDepthInMainChain();
576 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
577 return strprintf(_("%d/offline?"), nDepth);
579 return strprintf(_("%d/unconfirmed"), nDepth);
581 return strprintf(_("%d confirmations"), nDepth);
585 string SingleLine(const string& strIn)
588 bool fOneSpace = false;
589 BOOST_FOREACH(unsigned char c, strIn)
597 if (fOneSpace && !strOut.empty())
606 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
608 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
609 int64 nCredit = wtx.GetCredit(true);
610 int64 nDebit = wtx.GetDebit();
611 int64 nNet = nCredit - nDebit;
612 uint256 hash = wtx.GetHash();
613 string strStatus = FormatTxStatus(wtx);
614 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
615 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
616 map<string, string> mapValue = wtx.mapValue;
617 wtx.nLinesDisplayed = 1;
621 if (wtx.IsCoinBase())
623 // Don't show generated coin until confirmed by at least one block after it
624 // so we don't get the user's hopes up until it looks like it's probably accepted.
626 // It is not an error when generated blocks are not accepted. By design,
627 // some percentage of blocks, like 10% or more, will end up not accepted.
628 // This is the normal mechanism by which the network copes with latency.
630 // We display regular transactions right away before any confirmation
631 // because they can always get into some block eventually. Generated coins
632 // are special because if their block is not accepted, they are not valid.
634 if (wtx.GetDepthInMainChain() < 2)
636 wtx.nLinesDisplayed = 0;
644 // Find the block the tx is in
645 CBlockIndex* pindex = NULL;
646 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
647 if (mi != mapBlockIndex.end())
648 pindex = (*mi).second;
650 // Sort order, unrecorded transactions sort to the top
651 string strSort = strprintf("%010d-%01d-%010u",
652 (pindex ? pindex->nHeight : INT_MAX),
653 (wtx.IsCoinBase() ? 1 : 0),
657 if (nNet > 0 || wtx.IsCoinBase())
662 string strDescription;
663 if (wtx.IsCoinBase())
666 strDescription = _("Generated");
669 int64 nUnmatured = 0;
670 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
671 nUnmatured += pwalletMain->GetCredit(txout);
672 if (wtx.IsInMainChain())
674 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
676 // Check if the block was requested by anyone
677 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
678 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
682 strDescription = _("Generated (not accepted)");
686 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
688 // Received by IP connection
691 if (!mapValue["from"].empty())
692 strDescription += _("From: ") + mapValue["from"];
693 if (!mapValue["message"].empty())
695 if (!strDescription.empty())
696 strDescription += " - ";
697 strDescription += mapValue["message"];
702 // Received by Bitcoin Address
705 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
707 if (pwalletMain->IsMine(txout))
709 CBitcoinAddress address;
710 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
712 CRITICAL_BLOCK(pwalletMain->cs_wallet)
714 //strDescription += _("Received payment to ");
715 //strDescription += _("Received with address ");
716 strDescription += _("Received with: ");
717 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
718 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
720 string strLabel = (*mi).second;
721 strDescription += address.ToString().substr(0,12) + "... ";
722 strDescription += "(" + strLabel + ")";
725 strDescription += address.ToString();
733 string strCredit = FormatMoney(nNet, true);
735 strCredit = "[" + strCredit + "]";
737 InsertLine(fNew, nIndex, hash, strSort, colour,
739 nTime ? DateTimeStr(nTime) : "",
740 SingleLine(strDescription),
746 bool fAllFromMe = true;
747 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
748 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
750 bool fAllToMe = true;
751 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
752 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
754 if (fAllFromMe && fAllToMe)
757 int64 nChange = wtx.GetChange();
758 InsertLine(fNew, nIndex, hash, strSort, colour,
760 nTime ? DateTimeStr(nTime) : "",
761 _("Payment to yourself"),
762 FormatMoney(-(nDebit - nChange), true),
763 FormatMoney(nCredit - nChange, true));
773 int64 nTxFee = nDebit - wtx.GetValueOut();
774 wtx.nLinesDisplayed = 0;
775 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
777 const CTxOut& txout = wtx.vout[nOut];
778 if (pwalletMain->IsMine(txout))
781 CBitcoinAddress address;
783 if (!mapValue["to"].empty())
786 strAddress = mapValue["to"];
790 // Sent to Bitcoin Address
791 if (ExtractAddress(txout.scriptPubKey, NULL, address))
792 strAddress = address.ToString();
795 string strDescription = _("To: ");
796 CRITICAL_BLOCK(pwalletMain->cs_wallet)
797 if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
798 strDescription += pwalletMain->mapAddressBook[address] + " ";
799 strDescription += strAddress;
800 if (!mapValue["message"].empty())
802 if (!strDescription.empty())
803 strDescription += " - ";
804 strDescription += mapValue["message"];
806 else if (!mapValue["comment"].empty())
808 if (!strDescription.empty())
809 strDescription += " - ";
810 strDescription += mapValue["comment"];
813 int64 nValue = txout.nValue;
820 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
822 nTime ? DateTimeStr(nTime) : "",
823 SingleLine(strDescription),
824 FormatMoney(-nValue, true),
827 wtx.nLinesDisplayed++;
833 // Mixed debit transaction, can't break down payees
835 bool fAllMine = true;
836 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
837 fAllMine = fAllMine && pwalletMain->IsMine(txout);
838 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
839 fAllMine = fAllMine && pwalletMain->IsMine(txin);
841 InsertLine(fNew, nIndex, hash, strSort, colour,
843 nTime ? DateTimeStr(nTime) : "",
845 FormatMoney(nNet, true),
853 void CMainFrame::RefreshListCtrl()
855 fRefreshListCtrl = true;
859 void CMainFrame::OnIdle(wxIdleEvent& event)
861 if (fRefreshListCtrl)
863 // Collect list of wallet transactions and sort newest first
864 bool fEntered = false;
865 vector<pair<unsigned int, uint256> > vSorted;
866 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
868 printf("RefreshListCtrl starting\n");
870 fRefreshListCtrl = false;
871 pwalletMain->vWalletUpdated.clear();
873 // Do the newest transactions first
874 vSorted.reserve(pwalletMain->mapWallet.size());
875 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
877 const CWalletTx& wtx = (*it).second;
878 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
879 vSorted.push_back(make_pair(nTime, (*it).first));
881 m_listCtrl->DeleteAllItems();
886 sort(vSorted.begin(), vSorted.end());
889 for (int i = 0; i < vSorted.size();)
893 bool fEntered = false;
894 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
897 uint256& hash = vSorted[i++].second;
898 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
899 if (mi != pwalletMain->mapWallet.end())
900 InsertTransaction((*mi).second, true);
902 if (!fEntered || i == 100 || i % 500 == 0)
906 printf("RefreshListCtrl done\n");
908 // Update transaction total display
913 // Check for time updates
914 static int64 nLastTime;
915 if (GetTime() > nLastTime + 30)
917 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
919 nLastTime = GetTime();
920 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
922 CWalletTx& wtx = (*it).second;
923 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
924 InsertTransaction(wtx, false);
931 void CMainFrame::RefreshStatusColumn()
934 static CBlockIndex* pindexLastBest;
935 static unsigned int nLastRefreshed;
937 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
938 if (nTop == nLastTop && pindexLastBest == pindexBest)
941 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
944 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
946 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
948 // If no updates, only need to do the part that moved onto the screen
949 if (nStart >= nLastTop && nStart < nLastTop + 100)
950 nStart = nLastTop + 100;
951 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
955 pindexLastBest = pindexBest;
956 nLastRefreshed = nListViewUpdated;
958 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
960 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
961 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
962 if (mi == pwalletMain->mapWallet.end())
964 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
967 CWalletTx& wtx = (*mi).second;
968 if (wtx.IsCoinBase() ||
969 wtx.GetTxTime() != wtx.nTimeDisplayed ||
970 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
972 if (!InsertTransaction(wtx, false, nIndex))
973 m_listCtrl->DeleteItem(nIndex--);
977 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
983 void CMainFrame::OnPaint(wxPaintEvent& event)
994 unsigned int nNeedRepaint = 0;
995 unsigned int nLastRepaint = 0;
996 int64 nLastRepaintTime = 0;
997 int64 nRepaintInterval = 500;
999 void ThreadDelayedRepaint(void* parg)
1003 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1005 nLastRepaint = nNeedRepaint;
1008 printf("DelayedRepaint\n");
1010 pframeMain->fRefresh = true;
1011 pframeMain->GetEventHandler()->AddPendingEvent(event);
1014 Sleep(nRepaintInterval);
1018 void MainFrameRepaint()
1020 // This is called by network code that shouldn't access pframeMain
1021 // directly because it could still be running after the UI is closed.
1024 // Don't repaint too often
1025 static int64 nLastRepaintRequest;
1026 if (GetTimeMillis() - nLastRepaintRequest < 100)
1031 nLastRepaintRequest = GetTimeMillis();
1033 printf("MainFrameRepaint\n");
1035 pframeMain->fRefresh = true;
1036 pframeMain->GetEventHandler()->AddPendingEvent(event);
1040 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
1042 // Skip lets the listctrl do the paint, we're just hooking the message
1046 ptaskbaricon->UpdateTooltip();
1051 static int nTransactionCount;
1052 bool fPaintedBalance = false;
1053 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1055 nLastRepaint = nNeedRepaint;
1056 nLastRepaintTime = GetTimeMillis();
1058 // Update listctrl contents
1059 if (!pwalletMain->vWalletUpdated.empty())
1061 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
1064 if (m_listCtrl->GetItemCount())
1065 strTop = (string)m_listCtrl->GetItemText(0);
1066 BOOST_FOREACH(uint256 hash, pwalletMain->vWalletUpdated)
1068 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1069 if (mi != pwalletMain->mapWallet.end())
1070 InsertTransaction((*mi).second, false);
1072 pwalletMain->vWalletUpdated.clear();
1073 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1074 m_listCtrl->ScrollList(0, INT_MIN/2);
1079 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
1081 fPaintedBalance = true;
1082 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
1084 // Count hidden and multi-line transactions
1085 nTransactionCount = 0;
1086 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1088 CWalletTx& wtx = (*it).second;
1089 nTransactionCount += wtx.nLinesDisplayed;
1093 if (!pwalletMain->vWalletUpdated.empty() || !fPaintedBalance)
1096 // Update status column of visible items only
1097 RefreshStatusColumn();
1099 // Update status bar
1100 static string strPrevWarning;
1101 string strWarning = GetWarnings("statusbar");
1102 if (strWarning != "")
1103 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1104 else if (strPrevWarning != "")
1105 m_statusBar->SetStatusText("", 0);
1106 strPrevWarning = strWarning;
1109 if (fGenerateBitcoins)
1110 strGen = _(" Generating");
1111 if (fGenerateBitcoins && vNodes.empty())
1112 strGen = _("(not connected)");
1113 m_statusBar->SetStatusText(strGen, 1);
1115 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1116 m_statusBar->SetStatusText(strStatus, 2);
1118 // Update receiving address
1119 string strDefaultAddress = CBitcoinAddress(pwalletMain->vchDefaultKey).ToString();
1120 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1121 m_textCtrlAddress->SetValue(strDefaultAddress);
1125 void UIThreadCall(boost::function0<void> fn)
1127 // Call this with a function object created with bind.
1128 // bind needs all parameters to match the function's expected types
1129 // and all default parameters specified. Some examples:
1130 // UIThreadCall(bind(wxBell));
1131 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1132 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1135 wxCommandEvent event(wxEVT_UITHREADCALL);
1136 event.SetClientData((void*)new boost::function0<void>(fn));
1137 pframeMain->GetEventHandler()->AddPendingEvent(event);
1141 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1143 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1148 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1154 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1156 event.Check(fGenerateBitcoins);
1159 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1161 // Options->Your Receiving Addresses
1162 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1163 if (!dialog.ShowModal())
1167 void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event)
1169 // Options->Encrypt Wallet
1170 if (pwalletMain->IsCrypted())
1172 wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR);
1176 string strWalletPass;
1177 strWalletPass.reserve(100);
1178 mlock(&strWalletPass[0], strWalletPass.capacity());
1180 // obtain current wallet encrypt/decrypt key, from passphrase
1181 // Note that the passphrase is not mlock()d during this entry and could potentially
1182 // be obtained from disk long after bitcoin has run.
1183 strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."),
1184 _("Passphrase")).ToStdString();
1186 if (!strWalletPass.size())
1188 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1189 munlock(&strWalletPass[0], strWalletPass.capacity());
1190 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1194 if(wxMessageBox(_("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"), "Bitcoin", wxYES_NO) != wxYES)
1197 string strWalletPassTest;
1198 strWalletPassTest.reserve(100);
1199 mlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1200 strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."),
1201 _("Passphrase")).ToStdString();
1203 if (strWalletPassTest != strWalletPass)
1205 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1206 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1207 munlock(&strWalletPass[0], strWalletPass.capacity());
1208 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1209 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1213 if (!pwalletMain->EncryptWallet(strWalletPass))
1215 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1216 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1217 munlock(&strWalletPass[0], strWalletPass.capacity());
1218 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1219 wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR);
1222 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1223 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1224 munlock(&strWalletPass[0], strWalletPass.capacity());
1225 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1226 wxMessageBox(_("Wallet Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1228 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
1229 m_menuOptions->Insert(m_menuOptions->GetMenuItemCount() - 1, m_menuOptionsChangeWalletPassphrase);
1232 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1234 // Options->Change Wallet Encryption Passphrase
1235 if (!pwalletMain->IsCrypted())
1237 wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1241 string strOldWalletPass;
1242 strOldWalletPass.reserve(100);
1243 mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1245 // obtain current wallet encrypt/decrypt key, from passphrase
1246 // Note that the passphrase is not mlock()d during this entry and could potentially
1247 // be obtained from disk long after bitcoin has run.
1248 strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1249 _("Passphrase")).ToStdString();
1251 bool fWasLocked = pwalletMain->IsLocked();
1252 pwalletMain->Lock();
1254 if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1256 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1257 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1258 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1263 pwalletMain->Lock();
1265 string strNewWalletPass;
1266 strNewWalletPass.reserve(100);
1267 mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1269 // obtain new wallet encrypt/decrypt key, from passphrase
1270 // Note that the passphrase is not mlock()d during this entry and could potentially
1271 // be obtained from disk long after bitcoin has run.
1272 strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1273 _("Passphrase")).ToStdString();
1275 if (!strNewWalletPass.size())
1277 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1278 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1279 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1280 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1281 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1285 string strNewWalletPassTest;
1286 strNewWalletPassTest.reserve(100);
1287 mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1289 // obtain new wallet encrypt/decrypt key, from passphrase
1290 // Note that the passphrase is not mlock()d during this entry and could potentially
1291 // be obtained from disk long after bitcoin has run.
1292 strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1293 _("Passphrase")).ToStdString();
1295 if (strNewWalletPassTest != strNewWalletPass)
1297 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1298 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1299 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1300 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1301 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1302 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1303 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1307 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1309 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1310 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1311 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1312 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1313 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1314 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1315 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1318 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1319 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1320 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1321 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1322 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1323 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1324 wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
1327 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1330 COptionsDialog dialog(this);
1334 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1337 CAboutDialog dialog(this);
1341 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1344 CSendDialog dialog(this);
1348 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1350 // Toolbar: Address Book
1351 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1352 if (dialogAddr.ShowModal() == 2)
1355 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1356 dialogSend.ShowModal();
1360 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1362 // Automatically select-all when entering window
1364 m_textCtrlAddress->SetSelection(-1, -1);
1365 fOnSetFocusAddress = true;
1368 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1371 if (fOnSetFocusAddress)
1372 m_textCtrlAddress->SetSelection(-1, -1);
1373 fOnSetFocusAddress = false;
1376 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1379 CGetTextFromUserDialog dialog(this,
1380 _("New Receiving Address"),
1381 _("You should use a new address for each payment you receive.\n\nLabel"),
1383 if (!dialog.ShowModal())
1385 string strName = dialog.GetValue();
1389 bool fWasLocked = pwalletMain->IsLocked();
1390 if (!GetWalletPassphrase())
1394 std::vector<unsigned char> newKey;
1395 pwalletMain->GetKeyFromPool(newKey, true);
1396 strAddress = CBitcoinAddress(newKey).ToString();
1399 pwalletMain->Lock();
1402 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1403 pwalletMain->SetAddressBookName(strAddress, strName);
1404 SetDefaultReceivingAddress(strAddress);
1407 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1409 // Copy address box to clipboard
1410 if (wxTheClipboard->Open())
1412 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1413 wxTheClipboard->Close();
1417 void CMainFrame::OnListItemActivated(wxListEvent& event)
1419 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1421 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1423 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1424 if (mi == pwalletMain->mapWallet.end())
1426 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1431 CTxDetailsDialog dialog(this, wtx);
1433 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1442 //////////////////////////////////////////////////////////////////////////////
1447 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1450 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1452 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1455 strHTML.reserve(4000);
1456 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1458 int64 nTime = wtx.GetTxTime();
1459 int64 nCredit = wtx.GetCredit();
1460 int64 nDebit = wtx.GetDebit();
1461 int64 nNet = nCredit - nDebit;
1465 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1466 int nRequests = wtx.GetRequestCount();
1467 if (nRequests != -1)
1470 strHTML += _(", has not been successfully broadcast yet");
1471 else if (nRequests == 1)
1472 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1474 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1478 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1484 if (wtx.IsCoinBase())
1486 strHTML += _("<b>Source:</b> Generated<br>");
1488 else if (!wtx.mapValue["from"].empty())
1490 // Online transaction
1491 if (!wtx.mapValue["from"].empty())
1492 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1496 // Offline transaction
1500 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1502 if (pwalletMain->IsMine(txout))
1504 CBitcoinAddress address;
1505 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1507 if (pwalletMain->mapAddressBook.count(address))
1509 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1510 strHTML += _("<b>To:</b> ");
1511 strHTML += HtmlEscape(address.ToString());
1512 if (!pwalletMain->mapAddressBook[address].empty())
1513 strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[address] + ")";
1515 strHTML += _(" (yours)");
1530 if (!wtx.mapValue["to"].empty())
1532 // Online transaction
1533 strAddress = wtx.mapValue["to"];
1534 strHTML += _("<b>To:</b> ");
1535 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1536 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1537 strHTML += HtmlEscape(strAddress) + "<br>";
1544 if (wtx.IsCoinBase() && nCredit == 0)
1549 int64 nUnmatured = 0;
1550 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1551 nUnmatured += pwalletMain->GetCredit(txout);
1552 strHTML += _("<b>Credit:</b> ");
1553 if (wtx.IsInMainChain())
1554 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1556 strHTML += _("(not accepted)");
1564 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1568 bool fAllFromMe = true;
1569 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1570 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1572 bool fAllToMe = true;
1573 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1574 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1581 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1583 if (pwalletMain->IsMine(txout))
1586 if (wtx.mapValue["to"].empty())
1588 // Offline transaction
1589 CBitcoinAddress address;
1590 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1592 string strAddress = address.ToString();
1593 strHTML += _("<b>To:</b> ");
1594 if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
1595 strHTML += pwalletMain->mapAddressBook[address] + " ";
1596 strHTML += strAddress;
1601 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1607 int64 nChange = wtx.GetChange();
1608 int64 nValue = nCredit - nChange;
1609 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1610 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1613 int64 nTxFee = nDebit - wtx.GetValueOut();
1615 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1620 // Mixed debit transaction
1622 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1623 if (pwalletMain->IsMine(txin))
1624 strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1625 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1626 if (pwalletMain->IsMine(txout))
1627 strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1631 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1637 if (!wtx.mapValue["message"].empty())
1638 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1639 if (!wtx.mapValue["comment"].empty())
1640 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1642 if (wtx.IsCoinBase())
1643 strHTML += string() + "<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>";
1651 strHTML += "<hr><br>debug print<br><br>";
1652 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1653 if (pwalletMain->IsMine(txin))
1654 strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1655 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1656 if (pwalletMain->IsMine(txout))
1657 strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1659 strHTML += "<br><b>Transaction:</b><br>";
1660 strHTML += HtmlEscape(wtx.ToString(), true);
1662 strHTML += "<br><b>Inputs:</b><br>";
1663 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1665 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1667 COutPoint prevout = txin.prevout;
1668 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1669 if (mi != pwalletMain->mapWallet.end())
1671 const CWalletTx& prev = (*mi).second;
1672 if (prevout.n < prev.vout.size())
1674 strHTML += HtmlEscape(prev.ToString(), true);
1675 strHTML += " " + FormatTxStatus(prev) + ", ";
1676 strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1685 strHTML += "</font></html>";
1686 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1687 m_htmlWin->SetPage(strHTML);
1688 m_buttonOK->SetFocus();
1692 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1702 //////////////////////////////////////////////////////////////////////////////
1708 string StartupShortcutPath()
1710 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1713 bool GetStartOnSystemStartup()
1715 return filesystem::exists(StartupShortcutPath().c_str());
1718 void SetStartOnSystemStartup(bool fAutoStart)
1720 // If the shortcut exists already, remove it for updating
1721 remove(StartupShortcutPath().c_str());
1727 // Get a pointer to the IShellLink interface.
1728 IShellLink* psl = NULL;
1729 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1730 CLSCTX_INPROC_SERVER, IID_IShellLink,
1731 reinterpret_cast<void**>(&psl));
1733 if (SUCCEEDED(hres))
1735 // Get the current executable path
1736 TCHAR pszExePath[MAX_PATH];
1737 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1739 // Set the path to the shortcut target
1740 psl->SetPath(pszExePath);
1741 PathRemoveFileSpec(pszExePath);
1742 psl->SetWorkingDirectory(pszExePath);
1743 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1745 // Query IShellLink for the IPersistFile interface for
1746 // saving the shortcut in persistent storage.
1747 IPersistFile* ppf = NULL;
1748 hres = psl->QueryInterface(IID_IPersistFile,
1749 reinterpret_cast<void**>(&ppf));
1750 if (SUCCEEDED(hres))
1752 WCHAR pwsz[MAX_PATH];
1753 // Ensure that the string is ANSI.
1754 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1755 // Save the link by calling IPersistFile::Save.
1756 hres = ppf->Save(pwsz, TRUE);
1765 #elif defined(__WXGTK__)
1767 // Follow the Desktop Application Autostart Spec:
1768 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1770 boost::filesystem::path GetAutostartDir()
1772 namespace fs = boost::filesystem;
1774 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1775 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1776 char* pszHome = getenv("HOME");
1777 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1781 boost::filesystem::path GetAutostartFilePath()
1783 return GetAutostartDir() / boost::filesystem::path("ppcoin.desktop");
1786 bool GetStartOnSystemStartup()
1788 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1789 if (!optionFile.good())
1791 // Scan through file for "Hidden=true":
1793 while (!optionFile.eof())
1795 getline(optionFile, line);
1796 if (line.find("Hidden") != string::npos &&
1797 line.find("true") != string::npos)
1805 void SetStartOnSystemStartup(bool fAutoStart)
1809 #if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
1810 unlink(GetAutostartFilePath().string().c_str());
1812 unlink(GetAutostartFilePath().native_file_string().c_str());
1817 char pszExePath[MAX_PATH+1];
1818 memset(pszExePath, 0, sizeof(pszExePath));
1819 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1822 boost::filesystem::create_directories(GetAutostartDir());
1824 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1825 if (!optionFile.good())
1827 wxMessageBox(_("Cannot write autostart/ppcoin.desktop file"), "Bitcoin");
1830 // Write a ppcoin.desktop file to the autostart directory:
1831 optionFile << "[Desktop Entry]\n";
1832 optionFile << "Type=Application\n";
1833 optionFile << "Name=Bitcoin\n";
1834 optionFile << "Exec=" << pszExePath << "\n";
1835 optionFile << "Terminal=false\n";
1836 optionFile << "Hidden=false\n";
1842 // TODO: OSX startup stuff; see:
1843 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1845 bool GetStartOnSystemStartup() { return false; }
1846 void SetStartOnSystemStartup(bool fAutoStart) { }
1855 //////////////////////////////////////////////////////////////////////////////
1860 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1862 // Set up list box of page choices
1863 m_listBox->Append(_("Main"));
1864 //m_listBox->Append(_("Test 2"));
1865 m_listBox->SetSelection(0);
1868 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1870 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1872 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1873 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1874 if (!GetBoolArg("-minimizetotray"))
1876 // Minimize to tray is just too buggy on Linux
1877 fMinimizeToTray = false;
1878 m_checkBoxMinimizeToTray->SetValue(false);
1879 m_checkBoxMinimizeToTray->Enable(false);
1880 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1883 #ifdef __WXMAC_OSX__
1884 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1888 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1889 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1890 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1891 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1893 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1895 m_checkBoxUseUPnP->Enable(false);
1896 m_checkBoxUseProxy->SetValue(fUseProxy);
1897 m_textCtrlProxyIP->Enable(fUseProxy);
1898 m_textCtrlProxyPort->Enable(fUseProxy);
1899 m_staticTextProxyIP->Enable(fUseProxy);
1900 m_staticTextProxyPort->Enable(fUseProxy);
1901 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1902 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1904 m_buttonOK->SetFocus();
1907 void COptionsDialog::SelectPage(int nPage)
1909 m_panelMain->Show(nPage == 0);
1910 m_panelTest2->Show(nPage == 1);
1912 m_scrolledWindow->Layout();
1913 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1916 void COptionsDialog::OnListBox(wxCommandEvent& event)
1918 SelectPage(event.GetSelection());
1921 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1924 int64 nTmp = nTransactionFee;
1925 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1926 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1929 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1931 m_textCtrlProxyIP->Enable(event.IsChecked());
1932 m_textCtrlProxyPort->Enable(event.IsChecked());
1933 m_staticTextProxyIP->Enable(event.IsChecked());
1934 m_staticTextProxyPort->Enable(event.IsChecked());
1937 CAddress COptionsDialog::GetProxyAddr()
1939 // Be careful about byte order, addr.ip and addr.port are big endian
1940 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1941 if (addr.ip == INADDR_NONE)
1942 addr.ip = addrProxy.ip;
1943 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1944 addr.port = htons(nPort);
1945 if (nPort <= 0 || nPort > USHRT_MAX)
1946 addr.port = addrProxy.port;
1950 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1953 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1954 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1958 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1960 OnButtonApply(event);
1964 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1969 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1971 CWalletDB walletdb(pwalletMain->strWalletFile);
1973 int64 nPrevTransactionFee = nTransactionFee;
1974 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1975 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1977 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1979 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1980 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1983 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1985 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1986 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1987 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1990 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1992 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1993 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1996 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1998 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1999 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
2003 fUseProxy = m_checkBoxUseProxy->GetValue();
2004 walletdb.WriteSetting("fUseProxy", fUseProxy);
2006 addrProxy = GetProxyAddr();
2007 walletdb.WriteSetting("addrProxy", addrProxy);
2015 //////////////////////////////////////////////////////////////////////////////
2020 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2022 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2024 // Change (c) into UTF-8 or ANSI copyright symbol
2025 wxString str = m_staticTextMain->GetLabel();
2027 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2029 str.Replace("(c)", "\xA9");
2031 m_staticTextMain->SetLabel(str);
2033 // Resize on Linux to make the window fit the text.
2034 // The text was wrapped manually rather than using the Wrap setting because
2035 // the wrap would be too small on Linux and it can't be changed at this point.
2036 wxFont fontTmp = m_staticTextMain->GetFont();
2037 if (fontTmp.GetPointSize() > 8);
2038 fontTmp.SetPointSize(8);
2039 m_staticTextMain->SetFont(fontTmp);
2040 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2042 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2046 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2056 //////////////////////////////////////////////////////////////////////////////
2061 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2064 m_textCtrlAddress->SetValue(strAddress);
2065 m_choiceTransferType->SetSelection(0);
2066 m_bitmapCheckMark->Show(false);
2067 fEnabledPrev = true;
2068 m_textCtrlAddress->SetFocus();
2070 //// todo: should add a display of your balance for convenience
2072 wxFont fontTmp = m_staticTextInstructions->GetFont();
2073 if (fontTmp.GetPointSize() > 9);
2074 fontTmp.SetPointSize(9);
2075 m_staticTextInstructions->SetFont(fontTmp);
2078 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2082 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2085 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2090 SetIcon(wxICON(bitcoin));
2093 // Fixup the tab order
2094 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2095 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2099 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2101 // Reformat the amount
2103 if (m_textCtrlAmount->GetValue().Trim().empty())
2106 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2107 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2110 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2112 // Open address book
2113 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2114 if (dialog.ShowModal())
2115 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2118 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2120 // Copy clipboard to address box
2121 if (wxTheClipboard->Open())
2123 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2125 wxTextDataObject data;
2126 wxTheClipboard->GetData(data);
2127 m_textCtrlAddress->SetValue(data.GetText());
2129 wxTheClipboard->Close();
2133 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2135 static CCriticalSection cs_sendlock;
2136 TRY_CRITICAL_BLOCK(cs_sendlock)
2139 string strAddress = (string)m_textCtrlAddress->GetValue();
2143 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2145 wxMessageBox(_("Error in amount "), _("Send Coins"));
2148 if (nValue > pwalletMain->GetBalance())
2150 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2153 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2155 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2159 // Parse bitcoin address
2160 CBitcoinAddress address(strAddress);
2161 bool fBitcoinAddress = address.IsValid();
2163 if (fBitcoinAddress)
2165 bool fWasLocked = pwalletMain->IsLocked();
2166 if (!GetWalletPassphrase())
2170 CRITICAL_BLOCK(cs_main)
2171 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2173 // Send to bitcoin address
2174 CScript scriptPubKey;
2175 scriptPubKey.SetBitcoinAddress(address);
2177 strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2181 pframeMain->RefreshListCtrl();
2182 wxMessageBox(_("Payment sent "), _("Sending..."));
2184 else if (strError == "ABORTED")
2187 pwalletMain->Lock();
2188 return; // leave send dialog open
2192 wxMessageBox(strError + " ", _("Sending..."));
2195 pwalletMain->Lock();
2200 pwalletMain->Lock();
2205 CAddress addr(strAddress);
2206 if (!addr.IsValid())
2208 wxMessageBox(_("Invalid address "), _("Send Coins"));
2213 wtx.mapValue["to"] = strAddress;
2215 // Send to IP address
2216 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2217 if (!pdialog->ShowModal())
2221 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2222 if (!pwalletMain->mapAddressBook.count(address))
2223 pwalletMain->SetAddressBookName(strAddress, "");
2229 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2240 //////////////////////////////////////////////////////////////////////////////
2245 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
2250 start = wxDateTime::UNow();
2251 memset(pszStatus, 0, sizeof(pszStatus));
2258 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2260 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2263 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2264 m_textCtrlStatus->SetValue("");
2266 CreateThread(SendingDialogStartTransfer, this);
2269 CSendingDialog::~CSendingDialog()
2271 printf("~CSendingDialog()\n");
2274 void CSendingDialog::Close()
2276 // Last one out turn out the lights.
2277 // fWorkDone signals that work side is done and UI thread should call destroy.
2278 // fUIDone signals that UI window has closed and work thread should call destroy.
2279 // This allows the window to disappear and end modality when cancelled
2280 // without making the user wait for ConnectNode to return. The dialog object
2281 // hangs around in the background until the work thread exits.
2292 void CSendingDialog::OnClose(wxCloseEvent& event)
2294 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2301 wxCommandEvent cmdevent;
2302 OnButtonCancel(cmdevent);
2306 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2312 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2318 void CSendingDialog::OnPaint(wxPaintEvent& event)
2321 if (strlen(pszStatus) > 130)
2322 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2324 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2325 m_staticTextSending->SetFocus();
2327 m_buttonCancel->Enable(false);
2330 m_buttonOK->Enable(true);
2331 m_buttonOK->SetFocus();
2332 m_buttonCancel->Enable(false);
2334 if (fAbort && fCanCancel && IsShown())
2336 strcpy(pszStatus, _("CANCELLED"));
2337 m_buttonOK->Enable(true);
2338 m_buttonOK->SetFocus();
2339 m_buttonCancel->Enable(false);
2340 m_buttonCancel->SetLabel(_("Cancelled"));
2342 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2348 // Everything from here on is not in the UI thread and must only communicate
2349 // with the rest of the dialog through variables and calling repaint.
2352 void CSendingDialog::Repaint()
2356 GetEventHandler()->AddPendingEvent(event);
2359 bool CSendingDialog::Status()
2366 if (fAbort && fCanCancel)
2368 memset(pszStatus, 0, 10);
2369 strcpy(pszStatus, _("CANCELLED"));
2377 bool CSendingDialog::Status(const string& str)
2382 // This can be read by the UI thread at any time,
2383 // so copy in a way that can be read cleanly at all times.
2384 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2385 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2391 bool CSendingDialog::Error(const string& str)
2395 Status(string(_("Error: ")) + str);
2399 void SendingDialogStartTransfer(void* parg)
2401 ((CSendingDialog*)parg)->StartTransfer();
2404 void CSendingDialog::StartTransfer()
2406 // Make sure we have enough money
2407 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2409 Error(_("Insufficient funds"));
2413 // We may have connected already for product details
2414 if (!Status(_("Connecting...")))
2416 CNode* pnode = ConnectNode(addr, 15 * 60);
2419 Error(_("Unable to connect"));
2423 // Send order to seller, with response going to OnReply2 via event handler
2424 if (!Status(_("Requesting public key...")))
2426 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2429 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2431 ((CSendingDialog*)parg)->OnReply2(vRecv);
2434 void CSendingDialog::OnReply2(CDataStream& vRecv)
2436 if (!Status(_("Received public key...")))
2439 CScript scriptPubKey;
2448 vRecv >> strMessage;
2450 Error(_("Recipient is not accepting transactions sent by IP address"));
2452 Error(_("Transfer was not accepted"));
2453 //// todo: enlarge the window and enable a hidden white box to put seller's message
2456 vRecv >> scriptPubKey;
2460 //// what do we want to do about this?
2461 Error(_("Invalid response received"));
2465 // Pause to give the user a chance to cancel
2466 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2474 if (!Status(_("Creating transaction...")))
2476 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2478 Error(_("Insufficient funds"));
2482 CReserveKey reservekey(pwalletMain);
2484 bool fWasLocked = pwalletMain->IsLocked();
2485 if (!GetWalletPassphrase())
2488 bool fTxCreated = false;
2489 CRITICAL_BLOCK(cs_main)
2490 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2492 fTxCreated = pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired);
2496 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2497 Error(strprintf(_("This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds"), FormatMoney(nFeeRequired).c_str()));
2499 Error(_("Transaction creation failed"));
2504 pwalletMain->Lock();
2507 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2509 Error(_("Transaction aborted"));
2513 // Make sure we're still connected
2514 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2517 Error(_("Lost connection, transaction cancelled"));
2521 // Last chance to cancel
2533 if (!Status(_("Sending payment...")))
2537 bool fTxCommitted = false;
2538 CRITICAL_BLOCK(cs_main)
2539 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2541 fTxCommitted = pwalletMain->CommitTransaction(wtx, reservekey);
2545 Error(_("The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."));
2549 // Send payment tx to seller, with response going to OnReply3 via event handler
2550 CWalletTx wtxSend = wtx;
2551 wtxSend.fFromMe = false;
2552 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2554 Status(_("Waiting for confirmation..."));
2558 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2560 ((CSendingDialog*)parg)->OnReply3(vRecv);
2563 void CSendingDialog::OnReply3(CDataStream& vRecv)
2571 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2572 "The transaction is recorded and will credit to the recipient,\n"
2573 "but the comment information will be blank."));
2579 //// what do we want to do about this?
2580 Error(_("Payment was sent, but an invalid response was received"));
2586 Status(_("Payment completed"));
2594 //////////////////////////////////////////////////////////////////////////////
2596 // CAddressBookDialog
2599 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2602 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2605 // Set initially selected page
2606 wxNotebookEvent event;
2607 event.SetSelection(nPageIn);
2608 OnNotebookPageChanged(event);
2609 m_notebook->ChangeSelection(nPageIn);
2611 fDuringSend = fDuringSendIn;
2613 m_buttonCancel->Show(false);
2616 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2618 wxIcon iconAddressBook;
2619 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2620 SetIcon(iconAddressBook);
2624 SetIcon(wxICON(bitcoin));
2627 // Init column headers
2628 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2629 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2630 m_listCtrlSending->SetFocus();
2631 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2632 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2633 m_listCtrlReceiving->SetFocus();
2635 // Fill listctrl with address book data
2636 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2638 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2639 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2641 const CBitcoinAddress& address = item.first;
2642 string strName = item.second;
2643 bool fMine = pwalletMain->HaveKey(address);
2644 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2645 int nIndex = InsertLine(plistCtrl, strName, address.ToString());
2646 if (address.ToString() == (fMine ? strDefaultReceiving : string(strInitSelected)))
2647 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2652 wxString CAddressBookDialog::GetSelectedAddress()
2654 int nIndex = GetSelection(m_listCtrl);
2657 return GetItemText(m_listCtrl, nIndex, 1);
2660 wxString CAddressBookDialog::GetSelectedSendingAddress()
2662 int nIndex = GetSelection(m_listCtrlSending);
2665 return GetItemText(m_listCtrlSending, nIndex, 1);
2668 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2670 int nIndex = GetSelection(m_listCtrlReceiving);
2673 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2676 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2679 nPage = event.GetSelection();
2680 if (nPage == SENDING)
2681 m_listCtrl = m_listCtrlSending;
2682 else if (nPage == RECEIVING)
2683 m_listCtrl = m_listCtrlReceiving;
2684 m_buttonDelete->Show(nPage == SENDING);
2685 m_buttonCopy->Show(nPage == RECEIVING);
2687 m_listCtrl->SetFocus();
2690 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2692 // Update address book with edited name
2694 if (event.IsEditCancelled())
2696 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2697 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2698 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2699 pframeMain->RefreshListCtrl();
2702 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2705 if (nPage == RECEIVING)
2706 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2709 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2714 // Doubleclick returns selection
2715 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2719 // Doubleclick edits item
2720 wxCommandEvent event2;
2721 OnButtonEdit(event2);
2724 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2726 if (nPage != SENDING)
2728 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2730 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2732 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2733 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2734 pwalletMain->DelAddressBookName(strAddress);
2735 m_listCtrl->DeleteItem(nIndex);
2738 pframeMain->RefreshListCtrl();
2741 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2743 // Copy address box to clipboard
2744 if (wxTheClipboard->Open())
2746 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2747 wxTheClipboard->Close();
2751 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2753 CBitcoinAddress address(strAddress);
2754 bool fMine = address.IsValid() && pwalletMain->HaveKey(address);
2756 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2760 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2762 int nIndex = GetSelection(m_listCtrl);
2765 string strName = (string)m_listCtrl->GetItemText(nIndex);
2766 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2767 string strAddressOrg = strAddress;
2769 if (nPage == SENDING)
2771 // Ask name and address
2774 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2775 if (!dialog.ShowModal())
2777 strName = dialog.GetValue1();
2778 strAddress = dialog.GetValue2();
2780 while (CheckIfMine(strAddress, _("Edit Address")));
2783 else if (nPage == RECEIVING)
2786 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2787 if (!dialog.ShowModal())
2789 strName = dialog.GetValue();
2793 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2795 if (strAddress != strAddressOrg)
2796 pwalletMain->DelAddressBookName(strAddressOrg);
2797 pwalletMain->SetAddressBookName(strAddress, strName);
2799 m_listCtrl->SetItem(nIndex, 1, strAddress);
2800 m_listCtrl->SetItemText(nIndex, strName);
2801 pframeMain->RefreshListCtrl();
2804 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2809 if (nPage == SENDING)
2811 // Ask name and address
2814 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2815 if (!dialog.ShowModal())
2817 strName = dialog.GetValue1();
2818 strAddress = dialog.GetValue2();
2820 while (CheckIfMine(strAddress, _("Add Address")));
2822 else if (nPage == RECEIVING)
2825 CGetTextFromUserDialog dialog(this,
2826 _("New Receiving Address"),
2827 _("You should use a new address for each payment you receive.\n\nLabel"),
2829 if (!dialog.ShowModal())
2831 strName = dialog.GetValue();
2833 bool fWasLocked = pwalletMain->IsLocked();
2834 if (!GetWalletPassphrase())
2838 std::vector<unsigned char> newKey;
2839 pwalletMain->GetKeyFromPool(newKey, true);
2840 strAddress = CBitcoinAddress(newKey).ToString();
2843 pwalletMain->Lock();
2846 // Add to list and select it
2847 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2848 pwalletMain->SetAddressBookName(strAddress, strName);
2849 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2850 SetSelection(m_listCtrl, nIndex);
2851 m_listCtrl->SetFocus();
2852 if (nPage == SENDING)
2853 pframeMain->RefreshListCtrl();
2856 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2859 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2862 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2868 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2879 //////////////////////////////////////////////////////////////////////////////
2886 ID_TASKBAR_RESTORE = 10001,
2889 ID_TASKBAR_GENERATE,
2893 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2894 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2895 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2896 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2897 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2898 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2899 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2902 void CMyTaskBarIcon::Show(bool fShow)
2904 static char pszPrevTip[200];
2907 string strTooltip = _("Bitcoin");
2908 if (fGenerateBitcoins)
2909 strTooltip = _("Bitcoin - Generating");
2910 if (fGenerateBitcoins && vNodes.empty())
2911 strTooltip = _("Bitcoin - (not connected)");
2913 // Optimization, only update when changed, using char array to be reentrant
2914 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2916 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2918 // somehow it'll choose the wrong size and scale it down if
2919 // we use the main icon, so we hand it one with only 16x16
2920 SetIcon(wxICON(favicon), strTooltip);
2922 SetIcon(bitcoin80_xpm, strTooltip);
2928 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2933 void CMyTaskBarIcon::Hide()
2938 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2943 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2948 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2951 CSendDialog dialog(pframeMain);
2955 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2957 // Since it's modal, get the main window to do it
2958 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2959 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2962 void CMyTaskBarIcon::Restore()
2965 wxIconizeEvent event(0, false);
2966 pframeMain->GetEventHandler()->AddPendingEvent(event);
2967 pframeMain->Iconize(false);
2968 pframeMain->Raise();
2971 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2973 event.Check(fGenerateBitcoins);
2976 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2978 pframeMain->Close(true);
2981 void CMyTaskBarIcon::UpdateTooltip()
2983 if (IsIconInstalled())
2987 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2989 wxMenu* pmenu = new wxMenu;
2990 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2991 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2992 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2993 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2994 pmenu->AppendSeparator();
2995 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
3005 //////////////////////////////////////////////////////////////////////////////
3010 void CreateMainWindow()
3012 pframeMain = new CMainFrame(NULL);
3013 if (GetBoolArg("-min"))
3014 pframeMain->Iconize(true);
3015 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3016 if (!GetBoolArg("-minimizetotray"))
3017 fMinimizeToTray = false;
3019 pframeMain->Show(true); // have to show first to get taskbar button to hide
3020 if (fMinimizeToTray && pframeMain->IsIconized())
3021 fClosedToTray = true;
3022 pframeMain->Show(!fClosedToTray);
3023 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3024 CreateThread(ThreadDelayedRepaint, NULL);
3028 // Define a new application
3029 class CMyApp : public wxApp
3038 // Hook Initialize so we can start without GUI
3039 virtual bool Initialize(int& argc, wxChar** argv);
3041 // 2nd-level exception handling: we get all the exceptions occurring in any
3042 // event handler here
3043 virtual bool OnExceptionInMainLoop();
3045 // 3rd, and final, level exception handling: whenever an unhandled
3046 // exception is caught, this function is called
3047 virtual void OnUnhandledException();
3049 // and now for something different: this function is called in case of a
3050 // crash (e.g. dereferencing null pointer, division by 0, ...)
3051 virtual void OnFatalException();
3054 IMPLEMENT_APP(CMyApp)
3056 bool CMyApp::Initialize(int& argc, wxChar** argv)
3058 for (int i = 1; i < argc; i++)
3059 if (!IsSwitchChar(argv[i][0]))
3060 fCommandLine = true;
3064 // wxApp::Initialize will remove environment-specific parameters,
3065 // so it's too early to call ParseParameters yet
3066 for (int i = 1; i < argc; i++)
3068 wxString str = argv[i];
3070 if (str.size() >= 1 && str[0] == '/')
3072 char pszLower[MAX_PATH];
3073 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3077 if (str == "-daemon")
3083 if (fDaemon || fCommandLine)
3085 // Call the original Initialize while suppressing error messages
3086 // and ignoring failure. If unable to initialize GTK, it fails
3087 // near the end so hopefully the last few things don't matter.
3090 wxApp::Initialize(argc, argv);
3099 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3103 pthread_exit((void*)0);
3105 pid_t sid = setsid();
3107 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3114 return wxApp::Initialize(argc, argv);
3117 bool CMyApp::OnInit()
3119 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3120 // Disable malfunctioning wxWidgets debug assertion
3121 extern int g_isPainting;
3122 g_isPainting = 10000;
3124 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3125 SetAppName("PPCoin");
3127 SetAppName("ppcoin");
3131 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3132 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3133 class wxMBConv_win32 : public wxMBConv
3137 size_t m_minMBCharWidth;
3139 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3140 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3144 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3145 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3146 g_locale.AddCatalogLookupPathPrefix("locale");
3148 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3149 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3151 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3152 g_locale.AddCatalog("bitcoin");
3155 HDC hdc = GetDC(NULL);
3158 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3159 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3160 ReleaseDC(NULL, hdc);
3164 return AppInit(argc, argv);
3167 int CMyApp::OnExit()
3170 return wxApp::OnExit();
3173 bool CMyApp::OnExceptionInMainLoop()
3179 catch (std::exception& e)
3181 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3182 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3188 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3189 wxLogWarning("Unknown exception");
3196 void CMyApp::OnUnhandledException()
3198 // this shows how we may let some exception propagate uncaught
3203 catch (std::exception& e)
3205 PrintException(&e, "CMyApp::OnUnhandledException()");
3206 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3212 PrintException(NULL, "CMyApp::OnUnhandledException()");
3213 wxLogWarning("Unknown exception");
3219 void CMyApp::OnFatalException()
3221 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);