1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2011 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
10 #include <boost/filesystem/fstream.hpp>
11 #include <boost/filesystem/convenience.hpp>
17 using namespace boost;
20 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
22 CMainFrame* pframeMain = NULL;
23 CMyTaskBarIcon* ptaskbaricon = NULL;
24 bool fClosedToTray = false;
31 static const double nScaleX = 1.0;
32 static const double nScaleY = 1.0;
42 //////////////////////////////////////////////////////////////////////////////
47 void HandleCtrlA(wxKeyEvent& event)
51 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
52 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
53 textCtrl->SetSelection(-1, -1);
58 //char pszHourFormat[256];
59 //pszHourFormat[0] = '\0';
60 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
61 //return (pszHourFormat[0] != '0');
65 string DateStr(int64 nTime)
67 // Can only be used safely here in the UI
68 return (string)wxDateTime((time_t)nTime).FormatDate();
71 string DateTimeStr(int64 nTime)
73 // Can only be used safely here in the UI
74 wxDateTime datetime((time_t)nTime);
76 return (string)datetime.Format("%x %H:%M");
78 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
81 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
83 // Helper to simplify access to listctrl
85 item.m_itemId = nIndex;
87 item.m_mask = wxLIST_MASK_TEXT;
88 if (!listCtrl->GetItem(item))
90 return item.GetText();
93 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
95 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
96 listCtrl->SetItem(nIndex, 1, str1);
100 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
102 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
103 listCtrl->SetItem(nIndex, 1, str1);
104 listCtrl->SetItem(nIndex, 2, str2);
105 listCtrl->SetItem(nIndex, 3, str3);
106 listCtrl->SetItem(nIndex, 4, str4);
110 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
112 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
113 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
114 listCtrl->SetItem(nIndex, 1, str1);
115 listCtrl->SetItem(nIndex, 2, str2);
116 listCtrl->SetItem(nIndex, 3, str3);
117 listCtrl->SetItem(nIndex, 4, str4);
121 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
123 // Repaint on Windows is more flickery if the colour has ever been set,
124 // so don't want to set it unless it's different. Default colour has
125 // alpha 0 transparent, so our colours don't match using operator==.
126 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
128 c1 = wxColour(0,0,0);
129 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
130 listCtrl->SetItemTextColour(nIndex, colour);
133 void SetSelection(wxListCtrl* listCtrl, int nIndex)
135 int nSize = listCtrl->GetItemCount();
136 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
137 for (int i = 0; i < nSize; i++)
138 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
141 int GetSelection(wxListCtrl* listCtrl)
143 int nSize = listCtrl->GetItemCount();
144 for (int i = 0; i < nSize; i++)
145 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
150 string HtmlEscape(const char* psz, bool fMultiLine=false)
153 for (const char* p = psz; *p; p++)
155 if (*p == '<') len += 4;
156 else if (*p == '>') len += 4;
157 else if (*p == '&') len += 5;
158 else if (*p == '"') len += 6;
159 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
160 else if (*p == '\n' && fMultiLine) len += 5;
166 for (const char* p = psz; *p; p++)
168 if (*p == '<') str += "<";
169 else if (*p == '>') str += ">";
170 else if (*p == '&') str += "&";
171 else if (*p == '"') str += """;
172 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
173 else if (*p == '\n' && fMultiLine) str += "<br>\n";
180 string HtmlEscape(const string& str, bool fMultiLine=false)
182 return HtmlEscape(str.c_str(), fMultiLine);
185 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
187 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
191 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
194 return wxMessageBox(message, caption, style, parent, x, y);
196 if (wxThread::IsMain() || fDaemon)
198 return wxMessageBox(message, caption, style, parent, x, y);
204 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
212 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
214 if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
216 string strMessage = strprintf(
217 _("This transaction is over the size limit. You can still send it for a fee of %s, "
218 "which goes to the nodes that process your transaction and helps to support the network. "
219 "Do you want to pay the fee?"),
220 FormatMoney(nFeeRequired).c_str());
221 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
224 void CalledSetStatusBar(const string& strText, int nField)
226 if (nField == 0 && GetWarnings("statusbar") != "")
228 if (pframeMain && pframeMain->m_statusBar)
229 pframeMain->m_statusBar->SetStatusText(strText, nField);
232 void SetDefaultReceivingAddress(const string& strAddress)
234 // Update main window address and database
235 if (pframeMain == NULL)
237 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
239 CBitcoinAddress address(strAddress);
240 if (!address.IsValid())
242 vector<unsigned char> vchPubKey;
243 if (!pwalletMain->GetPubKey(address, vchPubKey))
245 pwalletMain->SetDefaultKey(vchPubKey);
246 pframeMain->m_textCtrlAddress->SetValue(strAddress);
250 bool GetWalletPassphrase()
252 if (pwalletMain->IsLocked())
254 string strWalletPass;
255 strWalletPass.reserve(100);
256 mlock(&strWalletPass[0], strWalletPass.capacity());
258 // obtain current wallet encrypt/decrypt key, from passphrase
259 // Note that the passphrase is not mlock()d during this entry and could potentially
260 // be obtained from disk long after bitcoin has run.
261 strWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
262 _("Passphrase")).ToStdString();
264 if (!strWalletPass.size())
266 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
267 munlock(&strWalletPass[0], strWalletPass.capacity());
268 wxMessageBox(_("Please supply the current wallet decryption passphrase."), "Bitcoin");
272 if (!pwalletMain->Unlock(strWalletPass))
274 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
275 munlock(&strWalletPass[0], strWalletPass.capacity());
276 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin");
279 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
280 munlock(&strWalletPass[0], strWalletPass.capacity());
294 //////////////////////////////////////////////////////////////////////////////
299 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
301 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
303 // Set initially selected page
304 wxNotebookEvent event;
305 event.SetSelection(0);
306 OnNotebookPageChanged(event);
307 m_notebook->ChangeSelection(0);
310 fRefreshListCtrl = false;
311 fRefreshListCtrlRunning = false;
312 fOnSetFocusAddress = false;
314 m_choiceFilter->SetSelection(0);
315 double dResize = nScaleX;
317 SetIcon(wxICON(bitcoin));
318 SetSize(dResize * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
320 SetIcon(bitcoin80_xpm);
321 SetBackgroundColour(m_toolBar->GetBackgroundColour());
322 wxFont fontTmp = m_staticText41->GetFont();
323 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
324 m_staticTextBalance->SetFont(fontTmp);
325 m_staticTextBalance->SetSize(140, 17);
326 // resize to fit ubuntu's huge default font
328 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
330 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
331 m_listCtrl->SetFocus();
332 ptaskbaricon = new CMyTaskBarIcon();
334 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
335 // to their standard places, leaving these menus empty.
336 GetMenuBar()->Remove(2); // remove Help menu
337 GetMenuBar()->Remove(0); // remove File menu
340 // Init column headers
341 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
342 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
348 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
349 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
351 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
352 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
353 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
354 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
355 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
356 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
357 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
361 int pnWidths[3] = { -100, 88, 300 };
363 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
364 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
366 m_statusBar->SetFieldsCount(3, pnWidths);
368 // Fill your address text box
369 vector<unsigned char> vchPubKey;
370 if (CWalletDB(pwalletMain->strWalletFile,"r").ReadDefaultKey(vchPubKey))
371 m_textCtrlAddress->SetValue(CBitcoinAddress(vchPubKey).ToString());
373 if (pwalletMain->IsCrypted())
374 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
376 m_menuOptions->Remove(m_menuOptionsChangeWalletPassphrase);
378 // Fill listctrl with wallet transactions
382 CMainFrame::~CMainFrame()
389 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
392 nPage = event.GetSelection();
395 m_listCtrl = m_listCtrlAll;
396 fShowGenerated = true;
398 fShowReceived = true;
400 else if (nPage == SENTRECEIVED)
402 m_listCtrl = m_listCtrlSentReceived;
403 fShowGenerated = false;
405 fShowReceived = true;
407 else if (nPage == SENT)
409 m_listCtrl = m_listCtrlSent;
410 fShowGenerated = false;
412 fShowReceived = false;
414 else if (nPage == RECEIVED)
416 m_listCtrl = m_listCtrlReceived;
417 fShowGenerated = false;
419 fShowReceived = true;
422 m_listCtrl->SetFocus();
425 void CMainFrame::OnClose(wxCloseEvent& event)
427 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
429 // Divert close to minimize
431 fClosedToTray = true;
437 CreateThread(Shutdown, NULL);
441 void CMainFrame::OnIconize(wxIconizeEvent& event)
444 // Hide the task bar button when minimized.
445 // Event is sent when the frame is minimized or restored.
446 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
447 // to get rid of the deprecated warning. Just ignore it.
448 if (!event.Iconized())
449 fClosedToTray = false;
450 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
451 if (GetBoolArg("-minimizetotray")) {
453 // The tray icon sometimes disappears on ubuntu karmic
454 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
455 // Reports of CPU peg on 64-bit linux
456 if (fMinimizeToTray && event.Iconized())
457 fClosedToTray = true;
458 Show(!fClosedToTray);
459 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
460 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
465 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
469 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
470 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
473 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
475 // Hidden columns not resizeable
476 if (event.GetColumn() <= 1 && !fDebug)
482 int CMainFrame::GetSortIndex(const string& strSort)
487 // The wx generic listctrl implementation used on GTK doesn't sort,
488 // so we have to do it ourselves. Remember, we sort in reverse order.
489 // In the wx generic implementation, they store the list of items
490 // in a vector, so indexed lookups are fast, but inserts are slower
491 // the closer they are to the top.
493 int high = m_listCtrl->GetItemCount();
496 int mid = low + ((high - low) / 2);
497 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
506 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)
508 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
509 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
512 if (!fNew && nIndex == -1)
514 string strHash = " " + hashKey.ToString();
515 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
516 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
520 // fNew is for blind insert, only use if you're sure it's new
521 if (fNew || nIndex == -1)
523 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
527 // If sort key changed, must delete and reinsert to make it relocate
528 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
530 m_listCtrl->DeleteItem(nIndex);
531 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
535 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
536 m_listCtrl->SetItem(nIndex, 2, str2);
537 m_listCtrl->SetItem(nIndex, 3, str3);
538 m_listCtrl->SetItem(nIndex, 4, str4);
539 m_listCtrl->SetItem(nIndex, 5, str5);
540 m_listCtrl->SetItem(nIndex, 6, str6);
541 m_listCtrl->SetItemData(nIndex, nData);
542 SetItemTextColour(m_listCtrl, nIndex, colour);
545 bool CMainFrame::DeleteLine(uint256 hashKey)
547 long nData = *(long*)&hashKey;
551 string strHash = " " + hashKey.ToString();
552 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
553 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
557 m_listCtrl->DeleteItem(nIndex);
562 string FormatTxStatus(const CWalletTx& wtx)
567 if (wtx.nLockTime < LOCKTIME_THRESHOLD)
568 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
570 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
574 int nDepth = wtx.GetDepthInMainChain();
575 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
576 return strprintf(_("%d/offline?"), nDepth);
578 return strprintf(_("%d/unconfirmed"), nDepth);
580 return strprintf(_("%d confirmations"), nDepth);
584 string SingleLine(const string& strIn)
587 bool fOneSpace = false;
588 BOOST_FOREACH(unsigned char c, strIn)
596 if (fOneSpace && !strOut.empty())
605 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
607 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
608 int64 nCredit = wtx.GetCredit(true);
609 int64 nDebit = wtx.GetDebit();
610 int64 nNet = nCredit - nDebit;
611 uint256 hash = wtx.GetHash();
612 string strStatus = FormatTxStatus(wtx);
613 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
614 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
615 map<string, string> mapValue = wtx.mapValue;
616 wtx.nLinesDisplayed = 1;
620 if (wtx.IsCoinBase())
622 // Don't show generated coin until confirmed by at least one block after it
623 // so we don't get the user's hopes up until it looks like it's probably accepted.
625 // It is not an error when generated blocks are not accepted. By design,
626 // some percentage of blocks, like 10% or more, will end up not accepted.
627 // This is the normal mechanism by which the network copes with latency.
629 // We display regular transactions right away before any confirmation
630 // because they can always get into some block eventually. Generated coins
631 // are special because if their block is not accepted, they are not valid.
633 if (wtx.GetDepthInMainChain() < 2)
635 wtx.nLinesDisplayed = 0;
643 // Find the block the tx is in
644 CBlockIndex* pindex = NULL;
645 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
646 if (mi != mapBlockIndex.end())
647 pindex = (*mi).second;
649 // Sort order, unrecorded transactions sort to the top
650 string strSort = strprintf("%010d-%01d-%010u",
651 (pindex ? pindex->nHeight : INT_MAX),
652 (wtx.IsCoinBase() ? 1 : 0),
656 if (nNet > 0 || wtx.IsCoinBase())
661 string strDescription;
662 if (wtx.IsCoinBase())
665 strDescription = _("Generated");
668 int64 nUnmatured = 0;
669 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
670 nUnmatured += pwalletMain->GetCredit(txout);
671 if (wtx.IsInMainChain())
673 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
675 // Check if the block was requested by anyone
676 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
677 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
681 strDescription = _("Generated (not accepted)");
685 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
687 // Received by IP connection
690 if (!mapValue["from"].empty())
691 strDescription += _("From: ") + mapValue["from"];
692 if (!mapValue["message"].empty())
694 if (!strDescription.empty())
695 strDescription += " - ";
696 strDescription += mapValue["message"];
701 // Received by Bitcoin Address
704 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
706 if (pwalletMain->IsMine(txout))
708 CBitcoinAddress address;
709 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
711 CRITICAL_BLOCK(pwalletMain->cs_wallet)
713 //strDescription += _("Received payment to ");
714 //strDescription += _("Received with address ");
715 strDescription += _("Received with: ");
716 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
717 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
719 string strLabel = (*mi).second;
720 strDescription += address.ToString().substr(0,12) + "... ";
721 strDescription += "(" + strLabel + ")";
724 strDescription += address.ToString();
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))
780 CBitcoinAddress address;
782 if (!mapValue["to"].empty())
785 strAddress = mapValue["to"];
789 // Sent to Bitcoin Address
790 if (ExtractAddress(txout.scriptPubKey, NULL, address))
791 strAddress = address.ToString();
794 string strDescription = _("To: ");
795 CRITICAL_BLOCK(pwalletMain->cs_wallet)
796 if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
797 strDescription += pwalletMain->mapAddressBook[address] + " ";
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_wallet)
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_wallet)
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_wallet)
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_wallet)
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->fRefreshListCtrl = true;
1036 pframeMain->GetEventHandler()->AddPendingEvent(event);
1040 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
1042 // Skip lets the listctrl do the paint, we're just hooking the message
1046 ptaskbaricon->UpdateTooltip();
1051 static int nTransactionCount;
1052 bool fPaintedBalance = false;
1053 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1055 nLastRepaint = nNeedRepaint;
1056 nLastRepaintTime = GetTimeMillis();
1058 // Update listctrl contents
1059 if (!pwalletMain->vWalletUpdated.empty())
1061 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
1064 if (m_listCtrl->GetItemCount())
1065 strTop = (string)m_listCtrl->GetItemText(0);
1066 BOOST_FOREACH(uint256 hash, pwalletMain->vWalletUpdated)
1068 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1069 if (mi != pwalletMain->mapWallet.end())
1070 InsertTransaction((*mi).second, false);
1072 pwalletMain->vWalletUpdated.clear();
1073 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1074 m_listCtrl->ScrollList(0, INT_MIN/2);
1079 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
1081 fPaintedBalance = true;
1082 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
1084 // Count hidden and multi-line transactions
1085 nTransactionCount = 0;
1086 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1088 CWalletTx& wtx = (*it).second;
1089 nTransactionCount += wtx.nLinesDisplayed;
1093 if (!pwalletMain->vWalletUpdated.empty() || !fPaintedBalance)
1096 // Update status column of visible items only
1097 RefreshStatusColumn();
1099 // Update status bar
1100 static string strPrevWarning;
1101 string strWarning = GetWarnings("statusbar");
1102 if (strWarning != "")
1103 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1104 else if (strPrevWarning != "")
1105 m_statusBar->SetStatusText("", 0);
1106 strPrevWarning = strWarning;
1109 if (fGenerateBitcoins)
1110 strGen = _(" Generating");
1111 if (fGenerateBitcoins && vNodes.empty())
1112 strGen = _("(not connected)");
1113 m_statusBar->SetStatusText(strGen, 1);
1115 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1116 m_statusBar->SetStatusText(strStatus, 2);
1118 // Update receiving address
1119 string strDefaultAddress = CBitcoinAddress(pwalletMain->vchDefaultKey).ToString();
1120 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1121 m_textCtrlAddress->SetValue(strDefaultAddress);
1125 void UIThreadCall(boost::function0<void> fn)
1127 // Call this with a function object created with bind.
1128 // bind needs all parameters to match the function's expected types
1129 // and all default parameters specified. Some examples:
1130 // UIThreadCall(bind(wxBell));
1131 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1132 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1135 wxCommandEvent event(wxEVT_UITHREADCALL);
1136 event.SetClientData((void*)new boost::function0<void>(fn));
1137 pframeMain->GetEventHandler()->AddPendingEvent(event);
1141 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1143 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1148 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1154 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1156 event.Check(fGenerateBitcoins);
1159 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1161 // Options->Your Receiving Addresses
1162 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1163 if (!dialog.ShowModal())
1167 void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event)
1169 // Options->Encrypt Wallet
1170 if (pwalletMain->IsCrypted())
1172 wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR);
1176 string strWalletPass;
1177 strWalletPass.reserve(100);
1178 mlock(&strWalletPass[0], strWalletPass.capacity());
1180 // obtain current wallet encrypt/decrypt key, from passphrase
1181 // Note that the passphrase is not mlock()d during this entry and could potentially
1182 // be obtained from disk long after bitcoin has run.
1183 strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."),
1184 _("Passphrase")).ToStdString();
1186 if (!strWalletPass.size())
1188 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1189 munlock(&strWalletPass[0], strWalletPass.capacity());
1190 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1194 if(wxMessageBox(_("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"), "Bitcoin", wxYES_NO) != wxYES)
1197 string strWalletPassTest;
1198 strWalletPassTest.reserve(100);
1199 mlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1200 strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."),
1201 _("Passphrase")).ToStdString();
1203 if (strWalletPassTest != strWalletPass)
1205 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1206 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1207 munlock(&strWalletPass[0], strWalletPass.capacity());
1208 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1209 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1213 if (!pwalletMain->EncryptWallet(strWalletPass))
1215 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1216 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1217 munlock(&strWalletPass[0], strWalletPass.capacity());
1218 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1219 wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR);
1222 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1223 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1224 munlock(&strWalletPass[0], strWalletPass.capacity());
1225 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1226 wxMessageBox(_("Wallet Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1228 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
1229 m_menuOptions->Insert(m_menuOptions->GetMenuItemCount() - 1, m_menuOptionsChangeWalletPassphrase);
1232 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1234 // Options->Change Wallet Encryption Passphrase
1235 if (!pwalletMain->IsCrypted())
1237 wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1241 string strOldWalletPass;
1242 strOldWalletPass.reserve(100);
1243 mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1245 // obtain current wallet encrypt/decrypt key, from passphrase
1246 // Note that the passphrase is not mlock()d during this entry and could potentially
1247 // be obtained from disk long after bitcoin has run.
1248 strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1249 _("Passphrase")).ToStdString();
1251 bool fWasLocked = pwalletMain->IsLocked();
1252 pwalletMain->Lock();
1254 if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1256 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1257 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1258 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1263 pwalletMain->Lock();
1265 string strNewWalletPass;
1266 strNewWalletPass.reserve(100);
1267 mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1269 // obtain new wallet encrypt/decrypt key, from passphrase
1270 // Note that the passphrase is not mlock()d during this entry and could potentially
1271 // be obtained from disk long after bitcoin has run.
1272 strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1273 _("Passphrase")).ToStdString();
1275 if (!strNewWalletPass.size())
1277 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1278 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1279 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1280 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1281 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1285 string strNewWalletPassTest;
1286 strNewWalletPassTest.reserve(100);
1287 mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1289 // obtain new wallet encrypt/decrypt key, from passphrase
1290 // Note that the passphrase is not mlock()d during this entry and could potentially
1291 // be obtained from disk long after bitcoin has run.
1292 strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1293 _("Passphrase")).ToStdString();
1295 if (strNewWalletPassTest != strNewWalletPass)
1297 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1298 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1299 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1300 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1301 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1302 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1303 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1307 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1309 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1310 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1311 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1312 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1313 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1314 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1315 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1318 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1319 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1320 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1321 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1322 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1323 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1324 wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
1327 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1330 COptionsDialog dialog(this);
1334 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1337 CAboutDialog dialog(this);
1341 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1344 CSendDialog dialog(this);
1348 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1350 // Toolbar: Address Book
1351 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1352 if (dialogAddr.ShowModal() == 2)
1355 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1356 dialogSend.ShowModal();
1360 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1362 // Automatically select-all when entering window
1364 m_textCtrlAddress->SetSelection(-1, -1);
1365 fOnSetFocusAddress = true;
1368 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1371 if (fOnSetFocusAddress)
1372 m_textCtrlAddress->SetSelection(-1, -1);
1373 fOnSetFocusAddress = false;
1376 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1379 CGetTextFromUserDialog dialog(this,
1380 _("New Receiving Address"),
1381 _("You should use a new address for each payment you receive.\n\nLabel"),
1383 if (!dialog.ShowModal())
1385 string strName = dialog.GetValue();
1389 bool fWasLocked = pwalletMain->IsLocked();
1390 if (!GetWalletPassphrase())
1394 strAddress = CBitcoinAddress(pwalletMain->GetOrReuseKeyFromPool()).ToString();
1397 pwalletMain->Lock();
1400 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1401 pwalletMain->SetAddressBookName(strAddress, strName);
1402 SetDefaultReceivingAddress(strAddress);
1405 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1407 // Copy address box to clipboard
1408 if (wxTheClipboard->Open())
1410 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1411 wxTheClipboard->Close();
1415 void CMainFrame::OnListItemActivated(wxListEvent& event)
1417 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1419 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1421 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1422 if (mi == pwalletMain->mapWallet.end())
1424 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1429 CTxDetailsDialog dialog(this, wtx);
1431 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1440 //////////////////////////////////////////////////////////////////////////////
1445 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1448 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1450 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1453 strHTML.reserve(4000);
1454 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1456 int64 nTime = wtx.GetTxTime();
1457 int64 nCredit = wtx.GetCredit();
1458 int64 nDebit = wtx.GetDebit();
1459 int64 nNet = nCredit - nDebit;
1463 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1464 int nRequests = wtx.GetRequestCount();
1465 if (nRequests != -1)
1468 strHTML += _(", has not been successfully broadcast yet");
1469 else if (nRequests == 1)
1470 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1472 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1476 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1482 if (wtx.IsCoinBase())
1484 strHTML += _("<b>Source:</b> Generated<br>");
1486 else if (!wtx.mapValue["from"].empty())
1488 // Online transaction
1489 if (!wtx.mapValue["from"].empty())
1490 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1494 // Offline transaction
1498 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1500 if (pwalletMain->IsMine(txout))
1502 CBitcoinAddress address;
1503 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1505 if (pwalletMain->mapAddressBook.count(address))
1507 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1508 strHTML += _("<b>To:</b> ");
1509 strHTML += HtmlEscape(address.ToString());
1510 if (!pwalletMain->mapAddressBook[address].empty())
1511 strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[address] + ")";
1513 strHTML += _(" (yours)");
1528 if (!wtx.mapValue["to"].empty())
1530 // Online transaction
1531 strAddress = wtx.mapValue["to"];
1532 strHTML += _("<b>To:</b> ");
1533 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1534 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1535 strHTML += HtmlEscape(strAddress) + "<br>";
1542 if (wtx.IsCoinBase() && nCredit == 0)
1547 int64 nUnmatured = 0;
1548 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1549 nUnmatured += pwalletMain->GetCredit(txout);
1550 strHTML += _("<b>Credit:</b> ");
1551 if (wtx.IsInMainChain())
1552 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1554 strHTML += _("(not accepted)");
1562 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1566 bool fAllFromMe = true;
1567 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1568 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1570 bool fAllToMe = true;
1571 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1572 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1579 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1581 if (pwalletMain->IsMine(txout))
1584 if (wtx.mapValue["to"].empty())
1586 // Offline transaction
1587 CBitcoinAddress address;
1588 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1590 string strAddress = address.ToString();
1591 strHTML += _("<b>To:</b> ");
1592 if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
1593 strHTML += pwalletMain->mapAddressBook[address] + " ";
1594 strHTML += strAddress;
1599 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1605 int64 nChange = wtx.GetChange();
1606 int64 nValue = nCredit - nChange;
1607 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1608 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1611 int64 nTxFee = nDebit - wtx.GetValueOut();
1613 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1618 // Mixed debit transaction
1620 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1621 if (pwalletMain->IsMine(txin))
1622 strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1623 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1624 if (pwalletMain->IsMine(txout))
1625 strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1629 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1635 if (!wtx.mapValue["message"].empty())
1636 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1637 if (!wtx.mapValue["comment"].empty())
1638 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1640 if (wtx.IsCoinBase())
1641 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>";
1649 strHTML += "<hr><br>debug print<br><br>";
1650 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1651 if (pwalletMain->IsMine(txin))
1652 strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1653 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1654 if (pwalletMain->IsMine(txout))
1655 strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1657 strHTML += "<br><b>Transaction:</b><br>";
1658 strHTML += HtmlEscape(wtx.ToString(), true);
1660 strHTML += "<br><b>Inputs:</b><br>";
1661 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1663 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1665 COutPoint prevout = txin.prevout;
1666 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1667 if (mi != pwalletMain->mapWallet.end())
1669 const CWalletTx& prev = (*mi).second;
1670 if (prevout.n < prev.vout.size())
1672 strHTML += HtmlEscape(prev.ToString(), true);
1673 strHTML += " " + FormatTxStatus(prev) + ", ";
1674 strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1683 strHTML += "</font></html>";
1684 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1685 m_htmlWin->SetPage(strHTML);
1686 m_buttonOK->SetFocus();
1690 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1700 //////////////////////////////////////////////////////////////////////////////
1706 string StartupShortcutPath()
1708 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1711 bool GetStartOnSystemStartup()
1713 return filesystem::exists(StartupShortcutPath().c_str());
1716 void SetStartOnSystemStartup(bool fAutoStart)
1718 // If the shortcut exists already, remove it for updating
1719 remove(StartupShortcutPath().c_str());
1725 // Get a pointer to the IShellLink interface.
1726 IShellLink* psl = NULL;
1727 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1728 CLSCTX_INPROC_SERVER, IID_IShellLink,
1729 reinterpret_cast<void**>(&psl));
1731 if (SUCCEEDED(hres))
1733 // Get the current executable path
1734 TCHAR pszExePath[MAX_PATH];
1735 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1737 // Set the path to the shortcut target
1738 psl->SetPath(pszExePath);
1739 PathRemoveFileSpec(pszExePath);
1740 psl->SetWorkingDirectory(pszExePath);
1741 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1743 // Query IShellLink for the IPersistFile interface for
1744 // saving the shortcut in persistent storage.
1745 IPersistFile* ppf = NULL;
1746 hres = psl->QueryInterface(IID_IPersistFile,
1747 reinterpret_cast<void**>(&ppf));
1748 if (SUCCEEDED(hres))
1750 WCHAR pwsz[MAX_PATH];
1751 // Ensure that the string is ANSI.
1752 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1753 // Save the link by calling IPersistFile::Save.
1754 hres = ppf->Save(pwsz, TRUE);
1763 #elif defined(__WXGTK__)
1765 // Follow the Desktop Application Autostart Spec:
1766 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1768 boost::filesystem::path GetAutostartDir()
1770 namespace fs = boost::filesystem;
1772 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1773 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1774 char* pszHome = getenv("HOME");
1775 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1779 boost::filesystem::path GetAutostartFilePath()
1781 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1784 bool GetStartOnSystemStartup()
1786 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1787 if (!optionFile.good())
1789 // Scan through file for "Hidden=true":
1791 while (!optionFile.eof())
1793 getline(optionFile, line);
1794 if (line.find("Hidden") != string::npos &&
1795 line.find("true") != string::npos)
1803 void SetStartOnSystemStartup(bool fAutoStart)
1807 unlink(GetAutostartFilePath().native_file_string().c_str());
1811 char pszExePath[MAX_PATH+1];
1812 memset(pszExePath, 0, sizeof(pszExePath));
1813 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1816 boost::filesystem::create_directories(GetAutostartDir());
1818 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1819 if (!optionFile.good())
1821 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1824 // Write a bitcoin.desktop file to the autostart directory:
1825 optionFile << "[Desktop Entry]\n";
1826 optionFile << "Type=Application\n";
1827 optionFile << "Name=Bitcoin\n";
1828 optionFile << "Exec=" << pszExePath << "\n";
1829 optionFile << "Terminal=false\n";
1830 optionFile << "Hidden=false\n";
1836 // TODO: OSX startup stuff; see:
1837 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1839 bool GetStartOnSystemStartup() { return false; }
1840 void SetStartOnSystemStartup(bool fAutoStart) { }
1849 //////////////////////////////////////////////////////////////////////////////
1854 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1856 // Set up list box of page choices
1857 m_listBox->Append(_("Main"));
1858 //m_listBox->Append(_("Test 2"));
1859 m_listBox->SetSelection(0);
1862 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1864 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1866 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1867 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1868 if (!GetBoolArg("-minimizetotray"))
1870 // Minimize to tray is just too buggy on Linux
1871 fMinimizeToTray = false;
1872 m_checkBoxMinimizeToTray->SetValue(false);
1873 m_checkBoxMinimizeToTray->Enable(false);
1874 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1877 #ifdef __WXMAC_OSX__
1878 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1882 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1883 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1884 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1885 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1887 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1889 m_checkBoxUseUPnP->Enable(false);
1890 m_checkBoxUseProxy->SetValue(fUseProxy);
1891 m_textCtrlProxyIP->Enable(fUseProxy);
1892 m_textCtrlProxyPort->Enable(fUseProxy);
1893 m_staticTextProxyIP->Enable(fUseProxy);
1894 m_staticTextProxyPort->Enable(fUseProxy);
1895 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1896 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1898 m_buttonOK->SetFocus();
1901 void COptionsDialog::SelectPage(int nPage)
1903 m_panelMain->Show(nPage == 0);
1904 m_panelTest2->Show(nPage == 1);
1906 m_scrolledWindow->Layout();
1907 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1910 void COptionsDialog::OnListBox(wxCommandEvent& event)
1912 SelectPage(event.GetSelection());
1915 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1918 int64 nTmp = nTransactionFee;
1919 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1920 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1923 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1925 m_textCtrlProxyIP->Enable(event.IsChecked());
1926 m_textCtrlProxyPort->Enable(event.IsChecked());
1927 m_staticTextProxyIP->Enable(event.IsChecked());
1928 m_staticTextProxyPort->Enable(event.IsChecked());
1931 CAddress COptionsDialog::GetProxyAddr()
1933 // Be careful about byte order, addr.ip and addr.port are big endian
1934 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1935 if (addr.ip == INADDR_NONE)
1936 addr.ip = addrProxy.ip;
1937 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1938 addr.port = htons(nPort);
1939 if (nPort <= 0 || nPort > USHRT_MAX)
1940 addr.port = addrProxy.port;
1944 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1947 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1948 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1952 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1954 OnButtonApply(event);
1958 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1963 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1965 CWalletDB walletdb(pwalletMain->strWalletFile);
1967 int64 nPrevTransactionFee = nTransactionFee;
1968 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1969 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1971 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1973 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1974 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1977 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1979 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1980 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1981 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1984 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1986 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1987 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1990 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1992 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1993 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1997 fUseProxy = m_checkBoxUseProxy->GetValue();
1998 walletdb.WriteSetting("fUseProxy", fUseProxy);
2000 addrProxy = GetProxyAddr();
2001 walletdb.WriteSetting("addrProxy", addrProxy);
2009 //////////////////////////////////////////////////////////////////////////////
2014 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2016 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2018 // Change (c) into UTF-8 or ANSI copyright symbol
2019 wxString str = m_staticTextMain->GetLabel();
2021 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2023 str.Replace("(c)", "\xA9");
2025 m_staticTextMain->SetLabel(str);
2027 // Resize on Linux to make the window fit the text.
2028 // The text was wrapped manually rather than using the Wrap setting because
2029 // the wrap would be too small on Linux and it can't be changed at this point.
2030 wxFont fontTmp = m_staticTextMain->GetFont();
2031 if (fontTmp.GetPointSize() > 8);
2032 fontTmp.SetPointSize(8);
2033 m_staticTextMain->SetFont(fontTmp);
2034 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2036 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2040 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2050 //////////////////////////////////////////////////////////////////////////////
2055 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2058 m_textCtrlAddress->SetValue(strAddress);
2059 m_choiceTransferType->SetSelection(0);
2060 m_bitmapCheckMark->Show(false);
2061 fEnabledPrev = true;
2062 m_textCtrlAddress->SetFocus();
2064 //// todo: should add a display of your balance for convenience
2066 wxFont fontTmp = m_staticTextInstructions->GetFont();
2067 if (fontTmp.GetPointSize() > 9);
2068 fontTmp.SetPointSize(9);
2069 m_staticTextInstructions->SetFont(fontTmp);
2072 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2076 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2079 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2084 SetIcon(wxICON(bitcoin));
2087 // Fixup the tab order
2088 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2089 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2093 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2095 // Reformat the amount
2097 if (m_textCtrlAmount->GetValue().Trim().empty())
2100 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2101 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2104 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2106 // Open address book
2107 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2108 if (dialog.ShowModal())
2109 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2112 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2114 // Copy clipboard to address box
2115 if (wxTheClipboard->Open())
2117 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2119 wxTextDataObject data;
2120 wxTheClipboard->GetData(data);
2121 m_textCtrlAddress->SetValue(data.GetText());
2123 wxTheClipboard->Close();
2127 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2129 static CCriticalSection cs_sendlock;
2130 TRY_CRITICAL_BLOCK(cs_sendlock)
2133 string strAddress = (string)m_textCtrlAddress->GetValue();
2137 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2139 wxMessageBox(_("Error in amount "), _("Send Coins"));
2142 if (nValue > pwalletMain->GetBalance())
2144 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2147 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2149 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2153 // Parse bitcoin address
2154 CBitcoinAddress address(strAddress);
2155 bool fBitcoinAddress = address.IsValid();
2157 if (fBitcoinAddress)
2159 bool fWasLocked = pwalletMain->IsLocked();
2160 if (!GetWalletPassphrase())
2164 CRITICAL_BLOCK(cs_main)
2165 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2167 // Send to bitcoin address
2168 CScript scriptPubKey;
2169 scriptPubKey.SetBitcoinAddress(address);
2171 strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2174 wxMessageBox(_("Payment sent "), _("Sending..."));
2175 else if (strError == "ABORTED")
2178 pwalletMain->Lock();
2179 return; // leave send dialog open
2183 wxMessageBox(strError + " ", _("Sending..."));
2186 pwalletMain->Lock();
2191 pwalletMain->Lock();
2196 CAddress addr(strAddress);
2197 if (!addr.IsValid())
2199 wxMessageBox(_("Invalid address "), _("Send Coins"));
2204 wtx.mapValue["to"] = strAddress;
2206 // Send to IP address
2207 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2208 if (!pdialog->ShowModal())
2212 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2213 if (!pwalletMain->mapAddressBook.count(address))
2214 pwalletMain->SetAddressBookName(strAddress, "");
2220 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2231 //////////////////////////////////////////////////////////////////////////////
2236 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
2241 start = wxDateTime::UNow();
2242 memset(pszStatus, 0, sizeof(pszStatus));
2249 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2251 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2254 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2255 m_textCtrlStatus->SetValue("");
2257 CreateThread(SendingDialogStartTransfer, this);
2260 CSendingDialog::~CSendingDialog()
2262 printf("~CSendingDialog()\n");
2265 void CSendingDialog::Close()
2267 // Last one out turn out the lights.
2268 // fWorkDone signals that work side is done and UI thread should call destroy.
2269 // fUIDone signals that UI window has closed and work thread should call destroy.
2270 // This allows the window to disappear and end modality when cancelled
2271 // without making the user wait for ConnectNode to return. The dialog object
2272 // hangs around in the background until the work thread exits.
2283 void CSendingDialog::OnClose(wxCloseEvent& event)
2285 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2292 wxCommandEvent cmdevent;
2293 OnButtonCancel(cmdevent);
2297 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2303 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2309 void CSendingDialog::OnPaint(wxPaintEvent& event)
2312 if (strlen(pszStatus) > 130)
2313 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2315 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2316 m_staticTextSending->SetFocus();
2318 m_buttonCancel->Enable(false);
2321 m_buttonOK->Enable(true);
2322 m_buttonOK->SetFocus();
2323 m_buttonCancel->Enable(false);
2325 if (fAbort && fCanCancel && IsShown())
2327 strcpy(pszStatus, _("CANCELLED"));
2328 m_buttonOK->Enable(true);
2329 m_buttonOK->SetFocus();
2330 m_buttonCancel->Enable(false);
2331 m_buttonCancel->SetLabel(_("Cancelled"));
2333 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2339 // Everything from here on is not in the UI thread and must only communicate
2340 // with the rest of the dialog through variables and calling repaint.
2343 void CSendingDialog::Repaint()
2347 GetEventHandler()->AddPendingEvent(event);
2350 bool CSendingDialog::Status()
2357 if (fAbort && fCanCancel)
2359 memset(pszStatus, 0, 10);
2360 strcpy(pszStatus, _("CANCELLED"));
2368 bool CSendingDialog::Status(const string& str)
2373 // This can be read by the UI thread at any time,
2374 // so copy in a way that can be read cleanly at all times.
2375 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2376 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2382 bool CSendingDialog::Error(const string& str)
2386 Status(string(_("Error: ")) + str);
2390 void SendingDialogStartTransfer(void* parg)
2392 ((CSendingDialog*)parg)->StartTransfer();
2395 void CSendingDialog::StartTransfer()
2397 // Make sure we have enough money
2398 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2400 Error(_("Insufficient funds"));
2404 // We may have connected already for product details
2405 if (!Status(_("Connecting...")))
2407 CNode* pnode = ConnectNode(addr, 15 * 60);
2410 Error(_("Unable to connect"));
2414 // Send order to seller, with response going to OnReply2 via event handler
2415 if (!Status(_("Requesting public key...")))
2417 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2420 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2422 ((CSendingDialog*)parg)->OnReply2(vRecv);
2425 void CSendingDialog::OnReply2(CDataStream& vRecv)
2427 if (!Status(_("Received public key...")))
2430 CScript scriptPubKey;
2439 vRecv >> strMessage;
2441 Error(_("Recipient is not accepting transactions sent by IP address"));
2443 Error(_("Transfer was not accepted"));
2444 //// todo: enlarge the window and enable a hidden white box to put seller's message
2447 vRecv >> scriptPubKey;
2451 //// what do we want to do about this?
2452 Error(_("Invalid response received"));
2456 // Pause to give the user a chance to cancel
2457 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2465 if (!Status(_("Creating transaction...")))
2467 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2469 Error(_("Insufficient funds"));
2473 CReserveKey reservekey(pwalletMain);
2475 bool fWasLocked = pwalletMain->IsLocked();
2476 if (!GetWalletPassphrase())
2479 bool fTxCreated = false;
2480 CRITICAL_BLOCK(cs_main)
2481 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2483 fTxCreated = 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();
2498 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2500 Error(_("Transaction aborted"));
2504 // Make sure we're still connected
2505 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2508 Error(_("Lost connection, transaction cancelled"));
2512 // Last chance to cancel
2524 if (!Status(_("Sending payment...")))
2528 bool fTxCommitted = false;
2529 CRITICAL_BLOCK(cs_main)
2530 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2532 fTxCommitted = pwalletMain->CommitTransaction(wtx, reservekey);
2536 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."));
2540 // Send payment tx to seller, with response going to OnReply3 via event handler
2541 CWalletTx wtxSend = wtx;
2542 wtxSend.fFromMe = false;
2543 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2545 Status(_("Waiting for confirmation..."));
2549 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2551 ((CSendingDialog*)parg)->OnReply3(vRecv);
2554 void CSendingDialog::OnReply3(CDataStream& vRecv)
2562 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2563 "The transaction is recorded and will credit to the recipient,\n"
2564 "but the comment information will be blank."));
2570 //// what do we want to do about this?
2571 Error(_("Payment was sent, but an invalid response was received"));
2577 Status(_("Payment completed"));
2585 //////////////////////////////////////////////////////////////////////////////
2587 // CAddressBookDialog
2590 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2593 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2596 // Set initially selected page
2597 wxNotebookEvent event;
2598 event.SetSelection(nPageIn);
2599 OnNotebookPageChanged(event);
2600 m_notebook->ChangeSelection(nPageIn);
2602 fDuringSend = fDuringSendIn;
2604 m_buttonCancel->Show(false);
2607 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2609 wxIcon iconAddressBook;
2610 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2611 SetIcon(iconAddressBook);
2615 SetIcon(wxICON(bitcoin));
2618 // Init column headers
2619 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2620 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2621 m_listCtrlSending->SetFocus();
2622 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2623 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2624 m_listCtrlReceiving->SetFocus();
2626 // Fill listctrl with address book data
2627 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2629 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2630 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2632 const CBitcoinAddress& address = item.first;
2633 string strName = item.second;
2634 bool fMine = pwalletMain->HaveKey(address);
2635 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2636 int nIndex = InsertLine(plistCtrl, strName, address.ToString());
2637 if (address.ToString() == (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_wallet)
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_wallet)
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)
2744 CBitcoinAddress address(strAddress);
2745 bool fMine = address.IsValid() && pwalletMain->HaveKey(address);
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_wallet)
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 bool fWasLocked = pwalletMain->IsLocked();
2825 if (!GetWalletPassphrase())
2829 strAddress = CBitcoinAddress(pwalletMain->GetOrReuseKeyFromPool()).ToString();
2832 pwalletMain->Lock();
2835 // Add to list and select it
2836 CRITICAL_BLOCK(pwalletMain->cs_wallet)
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);