1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
9 #include <boost/filesystem/fstream.hpp>
10 #include <boost/filesystem/convenience.hpp>
16 using namespace boost;
19 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
21 CMainFrame* pframeMain = NULL;
22 CMyTaskBarIcon* ptaskbaricon = NULL;
23 bool fClosedToTray = false;
30 static const double nScaleX = 1.0;
31 static const double nScaleY = 1.0;
41 //////////////////////////////////////////////////////////////////////////////
46 void HandleCtrlA(wxKeyEvent& event)
50 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
51 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
52 textCtrl->SetSelection(-1, -1);
57 //char pszHourFormat[256];
58 //pszHourFormat[0] = '\0';
59 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
60 //return (pszHourFormat[0] != '0');
64 string DateStr(int64 nTime)
66 // Can only be used safely here in the UI
67 return (string)wxDateTime((time_t)nTime).FormatDate();
70 string DateTimeStr(int64 nTime)
72 // Can only be used safely here in the UI
73 wxDateTime datetime((time_t)nTime);
75 return (string)datetime.Format("%x %H:%M");
77 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
80 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
82 // Helper to simplify access to listctrl
84 item.m_itemId = nIndex;
86 item.m_mask = wxLIST_MASK_TEXT;
87 if (!listCtrl->GetItem(item))
89 return item.GetText();
92 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
94 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
95 listCtrl->SetItem(nIndex, 1, str1);
99 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
101 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
102 listCtrl->SetItem(nIndex, 1, str1);
103 listCtrl->SetItem(nIndex, 2, str2);
104 listCtrl->SetItem(nIndex, 3, str3);
105 listCtrl->SetItem(nIndex, 4, str4);
109 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
111 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
112 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
113 listCtrl->SetItem(nIndex, 1, str1);
114 listCtrl->SetItem(nIndex, 2, str2);
115 listCtrl->SetItem(nIndex, 3, str3);
116 listCtrl->SetItem(nIndex, 4, str4);
120 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
122 // Repaint on Windows is more flickery if the colour has ever been set,
123 // so don't want to set it unless it's different. Default colour has
124 // alpha 0 transparent, so our colours don't match using operator==.
125 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
127 c1 = wxColour(0,0,0);
128 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
129 listCtrl->SetItemTextColour(nIndex, colour);
132 void SetSelection(wxListCtrl* listCtrl, int nIndex)
134 int nSize = listCtrl->GetItemCount();
135 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
136 for (int i = 0; i < nSize; i++)
137 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
140 int GetSelection(wxListCtrl* listCtrl)
142 int nSize = listCtrl->GetItemCount();
143 for (int i = 0; i < nSize; i++)
144 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
149 string HtmlEscape(const char* psz, bool fMultiLine=false)
152 for (const char* p = psz; *p; p++)
154 if (*p == '<') len += 4;
155 else if (*p == '>') len += 4;
156 else if (*p == '&') len += 5;
157 else if (*p == '"') len += 6;
158 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
159 else if (*p == '\n' && fMultiLine) len += 5;
165 for (const char* p = psz; *p; p++)
167 if (*p == '<') str += "<";
168 else if (*p == '>') str += ">";
169 else if (*p == '&') str += "&";
170 else if (*p == '"') str += """;
171 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
172 else if (*p == '\n' && fMultiLine) str += "<br>\n";
179 string HtmlEscape(const string& str, bool fMultiLine=false)
181 return HtmlEscape(str.c_str(), fMultiLine);
184 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
186 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
190 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
193 return wxMessageBox(message, caption, style, parent, x, y);
195 if (wxThread::IsMain() || fDaemon)
197 return wxMessageBox(message, caption, style, parent, x, y);
203 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
211 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
213 if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
215 string strMessage = strprintf(
216 _("This transaction is over the size limit. You can still send it for a fee of %s, "
217 "which goes to the nodes that process your transaction and helps to support the network. "
218 "Do you want to pay the fee?"),
219 FormatMoney(nFeeRequired).c_str());
220 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
223 void CalledSetStatusBar(const string& strText, int nField)
225 if (nField == 0 && GetWarnings("statusbar") != "")
227 if (pframeMain && pframeMain->m_statusBar)
228 pframeMain->m_statusBar->SetStatusText(strText, nField);
231 void SetDefaultReceivingAddress(const string& strAddress)
233 // Update main window address and database
234 if (pframeMain == NULL)
236 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
239 if (!AddressToHash160(strAddress, hash160))
241 if (!mapPubKeys.count(hash160))
243 pwalletMain->SetDefaultKey(mapPubKeys[hash160]);
244 pframeMain->m_textCtrlAddress->SetValue(strAddress);
248 bool GetWalletPassphrase()
250 if (pwalletMain->IsLocked())
252 string strWalletPass;
253 strWalletPass.reserve(100);
254 mlock(&strWalletPass[0], strWalletPass.capacity());
256 // obtain current wallet encrypt/decrypt key, from passphrase
257 // Note that the passphrase is not mlock()d during this entry and could potentially
258 // be obtained from disk long after bitcoin has run.
259 strWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
260 _("Passphrase")).ToStdString();
262 if (!strWalletPass.size())
264 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
265 munlock(&strWalletPass[0], strWalletPass.capacity());
266 wxMessageBox(_("Please supply the current wallet decryption passphrase."), "Bitcoin");
270 if (!pwalletMain->Unlock(strWalletPass))
272 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
273 munlock(&strWalletPass[0], strWalletPass.capacity());
274 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin");
277 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
278 munlock(&strWalletPass[0], strWalletPass.capacity());
292 //////////////////////////////////////////////////////////////////////////////
297 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
299 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
301 // Set initially selected page
302 wxNotebookEvent event;
303 event.SetSelection(0);
304 OnNotebookPageChanged(event);
305 m_notebook->ChangeSelection(0);
308 fRefreshListCtrl = false;
309 fRefreshListCtrlRunning = false;
310 fOnSetFocusAddress = false;
312 m_choiceFilter->SetSelection(0);
313 double dResize = nScaleX;
315 SetIcon(wxICON(bitcoin));
316 SetSize(dResize * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
318 SetIcon(bitcoin80_xpm);
319 SetBackgroundColour(m_toolBar->GetBackgroundColour());
320 wxFont fontTmp = m_staticText41->GetFont();
321 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
322 m_staticTextBalance->SetFont(fontTmp);
323 m_staticTextBalance->SetSize(140, 17);
324 // resize to fit ubuntu's huge default font
326 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
328 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
329 m_listCtrl->SetFocus();
330 ptaskbaricon = new CMyTaskBarIcon();
332 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
333 // to their standard places, leaving these menus empty.
334 GetMenuBar()->Remove(2); // remove Help menu
335 GetMenuBar()->Remove(0); // remove File menu
338 // Init column headers
339 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
340 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
346 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
347 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
349 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
350 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
351 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
352 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
353 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
354 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
355 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
359 int pnWidths[3] = { -100, 88, 300 };
361 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
362 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
364 m_statusBar->SetFieldsCount(3, pnWidths);
366 // Fill your address text box
367 vector<unsigned char> vchPubKey;
368 if (CWalletDB(pwalletMain->strWalletFile,"r").ReadDefaultKey(vchPubKey))
369 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
371 if (pwalletMain->IsCrypted())
372 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
374 m_menuOptions->Remove(m_menuOptionsChangeWalletPassphrase);
376 // Fill listctrl with wallet transactions
380 CMainFrame::~CMainFrame()
387 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
390 nPage = event.GetSelection();
393 m_listCtrl = m_listCtrlAll;
394 fShowGenerated = true;
396 fShowReceived = true;
398 else if (nPage == SENTRECEIVED)
400 m_listCtrl = m_listCtrlSentReceived;
401 fShowGenerated = false;
403 fShowReceived = true;
405 else if (nPage == SENT)
407 m_listCtrl = m_listCtrlSent;
408 fShowGenerated = false;
410 fShowReceived = false;
412 else if (nPage == RECEIVED)
414 m_listCtrl = m_listCtrlReceived;
415 fShowGenerated = false;
417 fShowReceived = true;
420 m_listCtrl->SetFocus();
423 void CMainFrame::OnClose(wxCloseEvent& event)
425 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
427 // Divert close to minimize
429 fClosedToTray = true;
435 CreateThread(Shutdown, NULL);
439 void CMainFrame::OnIconize(wxIconizeEvent& event)
442 // Hide the task bar button when minimized.
443 // Event is sent when the frame is minimized or restored.
444 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
445 // to get rid of the deprecated warning. Just ignore it.
446 if (!event.Iconized())
447 fClosedToTray = false;
448 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
449 if (GetBoolArg("-minimizetotray")) {
451 // The tray icon sometimes disappears on ubuntu karmic
452 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
453 // Reports of CPU peg on 64-bit linux
454 if (fMinimizeToTray && event.Iconized())
455 fClosedToTray = true;
456 Show(!fClosedToTray);
457 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
458 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
463 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
467 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
468 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
471 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
473 // Hidden columns not resizeable
474 if (event.GetColumn() <= 1 && !fDebug)
480 int CMainFrame::GetSortIndex(const string& strSort)
485 // The wx generic listctrl implementation used on GTK doesn't sort,
486 // so we have to do it ourselves. Remember, we sort in reverse order.
487 // In the wx generic implementation, they store the list of items
488 // in a vector, so indexed lookups are fast, but inserts are slower
489 // the closer they are to the top.
491 int high = m_listCtrl->GetItemCount();
494 int mid = low + ((high - low) / 2);
495 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
504 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)
506 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
507 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
510 if (!fNew && nIndex == -1)
512 string strHash = " " + hashKey.ToString();
513 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
514 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
518 // fNew is for blind insert, only use if you're sure it's new
519 if (fNew || nIndex == -1)
521 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
525 // If sort key changed, must delete and reinsert to make it relocate
526 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
528 m_listCtrl->DeleteItem(nIndex);
529 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
533 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
534 m_listCtrl->SetItem(nIndex, 2, str2);
535 m_listCtrl->SetItem(nIndex, 3, str3);
536 m_listCtrl->SetItem(nIndex, 4, str4);
537 m_listCtrl->SetItem(nIndex, 5, str5);
538 m_listCtrl->SetItem(nIndex, 6, str6);
539 m_listCtrl->SetItemData(nIndex, nData);
540 SetItemTextColour(m_listCtrl, nIndex, colour);
543 bool CMainFrame::DeleteLine(uint256 hashKey)
545 long nData = *(long*)&hashKey;
549 string strHash = " " + hashKey.ToString();
550 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
551 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
555 m_listCtrl->DeleteItem(nIndex);
560 string FormatTxStatus(const CWalletTx& wtx)
565 if (wtx.nLockTime < 500000000)
566 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
568 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
572 int nDepth = wtx.GetDepthInMainChain();
573 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
574 return strprintf(_("%d/offline?"), nDepth);
576 return strprintf(_("%d/unconfirmed"), nDepth);
578 return strprintf(_("%d confirmations"), nDepth);
582 string SingleLine(const string& strIn)
585 bool fOneSpace = false;
586 BOOST_FOREACH(unsigned char c, strIn)
594 if (fOneSpace && !strOut.empty())
603 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
605 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
606 int64 nCredit = wtx.GetCredit(true);
607 int64 nDebit = wtx.GetDebit();
608 int64 nNet = nCredit - nDebit;
609 uint256 hash = wtx.GetHash();
610 string strStatus = FormatTxStatus(wtx);
611 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
612 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
613 map<string, string> mapValue = wtx.mapValue;
614 wtx.nLinesDisplayed = 1;
618 if (wtx.IsCoinBase())
620 // Don't show generated coin until confirmed by at least one block after it
621 // so we don't get the user's hopes up until it looks like it's probably accepted.
623 // It is not an error when generated blocks are not accepted. By design,
624 // some percentage of blocks, like 10% or more, will end up not accepted.
625 // This is the normal mechanism by which the network copes with latency.
627 // We display regular transactions right away before any confirmation
628 // because they can always get into some block eventually. Generated coins
629 // are special because if their block is not accepted, they are not valid.
631 if (wtx.GetDepthInMainChain() < 2)
633 wtx.nLinesDisplayed = 0;
641 // Find the block the tx is in
642 CBlockIndex* pindex = NULL;
643 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
644 if (mi != mapBlockIndex.end())
645 pindex = (*mi).second;
647 // Sort order, unrecorded transactions sort to the top
648 string strSort = strprintf("%010d-%01d-%010u",
649 (pindex ? pindex->nHeight : INT_MAX),
650 (wtx.IsCoinBase() ? 1 : 0),
654 if (nNet > 0 || wtx.IsCoinBase())
659 string strDescription;
660 if (wtx.IsCoinBase())
663 strDescription = _("Generated");
666 int64 nUnmatured = 0;
667 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
668 nUnmatured += pwalletMain->GetCredit(txout);
669 if (wtx.IsInMainChain())
671 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
673 // Check if the block was requested by anyone
674 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
675 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
679 strDescription = _("Generated (not accepted)");
683 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
685 // Received by IP connection
688 if (!mapValue["from"].empty())
689 strDescription += _("From: ") + mapValue["from"];
690 if (!mapValue["message"].empty())
692 if (!strDescription.empty())
693 strDescription += " - ";
694 strDescription += mapValue["message"];
699 // Received by Bitcoin Address
702 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
704 if (pwalletMain->IsMine(txout))
706 vector<unsigned char> vchPubKey;
707 if (ExtractPubKey(txout.scriptPubKey, pwalletMain, vchPubKey))
709 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
711 //strDescription += _("Received payment to ");
712 //strDescription += _("Received with address ");
713 strDescription += _("Received with: ");
714 string strAddress = PubKeyToAddress(vchPubKey);
715 map<string, string>::iterator mi = pwalletMain->mapAddressBook.find(strAddress);
716 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
718 string strLabel = (*mi).second;
719 strDescription += strAddress.substr(0,12) + "... ";
720 strDescription += "(" + strLabel + ")";
723 strDescription += strAddress;
731 string strCredit = FormatMoney(nNet, true);
733 strCredit = "[" + strCredit + "]";
735 InsertLine(fNew, nIndex, hash, strSort, colour,
737 nTime ? DateTimeStr(nTime) : "",
738 SingleLine(strDescription),
744 bool fAllFromMe = true;
745 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
746 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
748 bool fAllToMe = true;
749 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
750 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
752 if (fAllFromMe && fAllToMe)
755 int64 nChange = wtx.GetChange();
756 InsertLine(fNew, nIndex, hash, strSort, colour,
758 nTime ? DateTimeStr(nTime) : "",
759 _("Payment to yourself"),
760 FormatMoney(-(nDebit - nChange), true),
761 FormatMoney(nCredit - nChange, true));
771 int64 nTxFee = nDebit - wtx.GetValueOut();
772 wtx.nLinesDisplayed = 0;
773 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
775 const CTxOut& txout = wtx.vout[nOut];
776 if (pwalletMain->IsMine(txout))
780 if (!mapValue["to"].empty())
783 strAddress = mapValue["to"];
787 // Sent to Bitcoin Address
789 if (ExtractHash160(txout.scriptPubKey, hash160))
790 strAddress = Hash160ToAddress(hash160);
793 string strDescription = _("To: ");
794 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
795 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
796 strDescription += pwalletMain->mapAddressBook[strAddress] + " ";
797 strDescription += strAddress;
798 if (!mapValue["message"].empty())
800 if (!strDescription.empty())
801 strDescription += " - ";
802 strDescription += mapValue["message"];
804 else if (!mapValue["comment"].empty())
806 if (!strDescription.empty())
807 strDescription += " - ";
808 strDescription += mapValue["comment"];
811 int64 nValue = txout.nValue;
818 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
820 nTime ? DateTimeStr(nTime) : "",
821 SingleLine(strDescription),
822 FormatMoney(-nValue, true),
825 wtx.nLinesDisplayed++;
831 // Mixed debit transaction, can't break down payees
833 bool fAllMine = true;
834 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
835 fAllMine = fAllMine && pwalletMain->IsMine(txout);
836 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
837 fAllMine = fAllMine && pwalletMain->IsMine(txin);
839 InsertLine(fNew, nIndex, hash, strSort, colour,
841 nTime ? DateTimeStr(nTime) : "",
843 FormatMoney(nNet, true),
851 void CMainFrame::RefreshListCtrl()
853 fRefreshListCtrl = true;
857 void CMainFrame::OnIdle(wxIdleEvent& event)
859 if (fRefreshListCtrl)
861 // Collect list of wallet transactions and sort newest first
862 bool fEntered = false;
863 vector<pair<unsigned int, uint256> > vSorted;
864 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
866 printf("RefreshListCtrl starting\n");
868 fRefreshListCtrl = false;
869 pwalletMain->vWalletUpdated.clear();
871 // Do the newest transactions first
872 vSorted.reserve(pwalletMain->mapWallet.size());
873 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
875 const CWalletTx& wtx = (*it).second;
876 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
877 vSorted.push_back(make_pair(nTime, (*it).first));
879 m_listCtrl->DeleteAllItems();
884 sort(vSorted.begin(), vSorted.end());
887 for (int i = 0; i < vSorted.size();)
891 bool fEntered = false;
892 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
895 uint256& hash = vSorted[i++].second;
896 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
897 if (mi != pwalletMain->mapWallet.end())
898 InsertTransaction((*mi).second, true);
900 if (!fEntered || i == 100 || i % 500 == 0)
904 printf("RefreshListCtrl done\n");
906 // Update transaction total display
911 // Check for time updates
912 static int64 nLastTime;
913 if (GetTime() > nLastTime + 30)
915 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
917 nLastTime = GetTime();
918 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
920 CWalletTx& wtx = (*it).second;
921 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
922 InsertTransaction(wtx, false);
929 void CMainFrame::RefreshStatusColumn()
932 static CBlockIndex* pindexLastBest;
933 static unsigned int nLastRefreshed;
935 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
936 if (nTop == nLastTop && pindexLastBest == pindexBest)
939 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
942 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
944 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
946 // If no updates, only need to do the part that moved onto the screen
947 if (nStart >= nLastTop && nStart < nLastTop + 100)
948 nStart = nLastTop + 100;
949 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
953 pindexLastBest = pindexBest;
954 nLastRefreshed = nListViewUpdated;
956 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
958 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
959 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
960 if (mi == pwalletMain->mapWallet.end())
962 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
965 CWalletTx& wtx = (*mi).second;
966 if (wtx.IsCoinBase() ||
967 wtx.GetTxTime() != wtx.nTimeDisplayed ||
968 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
970 if (!InsertTransaction(wtx, false, nIndex))
971 m_listCtrl->DeleteItem(nIndex--);
975 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
981 void CMainFrame::OnPaint(wxPaintEvent& event)
992 unsigned int nNeedRepaint = 0;
993 unsigned int nLastRepaint = 0;
994 int64 nLastRepaintTime = 0;
995 int64 nRepaintInterval = 500;
997 void ThreadDelayedRepaint(void* parg)
1001 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1003 nLastRepaint = nNeedRepaint;
1006 printf("DelayedRepaint\n");
1008 pframeMain->fRefresh = true;
1009 pframeMain->GetEventHandler()->AddPendingEvent(event);
1012 Sleep(nRepaintInterval);
1016 void MainFrameRepaint()
1018 // This is called by network code that shouldn't access pframeMain
1019 // directly because it could still be running after the UI is closed.
1022 // Don't repaint too often
1023 static int64 nLastRepaintRequest;
1024 if (GetTimeMillis() - nLastRepaintRequest < 100)
1029 nLastRepaintRequest = GetTimeMillis();
1031 printf("MainFrameRepaint\n");
1033 pframeMain->fRefresh = true;
1034 pframeMain->GetEventHandler()->AddPendingEvent(event);
1038 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
1040 // Skip lets the listctrl do the paint, we're just hooking the message
1044 ptaskbaricon->UpdateTooltip();
1049 static int nTransactionCount;
1050 bool fPaintedBalance = false;
1051 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1053 nLastRepaint = nNeedRepaint;
1054 nLastRepaintTime = GetTimeMillis();
1056 // Update listctrl contents
1057 if (!pwalletMain->vWalletUpdated.empty())
1059 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1062 if (m_listCtrl->GetItemCount())
1063 strTop = (string)m_listCtrl->GetItemText(0);
1064 BOOST_FOREACH(uint256 hash, pwalletMain->vWalletUpdated)
1066 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1067 if (mi != pwalletMain->mapWallet.end())
1068 InsertTransaction((*mi).second, false);
1070 pwalletMain->vWalletUpdated.clear();
1071 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1072 m_listCtrl->ScrollList(0, INT_MIN/2);
1077 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1079 fPaintedBalance = true;
1080 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
1082 // Count hidden and multi-line transactions
1083 nTransactionCount = 0;
1084 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1086 CWalletTx& wtx = (*it).second;
1087 nTransactionCount += wtx.nLinesDisplayed;
1091 if (!pwalletMain->vWalletUpdated.empty() || !fPaintedBalance)
1094 // Update status column of visible items only
1095 RefreshStatusColumn();
1097 // Update status bar
1098 static string strPrevWarning;
1099 string strWarning = GetWarnings("statusbar");
1100 if (strWarning != "")
1101 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1102 else if (strPrevWarning != "")
1103 m_statusBar->SetStatusText("", 0);
1104 strPrevWarning = strWarning;
1107 if (fGenerateBitcoins)
1108 strGen = _(" Generating");
1109 if (fGenerateBitcoins && vNodes.empty())
1110 strGen = _("(not connected)");
1111 m_statusBar->SetStatusText(strGen, 1);
1113 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1114 m_statusBar->SetStatusText(strStatus, 2);
1116 // Update receiving address
1117 string strDefaultAddress = PubKeyToAddress(pwalletMain->vchDefaultKey);
1118 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1119 m_textCtrlAddress->SetValue(strDefaultAddress);
1123 void UIThreadCall(boost::function0<void> fn)
1125 // Call this with a function object created with bind.
1126 // bind needs all parameters to match the function's expected types
1127 // and all default parameters specified. Some examples:
1128 // UIThreadCall(bind(wxBell));
1129 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1130 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1133 wxCommandEvent event(wxEVT_UITHREADCALL);
1134 event.SetClientData((void*)new boost::function0<void>(fn));
1135 pframeMain->GetEventHandler()->AddPendingEvent(event);
1139 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1141 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1146 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1152 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1154 event.Check(fGenerateBitcoins);
1157 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1159 // Options->Your Receiving Addresses
1160 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1161 if (!dialog.ShowModal())
1165 void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event)
1167 // Options->Encrypt Wallet
1168 if (pwalletMain->IsCrypted())
1170 wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR);
1174 string strWalletPass;
1175 strWalletPass.reserve(100);
1176 mlock(&strWalletPass[0], strWalletPass.capacity());
1178 // obtain current wallet encrypt/decrypt key, from passphrase
1179 // Note that the passphrase is not mlock()d during this entry and could potentially
1180 // be obtained from disk long after bitcoin has run.
1181 strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."),
1182 _("Passphrase")).ToStdString();
1184 if (!strWalletPass.size())
1186 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1187 munlock(&strWalletPass[0], strWalletPass.capacity());
1188 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1192 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)
1195 string strWalletPassTest;
1196 strWalletPassTest.reserve(100);
1197 mlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1198 strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."),
1199 _("Passphrase")).ToStdString();
1201 if (strWalletPassTest != strWalletPass)
1203 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1204 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1205 munlock(&strWalletPass[0], strWalletPass.capacity());
1206 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1207 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1211 if (!pwalletMain->EncryptWallet(strWalletPass))
1213 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1214 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1215 munlock(&strWalletPass[0], strWalletPass.capacity());
1216 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1217 wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR);
1220 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1221 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1222 munlock(&strWalletPass[0], strWalletPass.capacity());
1223 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1224 wxMessageBox(_("Wallet Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1226 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
1227 m_menuOptions->Insert(m_menuOptions->GetMenuItemCount() - 1, m_menuOptionsChangeWalletPassphrase);
1230 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1232 // Options->Change Wallet Encryption Passphrase
1233 if (!pwalletMain->IsCrypted())
1235 wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1239 string strOldWalletPass;
1240 strOldWalletPass.reserve(100);
1241 mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1243 // obtain current wallet encrypt/decrypt key, from passphrase
1244 // Note that the passphrase is not mlock()d during this entry and could potentially
1245 // be obtained from disk long after bitcoin has run.
1246 strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1247 _("Passphrase")).ToStdString();
1249 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
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");
1328 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1331 COptionsDialog dialog(this);
1335 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1338 CAboutDialog dialog(this);
1342 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1345 CSendDialog dialog(this);
1349 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1351 // Toolbar: Address Book
1352 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1353 if (dialogAddr.ShowModal() == 2)
1356 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1357 dialogSend.ShowModal();
1361 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1363 // Automatically select-all when entering window
1365 m_textCtrlAddress->SetSelection(-1, -1);
1366 fOnSetFocusAddress = true;
1369 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1372 if (fOnSetFocusAddress)
1373 m_textCtrlAddress->SetSelection(-1, -1);
1374 fOnSetFocusAddress = false;
1377 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1380 CGetTextFromUserDialog dialog(this,
1381 _("New Receiving Address"),
1382 _("You should use a new address for each payment you receive.\n\nLabel"),
1384 if (!dialog.ShowModal())
1386 string strName = dialog.GetValue();
1389 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1391 bool fWasLocked = pwalletMain->IsLocked();
1392 if (!GetWalletPassphrase())
1396 strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
1399 pwalletMain->Lock();
1403 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1404 pwalletMain->SetAddressBookName(strAddress, strName);
1405 SetDefaultReceivingAddress(strAddress);
1408 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1410 // Copy address box to clipboard
1411 if (wxTheClipboard->Open())
1413 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1414 wxTheClipboard->Close();
1418 void CMainFrame::OnListItemActivated(wxListEvent& event)
1420 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1422 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1424 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1425 if (mi == pwalletMain->mapWallet.end())
1427 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1432 CTxDetailsDialog dialog(this, wtx);
1434 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1443 //////////////////////////////////////////////////////////////////////////////
1448 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1451 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1453 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1456 strHTML.reserve(4000);
1457 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1459 int64 nTime = wtx.GetTxTime();
1460 int64 nCredit = wtx.GetCredit();
1461 int64 nDebit = wtx.GetDebit();
1462 int64 nNet = nCredit - nDebit;
1466 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1467 int nRequests = wtx.GetRequestCount();
1468 if (nRequests != -1)
1471 strHTML += _(", has not been successfully broadcast yet");
1472 else if (nRequests == 1)
1473 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1475 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1479 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1485 if (wtx.IsCoinBase())
1487 strHTML += _("<b>Source:</b> Generated<br>");
1489 else if (!wtx.mapValue["from"].empty())
1491 // Online transaction
1492 if (!wtx.mapValue["from"].empty())
1493 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1497 // Offline transaction
1501 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1503 if (pwalletMain->IsMine(txout))
1505 vector<unsigned char> vchPubKey;
1506 if (ExtractPubKey(txout.scriptPubKey, pwalletMain, vchPubKey))
1508 string strAddress = PubKeyToAddress(vchPubKey);
1509 if (pwalletMain->mapAddressBook.count(strAddress))
1511 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1512 strHTML += _("<b>To:</b> ");
1513 strHTML += HtmlEscape(strAddress);
1514 if (!pwalletMain->mapAddressBook[strAddress].empty())
1515 strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[strAddress] + ")";
1517 strHTML += _(" (yours)");
1532 if (!wtx.mapValue["to"].empty())
1534 // Online transaction
1535 strAddress = wtx.mapValue["to"];
1536 strHTML += _("<b>To:</b> ");
1537 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1538 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1539 strHTML += HtmlEscape(strAddress) + "<br>";
1546 if (wtx.IsCoinBase() && nCredit == 0)
1551 int64 nUnmatured = 0;
1552 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1553 nUnmatured += pwalletMain->GetCredit(txout);
1554 strHTML += _("<b>Credit:</b> ");
1555 if (wtx.IsInMainChain())
1556 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1558 strHTML += _("(not accepted)");
1566 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1570 bool fAllFromMe = true;
1571 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1572 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1574 bool fAllToMe = true;
1575 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1576 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1583 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1585 if (pwalletMain->IsMine(txout))
1588 if (wtx.mapValue["to"].empty())
1590 // Offline transaction
1592 if (ExtractHash160(txout.scriptPubKey, hash160))
1594 string strAddress = Hash160ToAddress(hash160);
1595 strHTML += _("<b>To:</b> ");
1596 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1597 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1598 strHTML += strAddress;
1603 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1609 int64 nChange = wtx.GetChange();
1610 int64 nValue = nCredit - nChange;
1611 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1612 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1615 int64 nTxFee = nDebit - wtx.GetValueOut();
1617 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1622 // Mixed debit transaction
1624 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1625 if (pwalletMain->IsMine(txin))
1626 strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1627 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1628 if (pwalletMain->IsMine(txout))
1629 strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1633 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1639 if (!wtx.mapValue["message"].empty())
1640 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1641 if (!wtx.mapValue["comment"].empty())
1642 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1644 if (wtx.IsCoinBase())
1645 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>";
1653 strHTML += "<hr><br>debug print<br><br>";
1654 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1655 if (pwalletMain->IsMine(txin))
1656 strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1657 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1658 if (pwalletMain->IsMine(txout))
1659 strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1661 strHTML += "<br><b>Transaction:</b><br>";
1662 strHTML += HtmlEscape(wtx.ToString(), true);
1664 strHTML += "<br><b>Inputs:</b><br>";
1665 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1667 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1669 COutPoint prevout = txin.prevout;
1670 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1671 if (mi != pwalletMain->mapWallet.end())
1673 const CWalletTx& prev = (*mi).second;
1674 if (prevout.n < prev.vout.size())
1676 strHTML += HtmlEscape(prev.ToString(), true);
1677 strHTML += " " + FormatTxStatus(prev) + ", ";
1678 strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1687 strHTML += "</font></html>";
1688 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1689 m_htmlWin->SetPage(strHTML);
1690 m_buttonOK->SetFocus();
1694 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1704 //////////////////////////////////////////////////////////////////////////////
1710 string StartupShortcutPath()
1712 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1715 bool GetStartOnSystemStartup()
1717 return filesystem::exists(StartupShortcutPath().c_str());
1720 void SetStartOnSystemStartup(bool fAutoStart)
1722 // If the shortcut exists already, remove it for updating
1723 remove(StartupShortcutPath().c_str());
1729 // Get a pointer to the IShellLink interface.
1730 IShellLink* psl = NULL;
1731 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1732 CLSCTX_INPROC_SERVER, IID_IShellLink,
1733 reinterpret_cast<void**>(&psl));
1735 if (SUCCEEDED(hres))
1737 // Get the current executable path
1738 TCHAR pszExePath[MAX_PATH];
1739 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1741 // Set the path to the shortcut target
1742 psl->SetPath(pszExePath);
1743 PathRemoveFileSpec(pszExePath);
1744 psl->SetWorkingDirectory(pszExePath);
1745 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1747 // Query IShellLink for the IPersistFile interface for
1748 // saving the shortcut in persistent storage.
1749 IPersistFile* ppf = NULL;
1750 hres = psl->QueryInterface(IID_IPersistFile,
1751 reinterpret_cast<void**>(&ppf));
1752 if (SUCCEEDED(hres))
1754 WCHAR pwsz[MAX_PATH];
1755 // Ensure that the string is ANSI.
1756 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1757 // Save the link by calling IPersistFile::Save.
1758 hres = ppf->Save(pwsz, TRUE);
1767 #elif defined(__WXGTK__)
1769 // Follow the Desktop Application Autostart Spec:
1770 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1772 boost::filesystem::path GetAutostartDir()
1774 namespace fs = boost::filesystem;
1776 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1777 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1778 char* pszHome = getenv("HOME");
1779 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1783 boost::filesystem::path GetAutostartFilePath()
1785 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1788 bool GetStartOnSystemStartup()
1790 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1791 if (!optionFile.good())
1793 // Scan through file for "Hidden=true":
1795 while (!optionFile.eof())
1797 getline(optionFile, line);
1798 if (line.find("Hidden") != string::npos &&
1799 line.find("true") != string::npos)
1807 void SetStartOnSystemStartup(bool fAutoStart)
1811 unlink(GetAutostartFilePath().native_file_string().c_str());
1815 char pszExePath[MAX_PATH+1];
1816 memset(pszExePath, 0, sizeof(pszExePath));
1817 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1820 boost::filesystem::create_directories(GetAutostartDir());
1822 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1823 if (!optionFile.good())
1825 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1828 // Write a bitcoin.desktop file to the autostart directory:
1829 optionFile << "[Desktop Entry]\n";
1830 optionFile << "Type=Application\n";
1831 optionFile << "Name=Bitcoin\n";
1832 optionFile << "Exec=" << pszExePath << "\n";
1833 optionFile << "Terminal=false\n";
1834 optionFile << "Hidden=false\n";
1840 // TODO: OSX startup stuff; see:
1841 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1843 bool GetStartOnSystemStartup() { return false; }
1844 void SetStartOnSystemStartup(bool fAutoStart) { }
1853 //////////////////////////////////////////////////////////////////////////////
1858 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1860 // Set up list box of page choices
1861 m_listBox->Append(_("Main"));
1862 //m_listBox->Append(_("Test 2"));
1863 m_listBox->SetSelection(0);
1866 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1868 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1870 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1871 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1872 if (!GetBoolArg("-minimizetotray"))
1874 // Minimize to tray is just too buggy on Linux
1875 fMinimizeToTray = false;
1876 m_checkBoxMinimizeToTray->SetValue(false);
1877 m_checkBoxMinimizeToTray->Enable(false);
1878 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1881 #ifdef __WXMAC_OSX__
1882 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1886 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1887 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1888 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1889 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1891 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1893 m_checkBoxUseUPnP->Enable(false);
1894 m_checkBoxUseProxy->SetValue(fUseProxy);
1895 m_textCtrlProxyIP->Enable(fUseProxy);
1896 m_textCtrlProxyPort->Enable(fUseProxy);
1897 m_staticTextProxyIP->Enable(fUseProxy);
1898 m_staticTextProxyPort->Enable(fUseProxy);
1899 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1900 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1902 m_buttonOK->SetFocus();
1905 void COptionsDialog::SelectPage(int nPage)
1907 m_panelMain->Show(nPage == 0);
1908 m_panelTest2->Show(nPage == 1);
1910 m_scrolledWindow->Layout();
1911 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1914 void COptionsDialog::OnListBox(wxCommandEvent& event)
1916 SelectPage(event.GetSelection());
1919 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1922 int64 nTmp = nTransactionFee;
1923 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1924 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1927 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1929 m_textCtrlProxyIP->Enable(event.IsChecked());
1930 m_textCtrlProxyPort->Enable(event.IsChecked());
1931 m_staticTextProxyIP->Enable(event.IsChecked());
1932 m_staticTextProxyPort->Enable(event.IsChecked());
1935 CAddress COptionsDialog::GetProxyAddr()
1937 // Be careful about byte order, addr.ip and addr.port are big endian
1938 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1939 if (addr.ip == INADDR_NONE)
1940 addr.ip = addrProxy.ip;
1941 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1942 addr.port = htons(nPort);
1943 if (nPort <= 0 || nPort > USHRT_MAX)
1944 addr.port = addrProxy.port;
1948 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1951 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1952 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1956 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1958 OnButtonApply(event);
1962 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1967 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1969 CWalletDB walletdb(pwalletMain->strWalletFile);
1971 int64 nPrevTransactionFee = nTransactionFee;
1972 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1973 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1975 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1977 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1978 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1981 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1983 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1984 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1985 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1988 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1990 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1991 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1994 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1996 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1997 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
2001 fUseProxy = m_checkBoxUseProxy->GetValue();
2002 walletdb.WriteSetting("fUseProxy", fUseProxy);
2004 addrProxy = GetProxyAddr();
2005 walletdb.WriteSetting("addrProxy", addrProxy);
2013 //////////////////////////////////////////////////////////////////////////////
2018 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2020 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2022 // Change (c) into UTF-8 or ANSI copyright symbol
2023 wxString str = m_staticTextMain->GetLabel();
2025 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2027 str.Replace("(c)", "\xA9");
2029 m_staticTextMain->SetLabel(str);
2031 // Resize on Linux to make the window fit the text.
2032 // The text was wrapped manually rather than using the Wrap setting because
2033 // the wrap would be too small on Linux and it can't be changed at this point.
2034 wxFont fontTmp = m_staticTextMain->GetFont();
2035 if (fontTmp.GetPointSize() > 8);
2036 fontTmp.SetPointSize(8);
2037 m_staticTextMain->SetFont(fontTmp);
2038 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2040 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2044 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2054 //////////////////////////////////////////////////////////////////////////////
2059 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2062 m_textCtrlAddress->SetValue(strAddress);
2063 m_choiceTransferType->SetSelection(0);
2064 m_bitmapCheckMark->Show(false);
2065 fEnabledPrev = true;
2066 m_textCtrlAddress->SetFocus();
2068 //// todo: should add a display of your balance for convenience
2070 wxFont fontTmp = m_staticTextInstructions->GetFont();
2071 if (fontTmp.GetPointSize() > 9);
2072 fontTmp.SetPointSize(9);
2073 m_staticTextInstructions->SetFont(fontTmp);
2076 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2080 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2083 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2088 SetIcon(wxICON(bitcoin));
2091 // Fixup the tab order
2092 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2093 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2097 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2099 // Reformat the amount
2101 if (m_textCtrlAmount->GetValue().Trim().empty())
2104 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2105 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2108 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2110 // Open address book
2111 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2112 if (dialog.ShowModal())
2113 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2116 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2118 // Copy clipboard to address box
2119 if (wxTheClipboard->Open())
2121 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2123 wxTextDataObject data;
2124 wxTheClipboard->GetData(data);
2125 m_textCtrlAddress->SetValue(data.GetText());
2127 wxTheClipboard->Close();
2131 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2133 static CCriticalSection cs_sendlock;
2134 TRY_CRITICAL_BLOCK(cs_sendlock)
2137 string strAddress = (string)m_textCtrlAddress->GetValue();
2141 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2143 wxMessageBox(_("Error in amount "), _("Send Coins"));
2146 if (nValue > pwalletMain->GetBalance())
2148 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2151 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2153 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2157 // Parse bitcoin address
2159 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
2161 if (fBitcoinAddress)
2163 CRITICAL_BLOCK(cs_main)
2164 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2166 bool fWasLocked = pwalletMain->IsLocked();
2167 if (!GetWalletPassphrase())
2170 // Send to bitcoin address
2171 CScript scriptPubKey;
2172 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
2174 string strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2176 wxMessageBox(_("Payment sent "), _("Sending..."));
2177 else if (strError == "ABORTED")
2180 pwalletMain->Lock();
2181 return; // leave send dialog open
2185 wxMessageBox(strError + " ", _("Sending..."));
2188 pwalletMain->Lock();
2193 pwalletMain->Lock();
2199 CAddress addr(strAddress);
2200 if (!addr.IsValid())
2202 wxMessageBox(_("Invalid address "), _("Send Coins"));
2207 wtx.mapValue["to"] = strAddress;
2209 // Send to IP address
2210 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2211 if (!pdialog->ShowModal())
2215 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2216 if (!pwalletMain->mapAddressBook.count(strAddress))
2217 pwalletMain->SetAddressBookName(strAddress, "");
2223 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2234 //////////////////////////////////////////////////////////////////////////////
2239 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
2244 start = wxDateTime::UNow();
2245 memset(pszStatus, 0, sizeof(pszStatus));
2252 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2254 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2257 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2258 m_textCtrlStatus->SetValue("");
2260 CreateThread(SendingDialogStartTransfer, this);
2263 CSendingDialog::~CSendingDialog()
2265 printf("~CSendingDialog()\n");
2268 void CSendingDialog::Close()
2270 // Last one out turn out the lights.
2271 // fWorkDone signals that work side is done and UI thread should call destroy.
2272 // fUIDone signals that UI window has closed and work thread should call destroy.
2273 // This allows the window to disappear and end modality when cancelled
2274 // without making the user wait for ConnectNode to return. The dialog object
2275 // hangs around in the background until the work thread exits.
2286 void CSendingDialog::OnClose(wxCloseEvent& event)
2288 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2295 wxCommandEvent cmdevent;
2296 OnButtonCancel(cmdevent);
2300 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2306 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2312 void CSendingDialog::OnPaint(wxPaintEvent& event)
2315 if (strlen(pszStatus) > 130)
2316 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2318 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2319 m_staticTextSending->SetFocus();
2321 m_buttonCancel->Enable(false);
2324 m_buttonOK->Enable(true);
2325 m_buttonOK->SetFocus();
2326 m_buttonCancel->Enable(false);
2328 if (fAbort && fCanCancel && IsShown())
2330 strcpy(pszStatus, _("CANCELLED"));
2331 m_buttonOK->Enable(true);
2332 m_buttonOK->SetFocus();
2333 m_buttonCancel->Enable(false);
2334 m_buttonCancel->SetLabel(_("Cancelled"));
2336 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2342 // Everything from here on is not in the UI thread and must only communicate
2343 // with the rest of the dialog through variables and calling repaint.
2346 void CSendingDialog::Repaint()
2350 GetEventHandler()->AddPendingEvent(event);
2353 bool CSendingDialog::Status()
2360 if (fAbort && fCanCancel)
2362 memset(pszStatus, 0, 10);
2363 strcpy(pszStatus, _("CANCELLED"));
2371 bool CSendingDialog::Status(const string& str)
2376 // This can be read by the UI thread at any time,
2377 // so copy in a way that can be read cleanly at all times.
2378 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2379 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2385 bool CSendingDialog::Error(const string& str)
2389 Status(string(_("Error: ")) + str);
2393 void SendingDialogStartTransfer(void* parg)
2395 ((CSendingDialog*)parg)->StartTransfer();
2398 void CSendingDialog::StartTransfer()
2400 // Make sure we have enough money
2401 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2403 Error(_("Insufficient funds"));
2407 // We may have connected already for product details
2408 if (!Status(_("Connecting...")))
2410 CNode* pnode = ConnectNode(addr, 15 * 60);
2413 Error(_("Unable to connect"));
2417 // Send order to seller, with response going to OnReply2 via event handler
2418 if (!Status(_("Requesting public key...")))
2420 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2423 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2425 ((CSendingDialog*)parg)->OnReply2(vRecv);
2428 void CSendingDialog::OnReply2(CDataStream& vRecv)
2430 if (!Status(_("Received public key...")))
2433 CScript scriptPubKey;
2442 vRecv >> strMessage;
2444 Error(_("Recipient is not accepting transactions sent by IP address"));
2446 Error(_("Transfer was not accepted"));
2447 //// todo: enlarge the window and enable a hidden white box to put seller's message
2450 vRecv >> scriptPubKey;
2454 //// what do we want to do about this?
2455 Error(_("Invalid response received"));
2459 // Pause to give the user a chance to cancel
2460 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2467 CRITICAL_BLOCK(cs_main)
2470 if (!Status(_("Creating transaction...")))
2472 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2474 Error(_("Insufficient funds"));
2478 CReserveKey reservekey(pwalletMain);
2480 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2482 bool fWasLocked = pwalletMain->IsLocked();
2483 if (!GetWalletPassphrase())
2486 if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2488 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2489 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()));
2491 Error(_("Transaction creation failed"));
2496 pwalletMain->Lock();
2500 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2502 Error(_("Transaction aborted"));
2506 // Make sure we're still connected
2507 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2510 Error(_("Lost connection, transaction cancelled"));
2514 // Last chance to cancel
2526 if (!Status(_("Sending payment...")))
2530 if (!pwalletMain->CommitTransaction(wtx, reservekey))
2532 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."));
2536 // Send payment tx to seller, with response going to OnReply3 via event handler
2537 CWalletTx wtxSend = wtx;
2538 wtxSend.fFromMe = false;
2539 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2541 Status(_("Waiting for confirmation..."));
2546 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2548 ((CSendingDialog*)parg)->OnReply3(vRecv);
2551 void CSendingDialog::OnReply3(CDataStream& vRecv)
2559 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2560 "The transaction is recorded and will credit to the recipient,\n"
2561 "but the comment information will be blank."));
2567 //// what do we want to do about this?
2568 Error(_("Payment was sent, but an invalid response was received"));
2574 Status(_("Payment completed"));
2582 //////////////////////////////////////////////////////////////////////////////
2584 // CAddressBookDialog
2587 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2590 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2593 // Set initially selected page
2594 wxNotebookEvent event;
2595 event.SetSelection(nPageIn);
2596 OnNotebookPageChanged(event);
2597 m_notebook->ChangeSelection(nPageIn);
2599 fDuringSend = fDuringSendIn;
2601 m_buttonCancel->Show(false);
2604 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2606 wxIcon iconAddressBook;
2607 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2608 SetIcon(iconAddressBook);
2612 SetIcon(wxICON(bitcoin));
2615 // Init column headers
2616 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2617 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2618 m_listCtrlSending->SetFocus();
2619 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2620 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2621 m_listCtrlReceiving->SetFocus();
2623 // Fill listctrl with address book data
2624 CRITICAL_BLOCK(pwalletMain->cs_KeyStore)
2625 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2627 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2628 BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook)
2630 string strAddress = item.first;
2631 string strName = item.second;
2633 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2634 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2635 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2636 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2637 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2642 wxString CAddressBookDialog::GetSelectedAddress()
2644 int nIndex = GetSelection(m_listCtrl);
2647 return GetItemText(m_listCtrl, nIndex, 1);
2650 wxString CAddressBookDialog::GetSelectedSendingAddress()
2652 int nIndex = GetSelection(m_listCtrlSending);
2655 return GetItemText(m_listCtrlSending, nIndex, 1);
2658 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2660 int nIndex = GetSelection(m_listCtrlReceiving);
2663 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2666 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2669 nPage = event.GetSelection();
2670 if (nPage == SENDING)
2671 m_listCtrl = m_listCtrlSending;
2672 else if (nPage == RECEIVING)
2673 m_listCtrl = m_listCtrlReceiving;
2674 m_buttonDelete->Show(nPage == SENDING);
2675 m_buttonCopy->Show(nPage == RECEIVING);
2677 m_listCtrl->SetFocus();
2680 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2682 // Update address book with edited name
2684 if (event.IsEditCancelled())
2686 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2687 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2688 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2689 pframeMain->RefreshListCtrl();
2692 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2695 if (nPage == RECEIVING)
2696 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2699 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2704 // Doubleclick returns selection
2705 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2709 // Doubleclick edits item
2710 wxCommandEvent event2;
2711 OnButtonEdit(event2);
2714 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2716 if (nPage != SENDING)
2718 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2720 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2722 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2723 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2724 pwalletMain->DelAddressBookName(strAddress);
2725 m_listCtrl->DeleteItem(nIndex);
2728 pframeMain->RefreshListCtrl();
2731 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2733 // Copy address box to clipboard
2734 if (wxTheClipboard->Open())
2736 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2737 wxTheClipboard->Close();
2741 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2744 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2746 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2750 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2752 int nIndex = GetSelection(m_listCtrl);
2755 string strName = (string)m_listCtrl->GetItemText(nIndex);
2756 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2757 string strAddressOrg = strAddress;
2759 if (nPage == SENDING)
2761 // Ask name and address
2764 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2765 if (!dialog.ShowModal())
2767 strName = dialog.GetValue1();
2768 strAddress = dialog.GetValue2();
2770 while (CheckIfMine(strAddress, _("Edit Address")));
2773 else if (nPage == RECEIVING)
2776 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2777 if (!dialog.ShowModal())
2779 strName = dialog.GetValue();
2783 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2785 if (strAddress != strAddressOrg)
2786 pwalletMain->DelAddressBookName(strAddressOrg);
2787 pwalletMain->SetAddressBookName(strAddress, strName);
2789 m_listCtrl->SetItem(nIndex, 1, strAddress);
2790 m_listCtrl->SetItemText(nIndex, strName);
2791 pframeMain->RefreshListCtrl();
2794 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2799 if (nPage == SENDING)
2801 // Ask name and address
2804 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2805 if (!dialog.ShowModal())
2807 strName = dialog.GetValue1();
2808 strAddress = dialog.GetValue2();
2810 while (CheckIfMine(strAddress, _("Add Address")));
2812 else if (nPage == RECEIVING)
2815 CGetTextFromUserDialog dialog(this,
2816 _("New Receiving Address"),
2817 _("You should use a new address for each payment you receive.\n\nLabel"),
2819 if (!dialog.ShowModal())
2821 strName = dialog.GetValue();
2823 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2825 bool fWasLocked = pwalletMain->IsLocked();
2826 if (!GetWalletPassphrase())
2830 strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
2833 pwalletMain->Lock();
2837 // Add to list and select it
2838 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2839 pwalletMain->SetAddressBookName(strAddress, strName);
2840 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2841 SetSelection(m_listCtrl, nIndex);
2842 m_listCtrl->SetFocus();
2843 if (nPage == SENDING)
2844 pframeMain->RefreshListCtrl();
2847 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2850 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2853 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2859 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2870 //////////////////////////////////////////////////////////////////////////////
2877 ID_TASKBAR_RESTORE = 10001,
2880 ID_TASKBAR_GENERATE,
2884 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2885 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2886 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2887 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2888 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2889 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2890 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2893 void CMyTaskBarIcon::Show(bool fShow)
2895 static char pszPrevTip[200];
2898 string strTooltip = _("Bitcoin");
2899 if (fGenerateBitcoins)
2900 strTooltip = _("Bitcoin - Generating");
2901 if (fGenerateBitcoins && vNodes.empty())
2902 strTooltip = _("Bitcoin - (not connected)");
2904 // Optimization, only update when changed, using char array to be reentrant
2905 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2907 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2909 // somehow it'll choose the wrong size and scale it down if
2910 // we use the main icon, so we hand it one with only 16x16
2911 SetIcon(wxICON(favicon), strTooltip);
2913 SetIcon(bitcoin80_xpm, strTooltip);
2919 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2924 void CMyTaskBarIcon::Hide()
2929 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2934 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2939 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2942 CSendDialog dialog(pframeMain);
2946 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2948 // Since it's modal, get the main window to do it
2949 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2950 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2953 void CMyTaskBarIcon::Restore()
2956 wxIconizeEvent event(0, false);
2957 pframeMain->GetEventHandler()->AddPendingEvent(event);
2958 pframeMain->Iconize(false);
2959 pframeMain->Raise();
2962 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2964 event.Check(fGenerateBitcoins);
2967 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2969 pframeMain->Close(true);
2972 void CMyTaskBarIcon::UpdateTooltip()
2974 if (IsIconInstalled())
2978 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2980 wxMenu* pmenu = new wxMenu;
2981 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2982 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2983 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2984 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2985 pmenu->AppendSeparator();
2986 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2996 //////////////////////////////////////////////////////////////////////////////
3001 void CreateMainWindow()
3003 pframeMain = new CMainFrame(NULL);
3004 if (GetBoolArg("-min"))
3005 pframeMain->Iconize(true);
3006 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3007 if (!GetBoolArg("-minimizetotray"))
3008 fMinimizeToTray = false;
3010 pframeMain->Show(true); // have to show first to get taskbar button to hide
3011 if (fMinimizeToTray && pframeMain->IsIconized())
3012 fClosedToTray = true;
3013 pframeMain->Show(!fClosedToTray);
3014 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3015 CreateThread(ThreadDelayedRepaint, NULL);
3019 // Define a new application
3020 class CMyApp : public wxApp
3029 // Hook Initialize so we can start without GUI
3030 virtual bool Initialize(int& argc, wxChar** argv);
3032 // 2nd-level exception handling: we get all the exceptions occurring in any
3033 // event handler here
3034 virtual bool OnExceptionInMainLoop();
3036 // 3rd, and final, level exception handling: whenever an unhandled
3037 // exception is caught, this function is called
3038 virtual void OnUnhandledException();
3040 // and now for something different: this function is called in case of a
3041 // crash (e.g. dereferencing null pointer, division by 0, ...)
3042 virtual void OnFatalException();
3045 IMPLEMENT_APP(CMyApp)
3047 bool CMyApp::Initialize(int& argc, wxChar** argv)
3049 for (int i = 1; i < argc; i++)
3050 if (!IsSwitchChar(argv[i][0]))
3051 fCommandLine = true;
3055 // wxApp::Initialize will remove environment-specific parameters,
3056 // so it's too early to call ParseParameters yet
3057 for (int i = 1; i < argc; i++)
3059 wxString str = argv[i];
3061 if (str.size() >= 1 && str[0] == '/')
3063 char pszLower[MAX_PATH];
3064 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3068 if (str == "-daemon")
3074 if (fDaemon || fCommandLine)
3076 // Call the original Initialize while suppressing error messages
3077 // and ignoring failure. If unable to initialize GTK, it fails
3078 // near the end so hopefully the last few things don't matter.
3081 wxApp::Initialize(argc, argv);
3090 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3094 pthread_exit((void*)0);
3096 pid_t sid = setsid();
3098 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3105 return wxApp::Initialize(argc, argv);
3108 bool CMyApp::OnInit()
3110 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3111 // Disable malfunctioning wxWidgets debug assertion
3112 extern int g_isPainting;
3113 g_isPainting = 10000;
3115 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3116 SetAppName("Bitcoin");
3118 SetAppName("bitcoin");
3122 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3123 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3124 class wxMBConv_win32 : public wxMBConv
3128 size_t m_minMBCharWidth;
3130 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3131 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3135 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3136 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3137 g_locale.AddCatalogLookupPathPrefix("locale");
3139 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3140 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3142 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3143 g_locale.AddCatalog("bitcoin");
3146 HDC hdc = GetDC(NULL);
3149 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3150 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3151 ReleaseDC(NULL, hdc);
3155 return AppInit(argc, argv);
3158 int CMyApp::OnExit()
3161 return wxApp::OnExit();
3164 bool CMyApp::OnExceptionInMainLoop()
3170 catch (std::exception& e)
3172 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3173 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3179 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3180 wxLogWarning("Unknown exception");
3187 void CMyApp::OnUnhandledException()
3189 // this shows how we may let some exception propagate uncaught
3194 catch (std::exception& e)
3196 PrintException(&e, "CMyApp::OnUnhandledException()");
3197 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3203 PrintException(NULL, "CMyApp::OnUnhandledException()");
3204 wxLogWarning("Unknown exception");
3210 void CMyApp::OnFatalException()
3212 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);