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->GetEventHandler()->AddPendingEvent(event);
1039 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
1041 // Skip lets the listctrl do the paint, we're just hooking the message
1045 ptaskbaricon->UpdateTooltip();
1050 static int nTransactionCount;
1051 bool fPaintedBalance = false;
1052 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1054 nLastRepaint = nNeedRepaint;
1055 nLastRepaintTime = GetTimeMillis();
1057 // Update listctrl contents
1058 if (!pwalletMain->vWalletUpdated.empty())
1060 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
1063 if (m_listCtrl->GetItemCount())
1064 strTop = (string)m_listCtrl->GetItemText(0);
1065 BOOST_FOREACH(uint256 hash, pwalletMain->vWalletUpdated)
1067 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1068 if (mi != pwalletMain->mapWallet.end())
1069 InsertTransaction((*mi).second, false);
1071 pwalletMain->vWalletUpdated.clear();
1072 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1073 m_listCtrl->ScrollList(0, INT_MIN/2);
1078 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
1080 fPaintedBalance = true;
1081 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
1083 // Count hidden and multi-line transactions
1084 nTransactionCount = 0;
1085 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1087 CWalletTx& wtx = (*it).second;
1088 nTransactionCount += wtx.nLinesDisplayed;
1092 if (!pwalletMain->vWalletUpdated.empty() || !fPaintedBalance)
1095 // Update status column of visible items only
1096 RefreshStatusColumn();
1098 // Update status bar
1099 static string strPrevWarning;
1100 string strWarning = GetWarnings("statusbar");
1101 if (strWarning != "")
1102 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1103 else if (strPrevWarning != "")
1104 m_statusBar->SetStatusText("", 0);
1105 strPrevWarning = strWarning;
1108 if (fGenerateBitcoins)
1109 strGen = _(" Generating");
1110 if (fGenerateBitcoins && vNodes.empty())
1111 strGen = _("(not connected)");
1112 m_statusBar->SetStatusText(strGen, 1);
1114 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1115 m_statusBar->SetStatusText(strStatus, 2);
1117 // Update receiving address
1118 string strDefaultAddress = CBitcoinAddress(pwalletMain->vchDefaultKey).ToString();
1119 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1120 m_textCtrlAddress->SetValue(strDefaultAddress);
1124 void UIThreadCall(boost::function0<void> fn)
1126 // Call this with a function object created with bind.
1127 // bind needs all parameters to match the function's expected types
1128 // and all default parameters specified. Some examples:
1129 // UIThreadCall(bind(wxBell));
1130 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1131 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1134 wxCommandEvent event(wxEVT_UITHREADCALL);
1135 event.SetClientData((void*)new boost::function0<void>(fn));
1136 pframeMain->GetEventHandler()->AddPendingEvent(event);
1140 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1142 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1147 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1153 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1155 event.Check(fGenerateBitcoins);
1158 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1160 // Options->Your Receiving Addresses
1161 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1162 if (!dialog.ShowModal())
1166 void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event)
1168 // Options->Encrypt Wallet
1169 if (pwalletMain->IsCrypted())
1171 wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR);
1175 string strWalletPass;
1176 strWalletPass.reserve(100);
1177 mlock(&strWalletPass[0], strWalletPass.capacity());
1179 // obtain current wallet encrypt/decrypt key, from passphrase
1180 // Note that the passphrase is not mlock()d during this entry and could potentially
1181 // be obtained from disk long after bitcoin has run.
1182 strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."),
1183 _("Passphrase")).ToStdString();
1185 if (!strWalletPass.size())
1187 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1188 munlock(&strWalletPass[0], strWalletPass.capacity());
1189 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1193 if(wxMessageBox(_("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"), "Bitcoin", wxYES_NO) != wxYES)
1196 string strWalletPassTest;
1197 strWalletPassTest.reserve(100);
1198 mlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1199 strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."),
1200 _("Passphrase")).ToStdString();
1202 if (strWalletPassTest != strWalletPass)
1204 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1205 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1206 munlock(&strWalletPass[0], strWalletPass.capacity());
1207 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1208 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1212 if (!pwalletMain->EncryptWallet(strWalletPass))
1214 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1215 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1216 munlock(&strWalletPass[0], strWalletPass.capacity());
1217 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1218 wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR);
1221 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1222 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1223 munlock(&strWalletPass[0], strWalletPass.capacity());
1224 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1225 wxMessageBox(_("Wallet Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1227 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
1228 m_menuOptions->Insert(m_menuOptions->GetMenuItemCount() - 1, m_menuOptionsChangeWalletPassphrase);
1231 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1233 // Options->Change Wallet Encryption Passphrase
1234 if (!pwalletMain->IsCrypted())
1236 wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1240 string strOldWalletPass;
1241 strOldWalletPass.reserve(100);
1242 mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1244 // obtain current wallet encrypt/decrypt key, from passphrase
1245 // Note that the passphrase is not mlock()d during this entry and could potentially
1246 // be obtained from disk long after bitcoin has run.
1247 strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1248 _("Passphrase")).ToStdString();
1250 bool fWasLocked = pwalletMain->IsLocked();
1251 pwalletMain->Lock();
1253 if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1255 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1256 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1257 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1262 pwalletMain->Lock();
1264 string strNewWalletPass;
1265 strNewWalletPass.reserve(100);
1266 mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1268 // obtain new wallet encrypt/decrypt key, from passphrase
1269 // Note that the passphrase is not mlock()d during this entry and could potentially
1270 // be obtained from disk long after bitcoin has run.
1271 strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1272 _("Passphrase")).ToStdString();
1274 if (!strNewWalletPass.size())
1276 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1277 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1278 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1279 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1280 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1284 string strNewWalletPassTest;
1285 strNewWalletPassTest.reserve(100);
1286 mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1288 // obtain new wallet encrypt/decrypt key, from passphrase
1289 // Note that the passphrase is not mlock()d during this entry and could potentially
1290 // be obtained from disk long after bitcoin has run.
1291 strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1292 _("Passphrase")).ToStdString();
1294 if (strNewWalletPassTest != strNewWalletPass)
1296 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1297 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1298 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1299 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1300 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1301 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1302 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1306 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1308 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1309 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1310 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1311 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1312 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1313 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1314 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1317 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1318 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1319 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1320 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1321 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1322 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1323 wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
1326 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1329 COptionsDialog dialog(this);
1333 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1336 CAboutDialog dialog(this);
1340 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1343 CSendDialog dialog(this);
1347 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1349 // Toolbar: Address Book
1350 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1351 if (dialogAddr.ShowModal() == 2)
1354 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1355 dialogSend.ShowModal();
1359 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1361 // Automatically select-all when entering window
1363 m_textCtrlAddress->SetSelection(-1, -1);
1364 fOnSetFocusAddress = true;
1367 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1370 if (fOnSetFocusAddress)
1371 m_textCtrlAddress->SetSelection(-1, -1);
1372 fOnSetFocusAddress = false;
1375 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1378 CGetTextFromUserDialog dialog(this,
1379 _("New Receiving Address"),
1380 _("You should use a new address for each payment you receive.\n\nLabel"),
1382 if (!dialog.ShowModal())
1384 string strName = dialog.GetValue();
1388 bool fWasLocked = pwalletMain->IsLocked();
1389 if (!GetWalletPassphrase())
1393 std::vector<unsigned char> newKey;
1394 pwalletMain->GetKeyFromPool(newKey, true);
1395 strAddress = CBitcoinAddress(newKey).ToString();
1398 pwalletMain->Lock();
1401 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1402 pwalletMain->SetAddressBookName(strAddress, strName);
1403 SetDefaultReceivingAddress(strAddress);
1406 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1408 // Copy address box to clipboard
1409 if (wxTheClipboard->Open())
1411 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1412 wxTheClipboard->Close();
1416 void CMainFrame::OnListItemActivated(wxListEvent& event)
1418 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1420 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1422 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1423 if (mi == pwalletMain->mapWallet.end())
1425 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1430 CTxDetailsDialog dialog(this, wtx);
1432 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1441 //////////////////////////////////////////////////////////////////////////////
1446 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1449 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1451 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1454 strHTML.reserve(4000);
1455 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1457 int64 nTime = wtx.GetTxTime();
1458 int64 nCredit = wtx.GetCredit();
1459 int64 nDebit = wtx.GetDebit();
1460 int64 nNet = nCredit - nDebit;
1464 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1465 int nRequests = wtx.GetRequestCount();
1466 if (nRequests != -1)
1469 strHTML += _(", has not been successfully broadcast yet");
1470 else if (nRequests == 1)
1471 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1473 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1477 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1483 if (wtx.IsCoinBase())
1485 strHTML += _("<b>Source:</b> Generated<br>");
1487 else if (!wtx.mapValue["from"].empty())
1489 // Online transaction
1490 if (!wtx.mapValue["from"].empty())
1491 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1495 // Offline transaction
1499 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1501 if (pwalletMain->IsMine(txout))
1503 CBitcoinAddress address;
1504 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1506 if (pwalletMain->mapAddressBook.count(address))
1508 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1509 strHTML += _("<b>To:</b> ");
1510 strHTML += HtmlEscape(address.ToString());
1511 if (!pwalletMain->mapAddressBook[address].empty())
1512 strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[address] + ")";
1514 strHTML += _(" (yours)");
1529 if (!wtx.mapValue["to"].empty())
1531 // Online transaction
1532 strAddress = wtx.mapValue["to"];
1533 strHTML += _("<b>To:</b> ");
1534 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1535 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1536 strHTML += HtmlEscape(strAddress) + "<br>";
1543 if (wtx.IsCoinBase() && nCredit == 0)
1548 int64 nUnmatured = 0;
1549 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1550 nUnmatured += pwalletMain->GetCredit(txout);
1551 strHTML += _("<b>Credit:</b> ");
1552 if (wtx.IsInMainChain())
1553 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1555 strHTML += _("(not accepted)");
1563 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1567 bool fAllFromMe = true;
1568 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1569 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1571 bool fAllToMe = true;
1572 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1573 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1580 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1582 if (pwalletMain->IsMine(txout))
1585 if (wtx.mapValue["to"].empty())
1587 // Offline transaction
1588 CBitcoinAddress address;
1589 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1591 string strAddress = address.ToString();
1592 strHTML += _("<b>To:</b> ");
1593 if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
1594 strHTML += pwalletMain->mapAddressBook[address] + " ";
1595 strHTML += strAddress;
1600 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1606 int64 nChange = wtx.GetChange();
1607 int64 nValue = nCredit - nChange;
1608 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1609 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1612 int64 nTxFee = nDebit - wtx.GetValueOut();
1614 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1619 // Mixed debit transaction
1621 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1622 if (pwalletMain->IsMine(txin))
1623 strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1624 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1625 if (pwalletMain->IsMine(txout))
1626 strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1630 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1636 if (!wtx.mapValue["message"].empty())
1637 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1638 if (!wtx.mapValue["comment"].empty())
1639 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1641 if (wtx.IsCoinBase())
1642 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>";
1650 strHTML += "<hr><br>debug print<br><br>";
1651 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1652 if (pwalletMain->IsMine(txin))
1653 strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1654 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1655 if (pwalletMain->IsMine(txout))
1656 strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1658 strHTML += "<br><b>Transaction:</b><br>";
1659 strHTML += HtmlEscape(wtx.ToString(), true);
1661 strHTML += "<br><b>Inputs:</b><br>";
1662 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1664 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1666 COutPoint prevout = txin.prevout;
1667 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1668 if (mi != pwalletMain->mapWallet.end())
1670 const CWalletTx& prev = (*mi).second;
1671 if (prevout.n < prev.vout.size())
1673 strHTML += HtmlEscape(prev.ToString(), true);
1674 strHTML += " " + FormatTxStatus(prev) + ", ";
1675 strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1684 strHTML += "</font></html>";
1685 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1686 m_htmlWin->SetPage(strHTML);
1687 m_buttonOK->SetFocus();
1691 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1701 //////////////////////////////////////////////////////////////////////////////
1707 string StartupShortcutPath()
1709 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1712 bool GetStartOnSystemStartup()
1714 return filesystem::exists(StartupShortcutPath().c_str());
1717 void SetStartOnSystemStartup(bool fAutoStart)
1719 // If the shortcut exists already, remove it for updating
1720 remove(StartupShortcutPath().c_str());
1726 // Get a pointer to the IShellLink interface.
1727 IShellLink* psl = NULL;
1728 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1729 CLSCTX_INPROC_SERVER, IID_IShellLink,
1730 reinterpret_cast<void**>(&psl));
1732 if (SUCCEEDED(hres))
1734 // Get the current executable path
1735 TCHAR pszExePath[MAX_PATH];
1736 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1738 // Set the path to the shortcut target
1739 psl->SetPath(pszExePath);
1740 PathRemoveFileSpec(pszExePath);
1741 psl->SetWorkingDirectory(pszExePath);
1742 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1744 // Query IShellLink for the IPersistFile interface for
1745 // saving the shortcut in persistent storage.
1746 IPersistFile* ppf = NULL;
1747 hres = psl->QueryInterface(IID_IPersistFile,
1748 reinterpret_cast<void**>(&ppf));
1749 if (SUCCEEDED(hres))
1751 WCHAR pwsz[MAX_PATH];
1752 // Ensure that the string is ANSI.
1753 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1754 // Save the link by calling IPersistFile::Save.
1755 hres = ppf->Save(pwsz, TRUE);
1764 #elif defined(__WXGTK__)
1766 // Follow the Desktop Application Autostart Spec:
1767 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1769 boost::filesystem::path GetAutostartDir()
1771 namespace fs = boost::filesystem;
1773 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1774 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1775 char* pszHome = getenv("HOME");
1776 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1780 boost::filesystem::path GetAutostartFilePath()
1782 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1785 bool GetStartOnSystemStartup()
1787 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1788 if (!optionFile.good())
1790 // Scan through file for "Hidden=true":
1792 while (!optionFile.eof())
1794 getline(optionFile, line);
1795 if (line.find("Hidden") != string::npos &&
1796 line.find("true") != string::npos)
1804 void SetStartOnSystemStartup(bool fAutoStart)
1808 unlink(GetAutostartFilePath().native_file_string().c_str());
1812 char pszExePath[MAX_PATH+1];
1813 memset(pszExePath, 0, sizeof(pszExePath));
1814 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1817 boost::filesystem::create_directories(GetAutostartDir());
1819 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1820 if (!optionFile.good())
1822 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1825 // Write a bitcoin.desktop file to the autostart directory:
1826 optionFile << "[Desktop Entry]\n";
1827 optionFile << "Type=Application\n";
1828 optionFile << "Name=Bitcoin\n";
1829 optionFile << "Exec=" << pszExePath << "\n";
1830 optionFile << "Terminal=false\n";
1831 optionFile << "Hidden=false\n";
1837 // TODO: OSX startup stuff; see:
1838 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1840 bool GetStartOnSystemStartup() { return false; }
1841 void SetStartOnSystemStartup(bool fAutoStart) { }
1850 //////////////////////////////////////////////////////////////////////////////
1855 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1857 // Set up list box of page choices
1858 m_listBox->Append(_("Main"));
1859 //m_listBox->Append(_("Test 2"));
1860 m_listBox->SetSelection(0);
1863 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1865 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1867 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1868 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1869 if (!GetBoolArg("-minimizetotray"))
1871 // Minimize to tray is just too buggy on Linux
1872 fMinimizeToTray = false;
1873 m_checkBoxMinimizeToTray->SetValue(false);
1874 m_checkBoxMinimizeToTray->Enable(false);
1875 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1878 #ifdef __WXMAC_OSX__
1879 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1883 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1884 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1885 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1886 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1888 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1890 m_checkBoxUseUPnP->Enable(false);
1891 m_checkBoxUseProxy->SetValue(fUseProxy);
1892 m_textCtrlProxyIP->Enable(fUseProxy);
1893 m_textCtrlProxyPort->Enable(fUseProxy);
1894 m_staticTextProxyIP->Enable(fUseProxy);
1895 m_staticTextProxyPort->Enable(fUseProxy);
1896 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1897 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1899 m_buttonOK->SetFocus();
1902 void COptionsDialog::SelectPage(int nPage)
1904 m_panelMain->Show(nPage == 0);
1905 m_panelTest2->Show(nPage == 1);
1907 m_scrolledWindow->Layout();
1908 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1911 void COptionsDialog::OnListBox(wxCommandEvent& event)
1913 SelectPage(event.GetSelection());
1916 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1919 int64 nTmp = nTransactionFee;
1920 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1921 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1924 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1926 m_textCtrlProxyIP->Enable(event.IsChecked());
1927 m_textCtrlProxyPort->Enable(event.IsChecked());
1928 m_staticTextProxyIP->Enable(event.IsChecked());
1929 m_staticTextProxyPort->Enable(event.IsChecked());
1932 CAddress COptionsDialog::GetProxyAddr()
1934 // Be careful about byte order, addr.ip and addr.port are big endian
1935 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1936 if (addr.ip == INADDR_NONE)
1937 addr.ip = addrProxy.ip;
1938 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1939 addr.port = htons(nPort);
1940 if (nPort <= 0 || nPort > USHRT_MAX)
1941 addr.port = addrProxy.port;
1945 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1948 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1949 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1953 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1955 OnButtonApply(event);
1959 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1964 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1966 CWalletDB walletdb(pwalletMain->strWalletFile);
1968 int64 nPrevTransactionFee = nTransactionFee;
1969 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1970 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1972 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1974 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1975 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1978 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1980 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1981 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1982 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1985 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1987 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1988 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1991 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1993 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1994 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1998 fUseProxy = m_checkBoxUseProxy->GetValue();
1999 walletdb.WriteSetting("fUseProxy", fUseProxy);
2001 addrProxy = GetProxyAddr();
2002 walletdb.WriteSetting("addrProxy", addrProxy);
2010 //////////////////////////////////////////////////////////////////////////////
2015 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2017 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2019 // Change (c) into UTF-8 or ANSI copyright symbol
2020 wxString str = m_staticTextMain->GetLabel();
2022 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2024 str.Replace("(c)", "\xA9");
2026 m_staticTextMain->SetLabel(str);
2028 // Resize on Linux to make the window fit the text.
2029 // The text was wrapped manually rather than using the Wrap setting because
2030 // the wrap would be too small on Linux and it can't be changed at this point.
2031 wxFont fontTmp = m_staticTextMain->GetFont();
2032 if (fontTmp.GetPointSize() > 8);
2033 fontTmp.SetPointSize(8);
2034 m_staticTextMain->SetFont(fontTmp);
2035 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2037 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2041 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2051 //////////////////////////////////////////////////////////////////////////////
2056 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2059 m_textCtrlAddress->SetValue(strAddress);
2060 m_choiceTransferType->SetSelection(0);
2061 m_bitmapCheckMark->Show(false);
2062 fEnabledPrev = true;
2063 m_textCtrlAddress->SetFocus();
2065 //// todo: should add a display of your balance for convenience
2067 wxFont fontTmp = m_staticTextInstructions->GetFont();
2068 if (fontTmp.GetPointSize() > 9);
2069 fontTmp.SetPointSize(9);
2070 m_staticTextInstructions->SetFont(fontTmp);
2073 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2077 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2080 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2085 SetIcon(wxICON(bitcoin));
2088 // Fixup the tab order
2089 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2090 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2094 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2096 // Reformat the amount
2098 if (m_textCtrlAmount->GetValue().Trim().empty())
2101 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2102 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2105 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2107 // Open address book
2108 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2109 if (dialog.ShowModal())
2110 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2113 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2115 // Copy clipboard to address box
2116 if (wxTheClipboard->Open())
2118 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2120 wxTextDataObject data;
2121 wxTheClipboard->GetData(data);
2122 m_textCtrlAddress->SetValue(data.GetText());
2124 wxTheClipboard->Close();
2128 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2130 static CCriticalSection cs_sendlock;
2131 TRY_CRITICAL_BLOCK(cs_sendlock)
2134 string strAddress = (string)m_textCtrlAddress->GetValue();
2138 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2140 wxMessageBox(_("Error in amount "), _("Send Coins"));
2143 if (nValue > pwalletMain->GetBalance())
2145 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2148 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2150 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2154 // Parse bitcoin address
2155 CBitcoinAddress address(strAddress);
2156 bool fBitcoinAddress = address.IsValid();
2158 if (fBitcoinAddress)
2160 bool fWasLocked = pwalletMain->IsLocked();
2161 if (!GetWalletPassphrase())
2165 CRITICAL_BLOCK(cs_main)
2166 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2168 // Send to bitcoin address
2169 CScript scriptPubKey;
2170 scriptPubKey.SetBitcoinAddress(address);
2172 strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2176 pframeMain->RefreshListCtrl();
2177 wxMessageBox(_("Payment sent "), _("Sending..."));
2179 else if (strError == "ABORTED")
2182 pwalletMain->Lock();
2183 return; // leave send dialog open
2187 wxMessageBox(strError + " ", _("Sending..."));
2190 pwalletMain->Lock();
2195 pwalletMain->Lock();
2200 CAddress addr(strAddress);
2201 if (!addr.IsValid())
2203 wxMessageBox(_("Invalid address "), _("Send Coins"));
2208 wtx.mapValue["to"] = strAddress;
2210 // Send to IP address
2211 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2212 if (!pdialog->ShowModal())
2216 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2217 if (!pwalletMain->mapAddressBook.count(address))
2218 pwalletMain->SetAddressBookName(strAddress, "");
2224 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2235 //////////////////////////////////////////////////////////////////////////////
2240 CSendingDialog::CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 nPriceIn, const CWalletTx& wtxIn) : CSendingDialogBase(NULL) // we have to give null so parent can't destroy us
2245 start = wxDateTime::UNow();
2246 memset(pszStatus, 0, sizeof(pszStatus));
2253 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2255 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2258 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2259 m_textCtrlStatus->SetValue("");
2261 CreateThread(SendingDialogStartTransfer, this);
2264 CSendingDialog::~CSendingDialog()
2266 printf("~CSendingDialog()\n");
2269 void CSendingDialog::Close()
2271 // Last one out turn out the lights.
2272 // fWorkDone signals that work side is done and UI thread should call destroy.
2273 // fUIDone signals that UI window has closed and work thread should call destroy.
2274 // This allows the window to disappear and end modality when cancelled
2275 // without making the user wait for ConnectNode to return. The dialog object
2276 // hangs around in the background until the work thread exits.
2287 void CSendingDialog::OnClose(wxCloseEvent& event)
2289 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2296 wxCommandEvent cmdevent;
2297 OnButtonCancel(cmdevent);
2301 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2307 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2313 void CSendingDialog::OnPaint(wxPaintEvent& event)
2316 if (strlen(pszStatus) > 130)
2317 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2319 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2320 m_staticTextSending->SetFocus();
2322 m_buttonCancel->Enable(false);
2325 m_buttonOK->Enable(true);
2326 m_buttonOK->SetFocus();
2327 m_buttonCancel->Enable(false);
2329 if (fAbort && fCanCancel && IsShown())
2331 strcpy(pszStatus, _("CANCELLED"));
2332 m_buttonOK->Enable(true);
2333 m_buttonOK->SetFocus();
2334 m_buttonCancel->Enable(false);
2335 m_buttonCancel->SetLabel(_("Cancelled"));
2337 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2343 // Everything from here on is not in the UI thread and must only communicate
2344 // with the rest of the dialog through variables and calling repaint.
2347 void CSendingDialog::Repaint()
2351 GetEventHandler()->AddPendingEvent(event);
2354 bool CSendingDialog::Status()
2361 if (fAbort && fCanCancel)
2363 memset(pszStatus, 0, 10);
2364 strcpy(pszStatus, _("CANCELLED"));
2372 bool CSendingDialog::Status(const string& str)
2377 // This can be read by the UI thread at any time,
2378 // so copy in a way that can be read cleanly at all times.
2379 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2380 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2386 bool CSendingDialog::Error(const string& str)
2390 Status(string(_("Error: ")) + str);
2394 void SendingDialogStartTransfer(void* parg)
2396 ((CSendingDialog*)parg)->StartTransfer();
2399 void CSendingDialog::StartTransfer()
2401 // Make sure we have enough money
2402 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2404 Error(_("Insufficient funds"));
2408 // We may have connected already for product details
2409 if (!Status(_("Connecting...")))
2411 CNode* pnode = ConnectNode(addr, 15 * 60);
2414 Error(_("Unable to connect"));
2418 // Send order to seller, with response going to OnReply2 via event handler
2419 if (!Status(_("Requesting public key...")))
2421 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2424 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2426 ((CSendingDialog*)parg)->OnReply2(vRecv);
2429 void CSendingDialog::OnReply2(CDataStream& vRecv)
2431 if (!Status(_("Received public key...")))
2434 CScript scriptPubKey;
2443 vRecv >> strMessage;
2445 Error(_("Recipient is not accepting transactions sent by IP address"));
2447 Error(_("Transfer was not accepted"));
2448 //// todo: enlarge the window and enable a hidden white box to put seller's message
2451 vRecv >> scriptPubKey;
2455 //// what do we want to do about this?
2456 Error(_("Invalid response received"));
2460 // Pause to give the user a chance to cancel
2461 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2469 if (!Status(_("Creating transaction...")))
2471 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2473 Error(_("Insufficient funds"));
2477 CReserveKey reservekey(pwalletMain);
2479 bool fWasLocked = pwalletMain->IsLocked();
2480 if (!GetWalletPassphrase())
2483 bool fTxCreated = false;
2484 CRITICAL_BLOCK(cs_main)
2485 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2487 fTxCreated = pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired);
2491 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2492 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()));
2494 Error(_("Transaction creation failed"));
2499 pwalletMain->Lock();
2502 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2504 Error(_("Transaction aborted"));
2508 // Make sure we're still connected
2509 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2512 Error(_("Lost connection, transaction cancelled"));
2516 // Last chance to cancel
2528 if (!Status(_("Sending payment...")))
2532 bool fTxCommitted = false;
2533 CRITICAL_BLOCK(cs_main)
2534 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2536 fTxCommitted = pwalletMain->CommitTransaction(wtx, reservekey);
2540 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."));
2544 // Send payment tx to seller, with response going to OnReply3 via event handler
2545 CWalletTx wtxSend = wtx;
2546 wtxSend.fFromMe = false;
2547 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2549 Status(_("Waiting for confirmation..."));
2553 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2555 ((CSendingDialog*)parg)->OnReply3(vRecv);
2558 void CSendingDialog::OnReply3(CDataStream& vRecv)
2566 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2567 "The transaction is recorded and will credit to the recipient,\n"
2568 "but the comment information will be blank."));
2574 //// what do we want to do about this?
2575 Error(_("Payment was sent, but an invalid response was received"));
2581 Status(_("Payment completed"));
2589 //////////////////////////////////////////////////////////////////////////////
2591 // CAddressBookDialog
2594 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2597 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2600 // Set initially selected page
2601 wxNotebookEvent event;
2602 event.SetSelection(nPageIn);
2603 OnNotebookPageChanged(event);
2604 m_notebook->ChangeSelection(nPageIn);
2606 fDuringSend = fDuringSendIn;
2608 m_buttonCancel->Show(false);
2611 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2613 wxIcon iconAddressBook;
2614 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2615 SetIcon(iconAddressBook);
2619 SetIcon(wxICON(bitcoin));
2622 // Init column headers
2623 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2624 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2625 m_listCtrlSending->SetFocus();
2626 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2627 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2628 m_listCtrlReceiving->SetFocus();
2630 // Fill listctrl with address book data
2631 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2633 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2634 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2636 const CBitcoinAddress& address = item.first;
2637 string strName = item.second;
2638 bool fMine = pwalletMain->HaveKey(address);
2639 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2640 int nIndex = InsertLine(plistCtrl, strName, address.ToString());
2641 if (address.ToString() == (fMine ? strDefaultReceiving : string(strInitSelected)))
2642 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2647 wxString CAddressBookDialog::GetSelectedAddress()
2649 int nIndex = GetSelection(m_listCtrl);
2652 return GetItemText(m_listCtrl, nIndex, 1);
2655 wxString CAddressBookDialog::GetSelectedSendingAddress()
2657 int nIndex = GetSelection(m_listCtrlSending);
2660 return GetItemText(m_listCtrlSending, nIndex, 1);
2663 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2665 int nIndex = GetSelection(m_listCtrlReceiving);
2668 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2671 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2674 nPage = event.GetSelection();
2675 if (nPage == SENDING)
2676 m_listCtrl = m_listCtrlSending;
2677 else if (nPage == RECEIVING)
2678 m_listCtrl = m_listCtrlReceiving;
2679 m_buttonDelete->Show(nPage == SENDING);
2680 m_buttonCopy->Show(nPage == RECEIVING);
2682 m_listCtrl->SetFocus();
2685 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2687 // Update address book with edited name
2689 if (event.IsEditCancelled())
2691 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2692 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2693 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2694 pframeMain->RefreshListCtrl();
2697 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2700 if (nPage == RECEIVING)
2701 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2704 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2709 // Doubleclick returns selection
2710 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2714 // Doubleclick edits item
2715 wxCommandEvent event2;
2716 OnButtonEdit(event2);
2719 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2721 if (nPage != SENDING)
2723 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2725 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2727 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2728 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2729 pwalletMain->DelAddressBookName(strAddress);
2730 m_listCtrl->DeleteItem(nIndex);
2733 pframeMain->RefreshListCtrl();
2736 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2738 // Copy address box to clipboard
2739 if (wxTheClipboard->Open())
2741 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2742 wxTheClipboard->Close();
2746 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2748 CBitcoinAddress address(strAddress);
2749 bool fMine = address.IsValid() && pwalletMain->HaveKey(address);
2751 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2755 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2757 int nIndex = GetSelection(m_listCtrl);
2760 string strName = (string)m_listCtrl->GetItemText(nIndex);
2761 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2762 string strAddressOrg = strAddress;
2764 if (nPage == SENDING)
2766 // Ask name and address
2769 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2770 if (!dialog.ShowModal())
2772 strName = dialog.GetValue1();
2773 strAddress = dialog.GetValue2();
2775 while (CheckIfMine(strAddress, _("Edit Address")));
2778 else if (nPage == RECEIVING)
2781 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2782 if (!dialog.ShowModal())
2784 strName = dialog.GetValue();
2788 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2790 if (strAddress != strAddressOrg)
2791 pwalletMain->DelAddressBookName(strAddressOrg);
2792 pwalletMain->SetAddressBookName(strAddress, strName);
2794 m_listCtrl->SetItem(nIndex, 1, strAddress);
2795 m_listCtrl->SetItemText(nIndex, strName);
2796 pframeMain->RefreshListCtrl();
2799 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2804 if (nPage == SENDING)
2806 // Ask name and address
2809 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2810 if (!dialog.ShowModal())
2812 strName = dialog.GetValue1();
2813 strAddress = dialog.GetValue2();
2815 while (CheckIfMine(strAddress, _("Add Address")));
2817 else if (nPage == RECEIVING)
2820 CGetTextFromUserDialog dialog(this,
2821 _("New Receiving Address"),
2822 _("You should use a new address for each payment you receive.\n\nLabel"),
2824 if (!dialog.ShowModal())
2826 strName = dialog.GetValue();
2828 bool fWasLocked = pwalletMain->IsLocked();
2829 if (!GetWalletPassphrase())
2833 std::vector<unsigned char> newKey;
2834 pwalletMain->GetKeyFromPool(newKey, true);
2835 strAddress = CBitcoinAddress(newKey).ToString();
2838 pwalletMain->Lock();
2841 // Add to list and select it
2842 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2843 pwalletMain->SetAddressBookName(strAddress, strName);
2844 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2845 SetSelection(m_listCtrl, nIndex);
2846 m_listCtrl->SetFocus();
2847 if (nPage == SENDING)
2848 pframeMain->RefreshListCtrl();
2851 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2854 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2857 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2863 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2874 //////////////////////////////////////////////////////////////////////////////
2881 ID_TASKBAR_RESTORE = 10001,
2884 ID_TASKBAR_GENERATE,
2888 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2889 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2890 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2891 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2892 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2893 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2894 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2897 void CMyTaskBarIcon::Show(bool fShow)
2899 static char pszPrevTip[200];
2902 string strTooltip = _("Bitcoin");
2903 if (fGenerateBitcoins)
2904 strTooltip = _("Bitcoin - Generating");
2905 if (fGenerateBitcoins && vNodes.empty())
2906 strTooltip = _("Bitcoin - (not connected)");
2908 // Optimization, only update when changed, using char array to be reentrant
2909 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2911 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2913 // somehow it'll choose the wrong size and scale it down if
2914 // we use the main icon, so we hand it one with only 16x16
2915 SetIcon(wxICON(favicon), strTooltip);
2917 SetIcon(bitcoin80_xpm, strTooltip);
2923 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2928 void CMyTaskBarIcon::Hide()
2933 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2938 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2943 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2946 CSendDialog dialog(pframeMain);
2950 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2952 // Since it's modal, get the main window to do it
2953 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2954 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2957 void CMyTaskBarIcon::Restore()
2960 wxIconizeEvent event(0, false);
2961 pframeMain->GetEventHandler()->AddPendingEvent(event);
2962 pframeMain->Iconize(false);
2963 pframeMain->Raise();
2966 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2968 event.Check(fGenerateBitcoins);
2971 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2973 pframeMain->Close(true);
2976 void CMyTaskBarIcon::UpdateTooltip()
2978 if (IsIconInstalled())
2982 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2984 wxMenu* pmenu = new wxMenu;
2985 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2986 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2987 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2988 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2989 pmenu->AppendSeparator();
2990 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
3000 //////////////////////////////////////////////////////////////////////////////
3005 void CreateMainWindow()
3007 pframeMain = new CMainFrame(NULL);
3008 if (GetBoolArg("-min"))
3009 pframeMain->Iconize(true);
3010 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3011 if (!GetBoolArg("-minimizetotray"))
3012 fMinimizeToTray = false;
3014 pframeMain->Show(true); // have to show first to get taskbar button to hide
3015 if (fMinimizeToTray && pframeMain->IsIconized())
3016 fClosedToTray = true;
3017 pframeMain->Show(!fClosedToTray);
3018 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3019 CreateThread(ThreadDelayedRepaint, NULL);
3023 // Define a new application
3024 class CMyApp : public wxApp
3033 // Hook Initialize so we can start without GUI
3034 virtual bool Initialize(int& argc, wxChar** argv);
3036 // 2nd-level exception handling: we get all the exceptions occurring in any
3037 // event handler here
3038 virtual bool OnExceptionInMainLoop();
3040 // 3rd, and final, level exception handling: whenever an unhandled
3041 // exception is caught, this function is called
3042 virtual void OnUnhandledException();
3044 // and now for something different: this function is called in case of a
3045 // crash (e.g. dereferencing null pointer, division by 0, ...)
3046 virtual void OnFatalException();
3049 IMPLEMENT_APP(CMyApp)
3051 bool CMyApp::Initialize(int& argc, wxChar** argv)
3053 for (int i = 1; i < argc; i++)
3054 if (!IsSwitchChar(argv[i][0]))
3055 fCommandLine = true;
3059 // wxApp::Initialize will remove environment-specific parameters,
3060 // so it's too early to call ParseParameters yet
3061 for (int i = 1; i < argc; i++)
3063 wxString str = argv[i];
3065 if (str.size() >= 1 && str[0] == '/')
3067 char pszLower[MAX_PATH];
3068 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3072 if (str == "-daemon")
3078 if (fDaemon || fCommandLine)
3080 // Call the original Initialize while suppressing error messages
3081 // and ignoring failure. If unable to initialize GTK, it fails
3082 // near the end so hopefully the last few things don't matter.
3085 wxApp::Initialize(argc, argv);
3094 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3098 pthread_exit((void*)0);
3100 pid_t sid = setsid();
3102 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3109 return wxApp::Initialize(argc, argv);
3112 bool CMyApp::OnInit()
3114 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3115 // Disable malfunctioning wxWidgets debug assertion
3116 extern int g_isPainting;
3117 g_isPainting = 10000;
3119 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3120 SetAppName("Bitcoin");
3122 SetAppName("bitcoin");
3126 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3127 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3128 class wxMBConv_win32 : public wxMBConv
3132 size_t m_minMBCharWidth;
3134 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3135 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3139 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3140 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3141 g_locale.AddCatalogLookupPathPrefix("locale");
3143 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3144 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3146 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3147 g_locale.AddCatalog("bitcoin");
3150 HDC hdc = GetDC(NULL);
3153 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3154 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3155 ReleaseDC(NULL, hdc);
3159 return AppInit(argc, argv);
3162 int CMyApp::OnExit()
3165 return wxApp::OnExit();
3168 bool CMyApp::OnExceptionInMainLoop()
3174 catch (std::exception& e)
3176 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3177 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3183 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3184 wxLogWarning("Unknown exception");
3191 void CMyApp::OnUnhandledException()
3193 // this shows how we may let some exception propagate uncaught
3198 catch (std::exception& e)
3200 PrintException(&e, "CMyApp::OnUnhandledException()");
3201 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3207 PrintException(NULL, "CMyApp::OnUnhandledException()");
3208 wxLogWarning("Unknown exception");
3214 void CMyApp::OnFatalException()
3216 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);