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);
112 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
113 listCtrl->SetItemTextColour(nIndex, colour);
116 void SetSelection(wxListCtrl* listCtrl, int nIndex)
118 int nSize = listCtrl->GetItemCount();
119 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
120 for (int i = 0; i < nSize; i++)
121 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
124 int GetSelection(wxListCtrl* listCtrl)
126 int nSize = listCtrl->GetItemCount();
127 for (int i = 0; i < nSize; i++)
128 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
133 string HtmlEscape(const char* psz, bool fMultiLine=false)
136 for (const char* p = psz; *p; p++)
138 if (*p == '<') len += 4;
139 else if (*p == '>') len += 4;
140 else if (*p == '&') len += 5;
141 else if (*p == '"') len += 6;
142 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
143 else if (*p == '\n' && fMultiLine) len += 5;
149 for (const char* p = psz; *p; p++)
151 if (*p == '<') str += "<";
152 else if (*p == '>') str += ">";
153 else if (*p == '&') str += "&";
154 else if (*p == '"') str += """;
155 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
156 else if (*p == '\n' && fMultiLine) str += "<br>\n";
163 string HtmlEscape(const string& str, bool fMultiLine=false)
165 return HtmlEscape(str.c_str(), fMultiLine);
168 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
170 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
174 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
177 return wxMessageBox(message, caption, style, parent, x, y);
179 if (wxThread::IsMain() || fDaemon)
181 return wxMessageBox(message, caption, style, parent, x, y);
187 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
195 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
197 if (nFeeRequired < CENT || fDaemon)
199 string strMessage = strprintf(
200 _("This transaction is over the size limit. You can still send it for a fee of %s, "
201 "which goes to the nodes that process your transaction and helps to support the network. "
202 "Do you want to pay the fee?"),
203 FormatMoney(nFeeRequired).c_str());
204 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
207 void CalledSetStatusBar(const string& strText, int nField)
209 if (nField == 0 && GetWarnings("statusbar") != "")
211 if (pframeMain && pframeMain->m_statusBar)
212 pframeMain->m_statusBar->SetStatusText(strText, nField);
215 void SetDefaultReceivingAddress(const string& strAddress)
217 // Update main window address and database
218 if (pframeMain == NULL)
220 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
223 if (!AddressToHash160(strAddress, hash160))
225 if (!mapPubKeys.count(hash160))
227 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
228 pframeMain->m_textCtrlAddress->SetValue(strAddress);
241 //////////////////////////////////////////////////////////////////////////////
246 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
248 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
250 // Set initially selected page
251 wxNotebookEvent event;
252 event.SetSelection(0);
253 OnNotebookPageChanged(event);
254 m_notebook->ChangeSelection(0);
257 fRefreshListCtrl = false;
258 fRefreshListCtrlRunning = false;
259 fOnSetFocusAddress = false;
261 m_choiceFilter->SetSelection(0);
262 double dResize = 1.0;
264 SetIcon(wxICON(bitcoin));
266 SetIcon(bitcoin80_xpm);
267 SetBackgroundColour(m_toolBar->GetBackgroundColour());
268 wxFont fontTmp = m_staticText41->GetFont();
269 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
270 m_staticTextBalance->SetFont(fontTmp);
271 m_staticTextBalance->SetSize(140, 17);
272 // resize to fit ubuntu's huge default font
274 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
276 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
277 m_listCtrl->SetFocus();
278 ptaskbaricon = new CMyTaskBarIcon();
280 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
281 // to their standard places, leaving these menus empty.
282 GetMenuBar()->Remove(2); // remove Help menu
283 GetMenuBar()->Remove(0); // remove File menu
286 // Init column headers
287 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
288 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
294 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
295 foreach(wxListCtrl* p, pplistCtrl)
297 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
298 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
299 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
300 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
301 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
302 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
303 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
307 int pnWidths[3] = { -100, 88, 300 };
309 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
310 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
312 m_statusBar->SetFieldsCount(3, pnWidths);
314 // Fill your address text box
315 vector<unsigned char> vchPubKey;
316 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
317 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
319 // Fill listctrl with wallet transactions
323 CMainFrame::~CMainFrame()
330 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
333 nPage = event.GetSelection();
336 m_listCtrl = m_listCtrlAll;
337 fShowGenerated = true;
339 fShowReceived = true;
341 else if (nPage == SENTRECEIVED)
343 m_listCtrl = m_listCtrlSentReceived;
344 fShowGenerated = false;
346 fShowReceived = true;
348 else if (nPage == SENT)
350 m_listCtrl = m_listCtrlSent;
351 fShowGenerated = false;
353 fShowReceived = false;
355 else if (nPage == RECEIVED)
357 m_listCtrl = m_listCtrlReceived;
358 fShowGenerated = false;
360 fShowReceived = true;
363 m_listCtrl->SetFocus();
366 void CMainFrame::OnClose(wxCloseEvent& event)
368 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
370 // Divert close to minimize
372 fClosedToTray = true;
378 CreateThread(Shutdown, NULL);
382 void CMainFrame::OnIconize(wxIconizeEvent& event)
385 // Hide the task bar button when minimized.
386 // Event is sent when the frame is minimized or restored.
387 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
388 // to get rid of the deprecated warning. Just ignore it.
389 if (!event.Iconized())
390 fClosedToTray = false;
391 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
392 if (mapArgs.count("-minimizetotray")) {
394 // The tray icon sometimes disappears on ubuntu karmic
395 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
396 // Reports of CPU peg on 64-bit linux
397 if (fMinimizeToTray && event.Iconized())
398 fClosedToTray = true;
399 Show(!fClosedToTray);
400 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
401 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
406 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
410 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
411 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
414 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
416 // Hidden columns not resizeable
417 if (event.GetColumn() <= 1 && !fDebug)
423 int CMainFrame::GetSortIndex(const string& strSort)
428 // The wx generic listctrl implementation used on GTK doesn't sort,
429 // so we have to do it ourselves. Remember, we sort in reverse order.
430 // In the wx generic implementation, they store the list of items
431 // in a vector, so indexed lookups are fast, but inserts are slower
432 // the closer they are to the top.
434 int high = m_listCtrl->GetItemCount();
437 int mid = low + ((high - low) / 2);
438 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
447 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)
449 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
450 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
453 if (!fNew && nIndex == -1)
455 string strHash = " " + hashKey.ToString();
456 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
457 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
461 // fNew is for blind insert, only use if you're sure it's new
462 if (fNew || nIndex == -1)
464 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
468 // If sort key changed, must delete and reinsert to make it relocate
469 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
471 m_listCtrl->DeleteItem(nIndex);
472 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
476 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
477 m_listCtrl->SetItem(nIndex, 2, str2);
478 m_listCtrl->SetItem(nIndex, 3, str3);
479 m_listCtrl->SetItem(nIndex, 4, str4);
480 m_listCtrl->SetItem(nIndex, 5, str5);
481 m_listCtrl->SetItem(nIndex, 6, str6);
482 m_listCtrl->SetItemData(nIndex, nData);
483 SetItemTextColour(m_listCtrl, nIndex, colour);
486 bool CMainFrame::DeleteLine(uint256 hashKey)
488 long nData = *(long*)&hashKey;
492 string strHash = " " + hashKey.ToString();
493 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
494 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
498 m_listCtrl->DeleteItem(nIndex);
503 string FormatTxStatus(const CWalletTx& wtx, bool& fConfirmed)
509 if (wtx.nLockTime < 500000000)
510 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
512 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
516 int nDepth = wtx.GetDepthInMainChain();
517 if (nDepth >= 1 || wtx.GetDebit() > 0)
519 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
520 return strprintf(_("%d/offline?"), nDepth);
522 return strprintf(_("%d/unconfirmed"), nDepth);
524 return strprintf(_("%d confirmations"), nDepth);
528 string FormatTxStatus(const CWalletTx& wtx)
531 return FormatTxStatus(wtx, fConfirmed);
534 string SingleLine(const string& strIn)
537 bool fOneSpace = false;
538 foreach(unsigned char c, strIn)
546 if (fOneSpace && !strOut.empty())
555 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
557 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
558 int64 nCredit = wtx.GetCredit(true);
559 int64 nDebit = wtx.GetDebit();
560 int64 nNet = nCredit - nDebit;
561 uint256 hash = wtx.GetHash();
563 string strStatus = FormatTxStatus(wtx, fConfirmed);
564 wtx.fConfirmedDisplayed = fConfirmed;
565 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
566 map<string, string> mapValue = wtx.mapValue;
567 wtx.nLinesDisplayed = 1;
571 if (wtx.IsCoinBase())
573 // Don't show generated coin until confirmed by at least one block after it
574 // so we don't get the user's hopes up until it looks like it's probably accepted.
576 // It is not an error when generated blocks are not accepted. By design,
577 // some percentage of blocks, like 10% or more, will end up not accepted.
578 // This is the normal mechanism by which the network copes with latency.
580 // We display regular transactions right away before any confirmation
581 // because they can always get into some block eventually. Generated coins
582 // are special because if their block is not accepted, they are not valid.
584 if (wtx.GetDepthInMainChain() < 2)
586 wtx.nLinesDisplayed = 0;
594 // Find the block the tx is in
595 CBlockIndex* pindex = NULL;
596 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
597 if (mi != mapBlockIndex.end())
598 pindex = (*mi).second;
600 // Sort order, unrecorded transactions sort to the top
601 string strSort = strprintf("%010d-%01d-%010u",
602 (pindex ? pindex->nHeight : INT_MAX),
603 (wtx.IsCoinBase() ? 1 : 0),
607 if (nNet > 0 || wtx.IsCoinBase())
612 string strDescription;
613 if (wtx.IsCoinBase())
616 strDescription = _("Generated");
619 int64 nUnmatured = 0;
620 foreach(const CTxOut& txout, wtx.vout)
621 nUnmatured += txout.GetCredit();
622 if (wtx.IsInMainChain())
624 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
626 // Check if the block was requested by anyone
627 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
628 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
632 strDescription = _("Generated (not accepted)");
636 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
638 // Received by IP connection
641 if (!mapValue["from"].empty())
642 strDescription += _("From: ") + mapValue["from"];
643 if (!mapValue["message"].empty())
645 if (!strDescription.empty())
646 strDescription += " - ";
647 strDescription += mapValue["message"];
652 // Received by Bitcoin Address
655 foreach(const CTxOut& txout, wtx.vout)
659 vector<unsigned char> vchPubKey;
660 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
662 CRITICAL_BLOCK(cs_mapAddressBook)
664 //strDescription += _("Received payment to ");
665 //strDescription += _("Received with address ");
666 strDescription += _("Received with: ");
667 string strAddress = PubKeyToAddress(vchPubKey);
668 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
669 if (mi != mapAddressBook.end() && !(*mi).second.empty())
671 string strLabel = (*mi).second;
672 strDescription += strAddress.substr(0,12) + "... ";
673 strDescription += "(" + strLabel + ")";
676 strDescription += strAddress;
684 string strCredit = FormatMoney(nNet, true);
686 strCredit = "[" + strCredit + "]";
688 InsertLine(fNew, nIndex, hash, strSort, colour,
690 nTime ? DateTimeStr(nTime) : "",
691 SingleLine(strDescription),
697 bool fAllFromMe = true;
698 foreach(const CTxIn& txin, wtx.vin)
699 fAllFromMe = fAllFromMe && txin.IsMine();
701 bool fAllToMe = true;
702 foreach(const CTxOut& txout, wtx.vout)
703 fAllToMe = fAllToMe && txout.IsMine();
705 if (fAllFromMe && fAllToMe)
708 int64 nValue = wtx.vout[0].nValue;
709 InsertLine(fNew, nIndex, hash, strSort, colour,
711 nTime ? DateTimeStr(nTime) : "",
712 _("Payment to yourself"),
715 /// issue: can't tell which is the payment and which is the change anymore
716 // FormatMoney(nNet - nValue, true),
717 // FormatMoney(nValue, true));
727 int64 nTxFee = nDebit - wtx.GetValueOut();
728 wtx.nLinesDisplayed = 0;
729 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
731 const CTxOut& txout = wtx.vout[nOut];
736 if (!mapValue["to"].empty())
739 strAddress = mapValue["to"];
743 // Sent to Bitcoin Address
745 if (ExtractHash160(txout.scriptPubKey, hash160))
746 strAddress = Hash160ToAddress(hash160);
749 string strDescription = _("To: ");
750 CRITICAL_BLOCK(cs_mapAddressBook)
751 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
752 strDescription += mapAddressBook[strAddress] + " ";
753 strDescription += strAddress;
754 if (!mapValue["message"].empty())
756 if (!strDescription.empty())
757 strDescription += " - ";
758 strDescription += mapValue["message"];
761 int64 nValue = txout.nValue;
768 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
770 nTime ? DateTimeStr(nTime) : "",
771 SingleLine(strDescription),
772 FormatMoney(-nValue, true),
774 wtx.nLinesDisplayed++;
780 // Mixed debit transaction, can't break down payees
782 bool fAllMine = true;
783 foreach(const CTxOut& txout, wtx.vout)
784 fAllMine = fAllMine && txout.IsMine();
785 foreach(const CTxIn& txin, wtx.vin)
786 fAllMine = fAllMine && txin.IsMine();
788 InsertLine(fNew, nIndex, hash, strSort, colour,
790 nTime ? DateTimeStr(nTime) : "",
792 FormatMoney(nNet, true),
800 void CMainFrame::RefreshListCtrl()
802 fRefreshListCtrl = true;
806 void CMainFrame::OnIdle(wxIdleEvent& event)
808 if (fRefreshListCtrl)
810 // Collect list of wallet transactions and sort newest first
811 bool fEntered = false;
812 vector<pair<unsigned int, uint256> > vSorted;
813 TRY_CRITICAL_BLOCK(cs_mapWallet)
815 printf("RefreshListCtrl starting\n");
817 fRefreshListCtrl = false;
818 vWalletUpdated.clear();
820 // Do the newest transactions first
821 vSorted.reserve(mapWallet.size());
822 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
824 const CWalletTx& wtx = (*it).second;
825 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
826 vSorted.push_back(make_pair(nTime, (*it).first));
828 m_listCtrl->DeleteAllItems();
833 sort(vSorted.begin(), vSorted.end());
836 for (int i = 0; i < vSorted.size();)
840 bool fEntered = false;
841 TRY_CRITICAL_BLOCK(cs_mapWallet)
844 uint256& hash = vSorted[i++].second;
845 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
846 if (mi != mapWallet.end())
847 InsertTransaction((*mi).second, true);
849 if (!fEntered || i == 100 || i % 500 == 0)
853 printf("RefreshListCtrl done\n");
855 // Update transaction total display
860 // Check for time updates
861 static int64 nLastTime;
862 if (GetTime() > nLastTime + 30)
864 TRY_CRITICAL_BLOCK(cs_mapWallet)
866 nLastTime = GetTime();
867 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
869 CWalletTx& wtx = (*it).second;
870 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
871 InsertTransaction(wtx, false);
878 void CMainFrame::RefreshStatusColumn()
881 static CBlockIndex* pindexLastBest;
882 static unsigned int nLastRefreshed;
884 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
885 if (nTop == nLastTop && pindexLastBest == pindexBest)
888 TRY_CRITICAL_BLOCK(cs_mapWallet)
891 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
893 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
895 // If no updates, only need to do the part that moved onto the screen
896 if (nStart >= nLastTop && nStart < nLastTop + 100)
897 nStart = nLastTop + 100;
898 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
902 pindexLastBest = pindexBest;
903 nLastRefreshed = nListViewUpdated;
905 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
907 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
908 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
909 if (mi == mapWallet.end())
911 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
914 CWalletTx& wtx = (*mi).second;
916 string strStatus = FormatTxStatus(wtx, fConfirmed);
917 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed || fConfirmed != wtx.fConfirmedDisplayed)
919 if (!InsertTransaction(wtx, false, nIndex))
920 m_listCtrl->DeleteItem(nIndex--);
924 m_listCtrl->SetItem(nIndex, 2, strStatus);
930 void CMainFrame::OnPaint(wxPaintEvent& event)
941 unsigned int nNeedRepaint = 0;
942 unsigned int nLastRepaint = 0;
943 int64 nLastRepaintTime = 0;
944 int64 nRepaintInterval = 500;
946 void ThreadDelayedRepaint(void* parg)
950 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
952 nLastRepaint = nNeedRepaint;
955 printf("DelayedRepaint\n");
957 pframeMain->fRefresh = true;
958 pframeMain->GetEventHandler()->AddPendingEvent(event);
961 Sleep(nRepaintInterval);
965 void MainFrameRepaint()
967 // This is called by network code that shouldn't access pframeMain
968 // directly because it could still be running after the UI is closed.
971 // Don't repaint too often
972 static int64 nLastRepaintRequest;
973 if (GetTimeMillis() - nLastRepaintRequest < 100)
978 nLastRepaintRequest = GetTimeMillis();
980 printf("MainFrameRepaint\n");
982 pframeMain->fRefresh = true;
983 pframeMain->GetEventHandler()->AddPendingEvent(event);
987 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
989 // Skip lets the listctrl do the paint, we're just hooking the message
993 ptaskbaricon->UpdateTooltip();
998 static int nTransactionCount;
999 bool fPaintedBalance = false;
1000 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
1002 nLastRepaint = nNeedRepaint;
1003 nLastRepaintTime = GetTimeMillis();
1005 // Update listctrl contents
1006 if (!vWalletUpdated.empty())
1008 TRY_CRITICAL_BLOCK(cs_mapWallet)
1011 if (m_listCtrl->GetItemCount())
1012 strTop = (string)m_listCtrl->GetItemText(0);
1013 foreach(uint256 hash, vWalletUpdated)
1015 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1016 if (mi != mapWallet.end())
1017 InsertTransaction((*mi).second, false);
1019 vWalletUpdated.clear();
1020 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1021 m_listCtrl->ScrollList(0, INT_MIN/2);
1026 TRY_CRITICAL_BLOCK(cs_mapWallet)
1028 fPaintedBalance = true;
1029 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
1031 // Count hidden and multi-line transactions
1032 nTransactionCount = 0;
1033 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1035 CWalletTx& wtx = (*it).second;
1036 nTransactionCount += wtx.nLinesDisplayed;
1040 if (!vWalletUpdated.empty() || !fPaintedBalance)
1043 // Update status column of visible items only
1044 RefreshStatusColumn();
1046 // Update status bar
1047 static string strPrevWarning;
1048 string strWarning = GetWarnings("statusbar");
1049 if (strWarning != "")
1050 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1051 else if (strPrevWarning != "")
1052 m_statusBar->SetStatusText("", 0);
1053 strPrevWarning = strWarning;
1056 if (fGenerateBitcoins)
1057 strGen = _(" Generating");
1058 if (fGenerateBitcoins && vNodes.empty())
1059 strGen = _("(not connected)");
1060 m_statusBar->SetStatusText(strGen, 1);
1062 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1063 m_statusBar->SetStatusText(strStatus, 2);
1065 // Update receiving address
1066 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1067 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1068 m_textCtrlAddress->SetValue(strDefaultAddress);
1072 void UIThreadCall(boost::function0<void> fn)
1074 // Call this with a function object created with bind.
1075 // bind needs all parameters to match the function's expected types
1076 // and all default parameters specified. Some examples:
1077 // UIThreadCall(bind(wxBell));
1078 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1079 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1082 wxCommandEvent event(wxEVT_UITHREADCALL);
1083 event.SetClientData((void*)new boost::function0<void>(fn));
1084 pframeMain->GetEventHandler()->AddPendingEvent(event);
1088 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1090 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1095 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1101 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
1103 // Options->Generate Coins
1104 GenerateBitcoins(event.IsChecked());
1107 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1109 event.Check(fGenerateBitcoins);
1112 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1114 // Options->Your Receiving Addresses
1115 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1116 if (!dialog.ShowModal())
1120 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1123 COptionsDialog dialog(this);
1127 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1130 CAboutDialog dialog(this);
1134 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1137 CSendDialog dialog(this);
1141 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1143 // Toolbar: Address Book
1144 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1145 if (dialogAddr.ShowModal() == 2)
1148 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1149 dialogSend.ShowModal();
1153 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1155 // Automatically select-all when entering window
1157 m_textCtrlAddress->SetSelection(-1, -1);
1158 fOnSetFocusAddress = true;
1161 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1164 if (fOnSetFocusAddress)
1165 m_textCtrlAddress->SetSelection(-1, -1);
1166 fOnSetFocusAddress = false;
1169 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1172 CGetTextFromUserDialog dialog(this,
1173 _("New Receiving Address"),
1174 _("You should use a new address for each payment you receive.\n\nLabel"),
1176 if (!dialog.ShowModal())
1178 string strName = dialog.GetValue();
1181 string strAddress = PubKeyToAddress(GenerateNewKey());
1184 SetAddressBookName(strAddress, strName);
1185 SetDefaultReceivingAddress(strAddress);
1188 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1190 // Copy address box to clipboard
1191 if (wxTheClipboard->Open())
1193 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1194 wxTheClipboard->Close();
1198 void CMainFrame::OnListItemActivated(wxListEvent& event)
1200 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1202 CRITICAL_BLOCK(cs_mapWallet)
1204 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1205 if (mi == mapWallet.end())
1207 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1212 CTxDetailsDialog dialog(this, wtx);
1214 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1223 //////////////////////////////////////////////////////////////////////////////
1228 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1230 CRITICAL_BLOCK(cs_mapAddressBook)
1233 strHTML.reserve(4000);
1234 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1236 int64 nTime = wtx.GetTxTime();
1237 int64 nCredit = wtx.GetCredit();
1238 int64 nDebit = wtx.GetDebit();
1239 int64 nNet = nCredit - nDebit;
1243 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1244 int nRequests = wtx.GetRequestCount();
1245 if (nRequests != -1)
1248 strHTML += _(", has not been successfully broadcast yet");
1249 else if (nRequests == 1)
1250 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1252 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1256 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1262 if (wtx.IsCoinBase())
1264 strHTML += _("<b>Source:</b> Generated<br>");
1266 else if (!wtx.mapValue["from"].empty())
1268 // Online transaction
1269 if (!wtx.mapValue["from"].empty())
1270 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1274 // Offline transaction
1278 foreach(const CTxOut& txout, wtx.vout)
1282 vector<unsigned char> vchPubKey;
1283 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1285 string strAddress = PubKeyToAddress(vchPubKey);
1286 if (mapAddressBook.count(strAddress))
1288 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1289 strHTML += _("<b>To:</b> ");
1290 strHTML += HtmlEscape(strAddress);
1291 if (!mapAddressBook[strAddress].empty())
1292 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1294 strHTML += _(" (yours)");
1309 if (!wtx.mapValue["to"].empty())
1311 // Online transaction
1312 strAddress = wtx.mapValue["to"];
1313 strHTML += _("<b>To:</b> ");
1314 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1315 strHTML += mapAddressBook[strAddress] + " ";
1316 strHTML += HtmlEscape(strAddress) + "<br>";
1323 if (wtx.IsCoinBase() && nCredit == 0)
1328 int64 nUnmatured = 0;
1329 foreach(const CTxOut& txout, wtx.vout)
1330 nUnmatured += txout.GetCredit();
1331 strHTML += _("<b>Credit:</b> ");
1332 if (wtx.IsInMainChain())
1333 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1335 strHTML += _("(not accepted)");
1343 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1347 bool fAllFromMe = true;
1348 foreach(const CTxIn& txin, wtx.vin)
1349 fAllFromMe = fAllFromMe && txin.IsMine();
1351 bool fAllToMe = true;
1352 foreach(const CTxOut& txout, wtx.vout)
1353 fAllToMe = fAllToMe && txout.IsMine();
1360 foreach(const CTxOut& txout, wtx.vout)
1365 if (wtx.mapValue["to"].empty())
1367 // Offline transaction
1369 if (ExtractHash160(txout.scriptPubKey, hash160))
1371 string strAddress = Hash160ToAddress(hash160);
1372 strHTML += _("<b>To:</b> ");
1373 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1374 strHTML += mapAddressBook[strAddress] + " ";
1375 strHTML += strAddress;
1380 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1386 /// issue: can't tell which is the payment and which is the change anymore
1387 //int64 nValue = wtx.vout[0].nValue;
1388 //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1389 //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1392 int64 nTxFee = nDebit - wtx.GetValueOut();
1394 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1399 // Mixed debit transaction
1401 foreach(const CTxIn& txin, wtx.vin)
1403 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1404 foreach(const CTxOut& txout, wtx.vout)
1406 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1410 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1416 if (!wtx.mapValue["message"].empty())
1417 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1419 if (wtx.IsCoinBase())
1420 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>";
1428 strHTML += "<hr><br>debug print<br><br>";
1429 foreach(const CTxIn& txin, wtx.vin)
1431 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1432 foreach(const CTxOut& txout, wtx.vout)
1434 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1436 strHTML += "<b>Inputs:</b><br>";
1437 CRITICAL_BLOCK(cs_mapWallet)
1439 foreach(const CTxIn& txin, wtx.vin)
1441 COutPoint prevout = txin.prevout;
1442 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1443 if (mi != mapWallet.end())
1445 const CWalletTx& prev = (*mi).second;
1446 if (prevout.n < prev.vout.size())
1448 strHTML += HtmlEscape(prev.ToString(), true);
1449 strHTML += " " + FormatTxStatus(prev) + ", ";
1450 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1456 strHTML += "<br><hr><br><b>Transaction:</b><br>";
1457 strHTML += HtmlEscape(wtx.ToString(), true);
1462 strHTML += "</font></html>";
1463 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1464 m_htmlWin->SetPage(strHTML);
1465 m_buttonOK->SetFocus();
1469 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1480 //////////////////////////////////////////////////////////////////////////////
1486 string StartupShortcutPath()
1488 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1491 bool GetStartOnSystemStartup()
1493 return filesystem::exists(StartupShortcutPath().c_str());
1496 void SetStartOnSystemStartup(bool fAutoStart)
1498 // If the shortcut exists already, remove it for updating
1499 remove(StartupShortcutPath().c_str());
1505 // Get a pointer to the IShellLink interface.
1506 IShellLink* psl = NULL;
1507 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1508 CLSCTX_INPROC_SERVER, IID_IShellLink,
1509 reinterpret_cast<void**>(&psl));
1511 if (SUCCEEDED(hres))
1513 // Get the current executable path
1514 TCHAR pszExePath[MAX_PATH];
1515 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1517 // Set the path to the shortcut target
1518 psl->SetPath(pszExePath);
1519 PathRemoveFileSpec(pszExePath);
1520 psl->SetWorkingDirectory(pszExePath);
1521 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1523 // Query IShellLink for the IPersistFile interface for
1524 // saving the shortcut in persistent storage.
1525 IPersistFile* ppf = NULL;
1526 hres = psl->QueryInterface(IID_IPersistFile,
1527 reinterpret_cast<void**>(&ppf));
1528 if (SUCCEEDED(hres))
1530 WCHAR pwsz[MAX_PATH];
1531 // Ensure that the string is ANSI.
1532 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1533 // Save the link by calling IPersistFile::Save.
1534 hres = ppf->Save(pwsz, TRUE);
1543 #elif defined(__WXGTK__)
1545 // Follow the Desktop Application Autostart Spec:
1546 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1548 boost::filesystem::path GetAutostartDir()
1550 namespace fs = boost::filesystem;
1552 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1553 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1554 char* pszHome = getenv("HOME");
1555 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1559 boost::filesystem::path GetAutostartFilePath()
1561 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1564 bool GetStartOnSystemStartup()
1566 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1567 if (!optionFile.good())
1569 // Scan through file for "Hidden=true":
1571 while (!optionFile.eof())
1573 getline(optionFile, line);
1574 if (line.find("Hidden") != string::npos &&
1575 line.find("true") != string::npos)
1583 void SetStartOnSystemStartup(bool fAutoStart)
1587 unlink(GetAutostartFilePath().native_file_string().c_str());
1591 char pszExePath[MAX_PATH+1];
1592 memset(pszExePath, 0, sizeof(pszExePath));
1593 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1596 boost::filesystem::create_directories(GetAutostartDir());
1598 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1599 if (!optionFile.good())
1601 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1604 // Write a bitcoin.desktop file to the autostart directory:
1605 optionFile << "[Desktop Entry]\n";
1606 optionFile << "Type=Application\n";
1607 optionFile << "Name=Bitcoin\n";
1608 optionFile << "Exec=" << pszExePath << "\n";
1609 optionFile << "Terminal=false\n";
1610 optionFile << "Hidden=false\n";
1616 // TODO: OSX startup stuff; see:
1617 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1619 bool GetStartOnSystemStartup() { return false; }
1620 void SetStartOnSystemStartup(bool fAutoStart) { }
1629 //////////////////////////////////////////////////////////////////////////////
1634 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1636 // Set up list box of page choices
1637 m_listBox->Append(_("Main"));
1638 //m_listBox->Append(_("Test 2"));
1639 m_listBox->SetSelection(0);
1641 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1642 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1643 if (!mapArgs.count("-minimizetotray"))
1645 // Minimize to tray is just too buggy on Linux
1646 fMinimizeToTray = false;
1647 m_checkBoxMinimizeToTray->SetValue(false);
1648 m_checkBoxMinimizeToTray->Enable(false);
1649 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1652 #ifdef __WXMAC_OSX__
1653 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1657 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1658 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
1659 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
1660 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
1661 int nProcessors = wxThread::GetCPUCount();
1662 if (nProcessors < 1)
1664 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
1665 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1666 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1667 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1668 m_checkBoxUseProxy->SetValue(fUseProxy);
1669 m_textCtrlProxyIP->Enable(fUseProxy);
1670 m_textCtrlProxyPort->Enable(fUseProxy);
1671 m_staticTextProxyIP->Enable(fUseProxy);
1672 m_staticTextProxyPort->Enable(fUseProxy);
1673 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1674 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1676 m_buttonOK->SetFocus();
1679 void COptionsDialog::SelectPage(int nPage)
1681 m_panelMain->Show(nPage == 0);
1682 m_panelTest2->Show(nPage == 1);
1684 m_scrolledWindow->Layout();
1685 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1688 void COptionsDialog::OnListBox(wxCommandEvent& event)
1690 SelectPage(event.GetSelection());
1693 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1696 int64 nTmp = nTransactionFee;
1697 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1698 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1701 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
1703 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
1706 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1708 m_textCtrlProxyIP->Enable(event.IsChecked());
1709 m_textCtrlProxyPort->Enable(event.IsChecked());
1710 m_staticTextProxyIP->Enable(event.IsChecked());
1711 m_staticTextProxyPort->Enable(event.IsChecked());
1714 CAddress COptionsDialog::GetProxyAddr()
1716 // Be careful about byte order, addr.ip and addr.port are big endian
1717 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1718 if (addr.ip == INADDR_NONE)
1719 addr.ip = addrProxy.ip;
1720 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1721 addr.port = htons(nPort);
1722 if (nPort <= 0 || nPort > USHRT_MAX)
1723 addr.port = addrProxy.port;
1727 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1730 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1731 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1735 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1737 OnButtonApply(event);
1741 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1746 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1750 int64 nPrevTransactionFee = nTransactionFee;
1751 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1752 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1754 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
1755 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
1757 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
1758 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
1760 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
1762 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
1763 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
1765 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
1766 GenerateBitcoins(fGenerateBitcoins);
1768 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1770 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1771 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1774 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1776 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1777 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1778 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1781 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1783 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1784 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1787 fUseProxy = m_checkBoxUseProxy->GetValue();
1788 walletdb.WriteSetting("fUseProxy", fUseProxy);
1790 addrProxy = GetProxyAddr();
1791 walletdb.WriteSetting("addrProxy", addrProxy);
1799 //////////////////////////////////////////////////////////////////////////////
1804 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1806 m_staticTextVersion->SetLabel(strprintf(_("version %s%s beta"), FormatVersion(VERSION).c_str(), pszSubVer));
1808 // Change (c) into UTF-8 or ANSI copyright symbol
1809 wxString str = m_staticTextMain->GetLabel();
1811 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1813 str.Replace("(c)", "\xA9");
1815 m_staticTextMain->SetLabel(str);
1817 // Resize on Linux to make the window fit the text.
1818 // The text was wrapped manually rather than using the Wrap setting because
1819 // the wrap would be too small on Linux and it can't be changed at this point.
1820 wxFont fontTmp = m_staticTextMain->GetFont();
1821 if (fontTmp.GetPointSize() > 8);
1822 fontTmp.SetPointSize(8);
1823 m_staticTextMain->SetFont(fontTmp);
1824 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1828 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1838 //////////////////////////////////////////////////////////////////////////////
1843 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1846 m_textCtrlAddress->SetValue(strAddress);
1847 m_choiceTransferType->SetSelection(0);
1848 m_bitmapCheckMark->Show(false);
1849 fEnabledPrev = true;
1850 m_textCtrlAddress->SetFocus();
1851 //// todo: should add a display of your balance for convenience
1853 wxFont fontTmp = m_staticTextInstructions->GetFont();
1854 if (fontTmp.GetPointSize() > 9);
1855 fontTmp.SetPointSize(9);
1856 m_staticTextInstructions->SetFont(fontTmp);
1862 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1865 wxCommandEvent event;
1866 OnTextAddress(event);
1868 // Fixup the tab order
1869 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1870 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1874 void CSendDialog::OnTextAddress(wxCommandEvent& event)
1878 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
1879 m_bitmapCheckMark->Show(fBitcoinAddress);
1881 // Grey out message if bitcoin address
1882 bool fEnable = !fBitcoinAddress;
1883 m_staticTextFrom->Enable(fEnable);
1884 m_textCtrlFrom->Enable(fEnable);
1885 m_staticTextMessage->Enable(fEnable);
1886 m_textCtrlMessage->Enable(fEnable);
1887 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
1888 if (!fEnable && fEnabledPrev)
1890 strFromSave = m_textCtrlFrom->GetValue();
1891 strMessageSave = m_textCtrlMessage->GetValue();
1892 m_textCtrlFrom->SetValue(_("n/a"));
1893 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
1895 else if (fEnable && !fEnabledPrev)
1897 m_textCtrlFrom->SetValue(strFromSave);
1898 m_textCtrlMessage->SetValue(strMessageSave);
1900 fEnabledPrev = fEnable;
1903 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1905 // Reformat the amount
1907 if (m_textCtrlAmount->GetValue().Trim().empty())
1910 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1911 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1914 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1916 // Open address book
1917 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1918 if (dialog.ShowModal())
1919 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1922 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1924 // Copy clipboard to address box
1925 if (wxTheClipboard->Open())
1927 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1929 wxTextDataObject data;
1930 wxTheClipboard->GetData(data);
1931 m_textCtrlAddress->SetValue(data.GetText());
1933 wxTheClipboard->Close();
1937 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1940 string strAddress = (string)m_textCtrlAddress->GetValue();
1944 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1946 wxMessageBox(_("Error in amount "), _("Send Coins"));
1949 if (nValue > GetBalance())
1951 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1954 if (nValue + nTransactionFee > GetBalance())
1956 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1960 // Parse bitcoin address
1962 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1964 if (fBitcoinAddress)
1966 // Send to bitcoin address
1967 CScript scriptPubKey;
1968 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1970 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1972 wxMessageBox(_("Payment sent "), _("Sending..."));
1973 else if (strError != "ABORTED")
1974 wxMessageBox(strError + " ", _("Sending..."));
1979 CAddress addr(strAddress);
1980 if (!addr.IsValid())
1982 wxMessageBox(_("Invalid address "), _("Send Coins"));
1987 wtx.mapValue["to"] = strAddress;
1988 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
1989 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
1991 // Send to IP address
1992 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1993 if (!pdialog->ShowModal())
1997 CRITICAL_BLOCK(cs_mapAddressBook)
1998 if (!mapAddressBook.count(strAddress))
1999 SetAddressBookName(strAddress, "");
2004 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2015 //////////////////////////////////////////////////////////////////////////////
2020 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
2025 start = wxDateTime::UNow();
2026 memset(pszStatus, 0, sizeof(pszStatus));
2033 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2036 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2037 m_textCtrlStatus->SetValue("");
2039 CreateThread(SendingDialogStartTransfer, this);
2042 CSendingDialog::~CSendingDialog()
2044 printf("~CSendingDialog()\n");
2047 void CSendingDialog::Close()
2049 // Last one out turn out the lights.
2050 // fWorkDone signals that work side is done and UI thread should call destroy.
2051 // fUIDone signals that UI window has closed and work thread should call destroy.
2052 // This allows the window to disappear and end modality when cancelled
2053 // without making the user wait for ConnectNode to return. The dialog object
2054 // hangs around in the background until the work thread exits.
2065 void CSendingDialog::OnClose(wxCloseEvent& event)
2067 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2074 wxCommandEvent cmdevent;
2075 OnButtonCancel(cmdevent);
2079 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2085 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2091 void CSendingDialog::OnPaint(wxPaintEvent& event)
2094 if (strlen(pszStatus) > 130)
2095 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2097 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2098 m_staticTextSending->SetFocus();
2100 m_buttonCancel->Enable(false);
2103 m_buttonOK->Enable(true);
2104 m_buttonOK->SetFocus();
2105 m_buttonCancel->Enable(false);
2107 if (fAbort && fCanCancel && IsShown())
2109 strcpy(pszStatus, _("CANCELLED"));
2110 m_buttonOK->Enable(true);
2111 m_buttonOK->SetFocus();
2112 m_buttonCancel->Enable(false);
2113 m_buttonCancel->SetLabel(_("Cancelled"));
2115 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2121 // Everything from here on is not in the UI thread and must only communicate
2122 // with the rest of the dialog through variables and calling repaint.
2125 void CSendingDialog::Repaint()
2129 GetEventHandler()->AddPendingEvent(event);
2132 bool CSendingDialog::Status()
2139 if (fAbort && fCanCancel)
2141 memset(pszStatus, 0, 10);
2142 strcpy(pszStatus, _("CANCELLED"));
2150 bool CSendingDialog::Status(const string& str)
2155 // This can be read by the UI thread at any time,
2156 // so copy in a way that can be read cleanly at all times.
2157 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2158 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2164 bool CSendingDialog::Error(const string& str)
2168 Status(string(_("Error: ")) + str);
2172 void SendingDialogStartTransfer(void* parg)
2174 ((CSendingDialog*)parg)->StartTransfer();
2177 void CSendingDialog::StartTransfer()
2179 // Make sure we have enough money
2180 if (nPrice + nTransactionFee > GetBalance())
2182 Error(_("Insufficient funds"));
2186 // We may have connected already for product details
2187 if (!Status(_("Connecting...")))
2189 CNode* pnode = ConnectNode(addr, 15 * 60);
2192 Error(_("Unable to connect"));
2196 // Send order to seller, with response going to OnReply2 via event handler
2197 if (!Status(_("Requesting public key...")))
2199 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2202 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2204 ((CSendingDialog*)parg)->OnReply2(vRecv);
2207 void CSendingDialog::OnReply2(CDataStream& vRecv)
2209 if (!Status(_("Received public key...")))
2212 CScript scriptPubKey;
2221 vRecv >> strMessage;
2223 Error(_("Recipient is not accepting transactions sent by IP address"));
2225 Error(_("Transfer was not accepted"));
2226 //// todo: enlarge the window and enable a hidden white box to put seller's message
2229 vRecv >> scriptPubKey;
2233 //// what do we want to do about this?
2234 Error(_("Invalid response received"));
2238 // Pause to give the user a chance to cancel
2239 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2246 CRITICAL_BLOCK(cs_main)
2249 if (!Status(_("Creating transaction...")))
2251 if (nPrice + nTransactionFee > GetBalance())
2253 Error(_("Insufficient funds"));
2258 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
2260 if (nPrice + nFeeRequired > GetBalance())
2261 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
2263 Error(_("Transaction creation failed"));
2268 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2270 Error(_("Transaction aborted"));
2274 // Make sure we're still connected
2275 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2278 Error(_("Lost connection, transaction cancelled"));
2282 // Last chance to cancel
2294 if (!Status(_("Sending payment...")))
2298 if (!CommitTransaction(wtx, key))
2300 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."));
2304 // Send payment tx to seller, with response going to OnReply3 via event handler
2305 CWalletTx wtxSend = wtx;
2306 wtxSend.fFromMe = false;
2307 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2309 Status(_("Waiting for confirmation..."));
2314 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2316 ((CSendingDialog*)parg)->OnReply3(vRecv);
2319 void CSendingDialog::OnReply3(CDataStream& vRecv)
2327 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2328 "The transaction is recorded and will credit to the recipient,\n"
2329 "but the comment information will be blank."));
2335 //// what do we want to do about this?
2336 Error(_("Payment was sent, but an invalid response was received"));
2342 Status(_("Payment completed"));
2350 //////////////////////////////////////////////////////////////////////////////
2352 // CAddressBookDialog
2355 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2357 // Set initially selected page
2358 wxNotebookEvent event;
2359 event.SetSelection(nPageIn);
2360 OnNotebookPageChanged(event);
2361 m_notebook->ChangeSelection(nPageIn);
2363 fDuringSend = fDuringSendIn;
2365 m_buttonCancel->Show(false);
2368 wxIcon iconAddressBook;
2369 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2370 SetIcon(iconAddressBook);
2372 // Init column headers
2373 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2374 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2375 m_listCtrlSending->SetFocus();
2376 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2377 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2378 m_listCtrlReceiving->SetFocus();
2380 // Fill listctrl with address book data
2381 CRITICAL_BLOCK(cs_mapKeys)
2382 CRITICAL_BLOCK(cs_mapAddressBook)
2384 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2385 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2387 string strAddress = item.first;
2388 string strName = item.second;
2390 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2391 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2392 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2393 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2394 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2399 wxString CAddressBookDialog::GetSelectedAddress()
2401 int nIndex = GetSelection(m_listCtrl);
2404 return GetItemText(m_listCtrl, nIndex, 1);
2407 wxString CAddressBookDialog::GetSelectedSendingAddress()
2409 int nIndex = GetSelection(m_listCtrlSending);
2412 return GetItemText(m_listCtrlSending, nIndex, 1);
2415 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2417 int nIndex = GetSelection(m_listCtrlReceiving);
2420 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2423 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2426 nPage = event.GetSelection();
2427 if (nPage == SENDING)
2428 m_listCtrl = m_listCtrlSending;
2429 else if (nPage == RECEIVING)
2430 m_listCtrl = m_listCtrlReceiving;
2431 m_buttonDelete->Show(nPage == SENDING);
2432 m_buttonCopy->Show(nPage == RECEIVING);
2434 m_listCtrl->SetFocus();
2437 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2439 // Update address book with edited name
2441 if (event.IsEditCancelled())
2443 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2444 SetAddressBookName(strAddress, string(event.GetText()));
2445 pframeMain->RefreshListCtrl();
2448 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2451 if (nPage == RECEIVING)
2452 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2455 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2460 // Doubleclick returns selection
2461 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2465 // Doubleclick edits item
2466 wxCommandEvent event2;
2467 OnButtonEdit(event2);
2470 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2472 if (nPage != SENDING)
2474 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2476 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2478 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2479 CWalletDB().EraseName(strAddress);
2480 m_listCtrl->DeleteItem(nIndex);
2483 pframeMain->RefreshListCtrl();
2486 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2488 // Copy address box to clipboard
2489 if (wxTheClipboard->Open())
2491 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2492 wxTheClipboard->Close();
2496 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2499 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2501 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2505 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2507 int nIndex = GetSelection(m_listCtrl);
2510 string strName = (string)m_listCtrl->GetItemText(nIndex);
2511 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2512 string strAddressOrg = strAddress;
2514 if (nPage == SENDING)
2516 // Ask name and address
2519 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2520 if (!dialog.ShowModal())
2522 strName = dialog.GetValue1();
2523 strAddress = dialog.GetValue2();
2525 while (CheckIfMine(strAddress, _("Edit Address")));
2528 else if (nPage == RECEIVING)
2531 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2532 if (!dialog.ShowModal())
2534 strName = dialog.GetValue();
2538 if (strAddress != strAddressOrg)
2539 CWalletDB().EraseName(strAddressOrg);
2540 SetAddressBookName(strAddress, strName);
2541 m_listCtrl->SetItem(nIndex, 1, strAddress);
2542 m_listCtrl->SetItemText(nIndex, strName);
2543 pframeMain->RefreshListCtrl();
2546 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2551 if (nPage == SENDING)
2553 // Ask name and address
2556 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2557 if (!dialog.ShowModal())
2559 strName = dialog.GetValue1();
2560 strAddress = dialog.GetValue2();
2562 while (CheckIfMine(strAddress, _("Add Address")));
2564 else if (nPage == RECEIVING)
2567 CGetTextFromUserDialog dialog(this,
2568 _("New Receiving Address"),
2569 _("You should use a new address for each payment you receive.\n\nLabel"),
2571 if (!dialog.ShowModal())
2573 strName = dialog.GetValue();
2576 strAddress = PubKeyToAddress(GenerateNewKey());
2579 // Add to list and select it
2580 SetAddressBookName(strAddress, strName);
2581 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2582 SetSelection(m_listCtrl, nIndex);
2583 m_listCtrl->SetFocus();
2584 if (nPage == SENDING)
2585 pframeMain->RefreshListCtrl();
2588 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2591 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2594 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2600 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2611 //////////////////////////////////////////////////////////////////////////////
2618 ID_TASKBAR_RESTORE = 10001,
2620 ID_TASKBAR_GENERATE,
2624 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2625 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2626 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2627 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2628 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
2629 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2630 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2633 void CMyTaskBarIcon::Show(bool fShow)
2635 static char pszPrevTip[200];
2638 string strTooltip = _("Bitcoin");
2639 if (fGenerateBitcoins)
2640 strTooltip = _("Bitcoin - Generating");
2641 if (fGenerateBitcoins && vNodes.empty())
2642 strTooltip = _("Bitcoin - (not connected)");
2644 // Optimization, only update when changed, using char array to be reentrant
2645 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2647 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2649 // somehow it'll choose the wrong size and scale it down if
2650 // we use the main icon, so we hand it one with only 16x16
2651 SetIcon(wxICON(favicon), strTooltip);
2653 SetIcon(bitcoin80_xpm, strTooltip);
2659 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2664 void CMyTaskBarIcon::Hide()
2669 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2674 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2679 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2681 // Since it's modal, get the main window to do it
2682 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2683 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2686 void CMyTaskBarIcon::Restore()
2689 wxIconizeEvent event(0, false);
2690 pframeMain->GetEventHandler()->AddPendingEvent(event);
2691 pframeMain->Iconize(false);
2692 pframeMain->Raise();
2695 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
2697 GenerateBitcoins(event.IsChecked());
2700 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2702 event.Check(fGenerateBitcoins);
2705 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2707 pframeMain->Close(true);
2710 void CMyTaskBarIcon::UpdateTooltip()
2712 if (IsIconInstalled())
2716 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2718 wxMenu* pmenu = new wxMenu;
2719 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2720 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2721 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
2722 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2723 pmenu->AppendSeparator();
2724 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2734 //////////////////////////////////////////////////////////////////////////////
2739 void CreateMainWindow()
2741 pframeMain = new CMainFrame(NULL);
2742 if (mapArgs.count("-min"))
2743 pframeMain->Iconize(true);
2744 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2745 if (!mapArgs.count("-minimizetotray"))
2746 fMinimizeToTray = false;
2748 pframeMain->Show(true); // have to show first to get taskbar button to hide
2749 if (fMinimizeToTray && pframeMain->IsIconized())
2750 fClosedToTray = true;
2751 pframeMain->Show(!fClosedToTray);
2752 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2753 CreateThread(ThreadDelayedRepaint, NULL);
2757 // Define a new application
2758 class CMyApp : public wxApp
2767 // Hook Initialize so we can start without GUI
2768 virtual bool Initialize(int& argc, wxChar** argv);
2770 // 2nd-level exception handling: we get all the exceptions occurring in any
2771 // event handler here
2772 virtual bool OnExceptionInMainLoop();
2774 // 3rd, and final, level exception handling: whenever an unhandled
2775 // exception is caught, this function is called
2776 virtual void OnUnhandledException();
2778 // and now for something different: this function is called in case of a
2779 // crash (e.g. dereferencing null pointer, division by 0, ...)
2780 virtual void OnFatalException();
2783 IMPLEMENT_APP(CMyApp)
2785 bool CMyApp::Initialize(int& argc, wxChar** argv)
2787 for (int i = 1; i < argc; i++)
2788 if (!IsSwitchChar(argv[i][0]))
2789 fCommandLine = true;
2793 // wxApp::Initialize will remove environment-specific parameters,
2794 // so it's too early to call ParseParameters yet
2795 for (int i = 1; i < argc; i++)
2797 wxString str = argv[i];
2799 if (str.size() >= 1 && str[0] == '/')
2801 char pszLower[MAX_PATH];
2802 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2806 if (str == "-daemon")
2812 if (fDaemon || fCommandLine)
2814 // Call the original Initialize while suppressing error messages
2815 // and ignoring failure. If unable to initialize GTK, it fails
2816 // near the end so hopefully the last few things don't matter.
2819 wxApp::Initialize(argc, argv);
2828 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2832 pthread_exit((void*)0);
2839 return wxApp::Initialize(argc, argv);
2842 bool CMyApp::OnInit()
2844 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2845 // Disable malfunctioning wxWidgets debug assertion
2846 extern int g_isPainting;
2847 g_isPainting = 10000;
2850 wxImage::AddHandler(new wxPNGHandler);
2852 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2853 SetAppName("Bitcoin");
2855 SetAppName("bitcoin");
2859 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2860 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2861 class wxMBConv_win32 : public wxMBConv
2865 size_t m_minMBCharWidth;
2867 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2868 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2872 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2873 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2874 g_locale.AddCatalogLookupPathPrefix("locale");
2876 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2877 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2879 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2880 g_locale.AddCatalog("bitcoin");
2882 return AppInit(argc, argv);
2885 int CMyApp::OnExit()
2888 return wxApp::OnExit();
2891 bool CMyApp::OnExceptionInMainLoop()
2897 catch (std::exception& e)
2899 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2900 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2906 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2907 wxLogWarning("Unknown exception");
2914 void CMyApp::OnUnhandledException()
2916 // this shows how we may let some exception propagate uncaught
2921 catch (std::exception& e)
2923 PrintException(&e, "CMyApp::OnUnhandledException()");
2924 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2930 PrintException(NULL, "CMyApp::OnUnhandledException()");
2931 wxLogWarning("Unknown exception");
2937 void CMyApp::OnFatalException()
2939 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);