1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
11 using namespace boost;
14 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
16 CMainFrame* pframeMain = NULL;
17 CMyTaskBarIcon* ptaskbaricon = NULL;
18 bool fClosedToTray = false;
29 //////////////////////////////////////////////////////////////////////////////
34 void HandleCtrlA(wxKeyEvent& event)
38 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
39 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
40 textCtrl->SetSelection(-1, -1);
45 //char pszHourFormat[256];
46 //pszHourFormat[0] = '\0';
47 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
48 //return (pszHourFormat[0] != '0');
52 string DateStr(int64 nTime)
54 // Can only be used safely here in the UI
55 return (string)wxDateTime((time_t)nTime).FormatDate();
58 string DateTimeStr(int64 nTime)
60 // Can only be used safely here in the UI
61 wxDateTime datetime((time_t)nTime);
63 return (string)datetime.Format("%x %H:%M");
65 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
68 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
70 // Helper to simplify access to listctrl
72 item.m_itemId = nIndex;
74 item.m_mask = wxLIST_MASK_TEXT;
75 if (!listCtrl->GetItem(item))
77 return item.GetText();
80 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
82 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
83 listCtrl->SetItem(nIndex, 1, str1);
87 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
89 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
90 listCtrl->SetItem(nIndex, 1, str1);
91 listCtrl->SetItem(nIndex, 2, str2);
92 listCtrl->SetItem(nIndex, 3, str3);
93 listCtrl->SetItem(nIndex, 4, str4);
97 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
99 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
100 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
101 listCtrl->SetItem(nIndex, 1, str1);
102 listCtrl->SetItem(nIndex, 2, str2);
103 listCtrl->SetItem(nIndex, 3, str3);
104 listCtrl->SetItem(nIndex, 4, str4);
108 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
110 // Repaint on Windows is more flickery if the colour has ever been set,
111 // so don't want to set it unless it's different. Default colour has
112 // alpha 0 transparent, so our colours don't match using operator==.
113 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
115 c1 = wxColour(0,0,0);
116 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
117 listCtrl->SetItemTextColour(nIndex, colour);
120 void SetSelection(wxListCtrl* listCtrl, int nIndex)
122 int nSize = listCtrl->GetItemCount();
123 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
124 for (int i = 0; i < nSize; i++)
125 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
128 int GetSelection(wxListCtrl* listCtrl)
130 int nSize = listCtrl->GetItemCount();
131 for (int i = 0; i < nSize; i++)
132 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
137 string HtmlEscape(const char* psz, bool fMultiLine=false)
140 for (const char* p = psz; *p; p++)
142 if (*p == '<') len += 4;
143 else if (*p == '>') len += 4;
144 else if (*p == '&') len += 5;
145 else if (*p == '"') len += 6;
146 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
147 else if (*p == '\n' && fMultiLine) len += 5;
153 for (const char* p = psz; *p; p++)
155 if (*p == '<') str += "<";
156 else if (*p == '>') str += ">";
157 else if (*p == '&') str += "&";
158 else if (*p == '"') str += """;
159 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
160 else if (*p == '\n' && fMultiLine) str += "<br>\n";
167 string HtmlEscape(const string& str, bool fMultiLine=false)
169 return HtmlEscape(str.c_str(), fMultiLine);
172 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
174 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
178 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
181 return wxMessageBox(message, caption, style, parent, x, y);
183 if (wxThread::IsMain() || fDaemon)
185 return wxMessageBox(message, caption, style, parent, x, y);
191 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
199 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
201 if (nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
203 string strMessage = strprintf(
204 _("This transaction is over the size limit. You can still send it for a fee of %s, "
205 "which goes to the nodes that process your transaction and helps to support the network. "
206 "Do you want to pay the fee?"),
207 FormatMoney(nFeeRequired).c_str());
208 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
211 void CalledSetStatusBar(const string& strText, int nField)
213 if (nField == 0 && GetWarnings("statusbar") != "")
215 if (pframeMain && pframeMain->m_statusBar)
216 pframeMain->m_statusBar->SetStatusText(strText, nField);
219 void SetDefaultReceivingAddress(const string& strAddress)
221 // Update main window address and database
222 if (pframeMain == NULL)
224 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
227 if (!AddressToHash160(strAddress, hash160))
229 if (!mapPubKeys.count(hash160))
231 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
232 pframeMain->m_textCtrlAddress->SetValue(strAddress);
245 //////////////////////////////////////////////////////////////////////////////
250 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
252 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
254 // Set initially selected page
255 wxNotebookEvent event;
256 event.SetSelection(0);
257 OnNotebookPageChanged(event);
258 m_notebook->ChangeSelection(0);
261 fRefreshListCtrl = false;
262 fRefreshListCtrlRunning = false;
263 fOnSetFocusAddress = false;
265 m_choiceFilter->SetSelection(0);
266 double dResize = 1.0;
268 SetIcon(wxICON(bitcoin));
270 SetIcon(bitcoin80_xpm);
271 SetBackgroundColour(m_toolBar->GetBackgroundColour());
272 wxFont fontTmp = m_staticText41->GetFont();
273 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
274 m_staticTextBalance->SetFont(fontTmp);
275 m_staticTextBalance->SetSize(140, 17);
276 // resize to fit ubuntu's huge default font
278 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
280 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
281 m_listCtrl->SetFocus();
282 ptaskbaricon = new CMyTaskBarIcon();
284 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
285 // to their standard places, leaving these menus empty.
286 GetMenuBar()->Remove(2); // remove Help menu
287 GetMenuBar()->Remove(0); // remove File menu
290 // Init column headers
291 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
292 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
298 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
299 BOOST_FOREACH(wxListCtrl* p, pplistCtrl)
301 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
302 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
303 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
304 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
305 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
306 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
307 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
311 int pnWidths[3] = { -100, 88, 300 };
313 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
314 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
316 m_statusBar->SetFieldsCount(3, pnWidths);
318 // Fill your address text box
319 vector<unsigned char> vchPubKey;
320 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
321 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
323 // Fill listctrl with wallet transactions
327 CMainFrame::~CMainFrame()
334 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
337 nPage = event.GetSelection();
340 m_listCtrl = m_listCtrlAll;
341 fShowGenerated = true;
343 fShowReceived = true;
345 else if (nPage == SENTRECEIVED)
347 m_listCtrl = m_listCtrlSentReceived;
348 fShowGenerated = false;
350 fShowReceived = true;
352 else if (nPage == SENT)
354 m_listCtrl = m_listCtrlSent;
355 fShowGenerated = false;
357 fShowReceived = false;
359 else if (nPage == RECEIVED)
361 m_listCtrl = m_listCtrlReceived;
362 fShowGenerated = false;
364 fShowReceived = true;
367 m_listCtrl->SetFocus();
370 void CMainFrame::OnClose(wxCloseEvent& event)
372 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
374 // Divert close to minimize
376 fClosedToTray = true;
382 CreateThread(Shutdown, NULL);
386 void CMainFrame::OnIconize(wxIconizeEvent& event)
389 // Hide the task bar button when minimized.
390 // Event is sent when the frame is minimized or restored.
391 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
392 // to get rid of the deprecated warning. Just ignore it.
393 if (!event.Iconized())
394 fClosedToTray = false;
395 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
396 if (GetBoolArg("-minimizetotray")) {
398 // The tray icon sometimes disappears on ubuntu karmic
399 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
400 // Reports of CPU peg on 64-bit linux
401 if (fMinimizeToTray && event.Iconized())
402 fClosedToTray = true;
403 Show(!fClosedToTray);
404 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
405 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
410 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
414 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
415 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
418 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
420 // Hidden columns not resizeable
421 if (event.GetColumn() <= 1 && !fDebug)
427 int CMainFrame::GetSortIndex(const string& strSort)
432 // The wx generic listctrl implementation used on GTK doesn't sort,
433 // so we have to do it ourselves. Remember, we sort in reverse order.
434 // In the wx generic implementation, they store the list of items
435 // in a vector, so indexed lookups are fast, but inserts are slower
436 // the closer they are to the top.
438 int high = m_listCtrl->GetItemCount();
441 int mid = low + ((high - low) / 2);
442 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
451 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)
453 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
454 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
457 if (!fNew && nIndex == -1)
459 string strHash = " " + hashKey.ToString();
460 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
461 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
465 // fNew is for blind insert, only use if you're sure it's new
466 if (fNew || nIndex == -1)
468 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
472 // If sort key changed, must delete and reinsert to make it relocate
473 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
475 m_listCtrl->DeleteItem(nIndex);
476 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
480 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
481 m_listCtrl->SetItem(nIndex, 2, str2);
482 m_listCtrl->SetItem(nIndex, 3, str3);
483 m_listCtrl->SetItem(nIndex, 4, str4);
484 m_listCtrl->SetItem(nIndex, 5, str5);
485 m_listCtrl->SetItem(nIndex, 6, str6);
486 m_listCtrl->SetItemData(nIndex, nData);
487 SetItemTextColour(m_listCtrl, nIndex, colour);
490 bool CMainFrame::DeleteLine(uint256 hashKey)
492 long nData = *(long*)&hashKey;
496 string strHash = " " + hashKey.ToString();
497 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
498 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
502 m_listCtrl->DeleteItem(nIndex);
507 string FormatTxStatus(const CWalletTx& wtx)
512 if (wtx.nLockTime < 500000000)
513 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
515 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
519 int nDepth = wtx.GetDepthInMainChain();
520 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
521 return strprintf(_("%d/offline?"), nDepth);
523 return strprintf(_("%d/unconfirmed"), nDepth);
525 return strprintf(_("%d confirmations"), nDepth);
529 string SingleLine(const string& strIn)
532 bool fOneSpace = false;
533 BOOST_FOREACH(unsigned char c, strIn)
541 if (fOneSpace && !strOut.empty())
550 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
552 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
553 int64 nCredit = wtx.GetCredit(true);
554 int64 nDebit = wtx.GetDebit();
555 int64 nNet = nCredit - nDebit;
556 uint256 hash = wtx.GetHash();
557 string strStatus = FormatTxStatus(wtx);
558 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
559 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
560 map<string, string> mapValue = wtx.mapValue;
561 wtx.nLinesDisplayed = 1;
565 if (wtx.IsCoinBase())
567 // Don't show generated coin until confirmed by at least one block after it
568 // so we don't get the user's hopes up until it looks like it's probably accepted.
570 // It is not an error when generated blocks are not accepted. By design,
571 // some percentage of blocks, like 10% or more, will end up not accepted.
572 // This is the normal mechanism by which the network copes with latency.
574 // We display regular transactions right away before any confirmation
575 // because they can always get into some block eventually. Generated coins
576 // are special because if their block is not accepted, they are not valid.
578 if (wtx.GetDepthInMainChain() < 2)
580 wtx.nLinesDisplayed = 0;
588 // Find the block the tx is in
589 CBlockIndex* pindex = NULL;
590 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
591 if (mi != mapBlockIndex.end())
592 pindex = (*mi).second;
594 // Sort order, unrecorded transactions sort to the top
595 string strSort = strprintf("%010d-%01d-%010u",
596 (pindex ? pindex->nHeight : INT_MAX),
597 (wtx.IsCoinBase() ? 1 : 0),
601 if (nNet > 0 || wtx.IsCoinBase())
606 string strDescription;
607 if (wtx.IsCoinBase())
610 strDescription = _("Generated");
613 int64 nUnmatured = 0;
614 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
615 nUnmatured += txout.GetCredit();
616 if (wtx.IsInMainChain())
618 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
620 // Check if the block was requested by anyone
621 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
622 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
626 strDescription = _("Generated (not accepted)");
630 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
632 // Received by IP connection
635 if (!mapValue["from"].empty())
636 strDescription += _("From: ") + mapValue["from"];
637 if (!mapValue["message"].empty())
639 if (!strDescription.empty())
640 strDescription += " - ";
641 strDescription += mapValue["message"];
646 // Received by Bitcoin Address
649 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
653 vector<unsigned char> vchPubKey;
654 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
656 CRITICAL_BLOCK(cs_mapAddressBook)
658 //strDescription += _("Received payment to ");
659 //strDescription += _("Received with address ");
660 strDescription += _("Received with: ");
661 string strAddress = PubKeyToAddress(vchPubKey);
662 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
663 if (mi != mapAddressBook.end() && !(*mi).second.empty())
665 string strLabel = (*mi).second;
666 strDescription += strAddress.substr(0,12) + "... ";
667 strDescription += "(" + strLabel + ")";
670 strDescription += strAddress;
678 string strCredit = FormatMoney(nNet, true);
680 strCredit = "[" + strCredit + "]";
682 InsertLine(fNew, nIndex, hash, strSort, colour,
684 nTime ? DateTimeStr(nTime) : "",
685 SingleLine(strDescription),
691 bool fAllFromMe = true;
692 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
693 fAllFromMe = fAllFromMe && txin.IsMine();
695 bool fAllToMe = true;
696 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
697 fAllToMe = fAllToMe && txout.IsMine();
699 if (fAllFromMe && fAllToMe)
702 int64 nChange = wtx.GetChange();
703 InsertLine(fNew, nIndex, hash, strSort, colour,
705 nTime ? DateTimeStr(nTime) : "",
706 _("Payment to yourself"),
707 FormatMoney(-(nDebit - nChange), true),
708 FormatMoney(nCredit - nChange, true));
718 int64 nTxFee = nDebit - wtx.GetValueOut();
719 wtx.nLinesDisplayed = 0;
720 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
722 const CTxOut& txout = wtx.vout[nOut];
727 if (!mapValue["to"].empty())
730 strAddress = mapValue["to"];
734 // Sent to Bitcoin Address
736 if (ExtractHash160(txout.scriptPubKey, hash160))
737 strAddress = Hash160ToAddress(hash160);
740 string strDescription = _("To: ");
741 CRITICAL_BLOCK(cs_mapAddressBook)
742 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
743 strDescription += mapAddressBook[strAddress] + " ";
744 strDescription += strAddress;
745 if (!mapValue["message"].empty())
747 if (!strDescription.empty())
748 strDescription += " - ";
749 strDescription += mapValue["message"];
751 else if (!mapValue["comment"].empty())
753 if (!strDescription.empty())
754 strDescription += " - ";
755 strDescription += mapValue["comment"];
758 int64 nValue = txout.nValue;
765 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
767 nTime ? DateTimeStr(nTime) : "",
768 SingleLine(strDescription),
769 FormatMoney(-nValue, true),
772 wtx.nLinesDisplayed++;
778 // Mixed debit transaction, can't break down payees
780 bool fAllMine = true;
781 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
782 fAllMine = fAllMine && txout.IsMine();
783 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
784 fAllMine = fAllMine && txin.IsMine();
786 InsertLine(fNew, nIndex, hash, strSort, colour,
788 nTime ? DateTimeStr(nTime) : "",
790 FormatMoney(nNet, true),
798 void CMainFrame::RefreshListCtrl()
800 fRefreshListCtrl = true;
804 void CMainFrame::OnIdle(wxIdleEvent& event)
806 if (fRefreshListCtrl)
808 // Collect list of wallet transactions and sort newest first
809 bool fEntered = false;
810 vector<pair<unsigned int, uint256> > vSorted;
811 TRY_CRITICAL_BLOCK(cs_mapWallet)
813 printf("RefreshListCtrl starting\n");
815 fRefreshListCtrl = false;
816 vWalletUpdated.clear();
818 // Do the newest transactions first
819 vSorted.reserve(mapWallet.size());
820 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
822 const CWalletTx& wtx = (*it).second;
823 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
824 vSorted.push_back(make_pair(nTime, (*it).first));
826 m_listCtrl->DeleteAllItems();
831 sort(vSorted.begin(), vSorted.end());
834 for (int i = 0; i < vSorted.size();)
838 bool fEntered = false;
839 TRY_CRITICAL_BLOCK(cs_mapWallet)
842 uint256& hash = vSorted[i++].second;
843 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
844 if (mi != mapWallet.end())
845 InsertTransaction((*mi).second, true);
847 if (!fEntered || i == 100 || i % 500 == 0)
851 printf("RefreshListCtrl done\n");
853 // Update transaction total display
858 // Check for time updates
859 static int64 nLastTime;
860 if (GetTime() > nLastTime + 30)
862 TRY_CRITICAL_BLOCK(cs_mapWallet)
864 nLastTime = GetTime();
865 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
867 CWalletTx& wtx = (*it).second;
868 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
869 InsertTransaction(wtx, false);
876 void CMainFrame::RefreshStatusColumn()
879 static CBlockIndex* pindexLastBest;
880 static unsigned int nLastRefreshed;
882 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
883 if (nTop == nLastTop && pindexLastBest == pindexBest)
886 TRY_CRITICAL_BLOCK(cs_mapWallet)
889 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
891 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
893 // If no updates, only need to do the part that moved onto the screen
894 if (nStart >= nLastTop && nStart < nLastTop + 100)
895 nStart = nLastTop + 100;
896 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
900 pindexLastBest = pindexBest;
901 nLastRefreshed = nListViewUpdated;
903 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
905 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
906 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
907 if (mi == mapWallet.end())
909 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
912 CWalletTx& wtx = (*mi).second;
913 if (wtx.IsCoinBase() ||
914 wtx.GetTxTime() != wtx.nTimeDisplayed ||
915 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
917 if (!InsertTransaction(wtx, false, nIndex))
918 m_listCtrl->DeleteItem(nIndex--);
922 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
928 void CMainFrame::OnPaint(wxPaintEvent& event)
939 unsigned int nNeedRepaint = 0;
940 unsigned int nLastRepaint = 0;
941 int64 nLastRepaintTime = 0;
942 int64 nRepaintInterval = 500;
944 void ThreadDelayedRepaint(void* parg)
948 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
950 nLastRepaint = nNeedRepaint;
953 printf("DelayedRepaint\n");
955 pframeMain->fRefresh = true;
956 pframeMain->GetEventHandler()->AddPendingEvent(event);
959 Sleep(nRepaintInterval);
963 void MainFrameRepaint()
965 // This is called by network code that shouldn't access pframeMain
966 // directly because it could still be running after the UI is closed.
969 // Don't repaint too often
970 static int64 nLastRepaintRequest;
971 if (GetTimeMillis() - nLastRepaintRequest < 100)
976 nLastRepaintRequest = GetTimeMillis();
978 printf("MainFrameRepaint\n");
980 pframeMain->fRefresh = true;
981 pframeMain->GetEventHandler()->AddPendingEvent(event);
985 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
987 // Skip lets the listctrl do the paint, we're just hooking the message
991 ptaskbaricon->UpdateTooltip();
996 static int nTransactionCount;
997 bool fPaintedBalance = false;
998 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1000 nLastRepaint = nNeedRepaint;
1001 nLastRepaintTime = GetTimeMillis();
1003 // Update listctrl contents
1004 if (!vWalletUpdated.empty())
1006 TRY_CRITICAL_BLOCK(cs_mapWallet)
1009 if (m_listCtrl->GetItemCount())
1010 strTop = (string)m_listCtrl->GetItemText(0);
1011 BOOST_FOREACH(uint256 hash, vWalletUpdated)
1013 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1014 if (mi != mapWallet.end())
1015 InsertTransaction((*mi).second, false);
1017 vWalletUpdated.clear();
1018 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1019 m_listCtrl->ScrollList(0, INT_MIN/2);
1024 TRY_CRITICAL_BLOCK(cs_mapWallet)
1026 fPaintedBalance = true;
1027 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
1029 // Count hidden and multi-line transactions
1030 nTransactionCount = 0;
1031 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1033 CWalletTx& wtx = (*it).second;
1034 nTransactionCount += wtx.nLinesDisplayed;
1038 if (!vWalletUpdated.empty() || !fPaintedBalance)
1041 // Update status column of visible items only
1042 RefreshStatusColumn();
1044 // Update status bar
1045 static string strPrevWarning;
1046 string strWarning = GetWarnings("statusbar");
1047 if (strWarning != "")
1048 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1049 else if (strPrevWarning != "")
1050 m_statusBar->SetStatusText("", 0);
1051 strPrevWarning = strWarning;
1054 if (fGenerateBitcoins)
1055 strGen = _(" Generating");
1056 if (fGenerateBitcoins && vNodes.empty())
1057 strGen = _("(not connected)");
1058 m_statusBar->SetStatusText(strGen, 1);
1060 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1061 m_statusBar->SetStatusText(strStatus, 2);
1063 // Update receiving address
1064 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1065 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1066 m_textCtrlAddress->SetValue(strDefaultAddress);
1070 void UIThreadCall(boost::function0<void> fn)
1072 // Call this with a function object created with bind.
1073 // bind needs all parameters to match the function's expected types
1074 // and all default parameters specified. Some examples:
1075 // UIThreadCall(bind(wxBell));
1076 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1077 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1080 wxCommandEvent event(wxEVT_UITHREADCALL);
1081 event.SetClientData((void*)new boost::function0<void>(fn));
1082 pframeMain->GetEventHandler()->AddPendingEvent(event);
1086 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1088 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1093 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1099 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1101 event.Check(fGenerateBitcoins);
1104 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1106 // Options->Your Receiving Addresses
1107 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1108 if (!dialog.ShowModal())
1112 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1115 COptionsDialog dialog(this);
1119 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1122 CAboutDialog dialog(this);
1126 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1129 CSendDialog dialog(this);
1133 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1135 // Toolbar: Address Book
1136 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1137 if (dialogAddr.ShowModal() == 2)
1140 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1141 dialogSend.ShowModal();
1145 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1147 // Automatically select-all when entering window
1149 m_textCtrlAddress->SetSelection(-1, -1);
1150 fOnSetFocusAddress = true;
1153 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1156 if (fOnSetFocusAddress)
1157 m_textCtrlAddress->SetSelection(-1, -1);
1158 fOnSetFocusAddress = false;
1161 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1164 CGetTextFromUserDialog dialog(this,
1165 _("New Receiving Address"),
1166 _("You should use a new address for each payment you receive.\n\nLabel"),
1168 if (!dialog.ShowModal())
1170 string strName = dialog.GetValue();
1173 string strAddress = PubKeyToAddress(GetKeyFromKeyPool());
1176 SetAddressBookName(strAddress, strName);
1177 SetDefaultReceivingAddress(strAddress);
1180 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1182 // Copy address box to clipboard
1183 if (wxTheClipboard->Open())
1185 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1186 wxTheClipboard->Close();
1190 void CMainFrame::OnListItemActivated(wxListEvent& event)
1192 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1194 CRITICAL_BLOCK(cs_mapWallet)
1196 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1197 if (mi == mapWallet.end())
1199 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1204 CTxDetailsDialog dialog(this, wtx);
1206 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1215 //////////////////////////////////////////////////////////////////////////////
1220 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1222 CRITICAL_BLOCK(cs_mapAddressBook)
1225 strHTML.reserve(4000);
1226 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1228 int64 nTime = wtx.GetTxTime();
1229 int64 nCredit = wtx.GetCredit();
1230 int64 nDebit = wtx.GetDebit();
1231 int64 nNet = nCredit - nDebit;
1235 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1236 int nRequests = wtx.GetRequestCount();
1237 if (nRequests != -1)
1240 strHTML += _(", has not been successfully broadcast yet");
1241 else if (nRequests == 1)
1242 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1244 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1248 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1254 if (wtx.IsCoinBase())
1256 strHTML += _("<b>Source:</b> Generated<br>");
1258 else if (!wtx.mapValue["from"].empty())
1260 // Online transaction
1261 if (!wtx.mapValue["from"].empty())
1262 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1266 // Offline transaction
1270 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1274 vector<unsigned char> vchPubKey;
1275 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1277 string strAddress = PubKeyToAddress(vchPubKey);
1278 if (mapAddressBook.count(strAddress))
1280 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1281 strHTML += _("<b>To:</b> ");
1282 strHTML += HtmlEscape(strAddress);
1283 if (!mapAddressBook[strAddress].empty())
1284 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1286 strHTML += _(" (yours)");
1301 if (!wtx.mapValue["to"].empty())
1303 // Online transaction
1304 strAddress = wtx.mapValue["to"];
1305 strHTML += _("<b>To:</b> ");
1306 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1307 strHTML += mapAddressBook[strAddress] + " ";
1308 strHTML += HtmlEscape(strAddress) + "<br>";
1315 if (wtx.IsCoinBase() && nCredit == 0)
1320 int64 nUnmatured = 0;
1321 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1322 nUnmatured += txout.GetCredit();
1323 strHTML += _("<b>Credit:</b> ");
1324 if (wtx.IsInMainChain())
1325 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1327 strHTML += _("(not accepted)");
1335 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1339 bool fAllFromMe = true;
1340 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1341 fAllFromMe = fAllFromMe && txin.IsMine();
1343 bool fAllToMe = true;
1344 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1345 fAllToMe = fAllToMe && txout.IsMine();
1352 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1357 if (wtx.mapValue["to"].empty())
1359 // Offline transaction
1361 if (ExtractHash160(txout.scriptPubKey, hash160))
1363 string strAddress = Hash160ToAddress(hash160);
1364 strHTML += _("<b>To:</b> ");
1365 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1366 strHTML += mapAddressBook[strAddress] + " ";
1367 strHTML += strAddress;
1372 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1378 int64 nChange = wtx.GetChange();
1379 int64 nValue = nCredit - nChange;
1380 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1381 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1384 int64 nTxFee = nDebit - wtx.GetValueOut();
1386 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1391 // Mixed debit transaction
1393 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1395 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1396 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1398 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1402 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1408 if (!wtx.mapValue["message"].empty())
1409 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1410 if (!wtx.mapValue["comment"].empty())
1411 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1413 if (wtx.IsCoinBase())
1414 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>";
1422 strHTML += "<hr><br>debug print<br><br>";
1423 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1425 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1426 BOOST_FOREACH(const CTxOut& txout, wtx.vout)
1428 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1430 strHTML += "<br><b>Transaction:</b><br>";
1431 strHTML += HtmlEscape(wtx.ToString(), true);
1433 strHTML += "<br><b>Inputs:</b><br>";
1434 CRITICAL_BLOCK(cs_mapWallet)
1436 BOOST_FOREACH(const CTxIn& txin, wtx.vin)
1438 COutPoint prevout = txin.prevout;
1439 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1440 if (mi != mapWallet.end())
1442 const CWalletTx& prev = (*mi).second;
1443 if (prevout.n < prev.vout.size())
1445 strHTML += HtmlEscape(prev.ToString(), true);
1446 strHTML += " " + FormatTxStatus(prev) + ", ";
1447 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1456 strHTML += "</font></html>";
1457 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1458 m_htmlWin->SetPage(strHTML);
1459 m_buttonOK->SetFocus();
1463 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1473 //////////////////////////////////////////////////////////////////////////////
1479 string StartupShortcutPath()
1481 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1484 bool GetStartOnSystemStartup()
1486 return filesystem::exists(StartupShortcutPath().c_str());
1489 void SetStartOnSystemStartup(bool fAutoStart)
1491 // If the shortcut exists already, remove it for updating
1492 remove(StartupShortcutPath().c_str());
1498 // Get a pointer to the IShellLink interface.
1499 IShellLink* psl = NULL;
1500 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1501 CLSCTX_INPROC_SERVER, IID_IShellLink,
1502 reinterpret_cast<void**>(&psl));
1504 if (SUCCEEDED(hres))
1506 // Get the current executable path
1507 TCHAR pszExePath[MAX_PATH];
1508 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1510 // Set the path to the shortcut target
1511 psl->SetPath(pszExePath);
1512 PathRemoveFileSpec(pszExePath);
1513 psl->SetWorkingDirectory(pszExePath);
1514 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1516 // Query IShellLink for the IPersistFile interface for
1517 // saving the shortcut in persistent storage.
1518 IPersistFile* ppf = NULL;
1519 hres = psl->QueryInterface(IID_IPersistFile,
1520 reinterpret_cast<void**>(&ppf));
1521 if (SUCCEEDED(hres))
1523 WCHAR pwsz[MAX_PATH];
1524 // Ensure that the string is ANSI.
1525 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1526 // Save the link by calling IPersistFile::Save.
1527 hres = ppf->Save(pwsz, TRUE);
1536 #elif defined(__WXGTK__)
1538 // Follow the Desktop Application Autostart Spec:
1539 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1541 boost::filesystem::path GetAutostartDir()
1543 namespace fs = boost::filesystem;
1545 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1546 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1547 char* pszHome = getenv("HOME");
1548 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1552 boost::filesystem::path GetAutostartFilePath()
1554 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1557 bool GetStartOnSystemStartup()
1559 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1560 if (!optionFile.good())
1562 // Scan through file for "Hidden=true":
1564 while (!optionFile.eof())
1566 getline(optionFile, line);
1567 if (line.find("Hidden") != string::npos &&
1568 line.find("true") != string::npos)
1576 void SetStartOnSystemStartup(bool fAutoStart)
1580 unlink(GetAutostartFilePath().native_file_string().c_str());
1584 char pszExePath[MAX_PATH+1];
1585 memset(pszExePath, 0, sizeof(pszExePath));
1586 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1589 boost::filesystem::create_directories(GetAutostartDir());
1591 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1592 if (!optionFile.good())
1594 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1597 // Write a bitcoin.desktop file to the autostart directory:
1598 optionFile << "[Desktop Entry]\n";
1599 optionFile << "Type=Application\n";
1600 optionFile << "Name=Bitcoin\n";
1601 optionFile << "Exec=" << pszExePath << "\n";
1602 optionFile << "Terminal=false\n";
1603 optionFile << "Hidden=false\n";
1609 // TODO: OSX startup stuff; see:
1610 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1612 bool GetStartOnSystemStartup() { return false; }
1613 void SetStartOnSystemStartup(bool fAutoStart) { }
1622 //////////////////////////////////////////////////////////////////////////////
1627 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1629 // Set up list box of page choices
1630 m_listBox->Append(_("Main"));
1631 //m_listBox->Append(_("Test 2"));
1632 m_listBox->SetSelection(0);
1635 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1637 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1638 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1639 if (!GetBoolArg("-minimizetotray"))
1641 // Minimize to tray is just too buggy on Linux
1642 fMinimizeToTray = false;
1643 m_checkBoxMinimizeToTray->SetValue(false);
1644 m_checkBoxMinimizeToTray->Enable(false);
1645 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1648 #ifdef __WXMAC_OSX__
1649 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1653 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1654 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1655 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1656 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1658 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1660 m_checkBoxUseUPnP->Enable(false);
1661 m_checkBoxUseProxy->SetValue(fUseProxy);
1662 m_textCtrlProxyIP->Enable(fUseProxy);
1663 m_textCtrlProxyPort->Enable(fUseProxy);
1664 m_staticTextProxyIP->Enable(fUseProxy);
1665 m_staticTextProxyPort->Enable(fUseProxy);
1666 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1667 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1669 m_buttonOK->SetFocus();
1672 void COptionsDialog::SelectPage(int nPage)
1674 m_panelMain->Show(nPage == 0);
1675 m_panelTest2->Show(nPage == 1);
1677 m_scrolledWindow->Layout();
1678 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1681 void COptionsDialog::OnListBox(wxCommandEvent& event)
1683 SelectPage(event.GetSelection());
1686 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1689 int64 nTmp = nTransactionFee;
1690 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1691 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1694 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1696 m_textCtrlProxyIP->Enable(event.IsChecked());
1697 m_textCtrlProxyPort->Enable(event.IsChecked());
1698 m_staticTextProxyIP->Enable(event.IsChecked());
1699 m_staticTextProxyPort->Enable(event.IsChecked());
1702 CAddress COptionsDialog::GetProxyAddr()
1704 // Be careful about byte order, addr.ip and addr.port are big endian
1705 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1706 if (addr.ip == INADDR_NONE)
1707 addr.ip = addrProxy.ip;
1708 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1709 addr.port = htons(nPort);
1710 if (nPort <= 0 || nPort > USHRT_MAX)
1711 addr.port = addrProxy.port;
1715 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1718 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1719 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1723 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1725 OnButtonApply(event);
1729 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1734 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1738 int64 nPrevTransactionFee = nTransactionFee;
1739 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1740 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1742 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1744 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1745 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1748 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1750 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1751 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1752 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1755 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1757 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1758 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1761 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1763 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1764 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1768 fUseProxy = m_checkBoxUseProxy->GetValue();
1769 walletdb.WriteSetting("fUseProxy", fUseProxy);
1771 addrProxy = GetProxyAddr();
1772 walletdb.WriteSetting("addrProxy", addrProxy);
1780 //////////////////////////////////////////////////////////////////////////////
1785 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1787 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
1789 // Change (c) into UTF-8 or ANSI copyright symbol
1790 wxString str = m_staticTextMain->GetLabel();
1792 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1794 str.Replace("(c)", "\xA9");
1796 m_staticTextMain->SetLabel(str);
1798 // Resize on Linux to make the window fit the text.
1799 // The text was wrapped manually rather than using the Wrap setting because
1800 // the wrap would be too small on Linux and it can't be changed at this point.
1801 wxFont fontTmp = m_staticTextMain->GetFont();
1802 if (fontTmp.GetPointSize() > 8);
1803 fontTmp.SetPointSize(8);
1804 m_staticTextMain->SetFont(fontTmp);
1805 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1809 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1819 //////////////////////////////////////////////////////////////////////////////
1824 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1827 m_textCtrlAddress->SetValue(strAddress);
1828 m_choiceTransferType->SetSelection(0);
1829 m_bitmapCheckMark->Show(false);
1830 fEnabledPrev = true;
1831 m_textCtrlAddress->SetFocus();
1833 //// todo: should add a display of your balance for convenience
1835 wxFont fontTmp = m_staticTextInstructions->GetFont();
1836 if (fontTmp.GetPointSize() > 9);
1837 fontTmp.SetPointSize(9);
1838 m_staticTextInstructions->SetFont(fontTmp);
1844 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1847 // Fixup the tab order
1848 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1849 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1853 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1855 // Reformat the amount
1857 if (m_textCtrlAmount->GetValue().Trim().empty())
1860 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1861 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1864 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1866 // Open address book
1867 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1868 if (dialog.ShowModal())
1869 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1872 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1874 // Copy clipboard to address box
1875 if (wxTheClipboard->Open())
1877 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1879 wxTextDataObject data;
1880 wxTheClipboard->GetData(data);
1881 m_textCtrlAddress->SetValue(data.GetText());
1883 wxTheClipboard->Close();
1887 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1889 static CCriticalSection cs_sendlock;
1890 TRY_CRITICAL_BLOCK(cs_sendlock)
1893 string strAddress = (string)m_textCtrlAddress->GetValue();
1897 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1899 wxMessageBox(_("Error in amount "), _("Send Coins"));
1902 if (nValue > GetBalance())
1904 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1907 if (nValue + nTransactionFee > GetBalance())
1909 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1913 // Parse bitcoin address
1915 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1917 if (fBitcoinAddress)
1919 CRITICAL_BLOCK(cs_main)
1921 // Send to bitcoin address
1922 CScript scriptPubKey;
1923 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1925 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1927 wxMessageBox(_("Payment sent "), _("Sending..."));
1928 else if (strError == "ABORTED")
1929 return; // leave send dialog open
1932 wxMessageBox(strError + " ", _("Sending..."));
1941 CAddress addr(strAddress);
1942 if (!addr.IsValid())
1944 wxMessageBox(_("Invalid address "), _("Send Coins"));
1949 wtx.mapValue["to"] = strAddress;
1951 // Send to IP address
1952 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1953 if (!pdialog->ShowModal())
1957 CRITICAL_BLOCK(cs_mapAddressBook)
1958 if (!mapAddressBook.count(strAddress))
1959 SetAddressBookName(strAddress, "");
1965 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1976 //////////////////////////////////////////////////////////////////////////////
1981 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
1986 start = wxDateTime::UNow();
1987 memset(pszStatus, 0, sizeof(pszStatus));
1994 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
1997 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
1998 m_textCtrlStatus->SetValue("");
2000 CreateThread(SendingDialogStartTransfer, this);
2003 CSendingDialog::~CSendingDialog()
2005 printf("~CSendingDialog()\n");
2008 void CSendingDialog::Close()
2010 // Last one out turn out the lights.
2011 // fWorkDone signals that work side is done and UI thread should call destroy.
2012 // fUIDone signals that UI window has closed and work thread should call destroy.
2013 // This allows the window to disappear and end modality when cancelled
2014 // without making the user wait for ConnectNode to return. The dialog object
2015 // hangs around in the background until the work thread exits.
2026 void CSendingDialog::OnClose(wxCloseEvent& event)
2028 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2035 wxCommandEvent cmdevent;
2036 OnButtonCancel(cmdevent);
2040 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2046 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2052 void CSendingDialog::OnPaint(wxPaintEvent& event)
2055 if (strlen(pszStatus) > 130)
2056 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2058 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2059 m_staticTextSending->SetFocus();
2061 m_buttonCancel->Enable(false);
2064 m_buttonOK->Enable(true);
2065 m_buttonOK->SetFocus();
2066 m_buttonCancel->Enable(false);
2068 if (fAbort && fCanCancel && IsShown())
2070 strcpy(pszStatus, _("CANCELLED"));
2071 m_buttonOK->Enable(true);
2072 m_buttonOK->SetFocus();
2073 m_buttonCancel->Enable(false);
2074 m_buttonCancel->SetLabel(_("Cancelled"));
2076 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2082 // Everything from here on is not in the UI thread and must only communicate
2083 // with the rest of the dialog through variables and calling repaint.
2086 void CSendingDialog::Repaint()
2090 GetEventHandler()->AddPendingEvent(event);
2093 bool CSendingDialog::Status()
2100 if (fAbort && fCanCancel)
2102 memset(pszStatus, 0, 10);
2103 strcpy(pszStatus, _("CANCELLED"));
2111 bool CSendingDialog::Status(const string& str)
2116 // This can be read by the UI thread at any time,
2117 // so copy in a way that can be read cleanly at all times.
2118 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2119 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2125 bool CSendingDialog::Error(const string& str)
2129 Status(string(_("Error: ")) + str);
2133 void SendingDialogStartTransfer(void* parg)
2135 ((CSendingDialog*)parg)->StartTransfer();
2138 void CSendingDialog::StartTransfer()
2140 // Make sure we have enough money
2141 if (nPrice + nTransactionFee > GetBalance())
2143 Error(_("Insufficient funds"));
2147 // We may have connected already for product details
2148 if (!Status(_("Connecting...")))
2150 CNode* pnode = ConnectNode(addr, 15 * 60);
2153 Error(_("Unable to connect"));
2157 // Send order to seller, with response going to OnReply2 via event handler
2158 if (!Status(_("Requesting public key...")))
2160 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2163 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2165 ((CSendingDialog*)parg)->OnReply2(vRecv);
2168 void CSendingDialog::OnReply2(CDataStream& vRecv)
2170 if (!Status(_("Received public key...")))
2173 CScript scriptPubKey;
2182 vRecv >> strMessage;
2184 Error(_("Recipient is not accepting transactions sent by IP address"));
2186 Error(_("Transfer was not accepted"));
2187 //// todo: enlarge the window and enable a hidden white box to put seller's message
2190 vRecv >> scriptPubKey;
2194 //// what do we want to do about this?
2195 Error(_("Invalid response received"));
2199 // Pause to give the user a chance to cancel
2200 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2207 CRITICAL_BLOCK(cs_main)
2210 if (!Status(_("Creating transaction...")))
2212 if (nPrice + nTransactionFee > GetBalance())
2214 Error(_("Insufficient funds"));
2217 CReserveKey reservekey;
2219 if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2221 if (nPrice + nFeeRequired > GetBalance())
2222 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()));
2224 Error(_("Transaction creation failed"));
2229 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2231 Error(_("Transaction aborted"));
2235 // Make sure we're still connected
2236 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2239 Error(_("Lost connection, transaction cancelled"));
2243 // Last chance to cancel
2255 if (!Status(_("Sending payment...")))
2259 if (!CommitTransaction(wtx, reservekey))
2261 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."));
2265 // Send payment tx to seller, with response going to OnReply3 via event handler
2266 CWalletTx wtxSend = wtx;
2267 wtxSend.fFromMe = false;
2268 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2270 Status(_("Waiting for confirmation..."));
2275 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2277 ((CSendingDialog*)parg)->OnReply3(vRecv);
2280 void CSendingDialog::OnReply3(CDataStream& vRecv)
2288 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2289 "The transaction is recorded and will credit to the recipient,\n"
2290 "but the comment information will be blank."));
2296 //// what do we want to do about this?
2297 Error(_("Payment was sent, but an invalid response was received"));
2303 Status(_("Payment completed"));
2311 //////////////////////////////////////////////////////////////////////////////
2313 // CAddressBookDialog
2316 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2318 // Set initially selected page
2319 wxNotebookEvent event;
2320 event.SetSelection(nPageIn);
2321 OnNotebookPageChanged(event);
2322 m_notebook->ChangeSelection(nPageIn);
2324 fDuringSend = fDuringSendIn;
2326 m_buttonCancel->Show(false);
2329 wxIcon iconAddressBook;
2330 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2331 SetIcon(iconAddressBook);
2333 // Init column headers
2334 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2335 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2336 m_listCtrlSending->SetFocus();
2337 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2338 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2339 m_listCtrlReceiving->SetFocus();
2341 // Fill listctrl with address book data
2342 CRITICAL_BLOCK(cs_mapKeys)
2343 CRITICAL_BLOCK(cs_mapAddressBook)
2345 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2346 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapAddressBook)
2348 string strAddress = item.first;
2349 string strName = item.second;
2351 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2352 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2353 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2354 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2355 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2360 wxString CAddressBookDialog::GetSelectedAddress()
2362 int nIndex = GetSelection(m_listCtrl);
2365 return GetItemText(m_listCtrl, nIndex, 1);
2368 wxString CAddressBookDialog::GetSelectedSendingAddress()
2370 int nIndex = GetSelection(m_listCtrlSending);
2373 return GetItemText(m_listCtrlSending, nIndex, 1);
2376 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2378 int nIndex = GetSelection(m_listCtrlReceiving);
2381 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2384 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2387 nPage = event.GetSelection();
2388 if (nPage == SENDING)
2389 m_listCtrl = m_listCtrlSending;
2390 else if (nPage == RECEIVING)
2391 m_listCtrl = m_listCtrlReceiving;
2392 m_buttonDelete->Show(nPage == SENDING);
2393 m_buttonCopy->Show(nPage == RECEIVING);
2395 m_listCtrl->SetFocus();
2398 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2400 // Update address book with edited name
2402 if (event.IsEditCancelled())
2404 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2405 SetAddressBookName(strAddress, string(event.GetText()));
2406 pframeMain->RefreshListCtrl();
2409 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2412 if (nPage == RECEIVING)
2413 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2416 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2421 // Doubleclick returns selection
2422 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2426 // Doubleclick edits item
2427 wxCommandEvent event2;
2428 OnButtonEdit(event2);
2431 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2433 if (nPage != SENDING)
2435 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2437 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2439 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2440 CWalletDB().EraseName(strAddress);
2441 m_listCtrl->DeleteItem(nIndex);
2444 pframeMain->RefreshListCtrl();
2447 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2449 // Copy address box to clipboard
2450 if (wxTheClipboard->Open())
2452 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2453 wxTheClipboard->Close();
2457 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2460 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2462 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2466 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2468 int nIndex = GetSelection(m_listCtrl);
2471 string strName = (string)m_listCtrl->GetItemText(nIndex);
2472 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2473 string strAddressOrg = strAddress;
2475 if (nPage == SENDING)
2477 // Ask name and address
2480 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2481 if (!dialog.ShowModal())
2483 strName = dialog.GetValue1();
2484 strAddress = dialog.GetValue2();
2486 while (CheckIfMine(strAddress, _("Edit Address")));
2489 else if (nPage == RECEIVING)
2492 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2493 if (!dialog.ShowModal())
2495 strName = dialog.GetValue();
2499 if (strAddress != strAddressOrg)
2500 CWalletDB().EraseName(strAddressOrg);
2501 SetAddressBookName(strAddress, strName);
2502 m_listCtrl->SetItem(nIndex, 1, strAddress);
2503 m_listCtrl->SetItemText(nIndex, strName);
2504 pframeMain->RefreshListCtrl();
2507 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2512 if (nPage == SENDING)
2514 // Ask name and address
2517 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2518 if (!dialog.ShowModal())
2520 strName = dialog.GetValue1();
2521 strAddress = dialog.GetValue2();
2523 while (CheckIfMine(strAddress, _("Add Address")));
2525 else if (nPage == RECEIVING)
2528 CGetTextFromUserDialog dialog(this,
2529 _("New Receiving Address"),
2530 _("You should use a new address for each payment you receive.\n\nLabel"),
2532 if (!dialog.ShowModal())
2534 strName = dialog.GetValue();
2537 strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2540 // Add to list and select it
2541 SetAddressBookName(strAddress, strName);
2542 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2543 SetSelection(m_listCtrl, nIndex);
2544 m_listCtrl->SetFocus();
2545 if (nPage == SENDING)
2546 pframeMain->RefreshListCtrl();
2549 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2552 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2555 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2561 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2572 //////////////////////////////////////////////////////////////////////////////
2579 ID_TASKBAR_RESTORE = 10001,
2582 ID_TASKBAR_GENERATE,
2586 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2587 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2588 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2589 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2590 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2591 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2592 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2595 void CMyTaskBarIcon::Show(bool fShow)
2597 static char pszPrevTip[200];
2600 string strTooltip = _("Bitcoin");
2601 if (fGenerateBitcoins)
2602 strTooltip = _("Bitcoin - Generating");
2603 if (fGenerateBitcoins && vNodes.empty())
2604 strTooltip = _("Bitcoin - (not connected)");
2606 // Optimization, only update when changed, using char array to be reentrant
2607 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2609 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2611 // somehow it'll choose the wrong size and scale it down if
2612 // we use the main icon, so we hand it one with only 16x16
2613 SetIcon(wxICON(favicon), strTooltip);
2615 SetIcon(bitcoin80_xpm, strTooltip);
2621 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2626 void CMyTaskBarIcon::Hide()
2631 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2636 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2641 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2644 CSendDialog dialog(pframeMain);
2648 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2650 // Since it's modal, get the main window to do it
2651 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2652 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2655 void CMyTaskBarIcon::Restore()
2658 wxIconizeEvent event(0, false);
2659 pframeMain->GetEventHandler()->AddPendingEvent(event);
2660 pframeMain->Iconize(false);
2661 pframeMain->Raise();
2664 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2666 event.Check(fGenerateBitcoins);
2669 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2671 pframeMain->Close(true);
2674 void CMyTaskBarIcon::UpdateTooltip()
2676 if (IsIconInstalled())
2680 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2682 wxMenu* pmenu = new wxMenu;
2683 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2684 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2685 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2686 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2687 pmenu->AppendSeparator();
2688 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2698 //////////////////////////////////////////////////////////////////////////////
2703 void CreateMainWindow()
2705 pframeMain = new CMainFrame(NULL);
2706 if (GetBoolArg("-min"))
2707 pframeMain->Iconize(true);
2708 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2709 if (!GetBoolArg("-minimizetotray"))
2710 fMinimizeToTray = false;
2712 pframeMain->Show(true); // have to show first to get taskbar button to hide
2713 if (fMinimizeToTray && pframeMain->IsIconized())
2714 fClosedToTray = true;
2715 pframeMain->Show(!fClosedToTray);
2716 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2717 CreateThread(ThreadDelayedRepaint, NULL);
2721 // Define a new application
2722 class CMyApp : public wxApp
2731 // Hook Initialize so we can start without GUI
2732 virtual bool Initialize(int& argc, wxChar** argv);
2734 // 2nd-level exception handling: we get all the exceptions occurring in any
2735 // event handler here
2736 virtual bool OnExceptionInMainLoop();
2738 // 3rd, and final, level exception handling: whenever an unhandled
2739 // exception is caught, this function is called
2740 virtual void OnUnhandledException();
2742 // and now for something different: this function is called in case of a
2743 // crash (e.g. dereferencing null pointer, division by 0, ...)
2744 virtual void OnFatalException();
2747 IMPLEMENT_APP(CMyApp)
2749 bool CMyApp::Initialize(int& argc, wxChar** argv)
2751 for (int i = 1; i < argc; i++)
2752 if (!IsSwitchChar(argv[i][0]))
2753 fCommandLine = true;
2757 // wxApp::Initialize will remove environment-specific parameters,
2758 // so it's too early to call ParseParameters yet
2759 for (int i = 1; i < argc; i++)
2761 wxString str = argv[i];
2763 if (str.size() >= 1 && str[0] == '/')
2765 char pszLower[MAX_PATH];
2766 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2770 if (str == "-daemon")
2776 if (fDaemon || fCommandLine)
2778 // Call the original Initialize while suppressing error messages
2779 // and ignoring failure. If unable to initialize GTK, it fails
2780 // near the end so hopefully the last few things don't matter.
2783 wxApp::Initialize(argc, argv);
2792 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2796 pthread_exit((void*)0);
2798 pid_t sid = setsid();
2800 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
2807 return wxApp::Initialize(argc, argv);
2810 bool CMyApp::OnInit()
2812 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2813 // Disable malfunctioning wxWidgets debug assertion
2814 extern int g_isPainting;
2815 g_isPainting = 10000;
2817 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2818 SetAppName("Bitcoin");
2820 SetAppName("bitcoin");
2824 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2825 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2826 class wxMBConv_win32 : public wxMBConv
2830 size_t m_minMBCharWidth;
2832 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2833 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2837 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2838 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2839 g_locale.AddCatalogLookupPathPrefix("locale");
2841 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2842 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2844 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2845 g_locale.AddCatalog("bitcoin");
2847 return AppInit(argc, argv);
2850 int CMyApp::OnExit()
2853 return wxApp::OnExit();
2856 bool CMyApp::OnExceptionInMainLoop()
2862 catch (std::exception& e)
2864 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2865 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2871 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2872 wxLogWarning("Unknown exception");
2879 void CMyApp::OnUnhandledException()
2881 // this shows how we may let some exception propagate uncaught
2886 catch (std::exception& e)
2888 PrintException(&e, "CMyApp::OnUnhandledException()");
2889 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2895 PrintException(NULL, "CMyApp::OnUnhandledException()");
2896 wxLogWarning("Unknown exception");
2902 void CMyApp::OnFatalException()
2904 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);