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.
8 #include <boost/filesystem/fstream.hpp>
9 #include <boost/filesystem/convenience.hpp>
15 using namespace boost;
18 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
20 CMainFrame* pframeMain = NULL;
21 CMyTaskBarIcon* ptaskbaricon = NULL;
22 bool fClosedToTray = false;
29 static const double nScaleX = 1.0;
30 static const double nScaleY = 1.0;
40 //////////////////////////////////////////////////////////////////////////////
45 void HandleCtrlA(wxKeyEvent& event)
49 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
50 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
51 textCtrl->SetSelection(-1, -1);
56 //char pszHourFormat[256];
57 //pszHourFormat[0] = '\0';
58 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
59 //return (pszHourFormat[0] != '0');
63 string DateStr(int64 nTime)
65 // Can only be used safely here in the UI
66 return (string)wxDateTime((time_t)nTime).FormatDate();
69 string DateTimeStr(int64 nTime)
71 // Can only be used safely here in the UI
72 wxDateTime datetime((time_t)nTime);
74 return (string)datetime.Format("%x %H:%M");
76 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
79 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
81 // Helper to simplify access to listctrl
83 item.m_itemId = nIndex;
85 item.m_mask = wxLIST_MASK_TEXT;
86 if (!listCtrl->GetItem(item))
88 return item.GetText();
91 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
93 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
94 listCtrl->SetItem(nIndex, 1, str1);
98 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
100 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
101 listCtrl->SetItem(nIndex, 1, str1);
102 listCtrl->SetItem(nIndex, 2, str2);
103 listCtrl->SetItem(nIndex, 3, str3);
104 listCtrl->SetItem(nIndex, 4, str4);
108 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
110 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
111 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
112 listCtrl->SetItem(nIndex, 1, str1);
113 listCtrl->SetItem(nIndex, 2, str2);
114 listCtrl->SetItem(nIndex, 3, str3);
115 listCtrl->SetItem(nIndex, 4, str4);
119 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
121 // Repaint on Windows is more flickery if the colour has ever been set,
122 // so don't want to set it unless it's different. Default colour has
123 // alpha 0 transparent, so our colours don't match using operator==.
124 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
126 c1 = wxColour(0,0,0);
127 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
128 listCtrl->SetItemTextColour(nIndex, colour);
131 void SetSelection(wxListCtrl* listCtrl, int nIndex)
133 int nSize = listCtrl->GetItemCount();
134 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
135 for (int i = 0; i < nSize; i++)
136 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
139 int GetSelection(wxListCtrl* listCtrl)
141 int nSize = listCtrl->GetItemCount();
142 for (int i = 0; i < nSize; i++)
143 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
148 string HtmlEscape(const char* psz, bool fMultiLine=false)
151 for (const char* p = psz; *p; p++)
153 if (*p == '<') len += 4;
154 else if (*p == '>') len += 4;
155 else if (*p == '&') len += 5;
156 else if (*p == '"') len += 6;
157 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
158 else if (*p == '\n' && fMultiLine) len += 5;
164 for (const char* p = psz; *p; p++)
166 if (*p == '<') str += "<";
167 else if (*p == '>') str += ">";
168 else if (*p == '&') str += "&";
169 else if (*p == '"') str += """;
170 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
171 else if (*p == '\n' && fMultiLine) str += "<br>\n";
178 string HtmlEscape(const string& str, bool fMultiLine=false)
180 return HtmlEscape(str.c_str(), fMultiLine);
183 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
185 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
189 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
192 return wxMessageBox(message, caption, style, parent, x, y);
194 if (wxThread::IsMain() || fDaemon)
196 return wxMessageBox(message, caption, style, parent, x, y);
202 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
210 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
212 if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
214 string strMessage = strprintf(
215 _("This transaction is over the size limit. You can still send it for a fee of %s, "
216 "which goes to the nodes that process your transaction and helps to support the network. "
217 "Do you want to pay the fee?"),
218 FormatMoney(nFeeRequired).c_str());
219 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
222 void CalledSetStatusBar(const string& strText, int nField)
224 if (nField == 0 && GetWarnings("statusbar") != "")
226 if (pframeMain && pframeMain->m_statusBar)
227 pframeMain->m_statusBar->SetStatusText(strText, nField);
230 void SetDefaultReceivingAddress(const string& strAddress)
232 // Update main window address and database
233 if (pframeMain == NULL)
235 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
238 if (!AddressToHash160(strAddress, hash160))
240 if (!mapPubKeys.count(hash160))
242 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
243 pframeMain->m_textCtrlAddress->SetValue(strAddress);
256 //////////////////////////////////////////////////////////////////////////////
261 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
263 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
265 // Set initially selected page
266 wxNotebookEvent event;
267 event.SetSelection(0);
268 OnNotebookPageChanged(event);
269 m_notebook->ChangeSelection(0);
272 fRefreshListCtrl = false;
273 fRefreshListCtrlRunning = false;
274 fOnSetFocusAddress = false;
276 m_choiceFilter->SetSelection(0);
277 double dResize = nScaleX;
279 SetIcon(wxICON(bitcoin));
280 SetSize(dResize * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
282 SetIcon(bitcoin80_xpm);
283 SetBackgroundColour(m_toolBar->GetBackgroundColour());
284 wxFont fontTmp = m_staticText41->GetFont();
285 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
286 m_staticTextBalance->SetFont(fontTmp);
287 m_staticTextBalance->SetSize(140, 17);
288 // resize to fit ubuntu's huge default font
290 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
292 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
293 m_listCtrl->SetFocus();
294 ptaskbaricon = new CMyTaskBarIcon();
296 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
297 // to their standard places, leaving these menus empty.
298 GetMenuBar()->Remove(2); // remove Help menu
299 GetMenuBar()->Remove(0); // remove File menu
302 // Init column headers
303 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
304 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
310 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
311 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
313 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
314 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
315 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
316 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
317 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
318 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
319 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
323 int pnWidths[3] = { -100, 88, 300 };
325 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
326 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
328 m_statusBar->SetFieldsCount(3, pnWidths);
330 // Fill your address text box
331 vector<unsigned char> vchPubKey;
332 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
333 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
335 // Fill listctrl with wallet transactions
339 CMainFrame::~CMainFrame()
346 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
349 nPage = event.GetSelection();
352 m_listCtrl = m_listCtrlAll;
353 fShowGenerated = true;
355 fShowReceived = true;
357 else if (nPage == SENTRECEIVED)
359 m_listCtrl = m_listCtrlSentReceived;
360 fShowGenerated = false;
362 fShowReceived = true;
364 else if (nPage == SENT)
366 m_listCtrl = m_listCtrlSent;
367 fShowGenerated = false;
369 fShowReceived = false;
371 else if (nPage == RECEIVED)
373 m_listCtrl = m_listCtrlReceived;
374 fShowGenerated = false;
376 fShowReceived = true;
379 m_listCtrl->SetFocus();
382 void CMainFrame::OnClose(wxCloseEvent& event)
384 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
386 // Divert close to minimize
388 fClosedToTray = true;
394 CreateThread(Shutdown, NULL);
398 void CMainFrame::OnIconize(wxIconizeEvent& event)
401 // Hide the task bar button when minimized.
402 // Event is sent when the frame is minimized or restored.
403 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
404 // to get rid of the deprecated warning. Just ignore it.
405 if (!event.Iconized())
406 fClosedToTray = false;
407 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
408 if (GetBoolArg("-minimizetotray")) {
410 // The tray icon sometimes disappears on ubuntu karmic
411 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
412 // Reports of CPU peg on 64-bit linux
413 if (fMinimizeToTray && event.Iconized())
414 fClosedToTray = true;
415 Show(!fClosedToTray);
416 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
417 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
422 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
426 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
427 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
430 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
432 // Hidden columns not resizeable
433 if (event.GetColumn() <= 1 && !fDebug)
439 int CMainFrame::GetSortIndex(const string& strSort)
444 // The wx generic listctrl implementation used on GTK doesn't sort,
445 // so we have to do it ourselves. Remember, we sort in reverse order.
446 // In the wx generic implementation, they store the list of items
447 // in a vector, so indexed lookups are fast, but inserts are slower
448 // the closer they are to the top.
450 int high = m_listCtrl->GetItemCount();
453 int mid = low + ((high - low) / 2);
454 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
463 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)
465 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
466 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
469 if (!fNew && nIndex == -1)
471 string strHash = " " + hashKey.ToString();
472 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
473 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
477 // fNew is for blind insert, only use if you're sure it's new
478 if (fNew || nIndex == -1)
480 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
484 // If sort key changed, must delete and reinsert to make it relocate
485 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
487 m_listCtrl->DeleteItem(nIndex);
488 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
492 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
493 m_listCtrl->SetItem(nIndex, 2, str2);
494 m_listCtrl->SetItem(nIndex, 3, str3);
495 m_listCtrl->SetItem(nIndex, 4, str4);
496 m_listCtrl->SetItem(nIndex, 5, str5);
497 m_listCtrl->SetItem(nIndex, 6, str6);
498 m_listCtrl->SetItemData(nIndex, nData);
499 SetItemTextColour(m_listCtrl, nIndex, colour);
502 bool CMainFrame::DeleteLine(uint256 hashKey)
504 long nData = *(long*)&hashKey;
508 string strHash = " " + hashKey.ToString();
509 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
510 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
514 m_listCtrl->DeleteItem(nIndex);
519 string FormatTxStatus(const CWalletTx& wtx)
524 if (wtx.nLockTime < 500000000)
525 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
527 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
531 int nDepth = wtx.GetDepthInMainChain();
532 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
533 return strprintf(_("%d/offline?"), nDepth);
535 return strprintf(_("%d/unconfirmed"), nDepth);
537 return strprintf(_("%d confirmations"), nDepth);
541 string SingleLine(const string& strIn)
544 bool fOneSpace = false;
545 BOOST_FOREACH(unsigned char c, strIn)
553 if (fOneSpace && !strOut.empty())
562 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
564 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
565 int64 nCredit = wtx.GetCredit(true);
566 int64 nDebit = wtx.GetDebit();
567 int64 nNet = nCredit - nDebit;
568 uint256 hash = wtx.GetHash();
569 string strStatus = FormatTxStatus(wtx);
570 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
571 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
572 map<string, string> mapValue = wtx.mapValue;
573 wtx.nLinesDisplayed = 1;
577 if (wtx.IsCoinBase())
579 // Don't show generated coin until confirmed by at least one block after it
580 // so we don't get the user's hopes up until it looks like it's probably accepted.
582 // It is not an error when generated blocks are not accepted. By design,
583 // some percentage of blocks, like 10% or more, will end up not accepted.
584 // This is the normal mechanism by which the network copes with latency.
586 // We display regular transactions right away before any confirmation
587 // because they can always get into some block eventually. Generated coins
588 // are special because if their block is not accepted, they are not valid.
590 if (wtx.GetDepthInMainChain() < 2)
592 wtx.nLinesDisplayed = 0;
600 // Find the block the tx is in
601 CBlockIndex* pindex = NULL;
602 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
603 if (mi != mapBlockIndex.end())
604 pindex = (*mi).second;
606 // Sort order, unrecorded transactions sort to the top
607 string strSort = strprintf("%010d-%01d-%010u",
608 (pindex ? pindex->nHeight : INT_MAX),
609 (wtx.IsCoinBase() ? 1 : 0),
613 if (nNet > 0 || wtx.IsCoinBase())
618 string strDescription;
619 if (wtx.IsCoinBase())
622 strDescription = _("Generated");
625 int64 nUnmatured = 0;
626 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
627 nUnmatured += txout.GetCredit();
628 if (wtx.IsInMainChain())
630 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
632 // Check if the block was requested by anyone
633 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
634 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
638 strDescription = _("Generated (not accepted)");
642 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
644 // Received by IP connection
647 if (!mapValue["from"].empty())
648 strDescription += _("From: ") + mapValue["from"];
649 if (!mapValue["message"].empty())
651 if (!strDescription.empty())
652 strDescription += " - ";
653 strDescription += mapValue["message"];
658 // Received by Bitcoin Address
661 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
665 vector<unsigned char> vchPubKey;
666 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
668 CRITICAL_BLOCK(cs_mapAddressBook)
670 //strDescription += _("Received payment to ");
671 //strDescription += _("Received with address ");
672 strDescription += _("Received with: ");
673 string strAddress = PubKeyToAddress(vchPubKey);
674 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
675 if (mi != mapAddressBook.end() && !(*mi).second.empty())
677 string strLabel = (*mi).second;
678 strDescription += strAddress.substr(0,12) + "... ";
679 strDescription += "(" + strLabel + ")";
682 strDescription += strAddress;
690 string strCredit = FormatMoney(nNet, true);
692 strCredit = "[" + strCredit + "]";
694 InsertLine(fNew, nIndex, hash, strSort, colour,
696 nTime ? DateTimeStr(nTime) : "",
697 SingleLine(strDescription),
703 bool fAllFromMe = true;
704 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
705 fAllFromMe = fAllFromMe && txin.IsMine();
707 bool fAllToMe = true;
708 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
709 fAllToMe = fAllToMe && txout.IsMine();
711 if (fAllFromMe && fAllToMe)
714 int64 nChange = wtx.GetChange();
715 InsertLine(fNew, nIndex, hash, strSort, colour,
717 nTime ? DateTimeStr(nTime) : "",
718 _("Payment to yourself"),
719 FormatMoney(-(nDebit - nChange), true),
720 FormatMoney(nCredit - nChange, true));
730 int64 nTxFee = nDebit - wtx.GetValueOut();
731 wtx.nLinesDisplayed = 0;
732 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
734 const CTxOut& txout = wtx.vout[nOut];
739 if (!mapValue["to"].empty())
742 strAddress = mapValue["to"];
746 // Sent to Bitcoin Address
748 if (ExtractHash160(txout.scriptPubKey, hash160))
749 strAddress = Hash160ToAddress(hash160);
752 string strDescription = _("To: ");
753 CRITICAL_BLOCK(cs_mapAddressBook)
754 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
755 strDescription += mapAddressBook[strAddress] + " ";
756 strDescription += strAddress;
757 if (!mapValue["message"].empty())
759 if (!strDescription.empty())
760 strDescription += " - ";
761 strDescription += mapValue["message"];
763 else if (!mapValue["comment"].empty())
765 if (!strDescription.empty())
766 strDescription += " - ";
767 strDescription += mapValue["comment"];
770 int64 nValue = txout.nValue;
777 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
779 nTime ? DateTimeStr(nTime) : "",
780 SingleLine(strDescription),
781 FormatMoney(-nValue, true),
784 wtx.nLinesDisplayed++;
790 // Mixed debit transaction, can't break down payees
792 bool fAllMine = true;
793 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
794 fAllMine = fAllMine && txout.IsMine();
795 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
796 fAllMine = fAllMine && txin.IsMine();
798 InsertLine(fNew, nIndex, hash, strSort, colour,
800 nTime ? DateTimeStr(nTime) : "",
802 FormatMoney(nNet, true),
810 void CMainFrame::RefreshListCtrl()
812 fRefreshListCtrl = true;
816 void CMainFrame::OnIdle(wxIdleEvent& event)
818 if (fRefreshListCtrl)
820 // Collect list of wallet transactions and sort newest first
821 bool fEntered = false;
822 vector<pair<unsigned int, uint256> > vSorted;
823 TRY_CRITICAL_BLOCK(cs_mapWallet)
825 printf("RefreshListCtrl starting\n");
827 fRefreshListCtrl = false;
828 vWalletUpdated.clear();
830 // Do the newest transactions first
831 vSorted.reserve(mapWallet.size());
832 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
834 const CWalletTx& wtx = (*it).second;
835 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
836 vSorted.push_back(make_pair(nTime, (*it).first));
838 m_listCtrl->DeleteAllItems();
843 sort(vSorted.begin(), vSorted.end());
846 for (int i = 0; i < vSorted.size();)
850 bool fEntered = false;
851 TRY_CRITICAL_BLOCK(cs_mapWallet)
854 uint256& hash = vSorted[i++].second;
855 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
856 if (mi != mapWallet.end())
857 InsertTransaction((*mi).second, true);
859 if (!fEntered || i == 100 || i % 500 == 0)
863 printf("RefreshListCtrl done\n");
865 // Update transaction total display
870 // Check for time updates
871 static int64 nLastTime;
872 if (GetTime() > nLastTime + 30)
874 TRY_CRITICAL_BLOCK(cs_mapWallet)
876 nLastTime = GetTime();
877 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
879 CWalletTx& wtx = (*it).second;
880 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
881 InsertTransaction(wtx, false);
888 void CMainFrame::RefreshStatusColumn()
891 static CBlockIndex* pindexLastBest;
892 static unsigned int nLastRefreshed;
894 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
895 if (nTop == nLastTop && pindexLastBest == pindexBest)
898 TRY_CRITICAL_BLOCK(cs_mapWallet)
901 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
903 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
905 // If no updates, only need to do the part that moved onto the screen
906 if (nStart >= nLastTop && nStart < nLastTop + 100)
907 nStart = nLastTop + 100;
908 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
912 pindexLastBest = pindexBest;
913 nLastRefreshed = nListViewUpdated;
915 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
917 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
918 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
919 if (mi == mapWallet.end())
921 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
924 CWalletTx& wtx = (*mi).second;
925 if (wtx.IsCoinBase() ||
926 wtx.GetTxTime() != wtx.nTimeDisplayed ||
927 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
929 if (!InsertTransaction(wtx, false, nIndex))
930 m_listCtrl->DeleteItem(nIndex--);
934 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
940 void CMainFrame::OnPaint(wxPaintEvent& event)
951 unsigned int nNeedRepaint = 0;
952 unsigned int nLastRepaint = 0;
953 int64 nLastRepaintTime = 0;
954 int64 nRepaintInterval = 500;
956 void ThreadDelayedRepaint(void* parg)
960 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
962 nLastRepaint = nNeedRepaint;
965 printf("DelayedRepaint\n");
967 pframeMain->fRefresh = true;
968 pframeMain->GetEventHandler()->AddPendingEvent(event);
971 Sleep(nRepaintInterval);
975 void MainFrameRepaint()
977 // This is called by network code that shouldn't access pframeMain
978 // directly because it could still be running after the UI is closed.
981 // Don't repaint too often
982 static int64 nLastRepaintRequest;
983 if (GetTimeMillis() - nLastRepaintRequest < 100)
988 nLastRepaintRequest = GetTimeMillis();
990 printf("MainFrameRepaint\n");
992 pframeMain->fRefresh = true;
993 pframeMain->GetEventHandler()->AddPendingEvent(event);
997 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
999 // Skip lets the listctrl do the paint, we're just hooking the message
1003 ptaskbaricon->UpdateTooltip();
1008 static int nTransactionCount;
1009 bool fPaintedBalance = false;
1010 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1012 nLastRepaint = nNeedRepaint;
1013 nLastRepaintTime = GetTimeMillis();
1015 // Update listctrl contents
1016 if (!vWalletUpdated.empty())
1018 TRY_CRITICAL_BLOCK(cs_mapWallet)
1021 if (m_listCtrl->GetItemCount())
1022 strTop = (string)m_listCtrl->GetItemText(0);
1023 BOOST_FOREACH(uint256 hash, vWalletUpdated)
1025 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1026 if (mi != mapWallet.end())
1027 InsertTransaction((*mi).second, false);
1029 vWalletUpdated.clear();
1030 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1031 m_listCtrl->ScrollList(0, INT_MIN/2);
1036 TRY_CRITICAL_BLOCK(cs_mapWallet)
1038 fPaintedBalance = true;
1039 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
1041 // Count hidden and multi-line transactions
1042 nTransactionCount = 0;
1043 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1045 CWalletTx& wtx = (*it).second;
1046 nTransactionCount += wtx.nLinesDisplayed;
1050 if (!vWalletUpdated.empty() || !fPaintedBalance)
1053 // Update status column of visible items only
1054 RefreshStatusColumn();
1056 // Update status bar
1057 static string strPrevWarning;
1058 string strWarning = GetWarnings("statusbar");
1059 if (strWarning != "")
1060 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1061 else if (strPrevWarning != "")
1062 m_statusBar->SetStatusText("", 0);
1063 strPrevWarning = strWarning;
1066 if (fGenerateBitcoins)
1067 strGen = _(" Generating");
1068 if (fGenerateBitcoins && vNodes.empty())
1069 strGen = _("(not connected)");
1070 m_statusBar->SetStatusText(strGen, 1);
1072 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1073 m_statusBar->SetStatusText(strStatus, 2);
1075 // Update receiving address
1076 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1077 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1078 m_textCtrlAddress->SetValue(strDefaultAddress);
1082 void UIThreadCall(boost::function0<void> fn)
1084 // Call this with a function object created with bind.
1085 // bind needs all parameters to match the function's expected types
1086 // and all default parameters specified. Some examples:
1087 // UIThreadCall(bind(wxBell));
1088 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1089 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1092 wxCommandEvent event(wxEVT_UITHREADCALL);
1093 event.SetClientData((void*)new boost::function0<void>(fn));
1094 pframeMain->GetEventHandler()->AddPendingEvent(event);
1098 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1100 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1105 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1111 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1113 event.Check(fGenerateBitcoins);
1116 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1118 // Options->Your Receiving Addresses
1119 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1120 if (!dialog.ShowModal())
1124 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1127 COptionsDialog dialog(this);
1131 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1134 CAboutDialog dialog(this);
1138 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1141 CSendDialog dialog(this);
1145 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1147 // Toolbar: Address Book
1148 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1149 if (dialogAddr.ShowModal() == 2)
1152 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1153 dialogSend.ShowModal();
1157 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1159 // Automatically select-all when entering window
1161 m_textCtrlAddress->SetSelection(-1, -1);
1162 fOnSetFocusAddress = true;
1165 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1168 if (fOnSetFocusAddress)
1169 m_textCtrlAddress->SetSelection(-1, -1);
1170 fOnSetFocusAddress = false;
1173 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1176 CGetTextFromUserDialog dialog(this,
1177 _("New Receiving Address"),
1178 _("You should use a new address for each payment you receive.\n\nLabel"),
1180 if (!dialog.ShowModal())
1182 string strName = dialog.GetValue();
1185 string strAddress = PubKeyToAddress(GetKeyFromKeyPool());
1188 SetAddressBookName(strAddress, strName);
1189 SetDefaultReceivingAddress(strAddress);
1192 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1194 // Copy address box to clipboard
1195 if (wxTheClipboard->Open())
1197 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1198 wxTheClipboard->Close();
1202 void CMainFrame::OnListItemActivated(wxListEvent& event)
1204 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1206 CRITICAL_BLOCK(cs_mapWallet)
1208 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1209 if (mi == mapWallet.end())
1211 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1216 CTxDetailsDialog dialog(this, wtx);
1218 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1227 //////////////////////////////////////////////////////////////////////////////
1232 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1235 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1237 CRITICAL_BLOCK(cs_mapAddressBook)
1240 strHTML.reserve(4000);
1241 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1243 int64 nTime = wtx.GetTxTime();
1244 int64 nCredit = wtx.GetCredit();
1245 int64 nDebit = wtx.GetDebit();
1246 int64 nNet = nCredit - nDebit;
1250 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1251 int nRequests = wtx.GetRequestCount();
1252 if (nRequests != -1)
1255 strHTML += _(", has not been successfully broadcast yet");
1256 else if (nRequests == 1)
1257 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1259 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1263 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1269 if (wtx.IsCoinBase())
1271 strHTML += _("<b>Source:</b> Generated<br>");
1273 else if (!wtx.mapValue["from"].empty())
1275 // Online transaction
1276 if (!wtx.mapValue["from"].empty())
1277 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1281 // Offline transaction
1285 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1289 vector<unsigned char> vchPubKey;
1290 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1292 string strAddress = PubKeyToAddress(vchPubKey);
1293 if (mapAddressBook.count(strAddress))
1295 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1296 strHTML += _("<b>To:</b> ");
1297 strHTML += HtmlEscape(strAddress);
1298 if (!mapAddressBook[strAddress].empty())
1299 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1301 strHTML += _(" (yours)");
1316 if (!wtx.mapValue["to"].empty())
1318 // Online transaction
1319 strAddress = wtx.mapValue["to"];
1320 strHTML += _("<b>To:</b> ");
1321 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1322 strHTML += mapAddressBook[strAddress] + " ";
1323 strHTML += HtmlEscape(strAddress) + "<br>";
1330 if (wtx.IsCoinBase() && nCredit == 0)
1335 int64 nUnmatured = 0;
1336 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1337 nUnmatured += txout.GetCredit();
1338 strHTML += _("<b>Credit:</b> ");
1339 if (wtx.IsInMainChain())
1340 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1342 strHTML += _("(not accepted)");
1350 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1354 bool fAllFromMe = true;
1355 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1356 fAllFromMe = fAllFromMe && txin.IsMine();
1358 bool fAllToMe = true;
1359 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1360 fAllToMe = fAllToMe && txout.IsMine();
1367 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1372 if (wtx.mapValue["to"].empty())
1374 // Offline transaction
1376 if (ExtractHash160(txout.scriptPubKey, hash160))
1378 string strAddress = Hash160ToAddress(hash160);
1379 strHTML += _("<b>To:</b> ");
1380 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1381 strHTML += mapAddressBook[strAddress] + " ";
1382 strHTML += strAddress;
1387 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1393 int64 nChange = wtx.GetChange();
1394 int64 nValue = nCredit - nChange;
1395 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1396 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1399 int64 nTxFee = nDebit - wtx.GetValueOut();
1401 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1406 // Mixed debit transaction
1408 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1410 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1411 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1413 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1417 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1423 if (!wtx.mapValue["message"].empty())
1424 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1425 if (!wtx.mapValue["comment"].empty())
1426 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1428 if (wtx.IsCoinBase())
1429 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>";
1437 strHTML += "<hr><br>debug print<br><br>";
1438 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1440 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1441 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1443 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1445 strHTML += "<br><b>Transaction:</b><br>";
1446 strHTML += HtmlEscape(wtx.ToString(), true);
1448 strHTML += "<br><b>Inputs:</b><br>";
1449 CRITICAL_BLOCK(cs_mapWallet)
1451 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1453 COutPoint prevout = txin.prevout;
1454 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1455 if (mi != mapWallet.end())
1457 const CWalletTx& prev = (*mi).second;
1458 if (prevout.n < prev.vout.size())
1460 strHTML += HtmlEscape(prev.ToString(), true);
1461 strHTML += " " + FormatTxStatus(prev) + ", ";
1462 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1471 strHTML += "</font></html>";
1472 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1473 m_htmlWin->SetPage(strHTML);
1474 m_buttonOK->SetFocus();
1478 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1488 //////////////////////////////////////////////////////////////////////////////
1494 string StartupShortcutPath()
1496 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1499 bool GetStartOnSystemStartup()
1501 return filesystem::exists(StartupShortcutPath().c_str());
1504 void SetStartOnSystemStartup(bool fAutoStart)
1506 // If the shortcut exists already, remove it for updating
1507 remove(StartupShortcutPath().c_str());
1513 // Get a pointer to the IShellLink interface.
1514 IShellLink* psl = NULL;
1515 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1516 CLSCTX_INPROC_SERVER, IID_IShellLink,
1517 reinterpret_cast<void**>(&psl));
1519 if (SUCCEEDED(hres))
1521 // Get the current executable path
1522 TCHAR pszExePath[MAX_PATH];
1523 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1525 // Set the path to the shortcut target
1526 psl->SetPath(pszExePath);
1527 PathRemoveFileSpec(pszExePath);
1528 psl->SetWorkingDirectory(pszExePath);
1529 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1531 // Query IShellLink for the IPersistFile interface for
1532 // saving the shortcut in persistent storage.
1533 IPersistFile* ppf = NULL;
1534 hres = psl->QueryInterface(IID_IPersistFile,
1535 reinterpret_cast<void**>(&ppf));
1536 if (SUCCEEDED(hres))
1538 WCHAR pwsz[MAX_PATH];
1539 // Ensure that the string is ANSI.
1540 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1541 // Save the link by calling IPersistFile::Save.
1542 hres = ppf->Save(pwsz, TRUE);
1551 #elif defined(__WXGTK__)
1553 // Follow the Desktop Application Autostart Spec:
1554 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1556 boost::filesystem::path GetAutostartDir()
1558 namespace fs = boost::filesystem;
1560 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1561 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1562 char* pszHome = getenv("HOME");
1563 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1567 boost::filesystem::path GetAutostartFilePath()
1569 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1572 bool GetStartOnSystemStartup()
1574 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1575 if (!optionFile.good())
1577 // Scan through file for "Hidden=true":
1579 while (!optionFile.eof())
1581 getline(optionFile, line);
1582 if (line.find("Hidden") != string::npos &&
1583 line.find("true") != string::npos)
1591 void SetStartOnSystemStartup(bool fAutoStart)
1595 unlink(GetAutostartFilePath().native_file_string().c_str());
1599 char pszExePath[MAX_PATH+1];
1600 memset(pszExePath, 0, sizeof(pszExePath));
1601 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1604 boost::filesystem::create_directories(GetAutostartDir());
1606 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1607 if (!optionFile.good())
1609 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1612 // Write a bitcoin.desktop file to the autostart directory:
1613 optionFile << "[Desktop Entry]\n";
1614 optionFile << "Type=Application\n";
1615 optionFile << "Name=Bitcoin\n";
1616 optionFile << "Exec=" << pszExePath << "\n";
1617 optionFile << "Terminal=false\n";
1618 optionFile << "Hidden=false\n";
1624 // TODO: OSX startup stuff; see:
1625 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1627 bool GetStartOnSystemStartup() { return false; }
1628 void SetStartOnSystemStartup(bool fAutoStart) { }
1637 //////////////////////////////////////////////////////////////////////////////
1642 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1644 // Set up list box of page choices
1645 m_listBox->Append(_("Main"));
1646 //m_listBox->Append(_("Test 2"));
1647 m_listBox->SetSelection(0);
1650 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1652 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1654 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1655 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1656 if (!GetBoolArg("-minimizetotray"))
1658 // Minimize to tray is just too buggy on Linux
1659 fMinimizeToTray = false;
1660 m_checkBoxMinimizeToTray->SetValue(false);
1661 m_checkBoxMinimizeToTray->Enable(false);
1662 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1665 #ifdef __WXMAC_OSX__
1666 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1670 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1671 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1672 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1673 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1675 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1677 m_checkBoxUseUPnP->Enable(false);
1678 m_checkBoxUseProxy->SetValue(fUseProxy);
1679 m_textCtrlProxyIP->Enable(fUseProxy);
1680 m_textCtrlProxyPort->Enable(fUseProxy);
1681 m_staticTextProxyIP->Enable(fUseProxy);
1682 m_staticTextProxyPort->Enable(fUseProxy);
1683 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1684 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1686 m_buttonOK->SetFocus();
1689 void COptionsDialog::SelectPage(int nPage)
1691 m_panelMain->Show(nPage == 0);
1692 m_panelTest2->Show(nPage == 1);
1694 m_scrolledWindow->Layout();
1695 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1698 void COptionsDialog::OnListBox(wxCommandEvent& event)
1700 SelectPage(event.GetSelection());
1703 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1706 int64 nTmp = nTransactionFee;
1707 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1708 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1711 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1713 m_textCtrlProxyIP->Enable(event.IsChecked());
1714 m_textCtrlProxyPort->Enable(event.IsChecked());
1715 m_staticTextProxyIP->Enable(event.IsChecked());
1716 m_staticTextProxyPort->Enable(event.IsChecked());
1719 CAddress COptionsDialog::GetProxyAddr()
1721 // Be careful about byte order, addr.ip and addr.port are big endian
1722 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1723 if (addr.ip == INADDR_NONE)
1724 addr.ip = addrProxy.ip;
1725 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1726 addr.port = htons(nPort);
1727 if (nPort <= 0 || nPort > USHRT_MAX)
1728 addr.port = addrProxy.port;
1732 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1735 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1736 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1740 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1742 OnButtonApply(event);
1746 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1751 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1755 int64 nPrevTransactionFee = nTransactionFee;
1756 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1757 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1759 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1761 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1762 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1765 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1767 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1768 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1769 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1772 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1774 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1775 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1778 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1780 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1781 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1785 fUseProxy = m_checkBoxUseProxy->GetValue();
1786 walletdb.WriteSetting("fUseProxy", fUseProxy);
1788 addrProxy = GetProxyAddr();
1789 walletdb.WriteSetting("addrProxy", addrProxy);
1797 //////////////////////////////////////////////////////////////////////////////
1802 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1804 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
1806 // Change (c) into UTF-8 or ANSI copyright symbol
1807 wxString str = m_staticTextMain->GetLabel();
1809 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1811 str.Replace("(c)", "\xA9");
1813 m_staticTextMain->SetLabel(str);
1815 // Resize on Linux to make the window fit the text.
1816 // The text was wrapped manually rather than using the Wrap setting because
1817 // the wrap would be too small on Linux and it can't be changed at this point.
1818 wxFont fontTmp = m_staticTextMain->GetFont();
1819 if (fontTmp.GetPointSize() > 8);
1820 fontTmp.SetPointSize(8);
1821 m_staticTextMain->SetFont(fontTmp);
1822 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1824 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1828 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1838 //////////////////////////////////////////////////////////////////////////////
1843 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1846 m_textCtrlAddress->SetValue(strAddress);
1847 m_choiceTransferType->SetSelection(0);
1848 m_bitmapCheckMark->Show(false);
1849 fEnabledPrev = true;
1850 m_textCtrlAddress->SetFocus();
1852 //// todo: should add a display of your balance for convenience
1854 wxFont fontTmp = m_staticTextInstructions->GetFont();
1855 if (fontTmp.GetPointSize() > 9);
1856 fontTmp.SetPointSize(9);
1857 m_staticTextInstructions->SetFont(fontTmp);
1860 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1864 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
1867 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1872 SetIcon(wxICON(bitcoin));
1875 // Fixup the tab order
1876 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1877 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1881 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1883 // Reformat the amount
1885 if (m_textCtrlAmount->GetValue().Trim().empty())
1888 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1889 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1892 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1894 // Open address book
1895 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1896 if (dialog.ShowModal())
1897 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1900 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1902 // Copy clipboard to address box
1903 if (wxTheClipboard->Open())
1905 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1907 wxTextDataObject data;
1908 wxTheClipboard->GetData(data);
1909 m_textCtrlAddress->SetValue(data.GetText());
1911 wxTheClipboard->Close();
1915 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1917 static CCriticalSection cs_sendlock;
1918 TRY_CRITICAL_BLOCK(cs_sendlock)
1921 string strAddress = (string)m_textCtrlAddress->GetValue();
1925 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1927 wxMessageBox(_("Error in amount "), _("Send Coins"));
1930 if (nValue > GetBalance())
1932 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1935 if (nValue + nTransactionFee > GetBalance())
1937 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1941 // Parse bitcoin address
1943 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1945 if (fBitcoinAddress)
1947 CRITICAL_BLOCK(cs_main)
1949 // Send to bitcoin address
1950 CScript scriptPubKey;
1951 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1953 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1955 wxMessageBox(_("Payment sent "), _("Sending..."));
1956 else if (strError == "ABORTED")
1957 return; // leave send dialog open
1960 wxMessageBox(strError + " ", _("Sending..."));
1969 CAddress addr(strAddress);
1970 if (!addr.IsValid())
1972 wxMessageBox(_("Invalid address "), _("Send Coins"));
1977 wtx.mapValue["to"] = strAddress;
1979 // Send to IP address
1980 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1981 if (!pdialog->ShowModal())
1985 CRITICAL_BLOCK(cs_mapAddressBook)
1986 if (!mapAddressBook.count(strAddress))
1987 SetAddressBookName(strAddress, "");
1993 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2004 //////////////////////////////////////////////////////////////////////////////
2009 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
2014 start = wxDateTime::UNow();
2015 memset(pszStatus, 0, sizeof(pszStatus));
2022 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2024 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2027 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2028 m_textCtrlStatus->SetValue("");
2030 CreateThread(SendingDialogStartTransfer, this);
2033 CSendingDialog::~CSendingDialog()
2035 printf("~CSendingDialog()\n");
2038 void CSendingDialog::Close()
2040 // Last one out turn out the lights.
2041 // fWorkDone signals that work side is done and UI thread should call destroy.
2042 // fUIDone signals that UI window has closed and work thread should call destroy.
2043 // This allows the window to disappear and end modality when cancelled
2044 // without making the user wait for ConnectNode to return. The dialog object
2045 // hangs around in the background until the work thread exits.
2056 void CSendingDialog::OnClose(wxCloseEvent& event)
2058 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2065 wxCommandEvent cmdevent;
2066 OnButtonCancel(cmdevent);
2070 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2076 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2082 void CSendingDialog::OnPaint(wxPaintEvent& event)
2085 if (strlen(pszStatus) > 130)
2086 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2088 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2089 m_staticTextSending->SetFocus();
2091 m_buttonCancel->Enable(false);
2094 m_buttonOK->Enable(true);
2095 m_buttonOK->SetFocus();
2096 m_buttonCancel->Enable(false);
2098 if (fAbort && fCanCancel && IsShown())
2100 strcpy(pszStatus, _("CANCELLED"));
2101 m_buttonOK->Enable(true);
2102 m_buttonOK->SetFocus();
2103 m_buttonCancel->Enable(false);
2104 m_buttonCancel->SetLabel(_("Cancelled"));
2106 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2112 // Everything from here on is not in the UI thread and must only communicate
2113 // with the rest of the dialog through variables and calling repaint.
2116 void CSendingDialog::Repaint()
2120 GetEventHandler()->AddPendingEvent(event);
2123 bool CSendingDialog::Status()
2130 if (fAbort && fCanCancel)
2132 memset(pszStatus, 0, 10);
2133 strcpy(pszStatus, _("CANCELLED"));
2141 bool CSendingDialog::Status(const string& str)
2146 // This can be read by the UI thread at any time,
2147 // so copy in a way that can be read cleanly at all times.
2148 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2149 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2155 bool CSendingDialog::Error(const string& str)
2159 Status(string(_("Error: ")) + str);
2163 void SendingDialogStartTransfer(void* parg)
2165 ((CSendingDialog*)parg)->StartTransfer();
2168 void CSendingDialog::StartTransfer()
2170 // Make sure we have enough money
2171 if (nPrice + nTransactionFee > GetBalance())
2173 Error(_("Insufficient funds"));
2177 // We may have connected already for product details
2178 if (!Status(_("Connecting...")))
2180 CNode* pnode = ConnectNode(addr, 15 * 60);
2183 Error(_("Unable to connect"));
2187 // Send order to seller, with response going to OnReply2 via event handler
2188 if (!Status(_("Requesting public key...")))
2190 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2193 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2195 ((CSendingDialog*)parg)->OnReply2(vRecv);
2198 void CSendingDialog::OnReply2(CDataStream& vRecv)
2200 if (!Status(_("Received public key...")))
2203 CScript scriptPubKey;
2212 vRecv >> strMessage;
2214 Error(_("Recipient is not accepting transactions sent by IP address"));
2216 Error(_("Transfer was not accepted"));
2217 //// todo: enlarge the window and enable a hidden white box to put seller's message
2220 vRecv >> scriptPubKey;
2224 //// what do we want to do about this?
2225 Error(_("Invalid response received"));
2229 // Pause to give the user a chance to cancel
2230 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2237 CRITICAL_BLOCK(cs_main)
2240 if (!Status(_("Creating transaction...")))
2242 if (nPrice + nTransactionFee > GetBalance())
2244 Error(_("Insufficient funds"));
2247 CReserveKey reservekey;
2249 if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2251 if (nPrice + nFeeRequired > GetBalance())
2252 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()));
2254 Error(_("Transaction creation failed"));
2259 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2261 Error(_("Transaction aborted"));
2265 // Make sure we're still connected
2266 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2269 Error(_("Lost connection, transaction cancelled"));
2273 // Last chance to cancel
2285 if (!Status(_("Sending payment...")))
2289 if (!CommitTransaction(wtx, reservekey))
2291 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."));
2295 // Send payment tx to seller, with response going to OnReply3 via event handler
2296 CWalletTx wtxSend = wtx;
2297 wtxSend.fFromMe = false;
2298 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2300 Status(_("Waiting for confirmation..."));
2305 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2307 ((CSendingDialog*)parg)->OnReply3(vRecv);
2310 void CSendingDialog::OnReply3(CDataStream& vRecv)
2318 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2319 "The transaction is recorded and will credit to the recipient,\n"
2320 "but the comment information will be blank."));
2326 //// what do we want to do about this?
2327 Error(_("Payment was sent, but an invalid response was received"));
2333 Status(_("Payment completed"));
2341 //////////////////////////////////////////////////////////////////////////////
2343 // CAddressBookDialog
2346 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2349 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2352 // Set initially selected page
2353 wxNotebookEvent event;
2354 event.SetSelection(nPageIn);
2355 OnNotebookPageChanged(event);
2356 m_notebook->ChangeSelection(nPageIn);
2358 fDuringSend = fDuringSendIn;
2360 m_buttonCancel->Show(false);
2363 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2365 wxIcon iconAddressBook;
2366 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2367 SetIcon(iconAddressBook);
2371 SetIcon(wxICON(bitcoin));
2374 // Init column headers
2375 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2376 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2377 m_listCtrlSending->SetFocus();
2378 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2379 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2380 m_listCtrlReceiving->SetFocus();
2382 // Fill listctrl with address book data
2383 CRITICAL_BLOCK(cs_mapKeys)
2384 CRITICAL_BLOCK(cs_mapAddressBook)
2386 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2387 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook)
2389 string strAddress = item.first;
2390 string strName = item.second;
2392 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2393 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2394 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2395 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2396 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2401 wxString CAddressBookDialog::GetSelectedAddress()
2403 int nIndex = GetSelection(m_listCtrl);
2406 return GetItemText(m_listCtrl, nIndex, 1);
2409 wxString CAddressBookDialog::GetSelectedSendingAddress()
2411 int nIndex = GetSelection(m_listCtrlSending);
2414 return GetItemText(m_listCtrlSending, nIndex, 1);
2417 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2419 int nIndex = GetSelection(m_listCtrlReceiving);
2422 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2425 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2428 nPage = event.GetSelection();
2429 if (nPage == SENDING)
2430 m_listCtrl = m_listCtrlSending;
2431 else if (nPage == RECEIVING)
2432 m_listCtrl = m_listCtrlReceiving;
2433 m_buttonDelete->Show(nPage == SENDING);
2434 m_buttonCopy->Show(nPage == RECEIVING);
2436 m_listCtrl->SetFocus();
2439 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2441 // Update address book with edited name
2443 if (event.IsEditCancelled())
2445 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2446 SetAddressBookName(strAddress, string(event.GetText()));
2447 pframeMain->RefreshListCtrl();
2450 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2453 if (nPage == RECEIVING)
2454 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2457 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2462 // Doubleclick returns selection
2463 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2467 // Doubleclick edits item
2468 wxCommandEvent event2;
2469 OnButtonEdit(event2);
2472 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2474 if (nPage != SENDING)
2476 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2478 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2480 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2481 CWalletDB().EraseName(strAddress);
2482 m_listCtrl->DeleteItem(nIndex);
2485 pframeMain->RefreshListCtrl();
2488 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2490 // Copy address box to clipboard
2491 if (wxTheClipboard->Open())
2493 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2494 wxTheClipboard->Close();
2498 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2501 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2503 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2507 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2509 int nIndex = GetSelection(m_listCtrl);
2512 string strName = (string)m_listCtrl->GetItemText(nIndex);
2513 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2514 string strAddressOrg = strAddress;
2516 if (nPage == SENDING)
2518 // Ask name and address
2521 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2522 if (!dialog.ShowModal())
2524 strName = dialog.GetValue1();
2525 strAddress = dialog.GetValue2();
2527 while (CheckIfMine(strAddress, _("Edit Address")));
2530 else if (nPage == RECEIVING)
2533 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2534 if (!dialog.ShowModal())
2536 strName = dialog.GetValue();
2540 if (strAddress != strAddressOrg)
2541 CWalletDB().EraseName(strAddressOrg);
2542 SetAddressBookName(strAddress, strName);
2543 m_listCtrl->SetItem(nIndex, 1, strAddress);
2544 m_listCtrl->SetItemText(nIndex, strName);
2545 pframeMain->RefreshListCtrl();
2548 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2553 if (nPage == SENDING)
2555 // Ask name and address
2558 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2559 if (!dialog.ShowModal())
2561 strName = dialog.GetValue1();
2562 strAddress = dialog.GetValue2();
2564 while (CheckIfMine(strAddress, _("Add Address")));
2566 else if (nPage == RECEIVING)
2569 CGetTextFromUserDialog dialog(this,
2570 _("New Receiving Address"),
2571 _("You should use a new address for each payment you receive.\n\nLabel"),
2573 if (!dialog.ShowModal())
2575 strName = dialog.GetValue();
2578 strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2581 // Add to list and select it
2582 SetAddressBookName(strAddress, strName);
2583 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2584 SetSelection(m_listCtrl, nIndex);
2585 m_listCtrl->SetFocus();
2586 if (nPage == SENDING)
2587 pframeMain->RefreshListCtrl();
2590 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2593 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2596 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2602 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2613 //////////////////////////////////////////////////////////////////////////////
2620 ID_TASKBAR_RESTORE = 10001,
2623 ID_TASKBAR_GENERATE,
2627 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2628 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2629 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2630 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2631 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2632 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2633 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2636 void CMyTaskBarIcon::Show(bool fShow)
2638 static char pszPrevTip[200];
2641 string strTooltip = _("Bitcoin");
2642 if (fGenerateBitcoins)
2643 strTooltip = _("Bitcoin - Generating");
2644 if (fGenerateBitcoins && vNodes.empty())
2645 strTooltip = _("Bitcoin - (not connected)");
2647 // Optimization, only update when changed, using char array to be reentrant
2648 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2650 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2652 // somehow it'll choose the wrong size and scale it down if
2653 // we use the main icon, so we hand it one with only 16x16
2654 SetIcon(wxICON(favicon), strTooltip);
2656 SetIcon(bitcoin80_xpm, strTooltip);
2662 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2667 void CMyTaskBarIcon::Hide()
2672 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2677 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2682 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2685 CSendDialog dialog(pframeMain);
2689 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2691 // Since it's modal, get the main window to do it
2692 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2693 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2696 void CMyTaskBarIcon::Restore()
2699 wxIconizeEvent event(0, false);
2700 pframeMain->GetEventHandler()->AddPendingEvent(event);
2701 pframeMain->Iconize(false);
2702 pframeMain->Raise();
2705 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2707 event.Check(fGenerateBitcoins);
2710 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2712 pframeMain->Close(true);
2715 void CMyTaskBarIcon::UpdateTooltip()
2717 if (IsIconInstalled())
2721 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2723 wxMenu* pmenu = new wxMenu;
2724 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2725 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2726 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2727 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2728 pmenu->AppendSeparator();
2729 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2739 //////////////////////////////////////////////////////////////////////////////
2744 void CreateMainWindow()
2746 pframeMain = new CMainFrame(NULL);
2747 if (GetBoolArg("-min"))
2748 pframeMain->Iconize(true);
2749 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2750 if (!GetBoolArg("-minimizetotray"))
2751 fMinimizeToTray = false;
2753 pframeMain->Show(true); // have to show first to get taskbar button to hide
2754 if (fMinimizeToTray && pframeMain->IsIconized())
2755 fClosedToTray = true;
2756 pframeMain->Show(!fClosedToTray);
2757 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2758 CreateThread(ThreadDelayedRepaint, NULL);
2762 // Define a new application
2763 class CMyApp : public wxApp
2772 // Hook Initialize so we can start without GUI
2773 virtual bool Initialize(int& argc, wxChar** argv);
2775 // 2nd-level exception handling: we get all the exceptions occurring in any
2776 // event handler here
2777 virtual bool OnExceptionInMainLoop();
2779 // 3rd, and final, level exception handling: whenever an unhandled
2780 // exception is caught, this function is called
2781 virtual void OnUnhandledException();
2783 // and now for something different: this function is called in case of a
2784 // crash (e.g. dereferencing null pointer, division by 0, ...)
2785 virtual void OnFatalException();
2788 IMPLEMENT_APP(CMyApp)
2790 bool CMyApp::Initialize(int& argc, wxChar** argv)
2792 for (int i = 1; i < argc; i++)
2793 if (!IsSwitchChar(argv[i][0]))
2794 fCommandLine = true;
2798 // wxApp::Initialize will remove environment-specific parameters,
2799 // so it's too early to call ParseParameters yet
2800 for (int i = 1; i < argc; i++)
2802 wxString str = argv[i];
2804 if (str.size() >= 1 && str[0] == '/')
2806 char pszLower[MAX_PATH];
2807 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2811 if (str == "-daemon")
2817 if (fDaemon || fCommandLine)
2819 // Call the original Initialize while suppressing error messages
2820 // and ignoring failure. If unable to initialize GTK, it fails
2821 // near the end so hopefully the last few things don't matter.
2824 wxApp::Initialize(argc, argv);
2833 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2837 pthread_exit((void*)0);
2839 pid_t sid = setsid();
2841 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
2848 return wxApp::Initialize(argc, argv);
2851 bool CMyApp::OnInit()
2853 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2854 // Disable malfunctioning wxWidgets debug assertion
2855 extern int g_isPainting;
2856 g_isPainting = 10000;
2858 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2859 SetAppName("Bitcoin");
2861 SetAppName("bitcoin");
2865 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2866 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2867 class wxMBConv_win32 : public wxMBConv
2871 size_t m_minMBCharWidth;
2873 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2874 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2878 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2879 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2880 g_locale.AddCatalogLookupPathPrefix("locale");
2882 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2883 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2885 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2886 g_locale.AddCatalog("bitcoin");
2889 HDC hdc = GetDC(NULL);
2892 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
2893 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
2894 ReleaseDC(NULL, hdc);
2898 return AppInit(argc, argv);
2901 int CMyApp::OnExit()
2904 return wxApp::OnExit();
2907 bool CMyApp::OnExceptionInMainLoop()
2913 catch (std::exception& e)
2915 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2916 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2922 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2923 wxLogWarning("Unknown exception");
2930 void CMyApp::OnUnhandledException()
2932 // this shows how we may let some exception propagate uncaught
2937 catch (std::exception& e)
2939 PrintException(&e, "CMyApp::OnUnhandledException()");
2940 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2946 PrintException(NULL, "CMyApp::OnUnhandledException()");
2947 wxLogWarning("Unknown exception");
2953 void CMyApp::OnFatalException()
2955 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);