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.
12 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
14 CMainFrame* pframeMain = NULL;
15 CMyTaskBarIcon* ptaskbaricon = NULL;
16 bool fClosedToTray = false;
27 //////////////////////////////////////////////////////////////////////////////
32 void HandleCtrlA(wxKeyEvent& event)
36 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
37 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
38 textCtrl->SetSelection(-1, -1);
43 //char pszHourFormat[256];
44 //pszHourFormat[0] = '\0';
45 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
46 //return (pszHourFormat[0] != '0');
50 string DateStr(int64 nTime)
52 // Can only be used safely here in the UI
53 return (string)wxDateTime((time_t)nTime).FormatDate();
56 string DateTimeStr(int64 nTime)
58 // Can only be used safely here in the UI
59 wxDateTime datetime((time_t)nTime);
61 return (string)datetime.Format("%x %H:%M");
63 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
66 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
68 // Helper to simplify access to listctrl
70 item.m_itemId = nIndex;
72 item.m_mask = wxLIST_MASK_TEXT;
73 if (!listCtrl->GetItem(item))
75 return item.GetText();
78 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
80 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
81 listCtrl->SetItem(nIndex, 1, str1);
85 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
87 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
88 listCtrl->SetItem(nIndex, 1, str1);
89 listCtrl->SetItem(nIndex, 2, str2);
90 listCtrl->SetItem(nIndex, 3, str3);
91 listCtrl->SetItem(nIndex, 4, str4);
95 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
97 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
98 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
99 listCtrl->SetItem(nIndex, 1, str1);
100 listCtrl->SetItem(nIndex, 2, str2);
101 listCtrl->SetItem(nIndex, 3, str3);
102 listCtrl->SetItem(nIndex, 4, str4);
106 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
108 // Repaint on Windows is more flickery if the colour has ever been set,
109 // so don't want to set it unless it's different. Default colour has
110 // alpha 0 transparent, so our colours don't match using operator==.
111 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
113 c1 = wxColour(0,0,0);
114 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
115 listCtrl->SetItemTextColour(nIndex, colour);
118 void SetSelection(wxListCtrl* listCtrl, int nIndex)
120 int nSize = listCtrl->GetItemCount();
121 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
122 for (int i = 0; i < nSize; i++)
123 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
126 int GetSelection(wxListCtrl* listCtrl)
128 int nSize = listCtrl->GetItemCount();
129 for (int i = 0; i < nSize; i++)
130 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
135 string HtmlEscape(const char* psz, bool fMultiLine=false)
138 for (const char* p = psz; *p; p++)
140 if (*p == '<') len += 4;
141 else if (*p == '>') len += 4;
142 else if (*p == '&') len += 5;
143 else if (*p == '"') len += 6;
144 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
145 else if (*p == '\n' && fMultiLine) len += 5;
151 for (const char* p = psz; *p; p++)
153 if (*p == '<') str += "<";
154 else if (*p == '>') str += ">";
155 else if (*p == '&') str += "&";
156 else if (*p == '"') str += """;
157 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
158 else if (*p == '\n' && fMultiLine) str += "<br>\n";
165 string HtmlEscape(const string& str, bool fMultiLine=false)
167 return HtmlEscape(str.c_str(), fMultiLine);
170 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
172 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
176 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
179 return wxMessageBox(message, caption, style, parent, x, y);
181 if (wxThread::IsMain() || fDaemon)
183 return wxMessageBox(message, caption, style, parent, x, y);
189 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
197 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
199 if (nFeeRequired < CENT || nFeeRequired <= nTransactionFee || fDaemon)
201 string strMessage = strprintf(
202 _("This transaction is over the size limit. You can still send it for a fee of %s, "
203 "which goes to the nodes that process your transaction and helps to support the network. "
204 "Do you want to pay the fee?"),
205 FormatMoney(nFeeRequired).c_str());
206 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
209 void CalledSetStatusBar(const string& strText, int nField)
211 if (nField == 0 && GetWarnings("statusbar") != "")
213 if (pframeMain && pframeMain->m_statusBar)
214 pframeMain->m_statusBar->SetStatusText(strText, nField);
217 void SetDefaultReceivingAddress(const string& strAddress)
219 // Update main window address and database
220 if (pframeMain == NULL)
222 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
225 if (!AddressToHash160(strAddress, hash160))
227 if (!mapPubKeys.count(hash160))
229 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
230 pframeMain->m_textCtrlAddress->SetValue(strAddress);
243 //////////////////////////////////////////////////////////////////////////////
248 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
250 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
252 // Set initially selected page
253 wxNotebookEvent event;
254 event.SetSelection(0);
255 OnNotebookPageChanged(event);
256 m_notebook->ChangeSelection(0);
259 fRefreshListCtrl = false;
260 fRefreshListCtrlRunning = false;
261 fOnSetFocusAddress = false;
263 m_choiceFilter->SetSelection(0);
264 double dResize = 1.0;
266 SetIcon(wxICON(bitcoin));
268 SetIcon(bitcoin80_xpm);
269 SetBackgroundColour(m_toolBar->GetBackgroundColour());
270 wxFont fontTmp = m_staticText41->GetFont();
271 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
272 m_staticTextBalance->SetFont(fontTmp);
273 m_staticTextBalance->SetSize(140, 17);
274 // resize to fit ubuntu's huge default font
276 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
278 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
279 m_listCtrl->SetFocus();
280 ptaskbaricon = new CMyTaskBarIcon();
282 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
283 // to their standard places, leaving these menus empty.
284 GetMenuBar()->Remove(2); // remove Help menu
285 GetMenuBar()->Remove(0); // remove File menu
288 // Init column headers
289 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
290 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
296 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
297 foreach(wxListCtrl* p, pplistCtrl)
299 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
300 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
301 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
302 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
303 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
304 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
305 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
309 int pnWidths[3] = { -100, 88, 300 };
311 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
312 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
314 m_statusBar->SetFieldsCount(3, pnWidths);
316 // Fill your address text box
317 vector<unsigned char> vchPubKey;
318 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
319 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
321 // Fill listctrl with wallet transactions
325 CMainFrame::~CMainFrame()
332 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
335 nPage = event.GetSelection();
338 m_listCtrl = m_listCtrlAll;
339 fShowGenerated = true;
341 fShowReceived = true;
343 else if (nPage == SENTRECEIVED)
345 m_listCtrl = m_listCtrlSentReceived;
346 fShowGenerated = false;
348 fShowReceived = true;
350 else if (nPage == SENT)
352 m_listCtrl = m_listCtrlSent;
353 fShowGenerated = false;
355 fShowReceived = false;
357 else if (nPage == RECEIVED)
359 m_listCtrl = m_listCtrlReceived;
360 fShowGenerated = false;
362 fShowReceived = true;
365 m_listCtrl->SetFocus();
368 void CMainFrame::OnClose(wxCloseEvent& event)
370 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
372 // Divert close to minimize
374 fClosedToTray = true;
380 CreateThread(Shutdown, NULL);
384 void CMainFrame::OnIconize(wxIconizeEvent& event)
387 // Hide the task bar button when minimized.
388 // Event is sent when the frame is minimized or restored.
389 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
390 // to get rid of the deprecated warning. Just ignore it.
391 if (!event.Iconized())
392 fClosedToTray = false;
393 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
394 if (GetBoolArg("-minimizetotray")) {
396 // The tray icon sometimes disappears on ubuntu karmic
397 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
398 // Reports of CPU peg on 64-bit linux
399 if (fMinimizeToTray && event.Iconized())
400 fClosedToTray = true;
401 Show(!fClosedToTray);
402 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
403 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
408 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
412 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
413 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
416 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
418 // Hidden columns not resizeable
419 if (event.GetColumn() <= 1 && !fDebug)
425 int CMainFrame::GetSortIndex(const string& strSort)
430 // The wx generic listctrl implementation used on GTK doesn't sort,
431 // so we have to do it ourselves. Remember, we sort in reverse order.
432 // In the wx generic implementation, they store the list of items
433 // in a vector, so indexed lookups are fast, but inserts are slower
434 // the closer they are to the top.
436 int high = m_listCtrl->GetItemCount();
439 int mid = low + ((high - low) / 2);
440 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
449 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)
451 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
452 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
455 if (!fNew && nIndex == -1)
457 string strHash = " " + hashKey.ToString();
458 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
459 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
463 // fNew is for blind insert, only use if you're sure it's new
464 if (fNew || nIndex == -1)
466 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
470 // If sort key changed, must delete and reinsert to make it relocate
471 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
473 m_listCtrl->DeleteItem(nIndex);
474 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
478 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
479 m_listCtrl->SetItem(nIndex, 2, str2);
480 m_listCtrl->SetItem(nIndex, 3, str3);
481 m_listCtrl->SetItem(nIndex, 4, str4);
482 m_listCtrl->SetItem(nIndex, 5, str5);
483 m_listCtrl->SetItem(nIndex, 6, str6);
484 m_listCtrl->SetItemData(nIndex, nData);
485 SetItemTextColour(m_listCtrl, nIndex, colour);
488 bool CMainFrame::DeleteLine(uint256 hashKey)
490 long nData = *(long*)&hashKey;
494 string strHash = " " + hashKey.ToString();
495 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
496 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
500 m_listCtrl->DeleteItem(nIndex);
505 string FormatTxStatus(const CWalletTx& wtx)
510 if (wtx.nLockTime < 500000000)
511 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
513 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
517 int nDepth = wtx.GetDepthInMainChain();
518 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
519 return strprintf(_("%d/offline?"), nDepth);
521 return strprintf(_("%d/unconfirmed"), nDepth);
523 return strprintf(_("%d confirmations"), nDepth);
527 string SingleLine(const string& strIn)
530 bool fOneSpace = false;
531 foreach(unsigned char c, strIn)
539 if (fOneSpace && !strOut.empty())
548 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
550 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
551 int64 nCredit = wtx.GetCredit(true);
552 int64 nDebit = wtx.GetDebit();
553 int64 nNet = nCredit - nDebit;
554 uint256 hash = wtx.GetHash();
555 string strStatus = FormatTxStatus(wtx);
556 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
557 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
558 map<string, string> mapValue = wtx.mapValue;
559 wtx.nLinesDisplayed = 1;
563 if (wtx.IsCoinBase())
565 // Don't show generated coin until confirmed by at least one block after it
566 // so we don't get the user's hopes up until it looks like it's probably accepted.
568 // It is not an error when generated blocks are not accepted. By design,
569 // some percentage of blocks, like 10% or more, will end up not accepted.
570 // This is the normal mechanism by which the network copes with latency.
572 // We display regular transactions right away before any confirmation
573 // because they can always get into some block eventually. Generated coins
574 // are special because if their block is not accepted, they are not valid.
576 if (wtx.GetDepthInMainChain() < 2)
578 wtx.nLinesDisplayed = 0;
586 // Find the block the tx is in
587 CBlockIndex* pindex = NULL;
588 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
589 if (mi != mapBlockIndex.end())
590 pindex = (*mi).second;
592 // Sort order, unrecorded transactions sort to the top
593 string strSort = strprintf("%010d-%01d-%010u",
594 (pindex ? pindex->nHeight : INT_MAX),
595 (wtx.IsCoinBase() ? 1 : 0),
599 if (nNet > 0 || wtx.IsCoinBase())
604 string strDescription;
605 if (wtx.IsCoinBase())
608 strDescription = _("Generated");
611 int64 nUnmatured = 0;
612 foreach(const CTxOut& txout, wtx.vout)
613 nUnmatured += txout.GetCredit();
614 if (wtx.IsInMainChain())
616 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
618 // Check if the block was requested by anyone
619 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
620 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
624 strDescription = _("Generated (not accepted)");
628 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
630 // Received by IP connection
633 if (!mapValue["from"].empty())
634 strDescription += _("From: ") + mapValue["from"];
635 if (!mapValue["message"].empty())
637 if (!strDescription.empty())
638 strDescription += " - ";
639 strDescription += mapValue["message"];
644 // Received by Bitcoin Address
647 foreach(const CTxOut& txout, wtx.vout)
651 vector<unsigned char> vchPubKey;
652 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
654 CRITICAL_BLOCK(cs_mapAddressBook)
656 //strDescription += _("Received payment to ");
657 //strDescription += _("Received with address ");
658 strDescription += _("Received with: ");
659 string strAddress = PubKeyToAddress(vchPubKey);
660 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
661 if (mi != mapAddressBook.end() && !(*mi).second.empty())
663 string strLabel = (*mi).second;
664 strDescription += strAddress.substr(0,12) + "... ";
665 strDescription += "(" + strLabel + ")";
668 strDescription += strAddress;
676 string strCredit = FormatMoney(nNet, true);
678 strCredit = "[" + strCredit + "]";
680 InsertLine(fNew, nIndex, hash, strSort, colour,
682 nTime ? DateTimeStr(nTime) : "",
683 SingleLine(strDescription),
689 bool fAllFromMe = true;
690 foreach(const CTxIn& txin, wtx.vin)
691 fAllFromMe = fAllFromMe && txin.IsMine();
693 bool fAllToMe = true;
694 foreach(const CTxOut& txout, wtx.vout)
695 fAllToMe = fAllToMe && txout.IsMine();
697 if (fAllFromMe && fAllToMe)
700 int64 nChange = wtx.GetChange();
701 InsertLine(fNew, nIndex, hash, strSort, colour,
703 nTime ? DateTimeStr(nTime) : "",
704 _("Payment to yourself"),
705 FormatMoney(-(nDebit - nChange), true),
706 FormatMoney(nCredit - nChange, true));
716 int64 nTxFee = nDebit - wtx.GetValueOut();
717 wtx.nLinesDisplayed = 0;
718 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
720 const CTxOut& txout = wtx.vout[nOut];
725 if (!mapValue["to"].empty())
728 strAddress = mapValue["to"];
732 // Sent to Bitcoin Address
734 if (ExtractHash160(txout.scriptPubKey, hash160))
735 strAddress = Hash160ToAddress(hash160);
738 string strDescription = _("To: ");
739 CRITICAL_BLOCK(cs_mapAddressBook)
740 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
741 strDescription += mapAddressBook[strAddress] + " ";
742 strDescription += strAddress;
743 if (!mapValue["message"].empty())
745 if (!strDescription.empty())
746 strDescription += " - ";
747 strDescription += mapValue["message"];
749 else if (!mapValue["comment"].empty())
751 if (!strDescription.empty())
752 strDescription += " - ";
753 strDescription += mapValue["comment"];
756 int64 nValue = txout.nValue;
763 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
765 nTime ? DateTimeStr(nTime) : "",
766 SingleLine(strDescription),
767 FormatMoney(-nValue, true),
770 wtx.nLinesDisplayed++;
776 // Mixed debit transaction, can't break down payees
778 bool fAllMine = true;
779 foreach(const CTxOut& txout, wtx.vout)
780 fAllMine = fAllMine && txout.IsMine();
781 foreach(const CTxIn& txin, wtx.vin)
782 fAllMine = fAllMine && txin.IsMine();
784 InsertLine(fNew, nIndex, hash, strSort, colour,
786 nTime ? DateTimeStr(nTime) : "",
788 FormatMoney(nNet, true),
796 void CMainFrame::RefreshListCtrl()
798 fRefreshListCtrl = true;
802 void CMainFrame::OnIdle(wxIdleEvent& event)
804 if (fRefreshListCtrl)
806 // Collect list of wallet transactions and sort newest first
807 bool fEntered = false;
808 vector<pair<unsigned int, uint256> > vSorted;
809 TRY_CRITICAL_BLOCK(cs_mapWallet)
811 printf("RefreshListCtrl starting\n");
813 fRefreshListCtrl = false;
814 vWalletUpdated.clear();
816 // Do the newest transactions first
817 vSorted.reserve(mapWallet.size());
818 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
820 const CWalletTx& wtx = (*it).second;
821 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
822 vSorted.push_back(make_pair(nTime, (*it).first));
824 m_listCtrl->DeleteAllItems();
829 sort(vSorted.begin(), vSorted.end());
832 for (int i = 0; i < vSorted.size();)
836 bool fEntered = false;
837 TRY_CRITICAL_BLOCK(cs_mapWallet)
840 uint256& hash = vSorted[i++].second;
841 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
842 if (mi != mapWallet.end())
843 InsertTransaction((*mi).second, true);
845 if (!fEntered || i == 100 || i % 500 == 0)
849 printf("RefreshListCtrl done\n");
851 // Update transaction total display
856 // Check for time updates
857 static int64 nLastTime;
858 if (GetTime() > nLastTime + 30)
860 TRY_CRITICAL_BLOCK(cs_mapWallet)
862 nLastTime = GetTime();
863 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
865 CWalletTx& wtx = (*it).second;
866 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
867 InsertTransaction(wtx, false);
874 void CMainFrame::RefreshStatusColumn()
877 static CBlockIndex* pindexLastBest;
878 static unsigned int nLastRefreshed;
880 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
881 if (nTop == nLastTop && pindexLastBest == pindexBest)
884 TRY_CRITICAL_BLOCK(cs_mapWallet)
887 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
889 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
891 // If no updates, only need to do the part that moved onto the screen
892 if (nStart >= nLastTop && nStart < nLastTop + 100)
893 nStart = nLastTop + 100;
894 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
898 pindexLastBest = pindexBest;
899 nLastRefreshed = nListViewUpdated;
901 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
903 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
904 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
905 if (mi == mapWallet.end())
907 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
910 CWalletTx& wtx = (*mi).second;
911 if (wtx.IsCoinBase() ||
912 wtx.GetTxTime() != wtx.nTimeDisplayed ||
913 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
915 if (!InsertTransaction(wtx, false, nIndex))
916 m_listCtrl->DeleteItem(nIndex--);
920 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
926 void CMainFrame::OnPaint(wxPaintEvent& event)
937 unsigned int nNeedRepaint = 0;
938 unsigned int nLastRepaint = 0;
939 int64 nLastRepaintTime = 0;
940 int64 nRepaintInterval = 500;
942 void ThreadDelayedRepaint(void* parg)
946 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
948 nLastRepaint = nNeedRepaint;
951 printf("DelayedRepaint\n");
953 pframeMain->fRefresh = true;
954 pframeMain->GetEventHandler()->AddPendingEvent(event);
957 Sleep(nRepaintInterval);
961 void MainFrameRepaint()
963 // This is called by network code that shouldn't access pframeMain
964 // directly because it could still be running after the UI is closed.
967 // Don't repaint too often
968 static int64 nLastRepaintRequest;
969 if (GetTimeMillis() - nLastRepaintRequest < 100)
974 nLastRepaintRequest = GetTimeMillis();
976 printf("MainFrameRepaint\n");
978 pframeMain->fRefresh = true;
979 pframeMain->GetEventHandler()->AddPendingEvent(event);
983 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
985 // Skip lets the listctrl do the paint, we're just hooking the message
989 ptaskbaricon->UpdateTooltip();
994 static int nTransactionCount;
995 bool fPaintedBalance = false;
996 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
998 nLastRepaint = nNeedRepaint;
999 nLastRepaintTime = GetTimeMillis();
1001 // Update listctrl contents
1002 if (!vWalletUpdated.empty())
1004 TRY_CRITICAL_BLOCK(cs_mapWallet)
1007 if (m_listCtrl->GetItemCount())
1008 strTop = (string)m_listCtrl->GetItemText(0);
1009 foreach(uint256 hash, vWalletUpdated)
1011 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1012 if (mi != mapWallet.end())
1013 InsertTransaction((*mi).second, false);
1015 vWalletUpdated.clear();
1016 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1017 m_listCtrl->ScrollList(0, INT_MIN/2);
1022 TRY_CRITICAL_BLOCK(cs_mapWallet)
1024 fPaintedBalance = true;
1025 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
1027 // Count hidden and multi-line transactions
1028 nTransactionCount = 0;
1029 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1031 CWalletTx& wtx = (*it).second;
1032 nTransactionCount += wtx.nLinesDisplayed;
1036 if (!vWalletUpdated.empty() || !fPaintedBalance)
1039 // Update status column of visible items only
1040 RefreshStatusColumn();
1042 // Update status bar
1043 static string strPrevWarning;
1044 string strWarning = GetWarnings("statusbar");
1045 if (strWarning != "")
1046 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1047 else if (strPrevWarning != "")
1048 m_statusBar->SetStatusText("", 0);
1049 strPrevWarning = strWarning;
1052 if (fGenerateBitcoins)
1053 strGen = _(" Generating");
1054 if (fGenerateBitcoins && vNodes.empty())
1055 strGen = _("(not connected)");
1056 m_statusBar->SetStatusText(strGen, 1);
1058 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1059 m_statusBar->SetStatusText(strStatus, 2);
1061 // Update receiving address
1062 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1063 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1064 m_textCtrlAddress->SetValue(strDefaultAddress);
1068 void UIThreadCall(boost::function0<void> fn)
1070 // Call this with a function object created with bind.
1071 // bind needs all parameters to match the function's expected types
1072 // and all default parameters specified. Some examples:
1073 // UIThreadCall(bind(wxBell));
1074 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1075 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1078 wxCommandEvent event(wxEVT_UITHREADCALL);
1079 event.SetClientData((void*)new boost::function0<void>(fn));
1080 pframeMain->GetEventHandler()->AddPendingEvent(event);
1084 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1086 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1091 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1097 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1099 event.Check(fGenerateBitcoins);
1102 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1104 // Options->Your Receiving Addresses
1105 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1106 if (!dialog.ShowModal())
1110 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1113 COptionsDialog dialog(this);
1117 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1120 CAboutDialog dialog(this);
1124 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1127 CSendDialog dialog(this);
1131 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1133 // Toolbar: Address Book
1134 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1135 if (dialogAddr.ShowModal() == 2)
1138 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1139 dialogSend.ShowModal();
1143 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1145 // Automatically select-all when entering window
1147 m_textCtrlAddress->SetSelection(-1, -1);
1148 fOnSetFocusAddress = true;
1151 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1154 if (fOnSetFocusAddress)
1155 m_textCtrlAddress->SetSelection(-1, -1);
1156 fOnSetFocusAddress = false;
1159 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1162 CGetTextFromUserDialog dialog(this,
1163 _("New Receiving Address"),
1164 _("You should use a new address for each payment you receive.\n\nLabel"),
1166 if (!dialog.ShowModal())
1168 string strName = dialog.GetValue();
1171 string strAddress = PubKeyToAddress(GetKeyFromKeyPool());
1174 SetAddressBookName(strAddress, strName);
1175 SetDefaultReceivingAddress(strAddress);
1178 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1180 // Copy address box to clipboard
1181 if (wxTheClipboard->Open())
1183 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1184 wxTheClipboard->Close();
1188 void CMainFrame::OnListItemActivated(wxListEvent& event)
1190 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1192 CRITICAL_BLOCK(cs_mapWallet)
1194 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1195 if (mi == mapWallet.end())
1197 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1202 CTxDetailsDialog dialog(this, wtx);
1204 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1213 //////////////////////////////////////////////////////////////////////////////
1218 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1220 CRITICAL_BLOCK(cs_mapAddressBook)
1223 strHTML.reserve(4000);
1224 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1226 int64 nTime = wtx.GetTxTime();
1227 int64 nCredit = wtx.GetCredit();
1228 int64 nDebit = wtx.GetDebit();
1229 int64 nNet = nCredit - nDebit;
1233 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1234 int nRequests = wtx.GetRequestCount();
1235 if (nRequests != -1)
1238 strHTML += _(", has not been successfully broadcast yet");
1239 else if (nRequests == 1)
1240 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1242 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1246 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1252 if (wtx.IsCoinBase())
1254 strHTML += _("<b>Source:</b> Generated<br>");
1256 else if (!wtx.mapValue["from"].empty())
1258 // Online transaction
1259 if (!wtx.mapValue["from"].empty())
1260 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1264 // Offline transaction
1268 foreach(const CTxOut& txout, wtx.vout)
1272 vector<unsigned char> vchPubKey;
1273 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1275 string strAddress = PubKeyToAddress(vchPubKey);
1276 if (mapAddressBook.count(strAddress))
1278 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1279 strHTML += _("<b>To:</b> ");
1280 strHTML += HtmlEscape(strAddress);
1281 if (!mapAddressBook[strAddress].empty())
1282 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1284 strHTML += _(" (yours)");
1299 if (!wtx.mapValue["to"].empty())
1301 // Online transaction
1302 strAddress = wtx.mapValue["to"];
1303 strHTML += _("<b>To:</b> ");
1304 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1305 strHTML += mapAddressBook[strAddress] + " ";
1306 strHTML += HtmlEscape(strAddress) + "<br>";
1313 if (wtx.IsCoinBase() && nCredit == 0)
1318 int64 nUnmatured = 0;
1319 foreach(const CTxOut& txout, wtx.vout)
1320 nUnmatured += txout.GetCredit();
1321 strHTML += _("<b>Credit:</b> ");
1322 if (wtx.IsInMainChain())
1323 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1325 strHTML += _("(not accepted)");
1333 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1337 bool fAllFromMe = true;
1338 foreach(const CTxIn& txin, wtx.vin)
1339 fAllFromMe = fAllFromMe && txin.IsMine();
1341 bool fAllToMe = true;
1342 foreach(const CTxOut& txout, wtx.vout)
1343 fAllToMe = fAllToMe && txout.IsMine();
1350 foreach(const CTxOut& txout, wtx.vout)
1355 if (wtx.mapValue["to"].empty())
1357 // Offline transaction
1359 if (ExtractHash160(txout.scriptPubKey, hash160))
1361 string strAddress = Hash160ToAddress(hash160);
1362 strHTML += _("<b>To:</b> ");
1363 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1364 strHTML += mapAddressBook[strAddress] + " ";
1365 strHTML += strAddress;
1370 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1376 int64 nChange = wtx.GetChange();
1377 int64 nValue = nCredit - nChange;
1378 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1379 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1382 int64 nTxFee = nDebit - wtx.GetValueOut();
1384 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1389 // Mixed debit transaction
1391 foreach(const CTxIn& txin, wtx.vin)
1393 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1394 foreach(const CTxOut& txout, wtx.vout)
1396 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1400 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1406 if (!wtx.mapValue["message"].empty())
1407 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1408 if (!wtx.mapValue["comment"].empty())
1409 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1411 if (wtx.IsCoinBase())
1412 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>";
1420 strHTML += "<hr><br>debug print<br><br>";
1421 foreach(const CTxIn& txin, wtx.vin)
1423 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1424 foreach(const CTxOut& txout, wtx.vout)
1426 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1428 strHTML += "<br><b>Transaction:</b><br>";
1429 strHTML += HtmlEscape(wtx.ToString(), true);
1431 strHTML += "<br><b>Inputs:</b><br>";
1432 CRITICAL_BLOCK(cs_mapWallet)
1434 foreach(const CTxIn& txin, wtx.vin)
1436 COutPoint prevout = txin.prevout;
1437 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1438 if (mi != mapWallet.end())
1440 const CWalletTx& prev = (*mi).second;
1441 if (prevout.n < prev.vout.size())
1443 strHTML += HtmlEscape(prev.ToString(), true);
1444 strHTML += " " + FormatTxStatus(prev) + ", ";
1445 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1454 strHTML += "</font></html>";
1455 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1456 m_htmlWin->SetPage(strHTML);
1457 m_buttonOK->SetFocus();
1461 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1471 //////////////////////////////////////////////////////////////////////////////
1477 string StartupShortcutPath()
1479 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1482 bool GetStartOnSystemStartup()
1484 return filesystem::exists(StartupShortcutPath().c_str());
1487 void SetStartOnSystemStartup(bool fAutoStart)
1489 // If the shortcut exists already, remove it for updating
1490 remove(StartupShortcutPath().c_str());
1496 // Get a pointer to the IShellLink interface.
1497 IShellLink* psl = NULL;
1498 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1499 CLSCTX_INPROC_SERVER, IID_IShellLink,
1500 reinterpret_cast<void**>(&psl));
1502 if (SUCCEEDED(hres))
1504 // Get the current executable path
1505 TCHAR pszExePath[MAX_PATH];
1506 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1508 // Set the path to the shortcut target
1509 psl->SetPath(pszExePath);
1510 PathRemoveFileSpec(pszExePath);
1511 psl->SetWorkingDirectory(pszExePath);
1512 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1514 // Query IShellLink for the IPersistFile interface for
1515 // saving the shortcut in persistent storage.
1516 IPersistFile* ppf = NULL;
1517 hres = psl->QueryInterface(IID_IPersistFile,
1518 reinterpret_cast<void**>(&ppf));
1519 if (SUCCEEDED(hres))
1521 WCHAR pwsz[MAX_PATH];
1522 // Ensure that the string is ANSI.
1523 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1524 // Save the link by calling IPersistFile::Save.
1525 hres = ppf->Save(pwsz, TRUE);
1534 #elif defined(__WXGTK__)
1536 // Follow the Desktop Application Autostart Spec:
1537 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1539 boost::filesystem::path GetAutostartDir()
1541 namespace fs = boost::filesystem;
1543 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1544 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1545 char* pszHome = getenv("HOME");
1546 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1550 boost::filesystem::path GetAutostartFilePath()
1552 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1555 bool GetStartOnSystemStartup()
1557 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1558 if (!optionFile.good())
1560 // Scan through file for "Hidden=true":
1562 while (!optionFile.eof())
1564 getline(optionFile, line);
1565 if (line.find("Hidden") != string::npos &&
1566 line.find("true") != string::npos)
1574 void SetStartOnSystemStartup(bool fAutoStart)
1578 unlink(GetAutostartFilePath().native_file_string().c_str());
1582 char pszExePath[MAX_PATH+1];
1583 memset(pszExePath, 0, sizeof(pszExePath));
1584 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1587 boost::filesystem::create_directories(GetAutostartDir());
1589 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1590 if (!optionFile.good())
1592 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1595 // Write a bitcoin.desktop file to the autostart directory:
1596 optionFile << "[Desktop Entry]\n";
1597 optionFile << "Type=Application\n";
1598 optionFile << "Name=Bitcoin\n";
1599 optionFile << "Exec=" << pszExePath << "\n";
1600 optionFile << "Terminal=false\n";
1601 optionFile << "Hidden=false\n";
1607 // TODO: OSX startup stuff; see:
1608 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1610 bool GetStartOnSystemStartup() { return false; }
1611 void SetStartOnSystemStartup(bool fAutoStart) { }
1620 //////////////////////////////////////////////////////////////////////////////
1625 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1627 // Set up list box of page choices
1628 m_listBox->Append(_("Main"));
1629 //m_listBox->Append(_("Test 2"));
1630 m_listBox->SetSelection(0);
1633 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1635 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1636 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1637 if (!GetBoolArg("-minimizetotray"))
1639 // Minimize to tray is just too buggy on Linux
1640 fMinimizeToTray = false;
1641 m_checkBoxMinimizeToTray->SetValue(false);
1642 m_checkBoxMinimizeToTray->Enable(false);
1643 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1646 #ifdef __WXMAC_OSX__
1647 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1651 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1652 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1653 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1654 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1656 m_checkBoxUseUPnP->SetValue(fUseUPnP);
1658 m_checkBoxUseUPnP->Enable(false);
1659 m_checkBoxUseProxy->SetValue(fUseProxy);
1660 m_textCtrlProxyIP->Enable(fUseProxy);
1661 m_textCtrlProxyPort->Enable(fUseProxy);
1662 m_staticTextProxyIP->Enable(fUseProxy);
1663 m_staticTextProxyPort->Enable(fUseProxy);
1664 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1665 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1667 m_buttonOK->SetFocus();
1670 void COptionsDialog::SelectPage(int nPage)
1672 m_panelMain->Show(nPage == 0);
1673 m_panelTest2->Show(nPage == 1);
1675 m_scrolledWindow->Layout();
1676 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1679 void COptionsDialog::OnListBox(wxCommandEvent& event)
1681 SelectPage(event.GetSelection());
1684 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1687 int64 nTmp = nTransactionFee;
1688 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1689 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1692 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1694 m_textCtrlProxyIP->Enable(event.IsChecked());
1695 m_textCtrlProxyPort->Enable(event.IsChecked());
1696 m_staticTextProxyIP->Enable(event.IsChecked());
1697 m_staticTextProxyPort->Enable(event.IsChecked());
1700 CAddress COptionsDialog::GetProxyAddr()
1702 // Be careful about byte order, addr.ip and addr.port are big endian
1703 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1704 if (addr.ip == INADDR_NONE)
1705 addr.ip = addrProxy.ip;
1706 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1707 addr.port = htons(nPort);
1708 if (nPort <= 0 || nPort > USHRT_MAX)
1709 addr.port = addrProxy.port;
1713 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1716 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1717 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1721 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1723 OnButtonApply(event);
1727 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1732 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1736 int64 nPrevTransactionFee = nTransactionFee;
1737 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1738 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1740 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1742 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1743 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1746 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1748 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1749 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1750 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1753 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1755 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1756 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1759 if (fHaveUPnP && fUseUPnP != m_checkBoxUseUPnP->GetValue())
1761 fUseUPnP = m_checkBoxUseUPnP->GetValue();
1762 walletdb.WriteSetting("fUseUPnP", fUseUPnP);
1766 fUseProxy = m_checkBoxUseProxy->GetValue();
1767 walletdb.WriteSetting("fUseProxy", fUseProxy);
1769 addrProxy = GetProxyAddr();
1770 walletdb.WriteSetting("addrProxy", addrProxy);
1778 //////////////////////////////////////////////////////////////////////////////
1783 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1785 m_staticTextVersion->SetLabel(strprintf(_("version %s"), FormatFullVersion().c_str()));
1787 // Change (c) into UTF-8 or ANSI copyright symbol
1788 wxString str = m_staticTextMain->GetLabel();
1790 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1792 str.Replace("(c)", "\xA9");
1794 m_staticTextMain->SetLabel(str);
1796 // Resize on Linux to make the window fit the text.
1797 // The text was wrapped manually rather than using the Wrap setting because
1798 // the wrap would be too small on Linux and it can't be changed at this point.
1799 wxFont fontTmp = m_staticTextMain->GetFont();
1800 if (fontTmp.GetPointSize() > 8);
1801 fontTmp.SetPointSize(8);
1802 m_staticTextMain->SetFont(fontTmp);
1803 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1807 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1817 //////////////////////////////////////////////////////////////////////////////
1822 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1825 m_textCtrlAddress->SetValue(strAddress);
1826 m_choiceTransferType->SetSelection(0);
1827 m_bitmapCheckMark->Show(false);
1828 fEnabledPrev = true;
1829 m_textCtrlAddress->SetFocus();
1831 //// todo: should add a display of your balance for convenience
1833 wxFont fontTmp = m_staticTextInstructions->GetFont();
1834 if (fontTmp.GetPointSize() > 9);
1835 fontTmp.SetPointSize(9);
1836 m_staticTextInstructions->SetFont(fontTmp);
1842 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1845 // Fixup the tab order
1846 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1847 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1851 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1853 // Reformat the amount
1855 if (m_textCtrlAmount->GetValue().Trim().empty())
1858 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1859 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1862 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1864 // Open address book
1865 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1866 if (dialog.ShowModal())
1867 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1870 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1872 // Copy clipboard to address box
1873 if (wxTheClipboard->Open())
1875 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1877 wxTextDataObject data;
1878 wxTheClipboard->GetData(data);
1879 m_textCtrlAddress->SetValue(data.GetText());
1881 wxTheClipboard->Close();
1885 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1887 static CCriticalSection cs_sendlock;
1888 TRY_CRITICAL_BLOCK(cs_sendlock)
1891 string strAddress = (string)m_textCtrlAddress->GetValue();
1895 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1897 wxMessageBox(_("Error in amount "), _("Send Coins"));
1900 if (nValue > GetBalance())
1902 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1905 if (nValue + nTransactionFee > GetBalance())
1907 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1911 // Parse bitcoin address
1913 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1915 if (fBitcoinAddress)
1917 CRITICAL_BLOCK(cs_main)
1919 // Send to bitcoin address
1920 CScript scriptPubKey;
1921 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1923 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1925 wxMessageBox(_("Payment sent "), _("Sending..."));
1926 else if (strError == "ABORTED")
1927 return; // leave send dialog open
1930 wxMessageBox(strError + " ", _("Sending..."));
1939 CAddress addr(strAddress);
1940 if (!addr.IsValid())
1942 wxMessageBox(_("Invalid address "), _("Send Coins"));
1947 wtx.mapValue["to"] = strAddress;
1949 // Send to IP address
1950 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1951 if (!pdialog->ShowModal())
1955 CRITICAL_BLOCK(cs_mapAddressBook)
1956 if (!mapAddressBook.count(strAddress))
1957 SetAddressBookName(strAddress, "");
1963 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1974 //////////////////////////////////////////////////////////////////////////////
1979 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
1984 start = wxDateTime::UNow();
1985 memset(pszStatus, 0, sizeof(pszStatus));
1992 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
1995 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
1996 m_textCtrlStatus->SetValue("");
1998 CreateThread(SendingDialogStartTransfer, this);
2001 CSendingDialog::~CSendingDialog()
2003 printf("~CSendingDialog()\n");
2006 void CSendingDialog::Close()
2008 // Last one out turn out the lights.
2009 // fWorkDone signals that work side is done and UI thread should call destroy.
2010 // fUIDone signals that UI window has closed and work thread should call destroy.
2011 // This allows the window to disappear and end modality when cancelled
2012 // without making the user wait for ConnectNode to return. The dialog object
2013 // hangs around in the background until the work thread exits.
2024 void CSendingDialog::OnClose(wxCloseEvent& event)
2026 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2033 wxCommandEvent cmdevent;
2034 OnButtonCancel(cmdevent);
2038 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2044 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2050 void CSendingDialog::OnPaint(wxPaintEvent& event)
2053 if (strlen(pszStatus) > 130)
2054 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2056 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2057 m_staticTextSending->SetFocus();
2059 m_buttonCancel->Enable(false);
2062 m_buttonOK->Enable(true);
2063 m_buttonOK->SetFocus();
2064 m_buttonCancel->Enable(false);
2066 if (fAbort && fCanCancel && IsShown())
2068 strcpy(pszStatus, _("CANCELLED"));
2069 m_buttonOK->Enable(true);
2070 m_buttonOK->SetFocus();
2071 m_buttonCancel->Enable(false);
2072 m_buttonCancel->SetLabel(_("Cancelled"));
2074 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2080 // Everything from here on is not in the UI thread and must only communicate
2081 // with the rest of the dialog through variables and calling repaint.
2084 void CSendingDialog::Repaint()
2088 GetEventHandler()->AddPendingEvent(event);
2091 bool CSendingDialog::Status()
2098 if (fAbort && fCanCancel)
2100 memset(pszStatus, 0, 10);
2101 strcpy(pszStatus, _("CANCELLED"));
2109 bool CSendingDialog::Status(const string& str)
2114 // This can be read by the UI thread at any time,
2115 // so copy in a way that can be read cleanly at all times.
2116 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2117 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2123 bool CSendingDialog::Error(const string& str)
2127 Status(string(_("Error: ")) + str);
2131 void SendingDialogStartTransfer(void* parg)
2133 ((CSendingDialog*)parg)->StartTransfer();
2136 void CSendingDialog::StartTransfer()
2138 // Make sure we have enough money
2139 if (nPrice + nTransactionFee > GetBalance())
2141 Error(_("Insufficient funds"));
2145 // We may have connected already for product details
2146 if (!Status(_("Connecting...")))
2148 CNode* pnode = ConnectNode(addr, 15 * 60);
2151 Error(_("Unable to connect"));
2155 // Send order to seller, with response going to OnReply2 via event handler
2156 if (!Status(_("Requesting public key...")))
2158 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2161 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2163 ((CSendingDialog*)parg)->OnReply2(vRecv);
2166 void CSendingDialog::OnReply2(CDataStream& vRecv)
2168 if (!Status(_("Received public key...")))
2171 CScript scriptPubKey;
2180 vRecv >> strMessage;
2182 Error(_("Recipient is not accepting transactions sent by IP address"));
2184 Error(_("Transfer was not accepted"));
2185 //// todo: enlarge the window and enable a hidden white box to put seller's message
2188 vRecv >> scriptPubKey;
2192 //// what do we want to do about this?
2193 Error(_("Invalid response received"));
2197 // Pause to give the user a chance to cancel
2198 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2205 CRITICAL_BLOCK(cs_main)
2208 if (!Status(_("Creating transaction...")))
2210 if (nPrice + nTransactionFee > GetBalance())
2212 Error(_("Insufficient funds"));
2215 CReserveKey reservekey;
2217 if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2219 if (nPrice + nFeeRequired > GetBalance())
2220 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()));
2222 Error(_("Transaction creation failed"));
2227 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2229 Error(_("Transaction aborted"));
2233 // Make sure we're still connected
2234 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2237 Error(_("Lost connection, transaction cancelled"));
2241 // Last chance to cancel
2253 if (!Status(_("Sending payment...")))
2257 if (!CommitTransaction(wtx, reservekey))
2259 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."));
2263 // Send payment tx to seller, with response going to OnReply3 via event handler
2264 CWalletTx wtxSend = wtx;
2265 wtxSend.fFromMe = false;
2266 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2268 Status(_("Waiting for confirmation..."));
2273 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2275 ((CSendingDialog*)parg)->OnReply3(vRecv);
2278 void CSendingDialog::OnReply3(CDataStream& vRecv)
2286 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2287 "The transaction is recorded and will credit to the recipient,\n"
2288 "but the comment information will be blank."));
2294 //// what do we want to do about this?
2295 Error(_("Payment was sent, but an invalid response was received"));
2301 Status(_("Payment completed"));
2309 //////////////////////////////////////////////////////////////////////////////
2311 // CAddressBookDialog
2314 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2316 // Set initially selected page
2317 wxNotebookEvent event;
2318 event.SetSelection(nPageIn);
2319 OnNotebookPageChanged(event);
2320 m_notebook->ChangeSelection(nPageIn);
2322 fDuringSend = fDuringSendIn;
2324 m_buttonCancel->Show(false);
2327 wxIcon iconAddressBook;
2328 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2329 SetIcon(iconAddressBook);
2331 // Init column headers
2332 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2333 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2334 m_listCtrlSending->SetFocus();
2335 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2336 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2337 m_listCtrlReceiving->SetFocus();
2339 // Fill listctrl with address book data
2340 CRITICAL_BLOCK(cs_mapKeys)
2341 CRITICAL_BLOCK(cs_mapAddressBook)
2343 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2344 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2346 string strAddress = item.first;
2347 string strName = item.second;
2349 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2350 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2351 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2352 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2353 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2358 wxString CAddressBookDialog::GetSelectedAddress()
2360 int nIndex = GetSelection(m_listCtrl);
2363 return GetItemText(m_listCtrl, nIndex, 1);
2366 wxString CAddressBookDialog::GetSelectedSendingAddress()
2368 int nIndex = GetSelection(m_listCtrlSending);
2371 return GetItemText(m_listCtrlSending, nIndex, 1);
2374 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2376 int nIndex = GetSelection(m_listCtrlReceiving);
2379 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2382 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2385 nPage = event.GetSelection();
2386 if (nPage == SENDING)
2387 m_listCtrl = m_listCtrlSending;
2388 else if (nPage == RECEIVING)
2389 m_listCtrl = m_listCtrlReceiving;
2390 m_buttonDelete->Show(nPage == SENDING);
2391 m_buttonCopy->Show(nPage == RECEIVING);
2393 m_listCtrl->SetFocus();
2396 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2398 // Update address book with edited name
2400 if (event.IsEditCancelled())
2402 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2403 SetAddressBookName(strAddress, string(event.GetText()));
2404 pframeMain->RefreshListCtrl();
2407 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2410 if (nPage == RECEIVING)
2411 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2414 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2419 // Doubleclick returns selection
2420 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2424 // Doubleclick edits item
2425 wxCommandEvent event2;
2426 OnButtonEdit(event2);
2429 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2431 if (nPage != SENDING)
2433 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2435 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2437 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2438 CWalletDB().EraseName(strAddress);
2439 m_listCtrl->DeleteItem(nIndex);
2442 pframeMain->RefreshListCtrl();
2445 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2447 // Copy address box to clipboard
2448 if (wxTheClipboard->Open())
2450 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2451 wxTheClipboard->Close();
2455 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2458 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2460 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2464 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2466 int nIndex = GetSelection(m_listCtrl);
2469 string strName = (string)m_listCtrl->GetItemText(nIndex);
2470 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2471 string strAddressOrg = strAddress;
2473 if (nPage == SENDING)
2475 // Ask name and address
2478 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2479 if (!dialog.ShowModal())
2481 strName = dialog.GetValue1();
2482 strAddress = dialog.GetValue2();
2484 while (CheckIfMine(strAddress, _("Edit Address")));
2487 else if (nPage == RECEIVING)
2490 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2491 if (!dialog.ShowModal())
2493 strName = dialog.GetValue();
2497 if (strAddress != strAddressOrg)
2498 CWalletDB().EraseName(strAddressOrg);
2499 SetAddressBookName(strAddress, strName);
2500 m_listCtrl->SetItem(nIndex, 1, strAddress);
2501 m_listCtrl->SetItemText(nIndex, strName);
2502 pframeMain->RefreshListCtrl();
2505 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2510 if (nPage == SENDING)
2512 // Ask name and address
2515 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2516 if (!dialog.ShowModal())
2518 strName = dialog.GetValue1();
2519 strAddress = dialog.GetValue2();
2521 while (CheckIfMine(strAddress, _("Add Address")));
2523 else if (nPage == RECEIVING)
2526 CGetTextFromUserDialog dialog(this,
2527 _("New Receiving Address"),
2528 _("You should use a new address for each payment you receive.\n\nLabel"),
2530 if (!dialog.ShowModal())
2532 strName = dialog.GetValue();
2535 strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2538 // Add to list and select it
2539 SetAddressBookName(strAddress, strName);
2540 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2541 SetSelection(m_listCtrl, nIndex);
2542 m_listCtrl->SetFocus();
2543 if (nPage == SENDING)
2544 pframeMain->RefreshListCtrl();
2547 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2550 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2553 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2559 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2570 //////////////////////////////////////////////////////////////////////////////
2577 ID_TASKBAR_RESTORE = 10001,
2580 ID_TASKBAR_GENERATE,
2584 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2585 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2586 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2587 EVT_MENU(ID_TASKBAR_SEND, CMyTaskBarIcon::OnMenuSend)
2588 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2589 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2590 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2593 void CMyTaskBarIcon::Show(bool fShow)
2595 static char pszPrevTip[200];
2598 string strTooltip = _("Bitcoin");
2599 if (fGenerateBitcoins)
2600 strTooltip = _("Bitcoin - Generating");
2601 if (fGenerateBitcoins && vNodes.empty())
2602 strTooltip = _("Bitcoin - (not connected)");
2604 // Optimization, only update when changed, using char array to be reentrant
2605 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2607 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2609 // somehow it'll choose the wrong size and scale it down if
2610 // we use the main icon, so we hand it one with only 16x16
2611 SetIcon(wxICON(favicon), strTooltip);
2613 SetIcon(bitcoin80_xpm, strTooltip);
2619 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2624 void CMyTaskBarIcon::Hide()
2629 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2634 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2639 void CMyTaskBarIcon::OnMenuSend(wxCommandEvent& event)
2642 CSendDialog dialog(pframeMain);
2646 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2648 // Since it's modal, get the main window to do it
2649 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2650 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2653 void CMyTaskBarIcon::Restore()
2656 wxIconizeEvent event(0, false);
2657 pframeMain->GetEventHandler()->AddPendingEvent(event);
2658 pframeMain->Iconize(false);
2659 pframeMain->Raise();
2662 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2664 event.Check(fGenerateBitcoins);
2667 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2669 pframeMain->Close(true);
2672 void CMyTaskBarIcon::UpdateTooltip()
2674 if (IsIconInstalled())
2678 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2680 wxMenu* pmenu = new wxMenu;
2681 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2682 pmenu->Append(ID_TASKBAR_SEND, _("&Send Bitcoins"));
2683 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2684 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2685 pmenu->AppendSeparator();
2686 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2696 //////////////////////////////////////////////////////////////////////////////
2701 void CreateMainWindow()
2703 pframeMain = new CMainFrame(NULL);
2704 if (GetBoolArg("-min"))
2705 pframeMain->Iconize(true);
2706 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2707 if (!GetBoolArg("-minimizetotray"))
2708 fMinimizeToTray = false;
2710 pframeMain->Show(true); // have to show first to get taskbar button to hide
2711 if (fMinimizeToTray && pframeMain->IsIconized())
2712 fClosedToTray = true;
2713 pframeMain->Show(!fClosedToTray);
2714 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2715 CreateThread(ThreadDelayedRepaint, NULL);
2719 // Define a new application
2720 class CMyApp : public wxApp
2729 // Hook Initialize so we can start without GUI
2730 virtual bool Initialize(int& argc, wxChar** argv);
2732 // 2nd-level exception handling: we get all the exceptions occurring in any
2733 // event handler here
2734 virtual bool OnExceptionInMainLoop();
2736 // 3rd, and final, level exception handling: whenever an unhandled
2737 // exception is caught, this function is called
2738 virtual void OnUnhandledException();
2740 // and now for something different: this function is called in case of a
2741 // crash (e.g. dereferencing null pointer, division by 0, ...)
2742 virtual void OnFatalException();
2745 IMPLEMENT_APP(CMyApp)
2747 bool CMyApp::Initialize(int& argc, wxChar** argv)
2749 for (int i = 1; i < argc; i++)
2750 if (!IsSwitchChar(argv[i][0]))
2751 fCommandLine = true;
2755 // wxApp::Initialize will remove environment-specific parameters,
2756 // so it's too early to call ParseParameters yet
2757 for (int i = 1; i < argc; i++)
2759 wxString str = argv[i];
2761 if (str.size() >= 1 && str[0] == '/')
2763 char pszLower[MAX_PATH];
2764 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2768 if (str == "-daemon")
2774 if (fDaemon || fCommandLine)
2776 // Call the original Initialize while suppressing error messages
2777 // and ignoring failure. If unable to initialize GTK, it fails
2778 // near the end so hopefully the last few things don't matter.
2781 wxApp::Initialize(argc, argv);
2790 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2794 pthread_exit((void*)0);
2796 pid_t sid = setsid();
2798 fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
2805 return wxApp::Initialize(argc, argv);
2808 bool CMyApp::OnInit()
2810 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2811 // Disable malfunctioning wxWidgets debug assertion
2812 extern int g_isPainting;
2813 g_isPainting = 10000;
2815 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2816 SetAppName("Bitcoin");
2818 SetAppName("bitcoin");
2822 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2823 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2824 class wxMBConv_win32 : public wxMBConv
2828 size_t m_minMBCharWidth;
2830 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2831 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2835 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2836 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2837 g_locale.AddCatalogLookupPathPrefix("locale");
2839 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2840 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2842 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2843 g_locale.AddCatalog("bitcoin");
2845 return AppInit(argc, argv);
2848 int CMyApp::OnExit()
2851 return wxApp::OnExit();
2854 bool CMyApp::OnExceptionInMainLoop()
2860 catch (std::exception& e)
2862 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2863 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2869 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2870 wxLogWarning("Unknown exception");
2877 void CMyApp::OnUnhandledException()
2879 // this shows how we may let some exception propagate uncaught
2884 catch (std::exception& e)
2886 PrintException(&e, "CMyApp::OnUnhandledException()");
2887 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2893 PrintException(NULL, "CMyApp::OnUnhandledException()");
2894 wxLogWarning("Unknown exception");
2900 void CMyApp::OnFatalException()
2902 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);