1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
8 #include <boost/filesystem/fstream.hpp>
9 #include <boost/filesystem/convenience.hpp>
15 using namespace boost;
18 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
20 CMainFrame* pframeMain = NULL;
21 CMyTaskBarIcon* ptaskbaricon = NULL;
22 bool fClosedToTray = false;
33 //////////////////////////////////////////////////////////////////////////////
38 void HandleCtrlA(wxKeyEvent& event)
42 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
43 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
44 textCtrl->SetSelection(-1, -1);
49 //char pszHourFormat[256];
50 //pszHourFormat[0] = '\0';
51 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
52 //return (pszHourFormat[0] != '0');
56 string DateStr(int64 nTime)
58 // Can only be used safely here in the UI
59 return (string)wxDateTime((time_t)nTime).FormatDate();
62 string DateTimeStr(int64 nTime)
64 // Can only be used safely here in the UI
65 wxDateTime datetime((time_t)nTime);
67 return (string)datetime.Format("%x %H:%M");
69 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
72 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
74 // Helper to simplify access to listctrl
76 item.m_itemId = nIndex;
78 item.m_mask = wxLIST_MASK_TEXT;
79 if (!listCtrl->GetItem(item))
81 return item.GetText();
84 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
86 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
87 listCtrl->SetItem(nIndex, 1, str1);
91 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
93 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
94 listCtrl->SetItem(nIndex, 1, str1);
95 listCtrl->SetItem(nIndex, 2, str2);
96 listCtrl->SetItem(nIndex, 3, str3);
97 listCtrl->SetItem(nIndex, 4, str4);
101 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
103 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
104 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
105 listCtrl->SetItem(nIndex, 1, str1);
106 listCtrl->SetItem(nIndex, 2, str2);
107 listCtrl->SetItem(nIndex, 3, str3);
108 listCtrl->SetItem(nIndex, 4, str4);
112 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
114 // Repaint on Windows is more flickery if the colour has ever been set,
115 // so don't want to set it unless it's different. Default colour has
116 // alpha 0 transparent, so our colours don't match using operator==.
117 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
119 c1 = wxColour(0,0,0);
120 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
121 listCtrl->SetItemTextColour(nIndex, colour);
124 void SetSelection(wxListCtrl* listCtrl, int nIndex)
126 int nSize = listCtrl->GetItemCount();
127 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
128 for (int i = 0; i < nSize; i++)
129 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
132 int GetSelection(wxListCtrl* listCtrl)
134 int nSize = listCtrl->GetItemCount();
135 for (int i = 0; i < nSize; i++)
136 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
141 string HtmlEscape(const char* psz, bool fMultiLine=false)
144 for (const char* p = psz; *p; p++)
146 if (*p == '<') len += 4;
147 else if (*p == '>') len += 4;
148 else if (*p == '&') len += 5;
149 else if (*p == '"') len += 6;
150 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
151 else if (*p == '\n' && fMultiLine) len += 5;
157 for (const char* p = psz; *p; p++)
159 if (*p == '<') str += "<";
160 else if (*p == '>') str += ">";
161 else if (*p == '&') str += "&";
162 else if (*p == '"') str += """;
163 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
164 else if (*p == '\n' && fMultiLine) str += "<br>\n";
171 string HtmlEscape(const string& str, bool fMultiLine=false)
173 return HtmlEscape(str.c_str(), fMultiLine);
176 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
178 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
182 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
185 return wxMessageBox(message, caption, style, parent, x, y);
187 if (wxThread::IsMain() || fDaemon)
189 return wxMessageBox(message, caption, style, parent, x, y);
195 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
203 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
205 if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
207 string strMessage = strprintf(
208 _("This transaction is over the size limit. You can still send it for a fee of %s, "
209 "which goes to the nodes that process your transaction and helps to support the network. "
210 "Do you want to pay the fee?"),
211 FormatMoney(nFeeRequired).c_str());
212 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
215 void CalledSetStatusBar(const string& strText, int nField)
217 if (nField == 0 && GetWarnings("statusbar") != "")
219 if (pframeMain && pframeMain->m_statusBar)
220 pframeMain->m_statusBar->SetStatusText(strText, nField);
223 void SetDefaultReceivingAddress(const string& strAddress)
225 // Update main window address and database
226 if (pframeMain == NULL)
228 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
231 if (!AddressToHash160(strAddress, hash160))
233 if (!mapPubKeys.count(hash160))
235 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
236 pframeMain->m_textCtrlAddress->SetValue(strAddress);
249 //////////////////////////////////////////////////////////////////////////////
254 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
256 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
258 // Set initially selected page
259 wxNotebookEvent event;
260 event.SetSelection(0);
261 OnNotebookPageChanged(event);
262 m_notebook->ChangeSelection(0);
265 fRefreshListCtrl = false;
266 fRefreshListCtrlRunning = false;
267 fOnSetFocusAddress = false;
269 m_choiceFilter->SetSelection(0);
270 double dResize = 1.0;
272 SetIcon(wxICON(bitcoin));
274 SetIcon(bitcoin80_xpm);
275 SetBackgroundColour(m_toolBar->GetBackgroundColour());
276 wxFont fontTmp = m_staticText41->GetFont();
277 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
278 m_staticTextBalance->SetFont(fontTmp);
279 m_staticTextBalance->SetSize(140, 17);
280 // resize to fit ubuntu's huge default font
282 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
284 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
285 m_listCtrl->SetFocus();
286 ptaskbaricon = new CMyTaskBarIcon();
288 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
289 // to their standard places, leaving these menus empty.
290 GetMenuBar()->Remove(2); // remove Help menu
291 GetMenuBar()->Remove(0); // remove File menu
294 // Init column headers
295 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
296 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
302 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
303 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
305 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
306 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
307 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
308 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
309 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
310 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
311 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
315 int pnWidths[3] = { -100, 88, 300 };
317 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
318 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
320 m_statusBar->SetFieldsCount(3, pnWidths);
322 // Fill your address text box
323 vector<unsigned char> vchPubKey;
324 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
325 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
327 // Fill listctrl with wallet transactions
331 CMainFrame::~CMainFrame()
338 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
341 nPage = event.GetSelection();
344 m_listCtrl = m_listCtrlAll;
345 fShowGenerated = true;
347 fShowReceived = true;
349 else if (nPage == SENTRECEIVED)
351 m_listCtrl = m_listCtrlSentReceived;
352 fShowGenerated = false;
354 fShowReceived = true;
356 else if (nPage == SENT)
358 m_listCtrl = m_listCtrlSent;
359 fShowGenerated = false;
361 fShowReceived = false;
363 else if (nPage == RECEIVED)
365 m_listCtrl = m_listCtrlReceived;
366 fShowGenerated = false;
368 fShowReceived = true;
371 m_listCtrl->SetFocus();
374 void CMainFrame::OnClose(wxCloseEvent& event)
376 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
378 // Divert close to minimize
380 fClosedToTray = true;
386 CreateThread(Shutdown, NULL);
390 void CMainFrame::OnIconize(wxIconizeEvent& event)
393 // Hide the task bar button when minimized.
394 // Event is sent when the frame is minimized or restored.
395 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
396 // to get rid of the deprecated warning. Just ignore it.
397 if (!event.Iconized())
398 fClosedToTray = false;
399 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
400 if (GetBoolArg("-minimizetotray")) {
402 // The tray icon sometimes disappears on ubuntu karmic
403 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
404 // Reports of CPU peg on 64-bit linux
405 if (fMinimizeToTray && event.Iconized())
406 fClosedToTray = true;
407 Show(!fClosedToTray);
408 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
409 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
414 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
418 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
419 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
422 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
424 // Hidden columns not resizeable
425 if (event.GetColumn() <= 1 && !fDebug)
431 int CMainFrame::GetSortIndex(const string& strSort)
436 // The wx generic listctrl implementation used on GTK doesn't sort,
437 // so we have to do it ourselves. Remember, we sort in reverse order.
438 // In the wx generic implementation, they store the list of items
439 // in a vector, so indexed lookups are fast, but inserts are slower
440 // the closer they are to the top.
442 int high = m_listCtrl->GetItemCount();
445 int mid = low + ((high - low) / 2);
446 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
455 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)
457 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
458 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
461 if (!fNew && nIndex == -1)
463 string strHash = " " + hashKey.ToString();
464 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
465 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
469 // fNew is for blind insert, only use if you're sure it's new
470 if (fNew || nIndex == -1)
472 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
476 // If sort key changed, must delete and reinsert to make it relocate
477 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
479 m_listCtrl->DeleteItem(nIndex);
480 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
484 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
485 m_listCtrl->SetItem(nIndex, 2, str2);
486 m_listCtrl->SetItem(nIndex, 3, str3);
487 m_listCtrl->SetItem(nIndex, 4, str4);
488 m_listCtrl->SetItem(nIndex, 5, str5);
489 m_listCtrl->SetItem(nIndex, 6, str6);
490 m_listCtrl->SetItemData(nIndex, nData);
491 SetItemTextColour(m_listCtrl, nIndex, colour);
494 bool CMainFrame::DeleteLine(uint256 hashKey)
496 long nData = *(long*)&hashKey;
500 string strHash = " " + hashKey.ToString();
501 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
502 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
506 m_listCtrl->DeleteItem(nIndex);
511 string FormatTxStatus(const CWalletTx& wtx)
516 if (wtx.nLockTime < 500000000)
517 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
519 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
523 int nDepth = wtx.GetDepthInMainChain();
524 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
525 return strprintf(_("%d/offline?"), nDepth);
527 return strprintf(_("%d/unconfirmed"), nDepth);
529 return strprintf(_("%d confirmations"), nDepth);
533 string SingleLine(const string& strIn)
536 bool fOneSpace = false;
537 BOOST_FOREACH(unsigned char c, strIn)
545 if (fOneSpace && !strOut.empty())
554 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
556 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
557 int64 nCredit = wtx.GetCredit(true);
558 int64 nDebit = wtx.GetDebit();
559 int64 nNet = nCredit - nDebit;
560 uint256 hash = wtx.GetHash();
561 string strStatus = FormatTxStatus(wtx);
562 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
563 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
564 map<string, string> mapValue = wtx.mapValue;
565 wtx.nLinesDisplayed = 1;
569 if (wtx.IsCoinBase())
571 // Don't show generated coin until confirmed by at least one block after it
572 // so we don't get the user's hopes up until it looks like it's probably accepted.
574 // It is not an error when generated blocks are not accepted. By design,
575 // some percentage of blocks, like 10% or more, will end up not accepted.
576 // This is the normal mechanism by which the network copes with latency.
578 // We display regular transactions right away before any confirmation
579 // because they can always get into some block eventually. Generated coins
580 // are special because if their block is not accepted, they are not valid.
582 if (wtx.GetDepthInMainChain() < 2)
584 wtx.nLinesDisplayed = 0;
592 // Find the block the tx is in
593 CBlockIndex* pindex = NULL;
594 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
595 if (mi != mapBlockIndex.end())
596 pindex = (*mi).second;
598 // Sort order, unrecorded transactions sort to the top
599 string strSort = strprintf("%010d-%01d-%010u",
600 (pindex ? pindex->nHeight : INT_MAX),
601 (wtx.IsCoinBase() ? 1 : 0),
605 if (nNet > 0 || wtx.IsCoinBase())
610 string strDescription;
611 if (wtx.IsCoinBase())
614 strDescription = _("Generated");
617 int64 nUnmatured = 0;
618 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
619 nUnmatured += txout.GetCredit();
620 if (wtx.IsInMainChain())
622 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
624 // Check if the block was requested by anyone
625 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
626 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
630 strDescription = _("Generated (not accepted)");
634 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
636 // Received by IP connection
639 if (!mapValue["from"].empty())
640 strDescription += _("From: ") + mapValue["from"];
641 if (!mapValue["message"].empty())
643 if (!strDescription.empty())
644 strDescription += " - ";
645 strDescription += mapValue["message"];
650 // Received by Bitcoin Address
653 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
657 vector<unsigned char> vchPubKey;
658 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
660 CRITICAL_BLOCK(cs_mapAddressBook)
662 //strDescription += _("Received payment to ");
663 //strDescription += _("Received with address ");
664 strDescription += _("Received with: ");
665 string strAddress = PubKeyToAddress(vchPubKey);
666 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
667 if (mi != mapAddressBook.end() && !(*mi).second.empty())
669 string strLabel = (*mi).second;
670 strDescription += strAddress.substr(0,12) + "... ";
671 strDescription += "(" + strLabel + ")";
674 strDescription += strAddress;
682 string strCredit = FormatMoney(nNet, true);
684 strCredit = "[" + strCredit + "]";
686 InsertLine(fNew, nIndex, hash, strSort, colour,
688 nTime ? DateTimeStr(nTime) : "",
689 SingleLine(strDescription),
695 bool fAllFromMe = true;
696 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
697 fAllFromMe = fAllFromMe && txin.IsMine();
699 bool fAllToMe = true;
700 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
701 fAllToMe = fAllToMe && txout.IsMine();
703 if (fAllFromMe && fAllToMe)
706 int64 nChange = wtx.GetChange();
707 InsertLine(fNew, nIndex, hash, strSort, colour,
709 nTime ? DateTimeStr(nTime) : "",
710 _("Payment to yourself"),
711 FormatMoney(-(nDebit - nChange), true),
712 FormatMoney(nCredit - nChange, true));
722 int64 nTxFee = nDebit - wtx.GetValueOut();
723 wtx.nLinesDisplayed = 0;
724 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
726 const CTxOut& txout = wtx.vout[nOut];
731 if (!mapValue["to"].empty())
734 strAddress = mapValue["to"];
738 // Sent to Bitcoin Address
740 if (ExtractHash160(txout.scriptPubKey, hash160))
741 strAddress = Hash160ToAddress(hash160);
744 string strDescription = _("To: ");
745 CRITICAL_BLOCK(cs_mapAddressBook)
746 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
747 strDescription += mapAddressBook[strAddress] + " ";
748 strDescription += strAddress;
749 if (!mapValue["message"].empty())
751 if (!strDescription.empty())
752 strDescription += " - ";
753 strDescription += mapValue["message"];
755 else if (!mapValue["comment"].empty())
757 if (!strDescription.empty())
758 strDescription += " - ";
759 strDescription += mapValue["comment"];
762 int64 nValue = txout.nValue;
769 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
771 nTime ? DateTimeStr(nTime) : "",
772 SingleLine(strDescription),
773 FormatMoney(-nValue, true),
776 wtx.nLinesDisplayed++;
782 // Mixed debit transaction, can't break down payees
784 bool fAllMine = true;
785 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
786 fAllMine = fAllMine && txout.IsMine();
787 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
788 fAllMine = fAllMine && txin.IsMine();
790 InsertLine(fNew, nIndex, hash, strSort, colour,
792 nTime ? DateTimeStr(nTime) : "",
794 FormatMoney(nNet, true),
802 void CMainFrame::RefreshListCtrl()
804 fRefreshListCtrl = true;
808 void CMainFrame::OnIdle(wxIdleEvent& event)
810 if (fRefreshListCtrl)
812 // Collect list of wallet transactions and sort newest first
813 bool fEntered = false;
814 vector<pair<unsigned int, uint256> > vSorted;
815 TRY_CRITICAL_BLOCK(cs_mapWallet)
817 printf("RefreshListCtrl starting\n");
819 fRefreshListCtrl = false;
820 vWalletUpdated.clear();
822 // Do the newest transactions first
823 vSorted.reserve(mapWallet.size());
824 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
826 const CWalletTx& wtx = (*it).second;
827 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
828 vSorted.push_back(make_pair(nTime, (*it).first));
830 m_listCtrl->DeleteAllItems();
835 sort(vSorted.begin(), vSorted.end());
838 for (int i = 0; i < vSorted.size();)
842 bool fEntered = false;
843 TRY_CRITICAL_BLOCK(cs_mapWallet)
846 uint256& hash = vSorted[i++].second;
847 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
848 if (mi != mapWallet.end())
849 InsertTransaction((*mi).second, true);
851 if (!fEntered || i == 100 || i % 500 == 0)
855 printf("RefreshListCtrl done\n");
857 // Update transaction total display
862 // Check for time updates
863 static int64 nLastTime;
864 if (GetTime() > nLastTime + 30)
866 TRY_CRITICAL_BLOCK(cs_mapWallet)
868 nLastTime = GetTime();
869 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
871 CWalletTx& wtx = (*it).second;
872 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
873 InsertTransaction(wtx, false);
880 void CMainFrame::RefreshStatusColumn()
883 static CBlockIndex* pindexLastBest;
884 static unsigned int nLastRefreshed;
886 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
887 if (nTop == nLastTop && pindexLastBest == pindexBest)
890 TRY_CRITICAL_BLOCK(cs_mapWallet)
893 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
895 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
897 // If no updates, only need to do the part that moved onto the screen
898 if (nStart >= nLastTop && nStart < nLastTop + 100)
899 nStart = nLastTop + 100;
900 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
904 pindexLastBest = pindexBest;
905 nLastRefreshed = nListViewUpdated;
907 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
909 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
910 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
911 if (mi == mapWallet.end())
913 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
916 CWalletTx& wtx = (*mi).second;
917 if (wtx.IsCoinBase() ||
918 wtx.GetTxTime() != wtx.nTimeDisplayed ||
919 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
921 if (!InsertTransaction(wtx, false, nIndex))
922 m_listCtrl->DeleteItem(nIndex--);
926 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
932 void CMainFrame::OnPaint(wxPaintEvent& event)
943 unsigned int nNeedRepaint = 0;
944 unsigned int nLastRepaint = 0;
945 int64 nLastRepaintTime = 0;
946 int64 nRepaintInterval = 500;
948 void ThreadDelayedRepaint(void* parg)
952 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
954 nLastRepaint = nNeedRepaint;
957 printf("DelayedRepaint\n");
959 pframeMain->fRefresh = true;
960 pframeMain->GetEventHandler()->AddPendingEvent(event);
963 Sleep(nRepaintInterval);
967 void MainFrameRepaint()
969 // This is called by network code that shouldn't access pframeMain
970 // directly because it could still be running after the UI is closed.
973 // Don't repaint too often
974 static int64 nLastRepaintRequest;
975 if (GetTimeMillis() - nLastRepaintRequest < 100)
980 nLastRepaintRequest = GetTimeMillis();
982 printf("MainFrameRepaint\n");
984 pframeMain->fRefresh = true;
985 pframeMain->GetEventHandler()->AddPendingEvent(event);
989 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
991 // Skip lets the listctrl do the paint, we're just hooking the message
995 ptaskbaricon->UpdateTooltip();
1000 static int nTransactionCount;
1001 bool fPaintedBalance = false;
1002 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1004 nLastRepaint = nNeedRepaint;
1005 nLastRepaintTime = GetTimeMillis();
1007 // Update listctrl contents
1008 if (!vWalletUpdated.empty())
1010 TRY_CRITICAL_BLOCK(cs_mapWallet)
1013 if (m_listCtrl->GetItemCount())
1014 strTop = (string)m_listCtrl->GetItemText(0);
1015 BOOST_FOREACH(uint256 hash, vWalletUpdated)
1017 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1018 if (mi != mapWallet.end())
1019 InsertTransaction((*mi).second, false);
1021 vWalletUpdated.clear();
1022 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1023 m_listCtrl->ScrollList(0, INT_MIN/2);
1028 TRY_CRITICAL_BLOCK(cs_mapWallet)
1030 fPaintedBalance = true;
1031 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
1033 // Count hidden and multi-line transactions
1034 nTransactionCount = 0;
1035 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1037 CWalletTx& wtx = (*it).second;
1038 nTransactionCount += wtx.nLinesDisplayed;
1042 if (!vWalletUpdated.empty() || !fPaintedBalance)
1045 // Update status column of visible items only
1046 RefreshStatusColumn();
1048 // Update status bar
1049 static string strPrevWarning;
1050 string strWarning = GetWarnings("statusbar");
1051 if (strWarning != "")
1052 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1053 else if (strPrevWarning != "")
1054 m_statusBar->SetStatusText("", 0);
1055 strPrevWarning = strWarning;
1058 if (fGenerateBitcoins)
1059 strGen = _(" Generating");
1060 if (fGenerateBitcoins && vNodes.empty())
1061 strGen = _("(not connected)");
1062 m_statusBar->SetStatusText(strGen, 1);
1064 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1065 m_statusBar->SetStatusText(strStatus, 2);
1067 // Update receiving address
1068 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1069 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1070 m_textCtrlAddress->SetValue(strDefaultAddress);
1074 void UIThreadCall(boost::function0<void> fn)
1076 // Call this with a function object created with bind.
1077 // bind needs all parameters to match the function's expected types
1078 // and all default parameters specified. Some examples:
1079 // UIThreadCall(bind(wxBell));
1080 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1081 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1084 wxCommandEvent event(wxEVT_UITHREADCALL);
1085 event.SetClientData((void*)new boost::function0<void>(fn));
1086 pframeMain->GetEventHandler()->AddPendingEvent(event);
1090 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1092 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1097 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1103 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1105 event.Check(fGenerateBitcoins);
1108 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1110 // Options->Your Receiving Addresses
1111 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1112 if (!dialog.ShowModal())
1116 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1119 COptionsDialog dialog(this);
1123 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1126 CAboutDialog dialog(this);
1130 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1133 CSendDialog dialog(this);
1137 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1139 // Toolbar: Address Book
1140 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1141 if (dialogAddr.ShowModal() == 2)
1144 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1145 dialogSend.ShowModal();
1149 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1151 // Automatically select-all when entering window
1153 m_textCtrlAddress->SetSelection(-1, -1);
1154 fOnSetFocusAddress = true;
1157 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1160 if (fOnSetFocusAddress)
1161 m_textCtrlAddress->SetSelection(-1, -1);
1162 fOnSetFocusAddress = false;
1165 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1168 CGetTextFromUserDialog dialog(this,
1169 _("New Receiving Address"),
1170 _("You should use a new address for each payment you receive.\n\nLabel"),
1172 if (!dialog.ShowModal())
1174 string strName = dialog.GetValue();
1177 string strAddress = PubKeyToAddress(GetKeyFromKeyPool());
1180 SetAddressBookName(strAddress, strName);
1181 SetDefaultReceivingAddress(strAddress);
1184 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1186 // Copy address box to clipboard
1187 if (wxTheClipboard->Open())
1189 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1190 wxTheClipboard->Close();
1194 void CMainFrame::OnListItemActivated(wxListEvent& event)
1196 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1198 CRITICAL_BLOCK(cs_mapWallet)
1200 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1201 if (mi == mapWallet.end())
1203 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1208 CTxDetailsDialog dialog(this, wtx);
1210 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1219 //////////////////////////////////////////////////////////////////////////////
1224 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1226 CRITICAL_BLOCK(cs_mapAddressBook)
1229 strHTML.reserve(4000);
1230 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1232 int64 nTime = wtx.GetTxTime();
1233 int64 nCredit = wtx.GetCredit();
1234 int64 nDebit = wtx.GetDebit();
1235 int64 nNet = nCredit - nDebit;
1239 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1240 int nRequests = wtx.GetRequestCount();
1241 if (nRequests != -1)
1244 strHTML += _(", has not been successfully broadcast yet");
1245 else if (nRequests == 1)
1246 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1248 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1252 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1258 if (wtx.IsCoinBase())
1260 strHTML += _("<b>Source:</b> Generated<br>");
1262 else if (!wtx.mapValue["from"].empty())
1264 // Online transaction
1265 if (!wtx.mapValue["from"].empty())
1266 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1270 // Offline transaction
1274 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1278 vector<unsigned char> vchPubKey;
1279 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1281 string strAddress = PubKeyToAddress(vchPubKey);
1282 if (mapAddressBook.count(strAddress))
1284 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1285 strHTML += _("<b>To:</b> ");
1286 strHTML += HtmlEscape(strAddress);
1287 if (!mapAddressBook[strAddress].empty())
1288 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1290 strHTML += _(" (yours)");
1305 if (!wtx.mapValue["to"].empty())
1307 // Online transaction
1308 strAddress = wtx.mapValue["to"];
1309 strHTML += _("<b>To:</b> ");
1310 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1311 strHTML += mapAddressBook[strAddress] + " ";
1312 strHTML += HtmlEscape(strAddress) + "<br>";
1319 if (wtx.IsCoinBase() && nCredit == 0)
1324 int64 nUnmatured = 0;
1325 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1326 nUnmatured += txout.GetCredit();
1327 strHTML += _("<b>Credit:</b> ");
1328 if (wtx.IsInMainChain())
1329 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1331 strHTML += _("(not accepted)");
1339 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1343 bool fAllFromMe = true;
1344 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1345 fAllFromMe = fAllFromMe && txin.IsMine();
1347 bool fAllToMe = true;
1348 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1349 fAllToMe = fAllToMe && txout.IsMine();
1356 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1361 if (wtx.mapValue["to"].empty())
1363 // Offline transaction
1365 if (ExtractHash160(txout.scriptPubKey, hash160))
1367 string strAddress = Hash160ToAddress(hash160);
1368 strHTML += _("<b>To:</b> ");
1369 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1370 strHTML += mapAddressBook[strAddress] + " ";
1371 strHTML += strAddress;
1376 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1382 int64 nChange = wtx.GetChange();
1383 int64 nValue = nCredit - nChange;
1384 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1385 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1388 int64 nTxFee = nDebit - wtx.GetValueOut();
1390 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1395 // Mixed debit transaction
1397 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1399 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1400 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1402 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1406 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1412 if (!wtx.mapValue["message"].empty())
1413 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1414 if (!wtx.mapValue["comment"].empty())
1415 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1417 if (wtx.IsCoinBase())
1418 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>";
1426 strHTML += "<hr><br>debug print<br><br>";
1427 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1429 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1430 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1432 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1434 strHTML += "<br><b>Transaction:</b><br>";
1435 strHTML += HtmlEscape(wtx.ToString(), true);
1437 strHTML += "<br><b>Inputs:</b><br>";
1438 CRITICAL_BLOCK(cs_mapWallet)
1440 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1442 COutPoint prevout = txin.prevout;
1443 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1444 if (mi != mapWallet.end())
1446 const CWalletTx& prev = (*mi).second;
1447 if (prevout.n < prev.vout.size())
1449 strHTML += HtmlEscape(prev.ToString(), true);
1450 strHTML += " " + FormatTxStatus(prev) + ", ";
1451 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1460 strHTML += "</font></html>";
1461 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1462 m_htmlWin->SetPage(strHTML);
1463 m_buttonOK->SetFocus();
1467 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1477 //////////////////////////////////////////////////////////////////////////////
1483 string StartupShortcutPath()
1485 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1488 bool GetStartOnSystemStartup()
1490 return filesystem::exists(StartupShortcutPath().c_str());
1493 void SetStartOnSystemStartup(bool fAutoStart)
1495 // If the shortcut exists already, remove it for updating
1496 remove(StartupShortcutPath().c_str());
1502 // Get a pointer to the IShellLink interface.
1503 IShellLink* psl = NULL;
1504 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1505 CLSCTX_INPROC_SERVER, IID_IShellLink,
1506 reinterpret_cast<void**>(&psl));
1508 if (SUCCEEDED(hres))
1510 // Get the current executable path
1511 TCHAR pszExePath[MAX_PATH];
1512 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1514 // Set the path to the shortcut target
1515 psl->SetPath(pszExePath);
1516 PathRemoveFileSpec(pszExePath);
1517 psl->SetWorkingDirectory(pszExePath);
1518 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1520 // Query IShellLink for the IPersistFile interface for
1521 // saving the shortcut in persistent storage.
1522 IPersistFile* ppf = NULL;
1523 hres = psl->QueryInterface(IID_IPersistFile,
1524 reinterpret_cast<void**>(&ppf));
1525 if (SUCCEEDED(hres))
1527 WCHAR pwsz[MAX_PATH];
1528 // Ensure that the string is ANSI.
1529 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1530 // Save the link by calling IPersistFile::Save.
1531 hres = ppf->Save(pwsz, TRUE);
1540 #elif defined(__WXGTK__)
1542 // Follow the Desktop Application Autostart Spec:
1543 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1545 boost::filesystem::path GetAutostartDir()
1547 namespace fs = boost::filesystem;
1549 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1550 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1551 char* pszHome = getenv("HOME");
1552 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1556 boost::filesystem::path GetAutostartFilePath()
1558 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1561 bool GetStartOnSystemStartup()
1563 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1564 if (!optionFile.good())
1566 // Scan through file for "Hidden=true":
1568 while (!optionFile.eof())
1570 getline(optionFile, line);
1571 if (line.find("Hidden") != string::npos &&
1572 line.find("true") != string::npos)
1580 void SetStartOnSystemStartup(bool fAutoStart)
1584 unlink(GetAutostartFilePath().native_file_string().c_str());
1588 char pszExePath[MAX_PATH+1];
1589 memset(pszExePath, 0, sizeof(pszExePath));
1590 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1593 boost::filesystem::create_directories(GetAutostartDir());
1595 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1596 if (!optionFile.good())
1598 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1601 // Write a bitcoin.desktop file to the autostart directory:
1602 optionFile << "[Desktop Entry]\n";
1603 optionFile << "Type=Application\n";
1604 optionFile << "Name=Bitcoin\n";
1605 optionFile << "Exec=" << pszExePath << "\n";
1606 optionFile << "Terminal=false\n";
1607 optionFile << "Hidden=false\n";
1613 // TODO: OSX startup stuff; see:
1614 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1616 bool GetStartOnSystemStartup() { return false; }
1617 void SetStartOnSystemStartup(bool fAutoStart) { }
1626 //////////////////////////////////////////////////////////////////////////////
1631 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1633 // Set up list box of page choices
1634 m_listBox->Append(_("Main"));
1635 //m_listBox->Append(_("Test 2"));
1636 m_listBox->SetSelection(0);
1639 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1641 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1642 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1643 if (!GetBoolArg("-minimizetotray"))
1645 // Minimize to tray is just too buggy on Linux
1646 fMinimizeToTray = false;
1647 m_checkBoxMinimizeToTray->SetValue(false);
1648 m_checkBoxMinimizeToTray->Enable(false);
1649 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1652 #ifdef __WXMAC_OSX__
1653 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1657 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1658 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1659 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1660 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1662 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1664 m_checkBoxUseUPnP->Enable(false);
1665 m_checkBoxUseProxy->SetValue(fUseProxy);
1666 m_textCtrlProxyIP->Enable(fUseProxy);
1667 m_textCtrlProxyPort->Enable(fUseProxy);
1668 m_staticTextProxyIP->Enable(fUseProxy);
1669 m_staticTextProxyPort->Enable(fUseProxy);
1670 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1671 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1673 m_buttonOK->SetFocus();
1676 void COptionsDialog::SelectPage(int nPage)
1678 m_panelMain->Show(nPage == 0);
1679 m_panelTest2->Show(nPage == 1);
1681 m_scrolledWindow->Layout();
1682 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1685 void COptionsDialog::OnListBox(wxCommandEvent& event)
1687 SelectPage(event.GetSelection());
1690 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1693 int64 nTmp = nTransactionFee;
1694 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1695 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1698 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1700 m_textCtrlProxyIP->Enable(event.IsChecked());
1701 m_textCtrlProxyPort->Enable(event.IsChecked());
1702 m_staticTextProxyIP->Enable(event.IsChecked());
1703 m_staticTextProxyPort->Enable(event.IsChecked());
1706 CAddress COptionsDialog::GetProxyAddr()
1708 // Be careful about byte order, addr.ip and addr.port are big endian
1709 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1710 if (addr.ip == INADDR_NONE)
1711 addr.ip = addrProxy.ip;
1712 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1713 addr.port = htons(nPort);
1714 if (nPort <= 0 || nPort > USHRT_MAX)
1715 addr.port = addrProxy.port;
1719 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1722 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1723 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1727 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1729 OnButtonApply(event);
1733 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1738 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1742 int64 nPrevTransactionFee = nTransactionFee;
1743 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1744 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1746 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1748 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1749 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1752 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1754 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1755 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1756 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1759 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1761 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1762 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1765 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1767 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1768 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1772 fUseProxy = m_checkBoxUseProxy->GetValue();
1773 walletdb.WriteSetting("fUseProxy", fUseProxy);
1775 addrProxy = GetProxyAddr();
1776 walletdb.WriteSetting("addrProxy", addrProxy);
1784 //////////////////////////////////////////////////////////////////////////////
1789 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1791 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
1793 // Change (c) into UTF-8 or ANSI copyright symbol
1794 wxString str = m_staticTextMain->GetLabel();
1796 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1798 str.Replace("(c)", "\xA9");
1800 m_staticTextMain->SetLabel(str);
1802 // Resize on Linux to make the window fit the text.
1803 // The text was wrapped manually rather than using the Wrap setting because
1804 // the wrap would be too small on Linux and it can't be changed at this point.
1805 wxFont fontTmp = m_staticTextMain->GetFont();
1806 if (fontTmp.GetPointSize() > 8);
1807 fontTmp.SetPointSize(8);
1808 m_staticTextMain->SetFont(fontTmp);
1809 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1813 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1823 //////////////////////////////////////////////////////////////////////////////
1828 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1831 m_textCtrlAddress->SetValue(strAddress);
1832 m_choiceTransferType->SetSelection(0);
1833 m_bitmapCheckMark->Show(false);
1834 fEnabledPrev = true;
1835 m_textCtrlAddress->SetFocus();
1837 //// todo: should add a display of your balance for convenience
1839 wxFont fontTmp = m_staticTextInstructions->GetFont();
1840 if (fontTmp.GetPointSize() > 9);
1841 fontTmp.SetPointSize(9);
1842 m_staticTextInstructions->SetFont(fontTmp);
1848 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1851 // Fixup the tab order
1852 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1853 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1857 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1859 // Reformat the amount
1861 if (m_textCtrlAmount->GetValue().Trim().empty())
1864 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1865 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1868 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1870 // Open address book
1871 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1872 if (dialog.ShowModal())
1873 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1876 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1878 // Copy clipboard to address box
1879 if (wxTheClipboard->Open())
1881 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1883 wxTextDataObject data;
1884 wxTheClipboard->GetData(data);
1885 m_textCtrlAddress->SetValue(data.GetText());
1887 wxTheClipboard->Close();
1891 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1893 static CCriticalSection cs_sendlock;
1894 TRY_CRITICAL_BLOCK(cs_sendlock)
1897 string strAddress = (string)m_textCtrlAddress->GetValue();
1901 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1903 wxMessageBox(_("Error in amount "), _("Send Coins"));
1906 if (nValue > GetBalance())
1908 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1911 if (nValue + nTransactionFee > GetBalance())
1913 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1917 // Parse bitcoin address
1919 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1921 if (fBitcoinAddress)
1923 CRITICAL_BLOCK(cs_main)
1925 // Send to bitcoin address
1926 CScript scriptPubKey;
1927 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1929 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1931 wxMessageBox(_("Payment sent "), _("Sending..."));
1932 else if (strError == "ABORTED")
1933 return; // leave send dialog open
1936 wxMessageBox(strError + " ", _("Sending..."));
1945 CAddress addr(strAddress);
1946 if (!addr.IsValid())
1948 wxMessageBox(_("Invalid address "), _("Send Coins"));
1953 wtx.mapValue["to"] = strAddress;
1955 // Send to IP address
1956 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1957 if (!pdialog->ShowModal())
1961 CRITICAL_BLOCK(cs_mapAddressBook)
1962 if (!mapAddressBook.count(strAddress))
1963 SetAddressBookName(strAddress, "");
1969 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1980 //////////////////////////////////////////////////////////////////////////////
1985 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
1990 start = wxDateTime::UNow();
1991 memset(pszStatus, 0, sizeof(pszStatus));
1998 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2001 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2002 m_textCtrlStatus->SetValue("");
2004 CreateThread(SendingDialogStartTransfer, this);
2007 CSendingDialog::~CSendingDialog()
2009 printf("~CSendingDialog()\n");
2012 void CSendingDialog::Close()
2014 // Last one out turn out the lights.
2015 // fWorkDone signals that work side is done and UI thread should call destroy.
2016 // fUIDone signals that UI window has closed and work thread should call destroy.
2017 // This allows the window to disappear and end modality when cancelled
2018 // without making the user wait for ConnectNode to return. The dialog object
2019 // hangs around in the background until the work thread exits.
2030 void CSendingDialog::OnClose(wxCloseEvent& event)
2032 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2039 wxCommandEvent cmdevent;
2040 OnButtonCancel(cmdevent);
2044 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2050 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2056 void CSendingDialog::OnPaint(wxPaintEvent& event)
2059 if (strlen(pszStatus) > 130)
2060 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2062 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2063 m_staticTextSending->SetFocus();
2065 m_buttonCancel->Enable(false);
2068 m_buttonOK->Enable(true);
2069 m_buttonOK->SetFocus();
2070 m_buttonCancel->Enable(false);
2072 if (fAbort && fCanCancel && IsShown())
2074 strcpy(pszStatus, _("CANCELLED"));
2075 m_buttonOK->Enable(true);
2076 m_buttonOK->SetFocus();
2077 m_buttonCancel->Enable(false);
2078 m_buttonCancel->SetLabel(_("Cancelled"));
2080 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2086 // Everything from here on is not in the UI thread and must only communicate
2087 // with the rest of the dialog through variables and calling repaint.
2090 void CSendingDialog::Repaint()
2094 GetEventHandler()->AddPendingEvent(event);
2097 bool CSendingDialog::Status()
2104 if (fAbort && fCanCancel)
2106 memset(pszStatus, 0, 10);
2107 strcpy(pszStatus, _("CANCELLED"));
2115 bool CSendingDialog::Status(const string& str)
2120 // This can be read by the UI thread at any time,
2121 // so copy in a way that can be read cleanly at all times.
2122 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2123 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2129 bool CSendingDialog::Error(const string& str)
2133 Status(string(_("Error: ")) + str);
2137 void SendingDialogStartTransfer(void* parg)
2139 ((CSendingDialog*)parg)->StartTransfer();
2142 void CSendingDialog::StartTransfer()
2144 // Make sure we have enough money
2145 if (nPrice + nTransactionFee > GetBalance())
2147 Error(_("Insufficient funds"));
2151 // We may have connected already for product details
2152 if (!Status(_("Connecting...")))
2154 CNode* pnode = ConnectNode(addr, 15 * 60);
2157 Error(_("Unable to connect"));
2161 // Send order to seller, with response going to OnReply2 via event handler
2162 if (!Status(_("Requesting public key...")))
2164 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2167 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2169 ((CSendingDialog*)parg)->OnReply2(vRecv);
2172 void CSendingDialog::OnReply2(CDataStream& vRecv)
2174 if (!Status(_("Received public key...")))
2177 CScript scriptPubKey;
2186 vRecv >> strMessage;
2188 Error(_("Recipient is not accepting transactions sent by IP address"));
2190 Error(_("Transfer was not accepted"));
2191 //// todo: enlarge the window and enable a hidden white box to put seller's message
2194 vRecv >> scriptPubKey;
2198 //// what do we want to do about this?
2199 Error(_("Invalid response received"));
2203 // Pause to give the user a chance to cancel
2204 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2211 CRITICAL_BLOCK(cs_main)
2214 if (!Status(_("Creating transaction...")))
2216 if (nPrice + nTransactionFee > GetBalance())
2218 Error(_("Insufficient funds"));
2221 CReserveKey reservekey;
2223 if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2225 if (nPrice + nFeeRequired > GetBalance())
2226 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()));
2228 Error(_("Transaction creation failed"));
2233 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2235 Error(_("Transaction aborted"));
2239 // Make sure we're still connected
2240 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2243 Error(_("Lost connection, transaction cancelled"));
2247 // Last chance to cancel
2259 if (!Status(_("Sending payment...")))
2263 if (!CommitTransaction(wtx, reservekey))
2265 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."));
2269 // Send payment tx to seller, with response going to OnReply3 via event handler
2270 CWalletTx wtxSend = wtx;
2271 wtxSend.fFromMe = false;
2272 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2274 Status(_("Waiting for confirmation..."));
2279 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2281 ((CSendingDialog*)parg)->OnReply3(vRecv);
2284 void CSendingDialog::OnReply3(CDataStream& vRecv)
2292 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2293 "The transaction is recorded and will credit to the recipient,\n"
2294 "but the comment information will be blank."));
2300 //// what do we want to do about this?
2301 Error(_("Payment was sent, but an invalid response was received"));
2307 Status(_("Payment completed"));
2315 //////////////////////////////////////////////////////////////////////////////
2317 // CAddressBookDialog
2320 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2322 // Set initially selected page
2323 wxNotebookEvent event;
2324 event.SetSelection(nPageIn);
2325 OnNotebookPageChanged(event);
2326 m_notebook->ChangeSelection(nPageIn);
2328 fDuringSend = fDuringSendIn;
2330 m_buttonCancel->Show(false);
2333 wxIcon iconAddressBook;
2334 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2335 SetIcon(iconAddressBook);
2337 // Init column headers
2338 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2339 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2340 m_listCtrlSending->SetFocus();
2341 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2342 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2343 m_listCtrlReceiving->SetFocus();
2345 // Fill listctrl with address book data
2346 CRITICAL_BLOCK(cs_mapKeys)
2347 CRITICAL_BLOCK(cs_mapAddressBook)
2349 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2350 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook)
2352 string strAddress = item.first;
2353 string strName = item.second;
2355 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2356 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2357 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2358 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2359 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2364 wxString CAddressBookDialog::GetSelectedAddress()
2366 int nIndex = GetSelection(m_listCtrl);
2369 return GetItemText(m_listCtrl, nIndex, 1);
2372 wxString CAddressBookDialog::GetSelectedSendingAddress()
2374 int nIndex = GetSelection(m_listCtrlSending);
2377 return GetItemText(m_listCtrlSending, nIndex, 1);
2380 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2382 int nIndex = GetSelection(m_listCtrlReceiving);
2385 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2388 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2391 nPage = event.GetSelection();
2392 if (nPage == SENDING)
2393 m_listCtrl = m_listCtrlSending;
2394 else if (nPage == RECEIVING)
2395 m_listCtrl = m_listCtrlReceiving;
2396 m_buttonDelete->Show(nPage == SENDING);
2397 m_buttonCopy->Show(nPage == RECEIVING);
2399 m_listCtrl->SetFocus();
2402 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2404 // Update address book with edited name
2406 if (event.IsEditCancelled())
2408 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2409 SetAddressBookName(strAddress, string(event.GetText()));
2410 pframeMain->RefreshListCtrl();
2413 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2416 if (nPage == RECEIVING)
2417 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2420 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2425 // Doubleclick returns selection
2426 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2430 // Doubleclick edits item
2431 wxCommandEvent event2;
2432 OnButtonEdit(event2);
2435 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2437 if (nPage != SENDING)
2439 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2441 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2443 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2444 CWalletDB().EraseName(strAddress);
2445 m_listCtrl->DeleteItem(nIndex);
2448 pframeMain->RefreshListCtrl();
2451 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2453 // Copy address box to clipboard
2454 if (wxTheClipboard->Open())
2456 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2457 wxTheClipboard->Close();
2461 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2464 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2466 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2470 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2472 int nIndex = GetSelection(m_listCtrl);
2475 string strName = (string)m_listCtrl->GetItemText(nIndex);
2476 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2477 string strAddressOrg = strAddress;
2479 if (nPage == SENDING)
2481 // Ask name and address
2484 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2485 if (!dialog.ShowModal())
2487 strName = dialog.GetValue1();
2488 strAddress = dialog.GetValue2();
2490 while (CheckIfMine(strAddress, _("Edit Address")));
2493 else if (nPage == RECEIVING)
2496 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2497 if (!dialog.ShowModal())
2499 strName = dialog.GetValue();
2503 if (strAddress != strAddressOrg)
2504 CWalletDB().EraseName(strAddressOrg);
2505 SetAddressBookName(strAddress, strName);
2506 m_listCtrl->SetItem(nIndex, 1, strAddress);
2507 m_listCtrl->SetItemText(nIndex, strName);
2508 pframeMain->RefreshListCtrl();
2511 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2516 if (nPage == SENDING)
2518 // Ask name and address
2521 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2522 if (!dialog.ShowModal())
2524 strName = dialog.GetValue1();
2525 strAddress = dialog.GetValue2();
2527 while (CheckIfMine(strAddress, _("Add Address")));
2529 else if (nPage == RECEIVING)
2532 CGetTextFromUserDialog dialog(this,
2533 _("New Receiving Address"),
2534 _("You should use a new address for each payment you receive.\n\nLabel"),
2536 if (!dialog.ShowModal())
2538 strName = dialog.GetValue();
2541 strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2544 // Add to list and select it
2545 SetAddressBookName(strAddress, strName);
2546 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2547 SetSelection(m_listCtrl, nIndex);
2548 m_listCtrl->SetFocus();
2549 if (nPage == SENDING)
2550 pframeMain->RefreshListCtrl();
2553 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2556 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2559 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2565 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2576 //////////////////////////////////////////////////////////////////////////////
2583 ID_TASKBAR_RESTORE = 10001,
2586 ID_TASKBAR_GENERATE,
2590 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2591 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2592 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2593 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2594 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2595 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2596 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2599 void CMyTaskBarIcon::Show(bool fShow)
2601 static char pszPrevTip[200];
2604 string strTooltip = _("Bitcoin");
2605 if (fGenerateBitcoins)
2606 strTooltip = _("Bitcoin - Generating");
2607 if (fGenerateBitcoins && vNodes.empty())
2608 strTooltip = _("Bitcoin - (not connected)");
2610 // Optimization, only update when changed, using char array to be reentrant
2611 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2613 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2615 // somehow it'll choose the wrong size and scale it down if
2616 // we use the main icon, so we hand it one with only 16x16
2617 SetIcon(wxICON(favicon), strTooltip);
2619 SetIcon(bitcoin80_xpm, strTooltip);
2625 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2630 void CMyTaskBarIcon::Hide()
2635 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2640 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2645 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2648 CSendDialog dialog(pframeMain);
2652 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2654 // Since it's modal, get the main window to do it
2655 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2656 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2659 void CMyTaskBarIcon::Restore()
2662 wxIconizeEvent event(0, false);
2663 pframeMain->GetEventHandler()->AddPendingEvent(event);
2664 pframeMain->Iconize(false);
2665 pframeMain->Raise();
2668 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2670 event.Check(fGenerateBitcoins);
2673 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2675 pframeMain->Close(true);
2678 void CMyTaskBarIcon::UpdateTooltip()
2680 if (IsIconInstalled())
2684 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2686 wxMenu* pmenu = new wxMenu;
2687 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2688 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2689 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2690 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2691 pmenu->AppendSeparator();
2692 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2702 //////////////////////////////////////////////////////////////////////////////
2707 void CreateMainWindow()
2709 pframeMain = new CMainFrame(NULL);
2710 if (GetBoolArg("-min"))
2711 pframeMain->Iconize(true);
2712 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2713 if (!GetBoolArg("-minimizetotray"))
2714 fMinimizeToTray = false;
2716 pframeMain->Show(true); // have to show first to get taskbar button to hide
2717 if (fMinimizeToTray && pframeMain->IsIconized())
2718 fClosedToTray = true;
2719 pframeMain->Show(!fClosedToTray);
2720 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2721 CreateThread(ThreadDelayedRepaint, NULL);
2725 // Define a new application
2726 class CMyApp : public wxApp
2735 // Hook Initialize so we can start without GUI
2736 virtual bool Initialize(int& argc, wxChar** argv);
2738 // 2nd-level exception handling: we get all the exceptions occurring in any
2739 // event handler here
2740 virtual bool OnExceptionInMainLoop();
2742 // 3rd, and final, level exception handling: whenever an unhandled
2743 // exception is caught, this function is called
2744 virtual void OnUnhandledException();
2746 // and now for something different: this function is called in case of a
2747 // crash (e.g. dereferencing null pointer, division by 0, ...)
2748 virtual void OnFatalException();
2751 IMPLEMENT_APP(CMyApp)
2753 bool CMyApp::Initialize(int& argc, wxChar** argv)
2755 for (int i = 1; i < argc; i++)
2756 if (!IsSwitchChar(argv[i][0]))
2757 fCommandLine = true;
2761 // wxApp::Initialize will remove environment-specific parameters,
2762 // so it's too early to call ParseParameters yet
2763 for (int i = 1; i < argc; i++)
2765 wxString str = argv[i];
2767 if (str.size() >= 1 && str[0] == '/')
2769 char pszLower[MAX_PATH];
2770 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2774 if (str == "-daemon")
2780 if (fDaemon || fCommandLine)
2782 // Call the original Initialize while suppressing error messages
2783 // and ignoring failure. If unable to initialize GTK, it fails
2784 // near the end so hopefully the last few things don't matter.
2787 wxApp::Initialize(argc, argv);
2796 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2800 pthread_exit((void*)0);
2802 pid_t sid = setsid();
2804 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
2811 return wxApp::Initialize(argc, argv);
2814 bool CMyApp::OnInit()
2816 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2817 // Disable malfunctioning wxWidgets debug assertion
2818 extern int g_isPainting;
2819 g_isPainting = 10000;
2821 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2822 SetAppName("Bitcoin");
2824 SetAppName("bitcoin");
2828 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2829 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2830 class wxMBConv_win32 : public wxMBConv
2834 size_t m_minMBCharWidth;
2836 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2837 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2841 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2842 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2843 g_locale.AddCatalogLookupPathPrefix("locale");
2845 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2846 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2848 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2849 g_locale.AddCatalog("bitcoin");
2851 return AppInit(argc, argv);
2854 int CMyApp::OnExit()
2857 return wxApp::OnExit();
2860 bool CMyApp::OnExceptionInMainLoop()
2866 catch (std::exception& e)
2868 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2869 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2875 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2876 wxLogWarning("Unknown exception");
2883 void CMyApp::OnUnhandledException()
2885 // this shows how we may let some exception propagate uncaught
2890 catch (std::exception& e)
2892 PrintException(&e, "CMyApp::OnUnhandledException()");
2893 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2899 PrintException(NULL, "CMyApp::OnUnhandledException()");
2900 wxLogWarning("Unknown exception");
2906 void CMyApp::OnFatalException()
2908 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);