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.\nBitcoin will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1230 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1232 // Options->Change Wallet Encryption Passphrase
1233 if (!pwalletMain->IsCrypted())
1235 wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1239 string strOldWalletPass;
1240 strOldWalletPass.reserve(100);
1241 mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1243 // obtain current wallet encrypt/decrypt key, from passphrase
1244 // Note that the passphrase is not mlock()d during this entry and could potentially
1245 // be obtained from disk long after bitcoin has run.
1246 strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1247 _("Passphrase")).ToStdString();
1249 bool fWasLocked = pwalletMain->IsLocked();
1250 pwalletMain->Lock();
1252 if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1254 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1255 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1256 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1261 pwalletMain->Lock();
1263 string strNewWalletPass;
1264 strNewWalletPass.reserve(100);
1265 mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1267 // obtain new wallet encrypt/decrypt key, from passphrase
1268 // Note that the passphrase is not mlock()d during this entry and could potentially
1269 // be obtained from disk long after bitcoin has run.
1270 strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1271 _("Passphrase")).ToStdString();
1273 if (!strNewWalletPass.size())
1275 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1276 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1277 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1278 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1279 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1283 string strNewWalletPassTest;
1284 strNewWalletPassTest.reserve(100);
1285 mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1287 // obtain new wallet encrypt/decrypt key, from passphrase
1288 // Note that the passphrase is not mlock()d during this entry and could potentially
1289 // be obtained from disk long after bitcoin has run.
1290 strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1291 _("Passphrase")).ToStdString();
1293 if (strNewWalletPassTest != strNewWalletPass)
1295 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1296 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1297 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1298 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1299 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1300 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1301 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1305 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1307 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1308 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1309 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1310 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1311 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1312 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1313 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1316 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1317 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1318 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1319 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1320 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1321 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1322 wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
1325 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1328 COptionsDialog dialog(this);
1332 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1335 CAboutDialog dialog(this);
1339 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1342 CSendDialog dialog(this);
1346 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1348 // Toolbar: Address Book
1349 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1350 if (dialogAddr.ShowModal() == 2)
1353 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1354 dialogSend.ShowModal();
1358 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1360 // Automatically select-all when entering window
1362 m_textCtrlAddress->SetSelection(-1, -1);
1363 fOnSetFocusAddress = true;
1366 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1369 if (fOnSetFocusAddress)
1370 m_textCtrlAddress->SetSelection(-1, -1);
1371 fOnSetFocusAddress = false;
1374 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1377 CGetTextFromUserDialog dialog(this,
1378 _("New Receiving Address"),
1379 _("You should use a new address for each payment you receive.\n\nLabel"),
1381 if (!dialog.ShowModal())
1383 string strName = dialog.GetValue();
1387 bool fWasLocked = pwalletMain->IsLocked();
1388 if (!GetWalletPassphrase())
1392 std::vector<unsigned char> newKey;
1393 pwalletMain->GetKeyFromPool(newKey, true);
1394 strAddress = CBitcoinAddress(newKey).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 #if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
1808 unlink(GetAutostartFilePath().string().c_str());
1810 unlink(GetAutostartFilePath().native_file_string().c_str());
1815 char pszExePath[MAX_PATH+1];
1816 memset(pszExePath, 0, sizeof(pszExePath));
1817 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1820 boost::filesystem::create_directories(GetAutostartDir());
1822 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1823 if (!optionFile.good())
1825 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1828 // Write a bitcoin.desktop file to the autostart directory:
1829 optionFile << "[Desktop Entry]\n";
1830 optionFile << "Type=Application\n";
1831 optionFile << "Name=Bitcoin\n";
1832 optionFile << "Exec=" << pszExePath << "\n";
1833 optionFile << "Terminal=false\n";
1834 optionFile << "Hidden=false\n";
1840 // TODO: OSX startup stuff; see:
1841 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1843 bool GetStartOnSystemStartup() { return false; }
1844 void SetStartOnSystemStartup(bool fAutoStart) { }
1853 //////////////////////////////////////////////////////////////////////////////
1858 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1860 // Set up list box of page choices
1861 m_listBox->Append(_("Main"));
1862 //m_listBox->Append(_("Test 2"));
1863 m_listBox->SetSelection(0);
1866 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1868 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1870 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1871 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1872 if (!GetBoolArg("-minimizetotray"))
1874 // Minimize to tray is just too buggy on Linux
1875 fMinimizeToTray = false;
1876 m_checkBoxMinimizeToTray->SetValue(false);
1877 m_checkBoxMinimizeToTray->Enable(false);
1878 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1881 #ifdef __WXMAC_OSX__
1882 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1886 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1887 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1888 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1889 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1891 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1893 m_checkBoxUseUPnP->Enable(false);
1894 m_checkBoxUseProxy->SetValue(fUseProxy);
1895 m_textCtrlProxyIP->Enable(fUseProxy);
1896 m_textCtrlProxyPort->Enable(fUseProxy);
1897 m_staticTextProxyIP->Enable(fUseProxy);
1898 m_staticTextProxyPort->Enable(fUseProxy);
1899 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1900 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1902 m_buttonOK->SetFocus();
1905 void COptionsDialog::SelectPage(int nPage)
1907 m_panelMain->Show(nPage == 0);
1908 m_panelTest2->Show(nPage == 1);
1910 m_scrolledWindow->Layout();
1911 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1914 void COptionsDialog::OnListBox(wxCommandEvent& event)
1916 SelectPage(event.GetSelection());
1919 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1922 int64 nTmp = nTransactionFee;
1923 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1924 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1927 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1929 m_textCtrlProxyIP->Enable(event.IsChecked());
1930 m_textCtrlProxyPort->Enable(event.IsChecked());
1931 m_staticTextProxyIP->Enable(event.IsChecked());
1932 m_staticTextProxyPort->Enable(event.IsChecked());
1935 CAddress COptionsDialog::GetProxyAddr()
1937 // Be careful about byte order, addr.ip and addr.port are big endian
1938 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1939 if (addr.ip == INADDR_NONE)
1940 addr.ip = addrProxy.ip;
1941 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1942 addr.port = htons(nPort);
1943 if (nPort <= 0 || nPort > USHRT_MAX)
1944 addr.port = addrProxy.port;
1948 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1951 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1952 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1956 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1958 OnButtonApply(event);
1962 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1967 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1969 CWalletDB walletdb(pwalletMain->strWalletFile);
1971 int64 nPrevTransactionFee = nTransactionFee;
1972 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1973 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1975 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1977 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1978 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1981 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1983 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1984 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1985 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1988 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1990 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1991 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1994 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1996 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1997 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
2001 fUseProxy = m_checkBoxUseProxy->GetValue();
2002 walletdb.WriteSetting("fUseProxy", fUseProxy);
2004 addrProxy = GetProxyAddr();
2005 walletdb.WriteSetting("addrProxy", addrProxy);
2013 //////////////////////////////////////////////////////////////////////////////
2018 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2020 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2022 // Change (c) into UTF-8 or ANSI copyright symbol
2023 wxString str = m_staticTextMain->GetLabel();
2025 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2027 str.Replace("(c)", "\xA9");
2029 m_staticTextMain->SetLabel(str);
2031 // Resize on Linux to make the window fit the text.
2032 // The text was wrapped manually rather than using the Wrap setting because
2033 // the wrap would be too small on Linux and it can't be changed at this point.
2034 wxFont fontTmp = m_staticTextMain->GetFont();
2035 if (fontTmp.GetPointSize() > 8);
2036 fontTmp.SetPointSize(8);
2037 m_staticTextMain->SetFont(fontTmp);
2038 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2040 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2044 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2054 //////////////////////////////////////////////////////////////////////////////
2059 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2062 m_textCtrlAddress->SetValue(strAddress);
2063 m_choiceTransferType->SetSelection(0);
2064 m_bitmapCheckMark->Show(false);
2065 fEnabledPrev = true;
2066 m_textCtrlAddress->SetFocus();
2068 //// todo: should add a display of your balance for convenience
2070 wxFont fontTmp = m_staticTextInstructions->GetFont();
2071 if (fontTmp.GetPointSize() > 9);
2072 fontTmp.SetPointSize(9);
2073 m_staticTextInstructions->SetFont(fontTmp);
2076 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2080 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2083 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2088 SetIcon(wxICON(bitcoin));
2091 // Fixup the tab order
2092 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2093 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2097 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2099 // Reformat the amount
2101 if (m_textCtrlAmount->GetValue().Trim().empty())
2104 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2105 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2108 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2110 // Open address book
2111 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2112 if (dialog.ShowModal())
2113 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2116 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2118 // Copy clipboard to address box
2119 if (wxTheClipboard->Open())
2121 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2123 wxTextDataObject data;
2124 wxTheClipboard->GetData(data);
2125 m_textCtrlAddress->SetValue(data.GetText());
2127 wxTheClipboard->Close();
2131 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2133 static CCriticalSection cs_sendlock;
2134 TRY_CRITICAL_BLOCK(cs_sendlock)
2137 string strAddress = (string)m_textCtrlAddress->GetValue();
2141 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2143 wxMessageBox(_("Error in amount "), _("Send Coins"));
2146 if (nValue > pwalletMain->GetBalance())
2148 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2151 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2153 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2157 // Parse bitcoin address
2158 CBitcoinAddress address(strAddress);
2159 bool fBitcoinAddress = address.IsValid();
2161 if (fBitcoinAddress)
2163 bool fWasLocked = pwalletMain->IsLocked();
2164 if (!GetWalletPassphrase())
2168 CRITICAL_BLOCK(cs_main)
2169 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2171 // Send to bitcoin address
2172 CScript scriptPubKey;
2173 scriptPubKey.SetBitcoinAddress(address);
2175 strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2179 pframeMain->RefreshListCtrl();
2180 wxMessageBox(_("Payment sent "), _("Sending..."));
2182 else if (strError == "ABORTED")
2185 pwalletMain->Lock();
2186 return; // leave send dialog open
2190 wxMessageBox(strError + " ", _("Sending..."));
2193 pwalletMain->Lock();
2198 pwalletMain->Lock();
2203 CAddress addr(strAddress);
2204 if (!addr.IsValid())
2206 wxMessageBox(_("Invalid address "), _("Send Coins"));
2211 wtx.mapValue["to"] = strAddress;
2213 // Send to IP address
2214 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2215 if (!pdialog->ShowModal())
2219 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2220 if (!pwalletMain->mapAddressBook.count(address))
2221 pwalletMain->SetAddressBookName(strAddress, "");
2227 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2238 //////////////////////////////////////////////////////////////////////////////
2243 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
2248 start = wxDateTime::UNow();
2249 memset(pszStatus, 0, sizeof(pszStatus));
2256 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2258 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2261 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2262 m_textCtrlStatus->SetValue("");
2264 CreateThread(SendingDialogStartTransfer, this);
2267 CSendingDialog::~CSendingDialog()
2269 printf("~CSendingDialog()\n");
2272 void CSendingDialog::Close()
2274 // Last one out turn out the lights.
2275 // fWorkDone signals that work side is done and UI thread should call destroy.
2276 // fUIDone signals that UI window has closed and work thread should call destroy.
2277 // This allows the window to disappear and end modality when cancelled
2278 // without making the user wait for ConnectNode to return. The dialog object
2279 // hangs around in the background until the work thread exits.
2290 void CSendingDialog::OnClose(wxCloseEvent& event)
2292 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2299 wxCommandEvent cmdevent;
2300 OnButtonCancel(cmdevent);
2304 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2310 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2316 void CSendingDialog::OnPaint(wxPaintEvent& event)
2319 if (strlen(pszStatus) > 130)
2320 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2322 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2323 m_staticTextSending->SetFocus();
2325 m_buttonCancel->Enable(false);
2328 m_buttonOK->Enable(true);
2329 m_buttonOK->SetFocus();
2330 m_buttonCancel->Enable(false);
2332 if (fAbort && fCanCancel && IsShown())
2334 strcpy(pszStatus, _("CANCELLED"));
2335 m_buttonOK->Enable(true);
2336 m_buttonOK->SetFocus();
2337 m_buttonCancel->Enable(false);
2338 m_buttonCancel->SetLabel(_("Cancelled"));
2340 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2346 // Everything from here on is not in the UI thread and must only communicate
2347 // with the rest of the dialog through variables and calling repaint.
2350 void CSendingDialog::Repaint()
2354 GetEventHandler()->AddPendingEvent(event);
2357 bool CSendingDialog::Status()
2364 if (fAbort && fCanCancel)
2366 memset(pszStatus, 0, 10);
2367 strcpy(pszStatus, _("CANCELLED"));
2375 bool CSendingDialog::Status(const string& str)
2380 // This can be read by the UI thread at any time,
2381 // so copy in a way that can be read cleanly at all times.
2382 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2383 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2389 bool CSendingDialog::Error(const string& str)
2393 Status(string(_("Error: ")) + str);
2397 void SendingDialogStartTransfer(void* parg)
2399 ((CSendingDialog*)parg)->StartTransfer();
2402 void CSendingDialog::StartTransfer()
2404 // Make sure we have enough money
2405 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2407 Error(_("Insufficient funds"));
2411 // We may have connected already for product details
2412 if (!Status(_("Connecting...")))
2414 CNode* pnode = ConnectNode(addr, 15 * 60);
2417 Error(_("Unable to connect"));
2421 // Send order to seller, with response going to OnReply2 via event handler
2422 if (!Status(_("Requesting public key...")))
2424 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2427 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2429 ((CSendingDialog*)parg)->OnReply2(vRecv);
2432 void CSendingDialog::OnReply2(CDataStream& vRecv)
2434 if (!Status(_("Received public key...")))
2437 CScript scriptPubKey;
2446 vRecv >> strMessage;
2448 Error(_("Recipient is not accepting transactions sent by IP address"));
2450 Error(_("Transfer was not accepted"));
2451 //// todo: enlarge the window and enable a hidden white box to put seller's message
2454 vRecv >> scriptPubKey;
2458 //// what do we want to do about this?
2459 Error(_("Invalid response received"));
2463 // Pause to give the user a chance to cancel
2464 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2472 if (!Status(_("Creating transaction...")))
2474 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2476 Error(_("Insufficient funds"));
2480 CReserveKey reservekey(pwalletMain);
2482 bool fWasLocked = pwalletMain->IsLocked();
2483 if (!GetWalletPassphrase())
2486 bool fTxCreated = false;
2487 CRITICAL_BLOCK(cs_main)
2488 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2490 fTxCreated = pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired);
2494 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2495 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()));
2497 Error(_("Transaction creation failed"));
2502 pwalletMain->Lock();
2505 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2507 Error(_("Transaction aborted"));
2511 // Make sure we're still connected
2512 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2515 Error(_("Lost connection, transaction cancelled"));
2519 // Last chance to cancel
2531 if (!Status(_("Sending payment...")))
2535 bool fTxCommitted = false;
2536 CRITICAL_BLOCK(cs_main)
2537 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2539 fTxCommitted = pwalletMain->CommitTransaction(wtx, reservekey);
2543 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."));
2547 // Send payment tx to seller, with response going to OnReply3 via event handler
2548 CWalletTx wtxSend = wtx;
2549 wtxSend.fFromMe = false;
2550 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2552 Status(_("Waiting for confirmation..."));
2556 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2558 ((CSendingDialog*)parg)->OnReply3(vRecv);
2561 void CSendingDialog::OnReply3(CDataStream& vRecv)
2569 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2570 "The transaction is recorded and will credit to the recipient,\n"
2571 "but the comment information will be blank."));
2577 //// what do we want to do about this?
2578 Error(_("Payment was sent, but an invalid response was received"));
2584 Status(_("Payment completed"));
2592 //////////////////////////////////////////////////////////////////////////////
2594 // CAddressBookDialog
2597 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2600 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2603 // Set initially selected page
2604 wxNotebookEvent event;
2605 event.SetSelection(nPageIn);
2606 OnNotebookPageChanged(event);
2607 m_notebook->ChangeSelection(nPageIn);
2609 fDuringSend = fDuringSendIn;
2611 m_buttonCancel->Show(false);
2614 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2616 wxIcon iconAddressBook;
2617 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2618 SetIcon(iconAddressBook);
2622 SetIcon(wxICON(bitcoin));
2625 // Init column headers
2626 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2627 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2628 m_listCtrlSending->SetFocus();
2629 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2630 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2631 m_listCtrlReceiving->SetFocus();
2633 // Fill listctrl with address book data
2634 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2636 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2637 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2639 const CBitcoinAddress& address = item.first;
2640 string strName = item.second;
2641 bool fMine = pwalletMain->HaveKey(address);
2642 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2643 int nIndex = InsertLine(plistCtrl, strName, address.ToString());
2644 if (address.ToString() == (fMine ? strDefaultReceiving : string(strInitSelected)))
2645 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2650 wxString CAddressBookDialog::GetSelectedAddress()
2652 int nIndex = GetSelection(m_listCtrl);
2655 return GetItemText(m_listCtrl, nIndex, 1);
2658 wxString CAddressBookDialog::GetSelectedSendingAddress()
2660 int nIndex = GetSelection(m_listCtrlSending);
2663 return GetItemText(m_listCtrlSending, nIndex, 1);
2666 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2668 int nIndex = GetSelection(m_listCtrlReceiving);
2671 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2674 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2677 nPage = event.GetSelection();
2678 if (nPage == SENDING)
2679 m_listCtrl = m_listCtrlSending;
2680 else if (nPage == RECEIVING)
2681 m_listCtrl = m_listCtrlReceiving;
2682 m_buttonDelete->Show(nPage == SENDING);
2683 m_buttonCopy->Show(nPage == RECEIVING);
2685 m_listCtrl->SetFocus();
2688 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2690 // Update address book with edited name
2692 if (event.IsEditCancelled())
2694 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2695 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2696 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2697 pframeMain->RefreshListCtrl();
2700 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2703 if (nPage == RECEIVING)
2704 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2707 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2712 // Doubleclick returns selection
2713 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2717 // Doubleclick edits item
2718 wxCommandEvent event2;
2719 OnButtonEdit(event2);
2722 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2724 if (nPage != SENDING)
2726 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2728 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2730 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2731 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2732 pwalletMain->DelAddressBookName(strAddress);
2733 m_listCtrl->DeleteItem(nIndex);
2736 pframeMain->RefreshListCtrl();
2739 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2741 // Copy address box to clipboard
2742 if (wxTheClipboard->Open())
2744 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2745 wxTheClipboard->Close();
2749 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2751 CBitcoinAddress address(strAddress);
2752 bool fMine = address.IsValid() && pwalletMain->HaveKey(address);
2754 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2758 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2760 int nIndex = GetSelection(m_listCtrl);
2763 string strName = (string)m_listCtrl->GetItemText(nIndex);
2764 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2765 string strAddressOrg = strAddress;
2767 if (nPage == SENDING)
2769 // Ask name and address
2772 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2773 if (!dialog.ShowModal())
2775 strName = dialog.GetValue1();
2776 strAddress = dialog.GetValue2();
2778 while (CheckIfMine(strAddress, _("Edit Address")));
2781 else if (nPage == RECEIVING)
2784 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2785 if (!dialog.ShowModal())
2787 strName = dialog.GetValue();
2791 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2793 if (strAddress != strAddressOrg)
2794 pwalletMain->DelAddressBookName(strAddressOrg);
2795 pwalletMain->SetAddressBookName(strAddress, strName);
2797 m_listCtrl->SetItem(nIndex, 1, strAddress);
2798 m_listCtrl->SetItemText(nIndex, strName);
2799 pframeMain->RefreshListCtrl();
2802 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2807 if (nPage == SENDING)
2809 // Ask name and address
2812 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2813 if (!dialog.ShowModal())
2815 strName = dialog.GetValue1();
2816 strAddress = dialog.GetValue2();
2818 while (CheckIfMine(strAddress, _("Add Address")));
2820 else if (nPage == RECEIVING)
2823 CGetTextFromUserDialog dialog(this,
2824 _("New Receiving Address"),
2825 _("You should use a new address for each payment you receive.\n\nLabel"),
2827 if (!dialog.ShowModal())
2829 strName = dialog.GetValue();
2831 bool fWasLocked = pwalletMain->IsLocked();
2832 if (!GetWalletPassphrase())
2836 std::vector<unsigned char> newKey;
2837 pwalletMain->GetKeyFromPool(newKey, true);
2838 strAddress = CBitcoinAddress(newKey).ToString();
2841 pwalletMain->Lock();
2844 // Add to list and select it
2845 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2846 pwalletMain->SetAddressBookName(strAddress, strName);
2847 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2848 SetSelection(m_listCtrl, nIndex);
2849 m_listCtrl->SetFocus();
2850 if (nPage == SENDING)
2851 pframeMain->RefreshListCtrl();
2854 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2857 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2860 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2866 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2877 //////////////////////////////////////////////////////////////////////////////
2884 ID_TASKBAR_RESTORE = 10001,
2887 ID_TASKBAR_GENERATE,
2891 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2892 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2893 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2894 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2895 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2896 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2897 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2900 void CMyTaskBarIcon::Show(bool fShow)
2902 static char pszPrevTip[200];
2905 string strTooltip = _("Bitcoin");
2906 if (fGenerateBitcoins)
2907 strTooltip = _("Bitcoin - Generating");
2908 if (fGenerateBitcoins && vNodes.empty())
2909 strTooltip = _("Bitcoin - (not connected)");
2911 // Optimization, only update when changed, using char array to be reentrant
2912 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2914 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2916 // somehow it'll choose the wrong size and scale it down if
2917 // we use the main icon, so we hand it one with only 16x16
2918 SetIcon(wxICON(favicon), strTooltip);
2920 SetIcon(bitcoin80_xpm, strTooltip);
2926 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2931 void CMyTaskBarIcon::Hide()
2936 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2941 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2946 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2949 CSendDialog dialog(pframeMain);
2953 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2955 // Since it's modal, get the main window to do it
2956 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2957 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2960 void CMyTaskBarIcon::Restore()
2963 wxIconizeEvent event(0, false);
2964 pframeMain->GetEventHandler()->AddPendingEvent(event);
2965 pframeMain->Iconize(false);
2966 pframeMain->Raise();
2969 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2971 event.Check(fGenerateBitcoins);
2974 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2976 pframeMain->Close(true);
2979 void CMyTaskBarIcon::UpdateTooltip()
2981 if (IsIconInstalled())
2985 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2987 wxMenu* pmenu = new wxMenu;
2988 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2989 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2990 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2991 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2992 pmenu->AppendSeparator();
2993 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
3003 //////////////////////////////////////////////////////////////////////////////
3008 void CreateMainWindow()
3010 pframeMain = new CMainFrame(NULL);
3011 if (GetBoolArg("-min"))
3012 pframeMain->Iconize(true);
3013 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3014 if (!GetBoolArg("-minimizetotray"))
3015 fMinimizeToTray = false;
3017 pframeMain->Show(true); // have to show first to get taskbar button to hide
3018 if (fMinimizeToTray && pframeMain->IsIconized())
3019 fClosedToTray = true;
3020 pframeMain->Show(!fClosedToTray);
3021 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3022 CreateThread(ThreadDelayedRepaint, NULL);
3026 // Define a new application
3027 class CMyApp : public wxApp
3036 // Hook Initialize so we can start without GUI
3037 virtual bool Initialize(int& argc, wxChar** argv);
3039 // 2nd-level exception handling: we get all the exceptions occurring in any
3040 // event handler here
3041 virtual bool OnExceptionInMainLoop();
3043 // 3rd, and final, level exception handling: whenever an unhandled
3044 // exception is caught, this function is called
3045 virtual void OnUnhandledException();
3047 // and now for something different: this function is called in case of a
3048 // crash (e.g. dereferencing null pointer, division by 0, ...)
3049 virtual void OnFatalException();
3052 IMPLEMENT_APP(CMyApp)
3054 bool CMyApp::Initialize(int& argc, wxChar** argv)
3056 for (int i = 1; i < argc; i++)
3057 if (!IsSwitchChar(argv[i][0]))
3058 fCommandLine = true;
3062 // wxApp::Initialize will remove environment-specific parameters,
3063 // so it's too early to call ParseParameters yet
3064 for (int i = 1; i < argc; i++)
3066 wxString str = argv[i];
3068 if (str.size() >= 1 && str[0] == '/')
3070 char pszLower[MAX_PATH];
3071 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3075 if (str == "-daemon")
3081 if (fDaemon || fCommandLine)
3083 // Call the original Initialize while suppressing error messages
3084 // and ignoring failure. If unable to initialize GTK, it fails
3085 // near the end so hopefully the last few things don't matter.
3088 wxApp::Initialize(argc, argv);
3097 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3101 pthread_exit((void*)0);
3103 pid_t sid = setsid();
3105 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3112 return wxApp::Initialize(argc, argv);
3115 bool CMyApp::OnInit()
3117 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3118 // Disable malfunctioning wxWidgets debug assertion
3119 extern int g_isPainting;
3120 g_isPainting = 10000;
3122 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3123 SetAppName("Bitcoin");
3125 SetAppName("bitcoin");
3129 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3130 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3131 class wxMBConv_win32 : public wxMBConv
3135 size_t m_minMBCharWidth;
3137 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3138 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3142 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3143 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3144 g_locale.AddCatalogLookupPathPrefix("locale");
3146 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3147 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3149 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3150 g_locale.AddCatalog("bitcoin");
3153 HDC hdc = GetDC(NULL);
3156 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3157 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3158 ReleaseDC(NULL, hdc);
3162 return AppInit(argc, argv);
3165 int CMyApp::OnExit()
3168 return wxApp::OnExit();
3171 bool CMyApp::OnExceptionInMainLoop()
3177 catch (std::exception& e)
3179 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3180 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3186 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3187 wxLogWarning("Unknown exception");
3194 void CMyApp::OnUnhandledException()
3196 // this shows how we may let some exception propagate uncaught
3201 catch (std::exception& e)
3203 PrintException(&e, "CMyApp::OnUnhandledException()");
3204 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3210 PrintException(NULL, "CMyApp::OnUnhandledException()");
3211 wxLogWarning("Unknown exception");
3217 void CMyApp::OnFatalException()
3219 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);