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));
1868 SetIcon(wxICON(bitcoin));
1871 // Fixup the tab order
1872 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1873 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1877 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1879 // Reformat the amount
1881 if (m_textCtrlAmount->GetValue().Trim().empty())
1884 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1885 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1888 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1890 // Open address book
1891 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1892 if (dialog.ShowModal())
1893 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1896 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1898 // Copy clipboard to address box
1899 if (wxTheClipboard->Open())
1901 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1903 wxTextDataObject data;
1904 wxTheClipboard->GetData(data);
1905 m_textCtrlAddress->SetValue(data.GetText());
1907 wxTheClipboard->Close();
1911 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1913 static CCriticalSection cs_sendlock;
1914 TRY_CRITICAL_BLOCK(cs_sendlock)
1917 string strAddress = (string)m_textCtrlAddress->GetValue();
1921 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1923 wxMessageBox(_("Error in amount "), _("Send Coins"));
1926 if (nValue > GetBalance())
1928 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1931 if (nValue + nTransactionFee > GetBalance())
1933 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1937 // Parse bitcoin address
1939 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1941 if (fBitcoinAddress)
1943 CRITICAL_BLOCK(cs_main)
1945 // Send to bitcoin address
1946 CScript scriptPubKey;
1947 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1949 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1951 wxMessageBox(_("Payment sent "), _("Sending..."));
1952 else if (strError == "ABORTED")
1953 return; // leave send dialog open
1956 wxMessageBox(strError + " ", _("Sending..."));
1965 CAddress addr(strAddress);
1966 if (!addr.IsValid())
1968 wxMessageBox(_("Invalid address "), _("Send Coins"));
1973 wtx.mapValue["to"] = strAddress;
1975 // Send to IP address
1976 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1977 if (!pdialog->ShowModal())
1981 CRITICAL_BLOCK(cs_mapAddressBook)
1982 if (!mapAddressBook.count(strAddress))
1983 SetAddressBookName(strAddress, "");
1989 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2000 //////////////////////////////////////////////////////////////////////////////
2005 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
2010 start = wxDateTime::UNow();
2011 memset(pszStatus, 0, sizeof(pszStatus));
2018 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2020 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2023 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2024 m_textCtrlStatus->SetValue("");
2026 CreateThread(SendingDialogStartTransfer, this);
2029 CSendingDialog::~CSendingDialog()
2031 printf("~CSendingDialog()\n");
2034 void CSendingDialog::Close()
2036 // Last one out turn out the lights.
2037 // fWorkDone signals that work side is done and UI thread should call destroy.
2038 // fUIDone signals that UI window has closed and work thread should call destroy.
2039 // This allows the window to disappear and end modality when cancelled
2040 // without making the user wait for ConnectNode to return. The dialog object
2041 // hangs around in the background until the work thread exits.
2052 void CSendingDialog::OnClose(wxCloseEvent& event)
2054 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2061 wxCommandEvent cmdevent;
2062 OnButtonCancel(cmdevent);
2066 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2072 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2078 void CSendingDialog::OnPaint(wxPaintEvent& event)
2081 if (strlen(pszStatus) > 130)
2082 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2084 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2085 m_staticTextSending->SetFocus();
2087 m_buttonCancel->Enable(false);
2090 m_buttonOK->Enable(true);
2091 m_buttonOK->SetFocus();
2092 m_buttonCancel->Enable(false);
2094 if (fAbort && fCanCancel && IsShown())
2096 strcpy(pszStatus, _("CANCELLED"));
2097 m_buttonOK->Enable(true);
2098 m_buttonOK->SetFocus();
2099 m_buttonCancel->Enable(false);
2100 m_buttonCancel->SetLabel(_("Cancelled"));
2102 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2108 // Everything from here on is not in the UI thread and must only communicate
2109 // with the rest of the dialog through variables and calling repaint.
2112 void CSendingDialog::Repaint()
2116 GetEventHandler()->AddPendingEvent(event);
2119 bool CSendingDialog::Status()
2126 if (fAbort && fCanCancel)
2128 memset(pszStatus, 0, 10);
2129 strcpy(pszStatus, _("CANCELLED"));
2137 bool CSendingDialog::Status(const string& str)
2142 // This can be read by the UI thread at any time,
2143 // so copy in a way that can be read cleanly at all times.
2144 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2145 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2151 bool CSendingDialog::Error(const string& str)
2155 Status(string(_("Error: ")) + str);
2159 void SendingDialogStartTransfer(void* parg)
2161 ((CSendingDialog*)parg)->StartTransfer();
2164 void CSendingDialog::StartTransfer()
2166 // Make sure we have enough money
2167 if (nPrice + nTransactionFee > GetBalance())
2169 Error(_("Insufficient funds"));
2173 // We may have connected already for product details
2174 if (!Status(_("Connecting...")))
2176 CNode* pnode = ConnectNode(addr, 15 * 60);
2179 Error(_("Unable to connect"));
2183 // Send order to seller, with response going to OnReply2 via event handler
2184 if (!Status(_("Requesting public key...")))
2186 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2189 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2191 ((CSendingDialog*)parg)->OnReply2(vRecv);
2194 void CSendingDialog::OnReply2(CDataStream& vRecv)
2196 if (!Status(_("Received public key...")))
2199 CScript scriptPubKey;
2208 vRecv >> strMessage;
2210 Error(_("Recipient is not accepting transactions sent by IP address"));
2212 Error(_("Transfer was not accepted"));
2213 //// todo: enlarge the window and enable a hidden white box to put seller's message
2216 vRecv >> scriptPubKey;
2220 //// what do we want to do about this?
2221 Error(_("Invalid response received"));
2225 // Pause to give the user a chance to cancel
2226 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2233 CRITICAL_BLOCK(cs_main)
2236 if (!Status(_("Creating transaction...")))
2238 if (nPrice + nTransactionFee > GetBalance())
2240 Error(_("Insufficient funds"));
2243 CReserveKey reservekey;
2245 if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2247 if (nPrice + nFeeRequired > GetBalance())
2248 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()));
2250 Error(_("Transaction creation failed"));
2255 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2257 Error(_("Transaction aborted"));
2261 // Make sure we're still connected
2262 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2265 Error(_("Lost connection, transaction cancelled"));
2269 // Last chance to cancel
2281 if (!Status(_("Sending payment...")))
2285 if (!CommitTransaction(wtx, reservekey))
2287 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."));
2291 // Send payment tx to seller, with response going to OnReply3 via event handler
2292 CWalletTx wtxSend = wtx;
2293 wtxSend.fFromMe = false;
2294 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2296 Status(_("Waiting for confirmation..."));
2301 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2303 ((CSendingDialog*)parg)->OnReply3(vRecv);
2306 void CSendingDialog::OnReply3(CDataStream& vRecv)
2314 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2315 "The transaction is recorded and will credit to the recipient,\n"
2316 "but the comment information will be blank."));
2322 //// what do we want to do about this?
2323 Error(_("Payment was sent, but an invalid response was received"));
2329 Status(_("Payment completed"));
2337 //////////////////////////////////////////////////////////////////////////////
2339 // CAddressBookDialog
2342 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2345 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2348 // Set initially selected page
2349 wxNotebookEvent event;
2350 event.SetSelection(nPageIn);
2351 OnNotebookPageChanged(event);
2352 m_notebook->ChangeSelection(nPageIn);
2354 fDuringSend = fDuringSendIn;
2356 m_buttonCancel->Show(false);
2359 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2361 wxIcon iconAddressBook;
2362 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2363 SetIcon(iconAddressBook);
2367 SetIcon(wxICON(bitcoin));
2370 // Init column headers
2371 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2372 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2373 m_listCtrlSending->SetFocus();
2374 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2375 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2376 m_listCtrlReceiving->SetFocus();
2378 // Fill listctrl with address book data
2379 CRITICAL_BLOCK(cs_mapKeys)
2380 CRITICAL_BLOCK(cs_mapAddressBook)
2382 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2383 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook)
2385 string strAddress = item.first;
2386 string strName = item.second;
2388 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2389 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2390 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2391 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2392 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2397 wxString CAddressBookDialog::GetSelectedAddress()
2399 int nIndex = GetSelection(m_listCtrl);
2402 return GetItemText(m_listCtrl, nIndex, 1);
2405 wxString CAddressBookDialog::GetSelectedSendingAddress()
2407 int nIndex = GetSelection(m_listCtrlSending);
2410 return GetItemText(m_listCtrlSending, nIndex, 1);
2413 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2415 int nIndex = GetSelection(m_listCtrlReceiving);
2418 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2421 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2424 nPage = event.GetSelection();
2425 if (nPage == SENDING)
2426 m_listCtrl = m_listCtrlSending;
2427 else if (nPage == RECEIVING)
2428 m_listCtrl = m_listCtrlReceiving;
2429 m_buttonDelete->Show(nPage == SENDING);
2430 m_buttonCopy->Show(nPage == RECEIVING);
2432 m_listCtrl->SetFocus();
2435 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2437 // Update address book with edited name
2439 if (event.IsEditCancelled())
2441 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2442 SetAddressBookName(strAddress, string(event.GetText()));
2443 pframeMain->RefreshListCtrl();
2446 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2449 if (nPage == RECEIVING)
2450 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2453 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2458 // Doubleclick returns selection
2459 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2463 // Doubleclick edits item
2464 wxCommandEvent event2;
2465 OnButtonEdit(event2);
2468 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2470 if (nPage != SENDING)
2472 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2474 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2476 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2477 CWalletDB().EraseName(strAddress);
2478 m_listCtrl->DeleteItem(nIndex);
2481 pframeMain->RefreshListCtrl();
2484 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2486 // Copy address box to clipboard
2487 if (wxTheClipboard->Open())
2489 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2490 wxTheClipboard->Close();
2494 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2497 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2499 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2503 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2505 int nIndex = GetSelection(m_listCtrl);
2508 string strName = (string)m_listCtrl->GetItemText(nIndex);
2509 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2510 string strAddressOrg = strAddress;
2512 if (nPage == SENDING)
2514 // Ask name and address
2517 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2518 if (!dialog.ShowModal())
2520 strName = dialog.GetValue1();
2521 strAddress = dialog.GetValue2();
2523 while (CheckIfMine(strAddress, _("Edit Address")));
2526 else if (nPage == RECEIVING)
2529 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2530 if (!dialog.ShowModal())
2532 strName = dialog.GetValue();
2536 if (strAddress != strAddressOrg)
2537 CWalletDB().EraseName(strAddressOrg);
2538 SetAddressBookName(strAddress, strName);
2539 m_listCtrl->SetItem(nIndex, 1, strAddress);
2540 m_listCtrl->SetItemText(nIndex, strName);
2541 pframeMain->RefreshListCtrl();
2544 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2549 if (nPage == SENDING)
2551 // Ask name and address
2554 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2555 if (!dialog.ShowModal())
2557 strName = dialog.GetValue1();
2558 strAddress = dialog.GetValue2();
2560 while (CheckIfMine(strAddress, _("Add Address")));
2562 else if (nPage == RECEIVING)
2565 CGetTextFromUserDialog dialog(this,
2566 _("New Receiving Address"),
2567 _("You should use a new address for each payment you receive.\n\nLabel"),
2569 if (!dialog.ShowModal())
2571 strName = dialog.GetValue();
2574 strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2577 // Add to list and select it
2578 SetAddressBookName(strAddress, strName);
2579 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2580 SetSelection(m_listCtrl, nIndex);
2581 m_listCtrl->SetFocus();
2582 if (nPage == SENDING)
2583 pframeMain->RefreshListCtrl();
2586 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2589 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2592 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2598 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2609 //////////////////////////////////////////////////////////////////////////////
2616 ID_TASKBAR_RESTORE = 10001,
2619 ID_TASKBAR_GENERATE,
2623 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2624 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2625 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2626 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2627 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2628 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2629 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2632 void CMyTaskBarIcon::Show(bool fShow)
2634 static char pszPrevTip[200];
2637 string strTooltip = _("Bitcoin");
2638 if (fGenerateBitcoins)
2639 strTooltip = _("Bitcoin - Generating");
2640 if (fGenerateBitcoins && vNodes.empty())
2641 strTooltip = _("Bitcoin - (not connected)");
2643 // Optimization, only update when changed, using char array to be reentrant
2644 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2646 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2648 // somehow it'll choose the wrong size and scale it down if
2649 // we use the main icon, so we hand it one with only 16x16
2650 SetIcon(wxICON(favicon), strTooltip);
2652 SetIcon(bitcoin80_xpm, strTooltip);
2658 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2663 void CMyTaskBarIcon::Hide()
2668 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2673 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2678 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2681 CSendDialog dialog(pframeMain);
2685 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2687 // Since it's modal, get the main window to do it
2688 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2689 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2692 void CMyTaskBarIcon::Restore()
2695 wxIconizeEvent event(0, false);
2696 pframeMain->GetEventHandler()->AddPendingEvent(event);
2697 pframeMain->Iconize(false);
2698 pframeMain->Raise();
2701 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2703 event.Check(fGenerateBitcoins);
2706 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2708 pframeMain->Close(true);
2711 void CMyTaskBarIcon::UpdateTooltip()
2713 if (IsIconInstalled())
2717 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2719 wxMenu* pmenu = new wxMenu;
2720 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2721 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2722 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2723 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2724 pmenu->AppendSeparator();
2725 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2735 //////////////////////////////////////////////////////////////////////////////
2740 void CreateMainWindow()
2742 pframeMain = new CMainFrame(NULL);
2743 if (GetBoolArg("-min"))
2744 pframeMain->Iconize(true);
2745 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2746 if (!GetBoolArg("-minimizetotray"))
2747 fMinimizeToTray = false;
2749 pframeMain->Show(true); // have to show first to get taskbar button to hide
2750 if (fMinimizeToTray && pframeMain->IsIconized())
2751 fClosedToTray = true;
2752 pframeMain->Show(!fClosedToTray);
2753 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2754 CreateThread(ThreadDelayedRepaint, NULL);
2758 // Define a new application
2759 class CMyApp : public wxApp
2768 // Hook Initialize so we can start without GUI
2769 virtual bool Initialize(int& argc, wxChar** argv);
2771 // 2nd-level exception handling: we get all the exceptions occurring in any
2772 // event handler here
2773 virtual bool OnExceptionInMainLoop();
2775 // 3rd, and final, level exception handling: whenever an unhandled
2776 // exception is caught, this function is called
2777 virtual void OnUnhandledException();
2779 // and now for something different: this function is called in case of a
2780 // crash (e.g. dereferencing null pointer, division by 0, ...)
2781 virtual void OnFatalException();
2784 IMPLEMENT_APP(CMyApp)
2786 bool CMyApp::Initialize(int& argc, wxChar** argv)
2788 for (int i = 1; i < argc; i++)
2789 if (!IsSwitchChar(argv[i][0]))
2790 fCommandLine = true;
2794 // wxApp::Initialize will remove environment-specific parameters,
2795 // so it's too early to call ParseParameters yet
2796 for (int i = 1; i < argc; i++)
2798 wxString str = argv[i];
2800 if (str.size() >= 1 && str[0] == '/')
2802 char pszLower[MAX_PATH];
2803 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2807 if (str == "-daemon")
2813 if (fDaemon || fCommandLine)
2815 // Call the original Initialize while suppressing error messages
2816 // and ignoring failure. If unable to initialize GTK, it fails
2817 // near the end so hopefully the last few things don't matter.
2820 wxApp::Initialize(argc, argv);
2829 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2833 pthread_exit((void*)0);
2835 pid_t sid = setsid();
2837 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
2844 return wxApp::Initialize(argc, argv);
2847 bool CMyApp::OnInit()
2849 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2850 // Disable malfunctioning wxWidgets debug assertion
2851 extern int g_isPainting;
2852 g_isPainting = 10000;
2854 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2855 SetAppName("Bitcoin");
2857 SetAppName("bitcoin");
2861 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2862 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2863 class wxMBConv_win32 : public wxMBConv
2867 size_t m_minMBCharWidth;
2869 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2870 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2874 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2875 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2876 g_locale.AddCatalogLookupPathPrefix("locale");
2878 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2879 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2881 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2882 g_locale.AddCatalog("bitcoin");
2885 HDC hdc = GetDC(NULL);
2888 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
2889 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
2890 ReleaseDC(NULL, hdc);
2894 return AppInit(argc, argv);
2897 int CMyApp::OnExit()
2900 return wxApp::OnExit();
2903 bool CMyApp::OnExceptionInMainLoop()
2909 catch (std::exception& e)
2911 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2912 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2918 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2919 wxLogWarning("Unknown exception");
2926 void CMyApp::OnUnhandledException()
2928 // this shows how we may let some exception propagate uncaught
2933 catch (std::exception& e)
2935 PrintException(&e, "CMyApp::OnUnhandledException()");
2936 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2942 PrintException(NULL, "CMyApp::OnUnhandledException()");
2943 wxLogWarning("Unknown exception");
2949 void CMyApp::OnFatalException()
2951 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);