1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
9 #include <boost/filesystem/fstream.hpp>
10 #include <boost/filesystem/convenience.hpp>
16 using namespace boost;
19 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
21 CMainFrame* pframeMain = NULL;
22 CMyTaskBarIcon* ptaskbaricon = NULL;
23 bool fClosedToTray = false;
30 static const double nScaleX = 1.0;
31 static const double nScaleY = 1.0;
41 //////////////////////////////////////////////////////////////////////////////
46 void HandleCtrlA(wxKeyEvent& event)
50 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
51 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
52 textCtrl->SetSelection(-1, -1);
57 //char pszHourFormat[256];
58 //pszHourFormat[0] = '\0';
59 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
60 //return (pszHourFormat[0] != '0');
64 string DateStr(int64 nTime)
66 // Can only be used safely here in the UI
67 return (string)wxDateTime((time_t)nTime).FormatDate();
70 string DateTimeStr(int64 nTime)
72 // Can only be used safely here in the UI
73 wxDateTime datetime((time_t)nTime);
75 return (string)datetime.Format("%x %H:%M");
77 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
80 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
82 // Helper to simplify access to listctrl
84 item.m_itemId = nIndex;
86 item.m_mask = wxLIST_MASK_TEXT;
87 if (!listCtrl->GetItem(item))
89 return item.GetText();
92 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
94 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
95 listCtrl->SetItem(nIndex, 1, str1);
99 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
101 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
102 listCtrl->SetItem(nIndex, 1, str1);
103 listCtrl->SetItem(nIndex, 2, str2);
104 listCtrl->SetItem(nIndex, 3, str3);
105 listCtrl->SetItem(nIndex, 4, str4);
109 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
111 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
112 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
113 listCtrl->SetItem(nIndex, 1, str1);
114 listCtrl->SetItem(nIndex, 2, str2);
115 listCtrl->SetItem(nIndex, 3, str3);
116 listCtrl->SetItem(nIndex, 4, str4);
120 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
122 // Repaint on Windows is more flickery if the colour has ever been set,
123 // so don't want to set it unless it's different. Default colour has
124 // alpha 0 transparent, so our colours don't match using operator==.
125 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
127 c1 = wxColour(0,0,0);
128 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
129 listCtrl->SetItemTextColour(nIndex, colour);
132 void SetSelection(wxListCtrl* listCtrl, int nIndex)
134 int nSize = listCtrl->GetItemCount();
135 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
136 for (int i = 0; i < nSize; i++)
137 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
140 int GetSelection(wxListCtrl* listCtrl)
142 int nSize = listCtrl->GetItemCount();
143 for (int i = 0; i < nSize; i++)
144 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
149 string HtmlEscape(const char* psz, bool fMultiLine=false)
152 for (const char* p = psz; *p; p++)
154 if (*p == '<') len += 4;
155 else if (*p == '>') len += 4;
156 else if (*p == '&') len += 5;
157 else if (*p == '"') len += 6;
158 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
159 else if (*p == '\n' && fMultiLine) len += 5;
165 for (const char* p = psz; *p; p++)
167 if (*p == '<') str += "<";
168 else if (*p == '>') str += ">";
169 else if (*p == '&') str += "&";
170 else if (*p == '"') str += """;
171 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
172 else if (*p == '\n' && fMultiLine) str += "<br>\n";
179 string HtmlEscape(const string& str, bool fMultiLine=false)
181 return HtmlEscape(str.c_str(), fMultiLine);
184 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
186 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
190 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
193 return wxMessageBox(message, caption, style, parent, x, y);
195 if (wxThread::IsMain() || fDaemon)
197 return wxMessageBox(message, caption, style, parent, x, y);
203 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
211 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
213 if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
215 string strMessage = strprintf(
216 _("This transaction is over the size limit. You can still send it for a fee of %s, "
217 "which goes to the nodes that process your transaction and helps to support the network. "
218 "Do you want to pay the fee?"),
219 FormatMoney(nFeeRequired).c_str());
220 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
223 void CalledSetStatusBar(const string& strText, int nField)
225 if (nField == 0 && GetWarnings("statusbar") != "")
227 if (pframeMain && pframeMain->m_statusBar)
228 pframeMain->m_statusBar->SetStatusText(strText, nField);
231 void SetDefaultReceivingAddress(const string& strAddress)
233 // Update main window address and database
234 if (pframeMain == NULL)
236 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
239 if (!AddressToHash160(strAddress, hash160))
241 if (!mapPubKeys.count(hash160))
243 CWalletDB(pwalletMain->strWalletFile).WriteDefaultKey(mapPubKeys[hash160]);
244 pframeMain->m_textCtrlAddress->SetValue(strAddress);
257 //////////////////////////////////////////////////////////////////////////////
262 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
264 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
266 // Set initially selected page
267 wxNotebookEvent event;
268 event.SetSelection(0);
269 OnNotebookPageChanged(event);
270 m_notebook->ChangeSelection(0);
273 fRefreshListCtrl = false;
274 fRefreshListCtrlRunning = false;
275 fOnSetFocusAddress = false;
277 m_choiceFilter->SetSelection(0);
278 double dResize = nScaleX;
280 SetIcon(wxICON(bitcoin));
281 SetSize(dResize * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
283 SetIcon(bitcoin80_xpm);
284 SetBackgroundColour(m_toolBar->GetBackgroundColour());
285 wxFont fontTmp = m_staticText41->GetFont();
286 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
287 m_staticTextBalance->SetFont(fontTmp);
288 m_staticTextBalance->SetSize(140, 17);
289 // resize to fit ubuntu's huge default font
291 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
293 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
294 m_listCtrl->SetFocus();
295 ptaskbaricon = new CMyTaskBarIcon();
297 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
298 // to their standard places, leaving these menus empty.
299 GetMenuBar()->Remove(2); // remove Help menu
300 GetMenuBar()->Remove(0); // remove File menu
303 // Init column headers
304 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
305 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
311 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
312 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
314 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
315 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
316 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
317 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
318 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
319 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
320 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
324 int pnWidths[3] = { -100, 88, 300 };
326 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
327 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
329 m_statusBar->SetFieldsCount(3, pnWidths);
331 // Fill your address text box
332 vector<unsigned char> vchPubKey;
333 if (CWalletDB(pwalletMain->strWalletFile,"r").ReadDefaultKey(vchPubKey))
334 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
336 // Fill listctrl with wallet transactions
340 CMainFrame::~CMainFrame()
347 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
350 nPage = event.GetSelection();
353 m_listCtrl = m_listCtrlAll;
354 fShowGenerated = true;
356 fShowReceived = true;
358 else if (nPage == SENTRECEIVED)
360 m_listCtrl = m_listCtrlSentReceived;
361 fShowGenerated = false;
363 fShowReceived = true;
365 else if (nPage == SENT)
367 m_listCtrl = m_listCtrlSent;
368 fShowGenerated = false;
370 fShowReceived = false;
372 else if (nPage == RECEIVED)
374 m_listCtrl = m_listCtrlReceived;
375 fShowGenerated = false;
377 fShowReceived = true;
380 m_listCtrl->SetFocus();
383 void CMainFrame::OnClose(wxCloseEvent& event)
385 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
387 // Divert close to minimize
389 fClosedToTray = true;
395 CreateThread(Shutdown, NULL);
399 void CMainFrame::OnIconize(wxIconizeEvent& event)
402 // Hide the task bar button when minimized.
403 // Event is sent when the frame is minimized or restored.
404 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
405 // to get rid of the deprecated warning. Just ignore it.
406 if (!event.Iconized())
407 fClosedToTray = false;
408 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
409 if (GetBoolArg("-minimizetotray")) {
411 // The tray icon sometimes disappears on ubuntu karmic
412 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
413 // Reports of CPU peg on 64-bit linux
414 if (fMinimizeToTray && event.Iconized())
415 fClosedToTray = true;
416 Show(!fClosedToTray);
417 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
418 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
423 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
427 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
428 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
431 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
433 // Hidden columns not resizeable
434 if (event.GetColumn() <= 1 && !fDebug)
440 int CMainFrame::GetSortIndex(const string& strSort)
445 // The wx generic listctrl implementation used on GTK doesn't sort,
446 // so we have to do it ourselves. Remember, we sort in reverse order.
447 // In the wx generic implementation, they store the list of items
448 // in a vector, so indexed lookups are fast, but inserts are slower
449 // the closer they are to the top.
451 int high = m_listCtrl->GetItemCount();
454 int mid = low + ((high - low) / 2);
455 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
464 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)
466 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
467 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
470 if (!fNew && nIndex == -1)
472 string strHash = " " + hashKey.ToString();
473 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
474 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
478 // fNew is for blind insert, only use if you're sure it's new
479 if (fNew || nIndex == -1)
481 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
485 // If sort key changed, must delete and reinsert to make it relocate
486 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
488 m_listCtrl->DeleteItem(nIndex);
489 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
493 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
494 m_listCtrl->SetItem(nIndex, 2, str2);
495 m_listCtrl->SetItem(nIndex, 3, str3);
496 m_listCtrl->SetItem(nIndex, 4, str4);
497 m_listCtrl->SetItem(nIndex, 5, str5);
498 m_listCtrl->SetItem(nIndex, 6, str6);
499 m_listCtrl->SetItemData(nIndex, nData);
500 SetItemTextColour(m_listCtrl, nIndex, colour);
503 bool CMainFrame::DeleteLine(uint256 hashKey)
505 long nData = *(long*)&hashKey;
509 string strHash = " " + hashKey.ToString();
510 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
511 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
515 m_listCtrl->DeleteItem(nIndex);
520 string FormatTxStatus(const CWalletTx& wtx)
525 if (wtx.nLockTime < 500000000)
526 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
528 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
532 int nDepth = wtx.GetDepthInMainChain();
533 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
534 return strprintf(_("%d/offline?"), nDepth);
536 return strprintf(_("%d/unconfirmed"), nDepth);
538 return strprintf(_("%d confirmations"), nDepth);
542 string SingleLine(const string& strIn)
545 bool fOneSpace = false;
546 BOOST_FOREACH(unsigned char c, strIn)
554 if (fOneSpace && !strOut.empty())
563 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
565 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
566 int64 nCredit = wtx.GetCredit(true);
567 int64 nDebit = wtx.GetDebit();
568 int64 nNet = nCredit - nDebit;
569 uint256 hash = wtx.GetHash();
570 string strStatus = FormatTxStatus(wtx);
571 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
572 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
573 map<string, string> mapValue = wtx.mapValue;
574 wtx.nLinesDisplayed = 1;
578 if (wtx.IsCoinBase())
580 // Don't show generated coin until confirmed by at least one block after it
581 // so we don't get the user's hopes up until it looks like it's probably accepted.
583 // It is not an error when generated blocks are not accepted. By design,
584 // some percentage of blocks, like 10% or more, will end up not accepted.
585 // This is the normal mechanism by which the network copes with latency.
587 // We display regular transactions right away before any confirmation
588 // because they can always get into some block eventually. Generated coins
589 // are special because if their block is not accepted, they are not valid.
591 if (wtx.GetDepthInMainChain() < 2)
593 wtx.nLinesDisplayed = 0;
601 // Find the block the tx is in
602 CBlockIndex* pindex = NULL;
603 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
604 if (mi != mapBlockIndex.end())
605 pindex = (*mi).second;
607 // Sort order, unrecorded transactions sort to the top
608 string strSort = strprintf("%010d-%01d-%010u",
609 (pindex ? pindex->nHeight : INT_MAX),
610 (wtx.IsCoinBase() ? 1 : 0),
614 if (nNet > 0 || wtx.IsCoinBase())
619 string strDescription;
620 if (wtx.IsCoinBase())
623 strDescription = _("Generated");
626 int64 nUnmatured = 0;
627 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
628 nUnmatured += pwalletMain->GetCredit(txout);
629 if (wtx.IsInMainChain())
631 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
633 // Check if the block was requested by anyone
634 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
635 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
639 strDescription = _("Generated (not accepted)");
643 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
645 // Received by IP connection
648 if (!mapValue["from"].empty())
649 strDescription += _("From: ") + mapValue["from"];
650 if (!mapValue["message"].empty())
652 if (!strDescription.empty())
653 strDescription += " - ";
654 strDescription += mapValue["message"];
659 // Received by Bitcoin Address
662 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
664 if (pwalletMain->IsMine(txout))
666 vector<unsigned char> vchPubKey;
667 if (ExtractPubKey(txout.scriptPubKey, pwalletMain, vchPubKey))
669 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
671 //strDescription += _("Received payment to ");
672 //strDescription += _("Received with address ");
673 strDescription += _("Received with: ");
674 string strAddress = PubKeyToAddress(vchPubKey);
675 map<string, string>::iterator mi = pwalletMain->mapAddressBook.find(strAddress);
676 if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
678 string strLabel = (*mi).second;
679 strDescription += strAddress.substr(0,12) + "... ";
680 strDescription += "(" + strLabel + ")";
683 strDescription += strAddress;
691 string strCredit = FormatMoney(nNet, true);
693 strCredit = "[" + strCredit + "]";
695 InsertLine(fNew, nIndex, hash, strSort, colour,
697 nTime ? DateTimeStr(nTime) : "",
698 SingleLine(strDescription),
704 bool fAllFromMe = true;
705 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
706 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
708 bool fAllToMe = true;
709 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
710 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
712 if (fAllFromMe && fAllToMe)
715 int64 nChange = wtx.GetChange();
716 InsertLine(fNew, nIndex, hash, strSort, colour,
718 nTime ? DateTimeStr(nTime) : "",
719 _("Payment to yourself"),
720 FormatMoney(-(nDebit - nChange), true),
721 FormatMoney(nCredit - nChange, true));
731 int64 nTxFee = nDebit - wtx.GetValueOut();
732 wtx.nLinesDisplayed = 0;
733 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
735 const CTxOut& txout = wtx.vout[nOut];
736 if (pwalletMain->IsMine(txout))
740 if (!mapValue["to"].empty())
743 strAddress = mapValue["to"];
747 // Sent to Bitcoin Address
749 if (ExtractHash160(txout.scriptPubKey, hash160))
750 strAddress = Hash160ToAddress(hash160);
753 string strDescription = _("To: ");
754 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
755 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
756 strDescription += pwalletMain->mapAddressBook[strAddress] + " ";
757 strDescription += strAddress;
758 if (!mapValue["message"].empty())
760 if (!strDescription.empty())
761 strDescription += " - ";
762 strDescription += mapValue["message"];
764 else if (!mapValue["comment"].empty())
766 if (!strDescription.empty())
767 strDescription += " - ";
768 strDescription += mapValue["comment"];
771 int64 nValue = txout.nValue;
778 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
780 nTime ? DateTimeStr(nTime) : "",
781 SingleLine(strDescription),
782 FormatMoney(-nValue, true),
785 wtx.nLinesDisplayed++;
791 // Mixed debit transaction, can't break down payees
793 bool fAllMine = true;
794 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
795 fAllMine = fAllMine && pwalletMain->IsMine(txout);
796 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
797 fAllMine = fAllMine && pwalletMain->IsMine(txin);
799 InsertLine(fNew, nIndex, hash, strSort, colour,
801 nTime ? DateTimeStr(nTime) : "",
803 FormatMoney(nNet, true),
811 void CMainFrame::RefreshListCtrl()
813 fRefreshListCtrl = true;
817 void CMainFrame::OnIdle(wxIdleEvent& event)
819 if (fRefreshListCtrl)
821 // Collect list of wallet transactions and sort newest first
822 bool fEntered = false;
823 vector<pair<unsigned int, uint256> > vSorted;
824 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
826 printf("RefreshListCtrl starting\n");
828 fRefreshListCtrl = false;
829 pwalletMain->vWalletUpdated.clear();
831 // Do the newest transactions first
832 vSorted.reserve(pwalletMain->mapWallet.size());
833 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
835 const CWalletTx& wtx = (*it).second;
836 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
837 vSorted.push_back(make_pair(nTime, (*it).first));
839 m_listCtrl->DeleteAllItems();
844 sort(vSorted.begin(), vSorted.end());
847 for (int i = 0; i < vSorted.size();)
851 bool fEntered = false;
852 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
855 uint256& hash = vSorted[i++].second;
856 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
857 if (mi != pwalletMain->mapWallet.end())
858 InsertTransaction((*mi).second, true);
860 if (!fEntered || i == 100 || i % 500 == 0)
864 printf("RefreshListCtrl done\n");
866 // Update transaction total display
871 // Check for time updates
872 static int64 nLastTime;
873 if (GetTime() > nLastTime + 30)
875 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
877 nLastTime = GetTime();
878 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
880 CWalletTx& wtx = (*it).second;
881 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
882 InsertTransaction(wtx, false);
889 void CMainFrame::RefreshStatusColumn()
892 static CBlockIndex* pindexLastBest;
893 static unsigned int nLastRefreshed;
895 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
896 if (nTop == nLastTop && pindexLastBest == pindexBest)
899 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
902 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
904 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
906 // If no updates, only need to do the part that moved onto the screen
907 if (nStart >= nLastTop && nStart < nLastTop + 100)
908 nStart = nLastTop + 100;
909 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
913 pindexLastBest = pindexBest;
914 nLastRefreshed = nListViewUpdated;
916 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
918 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
919 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
920 if (mi == pwalletMain->mapWallet.end())
922 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
925 CWalletTx& wtx = (*mi).second;
926 if (wtx.IsCoinBase() ||
927 wtx.GetTxTime() != wtx.nTimeDisplayed ||
928 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
930 if (!InsertTransaction(wtx, false, nIndex))
931 m_listCtrl->DeleteItem(nIndex--);
935 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
941 void CMainFrame::OnPaint(wxPaintEvent& event)
952 unsigned int nNeedRepaint = 0;
953 unsigned int nLastRepaint = 0;
954 int64 nLastRepaintTime = 0;
955 int64 nRepaintInterval = 500;
957 void ThreadDelayedRepaint(void* parg)
961 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
963 nLastRepaint = nNeedRepaint;
966 printf("DelayedRepaint\n");
968 pframeMain->fRefresh = true;
969 pframeMain->GetEventHandler()->AddPendingEvent(event);
972 Sleep(nRepaintInterval);
976 void MainFrameRepaint()
978 // This is called by network code that shouldn't access pframeMain
979 // directly because it could still be running after the UI is closed.
982 // Don't repaint too often
983 static int64 nLastRepaintRequest;
984 if (GetTimeMillis() - nLastRepaintRequest < 100)
989 nLastRepaintRequest = GetTimeMillis();
991 printf("MainFrameRepaint\n");
993 pframeMain->fRefresh = true;
994 pframeMain->GetEventHandler()->AddPendingEvent(event);
998 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
1000 // Skip lets the listctrl do the paint, we're just hooking the message
1004 ptaskbaricon->UpdateTooltip();
1009 static int nTransactionCount;
1010 bool fPaintedBalance = false;
1011 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1013 nLastRepaint = nNeedRepaint;
1014 nLastRepaintTime = GetTimeMillis();
1016 // Update listctrl contents
1017 if (!pwalletMain->vWalletUpdated.empty())
1019 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1022 if (m_listCtrl->GetItemCount())
1023 strTop = (string)m_listCtrl->GetItemText(0);
1024 BOOST_FOREACH(uint256 hash, pwalletMain->vWalletUpdated)
1026 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1027 if (mi != pwalletMain->mapWallet.end())
1028 InsertTransaction((*mi).second, false);
1030 pwalletMain->vWalletUpdated.clear();
1031 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1032 m_listCtrl->ScrollList(0, INT_MIN/2);
1037 TRY_CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1039 fPaintedBalance = true;
1040 m_staticTextBalance->SetLabel(FormatMoney(pwalletMain->GetBalance()) + " ");
1042 // Count hidden and multi-line transactions
1043 nTransactionCount = 0;
1044 for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
1046 CWalletTx& wtx = (*it).second;
1047 nTransactionCount += wtx.nLinesDisplayed;
1051 if (!pwalletMain->vWalletUpdated.empty() || !fPaintedBalance)
1054 // Update status column of visible items only
1055 RefreshStatusColumn();
1057 // Update status bar
1058 static string strPrevWarning;
1059 string strWarning = GetWarnings("statusbar");
1060 if (strWarning != "")
1061 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1062 else if (strPrevWarning != "")
1063 m_statusBar->SetStatusText("", 0);
1064 strPrevWarning = strWarning;
1067 if (fGenerateBitcoins)
1068 strGen = _(" Generating");
1069 if (fGenerateBitcoins && vNodes.empty())
1070 strGen = _("(not connected)");
1071 m_statusBar->SetStatusText(strGen, 1);
1073 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1074 m_statusBar->SetStatusText(strStatus, 2);
1076 // Update receiving address
1077 string strDefaultAddress = PubKeyToAddress(pwalletMain->vchDefaultKey);
1078 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1079 m_textCtrlAddress->SetValue(strDefaultAddress);
1083 void UIThreadCall(boost::function0<void> fn)
1085 // Call this with a function object created with bind.
1086 // bind needs all parameters to match the function's expected types
1087 // and all default parameters specified. Some examples:
1088 // UIThreadCall(bind(wxBell));
1089 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1090 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1093 wxCommandEvent event(wxEVT_UITHREADCALL);
1094 event.SetClientData((void*)new boost::function0<void>(fn));
1095 pframeMain->GetEventHandler()->AddPendingEvent(event);
1099 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1101 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1106 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1112 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1114 event.Check(fGenerateBitcoins);
1117 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1119 // Options->Your Receiving Addresses
1120 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1121 if (!dialog.ShowModal())
1125 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1128 COptionsDialog dialog(this);
1132 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1135 CAboutDialog dialog(this);
1139 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1142 CSendDialog dialog(this);
1146 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1148 // Toolbar: Address Book
1149 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1150 if (dialogAddr.ShowModal() == 2)
1153 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1154 dialogSend.ShowModal();
1158 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1160 // Automatically select-all when entering window
1162 m_textCtrlAddress->SetSelection(-1, -1);
1163 fOnSetFocusAddress = true;
1166 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1169 if (fOnSetFocusAddress)
1170 m_textCtrlAddress->SetSelection(-1, -1);
1171 fOnSetFocusAddress = false;
1174 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1177 CGetTextFromUserDialog dialog(this,
1178 _("New Receiving Address"),
1179 _("You should use a new address for each payment you receive.\n\nLabel"),
1181 if (!dialog.ShowModal())
1183 string strName = dialog.GetValue();
1186 string strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool());
1189 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1190 pwalletMain->SetAddressBookName(strAddress, strName);
1191 SetDefaultReceivingAddress(strAddress);
1194 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1196 // Copy address box to clipboard
1197 if (wxTheClipboard->Open())
1199 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1200 wxTheClipboard->Close();
1204 void CMainFrame::OnListItemActivated(wxListEvent& event)
1206 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1208 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1210 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash);
1211 if (mi == pwalletMain->mapWallet.end())
1213 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1218 CTxDetailsDialog dialog(this, wtx);
1220 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1229 //////////////////////////////////////////////////////////////////////////////
1234 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1237 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1239 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1242 strHTML.reserve(4000);
1243 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1245 int64 nTime = wtx.GetTxTime();
1246 int64 nCredit = wtx.GetCredit();
1247 int64 nDebit = wtx.GetDebit();
1248 int64 nNet = nCredit - nDebit;
1252 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1253 int nRequests = wtx.GetRequestCount();
1254 if (nRequests != -1)
1257 strHTML += _(", has not been successfully broadcast yet");
1258 else if (nRequests == 1)
1259 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1261 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1265 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1271 if (wtx.IsCoinBase())
1273 strHTML += _("<b>Source:</b> Generated<br>");
1275 else if (!wtx.mapValue["from"].empty())
1277 // Online transaction
1278 if (!wtx.mapValue["from"].empty())
1279 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1283 // Offline transaction
1287 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1289 if (pwalletMain->IsMine(txout))
1291 vector<unsigned char> vchPubKey;
1292 if (ExtractPubKey(txout.scriptPubKey, pwalletMain, vchPubKey))
1294 string strAddress = PubKeyToAddress(vchPubKey);
1295 if (pwalletMain->mapAddressBook.count(strAddress))
1297 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1298 strHTML += _("<b>To:</b> ");
1299 strHTML += HtmlEscape(strAddress);
1300 if (!pwalletMain->mapAddressBook[strAddress].empty())
1301 strHTML += _(" (yours, label: ") + pwalletMain->mapAddressBook[strAddress] + ")";
1303 strHTML += _(" (yours)");
1318 if (!wtx.mapValue["to"].empty())
1320 // Online transaction
1321 strAddress = wtx.mapValue["to"];
1322 strHTML += _("<b>To:</b> ");
1323 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1324 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1325 strHTML += HtmlEscape(strAddress) + "<br>";
1332 if (wtx.IsCoinBase() && nCredit == 0)
1337 int64 nUnmatured = 0;
1338 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1339 nUnmatured += pwalletMain->GetCredit(txout);
1340 strHTML += _("<b>Credit:</b> ");
1341 if (wtx.IsInMainChain())
1342 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1344 strHTML += _("(not accepted)");
1352 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1356 bool fAllFromMe = true;
1357 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1358 fAllFromMe = fAllFromMe && pwalletMain->IsMine(txin);
1360 bool fAllToMe = true;
1361 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1362 fAllToMe = fAllToMe && pwalletMain->IsMine(txout);
1369 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1371 if (pwalletMain->IsMine(txout))
1374 if (wtx.mapValue["to"].empty())
1376 // Offline transaction
1378 if (ExtractHash160(txout.scriptPubKey, hash160))
1380 string strAddress = Hash160ToAddress(hash160);
1381 strHTML += _("<b>To:</b> ");
1382 if (pwalletMain->mapAddressBook.count(strAddress) && !pwalletMain->mapAddressBook[strAddress].empty())
1383 strHTML += pwalletMain->mapAddressBook[strAddress] + " ";
1384 strHTML += strAddress;
1389 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1395 int64 nChange = wtx.GetChange();
1396 int64 nValue = nCredit - nChange;
1397 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1398 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1401 int64 nTxFee = nDebit - wtx.GetValueOut();
1403 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1408 // Mixed debit transaction
1410 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1411 if (pwalletMain->IsMine(txin))
1412 strHTML += _("<b>Debit:</b> ") + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1413 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1414 if (pwalletMain->IsMine(txout))
1415 strHTML += _("<b>Credit:</b> ") + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1419 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1425 if (!wtx.mapValue["message"].empty())
1426 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1427 if (!wtx.mapValue["comment"].empty())
1428 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1430 if (wtx.IsCoinBase())
1431 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>";
1439 strHTML += "<hr><br>debug print<br><br>";
1440 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1441 if (pwalletMain->IsMine(txin))
1442 strHTML += "<b>Debit:</b> " + FormatMoney(-pwalletMain->GetDebit(txin)) + "<br>";
1443 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1444 if (pwalletMain->IsMine(txout))
1445 strHTML += "<b>Credit:</b> " + FormatMoney(pwalletMain->GetCredit(txout)) + "<br>";
1447 strHTML += "<br><b>Transaction:</b><br>";
1448 strHTML += HtmlEscape(wtx.ToString(), true);
1450 strHTML += "<br><b>Inputs:</b><br>";
1451 CRITICAL_BLOCK(pwalletMain->cs_mapWallet)
1453 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1455 COutPoint prevout = txin.prevout;
1456 map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(prevout.hash);
1457 if (mi != pwalletMain->mapWallet.end())
1459 const CWalletTx& prev = (*mi).second;
1460 if (prevout.n < prev.vout.size())
1462 strHTML += HtmlEscape(prev.ToString(), true);
1463 strHTML += " " + FormatTxStatus(prev) + ", ";
1464 strHTML = strHTML + "IsMine=" + (pwalletMain->IsMine(prev.vout[prevout.n]) ? "true" : "false") + "<br>";
1473 strHTML += "</font></html>";
1474 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1475 m_htmlWin->SetPage(strHTML);
1476 m_buttonOK->SetFocus();
1480 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1490 //////////////////////////////////////////////////////////////////////////////
1496 string StartupShortcutPath()
1498 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1501 bool GetStartOnSystemStartup()
1503 return filesystem::exists(StartupShortcutPath().c_str());
1506 void SetStartOnSystemStartup(bool fAutoStart)
1508 // If the shortcut exists already, remove it for updating
1509 remove(StartupShortcutPath().c_str());
1515 // Get a pointer to the IShellLink interface.
1516 IShellLink* psl = NULL;
1517 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1518 CLSCTX_INPROC_SERVER, IID_IShellLink,
1519 reinterpret_cast<void**>(&psl));
1521 if (SUCCEEDED(hres))
1523 // Get the current executable path
1524 TCHAR pszExePath[MAX_PATH];
1525 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1527 // Set the path to the shortcut target
1528 psl->SetPath(pszExePath);
1529 PathRemoveFileSpec(pszExePath);
1530 psl->SetWorkingDirectory(pszExePath);
1531 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1533 // Query IShellLink for the IPersistFile interface for
1534 // saving the shortcut in persistent storage.
1535 IPersistFile* ppf = NULL;
1536 hres = psl->QueryInterface(IID_IPersistFile,
1537 reinterpret_cast<void**>(&ppf));
1538 if (SUCCEEDED(hres))
1540 WCHAR pwsz[MAX_PATH];
1541 // Ensure that the string is ANSI.
1542 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1543 // Save the link by calling IPersistFile::Save.
1544 hres = ppf->Save(pwsz, TRUE);
1553 #elif defined(__WXGTK__)
1555 // Follow the Desktop Application Autostart Spec:
1556 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1558 boost::filesystem::path GetAutostartDir()
1560 namespace fs = boost::filesystem;
1562 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1563 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1564 char* pszHome = getenv("HOME");
1565 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1569 boost::filesystem::path GetAutostartFilePath()
1571 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1574 bool GetStartOnSystemStartup()
1576 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1577 if (!optionFile.good())
1579 // Scan through file for "Hidden=true":
1581 while (!optionFile.eof())
1583 getline(optionFile, line);
1584 if (line.find("Hidden") != string::npos &&
1585 line.find("true") != string::npos)
1593 void SetStartOnSystemStartup(bool fAutoStart)
1597 unlink(GetAutostartFilePath().native_file_string().c_str());
1601 char pszExePath[MAX_PATH+1];
1602 memset(pszExePath, 0, sizeof(pszExePath));
1603 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1606 boost::filesystem::create_directories(GetAutostartDir());
1608 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1609 if (!optionFile.good())
1611 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1614 // Write a bitcoin.desktop file to the autostart directory:
1615 optionFile << "[Desktop Entry]\n";
1616 optionFile << "Type=Application\n";
1617 optionFile << "Name=Bitcoin\n";
1618 optionFile << "Exec=" << pszExePath << "\n";
1619 optionFile << "Terminal=false\n";
1620 optionFile << "Hidden=false\n";
1626 // TODO: OSX startup stuff; see:
1627 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1629 bool GetStartOnSystemStartup() { return false; }
1630 void SetStartOnSystemStartup(bool fAutoStart) { }
1639 //////////////////////////////////////////////////////////////////////////////
1644 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1646 // Set up list box of page choices
1647 m_listBox->Append(_("Main"));
1648 //m_listBox->Append(_("Test 2"));
1649 m_listBox->SetSelection(0);
1652 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1654 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1656 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1657 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1658 if (!GetBoolArg("-minimizetotray"))
1660 // Minimize to tray is just too buggy on Linux
1661 fMinimizeToTray = false;
1662 m_checkBoxMinimizeToTray->SetValue(false);
1663 m_checkBoxMinimizeToTray->Enable(false);
1664 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1667 #ifdef __WXMAC_OSX__
1668 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1672 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1673 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1674 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1675 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1677 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1679 m_checkBoxUseUPnP->Enable(false);
1680 m_checkBoxUseProxy->SetValue(fUseProxy);
1681 m_textCtrlProxyIP->Enable(fUseProxy);
1682 m_textCtrlProxyPort->Enable(fUseProxy);
1683 m_staticTextProxyIP->Enable(fUseProxy);
1684 m_staticTextProxyPort->Enable(fUseProxy);
1685 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1686 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1688 m_buttonOK->SetFocus();
1691 void COptionsDialog::SelectPage(int nPage)
1693 m_panelMain->Show(nPage == 0);
1694 m_panelTest2->Show(nPage == 1);
1696 m_scrolledWindow->Layout();
1697 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1700 void COptionsDialog::OnListBox(wxCommandEvent& event)
1702 SelectPage(event.GetSelection());
1705 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1708 int64 nTmp = nTransactionFee;
1709 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1710 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1713 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1715 m_textCtrlProxyIP->Enable(event.IsChecked());
1716 m_textCtrlProxyPort->Enable(event.IsChecked());
1717 m_staticTextProxyIP->Enable(event.IsChecked());
1718 m_staticTextProxyPort->Enable(event.IsChecked());
1721 CAddress COptionsDialog::GetProxyAddr()
1723 // Be careful about byte order, addr.ip and addr.port are big endian
1724 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1725 if (addr.ip == INADDR_NONE)
1726 addr.ip = addrProxy.ip;
1727 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1728 addr.port = htons(nPort);
1729 if (nPort <= 0 || nPort > USHRT_MAX)
1730 addr.port = addrProxy.port;
1734 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1737 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1738 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1742 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1744 OnButtonApply(event);
1748 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1753 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1755 CWalletDB walletdb(pwalletMain->strWalletFile);
1757 int64 nPrevTransactionFee = nTransactionFee;
1758 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1759 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1761 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1763 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1764 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1767 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1769 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1770 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1771 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1774 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1776 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1777 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1780 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1782 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1783 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1787 fUseProxy = m_checkBoxUseProxy->GetValue();
1788 walletdb.WriteSetting("fUseProxy", fUseProxy);
1790 addrProxy = GetProxyAddr();
1791 walletdb.WriteSetting("addrProxy", addrProxy);
1799 //////////////////////////////////////////////////////////////////////////////
1804 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1806 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
1808 // Change (c) into UTF-8 or ANSI copyright symbol
1809 wxString str = m_staticTextMain->GetLabel();
1811 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1813 str.Replace("(c)", "\xA9");
1815 m_staticTextMain->SetLabel(str);
1817 // Resize on Linux to make the window fit the text.
1818 // The text was wrapped manually rather than using the Wrap setting because
1819 // the wrap would be too small on Linux and it can't be changed at this point.
1820 wxFont fontTmp = m_staticTextMain->GetFont();
1821 if (fontTmp.GetPointSize() > 8);
1822 fontTmp.SetPointSize(8);
1823 m_staticTextMain->SetFont(fontTmp);
1824 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1826 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1830 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1840 //////////////////////////////////////////////////////////////////////////////
1845 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1848 m_textCtrlAddress->SetValue(strAddress);
1849 m_choiceTransferType->SetSelection(0);
1850 m_bitmapCheckMark->Show(false);
1851 fEnabledPrev = true;
1852 m_textCtrlAddress->SetFocus();
1854 //// todo: should add a display of your balance for convenience
1856 wxFont fontTmp = m_staticTextInstructions->GetFont();
1857 if (fontTmp.GetPointSize() > 9);
1858 fontTmp.SetPointSize(9);
1859 m_staticTextInstructions->SetFont(fontTmp);
1862 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
1866 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
1869 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1874 SetIcon(wxICON(bitcoin));
1877 // Fixup the tab order
1878 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1879 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1883 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1885 // Reformat the amount
1887 if (m_textCtrlAmount->GetValue().Trim().empty())
1890 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1891 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1894 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1896 // Open address book
1897 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1898 if (dialog.ShowModal())
1899 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1902 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1904 // Copy clipboard to address box
1905 if (wxTheClipboard->Open())
1907 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1909 wxTextDataObject data;
1910 wxTheClipboard->GetData(data);
1911 m_textCtrlAddress->SetValue(data.GetText());
1913 wxTheClipboard->Close();
1917 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1919 static CCriticalSection cs_sendlock;
1920 TRY_CRITICAL_BLOCK(cs_sendlock)
1923 string strAddress = (string)m_textCtrlAddress->GetValue();
1927 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1929 wxMessageBox(_("Error in amount "), _("Send Coins"));
1932 if (nValue > pwalletMain->GetBalance())
1934 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1937 if (nValue + nTransactionFee > pwalletMain->GetBalance())
1939 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1943 // Parse bitcoin address
1945 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1947 if (fBitcoinAddress)
1949 CRITICAL_BLOCK(cs_main)
1951 // Send to bitcoin address
1952 CScript scriptPubKey;
1953 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1955 string strError = pwalletMain->SendMoney(scriptPubKey, nValue, wtx, true);
1957 wxMessageBox(_("Payment sent "), _("Sending..."));
1958 else if (strError == "ABORTED")
1959 return; // leave send dialog open
1962 wxMessageBox(strError + " ", _("Sending..."));
1971 CAddress addr(strAddress);
1972 if (!addr.IsValid())
1974 wxMessageBox(_("Invalid address "), _("Send Coins"));
1979 wtx.mapValue["to"] = strAddress;
1981 // Send to IP address
1982 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1983 if (!pdialog->ShowModal())
1987 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
1988 if (!pwalletMain->mapAddressBook.count(strAddress))
1989 pwalletMain->SetAddressBookName(strAddress, "");
1995 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2006 //////////////////////////////////////////////////////////////////////////////
2011 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
2016 start = wxDateTime::UNow();
2017 memset(pszStatus, 0, sizeof(pszStatus));
2024 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2026 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2029 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2030 m_textCtrlStatus->SetValue("");
2032 CreateThread(SendingDialogStartTransfer, this);
2035 CSendingDialog::~CSendingDialog()
2037 printf("~CSendingDialog()\n");
2040 void CSendingDialog::Close()
2042 // Last one out turn out the lights.
2043 // fWorkDone signals that work side is done and UI thread should call destroy.
2044 // fUIDone signals that UI window has closed and work thread should call destroy.
2045 // This allows the window to disappear and end modality when cancelled
2046 // without making the user wait for ConnectNode to return. The dialog object
2047 // hangs around in the background until the work thread exits.
2058 void CSendingDialog::OnClose(wxCloseEvent& event)
2060 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2067 wxCommandEvent cmdevent;
2068 OnButtonCancel(cmdevent);
2072 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2078 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2084 void CSendingDialog::OnPaint(wxPaintEvent& event)
2087 if (strlen(pszStatus) > 130)
2088 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2090 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2091 m_staticTextSending->SetFocus();
2093 m_buttonCancel->Enable(false);
2096 m_buttonOK->Enable(true);
2097 m_buttonOK->SetFocus();
2098 m_buttonCancel->Enable(false);
2100 if (fAbort && fCanCancel && IsShown())
2102 strcpy(pszStatus, _("CANCELLED"));
2103 m_buttonOK->Enable(true);
2104 m_buttonOK->SetFocus();
2105 m_buttonCancel->Enable(false);
2106 m_buttonCancel->SetLabel(_("Cancelled"));
2108 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2114 // Everything from here on is not in the UI thread and must only communicate
2115 // with the rest of the dialog through variables and calling repaint.
2118 void CSendingDialog::Repaint()
2122 GetEventHandler()->AddPendingEvent(event);
2125 bool CSendingDialog::Status()
2132 if (fAbort && fCanCancel)
2134 memset(pszStatus, 0, 10);
2135 strcpy(pszStatus, _("CANCELLED"));
2143 bool CSendingDialog::Status(const string& str)
2148 // This can be read by the UI thread at any time,
2149 // so copy in a way that can be read cleanly at all times.
2150 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2151 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2157 bool CSendingDialog::Error(const string& str)
2161 Status(string(_("Error: ")) + str);
2165 void SendingDialogStartTransfer(void* parg)
2167 ((CSendingDialog*)parg)->StartTransfer();
2170 void CSendingDialog::StartTransfer()
2172 // Make sure we have enough money
2173 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2175 Error(_("Insufficient funds"));
2179 // We may have connected already for product details
2180 if (!Status(_("Connecting...")))
2182 CNode* pnode = ConnectNode(addr, 15 * 60);
2185 Error(_("Unable to connect"));
2189 // Send order to seller, with response going to OnReply2 via event handler
2190 if (!Status(_("Requesting public key...")))
2192 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2195 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2197 ((CSendingDialog*)parg)->OnReply2(vRecv);
2200 void CSendingDialog::OnReply2(CDataStream& vRecv)
2202 if (!Status(_("Received public key...")))
2205 CScript scriptPubKey;
2214 vRecv >> strMessage;
2216 Error(_("Recipient is not accepting transactions sent by IP address"));
2218 Error(_("Transfer was not accepted"));
2219 //// todo: enlarge the window and enable a hidden white box to put seller's message
2222 vRecv >> scriptPubKey;
2226 //// what do we want to do about this?
2227 Error(_("Invalid response received"));
2231 // Pause to give the user a chance to cancel
2232 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2239 CRITICAL_BLOCK(cs_main)
2242 if (!Status(_("Creating transaction...")))
2244 if (nPrice + nTransactionFee > pwalletMain->GetBalance())
2246 Error(_("Insufficient funds"));
2249 CReserveKey reservekey(pwalletMain);
2251 if (!pwalletMain->CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2253 if (nPrice + nFeeRequired > pwalletMain->GetBalance())
2254 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()));
2256 Error(_("Transaction creation failed"));
2261 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2263 Error(_("Transaction aborted"));
2267 // Make sure we're still connected
2268 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2271 Error(_("Lost connection, transaction cancelled"));
2275 // Last chance to cancel
2287 if (!Status(_("Sending payment...")))
2291 if (!pwalletMain->CommitTransaction(wtx, reservekey))
2293 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."));
2297 // Send payment tx to seller, with response going to OnReply3 via event handler
2298 CWalletTx wtxSend = wtx;
2299 wtxSend.fFromMe = false;
2300 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2302 Status(_("Waiting for confirmation..."));
2307 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2309 ((CSendingDialog*)parg)->OnReply3(vRecv);
2312 void CSendingDialog::OnReply3(CDataStream& vRecv)
2320 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2321 "The transaction is recorded and will credit to the recipient,\n"
2322 "but the comment information will be blank."));
2328 //// what do we want to do about this?
2329 Error(_("Payment was sent, but an invalid response was received"));
2335 Status(_("Payment completed"));
2343 //////////////////////////////////////////////////////////////////////////////
2345 // CAddressBookDialog
2348 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2351 SetSize(nScaleX * GetSize().GetWidth(), nScaleY * GetSize().GetHeight());
2354 // Set initially selected page
2355 wxNotebookEvent event;
2356 event.SetSelection(nPageIn);
2357 OnNotebookPageChanged(event);
2358 m_notebook->ChangeSelection(nPageIn);
2360 fDuringSend = fDuringSendIn;
2362 m_buttonCancel->Show(false);
2365 if (nScaleX == 1.0 && nScaleY == 1.0) // We don't have icons of the proper size otherwise
2367 wxIcon iconAddressBook;
2368 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2369 SetIcon(iconAddressBook);
2373 SetIcon(wxICON(bitcoin));
2376 // Init column headers
2377 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2378 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2379 m_listCtrlSending->SetFocus();
2380 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2381 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2382 m_listCtrlReceiving->SetFocus();
2384 // Fill listctrl with address book data
2385 CRITICAL_BLOCK(pwalletMain->cs_mapKeys)
2386 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2388 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2389 BOOST_FOREACH(const PAIRTYPE(string, string)& item, pwalletMain->mapAddressBook)
2391 string strAddress = item.first;
2392 string strName = item.second;
2394 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2395 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2396 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2397 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2398 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2403 wxString CAddressBookDialog::GetSelectedAddress()
2405 int nIndex = GetSelection(m_listCtrl);
2408 return GetItemText(m_listCtrl, nIndex, 1);
2411 wxString CAddressBookDialog::GetSelectedSendingAddress()
2413 int nIndex = GetSelection(m_listCtrlSending);
2416 return GetItemText(m_listCtrlSending, nIndex, 1);
2419 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2421 int nIndex = GetSelection(m_listCtrlReceiving);
2424 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2427 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2430 nPage = event.GetSelection();
2431 if (nPage == SENDING)
2432 m_listCtrl = m_listCtrlSending;
2433 else if (nPage == RECEIVING)
2434 m_listCtrl = m_listCtrlReceiving;
2435 m_buttonDelete->Show(nPage == SENDING);
2436 m_buttonCopy->Show(nPage == RECEIVING);
2438 m_listCtrl->SetFocus();
2441 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2443 // Update address book with edited name
2445 if (event.IsEditCancelled())
2447 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2448 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2449 pwalletMain->SetAddressBookName(strAddress, string(event.GetText()));
2450 pframeMain->RefreshListCtrl();
2453 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2456 if (nPage == RECEIVING)
2457 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2460 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2465 // Doubleclick returns selection
2466 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2470 // Doubleclick edits item
2471 wxCommandEvent event2;
2472 OnButtonEdit(event2);
2475 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2477 if (nPage != SENDING)
2479 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2481 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2483 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2484 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2485 pwalletMain->DelAddressBookName(strAddress);
2486 m_listCtrl->DeleteItem(nIndex);
2489 pframeMain->RefreshListCtrl();
2492 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2494 // Copy address box to clipboard
2495 if (wxTheClipboard->Open())
2497 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2498 wxTheClipboard->Close();
2502 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2505 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2507 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2511 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2513 int nIndex = GetSelection(m_listCtrl);
2516 string strName = (string)m_listCtrl->GetItemText(nIndex);
2517 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2518 string strAddressOrg = strAddress;
2520 if (nPage == SENDING)
2522 // Ask name and address
2525 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2526 if (!dialog.ShowModal())
2528 strName = dialog.GetValue1();
2529 strAddress = dialog.GetValue2();
2531 while (CheckIfMine(strAddress, _("Edit Address")));
2534 else if (nPage == RECEIVING)
2537 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2538 if (!dialog.ShowModal())
2540 strName = dialog.GetValue();
2544 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2546 if (strAddress != strAddressOrg)
2547 pwalletMain->DelAddressBookName(strAddressOrg);
2548 pwalletMain->SetAddressBookName(strAddress, strName);
2550 m_listCtrl->SetItem(nIndex, 1, strAddress);
2551 m_listCtrl->SetItemText(nIndex, strName);
2552 pframeMain->RefreshListCtrl();
2555 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2560 if (nPage == SENDING)
2562 // Ask name and address
2565 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2566 if (!dialog.ShowModal())
2568 strName = dialog.GetValue1();
2569 strAddress = dialog.GetValue2();
2571 while (CheckIfMine(strAddress, _("Add Address")));
2573 else if (nPage == RECEIVING)
2576 CGetTextFromUserDialog dialog(this,
2577 _("New Receiving Address"),
2578 _("You should use a new address for each payment you receive.\n\nLabel"),
2580 if (!dialog.ShowModal())
2582 strName = dialog.GetValue();
2585 strAddress = PubKeyToAddress(pwalletMain->GetKeyFromKeyPool());
2588 // Add to list and select it
2589 CRITICAL_BLOCK(pwalletMain->cs_mapAddressBook)
2590 pwalletMain->SetAddressBookName(strAddress, strName);
2591 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2592 SetSelection(m_listCtrl, nIndex);
2593 m_listCtrl->SetFocus();
2594 if (nPage == SENDING)
2595 pframeMain->RefreshListCtrl();
2598 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2601 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2604 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2610 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2621 //////////////////////////////////////////////////////////////////////////////
2628 ID_TASKBAR_RESTORE = 10001,
2631 ID_TASKBAR_GENERATE,
2635 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2636 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2637 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2638 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2639 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2640 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2641 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2644 void CMyTaskBarIcon::Show(bool fShow)
2646 static char pszPrevTip[200];
2649 string strTooltip = _("Bitcoin");
2650 if (fGenerateBitcoins)
2651 strTooltip = _("Bitcoin - Generating");
2652 if (fGenerateBitcoins && vNodes.empty())
2653 strTooltip = _("Bitcoin - (not connected)");
2655 // Optimization, only update when changed, using char array to be reentrant
2656 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2658 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2660 // somehow it'll choose the wrong size and scale it down if
2661 // we use the main icon, so we hand it one with only 16x16
2662 SetIcon(wxICON(favicon), strTooltip);
2664 SetIcon(bitcoin80_xpm, strTooltip);
2670 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2675 void CMyTaskBarIcon::Hide()
2680 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2685 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2690 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2693 CSendDialog dialog(pframeMain);
2697 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2699 // Since it's modal, get the main window to do it
2700 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2701 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2704 void CMyTaskBarIcon::Restore()
2707 wxIconizeEvent event(0, false);
2708 pframeMain->GetEventHandler()->AddPendingEvent(event);
2709 pframeMain->Iconize(false);
2710 pframeMain->Raise();
2713 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2715 event.Check(fGenerateBitcoins);
2718 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2720 pframeMain->Close(true);
2723 void CMyTaskBarIcon::UpdateTooltip()
2725 if (IsIconInstalled())
2729 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2731 wxMenu* pmenu = new wxMenu;
2732 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2733 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2734 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2735 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2736 pmenu->AppendSeparator();
2737 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2747 //////////////////////////////////////////////////////////////////////////////
2752 void CreateMainWindow()
2754 pframeMain = new CMainFrame(NULL);
2755 if (GetBoolArg("-min"))
2756 pframeMain->Iconize(true);
2757 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2758 if (!GetBoolArg("-minimizetotray"))
2759 fMinimizeToTray = false;
2761 pframeMain->Show(true); // have to show first to get taskbar button to hide
2762 if (fMinimizeToTray && pframeMain->IsIconized())
2763 fClosedToTray = true;
2764 pframeMain->Show(!fClosedToTray);
2765 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2766 CreateThread(ThreadDelayedRepaint, NULL);
2770 // Define a new application
2771 class CMyApp : public wxApp
2780 // Hook Initialize so we can start without GUI
2781 virtual bool Initialize(int& argc, wxChar** argv);
2783 // 2nd-level exception handling: we get all the exceptions occurring in any
2784 // event handler here
2785 virtual bool OnExceptionInMainLoop();
2787 // 3rd, and final, level exception handling: whenever an unhandled
2788 // exception is caught, this function is called
2789 virtual void OnUnhandledException();
2791 // and now for something different: this function is called in case of a
2792 // crash (e.g. dereferencing null pointer, division by 0, ...)
2793 virtual void OnFatalException();
2796 IMPLEMENT_APP(CMyApp)
2798 bool CMyApp::Initialize(int& argc, wxChar** argv)
2800 for (int i = 1; i < argc; i++)
2801 if (!IsSwitchChar(argv[i][0]))
2802 fCommandLine = true;
2806 // wxApp::Initialize will remove environment-specific parameters,
2807 // so it's too early to call ParseParameters yet
2808 for (int i = 1; i < argc; i++)
2810 wxString str = argv[i];
2812 if (str.size() >= 1 && str[0] == '/')
2814 char pszLower[MAX_PATH];
2815 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2819 if (str == "-daemon")
2825 if (fDaemon || fCommandLine)
2827 // Call the original Initialize while suppressing error messages
2828 // and ignoring failure. If unable to initialize GTK, it fails
2829 // near the end so hopefully the last few things don't matter.
2832 wxApp::Initialize(argc, argv);
2841 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2845 pthread_exit((void*)0);
2847 pid_t sid = setsid();
2849 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
2856 return wxApp::Initialize(argc, argv);
2859 bool CMyApp::OnInit()
2861 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2862 // Disable malfunctioning wxWidgets debug assertion
2863 extern int g_isPainting;
2864 g_isPainting = 10000;
2866 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2867 SetAppName("Bitcoin");
2869 SetAppName("bitcoin");
2873 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2874 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2875 class wxMBConv_win32 : public wxMBConv
2879 size_t m_minMBCharWidth;
2881 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2882 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2886 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2887 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2888 g_locale.AddCatalogLookupPathPrefix("locale");
2890 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2891 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2893 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2894 g_locale.AddCatalog("bitcoin");
2897 HDC hdc = GetDC(NULL);
2900 nScaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
2901 nScaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
2902 ReleaseDC(NULL, hdc);
2906 return AppInit(argc, argv);
2909 int CMyApp::OnExit()
2912 return wxApp::OnExit();
2915 bool CMyApp::OnExceptionInMainLoop()
2921 catch (std::exception& e)
2923 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2924 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2930 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2931 wxLogWarning("Unknown exception");
2938 void CMyApp::OnUnhandledException()
2940 // this shows how we may let some exception propagate uncaught
2945 catch (std::exception& e)
2947 PrintException(&e, "CMyApp::OnUnhandledException()");
2948 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2954 PrintException(NULL, "CMyApp::OnUnhandledException()");
2955 wxLogWarning("Unknown exception");
2961 void CMyApp::OnFatalException()
2963 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);