1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
9 #include <boost/filesystem/fstream.hpp>
10 #include <boost/filesystem/convenience.hpp>
16 using namespace boost;
19 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
21 CMainFrame* pframeMain = NULL;
22 CMyTaskBarIcon* ptaskbaricon = NULL;
23 bool fClosedToTray = false;
30 static const double nScaleX = 1.0;
31 static const double nScaleY = 1.0;
41 //////////////////////////////////////////////////////////////////////////////
46 void HandleCtrlA(wxKeyEvent& event)
50 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
51 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
52 textCtrl->SetSelection(-1, -1);
57 //char pszHourFormat[256];
58 //pszHourFormat[0] = '\0';
59 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
60 //return (pszHourFormat[0] != '0');
64 string DateStr(int64 nTime)
66 // Can only be used safely here in the UI
67 return (string)wxDateTime((time_t)nTime).FormatDate();
70 string DateTimeStr(int64 nTime)
72 // Can only be used safely here in the UI
73 wxDateTime datetime((time_t)nTime);
75 return (string)datetime.Format("%x %H:%M");
77 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
80 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
82 // Helper to simplify access to listctrl
84 item.m_itemId = nIndex;
86 item.m_mask = wxLIST_MASK_TEXT;
87 if (!listCtrl->GetItem(item))
89 return item.GetText();
92 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
94 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
95 listCtrl->SetItem(nIndex, 1, str1);
99 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
101 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
102 listCtrl->SetItem(nIndex, 1, str1);
103 listCtrl->SetItem(nIndex, 2, str2);
104 listCtrl->SetItem(nIndex, 3, str3);
105 listCtrl->SetItem(nIndex, 4, str4);
109 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
111 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
112 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
113 listCtrl->SetItem(nIndex, 1, str1);
114 listCtrl->SetItem(nIndex, 2, str2);
115 listCtrl->SetItem(nIndex, 3, str3);
116 listCtrl->SetItem(nIndex, 4, str4);
120 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
122 // Repaint on Windows is more flickery if the colour has ever been set,
123 // so don't want to set it unless it's different. Default colour has
124 // alpha 0 transparent, so our colours don't match using operator==.
125 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
127 c1 = wxColour(0,0,0);
128 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
129 listCtrl->SetItemTextColour(nIndex, colour);
132 void SetSelection(wxListCtrl* listCtrl, int nIndex)
134 int nSize = listCtrl->GetItemCount();
135 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
136 for (int i = 0; i < nSize; i++)
137 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
140 int GetSelection(wxListCtrl* listCtrl)
142 int nSize = listCtrl->GetItemCount();
143 for (int i = 0; i < nSize; i++)
144 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
149 string HtmlEscape(const char* psz, bool fMultiLine=false)
152 for (const char* p = psz; *p; p++)
154 if (*p == '<') len += 4;
155 else if (*p == '>') len += 4;
156 else if (*p == '&') len += 5;
157 else if (*p == '"') len += 6;
158 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
159 else if (*p == '\n' && fMultiLine) len += 5;
165 for (const char* p = psz; *p; p++)
167 if (*p == '<') str += "<";
168 else if (*p == '>') str += ">";
169 else if (*p == '&') str += "&";
170 else if (*p == '"') str += """;
171 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
172 else if (*p == '\n' && fMultiLine) str += "<br>\n";
179 string HtmlEscape(const string& str, bool fMultiLine=false)
181 return HtmlEscape(str.c_str(), fMultiLine);
184 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
186 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
190 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
193 return wxMessageBox(message, caption, style, parent, x, y);
195 if (wxThread::IsMain() || fDaemon)
197 return wxMessageBox(message, caption, style, parent, x, y);
203 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
211 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
213 if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
215 string strMessage = strprintf(
216 _("This transaction is over the size limit. You can still send it for a fee of %s, "
217 "which goes to the nodes that process your transaction and helps to support the network. "
218 "Do you want to pay the fee?"),
219 FormatMoney(nFeeRequired).c_str());
220 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
223 void CalledSetStatusBar(const string& strText, int nField)
225 if (nField == 0 && GetWarnings("statusbar") != "")
227 if (pframeMain && pframeMain->m_statusBar)
228 pframeMain->m_statusBar->SetStatusText(strText, nField);
231 void SetDefaultReceivingAddress(const string& strAddress)
233 // Update main window address and database
234 if (pframeMain == NULL)
236 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
239 if (!AddressToHash160(strAddress, hash160))
241 if (!mapPubKeys.count(hash160))
243 pwalletMain->SetDefaultKey(mapPubKeys[hash160]);
244 pframeMain->m_textCtrlAddress->SetValue(strAddress);
248 bool GetWalletPassphrase()
250 if (pwalletMain->IsLocked())
252 string strWalletPass;
253 strWalletPass.reserve(100);
254 mlock(&strWalletPass[0], strWalletPass.capacity());
256 // obtain current wallet encrypt/decrypt key, from passphrase
257 // Note that the passphrase is not mlock()d during this entry and could potentially
258 // be obtained from disk long after bitcoin has run.
259 strWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
260 _("Passphrase")).ToStdString();
262 if (!strWalletPass.size())
264 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
265 munlock(&strWalletPass[0], strWalletPass.capacity());
266 wxMessageBox(_("Please supply the current wallet decryption passphrase."), "Bitcoin");
270 if (!pwalletMain->Unlock(strWalletPass))
272 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
273 munlock(&strWalletPass[0], strWalletPass.capacity());
274 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin");
277 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
278 munlock(&strWalletPass[0], strWalletPass.capacity());
292 //////////////////////////////////////////////////////////////////////////////
297 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
299 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
301 // Set initially selected page
302 wxNotebookEvent event;
303 event.SetSelection(0);
304 OnNotebookPageChanged(event);
305 m_notebook->ChangeSelection(0);
308 fRefreshListCtrl = false;
309 fRefreshListCtrlRunning = false;
310 fOnSetFocusAddress = false;
312 m_choiceFilter->SetSelection(0);
313 double dResize = nScaleX;
315 SetIcon(wxICON(bitcoin));
316 SetSize(dResize * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
318 SetIcon(bitcoin80_xpm);
319 SetBackgroundColour(m_toolBar->GetBackgroundColour());
320 wxFont fontTmp = m_staticText41->GetFont();
321 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
322 m_staticTextBalance->SetFont(fontTmp);
323 m_staticTextBalance->SetSize(140, 17);
324 // resize to fit ubuntu's huge default font
326 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
328 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
329 m_listCtrl->SetFocus();
330 ptaskbaricon = new CMyTaskBarIcon();
332 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
333 // to their standard places, leaving these menus empty.
334 GetMenuBar()->Remove(2); // remove Help menu
335 GetMenuBar()->Remove(0); // remove File menu
338 // Init column headers
339 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
340 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
346 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
347 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
349 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
350 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
351 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
352 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
353 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
354 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
355 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
359 int pnWidths[3] = { -100, 88, 300 };
361 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
362 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
364 m_statusBar->SetFieldsCount(3, pnWidths);
366 // Fill your address text box
367 vector<unsigned char> vchPubKey;
368 if (CWalletDB(pwalletMain->strWalletFile,"r").ReadDefaultKey(vchPubKey))
369 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
371 // Fill listctrl with wallet transactions
375 CMainFrame::~CMainFrame()
382 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
385 nPage = event.GetSelection();
388 m_listCtrl = m_listCtrlAll;
389 fShowGenerated = true;
391 fShowReceived = true;
393 else if (nPage == SENTRECEIVED)
395 m_listCtrl = m_listCtrlSentReceived;
396 fShowGenerated = false;
398 fShowReceived = true;
400 else if (nPage == SENT)
402 m_listCtrl = m_listCtrlSent;
403 fShowGenerated = false;
405 fShowReceived = false;
407 else if (nPage == RECEIVED)
409 m_listCtrl = m_listCtrlReceived;
410 fShowGenerated = false;
412 fShowReceived = true;
415 m_listCtrl->SetFocus();
418 void CMainFrame::OnClose(wxCloseEvent& event)
420 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
422 // Divert close to minimize
424 fClosedToTray = true;
430 CreateThread(Shutdown, NULL);
434 void CMainFrame::OnIconize(wxIconizeEvent& event)
437 // Hide the task bar button when minimized.
438 // Event is sent when the frame is minimized or restored.
439 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
440 // to get rid of the deprecated warning. Just ignore it.
441 if (!event.Iconized())
442 fClosedToTray = false;
443 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
444 if (GetBoolArg("-minimizetotray")) {
446 // The tray icon sometimes disappears on ubuntu karmic
447 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
448 // Reports of CPU peg on 64-bit linux
449 if (fMinimizeToTray && event.Iconized())
450 fClosedToTray = true;
451 Show(!fClosedToTray);
452 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
453 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
458 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
462 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
463 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
466 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
468 // Hidden columns not resizeable
469 if (event.GetColumn() <= 1 && !fDebug)
475 int CMainFrame::GetSortIndex(const string& strSort)
480 // The wx generic listctrl implementation used on GTK doesn't sort,
481 // so we have to do it ourselves. Remember, we sort in reverse order.
482 // In the wx generic implementation, they store the list of items
483 // in a vector, so indexed lookups are fast, but inserts are slower
484 // the closer they are to the top.
486 int high = m_listCtrl->GetItemCount();
489 int mid = low + ((high - low) / 2);
490 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
499 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)
501 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
502 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
505 if (!fNew && nIndex == -1)
507 string strHash = " " + hashKey.ToString();
508 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
509 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
513 // fNew is for blind insert, only use if you're sure it's new
514 if (fNew || nIndex == -1)
516 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
520 // If sort key changed, must delete and reinsert to make it relocate
521 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
523 m_listCtrl->DeleteItem(nIndex);
524 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
528 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
529 m_listCtrl->SetItem(nIndex, 2, str2);
530 m_listCtrl->SetItem(nIndex, 3, str3);
531 m_listCtrl->SetItem(nIndex, 4, str4);
532 m_listCtrl->SetItem(nIndex, 5, str5);
533 m_listCtrl->SetItem(nIndex, 6, str6);
534 m_listCtrl->SetItemData(nIndex, nData);
535 SetItemTextColour(m_listCtrl, nIndex, colour);
538 bool CMainFrame::DeleteLine(uint256 hashKey)
540 long nData = *(long*)&hashKey;
544 string strHash = " " + hashKey.ToString();
545 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
546 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
550 m_listCtrl->DeleteItem(nIndex);
555 string FormatTxStatus(const CWalletTx& wtx)
560 if (wtx.nLockTime < 500000000)
561 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
563 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
567 int nDepth = wtx.GetDepthInMainChain();
568 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
569 return strprintf(_("%d/offline?"), nDepth);
571 return strprintf(_("%d/unconfirmed"), nDepth);
573 return strprintf(_("%d confirmations"), nDepth);
577 string SingleLine(const string& strIn)
580 bool fOneSpace = false;
581 BOOST_FOREACH(unsigned char c, strIn)
589 if (fOneSpace && !strOut.empty())
598 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
600 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
601 int64 nCredit = wtx.GetCredit(true);
602 int64 nDebit = wtx.GetDebit();
603 int64 nNet = nCredit - nDebit;
604 uint256 hash = wtx.GetHash();
605 string strStatus = FormatTxStatus(wtx);
606 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
607 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
608 map<string, string> mapValue = wtx.mapValue;
609 wtx.nLinesDisplayed = 1;
613 if (wtx.IsCoinBase())
615 // Don't show generated coin until confirmed by at least one block after it
616 // so we don't get the user's hopes up until it looks like it's probably accepted.
618 // It is not an error when generated blocks are not accepted. By design,
619 // some percentage of blocks, like 10% or more, will end up not accepted.
620 // This is the normal mechanism by which the network copes with latency.
622 // We display regular transactions right away before any confirmation
623 // because they can always get into some block eventually. Generated coins
624 // are special because if their block is not accepted, they are not valid.
626 if (wtx.GetDepthInMainChain() < 2)
628 wtx.nLinesDisplayed = 0;
636 // Find the block the tx is in
637 CBlockIndex* pindex = NULL;
638 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
639 if (mi != mapBlockIndex.end())
640 pindex = (*mi).second;
642 // Sort order, unrecorded transactions sort to the top
643 string strSort = strprintf("%010d-%01d-%010u",
644 (pindex ? pindex->nHeight : INT_MAX),
645 (wtx.IsCoinBase() ? 1 : 0),
649 if (nNet > 0 || wtx.IsCoinBase())
654 string strDescription;
655 if (wtx.IsCoinBase())
658 strDescription = _("Generated");
661 int64 nUnmatured = 0;
662 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
663 nUnmatured += pwalletMain->GetCredit(txout);
664 if (wtx.IsInMainChain())
666 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
668 // Check if the block was requested by anyone
669 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
670 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
674 strDescription = _("Generated (not accepted)");
678 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
680 // Received by IP connection
683 if (!mapValue["from"].empty())
684 strDescription += _("From: ") + mapValue["from"];
685 if (!mapValue["message"].empty())
687 if (!strDescription.empty())
688 strDescription += " - ";
689 strDescription += mapValue["message"];
694 // Received by Bitcoin Address
697 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
699 if (pwalletMain->IsMine(txout))
701 vector<unsigned char> vchPubKey;
702 if (ExtractPubKey(txout.scriptPubKey, pwalletMain, vchPubKey))
704 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
706 //strDescription += _("Received payment to ");
707 //strDescription += _("Received with address ");
708 strDescription += _("Received with: ");
709 string strAddress = PubKeyToAddress(vchPubKey);
710 map<string, string>::iterator mi = pwalletMain->mapAddressBook.find(strAddress);
711 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
713 string strLabel = (*mi).second;
714 strDescription += strAddress.substr(0,12) + "... ";
715 strDescription += "(" + strLabel + ")";
718 strDescription += strAddress;
726 string strCredit = FormatMoney(nNet, true);
728 strCredit = "[" + strCredit + "]";
730 InsertLine(fNew, nIndex, hash, strSort, colour,
732 nTime ? DateTimeStr(nTime) : "",
733 SingleLine(strDescription),
739 bool fAllFromMe = true;
740 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
741 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
743 bool fAllToMe = true;
744 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
745 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
747 if (fAllFromMe && fAllToMe)
750 int64 nChange = wtx.GetChange();
751 InsertLine(fNew, nIndex, hash, strSort, colour,
753 nTime ? DateTimeStr(nTime) : "",
754 _("Payment to yourself"),
755 FormatMoney(-(nDebit - nChange), true),
756 FormatMoney(nCredit - nChange, true));
766 int64 nTxFee = nDebit - wtx.GetValueOut();
767 wtx.nLinesDisplayed = 0;
768 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
770 const CTxOut& txout = wtx.vout[nOut];
771 if (pwalletMain->IsMine(txout))
775 if (!mapValue["to"].empty())
778 strAddress = mapValue["to"];
782 // Sent to Bitcoin Address
784 if (ExtractHash160(txout.scriptPubKey, hash160))
785 strAddress = Hash160ToAddress(hash160);
788 string strDescription = _("To: ");
789 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
790 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
791 strDescription += pwalletMain->mapAddressBook[strAddress] + " ";
792 strDescription += strAddress;
793 if (!mapValue["message"].empty())
795 if (!strDescription.empty())
796 strDescription += " - ";
797 strDescription += mapValue["message"];
799 else if (!mapValue["comment"].empty())
801 if (!strDescription.empty())
802 strDescription += " - ";
803 strDescription += mapValue["comment"];
806 int64 nValue = txout.nValue;
813 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
815 nTime ? DateTimeStr(nTime) : "",
816 SingleLine(strDescription),
817 FormatMoney(-nValue, true),
820 wtx.nLinesDisplayed++;
826 // Mixed debit transaction, can't break down payees
828 bool fAllMine = true;
829 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
830 fAllMine = fAllMine && pwalletMain->IsMine(txout);
831 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
832 fAllMine = fAllMine && pwalletMain->IsMine(txin);
834 InsertLine(fNew, nIndex, hash, strSort, colour,
836 nTime ? DateTimeStr(nTime) : "",
838 FormatMoney(nNet, true),
846 void CMainFrame::RefreshListCtrl()
848 fRefreshListCtrl = true;
852 void CMainFrame::OnIdle(wxIdleEvent& event)
854 if (fRefreshListCtrl)
856 // Collect list of wallet transactions and sort newest first
857 bool fEntered = false;
858 vector<pair<unsigned int, uint256> > vSorted;
859 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
861 printf("RefreshListCtrl starting\n");
863 fRefreshListCtrl = false;
864 pwalletMain->vWalletUpdated.clear();
866 // Do the newest transactions first
867 vSorted.reserve(pwalletMain->mapWallet.size());
868 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
870 const CWalletTx& wtx = (*it).second;
871 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
872 vSorted.push_back(make_pair(nTime, (*it).first));
874 m_listCtrl->DeleteAllItems();
879 sort(vSorted.begin(), vSorted.end());
882 for (int i = 0; i < vSorted.size();)
886 bool fEntered = false;
887 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
890 uint256& hash = vSorted[i++].second;
891 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
892 if (mi != pwalletMain->mapWallet.end())
893 InsertTransaction((*mi).second, true);
895 if (!fEntered || i == 100 || i % 500 == 0)
899 printf("RefreshListCtrl done\n");
901 // Update transaction total display
906 // Check for time updates
907 static int64 nLastTime;
908 if (GetTime() > nLastTime + 30)
910 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
912 nLastTime = GetTime();
913 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
915 CWalletTx& wtx = (*it).second;
916 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
917 InsertTransaction(wtx, false);
924 void CMainFrame::RefreshStatusColumn()
927 static CBlockIndex* pindexLastBest;
928 static unsigned int nLastRefreshed;
930 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
931 if (nTop == nLastTop && pindexLastBest == pindexBest)
934 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
937 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
939 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
941 // If no updates, only need to do the part that moved onto the screen
942 if (nStart >= nLastTop && nStart < nLastTop + 100)
943 nStart = nLastTop + 100;
944 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
948 pindexLastBest = pindexBest;
949 nLastRefreshed = nListViewUpdated;
951 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
953 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
954 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
955 if (mi == pwalletMain->mapWallet.end())
957 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
960 CWalletTx& wtx = (*mi).second;
961 if (wtx.IsCoinBase() ||
962 wtx.GetTxTime() != wtx.nTimeDisplayed ||
963 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
965 if (!InsertTransaction(wtx, false, nIndex))
966 m_listCtrl->DeleteItem(nIndex--);
970 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
976 void CMainFrame::OnPaint(wxPaintEvent& event)
987 unsigned int nNeedRepaint = 0;
988 unsigned int nLastRepaint = 0;
989 int64 nLastRepaintTime = 0;
990 int64 nRepaintInterval = 500;
992 void ThreadDelayedRepaint(void* parg)
996 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
998 nLastRepaint = nNeedRepaint;
1001 printf("DelayedRepaint\n");
1003 pframeMain->fRefresh = true;
1004 pframeMain->GetEventHandler()->AddPendingEvent(event);
1007 Sleep(nRepaintInterval);
1011 void MainFrameRepaint()
1013 // This is called by network code that shouldn't access pframeMain
1014 // directly because it could still be running after the UI is closed.
1017 // Don't repaint too often
1018 static int64 nLastRepaintRequest;
1019 if (GetTimeMillis() - nLastRepaintRequest < 100)
1024 nLastRepaintRequest = GetTimeMillis();
1026 printf("MainFrameRepaint\n");
1028 pframeMain->fRefresh = true;
1029 pframeMain->GetEventHandler()->AddPendingEvent(event);
1033 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
1035 // Skip lets the listctrl do the paint, we're just hooking the message
1039 ptaskbaricon->UpdateTooltip();
1044 static int nTransactionCount;
1045 bool fPaintedBalance = false;
1046 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1048 nLastRepaint = nNeedRepaint;
1049 nLastRepaintTime = GetTimeMillis();
1051 // Update listctrl contents
1052 if (!pwalletMain->vWalletUpdated.empty())
1054 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1057 if (m_listCtrl->GetItemCount())
1058 strTop = (string)m_listCtrl->GetItemText(0);
1059 BOOST_FOREACH(uint256 hash, pwalletMain->vWalletUpdated)
1061 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1062 if (mi != pwalletMain->mapWallet.end())
1063 InsertTransaction((*mi).second, false);
1065 pwalletMain->vWalletUpdated.clear();
1066 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1067 m_listCtrl->ScrollList(0, INT_MIN/2);
1072 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1074 fPaintedBalance = true;
1075 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
1077 // Count hidden and multi-line transactions
1078 nTransactionCount = 0;
1079 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1081 CWalletTx& wtx = (*it).second;
1082 nTransactionCount += wtx.nLinesDisplayed;
1086 if (!pwalletMain->vWalletUpdated.empty() || !fPaintedBalance)
1089 // Update status column of visible items only
1090 RefreshStatusColumn();
1092 // Update status bar
1093 static string strPrevWarning;
1094 string strWarning = GetWarnings("statusbar");
1095 if (strWarning != "")
1096 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1097 else if (strPrevWarning != "")
1098 m_statusBar->SetStatusText("", 0);
1099 strPrevWarning = strWarning;
1102 if (fGenerateBitcoins)
1103 strGen = _(" Generating");
1104 if (fGenerateBitcoins && vNodes.empty())
1105 strGen = _("(not connected)");
1106 m_statusBar->SetStatusText(strGen, 1);
1108 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1109 m_statusBar->SetStatusText(strStatus, 2);
1111 // Update receiving address
1112 string strDefaultAddress = PubKeyToAddress(pwalletMain->vchDefaultKey);
1113 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1114 m_textCtrlAddress->SetValue(strDefaultAddress);
1118 void UIThreadCall(boost::function0<void> fn)
1120 // Call this with a function object created with bind.
1121 // bind needs all parameters to match the function's expected types
1122 // and all default parameters specified. Some examples:
1123 // UIThreadCall(bind(wxBell));
1124 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1125 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1128 wxCommandEvent event(wxEVT_UITHREADCALL);
1129 event.SetClientData((void*)new boost::function0<void>(fn));
1130 pframeMain->GetEventHandler()->AddPendingEvent(event);
1134 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1136 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1141 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1147 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1149 event.Check(fGenerateBitcoins);
1152 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1154 // Options->Your Receiving Addresses
1155 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1156 if (!dialog.ShowModal())
1160 void CMainFrame::OnMenuOptionsEncryptWallet(wxCommandEvent& event)
1162 // Options->Encrypt Wallet
1163 if (pwalletMain->IsCrypted())
1165 wxMessageBox(_("Wallet already encrypted."), "Bitcoin", wxOK | wxICON_ERROR);
1169 string strWalletPass;
1170 strWalletPass.reserve(100);
1171 mlock(&strWalletPass[0], strWalletPass.capacity());
1173 // obtain current wallet encrypt/decrypt key, from passphrase
1174 // Note that the passphrase is not mlock()d during this entry and could potentially
1175 // be obtained from disk long after bitcoin has run.
1176 strWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase to the wallet.\nPlease use a passphrase of 10 or more random characters, or eight or more words."),
1177 _("Passphrase")).ToStdString();
1179 if (!strWalletPass.size())
1181 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1182 munlock(&strWalletPass[0], strWalletPass.capacity());
1183 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1187 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)
1190 string strWalletPassTest;
1191 strWalletPassTest.reserve(100);
1192 mlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1193 strWalletPassTest = wxGetPasswordFromUser(_("Please re-enter your new wallet passphrase."),
1194 _("Passphrase")).ToStdString();
1196 if (strWalletPassTest != strWalletPass)
1198 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1199 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1200 munlock(&strWalletPass[0], strWalletPass.capacity());
1201 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1202 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1206 if (!pwalletMain->EncryptWallet(strWalletPass))
1208 fill(strWalletPass.begin(), strWalletPass.end(), '\0');
1209 fill(strWalletPassTest.begin(), strWalletPassTest.end(), '\0');
1210 munlock(&strWalletPass[0], strWalletPass.capacity());
1211 munlock(&strWalletPassTest[0], strWalletPassTest.capacity());
1212 wxMessageBox(_("Wallet encryption failed."), "Bitcoin", wxOK | wxICON_ERROR);
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 Encrypted.\nRemember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."), "Bitcoin");
1222 void CMainFrame::OnMenuOptionsChangeWalletPassphrase(wxCommandEvent& event)
1224 // Options->Change Wallet Encryption Passphrase
1225 if (!pwalletMain->IsCrypted())
1227 wxMessageBox(_("Wallet is unencrypted, please encrypt it first."), "Bitcoin", wxOK | wxICON_ERROR);
1231 string strOldWalletPass;
1232 strOldWalletPass.reserve(100);
1233 mlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1235 // obtain current wallet encrypt/decrypt key, from passphrase
1236 // Note that the passphrase is not mlock()d during this entry and could potentially
1237 // be obtained from disk long after bitcoin has run.
1238 strOldWalletPass = wxGetPasswordFromUser(_("Enter the current passphrase to the wallet."),
1239 _("Passphrase")).ToStdString();
1241 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1243 bool fWasLocked = pwalletMain->IsLocked();
1244 pwalletMain->Lock();
1246 if (!strOldWalletPass.size() || !pwalletMain->Unlock(strOldWalletPass))
1248 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1249 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1250 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
1255 pwalletMain->Lock();
1257 string strNewWalletPass;
1258 strNewWalletPass.reserve(100);
1259 mlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1261 // obtain new wallet encrypt/decrypt key, from passphrase
1262 // Note that the passphrase is not mlock()d during this entry and could potentially
1263 // be obtained from disk long after bitcoin has run.
1264 strNewWalletPass = wxGetPasswordFromUser(_("Enter the new passphrase for the wallet."),
1265 _("Passphrase")).ToStdString();
1267 if (!strNewWalletPass.size())
1269 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1270 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1271 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1272 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1273 wxMessageBox(_("Error: The supplied passphrase was too short."), "Bitcoin", wxOK | wxICON_ERROR);
1277 string strNewWalletPassTest;
1278 strNewWalletPassTest.reserve(100);
1279 mlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1281 // obtain new wallet encrypt/decrypt key, from passphrase
1282 // Note that the passphrase is not mlock()d during this entry and could potentially
1283 // be obtained from disk long after bitcoin has run.
1284 strNewWalletPassTest = wxGetPasswordFromUser(_("Re-enter the new passphrase for the wallet."),
1285 _("Passphrase")).ToStdString();
1287 if (strNewWalletPassTest != strNewWalletPass)
1289 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1290 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1291 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1292 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1293 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1294 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1295 wxMessageBox(_("Error: the supplied passphrases didn't match."), "Bitcoin", wxOK | wxICON_ERROR);
1299 if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
1301 fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
1302 fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
1303 fill(strNewWalletPassTest.begin(), strNewWalletPassTest.end(), '\0');
1304 munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
1305 munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
1306 munlock(&strNewWalletPassTest[0], strNewWalletPassTest.capacity());
1307 wxMessageBox(_("The passphrase entered for the wallet decryption was incorrect."), "Bitcoin", wxOK | wxICON_ERROR);
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(_("Wallet Passphrase Changed."), "Bitcoin");
1320 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1323 COptionsDialog dialog(this);
1327 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1330 CAboutDialog dialog(this);
1334 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1337 CSendDialog dialog(this);
1341 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1343 // Toolbar: Address Book
1344 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1345 if (dialogAddr.ShowModal() == 2)
1348 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1349 dialogSend.ShowModal();
1353 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1355 // Automatically select-all when entering window
1357 m_textCtrlAddress->SetSelection(-1, -1);
1358 fOnSetFocusAddress = true;
1361 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1364 if (fOnSetFocusAddress)
1365 m_textCtrlAddress->SetSelection(-1, -1);
1366 fOnSetFocusAddress = false;
1369 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1372 CGetTextFromUserDialog dialog(this,
1373 _("New Receiving Address"),
1374 _("You should use a new address for each payment you receive.\n\nLabel"),
1376 if (!dialog.ShowModal())
1378 string strName = dialog.GetValue();
1381 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
1383 bool fWasLocked = pwalletMain->IsLocked();
1384 if (!GetWalletPassphrase())
1388 strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
1391 pwalletMain->Lock();
1395 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1396 pwalletMain->SetAddressBookName(strAddress, strName);
1397 SetDefaultReceivingAddress(strAddress);
1400 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1402 // Copy address box to clipboard
1403 if (wxTheClipboard->Open())
1405 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1406 wxTheClipboard->Close();
1410 void CMainFrame::OnListItemActivated(wxListEvent& event)
1412 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1414 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1416 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1417 if (mi == pwalletMain->mapWallet.end())
1419 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1424 CTxDetailsDialog dialog(this, wtx);
1426 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1435 //////////////////////////////////////////////////////////////////////////////
1440 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1443 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1445 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1448 strHTML.reserve(4000);
1449 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1451 int64 nTime = wtx.GetTxTime();
1452 int64 nCredit = wtx.GetCredit();
1453 int64 nDebit = wtx.GetDebit();
1454 int64 nNet = nCredit - nDebit;
1458 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1459 int nRequests = wtx.GetRequestCount();
1460 if (nRequests != -1)
1463 strHTML += _(", has not been successfully broadcast yet");
1464 else if (nRequests == 1)
1465 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1467 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1471 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1477 if (wtx.IsCoinBase())
1479 strHTML += _("<b>Source:</b> Generated<br>");
1481 else if (!wtx.mapValue["from"].empty())
1483 // Online transaction
1484 if (!wtx.mapValue["from"].empty())
1485 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1489 // Offline transaction
1493 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1495 if (pwalletMain->IsMine(txout))
1497 vector<unsigned char> vchPubKey;
1498 if (ExtractPubKey(txout.scriptPubKey, pwalletMain, vchPubKey))
1500 string strAddress = PubKeyToAddress(vchPubKey);
1501 if (pwalletMain->mapAddressBook.count(strAddress))
1503 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1504 strHTML += _("<b>To:</b> ");
1505 strHTML += HtmlEscape(strAddress);
1506 if (!pwalletMain->mapAddressBook[strAddress].empty())
1507 strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[strAddress] + ")";
1509 strHTML += _(" (yours)");
1524 if (!wtx.mapValue["to"].empty())
1526 // Online transaction
1527 strAddress = wtx.mapValue["to"];
1528 strHTML += _("<b>To:</b> ");
1529 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1530 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1531 strHTML += HtmlEscape(strAddress) + "<br>";
1538 if (wtx.IsCoinBase() && nCredit == 0)
1543 int64 nUnmatured = 0;
1544 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1545 nUnmatured += pwalletMain->GetCredit(txout);
1546 strHTML += _("<b>Credit:</b> ");
1547 if (wtx.IsInMainChain())
1548 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1550 strHTML += _("(not accepted)");
1558 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1562 bool fAllFromMe = true;
1563 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1564 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1566 bool fAllToMe = true;
1567 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1568 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1575 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1577 if (pwalletMain->IsMine(txout))
1580 if (wtx.mapValue["to"].empty())
1582 // Offline transaction
1584 if (ExtractHash160(txout.scriptPubKey, hash160))
1586 string strAddress = Hash160ToAddress(hash160);
1587 strHTML += _("<b>To:</b> ");
1588 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1589 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1590 strHTML += strAddress;
1595 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1601 int64 nChange = wtx.GetChange();
1602 int64 nValue = nCredit - nChange;
1603 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1604 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1607 int64 nTxFee = nDebit - wtx.GetValueOut();
1609 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1614 // Mixed debit transaction
1616 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1617 if (pwalletMain->IsMine(txin))
1618 strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1619 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1620 if (pwalletMain->IsMine(txout))
1621 strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1625 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1631 if (!wtx.mapValue["message"].empty())
1632 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1633 if (!wtx.mapValue["comment"].empty())
1634 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1636 if (wtx.IsCoinBase())
1637 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>";
1645 strHTML += "<hr><br>debug print<br><br>";
1646 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1647 if (pwalletMain->IsMine(txin))
1648 strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1649 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1650 if (pwalletMain->IsMine(txout))
1651 strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1653 strHTML += "<br><b>Transaction:</b><br>";
1654 strHTML += HtmlEscape(wtx.ToString(), true);
1656 strHTML += "<br><b>Inputs:</b><br>";
1657 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1659 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1661 COutPoint prevout = txin.prevout;
1662 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1663 if (mi != pwalletMain->mapWallet.end())
1665 const CWalletTx& prev = (*mi).second;
1666 if (prevout.n < prev.vout.size())
1668 strHTML += HtmlEscape(prev.ToString(), true);
1669 strHTML += " " + FormatTxStatus(prev) + ", ";
1670 strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1679 strHTML += "</font></html>";
1680 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1681 m_htmlWin->SetPage(strHTML);
1682 m_buttonOK->SetFocus();
1686 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1696 //////////////////////////////////////////////////////////////////////////////
1702 string StartupShortcutPath()
1704 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1707 bool GetStartOnSystemStartup()
1709 return filesystem::exists(StartupShortcutPath().c_str());
1712 void SetStartOnSystemStartup(bool fAutoStart)
1714 // If the shortcut exists already, remove it for updating
1715 remove(StartupShortcutPath().c_str());
1721 // Get a pointer to the IShellLink interface.
1722 IShellLink* psl = NULL;
1723 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1724 CLSCTX_INPROC_SERVER, IID_IShellLink,
1725 reinterpret_cast<void**>(&psl));
1727 if (SUCCEEDED(hres))
1729 // Get the current executable path
1730 TCHAR pszExePath[MAX_PATH];
1731 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1733 // Set the path to the shortcut target
1734 psl->SetPath(pszExePath);
1735 PathRemoveFileSpec(pszExePath);
1736 psl->SetWorkingDirectory(pszExePath);
1737 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1739 // Query IShellLink for the IPersistFile interface for
1740 // saving the shortcut in persistent storage.
1741 IPersistFile* ppf = NULL;
1742 hres = psl->QueryInterface(IID_IPersistFile,
1743 reinterpret_cast<void**>(&ppf));
1744 if (SUCCEEDED(hres))
1746 WCHAR pwsz[MAX_PATH];
1747 // Ensure that the string is ANSI.
1748 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1749 // Save the link by calling IPersistFile::Save.
1750 hres = ppf->Save(pwsz, TRUE);
1759 #elif defined(__WXGTK__)
1761 // Follow the Desktop Application Autostart Spec:
1762 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1764 boost::filesystem::path GetAutostartDir()
1766 namespace fs = boost::filesystem;
1768 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1769 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1770 char* pszHome = getenv("HOME");
1771 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1775 boost::filesystem::path GetAutostartFilePath()
1777 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1780 bool GetStartOnSystemStartup()
1782 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1783 if (!optionFile.good())
1785 // Scan through file for "Hidden=true":
1787 while (!optionFile.eof())
1789 getline(optionFile, line);
1790 if (line.find("Hidden") != string::npos &&
1791 line.find("true") != string::npos)
1799 void SetStartOnSystemStartup(bool fAutoStart)
1803 unlink(GetAutostartFilePath().native_file_string().c_str());
1807 char pszExePath[MAX_PATH+1];
1808 memset(pszExePath, 0, sizeof(pszExePath));
1809 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1812 boost::filesystem::create_directories(GetAutostartDir());
1814 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1815 if (!optionFile.good())
1817 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1820 // Write a bitcoin.desktop file to the autostart directory:
1821 optionFile << "[Desktop Entry]\n";
1822 optionFile << "Type=Application\n";
1823 optionFile << "Name=Bitcoin\n";
1824 optionFile << "Exec=" << pszExePath << "\n";
1825 optionFile << "Terminal=false\n";
1826 optionFile << "Hidden=false\n";
1832 // TODO: OSX startup stuff; see:
1833 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1835 bool GetStartOnSystemStartup() { return false; }
1836 void SetStartOnSystemStartup(bool fAutoStart) { }
1845 //////////////////////////////////////////////////////////////////////////////
1850 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1852 // Set up list box of page choices
1853 m_listBox->Append(_("Main"));
1854 //m_listBox->Append(_("Test 2"));
1855 m_listBox->SetSelection(0);
1858 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1860 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1862 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1863 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1864 if (!GetBoolArg("-minimizetotray"))
1866 // Minimize to tray is just too buggy on Linux
1867 fMinimizeToTray = false;
1868 m_checkBoxMinimizeToTray->SetValue(false);
1869 m_checkBoxMinimizeToTray->Enable(false);
1870 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1873 #ifdef __WXMAC_OSX__
1874 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1878 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1879 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1880 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1881 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1883 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1885 m_checkBoxUseUPnP->Enable(false);
1886 m_checkBoxUseProxy->SetValue(fUseProxy);
1887 m_textCtrlProxyIP->Enable(fUseProxy);
1888 m_textCtrlProxyPort->Enable(fUseProxy);
1889 m_staticTextProxyIP->Enable(fUseProxy);
1890 m_staticTextProxyPort->Enable(fUseProxy);
1891 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1892 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1894 m_buttonOK->SetFocus();
1897 void COptionsDialog::SelectPage(int nPage)
1899 m_panelMain->Show(nPage == 0);
1900 m_panelTest2->Show(nPage == 1);
1902 m_scrolledWindow->Layout();
1903 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1906 void COptionsDialog::OnListBox(wxCommandEvent& event)
1908 SelectPage(event.GetSelection());
1911 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1914 int64 nTmp = nTransactionFee;
1915 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1916 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1919 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1921 m_textCtrlProxyIP->Enable(event.IsChecked());
1922 m_textCtrlProxyPort->Enable(event.IsChecked());
1923 m_staticTextProxyIP->Enable(event.IsChecked());
1924 m_staticTextProxyPort->Enable(event.IsChecked());
1927 CAddress COptionsDialog::GetProxyAddr()
1929 // Be careful about byte order, addr.ip and addr.port are big endian
1930 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1931 if (addr.ip == INADDR_NONE)
1932 addr.ip = addrProxy.ip;
1933 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1934 addr.port = htons(nPort);
1935 if (nPort <= 0 || nPort > USHRT_MAX)
1936 addr.port = addrProxy.port;
1940 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1943 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1944 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1948 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1950 OnButtonApply(event);
1954 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1959 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1961 CWalletDB walletdb(pwalletMain->strWalletFile);
1963 int64 nPrevTransactionFee = nTransactionFee;
1964 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1965 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1967 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1969 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1970 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1973 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1975 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1976 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1977 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1980 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1982 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1983 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1986 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1988 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1989 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1993 fUseProxy = m_checkBoxUseProxy->GetValue();
1994 walletdb.WriteSetting("fUseProxy", fUseProxy);
1996 addrProxy = GetProxyAddr();
1997 walletdb.WriteSetting("addrProxy", addrProxy);
2005 //////////////////////////////////////////////////////////////////////////////
2010 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
2012 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
2014 // Change (c) into UTF-8 or ANSI copyright symbol
2015 wxString str = m_staticTextMain->GetLabel();
2017 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
2019 str.Replace("(c)", "\xA9");
2021 m_staticTextMain->SetLabel(str);
2023 // Resize on Linux to make the window fit the text.
2024 // The text was wrapped manually rather than using the Wrap setting because
2025 // the wrap would be too small on Linux and it can't be changed at this point.
2026 wxFont fontTmp = m_staticTextMain->GetFont();
2027 if (fontTmp.GetPointSize() > 8);
2028 fontTmp.SetPointSize(8);
2029 m_staticTextMain->SetFont(fontTmp);
2030 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
2032 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2036 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
2046 //////////////////////////////////////////////////////////////////////////////
2051 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
2054 m_textCtrlAddress->SetValue(strAddress);
2055 m_choiceTransferType->SetSelection(0);
2056 m_bitmapCheckMark->Show(false);
2057 fEnabledPrev = true;
2058 m_textCtrlAddress->SetFocus();
2060 //// todo: should add a display of your balance for convenience
2062 wxFont fontTmp = m_staticTextInstructions->GetFont();
2063 if (fontTmp.GetPointSize() > 9);
2064 fontTmp.SetPointSize(9);
2065 m_staticTextInstructions->SetFont(fontTmp);
2068 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2072 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2075 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
2080 SetIcon(wxICON(bitcoin));
2083 // Fixup the tab order
2084 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
2085 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
2089 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
2091 // Reformat the amount
2093 if (m_textCtrlAmount->GetValue().Trim().empty())
2096 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
2097 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
2100 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
2102 // Open address book
2103 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
2104 if (dialog.ShowModal())
2105 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
2108 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
2110 // Copy clipboard to address box
2111 if (wxTheClipboard->Open())
2113 if (wxTheClipboard->IsSupported(wxDF_TEXT))
2115 wxTextDataObject data;
2116 wxTheClipboard->GetData(data);
2117 m_textCtrlAddress->SetValue(data.GetText());
2119 wxTheClipboard->Close();
2123 void CSendDialog::OnButtonSend(wxCommandEvent& event)
2125 static CCriticalSection cs_sendlock;
2126 TRY_CRITICAL_BLOCK(cs_sendlock)
2129 string strAddress = (string)m_textCtrlAddress->GetValue();
2133 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
2135 wxMessageBox(_("Error in amount "), _("Send Coins"));
2138 if (nValue > pwalletMain->GetBalance())
2140 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
2143 if (nValue + nTransactionFee > pwalletMain->GetBalance())
2145 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
2149 // Parse bitcoin address
2151 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
2153 if (fBitcoinAddress)
2155 CRITICAL_BLOCK(cs_main)
2156 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2158 bool fWasLocked = pwalletMain->IsLocked();
2159 if (!GetWalletPassphrase())
2162 // Send to bitcoin address
2163 CScript scriptPubKey;
2164 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
2166 string strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
2168 wxMessageBox(_("Payment sent "), _("Sending..."));
2169 else if (strError == "ABORTED")
2172 pwalletMain->Lock();
2173 return; // leave send dialog open
2177 wxMessageBox(strError + " ", _("Sending..."));
2180 pwalletMain->Lock();
2185 pwalletMain->Lock();
2191 CAddress addr(strAddress);
2192 if (!addr.IsValid())
2194 wxMessageBox(_("Invalid address "), _("Send Coins"));
2199 wtx.mapValue["to"] = strAddress;
2201 // Send to IP address
2202 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2203 if (!pdialog->ShowModal())
2207 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2208 if (!pwalletMain->mapAddressBook.count(strAddress))
2209 pwalletMain->SetAddressBookName(strAddress, "");
2215 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2226 //////////////////////////////////////////////////////////////////////////////
2231 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
2236 start = wxDateTime::UNow();
2237 memset(pszStatus, 0, sizeof(pszStatus));
2244 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2246 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2249 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2250 m_textCtrlStatus->SetValue("");
2252 CreateThread(SendingDialogStartTransfer, this);
2255 CSendingDialog::~CSendingDialog()
2257 printf("~CSendingDialog()\n");
2260 void CSendingDialog::Close()
2262 // Last one out turn out the lights.
2263 // fWorkDone signals that work side is done and UI thread should call destroy.
2264 // fUIDone signals that UI window has closed and work thread should call destroy.
2265 // This allows the window to disappear and end modality when cancelled
2266 // without making the user wait for ConnectNode to return. The dialog object
2267 // hangs around in the background until the work thread exits.
2278 void CSendingDialog::OnClose(wxCloseEvent& event)
2280 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2287 wxCommandEvent cmdevent;
2288 OnButtonCancel(cmdevent);
2292 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2298 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2304 void CSendingDialog::OnPaint(wxPaintEvent& event)
2307 if (strlen(pszStatus) > 130)
2308 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2310 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2311 m_staticTextSending->SetFocus();
2313 m_buttonCancel->Enable(false);
2316 m_buttonOK->Enable(true);
2317 m_buttonOK->SetFocus();
2318 m_buttonCancel->Enable(false);
2320 if (fAbort && fCanCancel && IsShown())
2322 strcpy(pszStatus, _("CANCELLED"));
2323 m_buttonOK->Enable(true);
2324 m_buttonOK->SetFocus();
2325 m_buttonCancel->Enable(false);
2326 m_buttonCancel->SetLabel(_("Cancelled"));
2328 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2334 // Everything from here on is not in the UI thread and must only communicate
2335 // with the rest of the dialog through variables and calling repaint.
2338 void CSendingDialog::Repaint()
2342 GetEventHandler()->AddPendingEvent(event);
2345 bool CSendingDialog::Status()
2352 if (fAbort && fCanCancel)
2354 memset(pszStatus, 0, 10);
2355 strcpy(pszStatus, _("CANCELLED"));
2363 bool CSendingDialog::Status(const string& str)
2368 // This can be read by the UI thread at any time,
2369 // so copy in a way that can be read cleanly at all times.
2370 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2371 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2377 bool CSendingDialog::Error(const string& str)
2381 Status(string(_("Error: ")) + str);
2385 void SendingDialogStartTransfer(void* parg)
2387 ((CSendingDialog*)parg)->StartTransfer();
2390 void CSendingDialog::StartTransfer()
2392 // Make sure we have enough money
2393 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2395 Error(_("Insufficient funds"));
2399 // We may have connected already for product details
2400 if (!Status(_("Connecting...")))
2402 CNode* pnode = ConnectNode(addr, 15 * 60);
2405 Error(_("Unable to connect"));
2409 // Send order to seller, with response going to OnReply2 via event handler
2410 if (!Status(_("Requesting public key...")))
2412 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2415 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2417 ((CSendingDialog*)parg)->OnReply2(vRecv);
2420 void CSendingDialog::OnReply2(CDataStream& vRecv)
2422 if (!Status(_("Received public key...")))
2425 CScript scriptPubKey;
2434 vRecv >> strMessage;
2436 Error(_("Recipient is not accepting transactions sent by IP address"));
2438 Error(_("Transfer was not accepted"));
2439 //// todo: enlarge the window and enable a hidden white box to put seller's message
2442 vRecv >> scriptPubKey;
2446 //// what do we want to do about this?
2447 Error(_("Invalid response received"));
2451 // Pause to give the user a chance to cancel
2452 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2459 CRITICAL_BLOCK(cs_main)
2462 if (!Status(_("Creating transaction...")))
2464 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2466 Error(_("Insufficient funds"));
2470 CReserveKey reservekey(pwalletMain);
2472 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2474 bool fWasLocked = pwalletMain->IsLocked();
2475 if (!GetWalletPassphrase())
2478 if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2480 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2481 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()));
2483 Error(_("Transaction creation failed"));
2488 pwalletMain->Lock();
2492 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2494 Error(_("Transaction aborted"));
2498 // Make sure we're still connected
2499 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2502 Error(_("Lost connection, transaction cancelled"));
2506 // Last chance to cancel
2518 if (!Status(_("Sending payment...")))
2522 if (!pwalletMain->CommitTransaction(wtx, reservekey))
2524 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."));
2528 // Send payment tx to seller, with response going to OnReply3 via event handler
2529 CWalletTx wtxSend = wtx;
2530 wtxSend.fFromMe = false;
2531 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2533 Status(_("Waiting for confirmation..."));
2538 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2540 ((CSendingDialog*)parg)->OnReply3(vRecv);
2543 void CSendingDialog::OnReply3(CDataStream& vRecv)
2551 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2552 "The transaction is recorded and will credit to the recipient,\n"
2553 "but the comment information will be blank."));
2559 //// what do we want to do about this?
2560 Error(_("Payment was sent, but an invalid response was received"));
2566 Status(_("Payment completed"));
2574 //////////////////////////////////////////////////////////////////////////////
2576 // CAddressBookDialog
2579 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2582 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2585 // Set initially selected page
2586 wxNotebookEvent event;
2587 event.SetSelection(nPageIn);
2588 OnNotebookPageChanged(event);
2589 m_notebook->ChangeSelection(nPageIn);
2591 fDuringSend = fDuringSendIn;
2593 m_buttonCancel->Show(false);
2596 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2598 wxIcon iconAddressBook;
2599 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2600 SetIcon(iconAddressBook);
2604 SetIcon(wxICON(bitcoin));
2607 // Init column headers
2608 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2609 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2610 m_listCtrlSending->SetFocus();
2611 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2612 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2613 m_listCtrlReceiving->SetFocus();
2615 // Fill listctrl with address book data
2616 CRITICAL_BLOCK(pwalletMain->cs_KeyStore)
2617 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2619 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2620 BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook)
2622 string strAddress = item.first;
2623 string strName = item.second;
2625 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2626 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2627 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2628 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2629 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2634 wxString CAddressBookDialog::GetSelectedAddress()
2636 int nIndex = GetSelection(m_listCtrl);
2639 return GetItemText(m_listCtrl, nIndex, 1);
2642 wxString CAddressBookDialog::GetSelectedSendingAddress()
2644 int nIndex = GetSelection(m_listCtrlSending);
2647 return GetItemText(m_listCtrlSending, nIndex, 1);
2650 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2652 int nIndex = GetSelection(m_listCtrlReceiving);
2655 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2658 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2661 nPage = event.GetSelection();
2662 if (nPage == SENDING)
2663 m_listCtrl = m_listCtrlSending;
2664 else if (nPage == RECEIVING)
2665 m_listCtrl = m_listCtrlReceiving;
2666 m_buttonDelete->Show(nPage == SENDING);
2667 m_buttonCopy->Show(nPage == RECEIVING);
2669 m_listCtrl->SetFocus();
2672 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2674 // Update address book with edited name
2676 if (event.IsEditCancelled())
2678 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2679 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2680 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2681 pframeMain->RefreshListCtrl();
2684 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2687 if (nPage == RECEIVING)
2688 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2691 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2696 // Doubleclick returns selection
2697 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2701 // Doubleclick edits item
2702 wxCommandEvent event2;
2703 OnButtonEdit(event2);
2706 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2708 if (nPage != SENDING)
2710 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2712 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2714 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2715 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2716 pwalletMain->DelAddressBookName(strAddress);
2717 m_listCtrl->DeleteItem(nIndex);
2720 pframeMain->RefreshListCtrl();
2723 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2725 // Copy address box to clipboard
2726 if (wxTheClipboard->Open())
2728 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2729 wxTheClipboard->Close();
2733 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2736 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2738 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2742 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2744 int nIndex = GetSelection(m_listCtrl);
2747 string strName = (string)m_listCtrl->GetItemText(nIndex);
2748 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2749 string strAddressOrg = strAddress;
2751 if (nPage == SENDING)
2753 // Ask name and address
2756 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2757 if (!dialog.ShowModal())
2759 strName = dialog.GetValue1();
2760 strAddress = dialog.GetValue2();
2762 while (CheckIfMine(strAddress, _("Edit Address")));
2765 else if (nPage == RECEIVING)
2768 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2769 if (!dialog.ShowModal())
2771 strName = dialog.GetValue();
2775 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2777 if (strAddress != strAddressOrg)
2778 pwalletMain->DelAddressBookName(strAddressOrg);
2779 pwalletMain->SetAddressBookName(strAddress, strName);
2781 m_listCtrl->SetItem(nIndex, 1, strAddress);
2782 m_listCtrl->SetItemText(nIndex, strName);
2783 pframeMain->RefreshListCtrl();
2786 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2791 if (nPage == SENDING)
2793 // Ask name and address
2796 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2797 if (!dialog.ShowModal())
2799 strName = dialog.GetValue1();
2800 strAddress = dialog.GetValue2();
2802 while (CheckIfMine(strAddress, _("Add Address")));
2804 else if (nPage == RECEIVING)
2807 CGetTextFromUserDialog dialog(this,
2808 _("New Receiving Address"),
2809 _("You should use a new address for each payment you receive.\n\nLabel"),
2811 if (!dialog.ShowModal())
2813 strName = dialog.GetValue();
2815 CRITICAL_BLOCK(pwalletMain->cs_vMasterKey)
2817 bool fWasLocked = pwalletMain->IsLocked();
2818 if (!GetWalletPassphrase())
2822 strAddress = PubKeyToAddress(pwalletMain->GetOrReuseKeyFromPool());
2825 pwalletMain->Lock();
2829 // Add to list and select it
2830 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2831 pwalletMain->SetAddressBookName(strAddress, strName);
2832 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2833 SetSelection(m_listCtrl, nIndex);
2834 m_listCtrl->SetFocus();
2835 if (nPage == SENDING)
2836 pframeMain->RefreshListCtrl();
2839 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2842 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2845 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2851 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2862 //////////////////////////////////////////////////////////////////////////////
2869 ID_TASKBAR_RESTORE = 10001,
2872 ID_TASKBAR_GENERATE,
2876 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2877 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2878 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2879 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2880 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2881 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2882 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2885 void CMyTaskBarIcon::Show(bool fShow)
2887 static char pszPrevTip[200];
2890 string strTooltip = _("Bitcoin");
2891 if (fGenerateBitcoins)
2892 strTooltip = _("Bitcoin - Generating");
2893 if (fGenerateBitcoins && vNodes.empty())
2894 strTooltip = _("Bitcoin - (not connected)");
2896 // Optimization, only update when changed, using char array to be reentrant
2897 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2899 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2901 // somehow it'll choose the wrong size and scale it down if
2902 // we use the main icon, so we hand it one with only 16x16
2903 SetIcon(wxICON(favicon), strTooltip);
2905 SetIcon(bitcoin80_xpm, strTooltip);
2911 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2916 void CMyTaskBarIcon::Hide()
2921 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2926 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2931 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2934 CSendDialog dialog(pframeMain);
2938 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2940 // Since it's modal, get the main window to do it
2941 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2942 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2945 void CMyTaskBarIcon::Restore()
2948 wxIconizeEvent event(0, false);
2949 pframeMain->GetEventHandler()->AddPendingEvent(event);
2950 pframeMain->Iconize(false);
2951 pframeMain->Raise();
2954 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2956 event.Check(fGenerateBitcoins);
2959 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2961 pframeMain->Close(true);
2964 void CMyTaskBarIcon::UpdateTooltip()
2966 if (IsIconInstalled())
2970 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2972 wxMenu* pmenu = new wxMenu;
2973 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2974 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2975 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2976 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2977 pmenu->AppendSeparator();
2978 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2988 //////////////////////////////////////////////////////////////////////////////
2993 void CreateMainWindow()
2995 pframeMain = new CMainFrame(NULL);
2996 if (GetBoolArg("-min"))
2997 pframeMain->Iconize(true);
2998 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2999 if (!GetBoolArg("-minimizetotray"))
3000 fMinimizeToTray = false;
3002 pframeMain->Show(true); // have to show first to get taskbar button to hide
3003 if (fMinimizeToTray && pframeMain->IsIconized())
3004 fClosedToTray = true;
3005 pframeMain->Show(!fClosedToTray);
3006 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
3007 CreateThread(ThreadDelayedRepaint, NULL);
3011 // Define a new application
3012 class CMyApp : public wxApp
3021 // Hook Initialize so we can start without GUI
3022 virtual bool Initialize(int& argc, wxChar** argv);
3024 // 2nd-level exception handling: we get all the exceptions occurring in any
3025 // event handler here
3026 virtual bool OnExceptionInMainLoop();
3028 // 3rd, and final, level exception handling: whenever an unhandled
3029 // exception is caught, this function is called
3030 virtual void OnUnhandledException();
3032 // and now for something different: this function is called in case of a
3033 // crash (e.g. dereferencing null pointer, division by 0, ...)
3034 virtual void OnFatalException();
3037 IMPLEMENT_APP(CMyApp)
3039 bool CMyApp::Initialize(int& argc, wxChar** argv)
3041 for (int i = 1; i < argc; i++)
3042 if (!IsSwitchChar(argv[i][0]))
3043 fCommandLine = true;
3047 // wxApp::Initialize will remove environment-specific parameters,
3048 // so it's too early to call ParseParameters yet
3049 for (int i = 1; i < argc; i++)
3051 wxString str = argv[i];
3053 if (str.size() >= 1 && str[0] == '/')
3055 char pszLower[MAX_PATH];
3056 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
3060 if (str == "-daemon")
3066 if (fDaemon || fCommandLine)
3068 // Call the original Initialize while suppressing error messages
3069 // and ignoring failure. If unable to initialize GTK, it fails
3070 // near the end so hopefully the last few things don't matter.
3073 wxApp::Initialize(argc, argv);
3082 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
3086 pthread_exit((void*)0);
3088 pid_t sid = setsid();
3090 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
3097 return wxApp::Initialize(argc, argv);
3100 bool CMyApp::OnInit()
3102 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
3103 // Disable malfunctioning wxWidgets debug assertion
3104 extern int g_isPainting;
3105 g_isPainting = 10000;
3107 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
3108 SetAppName("Bitcoin");
3110 SetAppName("bitcoin");
3114 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
3115 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
3116 class wxMBConv_win32 : public wxMBConv
3120 size_t m_minMBCharWidth;
3122 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
3123 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
3127 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
3128 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
3129 g_locale.AddCatalogLookupPathPrefix("locale");
3131 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
3132 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
3134 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
3135 g_locale.AddCatalog("bitcoin");
3138 HDC hdc = GetDC(NULL);
3141 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
3142 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
3143 ReleaseDC(NULL, hdc);
3147 return AppInit(argc, argv);
3150 int CMyApp::OnExit()
3153 return wxApp::OnExit();
3156 bool CMyApp::OnExceptionInMainLoop()
3162 catch (std::exception& e)
3164 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
3165 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3171 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
3172 wxLogWarning("Unknown exception");
3179 void CMyApp::OnUnhandledException()
3181 // this shows how we may let some exception propagate uncaught
3186 catch (std::exception& e)
3188 PrintException(&e, "CMyApp::OnUnhandledException()");
3189 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
3195 PrintException(NULL, "CMyApp::OnUnhandledException()");
3196 wxLogWarning("Unknown exception");
3202 void CMyApp::OnFatalException()
3204 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);