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 vector<unsigned char> vchPubKey;
242 if (!pwalletMain->GetPubKey(hash160, vchPubKey))
244 pwalletMain->SetDefaultKey(vchPubKey);
245 pframeMain->m_textCtrlAddress->SetValue(strAddress);
249 bool GetWalletPassphrase()
251 if (pwalletMain->IsLocked())
253 string strWalletPass;
254 strWalletPass.reserve(100);
255 mlock(&strWalletPass[0], strWalletPass.capacity());
257 // obtain current wallet encrypt/decrypt key, from passphrase
258 // Note that the passphrase is not mlock()d during this entry and could potentially
259 // be obtained from disk long after bitcoin has run.
260 strWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
261 _("Passphrase")).ToStdString();
263 if (!strWalletPass.size())
265 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
266 munlock(&strWalletPass[0], strWalletPass.capacity());
267 wxMessageBox(_("Please supply the current wallet decryption passphrase."), "Bitcoin");
271 if (!pwalletMain->Unlock(strWalletPass))
273 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
274 munlock(&strWalletPass[0], strWalletPass.capacity());
275 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin");
278 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
279 munlock(&strWalletPass[0], strWalletPass.capacity());
293 //////////////////////////////////////////////////////////////////////////////
298 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
300 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
302 // Set initially selected page
303 wxNotebookEvent event;
304 event.SetSelection(0);
305 OnNotebookPageChanged(event);
306 m_notebook->ChangeSelection(0);
309 fRefreshListCtrl = false;
310 fRefreshListCtrlRunning = false;
311 fOnSetFocusAddress = false;
313 m_choiceFilter->SetSelection(0);
314 double dResize = nScaleX;
316 SetIcon(wxICON(bitcoin));
317 SetSize(dResize * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
319 SetIcon(bitcoin80_xpm);
320 SetBackgroundColour(m_toolBar->GetBackgroundColour());
321 wxFont fontTmp = m_staticText41->GetFont();
322 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
323 m_staticTextBalance->SetFont(fontTmp);
324 m_staticTextBalance->SetSize(140, 17);
325 // resize to fit ubuntu's huge default font
327 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
329 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
330 m_listCtrl->SetFocus();
331 ptaskbaricon = new CMyTaskBarIcon();
333 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
334 // to their standard places, leaving these menus empty.
335 GetMenuBar()->Remove(2); // remove Help menu
336 GetMenuBar()->Remove(0); // remove File menu
339 // Init column headers
340 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
341 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
347 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
348 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
350 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
351 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
352 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
353 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
354 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
355 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
356 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
360 int pnWidths[3] = { -100, 88, 300 };
362 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
363 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
365 m_statusBar->SetFieldsCount(3, pnWidths);
367 // Fill your address text box
368 vector<unsigned char> vchPubKey;
369 if (CWalletDB(pwalletMain->strWalletFile,"r").ReadDefaultKey(vchPubKey))
370 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
372 if (pwalletMain->IsCrypted())
373 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
375 m_menuOptions->Remove(m_menuOptionsChangeWalletPassphrase);
377 // Fill listctrl with wallet transactions
381 CMainFrame::~CMainFrame()
388 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
391 nPage = event.GetSelection();
394 m_listCtrl = m_listCtrlAll;
395 fShowGenerated = true;
397 fShowReceived = true;
399 else if (nPage == SENTRECEIVED)
401 m_listCtrl = m_listCtrlSentReceived;
402 fShowGenerated = false;
404 fShowReceived = true;
406 else if (nPage == SENT)
408 m_listCtrl = m_listCtrlSent;
409 fShowGenerated = false;
411 fShowReceived = false;
413 else if (nPage == RECEIVED)
415 m_listCtrl = m_listCtrlReceived;
416 fShowGenerated = false;
418 fShowReceived = true;
421 m_listCtrl->SetFocus();
424 void CMainFrame::OnClose(wxCloseEvent& event)
426 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
428 // Divert close to minimize
430 fClosedToTray = true;
436 CreateThread(Shutdown, NULL);
440 void CMainFrame::OnIconize(wxIconizeEvent& event)
443 // Hide the task bar button when minimized.
444 // Event is sent when the frame is minimized or restored.
445 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
446 // to get rid of the deprecated warning. Just ignore it.
447 if (!event.Iconized())
448 fClosedToTray = false;
449 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
450 if (GetBoolArg("-minimizetotray")) {
452 // The tray icon sometimes disappears on ubuntu karmic
453 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
454 // Reports of CPU peg on 64-bit linux
455 if (fMinimizeToTray && event.Iconized())
456 fClosedToTray = true;
457 Show(!fClosedToTray);
458 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
459 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
464 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
468 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
469 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
472 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
474 // Hidden columns not resizeable
475 if (event.GetColumn() <= 1 && !fDebug)
481 int CMainFrame::GetSortIndex(const string& strSort)
486 // The wx generic listctrl implementation used on GTK doesn't sort,
487 // so we have to do it ourselves. Remember, we sort in reverse order.
488 // In the wx generic implementation, they store the list of items
489 // in a vector, so indexed lookups are fast, but inserts are slower
490 // the closer they are to the top.
492 int high = m_listCtrl->GetItemCount();
495 int mid = low + ((high - low) / 2);
496 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
505 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)
507 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
508 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
511 if (!fNew && nIndex == -1)
513 string strHash = " " + hashKey.ToString();
514 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
515 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
519 // fNew is for blind insert, only use if you're sure it's new
520 if (fNew || nIndex == -1)
522 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
526 // If sort key changed, must delete and reinsert to make it relocate
527 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
529 m_listCtrl->DeleteItem(nIndex);
530 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
534 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
535 m_listCtrl->SetItem(nIndex, 2, str2);
536 m_listCtrl->SetItem(nIndex, 3, str3);
537 m_listCtrl->SetItem(nIndex, 4, str4);
538 m_listCtrl->SetItem(nIndex, 5, str5);
539 m_listCtrl->SetItem(nIndex, 6, str6);
540 m_listCtrl->SetItemData(nIndex, nData);
541 SetItemTextColour(m_listCtrl, nIndex, colour);
544 bool CMainFrame::DeleteLine(uint256 hashKey)
546 long nData = *(long*)&hashKey;
550 string strHash = " " + hashKey.ToString();
551 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
552 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
556 m_listCtrl->DeleteItem(nIndex);
561 string FormatTxStatus(const CWalletTx& wtx)
566 if (wtx.nLockTime < LOCKTIME_THRESHOLD)
567 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
569 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
573 int nDepth = wtx.GetDepthInMainChain();
574 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
575 return strprintf(_("%d/offline?"), nDepth);
577 return strprintf(_("%d/unconfirmed"), nDepth);
579 return strprintf(_("%d confirmations"), nDepth);
583 string SingleLine(const string& strIn)
586 bool fOneSpace = false;
587 BOOST_FOREACH(unsigned char c, strIn)
595 if (fOneSpace && !strOut.empty())
604 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
606 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
607 int64 nCredit = wtx.GetCredit(true);
608 int64 nDebit = wtx.GetDebit();
609 int64 nNet = nCredit - nDebit;
610 uint256 hash = wtx.GetHash();
611 string strStatus = FormatTxStatus(wtx);
612 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
613 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
614 map<string, string> mapValue = wtx.mapValue;
615 wtx.nLinesDisplayed = 1;
619 if (wtx.IsCoinBase())
621 // Don't show generated coin until confirmed by at least one block after it
622 // so we don't get the user's hopes up until it looks like it's probably accepted.
624 // It is not an error when generated blocks are not accepted. By design,
625 // some percentage of blocks, like 10% or more, will end up not accepted.
626 // This is the normal mechanism by which the network copes with latency.
628 // We display regular transactions right away before any confirmation
629 // because they can always get into some block eventually. Generated coins
630 // are special because if their block is not accepted, they are not valid.
632 if (wtx.GetDepthInMainChain() < 2)
634 wtx.nLinesDisplayed = 0;
642 // Find the block the tx is in
643 CBlockIndex* pindex = NULL;
644 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
645 if (mi != mapBlockIndex.end())
646 pindex = (*mi).second;
648 // Sort order, unrecorded transactions sort to the top
649 string strSort = strprintf("%010d-%01d-%010u",
650 (pindex ? pindex->nHeight : INT_MAX),
651 (wtx.IsCoinBase() ? 1 : 0),
655 if (nNet > 0 || wtx.IsCoinBase())
660 string strDescription;
661 if (wtx.IsCoinBase())
664 strDescription = _("Generated");
667 int64 nUnmatured = 0;
668 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
669 nUnmatured += pwalletMain->GetCredit(txout);
670 if (wtx.IsInMainChain())
672 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
674 // Check if the block was requested by anyone
675 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
676 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
680 strDescription = _("Generated (not accepted)");
684 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
686 // Received by IP connection
689 if (!mapValue["from"].empty())
690 strDescription += _("From: ") + mapValue["from"];
691 if (!mapValue["message"].empty())
693 if (!strDescription.empty())
694 strDescription += " - ";
695 strDescription += mapValue["message"];
700 // Received by Bitcoin Address
703 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
705 if (pwalletMain->IsMine(txout))
708 if (ExtractHash160(txout.scriptPubKey, pwalletMain, hash160))
710 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
712 //strDescription += _("Received payment to ");
713 //strDescription += _("Received with address ");
714 strDescription += _("Received with: ");
715 string strAddress = Hash160ToAddress(hash160);
716 map<string, string>::iterator mi = pwalletMain->mapAddressBook.find(strAddress);
717 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
719 string strLabel = (*mi).second;
720 strDescription += strAddress.substr(0,12) + "... ";
721 strDescription += "(" + strLabel + ")";
724 strDescription += strAddress;
732 string strCredit = FormatMoney(nNet, true);
734 strCredit = "[" + strCredit + "]";
736 InsertLine(fNew, nIndex, hash, strSort, colour,
738 nTime ? DateTimeStr(nTime) : "",
739 SingleLine(strDescription),
745 bool fAllFromMe = true;
746 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
747 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
749 bool fAllToMe = true;
750 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
751 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
753 if (fAllFromMe && fAllToMe)
756 int64 nChange = wtx.GetChange();
757 InsertLine(fNew, nIndex, hash, strSort, colour,
759 nTime ? DateTimeStr(nTime) : "",
760 _("Payment to yourself"),
761 FormatMoney(-(nDebit - nChange), true),
762 FormatMoney(nCredit - nChange, true));
772 int64 nTxFee = nDebit - wtx.GetValueOut();
773 wtx.nLinesDisplayed = 0;
774 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
776 const CTxOut& txout = wtx.vout[nOut];
777 if (pwalletMain->IsMine(txout))
781 if (!mapValue["to"].empty())
784 strAddress = mapValue["to"];
788 // Sent to Bitcoin Address
790 if (ExtractHash160(txout.scriptPubKey, pwalletMain, hash160))
791 strAddress = Hash160ToAddress(hash160);
794 string strDescription = _("To: ");
795 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
796 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
797 strDescription += pwalletMain->mapAddressBook[strAddress] + " ";
798 strDescription += strAddress;
799 if (!mapValue["message"].empty())
801 if (!strDescription.empty())
802 strDescription += " - ";
803 strDescription += mapValue["message"];
805 else if (!mapValue["comment"].empty())
807 if (!strDescription.empty())
808 strDescription += " - ";
809 strDescription += mapValue["comment"];
812 int64 nValue = txout.nValue;
819 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
821 nTime ? DateTimeStr(nTime) : "",
822 SingleLine(strDescription),
823 FormatMoney(-nValue, true),
826 wtx.nLinesDisplayed++;
832 // Mixed debit transaction, can't break down payees
834 bool fAllMine = true;
835 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
836 fAllMine = fAllMine && pwalletMain->IsMine(txout);
837 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
838 fAllMine = fAllMine && pwalletMain->IsMine(txin);
840 InsertLine(fNew, nIndex, hash, strSort, colour,
842 nTime ? DateTimeStr(nTime) : "",
844 FormatMoney(nNet, true),
852 void CMainFrame::RefreshListCtrl()
854 fRefreshListCtrl = true;
858 void CMainFrame::OnIdle(wxIdleEvent& event)
860 if (fRefreshListCtrl)
862 // Collect list of wallet transactions and sort newest first
863 bool fEntered = false;
864 vector<pair<unsigned int, uint256> > vSorted;
865 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
867 printf("RefreshListCtrl starting\n");
869 fRefreshListCtrl = false;
870 pwalletMain->vWalletUpdated.clear();
872 // Do the newest transactions first
873 vSorted.reserve(pwalletMain->mapWallet.size());
874 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
876 const CWalletTx& wtx = (*it).second;
877 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
878 vSorted.push_back(make_pair(nTime, (*it).first));
880 m_listCtrl->DeleteAllItems();
885 sort(vSorted.begin(), vSorted.end());
888 for (int i = 0; i < vSorted.size();)
892 bool fEntered = false;
893 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
896 uint256& hash = vSorted[i++].second;
897 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
898 if (mi != pwalletMain->mapWallet.end())
899 InsertTransaction((*mi).second, true);
901 if (!fEntered || i == 100 || i % 500 == 0)
905 printf("RefreshListCtrl done\n");
907 // Update transaction total display
912 // Check for time updates
913 static int64 nLastTime;
914 if (GetTime() > nLastTime + 30)
916 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
918 nLastTime = GetTime();
919 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
921 CWalletTx& wtx = (*it).second;
922 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
923 InsertTransaction(wtx, false);
930 void CMainFrame::RefreshStatusColumn()
933 static CBlockIndex* pindexLastBest;
934 static unsigned int nLastRefreshed;
936 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
937 if (nTop == nLastTop && pindexLastBest == pindexBest)
940 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
943 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
945 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
947 // If no updates, only need to do the part that moved onto the screen
948 if (nStart >= nLastTop && nStart < nLastTop + 100)
949 nStart = nLastTop + 100;
950 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
954 pindexLastBest = pindexBest;
955 nLastRefreshed = nListViewUpdated;
957 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
959 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
960 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
961 if (mi == pwalletMain->mapWallet.end())
963 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
966 CWalletTx& wtx = (*mi).second;
967 if (wtx.IsCoinBase() ||
968 wtx.GetTxTime() != wtx.nTimeDisplayed ||
969 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
971 if (!InsertTransaction(wtx, false, nIndex))
972 m_listCtrl->DeleteItem(nIndex--);
976 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
982 void CMainFrame::OnPaint(wxPaintEvent& event)
993 unsigned int nNeedRepaint = 0;
994 unsigned int nLastRepaint = 0;
995 int64 nLastRepaintTime = 0;
996 int64 nRepaintInterval = 500;
998 void ThreadDelayedRepaint(void* parg)
1002 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1004 nLastRepaint = nNeedRepaint;
1007 printf("DelayedRepaint\n");
1009 pframeMain->fRefresh = true;
1010 pframeMain->GetEventHandler()->AddPendingEvent(event);
1013 Sleep(nRepaintInterval);
1017 void MainFrameRepaint()
1019 // This is called by network code that shouldn't access pframeMain
1020 // directly because it could still be running after the UI is closed.
1023 // Don't repaint too often
1024 static int64 nLastRepaintRequest;
1025 if (GetTimeMillis() - nLastRepaintRequest < 100)
1030 nLastRepaintRequest = GetTimeMillis();
1032 printf("MainFrameRepaint\n");
1034 pframeMain->fRefresh = true;
1035 pframeMain->GetEventHandler()->AddPendingEvent(event);
1039 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
1041 // Skip lets the listctrl do the paint, we're just hooking the message
1045 ptaskbaricon->UpdateTooltip();
1050 static int nTransactionCount;
1051 bool fPaintedBalance = false;
1052 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1054 nLastRepaint = nNeedRepaint;
1055 nLastRepaintTime = GetTimeMillis();
1057 // Update listctrl contents
1058 if (!pwalletMain->vWalletUpdated.empty())
1060 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1063 if (m_listCtrl->GetItemCount())
1064 strTop = (string)m_listCtrl->GetItemText(0);
1065 BOOST_FOREACH(uint256 hash, pwalletMain->vWalletUpdated)
1067 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1068 if (mi != pwalletMain->mapWallet.end())
1069 InsertTransaction((*mi).second, false);
1071 pwalletMain->vWalletUpdated.clear();
1072 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1073 m_listCtrl->ScrollList(0, INT_MIN/2);
1078 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1080 fPaintedBalance = true;
1081 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
1083 // Count hidden and multi-line transactions
1084 nTransactionCount = 0;
1085 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1087 CWalletTx& wtx = (*it).second;
1088 nTransactionCount += wtx.nLinesDisplayed;
1092 if (!pwalletMain->vWalletUpdated.empty() || !fPaintedBalance)
1095 // Update status column of visible items only
1096 RefreshStatusColumn();
1098 // Update status bar
1099 static string strPrevWarning;
1100 string strWarning = GetWarnings("statusbar");
1101 if (strWarning != "")
1102 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1103 else if (strPrevWarning != "")
1104 m_statusBar->SetStatusText("", 0);
1105 strPrevWarning = strWarning;
1108 if (fGenerateBitcoins)
1109 strGen = _(" Generating");
1110 if (fGenerateBitcoins && vNodes.empty())
1111 strGen = _("(not connected)");
1112 m_statusBar->SetStatusText(strGen, 1);
1114 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1115 m_statusBar->SetStatusText(strStatus, 2);
1117 // Update receiving address
1118 string strDefaultAddress = PubKeyToAddress(pwalletMain->vchDefaultKey);
1119 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1120 m_textCtrlAddress->SetValue(strDefaultAddress);
1124 void UIThreadCall(boost::function0<void> fn)
1126 // Call this with a function object created with bind.
1127 // bind needs all parameters to match the function's expected types
1128 // and all default parameters specified. Some examples:
1129 // UIThreadCall(bind(wxBell));
1130 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1131 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1134 wxCommandEvent event(wxEVT_UITHREADCALL);
1135 event.SetClientData((void*)new boost::function0<void>(fn));
1136 pframeMain->GetEventHandler()->AddPendingEvent(event);
1140 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1142 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1147 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1153 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1155 event.Check(fGenerateBitcoins);
1158 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1160 // Options->Your Receiving Addresses
1161 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1162 if (!dialog.ShowModal())
1166 void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event)
1168 // Options->Encrypt Wallet
1169 if (pwalletMain->IsCrypted())
1171 wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR);
1175 string strWalletPass;
1176 strWalletPass.reserve(100);
1177 mlock(&strWalletPass[0], strWalletPass.capacity());
1179 // obtain current wallet encrypt/decrypt key, from passphrase
1180 // Note that the passphrase is not mlock()d during this entry and could potentially
1181 // be obtained from disk long after bitcoin has run.
1182 strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."),
1183 _("Passphrase")).ToStdString();
1185 if (!strWalletPass.size())
1187 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1188 munlock(&strWalletPass[0], strWalletPass.capacity());
1189 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1193 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)
1196 string strWalletPassTest;
1197 strWalletPassTest.reserve(100);
1198 mlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1199 strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."),
1200 _("Passphrase")).ToStdString();
1202 if (strWalletPassTest != strWalletPass)
1204 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1205 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1206 munlock(&strWalletPass[0], strWalletPass.capacity());
1207 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1208 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1212 if (!pwalletMain->EncryptWallet(strWalletPass))
1214 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1215 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1216 munlock(&strWalletPass[0], strWalletPass.capacity());
1217 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1218 wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR);
1221 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1222 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1223 munlock(&strWalletPass[0], strWalletPass.capacity());
1224 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1225 wxMessageBox(_("Wallet Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1227 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
1228 m_menuOptions->Insert(m_menuOptions->GetMenuItemCount() - 1, m_menuOptionsChangeWalletPassphrase);
1231 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1233 // Options->Change Wallet Encryption Passphrase
1234 if (!pwalletMain->IsCrypted())
1236 wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1240 string strOldWalletPass;
1241 strOldWalletPass.reserve(100);
1242 mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1244 // obtain current wallet encrypt/decrypt key, from passphrase
1245 // Note that the passphrase is not mlock()d during this entry and could potentially
1246 // be obtained from disk long after bitcoin has run.
1247 strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1248 _("Passphrase")).ToStdString();
1250 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1252 bool fWasLocked = pwalletMain->IsLocked();
1253 pwalletMain->Lock();
1255 if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1257 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1258 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1259 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1264 pwalletMain->Lock();
1266 string strNewWalletPass;
1267 strNewWalletPass.reserve(100);
1268 mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1270 // obtain new wallet encrypt/decrypt key, from passphrase
1271 // Note that the passphrase is not mlock()d during this entry and could potentially
1272 // be obtained from disk long after bitcoin has run.
1273 strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1274 _("Passphrase")).ToStdString();
1276 if (!strNewWalletPass.size())
1278 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1279 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1280 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1281 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1282 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1286 string strNewWalletPassTest;
1287 strNewWalletPassTest.reserve(100);
1288 mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1290 // obtain new wallet encrypt/decrypt key, from passphrase
1291 // Note that the passphrase is not mlock()d during this entry and could potentially
1292 // be obtained from disk long after bitcoin has run.
1293 strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1294 _("Passphrase")).ToStdString();
1296 if (strNewWalletPassTest != strNewWalletPass)
1298 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1299 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1300 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1301 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1302 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1303 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1304 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1308 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1310 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1311 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1312 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1313 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1314 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1315 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1316 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1319 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1320 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1321 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1322 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1323 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1324 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1325 wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
1329 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1332 COptionsDialog dialog(this);
1336 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1339 CAboutDialog dialog(this);
1343 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1346 CSendDialog dialog(this);
1350 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1352 // Toolbar: Address Book
1353 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1354 if (dialogAddr.ShowModal() == 2)
1357 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1358 dialogSend.ShowModal();
1362 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1364 // Automatically select-all when entering window
1366 m_textCtrlAddress->SetSelection(-1, -1);
1367 fOnSetFocusAddress = true;
1370 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1373 if (fOnSetFocusAddress)
1374 m_textCtrlAddress->SetSelection(-1, -1);
1375 fOnSetFocusAddress = false;
1378 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1381 CGetTextFromUserDialog dialog(this,
1382 _("New Receiving Address"),
1383 _("You should use a new address for each payment you receive.\n\nLabel"),
1385 if (!dialog.ShowModal())
1387 string strName = dialog.GetValue();
1390 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1392 bool fWasLocked = pwalletMain->IsLocked();
1393 if (!GetWalletPassphrase())
1397 strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
1400 pwalletMain->Lock();
1404 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1405 pwalletMain->SetAddressBookName(strAddress, strName);
1406 SetDefaultReceivingAddress(strAddress);
1409 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1411 // Copy address box to clipboard
1412 if (wxTheClipboard->Open())
1414 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1415 wxTheClipboard->Close();
1419 void CMainFrame::OnListItemActivated(wxListEvent& event)
1421 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1423 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1425 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1426 if (mi == pwalletMain->mapWallet.end())
1428 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1433 CTxDetailsDialog dialog(this, wtx);
1435 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1444 //////////////////////////////////////////////////////////////////////////////
1449 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1452 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1454 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1457 strHTML.reserve(4000);
1458 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1460 int64 nTime = wtx.GetTxTime();
1461 int64 nCredit = wtx.GetCredit();
1462 int64 nDebit = wtx.GetDebit();
1463 int64 nNet = nCredit - nDebit;
1467 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1468 int nRequests = wtx.GetRequestCount();
1469 if (nRequests != -1)
1472 strHTML += _(", has not been successfully broadcast yet");
1473 else if (nRequests == 1)
1474 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1476 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1480 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1486 if (wtx.IsCoinBase())
1488 strHTML += _("<b>Source:</b> Generated<br>");
1490 else if (!wtx.mapValue["from"].empty())
1492 // Online transaction
1493 if (!wtx.mapValue["from"].empty())
1494 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1498 // Offline transaction
1502 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1504 if (pwalletMain->IsMine(txout))
1507 if (ExtractHash160(txout.scriptPubKey, pwalletMain, hash160))
1509 string strAddress = Hash160ToAddress(hash160);
1510 if (pwalletMain->mapAddressBook.count(strAddress))
1512 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1513 strHTML += _("<b>To:</b> ");
1514 strHTML += HtmlEscape(strAddress);
1515 if (!pwalletMain->mapAddressBook[strAddress].empty())
1516 strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[strAddress] + ")";
1518 strHTML += _(" (yours)");
1533 if (!wtx.mapValue["to"].empty())
1535 // Online transaction
1536 strAddress = wtx.mapValue["to"];
1537 strHTML += _("<b>To:</b> ");
1538 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1539 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1540 strHTML += HtmlEscape(strAddress) + "<br>";
1547 if (wtx.IsCoinBase() && nCredit == 0)
1552 int64 nUnmatured = 0;
1553 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1554 nUnmatured += pwalletMain->GetCredit(txout);
1555 strHTML += _("<b>Credit:</b> ");
1556 if (wtx.IsInMainChain())
1557 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1559 strHTML += _("(not accepted)");
1567 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1571 bool fAllFromMe = true;
1572 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1573 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1575 bool fAllToMe = true;
1576 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1577 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1584 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1586 if (pwalletMain->IsMine(txout))
1589 if (wtx.mapValue["to"].empty())
1591 // Offline transaction
1593 if (ExtractHash160(txout.scriptPubKey, pwalletMain, hash160))
1595 string strAddress = Hash160ToAddress(hash160);
1596 strHTML += _("<b>To:</b> ");
1597 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1598 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1599 strHTML += strAddress;
1604 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1610 int64 nChange = wtx.GetChange();
1611 int64 nValue = nCredit - nChange;
1612 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1613 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1616 int64 nTxFee = nDebit - wtx.GetValueOut();
1618 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1623 // Mixed debit transaction
1625 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1626 if (pwalletMain->IsMine(txin))
1627 strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1628 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1629 if (pwalletMain->IsMine(txout))
1630 strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1634 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1640 if (!wtx.mapValue["message"].empty())
1641 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1642 if (!wtx.mapValue["comment"].empty())
1643 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1645 if (wtx.IsCoinBase())
1646 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>";
1654 strHTML += "<hr><br>debug print<br><br>";
1655 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1656 if (pwalletMain->IsMine(txin))
1657 strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1658 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1659 if (pwalletMain->IsMine(txout))
1660 strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1662 strHTML += "<br><b>Transaction:</b><br>";
1663 strHTML += HtmlEscape(wtx.ToString(), true);
1665 strHTML += "<br><b>Inputs:</b><br>";
1666 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1668 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1670 COutPoint prevout = txin.prevout;
1671 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1672 if (mi != pwalletMain->mapWallet.end())
1674 const CWalletTx& prev = (*mi).second;
1675 if (prevout.n < prev.vout.size())
1677 strHTML += HtmlEscape(prev.ToString(), true);
1678 strHTML += " " + FormatTxStatus(prev) + ", ";
1679 strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1688 strHTML += "</font></html>";
1689 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1690 m_htmlWin->SetPage(strHTML);
1691 m_buttonOK->SetFocus();
1695 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1705 //////////////////////////////////////////////////////////////////////////////
1711 string StartupShortcutPath()
1713 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1716 bool GetStartOnSystemStartup()
1718 return filesystem::exists(StartupShortcutPath().c_str());
1721 void SetStartOnSystemStartup(bool fAutoStart)
1723 // If the shortcut exists already, remove it for updating
1724 remove(StartupShortcutPath().c_str());
1730 // Get a pointer to the IShellLink interface.
1731 IShellLink* psl = NULL;
1732 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1733 CLSCTX_INPROC_SERVER, IID_IShellLink,
1734 reinterpret_cast<void**>(&psl));
1736 if (SUCCEEDED(hres))
1738 // Get the current executable path
1739 TCHAR pszExePath[MAX_PATH];
1740 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1742 // Set the path to the shortcut target
1743 psl->SetPath(pszExePath);
1744 PathRemoveFileSpec(pszExePath);
1745 psl->SetWorkingDirectory(pszExePath);
1746 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1748 // Query IShellLink for the IPersistFile interface for
1749 // saving the shortcut in persistent storage.
1750 IPersistFile* ppf = NULL;
1751 hres = psl->QueryInterface(IID_IPersistFile,
1752 reinterpret_cast<void**>(&ppf));
1753 if (SUCCEEDED(hres))
1755 WCHAR pwsz[MAX_PATH];
1756 // Ensure that the string is ANSI.
1757 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1758 // Save the link by calling IPersistFile::Save.
1759 hres = ppf->Save(pwsz, TRUE);
1768 #elif defined(__WXGTK__)
1770 // Follow the Desktop Application Autostart Spec:
1771 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1773 boost::filesystem::path GetAutostartDir()
1775 namespace fs = boost::filesystem;
1777 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1778 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1779 char* pszHome = getenv("HOME");
1780 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1784 boost::filesystem::path GetAutostartFilePath()
1786 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1789 bool GetStartOnSystemStartup()
1791 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1792 if (!optionFile.good())
1794 // Scan through file for "Hidden=true":
1796 while (!optionFile.eof())
1798 getline(optionFile, line);
1799 if (line.find("Hidden") != string::npos &&
1800 line.find("true") != string::npos)
1808 void SetStartOnSystemStartup(bool fAutoStart)
1812 unlink(GetAutostartFilePath().native_file_string().c_str());
1816 char pszExePath[MAX_PATH+1];
1817 memset(pszExePath, 0, sizeof(pszExePath));
1818 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1821 boost::filesystem::create_directories(GetAutostartDir());
1823 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1824 if (!optionFile.good())
1826 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1829 // Write a bitcoin.desktop file to the autostart directory:
1830 optionFile << "[Desktop Entry]\n";
1831 optionFile << "Type=Application\n";
1832 optionFile << "Name=Bitcoin\n";
1833 optionFile << "Exec=" << pszExePath << "\n";
1834 optionFile << "Terminal=false\n";
1835 optionFile << "Hidden=false\n";
1841 // TODO: OSX startup stuff; see:
1842 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1844 bool GetStartOnSystemStartup() { return false; }
1845 void SetStartOnSystemStartup(bool fAutoStart) { }
1854 //////////////////////////////////////////////////////////////////////////////
1859 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1861 // Set up list box of page choices
1862 m_listBox->Append(_("Main"));
1863 //m_listBox->Append(_("Test 2"));
1864 m_listBox->SetSelection(0);
1867 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1869 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1871 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1872 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1873 if (!GetBoolArg("-minimizetotray"))
1875 // Minimize to tray is just too buggy on Linux
1876 fMinimizeToTray = false;
1877 m_checkBoxMinimizeToTray->SetValue(false);
1878 m_checkBoxMinimizeToTray->Enable(false);
1879 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1882 #ifdef __WXMAC_OSX__
1883 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1887 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1888 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1889 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1890 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1892 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1894 m_checkBoxUseUPnP->Enable(false);
1895 m_checkBoxUseProxy->SetValue(fUseProxy);
1896 m_textCtrlProxyIP->Enable(fUseProxy);
1897 m_textCtrlProxyPort->Enable(fUseProxy);
1898 m_staticTextProxyIP->Enable(fUseProxy);
1899 m_staticTextProxyPort->Enable(fUseProxy);
1900 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1901 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1903 m_buttonOK->SetFocus();
1906 void COptionsDialog::SelectPage(int nPage)
1908 m_panelMain->Show(nPage == 0);
1909 m_panelTest2->Show(nPage == 1);
1911 m_scrolledWindow->Layout();
1912 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1915 void COptionsDialog::OnListBox(wxCommandEvent& event)
1917 SelectPage(event.GetSelection());
1920 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1923 int64 nTmp = nTransactionFee;
1924 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1925 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1928 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1930 m_textCtrlProxyIP->Enable(event.IsChecked());
1931 m_textCtrlProxyPort->Enable(event.IsChecked());
1932 m_staticTextProxyIP->Enable(event.IsChecked());
1933 m_staticTextProxyPort->Enable(event.IsChecked());
1936 CAddress COptionsDialog::GetProxyAddr()
1938 // Be careful about byte order, addr.ip and addr.port are big endian
1939 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1940 if (addr.ip == INADDR_NONE)
1941 addr.ip = addrProxy.ip;
1942 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1943 addr.port = htons(nPort);
1944 if (nPort <= 0 || nPort > USHRT_MAX)
1945 addr.port = addrProxy.port;
1949 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1952 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1953 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1957 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1959 OnButtonApply(event);
1963 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1968 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1970 CWalletDB walletdb(pwalletMain->strWalletFile);
1972 int64 nPrevTransactionFee = nTransactionFee;
1973 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1974 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1976 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1978 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1979 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1982 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1984 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1985 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1986 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1989 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1991 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1992 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1995 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1997 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1998 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
2002 fUseProxy = m_checkBoxUseProxy->GetValue();
2003 walletdb.WriteSetting("fUseProxy", fUseProxy);
2005 addrProxy = GetProxyAddr();
2006 walletdb.WriteSetting("addrProxy", addrProxy);
2014 //////////////////////////////////////////////////////////////////////////////
2019 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2021 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2023 // Change (c) into UTF-8 or ANSI copyright symbol
2024 wxString str = m_staticTextMain->GetLabel();
2026 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2028 str.Replace("(c)", "\xA9");
2030 m_staticTextMain->SetLabel(str);
2032 // Resize on Linux to make the window fit the text.
2033 // The text was wrapped manually rather than using the Wrap setting because
2034 // the wrap would be too small on Linux and it can't be changed at this point.
2035 wxFont fontTmp = m_staticTextMain->GetFont();
2036 if (fontTmp.GetPointSize() > 8);
2037 fontTmp.SetPointSize(8);
2038 m_staticTextMain->SetFont(fontTmp);
2039 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2041 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2045 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2055 //////////////////////////////////////////////////////////////////////////////
2060 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2063 m_textCtrlAddress->SetValue(strAddress);
2064 m_choiceTransferType->SetSelection(0);
2065 m_bitmapCheckMark->Show(false);
2066 fEnabledPrev = true;
2067 m_textCtrlAddress->SetFocus();
2069 //// todo: should add a display of your balance for convenience
2071 wxFont fontTmp = m_staticTextInstructions->GetFont();
2072 if (fontTmp.GetPointSize() > 9);
2073 fontTmp.SetPointSize(9);
2074 m_staticTextInstructions->SetFont(fontTmp);
2077 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2081 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2084 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2089 SetIcon(wxICON(bitcoin));
2092 // Fixup the tab order
2093 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2094 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2098 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2100 // Reformat the amount
2102 if (m_textCtrlAmount->GetValue().Trim().empty())
2105 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2106 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2109 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2111 // Open address book
2112 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2113 if (dialog.ShowModal())
2114 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2117 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2119 // Copy clipboard to address box
2120 if (wxTheClipboard->Open())
2122 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2124 wxTextDataObject data;
2125 wxTheClipboard->GetData(data);
2126 m_textCtrlAddress->SetValue(data.GetText());
2128 wxTheClipboard->Close();
2132 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2134 static CCriticalSection cs_sendlock;
2135 TRY_CRITICAL_BLOCK(cs_sendlock)
2138 string strAddress = (string)m_textCtrlAddress->GetValue();
2142 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2144 wxMessageBox(_("Error in amount "), _("Send Coins"));
2147 if (nValue > pwalletMain->GetBalance())
2149 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2152 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2154 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2158 // Parse bitcoin address
2160 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
2162 if (fBitcoinAddress)
2164 CRITICAL_BLOCK(cs_main)
2165 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2167 bool fWasLocked = pwalletMain->IsLocked();
2168 if (!GetWalletPassphrase())
2171 // Send to bitcoin address
2172 CScript scriptPubKey;
2173 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
2175 string strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2177 wxMessageBox(_("Payment sent "), _("Sending..."));
2178 else if (strError == "ABORTED")
2181 pwalletMain->Lock();
2182 return; // leave send dialog open
2186 wxMessageBox(strError + " ", _("Sending..."));
2189 pwalletMain->Lock();
2194 pwalletMain->Lock();
2200 CAddress addr(strAddress);
2201 if (!addr.IsValid())
2203 wxMessageBox(_("Invalid address "), _("Send Coins"));
2208 wtx.mapValue["to"] = strAddress;
2210 // Send to IP address
2211 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2212 if (!pdialog->ShowModal())
2216 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2217 if (!pwalletMain->mapAddressBook.count(strAddress))
2218 pwalletMain->SetAddressBookName(strAddress, "");
2224 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2235 //////////////////////////////////////////////////////////////////////////////
2240 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
2245 start = wxDateTime::UNow();
2246 memset(pszStatus, 0, sizeof(pszStatus));
2253 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2255 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2258 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2259 m_textCtrlStatus->SetValue("");
2261 CreateThread(SendingDialogStartTransfer, this);
2264 CSendingDialog::~CSendingDialog()
2266 printf("~CSendingDialog()\n");
2269 void CSendingDialog::Close()
2271 // Last one out turn out the lights.
2272 // fWorkDone signals that work side is done and UI thread should call destroy.
2273 // fUIDone signals that UI window has closed and work thread should call destroy.
2274 // This allows the window to disappear and end modality when cancelled
2275 // without making the user wait for ConnectNode to return. The dialog object
2276 // hangs around in the background until the work thread exits.
2287 void CSendingDialog::OnClose(wxCloseEvent& event)
2289 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2296 wxCommandEvent cmdevent;
2297 OnButtonCancel(cmdevent);
2301 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2307 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2313 void CSendingDialog::OnPaint(wxPaintEvent& event)
2316 if (strlen(pszStatus) > 130)
2317 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2319 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2320 m_staticTextSending->SetFocus();
2322 m_buttonCancel->Enable(false);
2325 m_buttonOK->Enable(true);
2326 m_buttonOK->SetFocus();
2327 m_buttonCancel->Enable(false);
2329 if (fAbort && fCanCancel && IsShown())
2331 strcpy(pszStatus, _("CANCELLED"));
2332 m_buttonOK->Enable(true);
2333 m_buttonOK->SetFocus();
2334 m_buttonCancel->Enable(false);
2335 m_buttonCancel->SetLabel(_("Cancelled"));
2337 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2343 // Everything from here on is not in the UI thread and must only communicate
2344 // with the rest of the dialog through variables and calling repaint.
2347 void CSendingDialog::Repaint()
2351 GetEventHandler()->AddPendingEvent(event);
2354 bool CSendingDialog::Status()
2361 if (fAbort && fCanCancel)
2363 memset(pszStatus, 0, 10);
2364 strcpy(pszStatus, _("CANCELLED"));
2372 bool CSendingDialog::Status(const string& str)
2377 // This can be read by the UI thread at any time,
2378 // so copy in a way that can be read cleanly at all times.
2379 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2380 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2386 bool CSendingDialog::Error(const string& str)
2390 Status(string(_("Error: ")) + str);
2394 void SendingDialogStartTransfer(void* parg)
2396 ((CSendingDialog*)parg)->StartTransfer();
2399 void CSendingDialog::StartTransfer()
2401 // Make sure we have enough money
2402 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2404 Error(_("Insufficient funds"));
2408 // We may have connected already for product details
2409 if (!Status(_("Connecting...")))
2411 CNode* pnode = ConnectNode(addr, 15 * 60);
2414 Error(_("Unable to connect"));
2418 // Send order to seller, with response going to OnReply2 via event handler
2419 if (!Status(_("Requesting public key...")))
2421 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2424 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2426 ((CSendingDialog*)parg)->OnReply2(vRecv);
2429 void CSendingDialog::OnReply2(CDataStream& vRecv)
2431 if (!Status(_("Received public key...")))
2434 CScript scriptPubKey;
2443 vRecv >> strMessage;
2445 Error(_("Recipient is not accepting transactions sent by IP address"));
2447 Error(_("Transfer was not accepted"));
2448 //// todo: enlarge the window and enable a hidden white box to put seller's message
2451 vRecv >> scriptPubKey;
2455 //// what do we want to do about this?
2456 Error(_("Invalid response received"));
2460 // Pause to give the user a chance to cancel
2461 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2468 CRITICAL_BLOCK(cs_main)
2471 if (!Status(_("Creating transaction...")))
2473 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2475 Error(_("Insufficient funds"));
2479 CReserveKey reservekey(pwalletMain);
2481 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2483 bool fWasLocked = pwalletMain->IsLocked();
2484 if (!GetWalletPassphrase())
2487 if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2489 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2490 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()));
2492 Error(_("Transaction creation failed"));
2497 pwalletMain->Lock();
2501 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2503 Error(_("Transaction aborted"));
2507 // Make sure we're still connected
2508 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2511 Error(_("Lost connection, transaction cancelled"));
2515 // Last chance to cancel
2527 if (!Status(_("Sending payment...")))
2531 if (!pwalletMain->CommitTransaction(wtx, reservekey))
2533 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."));
2537 // Send payment tx to seller, with response going to OnReply3 via event handler
2538 CWalletTx wtxSend = wtx;
2539 wtxSend.fFromMe = false;
2540 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2542 Status(_("Waiting for confirmation..."));
2547 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2549 ((CSendingDialog*)parg)->OnReply3(vRecv);
2552 void CSendingDialog::OnReply3(CDataStream& vRecv)
2560 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2561 "The transaction is recorded and will credit to the recipient,\n"
2562 "but the comment information will be blank."));
2568 //// what do we want to do about this?
2569 Error(_("Payment was sent, but an invalid response was received"));
2575 Status(_("Payment completed"));
2583 //////////////////////////////////////////////////////////////////////////////
2585 // CAddressBookDialog
2588 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2591 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2594 // Set initially selected page
2595 wxNotebookEvent event;
2596 event.SetSelection(nPageIn);
2597 OnNotebookPageChanged(event);
2598 m_notebook->ChangeSelection(nPageIn);
2600 fDuringSend = fDuringSendIn;
2602 m_buttonCancel->Show(false);
2605 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2607 wxIcon iconAddressBook;
2608 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2609 SetIcon(iconAddressBook);
2613 SetIcon(wxICON(bitcoin));
2616 // Init column headers
2617 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2618 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2619 m_listCtrlSending->SetFocus();
2620 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2621 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2622 m_listCtrlReceiving->SetFocus();
2624 // Fill listctrl with address book data
2625 CRITICAL_BLOCK(pwalletMain->cs_KeyStore)
2626 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2628 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2629 BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook)
2631 string strAddress = item.first;
2632 string strName = item.second;
2634 bool fMine = (AddressToHash160(strAddress, hash160) && pwalletMain->HaveKey(hash160));
2635 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2636 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2637 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2638 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2643 wxString CAddressBookDialog::GetSelectedAddress()
2645 int nIndex = GetSelection(m_listCtrl);
2648 return GetItemText(m_listCtrl, nIndex, 1);
2651 wxString CAddressBookDialog::GetSelectedSendingAddress()
2653 int nIndex = GetSelection(m_listCtrlSending);
2656 return GetItemText(m_listCtrlSending, nIndex, 1);
2659 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2661 int nIndex = GetSelection(m_listCtrlReceiving);
2664 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2667 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2670 nPage = event.GetSelection();
2671 if (nPage == SENDING)
2672 m_listCtrl = m_listCtrlSending;
2673 else if (nPage == RECEIVING)
2674 m_listCtrl = m_listCtrlReceiving;
2675 m_buttonDelete->Show(nPage == SENDING);
2676 m_buttonCopy->Show(nPage == RECEIVING);
2678 m_listCtrl->SetFocus();
2681 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2683 // Update address book with edited name
2685 if (event.IsEditCancelled())
2687 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2688 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2689 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2690 pframeMain->RefreshListCtrl();
2693 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2696 if (nPage == RECEIVING)
2697 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2700 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2705 // Doubleclick returns selection
2706 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2710 // Doubleclick edits item
2711 wxCommandEvent event2;
2712 OnButtonEdit(event2);
2715 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2717 if (nPage != SENDING)
2719 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2721 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2723 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2724 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2725 pwalletMain->DelAddressBookName(strAddress);
2726 m_listCtrl->DeleteItem(nIndex);
2729 pframeMain->RefreshListCtrl();
2732 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2734 // Copy address box to clipboard
2735 if (wxTheClipboard->Open())
2737 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2738 wxTheClipboard->Close();
2742 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2745 bool fMine = (AddressToHash160(strAddress, hash160) && pwalletMain->HaveKey(hash160));
2747 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2751 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2753 int nIndex = GetSelection(m_listCtrl);
2756 string strName = (string)m_listCtrl->GetItemText(nIndex);
2757 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2758 string strAddressOrg = strAddress;
2760 if (nPage == SENDING)
2762 // Ask name and address
2765 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2766 if (!dialog.ShowModal())
2768 strName = dialog.GetValue1();
2769 strAddress = dialog.GetValue2();
2771 while (CheckIfMine(strAddress, _("Edit Address")));
2774 else if (nPage == RECEIVING)
2777 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2778 if (!dialog.ShowModal())
2780 strName = dialog.GetValue();
2784 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2786 if (strAddress != strAddressOrg)
2787 pwalletMain->DelAddressBookName(strAddressOrg);
2788 pwalletMain->SetAddressBookName(strAddress, strName);
2790 m_listCtrl->SetItem(nIndex, 1, strAddress);
2791 m_listCtrl->SetItemText(nIndex, strName);
2792 pframeMain->RefreshListCtrl();
2795 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2800 if (nPage == SENDING)
2802 // Ask name and address
2805 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2806 if (!dialog.ShowModal())
2808 strName = dialog.GetValue1();
2809 strAddress = dialog.GetValue2();
2811 while (CheckIfMine(strAddress, _("Add Address")));
2813 else if (nPage == RECEIVING)
2816 CGetTextFromUserDialog dialog(this,
2817 _("New Receiving Address"),
2818 _("You should use a new address for each payment you receive.\n\nLabel"),
2820 if (!dialog.ShowModal())
2822 strName = dialog.GetValue();
2824 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2826 bool fWasLocked = pwalletMain->IsLocked();
2827 if (!GetWalletPassphrase())
2831 strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
2834 pwalletMain->Lock();
2838 // Add to list and select it
2839 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2840 pwalletMain->SetAddressBookName(strAddress, strName);
2841 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2842 SetSelection(m_listCtrl, nIndex);
2843 m_listCtrl->SetFocus();
2844 if (nPage == SENDING)
2845 pframeMain->RefreshListCtrl();
2848 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2851 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2854 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2860 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2871 //////////////////////////////////////////////////////////////////////////////
2878 ID_TASKBAR_RESTORE = 10001,
2881 ID_TASKBAR_GENERATE,
2885 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2886 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2887 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2888 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2889 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2890 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2891 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2894 void CMyTaskBarIcon::Show(bool fShow)
2896 static char pszPrevTip[200];
2899 string strTooltip = _("Bitcoin");
2900 if (fGenerateBitcoins)
2901 strTooltip = _("Bitcoin - Generating");
2902 if (fGenerateBitcoins && vNodes.empty())
2903 strTooltip = _("Bitcoin - (not connected)");
2905 // Optimization, only update when changed, using char array to be reentrant
2906 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2908 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2910 // somehow it'll choose the wrong size and scale it down if
2911 // we use the main icon, so we hand it one with only 16x16
2912 SetIcon(wxICON(favicon), strTooltip);
2914 SetIcon(bitcoin80_xpm, strTooltip);
2920 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2925 void CMyTaskBarIcon::Hide()
2930 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2935 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2940 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2943 CSendDialog dialog(pframeMain);
2947 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2949 // Since it's modal, get the main window to do it
2950 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2951 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2954 void CMyTaskBarIcon::Restore()
2957 wxIconizeEvent event(0, false);
2958 pframeMain->GetEventHandler()->AddPendingEvent(event);
2959 pframeMain->Iconize(false);
2960 pframeMain->Raise();
2963 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2965 event.Check(fGenerateBitcoins);
2968 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2970 pframeMain->Close(true);
2973 void CMyTaskBarIcon::UpdateTooltip()
2975 if (IsIconInstalled())
2979 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2981 wxMenu* pmenu = new wxMenu;
2982 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2983 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2984 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2985 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2986 pmenu->AppendSeparator();
2987 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2997 //////////////////////////////////////////////////////////////////////////////
3002 void CreateMainWindow()
3004 pframeMain = new CMainFrame(NULL);
3005 if (GetBoolArg("-min"))
3006 pframeMain->Iconize(true);
3007 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3008 if (!GetBoolArg("-minimizetotray"))
3009 fMinimizeToTray = false;
3011 pframeMain->Show(true); // have to show first to get taskbar button to hide
3012 if (fMinimizeToTray && pframeMain->IsIconized())
3013 fClosedToTray = true;
3014 pframeMain->Show(!fClosedToTray);
3015 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3016 CreateThread(ThreadDelayedRepaint, NULL);
3020 // Define a new application
3021 class CMyApp : public wxApp
3030 // Hook Initialize so we can start without GUI
3031 virtual bool Initialize(int& argc, wxChar** argv);
3033 // 2nd-level exception handling: we get all the exceptions occurring in any
3034 // event handler here
3035 virtual bool OnExceptionInMainLoop();
3037 // 3rd, and final, level exception handling: whenever an unhandled
3038 // exception is caught, this function is called
3039 virtual void OnUnhandledException();
3041 // and now for something different: this function is called in case of a
3042 // crash (e.g. dereferencing null pointer, division by 0, ...)
3043 virtual void OnFatalException();
3046 IMPLEMENT_APP(CMyApp)
3048 bool CMyApp::Initialize(int& argc, wxChar** argv)
3050 for (int i = 1; i < argc; i++)
3051 if (!IsSwitchChar(argv[i][0]))
3052 fCommandLine = true;
3056 // wxApp::Initialize will remove environment-specific parameters,
3057 // so it's too early to call ParseParameters yet
3058 for (int i = 1; i < argc; i++)
3060 wxString str = argv[i];
3062 if (str.size() >= 1 && str[0] == '/')
3064 char pszLower[MAX_PATH];
3065 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3069 if (str == "-daemon")
3075 if (fDaemon || fCommandLine)
3077 // Call the original Initialize while suppressing error messages
3078 // and ignoring failure. If unable to initialize GTK, it fails
3079 // near the end so hopefully the last few things don't matter.
3082 wxApp::Initialize(argc, argv);
3091 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3095 pthread_exit((void*)0);
3097 pid_t sid = setsid();
3099 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3106 return wxApp::Initialize(argc, argv);
3109 bool CMyApp::OnInit()
3111 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3112 // Disable malfunctioning wxWidgets debug assertion
3113 extern int g_isPainting;
3114 g_isPainting = 10000;
3116 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3117 SetAppName("Bitcoin");
3119 SetAppName("bitcoin");
3123 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3124 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3125 class wxMBConv_win32 : public wxMBConv
3129 size_t m_minMBCharWidth;
3131 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3132 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3136 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3137 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3138 g_locale.AddCatalogLookupPathPrefix("locale");
3140 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3141 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3143 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3144 g_locale.AddCatalog("bitcoin");
3147 HDC hdc = GetDC(NULL);
3150 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3151 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3152 ReleaseDC(NULL, hdc);
3156 return AppInit(argc, argv);
3159 int CMyApp::OnExit()
3162 return wxApp::OnExit();
3165 bool CMyApp::OnExceptionInMainLoop()
3171 catch (std::exception& e)
3173 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3174 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3180 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3181 wxLogWarning("Unknown exception");
3188 void CMyApp::OnUnhandledException()
3190 // this shows how we may let some exception propagate uncaught
3195 catch (std::exception& e)
3197 PrintException(&e, "CMyApp::OnUnhandledException()");
3198 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3204 PrintException(NULL, "CMyApp::OnUnhandledException()");
3205 wxLogWarning("Unknown exception");
3211 void CMyApp::OnFatalException()
3213 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);