1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
12 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
14 CMainFrame* pframeMain = NULL;
15 CMyTaskBarIcon* ptaskbaricon = NULL;
16 bool fClosedToTray = false;
27 //////////////////////////////////////////////////////////////////////////////
32 void HandleCtrlA(wxKeyEvent& event)
36 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
37 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
38 textCtrl->SetSelection(-1, -1);
43 //char pszHourFormat[256];
44 //pszHourFormat[0] = '\0';
45 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
46 //return (pszHourFormat[0] != '0');
50 string DateStr(int64 nTime)
52 // Can only be used safely here in the UI
53 return (string)wxDateTime((time_t)nTime).FormatDate();
56 string DateTimeStr(int64 nTime)
58 // Can only be used safely here in the UI
59 wxDateTime datetime((time_t)nTime);
61 return (string)datetime.Format("%x %H:%M");
63 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
66 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
68 // Helper to simplify access to listctrl
70 item.m_itemId = nIndex;
72 item.m_mask = wxLIST_MASK_TEXT;
73 if (!listCtrl->GetItem(item))
75 return item.GetText();
78 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
80 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
81 listCtrl->SetItem(nIndex, 1, str1);
85 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
87 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
88 listCtrl->SetItem(nIndex, 1, str1);
89 listCtrl->SetItem(nIndex, 2, str2);
90 listCtrl->SetItem(nIndex, 3, str3);
91 listCtrl->SetItem(nIndex, 4, str4);
95 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
97 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
98 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
99 listCtrl->SetItem(nIndex, 1, str1);
100 listCtrl->SetItem(nIndex, 2, str2);
101 listCtrl->SetItem(nIndex, 3, str3);
102 listCtrl->SetItem(nIndex, 4, str4);
106 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
108 // Repaint on Windows is more flickery if the colour has ever been set,
109 // so don't want to set it unless it's different. Default colour has
110 // alpha 0 transparent, so our colours don't match using operator==.
111 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
113 c1 = wxColour(0,0,0);
114 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
115 listCtrl->SetItemTextColour(nIndex, colour);
118 void SetSelection(wxListCtrl* listCtrl, int nIndex)
120 int nSize = listCtrl->GetItemCount();
121 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
122 for (int i = 0; i < nSize; i++)
123 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
126 int GetSelection(wxListCtrl* listCtrl)
128 int nSize = listCtrl->GetItemCount();
129 for (int i = 0; i < nSize; i++)
130 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
135 string HtmlEscape(const char* psz, bool fMultiLine=false)
138 for (const char* p = psz; *p; p++)
140 if (*p == '<') len += 4;
141 else if (*p == '>') len += 4;
142 else if (*p == '&') len += 5;
143 else if (*p == '"') len += 6;
144 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
145 else if (*p == '\n' && fMultiLine) len += 5;
151 for (const char* p = psz; *p; p++)
153 if (*p == '<') str += "<";
154 else if (*p == '>') str += ">";
155 else if (*p == '&') str += "&";
156 else if (*p == '"') str += """;
157 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
158 else if (*p == '\n' && fMultiLine) str += "<br>\n";
165 string HtmlEscape(const string& str, bool fMultiLine=false)
167 return HtmlEscape(str.c_str(), fMultiLine);
170 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
172 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
176 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
179 return wxMessageBox(message, caption, style, parent, x, y);
181 if (wxThread::IsMain() || fDaemon)
183 return wxMessageBox(message, caption, style, parent, x, y);
189 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
197 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
199 if (nFeeRequired < CENT || nFeeRequired <= nTransactionFee || fDaemon)
201 string strMessage = strprintf(
202 _("This transaction is over the size limit. You can still send it for a fee of %s, "
203 "which goes to the nodes that process your transaction and helps to support the network. "
204 "Do you want to pay the fee?"),
205 FormatMoney(nFeeRequired).c_str());
206 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
209 void CalledSetStatusBar(const string& strText, int nField)
211 if (nField == 0 && GetWarnings("statusbar") != "")
213 if (pframeMain && pframeMain->m_statusBar)
214 pframeMain->m_statusBar->SetStatusText(strText, nField);
217 void SetDefaultReceivingAddress(const string& strAddress)
219 // Update main window address and database
220 if (pframeMain == NULL)
222 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
225 if (!AddressToHash160(strAddress, hash160))
227 if (!mapPubKeys.count(hash160))
229 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
230 pframeMain->m_textCtrlAddress->SetValue(strAddress);
243 //////////////////////////////////////////////////////////////////////////////
248 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
250 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
252 // Set initially selected page
253 wxNotebookEvent event;
254 event.SetSelection(0);
255 OnNotebookPageChanged(event);
256 m_notebook->ChangeSelection(0);
259 fRefreshListCtrl = false;
260 fRefreshListCtrlRunning = false;
261 fOnSetFocusAddress = false;
263 m_choiceFilter->SetSelection(0);
264 double dResize = 1.0;
266 SetIcon(wxICON(bitcoin));
268 SetIcon(bitcoin80_xpm);
269 SetBackgroundColour(m_toolBar->GetBackgroundColour());
270 wxFont fontTmp = m_staticText41->GetFont();
271 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
272 m_staticTextBalance->SetFont(fontTmp);
273 m_staticTextBalance->SetSize(140, 17);
274 // resize to fit ubuntu's huge default font
276 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
278 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
279 m_listCtrl->SetFocus();
280 ptaskbaricon = new CMyTaskBarIcon();
282 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
283 // to their standard places, leaving these menus empty.
284 GetMenuBar()->Remove(2); // remove Help menu
285 GetMenuBar()->Remove(0); // remove File menu
288 // Init column headers
289 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
290 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
296 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
297 foreach(wxListCtrl* p, pplistCtrl)
299 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
300 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
301 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
302 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
303 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
304 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
305 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
309 int pnWidths[3] = { -100, 88, 300 };
311 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
312 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
314 m_statusBar->SetFieldsCount(3, pnWidths);
316 // Fill your address text box
317 vector<unsigned char> vchPubKey;
318 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
319 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
321 // Fill listctrl with wallet transactions
325 CMainFrame::~CMainFrame()
332 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
335 nPage = event.GetSelection();
338 m_listCtrl = m_listCtrlAll;
339 fShowGenerated = true;
341 fShowReceived = true;
343 else if (nPage == SENTRECEIVED)
345 m_listCtrl = m_listCtrlSentReceived;
346 fShowGenerated = false;
348 fShowReceived = true;
350 else if (nPage == SENT)
352 m_listCtrl = m_listCtrlSent;
353 fShowGenerated = false;
355 fShowReceived = false;
357 else if (nPage == RECEIVED)
359 m_listCtrl = m_listCtrlReceived;
360 fShowGenerated = false;
362 fShowReceived = true;
365 m_listCtrl->SetFocus();
368 void CMainFrame::OnClose(wxCloseEvent& event)
370 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
372 // Divert close to minimize
374 fClosedToTray = true;
380 CreateThread(Shutdown, NULL);
384 void CMainFrame::OnIconize(wxIconizeEvent& event)
387 // Hide the task bar button when minimized.
388 // Event is sent when the frame is minimized or restored.
389 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
390 // to get rid of the deprecated warning. Just ignore it.
391 if (!event.Iconized())
392 fClosedToTray = false;
393 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
394 if (GetBoolArg("-minimizetotray")) {
396 // The tray icon sometimes disappears on ubuntu karmic
397 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
398 // Reports of CPU peg on 64-bit linux
399 if (fMinimizeToTray && event.Iconized())
400 fClosedToTray = true;
401 Show(!fClosedToTray);
402 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
403 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
408 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
412 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
413 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
416 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
418 // Hidden columns not resizeable
419 if (event.GetColumn() <= 1 && !fDebug)
425 int CMainFrame::GetSortIndex(const string& strSort)
430 // The wx generic listctrl implementation used on GTK doesn't sort,
431 // so we have to do it ourselves. Remember, we sort in reverse order.
432 // In the wx generic implementation, they store the list of items
433 // in a vector, so indexed lookups are fast, but inserts are slower
434 // the closer they are to the top.
436 int high = m_listCtrl->GetItemCount();
439 int mid = low + ((high - low) / 2);
440 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
449 void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxColour& colour, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6)
451 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
452 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
455 if (!fNew && nIndex == -1)
457 string strHash = " " + hashKey.ToString();
458 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
459 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
463 // fNew is for blind insert, only use if you're sure it's new
464 if (fNew || nIndex == -1)
466 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
470 // If sort key changed, must delete and reinsert to make it relocate
471 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
473 m_listCtrl->DeleteItem(nIndex);
474 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
478 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
479 m_listCtrl->SetItem(nIndex, 2, str2);
480 m_listCtrl->SetItem(nIndex, 3, str3);
481 m_listCtrl->SetItem(nIndex, 4, str4);
482 m_listCtrl->SetItem(nIndex, 5, str5);
483 m_listCtrl->SetItem(nIndex, 6, str6);
484 m_listCtrl->SetItemData(nIndex, nData);
485 SetItemTextColour(m_listCtrl, nIndex, colour);
488 bool CMainFrame::DeleteLine(uint256 hashKey)
490 long nData = *(long*)&hashKey;
494 string strHash = " " + hashKey.ToString();
495 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
496 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
500 m_listCtrl->DeleteItem(nIndex);
505 string FormatTxStatus(const CWalletTx& wtx)
510 if (wtx.nLockTime < 500000000)
511 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
513 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
517 int nDepth = wtx.GetDepthInMainChain();
518 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
519 return strprintf(_("%d/offline?"), nDepth);
521 return strprintf(_("%d/unconfirmed"), nDepth);
523 return strprintf(_("%d confirmations"), nDepth);
527 string SingleLine(const string& strIn)
530 bool fOneSpace = false;
531 foreach(unsigned char c, strIn)
539 if (fOneSpace && !strOut.empty())
548 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
550 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
551 int64 nCredit = wtx.GetCredit(true);
552 int64 nDebit = wtx.GetDebit();
553 int64 nNet = nCredit - nDebit;
554 uint256 hash = wtx.GetHash();
555 string strStatus = FormatTxStatus(wtx);
556 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
557 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
558 map<string, string> mapValue = wtx.mapValue;
559 wtx.nLinesDisplayed = 1;
563 if (wtx.IsCoinBase())
565 // Don't show generated coin until confirmed by at least one block after it
566 // so we don't get the user's hopes up until it looks like it's probably accepted.
568 // It is not an error when generated blocks are not accepted. By design,
569 // some percentage of blocks, like 10% or more, will end up not accepted.
570 // This is the normal mechanism by which the network copes with latency.
572 // We display regular transactions right away before any confirmation
573 // because they can always get into some block eventually. Generated coins
574 // are special because if their block is not accepted, they are not valid.
576 if (wtx.GetDepthInMainChain() < 2)
578 wtx.nLinesDisplayed = 0;
586 // Find the block the tx is in
587 CBlockIndex* pindex = NULL;
588 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
589 if (mi != mapBlockIndex.end())
590 pindex = (*mi).second;
592 // Sort order, unrecorded transactions sort to the top
593 string strSort = strprintf("%010d-%01d-%010u",
594 (pindex ? pindex->nHeight : INT_MAX),
595 (wtx.IsCoinBase() ? 1 : 0),
599 if (nNet > 0 || wtx.IsCoinBase())
604 string strDescription;
605 if (wtx.IsCoinBase())
608 strDescription = _("Generated");
611 int64 nUnmatured = 0;
612 foreach(const CTxOut& txout, wtx.vout)
613 nUnmatured += txout.GetCredit();
614 if (wtx.IsInMainChain())
616 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
618 // Check if the block was requested by anyone
619 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
620 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
624 strDescription = _("Generated (not accepted)");
628 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
630 // Received by IP connection
633 if (!mapValue["from"].empty())
634 strDescription += _("From: ") + mapValue["from"];
635 if (!mapValue["message"].empty())
637 if (!strDescription.empty())
638 strDescription += " - ";
639 strDescription += mapValue["message"];
644 // Received by Bitcoin Address
647 foreach(const CTxOut& txout, wtx.vout)
651 vector<unsigned char> vchPubKey;
652 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
654 CRITICAL_BLOCK(cs_mapAddressBook)
656 //strDescription += _("Received payment to ");
657 //strDescription += _("Received with address ");
658 strDescription += _("Received with: ");
659 string strAddress = PubKeyToAddress(vchPubKey);
660 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
661 if (mi != mapAddressBook.end() && !(*mi).second.empty())
663 string strLabel = (*mi).second;
664 strDescription += strAddress.substr(0,12) + "... ";
665 strDescription += "(" + strLabel + ")";
668 strDescription += strAddress;
676 string strCredit = FormatMoney(nNet, true);
678 strCredit = "[" + strCredit + "]";
680 InsertLine(fNew, nIndex, hash, strSort, colour,
682 nTime ? DateTimeStr(nTime) : "",
683 SingleLine(strDescription),
689 bool fAllFromMe = true;
690 foreach(const CTxIn& txin, wtx.vin)
691 fAllFromMe = fAllFromMe && txin.IsMine();
693 bool fAllToMe = true;
694 foreach(const CTxOut& txout, wtx.vout)
695 fAllToMe = fAllToMe && txout.IsMine();
697 if (fAllFromMe && fAllToMe)
700 int64 nChange = wtx.GetChange();
701 InsertLine(fNew, nIndex, hash, strSort, colour,
703 nTime ? DateTimeStr(nTime) : "",
704 _("Payment to yourself"),
705 FormatMoney(-(nDebit - nChange), true),
706 FormatMoney(nCredit - nChange, true));
716 int64 nTxFee = nDebit - wtx.GetValueOut();
717 wtx.nLinesDisplayed = 0;
718 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
720 const CTxOut& txout = wtx.vout[nOut];
725 if (!mapValue["to"].empty())
728 strAddress = mapValue["to"];
732 // Sent to Bitcoin Address
734 if (ExtractHash160(txout.scriptPubKey, hash160))
735 strAddress = Hash160ToAddress(hash160);
738 string strDescription = _("To: ");
739 CRITICAL_BLOCK(cs_mapAddressBook)
740 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
741 strDescription += mapAddressBook[strAddress] + " ";
742 strDescription += strAddress;
743 if (!mapValue["message"].empty())
745 if (!strDescription.empty())
746 strDescription += " - ";
747 strDescription += mapValue["message"];
749 else if (!mapValue["comment"].empty())
751 if (!strDescription.empty())
752 strDescription += " - ";
753 strDescription += mapValue["comment"];
756 int64 nValue = txout.nValue;
763 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
765 nTime ? DateTimeStr(nTime) : "",
766 SingleLine(strDescription),
767 FormatMoney(-nValue, true),
770 wtx.nLinesDisplayed++;
776 // Mixed debit transaction, can't break down payees
778 bool fAllMine = true;
779 foreach(const CTxOut& txout, wtx.vout)
780 fAllMine = fAllMine && txout.IsMine();
781 foreach(const CTxIn& txin, wtx.vin)
782 fAllMine = fAllMine && txin.IsMine();
784 InsertLine(fNew, nIndex, hash, strSort, colour,
786 nTime ? DateTimeStr(nTime) : "",
788 FormatMoney(nNet, true),
796 void CMainFrame::RefreshListCtrl()
798 fRefreshListCtrl = true;
802 void CMainFrame::OnIdle(wxIdleEvent& event)
804 if (fRefreshListCtrl)
806 // Collect list of wallet transactions and sort newest first
807 bool fEntered = false;
808 vector<pair<unsigned int, uint256> > vSorted;
809 TRY_CRITICAL_BLOCK(cs_mapWallet)
811 printf("RefreshListCtrl starting\n");
813 fRefreshListCtrl = false;
814 vWalletUpdated.clear();
816 // Do the newest transactions first
817 vSorted.reserve(mapWallet.size());
818 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
820 const CWalletTx& wtx = (*it).second;
821 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
822 vSorted.push_back(make_pair(nTime, (*it).first));
824 m_listCtrl->DeleteAllItems();
829 sort(vSorted.begin(), vSorted.end());
832 for (int i = 0; i < vSorted.size();)
836 bool fEntered = false;
837 TRY_CRITICAL_BLOCK(cs_mapWallet)
840 uint256& hash = vSorted[i++].second;
841 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
842 if (mi != mapWallet.end())
843 InsertTransaction((*mi).second, true);
845 if (!fEntered || i == 100 || i % 500 == 0)
849 printf("RefreshListCtrl done\n");
851 // Update transaction total display
856 // Check for time updates
857 static int64 nLastTime;
858 if (GetTime() > nLastTime + 30)
860 TRY_CRITICAL_BLOCK(cs_mapWallet)
862 nLastTime = GetTime();
863 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
865 CWalletTx& wtx = (*it).second;
866 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
867 InsertTransaction(wtx, false);
874 void CMainFrame::RefreshStatusColumn()
877 static CBlockIndex* pindexLastBest;
878 static unsigned int nLastRefreshed;
880 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
881 if (nTop == nLastTop && pindexLastBest == pindexBest)
884 TRY_CRITICAL_BLOCK(cs_mapWallet)
887 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
889 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
891 // If no updates, only need to do the part that moved onto the screen
892 if (nStart >= nLastTop && nStart < nLastTop + 100)
893 nStart = nLastTop + 100;
894 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
898 pindexLastBest = pindexBest;
899 nLastRefreshed = nListViewUpdated;
901 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
903 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
904 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
905 if (mi == mapWallet.end())
907 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
910 CWalletTx& wtx = (*mi).second;
911 if (wtx.IsCoinBase() ||
912 wtx.GetTxTime() != wtx.nTimeDisplayed ||
913 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
915 if (!InsertTransaction(wtx, false, nIndex))
916 m_listCtrl->DeleteItem(nIndex--);
920 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
926 void CMainFrame::OnPaint(wxPaintEvent& event)
937 unsigned int nNeedRepaint = 0;
938 unsigned int nLastRepaint = 0;
939 int64 nLastRepaintTime = 0;
940 int64 nRepaintInterval = 500;
942 void ThreadDelayedRepaint(void* parg)
946 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
948 nLastRepaint = nNeedRepaint;
951 printf("DelayedRepaint\n");
953 pframeMain->fRefresh = true;
954 pframeMain->GetEventHandler()->AddPendingEvent(event);
957 Sleep(nRepaintInterval);
961 void MainFrameRepaint()
963 // This is called by network code that shouldn't access pframeMain
964 // directly because it could still be running after the UI is closed.
967 // Don't repaint too often
968 static int64 nLastRepaintRequest;
969 if (GetTimeMillis() - nLastRepaintRequest < 100)
974 nLastRepaintRequest = GetTimeMillis();
976 printf("MainFrameRepaint\n");
978 pframeMain->fRefresh = true;
979 pframeMain->GetEventHandler()->AddPendingEvent(event);
983 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
985 // Skip lets the listctrl do the paint, we're just hooking the message
989 ptaskbaricon->UpdateTooltip();
994 static int nTransactionCount;
995 bool fPaintedBalance = false;
996 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
998 nLastRepaint = nNeedRepaint;
999 nLastRepaintTime = GetTimeMillis();
1001 // Update listctrl contents
1002 if (!vWalletUpdated.empty())
1004 TRY_CRITICAL_BLOCK(cs_mapWallet)
1007 if (m_listCtrl->GetItemCount())
1008 strTop = (string)m_listCtrl->GetItemText(0);
1009 foreach(uint256 hash, vWalletUpdated)
1011 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1012 if (mi != mapWallet.end())
1013 InsertTransaction((*mi).second, false);
1015 vWalletUpdated.clear();
1016 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1017 m_listCtrl->ScrollList(0, INT_MIN/2);
1022 TRY_CRITICAL_BLOCK(cs_mapWallet)
1024 fPaintedBalance = true;
1025 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
1027 // Count hidden and multi-line transactions
1028 nTransactionCount = 0;
1029 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1031 CWalletTx& wtx = (*it).second;
1032 nTransactionCount += wtx.nLinesDisplayed;
1036 if (!vWalletUpdated.empty() || !fPaintedBalance)
1039 // Update status column of visible items only
1040 RefreshStatusColumn();
1042 // Update status bar
1043 static string strPrevWarning;
1044 string strWarning = GetWarnings("statusbar");
1045 if (strWarning != "")
1046 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1047 else if (strPrevWarning != "")
1048 m_statusBar->SetStatusText("", 0);
1049 strPrevWarning = strWarning;
1052 if (fGenerateBitcoins)
1053 strGen = _(" Generating");
1054 if (fGenerateBitcoins && vNodes.empty())
1055 strGen = _("(not connected)");
1056 m_statusBar->SetStatusText(strGen, 1);
1058 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1059 m_statusBar->SetStatusText(strStatus, 2);
1061 // Update receiving address
1062 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1063 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1064 m_textCtrlAddress->SetValue(strDefaultAddress);
1068 void UIThreadCall(boost::function0<void> fn)
1070 // Call this with a function object created with bind.
1071 // bind needs all parameters to match the function's expected types
1072 // and all default parameters specified. Some examples:
1073 // UIThreadCall(bind(wxBell));
1074 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1075 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1078 wxCommandEvent event(wxEVT_UITHREADCALL);
1079 event.SetClientData((void*)new boost::function0<void>(fn));
1080 pframeMain->GetEventHandler()->AddPendingEvent(event);
1084 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1086 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1091 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1097 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
1099 // Options->Generate Coins
1100 GenerateBitcoins(event.IsChecked());
1103 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1105 event.Check(fGenerateBitcoins);
1108 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1110 // Options->Your Receiving Addresses
1111 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1112 if (!dialog.ShowModal())
1116 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1119 COptionsDialog dialog(this);
1123 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1126 CAboutDialog dialog(this);
1130 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1133 CSendDialog dialog(this);
1137 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1139 // Toolbar: Address Book
1140 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1141 if (dialogAddr.ShowModal() == 2)
1144 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1145 dialogSend.ShowModal();
1149 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1151 // Automatically select-all when entering window
1153 m_textCtrlAddress->SetSelection(-1, -1);
1154 fOnSetFocusAddress = true;
1157 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1160 if (fOnSetFocusAddress)
1161 m_textCtrlAddress->SetSelection(-1, -1);
1162 fOnSetFocusAddress = false;
1165 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1168 CGetTextFromUserDialog dialog(this,
1169 _("New Receiving Address"),
1170 _("You should use a new address for each payment you receive.\n\nLabel"),
1172 if (!dialog.ShowModal())
1174 string strName = dialog.GetValue();
1177 string strAddress = PubKeyToAddress(GetKeyFromKeyPool());
1180 SetAddressBookName(strAddress, strName);
1181 SetDefaultReceivingAddress(strAddress);
1184 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1186 // Copy address box to clipboard
1187 if (wxTheClipboard->Open())
1189 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1190 wxTheClipboard->Close();
1194 void CMainFrame::OnListItemActivated(wxListEvent& event)
1196 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1198 CRITICAL_BLOCK(cs_mapWallet)
1200 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1201 if (mi == mapWallet.end())
1203 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1208 CTxDetailsDialog dialog(this, wtx);
1210 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1219 //////////////////////////////////////////////////////////////////////////////
1224 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1226 CRITICAL_BLOCK(cs_mapAddressBook)
1229 strHTML.reserve(4000);
1230 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1232 int64 nTime = wtx.GetTxTime();
1233 int64 nCredit = wtx.GetCredit();
1234 int64 nDebit = wtx.GetDebit();
1235 int64 nNet = nCredit - nDebit;
1239 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1240 int nRequests = wtx.GetRequestCount();
1241 if (nRequests != -1)
1244 strHTML += _(", has not been successfully broadcast yet");
1245 else if (nRequests == 1)
1246 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1248 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1252 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1258 if (wtx.IsCoinBase())
1260 strHTML += _("<b>Source:</b> Generated<br>");
1262 else if (!wtx.mapValue["from"].empty())
1264 // Online transaction
1265 if (!wtx.mapValue["from"].empty())
1266 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1270 // Offline transaction
1274 foreach(const CTxOut& txout, wtx.vout)
1278 vector<unsigned char> vchPubKey;
1279 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1281 string strAddress = PubKeyToAddress(vchPubKey);
1282 if (mapAddressBook.count(strAddress))
1284 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1285 strHTML += _("<b>To:</b> ");
1286 strHTML += HtmlEscape(strAddress);
1287 if (!mapAddressBook[strAddress].empty())
1288 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1290 strHTML += _(" (yours)");
1305 if (!wtx.mapValue["to"].empty())
1307 // Online transaction
1308 strAddress = wtx.mapValue["to"];
1309 strHTML += _("<b>To:</b> ");
1310 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1311 strHTML += mapAddressBook[strAddress] + " ";
1312 strHTML += HtmlEscape(strAddress) + "<br>";
1319 if (wtx.IsCoinBase() && nCredit == 0)
1324 int64 nUnmatured = 0;
1325 foreach(const CTxOut& txout, wtx.vout)
1326 nUnmatured += txout.GetCredit();
1327 strHTML += _("<b>Credit:</b> ");
1328 if (wtx.IsInMainChain())
1329 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1331 strHTML += _("(not accepted)");
1339 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1343 bool fAllFromMe = true;
1344 foreach(const CTxIn& txin, wtx.vin)
1345 fAllFromMe = fAllFromMe && txin.IsMine();
1347 bool fAllToMe = true;
1348 foreach(const CTxOut& txout, wtx.vout)
1349 fAllToMe = fAllToMe && txout.IsMine();
1356 foreach(const CTxOut& txout, wtx.vout)
1361 if (wtx.mapValue["to"].empty())
1363 // Offline transaction
1365 if (ExtractHash160(txout.scriptPubKey, hash160))
1367 string strAddress = Hash160ToAddress(hash160);
1368 strHTML += _("<b>To:</b> ");
1369 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1370 strHTML += mapAddressBook[strAddress] + " ";
1371 strHTML += strAddress;
1376 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1382 int64 nChange = wtx.GetChange();
1383 int64 nValue = nCredit - nChange;
1384 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1385 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1388 int64 nTxFee = nDebit - wtx.GetValueOut();
1390 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1395 // Mixed debit transaction
1397 foreach(const CTxIn& txin, wtx.vin)
1399 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1400 foreach(const CTxOut& txout, wtx.vout)
1402 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1406 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1412 if (!wtx.mapValue["message"].empty())
1413 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1414 if (!wtx.mapValue["comment"].empty())
1415 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1417 if (wtx.IsCoinBase())
1418 strHTML += string() + "<br>" + _("Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "<br>";
1426 strHTML += "<hr><br>debug print<br><br>";
1427 foreach(const CTxIn& txin, wtx.vin)
1429 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1430 foreach(const CTxOut& txout, wtx.vout)
1432 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1434 strHTML += "<br><b>Transaction:</b><br>";
1435 strHTML += HtmlEscape(wtx.ToString(), true);
1437 strHTML += "<br><b>Inputs:</b><br>";
1438 CRITICAL_BLOCK(cs_mapWallet)
1440 foreach(const CTxIn& txin, wtx.vin)
1442 COutPoint prevout = txin.prevout;
1443 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1444 if (mi != mapWallet.end())
1446 const CWalletTx& prev = (*mi).second;
1447 if (prevout.n < prev.vout.size())
1449 strHTML += HtmlEscape(prev.ToString(), true);
1450 strHTML += " " + FormatTxStatus(prev) + ", ";
1451 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1460 strHTML += "</font></html>";
1461 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1462 m_htmlWin->SetPage(strHTML);
1463 m_buttonOK->SetFocus();
1467 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1478 //////////////////////////////////////////////////////////////////////////////
1484 string StartupShortcutPath()
1486 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1489 bool GetStartOnSystemStartup()
1491 return filesystem::exists(StartupShortcutPath().c_str());
1494 void SetStartOnSystemStartup(bool fAutoStart)
1496 // If the shortcut exists already, remove it for updating
1497 remove(StartupShortcutPath().c_str());
1503 // Get a pointer to the IShellLink interface.
1504 IShellLink* psl = NULL;
1505 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1506 CLSCTX_INPROC_SERVER, IID_IShellLink,
1507 reinterpret_cast<void**>(&psl));
1509 if (SUCCEEDED(hres))
1511 // Get the current executable path
1512 TCHAR pszExePath[MAX_PATH];
1513 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1515 // Set the path to the shortcut target
1516 psl->SetPath(pszExePath);
1517 PathRemoveFileSpec(pszExePath);
1518 psl->SetWorkingDirectory(pszExePath);
1519 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1521 // Query IShellLink for the IPersistFile interface for
1522 // saving the shortcut in persistent storage.
1523 IPersistFile* ppf = NULL;
1524 hres = psl->QueryInterface(IID_IPersistFile,
1525 reinterpret_cast<void**>(&ppf));
1526 if (SUCCEEDED(hres))
1528 WCHAR pwsz[MAX_PATH];
1529 // Ensure that the string is ANSI.
1530 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1531 // Save the link by calling IPersistFile::Save.
1532 hres = ppf->Save(pwsz, TRUE);
1541 #elif defined(__WXGTK__)
1543 // Follow the Desktop Application Autostart Spec:
1544 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1546 boost::filesystem::path GetAutostartDir()
1548 namespace fs = boost::filesystem;
1550 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1551 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1552 char* pszHome = getenv("HOME");
1553 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1557 boost::filesystem::path GetAutostartFilePath()
1559 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1562 bool GetStartOnSystemStartup()
1564 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1565 if (!optionFile.good())
1567 // Scan through file for "Hidden=true":
1569 while (!optionFile.eof())
1571 getline(optionFile, line);
1572 if (line.find("Hidden") != string::npos &&
1573 line.find("true") != string::npos)
1581 void SetStartOnSystemStartup(bool fAutoStart)
1585 unlink(GetAutostartFilePath().native_file_string().c_str());
1589 char pszExePath[MAX_PATH+1];
1590 memset(pszExePath, 0, sizeof(pszExePath));
1591 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1594 boost::filesystem::create_directories(GetAutostartDir());
1596 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1597 if (!optionFile.good())
1599 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1602 // Write a bitcoin.desktop file to the autostart directory:
1603 optionFile << "[Desktop Entry]\n";
1604 optionFile << "Type=Application\n";
1605 optionFile << "Name=Bitcoin\n";
1606 optionFile << "Exec=" << pszExePath << "\n";
1607 optionFile << "Terminal=false\n";
1608 optionFile << "Hidden=false\n";
1614 // TODO: OSX startup stuff; see:
1615 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1617 bool GetStartOnSystemStartup() { return false; }
1618 void SetStartOnSystemStartup(bool fAutoStart) { }
1627 //////////////////////////////////////////////////////////////////////////////
1632 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1634 // Set up list box of page choices
1635 m_listBox->Append(_("Main"));
1636 //m_listBox->Append(_("Test 2"));
1637 m_listBox->SetSelection(0);
1640 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1642 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1643 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1644 if (!GetBoolArg("-minimizetotray"))
1646 // Minimize to tray is just too buggy on Linux
1647 fMinimizeToTray = false;
1648 m_checkBoxMinimizeToTray->SetValue(false);
1649 m_checkBoxMinimizeToTray->Enable(false);
1650 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1653 #ifdef __WXMAC_OSX__
1654 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1658 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1659 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
1660 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
1661 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
1662 int nProcessors = wxThread::GetCPUCount();
1663 if (nProcessors < 1)
1665 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
1666 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1667 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1668 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1669 m_checkBoxUseProxy->SetValue(fUseProxy);
1670 m_textCtrlProxyIP->Enable(fUseProxy);
1671 m_textCtrlProxyPort->Enable(fUseProxy);
1672 m_staticTextProxyIP->Enable(fUseProxy);
1673 m_staticTextProxyPort->Enable(fUseProxy);
1674 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1675 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1677 m_buttonOK->SetFocus();
1680 void COptionsDialog::SelectPage(int nPage)
1682 m_panelMain->Show(nPage == 0);
1683 m_panelTest2->Show(nPage == 1);
1685 m_scrolledWindow->Layout();
1686 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1689 void COptionsDialog::OnListBox(wxCommandEvent& event)
1691 SelectPage(event.GetSelection());
1694 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1697 int64 nTmp = nTransactionFee;
1698 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1699 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1702 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
1704 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
1707 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1709 m_textCtrlProxyIP->Enable(event.IsChecked());
1710 m_textCtrlProxyPort->Enable(event.IsChecked());
1711 m_staticTextProxyIP->Enable(event.IsChecked());
1712 m_staticTextProxyPort->Enable(event.IsChecked());
1715 CAddress COptionsDialog::GetProxyAddr()
1717 // Be careful about byte order, addr.ip and addr.port are big endian
1718 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1719 if (addr.ip == INADDR_NONE)
1720 addr.ip = addrProxy.ip;
1721 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1722 addr.port = htons(nPort);
1723 if (nPort <= 0 || nPort > USHRT_MAX)
1724 addr.port = addrProxy.port;
1728 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1731 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1732 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1736 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1738 OnButtonApply(event);
1742 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1747 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1751 int64 nPrevTransactionFee = nTransactionFee;
1752 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1753 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1755 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
1756 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
1758 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
1759 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
1761 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
1763 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
1764 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
1766 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
1767 GenerateBitcoins(fGenerateBitcoins);
1769 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1771 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1772 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1775 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1777 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1778 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1779 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1782 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1784 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1785 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1788 fUseProxy = m_checkBoxUseProxy->GetValue();
1789 walletdb.WriteSetting("fUseProxy", fUseProxy);
1791 addrProxy = GetProxyAddr();
1792 walletdb.WriteSetting("addrProxy", addrProxy);
1800 //////////////////////////////////////////////////////////////////////////////
1805 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1807 m_staticTextVersion->SetLabel(strprintf(_("version %s%s BETA"), FormatVersion(VERSION).c_str(), pszSubVer));
1809 // Change (c) into UTF-8 or ANSI copyright symbol
1810 wxString str = m_staticTextMain->GetLabel();
1812 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1814 str.Replace("(c)", "\xA9");
1816 m_staticTextMain->SetLabel(str);
1818 // Resize on Linux to make the window fit the text.
1819 // The text was wrapped manually rather than using the Wrap setting because
1820 // the wrap would be too small on Linux and it can't be changed at this point.
1821 wxFont fontTmp = m_staticTextMain->GetFont();
1822 if (fontTmp.GetPointSize() > 8);
1823 fontTmp.SetPointSize(8);
1824 m_staticTextMain->SetFont(fontTmp);
1825 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1829 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1839 //////////////////////////////////////////////////////////////////////////////
1844 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1847 m_textCtrlAddress->SetValue(strAddress);
1848 m_choiceTransferType->SetSelection(0);
1849 m_bitmapCheckMark->Show(false);
1850 fEnabledPrev = true;
1851 m_textCtrlAddress->SetFocus();
1852 //// todo: should add a display of your balance for convenience
1854 wxFont fontTmp = m_staticTextInstructions->GetFont();
1855 if (fontTmp.GetPointSize() > 9);
1856 fontTmp.SetPointSize(9);
1857 m_staticTextInstructions->SetFont(fontTmp);
1863 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1866 wxCommandEvent event;
1867 OnTextAddress(event);
1869 // Fixup the tab order
1870 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1871 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1875 void CSendDialog::OnTextAddress(wxCommandEvent& event)
1879 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
1880 m_bitmapCheckMark->Show(fBitcoinAddress);
1882 // Grey out message if bitcoin address
1883 bool fEnable = !fBitcoinAddress;
1884 m_staticTextFrom->Enable(fEnable);
1885 m_textCtrlFrom->Enable(fEnable);
1886 m_staticTextMessage->Enable(fEnable);
1887 m_textCtrlMessage->Enable(fEnable);
1888 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
1889 if (!fEnable && fEnabledPrev)
1891 strFromSave = m_textCtrlFrom->GetValue();
1892 strMessageSave = m_textCtrlMessage->GetValue();
1893 m_textCtrlFrom->SetValue(_("n/a"));
1894 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
1896 else if (fEnable && !fEnabledPrev)
1898 m_textCtrlFrom->SetValue(strFromSave);
1899 m_textCtrlMessage->SetValue(strMessageSave);
1901 fEnabledPrev = fEnable;
1904 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1906 // Reformat the amount
1908 if (m_textCtrlAmount->GetValue().Trim().empty())
1911 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1912 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1915 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1917 // Open address book
1918 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1919 if (dialog.ShowModal())
1920 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1923 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1925 // Copy clipboard to address box
1926 if (wxTheClipboard->Open())
1928 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1930 wxTextDataObject data;
1931 wxTheClipboard->GetData(data);
1932 m_textCtrlAddress->SetValue(data.GetText());
1934 wxTheClipboard->Close();
1938 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1940 static CCriticalSection cs_sendlock;
1941 TRY_CRITICAL_BLOCK(cs_sendlock)
1944 string strAddress = (string)m_textCtrlAddress->GetValue();
1948 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1950 wxMessageBox(_("Error in amount "), _("Send Coins"));
1953 if (nValue > GetBalance())
1955 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1958 if (nValue + nTransactionFee > GetBalance())
1960 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1964 // Parse bitcoin address
1966 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1968 if (fBitcoinAddress)
1970 // Send to bitcoin address
1971 CScript scriptPubKey;
1972 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1974 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1976 wxMessageBox(_("Payment sent "), _("Sending..."));
1977 else if (strError == "ABORTED")
1978 return; // leave send dialog open
1981 wxMessageBox(strError + " ", _("Sending..."));
1988 CAddress addr(strAddress);
1989 if (!addr.IsValid())
1991 wxMessageBox(_("Invalid address "), _("Send Coins"));
1996 wtx.mapValue["to"] = strAddress;
1997 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
1998 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
2000 // Send to IP address
2001 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2002 if (!pdialog->ShowModal())
2006 CRITICAL_BLOCK(cs_mapAddressBook)
2007 if (!mapAddressBook.count(strAddress))
2008 SetAddressBookName(strAddress, "");
2014 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2025 //////////////////////////////////////////////////////////////////////////////
2030 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
2035 start = wxDateTime::UNow();
2036 memset(pszStatus, 0, sizeof(pszStatus));
2043 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2046 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2047 m_textCtrlStatus->SetValue("");
2049 CreateThread(SendingDialogStartTransfer, this);
2052 CSendingDialog::~CSendingDialog()
2054 printf("~CSendingDialog()\n");
2057 void CSendingDialog::Close()
2059 // Last one out turn out the lights.
2060 // fWorkDone signals that work side is done and UI thread should call destroy.
2061 // fUIDone signals that UI window has closed and work thread should call destroy.
2062 // This allows the window to disappear and end modality when cancelled
2063 // without making the user wait for ConnectNode to return. The dialog object
2064 // hangs around in the background until the work thread exits.
2075 void CSendingDialog::OnClose(wxCloseEvent& event)
2077 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2084 wxCommandEvent cmdevent;
2085 OnButtonCancel(cmdevent);
2089 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2095 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2101 void CSendingDialog::OnPaint(wxPaintEvent& event)
2104 if (strlen(pszStatus) > 130)
2105 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2107 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2108 m_staticTextSending->SetFocus();
2110 m_buttonCancel->Enable(false);
2113 m_buttonOK->Enable(true);
2114 m_buttonOK->SetFocus();
2115 m_buttonCancel->Enable(false);
2117 if (fAbort && fCanCancel && IsShown())
2119 strcpy(pszStatus, _("CANCELLED"));
2120 m_buttonOK->Enable(true);
2121 m_buttonOK->SetFocus();
2122 m_buttonCancel->Enable(false);
2123 m_buttonCancel->SetLabel(_("Cancelled"));
2125 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2131 // Everything from here on is not in the UI thread and must only communicate
2132 // with the rest of the dialog through variables and calling repaint.
2135 void CSendingDialog::Repaint()
2139 GetEventHandler()->AddPendingEvent(event);
2142 bool CSendingDialog::Status()
2149 if (fAbort && fCanCancel)
2151 memset(pszStatus, 0, 10);
2152 strcpy(pszStatus, _("CANCELLED"));
2160 bool CSendingDialog::Status(const string& str)
2165 // This can be read by the UI thread at any time,
2166 // so copy in a way that can be read cleanly at all times.
2167 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2168 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2174 bool CSendingDialog::Error(const string& str)
2178 Status(string(_("Error: ")) + str);
2182 void SendingDialogStartTransfer(void* parg)
2184 ((CSendingDialog*)parg)->StartTransfer();
2187 void CSendingDialog::StartTransfer()
2189 // Make sure we have enough money
2190 if (nPrice + nTransactionFee > GetBalance())
2192 Error(_("Insufficient funds"));
2196 // We may have connected already for product details
2197 if (!Status(_("Connecting...")))
2199 CNode* pnode = ConnectNode(addr, 15 * 60);
2202 Error(_("Unable to connect"));
2206 // Send order to seller, with response going to OnReply2 via event handler
2207 if (!Status(_("Requesting public key...")))
2209 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2212 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2214 ((CSendingDialog*)parg)->OnReply2(vRecv);
2217 void CSendingDialog::OnReply2(CDataStream& vRecv)
2219 if (!Status(_("Received public key...")))
2222 CScript scriptPubKey;
2231 vRecv >> strMessage;
2233 Error(_("Recipient is not accepting transactions sent by IP address"));
2235 Error(_("Transfer was not accepted"));
2236 //// todo: enlarge the window and enable a hidden white box to put seller's message
2239 vRecv >> scriptPubKey;
2243 //// what do we want to do about this?
2244 Error(_("Invalid response received"));
2248 // Pause to give the user a chance to cancel
2249 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2256 CRITICAL_BLOCK(cs_main)
2259 if (!Status(_("Creating transaction...")))
2261 if (nPrice + nTransactionFee > GetBalance())
2263 Error(_("Insufficient funds"));
2266 CReserveKey reservekey;
2268 if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2270 if (nPrice + nFeeRequired > GetBalance())
2271 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
2273 Error(_("Transaction creation failed"));
2278 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2280 Error(_("Transaction aborted"));
2284 // Make sure we're still connected
2285 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2288 Error(_("Lost connection, transaction cancelled"));
2292 // Last chance to cancel
2304 if (!Status(_("Sending payment...")))
2308 if (!CommitTransaction(wtx, reservekey))
2310 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."));
2314 // Send payment tx to seller, with response going to OnReply3 via event handler
2315 CWalletTx wtxSend = wtx;
2316 wtxSend.fFromMe = false;
2317 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2319 Status(_("Waiting for confirmation..."));
2324 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2326 ((CSendingDialog*)parg)->OnReply3(vRecv);
2329 void CSendingDialog::OnReply3(CDataStream& vRecv)
2337 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2338 "The transaction is recorded and will credit to the recipient,\n"
2339 "but the comment information will be blank."));
2345 //// what do we want to do about this?
2346 Error(_("Payment was sent, but an invalid response was received"));
2352 Status(_("Payment completed"));
2360 //////////////////////////////////////////////////////////////////////////////
2362 // CAddressBookDialog
2365 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2367 // Set initially selected page
2368 wxNotebookEvent event;
2369 event.SetSelection(nPageIn);
2370 OnNotebookPageChanged(event);
2371 m_notebook->ChangeSelection(nPageIn);
2373 fDuringSend = fDuringSendIn;
2375 m_buttonCancel->Show(false);
2378 wxIcon iconAddressBook;
2379 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2380 SetIcon(iconAddressBook);
2382 // Init column headers
2383 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2384 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2385 m_listCtrlSending->SetFocus();
2386 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2387 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2388 m_listCtrlReceiving->SetFocus();
2390 // Fill listctrl with address book data
2391 CRITICAL_BLOCK(cs_mapKeys)
2392 CRITICAL_BLOCK(cs_mapAddressBook)
2394 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2395 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2397 string strAddress = item.first;
2398 string strName = item.second;
2400 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2401 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2402 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2403 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2404 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2409 wxString CAddressBookDialog::GetSelectedAddress()
2411 int nIndex = GetSelection(m_listCtrl);
2414 return GetItemText(m_listCtrl, nIndex, 1);
2417 wxString CAddressBookDialog::GetSelectedSendingAddress()
2419 int nIndex = GetSelection(m_listCtrlSending);
2422 return GetItemText(m_listCtrlSending, nIndex, 1);
2425 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2427 int nIndex = GetSelection(m_listCtrlReceiving);
2430 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2433 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2436 nPage = event.GetSelection();
2437 if (nPage == SENDING)
2438 m_listCtrl = m_listCtrlSending;
2439 else if (nPage == RECEIVING)
2440 m_listCtrl = m_listCtrlReceiving;
2441 m_buttonDelete->Show(nPage == SENDING);
2442 m_buttonCopy->Show(nPage == RECEIVING);
2444 m_listCtrl->SetFocus();
2447 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2449 // Update address book with edited name
2451 if (event.IsEditCancelled())
2453 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2454 SetAddressBookName(strAddress, string(event.GetText()));
2455 pframeMain->RefreshListCtrl();
2458 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2461 if (nPage == RECEIVING)
2462 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2465 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2470 // Doubleclick returns selection
2471 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2475 // Doubleclick edits item
2476 wxCommandEvent event2;
2477 OnButtonEdit(event2);
2480 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2482 if (nPage != SENDING)
2484 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2486 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2488 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2489 CWalletDB().EraseName(strAddress);
2490 m_listCtrl->DeleteItem(nIndex);
2493 pframeMain->RefreshListCtrl();
2496 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2498 // Copy address box to clipboard
2499 if (wxTheClipboard->Open())
2501 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2502 wxTheClipboard->Close();
2506 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2509 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2511 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2515 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2517 int nIndex = GetSelection(m_listCtrl);
2520 string strName = (string)m_listCtrl->GetItemText(nIndex);
2521 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2522 string strAddressOrg = strAddress;
2524 if (nPage == SENDING)
2526 // Ask name and address
2529 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2530 if (!dialog.ShowModal())
2532 strName = dialog.GetValue1();
2533 strAddress = dialog.GetValue2();
2535 while (CheckIfMine(strAddress, _("Edit Address")));
2538 else if (nPage == RECEIVING)
2541 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2542 if (!dialog.ShowModal())
2544 strName = dialog.GetValue();
2548 if (strAddress != strAddressOrg)
2549 CWalletDB().EraseName(strAddressOrg);
2550 SetAddressBookName(strAddress, strName);
2551 m_listCtrl->SetItem(nIndex, 1, strAddress);
2552 m_listCtrl->SetItemText(nIndex, strName);
2553 pframeMain->RefreshListCtrl();
2556 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2561 if (nPage == SENDING)
2563 // Ask name and address
2566 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2567 if (!dialog.ShowModal())
2569 strName = dialog.GetValue1();
2570 strAddress = dialog.GetValue2();
2572 while (CheckIfMine(strAddress, _("Add Address")));
2574 else if (nPage == RECEIVING)
2577 CGetTextFromUserDialog dialog(this,
2578 _("New Receiving Address"),
2579 _("You should use a new address for each payment you receive.\n\nLabel"),
2581 if (!dialog.ShowModal())
2583 strName = dialog.GetValue();
2586 strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2589 // Add to list and select it
2590 SetAddressBookName(strAddress, strName);
2591 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2592 SetSelection(m_listCtrl, nIndex);
2593 m_listCtrl->SetFocus();
2594 if (nPage == SENDING)
2595 pframeMain->RefreshListCtrl();
2598 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2601 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2604 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2610 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2621 //////////////////////////////////////////////////////////////////////////////
2628 ID_TASKBAR_RESTORE = 10001,
2630 ID_TASKBAR_GENERATE,
2634 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2635 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2636 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2637 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2638 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
2639 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2640 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2643 void CMyTaskBarIcon::Show(bool fShow)
2645 static char pszPrevTip[200];
2648 string strTooltip = _("Bitcoin");
2649 if (fGenerateBitcoins)
2650 strTooltip = _("Bitcoin - Generating");
2651 if (fGenerateBitcoins && vNodes.empty())
2652 strTooltip = _("Bitcoin - (not connected)");
2654 // Optimization, only update when changed, using char array to be reentrant
2655 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2657 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2659 // somehow it'll choose the wrong size and scale it down if
2660 // we use the main icon, so we hand it one with only 16x16
2661 SetIcon(wxICON(favicon), strTooltip);
2663 SetIcon(bitcoin80_xpm, strTooltip);
2669 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2674 void CMyTaskBarIcon::Hide()
2679 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2684 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2689 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2691 // Since it's modal, get the main window to do it
2692 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2693 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2696 void CMyTaskBarIcon::Restore()
2699 wxIconizeEvent event(0, false);
2700 pframeMain->GetEventHandler()->AddPendingEvent(event);
2701 pframeMain->Iconize(false);
2702 pframeMain->Raise();
2705 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
2707 GenerateBitcoins(event.IsChecked());
2710 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2712 event.Check(fGenerateBitcoins);
2715 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2717 pframeMain->Close(true);
2720 void CMyTaskBarIcon::UpdateTooltip()
2722 if (IsIconInstalled())
2726 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2728 wxMenu* pmenu = new wxMenu;
2729 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2730 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2731 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
2732 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2733 pmenu->AppendSeparator();
2734 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2744 //////////////////////////////////////////////////////////////////////////////
2749 void CreateMainWindow()
2751 pframeMain = new CMainFrame(NULL);
2752 if (GetBoolArg("-min"))
2753 pframeMain->Iconize(true);
2754 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2755 if (!GetBoolArg("-minimizetotray"))
2756 fMinimizeToTray = false;
2758 pframeMain->Show(true); // have to show first to get taskbar button to hide
2759 if (fMinimizeToTray && pframeMain->IsIconized())
2760 fClosedToTray = true;
2761 pframeMain->Show(!fClosedToTray);
2762 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2763 CreateThread(ThreadDelayedRepaint, NULL);
2767 // Define a new application
2768 class CMyApp : public wxApp
2777 // Hook Initialize so we can start without GUI
2778 virtual bool Initialize(int& argc, wxChar** argv);
2780 // 2nd-level exception handling: we get all the exceptions occurring in any
2781 // event handler here
2782 virtual bool OnExceptionInMainLoop();
2784 // 3rd, and final, level exception handling: whenever an unhandled
2785 // exception is caught, this function is called
2786 virtual void OnUnhandledException();
2788 // and now for something different: this function is called in case of a
2789 // crash (e.g. dereferencing null pointer, division by 0, ...)
2790 virtual void OnFatalException();
2793 IMPLEMENT_APP(CMyApp)
2795 bool CMyApp::Initialize(int& argc, wxChar** argv)
2797 for (int i = 1; i < argc; i++)
2798 if (!IsSwitchChar(argv[i][0]))
2799 fCommandLine = true;
2803 // wxApp::Initialize will remove environment-specific parameters,
2804 // so it's too early to call ParseParameters yet
2805 for (int i = 1; i < argc; i++)
2807 wxString str = argv[i];
2809 if (str.size() >= 1 && str[0] == '/')
2811 char pszLower[MAX_PATH];
2812 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2816 if (str == "-daemon")
2822 if (fDaemon || fCommandLine)
2824 // Call the original Initialize while suppressing error messages
2825 // and ignoring failure. If unable to initialize GTK, it fails
2826 // near the end so hopefully the last few things don't matter.
2829 wxApp::Initialize(argc, argv);
2838 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2842 pthread_exit((void*)0);
2849 return wxApp::Initialize(argc, argv);
2852 bool CMyApp::OnInit()
2854 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2855 // Disable malfunctioning wxWidgets debug assertion
2856 extern int g_isPainting;
2857 g_isPainting = 10000;
2860 wxImage::AddHandler(new wxPNGHandler);
2862 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2863 SetAppName("Bitcoin");
2865 SetAppName("bitcoin");
2869 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2870 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2871 class wxMBConv_win32 : public wxMBConv
2875 size_t m_minMBCharWidth;
2877 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2878 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2882 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2883 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2884 g_locale.AddCatalogLookupPathPrefix("locale");
2886 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2887 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2889 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2890 g_locale.AddCatalog("bitcoin");
2892 return AppInit(argc, argv);
2895 int CMyApp::OnExit()
2898 return wxApp::OnExit();
2901 bool CMyApp::OnExceptionInMainLoop()
2907 catch (std::exception& e)
2909 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2910 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2916 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2917 wxLogWarning("Unknown exception");
2924 void CMyApp::OnUnhandledException()
2926 // this shows how we may let some exception propagate uncaught
2931 catch (std::exception& e)
2933 PrintException(&e, "CMyApp::OnUnhandledException()");
2934 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2940 PrintException(NULL, "CMyApp::OnUnhandledException()");
2941 wxLogWarning("Unknown exception");
2947 void CMyApp::OnFatalException()
2949 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);