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.
11 using namespace boost;
14 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
16 CMainFrame* pframeMain = NULL;
17 CMyTaskBarIcon* ptaskbaricon = NULL;
18 bool fClosedToTray = false;
25 static const double nScaleX = 1.0;
26 static const double nScaleY = 1.0;
36 //////////////////////////////////////////////////////////////////////////////
41 void HandleCtrlA(wxKeyEvent& event)
45 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
46 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
47 textCtrl->SetSelection(-1, -1);
52 //char pszHourFormat[256];
53 //pszHourFormat[0] = '\0';
54 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
55 //return (pszHourFormat[0] != '0');
59 string DateStr(int64 nTime)
61 // Can only be used safely here in the UI
62 return (string)wxDateTime((time_t)nTime).FormatDate();
65 string DateTimeStr(int64 nTime)
67 // Can only be used safely here in the UI
68 wxDateTime datetime((time_t)nTime);
70 return (string)datetime.Format("%x %H:%M");
72 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
75 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
77 // Helper to simplify access to listctrl
79 item.m_itemId = nIndex;
81 item.m_mask = wxLIST_MASK_TEXT;
82 if (!listCtrl->GetItem(item))
84 return item.GetText();
87 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
89 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
90 listCtrl->SetItem(nIndex, 1, str1);
94 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
96 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
97 listCtrl->SetItem(nIndex, 1, str1);
98 listCtrl->SetItem(nIndex, 2, str2);
99 listCtrl->SetItem(nIndex, 3, str3);
100 listCtrl->SetItem(nIndex, 4, str4);
104 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
106 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
107 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
108 listCtrl->SetItem(nIndex, 1, str1);
109 listCtrl->SetItem(nIndex, 2, str2);
110 listCtrl->SetItem(nIndex, 3, str3);
111 listCtrl->SetItem(nIndex, 4, str4);
115 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
117 // Repaint on Windows is more flickery if the colour has ever been set,
118 // so don't want to set it unless it's different. Default colour has
119 // alpha 0 transparent, so our colours don't match using operator==.
120 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
122 c1 = wxColour(0,0,0);
123 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
124 listCtrl->SetItemTextColour(nIndex, colour);
127 void SetSelection(wxListCtrl* listCtrl, int nIndex)
129 int nSize = listCtrl->GetItemCount();
130 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
131 for (int i = 0; i < nSize; i++)
132 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
135 int GetSelection(wxListCtrl* listCtrl)
137 int nSize = listCtrl->GetItemCount();
138 for (int i = 0; i < nSize; i++)
139 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
144 string HtmlEscape(const char* psz, bool fMultiLine=false)
147 for (const char* p = psz; *p; p++)
149 if (*p == '<') len += 4;
150 else if (*p == '>') len += 4;
151 else if (*p == '&') len += 5;
152 else if (*p == '"') len += 6;
153 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
154 else if (*p == '\n' && fMultiLine) len += 5;
160 for (const char* p = psz; *p; p++)
162 if (*p == '<') str += "<";
163 else if (*p == '>') str += ">";
164 else if (*p == '&') str += "&";
165 else if (*p == '"') str += """;
166 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
167 else if (*p == '\n' && fMultiLine) str += "<br>\n";
174 string HtmlEscape(const string& str, bool fMultiLine=false)
176 return HtmlEscape(str.c_str(), fMultiLine);
179 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
181 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
185 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
188 return wxMessageBox(message, caption, style, parent, x, y);
190 if (wxThread::IsMain() || fDaemon)
192 return wxMessageBox(message, caption, style, parent, x, y);
198 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
206 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
208 if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
210 string strMessage = strprintf(
211 _("This transaction is over the size limit. You can still send it for a fee of %s, "
212 "which goes to the nodes that process your transaction and helps to support the network. "
213 "Do you want to pay the fee?"),
214 FormatMoney(nFeeRequired).c_str());
215 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
218 void CalledSetStatusBar(const string& strText, int nField)
220 if (nField == 0 && GetWarnings("statusbar") != "")
222 if (pframeMain && pframeMain->m_statusBar)
223 pframeMain->m_statusBar->SetStatusText(strText, nField);
226 void SetDefaultReceivingAddress(const string& strAddress)
228 // Update main window address and database
229 if (pframeMain == NULL)
231 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
234 if (!AddressToHash160(strAddress, hash160))
236 if (!mapPubKeys.count(hash160))
238 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
239 pframeMain->m_textCtrlAddress->SetValue(strAddress);
252 //////////////////////////////////////////////////////////////////////////////
257 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
259 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
261 // Set initially selected page
262 wxNotebookEvent event;
263 event.SetSelection(0);
264 OnNotebookPageChanged(event);
265 m_notebook->ChangeSelection(0);
268 fRefreshListCtrl = false;
269 fRefreshListCtrlRunning = false;
270 fOnSetFocusAddress = false;
272 m_choiceFilter->SetSelection(0);
273 double dResize = nScaleX;
275 SetIcon(wxICON(bitcoin));
276 SetSize(dResize * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
278 SetIcon(bitcoin80_xpm);
279 SetBackgroundColour(m_toolBar->GetBackgroundColour());
280 wxFont fontTmp = m_staticText41->GetFont();
281 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
282 m_staticTextBalance->SetFont(fontTmp);
283 m_staticTextBalance->SetSize(140, 17);
284 // resize to fit ubuntu's huge default font
286 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
288 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
289 m_listCtrl->SetFocus();
290 ptaskbaricon = new CMyTaskBarIcon();
292 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
293 // to their standard places, leaving these menus empty.
294 GetMenuBar()->Remove(2); // remove Help menu
295 GetMenuBar()->Remove(0); // remove File menu
298 // Init column headers
299 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
300 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
306 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
307 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
309 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
310 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
311 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
312 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
313 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
314 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
315 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
319 int pnWidths[3] = { -100, 88, 300 };
321 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
322 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
324 m_statusBar->SetFieldsCount(3, pnWidths);
326 // Fill your address text box
327 vector<unsigned char> vchPubKey;
328 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
329 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
331 // Fill listctrl with wallet transactions
335 CMainFrame::~CMainFrame()
342 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
345 nPage = event.GetSelection();
348 m_listCtrl = m_listCtrlAll;
349 fShowGenerated = true;
351 fShowReceived = true;
353 else if (nPage == SENTRECEIVED)
355 m_listCtrl = m_listCtrlSentReceived;
356 fShowGenerated = false;
358 fShowReceived = true;
360 else if (nPage == SENT)
362 m_listCtrl = m_listCtrlSent;
363 fShowGenerated = false;
365 fShowReceived = false;
367 else if (nPage == RECEIVED)
369 m_listCtrl = m_listCtrlReceived;
370 fShowGenerated = false;
372 fShowReceived = true;
375 m_listCtrl->SetFocus();
378 void CMainFrame::OnClose(wxCloseEvent& event)
380 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
382 // Divert close to minimize
384 fClosedToTray = true;
390 CreateThread(Shutdown, NULL);
394 void CMainFrame::OnIconize(wxIconizeEvent& event)
397 // Hide the task bar button when minimized.
398 // Event is sent when the frame is minimized or restored.
399 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
400 // to get rid of the deprecated warning. Just ignore it.
401 if (!event.Iconized())
402 fClosedToTray = false;
403 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
404 if (GetBoolArg("-minimizetotray")) {
406 // The tray icon sometimes disappears on ubuntu karmic
407 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
408 // Reports of CPU peg on 64-bit linux
409 if (fMinimizeToTray && event.Iconized())
410 fClosedToTray = true;
411 Show(!fClosedToTray);
412 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
413 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
418 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
422 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
423 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
426 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
428 // Hidden columns not resizeable
429 if (event.GetColumn() <= 1 && !fDebug)
435 int CMainFrame::GetSortIndex(const string& strSort)
440 // The wx generic listctrl implementation used on GTK doesn't sort,
441 // so we have to do it ourselves. Remember, we sort in reverse order.
442 // In the wx generic implementation, they store the list of items
443 // in a vector, so indexed lookups are fast, but inserts are slower
444 // the closer they are to the top.
446 int high = m_listCtrl->GetItemCount();
449 int mid = low + ((high - low) / 2);
450 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
459 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)
461 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
462 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
465 if (!fNew && nIndex == -1)
467 string strHash = " " + hashKey.ToString();
468 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
469 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
473 // fNew is for blind insert, only use if you're sure it's new
474 if (fNew || nIndex == -1)
476 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
480 // If sort key changed, must delete and reinsert to make it relocate
481 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
483 m_listCtrl->DeleteItem(nIndex);
484 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
488 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
489 m_listCtrl->SetItem(nIndex, 2, str2);
490 m_listCtrl->SetItem(nIndex, 3, str3);
491 m_listCtrl->SetItem(nIndex, 4, str4);
492 m_listCtrl->SetItem(nIndex, 5, str5);
493 m_listCtrl->SetItem(nIndex, 6, str6);
494 m_listCtrl->SetItemData(nIndex, nData);
495 SetItemTextColour(m_listCtrl, nIndex, colour);
498 bool CMainFrame::DeleteLine(uint256 hashKey)
500 long nData = *(long*)&hashKey;
504 string strHash = " " + hashKey.ToString();
505 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
506 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
510 m_listCtrl->DeleteItem(nIndex);
515 string FormatTxStatus(const CWalletTx& wtx)
520 if (wtx.nLockTime < 500000000)
521 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
523 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
527 int nDepth = wtx.GetDepthInMainChain();
528 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
529 return strprintf(_("%d/offline?"), nDepth);
531 return strprintf(_("%d/unconfirmed"), nDepth);
533 return strprintf(_("%d confirmations"), nDepth);
537 string SingleLine(const string& strIn)
540 bool fOneSpace = false;
541 BOOST_FOREACH(unsigned char c, strIn)
549 if (fOneSpace && !strOut.empty())
558 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
560 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
561 int64 nCredit = wtx.GetCredit(true);
562 int64 nDebit = wtx.GetDebit();
563 int64 nNet = nCredit - nDebit;
564 uint256 hash = wtx.GetHash();
565 string strStatus = FormatTxStatus(wtx);
566 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
567 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
568 map<string, string> mapValue = wtx.mapValue;
569 wtx.nLinesDisplayed = 1;
573 if (wtx.IsCoinBase())
575 // Don't show generated coin until confirmed by at least one block after it
576 // so we don't get the user's hopes up until it looks like it's probably accepted.
578 // It is not an error when generated blocks are not accepted. By design,
579 // some percentage of blocks, like 10% or more, will end up not accepted.
580 // This is the normal mechanism by which the network copes with latency.
582 // We display regular transactions right away before any confirmation
583 // because they can always get into some block eventually. Generated coins
584 // are special because if their block is not accepted, they are not valid.
586 if (wtx.GetDepthInMainChain() < 2)
588 wtx.nLinesDisplayed = 0;
596 // Find the block the tx is in
597 CBlockIndex* pindex = NULL;
598 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
599 if (mi != mapBlockIndex.end())
600 pindex = (*mi).second;
602 // Sort order, unrecorded transactions sort to the top
603 string strSort = strprintf("%010d-%01d-%010u",
604 (pindex ? pindex->nHeight : INT_MAX),
605 (wtx.IsCoinBase() ? 1 : 0),
609 if (nNet > 0 || wtx.IsCoinBase())
614 string strDescription;
615 if (wtx.IsCoinBase())
618 strDescription = _("Generated");
621 int64 nUnmatured = 0;
622 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
623 nUnmatured += txout.GetCredit();
624 if (wtx.IsInMainChain())
626 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
628 // Check if the block was requested by anyone
629 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
630 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
634 strDescription = _("Generated (not accepted)");
638 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
640 // Received by IP connection
643 if (!mapValue["from"].empty())
644 strDescription += _("From: ") + mapValue["from"];
645 if (!mapValue["message"].empty())
647 if (!strDescription.empty())
648 strDescription += " - ";
649 strDescription += mapValue["message"];
654 // Received by Bitcoin Address
657 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
661 vector<unsigned char> vchPubKey;
662 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
664 CRITICAL_BLOCK(cs_mapAddressBook)
666 //strDescription += _("Received payment to ");
667 //strDescription += _("Received with address ");
668 strDescription += _("Received with: ");
669 string strAddress = PubKeyToAddress(vchPubKey);
670 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
671 if (mi != mapAddressBook.end() && !(*mi).second.empty())
673 string strLabel = (*mi).second;
674 strDescription += strAddress.substr(0,12) + "... ";
675 strDescription += "(" + strLabel + ")";
678 strDescription += strAddress;
686 string strCredit = FormatMoney(nNet, true);
688 strCredit = "[" + strCredit + "]";
690 InsertLine(fNew, nIndex, hash, strSort, colour,
692 nTime ? DateTimeStr(nTime) : "",
693 SingleLine(strDescription),
699 bool fAllFromMe = true;
700 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
701 fAllFromMe = fAllFromMe && txin.IsMine();
703 bool fAllToMe = true;
704 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
705 fAllToMe = fAllToMe && txout.IsMine();
707 if (fAllFromMe && fAllToMe)
710 int64 nChange = wtx.GetChange();
711 InsertLine(fNew, nIndex, hash, strSort, colour,
713 nTime ? DateTimeStr(nTime) : "",
714 _("Payment to yourself"),
715 FormatMoney(-(nDebit - nChange), true),
716 FormatMoney(nCredit - nChange, true));
726 int64 nTxFee = nDebit - wtx.GetValueOut();
727 wtx.nLinesDisplayed = 0;
728 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
730 const CTxOut& txout = wtx.vout[nOut];
735 if (!mapValue["to"].empty())
738 strAddress = mapValue["to"];
742 // Sent to Bitcoin Address
744 if (ExtractHash160(txout.scriptPubKey, hash160))
745 strAddress = Hash160ToAddress(hash160);
748 string strDescription = _("To: ");
749 CRITICAL_BLOCK(cs_mapAddressBook)
750 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
751 strDescription += mapAddressBook[strAddress] + " ";
752 strDescription += strAddress;
753 if (!mapValue["message"].empty())
755 if (!strDescription.empty())
756 strDescription += " - ";
757 strDescription += mapValue["message"];
759 else if (!mapValue["comment"].empty())
761 if (!strDescription.empty())
762 strDescription += " - ";
763 strDescription += mapValue["comment"];
766 int64 nValue = txout.nValue;
773 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
775 nTime ? DateTimeStr(nTime) : "",
776 SingleLine(strDescription),
777 FormatMoney(-nValue, true),
780 wtx.nLinesDisplayed++;
786 // Mixed debit transaction, can't break down payees
788 bool fAllMine = true;
789 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
790 fAllMine = fAllMine && txout.IsMine();
791 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
792 fAllMine = fAllMine && txin.IsMine();
794 InsertLine(fNew, nIndex, hash, strSort, colour,
796 nTime ? DateTimeStr(nTime) : "",
798 FormatMoney(nNet, true),
806 void CMainFrame::RefreshListCtrl()
808 fRefreshListCtrl = true;
812 void CMainFrame::OnIdle(wxIdleEvent& event)
814 if (fRefreshListCtrl)
816 // Collect list of wallet transactions and sort newest first
817 bool fEntered = false;
818 vector<pair<unsigned int, uint256> > vSorted;
819 TRY_CRITICAL_BLOCK(cs_mapWallet)
821 printf("RefreshListCtrl starting\n");
823 fRefreshListCtrl = false;
824 vWalletUpdated.clear();
826 // Do the newest transactions first
827 vSorted.reserve(mapWallet.size());
828 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
830 const CWalletTx& wtx = (*it).second;
831 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
832 vSorted.push_back(make_pair(nTime, (*it).first));
834 m_listCtrl->DeleteAllItems();
839 sort(vSorted.begin(), vSorted.end());
842 for (int i = 0; i < vSorted.size();)
846 bool fEntered = false;
847 TRY_CRITICAL_BLOCK(cs_mapWallet)
850 uint256& hash = vSorted[i++].second;
851 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
852 if (mi != mapWallet.end())
853 InsertTransaction((*mi).second, true);
855 if (!fEntered || i == 100 || i % 500 == 0)
859 printf("RefreshListCtrl done\n");
861 // Update transaction total display
866 // Check for time updates
867 static int64 nLastTime;
868 if (GetTime() > nLastTime + 30)
870 TRY_CRITICAL_BLOCK(cs_mapWallet)
872 nLastTime = GetTime();
873 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
875 CWalletTx& wtx = (*it).second;
876 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
877 InsertTransaction(wtx, false);
884 void CMainFrame::RefreshStatusColumn()
887 static CBlockIndex* pindexLastBest;
888 static unsigned int nLastRefreshed;
890 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
891 if (nTop == nLastTop && pindexLastBest == pindexBest)
894 TRY_CRITICAL_BLOCK(cs_mapWallet)
897 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
899 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
901 // If no updates, only need to do the part that moved onto the screen
902 if (nStart >= nLastTop && nStart < nLastTop + 100)
903 nStart = nLastTop + 100;
904 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
908 pindexLastBest = pindexBest;
909 nLastRefreshed = nListViewUpdated;
911 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
913 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
914 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
915 if (mi == mapWallet.end())
917 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
920 CWalletTx& wtx = (*mi).second;
921 if (wtx.IsCoinBase() ||
922 wtx.GetTxTime() != wtx.nTimeDisplayed ||
923 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
925 if (!InsertTransaction(wtx, false, nIndex))
926 m_listCtrl->DeleteItem(nIndex--);
930 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
936 void CMainFrame::OnPaint(wxPaintEvent& event)
947 unsigned int nNeedRepaint = 0;
948 unsigned int nLastRepaint = 0;
949 int64 nLastRepaintTime = 0;
950 int64 nRepaintInterval = 500;
952 void ThreadDelayedRepaint(void* parg)
956 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
958 nLastRepaint = nNeedRepaint;
961 printf("DelayedRepaint\n");
963 pframeMain->fRefresh = true;
964 pframeMain->GetEventHandler()->AddPendingEvent(event);
967 Sleep(nRepaintInterval);
971 void MainFrameRepaint()
973 // This is called by network code that shouldn't access pframeMain
974 // directly because it could still be running after the UI is closed.
977 // Don't repaint too often
978 static int64 nLastRepaintRequest;
979 if (GetTimeMillis() - nLastRepaintRequest < 100)
984 nLastRepaintRequest = GetTimeMillis();
986 printf("MainFrameRepaint\n");
988 pframeMain->fRefresh = true;
989 pframeMain->GetEventHandler()->AddPendingEvent(event);
993 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
995 // Skip lets the listctrl do the paint, we're just hooking the message
999 ptaskbaricon->UpdateTooltip();
1004 static int nTransactionCount;
1005 bool fPaintedBalance = false;
1006 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1008 nLastRepaint = nNeedRepaint;
1009 nLastRepaintTime = GetTimeMillis();
1011 // Update listctrl contents
1012 if (!vWalletUpdated.empty())
1014 TRY_CRITICAL_BLOCK(cs_mapWallet)
1017 if (m_listCtrl->GetItemCount())
1018 strTop = (string)m_listCtrl->GetItemText(0);
1019 BOOST_FOREACH(uint256 hash, vWalletUpdated)
1021 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1022 if (mi != mapWallet.end())
1023 InsertTransaction((*mi).second, false);
1025 vWalletUpdated.clear();
1026 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1027 m_listCtrl->ScrollList(0, INT_MIN/2);
1032 TRY_CRITICAL_BLOCK(cs_mapWallet)
1034 fPaintedBalance = true;
1035 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
1037 // Count hidden and multi-line transactions
1038 nTransactionCount = 0;
1039 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1041 CWalletTx& wtx = (*it).second;
1042 nTransactionCount += wtx.nLinesDisplayed;
1046 if (!vWalletUpdated.empty() || !fPaintedBalance)
1049 // Update status column of visible items only
1050 RefreshStatusColumn();
1052 // Update status bar
1053 static string strPrevWarning;
1054 string strWarning = GetWarnings("statusbar");
1055 if (strWarning != "")
1056 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1057 else if (strPrevWarning != "")
1058 m_statusBar->SetStatusText("", 0);
1059 strPrevWarning = strWarning;
1062 if (fGenerateBitcoins)
1063 strGen = _(" Generating");
1064 if (fGenerateBitcoins && vNodes.empty())
1065 strGen = _("(not connected)");
1066 m_statusBar->SetStatusText(strGen, 1);
1068 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1069 m_statusBar->SetStatusText(strStatus, 2);
1071 // Update receiving address
1072 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1073 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1074 m_textCtrlAddress->SetValue(strDefaultAddress);
1078 void UIThreadCall(boost::function0<void> fn)
1080 // Call this with a function object created with bind.
1081 // bind needs all parameters to match the function's expected types
1082 // and all default parameters specified. Some examples:
1083 // UIThreadCall(bind(wxBell));
1084 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1085 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1088 wxCommandEvent event(wxEVT_UITHREADCALL);
1089 event.SetClientData((void*)new boost::function0<void>(fn));
1090 pframeMain->GetEventHandler()->AddPendingEvent(event);
1094 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1096 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1101 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1107 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1109 event.Check(fGenerateBitcoins);
1112 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1114 // Options->Your Receiving Addresses
1115 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1116 if (!dialog.ShowModal())
1120 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1123 COptionsDialog dialog(this);
1127 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1130 CAboutDialog dialog(this);
1134 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1137 CSendDialog dialog(this);
1141 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1143 // Toolbar: Address Book
1144 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1145 if (dialogAddr.ShowModal() == 2)
1148 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1149 dialogSend.ShowModal();
1153 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1155 // Automatically select-all when entering window
1157 m_textCtrlAddress->SetSelection(-1, -1);
1158 fOnSetFocusAddress = true;
1161 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1164 if (fOnSetFocusAddress)
1165 m_textCtrlAddress->SetSelection(-1, -1);
1166 fOnSetFocusAddress = false;
1169 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1172 CGetTextFromUserDialog dialog(this,
1173 _("New Receiving Address"),
1174 _("You should use a new address for each payment you receive.\n\nLabel"),
1176 if (!dialog.ShowModal())
1178 string strName = dialog.GetValue();
1181 string strAddress = PubKeyToAddress(GetKeyFromKeyPool());
1184 SetAddressBookName(strAddress, strName);
1185 SetDefaultReceivingAddress(strAddress);
1188 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1190 // Copy address box to clipboard
1191 if (wxTheClipboard->Open())
1193 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1194 wxTheClipboard->Close();
1198 void CMainFrame::OnListItemActivated(wxListEvent& event)
1200 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1202 CRITICAL_BLOCK(cs_mapWallet)
1204 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1205 if (mi == mapWallet.end())
1207 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1212 CTxDetailsDialog dialog(this, wtx);
1214 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1223 //////////////////////////////////////////////////////////////////////////////
1228 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1231 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1233 CRITICAL_BLOCK(cs_mapAddressBook)
1236 strHTML.reserve(4000);
1237 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1239 int64 nTime = wtx.GetTxTime();
1240 int64 nCredit = wtx.GetCredit();
1241 int64 nDebit = wtx.GetDebit();
1242 int64 nNet = nCredit - nDebit;
1246 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1247 int nRequests = wtx.GetRequestCount();
1248 if (nRequests != -1)
1251 strHTML += _(", has not been successfully broadcast yet");
1252 else if (nRequests == 1)
1253 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1255 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1259 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1265 if (wtx.IsCoinBase())
1267 strHTML += _("<b>Source:</b> Generated<br>");
1269 else if (!wtx.mapValue["from"].empty())
1271 // Online transaction
1272 if (!wtx.mapValue["from"].empty())
1273 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1277 // Offline transaction
1281 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1285 vector<unsigned char> vchPubKey;
1286 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1288 string strAddress = PubKeyToAddress(vchPubKey);
1289 if (mapAddressBook.count(strAddress))
1291 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1292 strHTML += _("<b>To:</b> ");
1293 strHTML += HtmlEscape(strAddress);
1294 if (!mapAddressBook[strAddress].empty())
1295 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1297 strHTML += _(" (yours)");
1312 if (!wtx.mapValue["to"].empty())
1314 // Online transaction
1315 strAddress = wtx.mapValue["to"];
1316 strHTML += _("<b>To:</b> ");
1317 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1318 strHTML += mapAddressBook[strAddress] + " ";
1319 strHTML += HtmlEscape(strAddress) + "<br>";
1326 if (wtx.IsCoinBase() && nCredit == 0)
1331 int64 nUnmatured = 0;
1332 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1333 nUnmatured += txout.GetCredit();
1334 strHTML += _("<b>Credit:</b> ");
1335 if (wtx.IsInMainChain())
1336 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1338 strHTML += _("(not accepted)");
1346 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1350 bool fAllFromMe = true;
1351 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1352 fAllFromMe = fAllFromMe && txin.IsMine();
1354 bool fAllToMe = true;
1355 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1356 fAllToMe = fAllToMe && txout.IsMine();
1363 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1368 if (wtx.mapValue["to"].empty())
1370 // Offline transaction
1372 if (ExtractHash160(txout.scriptPubKey, hash160))
1374 string strAddress = Hash160ToAddress(hash160);
1375 strHTML += _("<b>To:</b> ");
1376 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1377 strHTML += mapAddressBook[strAddress] + " ";
1378 strHTML += strAddress;
1383 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1389 int64 nChange = wtx.GetChange();
1390 int64 nValue = nCredit - nChange;
1391 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1392 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1395 int64 nTxFee = nDebit - wtx.GetValueOut();
1397 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1402 // Mixed debit transaction
1404 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1406 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1407 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1409 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1413 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1419 if (!wtx.mapValue["message"].empty())
1420 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1421 if (!wtx.mapValue["comment"].empty())
1422 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1424 if (wtx.IsCoinBase())
1425 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>";
1433 strHTML += "<hr><br>debug print<br><br>";
1434 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1436 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1437 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1439 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1441 strHTML += "<br><b>Transaction:</b><br>";
1442 strHTML += HtmlEscape(wtx.ToString(), true);
1444 strHTML += "<br><b>Inputs:</b><br>";
1445 CRITICAL_BLOCK(cs_mapWallet)
1447 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1449 COutPoint prevout = txin.prevout;
1450 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1451 if (mi != mapWallet.end())
1453 const CWalletTx& prev = (*mi).second;
1454 if (prevout.n < prev.vout.size())
1456 strHTML += HtmlEscape(prev.ToString(), true);
1457 strHTML += " " + FormatTxStatus(prev) + ", ";
1458 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1467 strHTML += "</font></html>";
1468 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1469 m_htmlWin->SetPage(strHTML);
1470 m_buttonOK->SetFocus();
1474 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1484 //////////////////////////////////////////////////////////////////////////////
1490 string StartupShortcutPath()
1492 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1495 bool GetStartOnSystemStartup()
1497 return filesystem::exists(StartupShortcutPath().c_str());
1500 void SetStartOnSystemStartup(bool fAutoStart)
1502 // If the shortcut exists already, remove it for updating
1503 remove(StartupShortcutPath().c_str());
1509 // Get a pointer to the IShellLink interface.
1510 IShellLink* psl = NULL;
1511 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1512 CLSCTX_INPROC_SERVER, IID_IShellLink,
1513 reinterpret_cast<void**>(&psl));
1515 if (SUCCEEDED(hres))
1517 // Get the current executable path
1518 TCHAR pszExePath[MAX_PATH];
1519 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1521 // Set the path to the shortcut target
1522 psl->SetPath(pszExePath);
1523 PathRemoveFileSpec(pszExePath);
1524 psl->SetWorkingDirectory(pszExePath);
1525 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1527 // Query IShellLink for the IPersistFile interface for
1528 // saving the shortcut in persistent storage.
1529 IPersistFile* ppf = NULL;
1530 hres = psl->QueryInterface(IID_IPersistFile,
1531 reinterpret_cast<void**>(&ppf));
1532 if (SUCCEEDED(hres))
1534 WCHAR pwsz[MAX_PATH];
1535 // Ensure that the string is ANSI.
1536 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1537 // Save the link by calling IPersistFile::Save.
1538 hres = ppf->Save(pwsz, TRUE);
1547 #elif defined(__WXGTK__)
1549 // Follow the Desktop Application Autostart Spec:
1550 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1552 boost::filesystem::path GetAutostartDir()
1554 namespace fs = boost::filesystem;
1556 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1557 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1558 char* pszHome = getenv("HOME");
1559 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1563 boost::filesystem::path GetAutostartFilePath()
1565 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1568 bool GetStartOnSystemStartup()
1570 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1571 if (!optionFile.good())
1573 // Scan through file for "Hidden=true":
1575 while (!optionFile.eof())
1577 getline(optionFile, line);
1578 if (line.find("Hidden") != string::npos &&
1579 line.find("true") != string::npos)
1587 void SetStartOnSystemStartup(bool fAutoStart)
1591 unlink(GetAutostartFilePath().native_file_string().c_str());
1595 char pszExePath[MAX_PATH+1];
1596 memset(pszExePath, 0, sizeof(pszExePath));
1597 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1600 boost::filesystem::create_directories(GetAutostartDir());
1602 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1603 if (!optionFile.good())
1605 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1608 // Write a bitcoin.desktop file to the autostart directory:
1609 optionFile << "[Desktop Entry]\n";
1610 optionFile << "Type=Application\n";
1611 optionFile << "Name=Bitcoin\n";
1612 optionFile << "Exec=" << pszExePath << "\n";
1613 optionFile << "Terminal=false\n";
1614 optionFile << "Hidden=false\n";
1620 // TODO: OSX startup stuff; see:
1621 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1623 bool GetStartOnSystemStartup() { return false; }
1624 void SetStartOnSystemStartup(bool fAutoStart) { }
1633 //////////////////////////////////////////////////////////////////////////////
1638 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1640 // Set up list box of page choices
1641 m_listBox->Append(_("Main"));
1642 //m_listBox->Append(_("Test 2"));
1643 m_listBox->SetSelection(0);
1646 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1648 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1650 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1651 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1652 if (!GetBoolArg("-minimizetotray"))
1654 // Minimize to tray is just too buggy on Linux
1655 fMinimizeToTray = false;
1656 m_checkBoxMinimizeToTray->SetValue(false);
1657 m_checkBoxMinimizeToTray->Enable(false);
1658 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1661 #ifdef __WXMAC_OSX__
1662 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1666 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1667 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1668 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1669 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1671 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1673 m_checkBoxUseUPnP->Enable(false);
1674 m_checkBoxUseProxy->SetValue(fUseProxy);
1675 m_textCtrlProxyIP->Enable(fUseProxy);
1676 m_textCtrlProxyPort->Enable(fUseProxy);
1677 m_staticTextProxyIP->Enable(fUseProxy);
1678 m_staticTextProxyPort->Enable(fUseProxy);
1679 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1680 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1682 m_buttonOK->SetFocus();
1685 void COptionsDialog::SelectPage(int nPage)
1687 m_panelMain->Show(nPage == 0);
1688 m_panelTest2->Show(nPage == 1);
1690 m_scrolledWindow->Layout();
1691 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1694 void COptionsDialog::OnListBox(wxCommandEvent& event)
1696 SelectPage(event.GetSelection());
1699 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1702 int64 nTmp = nTransactionFee;
1703 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1704 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1707 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1709 m_textCtrlProxyIP->Enable(event.IsChecked());
1710 m_textCtrlProxyPort->Enable(event.IsChecked());
1711 m_staticTextProxyIP->Enable(event.IsChecked());
1712 m_staticTextProxyPort->Enable(event.IsChecked());
1715 CAddress COptionsDialog::GetProxyAddr()
1717 // Be careful about byte order, addr.ip and addr.port are big endian
1718 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1719 if (addr.ip == INADDR_NONE)
1720 addr.ip = addrProxy.ip;
1721 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1722 addr.port = htons(nPort);
1723 if (nPort <= 0 || nPort > USHRT_MAX)
1724 addr.port = addrProxy.port;
1728 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1731 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1732 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1736 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1738 OnButtonApply(event);
1742 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1747 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1751 int64 nPrevTransactionFee = nTransactionFee;
1752 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1753 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1755 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1757 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1758 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1761 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1763 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1764 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1765 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1768 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1770 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1771 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1774 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1776 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1777 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1781 fUseProxy = m_checkBoxUseProxy->GetValue();
1782 walletdb.WriteSetting("fUseProxy", fUseProxy);
1784 addrProxy = GetProxyAddr();
1785 walletdb.WriteSetting("addrProxy", addrProxy);
1793 //////////////////////////////////////////////////////////////////////////////
1798 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1800 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
1802 // Change (c) into UTF-8 or ANSI copyright symbol
1803 wxString str = m_staticTextMain->GetLabel();
1805 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1807 str.Replace("(c)", "\xA9");
1809 m_staticTextMain->SetLabel(str);
1811 // Resize on Linux to make the window fit the text.
1812 // The text was wrapped manually rather than using the Wrap setting because
1813 // the wrap would be too small on Linux and it can't be changed at this point.
1814 wxFont fontTmp = m_staticTextMain->GetFont();
1815 if (fontTmp.GetPointSize() > 8);
1816 fontTmp.SetPointSize(8);
1817 m_staticTextMain->SetFont(fontTmp);
1818 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1820 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1824 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1834 //////////////////////////////////////////////////////////////////////////////
1839 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1842 m_textCtrlAddress->SetValue(strAddress);
1843 m_choiceTransferType->SetSelection(0);
1844 m_bitmapCheckMark->Show(false);
1845 fEnabledPrev = true;
1846 m_textCtrlAddress->SetFocus();
1848 //// todo: should add a display of your balance for convenience
1850 wxFont fontTmp = m_staticTextInstructions->GetFont();
1851 if (fontTmp.GetPointSize() > 9);
1852 fontTmp.SetPointSize(9);
1853 m_staticTextInstructions->SetFont(fontTmp);
1856 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1860 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
1863 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1867 SetIcon(wxICON(bitcoin));
1869 // Fixup the tab order
1870 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1871 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1875 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1877 // Reformat the amount
1879 if (m_textCtrlAmount->GetValue().Trim().empty())
1882 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1883 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1886 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1888 // Open address book
1889 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1890 if (dialog.ShowModal())
1891 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1894 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1896 // Copy clipboard to address box
1897 if (wxTheClipboard->Open())
1899 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1901 wxTextDataObject data;
1902 wxTheClipboard->GetData(data);
1903 m_textCtrlAddress->SetValue(data.GetText());
1905 wxTheClipboard->Close();
1909 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1911 static CCriticalSection cs_sendlock;
1912 TRY_CRITICAL_BLOCK(cs_sendlock)
1915 string strAddress = (string)m_textCtrlAddress->GetValue();
1919 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1921 wxMessageBox(_("Error in amount "), _("Send Coins"));
1924 if (nValue > GetBalance())
1926 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1929 if (nValue + nTransactionFee > GetBalance())
1931 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1935 // Parse bitcoin address
1937 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1939 if (fBitcoinAddress)
1941 CRITICAL_BLOCK(cs_main)
1943 // Send to bitcoin address
1944 CScript scriptPubKey;
1945 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1947 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1949 wxMessageBox(_("Payment sent "), _("Sending..."));
1950 else if (strError == "ABORTED")
1951 return; // leave send dialog open
1954 wxMessageBox(strError + " ", _("Sending..."));
1963 CAddress addr(strAddress);
1964 if (!addr.IsValid())
1966 wxMessageBox(_("Invalid address "), _("Send Coins"));
1971 wtx.mapValue["to"] = strAddress;
1973 // Send to IP address
1974 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1975 if (!pdialog->ShowModal())
1979 CRITICAL_BLOCK(cs_mapAddressBook)
1980 if (!mapAddressBook.count(strAddress))
1981 SetAddressBookName(strAddress, "");
1987 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1998 //////////////////////////////////////////////////////////////////////////////
2003 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
2008 start = wxDateTime::UNow();
2009 memset(pszStatus, 0, sizeof(pszStatus));
2016 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2018 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2021 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2022 m_textCtrlStatus->SetValue("");
2024 CreateThread(SendingDialogStartTransfer, this);
2027 CSendingDialog::~CSendingDialog()
2029 printf("~CSendingDialog()\n");
2032 void CSendingDialog::Close()
2034 // Last one out turn out the lights.
2035 // fWorkDone signals that work side is done and UI thread should call destroy.
2036 // fUIDone signals that UI window has closed and work thread should call destroy.
2037 // This allows the window to disappear and end modality when cancelled
2038 // without making the user wait for ConnectNode to return. The dialog object
2039 // hangs around in the background until the work thread exits.
2050 void CSendingDialog::OnClose(wxCloseEvent& event)
2052 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2059 wxCommandEvent cmdevent;
2060 OnButtonCancel(cmdevent);
2064 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2070 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2076 void CSendingDialog::OnPaint(wxPaintEvent& event)
2079 if (strlen(pszStatus) > 130)
2080 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2082 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2083 m_staticTextSending->SetFocus();
2085 m_buttonCancel->Enable(false);
2088 m_buttonOK->Enable(true);
2089 m_buttonOK->SetFocus();
2090 m_buttonCancel->Enable(false);
2092 if (fAbort && fCanCancel && IsShown())
2094 strcpy(pszStatus, _("CANCELLED"));
2095 m_buttonOK->Enable(true);
2096 m_buttonOK->SetFocus();
2097 m_buttonCancel->Enable(false);
2098 m_buttonCancel->SetLabel(_("Cancelled"));
2100 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2106 // Everything from here on is not in the UI thread and must only communicate
2107 // with the rest of the dialog through variables and calling repaint.
2110 void CSendingDialog::Repaint()
2114 GetEventHandler()->AddPendingEvent(event);
2117 bool CSendingDialog::Status()
2124 if (fAbort && fCanCancel)
2126 memset(pszStatus, 0, 10);
2127 strcpy(pszStatus, _("CANCELLED"));
2135 bool CSendingDialog::Status(const string& str)
2140 // This can be read by the UI thread at any time,
2141 // so copy in a way that can be read cleanly at all times.
2142 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2143 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2149 bool CSendingDialog::Error(const string& str)
2153 Status(string(_("Error: ")) + str);
2157 void SendingDialogStartTransfer(void* parg)
2159 ((CSendingDialog*)parg)->StartTransfer();
2162 void CSendingDialog::StartTransfer()
2164 // Make sure we have enough money
2165 if (nPrice + nTransactionFee > GetBalance())
2167 Error(_("Insufficient funds"));
2171 // We may have connected already for product details
2172 if (!Status(_("Connecting...")))
2174 CNode* pnode = ConnectNode(addr, 15 * 60);
2177 Error(_("Unable to connect"));
2181 // Send order to seller, with response going to OnReply2 via event handler
2182 if (!Status(_("Requesting public key...")))
2184 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2187 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2189 ((CSendingDialog*)parg)->OnReply2(vRecv);
2192 void CSendingDialog::OnReply2(CDataStream& vRecv)
2194 if (!Status(_("Received public key...")))
2197 CScript scriptPubKey;
2206 vRecv >> strMessage;
2208 Error(_("Recipient is not accepting transactions sent by IP address"));
2210 Error(_("Transfer was not accepted"));
2211 //// todo: enlarge the window and enable a hidden white box to put seller's message
2214 vRecv >> scriptPubKey;
2218 //// what do we want to do about this?
2219 Error(_("Invalid response received"));
2223 // Pause to give the user a chance to cancel
2224 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2231 CRITICAL_BLOCK(cs_main)
2234 if (!Status(_("Creating transaction...")))
2236 if (nPrice + nTransactionFee > GetBalance())
2238 Error(_("Insufficient funds"));
2241 CReserveKey reservekey;
2243 if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2245 if (nPrice + nFeeRequired > GetBalance())
2246 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()));
2248 Error(_("Transaction creation failed"));
2253 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2255 Error(_("Transaction aborted"));
2259 // Make sure we're still connected
2260 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2263 Error(_("Lost connection, transaction cancelled"));
2267 // Last chance to cancel
2279 if (!Status(_("Sending payment...")))
2283 if (!CommitTransaction(wtx, reservekey))
2285 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."));
2289 // Send payment tx to seller, with response going to OnReply3 via event handler
2290 CWalletTx wtxSend = wtx;
2291 wtxSend.fFromMe = false;
2292 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2294 Status(_("Waiting for confirmation..."));
2299 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2301 ((CSendingDialog*)parg)->OnReply3(vRecv);
2304 void CSendingDialog::OnReply3(CDataStream& vRecv)
2312 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2313 "The transaction is recorded and will credit to the recipient,\n"
2314 "but the comment information will be blank."));
2320 //// what do we want to do about this?
2321 Error(_("Payment was sent, but an invalid response was received"));
2327 Status(_("Payment completed"));
2335 //////////////////////////////////////////////////////////////////////////////
2337 // CAddressBookDialog
2340 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2343 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2346 // Set initially selected page
2347 wxNotebookEvent event;
2348 event.SetSelection(nPageIn);
2349 OnNotebookPageChanged(event);
2350 m_notebook->ChangeSelection(nPageIn);
2352 fDuringSend = fDuringSendIn;
2354 m_buttonCancel->Show(false);
2357 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2359 wxIcon iconAddressBook;
2360 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2361 SetIcon(iconAddressBook);
2364 SetIcon(wxICON(bitcoin));
2366 // Init column headers
2367 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2368 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2369 m_listCtrlSending->SetFocus();
2370 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2371 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2372 m_listCtrlReceiving->SetFocus();
2374 // Fill listctrl with address book data
2375 CRITICAL_BLOCK(cs_mapKeys)
2376 CRITICAL_BLOCK(cs_mapAddressBook)
2378 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2379 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook)
2381 string strAddress = item.first;
2382 string strName = item.second;
2384 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2385 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2386 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2387 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2388 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2393 wxString CAddressBookDialog::GetSelectedAddress()
2395 int nIndex = GetSelection(m_listCtrl);
2398 return GetItemText(m_listCtrl, nIndex, 1);
2401 wxString CAddressBookDialog::GetSelectedSendingAddress()
2403 int nIndex = GetSelection(m_listCtrlSending);
2406 return GetItemText(m_listCtrlSending, nIndex, 1);
2409 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2411 int nIndex = GetSelection(m_listCtrlReceiving);
2414 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2417 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2420 nPage = event.GetSelection();
2421 if (nPage == SENDING)
2422 m_listCtrl = m_listCtrlSending;
2423 else if (nPage == RECEIVING)
2424 m_listCtrl = m_listCtrlReceiving;
2425 m_buttonDelete->Show(nPage == SENDING);
2426 m_buttonCopy->Show(nPage == RECEIVING);
2428 m_listCtrl->SetFocus();
2431 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2433 // Update address book with edited name
2435 if (event.IsEditCancelled())
2437 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2438 SetAddressBookName(strAddress, string(event.GetText()));
2439 pframeMain->RefreshListCtrl();
2442 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2445 if (nPage == RECEIVING)
2446 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2449 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2454 // Doubleclick returns selection
2455 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2459 // Doubleclick edits item
2460 wxCommandEvent event2;
2461 OnButtonEdit(event2);
2464 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2466 if (nPage != SENDING)
2468 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2470 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2472 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2473 CWalletDB().EraseName(strAddress);
2474 m_listCtrl->DeleteItem(nIndex);
2477 pframeMain->RefreshListCtrl();
2480 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2482 // Copy address box to clipboard
2483 if (wxTheClipboard->Open())
2485 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2486 wxTheClipboard->Close();
2490 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2493 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2495 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2499 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2501 int nIndex = GetSelection(m_listCtrl);
2504 string strName = (string)m_listCtrl->GetItemText(nIndex);
2505 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2506 string strAddressOrg = strAddress;
2508 if (nPage == SENDING)
2510 // Ask name and address
2513 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2514 if (!dialog.ShowModal())
2516 strName = dialog.GetValue1();
2517 strAddress = dialog.GetValue2();
2519 while (CheckIfMine(strAddress, _("Edit Address")));
2522 else if (nPage == RECEIVING)
2525 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2526 if (!dialog.ShowModal())
2528 strName = dialog.GetValue();
2532 if (strAddress != strAddressOrg)
2533 CWalletDB().EraseName(strAddressOrg);
2534 SetAddressBookName(strAddress, strName);
2535 m_listCtrl->SetItem(nIndex, 1, strAddress);
2536 m_listCtrl->SetItemText(nIndex, strName);
2537 pframeMain->RefreshListCtrl();
2540 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2545 if (nPage == SENDING)
2547 // Ask name and address
2550 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2551 if (!dialog.ShowModal())
2553 strName = dialog.GetValue1();
2554 strAddress = dialog.GetValue2();
2556 while (CheckIfMine(strAddress, _("Add Address")));
2558 else if (nPage == RECEIVING)
2561 CGetTextFromUserDialog dialog(this,
2562 _("New Receiving Address"),
2563 _("You should use a new address for each payment you receive.\n\nLabel"),
2565 if (!dialog.ShowModal())
2567 strName = dialog.GetValue();
2570 strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2573 // Add to list and select it
2574 SetAddressBookName(strAddress, strName);
2575 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2576 SetSelection(m_listCtrl, nIndex);
2577 m_listCtrl->SetFocus();
2578 if (nPage == SENDING)
2579 pframeMain->RefreshListCtrl();
2582 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2585 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2588 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2594 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2605 //////////////////////////////////////////////////////////////////////////////
2612 ID_TASKBAR_RESTORE = 10001,
2615 ID_TASKBAR_GENERATE,
2619 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2620 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2621 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2622 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2623 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2624 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2625 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2628 void CMyTaskBarIcon::Show(bool fShow)
2630 static char pszPrevTip[200];
2633 string strTooltip = _("Bitcoin");
2634 if (fGenerateBitcoins)
2635 strTooltip = _("Bitcoin - Generating");
2636 if (fGenerateBitcoins && vNodes.empty())
2637 strTooltip = _("Bitcoin - (not connected)");
2639 // Optimization, only update when changed, using char array to be reentrant
2640 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2642 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2644 // somehow it'll choose the wrong size and scale it down if
2645 // we use the main icon, so we hand it one with only 16x16
2646 SetIcon(wxICON(favicon), strTooltip);
2648 SetIcon(bitcoin80_xpm, strTooltip);
2654 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2659 void CMyTaskBarIcon::Hide()
2664 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2669 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2674 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2677 CSendDialog dialog(pframeMain);
2681 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2683 // Since it's modal, get the main window to do it
2684 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2685 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2688 void CMyTaskBarIcon::Restore()
2691 wxIconizeEvent event(0, false);
2692 pframeMain->GetEventHandler()->AddPendingEvent(event);
2693 pframeMain->Iconize(false);
2694 pframeMain->Raise();
2697 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2699 event.Check(fGenerateBitcoins);
2702 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2704 pframeMain->Close(true);
2707 void CMyTaskBarIcon::UpdateTooltip()
2709 if (IsIconInstalled())
2713 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2715 wxMenu* pmenu = new wxMenu;
2716 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2717 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2718 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2719 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2720 pmenu->AppendSeparator();
2721 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2731 //////////////////////////////////////////////////////////////////////////////
2736 void CreateMainWindow()
2738 pframeMain = new CMainFrame(NULL);
2739 if (GetBoolArg("-min"))
2740 pframeMain->Iconize(true);
2741 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2742 if (!GetBoolArg("-minimizetotray"))
2743 fMinimizeToTray = false;
2745 pframeMain->Show(true); // have to show first to get taskbar button to hide
2746 if (fMinimizeToTray && pframeMain->IsIconized())
2747 fClosedToTray = true;
2748 pframeMain->Show(!fClosedToTray);
2749 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2750 CreateThread(ThreadDelayedRepaint, NULL);
2754 // Define a new application
2755 class CMyApp : public wxApp
2764 // Hook Initialize so we can start without GUI
2765 virtual bool Initialize(int& argc, wxChar** argv);
2767 // 2nd-level exception handling: we get all the exceptions occurring in any
2768 // event handler here
2769 virtual bool OnExceptionInMainLoop();
2771 // 3rd, and final, level exception handling: whenever an unhandled
2772 // exception is caught, this function is called
2773 virtual void OnUnhandledException();
2775 // and now for something different: this function is called in case of a
2776 // crash (e.g. dereferencing null pointer, division by 0, ...)
2777 virtual void OnFatalException();
2780 IMPLEMENT_APP(CMyApp)
2782 bool CMyApp::Initialize(int& argc, wxChar** argv)
2784 for (int i = 1; i < argc; i++)
2785 if (!IsSwitchChar(argv[i][0]))
2786 fCommandLine = true;
2790 // wxApp::Initialize will remove environment-specific parameters,
2791 // so it's too early to call ParseParameters yet
2792 for (int i = 1; i < argc; i++)
2794 wxString str = argv[i];
2796 if (str.size() >= 1 && str[0] == '/')
2798 char pszLower[MAX_PATH];
2799 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2803 if (str == "-daemon")
2809 if (fDaemon || fCommandLine)
2811 // Call the original Initialize while suppressing error messages
2812 // and ignoring failure. If unable to initialize GTK, it fails
2813 // near the end so hopefully the last few things don't matter.
2816 wxApp::Initialize(argc, argv);
2825 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2829 pthread_exit((void*)0);
2831 pid_t sid = setsid();
2833 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
2840 return wxApp::Initialize(argc, argv);
2843 bool CMyApp::OnInit()
2845 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2846 // Disable malfunctioning wxWidgets debug assertion
2847 extern int g_isPainting;
2848 g_isPainting = 10000;
2850 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2851 SetAppName("Bitcoin");
2853 SetAppName("bitcoin");
2857 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2858 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2859 class wxMBConv_win32 : public wxMBConv
2863 size_t m_minMBCharWidth;
2865 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2866 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2870 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2871 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2872 g_locale.AddCatalogLookupPathPrefix("locale");
2874 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2875 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2877 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2878 g_locale.AddCatalog("bitcoin");
2881 HDC hdc = GetDC(NULL);
2884 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
2885 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
2886 ReleaseDC(NULL, hdc);
2890 return AppInit(argc, argv);
2893 int CMyApp::OnExit()
2896 return wxApp::OnExit();
2899 bool CMyApp::OnExceptionInMainLoop()
2905 catch (std::exception& e)
2907 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2908 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2914 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2915 wxLogWarning("Unknown exception");
2922 void CMyApp::OnUnhandledException()
2924 // this shows how we may let some exception propagate uncaught
2929 catch (std::exception& e)
2931 PrintException(&e, "CMyApp::OnUnhandledException()");
2932 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2938 PrintException(NULL, "CMyApp::OnUnhandledException()");
2939 wxLogWarning("Unknown exception");
2945 void CMyApp::OnFatalException()
2947 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);