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 #if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
1809 unlink(GetAutostartFilePath().string().c_str());
1811 unlink(GetAutostartFilePath().native_file_string().c_str());
1816 char pszExePath[MAX_PATH+1];
1817 memset(pszExePath, 0, sizeof(pszExePath));
1818 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1821 boost::filesystem::create_directories(GetAutostartDir());
1823 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1824 if (!optionFile.good())
1826 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1829 // Write a bitcoin.desktop file to the autostart directory:
1830 optionFile << "[Desktop Entry]\n";
1831 optionFile << "Type=Application\n";
1832 optionFile << "Name=Bitcoin\n";
1833 optionFile << "Exec=" << pszExePath << "\n";
1834 optionFile << "Terminal=false\n";
1835 optionFile << "Hidden=false\n";
1841 // TODO: OSX startup stuff; see:
1842 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1844 bool GetStartOnSystemStartup() { return false; }
1845 void SetStartOnSystemStartup(bool fAutoStart) { }
1854 //////////////////////////////////////////////////////////////////////////////
1859 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1861 // Set up list box of page choices
1862 m_listBox->Append(_("Main"));
1863 //m_listBox->Append(_("Test 2"));
1864 m_listBox->SetSelection(0);
1867 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1869 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1871 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1872 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1873 if (!GetBoolArg("-minimizetotray"))
1875 // Minimize to tray is just too buggy on Linux
1876 fMinimizeToTray = false;
1877 m_checkBoxMinimizeToTray->SetValue(false);
1878 m_checkBoxMinimizeToTray->Enable(false);
1879 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1882 #ifdef __WXMAC_OSX__
1883 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1887 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1888 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1889 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1890 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1892 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1894 m_checkBoxUseUPnP->Enable(false);
1895 m_checkBoxUseProxy->SetValue(fUseProxy);
1896 m_textCtrlProxyIP->Enable(fUseProxy);
1897 m_textCtrlProxyPort->Enable(fUseProxy);
1898 m_staticTextProxyIP->Enable(fUseProxy);
1899 m_staticTextProxyPort->Enable(fUseProxy);
1900 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1901 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1903 m_buttonOK->SetFocus();
1906 void COptionsDialog::SelectPage(int nPage)
1908 m_panelMain->Show(nPage == 0);
1909 m_panelTest2->Show(nPage == 1);
1911 m_scrolledWindow->Layout();
1912 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1915 void COptionsDialog::OnListBox(wxCommandEvent& event)
1917 SelectPage(event.GetSelection());
1920 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1923 int64 nTmp = nTransactionFee;
1924 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1925 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1928 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1930 m_textCtrlProxyIP->Enable(event.IsChecked());
1931 m_textCtrlProxyPort->Enable(event.IsChecked());
1932 m_staticTextProxyIP->Enable(event.IsChecked());
1933 m_staticTextProxyPort->Enable(event.IsChecked());
1936 CAddress COptionsDialog::GetProxyAddr()
1938 // Be careful about byte order, addr.ip and addr.port are big endian
1939 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1940 if (addr.ip == INADDR_NONE)
1941 addr.ip = addrProxy.ip;
1942 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1943 addr.port = htons(nPort);
1944 if (nPort <= 0 || nPort > USHRT_MAX)
1945 addr.port = addrProxy.port;
1949 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1952 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1953 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1957 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1959 OnButtonApply(event);
1963 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1968 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1970 CWalletDB walletdb(pwalletMain->strWalletFile);
1972 int64 nPrevTransactionFee = nTransactionFee;
1973 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1974 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1976 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1978 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1979 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1982 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1984 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1985 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1986 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1989 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1991 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1992 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1995 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1997 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1998 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
2002 fUseProxy = m_checkBoxUseProxy->GetValue();
2003 walletdb.WriteSetting("fUseProxy", fUseProxy);
2005 addrProxy = GetProxyAddr();
2006 walletdb.WriteSetting("addrProxy", addrProxy);
2014 //////////////////////////////////////////////////////////////////////////////
2019 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2021 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2023 // Change (c) into UTF-8 or ANSI copyright symbol
2024 wxString str = m_staticTextMain->GetLabel();
2026 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2028 str.Replace("(c)", "\xA9");
2030 m_staticTextMain->SetLabel(str);
2032 // Resize on Linux to make the window fit the text.
2033 // The text was wrapped manually rather than using the Wrap setting because
2034 // the wrap would be too small on Linux and it can't be changed at this point.
2035 wxFont fontTmp = m_staticTextMain->GetFont();
2036 if (fontTmp.GetPointSize() > 8);
2037 fontTmp.SetPointSize(8);
2038 m_staticTextMain->SetFont(fontTmp);
2039 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2041 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2045 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2055 //////////////////////////////////////////////////////////////////////////////
2060 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2063 m_textCtrlAddress->SetValue(strAddress);
2064 m_choiceTransferType->SetSelection(0);
2065 m_bitmapCheckMark->Show(false);
2066 fEnabledPrev = true;
2067 m_textCtrlAddress->SetFocus();
2069 //// todo: should add a display of your balance for convenience
2071 wxFont fontTmp = m_staticTextInstructions->GetFont();
2072 if (fontTmp.GetPointSize() > 9);
2073 fontTmp.SetPointSize(9);
2074 m_staticTextInstructions->SetFont(fontTmp);
2077 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2081 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2084 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2089 SetIcon(wxICON(bitcoin));
2092 // Fixup the tab order
2093 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2094 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2098 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2100 // Reformat the amount
2102 if (m_textCtrlAmount->GetValue().Trim().empty())
2105 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2106 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2109 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2111 // Open address book
2112 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2113 if (dialog.ShowModal())
2114 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2117 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2119 // Copy clipboard to address box
2120 if (wxTheClipboard->Open())
2122 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2124 wxTextDataObject data;
2125 wxTheClipboard->GetData(data);
2126 m_textCtrlAddress->SetValue(data.GetText());
2128 wxTheClipboard->Close();
2132 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2134 static CCriticalSection cs_sendlock;
2135 TRY_CRITICAL_BLOCK(cs_sendlock)
2138 string strAddress = (string)m_textCtrlAddress->GetValue();
2142 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2144 wxMessageBox(_("Error in amount "), _("Send Coins"));
2147 if (nValue > pwalletMain->GetBalance())
2149 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2152 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2154 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2158 // Parse bitcoin address
2159 CBitcoinAddress address(strAddress);
2160 bool fBitcoinAddress = address.IsValid();
2162 if (fBitcoinAddress)
2164 bool fWasLocked = pwalletMain->IsLocked();
2165 if (!GetWalletPassphrase())
2169 CRITICAL_BLOCK(cs_main)
2170 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2172 // Send to bitcoin address
2173 CScript scriptPubKey;
2174 scriptPubKey.SetBitcoinAddress(address);
2176 strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2180 pframeMain->RefreshListCtrl();
2181 wxMessageBox(_("Payment sent "), _("Sending..."));
2183 else if (strError == "ABORTED")
2186 pwalletMain->Lock();
2187 return; // leave send dialog open
2191 wxMessageBox(strError + " ", _("Sending..."));
2194 pwalletMain->Lock();
2199 pwalletMain->Lock();
2204 CAddress addr(strAddress);
2205 if (!addr.IsValid())
2207 wxMessageBox(_("Invalid address "), _("Send Coins"));
2212 wtx.mapValue["to"] = strAddress;
2214 // Send to IP address
2215 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2216 if (!pdialog->ShowModal())
2220 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2221 if (!pwalletMain->mapAddressBook.count(address))
2222 pwalletMain->SetAddressBookName(strAddress, "");
2228 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2239 //////////////////////////////////////////////////////////////////////////////
2244 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
2249 start = wxDateTime::UNow();
2250 memset(pszStatus, 0, sizeof(pszStatus));
2257 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2259 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2262 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2263 m_textCtrlStatus->SetValue("");
2265 CreateThread(SendingDialogStartTransfer, this);
2268 CSendingDialog::~CSendingDialog()
2270 printf("~CSendingDialog()\n");
2273 void CSendingDialog::Close()
2275 // Last one out turn out the lights.
2276 // fWorkDone signals that work side is done and UI thread should call destroy.
2277 // fUIDone signals that UI window has closed and work thread should call destroy.
2278 // This allows the window to disappear and end modality when cancelled
2279 // without making the user wait for ConnectNode to return. The dialog object
2280 // hangs around in the background until the work thread exits.
2291 void CSendingDialog::OnClose(wxCloseEvent& event)
2293 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2300 wxCommandEvent cmdevent;
2301 OnButtonCancel(cmdevent);
2305 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2311 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2317 void CSendingDialog::OnPaint(wxPaintEvent& event)
2320 if (strlen(pszStatus) > 130)
2321 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2323 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2324 m_staticTextSending->SetFocus();
2326 m_buttonCancel->Enable(false);
2329 m_buttonOK->Enable(true);
2330 m_buttonOK->SetFocus();
2331 m_buttonCancel->Enable(false);
2333 if (fAbort && fCanCancel && IsShown())
2335 strcpy(pszStatus, _("CANCELLED"));
2336 m_buttonOK->Enable(true);
2337 m_buttonOK->SetFocus();
2338 m_buttonCancel->Enable(false);
2339 m_buttonCancel->SetLabel(_("Cancelled"));
2341 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2347 // Everything from here on is not in the UI thread and must only communicate
2348 // with the rest of the dialog through variables and calling repaint.
2351 void CSendingDialog::Repaint()
2355 GetEventHandler()->AddPendingEvent(event);
2358 bool CSendingDialog::Status()
2365 if (fAbort && fCanCancel)
2367 memset(pszStatus, 0, 10);
2368 strcpy(pszStatus, _("CANCELLED"));
2376 bool CSendingDialog::Status(const string& str)
2381 // This can be read by the UI thread at any time,
2382 // so copy in a way that can be read cleanly at all times.
2383 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2384 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2390 bool CSendingDialog::Error(const string& str)
2394 Status(string(_("Error: ")) + str);
2398 void SendingDialogStartTransfer(void* parg)
2400 ((CSendingDialog*)parg)->StartTransfer();
2403 void CSendingDialog::StartTransfer()
2405 // Make sure we have enough money
2406 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2408 Error(_("Insufficient funds"));
2412 // We may have connected already for product details
2413 if (!Status(_("Connecting...")))
2415 CNode* pnode = ConnectNode(addr, 15 * 60);
2418 Error(_("Unable to connect"));
2422 // Send order to seller, with response going to OnReply2 via event handler
2423 if (!Status(_("Requesting public key...")))
2425 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2428 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2430 ((CSendingDialog*)parg)->OnReply2(vRecv);
2433 void CSendingDialog::OnReply2(CDataStream& vRecv)
2435 if (!Status(_("Received public key...")))
2438 CScript scriptPubKey;
2447 vRecv >> strMessage;
2449 Error(_("Recipient is not accepting transactions sent by IP address"));
2451 Error(_("Transfer was not accepted"));
2452 //// todo: enlarge the window and enable a hidden white box to put seller's message
2455 vRecv >> scriptPubKey;
2459 //// what do we want to do about this?
2460 Error(_("Invalid response received"));
2464 // Pause to give the user a chance to cancel
2465 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2473 if (!Status(_("Creating transaction...")))
2475 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2477 Error(_("Insufficient funds"));
2481 CReserveKey reservekey(pwalletMain);
2483 bool fWasLocked = pwalletMain->IsLocked();
2484 if (!GetWalletPassphrase())
2487 bool fTxCreated = false;
2488 CRITICAL_BLOCK(cs_main)
2489 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2491 fTxCreated = pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired);
2495 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2496 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()));
2498 Error(_("Transaction creation failed"));
2503 pwalletMain->Lock();
2506 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2508 Error(_("Transaction aborted"));
2512 // Make sure we're still connected
2513 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2516 Error(_("Lost connection, transaction cancelled"));
2520 // Last chance to cancel
2532 if (!Status(_("Sending payment...")))
2536 bool fTxCommitted = false;
2537 CRITICAL_BLOCK(cs_main)
2538 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2540 fTxCommitted = pwalletMain->CommitTransaction(wtx, reservekey);
2544 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."));
2548 // Send payment tx to seller, with response going to OnReply3 via event handler
2549 CWalletTx wtxSend = wtx;
2550 wtxSend.fFromMe = false;
2551 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2553 Status(_("Waiting for confirmation..."));
2557 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2559 ((CSendingDialog*)parg)->OnReply3(vRecv);
2562 void CSendingDialog::OnReply3(CDataStream& vRecv)
2570 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2571 "The transaction is recorded and will credit to the recipient,\n"
2572 "but the comment information will be blank."));
2578 //// what do we want to do about this?
2579 Error(_("Payment was sent, but an invalid response was received"));
2585 Status(_("Payment completed"));
2593 //////////////////////////////////////////////////////////////////////////////
2595 // CAddressBookDialog
2598 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2601 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2604 // Set initially selected page
2605 wxNotebookEvent event;
2606 event.SetSelection(nPageIn);
2607 OnNotebookPageChanged(event);
2608 m_notebook->ChangeSelection(nPageIn);
2610 fDuringSend = fDuringSendIn;
2612 m_buttonCancel->Show(false);
2615 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2617 wxIcon iconAddressBook;
2618 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2619 SetIcon(iconAddressBook);
2623 SetIcon(wxICON(bitcoin));
2626 // Init column headers
2627 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2628 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2629 m_listCtrlSending->SetFocus();
2630 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2631 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2632 m_listCtrlReceiving->SetFocus();
2634 // Fill listctrl with address book data
2635 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2637 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2638 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2640 const CBitcoinAddress& address = item.first;
2641 string strName = item.second;
2642 bool fMine = pwalletMain->HaveKey(address);
2643 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2644 int nIndex = InsertLine(plistCtrl, strName, address.ToString());
2645 if (address.ToString() == (fMine ? strDefaultReceiving : string(strInitSelected)))
2646 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2651 wxString CAddressBookDialog::GetSelectedAddress()
2653 int nIndex = GetSelection(m_listCtrl);
2656 return GetItemText(m_listCtrl, nIndex, 1);
2659 wxString CAddressBookDialog::GetSelectedSendingAddress()
2661 int nIndex = GetSelection(m_listCtrlSending);
2664 return GetItemText(m_listCtrlSending, nIndex, 1);
2667 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2669 int nIndex = GetSelection(m_listCtrlReceiving);
2672 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2675 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2678 nPage = event.GetSelection();
2679 if (nPage == SENDING)
2680 m_listCtrl = m_listCtrlSending;
2681 else if (nPage == RECEIVING)
2682 m_listCtrl = m_listCtrlReceiving;
2683 m_buttonDelete->Show(nPage == SENDING);
2684 m_buttonCopy->Show(nPage == RECEIVING);
2686 m_listCtrl->SetFocus();
2689 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2691 // Update address book with edited name
2693 if (event.IsEditCancelled())
2695 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2696 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2697 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2698 pframeMain->RefreshListCtrl();
2701 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2704 if (nPage == RECEIVING)
2705 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2708 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2713 // Doubleclick returns selection
2714 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2718 // Doubleclick edits item
2719 wxCommandEvent event2;
2720 OnButtonEdit(event2);
2723 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2725 if (nPage != SENDING)
2727 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2729 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2731 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2732 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2733 pwalletMain->DelAddressBookName(strAddress);
2734 m_listCtrl->DeleteItem(nIndex);
2737 pframeMain->RefreshListCtrl();
2740 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2742 // Copy address box to clipboard
2743 if (wxTheClipboard->Open())
2745 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2746 wxTheClipboard->Close();
2750 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2752 CBitcoinAddress address(strAddress);
2753 bool fMine = address.IsValid() && pwalletMain->HaveKey(address);
2755 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2759 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2761 int nIndex = GetSelection(m_listCtrl);
2764 string strName = (string)m_listCtrl->GetItemText(nIndex);
2765 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2766 string strAddressOrg = strAddress;
2768 if (nPage == SENDING)
2770 // Ask name and address
2773 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2774 if (!dialog.ShowModal())
2776 strName = dialog.GetValue1();
2777 strAddress = dialog.GetValue2();
2779 while (CheckIfMine(strAddress, _("Edit Address")));
2782 else if (nPage == RECEIVING)
2785 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2786 if (!dialog.ShowModal())
2788 strName = dialog.GetValue();
2792 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2794 if (strAddress != strAddressOrg)
2795 pwalletMain->DelAddressBookName(strAddressOrg);
2796 pwalletMain->SetAddressBookName(strAddress, strName);
2798 m_listCtrl->SetItem(nIndex, 1, strAddress);
2799 m_listCtrl->SetItemText(nIndex, strName);
2800 pframeMain->RefreshListCtrl();
2803 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2808 if (nPage == SENDING)
2810 // Ask name and address
2813 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2814 if (!dialog.ShowModal())
2816 strName = dialog.GetValue1();
2817 strAddress = dialog.GetValue2();
2819 while (CheckIfMine(strAddress, _("Add Address")));
2821 else if (nPage == RECEIVING)
2824 CGetTextFromUserDialog dialog(this,
2825 _("New Receiving Address"),
2826 _("You should use a new address for each payment you receive.\n\nLabel"),
2828 if (!dialog.ShowModal())
2830 strName = dialog.GetValue();
2832 bool fWasLocked = pwalletMain->IsLocked();
2833 if (!GetWalletPassphrase())
2837 std::vector<unsigned char> newKey;
2838 pwalletMain->GetKeyFromPool(newKey, true);
2839 strAddress = CBitcoinAddress(newKey).ToString();
2842 pwalletMain->Lock();
2845 // Add to list and select it
2846 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2847 pwalletMain->SetAddressBookName(strAddress, strName);
2848 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2849 SetSelection(m_listCtrl, nIndex);
2850 m_listCtrl->SetFocus();
2851 if (nPage == SENDING)
2852 pframeMain->RefreshListCtrl();
2855 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2858 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2861 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2867 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2878 //////////////////////////////////////////////////////////////////////////////
2885 ID_TASKBAR_RESTORE = 10001,
2888 ID_TASKBAR_GENERATE,
2892 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2893 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2894 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2895 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2896 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2897 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2898 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2901 void CMyTaskBarIcon::Show(bool fShow)
2903 static char pszPrevTip[200];
2906 string strTooltip = _("Bitcoin");
2907 if (fGenerateBitcoins)
2908 strTooltip = _("Bitcoin - Generating");
2909 if (fGenerateBitcoins && vNodes.empty())
2910 strTooltip = _("Bitcoin - (not connected)");
2912 // Optimization, only update when changed, using char array to be reentrant
2913 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2915 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2917 // somehow it'll choose the wrong size and scale it down if
2918 // we use the main icon, so we hand it one with only 16x16
2919 SetIcon(wxICON(favicon), strTooltip);
2921 SetIcon(bitcoin80_xpm, strTooltip);
2927 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2932 void CMyTaskBarIcon::Hide()
2937 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2942 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2947 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2950 CSendDialog dialog(pframeMain);
2954 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2956 // Since it's modal, get the main window to do it
2957 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2958 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2961 void CMyTaskBarIcon::Restore()
2964 wxIconizeEvent event(0, false);
2965 pframeMain->GetEventHandler()->AddPendingEvent(event);
2966 pframeMain->Iconize(false);
2967 pframeMain->Raise();
2970 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2972 event.Check(fGenerateBitcoins);
2975 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2977 pframeMain->Close(true);
2980 void CMyTaskBarIcon::UpdateTooltip()
2982 if (IsIconInstalled())
2986 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2988 wxMenu* pmenu = new wxMenu;
2989 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2990 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2991 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2992 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2993 pmenu->AppendSeparator();
2994 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
3004 //////////////////////////////////////////////////////////////////////////////
3009 void CreateMainWindow()
3011 pframeMain = new CMainFrame(NULL);
3012 if (GetBoolArg("-min"))
3013 pframeMain->Iconize(true);
3014 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3015 if (!GetBoolArg("-minimizetotray"))
3016 fMinimizeToTray = false;
3018 pframeMain->Show(true); // have to show first to get taskbar button to hide
3019 if (fMinimizeToTray && pframeMain->IsIconized())
3020 fClosedToTray = true;
3021 pframeMain->Show(!fClosedToTray);
3022 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3023 CreateThread(ThreadDelayedRepaint, NULL);
3027 // Define a new application
3028 class CMyApp : public wxApp
3037 // Hook Initialize so we can start without GUI
3038 virtual bool Initialize(int& argc, wxChar** argv);
3040 // 2nd-level exception handling: we get all the exceptions occurring in any
3041 // event handler here
3042 virtual bool OnExceptionInMainLoop();
3044 // 3rd, and final, level exception handling: whenever an unhandled
3045 // exception is caught, this function is called
3046 virtual void OnUnhandledException();
3048 // and now for something different: this function is called in case of a
3049 // crash (e.g. dereferencing null pointer, division by 0, ...)
3050 virtual void OnFatalException();
3053 IMPLEMENT_APP(CMyApp)
3055 bool CMyApp::Initialize(int& argc, wxChar** argv)
3057 for (int i = 1; i < argc; i++)
3058 if (!IsSwitchChar(argv[i][0]))
3059 fCommandLine = true;
3063 // wxApp::Initialize will remove environment-specific parameters,
3064 // so it's too early to call ParseParameters yet
3065 for (int i = 1; i < argc; i++)
3067 wxString str = argv[i];
3069 if (str.size() >= 1 && str[0] == '/')
3071 char pszLower[MAX_PATH];
3072 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3076 if (str == "-daemon")
3082 if (fDaemon || fCommandLine)
3084 // Call the original Initialize while suppressing error messages
3085 // and ignoring failure. If unable to initialize GTK, it fails
3086 // near the end so hopefully the last few things don't matter.
3089 wxApp::Initialize(argc, argv);
3098 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3102 pthread_exit((void*)0);
3104 pid_t sid = setsid();
3106 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3113 return wxApp::Initialize(argc, argv);
3116 bool CMyApp::OnInit()
3118 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3119 // Disable malfunctioning wxWidgets debug assertion
3120 extern int g_isPainting;
3121 g_isPainting = 10000;
3123 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3124 SetAppName("Bitcoin");
3126 SetAppName("bitcoin");
3130 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3131 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3132 class wxMBConv_win32 : public wxMBConv
3136 size_t m_minMBCharWidth;
3138 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3139 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3143 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3144 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3145 g_locale.AddCatalogLookupPathPrefix("locale");
3147 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3148 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3150 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3151 g_locale.AddCatalog("bitcoin");
3154 HDC hdc = GetDC(NULL);
3157 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3158 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3159 ReleaseDC(NULL, hdc);
3163 return AppInit(argc, argv);
3166 int CMyApp::OnExit()
3169 return wxApp::OnExit();
3172 bool CMyApp::OnExceptionInMainLoop()
3178 catch (std::exception& e)
3180 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3181 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3187 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3188 wxLogWarning("Unknown exception");
3195 void CMyApp::OnUnhandledException()
3197 // this shows how we may let some exception propagate uncaught
3202 catch (std::exception& e)
3204 PrintException(&e, "CMyApp::OnUnhandledException()");
3205 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3211 PrintException(NULL, "CMyApp::OnUnhandledException()");
3212 wxLogWarning("Unknown exception");
3218 void CMyApp::OnFatalException()
3220 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);