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"];
750 int64 nValue = txout.nValue;
757 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
759 nTime ? DateTimeStr(nTime) : "",
760 SingleLine(strDescription),
761 FormatMoney(-nValue, true),
764 wtx.nLinesDisplayed++;
770 // Mixed debit transaction, can't break down payees
772 bool fAllMine = true;
773 foreach(const CTxOut& txout, wtx.vout)
774 fAllMine = fAllMine && txout.IsMine();
775 foreach(const CTxIn& txin, wtx.vin)
776 fAllMine = fAllMine && txin.IsMine();
778 InsertLine(fNew, nIndex, hash, strSort, colour,
780 nTime ? DateTimeStr(nTime) : "",
782 FormatMoney(nNet, true),
790 void CMainFrame::RefreshListCtrl()
792 fRefreshListCtrl = true;
796 void CMainFrame::OnIdle(wxIdleEvent& event)
798 if (fRefreshListCtrl)
800 // Collect list of wallet transactions and sort newest first
801 bool fEntered = false;
802 vector<pair<unsigned int, uint256> > vSorted;
803 TRY_CRITICAL_BLOCK(cs_mapWallet)
805 printf("RefreshListCtrl starting\n");
807 fRefreshListCtrl = false;
808 vWalletUpdated.clear();
810 // Do the newest transactions first
811 vSorted.reserve(mapWallet.size());
812 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
814 const CWalletTx& wtx = (*it).second;
815 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
816 vSorted.push_back(make_pair(nTime, (*it).first));
818 m_listCtrl->DeleteAllItems();
823 sort(vSorted.begin(), vSorted.end());
826 for (int i = 0; i < vSorted.size();)
830 bool fEntered = false;
831 TRY_CRITICAL_BLOCK(cs_mapWallet)
834 uint256& hash = vSorted[i++].second;
835 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
836 if (mi != mapWallet.end())
837 InsertTransaction((*mi).second, true);
839 if (!fEntered || i == 100 || i % 500 == 0)
843 printf("RefreshListCtrl done\n");
845 // Update transaction total display
850 // Check for time updates
851 static int64 nLastTime;
852 if (GetTime() > nLastTime + 30)
854 TRY_CRITICAL_BLOCK(cs_mapWallet)
856 nLastTime = GetTime();
857 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
859 CWalletTx& wtx = (*it).second;
860 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
861 InsertTransaction(wtx, false);
868 void CMainFrame::RefreshStatusColumn()
871 static CBlockIndex* pindexLastBest;
872 static unsigned int nLastRefreshed;
874 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
875 if (nTop == nLastTop && pindexLastBest == pindexBest)
878 TRY_CRITICAL_BLOCK(cs_mapWallet)
881 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
883 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
885 // If no updates, only need to do the part that moved onto the screen
886 if (nStart >= nLastTop && nStart < nLastTop + 100)
887 nStart = nLastTop + 100;
888 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
892 pindexLastBest = pindexBest;
893 nLastRefreshed = nListViewUpdated;
895 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
897 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
898 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
899 if (mi == mapWallet.end())
901 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
904 CWalletTx& wtx = (*mi).second;
905 if (wtx.IsCoinBase() ||
906 wtx.GetTxTime() != wtx.nTimeDisplayed ||
907 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
909 if (!InsertTransaction(wtx, false, nIndex))
910 m_listCtrl->DeleteItem(nIndex--);
914 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
920 void CMainFrame::OnPaint(wxPaintEvent& event)
931 unsigned int nNeedRepaint = 0;
932 unsigned int nLastRepaint = 0;
933 int64 nLastRepaintTime = 0;
934 int64 nRepaintInterval = 500;
936 void ThreadDelayedRepaint(void* parg)
940 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
942 nLastRepaint = nNeedRepaint;
945 printf("DelayedRepaint\n");
947 pframeMain->fRefresh = true;
948 pframeMain->GetEventHandler()->AddPendingEvent(event);
951 Sleep(nRepaintInterval);
955 void MainFrameRepaint()
957 // This is called by network code that shouldn't access pframeMain
958 // directly because it could still be running after the UI is closed.
961 // Don't repaint too often
962 static int64 nLastRepaintRequest;
963 if (GetTimeMillis() - nLastRepaintRequest < 100)
968 nLastRepaintRequest = GetTimeMillis();
970 printf("MainFrameRepaint\n");
972 pframeMain->fRefresh = true;
973 pframeMain->GetEventHandler()->AddPendingEvent(event);
977 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
979 // Skip lets the listctrl do the paint, we're just hooking the message
983 ptaskbaricon->UpdateTooltip();
988 static int nTransactionCount;
989 bool fPaintedBalance = false;
990 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
992 nLastRepaint = nNeedRepaint;
993 nLastRepaintTime = GetTimeMillis();
995 // Update listctrl contents
996 if (!vWalletUpdated.empty())
998 TRY_CRITICAL_BLOCK(cs_mapWallet)
1001 if (m_listCtrl->GetItemCount())
1002 strTop = (string)m_listCtrl->GetItemText(0);
1003 foreach(uint256 hash, vWalletUpdated)
1005 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1006 if (mi != mapWallet.end())
1007 InsertTransaction((*mi).second, false);
1009 vWalletUpdated.clear();
1010 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1011 m_listCtrl->ScrollList(0, INT_MIN/2);
1016 TRY_CRITICAL_BLOCK(cs_mapWallet)
1018 fPaintedBalance = true;
1019 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
1021 // Count hidden and multi-line transactions
1022 nTransactionCount = 0;
1023 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1025 CWalletTx& wtx = (*it).second;
1026 nTransactionCount += wtx.nLinesDisplayed;
1030 if (!vWalletUpdated.empty() || !fPaintedBalance)
1033 // Update status column of visible items only
1034 RefreshStatusColumn();
1036 // Update status bar
1037 static string strPrevWarning;
1038 string strWarning = GetWarnings("statusbar");
1039 if (strWarning != "")
1040 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1041 else if (strPrevWarning != "")
1042 m_statusBar->SetStatusText("", 0);
1043 strPrevWarning = strWarning;
1046 if (fGenerateBitcoins)
1047 strGen = _(" Generating");
1048 if (fGenerateBitcoins && vNodes.empty())
1049 strGen = _("(not connected)");
1050 m_statusBar->SetStatusText(strGen, 1);
1052 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1053 m_statusBar->SetStatusText(strStatus, 2);
1055 // Update receiving address
1056 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1057 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1058 m_textCtrlAddress->SetValue(strDefaultAddress);
1062 void UIThreadCall(boost::function0<void> fn)
1064 // Call this with a function object created with bind.
1065 // bind needs all parameters to match the function's expected types
1066 // and all default parameters specified. Some examples:
1067 // UIThreadCall(bind(wxBell));
1068 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1069 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1072 wxCommandEvent event(wxEVT_UITHREADCALL);
1073 event.SetClientData((void*)new boost::function0<void>(fn));
1074 pframeMain->GetEventHandler()->AddPendingEvent(event);
1078 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1080 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1085 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1091 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
1093 // Options->Generate Coins
1094 GenerateBitcoins(event.IsChecked());
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>";
1409 if (wtx.IsCoinBase())
1410 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>";
1418 strHTML += "<hr><br>debug print<br><br>";
1419 foreach(const CTxIn& txin, wtx.vin)
1421 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1422 foreach(const CTxOut& txout, wtx.vout)
1424 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1426 strHTML += "<br><b>Transaction:</b><br>";
1427 strHTML += HtmlEscape(wtx.ToString(), true);
1429 strHTML += "<br><b>Inputs:</b><br>";
1430 CRITICAL_BLOCK(cs_mapWallet)
1432 foreach(const CTxIn& txin, wtx.vin)
1434 COutPoint prevout = txin.prevout;
1435 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1436 if (mi != mapWallet.end())
1438 const CWalletTx& prev = (*mi).second;
1439 if (prevout.n < prev.vout.size())
1441 strHTML += HtmlEscape(prev.ToString(), true);
1442 strHTML += " " + FormatTxStatus(prev) + ", ";
1443 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1452 strHTML += "</font></html>";
1453 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1454 m_htmlWin->SetPage(strHTML);
1455 m_buttonOK->SetFocus();
1459 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1470 //////////////////////////////////////////////////////////////////////////////
1476 string StartupShortcutPath()
1478 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1481 bool GetStartOnSystemStartup()
1483 return filesystem::exists(StartupShortcutPath().c_str());
1486 void SetStartOnSystemStartup(bool fAutoStart)
1488 // If the shortcut exists already, remove it for updating
1489 remove(StartupShortcutPath().c_str());
1495 // Get a pointer to the IShellLink interface.
1496 IShellLink* psl = NULL;
1497 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1498 CLSCTX_INPROC_SERVER, IID_IShellLink,
1499 reinterpret_cast<void**>(&psl));
1501 if (SUCCEEDED(hres))
1503 // Get the current executable path
1504 TCHAR pszExePath[MAX_PATH];
1505 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1507 // Set the path to the shortcut target
1508 psl->SetPath(pszExePath);
1509 PathRemoveFileSpec(pszExePath);
1510 psl->SetWorkingDirectory(pszExePath);
1511 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1513 // Query IShellLink for the IPersistFile interface for
1514 // saving the shortcut in persistent storage.
1515 IPersistFile* ppf = NULL;
1516 hres = psl->QueryInterface(IID_IPersistFile,
1517 reinterpret_cast<void**>(&ppf));
1518 if (SUCCEEDED(hres))
1520 WCHAR pwsz[MAX_PATH];
1521 // Ensure that the string is ANSI.
1522 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1523 // Save the link by calling IPersistFile::Save.
1524 hres = ppf->Save(pwsz, TRUE);
1533 #elif defined(__WXGTK__)
1535 // Follow the Desktop Application Autostart Spec:
1536 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1538 boost::filesystem::path GetAutostartDir()
1540 namespace fs = boost::filesystem;
1542 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1543 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1544 char* pszHome = getenv("HOME");
1545 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1549 boost::filesystem::path GetAutostartFilePath()
1551 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1554 bool GetStartOnSystemStartup()
1556 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1557 if (!optionFile.good())
1559 // Scan through file for "Hidden=true":
1561 while (!optionFile.eof())
1563 getline(optionFile, line);
1564 if (line.find("Hidden") != string::npos &&
1565 line.find("true") != string::npos)
1573 void SetStartOnSystemStartup(bool fAutoStart)
1577 unlink(GetAutostartFilePath().native_file_string().c_str());
1581 char pszExePath[MAX_PATH+1];
1582 memset(pszExePath, 0, sizeof(pszExePath));
1583 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1586 boost::filesystem::create_directories(GetAutostartDir());
1588 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1589 if (!optionFile.good())
1591 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1594 // Write a bitcoin.desktop file to the autostart directory:
1595 optionFile << "[Desktop Entry]\n";
1596 optionFile << "Type=Application\n";
1597 optionFile << "Name=Bitcoin\n";
1598 optionFile << "Exec=" << pszExePath << "\n";
1599 optionFile << "Terminal=false\n";
1600 optionFile << "Hidden=false\n";
1606 // TODO: OSX startup stuff; see:
1607 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1609 bool GetStartOnSystemStartup() { return false; }
1610 void SetStartOnSystemStartup(bool fAutoStart) { }
1619 //////////////////////////////////////////////////////////////////////////////
1624 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1626 // Set up list box of page choices
1627 m_listBox->Append(_("Main"));
1628 //m_listBox->Append(_("Test 2"));
1629 m_listBox->SetSelection(0);
1632 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1634 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1635 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1636 if (!GetBoolArg("-minimizetotray"))
1638 // Minimize to tray is just too buggy on Linux
1639 fMinimizeToTray = false;
1640 m_checkBoxMinimizeToTray->SetValue(false);
1641 m_checkBoxMinimizeToTray->Enable(false);
1642 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1645 #ifdef __WXMAC_OSX__
1646 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1650 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1651 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
1652 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
1653 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
1654 int nProcessors = wxThread::GetCPUCount();
1655 if (nProcessors < 1)
1657 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
1658 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1659 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1660 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
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::OnCheckBoxLimitProcessors(wxCommandEvent& event)
1696 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
1699 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1701 m_textCtrlProxyIP->Enable(event.IsChecked());
1702 m_textCtrlProxyPort->Enable(event.IsChecked());
1703 m_staticTextProxyIP->Enable(event.IsChecked());
1704 m_staticTextProxyPort->Enable(event.IsChecked());
1707 CAddress COptionsDialog::GetProxyAddr()
1709 // Be careful about byte order, addr.ip and addr.port are big endian
1710 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1711 if (addr.ip == INADDR_NONE)
1712 addr.ip = addrProxy.ip;
1713 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1714 addr.port = htons(nPort);
1715 if (nPort <= 0 || nPort > USHRT_MAX)
1716 addr.port = addrProxy.port;
1720 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1723 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1724 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1728 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1730 OnButtonApply(event);
1734 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1739 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1743 int64 nPrevTransactionFee = nTransactionFee;
1744 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1745 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1747 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
1748 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
1750 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
1751 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
1753 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
1755 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
1756 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
1758 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
1759 GenerateBitcoins(fGenerateBitcoins);
1761 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1763 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1764 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1767 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1769 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1770 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1771 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1774 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1776 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1777 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1780 fUseProxy = m_checkBoxUseProxy->GetValue();
1781 walletdb.WriteSetting("fUseProxy", fUseProxy);
1783 addrProxy = GetProxyAddr();
1784 walletdb.WriteSetting("addrProxy", addrProxy);
1792 //////////////////////////////////////////////////////////////////////////////
1797 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1799 m_staticTextVersion->SetLabel(strprintf(_("version %s%s beta"), FormatVersion(VERSION).c_str(), pszSubVer));
1801 // Change (c) into UTF-8 or ANSI copyright symbol
1802 wxString str = m_staticTextMain->GetLabel();
1804 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1806 str.Replace("(c)", "\xA9");
1808 m_staticTextMain->SetLabel(str);
1810 // Resize on Linux to make the window fit the text.
1811 // The text was wrapped manually rather than using the Wrap setting because
1812 // the wrap would be too small on Linux and it can't be changed at this point.
1813 wxFont fontTmp = m_staticTextMain->GetFont();
1814 if (fontTmp.GetPointSize() > 8);
1815 fontTmp.SetPointSize(8);
1816 m_staticTextMain->SetFont(fontTmp);
1817 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1821 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1831 //////////////////////////////////////////////////////////////////////////////
1836 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1839 m_textCtrlAddress->SetValue(strAddress);
1840 m_choiceTransferType->SetSelection(0);
1841 m_bitmapCheckMark->Show(false);
1842 fEnabledPrev = true;
1843 m_textCtrlAddress->SetFocus();
1844 //// todo: should add a display of your balance for convenience
1846 wxFont fontTmp = m_staticTextInstructions->GetFont();
1847 if (fontTmp.GetPointSize() > 9);
1848 fontTmp.SetPointSize(9);
1849 m_staticTextInstructions->SetFont(fontTmp);
1855 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1858 wxCommandEvent event;
1859 OnTextAddress(event);
1861 // Fixup the tab order
1862 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1863 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1867 void CSendDialog::OnTextAddress(wxCommandEvent& event)
1871 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
1872 m_bitmapCheckMark->Show(fBitcoinAddress);
1874 // Grey out message if bitcoin address
1875 bool fEnable = !fBitcoinAddress;
1876 m_staticTextFrom->Enable(fEnable);
1877 m_textCtrlFrom->Enable(fEnable);
1878 m_staticTextMessage->Enable(fEnable);
1879 m_textCtrlMessage->Enable(fEnable);
1880 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
1881 if (!fEnable && fEnabledPrev)
1883 strFromSave = m_textCtrlFrom->GetValue();
1884 strMessageSave = m_textCtrlMessage->GetValue();
1885 m_textCtrlFrom->SetValue(_("n/a"));
1886 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
1888 else if (fEnable && !fEnabledPrev)
1890 m_textCtrlFrom->SetValue(strFromSave);
1891 m_textCtrlMessage->SetValue(strMessageSave);
1893 fEnabledPrev = fEnable;
1896 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1898 // Reformat the amount
1900 if (m_textCtrlAmount->GetValue().Trim().empty())
1903 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1904 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1907 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1909 // Open address book
1910 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1911 if (dialog.ShowModal())
1912 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1915 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1917 // Copy clipboard to address box
1918 if (wxTheClipboard->Open())
1920 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1922 wxTextDataObject data;
1923 wxTheClipboard->GetData(data);
1924 m_textCtrlAddress->SetValue(data.GetText());
1926 wxTheClipboard->Close();
1930 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1932 static CCriticalSection cs_sendlock;
1933 TRY_CRITICAL_BLOCK(cs_sendlock)
1936 string strAddress = (string)m_textCtrlAddress->GetValue();
1940 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1942 wxMessageBox(_("Error in amount "), _("Send Coins"));
1945 if (nValue > GetBalance())
1947 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1950 if (nValue + nTransactionFee > GetBalance())
1952 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1956 // Parse bitcoin address
1958 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1960 if (fBitcoinAddress)
1962 // Send to bitcoin address
1963 CScript scriptPubKey;
1964 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1966 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1968 wxMessageBox(_("Payment sent "), _("Sending..."));
1969 else if (strError == "ABORTED")
1970 return; // leave send dialog open
1973 wxMessageBox(strError + " ", _("Sending..."));
1980 CAddress addr(strAddress);
1981 if (!addr.IsValid())
1983 wxMessageBox(_("Invalid address "), _("Send Coins"));
1988 wtx.mapValue["to"] = strAddress;
1989 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
1990 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
1992 // Send to IP address
1993 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1994 if (!pdialog->ShowModal())
1998 CRITICAL_BLOCK(cs_mapAddressBook)
1999 if (!mapAddressBook.count(strAddress))
2000 SetAddressBookName(strAddress, "");
2006 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2017 //////////////////////////////////////////////////////////////////////////////
2022 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
2027 start = wxDateTime::UNow();
2028 memset(pszStatus, 0, sizeof(pszStatus));
2035 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2038 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2039 m_textCtrlStatus->SetValue("");
2041 CreateThread(SendingDialogStartTransfer, this);
2044 CSendingDialog::~CSendingDialog()
2046 printf("~CSendingDialog()\n");
2049 void CSendingDialog::Close()
2051 // Last one out turn out the lights.
2052 // fWorkDone signals that work side is done and UI thread should call destroy.
2053 // fUIDone signals that UI window has closed and work thread should call destroy.
2054 // This allows the window to disappear and end modality when cancelled
2055 // without making the user wait for ConnectNode to return. The dialog object
2056 // hangs around in the background until the work thread exits.
2067 void CSendingDialog::OnClose(wxCloseEvent& event)
2069 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2076 wxCommandEvent cmdevent;
2077 OnButtonCancel(cmdevent);
2081 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2087 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2093 void CSendingDialog::OnPaint(wxPaintEvent& event)
2096 if (strlen(pszStatus) > 130)
2097 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2099 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2100 m_staticTextSending->SetFocus();
2102 m_buttonCancel->Enable(false);
2105 m_buttonOK->Enable(true);
2106 m_buttonOK->SetFocus();
2107 m_buttonCancel->Enable(false);
2109 if (fAbort && fCanCancel && IsShown())
2111 strcpy(pszStatus, _("CANCELLED"));
2112 m_buttonOK->Enable(true);
2113 m_buttonOK->SetFocus();
2114 m_buttonCancel->Enable(false);
2115 m_buttonCancel->SetLabel(_("Cancelled"));
2117 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2123 // Everything from here on is not in the UI thread and must only communicate
2124 // with the rest of the dialog through variables and calling repaint.
2127 void CSendingDialog::Repaint()
2131 GetEventHandler()->AddPendingEvent(event);
2134 bool CSendingDialog::Status()
2141 if (fAbort && fCanCancel)
2143 memset(pszStatus, 0, 10);
2144 strcpy(pszStatus, _("CANCELLED"));
2152 bool CSendingDialog::Status(const string& str)
2157 // This can be read by the UI thread at any time,
2158 // so copy in a way that can be read cleanly at all times.
2159 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2160 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2166 bool CSendingDialog::Error(const string& str)
2170 Status(string(_("Error: ")) + str);
2174 void SendingDialogStartTransfer(void* parg)
2176 ((CSendingDialog*)parg)->StartTransfer();
2179 void CSendingDialog::StartTransfer()
2181 // Make sure we have enough money
2182 if (nPrice + nTransactionFee > GetBalance())
2184 Error(_("Insufficient funds"));
2188 // We may have connected already for product details
2189 if (!Status(_("Connecting...")))
2191 CNode* pnode = ConnectNode(addr, 15 * 60);
2194 Error(_("Unable to connect"));
2198 // Send order to seller, with response going to OnReply2 via event handler
2199 if (!Status(_("Requesting public key...")))
2201 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2204 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2206 ((CSendingDialog*)parg)->OnReply2(vRecv);
2209 void CSendingDialog::OnReply2(CDataStream& vRecv)
2211 if (!Status(_("Received public key...")))
2214 CScript scriptPubKey;
2223 vRecv >> strMessage;
2225 Error(_("Recipient is not accepting transactions sent by IP address"));
2227 Error(_("Transfer was not accepted"));
2228 //// todo: enlarge the window and enable a hidden white box to put seller's message
2231 vRecv >> scriptPubKey;
2235 //// what do we want to do about this?
2236 Error(_("Invalid response received"));
2240 // Pause to give the user a chance to cancel
2241 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2248 CRITICAL_BLOCK(cs_main)
2251 if (!Status(_("Creating transaction...")))
2253 if (nPrice + nTransactionFee > GetBalance())
2255 Error(_("Insufficient funds"));
2258 CReserveKey reservekey;
2260 if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2262 if (nPrice + nFeeRequired > GetBalance())
2263 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
2265 Error(_("Transaction creation failed"));
2270 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2272 Error(_("Transaction aborted"));
2276 // Make sure we're still connected
2277 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2280 Error(_("Lost connection, transaction cancelled"));
2284 // Last chance to cancel
2296 if (!Status(_("Sending payment...")))
2300 if (!CommitTransaction(wtx, reservekey))
2302 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."));
2306 // Send payment tx to seller, with response going to OnReply3 via event handler
2307 CWalletTx wtxSend = wtx;
2308 wtxSend.fFromMe = false;
2309 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2311 Status(_("Waiting for confirmation..."));
2316 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2318 ((CSendingDialog*)parg)->OnReply3(vRecv);
2321 void CSendingDialog::OnReply3(CDataStream& vRecv)
2329 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2330 "The transaction is recorded and will credit to the recipient,\n"
2331 "but the comment information will be blank."));
2337 //// what do we want to do about this?
2338 Error(_("Payment was sent, but an invalid response was received"));
2344 Status(_("Payment completed"));
2352 //////////////////////////////////////////////////////////////////////////////
2354 // CAddressBookDialog
2357 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2359 // Set initially selected page
2360 wxNotebookEvent event;
2361 event.SetSelection(nPageIn);
2362 OnNotebookPageChanged(event);
2363 m_notebook->ChangeSelection(nPageIn);
2365 fDuringSend = fDuringSendIn;
2367 m_buttonCancel->Show(false);
2370 wxIcon iconAddressBook;
2371 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2372 SetIcon(iconAddressBook);
2374 // Init column headers
2375 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2376 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2377 m_listCtrlSending->SetFocus();
2378 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2379 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2380 m_listCtrlReceiving->SetFocus();
2382 // Fill listctrl with address book data
2383 CRITICAL_BLOCK(cs_mapKeys)
2384 CRITICAL_BLOCK(cs_mapAddressBook)
2386 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2387 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2389 string strAddress = item.first;
2390 string strName = item.second;
2392 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2393 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2394 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2395 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2396 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2401 wxString CAddressBookDialog::GetSelectedAddress()
2403 int nIndex = GetSelection(m_listCtrl);
2406 return GetItemText(m_listCtrl, nIndex, 1);
2409 wxString CAddressBookDialog::GetSelectedSendingAddress()
2411 int nIndex = GetSelection(m_listCtrlSending);
2414 return GetItemText(m_listCtrlSending, nIndex, 1);
2417 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2419 int nIndex = GetSelection(m_listCtrlReceiving);
2422 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2425 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2428 nPage = event.GetSelection();
2429 if (nPage == SENDING)
2430 m_listCtrl = m_listCtrlSending;
2431 else if (nPage == RECEIVING)
2432 m_listCtrl = m_listCtrlReceiving;
2433 m_buttonDelete->Show(nPage == SENDING);
2434 m_buttonCopy->Show(nPage == RECEIVING);
2436 m_listCtrl->SetFocus();
2439 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2441 // Update address book with edited name
2443 if (event.IsEditCancelled())
2445 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2446 SetAddressBookName(strAddress, string(event.GetText()));
2447 pframeMain->RefreshListCtrl();
2450 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2453 if (nPage == RECEIVING)
2454 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2457 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2462 // Doubleclick returns selection
2463 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2467 // Doubleclick edits item
2468 wxCommandEvent event2;
2469 OnButtonEdit(event2);
2472 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2474 if (nPage != SENDING)
2476 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2478 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2480 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2481 CWalletDB().EraseName(strAddress);
2482 m_listCtrl->DeleteItem(nIndex);
2485 pframeMain->RefreshListCtrl();
2488 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2490 // Copy address box to clipboard
2491 if (wxTheClipboard->Open())
2493 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2494 wxTheClipboard->Close();
2498 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2501 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2503 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2507 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2509 int nIndex = GetSelection(m_listCtrl);
2512 string strName = (string)m_listCtrl->GetItemText(nIndex);
2513 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2514 string strAddressOrg = strAddress;
2516 if (nPage == SENDING)
2518 // Ask name and address
2521 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2522 if (!dialog.ShowModal())
2524 strName = dialog.GetValue1();
2525 strAddress = dialog.GetValue2();
2527 while (CheckIfMine(strAddress, _("Edit Address")));
2530 else if (nPage == RECEIVING)
2533 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2534 if (!dialog.ShowModal())
2536 strName = dialog.GetValue();
2540 if (strAddress != strAddressOrg)
2541 CWalletDB().EraseName(strAddressOrg);
2542 SetAddressBookName(strAddress, strName);
2543 m_listCtrl->SetItem(nIndex, 1, strAddress);
2544 m_listCtrl->SetItemText(nIndex, strName);
2545 pframeMain->RefreshListCtrl();
2548 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2553 if (nPage == SENDING)
2555 // Ask name and address
2558 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2559 if (!dialog.ShowModal())
2561 strName = dialog.GetValue1();
2562 strAddress = dialog.GetValue2();
2564 while (CheckIfMine(strAddress, _("Add Address")));
2566 else if (nPage == RECEIVING)
2569 CGetTextFromUserDialog dialog(this,
2570 _("New Receiving Address"),
2571 _("You should use a new address for each payment you receive.\n\nLabel"),
2573 if (!dialog.ShowModal())
2575 strName = dialog.GetValue();
2578 strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2581 // Add to list and select it
2582 SetAddressBookName(strAddress, strName);
2583 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2584 SetSelection(m_listCtrl, nIndex);
2585 m_listCtrl->SetFocus();
2586 if (nPage == SENDING)
2587 pframeMain->RefreshListCtrl();
2590 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2593 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2596 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2602 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2613 //////////////////////////////////////////////////////////////////////////////
2620 ID_TASKBAR_RESTORE = 10001,
2622 ID_TASKBAR_GENERATE,
2626 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2627 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2628 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2629 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2630 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
2631 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2632 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2635 void CMyTaskBarIcon::Show(bool fShow)
2637 static char pszPrevTip[200];
2640 string strTooltip = _("Bitcoin");
2641 if (fGenerateBitcoins)
2642 strTooltip = _("Bitcoin - Generating");
2643 if (fGenerateBitcoins && vNodes.empty())
2644 strTooltip = _("Bitcoin - (not connected)");
2646 // Optimization, only update when changed, using char array to be reentrant
2647 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2649 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2651 // somehow it'll choose the wrong size and scale it down if
2652 // we use the main icon, so we hand it one with only 16x16
2653 SetIcon(wxICON(favicon), strTooltip);
2655 SetIcon(bitcoin80_xpm, strTooltip);
2661 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2666 void CMyTaskBarIcon::Hide()
2671 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2676 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2681 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2683 // Since it's modal, get the main window to do it
2684 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2685 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2688 void CMyTaskBarIcon::Restore()
2691 wxIconizeEvent event(0, false);
2692 pframeMain->GetEventHandler()->AddPendingEvent(event);
2693 pframeMain->Iconize(false);
2694 pframeMain->Raise();
2697 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
2699 GenerateBitcoins(event.IsChecked());
2702 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2704 event.Check(fGenerateBitcoins);
2707 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2709 pframeMain->Close(true);
2712 void CMyTaskBarIcon::UpdateTooltip()
2714 if (IsIconInstalled())
2718 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2720 wxMenu* pmenu = new wxMenu;
2721 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2722 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2723 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
2724 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2725 pmenu->AppendSeparator();
2726 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2736 //////////////////////////////////////////////////////////////////////////////
2741 void CreateMainWindow()
2743 pframeMain = new CMainFrame(NULL);
2744 if (GetBoolArg("-min"))
2745 pframeMain->Iconize(true);
2746 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2747 if (!GetBoolArg("-minimizetotray"))
2748 fMinimizeToTray = false;
2750 pframeMain->Show(true); // have to show first to get taskbar button to hide
2751 if (fMinimizeToTray && pframeMain->IsIconized())
2752 fClosedToTray = true;
2753 pframeMain->Show(!fClosedToTray);
2754 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2755 CreateThread(ThreadDelayedRepaint, NULL);
2759 // Define a new application
2760 class CMyApp : public wxApp
2769 // Hook Initialize so we can start without GUI
2770 virtual bool Initialize(int& argc, wxChar** argv);
2772 // 2nd-level exception handling: we get all the exceptions occurring in any
2773 // event handler here
2774 virtual bool OnExceptionInMainLoop();
2776 // 3rd, and final, level exception handling: whenever an unhandled
2777 // exception is caught, this function is called
2778 virtual void OnUnhandledException();
2780 // and now for something different: this function is called in case of a
2781 // crash (e.g. dereferencing null pointer, division by 0, ...)
2782 virtual void OnFatalException();
2785 IMPLEMENT_APP(CMyApp)
2787 bool CMyApp::Initialize(int& argc, wxChar** argv)
2789 for (int i = 1; i < argc; i++)
2790 if (!IsSwitchChar(argv[i][0]))
2791 fCommandLine = true;
2795 // wxApp::Initialize will remove environment-specific parameters,
2796 // so it's too early to call ParseParameters yet
2797 for (int i = 1; i < argc; i++)
2799 wxString str = argv[i];
2801 if (str.size() >= 1 && str[0] == '/')
2803 char pszLower[MAX_PATH];
2804 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2808 if (str == "-daemon")
2814 if (fDaemon || fCommandLine)
2816 // Call the original Initialize while suppressing error messages
2817 // and ignoring failure. If unable to initialize GTK, it fails
2818 // near the end so hopefully the last few things don't matter.
2821 wxApp::Initialize(argc, argv);
2830 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2834 pthread_exit((void*)0);
2841 return wxApp::Initialize(argc, argv);
2844 bool CMyApp::OnInit()
2846 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2847 // Disable malfunctioning wxWidgets debug assertion
2848 extern int g_isPainting;
2849 g_isPainting = 10000;
2852 wxImage::AddHandler(new wxPNGHandler);
2854 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2855 SetAppName("Bitcoin");
2857 SetAppName("bitcoin");
2861 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2862 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2863 class wxMBConv_win32 : public wxMBConv
2867 size_t m_minMBCharWidth;
2869 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2870 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2874 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2875 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2876 g_locale.AddCatalogLookupPathPrefix("locale");
2878 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2879 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2881 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2882 g_locale.AddCatalog("bitcoin");
2884 return AppInit(argc, argv);
2887 int CMyApp::OnExit()
2890 return wxApp::OnExit();
2893 bool CMyApp::OnExceptionInMainLoop()
2899 catch (std::exception& e)
2901 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2902 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2908 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2909 wxLogWarning("Unknown exception");
2916 void CMyApp::OnUnhandledException()
2918 // this shows how we may let some exception propagate uncaught
2923 catch (std::exception& e)
2925 PrintException(&e, "CMyApp::OnUnhandledException()");
2926 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2932 PrintException(NULL, "CMyApp::OnUnhandledException()");
2933 wxLogWarning("Unknown exception");
2939 void CMyApp::OnFatalException()
2941 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);