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_mapAddressBook)
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_mapAddressBook)
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_mapWallet)
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_mapWallet)
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_mapWallet)
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_mapWallet)
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_mapWallet)
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_mapWallet)
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 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1252 bool fWasLocked = pwalletMain->IsLocked();
1253 pwalletMain->Lock();
1255 if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1257 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1258 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1259 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1264 pwalletMain->Lock();
1266 string strNewWalletPass;
1267 strNewWalletPass.reserve(100);
1268 mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1270 // obtain new wallet encrypt/decrypt key, from passphrase
1271 // Note that the passphrase is not mlock()d during this entry and could potentially
1272 // be obtained from disk long after bitcoin has run.
1273 strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1274 _("Passphrase")).ToStdString();
1276 if (!strNewWalletPass.size())
1278 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1279 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1280 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1281 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1282 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1286 string strNewWalletPassTest;
1287 strNewWalletPassTest.reserve(100);
1288 mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1290 // obtain new wallet encrypt/decrypt key, from passphrase
1291 // Note that the passphrase is not mlock()d during this entry and could potentially
1292 // be obtained from disk long after bitcoin has run.
1293 strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1294 _("Passphrase")).ToStdString();
1296 if (strNewWalletPassTest != strNewWalletPass)
1298 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1299 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1300 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1301 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1302 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1303 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1304 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1308 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1310 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1311 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1312 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1313 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1314 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1315 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1316 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1319 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1320 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1321 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1322 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1323 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1324 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1325 wxMessageBox(_("Wallet Passphrase Changed."), "Bitcoin");
1329 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1332 COptionsDialog dialog(this);
1336 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1339 CAboutDialog dialog(this);
1343 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1346 CSendDialog dialog(this);
1350 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1352 // Toolbar: Address Book
1353 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1354 if (dialogAddr.ShowModal() == 2)
1357 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1358 dialogSend.ShowModal();
1362 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1364 // Automatically select-all when entering window
1366 m_textCtrlAddress->SetSelection(-1, -1);
1367 fOnSetFocusAddress = true;
1370 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1373 if (fOnSetFocusAddress)
1374 m_textCtrlAddress->SetSelection(-1, -1);
1375 fOnSetFocusAddress = false;
1378 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1381 CGetTextFromUserDialog dialog(this,
1382 _("New Receiving Address"),
1383 _("You should use a new address for each payment you receive.\n\nLabel"),
1385 if (!dialog.ShowModal())
1387 string strName = dialog.GetValue();
1390 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1392 bool fWasLocked = pwalletMain->IsLocked();
1393 if (!GetWalletPassphrase())
1397 strAddress = CBitcoinAddress(pwalletMain->GetOrReuseKeyFromPool()).ToString();
1400 pwalletMain->Lock();
1404 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1405 pwalletMain->SetAddressBookName(strAddress, strName);
1406 SetDefaultReceivingAddress(strAddress);
1409 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1411 // Copy address box to clipboard
1412 if (wxTheClipboard->Open())
1414 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1415 wxTheClipboard->Close();
1419 void CMainFrame::OnListItemActivated(wxListEvent& event)
1421 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1423 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1425 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1426 if (mi == pwalletMain->mapWallet.end())
1428 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1433 CTxDetailsDialog dialog(this, wtx);
1435 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1444 //////////////////////////////////////////////////////////////////////////////
1449 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1452 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1454 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1457 strHTML.reserve(4000);
1458 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1460 int64 nTime = wtx.GetTxTime();
1461 int64 nCredit = wtx.GetCredit();
1462 int64 nDebit = wtx.GetDebit();
1463 int64 nNet = nCredit - nDebit;
1467 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1468 int nRequests = wtx.GetRequestCount();
1469 if (nRequests != -1)
1472 strHTML += _(", has not been successfully broadcast yet");
1473 else if (nRequests == 1)
1474 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1476 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1480 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1486 if (wtx.IsCoinBase())
1488 strHTML += _("<b>Source:</b> Generated<br>");
1490 else if (!wtx.mapValue["from"].empty())
1492 // Online transaction
1493 if (!wtx.mapValue["from"].empty())
1494 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1498 // Offline transaction
1502 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1504 if (pwalletMain->IsMine(txout))
1506 CBitcoinAddress address;
1507 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1509 if (pwalletMain->mapAddressBook.count(address))
1511 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1512 strHTML += _("<b>To:</b> ");
1513 strHTML += HtmlEscape(address.ToString());
1514 if (!pwalletMain->mapAddressBook[address].empty())
1515 strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[address] + ")";
1517 strHTML += _(" (yours)");
1532 if (!wtx.mapValue["to"].empty())
1534 // Online transaction
1535 strAddress = wtx.mapValue["to"];
1536 strHTML += _("<b>To:</b> ");
1537 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1538 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1539 strHTML += HtmlEscape(strAddress) + "<br>";
1546 if (wtx.IsCoinBase() && nCredit == 0)
1551 int64 nUnmatured = 0;
1552 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1553 nUnmatured += pwalletMain->GetCredit(txout);
1554 strHTML += _("<b>Credit:</b> ");
1555 if (wtx.IsInMainChain())
1556 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1558 strHTML += _("(not accepted)");
1566 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1570 bool fAllFromMe = true;
1571 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1572 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1574 bool fAllToMe = true;
1575 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1576 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1583 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1585 if (pwalletMain->IsMine(txout))
1588 if (wtx.mapValue["to"].empty())
1590 // Offline transaction
1591 CBitcoinAddress address;
1592 if (ExtractAddress(txout.scriptPubKey, pwalletMain, address))
1594 string strAddress = address.ToString();
1595 strHTML += _("<b>To:</b> ");
1596 if (pwalletMain->mapAddressBook.count(address) && !pwalletMain->mapAddressBook[address].empty())
1597 strHTML += pwalletMain->mapAddressBook[address] + " ";
1598 strHTML += strAddress;
1603 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1609 int64 nChange = wtx.GetChange();
1610 int64 nValue = nCredit - nChange;
1611 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1612 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1615 int64 nTxFee = nDebit - wtx.GetValueOut();
1617 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1622 // Mixed debit transaction
1624 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1625 if (pwalletMain->IsMine(txin))
1626 strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1627 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1628 if (pwalletMain->IsMine(txout))
1629 strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1633 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1639 if (!wtx.mapValue["message"].empty())
1640 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1641 if (!wtx.mapValue["comment"].empty())
1642 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1644 if (wtx.IsCoinBase())
1645 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>";
1653 strHTML += "<hr><br>debug print<br><br>";
1654 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1655 if (pwalletMain->IsMine(txin))
1656 strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1657 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1658 if (pwalletMain->IsMine(txout))
1659 strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1661 strHTML += "<br><b>Transaction:</b><br>";
1662 strHTML += HtmlEscape(wtx.ToString(), true);
1664 strHTML += "<br><b>Inputs:</b><br>";
1665 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1667 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1669 COutPoint prevout = txin.prevout;
1670 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1671 if (mi != pwalletMain->mapWallet.end())
1673 const CWalletTx& prev = (*mi).second;
1674 if (prevout.n < prev.vout.size())
1676 strHTML += HtmlEscape(prev.ToString(), true);
1677 strHTML += " " + FormatTxStatus(prev) + ", ";
1678 strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1687 strHTML += "</font></html>";
1688 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1689 m_htmlWin->SetPage(strHTML);
1690 m_buttonOK->SetFocus();
1694 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1704 //////////////////////////////////////////////////////////////////////////////
1710 string StartupShortcutPath()
1712 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1715 bool GetStartOnSystemStartup()
1717 return filesystem::exists(StartupShortcutPath().c_str());
1720 void SetStartOnSystemStartup(bool fAutoStart)
1722 // If the shortcut exists already, remove it for updating
1723 remove(StartupShortcutPath().c_str());
1729 // Get a pointer to the IShellLink interface.
1730 IShellLink* psl = NULL;
1731 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1732 CLSCTX_INPROC_SERVER, IID_IShellLink,
1733 reinterpret_cast<void**>(&psl));
1735 if (SUCCEEDED(hres))
1737 // Get the current executable path
1738 TCHAR pszExePath[MAX_PATH];
1739 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1741 // Set the path to the shortcut target
1742 psl->SetPath(pszExePath);
1743 PathRemoveFileSpec(pszExePath);
1744 psl->SetWorkingDirectory(pszExePath);
1745 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1747 // Query IShellLink for the IPersistFile interface for
1748 // saving the shortcut in persistent storage.
1749 IPersistFile* ppf = NULL;
1750 hres = psl->QueryInterface(IID_IPersistFile,
1751 reinterpret_cast<void**>(&ppf));
1752 if (SUCCEEDED(hres))
1754 WCHAR pwsz[MAX_PATH];
1755 // Ensure that the string is ANSI.
1756 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1757 // Save the link by calling IPersistFile::Save.
1758 hres = ppf->Save(pwsz, TRUE);
1767 #elif defined(__WXGTK__)
1769 // Follow the Desktop Application Autostart Spec:
1770 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1772 boost::filesystem::path GetAutostartDir()
1774 namespace fs = boost::filesystem;
1776 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1777 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1778 char* pszHome = getenv("HOME");
1779 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1783 boost::filesystem::path GetAutostartFilePath()
1785 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1788 bool GetStartOnSystemStartup()
1790 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1791 if (!optionFile.good())
1793 // Scan through file for "Hidden=true":
1795 while (!optionFile.eof())
1797 getline(optionFile, line);
1798 if (line.find("Hidden") != string::npos &&
1799 line.find("true") != string::npos)
1807 void SetStartOnSystemStartup(bool fAutoStart)
1811 unlink(GetAutostartFilePath().native_file_string().c_str());
1815 char pszExePath[MAX_PATH+1];
1816 memset(pszExePath, 0, sizeof(pszExePath));
1817 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1820 boost::filesystem::create_directories(GetAutostartDir());
1822 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1823 if (!optionFile.good())
1825 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1828 // Write a bitcoin.desktop file to the autostart directory:
1829 optionFile << "[Desktop Entry]\n";
1830 optionFile << "Type=Application\n";
1831 optionFile << "Name=Bitcoin\n";
1832 optionFile << "Exec=" << pszExePath << "\n";
1833 optionFile << "Terminal=false\n";
1834 optionFile << "Hidden=false\n";
1840 // TODO: OSX startup stuff; see:
1841 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1843 bool GetStartOnSystemStartup() { return false; }
1844 void SetStartOnSystemStartup(bool fAutoStart) { }
1853 //////////////////////////////////////////////////////////////////////////////
1858 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1860 // Set up list box of page choices
1861 m_listBox->Append(_("Main"));
1862 //m_listBox->Append(_("Test 2"));
1863 m_listBox->SetSelection(0);
1866 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1868 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1870 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1871 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1872 if (!GetBoolArg("-minimizetotray"))
1874 // Minimize to tray is just too buggy on Linux
1875 fMinimizeToTray = false;
1876 m_checkBoxMinimizeToTray->SetValue(false);
1877 m_checkBoxMinimizeToTray->Enable(false);
1878 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1881 #ifdef __WXMAC_OSX__
1882 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1886 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1887 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1888 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1889 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1891 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1893 m_checkBoxUseUPnP->Enable(false);
1894 m_checkBoxUseProxy->SetValue(fUseProxy);
1895 m_textCtrlProxyIP->Enable(fUseProxy);
1896 m_textCtrlProxyPort->Enable(fUseProxy);
1897 m_staticTextProxyIP->Enable(fUseProxy);
1898 m_staticTextProxyPort->Enable(fUseProxy);
1899 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1900 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1902 m_buttonOK->SetFocus();
1905 void COptionsDialog::SelectPage(int nPage)
1907 m_panelMain->Show(nPage == 0);
1908 m_panelTest2->Show(nPage == 1);
1910 m_scrolledWindow->Layout();
1911 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1914 void COptionsDialog::OnListBox(wxCommandEvent& event)
1916 SelectPage(event.GetSelection());
1919 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1922 int64 nTmp = nTransactionFee;
1923 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1924 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1927 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1929 m_textCtrlProxyIP->Enable(event.IsChecked());
1930 m_textCtrlProxyPort->Enable(event.IsChecked());
1931 m_staticTextProxyIP->Enable(event.IsChecked());
1932 m_staticTextProxyPort->Enable(event.IsChecked());
1935 CAddress COptionsDialog::GetProxyAddr()
1937 // Be careful about byte order, addr.ip and addr.port are big endian
1938 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1939 if (addr.ip == INADDR_NONE)
1940 addr.ip = addrProxy.ip;
1941 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1942 addr.port = htons(nPort);
1943 if (nPort <= 0 || nPort > USHRT_MAX)
1944 addr.port = addrProxy.port;
1948 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1951 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1952 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1956 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1958 OnButtonApply(event);
1962 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1967 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1969 CWalletDB walletdb(pwalletMain->strWalletFile);
1971 int64 nPrevTransactionFee = nTransactionFee;
1972 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1973 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1975 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1977 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1978 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1981 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1983 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1984 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1985 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1988 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1990 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1991 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1994 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1996 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1997 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
2001 fUseProxy = m_checkBoxUseProxy->GetValue();
2002 walletdb.WriteSetting("fUseProxy", fUseProxy);
2004 addrProxy = GetProxyAddr();
2005 walletdb.WriteSetting("addrProxy", addrProxy);
2013 //////////////////////////////////////////////////////////////////////////////
2018 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2020 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2022 // Change (c) into UTF-8 or ANSI copyright symbol
2023 wxString str = m_staticTextMain->GetLabel();
2025 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2027 str.Replace("(c)", "\xA9");
2029 m_staticTextMain->SetLabel(str);
2031 // Resize on Linux to make the window fit the text.
2032 // The text was wrapped manually rather than using the Wrap setting because
2033 // the wrap would be too small on Linux and it can't be changed at this point.
2034 wxFont fontTmp = m_staticTextMain->GetFont();
2035 if (fontTmp.GetPointSize() > 8);
2036 fontTmp.SetPointSize(8);
2037 m_staticTextMain->SetFont(fontTmp);
2038 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2040 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2044 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2054 //////////////////////////////////////////////////////////////////////////////
2059 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2062 m_textCtrlAddress->SetValue(strAddress);
2063 m_choiceTransferType->SetSelection(0);
2064 m_bitmapCheckMark->Show(false);
2065 fEnabledPrev = true;
2066 m_textCtrlAddress->SetFocus();
2068 //// todo: should add a display of your balance for convenience
2070 wxFont fontTmp = m_staticTextInstructions->GetFont();
2071 if (fontTmp.GetPointSize() > 9);
2072 fontTmp.SetPointSize(9);
2073 m_staticTextInstructions->SetFont(fontTmp);
2076 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2080 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2083 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2088 SetIcon(wxICON(bitcoin));
2091 // Fixup the tab order
2092 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2093 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2097 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2099 // Reformat the amount
2101 if (m_textCtrlAmount->GetValue().Trim().empty())
2104 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2105 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2108 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2110 // Open address book
2111 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2112 if (dialog.ShowModal())
2113 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2116 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2118 // Copy clipboard to address box
2119 if (wxTheClipboard->Open())
2121 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2123 wxTextDataObject data;
2124 wxTheClipboard->GetData(data);
2125 m_textCtrlAddress->SetValue(data.GetText());
2127 wxTheClipboard->Close();
2131 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2133 static CCriticalSection cs_sendlock;
2134 TRY_CRITICAL_BLOCK(cs_sendlock)
2137 string strAddress = (string)m_textCtrlAddress->GetValue();
2141 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2143 wxMessageBox(_("Error in amount "), _("Send Coins"));
2146 if (nValue > pwalletMain->GetBalance())
2148 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2151 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2153 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2157 // Parse bitcoin address
2158 CBitcoinAddress address(strAddress);
2159 bool fBitcoinAddress = address.IsValid();
2161 if (fBitcoinAddress)
2163 CRITICAL_BLOCK(cs_main)
2164 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2166 bool fWasLocked = pwalletMain->IsLocked();
2167 if (!GetWalletPassphrase())
2170 // Send to bitcoin address
2171 CScript scriptPubKey;
2172 scriptPubKey.SetBitcoinAddress(address);
2174 string 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();
2199 CAddress addr(strAddress);
2200 if (!addr.IsValid())
2202 wxMessageBox(_("Invalid address "), _("Send Coins"));
2207 wtx.mapValue["to"] = strAddress;
2209 // Send to IP address
2210 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2211 if (!pdialog->ShowModal())
2215 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2216 if (!pwalletMain->mapAddressBook.count(address))
2217 pwalletMain->SetAddressBookName(strAddress, "");
2223 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2234 //////////////////////////////////////////////////////////////////////////////
2239 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
2244 start = wxDateTime::UNow();
2245 memset(pszStatus, 0, sizeof(pszStatus));
2252 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2254 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2257 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2258 m_textCtrlStatus->SetValue("");
2260 CreateThread(SendingDialogStartTransfer, this);
2263 CSendingDialog::~CSendingDialog()
2265 printf("~CSendingDialog()\n");
2268 void CSendingDialog::Close()
2270 // Last one out turn out the lights.
2271 // fWorkDone signals that work side is done and UI thread should call destroy.
2272 // fUIDone signals that UI window has closed and work thread should call destroy.
2273 // This allows the window to disappear and end modality when cancelled
2274 // without making the user wait for ConnectNode to return. The dialog object
2275 // hangs around in the background until the work thread exits.
2286 void CSendingDialog::OnClose(wxCloseEvent& event)
2288 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2295 wxCommandEvent cmdevent;
2296 OnButtonCancel(cmdevent);
2300 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2306 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2312 void CSendingDialog::OnPaint(wxPaintEvent& event)
2315 if (strlen(pszStatus) > 130)
2316 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2318 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2319 m_staticTextSending->SetFocus();
2321 m_buttonCancel->Enable(false);
2324 m_buttonOK->Enable(true);
2325 m_buttonOK->SetFocus();
2326 m_buttonCancel->Enable(false);
2328 if (fAbort && fCanCancel && IsShown())
2330 strcpy(pszStatus, _("CANCELLED"));
2331 m_buttonOK->Enable(true);
2332 m_buttonOK->SetFocus();
2333 m_buttonCancel->Enable(false);
2334 m_buttonCancel->SetLabel(_("Cancelled"));
2336 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2342 // Everything from here on is not in the UI thread and must only communicate
2343 // with the rest of the dialog through variables and calling repaint.
2346 void CSendingDialog::Repaint()
2350 GetEventHandler()->AddPendingEvent(event);
2353 bool CSendingDialog::Status()
2360 if (fAbort && fCanCancel)
2362 memset(pszStatus, 0, 10);
2363 strcpy(pszStatus, _("CANCELLED"));
2371 bool CSendingDialog::Status(const string& str)
2376 // This can be read by the UI thread at any time,
2377 // so copy in a way that can be read cleanly at all times.
2378 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2379 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2385 bool CSendingDialog::Error(const string& str)
2389 Status(string(_("Error: ")) + str);
2393 void SendingDialogStartTransfer(void* parg)
2395 ((CSendingDialog*)parg)->StartTransfer();
2398 void CSendingDialog::StartTransfer()
2400 // Make sure we have enough money
2401 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2403 Error(_("Insufficient funds"));
2407 // We may have connected already for product details
2408 if (!Status(_("Connecting...")))
2410 CNode* pnode = ConnectNode(addr, 15 * 60);
2413 Error(_("Unable to connect"));
2417 // Send order to seller, with response going to OnReply2 via event handler
2418 if (!Status(_("Requesting public key...")))
2420 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2423 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2425 ((CSendingDialog*)parg)->OnReply2(vRecv);
2428 void CSendingDialog::OnReply2(CDataStream& vRecv)
2430 if (!Status(_("Received public key...")))
2433 CScript scriptPubKey;
2442 vRecv >> strMessage;
2444 Error(_("Recipient is not accepting transactions sent by IP address"));
2446 Error(_("Transfer was not accepted"));
2447 //// todo: enlarge the window and enable a hidden white box to put seller's message
2450 vRecv >> scriptPubKey;
2454 //// what do we want to do about this?
2455 Error(_("Invalid response received"));
2459 // Pause to give the user a chance to cancel
2460 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2467 CRITICAL_BLOCK(cs_main)
2470 if (!Status(_("Creating transaction...")))
2472 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2474 Error(_("Insufficient funds"));
2478 CReserveKey reservekey(pwalletMain);
2480 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2482 bool fWasLocked = pwalletMain->IsLocked();
2483 if (!GetWalletPassphrase())
2486 if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2488 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2489 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()));
2491 Error(_("Transaction creation failed"));
2496 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 if (!pwalletMain->CommitTransaction(wtx, reservekey))
2532 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."));
2536 // Send payment tx to seller, with response going to OnReply3 via event handler
2537 CWalletTx wtxSend = wtx;
2538 wtxSend.fFromMe = false;
2539 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2541 Status(_("Waiting for confirmation..."));
2546 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2548 ((CSendingDialog*)parg)->OnReply3(vRecv);
2551 void CSendingDialog::OnReply3(CDataStream& vRecv)
2559 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2560 "The transaction is recorded and will credit to the recipient,\n"
2561 "but the comment information will be blank."));
2567 //// what do we want to do about this?
2568 Error(_("Payment was sent, but an invalid response was received"));
2574 Status(_("Payment completed"));
2582 //////////////////////////////////////////////////////////////////////////////
2584 // CAddressBookDialog
2587 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2590 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2593 // Set initially selected page
2594 wxNotebookEvent event;
2595 event.SetSelection(nPageIn);
2596 OnNotebookPageChanged(event);
2597 m_notebook->ChangeSelection(nPageIn);
2599 fDuringSend = fDuringSendIn;
2601 m_buttonCancel->Show(false);
2604 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2606 wxIcon iconAddressBook;
2607 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2608 SetIcon(iconAddressBook);
2612 SetIcon(wxICON(bitcoin));
2615 // Init column headers
2616 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2617 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2618 m_listCtrlSending->SetFocus();
2619 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2620 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2621 m_listCtrlReceiving->SetFocus();
2623 // Fill listctrl with address book data
2624 CRITICAL_BLOCK(pwalletMain->cs_KeyStore)
2625 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2627 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2628 BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
2630 const CBitcoinAddress& address = item.first;
2631 string strName = item.second;
2632 bool fMine = pwalletMain->HaveKey(address);
2633 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2634 int nIndex = InsertLine(plistCtrl, strName, address.ToString());
2635 if (address.ToString() == (fMine ? strDefaultReceiving : string(strInitSelected)))
2636 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2641 wxString CAddressBookDialog::GetSelectedAddress()
2643 int nIndex = GetSelection(m_listCtrl);
2646 return GetItemText(m_listCtrl, nIndex, 1);
2649 wxString CAddressBookDialog::GetSelectedSendingAddress()
2651 int nIndex = GetSelection(m_listCtrlSending);
2654 return GetItemText(m_listCtrlSending, nIndex, 1);
2657 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2659 int nIndex = GetSelection(m_listCtrlReceiving);
2662 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2665 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2668 nPage = event.GetSelection();
2669 if (nPage == SENDING)
2670 m_listCtrl = m_listCtrlSending;
2671 else if (nPage == RECEIVING)
2672 m_listCtrl = m_listCtrlReceiving;
2673 m_buttonDelete->Show(nPage == SENDING);
2674 m_buttonCopy->Show(nPage == RECEIVING);
2676 m_listCtrl->SetFocus();
2679 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2681 // Update address book with edited name
2683 if (event.IsEditCancelled())
2685 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2686 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2687 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2688 pframeMain->RefreshListCtrl();
2691 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2694 if (nPage == RECEIVING)
2695 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2698 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2703 // Doubleclick returns selection
2704 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2708 // Doubleclick edits item
2709 wxCommandEvent event2;
2710 OnButtonEdit(event2);
2713 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2715 if (nPage != SENDING)
2717 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2719 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2721 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2722 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2723 pwalletMain->DelAddressBookName(strAddress);
2724 m_listCtrl->DeleteItem(nIndex);
2727 pframeMain->RefreshListCtrl();
2730 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2732 // Copy address box to clipboard
2733 if (wxTheClipboard->Open())
2735 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2736 wxTheClipboard->Close();
2740 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2742 CBitcoinAddress address(strAddress);
2743 bool fMine = address.IsValid() && pwalletMain->HaveKey(address);
2745 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2749 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2751 int nIndex = GetSelection(m_listCtrl);
2754 string strName = (string)m_listCtrl->GetItemText(nIndex);
2755 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2756 string strAddressOrg = strAddress;
2758 if (nPage == SENDING)
2760 // Ask name and address
2763 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2764 if (!dialog.ShowModal())
2766 strName = dialog.GetValue1();
2767 strAddress = dialog.GetValue2();
2769 while (CheckIfMine(strAddress, _("Edit Address")));
2772 else if (nPage == RECEIVING)
2775 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2776 if (!dialog.ShowModal())
2778 strName = dialog.GetValue();
2782 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2784 if (strAddress != strAddressOrg)
2785 pwalletMain->DelAddressBookName(strAddressOrg);
2786 pwalletMain->SetAddressBookName(strAddress, strName);
2788 m_listCtrl->SetItem(nIndex, 1, strAddress);
2789 m_listCtrl->SetItemText(nIndex, strName);
2790 pframeMain->RefreshListCtrl();
2793 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2798 if (nPage == SENDING)
2800 // Ask name and address
2803 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2804 if (!dialog.ShowModal())
2806 strName = dialog.GetValue1();
2807 strAddress = dialog.GetValue2();
2809 while (CheckIfMine(strAddress, _("Add Address")));
2811 else if (nPage == RECEIVING)
2814 CGetTextFromUserDialog dialog(this,
2815 _("New Receiving Address"),
2816 _("You should use a new address for each payment you receive.\n\nLabel"),
2818 if (!dialog.ShowModal())
2820 strName = dialog.GetValue();
2822 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2824 bool fWasLocked = pwalletMain->IsLocked();
2825 if (!GetWalletPassphrase())
2829 strAddress = CBitcoinAddress(pwalletMain->GetOrReuseKeyFromPool()).ToString();
2832 pwalletMain->Lock();
2836 // Add to list and select it
2837 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2838 pwalletMain->SetAddressBookName(strAddress, strName);
2839 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2840 SetSelection(m_listCtrl, nIndex);
2841 m_listCtrl->SetFocus();
2842 if (nPage == SENDING)
2843 pframeMain->RefreshListCtrl();
2846 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2849 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2852 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2858 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2869 //////////////////////////////////////////////////////////////////////////////
2876 ID_TASKBAR_RESTORE = 10001,
2879 ID_TASKBAR_GENERATE,
2883 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2884 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2885 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2886 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2887 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2888 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2889 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2892 void CMyTaskBarIcon::Show(bool fShow)
2894 static char pszPrevTip[200];
2897 string strTooltip = _("Bitcoin");
2898 if (fGenerateBitcoins)
2899 strTooltip = _("Bitcoin - Generating");
2900 if (fGenerateBitcoins && vNodes.empty())
2901 strTooltip = _("Bitcoin - (not connected)");
2903 // Optimization, only update when changed, using char array to be reentrant
2904 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2906 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2908 // somehow it'll choose the wrong size and scale it down if
2909 // we use the main icon, so we hand it one with only 16x16
2910 SetIcon(wxICON(favicon), strTooltip);
2912 SetIcon(bitcoin80_xpm, strTooltip);
2918 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2923 void CMyTaskBarIcon::Hide()
2928 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2933 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2938 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2941 CSendDialog dialog(pframeMain);
2945 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2947 // Since it's modal, get the main window to do it
2948 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2949 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2952 void CMyTaskBarIcon::Restore()
2955 wxIconizeEvent event(0, false);
2956 pframeMain->GetEventHandler()->AddPendingEvent(event);
2957 pframeMain->Iconize(false);
2958 pframeMain->Raise();
2961 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2963 event.Check(fGenerateBitcoins);
2966 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2968 pframeMain->Close(true);
2971 void CMyTaskBarIcon::UpdateTooltip()
2973 if (IsIconInstalled())
2977 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2979 wxMenu* pmenu = new wxMenu;
2980 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2981 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2982 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2983 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2984 pmenu->AppendSeparator();
2985 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2995 //////////////////////////////////////////////////////////////////////////////
3000 void CreateMainWindow()
3002 pframeMain = new CMainFrame(NULL);
3003 if (GetBoolArg("-min"))
3004 pframeMain->Iconize(true);
3005 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
3006 if (!GetBoolArg("-minimizetotray"))
3007 fMinimizeToTray = false;
3009 pframeMain->Show(true); // have to show first to get taskbar button to hide
3010 if (fMinimizeToTray && pframeMain->IsIconized())
3011 fClosedToTray = true;
3012 pframeMain->Show(!fClosedToTray);
3013 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3014 CreateThread(ThreadDelayedRepaint, NULL);
3018 // Define a new application
3019 class CMyApp : public wxApp
3028 // Hook Initialize so we can start without GUI
3029 virtual bool Initialize(int& argc, wxChar** argv);
3031 // 2nd-level exception handling: we get all the exceptions occurring in any
3032 // event handler here
3033 virtual bool OnExceptionInMainLoop();
3035 // 3rd, and final, level exception handling: whenever an unhandled
3036 // exception is caught, this function is called
3037 virtual void OnUnhandledException();
3039 // and now for something different: this function is called in case of a
3040 // crash (e.g. dereferencing null pointer, division by 0, ...)
3041 virtual void OnFatalException();
3044 IMPLEMENT_APP(CMyApp)
3046 bool CMyApp::Initialize(int& argc, wxChar** argv)
3048 for (int i = 1; i < argc; i++)
3049 if (!IsSwitchChar(argv[i][0]))
3050 fCommandLine = true;
3054 // wxApp::Initialize will remove environment-specific parameters,
3055 // so it's too early to call ParseParameters yet
3056 for (int i = 1; i < argc; i++)
3058 wxString str = argv[i];
3060 if (str.size() >= 1 && str[0] == '/')
3062 char pszLower[MAX_PATH];
3063 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3067 if (str == "-daemon")
3073 if (fDaemon || fCommandLine)
3075 // Call the original Initialize while suppressing error messages
3076 // and ignoring failure. If unable to initialize GTK, it fails
3077 // near the end so hopefully the last few things don't matter.
3080 wxApp::Initialize(argc, argv);
3089 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3093 pthread_exit((void*)0);
3095 pid_t sid = setsid();
3097 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3104 return wxApp::Initialize(argc, argv);
3107 bool CMyApp::OnInit()
3109 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3110 // Disable malfunctioning wxWidgets debug assertion
3111 extern int g_isPainting;
3112 g_isPainting = 10000;
3114 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3115 SetAppName("Bitcoin");
3117 SetAppName("bitcoin");
3121 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3122 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3123 class wxMBConv_win32 : public wxMBConv
3127 size_t m_minMBCharWidth;
3129 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3130 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3134 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3135 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3136 g_locale.AddCatalogLookupPathPrefix("locale");
3138 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3139 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3141 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3142 g_locale.AddCatalog("bitcoin");
3145 HDC hdc = GetDC(NULL);
3148 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3149 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3150 ReleaseDC(NULL, hdc);
3154 return AppInit(argc, argv);
3157 int CMyApp::OnExit()
3160 return wxApp::OnExit();
3163 bool CMyApp::OnExceptionInMainLoop()
3169 catch (std::exception& e)
3171 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3172 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3178 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3179 wxLogWarning("Unknown exception");
3186 void CMyApp::OnUnhandledException()
3188 // this shows how we may let some exception propagate uncaught
3193 catch (std::exception& e)
3195 PrintException(&e, "CMyApp::OnUnhandledException()");
3196 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3202 PrintException(NULL, "CMyApp::OnUnhandledException()");
3203 wxLogWarning("Unknown exception");
3209 void CMyApp::OnFatalException()
3211 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);