1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2011 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
10 #include <boost/filesystem/fstream.hpp>
11 #include <boost/filesystem/convenience.hpp>
17 using namespace boost;
20 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
22 CMainFrame* pframeMain = NULL;
23 CMyTaskBarIcon* ptaskbaricon = NULL;
24 bool fClosedToTray = false;
31 static const double nScaleX = 1.0;
32 static const double nScaleY = 1.0;
42 //////////////////////////////////////////////////////////////////////////////
47 void HandleCtrlA(wxKeyEvent& event)
51 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
52 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
53 textCtrl->SetSelection(-1, -1);
58 //char pszHourFormat[256];
59 //pszHourFormat[0] = '\0';
60 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
61 //return (pszHourFormat[0] != '0');
65 string DateStr(int64 nTime)
67 // Can only be used safely here in the UI
68 return (string)wxDateTime((time_t)nTime).FormatDate();
71 string DateTimeStr(int64 nTime)
73 // Can only be used safely here in the UI
74 wxDateTime datetime((time_t)nTime);
76 return (string)datetime.Format("%x %H:%M");
78 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
81 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
83 // Helper to simplify access to listctrl
85 item.m_itemId = nIndex;
87 item.m_mask = wxLIST_MASK_TEXT;
88 if (!listCtrl->GetItem(item))
90 return item.GetText();
93 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
95 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
96 listCtrl->SetItem(nIndex, 1, str1);
100 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
102 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
103 listCtrl->SetItem(nIndex, 1, str1);
104 listCtrl->SetItem(nIndex, 2, str2);
105 listCtrl->SetItem(nIndex, 3, str3);
106 listCtrl->SetItem(nIndex, 4, str4);
110 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
112 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
113 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
114 listCtrl->SetItem(nIndex, 1, str1);
115 listCtrl->SetItem(nIndex, 2, str2);
116 listCtrl->SetItem(nIndex, 3, str3);
117 listCtrl->SetItem(nIndex, 4, str4);
121 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
123 // Repaint on Windows is more flickery if the colour has ever been set,
124 // so don't want to set it unless it's different. Default colour has
125 // alpha 0 transparent, so our colours don't match using operator==.
126 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
128 c1 = wxColour(0,0,0);
129 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
130 listCtrl->SetItemTextColour(nIndex, colour);
133 void SetSelection(wxListCtrl* listCtrl, int nIndex)
135 int nSize = listCtrl->GetItemCount();
136 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
137 for (int i = 0; i < nSize; i++)
138 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
141 int GetSelection(wxListCtrl* listCtrl)
143 int nSize = listCtrl->GetItemCount();
144 for (int i = 0; i < nSize; i++)
145 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
150 string HtmlEscape(const char* psz, bool fMultiLine=false)
153 for (const char* p = psz; *p; p++)
155 if (*p == '<') len += 4;
156 else if (*p == '>') len += 4;
157 else if (*p == '&') len += 5;
158 else if (*p == '"') len += 6;
159 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
160 else if (*p == '\n' && fMultiLine) len += 5;
166 for (const char* p = psz; *p; p++)
168 if (*p == '<') str += "<";
169 else if (*p == '>') str += ">";
170 else if (*p == '&') str += "&";
171 else if (*p == '"') str += """;
172 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
173 else if (*p == '\n' && fMultiLine) str += "<br>\n";
180 string HtmlEscape(const string& str, bool fMultiLine=false)
182 return HtmlEscape(str.c_str(), fMultiLine);
185 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
187 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
191 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
194 return wxMessageBox(message, caption, style, parent, x, y);
196 if (wxThread::IsMain() || fDaemon)
198 return wxMessageBox(message, caption, style, parent, x, y);
204 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
212 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
214 if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
216 string strMessage = strprintf(
217 _("This transaction is over the size limit. You can still send it for a fee of %s, "
218 "which goes to the nodes that process your transaction and helps to support the network. "
219 "Do you want to pay the fee?"),
220 FormatMoney(nFeeRequired).c_str());
221 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
224 void CalledSetStatusBar(const string& strText, int nField)
226 if (nField == 0 && GetWarnings("statusbar") != "")
228 if (pframeMain && pframeMain->m_statusBar)
229 pframeMain->m_statusBar->SetStatusText(strText, nField);
232 void SetDefaultReceivingAddress(const string& strAddress)
234 // Update main window address and database
235 if (pframeMain == NULL)
237 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
239 CBitcoinAddress address(strAddress);
240 if (!address.IsValid())
242 vector<unsigned char> vchPubKey;
243 if (!pwalletMain->GetPubKey(address, vchPubKey))
245 pwalletMain->SetDefaultKey(vchPubKey);
246 pframeMain->m_textCtrlAddress->SetValue(strAddress);
250 bool GetWalletPassphrase()
252 if (pwalletMain->IsLocked())
254 string strWalletPass;
255 strWalletPass.reserve(100);
256 mlock(&strWalletPass[0], strWalletPass.capacity());
258 // obtain current wallet encrypt/decrypt key, from passphrase
259 // Note that the passphrase is not mlock()d during this entry and could potentially
260 // be obtained from disk long after bitcoin has run.
261 strWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
262 _("Passphrase")).ToStdString();
264 if (!strWalletPass.size())
266 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
267 munlock(&strWalletPass[0], strWalletPass.capacity());
268 wxMessageBox(_("Please supply the current wallet decryption passphrase."), "Bitcoin");
272 if (!pwalletMain->Unlock(strWalletPass))
274 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
275 munlock(&strWalletPass[0], strWalletPass.capacity());
276 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin");
279 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
280 munlock(&strWalletPass[0], strWalletPass.capacity());
294 //////////////////////////////////////////////////////////////////////////////
299 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
301 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
303 // Set initially selected page
304 wxNotebookEvent event;
305 event.SetSelection(0);
306 OnNotebookPageChanged(event);
307 m_notebook->ChangeSelection(0);
310 fRefreshListCtrl = false;
311 fRefreshListCtrlRunning = false;
312 fOnSetFocusAddress = false;
314 m_choiceFilter->SetSelection(0);
315 double dResize = nScaleX;
317 SetIcon(wxICON(bitcoin));
318 SetSize(dResize * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
320 SetIcon(bitcoin80_xpm);
321 SetBackgroundColour(m_toolBar->GetBackgroundColour());
322 wxFont fontTmp = m_staticText41->GetFont();
323 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
324 m_staticTextBalance->SetFont(fontTmp);
325 m_staticTextBalance->SetSize(140, 17);
326 // resize to fit ubuntu's huge default font
328 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
330 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
331 m_listCtrl->SetFocus();
332 ptaskbaricon = new CMyTaskBarIcon();
334 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
335 // to their standard places, leaving these menus empty.
336 GetMenuBar()->Remove(2); // remove Help menu
337 GetMenuBar()->Remove(0); // remove File menu
340 // Init column headers
341 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
342 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
348 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
349 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
351 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
352 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
353 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
354 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
355 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
356 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
357 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
361 int pnWidths[3] = { -100, 88, 300 };
363 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
364 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
366 m_statusBar->SetFieldsCount(3, pnWidths);
368 // Fill your address text box
369 vector<unsigned char> vchPubKey;
370 if (CWalletDB(pwalletMain->strWalletFile,"r").ReadDefaultKey(vchPubKey))
371 m_textCtrlAddress->SetValue(CBitcoinAddress(vchPubKey).ToString());
373 if (pwalletMain->IsCrypted())
374 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
376 m_menuOptions->Remove(m_menuOptionsChangeWalletPassphrase);
378 // Fill listctrl with wallet transactions
382 CMainFrame::~CMainFrame()
389 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
392 nPage = event.GetSelection();
395 m_listCtrl = m_listCtrlAll;
396 fShowGenerated = true;
398 fShowReceived = true;
400 else if (nPage == SENTRECEIVED)
402 m_listCtrl = m_listCtrlSentReceived;
403 fShowGenerated = false;
405 fShowReceived = true;
407 else if (nPage == SENT)
409 m_listCtrl = m_listCtrlSent;
410 fShowGenerated = false;
412 fShowReceived = false;
414 else if (nPage == RECEIVED)
416 m_listCtrl = m_listCtrlReceived;
417 fShowGenerated = false;
419 fShowReceived = true;
422 m_listCtrl->SetFocus();
425 void CMainFrame::OnClose(wxCloseEvent& event)
427 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
429 // Divert close to minimize
431 fClosedToTray = true;
437 CreateThread(Shutdown, NULL);
441 void CMainFrame::OnIconize(wxIconizeEvent& event)
444 // Hide the task bar button when minimized.
445 // Event is sent when the frame is minimized or restored.
446 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
447 // to get rid of the deprecated warning. Just ignore it.
448 if (!event.Iconized())
449 fClosedToTray = false;
450 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
451 if (GetBoolArg("-minimizetotray")) {
453 // The tray icon sometimes disappears on ubuntu karmic
454 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
455 // Reports of CPU peg on 64-bit linux
456 if (fMinimizeToTray && event.Iconized())
457 fClosedToTray = true;
458 Show(!fClosedToTray);
459 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
460 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
465 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
469 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
470 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
473 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
475 // Hidden columns not resizeable
476 if (event.GetColumn() <= 1 && !fDebug)
482 int CMainFrame::GetSortIndex(const string& strSort)
487 // The wx generic listctrl implementation used on GTK doesn't sort,
488 // so we have to do it ourselves. Remember, we sort in reverse order.
489 // In the wx generic implementation, they store the list of items
490 // in a vector, so indexed lookups are fast, but inserts are slower
491 // the closer they are to the top.
493 int high = m_listCtrl->GetItemCount();
496 int mid = low + ((high - low) / 2);
497 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
506 void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxColour& colour, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6)
508 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
509 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
512 if (!fNew && nIndex == -1)
514 string strHash = " " + hashKey.ToString();
515 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
516 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
520 // fNew is for blind insert, only use if you're sure it's new
521 if (fNew || nIndex == -1)
523 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
527 // If sort key changed, must delete and reinsert to make it relocate
528 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
530 m_listCtrl->DeleteItem(nIndex);
531 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
535 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
536 m_listCtrl->SetItem(nIndex, 2, str2);
537 m_listCtrl->SetItem(nIndex, 3, str3);
538 m_listCtrl->SetItem(nIndex, 4, str4);
539 m_listCtrl->SetItem(nIndex, 5, str5);
540 m_listCtrl->SetItem(nIndex, 6, str6);
541 m_listCtrl->SetItemData(nIndex, nData);
542 SetItemTextColour(m_listCtrl, nIndex, colour);
545 bool CMainFrame::DeleteLine(uint256 hashKey)
547 long nData = *(long*)&hashKey;
551 string strHash = " " + hashKey.ToString();
552 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
553 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
557 m_listCtrl->DeleteItem(nIndex);
562 string FormatTxStatus(const CWalletTx& wtx)
567 if (wtx.nLockTime < LOCKTIME_THRESHOLD)
568 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
570 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
574 int nDepth = wtx.GetDepthInMainChain();
575 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
576 return strprintf(_("%d/offline?"), nDepth);
578 return strprintf(_("%d/unconfirmed"), nDepth);
580 return strprintf(_("%d confirmations"), nDepth);
584 string SingleLine(const string& strIn)
587 bool fOneSpace = false;
588 BOOST_FOREACH(unsigned char c, strIn)
596 if (fOneSpace && !strOut.empty())
605 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
607 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
608 int64 nCredit = wtx.GetCredit(true);
609 int64 nDebit = wtx.GetDebit();
610 int64 nNet = nCredit - nDebit;
611 uint256 hash = wtx.GetHash();
612 string strStatus = FormatTxStatus(wtx);
613 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
614 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
615 map<string, string> mapValue = wtx.mapValue;
616 wtx.nLinesDisplayed = 1;
620 if (wtx.IsCoinBase())
622 // Don't show generated coin until confirmed by at least one block after it
623 // so we don't get the user's hopes up until it looks like it's probably accepted.
625 // It is not an error when generated blocks are not accepted. By design,
626 // some percentage of blocks, like 10% or more, will end up not accepted.
627 // This is the normal mechanism by which the network copes with latency.
629 // We display regular transactions right away before any confirmation
630 // because they can always get into some block eventually. Generated coins
631 // are special because if their block is not accepted, they are not valid.
633 if (wtx.GetDepthInMainChain() < 2)
635 wtx.nLinesDisplayed = 0;
643 // Find the block the tx is in
644 CBlockIndex* pindex = NULL;
645 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
646 if (mi != mapBlockIndex.end())
647 pindex = (*mi).second;
649 // Sort order, unrecorded transactions sort to the top
650 string strSort = strprintf("%010d-%01d-%010u",
651 (pindex ? pindex->nHeight : INT_MAX),
652 (wtx.IsCoinBase() ? 1 : 0),
656 if (nNet > 0 || wtx.IsCoinBase())
661 string strDescription;
662 if (wtx.IsCoinBase())
665 strDescription = _("Generated");
668 int64 nUnmatured = 0;
669 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
670 nUnmatured += pwalletMain->GetCredit(txout);
671 if (wtx.IsInMainChain())
673 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
675 // Check if the block was requested by anyone
676 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
677 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
681 strDescription = _("Generated (not accepted)");
685 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
687 // Received by IP connection
690 if (!mapValue["from"].empty())
691 strDescription += _("From: ") + mapValue["from"];
692 if (!mapValue["message"].empty())
694 if (!strDescription.empty())
695 strDescription += " - ";
696 strDescription += mapValue["message"];
701 // Received by Bitcoin Address
704 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
706 if (pwalletMain->IsMine(txout))
708 CBitcoinAddress address;
709 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
711 CRITICAL_BLOCK(pwalletMain->cs_wallet)
713 //strDescription += _("Received payment to ");
714 //strDescription += _("Received with address ");
715 strDescription += _("Received with: ");
716 map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
717 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
719 string strLabel = (*mi).second;
720 strDescription += address.ToString().substr(0,12) + "... ";
721 strDescription += "(" + strLabel + ")";
724 strDescription += address.ToString();
732 string strCredit = FormatMoney(nNet, true);
734 strCredit = "[" + strCredit + "]";
736 InsertLine(fNew, nIndex, hash, strSort, colour,
738 nTime ? DateTimeStr(nTime) : "",
739 SingleLine(strDescription),
745 bool fAllFromMe = true;
746 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
747 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
749 bool fAllToMe = true;
750 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
751 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
753 if (fAllFromMe && fAllToMe)
756 int64 nChange = wtx.GetChange();
757 InsertLine(fNew, nIndex, hash, strSort, colour,
759 nTime ? DateTimeStr(nTime) : "",
760 _("Payment to yourself"),
761 FormatMoney(-(nDebit - nChange), true),
762 FormatMoney(nCredit - nChange, true));
772 int64 nTxFee = nDebit - wtx.GetValueOut();
773 wtx.nLinesDisplayed = 0;
774 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
776 const CTxOut& txout = wtx.vout[nOut];
777 if (pwalletMain->IsMine(txout))
780 CBitcoinAddress address;
782 if (!mapValue["to"].empty())
785 strAddress = mapValue["to"];
789 // Sent to Bitcoin Address
790 if (ExtractAddress(txout.scriptPubKey, NULL, address))
791 strAddress = address.ToString();
794 string strDescription = _("To: ");
795 CRITICAL_BLOCK(pwalletMain->cs_wallet)
796 if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
797 strDescription += pwalletMain->mapAddressBook[address] + " ";
798 strDescription += strAddress;
799 if (!mapValue["message"].empty())
801 if (!strDescription.empty())
802 strDescription += " - ";
803 strDescription += mapValue["message"];
805 else if (!mapValue["comment"].empty())
807 if (!strDescription.empty())
808 strDescription += " - ";
809 strDescription += mapValue["comment"];
812 int64 nValue = txout.nValue;
819 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
821 nTime ? DateTimeStr(nTime) : "",
822 SingleLine(strDescription),
823 FormatMoney(-nValue, true),
826 wtx.nLinesDisplayed++;
832 // Mixed debit transaction, can't break down payees
834 bool fAllMine = true;
835 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
836 fAllMine = fAllMine && pwalletMain->IsMine(txout);
837 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
838 fAllMine = fAllMine && pwalletMain->IsMine(txin);
840 InsertLine(fNew, nIndex, hash, strSort, colour,
842 nTime ? DateTimeStr(nTime) : "",
844 FormatMoney(nNet, true),
852 void CMainFrame::RefreshListCtrl()
854 fRefreshListCtrl = true;
858 void CMainFrame::OnIdle(wxIdleEvent& event)
860 if (fRefreshListCtrl)
862 // Collect list of wallet transactions and sort newest first
863 bool fEntered = false;
864 vector<pair<unsigned int, uint256> > vSorted;
865 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
867 printf("RefreshListCtrl starting\n");
869 fRefreshListCtrl = false;
870 pwalletMain->vWalletUpdated.clear();
872 // Do the newest transactions first
873 vSorted.reserve(pwalletMain->mapWallet.size());
874 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
876 const CWalletTx& wtx = (*it).second;
877 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
878 vSorted.push_back(make_pair(nTime, (*it).first));
880 m_listCtrl->DeleteAllItems();
885 sort(vSorted.begin(), vSorted.end());
888 for (int i = 0; i < vSorted.size();)
892 bool fEntered = false;
893 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
896 uint256& hash = vSorted[i++].second;
897 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
898 if (mi != pwalletMain->mapWallet.end())
899 InsertTransaction((*mi).second, true);
901 if (!fEntered || i == 100 || i % 500 == 0)
905 printf("RefreshListCtrl done\n");
907 // Update transaction total display
912 // Check for time updates
913 static int64 nLastTime;
914 if (GetTime() > nLastTime + 30)
916 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
918 nLastTime = GetTime();
919 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
921 CWalletTx& wtx = (*it).second;
922 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
923 InsertTransaction(wtx, false);
930 void CMainFrame::RefreshStatusColumn()
933 static CBlockIndex* pindexLastBest;
934 static unsigned int nLastRefreshed;
936 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
937 if (nTop == nLastTop && pindexLastBest == pindexBest)
940 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
943 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
945 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
947 // If no updates, only need to do the part that moved onto the screen
948 if (nStart >= nLastTop && nStart < nLastTop + 100)
949 nStart = nLastTop + 100;
950 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
954 pindexLastBest = pindexBest;
955 nLastRefreshed = nListViewUpdated;
957 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
959 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
960 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
961 if (mi == pwalletMain->mapWallet.end())
963 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
966 CWalletTx& wtx = (*mi).second;
967 if (wtx.IsCoinBase() ||
968 wtx.GetTxTime() != wtx.nTimeDisplayed ||
969 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
971 if (!InsertTransaction(wtx, false, nIndex))
972 m_listCtrl->DeleteItem(nIndex--);
976 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
982 void CMainFrame::OnPaint(wxPaintEvent& event)
993 unsigned int nNeedRepaint = 0;
994 unsigned int nLastRepaint = 0;
995 int64 nLastRepaintTime = 0;
996 int64 nRepaintInterval = 500;
998 void ThreadDelayedRepaint(void* parg)
1002 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1004 nLastRepaint = nNeedRepaint;
1007 printf("DelayedRepaint\n");
1009 pframeMain->fRefresh = true;
1010 pframeMain->GetEventHandler()->AddPendingEvent(event);
1013 Sleep(nRepaintInterval);
1017 void MainFrameRepaint()
1019 // This is called by network code that shouldn't access pframeMain
1020 // directly because it could still be running after the UI is closed.
1023 // Don't repaint too often
1024 static int64 nLastRepaintRequest;
1025 if (GetTimeMillis() - nLastRepaintRequest < 100)
1030 nLastRepaintRequest = GetTimeMillis();
1032 printf("MainFrameRepaint\n");
1034 pframeMain->fRefresh = true;
1035 pframeMain->fRefreshListCtrl = true;
1036 pframeMain->GetEventHandler()->AddPendingEvent(event);
1040 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
1042 // Skip lets the listctrl do the paint, we're just hooking the message
1046 ptaskbaricon->UpdateTooltip();
1051 static int nTransactionCount;
1052 bool fPaintedBalance = false;
1053 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1055 nLastRepaint = nNeedRepaint;
1056 nLastRepaintTime = GetTimeMillis();
1058 // Update listctrl contents
1059 if (!pwalletMain->vWalletUpdated.empty())
1061 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
1064 if (m_listCtrl->GetItemCount())
1065 strTop = (string)m_listCtrl->GetItemText(0);
1066 BOOST_FOREACH(uint256 hash, pwalletMain->vWalletUpdated)
1068 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1069 if (mi != pwalletMain->mapWallet.end())
1070 InsertTransaction((*mi).second, false);
1072 pwalletMain->vWalletUpdated.clear();
1073 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1074 m_listCtrl->ScrollList(0, INT_MIN/2);
1079 TRY_CRITICAL_BLOCK(pwalletMain->cs_wallet)
1081 fPaintedBalance = true;
1082 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
1084 // Count hidden and multi-line transactions
1085 nTransactionCount = 0;
1086 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1088 CWalletTx& wtx = (*it).second;
1089 nTransactionCount += wtx.nLinesDisplayed;
1093 if (!pwalletMain->vWalletUpdated.empty() || !fPaintedBalance)
1096 // Update status column of visible items only
1097 RefreshStatusColumn();
1099 // Update status bar
1100 static string strPrevWarning;
1101 string strWarning = GetWarnings("statusbar");
1102 if (strWarning != "")
1103 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1104 else if (strPrevWarning != "")
1105 m_statusBar->SetStatusText("", 0);
1106 strPrevWarning = strWarning;
1109 if (fGenerateBitcoins)
1110 strGen = _(" Generating");
1111 if (fGenerateBitcoins && vNodes.empty())
1112 strGen = _("(not connected)");
1113 m_statusBar->SetStatusText(strGen, 1);
1115 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1116 m_statusBar->SetStatusText(strStatus, 2);
1118 // Update receiving address
1119 string strDefaultAddress = CBitcoinAddress(pwalletMain->vchDefaultKey).ToString();
1120 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1121 m_textCtrlAddress->SetValue(strDefaultAddress);
1125 void UIThreadCall(boost::function0<void> fn)
1127 // Call this with a function object created with bind.
1128 // bind needs all parameters to match the function's expected types
1129 // and all default parameters specified. Some examples:
1130 // UIThreadCall(bind(wxBell));
1131 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1132 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1135 wxCommandEvent event(wxEVT_UITHREADCALL);
1136 event.SetClientData((void*)new boost::function0<void>(fn));
1137 pframeMain->GetEventHandler()->AddPendingEvent(event);
1141 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1143 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1148 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1154 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1156 event.Check(fGenerateBitcoins);
1159 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1161 // Options->Your Receiving Addresses
1162 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1163 if (!dialog.ShowModal())
1167 void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event)
1169 // Options->Encrypt Wallet
1170 if (pwalletMain->IsCrypted())
1172 wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR);
1176 string strWalletPass;
1177 strWalletPass.reserve(100);
1178 mlock(&strWalletPass[0], strWalletPass.capacity());
1180 // obtain current wallet encrypt/decrypt key, from passphrase
1181 // Note that the passphrase is not mlock()d during this entry and could potentially
1182 // be obtained from disk long after bitcoin has run.
1183 strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."),
1184 _("Passphrase")).ToStdString();
1186 if (!strWalletPass.size())
1188 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1189 munlock(&strWalletPass[0], strWalletPass.capacity());
1190 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1194 if(wxMessageBox(_("WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!\nAre you sure you wish to encrypt your wallet?"), "Bitcoin", wxYES_NO) != wxYES)
1197 string strWalletPassTest;
1198 strWalletPassTest.reserve(100);
1199 mlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1200 strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."),
1201 _("Passphrase")).ToStdString();
1203 if (strWalletPassTest != strWalletPass)
1205 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1206 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1207 munlock(&strWalletPass[0], strWalletPass.capacity());
1208 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1209 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1213 if (!pwalletMain->EncryptWallet(strWalletPass))
1215 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1216 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1217 munlock(&strWalletPass[0], strWalletPass.capacity());
1218 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1219 wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR);
1222 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1223 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1224 munlock(&strWalletPass[0], strWalletPass.capacity());
1225 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1226 wxMessageBox(_("Wallet Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1228 m_menuOptions->Remove(m_menuOptionsEncryptWallet);
1229 m_menuOptions->Insert(m_menuOptions->GetMenuItemCount() - 1, m_menuOptionsChangeWalletPassphrase);
1232 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1234 // Options->Change Wallet Encryption Passphrase
1235 if (!pwalletMain->IsCrypted())
1237 wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1241 string strOldWalletPass;
1242 strOldWalletPass.reserve(100);
1243 mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1245 // obtain current wallet encrypt/decrypt key, from passphrase
1246 // Note that the passphrase is not mlock()d during this entry and could potentially
1247 // be obtained from disk long after bitcoin has run.
1248 strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1249 _("Passphrase")).ToStdString();
1251 bool fWasLocked = pwalletMain->IsLocked();
1252 pwalletMain->Lock();
1254 if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1256 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1257 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1258 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1263 pwalletMain->Lock();
1265 string strNewWalletPass;
1266 strNewWalletPass.reserve(100);
1267 mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1269 // obtain new wallet encrypt/decrypt key, from passphrase
1270 // Note that the passphrase is not mlock()d during this entry and could potentially
1271 // be obtained from disk long after bitcoin has run.
1272 strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1273 _("Passphrase")).ToStdString();
1275 if (!strNewWalletPass.size())
1277 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1278 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1279 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1280 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1281 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1285 string strNewWalletPassTest;
1286 strNewWalletPassTest.reserve(100);
1287 mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1289 // obtain new wallet encrypt/decrypt key, from passphrase
1290 // Note that the passphrase is not mlock()d during this entry and could potentially
1291 // be obtained from disk long after bitcoin has run.
1292 strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1293 _("Passphrase")).ToStdString();
1295 if (strNewWalletPassTest != strNewWalletPass)
1297 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1298 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1299 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1300 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1301 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1302 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1303 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1307 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1309 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1310 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1311 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1312 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1313 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1314 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1315 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1318 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1319 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1320 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1321 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1322 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1323 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1324 wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
1327 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1330 COptionsDialog dialog(this);
1334 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1337 CAboutDialog dialog(this);
1341 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1344 CSendDialog dialog(this);
1348 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1350 // Toolbar: Address Book
1351 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1352 if (dialogAddr.ShowModal() == 2)
1355 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1356 dialogSend.ShowModal();
1360 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1362 // Automatically select-all when entering window
1364 m_textCtrlAddress->SetSelection(-1, -1);
1365 fOnSetFocusAddress = true;
1368 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1371 if (fOnSetFocusAddress)
1372 m_textCtrlAddress->SetSelection(-1, -1);
1373 fOnSetFocusAddress = false;
1376 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1379 CGetTextFromUserDialog dialog(this,
1380 _("New Receiving Address"),
1381 _("You should use a new address for each payment you receive.\n\nLabel"),
1383 if (!dialog.ShowModal())
1385 string strName = dialog.GetValue();
1389 bool fWasLocked = pwalletMain->IsLocked();
1390 if (!GetWalletPassphrase())
1394 std::vector<unsigned char> newKey;
1395 pwalletMain->GetKeyFromPool(newKey, true);
1396 strAddress = CBitcoinAddress(newKey).ToString();
1399 pwalletMain->Lock();
1402 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1403 pwalletMain->SetAddressBookName(strAddress, strName);
1404 SetDefaultReceivingAddress(strAddress);
1407 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1409 // Copy address box to clipboard
1410 if (wxTheClipboard->Open())
1412 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1413 wxTheClipboard->Close();
1417 void CMainFrame::OnListItemActivated(wxListEvent& event)
1419 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1421 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1423 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1424 if (mi == pwalletMain->mapWallet.end())
1426 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1431 CTxDetailsDialog dialog(this, wtx);
1433 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1442 //////////////////////////////////////////////////////////////////////////////
1447 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1450 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1452 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1455 strHTML.reserve(4000);
1456 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1458 int64 nTime = wtx.GetTxTime();
1459 int64 nCredit = wtx.GetCredit();
1460 int64 nDebit = wtx.GetDebit();
1461 int64 nNet = nCredit - nDebit;
1465 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1466 int nRequests = wtx.GetRequestCount();
1467 if (nRequests != -1)
1470 strHTML += _(", has not been successfully broadcast yet");
1471 else if (nRequests == 1)
1472 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1474 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1478 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1484 if (wtx.IsCoinBase())
1486 strHTML += _("<b>Source:</b> Generated<br>");
1488 else if (!wtx.mapValue["from"].empty())
1490 // Online transaction
1491 if (!wtx.mapValue["from"].empty())
1492 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1496 // Offline transaction
1500 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1502 if (pwalletMain->IsMine(txout))
1504 CBitcoinAddress address;
1505 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1507 if (pwalletMain->mapAddressBook.count(address))
1509 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1510 strHTML += _("<b>To:</b> ");
1511 strHTML += HtmlEscape(address.ToString());
1512 if (!pwalletMain->mapAddressBook[address].empty())
1513 strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[address] + ")";
1515 strHTML += _(" (yours)");
1530 if (!wtx.mapValue["to"].empty())
1532 // Online transaction
1533 strAddress = wtx.mapValue["to"];
1534 strHTML += _("<b>To:</b> ");
1535 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1536 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1537 strHTML += HtmlEscape(strAddress) + "<br>";
1544 if (wtx.IsCoinBase() && nCredit == 0)
1549 int64 nUnmatured = 0;
1550 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1551 nUnmatured += pwalletMain->GetCredit(txout);
1552 strHTML += _("<b>Credit:</b> ");
1553 if (wtx.IsInMainChain())
1554 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1556 strHTML += _("(not accepted)");
1564 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1568 bool fAllFromMe = true;
1569 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1570 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1572 bool fAllToMe = true;
1573 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1574 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1581 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1583 if (pwalletMain->IsMine(txout))
1586 if (wtx.mapValue["to"].empty())
1588 // Offline transaction
1589 CBitcoinAddress address;
1590 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1592 string strAddress = address.ToString();
1593 strHTML += _("<b>To:</b> ");
1594 if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
1595 strHTML += pwalletMain->mapAddressBook[address] + " ";
1596 strHTML += strAddress;
1601 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1607 int64 nChange = wtx.GetChange();
1608 int64 nValue = nCredit - nChange;
1609 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1610 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1613 int64 nTxFee = nDebit - wtx.GetValueOut();
1615 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1620 // Mixed debit transaction
1622 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1623 if (pwalletMain->IsMine(txin))
1624 strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1625 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1626 if (pwalletMain->IsMine(txout))
1627 strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1631 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1637 if (!wtx.mapValue["message"].empty())
1638 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1639 if (!wtx.mapValue["comment"].empty())
1640 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1642 if (wtx.IsCoinBase())
1643 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>";
1651 strHTML += "<hr><br>debug print<br><br>";
1652 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1653 if (pwalletMain->IsMine(txin))
1654 strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1655 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1656 if (pwalletMain->IsMine(txout))
1657 strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1659 strHTML += "<br><b>Transaction:</b><br>";
1660 strHTML += HtmlEscape(wtx.ToString(), true);
1662 strHTML += "<br><b>Inputs:</b><br>";
1663 CRITICAL_BLOCK(pwalletMain->cs_wallet)
1665 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1667 COutPoint prevout = txin.prevout;
1668 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1669 if (mi != pwalletMain->mapWallet.end())
1671 const CWalletTx& prev = (*mi).second;
1672 if (prevout.n < prev.vout.size())
1674 strHTML += HtmlEscape(prev.ToString(), true);
1675 strHTML += " " + FormatTxStatus(prev) + ", ";
1676 strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1685 strHTML += "</font></html>";
1686 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1687 m_htmlWin->SetPage(strHTML);
1688 m_buttonOK->SetFocus();
1692 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1702 //////////////////////////////////////////////////////////////////////////////
1708 string StartupShortcutPath()
1710 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1713 bool GetStartOnSystemStartup()
1715 return filesystem::exists(StartupShortcutPath().c_str());
1718 void SetStartOnSystemStartup(bool fAutoStart)
1720 // If the shortcut exists already, remove it for updating
1721 remove(StartupShortcutPath().c_str());
1727 // Get a pointer to the IShellLink interface.
1728 IShellLink* psl = NULL;
1729 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1730 CLSCTX_INPROC_SERVER, IID_IShellLink,
1731 reinterpret_cast<void**>(&psl));
1733 if (SUCCEEDED(hres))
1735 // Get the current executable path
1736 TCHAR pszExePath[MAX_PATH];
1737 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1739 // Set the path to the shortcut target
1740 psl->SetPath(pszExePath);
1741 PathRemoveFileSpec(pszExePath);
1742 psl->SetWorkingDirectory(pszExePath);
1743 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1745 // Query IShellLink for the IPersistFile interface for
1746 // saving the shortcut in persistent storage.
1747 IPersistFile* ppf = NULL;
1748 hres = psl->QueryInterface(IID_IPersistFile,
1749 reinterpret_cast<void**>(&ppf));
1750 if (SUCCEEDED(hres))
1752 WCHAR pwsz[MAX_PATH];
1753 // Ensure that the string is ANSI.
1754 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1755 // Save the link by calling IPersistFile::Save.
1756 hres = ppf->Save(pwsz, TRUE);
1765 #elif defined(__WXGTK__)
1767 // Follow the Desktop Application Autostart Spec:
1768 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1770 boost::filesystem::path GetAutostartDir()
1772 namespace fs = boost::filesystem;
1774 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1775 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1776 char* pszHome = getenv("HOME");
1777 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1781 boost::filesystem::path GetAutostartFilePath()
1783 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1786 bool GetStartOnSystemStartup()
1788 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1789 if (!optionFile.good())
1791 // Scan through file for "Hidden=true":
1793 while (!optionFile.eof())
1795 getline(optionFile, line);
1796 if (line.find("Hidden") != string::npos &&
1797 line.find("true") != string::npos)
1805 void SetStartOnSystemStartup(bool fAutoStart)
1809 unlink(GetAutostartFilePath().native_file_string().c_str());
1813 char pszExePath[MAX_PATH+1];
1814 memset(pszExePath, 0, sizeof(pszExePath));
1815 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1818 boost::filesystem::create_directories(GetAutostartDir());
1820 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1821 if (!optionFile.good())
1823 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1826 // Write a bitcoin.desktop file to the autostart directory:
1827 optionFile << "[Desktop Entry]\n";
1828 optionFile << "Type=Application\n";
1829 optionFile << "Name=Bitcoin\n";
1830 optionFile << "Exec=" << pszExePath << "\n";
1831 optionFile << "Terminal=false\n";
1832 optionFile << "Hidden=false\n";
1838 // TODO: OSX startup stuff; see:
1839 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1841 bool GetStartOnSystemStartup() { return false; }
1842 void SetStartOnSystemStartup(bool fAutoStart) { }
1851 //////////////////////////////////////////////////////////////////////////////
1856 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1858 // Set up list box of page choices
1859 m_listBox->Append(_("Main"));
1860 //m_listBox->Append(_("Test 2"));
1861 m_listBox->SetSelection(0);
1864 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1866 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1868 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1869 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1870 if (!GetBoolArg("-minimizetotray"))
1872 // Minimize to tray is just too buggy on Linux
1873 fMinimizeToTray = false;
1874 m_checkBoxMinimizeToTray->SetValue(false);
1875 m_checkBoxMinimizeToTray->Enable(false);
1876 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1879 #ifdef __WXMAC_OSX__
1880 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1884 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1885 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1886 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1887 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1889 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1891 m_checkBoxUseUPnP->Enable(false);
1892 m_checkBoxUseProxy->SetValue(fUseProxy);
1893 m_textCtrlProxyIP->Enable(fUseProxy);
1894 m_textCtrlProxyPort->Enable(fUseProxy);
1895 m_staticTextProxyIP->Enable(fUseProxy);
1896 m_staticTextProxyPort->Enable(fUseProxy);
1897 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1898 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1900 m_buttonOK->SetFocus();
1903 void COptionsDialog::SelectPage(int nPage)
1905 m_panelMain->Show(nPage == 0);
1906 m_panelTest2->Show(nPage == 1);
1908 m_scrolledWindow->Layout();
1909 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1912 void COptionsDialog::OnListBox(wxCommandEvent& event)
1914 SelectPage(event.GetSelection());
1917 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1920 int64 nTmp = nTransactionFee;
1921 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1922 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1925 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1927 m_textCtrlProxyIP->Enable(event.IsChecked());
1928 m_textCtrlProxyPort->Enable(event.IsChecked());
1929 m_staticTextProxyIP->Enable(event.IsChecked());
1930 m_staticTextProxyPort->Enable(event.IsChecked());
1933 CAddress COptionsDialog::GetProxyAddr()
1935 // Be careful about byte order, addr.ip and addr.port are big endian
1936 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1937 if (addr.ip == INADDR_NONE)
1938 addr.ip = addrProxy.ip;
1939 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1940 addr.port = htons(nPort);
1941 if (nPort <= 0 || nPort > USHRT_MAX)
1942 addr.port = addrProxy.port;
1946 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1949 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1950 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1954 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1956 OnButtonApply(event);
1960 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1965 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1967 CWalletDB walletdb(pwalletMain->strWalletFile);
1969 int64 nPrevTransactionFee = nTransactionFee;
1970 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1971 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1973 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1975 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1976 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1979 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1981 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1982 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1983 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1986 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1988 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1989 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1992 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1994 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1995 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1999 fUseProxy = m_checkBoxUseProxy->GetValue();
2000 walletdb.WriteSetting("fUseProxy", fUseProxy);
2002 addrProxy = GetProxyAddr();
2003 walletdb.WriteSetting("addrProxy", addrProxy);
2011 //////////////////////////////////////////////////////////////////////////////
2016 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2018 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2020 // Change (c) into UTF-8 or ANSI copyright symbol
2021 wxString str = m_staticTextMain->GetLabel();
2023 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2025 str.Replace("(c)", "\xA9");
2027 m_staticTextMain->SetLabel(str);
2029 // Resize on Linux to make the window fit the text.
2030 // The text was wrapped manually rather than using the Wrap setting because
2031 // the wrap would be too small on Linux and it can't be changed at this point.
2032 wxFont fontTmp = m_staticTextMain->GetFont();
2033 if (fontTmp.GetPointSize() > 8);
2034 fontTmp.SetPointSize(8);
2035 m_staticTextMain->SetFont(fontTmp);
2036 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2038 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2042 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2052 //////////////////////////////////////////////////////////////////////////////
2057 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2060 m_textCtrlAddress->SetValue(strAddress);
2061 m_choiceTransferType->SetSelection(0);
2062 m_bitmapCheckMark->Show(false);
2063 fEnabledPrev = true;
2064 m_textCtrlAddress->SetFocus();
2066 //// todo: should add a display of your balance for convenience
2068 wxFont fontTmp = m_staticTextInstructions->GetFont();
2069 if (fontTmp.GetPointSize() > 9);
2070 fontTmp.SetPointSize(9);
2071 m_staticTextInstructions->SetFont(fontTmp);
2074 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2078 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2081 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2086 SetIcon(wxICON(bitcoin));
2089 // Fixup the tab order
2090 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2091 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2095 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2097 // Reformat the amount
2099 if (m_textCtrlAmount->GetValue().Trim().empty())
2102 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2103 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2106 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2108 // Open address book
2109 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2110 if (dialog.ShowModal())
2111 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2114 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2116 // Copy clipboard to address box
2117 if (wxTheClipboard->Open())
2119 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2121 wxTextDataObject data;
2122 wxTheClipboard->GetData(data);
2123 m_textCtrlAddress->SetValue(data.GetText());
2125 wxTheClipboard->Close();
2129 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2131 static CCriticalSection cs_sendlock;
2132 TRY_CRITICAL_BLOCK(cs_sendlock)
2135 string strAddress = (string)m_textCtrlAddress->GetValue();
2139 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2141 wxMessageBox(_("Error in amount "), _("Send Coins"));
2144 if (nValue > pwalletMain->GetBalance())
2146 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2149 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2151 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2155 // Parse bitcoin address
2156 CBitcoinAddress address(strAddress);
2157 bool fBitcoinAddress = address.IsValid();
2159 if (fBitcoinAddress)
2161 bool fWasLocked = pwalletMain->IsLocked();
2162 if (!GetWalletPassphrase())
2166 CRITICAL_BLOCK(cs_main)
2167 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2169 // Send to bitcoin address
2170 CScript scriptPubKey;
2171 scriptPubKey.SetBitcoinAddress(address);
2173 strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2176 wxMessageBox(_("Payment sent "), _("Sending..."));
2177 else if (strError == "ABORTED")
2180 pwalletMain->Lock();
2181 return; // leave send dialog open
2185 wxMessageBox(strError + " ", _("Sending..."));
2188 pwalletMain->Lock();
2193 pwalletMain->Lock();
2198 CAddress addr(strAddress);
2199 if (!addr.IsValid())
2201 wxMessageBox(_("Invalid address "), _("Send Coins"));
2206 wtx.mapValue["to"] = strAddress;
2208 // Send to IP address
2209 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2210 if (!pdialog->ShowModal())
2214 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2215 if (!pwalletMain->mapAddressBook.count(address))
2216 pwalletMain->SetAddressBookName(strAddress, "");
2222 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2233 //////////////////////////////////////////////////////////////////////////////
2238 CSendingDialog::CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 nPriceIn, const CWalletTx& wtxIn) : CSendingDialogBase(NULL) // we have to give null so parent can't destroy us
2243 start = wxDateTime::UNow();
2244 memset(pszStatus, 0, sizeof(pszStatus));
2251 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2253 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2256 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2257 m_textCtrlStatus->SetValue("");
2259 CreateThread(SendingDialogStartTransfer, this);
2262 CSendingDialog::~CSendingDialog()
2264 printf("~CSendingDialog()\n");
2267 void CSendingDialog::Close()
2269 // Last one out turn out the lights.
2270 // fWorkDone signals that work side is done and UI thread should call destroy.
2271 // fUIDone signals that UI window has closed and work thread should call destroy.
2272 // This allows the window to disappear and end modality when cancelled
2273 // without making the user wait for ConnectNode to return. The dialog object
2274 // hangs around in the background until the work thread exits.
2285 void CSendingDialog::OnClose(wxCloseEvent& event)
2287 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2294 wxCommandEvent cmdevent;
2295 OnButtonCancel(cmdevent);
2299 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2305 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2311 void CSendingDialog::OnPaint(wxPaintEvent& event)
2314 if (strlen(pszStatus) > 130)
2315 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2317 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2318 m_staticTextSending->SetFocus();
2320 m_buttonCancel->Enable(false);
2323 m_buttonOK->Enable(true);
2324 m_buttonOK->SetFocus();
2325 m_buttonCancel->Enable(false);
2327 if (fAbort && fCanCancel && IsShown())
2329 strcpy(pszStatus, _("CANCELLED"));
2330 m_buttonOK->Enable(true);
2331 m_buttonOK->SetFocus();
2332 m_buttonCancel->Enable(false);
2333 m_buttonCancel->SetLabel(_("Cancelled"));
2335 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2341 // Everything from here on is not in the UI thread and must only communicate
2342 // with the rest of the dialog through variables and calling repaint.
2345 void CSendingDialog::Repaint()
2349 GetEventHandler()->AddPendingEvent(event);
2352 bool CSendingDialog::Status()
2359 if (fAbort && fCanCancel)
2361 memset(pszStatus, 0, 10);
2362 strcpy(pszStatus, _("CANCELLED"));
2370 bool CSendingDialog::Status(const string& str)
2375 // This can be read by the UI thread at any time,
2376 // so copy in a way that can be read cleanly at all times.
2377 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2378 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2384 bool CSendingDialog::Error(const string& str)
2388 Status(string(_("Error: ")) + str);
2392 void SendingDialogStartTransfer(void* parg)
2394 ((CSendingDialog*)parg)->StartTransfer();
2397 void CSendingDialog::StartTransfer()
2399 // Make sure we have enough money
2400 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2402 Error(_("Insufficient funds"));
2406 // We may have connected already for product details
2407 if (!Status(_("Connecting...")))
2409 CNode* pnode = ConnectNode(addr, 15 * 60);
2412 Error(_("Unable to connect"));
2416 // Send order to seller, with response going to OnReply2 via event handler
2417 if (!Status(_("Requesting public key...")))
2419 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2422 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2424 ((CSendingDialog*)parg)->OnReply2(vRecv);
2427 void CSendingDialog::OnReply2(CDataStream& vRecv)
2429 if (!Status(_("Received public key...")))
2432 CScript scriptPubKey;
2441 vRecv >> strMessage;
2443 Error(_("Recipient is not accepting transactions sent by IP address"));
2445 Error(_("Transfer was not accepted"));
2446 //// todo: enlarge the window and enable a hidden white box to put seller's message
2449 vRecv >> scriptPubKey;
2453 //// what do we want to do about this?
2454 Error(_("Invalid response received"));
2458 // Pause to give the user a chance to cancel
2459 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2467 if (!Status(_("Creating transaction...")))
2469 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2471 Error(_("Insufficient funds"));
2475 CReserveKey reservekey(pwalletMain);
2477 bool fWasLocked = pwalletMain->IsLocked();
2478 if (!GetWalletPassphrase())
2481 bool fTxCreated = false;
2482 CRITICAL_BLOCK(cs_main)
2483 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2485 fTxCreated = pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired);
2489 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2490 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()));
2492 Error(_("Transaction creation failed"));
2497 pwalletMain->Lock();
2500 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2502 Error(_("Transaction aborted"));
2506 // Make sure we're still connected
2507 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2510 Error(_("Lost connection, transaction cancelled"));
2514 // Last chance to cancel
2526 if (!Status(_("Sending payment...")))
2530 bool fTxCommitted = false;
2531 CRITICAL_BLOCK(cs_main)
2532 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2534 fTxCommitted = pwalletMain->CommitTransaction(wtx, reservekey);
2538 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."));
2542 // Send payment tx to seller, with response going to OnReply3 via event handler
2543 CWalletTx wtxSend = wtx;
2544 wtxSend.fFromMe = false;
2545 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2547 Status(_("Waiting for confirmation..."));
2551 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2553 ((CSendingDialog*)parg)->OnReply3(vRecv);
2556 void CSendingDialog::OnReply3(CDataStream& vRecv)
2564 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2565 "The transaction is recorded and will credit to the recipient,\n"
2566 "but the comment information will be blank."));
2572 //// what do we want to do about this?
2573 Error(_("Payment was sent, but an invalid response was received"));
2579 Status(_("Payment completed"));
2587 //////////////////////////////////////////////////////////////////////////////
2589 // CAddressBookDialog
2592 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2595 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2598 // Set initially selected page
2599 wxNotebookEvent event;
2600 event.SetSelection(nPageIn);
2601 OnNotebookPageChanged(event);
2602 m_notebook->ChangeSelection(nPageIn);
2604 fDuringSend = fDuringSendIn;
2606 m_buttonCancel->Show(false);
2609 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2611 wxIcon iconAddressBook;
2612 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2613 SetIcon(iconAddressBook);
2617 SetIcon(wxICON(bitcoin));
2620 // Init column headers
2621 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2622 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2623 m_listCtrlSending->SetFocus();
2624 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2625 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2626 m_listCtrlReceiving->SetFocus();
2628 // Fill listctrl with address book data
2629 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2631 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2632 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2634 const CBitcoinAddress& address = item.first;
2635 string strName = item.second;
2636 bool fMine = pwalletMain->HaveKey(address);
2637 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2638 int nIndex = InsertLine(plistCtrl, strName, address.ToString());
2639 if (address.ToString() == (fMine ? strDefaultReceiving : string(strInitSelected)))
2640 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2645 wxString CAddressBookDialog::GetSelectedAddress()
2647 int nIndex = GetSelection(m_listCtrl);
2650 return GetItemText(m_listCtrl, nIndex, 1);
2653 wxString CAddressBookDialog::GetSelectedSendingAddress()
2655 int nIndex = GetSelection(m_listCtrlSending);
2658 return GetItemText(m_listCtrlSending, nIndex, 1);
2661 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2663 int nIndex = GetSelection(m_listCtrlReceiving);
2666 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2669 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2672 nPage = event.GetSelection();
2673 if (nPage == SENDING)
2674 m_listCtrl = m_listCtrlSending;
2675 else if (nPage == RECEIVING)
2676 m_listCtrl = m_listCtrlReceiving;
2677 m_buttonDelete->Show(nPage == SENDING);
2678 m_buttonCopy->Show(nPage == RECEIVING);
2680 m_listCtrl->SetFocus();
2683 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2685 // Update address book with edited name
2687 if (event.IsEditCancelled())
2689 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2690 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2691 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2692 pframeMain->RefreshListCtrl();
2695 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2698 if (nPage == RECEIVING)
2699 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2702 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2707 // Doubleclick returns selection
2708 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2712 // Doubleclick edits item
2713 wxCommandEvent event2;
2714 OnButtonEdit(event2);
2717 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2719 if (nPage != SENDING)
2721 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2723 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2725 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2726 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2727 pwalletMain->DelAddressBookName(strAddress);
2728 m_listCtrl->DeleteItem(nIndex);
2731 pframeMain->RefreshListCtrl();
2734 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2736 // Copy address box to clipboard
2737 if (wxTheClipboard->Open())
2739 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2740 wxTheClipboard->Close();
2744 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2746 CBitcoinAddress address(strAddress);
2747 bool fMine = address.IsValid() && pwalletMain->HaveKey(address);
2749 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2753 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2755 int nIndex = GetSelection(m_listCtrl);
2758 string strName = (string)m_listCtrl->GetItemText(nIndex);
2759 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2760 string strAddressOrg = strAddress;
2762 if (nPage == SENDING)
2764 // Ask name and address
2767 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2768 if (!dialog.ShowModal())
2770 strName = dialog.GetValue1();
2771 strAddress = dialog.GetValue2();
2773 while (CheckIfMine(strAddress, _("Edit Address")));
2776 else if (nPage == RECEIVING)
2779 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2780 if (!dialog.ShowModal())
2782 strName = dialog.GetValue();
2786 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2788 if (strAddress != strAddressOrg)
2789 pwalletMain->DelAddressBookName(strAddressOrg);
2790 pwalletMain->SetAddressBookName(strAddress, strName);
2792 m_listCtrl->SetItem(nIndex, 1, strAddress);
2793 m_listCtrl->SetItemText(nIndex, strName);
2794 pframeMain->RefreshListCtrl();
2797 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2802 if (nPage == SENDING)
2804 // Ask name and address
2807 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2808 if (!dialog.ShowModal())
2810 strName = dialog.GetValue1();
2811 strAddress = dialog.GetValue2();
2813 while (CheckIfMine(strAddress, _("Add Address")));
2815 else if (nPage == RECEIVING)
2818 CGetTextFromUserDialog dialog(this,
2819 _("New Receiving Address"),
2820 _("You should use a new address for each payment you receive.\n\nLabel"),
2822 if (!dialog.ShowModal())
2824 strName = dialog.GetValue();
2826 bool fWasLocked = pwalletMain->IsLocked();
2827 if (!GetWalletPassphrase())
2831 std::vector<unsigned char> newKey;
2832 pwalletMain->GetKeyFromPool(newKey, true);
2833 strAddress = CBitcoinAddress(newKey).ToString();
2836 pwalletMain->Lock();
2839 // Add to list and select it
2840 CRITICAL_BLOCK(pwalletMain->cs_wallet)
2841 pwalletMain->SetAddressBookName(strAddress, strName);
2842 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2843 SetSelection(m_listCtrl, nIndex);
2844 m_listCtrl->SetFocus();
2845 if (nPage == SENDING)
2846 pframeMain->RefreshListCtrl();
2849 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2852 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2855 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2861 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2872 //////////////////////////////////////////////////////////////////////////////
2879 ID_TASKBAR_RESTORE = 10001,
2882 ID_TASKBAR_GENERATE,
2886 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2887 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2888 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2889 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2890 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2891 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2892 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2895 void CMyTaskBarIcon::Show(bool fShow)
2897 static char pszPrevTip[200];
2900 string strTooltip = _("Bitcoin");
2901 if (fGenerateBitcoins)
2902 strTooltip = _("Bitcoin - Generating");
2903 if (fGenerateBitcoins && vNodes.empty())
2904 strTooltip = _("Bitcoin - (not connected)");
2906 // Optimization, only update when changed, using char array to be reentrant
2907 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2909 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2911 // somehow it'll choose the wrong size and scale it down if
2912 // we use the main icon, so we hand it one with only 16x16
2913 SetIcon(wxICON(favicon), strTooltip);
2915 SetIcon(bitcoin80_xpm, strTooltip);
2921 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2926 void CMyTaskBarIcon::Hide()
2931 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2936 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2941 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2944 CSendDialog dialog(pframeMain);
2948 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2950 // Since it's modal, get the main window to do it
2951 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2952 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2955 void CMyTaskBarIcon::Restore()
2958 wxIconizeEvent event(0, false);
2959 pframeMain->GetEventHandler()->AddPendingEvent(event);
2960 pframeMain->Iconize(false);
2961 pframeMain->Raise();
2964 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2966 event.Check(fGenerateBitcoins);
2969 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2971 pframeMain->Close(true);
2974 void CMyTaskBarIcon::UpdateTooltip()
2976 if (IsIconInstalled())
2980 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2982 wxMenu* pmenu = new wxMenu;
2983 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2984 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2985 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2986 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2987 pmenu->AppendSeparator();
2988 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2998 //////////////////////////////////////////////////////////////////////////////
3003 void CreateMainWindow()
3005 pframeMain = new CMainFrame(NULL);
3006 if (GetBoolArg("-min"))
3007 pframeMain->Iconize(true);
3008 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3009 if (!GetBoolArg("-minimizetotray"))
3010 fMinimizeToTray = false;
3012 pframeMain->Show(true); // have to show first to get taskbar button to hide
3013 if (fMinimizeToTray && pframeMain->IsIconized())
3014 fClosedToTray = true;
3015 pframeMain->Show(!fClosedToTray);
3016 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3017 CreateThread(ThreadDelayedRepaint, NULL);
3021 // Define a new application
3022 class CMyApp : public wxApp
3031 // Hook Initialize so we can start without GUI
3032 virtual bool Initialize(int& argc, wxChar** argv);
3034 // 2nd-level exception handling: we get all the exceptions occurring in any
3035 // event handler here
3036 virtual bool OnExceptionInMainLoop();
3038 // 3rd, and final, level exception handling: whenever an unhandled
3039 // exception is caught, this function is called
3040 virtual void OnUnhandledException();
3042 // and now for something different: this function is called in case of a
3043 // crash (e.g. dereferencing null pointer, division by 0, ...)
3044 virtual void OnFatalException();
3047 IMPLEMENT_APP(CMyApp)
3049 bool CMyApp::Initialize(int& argc, wxChar** argv)
3051 for (int i = 1; i < argc; i++)
3052 if (!IsSwitchChar(argv[i][0]))
3053 fCommandLine = true;
3057 // wxApp::Initialize will remove environment-specific parameters,
3058 // so it's too early to call ParseParameters yet
3059 for (int i = 1; i < argc; i++)
3061 wxString str = argv[i];
3063 if (str.size() >= 1 && str[0] == '/')
3065 char pszLower[MAX_PATH];
3066 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3070 if (str == "-daemon")
3076 if (fDaemon || fCommandLine)
3078 // Call the original Initialize while suppressing error messages
3079 // and ignoring failure. If unable to initialize GTK, it fails
3080 // near the end so hopefully the last few things don't matter.
3083 wxApp::Initialize(argc, argv);
3092 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3096 pthread_exit((void*)0);
3098 pid_t sid = setsid();
3100 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3107 return wxApp::Initialize(argc, argv);
3110 bool CMyApp::OnInit()
3112 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3113 // Disable malfunctioning wxWidgets debug assertion
3114 extern int g_isPainting;
3115 g_isPainting = 10000;
3117 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3118 SetAppName("Bitcoin");
3120 SetAppName("bitcoin");
3124 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3125 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3126 class wxMBConv_win32 : public wxMBConv
3130 size_t m_minMBCharWidth;
3132 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3133 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3137 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3138 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3139 g_locale.AddCatalogLookupPathPrefix("locale");
3141 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3142 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3144 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3145 g_locale.AddCatalog("bitcoin");
3148 HDC hdc = GetDC(NULL);
3151 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3152 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3153 ReleaseDC(NULL, hdc);
3157 return AppInit(argc, argv);
3160 int CMyApp::OnExit()
3163 return wxApp::OnExit();
3166 bool CMyApp::OnExceptionInMainLoop()
3172 catch (std::exception& e)
3174 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3175 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3181 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3182 wxLogWarning("Unknown exception");
3189 void CMyApp::OnUnhandledException()
3191 // this shows how we may let some exception propagate uncaught
3196 catch (std::exception& e)
3198 PrintException(&e, "CMyApp::OnUnhandledException()");
3199 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3205 PrintException(NULL, "CMyApp::OnUnhandledException()");
3206 wxLogWarning("Unknown exception");
3212 void CMyApp::OnFatalException()
3214 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);