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())
238 CBitcoinAddress address(strAddress);
239 if (!address.IsValid())
241 vector<unsigned char> vchPubKey;
242 if (!pwalletMain->GetPubKey(address, 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(CBitcoinAddress(vchPubKey).ToString());
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))
707 CBitcoinAddress address;
708 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
710 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
712 //strDescription += _("Received payment to ");
713 //strDescription += _("Received with address ");
714 strDescription += _("Received with: ");
715 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
716 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
718 string strLabel = (*mi).second;
719 strDescription += address.ToString().substr(0,12) + "... ";
720 strDescription += "(" + strLabel + ")";
723 strDescription += address.ToString();
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
788 CBitcoinAddress address;
789 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
790 strAddress = address.ToString();
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 = CBitcoinAddress(pwalletMain->vchDefaultKey).ToString();
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 = CBitcoinAddress(pwalletMain->GetOrReuseKeyFromPool()).ToString();
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 CBitcoinAddress address;
1506 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1508 if (pwalletMain->mapAddressBook.count(address))
1510 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1511 strHTML += _("<b>To:</b> ");
1512 strHTML += HtmlEscape(address.ToString());
1513 if (!pwalletMain->mapAddressBook[address].empty())
1514 strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[address] + ")";
1516 strHTML += _(" (yours)");
1531 if (!wtx.mapValue["to"].empty())
1533 // Online transaction
1534 strAddress = wtx.mapValue["to"];
1535 strHTML += _("<b>To:</b> ");
1536 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1537 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1538 strHTML += HtmlEscape(strAddress) + "<br>";
1545 if (wtx.IsCoinBase() && nCredit == 0)
1550 int64 nUnmatured = 0;
1551 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1552 nUnmatured += pwalletMain->GetCredit(txout);
1553 strHTML += _("<b>Credit:</b> ");
1554 if (wtx.IsInMainChain())
1555 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1557 strHTML += _("(not accepted)");
1565 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1569 bool fAllFromMe = true;
1570 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1571 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1573 bool fAllToMe = true;
1574 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1575 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1582 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1584 if (pwalletMain->IsMine(txout))
1587 if (wtx.mapValue["to"].empty())
1589 // Offline transaction
1590 CBitcoinAddress address;
1591 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1593 string strAddress = address.ToString();
1594 strHTML += _("<b>To:</b> ");
1595 if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
1596 strHTML += pwalletMain->mapAddressBook[address] + " ";
1597 strHTML += strAddress;
1602 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1608 int64 nChange = wtx.GetChange();
1609 int64 nValue = nCredit - nChange;
1610 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1611 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1614 int64 nTxFee = nDebit - wtx.GetValueOut();
1616 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1621 // Mixed debit transaction
1623 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1624 if (pwalletMain->IsMine(txin))
1625 strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1626 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1627 if (pwalletMain->IsMine(txout))
1628 strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1632 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1638 if (!wtx.mapValue["message"].empty())
1639 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1640 if (!wtx.mapValue["comment"].empty())
1641 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1643 if (wtx.IsCoinBase())
1644 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>";
1652 strHTML += "<hr><br>debug print<br><br>";
1653 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1654 if (pwalletMain->IsMine(txin))
1655 strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1656 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1657 if (pwalletMain->IsMine(txout))
1658 strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1660 strHTML += "<br><b>Transaction:</b><br>";
1661 strHTML += HtmlEscape(wtx.ToString(), true);
1663 strHTML += "<br><b>Inputs:</b><br>";
1664 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1666 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1668 COutPoint prevout = txin.prevout;
1669 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1670 if (mi != pwalletMain->mapWallet.end())
1672 const CWalletTx& prev = (*mi).second;
1673 if (prevout.n < prev.vout.size())
1675 strHTML += HtmlEscape(prev.ToString(), true);
1676 strHTML += " " + FormatTxStatus(prev) + ", ";
1677 strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1686 strHTML += "</font></html>";
1687 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1688 m_htmlWin->SetPage(strHTML);
1689 m_buttonOK->SetFocus();
1693 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1703 //////////////////////////////////////////////////////////////////////////////
1709 string StartupShortcutPath()
1711 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1714 bool GetStartOnSystemStartup()
1716 return filesystem::exists(StartupShortcutPath().c_str());
1719 void SetStartOnSystemStartup(bool fAutoStart)
1721 // If the shortcut exists already, remove it for updating
1722 remove(StartupShortcutPath().c_str());
1728 // Get a pointer to the IShellLink interface.
1729 IShellLink* psl = NULL;
1730 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1731 CLSCTX_INPROC_SERVER, IID_IShellLink,
1732 reinterpret_cast<void**>(&psl));
1734 if (SUCCEEDED(hres))
1736 // Get the current executable path
1737 TCHAR pszExePath[MAX_PATH];
1738 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1740 // Set the path to the shortcut target
1741 psl->SetPath(pszExePath);
1742 PathRemoveFileSpec(pszExePath);
1743 psl->SetWorkingDirectory(pszExePath);
1744 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1746 // Query IShellLink for the IPersistFile interface for
1747 // saving the shortcut in persistent storage.
1748 IPersistFile* ppf = NULL;
1749 hres = psl->QueryInterface(IID_IPersistFile,
1750 reinterpret_cast<void**>(&ppf));
1751 if (SUCCEEDED(hres))
1753 WCHAR pwsz[MAX_PATH];
1754 // Ensure that the string is ANSI.
1755 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1756 // Save the link by calling IPersistFile::Save.
1757 hres = ppf->Save(pwsz, TRUE);
1766 #elif defined(__WXGTK__)
1768 // Follow the Desktop Application Autostart Spec:
1769 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1771 boost::filesystem::path GetAutostartDir()
1773 namespace fs = boost::filesystem;
1775 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1776 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1777 char* pszHome = getenv("HOME");
1778 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1782 boost::filesystem::path GetAutostartFilePath()
1784 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1787 bool GetStartOnSystemStartup()
1789 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1790 if (!optionFile.good())
1792 // Scan through file for "Hidden=true":
1794 while (!optionFile.eof())
1796 getline(optionFile, line);
1797 if (line.find("Hidden") != string::npos &&
1798 line.find("true") != string::npos)
1806 void SetStartOnSystemStartup(bool fAutoStart)
1810 unlink(GetAutostartFilePath().native_file_string().c_str());
1814 char pszExePath[MAX_PATH+1];
1815 memset(pszExePath, 0, sizeof(pszExePath));
1816 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1819 boost::filesystem::create_directories(GetAutostartDir());
1821 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1822 if (!optionFile.good())
1824 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1827 // Write a bitcoin.desktop file to the autostart directory:
1828 optionFile << "[Desktop Entry]\n";
1829 optionFile << "Type=Application\n";
1830 optionFile << "Name=Bitcoin\n";
1831 optionFile << "Exec=" << pszExePath << "\n";
1832 optionFile << "Terminal=false\n";
1833 optionFile << "Hidden=false\n";
1839 // TODO: OSX startup stuff; see:
1840 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1842 bool GetStartOnSystemStartup() { return false; }
1843 void SetStartOnSystemStartup(bool fAutoStart) { }
1852 //////////////////////////////////////////////////////////////////////////////
1857 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1859 // Set up list box of page choices
1860 m_listBox->Append(_("Main"));
1861 //m_listBox->Append(_("Test 2"));
1862 m_listBox->SetSelection(0);
1865 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1867 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1869 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1870 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1871 if (!GetBoolArg("-minimizetotray"))
1873 // Minimize to tray is just too buggy on Linux
1874 fMinimizeToTray = false;
1875 m_checkBoxMinimizeToTray->SetValue(false);
1876 m_checkBoxMinimizeToTray->Enable(false);
1877 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1880 #ifdef __WXMAC_OSX__
1881 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1885 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1886 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1887 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1888 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1890 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1892 m_checkBoxUseUPnP->Enable(false);
1893 m_checkBoxUseProxy->SetValue(fUseProxy);
1894 m_textCtrlProxyIP->Enable(fUseProxy);
1895 m_textCtrlProxyPort->Enable(fUseProxy);
1896 m_staticTextProxyIP->Enable(fUseProxy);
1897 m_staticTextProxyPort->Enable(fUseProxy);
1898 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1899 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1901 m_buttonOK->SetFocus();
1904 void COptionsDialog::SelectPage(int nPage)
1906 m_panelMain->Show(nPage == 0);
1907 m_panelTest2->Show(nPage == 1);
1909 m_scrolledWindow->Layout();
1910 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1913 void COptionsDialog::OnListBox(wxCommandEvent& event)
1915 SelectPage(event.GetSelection());
1918 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1921 int64 nTmp = nTransactionFee;
1922 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1923 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1926 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1928 m_textCtrlProxyIP->Enable(event.IsChecked());
1929 m_textCtrlProxyPort->Enable(event.IsChecked());
1930 m_staticTextProxyIP->Enable(event.IsChecked());
1931 m_staticTextProxyPort->Enable(event.IsChecked());
1934 CAddress COptionsDialog::GetProxyAddr()
1936 // Be careful about byte order, addr.ip and addr.port are big endian
1937 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1938 if (addr.ip == INADDR_NONE)
1939 addr.ip = addrProxy.ip;
1940 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1941 addr.port = htons(nPort);
1942 if (nPort <= 0 || nPort > USHRT_MAX)
1943 addr.port = addrProxy.port;
1947 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1950 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1951 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1955 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1957 OnButtonApply(event);
1961 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1966 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1968 CWalletDB walletdb(pwalletMain->strWalletFile);
1970 int64 nPrevTransactionFee = nTransactionFee;
1971 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1972 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1974 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1976 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1977 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1980 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1982 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1983 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1984 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1987 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1989 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1990 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1993 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1995 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1996 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
2000 fUseProxy = m_checkBoxUseProxy->GetValue();
2001 walletdb.WriteSetting("fUseProxy", fUseProxy);
2003 addrProxy = GetProxyAddr();
2004 walletdb.WriteSetting("addrProxy", addrProxy);
2012 //////////////////////////////////////////////////////////////////////////////
2017 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2019 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2021 // Change (c) into UTF-8 or ANSI copyright symbol
2022 wxString str = m_staticTextMain->GetLabel();
2024 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2026 str.Replace("(c)", "\xA9");
2028 m_staticTextMain->SetLabel(str);
2030 // Resize on Linux to make the window fit the text.
2031 // The text was wrapped manually rather than using the Wrap setting because
2032 // the wrap would be too small on Linux and it can't be changed at this point.
2033 wxFont fontTmp = m_staticTextMain->GetFont();
2034 if (fontTmp.GetPointSize() > 8);
2035 fontTmp.SetPointSize(8);
2036 m_staticTextMain->SetFont(fontTmp);
2037 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2039 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2043 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2053 //////////////////////////////////////////////////////////////////////////////
2058 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2061 m_textCtrlAddress->SetValue(strAddress);
2062 m_choiceTransferType->SetSelection(0);
2063 m_bitmapCheckMark->Show(false);
2064 fEnabledPrev = true;
2065 m_textCtrlAddress->SetFocus();
2067 //// todo: should add a display of your balance for convenience
2069 wxFont fontTmp = m_staticTextInstructions->GetFont();
2070 if (fontTmp.GetPointSize() > 9);
2071 fontTmp.SetPointSize(9);
2072 m_staticTextInstructions->SetFont(fontTmp);
2075 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2079 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2082 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2087 SetIcon(wxICON(bitcoin));
2090 // Fixup the tab order
2091 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2092 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2096 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2098 // Reformat the amount
2100 if (m_textCtrlAmount->GetValue().Trim().empty())
2103 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2104 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2107 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2109 // Open address book
2110 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2111 if (dialog.ShowModal())
2112 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2115 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2117 // Copy clipboard to address box
2118 if (wxTheClipboard->Open())
2120 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2122 wxTextDataObject data;
2123 wxTheClipboard->GetData(data);
2124 m_textCtrlAddress->SetValue(data.GetText());
2126 wxTheClipboard->Close();
2130 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2132 static CCriticalSection cs_sendlock;
2133 TRY_CRITICAL_BLOCK(cs_sendlock)
2136 string strAddress = (string)m_textCtrlAddress->GetValue();
2140 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2142 wxMessageBox(_("Error in amount "), _("Send Coins"));
2145 if (nValue > pwalletMain->GetBalance())
2147 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2150 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2152 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2156 // Parse bitcoin address
2157 CBitcoinAddress address(strAddress);
2158 bool fBitcoinAddress = address.IsValid();
2160 if (fBitcoinAddress)
2162 CRITICAL_BLOCK(cs_main)
2163 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2165 bool fWasLocked = pwalletMain->IsLocked();
2166 if (!GetWalletPassphrase())
2169 // Send to bitcoin address
2170 CScript scriptPubKey;
2171 scriptPubKey.SetBitcoinAddress(address);
2173 string strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2175 wxMessageBox(_("Payment sent "), _("Sending..."));
2176 else if (strError == "ABORTED")
2179 pwalletMain->Lock();
2180 return; // leave send dialog open
2184 wxMessageBox(strError + " ", _("Sending..."));
2187 pwalletMain->Lock();
2192 pwalletMain->Lock();
2198 CAddress addr(strAddress);
2199 if (!addr.IsValid())
2201 wxMessageBox(_("Invalid address "), _("Send Coins"));
2206 wtx.mapValue["to"] = strAddress;
2208 // Send to IP address
2209 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2210 if (!pdialog->ShowModal())
2214 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2215 if (!pwalletMain->mapAddressBook.count(address))
2216 pwalletMain->SetAddressBookName(strAddress, "");
2222 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2233 //////////////////////////////////////////////////////////////////////////////
2238 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
2243 start = wxDateTime::UNow();
2244 memset(pszStatus, 0, sizeof(pszStatus));
2251 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2253 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2256 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2257 m_textCtrlStatus->SetValue("");
2259 CreateThread(SendingDialogStartTransfer, this);
2262 CSendingDialog::~CSendingDialog()
2264 printf("~CSendingDialog()\n");
2267 void CSendingDialog::Close()
2269 // Last one out turn out the lights.
2270 // fWorkDone signals that work side is done and UI thread should call destroy.
2271 // fUIDone signals that UI window has closed and work thread should call destroy.
2272 // This allows the window to disappear and end modality when cancelled
2273 // without making the user wait for ConnectNode to return. The dialog object
2274 // hangs around in the background until the work thread exits.
2285 void CSendingDialog::OnClose(wxCloseEvent& event)
2287 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2294 wxCommandEvent cmdevent;
2295 OnButtonCancel(cmdevent);
2299 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2305 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2311 void CSendingDialog::OnPaint(wxPaintEvent& event)
2314 if (strlen(pszStatus) > 130)
2315 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2317 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2318 m_staticTextSending->SetFocus();
2320 m_buttonCancel->Enable(false);
2323 m_buttonOK->Enable(true);
2324 m_buttonOK->SetFocus();
2325 m_buttonCancel->Enable(false);
2327 if (fAbort && fCanCancel && IsShown())
2329 strcpy(pszStatus, _("CANCELLED"));
2330 m_buttonOK->Enable(true);
2331 m_buttonOK->SetFocus();
2332 m_buttonCancel->Enable(false);
2333 m_buttonCancel->SetLabel(_("Cancelled"));
2335 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2341 // Everything from here on is not in the UI thread and must only communicate
2342 // with the rest of the dialog through variables and calling repaint.
2345 void CSendingDialog::Repaint()
2349 GetEventHandler()->AddPendingEvent(event);
2352 bool CSendingDialog::Status()
2359 if (fAbort && fCanCancel)
2361 memset(pszStatus, 0, 10);
2362 strcpy(pszStatus, _("CANCELLED"));
2370 bool CSendingDialog::Status(const string& str)
2375 // This can be read by the UI thread at any time,
2376 // so copy in a way that can be read cleanly at all times.
2377 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2378 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2384 bool CSendingDialog::Error(const string& str)
2388 Status(string(_("Error: ")) + str);
2392 void SendingDialogStartTransfer(void* parg)
2394 ((CSendingDialog*)parg)->StartTransfer();
2397 void CSendingDialog::StartTransfer()
2399 // Make sure we have enough money
2400 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2402 Error(_("Insufficient funds"));
2406 // We may have connected already for product details
2407 if (!Status(_("Connecting...")))
2409 CNode* pnode = ConnectNode(addr, 15 * 60);
2412 Error(_("Unable to connect"));
2416 // Send order to seller, with response going to OnReply2 via event handler
2417 if (!Status(_("Requesting public key...")))
2419 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2422 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2424 ((CSendingDialog*)parg)->OnReply2(vRecv);
2427 void CSendingDialog::OnReply2(CDataStream& vRecv)
2429 if (!Status(_("Received public key...")))
2432 CScript scriptPubKey;
2441 vRecv >> strMessage;
2443 Error(_("Recipient is not accepting transactions sent by IP address"));
2445 Error(_("Transfer was not accepted"));
2446 //// todo: enlarge the window and enable a hidden white box to put seller's message
2449 vRecv >> scriptPubKey;
2453 //// what do we want to do about this?
2454 Error(_("Invalid response received"));
2458 // Pause to give the user a chance to cancel
2459 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2466 CRITICAL_BLOCK(cs_main)
2469 if (!Status(_("Creating transaction...")))
2471 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2473 Error(_("Insufficient funds"));
2477 CReserveKey reservekey(pwalletMain);
2479 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2481 bool fWasLocked = pwalletMain->IsLocked();
2482 if (!GetWalletPassphrase())
2485 if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2487 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2488 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()));
2490 Error(_("Transaction creation failed"));
2495 pwalletMain->Lock();
2499 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2501 Error(_("Transaction aborted"));
2505 // Make sure we're still connected
2506 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2509 Error(_("Lost connection, transaction cancelled"));
2513 // Last chance to cancel
2525 if (!Status(_("Sending payment...")))
2529 if (!pwalletMain->CommitTransaction(wtx, reservekey))
2531 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."));
2535 // Send payment tx to seller, with response going to OnReply3 via event handler
2536 CWalletTx wtxSend = wtx;
2537 wtxSend.fFromMe = false;
2538 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2540 Status(_("Waiting for confirmation..."));
2545 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2547 ((CSendingDialog*)parg)->OnReply3(vRecv);
2550 void CSendingDialog::OnReply3(CDataStream& vRecv)
2558 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2559 "The transaction is recorded and will credit to the recipient,\n"
2560 "but the comment information will be blank."));
2566 //// what do we want to do about this?
2567 Error(_("Payment was sent, but an invalid response was received"));
2573 Status(_("Payment completed"));
2581 //////////////////////////////////////////////////////////////////////////////
2583 // CAddressBookDialog
2586 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2589 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2592 // Set initially selected page
2593 wxNotebookEvent event;
2594 event.SetSelection(nPageIn);
2595 OnNotebookPageChanged(event);
2596 m_notebook->ChangeSelection(nPageIn);
2598 fDuringSend = fDuringSendIn;
2600 m_buttonCancel->Show(false);
2603 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2605 wxIcon iconAddressBook;
2606 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2607 SetIcon(iconAddressBook);
2611 SetIcon(wxICON(bitcoin));
2614 // Init column headers
2615 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2616 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2617 m_listCtrlSending->SetFocus();
2618 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2619 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2620 m_listCtrlReceiving->SetFocus();
2622 // Fill listctrl with address book data
2623 CRITICAL_BLOCK(pwalletMain->cs_KeyStore)
2624 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2626 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2627 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2629 const CBitcoinAddress& address = item.first;
2630 string strName = item.second;
2631 bool fMine = pwalletMain->HaveKey(address);
2632 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2633 int nIndex = InsertLine(plistCtrl, strName, address.ToString());
2634 if (address.ToString() == (fMine ? strDefaultReceiving : string(strInitSelected)))
2635 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2640 wxString CAddressBookDialog::GetSelectedAddress()
2642 int nIndex = GetSelection(m_listCtrl);
2645 return GetItemText(m_listCtrl, nIndex, 1);
2648 wxString CAddressBookDialog::GetSelectedSendingAddress()
2650 int nIndex = GetSelection(m_listCtrlSending);
2653 return GetItemText(m_listCtrlSending, nIndex, 1);
2656 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2658 int nIndex = GetSelection(m_listCtrlReceiving);
2661 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2664 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2667 nPage = event.GetSelection();
2668 if (nPage == SENDING)
2669 m_listCtrl = m_listCtrlSending;
2670 else if (nPage == RECEIVING)
2671 m_listCtrl = m_listCtrlReceiving;
2672 m_buttonDelete->Show(nPage == SENDING);
2673 m_buttonCopy->Show(nPage == RECEIVING);
2675 m_listCtrl->SetFocus();
2678 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2680 // Update address book with edited name
2682 if (event.IsEditCancelled())
2684 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2685 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2686 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2687 pframeMain->RefreshListCtrl();
2690 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2693 if (nPage == RECEIVING)
2694 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2697 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2702 // Doubleclick returns selection
2703 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2707 // Doubleclick edits item
2708 wxCommandEvent event2;
2709 OnButtonEdit(event2);
2712 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2714 if (nPage != SENDING)
2716 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2718 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2720 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2721 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2722 pwalletMain->DelAddressBookName(strAddress);
2723 m_listCtrl->DeleteItem(nIndex);
2726 pframeMain->RefreshListCtrl();
2729 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2731 // Copy address box to clipboard
2732 if (wxTheClipboard->Open())
2734 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2735 wxTheClipboard->Close();
2739 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2741 CBitcoinAddress address(strAddress);
2742 bool fMine = address.IsValid() && pwalletMain->HaveKey(address);
2744 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2748 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2750 int nIndex = GetSelection(m_listCtrl);
2753 string strName = (string)m_listCtrl->GetItemText(nIndex);
2754 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2755 string strAddressOrg = strAddress;
2757 if (nPage == SENDING)
2759 // Ask name and address
2762 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2763 if (!dialog.ShowModal())
2765 strName = dialog.GetValue1();
2766 strAddress = dialog.GetValue2();
2768 while (CheckIfMine(strAddress, _("Edit Address")));
2771 else if (nPage == RECEIVING)
2774 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2775 if (!dialog.ShowModal())
2777 strName = dialog.GetValue();
2781 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2783 if (strAddress != strAddressOrg)
2784 pwalletMain->DelAddressBookName(strAddressOrg);
2785 pwalletMain->SetAddressBookName(strAddress, strName);
2787 m_listCtrl->SetItem(nIndex, 1, strAddress);
2788 m_listCtrl->SetItemText(nIndex, strName);
2789 pframeMain->RefreshListCtrl();
2792 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2797 if (nPage == SENDING)
2799 // Ask name and address
2802 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2803 if (!dialog.ShowModal())
2805 strName = dialog.GetValue1();
2806 strAddress = dialog.GetValue2();
2808 while (CheckIfMine(strAddress, _("Add Address")));
2810 else if (nPage == RECEIVING)
2813 CGetTextFromUserDialog dialog(this,
2814 _("New Receiving Address"),
2815 _("You should use a new address for each payment you receive.\n\nLabel"),
2817 if (!dialog.ShowModal())
2819 strName = dialog.GetValue();
2821 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2823 bool fWasLocked = pwalletMain->IsLocked();
2824 if (!GetWalletPassphrase())
2828 strAddress = CBitcoinAddress(pwalletMain->GetOrReuseKeyFromPool()).ToString();
2831 pwalletMain->Lock();
2835 // Add to list and select it
2836 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2837 pwalletMain->SetAddressBookName(strAddress, strName);
2838 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2839 SetSelection(m_listCtrl, nIndex);
2840 m_listCtrl->SetFocus();
2841 if (nPage == SENDING)
2842 pframeMain->RefreshListCtrl();
2845 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2848 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2851 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2857 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2868 //////////////////////////////////////////////////////////////////////////////
2875 ID_TASKBAR_RESTORE = 10001,
2878 ID_TASKBAR_GENERATE,
2882 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2883 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2884 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2885 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2886 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2887 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2888 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2891 void CMyTaskBarIcon::Show(bool fShow)
2893 static char pszPrevTip[200];
2896 string strTooltip = _("Bitcoin");
2897 if (fGenerateBitcoins)
2898 strTooltip = _("Bitcoin - Generating");
2899 if (fGenerateBitcoins && vNodes.empty())
2900 strTooltip = _("Bitcoin - (not connected)");
2902 // Optimization, only update when changed, using char array to be reentrant
2903 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2905 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2907 // somehow it'll choose the wrong size and scale it down if
2908 // we use the main icon, so we hand it one with only 16x16
2909 SetIcon(wxICON(favicon), strTooltip);
2911 SetIcon(bitcoin80_xpm, strTooltip);
2917 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2922 void CMyTaskBarIcon::Hide()
2927 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2932 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2937 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2940 CSendDialog dialog(pframeMain);
2944 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2946 // Since it's modal, get the main window to do it
2947 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2948 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2951 void CMyTaskBarIcon::Restore()
2954 wxIconizeEvent event(0, false);
2955 pframeMain->GetEventHandler()->AddPendingEvent(event);
2956 pframeMain->Iconize(false);
2957 pframeMain->Raise();
2960 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2962 event.Check(fGenerateBitcoins);
2965 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2967 pframeMain->Close(true);
2970 void CMyTaskBarIcon::UpdateTooltip()
2972 if (IsIconInstalled())
2976 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2978 wxMenu* pmenu = new wxMenu;
2979 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2980 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2981 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2982 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2983 pmenu->AppendSeparator();
2984 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2994 //////////////////////////////////////////////////////////////////////////////
2999 void CreateMainWindow()
3001 pframeMain = new CMainFrame(NULL);
3002 if (GetBoolArg("-min"))
3003 pframeMain->Iconize(true);
3004 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3005 if (!GetBoolArg("-minimizetotray"))
3006 fMinimizeToTray = false;
3008 pframeMain->Show(true); // have to show first to get taskbar button to hide
3009 if (fMinimizeToTray && pframeMain->IsIconized())
3010 fClosedToTray = true;
3011 pframeMain->Show(!fClosedToTray);
3012 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3013 CreateThread(ThreadDelayedRepaint, NULL);
3017 // Define a new application
3018 class CMyApp : public wxApp
3027 // Hook Initialize so we can start without GUI
3028 virtual bool Initialize(int& argc, wxChar** argv);
3030 // 2nd-level exception handling: we get all the exceptions occurring in any
3031 // event handler here
3032 virtual bool OnExceptionInMainLoop();
3034 // 3rd, and final, level exception handling: whenever an unhandled
3035 // exception is caught, this function is called
3036 virtual void OnUnhandledException();
3038 // and now for something different: this function is called in case of a
3039 // crash (e.g. dereferencing null pointer, division by 0, ...)
3040 virtual void OnFatalException();
3043 IMPLEMENT_APP(CMyApp)
3045 bool CMyApp::Initialize(int& argc, wxChar** argv)
3047 for (int i = 1; i < argc; i++)
3048 if (!IsSwitchChar(argv[i][0]))
3049 fCommandLine = true;
3053 // wxApp::Initialize will remove environment-specific parameters,
3054 // so it's too early to call ParseParameters yet
3055 for (int i = 1; i < argc; i++)
3057 wxString str = argv[i];
3059 if (str.size() >= 1 && str[0] == '/')
3061 char pszLower[MAX_PATH];
3062 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3066 if (str == "-daemon")
3072 if (fDaemon || fCommandLine)
3074 // Call the original Initialize while suppressing error messages
3075 // and ignoring failure. If unable to initialize GTK, it fails
3076 // near the end so hopefully the last few things don't matter.
3079 wxApp::Initialize(argc, argv);
3088 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3092 pthread_exit((void*)0);
3094 pid_t sid = setsid();
3096 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3103 return wxApp::Initialize(argc, argv);
3106 bool CMyApp::OnInit()
3108 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3109 // Disable malfunctioning wxWidgets debug assertion
3110 extern int g_isPainting;
3111 g_isPainting = 10000;
3113 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3114 SetAppName("Bitcoin");
3116 SetAppName("bitcoin");
3120 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3121 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3122 class wxMBConv_win32 : public wxMBConv
3126 size_t m_minMBCharWidth;
3128 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3129 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3133 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3134 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3135 g_locale.AddCatalogLookupPathPrefix("locale");
3137 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3138 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3140 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3141 g_locale.AddCatalog("bitcoin");
3144 HDC hdc = GetDC(NULL);
3147 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3148 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3149 ReleaseDC(NULL, hdc);
3153 return AppInit(argc, argv);
3156 int CMyApp::OnExit()
3159 return wxApp::OnExit();
3162 bool CMyApp::OnExceptionInMainLoop()
3168 catch (std::exception& e)
3170 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3171 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3177 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3178 wxLogWarning("Unknown exception");
3185 void CMyApp::OnUnhandledException()
3187 // this shows how we may let some exception propagate uncaught
3192 catch (std::exception& e)
3194 PrintException(&e, "CMyApp::OnUnhandledException()");
3195 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3201 PrintException(NULL, "CMyApp::OnUnhandledException()");
3202 wxLogWarning("Unknown exception");
3208 void CMyApp::OnFatalException()
3210 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);